From 9450be76d0e3ebedf301aa09e4f98b4d3a175229 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 22 Jul 2012 16:55:44 +0200 Subject: GPIO: gpio-pxa: simplify pxa_gpio_to_irq() and pxa_irq_to_chip() Simplify the code in gpio-pxa.c and make them based on irq_base. When not probed from devicetree, initialize irq_base from PXA_GPIO_TO_IRQ() or MMP_GPIO_TO_IRQ(), respectively, so the non-DT case still works. Only tested on PXA3xx. Signed-off-by: Daniel Mack Acked-by: Arnd Bergmann Acked-by: Linus Walleij Signed-off-by: Haojian Zhuang --- drivers/gpio/gpio-pxa.c | 70 +++++++++++-------------------------------------- 1 file changed, 16 insertions(+), 54 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 58a6a63a6ece..6d0cb9dff27d 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -59,6 +59,7 @@ #define BANK_OFF(n) (((n) < 3) ? (n) << 2 : 0x100 + (((n) - 3) << 2)) int pxa_last_gpio; +static int irq_base; #ifdef CONFIG_OF static struct irq_domain *domain; @@ -166,63 +167,14 @@ static inline int __gpio_is_occupied(unsigned gpio) return ret; } -#ifdef CONFIG_ARCH_PXA -static inline int __pxa_gpio_to_irq(int gpio) -{ - if (gpio_is_pxa_type(gpio_type)) - return PXA_GPIO_TO_IRQ(gpio); - return -1; -} - -static inline int __pxa_irq_to_gpio(int irq) -{ - if (gpio_is_pxa_type(gpio_type)) - return irq - PXA_GPIO_TO_IRQ(0); - return -1; -} -#else -static inline int __pxa_gpio_to_irq(int gpio) { return -1; } -static inline int __pxa_irq_to_gpio(int irq) { return -1; } -#endif - -#ifdef CONFIG_ARCH_MMP -static inline int __mmp_gpio_to_irq(int gpio) -{ - if (gpio_is_mmp_type(gpio_type)) - return MMP_GPIO_TO_IRQ(gpio); - return -1; -} - -static inline int __mmp_irq_to_gpio(int irq) -{ - if (gpio_is_mmp_type(gpio_type)) - return irq - MMP_GPIO_TO_IRQ(0); - return -1; -} -#else -static inline int __mmp_gpio_to_irq(int gpio) { return -1; } -static inline int __mmp_irq_to_gpio(int irq) { return -1; } -#endif - static int pxa_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { - int gpio, ret; - - gpio = chip->base + offset; - ret = __pxa_gpio_to_irq(gpio); - if (ret >= 0) - return ret; - return __mmp_gpio_to_irq(gpio); + return chip->base + offset + irq_base; } int pxa_irq_to_gpio(int irq) { - int ret; - - ret = __pxa_irq_to_gpio(irq); - if (ret >= 0) - return ret; - return __mmp_irq_to_gpio(irq); + return irq - irq_base; } static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned offset) @@ -510,7 +462,7 @@ const struct irq_domain_ops pxa_irq_domain_ops = { #ifdef CONFIG_OF static int __devinit pxa_gpio_probe_dt(struct platform_device *pdev) { - int ret, nr_banks, nr_gpios, irq_base; + int ret, nr_banks, nr_gpios; struct device_node *prev, *next, *np = pdev->dev.of_node; const struct of_device_id *of_id = of_match_device(pxa_gpio_dt_ids, &pdev->dev); @@ -564,10 +516,20 @@ static int __devinit pxa_gpio_probe(struct platform_device *pdev) int irq0 = 0, irq1 = 0, irq_mux, gpio_offset = 0; ret = pxa_gpio_probe_dt(pdev); - if (ret < 0) + if (ret < 0) { pxa_last_gpio = pxa_gpio_nums(); - else +#ifdef CONFIG_ARCH_PXA + if (gpio_is_pxa_type(gpio_type)) + irq_base = PXA_GPIO_TO_IRQ(0); +#endif +#ifdef CONFIG_ARCH_MMP + if (gpio_is_mmp_type(gpio_type)) + irq_base = MMP_GPIO_TO_IRQ(0); +#endif + } else { use_of = 1; + } + if (!pxa_last_gpio) return -EINVAL; -- cgit v1.2.3 From 0d2ee5d773c209504db643ec4d4b9f3624a6663f Mon Sep 17 00:00:00 2001 From: Chao Xie Date: Tue, 31 Jul 2012 14:13:09 +0800 Subject: gpio: pxa: add chain_eneter and chain_exit for irq handler Signed-off-by: Chao Xie Signed-off-by: Haojian Zhuang --- drivers/gpio/gpio-pxa.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 6d0cb9dff27d..db5c2958f973 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -26,6 +26,8 @@ #include #include +#include + #include /* @@ -331,6 +333,9 @@ 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; + struct irq_chip *chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); do { loop = 0; @@ -350,6 +355,8 @@ static void pxa_gpio_demux_handler(unsigned int irq, struct irq_desc *desc) } } } while (loop); + + chained_irq_exit(chip, desc); } static void pxa_ack_muxed_gpio(struct irq_data *d) -- cgit v1.2.3 From 172c6a13653ac8cd6a231293b87c93821e90c1d6 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Fri, 7 Sep 2012 06:49:47 +0900 Subject: gpio: samsung: add devicetree init for s3c24xx arches Until now the EXYNOS-SoC was the only Samsung-SoC supporting the GPIOs via the device tree. This patch implements dt-support for the s3c24xx arches. The controllers contain only 3 cells, as the underlying gpio controller does not support controlling the drive strength on a gpio level. Tested with the gpio-keys driver on a s3c2416 based machine. Signed-off-by: Heiko Stuebner Reviewed-by: Thomas Abraham Acked-by: Linus Walleij [kgene.kim@samsung.com: fixed build error] Signed-off-by: Kukjin Kim --- .../devicetree/bindings/gpio/gpio-samsung.txt | 43 +++++++++++++++ drivers/gpio/gpio-samsung.c | 63 ++++++++++++++++++++++ 2 files changed, 106 insertions(+) (limited to 'drivers/gpio') diff --git a/Documentation/devicetree/bindings/gpio/gpio-samsung.txt b/Documentation/devicetree/bindings/gpio/gpio-samsung.txt index 5375625e8cd2..f1e5dfecf55d 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-samsung.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-samsung.txt @@ -39,3 +39,46 @@ Example: #gpio-cells = <4>; gpio-controller; }; + + +Samsung S3C24XX GPIO Controller + +Required properties: +- compatible: Compatible property value should be "samsung,s3c24xx-gpio". + +- reg: Physical base address of the controller and length of memory mapped + region. + +- #gpio-cells: Should be 3. The syntax of the gpio specifier used by client nodes + should be the following with values derived from the SoC user manual. + <[phandle of the gpio controller node] + [pin number within the gpio controller] + [mux function] + [flags and pull up/down] + + Values for gpio specifier: + - Pin number: depending on the controller a number from 0 up to 15. + - Mux function: Depending on the SoC and the gpio bank the gpio can be set + as input, output or a special function + - Flags and Pull Up/Down: the values to use differ for the individual SoCs + example S3C2416/S3C2450: + 0 - Pull Up/Down Disabled. + 1 - Pull Down Enabled. + 2 - Pull Up Enabled. + Bit 16 (0x00010000) - Input is active low. + Consult the user manual for the correct values of Mux and Pull Up/Down. + +- gpio-controller: Specifies that the node is a gpio controller. +- #address-cells: should be 1. +- #size-cells: should be 1. + +Example: + + gpa: gpio-controller@56000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "samsung,s3c24xx-gpio"; + reg = <0x56000000 0x10>; + #gpio-cells = <3>; + gpio-controller; + }; diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index ba126cc04073..f4814a889a5b 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -938,6 +938,67 @@ static void __init samsung_gpiolib_add(struct samsung_gpio_chip *chip) s3c_gpiolib_track(chip); } +#if defined(CONFIG_PLAT_S3C24XX) && defined(CONFIG_OF) +static int s3c24xx_gpio_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, u32 *flags) +{ + unsigned int pin; + + if (WARN_ON(gc->of_gpio_n_cells < 3)) + return -EINVAL; + + if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells)) + return -EINVAL; + + if (gpiospec->args[0] > gc->ngpio) + return -EINVAL; + + pin = gc->base + gpiospec->args[0]; + + if (s3c_gpio_cfgpin(pin, S3C_GPIO_SFN(gpiospec->args[1]))) + pr_warn("gpio_xlate: failed to set pin function\n"); + if (s3c_gpio_setpull(pin, gpiospec->args[2] & 0xffff)) + pr_warn("gpio_xlate: failed to set pin pull up/down\n"); + + if (flags) + *flags = gpiospec->args[2] >> 16; + + return gpiospec->args[0]; +} + +static const struct of_device_id s3c24xx_gpio_dt_match[] __initdata = { + { .compatible = "samsung,s3c24xx-gpio", }, + {} +}; + +static __init void s3c24xx_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip, + u64 base, u64 offset) +{ + struct gpio_chip *gc = &chip->chip; + u64 address; + + if (!of_have_populated_dt()) + return; + + address = chip->base ? base + ((u32)chip->base & 0xfff) : base + offset; + gc->of_node = of_find_matching_node_by_address(NULL, + s3c24xx_gpio_dt_match, address); + if (!gc->of_node) { + pr_info("gpio: device tree node not found for gpio controller" + " with base address %08llx\n", address); + return; + } + gc->of_gpio_n_cells = 3; + gc->of_xlate = s3c24xx_gpio_xlate; +} +#else +static __init void s3c24xx_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip, + u64 base, u64 offset) +{ + return; +} +#endif /* defined(CONFIG_PLAT_S3C24XX) && defined(CONFIG_OF) */ + static void __init s3c24xx_gpiolib_add_chips(struct samsung_gpio_chip *chip, int nr_chips, void __iomem *base) { @@ -962,6 +1023,8 @@ static void __init s3c24xx_gpiolib_add_chips(struct samsung_gpio_chip *chip, gc->direction_output = samsung_gpiolib_2bit_output; samsung_gpiolib_add(chip); + + s3c24xx_gpiolib_attach_ofnode(chip, S3C24XX_PA_GPIO, i * 0x10); } } -- cgit v1.2.3 From f74ce8fb849e9f9c54a494cff5fc30d53ca4e963 Mon Sep 17 00:00:00 2001 From: Florian Vaussard Date: Wed, 5 Sep 2012 09:46:25 +0200 Subject: gpio/twl4030: get platform data from device tree Adds a number of missing device tree properties for twl4030/gpio, and update bindings: - "ti,use-leds" -> .use_leds - "ti,debounce" -> .debounce - "ti,mmc-cd" -> .mmc_cd - "ti,pullups" -> .pullups - "ti,pulldowns" -> .pulldowns Signed-off-by: Florian Vaussard Acked-by: Linus Walleij Acked-by: Vaibhav Hiremath [b-cousson@ti.com: Fix some checkpatch CHECK issues] Signed-off-by: Benoit Cousson --- .../devicetree/bindings/gpio/gpio-twl4030.txt | 6 ++ drivers/gpio/gpio-twl4030.c | 82 +++++++++++++++------- 2 files changed, 61 insertions(+), 27 deletions(-) (limited to 'drivers/gpio') diff --git a/Documentation/devicetree/bindings/gpio/gpio-twl4030.txt b/Documentation/devicetree/bindings/gpio/gpio-twl4030.txt index 16695d9cf1e8..66788fda1db3 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-twl4030.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-twl4030.txt @@ -11,6 +11,11 @@ Required properties: - interrupt-controller: Mark the device node as an interrupt controller The first cell is the GPIO number. The second cell is not used. +- ti,use-leds : Enables LEDA and LEDB outputs if set +- ti,debounce : if n-th bit is set, debounces GPIO-n +- ti,mmc-cd : if n-th bit is set, GPIO-n controls VMMC(n+1) +- ti,pullups : if n-th bit is set, set a pullup on GPIO-n +- ti,pulldowns : if n-th bit is set, set a pulldown on GPIO-n Example: @@ -20,4 +25,5 @@ twl_gpio: gpio { gpio-controller; #interrupt-cells = <2>; interrupt-controller; + ti,use-leds; }; diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index 94256fe7bf36..f923252da839 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -395,6 +395,31 @@ static int __devinit gpio_twl4030_debounce(u32 debounce, u8 mmc_cd) static int gpio_twl4030_remove(struct platform_device *pdev); +static struct twl4030_gpio_platform_data *of_gpio_twl4030(struct device *dev) +{ + struct twl4030_gpio_platform_data *omap_twl_info; + + omap_twl_info = devm_kzalloc(dev, sizeof(*omap_twl_info), GFP_KERNEL); + if (!omap_twl_info) + return NULL; + + omap_twl_info->gpio_base = -1; + + omap_twl_info->use_leds = of_property_read_bool(dev->of_node, + "ti,use-leds"); + + of_property_read_u32(dev->of_node, "ti,debounce", + &omap_twl_info->debounce); + of_property_read_u32(dev->of_node, "ti,mmc-cd", + (u32 *)&omap_twl_info->mmc_cd); + of_property_read_u32(dev->of_node, "ti,pullups", + &omap_twl_info->pullups); + of_property_read_u32(dev->of_node, "ti,pulldowns", + &omap_twl_info->pulldowns); + + return omap_twl_info; +} + static int __devinit gpio_twl4030_probe(struct platform_device *pdev) { struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data; @@ -423,39 +448,42 @@ static int __devinit gpio_twl4030_probe(struct platform_device *pdev) twl4030_gpio_irq_base = irq_base; no_irqs: - twl_gpiochip.base = -1; twl_gpiochip.ngpio = TWL4030_GPIO_MAX; twl_gpiochip.dev = &pdev->dev; - if (pdata) { - twl_gpiochip.base = pdata->gpio_base; + if (node) + pdata = of_gpio_twl4030(&pdev->dev); - /* - * NOTE: boards may waste power if they don't set pullups - * and pulldowns correctly ... default for non-ULPI pins is - * pulldown, and some other pins may have external pullups - * or pulldowns. Careful! - */ - ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns); - if (ret) - dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n", - pdata->pullups, pdata->pulldowns, - ret); - - ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd); - if (ret) - dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n", - pdata->debounce, pdata->mmc_cd, - ret); - - /* - * NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE, - * is (still) clear if use_leds is set. - */ - if (pdata->use_leds) - twl_gpiochip.ngpio += 2; + if (pdata == NULL) { + dev_err(&pdev->dev, "Platform data is missing\n"); + return -ENXIO; } + twl_gpiochip.base = pdata->gpio_base; + + /* + * NOTE: boards may waste power if they don't set pullups + * and pulldowns correctly ... default for non-ULPI pins is + * pulldown, and some other pins may have external pullups + * or pulldowns. Careful! + */ + ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns); + if (ret) + dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n", + pdata->pullups, pdata->pulldowns, ret); + + ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd); + if (ret) + dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n", + pdata->debounce, pdata->mmc_cd, ret); + + /* + * NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE, + * is (still) clear if use_leds is set. + */ + if (pdata->use_leds) + twl_gpiochip.ngpio += 2; + ret = gpiochip_add(&twl_gpiochip); if (ret < 0) { dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret); -- cgit v1.2.3 From 4e48b1c56019868b34efcd0e2334b7cdfac14092 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Wed, 8 Aug 2012 13:05:55 +1200 Subject: arm: vt8500: gpio: Devicetree support for arch-vt8500 Converted the existing arch-vt8500 gpio to a platform_device. Added support for WM8505 and WM8650 GPIO controllers. Replaced existing readl/writel calls with _relaxed variants. Replaced existing unsigned variables with u32 to match register size. Signed-off-by: Tony Prisk Reviewed-by: Linus Walleij --- drivers/gpio/Kconfig | 6 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-vt8500.c | 316 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 323 insertions(+) create mode 100644 drivers/gpio/gpio-vt8500.c (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ba7926f5c099..a00b828b1643 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -183,6 +183,12 @@ config GPIO_STA2X11 Say yes here to support the STA2x11/ConneXt GPIO device. The GPIO module has 128 GPIO pins with alternate functions. +config GPIO_VT8500 + bool "VIA/Wondermedia SoC GPIO Support" + depends on ARCH_VT8500 + help + Say yes here to support the VT8500/WM8505/WM8650 GPIO controller. + config GPIO_XILINX bool "Xilinx GPIO support" depends on PPC_OF || MICROBLAZE diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 153caceeb053..a288142ad998 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o +obj-$(CONFIG_GPIO_VT8500) += gpio-vt8500.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o diff --git a/drivers/gpio/gpio-vt8500.c b/drivers/gpio/gpio-vt8500.c new file mode 100644 index 000000000000..bcd8e4aa7c7d --- /dev/null +++ b/drivers/gpio/gpio-vt8500.c @@ -0,0 +1,316 @@ +/* drivers/gpio/gpio-vt8500.c + * + * Copyright (C) 2012 Tony Prisk + * Based on arch/arm/mach-vt8500/gpio.c: + * - Copyright (C) 2010 Alexey Charkov + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include +#include + +/* + We handle GPIOs by bank, each bank containing up to 32 GPIOs covered + by one set of registers (although not all may be valid). + + Because different SoC's have different register offsets, we pass the + register offsets as data in vt8500_gpio_dt_ids[]. + + A value of NO_REG is used to indicate that this register is not + supported. Only used for ->en at the moment. +*/ + +#define NO_REG 0xFFFF + +/* + * struct vt8500_gpio_bank_regoffsets + * @en: offset to enable register of the bank + * @dir: offset to direction register of the bank + * @data_out: offset to the data out register of the bank + * @data_in: offset to the data in register of the bank + * @ngpio: highest valid pin in this bank + */ + +struct vt8500_gpio_bank_regoffsets { + unsigned int en; + unsigned int dir; + unsigned int data_out; + unsigned int data_in; + unsigned char ngpio; +}; + +struct vt8500_gpio_data { + unsigned int num_banks; + struct vt8500_gpio_bank_regoffsets banks[]; +}; + +#define VT8500_BANK(__en, __dir, __out, __in, __ngpio) \ +{ \ + .en = __en, \ + .dir = __dir, \ + .data_out = __out, \ + .data_in = __in, \ + .ngpio = __ngpio, \ +} + +static struct vt8500_gpio_data vt8500_data = { + .num_banks = 7, + .banks = { + VT8500_BANK(0x00, 0x20, 0x40, 0x60, 26), + VT8500_BANK(0x04, 0x24, 0x44, 0x64, 28), + VT8500_BANK(0x08, 0x28, 0x48, 0x68, 31), + VT8500_BANK(0x0C, 0x2C, 0x4C, 0x6C, 19), + VT8500_BANK(0x10, 0x30, 0x50, 0x70, 19), + VT8500_BANK(0x14, 0x34, 0x54, 0x74, 23), + VT8500_BANK(NO_REG, 0x3C, 0x5C, 0x7C, 9), + }, +}; + +static struct vt8500_gpio_data wm8505_data = { + .num_banks = 10, + .banks = { + VT8500_BANK(0x40, 0x68, 0x90, 0xB8, 8), + VT8500_BANK(0x44, 0x6C, 0x94, 0xBC, 32), + VT8500_BANK(0x48, 0x70, 0x98, 0xC0, 6), + VT8500_BANK(0x4C, 0x74, 0x9C, 0xC4, 16), + VT8500_BANK(0x50, 0x78, 0xA0, 0xC8, 25), + VT8500_BANK(0x54, 0x7C, 0xA4, 0xCC, 5), + VT8500_BANK(0x58, 0x80, 0xA8, 0xD0, 5), + VT8500_BANK(0x5C, 0x84, 0xAC, 0xD4, 12), + VT8500_BANK(0x60, 0x88, 0xB0, 0xD8, 16), + VT8500_BANK(0x64, 0x8C, 0xB4, 0xDC, 22), + }, +}; + +/* + * No information about which bits are valid so we just make + * them all available until its figured out. + */ +static struct vt8500_gpio_data wm8650_data = { + .num_banks = 9, + .banks = { + VT8500_BANK(0x40, 0x80, 0xC0, 0x00, 32), + VT8500_BANK(0x44, 0x84, 0xC4, 0x04, 32), + VT8500_BANK(0x48, 0x88, 0xC8, 0x08, 32), + VT8500_BANK(0x4C, 0x8C, 0xCC, 0x0C, 32), + VT8500_BANK(0x50, 0x90, 0xD0, 0x10, 32), + VT8500_BANK(0x54, 0x94, 0xD4, 0x14, 32), + VT8500_BANK(0x58, 0x98, 0xD8, 0x18, 32), + VT8500_BANK(0x5C, 0x9C, 0xDC, 0x1C, 32), + VT8500_BANK(0x7C, 0xBC, 0xFC, 0x3C, 32), + }, +}; + +struct vt8500_gpio_chip { + struct gpio_chip chip; + + const struct vt8500_gpio_bank_regoffsets *regs; + void __iomem *base; +}; + + +#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip) + +static int vt8500_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + u32 val; + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + + if (vt8500_chip->regs->en == NO_REG) + return 0; + + val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->en); + val |= BIT(offset); + writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->en); + + return 0; +} + +static void vt8500_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + u32 val; + + if (vt8500_chip->regs->en == NO_REG) + return; + + val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->en); + val &= ~BIT(offset); + writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->en); +} + +static int vt8500_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + + u32 val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->dir); + val &= ~BIT(offset); + writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->dir); + + return 0; +} + +static int vt8500_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + + u32 val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->dir); + val |= BIT(offset); + writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->dir); + + if (value) { + val = readl_relaxed(vt8500_chip->base + + vt8500_chip->regs->data_out); + val |= BIT(offset); + writel_relaxed(val, vt8500_chip->base + + vt8500_chip->regs->data_out); + } + return 0; +} + +static int vt8500_gpio_get_value(struct gpio_chip *chip, unsigned offset) +{ + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + + return (readl_relaxed(vt8500_chip->base + vt8500_chip->regs->data_in) >> + offset) & 1; +} + +static void vt8500_gpio_set_value(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + + u32 val = readl_relaxed(vt8500_chip->base + + vt8500_chip->regs->data_out); + if (value) + val |= BIT(offset); + else + val &= ~BIT(offset); + + writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->data_out); +} + +static int vt8500_of_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, u32 *flags) +{ + /* bank if specificed in gpiospec->args[0] */ + if (flags) + *flags = gpiospec->args[2]; + + return gpiospec->args[1]; +} + +static int vt8500_add_chips(struct platform_device *pdev, void __iomem *base, + const struct vt8500_gpio_data *data) +{ + struct vt8500_gpio_chip *vtchip; + struct gpio_chip *chip; + int i; + int pin_cnt = 0; + + vtchip = devm_kzalloc(&pdev->dev, + sizeof(struct vt8500_gpio_chip) * data->num_banks, + GFP_KERNEL); + if (!vtchip) { + pr_err("%s: failed to allocate chip memory\n", __func__); + return -ENOMEM; + } + + for (i = 0; i < data->num_banks; i++) { + vtchip[i].base = base; + vtchip[i].regs = &data->banks[i]; + + chip = &vtchip[i].chip; + + chip->of_xlate = vt8500_of_xlate; + chip->of_gpio_n_cells = 3; + chip->of_node = pdev->dev.of_node; + + chip->request = vt8500_gpio_request; + chip->free = vt8500_gpio_free; + chip->direction_input = vt8500_gpio_direction_input; + chip->direction_output = vt8500_gpio_direction_output; + chip->get = vt8500_gpio_get_value; + chip->set = vt8500_gpio_set_value; + chip->can_sleep = 0; + chip->base = pin_cnt; + chip->ngpio = data->banks[i].ngpio; + + pin_cnt += data->banks[i].ngpio; + + gpiochip_add(chip); + } + return 0; +} + +static struct of_device_id vt8500_gpio_dt_ids[] = { + { .compatible = "via,vt8500-gpio", .data = &vt8500_data, }, + { .compatible = "wm,wm8505-gpio", .data = &wm8505_data, }, + { .compatible = "wm,wm8650-gpio", .data = &wm8650_data, }, + { /* Sentinel */ }, +}; + +static int __devinit vt8500_gpio_probe(struct platform_device *pdev) +{ + void __iomem *gpio_base; + struct device_node *np; + const struct of_device_id *of_id = + of_match_device(vt8500_gpio_dt_ids, &pdev->dev); + + if (!of_id) { + dev_err(&pdev->dev, "Failed to find gpio controller\n"); + return -ENODEV; + } + + np = pdev->dev.of_node; + if (!np) { + dev_err(&pdev->dev, "Missing GPIO description in devicetree\n"); + return -EFAULT; + } + + gpio_base = of_iomap(np, 0); + if (!gpio_base) { + dev_err(&pdev->dev, "Unable to map GPIO registers\n"); + of_node_put(np); + return -ENOMEM; + } + + vt8500_add_chips(pdev, gpio_base, of_id->data); + + return 0; +} + +static struct platform_driver vt8500_gpio_driver = { + .probe = vt8500_gpio_probe, + .driver = { + .name = "vt8500-gpio", + .owner = THIS_MODULE, + .of_match_table = vt8500_gpio_dt_ids, + }, +}; + +module_platform_driver(vt8500_gpio_driver); + +MODULE_DESCRIPTION("VT8500 GPIO Driver"); +MODULE_AUTHOR("Tony Prisk "); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, vt8500_gpio_dt_ids); -- cgit v1.2.3