cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

int0002_vgpio.c (7426B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Intel INT0002 "Virtual GPIO" driver
      4 *
      5 * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
      6 *
      7 * Loosely based on android x86 kernel code which is:
      8 *
      9 * Copyright (c) 2014, Intel Corporation.
     10 *
     11 * Author: Dyut Kumar Sil <dyut.k.sil@intel.com>
     12 *
     13 * Some peripherals on Bay Trail and Cherry Trail platforms signal a Power
     14 * Management Event (PME) to the Power Management Controller (PMC) to wakeup
     15 * the system. When this happens software needs to clear the PME bus 0 status
     16 * bit in the GPE0a_STS register to avoid an IRQ storm on IRQ 9.
     17 *
     18 * This is modelled in ACPI through the INT0002 ACPI device, which is
     19 * called a "Virtual GPIO controller" in ACPI because it defines the event
     20 * handler to call when the PME triggers through _AEI and _L02 / _E02
     21 * methods as would be done for a real GPIO interrupt in ACPI. Note this
     22 * is a hack to define an AML event handler for the PME while using existing
     23 * ACPI mechanisms, this is not a real GPIO at all.
     24 *
     25 * This driver will bind to the INT0002 device, and register as a GPIO
     26 * controller, letting gpiolib-acpi.c call the _L02 handler as it would
     27 * for a real GPIO controller.
     28 */
     29
     30#include <linux/acpi.h>
     31#include <linux/bitmap.h>
     32#include <linux/gpio/driver.h>
     33#include <linux/interrupt.h>
     34#include <linux/io.h>
     35#include <linux/kernel.h>
     36#include <linux/module.h>
     37#include <linux/platform_data/x86/soc.h>
     38#include <linux/platform_device.h>
     39#include <linux/slab.h>
     40#include <linux/suspend.h>
     41
     42#define DRV_NAME			"INT0002 Virtual GPIO"
     43
     44/* For some reason the virtual GPIO pin tied to the GPE is numbered pin 2 */
     45#define GPE0A_PME_B0_VIRT_GPIO_PIN	2
     46
     47#define GPE0A_PME_B0_STS_BIT		BIT(13)
     48#define GPE0A_PME_B0_EN_BIT		BIT(13)
     49#define GPE0A_STS_PORT			0x420
     50#define GPE0A_EN_PORT			0x428
     51
     52struct int0002_data {
     53	struct gpio_chip chip;
     54	int parent_irq;
     55	int wake_enable_count;
     56};
     57
     58/*
     59 * As this is not a real GPIO at all, but just a hack to model an event in
     60 * ACPI the get / set functions are dummy functions.
     61 */
     62
     63static int int0002_gpio_get(struct gpio_chip *chip, unsigned int offset)
     64{
     65	return 0;
     66}
     67
     68static void int0002_gpio_set(struct gpio_chip *chip, unsigned int offset,
     69			     int value)
     70{
     71}
     72
     73static int int0002_gpio_direction_output(struct gpio_chip *chip,
     74					 unsigned int offset, int value)
     75{
     76	return 0;
     77}
     78
     79static void int0002_irq_ack(struct irq_data *data)
     80{
     81	outl(GPE0A_PME_B0_STS_BIT, GPE0A_STS_PORT);
     82}
     83
     84static void int0002_irq_unmask(struct irq_data *data)
     85{
     86	u32 gpe_en_reg;
     87
     88	gpe_en_reg = inl(GPE0A_EN_PORT);
     89	gpe_en_reg |= GPE0A_PME_B0_EN_BIT;
     90	outl(gpe_en_reg, GPE0A_EN_PORT);
     91}
     92
     93static void int0002_irq_mask(struct irq_data *data)
     94{
     95	u32 gpe_en_reg;
     96
     97	gpe_en_reg = inl(GPE0A_EN_PORT);
     98	gpe_en_reg &= ~GPE0A_PME_B0_EN_BIT;
     99	outl(gpe_en_reg, GPE0A_EN_PORT);
    100}
    101
    102static int int0002_irq_set_wake(struct irq_data *data, unsigned int on)
    103{
    104	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
    105	struct int0002_data *int0002 = container_of(chip, struct int0002_data, chip);
    106
    107	/*
    108	 * Applying of the wakeup flag to our parent IRQ is delayed till system
    109	 * suspend, because we only want to do this when using s2idle.
    110	 */
    111	if (on)
    112		int0002->wake_enable_count++;
    113	else
    114		int0002->wake_enable_count--;
    115
    116	return 0;
    117}
    118
    119static irqreturn_t int0002_irq(int irq, void *data)
    120{
    121	struct gpio_chip *chip = data;
    122	u32 gpe_sts_reg;
    123
    124	gpe_sts_reg = inl(GPE0A_STS_PORT);
    125	if (!(gpe_sts_reg & GPE0A_PME_B0_STS_BIT))
    126		return IRQ_NONE;
    127
    128	generic_handle_irq(irq_find_mapping(chip->irq.domain,
    129					    GPE0A_PME_B0_VIRT_GPIO_PIN));
    130
    131	pm_wakeup_hard_event(chip->parent);
    132
    133	return IRQ_HANDLED;
    134}
    135
    136static bool int0002_check_wake(void *data)
    137{
    138	u32 gpe_sts_reg;
    139
    140	gpe_sts_reg = inl(GPE0A_STS_PORT);
    141	return (gpe_sts_reg & GPE0A_PME_B0_STS_BIT);
    142}
    143
    144static struct irq_chip int0002_irqchip = {
    145	.name			= DRV_NAME,
    146	.irq_ack		= int0002_irq_ack,
    147	.irq_mask		= int0002_irq_mask,
    148	.irq_unmask		= int0002_irq_unmask,
    149	.irq_set_wake		= int0002_irq_set_wake,
    150};
    151
    152static void int0002_init_irq_valid_mask(struct gpio_chip *chip,
    153					unsigned long *valid_mask,
    154					unsigned int ngpios)
    155{
    156	bitmap_clear(valid_mask, 0, GPE0A_PME_B0_VIRT_GPIO_PIN);
    157}
    158
    159static int int0002_probe(struct platform_device *pdev)
    160{
    161	struct device *dev = &pdev->dev;
    162	struct int0002_data *int0002;
    163	struct gpio_irq_chip *girq;
    164	struct gpio_chip *chip;
    165	int irq, ret;
    166
    167	/* Menlow has a different INT0002 device? <sigh> */
    168	if (!soc_intel_is_byt() && !soc_intel_is_cht())
    169		return -ENODEV;
    170
    171	irq = platform_get_irq(pdev, 0);
    172	if (irq < 0)
    173		return irq;
    174
    175	int0002 = devm_kzalloc(dev, sizeof(*int0002), GFP_KERNEL);
    176	if (!int0002)
    177		return -ENOMEM;
    178
    179	int0002->parent_irq = irq;
    180
    181	chip = &int0002->chip;
    182	chip->label = DRV_NAME;
    183	chip->parent = dev;
    184	chip->owner = THIS_MODULE;
    185	chip->get = int0002_gpio_get;
    186	chip->set = int0002_gpio_set;
    187	chip->direction_input = int0002_gpio_get;
    188	chip->direction_output = int0002_gpio_direction_output;
    189	chip->base = -1;
    190	chip->ngpio = GPE0A_PME_B0_VIRT_GPIO_PIN + 1;
    191	chip->irq.init_valid_mask = int0002_init_irq_valid_mask;
    192
    193	/*
    194	 * We directly request the irq here instead of passing a flow-handler
    195	 * to gpiochip_set_chained_irqchip, because the irq is shared.
    196	 * FIXME: augment this if we managed to pull handling of shared
    197	 * IRQs into gpiolib.
    198	 */
    199	ret = devm_request_irq(dev, irq, int0002_irq,
    200			       IRQF_SHARED, "INT0002", chip);
    201	if (ret) {
    202		dev_err(dev, "Error requesting IRQ %d: %d\n", irq, ret);
    203		return ret;
    204	}
    205
    206	girq = &chip->irq;
    207	girq->chip = &int0002_irqchip;
    208	/* This let us handle the parent IRQ in the driver */
    209	girq->parent_handler = NULL;
    210	girq->num_parents = 0;
    211	girq->parents = NULL;
    212	girq->default_type = IRQ_TYPE_NONE;
    213	girq->handler = handle_edge_irq;
    214
    215	ret = devm_gpiochip_add_data(dev, chip, NULL);
    216	if (ret) {
    217		dev_err(dev, "Error adding gpio chip: %d\n", ret);
    218		return ret;
    219	}
    220
    221	acpi_register_wakeup_handler(irq, int0002_check_wake, NULL);
    222	device_init_wakeup(dev, true);
    223	dev_set_drvdata(dev, int0002);
    224	return 0;
    225}
    226
    227static int int0002_remove(struct platform_device *pdev)
    228{
    229	device_init_wakeup(&pdev->dev, false);
    230	acpi_unregister_wakeup_handler(int0002_check_wake, NULL);
    231	return 0;
    232}
    233
    234static int int0002_suspend(struct device *dev)
    235{
    236	struct int0002_data *int0002 = dev_get_drvdata(dev);
    237
    238	/*
    239	 * The INT0002 parent IRQ is often shared with the ACPI GPE IRQ, don't
    240	 * muck with it when firmware based suspend is used, otherwise we may
    241	 * cause spurious wakeups from firmware managed suspend.
    242	 */
    243	if (!pm_suspend_via_firmware() && int0002->wake_enable_count)
    244		enable_irq_wake(int0002->parent_irq);
    245
    246	return 0;
    247}
    248
    249static int int0002_resume(struct device *dev)
    250{
    251	struct int0002_data *int0002 = dev_get_drvdata(dev);
    252
    253	if (!pm_suspend_via_firmware() && int0002->wake_enable_count)
    254		disable_irq_wake(int0002->parent_irq);
    255
    256	return 0;
    257}
    258
    259static const struct dev_pm_ops int0002_pm_ops = {
    260	.suspend = int0002_suspend,
    261	.resume = int0002_resume,
    262};
    263
    264static const struct acpi_device_id int0002_acpi_ids[] = {
    265	{ "INT0002", 0 },
    266	{ },
    267};
    268MODULE_DEVICE_TABLE(acpi, int0002_acpi_ids);
    269
    270static struct platform_driver int0002_driver = {
    271	.driver = {
    272		.name			= DRV_NAME,
    273		.acpi_match_table	= int0002_acpi_ids,
    274		.pm			= &int0002_pm_ops,
    275	},
    276	.probe	= int0002_probe,
    277	.remove	= int0002_remove,
    278};
    279
    280module_platform_driver(int0002_driver);
    281
    282MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
    283MODULE_DESCRIPTION("Intel INT0002 Virtual GPIO driver");
    284MODULE_LICENSE("GPL v2");