From 06caa7ad8341db2f03165fa763559475cc404584 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 24 May 2011 23:06:52 +0200 Subject: gpio: move U300 GPIO driver to drivers/gpio This moves the U300 GPIO driver out of arch/arm/mach-u300 and into the desired location indicated by the subsystem maintainer. Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- arch/arm/mach-u300/Makefile | 2 +- arch/arm/mach-u300/gpio.c | 700 -------------------------------------------- 2 files changed, 1 insertion(+), 701 deletions(-) delete mode 100644 arch/arm/mach-u300/gpio.c (limited to 'arch/arm') diff --git a/arch/arm/mach-u300/Makefile b/arch/arm/mach-u300/Makefile index fab46fe9a71f..8fd354aaf0a7 100644 --- a/arch/arm/mach-u300/Makefile +++ b/arch/arm/mach-u300/Makefile @@ -2,7 +2,7 @@ # Makefile for the linux kernel, U300 machine. # -obj-y := core.o clock.o timer.o gpio.o padmux.o +obj-y := core.o clock.o timer.o padmux.o obj-m := obj-n := obj- := diff --git a/arch/arm/mach-u300/gpio.c b/arch/arm/mach-u300/gpio.c deleted file mode 100644 index d92790140fe5..000000000000 --- a/arch/arm/mach-u300/gpio.c +++ /dev/null @@ -1,700 +0,0 @@ -/* - * - * arch/arm/mach-u300/gpio.c - * - * - * Copyright (C) 2007-2009 ST-Ericsson AB - * License terms: GNU General Public License (GPL) version 2 - * U300 GPIO module. - * 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: Jonas Aaberg - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Reference to GPIO block clock */ -static struct clk *clk; - -/* Memory resource */ -static struct resource *memres; -static void __iomem *virtbase; -static struct device *gpiodev; - -struct u300_gpio_port { - const char *name; - int irq; - int number; -}; - - -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 - -}; - - -#ifdef U300_COH901571_3 - -/* Default input value */ -#define DEFAULT_OUTPUT_LOW 0 -#define DEFAULT_OUTPUT_HIGH 1 - -/* GPIO Pull-Up status */ -#define DISABLE_PULL_UP 0 -#define ENABLE_PULL_UP 1 - -#define GPIO_NOT_USED 0 -#define GPIO_IN 1 -#define GPIO_OUT 2 - -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 - /* 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} - }, - /* 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} - }, - /* 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} - }, - /* 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} - }, - /* 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} - }, - /* 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} - }, - /* 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} - } -#endif - -#ifdef CONFIG_MACH_U300_BS365 - /* 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} - }, - /* 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} - }, - /* 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} - }, - /* 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} - }, - /* 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}, - /* 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} - } -#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 - */ -int gpio_register_callback(unsigned gpio, int (*func)(void *arg), void *data) -{ - 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; -} -EXPORT_SYMBOL(gpio_register_callback); - -int gpio_unregister_callback(unsigned gpio) -{ - 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; - - return 0; -} -EXPORT_SYMBOL(gpio_unregister_callback); - -/* Non-zero means valid */ -int gpio_is_valid(int number) -{ - if (number >= 0 && - number < (U300_GPIO_NUM_PORTS * U300_GPIO_PINS_PER_PORT)) - return 1; - return 0; -} -EXPORT_SYMBOL(gpio_is_valid); - -int gpio_request(unsigned gpio, const char *label) -{ - if (gpio_pin[gpio].users) - return -EINVAL; - else - gpio_pin[gpio].users++; - - gpio_users++; - - return 0; -} -EXPORT_SYMBOL(gpio_request); - -void gpio_free(unsigned gpio) -{ - 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); - -/* 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)); -} -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) -{ - u32 val; - unsigned long flags; - - 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); - } - local_irq_restore(flags); -} -EXPORT_SYMBOL(gpio_set_value); - -int gpio_direction_input(unsigned gpio) -{ - 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); - local_irq_restore(flags); - return 0; -} -EXPORT_SYMBOL(gpio_direction_input); - -int gpio_direction_output(unsigned gpio, int value) -{ - 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; -} -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) -{ - 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); -} -EXPORT_SYMBOL(enable_irq_on_gpio_pin); - -void disable_irq_on_gpio_pin(unsigned gpio) -{ - 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); - 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) -{ - 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); - } - local_irq_restore(flags); -} -EXPORT_SYMBOL(gpio_pullup); - -static irqreturn_t gpio_irq_handler(int irq, void *dev_id) -{ - struct u300_gpio_port *port = dev_id; - u32 val; - int pin; - - /* 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); - /* Mask relevant bits */ - val &= U300_GPIO_PXIEV_ALL_IRQ_EVENT_MASK; - /* 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); - } - return IRQ_HANDLED; -} - -static void gpio_set_initial_values(void) -{ -#ifdef U300_COH901571_3 - int i, j; - unsigned long flags; - u32 val; - - /* 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); - } - - /* - * 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); - } - } - - /* 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); - } -#endif -} - -static int __init gpio_probe(struct platform_device *pdev) -{ - u32 val; - int err = 0; - int i; - int num_irqs; - - gpiodev = &pdev->dev; - memset(gpio_pin, 0, sizeof(gpio_pin)); - - /* 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"); - goto err_no_clk; - } - err = clk_enable(clk); - if (err) { - dev_err(gpiodev, "could not enable GPIO clock\n"); - goto err_no_clk_enable; - } - - memres = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!memres) - goto err_no_resource; - - if (request_mem_region(memres->start, memres->end - memres->start, "GPIO Controller") - == NULL) { - err = -ENODEV; - goto err_no_ioregion; - } - - virtbase = ioremap(memres->start, resource_size(memres)); - if (!virtbase) { - 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; - } - /* Turns off PortX_irq_force */ - writel(0x0, virtbase + U300_GPIO_PXIFR + - num_irqs * U300_GPIO_PORTX_SPACING); - } - - 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, memres->end - memres->start); - 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); - return err; -} - -static int __exit gpio_remove(struct platform_device *pdev) -{ - int i; - - /* 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, memres->end - memres->start); - clk_disable(clk); - clk_put(clk); - return 0; -} - -static struct platform_driver gpio_driver = { - .driver = { - .name = "u300-gpio", - }, - .remove = __exit_p(gpio_remove), -}; - - -static int __init u300_gpio_init(void) -{ - return platform_driver_probe(&gpio_driver, gpio_probe); -} - -static void __exit u300_gpio_exit(void) -{ - platform_driver_unregister(&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_LICENSE("GPL"); -- cgit v1.2.3 From 37d72457644a1ded37d57dd9ae664e4e228a034d Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 26 May 2011 17:30:03 -0600 Subject: gpio: move Nomadik GPIO driver to drivers/gpio This moves the Nomadik GPIO driver out of arch/arm/plat-nomadik and into the desired location indicated by the subsystem maintainer. Signed-off-by: Linus Walleij [grant.likely: squashed with kconfig fixup] Signed-off-by: Grant Likely --- arch/arm/mach-nomadik/Kconfig | 1 - arch/arm/mach-ux500/Kconfig | 1 - arch/arm/plat-nomadik/Kconfig | 5 - arch/arm/plat-nomadik/Makefile | 1 - arch/arm/plat-nomadik/gpio.c | 1020 ---------------------------------------- drivers/gpio/Makefile | 1 + drivers/gpio/gpio-nomadik.c | 1020 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1021 insertions(+), 1028 deletions(-) delete mode 100644 arch/arm/plat-nomadik/gpio.c create mode 100644 drivers/gpio/gpio-nomadik.c (limited to 'arch/arm') diff --git a/arch/arm/mach-nomadik/Kconfig b/arch/arm/mach-nomadik/Kconfig index 71f3ea623974..3c5e0f522e9c 100644 --- a/arch/arm/mach-nomadik/Kconfig +++ b/arch/arm/mach-nomadik/Kconfig @@ -6,7 +6,6 @@ config MACH_NOMADIK_8815NHK bool "ST 8815 Nomadik Hardware Kit (evaluation board)" select NOMADIK_8815 select HAS_MTU - select NOMADIK_GPIO endmenu diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig index 54429d015954..f8b9392ee347 100644 --- a/arch/arm/mach-ux500/Kconfig +++ b/arch/arm/mach-ux500/Kconfig @@ -5,7 +5,6 @@ config UX500_SOC_COMMON default y select ARM_GIC select HAS_MTU - select NOMADIK_GPIO select ARM_ERRATA_753970 menu "Ux500 SoC" diff --git a/arch/arm/plat-nomadik/Kconfig b/arch/arm/plat-nomadik/Kconfig index 18296ee68802..ce659015535e 100644 --- a/arch/arm/plat-nomadik/Kconfig +++ b/arch/arm/plat-nomadik/Kconfig @@ -21,9 +21,4 @@ config HAS_MTU to multiple interrupt generating programmable 32-bit free running decrementing counters. -config NOMADIK_GPIO - bool - help - Support for the Nomadik GPIO controller. - endif diff --git a/arch/arm/plat-nomadik/Makefile b/arch/arm/plat-nomadik/Makefile index c33547361bd7..37c7cdd0f8f0 100644 --- a/arch/arm/plat-nomadik/Makefile +++ b/arch/arm/plat-nomadik/Makefile @@ -3,4 +3,3 @@ # Licensed under GPLv2 obj-$(CONFIG_HAS_MTU) += timer.o -obj-$(CONFIG_NOMADIK_GPIO) += gpio.o diff --git a/arch/arm/plat-nomadik/gpio.c b/arch/arm/plat-nomadik/gpio.c deleted file mode 100644 index 307b8131aa8c..000000000000 --- a/arch/arm/plat-nomadik/gpio.c +++ /dev/null @@ -1,1020 +0,0 @@ -/* - * Generic GPIO driver for logic cells found in the Nomadik SoC - * - * Copyright (C) 2008,2009 STMicroelectronics - * Copyright (C) 2009 Alessandro Rubini - * Rewritten based on work by Prafulla WADASKAR - * - * 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 - -/* - * The GPIO module in the Nomadik family of Systems-on-Chip is an - * AMBA device, managing 32 pins and alternate functions. The logic block - * is currently used in the Nomadik and ux500. - * - * Symbols in this file are called "nmk_gpio" for "nomadik gpio" - */ - -#define NMK_GPIO_PER_CHIP 32 - -struct nmk_gpio_chip { - struct gpio_chip chip; - void __iomem *addr; - struct clk *clk; - unsigned int bank; - unsigned int parent_irq; - int secondary_parent_irq; - u32 (*get_secondary_status)(unsigned int bank); - void (*set_ioforce)(bool enable); - spinlock_t lock; - /* Keep track of configured edges */ - u32 edge_rising; - u32 edge_falling; - u32 real_wake; - u32 rwimsc; - u32 fwimsc; - u32 slpm; - u32 enabled; -}; - -static struct nmk_gpio_chip * -nmk_gpio_chips[DIV_ROUND_UP(ARCH_NR_GPIOS, NMK_GPIO_PER_CHIP)]; - -static DEFINE_SPINLOCK(nmk_gpio_slpm_lock); - -#define NUM_BANKS ARRAY_SIZE(nmk_gpio_chips) - -static void __nmk_gpio_set_mode(struct nmk_gpio_chip *nmk_chip, - unsigned offset, int gpio_mode) -{ - u32 bit = 1 << offset; - u32 afunc, bfunc; - - afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & ~bit; - bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & ~bit; - if (gpio_mode & NMK_GPIO_ALT_A) - afunc |= bit; - if (gpio_mode & NMK_GPIO_ALT_B) - bfunc |= bit; - writel(afunc, nmk_chip->addr + NMK_GPIO_AFSLA); - writel(bfunc, nmk_chip->addr + NMK_GPIO_AFSLB); -} - -static void __nmk_gpio_set_slpm(struct nmk_gpio_chip *nmk_chip, - unsigned offset, enum nmk_gpio_slpm mode) -{ - u32 bit = 1 << offset; - u32 slpm; - - slpm = readl(nmk_chip->addr + NMK_GPIO_SLPC); - if (mode == NMK_GPIO_SLPM_NOCHANGE) - slpm |= bit; - else - slpm &= ~bit; - writel(slpm, nmk_chip->addr + NMK_GPIO_SLPC); -} - -static void __nmk_gpio_set_pull(struct nmk_gpio_chip *nmk_chip, - unsigned offset, enum nmk_gpio_pull pull) -{ - u32 bit = 1 << offset; - u32 pdis; - - pdis = readl(nmk_chip->addr + NMK_GPIO_PDIS); - if (pull == NMK_GPIO_PULL_NONE) - pdis |= bit; - else - pdis &= ~bit; - writel(pdis, nmk_chip->addr + NMK_GPIO_PDIS); - - if (pull == NMK_GPIO_PULL_UP) - writel(bit, nmk_chip->addr + NMK_GPIO_DATS); - else if (pull == NMK_GPIO_PULL_DOWN) - writel(bit, nmk_chip->addr + NMK_GPIO_DATC); -} - -static void __nmk_gpio_make_input(struct nmk_gpio_chip *nmk_chip, - unsigned offset) -{ - writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRC); -} - -static void __nmk_gpio_set_output(struct nmk_gpio_chip *nmk_chip, - unsigned offset, int val) -{ - if (val) - writel(1 << offset, nmk_chip->addr + NMK_GPIO_DATS); - else - writel(1 << offset, nmk_chip->addr + NMK_GPIO_DATC); -} - -static void __nmk_gpio_make_output(struct nmk_gpio_chip *nmk_chip, - unsigned offset, int val) -{ - writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRS); - __nmk_gpio_set_output(nmk_chip, offset, val); -} - -static void __nmk_gpio_set_mode_safe(struct nmk_gpio_chip *nmk_chip, - unsigned offset, int gpio_mode, - bool glitch) -{ - u32 rwimsc = readl(nmk_chip->addr + NMK_GPIO_RWIMSC); - u32 fwimsc = readl(nmk_chip->addr + NMK_GPIO_FWIMSC); - - if (glitch && nmk_chip->set_ioforce) { - u32 bit = BIT(offset); - - /* Prevent spurious wakeups */ - writel(rwimsc & ~bit, nmk_chip->addr + NMK_GPIO_RWIMSC); - writel(fwimsc & ~bit, nmk_chip->addr + NMK_GPIO_FWIMSC); - - nmk_chip->set_ioforce(true); - } - - __nmk_gpio_set_mode(nmk_chip, offset, gpio_mode); - - if (glitch && nmk_chip->set_ioforce) { - nmk_chip->set_ioforce(false); - - writel(rwimsc, nmk_chip->addr + NMK_GPIO_RWIMSC); - writel(fwimsc, nmk_chip->addr + NMK_GPIO_FWIMSC); - } -} - -static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, - pin_cfg_t cfg, bool sleep, unsigned int *slpmregs) -{ - static const char *afnames[] = { - [NMK_GPIO_ALT_GPIO] = "GPIO", - [NMK_GPIO_ALT_A] = "A", - [NMK_GPIO_ALT_B] = "B", - [NMK_GPIO_ALT_C] = "C" - }; - static const char *pullnames[] = { - [NMK_GPIO_PULL_NONE] = "none", - [NMK_GPIO_PULL_UP] = "up", - [NMK_GPIO_PULL_DOWN] = "down", - [3] /* illegal */ = "??" - }; - static const char *slpmnames[] = { - [NMK_GPIO_SLPM_INPUT] = "input/wakeup", - [NMK_GPIO_SLPM_NOCHANGE] = "no-change/no-wakeup", - }; - - int pin = PIN_NUM(cfg); - int pull = PIN_PULL(cfg); - int af = PIN_ALT(cfg); - int slpm = PIN_SLPM(cfg); - int output = PIN_DIR(cfg); - int val = PIN_VAL(cfg); - bool glitch = af == NMK_GPIO_ALT_C; - - dev_dbg(nmk_chip->chip.dev, "pin %d [%#lx]: af %s, pull %s, slpm %s (%s%s)\n", - pin, cfg, afnames[af], pullnames[pull], slpmnames[slpm], - output ? "output " : "input", - output ? (val ? "high" : "low") : ""); - - if (sleep) { - int slpm_pull = PIN_SLPM_PULL(cfg); - int slpm_output = PIN_SLPM_DIR(cfg); - int slpm_val = PIN_SLPM_VAL(cfg); - - af = NMK_GPIO_ALT_GPIO; - - /* - * The SLPM_* values are normal values + 1 to allow zero to - * mean "same as normal". - */ - if (slpm_pull) - pull = slpm_pull - 1; - if (slpm_output) - output = slpm_output - 1; - if (slpm_val) - val = slpm_val - 1; - - dev_dbg(nmk_chip->chip.dev, "pin %d: sleep pull %s, dir %s, val %s\n", - pin, - slpm_pull ? pullnames[pull] : "same", - slpm_output ? (output ? "output" : "input") : "same", - slpm_val ? (val ? "high" : "low") : "same"); - } - - if (output) - __nmk_gpio_make_output(nmk_chip, offset, val); - else { - __nmk_gpio_make_input(nmk_chip, offset); - __nmk_gpio_set_pull(nmk_chip, offset, pull); - } - - /* - * If we've backed up the SLPM registers (glitch workaround), modify - * the backups since they will be restored. - */ - if (slpmregs) { - if (slpm == NMK_GPIO_SLPM_NOCHANGE) - slpmregs[nmk_chip->bank] |= BIT(offset); - else - slpmregs[nmk_chip->bank] &= ~BIT(offset); - } else - __nmk_gpio_set_slpm(nmk_chip, offset, slpm); - - __nmk_gpio_set_mode_safe(nmk_chip, offset, af, glitch); -} - -/* - * Safe sequence used to switch IOs between GPIO and Alternate-C mode: - * - Save SLPM registers - * - Set SLPM=0 for the IOs you want to switch and others to 1 - * - Configure the GPIO registers for the IOs that are being switched - * - Set IOFORCE=1 - * - Modify the AFLSA/B registers for the IOs that are being switched - * - Set IOFORCE=0 - * - Restore SLPM registers - * - Any spurious wake up event during switch sequence to be ignored and - * cleared - */ -static void nmk_gpio_glitch_slpm_init(unsigned int *slpm) -{ - int i; - - for (i = 0; i < NUM_BANKS; i++) { - struct nmk_gpio_chip *chip = nmk_gpio_chips[i]; - unsigned int temp = slpm[i]; - - if (!chip) - break; - - slpm[i] = readl(chip->addr + NMK_GPIO_SLPC); - writel(temp, chip->addr + NMK_GPIO_SLPC); - } -} - -static void nmk_gpio_glitch_slpm_restore(unsigned int *slpm) -{ - int i; - - for (i = 0; i < NUM_BANKS; i++) { - struct nmk_gpio_chip *chip = nmk_gpio_chips[i]; - - if (!chip) - break; - - writel(slpm[i], chip->addr + NMK_GPIO_SLPC); - } -} - -static int __nmk_config_pins(pin_cfg_t *cfgs, int num, bool sleep) -{ - static unsigned int slpm[NUM_BANKS]; - unsigned long flags; - bool glitch = false; - int ret = 0; - int i; - - for (i = 0; i < num; i++) { - if (PIN_ALT(cfgs[i]) == NMK_GPIO_ALT_C) { - glitch = true; - break; - } - } - - spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); - - if (glitch) { - memset(slpm, 0xff, sizeof(slpm)); - - for (i = 0; i < num; i++) { - int pin = PIN_NUM(cfgs[i]); - int offset = pin % NMK_GPIO_PER_CHIP; - - if (PIN_ALT(cfgs[i]) == NMK_GPIO_ALT_C) - slpm[pin / NMK_GPIO_PER_CHIP] &= ~BIT(offset); - } - - nmk_gpio_glitch_slpm_init(slpm); - } - - for (i = 0; i < num; i++) { - struct nmk_gpio_chip *nmk_chip; - int pin = PIN_NUM(cfgs[i]); - - nmk_chip = irq_get_chip_data(NOMADIK_GPIO_TO_IRQ(pin)); - if (!nmk_chip) { - ret = -EINVAL; - break; - } - - 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); - } - - if (glitch) - nmk_gpio_glitch_slpm_restore(slpm); - - spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); - - return ret; -} - -/** - * nmk_config_pin - configure a pin's mux attributes - * @cfg: pin confguration - * - * Configures a pin's mode (alternate function or GPIO), its pull up status, - * and its sleep mode based on the specified configuration. The @cfg is - * usually one of the SoC specific macros defined in mach/-pins.h. These - * are constructed using, and can be further enhanced with, the macros in - * plat/pincfg.h. - * - * If a pin's mode is set to GPIO, it is configured as an input to avoid - * side-effects. The gpio can be manipulated later using standard GPIO API - * calls. - */ -int nmk_config_pin(pin_cfg_t cfg, bool sleep) -{ - return __nmk_config_pins(&cfg, 1, sleep); -} -EXPORT_SYMBOL(nmk_config_pin); - -/** - * nmk_config_pins - configure several pins at once - * @cfgs: array of pin configurations - * @num: number of elments in the array - * - * Configures several pins using nmk_config_pin(). Refer to that function for - * further information. - */ -int nmk_config_pins(pin_cfg_t *cfgs, int num) -{ - return __nmk_config_pins(cfgs, num, false); -} -EXPORT_SYMBOL(nmk_config_pins); - -int nmk_config_pins_sleep(pin_cfg_t *cfgs, int num) -{ - return __nmk_config_pins(cfgs, num, true); -} -EXPORT_SYMBOL(nmk_config_pins_sleep); - -/** - * nmk_gpio_set_slpm() - configure the sleep mode of a pin - * @gpio: pin number - * @mode: NMK_GPIO_SLPM_INPUT or NMK_GPIO_SLPM_NOCHANGE, - * - * Sets the sleep mode of a pin. If @mode is NMK_GPIO_SLPM_INPUT, the pin is - * changed to an input (with pullup/down enabled) in sleep and deep sleep. If - * @mode is NMK_GPIO_SLPM_NOCHANGE, the pin remains in the state it was - * configured even when in sleep and deep sleep. - * - * On DB8500v2 onwards, this setting loses the previous meaning and instead - * indicates if wakeup detection is enabled on the pin. Note that - * enable_irq_wake() will automatically enable wakeup detection. - */ -int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode) -{ - struct nmk_gpio_chip *nmk_chip; - unsigned long flags; - - nmk_chip = irq_get_chip_data(NOMADIK_GPIO_TO_IRQ(gpio)); - if (!nmk_chip) - return -EINVAL; - - spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); - spin_lock(&nmk_chip->lock); - - __nmk_gpio_set_slpm(nmk_chip, gpio - nmk_chip->chip.base, mode); - - spin_unlock(&nmk_chip->lock); - spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); - - return 0; -} - -/** - * nmk_gpio_set_pull() - enable/disable pull up/down on a gpio - * @gpio: pin number - * @pull: one of NMK_GPIO_PULL_DOWN, NMK_GPIO_PULL_UP, and NMK_GPIO_PULL_NONE - * - * Enables/disables pull up/down on a specified pin. This only takes effect if - * the pin is configured as an input (either explicitly or by the alternate - * function). - * - * NOTE: If enabling the pull up/down, the caller must ensure that the GPIO is - * configured as an input. Otherwise, due to the way the controller registers - * work, this function will change the value output on the pin. - */ -int nmk_gpio_set_pull(int gpio, enum nmk_gpio_pull pull) -{ - struct nmk_gpio_chip *nmk_chip; - unsigned long flags; - - nmk_chip = irq_get_chip_data(NOMADIK_GPIO_TO_IRQ(gpio)); - if (!nmk_chip) - return -EINVAL; - - 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); - - return 0; -} - -/* Mode functions */ -/** - * nmk_gpio_set_mode() - set the mux mode of a gpio pin - * @gpio: pin number - * @gpio_mode: one of NMK_GPIO_ALT_GPIO, NMK_GPIO_ALT_A, - * NMK_GPIO_ALT_B, and NMK_GPIO_ALT_C - * - * Sets the mode of the specified pin to one of the alternate functions or - * plain GPIO. - */ -int nmk_gpio_set_mode(int gpio, int gpio_mode) -{ - struct nmk_gpio_chip *nmk_chip; - unsigned long flags; - - nmk_chip = irq_get_chip_data(NOMADIK_GPIO_TO_IRQ(gpio)); - if (!nmk_chip) - return -EINVAL; - - 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); - - return 0; -} -EXPORT_SYMBOL(nmk_gpio_set_mode); - -int nmk_gpio_get_mode(int gpio) -{ - struct nmk_gpio_chip *nmk_chip; - u32 afunc, bfunc, bit; - - nmk_chip = irq_get_chip_data(NOMADIK_GPIO_TO_IRQ(gpio)); - if (!nmk_chip) - return -EINVAL; - - bit = 1 << (gpio - nmk_chip->chip.base); - - afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & bit; - bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & bit; - - return (afunc ? NMK_GPIO_ALT_A : 0) | (bfunc ? NMK_GPIO_ALT_B : 0); -} -EXPORT_SYMBOL(nmk_gpio_get_mode); - - -/* IRQ functions */ -static inline int nmk_gpio_get_bitmask(int gpio) -{ - return 1 << (gpio % 32); -} - -static void nmk_gpio_irq_ack(struct irq_data *d) -{ - int gpio; - struct nmk_gpio_chip *nmk_chip; - - gpio = NOMADIK_IRQ_TO_GPIO(d->irq); - nmk_chip = irq_data_get_irq_chip_data(d); - if (!nmk_chip) - return; - writel(nmk_gpio_get_bitmask(gpio), nmk_chip->addr + NMK_GPIO_IC); -} - -enum nmk_gpio_irq_type { - NORMAL, - WAKE, -}; - -static void __nmk_gpio_irq_modify(struct nmk_gpio_chip *nmk_chip, - int gpio, enum nmk_gpio_irq_type which, - bool enable) -{ - u32 rimsc = which == WAKE ? NMK_GPIO_RWIMSC : NMK_GPIO_RIMSC; - u32 fimsc = which == WAKE ? NMK_GPIO_FWIMSC : NMK_GPIO_FIMSC; - u32 bitmask = nmk_gpio_get_bitmask(gpio); - u32 reg; - - /* we must individually set/clear the two edges */ - if (nmk_chip->edge_rising & bitmask) { - reg = readl(nmk_chip->addr + rimsc); - if (enable) - reg |= bitmask; - else - reg &= ~bitmask; - writel(reg, nmk_chip->addr + rimsc); - } - if (nmk_chip->edge_falling & bitmask) { - reg = readl(nmk_chip->addr + fimsc); - if (enable) - reg |= bitmask; - else - reg &= ~bitmask; - writel(reg, nmk_chip->addr + fimsc); - } -} - -static void __nmk_gpio_set_wake(struct nmk_gpio_chip *nmk_chip, - int gpio, bool on) -{ - __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, on); -} - -static int nmk_gpio_irq_maskunmask(struct irq_data *d, bool enable) -{ - int gpio; - struct nmk_gpio_chip *nmk_chip; - unsigned long flags; - u32 bitmask; - - gpio = NOMADIK_IRQ_TO_GPIO(d->irq); - nmk_chip = irq_data_get_irq_chip_data(d); - bitmask = nmk_gpio_get_bitmask(gpio); - if (!nmk_chip) - return -EINVAL; - - if (enable) - nmk_chip->enabled |= bitmask; - else - nmk_chip->enabled &= ~bitmask; - - spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); - spin_lock(&nmk_chip->lock); - - __nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, enable); - - if (!(nmk_chip->real_wake & bitmask)) - __nmk_gpio_set_wake(nmk_chip, gpio, enable); - - spin_unlock(&nmk_chip->lock); - spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); - - return 0; -} - -static void nmk_gpio_irq_mask(struct irq_data *d) -{ - nmk_gpio_irq_maskunmask(d, false); -} - -static void nmk_gpio_irq_unmask(struct irq_data *d) -{ - nmk_gpio_irq_maskunmask(d, true); -} - -static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on) -{ - struct nmk_gpio_chip *nmk_chip; - unsigned long flags; - u32 bitmask; - int gpio; - - gpio = NOMADIK_IRQ_TO_GPIO(d->irq); - nmk_chip = irq_data_get_irq_chip_data(d); - if (!nmk_chip) - return -EINVAL; - bitmask = nmk_gpio_get_bitmask(gpio); - - spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); - spin_lock(&nmk_chip->lock); - - if (!(nmk_chip->enabled & bitmask)) - __nmk_gpio_set_wake(nmk_chip, gpio, on); - - if (on) - nmk_chip->real_wake |= bitmask; - else - nmk_chip->real_wake &= ~bitmask; - - spin_unlock(&nmk_chip->lock); - spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); - - return 0; -} - -static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type) -{ - bool enabled, wake = irqd_is_wakeup_set(d); - int gpio; - struct nmk_gpio_chip *nmk_chip; - unsigned long flags; - u32 bitmask; - - gpio = NOMADIK_IRQ_TO_GPIO(d->irq); - nmk_chip = irq_data_get_irq_chip_data(d); - bitmask = nmk_gpio_get_bitmask(gpio); - if (!nmk_chip) - return -EINVAL; - - if (type & IRQ_TYPE_LEVEL_HIGH) - return -EINVAL; - if (type & IRQ_TYPE_LEVEL_LOW) - return -EINVAL; - - enabled = nmk_chip->enabled & bitmask; - - spin_lock_irqsave(&nmk_chip->lock, flags); - - if (enabled) - __nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, false); - - if (enabled || wake) - __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, false); - - nmk_chip->edge_rising &= ~bitmask; - if (type & IRQ_TYPE_EDGE_RISING) - nmk_chip->edge_rising |= bitmask; - - nmk_chip->edge_falling &= ~bitmask; - if (type & IRQ_TYPE_EDGE_FALLING) - nmk_chip->edge_falling |= bitmask; - - if (enabled) - __nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, true); - - if (enabled || wake) - __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, true); - - spin_unlock_irqrestore(&nmk_chip->lock, flags); - - return 0; -} - -static struct irq_chip nmk_gpio_irq_chip = { - .name = "Nomadik-GPIO", - .irq_ack = nmk_gpio_irq_ack, - .irq_mask = nmk_gpio_irq_mask, - .irq_unmask = nmk_gpio_irq_unmask, - .irq_set_type = nmk_gpio_irq_set_type, - .irq_set_wake = nmk_gpio_irq_set_wake, -}; - -static void __nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc, - u32 status) -{ - struct nmk_gpio_chip *nmk_chip; - struct irq_chip *host_chip = irq_get_chip(irq); - unsigned int first_irq; - - chained_irq_enter(host_chip, desc); - - nmk_chip = irq_get_handler_data(irq); - first_irq = NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base); - while (status) { - int bit = __ffs(status); - - generic_handle_irq(first_irq + bit); - status &= ~BIT(bit); - } - - chained_irq_exit(host_chip, 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); - - __nmk_gpio_irq_handler(irq, desc, status); -} - -static void nmk_gpio_secondary_irq_handler(unsigned int irq, - struct irq_desc *desc) -{ - struct nmk_gpio_chip *nmk_chip = irq_get_handler_data(irq); - u32 status = nmk_chip->get_secondary_status(nmk_chip->bank); - - __nmk_gpio_irq_handler(irq, desc, status); -} - -static int nmk_gpio_init_irq(struct nmk_gpio_chip *nmk_chip) -{ - unsigned int first_irq; - int i; - - first_irq = NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base); - for (i = first_irq; i < first_irq + nmk_chip->chip.ngpio; i++) { - irq_set_chip_and_handler(i, &nmk_gpio_irq_chip, - handle_edge_irq); - set_irq_flags(i, IRQF_VALID); - irq_set_chip_data(i, nmk_chip); - irq_set_irq_type(i, IRQ_TYPE_EDGE_FALLING); - } - - irq_set_chained_handler(nmk_chip->parent_irq, nmk_gpio_irq_handler); - irq_set_handler_data(nmk_chip->parent_irq, nmk_chip); - - if (nmk_chip->secondary_parent_irq >= 0) { - irq_set_chained_handler(nmk_chip->secondary_parent_irq, - nmk_gpio_secondary_irq_handler); - irq_set_handler_data(nmk_chip->secondary_parent_irq, nmk_chip); - } - - return 0; -} - -/* I/O Functions */ -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); - - writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRC); - return 0; -} - -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; - - return (readl(nmk_chip->addr + NMK_GPIO_DAT) & bit) != 0; -} - -static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned offset, - int val) -{ - struct nmk_gpio_chip *nmk_chip = - container_of(chip, struct nmk_gpio_chip, chip); - - __nmk_gpio_set_output(nmk_chip, offset, val); -} - -static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset, - int val) -{ - struct nmk_gpio_chip *nmk_chip = - container_of(chip, struct nmk_gpio_chip, chip); - - __nmk_gpio_make_output(nmk_chip, offset, val); - - return 0; -} - -static int nmk_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct nmk_gpio_chip *nmk_chip = - container_of(chip, struct nmk_gpio_chip, chip); - - return NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base) + offset; -} - -#ifdef CONFIG_DEBUG_FS - -#include - -static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) -{ - int mode; - unsigned i; - unsigned gpio = chip->base; - int is_out; - struct nmk_gpio_chip *nmk_chip = - container_of(chip, struct nmk_gpio_chip, chip); - const char *modes[] = { - [NMK_GPIO_ALT_GPIO] = "gpio", - [NMK_GPIO_ALT_A] = "altA", - [NMK_GPIO_ALT_B] = "altB", - [NMK_GPIO_ALT_C] = "altC", - }; - - for (i = 0; i < chip->ngpio; i++, gpio++) { - const char *label = gpiochip_is_requested(chip, i); - bool pull; - u32 bit = 1 << i; - - if (!label) - continue; - - is_out = readl(nmk_chip->addr + NMK_GPIO_DIR) & bit; - pull = !(readl(nmk_chip->addr + NMK_GPIO_PDIS) & bit); - mode = nmk_gpio_get_mode(gpio); - seq_printf(s, " gpio-%-3d (%-20.20s) %s %s %s %s", - gpio, label, - is_out ? "out" : "in ", - chip->get - ? (chip->get(chip, i) ? "hi" : "lo") - : "? ", - (mode < 0) ? "unknown" : modes[mode], - pull ? "pull" : "none"); - seq_printf(s, "\n"); - } -} - -#else -#define nmk_gpio_dbg_show NULL -#endif - -/* This structure is replicated for each GPIO block allocated at probe time */ -static struct gpio_chip nmk_gpio_template = { - .direction_input = nmk_gpio_make_input, - .get = nmk_gpio_get_input, - .direction_output = nmk_gpio_make_output, - .set = nmk_gpio_set_output, - .to_irq = nmk_gpio_to_irq, - .dbg_show = nmk_gpio_dbg_show, - .can_sleep = 0, -}; - -/* - * 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, - * and not the rest of the interrupts which we needed to have as wakeups for - * cpuidle. - * - * PM ops are not used since this needs to be done at the end, after all the - * other drivers are done with their suspend callbacks. - */ -void nmk_gpio_wakeups_suspend(void) -{ - int i; - - for (i = 0; i < NUM_BANKS; i++) { - struct nmk_gpio_chip *chip = nmk_gpio_chips[i]; - - if (!chip) - break; - - chip->rwimsc = readl(chip->addr + NMK_GPIO_RWIMSC); - chip->fwimsc = readl(chip->addr + NMK_GPIO_FWIMSC); - - writel(chip->rwimsc & chip->real_wake, - chip->addr + NMK_GPIO_RWIMSC); - writel(chip->fwimsc & chip->real_wake, - chip->addr + NMK_GPIO_FWIMSC); - - if (cpu_is_u8500v2()) { - chip->slpm = readl(chip->addr + NMK_GPIO_SLPC); - - /* 0 -> wakeup enable */ - writel(~chip->real_wake, chip->addr + NMK_GPIO_SLPC); - } - } -} - -void nmk_gpio_wakeups_resume(void) -{ - int i; - - for (i = 0; i < NUM_BANKS; i++) { - struct nmk_gpio_chip *chip = nmk_gpio_chips[i]; - - if (!chip) - break; - - writel(chip->rwimsc, chip->addr + NMK_GPIO_RWIMSC); - writel(chip->fwimsc, chip->addr + NMK_GPIO_FWIMSC); - - if (cpu_is_u8500v2()) - writel(chip->slpm, chip->addr + NMK_GPIO_SLPC); - } -} - -static int __devinit nmk_gpio_probe(struct platform_device *dev) -{ - struct nmk_gpio_platform_data *pdata = dev->dev.platform_data; - struct nmk_gpio_chip *nmk_chip; - struct gpio_chip *chip; - struct resource *res; - struct clk *clk; - int secondary_irq; - int irq; - int ret; - - if (!pdata) - return -ENODEV; - - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENOENT; - goto out; - } - - irq = platform_get_irq(dev, 0); - if (irq < 0) { - ret = irq; - goto out; - } - - secondary_irq = platform_get_irq(dev, 1); - if (secondary_irq >= 0 && !pdata->get_secondary_status) { - ret = -EINVAL; - goto out; - } - - if (request_mem_region(res->start, resource_size(res), - dev_name(&dev->dev)) == NULL) { - ret = -EBUSY; - goto out; - } - - clk = clk_get(&dev->dev, NULL); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - goto out_release; - } - - clk_enable(clk); - - nmk_chip = kzalloc(sizeof(*nmk_chip), GFP_KERNEL); - if (!nmk_chip) { - ret = -ENOMEM; - goto out_clk; - } - /* - * The virt address in nmk_chip->addr is in the nomadik register space, - * so we can simply convert the resource address, without remapping - */ - nmk_chip->bank = dev->id; - nmk_chip->clk = clk; - nmk_chip->addr = io_p2v(res->start); - nmk_chip->chip = nmk_gpio_template; - nmk_chip->parent_irq = irq; - nmk_chip->secondary_parent_irq = secondary_irq; - nmk_chip->get_secondary_status = pdata->get_secondary_status; - nmk_chip->set_ioforce = pdata->set_ioforce; - spin_lock_init(&nmk_chip->lock); - - chip = &nmk_chip->chip; - chip->base = pdata->first_gpio; - chip->ngpio = pdata->num_gpio; - chip->label = pdata->name ?: dev_name(&dev->dev); - chip->dev = &dev->dev; - chip->owner = THIS_MODULE; - - ret = gpiochip_add(&nmk_chip->chip); - if (ret) - goto out_free; - - BUG_ON(nmk_chip->bank >= ARRAY_SIZE(nmk_gpio_chips)); - - nmk_gpio_chips[nmk_chip->bank] = nmk_chip; - platform_set_drvdata(dev, nmk_chip); - - nmk_gpio_init_irq(nmk_chip); - - dev_info(&dev->dev, "Bits %i-%i at address %p\n", - nmk_chip->chip.base, nmk_chip->chip.base+31, nmk_chip->addr); - return 0; - -out_free: - kfree(nmk_chip); -out_clk: - clk_disable(clk); - clk_put(clk); -out_release: - release_mem_region(res->start, resource_size(res)); -out: - dev_err(&dev->dev, "Failure %i for GPIO %i-%i\n", ret, - pdata->first_gpio, pdata->first_gpio+31); - return ret; -} - -static struct platform_driver nmk_gpio_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "gpio", - }, - .probe = nmk_gpio_probe, -}; - -static int __init nmk_gpio_init(void) -{ - return platform_driver_register(&nmk_gpio_driver); -} - -core_initcall(nmk_gpio_init); - -MODULE_AUTHOR("Prafulla WADASKAR and Alessandro Rubini"); -MODULE_DESCRIPTION("Nomadik GPIO Driver"); -MODULE_LICENSE("GPL"); - - diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 27a22086739b..7481e7da2adb 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_GPIO_WM8350) += wm8350-gpiolib.o obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio.o obj-$(CONFIG_GPIO_SCH) += sch_gpio.o obj-$(CONFIG_MACH_U300) += gpio-u300.o +obj-$(CONFIG_PLAT_NOMADIK) += gpio-nomadik.o obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o obj-$(CONFIG_GPIO_JANZ_TTL) += janz-ttl.o obj-$(CONFIG_GPIO_SX150X) += sx150x.o diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c new file mode 100644 index 000000000000..307b8131aa8c --- /dev/null +++ b/drivers/gpio/gpio-nomadik.c @@ -0,0 +1,1020 @@ +/* + * Generic GPIO driver for logic cells found in the Nomadik SoC + * + * Copyright (C) 2008,2009 STMicroelectronics + * Copyright (C) 2009 Alessandro Rubini + * Rewritten based on work by Prafulla WADASKAR + * + * 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 + +/* + * The GPIO module in the Nomadik family of Systems-on-Chip is an + * AMBA device, managing 32 pins and alternate functions. The logic block + * is currently used in the Nomadik and ux500. + * + * Symbols in this file are called "nmk_gpio" for "nomadik gpio" + */ + +#define NMK_GPIO_PER_CHIP 32 + +struct nmk_gpio_chip { + struct gpio_chip chip; + void __iomem *addr; + struct clk *clk; + unsigned int bank; + unsigned int parent_irq; + int secondary_parent_irq; + u32 (*get_secondary_status)(unsigned int bank); + void (*set_ioforce)(bool enable); + spinlock_t lock; + /* Keep track of configured edges */ + u32 edge_rising; + u32 edge_falling; + u32 real_wake; + u32 rwimsc; + u32 fwimsc; + u32 slpm; + u32 enabled; +}; + +static struct nmk_gpio_chip * +nmk_gpio_chips[DIV_ROUND_UP(ARCH_NR_GPIOS, NMK_GPIO_PER_CHIP)]; + +static DEFINE_SPINLOCK(nmk_gpio_slpm_lock); + +#define NUM_BANKS ARRAY_SIZE(nmk_gpio_chips) + +static void __nmk_gpio_set_mode(struct nmk_gpio_chip *nmk_chip, + unsigned offset, int gpio_mode) +{ + u32 bit = 1 << offset; + u32 afunc, bfunc; + + afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & ~bit; + bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & ~bit; + if (gpio_mode & NMK_GPIO_ALT_A) + afunc |= bit; + if (gpio_mode & NMK_GPIO_ALT_B) + bfunc |= bit; + writel(afunc, nmk_chip->addr + NMK_GPIO_AFSLA); + writel(bfunc, nmk_chip->addr + NMK_GPIO_AFSLB); +} + +static void __nmk_gpio_set_slpm(struct nmk_gpio_chip *nmk_chip, + unsigned offset, enum nmk_gpio_slpm mode) +{ + u32 bit = 1 << offset; + u32 slpm; + + slpm = readl(nmk_chip->addr + NMK_GPIO_SLPC); + if (mode == NMK_GPIO_SLPM_NOCHANGE) + slpm |= bit; + else + slpm &= ~bit; + writel(slpm, nmk_chip->addr + NMK_GPIO_SLPC); +} + +static void __nmk_gpio_set_pull(struct nmk_gpio_chip *nmk_chip, + unsigned offset, enum nmk_gpio_pull pull) +{ + u32 bit = 1 << offset; + u32 pdis; + + pdis = readl(nmk_chip->addr + NMK_GPIO_PDIS); + if (pull == NMK_GPIO_PULL_NONE) + pdis |= bit; + else + pdis &= ~bit; + writel(pdis, nmk_chip->addr + NMK_GPIO_PDIS); + + if (pull == NMK_GPIO_PULL_UP) + writel(bit, nmk_chip->addr + NMK_GPIO_DATS); + else if (pull == NMK_GPIO_PULL_DOWN) + writel(bit, nmk_chip->addr + NMK_GPIO_DATC); +} + +static void __nmk_gpio_make_input(struct nmk_gpio_chip *nmk_chip, + unsigned offset) +{ + writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRC); +} + +static void __nmk_gpio_set_output(struct nmk_gpio_chip *nmk_chip, + unsigned offset, int val) +{ + if (val) + writel(1 << offset, nmk_chip->addr + NMK_GPIO_DATS); + else + writel(1 << offset, nmk_chip->addr + NMK_GPIO_DATC); +} + +static void __nmk_gpio_make_output(struct nmk_gpio_chip *nmk_chip, + unsigned offset, int val) +{ + writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRS); + __nmk_gpio_set_output(nmk_chip, offset, val); +} + +static void __nmk_gpio_set_mode_safe(struct nmk_gpio_chip *nmk_chip, + unsigned offset, int gpio_mode, + bool glitch) +{ + u32 rwimsc = readl(nmk_chip->addr + NMK_GPIO_RWIMSC); + u32 fwimsc = readl(nmk_chip->addr + NMK_GPIO_FWIMSC); + + if (glitch && nmk_chip->set_ioforce) { + u32 bit = BIT(offset); + + /* Prevent spurious wakeups */ + writel(rwimsc & ~bit, nmk_chip->addr + NMK_GPIO_RWIMSC); + writel(fwimsc & ~bit, nmk_chip->addr + NMK_GPIO_FWIMSC); + + nmk_chip->set_ioforce(true); + } + + __nmk_gpio_set_mode(nmk_chip, offset, gpio_mode); + + if (glitch && nmk_chip->set_ioforce) { + nmk_chip->set_ioforce(false); + + writel(rwimsc, nmk_chip->addr + NMK_GPIO_RWIMSC); + writel(fwimsc, nmk_chip->addr + NMK_GPIO_FWIMSC); + } +} + +static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, + pin_cfg_t cfg, bool sleep, unsigned int *slpmregs) +{ + static const char *afnames[] = { + [NMK_GPIO_ALT_GPIO] = "GPIO", + [NMK_GPIO_ALT_A] = "A", + [NMK_GPIO_ALT_B] = "B", + [NMK_GPIO_ALT_C] = "C" + }; + static const char *pullnames[] = { + [NMK_GPIO_PULL_NONE] = "none", + [NMK_GPIO_PULL_UP] = "up", + [NMK_GPIO_PULL_DOWN] = "down", + [3] /* illegal */ = "??" + }; + static const char *slpmnames[] = { + [NMK_GPIO_SLPM_INPUT] = "input/wakeup", + [NMK_GPIO_SLPM_NOCHANGE] = "no-change/no-wakeup", + }; + + int pin = PIN_NUM(cfg); + int pull = PIN_PULL(cfg); + int af = PIN_ALT(cfg); + int slpm = PIN_SLPM(cfg); + int output = PIN_DIR(cfg); + int val = PIN_VAL(cfg); + bool glitch = af == NMK_GPIO_ALT_C; + + dev_dbg(nmk_chip->chip.dev, "pin %d [%#lx]: af %s, pull %s, slpm %s (%s%s)\n", + pin, cfg, afnames[af], pullnames[pull], slpmnames[slpm], + output ? "output " : "input", + output ? (val ? "high" : "low") : ""); + + if (sleep) { + int slpm_pull = PIN_SLPM_PULL(cfg); + int slpm_output = PIN_SLPM_DIR(cfg); + int slpm_val = PIN_SLPM_VAL(cfg); + + af = NMK_GPIO_ALT_GPIO; + + /* + * The SLPM_* values are normal values + 1 to allow zero to + * mean "same as normal". + */ + if (slpm_pull) + pull = slpm_pull - 1; + if (slpm_output) + output = slpm_output - 1; + if (slpm_val) + val = slpm_val - 1; + + dev_dbg(nmk_chip->chip.dev, "pin %d: sleep pull %s, dir %s, val %s\n", + pin, + slpm_pull ? pullnames[pull] : "same", + slpm_output ? (output ? "output" : "input") : "same", + slpm_val ? (val ? "high" : "low") : "same"); + } + + if (output) + __nmk_gpio_make_output(nmk_chip, offset, val); + else { + __nmk_gpio_make_input(nmk_chip, offset); + __nmk_gpio_set_pull(nmk_chip, offset, pull); + } + + /* + * If we've backed up the SLPM registers (glitch workaround), modify + * the backups since they will be restored. + */ + if (slpmregs) { + if (slpm == NMK_GPIO_SLPM_NOCHANGE) + slpmregs[nmk_chip->bank] |= BIT(offset); + else + slpmregs[nmk_chip->bank] &= ~BIT(offset); + } else + __nmk_gpio_set_slpm(nmk_chip, offset, slpm); + + __nmk_gpio_set_mode_safe(nmk_chip, offset, af, glitch); +} + +/* + * Safe sequence used to switch IOs between GPIO and Alternate-C mode: + * - Save SLPM registers + * - Set SLPM=0 for the IOs you want to switch and others to 1 + * - Configure the GPIO registers for the IOs that are being switched + * - Set IOFORCE=1 + * - Modify the AFLSA/B registers for the IOs that are being switched + * - Set IOFORCE=0 + * - Restore SLPM registers + * - Any spurious wake up event during switch sequence to be ignored and + * cleared + */ +static void nmk_gpio_glitch_slpm_init(unsigned int *slpm) +{ + int i; + + for (i = 0; i < NUM_BANKS; i++) { + struct nmk_gpio_chip *chip = nmk_gpio_chips[i]; + unsigned int temp = slpm[i]; + + if (!chip) + break; + + slpm[i] = readl(chip->addr + NMK_GPIO_SLPC); + writel(temp, chip->addr + NMK_GPIO_SLPC); + } +} + +static void nmk_gpio_glitch_slpm_restore(unsigned int *slpm) +{ + int i; + + for (i = 0; i < NUM_BANKS; i++) { + struct nmk_gpio_chip *chip = nmk_gpio_chips[i]; + + if (!chip) + break; + + writel(slpm[i], chip->addr + NMK_GPIO_SLPC); + } +} + +static int __nmk_config_pins(pin_cfg_t *cfgs, int num, bool sleep) +{ + static unsigned int slpm[NUM_BANKS]; + unsigned long flags; + bool glitch = false; + int ret = 0; + int i; + + for (i = 0; i < num; i++) { + if (PIN_ALT(cfgs[i]) == NMK_GPIO_ALT_C) { + glitch = true; + break; + } + } + + spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); + + if (glitch) { + memset(slpm, 0xff, sizeof(slpm)); + + for (i = 0; i < num; i++) { + int pin = PIN_NUM(cfgs[i]); + int offset = pin % NMK_GPIO_PER_CHIP; + + if (PIN_ALT(cfgs[i]) == NMK_GPIO_ALT_C) + slpm[pin / NMK_GPIO_PER_CHIP] &= ~BIT(offset); + } + + nmk_gpio_glitch_slpm_init(slpm); + } + + for (i = 0; i < num; i++) { + struct nmk_gpio_chip *nmk_chip; + int pin = PIN_NUM(cfgs[i]); + + nmk_chip = irq_get_chip_data(NOMADIK_GPIO_TO_IRQ(pin)); + if (!nmk_chip) { + ret = -EINVAL; + break; + } + + 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); + } + + if (glitch) + nmk_gpio_glitch_slpm_restore(slpm); + + spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); + + return ret; +} + +/** + * nmk_config_pin - configure a pin's mux attributes + * @cfg: pin confguration + * + * Configures a pin's mode (alternate function or GPIO), its pull up status, + * and its sleep mode based on the specified configuration. The @cfg is + * usually one of the SoC specific macros defined in mach/-pins.h. These + * are constructed using, and can be further enhanced with, the macros in + * plat/pincfg.h. + * + * If a pin's mode is set to GPIO, it is configured as an input to avoid + * side-effects. The gpio can be manipulated later using standard GPIO API + * calls. + */ +int nmk_config_pin(pin_cfg_t cfg, bool sleep) +{ + return __nmk_config_pins(&cfg, 1, sleep); +} +EXPORT_SYMBOL(nmk_config_pin); + +/** + * nmk_config_pins - configure several pins at once + * @cfgs: array of pin configurations + * @num: number of elments in the array + * + * Configures several pins using nmk_config_pin(). Refer to that function for + * further information. + */ +int nmk_config_pins(pin_cfg_t *cfgs, int num) +{ + return __nmk_config_pins(cfgs, num, false); +} +EXPORT_SYMBOL(nmk_config_pins); + +int nmk_config_pins_sleep(pin_cfg_t *cfgs, int num) +{ + return __nmk_config_pins(cfgs, num, true); +} +EXPORT_SYMBOL(nmk_config_pins_sleep); + +/** + * nmk_gpio_set_slpm() - configure the sleep mode of a pin + * @gpio: pin number + * @mode: NMK_GPIO_SLPM_INPUT or NMK_GPIO_SLPM_NOCHANGE, + * + * Sets the sleep mode of a pin. If @mode is NMK_GPIO_SLPM_INPUT, the pin is + * changed to an input (with pullup/down enabled) in sleep and deep sleep. If + * @mode is NMK_GPIO_SLPM_NOCHANGE, the pin remains in the state it was + * configured even when in sleep and deep sleep. + * + * On DB8500v2 onwards, this setting loses the previous meaning and instead + * indicates if wakeup detection is enabled on the pin. Note that + * enable_irq_wake() will automatically enable wakeup detection. + */ +int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode) +{ + struct nmk_gpio_chip *nmk_chip; + unsigned long flags; + + nmk_chip = irq_get_chip_data(NOMADIK_GPIO_TO_IRQ(gpio)); + if (!nmk_chip) + return -EINVAL; + + spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); + spin_lock(&nmk_chip->lock); + + __nmk_gpio_set_slpm(nmk_chip, gpio - nmk_chip->chip.base, mode); + + spin_unlock(&nmk_chip->lock); + spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); + + return 0; +} + +/** + * nmk_gpio_set_pull() - enable/disable pull up/down on a gpio + * @gpio: pin number + * @pull: one of NMK_GPIO_PULL_DOWN, NMK_GPIO_PULL_UP, and NMK_GPIO_PULL_NONE + * + * Enables/disables pull up/down on a specified pin. This only takes effect if + * the pin is configured as an input (either explicitly or by the alternate + * function). + * + * NOTE: If enabling the pull up/down, the caller must ensure that the GPIO is + * configured as an input. Otherwise, due to the way the controller registers + * work, this function will change the value output on the pin. + */ +int nmk_gpio_set_pull(int gpio, enum nmk_gpio_pull pull) +{ + struct nmk_gpio_chip *nmk_chip; + unsigned long flags; + + nmk_chip = irq_get_chip_data(NOMADIK_GPIO_TO_IRQ(gpio)); + if (!nmk_chip) + return -EINVAL; + + 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); + + return 0; +} + +/* Mode functions */ +/** + * nmk_gpio_set_mode() - set the mux mode of a gpio pin + * @gpio: pin number + * @gpio_mode: one of NMK_GPIO_ALT_GPIO, NMK_GPIO_ALT_A, + * NMK_GPIO_ALT_B, and NMK_GPIO_ALT_C + * + * Sets the mode of the specified pin to one of the alternate functions or + * plain GPIO. + */ +int nmk_gpio_set_mode(int gpio, int gpio_mode) +{ + struct nmk_gpio_chip *nmk_chip; + unsigned long flags; + + nmk_chip = irq_get_chip_data(NOMADIK_GPIO_TO_IRQ(gpio)); + if (!nmk_chip) + return -EINVAL; + + 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); + + return 0; +} +EXPORT_SYMBOL(nmk_gpio_set_mode); + +int nmk_gpio_get_mode(int gpio) +{ + struct nmk_gpio_chip *nmk_chip; + u32 afunc, bfunc, bit; + + nmk_chip = irq_get_chip_data(NOMADIK_GPIO_TO_IRQ(gpio)); + if (!nmk_chip) + return -EINVAL; + + bit = 1 << (gpio - nmk_chip->chip.base); + + afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & bit; + bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & bit; + + return (afunc ? NMK_GPIO_ALT_A : 0) | (bfunc ? NMK_GPIO_ALT_B : 0); +} +EXPORT_SYMBOL(nmk_gpio_get_mode); + + +/* IRQ functions */ +static inline int nmk_gpio_get_bitmask(int gpio) +{ + return 1 << (gpio % 32); +} + +static void nmk_gpio_irq_ack(struct irq_data *d) +{ + int gpio; + struct nmk_gpio_chip *nmk_chip; + + gpio = NOMADIK_IRQ_TO_GPIO(d->irq); + nmk_chip = irq_data_get_irq_chip_data(d); + if (!nmk_chip) + return; + writel(nmk_gpio_get_bitmask(gpio), nmk_chip->addr + NMK_GPIO_IC); +} + +enum nmk_gpio_irq_type { + NORMAL, + WAKE, +}; + +static void __nmk_gpio_irq_modify(struct nmk_gpio_chip *nmk_chip, + int gpio, enum nmk_gpio_irq_type which, + bool enable) +{ + u32 rimsc = which == WAKE ? NMK_GPIO_RWIMSC : NMK_GPIO_RIMSC; + u32 fimsc = which == WAKE ? NMK_GPIO_FWIMSC : NMK_GPIO_FIMSC; + u32 bitmask = nmk_gpio_get_bitmask(gpio); + u32 reg; + + /* we must individually set/clear the two edges */ + if (nmk_chip->edge_rising & bitmask) { + reg = readl(nmk_chip->addr + rimsc); + if (enable) + reg |= bitmask; + else + reg &= ~bitmask; + writel(reg, nmk_chip->addr + rimsc); + } + if (nmk_chip->edge_falling & bitmask) { + reg = readl(nmk_chip->addr + fimsc); + if (enable) + reg |= bitmask; + else + reg &= ~bitmask; + writel(reg, nmk_chip->addr + fimsc); + } +} + +static void __nmk_gpio_set_wake(struct nmk_gpio_chip *nmk_chip, + int gpio, bool on) +{ + __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, on); +} + +static int nmk_gpio_irq_maskunmask(struct irq_data *d, bool enable) +{ + int gpio; + struct nmk_gpio_chip *nmk_chip; + unsigned long flags; + u32 bitmask; + + gpio = NOMADIK_IRQ_TO_GPIO(d->irq); + nmk_chip = irq_data_get_irq_chip_data(d); + bitmask = nmk_gpio_get_bitmask(gpio); + if (!nmk_chip) + return -EINVAL; + + if (enable) + nmk_chip->enabled |= bitmask; + else + nmk_chip->enabled &= ~bitmask; + + spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); + spin_lock(&nmk_chip->lock); + + __nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, enable); + + if (!(nmk_chip->real_wake & bitmask)) + __nmk_gpio_set_wake(nmk_chip, gpio, enable); + + spin_unlock(&nmk_chip->lock); + spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); + + return 0; +} + +static void nmk_gpio_irq_mask(struct irq_data *d) +{ + nmk_gpio_irq_maskunmask(d, false); +} + +static void nmk_gpio_irq_unmask(struct irq_data *d) +{ + nmk_gpio_irq_maskunmask(d, true); +} + +static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct nmk_gpio_chip *nmk_chip; + unsigned long flags; + u32 bitmask; + int gpio; + + gpio = NOMADIK_IRQ_TO_GPIO(d->irq); + nmk_chip = irq_data_get_irq_chip_data(d); + if (!nmk_chip) + return -EINVAL; + bitmask = nmk_gpio_get_bitmask(gpio); + + spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); + spin_lock(&nmk_chip->lock); + + if (!(nmk_chip->enabled & bitmask)) + __nmk_gpio_set_wake(nmk_chip, gpio, on); + + if (on) + nmk_chip->real_wake |= bitmask; + else + nmk_chip->real_wake &= ~bitmask; + + spin_unlock(&nmk_chip->lock); + spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); + + return 0; +} + +static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + bool enabled, wake = irqd_is_wakeup_set(d); + int gpio; + struct nmk_gpio_chip *nmk_chip; + unsigned long flags; + u32 bitmask; + + gpio = NOMADIK_IRQ_TO_GPIO(d->irq); + nmk_chip = irq_data_get_irq_chip_data(d); + bitmask = nmk_gpio_get_bitmask(gpio); + if (!nmk_chip) + return -EINVAL; + + if (type & IRQ_TYPE_LEVEL_HIGH) + return -EINVAL; + if (type & IRQ_TYPE_LEVEL_LOW) + return -EINVAL; + + enabled = nmk_chip->enabled & bitmask; + + spin_lock_irqsave(&nmk_chip->lock, flags); + + if (enabled) + __nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, false); + + if (enabled || wake) + __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, false); + + nmk_chip->edge_rising &= ~bitmask; + if (type & IRQ_TYPE_EDGE_RISING) + nmk_chip->edge_rising |= bitmask; + + nmk_chip->edge_falling &= ~bitmask; + if (type & IRQ_TYPE_EDGE_FALLING) + nmk_chip->edge_falling |= bitmask; + + if (enabled) + __nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, true); + + if (enabled || wake) + __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, true); + + spin_unlock_irqrestore(&nmk_chip->lock, flags); + + return 0; +} + +static struct irq_chip nmk_gpio_irq_chip = { + .name = "Nomadik-GPIO", + .irq_ack = nmk_gpio_irq_ack, + .irq_mask = nmk_gpio_irq_mask, + .irq_unmask = nmk_gpio_irq_unmask, + .irq_set_type = nmk_gpio_irq_set_type, + .irq_set_wake = nmk_gpio_irq_set_wake, +}; + +static void __nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc, + u32 status) +{ + struct nmk_gpio_chip *nmk_chip; + struct irq_chip *host_chip = irq_get_chip(irq); + unsigned int first_irq; + + chained_irq_enter(host_chip, desc); + + nmk_chip = irq_get_handler_data(irq); + first_irq = NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base); + while (status) { + int bit = __ffs(status); + + generic_handle_irq(first_irq + bit); + status &= ~BIT(bit); + } + + chained_irq_exit(host_chip, 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); + + __nmk_gpio_irq_handler(irq, desc, status); +} + +static void nmk_gpio_secondary_irq_handler(unsigned int irq, + struct irq_desc *desc) +{ + struct nmk_gpio_chip *nmk_chip = irq_get_handler_data(irq); + u32 status = nmk_chip->get_secondary_status(nmk_chip->bank); + + __nmk_gpio_irq_handler(irq, desc, status); +} + +static int nmk_gpio_init_irq(struct nmk_gpio_chip *nmk_chip) +{ + unsigned int first_irq; + int i; + + first_irq = NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base); + for (i = first_irq; i < first_irq + nmk_chip->chip.ngpio; i++) { + irq_set_chip_and_handler(i, &nmk_gpio_irq_chip, + handle_edge_irq); + set_irq_flags(i, IRQF_VALID); + irq_set_chip_data(i, nmk_chip); + irq_set_irq_type(i, IRQ_TYPE_EDGE_FALLING); + } + + irq_set_chained_handler(nmk_chip->parent_irq, nmk_gpio_irq_handler); + irq_set_handler_data(nmk_chip->parent_irq, nmk_chip); + + if (nmk_chip->secondary_parent_irq >= 0) { + irq_set_chained_handler(nmk_chip->secondary_parent_irq, + nmk_gpio_secondary_irq_handler); + irq_set_handler_data(nmk_chip->secondary_parent_irq, nmk_chip); + } + + return 0; +} + +/* I/O Functions */ +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); + + writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRC); + return 0; +} + +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; + + return (readl(nmk_chip->addr + NMK_GPIO_DAT) & bit) != 0; +} + +static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned offset, + int val) +{ + struct nmk_gpio_chip *nmk_chip = + container_of(chip, struct nmk_gpio_chip, chip); + + __nmk_gpio_set_output(nmk_chip, offset, val); +} + +static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset, + int val) +{ + struct nmk_gpio_chip *nmk_chip = + container_of(chip, struct nmk_gpio_chip, chip); + + __nmk_gpio_make_output(nmk_chip, offset, val); + + return 0; +} + +static int nmk_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct nmk_gpio_chip *nmk_chip = + container_of(chip, struct nmk_gpio_chip, chip); + + return NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base) + offset; +} + +#ifdef CONFIG_DEBUG_FS + +#include + +static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + int mode; + unsigned i; + unsigned gpio = chip->base; + int is_out; + struct nmk_gpio_chip *nmk_chip = + container_of(chip, struct nmk_gpio_chip, chip); + const char *modes[] = { + [NMK_GPIO_ALT_GPIO] = "gpio", + [NMK_GPIO_ALT_A] = "altA", + [NMK_GPIO_ALT_B] = "altB", + [NMK_GPIO_ALT_C] = "altC", + }; + + for (i = 0; i < chip->ngpio; i++, gpio++) { + const char *label = gpiochip_is_requested(chip, i); + bool pull; + u32 bit = 1 << i; + + if (!label) + continue; + + is_out = readl(nmk_chip->addr + NMK_GPIO_DIR) & bit; + pull = !(readl(nmk_chip->addr + NMK_GPIO_PDIS) & bit); + mode = nmk_gpio_get_mode(gpio); + seq_printf(s, " gpio-%-3d (%-20.20s) %s %s %s %s", + gpio, label, + is_out ? "out" : "in ", + chip->get + ? (chip->get(chip, i) ? "hi" : "lo") + : "? ", + (mode < 0) ? "unknown" : modes[mode], + pull ? "pull" : "none"); + seq_printf(s, "\n"); + } +} + +#else +#define nmk_gpio_dbg_show NULL +#endif + +/* This structure is replicated for each GPIO block allocated at probe time */ +static struct gpio_chip nmk_gpio_template = { + .direction_input = nmk_gpio_make_input, + .get = nmk_gpio_get_input, + .direction_output = nmk_gpio_make_output, + .set = nmk_gpio_set_output, + .to_irq = nmk_gpio_to_irq, + .dbg_show = nmk_gpio_dbg_show, + .can_sleep = 0, +}; + +/* + * 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, + * and not the rest of the interrupts which we needed to have as wakeups for + * cpuidle. + * + * PM ops are not used since this needs to be done at the end, after all the + * other drivers are done with their suspend callbacks. + */ +void nmk_gpio_wakeups_suspend(void) +{ + int i; + + for (i = 0; i < NUM_BANKS; i++) { + struct nmk_gpio_chip *chip = nmk_gpio_chips[i]; + + if (!chip) + break; + + chip->rwimsc = readl(chip->addr + NMK_GPIO_RWIMSC); + chip->fwimsc = readl(chip->addr + NMK_GPIO_FWIMSC); + + writel(chip->rwimsc & chip->real_wake, + chip->addr + NMK_GPIO_RWIMSC); + writel(chip->fwimsc & chip->real_wake, + chip->addr + NMK_GPIO_FWIMSC); + + if (cpu_is_u8500v2()) { + chip->slpm = readl(chip->addr + NMK_GPIO_SLPC); + + /* 0 -> wakeup enable */ + writel(~chip->real_wake, chip->addr + NMK_GPIO_SLPC); + } + } +} + +void nmk_gpio_wakeups_resume(void) +{ + int i; + + for (i = 0; i < NUM_BANKS; i++) { + struct nmk_gpio_chip *chip = nmk_gpio_chips[i]; + + if (!chip) + break; + + writel(chip->rwimsc, chip->addr + NMK_GPIO_RWIMSC); + writel(chip->fwimsc, chip->addr + NMK_GPIO_FWIMSC); + + if (cpu_is_u8500v2()) + writel(chip->slpm, chip->addr + NMK_GPIO_SLPC); + } +} + +static int __devinit nmk_gpio_probe(struct platform_device *dev) +{ + struct nmk_gpio_platform_data *pdata = dev->dev.platform_data; + struct nmk_gpio_chip *nmk_chip; + struct gpio_chip *chip; + struct resource *res; + struct clk *clk; + int secondary_irq; + int irq; + int ret; + + if (!pdata) + return -ENODEV; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENOENT; + goto out; + } + + irq = platform_get_irq(dev, 0); + if (irq < 0) { + ret = irq; + goto out; + } + + secondary_irq = platform_get_irq(dev, 1); + if (secondary_irq >= 0 && !pdata->get_secondary_status) { + ret = -EINVAL; + goto out; + } + + if (request_mem_region(res->start, resource_size(res), + dev_name(&dev->dev)) == NULL) { + ret = -EBUSY; + goto out; + } + + clk = clk_get(&dev->dev, NULL); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto out_release; + } + + clk_enable(clk); + + nmk_chip = kzalloc(sizeof(*nmk_chip), GFP_KERNEL); + if (!nmk_chip) { + ret = -ENOMEM; + goto out_clk; + } + /* + * The virt address in nmk_chip->addr is in the nomadik register space, + * so we can simply convert the resource address, without remapping + */ + nmk_chip->bank = dev->id; + nmk_chip->clk = clk; + nmk_chip->addr = io_p2v(res->start); + nmk_chip->chip = nmk_gpio_template; + nmk_chip->parent_irq = irq; + nmk_chip->secondary_parent_irq = secondary_irq; + nmk_chip->get_secondary_status = pdata->get_secondary_status; + nmk_chip->set_ioforce = pdata->set_ioforce; + spin_lock_init(&nmk_chip->lock); + + chip = &nmk_chip->chip; + chip->base = pdata->first_gpio; + chip->ngpio = pdata->num_gpio; + chip->label = pdata->name ?: dev_name(&dev->dev); + chip->dev = &dev->dev; + chip->owner = THIS_MODULE; + + ret = gpiochip_add(&nmk_chip->chip); + if (ret) + goto out_free; + + BUG_ON(nmk_chip->bank >= ARRAY_SIZE(nmk_gpio_chips)); + + nmk_gpio_chips[nmk_chip->bank] = nmk_chip; + platform_set_drvdata(dev, nmk_chip); + + nmk_gpio_init_irq(nmk_chip); + + dev_info(&dev->dev, "Bits %i-%i at address %p\n", + nmk_chip->chip.base, nmk_chip->chip.base+31, nmk_chip->addr); + return 0; + +out_free: + kfree(nmk_chip); +out_clk: + clk_disable(clk); + clk_put(clk); +out_release: + release_mem_region(res->start, resource_size(res)); +out: + dev_err(&dev->dev, "Failure %i for GPIO %i-%i\n", ret, + pdata->first_gpio, pdata->first_gpio+31); + return ret; +} + +static struct platform_driver nmk_gpio_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "gpio", + }, + .probe = nmk_gpio_probe, +}; + +static int __init nmk_gpio_init(void) +{ + return platform_driver_register(&nmk_gpio_driver); +} + +core_initcall(nmk_gpio_init); + +MODULE_AUTHOR("Prafulla WADASKAR and Alessandro Rubini"); +MODULE_DESCRIPTION("Nomadik GPIO Driver"); +MODULE_LICENSE("GPL"); + + -- cgit v1.2.3 From bc6f5cf6484a509cfe0533b8ddf8b8ca60f35557 Mon Sep 17 00:00:00 2001 From: Rickard Andersson Date: Tue, 24 May 2011 23:07:17 +0200 Subject: gpio/nomadik: add function to read GPIO pull down status Signed-off-by: Rickard Andersson Reviewed-by: Martin Persson [Split off from larger patch] Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- arch/arm/plat-nomadik/include/plat/gpio.h | 2 ++ drivers/gpio/gpio-nomadik.c | 34 +++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 4 deletions(-) (limited to 'arch/arm') diff --git a/arch/arm/plat-nomadik/include/plat/gpio.h b/arch/arm/plat-nomadik/include/plat/gpio.h index 1b9f6f0843d1..ea19a5b2f227 100644 --- a/arch/arm/plat-nomadik/include/plat/gpio.h +++ b/arch/arm/plat-nomadik/include/plat/gpio.h @@ -78,6 +78,8 @@ extern int nmk_gpio_get_mode(int gpio); extern void nmk_gpio_wakeups_suspend(void); extern void nmk_gpio_wakeups_resume(void); +extern void nmk_gpio_read_pull(int gpio_bank, u32 *pull_up); + /* * Platform data to register a block: only the initial gpio/irq number. */ diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c index 380204781f84..4961ef9bc153 100644 --- a/drivers/gpio/gpio-nomadik.c +++ b/drivers/gpio/gpio-nomadik.c @@ -57,6 +57,7 @@ struct nmk_gpio_chip { u32 fwimsc; u32 slpm; u32 enabled; + u32 pull_up; }; static struct nmk_gpio_chip * @@ -103,16 +104,22 @@ static void __nmk_gpio_set_pull(struct nmk_gpio_chip *nmk_chip, u32 pdis; pdis = readl(nmk_chip->addr + NMK_GPIO_PDIS); - if (pull == NMK_GPIO_PULL_NONE) + if (pull == NMK_GPIO_PULL_NONE) { pdis |= bit; - else + nmk_chip->pull_up &= ~bit; + } else { pdis &= ~bit; + } + writel(pdis, nmk_chip->addr + NMK_GPIO_PDIS); - if (pull == NMK_GPIO_PULL_UP) + if (pull == NMK_GPIO_PULL_UP) { + nmk_chip->pull_up |= bit; writel(bit, nmk_chip->addr + NMK_GPIO_DATS); - else if (pull == NMK_GPIO_PULL_DOWN) + } else if (pull == NMK_GPIO_PULL_DOWN) { + nmk_chip->pull_up &= ~bit; writel(bit, nmk_chip->addr + NMK_GPIO_DATC); + } } static void __nmk_gpio_make_input(struct nmk_gpio_chip *nmk_chip, @@ -921,6 +928,25 @@ void nmk_gpio_wakeups_resume(void) } } +/* + * Read the pull up/pull down status. + * A bit set in 'pull_up' means that pull up + * is selected if pull is enabled in PDIS register. + * Note: only pull up/down set via this driver can + * be detected due to HW limitations. + */ +void nmk_gpio_read_pull(int gpio_bank, u32 *pull_up) +{ + if (gpio_bank < NUM_BANKS) { + struct nmk_gpio_chip *chip = nmk_gpio_chips[gpio_bank]; + + if (!chip) + return; + + *pull_up = chip->pull_up; + } +} + static int __devinit nmk_gpio_probe(struct platform_device *dev) { struct nmk_gpio_platform_data *pdata = dev->dev.platform_data; -- cgit v1.2.3 From fed6a0224714bd414bff91833e0bd5775a3e9c66 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Mon, 23 May 2011 17:27:38 +0900 Subject: gpio: Move to Samsung common GPIO library to drivers/gpio It's common gpiolib for recent Samsung SoCs. Move to drivers/gpio Signed-off-by: Kyungmin Park Signed-off-by: Grant Likely --- arch/arm/plat-samsung/Makefile | 1 - arch/arm/plat-samsung/gpiolib.c | 206 --------------------------------------- drivers/gpio/Kconfig | 6 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-plat-samsung.c | 206 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 213 insertions(+), 207 deletions(-) delete mode 100644 arch/arm/plat-samsung/gpiolib.c create mode 100644 drivers/gpio/gpio-plat-samsung.c (limited to 'arch/arm') diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index e9de58a2e294..53eb15b0a07d 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -19,7 +19,6 @@ obj-y += gpio.o obj-y += gpio-config.o obj-y += dev-asocdma.o -obj-$(CONFIG_SAMSUNG_GPIOLIB_4BIT) += gpiolib.o obj-$(CONFIG_SAMSUNG_CLKSRC) += clock-clksrc.o obj-$(CONFIG_SAMSUNG_IRQ_UART) += irq-uart.o diff --git a/arch/arm/plat-samsung/gpiolib.c b/arch/arm/plat-samsung/gpiolib.c deleted file mode 100644 index ea37c0461788..000000000000 --- a/arch/arm/plat-samsung/gpiolib.c +++ /dev/null @@ -1,206 +0,0 @@ -/* arch/arm/plat-samsung/gpiolib.c - * - * 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/Kconfig b/drivers/gpio/Kconfig index ebd1124b0764..3735c051074f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -86,6 +86,12 @@ config GPIO_IT8761E help Say yes here to support GPIO functionality of IT8761E super I/O chip. +config GPIO_PLAT_SAMSUNG + bool "Samsung SoCs GPIO library support" + default y if SAMSUNG_GPIOLIB_4BIT + help + Say yes here to support Samsung SoCs GPIO library + config GPIO_PL061 bool "PrimeCell PL061 GPIO support" depends on ARM_AMBA diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 7481e7da2adb..426483f60d63 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_GPIO_ADP5520) += adp5520-gpio.o obj-$(CONFIG_GPIO_ADP5588) += adp5588-gpio.o obj-$(CONFIG_GPIO_BASIC_MMIO_CORE) += basic_mmio_gpio.o obj-$(CONFIG_GPIO_BASIC_MMIO) += basic_mmio_gpio.o +obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o obj-$(CONFIG_GPIO_MAX730X) += max730x.o obj-$(CONFIG_GPIO_MAX7300) += max7300.o diff --git a/drivers/gpio/gpio-plat-samsung.c b/drivers/gpio/gpio-plat-samsung.c new file mode 100644 index 000000000000..ea37c0461788 --- /dev/null +++ b/drivers/gpio/gpio-plat-samsung.c @@ -0,0 +1,206 @@ +/* arch/arm/plat-samsung/gpiolib.c + * + * 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); +} -- cgit v1.2.3 From ab48f16137eb5a6fabcff4cc817319394fc0de7e Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Mon, 23 May 2011 17:27:45 +0900 Subject: gpio: Move the exynos4 GPIO to drivers/gpio Move the Samsung Exynos4 series SoCs GPIO driver to drivers/gpio Signed-off-by: Kyungmin Park Signed-off-by: Grant Likely --- arch/arm/mach-exynos4/Makefile | 2 +- arch/arm/mach-exynos4/gpiolib.c | 365 ---------------------------------------- drivers/gpio/Kconfig | 6 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-exynos4.c | 365 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 373 insertions(+), 366 deletions(-) delete mode 100644 arch/arm/mach-exynos4/gpiolib.c create mode 100644 drivers/gpio/gpio-exynos4.c (limited to 'arch/arm') diff --git a/arch/arm/mach-exynos4/Makefile b/arch/arm/mach-exynos4/Makefile index 777897551e42..97837e75afa3 100644 --- a/arch/arm/mach-exynos4/Makefile +++ b/arch/arm/mach-exynos4/Makefile @@ -13,7 +13,7 @@ obj- := # Core support for EXYNOS4 system obj-$(CONFIG_CPU_EXYNOS4210) += cpu.o init.o clock.o irq-combiner.o -obj-$(CONFIG_CPU_EXYNOS4210) += setup-i2c0.o gpiolib.o irq-eint.o dma.o +obj-$(CONFIG_CPU_EXYNOS4210) += setup-i2c0.o irq-eint.o dma.o obj-$(CONFIG_PM) += pm.o sleep.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o diff --git a/arch/arm/mach-exynos4/gpiolib.c b/arch/arm/mach-exynos4/gpiolib.c deleted file mode 100644 index d54ca6adb660..000000000000 --- a/arch/arm/mach-exynos4/gpiolib.c +++ /dev/null @@ -1,365 +0,0 @@ -/* linux/arch/arm/mach-exynos4/gpiolib.c - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * EXYNOS4 - 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 - -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, -}; - -/* - * 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/Kconfig b/drivers/gpio/Kconfig index 3735c051074f..dbe43b4e02ba 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -86,6 +86,12 @@ config GPIO_IT8761E help Say yes here to support GPIO functionality of IT8761E super I/O chip. +config GPIO_EXYNOS4 + bool "Samsung Exynos4 GPIO library support" + default y if CPU_EXYNOS4210 + help + Say yes here to support Samsung Exynos4 series SoCs GPIO library + config GPIO_PLAT_SAMSUNG bool "Samsung SoCs GPIO library support" default y if SAMSUNG_GPIOLIB_4BIT diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 426483f60d63..d7f960140bfb 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_GPIO_ADP5520) += adp5520-gpio.o obj-$(CONFIG_GPIO_ADP5588) += adp5588-gpio.o obj-$(CONFIG_GPIO_BASIC_MMIO_CORE) += basic_mmio_gpio.o obj-$(CONFIG_GPIO_BASIC_MMIO) += basic_mmio_gpio.o +obj-$(CONFIG_GPIO_EXYNOS4) += gpio-exynos4.o obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o obj-$(CONFIG_GPIO_MAX730X) += max730x.o diff --git a/drivers/gpio/gpio-exynos4.c b/drivers/gpio/gpio-exynos4.c new file mode 100644 index 000000000000..d54ca6adb660 --- /dev/null +++ b/drivers/gpio/gpio-exynos4.c @@ -0,0 +1,365 @@ +/* linux/arch/arm/mach-exynos4/gpiolib.c + * + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS4 - 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 + +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, +}; + +/* + * 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); -- cgit v1.2.3 From 347ec4e47dd249c0620f429d8458fc42eed63e0e Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Mon, 23 May 2011 17:27:51 +0900 Subject: gpio: Move the s5pv210 GPIO to drivers/gpio Move the Samsung s5pv210 SoC GPIO driver to drivers/gpio Signed-off-by: Kyungmin Park Signed-off-by: Grant Likely --- arch/arm/mach-s5pv210/Makefile | 2 +- arch/arm/mach-s5pv210/gpiolib.c | 288 ---------------------------------------- drivers/gpio/Kconfig | 6 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-s5pv210.c | 288 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 296 insertions(+), 289 deletions(-) delete mode 100644 arch/arm/mach-s5pv210/gpiolib.c create mode 100644 drivers/gpio/gpio-s5pv210.c (limited to 'arch/arm') diff --git a/arch/arm/mach-s5pv210/Makefile b/arch/arm/mach-s5pv210/Makefile index 11f17907b4e8..50907aca006c 100644 --- a/arch/arm/mach-s5pv210/Makefile +++ b/arch/arm/mach-s5pv210/Makefile @@ -12,7 +12,7 @@ obj- := # Core support for S5PV210 system -obj-$(CONFIG_CPU_S5PV210) += cpu.o init.o clock.o dma.o gpiolib.o +obj-$(CONFIG_CPU_S5PV210) += cpu.o init.o clock.o dma.o obj-$(CONFIG_CPU_S5PV210) += setup-i2c0.o obj-$(CONFIG_S5PV210_PM) += pm.o sleep.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o diff --git a/arch/arm/mach-s5pv210/gpiolib.c b/arch/arm/mach-s5pv210/gpiolib.c deleted file mode 100644 index 1ba20a703e05..000000000000 --- a/arch/arm/mach-s5pv210/gpiolib.c +++ /dev/null @@ -1,288 +0,0 @@ -/* linux/arch/arm/mach-s5pv210/gpiolib.c - * - * Copyright (c) 2010 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * S5PV210 - 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 - -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/Kconfig b/drivers/gpio/Kconfig index dbe43b4e02ba..92d16fdec206 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -98,6 +98,12 @@ config GPIO_PLAT_SAMSUNG help Say yes here to support Samsung SoCs GPIO library +config GPIO_S5PV210 + bool "Samsung S5PV210/S5PC110 GPIO library support" + default y if CPU_S5PV210 + help + Say yes here to support Samsung S5PV210/S5PC110 SoCs GPIO library + config GPIO_PL061 bool "PrimeCell PL061 GPIO support" depends on ARM_AMBA diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index d7f960140bfb..2edf6d660b32 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_GPIO_BASIC_MMIO_CORE) += basic_mmio_gpio.o obj-$(CONFIG_GPIO_BASIC_MMIO) += basic_mmio_gpio.o obj-$(CONFIG_GPIO_EXYNOS4) += gpio-exynos4.o obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o +obj-$(CONFIG_GPIO_S5PV210) += gpio-s5pv210.o obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o obj-$(CONFIG_GPIO_MAX730X) += max730x.o obj-$(CONFIG_GPIO_MAX7300) += max7300.o diff --git a/drivers/gpio/gpio-s5pv210.c b/drivers/gpio/gpio-s5pv210.c new file mode 100644 index 000000000000..1ba20a703e05 --- /dev/null +++ b/drivers/gpio/gpio-s5pv210.c @@ -0,0 +1,288 @@ +/* linux/arch/arm/mach-s5pv210/gpiolib.c + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5PV210 - 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 + +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); -- cgit v1.2.3 From 82ab0f75ee2f5defe300eadc91635aa455e01afd Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Mon, 23 May 2011 17:27:58 +0900 Subject: gpio: Move the s5pc100 GPIO to drivers/gpio Move the Samsung s5pc100 SoC GPIO driver to drivers/gpio Signed-off-by: Kyungmin Park Signed-off-by: Grant Likely --- arch/arm/mach-s5pc100/Makefile | 2 +- arch/arm/mach-s5pc100/gpiolib.c | 355 ---------------------------------------- drivers/gpio/Kconfig | 6 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-s5pc100.c | 355 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 363 insertions(+), 356 deletions(-) delete mode 100644 arch/arm/mach-s5pc100/gpiolib.c create mode 100644 drivers/gpio/gpio-s5pc100.c (limited to 'arch/arm') diff --git a/arch/arm/mach-s5pc100/Makefile b/arch/arm/mach-s5pc100/Makefile index eecab57d2e5d..a5e6e608b498 100644 --- a/arch/arm/mach-s5pc100/Makefile +++ b/arch/arm/mach-s5pc100/Makefile @@ -11,7 +11,7 @@ obj- := # Core support for S5PC100 system -obj-$(CONFIG_CPU_S5PC100) += cpu.o init.o clock.o gpiolib.o +obj-$(CONFIG_CPU_S5PC100) += cpu.o init.o clock.o obj-$(CONFIG_CPU_S5PC100) += setup-i2c0.o obj-$(CONFIG_CPU_S5PC100) += dma.o diff --git a/arch/arm/mach-s5pc100/gpiolib.c b/arch/arm/mach-s5pc100/gpiolib.c deleted file mode 100644 index 2842394b28b5..000000000000 --- a/arch/arm/mach-s5pc100/gpiolib.c +++ /dev/null @@ -1,355 +0,0 @@ -/* linux/arch/arm/mach-s5pc100/gpiolib.c - * - * Copyright (c) 2010 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Copyright 2009 Samsung Electronics Co - * Kyungmin Park - * - * S5PC100 - 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 - -/* 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/Kconfig b/drivers/gpio/Kconfig index 92d16fdec206..2c30911b2414 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -98,6 +98,12 @@ config GPIO_PLAT_SAMSUNG help Say yes here to support Samsung SoCs GPIO library +config GPIO_S5PC100 + bool "Samsung S5PC100 GPIO library support" + default y if CPU_S5PC100 + help + Say yes here to support Samsung S5PC100 SoCs GPIO library + config GPIO_S5PV210 bool "Samsung S5PV210/S5PC110 GPIO library support" default y if CPU_S5PV210 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 2edf6d660b32..7d5b5470ed0d 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_GPIO_BASIC_MMIO_CORE) += basic_mmio_gpio.o obj-$(CONFIG_GPIO_BASIC_MMIO) += basic_mmio_gpio.o obj-$(CONFIG_GPIO_EXYNOS4) += gpio-exynos4.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_GPIO_LANGWELL) += langwell_gpio.o obj-$(CONFIG_GPIO_MAX730X) += max730x.o diff --git a/drivers/gpio/gpio-s5pc100.c b/drivers/gpio/gpio-s5pc100.c new file mode 100644 index 000000000000..2842394b28b5 --- /dev/null +++ b/drivers/gpio/gpio-s5pc100.c @@ -0,0 +1,355 @@ +/* linux/arch/arm/mach-s5pc100/gpiolib.c + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Copyright 2009 Samsung Electronics Co + * Kyungmin Park + * + * S5PC100 - 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 + +/* 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); -- cgit v1.2.3 From 5ee1a81b84ee8b0bd0b4f8cdbac48b472863c1f2 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Wed, 20 Apr 2011 15:44:11 -0700 Subject: GPIO: OMAP: move register offset defines into Register offset defines are moved to so they can be used by SoC-specific device init code to fill out platform_data register offsets. Signed-off-by: Kevin Hilman --- arch/arm/plat-omap/gpio.c | 103 --------------------------------- arch/arm/plat-omap/include/plat/gpio.h | 103 +++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 103 deletions(-) (limited to 'arch/arm') diff --git a/arch/arm/plat-omap/gpio.c b/arch/arm/plat-omap/gpio.c index efb869390199..44acabd5f530 100644 --- a/arch/arm/plat-omap/gpio.c +++ b/arch/arm/plat-omap/gpio.c @@ -30,109 +30,6 @@ #include #include -/* - * OMAP1510 GPIO registers - */ -#define OMAP1510_GPIO_DATA_INPUT 0x00 -#define OMAP1510_GPIO_DATA_OUTPUT 0x04 -#define OMAP1510_GPIO_DIR_CONTROL 0x08 -#define OMAP1510_GPIO_INT_CONTROL 0x0c -#define OMAP1510_GPIO_INT_MASK 0x10 -#define OMAP1510_GPIO_INT_STATUS 0x14 -#define OMAP1510_GPIO_PIN_CONTROL 0x18 - -#define OMAP1510_IH_GPIO_BASE 64 - -/* - * OMAP1610 specific GPIO registers - */ -#define OMAP1610_GPIO_REVISION 0x0000 -#define OMAP1610_GPIO_SYSCONFIG 0x0010 -#define OMAP1610_GPIO_SYSSTATUS 0x0014 -#define OMAP1610_GPIO_IRQSTATUS1 0x0018 -#define OMAP1610_GPIO_IRQENABLE1 0x001c -#define OMAP1610_GPIO_WAKEUPENABLE 0x0028 -#define OMAP1610_GPIO_DATAIN 0x002c -#define OMAP1610_GPIO_DATAOUT 0x0030 -#define OMAP1610_GPIO_DIRECTION 0x0034 -#define OMAP1610_GPIO_EDGE_CTRL1 0x0038 -#define OMAP1610_GPIO_EDGE_CTRL2 0x003c -#define OMAP1610_GPIO_CLEAR_IRQENABLE1 0x009c -#define OMAP1610_GPIO_CLEAR_WAKEUPENA 0x00a8 -#define OMAP1610_GPIO_CLEAR_DATAOUT 0x00b0 -#define OMAP1610_GPIO_SET_IRQENABLE1 0x00dc -#define OMAP1610_GPIO_SET_WAKEUPENA 0x00e8 -#define OMAP1610_GPIO_SET_DATAOUT 0x00f0 - -/* - * OMAP7XX specific GPIO registers - */ -#define OMAP7XX_GPIO_DATA_INPUT 0x00 -#define OMAP7XX_GPIO_DATA_OUTPUT 0x04 -#define OMAP7XX_GPIO_DIR_CONTROL 0x08 -#define OMAP7XX_GPIO_INT_CONTROL 0x0c -#define OMAP7XX_GPIO_INT_MASK 0x10 -#define OMAP7XX_GPIO_INT_STATUS 0x14 - -/* - * omap2+ specific GPIO registers - */ -#define OMAP24XX_GPIO_REVISION 0x0000 -#define OMAP24XX_GPIO_IRQSTATUS1 0x0018 -#define OMAP24XX_GPIO_IRQSTATUS2 0x0028 -#define OMAP24XX_GPIO_IRQENABLE2 0x002c -#define OMAP24XX_GPIO_IRQENABLE1 0x001c -#define OMAP24XX_GPIO_WAKE_EN 0x0020 -#define OMAP24XX_GPIO_CTRL 0x0030 -#define OMAP24XX_GPIO_OE 0x0034 -#define OMAP24XX_GPIO_DATAIN 0x0038 -#define OMAP24XX_GPIO_DATAOUT 0x003c -#define OMAP24XX_GPIO_LEVELDETECT0 0x0040 -#define OMAP24XX_GPIO_LEVELDETECT1 0x0044 -#define OMAP24XX_GPIO_RISINGDETECT 0x0048 -#define OMAP24XX_GPIO_FALLINGDETECT 0x004c -#define OMAP24XX_GPIO_DEBOUNCE_EN 0x0050 -#define OMAP24XX_GPIO_DEBOUNCE_VAL 0x0054 -#define OMAP24XX_GPIO_CLEARIRQENABLE1 0x0060 -#define OMAP24XX_GPIO_SETIRQENABLE1 0x0064 -#define OMAP24XX_GPIO_CLEARWKUENA 0x0080 -#define OMAP24XX_GPIO_SETWKUENA 0x0084 -#define OMAP24XX_GPIO_CLEARDATAOUT 0x0090 -#define OMAP24XX_GPIO_SETDATAOUT 0x0094 - -#define OMAP4_GPIO_REVISION 0x0000 -#define OMAP4_GPIO_EOI 0x0020 -#define OMAP4_GPIO_IRQSTATUSRAW0 0x0024 -#define OMAP4_GPIO_IRQSTATUSRAW1 0x0028 -#define OMAP4_GPIO_IRQSTATUS0 0x002c -#define OMAP4_GPIO_IRQSTATUS1 0x0030 -#define OMAP4_GPIO_IRQSTATUSSET0 0x0034 -#define OMAP4_GPIO_IRQSTATUSSET1 0x0038 -#define OMAP4_GPIO_IRQSTATUSCLR0 0x003c -#define OMAP4_GPIO_IRQSTATUSCLR1 0x0040 -#define OMAP4_GPIO_IRQWAKEN0 0x0044 -#define OMAP4_GPIO_IRQWAKEN1 0x0048 -#define OMAP4_GPIO_IRQENABLE1 0x011c -#define OMAP4_GPIO_WAKE_EN 0x0120 -#define OMAP4_GPIO_IRQSTATUS2 0x0128 -#define OMAP4_GPIO_IRQENABLE2 0x012c -#define OMAP4_GPIO_CTRL 0x0130 -#define OMAP4_GPIO_OE 0x0134 -#define OMAP4_GPIO_DATAIN 0x0138 -#define OMAP4_GPIO_DATAOUT 0x013c -#define OMAP4_GPIO_LEVELDETECT0 0x0140 -#define OMAP4_GPIO_LEVELDETECT1 0x0144 -#define OMAP4_GPIO_RISINGDETECT 0x0148 -#define OMAP4_GPIO_FALLINGDETECT 0x014c -#define OMAP4_GPIO_DEBOUNCENABLE 0x0150 -#define OMAP4_GPIO_DEBOUNCINGTIME 0x0154 -#define OMAP4_GPIO_CLEARIRQENABLE1 0x0160 -#define OMAP4_GPIO_SETIRQENABLE1 0x0164 -#define OMAP4_GPIO_CLEARWKUENA 0x0180 -#define OMAP4_GPIO_SETWKUENA 0x0184 -#define OMAP4_GPIO_CLEARDATAOUT 0x0190 -#define OMAP4_GPIO_SETDATAOUT 0x0194 - struct gpio_bank { unsigned long pbase; void __iomem *base; diff --git a/arch/arm/plat-omap/include/plat/gpio.h b/arch/arm/plat-omap/include/plat/gpio.h index cac2e8ac6968..ec97e00cb581 100644 --- a/arch/arm/plat-omap/include/plat/gpio.h +++ b/arch/arm/plat-omap/include/plat/gpio.h @@ -52,6 +52,109 @@ #define OMAP34XX_NR_GPIOS 6 +/* + * OMAP1510 GPIO registers + */ +#define OMAP1510_GPIO_DATA_INPUT 0x00 +#define OMAP1510_GPIO_DATA_OUTPUT 0x04 +#define OMAP1510_GPIO_DIR_CONTROL 0x08 +#define OMAP1510_GPIO_INT_CONTROL 0x0c +#define OMAP1510_GPIO_INT_MASK 0x10 +#define OMAP1510_GPIO_INT_STATUS 0x14 +#define OMAP1510_GPIO_PIN_CONTROL 0x18 + +#define OMAP1510_IH_GPIO_BASE 64 + +/* + * OMAP1610 specific GPIO registers + */ +#define OMAP1610_GPIO_REVISION 0x0000 +#define OMAP1610_GPIO_SYSCONFIG 0x0010 +#define OMAP1610_GPIO_SYSSTATUS 0x0014 +#define OMAP1610_GPIO_IRQSTATUS1 0x0018 +#define OMAP1610_GPIO_IRQENABLE1 0x001c +#define OMAP1610_GPIO_WAKEUPENABLE 0x0028 +#define OMAP1610_GPIO_DATAIN 0x002c +#define OMAP1610_GPIO_DATAOUT 0x0030 +#define OMAP1610_GPIO_DIRECTION 0x0034 +#define OMAP1610_GPIO_EDGE_CTRL1 0x0038 +#define OMAP1610_GPIO_EDGE_CTRL2 0x003c +#define OMAP1610_GPIO_CLEAR_IRQENABLE1 0x009c +#define OMAP1610_GPIO_CLEAR_WAKEUPENA 0x00a8 +#define OMAP1610_GPIO_CLEAR_DATAOUT 0x00b0 +#define OMAP1610_GPIO_SET_IRQENABLE1 0x00dc +#define OMAP1610_GPIO_SET_WAKEUPENA 0x00e8 +#define OMAP1610_GPIO_SET_DATAOUT 0x00f0 + +/* + * OMAP7XX specific GPIO registers + */ +#define OMAP7XX_GPIO_DATA_INPUT 0x00 +#define OMAP7XX_GPIO_DATA_OUTPUT 0x04 +#define OMAP7XX_GPIO_DIR_CONTROL 0x08 +#define OMAP7XX_GPIO_INT_CONTROL 0x0c +#define OMAP7XX_GPIO_INT_MASK 0x10 +#define OMAP7XX_GPIO_INT_STATUS 0x14 + +/* + * omap2+ specific GPIO registers + */ +#define OMAP24XX_GPIO_REVISION 0x0000 +#define OMAP24XX_GPIO_IRQSTATUS1 0x0018 +#define OMAP24XX_GPIO_IRQSTATUS2 0x0028 +#define OMAP24XX_GPIO_IRQENABLE2 0x002c +#define OMAP24XX_GPIO_IRQENABLE1 0x001c +#define OMAP24XX_GPIO_WAKE_EN 0x0020 +#define OMAP24XX_GPIO_CTRL 0x0030 +#define OMAP24XX_GPIO_OE 0x0034 +#define OMAP24XX_GPIO_DATAIN 0x0038 +#define OMAP24XX_GPIO_DATAOUT 0x003c +#define OMAP24XX_GPIO_LEVELDETECT0 0x0040 +#define OMAP24XX_GPIO_LEVELDETECT1 0x0044 +#define OMAP24XX_GPIO_RISINGDETECT 0x0048 +#define OMAP24XX_GPIO_FALLINGDETECT 0x004c +#define OMAP24XX_GPIO_DEBOUNCE_EN 0x0050 +#define OMAP24XX_GPIO_DEBOUNCE_VAL 0x0054 +#define OMAP24XX_GPIO_CLEARIRQENABLE1 0x0060 +#define OMAP24XX_GPIO_SETIRQENABLE1 0x0064 +#define OMAP24XX_GPIO_CLEARWKUENA 0x0080 +#define OMAP24XX_GPIO_SETWKUENA 0x0084 +#define OMAP24XX_GPIO_CLEARDATAOUT 0x0090 +#define OMAP24XX_GPIO_SETDATAOUT 0x0094 + +#define OMAP4_GPIO_REVISION 0x0000 +#define OMAP4_GPIO_EOI 0x0020 +#define OMAP4_GPIO_IRQSTATUSRAW0 0x0024 +#define OMAP4_GPIO_IRQSTATUSRAW1 0x0028 +#define OMAP4_GPIO_IRQSTATUS0 0x002c +#define OMAP4_GPIO_IRQSTATUS1 0x0030 +#define OMAP4_GPIO_IRQSTATUSSET0 0x0034 +#define OMAP4_GPIO_IRQSTATUSSET1 0x0038 +#define OMAP4_GPIO_IRQSTATUSCLR0 0x003c +#define OMAP4_GPIO_IRQSTATUSCLR1 0x0040 +#define OMAP4_GPIO_IRQWAKEN0 0x0044 +#define OMAP4_GPIO_IRQWAKEN1 0x0048 +#define OMAP4_GPIO_IRQENABLE1 0x011c +#define OMAP4_GPIO_WAKE_EN 0x0120 +#define OMAP4_GPIO_IRQSTATUS2 0x0128 +#define OMAP4_GPIO_IRQENABLE2 0x012c +#define OMAP4_GPIO_CTRL 0x0130 +#define OMAP4_GPIO_OE 0x0134 +#define OMAP4_GPIO_DATAIN 0x0138 +#define OMAP4_GPIO_DATAOUT 0x013c +#define OMAP4_GPIO_LEVELDETECT0 0x0140 +#define OMAP4_GPIO_LEVELDETECT1 0x0144 +#define OMAP4_GPIO_RISINGDETECT 0x0148 +#define OMAP4_GPIO_FALLINGDETECT 0x014c +#define OMAP4_GPIO_DEBOUNCENABLE 0x0150 +#define OMAP4_GPIO_DEBOUNCINGTIME 0x0154 +#define OMAP4_GPIO_CLEARIRQENABLE1 0x0160 +#define OMAP4_GPIO_SETIRQENABLE1 0x0164 +#define OMAP4_GPIO_CLEARWKUENA 0x0180 +#define OMAP4_GPIO_SETWKUENA 0x0184 +#define OMAP4_GPIO_CLEARDATAOUT 0x0190 +#define OMAP4_GPIO_SETDATAOUT 0x0194 + #define OMAP_MPUIO(nr) (OMAP_MAX_GPIO_LINES + (nr)) #define OMAP_GPIO_IS_MPUIO(nr) ((nr) >= OMAP_MAX_GPIO_LINES) -- cgit v1.2.3 From e5cdb13ff95e1400bc94d3e6610fc5e95be3b5b1 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Fri, 20 May 2011 11:53:37 +0200 Subject: GPIO: OMAP: move to drivers/gpio Move OMAP GPIO driver to drivers/gpio. Builds whenever CONFIG_ARCH_OMAP=y. Signed-off-by: Kevin Hilman --- arch/arm/plat-omap/Makefile | 2 +- arch/arm/plat-omap/gpio.c | 2009 ------------------------------------------- drivers/gpio/Makefile | 1 + drivers/gpio/gpio-omap.c | 2007 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 2009 insertions(+), 2010 deletions(-) delete mode 100644 arch/arm/plat-omap/gpio.c create mode 100644 drivers/gpio/gpio-omap.c (limited to 'arch/arm') diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index a4a12859fdd5..f0233e6abcdf 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile @@ -3,7 +3,7 @@ # # Common support -obj-y := common.o sram.o clock.o devices.o dma.o mux.o gpio.o \ +obj-y := common.o sram.o clock.o devices.o dma.o mux.o \ usb.o fb.o io.o counter_32k.o obj-m := obj-n := diff --git a/arch/arm/plat-omap/gpio.c b/arch/arm/plat-omap/gpio.c deleted file mode 100644 index 44acabd5f530..000000000000 --- a/arch/arm/plat-omap/gpio.c +++ /dev/null @@ -1,2009 +0,0 @@ -/* - * linux/arch/arm/plat-omap/gpio.c - * - * Support functions for OMAP GPIO - * - * Copyright (C) 2003-2005 Nokia Corporation - * Written by Juha Yrjölä - * - * Copyright (C) 2009 Texas Instruments - * Added OMAP4 support - Santosh Shilimkar - * - * 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 - -struct gpio_bank { - unsigned long pbase; - void __iomem *base; - u16 irq; - u16 virtual_irq_start; - int method; -#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS) - u32 suspend_wakeup; - u32 saved_wakeup; -#endif - u32 non_wakeup_gpios; - u32 enabled_non_wakeup_gpios; - - u32 saved_datain; - u32 saved_fallingdetect; - u32 saved_risingdetect; - u32 level_mask; - u32 toggle_mask; - spinlock_t lock; - struct gpio_chip chip; - struct clk *dbck; - u32 mod_usage; - u32 dbck_enable_mask; - struct device *dev; - bool dbck_flag; - int stride; -}; - -#ifdef CONFIG_ARCH_OMAP3 -struct omap3_gpio_regs { - u32 irqenable1; - u32 irqenable2; - u32 wake_en; - u32 ctrl; - u32 oe; - u32 leveldetect0; - u32 leveldetect1; - u32 risingdetect; - u32 fallingdetect; - u32 dataout; -}; - -static struct omap3_gpio_regs gpio_context[OMAP34XX_NR_GPIOS]; -#endif - -/* - * TODO: Cleanup gpio_bank usage as it is having information - * related to all instances of the device - */ -static struct gpio_bank *gpio_bank; - -static int bank_width; - -/* TODO: Analyze removing gpio_bank_count usage from driver code */ -int gpio_bank_count; - -static inline struct gpio_bank *get_gpio_bank(int gpio) -{ - if (cpu_is_omap15xx()) { - if (OMAP_GPIO_IS_MPUIO(gpio)) - return &gpio_bank[0]; - return &gpio_bank[1]; - } - if (cpu_is_omap16xx()) { - if (OMAP_GPIO_IS_MPUIO(gpio)) - return &gpio_bank[0]; - return &gpio_bank[1 + (gpio >> 4)]; - } - if (cpu_is_omap7xx()) { - if (OMAP_GPIO_IS_MPUIO(gpio)) - return &gpio_bank[0]; - return &gpio_bank[1 + (gpio >> 5)]; - } - if (cpu_is_omap24xx()) - return &gpio_bank[gpio >> 5]; - if (cpu_is_omap34xx() || cpu_is_omap44xx()) - return &gpio_bank[gpio >> 5]; - BUG(); - return NULL; -} - -static inline int get_gpio_index(int gpio) -{ - if (cpu_is_omap7xx()) - return gpio & 0x1f; - if (cpu_is_omap24xx()) - return gpio & 0x1f; - if (cpu_is_omap34xx() || cpu_is_omap44xx()) - return gpio & 0x1f; - return gpio & 0x0f; -} - -static inline int gpio_valid(int gpio) -{ - if (gpio < 0) - return -1; - if (cpu_class_is_omap1() && OMAP_GPIO_IS_MPUIO(gpio)) { - if (gpio >= OMAP_MAX_GPIO_LINES + 16) - return -1; - return 0; - } - if (cpu_is_omap15xx() && gpio < 16) - return 0; - if ((cpu_is_omap16xx()) && gpio < 64) - return 0; - if (cpu_is_omap7xx() && gpio < 192) - return 0; - if (cpu_is_omap2420() && gpio < 128) - return 0; - if (cpu_is_omap2430() && gpio < 160) - return 0; - if ((cpu_is_omap34xx() || cpu_is_omap44xx()) && gpio < 192) - return 0; - return -1; -} - -static int check_gpio(int gpio) -{ - if (unlikely(gpio_valid(gpio) < 0)) { - printk(KERN_ERR "omap-gpio: invalid GPIO %d\n", gpio); - dump_stack(); - return -1; - } - return 0; -} - -static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input) -{ - void __iomem *reg = bank->base; - u32 l; - - switch (bank->method) { -#ifdef CONFIG_ARCH_OMAP1 - case METHOD_MPUIO: - reg += OMAP_MPUIO_IO_CNTL / bank->stride; - break; -#endif -#ifdef CONFIG_ARCH_OMAP15XX - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_DIR_CONTROL; - break; -#endif -#ifdef CONFIG_ARCH_OMAP16XX - case METHOD_GPIO_1610: - reg += OMAP1610_GPIO_DIRECTION; - break; -#endif -#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) - case METHOD_GPIO_7XX: - reg += OMAP7XX_GPIO_DIR_CONTROL; - break; -#endif -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) - case METHOD_GPIO_24XX: - reg += OMAP24XX_GPIO_OE; - break; -#endif -#if defined(CONFIG_ARCH_OMAP4) - case METHOD_GPIO_44XX: - reg += OMAP4_GPIO_OE; - break; -#endif - default: - WARN_ON(1); - return; - } - l = __raw_readl(reg); - if (is_input) - l |= 1 << gpio; - else - l &= ~(1 << gpio); - __raw_writel(l, reg); -} - -static void _set_gpio_dataout(struct gpio_bank *bank, int gpio, int enable) -{ - void __iomem *reg = bank->base; - u32 l = 0; - - switch (bank->method) { -#ifdef CONFIG_ARCH_OMAP1 - case METHOD_MPUIO: - reg += OMAP_MPUIO_OUTPUT / bank->stride; - l = __raw_readl(reg); - if (enable) - l |= 1 << gpio; - else - l &= ~(1 << gpio); - break; -#endif -#ifdef CONFIG_ARCH_OMAP15XX - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_DATA_OUTPUT; - l = __raw_readl(reg); - if (enable) - l |= 1 << gpio; - else - l &= ~(1 << gpio); - break; -#endif -#ifdef CONFIG_ARCH_OMAP16XX - case METHOD_GPIO_1610: - if (enable) - reg += OMAP1610_GPIO_SET_DATAOUT; - else - reg += OMAP1610_GPIO_CLEAR_DATAOUT; - l = 1 << gpio; - break; -#endif -#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) - case METHOD_GPIO_7XX: - reg += OMAP7XX_GPIO_DATA_OUTPUT; - l = __raw_readl(reg); - if (enable) - l |= 1 << gpio; - else - l &= ~(1 << gpio); - break; -#endif -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) - case METHOD_GPIO_24XX: - if (enable) - reg += OMAP24XX_GPIO_SETDATAOUT; - else - reg += OMAP24XX_GPIO_CLEARDATAOUT; - l = 1 << gpio; - break; -#endif -#ifdef CONFIG_ARCH_OMAP4 - case METHOD_GPIO_44XX: - if (enable) - reg += OMAP4_GPIO_SETDATAOUT; - else - reg += OMAP4_GPIO_CLEARDATAOUT; - l = 1 << gpio; - break; -#endif - default: - WARN_ON(1); - return; - } - __raw_writel(l, reg); -} - -static int _get_gpio_datain(struct gpio_bank *bank, int gpio) -{ - void __iomem *reg; - - if (check_gpio(gpio) < 0) - return -EINVAL; - reg = bank->base; - switch (bank->method) { -#ifdef CONFIG_ARCH_OMAP1 - case METHOD_MPUIO: - reg += OMAP_MPUIO_INPUT_LATCH / bank->stride; - break; -#endif -#ifdef CONFIG_ARCH_OMAP15XX - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_DATA_INPUT; - break; -#endif -#ifdef CONFIG_ARCH_OMAP16XX - case METHOD_GPIO_1610: - reg += OMAP1610_GPIO_DATAIN; - break; -#endif -#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) - case METHOD_GPIO_7XX: - reg += OMAP7XX_GPIO_DATA_INPUT; - break; -#endif -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) - case METHOD_GPIO_24XX: - reg += OMAP24XX_GPIO_DATAIN; - break; -#endif -#ifdef CONFIG_ARCH_OMAP4 - case METHOD_GPIO_44XX: - reg += OMAP4_GPIO_DATAIN; - break; -#endif - default: - return -EINVAL; - } - return (__raw_readl(reg) - & (1 << get_gpio_index(gpio))) != 0; -} - -static int _get_gpio_dataout(struct gpio_bank *bank, int gpio) -{ - void __iomem *reg; - - if (check_gpio(gpio) < 0) - return -EINVAL; - reg = bank->base; - - switch (bank->method) { -#ifdef CONFIG_ARCH_OMAP1 - case METHOD_MPUIO: - reg += OMAP_MPUIO_OUTPUT / bank->stride; - break; -#endif -#ifdef CONFIG_ARCH_OMAP15XX - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_DATA_OUTPUT; - break; -#endif -#ifdef CONFIG_ARCH_OMAP16XX - case METHOD_GPIO_1610: - reg += OMAP1610_GPIO_DATAOUT; - break; -#endif -#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) - case METHOD_GPIO_7XX: - reg += OMAP7XX_GPIO_DATA_OUTPUT; - break; -#endif -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) - case METHOD_GPIO_24XX: - reg += OMAP24XX_GPIO_DATAOUT; - break; -#endif -#ifdef CONFIG_ARCH_OMAP4 - case METHOD_GPIO_44XX: - reg += OMAP4_GPIO_DATAOUT; - break; -#endif - default: - return -EINVAL; - } - - return (__raw_readl(reg) & (1 << get_gpio_index(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) - -/** - * _set_gpio_debounce - low level gpio debounce time - * @bank: the gpio bank we're acting upon - * @gpio: the gpio number on this @gpio - * @debounce: debounce time to use - * - * OMAP's debounce time is in 31us steps so we need - * to convert and round up to the closest unit. - */ -static void _set_gpio_debounce(struct gpio_bank *bank, unsigned gpio, - unsigned debounce) -{ - void __iomem *reg = bank->base; - u32 val; - u32 l; - - if (!bank->dbck_flag) - return; - - if (debounce < 32) - debounce = 0x01; - else if (debounce > 7936) - debounce = 0xff; - else - debounce = (debounce / 0x1f) - 1; - - l = 1 << get_gpio_index(gpio); - - if (bank->method == METHOD_GPIO_44XX) - reg += OMAP4_GPIO_DEBOUNCINGTIME; - else - reg += OMAP24XX_GPIO_DEBOUNCE_VAL; - - __raw_writel(debounce, reg); - - reg = bank->base; - if (bank->method == METHOD_GPIO_44XX) - reg += OMAP4_GPIO_DEBOUNCENABLE; - else - reg += OMAP24XX_GPIO_DEBOUNCE_EN; - - val = __raw_readl(reg); - - if (debounce) { - val |= l; - clk_enable(bank->dbck); - } else { - val &= ~l; - clk_disable(bank->dbck); - } - bank->dbck_enable_mask = val; - - __raw_writel(val, reg); -} - -#ifdef CONFIG_ARCH_OMAP2PLUS -static inline void set_24xx_gpio_triggering(struct gpio_bank *bank, int gpio, - int trigger) -{ - void __iomem *base = bank->base; - u32 gpio_bit = 1 << gpio; - u32 val; - - 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); - } 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); - } - if (likely(!(bank->non_wakeup_gpios & gpio_bit))) { - if (cpu_is_omap44xx()) { - if (trigger != 0) - __raw_writel(1 << gpio, bank->base+ - OMAP4_GPIO_IRQWAKEN0); - else { - val = __raw_readl(bank->base + - OMAP4_GPIO_IRQWAKEN0); - __raw_writel(val & (~(1 << gpio)), bank->base + - OMAP4_GPIO_IRQWAKEN0); - } - } else { - /* - * GPIO wakeup request can only be generated on edge - * transitions - */ - if (trigger & IRQ_TYPE_EDGE_BOTH) - __raw_writel(1 << gpio, bank->base - + OMAP24XX_GPIO_SETWKUENA); - else - __raw_writel(1 << gpio, bank->base - + OMAP24XX_GPIO_CLEARWKUENA); - } - } - /* This part needs to be executed always for OMAP34xx */ - if (cpu_is_omap34xx() || (bank->non_wakeup_gpios & gpio_bit)) { - /* - * Log the edge gpio and manually trigger the IRQ - * after resume if the input level changes - * to avoid irq lost during PER RET/OFF mode - * Applies for omap2 non-wakeup gpio and all omap3 gpios - */ - if (trigger & IRQ_TYPE_EDGE_BOTH) - bank->enabled_non_wakeup_gpios |= gpio_bit; - else - bank->enabled_non_wakeup_gpios &= ~gpio_bit; - } - - if (cpu_is_omap44xx()) { - bank->level_mask = - __raw_readl(bank->base + OMAP4_GPIO_LEVELDETECT0) | - __raw_readl(bank->base + OMAP4_GPIO_LEVELDETECT1); - } else { - bank->level_mask = - __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT0) | - __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT1); - } -} -#endif - -#ifdef CONFIG_ARCH_OMAP1 -/* - * This only applies to chips that can't do both rising and falling edge - * detection at once. For all other chips, this function is a noop. - */ -static void _toggle_gpio_edge_triggering(struct gpio_bank *bank, int gpio) -{ - void __iomem *reg = bank->base; - u32 l = 0; - - switch (bank->method) { - case METHOD_MPUIO: - reg += OMAP_MPUIO_GPIO_INT_EDGE / bank->stride; - break; -#ifdef CONFIG_ARCH_OMAP15XX - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_INT_CONTROL; - break; -#endif -#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) - case METHOD_GPIO_7XX: - reg += OMAP7XX_GPIO_INT_CONTROL; - break; -#endif - default: - return; - } - - l = __raw_readl(reg); - if ((l >> gpio) & 1) - l &= ~(1 << gpio); - else - l |= 1 << gpio; - - __raw_writel(l, reg); -} -#endif - -static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) -{ - void __iomem *reg = bank->base; - u32 l = 0; - - switch (bank->method) { -#ifdef CONFIG_ARCH_OMAP1 - case METHOD_MPUIO: - reg += OMAP_MPUIO_GPIO_INT_EDGE / bank->stride; - l = __raw_readl(reg); - if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) - bank->toggle_mask |= 1 << gpio; - if (trigger & IRQ_TYPE_EDGE_RISING) - l |= 1 << gpio; - else if (trigger & IRQ_TYPE_EDGE_FALLING) - l &= ~(1 << gpio); - else - goto bad; - break; -#endif -#ifdef CONFIG_ARCH_OMAP15XX - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_INT_CONTROL; - l = __raw_readl(reg); - if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) - bank->toggle_mask |= 1 << gpio; - if (trigger & IRQ_TYPE_EDGE_RISING) - l |= 1 << gpio; - else if (trigger & IRQ_TYPE_EDGE_FALLING) - l &= ~(1 << gpio); - else - goto bad; - break; -#endif -#ifdef CONFIG_ARCH_OMAP16XX - case METHOD_GPIO_1610: - if (gpio & 0x08) - reg += OMAP1610_GPIO_EDGE_CTRL2; - else - reg += OMAP1610_GPIO_EDGE_CTRL1; - gpio &= 0x07; - l = __raw_readl(reg); - l &= ~(3 << (gpio << 1)); - if (trigger & IRQ_TYPE_EDGE_RISING) - l |= 2 << (gpio << 1); - if (trigger & IRQ_TYPE_EDGE_FALLING) - l |= 1 << (gpio << 1); - if (trigger) - /* Enable wake-up during idle for dynamic tick */ - __raw_writel(1 << gpio, bank->base + OMAP1610_GPIO_SET_WAKEUPENA); - else - __raw_writel(1 << gpio, bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA); - break; -#endif -#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) - case METHOD_GPIO_7XX: - reg += OMAP7XX_GPIO_INT_CONTROL; - l = __raw_readl(reg); - if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) - bank->toggle_mask |= 1 << gpio; - if (trigger & IRQ_TYPE_EDGE_RISING) - l |= 1 << gpio; - else if (trigger & IRQ_TYPE_EDGE_FALLING) - l &= ~(1 << gpio); - else - goto bad; - break; -#endif -#ifdef CONFIG_ARCH_OMAP2PLUS - case METHOD_GPIO_24XX: - case METHOD_GPIO_44XX: - set_24xx_gpio_triggering(bank, gpio, trigger); - return 0; -#endif - default: - goto bad; - } - __raw_writel(l, reg); - return 0; -bad: - return -EINVAL; -} - -static int gpio_irq_type(struct irq_data *d, unsigned type) -{ - struct gpio_bank *bank; - unsigned gpio; - int retval; - unsigned long flags; - - if (!cpu_class_is_omap2() && d->irq > IH_MPUIO_BASE) - gpio = OMAP_MPUIO(d->irq - IH_MPUIO_BASE); - else - gpio = d->irq - IH_GPIO_BASE; - - if (check_gpio(gpio) < 0) - return -EINVAL; - - if (type & ~IRQ_TYPE_SENSE_MASK) - return -EINVAL; - - /* OMAP1 allows only only edge triggering */ - if (!cpu_class_is_omap2() - && (type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH))) - return -EINVAL; - - bank = irq_data_get_irq_chip_data(d); - spin_lock_irqsave(&bank->lock, flags); - retval = _set_gpio_triggering(bank, get_gpio_index(gpio), type); - spin_unlock_irqrestore(&bank->lock, flags); - - if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) - __irq_set_handler_locked(d->irq, handle_level_irq); - else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) - __irq_set_handler_locked(d->irq, handle_edge_irq); - - return retval; -} - -static void _clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask) -{ - void __iomem *reg = bank->base; - - switch (bank->method) { -#ifdef CONFIG_ARCH_OMAP1 - case METHOD_MPUIO: - /* MPUIO irqstatus is reset by reading the status register, - * so do nothing here */ - return; -#endif -#ifdef CONFIG_ARCH_OMAP15XX - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_INT_STATUS; - break; -#endif -#ifdef CONFIG_ARCH_OMAP16XX - case METHOD_GPIO_1610: - reg += OMAP1610_GPIO_IRQSTATUS1; - break; -#endif -#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) - case METHOD_GPIO_7XX: - reg += OMAP7XX_GPIO_INT_STATUS; - break; -#endif -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) - case METHOD_GPIO_24XX: - reg += OMAP24XX_GPIO_IRQSTATUS1; - break; -#endif -#if defined(CONFIG_ARCH_OMAP4) - case METHOD_GPIO_44XX: - reg += OMAP4_GPIO_IRQSTATUS0; - break; -#endif - default: - WARN_ON(1); - return; - } - __raw_writel(gpio_mask, reg); - - /* Workaround for clearing DSP GPIO interrupts to allow retention */ - if (cpu_is_omap24xx() || cpu_is_omap34xx()) - reg = bank->base + OMAP24XX_GPIO_IRQSTATUS2; - else if (cpu_is_omap44xx()) - reg = bank->base + OMAP4_GPIO_IRQSTATUS1; - - if (cpu_is_omap24xx() || cpu_is_omap34xx() || cpu_is_omap44xx()) { - __raw_writel(gpio_mask, reg); - - /* Flush posted write for the irq status to avoid spurious interrupts */ - __raw_readl(reg); - } -} - -static inline void _clear_gpio_irqstatus(struct gpio_bank *bank, int gpio) -{ - _clear_gpio_irqbank(bank, 1 << get_gpio_index(gpio)); -} - -static u32 _get_gpio_irqbank_mask(struct gpio_bank *bank) -{ - void __iomem *reg = bank->base; - int inv = 0; - u32 l; - u32 mask; - - switch (bank->method) { -#ifdef CONFIG_ARCH_OMAP1 - case METHOD_MPUIO: - reg += OMAP_MPUIO_GPIO_MASKIT / bank->stride; - mask = 0xffff; - inv = 1; - break; -#endif -#ifdef CONFIG_ARCH_OMAP15XX - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_INT_MASK; - mask = 0xffff; - inv = 1; - break; -#endif -#ifdef CONFIG_ARCH_OMAP16XX - case METHOD_GPIO_1610: - reg += OMAP1610_GPIO_IRQENABLE1; - mask = 0xffff; - break; -#endif -#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) - case METHOD_GPIO_7XX: - reg += OMAP7XX_GPIO_INT_MASK; - mask = 0xffffffff; - inv = 1; - break; -#endif -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) - case METHOD_GPIO_24XX: - reg += OMAP24XX_GPIO_IRQENABLE1; - mask = 0xffffffff; - break; -#endif -#if defined(CONFIG_ARCH_OMAP4) - case METHOD_GPIO_44XX: - reg += OMAP4_GPIO_IRQSTATUSSET0; - mask = 0xffffffff; - break; -#endif - default: - WARN_ON(1); - return 0; - } - - l = __raw_readl(reg); - if (inv) - l = ~l; - l &= mask; - return l; -} - -static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask, int enable) -{ - void __iomem *reg = bank->base; - u32 l; - - switch (bank->method) { -#ifdef CONFIG_ARCH_OMAP1 - case METHOD_MPUIO: - reg += OMAP_MPUIO_GPIO_MASKIT / bank->stride; - l = __raw_readl(reg); - if (enable) - l &= ~(gpio_mask); - else - l |= gpio_mask; - break; -#endif -#ifdef CONFIG_ARCH_OMAP15XX - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_INT_MASK; - l = __raw_readl(reg); - if (enable) - l &= ~(gpio_mask); - else - l |= gpio_mask; - break; -#endif -#ifdef CONFIG_ARCH_OMAP16XX - case METHOD_GPIO_1610: - if (enable) - reg += OMAP1610_GPIO_SET_IRQENABLE1; - else - reg += OMAP1610_GPIO_CLEAR_IRQENABLE1; - l = gpio_mask; - break; -#endif -#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) - case METHOD_GPIO_7XX: - reg += OMAP7XX_GPIO_INT_MASK; - l = __raw_readl(reg); - if (enable) - l &= ~(gpio_mask); - else - l |= gpio_mask; - break; -#endif -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) - case METHOD_GPIO_24XX: - if (enable) - reg += OMAP24XX_GPIO_SETIRQENABLE1; - else - reg += OMAP24XX_GPIO_CLEARIRQENABLE1; - l = gpio_mask; - break; -#endif -#ifdef CONFIG_ARCH_OMAP4 - case METHOD_GPIO_44XX: - if (enable) - reg += OMAP4_GPIO_IRQSTATUSSET0; - else - reg += OMAP4_GPIO_IRQSTATUSCLR0; - l = gpio_mask; - break; -#endif - default: - WARN_ON(1); - return; - } - __raw_writel(l, reg); -} - -static inline void _set_gpio_irqenable(struct gpio_bank *bank, int gpio, int enable) -{ - _enable_gpio_irqbank(bank, 1 << get_gpio_index(gpio), enable); -} - -/* - * Note that ENAWAKEUP needs to be enabled in GPIO_SYSCONFIG register. - * 1510 does not seem to have a wake-up register. If JTAG is connected - * to the target, system will wake up always on GPIO events. While - * system is running all registered GPIO interrupts need to have wake-up - * enabled. When system is suspended, only selected GPIO interrupts need - * to have wake-up enabled. - */ -static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable) -{ - unsigned long uninitialized_var(flags); - - switch (bank->method) { -#ifdef CONFIG_ARCH_OMAP16XX - case METHOD_MPUIO: - case METHOD_GPIO_1610: - spin_lock_irqsave(&bank->lock, flags); - if (enable) - bank->suspend_wakeup |= (1 << gpio); - else - bank->suspend_wakeup &= ~(1 << gpio); - spin_unlock_irqrestore(&bank->lock, flags); - return 0; -#endif -#ifdef CONFIG_ARCH_OMAP2PLUS - case METHOD_GPIO_24XX: - case METHOD_GPIO_44XX: - if (bank->non_wakeup_gpios & (1 << gpio)) { - printk(KERN_ERR "Unable to modify wakeup on " - "non-wakeup GPIO%d\n", - (bank - gpio_bank) * 32 + gpio); - return -EINVAL; - } - spin_lock_irqsave(&bank->lock, flags); - if (enable) - bank->suspend_wakeup |= (1 << gpio); - else - bank->suspend_wakeup &= ~(1 << gpio); - spin_unlock_irqrestore(&bank->lock, flags); - return 0; -#endif - default: - printk(KERN_ERR "Can't enable GPIO wakeup for method %i\n", - bank->method); - return -EINVAL; - } -} - -static void _reset_gpio(struct gpio_bank *bank, int gpio) -{ - _set_gpio_direction(bank, get_gpio_index(gpio), 1); - _set_gpio_irqenable(bank, gpio, 0); - _clear_gpio_irqstatus(bank, gpio); - _set_gpio_triggering(bank, get_gpio_index(gpio), IRQ_TYPE_NONE); -} - -/* Use disable_irq_wake() and enable_irq_wake() functions from drivers */ -static int gpio_wake_enable(struct irq_data *d, unsigned int enable) -{ - unsigned int gpio = d->irq - IH_GPIO_BASE; - struct gpio_bank *bank; - int retval; - - if (check_gpio(gpio) < 0) - return -ENODEV; - bank = irq_data_get_irq_chip_data(d); - retval = _set_gpio_wakeup(bank, get_gpio_index(gpio), enable); - - return retval; -} - -static int omap_gpio_request(struct gpio_chip *chip, unsigned offset) -{ - struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); - unsigned long flags; - - spin_lock_irqsave(&bank->lock, flags); - - /* Set trigger to none. You need to enable the desired trigger with - * request_irq() or set_irq_type(). - */ - _set_gpio_triggering(bank, offset, IRQ_TYPE_NONE); - -#ifdef CONFIG_ARCH_OMAP15XX - if (bank->method == METHOD_GPIO_1510) { - void __iomem *reg; - - /* Claim the pin for MPU */ - reg = bank->base + OMAP1510_GPIO_PIN_CONTROL; - __raw_writel(__raw_readl(reg) | (1 << offset), reg); - } -#endif - if (!cpu_class_is_omap1()) { - if (!bank->mod_usage) { - void __iomem *reg = bank->base; - u32 ctrl; - - if (cpu_is_omap24xx() || cpu_is_omap34xx()) - reg += OMAP24XX_GPIO_CTRL; - else if (cpu_is_omap44xx()) - reg += OMAP4_GPIO_CTRL; - ctrl = __raw_readl(reg); - /* Module is enabled, clocks are not gated */ - ctrl &= 0xFFFFFFFE; - __raw_writel(ctrl, reg); - } - bank->mod_usage |= 1 << offset; - } - spin_unlock_irqrestore(&bank->lock, flags); - - return 0; -} - -static void omap_gpio_free(struct gpio_chip *chip, unsigned offset) -{ - struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); - unsigned long flags; - - spin_lock_irqsave(&bank->lock, flags); -#ifdef CONFIG_ARCH_OMAP16XX - if (bank->method == METHOD_GPIO_1610) { - /* Disable wake-up during idle for dynamic tick */ - void __iomem *reg = bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA; - __raw_writel(1 << offset, reg); - } -#endif -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) - if (bank->method == METHOD_GPIO_24XX) { - /* Disable wake-up during idle for dynamic tick */ - void __iomem *reg = bank->base + OMAP24XX_GPIO_CLEARWKUENA; - __raw_writel(1 << offset, reg); - } -#endif -#ifdef CONFIG_ARCH_OMAP4 - if (bank->method == METHOD_GPIO_44XX) { - /* Disable wake-up during idle for dynamic tick */ - void __iomem *reg = bank->base + OMAP4_GPIO_IRQWAKEN0; - __raw_writel(1 << offset, reg); - } -#endif - if (!cpu_class_is_omap1()) { - bank->mod_usage &= ~(1 << offset); - if (!bank->mod_usage) { - void __iomem *reg = bank->base; - u32 ctrl; - - if (cpu_is_omap24xx() || cpu_is_omap34xx()) - reg += OMAP24XX_GPIO_CTRL; - else if (cpu_is_omap44xx()) - reg += OMAP4_GPIO_CTRL; - ctrl = __raw_readl(reg); - /* Module is disabled, clocks are gated */ - ctrl |= 1; - __raw_writel(ctrl, reg); - } - } - _reset_gpio(bank, bank->chip.base + offset); - spin_unlock_irqrestore(&bank->lock, flags); -} - -/* - * We need to unmask the GPIO bank interrupt as soon as possible to - * avoid missing GPIO interrupts for other lines in the bank. - * Then we need to mask-read-clear-unmask the triggered GPIO lines - * in the bank to avoid missing nested interrupts for a GPIO line. - * If we wait to unmask individual GPIO lines in the bank after the - * line's interrupt handler has been run, we may miss some nested - * interrupts. - */ -static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) -{ - void __iomem *isr_reg = NULL; - u32 isr; - unsigned int gpio_irq, gpio_index; - struct gpio_bank *bank; - u32 retrigger = 0; - int unmasked = 0; - struct irq_chip *chip = irq_desc_get_chip(desc); - - chained_irq_enter(chip, desc); - - bank = irq_get_handler_data(irq); -#ifdef CONFIG_ARCH_OMAP1 - if (bank->method == METHOD_MPUIO) - isr_reg = bank->base + - OMAP_MPUIO_GPIO_INT / bank->stride; -#endif -#ifdef CONFIG_ARCH_OMAP15XX - if (bank->method == METHOD_GPIO_1510) - isr_reg = bank->base + OMAP1510_GPIO_INT_STATUS; -#endif -#if defined(CONFIG_ARCH_OMAP16XX) - if (bank->method == METHOD_GPIO_1610) - isr_reg = bank->base + OMAP1610_GPIO_IRQSTATUS1; -#endif -#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) - if (bank->method == METHOD_GPIO_7XX) - isr_reg = bank->base + OMAP7XX_GPIO_INT_STATUS; -#endif -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) - if (bank->method == METHOD_GPIO_24XX) - isr_reg = bank->base + OMAP24XX_GPIO_IRQSTATUS1; -#endif -#if defined(CONFIG_ARCH_OMAP4) - if (bank->method == METHOD_GPIO_44XX) - isr_reg = bank->base + OMAP4_GPIO_IRQSTATUS0; -#endif - - if (WARN_ON(!isr_reg)) - goto exit; - - while(1) { - u32 isr_saved, level_mask = 0; - u32 enabled; - - enabled = _get_gpio_irqbank_mask(bank); - isr_saved = isr = __raw_readl(isr_reg) & enabled; - - if (cpu_is_omap15xx() && (bank->method == METHOD_MPUIO)) - isr &= 0x0000ffff; - - if (cpu_class_is_omap2()) { - level_mask = bank->level_mask & enabled; - } - - /* clear edge sensitive interrupts before handler(s) are - called so that we don't miss any interrupt occurred while - executing them */ - _enable_gpio_irqbank(bank, isr_saved & ~level_mask, 0); - _clear_gpio_irqbank(bank, isr_saved & ~level_mask); - _enable_gpio_irqbank(bank, isr_saved & ~level_mask, 1); - - /* if there is only edge sensitive GPIO pin interrupts - configured, we could unmask GPIO bank interrupt immediately */ - if (!level_mask && !unmasked) { - unmasked = 1; - chained_irq_exit(chip, desc); - } - - isr |= retrigger; - retrigger = 0; - if (!isr) - break; - - gpio_irq = bank->virtual_irq_start; - for (; isr != 0; isr >>= 1, gpio_irq++) { - gpio_index = get_gpio_index(irq_to_gpio(gpio_irq)); - - if (!(isr & 1)) - continue; - -#ifdef CONFIG_ARCH_OMAP1 - /* - * Some chips can't respond to both rising and falling - * at the same time. If this irq was requested with - * both flags, we need to flip the ICR data for the IRQ - * to respond to the IRQ for the opposite direction. - * This will be indicated in the bank toggle_mask. - */ - if (bank->toggle_mask & (1 << gpio_index)) - _toggle_gpio_edge_triggering(bank, gpio_index); -#endif - - generic_handle_irq(gpio_irq); - } - } - /* if bank has any level sensitive GPIO pin interrupt - configured, we must unmask the bank interrupt only after - handler(s) are executed in order to avoid spurious bank - interrupt */ -exit: - if (!unmasked) - chained_irq_exit(chip, desc); -} - -static void gpio_irq_shutdown(struct irq_data *d) -{ - unsigned int gpio = d->irq - IH_GPIO_BASE; - struct gpio_bank *bank = irq_data_get_irq_chip_data(d); - - _reset_gpio(bank, gpio); -} - -static void gpio_ack_irq(struct irq_data *d) -{ - unsigned int gpio = d->irq - IH_GPIO_BASE; - struct gpio_bank *bank = irq_data_get_irq_chip_data(d); - - _clear_gpio_irqstatus(bank, gpio); -} - -static void gpio_mask_irq(struct irq_data *d) -{ - unsigned int gpio = d->irq - IH_GPIO_BASE; - struct gpio_bank *bank = irq_data_get_irq_chip_data(d); - - _set_gpio_irqenable(bank, gpio, 0); - _set_gpio_triggering(bank, get_gpio_index(gpio), IRQ_TYPE_NONE); -} - -static void gpio_unmask_irq(struct irq_data *d) -{ - unsigned int gpio = d->irq - IH_GPIO_BASE; - struct gpio_bank *bank = irq_data_get_irq_chip_data(d); - unsigned int irq_mask = 1 << get_gpio_index(gpio); - u32 trigger = irqd_get_trigger_type(d); - - if (trigger) - _set_gpio_triggering(bank, get_gpio_index(gpio), trigger); - - /* For level-triggered GPIOs, the clearing must be done after - * the HW source is cleared, thus after the handler has run */ - if (bank->level_mask & irq_mask) { - _set_gpio_irqenable(bank, gpio, 0); - _clear_gpio_irqstatus(bank, gpio); - } - - _set_gpio_irqenable(bank, gpio, 1); -} - -static struct irq_chip gpio_irq_chip = { - .name = "GPIO", - .irq_shutdown = gpio_irq_shutdown, - .irq_ack = gpio_ack_irq, - .irq_mask = gpio_mask_irq, - .irq_unmask = gpio_unmask_irq, - .irq_set_type = gpio_irq_type, - .irq_set_wake = gpio_wake_enable, -}; - -/*---------------------------------------------------------------------*/ - -#ifdef CONFIG_ARCH_OMAP1 - -/* MPUIO uses the always-on 32k clock */ - -static void mpuio_ack_irq(struct irq_data *d) -{ - /* The ISR is reset automatically, so do nothing here. */ -} - -static void mpuio_mask_irq(struct irq_data *d) -{ - unsigned int gpio = OMAP_MPUIO(d->irq - IH_MPUIO_BASE); - struct gpio_bank *bank = irq_data_get_irq_chip_data(d); - - _set_gpio_irqenable(bank, gpio, 0); -} - -static void mpuio_unmask_irq(struct irq_data *d) -{ - unsigned int gpio = OMAP_MPUIO(d->irq - IH_MPUIO_BASE); - struct gpio_bank *bank = irq_data_get_irq_chip_data(d); - - _set_gpio_irqenable(bank, gpio, 1); -} - -static struct irq_chip mpuio_irq_chip = { - .name = "MPUIO", - .irq_ack = mpuio_ack_irq, - .irq_mask = mpuio_mask_irq, - .irq_unmask = mpuio_unmask_irq, - .irq_set_type = gpio_irq_type, -#ifdef CONFIG_ARCH_OMAP16XX - /* REVISIT: assuming only 16xx supports MPUIO wake events */ - .irq_set_wake = gpio_wake_enable, -#endif -}; - - -#define bank_is_mpuio(bank) ((bank)->method == METHOD_MPUIO) - - -#ifdef CONFIG_ARCH_OMAP16XX - -#include - -static int omap_mpuio_suspend_noirq(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct gpio_bank *bank = platform_get_drvdata(pdev); - void __iomem *mask_reg = bank->base + - OMAP_MPUIO_GPIO_MASKIT / bank->stride; - unsigned long flags; - - spin_lock_irqsave(&bank->lock, flags); - bank->saved_wakeup = __raw_readl(mask_reg); - __raw_writel(0xffff & ~bank->suspend_wakeup, mask_reg); - spin_unlock_irqrestore(&bank->lock, flags); - - return 0; -} - -static int omap_mpuio_resume_noirq(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct gpio_bank *bank = platform_get_drvdata(pdev); - void __iomem *mask_reg = bank->base + - OMAP_MPUIO_GPIO_MASKIT / bank->stride; - unsigned long flags; - - spin_lock_irqsave(&bank->lock, flags); - __raw_writel(bank->saved_wakeup, mask_reg); - spin_unlock_irqrestore(&bank->lock, flags); - - return 0; -} - -static const struct dev_pm_ops omap_mpuio_dev_pm_ops = { - .suspend_noirq = omap_mpuio_suspend_noirq, - .resume_noirq = omap_mpuio_resume_noirq, -}; - -/* use platform_driver for this. */ -static struct platform_driver omap_mpuio_driver = { - .driver = { - .name = "mpuio", - .pm = &omap_mpuio_dev_pm_ops, - }, -}; - -static struct platform_device omap_mpuio_device = { - .name = "mpuio", - .id = -1, - .dev = { - .driver = &omap_mpuio_driver.driver, - } - /* could list the /proc/iomem resources */ -}; - -static inline void mpuio_init(void) -{ - struct gpio_bank *bank = get_gpio_bank(OMAP_MPUIO(0)); - platform_set_drvdata(&omap_mpuio_device, bank); - - if (platform_driver_register(&omap_mpuio_driver) == 0) - (void) platform_device_register(&omap_mpuio_device); -} - -#else -static inline void mpuio_init(void) {} -#endif /* 16xx */ - -#else - -extern struct irq_chip mpuio_irq_chip; - -#define bank_is_mpuio(bank) 0 -static inline void mpuio_init(void) {} - -#endif - -/*---------------------------------------------------------------------*/ - -/* REVISIT these are stupid implementations! replace by ones that - * don't switch on METHOD_* and which mostly avoid spinlocks - */ - -static int gpio_input(struct gpio_chip *chip, unsigned offset) -{ - struct gpio_bank *bank; - unsigned long flags; - - bank = container_of(chip, struct gpio_bank, chip); - spin_lock_irqsave(&bank->lock, flags); - _set_gpio_direction(bank, offset, 1); - spin_unlock_irqrestore(&bank->lock, flags); - return 0; -} - -static int gpio_is_input(struct gpio_bank *bank, int mask) -{ - void __iomem *reg = bank->base; - - switch (bank->method) { - case METHOD_MPUIO: - reg += OMAP_MPUIO_IO_CNTL / bank->stride; - break; - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_DIR_CONTROL; - break; - case METHOD_GPIO_1610: - reg += OMAP1610_GPIO_DIRECTION; - break; - case METHOD_GPIO_7XX: - reg += OMAP7XX_GPIO_DIR_CONTROL; - break; - case METHOD_GPIO_24XX: - reg += OMAP24XX_GPIO_OE; - break; - case METHOD_GPIO_44XX: - reg += OMAP4_GPIO_OE; - break; - default: - WARN_ONCE(1, "gpio_is_input: incorrect OMAP GPIO method"); - return -EINVAL; - } - return __raw_readl(reg) & mask; -} - -static int gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct gpio_bank *bank; - void __iomem *reg; - int gpio; - u32 mask; - - gpio = chip->base + offset; - bank = get_gpio_bank(gpio); - reg = bank->base; - mask = 1 << get_gpio_index(gpio); - - if (gpio_is_input(bank, mask)) - return _get_gpio_datain(bank, gpio); - else - return _get_gpio_dataout(bank, gpio); -} - -static int gpio_output(struct gpio_chip *chip, unsigned offset, int value) -{ - struct gpio_bank *bank; - unsigned long flags; - - bank = container_of(chip, struct gpio_bank, chip); - spin_lock_irqsave(&bank->lock, flags); - _set_gpio_dataout(bank, offset, value); - _set_gpio_direction(bank, offset, 0); - spin_unlock_irqrestore(&bank->lock, flags); - return 0; -} - -static int gpio_debounce(struct gpio_chip *chip, unsigned offset, - unsigned debounce) -{ - struct gpio_bank *bank; - unsigned long flags; - - bank = container_of(chip, struct gpio_bank, chip); - - if (!bank->dbck) { - bank->dbck = clk_get(bank->dev, "dbclk"); - if (IS_ERR(bank->dbck)) - dev_err(bank->dev, "Could not get gpio dbck\n"); - } - - spin_lock_irqsave(&bank->lock, flags); - _set_gpio_debounce(bank, offset, debounce); - spin_unlock_irqrestore(&bank->lock, flags); - - return 0; -} - -static void gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct gpio_bank *bank; - unsigned long flags; - - bank = container_of(chip, struct gpio_bank, chip); - spin_lock_irqsave(&bank->lock, flags); - _set_gpio_dataout(bank, offset, value); - spin_unlock_irqrestore(&bank->lock, flags); -} - -static int gpio_2irq(struct gpio_chip *chip, unsigned offset) -{ - struct gpio_bank *bank; - - bank = container_of(chip, struct gpio_bank, chip); - return bank->virtual_irq_start + offset; -} - -/*---------------------------------------------------------------------*/ - -static void __init omap_gpio_show_rev(struct gpio_bank *bank) -{ - u32 rev; - - if (cpu_is_omap16xx() && !(bank->method != METHOD_MPUIO)) - rev = __raw_readw(bank->base + OMAP1610_GPIO_REVISION); - else if (cpu_is_omap24xx() || cpu_is_omap34xx()) - rev = __raw_readl(bank->base + OMAP24XX_GPIO_REVISION); - else if (cpu_is_omap44xx()) - rev = __raw_readl(bank->base + OMAP4_GPIO_REVISION); - else - return; - - printk(KERN_INFO "OMAP GPIO hardware version %d.%d\n", - (rev >> 4) & 0x0f, rev & 0x0f); -} - -/* This lock class tells lockdep that GPIO irqs are in a different - * category than their parents, so it won't report false recursion. - */ -static struct lock_class_key gpio_lock_class; - -static inline int init_gpio_info(struct platform_device *pdev) -{ - /* TODO: Analyze removing gpio_bank_count usage from driver code */ - gpio_bank = kzalloc(gpio_bank_count * sizeof(struct gpio_bank), - GFP_KERNEL); - if (!gpio_bank) { - dev_err(&pdev->dev, "Memory alloc failed for gpio_bank\n"); - return -ENOMEM; - } - return 0; -} - -/* TODO: Cleanup cpu_is_* checks */ -static void omap_gpio_mod_init(struct gpio_bank *bank, int id) -{ - if (cpu_class_is_omap2()) { - if (cpu_is_omap44xx()) { - __raw_writel(0xffffffff, bank->base + - OMAP4_GPIO_IRQSTATUSCLR0); - __raw_writel(0x00000000, bank->base + - OMAP4_GPIO_DEBOUNCENABLE); - /* Initialize interface clk ungated, module enabled */ - __raw_writel(0, bank->base + OMAP4_GPIO_CTRL); - } else if (cpu_is_omap34xx()) { - __raw_writel(0x00000000, bank->base + - OMAP24XX_GPIO_IRQENABLE1); - __raw_writel(0xffffffff, bank->base + - OMAP24XX_GPIO_IRQSTATUS1); - __raw_writel(0x00000000, bank->base + - OMAP24XX_GPIO_DEBOUNCE_EN); - - /* Initialize interface clk ungated, module enabled */ - __raw_writel(0, bank->base + OMAP24XX_GPIO_CTRL); - } else if (cpu_is_omap24xx()) { - static const u32 non_wakeup_gpios[] = { - 0xe203ffc0, 0x08700040 - }; - if (id < ARRAY_SIZE(non_wakeup_gpios)) - bank->non_wakeup_gpios = non_wakeup_gpios[id]; - } - } else if (cpu_class_is_omap1()) { - if (bank_is_mpuio(bank)) - __raw_writew(0xffff, bank->base + - OMAP_MPUIO_GPIO_MASKIT / bank->stride); - if (cpu_is_omap15xx() && bank->method == METHOD_GPIO_1510) { - __raw_writew(0xffff, bank->base - + OMAP1510_GPIO_INT_MASK); - __raw_writew(0x0000, bank->base - + OMAP1510_GPIO_INT_STATUS); - } - if (cpu_is_omap16xx() && bank->method == METHOD_GPIO_1610) { - __raw_writew(0x0000, bank->base - + OMAP1610_GPIO_IRQENABLE1); - __raw_writew(0xffff, bank->base - + OMAP1610_GPIO_IRQSTATUS1); - __raw_writew(0x0014, bank->base - + OMAP1610_GPIO_SYSCONFIG); - - /* - * Enable system clock for GPIO module. - * The CAM_CLK_CTRL *is* really the right place. - */ - omap_writel(omap_readl(ULPD_CAM_CLK_CTRL) | 0x04, - ULPD_CAM_CLK_CTRL); - } - if (cpu_is_omap7xx() && bank->method == METHOD_GPIO_7XX) { - __raw_writel(0xffffffff, bank->base - + OMAP7XX_GPIO_INT_MASK); - __raw_writel(0x00000000, bank->base - + OMAP7XX_GPIO_INT_STATUS); - } - } -} - -static void __init omap_gpio_chip_init(struct gpio_bank *bank) -{ - int j; - static int gpio; - - bank->mod_usage = 0; - /* - * REVISIT eventually switch from OMAP-specific gpio structs - * over to the generic ones - */ - bank->chip.request = omap_gpio_request; - bank->chip.free = omap_gpio_free; - bank->chip.direction_input = gpio_input; - bank->chip.get = gpio_get; - bank->chip.direction_output = gpio_output; - bank->chip.set_debounce = gpio_debounce; - bank->chip.set = gpio_set; - bank->chip.to_irq = gpio_2irq; - if (bank_is_mpuio(bank)) { - bank->chip.label = "mpuio"; -#ifdef CONFIG_ARCH_OMAP16XX - bank->chip.dev = &omap_mpuio_device.dev; -#endif - bank->chip.base = OMAP_MPUIO(0); - } else { - bank->chip.label = "gpio"; - bank->chip.base = gpio; - gpio += bank_width; - } - bank->chip.ngpio = bank_width; - - gpiochip_add(&bank->chip); - - for (j = bank->virtual_irq_start; - j < bank->virtual_irq_start + bank_width; j++) { - irq_set_lockdep_class(j, &gpio_lock_class); - irq_set_chip_data(j, bank); - if (bank_is_mpuio(bank)) - irq_set_chip(j, &mpuio_irq_chip); - else - irq_set_chip(j, &gpio_irq_chip); - irq_set_handler(j, handle_simple_irq); - set_irq_flags(j, IRQF_VALID); - } - irq_set_chained_handler(bank->irq, gpio_irq_handler); - irq_set_handler_data(bank->irq, bank); -} - -static int __devinit omap_gpio_probe(struct platform_device *pdev) -{ - static int gpio_init_done; - struct omap_gpio_platform_data *pdata; - struct resource *res; - int id; - struct gpio_bank *bank; - - if (!pdev->dev.platform_data) - return -EINVAL; - - pdata = pdev->dev.platform_data; - - if (!gpio_init_done) { - int ret; - - ret = init_gpio_info(pdev); - if (ret) - return ret; - } - - id = pdev->id; - bank = &gpio_bank[id]; - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (unlikely(!res)) { - dev_err(&pdev->dev, "GPIO Bank %i Invalid IRQ resource\n", id); - return -ENODEV; - } - - bank->irq = res->start; - bank->virtual_irq_start = pdata->virtual_irq_start; - bank->method = pdata->bank_type; - bank->dev = &pdev->dev; - bank->dbck_flag = pdata->dbck_flag; - bank->stride = pdata->bank_stride; - bank_width = pdata->bank_width; - - spin_lock_init(&bank->lock); - - /* Static mapping, never released */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (unlikely(!res)) { - dev_err(&pdev->dev, "GPIO Bank %i Invalid mem resource\n", id); - return -ENODEV; - } - - bank->base = ioremap(res->start, resource_size(res)); - if (!bank->base) { - dev_err(&pdev->dev, "Could not ioremap gpio bank%i\n", id); - return -ENOMEM; - } - - pm_runtime_enable(bank->dev); - pm_runtime_get_sync(bank->dev); - - omap_gpio_mod_init(bank, id); - omap_gpio_chip_init(bank); - omap_gpio_show_rev(bank); - - if (!gpio_init_done) - gpio_init_done = 1; - - return 0; -} - -#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS) -static int omap_gpio_suspend(void) -{ - int i; - - if (!cpu_class_is_omap2() && !cpu_is_omap16xx()) - return 0; - - for (i = 0; i < gpio_bank_count; i++) { - struct gpio_bank *bank = &gpio_bank[i]; - void __iomem *wake_status; - void __iomem *wake_clear; - void __iomem *wake_set; - unsigned long flags; - - switch (bank->method) { -#ifdef CONFIG_ARCH_OMAP16XX - case METHOD_GPIO_1610: - wake_status = bank->base + OMAP1610_GPIO_WAKEUPENABLE; - wake_clear = bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA; - wake_set = bank->base + OMAP1610_GPIO_SET_WAKEUPENA; - break; -#endif -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) - case METHOD_GPIO_24XX: - wake_status = bank->base + OMAP24XX_GPIO_WAKE_EN; - wake_clear = bank->base + OMAP24XX_GPIO_CLEARWKUENA; - wake_set = bank->base + OMAP24XX_GPIO_SETWKUENA; - break; -#endif -#ifdef CONFIG_ARCH_OMAP4 - case METHOD_GPIO_44XX: - wake_status = bank->base + OMAP4_GPIO_IRQWAKEN0; - wake_clear = bank->base + OMAP4_GPIO_IRQWAKEN0; - wake_set = bank->base + OMAP4_GPIO_IRQWAKEN0; - break; -#endif - default: - continue; - } - - spin_lock_irqsave(&bank->lock, flags); - bank->saved_wakeup = __raw_readl(wake_status); - __raw_writel(0xffffffff, wake_clear); - __raw_writel(bank->suspend_wakeup, wake_set); - spin_unlock_irqrestore(&bank->lock, flags); - } - - return 0; -} - -static void omap_gpio_resume(void) -{ - int i; - - if (!cpu_class_is_omap2() && !cpu_is_omap16xx()) - return; - - for (i = 0; i < gpio_bank_count; i++) { - struct gpio_bank *bank = &gpio_bank[i]; - void __iomem *wake_clear; - void __iomem *wake_set; - unsigned long flags; - - switch (bank->method) { -#ifdef CONFIG_ARCH_OMAP16XX - case METHOD_GPIO_1610: - wake_clear = bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA; - wake_set = bank->base + OMAP1610_GPIO_SET_WAKEUPENA; - break; -#endif -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) - case METHOD_GPIO_24XX: - wake_clear = bank->base + OMAP24XX_GPIO_CLEARWKUENA; - wake_set = bank->base + OMAP24XX_GPIO_SETWKUENA; - break; -#endif -#ifdef CONFIG_ARCH_OMAP4 - case METHOD_GPIO_44XX: - wake_clear = bank->base + OMAP4_GPIO_IRQWAKEN0; - wake_set = bank->base + OMAP4_GPIO_IRQWAKEN0; - break; -#endif - default: - continue; - } - - spin_lock_irqsave(&bank->lock, flags); - __raw_writel(0xffffffff, wake_clear); - __raw_writel(bank->saved_wakeup, wake_set); - spin_unlock_irqrestore(&bank->lock, flags); - } -} - -static struct syscore_ops omap_gpio_syscore_ops = { - .suspend = omap_gpio_suspend, - .resume = omap_gpio_resume, -}; - -#endif - -#ifdef CONFIG_ARCH_OMAP2PLUS - -static int workaround_enabled; - -void omap2_gpio_prepare_for_idle(int off_mode) -{ - int i, c = 0; - int min = 0; - - if (cpu_is_omap34xx()) - min = 1; - - for (i = min; i < gpio_bank_count; i++) { - struct gpio_bank *bank = &gpio_bank[i]; - u32 l1 = 0, l2 = 0; - int j; - - for (j = 0; j < hweight_long(bank->dbck_enable_mask); j++) - clk_disable(bank->dbck); - - if (!off_mode) - continue; - - /* If going to OFF, remove triggering for all - * non-wakeup GPIOs. Otherwise spurious IRQs will be - * generated. See OMAP2420 Errata item 1.101. */ - if (!(bank->enabled_non_wakeup_gpios)) - continue; - - if (cpu_is_omap24xx() || cpu_is_omap34xx()) { - bank->saved_datain = __raw_readl(bank->base + - OMAP24XX_GPIO_DATAIN); - l1 = __raw_readl(bank->base + - OMAP24XX_GPIO_FALLINGDETECT); - l2 = __raw_readl(bank->base + - OMAP24XX_GPIO_RISINGDETECT); - } - - if (cpu_is_omap44xx()) { - bank->saved_datain = __raw_readl(bank->base + - OMAP4_GPIO_DATAIN); - l1 = __raw_readl(bank->base + - OMAP4_GPIO_FALLINGDETECT); - l2 = __raw_readl(bank->base + - OMAP4_GPIO_RISINGDETECT); - } - - bank->saved_fallingdetect = l1; - bank->saved_risingdetect = l2; - l1 &= ~bank->enabled_non_wakeup_gpios; - l2 &= ~bank->enabled_non_wakeup_gpios; - - if (cpu_is_omap24xx() || cpu_is_omap34xx()) { - __raw_writel(l1, bank->base + - OMAP24XX_GPIO_FALLINGDETECT); - __raw_writel(l2, bank->base + - OMAP24XX_GPIO_RISINGDETECT); - } - - if (cpu_is_omap44xx()) { - __raw_writel(l1, bank->base + OMAP4_GPIO_FALLINGDETECT); - __raw_writel(l2, bank->base + OMAP4_GPIO_RISINGDETECT); - } - - c++; - } - if (!c) { - workaround_enabled = 0; - return; - } - workaround_enabled = 1; -} - -void omap2_gpio_resume_after_idle(void) -{ - int i; - int min = 0; - - if (cpu_is_omap34xx()) - min = 1; - for (i = min; i < gpio_bank_count; i++) { - struct gpio_bank *bank = &gpio_bank[i]; - u32 l = 0, gen, gen0, gen1; - int j; - - for (j = 0; j < hweight_long(bank->dbck_enable_mask); j++) - clk_enable(bank->dbck); - - if (!workaround_enabled) - continue; - - if (!(bank->enabled_non_wakeup_gpios)) - continue; - - if (cpu_is_omap24xx() || cpu_is_omap34xx()) { - __raw_writel(bank->saved_fallingdetect, - bank->base + OMAP24XX_GPIO_FALLINGDETECT); - __raw_writel(bank->saved_risingdetect, - bank->base + OMAP24XX_GPIO_RISINGDETECT); - l = __raw_readl(bank->base + OMAP24XX_GPIO_DATAIN); - } - - if (cpu_is_omap44xx()) { - __raw_writel(bank->saved_fallingdetect, - bank->base + OMAP4_GPIO_FALLINGDETECT); - __raw_writel(bank->saved_risingdetect, - bank->base + OMAP4_GPIO_RISINGDETECT); - l = __raw_readl(bank->base + OMAP4_GPIO_DATAIN); - } - - /* Check if any of the non-wakeup interrupt GPIOs have changed - * state. If so, generate an IRQ by software. This is - * horribly racy, but it's the best we can do to work around - * this silicon bug. */ - l ^= bank->saved_datain; - l &= bank->enabled_non_wakeup_gpios; - - /* - * No need to generate IRQs for the rising edge for gpio IRQs - * configured with falling edge only; and vice versa. - */ - gen0 = l & bank->saved_fallingdetect; - gen0 &= bank->saved_datain; - - gen1 = l & bank->saved_risingdetect; - gen1 &= ~(bank->saved_datain); - - /* FIXME: Consider GPIO IRQs with level detections properly! */ - gen = l & (~(bank->saved_fallingdetect) & - ~(bank->saved_risingdetect)); - /* Consider all GPIO IRQs needed to be updated */ - gen |= gen0 | gen1; - - if (gen) { - u32 old0, old1; - - if (cpu_is_omap24xx() || cpu_is_omap34xx()) { - old0 = __raw_readl(bank->base + - OMAP24XX_GPIO_LEVELDETECT0); - old1 = __raw_readl(bank->base + - OMAP24XX_GPIO_LEVELDETECT1); - __raw_writel(old0 | gen, bank->base + - OMAP24XX_GPIO_LEVELDETECT0); - __raw_writel(old1 | gen, bank->base + - OMAP24XX_GPIO_LEVELDETECT1); - __raw_writel(old0, bank->base + - OMAP24XX_GPIO_LEVELDETECT0); - __raw_writel(old1, bank->base + - OMAP24XX_GPIO_LEVELDETECT1); - } - - if (cpu_is_omap44xx()) { - old0 = __raw_readl(bank->base + - OMAP4_GPIO_LEVELDETECT0); - old1 = __raw_readl(bank->base + - OMAP4_GPIO_LEVELDETECT1); - __raw_writel(old0 | l, bank->base + - OMAP4_GPIO_LEVELDETECT0); - __raw_writel(old1 | l, bank->base + - OMAP4_GPIO_LEVELDETECT1); - __raw_writel(old0, bank->base + - OMAP4_GPIO_LEVELDETECT0); - __raw_writel(old1, bank->base + - OMAP4_GPIO_LEVELDETECT1); - } - } - } - -} - -#endif - -#ifdef CONFIG_ARCH_OMAP3 -/* save the registers of bank 2-6 */ -void omap_gpio_save_context(void) -{ - int i; - - /* saving banks from 2-6 only since GPIO1 is in WKUP */ - for (i = 1; i < gpio_bank_count; i++) { - struct gpio_bank *bank = &gpio_bank[i]; - gpio_context[i].irqenable1 = - __raw_readl(bank->base + OMAP24XX_GPIO_IRQENABLE1); - gpio_context[i].irqenable2 = - __raw_readl(bank->base + OMAP24XX_GPIO_IRQENABLE2); - gpio_context[i].wake_en = - __raw_readl(bank->base + OMAP24XX_GPIO_WAKE_EN); - gpio_context[i].ctrl = - __raw_readl(bank->base + OMAP24XX_GPIO_CTRL); - gpio_context[i].oe = - __raw_readl(bank->base + OMAP24XX_GPIO_OE); - gpio_context[i].leveldetect0 = - __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT0); - gpio_context[i].leveldetect1 = - __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT1); - gpio_context[i].risingdetect = - __raw_readl(bank->base + OMAP24XX_GPIO_RISINGDETECT); - gpio_context[i].fallingdetect = - __raw_readl(bank->base + OMAP24XX_GPIO_FALLINGDETECT); - gpio_context[i].dataout = - __raw_readl(bank->base + OMAP24XX_GPIO_DATAOUT); - } -} - -/* restore the required registers of bank 2-6 */ -void omap_gpio_restore_context(void) -{ - int i; - - for (i = 1; i < gpio_bank_count; i++) { - struct gpio_bank *bank = &gpio_bank[i]; - __raw_writel(gpio_context[i].irqenable1, - bank->base + OMAP24XX_GPIO_IRQENABLE1); - __raw_writel(gpio_context[i].irqenable2, - bank->base + OMAP24XX_GPIO_IRQENABLE2); - __raw_writel(gpio_context[i].wake_en, - bank->base + OMAP24XX_GPIO_WAKE_EN); - __raw_writel(gpio_context[i].ctrl, - bank->base + OMAP24XX_GPIO_CTRL); - __raw_writel(gpio_context[i].oe, - bank->base + OMAP24XX_GPIO_OE); - __raw_writel(gpio_context[i].leveldetect0, - bank->base + OMAP24XX_GPIO_LEVELDETECT0); - __raw_writel(gpio_context[i].leveldetect1, - bank->base + OMAP24XX_GPIO_LEVELDETECT1); - __raw_writel(gpio_context[i].risingdetect, - bank->base + OMAP24XX_GPIO_RISINGDETECT); - __raw_writel(gpio_context[i].fallingdetect, - bank->base + OMAP24XX_GPIO_FALLINGDETECT); - __raw_writel(gpio_context[i].dataout, - bank->base + OMAP24XX_GPIO_DATAOUT); - } -} -#endif - -static struct platform_driver omap_gpio_driver = { - .probe = omap_gpio_probe, - .driver = { - .name = "omap_gpio", - }, -}; - -/* - * gpio driver register needs to be done before - * machine_init functions access gpio APIs. - * Hence omap_gpio_drv_reg() is a postcore_initcall. - */ -static int __init omap_gpio_drv_reg(void) -{ - return platform_driver_register(&omap_gpio_driver); -} -postcore_initcall(omap_gpio_drv_reg); - -static int __init omap_gpio_sysinit(void) -{ - mpuio_init(); - -#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS) - if (cpu_is_omap16xx() || cpu_class_is_omap2()) - register_syscore_ops(&omap_gpio_syscore_ops); -#endif - - return 0; -} - -arch_initcall(omap_gpio_sysinit); diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index becef5954356..a6f1d42f2653 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_GPIO_MAX732X) += max732x.o obj-$(CONFIG_GPIO_MC33880) += mc33880.o obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o obj-$(CONFIG_GPIO_74X164) += 74x164.o +obj-$(CONFIG_ARCH_OMAP) += gpio-omap.o obj-$(CONFIG_GPIO_PCA953X) += pca953x.o obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o obj-$(CONFIG_GPIO_PCH) += pch_gpio.o diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c new file mode 100644 index 000000000000..6c51191da567 --- /dev/null +++ b/drivers/gpio/gpio-omap.c @@ -0,0 +1,2007 @@ +/* + * Support functions for OMAP GPIO + * + * Copyright (C) 2003-2005 Nokia Corporation + * Written by Juha Yrjölä + * + * Copyright (C) 2009 Texas Instruments + * Added OMAP4 support - Santosh Shilimkar + * + * 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 + +struct gpio_bank { + unsigned long pbase; + void __iomem *base; + u16 irq; + u16 virtual_irq_start; + int method; +#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS) + u32 suspend_wakeup; + u32 saved_wakeup; +#endif + u32 non_wakeup_gpios; + u32 enabled_non_wakeup_gpios; + + u32 saved_datain; + u32 saved_fallingdetect; + u32 saved_risingdetect; + u32 level_mask; + u32 toggle_mask; + spinlock_t lock; + struct gpio_chip chip; + struct clk *dbck; + u32 mod_usage; + u32 dbck_enable_mask; + struct device *dev; + bool dbck_flag; + int stride; +}; + +#ifdef CONFIG_ARCH_OMAP3 +struct omap3_gpio_regs { + u32 irqenable1; + u32 irqenable2; + u32 wake_en; + u32 ctrl; + u32 oe; + u32 leveldetect0; + u32 leveldetect1; + u32 risingdetect; + u32 fallingdetect; + u32 dataout; +}; + +static struct omap3_gpio_regs gpio_context[OMAP34XX_NR_GPIOS]; +#endif + +/* + * TODO: Cleanup gpio_bank usage as it is having information + * related to all instances of the device + */ +static struct gpio_bank *gpio_bank; + +static int bank_width; + +/* TODO: Analyze removing gpio_bank_count usage from driver code */ +int gpio_bank_count; + +static inline struct gpio_bank *get_gpio_bank(int gpio) +{ + if (cpu_is_omap15xx()) { + if (OMAP_GPIO_IS_MPUIO(gpio)) + return &gpio_bank[0]; + return &gpio_bank[1]; + } + if (cpu_is_omap16xx()) { + if (OMAP_GPIO_IS_MPUIO(gpio)) + return &gpio_bank[0]; + return &gpio_bank[1 + (gpio >> 4)]; + } + if (cpu_is_omap7xx()) { + if (OMAP_GPIO_IS_MPUIO(gpio)) + return &gpio_bank[0]; + return &gpio_bank[1 + (gpio >> 5)]; + } + if (cpu_is_omap24xx()) + return &gpio_bank[gpio >> 5]; + if (cpu_is_omap34xx() || cpu_is_omap44xx()) + return &gpio_bank[gpio >> 5]; + BUG(); + return NULL; +} + +static inline int get_gpio_index(int gpio) +{ + if (cpu_is_omap7xx()) + return gpio & 0x1f; + if (cpu_is_omap24xx()) + return gpio & 0x1f; + if (cpu_is_omap34xx() || cpu_is_omap44xx()) + return gpio & 0x1f; + return gpio & 0x0f; +} + +static inline int gpio_valid(int gpio) +{ + if (gpio < 0) + return -1; + if (cpu_class_is_omap1() && OMAP_GPIO_IS_MPUIO(gpio)) { + if (gpio >= OMAP_MAX_GPIO_LINES + 16) + return -1; + return 0; + } + if (cpu_is_omap15xx() && gpio < 16) + return 0; + if ((cpu_is_omap16xx()) && gpio < 64) + return 0; + if (cpu_is_omap7xx() && gpio < 192) + return 0; + if (cpu_is_omap2420() && gpio < 128) + return 0; + if (cpu_is_omap2430() && gpio < 160) + return 0; + if ((cpu_is_omap34xx() || cpu_is_omap44xx()) && gpio < 192) + return 0; + return -1; +} + +static int check_gpio(int gpio) +{ + if (unlikely(gpio_valid(gpio) < 0)) { + printk(KERN_ERR "omap-gpio: invalid GPIO %d\n", gpio); + dump_stack(); + return -1; + } + return 0; +} + +static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input) +{ + void __iomem *reg = bank->base; + u32 l; + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP1 + case METHOD_MPUIO: + reg += OMAP_MPUIO_IO_CNTL / bank->stride; + break; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_DIR_CONTROL; + break; +#endif +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + reg += OMAP1610_GPIO_DIRECTION; + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_DIR_CONTROL; + break; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + case METHOD_GPIO_24XX: + reg += OMAP24XX_GPIO_OE; + break; +#endif +#if defined(CONFIG_ARCH_OMAP4) + case METHOD_GPIO_44XX: + reg += OMAP4_GPIO_OE; + break; +#endif + default: + WARN_ON(1); + return; + } + l = __raw_readl(reg); + if (is_input) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + __raw_writel(l, reg); +} + +static void _set_gpio_dataout(struct gpio_bank *bank, int gpio, int enable) +{ + void __iomem *reg = bank->base; + u32 l = 0; + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP1 + case METHOD_MPUIO: + reg += OMAP_MPUIO_OUTPUT / bank->stride; + l = __raw_readl(reg); + if (enable) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + break; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_DATA_OUTPUT; + l = __raw_readl(reg); + if (enable) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + break; +#endif +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + if (enable) + reg += OMAP1610_GPIO_SET_DATAOUT; + else + reg += OMAP1610_GPIO_CLEAR_DATAOUT; + l = 1 << gpio; + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_DATA_OUTPUT; + l = __raw_readl(reg); + if (enable) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + break; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + case METHOD_GPIO_24XX: + if (enable) + reg += OMAP24XX_GPIO_SETDATAOUT; + else + reg += OMAP24XX_GPIO_CLEARDATAOUT; + l = 1 << gpio; + break; +#endif +#ifdef CONFIG_ARCH_OMAP4 + case METHOD_GPIO_44XX: + if (enable) + reg += OMAP4_GPIO_SETDATAOUT; + else + reg += OMAP4_GPIO_CLEARDATAOUT; + l = 1 << gpio; + break; +#endif + default: + WARN_ON(1); + return; + } + __raw_writel(l, reg); +} + +static int _get_gpio_datain(struct gpio_bank *bank, int gpio) +{ + void __iomem *reg; + + if (check_gpio(gpio) < 0) + return -EINVAL; + reg = bank->base; + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP1 + case METHOD_MPUIO: + reg += OMAP_MPUIO_INPUT_LATCH / bank->stride; + break; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_DATA_INPUT; + break; +#endif +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + reg += OMAP1610_GPIO_DATAIN; + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_DATA_INPUT; + break; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + case METHOD_GPIO_24XX: + reg += OMAP24XX_GPIO_DATAIN; + break; +#endif +#ifdef CONFIG_ARCH_OMAP4 + case METHOD_GPIO_44XX: + reg += OMAP4_GPIO_DATAIN; + break; +#endif + default: + return -EINVAL; + } + return (__raw_readl(reg) + & (1 << get_gpio_index(gpio))) != 0; +} + +static int _get_gpio_dataout(struct gpio_bank *bank, int gpio) +{ + void __iomem *reg; + + if (check_gpio(gpio) < 0) + return -EINVAL; + reg = bank->base; + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP1 + case METHOD_MPUIO: + reg += OMAP_MPUIO_OUTPUT / bank->stride; + break; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_DATA_OUTPUT; + break; +#endif +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + reg += OMAP1610_GPIO_DATAOUT; + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_DATA_OUTPUT; + break; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + case METHOD_GPIO_24XX: + reg += OMAP24XX_GPIO_DATAOUT; + break; +#endif +#ifdef CONFIG_ARCH_OMAP4 + case METHOD_GPIO_44XX: + reg += OMAP4_GPIO_DATAOUT; + break; +#endif + default: + return -EINVAL; + } + + return (__raw_readl(reg) & (1 << get_gpio_index(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) + +/** + * _set_gpio_debounce - low level gpio debounce time + * @bank: the gpio bank we're acting upon + * @gpio: the gpio number on this @gpio + * @debounce: debounce time to use + * + * OMAP's debounce time is in 31us steps so we need + * to convert and round up to the closest unit. + */ +static void _set_gpio_debounce(struct gpio_bank *bank, unsigned gpio, + unsigned debounce) +{ + void __iomem *reg = bank->base; + u32 val; + u32 l; + + if (!bank->dbck_flag) + return; + + if (debounce < 32) + debounce = 0x01; + else if (debounce > 7936) + debounce = 0xff; + else + debounce = (debounce / 0x1f) - 1; + + l = 1 << get_gpio_index(gpio); + + if (bank->method == METHOD_GPIO_44XX) + reg += OMAP4_GPIO_DEBOUNCINGTIME; + else + reg += OMAP24XX_GPIO_DEBOUNCE_VAL; + + __raw_writel(debounce, reg); + + reg = bank->base; + if (bank->method == METHOD_GPIO_44XX) + reg += OMAP4_GPIO_DEBOUNCENABLE; + else + reg += OMAP24XX_GPIO_DEBOUNCE_EN; + + val = __raw_readl(reg); + + if (debounce) { + val |= l; + clk_enable(bank->dbck); + } else { + val &= ~l; + clk_disable(bank->dbck); + } + bank->dbck_enable_mask = val; + + __raw_writel(val, reg); +} + +#ifdef CONFIG_ARCH_OMAP2PLUS +static inline void set_24xx_gpio_triggering(struct gpio_bank *bank, int gpio, + int trigger) +{ + void __iomem *base = bank->base; + u32 gpio_bit = 1 << gpio; + u32 val; + + 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); + } 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); + } + if (likely(!(bank->non_wakeup_gpios & gpio_bit))) { + if (cpu_is_omap44xx()) { + if (trigger != 0) + __raw_writel(1 << gpio, bank->base+ + OMAP4_GPIO_IRQWAKEN0); + else { + val = __raw_readl(bank->base + + OMAP4_GPIO_IRQWAKEN0); + __raw_writel(val & (~(1 << gpio)), bank->base + + OMAP4_GPIO_IRQWAKEN0); + } + } else { + /* + * GPIO wakeup request can only be generated on edge + * transitions + */ + if (trigger & IRQ_TYPE_EDGE_BOTH) + __raw_writel(1 << gpio, bank->base + + OMAP24XX_GPIO_SETWKUENA); + else + __raw_writel(1 << gpio, bank->base + + OMAP24XX_GPIO_CLEARWKUENA); + } + } + /* This part needs to be executed always for OMAP34xx */ + if (cpu_is_omap34xx() || (bank->non_wakeup_gpios & gpio_bit)) { + /* + * Log the edge gpio and manually trigger the IRQ + * after resume if the input level changes + * to avoid irq lost during PER RET/OFF mode + * Applies for omap2 non-wakeup gpio and all omap3 gpios + */ + if (trigger & IRQ_TYPE_EDGE_BOTH) + bank->enabled_non_wakeup_gpios |= gpio_bit; + else + bank->enabled_non_wakeup_gpios &= ~gpio_bit; + } + + if (cpu_is_omap44xx()) { + bank->level_mask = + __raw_readl(bank->base + OMAP4_GPIO_LEVELDETECT0) | + __raw_readl(bank->base + OMAP4_GPIO_LEVELDETECT1); + } else { + bank->level_mask = + __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT0) | + __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT1); + } +} +#endif + +#ifdef CONFIG_ARCH_OMAP1 +/* + * This only applies to chips that can't do both rising and falling edge + * detection at once. For all other chips, this function is a noop. + */ +static void _toggle_gpio_edge_triggering(struct gpio_bank *bank, int gpio) +{ + void __iomem *reg = bank->base; + u32 l = 0; + + switch (bank->method) { + case METHOD_MPUIO: + reg += OMAP_MPUIO_GPIO_INT_EDGE / bank->stride; + break; +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_INT_CONTROL; + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_INT_CONTROL; + break; +#endif + default: + return; + } + + l = __raw_readl(reg); + if ((l >> gpio) & 1) + l &= ~(1 << gpio); + else + l |= 1 << gpio; + + __raw_writel(l, reg); +} +#endif + +static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) +{ + void __iomem *reg = bank->base; + u32 l = 0; + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP1 + case METHOD_MPUIO: + reg += OMAP_MPUIO_GPIO_INT_EDGE / bank->stride; + l = __raw_readl(reg); + if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) + bank->toggle_mask |= 1 << gpio; + if (trigger & IRQ_TYPE_EDGE_RISING) + l |= 1 << gpio; + else if (trigger & IRQ_TYPE_EDGE_FALLING) + l &= ~(1 << gpio); + else + goto bad; + break; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_INT_CONTROL; + l = __raw_readl(reg); + if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) + bank->toggle_mask |= 1 << gpio; + if (trigger & IRQ_TYPE_EDGE_RISING) + l |= 1 << gpio; + else if (trigger & IRQ_TYPE_EDGE_FALLING) + l &= ~(1 << gpio); + else + goto bad; + break; +#endif +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + if (gpio & 0x08) + reg += OMAP1610_GPIO_EDGE_CTRL2; + else + reg += OMAP1610_GPIO_EDGE_CTRL1; + gpio &= 0x07; + l = __raw_readl(reg); + l &= ~(3 << (gpio << 1)); + if (trigger & IRQ_TYPE_EDGE_RISING) + l |= 2 << (gpio << 1); + if (trigger & IRQ_TYPE_EDGE_FALLING) + l |= 1 << (gpio << 1); + if (trigger) + /* Enable wake-up during idle for dynamic tick */ + __raw_writel(1 << gpio, bank->base + OMAP1610_GPIO_SET_WAKEUPENA); + else + __raw_writel(1 << gpio, bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA); + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_INT_CONTROL; + l = __raw_readl(reg); + if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) + bank->toggle_mask |= 1 << gpio; + if (trigger & IRQ_TYPE_EDGE_RISING) + l |= 1 << gpio; + else if (trigger & IRQ_TYPE_EDGE_FALLING) + l &= ~(1 << gpio); + else + goto bad; + break; +#endif +#ifdef CONFIG_ARCH_OMAP2PLUS + case METHOD_GPIO_24XX: + case METHOD_GPIO_44XX: + set_24xx_gpio_triggering(bank, gpio, trigger); + return 0; +#endif + default: + goto bad; + } + __raw_writel(l, reg); + return 0; +bad: + return -EINVAL; +} + +static int gpio_irq_type(struct irq_data *d, unsigned type) +{ + struct gpio_bank *bank; + unsigned gpio; + int retval; + unsigned long flags; + + if (!cpu_class_is_omap2() && d->irq > IH_MPUIO_BASE) + gpio = OMAP_MPUIO(d->irq - IH_MPUIO_BASE); + else + gpio = d->irq - IH_GPIO_BASE; + + if (check_gpio(gpio) < 0) + return -EINVAL; + + if (type & ~IRQ_TYPE_SENSE_MASK) + return -EINVAL; + + /* OMAP1 allows only only edge triggering */ + if (!cpu_class_is_omap2() + && (type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH))) + return -EINVAL; + + bank = irq_data_get_irq_chip_data(d); + spin_lock_irqsave(&bank->lock, flags); + retval = _set_gpio_triggering(bank, get_gpio_index(gpio), type); + spin_unlock_irqrestore(&bank->lock, flags); + + if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) + __irq_set_handler_locked(d->irq, handle_level_irq); + else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) + __irq_set_handler_locked(d->irq, handle_edge_irq); + + return retval; +} + +static void _clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask) +{ + void __iomem *reg = bank->base; + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP1 + case METHOD_MPUIO: + /* MPUIO irqstatus is reset by reading the status register, + * so do nothing here */ + return; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_INT_STATUS; + break; +#endif +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + reg += OMAP1610_GPIO_IRQSTATUS1; + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_INT_STATUS; + break; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + case METHOD_GPIO_24XX: + reg += OMAP24XX_GPIO_IRQSTATUS1; + break; +#endif +#if defined(CONFIG_ARCH_OMAP4) + case METHOD_GPIO_44XX: + reg += OMAP4_GPIO_IRQSTATUS0; + break; +#endif + default: + WARN_ON(1); + return; + } + __raw_writel(gpio_mask, reg); + + /* Workaround for clearing DSP GPIO interrupts to allow retention */ + if (cpu_is_omap24xx() || cpu_is_omap34xx()) + reg = bank->base + OMAP24XX_GPIO_IRQSTATUS2; + else if (cpu_is_omap44xx()) + reg = bank->base + OMAP4_GPIO_IRQSTATUS1; + + if (cpu_is_omap24xx() || cpu_is_omap34xx() || cpu_is_omap44xx()) { + __raw_writel(gpio_mask, reg); + + /* Flush posted write for the irq status to avoid spurious interrupts */ + __raw_readl(reg); + } +} + +static inline void _clear_gpio_irqstatus(struct gpio_bank *bank, int gpio) +{ + _clear_gpio_irqbank(bank, 1 << get_gpio_index(gpio)); +} + +static u32 _get_gpio_irqbank_mask(struct gpio_bank *bank) +{ + void __iomem *reg = bank->base; + int inv = 0; + u32 l; + u32 mask; + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP1 + case METHOD_MPUIO: + reg += OMAP_MPUIO_GPIO_MASKIT / bank->stride; + mask = 0xffff; + inv = 1; + break; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_INT_MASK; + mask = 0xffff; + inv = 1; + break; +#endif +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + reg += OMAP1610_GPIO_IRQENABLE1; + mask = 0xffff; + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_INT_MASK; + mask = 0xffffffff; + inv = 1; + break; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + case METHOD_GPIO_24XX: + reg += OMAP24XX_GPIO_IRQENABLE1; + mask = 0xffffffff; + break; +#endif +#if defined(CONFIG_ARCH_OMAP4) + case METHOD_GPIO_44XX: + reg += OMAP4_GPIO_IRQSTATUSSET0; + mask = 0xffffffff; + break; +#endif + default: + WARN_ON(1); + return 0; + } + + l = __raw_readl(reg); + if (inv) + l = ~l; + l &= mask; + return l; +} + +static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask, int enable) +{ + void __iomem *reg = bank->base; + u32 l; + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP1 + case METHOD_MPUIO: + reg += OMAP_MPUIO_GPIO_MASKIT / bank->stride; + l = __raw_readl(reg); + if (enable) + l &= ~(gpio_mask); + else + l |= gpio_mask; + break; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_INT_MASK; + l = __raw_readl(reg); + if (enable) + l &= ~(gpio_mask); + else + l |= gpio_mask; + break; +#endif +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + if (enable) + reg += OMAP1610_GPIO_SET_IRQENABLE1; + else + reg += OMAP1610_GPIO_CLEAR_IRQENABLE1; + l = gpio_mask; + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_INT_MASK; + l = __raw_readl(reg); + if (enable) + l &= ~(gpio_mask); + else + l |= gpio_mask; + break; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + case METHOD_GPIO_24XX: + if (enable) + reg += OMAP24XX_GPIO_SETIRQENABLE1; + else + reg += OMAP24XX_GPIO_CLEARIRQENABLE1; + l = gpio_mask; + break; +#endif +#ifdef CONFIG_ARCH_OMAP4 + case METHOD_GPIO_44XX: + if (enable) + reg += OMAP4_GPIO_IRQSTATUSSET0; + else + reg += OMAP4_GPIO_IRQSTATUSCLR0; + l = gpio_mask; + break; +#endif + default: + WARN_ON(1); + return; + } + __raw_writel(l, reg); +} + +static inline void _set_gpio_irqenable(struct gpio_bank *bank, int gpio, int enable) +{ + _enable_gpio_irqbank(bank, 1 << get_gpio_index(gpio), enable); +} + +/* + * Note that ENAWAKEUP needs to be enabled in GPIO_SYSCONFIG register. + * 1510 does not seem to have a wake-up register. If JTAG is connected + * to the target, system will wake up always on GPIO events. While + * system is running all registered GPIO interrupts need to have wake-up + * enabled. When system is suspended, only selected GPIO interrupts need + * to have wake-up enabled. + */ +static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable) +{ + unsigned long uninitialized_var(flags); + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_MPUIO: + case METHOD_GPIO_1610: + spin_lock_irqsave(&bank->lock, flags); + if (enable) + bank->suspend_wakeup |= (1 << gpio); + else + bank->suspend_wakeup &= ~(1 << gpio); + spin_unlock_irqrestore(&bank->lock, flags); + return 0; +#endif +#ifdef CONFIG_ARCH_OMAP2PLUS + case METHOD_GPIO_24XX: + case METHOD_GPIO_44XX: + if (bank->non_wakeup_gpios & (1 << gpio)) { + printk(KERN_ERR "Unable to modify wakeup on " + "non-wakeup GPIO%d\n", + (bank - gpio_bank) * 32 + gpio); + return -EINVAL; + } + spin_lock_irqsave(&bank->lock, flags); + if (enable) + bank->suspend_wakeup |= (1 << gpio); + else + bank->suspend_wakeup &= ~(1 << gpio); + spin_unlock_irqrestore(&bank->lock, flags); + return 0; +#endif + default: + printk(KERN_ERR "Can't enable GPIO wakeup for method %i\n", + bank->method); + return -EINVAL; + } +} + +static void _reset_gpio(struct gpio_bank *bank, int gpio) +{ + _set_gpio_direction(bank, get_gpio_index(gpio), 1); + _set_gpio_irqenable(bank, gpio, 0); + _clear_gpio_irqstatus(bank, gpio); + _set_gpio_triggering(bank, get_gpio_index(gpio), IRQ_TYPE_NONE); +} + +/* Use disable_irq_wake() and enable_irq_wake() functions from drivers */ +static int gpio_wake_enable(struct irq_data *d, unsigned int enable) +{ + unsigned int gpio = d->irq - IH_GPIO_BASE; + struct gpio_bank *bank; + int retval; + + if (check_gpio(gpio) < 0) + return -ENODEV; + bank = irq_data_get_irq_chip_data(d); + retval = _set_gpio_wakeup(bank, get_gpio_index(gpio), enable); + + return retval; +} + +static int omap_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); + + /* Set trigger to none. You need to enable the desired trigger with + * request_irq() or set_irq_type(). + */ + _set_gpio_triggering(bank, offset, IRQ_TYPE_NONE); + +#ifdef CONFIG_ARCH_OMAP15XX + if (bank->method == METHOD_GPIO_1510) { + void __iomem *reg; + + /* Claim the pin for MPU */ + reg = bank->base + OMAP1510_GPIO_PIN_CONTROL; + __raw_writel(__raw_readl(reg) | (1 << offset), reg); + } +#endif + if (!cpu_class_is_omap1()) { + if (!bank->mod_usage) { + void __iomem *reg = bank->base; + u32 ctrl; + + if (cpu_is_omap24xx() || cpu_is_omap34xx()) + reg += OMAP24XX_GPIO_CTRL; + else if (cpu_is_omap44xx()) + reg += OMAP4_GPIO_CTRL; + ctrl = __raw_readl(reg); + /* Module is enabled, clocks are not gated */ + ctrl &= 0xFFFFFFFE; + __raw_writel(ctrl, reg); + } + bank->mod_usage |= 1 << offset; + } + spin_unlock_irqrestore(&bank->lock, flags); + + return 0; +} + +static void omap_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); +#ifdef CONFIG_ARCH_OMAP16XX + if (bank->method == METHOD_GPIO_1610) { + /* Disable wake-up during idle for dynamic tick */ + void __iomem *reg = bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA; + __raw_writel(1 << offset, reg); + } +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + if (bank->method == METHOD_GPIO_24XX) { + /* Disable wake-up during idle for dynamic tick */ + void __iomem *reg = bank->base + OMAP24XX_GPIO_CLEARWKUENA; + __raw_writel(1 << offset, reg); + } +#endif +#ifdef CONFIG_ARCH_OMAP4 + if (bank->method == METHOD_GPIO_44XX) { + /* Disable wake-up during idle for dynamic tick */ + void __iomem *reg = bank->base + OMAP4_GPIO_IRQWAKEN0; + __raw_writel(1 << offset, reg); + } +#endif + if (!cpu_class_is_omap1()) { + bank->mod_usage &= ~(1 << offset); + if (!bank->mod_usage) { + void __iomem *reg = bank->base; + u32 ctrl; + + if (cpu_is_omap24xx() || cpu_is_omap34xx()) + reg += OMAP24XX_GPIO_CTRL; + else if (cpu_is_omap44xx()) + reg += OMAP4_GPIO_CTRL; + ctrl = __raw_readl(reg); + /* Module is disabled, clocks are gated */ + ctrl |= 1; + __raw_writel(ctrl, reg); + } + } + _reset_gpio(bank, bank->chip.base + offset); + spin_unlock_irqrestore(&bank->lock, flags); +} + +/* + * We need to unmask the GPIO bank interrupt as soon as possible to + * avoid missing GPIO interrupts for other lines in the bank. + * Then we need to mask-read-clear-unmask the triggered GPIO lines + * in the bank to avoid missing nested interrupts for a GPIO line. + * If we wait to unmask individual GPIO lines in the bank after the + * line's interrupt handler has been run, we may miss some nested + * interrupts. + */ +static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + void __iomem *isr_reg = NULL; + u32 isr; + unsigned int gpio_irq, gpio_index; + struct gpio_bank *bank; + u32 retrigger = 0; + int unmasked = 0; + struct irq_chip *chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); + + bank = irq_get_handler_data(irq); +#ifdef CONFIG_ARCH_OMAP1 + if (bank->method == METHOD_MPUIO) + isr_reg = bank->base + + OMAP_MPUIO_GPIO_INT / bank->stride; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + if (bank->method == METHOD_GPIO_1510) + isr_reg = bank->base + OMAP1510_GPIO_INT_STATUS; +#endif +#if defined(CONFIG_ARCH_OMAP16XX) + if (bank->method == METHOD_GPIO_1610) + isr_reg = bank->base + OMAP1610_GPIO_IRQSTATUS1; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + if (bank->method == METHOD_GPIO_7XX) + isr_reg = bank->base + OMAP7XX_GPIO_INT_STATUS; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + if (bank->method == METHOD_GPIO_24XX) + isr_reg = bank->base + OMAP24XX_GPIO_IRQSTATUS1; +#endif +#if defined(CONFIG_ARCH_OMAP4) + if (bank->method == METHOD_GPIO_44XX) + isr_reg = bank->base + OMAP4_GPIO_IRQSTATUS0; +#endif + + if (WARN_ON(!isr_reg)) + goto exit; + + while(1) { + u32 isr_saved, level_mask = 0; + u32 enabled; + + enabled = _get_gpio_irqbank_mask(bank); + isr_saved = isr = __raw_readl(isr_reg) & enabled; + + if (cpu_is_omap15xx() && (bank->method == METHOD_MPUIO)) + isr &= 0x0000ffff; + + if (cpu_class_is_omap2()) { + level_mask = bank->level_mask & enabled; + } + + /* clear edge sensitive interrupts before handler(s) are + called so that we don't miss any interrupt occurred while + executing them */ + _enable_gpio_irqbank(bank, isr_saved & ~level_mask, 0); + _clear_gpio_irqbank(bank, isr_saved & ~level_mask); + _enable_gpio_irqbank(bank, isr_saved & ~level_mask, 1); + + /* if there is only edge sensitive GPIO pin interrupts + configured, we could unmask GPIO bank interrupt immediately */ + if (!level_mask && !unmasked) { + unmasked = 1; + chained_irq_exit(chip, desc); + } + + isr |= retrigger; + retrigger = 0; + if (!isr) + break; + + gpio_irq = bank->virtual_irq_start; + for (; isr != 0; isr >>= 1, gpio_irq++) { + gpio_index = get_gpio_index(irq_to_gpio(gpio_irq)); + + if (!(isr & 1)) + continue; + +#ifdef CONFIG_ARCH_OMAP1 + /* + * Some chips can't respond to both rising and falling + * at the same time. If this irq was requested with + * both flags, we need to flip the ICR data for the IRQ + * to respond to the IRQ for the opposite direction. + * This will be indicated in the bank toggle_mask. + */ + if (bank->toggle_mask & (1 << gpio_index)) + _toggle_gpio_edge_triggering(bank, gpio_index); +#endif + + generic_handle_irq(gpio_irq); + } + } + /* if bank has any level sensitive GPIO pin interrupt + configured, we must unmask the bank interrupt only after + handler(s) are executed in order to avoid spurious bank + interrupt */ +exit: + if (!unmasked) + chained_irq_exit(chip, desc); +} + +static void gpio_irq_shutdown(struct irq_data *d) +{ + unsigned int gpio = d->irq - IH_GPIO_BASE; + struct gpio_bank *bank = irq_data_get_irq_chip_data(d); + + _reset_gpio(bank, gpio); +} + +static void gpio_ack_irq(struct irq_data *d) +{ + unsigned int gpio = d->irq - IH_GPIO_BASE; + struct gpio_bank *bank = irq_data_get_irq_chip_data(d); + + _clear_gpio_irqstatus(bank, gpio); +} + +static void gpio_mask_irq(struct irq_data *d) +{ + unsigned int gpio = d->irq - IH_GPIO_BASE; + struct gpio_bank *bank = irq_data_get_irq_chip_data(d); + + _set_gpio_irqenable(bank, gpio, 0); + _set_gpio_triggering(bank, get_gpio_index(gpio), IRQ_TYPE_NONE); +} + +static void gpio_unmask_irq(struct irq_data *d) +{ + unsigned int gpio = d->irq - IH_GPIO_BASE; + struct gpio_bank *bank = irq_data_get_irq_chip_data(d); + unsigned int irq_mask = 1 << get_gpio_index(gpio); + u32 trigger = irqd_get_trigger_type(d); + + if (trigger) + _set_gpio_triggering(bank, get_gpio_index(gpio), trigger); + + /* For level-triggered GPIOs, the clearing must be done after + * the HW source is cleared, thus after the handler has run */ + if (bank->level_mask & irq_mask) { + _set_gpio_irqenable(bank, gpio, 0); + _clear_gpio_irqstatus(bank, gpio); + } + + _set_gpio_irqenable(bank, gpio, 1); +} + +static struct irq_chip gpio_irq_chip = { + .name = "GPIO", + .irq_shutdown = gpio_irq_shutdown, + .irq_ack = gpio_ack_irq, + .irq_mask = gpio_mask_irq, + .irq_unmask = gpio_unmask_irq, + .irq_set_type = gpio_irq_type, + .irq_set_wake = gpio_wake_enable, +}; + +/*---------------------------------------------------------------------*/ + +#ifdef CONFIG_ARCH_OMAP1 + +/* MPUIO uses the always-on 32k clock */ + +static void mpuio_ack_irq(struct irq_data *d) +{ + /* The ISR is reset automatically, so do nothing here. */ +} + +static void mpuio_mask_irq(struct irq_data *d) +{ + unsigned int gpio = OMAP_MPUIO(d->irq - IH_MPUIO_BASE); + struct gpio_bank *bank = irq_data_get_irq_chip_data(d); + + _set_gpio_irqenable(bank, gpio, 0); +} + +static void mpuio_unmask_irq(struct irq_data *d) +{ + unsigned int gpio = OMAP_MPUIO(d->irq - IH_MPUIO_BASE); + struct gpio_bank *bank = irq_data_get_irq_chip_data(d); + + _set_gpio_irqenable(bank, gpio, 1); +} + +static struct irq_chip mpuio_irq_chip = { + .name = "MPUIO", + .irq_ack = mpuio_ack_irq, + .irq_mask = mpuio_mask_irq, + .irq_unmask = mpuio_unmask_irq, + .irq_set_type = gpio_irq_type, +#ifdef CONFIG_ARCH_OMAP16XX + /* REVISIT: assuming only 16xx supports MPUIO wake events */ + .irq_set_wake = gpio_wake_enable, +#endif +}; + + +#define bank_is_mpuio(bank) ((bank)->method == METHOD_MPUIO) + + +#ifdef CONFIG_ARCH_OMAP16XX + +#include + +static int omap_mpuio_suspend_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_bank *bank = platform_get_drvdata(pdev); + void __iomem *mask_reg = bank->base + + OMAP_MPUIO_GPIO_MASKIT / bank->stride; + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); + bank->saved_wakeup = __raw_readl(mask_reg); + __raw_writel(0xffff & ~bank->suspend_wakeup, mask_reg); + spin_unlock_irqrestore(&bank->lock, flags); + + return 0; +} + +static int omap_mpuio_resume_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_bank *bank = platform_get_drvdata(pdev); + void __iomem *mask_reg = bank->base + + OMAP_MPUIO_GPIO_MASKIT / bank->stride; + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); + __raw_writel(bank->saved_wakeup, mask_reg); + spin_unlock_irqrestore(&bank->lock, flags); + + return 0; +} + +static const struct dev_pm_ops omap_mpuio_dev_pm_ops = { + .suspend_noirq = omap_mpuio_suspend_noirq, + .resume_noirq = omap_mpuio_resume_noirq, +}; + +/* use platform_driver for this. */ +static struct platform_driver omap_mpuio_driver = { + .driver = { + .name = "mpuio", + .pm = &omap_mpuio_dev_pm_ops, + }, +}; + +static struct platform_device omap_mpuio_device = { + .name = "mpuio", + .id = -1, + .dev = { + .driver = &omap_mpuio_driver.driver, + } + /* could list the /proc/iomem resources */ +}; + +static inline void mpuio_init(void) +{ + struct gpio_bank *bank = get_gpio_bank(OMAP_MPUIO(0)); + platform_set_drvdata(&omap_mpuio_device, bank); + + if (platform_driver_register(&omap_mpuio_driver) == 0) + (void) platform_device_register(&omap_mpuio_device); +} + +#else +static inline void mpuio_init(void) {} +#endif /* 16xx */ + +#else + +extern struct irq_chip mpuio_irq_chip; + +#define bank_is_mpuio(bank) 0 +static inline void mpuio_init(void) {} + +#endif + +/*---------------------------------------------------------------------*/ + +/* REVISIT these are stupid implementations! replace by ones that + * don't switch on METHOD_* and which mostly avoid spinlocks + */ + +static int gpio_input(struct gpio_chip *chip, unsigned offset) +{ + struct gpio_bank *bank; + unsigned long flags; + + bank = container_of(chip, struct gpio_bank, chip); + spin_lock_irqsave(&bank->lock, flags); + _set_gpio_direction(bank, offset, 1); + spin_unlock_irqrestore(&bank->lock, flags); + return 0; +} + +static int gpio_is_input(struct gpio_bank *bank, int mask) +{ + void __iomem *reg = bank->base; + + switch (bank->method) { + case METHOD_MPUIO: + reg += OMAP_MPUIO_IO_CNTL / bank->stride; + break; + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_DIR_CONTROL; + break; + case METHOD_GPIO_1610: + reg += OMAP1610_GPIO_DIRECTION; + break; + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_DIR_CONTROL; + break; + case METHOD_GPIO_24XX: + reg += OMAP24XX_GPIO_OE; + break; + case METHOD_GPIO_44XX: + reg += OMAP4_GPIO_OE; + break; + default: + WARN_ONCE(1, "gpio_is_input: incorrect OMAP GPIO method"); + return -EINVAL; + } + return __raw_readl(reg) & mask; +} + +static int gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct gpio_bank *bank; + void __iomem *reg; + int gpio; + u32 mask; + + gpio = chip->base + offset; + bank = get_gpio_bank(gpio); + reg = bank->base; + mask = 1 << get_gpio_index(gpio); + + if (gpio_is_input(bank, mask)) + return _get_gpio_datain(bank, gpio); + else + return _get_gpio_dataout(bank, gpio); +} + +static int gpio_output(struct gpio_chip *chip, unsigned offset, int value) +{ + struct gpio_bank *bank; + unsigned long flags; + + bank = container_of(chip, struct gpio_bank, chip); + spin_lock_irqsave(&bank->lock, flags); + _set_gpio_dataout(bank, offset, value); + _set_gpio_direction(bank, offset, 0); + spin_unlock_irqrestore(&bank->lock, flags); + return 0; +} + +static int gpio_debounce(struct gpio_chip *chip, unsigned offset, + unsigned debounce) +{ + struct gpio_bank *bank; + unsigned long flags; + + bank = container_of(chip, struct gpio_bank, chip); + + if (!bank->dbck) { + bank->dbck = clk_get(bank->dev, "dbclk"); + if (IS_ERR(bank->dbck)) + dev_err(bank->dev, "Could not get gpio dbck\n"); + } + + spin_lock_irqsave(&bank->lock, flags); + _set_gpio_debounce(bank, offset, debounce); + spin_unlock_irqrestore(&bank->lock, flags); + + return 0; +} + +static void gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct gpio_bank *bank; + unsigned long flags; + + bank = container_of(chip, struct gpio_bank, chip); + spin_lock_irqsave(&bank->lock, flags); + _set_gpio_dataout(bank, offset, value); + spin_unlock_irqrestore(&bank->lock, flags); +} + +static int gpio_2irq(struct gpio_chip *chip, unsigned offset) +{ + struct gpio_bank *bank; + + bank = container_of(chip, struct gpio_bank, chip); + return bank->virtual_irq_start + offset; +} + +/*---------------------------------------------------------------------*/ + +static void __init omap_gpio_show_rev(struct gpio_bank *bank) +{ + u32 rev; + + if (cpu_is_omap16xx() && !(bank->method != METHOD_MPUIO)) + rev = __raw_readw(bank->base + OMAP1610_GPIO_REVISION); + else if (cpu_is_omap24xx() || cpu_is_omap34xx()) + rev = __raw_readl(bank->base + OMAP24XX_GPIO_REVISION); + else if (cpu_is_omap44xx()) + rev = __raw_readl(bank->base + OMAP4_GPIO_REVISION); + else + return; + + printk(KERN_INFO "OMAP GPIO hardware version %d.%d\n", + (rev >> 4) & 0x0f, rev & 0x0f); +} + +/* This lock class tells lockdep that GPIO irqs are in a different + * category than their parents, so it won't report false recursion. + */ +static struct lock_class_key gpio_lock_class; + +static inline int init_gpio_info(struct platform_device *pdev) +{ + /* TODO: Analyze removing gpio_bank_count usage from driver code */ + gpio_bank = kzalloc(gpio_bank_count * sizeof(struct gpio_bank), + GFP_KERNEL); + if (!gpio_bank) { + dev_err(&pdev->dev, "Memory alloc failed for gpio_bank\n"); + return -ENOMEM; + } + return 0; +} + +/* TODO: Cleanup cpu_is_* checks */ +static void omap_gpio_mod_init(struct gpio_bank *bank, int id) +{ + if (cpu_class_is_omap2()) { + if (cpu_is_omap44xx()) { + __raw_writel(0xffffffff, bank->base + + OMAP4_GPIO_IRQSTATUSCLR0); + __raw_writel(0x00000000, bank->base + + OMAP4_GPIO_DEBOUNCENABLE); + /* Initialize interface clk ungated, module enabled */ + __raw_writel(0, bank->base + OMAP4_GPIO_CTRL); + } else if (cpu_is_omap34xx()) { + __raw_writel(0x00000000, bank->base + + OMAP24XX_GPIO_IRQENABLE1); + __raw_writel(0xffffffff, bank->base + + OMAP24XX_GPIO_IRQSTATUS1); + __raw_writel(0x00000000, bank->base + + OMAP24XX_GPIO_DEBOUNCE_EN); + + /* Initialize interface clk ungated, module enabled */ + __raw_writel(0, bank->base + OMAP24XX_GPIO_CTRL); + } else if (cpu_is_omap24xx()) { + static const u32 non_wakeup_gpios[] = { + 0xe203ffc0, 0x08700040 + }; + if (id < ARRAY_SIZE(non_wakeup_gpios)) + bank->non_wakeup_gpios = non_wakeup_gpios[id]; + } + } else if (cpu_class_is_omap1()) { + if (bank_is_mpuio(bank)) + __raw_writew(0xffff, bank->base + + OMAP_MPUIO_GPIO_MASKIT / bank->stride); + if (cpu_is_omap15xx() && bank->method == METHOD_GPIO_1510) { + __raw_writew(0xffff, bank->base + + OMAP1510_GPIO_INT_MASK); + __raw_writew(0x0000, bank->base + + OMAP1510_GPIO_INT_STATUS); + } + if (cpu_is_omap16xx() && bank->method == METHOD_GPIO_1610) { + __raw_writew(0x0000, bank->base + + OMAP1610_GPIO_IRQENABLE1); + __raw_writew(0xffff, bank->base + + OMAP1610_GPIO_IRQSTATUS1); + __raw_writew(0x0014, bank->base + + OMAP1610_GPIO_SYSCONFIG); + + /* + * Enable system clock for GPIO module. + * The CAM_CLK_CTRL *is* really the right place. + */ + omap_writel(omap_readl(ULPD_CAM_CLK_CTRL) | 0x04, + ULPD_CAM_CLK_CTRL); + } + if (cpu_is_omap7xx() && bank->method == METHOD_GPIO_7XX) { + __raw_writel(0xffffffff, bank->base + + OMAP7XX_GPIO_INT_MASK); + __raw_writel(0x00000000, bank->base + + OMAP7XX_GPIO_INT_STATUS); + } + } +} + +static void __init omap_gpio_chip_init(struct gpio_bank *bank) +{ + int j; + static int gpio; + + bank->mod_usage = 0; + /* + * REVISIT eventually switch from OMAP-specific gpio structs + * over to the generic ones + */ + bank->chip.request = omap_gpio_request; + bank->chip.free = omap_gpio_free; + bank->chip.direction_input = gpio_input; + bank->chip.get = gpio_get; + bank->chip.direction_output = gpio_output; + bank->chip.set_debounce = gpio_debounce; + bank->chip.set = gpio_set; + bank->chip.to_irq = gpio_2irq; + if (bank_is_mpuio(bank)) { + bank->chip.label = "mpuio"; +#ifdef CONFIG_ARCH_OMAP16XX + bank->chip.dev = &omap_mpuio_device.dev; +#endif + bank->chip.base = OMAP_MPUIO(0); + } else { + bank->chip.label = "gpio"; + bank->chip.base = gpio; + gpio += bank_width; + } + bank->chip.ngpio = bank_width; + + gpiochip_add(&bank->chip); + + for (j = bank->virtual_irq_start; + j < bank->virtual_irq_start + bank_width; j++) { + irq_set_lockdep_class(j, &gpio_lock_class); + irq_set_chip_data(j, bank); + if (bank_is_mpuio(bank)) + irq_set_chip(j, &mpuio_irq_chip); + else + irq_set_chip(j, &gpio_irq_chip); + irq_set_handler(j, handle_simple_irq); + set_irq_flags(j, IRQF_VALID); + } + irq_set_chained_handler(bank->irq, gpio_irq_handler); + irq_set_handler_data(bank->irq, bank); +} + +static int __devinit omap_gpio_probe(struct platform_device *pdev) +{ + static int gpio_init_done; + struct omap_gpio_platform_data *pdata; + struct resource *res; + int id; + struct gpio_bank *bank; + + if (!pdev->dev.platform_data) + return -EINVAL; + + pdata = pdev->dev.platform_data; + + if (!gpio_init_done) { + int ret; + + ret = init_gpio_info(pdev); + if (ret) + return ret; + } + + id = pdev->id; + bank = &gpio_bank[id]; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (unlikely(!res)) { + dev_err(&pdev->dev, "GPIO Bank %i Invalid IRQ resource\n", id); + return -ENODEV; + } + + bank->irq = res->start; + bank->virtual_irq_start = pdata->virtual_irq_start; + bank->method = pdata->bank_type; + bank->dev = &pdev->dev; + bank->dbck_flag = pdata->dbck_flag; + bank->stride = pdata->bank_stride; + bank_width = pdata->bank_width; + + spin_lock_init(&bank->lock); + + /* Static mapping, never released */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!res)) { + dev_err(&pdev->dev, "GPIO Bank %i Invalid mem resource\n", id); + return -ENODEV; + } + + bank->base = ioremap(res->start, resource_size(res)); + if (!bank->base) { + dev_err(&pdev->dev, "Could not ioremap gpio bank%i\n", id); + return -ENOMEM; + } + + pm_runtime_enable(bank->dev); + pm_runtime_get_sync(bank->dev); + + omap_gpio_mod_init(bank, id); + omap_gpio_chip_init(bank); + omap_gpio_show_rev(bank); + + if (!gpio_init_done) + gpio_init_done = 1; + + return 0; +} + +#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS) +static int omap_gpio_suspend(void) +{ + int i; + + if (!cpu_class_is_omap2() && !cpu_is_omap16xx()) + return 0; + + for (i = 0; i < gpio_bank_count; i++) { + struct gpio_bank *bank = &gpio_bank[i]; + void __iomem *wake_status; + void __iomem *wake_clear; + void __iomem *wake_set; + unsigned long flags; + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + wake_status = bank->base + OMAP1610_GPIO_WAKEUPENABLE; + wake_clear = bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA; + wake_set = bank->base + OMAP1610_GPIO_SET_WAKEUPENA; + break; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + case METHOD_GPIO_24XX: + wake_status = bank->base + OMAP24XX_GPIO_WAKE_EN; + wake_clear = bank->base + OMAP24XX_GPIO_CLEARWKUENA; + wake_set = bank->base + OMAP24XX_GPIO_SETWKUENA; + break; +#endif +#ifdef CONFIG_ARCH_OMAP4 + case METHOD_GPIO_44XX: + wake_status = bank->base + OMAP4_GPIO_IRQWAKEN0; + wake_clear = bank->base + OMAP4_GPIO_IRQWAKEN0; + wake_set = bank->base + OMAP4_GPIO_IRQWAKEN0; + break; +#endif + default: + continue; + } + + spin_lock_irqsave(&bank->lock, flags); + bank->saved_wakeup = __raw_readl(wake_status); + __raw_writel(0xffffffff, wake_clear); + __raw_writel(bank->suspend_wakeup, wake_set); + spin_unlock_irqrestore(&bank->lock, flags); + } + + return 0; +} + +static void omap_gpio_resume(void) +{ + int i; + + if (!cpu_class_is_omap2() && !cpu_is_omap16xx()) + return; + + for (i = 0; i < gpio_bank_count; i++) { + struct gpio_bank *bank = &gpio_bank[i]; + void __iomem *wake_clear; + void __iomem *wake_set; + unsigned long flags; + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + wake_clear = bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA; + wake_set = bank->base + OMAP1610_GPIO_SET_WAKEUPENA; + break; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + case METHOD_GPIO_24XX: + wake_clear = bank->base + OMAP24XX_GPIO_CLEARWKUENA; + wake_set = bank->base + OMAP24XX_GPIO_SETWKUENA; + break; +#endif +#ifdef CONFIG_ARCH_OMAP4 + case METHOD_GPIO_44XX: + wake_clear = bank->base + OMAP4_GPIO_IRQWAKEN0; + wake_set = bank->base + OMAP4_GPIO_IRQWAKEN0; + break; +#endif + default: + continue; + } + + spin_lock_irqsave(&bank->lock, flags); + __raw_writel(0xffffffff, wake_clear); + __raw_writel(bank->saved_wakeup, wake_set); + spin_unlock_irqrestore(&bank->lock, flags); + } +} + +static struct syscore_ops omap_gpio_syscore_ops = { + .suspend = omap_gpio_suspend, + .resume = omap_gpio_resume, +}; + +#endif + +#ifdef CONFIG_ARCH_OMAP2PLUS + +static int workaround_enabled; + +void omap2_gpio_prepare_for_idle(int off_mode) +{ + int i, c = 0; + int min = 0; + + if (cpu_is_omap34xx()) + min = 1; + + for (i = min; i < gpio_bank_count; i++) { + struct gpio_bank *bank = &gpio_bank[i]; + u32 l1 = 0, l2 = 0; + int j; + + for (j = 0; j < hweight_long(bank->dbck_enable_mask); j++) + clk_disable(bank->dbck); + + if (!off_mode) + continue; + + /* If going to OFF, remove triggering for all + * non-wakeup GPIOs. Otherwise spurious IRQs will be + * generated. See OMAP2420 Errata item 1.101. */ + if (!(bank->enabled_non_wakeup_gpios)) + continue; + + if (cpu_is_omap24xx() || cpu_is_omap34xx()) { + bank->saved_datain = __raw_readl(bank->base + + OMAP24XX_GPIO_DATAIN); + l1 = __raw_readl(bank->base + + OMAP24XX_GPIO_FALLINGDETECT); + l2 = __raw_readl(bank->base + + OMAP24XX_GPIO_RISINGDETECT); + } + + if (cpu_is_omap44xx()) { + bank->saved_datain = __raw_readl(bank->base + + OMAP4_GPIO_DATAIN); + l1 = __raw_readl(bank->base + + OMAP4_GPIO_FALLINGDETECT); + l2 = __raw_readl(bank->base + + OMAP4_GPIO_RISINGDETECT); + } + + bank->saved_fallingdetect = l1; + bank->saved_risingdetect = l2; + l1 &= ~bank->enabled_non_wakeup_gpios; + l2 &= ~bank->enabled_non_wakeup_gpios; + + if (cpu_is_omap24xx() || cpu_is_omap34xx()) { + __raw_writel(l1, bank->base + + OMAP24XX_GPIO_FALLINGDETECT); + __raw_writel(l2, bank->base + + OMAP24XX_GPIO_RISINGDETECT); + } + + if (cpu_is_omap44xx()) { + __raw_writel(l1, bank->base + OMAP4_GPIO_FALLINGDETECT); + __raw_writel(l2, bank->base + OMAP4_GPIO_RISINGDETECT); + } + + c++; + } + if (!c) { + workaround_enabled = 0; + return; + } + workaround_enabled = 1; +} + +void omap2_gpio_resume_after_idle(void) +{ + int i; + int min = 0; + + if (cpu_is_omap34xx()) + min = 1; + for (i = min; i < gpio_bank_count; i++) { + struct gpio_bank *bank = &gpio_bank[i]; + u32 l = 0, gen, gen0, gen1; + int j; + + for (j = 0; j < hweight_long(bank->dbck_enable_mask); j++) + clk_enable(bank->dbck); + + if (!workaround_enabled) + continue; + + if (!(bank->enabled_non_wakeup_gpios)) + continue; + + if (cpu_is_omap24xx() || cpu_is_omap34xx()) { + __raw_writel(bank->saved_fallingdetect, + bank->base + OMAP24XX_GPIO_FALLINGDETECT); + __raw_writel(bank->saved_risingdetect, + bank->base + OMAP24XX_GPIO_RISINGDETECT); + l = __raw_readl(bank->base + OMAP24XX_GPIO_DATAIN); + } + + if (cpu_is_omap44xx()) { + __raw_writel(bank->saved_fallingdetect, + bank->base + OMAP4_GPIO_FALLINGDETECT); + __raw_writel(bank->saved_risingdetect, + bank->base + OMAP4_GPIO_RISINGDETECT); + l = __raw_readl(bank->base + OMAP4_GPIO_DATAIN); + } + + /* Check if any of the non-wakeup interrupt GPIOs have changed + * state. If so, generate an IRQ by software. This is + * horribly racy, but it's the best we can do to work around + * this silicon bug. */ + l ^= bank->saved_datain; + l &= bank->enabled_non_wakeup_gpios; + + /* + * No need to generate IRQs for the rising edge for gpio IRQs + * configured with falling edge only; and vice versa. + */ + gen0 = l & bank->saved_fallingdetect; + gen0 &= bank->saved_datain; + + gen1 = l & bank->saved_risingdetect; + gen1 &= ~(bank->saved_datain); + + /* FIXME: Consider GPIO IRQs with level detections properly! */ + gen = l & (~(bank->saved_fallingdetect) & + ~(bank->saved_risingdetect)); + /* Consider all GPIO IRQs needed to be updated */ + gen |= gen0 | gen1; + + if (gen) { + u32 old0, old1; + + if (cpu_is_omap24xx() || cpu_is_omap34xx()) { + old0 = __raw_readl(bank->base + + OMAP24XX_GPIO_LEVELDETECT0); + old1 = __raw_readl(bank->base + + OMAP24XX_GPIO_LEVELDETECT1); + __raw_writel(old0 | gen, bank->base + + OMAP24XX_GPIO_LEVELDETECT0); + __raw_writel(old1 | gen, bank->base + + OMAP24XX_GPIO_LEVELDETECT1); + __raw_writel(old0, bank->base + + OMAP24XX_GPIO_LEVELDETECT0); + __raw_writel(old1, bank->base + + OMAP24XX_GPIO_LEVELDETECT1); + } + + if (cpu_is_omap44xx()) { + old0 = __raw_readl(bank->base + + OMAP4_GPIO_LEVELDETECT0); + old1 = __raw_readl(bank->base + + OMAP4_GPIO_LEVELDETECT1); + __raw_writel(old0 | l, bank->base + + OMAP4_GPIO_LEVELDETECT0); + __raw_writel(old1 | l, bank->base + + OMAP4_GPIO_LEVELDETECT1); + __raw_writel(old0, bank->base + + OMAP4_GPIO_LEVELDETECT0); + __raw_writel(old1, bank->base + + OMAP4_GPIO_LEVELDETECT1); + } + } + } + +} + +#endif + +#ifdef CONFIG_ARCH_OMAP3 +/* save the registers of bank 2-6 */ +void omap_gpio_save_context(void) +{ + int i; + + /* saving banks from 2-6 only since GPIO1 is in WKUP */ + for (i = 1; i < gpio_bank_count; i++) { + struct gpio_bank *bank = &gpio_bank[i]; + gpio_context[i].irqenable1 = + __raw_readl(bank->base + OMAP24XX_GPIO_IRQENABLE1); + gpio_context[i].irqenable2 = + __raw_readl(bank->base + OMAP24XX_GPIO_IRQENABLE2); + gpio_context[i].wake_en = + __raw_readl(bank->base + OMAP24XX_GPIO_WAKE_EN); + gpio_context[i].ctrl = + __raw_readl(bank->base + OMAP24XX_GPIO_CTRL); + gpio_context[i].oe = + __raw_readl(bank->base + OMAP24XX_GPIO_OE); + gpio_context[i].leveldetect0 = + __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT0); + gpio_context[i].leveldetect1 = + __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT1); + gpio_context[i].risingdetect = + __raw_readl(bank->base + OMAP24XX_GPIO_RISINGDETECT); + gpio_context[i].fallingdetect = + __raw_readl(bank->base + OMAP24XX_GPIO_FALLINGDETECT); + gpio_context[i].dataout = + __raw_readl(bank->base + OMAP24XX_GPIO_DATAOUT); + } +} + +/* restore the required registers of bank 2-6 */ +void omap_gpio_restore_context(void) +{ + int i; + + for (i = 1; i < gpio_bank_count; i++) { + struct gpio_bank *bank = &gpio_bank[i]; + __raw_writel(gpio_context[i].irqenable1, + bank->base + OMAP24XX_GPIO_IRQENABLE1); + __raw_writel(gpio_context[i].irqenable2, + bank->base + OMAP24XX_GPIO_IRQENABLE2); + __raw_writel(gpio_context[i].wake_en, + bank->base + OMAP24XX_GPIO_WAKE_EN); + __raw_writel(gpio_context[i].ctrl, + bank->base + OMAP24XX_GPIO_CTRL); + __raw_writel(gpio_context[i].oe, + bank->base + OMAP24XX_GPIO_OE); + __raw_writel(gpio_context[i].leveldetect0, + bank->base + OMAP24XX_GPIO_LEVELDETECT0); + __raw_writel(gpio_context[i].leveldetect1, + bank->base + OMAP24XX_GPIO_LEVELDETECT1); + __raw_writel(gpio_context[i].risingdetect, + bank->base + OMAP24XX_GPIO_RISINGDETECT); + __raw_writel(gpio_context[i].fallingdetect, + bank->base + OMAP24XX_GPIO_FALLINGDETECT); + __raw_writel(gpio_context[i].dataout, + bank->base + OMAP24XX_GPIO_DATAOUT); + } +} +#endif + +static struct platform_driver omap_gpio_driver = { + .probe = omap_gpio_probe, + .driver = { + .name = "omap_gpio", + }, +}; + +/* + * gpio driver register needs to be done before + * machine_init functions access gpio APIs. + * Hence omap_gpio_drv_reg() is a postcore_initcall. + */ +static int __init omap_gpio_drv_reg(void) +{ + return platform_driver_register(&omap_gpio_driver); +} +postcore_initcall(omap_gpio_drv_reg); + +static int __init omap_gpio_sysinit(void) +{ + mpuio_init(); + +#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS) + if (cpu_is_omap16xx() || cpu_class_is_omap2()) + register_syscore_ops(&omap_gpio_syscore_ops); +#endif + + return 0; +} + +arch_initcall(omap_gpio_sysinit); -- cgit v1.2.3