Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.17-rc6 219 lines 5.9 kB view raw
1/* 2 * Intel INT0002 "Virtual GPIO" driver 3 * 4 * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com> 5 * 6 * Loosely based on android x86 kernel code which is: 7 * 8 * Copyright (c) 2014, Intel Corporation. 9 * 10 * Author: Dyut Kumar Sil <dyut.k.sil@intel.com> 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License version 2 as 14 * published by the Free Software Foundation. 15 * 16 * Some peripherals on Bay Trail and Cherry Trail platforms signal a Power 17 * Management Event (PME) to the Power Management Controller (PMC) to wakeup 18 * the system. When this happens software needs to clear the PME bus 0 status 19 * bit in the GPE0a_STS register to avoid an IRQ storm on IRQ 9. 20 * 21 * This is modelled in ACPI through the INT0002 ACPI device, which is 22 * called a "Virtual GPIO controller" in ACPI because it defines the event 23 * handler to call when the PME triggers through _AEI and _L02 / _E02 24 * methods as would be done for a real GPIO interrupt in ACPI. Note this 25 * is a hack to define an AML event handler for the PME while using existing 26 * ACPI mechanisms, this is not a real GPIO at all. 27 * 28 * This driver will bind to the INT0002 device, and register as a GPIO 29 * controller, letting gpiolib-acpi.c call the _L02 handler as it would 30 * for a real GPIO controller. 31 */ 32 33#include <linux/acpi.h> 34#include <linux/bitmap.h> 35#include <linux/gpio/driver.h> 36#include <linux/interrupt.h> 37#include <linux/io.h> 38#include <linux/kernel.h> 39#include <linux/module.h> 40#include <linux/platform_device.h> 41#include <linux/slab.h> 42#include <linux/suspend.h> 43 44#include <asm/cpu_device_id.h> 45#include <asm/intel-family.h> 46 47#define DRV_NAME "INT0002 Virtual GPIO" 48 49/* For some reason the virtual GPIO pin tied to the GPE is numbered pin 2 */ 50#define GPE0A_PME_B0_VIRT_GPIO_PIN 2 51 52#define GPE0A_PME_B0_STS_BIT BIT(13) 53#define GPE0A_PME_B0_EN_BIT BIT(13) 54#define GPE0A_STS_PORT 0x420 55#define GPE0A_EN_PORT 0x428 56 57#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, } 58 59static const struct x86_cpu_id int0002_cpu_ids[] = { 60/* 61 * Limit ourselves to Cherry Trail for now, until testing shows we 62 * need to handle the INT0002 device on Baytrail too. 63 * ICPU(INTEL_FAM6_ATOM_SILVERMONT1), * Valleyview, Bay Trail * 64 */ 65 ICPU(INTEL_FAM6_ATOM_AIRMONT), /* Braswell, Cherry Trail */ 66 {} 67}; 68 69/* 70 * As this is not a real GPIO at all, but just a hack to model an event in 71 * ACPI the get / set functions are dummy functions. 72 */ 73 74static int int0002_gpio_get(struct gpio_chip *chip, unsigned int offset) 75{ 76 return 0; 77} 78 79static void int0002_gpio_set(struct gpio_chip *chip, unsigned int offset, 80 int value) 81{ 82} 83 84static int int0002_gpio_direction_output(struct gpio_chip *chip, 85 unsigned int offset, int value) 86{ 87 return 0; 88} 89 90static void int0002_irq_ack(struct irq_data *data) 91{ 92 outl(GPE0A_PME_B0_STS_BIT, GPE0A_STS_PORT); 93} 94 95static void int0002_irq_unmask(struct irq_data *data) 96{ 97 u32 gpe_en_reg; 98 99 gpe_en_reg = inl(GPE0A_EN_PORT); 100 gpe_en_reg |= GPE0A_PME_B0_EN_BIT; 101 outl(gpe_en_reg, GPE0A_EN_PORT); 102} 103 104static void int0002_irq_mask(struct irq_data *data) 105{ 106 u32 gpe_en_reg; 107 108 gpe_en_reg = inl(GPE0A_EN_PORT); 109 gpe_en_reg &= ~GPE0A_PME_B0_EN_BIT; 110 outl(gpe_en_reg, GPE0A_EN_PORT); 111} 112 113static irqreturn_t int0002_irq(int irq, void *data) 114{ 115 struct gpio_chip *chip = data; 116 u32 gpe_sts_reg; 117 118 gpe_sts_reg = inl(GPE0A_STS_PORT); 119 if (!(gpe_sts_reg & GPE0A_PME_B0_STS_BIT)) 120 return IRQ_NONE; 121 122 generic_handle_irq(irq_find_mapping(chip->irq.domain, 123 GPE0A_PME_B0_VIRT_GPIO_PIN)); 124 125 pm_system_wakeup(); 126 127 return IRQ_HANDLED; 128} 129 130static struct irq_chip int0002_irqchip = { 131 .name = DRV_NAME, 132 .irq_ack = int0002_irq_ack, 133 .irq_mask = int0002_irq_mask, 134 .irq_unmask = int0002_irq_unmask, 135}; 136 137static int int0002_probe(struct platform_device *pdev) 138{ 139 struct device *dev = &pdev->dev; 140 const struct x86_cpu_id *cpu_id; 141 struct gpio_chip *chip; 142 int irq, ret; 143 144 /* Menlow has a different INT0002 device? <sigh> */ 145 cpu_id = x86_match_cpu(int0002_cpu_ids); 146 if (!cpu_id) 147 return -ENODEV; 148 149 irq = platform_get_irq(pdev, 0); 150 if (irq < 0) { 151 dev_err(dev, "Error getting IRQ: %d\n", irq); 152 return irq; 153 } 154 155 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); 156 if (!chip) 157 return -ENOMEM; 158 159 chip->label = DRV_NAME; 160 chip->parent = dev; 161 chip->owner = THIS_MODULE; 162 chip->get = int0002_gpio_get; 163 chip->set = int0002_gpio_set; 164 chip->direction_input = int0002_gpio_get; 165 chip->direction_output = int0002_gpio_direction_output; 166 chip->base = -1; 167 chip->ngpio = GPE0A_PME_B0_VIRT_GPIO_PIN + 1; 168 chip->irq.need_valid_mask = true; 169 170 ret = devm_gpiochip_add_data(&pdev->dev, chip, NULL); 171 if (ret) { 172 dev_err(dev, "Error adding gpio chip: %d\n", ret); 173 return ret; 174 } 175 176 bitmap_clear(chip->irq.valid_mask, 0, GPE0A_PME_B0_VIRT_GPIO_PIN); 177 178 /* 179 * We manually request the irq here instead of passing a flow-handler 180 * to gpiochip_set_chained_irqchip, because the irq is shared. 181 */ 182 ret = devm_request_irq(dev, irq, int0002_irq, 183 IRQF_SHARED, "INT0002", chip); 184 if (ret) { 185 dev_err(dev, "Error requesting IRQ %d: %d\n", irq, ret); 186 return ret; 187 } 188 189 ret = gpiochip_irqchip_add(chip, &int0002_irqchip, 0, handle_edge_irq, 190 IRQ_TYPE_NONE); 191 if (ret) { 192 dev_err(dev, "Error adding irqchip: %d\n", ret); 193 return ret; 194 } 195 196 gpiochip_set_chained_irqchip(chip, &int0002_irqchip, irq, NULL); 197 198 return 0; 199} 200 201static const struct acpi_device_id int0002_acpi_ids[] = { 202 { "INT0002", 0 }, 203 { }, 204}; 205MODULE_DEVICE_TABLE(acpi, int0002_acpi_ids); 206 207static struct platform_driver int0002_driver = { 208 .driver = { 209 .name = DRV_NAME, 210 .acpi_match_table = int0002_acpi_ids, 211 }, 212 .probe = int0002_probe, 213}; 214 215module_platform_driver(int0002_driver); 216 217MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 218MODULE_DESCRIPTION("Intel INT0002 Virtual GPIO driver"); 219MODULE_LICENSE("GPL");