From: "Chang, Rebecca Swee Fun" <[email protected]>
The GPIO pins are divided into 3 clusters, namely SOUTH,SUS and NORTH. Signed-off-by: Chang, Rebecca Swee Fun <[email protected]> --- ...upport-for-Intel-Baytrail-GPIO-controller.patch | 904 ++++++++++++++++++++ 1 file changed, 904 insertions(+) create mode 100644 meta/cfg/kernel-cache/features/valleyisland-io/0002-gpio-add-support-for-Intel-Baytrail-GPIO-controller.patch diff --git a/meta/cfg/kernel-cache/features/valleyisland-io/0002-gpio-add-support-for-Intel-Baytrail-GPIO-controller.patch b/meta/cfg/kernel-cache/features/valleyisland-io/0002-gpio-add-support-for-Intel-Baytrail-GPIO-controller.patch new file mode 100644 index 0000000..c6ae730 --- /dev/null +++ b/meta/cfg/kernel-cache/features/valleyisland-io/0002-gpio-add-support-for-Intel-Baytrail-GPIO-controller.patch @@ -0,0 +1,904 @@ +gpio: add support for Intel Baytrail GPIO controller + +The GPIO pins are divided into 3 clusters, namely SOUTH,SUS and NORTH. + +Signed-off-by: Chang, Rebecca Swee Fun <[email protected]> +--- + drivers/gpio/Kconfig | 14 +- + drivers/gpio/Makefile | 2 + + drivers/gpio/gpio-byt-device.c | 157 ++++++++++ + drivers/gpio/gpio-byt.c | 622 ++++++++++++++++++++++++++++++++++++++++ + include/linux/acpi_gpio.h | 4 + + include/linux/gpio-byt.h | 16 ++ + 6 files changed, 814 insertions(+), 1 deletion(-) + create mode 100644 drivers/gpio/gpio-byt-device.c + create mode 100644 drivers/gpio/gpio-byt.c + create mode 100644 include/linux/gpio-byt.h + +diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig +index 682de75..061f91b 100644 +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -224,6 +224,19 @@ config GPIO_TS5500 + blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600 + LCD port. + ++config GPIO_BYT ++ bool "Intel BYT GPIO support" ++ select IRQ_DOMAIN ++ help ++ Driver for memory mapped GPIO functionality on Intel BYT chipset. ++ Requires ACPI5 framework or GPIO_BYT_DEVICE to setup GPIO devices. ++ ++config GPIO_BYT_DEVICE ++ bool "Intel BYT GPIO Platform Device Emulation" ++ depends on GPIO_BYT ++ help ++ This is for generating BYT GPIO platform devices for Intel BYT where ACPI5 framework is absent. ++ + config GPIO_VT8500 + bool "VIA/Wondermedia SoC GPIO Support" + depends on ARCH_VT8500 +@@ -685,7 +698,6 @@ config GPIO_MSIC + intel MID devices + + comment "USB GPIO expanders:" +- + config GPIO_VIPERBOARD + tristate "Viperboard GPIO a & b support" + depends on MFD_VIPERBOARD && USB +diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile +index c5aebd0..338f7c4 100644 +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -76,6 +76,8 @@ obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o + obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o + obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o + obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o ++obj-$(CONFIG_GPIO_BYT) += gpio-byt.o ++obj-$(CONFIG_GPIO_BYT_DEVICE) += gpio-byt-device.o + obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o + obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o + obj-$(CONFIG_GPIO_VT8500) += gpio-vt8500.o +diff --git a/drivers/gpio/gpio-byt-device.c b/drivers/gpio/gpio-byt-device.c +new file mode 100644 +index 0000000..4cc1459 +--- /dev/null ++++ b/drivers/gpio/gpio-byt-device.c +@@ -0,0 +1,157 @@ ++/* ++ * platform_byt_gpio.c: GPIO Platform Device ++ * ++ * (C) Copyright 2008 Intel Corporation ++ * Author: Kean Ho, Chew ([email protected]) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; version 2 ++ * of the License. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/types.h> ++#include <linux/bitops.h> ++#include <linux/interrupt.h> ++#include <linux/platform_device.h> ++#include <linux/seq_file.h> ++#include <linux/pci.h> ++#include <linux/gpio-byt.h> ++ ++/* PCI Memory Base Access */ ++#define PCI_DEVICE_ID_INTEL_BYT_PCU 0x0f1c ++#define NO_REGISTER_SETTINGS (BIT(0) | BIT(1) | BIT(2)) ++ ++/* Offsets */ ++#define SCORE_OFFSET 0x0 ++#define NCORE_OFFSET 0x1000 ++#define SUS_OFFSET 0x2000 ++#define SCORE_END 0x72C ++#define NCORE_END 0x970 ++#define SUS_END 0x98C ++ ++static struct byt_gpio_port byt_gpio_score_platform_data = { ++ .unique_id = "1", ++}; ++ ++static struct resource byt_gpio_score_resources[] = { ++ { ++ .start = 0x0, ++ .end = 0x0, ++ .flags = IORESOURCE_MEM, ++ .name = "io-memory", ++ }, ++ { ++ .start = 49, ++ .end = 49, ++ .flags = IORESOURCE_IRQ, ++ .name = "irq", ++ } ++}; ++ ++static struct byt_gpio_port byt_gpio_ncore_platform_data = { ++ .unique_id = "2", ++}; ++ ++static struct resource byt_gpio_ncore_resources[] = { ++ { ++ .start = 0x0, ++ .end = 0x0, ++ .flags = IORESOURCE_MEM, ++ .name = "io-memory", ++ }, ++ { ++ .start = 48, ++ .end = 48, ++ .flags = IORESOURCE_IRQ, ++ .name = "irq", ++ } ++}; ++ ++static struct byt_gpio_port byt_gpio_sus_platform_data = { ++ .unique_id = "3", ++}; ++ ++static struct resource byt_gpio_sus_resources[] = { ++ { ++ .start = 0x0, ++ .end = 0x0, ++ .flags = IORESOURCE_MEM, ++ .name = "io-memory", ++ }, ++ { ++ .start = 50, ++ .end = 50, ++ .flags = IORESOURCE_IRQ, ++ .name = "irq", ++ } ++}; ++ ++static struct platform_device byt_gpio_score_device = { ++ .name = "byt_gpio", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(byt_gpio_score_resources), ++ .resource = byt_gpio_score_resources, ++ .dev = { ++ .platform_data = &byt_gpio_score_platform_data, ++ } ++}; ++ ++static struct platform_device byt_gpio_ncore_device = { ++ .name = "byt_gpio", ++ .id = 1, ++ .num_resources = ARRAY_SIZE(byt_gpio_ncore_resources), ++ .resource = byt_gpio_ncore_resources, ++ .dev = { ++ .platform_data = &byt_gpio_ncore_platform_data, ++ } ++}; ++ ++static struct platform_device byt_gpio_sus_device = { ++ .name = "byt_gpio", ++ .id = 2, ++ .num_resources = ARRAY_SIZE(byt_gpio_sus_resources), ++ .resource = byt_gpio_sus_resources, ++ .dev = { ++ .platform_data = &byt_gpio_sus_platform_data, ++ } ++}; ++ ++static struct platform_device *devices[] __initdata = { ++ &byt_gpio_score_device, ++ &byt_gpio_ncore_device, ++ &byt_gpio_sus_device, ++}; ++ ++static int __init get_pci_memory_init(void) ++{ ++ u32 io_base_add; ++ struct pci_dev *pci_dev; ++ pci_dev = pci_get_device(PCI_VENDOR_ID_INTEL, ++ PCI_DEVICE_ID_INTEL_BYT_PCU, ++ NULL); ++ ++ if (pci_dev == NULL) { ++ return -EFAULT; ++ }; ++ pci_read_config_dword(pci_dev, 0x4c, &io_base_add); ++ io_base_add &= ~NO_REGISTER_SETTINGS; ++ byt_gpio_score_resources[0].start = io_base_add + SCORE_OFFSET; ++ byt_gpio_score_resources[0].end = io_base_add + SCORE_OFFSET + SCORE_END; ++ byt_gpio_ncore_resources[0].start = io_base_add + NCORE_OFFSET; ++ byt_gpio_ncore_resources[0].end = io_base_add + NCORE_OFFSET + NCORE_END; ++ byt_gpio_sus_resources[0].start = io_base_add + SUS_OFFSET; ++ byt_gpio_sus_resources[0].end = io_base_add + SUS_OFFSET + SUS_END; ++ return 0; ++}; ++rootfs_initcall(get_pci_memory_init); ++ ++ ++static int __init byt_gpio_device_init(void) ++{ ++ return platform_add_devices(devices, ARRAY_SIZE(devices)); ++}; ++device_initcall(byt_gpio_device_init); +diff --git a/drivers/gpio/gpio-byt.c b/drivers/gpio/gpio-byt.c +new file mode 100644 +index 0000000..428c3eb +--- /dev/null ++++ b/drivers/gpio/gpio-byt.c +@@ -0,0 +1,622 @@ ++/* ++ * GPIO driver for Intel BYT ++ * Copyright (c) 2012, Intel Corporation. ++ * ++ * Author: Mathias Nyman <[email protected]> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/types.h> ++#include <linux/bitops.h> ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++#include <linux/gpio.h> ++#include <linux/irqdomain.h> ++#include <linux/acpi.h> ++#include <linux/acpi_gpio.h> ++#include <linux/platform_device.h> ++#include <linux/seq_file.h> ++#include <linux/io.h> ++#include <linux/pm_runtime.h> ++#include <linux/gpio-byt.h> ++ ++/* memory mapped register offsets */ ++#define BYT_CONF0_REG 0x000 ++#define BYT_CONF1_REG 0x004 ++#define BYT_VAL_REG 0x008 ++#define BYT_DFT_REG 0x00c ++#define BYT_INT_STAT_REG 0x800 ++ ++/* BYT_CONF0_REG register bits */ ++#define BYT_TRIG_NEG BIT(26) ++#define BYT_TRIG_POS BIT(25) ++#define BYT_TRIG_LVL BIT(24) ++#define BYT_PIN_MUX 0x07 ++#define BYT_GPIO_FUNC 0x01 ++ ++/* BYT_VAL_REG register bits */ ++#define BYT_INPUT_EN BIT(2) /*(active low)*/ ++#define BYT_OUTPUT_EN (BIT(1) | BIT(2)) /*(active low)*/ ++#define BYT_LEVEL BIT(0) ++ ++#define BYT_DIR_MASK (BIT(1) | BIT(2)) ++#define BYT_TRIG_MASK (BIT(26) | BIT(25) | BIT(24)) ++ ++#define BYT_NGPIO_SCORE 102 ++#define BYT_NGPIO_NCORE 28 ++#define BYT_NGPIO_SUS 44 ++ ++/* ++ * Baytrail gpio controller consist of three separate sub-controllers called ++ * SCORE, NCORE and SUS. The sub-controllers are identified by their acpi UID. ++ * ++ * GPIO numbering is _not_ ordered meaning that gpio # 0 in ACPI namespace does ++ * _not_ correspond to the first gpio register at controller's gpio base. ++ * There is no logic or pattern in mapping gpio numbers to registers (pads) so ++ * each sub-controller needs to have its own mapping table ++ */ ++ ++static unsigned score_gpio_to_pad[BYT_NGPIO_SCORE] = { ++ 85, 89, 93, 96, 99, 102, 98, 101, 34, 37, ++ 36, 38, 39, 35, 40, 84, 62, 61, 64, 59, ++ 54, 56, 60, 55, 63, 57, 51, 50, 53, 47, ++ 52, 49, 48, 43, 46, 41, 45, 42, 58, 44, ++ 95, 105, 70, 68, 67, 66, 69, 71, 65, 72, ++ 86, 90, 88, 92, 103, 77, 79, 83, 78, 81, ++ 80, 82, 13, 12, 15, 14, 17, 18, 19, 16, ++ 2, 1, 0, 4, 6, 7, 9, 8, 33, 32, ++ 31, 30, 29, 27, 25, 28, 26, 23, 21, 20, ++ 24, 22, 5, 3, 10, 11, 106, 87, 91, 104, ++ 97, 100, ++}; ++ ++static unsigned ncore_gpio_to_pad[BYT_NGPIO_NCORE] = { ++ 19, 18, 17, 20, 21, 22, 24, 25, 23, 16, ++ 14, 15, 12, 26, 27, 1, 4, 8, 11, 0, ++ 3, 6, 10, 13, 2, 5, 9, 7, ++}; ++ ++static unsigned sus_gpio_to_pad[BYT_NGPIO_SUS] = { ++ 29, 33, 30, 31, 32, 34, 36, 35, 38, 37, ++ 18, 7, 11, 20, 17, 1, 8, 10, 19, 12, ++ 0, 2, 23, 39, 28, 27, 22, 21, 24, 25, ++ 26, 51, 56, 54, 49, 55, 48, 57, 50, 58, ++ 52, 53, 59, 40, ++}; ++ ++struct gpio_bank { ++ char *uid; /* acpi _UID */ ++ int ngpio; ++ unsigned *to_pad; ++}; ++ ++static struct gpio_bank byt_banks[] = { ++ { ++ .uid = "1", ++ .ngpio = BYT_NGPIO_SCORE, ++ .to_pad = score_gpio_to_pad, ++ }, ++ { ++ .uid = "2", ++ .ngpio = BYT_NGPIO_NCORE, ++ .to_pad = ncore_gpio_to_pad, ++ }, ++ { ++ .uid = "3", ++ .ngpio = BYT_NGPIO_SUS, ++ .to_pad = sus_gpio_to_pad, ++ }, ++ { ++ }, ++}; ++ ++struct byt_gpio { ++ struct gpio_chip chip; ++ struct irq_domain *domain; ++ struct platform_device *pdev; ++ spinlock_t lock; ++ void __iomem *reg_base; ++ unsigned *gpio_to_pad; ++ unsigned hwirq; ++}; ++ ++static void __iomem *byt_gpio_reg(struct gpio_chip *chip, unsigned offset, ++ int reg) ++{ ++ struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); ++ u32 reg_offset; ++ void __iomem *ptr; ++ ++ if (reg == BYT_INT_STAT_REG) ++ reg_offset = (offset / 32) * 4; ++ else ++ reg_offset = vg->gpio_to_pad[offset] * 16; ++ ++ ptr = (void __iomem *) (vg->reg_base + reg_offset + reg); ++ return ptr; ++} ++ ++static int byt_gpio_request(struct gpio_chip *chip, unsigned offset) ++{ ++ struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); ++ void __iomem *reg = byt_gpio_reg(chip, offset, BYT_CONF0_REG); ++ u32 value; ++ ++ value = readl(reg); ++ if (!strcmp(chip->label, "byt_gpio.2") && offset >= 11 && offset <= 21){ ++ writel(value | BYT_GPIO_FUNC, reg); ++ } else { ++ writel(value & ~BYT_GPIO_FUNC, reg); ++ } ++ ++ pm_runtime_get(&vg->pdev->dev); ++ return 0; ++/* ++ * Current Implemntation: Option 2 ++ * ++ * Policy about what should be done when requesting a gpio is unclear. ++ * In most cases PIN MUX 000 means gpio function, with the exception of SUS ++ * core pins 11-21 where gpio is mux 001. ++ * ++ * Some pins are set by bios to a non-gpio mux, but still marked as gpio ++ * resource in acpi tables, and they work just as they should when not touching ++ * the pin muxing. (For example mmc card detect switch) ++ * ++ * option 1, check pin mux is "gpio", else fail (FIXME gpio SUS pins 11-21): ++ * if (value) ++ * return -EINVAL; ++ * ++ * option 2, force pin mux to gpio mode (FIXME gpio SUS pins 11-21): ++ * writel(value & ~BYT_PIN_MUX, reg); ++ * ++ * option 3: don't touch the pinmuxing at all, let BIOS handle it ++ */ ++} ++ ++static void byt_gpio_free(struct gpio_chip *chip, unsigned offset) ++{ ++ struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); ++ void __iomem *reg = byt_gpio_reg(chip, offset, BYT_CONF0_REG); ++ u32 value; ++ unsigned int virq; ++ ++ value = readl(reg); ++ value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); ++ ++ if (!strcmp(chip->label, "byt_gpio.2") && offset >= 11 && offset <= 21){ ++ writel(value & ~BYT_GPIO_FUNC, reg); ++ } else { ++ writel(value | BYT_GPIO_FUNC, reg); ++ } ++ writel(value, reg); ++ virq = irq_find_mapping(vg->domain, offset); ++ irq_dispose_mapping(virq); ++ pm_runtime_put(&vg->pdev->dev); ++} ++ ++static int byt_gpio_get(struct gpio_chip *chip, unsigned offset) ++{ ++ void __iomem *reg = byt_gpio_reg(chip, offset, BYT_VAL_REG); ++ return readl(reg) & BYT_LEVEL; ++} ++ ++static void byt_gpio_set(struct gpio_chip *chip, unsigned offset, int value) ++{ ++ struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); ++ void __iomem *reg = byt_gpio_reg(chip, offset, BYT_VAL_REG); ++ unsigned long flags; ++ u32 old_val; ++ ++ spin_lock_irqsave(&vg->lock, flags); ++ ++ old_val = readl(reg); ++ ++ if (value) ++ writel(old_val | BYT_LEVEL, reg); ++ else ++ writel(old_val & ~BYT_LEVEL, reg); ++ ++ spin_unlock_irqrestore(&vg->lock, flags); ++} ++ ++static int byt_gpio_direction_input(struct gpio_chip *chip, unsigned offset) ++{ ++ struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); ++ void __iomem *reg = byt_gpio_reg(chip, offset, BYT_VAL_REG); ++ unsigned long flags; ++ u32 value; ++ ++ spin_lock_irqsave(&vg->lock, flags); ++ ++ value = readl(reg) | BYT_DIR_MASK; ++ value &= ~BYT_INPUT_EN; /* active low */ ++ writel(value, reg); ++ ++ spin_unlock_irqrestore(&vg->lock, flags); ++ ++ return 0; ++} ++ ++static int byt_gpio_direction_output(struct gpio_chip *chip, ++ unsigned gpio, int value) ++{ ++ struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); ++ void __iomem *reg = byt_gpio_reg(chip, gpio, BYT_VAL_REG); ++ unsigned long flags; ++ u32 reg_val; ++ ++ spin_lock_irqsave(&vg->lock, flags); ++ ++ reg_val = readl(reg) | (BYT_DIR_MASK | !!value); ++ reg_val &= ~(BYT_OUTPUT_EN | !value); ++ writel(reg_val, reg); ++ ++ spin_unlock_irqrestore(&vg->lock, flags); ++ ++ return 0; ++} ++ ++static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) ++{ ++ struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); ++ int i; ++ unsigned long flags; ++ u32 conf0, val, offs; ++ ++ spin_lock_irqsave(&vg->lock, flags); ++ ++ for (i = 0; i < vg->chip.ngpio; i++) { ++ offs = vg->gpio_to_pad[i] * 16; ++ conf0 = readl(vg->reg_base + offs + BYT_CONF0_REG); ++ val = readl(vg->reg_base + offs + BYT_VAL_REG); ++ ++ seq_printf(s, " gpio-%-3d %s %s %s pad-%-3d offset:0x%03x " ++ "mux:%d %s %s %s\n", ++ i, ++ val & BYT_INPUT_EN ? " " : "in", ++ val & BYT_OUTPUT_EN ? " " : "out", ++ val & BYT_LEVEL ? "hi" : "lo", ++ vg->gpio_to_pad[i], offs, ++ conf0 & 0x7, ++ conf0 & BYT_TRIG_NEG ? "fall" : "", ++ conf0 & BYT_TRIG_POS ? "rise" : "", ++ conf0 & BYT_TRIG_LVL ? "lvl " : ""); ++ } ++ spin_unlock_irqrestore(&vg->lock, flags); ++} ++ ++static int byt_irq_type(struct irq_data *d, unsigned type) ++{ ++ struct byt_gpio *vg = irq_data_get_irq_chip_data(d); ++ u32 offset = irqd_to_hwirq(d); ++ u32 value; ++ unsigned long flags; ++ void __iomem *reg = byt_gpio_reg(&vg->chip, offset, BYT_CONF0_REG); ++ ++ if (offset >= vg->chip.ngpio) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&vg->lock, flags); ++ ++ /* For level triggers, the BYT_TRIG_POS and BYT_TRIG_NEG bits ++ * are used to indicate high and low level triggering ++ */ ++ value = readl(reg); ++ value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); ++ switch(type){ ++ case IRQ_TYPE_LEVEL_HIGH: ++ value |= BYT_TRIG_LVL; ++ case IRQ_TYPE_EDGE_RISING: ++ value |= BYT_TRIG_POS; ++ break; ++ case IRQ_TYPE_LEVEL_LOW: ++ value |= BYT_TRIG_LVL; ++ case IRQ_TYPE_EDGE_FALLING: ++ value |= BYT_TRIG_NEG; ++ break; ++ case IRQ_TYPE_EDGE_BOTH: ++ value |= (BYT_TRIG_NEG | BYT_TRIG_POS); ++ break; ++ } ++ writel(value, reg); ++ spin_unlock_irqrestore(&vg->lock, flags); ++ return 0; ++} ++ ++static int byt_gpio_to_irq(struct gpio_chip *chip, unsigned offset) ++{ ++ struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); ++ return irq_create_mapping(vg->domain, offset); ++} ++ ++static void byt_gpio_irq_handler(unsigned irq, struct irq_desc *desc) ++{ ++ struct irq_data *data = irq_desc_get_irq_data(desc); ++ struct byt_gpio *vg = irq_data_get_irq_handler_data(data); ++ struct irq_chip *chip = irq_data_get_irq_chip(data); ++ u32 base, pin, mask; ++ void __iomem *reg; ++ u32 pending; ++ unsigned virq; ++ int looplimit = 0; ++ ++ /* check from GPIO controller which pin triggered the interrupt */ ++ for (base = 0; base < vg->chip.ngpio; base += 32) { ++ reg = byt_gpio_reg(&vg->chip, base, BYT_INT_STAT_REG); ++ while ((pending = readl(reg))) { ++ pin = __ffs(pending); ++ mask = BIT(pin); ++ /* Clear before handling so we can't lose an edge */ ++ writel(mask, reg); ++ virq = irq_find_mapping(vg->domain, base + pin); ++ generic_handle_irq(virq); ++ ++ /* In case bios or user sets triggering incorrectly, a ++ * pin might remain in "interrupt triggered" state. ++ */ ++ if(looplimit++ > 32){ ++ dev_err(&vg->pdev->dev, ++ "GPIO %d interrupt flood, disabling\n", ++ base + pin); ++ reg = byt_gpio_reg(&vg->chip, base + pin, ++ BYT_CONF0_REG); ++ mask = readl(reg); ++ mask &= ~(BYT_TRIG_NEG | BYT_TRIG_POS | ++ BYT_TRIG_LVL); ++ writel(mask, reg); ++ mask = readl(reg); /* flush */ ++ break; ++ } ++ } ++ } ++ chip->irq_eoi(data); ++} ++ ++static void byt_irq_unmask(struct irq_data *d) ++{ ++} ++ ++static void byt_irq_mask(struct irq_data *d) ++{ ++} ++ ++static struct irq_chip byt_irqchip = { ++ .name = "BYT-GPIO", ++ .irq_mask = byt_irq_mask, ++ .irq_unmask = byt_irq_unmask, ++ .irq_set_type = byt_irq_type, ++}; ++ ++static void byt_gpio_irq_init_hw(struct byt_gpio *vg) ++{ ++ void __iomem *reg; ++ u32 base, value; ++ ++ /* clear interrupt status trigger registers */ ++ for (base = 0; base < vg->chip.ngpio; base += 32) { ++ reg = byt_gpio_reg(&vg->chip, base, BYT_INT_STAT_REG); ++ writel(~0x0, reg); ++ } ++ /* Scan through any GPIO pin interrupt(s) which is/are still ++ * firing. Disable interrupt feature if any pin is found to prevent ++ * booting issue. ++ */ ++ for (base = 0x0; base < 0xd; base += 0x04) { ++ reg = (void __iomem *) (vg->reg_base + BYT_INT_STAT_REG + base); ++ value = readl(reg); ++ if(value){ ++ dev_err(&vg->pdev->dev, "GPIO interrupts flood. " ++ "Pin misconfigured at BIOS/Bootloader. Offset:%xh" ++ " Int Status:0x%x\n", BYT_INT_STAT_REG + base, ++ value); ++ } ++ } ++} ++ ++static int byt_gpio_irq_map(struct irq_domain *d, unsigned int virq, ++ irq_hw_number_t hw) ++{ ++ struct byt_gpio *vg = d->host_data; ++ ++ irq_set_chip_and_handler_name(virq, &byt_irqchip, handle_simple_irq, ++ "demux"); ++ irq_set_chip_data(virq, vg); ++ irq_set_irq_type(virq, IRQ_TYPE_NONE); ++ return 0; ++} ++ ++static const struct irq_domain_ops byt_gpio_irq_ops = { ++ .map = byt_gpio_irq_map, ++}; ++ ++#ifdef CONFIG_GPIO_BYT_DEVICE ++static int byt_gpio_irq_enable(unsigned hwirq, struct platform_device *pdev) ++{ ++ struct io_apic_irq_attr irq_attr; ++ struct device *dev = &pdev->dev; ++ /* ++ * Since PCI BIOS is not able to provide IRQ mapping to ++ * IRQ24 and onward, we need register to ioapic directly ++ * and hardcode pci->irq= hwirq ++ */ ++ irq_attr.ioapic = mp_find_ioapic(hwirq); ++ if(irq_attr.ioapic<0){ ++ printk(KERN_ERR "ERROR: No IOAPIC for IRQ=%d DID=0x%x \n", ++ hwirq, (unsigned int) dev); ++ return irq_attr.ioapic; ++ } ++ irq_attr.ioapic_pin = hwirq; ++ irq_attr.trigger = 1; /* level */ ++ irq_attr.polarity = 1; /* active low */ ++ io_apic_set_pci_routing(dev, hwirq, &irq_attr); ++ return 0; ++ ++} ++#endif ++ ++static int byt_gpio_probe(struct platform_device *pdev) ++{ ++ struct byt_gpio *vg; ++ struct gpio_chip *gc; ++ struct resource *mem_rc, *irq_rc; ++ struct device *dev = &pdev->dev; ++ struct gpio_bank *bank; ++ int ret; ++ ++#ifdef CONFIG_GPIO_BYT_DEVICE ++ struct byt_gpio_port *platform_data = dev->platform_data; ++#else ++ struct acpi_device *acpi_dev; ++ acpi_handle handle = ACPI_HANDLE(dev); ++ ++ if (acpi_bus_get_device(handle, &acpi_dev)) ++ return -ENODEV; ++#endif ++ vg = devm_kzalloc(dev, sizeof(struct byt_gpio), GFP_KERNEL); ++ if (!vg) { ++ dev_err(&pdev->dev, "can't allocate byt_gpio chip data\n"); ++ return -ENOMEM; ++ } ++ for (bank = byt_banks; bank->uid; bank++) { ++#ifdef CONFIG_GPIO_BYT_DEVICE ++ if (!strcmp(platform_data->unique_id, bank->uid)){ ++#else ++ if (!strcmp(acpi_dev->pnp.unique_id, bank->uid)){ ++#endif ++ vg->chip.ngpio = bank->ngpio; ++ vg->gpio_to_pad = bank->to_pad; ++ break; ++ } ++ } ++ if (!vg->chip.ngpio || !vg->gpio_to_pad) ++ return -ENODEV; ++ ++ vg->pdev = pdev; ++ platform_set_drvdata(pdev, vg); ++ ++ mem_rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ vg->reg_base = devm_request_and_ioremap(dev, mem_rc); ++ if (vg->reg_base == NULL) { ++ return -EFAULT; ++ } ++ ++ spin_lock_init(&vg->lock); ++ ++ gc = &vg->chip; ++ gc->label = dev_name(&pdev->dev); ++ gc->owner = THIS_MODULE; ++ gc->request = byt_gpio_request; ++ gc->free = byt_gpio_free; ++ gc->direction_input = byt_gpio_direction_input; ++ gc->direction_output = byt_gpio_direction_output; ++ gc->get = byt_gpio_get; ++ gc->set = byt_gpio_set; ++ gc->dbg_show = byt_gpio_dbg_show; ++ gc->base = -1; ++ gc->can_sleep = 0; ++ gc->dev = dev; ++ ++ ret = gpiochip_add(gc); ++ if (ret) { ++ dev_err(&pdev->dev, "failed adding byt-gpio chip\n"); ++ return ret; ++ } ++ ++ byt_gpio_irq_init_hw(vg); ++ /* set up interrupts */ ++ irq_rc = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (irq_rc && irq_rc->start) { ++ vg->hwirq = irq_rc->start; ++ gc->to_irq = byt_gpio_to_irq; ++#ifdef CONFIG_GPIO_BYT_DEVICE ++ ret = byt_gpio_irq_enable(vg->hwirq, pdev); ++ if (ret){ ++ dev_err(&pdev->dev, "failed to add GPIO to APIC\n"); ++ return ret; ++ } ++#endif ++ vg->domain = irq_domain_add_linear(NULL, gc->ngpio, ++ &byt_gpio_irq_ops, vg); ++ if (!vg->domain) ++ return -ENXIO; ++ irq_set_handler_data(vg->hwirq, vg); ++ irq_set_chained_handler(vg->hwirq, byt_gpio_irq_handler); ++#ifndef CONFIG_GPIO_BYT_DEVICE ++ /* Register interrupt handlers for gpio */ ++ acpi_gpiochip_request_interrupts(gc); ++#endif ++ } ++ pm_runtime_enable(dev); ++ return 0; ++} ++ ++static int byt_gpio_runtime_suspend(struct device *dev) ++{ ++ return 0; ++} ++ ++static int byt_gpio_runtime_resume(struct device *dev) ++{ ++ return 0; ++} ++ ++static const struct dev_pm_ops byt_gpio_pm_ops = { ++ .runtime_suspend = byt_gpio_runtime_suspend, ++ .runtime_resume = byt_gpio_runtime_resume, ++}; ++ ++#ifndef CONFIG_GPIO_BYT_DEVICE ++static const struct acpi_device_id byt_gpio_acpi_match[] = { ++ { "INT33B2", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(acpi, byt_gpio_acpi_match); ++#endif ++ ++static int byt_gpio_remove(struct platform_device *pdev) ++{ ++ struct byt_gpio *vg = platform_get_drvdata(pdev); ++ int err; ++ err = gpiochip_remove(&vg->chip); ++ if (err) ++ dev_warn(&pdev->dev, "failed to remove gpio_chip.\n"); ++ platform_set_drvdata(pdev, NULL); ++ return 0; ++} ++ ++static struct platform_driver byt_gpio_driver = { ++ .probe = byt_gpio_probe, ++ .remove = byt_gpio_remove, ++ .driver = { ++ .name = "byt_gpio", ++ .owner = THIS_MODULE, ++#ifndef CONFIG_GPIO_BYT_DEVICE ++ .pm = &byt_gpio_pm_ops, ++ .acpi_match_table = ACPI_PTR(byt_gpio_acpi_match), ++#endif ++ }, ++}; ++ ++static int __init byt_gpio_init(void) ++{ ++ return platform_driver_register(&byt_gpio_driver); ++} ++ ++subsys_initcall(byt_gpio_init); +diff --git a/include/linux/acpi_gpio.h b/include/linux/acpi_gpio.h +index 91615a3..b76ebd0 100644 +--- a/include/linux/acpi_gpio.h ++++ b/include/linux/acpi_gpio.h +@@ -2,10 +2,12 @@ + #define _LINUX_ACPI_GPIO_H_ + + #include <linux/errno.h> ++#include <linux/gpio.h> + + #ifdef CONFIG_GPIO_ACPI + + int acpi_get_gpio(char *path, int pin); ++void acpi_gpiochip_request_interrupts(struct gpio_chip *chip); + + #else /* CONFIG_GPIO_ACPI */ + +@@ -14,6 +16,8 @@ static inline int acpi_get_gpio(char *path, int pin) + return -ENODEV; + } + ++static inline void acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { } ++ + #endif /* CONFIG_GPIO_ACPI */ + + #endif /* _LINUX_ACPI_GPIO_H_ */ +diff --git a/include/linux/gpio-byt.h b/include/linux/gpio-byt.h +new file mode 100644 +index 0000000..7ebf1f4 +--- /dev/null ++++ b/include/linux/gpio-byt.h +@@ -0,0 +1,16 @@ ++/* ++ * platform_byt_gpio.h: BYT GPIO header file ++ * ++ * (C) Copyright 2008 Intel Corporation ++ * Author: ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; version 2 ++ * of the License. ++ */ ++#ifdef CONFIG_GPIO_BYT_DEVICE ++struct byt_gpio_port { ++ char * unique_id; ++}; ++#endif +-- +1.7.10.4 + -- 1.7.10.4 _______________________________________________ linux-yocto mailing list [email protected] https://lists.yoctoproject.org/listinfo/linux-yocto
