[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