From 1bc857f700fb14bbcb990a81b1255f39807ae59e Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 26 Jul 2011 10:54:55 +0100 Subject: ARM: gpio: omap: convert drivers to use asm/gpio.h rather than mach/gpio.h Signed-off-by: Russell King --- drivers/gpio/gpio-omap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 0599854e2217..9c27244fd680 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include struct gpio_bank { -- cgit v1.2.2 From 75482dc3ded2be0b04ee00294f8db94a38748096 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 26 Jul 2011 10:57:37 +0100 Subject: ARM: gpio: nomadik: convert drivers to use asm/gpio.h rather than mach/gpio.h Signed-off-by: Russell King --- drivers/gpio/gpio-nomadik.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c index 2c212c732d76..f7f0406ed880 100644 --- a/drivers/gpio/gpio-nomadik.c +++ b/drivers/gpio/gpio-nomadik.c @@ -28,7 +28,7 @@ #include #include -#include +#include /* * The GPIO module in the Nomadik family of Systems-on-Chip is an -- cgit v1.2.2 From 09ad8039dad91baed6e43f6ba4741805e7b48932 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 14 Aug 2011 00:14:02 +0800 Subject: gpio/mxc: add .to_irq for gpio chip It adds .to_irq support for gpio chip, so that __gpio_to_irq in gpiolib becomes usable. Signed-off-by: Shawn Guo Signed-off-by: Sascha Hauer --- drivers/gpio/gpio-mxc.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 4340acae3bd3..64aff20bc620 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -337,6 +337,15 @@ static void __devinit mxc_gpio_get_hw(struct platform_device *pdev) mxc_gpio_hwtype = hwtype; } +static int mxc_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + struct mxc_gpio_port *port = + container_of(bgc, struct mxc_gpio_port, bgc); + + return port->virtual_irq_start + offset; +} + static int __devinit mxc_gpio_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -403,6 +412,7 @@ static int __devinit mxc_gpio_probe(struct platform_device *pdev) if (err) goto out_iounmap; + port->bgc.gc.to_irq = mxc_gpio_to_irq; port->bgc.gc.base = pdev->id * 32; port->bgc.dir = port->bgc.read_reg(port->bgc.reg_dir); port->bgc.data = port->bgc.read_reg(port->bgc.reg_set); -- cgit v1.2.2 From a4395612290c7b70041952add7ad75b534c8b40c Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 14 Aug 2011 00:14:04 +0800 Subject: gpio/mxc: move irq_to_gpio() into gpio-mxc driver As irq_to_gpio() is only being used by gpio-mxc driver, it should be moved from mach/gpio.h into gpio-mxc.c. Signed-off-by: Shawn Guo Signed-off-by: Sascha Hauer --- drivers/gpio/gpio-mxc.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 64aff20bc620..b588f8a41e60 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -31,6 +31,8 @@ #include #include +#define irq_to_gpio(irq) ((irq) - MXC_GPIO_IRQ_START) + enum mxc_gpio_hwtype { IMX1_GPIO, /* runs on i.mx1 */ IMX21_GPIO, /* runs on i.mx21 and i.mx27 */ -- cgit v1.2.2 From 7e6c53aac38eec4e7bca1e4e9a5a6d7e45b30784 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 14 Aug 2011 00:14:06 +0800 Subject: gpio/mxs: move irq_to_gpio() into gpio-mxs driver As irq_to_gpio() is only being used by gpio-mxs driver, it should be moved from mach/gpio.h into gpio-mxs.c. Signed-off-by: Shawn Guo Signed-off-by: Sascha Hauer --- drivers/gpio/gpio-mxs.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index af55a8577c2e..292b50481db9 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -49,6 +49,8 @@ #define GPIO_INT_LEV_MASK (1 << 0) #define GPIO_INT_POL_MASK (1 << 1) +#define irq_to_gpio(irq) ((irq) - MXS_GPIO_IRQ_START) + struct mxs_gpio_port { void __iomem *base; int id; -- cgit v1.2.2 From 0f33286190634eeb3ec7638045980c39c98380f4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 22 Aug 2011 08:33:30 +0100 Subject: ARM: 7032/1: plat-nomadik: break out GPIO driver specifics The <[plat|mach]/gpio.h> file is included from upper directories and deal with generic GPIO and gpiolib stuff. Break out the platform and driver specific defines and functions into its own header file. Cc: Srinidhi Kasagar Cc: Alessandro Rubini Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/gpio/gpio-nomadik.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c index f7f0406ed880..740caed2b278 100644 --- a/drivers/gpio/gpio-nomadik.c +++ b/drivers/gpio/gpio-nomadik.c @@ -27,6 +27,7 @@ #include #include +#include #include #include -- cgit v1.2.2 From eb3cf18cccc33d86179674ac1c49dcbc142fc2d9 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 22 Aug 2011 08:34:26 +0100 Subject: ARM: 7033/1: mach-u300: break out GPIO driver specifics The file is included from upper directories and deal with generic GPIO and gpiolib stuff. Break out the platform and driver specific defines and functions into its own header file. Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/gpio/gpio-u300.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-u300.c b/drivers/gpio/gpio-u300.c index 53e8255cb0ba..92f2b8c06de1 100644 --- a/drivers/gpio/gpio-u300.c +++ b/drivers/gpio/gpio-u300.c @@ -21,6 +21,7 @@ #include #include #include +#include /* Reference to GPIO block clock */ static struct clk *clk; -- cgit v1.2.2 From db23c7332b5391646c32585b06328da0463418f8 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 22 Aug 2011 08:37:10 +0100 Subject: ARM: 7035/1: mach-ks8695: move GPIO driver to GPIO subsystem As per example from the other ARM boards, push the KS8695 GPIO driver down to the GPIO subsystem so it can be consolidated. Cc: zeal Cc: Ben Dooks Acked-by: Daniel Silverstone Acked-by: Simtec Linux Team Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/gpio/Makefile | 1 + drivers/gpio/gpio-ks8695.c | 318 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 319 insertions(+) create mode 100644 drivers/gpio/gpio-ks8695.c (limited to 'drivers/gpio') diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9588948c96f0..27295cbadaf3 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o obj-$(CONFIG_GPIO_EXYNOS4) += gpio-exynos4.o obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o +obj-$(CONFIG_MACH_KS8695) += gpio-ks8695.o obj-$(CONFIG_GPIO_LANGWELL) += gpio-langwell.o obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o diff --git a/drivers/gpio/gpio-ks8695.c b/drivers/gpio/gpio-ks8695.c new file mode 100644 index 000000000000..b3fdfe1d262f --- /dev/null +++ b/drivers/gpio/gpio-ks8695.c @@ -0,0 +1,318 @@ +/* + * arch/arm/mach-ks8695/gpio.c + * + * Copyright (C) 2006 Andrew Victor + * Updated to GPIOLIB, Copyright 2008 Simtec Electronics + * Daniel Silverstone + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* + * Configure a GPIO line for either GPIO function, or its internal + * function (Interrupt, Timer, etc). + */ +static void ks8695_gpio_mode(unsigned int pin, short gpio) +{ + unsigned int enable[] = { IOPC_IOEINT0EN, IOPC_IOEINT1EN, IOPC_IOEINT2EN, IOPC_IOEINT3EN, IOPC_IOTIM0EN, IOPC_IOTIM1EN }; + unsigned long x, flags; + + if (pin > KS8695_GPIO_5) /* only GPIO 0..5 have internal functions */ + return; + + local_irq_save(flags); + + x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPC); + if (gpio) /* GPIO: set bit to 0 */ + x &= ~enable[pin]; + else /* Internal function: set bit to 1 */ + x |= enable[pin]; + __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPC); + + local_irq_restore(flags); +} + + +static unsigned short gpio_irq[] = { KS8695_IRQ_EXTERN0, KS8695_IRQ_EXTERN1, KS8695_IRQ_EXTERN2, KS8695_IRQ_EXTERN3 }; + +/* + * Configure GPIO pin as external interrupt source. + */ +int ks8695_gpio_interrupt(unsigned int pin, unsigned int type) +{ + unsigned long x, flags; + + if (pin > KS8695_GPIO_3) /* only GPIO 0..3 can generate IRQ */ + return -EINVAL; + + local_irq_save(flags); + + /* set pin as input */ + x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPM); + x &= ~IOPM(pin); + __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPM); + + local_irq_restore(flags); + + /* Set IRQ triggering type */ + irq_set_irq_type(gpio_irq[pin], type); + + /* enable interrupt mode */ + ks8695_gpio_mode(pin, 0); + + return 0; +} +EXPORT_SYMBOL(ks8695_gpio_interrupt); + + + +/* .... Generic GPIO interface .............................................. */ + +/* + * Configure the GPIO line as an input. + */ +static int ks8695_gpio_direction_input(struct gpio_chip *gc, unsigned int pin) +{ + unsigned long x, flags; + + if (pin > KS8695_GPIO_15) + return -EINVAL; + + /* set pin to GPIO mode */ + ks8695_gpio_mode(pin, 1); + + local_irq_save(flags); + + /* set pin as input */ + x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPM); + x &= ~IOPM(pin); + __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPM); + + local_irq_restore(flags); + + return 0; +} + + +/* + * Configure the GPIO line as an output, with default state. + */ +static int ks8695_gpio_direction_output(struct gpio_chip *gc, + unsigned int pin, int state) +{ + unsigned long x, flags; + + if (pin > KS8695_GPIO_15) + return -EINVAL; + + /* set pin to GPIO mode */ + ks8695_gpio_mode(pin, 1); + + local_irq_save(flags); + + /* set line state */ + x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD); + if (state) + x |= IOPD(pin); + else + x &= ~IOPD(pin); + __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPD); + + /* set pin as output */ + x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPM); + x |= IOPM(pin); + __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPM); + + local_irq_restore(flags); + + return 0; +} + + +/* + * Set the state of an output GPIO line. + */ +static void ks8695_gpio_set_value(struct gpio_chip *gc, + unsigned int pin, int state) +{ + unsigned long x, flags; + + if (pin > KS8695_GPIO_15) + return; + + local_irq_save(flags); + + /* set output line state */ + x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD); + if (state) + x |= IOPD(pin); + else + x &= ~IOPD(pin); + __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPD); + + local_irq_restore(flags); +} + + +/* + * Read the state of a GPIO line. + */ +static int ks8695_gpio_get_value(struct gpio_chip *gc, unsigned int pin) +{ + unsigned long x; + + if (pin > KS8695_GPIO_15) + return -EINVAL; + + x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD); + return (x & IOPD(pin)) != 0; +} + + +/* + * Map GPIO line to IRQ number. + */ +static int ks8695_gpio_to_irq(struct gpio_chip *gc, unsigned int pin) +{ + if (pin > KS8695_GPIO_3) /* only GPIO 0..3 can generate IRQ */ + return -EINVAL; + + return gpio_irq[pin]; +} + +/* + * Map IRQ number to GPIO line. + */ +int irq_to_gpio(unsigned int irq) +{ + if ((irq < KS8695_IRQ_EXTERN0) || (irq > KS8695_IRQ_EXTERN3)) + return -EINVAL; + + return (irq - KS8695_IRQ_EXTERN0); +} +EXPORT_SYMBOL(irq_to_gpio); + +/* GPIOLIB interface */ + +static struct gpio_chip ks8695_gpio_chip = { + .label = "KS8695", + .direction_input = ks8695_gpio_direction_input, + .direction_output = ks8695_gpio_direction_output, + .get = ks8695_gpio_get_value, + .set = ks8695_gpio_set_value, + .to_irq = ks8695_gpio_to_irq, + .base = 0, + .ngpio = 16, + .can_sleep = 0, +}; + +/* Register the GPIOs */ +void ks8695_register_gpios(void) +{ + if (gpiochip_add(&ks8695_gpio_chip)) + printk(KERN_ERR "Unable to register core GPIOs\n"); +} + +/* .... Debug interface ..................................................... */ + +#ifdef CONFIG_DEBUG_FS + +static int ks8695_gpio_show(struct seq_file *s, void *unused) +{ + unsigned int enable[] = { IOPC_IOEINT0EN, IOPC_IOEINT1EN, IOPC_IOEINT2EN, IOPC_IOEINT3EN, IOPC_IOTIM0EN, IOPC_IOTIM1EN }; + unsigned int intmask[] = { IOPC_IOEINT0TM, IOPC_IOEINT1TM, IOPC_IOEINT2TM, IOPC_IOEINT3TM }; + unsigned long mode, ctrl, data; + int i; + + mode = __raw_readl(KS8695_GPIO_VA + KS8695_IOPM); + ctrl = __raw_readl(KS8695_GPIO_VA + KS8695_IOPC); + data = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD); + + seq_printf(s, "Pin\tI/O\tFunction\tState\n\n"); + + for (i = KS8695_GPIO_0; i <= KS8695_GPIO_15 ; i++) { + seq_printf(s, "%i:\t", i); + + seq_printf(s, "%s\t", (mode & IOPM(i)) ? "Output" : "Input"); + + if (i <= KS8695_GPIO_3) { + if (ctrl & enable[i]) { + seq_printf(s, "EXT%i ", i); + + switch ((ctrl & intmask[i]) >> (4 * i)) { + case IOPC_TM_LOW: + seq_printf(s, "(Low)"); break; + case IOPC_TM_HIGH: + seq_printf(s, "(High)"); break; + case IOPC_TM_RISING: + seq_printf(s, "(Rising)"); break; + case IOPC_TM_FALLING: + seq_printf(s, "(Falling)"); break; + case IOPC_TM_EDGE: + seq_printf(s, "(Edges)"); break; + } + } + else + seq_printf(s, "GPIO\t"); + } + else if (i <= KS8695_GPIO_5) { + if (ctrl & enable[i]) + seq_printf(s, "TOUT%i\t", i - KS8695_GPIO_4); + else + seq_printf(s, "GPIO\t"); + } + else + seq_printf(s, "GPIO\t"); + + seq_printf(s, "\t"); + + seq_printf(s, "%i\n", (data & IOPD(i)) ? 1 : 0); + } + return 0; +} + +static int ks8695_gpio_open(struct inode *inode, struct file *file) +{ + return single_open(file, ks8695_gpio_show, NULL); +} + +static const struct file_operations ks8695_gpio_operations = { + .open = ks8695_gpio_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init ks8695_gpio_debugfs_init(void) +{ + /* /sys/kernel/debug/ks8695_gpio */ + (void) debugfs_create_file("ks8695_gpio", S_IFREG | S_IRUGO, NULL, NULL, &ks8695_gpio_operations); + return 0; +} +postcore_initcall(ks8695_gpio_debugfs_init); + +#endif -- cgit v1.2.2 From e24e4498cb4930bf785e46f98e7be7becece8b4c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 22 Aug 2011 08:37:38 +0100 Subject: ARM: 7036/1: mach-ks8695: break out GPIO driver specifics The file is included from upper directories and deal with generic GPIO and gpiolib stuff. Break out the platform and driver specific defines and functions into its own header file. Cc: zeal Cc: Ben Dooks Acked-by: Daniel Silverstone Acked-by: Simtec Linux Team Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/gpio/gpio-ks8695.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ks8695.c b/drivers/gpio/gpio-ks8695.c index b3fdfe1d262f..a3ac66ea364b 100644 --- a/drivers/gpio/gpio-ks8695.c +++ b/drivers/gpio/gpio-ks8695.c @@ -31,6 +31,7 @@ #include #include +#include /* * Configure a GPIO line for either GPIO function, or its internal -- cgit v1.2.2 From 8338d87fea2c001b4f5a07f9217df89956d1fddd Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 22 Aug 2011 08:39:28 +0100 Subject: ARM: 7038/1: mach-davinci: move GPIO driver to GPIO subsystem As per example from the other ARM boards, push the DaVinci GPIO driver down to the GPIO subsystem so it can be consolidated. Cc: Sekhar Nori Cc: Kevin Hilman Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/gpio/Makefile | 1 + drivers/gpio/gpio-davinci.c | 458 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 459 insertions(+) create mode 100644 drivers/gpio/gpio-davinci.c (limited to 'drivers/gpio') diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 27295cbadaf3..a26fffacdfa5 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o +obj-$(CONFIG_ARCH_DAVINCI) += gpio-davinci.o obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o obj-$(CONFIG_GPIO_EXYNOS4) += gpio-exynos4.o obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c new file mode 100644 index 000000000000..a8066e8edade --- /dev/null +++ b/drivers/gpio/gpio-davinci.c @@ -0,0 +1,458 @@ +/* + * TI DaVinci GPIO Support + * + * Copyright (c) 2006-2007 David Brownell + * Copyright (c) 2007, MontaVista Software, Inc. + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include + +#include + +struct davinci_gpio_regs { + u32 dir; + u32 out_data; + u32 set_data; + u32 clr_data; + u32 in_data; + u32 set_rising; + u32 clr_rising; + u32 set_falling; + u32 clr_falling; + u32 intstat; +}; + +#define chip2controller(chip) \ + container_of(chip, struct davinci_gpio_controller, chip) + +static struct davinci_gpio_controller chips[DIV_ROUND_UP(DAVINCI_N_GPIO, 32)]; +static void __iomem *gpio_base; + +static struct davinci_gpio_regs __iomem __init *gpio2regs(unsigned gpio) +{ + void __iomem *ptr; + + if (gpio < 32 * 1) + ptr = gpio_base + 0x10; + else if (gpio < 32 * 2) + ptr = gpio_base + 0x38; + else if (gpio < 32 * 3) + ptr = gpio_base + 0x60; + else if (gpio < 32 * 4) + ptr = gpio_base + 0x88; + else if (gpio < 32 * 5) + ptr = gpio_base + 0xb0; + else + ptr = NULL; + return ptr; +} + +static inline struct davinci_gpio_regs __iomem *irq2regs(int irq) +{ + struct davinci_gpio_regs __iomem *g; + + g = (__force struct davinci_gpio_regs __iomem *)irq_get_chip_data(irq); + + return g; +} + +static int __init davinci_gpio_irq_setup(void); + +/*--------------------------------------------------------------------------*/ + +/* board setup code *MUST* setup pinmux and enable the GPIO clock. */ +static inline int __davinci_direction(struct gpio_chip *chip, + unsigned offset, bool out, int value) +{ + struct davinci_gpio_controller *d = chip2controller(chip); + struct davinci_gpio_regs __iomem *g = d->regs; + unsigned long flags; + u32 temp; + u32 mask = 1 << offset; + + spin_lock_irqsave(&d->lock, flags); + temp = __raw_readl(&g->dir); + if (out) { + temp &= ~mask; + __raw_writel(mask, value ? &g->set_data : &g->clr_data); + } else { + temp |= mask; + } + __raw_writel(temp, &g->dir); + spin_unlock_irqrestore(&d->lock, flags); + + return 0; +} + +static int davinci_direction_in(struct gpio_chip *chip, unsigned offset) +{ + return __davinci_direction(chip, offset, false, 0); +} + +static int +davinci_direction_out(struct gpio_chip *chip, unsigned offset, int value) +{ + return __davinci_direction(chip, offset, true, value); +} + +/* + * Read the pin's value (works even if it's set up as output); + * returns zero/nonzero. + * + * Note that changes are synched to the GPIO clock, so reading values back + * right after you've set them may give old values. + */ +static int davinci_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct davinci_gpio_controller *d = chip2controller(chip); + struct davinci_gpio_regs __iomem *g = d->regs; + + return (1 << offset) & __raw_readl(&g->in_data); +} + +/* + * Assuming the pin is muxed as a gpio output, set its output value. + */ +static void +davinci_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct davinci_gpio_controller *d = chip2controller(chip); + struct davinci_gpio_regs __iomem *g = d->regs; + + __raw_writel((1 << offset), value ? &g->set_data : &g->clr_data); +} + +static int __init davinci_gpio_setup(void) +{ + int i, base; + unsigned ngpio; + struct davinci_soc_info *soc_info = &davinci_soc_info; + struct davinci_gpio_regs *regs; + + if (soc_info->gpio_type != GPIO_TYPE_DAVINCI) + return 0; + + /* + * The gpio banks conceptually expose a segmented bitmap, + * and "ngpio" is one more than the largest zero-based + * bit index that's valid. + */ + ngpio = soc_info->gpio_num; + if (ngpio == 0) { + pr_err("GPIO setup: how many GPIOs?\n"); + return -EINVAL; + } + + if (WARN_ON(DAVINCI_N_GPIO < ngpio)) + ngpio = DAVINCI_N_GPIO; + + gpio_base = ioremap(soc_info->gpio_base, SZ_4K); + if (WARN_ON(!gpio_base)) + return -ENOMEM; + + for (i = 0, base = 0; base < ngpio; i++, base += 32) { + chips[i].chip.label = "DaVinci"; + + chips[i].chip.direction_input = davinci_direction_in; + chips[i].chip.get = davinci_gpio_get; + chips[i].chip.direction_output = davinci_direction_out; + chips[i].chip.set = davinci_gpio_set; + + chips[i].chip.base = base; + chips[i].chip.ngpio = ngpio - base; + if (chips[i].chip.ngpio > 32) + chips[i].chip.ngpio = 32; + + spin_lock_init(&chips[i].lock); + + regs = gpio2regs(base); + chips[i].regs = regs; + chips[i].set_data = ®s->set_data; + chips[i].clr_data = ®s->clr_data; + chips[i].in_data = ®s->in_data; + + gpiochip_add(&chips[i].chip); + } + + soc_info->gpio_ctlrs = chips; + soc_info->gpio_ctlrs_num = DIV_ROUND_UP(ngpio, 32); + + davinci_gpio_irq_setup(); + return 0; +} +pure_initcall(davinci_gpio_setup); + +/*--------------------------------------------------------------------------*/ +/* + * We expect irqs will normally be set up as input pins, but they can also be + * used as output pins ... which is convenient for testing. + * + * NOTE: The first few GPIOs also have direct INTC hookups in addition + * to their GPIOBNK0 irq, with a bit less overhead. + * + * All those INTC hookups (direct, plus several IRQ banks) can also + * serve as EDMA event triggers. + */ + +static void gpio_irq_disable(struct irq_data *d) +{ + struct davinci_gpio_regs __iomem *g = irq2regs(d->irq); + u32 mask = (u32) irq_data_get_irq_handler_data(d); + + __raw_writel(mask, &g->clr_falling); + __raw_writel(mask, &g->clr_rising); +} + +static void gpio_irq_enable(struct irq_data *d) +{ + struct davinci_gpio_regs __iomem *g = irq2regs(d->irq); + u32 mask = (u32) irq_data_get_irq_handler_data(d); + unsigned status = irqd_get_trigger_type(d); + + status &= IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING; + if (!status) + status = IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING; + + if (status & IRQ_TYPE_EDGE_FALLING) + __raw_writel(mask, &g->set_falling); + if (status & IRQ_TYPE_EDGE_RISING) + __raw_writel(mask, &g->set_rising); +} + +static int gpio_irq_type(struct irq_data *d, unsigned trigger) +{ + struct davinci_gpio_regs __iomem *g = irq2regs(d->irq); + u32 mask = (u32) irq_data_get_irq_handler_data(d); + + if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) + return -EINVAL; + + return 0; +} + +static struct irq_chip gpio_irqchip = { + .name = "GPIO", + .irq_enable = gpio_irq_enable, + .irq_disable = gpio_irq_disable, + .irq_set_type = gpio_irq_type, + .flags = IRQCHIP_SET_TYPE_MASKED, +}; + +static void +gpio_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct davinci_gpio_regs __iomem *g; + u32 mask = 0xffff; + struct davinci_gpio_controller *d; + + d = (struct davinci_gpio_controller *)irq_desc_get_handler_data(desc); + g = (struct davinci_gpio_regs __iomem *)d->regs; + + /* we only care about one bank */ + if (irq & 1) + mask <<= 16; + + /* temporarily mask (level sensitive) parent IRQ */ + desc->irq_data.chip->irq_mask(&desc->irq_data); + desc->irq_data.chip->irq_ack(&desc->irq_data); + while (1) { + u32 status; + int n; + int res; + + /* ack any irqs */ + status = __raw_readl(&g->intstat) & mask; + if (!status) + break; + __raw_writel(status, &g->intstat); + + /* now demux them to the right lowlevel handler */ + n = d->irq_base; + if (irq & 1) { + n += 16; + status >>= 16; + } + + while (status) { + res = ffs(status); + n += res; + generic_handle_irq(n - 1); + status >>= res; + } + } + desc->irq_data.chip->irq_unmask(&desc->irq_data); + /* now it may re-trigger */ +} + +static int gpio_to_irq_banked(struct gpio_chip *chip, unsigned offset) +{ + struct davinci_gpio_controller *d = chip2controller(chip); + + if (d->irq_base >= 0) + return d->irq_base + offset; + else + return -ENODEV; +} + +static int gpio_to_irq_unbanked(struct gpio_chip *chip, unsigned offset) +{ + struct davinci_soc_info *soc_info = &davinci_soc_info; + + /* NOTE: we assume for now that only irqs in the first gpio_chip + * can provide direct-mapped IRQs to AINTC (up to 32 GPIOs). + */ + if (offset < soc_info->gpio_unbanked) + return soc_info->gpio_irq + offset; + else + return -ENODEV; +} + +static int gpio_irq_type_unbanked(struct irq_data *d, unsigned trigger) +{ + struct davinci_gpio_regs __iomem *g = irq2regs(d->irq); + u32 mask = (u32) irq_data_get_irq_handler_data(d); + + if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) + return -EINVAL; + + __raw_writel(mask, (trigger & IRQ_TYPE_EDGE_FALLING) + ? &g->set_falling : &g->clr_falling); + __raw_writel(mask, (trigger & IRQ_TYPE_EDGE_RISING) + ? &g->set_rising : &g->clr_rising); + + return 0; +} + +/* + * NOTE: for suspend/resume, probably best to make a platform_device with + * suspend_late/resume_resume calls hooking into results of the set_wake() + * calls ... so if no gpios are wakeup events the clock can be disabled, + * with outputs left at previously set levels, and so that VDD3P3V.IOPWDN0 + * (dm6446) can be set appropriately for GPIOV33 pins. + */ + +static int __init davinci_gpio_irq_setup(void) +{ + unsigned gpio, irq, bank; + struct clk *clk; + u32 binten = 0; + unsigned ngpio, bank_irq; + struct davinci_soc_info *soc_info = &davinci_soc_info; + struct davinci_gpio_regs __iomem *g; + + ngpio = soc_info->gpio_num; + + bank_irq = soc_info->gpio_irq; + if (bank_irq == 0) { + printk(KERN_ERR "Don't know first GPIO bank IRQ.\n"); + return -EINVAL; + } + + clk = clk_get(NULL, "gpio"); + if (IS_ERR(clk)) { + printk(KERN_ERR "Error %ld getting gpio clock?\n", + PTR_ERR(clk)); + return PTR_ERR(clk); + } + clk_enable(clk); + + /* Arrange gpio_to_irq() support, handling either direct IRQs or + * banked IRQs. Having GPIOs in the first GPIO bank use direct + * IRQs, while the others use banked IRQs, would need some setup + * tweaks to recognize hardware which can do that. + */ + for (gpio = 0, bank = 0; gpio < ngpio; bank++, gpio += 32) { + chips[bank].chip.to_irq = gpio_to_irq_banked; + chips[bank].irq_base = soc_info->gpio_unbanked + ? -EINVAL + : (soc_info->intc_irq_num + gpio); + } + + /* + * AINTC can handle direct/unbanked IRQs for GPIOs, with the GPIO + * controller only handling trigger modes. We currently assume no + * IRQ mux conflicts; gpio_irq_type_unbanked() is only for GPIOs. + */ + if (soc_info->gpio_unbanked) { + static struct irq_chip gpio_irqchip_unbanked; + + /* pass "bank 0" GPIO IRQs to AINTC */ + chips[0].chip.to_irq = gpio_to_irq_unbanked; + binten = BIT(0); + + /* AINTC handles mask/unmask; GPIO handles triggering */ + irq = bank_irq; + gpio_irqchip_unbanked = *irq_get_chip(irq); + gpio_irqchip_unbanked.name = "GPIO-AINTC"; + gpio_irqchip_unbanked.irq_set_type = gpio_irq_type_unbanked; + + /* default trigger: both edges */ + g = gpio2regs(0); + __raw_writel(~0, &g->set_falling); + __raw_writel(~0, &g->set_rising); + + /* set the direct IRQs up to use that irqchip */ + for (gpio = 0; gpio < soc_info->gpio_unbanked; gpio++, irq++) { + irq_set_chip(irq, &gpio_irqchip_unbanked); + irq_set_handler_data(irq, (void *)__gpio_mask(gpio)); + irq_set_chip_data(irq, (__force void *)g); + irq_set_status_flags(irq, IRQ_TYPE_EDGE_BOTH); + } + + goto done; + } + + /* + * Or, AINTC can handle IRQs for banks of 16 GPIO IRQs, which we + * then chain through our own handler. + */ + for (gpio = 0, irq = gpio_to_irq(0), bank = 0; + gpio < ngpio; + bank++, bank_irq++) { + unsigned i; + + /* disabled by default, enabled only as needed */ + g = gpio2regs(gpio); + __raw_writel(~0, &g->clr_falling); + __raw_writel(~0, &g->clr_rising); + + /* set up all irqs in this bank */ + irq_set_chained_handler(bank_irq, gpio_irq_handler); + + /* + * Each chip handles 32 gpios, and each irq bank consists of 16 + * gpio irqs. Pass the irq bank's corresponding controller to + * the chained irq handler. + */ + irq_set_handler_data(bank_irq, &chips[gpio / 32]); + + for (i = 0; i < 16 && gpio < ngpio; i++, irq++, gpio++) { + irq_set_chip(irq, &gpio_irqchip); + irq_set_chip_data(irq, (__force void *)g); + irq_set_handler_data(irq, (void *)__gpio_mask(gpio)); + irq_set_handler(irq, handle_simple_irq); + set_irq_flags(irq, IRQF_VALID); + } + + binten |= BIT(bank); + } + +done: + /* BINTEN -- per-bank interrupt enable. genirq would also let these + * bits be set/cleared dynamically. + */ + __raw_writel(binten, gpio_base + 0x08); + + printk(KERN_INFO "DaVinci: %d gpio irqs\n", irq - gpio_to_irq(0)); + + return 0; +} -- cgit v1.2.2 From bd08d709c4a44089a9d87a3b402a54c1d6b8890b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 22 Aug 2011 08:40:10 +0100 Subject: ARM: 7039/1: mach-davinci: move DaVinci TNET GPIO driver to GPIO subsystem As per example from the other ARM boards, push the DaVinci TNET GPIO driver down to the GPIO subsystem so it can be consolidated. Cc: Sekhar Nori Cc: Kevin Hilman Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/gpio/Makefile | 1 + drivers/gpio/gpio-tnetv107x.c | 205 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 drivers/gpio/gpio-tnetv107x.c (limited to 'drivers/gpio') diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index a26fffacdfa5..226982277330 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o +obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += gpio-tnetv107x.o obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o diff --git a/drivers/gpio/gpio-tnetv107x.c b/drivers/gpio/gpio-tnetv107x.c new file mode 100644 index 000000000000..3fa3e2867e19 --- /dev/null +++ b/drivers/gpio/gpio-tnetv107x.c @@ -0,0 +1,205 @@ +/* + * Texas Instruments TNETV107X GPIO Controller + * + * Copyright (C) 2010 Texas Instruments + * + * 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. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include + +#include +#include + +struct tnetv107x_gpio_regs { + u32 idver; + u32 data_in[3]; + u32 data_out[3]; + u32 direction[3]; + u32 enable[3]; +}; + +#define gpio_reg_index(gpio) ((gpio) >> 5) +#define gpio_reg_bit(gpio) BIT((gpio) & 0x1f) + +#define gpio_reg_rmw(reg, mask, val) \ + __raw_writel((__raw_readl(reg) & ~(mask)) | (val), (reg)) + +#define gpio_reg_set_bit(reg, gpio) \ + gpio_reg_rmw((reg) + gpio_reg_index(gpio), 0, gpio_reg_bit(gpio)) + +#define gpio_reg_clear_bit(reg, gpio) \ + gpio_reg_rmw((reg) + gpio_reg_index(gpio), gpio_reg_bit(gpio), 0) + +#define gpio_reg_get_bit(reg, gpio) \ + (__raw_readl((reg) + gpio_reg_index(gpio)) & gpio_reg_bit(gpio)) + +#define chip2controller(chip) \ + container_of(chip, struct davinci_gpio_controller, chip) + +#define TNETV107X_GPIO_CTLRS DIV_ROUND_UP(TNETV107X_N_GPIO, 32) + +static struct davinci_gpio_controller chips[TNETV107X_GPIO_CTLRS]; + +static int tnetv107x_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct davinci_gpio_controller *ctlr = chip2controller(chip); + struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; + unsigned gpio = chip->base + offset; + unsigned long flags; + + spin_lock_irqsave(&ctlr->lock, flags); + + gpio_reg_set_bit(regs->enable, gpio); + + spin_unlock_irqrestore(&ctlr->lock, flags); + + return 0; +} + +static void tnetv107x_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct davinci_gpio_controller *ctlr = chip2controller(chip); + struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; + unsigned gpio = chip->base + offset; + unsigned long flags; + + spin_lock_irqsave(&ctlr->lock, flags); + + gpio_reg_clear_bit(regs->enable, gpio); + + spin_unlock_irqrestore(&ctlr->lock, flags); +} + +static int tnetv107x_gpio_dir_in(struct gpio_chip *chip, unsigned offset) +{ + struct davinci_gpio_controller *ctlr = chip2controller(chip); + struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; + unsigned gpio = chip->base + offset; + unsigned long flags; + + spin_lock_irqsave(&ctlr->lock, flags); + + gpio_reg_set_bit(regs->direction, gpio); + + spin_unlock_irqrestore(&ctlr->lock, flags); + + return 0; +} + +static int tnetv107x_gpio_dir_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct davinci_gpio_controller *ctlr = chip2controller(chip); + struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; + unsigned gpio = chip->base + offset; + unsigned long flags; + + spin_lock_irqsave(&ctlr->lock, flags); + + if (value) + gpio_reg_set_bit(regs->data_out, gpio); + else + gpio_reg_clear_bit(regs->data_out, gpio); + + gpio_reg_clear_bit(regs->direction, gpio); + + spin_unlock_irqrestore(&ctlr->lock, flags); + + return 0; +} + +static int tnetv107x_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct davinci_gpio_controller *ctlr = chip2controller(chip); + struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; + unsigned gpio = chip->base + offset; + int ret; + + ret = gpio_reg_get_bit(regs->data_in, gpio); + + return ret ? 1 : 0; +} + +static void tnetv107x_gpio_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct davinci_gpio_controller *ctlr = chip2controller(chip); + struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; + unsigned gpio = chip->base + offset; + unsigned long flags; + + spin_lock_irqsave(&ctlr->lock, flags); + + if (value) + gpio_reg_set_bit(regs->data_out, gpio); + else + gpio_reg_clear_bit(regs->data_out, gpio); + + spin_unlock_irqrestore(&ctlr->lock, flags); +} + +static int __init tnetv107x_gpio_setup(void) +{ + int i, base; + unsigned ngpio; + struct davinci_soc_info *soc_info = &davinci_soc_info; + struct tnetv107x_gpio_regs *regs; + struct davinci_gpio_controller *ctlr; + + if (soc_info->gpio_type != GPIO_TYPE_TNETV107X) + return 0; + + ngpio = soc_info->gpio_num; + if (ngpio == 0) { + pr_err("GPIO setup: how many GPIOs?\n"); + return -EINVAL; + } + + if (WARN_ON(TNETV107X_N_GPIO < ngpio)) + ngpio = TNETV107X_N_GPIO; + + regs = ioremap(soc_info->gpio_base, SZ_4K); + if (WARN_ON(!regs)) + return -EINVAL; + + for (i = 0, base = 0; base < ngpio; i++, base += 32) { + ctlr = &chips[i]; + + ctlr->chip.label = "tnetv107x"; + ctlr->chip.can_sleep = 0; + ctlr->chip.base = base; + ctlr->chip.ngpio = ngpio - base; + if (ctlr->chip.ngpio > 32) + ctlr->chip.ngpio = 32; + + ctlr->chip.request = tnetv107x_gpio_request; + ctlr->chip.free = tnetv107x_gpio_free; + ctlr->chip.direction_input = tnetv107x_gpio_dir_in; + ctlr->chip.get = tnetv107x_gpio_get; + ctlr->chip.direction_output = tnetv107x_gpio_dir_out; + ctlr->chip.set = tnetv107x_gpio_set; + + spin_lock_init(&ctlr->lock); + + ctlr->regs = regs; + ctlr->set_data = ®s->data_out[i]; + ctlr->clr_data = ®s->data_out[i]; + ctlr->in_data = ®s->data_in[i]; + + gpiochip_add(&ctlr->chip); + } + + soc_info->gpio_ctlrs = chips; + soc_info->gpio_ctlrs_num = DIV_ROUND_UP(ngpio, 32); + return 0; +} +pure_initcall(tnetv107x_gpio_setup); -- cgit v1.2.2 From 257af9f9725aa8a863b306659208a031135d59e7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 22 Aug 2011 08:43:04 +0100 Subject: ARM: 7041/1: gpio-ep93xx: hookup the to_irq callback in the driver Remove the ep93xx machine specific dependencies for gpio_to_irq() by hooking up the callback in the driver and using __gpio_to_irq. Signed-off-by: H Hartley Sweeten Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/gpio/gpio-ep93xx.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 72fb9c665320..fbd6a6066777 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -307,6 +307,21 @@ static int ep93xx_gpio_set_debounce(struct gpio_chip *chip, return 0; } +/* + * Map GPIO A0..A7 (0..7) to irq 64..71, + * B0..B7 (7..15) to irq 72..79, and + * F0..F7 (16..24) to irq 80..87. + */ +static int ep93xx_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + int gpio = chip->base + offset; + + if (gpio > EP93XX_GPIO_LINE_MAX_IRQ) + return -EINVAL; + + return 64 + gpio; +} + static int ep93xx_gpio_add_bank(struct bgpio_chip *bgc, struct device *dev, void __iomem *mmio_base, struct ep93xx_gpio_bank *bank) { @@ -321,8 +336,10 @@ static int ep93xx_gpio_add_bank(struct bgpio_chip *bgc, struct device *dev, bgc->gc.label = bank->label; bgc->gc.base = bank->base; - if (bank->has_debounce) + if (bank->has_debounce) { bgc->gc.set_debounce = ep93xx_gpio_set_debounce; + bgc->gc.to_irq = ep93xx_gpio_to_irq; + } return gpiochip_add(&bgc->gc); } -- cgit v1.2.2 From f80cb526e301ab625dd04f6137f9995607348ce3 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 22 Aug 2011 08:44:45 +0100 Subject: ARM: 7044/1: mach-lpc32xx: move LPC32XX GPIO driver to GPIO subsystem As per example from the other ARM boards, push the LPC32XX GPIO driver down to the GPIO subsystem so it can be consolidated. Cc: Wolfram Sang Cc: Barry Song Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/gpio/Makefile | 1 + drivers/gpio/gpio-lpc32xx.c | 445 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 446 insertions(+) create mode 100644 drivers/gpio/gpio-lpc32xx.c (limited to 'drivers/gpio') diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 226982277330..33e095bdb65c 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o obj-$(CONFIG_MACH_KS8695) += gpio-ks8695.o obj-$(CONFIG_GPIO_LANGWELL) += gpio-langwell.o +obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c new file mode 100644 index 000000000000..b4e9d35a6fa6 --- /dev/null +++ b/drivers/gpio/gpio-lpc32xx.c @@ -0,0 +1,445 @@ +/* + * arch/arm/mach-lpc32xx/gpiolib.c + * + * Author: Kevin Wells + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define LPC32XX_GPIO_P3_INP_STATE _GPREG(0x000) +#define LPC32XX_GPIO_P3_OUTP_SET _GPREG(0x004) +#define LPC32XX_GPIO_P3_OUTP_CLR _GPREG(0x008) +#define LPC32XX_GPIO_P3_OUTP_STATE _GPREG(0x00C) +#define LPC32XX_GPIO_P2_DIR_SET _GPREG(0x010) +#define LPC32XX_GPIO_P2_DIR_CLR _GPREG(0x014) +#define LPC32XX_GPIO_P2_DIR_STATE _GPREG(0x018) +#define LPC32XX_GPIO_P2_INP_STATE _GPREG(0x01C) +#define LPC32XX_GPIO_P2_OUTP_SET _GPREG(0x020) +#define LPC32XX_GPIO_P2_OUTP_CLR _GPREG(0x024) +#define LPC32XX_GPIO_P2_MUX_SET _GPREG(0x028) +#define LPC32XX_GPIO_P2_MUX_CLR _GPREG(0x02C) +#define LPC32XX_GPIO_P2_MUX_STATE _GPREG(0x030) +#define LPC32XX_GPIO_P0_INP_STATE _GPREG(0x040) +#define LPC32XX_GPIO_P0_OUTP_SET _GPREG(0x044) +#define LPC32XX_GPIO_P0_OUTP_CLR _GPREG(0x048) +#define LPC32XX_GPIO_P0_OUTP_STATE _GPREG(0x04C) +#define LPC32XX_GPIO_P0_DIR_SET _GPREG(0x050) +#define LPC32XX_GPIO_P0_DIR_CLR _GPREG(0x054) +#define LPC32XX_GPIO_P0_DIR_STATE _GPREG(0x058) +#define LPC32XX_GPIO_P1_INP_STATE _GPREG(0x060) +#define LPC32XX_GPIO_P1_OUTP_SET _GPREG(0x064) +#define LPC32XX_GPIO_P1_OUTP_CLR _GPREG(0x068) +#define LPC32XX_GPIO_P1_OUTP_STATE _GPREG(0x06C) +#define LPC32XX_GPIO_P1_DIR_SET _GPREG(0x070) +#define LPC32XX_GPIO_P1_DIR_CLR _GPREG(0x074) +#define LPC32XX_GPIO_P1_DIR_STATE _GPREG(0x078) + +#define GPIO012_PIN_TO_BIT(x) (1 << (x)) +#define GPIO3_PIN_TO_BIT(x) (1 << ((x) + 25)) +#define GPO3_PIN_TO_BIT(x) (1 << (x)) +#define GPIO012_PIN_IN_SEL(x, y) (((x) >> (y)) & 1) +#define GPIO3_PIN_IN_SHIFT(x) ((x) == 5 ? 24 : 10 + (x)) +#define GPIO3_PIN_IN_SEL(x, y) ((x) >> GPIO3_PIN_IN_SHIFT(y)) +#define GPIO3_PIN5_IN_SEL(x) (((x) >> 24) & 1) +#define GPI3_PIN_IN_SEL(x, y) (((x) >> (y)) & 1) + +struct gpio_regs { + void __iomem *inp_state; + void __iomem *outp_set; + void __iomem *outp_clr; + void __iomem *dir_set; + void __iomem *dir_clr; +}; + +/* + * GPIO names + */ +static const char *gpio_p0_names[LPC32XX_GPIO_P0_MAX] = { + "p0.0", "p0.1", "p0.2", "p0.3", + "p0.4", "p0.5", "p0.6", "p0.7" +}; + +static const char *gpio_p1_names[LPC32XX_GPIO_P1_MAX] = { + "p1.0", "p1.1", "p1.2", "p1.3", + "p1.4", "p1.5", "p1.6", "p1.7", + "p1.8", "p1.9", "p1.10", "p1.11", + "p1.12", "p1.13", "p1.14", "p1.15", + "p1.16", "p1.17", "p1.18", "p1.19", + "p1.20", "p1.21", "p1.22", "p1.23", +}; + +static const char *gpio_p2_names[LPC32XX_GPIO_P2_MAX] = { + "p2.0", "p2.1", "p2.2", "p2.3", + "p2.4", "p2.5", "p2.6", "p2.7", + "p2.8", "p2.9", "p2.10", "p2.11", + "p2.12" +}; + +static const char *gpio_p3_names[LPC32XX_GPIO_P3_MAX] = { + "gpi000", "gpio01", "gpio02", "gpio03", + "gpio04", "gpio05" +}; + +static const char *gpi_p3_names[LPC32XX_GPI_P3_MAX] = { + "gpi00", "gpi01", "gpi02", "gpi03", + "gpi04", "gpi05", "gpi06", "gpi07", + "gpi08", "gpi09", NULL, NULL, + NULL, NULL, NULL, "gpi15", + "gpi16", "gpi17", "gpi18", "gpi19", + "gpi20", "gpi21", "gpi22", "gpi23", + "gpi24", "gpi25", "gpi26", "gpi27" +}; + +static const char *gpo_p3_names[LPC32XX_GPO_P3_MAX] = { + "gpo00", "gpo01", "gpo02", "gpo03", + "gpo04", "gpo05", "gpo06", "gpo07", + "gpo08", "gpo09", "gpo10", "gpo11", + "gpo12", "gpo13", "gpo14", "gpo15", + "gpo16", "gpo17", "gpo18", "gpo19", + "gpo20", "gpo21", "gpo22", "gpo23" +}; + +static struct gpio_regs gpio_grp_regs_p0 = { + .inp_state = LPC32XX_GPIO_P0_INP_STATE, + .outp_set = LPC32XX_GPIO_P0_OUTP_SET, + .outp_clr = LPC32XX_GPIO_P0_OUTP_CLR, + .dir_set = LPC32XX_GPIO_P0_DIR_SET, + .dir_clr = LPC32XX_GPIO_P0_DIR_CLR, +}; + +static struct gpio_regs gpio_grp_regs_p1 = { + .inp_state = LPC32XX_GPIO_P1_INP_STATE, + .outp_set = LPC32XX_GPIO_P1_OUTP_SET, + .outp_clr = LPC32XX_GPIO_P1_OUTP_CLR, + .dir_set = LPC32XX_GPIO_P1_DIR_SET, + .dir_clr = LPC32XX_GPIO_P1_DIR_CLR, +}; + +static struct gpio_regs gpio_grp_regs_p2 = { + .inp_state = LPC32XX_GPIO_P2_INP_STATE, + .outp_set = LPC32XX_GPIO_P2_OUTP_SET, + .outp_clr = LPC32XX_GPIO_P2_OUTP_CLR, + .dir_set = LPC32XX_GPIO_P2_DIR_SET, + .dir_clr = LPC32XX_GPIO_P2_DIR_CLR, +}; + +static struct gpio_regs gpio_grp_regs_p3 = { + .inp_state = LPC32XX_GPIO_P3_INP_STATE, + .outp_set = LPC32XX_GPIO_P3_OUTP_SET, + .outp_clr = LPC32XX_GPIO_P3_OUTP_CLR, + .dir_set = LPC32XX_GPIO_P2_DIR_SET, + .dir_clr = LPC32XX_GPIO_P2_DIR_CLR, +}; + +struct lpc32xx_gpio_chip { + struct gpio_chip chip; + struct gpio_regs *gpio_grp; +}; + +static inline struct lpc32xx_gpio_chip *to_lpc32xx_gpio( + struct gpio_chip *gpc) +{ + return container_of(gpc, struct lpc32xx_gpio_chip, chip); +} + +static void __set_gpio_dir_p012(struct lpc32xx_gpio_chip *group, + unsigned pin, int input) +{ + if (input) + __raw_writel(GPIO012_PIN_TO_BIT(pin), + group->gpio_grp->dir_clr); + else + __raw_writel(GPIO012_PIN_TO_BIT(pin), + group->gpio_grp->dir_set); +} + +static void __set_gpio_dir_p3(struct lpc32xx_gpio_chip *group, + unsigned pin, int input) +{ + u32 u = GPIO3_PIN_TO_BIT(pin); + + if (input) + __raw_writel(u, group->gpio_grp->dir_clr); + else + __raw_writel(u, group->gpio_grp->dir_set); +} + +static void __set_gpio_level_p012(struct lpc32xx_gpio_chip *group, + unsigned pin, int high) +{ + if (high) + __raw_writel(GPIO012_PIN_TO_BIT(pin), + group->gpio_grp->outp_set); + else + __raw_writel(GPIO012_PIN_TO_BIT(pin), + group->gpio_grp->outp_clr); +} + +static void __set_gpio_level_p3(struct lpc32xx_gpio_chip *group, + unsigned pin, int high) +{ + u32 u = GPIO3_PIN_TO_BIT(pin); + + if (high) + __raw_writel(u, group->gpio_grp->outp_set); + else + __raw_writel(u, group->gpio_grp->outp_clr); +} + +static void __set_gpo_level_p3(struct lpc32xx_gpio_chip *group, + unsigned pin, int high) +{ + if (high) + __raw_writel(GPO3_PIN_TO_BIT(pin), group->gpio_grp->outp_set); + else + __raw_writel(GPO3_PIN_TO_BIT(pin), group->gpio_grp->outp_clr); +} + +static int __get_gpio_state_p012(struct lpc32xx_gpio_chip *group, + unsigned pin) +{ + return GPIO012_PIN_IN_SEL(__raw_readl(group->gpio_grp->inp_state), + pin); +} + +static int __get_gpio_state_p3(struct lpc32xx_gpio_chip *group, + unsigned pin) +{ + int state = __raw_readl(group->gpio_grp->inp_state); + + /* + * P3 GPIO pin input mapping is not contiguous, GPIOP3-0..4 is mapped + * to bits 10..14, while GPIOP3-5 is mapped to bit 24. + */ + return GPIO3_PIN_IN_SEL(state, pin); +} + +static int __get_gpi_state_p3(struct lpc32xx_gpio_chip *group, + unsigned pin) +{ + return GPI3_PIN_IN_SEL(__raw_readl(group->gpio_grp->inp_state), pin); +} + +/* + * GENERIC_GPIO primitives. + */ +static int lpc32xx_gpio_dir_input_p012(struct gpio_chip *chip, + unsigned pin) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + __set_gpio_dir_p012(group, pin, 1); + + return 0; +} + +static int lpc32xx_gpio_dir_input_p3(struct gpio_chip *chip, + unsigned pin) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + __set_gpio_dir_p3(group, pin, 1); + + return 0; +} + +static int lpc32xx_gpio_dir_in_always(struct gpio_chip *chip, + unsigned pin) +{ + return 0; +} + +static int lpc32xx_gpio_get_value_p012(struct gpio_chip *chip, unsigned pin) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + return __get_gpio_state_p012(group, pin); +} + +static int lpc32xx_gpio_get_value_p3(struct gpio_chip *chip, unsigned pin) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + return __get_gpio_state_p3(group, pin); +} + +static int lpc32xx_gpi_get_value(struct gpio_chip *chip, unsigned pin) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + return __get_gpi_state_p3(group, pin); +} + +static int lpc32xx_gpio_dir_output_p012(struct gpio_chip *chip, unsigned pin, + int value) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + __set_gpio_dir_p012(group, pin, 0); + + return 0; +} + +static int lpc32xx_gpio_dir_output_p3(struct gpio_chip *chip, unsigned pin, + int value) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + __set_gpio_dir_p3(group, pin, 0); + + return 0; +} + +static int lpc32xx_gpio_dir_out_always(struct gpio_chip *chip, unsigned pin, + int value) +{ + return 0; +} + +static void lpc32xx_gpio_set_value_p012(struct gpio_chip *chip, unsigned pin, + int value) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + __set_gpio_level_p012(group, pin, value); +} + +static void lpc32xx_gpio_set_value_p3(struct gpio_chip *chip, unsigned pin, + int value) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + __set_gpio_level_p3(group, pin, value); +} + +static void lpc32xx_gpo_set_value(struct gpio_chip *chip, unsigned pin, + int value) +{ + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + __set_gpo_level_p3(group, pin, value); +} + +static int lpc32xx_gpio_request(struct gpio_chip *chip, unsigned pin) +{ + if (pin < chip->ngpio) + return 0; + + return -EINVAL; +} + +static struct lpc32xx_gpio_chip lpc32xx_gpiochip[] = { + { + .chip = { + .label = "gpio_p0", + .direction_input = lpc32xx_gpio_dir_input_p012, + .get = lpc32xx_gpio_get_value_p012, + .direction_output = lpc32xx_gpio_dir_output_p012, + .set = lpc32xx_gpio_set_value_p012, + .request = lpc32xx_gpio_request, + .base = LPC32XX_GPIO_P0_GRP, + .ngpio = LPC32XX_GPIO_P0_MAX, + .names = gpio_p0_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs_p0, + }, + { + .chip = { + .label = "gpio_p1", + .direction_input = lpc32xx_gpio_dir_input_p012, + .get = lpc32xx_gpio_get_value_p012, + .direction_output = lpc32xx_gpio_dir_output_p012, + .set = lpc32xx_gpio_set_value_p012, + .request = lpc32xx_gpio_request, + .base = LPC32XX_GPIO_P1_GRP, + .ngpio = LPC32XX_GPIO_P1_MAX, + .names = gpio_p1_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs_p1, + }, + { + .chip = { + .label = "gpio_p2", + .direction_input = lpc32xx_gpio_dir_input_p012, + .get = lpc32xx_gpio_get_value_p012, + .direction_output = lpc32xx_gpio_dir_output_p012, + .set = lpc32xx_gpio_set_value_p012, + .request = lpc32xx_gpio_request, + .base = LPC32XX_GPIO_P2_GRP, + .ngpio = LPC32XX_GPIO_P2_MAX, + .names = gpio_p2_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs_p2, + }, + { + .chip = { + .label = "gpio_p3", + .direction_input = lpc32xx_gpio_dir_input_p3, + .get = lpc32xx_gpio_get_value_p3, + .direction_output = lpc32xx_gpio_dir_output_p3, + .set = lpc32xx_gpio_set_value_p3, + .request = lpc32xx_gpio_request, + .base = LPC32XX_GPIO_P3_GRP, + .ngpio = LPC32XX_GPIO_P3_MAX, + .names = gpio_p3_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs_p3, + }, + { + .chip = { + .label = "gpi_p3", + .direction_input = lpc32xx_gpio_dir_in_always, + .get = lpc32xx_gpi_get_value, + .request = lpc32xx_gpio_request, + .base = LPC32XX_GPI_P3_GRP, + .ngpio = LPC32XX_GPI_P3_MAX, + .names = gpi_p3_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs_p3, + }, + { + .chip = { + .label = "gpo_p3", + .direction_output = lpc32xx_gpio_dir_out_always, + .set = lpc32xx_gpo_set_value, + .request = lpc32xx_gpio_request, + .base = LPC32XX_GPO_P3_GRP, + .ngpio = LPC32XX_GPO_P3_MAX, + .names = gpo_p3_names, + .can_sleep = 0, + }, + .gpio_grp = &gpio_grp_regs_p3, + }, +}; + +void __init lpc32xx_gpio_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(lpc32xx_gpiochip); i++) + gpiochip_add(&lpc32xx_gpiochip[i].chip); +} -- cgit v1.2.2 From 9c587c05d715ca8461342c2cb3b4a67036b5c22b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 22 Aug 2011 08:45:15 +0100 Subject: ARM: 7045/1: mach-lpc32xx: break out GPIO driver specifics The file is included from upper directories and deal with generic GPIO and gpiolib stuff. Break out the platform and driver specific defines and functions into its own header file. Cc: Wolfram Sang Cc: Barry Song Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/gpio/gpio-lpc32xx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c index b4e9d35a6fa6..5b6948081f8f 100644 --- a/drivers/gpio/gpio-lpc32xx.c +++ b/drivers/gpio/gpio-lpc32xx.c @@ -24,6 +24,7 @@ #include #include +#include #define LPC32XX_GPIO_P3_INP_STATE _GPREG(0x000) #define LPC32XX_GPIO_P3_OUTP_SET _GPREG(0x004) -- cgit v1.2.2 From 2428835fc6a579b68dde16d37e0b72ca29259c96 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 22 Aug 2011 08:49:07 +0100 Subject: ARM: 7049/1: mach-sa1100: move SA1100 GPIO driver to GPIO subsystem As per example from the other ARM boards, push the SA100 GPIO driver down to the GPIO subsystem so it can be consolidated. Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/gpio/Makefile | 2 +- drivers/gpio/gpio-sa1100.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 drivers/gpio/gpio-sa1100.c (limited to 'drivers/gpio') diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 33e095bdb65c..84bc7389e367 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -45,7 +45,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o obj-$(CONFIG_GPIO_S5PC100) += gpio-s5pc100.o obj-$(CONFIG_GPIO_S5PV210) += gpio-s5pv210.o - +obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o obj-$(CONFIG_GPIO_SCH) += gpio-sch.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c new file mode 100644 index 000000000000..b6c1f6d80649 --- /dev/null +++ b/drivers/gpio/gpio-sa1100.c @@ -0,0 +1,63 @@ +/* + * linux/arch/arm/mach-sa1100/gpio.c + * + * Generic SA-1100 GPIO handling + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include + +#include + +static int sa1100_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + return GPLR & GPIO_GPIO(offset); +} + +static void sa1100_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + if (value) + GPSR = GPIO_GPIO(offset); + else + GPCR = GPIO_GPIO(offset); +} + +static int sa1100_direction_input(struct gpio_chip *chip, unsigned offset) +{ + unsigned long flags; + + local_irq_save(flags); + GPDR &= ~GPIO_GPIO(offset); + local_irq_restore(flags); + return 0; +} + +static int sa1100_direction_output(struct gpio_chip *chip, unsigned offset, int value) +{ + unsigned long flags; + + local_irq_save(flags); + sa1100_gpio_set(chip, offset, value); + GPDR |= GPIO_GPIO(offset); + local_irq_restore(flags); + return 0; +} + +static struct gpio_chip sa1100_gpio_chip = { + .label = "gpio", + .direction_input = sa1100_direction_input, + .direction_output = sa1100_direction_output, + .set = sa1100_gpio_set, + .get = sa1100_gpio_get, + .base = 0, + .ngpio = GPIO_MAX + 1, +}; + +void __init sa1100_init_gpio(void) +{ + gpiochip_add(&sa1100_gpio_chip); +} -- cgit v1.2.2 From ece9528e5f88cee11303fceefe39382f1030cd4e Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Tue, 12 Jul 2011 08:18:15 -0700 Subject: gpio/omap: replace MOD_REG_BIT macro with static inline This macro is ugly and confusing, especially since it passes in most arguments, but uses an implied 'base' from the caller. Replace it with an equivalent static inline. Signed-off-by: Kevin Hilman --- drivers/gpio/gpio-omap.c | 54 ++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 25 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 0599854e2217..34a7110d9bc8 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -148,13 +148,17 @@ static int _get_gpio_dataout(struct gpio_bank *bank, int gpio) return (__raw_readl(reg) & GPIO_BIT(bank, gpio)) != 0; } -#define MOD_REG_BIT(reg, bit_mask, set) \ -do { \ - int l = __raw_readl(base + reg); \ - if (set) l |= bit_mask; \ - else l &= ~bit_mask; \ - __raw_writel(l, base + reg); \ -} while(0) +static inline void _gpio_rmw(void __iomem *base, u32 reg, u32 mask, bool set) +{ + int l = __raw_readl(base + reg); + + if (set) + l |= mask; + else + l &= ~mask; + + __raw_writel(l, base + reg); +} /** * _set_gpio_debounce - low level gpio debounce time @@ -210,28 +214,28 @@ static inline void set_24xx_gpio_triggering(struct gpio_bank *bank, int gpio, u32 gpio_bit = 1 << gpio; if (cpu_is_omap44xx()) { - MOD_REG_BIT(OMAP4_GPIO_LEVELDETECT0, gpio_bit, - trigger & IRQ_TYPE_LEVEL_LOW); - MOD_REG_BIT(OMAP4_GPIO_LEVELDETECT1, gpio_bit, - trigger & IRQ_TYPE_LEVEL_HIGH); - MOD_REG_BIT(OMAP4_GPIO_RISINGDETECT, gpio_bit, - trigger & IRQ_TYPE_EDGE_RISING); - MOD_REG_BIT(OMAP4_GPIO_FALLINGDETECT, gpio_bit, - trigger & IRQ_TYPE_EDGE_FALLING); + _gpio_rmw(base, OMAP4_GPIO_LEVELDETECT0, gpio_bit, + trigger & IRQ_TYPE_LEVEL_LOW); + _gpio_rmw(base, OMAP4_GPIO_LEVELDETECT1, gpio_bit, + trigger & IRQ_TYPE_LEVEL_HIGH); + _gpio_rmw(base, OMAP4_GPIO_RISINGDETECT, gpio_bit, + trigger & IRQ_TYPE_EDGE_RISING); + _gpio_rmw(base, OMAP4_GPIO_FALLINGDETECT, gpio_bit, + trigger & IRQ_TYPE_EDGE_FALLING); } else { - MOD_REG_BIT(OMAP24XX_GPIO_LEVELDETECT0, gpio_bit, - trigger & IRQ_TYPE_LEVEL_LOW); - MOD_REG_BIT(OMAP24XX_GPIO_LEVELDETECT1, gpio_bit, - trigger & IRQ_TYPE_LEVEL_HIGH); - MOD_REG_BIT(OMAP24XX_GPIO_RISINGDETECT, gpio_bit, - trigger & IRQ_TYPE_EDGE_RISING); - MOD_REG_BIT(OMAP24XX_GPIO_FALLINGDETECT, gpio_bit, - trigger & IRQ_TYPE_EDGE_FALLING); + _gpio_rmw(base, OMAP24XX_GPIO_LEVELDETECT0, gpio_bit, + trigger & IRQ_TYPE_LEVEL_LOW); + _gpio_rmw(base, OMAP24XX_GPIO_LEVELDETECT1, gpio_bit, + trigger & IRQ_TYPE_LEVEL_HIGH); + _gpio_rmw(base, OMAP24XX_GPIO_RISINGDETECT, gpio_bit, + trigger & IRQ_TYPE_EDGE_RISING); + _gpio_rmw(base, OMAP24XX_GPIO_FALLINGDETECT, gpio_bit, + trigger & IRQ_TYPE_EDGE_FALLING); } if (likely(!(bank->non_wakeup_gpios & gpio_bit))) { if (cpu_is_omap44xx()) { - MOD_REG_BIT(OMAP4_GPIO_IRQWAKEN0, gpio_bit, - trigger != 0); + _gpio_rmw(base, OMAP4_GPIO_IRQWAKEN0, gpio_bit, + trigger != 0); } else { /* * GPIO wakeup request can only be generated on edge -- cgit v1.2.2 From 832337490f22987a1b739ba840e105c0c9af01bc Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 18 Jul 2011 07:43:14 -0700 Subject: gpio/omap: check return value from irq_alloc_generic_chip Ensure return value of irq_alloc_generic_chip() is checked before continuing on to use it. Signed-off-by: Todd Poynor Signed-off-by: Kevin Hilman --- drivers/gpio/gpio-omap.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 34a7110d9bc8..f0208a958185 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1090,6 +1090,11 @@ omap_mpuio_alloc_gc(struct gpio_bank *bank, unsigned int irq_start, gc = irq_alloc_generic_chip("MPUIO", 1, irq_start, bank->base, handle_simple_irq); + if (!gc) { + dev_err(bank->dev, "Memory alloc failed for gc\n"); + return; + } + ct = gc->chip_types; /* NOTE: No ack required, reading IRQ status clears it. */ -- cgit v1.2.2 From 470080015c1f8bbd15ba1486d5c4bd8a3e7fa79a Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 23 Aug 2011 00:39:55 +0100 Subject: ARM: 7052/1: gpio/tegra: Remove use of irq_to_gpio irq_to_gpio is being removed. Replace the only use of that API by the ARM Tegra sub-architecture. Signed-off-by: Stephen Warren Signed-off-by: Russell King --- drivers/gpio/gpio-tegra.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 747eb40e8afe..ecade29d79e8 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -331,6 +331,7 @@ static struct lock_class_key gpio_lock_class; static int __init tegra_gpio_init(void) { struct tegra_gpio_bank *bank; + int gpio; int i; int j; @@ -352,14 +353,17 @@ static int __init tegra_gpio_init(void) gpiochip_add(&tegra_gpio_chip); - for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + TEGRA_NR_GPIOS); i++) { - bank = &tegra_gpio_banks[GPIO_BANK(irq_to_gpio(i))]; + for (gpio = 0; gpio < TEGRA_NR_GPIOS; gpio++) { + int irq = TEGRA_GPIO_TO_IRQ(gpio); + /* No validity check; all Tegra GPIOs are valid IRQs */ - irq_set_lockdep_class(i, &gpio_lock_class); - irq_set_chip_data(i, bank); - irq_set_chip_and_handler(i, &tegra_gpio_irq_chip, + bank = &tegra_gpio_banks[GPIO_BANK(gpio)]; + + irq_set_lockdep_class(irq, &gpio_lock_class); + irq_set_chip_data(irq, bank); + irq_set_chip_and_handler(irq, &tegra_gpio_irq_chip, handle_simple_irq); - set_irq_flags(i, IRQF_VALID); + set_irq_flags(irq, IRQF_VALID); } for (i = 0; i < ARRAY_SIZE(tegra_gpio_banks); i++) { -- cgit v1.2.2 From 438a99c078b1bc3c9aebd92e7928f2477311d3e5 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 23 Aug 2011 00:39:56 +0100 Subject: ARM: 7053/1: gpio/tegra: Implement gpio_chip.to_irq Signed-off-by: Stephen Warren Signed-off-by: Russell King --- drivers/gpio/gpio-tegra.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index ecade29d79e8..df64536464f0 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -134,7 +134,10 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip, unsigned offset, return 0; } - +static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + return TEGRA_GPIO_TO_IRQ(offset); +} static struct gpio_chip tegra_gpio_chip = { .label = "tegra-gpio", @@ -142,6 +145,7 @@ static struct gpio_chip tegra_gpio_chip = { .get = tegra_gpio_get, .direction_output = tegra_gpio_direction_output, .set = tegra_gpio_set, + .to_irq = tegra_gpio_to_irq, .base = 0, .ngpio = TEGRA_NR_GPIOS, }; -- cgit v1.2.2 From 526a0dc771a5cac3224e26abd1f018976b967516 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 5 Sep 2011 03:29:04 +0100 Subject: ARM: 7074/1: gpio: davinci: eliminate unused variable warnings Since commit 5093aec872e5be7a55d8dd2b639e8a3818dc19db "arm: davinci: Cleanup irq chip code", the variable 'mask' and 'g' are not being used. This patch eliminate below unused variable warnings: CC drivers/gpio/gpio-davinci.o drivers/gpio/gpio-davinci.c: In function 'gpio_irq_type': drivers/gpio/gpio-davinci.c:234: warning: unused variable 'mask' drivers/gpio/gpio-davinci.c:233: warning: unused variable 'g' Signed-off-by: Axel Lin Signed-off-by: Russell King --- drivers/gpio/gpio-davinci.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index a8066e8edade..df0d59570a84 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -230,9 +230,6 @@ static void gpio_irq_enable(struct irq_data *d) static int gpio_irq_type(struct irq_data *d, unsigned trigger) { - struct davinci_gpio_regs __iomem *g = irq2regs(d->irq); - u32 mask = (u32) irq_data_get_irq_handler_data(d); - if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) return -EINVAL; -- cgit v1.2.2 From cc890cd78acd7ab03442907d354b6af34e973cb3 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 8 Sep 2011 09:04:51 +0100 Subject: ARM: 7083/1: rewrite U300 GPIO to use gpiolib This rewrites the U300 GPIO so as to use gpiolib and struct gpio_chip instead of just generic GPIO, hiding all the platform specifics and passing in GPIO chip variant as platform data at runtime instead of the compiletime kludges. As a result is now empty for U300 and using just defaults. Cc: Grant Likely Cc: Debian kernel maintainers Cc: Arnaud Patard Reported-by: Ben Hutchings Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/gpio/Kconfig | 9 + drivers/gpio/gpio-u300.c | 1189 +++++++++++++++++++++++++++------------------- 2 files changed, 713 insertions(+), 485 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d539efd96d4b..4caa3d37bbde 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -178,6 +178,15 @@ config GPIO_SCH The Intel Tunnel Creek processor has 5 GPIOs powered by the core power rail and 9 from suspend power supply. +config GPIO_U300 + bool "ST-Ericsson U300 COH 901 335/571 GPIO" + depends on GPIOLIB && ARCH_U300 + help + Say yes here to support GPIO interface on ST-Ericsson U300. + The names of the two IP block variants supported are + COH 901 335 and COH 901 571/3. They contain 3, 5 or 7 + ports of 8 GPIO pins each. + config GPIO_VX855 tristate "VIA VX855/VX875 GPIO" depends on MFD_SUPPORT && PCI diff --git a/drivers/gpio/gpio-u300.c b/drivers/gpio/gpio-u300.c index 92f2b8c06de1..4035778852b0 100644 --- a/drivers/gpio/gpio-u300.c +++ b/drivers/gpio/gpio-u300.c @@ -1,18 +1,17 @@ /* * U300 GPIO module. * - * Copyright (C) 2007-2009 ST-Ericsson AB + * Copyright (C) 2007-2011 ST-Ericsson AB * License terms: GNU General Public License (GPL) version 2 * This can driver either of the two basic GPIO cores * available in the U300 platforms: * COH 901 335 - Used in DB3150 (U300 1.0) and DB3200 (U330 1.0) * COH 901 571/3 - Used in DB3210 (U365 2.0) and DB3350 (U335 1.0) - * Notice that you also have inline macros in - * Author: Linus Walleij + * Author: Linus Walleij * Author: Jonas Aaberg - * */ #include +#include #include #include #include @@ -21,678 +20,898 @@ #include #include #include +#include +#include #include -/* Reference to GPIO block clock */ -static struct clk *clk; +/* + * Bias modes for U300 GPIOs + * + * GPIO_U300_CONFIG_BIAS_UNKNOWN: this bias mode is not known to us + * GPIO_U300_CONFIG_BIAS_FLOAT: no specific bias, the GPIO will float or state + * is not controlled by software + * GPIO_U300_CONFIG_BIAS_PULL_UP: the GPIO will be pulled up (usually with high + * impedance to VDD) + */ +#define GPIO_U300_CONFIG_BIAS_UNKNOWN 0x1000 +#define GPIO_U300_CONFIG_BIAS_FLOAT 0x1001 +#define GPIO_U300_CONFIG_BIAS_PULL_UP 0x1002 -/* Memory resource */ -static struct resource *memres; -static void __iomem *virtbase; -static struct device *gpiodev; +/* + * Drive modes for U300 GPIOs (output) + * + * GPIO_U300_CONFIG_DRIVE_PUSH_PULL: the GPIO will be driven actively high and + * low, this is the most typical case and is typically achieved with two + * active transistors on the output + * GPIO_U300_CONFIG_DRIVE_OPEN_DRAIN: the GPIO will be driven with open drain + * (open collector) which means it is usually wired with other output + * ports which are then pulled up with an external resistor + * GPIO_U300_CONFIG_DRIVE_OPEN_SOURCE: the GPIO will be driven with open drain + * (open emitter) which is the same as open drain mutatis mutandis but + * pulled to ground + */ +#define GPIO_U300_CONFIG_DRIVE_PUSH_PULL 0x2000 +#define GPIO_U300_CONFIG_DRIVE_OPEN_DRAIN 0x2001 +#define GPIO_U300_CONFIG_DRIVE_OPEN_SOURCE 0x2002 + +/* + * Register definitions for COH 901 335 variant + */ +#define U300_335_PORT_STRIDE (0x1C) +/* Port X Pin Data Register 32bit, this is both input and output (R/W) */ +#define U300_335_PXPDIR (0x00) +#define U300_335_PXPDOR (0x00) +/* Port X Pin Config Register 32bit (R/W) */ +#define U300_335_PXPCR (0x04) +/* This register layout is the same in both blocks */ +#define U300_GPIO_PXPCR_ALL_PINS_MODE_MASK (0x0000FFFFUL) +#define U300_GPIO_PXPCR_PIN_MODE_MASK (0x00000003UL) +#define U300_GPIO_PXPCR_PIN_MODE_SHIFT (0x00000002UL) +#define U300_GPIO_PXPCR_PIN_MODE_INPUT (0x00000000UL) +#define U300_GPIO_PXPCR_PIN_MODE_OUTPUT_PUSH_PULL (0x00000001UL) +#define U300_GPIO_PXPCR_PIN_MODE_OUTPUT_OPEN_DRAIN (0x00000002UL) +#define U300_GPIO_PXPCR_PIN_MODE_OUTPUT_OPEN_SOURCE (0x00000003UL) +/* Port X Interrupt Event Register 32bit (R/W) */ +#define U300_335_PXIEV (0x08) +/* Port X Interrupt Enable Register 32bit (R/W) */ +#define U300_335_PXIEN (0x0C) +/* Port X Interrupt Force Register 32bit (R/W) */ +#define U300_335_PXIFR (0x10) +/* Port X Interrupt Config Register 32bit (R/W) */ +#define U300_335_PXICR (0x14) +/* This register layout is the same in both blocks */ +#define U300_GPIO_PXICR_ALL_IRQ_CONFIG_MASK (0x000000FFUL) +#define U300_GPIO_PXICR_IRQ_CONFIG_MASK (0x00000001UL) +#define U300_GPIO_PXICR_IRQ_CONFIG_FALLING_EDGE (0x00000000UL) +#define U300_GPIO_PXICR_IRQ_CONFIG_RISING_EDGE (0x00000001UL) +/* Port X Pull-up Enable Register 32bit (R/W) */ +#define U300_335_PXPER (0x18) +/* This register layout is the same in both blocks */ +#define U300_GPIO_PXPER_ALL_PULL_UP_DISABLE_MASK (0x000000FFUL) +#define U300_GPIO_PXPER_PULL_UP_DISABLE (0x00000001UL) +/* Control Register 32bit (R/W) */ +#define U300_335_CR (0x54) +#define U300_335_CR_BLOCK_CLOCK_ENABLE (0x00000001UL) + +/* + * Register definitions for COH 901 571 / 3 variant + */ +#define U300_571_PORT_STRIDE (0x30) +/* + * Control Register 32bit (R/W) + * bit 15-9 (mask 0x0000FE00) contains the number of cores. 8*cores + * gives the number of GPIO pins. + * bit 8-2 (mask 0x000001FC) contains the core version ID. + */ +#define U300_571_CR (0x00) +#define U300_571_CR_SYNC_SEL_ENABLE (0x00000002UL) +#define U300_571_CR_BLOCK_CLKRQ_ENABLE (0x00000001UL) +/* + * These registers have the same layout and function as the corresponding + * COH 901 335 registers, just at different offset. + */ +#define U300_571_PXPDIR (0x04) +#define U300_571_PXPDOR (0x08) +#define U300_571_PXPCR (0x0C) +#define U300_571_PXPER (0x10) +#define U300_571_PXIEV (0x14) +#define U300_571_PXIEN (0x18) +#define U300_571_PXIFR (0x1C) +#define U300_571_PXICR (0x20) + +/* 8 bits per port, no version has more than 7 ports */ +#define U300_GPIO_PINS_PER_PORT 8 +#define U300_GPIO_MAX (U300_GPIO_PINS_PER_PORT * 7) + +struct u300_gpio { + struct gpio_chip chip; + struct list_head port_list; + struct clk *clk; + struct resource *memres; + void __iomem *base; + struct device *dev; + int irq_base; + u32 stride; + /* Register offsets */ + u32 pcr; + u32 dor; + u32 dir; + u32 per; + u32 icr; + u32 ien; + u32 iev; +}; struct u300_gpio_port { - const char *name; + struct list_head node; + struct u300_gpio *gpio; + char name[8]; int irq; int number; + u8 toggle_edge_mode; }; +/* + * Macro to expand to read a specific register found in the "gpio" + * struct. It requires the struct u300_gpio *gpio variable to exist in + * its context. It calculates the port offset from the given pin + * offset, muliplies by the port stride and adds the register offset + * so it provides a pointer to the desired register. + */ +#define U300_PIN_REG(pin, reg) \ + (gpio->base + (pin >> 3) * gpio->stride + gpio->reg) -static struct u300_gpio_port gpio_ports[] = { - { - .name = "gpio0", - .number = 0, - }, - { - .name = "gpio1", - .number = 1, - }, - { - .name = "gpio2", - .number = 2, - }, -#ifdef U300_COH901571_3 - { - .name = "gpio3", - .number = 3, - }, - { - .name = "gpio4", - .number = 4, - }, -#ifdef CONFIG_MACH_U300_BS335 - { - .name = "gpio5", - .number = 5, - }, - { - .name = "gpio6", - .number = 6, - }, -#endif -#endif +/* + * Provides a bitmask for a specific gpio pin inside an 8-bit GPIO + * register. + */ +#define U300_PIN_BIT(pin) \ + (1 << (pin & 0x07)) +struct u300_gpio_confdata { + u16 bias_mode; + bool output; + int outval; }; +/* BS335 has seven ports of 8 bits each = GPIO pins 0..55 */ +#define BS335_GPIO_NUM_PORTS 7 +/* BS365 has five ports of 8 bits each = GPIO pins 0..39 */ +#define BS365_GPIO_NUM_PORTS 5 -#ifdef U300_COH901571_3 +#define U300_FLOATING_INPUT { \ + .bias_mode = GPIO_U300_CONFIG_BIAS_FLOAT, \ + .output = false, \ +} -/* Default input value */ -#define DEFAULT_OUTPUT_LOW 0 -#define DEFAULT_OUTPUT_HIGH 1 +#define U300_PULL_UP_INPUT { \ + .bias_mode = GPIO_U300_CONFIG_BIAS_PULL_UP, \ + .output = false, \ +} -/* GPIO Pull-Up status */ -#define DISABLE_PULL_UP 0 -#define ENABLE_PULL_UP 1 +#define U300_OUTPUT_LOW { \ + .output = true, \ + .outval = 0, \ +} -#define GPIO_NOT_USED 0 -#define GPIO_IN 1 -#define GPIO_OUT 2 +#define U300_OUTPUT_HIGH { \ + .output = true, \ + .outval = 1, \ +} -struct u300_gpio_configuration_data { - unsigned char pin_usage; - unsigned char default_output_value; - unsigned char pull_up; -}; /* Initial configuration */ -const struct u300_gpio_configuration_data -u300_gpio_config[U300_GPIO_NUM_PORTS][U300_GPIO_PINS_PER_PORT] = { -#ifdef CONFIG_MACH_U300_BS335 +static const struct __initdata u300_gpio_confdata +bs335_gpio_config[BS335_GPIO_NUM_PORTS][U300_GPIO_PINS_PER_PORT] = { /* Port 0, pins 0-7 */ { - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_HIGH, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP} + U300_FLOATING_INPUT, + U300_OUTPUT_HIGH, + U300_FLOATING_INPUT, + U300_OUTPUT_LOW, + U300_OUTPUT_LOW, + U300_OUTPUT_LOW, + U300_OUTPUT_LOW, + U300_OUTPUT_LOW, }, /* Port 1, pins 0-7 */ { - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_HIGH, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP} + U300_OUTPUT_LOW, + U300_OUTPUT_LOW, + U300_OUTPUT_LOW, + U300_PULL_UP_INPUT, + U300_FLOATING_INPUT, + U300_OUTPUT_HIGH, + U300_OUTPUT_LOW, + U300_OUTPUT_LOW, }, /* Port 2, pins 0-7 */ { - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP} + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_OUTPUT_LOW, + U300_PULL_UP_INPUT, + U300_OUTPUT_LOW, + U300_PULL_UP_INPUT, }, /* Port 3, pins 0-7 */ { - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP} + U300_PULL_UP_INPUT, + U300_OUTPUT_LOW, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, }, /* Port 4, pins 0-7 */ { - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP} + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, }, /* Port 5, pins 0-7 */ { - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP} + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, }, /* Port 6, pind 0-7 */ { - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP} + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, } -#endif +}; -#ifdef CONFIG_MACH_U300_BS365 +static const struct __initdata u300_gpio_confdata +bs365_gpio_config[BS365_GPIO_NUM_PORTS][U300_GPIO_PINS_PER_PORT] = { /* Port 0, pins 0-7 */ { - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP} + U300_FLOATING_INPUT, + U300_OUTPUT_LOW, + U300_FLOATING_INPUT, + U300_OUTPUT_LOW, + U300_OUTPUT_LOW, + U300_OUTPUT_LOW, + U300_PULL_UP_INPUT, + U300_FLOATING_INPUT, }, /* Port 1, pins 0-7 */ { - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_HIGH, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP} + U300_OUTPUT_LOW, + U300_FLOATING_INPUT, + U300_OUTPUT_LOW, + U300_FLOATING_INPUT, + U300_FLOATING_INPUT, + U300_OUTPUT_HIGH, + U300_OUTPUT_LOW, + U300_OUTPUT_LOW, }, /* Port 2, pins 0-7 */ { - {GPIO_IN, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, DISABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP} + U300_FLOATING_INPUT, + U300_PULL_UP_INPUT, + U300_OUTPUT_LOW, + U300_OUTPUT_LOW, + U300_PULL_UP_INPUT, + U300_PULL_UP_INPUT, + U300_PULL_UP_INPUT, + U300_PULL_UP_INPUT, }, /* Port 3, pins 0-7 */ { - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP} + U300_PULL_UP_INPUT, + U300_PULL_UP_INPUT, + U300_PULL_UP_INPUT, + U300_PULL_UP_INPUT, + U300_PULL_UP_INPUT, + U300_PULL_UP_INPUT, + U300_PULL_UP_INPUT, + U300_PULL_UP_INPUT, }, /* Port 4, pins 0-7 */ { - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_IN, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, + U300_PULL_UP_INPUT, + U300_PULL_UP_INPUT, + U300_PULL_UP_INPUT, + U300_PULL_UP_INPUT, /* These 4 pins doesn't exist on DB3210 */ - {GPIO_OUT, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP}, - {GPIO_OUT, DEFAULT_OUTPUT_LOW, ENABLE_PULL_UP} + U300_OUTPUT_LOW, + U300_OUTPUT_LOW, + U300_OUTPUT_LOW, + U300_OUTPUT_LOW, } -#endif }; -#endif - -/* No users == we can power down GPIO */ -static int gpio_users; - -struct gpio_struct { - int (*callback)(void *); - void *data; - int users; -}; - -static struct gpio_struct gpio_pin[U300_GPIO_MAX]; - -/* - * Let drivers register callback in order to get notified when there is - * an interrupt on the gpio pin +/** + * to_u300_gpio() - get the pointer to u300_gpio + * @chip: the gpio chip member of the structure u300_gpio */ -int gpio_register_callback(unsigned gpio, int (*func)(void *arg), void *data) +static inline struct u300_gpio *to_u300_gpio(struct gpio_chip *chip) { - if (gpio_pin[gpio].callback) - dev_warn(gpiodev, "%s: WARNING: callback already " - "registered for gpio pin#%d\n", __func__, gpio); - gpio_pin[gpio].callback = func; - gpio_pin[gpio].data = data; - - return 0; + return container_of(chip, struct u300_gpio, chip); } -EXPORT_SYMBOL(gpio_register_callback); -int gpio_unregister_callback(unsigned gpio) +static int u300_gpio_get(struct gpio_chip *chip, unsigned offset) { - if (!gpio_pin[gpio].callback) - dev_warn(gpiodev, "%s: WARNING: callback already " - "unregistered for gpio pin#%d\n", __func__, gpio); - gpio_pin[gpio].callback = NULL; - gpio_pin[gpio].data = NULL; + struct u300_gpio *gpio = to_u300_gpio(chip); - return 0; + return readl(U300_PIN_REG(offset, dir)) & U300_PIN_BIT(offset); } -EXPORT_SYMBOL(gpio_unregister_callback); -/* Non-zero means valid */ -int gpio_is_valid(int number) +static void u300_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { - if (number >= 0 && - number < (U300_GPIO_NUM_PORTS * U300_GPIO_PINS_PER_PORT)) - return 1; - return 0; -} -EXPORT_SYMBOL(gpio_is_valid); + struct u300_gpio *gpio = to_u300_gpio(chip); + unsigned long flags; + u32 val; -int gpio_request(unsigned gpio, const char *label) -{ - if (gpio_pin[gpio].users) - return -EINVAL; - else - gpio_pin[gpio].users++; + local_irq_save(flags); - gpio_users++; + val = readl(U300_PIN_REG(offset, dor)); + if (value) + writel(val | U300_PIN_BIT(offset), U300_PIN_REG(offset, dor)); + else + writel(val & ~U300_PIN_BIT(offset), U300_PIN_REG(offset, dor)); - return 0; + local_irq_restore(flags); } -EXPORT_SYMBOL(gpio_request); -void gpio_free(unsigned gpio) +static int u300_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { - gpio_users--; - gpio_pin[gpio].users--; - if (unlikely(gpio_pin[gpio].users < 0)) { - dev_warn(gpiodev, "warning: gpio#%d release mismatch\n", - gpio); - gpio_pin[gpio].users = 0; - } - - return; -} -EXPORT_SYMBOL(gpio_free); + struct u300_gpio *gpio = to_u300_gpio(chip); + unsigned long flags; + u32 val; -/* This returns zero or nonzero */ -int gpio_get_value(unsigned gpio) -{ - return readl(virtbase + U300_GPIO_PXPDIR + - PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING) & (1 << (gpio & 0x07)); + local_irq_save(flags); + val = readl(U300_PIN_REG(offset, pcr)); + /* Mask out this pin, note 2 bits per setting */ + val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK << ((offset & 0x07) << 1)); + writel(val, U300_PIN_REG(offset, pcr)); + local_irq_restore(flags); + return 0; } -EXPORT_SYMBOL(gpio_get_value); -/* - * We hope that the compiler will optimize away the unused branch - * in case "value" is a constant - */ -void gpio_set_value(unsigned gpio, int value) +static int u300_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) { - u32 val; + struct u300_gpio *gpio = to_u300_gpio(chip); unsigned long flags; + u32 oldmode; + u32 val; local_irq_save(flags); - if (value) { - /* set */ - val = readl(virtbase + U300_GPIO_PXPDOR + - PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING) - & (1 << (gpio & 0x07)); - writel(val | (1 << (gpio & 0x07)), virtbase + - U300_GPIO_PXPDOR + - PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING); - } else { - /* clear */ - val = readl(virtbase + U300_GPIO_PXPDOR + - PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING) - & (1 << (gpio & 0x07)); - writel(val & ~(1 << (gpio & 0x07)), virtbase + - U300_GPIO_PXPDOR + - PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING); + val = readl(U300_PIN_REG(offset, pcr)); + /* + * Drive mode must be set by the special mode set function, set + * push/pull mode by default if no mode has been selected. + */ + oldmode = val & (U300_GPIO_PXPCR_PIN_MODE_MASK << + ((offset & 0x07) << 1)); + /* mode = 0 means input, else some mode is already set */ + if (oldmode == 0) { + val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK << + ((offset & 0x07) << 1)); + val |= (U300_GPIO_PXPCR_PIN_MODE_OUTPUT_PUSH_PULL + << ((offset & 0x07) << 1)); + writel(val, U300_PIN_REG(offset, pcr)); } + u300_gpio_set(chip, offset, value); local_irq_restore(flags); + return 0; } -EXPORT_SYMBOL(gpio_set_value); -int gpio_direction_input(unsigned gpio) +static int u300_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { + struct u300_gpio *gpio = to_u300_gpio(chip); + int retirq = gpio->irq_base + offset; + + dev_dbg(gpio->dev, "request IRQ for GPIO %d, return %d\n", offset, + retirq); + return retirq; +} + +static int u300_gpio_config(struct gpio_chip *chip, unsigned offset, + u16 param, unsigned long *data) +{ + struct u300_gpio *gpio = to_u300_gpio(chip); unsigned long flags; u32 val; - if (gpio > U300_GPIO_MAX) - return -EINVAL; - local_irq_save(flags); - val = readl(virtbase + U300_GPIO_PXPCR + PIN_TO_PORT(gpio) * - U300_GPIO_PORTX_SPACING); - /* Mask out this pin*/ - val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK << ((gpio & 0x07) << 1)); - /* This is not needed since it sets the bits to zero.*/ - /* val |= (U300_GPIO_PXPCR_PIN_MODE_INPUT << (gpio*2)); */ - writel(val, virtbase + U300_GPIO_PXPCR + PIN_TO_PORT(gpio) * - U300_GPIO_PORTX_SPACING); + switch (param) { + case GPIO_U300_CONFIG_BIAS_UNKNOWN: + case GPIO_U300_CONFIG_BIAS_FLOAT: + val = readl(U300_PIN_REG(offset, per)); + writel(val | U300_PIN_BIT(offset), U300_PIN_REG(offset, per)); + break; + case GPIO_U300_CONFIG_BIAS_PULL_UP: + val = readl(U300_PIN_REG(offset, per)); + writel(val & ~U300_PIN_BIT(offset), U300_PIN_REG(offset, per)); + break; + case GPIO_U300_CONFIG_DRIVE_PUSH_PULL: + val = readl(U300_PIN_REG(offset, pcr)); + val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK + << ((offset & 0x07) << 1)); + val |= (U300_GPIO_PXPCR_PIN_MODE_OUTPUT_PUSH_PULL + << ((offset & 0x07) << 1)); + writel(val, U300_PIN_REG(offset, pcr)); + break; + case GPIO_U300_CONFIG_DRIVE_OPEN_DRAIN: + val = readl(U300_PIN_REG(offset, pcr)); + val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK + << ((offset & 0x07) << 1)); + val |= (U300_GPIO_PXPCR_PIN_MODE_OUTPUT_OPEN_DRAIN + << ((offset & 0x07) << 1)); + writel(val, U300_PIN_REG(offset, pcr)); + break; + case GPIO_U300_CONFIG_DRIVE_OPEN_SOURCE: + val = readl(U300_PIN_REG(offset, pcr)); + val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK + << ((offset & 0x07) << 1)); + val |= (U300_GPIO_PXPCR_PIN_MODE_OUTPUT_OPEN_SOURCE + << ((offset & 0x07) << 1)); + writel(val, U300_PIN_REG(offset, pcr)); + break; + default: + local_irq_restore(flags); + dev_err(gpio->dev, "illegal configuration requested\n"); + return -EINVAL; + } local_irq_restore(flags); return 0; } -EXPORT_SYMBOL(gpio_direction_input); -int gpio_direction_output(unsigned gpio, int value) +static struct gpio_chip u300_gpio_chip = { + .label = "u300-gpio-chip", + .owner = THIS_MODULE, + .get = u300_gpio_get, + .set = u300_gpio_set, + .direction_input = u300_gpio_direction_input, + .direction_output = u300_gpio_direction_output, + .to_irq = u300_gpio_to_irq, +}; + +static void u300_toggle_trigger(struct u300_gpio *gpio, unsigned offset) { - unsigned long flags; u32 val; - if (gpio > U300_GPIO_MAX) - return -EINVAL; - - local_irq_save(flags); - val = readl(virtbase + U300_GPIO_PXPCR + PIN_TO_PORT(gpio) * - U300_GPIO_PORTX_SPACING); - /* Mask out this pin */ - val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK << ((gpio & 0x07) << 1)); - /* - * FIXME: configure for push/pull, open drain or open source per pin - * in setup. The current driver will only support push/pull. - */ - val |= (U300_GPIO_PXPCR_PIN_MODE_OUTPUT_PUSH_PULL - << ((gpio & 0x07) << 1)); - writel(val, virtbase + U300_GPIO_PXPCR + PIN_TO_PORT(gpio) * - U300_GPIO_PORTX_SPACING); - gpio_set_value(gpio, value); - local_irq_restore(flags); - return 0; + val = readl(U300_PIN_REG(offset, icr)); + /* Set mode depending on state */ + if (u300_gpio_get(&gpio->chip, offset)) { + /* High now, let's trigger on falling edge next then */ + writel(val & ~U300_PIN_BIT(offset), U300_PIN_REG(offset, icr)); + dev_dbg(gpio->dev, "next IRQ on falling edge on pin %d\n", + offset); + } else { + /* Low now, let's trigger on rising edge next then */ + writel(val | U300_PIN_BIT(offset), U300_PIN_REG(offset, icr)); + dev_dbg(gpio->dev, "next IRQ on rising edge on pin %d\n", + offset); + } } -EXPORT_SYMBOL(gpio_direction_output); -/* - * Enable an IRQ, edge is rising edge (!= 0) or falling edge (==0). - */ -void enable_irq_on_gpio_pin(unsigned gpio, int edge) +static int u300_gpio_irq_type(struct irq_data *d, unsigned trigger) { + struct u300_gpio_port *port = irq_data_get_irq_chip_data(d); + struct u300_gpio *gpio = port->gpio; + int offset = d->irq - gpio->irq_base; u32 val; - unsigned long flags; - local_irq_save(flags); - val = readl(virtbase + U300_GPIO_PXIEN + PIN_TO_PORT(gpio) * - U300_GPIO_PORTX_SPACING); - val |= (1 << (gpio & 0x07)); - writel(val, virtbase + U300_GPIO_PXIEN + PIN_TO_PORT(gpio) * - U300_GPIO_PORTX_SPACING); - val = readl(virtbase + U300_GPIO_PXICR + PIN_TO_PORT(gpio) * - U300_GPIO_PORTX_SPACING); - if (edge) - val |= (1 << (gpio & 0x07)); - else - val &= ~(1 << (gpio & 0x07)); - writel(val, virtbase + U300_GPIO_PXICR + PIN_TO_PORT(gpio) * - U300_GPIO_PORTX_SPACING); - local_irq_restore(flags); + if ((trigger & IRQF_TRIGGER_RISING) && + (trigger & IRQF_TRIGGER_FALLING)) { + /* + * The GPIO block can only trigger on falling OR rising edges, + * not both. So we need to toggle the mode whenever the pin + * goes from one state to the other with a special state flag + */ + dev_dbg(gpio->dev, + "trigger on both rising and falling edge on pin %d\n", + offset); + port->toggle_edge_mode |= U300_PIN_BIT(offset); + u300_toggle_trigger(gpio, offset); + } else if (trigger & IRQF_TRIGGER_RISING) { + dev_dbg(gpio->dev, "trigger on rising edge on pin %d\n", + offset); + val = readl(U300_PIN_REG(offset, icr)); + writel(val | U300_PIN_BIT(offset), U300_PIN_REG(offset, icr)); + port->toggle_edge_mode &= ~U300_PIN_BIT(offset); + } else if (trigger & IRQF_TRIGGER_FALLING) { + dev_dbg(gpio->dev, "trigger on falling edge on pin %d\n", + offset); + val = readl(U300_PIN_REG(offset, icr)); + writel(val & ~U300_PIN_BIT(offset), U300_PIN_REG(offset, icr)); + port->toggle_edge_mode &= ~U300_PIN_BIT(offset); + } + + return 0; } -EXPORT_SYMBOL(enable_irq_on_gpio_pin); -void disable_irq_on_gpio_pin(unsigned gpio) +static void u300_gpio_irq_enable(struct irq_data *d) { + struct u300_gpio_port *port = irq_data_get_irq_chip_data(d); + struct u300_gpio *gpio = port->gpio; + int offset = d->irq - gpio->irq_base; u32 val; unsigned long flags; local_irq_save(flags); - val = readl(virtbase + U300_GPIO_PXIEN + PIN_TO_PORT(gpio) * - U300_GPIO_PORTX_SPACING); - val &= ~(1 << (gpio & 0x07)); - writel(val, virtbase + U300_GPIO_PXIEN + PIN_TO_PORT(gpio) * - U300_GPIO_PORTX_SPACING); + val = readl(U300_PIN_REG(offset, ien)); + writel(val | U300_PIN_BIT(offset), U300_PIN_REG(offset, ien)); local_irq_restore(flags); } -EXPORT_SYMBOL(disable_irq_on_gpio_pin); -/* Enable (value == 0) or disable (value == 1) internal pullup */ -void gpio_pullup(unsigned gpio, int value) +static void u300_gpio_irq_disable(struct irq_data *d) { + struct u300_gpio_port *port = irq_data_get_irq_chip_data(d); + struct u300_gpio *gpio = port->gpio; + int offset = d->irq - gpio->irq_base; u32 val; unsigned long flags; local_irq_save(flags); - if (value) { - val = readl(virtbase + U300_GPIO_PXPER + PIN_TO_PORT(gpio) * - U300_GPIO_PORTX_SPACING); - writel(val | (1 << (gpio & 0x07)), virtbase + U300_GPIO_PXPER + - PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING); - } else { - val = readl(virtbase + U300_GPIO_PXPER + PIN_TO_PORT(gpio) * - U300_GPIO_PORTX_SPACING); - writel(val & ~(1 << (gpio & 0x07)), virtbase + U300_GPIO_PXPER + - PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING); - } + val = readl(U300_PIN_REG(offset, ien)); + writel(val & ~U300_PIN_BIT(offset), U300_PIN_REG(offset, ien)); local_irq_restore(flags); } -EXPORT_SYMBOL(gpio_pullup); -static irqreturn_t gpio_irq_handler(int irq, void *dev_id) +static struct irq_chip u300_gpio_irqchip = { + .name = "u300-gpio-irqchip", + .irq_enable = u300_gpio_irq_enable, + .irq_disable = u300_gpio_irq_disable, + .irq_set_type = u300_gpio_irq_type, + +}; + +static void u300_gpio_irq_handler(unsigned irq, struct irq_desc *desc) { - struct u300_gpio_port *port = dev_id; - u32 val; - int pin; + struct u300_gpio_port *port = irq_get_handler_data(irq); + struct u300_gpio *gpio = port->gpio; + int pinoffset = port->number << 3; /* get the right stride */ + unsigned long val; + desc->irq_data.chip->irq_ack(&desc->irq_data); /* Read event register */ - val = readl(virtbase + U300_GPIO_PXIEV + port->number * - U300_GPIO_PORTX_SPACING); - /* Mask with enable register */ - val &= readl(virtbase + U300_GPIO_PXIEV + port->number * - U300_GPIO_PORTX_SPACING); + val = readl(U300_PIN_REG(pinoffset, iev)); /* Mask relevant bits */ - val &= U300_GPIO_PXIEV_ALL_IRQ_EVENT_MASK; + val &= 0xFFU; /* 8 bits per port */ /* ACK IRQ (clear event) */ - writel(val, virtbase + U300_GPIO_PXIEV + port->number * - U300_GPIO_PORTX_SPACING); - /* Print message */ - while (val != 0) { - unsigned gpio; - - pin = __ffs(val); - /* mask off this pin */ - val &= ~(1 << pin); - gpio = (port->number << 3) + pin; - - if (gpio_pin[gpio].callback) - (void)gpio_pin[gpio].callback(gpio_pin[gpio].data); - else - dev_dbg(gpiodev, "stray GPIO IRQ on line %d\n", - gpio); + writel(val, U300_PIN_REG(pinoffset, iev)); + + /* Call IRQ handler */ + if (val != 0) { + int irqoffset; + + for_each_set_bit(irqoffset, &val, U300_GPIO_PINS_PER_PORT) { + int pin_irq = gpio->irq_base + (port->number << 3) + + irqoffset; + int offset = pinoffset + irqoffset; + + dev_dbg(gpio->dev, "GPIO IRQ %d on pin %d\n", + pin_irq, offset); + generic_handle_irq(pin_irq); + /* + * Triggering IRQ on both rising and falling edge + * needs mockery + */ + if (port->toggle_edge_mode & U300_PIN_BIT(offset)) + u300_toggle_trigger(gpio, offset); + } } - return IRQ_HANDLED; + + desc->irq_data.chip->irq_unmask(&desc->irq_data); } -static void gpio_set_initial_values(void) +static void __init u300_gpio_init_pin(struct u300_gpio *gpio, + int offset, + const struct u300_gpio_confdata *conf) { -#ifdef U300_COH901571_3 - int i, j; - unsigned long flags; - u32 val; + /* Set mode: input or output */ + if (conf->output) { + u300_gpio_direction_output(&gpio->chip, offset, conf->outval); - /* Write default values to all pins */ - for (i = 0; i < U300_GPIO_NUM_PORTS; i++) { - val = 0; - for (j = 0; j < 8; j++) - val |= (u32) (u300_gpio_config[i][j].default_output_value != DEFAULT_OUTPUT_LOW) << j; - local_irq_save(flags); - writel(val, virtbase + U300_GPIO_PXPDOR + i * U300_GPIO_PORTX_SPACING); - local_irq_restore(flags); + /* Deactivate bias mode for output */ + u300_gpio_config(&gpio->chip, offset, + GPIO_U300_CONFIG_BIAS_FLOAT, + NULL); + + /* Set drive mode for output */ + u300_gpio_config(&gpio->chip, offset, + GPIO_U300_CONFIG_DRIVE_PUSH_PULL, NULL); + + dev_dbg(gpio->dev, "set up pin %d as output, value: %d\n", + offset, conf->outval); + } else { + u300_gpio_direction_input(&gpio->chip, offset); + + /* Always set output low on input pins */ + u300_gpio_set(&gpio->chip, offset, 0); + + /* Set bias mode for input */ + u300_gpio_config(&gpio->chip, offset, conf->bias_mode, NULL); + + dev_dbg(gpio->dev, "set up pin %d as input, bias: %04x\n", + offset, conf->bias_mode); } +} - /* - * Put all pins that are set to either 'GPIO_OUT' or 'GPIO_NOT_USED' - * to output and 'GPIO_IN' to input for each port. And initialize - * default value on outputs. - */ - for (i = 0; i < U300_GPIO_NUM_PORTS; i++) { - for (j = 0; j < U300_GPIO_PINS_PER_PORT; j++) { - local_irq_save(flags); - val = readl(virtbase + U300_GPIO_PXPCR + - i * U300_GPIO_PORTX_SPACING); - /* Mask out this pin */ - val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK << (j << 1)); - - if (u300_gpio_config[i][j].pin_usage != GPIO_IN) - val |= (U300_GPIO_PXPCR_PIN_MODE_OUTPUT_PUSH_PULL << (j << 1)); - writel(val, virtbase + U300_GPIO_PXPCR + - i * U300_GPIO_PORTX_SPACING); - local_irq_restore(flags); +static void __init u300_gpio_init_coh901571(struct u300_gpio *gpio, + struct u300_gpio_platform *plat) +{ + int i, j; + + /* Write default config and values to all pins */ + for (i = 0; i < plat->ports; i++) { + for (j = 0; j < 8; j++) { + const struct u300_gpio_confdata *conf; + int offset = (i*8) + j; + + if (plat->variant == U300_GPIO_COH901571_3_BS335) + conf = &bs335_gpio_config[i][j]; + else if (plat->variant == U300_GPIO_COH901571_3_BS365) + conf = &bs365_gpio_config[i][j]; + else + break; + + u300_gpio_init_pin(gpio, offset, conf); } } +} - /* Enable or disable the internal pull-ups in the GPIO ASIC block */ - for (i = 0; i < U300_GPIO_MAX; i++) { - val = 0; - for (j = 0; j < 8; j++) - val |= (u32)((u300_gpio_config[i][j].pull_up == DISABLE_PULL_UP) << j); - local_irq_save(flags); - writel(val, virtbase + U300_GPIO_PXPER + i * U300_GPIO_PORTX_SPACING); - local_irq_restore(flags); +static inline void u300_gpio_free_ports(struct u300_gpio *gpio) +{ + struct u300_gpio_port *port; + struct list_head *p, *n; + + list_for_each_safe(p, n, &gpio->port_list) { + port = list_entry(p, struct u300_gpio_port, node); + list_del(&port->node); + free_irq(port->irq, port); + kfree(port); } -#endif } -static int __init gpio_probe(struct platform_device *pdev) +static int __init u300_gpio_probe(struct platform_device *pdev) { - u32 val; + struct u300_gpio_platform *plat = dev_get_platdata(&pdev->dev); + struct u300_gpio *gpio; int err = 0; + int portno; + u32 val; + u32 ifr; int i; - int num_irqs; - gpiodev = &pdev->dev; - memset(gpio_pin, 0, sizeof(gpio_pin)); + gpio = kzalloc(sizeof(struct u300_gpio), GFP_KERNEL); + if (gpio == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + gpio->chip = u300_gpio_chip; + gpio->chip.ngpio = plat->ports * U300_GPIO_PINS_PER_PORT; + gpio->irq_base = plat->gpio_irq_base; + gpio->chip.dev = &pdev->dev; + gpio->chip.base = plat->gpio_base; + gpio->dev = &pdev->dev; /* Get GPIO clock */ - clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) { - err = PTR_ERR(clk); - dev_err(gpiodev, "could not get GPIO clock\n"); + gpio->clk = clk_get(gpio->dev, NULL); + if (IS_ERR(gpio->clk)) { + err = PTR_ERR(gpio->clk); + dev_err(gpio->dev, "could not get GPIO clock\n"); goto err_no_clk; } - err = clk_enable(clk); + err = clk_enable(gpio->clk); if (err) { - dev_err(gpiodev, "could not enable GPIO clock\n"); + dev_err(gpio->dev, "could not enable GPIO clock\n"); goto err_no_clk_enable; } - memres = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!memres) + gpio->memres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!gpio->memres) { + dev_err(gpio->dev, "could not get GPIO memory resource\n"); + err = -ENODEV; goto err_no_resource; + } - if (!request_mem_region(memres->start, resource_size(memres), + if (!request_mem_region(gpio->memres->start, + resource_size(gpio->memres), "GPIO Controller")) { err = -ENODEV; goto err_no_ioregion; } - virtbase = ioremap(memres->start, resource_size(memres)); - if (!virtbase) { + gpio->base = ioremap(gpio->memres->start, resource_size(gpio->memres)); + if (!gpio->base) { err = -ENOMEM; goto err_no_ioremap; } - dev_info(gpiodev, "remapped 0x%08x to %p\n", - memres->start, virtbase); - -#ifdef U300_COH901335 - dev_info(gpiodev, "initializing GPIO Controller COH 901 335\n"); - /* Turn on the GPIO block */ - writel(U300_GPIO_CR_BLOCK_CLOCK_ENABLE, virtbase + U300_GPIO_CR); -#endif - -#ifdef U300_COH901571_3 - dev_info(gpiodev, "initializing GPIO Controller COH 901 571/3\n"); - val = readl(virtbase + U300_GPIO_CR); - dev_info(gpiodev, "COH901571/3 block version: %d, " \ - "number of cores: %d\n", - ((val & 0x0000FE00) >> 9), - ((val & 0x000001FC) >> 2)); - writel(U300_GPIO_CR_BLOCK_CLKRQ_ENABLE, virtbase + U300_GPIO_CR); -#endif - - gpio_set_initial_values(); - - for (num_irqs = 0 ; num_irqs < U300_GPIO_NUM_PORTS; num_irqs++) { - - gpio_ports[num_irqs].irq = - platform_get_irq_byname(pdev, - gpio_ports[num_irqs].name); - - err = request_irq(gpio_ports[num_irqs].irq, - gpio_irq_handler, IRQF_DISABLED, - gpio_ports[num_irqs].name, - &gpio_ports[num_irqs]); - if (err) { - dev_err(gpiodev, "cannot allocate IRQ for %s!\n", - gpio_ports[num_irqs].name); - goto err_no_irq; + + if (plat->variant == U300_GPIO_COH901335) { + dev_info(gpio->dev, + "initializing GPIO Controller COH 901 335\n"); + gpio->stride = U300_335_PORT_STRIDE; + gpio->pcr = U300_335_PXPCR; + gpio->dor = U300_335_PXPDOR; + gpio->dir = U300_335_PXPDIR; + gpio->per = U300_335_PXPER; + gpio->icr = U300_335_PXICR; + gpio->ien = U300_335_PXIEN; + gpio->iev = U300_335_PXIEV; + ifr = U300_335_PXIFR; + + /* Turn on the GPIO block */ + writel(U300_335_CR_BLOCK_CLOCK_ENABLE, + gpio->base + U300_335_CR); + } else if (plat->variant == U300_GPIO_COH901571_3_BS335 || + plat->variant == U300_GPIO_COH901571_3_BS365) { + dev_info(gpio->dev, + "initializing GPIO Controller COH 901 571/3\n"); + gpio->stride = U300_571_PORT_STRIDE; + gpio->pcr = U300_571_PXPCR; + gpio->dor = U300_571_PXPDOR; + gpio->dir = U300_571_PXPDIR; + gpio->per = U300_571_PXPER; + gpio->icr = U300_571_PXICR; + gpio->ien = U300_571_PXIEN; + gpio->iev = U300_571_PXIEV; + ifr = U300_571_PXIFR; + + val = readl(gpio->base + U300_571_CR); + dev_info(gpio->dev, "COH901571/3 block version: %d, " \ + "number of cores: %d totalling %d pins\n", + ((val & 0x000001FC) >> 2), + ((val & 0x0000FE00) >> 9), + ((val & 0x0000FE00) >> 9) * 8); + writel(U300_571_CR_BLOCK_CLKRQ_ENABLE, + gpio->base + U300_571_CR); + u300_gpio_init_coh901571(gpio, plat); + } else { + dev_err(gpio->dev, "unknown block variant\n"); + err = -ENODEV; + goto err_unknown_variant; + } + + /* Add each port with its IRQ separately */ + INIT_LIST_HEAD(&gpio->port_list); + for (portno = 0 ; portno < plat->ports; portno++) { + struct u300_gpio_port *port = + kmalloc(sizeof(struct u300_gpio_port), GFP_KERNEL); + + if (!port) { + dev_err(gpio->dev, "out of memory\n"); + err = -ENOMEM; + goto err_no_port; } - /* Turns off PortX_irq_force */ - writel(0x0, virtbase + U300_GPIO_PXIFR + - num_irqs * U300_GPIO_PORTX_SPACING); + + snprintf(port->name, 8, "gpio%d", portno); + port->number = portno; + port->gpio = gpio; + + port->irq = platform_get_irq_byname(pdev, + port->name); + + dev_dbg(gpio->dev, "register IRQ %d for %s\n", port->irq, + port->name); + + irq_set_chained_handler(port->irq, u300_gpio_irq_handler); + irq_set_handler_data(port->irq, port); + + /* For each GPIO pin set the unique IRQ handler */ + for (i = 0; i < U300_GPIO_PINS_PER_PORT; i++) { + int irqno = gpio->irq_base + (portno << 3) + i; + + dev_dbg(gpio->dev, "handler for IRQ %d on %s\n", + irqno, port->name); + irq_set_chip_and_handler(irqno, &u300_gpio_irqchip, + handle_simple_irq); + set_irq_flags(irqno, IRQF_VALID); + irq_set_chip_data(irqno, port); + } + + /* Turns off irq force (test register) for this port */ + writel(0x0, gpio->base + portno * gpio->stride + ifr); + + list_add_tail(&port->node, &gpio->port_list); } + dev_dbg(gpio->dev, "initialized %d GPIO ports\n", portno); + + err = gpiochip_add(&gpio->chip); + if (err) { + dev_err(gpio->dev, "unable to add gpiochip: %d\n", err); + goto err_no_chip; + } + + platform_set_drvdata(pdev, gpio); return 0; - err_no_irq: - for (i = 0; i < num_irqs; i++) - free_irq(gpio_ports[i].irq, &gpio_ports[i]); - iounmap(virtbase); - err_no_ioremap: - release_mem_region(memres->start, resource_size(memres)); - err_no_ioregion: - err_no_resource: - clk_disable(clk); - err_no_clk_enable: - clk_put(clk); - err_no_clk: - dev_info(gpiodev, "module ERROR:%d\n", err); +err_no_chip: +err_no_port: + u300_gpio_free_ports(gpio); +err_unknown_variant: + iounmap(gpio->base); +err_no_ioremap: + release_mem_region(gpio->memres->start, resource_size(gpio->memres)); +err_no_ioregion: +err_no_resource: + clk_disable(gpio->clk); +err_no_clk_enable: + clk_put(gpio->clk); +err_no_clk: + kfree(gpio); + dev_info(&pdev->dev, "module ERROR:%d\n", err); return err; } -static int __exit gpio_remove(struct platform_device *pdev) +static int __exit u300_gpio_remove(struct platform_device *pdev) { - int i; + struct u300_gpio_platform *plat = dev_get_platdata(&pdev->dev); + struct u300_gpio *gpio = platform_get_drvdata(pdev); + int err; /* Turn off the GPIO block */ - writel(0x00000000U, virtbase + U300_GPIO_CR); - for (i = 0 ; i < U300_GPIO_NUM_PORTS; i++) - free_irq(gpio_ports[i].irq, &gpio_ports[i]); - iounmap(virtbase); - release_mem_region(memres->start, resource_size(memres)); - clk_disable(clk); - clk_put(clk); + if (plat->variant == U300_GPIO_COH901335) + writel(0x00000000U, gpio->base + U300_335_CR); + if (plat->variant == U300_GPIO_COH901571_3_BS335 || + plat->variant == U300_GPIO_COH901571_3_BS365) + writel(0x00000000U, gpio->base + U300_571_CR); + + err = gpiochip_remove(&gpio->chip); + if (err < 0) { + dev_err(gpio->dev, "unable to remove gpiochip: %d\n", err); + return err; + } + u300_gpio_free_ports(gpio); + iounmap(gpio->base); + release_mem_region(gpio->memres->start, + resource_size(gpio->memres)); + clk_disable(gpio->clk); + clk_put(gpio->clk); + platform_set_drvdata(pdev, NULL); + kfree(gpio); return 0; } -static struct platform_driver gpio_driver = { +static struct platform_driver u300_gpio_driver = { .driver = { .name = "u300-gpio", }, - .remove = __exit_p(gpio_remove), + .remove = __exit_p(u300_gpio_remove), }; static int __init u300_gpio_init(void) { - return platform_driver_probe(&gpio_driver, gpio_probe); + return platform_driver_probe(&u300_gpio_driver, u300_gpio_probe); } static void __exit u300_gpio_exit(void) { - platform_driver_unregister(&gpio_driver); + platform_driver_unregister(&u300_gpio_driver); } arch_initcall(u300_gpio_init); module_exit(u300_gpio_exit); MODULE_AUTHOR("Linus Walleij "); - -#ifdef U300_COH901571_3 -MODULE_DESCRIPTION("ST-Ericsson AB COH 901 571/3 GPIO driver"); -#endif - -#ifdef U300_COH901335 -MODULE_DESCRIPTION("ST-Ericsson AB COH 901 335 GPIO driver"); -#endif - +MODULE_DESCRIPTION("ST-Ericsson AB COH 901 335/COH 901 571/3 GPIO driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.2 From 4f5b04800a224aadb6cffcbbc3d3fa26e2367c7f Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 14 Sep 2011 16:22:29 -0700 Subject: drivers/gpio/gpio-generic.c: fix build errors Building a kernel with hotplug disabled results in a link failure: `bgpio_remove' referenced in section `___ksymtab_gpl+bgpio_remove' of drivers/built-in.o: defined in discarded section `.devexit.text' of drivers/built-in.o This is because of bgpio_remove() is exported. It is illegal to export symbols which are discarded either at link time or as part of an init/exit section. Fix this by dropping the __devexit attributation from bgpio_remove(). Also drop the __devinit attributation from bgpio_init(). Signed-off-by: Russell King Cc: Grant Likely Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/gpio/gpio-generic.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c index 231714def4d2..4e24436b0f82 100644 --- a/drivers/gpio/gpio-generic.c +++ b/drivers/gpio/gpio-generic.c @@ -351,7 +351,7 @@ static int bgpio_setup_direction(struct bgpio_chip *bgc, return 0; } -int __devexit bgpio_remove(struct bgpio_chip *bgc) +int bgpio_remove(struct bgpio_chip *bgc) { int err = gpiochip_remove(&bgc->gc); @@ -361,15 +361,10 @@ int __devexit bgpio_remove(struct bgpio_chip *bgc) } EXPORT_SYMBOL_GPL(bgpio_remove); -int __devinit bgpio_init(struct bgpio_chip *bgc, - struct device *dev, - unsigned long sz, - void __iomem *dat, - void __iomem *set, - void __iomem *clr, - void __iomem *dirout, - void __iomem *dirin, - bool big_endian) +int bgpio_init(struct bgpio_chip *bgc, struct device *dev, + unsigned long sz, void __iomem *dat, void __iomem *set, + void __iomem *clr, void __iomem *dirout, void __iomem *dirin, + bool big_endian) { int ret; -- cgit v1.2.2 From 3c0227d262a5835849c68eb8328db016caad6085 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Tue, 20 Sep 2011 10:50:03 +0200 Subject: gpio/nomadik: disable clocks when unused The GPIO clock is required for register access and interrupt detection. When interrupt detection is not required on any of the pin in a block, the block's clock can be disabled when the registers are not being accessed. Signed-off-by: Rabin Vincent Reviewed-by: Rickard Andersson Reviewed-by: Jonas Aberg [Adjust for new IRQ chip core code, use only local functions] Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpio-nomadik.c | 120 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 113 insertions(+), 7 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c index 2c212c732d76..97e9be94141b 100644 --- a/drivers/gpio/gpio-nomadik.c +++ b/drivers/gpio/gpio-nomadik.c @@ -276,6 +276,8 @@ static void nmk_gpio_glitch_slpm_init(unsigned int *slpm) if (!chip) break; + clk_enable(chip->clk); + slpm[i] = readl(chip->addr + NMK_GPIO_SLPC); writel(temp, chip->addr + NMK_GPIO_SLPC); } @@ -292,6 +294,8 @@ static void nmk_gpio_glitch_slpm_restore(unsigned int *slpm) break; writel(slpm[i], chip->addr + NMK_GPIO_SLPC); + + clk_disable(chip->clk); } } @@ -336,10 +340,12 @@ static int __nmk_config_pins(pin_cfg_t *cfgs, int num, bool sleep) break; } + clk_enable(nmk_chip->clk); spin_lock(&nmk_chip->lock); __nmk_config_pin(nmk_chip, pin - nmk_chip->chip.base, cfgs[i], sleep, glitch ? slpm : NULL); spin_unlock(&nmk_chip->lock); + clk_disable(nmk_chip->clk); } if (glitch) @@ -424,6 +430,7 @@ int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode) if (!nmk_chip) return -EINVAL; + clk_enable(nmk_chip->clk); spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); spin_lock(&nmk_chip->lock); @@ -431,6 +438,7 @@ int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode) spin_unlock(&nmk_chip->lock); spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); + clk_disable(nmk_chip->clk); return 0; } @@ -457,9 +465,11 @@ int nmk_gpio_set_pull(int gpio, enum nmk_gpio_pull pull) if (!nmk_chip) return -EINVAL; + clk_enable(nmk_chip->clk); spin_lock_irqsave(&nmk_chip->lock, flags); __nmk_gpio_set_pull(nmk_chip, gpio - nmk_chip->chip.base, pull); spin_unlock_irqrestore(&nmk_chip->lock, flags); + clk_disable(nmk_chip->clk); return 0; } @@ -483,9 +493,11 @@ int nmk_gpio_set_mode(int gpio, int gpio_mode) if (!nmk_chip) return -EINVAL; + clk_enable(nmk_chip->clk); spin_lock_irqsave(&nmk_chip->lock, flags); __nmk_gpio_set_mode(nmk_chip, gpio - nmk_chip->chip.base, gpio_mode); spin_unlock_irqrestore(&nmk_chip->lock, flags); + clk_disable(nmk_chip->clk); return 0; } @@ -502,9 +514,13 @@ int nmk_gpio_get_mode(int gpio) bit = 1 << (gpio - nmk_chip->chip.base); + clk_enable(nmk_chip->clk); + afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & bit; bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & bit; + clk_disable(nmk_chip->clk); + return (afunc ? NMK_GPIO_ALT_A : 0) | (bfunc ? NMK_GPIO_ALT_B : 0); } EXPORT_SYMBOL(nmk_gpio_get_mode); @@ -525,7 +541,10 @@ static void nmk_gpio_irq_ack(struct irq_data *d) nmk_chip = irq_data_get_irq_chip_data(d); if (!nmk_chip) return; + + clk_enable(nmk_chip->clk); writel(nmk_gpio_get_bitmask(gpio), nmk_chip->addr + NMK_GPIO_IC); + clk_disable(nmk_chip->clk); } enum nmk_gpio_irq_type { @@ -591,6 +610,7 @@ static int nmk_gpio_irq_maskunmask(struct irq_data *d, bool enable) else nmk_chip->enabled &= ~bitmask; + clk_enable(nmk_chip->clk); spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); spin_lock(&nmk_chip->lock); @@ -601,6 +621,7 @@ static int nmk_gpio_irq_maskunmask(struct irq_data *d, bool enable) spin_unlock(&nmk_chip->lock); spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); + clk_disable(nmk_chip->clk); return 0; } @@ -628,6 +649,7 @@ static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on) return -EINVAL; bitmask = nmk_gpio_get_bitmask(gpio); + clk_enable(nmk_chip->clk); spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); spin_lock(&nmk_chip->lock); @@ -641,13 +663,15 @@ static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on) spin_unlock(&nmk_chip->lock); spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); + clk_disable(nmk_chip->clk); return 0; } static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type) { - bool enabled, wake = irqd_is_wakeup_set(d); + bool enabled; + bool wake = irqd_is_wakeup_set(d); int gpio; struct nmk_gpio_chip *nmk_chip; unsigned long flags; @@ -664,10 +688,10 @@ static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type) if (type & IRQ_TYPE_LEVEL_LOW) return -EINVAL; - enabled = nmk_chip->enabled & bitmask; - + clk_enable(nmk_chip->clk); spin_lock_irqsave(&nmk_chip->lock, flags); + enabled = !!(nmk_chip->enabled & bitmask); if (enabled) __nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, false); @@ -689,10 +713,28 @@ static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type) __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, true); spin_unlock_irqrestore(&nmk_chip->lock, flags); + clk_disable(nmk_chip->clk); + + return 0; +} + +static unsigned int nmk_gpio_irq_startup(struct irq_data *d) +{ + struct nmk_gpio_chip *nmk_chip = irq_data_get_irq_chip_data(d); + clk_enable(nmk_chip->clk); + nmk_gpio_irq_unmask(d); return 0; } +static void nmk_gpio_irq_shutdown(struct irq_data *d) +{ + struct nmk_gpio_chip *nmk_chip = irq_data_get_irq_chip_data(d); + + nmk_gpio_irq_mask(d); + clk_disable(nmk_chip->clk); +} + static struct irq_chip nmk_gpio_irq_chip = { .name = "Nomadik-GPIO", .irq_ack = nmk_gpio_irq_ack, @@ -700,6 +742,8 @@ static struct irq_chip nmk_gpio_irq_chip = { .irq_unmask = nmk_gpio_irq_unmask, .irq_set_type = nmk_gpio_irq_set_type, .irq_set_wake = nmk_gpio_irq_set_wake, + .irq_startup = nmk_gpio_irq_startup, + .irq_shutdown = nmk_gpio_irq_shutdown, }; static void __nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc, @@ -726,7 +770,11 @@ static void __nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc, static void nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) { struct nmk_gpio_chip *nmk_chip = irq_get_handler_data(irq); - u32 status = readl(nmk_chip->addr + NMK_GPIO_IS); + u32 status; + + clk_enable(nmk_chip->clk); + status = readl(nmk_chip->addr + NMK_GPIO_IS); + clk_disable(nmk_chip->clk); __nmk_gpio_irq_handler(irq, desc, status); } @@ -772,7 +820,12 @@ static int nmk_gpio_make_input(struct gpio_chip *chip, unsigned offset) struct nmk_gpio_chip *nmk_chip = container_of(chip, struct nmk_gpio_chip, chip); + clk_enable(nmk_chip->clk); + writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRC); + + clk_disable(nmk_chip->clk); + return 0; } @@ -781,8 +834,15 @@ static int nmk_gpio_get_input(struct gpio_chip *chip, unsigned offset) struct nmk_gpio_chip *nmk_chip = container_of(chip, struct nmk_gpio_chip, chip); u32 bit = 1 << offset; + int value; + + clk_enable(nmk_chip->clk); + + value = (readl(nmk_chip->addr + NMK_GPIO_DAT) & bit) != 0; - return (readl(nmk_chip->addr + NMK_GPIO_DAT) & bit) != 0; + clk_disable(nmk_chip->clk); + + return value; } static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned offset, @@ -791,7 +851,11 @@ static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned offset, struct nmk_gpio_chip *nmk_chip = container_of(chip, struct nmk_gpio_chip, chip); + clk_enable(nmk_chip->clk); + __nmk_gpio_set_output(nmk_chip, offset, val); + + clk_disable(nmk_chip->clk); } static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset, @@ -800,8 +864,12 @@ static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset, struct nmk_gpio_chip *nmk_chip = container_of(chip, struct nmk_gpio_chip, chip); + clk_enable(nmk_chip->clk); + __nmk_gpio_make_output(nmk_chip, offset, val); + clk_disable(nmk_chip->clk); + return 0; } @@ -832,6 +900,8 @@ static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) [NMK_GPIO_ALT_C] = "altC", }; + clk_enable(nmk_chip->clk); + for (i = 0; i < chip->ngpio; i++, gpio++) { const char *label = gpiochip_is_requested(chip, i); bool pull; @@ -876,6 +946,8 @@ static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) seq_printf(s, "\n"); } + + clk_disable(nmk_chip->clk); } #else @@ -893,6 +965,34 @@ static struct gpio_chip nmk_gpio_template = { .can_sleep = 0, }; +void nmk_gpio_clocks_enable(void) +{ + int i; + + for (i = 0; i < NUM_BANKS; i++) { + struct nmk_gpio_chip *chip = nmk_gpio_chips[i]; + + if (!chip) + continue; + + clk_enable(chip->clk); + } +} + +void nmk_gpio_clocks_disable(void) +{ + int i; + + for (i = 0; i < NUM_BANKS; i++) { + struct nmk_gpio_chip *chip = nmk_gpio_chips[i]; + + if (!chip) + continue; + + clk_disable(chip->clk); + } +} + /* * Called from the suspend/resume path to only keep the real wakeup interrupts * (those that have had set_irq_wake() called on them) as wakeup interrupts, @@ -912,6 +1012,8 @@ void nmk_gpio_wakeups_suspend(void) if (!chip) break; + clk_enable(chip->clk); + chip->rwimsc = readl(chip->addr + NMK_GPIO_RWIMSC); chip->fwimsc = readl(chip->addr + NMK_GPIO_FWIMSC); @@ -926,6 +1028,8 @@ void nmk_gpio_wakeups_suspend(void) /* 0 -> wakeup enable */ writel(~chip->real_wake, chip->addr + NMK_GPIO_SLPC); } + + clk_disable(chip->clk); } } @@ -939,11 +1043,15 @@ void nmk_gpio_wakeups_resume(void) if (!chip) break; + clk_enable(chip->clk); + writel(chip->rwimsc, chip->addr + NMK_GPIO_RWIMSC); writel(chip->fwimsc, chip->addr + NMK_GPIO_FWIMSC); if (chip->sleepmode) writel(chip->slpm, chip->addr + NMK_GPIO_SLPC); + + clk_disable(chip->clk); } } @@ -1010,8 +1118,6 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) goto out_release; } - clk_enable(clk); - nmk_chip = kzalloc(sizeof(*nmk_chip), GFP_KERNEL); if (!nmk_chip) { ret = -ENOMEM; -- cgit v1.2.2 From 479a0c7eee68243ad9915a9ccc217a35c4e5e0e2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 20 Sep 2011 10:50:15 +0200 Subject: gpio/nomadik: use genirq core to track enablement Currently the Nomadik GPIO driver tracks enabled/disabled interrupt status with a local variable, switch to using the interrupt core. Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpio-nomadik.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c index 97e9be94141b..0b7ec1258c8e 100644 --- a/drivers/gpio/gpio-nomadik.c +++ b/drivers/gpio/gpio-nomadik.c @@ -58,7 +58,6 @@ struct nmk_gpio_chip { u32 rwimsc; u32 fwimsc; u32 slpm; - u32 enabled; u32 pull_up; }; @@ -605,11 +604,6 @@ static int nmk_gpio_irq_maskunmask(struct irq_data *d, bool enable) if (!nmk_chip) return -EINVAL; - if (enable) - nmk_chip->enabled |= bitmask; - else - nmk_chip->enabled &= ~bitmask; - clk_enable(nmk_chip->clk); spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); spin_lock(&nmk_chip->lock); @@ -653,7 +647,7 @@ static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on) spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); spin_lock(&nmk_chip->lock); - if (!(nmk_chip->enabled & bitmask)) + if (irqd_irq_disabled(d)) __nmk_gpio_set_wake(nmk_chip, gpio, on); if (on) @@ -670,7 +664,7 @@ static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on) static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type) { - bool enabled; + bool enabled = !irqd_irq_disabled(d); bool wake = irqd_is_wakeup_set(d); int gpio; struct nmk_gpio_chip *nmk_chip; @@ -691,7 +685,6 @@ static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type) clk_enable(nmk_chip->clk); spin_lock_irqsave(&nmk_chip->lock, flags); - enabled = !!(nmk_chip->enabled & bitmask); if (enabled) __nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, false); -- cgit v1.2.2 From 536137bc9ff1738a7bee9b31047a7cd56860180e Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Fri, 26 Aug 2011 11:03:03 +0900 Subject: gpio/s3c24xx: move gpio driver into drivers/gpio/ Cc: Ben Dooks Acked-by: Grant Likely Signed-off-by: Kukjin Kim --- drivers/gpio/Kconfig | 4 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-s3c24xx.c | 283 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 288 insertions(+) create mode 100644 drivers/gpio/gpio-s3c24xx.c (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d539efd96d4b..5654e1b082af 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -135,6 +135,10 @@ config GPIO_PLAT_SAMSUNG def_bool y depends on SAMSUNG_GPIOLIB_4BIT +config GPIO_S3C24XX + def_bool y + depends on PLAT_S3C24XX + config GPIO_S5PC100 def_bool y depends on CPU_S5PC100 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9588948c96f0..c7f1c00986c9 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o +obj-$(CONFIG_GPIO_S3C24XX) += gpio-s3c24xx.o obj-$(CONFIG_GPIO_S5PC100) += gpio-s5pc100.o obj-$(CONFIG_GPIO_S5PV210) += gpio-s5pv210.o diff --git a/drivers/gpio/gpio-s3c24xx.c b/drivers/gpio/gpio-s3c24xx.c new file mode 100644 index 000000000000..ff6103130fe8 --- /dev/null +++ b/drivers/gpio/gpio-s3c24xx.c @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2008-2010 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * S3C24XX GPIOlib support + * + * 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; either version 2 of the License. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +static int s3c24xx_gpiolib_banka_input(struct gpio_chip *chip, unsigned offset) +{ + return -EINVAL; +} + +static int s3c24xx_gpiolib_banka_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long flags; + unsigned long dat; + unsigned long con; + + local_irq_save(flags); + + con = __raw_readl(base + 0x00); + dat = __raw_readl(base + 0x04); + + dat &= ~(1 << offset); + if (value) + dat |= 1 << offset; + + __raw_writel(dat, base + 0x04); + + con &= ~(1 << offset); + + __raw_writel(con, base + 0x00); + __raw_writel(dat, base + 0x04); + + local_irq_restore(flags); + return 0; +} + +static int s3c24xx_gpiolib_bankf_toirq(struct gpio_chip *chip, unsigned offset) +{ + if (offset < 4) + return IRQ_EINT0 + offset; + + if (offset < 8) + return IRQ_EINT4 + offset - 4; + + return -EINVAL; +} + +static struct s3c_gpio_cfg s3c24xx_gpiocfg_banka = { + .set_config = s3c_gpio_setcfg_s3c24xx_a, + .get_config = s3c_gpio_getcfg_s3c24xx_a, +}; + +struct s3c_gpio_cfg s3c24xx_gpiocfg_default = { + .set_config = s3c_gpio_setcfg_s3c24xx, + .get_config = s3c_gpio_getcfg_s3c24xx, +}; + +struct s3c_gpio_chip s3c24xx_gpios[] = { + [0] = { + .base = S3C2410_GPACON, + .pm = __gpio_pm(&s3c_gpio_pm_1bit), + .config = &s3c24xx_gpiocfg_banka, + .chip = { + .base = S3C2410_GPA(0), + .owner = THIS_MODULE, + .label = "GPIOA", + .ngpio = 24, + .direction_input = s3c24xx_gpiolib_banka_input, + .direction_output = s3c24xx_gpiolib_banka_output, + }, + }, + [1] = { + .base = S3C2410_GPBCON, + .pm = __gpio_pm(&s3c_gpio_pm_2bit), + .chip = { + .base = S3C2410_GPB(0), + .owner = THIS_MODULE, + .label = "GPIOB", + .ngpio = 16, + }, + }, + [2] = { + .base = S3C2410_GPCCON, + .pm = __gpio_pm(&s3c_gpio_pm_2bit), + .chip = { + .base = S3C2410_GPC(0), + .owner = THIS_MODULE, + .label = "GPIOC", + .ngpio = 16, + }, + }, + [3] = { + .base = S3C2410_GPDCON, + .pm = __gpio_pm(&s3c_gpio_pm_2bit), + .chip = { + .base = S3C2410_GPD(0), + .owner = THIS_MODULE, + .label = "GPIOD", + .ngpio = 16, + }, + }, + [4] = { + .base = S3C2410_GPECON, + .pm = __gpio_pm(&s3c_gpio_pm_2bit), + .chip = { + .base = S3C2410_GPE(0), + .label = "GPIOE", + .owner = THIS_MODULE, + .ngpio = 16, + }, + }, + [5] = { + .base = S3C2410_GPFCON, + .pm = __gpio_pm(&s3c_gpio_pm_2bit), + .chip = { + .base = S3C2410_GPF(0), + .owner = THIS_MODULE, + .label = "GPIOF", + .ngpio = 8, + .to_irq = s3c24xx_gpiolib_bankf_toirq, + }, + }, + [6] = { + .base = S3C2410_GPGCON, + .pm = __gpio_pm(&s3c_gpio_pm_2bit), + .irq_base = IRQ_EINT8, + .chip = { + .base = S3C2410_GPG(0), + .owner = THIS_MODULE, + .label = "GPIOG", + .ngpio = 16, + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = S3C2410_GPHCON, + .pm = __gpio_pm(&s3c_gpio_pm_2bit), + .chip = { + .base = S3C2410_GPH(0), + .owner = THIS_MODULE, + .label = "GPIOH", + .ngpio = 11, + }, + }, + /* GPIOS for the S3C2443 and later devices. */ + { + .base = S3C2440_GPJCON, + .pm = __gpio_pm(&s3c_gpio_pm_2bit), + .chip = { + .base = S3C2410_GPJ(0), + .owner = THIS_MODULE, + .label = "GPIOJ", + .ngpio = 16, + }, + }, { + .base = S3C2443_GPKCON, + .pm = __gpio_pm(&s3c_gpio_pm_2bit), + .chip = { + .base = S3C2410_GPK(0), + .owner = THIS_MODULE, + .label = "GPIOK", + .ngpio = 16, + }, + }, { + .base = S3C2443_GPLCON, + .pm = __gpio_pm(&s3c_gpio_pm_2bit), + .chip = { + .base = S3C2410_GPL(0), + .owner = THIS_MODULE, + .label = "GPIOL", + .ngpio = 15, + }, + }, { + .base = S3C2443_GPMCON, + .pm = __gpio_pm(&s3c_gpio_pm_2bit), + .chip = { + .base = S3C2410_GPM(0), + .owner = THIS_MODULE, + .label = "GPIOM", + .ngpio = 2, + }, + }, +}; + +static __init int s3c24xx_gpiolib_init(void) +{ + struct s3c_gpio_chip *chip = s3c24xx_gpios; + int gpn; + + for (gpn = 0; gpn < ARRAY_SIZE(s3c24xx_gpios); gpn++, chip++) { + if (!chip->config) + chip->config = &s3c24xx_gpiocfg_default; + + s3c_gpiolib_add(chip); + } + + return 0; +} +core_initcall(s3c24xx_gpiolib_init); + +/* gpiolib wrappers until these are totally eliminated */ + +void s3c2410_gpio_pullup(unsigned int pin, unsigned int to) +{ + int ret; + + WARN_ON(to); /* should be none of these left */ + + if (!to) { + /* if pull is enabled, try first with up, and if that + * fails, try using down */ + + ret = s3c_gpio_setpull(pin, S3C_GPIO_PULL_UP); + if (ret) + s3c_gpio_setpull(pin, S3C_GPIO_PULL_DOWN); + } else { + s3c_gpio_setpull(pin, S3C_GPIO_PULL_NONE); + } +} +EXPORT_SYMBOL(s3c2410_gpio_pullup); + +void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) +{ + /* do this via gpiolib until all users removed */ + + gpio_request(pin, "temporary"); + gpio_set_value(pin, to); + gpio_free(pin); +} +EXPORT_SYMBOL(s3c2410_gpio_setpin); + +unsigned int s3c2410_gpio_getpin(unsigned int pin) +{ + struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); + unsigned long offs = pin - chip->chip.base; + + return __raw_readl(chip->base + 0x04) & (1<< offs); +} +EXPORT_SYMBOL(s3c2410_gpio_getpin); + +unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change) +{ + unsigned long flags; + unsigned long misccr; + + local_irq_save(flags); + misccr = __raw_readl(S3C24XX_MISCCR); + misccr &= ~clear; + misccr ^= change; + __raw_writel(misccr, S3C24XX_MISCCR); + local_irq_restore(flags); + + return misccr; +} +EXPORT_SYMBOL(s3c2410_modify_misccr); -- cgit v1.2.2 From ec080059c1ce7bfdc8453bf4f3328ac0315a66a1 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Fri, 26 Aug 2011 11:06:25 +0900 Subject: gpio/s3c64xx: move gpio driver into drivers/gpio/ Cc: Ben Dooks Acked-by: Grant Likely Signed-off-by: Kukjin Kim --- drivers/gpio/Kconfig | 4 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-s3c64xx.c | 289 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 294 insertions(+) create mode 100644 drivers/gpio/gpio-s3c64xx.c (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5654e1b082af..6368730a2e58 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -139,6 +139,10 @@ config GPIO_S3C24XX def_bool y depends on PLAT_S3C24XX +config GPIO_S3C64XX + def_bool y + depends on ARCH_S3C64XX + config GPIO_S5PC100 def_bool y depends on CPU_S5PC100 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index c7f1c00986c9..8c1fb23285cb 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o obj-$(CONFIG_GPIO_S3C24XX) += gpio-s3c24xx.o +obj-$(CONFIG_GPIO_S3C64XX) += gpio-s3c64xx.o obj-$(CONFIG_GPIO_S5PC100) += gpio-s5pc100.o obj-$(CONFIG_GPIO_S5PV210) += gpio-s5pv210.o diff --git a/drivers/gpio/gpio-s3c64xx.c b/drivers/gpio/gpio-s3c64xx.c new file mode 100644 index 000000000000..b4f1c8204f03 --- /dev/null +++ b/drivers/gpio/gpio-s3c64xx.c @@ -0,0 +1,289 @@ +/* + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C64XX - GPIOlib support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +/* GPIO bank summary: + * + * Bank GPIOs Style SlpCon ExtInt Group + * A 8 4Bit Yes 1 + * B 7 4Bit Yes 1 + * C 8 4Bit Yes 2 + * D 5 4Bit Yes 3 + * E 5 4Bit Yes None + * F 16 2Bit Yes 4 [1] + * G 7 4Bit Yes 5 + * H 10 4Bit[2] Yes 6 + * I 16 2Bit Yes None + * J 12 2Bit Yes None + * K 16 4Bit[2] No None + * L 15 4Bit[2] No None + * M 6 4Bit No IRQ_EINT + * N 16 2Bit No IRQ_EINT + * O 16 2Bit Yes 7 + * P 15 2Bit Yes 8 + * Q 9 2Bit Yes 9 + * + * [1] BANKF pins 14,15 do not form part of the external interrupt sources + * [2] BANK has two control registers, GPxCON0 and GPxCON1 + */ + +static struct s3c_gpio_cfg gpio_4bit_cfg_noint = { + .set_config = s3c_gpio_setcfg_s3c64xx_4bit, + .get_config = s3c_gpio_getcfg_s3c64xx_4bit, + .set_pull = s3c_gpio_setpull_updown, + .get_pull = s3c_gpio_getpull_updown, +}; + +static struct s3c_gpio_cfg gpio_4bit_cfg_eint0111 = { + .cfg_eint = 7, + .set_config = s3c_gpio_setcfg_s3c64xx_4bit, + .get_config = s3c_gpio_getcfg_s3c64xx_4bit, + .set_pull = s3c_gpio_setpull_updown, + .get_pull = s3c_gpio_getpull_updown, +}; + +static struct s3c_gpio_cfg gpio_4bit_cfg_eint0011 = { + .cfg_eint = 3, + .get_config = s3c_gpio_getcfg_s3c64xx_4bit, + .set_config = s3c_gpio_setcfg_s3c64xx_4bit, + .set_pull = s3c_gpio_setpull_updown, + .get_pull = s3c_gpio_getpull_updown, +}; + +static int s3c64xx_gpio2int_gpm(struct gpio_chip *chip, unsigned pin) +{ + return pin < 5 ? IRQ_EINT(23) + pin : -ENXIO; +} + +static struct s3c_gpio_chip gpio_4bit[] = { + { + .base = S3C64XX_GPA_BASE, + .config = &gpio_4bit_cfg_eint0111, + .chip = { + .base = S3C64XX_GPA(0), + .ngpio = S3C64XX_GPIO_A_NR, + .label = "GPA", + }, + }, { + .base = S3C64XX_GPB_BASE, + .config = &gpio_4bit_cfg_eint0111, + .chip = { + .base = S3C64XX_GPB(0), + .ngpio = S3C64XX_GPIO_B_NR, + .label = "GPB", + }, + }, { + .base = S3C64XX_GPC_BASE, + .config = &gpio_4bit_cfg_eint0111, + .chip = { + .base = S3C64XX_GPC(0), + .ngpio = S3C64XX_GPIO_C_NR, + .label = "GPC", + }, + }, { + .base = S3C64XX_GPD_BASE, + .config = &gpio_4bit_cfg_eint0111, + .chip = { + .base = S3C64XX_GPD(0), + .ngpio = S3C64XX_GPIO_D_NR, + .label = "GPD", + }, + }, { + .base = S3C64XX_GPE_BASE, + .config = &gpio_4bit_cfg_noint, + .chip = { + .base = S3C64XX_GPE(0), + .ngpio = S3C64XX_GPIO_E_NR, + .label = "GPE", + }, + }, { + .base = S3C64XX_GPG_BASE, + .config = &gpio_4bit_cfg_eint0111, + .chip = { + .base = S3C64XX_GPG(0), + .ngpio = S3C64XX_GPIO_G_NR, + .label = "GPG", + }, + }, { + .base = S3C64XX_GPM_BASE, + .config = &gpio_4bit_cfg_eint0011, + .chip = { + .base = S3C64XX_GPM(0), + .ngpio = S3C64XX_GPIO_M_NR, + .label = "GPM", + .to_irq = s3c64xx_gpio2int_gpm, + }, + }, +}; + +static int s3c64xx_gpio2int_gpl(struct gpio_chip *chip, unsigned pin) +{ + return pin >= 8 ? IRQ_EINT(16) + pin - 8 : -ENXIO; +} + +static struct s3c_gpio_chip gpio_4bit2[] = { + { + .base = S3C64XX_GPH_BASE + 0x4, + .config = &gpio_4bit_cfg_eint0111, + .chip = { + .base = S3C64XX_GPH(0), + .ngpio = S3C64XX_GPIO_H_NR, + .label = "GPH", + }, + }, { + .base = S3C64XX_GPK_BASE + 0x4, + .config = &gpio_4bit_cfg_noint, + .chip = { + .base = S3C64XX_GPK(0), + .ngpio = S3C64XX_GPIO_K_NR, + .label = "GPK", + }, + }, { + .base = S3C64XX_GPL_BASE + 0x4, + .config = &gpio_4bit_cfg_eint0011, + .chip = { + .base = S3C64XX_GPL(0), + .ngpio = S3C64XX_GPIO_L_NR, + .label = "GPL", + .to_irq = s3c64xx_gpio2int_gpl, + }, + }, +}; + +static struct s3c_gpio_cfg gpio_2bit_cfg_noint = { + .set_config = s3c_gpio_setcfg_s3c24xx, + .get_config = s3c_gpio_getcfg_s3c24xx, + .set_pull = s3c_gpio_setpull_updown, + .get_pull = s3c_gpio_getpull_updown, +}; + +static struct s3c_gpio_cfg gpio_2bit_cfg_eint10 = { + .cfg_eint = 2, + .set_config = s3c_gpio_setcfg_s3c24xx, + .get_config = s3c_gpio_getcfg_s3c24xx, + .set_pull = s3c_gpio_setpull_updown, + .get_pull = s3c_gpio_getpull_updown, +}; + +static struct s3c_gpio_cfg gpio_2bit_cfg_eint11 = { + .cfg_eint = 3, + .set_config = s3c_gpio_setcfg_s3c24xx, + .get_config = s3c_gpio_getcfg_s3c24xx, + .set_pull = s3c_gpio_setpull_updown, + .get_pull = s3c_gpio_getpull_updown, +}; + +static struct s3c_gpio_chip gpio_2bit[] = { + { + .base = S3C64XX_GPF_BASE, + .config = &gpio_2bit_cfg_eint11, + .chip = { + .base = S3C64XX_GPF(0), + .ngpio = S3C64XX_GPIO_F_NR, + .label = "GPF", + }, + }, { + .base = S3C64XX_GPI_BASE, + .config = &gpio_2bit_cfg_noint, + .chip = { + .base = S3C64XX_GPI(0), + .ngpio = S3C64XX_GPIO_I_NR, + .label = "GPI", + }, + }, { + .base = S3C64XX_GPJ_BASE, + .config = &gpio_2bit_cfg_noint, + .chip = { + .base = S3C64XX_GPJ(0), + .ngpio = S3C64XX_GPIO_J_NR, + .label = "GPJ", + }, + }, { + .base = S3C64XX_GPN_BASE, + .irq_base = IRQ_EINT(0), + .config = &gpio_2bit_cfg_eint10, + .chip = { + .base = S3C64XX_GPN(0), + .ngpio = S3C64XX_GPIO_N_NR, + .label = "GPN", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = S3C64XX_GPO_BASE, + .config = &gpio_2bit_cfg_eint11, + .chip = { + .base = S3C64XX_GPO(0), + .ngpio = S3C64XX_GPIO_O_NR, + .label = "GPO", + }, + }, { + .base = S3C64XX_GPP_BASE, + .config = &gpio_2bit_cfg_eint11, + .chip = { + .base = S3C64XX_GPP(0), + .ngpio = S3C64XX_GPIO_P_NR, + .label = "GPP", + }, + }, { + .base = S3C64XX_GPQ_BASE, + .config = &gpio_2bit_cfg_eint11, + .chip = { + .base = S3C64XX_GPQ(0), + .ngpio = S3C64XX_GPIO_Q_NR, + .label = "GPQ", + }, + }, +}; + +static __init void s3c64xx_gpiolib_add_2bit(struct s3c_gpio_chip *chip) +{ + chip->pm = __gpio_pm(&s3c_gpio_pm_2bit); +} + +static __init void s3c64xx_gpiolib_add(struct s3c_gpio_chip *chips, + int nr_chips, + void (*fn)(struct s3c_gpio_chip *)) +{ + for (; nr_chips > 0; nr_chips--, chips++) { + if (fn) + (fn)(chips); + s3c_gpiolib_add(chips); + } +} + +static __init int s3c64xx_gpiolib_init(void) +{ + s3c64xx_gpiolib_add(gpio_4bit, ARRAY_SIZE(gpio_4bit), + samsung_gpiolib_add_4bit); + + s3c64xx_gpiolib_add(gpio_4bit2, ARRAY_SIZE(gpio_4bit2), + samsung_gpiolib_add_4bit2); + + s3c64xx_gpiolib_add(gpio_2bit, ARRAY_SIZE(gpio_2bit), + s3c64xx_gpiolib_add_2bit); + + return 0; +} + +core_initcall(s3c64xx_gpiolib_init); -- cgit v1.2.2 From c4b3fd38dfb677d7a3997527c9cbdc21b81424a3 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Fri, 26 Aug 2011 11:07:26 +0900 Subject: gpio/s5p64x0: move gpio driver into drivers/gpio/ Acked-by: Grant Likely Signed-off-by: Kukjin Kim --- drivers/gpio/Kconfig | 4 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-s5p64x0.c | 510 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 515 insertions(+) create mode 100644 drivers/gpio/gpio-s5p64x0.c (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 6368730a2e58..5eb29bc18a56 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -143,6 +143,10 @@ config GPIO_S3C64XX def_bool y depends on ARCH_S3C64XX +config GPIO_S5P64X0 + def_bool y + depends on ARCH_S5P64X0 + config GPIO_S5PC100 def_bool y depends on CPU_S5PC100 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 8c1fb23285cb..10f3bbf0eceb 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o obj-$(CONFIG_GPIO_S3C24XX) += gpio-s3c24xx.o obj-$(CONFIG_GPIO_S3C64XX) += gpio-s3c64xx.o +obj-$(CONFIG_GPIO_S5P64X0) += gpio-s5p64x0.o obj-$(CONFIG_GPIO_S5PC100) += gpio-s5pc100.o obj-$(CONFIG_GPIO_S5PV210) += gpio-s5pv210.o diff --git a/drivers/gpio/gpio-s5p64x0.c b/drivers/gpio/gpio-s5p64x0.c new file mode 100644 index 000000000000..96e816f5cc95 --- /dev/null +++ b/drivers/gpio/gpio-s5p64x0.c @@ -0,0 +1,510 @@ +/* + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * S5P64X0 - GPIOlib support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +/* + * S5P6440 GPIO bank summary: + * + * Bank GPIOs Style SlpCon ExtInt Group + * A 6 4Bit Yes 1 + * B 7 4Bit Yes 1 + * C 8 4Bit Yes 2 + * F 2 2Bit Yes 4 [1] + * G 7 4Bit Yes 5 + * H 10 4Bit[2] Yes 6 + * I 16 2Bit Yes None + * J 12 2Bit Yes None + * N 16 2Bit No IRQ_EINT + * P 8 2Bit Yes 8 + * R 15 4Bit[2] Yes 8 + * + * S5P6450 GPIO bank summary: + * + * Bank GPIOs Style SlpCon ExtInt Group + * A 6 4Bit Yes 1 + * B 7 4Bit Yes 1 + * C 8 4Bit Yes 2 + * D 8 4Bit Yes None + * F 2 2Bit Yes None + * G 14 4Bit[2] Yes 5 + * H 10 4Bit[2] Yes 6 + * I 16 2Bit Yes None + * J 12 2Bit Yes None + * K 5 4Bit Yes None + * N 16 2Bit No IRQ_EINT + * P 11 2Bit Yes 8 + * Q 14 2Bit Yes None + * R 15 4Bit[2] Yes None + * S 8 2Bit Yes None + * + * [1] BANKF pins 14,15 do not form part of the external interrupt sources + * [2] BANK has two control registers, GPxCON0 and GPxCON1 + */ + +static int s5p64x0_gpiolib_rbank_4bit2_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + unsigned long flags; + + switch (offset) { + case 6: + offset += 1; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + regcon -= 4; + break; + default: + offset -= 7; + break; + } + + s3c_gpio_lock(ourchip, flags); + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(offset)); + __raw_writel(con, regcon); + + s3c_gpio_unlock(ourchip, flags); + + return 0; +} + +static int s5p64x0_gpiolib_rbank_4bit2_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + unsigned long dat; + unsigned long flags; + unsigned con_offset = offset; + + switch (con_offset) { + case 6: + con_offset += 1; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + regcon -= 4; + break; + default: + con_offset -= 7; + break; + } + + s3c_gpio_lock(ourchip, flags); + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(con_offset)); + con |= 0x1 << con_4bit_shift(con_offset); + + dat = __raw_readl(base + GPIODAT_OFF); + if (value) + dat |= 1 << offset; + else + dat &= ~(1 << offset); + + __raw_writel(con, regcon); + __raw_writel(dat, base + GPIODAT_OFF); + + s3c_gpio_unlock(ourchip, flags); + + return 0; +} + +int s5p64x0_gpio_setcfg_4bit_rbank(struct s3c_gpio_chip *chip, + unsigned int off, unsigned int cfg) +{ + void __iomem *reg = chip->base; + unsigned int shift; + u32 con; + + switch (off) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + shift = (off & 7) * 4; + reg -= 4; + break; + case 6: + shift = ((off + 1) & 7) * 4; + reg -= 4; + default: + shift = ((off + 1) & 7) * 4; + break; + } + + if (s3c_gpio_is_cfg_special(cfg)) { + cfg &= 0xf; + cfg <<= shift; + } + + con = __raw_readl(reg); + con &= ~(0xf << shift); + con |= cfg; + __raw_writel(con, reg); + + return 0; +} + +static struct s3c_gpio_cfg s5p64x0_gpio_cfgs[] = { + { + .cfg_eint = 0, + }, { + .cfg_eint = 7, + }, { + .cfg_eint = 3, + .set_config = s5p64x0_gpio_setcfg_4bit_rbank, + }, { + .cfg_eint = 0, + .set_config = s3c_gpio_setcfg_s3c24xx, + .get_config = s3c_gpio_getcfg_s3c24xx, + }, { + .cfg_eint = 2, + .set_config = s3c_gpio_setcfg_s3c24xx, + .get_config = s3c_gpio_getcfg_s3c24xx, + }, { + .cfg_eint = 3, + .set_config = s3c_gpio_setcfg_s3c24xx, + .get_config = s3c_gpio_getcfg_s3c24xx, + }, +}; + +static struct s3c_gpio_chip s5p6440_gpio_4bit[] = { + { + .base = S5P64X0_GPA_BASE, + .config = &s5p64x0_gpio_cfgs[1], + .chip = { + .base = S5P6440_GPA(0), + .ngpio = S5P6440_GPIO_A_NR, + .label = "GPA", + }, + }, { + .base = S5P64X0_GPB_BASE, + .config = &s5p64x0_gpio_cfgs[1], + .chip = { + .base = S5P6440_GPB(0), + .ngpio = S5P6440_GPIO_B_NR, + .label = "GPB", + }, + }, { + .base = S5P64X0_GPC_BASE, + .config = &s5p64x0_gpio_cfgs[1], + .chip = { + .base = S5P6440_GPC(0), + .ngpio = S5P6440_GPIO_C_NR, + .label = "GPC", + }, + }, { + .base = S5P64X0_GPG_BASE, + .config = &s5p64x0_gpio_cfgs[1], + .chip = { + .base = S5P6440_GPG(0), + .ngpio = S5P6440_GPIO_G_NR, + .label = "GPG", + }, + }, +}; + +static struct s3c_gpio_chip s5p6440_gpio_4bit2[] = { + { + .base = S5P64X0_GPH_BASE + 0x4, + .config = &s5p64x0_gpio_cfgs[1], + .chip = { + .base = S5P6440_GPH(0), + .ngpio = S5P6440_GPIO_H_NR, + .label = "GPH", + }, + }, +}; + +static struct s3c_gpio_chip s5p6440_gpio_rbank_4bit2[] = { + { + .base = S5P64X0_GPR_BASE + 0x4, + .config = &s5p64x0_gpio_cfgs[2], + .chip = { + .base = S5P6440_GPR(0), + .ngpio = S5P6440_GPIO_R_NR, + .label = "GPR", + }, + }, +}; + +static struct s3c_gpio_chip s5p6440_gpio_2bit[] = { + { + .base = S5P64X0_GPF_BASE, + .config = &s5p64x0_gpio_cfgs[5], + .chip = { + .base = S5P6440_GPF(0), + .ngpio = S5P6440_GPIO_F_NR, + .label = "GPF", + }, + }, { + .base = S5P64X0_GPI_BASE, + .config = &s5p64x0_gpio_cfgs[3], + .chip = { + .base = S5P6440_GPI(0), + .ngpio = S5P6440_GPIO_I_NR, + .label = "GPI", + }, + }, { + .base = S5P64X0_GPJ_BASE, + .config = &s5p64x0_gpio_cfgs[3], + .chip = { + .base = S5P6440_GPJ(0), + .ngpio = S5P6440_GPIO_J_NR, + .label = "GPJ", + }, + }, { + .base = S5P64X0_GPN_BASE, + .config = &s5p64x0_gpio_cfgs[4], + .chip = { + .base = S5P6440_GPN(0), + .ngpio = S5P6440_GPIO_N_NR, + .label = "GPN", + }, + }, { + .base = S5P64X0_GPP_BASE, + .config = &s5p64x0_gpio_cfgs[5], + .chip = { + .base = S5P6440_GPP(0), + .ngpio = S5P6440_GPIO_P_NR, + .label = "GPP", + }, + }, +}; + +static struct s3c_gpio_chip s5p6450_gpio_4bit[] = { + { + .base = S5P64X0_GPA_BASE, + .config = &s5p64x0_gpio_cfgs[1], + .chip = { + .base = S5P6450_GPA(0), + .ngpio = S5P6450_GPIO_A_NR, + .label = "GPA", + }, + }, { + .base = S5P64X0_GPB_BASE, + .config = &s5p64x0_gpio_cfgs[1], + .chip = { + .base = S5P6450_GPB(0), + .ngpio = S5P6450_GPIO_B_NR, + .label = "GPB", + }, + }, { + .base = S5P64X0_GPC_BASE, + .config = &s5p64x0_gpio_cfgs[1], + .chip = { + .base = S5P6450_GPC(0), + .ngpio = S5P6450_GPIO_C_NR, + .label = "GPC", + }, + }, { + .base = S5P6450_GPD_BASE, + .config = &s5p64x0_gpio_cfgs[1], + .chip = { + .base = S5P6450_GPD(0), + .ngpio = S5P6450_GPIO_D_NR, + .label = "GPD", + }, + }, { + .base = S5P6450_GPK_BASE, + .config = &s5p64x0_gpio_cfgs[1], + .chip = { + .base = S5P6450_GPK(0), + .ngpio = S5P6450_GPIO_K_NR, + .label = "GPK", + }, + }, +}; + +static struct s3c_gpio_chip s5p6450_gpio_4bit2[] = { + { + .base = S5P64X0_GPG_BASE + 0x4, + .config = &s5p64x0_gpio_cfgs[1], + .chip = { + .base = S5P6450_GPG(0), + .ngpio = S5P6450_GPIO_G_NR, + .label = "GPG", + }, + }, { + .base = S5P64X0_GPH_BASE + 0x4, + .config = &s5p64x0_gpio_cfgs[1], + .chip = { + .base = S5P6450_GPH(0), + .ngpio = S5P6450_GPIO_H_NR, + .label = "GPH", + }, + }, +}; + +static struct s3c_gpio_chip s5p6450_gpio_rbank_4bit2[] = { + { + .base = S5P64X0_GPR_BASE + 0x4, + .config = &s5p64x0_gpio_cfgs[2], + .chip = { + .base = S5P6450_GPR(0), + .ngpio = S5P6450_GPIO_R_NR, + .label = "GPR", + }, + }, +}; + +static struct s3c_gpio_chip s5p6450_gpio_2bit[] = { + { + .base = S5P64X0_GPF_BASE, + .config = &s5p64x0_gpio_cfgs[5], + .chip = { + .base = S5P6450_GPF(0), + .ngpio = S5P6450_GPIO_F_NR, + .label = "GPF", + }, + }, { + .base = S5P64X0_GPI_BASE, + .config = &s5p64x0_gpio_cfgs[3], + .chip = { + .base = S5P6450_GPI(0), + .ngpio = S5P6450_GPIO_I_NR, + .label = "GPI", + }, + }, { + .base = S5P64X0_GPJ_BASE, + .config = &s5p64x0_gpio_cfgs[3], + .chip = { + .base = S5P6450_GPJ(0), + .ngpio = S5P6450_GPIO_J_NR, + .label = "GPJ", + }, + }, { + .base = S5P64X0_GPN_BASE, + .config = &s5p64x0_gpio_cfgs[4], + .chip = { + .base = S5P6450_GPN(0), + .ngpio = S5P6450_GPIO_N_NR, + .label = "GPN", + }, + }, { + .base = S5P64X0_GPP_BASE, + .config = &s5p64x0_gpio_cfgs[5], + .chip = { + .base = S5P6450_GPP(0), + .ngpio = S5P6450_GPIO_P_NR, + .label = "GPP", + }, + }, { + .base = S5P6450_GPQ_BASE, + .config = &s5p64x0_gpio_cfgs[4], + .chip = { + .base = S5P6450_GPQ(0), + .ngpio = S5P6450_GPIO_Q_NR, + .label = "GPQ", + }, + }, { + .base = S5P6450_GPS_BASE, + .config = &s5p64x0_gpio_cfgs[5], + .chip = { + .base = S5P6450_GPS(0), + .ngpio = S5P6450_GPIO_S_NR, + .label = "GPS", + }, + }, +}; + +void __init s5p64x0_gpiolib_set_cfg(struct s3c_gpio_cfg *chipcfg, int nr_chips) +{ + for (; nr_chips > 0; nr_chips--, chipcfg++) { + if (!chipcfg->set_config) + chipcfg->set_config = s3c_gpio_setcfg_s3c64xx_4bit; + if (!chipcfg->get_config) + chipcfg->get_config = s3c_gpio_getcfg_s3c64xx_4bit; + if (!chipcfg->set_pull) + chipcfg->set_pull = s3c_gpio_setpull_updown; + if (!chipcfg->get_pull) + chipcfg->get_pull = s3c_gpio_getpull_updown; + } +} + +static void __init s5p64x0_gpio_add_rbank_4bit2(struct s3c_gpio_chip *chip, + int nr_chips) +{ + for (; nr_chips > 0; nr_chips--, chip++) { + chip->chip.direction_input = s5p64x0_gpiolib_rbank_4bit2_input; + chip->chip.direction_output = + s5p64x0_gpiolib_rbank_4bit2_output; + s3c_gpiolib_add(chip); + } +} + +static int __init s5p64x0_gpiolib_init(void) +{ + unsigned int chipid; + + chipid = __raw_readl(S5P64X0_SYS_ID); + + s5p64x0_gpiolib_set_cfg(s5p64x0_gpio_cfgs, + ARRAY_SIZE(s5p64x0_gpio_cfgs)); + + if ((chipid & 0xff000) == 0x50000) { + samsung_gpiolib_add_2bit_chips(s5p6450_gpio_2bit, + ARRAY_SIZE(s5p6450_gpio_2bit)); + + samsung_gpiolib_add_4bit_chips(s5p6450_gpio_4bit, + ARRAY_SIZE(s5p6450_gpio_4bit)); + + samsung_gpiolib_add_4bit2_chips(s5p6450_gpio_4bit2, + ARRAY_SIZE(s5p6450_gpio_4bit2)); + + s5p64x0_gpio_add_rbank_4bit2(s5p6450_gpio_rbank_4bit2, + ARRAY_SIZE(s5p6450_gpio_rbank_4bit2)); + } else { + samsung_gpiolib_add_2bit_chips(s5p6440_gpio_2bit, + ARRAY_SIZE(s5p6440_gpio_2bit)); + + samsung_gpiolib_add_4bit_chips(s5p6440_gpio_4bit, + ARRAY_SIZE(s5p6440_gpio_4bit)); + + samsung_gpiolib_add_4bit2_chips(s5p6440_gpio_4bit2, + ARRAY_SIZE(s5p6440_gpio_4bit2)); + + s5p64x0_gpio_add_rbank_4bit2(s5p6440_gpio_rbank_4bit2, + ARRAY_SIZE(s5p6440_gpio_rbank_4bit2)); + } + + return 0; +} +core_initcall(s5p64x0_gpiolib_init); -- cgit v1.2.2 From 1b39d5f2cc5c28085bbf48db80bf704ab4dedda9 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Tue, 30 Aug 2011 20:39:08 +0900 Subject: gpio/samsung: gpio-samsung.c to support Samsung GPIOs This patch adds support for Samsung GPIOs with one gpio driver and removes old GPIO drivers which are drivers/gpio-s3c24xx.c, gpio-s3c64xx.c, gpio-s5p64x0.c, gpio-s5pc100.c, gpio-s5pv210.c, gpio-exynos4.c, gpio-plat-samsung.c, plat-samsung/gpio-config.c and gpio.c to support each Samsung SoCs before. Because the gpio-samsung.c can replace old Samsung GPIO drivers. Basically, the gpio-samsung.c has been made by their merging and removing duplicated definitions. Note: gpio-samsung.c includes some SoC dependent codes and it will be replaced next time. Cc: Ben Dooks Acked-by: Grant Likely [kgene.kim@samsung.com: squash the removing and adding patches] [kgene.kim@samsung.com: fixes bug during to register of gpio_chips] Signed-off-by: Kukjin Kim --- drivers/gpio/Kconfig | 28 - drivers/gpio/Makefile | 10 +- drivers/gpio/gpio-exynos4.c | 385 ------ drivers/gpio/gpio-plat-samsung.c | 205 --- drivers/gpio/gpio-s3c24xx.c | 283 ---- drivers/gpio/gpio-s3c64xx.c | 289 ---- drivers/gpio/gpio-s5p64x0.c | 510 -------- drivers/gpio/gpio-s5pc100.c | 354 ----- drivers/gpio/gpio-s5pv210.c | 287 ---- drivers/gpio/gpio-samsung.c | 2686 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 2687 insertions(+), 2350 deletions(-) delete mode 100644 drivers/gpio/gpio-exynos4.c delete mode 100644 drivers/gpio/gpio-plat-samsung.c delete mode 100644 drivers/gpio/gpio-s3c24xx.c delete mode 100644 drivers/gpio/gpio-s3c64xx.c delete mode 100644 drivers/gpio/gpio-s5p64x0.c delete mode 100644 drivers/gpio/gpio-s5pc100.c delete mode 100644 drivers/gpio/gpio-s5pv210.c create mode 100644 drivers/gpio/gpio-samsung.c (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5eb29bc18a56..ca44d2cceb02 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -95,10 +95,6 @@ config GPIO_EP93XX depends on ARCH_EP93XX select GPIO_GENERIC -config GPIO_EXYNOS4 - def_bool y - depends on CPU_EXYNOS4210 - config GPIO_MPC5200 def_bool y depends on PPC_MPC52xx @@ -131,30 +127,6 @@ config GPIO_MXS select GPIO_GENERIC select GENERIC_IRQ_CHIP -config GPIO_PLAT_SAMSUNG - def_bool y - depends on SAMSUNG_GPIOLIB_4BIT - -config GPIO_S3C24XX - def_bool y - depends on PLAT_S3C24XX - -config GPIO_S3C64XX - def_bool y - depends on ARCH_S3C64XX - -config GPIO_S5P64X0 - def_bool y - depends on ARCH_S5P64X0 - -config GPIO_S5PC100 - def_bool y - depends on CPU_S5PC100 - -config GPIO_S5PV210 - def_bool y - depends on CPU_S5PV210 - config GPIO_PL061 bool "PrimeCell PL061 GPIO support" depends on ARM_AMBA diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 10f3bbf0eceb..62db458c850d 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o -obj-$(CONFIG_GPIO_EXYNOS4) += gpio-exynos4.o obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o obj-$(CONFIG_GPIO_LANGWELL) += gpio-langwell.o @@ -38,14 +37,7 @@ obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o obj-$(CONFIG_GPIO_PCH) += gpio-pch.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o - -obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o -obj-$(CONFIG_GPIO_S3C24XX) += gpio-s3c24xx.o -obj-$(CONFIG_GPIO_S3C64XX) += gpio-s3c64xx.o -obj-$(CONFIG_GPIO_S5P64X0) += gpio-s5p64x0.o -obj-$(CONFIG_GPIO_S5PC100) += gpio-s5pc100.o -obj-$(CONFIG_GPIO_S5PV210) += gpio-s5pv210.o - +obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o obj-$(CONFIG_GPIO_SCH) += gpio-sch.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o diff --git a/drivers/gpio/gpio-exynos4.c b/drivers/gpio/gpio-exynos4.c deleted file mode 100644 index d24b337cf1ac..000000000000 --- a/drivers/gpio/gpio-exynos4.c +++ /dev/null @@ -1,385 +0,0 @@ -/* - * EXYNOS4 - GPIOlib support - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#include -#include -#include -#include - -#include - -#include -#include -#include - -int s3c_gpio_setpull_exynos4(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull) -{ - if (pull == S3C_GPIO_PULL_UP) - pull = 3; - - return s3c_gpio_setpull_updown(chip, off, pull); -} - -s3c_gpio_pull_t s3c_gpio_getpull_exynos4(struct s3c_gpio_chip *chip, - unsigned int off) -{ - s3c_gpio_pull_t pull; - - pull = s3c_gpio_getpull_updown(chip, off); - if (pull == 3) - pull = S3C_GPIO_PULL_UP; - - return pull; -} - -static struct s3c_gpio_cfg gpio_cfg = { - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_exynos4, - .get_pull = s3c_gpio_getpull_exynos4, -}; - -static struct s3c_gpio_cfg gpio_cfg_noint = { - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_exynos4, - .get_pull = s3c_gpio_getpull_exynos4, -}; - -/* - * Following are the gpio banks in v310. - * - * The 'config' member when left to NULL, is initialized to the default - * structure gpio_cfg in the init function below. - * - * The 'base' member is also initialized in the init function below. - * Note: The initialization of 'base' member of s3c_gpio_chip structure - * uses the above macro and depends on the banks being listed in order here. - */ -static struct s3c_gpio_chip exynos4_gpio_part1_4bit[] = { - { - .chip = { - .base = EXYNOS4_GPA0(0), - .ngpio = EXYNOS4_GPIO_A0_NR, - .label = "GPA0", - }, - }, { - .chip = { - .base = EXYNOS4_GPA1(0), - .ngpio = EXYNOS4_GPIO_A1_NR, - .label = "GPA1", - }, - }, { - .chip = { - .base = EXYNOS4_GPB(0), - .ngpio = EXYNOS4_GPIO_B_NR, - .label = "GPB", - }, - }, { - .chip = { - .base = EXYNOS4_GPC0(0), - .ngpio = EXYNOS4_GPIO_C0_NR, - .label = "GPC0", - }, - }, { - .chip = { - .base = EXYNOS4_GPC1(0), - .ngpio = EXYNOS4_GPIO_C1_NR, - .label = "GPC1", - }, - }, { - .chip = { - .base = EXYNOS4_GPD0(0), - .ngpio = EXYNOS4_GPIO_D0_NR, - .label = "GPD0", - }, - }, { - .chip = { - .base = EXYNOS4_GPD1(0), - .ngpio = EXYNOS4_GPIO_D1_NR, - .label = "GPD1", - }, - }, { - .chip = { - .base = EXYNOS4_GPE0(0), - .ngpio = EXYNOS4_GPIO_E0_NR, - .label = "GPE0", - }, - }, { - .chip = { - .base = EXYNOS4_GPE1(0), - .ngpio = EXYNOS4_GPIO_E1_NR, - .label = "GPE1", - }, - }, { - .chip = { - .base = EXYNOS4_GPE2(0), - .ngpio = EXYNOS4_GPIO_E2_NR, - .label = "GPE2", - }, - }, { - .chip = { - .base = EXYNOS4_GPE3(0), - .ngpio = EXYNOS4_GPIO_E3_NR, - .label = "GPE3", - }, - }, { - .chip = { - .base = EXYNOS4_GPE4(0), - .ngpio = EXYNOS4_GPIO_E4_NR, - .label = "GPE4", - }, - }, { - .chip = { - .base = EXYNOS4_GPF0(0), - .ngpio = EXYNOS4_GPIO_F0_NR, - .label = "GPF0", - }, - }, { - .chip = { - .base = EXYNOS4_GPF1(0), - .ngpio = EXYNOS4_GPIO_F1_NR, - .label = "GPF1", - }, - }, { - .chip = { - .base = EXYNOS4_GPF2(0), - .ngpio = EXYNOS4_GPIO_F2_NR, - .label = "GPF2", - }, - }, { - .chip = { - .base = EXYNOS4_GPF3(0), - .ngpio = EXYNOS4_GPIO_F3_NR, - .label = "GPF3", - }, - }, -}; - -static struct s3c_gpio_chip exynos4_gpio_part2_4bit[] = { - { - .chip = { - .base = EXYNOS4_GPJ0(0), - .ngpio = EXYNOS4_GPIO_J0_NR, - .label = "GPJ0", - }, - }, { - .chip = { - .base = EXYNOS4_GPJ1(0), - .ngpio = EXYNOS4_GPIO_J1_NR, - .label = "GPJ1", - }, - }, { - .chip = { - .base = EXYNOS4_GPK0(0), - .ngpio = EXYNOS4_GPIO_K0_NR, - .label = "GPK0", - }, - }, { - .chip = { - .base = EXYNOS4_GPK1(0), - .ngpio = EXYNOS4_GPIO_K1_NR, - .label = "GPK1", - }, - }, { - .chip = { - .base = EXYNOS4_GPK2(0), - .ngpio = EXYNOS4_GPIO_K2_NR, - .label = "GPK2", - }, - }, { - .chip = { - .base = EXYNOS4_GPK3(0), - .ngpio = EXYNOS4_GPIO_K3_NR, - .label = "GPK3", - }, - }, { - .chip = { - .base = EXYNOS4_GPL0(0), - .ngpio = EXYNOS4_GPIO_L0_NR, - .label = "GPL0", - }, - }, { - .chip = { - .base = EXYNOS4_GPL1(0), - .ngpio = EXYNOS4_GPIO_L1_NR, - .label = "GPL1", - }, - }, { - .chip = { - .base = EXYNOS4_GPL2(0), - .ngpio = EXYNOS4_GPIO_L2_NR, - .label = "GPL2", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = EXYNOS4_GPY0(0), - .ngpio = EXYNOS4_GPIO_Y0_NR, - .label = "GPY0", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = EXYNOS4_GPY1(0), - .ngpio = EXYNOS4_GPIO_Y1_NR, - .label = "GPY1", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = EXYNOS4_GPY2(0), - .ngpio = EXYNOS4_GPIO_Y2_NR, - .label = "GPY2", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = EXYNOS4_GPY3(0), - .ngpio = EXYNOS4_GPIO_Y3_NR, - .label = "GPY3", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = EXYNOS4_GPY4(0), - .ngpio = EXYNOS4_GPIO_Y4_NR, - .label = "GPY4", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = EXYNOS4_GPY5(0), - .ngpio = EXYNOS4_GPIO_Y5_NR, - .label = "GPY5", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = EXYNOS4_GPY6(0), - .ngpio = EXYNOS4_GPIO_Y6_NR, - .label = "GPY6", - }, - }, { - .base = (S5P_VA_GPIO2 + 0xC00), - .config = &gpio_cfg_noint, - .irq_base = IRQ_EINT(0), - .chip = { - .base = EXYNOS4_GPX0(0), - .ngpio = EXYNOS4_GPIO_X0_NR, - .label = "GPX0", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO2 + 0xC20), - .config = &gpio_cfg_noint, - .irq_base = IRQ_EINT(8), - .chip = { - .base = EXYNOS4_GPX1(0), - .ngpio = EXYNOS4_GPIO_X1_NR, - .label = "GPX1", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO2 + 0xC40), - .config = &gpio_cfg_noint, - .irq_base = IRQ_EINT(16), - .chip = { - .base = EXYNOS4_GPX2(0), - .ngpio = EXYNOS4_GPIO_X2_NR, - .label = "GPX2", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO2 + 0xC60), - .config = &gpio_cfg_noint, - .irq_base = IRQ_EINT(24), - .chip = { - .base = EXYNOS4_GPX3(0), - .ngpio = EXYNOS4_GPIO_X3_NR, - .label = "GPX3", - .to_irq = samsung_gpiolib_to_irq, - }, - }, -}; - -static struct s3c_gpio_chip exynos4_gpio_part3_4bit[] = { - { - .chip = { - .base = EXYNOS4_GPZ(0), - .ngpio = EXYNOS4_GPIO_Z_NR, - .label = "GPZ", - }, - }, -}; - -static __init int exynos4_gpiolib_init(void) -{ - struct s3c_gpio_chip *chip; - int i; - int group = 0; - int nr_chips; - - /* GPIO part 1 */ - - chip = exynos4_gpio_part1_4bit; - nr_chips = ARRAY_SIZE(exynos4_gpio_part1_4bit); - - for (i = 0; i < nr_chips; i++, chip++) { - if (chip->config == NULL) { - chip->config = &gpio_cfg; - /* Assign the GPIO interrupt group */ - chip->group = group++; - } - if (chip->base == NULL) - chip->base = S5P_VA_GPIO1 + (i) * 0x20; - } - - samsung_gpiolib_add_4bit_chips(exynos4_gpio_part1_4bit, nr_chips); - - /* GPIO part 2 */ - - chip = exynos4_gpio_part2_4bit; - nr_chips = ARRAY_SIZE(exynos4_gpio_part2_4bit); - - for (i = 0; i < nr_chips; i++, chip++) { - if (chip->config == NULL) { - chip->config = &gpio_cfg; - /* Assign the GPIO interrupt group */ - chip->group = group++; - } - if (chip->base == NULL) - chip->base = S5P_VA_GPIO2 + (i) * 0x20; - } - - samsung_gpiolib_add_4bit_chips(exynos4_gpio_part2_4bit, nr_chips); - - /* GPIO part 3 */ - - chip = exynos4_gpio_part3_4bit; - nr_chips = ARRAY_SIZE(exynos4_gpio_part3_4bit); - - for (i = 0; i < nr_chips; i++, chip++) { - if (chip->config == NULL) { - chip->config = &gpio_cfg; - /* Assign the GPIO interrupt group */ - chip->group = group++; - } - if (chip->base == NULL) - chip->base = S5P_VA_GPIO3 + (i) * 0x20; - } - - samsung_gpiolib_add_4bit_chips(exynos4_gpio_part3_4bit, nr_chips); - s5p_register_gpioint_bank(IRQ_GPIO_XA, 0, IRQ_GPIO1_NR_GROUPS); - s5p_register_gpioint_bank(IRQ_GPIO_XB, IRQ_GPIO1_NR_GROUPS, IRQ_GPIO2_NR_GROUPS); - - return 0; -} -core_initcall(exynos4_gpiolib_init); diff --git a/drivers/gpio/gpio-plat-samsung.c b/drivers/gpio/gpio-plat-samsung.c deleted file mode 100644 index ef67f1952a72..000000000000 --- a/drivers/gpio/gpio-plat-samsung.c +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * Copyright (c) 2009 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * SAMSUNG - GPIOlib support - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#ifndef DEBUG_GPIO -#define gpio_dbg(x...) do { } while (0) -#else -#define gpio_dbg(x...) printk(KERN_DEBUG x) -#endif - -/* The samsung_gpiolib_4bit routines are to control the gpio banks where - * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the - * following example: - * - * base + 0x00: Control register, 4 bits per gpio - * gpio n: 4 bits starting at (4*n) - * 0000 = input, 0001 = output, others mean special-function - * base + 0x04: Data register, 1 bit per gpio - * bit n: data bit n - * - * Note, since the data register is one bit per gpio and is at base + 0x4 - * we can use s3c_gpiolib_get and s3c_gpiolib_set to change the state of - * the output. -*/ - -static int samsung_gpiolib_4bit_input(struct gpio_chip *chip, - unsigned int offset) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - unsigned long con; - - con = __raw_readl(base + GPIOCON_OFF); - con &= ~(0xf << con_4bit_shift(offset)); - __raw_writel(con, base + GPIOCON_OFF); - - gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con); - - return 0; -} - -static int samsung_gpiolib_4bit_output(struct gpio_chip *chip, - unsigned int offset, int value) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - unsigned long con; - unsigned long dat; - - con = __raw_readl(base + GPIOCON_OFF); - con &= ~(0xf << con_4bit_shift(offset)); - con |= 0x1 << con_4bit_shift(offset); - - dat = __raw_readl(base + GPIODAT_OFF); - - if (value) - dat |= 1 << offset; - else - dat &= ~(1 << offset); - - __raw_writel(dat, base + GPIODAT_OFF); - __raw_writel(con, base + GPIOCON_OFF); - __raw_writel(dat, base + GPIODAT_OFF); - - gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); - - return 0; -} - -/* The next set of routines are for the case where the GPIO configuration - * registers are 4 bits per GPIO but there is more than one register (the - * bank has more than 8 GPIOs. - * - * This case is the similar to the 4 bit case, but the registers are as - * follows: - * - * base + 0x00: Control register, 4 bits per gpio (lower 8 GPIOs) - * gpio n: 4 bits starting at (4*n) - * 0000 = input, 0001 = output, others mean special-function - * base + 0x04: Control register, 4 bits per gpio (up to 8 additions GPIOs) - * gpio n: 4 bits starting at (4*n) - * 0000 = input, 0001 = output, others mean special-function - * base + 0x08: Data register, 1 bit per gpio - * bit n: data bit n - * - * To allow us to use the s3c_gpiolib_get and s3c_gpiolib_set routines we - * store the 'base + 0x4' address so that these routines see the data - * register at ourchip->base + 0x04. - */ - -static int samsung_gpiolib_4bit2_input(struct gpio_chip *chip, - unsigned int offset) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - void __iomem *regcon = base; - unsigned long con; - - if (offset > 7) - offset -= 8; - else - regcon -= 4; - - con = __raw_readl(regcon); - con &= ~(0xf << con_4bit_shift(offset)); - __raw_writel(con, regcon); - - gpio_dbg("%s: %p: CON %08lx\n", __func__, base, con); - - return 0; -} - -static int samsung_gpiolib_4bit2_output(struct gpio_chip *chip, - unsigned int offset, int value) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - void __iomem *regcon = base; - unsigned long con; - unsigned long dat; - unsigned con_offset = offset; - - if (con_offset > 7) - con_offset -= 8; - else - regcon -= 4; - - con = __raw_readl(regcon); - con &= ~(0xf << con_4bit_shift(con_offset)); - con |= 0x1 << con_4bit_shift(con_offset); - - dat = __raw_readl(base + GPIODAT_OFF); - - if (value) - dat |= 1 << offset; - else - dat &= ~(1 << offset); - - __raw_writel(dat, base + GPIODAT_OFF); - __raw_writel(con, regcon); - __raw_writel(dat, base + GPIODAT_OFF); - - gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); - - return 0; -} - -void __init samsung_gpiolib_add_4bit(struct s3c_gpio_chip *chip) -{ - chip->chip.direction_input = samsung_gpiolib_4bit_input; - chip->chip.direction_output = samsung_gpiolib_4bit_output; - chip->pm = __gpio_pm(&s3c_gpio_pm_4bit); -} - -void __init samsung_gpiolib_add_4bit2(struct s3c_gpio_chip *chip) -{ - chip->chip.direction_input = samsung_gpiolib_4bit2_input; - chip->chip.direction_output = samsung_gpiolib_4bit2_output; - chip->pm = __gpio_pm(&s3c_gpio_pm_4bit); -} - -void __init samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip, - int nr_chips) -{ - for (; nr_chips > 0; nr_chips--, chip++) { - samsung_gpiolib_add_4bit(chip); - s3c_gpiolib_add(chip); - } -} - -void __init samsung_gpiolib_add_4bit2_chips(struct s3c_gpio_chip *chip, - int nr_chips) -{ - for (; nr_chips > 0; nr_chips--, chip++) { - samsung_gpiolib_add_4bit2(chip); - s3c_gpiolib_add(chip); - } -} - -void __init samsung_gpiolib_add_2bit_chips(struct s3c_gpio_chip *chip, - int nr_chips) -{ - for (; nr_chips > 0; nr_chips--, chip++) - s3c_gpiolib_add(chip); -} diff --git a/drivers/gpio/gpio-s3c24xx.c b/drivers/gpio/gpio-s3c24xx.c deleted file mode 100644 index ff6103130fe8..000000000000 --- a/drivers/gpio/gpio-s3c24xx.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (c) 2008-2010 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * S3C24XX GPIOlib support - * - * 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; either version 2 of the License. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -static int s3c24xx_gpiolib_banka_input(struct gpio_chip *chip, unsigned offset) -{ - return -EINVAL; -} - -static int s3c24xx_gpiolib_banka_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - unsigned long flags; - unsigned long dat; - unsigned long con; - - local_irq_save(flags); - - con = __raw_readl(base + 0x00); - dat = __raw_readl(base + 0x04); - - dat &= ~(1 << offset); - if (value) - dat |= 1 << offset; - - __raw_writel(dat, base + 0x04); - - con &= ~(1 << offset); - - __raw_writel(con, base + 0x00); - __raw_writel(dat, base + 0x04); - - local_irq_restore(flags); - return 0; -} - -static int s3c24xx_gpiolib_bankf_toirq(struct gpio_chip *chip, unsigned offset) -{ - if (offset < 4) - return IRQ_EINT0 + offset; - - if (offset < 8) - return IRQ_EINT4 + offset - 4; - - return -EINVAL; -} - -static struct s3c_gpio_cfg s3c24xx_gpiocfg_banka = { - .set_config = s3c_gpio_setcfg_s3c24xx_a, - .get_config = s3c_gpio_getcfg_s3c24xx_a, -}; - -struct s3c_gpio_cfg s3c24xx_gpiocfg_default = { - .set_config = s3c_gpio_setcfg_s3c24xx, - .get_config = s3c_gpio_getcfg_s3c24xx, -}; - -struct s3c_gpio_chip s3c24xx_gpios[] = { - [0] = { - .base = S3C2410_GPACON, - .pm = __gpio_pm(&s3c_gpio_pm_1bit), - .config = &s3c24xx_gpiocfg_banka, - .chip = { - .base = S3C2410_GPA(0), - .owner = THIS_MODULE, - .label = "GPIOA", - .ngpio = 24, - .direction_input = s3c24xx_gpiolib_banka_input, - .direction_output = s3c24xx_gpiolib_banka_output, - }, - }, - [1] = { - .base = S3C2410_GPBCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPB(0), - .owner = THIS_MODULE, - .label = "GPIOB", - .ngpio = 16, - }, - }, - [2] = { - .base = S3C2410_GPCCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPC(0), - .owner = THIS_MODULE, - .label = "GPIOC", - .ngpio = 16, - }, - }, - [3] = { - .base = S3C2410_GPDCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPD(0), - .owner = THIS_MODULE, - .label = "GPIOD", - .ngpio = 16, - }, - }, - [4] = { - .base = S3C2410_GPECON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPE(0), - .label = "GPIOE", - .owner = THIS_MODULE, - .ngpio = 16, - }, - }, - [5] = { - .base = S3C2410_GPFCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPF(0), - .owner = THIS_MODULE, - .label = "GPIOF", - .ngpio = 8, - .to_irq = s3c24xx_gpiolib_bankf_toirq, - }, - }, - [6] = { - .base = S3C2410_GPGCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .irq_base = IRQ_EINT8, - .chip = { - .base = S3C2410_GPG(0), - .owner = THIS_MODULE, - .label = "GPIOG", - .ngpio = 16, - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = S3C2410_GPHCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPH(0), - .owner = THIS_MODULE, - .label = "GPIOH", - .ngpio = 11, - }, - }, - /* GPIOS for the S3C2443 and later devices. */ - { - .base = S3C2440_GPJCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPJ(0), - .owner = THIS_MODULE, - .label = "GPIOJ", - .ngpio = 16, - }, - }, { - .base = S3C2443_GPKCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPK(0), - .owner = THIS_MODULE, - .label = "GPIOK", - .ngpio = 16, - }, - }, { - .base = S3C2443_GPLCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPL(0), - .owner = THIS_MODULE, - .label = "GPIOL", - .ngpio = 15, - }, - }, { - .base = S3C2443_GPMCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPM(0), - .owner = THIS_MODULE, - .label = "GPIOM", - .ngpio = 2, - }, - }, -}; - -static __init int s3c24xx_gpiolib_init(void) -{ - struct s3c_gpio_chip *chip = s3c24xx_gpios; - int gpn; - - for (gpn = 0; gpn < ARRAY_SIZE(s3c24xx_gpios); gpn++, chip++) { - if (!chip->config) - chip->config = &s3c24xx_gpiocfg_default; - - s3c_gpiolib_add(chip); - } - - return 0; -} -core_initcall(s3c24xx_gpiolib_init); - -/* gpiolib wrappers until these are totally eliminated */ - -void s3c2410_gpio_pullup(unsigned int pin, unsigned int to) -{ - int ret; - - WARN_ON(to); /* should be none of these left */ - - if (!to) { - /* if pull is enabled, try first with up, and if that - * fails, try using down */ - - ret = s3c_gpio_setpull(pin, S3C_GPIO_PULL_UP); - if (ret) - s3c_gpio_setpull(pin, S3C_GPIO_PULL_DOWN); - } else { - s3c_gpio_setpull(pin, S3C_GPIO_PULL_NONE); - } -} -EXPORT_SYMBOL(s3c2410_gpio_pullup); - -void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) -{ - /* do this via gpiolib until all users removed */ - - gpio_request(pin, "temporary"); - gpio_set_value(pin, to); - gpio_free(pin); -} -EXPORT_SYMBOL(s3c2410_gpio_setpin); - -unsigned int s3c2410_gpio_getpin(unsigned int pin) -{ - struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); - unsigned long offs = pin - chip->chip.base; - - return __raw_readl(chip->base + 0x04) & (1<< offs); -} -EXPORT_SYMBOL(s3c2410_gpio_getpin); - -unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change) -{ - unsigned long flags; - unsigned long misccr; - - local_irq_save(flags); - misccr = __raw_readl(S3C24XX_MISCCR); - misccr &= ~clear; - misccr ^= change; - __raw_writel(misccr, S3C24XX_MISCCR); - local_irq_restore(flags); - - return misccr; -} -EXPORT_SYMBOL(s3c2410_modify_misccr); diff --git a/drivers/gpio/gpio-s3c64xx.c b/drivers/gpio/gpio-s3c64xx.c deleted file mode 100644 index b4f1c8204f03..000000000000 --- a/drivers/gpio/gpio-s3c64xx.c +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C64XX - GPIOlib support - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -/* GPIO bank summary: - * - * Bank GPIOs Style SlpCon ExtInt Group - * A 8 4Bit Yes 1 - * B 7 4Bit Yes 1 - * C 8 4Bit Yes 2 - * D 5 4Bit Yes 3 - * E 5 4Bit Yes None - * F 16 2Bit Yes 4 [1] - * G 7 4Bit Yes 5 - * H 10 4Bit[2] Yes 6 - * I 16 2Bit Yes None - * J 12 2Bit Yes None - * K 16 4Bit[2] No None - * L 15 4Bit[2] No None - * M 6 4Bit No IRQ_EINT - * N 16 2Bit No IRQ_EINT - * O 16 2Bit Yes 7 - * P 15 2Bit Yes 8 - * Q 9 2Bit Yes 9 - * - * [1] BANKF pins 14,15 do not form part of the external interrupt sources - * [2] BANK has two control registers, GPxCON0 and GPxCON1 - */ - -static struct s3c_gpio_cfg gpio_4bit_cfg_noint = { - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .get_config = s3c_gpio_getcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static struct s3c_gpio_cfg gpio_4bit_cfg_eint0111 = { - .cfg_eint = 7, - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .get_config = s3c_gpio_getcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static struct s3c_gpio_cfg gpio_4bit_cfg_eint0011 = { - .cfg_eint = 3, - .get_config = s3c_gpio_getcfg_s3c64xx_4bit, - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static int s3c64xx_gpio2int_gpm(struct gpio_chip *chip, unsigned pin) -{ - return pin < 5 ? IRQ_EINT(23) + pin : -ENXIO; -} - -static struct s3c_gpio_chip gpio_4bit[] = { - { - .base = S3C64XX_GPA_BASE, - .config = &gpio_4bit_cfg_eint0111, - .chip = { - .base = S3C64XX_GPA(0), - .ngpio = S3C64XX_GPIO_A_NR, - .label = "GPA", - }, - }, { - .base = S3C64XX_GPB_BASE, - .config = &gpio_4bit_cfg_eint0111, - .chip = { - .base = S3C64XX_GPB(0), - .ngpio = S3C64XX_GPIO_B_NR, - .label = "GPB", - }, - }, { - .base = S3C64XX_GPC_BASE, - .config = &gpio_4bit_cfg_eint0111, - .chip = { - .base = S3C64XX_GPC(0), - .ngpio = S3C64XX_GPIO_C_NR, - .label = "GPC", - }, - }, { - .base = S3C64XX_GPD_BASE, - .config = &gpio_4bit_cfg_eint0111, - .chip = { - .base = S3C64XX_GPD(0), - .ngpio = S3C64XX_GPIO_D_NR, - .label = "GPD", - }, - }, { - .base = S3C64XX_GPE_BASE, - .config = &gpio_4bit_cfg_noint, - .chip = { - .base = S3C64XX_GPE(0), - .ngpio = S3C64XX_GPIO_E_NR, - .label = "GPE", - }, - }, { - .base = S3C64XX_GPG_BASE, - .config = &gpio_4bit_cfg_eint0111, - .chip = { - .base = S3C64XX_GPG(0), - .ngpio = S3C64XX_GPIO_G_NR, - .label = "GPG", - }, - }, { - .base = S3C64XX_GPM_BASE, - .config = &gpio_4bit_cfg_eint0011, - .chip = { - .base = S3C64XX_GPM(0), - .ngpio = S3C64XX_GPIO_M_NR, - .label = "GPM", - .to_irq = s3c64xx_gpio2int_gpm, - }, - }, -}; - -static int s3c64xx_gpio2int_gpl(struct gpio_chip *chip, unsigned pin) -{ - return pin >= 8 ? IRQ_EINT(16) + pin - 8 : -ENXIO; -} - -static struct s3c_gpio_chip gpio_4bit2[] = { - { - .base = S3C64XX_GPH_BASE + 0x4, - .config = &gpio_4bit_cfg_eint0111, - .chip = { - .base = S3C64XX_GPH(0), - .ngpio = S3C64XX_GPIO_H_NR, - .label = "GPH", - }, - }, { - .base = S3C64XX_GPK_BASE + 0x4, - .config = &gpio_4bit_cfg_noint, - .chip = { - .base = S3C64XX_GPK(0), - .ngpio = S3C64XX_GPIO_K_NR, - .label = "GPK", - }, - }, { - .base = S3C64XX_GPL_BASE + 0x4, - .config = &gpio_4bit_cfg_eint0011, - .chip = { - .base = S3C64XX_GPL(0), - .ngpio = S3C64XX_GPIO_L_NR, - .label = "GPL", - .to_irq = s3c64xx_gpio2int_gpl, - }, - }, -}; - -static struct s3c_gpio_cfg gpio_2bit_cfg_noint = { - .set_config = s3c_gpio_setcfg_s3c24xx, - .get_config = s3c_gpio_getcfg_s3c24xx, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static struct s3c_gpio_cfg gpio_2bit_cfg_eint10 = { - .cfg_eint = 2, - .set_config = s3c_gpio_setcfg_s3c24xx, - .get_config = s3c_gpio_getcfg_s3c24xx, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static struct s3c_gpio_cfg gpio_2bit_cfg_eint11 = { - .cfg_eint = 3, - .set_config = s3c_gpio_setcfg_s3c24xx, - .get_config = s3c_gpio_getcfg_s3c24xx, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static struct s3c_gpio_chip gpio_2bit[] = { - { - .base = S3C64XX_GPF_BASE, - .config = &gpio_2bit_cfg_eint11, - .chip = { - .base = S3C64XX_GPF(0), - .ngpio = S3C64XX_GPIO_F_NR, - .label = "GPF", - }, - }, { - .base = S3C64XX_GPI_BASE, - .config = &gpio_2bit_cfg_noint, - .chip = { - .base = S3C64XX_GPI(0), - .ngpio = S3C64XX_GPIO_I_NR, - .label = "GPI", - }, - }, { - .base = S3C64XX_GPJ_BASE, - .config = &gpio_2bit_cfg_noint, - .chip = { - .base = S3C64XX_GPJ(0), - .ngpio = S3C64XX_GPIO_J_NR, - .label = "GPJ", - }, - }, { - .base = S3C64XX_GPN_BASE, - .irq_base = IRQ_EINT(0), - .config = &gpio_2bit_cfg_eint10, - .chip = { - .base = S3C64XX_GPN(0), - .ngpio = S3C64XX_GPIO_N_NR, - .label = "GPN", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = S3C64XX_GPO_BASE, - .config = &gpio_2bit_cfg_eint11, - .chip = { - .base = S3C64XX_GPO(0), - .ngpio = S3C64XX_GPIO_O_NR, - .label = "GPO", - }, - }, { - .base = S3C64XX_GPP_BASE, - .config = &gpio_2bit_cfg_eint11, - .chip = { - .base = S3C64XX_GPP(0), - .ngpio = S3C64XX_GPIO_P_NR, - .label = "GPP", - }, - }, { - .base = S3C64XX_GPQ_BASE, - .config = &gpio_2bit_cfg_eint11, - .chip = { - .base = S3C64XX_GPQ(0), - .ngpio = S3C64XX_GPIO_Q_NR, - .label = "GPQ", - }, - }, -}; - -static __init void s3c64xx_gpiolib_add_2bit(struct s3c_gpio_chip *chip) -{ - chip->pm = __gpio_pm(&s3c_gpio_pm_2bit); -} - -static __init void s3c64xx_gpiolib_add(struct s3c_gpio_chip *chips, - int nr_chips, - void (*fn)(struct s3c_gpio_chip *)) -{ - for (; nr_chips > 0; nr_chips--, chips++) { - if (fn) - (fn)(chips); - s3c_gpiolib_add(chips); - } -} - -static __init int s3c64xx_gpiolib_init(void) -{ - s3c64xx_gpiolib_add(gpio_4bit, ARRAY_SIZE(gpio_4bit), - samsung_gpiolib_add_4bit); - - s3c64xx_gpiolib_add(gpio_4bit2, ARRAY_SIZE(gpio_4bit2), - samsung_gpiolib_add_4bit2); - - s3c64xx_gpiolib_add(gpio_2bit, ARRAY_SIZE(gpio_2bit), - s3c64xx_gpiolib_add_2bit); - - return 0; -} - -core_initcall(s3c64xx_gpiolib_init); diff --git a/drivers/gpio/gpio-s5p64x0.c b/drivers/gpio/gpio-s5p64x0.c deleted file mode 100644 index 96e816f5cc95..000000000000 --- a/drivers/gpio/gpio-s5p64x0.c +++ /dev/null @@ -1,510 +0,0 @@ -/* - * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * S5P64X0 - GPIOlib support - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -/* - * S5P6440 GPIO bank summary: - * - * Bank GPIOs Style SlpCon ExtInt Group - * A 6 4Bit Yes 1 - * B 7 4Bit Yes 1 - * C 8 4Bit Yes 2 - * F 2 2Bit Yes 4 [1] - * G 7 4Bit Yes 5 - * H 10 4Bit[2] Yes 6 - * I 16 2Bit Yes None - * J 12 2Bit Yes None - * N 16 2Bit No IRQ_EINT - * P 8 2Bit Yes 8 - * R 15 4Bit[2] Yes 8 - * - * S5P6450 GPIO bank summary: - * - * Bank GPIOs Style SlpCon ExtInt Group - * A 6 4Bit Yes 1 - * B 7 4Bit Yes 1 - * C 8 4Bit Yes 2 - * D 8 4Bit Yes None - * F 2 2Bit Yes None - * G 14 4Bit[2] Yes 5 - * H 10 4Bit[2] Yes 6 - * I 16 2Bit Yes None - * J 12 2Bit Yes None - * K 5 4Bit Yes None - * N 16 2Bit No IRQ_EINT - * P 11 2Bit Yes 8 - * Q 14 2Bit Yes None - * R 15 4Bit[2] Yes None - * S 8 2Bit Yes None - * - * [1] BANKF pins 14,15 do not form part of the external interrupt sources - * [2] BANK has two control registers, GPxCON0 and GPxCON1 - */ - -static int s5p64x0_gpiolib_rbank_4bit2_input(struct gpio_chip *chip, - unsigned int offset) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - void __iomem *regcon = base; - unsigned long con; - unsigned long flags; - - switch (offset) { - case 6: - offset += 1; - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - regcon -= 4; - break; - default: - offset -= 7; - break; - } - - s3c_gpio_lock(ourchip, flags); - - con = __raw_readl(regcon); - con &= ~(0xf << con_4bit_shift(offset)); - __raw_writel(con, regcon); - - s3c_gpio_unlock(ourchip, flags); - - return 0; -} - -static int s5p64x0_gpiolib_rbank_4bit2_output(struct gpio_chip *chip, - unsigned int offset, int value) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - void __iomem *regcon = base; - unsigned long con; - unsigned long dat; - unsigned long flags; - unsigned con_offset = offset; - - switch (con_offset) { - case 6: - con_offset += 1; - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - regcon -= 4; - break; - default: - con_offset -= 7; - break; - } - - s3c_gpio_lock(ourchip, flags); - - con = __raw_readl(regcon); - con &= ~(0xf << con_4bit_shift(con_offset)); - con |= 0x1 << con_4bit_shift(con_offset); - - dat = __raw_readl(base + GPIODAT_OFF); - if (value) - dat |= 1 << offset; - else - dat &= ~(1 << offset); - - __raw_writel(con, regcon); - __raw_writel(dat, base + GPIODAT_OFF); - - s3c_gpio_unlock(ourchip, flags); - - return 0; -} - -int s5p64x0_gpio_setcfg_4bit_rbank(struct s3c_gpio_chip *chip, - unsigned int off, unsigned int cfg) -{ - void __iomem *reg = chip->base; - unsigned int shift; - u32 con; - - switch (off) { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - shift = (off & 7) * 4; - reg -= 4; - break; - case 6: - shift = ((off + 1) & 7) * 4; - reg -= 4; - default: - shift = ((off + 1) & 7) * 4; - break; - } - - if (s3c_gpio_is_cfg_special(cfg)) { - cfg &= 0xf; - cfg <<= shift; - } - - con = __raw_readl(reg); - con &= ~(0xf << shift); - con |= cfg; - __raw_writel(con, reg); - - return 0; -} - -static struct s3c_gpio_cfg s5p64x0_gpio_cfgs[] = { - { - .cfg_eint = 0, - }, { - .cfg_eint = 7, - }, { - .cfg_eint = 3, - .set_config = s5p64x0_gpio_setcfg_4bit_rbank, - }, { - .cfg_eint = 0, - .set_config = s3c_gpio_setcfg_s3c24xx, - .get_config = s3c_gpio_getcfg_s3c24xx, - }, { - .cfg_eint = 2, - .set_config = s3c_gpio_setcfg_s3c24xx, - .get_config = s3c_gpio_getcfg_s3c24xx, - }, { - .cfg_eint = 3, - .set_config = s3c_gpio_setcfg_s3c24xx, - .get_config = s3c_gpio_getcfg_s3c24xx, - }, -}; - -static struct s3c_gpio_chip s5p6440_gpio_4bit[] = { - { - .base = S5P64X0_GPA_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6440_GPA(0), - .ngpio = S5P6440_GPIO_A_NR, - .label = "GPA", - }, - }, { - .base = S5P64X0_GPB_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6440_GPB(0), - .ngpio = S5P6440_GPIO_B_NR, - .label = "GPB", - }, - }, { - .base = S5P64X0_GPC_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6440_GPC(0), - .ngpio = S5P6440_GPIO_C_NR, - .label = "GPC", - }, - }, { - .base = S5P64X0_GPG_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6440_GPG(0), - .ngpio = S5P6440_GPIO_G_NR, - .label = "GPG", - }, - }, -}; - -static struct s3c_gpio_chip s5p6440_gpio_4bit2[] = { - { - .base = S5P64X0_GPH_BASE + 0x4, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6440_GPH(0), - .ngpio = S5P6440_GPIO_H_NR, - .label = "GPH", - }, - }, -}; - -static struct s3c_gpio_chip s5p6440_gpio_rbank_4bit2[] = { - { - .base = S5P64X0_GPR_BASE + 0x4, - .config = &s5p64x0_gpio_cfgs[2], - .chip = { - .base = S5P6440_GPR(0), - .ngpio = S5P6440_GPIO_R_NR, - .label = "GPR", - }, - }, -}; - -static struct s3c_gpio_chip s5p6440_gpio_2bit[] = { - { - .base = S5P64X0_GPF_BASE, - .config = &s5p64x0_gpio_cfgs[5], - .chip = { - .base = S5P6440_GPF(0), - .ngpio = S5P6440_GPIO_F_NR, - .label = "GPF", - }, - }, { - .base = S5P64X0_GPI_BASE, - .config = &s5p64x0_gpio_cfgs[3], - .chip = { - .base = S5P6440_GPI(0), - .ngpio = S5P6440_GPIO_I_NR, - .label = "GPI", - }, - }, { - .base = S5P64X0_GPJ_BASE, - .config = &s5p64x0_gpio_cfgs[3], - .chip = { - .base = S5P6440_GPJ(0), - .ngpio = S5P6440_GPIO_J_NR, - .label = "GPJ", - }, - }, { - .base = S5P64X0_GPN_BASE, - .config = &s5p64x0_gpio_cfgs[4], - .chip = { - .base = S5P6440_GPN(0), - .ngpio = S5P6440_GPIO_N_NR, - .label = "GPN", - }, - }, { - .base = S5P64X0_GPP_BASE, - .config = &s5p64x0_gpio_cfgs[5], - .chip = { - .base = S5P6440_GPP(0), - .ngpio = S5P6440_GPIO_P_NR, - .label = "GPP", - }, - }, -}; - -static struct s3c_gpio_chip s5p6450_gpio_4bit[] = { - { - .base = S5P64X0_GPA_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6450_GPA(0), - .ngpio = S5P6450_GPIO_A_NR, - .label = "GPA", - }, - }, { - .base = S5P64X0_GPB_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6450_GPB(0), - .ngpio = S5P6450_GPIO_B_NR, - .label = "GPB", - }, - }, { - .base = S5P64X0_GPC_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6450_GPC(0), - .ngpio = S5P6450_GPIO_C_NR, - .label = "GPC", - }, - }, { - .base = S5P6450_GPD_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6450_GPD(0), - .ngpio = S5P6450_GPIO_D_NR, - .label = "GPD", - }, - }, { - .base = S5P6450_GPK_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6450_GPK(0), - .ngpio = S5P6450_GPIO_K_NR, - .label = "GPK", - }, - }, -}; - -static struct s3c_gpio_chip s5p6450_gpio_4bit2[] = { - { - .base = S5P64X0_GPG_BASE + 0x4, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6450_GPG(0), - .ngpio = S5P6450_GPIO_G_NR, - .label = "GPG", - }, - }, { - .base = S5P64X0_GPH_BASE + 0x4, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6450_GPH(0), - .ngpio = S5P6450_GPIO_H_NR, - .label = "GPH", - }, - }, -}; - -static struct s3c_gpio_chip s5p6450_gpio_rbank_4bit2[] = { - { - .base = S5P64X0_GPR_BASE + 0x4, - .config = &s5p64x0_gpio_cfgs[2], - .chip = { - .base = S5P6450_GPR(0), - .ngpio = S5P6450_GPIO_R_NR, - .label = "GPR", - }, - }, -}; - -static struct s3c_gpio_chip s5p6450_gpio_2bit[] = { - { - .base = S5P64X0_GPF_BASE, - .config = &s5p64x0_gpio_cfgs[5], - .chip = { - .base = S5P6450_GPF(0), - .ngpio = S5P6450_GPIO_F_NR, - .label = "GPF", - }, - }, { - .base = S5P64X0_GPI_BASE, - .config = &s5p64x0_gpio_cfgs[3], - .chip = { - .base = S5P6450_GPI(0), - .ngpio = S5P6450_GPIO_I_NR, - .label = "GPI", - }, - }, { - .base = S5P64X0_GPJ_BASE, - .config = &s5p64x0_gpio_cfgs[3], - .chip = { - .base = S5P6450_GPJ(0), - .ngpio = S5P6450_GPIO_J_NR, - .label = "GPJ", - }, - }, { - .base = S5P64X0_GPN_BASE, - .config = &s5p64x0_gpio_cfgs[4], - .chip = { - .base = S5P6450_GPN(0), - .ngpio = S5P6450_GPIO_N_NR, - .label = "GPN", - }, - }, { - .base = S5P64X0_GPP_BASE, - .config = &s5p64x0_gpio_cfgs[5], - .chip = { - .base = S5P6450_GPP(0), - .ngpio = S5P6450_GPIO_P_NR, - .label = "GPP", - }, - }, { - .base = S5P6450_GPQ_BASE, - .config = &s5p64x0_gpio_cfgs[4], - .chip = { - .base = S5P6450_GPQ(0), - .ngpio = S5P6450_GPIO_Q_NR, - .label = "GPQ", - }, - }, { - .base = S5P6450_GPS_BASE, - .config = &s5p64x0_gpio_cfgs[5], - .chip = { - .base = S5P6450_GPS(0), - .ngpio = S5P6450_GPIO_S_NR, - .label = "GPS", - }, - }, -}; - -void __init s5p64x0_gpiolib_set_cfg(struct s3c_gpio_cfg *chipcfg, int nr_chips) -{ - for (; nr_chips > 0; nr_chips--, chipcfg++) { - if (!chipcfg->set_config) - chipcfg->set_config = s3c_gpio_setcfg_s3c64xx_4bit; - if (!chipcfg->get_config) - chipcfg->get_config = s3c_gpio_getcfg_s3c64xx_4bit; - if (!chipcfg->set_pull) - chipcfg->set_pull = s3c_gpio_setpull_updown; - if (!chipcfg->get_pull) - chipcfg->get_pull = s3c_gpio_getpull_updown; - } -} - -static void __init s5p64x0_gpio_add_rbank_4bit2(struct s3c_gpio_chip *chip, - int nr_chips) -{ - for (; nr_chips > 0; nr_chips--, chip++) { - chip->chip.direction_input = s5p64x0_gpiolib_rbank_4bit2_input; - chip->chip.direction_output = - s5p64x0_gpiolib_rbank_4bit2_output; - s3c_gpiolib_add(chip); - } -} - -static int __init s5p64x0_gpiolib_init(void) -{ - unsigned int chipid; - - chipid = __raw_readl(S5P64X0_SYS_ID); - - s5p64x0_gpiolib_set_cfg(s5p64x0_gpio_cfgs, - ARRAY_SIZE(s5p64x0_gpio_cfgs)); - - if ((chipid & 0xff000) == 0x50000) { - samsung_gpiolib_add_2bit_chips(s5p6450_gpio_2bit, - ARRAY_SIZE(s5p6450_gpio_2bit)); - - samsung_gpiolib_add_4bit_chips(s5p6450_gpio_4bit, - ARRAY_SIZE(s5p6450_gpio_4bit)); - - samsung_gpiolib_add_4bit2_chips(s5p6450_gpio_4bit2, - ARRAY_SIZE(s5p6450_gpio_4bit2)); - - s5p64x0_gpio_add_rbank_4bit2(s5p6450_gpio_rbank_4bit2, - ARRAY_SIZE(s5p6450_gpio_rbank_4bit2)); - } else { - samsung_gpiolib_add_2bit_chips(s5p6440_gpio_2bit, - ARRAY_SIZE(s5p6440_gpio_2bit)); - - samsung_gpiolib_add_4bit_chips(s5p6440_gpio_4bit, - ARRAY_SIZE(s5p6440_gpio_4bit)); - - samsung_gpiolib_add_4bit2_chips(s5p6440_gpio_4bit2, - ARRAY_SIZE(s5p6440_gpio_4bit2)); - - s5p64x0_gpio_add_rbank_4bit2(s5p6440_gpio_rbank_4bit2, - ARRAY_SIZE(s5p6440_gpio_rbank_4bit2)); - } - - return 0; -} -core_initcall(s5p64x0_gpiolib_init); diff --git a/drivers/gpio/gpio-s5pc100.c b/drivers/gpio/gpio-s5pc100.c deleted file mode 100644 index 7f87b0c76e0b..000000000000 --- a/drivers/gpio/gpio-s5pc100.c +++ /dev/null @@ -1,354 +0,0 @@ -/* - * S5PC100 - GPIOlib support - * - * Copyright (c) 2010 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Copyright 2009 Samsung Electronics Co - * Kyungmin Park - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -/* S5PC100 GPIO bank summary: - * - * Bank GPIOs Style INT Type - * A0 8 4Bit GPIO_INT0 - * A1 5 4Bit GPIO_INT1 - * B 8 4Bit GPIO_INT2 - * C 5 4Bit GPIO_INT3 - * D 7 4Bit GPIO_INT4 - * E0 8 4Bit GPIO_INT5 - * E1 6 4Bit GPIO_INT6 - * F0 8 4Bit GPIO_INT7 - * F1 8 4Bit GPIO_INT8 - * F2 8 4Bit GPIO_INT9 - * F3 4 4Bit GPIO_INT10 - * G0 8 4Bit GPIO_INT11 - * G1 3 4Bit GPIO_INT12 - * G2 7 4Bit GPIO_INT13 - * G3 7 4Bit GPIO_INT14 - * H0 8 4Bit WKUP_INT - * H1 8 4Bit WKUP_INT - * H2 8 4Bit WKUP_INT - * H3 8 4Bit WKUP_INT - * I 8 4Bit GPIO_INT15 - * J0 8 4Bit GPIO_INT16 - * J1 5 4Bit GPIO_INT17 - * J2 8 4Bit GPIO_INT18 - * J3 8 4Bit GPIO_INT19 - * J4 4 4Bit GPIO_INT20 - * K0 8 4Bit None - * K1 6 4Bit None - * K2 8 4Bit None - * K3 8 4Bit None - * L0 8 4Bit None - * L1 8 4Bit None - * L2 8 4Bit None - * L3 8 4Bit None - */ - -static struct s3c_gpio_cfg gpio_cfg = { - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static struct s3c_gpio_cfg gpio_cfg_eint = { - .cfg_eint = 0xf, - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static struct s3c_gpio_cfg gpio_cfg_noint = { - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -/* - * GPIO bank's base address given the index of the bank in the - * list of all gpio banks. - */ -#define S5PC100_BANK_BASE(bank_nr) (S5P_VA_GPIO + ((bank_nr) * 0x20)) - -/* - * Following are the gpio banks in S5PC100. - * - * The 'config' member when left to NULL, is initialized to the default - * structure gpio_cfg in the init function below. - * - * The 'base' member is also initialized in the init function below. - * Note: The initialization of 'base' member of s3c_gpio_chip structure - * uses the above macro and depends on the banks being listed in order here. - */ -static struct s3c_gpio_chip s5pc100_gpio_chips[] = { - { - .chip = { - .base = S5PC100_GPA0(0), - .ngpio = S5PC100_GPIO_A0_NR, - .label = "GPA0", - }, - }, { - .chip = { - .base = S5PC100_GPA1(0), - .ngpio = S5PC100_GPIO_A1_NR, - .label = "GPA1", - }, - }, { - .chip = { - .base = S5PC100_GPB(0), - .ngpio = S5PC100_GPIO_B_NR, - .label = "GPB", - }, - }, { - .chip = { - .base = S5PC100_GPC(0), - .ngpio = S5PC100_GPIO_C_NR, - .label = "GPC", - }, - }, { - .chip = { - .base = S5PC100_GPD(0), - .ngpio = S5PC100_GPIO_D_NR, - .label = "GPD", - }, - }, { - .chip = { - .base = S5PC100_GPE0(0), - .ngpio = S5PC100_GPIO_E0_NR, - .label = "GPE0", - }, - }, { - .chip = { - .base = S5PC100_GPE1(0), - .ngpio = S5PC100_GPIO_E1_NR, - .label = "GPE1", - }, - }, { - .chip = { - .base = S5PC100_GPF0(0), - .ngpio = S5PC100_GPIO_F0_NR, - .label = "GPF0", - }, - }, { - .chip = { - .base = S5PC100_GPF1(0), - .ngpio = S5PC100_GPIO_F1_NR, - .label = "GPF1", - }, - }, { - .chip = { - .base = S5PC100_GPF2(0), - .ngpio = S5PC100_GPIO_F2_NR, - .label = "GPF2", - }, - }, { - .chip = { - .base = S5PC100_GPF3(0), - .ngpio = S5PC100_GPIO_F3_NR, - .label = "GPF3", - }, - }, { - .chip = { - .base = S5PC100_GPG0(0), - .ngpio = S5PC100_GPIO_G0_NR, - .label = "GPG0", - }, - }, { - .chip = { - .base = S5PC100_GPG1(0), - .ngpio = S5PC100_GPIO_G1_NR, - .label = "GPG1", - }, - }, { - .chip = { - .base = S5PC100_GPG2(0), - .ngpio = S5PC100_GPIO_G2_NR, - .label = "GPG2", - }, - }, { - .chip = { - .base = S5PC100_GPG3(0), - .ngpio = S5PC100_GPIO_G3_NR, - .label = "GPG3", - }, - }, { - .chip = { - .base = S5PC100_GPI(0), - .ngpio = S5PC100_GPIO_I_NR, - .label = "GPI", - }, - }, { - .chip = { - .base = S5PC100_GPJ0(0), - .ngpio = S5PC100_GPIO_J0_NR, - .label = "GPJ0", - }, - }, { - .chip = { - .base = S5PC100_GPJ1(0), - .ngpio = S5PC100_GPIO_J1_NR, - .label = "GPJ1", - }, - }, { - .chip = { - .base = S5PC100_GPJ2(0), - .ngpio = S5PC100_GPIO_J2_NR, - .label = "GPJ2", - }, - }, { - .chip = { - .base = S5PC100_GPJ3(0), - .ngpio = S5PC100_GPIO_J3_NR, - .label = "GPJ3", - }, - }, { - .chip = { - .base = S5PC100_GPJ4(0), - .ngpio = S5PC100_GPIO_J4_NR, - .label = "GPJ4", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPK0(0), - .ngpio = S5PC100_GPIO_K0_NR, - .label = "GPK0", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPK1(0), - .ngpio = S5PC100_GPIO_K1_NR, - .label = "GPK1", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPK2(0), - .ngpio = S5PC100_GPIO_K2_NR, - .label = "GPK2", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPK3(0), - .ngpio = S5PC100_GPIO_K3_NR, - .label = "GPK3", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPL0(0), - .ngpio = S5PC100_GPIO_L0_NR, - .label = "GPL0", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPL1(0), - .ngpio = S5PC100_GPIO_L1_NR, - .label = "GPL1", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPL2(0), - .ngpio = S5PC100_GPIO_L2_NR, - .label = "GPL2", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPL3(0), - .ngpio = S5PC100_GPIO_L3_NR, - .label = "GPL3", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPL4(0), - .ngpio = S5PC100_GPIO_L4_NR, - .label = "GPL4", - }, - }, { - .base = (S5P_VA_GPIO + 0xC00), - .config = &gpio_cfg_eint, - .irq_base = IRQ_EINT(0), - .chip = { - .base = S5PC100_GPH0(0), - .ngpio = S5PC100_GPIO_H0_NR, - .label = "GPH0", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO + 0xC20), - .config = &gpio_cfg_eint, - .irq_base = IRQ_EINT(8), - .chip = { - .base = S5PC100_GPH1(0), - .ngpio = S5PC100_GPIO_H1_NR, - .label = "GPH1", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO + 0xC40), - .config = &gpio_cfg_eint, - .irq_base = IRQ_EINT(16), - .chip = { - .base = S5PC100_GPH2(0), - .ngpio = S5PC100_GPIO_H2_NR, - .label = "GPH2", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO + 0xC60), - .config = &gpio_cfg_eint, - .irq_base = IRQ_EINT(24), - .chip = { - .base = S5PC100_GPH3(0), - .ngpio = S5PC100_GPIO_H3_NR, - .label = "GPH3", - .to_irq = samsung_gpiolib_to_irq, - }, - }, -}; - -static __init int s5pc100_gpiolib_init(void) -{ - struct s3c_gpio_chip *chip = s5pc100_gpio_chips; - int nr_chips = ARRAY_SIZE(s5pc100_gpio_chips); - int gpioint_group = 0; - int i; - - for (i = 0; i < nr_chips; i++, chip++) { - if (chip->config == NULL) { - chip->config = &gpio_cfg; - chip->group = gpioint_group++; - } - if (chip->base == NULL) - chip->base = S5PC100_BANK_BASE(i); - } - - samsung_gpiolib_add_4bit_chips(s5pc100_gpio_chips, nr_chips); - s5p_register_gpioint_bank(IRQ_GPIOINT, 0, S5P_GPIOINT_GROUP_MAXNR); - - return 0; -} -core_initcall(s5pc100_gpiolib_init); diff --git a/drivers/gpio/gpio-s5pv210.c b/drivers/gpio/gpio-s5pv210.c deleted file mode 100644 index eb12f1602de9..000000000000 --- a/drivers/gpio/gpio-s5pv210.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * S5PV210 - GPIOlib support - * - * Copyright (c) 2010 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static struct s3c_gpio_cfg gpio_cfg = { - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static struct s3c_gpio_cfg gpio_cfg_noint = { - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -/* GPIO bank's base address given the index of the bank in the - * list of all gpio banks. - */ -#define S5PV210_BANK_BASE(bank_nr) (S5P_VA_GPIO + ((bank_nr) * 0x20)) - -/* - * Following are the gpio banks in v210. - * - * The 'config' member when left to NULL, is initialized to the default - * structure gpio_cfg in the init function below. - * - * The 'base' member is also initialized in the init function below. - * Note: The initialization of 'base' member of s3c_gpio_chip structure - * uses the above macro and depends on the banks being listed in order here. - */ -static struct s3c_gpio_chip s5pv210_gpio_4bit[] = { - { - .chip = { - .base = S5PV210_GPA0(0), - .ngpio = S5PV210_GPIO_A0_NR, - .label = "GPA0", - }, - }, { - .chip = { - .base = S5PV210_GPA1(0), - .ngpio = S5PV210_GPIO_A1_NR, - .label = "GPA1", - }, - }, { - .chip = { - .base = S5PV210_GPB(0), - .ngpio = S5PV210_GPIO_B_NR, - .label = "GPB", - }, - }, { - .chip = { - .base = S5PV210_GPC0(0), - .ngpio = S5PV210_GPIO_C0_NR, - .label = "GPC0", - }, - }, { - .chip = { - .base = S5PV210_GPC1(0), - .ngpio = S5PV210_GPIO_C1_NR, - .label = "GPC1", - }, - }, { - .chip = { - .base = S5PV210_GPD0(0), - .ngpio = S5PV210_GPIO_D0_NR, - .label = "GPD0", - }, - }, { - .chip = { - .base = S5PV210_GPD1(0), - .ngpio = S5PV210_GPIO_D1_NR, - .label = "GPD1", - }, - }, { - .chip = { - .base = S5PV210_GPE0(0), - .ngpio = S5PV210_GPIO_E0_NR, - .label = "GPE0", - }, - }, { - .chip = { - .base = S5PV210_GPE1(0), - .ngpio = S5PV210_GPIO_E1_NR, - .label = "GPE1", - }, - }, { - .chip = { - .base = S5PV210_GPF0(0), - .ngpio = S5PV210_GPIO_F0_NR, - .label = "GPF0", - }, - }, { - .chip = { - .base = S5PV210_GPF1(0), - .ngpio = S5PV210_GPIO_F1_NR, - .label = "GPF1", - }, - }, { - .chip = { - .base = S5PV210_GPF2(0), - .ngpio = S5PV210_GPIO_F2_NR, - .label = "GPF2", - }, - }, { - .chip = { - .base = S5PV210_GPF3(0), - .ngpio = S5PV210_GPIO_F3_NR, - .label = "GPF3", - }, - }, { - .chip = { - .base = S5PV210_GPG0(0), - .ngpio = S5PV210_GPIO_G0_NR, - .label = "GPG0", - }, - }, { - .chip = { - .base = S5PV210_GPG1(0), - .ngpio = S5PV210_GPIO_G1_NR, - .label = "GPG1", - }, - }, { - .chip = { - .base = S5PV210_GPG2(0), - .ngpio = S5PV210_GPIO_G2_NR, - .label = "GPG2", - }, - }, { - .chip = { - .base = S5PV210_GPG3(0), - .ngpio = S5PV210_GPIO_G3_NR, - .label = "GPG3", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PV210_GPI(0), - .ngpio = S5PV210_GPIO_I_NR, - .label = "GPI", - }, - }, { - .chip = { - .base = S5PV210_GPJ0(0), - .ngpio = S5PV210_GPIO_J0_NR, - .label = "GPJ0", - }, - }, { - .chip = { - .base = S5PV210_GPJ1(0), - .ngpio = S5PV210_GPIO_J1_NR, - .label = "GPJ1", - }, - }, { - .chip = { - .base = S5PV210_GPJ2(0), - .ngpio = S5PV210_GPIO_J2_NR, - .label = "GPJ2", - }, - }, { - .chip = { - .base = S5PV210_GPJ3(0), - .ngpio = S5PV210_GPIO_J3_NR, - .label = "GPJ3", - }, - }, { - .chip = { - .base = S5PV210_GPJ4(0), - .ngpio = S5PV210_GPIO_J4_NR, - .label = "GPJ4", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PV210_MP01(0), - .ngpio = S5PV210_GPIO_MP01_NR, - .label = "MP01", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PV210_MP02(0), - .ngpio = S5PV210_GPIO_MP02_NR, - .label = "MP02", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PV210_MP03(0), - .ngpio = S5PV210_GPIO_MP03_NR, - .label = "MP03", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PV210_MP04(0), - .ngpio = S5PV210_GPIO_MP04_NR, - .label = "MP04", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PV210_MP05(0), - .ngpio = S5PV210_GPIO_MP05_NR, - .label = "MP05", - }, - }, { - .base = (S5P_VA_GPIO + 0xC00), - .config = &gpio_cfg_noint, - .irq_base = IRQ_EINT(0), - .chip = { - .base = S5PV210_GPH0(0), - .ngpio = S5PV210_GPIO_H0_NR, - .label = "GPH0", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO + 0xC20), - .config = &gpio_cfg_noint, - .irq_base = IRQ_EINT(8), - .chip = { - .base = S5PV210_GPH1(0), - .ngpio = S5PV210_GPIO_H1_NR, - .label = "GPH1", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO + 0xC40), - .config = &gpio_cfg_noint, - .irq_base = IRQ_EINT(16), - .chip = { - .base = S5PV210_GPH2(0), - .ngpio = S5PV210_GPIO_H2_NR, - .label = "GPH2", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO + 0xC60), - .config = &gpio_cfg_noint, - .irq_base = IRQ_EINT(24), - .chip = { - .base = S5PV210_GPH3(0), - .ngpio = S5PV210_GPIO_H3_NR, - .label = "GPH3", - .to_irq = samsung_gpiolib_to_irq, - }, - }, -}; - -static __init int s5pv210_gpiolib_init(void) -{ - struct s3c_gpio_chip *chip = s5pv210_gpio_4bit; - int nr_chips = ARRAY_SIZE(s5pv210_gpio_4bit); - int gpioint_group = 0; - int i = 0; - - for (i = 0; i < nr_chips; i++, chip++) { - if (chip->config == NULL) { - chip->config = &gpio_cfg; - chip->group = gpioint_group++; - } - if (chip->base == NULL) - chip->base = S5PV210_BANK_BASE(i); - } - - samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit, nr_chips); - s5p_register_gpioint_bank(IRQ_GPIOINT, 0, S5P_GPIOINT_GROUP_MAXNR); - - return 0; -} -core_initcall(s5pv210_gpiolib_init); diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c new file mode 100644 index 000000000000..36f3675e7de8 --- /dev/null +++ b/drivers/gpio/gpio-samsung.c @@ -0,0 +1,2686 @@ +/* + * Copyright (c) 2009-2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * SAMSUNG - GPIOlib support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifndef DEBUG_GPIO +#define gpio_dbg(x...) do { } while (0) +#else +#define gpio_dbg(x...) printk(KERN_DEBUG x) +#endif + +int samsung_gpio_setpull_updown(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull) +{ + void __iomem *reg = chip->base + 0x08; + int shift = off * 2; + u32 pup; + + pup = __raw_readl(reg); + pup &= ~(3 << shift); + pup |= pull << shift; + __raw_writel(pup, reg); + + return 0; +} + +samsung_gpio_pull_t samsung_gpio_getpull_updown(struct samsung_gpio_chip *chip, + unsigned int off) +{ + void __iomem *reg = chip->base + 0x08; + int shift = off * 2; + u32 pup = __raw_readl(reg); + + pup >>= shift; + pup &= 0x3; + + return (__force samsung_gpio_pull_t)pup; +} + +int s3c2443_gpio_setpull(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull) +{ + switch (pull) { + case S3C_GPIO_PULL_NONE: + pull = 0x01; + break; + case S3C_GPIO_PULL_UP: + pull = 0x00; + break; + case S3C_GPIO_PULL_DOWN: + pull = 0x02; + break; + } + return samsung_gpio_setpull_updown(chip, off, pull); +} + +samsung_gpio_pull_t s3c2443_gpio_getpull(struct samsung_gpio_chip *chip, + unsigned int off) +{ + samsung_gpio_pull_t pull; + + pull = samsung_gpio_getpull_updown(chip, off); + + switch (pull) { + case 0x00: + pull = S3C_GPIO_PULL_UP; + break; + case 0x01: + case 0x03: + pull = S3C_GPIO_PULL_NONE; + break; + case 0x02: + pull = S3C_GPIO_PULL_DOWN; + break; + } + + return pull; +} + +static int s3c24xx_gpio_setpull_1(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull, + samsung_gpio_pull_t updown) +{ + void __iomem *reg = chip->base + 0x08; + u32 pup = __raw_readl(reg); + + if (pull == updown) + pup &= ~(1 << off); + else if (pull == S3C_GPIO_PULL_NONE) + pup |= (1 << off); + else + return -EINVAL; + + __raw_writel(pup, reg); + return 0; +} + +static samsung_gpio_pull_t s3c24xx_gpio_getpull_1(struct samsung_gpio_chip *chip, + unsigned int off, + samsung_gpio_pull_t updown) +{ + void __iomem *reg = chip->base + 0x08; + u32 pup = __raw_readl(reg); + + pup &= (1 << off); + return pup ? S3C_GPIO_PULL_NONE : updown; +} + +samsung_gpio_pull_t s3c24xx_gpio_getpull_1up(struct samsung_gpio_chip *chip, + unsigned int off) +{ + return s3c24xx_gpio_getpull_1(chip, off, S3C_GPIO_PULL_UP); +} + +int s3c24xx_gpio_setpull_1up(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull) +{ + return s3c24xx_gpio_setpull_1(chip, off, pull, S3C_GPIO_PULL_UP); +} + +samsung_gpio_pull_t s3c24xx_gpio_getpull_1down(struct samsung_gpio_chip *chip, + unsigned int off) +{ + return s3c24xx_gpio_getpull_1(chip, off, S3C_GPIO_PULL_DOWN); +} + +int s3c24xx_gpio_setpull_1down(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull) +{ + return s3c24xx_gpio_setpull_1(chip, off, pull, S3C_GPIO_PULL_DOWN); +} + +static int exynos4_gpio_setpull(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull) +{ + if (pull == S3C_GPIO_PULL_UP) + pull = 3; + + return samsung_gpio_setpull_updown(chip, off, pull); +} + +static samsung_gpio_pull_t exynos4_gpio_getpull(struct samsung_gpio_chip *chip, + unsigned int off) +{ + samsung_gpio_pull_t pull; + + pull = samsung_gpio_getpull_updown(chip, off); + + if (pull == 3) + pull = S3C_GPIO_PULL_UP; + + return pull; +} + +/* + * samsung_gpio_setcfg_2bit - Samsung 2bit style GPIO configuration. + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * @cfg: The configuration value to set. + * + * This helper deal with the GPIO cases where the control register + * has two bits of configuration per gpio, which have the following + * functions: + * 00 = input + * 01 = output + * 1x = special function + */ + +static int samsung_gpio_setcfg_2bit(struct samsung_gpio_chip *chip, + unsigned int off, unsigned int cfg) +{ + void __iomem *reg = chip->base; + unsigned int shift = off * 2; + u32 con; + + if (samsung_gpio_is_cfg_special(cfg)) { + cfg &= 0xf; + if (cfg > 3) + return -EINVAL; + + cfg <<= shift; + } + + con = __raw_readl(reg); + con &= ~(0x3 << shift); + con |= cfg; + __raw_writel(con, reg); + + return 0; +} + +/* + * samsung_gpio_getcfg_2bit - Samsung 2bit style GPIO configuration read. + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * + * The reverse of samsung_gpio_setcfg_2bit(). Will return a value whicg + * could be directly passed back to samsung_gpio_setcfg_2bit(), from the + * S3C_GPIO_SPECIAL() macro. + */ + +static unsigned int samsung_gpio_getcfg_2bit(struct samsung_gpio_chip *chip, + unsigned int off) +{ + u32 con; + + con = __raw_readl(chip->base); + con >>= off * 2; + con &= 3; + + /* this conversion works for IN and OUT as well as special mode */ + return S3C_GPIO_SPECIAL(con); +} + +/* + * samsung_gpio_setcfg_4bit - Samsung 4bit single register GPIO config. + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * @cfg: The configuration value to set. + * + * This helper deal with the GPIO cases where the control register has 4 bits + * of control per GPIO, generally in the form of: + * 0000 = Input + * 0001 = Output + * others = Special functions (dependent on bank) + * + * Note, since the code to deal with the case where there are two control + * registers instead of one, we do not have a separate set of functions for + * each case. + */ + +static int samsung_gpio_setcfg_4bit(struct samsung_gpio_chip *chip, + unsigned int off, unsigned int cfg) +{ + void __iomem *reg = chip->base; + unsigned int shift = (off & 7) * 4; + u32 con; + + if (off < 8 && chip->chip.ngpio > 8) + reg -= 4; + + if (samsung_gpio_is_cfg_special(cfg)) { + cfg &= 0xf; + cfg <<= shift; + } + + con = __raw_readl(reg); + con &= ~(0xf << shift); + con |= cfg; + __raw_writel(con, reg); + + return 0; +} + +/* + * samsung_gpio_getcfg_4bit - Samsung 4bit single register GPIO config read. + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * + * The reverse of samsung_gpio_setcfg_4bit(), turning a gpio configuration + * register setting into a value the software can use, such as could be passed + * to samsung_gpio_setcfg_4bit(). + * + * @sa samsung_gpio_getcfg_2bit + */ + +static unsigned samsung_gpio_getcfg_4bit(struct samsung_gpio_chip *chip, + unsigned int off) +{ + void __iomem *reg = chip->base; + unsigned int shift = (off & 7) * 4; + u32 con; + + if (off < 8 && chip->chip.ngpio > 8) + reg -= 4; + + con = __raw_readl(reg); + con >>= shift; + con &= 0xf; + + /* this conversion works for IN and OUT as well as special mode */ + return S3C_GPIO_SPECIAL(con); +} + +/* + * s3c24xx_gpio_setcfg_abank - S3C24XX style GPIO configuration (Bank A) + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * @cfg: The configuration value to set. + * + * This helper deal with the GPIO cases where the control register + * has one bit of configuration for the gpio, where setting the bit + * means the pin is in special function mode and unset means output. + */ + +static int s3c24xx_gpio_setcfg_abank(struct samsung_gpio_chip *chip, + unsigned int off, unsigned int cfg) +{ + void __iomem *reg = chip->base; + unsigned int shift = off; + u32 con; + + if (samsung_gpio_is_cfg_special(cfg)) { + cfg &= 0xf; + + /* Map output to 0, and SFN2 to 1 */ + cfg -= 1; + if (cfg > 1) + return -EINVAL; + + cfg <<= shift; + } + + con = __raw_readl(reg); + con &= ~(0x1 << shift); + con |= cfg; + __raw_writel(con, reg); + + return 0; +} + +/* + * s3c24xx_gpio_getcfg_abank - S3C24XX style GPIO configuration read (Bank A) + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * + * The reverse of s3c24xx_gpio_setcfg_abank() turning an GPIO into a usable + * GPIO configuration value. + * + * @sa samsung_gpio_getcfg_2bit + * @sa samsung_gpio_getcfg_4bit + */ + +static unsigned s3c24xx_gpio_getcfg_abank(struct samsung_gpio_chip *chip, + unsigned int off) +{ + u32 con; + + con = __raw_readl(chip->base); + con >>= off; + con &= 1; + con++; + + return S3C_GPIO_SFN(con); +} + +static int s5p64x0_gpio_setcfg_rbank(struct samsung_gpio_chip *chip, + unsigned int off, unsigned int cfg) +{ + void __iomem *reg = chip->base; + unsigned int shift; + u32 con; + + switch (off) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + shift = (off & 7) * 4; + reg -= 4; + break; + case 6: + shift = ((off + 1) & 7) * 4; + reg -= 4; + default: + shift = ((off + 1) & 7) * 4; + break; + } + + if (samsung_gpio_is_cfg_special(cfg)) { + cfg &= 0xf; + cfg <<= shift; + } + + con = __raw_readl(reg); + con &= ~(0xf << shift); + con |= cfg; + __raw_writel(con, reg); + + return 0; +} + +static void __init samsung_gpiolib_set_cfg(struct samsung_gpio_cfg *chipcfg, + int nr_chips) +{ + for (; nr_chips > 0; nr_chips--, chipcfg++) { + if (!chipcfg->set_config) + chipcfg->set_config = samsung_gpio_setcfg_4bit; + if (!chipcfg->get_config) + chipcfg->get_config = samsung_gpio_getcfg_4bit; + if (!chipcfg->set_pull) + chipcfg->set_pull = samsung_gpio_setpull_updown; + if (!chipcfg->get_pull) + chipcfg->get_pull = samsung_gpio_getpull_updown; + } +} + +struct samsung_gpio_cfg s3c24xx_gpiocfg_default = { + .set_config = samsung_gpio_setcfg_2bit, + .get_config = samsung_gpio_getcfg_2bit, +}; + +static struct samsung_gpio_cfg s3c24xx_gpiocfg_banka = { + .set_config = s3c24xx_gpio_setcfg_abank, + .get_config = s3c24xx_gpio_getcfg_abank, +}; + +static struct samsung_gpio_cfg exynos4_gpio_cfg = { + .set_pull = exynos4_gpio_setpull, + .get_pull = exynos4_gpio_getpull, +}; + +static struct samsung_gpio_cfg s5p64x0_gpio_cfg_rbank = { + .cfg_eint = 0x3, + .set_config = s5p64x0_gpio_setcfg_rbank, + .get_config = samsung_gpio_getcfg_4bit, + .set_pull = samsung_gpio_setpull_updown, + .get_pull = samsung_gpio_getpull_updown, +}; + +static struct samsung_gpio_cfg samsung_gpio_cfgs[] = { + { + .cfg_eint = 0x0, + }, { + .cfg_eint = 0x3, + }, { + .cfg_eint = 0x7, + }, { + .cfg_eint = 0xF, + }, { + .cfg_eint = 0x0, + .set_config = samsung_gpio_setcfg_2bit, + .get_config = samsung_gpio_getcfg_2bit, + }, { + .cfg_eint = 0x2, + .set_config = samsung_gpio_setcfg_2bit, + .get_config = samsung_gpio_getcfg_2bit, + }, { + .cfg_eint = 0x3, + .set_config = samsung_gpio_setcfg_2bit, + .get_config = samsung_gpio_getcfg_2bit, + }, { + .set_config = samsung_gpio_setcfg_2bit, + .get_config = samsung_gpio_getcfg_2bit, + }, +}; + +/* + * Default routines for controlling GPIO, based on the original S3C24XX + * GPIO functions which deal with the case where each gpio bank of the + * chip is as following: + * + * base + 0x00: Control register, 2 bits per gpio + * gpio n: 2 bits starting at (2*n) + * 00 = input, 01 = output, others mean special-function + * base + 0x04: Data register, 1 bit per gpio + * bit n: data bit n +*/ + +static int samsung_gpiolib_2bit_input(struct gpio_chip *chip, unsigned offset) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long flags; + unsigned long con; + + samsung_gpio_lock(ourchip, flags); + + con = __raw_readl(base + 0x00); + con &= ~(3 << (offset * 2)); + + __raw_writel(con, base + 0x00); + + samsung_gpio_unlock(ourchip, flags); + return 0; +} + +static int samsung_gpiolib_2bit_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long flags; + unsigned long dat; + unsigned long con; + + samsung_gpio_lock(ourchip, flags); + + dat = __raw_readl(base + 0x04); + dat &= ~(1 << offset); + if (value) + dat |= 1 << offset; + __raw_writel(dat, base + 0x04); + + con = __raw_readl(base + 0x00); + con &= ~(3 << (offset * 2)); + con |= 1 << (offset * 2); + + __raw_writel(con, base + 0x00); + __raw_writel(dat, base + 0x04); + + samsung_gpio_unlock(ourchip, flags); + return 0; +} + +/* + * The samsung_gpiolib_4bit routines are to control the gpio banks where + * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the + * following example: + * + * base + 0x00: Control register, 4 bits per gpio + * gpio n: 4 bits starting at (4*n) + * 0000 = input, 0001 = output, others mean special-function + * base + 0x04: Data register, 1 bit per gpio + * bit n: data bit n + * + * Note, since the data register is one bit per gpio and is at base + 0x4 + * we can use samsung_gpiolib_get and samsung_gpiolib_set to change the + * state of the output. + */ + +static int samsung_gpiolib_4bit_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long con; + + con = __raw_readl(base + GPIOCON_OFF); + con &= ~(0xf << con_4bit_shift(offset)); + __raw_writel(con, base + GPIOCON_OFF); + + gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con); + + return 0; +} + +static int samsung_gpiolib_4bit_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long con; + unsigned long dat; + + con = __raw_readl(base + GPIOCON_OFF); + con &= ~(0xf << con_4bit_shift(offset)); + con |= 0x1 << con_4bit_shift(offset); + + dat = __raw_readl(base + GPIODAT_OFF); + + if (value) + dat |= 1 << offset; + else + dat &= ~(1 << offset); + + __raw_writel(dat, base + GPIODAT_OFF); + __raw_writel(con, base + GPIOCON_OFF); + __raw_writel(dat, base + GPIODAT_OFF); + + gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); + + return 0; +} + +/* + * The next set of routines are for the case where the GPIO configuration + * registers are 4 bits per GPIO but there is more than one register (the + * bank has more than 8 GPIOs. + * + * This case is the similar to the 4 bit case, but the registers are as + * follows: + * + * base + 0x00: Control register, 4 bits per gpio (lower 8 GPIOs) + * gpio n: 4 bits starting at (4*n) + * 0000 = input, 0001 = output, others mean special-function + * base + 0x04: Control register, 4 bits per gpio (up to 8 additions GPIOs) + * gpio n: 4 bits starting at (4*n) + * 0000 = input, 0001 = output, others mean special-function + * base + 0x08: Data register, 1 bit per gpio + * bit n: data bit n + * + * To allow us to use the samsung_gpiolib_get and samsung_gpiolib_set + * routines we store the 'base + 0x4' address so that these routines see + * the data register at ourchip->base + 0x04. + */ + +static int samsung_gpiolib_4bit2_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + + if (offset > 7) + offset -= 8; + else + regcon -= 4; + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(offset)); + __raw_writel(con, regcon); + + gpio_dbg("%s: %p: CON %08lx\n", __func__, base, con); + + return 0; +} + +static int samsung_gpiolib_4bit2_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + unsigned long dat; + unsigned con_offset = offset; + + if (con_offset > 7) + con_offset -= 8; + else + regcon -= 4; + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(con_offset)); + con |= 0x1 << con_4bit_shift(con_offset); + + dat = __raw_readl(base + GPIODAT_OFF); + + if (value) + dat |= 1 << offset; + else + dat &= ~(1 << offset); + + __raw_writel(dat, base + GPIODAT_OFF); + __raw_writel(con, regcon); + __raw_writel(dat, base + GPIODAT_OFF); + + gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); + + return 0; +} + +/* The next set of routines are for the case of s3c24xx bank a */ + +static int s3c24xx_gpiolib_banka_input(struct gpio_chip *chip, unsigned offset) +{ + return -EINVAL; +} + +static int s3c24xx_gpiolib_banka_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long flags; + unsigned long dat; + unsigned long con; + + local_irq_save(flags); + + con = __raw_readl(base + 0x00); + dat = __raw_readl(base + 0x04); + + dat &= ~(1 << offset); + if (value) + dat |= 1 << offset; + + __raw_writel(dat, base + 0x04); + + con &= ~(1 << offset); + + __raw_writel(con, base + 0x00); + __raw_writel(dat, base + 0x04); + + local_irq_restore(flags); + return 0; +} + +/* The next set of routines are for the case of s5p64x0 bank r */ + +static int s5p64x0_gpiolib_rbank_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + unsigned long flags; + + switch (offset) { + case 6: + offset += 1; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + regcon -= 4; + break; + default: + offset -= 7; + break; + } + + samsung_gpio_lock(ourchip, flags); + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(offset)); + __raw_writel(con, regcon); + + samsung_gpio_unlock(ourchip, flags); + + return 0; +} + +static int s5p64x0_gpiolib_rbank_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + unsigned long dat; + unsigned long flags; + unsigned con_offset = offset; + + switch (con_offset) { + case 6: + con_offset += 1; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + regcon -= 4; + break; + default: + con_offset -= 7; + break; + } + + samsung_gpio_lock(ourchip, flags); + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(con_offset)); + con |= 0x1 << con_4bit_shift(con_offset); + + dat = __raw_readl(base + GPIODAT_OFF); + if (value) + dat |= 1 << offset; + else + dat &= ~(1 << offset); + + __raw_writel(con, regcon); + __raw_writel(dat, base + GPIODAT_OFF); + + samsung_gpio_unlock(ourchip, flags); + + return 0; +} + +static void samsung_gpiolib_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long flags; + unsigned long dat; + + samsung_gpio_lock(ourchip, flags); + + dat = __raw_readl(base + 0x04); + dat &= ~(1 << offset); + if (value) + dat |= 1 << offset; + __raw_writel(dat, base + 0x04); + + samsung_gpio_unlock(ourchip, flags); +} + +static int samsung_gpiolib_get(struct gpio_chip *chip, unsigned offset) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + unsigned long val; + + val = __raw_readl(ourchip->base + 0x04); + val >>= offset; + val &= 1; + + return val; +} + +/* + * CONFIG_S3C_GPIO_TRACK enables the tracking of the s3c specific gpios + * for use with the configuration calls, and other parts of the s3c gpiolib + * support code. + * + * Not all s3c support code will need this, as some configurations of cpu + * may only support one or two different configuration options and have an + * easy gpio to samsung_gpio_chip mapping function. If this is the case, then + * the machine support file should provide its own samsung_gpiolib_getchip() + * and any other necessary functions. + */ + +#ifdef CONFIG_S3C_GPIO_TRACK +struct samsung_gpio_chip *s3c_gpios[S3C_GPIO_END]; + +static __init void s3c_gpiolib_track(struct samsung_gpio_chip *chip) +{ + unsigned int gpn; + int i; + + gpn = chip->chip.base; + for (i = 0; i < chip->chip.ngpio; i++, gpn++) { + BUG_ON(gpn >= ARRAY_SIZE(s3c_gpios)); + s3c_gpios[gpn] = chip; + } +} +#endif /* CONFIG_S3C_GPIO_TRACK */ + +/* + * samsung_gpiolib_add() - add the Samsung gpio_chip. + * @chip: The chip to register + * + * This is a wrapper to gpiochip_add() that takes our specific gpio chip + * information and makes the necessary alterations for the platform and + * notes the information for use with the configuration systems and any + * other parts of the system. + */ + +static void __init samsung_gpiolib_add(struct samsung_gpio_chip *chip) +{ + struct gpio_chip *gc = &chip->chip; + int ret; + + BUG_ON(!chip->base); + BUG_ON(!gc->label); + BUG_ON(!gc->ngpio); + + spin_lock_init(&chip->lock); + + if (!gc->direction_input) + gc->direction_input = samsung_gpiolib_2bit_input; + if (!gc->direction_output) + gc->direction_output = samsung_gpiolib_2bit_output; + if (!gc->set) + gc->set = samsung_gpiolib_set; + if (!gc->get) + gc->get = samsung_gpiolib_get; + +#ifdef CONFIG_PM + if (chip->pm != NULL) { + if (!chip->pm->save || !chip->pm->resume) + printk(KERN_ERR "gpio: %s has missing PM functions\n", + gc->label); + } else + printk(KERN_ERR "gpio: %s has no PM function\n", gc->label); +#endif + + /* gpiochip_add() prints own failure message on error. */ + ret = gpiochip_add(gc); + if (ret >= 0) + s3c_gpiolib_track(chip); +} + +static void __init s3c24xx_gpiolib_add_chips(struct samsung_gpio_chip *chip, + int nr_chips, void __iomem *base) +{ + int i; + struct gpio_chip *gc = &chip->chip; + + for (i = 0 ; i < nr_chips; i++, chip++) { + if (!chip->config) + chip->config = &s3c24xx_gpiocfg_default; + if (!chip->pm) + chip->pm = __gpio_pm(&samsung_gpio_pm_2bit); + if ((base != NULL) && (chip->base == NULL)) + chip->base = base + ((i) * 0x10); + + if (!gc->direction_input) + gc->direction_input = samsung_gpiolib_2bit_input; + if (!gc->direction_output) + gc->direction_output = samsung_gpiolib_2bit_output; + + samsung_gpiolib_add(chip); + } +} + +static void __init samsung_gpiolib_add_2bit_chips(struct samsung_gpio_chip *chip, + int nr_chips, void __iomem *base, + unsigned int offset) +{ + int i; + + for (i = 0 ; i < nr_chips; i++, chip++) { + chip->chip.direction_input = samsung_gpiolib_2bit_input; + chip->chip.direction_output = samsung_gpiolib_2bit_output; + + if (!chip->config) + chip->config = &samsung_gpio_cfgs[7]; + if (!chip->pm) + chip->pm = __gpio_pm(&samsung_gpio_pm_2bit); + if ((base != NULL) && (chip->base == NULL)) + chip->base = base + ((i) * offset); + + samsung_gpiolib_add(chip); + } +} + +/* + * samsung_gpiolib_add_4bit_chips - 4bit single register GPIO config. + * @chip: The gpio chip that is being configured. + * @nr_chips: The no of chips (gpio ports) for the GPIO being configured. + * + * This helper deal with the GPIO cases where the control register has 4 bits + * of control per GPIO, generally in the form of: + * 0000 = Input + * 0001 = Output + * others = Special functions (dependent on bank) + * + * Note, since the code to deal with the case where there are two control + * registers instead of one, we do not have a separate set of function + * (samsung_gpiolib_add_4bit2_chips)for each case. + */ + +static void __init samsung_gpiolib_add_4bit_chips(struct samsung_gpio_chip *chip, + int nr_chips, void __iomem *base) +{ + int i; + + for (i = 0 ; i < nr_chips; i++, chip++) { + chip->chip.direction_input = samsung_gpiolib_4bit_input; + chip->chip.direction_output = samsung_gpiolib_4bit_output; + + if (!chip->config) + chip->config = &samsung_gpio_cfgs[2]; + if (!chip->pm) + chip->pm = __gpio_pm(&samsung_gpio_pm_4bit); + if ((base != NULL) && (chip->base == NULL)) + chip->base = base + ((i) * 0x20); + + samsung_gpiolib_add(chip); + } +} + +static void __init samsung_gpiolib_add_4bit2_chips(struct samsung_gpio_chip *chip, + int nr_chips) +{ + for (; nr_chips > 0; nr_chips--, chip++) { + chip->chip.direction_input = samsung_gpiolib_4bit2_input; + chip->chip.direction_output = samsung_gpiolib_4bit2_output; + + if (!chip->config) + chip->config = &samsung_gpio_cfgs[2]; + if (!chip->pm) + chip->pm = __gpio_pm(&samsung_gpio_pm_4bit); + + samsung_gpiolib_add(chip); + } +} + +static void __init s5p64x0_gpiolib_add_rbank(struct samsung_gpio_chip *chip, + int nr_chips) +{ + for (; nr_chips > 0; nr_chips--, chip++) { + chip->chip.direction_input = s5p64x0_gpiolib_rbank_input; + chip->chip.direction_output = s5p64x0_gpiolib_rbank_output; + + if (!chip->pm) + chip->pm = __gpio_pm(&samsung_gpio_pm_4bit); + + samsung_gpiolib_add(chip); + } +} + +int samsung_gpiolib_to_irq(struct gpio_chip *chip, unsigned int offset) +{ + struct samsung_gpio_chip *samsung_chip = container_of(chip, struct samsung_gpio_chip, chip); + + return samsung_chip->irq_base + offset; +} + +#ifdef CONFIG_PLAT_S3C24XX +static int s3c24xx_gpiolib_fbank_to_irq(struct gpio_chip *chip, unsigned offset) +{ + if (offset < 4) + return IRQ_EINT0 + offset; + + if (offset < 8) + return IRQ_EINT4 + offset - 4; + + return -EINVAL; +} +#endif + +#ifdef CONFIG_PLAT_S3C64XX +static int s3c64xx_gpiolib_mbank_to_irq(struct gpio_chip *chip, unsigned pin) +{ + return pin < 5 ? IRQ_EINT(23) + pin : -ENXIO; +} + +static int s3c64xx_gpiolib_lbank_to_irq(struct gpio_chip *chip, unsigned pin) +{ + return pin >= 8 ? IRQ_EINT(16) + pin - 8 : -ENXIO; +} +#endif + +struct samsung_gpio_chip s3c24xx_gpios[] = { +#ifdef CONFIG_PLAT_S3C24XX + { + .config = &s3c24xx_gpiocfg_banka, + .chip = { + .base = S3C2410_GPA(0), + .owner = THIS_MODULE, + .label = "GPIOA", + .ngpio = 24, + .direction_input = s3c24xx_gpiolib_banka_input, + .direction_output = s3c24xx_gpiolib_banka_output, + }, + }, { + .chip = { + .base = S3C2410_GPB(0), + .owner = THIS_MODULE, + .label = "GPIOB", + .ngpio = 16, + }, + }, { + .chip = { + .base = S3C2410_GPC(0), + .owner = THIS_MODULE, + .label = "GPIOC", + .ngpio = 16, + }, + }, { + .chip = { + .base = S3C2410_GPD(0), + .owner = THIS_MODULE, + .label = "GPIOD", + .ngpio = 16, + }, + }, { + .chip = { + .base = S3C2410_GPE(0), + .label = "GPIOE", + .owner = THIS_MODULE, + .ngpio = 16, + }, + }, { + .chip = { + .base = S3C2410_GPF(0), + .owner = THIS_MODULE, + .label = "GPIOF", + .ngpio = 8, + .to_irq = s3c24xx_gpiolib_fbank_to_irq, + }, + }, { + .irq_base = IRQ_EINT8, + .chip = { + .base = S3C2410_GPG(0), + .owner = THIS_MODULE, + .label = "GPIOG", + .ngpio = 16, + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .chip = { + .base = S3C2410_GPH(0), + .owner = THIS_MODULE, + .label = "GPIOH", + .ngpio = 11, + }, + }, + /* GPIOS for the S3C2443 and later devices. */ + { + .base = S3C2440_GPJCON, + .chip = { + .base = S3C2410_GPJ(0), + .owner = THIS_MODULE, + .label = "GPIOJ", + .ngpio = 16, + }, + }, { + .base = S3C2443_GPKCON, + .chip = { + .base = S3C2410_GPK(0), + .owner = THIS_MODULE, + .label = "GPIOK", + .ngpio = 16, + }, + }, { + .base = S3C2443_GPLCON, + .chip = { + .base = S3C2410_GPL(0), + .owner = THIS_MODULE, + .label = "GPIOL", + .ngpio = 15, + }, + }, { + .base = S3C2443_GPMCON, + .chip = { + .base = S3C2410_GPM(0), + .owner = THIS_MODULE, + .label = "GPIOM", + .ngpio = 2, + }, + }, +#endif +}; + +/* + * GPIO bank summary: + * + * Bank GPIOs Style SlpCon ExtInt Group + * A 8 4Bit Yes 1 + * B 7 4Bit Yes 1 + * C 8 4Bit Yes 2 + * D 5 4Bit Yes 3 + * E 5 4Bit Yes None + * F 16 2Bit Yes 4 [1] + * G 7 4Bit Yes 5 + * H 10 4Bit[2] Yes 6 + * I 16 2Bit Yes None + * J 12 2Bit Yes None + * K 16 4Bit[2] No None + * L 15 4Bit[2] No None + * M 6 4Bit No IRQ_EINT + * N 16 2Bit No IRQ_EINT + * O 16 2Bit Yes 7 + * P 15 2Bit Yes 8 + * Q 9 2Bit Yes 9 + * + * [1] BANKF pins 14,15 do not form part of the external interrupt sources + * [2] BANK has two control registers, GPxCON0 and GPxCON1 + */ + +static struct samsung_gpio_chip s3c64xx_gpios_4bit[] = { +#ifdef CONFIG_PLAT_S3C64XX + { + .chip = { + .base = S3C64XX_GPA(0), + .ngpio = S3C64XX_GPIO_A_NR, + .label = "GPA", + }, + }, { + .chip = { + .base = S3C64XX_GPB(0), + .ngpio = S3C64XX_GPIO_B_NR, + .label = "GPB", + }, + }, { + .chip = { + .base = S3C64XX_GPC(0), + .ngpio = S3C64XX_GPIO_C_NR, + .label = "GPC", + }, + }, { + .chip = { + .base = S3C64XX_GPD(0), + .ngpio = S3C64XX_GPIO_D_NR, + .label = "GPD", + }, + }, { + .config = &samsung_gpio_cfgs[0], + .chip = { + .base = S3C64XX_GPE(0), + .ngpio = S3C64XX_GPIO_E_NR, + .label = "GPE", + }, + }, { + .base = S3C64XX_GPG_BASE, + .chip = { + .base = S3C64XX_GPG(0), + .ngpio = S3C64XX_GPIO_G_NR, + .label = "GPG", + }, + }, { + .base = S3C64XX_GPM_BASE, + .config = &samsung_gpio_cfgs[1], + .chip = { + .base = S3C64XX_GPM(0), + .ngpio = S3C64XX_GPIO_M_NR, + .label = "GPM", + .to_irq = s3c64xx_gpiolib_mbank_to_irq, + }, + }, +#endif +}; + +static struct samsung_gpio_chip s3c64xx_gpios_4bit2[] = { +#ifdef CONFIG_PLAT_S3C64XX + { + .base = S3C64XX_GPH_BASE + 0x4, + .chip = { + .base = S3C64XX_GPH(0), + .ngpio = S3C64XX_GPIO_H_NR, + .label = "GPH", + }, + }, { + .base = S3C64XX_GPK_BASE + 0x4, + .config = &samsung_gpio_cfgs[0], + .chip = { + .base = S3C64XX_GPK(0), + .ngpio = S3C64XX_GPIO_K_NR, + .label = "GPK", + }, + }, { + .base = S3C64XX_GPL_BASE + 0x4, + .config = &samsung_gpio_cfgs[1], + .chip = { + .base = S3C64XX_GPL(0), + .ngpio = S3C64XX_GPIO_L_NR, + .label = "GPL", + .to_irq = s3c64xx_gpiolib_lbank_to_irq, + }, + }, +#endif +}; + +static struct samsung_gpio_chip s3c64xx_gpios_2bit[] = { +#ifdef CONFIG_PLAT_S3C64XX + { + .base = S3C64XX_GPF_BASE, + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S3C64XX_GPF(0), + .ngpio = S3C64XX_GPIO_F_NR, + .label = "GPF", + }, + }, { + .config = &samsung_gpio_cfgs[7], + .chip = { + .base = S3C64XX_GPI(0), + .ngpio = S3C64XX_GPIO_I_NR, + .label = "GPI", + }, + }, { + .config = &samsung_gpio_cfgs[7], + .chip = { + .base = S3C64XX_GPJ(0), + .ngpio = S3C64XX_GPIO_J_NR, + .label = "GPJ", + }, + }, { + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S3C64XX_GPO(0), + .ngpio = S3C64XX_GPIO_O_NR, + .label = "GPO", + }, + }, { + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S3C64XX_GPP(0), + .ngpio = S3C64XX_GPIO_P_NR, + .label = "GPP", + }, + }, { + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S3C64XX_GPQ(0), + .ngpio = S3C64XX_GPIO_Q_NR, + .label = "GPQ", + }, + }, { + .base = S3C64XX_GPN_BASE, + .irq_base = IRQ_EINT(0), + .config = &samsung_gpio_cfgs[5], + .chip = { + .base = S3C64XX_GPN(0), + .ngpio = S3C64XX_GPIO_N_NR, + .label = "GPN", + .to_irq = samsung_gpiolib_to_irq, + }, + }, +#endif +}; + +/* + * S5P6440 GPIO bank summary: + * + * Bank GPIOs Style SlpCon ExtInt Group + * A 6 4Bit Yes 1 + * B 7 4Bit Yes 1 + * C 8 4Bit Yes 2 + * F 2 2Bit Yes 4 [1] + * G 7 4Bit Yes 5 + * H 10 4Bit[2] Yes 6 + * I 16 2Bit Yes None + * J 12 2Bit Yes None + * N 16 2Bit No IRQ_EINT + * P 8 2Bit Yes 8 + * R 15 4Bit[2] Yes 8 + */ + +static struct samsung_gpio_chip s5p6440_gpios_4bit[] = { +#ifdef CONFIG_CPU_S5P6440 + { + .chip = { + .base = S5P6440_GPA(0), + .ngpio = S5P6440_GPIO_A_NR, + .label = "GPA", + }, + }, { + .chip = { + .base = S5P6440_GPB(0), + .ngpio = S5P6440_GPIO_B_NR, + .label = "GPB", + }, + }, { + .chip = { + .base = S5P6440_GPC(0), + .ngpio = S5P6440_GPIO_C_NR, + .label = "GPC", + }, + }, { + .base = S5P64X0_GPG_BASE, + .chip = { + .base = S5P6440_GPG(0), + .ngpio = S5P6440_GPIO_G_NR, + .label = "GPG", + }, + }, +#endif +}; + +static struct samsung_gpio_chip s5p6440_gpios_4bit2[] = { +#ifdef CONFIG_CPU_S5P6440 + { + .base = S5P64X0_GPH_BASE + 0x4, + .chip = { + .base = S5P6440_GPH(0), + .ngpio = S5P6440_GPIO_H_NR, + .label = "GPH", + }, + }, +#endif +}; + +static struct samsung_gpio_chip s5p6440_gpios_rbank[] = { +#ifdef CONFIG_CPU_S5P6440 + { + .base = S5P64X0_GPR_BASE + 0x4, + .config = &s5p64x0_gpio_cfg_rbank, + .chip = { + .base = S5P6440_GPR(0), + .ngpio = S5P6440_GPIO_R_NR, + .label = "GPR", + }, + }, +#endif +}; + +static struct samsung_gpio_chip s5p6440_gpios_2bit[] = { +#ifdef CONFIG_CPU_S5P6440 + { + .base = S5P64X0_GPF_BASE, + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S5P6440_GPF(0), + .ngpio = S5P6440_GPIO_F_NR, + .label = "GPF", + }, + }, { + .base = S5P64X0_GPI_BASE, + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = S5P6440_GPI(0), + .ngpio = S5P6440_GPIO_I_NR, + .label = "GPI", + }, + }, { + .base = S5P64X0_GPJ_BASE, + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = S5P6440_GPJ(0), + .ngpio = S5P6440_GPIO_J_NR, + .label = "GPJ", + }, + }, { + .base = S5P64X0_GPN_BASE, + .config = &samsung_gpio_cfgs[5], + .chip = { + .base = S5P6440_GPN(0), + .ngpio = S5P6440_GPIO_N_NR, + .label = "GPN", + }, + }, { + .base = S5P64X0_GPP_BASE, + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S5P6440_GPP(0), + .ngpio = S5P6440_GPIO_P_NR, + .label = "GPP", + }, + }, +#endif +}; + +/* + * S5P6450 GPIO bank summary: + * + * Bank GPIOs Style SlpCon ExtInt Group + * A 6 4Bit Yes 1 + * B 7 4Bit Yes 1 + * C 8 4Bit Yes 2 + * D 8 4Bit Yes None + * F 2 2Bit Yes None + * G 14 4Bit[2] Yes 5 + * H 10 4Bit[2] Yes 6 + * I 16 2Bit Yes None + * J 12 2Bit Yes None + * K 5 4Bit Yes None + * N 16 2Bit No IRQ_EINT + * P 11 2Bit Yes 8 + * Q 14 2Bit Yes None + * R 15 4Bit[2] Yes None + * S 8 2Bit Yes None + * + * [1] BANKF pins 14,15 do not form part of the external interrupt sources + * [2] BANK has two control registers, GPxCON0 and GPxCON1 + */ + +static struct samsung_gpio_chip s5p6450_gpios_4bit[] = { +#ifdef CONFIG_CPU_S5P6450 + { + .chip = { + .base = S5P6450_GPA(0), + .ngpio = S5P6450_GPIO_A_NR, + .label = "GPA", + }, + }, { + .chip = { + .base = S5P6450_GPB(0), + .ngpio = S5P6450_GPIO_B_NR, + .label = "GPB", + }, + }, { + .chip = { + .base = S5P6450_GPC(0), + .ngpio = S5P6450_GPIO_C_NR, + .label = "GPC", + }, + }, { + .chip = { + .base = S5P6450_GPD(0), + .ngpio = S5P6450_GPIO_D_NR, + .label = "GPD", + }, + }, { + .base = S5P6450_GPK_BASE, + .chip = { + .base = S5P6450_GPK(0), + .ngpio = S5P6450_GPIO_K_NR, + .label = "GPK", + }, + }, +#endif +}; + +static struct samsung_gpio_chip s5p6450_gpios_4bit2[] = { +#ifdef CONFIG_CPU_S5P6450 + { + .base = S5P64X0_GPG_BASE + 0x4, + .chip = { + .base = S5P6450_GPG(0), + .ngpio = S5P6450_GPIO_G_NR, + .label = "GPG", + }, + }, { + .base = S5P64X0_GPH_BASE + 0x4, + .chip = { + .base = S5P6450_GPH(0), + .ngpio = S5P6450_GPIO_H_NR, + .label = "GPH", + }, + }, +#endif +}; + +static struct samsung_gpio_chip s5p6450_gpios_rbank[] = { +#ifdef CONFIG_CPU_S5P6450 + { + .base = S5P64X0_GPR_BASE + 0x4, + .config = &s5p64x0_gpio_cfg_rbank, + .chip = { + .base = S5P6450_GPR(0), + .ngpio = S5P6450_GPIO_R_NR, + .label = "GPR", + }, + }, +#endif +}; + +static struct samsung_gpio_chip s5p6450_gpios_2bit[] = { +#ifdef CONFIG_CPU_S5P6450 + { + .base = S5P64X0_GPF_BASE, + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S5P6450_GPF(0), + .ngpio = S5P6450_GPIO_F_NR, + .label = "GPF", + }, + }, { + .base = S5P64X0_GPI_BASE, + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = S5P6450_GPI(0), + .ngpio = S5P6450_GPIO_I_NR, + .label = "GPI", + }, + }, { + .base = S5P64X0_GPJ_BASE, + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = S5P6450_GPJ(0), + .ngpio = S5P6450_GPIO_J_NR, + .label = "GPJ", + }, + }, { + .base = S5P64X0_GPN_BASE, + .config = &samsung_gpio_cfgs[5], + .chip = { + .base = S5P6450_GPN(0), + .ngpio = S5P6450_GPIO_N_NR, + .label = "GPN", + }, + }, { + .base = S5P64X0_GPP_BASE, + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S5P6450_GPP(0), + .ngpio = S5P6450_GPIO_P_NR, + .label = "GPP", + }, + }, { + .base = S5P6450_GPQ_BASE, + .config = &samsung_gpio_cfgs[5], + .chip = { + .base = S5P6450_GPQ(0), + .ngpio = S5P6450_GPIO_Q_NR, + .label = "GPQ", + }, + }, { + .base = S5P6450_GPS_BASE, + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S5P6450_GPS(0), + .ngpio = S5P6450_GPIO_S_NR, + .label = "GPS", + }, + }, +#endif +}; + +/* + * S5PC100 GPIO bank summary: + * + * Bank GPIOs Style INT Type + * A0 8 4Bit GPIO_INT0 + * A1 5 4Bit GPIO_INT1 + * B 8 4Bit GPIO_INT2 + * C 5 4Bit GPIO_INT3 + * D 7 4Bit GPIO_INT4 + * E0 8 4Bit GPIO_INT5 + * E1 6 4Bit GPIO_INT6 + * F0 8 4Bit GPIO_INT7 + * F1 8 4Bit GPIO_INT8 + * F2 8 4Bit GPIO_INT9 + * F3 4 4Bit GPIO_INT10 + * G0 8 4Bit GPIO_INT11 + * G1 3 4Bit GPIO_INT12 + * G2 7 4Bit GPIO_INT13 + * G3 7 4Bit GPIO_INT14 + * H0 8 4Bit WKUP_INT + * H1 8 4Bit WKUP_INT + * H2 8 4Bit WKUP_INT + * H3 8 4Bit WKUP_INT + * I 8 4Bit GPIO_INT15 + * J0 8 4Bit GPIO_INT16 + * J1 5 4Bit GPIO_INT17 + * J2 8 4Bit GPIO_INT18 + * J3 8 4Bit GPIO_INT19 + * J4 4 4Bit GPIO_INT20 + * K0 8 4Bit None + * K1 6 4Bit None + * K2 8 4Bit None + * K3 8 4Bit None + * L0 8 4Bit None + * L1 8 4Bit None + * L2 8 4Bit None + * L3 8 4Bit None + */ + +static struct samsung_gpio_chip s5pc100_gpios_4bit[] = { +#ifdef CONFIG_CPU_S5PC100 + { + .chip = { + .base = S5PC100_GPA0(0), + .ngpio = S5PC100_GPIO_A0_NR, + .label = "GPA0", + }, + }, { + .chip = { + .base = S5PC100_GPA1(0), + .ngpio = S5PC100_GPIO_A1_NR, + .label = "GPA1", + }, + }, { + .chip = { + .base = S5PC100_GPB(0), + .ngpio = S5PC100_GPIO_B_NR, + .label = "GPB", + }, + }, { + .chip = { + .base = S5PC100_GPC(0), + .ngpio = S5PC100_GPIO_C_NR, + .label = "GPC", + }, + }, { + .chip = { + .base = S5PC100_GPD(0), + .ngpio = S5PC100_GPIO_D_NR, + .label = "GPD", + }, + }, { + .chip = { + .base = S5PC100_GPE0(0), + .ngpio = S5PC100_GPIO_E0_NR, + .label = "GPE0", + }, + }, { + .chip = { + .base = S5PC100_GPE1(0), + .ngpio = S5PC100_GPIO_E1_NR, + .label = "GPE1", + }, + }, { + .chip = { + .base = S5PC100_GPF0(0), + .ngpio = S5PC100_GPIO_F0_NR, + .label = "GPF0", + }, + }, { + .chip = { + .base = S5PC100_GPF1(0), + .ngpio = S5PC100_GPIO_F1_NR, + .label = "GPF1", + }, + }, { + .chip = { + .base = S5PC100_GPF2(0), + .ngpio = S5PC100_GPIO_F2_NR, + .label = "GPF2", + }, + }, { + .chip = { + .base = S5PC100_GPF3(0), + .ngpio = S5PC100_GPIO_F3_NR, + .label = "GPF3", + }, + }, { + .chip = { + .base = S5PC100_GPG0(0), + .ngpio = S5PC100_GPIO_G0_NR, + .label = "GPG0", + }, + }, { + .chip = { + .base = S5PC100_GPG1(0), + .ngpio = S5PC100_GPIO_G1_NR, + .label = "GPG1", + }, + }, { + .chip = { + .base = S5PC100_GPG2(0), + .ngpio = S5PC100_GPIO_G2_NR, + .label = "GPG2", + }, + }, { + .chip = { + .base = S5PC100_GPG3(0), + .ngpio = S5PC100_GPIO_G3_NR, + .label = "GPG3", + }, + }, { + .chip = { + .base = S5PC100_GPI(0), + .ngpio = S5PC100_GPIO_I_NR, + .label = "GPI", + }, + }, { + .chip = { + .base = S5PC100_GPJ0(0), + .ngpio = S5PC100_GPIO_J0_NR, + .label = "GPJ0", + }, + }, { + .chip = { + .base = S5PC100_GPJ1(0), + .ngpio = S5PC100_GPIO_J1_NR, + .label = "GPJ1", + }, + }, { + .chip = { + .base = S5PC100_GPJ2(0), + .ngpio = S5PC100_GPIO_J2_NR, + .label = "GPJ2", + }, + }, { + .chip = { + .base = S5PC100_GPJ3(0), + .ngpio = S5PC100_GPIO_J3_NR, + .label = "GPJ3", + }, + }, { + .chip = { + .base = S5PC100_GPJ4(0), + .ngpio = S5PC100_GPIO_J4_NR, + .label = "GPJ4", + }, + }, { + .chip = { + .base = S5PC100_GPK0(0), + .ngpio = S5PC100_GPIO_K0_NR, + .label = "GPK0", + }, + }, { + .chip = { + .base = S5PC100_GPK1(0), + .ngpio = S5PC100_GPIO_K1_NR, + .label = "GPK1", + }, + }, { + .chip = { + .base = S5PC100_GPK2(0), + .ngpio = S5PC100_GPIO_K2_NR, + .label = "GPK2", + }, + }, { + .chip = { + .base = S5PC100_GPK3(0), + .ngpio = S5PC100_GPIO_K3_NR, + .label = "GPK3", + }, + }, { + .chip = { + .base = S5PC100_GPL0(0), + .ngpio = S5PC100_GPIO_L0_NR, + .label = "GPL0", + }, + }, { + .chip = { + .base = S5PC100_GPL1(0), + .ngpio = S5PC100_GPIO_L1_NR, + .label = "GPL1", + }, + }, { + .chip = { + .base = S5PC100_GPL2(0), + .ngpio = S5PC100_GPIO_L2_NR, + .label = "GPL2", + }, + }, { + .chip = { + .base = S5PC100_GPL3(0), + .ngpio = S5PC100_GPIO_L3_NR, + .label = "GPL3", + }, + }, { + .chip = { + .base = S5PC100_GPL4(0), + .ngpio = S5PC100_GPIO_L4_NR, + .label = "GPL4", + }, + }, { + .base = (S5P_VA_GPIO + 0xC00), + .irq_base = IRQ_EINT(0), + .chip = { + .base = S5PC100_GPH0(0), + .ngpio = S5PC100_GPIO_H0_NR, + .label = "GPH0", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO + 0xC20), + .irq_base = IRQ_EINT(8), + .chip = { + .base = S5PC100_GPH1(0), + .ngpio = S5PC100_GPIO_H1_NR, + .label = "GPH1", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO + 0xC40), + .irq_base = IRQ_EINT(16), + .chip = { + .base = S5PC100_GPH2(0), + .ngpio = S5PC100_GPIO_H2_NR, + .label = "GPH2", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO + 0xC60), + .irq_base = IRQ_EINT(24), + .chip = { + .base = S5PC100_GPH3(0), + .ngpio = S5PC100_GPIO_H3_NR, + .label = "GPH3", + .to_irq = samsung_gpiolib_to_irq, + }, + }, +#endif +}; + +/* + * Followings are the gpio banks in S5PV210/S5PC110 + * + * The 'config' member when left to NULL, is initialized to the default + * structure samsung_gpio_cfgs[4] in the init function below. + * + * The 'base' member is also initialized in the init function below. + * Note: The initialization of 'base' member of samsung_gpio_chip structure + * uses the above macro and depends on the banks being listed in order here. + */ + +static struct samsung_gpio_chip s5pv210_gpios_4bit[] = { +#ifdef CONFIG_CPU_S5PV210 + { + .chip = { + .base = S5PV210_GPA0(0), + .ngpio = S5PV210_GPIO_A0_NR, + .label = "GPA0", + }, + }, { + .chip = { + .base = S5PV210_GPA1(0), + .ngpio = S5PV210_GPIO_A1_NR, + .label = "GPA1", + }, + }, { + .chip = { + .base = S5PV210_GPB(0), + .ngpio = S5PV210_GPIO_B_NR, + .label = "GPB", + }, + }, { + .chip = { + .base = S5PV210_GPC0(0), + .ngpio = S5PV210_GPIO_C0_NR, + .label = "GPC0", + }, + }, { + .chip = { + .base = S5PV210_GPC1(0), + .ngpio = S5PV210_GPIO_C1_NR, + .label = "GPC1", + }, + }, { + .chip = { + .base = S5PV210_GPD0(0), + .ngpio = S5PV210_GPIO_D0_NR, + .label = "GPD0", + }, + }, { + .chip = { + .base = S5PV210_GPD1(0), + .ngpio = S5PV210_GPIO_D1_NR, + .label = "GPD1", + }, + }, { + .chip = { + .base = S5PV210_GPE0(0), + .ngpio = S5PV210_GPIO_E0_NR, + .label = "GPE0", + }, + }, { + .chip = { + .base = S5PV210_GPE1(0), + .ngpio = S5PV210_GPIO_E1_NR, + .label = "GPE1", + }, + }, { + .chip = { + .base = S5PV210_GPF0(0), + .ngpio = S5PV210_GPIO_F0_NR, + .label = "GPF0", + }, + }, { + .chip = { + .base = S5PV210_GPF1(0), + .ngpio = S5PV210_GPIO_F1_NR, + .label = "GPF1", + }, + }, { + .chip = { + .base = S5PV210_GPF2(0), + .ngpio = S5PV210_GPIO_F2_NR, + .label = "GPF2", + }, + }, { + .chip = { + .base = S5PV210_GPF3(0), + .ngpio = S5PV210_GPIO_F3_NR, + .label = "GPF3", + }, + }, { + .chip = { + .base = S5PV210_GPG0(0), + .ngpio = S5PV210_GPIO_G0_NR, + .label = "GPG0", + }, + }, { + .chip = { + .base = S5PV210_GPG1(0), + .ngpio = S5PV210_GPIO_G1_NR, + .label = "GPG1", + }, + }, { + .chip = { + .base = S5PV210_GPG2(0), + .ngpio = S5PV210_GPIO_G2_NR, + .label = "GPG2", + }, + }, { + .chip = { + .base = S5PV210_GPG3(0), + .ngpio = S5PV210_GPIO_G3_NR, + .label = "GPG3", + }, + }, { + .chip = { + .base = S5PV210_GPI(0), + .ngpio = S5PV210_GPIO_I_NR, + .label = "GPI", + }, + }, { + .chip = { + .base = S5PV210_GPJ0(0), + .ngpio = S5PV210_GPIO_J0_NR, + .label = "GPJ0", + }, + }, { + .chip = { + .base = S5PV210_GPJ1(0), + .ngpio = S5PV210_GPIO_J1_NR, + .label = "GPJ1", + }, + }, { + .chip = { + .base = S5PV210_GPJ2(0), + .ngpio = S5PV210_GPIO_J2_NR, + .label = "GPJ2", + }, + }, { + .chip = { + .base = S5PV210_GPJ3(0), + .ngpio = S5PV210_GPIO_J3_NR, + .label = "GPJ3", + }, + }, { + .chip = { + .base = S5PV210_GPJ4(0), + .ngpio = S5PV210_GPIO_J4_NR, + .label = "GPJ4", + }, + }, { + .chip = { + .base = S5PV210_MP01(0), + .ngpio = S5PV210_GPIO_MP01_NR, + .label = "MP01", + }, + }, { + .chip = { + .base = S5PV210_MP02(0), + .ngpio = S5PV210_GPIO_MP02_NR, + .label = "MP02", + }, + }, { + .chip = { + .base = S5PV210_MP03(0), + .ngpio = S5PV210_GPIO_MP03_NR, + .label = "MP03", + }, + }, { + .chip = { + .base = S5PV210_MP04(0), + .ngpio = S5PV210_GPIO_MP04_NR, + .label = "MP04", + }, + }, { + .chip = { + .base = S5PV210_MP05(0), + .ngpio = S5PV210_GPIO_MP05_NR, + .label = "MP05", + }, + }, { + .base = (S5P_VA_GPIO + 0xC00), + .irq_base = IRQ_EINT(0), + .chip = { + .base = S5PV210_GPH0(0), + .ngpio = S5PV210_GPIO_H0_NR, + .label = "GPH0", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO + 0xC20), + .irq_base = IRQ_EINT(8), + .chip = { + .base = S5PV210_GPH1(0), + .ngpio = S5PV210_GPIO_H1_NR, + .label = "GPH1", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO + 0xC40), + .irq_base = IRQ_EINT(16), + .chip = { + .base = S5PV210_GPH2(0), + .ngpio = S5PV210_GPIO_H2_NR, + .label = "GPH2", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO + 0xC60), + .irq_base = IRQ_EINT(24), + .chip = { + .base = S5PV210_GPH3(0), + .ngpio = S5PV210_GPIO_H3_NR, + .label = "GPH3", + .to_irq = samsung_gpiolib_to_irq, + }, + }, +#endif +}; + +/* + * Followings are the gpio banks in EXYNOS4210 + * + * The 'config' member when left to NULL, is initialized to the default + * structure samsung_gpio_cfgs[4] in the init function below. + * + * The 'base' member is also initialized in the init function below. + * Note: The initialization of 'base' member of samsung_gpio_chip structure + * uses the above macro and depends on the banks being listed in order here. + */ + +static struct samsung_gpio_chip exynos4_gpios_1[] = { +#ifdef CONFIG_ARCH_EXYNOS4 + { + .chip = { + .base = EXYNOS4_GPA0(0), + .ngpio = EXYNOS4_GPIO_A0_NR, + .label = "GPA0", + }, + }, { + .chip = { + .base = EXYNOS4_GPA1(0), + .ngpio = EXYNOS4_GPIO_A1_NR, + .label = "GPA1", + }, + }, { + .chip = { + .base = EXYNOS4_GPB(0), + .ngpio = EXYNOS4_GPIO_B_NR, + .label = "GPB", + }, + }, { + .chip = { + .base = EXYNOS4_GPC0(0), + .ngpio = EXYNOS4_GPIO_C0_NR, + .label = "GPC0", + }, + }, { + .chip = { + .base = EXYNOS4_GPC1(0), + .ngpio = EXYNOS4_GPIO_C1_NR, + .label = "GPC1", + }, + }, { + .chip = { + .base = EXYNOS4_GPD0(0), + .ngpio = EXYNOS4_GPIO_D0_NR, + .label = "GPD0", + }, + }, { + .chip = { + .base = EXYNOS4_GPD1(0), + .ngpio = EXYNOS4_GPIO_D1_NR, + .label = "GPD1", + }, + }, { + .chip = { + .base = EXYNOS4_GPE0(0), + .ngpio = EXYNOS4_GPIO_E0_NR, + .label = "GPE0", + }, + }, { + .chip = { + .base = EXYNOS4_GPE1(0), + .ngpio = EXYNOS4_GPIO_E1_NR, + .label = "GPE1", + }, + }, { + .chip = { + .base = EXYNOS4_GPE2(0), + .ngpio = EXYNOS4_GPIO_E2_NR, + .label = "GPE2", + }, + }, { + .chip = { + .base = EXYNOS4_GPE3(0), + .ngpio = EXYNOS4_GPIO_E3_NR, + .label = "GPE3", + }, + }, { + .chip = { + .base = EXYNOS4_GPE4(0), + .ngpio = EXYNOS4_GPIO_E4_NR, + .label = "GPE4", + }, + }, { + .chip = { + .base = EXYNOS4_GPF0(0), + .ngpio = EXYNOS4_GPIO_F0_NR, + .label = "GPF0", + }, + }, { + .chip = { + .base = EXYNOS4_GPF1(0), + .ngpio = EXYNOS4_GPIO_F1_NR, + .label = "GPF1", + }, + }, { + .chip = { + .base = EXYNOS4_GPF2(0), + .ngpio = EXYNOS4_GPIO_F2_NR, + .label = "GPF2", + }, + }, { + .chip = { + .base = EXYNOS4_GPF3(0), + .ngpio = EXYNOS4_GPIO_F3_NR, + .label = "GPF3", + }, + }, +#endif +}; + +static struct samsung_gpio_chip exynos4_gpios_2[] = { +#ifdef CONFIG_ARCH_EXYNOS4 + { + .chip = { + .base = EXYNOS4_GPJ0(0), + .ngpio = EXYNOS4_GPIO_J0_NR, + .label = "GPJ0", + }, + }, { + .chip = { + .base = EXYNOS4_GPJ1(0), + .ngpio = EXYNOS4_GPIO_J1_NR, + .label = "GPJ1", + }, + }, { + .chip = { + .base = EXYNOS4_GPK0(0), + .ngpio = EXYNOS4_GPIO_K0_NR, + .label = "GPK0", + }, + }, { + .chip = { + .base = EXYNOS4_GPK1(0), + .ngpio = EXYNOS4_GPIO_K1_NR, + .label = "GPK1", + }, + }, { + .chip = { + .base = EXYNOS4_GPK2(0), + .ngpio = EXYNOS4_GPIO_K2_NR, + .label = "GPK2", + }, + }, { + .chip = { + .base = EXYNOS4_GPK3(0), + .ngpio = EXYNOS4_GPIO_K3_NR, + .label = "GPK3", + }, + }, { + .chip = { + .base = EXYNOS4_GPL0(0), + .ngpio = EXYNOS4_GPIO_L0_NR, + .label = "GPL0", + }, + }, { + .chip = { + .base = EXYNOS4_GPL1(0), + .ngpio = EXYNOS4_GPIO_L1_NR, + .label = "GPL1", + }, + }, { + .chip = { + .base = EXYNOS4_GPL2(0), + .ngpio = EXYNOS4_GPIO_L2_NR, + .label = "GPL2", + }, + }, { + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = EXYNOS4_GPY0(0), + .ngpio = EXYNOS4_GPIO_Y0_NR, + .label = "GPY0", + }, + }, { + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = EXYNOS4_GPY1(0), + .ngpio = EXYNOS4_GPIO_Y1_NR, + .label = "GPY1", + }, + }, { + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = EXYNOS4_GPY2(0), + .ngpio = EXYNOS4_GPIO_Y2_NR, + .label = "GPY2", + }, + }, { + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = EXYNOS4_GPY3(0), + .ngpio = EXYNOS4_GPIO_Y3_NR, + .label = "GPY3", + }, + }, { + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = EXYNOS4_GPY4(0), + .ngpio = EXYNOS4_GPIO_Y4_NR, + .label = "GPY4", + }, + }, { + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = EXYNOS4_GPY5(0), + .ngpio = EXYNOS4_GPIO_Y5_NR, + .label = "GPY5", + }, + }, { + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = EXYNOS4_GPY6(0), + .ngpio = EXYNOS4_GPIO_Y6_NR, + .label = "GPY6", + }, + }, { + .base = (S5P_VA_GPIO2 + 0xC00), + .config = &samsung_gpio_cfgs[4], + .irq_base = IRQ_EINT(0), + .chip = { + .base = EXYNOS4_GPX0(0), + .ngpio = EXYNOS4_GPIO_X0_NR, + .label = "GPX0", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO2 + 0xC20), + .config = &samsung_gpio_cfgs[4], + .irq_base = IRQ_EINT(8), + .chip = { + .base = EXYNOS4_GPX1(0), + .ngpio = EXYNOS4_GPIO_X1_NR, + .label = "GPX1", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO2 + 0xC40), + .config = &samsung_gpio_cfgs[4], + .irq_base = IRQ_EINT(16), + .chip = { + .base = EXYNOS4_GPX2(0), + .ngpio = EXYNOS4_GPIO_X2_NR, + .label = "GPX2", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO2 + 0xC60), + .config = &samsung_gpio_cfgs[4], + .irq_base = IRQ_EINT(24), + .chip = { + .base = EXYNOS4_GPX3(0), + .ngpio = EXYNOS4_GPIO_X3_NR, + .label = "GPX3", + .to_irq = samsung_gpiolib_to_irq, + }, + }, +#endif +}; + +static struct samsung_gpio_chip exynos4_gpios_3[] = { +#ifdef CONFIG_ARCH_EXYNOS4 + { + .chip = { + .base = EXYNOS4_GPZ(0), + .ngpio = EXYNOS4_GPIO_Z_NR, + .label = "GPZ", + }, + }, +#endif +}; + +/* TODO: cleanup soc_is_* */ +static __init int samsung_gpiolib_init(void) +{ + struct samsung_gpio_chip *chip; + int i, nr_chips; + int group = 0; + + samsung_gpiolib_set_cfg(samsung_gpio_cfgs, ARRAY_SIZE(samsung_gpio_cfgs)); + + if (soc_is_s3c24xx()) { + s3c24xx_gpiolib_add_chips(s3c24xx_gpios, + ARRAY_SIZE(s3c24xx_gpios), S3C24XX_VA_GPIO); + } else if (soc_is_s3c64xx()) { + samsung_gpiolib_add_2bit_chips(s3c64xx_gpios_2bit, + ARRAY_SIZE(s3c64xx_gpios_2bit), + S3C64XX_VA_GPIO + 0xE0, 0x20); + samsung_gpiolib_add_4bit_chips(s3c64xx_gpios_4bit, + ARRAY_SIZE(s3c64xx_gpios_4bit), + S3C64XX_VA_GPIO); + samsung_gpiolib_add_4bit2_chips(s3c64xx_gpios_4bit2, + ARRAY_SIZE(s3c64xx_gpios_4bit2)); + } else if (soc_is_s5p6440()) { + samsung_gpiolib_add_2bit_chips(s5p6440_gpios_2bit, + ARRAY_SIZE(s5p6440_gpios_2bit), NULL, 0x0); + samsung_gpiolib_add_4bit_chips(s5p6440_gpios_4bit, + ARRAY_SIZE(s5p6440_gpios_4bit), S5P_VA_GPIO); + samsung_gpiolib_add_4bit2_chips(s5p6440_gpios_4bit2, + ARRAY_SIZE(s5p6440_gpios_4bit2)); + s5p64x0_gpiolib_add_rbank(s5p6440_gpios_rbank, + ARRAY_SIZE(s5p6440_gpios_rbank)); + } else if (soc_is_s5p6450()) { + samsung_gpiolib_add_2bit_chips(s5p6450_gpios_2bit, + ARRAY_SIZE(s5p6450_gpios_2bit), NULL, 0x0); + samsung_gpiolib_add_4bit_chips(s5p6450_gpios_4bit, + ARRAY_SIZE(s5p6450_gpios_4bit), S5P_VA_GPIO); + samsung_gpiolib_add_4bit2_chips(s5p6450_gpios_4bit2, + ARRAY_SIZE(s5p6450_gpios_4bit2)); + s5p64x0_gpiolib_add_rbank(s5p6450_gpios_rbank, + ARRAY_SIZE(s5p6450_gpios_rbank)); + } else if (soc_is_s5pc100()) { + group = 0; + chip = s5pc100_gpios_4bit; + nr_chips = ARRAY_SIZE(s5pc100_gpios_4bit); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &samsung_gpio_cfgs[4]; + chip->group = group++; + } + } + samsung_gpiolib_add_4bit_chips(s5pc100_gpios_4bit, nr_chips, S5P_VA_GPIO); +#if defined(CONFIG_CPU_S5PC100) && defined(CONFIG_S5P_GPIO_INT) + s5p_register_gpioint_bank(IRQ_GPIOINT, 0, S5P_GPIOINT_GROUP_MAXNR); +#endif + } else if (soc_is_s5pv210()) { + group = 0; + chip = s5pv210_gpios_4bit; + nr_chips = ARRAY_SIZE(s5pv210_gpios_4bit); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &samsung_gpio_cfgs[4]; + chip->group = group++; + } + } + samsung_gpiolib_add_4bit_chips(s5pv210_gpios_4bit, nr_chips, S5P_VA_GPIO); +#if defined(CONFIG_CPU_S5PV210) && defined(CONFIG_S5P_GPIO_INT) + s5p_register_gpioint_bank(IRQ_GPIOINT, 0, S5P_GPIOINT_GROUP_MAXNR); +#endif + } else if (soc_is_exynos4210()) { + group = 0; + + /* gpio part1 */ + chip = exynos4_gpios_1; + nr_chips = ARRAY_SIZE(exynos4_gpios_1); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &exynos4_gpio_cfg; + chip->group = group++; + } + } + samsung_gpiolib_add_4bit_chips(exynos4_gpios_1, nr_chips, S5P_VA_GPIO1); + + /* gpio part2 */ + chip = exynos4_gpios_2; + nr_chips = ARRAY_SIZE(exynos4_gpios_2); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &exynos4_gpio_cfg; + chip->group = group++; + } + } + samsung_gpiolib_add_4bit_chips(exynos4_gpios_2, nr_chips, S5P_VA_GPIO2); + + /* gpio part3 */ + chip = exynos4_gpios_3; + nr_chips = ARRAY_SIZE(exynos4_gpios_3); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &exynos4_gpio_cfg; + chip->group = group++; + } + } + samsung_gpiolib_add_4bit_chips(exynos4_gpios_3, nr_chips, S5P_VA_GPIO3); + +#if defined(CONFIG_SOC_EXYNOS4210) && defined(CONFIG_S5P_GPIO_INT) + s5p_register_gpioint_bank(IRQ_GPIO_XA, 0, IRQ_GPIO1_NR_GROUPS); + s5p_register_gpioint_bank(IRQ_GPIO_XB, IRQ_GPIO1_NR_GROUPS, IRQ_GPIO2_NR_GROUPS); +#endif + } + + return 0; +} +core_initcall(samsung_gpiolib_init); + +int s3c_gpio_cfgpin(unsigned int pin, unsigned int config) +{ + struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); + unsigned long flags; + int offset; + int ret; + + if (!chip) + return -EINVAL; + + offset = pin - chip->chip.base; + + samsung_gpio_lock(chip, flags); + ret = samsung_gpio_do_setcfg(chip, offset, config); + samsung_gpio_unlock(chip, flags); + + return ret; +} +EXPORT_SYMBOL(s3c_gpio_cfgpin); + +int s3c_gpio_cfgpin_range(unsigned int start, unsigned int nr, + unsigned int cfg) +{ + int ret; + + for (; nr > 0; nr--, start++) { + ret = s3c_gpio_cfgpin(start, cfg); + if (ret != 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(s3c_gpio_cfgpin_range); + +int s3c_gpio_cfgall_range(unsigned int start, unsigned int nr, + unsigned int cfg, samsung_gpio_pull_t pull) +{ + int ret; + + for (; nr > 0; nr--, start++) { + s3c_gpio_setpull(start, pull); + ret = s3c_gpio_cfgpin(start, cfg); + if (ret != 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(s3c_gpio_cfgall_range); + +unsigned s3c_gpio_getcfg(unsigned int pin) +{ + struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); + unsigned long flags; + unsigned ret = 0; + int offset; + + if (chip) { + offset = pin - chip->chip.base; + + samsung_gpio_lock(chip, flags); + ret = samsung_gpio_do_getcfg(chip, offset); + samsung_gpio_unlock(chip, flags); + } + + return ret; +} +EXPORT_SYMBOL(s3c_gpio_getcfg); + +int s3c_gpio_setpull(unsigned int pin, samsung_gpio_pull_t pull) +{ + struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); + unsigned long flags; + int offset, ret; + + if (!chip) + return -EINVAL; + + offset = pin - chip->chip.base; + + samsung_gpio_lock(chip, flags); + ret = samsung_gpio_do_setpull(chip, offset, pull); + samsung_gpio_unlock(chip, flags); + + return ret; +} +EXPORT_SYMBOL(s3c_gpio_setpull); + +samsung_gpio_pull_t s3c_gpio_getpull(unsigned int pin) +{ + struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); + unsigned long flags; + int offset; + u32 pup = 0; + + if (chip) { + offset = pin - chip->chip.base; + + samsung_gpio_lock(chip, flags); + pup = samsung_gpio_do_getpull(chip, offset); + samsung_gpio_unlock(chip, flags); + } + + return (__force samsung_gpio_pull_t)pup; +} +EXPORT_SYMBOL(s3c_gpio_getpull); + +/* gpiolib wrappers until these are totally eliminated */ + +void s3c2410_gpio_pullup(unsigned int pin, unsigned int to) +{ + int ret; + + WARN_ON(to); /* should be none of these left */ + + if (!to) { + /* if pull is enabled, try first with up, and if that + * fails, try using down */ + + ret = s3c_gpio_setpull(pin, S3C_GPIO_PULL_UP); + if (ret) + s3c_gpio_setpull(pin, S3C_GPIO_PULL_DOWN); + } else { + s3c_gpio_setpull(pin, S3C_GPIO_PULL_NONE); + } +} +EXPORT_SYMBOL(s3c2410_gpio_pullup); + +void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) +{ + /* do this via gpiolib until all users removed */ + + gpio_request(pin, "temporary"); + gpio_set_value(pin, to); + gpio_free(pin); +} +EXPORT_SYMBOL(s3c2410_gpio_setpin); + +unsigned int s3c2410_gpio_getpin(unsigned int pin) +{ + struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); + unsigned long offs = pin - chip->chip.base; + + return __raw_readl(chip->base + 0x04) & (1 << offs); +} +EXPORT_SYMBOL(s3c2410_gpio_getpin); + +#ifdef CONFIG_S5P_GPIO_DRVSTR +s5p_gpio_drvstr_t s5p_gpio_get_drvstr(unsigned int pin) +{ + struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); + unsigned int off; + void __iomem *reg; + int shift; + u32 drvstr; + + if (!chip) + return -EINVAL; + + off = pin - chip->chip.base; + shift = off * 2; + reg = chip->base + 0x0C; + + drvstr = __raw_readl(reg); + drvstr = drvstr >> shift; + drvstr &= 0x3; + + return (__force s5p_gpio_drvstr_t)drvstr; +} +EXPORT_SYMBOL(s5p_gpio_get_drvstr); + +int s5p_gpio_set_drvstr(unsigned int pin, s5p_gpio_drvstr_t drvstr) +{ + struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); + unsigned int off; + void __iomem *reg; + int shift; + u32 tmp; + + if (!chip) + return -EINVAL; + + off = pin - chip->chip.base; + shift = off * 2; + reg = chip->base + 0x0C; + + tmp = __raw_readl(reg); + tmp &= ~(0x3 << shift); + tmp |= drvstr << shift; + + __raw_writel(tmp, reg); + + return 0; +} +EXPORT_SYMBOL(s5p_gpio_set_drvstr); +#endif /* CONFIG_S5P_GPIO_DRVSTR */ + +#ifdef CONFIG_PLAT_S3C24XX +unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change) +{ + unsigned long flags; + unsigned long misccr; + + local_irq_save(flags); + misccr = __raw_readl(S3C24XX_MISCCR); + misccr &= ~clear; + misccr ^= change; + __raw_writel(misccr, S3C24XX_MISCCR); + local_irq_restore(flags); + + return misccr; +} +EXPORT_SYMBOL(s3c2410_modify_misccr); +#endif -- cgit v1.2.2 From 0e44b6eccfcb0b2da65b0e9eddd5d8b4eac5d8df Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 21 Sep 2011 21:24:04 +0800 Subject: gpio/mxc: add chained_irq_enter/exit() to mx3_gpio_irq_handler() The mx3_gpio_irq_handler() is also called on imx6q which has GIC as the primary interrupt controller. As GIC implements the fasteoi flow control, we need to add chained_irq_enter/exit() to mx3_gpio_irq_handler() for signaling EOI, otherwise system will hang whenever there is a gpio irq triggered. v2: use chained_irq_{enter,exit}() Signed-off-by: Shawn Guo Signed-off-by: Grant Likely --- drivers/gpio/gpio-mxc.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 4340acae3bd3..82f7b65baf72 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -30,6 +30,7 @@ #include #include #include +#include enum mxc_gpio_hwtype { IMX1_GPIO, /* runs on i.mx1 */ @@ -232,10 +233,15 @@ static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc) { u32 irq_stat; struct mxc_gpio_port *port = irq_get_handler_data(irq); + struct irq_chip *chip = irq_get_chip(irq); + + chained_irq_enter(chip, desc); irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR); mxc_gpio_irq_handler(port, irq_stat); + + chained_irq_exit(chip, desc); } /* MX2 has one interrupt *for all* gpio ports */ -- cgit v1.2.2 From c68308dd50c3827a4ce77a1d70e0eb2d2521cafd Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 21 Sep 2011 12:49:20 +0200 Subject: gpio: move mpc8xxx/512x gpio driver to drivers/gpio Move the driver to the place where it is expected to be nowadays. Also rename its CONFIG-name to match the rest and adapt the defconfigs. Finally, move selection of REQUIRE_GPIOLIB or WANTS_OPTIONAL_GPIOLIB to the platforms, because this option is per-platform and not per-driver. Signed-off-by: Wolfram Sang Cc: Anatolij Gustschin Cc: Grant Likely Cc: Benjamin Herrenschmidt Acked-by: Grant Likely Signed-off-by: Anatolij Gustschin --- drivers/gpio/Kconfig | 8 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-mpc8xxx.c | 395 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 404 insertions(+) create mode 100644 drivers/gpio/gpio-mpc8xxx.c (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d539efd96d4b..4744cf246d4a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -103,6 +103,14 @@ config GPIO_MPC5200 def_bool y depends on PPC_MPC52xx +config GPIO_MPC8XXX + bool "MPC512x/MPC8xxx GPIO support" + depends on PPC_MPC512x || PPC_MPC831x || PPC_MPC834x || PPC_MPC837x || \ + FSL_SOC_BOOKE || PPC_86xx + help + Say Y here if you're going to use hardware that connects to the + MPC512x/831x/834x/837x/8572/8610 GPIOs. + config GPIO_MSM_V1 tristate "Qualcomm MSM GPIO v1" depends on GPIOLIB && ARCH_MSM diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9588948c96f0..828bba9883c3 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o obj-$(CONFIG_GPIO_MCP23S08) += gpio-mcp23s08.o obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o +obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o obj-$(CONFIG_GPIO_MSM_V1) += gpio-msm-v1.o obj-$(CONFIG_GPIO_MSM_V2) += gpio-msm-v2.o obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c new file mode 100644 index 000000000000..fb4963abdf55 --- /dev/null +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -0,0 +1,395 @@ +/* + * GPIOs on MPC512x/8349/8572/8610 and compatible + * + * Copyright (C) 2008 Peter Korsgaard + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MPC8XXX_GPIO_PINS 32 + +#define GPIO_DIR 0x00 +#define GPIO_ODR 0x04 +#define GPIO_DAT 0x08 +#define GPIO_IER 0x0c +#define GPIO_IMR 0x10 +#define GPIO_ICR 0x14 +#define GPIO_ICR2 0x18 + +struct mpc8xxx_gpio_chip { + struct of_mm_gpio_chip mm_gc; + spinlock_t lock; + + /* + * shadowed data register to be able to clear/set output pins in + * open drain mode safely + */ + u32 data; + struct irq_host *irq; + void *of_dev_id_data; +}; + +static inline u32 mpc8xxx_gpio2mask(unsigned int gpio) +{ + return 1u << (MPC8XXX_GPIO_PINS - 1 - gpio); +} + +static inline struct mpc8xxx_gpio_chip * +to_mpc8xxx_gpio_chip(struct of_mm_gpio_chip *mm) +{ + return container_of(mm, struct mpc8xxx_gpio_chip, mm_gc); +} + +static void mpc8xxx_gpio_save_regs(struct of_mm_gpio_chip *mm) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); + + mpc8xxx_gc->data = in_be32(mm->regs + GPIO_DAT); +} + +/* Workaround GPIO 1 errata on MPC8572/MPC8536. The status of GPIOs + * defined as output cannot be determined by reading GPDAT register, + * so we use shadow data register instead. The status of input pins + * is determined by reading GPDAT register. + */ +static int mpc8572_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + u32 val; + struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); + struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); + + val = in_be32(mm->regs + GPIO_DAT) & ~in_be32(mm->regs + GPIO_DIR); + + return (val | mpc8xxx_gc->data) & mpc8xxx_gpio2mask(gpio); +} + +static int mpc8xxx_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); + + return in_be32(mm->regs + GPIO_DAT) & mpc8xxx_gpio2mask(gpio); +} + +static void mpc8xxx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); + struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); + unsigned long flags; + + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + + if (val) + mpc8xxx_gc->data |= mpc8xxx_gpio2mask(gpio); + else + mpc8xxx_gc->data &= ~mpc8xxx_gpio2mask(gpio); + + out_be32(mm->regs + GPIO_DAT, mpc8xxx_gc->data); + + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); +} + +static int mpc8xxx_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); + struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); + unsigned long flags; + + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + + clrbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio)); + + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); + + return 0; +} + +static int mpc8xxx_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); + struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); + unsigned long flags; + + mpc8xxx_gpio_set(gc, gpio, val); + + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + + setbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio)); + + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); + + return 0; +} + +static int mpc8xxx_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); + struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); + + if (mpc8xxx_gc->irq && offset < MPC8XXX_GPIO_PINS) + return irq_create_mapping(mpc8xxx_gc->irq, offset); + else + return -ENXIO; +} + +static void mpc8xxx_gpio_irq_cascade(unsigned int irq, struct irq_desc *desc) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_desc_get_handler_data(desc); + struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; + unsigned int mask; + + mask = in_be32(mm->regs + GPIO_IER) & in_be32(mm->regs + GPIO_IMR); + if (mask) + generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq, + 32 - ffs(mask))); +} + +static void mpc8xxx_irq_unmask(struct irq_data *d) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); + struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; + unsigned long flags; + + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + + setbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d))); + + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); +} + +static void mpc8xxx_irq_mask(struct irq_data *d) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); + struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; + unsigned long flags; + + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + + clrbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d))); + + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); +} + +static void mpc8xxx_irq_ack(struct irq_data *d) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); + struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; + + out_be32(mm->regs + GPIO_IER, mpc8xxx_gpio2mask(irqd_to_hwirq(d))); +} + +static int mpc8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); + struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; + unsigned long flags; + + switch (flow_type) { + case IRQ_TYPE_EDGE_FALLING: + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + setbits32(mm->regs + GPIO_ICR, + mpc8xxx_gpio2mask(irqd_to_hwirq(d))); + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); + break; + + case IRQ_TYPE_EDGE_BOTH: + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + clrbits32(mm->regs + GPIO_ICR, + mpc8xxx_gpio2mask(irqd_to_hwirq(d))); + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int mpc512x_irq_set_type(struct irq_data *d, unsigned int flow_type) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); + struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; + unsigned long gpio = irqd_to_hwirq(d); + void __iomem *reg; + unsigned int shift; + unsigned long flags; + + if (gpio < 16) { + reg = mm->regs + GPIO_ICR; + shift = (15 - gpio) * 2; + } else { + reg = mm->regs + GPIO_ICR2; + shift = (15 - (gpio % 16)) * 2; + } + + switch (flow_type) { + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_LEVEL_LOW: + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + clrsetbits_be32(reg, 3 << shift, 2 << shift); + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); + break; + + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_LEVEL_HIGH: + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + clrsetbits_be32(reg, 3 << shift, 1 << shift); + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); + break; + + case IRQ_TYPE_EDGE_BOTH: + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + clrbits32(reg, 3 << shift); + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static struct irq_chip mpc8xxx_irq_chip = { + .name = "mpc8xxx-gpio", + .irq_unmask = mpc8xxx_irq_unmask, + .irq_mask = mpc8xxx_irq_mask, + .irq_ack = mpc8xxx_irq_ack, + .irq_set_type = mpc8xxx_irq_set_type, +}; + +static int mpc8xxx_gpio_irq_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = h->host_data; + + if (mpc8xxx_gc->of_dev_id_data) + mpc8xxx_irq_chip.irq_set_type = mpc8xxx_gc->of_dev_id_data; + + irq_set_chip_data(virq, h->host_data); + irq_set_chip_and_handler(virq, &mpc8xxx_irq_chip, handle_level_irq); + irq_set_irq_type(virq, IRQ_TYPE_NONE); + + return 0; +} + +static int mpc8xxx_gpio_irq_xlate(struct irq_host *h, struct device_node *ct, + const u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, + unsigned int *out_flags) + +{ + /* interrupt sense values coming from the device tree equal either + * EDGE_FALLING or EDGE_BOTH + */ + *out_hwirq = intspec[0]; + *out_flags = intspec[1]; + + return 0; +} + +static struct irq_host_ops mpc8xxx_gpio_irq_ops = { + .map = mpc8xxx_gpio_irq_map, + .xlate = mpc8xxx_gpio_irq_xlate, +}; + +static struct of_device_id mpc8xxx_gpio_ids[] __initdata = { + { .compatible = "fsl,mpc8349-gpio", }, + { .compatible = "fsl,mpc8572-gpio", }, + { .compatible = "fsl,mpc8610-gpio", }, + { .compatible = "fsl,mpc5121-gpio", .data = mpc512x_irq_set_type, }, + { .compatible = "fsl,qoriq-gpio", }, + {} +}; + +static void __init mpc8xxx_add_controller(struct device_node *np) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc; + struct of_mm_gpio_chip *mm_gc; + struct gpio_chip *gc; + const struct of_device_id *id; + unsigned hwirq; + int ret; + + mpc8xxx_gc = kzalloc(sizeof(*mpc8xxx_gc), GFP_KERNEL); + if (!mpc8xxx_gc) { + ret = -ENOMEM; + goto err; + } + + spin_lock_init(&mpc8xxx_gc->lock); + + mm_gc = &mpc8xxx_gc->mm_gc; + gc = &mm_gc->gc; + + mm_gc->save_regs = mpc8xxx_gpio_save_regs; + gc->ngpio = MPC8XXX_GPIO_PINS; + gc->direction_input = mpc8xxx_gpio_dir_in; + gc->direction_output = mpc8xxx_gpio_dir_out; + if (of_device_is_compatible(np, "fsl,mpc8572-gpio")) + gc->get = mpc8572_gpio_get; + else + gc->get = mpc8xxx_gpio_get; + gc->set = mpc8xxx_gpio_set; + gc->to_irq = mpc8xxx_gpio_to_irq; + + ret = of_mm_gpiochip_add(np, mm_gc); + if (ret) + goto err; + + hwirq = irq_of_parse_and_map(np, 0); + if (hwirq == NO_IRQ) + goto skip_irq; + + mpc8xxx_gc->irq = + irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, MPC8XXX_GPIO_PINS, + &mpc8xxx_gpio_irq_ops, MPC8XXX_GPIO_PINS); + if (!mpc8xxx_gc->irq) + goto skip_irq; + + id = of_match_node(mpc8xxx_gpio_ids, np); + if (id) + mpc8xxx_gc->of_dev_id_data = id->data; + + mpc8xxx_gc->irq->host_data = mpc8xxx_gc; + + /* ack and mask all irqs */ + out_be32(mm_gc->regs + GPIO_IER, 0xffffffff); + out_be32(mm_gc->regs + GPIO_IMR, 0); + + irq_set_handler_data(hwirq, mpc8xxx_gc); + irq_set_chained_handler(hwirq, mpc8xxx_gpio_irq_cascade); + +skip_irq: + return; + +err: + pr_err("%s: registration failed with status %d\n", + np->full_name, ret); + kfree(mpc8xxx_gc); + + return; +} + +static int __init mpc8xxx_add_gpiochips(void) +{ + struct device_node *np; + + for_each_matching_node(np, mpc8xxx_gpio_ids) + mpc8xxx_add_controller(np); + + return 0; +} +arch_initcall(mpc8xxx_add_gpiochips); -- cgit v1.2.2 From ea5abbd215b749b2ff14397a47a5e65741c67bf4 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 26 Sep 2011 19:00:02 +0100 Subject: ARM: 7101/1: arm/tegra: Replace with This will eventually allow to be deleted. This mirrors LinusW's recent equivalent work on various other ARM platforms. Signed-off-by: Stephen Warren Acked-by: Olof Johansson Signed-off-by: Russell King --- drivers/gpio/gpio-tegra.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index df64536464f0..6b65207c8106 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -27,6 +27,7 @@ #include +#include #include #include -- cgit v1.2.2 From bd5f12a24766c1f299def0a78b008d4746f528f2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 22 Sep 2011 08:07:00 +0100 Subject: ARM: 7042/3: mach-ep93xx: break out GPIO driver specifics The file is included from upper directories and deal with generic GPIO and gpiolib stuff. Break out the platform and driver specific defines and functions into its own header file. Cc: Ryan Mallon Acked-by: Hartley Sweeten Acked-by: Grant Likely Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/gpio/gpio-ep93xx.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index fbd6a6066777..7aafbb437339 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -23,6 +23,9 @@ #include #include +#include + +#define irq_to_gpio(irq) ((irq) - gpio_to_irq(0)) struct ep93xx_gpio { void __iomem *mmio_base; -- cgit v1.2.2 From 354bf8010ffea1ca5accd9fae26b7373df96bcb7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 28 Sep 2011 09:10:54 +0100 Subject: ARM: 7103/1: plat-pxa: move PXA GPIO driver to GPIO subsystem As per example from the other ARM boards, push the PXA GPIO driver down to the GPIO subsystem so it can be consolidated. Acked-by: Eric Miao Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/gpio/Makefile | 1 + drivers/gpio/gpio-pxa.c | 336 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 337 insertions(+) create mode 100644 drivers/gpio/gpio-pxa.c (limited to 'drivers/gpio') diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 84bc7389e367..19c5d27b6d2e 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o obj-$(CONFIG_GPIO_PCH) += gpio-pch.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o +obj-$(CONFIG_PLAT_PXA) += gpio-pxa.o obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c new file mode 100644 index 000000000000..5d6a86bfc68d --- /dev/null +++ b/drivers/gpio/gpio-pxa.c @@ -0,0 +1,336 @@ +/* + * linux/arch/arm/plat-pxa/gpio.c + * + * Generic PXA GPIO handling + * + * Author: Nicolas Pitre + * Created: Jun 15, 2001 + * Copyright: MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include + +int pxa_last_gpio; + +struct pxa_gpio_chip { + struct gpio_chip chip; + void __iomem *regbase; + char label[10]; + + unsigned long irq_mask; + unsigned long irq_edge_rise; + unsigned long irq_edge_fall; + +#ifdef CONFIG_PM + unsigned long saved_gplr; + unsigned long saved_gpdr; + unsigned long saved_grer; + unsigned long saved_gfer; +#endif +}; + +static DEFINE_SPINLOCK(gpio_lock); +static struct pxa_gpio_chip *pxa_gpio_chips; + +#define for_each_gpio_chip(i, c) \ + for (i = 0, c = &pxa_gpio_chips[0]; i <= pxa_last_gpio; i += 32, c++) + +static inline void __iomem *gpio_chip_base(struct gpio_chip *c) +{ + return container_of(c, struct pxa_gpio_chip, chip)->regbase; +} + +static inline struct pxa_gpio_chip *gpio_to_pxachip(unsigned gpio) +{ + return &pxa_gpio_chips[gpio_to_bank(gpio)]; +} + +static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + void __iomem *base = gpio_chip_base(chip); + uint32_t value, mask = 1 << offset; + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + + value = __raw_readl(base + GPDR_OFFSET); + if (__gpio_is_inverted(chip->base + offset)) + value |= mask; + else + value &= ~mask; + __raw_writel(value, base + GPDR_OFFSET); + + spin_unlock_irqrestore(&gpio_lock, flags); + return 0; +} + +static int pxa_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + void __iomem *base = gpio_chip_base(chip); + uint32_t tmp, mask = 1 << offset; + unsigned long flags; + + __raw_writel(mask, base + (value ? GPSR_OFFSET : GPCR_OFFSET)); + + spin_lock_irqsave(&gpio_lock, flags); + + tmp = __raw_readl(base + GPDR_OFFSET); + if (__gpio_is_inverted(chip->base + offset)) + tmp &= ~mask; + else + tmp |= mask; + __raw_writel(tmp, base + GPDR_OFFSET); + + spin_unlock_irqrestore(&gpio_lock, flags); + return 0; +} + +static int pxa_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + return __raw_readl(gpio_chip_base(chip) + GPLR_OFFSET) & (1 << offset); +} + +static void pxa_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + __raw_writel(1 << offset, gpio_chip_base(chip) + + (value ? GPSR_OFFSET : GPCR_OFFSET)); +} + +static int __init pxa_init_gpio_chip(int gpio_end) +{ + int i, gpio, nbanks = gpio_to_bank(gpio_end) + 1; + struct pxa_gpio_chip *chips; + + chips = kzalloc(nbanks * sizeof(struct pxa_gpio_chip), GFP_KERNEL); + if (chips == NULL) { + pr_err("%s: failed to allocate GPIO chips\n", __func__); + return -ENOMEM; + } + + for (i = 0, gpio = 0; i < nbanks; i++, gpio += 32) { + struct gpio_chip *c = &chips[i].chip; + + sprintf(chips[i].label, "gpio-%d", i); + chips[i].regbase = (void __iomem *)GPIO_BANK(i); + + c->base = gpio; + c->label = chips[i].label; + + c->direction_input = pxa_gpio_direction_input; + c->direction_output = pxa_gpio_direction_output; + c->get = pxa_gpio_get; + c->set = pxa_gpio_set; + + /* number of GPIOs on last bank may be less than 32 */ + c->ngpio = (gpio + 31 > gpio_end) ? (gpio_end - gpio + 1) : 32; + gpiochip_add(c); + } + pxa_gpio_chips = chips; + return 0; +} + +/* Update only those GRERx and GFERx edge detection register bits if those + * bits are set in c->irq_mask + */ +static inline void update_edge_detect(struct pxa_gpio_chip *c) +{ + uint32_t grer, gfer; + + grer = __raw_readl(c->regbase + GRER_OFFSET) & ~c->irq_mask; + gfer = __raw_readl(c->regbase + GFER_OFFSET) & ~c->irq_mask; + grer |= c->irq_edge_rise & c->irq_mask; + gfer |= c->irq_edge_fall & c->irq_mask; + __raw_writel(grer, c->regbase + GRER_OFFSET); + __raw_writel(gfer, c->regbase + GFER_OFFSET); +} + +static int pxa_gpio_irq_type(struct irq_data *d, unsigned int type) +{ + struct pxa_gpio_chip *c; + int gpio = irq_to_gpio(d->irq); + unsigned long gpdr, mask = GPIO_bit(gpio); + + c = gpio_to_pxachip(gpio); + + if (type == IRQ_TYPE_PROBE) { + /* Don't mess with enabled GPIOs using preconfigured edges or + * GPIOs set to alternate function or to output during probe + */ + if ((c->irq_edge_rise | c->irq_edge_fall) & GPIO_bit(gpio)) + return 0; + + if (__gpio_is_occupied(gpio)) + return 0; + + type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; + } + + gpdr = __raw_readl(c->regbase + GPDR_OFFSET); + + if (__gpio_is_inverted(gpio)) + __raw_writel(gpdr | mask, c->regbase + GPDR_OFFSET); + else + __raw_writel(gpdr & ~mask, c->regbase + GPDR_OFFSET); + + if (type & IRQ_TYPE_EDGE_RISING) + c->irq_edge_rise |= mask; + else + c->irq_edge_rise &= ~mask; + + if (type & IRQ_TYPE_EDGE_FALLING) + c->irq_edge_fall |= mask; + else + c->irq_edge_fall &= ~mask; + + update_edge_detect(c); + + pr_debug("%s: IRQ%d (GPIO%d) - edge%s%s\n", __func__, d->irq, gpio, + ((type & IRQ_TYPE_EDGE_RISING) ? " rising" : ""), + ((type & IRQ_TYPE_EDGE_FALLING) ? " falling" : "")); + return 0; +} + +static void pxa_gpio_demux_handler(unsigned int irq, struct irq_desc *desc) +{ + struct pxa_gpio_chip *c; + int loop, gpio, gpio_base, n; + unsigned long gedr; + + do { + loop = 0; + for_each_gpio_chip(gpio, c) { + gpio_base = c->chip.base; + + gedr = __raw_readl(c->regbase + GEDR_OFFSET); + gedr = gedr & c->irq_mask; + __raw_writel(gedr, c->regbase + GEDR_OFFSET); + + n = find_first_bit(&gedr, BITS_PER_LONG); + while (n < BITS_PER_LONG) { + loop = 1; + + generic_handle_irq(gpio_to_irq(gpio_base + n)); + n = find_next_bit(&gedr, BITS_PER_LONG, n + 1); + } + } + } while (loop); +} + +static void pxa_ack_muxed_gpio(struct irq_data *d) +{ + int gpio = irq_to_gpio(d->irq); + struct pxa_gpio_chip *c = gpio_to_pxachip(gpio); + + __raw_writel(GPIO_bit(gpio), c->regbase + GEDR_OFFSET); +} + +static void pxa_mask_muxed_gpio(struct irq_data *d) +{ + int gpio = irq_to_gpio(d->irq); + struct pxa_gpio_chip *c = gpio_to_pxachip(gpio); + uint32_t grer, gfer; + + c->irq_mask &= ~GPIO_bit(gpio); + + grer = __raw_readl(c->regbase + GRER_OFFSET) & ~GPIO_bit(gpio); + gfer = __raw_readl(c->regbase + GFER_OFFSET) & ~GPIO_bit(gpio); + __raw_writel(grer, c->regbase + GRER_OFFSET); + __raw_writel(gfer, c->regbase + GFER_OFFSET); +} + +static void pxa_unmask_muxed_gpio(struct irq_data *d) +{ + int gpio = irq_to_gpio(d->irq); + struct pxa_gpio_chip *c = gpio_to_pxachip(gpio); + + c->irq_mask |= GPIO_bit(gpio); + update_edge_detect(c); +} + +static struct irq_chip pxa_muxed_gpio_chip = { + .name = "GPIO", + .irq_ack = pxa_ack_muxed_gpio, + .irq_mask = pxa_mask_muxed_gpio, + .irq_unmask = pxa_unmask_muxed_gpio, + .irq_set_type = pxa_gpio_irq_type, +}; + +void __init pxa_init_gpio(int mux_irq, int start, int end, set_wake_t fn) +{ + struct pxa_gpio_chip *c; + int gpio, irq; + + pxa_last_gpio = end; + + /* Initialize GPIO chips */ + pxa_init_gpio_chip(end); + + /* clear all GPIO edge detects */ + for_each_gpio_chip(gpio, c) { + __raw_writel(0, c->regbase + GFER_OFFSET); + __raw_writel(0, c->regbase + GRER_OFFSET); + __raw_writel(~0,c->regbase + GEDR_OFFSET); + } + + for (irq = gpio_to_irq(start); irq <= gpio_to_irq(end); irq++) { + irq_set_chip_and_handler(irq, &pxa_muxed_gpio_chip, + handle_edge_irq); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } + + /* Install handler for GPIO>=2 edge detect interrupts */ + irq_set_chained_handler(mux_irq, pxa_gpio_demux_handler); + pxa_muxed_gpio_chip.irq_set_wake = fn; +} + +#ifdef CONFIG_PM +static int pxa_gpio_suspend(void) +{ + struct pxa_gpio_chip *c; + int gpio; + + for_each_gpio_chip(gpio, c) { + c->saved_gplr = __raw_readl(c->regbase + GPLR_OFFSET); + c->saved_gpdr = __raw_readl(c->regbase + GPDR_OFFSET); + c->saved_grer = __raw_readl(c->regbase + GRER_OFFSET); + c->saved_gfer = __raw_readl(c->regbase + GFER_OFFSET); + + /* Clear GPIO transition detect bits */ + __raw_writel(0xffffffff, c->regbase + GEDR_OFFSET); + } + return 0; +} + +static void pxa_gpio_resume(void) +{ + struct pxa_gpio_chip *c; + int gpio; + + for_each_gpio_chip(gpio, c) { + /* restore level with set/clear */ + __raw_writel( c->saved_gplr, c->regbase + GPSR_OFFSET); + __raw_writel(~c->saved_gplr, c->regbase + GPCR_OFFSET); + + __raw_writel(c->saved_grer, c->regbase + GRER_OFFSET); + __raw_writel(c->saved_gfer, c->regbase + GFER_OFFSET); + __raw_writel(c->saved_gpdr, c->regbase + GPDR_OFFSET); + } +} +#else +#define pxa_gpio_suspend NULL +#define pxa_gpio_resume NULL +#endif + +struct syscore_ops pxa_gpio_syscore_ops = { + .suspend = pxa_gpio_suspend, + .resume = pxa_gpio_resume, +}; -- cgit v1.2.2 From f55be1bf52aad524dc1bf556ae26c90262c87825 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 28 Sep 2011 09:11:30 +0100 Subject: ARM: 7104/1: plat-pxa: break out GPIO driver specifics The file is included from upper directories and deal with generic GPIO and gpiolib stuff. Break out the platform and driver specific defines and functions into its own header file. Cc: Eric Miao Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/gpio/gpio-pxa.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 5d6a86bfc68d..9052925c6fa2 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -18,6 +18,8 @@ #include #include +#include + int pxa_last_gpio; struct pxa_gpio_chip { -- cgit v1.2.2 From f79e40eb66af17a4cfc56560b20fec73b0c7490e Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 26 Sep 2011 13:06:57 +0900 Subject: gpio/samsung: fix broken configuration for EXYNOS4 GPIO banks Commit 1b39d5f2cc introduced new common gpio driver for all Samsung GPIO SoCs. The new driver doesn't work correctly on Samsung Exynos4 SoC. It fails to set configuration for all but external interrupt pins. This patch fixes this issue. Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Acked-by: Grant Likely Signed-off-by: Kukjin Kim --- drivers/gpio/gpio-samsung.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index 36f3675e7de8..be135808f93f 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -446,6 +446,8 @@ static struct samsung_gpio_cfg s3c24xx_gpiocfg_banka = { static struct samsung_gpio_cfg exynos4_gpio_cfg = { .set_pull = exynos4_gpio_setpull, .get_pull = exynos4_gpio_getpull, + .set_config = samsung_gpio_setcfg_4bit, + .get_config = samsung_gpio_getcfg_4bit, }; static struct samsung_gpio_cfg s5p64x0_gpio_cfg_rbank = { -- cgit v1.2.2 From 3538d5f3ef42600e4f3b6ea202672b7730f9510e Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 26 Sep 2011 13:09:08 +0900 Subject: gpio/samsung: fix GPIO interrupt registration for EXYNOS4 SoCs Commit 1b39d5f2cc introduced new common gpio driver for all Samsung GPIO SoCs. The new driver doesn't correctly register GPIO interrupts on Samsung Exynos4 SoCs. This is caused by a typo in define name. This patch fixes this issue. Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim --- drivers/gpio/gpio-samsung.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index be135808f93f..cdffb0c8401c 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -2461,7 +2461,7 @@ static __init int samsung_gpiolib_init(void) } samsung_gpiolib_add_4bit_chips(exynos4_gpios_3, nr_chips, S5P_VA_GPIO3); -#if defined(CONFIG_SOC_EXYNOS4210) && defined(CONFIG_S5P_GPIO_INT) +#if defined(CONFIG_CPU_EXYNOS4210) && defined(CONFIG_S5P_GPIO_INT) s5p_register_gpioint_bank(IRQ_GPIO_XA, 0, IRQ_GPIO1_NR_GROUPS); s5p_register_gpioint_bank(IRQ_GPIO_XB, IRQ_GPIO1_NR_GROUPS, IRQ_GPIO2_NR_GROUPS); #endif -- cgit v1.2.2 From b391f8cf606679e97b02e3b9dca8a1d9956a5301 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 26 Sep 2011 13:10:32 +0900 Subject: gpio/samsung: correct pin configuration for S5PC100/S5PC110/EXYNOS4 Commit 1b39d5f2cc introduced new common gpio driver for all Samsung GPIO SoCs. The new driver use wrong configuration setup for all gpio pins on S5PC100 and S5PV210 SoCs and external interrupt lines on Exynos4 SoCs. This patch fixes this issue. Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Acked-by: Grant Likely Signed-off-by: Kukjin Kim --- drivers/gpio/gpio-samsung.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index cdffb0c8401c..b6be77ae4973 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -1862,7 +1862,7 @@ static struct samsung_gpio_chip s5pc100_gpios_4bit[] = { * Followings are the gpio banks in S5PV210/S5PC110 * * The 'config' member when left to NULL, is initialized to the default - * structure samsung_gpio_cfgs[4] in the init function below. + * structure samsung_gpio_cfgs[3] in the init function below. * * The 'base' member is also initialized in the init function below. * Note: The initialization of 'base' member of samsung_gpio_chip structure @@ -2083,7 +2083,7 @@ static struct samsung_gpio_chip s5pv210_gpios_4bit[] = { * Followings are the gpio banks in EXYNOS4210 * * The 'config' member when left to NULL, is initialized to the default - * structure samsung_gpio_cfgs[4] in the init function below. + * structure samsung_gpio_cfgs[3] in the init function below. * * The 'base' member is also initialized in the init function below. * Note: The initialization of 'base' member of samsung_gpio_chip structure @@ -2249,49 +2249,49 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = { .label = "GPL2", }, }, { - .config = &samsung_gpio_cfgs[4], + .config = &samsung_gpio_cfgs[0], .chip = { .base = EXYNOS4_GPY0(0), .ngpio = EXYNOS4_GPIO_Y0_NR, .label = "GPY0", }, }, { - .config = &samsung_gpio_cfgs[4], + .config = &samsung_gpio_cfgs[0], .chip = { .base = EXYNOS4_GPY1(0), .ngpio = EXYNOS4_GPIO_Y1_NR, .label = "GPY1", }, }, { - .config = &samsung_gpio_cfgs[4], + .config = &samsung_gpio_cfgs[0], .chip = { .base = EXYNOS4_GPY2(0), .ngpio = EXYNOS4_GPIO_Y2_NR, .label = "GPY2", }, }, { - .config = &samsung_gpio_cfgs[4], + .config = &samsung_gpio_cfgs[0], .chip = { .base = EXYNOS4_GPY3(0), .ngpio = EXYNOS4_GPIO_Y3_NR, .label = "GPY3", }, }, { - .config = &samsung_gpio_cfgs[4], + .config = &samsung_gpio_cfgs[0], .chip = { .base = EXYNOS4_GPY4(0), .ngpio = EXYNOS4_GPIO_Y4_NR, .label = "GPY4", }, }, { - .config = &samsung_gpio_cfgs[4], + .config = &samsung_gpio_cfgs[0], .chip = { .base = EXYNOS4_GPY5(0), .ngpio = EXYNOS4_GPIO_Y5_NR, .label = "GPY5", }, }, { - .config = &samsung_gpio_cfgs[4], + .config = &samsung_gpio_cfgs[0], .chip = { .base = EXYNOS4_GPY6(0), .ngpio = EXYNOS4_GPIO_Y6_NR, @@ -2299,7 +2299,7 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = { }, }, { .base = (S5P_VA_GPIO2 + 0xC00), - .config = &samsung_gpio_cfgs[4], + .config = &samsung_gpio_cfgs[3], .irq_base = IRQ_EINT(0), .chip = { .base = EXYNOS4_GPX0(0), @@ -2309,7 +2309,7 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = { }, }, { .base = (S5P_VA_GPIO2 + 0xC20), - .config = &samsung_gpio_cfgs[4], + .config = &samsung_gpio_cfgs[3], .irq_base = IRQ_EINT(8), .chip = { .base = EXYNOS4_GPX1(0), @@ -2319,7 +2319,7 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = { }, }, { .base = (S5P_VA_GPIO2 + 0xC40), - .config = &samsung_gpio_cfgs[4], + .config = &samsung_gpio_cfgs[3], .irq_base = IRQ_EINT(16), .chip = { .base = EXYNOS4_GPX2(0), @@ -2329,7 +2329,7 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = { }, }, { .base = (S5P_VA_GPIO2 + 0xC60), - .config = &samsung_gpio_cfgs[4], + .config = &samsung_gpio_cfgs[3], .irq_base = IRQ_EINT(24), .chip = { .base = EXYNOS4_GPX3(0), @@ -2399,7 +2399,7 @@ static __init int samsung_gpiolib_init(void) for (i = 0; i < nr_chips; i++, chip++) { if (!chip->config) { - chip->config = &samsung_gpio_cfgs[4]; + chip->config = &samsung_gpio_cfgs[3]; chip->group = group++; } } @@ -2414,7 +2414,7 @@ static __init int samsung_gpiolib_init(void) for (i = 0; i < nr_chips; i++, chip++) { if (!chip->config) { - chip->config = &samsung_gpio_cfgs[4]; + chip->config = &samsung_gpio_cfgs[3]; chip->group = group++; } } -- cgit v1.2.2 From 45d198c4cf3a9a751b734eb32426b6de4631ef2e Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Fri, 5 Aug 2011 13:04:20 +0900 Subject: gpio-ml-ioh: Delete unnecessary code This register restore processing is unnecessary in suspend processing. (The restore processing is already in resume processing) Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/gpio-ml-ioh.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index a9016f56ed7e..655e55c55e02 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -284,7 +284,6 @@ static int ioh_gpio_suspend(struct pci_dev *pdev, pm_message_t state) struct ioh_gpio *chip = pci_get_drvdata(pdev); ioh_gpio_save_reg_conf(chip); - ioh_gpio_restore_reg_conf(chip); ret = pci_save_state(pdev); if (ret) { -- cgit v1.2.2 From 54be566317b6aece2389a95bb19ea209af9359be Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Fri, 5 Aug 2011 13:04:21 +0900 Subject: gpio-ml-ioh: Support interrupt function Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-ml-ioh.c | 209 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 207 insertions(+), 3 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d539efd96d4b..04499c19e986 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -402,6 +402,7 @@ config GPIO_PCH config GPIO_ML_IOH tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support" depends on PCI + select GENERIC_IRQ_CHIP help ML7213 is companion chip for Intel Atom E6xx series. This driver can be used for OKI SEMICONDUCTOR ML7213 IOH(Input/Output diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 655e55c55e02..4fab37bf564d 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -18,6 +18,17 @@ #include #include #include +#include +#include + +#define IOH_EDGE_FALLING 0 +#define IOH_EDGE_RISING BIT(0) +#define IOH_LEVEL_L BIT(1) +#define IOH_LEVEL_H (BIT(0) | BIT(1)) +#define IOH_EDGE_BOTH BIT(2) +#define IOH_IM_MASK (BIT(0) | BIT(1) | BIT(2)) + +#define IOH_IRQ_BASE 0 #define PCI_VENDOR_ID_ROHM 0x10DB @@ -46,12 +57,20 @@ struct ioh_regs { /** * struct ioh_gpio_reg_data - The register store data. + * @ien_reg To store contents of interrupt enable register. + * @imask_reg: To store contents of interrupt mask regist * @po_reg: To store contents of PO register. * @pm_reg: To store contents of PM register. + * @im0_reg: To store contents of interrupt mode regist0 + * @im1_reg: To store contents of interrupt mode regist1 */ struct ioh_gpio_reg_data { + u32 ien_reg; + u32 imask_reg; u32 po_reg; u32 pm_reg; + u32 im0_reg; + u32 im1_reg; }; /** @@ -63,6 +82,9 @@ struct ioh_gpio_reg_data { * @ioh_gpio_reg: Memory mapped Register data is saved here * when suspend. * @ch: Indicate GPIO channel + * @irq_base: Save base of IRQ number for interrupt + * @spinlock: Used for register access protection in + * interrupt context ioh_irq_type and PM; */ struct ioh_gpio { void __iomem *base; @@ -72,6 +94,8 @@ struct ioh_gpio { struct ioh_gpio_reg_data ioh_gpio_reg; struct mutex lock; int ch; + int irq_base; + spinlock_t spinlock; }; static const int num_ports[] = {6, 12, 16, 16, 15, 16, 16, 12}; @@ -147,6 +171,10 @@ static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip) { chip->ioh_gpio_reg.po_reg = ioread32(&chip->reg->regs[chip->ch].po); chip->ioh_gpio_reg.pm_reg = ioread32(&chip->reg->regs[chip->ch].pm); + chip->ioh_gpio_reg.ien_reg = ioread32(&chip->reg->regs[chip->ch].ien); + chip->ioh_gpio_reg.imask_reg = ioread32(&chip->reg->regs[chip->ch].imask); + chip->ioh_gpio_reg.im0_reg = ioread32(&chip->reg->regs[chip->ch].im_0); + chip->ioh_gpio_reg.im1_reg = ioread32(&chip->reg->regs[chip->ch].im_1); } /* @@ -154,13 +182,21 @@ static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip) */ static void ioh_gpio_restore_reg_conf(struct ioh_gpio *chip) { - /* to store contents of PO register */ iowrite32(chip->ioh_gpio_reg.po_reg, &chip->reg->regs[chip->ch].po); - /* to store contents of PM register */ iowrite32(chip->ioh_gpio_reg.pm_reg, &chip->reg->regs[chip->ch].pm); + iowrite32(chip->ioh_gpio_reg.ien_reg, &chip->reg->regs[chip->ch].ien); + iowrite32(chip->ioh_gpio_reg.imask_reg, &chip->reg->regs[chip->ch].imask); + iowrite32(chip->ioh_gpio_reg.im0_reg, &chip->reg->regs[chip->ch].im_0); + iowrite32(chip->ioh_gpio_reg.im1_reg, &chip->reg->regs[chip->ch].im_1); } #endif +static int ioh_gpio_to_irq(struct gpio_chip *gpio, unsigned offset) +{ + struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio); + return chip->irq_base + offset; +} + static void ioh_gpio_setup(struct ioh_gpio *chip, int num_port) { struct gpio_chip *gpio = &chip->gpio; @@ -175,16 +211,148 @@ static void ioh_gpio_setup(struct ioh_gpio *chip, int num_port) gpio->base = -1; gpio->ngpio = num_port; gpio->can_sleep = 0; + gpio->to_irq = ioh_gpio_to_irq; +} + +static int ioh_irq_type(struct irq_data *d, unsigned int type) +{ + u32 im; + u32 *im_reg; + u32 ien; + u32 im_pos; + int ch; + unsigned long flags; + u32 val; + int irq = d->irq; + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct ioh_gpio *chip = gc->private; + + ch = irq - chip->irq_base; + if (irq <= chip->irq_base + 7) { + im_reg = &chip->reg->regs[chip->ch].im_0; + im_pos = ch; + } else { + im_reg = &chip->reg->regs[chip->ch].im_1; + im_pos = ch - 8; + } + dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d type=%d\n", + __func__, irq, type, ch, im_pos, type); + + spin_lock_irqsave(&chip->spinlock, flags); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + val = IOH_EDGE_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + val = IOH_EDGE_FALLING; + break; + case IRQ_TYPE_EDGE_BOTH: + val = IOH_EDGE_BOTH; + break; + case IRQ_TYPE_LEVEL_HIGH: + val = IOH_LEVEL_H; + break; + case IRQ_TYPE_LEVEL_LOW: + val = IOH_LEVEL_L; + break; + case IRQ_TYPE_PROBE: + goto end; + default: + dev_warn(chip->dev, "%s: unknown type(%dd)", + __func__, type); + goto end; + } + + /* Set interrupt mode */ + im = ioread32(im_reg) & ~(IOH_IM_MASK << (im_pos * 4)); + iowrite32(im | (val << (im_pos * 4)), im_reg); + + /* iclr */ + iowrite32(BIT(ch), &chip->reg->regs[chip->ch].iclr); + + /* IMASKCLR */ + iowrite32(BIT(ch), &chip->reg->regs[chip->ch].imaskclr); + + /* Enable interrupt */ + ien = ioread32(&chip->reg->regs[chip->ch].ien); + iowrite32(ien | BIT(ch), &chip->reg->regs[chip->ch].ien); +end: + spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; +} + +static void ioh_irq_unmask(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct ioh_gpio *chip = gc->private; + + iowrite32(1 << (d->irq - chip->irq_base), + &chip->reg->regs[chip->ch].imaskclr); +} + +static void ioh_irq_mask(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct ioh_gpio *chip = gc->private; + + iowrite32(1 << (d->irq - chip->irq_base), + &chip->reg->regs[chip->ch].imask); +} + +static irqreturn_t ioh_gpio_handler(int irq, void *dev_id) +{ + struct ioh_gpio *chip = dev_id; + u32 reg_val; + int i, j; + int ret = IRQ_NONE; + + for (i = 0; i < 8; i++) { + reg_val = ioread32(&chip->reg->regs[i].istatus); + for (j = 0; j < num_ports[i]; j++) { + if (reg_val & BIT(j)) { + dev_dbg(chip->dev, + "%s:[%d]:irq=%d status=0x%x\n", + __func__, j, irq, reg_val); + iowrite32(BIT(j), + &chip->reg->regs[chip->ch].iclr); + generic_handle_irq(chip->irq_base + j); + ret = IRQ_HANDLED; + } + } + } + return ret; +} + +static __devinit void ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip, + unsigned int irq_start, unsigned int num) +{ + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + + gc = irq_alloc_generic_chip("ioh_gpio", 1, irq_start, chip->base, + handle_simple_irq); + gc->private = chip; + ct = gc->chip_types; + + ct->chip.irq_mask = ioh_irq_mask; + ct->chip.irq_unmask = ioh_irq_unmask; + ct->chip.irq_set_type = ioh_irq_type; + + irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, + IRQ_NOREQUEST | IRQ_NOPROBE, 0); } static int __devinit ioh_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int ret; - int i; + int i, j; struct ioh_gpio *chip; void __iomem *base; void __iomem *chip_save; + int irq_base; ret = pci_enable_device(pdev); if (ret) { @@ -228,10 +396,41 @@ static int __devinit ioh_gpio_probe(struct pci_dev *pdev, } chip = chip_save; + for (j = 0; j < 8; j++, chip++) { + irq_base = irq_alloc_descs(-1, IOH_IRQ_BASE, num_ports[j], + GFP_KERNEL); + if (irq_base < 0) { + dev_warn(&pdev->dev, + "ml_ioh_gpio: Failed to get IRQ base num\n"); + chip->irq_base = -1; + goto err_irq_alloc_descs; + } + chip->irq_base = irq_base; + ioh_gpio_alloc_generic_chip(chip, irq_base, num_ports[j]); + } + + chip = chip_save; + ret = request_irq(pdev->irq, ioh_gpio_handler, + IRQF_SHARED, KBUILD_MODNAME, chip); + if (ret != 0) { + dev_err(&pdev->dev, + "%s request_irq failed\n", __func__); + goto err_request_irq; + } + pci_set_drvdata(pdev, chip); return 0; +err_request_irq: + chip = chip_save; +err_irq_alloc_descs: + while (--j >= 0) { + chip--; + irq_free_descs(chip->irq_base, num_ports[j]); + } + + chip = chip_save; err_gpiochip_add: while (--i >= 0) { chip--; @@ -264,7 +463,11 @@ static void __devexit ioh_gpio_remove(struct pci_dev *pdev) void __iomem *chip_save; chip_save = chip; + + free_irq(pdev->irq, chip); + for (i = 0; i < 8; i++, chip++) { + irq_free_descs(chip->irq_base, num_ports[i]); err = gpiochip_remove(&chip->gpio); if (err) dev_err(&pdev->dev, "Failed gpiochip_remove\n"); -- cgit v1.2.2 From b490fa0bf86edbc06562024cbace5e84f0e2cf0e Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Fri, 5 Aug 2011 13:04:22 +0900 Subject: gpio-ml-ioh: Fix suspend/resume issue Currently, some registers are not saved in case changing to suspend state. This patch fixes the issue. Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/gpio-ml-ioh.c | 60 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 12 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 4fab37bf564d..274fd4d0792f 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -63,6 +63,7 @@ struct ioh_regs { * @pm_reg: To store contents of PM register. * @im0_reg: To store contents of interrupt mode regist0 * @im1_reg: To store contents of interrupt mode regist1 + * @use_sel_reg: To store contents of GPIO_USE_SEL0~3 */ struct ioh_gpio_reg_data { u32 ien_reg; @@ -71,6 +72,7 @@ struct ioh_gpio_reg_data { u32 pm_reg; u32 im0_reg; u32 im1_reg; + u32 use_sel_reg; }; /** @@ -81,6 +83,7 @@ struct ioh_gpio_reg_data { * @gpio: Data for GPIO infrastructure. * @ioh_gpio_reg: Memory mapped Register data is saved here * when suspend. + * @gpio_use_sel: Save GPIO_USE_SEL1~4 register for PM * @ch: Indicate GPIO channel * @irq_base: Save base of IRQ number for interrupt * @spinlock: Used for register access protection in @@ -92,6 +95,7 @@ struct ioh_gpio { struct device *dev; struct gpio_chip gpio; struct ioh_gpio_reg_data ioh_gpio_reg; + u32 gpio_use_sel; struct mutex lock; int ch; int irq_base; @@ -169,12 +173,25 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) */ static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip) { - chip->ioh_gpio_reg.po_reg = ioread32(&chip->reg->regs[chip->ch].po); - chip->ioh_gpio_reg.pm_reg = ioread32(&chip->reg->regs[chip->ch].pm); - chip->ioh_gpio_reg.ien_reg = ioread32(&chip->reg->regs[chip->ch].ien); - chip->ioh_gpio_reg.imask_reg = ioread32(&chip->reg->regs[chip->ch].imask); - chip->ioh_gpio_reg.im0_reg = ioread32(&chip->reg->regs[chip->ch].im_0); - chip->ioh_gpio_reg.im1_reg = ioread32(&chip->reg->regs[chip->ch].im_1); + int i; + + for (i = 0; i < 8; i ++, chip++) { + chip->ioh_gpio_reg.po_reg = + ioread32(&chip->reg->regs[chip->ch].po); + chip->ioh_gpio_reg.pm_reg = + ioread32(&chip->reg->regs[chip->ch].pm); + chip->ioh_gpio_reg.ien_reg = + ioread32(&chip->reg->regs[chip->ch].ien); + chip->ioh_gpio_reg.imask_reg = + ioread32(&chip->reg->regs[chip->ch].imask); + chip->ioh_gpio_reg.im0_reg = + ioread32(&chip->reg->regs[chip->ch].im_0); + chip->ioh_gpio_reg.im1_reg = + ioread32(&chip->reg->regs[chip->ch].im_1); + if (i < 4) + chip->ioh_gpio_reg.use_sel_reg = + ioread32(&chip->reg->ioh_sel_reg[i]); + } } /* @@ -182,12 +199,25 @@ static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip) */ static void ioh_gpio_restore_reg_conf(struct ioh_gpio *chip) { - iowrite32(chip->ioh_gpio_reg.po_reg, &chip->reg->regs[chip->ch].po); - iowrite32(chip->ioh_gpio_reg.pm_reg, &chip->reg->regs[chip->ch].pm); - iowrite32(chip->ioh_gpio_reg.ien_reg, &chip->reg->regs[chip->ch].ien); - iowrite32(chip->ioh_gpio_reg.imask_reg, &chip->reg->regs[chip->ch].imask); - iowrite32(chip->ioh_gpio_reg.im0_reg, &chip->reg->regs[chip->ch].im_0); - iowrite32(chip->ioh_gpio_reg.im1_reg, &chip->reg->regs[chip->ch].im_1); + int i; + + for (i = 0; i < 8; i ++, chip++) { + iowrite32(chip->ioh_gpio_reg.po_reg, + &chip->reg->regs[chip->ch].po); + iowrite32(chip->ioh_gpio_reg.pm_reg, + &chip->reg->regs[chip->ch].pm); + iowrite32(chip->ioh_gpio_reg.ien_reg, + &chip->reg->regs[chip->ch].ien); + iowrite32(chip->ioh_gpio_reg.imask_reg, + &chip->reg->regs[chip->ch].imask); + iowrite32(chip->ioh_gpio_reg.im0_reg, + &chip->reg->regs[chip->ch].im_0); + iowrite32(chip->ioh_gpio_reg.im1_reg, + &chip->reg->regs[chip->ch].im_1); + if (i < 4) + iowrite32(chip->ioh_gpio_reg.use_sel_reg, + &chip->reg->ioh_sel_reg[i]); + } } #endif @@ -485,8 +515,11 @@ static int ioh_gpio_suspend(struct pci_dev *pdev, pm_message_t state) { s32 ret; struct ioh_gpio *chip = pci_get_drvdata(pdev); + unsigned long flags; + spin_lock_irqsave(&chip->spinlock, flags); ioh_gpio_save_reg_conf(chip); + spin_unlock_irqrestore(&chip->spinlock, flags); ret = pci_save_state(pdev); if (ret) { @@ -506,6 +539,7 @@ static int ioh_gpio_resume(struct pci_dev *pdev) { s32 ret; struct ioh_gpio *chip = pci_get_drvdata(pdev); + unsigned long flags; ret = pci_enable_wake(pdev, PCI_D0, 0); @@ -517,9 +551,11 @@ static int ioh_gpio_resume(struct pci_dev *pdev) } pci_restore_state(pdev); + spin_lock_irqsave(&chip->spinlock, flags); iowrite32(0x01, &chip->reg->srst); iowrite32(0x00, &chip->reg->srst); ioh_gpio_restore_reg_conf(chip); + spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } -- cgit v1.2.2 From 829e8256f139a9665f861d7ba880ed90abd75b65 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 21 Jul 2011 09:19:54 +0900 Subject: gpio-pch: Delete invalid "restore" code in suspend() Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/gpio-pch.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 36919e77c495..ca9c7b051e07 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -241,7 +241,6 @@ static int pch_gpio_suspend(struct pci_dev *pdev, pm_message_t state) struct pch_gpio *chip = pci_get_drvdata(pdev); pch_gpio_save_reg_conf(chip); - pch_gpio_restore_reg_conf(chip); ret = pci_save_state(pdev); if (ret) { -- cgit v1.2.2 From d568a6814fde60f5ab6b0c29b6261ff1899da443 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 21 Jul 2011 09:19:55 +0900 Subject: gpio-pch: add spinlock in suspend/resume processing Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/gpio-pch.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index ca9c7b051e07..252bddbd3f44 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -55,6 +55,9 @@ struct pch_gpio_reg_data { * @gpio: Data for GPIO infrastructure. * @pch_gpio_reg: Memory mapped Register data is saved here * when suspend. + * @spinlock: Used for register access protection in + * interrupt context pch_irq_mask, + * pch_irq_unmask and pch_irq_type; */ struct pch_gpio { void __iomem *base; @@ -63,6 +66,7 @@ struct pch_gpio { struct gpio_chip gpio; struct pch_gpio_reg_data pch_gpio_reg; struct mutex lock; + spinlock_t spinlock; }; static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) @@ -239,8 +243,11 @@ static int pch_gpio_suspend(struct pci_dev *pdev, pm_message_t state) { s32 ret; struct pch_gpio *chip = pci_get_drvdata(pdev); + unsigned long flags; + spin_lock_irqsave(&chip->spinlock, flags); pch_gpio_save_reg_conf(chip); + spin_unlock_irqrestore(&chip->spinlock, flags); ret = pci_save_state(pdev); if (ret) { @@ -260,6 +267,7 @@ static int pch_gpio_resume(struct pci_dev *pdev) { s32 ret; struct pch_gpio *chip = pci_get_drvdata(pdev); + unsigned long flags; ret = pci_enable_wake(pdev, PCI_D0, 0); @@ -271,9 +279,11 @@ static int pch_gpio_resume(struct pci_dev *pdev) } pci_restore_state(pdev); + spin_lock_irqsave(&chip->spinlock, flags); iowrite32(0x01, &chip->reg->reset); iowrite32(0x00, &chip->reg->reset); pch_gpio_restore_reg_conf(chip); + spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } -- cgit v1.2.2 From c3520a1a84f13becf7489ddee4571eaccf108934 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 21 Jul 2011 09:19:56 +0900 Subject: gpio-pch: support ML7223 IOH n-Bus Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/gpio-pch.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 252bddbd3f44..d548069d3912 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -296,6 +296,7 @@ static int pch_gpio_resume(struct pci_dev *pdev) static DEFINE_PCI_DEVICE_TABLE(pch_gpio_pcidev_id) = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8803) }, { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8014) }, + { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8043) }, { 0, } }; MODULE_DEVICE_TABLE(pci, pch_gpio_pcidev_id); -- cgit v1.2.2 From d4260e6dddfe642ab50ec6398aeac794a6aff151 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 21 Jul 2011 09:19:57 +0900 Subject: gpio-pch: modify gpio_nums and mask Currently, the number of GPIO pins is set fixed value(=12). Also PIN MASK is set as '0xfff'. However the pins differs by IOH. This patch sets the value correctly. Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/gpio-pch.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index d548069d3912..4ac69bd7ad4a 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -18,9 +18,6 @@ #include #include -#define PCH_GPIO_ALL_PINS 0xfff /* Mask for GPIO pins 0 to 11 */ -#define GPIO_NUM_PINS 12 /* Specifies number of GPIO PINS GPIO0-GPIO11 */ - struct pch_regs { u32 ien; u32 istatus; @@ -37,6 +34,19 @@ struct pch_regs { u32 reset; }; +enum pch_type_t { + INTEL_EG20T_PCH, + OKISEMI_ML7223m_IOH, /* OKISEMI ML7223 IOH PCIe Bus-m */ + OKISEMI_ML7223n_IOH /* OKISEMI ML7223 IOH PCIe Bus-n */ +}; + +/* Specifies number of GPIO PINS */ +static int gpio_pins[] = { + [INTEL_EG20T_PCH] = 12, + [OKISEMI_ML7223m_IOH] = 8, + [OKISEMI_ML7223n_IOH] = 8, +}; + /** * struct pch_gpio_reg_data - The register store data. * @po_reg: To store contents of PO register. @@ -55,6 +65,7 @@ struct pch_gpio_reg_data { * @gpio: Data for GPIO infrastructure. * @pch_gpio_reg: Memory mapped Register data is saved here * when suspend. + * @ioh: IOH ID * @spinlock: Used for register access protection in * interrupt context pch_irq_mask, * pch_irq_unmask and pch_irq_type; @@ -66,6 +77,7 @@ struct pch_gpio { struct gpio_chip gpio; struct pch_gpio_reg_data pch_gpio_reg; struct mutex lock; + enum pch_type_t ioh; spinlock_t spinlock; }; @@ -100,7 +112,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, u32 reg_val; mutex_lock(&chip->lock); - pm = ioread32(&chip->reg->pm) & PCH_GPIO_ALL_PINS; + pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1); pm |= (1 << nr); iowrite32(pm, &chip->reg->pm); @@ -122,7 +134,7 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) u32 pm; mutex_lock(&chip->lock); - pm = ioread32(&chip->reg->pm) & PCH_GPIO_ALL_PINS; /*bits 0-11*/ + pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1); pm &= ~(1 << nr); iowrite32(pm, &chip->reg->pm); mutex_unlock(&chip->lock); @@ -162,7 +174,7 @@ static void pch_gpio_setup(struct pch_gpio *chip) gpio->set = pch_gpio_set; gpio->dbg_show = NULL; gpio->base = -1; - gpio->ngpio = GPIO_NUM_PINS; + gpio->ngpio = gpio_pins[chip->ioh]; gpio->can_sleep = 0; } @@ -196,6 +208,13 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev, goto err_iomap; } + if (pdev->device == 0x8803) + chip->ioh = INTEL_EG20T_PCH; + else if (pdev->device == 0x8014) + chip->ioh = OKISEMI_ML7223m_IOH; + else if (pdev->device == 0x8043) + chip->ioh = OKISEMI_ML7223n_IOH; + chip->reg = chip->base; pci_set_drvdata(pdev, chip); mutex_init(&chip->lock); -- cgit v1.2.2 From e98bed7f0fa847492db8316db4605f2681f39868 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 21 Jul 2011 09:19:58 +0900 Subject: gpio-pch: Save register value in suspend() Currently, when suspend is occurred, register im0/1 and gpio_use_sel are not saved. This patch modifies so that register im0/1 and gpio_use_sel are saved. Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/gpio-pch.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 4ac69bd7ad4a..7f773afeb340 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -30,7 +30,8 @@ struct pch_regs { u32 pm; u32 im0; u32 im1; - u32 reserved[4]; + u32 reserved[3]; + u32 gpio_use_sel; u32 reset; }; @@ -51,10 +52,17 @@ static int gpio_pins[] = { * struct pch_gpio_reg_data - The register store data. * @po_reg: To store contents of PO register. * @pm_reg: To store contents of PM register. + * @im0_reg: To store contents of IM0 register. + * @im1_reg: To store contents of IM1 register. + * @gpio_use_sel_reg : To store contents of GPIO_USE_SEL register. + * (Only ML7223 Bus-n) */ struct pch_gpio_reg_data { u32 po_reg; u32 pm_reg; + u32 im0_reg; + u32 im1_reg; + u32 gpio_use_sel_reg; }; /** @@ -149,6 +157,12 @@ static void pch_gpio_save_reg_conf(struct pch_gpio *chip) { chip->pch_gpio_reg.po_reg = ioread32(&chip->reg->po); chip->pch_gpio_reg.pm_reg = ioread32(&chip->reg->pm); + chip->pch_gpio_reg.im0_reg = ioread32(&chip->reg->im0); + if (chip->ioh == INTEL_EG20T_PCH) + chip->pch_gpio_reg.im1_reg = ioread32(&chip->reg->im1); + if (chip->ioh == OKISEMI_ML7223n_IOH) + chip->pch_gpio_reg.gpio_use_sel_reg =\ + ioread32(&chip->reg->gpio_use_sel); } /* @@ -160,6 +174,12 @@ static void pch_gpio_restore_reg_conf(struct pch_gpio *chip) iowrite32(chip->pch_gpio_reg.po_reg, &chip->reg->po); /* to store contents of PM register */ iowrite32(chip->pch_gpio_reg.pm_reg, &chip->reg->pm); + iowrite32(chip->pch_gpio_reg.im0_reg, &chip->reg->im0); + if (chip->ioh == INTEL_EG20T_PCH) + iowrite32(chip->pch_gpio_reg.im1_reg, &chip->reg->im1); + if (chip->ioh == OKISEMI_ML7223n_IOH) + iowrite32(chip->pch_gpio_reg.gpio_use_sel_reg, + &chip->reg->gpio_use_sel); } static void pch_gpio_setup(struct pch_gpio *chip) -- cgit v1.2.2 From 38eb18a6f92da886fc1af509d25e8f7a49e23d9a Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 21 Jul 2011 09:19:59 +0900 Subject: gpio-pch: Support interrupt function Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-pch.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 04499c19e986..de5fea60b385 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -388,6 +388,7 @@ config GPIO_LANGWELL config GPIO_PCH tristate "Intel EG20T PCH / OKI SEMICONDUCTOR ML7223 IOH GPIO" depends on PCI && X86 + select GENERIC_IRQ_CHIP help This driver is for PCH(Platform controller Hub) GPIO of Intel Topcliff which is an IOH(Input/Output Hub) for x86 embedded processor. diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 7f773afeb340..46b5209878f6 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -17,6 +17,17 @@ #include #include #include +#include +#include + +#define PCH_EDGE_FALLING 0 +#define PCH_EDGE_RISING BIT(0) +#define PCH_LEVEL_L BIT(1) +#define PCH_LEVEL_H (BIT(0) | BIT(1)) +#define PCH_EDGE_BOTH BIT(2) +#define PCH_IM_MASK (BIT(0) | BIT(1) | BIT(2)) + +#define PCH_IRQ_BASE 24 struct pch_regs { u32 ien; @@ -50,6 +61,8 @@ static int gpio_pins[] = { /** * struct pch_gpio_reg_data - The register store data. + * @ien_reg: To store contents of IEN register. + * @imask_reg: To store contents of IMASK register. * @po_reg: To store contents of PO register. * @pm_reg: To store contents of PM register. * @im0_reg: To store contents of IM0 register. @@ -58,6 +71,8 @@ static int gpio_pins[] = { * (Only ML7223 Bus-n) */ struct pch_gpio_reg_data { + u32 ien_reg; + u32 imask_reg; u32 po_reg; u32 pm_reg; u32 im0_reg; @@ -73,6 +88,8 @@ struct pch_gpio_reg_data { * @gpio: Data for GPIO infrastructure. * @pch_gpio_reg: Memory mapped Register data is saved here * when suspend. + * @lock: Used for register access protection + * @irq_base: Save base of IRQ number for interrupt * @ioh: IOH ID * @spinlock: Used for register access protection in * interrupt context pch_irq_mask, @@ -85,6 +102,7 @@ struct pch_gpio { struct gpio_chip gpio; struct pch_gpio_reg_data pch_gpio_reg; struct mutex lock; + int irq_base; enum pch_type_t ioh; spinlock_t spinlock; }; @@ -155,6 +173,8 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) */ static void pch_gpio_save_reg_conf(struct pch_gpio *chip) { + chip->pch_gpio_reg.ien_reg = ioread32(&chip->reg->ien); + chip->pch_gpio_reg.imask_reg = ioread32(&chip->reg->imask); chip->pch_gpio_reg.po_reg = ioread32(&chip->reg->po); chip->pch_gpio_reg.pm_reg = ioread32(&chip->reg->pm); chip->pch_gpio_reg.im0_reg = ioread32(&chip->reg->im0); @@ -170,6 +190,8 @@ static void pch_gpio_save_reg_conf(struct pch_gpio *chip) */ static void pch_gpio_restore_reg_conf(struct pch_gpio *chip) { + iowrite32(chip->pch_gpio_reg.ien_reg, &chip->reg->ien); + iowrite32(chip->pch_gpio_reg.imask_reg, &chip->reg->imask); /* to store contents of PO register */ iowrite32(chip->pch_gpio_reg.po_reg, &chip->reg->po); /* to store contents of PM register */ @@ -182,6 +204,12 @@ static void pch_gpio_restore_reg_conf(struct pch_gpio *chip) &chip->reg->gpio_use_sel); } +static int pch_gpio_to_irq(struct gpio_chip *gpio, unsigned offset) +{ + struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio); + return chip->irq_base + offset; +} + static void pch_gpio_setup(struct pch_gpio *chip) { struct gpio_chip *gpio = &chip->gpio; @@ -196,6 +224,130 @@ static void pch_gpio_setup(struct pch_gpio *chip) gpio->base = -1; gpio->ngpio = gpio_pins[chip->ioh]; gpio->can_sleep = 0; + gpio->to_irq = pch_gpio_to_irq; +} + +static int pch_irq_type(struct irq_data *d, unsigned int type) +{ + u32 im; + u32 *im_reg; + u32 ien; + u32 im_pos; + int ch; + unsigned long flags; + u32 val; + int irq = d->irq; + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct pch_gpio *chip = gc->private; + + ch = irq - chip->irq_base; + if (irq <= chip->irq_base + 7) { + im_reg = &chip->reg->im0; + im_pos = ch; + } else { + im_reg = &chip->reg->im1; + im_pos = ch - 8; + } + dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d\n", + __func__, irq, type, ch, im_pos); + + spin_lock_irqsave(&chip->spinlock, flags); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + val = PCH_EDGE_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + val = PCH_EDGE_FALLING; + break; + case IRQ_TYPE_EDGE_BOTH: + val = PCH_EDGE_BOTH; + break; + case IRQ_TYPE_LEVEL_HIGH: + val = PCH_LEVEL_H; + break; + case IRQ_TYPE_LEVEL_LOW: + val = PCH_LEVEL_L; + break; + case IRQ_TYPE_PROBE: + goto end; + default: + dev_warn(chip->dev, "%s: unknown type(%dd)", + __func__, type); + goto end; + } + + /* Set interrupt mode */ + im = ioread32(im_reg) & ~(PCH_IM_MASK << (im_pos * 4)); + iowrite32(im | (val << (im_pos * 4)), im_reg); + + /* iclr */ + iowrite32(BIT(ch), &chip->reg->iclr); + + /* IMASKCLR */ + iowrite32(BIT(ch), &chip->reg->imaskclr); + + /* Enable interrupt */ + ien = ioread32(&chip->reg->ien); + iowrite32(ien | BIT(ch), &chip->reg->ien); +end: + spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; +} + +static void pch_irq_unmask(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct pch_gpio *chip = gc->private; + + iowrite32(1 << (d->irq - chip->irq_base), &chip->reg->imaskclr); +} + +static void pch_irq_mask(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct pch_gpio *chip = gc->private; + + iowrite32(1 << (d->irq - chip->irq_base), &chip->reg->imask); +} + +static irqreturn_t pch_gpio_handler(int irq, void *dev_id) +{ + struct pch_gpio *chip = dev_id; + u32 reg_val = ioread32(&chip->reg->istatus); + int i; + int ret = IRQ_NONE; + + for (i = 0; i < gpio_pins[chip->ioh]; i++) { + if (reg_val & BIT(i)) { + dev_dbg(chip->dev, "%s:[%d]:irq=%d status=0x%x\n", + __func__, i, irq, reg_val); + iowrite32(BIT(i), &chip->reg->iclr); + generic_handle_irq(chip->irq_base + i); + ret = IRQ_HANDLED; + } + } + return ret; +} + +static __devinit void pch_gpio_alloc_generic_chip(struct pch_gpio *chip, + unsigned int irq_start, unsigned int num) +{ + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + + gc = irq_alloc_generic_chip("pch_gpio", 1, irq_start, chip->base, + handle_simple_irq); + gc->private = chip; + ct = gc->chip_types; + + ct->chip.irq_mask = pch_irq_mask; + ct->chip.irq_unmask = pch_irq_unmask; + ct->chip.irq_set_type = pch_irq_type; + + irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, + IRQ_NOREQUEST | IRQ_NOPROBE, 0); } static int __devinit pch_gpio_probe(struct pci_dev *pdev, @@ -203,6 +355,7 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev, { s32 ret; struct pch_gpio *chip; + int irq_base; chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (chip == NULL) @@ -245,8 +398,36 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev, goto err_gpiochip_add; } + irq_base = irq_alloc_descs(-1, 0, gpio_pins[chip->ioh], GFP_KERNEL); + if (irq_base < 0) { + dev_warn(&pdev->dev, "PCH gpio: Failed to get IRQ base num\n"); + chip->irq_base = -1; + goto end; + } + chip->irq_base = irq_base; + + ret = request_irq(pdev->irq, pch_gpio_handler, + IRQF_SHARED, KBUILD_MODNAME, chip); + if (ret != 0) { + dev_err(&pdev->dev, + "%s request_irq failed\n", __func__); + goto err_request_irq; + } + + pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]); + + /* Initialize interrupt ien register */ + iowrite32(0, &chip->reg->ien); +end: return 0; +err_request_irq: + irq_free_descs(irq_base, gpio_pins[chip->ioh]); + + ret = gpiochip_remove(&chip->gpio); + if (ret) + dev_err(&pdev->dev, "%s gpiochip_remove failed\n", __func__); + err_gpiochip_add: pci_iounmap(pdev, chip->base); @@ -267,6 +448,12 @@ static void __devexit pch_gpio_remove(struct pci_dev *pdev) int err; struct pch_gpio *chip = pci_get_drvdata(pdev); + if (chip->irq_base != -1) { + free_irq(pdev->irq, chip); + + irq_free_descs(chip->irq_base, gpio_pins[chip->ioh]); + } + err = gpiochip_remove(&chip->gpio); if (err) dev_err(&pdev->dev, "Failed gpiochip_remove\n"); -- cgit v1.2.2 From 8c0f7b10f1028d0bc78486affe2ccf39cdf45282 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 3 Oct 2011 14:36:07 +0300 Subject: gpio: langwell: ensure alternate function is cleared Alternate function must be zero for the pin to act as a GPIO. Signed-off-by: Adrian Hunter Signed-off-by: Alan Cox Signed-off-by: Grant Likely --- drivers/gpio/gpio-langwell.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c index d2eb57c60e0e..00692e89ef87 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -59,6 +59,7 @@ enum GPIO_REG { GRER, /* rising edge detect */ GFER, /* falling edge detect */ GEDR, /* edge detect result */ + GAFR, /* alt function */ }; struct lnw_gpio { @@ -81,6 +82,31 @@ static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset, return ptr; } +static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset, + enum GPIO_REG reg_type) +{ + struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + unsigned nreg = chip->ngpio / 32; + u8 reg = offset / 16; + void __iomem *ptr; + + ptr = (void __iomem *)(lnw->reg_base + reg_type * nreg * 4 + reg * 4); + return ptr; +} + +static int lnw_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + void __iomem *gafr = gpio_reg_2bit(chip, offset, GAFR); + u32 value = readl(gafr); + int shift = (offset % 16) << 1, af = (value >> shift) & 3; + + if (af) { + value &= ~(3 << shift); + writel(value, gafr); + } + return 0; +} + static int lnw_gpio_get(struct gpio_chip *chip, unsigned offset) { void __iomem *gplr = gpio_reg(chip, offset, GPLR); @@ -321,6 +347,7 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev, lnw->reg_base = base; lnw->irq_base = irq_base; lnw->chip.label = dev_name(&pdev->dev); + lnw->chip.request = lnw_gpio_request; lnw->chip.direction_input = lnw_gpio_direction_input; lnw->chip.direction_output = lnw_gpio_direction_output; lnw->chip.get = lnw_gpio_get; -- cgit v1.2.2 From 8a8ab2e64e09b56dc1324fd2f7da12346166cad1 Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Mon, 10 Oct 2011 19:55:58 +0900 Subject: gpio/samsung: only register available gpio banks Only register gpio banks provided by SoC instead of the maximum possible to lessen confusion, get rid of a warning from gpiolib and stop it from eating into the extra gpios for configs with S3C24XX_GPIO_EXTRA != 0. Signed-off-by: Peter Korsgaard Signed-off-by: Kukjin Kim --- drivers/gpio/gpio-samsung.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index b6be77ae4973..c87b65af8c96 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -914,6 +914,10 @@ static void __init s3c24xx_gpiolib_add_chips(struct samsung_gpio_chip *chip, struct gpio_chip *gc = &chip->chip; for (i = 0 ; i < nr_chips; i++, chip++) { + /* skip banks not present on SoC */ + if (chip->chip.base >= S3C_GPIO_END) + continue; + if (!chip->config) chip->config = &s3c24xx_gpiocfg_default; if (!chip->pm) -- cgit v1.2.2 From b82cee243633fbb734de704e38f57c771d7afd73 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Wed, 12 Oct 2011 20:11:17 +0900 Subject: gpio/samsung: Fix incorrect gpio pull up/down callback for EXYNOS4 Some of the gpio chips of exynos4 are assigned a default gpio config without the exynos4 specific pull up/down callbacks which resulted in incorrect setting of pull up/down configuration. Fix this by adding two new exynos4 specific entries in the array of default configs with set_pull and get_pull callbacks set to exynos4 specific callbacks The new default gpio configs can then be used for exynos4 gpio chips. Signed-off-by: Thomas Abraham Signed-off-by: Kukjin Kim --- drivers/gpio/gpio-samsung.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index c87b65af8c96..de8788de759d 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -482,7 +482,14 @@ static struct samsung_gpio_cfg samsung_gpio_cfgs[] = { }, { .set_config = samsung_gpio_setcfg_2bit, .get_config = samsung_gpio_getcfg_2bit, - }, + }, { + .set_pull = exynos4_gpio_setpull, + .get_pull = exynos4_gpio_getpull, + }, { + .cfg_eint = 0x3, + .set_pull = exynos4_gpio_setpull, + .get_pull = exynos4_gpio_getpull, + } }; /* @@ -2253,49 +2260,49 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = { .label = "GPL2", }, }, { - .config = &samsung_gpio_cfgs[0], + .config = &samsung_gpio_cfgs[8], .chip = { .base = EXYNOS4_GPY0(0), .ngpio = EXYNOS4_GPIO_Y0_NR, .label = "GPY0", }, }, { - .config = &samsung_gpio_cfgs[0], + .config = &samsung_gpio_cfgs[8], .chip = { .base = EXYNOS4_GPY1(0), .ngpio = EXYNOS4_GPIO_Y1_NR, .label = "GPY1", }, }, { - .config = &samsung_gpio_cfgs[0], + .config = &samsung_gpio_cfgs[8], .chip = { .base = EXYNOS4_GPY2(0), .ngpio = EXYNOS4_GPIO_Y2_NR, .label = "GPY2", }, }, { - .config = &samsung_gpio_cfgs[0], + .config = &samsung_gpio_cfgs[8], .chip = { .base = EXYNOS4_GPY3(0), .ngpio = EXYNOS4_GPIO_Y3_NR, .label = "GPY3", }, }, { - .config = &samsung_gpio_cfgs[0], + .config = &samsung_gpio_cfgs[8], .chip = { .base = EXYNOS4_GPY4(0), .ngpio = EXYNOS4_GPIO_Y4_NR, .label = "GPY4", }, }, { - .config = &samsung_gpio_cfgs[0], + .config = &samsung_gpio_cfgs[8], .chip = { .base = EXYNOS4_GPY5(0), .ngpio = EXYNOS4_GPIO_Y5_NR, .label = "GPY5", }, }, { - .config = &samsung_gpio_cfgs[0], + .config = &samsung_gpio_cfgs[8], .chip = { .base = EXYNOS4_GPY6(0), .ngpio = EXYNOS4_GPIO_Y6_NR, @@ -2303,7 +2310,7 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = { }, }, { .base = (S5P_VA_GPIO2 + 0xC00), - .config = &samsung_gpio_cfgs[3], + .config = &samsung_gpio_cfgs[9], .irq_base = IRQ_EINT(0), .chip = { .base = EXYNOS4_GPX0(0), @@ -2313,7 +2320,7 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = { }, }, { .base = (S5P_VA_GPIO2 + 0xC20), - .config = &samsung_gpio_cfgs[3], + .config = &samsung_gpio_cfgs[9], .irq_base = IRQ_EINT(8), .chip = { .base = EXYNOS4_GPX1(0), @@ -2323,7 +2330,7 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = { }, }, { .base = (S5P_VA_GPIO2 + 0xC40), - .config = &samsung_gpio_cfgs[3], + .config = &samsung_gpio_cfgs[9], .irq_base = IRQ_EINT(16), .chip = { .base = EXYNOS4_GPX2(0), @@ -2333,7 +2340,7 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = { }, }, { .base = (S5P_VA_GPIO2 + 0xC60), - .config = &samsung_gpio_cfgs[3], + .config = &samsung_gpio_cfgs[9], .irq_base = IRQ_EINT(24), .chip = { .base = EXYNOS4_GPX3(0), -- cgit v1.2.2 From c034b184597d93ad7749aca3e8bd1c2105104f07 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Wed, 5 Oct 2011 08:55:49 +0900 Subject: gpio/samsung: Move SoC specific codes within macro In drivers/gpio/gpio-samsung.c, there are certain structures and functions which are not getting used if the particular CPU is not selected. These code segments are moved under CPU specific macros to remove compilation warnings. Signed-off-by: Tushar Behera Acked-by: Grant Likely Signed-off-by: Kukjin Kim --- drivers/gpio/gpio-samsung.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index de8788de759d..479edc3b8ea3 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -318,6 +318,7 @@ static unsigned samsung_gpio_getcfg_4bit(struct samsung_gpio_chip *chip, return S3C_GPIO_SPECIAL(con); } +#ifdef CONFIG_PLAT_S3C24XX /* * s3c24xx_gpio_setcfg_abank - S3C24XX style GPIO configuration (Bank A) * @chip: The gpio chip that is being configured. @@ -379,7 +380,9 @@ static unsigned s3c24xx_gpio_getcfg_abank(struct samsung_gpio_chip *chip, return S3C_GPIO_SFN(con); } +#endif +#if defined(CONFIG_CPU_S5P6440) || defined(CONFIG_CPU_S5P6450) static int s5p64x0_gpio_setcfg_rbank(struct samsung_gpio_chip *chip, unsigned int off, unsigned int cfg) { @@ -417,6 +420,7 @@ static int s5p64x0_gpio_setcfg_rbank(struct samsung_gpio_chip *chip, return 0; } +#endif static void __init samsung_gpiolib_set_cfg(struct samsung_gpio_cfg *chipcfg, int nr_chips) @@ -438,10 +442,12 @@ struct samsung_gpio_cfg s3c24xx_gpiocfg_default = { .get_config = samsung_gpio_getcfg_2bit, }; +#ifdef CONFIG_PLAT_S3C24XX static struct samsung_gpio_cfg s3c24xx_gpiocfg_banka = { .set_config = s3c24xx_gpio_setcfg_abank, .get_config = s3c24xx_gpio_getcfg_abank, }; +#endif static struct samsung_gpio_cfg exynos4_gpio_cfg = { .set_pull = exynos4_gpio_setpull, @@ -450,6 +456,7 @@ static struct samsung_gpio_cfg exynos4_gpio_cfg = { .get_config = samsung_gpio_getcfg_4bit, }; +#if defined(CONFIG_CPU_S5P6440) || defined(CONFIG_CPU_S5P6450) static struct samsung_gpio_cfg s5p64x0_gpio_cfg_rbank = { .cfg_eint = 0x3, .set_config = s5p64x0_gpio_setcfg_rbank, @@ -457,6 +464,7 @@ static struct samsung_gpio_cfg s5p64x0_gpio_cfg_rbank = { .set_pull = samsung_gpio_setpull_updown, .get_pull = samsung_gpio_getpull_updown, }; +#endif static struct samsung_gpio_cfg samsung_gpio_cfgs[] = { { @@ -689,6 +697,7 @@ static int samsung_gpiolib_4bit2_output(struct gpio_chip *chip, return 0; } +#ifdef CONFIG_PLAT_S3C24XX /* The next set of routines are for the case of s3c24xx bank a */ static int s3c24xx_gpiolib_banka_input(struct gpio_chip *chip, unsigned offset) @@ -724,6 +733,7 @@ static int s3c24xx_gpiolib_banka_output(struct gpio_chip *chip, local_irq_restore(flags); return 0; } +#endif /* The next set of routines are for the case of s5p64x0 bank r */ -- cgit v1.2.2 From 78a43158724793f9dc25f1e4c866393654704b87 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Tue, 23 Aug 2011 13:42:24 +0200 Subject: gpio/omap: fix build error with certain OMAP1 configs With commit f64ad1a0e21a, "gpio/omap: cleanup _set_gpio_wakeup(), remove ifdefs", access to build time conditionally omitted 'suspend_wakeup' member of the 'gpio_bank' structure has been placed unconditionally in function _set_gpio_wakeup(), which is always built. This resulted in the driver compilation broken for certain OMAP1, i.e., non-OMAP16xx, configurations. Really required or not in previously excluded cases, define this structure member unconditionally as a fix. Tested with a custom OMAP1510 only configuration. Signed-off-by: Janusz Krzysztofik Acked-by: Kevin Hilman Tested-by: Aaro Koskinen Signed-off-by: Grant Likely --- drivers/gpio/gpio-omap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 0599854e2217..118ec12d2d5f 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -34,8 +34,8 @@ struct gpio_bank { u16 irq; u16 virtual_irq_start; int method; -#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS) u32 suspend_wakeup; +#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS) u32 saved_wakeup; #endif u32 non_wakeup_gpios; -- cgit v1.2.2 From 25fcf2b7f1f65d2cc12182ced3ccd47576970be4 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Tue, 11 Oct 2011 00:22:45 +0200 Subject: gpio-pca953x: fix gpio_base gpio_base was set to 0 if no system platform data or open firmware platform data was provided. This led to conflicts, if any other gpiochip with a gpiobase of 0 was instantiated already. Setting it to -1 will automatically use the first one available. Signed-off-by: Hartmut Knaack Signed-off-by: Grant Likely --- drivers/gpio/gpio-pca953x.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index c43b8ff626a7..0550dcb85814 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -577,6 +577,7 @@ pca953x_get_alt_pdata(struct i2c_client *client, int *gpio_base, int *invert) void pca953x_get_alt_pdata(struct i2c_client *client, int *gpio_base, int *invert) { + *gpio_base = -1; } #endif -- cgit v1.2.2 From 88d8951e5896da908d31bc24735efae801566066 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 11 Oct 2011 16:16:14 -0600 Subject: gpio/tegra: Convert to a platform device v3: Make regs variable static. Remove empty init of tegra_gpio_banks. v2: Retrieve IRQ and memory addresses from resources instead of hard- coding them. Add back initialization of tegra_gpio_chip.of_node. Signed-off-by: Stephen Warren Acked-by: Grant Likely [olof: switched probe routine to __devinit] Signed-off-by: Olof Johansson --- drivers/gpio/gpio-tegra.c | 143 ++++++++++++++++++++++++++++++---------------- 1 file changed, 95 insertions(+), 48 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 747eb40e8afe..75cf91138b69 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -20,10 +20,11 @@ #include #include #include - #include #include #include +#include +#include #include @@ -34,9 +35,7 @@ #define GPIO_PORT(x) (((x) >> 3) & 0x3) #define GPIO_BIT(x) ((x) & 0x7) -#define GPIO_REG(x) (IO_TO_VIRT(TEGRA_GPIO_BASE) + \ - GPIO_BANK(x) * 0x80 + \ - GPIO_PORT(x) * 4) +#define GPIO_REG(x) (GPIO_BANK(x) * 0x80 + GPIO_PORT(x) * 4) #define GPIO_CNF(x) (GPIO_REG(x) + 0x00) #define GPIO_OE(x) (GPIO_REG(x) + 0x10) @@ -75,15 +74,18 @@ struct tegra_gpio_bank { }; -static struct tegra_gpio_bank tegra_gpio_banks[] = { - {.bank = 0, .irq = INT_GPIO1}, - {.bank = 1, .irq = INT_GPIO2}, - {.bank = 2, .irq = INT_GPIO3}, - {.bank = 3, .irq = INT_GPIO4}, - {.bank = 4, .irq = INT_GPIO5}, - {.bank = 5, .irq = INT_GPIO6}, - {.bank = 6, .irq = INT_GPIO7}, -}; +static void __iomem *regs; +static struct tegra_gpio_bank tegra_gpio_banks[7]; + +static inline void tegra_gpio_writel(u32 val, u32 reg) +{ + __raw_writel(val, regs + reg); +} + +static inline u32 tegra_gpio_readl(u32 reg) +{ + return __raw_readl(regs + reg); +} static int tegra_gpio_compose(int bank, int port, int bit) { @@ -97,7 +99,7 @@ static void tegra_gpio_mask_write(u32 reg, int gpio, int value) val = 0x100 << GPIO_BIT(gpio); if (value) val |= 1 << GPIO_BIT(gpio); - __raw_writel(val, reg); + tegra_gpio_writel(val, reg); } void tegra_gpio_enable(int gpio) @@ -117,7 +119,7 @@ static void tegra_gpio_set(struct gpio_chip *chip, unsigned offset, int value) static int tegra_gpio_get(struct gpio_chip *chip, unsigned offset) { - return (__raw_readl(GPIO_IN(offset)) >> GPIO_BIT(offset)) & 0x1; + return (tegra_gpio_readl(GPIO_IN(offset)) >> GPIO_BIT(offset)) & 0x1; } static int tegra_gpio_direction_input(struct gpio_chip *chip, unsigned offset) @@ -150,7 +152,7 @@ static void tegra_gpio_irq_ack(struct irq_data *d) { int gpio = d->irq - INT_GPIO_BASE; - __raw_writel(1 << GPIO_BIT(gpio), GPIO_INT_CLR(gpio)); + tegra_gpio_writel(1 << GPIO_BIT(gpio), GPIO_INT_CLR(gpio)); } static void tegra_gpio_irq_mask(struct irq_data *d) @@ -203,10 +205,10 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) spin_lock_irqsave(&bank->lvl_lock[port], flags); - val = __raw_readl(GPIO_INT_LVL(gpio)); + val = tegra_gpio_readl(GPIO_INT_LVL(gpio)); val &= ~(GPIO_INT_LVL_MASK << GPIO_BIT(gpio)); val |= lvl_type << GPIO_BIT(gpio); - __raw_writel(val, GPIO_INT_LVL(gpio)); + tegra_gpio_writel(val, GPIO_INT_LVL(gpio)); spin_unlock_irqrestore(&bank->lvl_lock[port], flags); @@ -232,12 +234,12 @@ static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) for (port = 0; port < 4; port++) { int gpio = tegra_gpio_compose(bank->bank, port, 0); - unsigned long sta = __raw_readl(GPIO_INT_STA(gpio)) & - __raw_readl(GPIO_INT_ENB(gpio)); - u32 lvl = __raw_readl(GPIO_INT_LVL(gpio)); + unsigned long sta = tegra_gpio_readl(GPIO_INT_STA(gpio)) & + tegra_gpio_readl(GPIO_INT_ENB(gpio)); + u32 lvl = tegra_gpio_readl(GPIO_INT_LVL(gpio)); for_each_set_bit(pin, &sta, 8) { - __raw_writel(1 << pin, GPIO_INT_CLR(gpio)); + tegra_gpio_writel(1 << pin, GPIO_INT_CLR(gpio)); /* if gpio is edge triggered, clear condition * before executing the hander so that we don't @@ -271,11 +273,11 @@ void tegra_gpio_resume(void) for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { unsigned int gpio = (b<<5) | (p<<3); - __raw_writel(bank->cnf[p], GPIO_CNF(gpio)); - __raw_writel(bank->out[p], GPIO_OUT(gpio)); - __raw_writel(bank->oe[p], GPIO_OE(gpio)); - __raw_writel(bank->int_lvl[p], GPIO_INT_LVL(gpio)); - __raw_writel(bank->int_enb[p], GPIO_INT_ENB(gpio)); + tegra_gpio_writel(bank->cnf[p], GPIO_CNF(gpio)); + tegra_gpio_writel(bank->out[p], GPIO_OUT(gpio)); + tegra_gpio_writel(bank->oe[p], GPIO_OE(gpio)); + tegra_gpio_writel(bank->int_lvl[p], GPIO_INT_LVL(gpio)); + tegra_gpio_writel(bank->int_enb[p], GPIO_INT_ENB(gpio)); } } @@ -294,11 +296,11 @@ void tegra_gpio_suspend(void) for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { unsigned int gpio = (b<<5) | (p<<3); - bank->cnf[p] = __raw_readl(GPIO_CNF(gpio)); - bank->out[p] = __raw_readl(GPIO_OUT(gpio)); - bank->oe[p] = __raw_readl(GPIO_OE(gpio)); - bank->int_enb[p] = __raw_readl(GPIO_INT_ENB(gpio)); - bank->int_lvl[p] = __raw_readl(GPIO_INT_LVL(gpio)); + bank->cnf[p] = tegra_gpio_readl(GPIO_CNF(gpio)); + bank->out[p] = tegra_gpio_readl(GPIO_OUT(gpio)); + bank->oe[p] = tegra_gpio_readl(GPIO_OE(gpio)); + bank->int_enb[p] = tegra_gpio_readl(GPIO_INT_ENB(gpio)); + bank->int_lvl[p] = tegra_gpio_readl(GPIO_INT_LVL(gpio)); } } local_irq_restore(flags); @@ -328,27 +330,54 @@ static struct irq_chip tegra_gpio_irq_chip = { */ static struct lock_class_key gpio_lock_class; -static int __init tegra_gpio_init(void) +static int __devinit tegra_gpio_probe(struct platform_device *pdev) { + struct resource *res; struct tegra_gpio_bank *bank; int i; int j; + for (i = 0; i < ARRAY_SIZE(tegra_gpio_banks); i++) { + res = platform_get_resource(pdev, IORESOURCE_IRQ, i); + if (!res) { + dev_err(&pdev->dev, "Missing IRQ resource\n"); + return -ENODEV; + } + + bank = &tegra_gpio_banks[i]; + bank->bank = i; + bank->irq = res->start; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Missing MEM resource\n"); + return -ENODEV; + } + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), + dev_name(&pdev->dev))) { + dev_err(&pdev->dev, "Couldn't request MEM resource\n"); + return -ENODEV; + } + + regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!regs) { + dev_err(&pdev->dev, "Couldn't ioremap regs\n"); + return -ENODEV; + } + for (i = 0; i < 7; i++) { for (j = 0; j < 4; j++) { int gpio = tegra_gpio_compose(i, j, 0); - __raw_writel(0x00, GPIO_INT_ENB(gpio)); + tegra_gpio_writel(0x00, GPIO_INT_ENB(gpio)); } } #ifdef CONFIG_OF_GPIO - /* - * This isn't ideal, but it gets things hooked up until this - * driver is converted into a platform_device - */ - tegra_gpio_chip.of_node = of_find_compatible_node(NULL, NULL, - "nvidia,tegra20-gpio"); -#endif /* CONFIG_OF_GPIO */ + tegra_gpio_chip.of_node = pdev->dev.of_node; +#endif gpiochip_add(&tegra_gpio_chip); @@ -375,6 +404,24 @@ static int __init tegra_gpio_init(void) return 0; } +static struct of_device_id tegra_gpio_of_match[] __devinitdata = { + { .compatible = "nvidia,tegra20-gpio", }, + { }, +}; + +static struct platform_driver tegra_gpio_driver = { + .driver = { + .name = "tegra-gpio", + .owner = THIS_MODULE, + .of_match_table = tegra_gpio_of_match, + }, + .probe = tegra_gpio_probe, +}; + +static int __init tegra_gpio_init(void) +{ + return platform_driver_register(&tegra_gpio_driver); +} postcore_initcall(tegra_gpio_init); void __init tegra_gpio_config(struct tegra_gpio_table *table, int num) @@ -407,13 +454,13 @@ static int dbg_gpio_show(struct seq_file *s, void *unused) seq_printf(s, "%d:%d %02x %02x %02x %02x %02x %02x %06x\n", i, j, - __raw_readl(GPIO_CNF(gpio)), - __raw_readl(GPIO_OE(gpio)), - __raw_readl(GPIO_OUT(gpio)), - __raw_readl(GPIO_IN(gpio)), - __raw_readl(GPIO_INT_STA(gpio)), - __raw_readl(GPIO_INT_ENB(gpio)), - __raw_readl(GPIO_INT_LVL(gpio))); + tegra_gpio_readl(GPIO_CNF(gpio)), + tegra_gpio_readl(GPIO_OE(gpio)), + tegra_gpio_readl(GPIO_OUT(gpio)), + tegra_gpio_readl(GPIO_IN(gpio)), + tegra_gpio_readl(GPIO_INT_STA(gpio)), + tegra_gpio_readl(GPIO_INT_ENB(gpio)), + tegra_gpio_readl(GPIO_INT_LVL(gpio))); } } return 0; -- cgit v1.2.2 From fbe92fcc7570eaba4bd5786cb1bbc5e693dba6bd Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 18 Oct 2011 08:46:50 +0900 Subject: gpio/samsung: Complain loudly if we don't know the SoC If we don't know the SoC type then we won't add any chips which is rather unfortunate as neither GPIO nor pinmux APIs will work, breaking lots of different subsystems. Logging at least provides a hint to the user as to what's gone wrong. Signed-off-by: Mark Brown Signed-off-by: Kukjin Kim --- drivers/gpio/gpio-samsung.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index 479edc3b8ea3..866251852719 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -2486,6 +2486,9 @@ static __init int samsung_gpiolib_init(void) s5p_register_gpioint_bank(IRQ_GPIO_XA, 0, IRQ_GPIO1_NR_GROUPS); s5p_register_gpioint_bank(IRQ_GPIO_XB, IRQ_GPIO1_NR_GROUPS, IRQ_GPIO2_NR_GROUPS); #endif + } else { + WARN(1, "Unknown SoC in gpio-samsung, no GPIOs added\n"); + return -ENODEV; } return 0; -- cgit v1.2.2 From 2824bc9c38dcb9a3e8f2d72a6ede8563c222959f Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Wed, 19 Oct 2011 10:37:39 +0900 Subject: gpio-pch: Use NUMA_NO_NODE not GFP_KERNEL Currently, GFP_KERNEL is used as parameter of irq_alloc_descs like below. irq_base = irq_alloc_descs(-1, IOH_IRQ_BASE, num_ports[j], GFP_KERNEL); This is not true. So, this patch uses NUMA_NO_NODE not GFP_KERNEL. Reported-by: Dan Carpenter Reported-by: David Rientjes Signed-off-by: Tomoya MORINAGA Acked-by: David Rientjes Signed-off-by: Grant Likely --- drivers/gpio/gpio-pch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 46b5209878f6..1e8a4a538810 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -398,7 +398,7 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev, goto err_gpiochip_add; } - irq_base = irq_alloc_descs(-1, 0, gpio_pins[chip->ioh], GFP_KERNEL); + irq_base = irq_alloc_descs(-1, 0, gpio_pins[chip->ioh], NUMA_NO_NODE); if (irq_base < 0) { dev_warn(&pdev->dev, "PCH gpio: Failed to get IRQ base num\n"); chip->irq_base = -1; -- cgit v1.2.2 From a7aaa4f888968b1261c2701cc66f18a3d4b9777b Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Wed, 19 Oct 2011 10:37:40 +0900 Subject: gpio-ml-ioh: Use NUMA_NO_NODE not GFP_KERNEL Currently, GFP_KERNEL is used as parameter of irq_alloc_descs like below. irq_base = irq_alloc_descs(-1, IOH_IRQ_BASE, num_ports[j], GFP_KERNEL); This is not true. So, this patch uses NUMA_NO_NODE not GFP_KERNEL. Reported-by: Dan Carpenter Reported-by: David Rientjes Signed-off-by: Tomoya MORINAGA Acked-by: David Rientjes Signed-off-by: Grant Likely --- drivers/gpio/gpio-ml-ioh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 274fd4d0792f..3aa6beec8c1e 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -428,7 +428,7 @@ static int __devinit ioh_gpio_probe(struct pci_dev *pdev, chip = chip_save; for (j = 0; j < 8; j++, chip++) { irq_base = irq_alloc_descs(-1, IOH_IRQ_BASE, num_ports[j], - GFP_KERNEL); + NUMA_NO_NODE); if (irq_base < 0) { dev_warn(&pdev->dev, "ml_ioh_gpio: Failed to get IRQ base num\n"); -- cgit v1.2.2 From 8a0a8e8e42a4e30a1fc4c40205fa790e264d00f3 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 2 Sep 2011 16:43:36 +0200 Subject: mfd: remove CONFIG_MFD_SUPPORT We currently have two symbols to control compilation the MFD subsystem, MFD_SUPPORT and MFD_CORE. The MFD_SUPPORT is actually not required at all, it only hides the submenu when not set, with the effect that Kconfig warns about missing dependencies when another driver selects an MFD driver while MFD_SUPPORT is disabled. Turning the MFD submenu back from menuconfig into a plain menu simplifies the Kconfig syntax for those kinds of users and avoids the surprise when the menu suddenly appears because another driver was enabled that selects this symbol. Signed-off-by: Arnd Bergmann --- drivers/gpio/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d539efd96d4b..fbc5fd449a04 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -180,7 +180,7 @@ config GPIO_SCH config GPIO_VX855 tristate "VIA VX855/VX875 GPIO" - depends on MFD_SUPPORT && PCI + depends on PCI select MFD_CORE select MFD_VX855 help @@ -417,7 +417,6 @@ config GPIO_TIMBERDALE config GPIO_RDC321X tristate "RDC R-321x GPIO support" depends on PCI - select MFD_SUPPORT select MFD_CORE select MFD_RDC321X help -- cgit v1.2.2 From 76c05c8a0d56faf210cb9681786bb3e17cd59793 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 10 Aug 2011 16:31:46 -0500 Subject: gpio: pl061: add DT binding support This adds devicetree binding support to the ARM pl061 driver removing the platform_data dependency. When DT binding is used, the gpio numbering is assigned dynamically. For now, interrupts are not supported with DT until irqdomains learn dynamic irq assignment. Rather than add another case of -1, updating the driver to use NO_IRQ. Signed-off-by: Rob Herring Acked-by: Baruch Siach Signed-off-by: Grant Likely --- drivers/gpio/gpio-pl061.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 2c5a18f32bf3..093c90bd3c1d 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -118,7 +118,7 @@ static int pl061_to_irq(struct gpio_chip *gc, unsigned offset) { struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); - if (chip->irq_base == (unsigned) -1) + if (chip->irq_base == NO_IRQ) return -EINVAL; return chip->irq_base + offset; @@ -246,6 +246,18 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) if (chip == NULL) return -ENOMEM; + pdata = dev->dev.platform_data; + if (pdata) { + chip->gc.base = pdata->gpio_base; + chip->irq_base = pdata->irq_base; + } else if (dev->dev.of_node) { + chip->gc.base = -1; + chip->irq_base = NO_IRQ; + } else { + ret = -ENODEV; + goto free_mem; + } + if (!request_mem_region(dev->res.start, resource_size(&dev->res), "pl061")) { ret = -EBUSY; @@ -267,14 +279,11 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) chip->gc.get = pl061_get_value; chip->gc.set = pl061_set_value; chip->gc.to_irq = pl061_to_irq; - chip->gc.base = pdata->gpio_base; chip->gc.ngpio = PL061_GPIO_NR; chip->gc.label = dev_name(&dev->dev); chip->gc.dev = &dev->dev; chip->gc.owner = THIS_MODULE; - chip->irq_base = pdata->irq_base; - ret = gpiochip_add(&chip->gc); if (ret) goto iounmap; @@ -283,7 +292,7 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) * irq_chip support */ - if (chip->irq_base == (unsigned) -1) + if (chip->irq_base == NO_IRQ) return 0; writeb(0, chip->base + GPIOIE); /* disable irqs */ @@ -307,11 +316,13 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) list_add(&chip->list, chip_list); for (i = 0; i < PL061_GPIO_NR; i++) { - if (pdata->directions & (1 << i)) - pl061_direction_output(&chip->gc, i, - pdata->values & (1 << i)); - else - pl061_direction_input(&chip->gc, i); + if (pdata) { + if (pdata->directions & (1 << i)) + pl061_direction_output(&chip->gc, i, + pdata->values & (1 << i)); + else + pl061_direction_input(&chip->gc, i); + } irq_set_chip_and_handler(i + chip->irq_base, &pl061_irqchip, handle_simple_irq); -- cgit v1.2.2 From f0eb824beee3f596b9799e667a6fdac3116e9f7d Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 14 Oct 2011 15:31:59 +0200 Subject: gpio: pca953x: remove unneeded check for chip type We can assume our own device_id table is correct, so remove checking if the chip type is valid. (The check was bogus anyway: If it found an invalid entry, it returned with 0!) This is in preparation for further cleanups. Signed-off-by: Wolfram Sang Cc: Grant Likely Signed-off-by: Grant Likely --- drivers/gpio/gpio-pca953x.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index c43b8ff626a7..45de6a4ac632 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -673,10 +673,8 @@ static int __devinit pca953x_probe(struct i2c_client *client, if (chip->chip_type == PCA953X_TYPE) device_pca953x_init(chip, invert); - else if (chip->chip_type == PCA957X_TYPE) - device_pca957x_init(chip, invert); else - goto out_failed; + device_pca957x_init(chip, invert); ret = pca953x_irq_setup(chip, id, irq_base); if (ret) -- cgit v1.2.2 From 7ea2aa2046a15af1c048115e7c05f1ba1566899d Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 14 Oct 2011 15:32:00 +0200 Subject: gpio: pca953x: propagate the errno from the chip_init functions Initializing the chips may return with an error, but this error gets dropped in probe(). Propagate this further to the driver core. Also, simplify returning the error in one of the init functions. Signed-off-by: Wolfram Sang Cc: Grant Likely Signed-off-by: Grant Likely --- drivers/gpio/gpio-pca953x.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 45de6a4ac632..a3fef0c94ba6 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -595,9 +595,6 @@ static int __devinit device_pca953x_init(struct pca953x_chip *chip, int invert) /* set platform specific polarity inversion */ ret = pca953x_write_reg(chip, PCA953X_INVERT, invert); - if (ret) - goto out; - return 0; out: return ret; } @@ -639,7 +636,7 @@ static int __devinit pca953x_probe(struct i2c_client *client, struct pca953x_platform_data *pdata; struct pca953x_chip *chip; int irq_base=0, invert=0; - int ret = 0; + int ret; chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL); if (chip == NULL) @@ -672,9 +669,11 @@ static int __devinit pca953x_probe(struct i2c_client *client, pca953x_setup_gpio(chip, id->driver_data & PCA_GPIO_MASK); if (chip->chip_type == PCA953X_TYPE) - device_pca953x_init(chip, invert); + ret = device_pca953x_init(chip, invert); else - device_pca957x_init(chip, invert); + ret = device_pca957x_init(chip, invert); + if (ret) + goto out_failed; ret = pca953x_irq_setup(chip, id, irq_base); if (ret) -- cgit v1.2.2 From bb207ef1e84ffc4afe89f3a5b84788bac0f968e7 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 3 Jul 2011 13:38:09 -0400 Subject: drivers/gpio: Fix drivers who are implicit users of module.h A pending cleanup will mean that module.h won't be implicitly everywhere anymore. Make sure the modular drivers in gpio are actually calling out for explicitly in advance. Signed-off-by: Paul Gortmaker --- drivers/gpio/gpio-74x164.c | 1 + drivers/gpio/gpio-ep93xx.c | 1 + drivers/gpio/gpio-mc33880.c | 1 + drivers/gpio/gpio-mcp23s08.c | 1 + drivers/gpio/gpio-ml-ioh.c | 1 + drivers/gpio/gpio-mpc5200.c | 1 + drivers/gpio/gpio-mxc.c | 1 + drivers/gpio/gpio-mxs.c | 1 + drivers/gpio/gpio-pcf857x.c | 1 + drivers/gpio/gpio-pch.c | 1 + drivers/gpio/gpio-xilinx.c | 1 + 11 files changed, 11 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index ff525c0958dd..a31ad6f5d910 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -15,6 +15,7 @@ #include #include #include +#include struct gen_74x164_chip { struct spi_device *spi; diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 7aafbb437339..1c0fc3756cb1 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -15,6 +15,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include diff --git a/drivers/gpio/gpio-mc33880.c b/drivers/gpio/gpio-mc33880.c index b3b4652e89ec..2de57ce5feb6 100644 --- a/drivers/gpio/gpio-mc33880.c +++ b/drivers/gpio/gpio-mc33880.c @@ -26,6 +26,7 @@ #include #include #include +#include #define DRIVER_NAME "mc33880" diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c index 1ef46e6c2a2a..c5d83a8a91c2 100644 --- a/drivers/gpio/gpio-mcp23s08.c +++ b/drivers/gpio/gpio-mcp23s08.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 3aa6beec8c1e..ea8e73869250 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -14,6 +14,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ +#include #include #include #include diff --git a/drivers/gpio/gpio-mpc5200.c b/drivers/gpio/gpio-mpc5200.c index 52d3ed208105..2c7cef367fc0 100644 --- a/drivers/gpio/gpio-mpc5200.c +++ b/drivers/gpio/gpio-mpc5200.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 82f7b65baf72..addbea09aece 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index af55a8577c2e..a63b08d66dce 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #define MXS_SET 0x4 diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 7369fdda92b0..3e1f1ecd07be 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -23,6 +23,7 @@ #include #include #include +#include static const struct i2c_device_id pcf857x_id[] = { diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 1e8a4a538810..a6008e123d04 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -14,6 +14,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ +#include #include #include #include diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 846fbd5e31bf..0ce6ac9898b1 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include -- cgit v1.2.2 From 15a5148c6fa6d786d4ed94c6da3b6ed1b2b44f25 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Sat, 22 Oct 2011 16:20:42 -0500 Subject: powerpc/85xx: Add 'fsl,pq3-gpio' compatiable for GPIO driver Support MPC85xx platforms outside of MPC8572/MPC8536. The MPC8572/MPC8536 have an erratum that is worked around based on having "fsl,mpc8572-gpio" in the compatiable list. All other MPC85xx SoCs don't require this workaround and thus utilize the 'fsl,pq3-gpio' compatiable. Signed-off-by: Kumar Gala --- drivers/gpio/gpio-mpc8xxx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index fb4963abdf55..d74d19bf3af5 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -310,6 +310,7 @@ static struct of_device_id mpc8xxx_gpio_ids[] __initdata = { { .compatible = "fsl,mpc8572-gpio", }, { .compatible = "fsl,mpc8610-gpio", }, { .compatible = "fsl,mpc5121-gpio", .data = mpc512x_irq_set_type, }, + { .compatible = "fsl,pq3-gpio", }, { .compatible = "fsl,qoriq-gpio", }, {} }; -- cgit v1.2.2 From cfadd83852eb6b9a015bc1ea396b98f395f073e0 Mon Sep 17 00:00:00 2001 From: Felix Radensky Date: Tue, 11 Oct 2011 10:24:21 +0200 Subject: powerpc/8xxx: Fix interrupt handling in MPC8xxx GPIO driver Interrupt handler in MPC8xxx GPIO driver is missing the call to PIC EOI (end of interrupt) handler. As a result, at least on 85XX systems, GPIO interrupt is delivered only once. This patch adds the missing EOI call. Tested on custom P1022 board. Signed-off-by: Felix Radensky Signed-off-by: Kumar Gala --- drivers/gpio/gpio-mpc8xxx.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index d74d19bf3af5..ec3fcf0a7e12 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -146,6 +146,7 @@ static int mpc8xxx_gpio_to_irq(struct gpio_chip *gc, unsigned offset) static void mpc8xxx_gpio_irq_cascade(unsigned int irq, struct irq_desc *desc) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; unsigned int mask; @@ -153,6 +154,7 @@ static void mpc8xxx_gpio_irq_cascade(unsigned int irq, struct irq_desc *desc) if (mask) generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq, 32 - ffs(mask))); + chip->irq_eoi(&desc->irq_data); } static void mpc8xxx_irq_unmask(struct irq_data *d) -- cgit v1.2.2