diff options
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 49 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 85 | ||||
-rw-r--r-- | drivers/gpio/gpio-74x164.c (renamed from drivers/gpio/74x164.c) | 33 | ||||
-rw-r--r-- | drivers/gpio/gpio-ab8500.c (renamed from drivers/gpio/ab8500-gpio.c) | 0 | ||||
-rw-r--r-- | drivers/gpio/gpio-adp5520.c (renamed from drivers/gpio/adp5520-gpio.c) | 0 | ||||
-rw-r--r-- | drivers/gpio/gpio-adp5588.c (renamed from drivers/gpio/adp5588-gpio.c) | 0 | ||||
-rw-r--r-- | drivers/gpio/gpio-bt8xx.c (renamed from drivers/gpio/bt8xxgpio.c) | 0 | ||||
-rw-r--r-- | drivers/gpio/gpio-cs5535.c (renamed from drivers/gpio/cs5535-gpio.c) | 0 | ||||
-rw-r--r-- | drivers/gpio/gpio-ep93xx.c | 405 | ||||
-rw-r--r-- | drivers/gpio/gpio-exynos4.c | 5 | ||||
-rw-r--r-- | drivers/gpio/gpio-generic.c (renamed from drivers/gpio/basic_mmio_gpio.c) | 6 | ||||
-rw-r--r-- | drivers/gpio/gpio-it8761e.c (renamed from drivers/gpio/it8761e_gpio.c) | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-janz-ttl.c (renamed from drivers/gpio/janz-ttl.c) | 0 | ||||
-rw-r--r-- | drivers/gpio/gpio-langwell.c (renamed from drivers/gpio/langwell_gpio.c) | 4 | ||||
-rw-r--r-- | drivers/gpio/gpio-max7300.c (renamed from drivers/gpio/max7300.c) | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-max7301.c (renamed from drivers/gpio/max7301.c) | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-max730x.c (renamed from drivers/gpio/max730x.c) | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-max732x.c (renamed from drivers/gpio/max732x.c) | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-mc33880.c (renamed from drivers/gpio/mc33880.c) | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-mcp23s08.c (renamed from drivers/gpio/mcp23s08.c) | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-ml-ioh.c (renamed from drivers/gpio/ml_ioh_gpio.c) | 0 | ||||
-rw-r--r-- | drivers/gpio/gpio-mxc.c | 347 | ||||
-rw-r--r-- | drivers/gpio/gpio-mxs.c | 289 | ||||
-rw-r--r-- | drivers/gpio/gpio-pca953x.c (renamed from drivers/gpio/pca953x.c) | 17 | ||||
-rw-r--r-- | drivers/gpio/gpio-pcf857x.c (renamed from drivers/gpio/pcf857x.c) | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-pch.c (renamed from drivers/gpio/pch_gpio.c) | 0 | ||||
-rw-r--r-- | drivers/gpio/gpio-pl061.c (renamed from drivers/gpio/pl061.c) | 4 | ||||
-rw-r--r-- | drivers/gpio/gpio-plat-samsung.c | 3 | ||||
-rw-r--r-- | drivers/gpio/gpio-rdc321x.c (renamed from drivers/gpio/rdc321x-gpio.c) | 0 | ||||
-rw-r--r-- | drivers/gpio/gpio-s5pc100.c | 5 | ||||
-rw-r--r-- | drivers/gpio/gpio-s5pv210.c | 5 | ||||
-rw-r--r-- | drivers/gpio/gpio-sch.c (renamed from drivers/gpio/sch_gpio.c) | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-stmpe.c (renamed from drivers/gpio/stmpe-gpio.c) | 0 | ||||
-rw-r--r-- | drivers/gpio/gpio-sx150x.c (renamed from drivers/gpio/sx150x.c) | 0 | ||||
-rw-r--r-- | drivers/gpio/gpio-tc3589x.c (renamed from drivers/gpio/tc3589x-gpio.c) | 0 | ||||
-rw-r--r-- | drivers/gpio/gpio-tegra.c | 441 | ||||
-rw-r--r-- | drivers/gpio/gpio-timberdale.c (renamed from drivers/gpio/timbgpio.c) | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-tps65910.c (renamed from drivers/gpio/tps65910-gpio.c) | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-twl4030.c (renamed from drivers/gpio/twl4030-gpio.c) | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-u300.c | 5 | ||||
-rw-r--r-- | drivers/gpio/gpio-ucb1400.c (renamed from drivers/gpio/ucb1400_gpio.c) | 0 | ||||
-rw-r--r-- | drivers/gpio/gpio-vr41xx.c (renamed from drivers/gpio/vr41xx_giu.c) | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-vx855.c (renamed from drivers/gpio/vx855_gpio.c) | 0 | ||||
-rw-r--r-- | drivers/gpio/gpio-wm831x.c (renamed from drivers/gpio/wm831x-gpio.c) | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-wm8350.c (renamed from drivers/gpio/wm8350-gpiolib.c) | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-wm8994.c (renamed from drivers/gpio/wm8994-gpio.c) | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-xilinx.c (renamed from drivers/gpio/xilinx_gpio.c) | 0 |
47 files changed, 1605 insertions, 130 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 2967002a9f8..9f06e63fbc8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -63,6 +63,9 @@ config GPIO_SYSFS Kernel drivers may also request that a particular GPIO be exported to userspace; this can be useful when debugging. +config GPIO_GENERIC + tristate + # put drivers in the right section, in alphabetical order config GPIO_MAX730X @@ -70,26 +73,38 @@ config GPIO_MAX730X comment "Memory mapped GPIO drivers:" -config GPIO_BASIC_MMIO_CORE - tristate - help - Provides core functionality for basic memory-mapped GPIO controllers. - -config GPIO_BASIC_MMIO - tristate "Basic memory-mapped GPIO controllers support" - select GPIO_BASIC_MMIO_CORE +config GPIO_GENERIC_PLATFORM + tristate "Generic memory-mapped GPIO controller support (MMIO platform device)" + select GPIO_GENERIC help - Say yes here to support basic memory-mapped GPIO controllers. + Say yes here to support basic platform_device memory-mapped GPIO controllers. config GPIO_IT8761E tristate "IT8761E GPIO support" help Say yes here to support GPIO functionality of IT8761E super I/O chip. +config GPIO_EP93XX + def_bool y + depends on ARCH_EP93XX + select GPIO_GENERIC + config GPIO_EXYNOS4 def_bool y depends on CPU_EXYNOS4210 +config GPIO_MXC + def_bool y + depends on ARCH_MXC + select GPIO_GENERIC + select GENERIC_IRQ_CHIP + +config GPIO_MXS + def_bool y + depends on ARCH_MXS + select GPIO_GENERIC + select GENERIC_IRQ_CHIP + config GPIO_PLAT_SAMSUNG def_bool y depends on SAMSUNG_GPIOLIB_4BIT @@ -137,9 +152,6 @@ config GPIO_SCH The Intel Tunnel Creek processor has 5 GPIOs powered by the core power rail and 9 from suspend power supply. - This driver can also be built as a module. If so, the module - will be called sch-gpio. - config GPIO_VX855 tristate "VIA VX855/VX875 GPIO" depends on MFD_SUPPORT && PCI @@ -202,9 +214,6 @@ config GPIO_PCA953X 16 bits: pca9535, pca9539, pca9555, tca6416 - This driver can also be built as a module. If so, the module - will be called pca953x. - config GPIO_PCA953X_IRQ bool "Interrupt controller support for PCA953x" depends on GPIO_PCA953X=y @@ -296,17 +305,12 @@ config GPIO_ADP5520 This option enables support for on-chip GPIO found on Analog Devices ADP5520 PMICs. - To compile this driver as a module, choose M here: the module will - be called adp5520-gpio. - config GPIO_ADP5588 tristate "ADP5588 I2C GPIO expander" depends on I2C help This option enables support for 18 GPIOs found on Analog Devices ADP5588 GPIO Expanders. - To compile this driver as a module, choose M here: the module will be - called adp5588-gpio. config GPIO_ADP5588_IRQ bool "Interrupt controller support for ADP5588" @@ -428,9 +432,6 @@ config GPIO_UCB1400 This enables support for the Philips UCB1400 GPIO pins. The UCB1400 is an AC97 audio codec. - To compile this driver as a module, choose M here: the - module will be called ucb1400_gpio. - comment "MODULbus GPIO expanders:" config GPIO_JANZ_TTL @@ -441,7 +442,7 @@ config GPIO_JANZ_TTL This driver provides support for driving the pins in output mode only. Input mode is not supported. -config AB8500_GPIO +config GPIO_AB8500 bool "ST-Ericsson AB8500 Mixed Signal Circuit gpio functions" depends on AB8500_CORE && BROKEN help diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index b605f8ec6fb..0fbdd75996e 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -4,47 +4,54 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG obj-$(CONFIG_GPIOLIB) += gpiolib.o -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 +# Device drivers. Generally keep list sorted alphabetically +obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o + +obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o +obj-$(CONFIG_GPIO_AB8500) += gpio-ab8500.o +obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o +obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o +obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o +obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o +obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o obj-$(CONFIG_GPIO_EXYNOS4) += gpio-exynos4.o +obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o +obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o +obj-$(CONFIG_GPIO_LANGWELL) += gpio-langwell.o +obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o +obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o +obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o +obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o +obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o +obj-$(CONFIG_GPIO_MCP23S08) += gpio-mcp23s08.o +obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o +obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o +obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o +obj-$(CONFIG_PLAT_NOMADIK) += gpio-nomadik.o +obj-$(CONFIG_ARCH_OMAP) += gpio-omap.o +obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o +obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o +obj-$(CONFIG_GPIO_PCH) += gpio-pch.o +obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o +obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o + obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o obj-$(CONFIG_GPIO_S5PC100) += gpio-s5pc100.o obj-$(CONFIG_GPIO_S5PV210) += gpio-s5pv210.o -obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o -obj-$(CONFIG_GPIO_MAX730X) += max730x.o -obj-$(CONFIG_GPIO_MAX7300) += max7300.o -obj-$(CONFIG_GPIO_MAX7301) += max7301.o -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 -obj-$(CONFIG_GPIO_PL061) += pl061.o -obj-$(CONFIG_GPIO_STMPE) += stmpe-gpio.o -obj-$(CONFIG_GPIO_TC3589X) += tc3589x-gpio.o -obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o -obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o -obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o -obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o -obj-$(CONFIG_GPIO_CS5535) += cs5535-gpio.o -obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o -obj-$(CONFIG_GPIO_IT8761E) += it8761e_gpio.o -obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o -obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o -obj-$(CONFIG_GPIO_WM8350) += wm8350-gpiolib.o -obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio.o -obj-$(CONFIG_GPIO_SCH) += sch_gpio.o + +obj-$(CONFIG_GPIO_SCH) += gpio-sch.o +obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o +obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o +obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o +obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o +obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o +obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o +obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.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 -obj-$(CONFIG_GPIO_VX855) += vx855_gpio.o -obj-$(CONFIG_GPIO_ML_IOH) += ml_ioh_gpio.o -obj-$(CONFIG_AB8500_GPIO) += ab8500-gpio.o -obj-$(CONFIG_GPIO_TPS65910) += tps65910-gpio.o +obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o +obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o +obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o +obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o +obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o +obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o +obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o diff --git a/drivers/gpio/74x164.c b/drivers/gpio/gpio-74x164.c index 84e07021983..ff525c0958d 100644 --- a/drivers/gpio/74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -16,9 +16,6 @@ #include <linux/gpio.h> #include <linux/slab.h> -#define GEN_74X164_GPIO_COUNT 8 - - struct gen_74x164_chip { struct spi_device *spi; struct gpio_chip gpio_chip; @@ -26,9 +23,7 @@ struct gen_74x164_chip { u8 port_config; }; -static void gen_74x164_set_value(struct gpio_chip *, unsigned, int); - -static struct gen_74x164_chip *gpio_to_chip(struct gpio_chip *gc) +static struct gen_74x164_chip *gpio_to_74x164_chip(struct gpio_chip *gc) { return container_of(gc, struct gen_74x164_chip, gpio_chip); } @@ -39,16 +34,9 @@ static int __gen_74x164_write_config(struct gen_74x164_chip *chip) &chip->port_config, sizeof(chip->port_config)); } -static int gen_74x164_direction_output(struct gpio_chip *gc, - unsigned offset, int val) -{ - gen_74x164_set_value(gc, offset, val); - return 0; -} - static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset) { - struct gen_74x164_chip *chip = gpio_to_chip(gc); + struct gen_74x164_chip *chip = gpio_to_74x164_chip(gc); int ret; mutex_lock(&chip->lock); @@ -61,7 +49,7 @@ static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset) static void gen_74x164_set_value(struct gpio_chip *gc, unsigned offset, int val) { - struct gen_74x164_chip *chip = gpio_to_chip(gc); + struct gen_74x164_chip *chip = gpio_to_74x164_chip(gc); mutex_lock(&chip->lock); if (val) @@ -73,6 +61,13 @@ static void gen_74x164_set_value(struct gpio_chip *gc, mutex_unlock(&chip->lock); } +static int gen_74x164_direction_output(struct gpio_chip *gc, + unsigned offset, int val) +{ + gen_74x164_set_value(gc, offset, val); + return 0; +} + static int __devinit gen_74x164_probe(struct spi_device *spi) { struct gen_74x164_chip *chip; @@ -104,12 +99,12 @@ static int __devinit gen_74x164_probe(struct spi_device *spi) chip->spi = spi; - chip->gpio_chip.label = GEN_74X164_DRIVER_NAME, - chip->gpio_chip.direction_output = gen_74x164_direction_output; + chip->gpio_chip.label = spi->modalias; + chip->gpio_chip.direction_output = gen_74x164_direction_output; chip->gpio_chip.get = gen_74x164_get_value; chip->gpio_chip.set = gen_74x164_set_value; chip->gpio_chip.base = pdata->base; - chip->gpio_chip.ngpio = GEN_74X164_GPIO_COUNT; + chip->gpio_chip.ngpio = 8; chip->gpio_chip.can_sleep = 1; chip->gpio_chip.dev = &spi->dev; chip->gpio_chip.owner = THIS_MODULE; @@ -157,7 +152,7 @@ static int __devexit gen_74x164_remove(struct spi_device *spi) static struct spi_driver gen_74x164_driver = { .driver = { - .name = GEN_74X164_DRIVER_NAME, + .name = "74x164", .owner = THIS_MODULE, }, .probe = gen_74x164_probe, diff --git a/drivers/gpio/ab8500-gpio.c b/drivers/gpio/gpio-ab8500.c index 970053c89ff..970053c89ff 100644 --- a/drivers/gpio/ab8500-gpio.c +++ b/drivers/gpio/gpio-ab8500.c diff --git a/drivers/gpio/adp5520-gpio.c b/drivers/gpio/gpio-adp5520.c index 9f278153700..9f278153700 100644 --- a/drivers/gpio/adp5520-gpio.c +++ b/drivers/gpio/gpio-adp5520.c diff --git a/drivers/gpio/adp5588-gpio.c b/drivers/gpio/gpio-adp5588.c index 3525ad91877..3525ad91877 100644 --- a/drivers/gpio/adp5588-gpio.c +++ b/drivers/gpio/gpio-adp5588.c diff --git a/drivers/gpio/bt8xxgpio.c b/drivers/gpio/gpio-bt8xx.c index aa4f09ad3ce..aa4f09ad3ce 100644 --- a/drivers/gpio/bt8xxgpio.c +++ b/drivers/gpio/gpio-bt8xx.c diff --git a/drivers/gpio/cs5535-gpio.c b/drivers/gpio/gpio-cs5535.c index 6e16cba56ad..6e16cba56ad 100644 --- a/drivers/gpio/cs5535-gpio.c +++ b/drivers/gpio/gpio-cs5535.c diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c new file mode 100644 index 00000000000..3bfd3417ab1 --- /dev/null +++ b/drivers/gpio/gpio-ep93xx.c @@ -0,0 +1,405 @@ +/* + * Generic EP93xx GPIO handling + * + * Copyright (c) 2008 Ryan Mallon <ryan@bluewatersys.com> + * Copyright (c) 2011 H Hartley Sweeten <hsweeten@visionengravers.com> + * + * Based on code originally from: + * linux/arch/arm/mach-ep93xx/core.c + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/basic_mmio_gpio.h> + +#include <mach/hardware.h> + +struct ep93xx_gpio { + void __iomem *mmio_base; + struct bgpio_chip bgc[8]; +}; + +/************************************************************************* + * Interrupt handling for EP93xx on-chip GPIOs + *************************************************************************/ +static unsigned char gpio_int_unmasked[3]; +static unsigned char gpio_int_enabled[3]; +static unsigned char gpio_int_type1[3]; +static unsigned char gpio_int_type2[3]; +static unsigned char gpio_int_debounce[3]; + +/* Port ordering is: A B F */ +static const u8 int_type1_register_offset[3] = { 0x90, 0xac, 0x4c }; +static const u8 int_type2_register_offset[3] = { 0x94, 0xb0, 0x50 }; +static const u8 eoi_register_offset[3] = { 0x98, 0xb4, 0x54 }; +static const u8 int_en_register_offset[3] = { 0x9c, 0xb8, 0x58 }; +static const u8 int_debounce_register_offset[3] = { 0xa8, 0xc4, 0x64 }; + +static void ep93xx_gpio_update_int_params(unsigned port) +{ + BUG_ON(port > 2); + + __raw_writeb(0, EP93XX_GPIO_REG(int_en_register_offset[port])); + + __raw_writeb(gpio_int_type2[port], + EP93XX_GPIO_REG(int_type2_register_offset[port])); + + __raw_writeb(gpio_int_type1[port], + EP93XX_GPIO_REG(int_type1_register_offset[port])); + + __raw_writeb(gpio_int_unmasked[port] & gpio_int_enabled[port], + EP93XX_GPIO_REG(int_en_register_offset[port])); +} + +static inline void ep93xx_gpio_int_mask(unsigned line) +{ + gpio_int_unmasked[line >> 3] &= ~(1 << (line & 7)); +} + +static void ep93xx_gpio_int_debounce(unsigned int irq, bool enable) +{ + int line = irq_to_gpio(irq); + int port = line >> 3; + int port_mask = 1 << (line & 7); + + if (enable) + gpio_int_debounce[port] |= port_mask; + else + gpio_int_debounce[port] &= ~port_mask; + + __raw_writeb(gpio_int_debounce[port], + EP93XX_GPIO_REG(int_debounce_register_offset[port])); +} + +static void ep93xx_gpio_ab_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + unsigned char status; + int i; + + status = __raw_readb(EP93XX_GPIO_A_INT_STATUS); + for (i = 0; i < 8; i++) { + if (status & (1 << i)) { + int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_A(0)) + i; + generic_handle_irq(gpio_irq); + } + } + + status = __raw_readb(EP93XX_GPIO_B_INT_STATUS); + for (i = 0; i < 8; i++) { + if (status & (1 << i)) { + int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_B(0)) + i; + generic_handle_irq(gpio_irq); + } + } +} + +static void ep93xx_gpio_f_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + /* + * map discontiguous hw irq range to continuous sw irq range: + * + * IRQ_EP93XX_GPIO{0..7}MUX -> gpio_to_irq(EP93XX_GPIO_LINE_F({0..7}) + */ + int port_f_idx = ((irq + 1) & 7) ^ 4; /* {19..22,47..50} -> {0..7} */ + int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_F(0)) + port_f_idx; + + generic_handle_irq(gpio_irq); +} + +static void ep93xx_gpio_irq_ack(struct irq_data *d) +{ + int line = irq_to_gpio(d->irq); + int port = line >> 3; + int port_mask = 1 << (line & 7); + + if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) { + gpio_int_type2[port] ^= port_mask; /* switch edge direction */ + ep93xx_gpio_update_int_params(port); + } + + __raw_writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port])); +} + +static void ep93xx_gpio_irq_mask_ack(struct irq_data *d) +{ + int line = irq_to_gpio(d->irq); + int port = line >> 3; + int port_mask = 1 << (line & 7); + + if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) + gpio_int_type2[port] ^= port_mask; /* switch edge direction */ + + gpio_int_unmasked[port] &= ~port_mask; + ep93xx_gpio_update_int_params(port); + + __raw_writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port])); +} + +static void ep93xx_gpio_irq_mask(struct irq_data *d) +{ + int line = irq_to_gpio(d->irq); + int port = line >> 3; + + gpio_int_unmasked[port] &= ~(1 << (line & 7)); + ep93xx_gpio_update_int_params(port); +} + +static void ep93xx_gpio_irq_unmask(struct irq_data *d) +{ + int line = irq_to_gpio(d->irq); + int port = line >> 3; + + gpio_int_unmasked[port] |= 1 << (line & 7); + ep93xx_gpio_update_int_params(port); +} + +/* + * gpio_int_type1 controls whether the interrupt is level (0) or + * edge (1) triggered, while gpio_int_type2 controls whether it + * triggers on low/falling (0) or high/rising (1). + */ +static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type) +{ + const int gpio = irq_to_gpio(d->irq); + const int port = gpio >> 3; + const int port_mask = 1 << (gpio & 7); + irq_flow_handler_t handler; + + gpio_direction_input(gpio); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + gpio_int_type1[port] |= port_mask; + gpio_int_type2[port] |= port_mask; + handler = handle_edge_irq; + break; + case IRQ_TYPE_EDGE_FALLING: + gpio_int_type1[port] |= port_mask; + gpio_int_type2[port] &= ~port_mask; + handler = handle_edge_irq; + break; + case IRQ_TYPE_LEVEL_HIGH: + gpio_int_type1[port] &= ~port_mask; + gpio_int_type2[port] |= port_mask; + handler = handle_level_irq; + break; + case IRQ_TYPE_LEVEL_LOW: + gpio_int_type1[port] &= ~port_mask; + gpio_int_type2[port] &= ~port_mask; + handler = handle_level_irq; + break; + case IRQ_TYPE_EDGE_BOTH: + gpio_int_type1[port] |= port_mask; + /* set initial polarity based on current input level */ + if (gpio_get_value(gpio)) + gpio_int_type2[port] &= ~port_mask; /* falling */ + else + gpio_int_type2[port] |= port_mask; /* rising */ + handler = handle_edge_irq; + break; + default: + pr_err("failed to set irq type %d for gpio %d\n", type, gpio); + return -EINVAL; + } + + __irq_set_handler_locked(d->irq, handler); + + gpio_int_enabled[port] |= port_mask; + + ep93xx_gpio_update_int_params(port); + + return 0; +} + +static struct irq_chip ep93xx_gpio_irq_chip = { + .name = "GPIO", + .irq_ack = ep93xx_gpio_irq_ack, + .irq_mask_ack = ep93xx_gpio_irq_mask_ack, + .irq_mask = ep93xx_gpio_irq_mask, + .irq_unmask = ep93xx_gpio_irq_unmask, + .irq_set_type = ep93xx_gpio_irq_type, +}; + +static void ep93xx_gpio_init_irq(void) +{ + int gpio_irq; + + for (gpio_irq = gpio_to_irq(0); + gpio_irq <= gpio_to_irq(EP93XX_GPIO_LINE_MAX_IRQ); ++gpio_irq) { + irq_set_chip_and_handler(gpio_irq, &ep93xx_gpio_irq_chip, + handle_level_irq); + set_irq_flags(gpio_irq, IRQF_VALID); + } + + irq_set_chained_handler(IRQ_EP93XX_GPIO_AB, + ep93xx_gpio_ab_irq_handler); + irq_set_chained_handler(IRQ_EP93XX_GPIO0MUX, + ep93xx_gpio_f_irq_handler); + irq_set_chained_handler(IRQ_EP93XX_GPIO1MUX, + ep93xx_gpio_f_irq_handler); + irq_set_chained_handler(IRQ_EP93XX_GPIO2MUX, + ep93xx_gpio_f_irq_handler); + irq_set_chained_handler(IRQ_EP93XX_GPIO3MUX, + ep93xx_gpio_f_irq_handler); + irq_set_chained_handler(IRQ_EP93XX_GPIO4MUX, + ep93xx_gpio_f_irq_handler); + irq_set_chained_handler(IRQ_EP93XX_GPIO5MUX, + ep93xx_gpio_f_irq_handler); + irq_set_chained_handler(IRQ_EP93XX_GPIO6MUX, + ep93xx_gpio_f_irq_handler); + irq_set_chained_handler(IRQ_EP93XX_GPIO7MUX, + ep93xx_gpio_f_irq_handler); +} + + +/************************************************************************* + * gpiolib interface for EP93xx on-chip GPIOs + *************************************************************************/ +struct ep93xx_gpio_bank { + const char *label; + int data; + int dir; + int base; + bool has_debounce; +}; + +#define EP93XX_GPIO_BANK(_label, _data, _dir, _base, _debounce) \ + { \ + .label = _label, \ + .data = _data, \ + .dir = _dir, \ + .base = _base, \ + .has_debounce = _debounce, \ + } + +static struct ep93xx_gpio_bank ep93xx_gpio_banks[] = { + EP93XX_GPIO_BANK("A", 0x00, 0x10, 0, true), + EP93XX_GPIO_BANK("B", 0x04, 0x14, 8, true), + EP93XX_GPIO_BANK("C", 0x08, 0x18, 40, false), + EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 24, false), + EP93XX_GPIO_BANK("E", 0x20, 0x24, 32, false), + EP93XX_GPIO_BANK("F", 0x30, 0x34, 16, true), + EP93XX_GPIO_BANK("G", 0x38, 0x3c, 48, false), + EP93XX_GPIO_BANK("H", 0x40, 0x44, 56, false), +}; + +static int ep93xx_gpio_set_debounce(struct gpio_chip *chip, + unsigned offset, unsigned debounce) +{ + int gpio = chip->base + offset; + int irq = gpio_to_irq(gpio); + + if (irq < 0) + return -EINVAL; + + ep93xx_gpio_int_debounce(irq, debounce ? true : false); + + return 0; +} + +static int ep93xx_gpio_add_bank(struct bgpio_chip *bgc, struct device *dev, + void __iomem *mmio_base, struct ep93xx_gpio_bank *bank) +{ + void __iomem *data = mmio_base + bank->data; + void __iomem *dir = mmio_base + bank->dir; + int err; + + err = bgpio_init(bgc, dev, 1, data, NULL, NULL, dir, NULL, false); + if (err) + return err; + + bgc->gc.label = bank->label; + bgc->gc.base = bank->base; + + if (bank->has_debounce) + bgc->gc.set_debounce = ep93xx_gpio_set_debounce; + + return gpiochip_add(&bgc->gc); +} + +static int __devinit ep93xx_gpio_probe(struct platform_device *pdev) +{ + struct ep93xx_gpio *ep93xx_gpio; + struct resource *res; + void __iomem *mmio; + int i; + int ret; + + ep93xx_gpio = kzalloc(sizeof(*ep93xx_gpio), GFP_KERNEL); + if (!ep93xx_gpio) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENXIO; + goto exit_free; + } + + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { + ret = -EBUSY; + goto exit_free; + } + + mmio = ioremap(res->start, resource_size(res)); + if (!mmio) { + ret = -ENXIO; + goto exit_release; + } + ep93xx_gpio->mmio_base = mmio; + + /* Default all ports to GPIO */ + ep93xx_devcfg_set_bits(EP93XX_SYSCON_DEVCFG_KEYS | + EP93XX_SYSCON_DEVCFG_GONK | + EP93XX_SYSCON_DEVCFG_EONIDE | + EP93XX_SYSCON_DEVCFG_GONIDE | + EP93XX_SYSCON_DEVCFG_HONIDE); + + for (i = 0; i < ARRAY_SIZE(ep93xx_gpio_banks); i++) { + struct bgpio_chip *bgc = &ep93xx_gpio->bgc[i]; + struct ep93xx_gpio_bank *bank = &ep93xx_gpio_banks[i]; + + if (ep93xx_gpio_add_bank(bgc, &pdev->dev, mmio, bank)) + dev_warn(&pdev->dev, "Unable to add gpio bank %s\n", + bank->label); + } + + ep93xx_gpio_init_irq(); + + return 0; + +exit_release: + release_mem_region(res->start, resource_size(res)); +exit_free: + kfree(ep93xx_gpio); + dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, ret); + return ret; +} + +static struct platform_driver ep93xx_gpio_driver = { + .driver = { + .name = "gpio-ep93xx", + .owner = THIS_MODULE, + }, + .probe = ep93xx_gpio_probe, +}; + +static int __init ep93xx_gpio_init(void) +{ + return platform_driver_register(&ep93xx_gpio_driver); +} +postcore_initcall(ep93xx_gpio_init); + +MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com> " + "H Hartley Sweeten <hsweeten@visionengravers.com>"); +MODULE_DESCRIPTION("EP93XX GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-exynos4.c b/drivers/gpio/gpio-exynos4.c index 9029835112e..d24b337cf1a 100644 --- a/drivers/gpio/gpio-exynos4.c +++ b/drivers/gpio/gpio-exynos4.c @@ -1,10 +1,9 @@ -/* linux/arch/arm/mach-exynos4/gpiolib.c +/* + * EXYNOS4 - GPIOlib support * * 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. diff --git a/drivers/gpio/basic_mmio_gpio.c b/drivers/gpio/gpio-generic.c index 8152e9f516b..231714def4d 100644 --- a/drivers/gpio/basic_mmio_gpio.c +++ b/drivers/gpio/gpio-generic.c @@ -1,5 +1,5 @@ /* - * Driver for basic memory-mapped GPIO controllers. + * Generic driver for memory-mapped GPIO controllers. * * Copyright 2008 MontaVista Software, Inc. * Copyright 2008,2010 Anton Vorontsov <cbouatmailru@gmail.com> @@ -404,7 +404,7 @@ int __devinit bgpio_init(struct bgpio_chip *bgc, } EXPORT_SYMBOL_GPL(bgpio_init); -#ifdef CONFIG_GPIO_BASIC_MMIO +#ifdef CONFIG_GPIO_GENERIC_PLATFORM static void __iomem *bgpio_map(struct platform_device *pdev, const char *name, @@ -541,7 +541,7 @@ static void __exit bgpio_platform_exit(void) } module_exit(bgpio_platform_exit); -#endif /* CONFIG_GPIO_BASIC_MMIO */ +#endif /* CONFIG_GPIO_GENERIC_PLATFORM */ MODULE_DESCRIPTION("Driver for basic memory-mapped GPIO controllers"); MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>"); diff --git a/drivers/gpio/it8761e_gpio.c b/drivers/gpio/gpio-it8761e.c index 48fc43c4bdd..278b8131701 100644 --- a/drivers/gpio/it8761e_gpio.c +++ b/drivers/gpio/gpio-it8761e.c @@ -1,5 +1,5 @@ /* - * it8761_gpio.c - GPIO interface for IT8761E Super I/O chip + * GPIO interface for IT8761E Super I/O chip * * Author: Denis Turischev <denis@compulab.co.il> * diff --git a/drivers/gpio/janz-ttl.c b/drivers/gpio/gpio-janz-ttl.c index 813ac077e5d..813ac077e5d 100644 --- a/drivers/gpio/janz-ttl.c +++ b/drivers/gpio/gpio-janz-ttl.c diff --git a/drivers/gpio/langwell_gpio.c b/drivers/gpio/gpio-langwell.c index bd6571e0097..e7a7ea760ef 100644 --- a/drivers/gpio/langwell_gpio.c +++ b/drivers/gpio/gpio-langwell.c @@ -1,4 +1,6 @@ -/* langwell_gpio.c Moorestown platform Langwell chip GPIO driver +/* + * Moorestown platform Langwell chip GPIO driver + * * Copyright (c) 2008 - 2009, Intel Corporation. * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/gpio/max7300.c b/drivers/gpio/gpio-max7300.c index 962f661c18c..a5ca0ab1b37 100644 --- a/drivers/gpio/max7300.c +++ b/drivers/gpio/gpio-max7300.c @@ -1,6 +1,4 @@ /* - * drivers/gpio/max7300.c - * * Copyright (C) 2009 Wolfram Sang, Pengutronix * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/gpio/max7301.c b/drivers/gpio/gpio-max7301.c index 92a100ddef6..741acfcbe76 100644 --- a/drivers/gpio/max7301.c +++ b/drivers/gpio/gpio-max7301.c @@ -1,6 +1,4 @@ /* - * drivers/gpio/max7301.c - * * Copyright (C) 2006 Juergen Beisert, Pengutronix * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix * Copyright (C) 2009 Wolfram Sang, Pengutronix diff --git a/drivers/gpio/max730x.c b/drivers/gpio/gpio-max730x.c index 94ce773f95f..05e2dac60b3 100644 --- a/drivers/gpio/max730x.c +++ b/drivers/gpio/gpio-max730x.c @@ -1,6 +1,4 @@ /** - * drivers/gpio/max7301.c - * * Copyright (C) 2006 Juergen Beisert, Pengutronix * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix * Copyright (C) 2009 Wolfram Sang, Pengutronix diff --git a/drivers/gpio/max732x.c b/drivers/gpio/gpio-max732x.c index ad6951edc16..9504120812a 100644 --- a/drivers/gpio/max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -1,5 +1,5 @@ /* - * max732x.c - I2C Port Expander with 8/16 I/O + * MAX732x I2C Port Expander with 8/16 I/O * * Copyright (C) 2007 Marvell International Ltd. * Copyright (C) 2008 Jack Ren <jack.ren@marvell.com> diff --git a/drivers/gpio/mc33880.c b/drivers/gpio/gpio-mc33880.c index 4ec797593bd..b3b4652e89e 100644 --- a/drivers/gpio/mc33880.c +++ b/drivers/gpio/gpio-mc33880.c @@ -1,5 +1,5 @@ /* - * mc33880.c MC33880 high-side/low-side switch GPIO driver + * MC33880 high-side/low-side switch GPIO driver * Copyright (c) 2009 Intel Corporation * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/gpio/mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c index 40e076083ec..0083ec051de 100644 --- a/drivers/gpio/mcp23s08.c +++ b/drivers/gpio/gpio-mcp23s08.c @@ -1,5 +1,5 @@ /* - * mcp23s08.c - SPI gpio expander driver + * MCP23S08 SPI gpio expander driver */ #include <linux/kernel.h> diff --git a/drivers/gpio/ml_ioh_gpio.c b/drivers/gpio/gpio-ml-ioh.c index 1bc621ac353..1bc621ac353 100644 --- a/drivers/gpio/ml_ioh_gpio.c +++ b/drivers/gpio/gpio-ml-ioh.c diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c new file mode 100644 index 00000000000..2f6a81b8f12 --- /dev/null +++ b/drivers/gpio/gpio-mxc.c @@ -0,0 +1,347 @@ +/* + * MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de> + * Copyright 2008 Juergen Beisert, kernel@pengutronix.de + * + * Based on code from Freescale, + * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/basic_mmio_gpio.h> +#include <mach/hardware.h> +#include <asm-generic/bug.h> + +struct mxc_gpio_port { + struct list_head node; + void __iomem *base; + int irq; + int irq_high; + int virtual_irq_start; + struct bgpio_chip bgc; + u32 both_edges; +}; + +/* + * MX2 has one interrupt *for all* gpio ports. The list is used + * to save the references to all ports, so that mx2_gpio_irq_handler + * can walk through all interrupt status registers. + */ +static LIST_HEAD(mxc_gpio_ports); + +#define cpu_is_mx1_mx2() (cpu_is_mx1() || cpu_is_mx2()) + +#define GPIO_DR (cpu_is_mx1_mx2() ? 0x1c : 0x00) +#define GPIO_GDIR (cpu_is_mx1_mx2() ? 0x00 : 0x04) +#define GPIO_PSR (cpu_is_mx1_mx2() ? 0x24 : 0x08) +#define GPIO_ICR1 (cpu_is_mx1_mx2() ? 0x28 : 0x0C) +#define GPIO_ICR2 (cpu_is_mx1_mx2() ? 0x2C : 0x10) +#define GPIO_IMR (cpu_is_mx1_mx2() ? 0x30 : 0x14) +#define GPIO_ISR (cpu_is_mx1_mx2() ? 0x34 : 0x18) + +#define GPIO_INT_LOW_LEV (cpu_is_mx1_mx2() ? 0x3 : 0x0) +#define GPIO_INT_HIGH_LEV (cpu_is_mx1_mx2() ? 0x2 : 0x1) +#define GPIO_INT_RISE_EDGE (cpu_is_mx1_mx2() ? 0x0 : 0x2) +#define GPIO_INT_FALL_EDGE (cpu_is_mx1_mx2() ? 0x1 : 0x3) +#define GPIO_INT_NONE 0x4 + +/* Note: This driver assumes 32 GPIOs are handled in one register */ + +static int gpio_set_irq_type(struct irq_data *d, u32 type) +{ + u32 gpio = irq_to_gpio(d->irq); + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct mxc_gpio_port *port = gc->private; + u32 bit, val; + int edge; + void __iomem *reg = port->base; + + port->both_edges &= ~(1 << (gpio & 31)); + switch (type) { + case IRQ_TYPE_EDGE_RISING: + edge = GPIO_INT_RISE_EDGE; + break; + case IRQ_TYPE_EDGE_FALLING: + edge = GPIO_INT_FALL_EDGE; + break; + case IRQ_TYPE_EDGE_BOTH: + val = gpio_get_value(gpio); + if (val) { + edge = GPIO_INT_LOW_LEV; + pr_debug("mxc: set GPIO %d to low trigger\n", gpio); + } else { + edge = GPIO_INT_HIGH_LEV; + pr_debug("mxc: set GPIO %d to high trigger\n", gpio); + } + port->both_edges |= 1 << (gpio & 31); + break; + case IRQ_TYPE_LEVEL_LOW: + edge = GPIO_INT_LOW_LEV; + break; + case IRQ_TYPE_LEVEL_HIGH: + edge = GPIO_INT_HIGH_LEV; + break; + default: + return -EINVAL; + } + + reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */ + bit = gpio & 0xf; + val = readl(reg) & ~(0x3 << (bit << 1)); + writel(val | (edge << (bit << 1)), reg); + writel(1 << (gpio & 0x1f), port->base + GPIO_ISR); + + return 0; +} + +static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio) +{ + void __iomem *reg = port->base; + u32 bit, val; + int edge; + + reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */ + bit = gpio & 0xf; + val = readl(reg); + edge = (val >> (bit << 1)) & 3; + val &= ~(0x3 << (bit << 1)); + if (edge == GPIO_INT_HIGH_LEV) { + edge = GPIO_INT_LOW_LEV; + pr_debug("mxc: switch GPIO %d to low trigger\n", gpio); + } else if (edge == GPIO_INT_LOW_LEV) { + edge = GPIO_INT_HIGH_LEV; + pr_debug("mxc: switch GPIO %d to high trigger\n", gpio); + } else { + pr_err("mxc: invalid configuration for GPIO %d: %x\n", + gpio, edge); + return; + } + writel(val | (edge << (bit << 1)), reg); +} + +/* handle 32 interrupts in one status register */ +static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat) +{ + u32 gpio_irq_no_base = port->virtual_irq_start; + + while (irq_stat != 0) { + int irqoffset = fls(irq_stat) - 1; + + if (port->both_edges & (1 << irqoffset)) + mxc_flip_edge(port, irqoffset); + + generic_handle_irq(gpio_irq_no_base + irqoffset); + + irq_stat &= ~(1 << irqoffset); + } +} + +/* MX1 and MX3 has one interrupt *per* gpio port */ +static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc) +{ + u32 irq_stat; + struct mxc_gpio_port *port = irq_get_handler_data(irq); + + irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR); + + mxc_gpio_irq_handler(port, irq_stat); +} + +/* MX2 has one interrupt *for all* gpio ports */ +static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc) +{ + u32 irq_msk, irq_stat; + struct mxc_gpio_port *port; + + /* walk through all interrupt status registers */ + list_for_each_entry(port, &mxc_gpio_ports, node) { + irq_msk = readl(port->base + GPIO_IMR); + if (!irq_msk) + continue; + + irq_stat = readl(port->base + GPIO_ISR) & irq_msk; + if (irq_stat) + mxc_gpio_irq_handler(port, irq_stat); + } +} + +/* + * Set interrupt number "irq" in the GPIO as a wake-up source. + * 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. + * @param irq interrupt source number + * @param enable enable as wake-up if equal to non-zero + * @return This function returns 0 on success. + */ +static int gpio_set_wake_irq(struct irq_data *d, u32 enable) +{ + u32 gpio = irq_to_gpio(d->irq); + u32 gpio_idx = gpio & 0x1F; + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct mxc_gpio_port *port = gc->private; + + if (enable) { + if (port->irq_high && (gpio_idx >= 16)) + enable_irq_wake(port->irq_high); + else + enable_irq_wake(port->irq); + } else { + if (port->irq_high && (gpio_idx >= 16)) + disable_irq_wake(port->irq_high); + else + disable_irq_wake(port->irq); + } + + return 0; +} + +static void __init mxc_gpio_init_gc(struct mxc_gpio_port *port) +{ + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + + gc = irq_alloc_generic_chip("gpio-mxc", 1, port->virtual_irq_start, + port->base, handle_level_irq); + gc->private = port; + + ct = gc->chip_types; + ct->chip.irq_ack = irq_gc_ack, + ct->chip.irq_mask = irq_gc_mask_clr_bit; + ct->chip.irq_unmask = irq_gc_mask_set_bit; + ct->chip.irq_set_type = gpio_set_irq_type; + ct->chip.irq_set_wake = gpio_set_wake_irq, + ct->regs.ack = GPIO_ISR; + ct->regs.mask = GPIO_IMR; + + irq_setup_generic_chip(gc, IRQ_MSK(32), IRQ_GC_INIT_NESTED_LOCK, + IRQ_NOREQUEST, 0); +} + +static int __devinit mxc_gpio_probe(struct platform_device *pdev) +{ + struct mxc_gpio_port *port; + struct resource *iores; + int err; + + port = kzalloc(sizeof(struct mxc_gpio_port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + port->virtual_irq_start = MXC_GPIO_IRQ_START + pdev->id * 32; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iores) { + err = -ENODEV; + goto out_kfree; + } + + if (!request_mem_region(iores->start, resource_size(iores), + pdev->name)) { + err = -EBUSY; + goto out_kfree; + } + + port->base = ioremap(iores->start, resource_size(iores)); + if (!port->base) { + err = -ENOMEM; + goto out_release_mem; + } + + port->irq_high = platform_get_irq(pdev, 1); + port->irq = platform_get_irq(pdev, 0); + if (port->irq < 0) { + err = -EINVAL; + goto out_iounmap; + } + + /* disable the interrupt and clear the status */ + writel(0, port->base + GPIO_IMR); + writel(~0, port->base + GPIO_ISR); + + /* gpio-mxc can be a generic irq chip */ + mxc_gpio_init_gc(port); + + if (cpu_is_mx2()) { + /* setup one handler for all GPIO interrupts */ + if (pdev->id == 0) + irq_set_chained_handler(port->irq, + mx2_gpio_irq_handler); + } else { + /* setup one handler for each entry */ + irq_set_chained_handler(port->irq, mx3_gpio_irq_handler); + irq_set_handler_data(port->irq, port); + if (port->irq_high > 0) { + /* setup handler for GPIO 16 to 31 */ + irq_set_chained_handler(port->irq_high, + mx3_gpio_irq_handler); + irq_set_handler_data(port->irq_high, port); + } + } + + err = bgpio_init(&port->bgc, &pdev->dev, 4, + port->base + GPIO_PSR, + port->base + GPIO_DR, NULL, + port->base + GPIO_GDIR, NULL, false); + if (err) + goto out_iounmap; + + port->bgc.gc.base = pdev->id * 32; + + err = gpiochip_add(&port->bgc.gc); + if (err) + goto out_bgpio_remove; + + list_add_tail(&port->node, &mxc_gpio_ports); + + return 0; + +out_bgpio_remove: + bgpio_remove(&port->bgc); +out_iounmap: + iounmap(port->base); +out_release_mem: + release_mem_region(iores->start, resource_size(iores)); +out_kfree: + kfree(port); + dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err); + return err; +} + +static struct platform_driver mxc_gpio_driver = { + .driver = { + .name = "gpio-mxc", + .owner = THIS_MODULE, + }, + .probe = mxc_gpio_probe, +}; + +static int __init gpio_mxc_init(void) +{ + return platform_driver_register(&mxc_gpio_driver); +} +postcore_initcall(gpio_mxc_init); + +MODULE_AUTHOR("Freescale Semiconductor, " + "Daniel Mack <danielncaiaq.de>, " + "Juergen Beisert <kernel@pengutronix.de>"); +MODULE_DESCRIPTION("Freescale MXC GPIO"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c new file mode 100644 index 00000000000..d8cafba8c82 --- /dev/null +++ b/drivers/gpio/gpio-mxs.c @@ -0,0 +1,289 @@ +/* + * MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de> + * Copyright 2008 Juergen Beisert, kernel@pengutronix.de + * + * Based on code from Freescale, + * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/basic_mmio_gpio.h> +#include <mach/mxs.h> + +#define MXS_SET 0x4 +#define MXS_CLR 0x8 + +#define PINCTRL_DOUT(n) ((cpu_is_mx23() ? 0x0500 : 0x0700) + (n) * 0x10) +#define PINCTRL_DIN(n) ((cpu_is_mx23() ? 0x0600 : 0x0900) + (n) * 0x10) +#define PINCTRL_DOE(n) ((cpu_is_mx23() ? 0x0700 : 0x0b00) + (n) * 0x10) +#define PINCTRL_PIN2IRQ(n) ((cpu_is_mx23() ? 0x0800 : 0x1000) + (n) * 0x10) +#define PINCTRL_IRQEN(n) ((cpu_is_mx23() ? 0x0900 : 0x1100) + (n) * 0x10) +#define PINCTRL_IRQLEV(n) ((cpu_is_mx23() ? 0x0a00 : 0x1200) + (n) * 0x10) +#define PINCTRL_IRQPOL(n) ((cpu_is_mx23() ? 0x0b00 : 0x1300) + (n) * 0x10) +#define PINCTRL_IRQSTAT(n) ((cpu_is_mx23() ? 0x0c00 : 0x1400) + (n) * 0x10) + +#define GPIO_INT_FALL_EDGE 0x0 +#define GPIO_INT_LOW_LEV 0x1 +#define GPIO_INT_RISE_EDGE 0x2 +#define GPIO_INT_HIGH_LEV 0x3 +#define GPIO_INT_LEV_MASK (1 << 0) +#define GPIO_INT_POL_MASK (1 << 1) + +struct mxs_gpio_port { + void __iomem *base; + int id; + int irq; + int virtual_irq_start; + struct bgpio_chip bgc; +}; + +/* Note: This driver assumes 32 GPIOs are handled in one register */ + +static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type) +{ + u32 gpio = irq_to_gpio(d->irq); + u32 pin_mask = 1 << (gpio & 31); + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct mxs_gpio_port *port = gc->private; + void __iomem *pin_addr; + int edge; + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + edge = GPIO_INT_RISE_EDGE; + break; + case IRQ_TYPE_EDGE_FALLING: + edge = GPIO_INT_FALL_EDGE; + break; + case IRQ_TYPE_LEVEL_LOW: + edge = GPIO_INT_LOW_LEV; + break; + case IRQ_TYPE_LEVEL_HIGH: + edge = GPIO_INT_HIGH_LEV; + break; + default: + return -EINVAL; + } + + /* set level or edge */ + pin_addr = port->base + PINCTRL_IRQLEV(port->id); + if (edge & GPIO_INT_LEV_MASK) + writel(pin_mask, pin_addr + MXS_SET); + else + writel(pin_mask, pin_addr + MXS_CLR); + + /* set polarity */ + pin_addr = port->base + PINCTRL_IRQPOL(port->id); + if (edge & GPIO_INT_POL_MASK) + writel(pin_mask, pin_addr + MXS_SET); + else + writel(pin_mask, pin_addr + MXS_CLR); + + writel(1 << (gpio & 0x1f), + port->base + PINCTRL_IRQSTAT(port->id) + MXS_CLR); + + return 0; +} + +/* MXS has one interrupt *per* gpio port */ +static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc) +{ + u32 irq_stat; + struct mxs_gpio_port *port = irq_get_handler_data(irq); + u32 gpio_irq_no_base = port->virtual_irq_start; + + desc->irq_data.chip->irq_ack(&desc->irq_data); + + irq_stat = readl(port->base + PINCTRL_IRQSTAT(port->id)) & + readl(port->base + PINCTRL_IRQEN(port->id)); + + while (irq_stat != 0) { + int irqoffset = fls(irq_stat) - 1; + generic_handle_irq(gpio_irq_no_base + irqoffset); + irq_stat &= ~(1 << irqoffset); + } +} + +/* + * Set interrupt number "irq" in the GPIO as a wake-up source. + * 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. + * @param irq interrupt source number + * @param enable enable as wake-up if equal to non-zero + * @return This function returns 0 on success. + */ +static int mxs_gpio_set_wake_irq(struct irq_data *d, unsigned int enable) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct mxs_gpio_port *port = gc->private; + + if (enable) + enable_irq_wake(port->irq); + else + disable_irq_wake(port->irq); + + return 0; +} + +static void __init mxs_gpio_init_gc(struct mxs_gpio_port *port) +{ + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + + gc = irq_alloc_generic_chip("gpio-mxs", 1, port->virtual_irq_start, + port->base, handle_level_irq); + gc->private = port; + + ct = gc->chip_types; + ct->chip.irq_ack = irq_gc_ack, + ct->chip.irq_mask = irq_gc_mask_clr_bit; + ct->chip.irq_unmask = irq_gc_mask_set_bit; + ct->chip.irq_set_type = mxs_gpio_set_irq_type; + ct->chip.irq_set_wake = mxs_gpio_set_wake_irq, + ct->regs.ack = PINCTRL_IRQSTAT(port->id) + MXS_CLR; + ct->regs.mask = PINCTRL_IRQEN(port->id); + + irq_setup_generic_chip(gc, IRQ_MSK(32), 0, IRQ_NOREQUEST, 0); +} + +static int mxs_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + struct mxs_gpio_port *port = + container_of(bgc, struct mxs_gpio_port, bgc); + + return port->virtual_irq_start + offset; +} + +static int __devinit mxs_gpio_probe(struct platform_device *pdev) +{ + static void __iomem *base; + struct mxs_gpio_port *port; + struct resource *iores = NULL; + int err; + + port = kzalloc(sizeof(struct mxs_gpio_port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + port->id = pdev->id; + port->virtual_irq_start = MXS_GPIO_IRQ_START + port->id * 32; + + /* + * map memory region only once, as all the gpio ports + * share the same one + */ + if (!base) { + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iores) { + err = -ENODEV; + goto out_kfree; + } + + if (!request_mem_region(iores->start, resource_size(iores), + pdev->name)) { + err = -EBUSY; + goto out_kfree; + } + + base = ioremap(iores->start, resource_size(iores)); + if (!base) { + err = -ENOMEM; + goto out_release_mem; + } + } + port->base = base; + + port->irq = platform_get_irq(pdev, 0); + if (port->irq < 0) { + err = -EINVAL; + goto out_iounmap; + } + + /* + * select the pin interrupt functionality but initially + * disable the interrupts + */ + writel(~0U, port->base + PINCTRL_PIN2IRQ(port->id)); + writel(0, port->base + PINCTRL_IRQEN(port->id)); + + /* clear address has to be used to clear IRQSTAT bits */ + writel(~0U, port->base + PINCTRL_IRQSTAT(port->id) + MXS_CLR); + + /* gpio-mxs can be a generic irq chip */ + mxs_gpio_init_gc(port); + + /* setup one handler for each entry */ + irq_set_chained_handler(port->irq, mxs_gpio_irq_handler); + irq_set_handler_data(port->irq, port); + + err = bgpio_init(&port->bgc, &pdev->dev, 4, + port->base + PINCTRL_DIN(port->id), + port->base + PINCTRL_DOUT(port->id), NULL, + port->base + PINCTRL_DOE(port->id), NULL, false); + if (err) + goto out_iounmap; + + port->bgc.gc.to_irq = mxs_gpio_to_irq; + port->bgc.gc.base = port->id * 32; + + err = gpiochip_add(&port->bgc.gc); + if (err) + goto out_bgpio_remove; + + return 0; + +out_bgpio_remove: + bgpio_remove(&port->bgc); +out_iounmap: + if (iores) + iounmap(port->base); +out_release_mem: + if (iores) + release_mem_region(iores->start, resource_size(iores)); +out_kfree: + kfree(port); + dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err); + return err; +} + +static struct platform_driver mxs_gpio_driver = { + .driver = { + .name = "gpio-mxs", + .owner = THIS_MODULE, + }, + .probe = mxs_gpio_probe, +}; + +static int __init mxs_gpio_init(void) +{ + return platform_driver_register(&mxs_gpio_driver); +} +postcore_initcall(mxs_gpio_init); + +MODULE_AUTHOR("Freescale Semiconductor, " + "Daniel Mack <danielncaiaq.de>, " + "Juergen Beisert <kernel@pengutronix.de>"); +MODULE_DESCRIPTION("Freescale MXS GPIO"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/gpio-pca953x.c index 0451d7ac94a..4b8446e98de 100644 --- a/drivers/gpio/pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -1,5 +1,5 @@ /* - * pca953x.c - 4/8/16 bit I/O ports + * PCA953x 4/8/16 bit I/O ports * * Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com> * Copyright (C) 2007 Marvell International Ltd. @@ -437,7 +437,7 @@ static irqreturn_t pca953x_irq_handler(int irq, void *devid) do { level = __ffs(pending); - generic_handle_irq(level + chip->irq_base); + handle_nested_irq(level + chip->irq_base); pending &= ~(1 << level); } while (pending); @@ -474,15 +474,19 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, * this purpose. */ chip->irq_stat &= chip->reg_direction; - chip->irq_base = pdata->irq_base; mutex_init(&chip->irq_lock); + chip->irq_base = irq_alloc_descs(-1, pdata->irq_base, chip->gpio_chip.ngpio, -1); + if (chip->irq_base < 0) + goto out_failed; + for (lvl = 0; lvl < chip->gpio_chip.ngpio; lvl++) { int irq = lvl + chip->irq_base; + irq_clear_status_flags(irq, IRQ_NOREQUEST); irq_set_chip_data(irq, chip); - irq_set_chip_and_handler(irq, &pca953x_irq_chip, - handle_simple_irq); + irq_set_chip(irq, &pca953x_irq_chip); + irq_set_nested_thread(irq, true); #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); #else @@ -493,8 +497,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, ret = request_threaded_irq(client->irq, NULL, pca953x_irq_handler, - IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, dev_name(&client->dev), chip); if (ret) { dev_err(&client->dev, "failed to request irq %d\n", diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 879b473aab5..7369fdda92b 100644 --- a/drivers/gpio/pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -1,5 +1,5 @@ /* - * pcf857x - driver for pcf857x, pca857x, and pca967x I2C GPIO expanders + * Driver for pcf857x, pca857x, and pca967x I2C GPIO expanders * * Copyright (C) 2007 David Brownell * diff --git a/drivers/gpio/pch_gpio.c b/drivers/gpio/gpio-pch.c index 36919e77c49..36919e77c49 100644 --- a/drivers/gpio/pch_gpio.c +++ b/drivers/gpio/gpio-pch.c diff --git a/drivers/gpio/pl061.c b/drivers/gpio/gpio-pl061.c index 6fcb28cdd86..2c5a18f32bf 100644 --- a/drivers/gpio/pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -1,7 +1,5 @@ /* - * linux/drivers/gpio/pl061.c - * - * Copyright (C) 2008, 2009 Provigent Ltd. + * Copyright (C) 2008, 2009 Provigent Ltd. * * 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 diff --git a/drivers/gpio/gpio-plat-samsung.c b/drivers/gpio/gpio-plat-samsung.c index ea37c046178..ef67f1952a7 100644 --- a/drivers/gpio/gpio-plat-samsung.c +++ b/drivers/gpio/gpio-plat-samsung.c @@ -1,5 +1,4 @@ -/* arch/arm/plat-samsung/gpiolib.c - * +/* * Copyright 2008 Openmoko, Inc. * Copyright 2008 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> diff --git a/drivers/gpio/rdc321x-gpio.c b/drivers/gpio/gpio-rdc321x.c index 2762698e020..2762698e020 100644 --- a/drivers/gpio/rdc321x-gpio.c +++ b/drivers/gpio/gpio-rdc321x.c diff --git a/drivers/gpio/gpio-s5pc100.c b/drivers/gpio/gpio-s5pc100.c index 2842394b28b..7f87b0c76e0 100644 --- a/drivers/gpio/gpio-s5pc100.c +++ b/drivers/gpio/gpio-s5pc100.c @@ -1,4 +1,5 @@ -/* linux/arch/arm/mach-s5pc100/gpiolib.c +/* + * S5PC100 - GPIOlib support * * Copyright (c) 2010 Samsung Electronics Co., Ltd. * http://www.samsung.com @@ -6,8 +7,6 @@ * Copyright 2009 Samsung Electronics Co * Kyungmin Park <kyungmin.park@samsung.com> * - * 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. diff --git a/drivers/gpio/gpio-s5pv210.c b/drivers/gpio/gpio-s5pv210.c index 1ba20a703e0..eb12f1602de 100644 --- a/drivers/gpio/gpio-s5pv210.c +++ b/drivers/gpio/gpio-s5pv210.c @@ -1,10 +1,9 @@ -/* linux/arch/arm/mach-s5pv210/gpiolib.c +/* + * S5PV210 - GPIOlib support * * 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. diff --git a/drivers/gpio/sch_gpio.c b/drivers/gpio/gpio-sch.c index 56060421cdf..16351584549 100644 --- a/drivers/gpio/sch_gpio.c +++ b/drivers/gpio/gpio-sch.c @@ -1,5 +1,5 @@ /* - * sch_gpio.c - GPIO interface for Intel Poulsbo SCH + * GPIO interface for Intel Poulsbo SCH * * Copyright (c) 2010 CompuLab Ltd * Author: Denis Turischev <denis@compulab.co.il> diff --git a/drivers/gpio/stmpe-gpio.c b/drivers/gpio/gpio-stmpe.c index 4c980b57332..4c980b57332 100644 --- a/drivers/gpio/stmpe-gpio.c +++ b/drivers/gpio/gpio-stmpe.c diff --git a/drivers/gpio/sx150x.c b/drivers/gpio/gpio-sx150x.c index a4f73534394..a4f73534394 100644 --- a/drivers/gpio/sx150x.c +++ b/drivers/gpio/gpio-sx150x.c diff --git a/drivers/gpio/tc3589x-gpio.c b/drivers/gpio/gpio-tc3589x.c index 2a82e8999a4..2a82e8999a4 100644 --- a/drivers/gpio/tc3589x-gpio.c +++ b/drivers/gpio/gpio-tc3589x.c diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c new file mode 100644 index 00000000000..13afb881ffc --- /dev/null +++ b/drivers/gpio/gpio-tegra.c @@ -0,0 +1,441 @@ +/* + * arch/arm/mach-tegra/gpio.c + * + * Copyright (c) 2010 Google, Inc + * + * Author: + * Erik Gilling <konkers@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/interrupt.h> + +#include <linux/io.h> +#include <linux/gpio.h> +#include <linux/of.h> + +#include <asm/mach/irq.h> + +#include <mach/iomap.h> +#include <mach/suspend.h> + +#define GPIO_BANK(x) ((x) >> 5) +#define GPIO_PORT(x) (((x) >> 3) & 0x3) +#define GPIO_BIT(x) ((x) & 0x7) + +#define GPIO_REG(x) (IO_TO_VIRT(TEGRA_GPIO_BASE) + \ + GPIO_BANK(x) * 0x80 + \ + GPIO_PORT(x) * 4) + +#define GPIO_CNF(x) (GPIO_REG(x) + 0x00) +#define GPIO_OE(x) (GPIO_REG(x) + 0x10) +#define GPIO_OUT(x) (GPIO_REG(x) + 0X20) +#define GPIO_IN(x) (GPIO_REG(x) + 0x30) +#define GPIO_INT_STA(x) (GPIO_REG(x) + 0x40) +#define GPIO_INT_ENB(x) (GPIO_REG(x) + 0x50) +#define GPIO_INT_LVL(x) (GPIO_REG(x) + 0x60) +#define GPIO_INT_CLR(x) (GPIO_REG(x) + 0x70) + +#define GPIO_MSK_CNF(x) (GPIO_REG(x) + 0x800) +#define GPIO_MSK_OE(x) (GPIO_REG(x) + 0x810) +#define GPIO_MSK_OUT(x) (GPIO_REG(x) + 0X820) +#define GPIO_MSK_INT_STA(x) (GPIO_REG(x) + 0x840) +#define GPIO_MSK_INT_ENB(x) (GPIO_REG(x) + 0x850) +#define GPIO_MSK_INT_LVL(x) (GPIO_REG(x) + 0x860) + +#define GPIO_INT_LVL_MASK 0x010101 +#define GPIO_INT_LVL_EDGE_RISING 0x000101 +#define GPIO_INT_LVL_EDGE_FALLING 0x000100 +#define GPIO_INT_LVL_EDGE_BOTH 0x010100 +#define GPIO_INT_LVL_LEVEL_HIGH 0x000001 +#define GPIO_INT_LVL_LEVEL_LOW 0x000000 + +struct tegra_gpio_bank { + int bank; + int irq; + spinlock_t lvl_lock[4]; +#ifdef CONFIG_PM + u32 cnf[4]; + u32 out[4]; + u32 oe[4]; + u32 int_enb[4]; + u32 int_lvl[4]; +#endif +}; + + +static struct tegra_gpio_bank tegra_gpio_banks[] = { + {.bank = 0, .irq = INT_GPIO1}, + {.bank = 1, .irq = INT_GPIO2}, + {.bank = 2, .irq = INT_GPIO3}, + {.bank = 3, .irq = INT_GPIO4}, + {.bank = 4, .irq = INT_GPIO5}, + {.bank = 5, .irq = INT_GPIO6}, + {.bank = 6, .irq = INT_GPIO7}, +}; + +static int tegra_gpio_compose(int bank, int port, int bit) +{ + return (bank << 5) | ((port & 0x3) << 3) | (bit & 0x7); +} + +static void tegra_gpio_mask_write(u32 reg, int gpio, int value) +{ + u32 val; + + val = 0x100 << GPIO_BIT(gpio); + if (value) + val |= 1 << GPIO_BIT(gpio); + __raw_writel(val, reg); +} + +void tegra_gpio_enable(int gpio) +{ + tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 1); +} + +void tegra_gpio_disable(int gpio) +{ + tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 0); +} + +static void tegra_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + tegra_gpio_mask_write(GPIO_MSK_OUT(offset), offset, value); +} + +static int tegra_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + return (__raw_readl(GPIO_IN(offset)) >> GPIO_BIT(offset)) & 0x1; +} + +static int tegra_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + tegra_gpio_mask_write(GPIO_MSK_OE(offset), offset, 0); + return 0; +} + +static int tegra_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + tegra_gpio_set(chip, offset, value); + tegra_gpio_mask_write(GPIO_MSK_OE(offset), offset, 1); + return 0; +} + + + +static struct gpio_chip tegra_gpio_chip = { + .label = "tegra-gpio", + .direction_input = tegra_gpio_direction_input, + .get = tegra_gpio_get, + .direction_output = tegra_gpio_direction_output, + .set = tegra_gpio_set, + .base = 0, + .ngpio = TEGRA_NR_GPIOS, +}; + +static void tegra_gpio_irq_ack(struct irq_data *d) +{ + int gpio = d->irq - INT_GPIO_BASE; + + __raw_writel(1 << GPIO_BIT(gpio), GPIO_INT_CLR(gpio)); +} + +static void tegra_gpio_irq_mask(struct irq_data *d) +{ + int gpio = d->irq - INT_GPIO_BASE; + + tegra_gpio_mask_write(GPIO_MSK_INT_ENB(gpio), gpio, 0); +} + +static void tegra_gpio_irq_unmask(struct irq_data *d) +{ + int gpio = d->irq - INT_GPIO_BASE; + + tegra_gpio_mask_write(GPIO_MSK_INT_ENB(gpio), gpio, 1); +} + +static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + int gpio = d->irq - INT_GPIO_BASE; + struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); + int port = GPIO_PORT(gpio); + int lvl_type; + int val; + unsigned long flags; + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_RISING: + lvl_type = GPIO_INT_LVL_EDGE_RISING; + break; + + case IRQ_TYPE_EDGE_FALLING: + lvl_type = GPIO_INT_LVL_EDGE_FALLING; + break; + + case IRQ_TYPE_EDGE_BOTH: + lvl_type = GPIO_INT_LVL_EDGE_BOTH; + break; + + case IRQ_TYPE_LEVEL_HIGH: + lvl_type = GPIO_INT_LVL_LEVEL_HIGH; + break; + + case IRQ_TYPE_LEVEL_LOW: + lvl_type = GPIO_INT_LVL_LEVEL_LOW; + break; + + default: + return -EINVAL; + } + + spin_lock_irqsave(&bank->lvl_lock[port], flags); + + val = __raw_readl(GPIO_INT_LVL(gpio)); + val &= ~(GPIO_INT_LVL_MASK << GPIO_BIT(gpio)); + val |= lvl_type << GPIO_BIT(gpio); + __raw_writel(val, GPIO_INT_LVL(gpio)); + + spin_unlock_irqrestore(&bank->lvl_lock[port], 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 0; +} + +static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + struct tegra_gpio_bank *bank; + int port; + int pin; + int unmasked = 0; + struct irq_chip *chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); + + bank = irq_get_handler_data(irq); + + for (port = 0; port < 4; port++) { + int gpio = tegra_gpio_compose(bank->bank, port, 0); + unsigned long sta = __raw_readl(GPIO_INT_STA(gpio)) & + __raw_readl(GPIO_INT_ENB(gpio)); + u32 lvl = __raw_readl(GPIO_INT_LVL(gpio)); + + for_each_set_bit(pin, &sta, 8) { + __raw_writel(1 << pin, GPIO_INT_CLR(gpio)); + + /* if gpio is edge triggered, clear condition + * before executing the hander so that we don't + * miss edges + */ + if (lvl & (0x100 << pin)) { + unmasked = 1; + chained_irq_exit(chip, desc); + } + + generic_handle_irq(gpio_to_irq(gpio + pin)); + } + } + + if (!unmasked) + chained_irq_exit(chip, desc); + +} + +#ifdef CONFIG_PM +void tegra_gpio_resume(void) +{ + unsigned long flags; + int b; + int p; + + local_irq_save(flags); + + for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) { + struct tegra_gpio_bank *bank = &tegra_gpio_banks[b]; + + for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { + unsigned int gpio = (b<<5) | (p<<3); + __raw_writel(bank->cnf[p], GPIO_CNF(gpio)); + __raw_writel(bank->out[p], GPIO_OUT(gpio)); + __raw_writel(bank->oe[p], GPIO_OE(gpio)); + __raw_writel(bank->int_lvl[p], GPIO_INT_LVL(gpio)); + __raw_writel(bank->int_enb[p], GPIO_INT_ENB(gpio)); + } + } + + local_irq_restore(flags); +} + +void tegra_gpio_suspend(void) +{ + unsigned long flags; + int b; + int p; + + local_irq_save(flags); + for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) { + struct tegra_gpio_bank *bank = &tegra_gpio_banks[b]; + + for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { + unsigned int gpio = (b<<5) | (p<<3); + bank->cnf[p] = __raw_readl(GPIO_CNF(gpio)); + bank->out[p] = __raw_readl(GPIO_OUT(gpio)); + bank->oe[p] = __raw_readl(GPIO_OE(gpio)); + bank->int_enb[p] = __raw_readl(GPIO_INT_ENB(gpio)); + bank->int_lvl[p] = __raw_readl(GPIO_INT_LVL(gpio)); + } + } + local_irq_restore(flags); +} + +static int tegra_gpio_wake_enable(struct irq_data *d, unsigned int enable) +{ + struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); + return irq_set_irq_wake(bank->irq, enable); +} +#endif + +static struct irq_chip tegra_gpio_irq_chip = { + .name = "GPIO", + .irq_ack = tegra_gpio_irq_ack, + .irq_mask = tegra_gpio_irq_mask, + .irq_unmask = tegra_gpio_irq_unmask, + .irq_set_type = tegra_gpio_irq_set_type, +#ifdef CONFIG_PM + .irq_set_wake = tegra_gpio_wake_enable, +#endif +}; + + +/* 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 int __init tegra_gpio_init(void) +{ + struct tegra_gpio_bank *bank; + int i; + int j; + + for (i = 0; i < 7; i++) { + for (j = 0; j < 4; j++) { + int gpio = tegra_gpio_compose(i, j, 0); + __raw_writel(0x00, GPIO_INT_ENB(gpio)); + } + } + +#ifdef CONFIG_OF_GPIO + /* + * This isn't ideal, but it gets things hooked up until this + * driver is converted into a platform_device + */ + tegra_gpio_chip.of_node = of_find_compatible_node(NULL, NULL, + "nvidia,tegra250-gpio"); +#endif /* CONFIG_OF_GPIO */ + + gpiochip_add(&tegra_gpio_chip); + + for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + TEGRA_NR_GPIOS); i++) { + bank = &tegra_gpio_banks[GPIO_BANK(irq_to_gpio(i))]; + + irq_set_lockdep_class(i, &gpio_lock_class); + irq_set_chip_data(i, bank); + irq_set_chip_and_handler(i, &tegra_gpio_irq_chip, + handle_simple_irq); + set_irq_flags(i, IRQF_VALID); + } + + for (i = 0; i < ARRAY_SIZE(tegra_gpio_banks); i++) { + bank = &tegra_gpio_banks[i]; + + irq_set_chained_handler(bank->irq, tegra_gpio_irq_handler); + irq_set_handler_data(bank->irq, bank); + + for (j = 0; j < 4; j++) + spin_lock_init(&bank->lvl_lock[j]); + } + + return 0; +} + +postcore_initcall(tegra_gpio_init); + +void __init tegra_gpio_config(struct tegra_gpio_table *table, int num) +{ + int i; + + for (i = 0; i < num; i++) { + int gpio = table[i].gpio; + + if (table[i].enable) + tegra_gpio_enable(gpio); + else + tegra_gpio_disable(gpio); + } +} + +#ifdef CONFIG_DEBUG_FS + +#include <linux/debugfs.h> +#include <linux/seq_file.h> + +static int dbg_gpio_show(struct seq_file *s, void *unused) +{ + int i; + int j; + + for (i = 0; i < 7; i++) { + for (j = 0; j < 4; j++) { + int gpio = tegra_gpio_compose(i, j, 0); + seq_printf(s, + "%d:%d %02x %02x %02x %02x %02x %02x %06x\n", + i, j, + __raw_readl(GPIO_CNF(gpio)), + __raw_readl(GPIO_OE(gpio)), + __raw_readl(GPIO_OUT(gpio)), + __raw_readl(GPIO_IN(gpio)), + __raw_readl(GPIO_INT_STA(gpio)), + __raw_readl(GPIO_INT_ENB(gpio)), + __raw_readl(GPIO_INT_LVL(gpio))); + } + } + return 0; +} + +static int dbg_gpio_open(struct inode *inode, struct file *file) +{ + return single_open(file, dbg_gpio_show, &inode->i_private); +} + +static const struct file_operations debug_fops = { + .open = dbg_gpio_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init tegra_gpio_debuginit(void) +{ + (void) debugfs_create_file("tegra_gpio", S_IRUGO, + NULL, NULL, &debug_fops); + return 0; +} +late_initcall(tegra_gpio_debuginit); +#endif diff --git a/drivers/gpio/timbgpio.c b/drivers/gpio/gpio-timberdale.c index 0265872e57d..c593bd46bfb 100644 --- a/drivers/gpio/timbgpio.c +++ b/drivers/gpio/gpio-timberdale.c @@ -1,5 +1,5 @@ /* - * timbgpio.c timberdale FPGA GPIO driver + * Timberdale FPGA GPIO driver * Copyright (c) 2009 Intel Corporation * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/gpio/tps65910-gpio.c b/drivers/gpio/gpio-tps65910.c index 8d1ddfdd63e..41710332cb0 100644 --- a/drivers/gpio/tps65910-gpio.c +++ b/drivers/gpio/gpio-tps65910.c @@ -1,5 +1,5 @@ /* - * tps65910-gpio.c -- TI TPS6591x + * TI TPS6591x GPIO driver * * Copyright 2010 Texas Instruments Inc. * diff --git a/drivers/gpio/twl4030-gpio.c b/drivers/gpio/gpio-twl4030.c index 57635ac35a7..b8b4f228757 100644 --- a/drivers/gpio/twl4030-gpio.c +++ b/drivers/gpio/gpio-twl4030.c @@ -1,5 +1,5 @@ /* - * twl4030_gpio.c -- access to GPIOs on TWL4030/TPS659x0 chips + * Access to GPIOs on TWL4030/TPS659x0 chips * * Copyright (C) 2006-2007 Texas Instruments, Inc. * Copyright (C) 2006 MontaVista Software, Inc. diff --git a/drivers/gpio/gpio-u300.c b/drivers/gpio/gpio-u300.c index d92790140fe..fd2dfeeefdf 100644 --- a/drivers/gpio/gpio-u300.c +++ b/drivers/gpio/gpio-u300.c @@ -1,11 +1,8 @@ /* - * - * arch/arm/mach-u300/gpio.c - * + * U300 GPIO module. * * 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) diff --git a/drivers/gpio/ucb1400_gpio.c b/drivers/gpio/gpio-ucb1400.c index 50e6bd1392c..50e6bd1392c 100644 --- a/drivers/gpio/ucb1400_gpio.c +++ b/drivers/gpio/gpio-ucb1400.c diff --git a/drivers/gpio/vr41xx_giu.c b/drivers/gpio/gpio-vr41xx.c index a365be040b3..98723cb9ac6 100644 --- a/drivers/gpio/vr41xx_giu.c +++ b/drivers/gpio/gpio-vr41xx.c @@ -518,7 +518,7 @@ static int __devinit giu_probe(struct platform_device *pdev) if (!res) return -EBUSY; - giu_base = ioremap(res->start, res->end - res->start + 1); + giu_base = ioremap(res->start, resource_size(res)); if (!giu_base) return -ENOMEM; diff --git a/drivers/gpio/vx855_gpio.c b/drivers/gpio/gpio-vx855.c index ef5aabd8b8b..ef5aabd8b8b 100644 --- a/drivers/gpio/vx855_gpio.c +++ b/drivers/gpio/gpio-vx855.c diff --git a/drivers/gpio/wm831x-gpio.c b/drivers/gpio/gpio-wm831x.c index 309644cf4d9..31a9ed7bba8 100644 --- a/drivers/gpio/wm831x-gpio.c +++ b/drivers/gpio/gpio-wm831x.c @@ -1,5 +1,5 @@ /* - * wm831x-gpio.c -- gpiolib support for Wolfson WM831x PMICs + * gpiolib support for Wolfson WM831x PMICs * * Copyright 2009 Wolfson Microelectronics PLC. * diff --git a/drivers/gpio/wm8350-gpiolib.c b/drivers/gpio/gpio-wm8350.c index 359999290f5..a06af515483 100644 --- a/drivers/gpio/wm8350-gpiolib.c +++ b/drivers/gpio/gpio-wm8350.c @@ -1,5 +1,5 @@ /* - * wm835x-gpiolib.c -- gpiolib support for Wolfson WM835x PMICs + * gpiolib support for Wolfson WM835x PMICs * * Copyright 2009 Wolfson Microelectronics PLC. * diff --git a/drivers/gpio/wm8994-gpio.c b/drivers/gpio/gpio-wm8994.c index c822baacd8f..96198f3fab7 100644 --- a/drivers/gpio/wm8994-gpio.c +++ b/drivers/gpio/gpio-wm8994.c @@ -1,5 +1,5 @@ /* - * wm8994-gpio.c -- gpiolib support for Wolfson WM8994 + * gpiolib support for Wolfson WM8994 * * Copyright 2009 Wolfson Microelectronics PLC. * diff --git a/drivers/gpio/xilinx_gpio.c b/drivers/gpio/gpio-xilinx.c index 846fbd5e31b..846fbd5e31b 100644 --- a/drivers/gpio/xilinx_gpio.c +++ b/drivers/gpio/gpio-xilinx.c |