[linux-yocto] [PATCH 2/2] valleyisland-io: add patch to enable GPIO controller support on Baytrail
rebecca.swee.fun.chang at intel.com
rebecca.swee.fun.chang at intel.com
Wed Dec 11 01:41:52 PST 2013
From: "Chang, Rebecca Swee Fun" <rebecca.swee.fun.chang at intel.com>
The GPIO pins are divided into 3 clusters, namely SOUTH,SUS and NORTH.
Signed-off-by: Chang, Rebecca Swee Fun <rebecca.swee.fun.chang at intel.com>
---
...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 <rebecca.swee.fun.chang at intel.com>
+---
+ 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 (kean.ho.chew at intel.com)
++ *
++ * 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 <mathias.nyman at linux.intel.com>
++ *
++ * 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
More information about the linux-yocto
mailing list