From a3cc68c37897f0656489a0d853d6e342fc6f076b Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 27 May 2011 16:35:59 -0700 Subject: gpio/74x164: remove unnecessary defines and prototype Remove the #define GEN_74X164_GPIO_COUNT since it's only used in one place and it's meaning is obvious. Also remove the #define GEN_74X164_DRIVER_NAME and use spi->modalias to set the gpio chip's label and the string "74x164" for the driver name. Reorder the code slightly to remove the need to prototype gen_74x164_set_value. Signed-off-by: H Hartley Sweeten Signed-off-by: Grant Likely --- drivers/gpio/74x164.c | 27 +++++++++++---------------- include/linux/spi/74x164.h | 2 -- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/drivers/gpio/74x164.c b/drivers/gpio/74x164.c index 84e07021983..7fb60b6bf52 100644 --- a/drivers/gpio/74x164.c +++ b/drivers/gpio/74x164.c @@ -16,9 +16,6 @@ #include #include -#define GEN_74X164_GPIO_COUNT 8 - - struct gen_74x164_chip { struct spi_device *spi; struct gpio_chip gpio_chip; @@ -26,8 +23,6 @@ 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) { return container_of(gc, struct gen_74x164_chip, gpio_chip); @@ -39,13 +34,6 @@ 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); @@ -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/include/linux/spi/74x164.h b/include/linux/spi/74x164.h index d85c52f294a..0aa6acc7331 100644 --- a/include/linux/spi/74x164.h +++ b/include/linux/spi/74x164.h @@ -1,8 +1,6 @@ #ifndef LINUX_SPI_74X164_H #define LINUX_SPI_74X164_H -#define GEN_74X164_DRIVER_NAME "74x164" - struct gen_74x164_chip_platform_data { /* number assigned to the first GPIO */ unsigned base; -- cgit v1.2.3 From 2bcc7ed5b83b0a59c6976476e8788675038fb11b Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 31 May 2011 16:23:43 +0800 Subject: leds: remove config option LEDS_GPIO_PLATFORM from Kconfig Since the commit a314c5c0040aab51ebb1ecfd37a9198a91962243 (leds/leds-gpio: merge platform_driver with of_platform_driver), the config option LEDS_GPIO_PLATFORM becomes useless, so remove it. Signed-off-by: Shawn Guo [grant.likely: also remove LEDS_GPIO_OF for same reason] Signed-off-by: Grant Likely --- drivers/leds/Kconfig | 19 +------------------ drivers/leds/leds-gpio.c | 6 +++--- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 23f0d5e99f3..9d7d9400bbf 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -93,7 +93,7 @@ config LEDS_NET48XX config LEDS_NET5501 tristate "LED Support for Soekris net5501 series Error LED" depends on LEDS_TRIGGERS - depends on X86 && LEDS_GPIO_PLATFORM && GPIO_CS5535 + depends on X86 && GPIO_CS5535 select LEDS_TRIGGER_DEFAULT_ON default n help @@ -183,23 +183,6 @@ config LEDS_GPIO defined as platform devices and/or OpenFirmware platform devices. The code to use these bindings can be selected below. -config LEDS_GPIO_PLATFORM - bool "Platform device bindings for GPIO LEDs" - depends on LEDS_GPIO - default y - help - Let the leds-gpio driver drive LEDs which have been defined as - platform devices. If you don't know what this means, say yes. - -config LEDS_GPIO_OF - bool "OpenFirmware platform device bindings for GPIO LEDs" - depends on LEDS_GPIO && OF_DEVICE - default y - help - Let the leds-gpio driver drive LEDs which have been defined as - of_platform devices. For instance, LEDs which are listed in a "dts" - file. - config LEDS_LP3944 tristate "LED Support for N.S. LP3944 (Fun Light) I2C chip" depends on LEDS_CLASS diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index b0480c8fbcb..3d8bc327a68 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -165,7 +165,7 @@ static inline int sizeof_gpio_leds_priv(int num_leds) } /* Code to create from OpenFirmware platform devices */ -#ifdef CONFIG_LEDS_GPIO_OF +#ifdef CONFIG_OF_GPIO static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node, *child; @@ -223,13 +223,13 @@ static const struct of_device_id of_gpio_leds_match[] = { { .compatible = "gpio-leds", }, {}, }; -#else +#else /* CONFIG_OF_GPIO */ static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_device *pdev) { return NULL; } #define of_gpio_leds_match NULL -#endif +#endif /* CONFIG_OF_GPIO */ static int __devinit gpio_led_probe(struct platform_device *pdev) -- cgit v1.2.3 From a26221a834fe30c22d440b954b2e11e607fa57f5 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 3 Jun 2011 13:32:41 -0500 Subject: gpio/samsung: make Kconfig options def_bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Samsung GPIO drivers are always built-in when the relevant platform is selected.  Change the Kconfig symbol to def_bool y dependant on the platform. Signed-off-by: H Hartley Sweeten Acked-by: Kyungmin Park Signed-off-by: Grant Likely --- drivers/gpio/Kconfig | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4a7f6314345..2967002a9f8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -87,32 +87,20 @@ config GPIO_IT8761E 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 - depends on ARM - help - Say yes here to support Samsung Exynos4 series SoCs GPIO library + def_bool y + depends on CPU_EXYNOS4210 config GPIO_PLAT_SAMSUNG - bool "Samsung SoCs GPIO library support" - default y if SAMSUNG_GPIOLIB_4BIT - depends on ARM - help - Say yes here to support Samsung SoCs GPIO library + def_bool y + depends on SAMSUNG_GPIOLIB_4BIT config GPIO_S5PC100 - bool "Samsung S5PC100 GPIO library support" - default y if CPU_S5PC100 - depends on ARM - help - Say yes here to support Samsung S5PC100 SoCs GPIO library + def_bool y + depends on CPU_S5PC100 config GPIO_S5PV210 - bool "Samsung S5PV210/S5PC110 GPIO library support" - default y if CPU_S5PV210 - depends on ARM - help - Say yes here to support Samsung S5PV210/S5PC110 SoCs GPIO library + def_bool y + depends on CPU_S5PV210 config GPIO_PL061 bool "PrimeCell PL061 GPIO support" -- cgit v1.2.3 From 7b2fa5702059a03c11f934224724b2f712a749ce Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Mon, 6 Jun 2011 09:37:58 -0600 Subject: gpio/mxs: Move Freescale mxs gpio driver to drivers/gpio GPIO drivers are getting moved to drivers/gpio for cleanup and consolidation. This patch moves the mxs driver. Follow up patches will clean it up and make it a fine upstanding example of a gpio driver. v2: Removed header file entirely and put struct definition directly into driver. The struct isn't used anywhere else in the kernel. Signed-off-by: Grant Likely Tested-by: Shawn Guo Acked-by: Sascha Hauer --- arch/arm/mach-mxs/Makefile | 2 +- arch/arm/mach-mxs/gpio.c | 331 -------------------------------------- arch/arm/mach-mxs/gpio.h | 34 ---- arch/arm/mach-mxs/mach-mx28evk.c | 1 - drivers/gpio/Kconfig | 4 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-mxs.c | 338 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 344 insertions(+), 367 deletions(-) delete mode 100644 arch/arm/mach-mxs/gpio.c delete mode 100644 arch/arm/mach-mxs/gpio.h create mode 100644 drivers/gpio/gpio-mxs.c diff --git a/arch/arm/mach-mxs/Makefile b/arch/arm/mach-mxs/Makefile index 58e892376bf..6c38262a3aa 100644 --- a/arch/arm/mach-mxs/Makefile +++ b/arch/arm/mach-mxs/Makefile @@ -1,5 +1,5 @@ # Common support -obj-y := clock.o devices.o gpio.o icoll.o iomux.o system.o timer.o +obj-y := clock.o devices.o icoll.o iomux.o system.o timer.o obj-$(CONFIG_MXS_OCOTP) += ocotp.o obj-$(CONFIG_PM) += pm.o diff --git a/arch/arm/mach-mxs/gpio.c b/arch/arm/mach-mxs/gpio.c deleted file mode 100644 index 2c950fef71a..00000000000 --- a/arch/arm/mach-mxs/gpio.c +++ /dev/null @@ -1,331 +0,0 @@ -/* - * MXC GPIO support. (c) 2008 Daniel Mack - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include "gpio.h" - -static struct mxs_gpio_port *mxs_gpio_ports; -static int gpio_table_size; - -#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) - -/* Note: This driver assumes 32 GPIOs are handled in one register */ - -static void clear_gpio_irqstatus(struct mxs_gpio_port *port, u32 index) -{ - __mxs_clrl(1 << index, port->base + PINCTRL_IRQSTAT(port->id)); -} - -static void set_gpio_irqenable(struct mxs_gpio_port *port, u32 index, - int enable) -{ - if (enable) { - __mxs_setl(1 << index, port->base + PINCTRL_IRQEN(port->id)); - __mxs_setl(1 << index, port->base + PINCTRL_PIN2IRQ(port->id)); - } else { - __mxs_clrl(1 << index, port->base + PINCTRL_IRQEN(port->id)); - } -} - -static void mxs_gpio_ack_irq(struct irq_data *d) -{ - u32 gpio = irq_to_gpio(d->irq); - clear_gpio_irqstatus(&mxs_gpio_ports[gpio / 32], gpio & 0x1f); -} - -static void mxs_gpio_mask_irq(struct irq_data *d) -{ - u32 gpio = irq_to_gpio(d->irq); - set_gpio_irqenable(&mxs_gpio_ports[gpio / 32], gpio & 0x1f, 0); -} - -static void mxs_gpio_unmask_irq(struct irq_data *d) -{ - u32 gpio = irq_to_gpio(d->irq); - set_gpio_irqenable(&mxs_gpio_ports[gpio / 32], gpio & 0x1f, 1); -} - -static int mxs_gpio_get(struct gpio_chip *chip, unsigned offset); - -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 mxs_gpio_port *port = &mxs_gpio_ports[gpio / 32]; - 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) - __mxs_setl(pin_mask, pin_addr); - else - __mxs_clrl(pin_mask, pin_addr); - - /* set polarity */ - pin_addr = port->base + PINCTRL_IRQPOL(port->id); - if (edge & GPIO_INT_POL_MASK) - __mxs_setl(pin_mask, pin_addr); - else - __mxs_clrl(pin_mask, pin_addr); - - clear_gpio_irqstatus(port, gpio & 0x1f); - - 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 = (struct mxs_gpio_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 = __raw_readl(port->base + PINCTRL_IRQSTAT(port->id)) & - __raw_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) -{ - u32 gpio = irq_to_gpio(d->irq); - u32 gpio_idx = gpio & 0x1f; - struct mxs_gpio_port *port = &mxs_gpio_ports[gpio / 32]; - - 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 struct irq_chip gpio_irq_chip = { - .name = "mxs gpio", - .irq_ack = mxs_gpio_ack_irq, - .irq_mask = mxs_gpio_mask_irq, - .irq_unmask = mxs_gpio_unmask_irq, - .irq_set_type = mxs_gpio_set_irq_type, - .irq_set_wake = mxs_gpio_set_wake_irq, -}; - -static void mxs_set_gpio_direction(struct gpio_chip *chip, unsigned offset, - int dir) -{ - struct mxs_gpio_port *port = - container_of(chip, struct mxs_gpio_port, chip); - void __iomem *pin_addr = port->base + PINCTRL_DOE(port->id); - - if (dir) - __mxs_setl(1 << offset, pin_addr); - else - __mxs_clrl(1 << offset, pin_addr); -} - -static int mxs_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct mxs_gpio_port *port = - container_of(chip, struct mxs_gpio_port, chip); - - return (__raw_readl(port->base + PINCTRL_DIN(port->id)) >> offset) & 1; -} - -static void mxs_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct mxs_gpio_port *port = - container_of(chip, struct mxs_gpio_port, chip); - void __iomem *pin_addr = port->base + PINCTRL_DOUT(port->id); - - if (value) - __mxs_setl(1 << offset, pin_addr); - else - __mxs_clrl(1 << offset, pin_addr); -} - -static int mxs_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct mxs_gpio_port *port = - container_of(chip, struct mxs_gpio_port, chip); - - return port->virtual_irq_start + offset; -} - -static int mxs_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - mxs_set_gpio_direction(chip, offset, 0); - return 0; -} - -static int mxs_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - mxs_gpio_set(chip, offset, value); - mxs_set_gpio_direction(chip, offset, 1); - return 0; -} - -int __init mxs_gpio_init(struct mxs_gpio_port *port, int cnt) -{ - int i, j; - - /* save for local usage */ - mxs_gpio_ports = port; - gpio_table_size = cnt; - - pr_info("MXS GPIO hardware\n"); - - for (i = 0; i < cnt; i++) { - /* disable the interrupt and clear the status */ - __raw_writel(0, port[i].base + PINCTRL_PIN2IRQ(i)); - __raw_writel(0, port[i].base + PINCTRL_IRQEN(i)); - - /* clear address has to be used to clear IRQSTAT bits */ - __mxs_clrl(~0U, port[i].base + PINCTRL_IRQSTAT(i)); - - for (j = port[i].virtual_irq_start; - j < port[i].virtual_irq_start + 32; j++) { - irq_set_chip_and_handler(j, &gpio_irq_chip, - handle_level_irq); - set_irq_flags(j, IRQF_VALID); - } - - /* setup one handler for each entry */ - irq_set_chained_handler(port[i].irq, mxs_gpio_irq_handler); - irq_set_handler_data(port[i].irq, &port[i]); - - /* register gpio chip */ - port[i].chip.direction_input = mxs_gpio_direction_input; - port[i].chip.direction_output = mxs_gpio_direction_output; - port[i].chip.get = mxs_gpio_get; - port[i].chip.set = mxs_gpio_set; - port[i].chip.to_irq = mxs_gpio_to_irq; - port[i].chip.base = i * 32; - port[i].chip.ngpio = 32; - - /* its a serious configuration bug when it fails */ - BUG_ON(gpiochip_add(&port[i].chip) < 0); - } - - return 0; -} - -#define MX23_GPIO_BASE MX23_IO_ADDRESS(MX23_PINCTRL_BASE_ADDR) -#define MX28_GPIO_BASE MX28_IO_ADDRESS(MX28_PINCTRL_BASE_ADDR) - -#define DEFINE_MXS_GPIO_PORT(_base, _irq, _id) \ - { \ - .chip.label = "gpio-" #_id, \ - .id = _id, \ - .irq = _irq, \ - .base = _base, \ - .virtual_irq_start = MXS_GPIO_IRQ_START + (_id) * 32, \ - } - -#ifdef CONFIG_SOC_IMX23 -static struct mxs_gpio_port mx23_gpio_ports[] = { - DEFINE_MXS_GPIO_PORT(MX23_GPIO_BASE, MX23_INT_GPIO0, 0), - DEFINE_MXS_GPIO_PORT(MX23_GPIO_BASE, MX23_INT_GPIO1, 1), - DEFINE_MXS_GPIO_PORT(MX23_GPIO_BASE, MX23_INT_GPIO2, 2), -}; - -int __init mx23_register_gpios(void) -{ - return mxs_gpio_init(mx23_gpio_ports, ARRAY_SIZE(mx23_gpio_ports)); -} -#endif - -#ifdef CONFIG_SOC_IMX28 -static struct mxs_gpio_port mx28_gpio_ports[] = { - DEFINE_MXS_GPIO_PORT(MX28_GPIO_BASE, MX28_INT_GPIO0, 0), - DEFINE_MXS_GPIO_PORT(MX28_GPIO_BASE, MX28_INT_GPIO1, 1), - DEFINE_MXS_GPIO_PORT(MX28_GPIO_BASE, MX28_INT_GPIO2, 2), - DEFINE_MXS_GPIO_PORT(MX28_GPIO_BASE, MX28_INT_GPIO3, 3), - DEFINE_MXS_GPIO_PORT(MX28_GPIO_BASE, MX28_INT_GPIO4, 4), -}; - -int __init mx28_register_gpios(void) -{ - return mxs_gpio_init(mx28_gpio_ports, ARRAY_SIZE(mx28_gpio_ports)); -} -#endif diff --git a/arch/arm/mach-mxs/gpio.h b/arch/arm/mach-mxs/gpio.h deleted file mode 100644 index 005bb06630b..00000000000 --- a/arch/arm/mach-mxs/gpio.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright 2008 Juergen Beisert, kernel@pengutronix.de - * - * 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. - */ - -#ifndef __MXS_GPIO_H__ -#define __MXS_GPIO_H__ - -struct mxs_gpio_port { - void __iomem *base; - int id; - int irq; - int irq_high; - int virtual_irq_start; - struct gpio_chip chip; -}; - -int mxs_gpio_init(struct mxs_gpio_port*, int); - -#endif /* __MXS_GPIO_H__ */ diff --git a/arch/arm/mach-mxs/mach-mx28evk.c b/arch/arm/mach-mxs/mach-mx28evk.c index eacdc6b0e70..56767a5cce0 100644 --- a/arch/arm/mach-mxs/mach-mx28evk.c +++ b/arch/arm/mach-mxs/mach-mx28evk.c @@ -26,7 +26,6 @@ #include #include "devices-mx28.h" -#include "gpio.h" #define MX28EVK_FLEXCAN_SWITCH MXS_GPIO_NR(2, 13) #define MX28EVK_FEC_PHY_POWER MXS_GPIO_NR(2, 15) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4a7f6314345..48711749ffc 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -93,6 +93,10 @@ config GPIO_EXYNOS4 help Say yes here to support Samsung Exynos4 series SoCs GPIO library +config GPIO_MXS + def_bool y + depends on ARCH_MXS + 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 b605f8ec6fb..f879cbe493d 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -9,6 +9,7 @@ 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_MXS) += gpio-mxs.o obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o obj-$(CONFIG_GPIO_S5PC100) += gpio-s5pc100.o obj-$(CONFIG_GPIO_S5PV210) += gpio-s5pv210.o diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c new file mode 100644 index 00000000000..e26bf309b21 --- /dev/null +++ b/drivers/gpio/gpio-mxs.c @@ -0,0 +1,338 @@ +/* + * MXC GPIO support. (c) 2008 Daniel Mack + * 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 +#include +#include +#include +#include +#include +#include +#include + +static struct mxs_gpio_port *mxs_gpio_ports; +static int gpio_table_size; + +#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 irq_high; + int virtual_irq_start; + struct gpio_chip chip; +}; + +/* Note: This driver assumes 32 GPIOs are handled in one register */ + +static void clear_gpio_irqstatus(struct mxs_gpio_port *port, u32 index) +{ + __mxs_clrl(1 << index, port->base + PINCTRL_IRQSTAT(port->id)); +} + +static void set_gpio_irqenable(struct mxs_gpio_port *port, u32 index, + int enable) +{ + if (enable) { + __mxs_setl(1 << index, port->base + PINCTRL_IRQEN(port->id)); + __mxs_setl(1 << index, port->base + PINCTRL_PIN2IRQ(port->id)); + } else { + __mxs_clrl(1 << index, port->base + PINCTRL_IRQEN(port->id)); + } +} + +static void mxs_gpio_ack_irq(struct irq_data *d) +{ + u32 gpio = irq_to_gpio(d->irq); + clear_gpio_irqstatus(&mxs_gpio_ports[gpio / 32], gpio & 0x1f); +} + +static void mxs_gpio_mask_irq(struct irq_data *d) +{ + u32 gpio = irq_to_gpio(d->irq); + set_gpio_irqenable(&mxs_gpio_ports[gpio / 32], gpio & 0x1f, 0); +} + +static void mxs_gpio_unmask_irq(struct irq_data *d) +{ + u32 gpio = irq_to_gpio(d->irq); + set_gpio_irqenable(&mxs_gpio_ports[gpio / 32], gpio & 0x1f, 1); +} + +static int mxs_gpio_get(struct gpio_chip *chip, unsigned offset); + +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 mxs_gpio_port *port = &mxs_gpio_ports[gpio / 32]; + 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) + __mxs_setl(pin_mask, pin_addr); + else + __mxs_clrl(pin_mask, pin_addr); + + /* set polarity */ + pin_addr = port->base + PINCTRL_IRQPOL(port->id); + if (edge & GPIO_INT_POL_MASK) + __mxs_setl(pin_mask, pin_addr); + else + __mxs_clrl(pin_mask, pin_addr); + + clear_gpio_irqstatus(port, gpio & 0x1f); + + 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 = (struct mxs_gpio_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 = __raw_readl(port->base + PINCTRL_IRQSTAT(port->id)) & + __raw_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) +{ + u32 gpio = irq_to_gpio(d->irq); + u32 gpio_idx = gpio & 0x1f; + struct mxs_gpio_port *port = &mxs_gpio_ports[gpio / 32]; + + 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 struct irq_chip gpio_irq_chip = { + .name = "mxs gpio", + .irq_ack = mxs_gpio_ack_irq, + .irq_mask = mxs_gpio_mask_irq, + .irq_unmask = mxs_gpio_unmask_irq, + .irq_set_type = mxs_gpio_set_irq_type, + .irq_set_wake = mxs_gpio_set_wake_irq, +}; + +static void mxs_set_gpio_direction(struct gpio_chip *chip, unsigned offset, + int dir) +{ + struct mxs_gpio_port *port = + container_of(chip, struct mxs_gpio_port, chip); + void __iomem *pin_addr = port->base + PINCTRL_DOE(port->id); + + if (dir) + __mxs_setl(1 << offset, pin_addr); + else + __mxs_clrl(1 << offset, pin_addr); +} + +static int mxs_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct mxs_gpio_port *port = + container_of(chip, struct mxs_gpio_port, chip); + + return (__raw_readl(port->base + PINCTRL_DIN(port->id)) >> offset) & 1; +} + +static void mxs_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct mxs_gpio_port *port = + container_of(chip, struct mxs_gpio_port, chip); + void __iomem *pin_addr = port->base + PINCTRL_DOUT(port->id); + + if (value) + __mxs_setl(1 << offset, pin_addr); + else + __mxs_clrl(1 << offset, pin_addr); +} + +static int mxs_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct mxs_gpio_port *port = + container_of(chip, struct mxs_gpio_port, chip); + + return port->virtual_irq_start + offset; +} + +static int mxs_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + mxs_set_gpio_direction(chip, offset, 0); + return 0; +} + +static int mxs_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + mxs_gpio_set(chip, offset, value); + mxs_set_gpio_direction(chip, offset, 1); + return 0; +} + +int __init mxs_gpio_init(struct mxs_gpio_port *port, int cnt) +{ + int i, j; + + /* save for local usage */ + mxs_gpio_ports = port; + gpio_table_size = cnt; + + pr_info("MXS GPIO hardware\n"); + + for (i = 0; i < cnt; i++) { + /* disable the interrupt and clear the status */ + __raw_writel(0, port[i].base + PINCTRL_PIN2IRQ(i)); + __raw_writel(0, port[i].base + PINCTRL_IRQEN(i)); + + /* clear address has to be used to clear IRQSTAT bits */ + __mxs_clrl(~0U, port[i].base + PINCTRL_IRQSTAT(i)); + + for (j = port[i].virtual_irq_start; + j < port[i].virtual_irq_start + 32; j++) { + irq_set_chip_and_handler(j, &gpio_irq_chip, + handle_level_irq); + set_irq_flags(j, IRQF_VALID); + } + + /* setup one handler for each entry */ + irq_set_chained_handler(port[i].irq, mxs_gpio_irq_handler); + irq_set_handler_data(port[i].irq, &port[i]); + + /* register gpio chip */ + port[i].chip.direction_input = mxs_gpio_direction_input; + port[i].chip.direction_output = mxs_gpio_direction_output; + port[i].chip.get = mxs_gpio_get; + port[i].chip.set = mxs_gpio_set; + port[i].chip.to_irq = mxs_gpio_to_irq; + port[i].chip.base = i * 32; + port[i].chip.ngpio = 32; + + /* its a serious configuration bug when it fails */ + BUG_ON(gpiochip_add(&port[i].chip) < 0); + } + + return 0; +} + +#define MX23_GPIO_BASE MX23_IO_ADDRESS(MX23_PINCTRL_BASE_ADDR) +#define MX28_GPIO_BASE MX28_IO_ADDRESS(MX28_PINCTRL_BASE_ADDR) + +#define DEFINE_MXS_GPIO_PORT(_base, _irq, _id) \ + { \ + .chip.label = "gpio-" #_id, \ + .id = _id, \ + .irq = _irq, \ + .base = _base, \ + .virtual_irq_start = MXS_GPIO_IRQ_START + (_id) * 32, \ + } + +#ifdef CONFIG_SOC_IMX23 +static struct mxs_gpio_port mx23_gpio_ports[] = { + DEFINE_MXS_GPIO_PORT(MX23_GPIO_BASE, MX23_INT_GPIO0, 0), + DEFINE_MXS_GPIO_PORT(MX23_GPIO_BASE, MX23_INT_GPIO1, 1), + DEFINE_MXS_GPIO_PORT(MX23_GPIO_BASE, MX23_INT_GPIO2, 2), +}; + +int __init mx23_register_gpios(void) +{ + return mxs_gpio_init(mx23_gpio_ports, ARRAY_SIZE(mx23_gpio_ports)); +} +#endif + +#ifdef CONFIG_SOC_IMX28 +static struct mxs_gpio_port mx28_gpio_ports[] = { + DEFINE_MXS_GPIO_PORT(MX28_GPIO_BASE, MX28_INT_GPIO0, 0), + DEFINE_MXS_GPIO_PORT(MX28_GPIO_BASE, MX28_INT_GPIO1, 1), + DEFINE_MXS_GPIO_PORT(MX28_GPIO_BASE, MX28_INT_GPIO2, 2), + DEFINE_MXS_GPIO_PORT(MX28_GPIO_BASE, MX28_INT_GPIO3, 3), + DEFINE_MXS_GPIO_PORT(MX28_GPIO_BASE, MX28_INT_GPIO4, 4), +}; + +int __init mx28_register_gpios(void) +{ + return mxs_gpio_init(mx28_gpio_ports, ARRAY_SIZE(mx28_gpio_ports)); +} +#endif -- cgit v1.2.3 From 8d7cf8370d5fb75a3265408dceb1d6173eebfafd Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 6 Jun 2011 09:37:58 -0600 Subject: gpio/mxs: Change gpio-mxs into an upstanding gpio driver The patch makes necessary changes on gpio-mxs as below to turn it into an upstanding gpio driver. * Clean up the gpio port definition stuff * Use readl/writel to replace mach-specific accessors __raw_readl/__raw_writel * Change mxs_gpio_init into mxs_gpio_probe function And it then migrates mach-mxs to the updated driver by adding corresponding platform devices. Signed-off-by: Shawn Guo Acked-by: Arnd Bergmann Acked-by: Sascha Hauer Signed-off-by: Grant Likely --- arch/arm/mach-mxs/devices.c | 11 ++ arch/arm/mach-mxs/devices/Makefile | 1 + arch/arm/mach-mxs/devices/platform-gpio-mxs.c | 53 ++++++ arch/arm/mach-mxs/include/mach/devices-common.h | 2 + arch/arm/mach-mxs/mm-mx23.c | 1 - arch/arm/mach-mxs/mm-mx28.c | 1 - drivers/gpio/gpio-mxs.c | 216 ++++++++++++++---------- 7 files changed, 191 insertions(+), 94 deletions(-) create mode 100644 arch/arm/mach-mxs/devices/platform-gpio-mxs.c diff --git a/arch/arm/mach-mxs/devices.c b/arch/arm/mach-mxs/devices.c index cfdb6b28470..fe3e847930c 100644 --- a/arch/arm/mach-mxs/devices.c +++ b/arch/arm/mach-mxs/devices.c @@ -88,3 +88,14 @@ int __init mxs_add_amba_device(const struct amba_device *dev) return amba_device_register(adev, &iomem_resource); } + +struct device mxs_apbh_bus = { + .init_name = "mxs_apbh", + .parent = &platform_bus, +}; + +static int __init mxs_device_init(void) +{ + return device_register(&mxs_apbh_bus); +} +core_initcall(mxs_device_init); diff --git a/arch/arm/mach-mxs/devices/Makefile b/arch/arm/mach-mxs/devices/Makefile index 324f2824d38..351915c683f 100644 --- a/arch/arm/mach-mxs/devices/Makefile +++ b/arch/arm/mach-mxs/devices/Makefile @@ -6,4 +6,5 @@ obj-$(CONFIG_MXS_HAVE_PLATFORM_FLEXCAN) += platform-flexcan.o obj-$(CONFIG_MXS_HAVE_PLATFORM_MXS_I2C) += platform-mxs-i2c.o obj-$(CONFIG_MXS_HAVE_PLATFORM_MXS_MMC) += platform-mxs-mmc.o obj-$(CONFIG_MXS_HAVE_PLATFORM_MXS_PWM) += platform-mxs-pwm.o +obj-y += platform-gpio-mxs.o obj-$(CONFIG_MXS_HAVE_PLATFORM_MXSFB) += platform-mxsfb.o diff --git a/arch/arm/mach-mxs/devices/platform-gpio-mxs.c b/arch/arm/mach-mxs/devices/platform-gpio-mxs.c new file mode 100644 index 00000000000..ed0885e414e --- /dev/null +++ b/arch/arm/mach-mxs/devices/platform-gpio-mxs.c @@ -0,0 +1,53 @@ +/* + * Copyright 2011 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 version 2 as published by the + * Free Software Foundation. + */ +#include +#include +#include + +#include +#include +#include + +struct platform_device *__init mxs_add_gpio( + int id, resource_size_t iobase, int irq) +{ + struct resource res[] = { + { + .start = iobase, + .end = iobase + SZ_8K - 1, + .flags = IORESOURCE_MEM, + }, { + .start = irq, + .end = irq, + .flags = IORESOURCE_IRQ, + }, + }; + + return platform_device_register_resndata(&mxs_apbh_bus, + "gpio-mxs", id, res, ARRAY_SIZE(res), NULL, 0); +} + +static int __init mxs_add_mxs_gpio(void) +{ + if (cpu_is_mx23()) { + mxs_add_gpio(0, MX23_PINCTRL_BASE_ADDR, MX23_INT_GPIO0); + mxs_add_gpio(1, MX23_PINCTRL_BASE_ADDR, MX23_INT_GPIO1); + mxs_add_gpio(2, MX23_PINCTRL_BASE_ADDR, MX23_INT_GPIO2); + } + + if (cpu_is_mx28()) { + mxs_add_gpio(0, MX28_PINCTRL_BASE_ADDR, MX28_INT_GPIO0); + mxs_add_gpio(1, MX28_PINCTRL_BASE_ADDR, MX28_INT_GPIO1); + mxs_add_gpio(2, MX28_PINCTRL_BASE_ADDR, MX28_INT_GPIO2); + mxs_add_gpio(3, MX28_PINCTRL_BASE_ADDR, MX28_INT_GPIO3); + mxs_add_gpio(4, MX28_PINCTRL_BASE_ADDR, MX28_INT_GPIO4); + } + + return 0; +} +postcore_initcall(mxs_add_mxs_gpio); diff --git a/arch/arm/mach-mxs/include/mach/devices-common.h b/arch/arm/mach-mxs/include/mach/devices-common.h index 7a37469ed5b..812d7a813a7 100644 --- a/arch/arm/mach-mxs/include/mach/devices-common.h +++ b/arch/arm/mach-mxs/include/mach/devices-common.h @@ -11,6 +11,8 @@ #include #include +extern struct device mxs_apbh_bus; + struct platform_device *mxs_add_platform_device_dmamask( const char *name, int id, const struct resource *res, unsigned int num_resources, diff --git a/arch/arm/mach-mxs/mm-mx23.c b/arch/arm/mach-mxs/mm-mx23.c index 5148cd64a6b..1b2345ac1a8 100644 --- a/arch/arm/mach-mxs/mm-mx23.c +++ b/arch/arm/mach-mxs/mm-mx23.c @@ -41,5 +41,4 @@ void __init mx23_map_io(void) void __init mx23_init_irq(void) { icoll_init_irq(); - mx23_register_gpios(); } diff --git a/arch/arm/mach-mxs/mm-mx28.c b/arch/arm/mach-mxs/mm-mx28.c index 7e4cea32ebc..b6e18ddb92c 100644 --- a/arch/arm/mach-mxs/mm-mx28.c +++ b/arch/arm/mach-mxs/mm-mx28.c @@ -41,5 +41,4 @@ void __init mx28_map_io(void) void __init mx28_init_irq(void) { icoll_init_irq(); - mx28_register_gpios(); } diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index e26bf309b21..a28761428bb 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -25,12 +25,12 @@ #include #include #include -#include -#include -#include +#include +#include +#include -static struct mxs_gpio_port *mxs_gpio_ports; -static int gpio_table_size; +#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) @@ -61,36 +61,42 @@ struct mxs_gpio_port { static void clear_gpio_irqstatus(struct mxs_gpio_port *port, u32 index) { - __mxs_clrl(1 << index, port->base + PINCTRL_IRQSTAT(port->id)); + writel(1 << index, port->base + PINCTRL_IRQSTAT(port->id) + MXS_CLR); } static void set_gpio_irqenable(struct mxs_gpio_port *port, u32 index, int enable) { if (enable) { - __mxs_setl(1 << index, port->base + PINCTRL_IRQEN(port->id)); - __mxs_setl(1 << index, port->base + PINCTRL_PIN2IRQ(port->id)); + writel(1 << index, + port->base + PINCTRL_IRQEN(port->id) + MXS_SET); + writel(1 << index, + port->base + PINCTRL_PIN2IRQ(port->id) + MXS_SET); } else { - __mxs_clrl(1 << index, port->base + PINCTRL_IRQEN(port->id)); + writel(1 << index, + port->base + PINCTRL_IRQEN(port->id) + MXS_CLR); } } static void mxs_gpio_ack_irq(struct irq_data *d) { + struct mxs_gpio_port *port = irq_data_get_irq_chip_data(d); u32 gpio = irq_to_gpio(d->irq); - clear_gpio_irqstatus(&mxs_gpio_ports[gpio / 32], gpio & 0x1f); + clear_gpio_irqstatus(port, gpio & 0x1f); } static void mxs_gpio_mask_irq(struct irq_data *d) { + struct mxs_gpio_port *port = irq_data_get_irq_chip_data(d); u32 gpio = irq_to_gpio(d->irq); - set_gpio_irqenable(&mxs_gpio_ports[gpio / 32], gpio & 0x1f, 0); + set_gpio_irqenable(port, gpio & 0x1f, 0); } static void mxs_gpio_unmask_irq(struct irq_data *d) { + struct mxs_gpio_port *port = irq_data_get_irq_chip_data(d); u32 gpio = irq_to_gpio(d->irq); - set_gpio_irqenable(&mxs_gpio_ports[gpio / 32], gpio & 0x1f, 1); + set_gpio_irqenable(port, gpio & 0x1f, 1); } static int mxs_gpio_get(struct gpio_chip *chip, unsigned offset); @@ -99,7 +105,7 @@ 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 mxs_gpio_port *port = &mxs_gpio_ports[gpio / 32]; + struct mxs_gpio_port *port = irq_data_get_irq_chip_data(d); void __iomem *pin_addr; int edge; @@ -123,16 +129,16 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type) /* set level or edge */ pin_addr = port->base + PINCTRL_IRQLEV(port->id); if (edge & GPIO_INT_LEV_MASK) - __mxs_setl(pin_mask, pin_addr); + writel(pin_mask, pin_addr + MXS_SET); else - __mxs_clrl(pin_mask, pin_addr); + writel(pin_mask, pin_addr + MXS_CLR); /* set polarity */ pin_addr = port->base + PINCTRL_IRQPOL(port->id); if (edge & GPIO_INT_POL_MASK) - __mxs_setl(pin_mask, pin_addr); + writel(pin_mask, pin_addr + MXS_SET); else - __mxs_clrl(pin_mask, pin_addr); + writel(pin_mask, pin_addr + MXS_CLR); clear_gpio_irqstatus(port, gpio & 0x1f); @@ -143,13 +149,13 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type) static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc) { u32 irq_stat; - struct mxs_gpio_port *port = (struct mxs_gpio_port *)irq_get_handler_data(irq); + 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 = __raw_readl(port->base + PINCTRL_IRQSTAT(port->id)) & - __raw_readl(port->base + PINCTRL_IRQEN(port->id)); + 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; @@ -171,7 +177,7 @@ static int mxs_gpio_set_wake_irq(struct irq_data *d, unsigned int enable) { u32 gpio = irq_to_gpio(d->irq); u32 gpio_idx = gpio & 0x1f; - struct mxs_gpio_port *port = &mxs_gpio_ports[gpio / 32]; + struct mxs_gpio_port *port = irq_data_get_irq_chip_data(d); if (enable) { if (port->irq_high && (gpio_idx >= 16)) @@ -205,9 +211,9 @@ static void mxs_set_gpio_direction(struct gpio_chip *chip, unsigned offset, void __iomem *pin_addr = port->base + PINCTRL_DOE(port->id); if (dir) - __mxs_setl(1 << offset, pin_addr); + writel(1 << offset, pin_addr + MXS_SET); else - __mxs_clrl(1 << offset, pin_addr); + writel(1 << offset, pin_addr + MXS_CLR); } static int mxs_gpio_get(struct gpio_chip *chip, unsigned offset) @@ -215,7 +221,7 @@ static int mxs_gpio_get(struct gpio_chip *chip, unsigned offset) struct mxs_gpio_port *port = container_of(chip, struct mxs_gpio_port, chip); - return (__raw_readl(port->base + PINCTRL_DIN(port->id)) >> offset) & 1; + return (readl(port->base + PINCTRL_DIN(port->id)) >> offset) & 1; } static void mxs_gpio_set(struct gpio_chip *chip, unsigned offset, int value) @@ -225,9 +231,9 @@ static void mxs_gpio_set(struct gpio_chip *chip, unsigned offset, int value) void __iomem *pin_addr = port->base + PINCTRL_DOUT(port->id); if (value) - __mxs_setl(1 << offset, pin_addr); + writel(1 << offset, pin_addr + MXS_SET); else - __mxs_clrl(1 << offset, pin_addr); + writel(1 << offset, pin_addr + MXS_CLR); } static int mxs_gpio_to_irq(struct gpio_chip *chip, unsigned offset) @@ -252,87 +258,113 @@ static int mxs_gpio_direction_output(struct gpio_chip *chip, return 0; } -int __init mxs_gpio_init(struct mxs_gpio_port *port, int cnt) +static int __devinit mxs_gpio_probe(struct platform_device *pdev) { - int i, j; + static void __iomem *base; + struct mxs_gpio_port *port; + struct resource *iores = NULL; + int err, i; + + 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; + } - /* save for local usage */ - mxs_gpio_ports = port; - gpio_table_size = cnt; + if (!request_mem_region(iores->start, resource_size(iores), + pdev->name)) { + err = -EBUSY; + goto out_kfree; + } - pr_info("MXS GPIO hardware\n"); + base = ioremap(iores->start, resource_size(iores)); + if (!base) { + err = -ENOMEM; + goto out_release_mem; + } + } + port->base = base; - for (i = 0; i < cnt; i++) { - /* disable the interrupt and clear the status */ - __raw_writel(0, port[i].base + PINCTRL_PIN2IRQ(i)); - __raw_writel(0, port[i].base + PINCTRL_IRQEN(i)); + port->irq = platform_get_irq(pdev, 0); + if (port->irq < 0) { + err = -EINVAL; + goto out_iounmap; + } - /* clear address has to be used to clear IRQSTAT bits */ - __mxs_clrl(~0U, port[i].base + PINCTRL_IRQSTAT(i)); + /* disable the interrupt and clear the status */ + writel(0, port->base + PINCTRL_PIN2IRQ(port->id)); + writel(0, port->base + PINCTRL_IRQEN(port->id)); - for (j = port[i].virtual_irq_start; - j < port[i].virtual_irq_start + 32; j++) { - irq_set_chip_and_handler(j, &gpio_irq_chip, - handle_level_irq); - set_irq_flags(j, IRQF_VALID); - } + /* clear address has to be used to clear IRQSTAT bits */ + writel(~0U, port->base + PINCTRL_IRQSTAT(port->id) + MXS_CLR); - /* setup one handler for each entry */ - irq_set_chained_handler(port[i].irq, mxs_gpio_irq_handler); - irq_set_handler_data(port[i].irq, &port[i]); - - /* register gpio chip */ - port[i].chip.direction_input = mxs_gpio_direction_input; - port[i].chip.direction_output = mxs_gpio_direction_output; - port[i].chip.get = mxs_gpio_get; - port[i].chip.set = mxs_gpio_set; - port[i].chip.to_irq = mxs_gpio_to_irq; - port[i].chip.base = i * 32; - port[i].chip.ngpio = 32; - - /* its a serious configuration bug when it fails */ - BUG_ON(gpiochip_add(&port[i].chip) < 0); + for (i = port->virtual_irq_start; + i < port->virtual_irq_start + 32; i++) { + irq_set_chip_and_handler(i, &gpio_irq_chip, + handle_level_irq); + set_irq_flags(i, IRQF_VALID); + irq_set_chip_data(i, port); } - return 0; -} + /* setup one handler for each entry */ + irq_set_chained_handler(port->irq, mxs_gpio_irq_handler); + irq_set_handler_data(port->irq, port); -#define MX23_GPIO_BASE MX23_IO_ADDRESS(MX23_PINCTRL_BASE_ADDR) -#define MX28_GPIO_BASE MX28_IO_ADDRESS(MX28_PINCTRL_BASE_ADDR) + /* register gpio chip */ + port->chip.direction_input = mxs_gpio_direction_input; + port->chip.direction_output = mxs_gpio_direction_output; + port->chip.get = mxs_gpio_get; + port->chip.set = mxs_gpio_set; + port->chip.to_irq = mxs_gpio_to_irq; + port->chip.base = port->id * 32; + port->chip.ngpio = 32; -#define DEFINE_MXS_GPIO_PORT(_base, _irq, _id) \ - { \ - .chip.label = "gpio-" #_id, \ - .id = _id, \ - .irq = _irq, \ - .base = _base, \ - .virtual_irq_start = MXS_GPIO_IRQ_START + (_id) * 32, \ - } + err = gpiochip_add(&port->chip); + if (err) + goto out_iounmap; -#ifdef CONFIG_SOC_IMX23 -static struct mxs_gpio_port mx23_gpio_ports[] = { - DEFINE_MXS_GPIO_PORT(MX23_GPIO_BASE, MX23_INT_GPIO0, 0), - DEFINE_MXS_GPIO_PORT(MX23_GPIO_BASE, MX23_INT_GPIO1, 1), - DEFINE_MXS_GPIO_PORT(MX23_GPIO_BASE, MX23_INT_GPIO2, 2), -}; + return 0; -int __init mx23_register_gpios(void) -{ - return mxs_gpio_init(mx23_gpio_ports, ARRAY_SIZE(mx23_gpio_ports)); +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; } -#endif - -#ifdef CONFIG_SOC_IMX28 -static struct mxs_gpio_port mx28_gpio_ports[] = { - DEFINE_MXS_GPIO_PORT(MX28_GPIO_BASE, MX28_INT_GPIO0, 0), - DEFINE_MXS_GPIO_PORT(MX28_GPIO_BASE, MX28_INT_GPIO1, 1), - DEFINE_MXS_GPIO_PORT(MX28_GPIO_BASE, MX28_INT_GPIO2, 2), - DEFINE_MXS_GPIO_PORT(MX28_GPIO_BASE, MX28_INT_GPIO3, 3), - DEFINE_MXS_GPIO_PORT(MX28_GPIO_BASE, MX28_INT_GPIO4, 4), + +static struct platform_driver mxs_gpio_driver = { + .driver = { + .name = "gpio-mxs", + .owner = THIS_MODULE, + }, + .probe = mxs_gpio_probe, }; -int __init mx28_register_gpios(void) +static int __init mxs_gpio_init(void) { - return mxs_gpio_init(mx28_gpio_ports, ARRAY_SIZE(mx28_gpio_ports)); + return platform_driver_register(&mxs_gpio_driver); } -#endif +postcore_initcall(mxs_gpio_init); + +MODULE_AUTHOR("Freescale Semiconductor, " + "Daniel Mack , " + "Juergen Beisert "); +MODULE_DESCRIPTION("Freescale MXS GPIO"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From d37a65bb4663bde7cf3dbc51aec7f264fa4d0ebf Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 6 Jun 2011 00:07:54 +0800 Subject: gpio/mxc: Move Freescale MXC gpio driver to drivers/gpio GPIO drivers are getting moved to drivers/gpio for cleanup and consolidation. This patch moves the plat-mxc driver. Follow up patches will clean it up and make it a fine upstanding gpio driver. Signed-off-by: Shawn Guo Acked-by: Sascha Hauer Signed-off-by: Grant Likely --- arch/arm/plat-mxc/Makefile | 2 +- arch/arm/plat-mxc/gpio.c | 361 --------------------------------------------- drivers/gpio/Kconfig | 4 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-mxc.c | 361 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 367 insertions(+), 362 deletions(-) delete mode 100644 arch/arm/plat-mxc/gpio.c create mode 100644 drivers/gpio/gpio-mxc.c diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile index a1387875a49..d53c35fe2ea 100644 --- a/arch/arm/plat-mxc/Makefile +++ b/arch/arm/plat-mxc/Makefile @@ -3,7 +3,7 @@ # # Common support -obj-y := clock.o gpio.o time.o devices.o cpu.o system.o irq-common.o +obj-y := clock.o time.o devices.o cpu.o system.o irq-common.o # MX51 uses the TZIC interrupt controller, older platforms use AVIC obj-$(CONFIG_MXC_TZIC) += tzic.o diff --git a/arch/arm/plat-mxc/gpio.c b/arch/arm/plat-mxc/gpio.c deleted file mode 100644 index 6cd6d7f686f..00000000000 --- a/arch/arm/plat-mxc/gpio.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - * MXC GPIO support. (c) 2008 Daniel Mack - * 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 -#include -#include -#include -#include -#include -#include - -static struct mxc_gpio_port *mxc_gpio_ports; -static int gpio_table_size; - -#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 void _clear_gpio_irqstatus(struct mxc_gpio_port *port, u32 index) -{ - __raw_writel(1 << index, port->base + GPIO_ISR); -} - -static void _set_gpio_irqenable(struct mxc_gpio_port *port, u32 index, - int enable) -{ - u32 l; - - l = __raw_readl(port->base + GPIO_IMR); - l = (l & (~(1 << index))) | (!!enable << index); - __raw_writel(l, port->base + GPIO_IMR); -} - -static void gpio_ack_irq(struct irq_data *d) -{ - u32 gpio = irq_to_gpio(d->irq); - _clear_gpio_irqstatus(&mxc_gpio_ports[gpio / 32], gpio & 0x1f); -} - -static void gpio_mask_irq(struct irq_data *d) -{ - u32 gpio = irq_to_gpio(d->irq); - _set_gpio_irqenable(&mxc_gpio_ports[gpio / 32], gpio & 0x1f, 0); -} - -static void gpio_unmask_irq(struct irq_data *d) -{ - u32 gpio = irq_to_gpio(d->irq); - _set_gpio_irqenable(&mxc_gpio_ports[gpio / 32], gpio & 0x1f, 1); -} - -static int mxc_gpio_get(struct gpio_chip *chip, unsigned offset); - -static int gpio_set_irq_type(struct irq_data *d, u32 type) -{ - u32 gpio = irq_to_gpio(d->irq); - struct mxc_gpio_port *port = &mxc_gpio_ports[gpio / 32]; - 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 = mxc_gpio_get(&port->chip, gpio & 31); - 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 = __raw_readl(reg) & ~(0x3 << (bit << 1)); - __raw_writel(val | (edge << (bit << 1)), reg); - _clear_gpio_irqstatus(port, gpio & 0x1f); - - 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 = __raw_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; - } - __raw_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 = __raw_readl(port->base + GPIO_ISR) & - __raw_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) -{ - int i; - u32 irq_msk, irq_stat; - struct mxc_gpio_port *port = irq_get_handler_data(irq); - - /* walk through all interrupt status registers */ - for (i = 0; i < gpio_table_size; i++) { - irq_msk = __raw_readl(port[i].base + GPIO_IMR); - if (!irq_msk) - continue; - - irq_stat = __raw_readl(port[i].base + GPIO_ISR) & irq_msk; - if (irq_stat) - mxc_gpio_irq_handler(&port[i], 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 mxc_gpio_port *port = &mxc_gpio_ports[gpio / 32]; - - 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 struct irq_chip gpio_irq_chip = { - .name = "GPIO", - .irq_ack = gpio_ack_irq, - .irq_mask = gpio_mask_irq, - .irq_unmask = gpio_unmask_irq, - .irq_set_type = gpio_set_irq_type, - .irq_set_wake = gpio_set_wake_irq, -}; - -static void _set_gpio_direction(struct gpio_chip *chip, unsigned offset, - int dir) -{ - struct mxc_gpio_port *port = - container_of(chip, struct mxc_gpio_port, chip); - u32 l; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - l = __raw_readl(port->base + GPIO_GDIR); - if (dir) - l |= 1 << offset; - else - l &= ~(1 << offset); - __raw_writel(l, port->base + GPIO_GDIR); - spin_unlock_irqrestore(&port->lock, flags); -} - -static void mxc_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct mxc_gpio_port *port = - container_of(chip, struct mxc_gpio_port, chip); - void __iomem *reg = port->base + GPIO_DR; - u32 l; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - l = (__raw_readl(reg) & (~(1 << offset))) | (!!value << offset); - __raw_writel(l, reg); - spin_unlock_irqrestore(&port->lock, flags); -} - -static int mxc_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct mxc_gpio_port *port = - container_of(chip, struct mxc_gpio_port, chip); - - return (__raw_readl(port->base + GPIO_PSR) >> offset) & 1; -} - -static int mxc_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - _set_gpio_direction(chip, offset, 0); - return 0; -} - -static int mxc_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - mxc_gpio_set(chip, offset, value); - _set_gpio_direction(chip, offset, 1); - return 0; -} - -/* - * 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; - -int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt) -{ - int i, j; - - /* save for local usage */ - mxc_gpio_ports = port; - gpio_table_size = cnt; - - printk(KERN_INFO "MXC GPIO hardware\n"); - - for (i = 0; i < cnt; i++) { - /* disable the interrupt and clear the status */ - __raw_writel(0, port[i].base + GPIO_IMR); - __raw_writel(~0, port[i].base + GPIO_ISR); - for (j = port[i].virtual_irq_start; - j < port[i].virtual_irq_start + 32; j++) { - irq_set_lockdep_class(j, &gpio_lock_class); - irq_set_chip_and_handler(j, &gpio_irq_chip, - handle_level_irq); - set_irq_flags(j, IRQF_VALID); - } - - /* register gpio chip */ - port[i].chip.direction_input = mxc_gpio_direction_input; - port[i].chip.direction_output = mxc_gpio_direction_output; - port[i].chip.get = mxc_gpio_get; - port[i].chip.set = mxc_gpio_set; - port[i].chip.base = i * 32; - port[i].chip.ngpio = 32; - - spin_lock_init(&port[i].lock); - - /* its a serious configuration bug when it fails */ - BUG_ON( gpiochip_add(&port[i].chip) < 0 ); - - if (cpu_is_mx1() || cpu_is_mx3() || cpu_is_mx25() || cpu_is_mx51()) { - /* setup one handler for each entry */ - irq_set_chained_handler(port[i].irq, - mx3_gpio_irq_handler); - irq_set_handler_data(port[i].irq, &port[i]); - if (port[i].irq_high) { - /* setup handler for GPIO 16 to 31 */ - irq_set_chained_handler(port[i].irq_high, - mx3_gpio_irq_handler); - irq_set_handler_data(port[i].irq_high, - &port[i]); - } - } - } - - if (cpu_is_mx2()) { - /* setup one handler for all GPIO interrupts */ - irq_set_chained_handler(port[0].irq, mx2_gpio_irq_handler); - irq_set_handler_data(port[0].irq, port); - } - - return 0; -} diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 48711749ffc..1ee725aa021 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -97,6 +97,10 @@ config GPIO_MXS def_bool y depends on ARCH_MXS +config GPIO_MXC + def_bool y + depends on ARCH_MXC + 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 f879cbe493d..9c37339a57a 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -9,6 +9,7 @@ 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_MXC) += gpio-mxc.o obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o obj-$(CONFIG_GPIO_S5PC100) += gpio-s5pc100.o diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c new file mode 100644 index 00000000000..6cd6d7f686f --- /dev/null +++ b/drivers/gpio/gpio-mxc.c @@ -0,0 +1,361 @@ +/* + * MXC GPIO support. (c) 2008 Daniel Mack + * 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 +#include +#include +#include +#include +#include +#include + +static struct mxc_gpio_port *mxc_gpio_ports; +static int gpio_table_size; + +#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 void _clear_gpio_irqstatus(struct mxc_gpio_port *port, u32 index) +{ + __raw_writel(1 << index, port->base + GPIO_ISR); +} + +static void _set_gpio_irqenable(struct mxc_gpio_port *port, u32 index, + int enable) +{ + u32 l; + + l = __raw_readl(port->base + GPIO_IMR); + l = (l & (~(1 << index))) | (!!enable << index); + __raw_writel(l, port->base + GPIO_IMR); +} + +static void gpio_ack_irq(struct irq_data *d) +{ + u32 gpio = irq_to_gpio(d->irq); + _clear_gpio_irqstatus(&mxc_gpio_ports[gpio / 32], gpio & 0x1f); +} + +static void gpio_mask_irq(struct irq_data *d) +{ + u32 gpio = irq_to_gpio(d->irq); + _set_gpio_irqenable(&mxc_gpio_ports[gpio / 32], gpio & 0x1f, 0); +} + +static void gpio_unmask_irq(struct irq_data *d) +{ + u32 gpio = irq_to_gpio(d->irq); + _set_gpio_irqenable(&mxc_gpio_ports[gpio / 32], gpio & 0x1f, 1); +} + +static int mxc_gpio_get(struct gpio_chip *chip, unsigned offset); + +static int gpio_set_irq_type(struct irq_data *d, u32 type) +{ + u32 gpio = irq_to_gpio(d->irq); + struct mxc_gpio_port *port = &mxc_gpio_ports[gpio / 32]; + 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 = mxc_gpio_get(&port->chip, gpio & 31); + 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 = __raw_readl(reg) & ~(0x3 << (bit << 1)); + __raw_writel(val | (edge << (bit << 1)), reg); + _clear_gpio_irqstatus(port, gpio & 0x1f); + + 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 = __raw_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; + } + __raw_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 = __raw_readl(port->base + GPIO_ISR) & + __raw_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) +{ + int i; + u32 irq_msk, irq_stat; + struct mxc_gpio_port *port = irq_get_handler_data(irq); + + /* walk through all interrupt status registers */ + for (i = 0; i < gpio_table_size; i++) { + irq_msk = __raw_readl(port[i].base + GPIO_IMR); + if (!irq_msk) + continue; + + irq_stat = __raw_readl(port[i].base + GPIO_ISR) & irq_msk; + if (irq_stat) + mxc_gpio_irq_handler(&port[i], 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 mxc_gpio_port *port = &mxc_gpio_ports[gpio / 32]; + + 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 struct irq_chip gpio_irq_chip = { + .name = "GPIO", + .irq_ack = gpio_ack_irq, + .irq_mask = gpio_mask_irq, + .irq_unmask = gpio_unmask_irq, + .irq_set_type = gpio_set_irq_type, + .irq_set_wake = gpio_set_wake_irq, +}; + +static void _set_gpio_direction(struct gpio_chip *chip, unsigned offset, + int dir) +{ + struct mxc_gpio_port *port = + container_of(chip, struct mxc_gpio_port, chip); + u32 l; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + l = __raw_readl(port->base + GPIO_GDIR); + if (dir) + l |= 1 << offset; + else + l &= ~(1 << offset); + __raw_writel(l, port->base + GPIO_GDIR); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void mxc_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct mxc_gpio_port *port = + container_of(chip, struct mxc_gpio_port, chip); + void __iomem *reg = port->base + GPIO_DR; + u32 l; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + l = (__raw_readl(reg) & (~(1 << offset))) | (!!value << offset); + __raw_writel(l, reg); + spin_unlock_irqrestore(&port->lock, flags); +} + +static int mxc_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct mxc_gpio_port *port = + container_of(chip, struct mxc_gpio_port, chip); + + return (__raw_readl(port->base + GPIO_PSR) >> offset) & 1; +} + +static int mxc_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + _set_gpio_direction(chip, offset, 0); + return 0; +} + +static int mxc_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + mxc_gpio_set(chip, offset, value); + _set_gpio_direction(chip, offset, 1); + return 0; +} + +/* + * 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; + +int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt) +{ + int i, j; + + /* save for local usage */ + mxc_gpio_ports = port; + gpio_table_size = cnt; + + printk(KERN_INFO "MXC GPIO hardware\n"); + + for (i = 0; i < cnt; i++) { + /* disable the interrupt and clear the status */ + __raw_writel(0, port[i].base + GPIO_IMR); + __raw_writel(~0, port[i].base + GPIO_ISR); + for (j = port[i].virtual_irq_start; + j < port[i].virtual_irq_start + 32; j++) { + irq_set_lockdep_class(j, &gpio_lock_class); + irq_set_chip_and_handler(j, &gpio_irq_chip, + handle_level_irq); + set_irq_flags(j, IRQF_VALID); + } + + /* register gpio chip */ + port[i].chip.direction_input = mxc_gpio_direction_input; + port[i].chip.direction_output = mxc_gpio_direction_output; + port[i].chip.get = mxc_gpio_get; + port[i].chip.set = mxc_gpio_set; + port[i].chip.base = i * 32; + port[i].chip.ngpio = 32; + + spin_lock_init(&port[i].lock); + + /* its a serious configuration bug when it fails */ + BUG_ON( gpiochip_add(&port[i].chip) < 0 ); + + if (cpu_is_mx1() || cpu_is_mx3() || cpu_is_mx25() || cpu_is_mx51()) { + /* setup one handler for each entry */ + irq_set_chained_handler(port[i].irq, + mx3_gpio_irq_handler); + irq_set_handler_data(port[i].irq, &port[i]); + if (port[i].irq_high) { + /* setup handler for GPIO 16 to 31 */ + irq_set_chained_handler(port[i].irq_high, + mx3_gpio_irq_handler); + irq_set_handler_data(port[i].irq_high, + &port[i]); + } + } + } + + if (cpu_is_mx2()) { + /* setup one handler for all GPIO interrupts */ + irq_set_chained_handler(port[0].irq, mx2_gpio_irq_handler); + irq_set_handler_data(port[0].irq, port); + } + + return 0; +} -- cgit v1.2.3 From b78d8e59a6f611e229fab8ec3014c58eba975000 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 6 Jun 2011 00:07:55 +0800 Subject: gpio/mxc: Change gpio-mxc into an upstanding gpio driver The patch makes necessary changes on gpio-mxc as below to turn it into an upstanding gpio driver. * Add a list to save all mx2 ports references, so that mx2_gpio_irq_handler can walk through all interrupt status registers * Use readl/writel to replace mach-specific accessors __raw_readl/__raw_writel * Change mxc_gpio_init into mxc_gpio_probe function * Move "struct mxc_gpio_port" into gpio-mxc.c, as it needs not to be public at all, and also make some other cleanup on plat-mxc/include/mach/gpio.h at the same time And the patch then migrates mach-imx and mach-mx5 to the updated driver by adding corresponding platform devices. Signed-off-by: Shawn Guo Acked-by: Olof Johansson Acked-by: Sascha Hauer Signed-off-by: Grant Likely --- arch/arm/mach-imx/mach-apf9328.c | 2 + arch/arm/mach-imx/mach-armadillo5x0.c | 2 + arch/arm/mach-imx/mach-bug.c | 2 + arch/arm/mach-imx/mach-cpuimx27.c | 2 + arch/arm/mach-imx/mach-cpuimx35.c | 2 + arch/arm/mach-imx/mach-eukrea_cpuimx25.c | 2 + arch/arm/mach-imx/mach-imx27_visstrim_m10.c | 2 + arch/arm/mach-imx/mach-imx27ipcam.c | 2 + arch/arm/mach-imx/mach-imx27lite.c | 2 + arch/arm/mach-imx/mach-kzm_arm11_01.c | 2 + arch/arm/mach-imx/mach-mx1ads.c | 2 + arch/arm/mach-imx/mach-mx21ads.c | 2 + arch/arm/mach-imx/mach-mx25_3ds.c | 2 + arch/arm/mach-imx/mach-mx27_3ds.c | 2 + arch/arm/mach-imx/mach-mx27ads.c | 2 + arch/arm/mach-imx/mach-mx31_3ds.c | 2 + arch/arm/mach-imx/mach-mx31ads.c | 2 + arch/arm/mach-imx/mach-mx31lilly.c | 2 + arch/arm/mach-imx/mach-mx31lite.c | 2 + arch/arm/mach-imx/mach-mx31moboard.c | 2 + arch/arm/mach-imx/mach-mx35_3ds.c | 2 + arch/arm/mach-imx/mach-mxt_td60.c | 2 + arch/arm/mach-imx/mach-pca100.c | 2 + arch/arm/mach-imx/mach-pcm037.c | 2 + arch/arm/mach-imx/mach-pcm038.c | 2 + arch/arm/mach-imx/mach-pcm043.c | 2 + arch/arm/mach-imx/mach-qong.c | 2 + arch/arm/mach-imx/mach-scb9328.c | 2 + arch/arm/mach-imx/mach-vpr200.c | 2 + arch/arm/mach-imx/mm-imx1.c | 17 +- arch/arm/mach-imx/mm-imx21.c | 21 ++- arch/arm/mach-imx/mm-imx25.c | 16 +- arch/arm/mach-imx/mm-imx27.c | 21 ++- arch/arm/mach-imx/mm-imx31.c | 15 +- arch/arm/mach-imx/mm-imx35.c | 15 +- arch/arm/mach-mx5/board-cpuimx51.c | 2 + arch/arm/mach-mx5/board-cpuimx51sd.c | 2 + arch/arm/mach-mx5/board-mx50_rdp.c | 2 + arch/arm/mach-mx5/board-mx51_3ds.c | 2 + arch/arm/mach-mx5/board-mx51_babbage.c | 2 + arch/arm/mach-mx5/board-mx51_efikamx.c | 2 + arch/arm/mach-mx5/board-mx51_efikasb.c | 2 + arch/arm/mach-mx5/board-mx53_evk.c | 2 + arch/arm/mach-mx5/board-mx53_loco.c | 2 + arch/arm/mach-mx5/board-mx53_smd.c | 2 + arch/arm/mach-mx5/devices.c | 64 ------- arch/arm/mach-mx5/mm-mx50.c | 21 ++- arch/arm/mach-mx5/mm.c | 25 ++- arch/arm/plat-mxc/devices.c | 11 ++ arch/arm/plat-mxc/devices/Makefile | 1 + arch/arm/plat-mxc/devices/platform-gpio-mxc.c | 32 ++++ arch/arm/plat-mxc/include/mach/common.h | 12 +- arch/arm/plat-mxc/include/mach/devices-common.h | 2 + arch/arm/plat-mxc/include/mach/gpio.h | 27 --- drivers/gpio/gpio-mxc.c | 217 ++++++++++++++++-------- 55 files changed, 358 insertions(+), 237 deletions(-) create mode 100644 arch/arm/plat-mxc/devices/platform-gpio-mxc.c diff --git a/arch/arm/mach-imx/mach-apf9328.c b/arch/arm/mach-imx/mach-apf9328.c index 15e45c84e37..59d2a3b137d 100644 --- a/arch/arm/mach-imx/mach-apf9328.c +++ b/arch/arm/mach-imx/mach-apf9328.c @@ -115,6 +115,8 @@ static struct platform_device *devices[] __initdata = { static void __init apf9328_init(void) { + imx1_soc_init(); + mxc_gpio_setup_multiple_pins(apf9328_pins, ARRAY_SIZE(apf9328_pins), "APF9328"); diff --git a/arch/arm/mach-imx/mach-armadillo5x0.c b/arch/arm/mach-imx/mach-armadillo5x0.c index ffb40ff619b..ede2710f8b7 100644 --- a/arch/arm/mach-imx/mach-armadillo5x0.c +++ b/arch/arm/mach-imx/mach-armadillo5x0.c @@ -490,6 +490,8 @@ static struct platform_device *devices[] __initdata = { */ static void __init armadillo5x0_init(void) { + imx31_soc_init(); + mxc_iomux_setup_multiple_pins(armadillo5x0_pins, ARRAY_SIZE(armadillo5x0_pins), "armadillo5x0"); diff --git a/arch/arm/mach-imx/mach-bug.c b/arch/arm/mach-imx/mach-bug.c index 42e4f078a19..f49470553bd 100644 --- a/arch/arm/mach-imx/mach-bug.c +++ b/arch/arm/mach-imx/mach-bug.c @@ -42,6 +42,8 @@ static const unsigned int bug_pins[] __initconst = { static void __init bug_board_init(void) { + imx31_soc_init(); + mxc_iomux_setup_multiple_pins(bug_pins, ARRAY_SIZE(bug_pins), "uart-4"); imx31_add_imx_uart4(&uart_pdata); diff --git a/arch/arm/mach-imx/mach-cpuimx27.c b/arch/arm/mach-imx/mach-cpuimx27.c index 46a2e41d43d..87887ac5806 100644 --- a/arch/arm/mach-imx/mach-cpuimx27.c +++ b/arch/arm/mach-imx/mach-cpuimx27.c @@ -250,6 +250,8 @@ __setup("otg_mode=", eukrea_cpuimx27_otg_mode); static void __init eukrea_cpuimx27_init(void) { + imx27_soc_init(); + mxc_gpio_setup_multiple_pins(eukrea_cpuimx27_pins, ARRAY_SIZE(eukrea_cpuimx27_pins), "CPUIMX27"); diff --git a/arch/arm/mach-imx/mach-cpuimx35.c b/arch/arm/mach-imx/mach-cpuimx35.c index 3f8ef825fa6..f39a478ba1a 100644 --- a/arch/arm/mach-imx/mach-cpuimx35.c +++ b/arch/arm/mach-imx/mach-cpuimx35.c @@ -156,6 +156,8 @@ __setup("otg_mode=", eukrea_cpuimx35_otg_mode); */ static void __init eukrea_cpuimx35_init(void) { + imx35_soc_init(); + mxc_iomux_v3_setup_multiple_pads(eukrea_cpuimx35_pads, ARRAY_SIZE(eukrea_cpuimx35_pads)); diff --git a/arch/arm/mach-imx/mach-eukrea_cpuimx25.c b/arch/arm/mach-imx/mach-eukrea_cpuimx25.c index 148cff2819b..da36da52969 100644 --- a/arch/arm/mach-imx/mach-eukrea_cpuimx25.c +++ b/arch/arm/mach-imx/mach-eukrea_cpuimx25.c @@ -125,6 +125,8 @@ __setup("otg_mode=", eukrea_cpuimx25_otg_mode); static void __init eukrea_cpuimx25_init(void) { + imx25_soc_init(); + if (mxc_iomux_v3_setup_multiple_pads(eukrea_cpuimx25_pads, ARRAY_SIZE(eukrea_cpuimx25_pads))) printk(KERN_ERR "error setting cpuimx25 pads !\n"); diff --git a/arch/arm/mach-imx/mach-imx27_visstrim_m10.c b/arch/arm/mach-imx/mach-imx27_visstrim_m10.c index 7ae43b1ec51..c6269d60ddb 100644 --- a/arch/arm/mach-imx/mach-imx27_visstrim_m10.c +++ b/arch/arm/mach-imx/mach-imx27_visstrim_m10.c @@ -231,6 +231,8 @@ static void __init visstrim_m10_board_init(void) { int ret; + imx27_soc_init(); + ret = mxc_gpio_setup_multiple_pins(visstrim_m10_pins, ARRAY_SIZE(visstrim_m10_pins), "VISSTRIM_M10"); if (ret) diff --git a/arch/arm/mach-imx/mach-imx27ipcam.c b/arch/arm/mach-imx/mach-imx27ipcam.c index 9be6cd6fbf8..272f793e924 100644 --- a/arch/arm/mach-imx/mach-imx27ipcam.c +++ b/arch/arm/mach-imx/mach-imx27ipcam.c @@ -50,6 +50,8 @@ static const int mx27ipcam_pins[] __initconst = { static void __init mx27ipcam_init(void) { + imx27_soc_init(); + mxc_gpio_setup_multiple_pins(mx27ipcam_pins, ARRAY_SIZE(mx27ipcam_pins), "mx27ipcam"); diff --git a/arch/arm/mach-imx/mach-imx27lite.c b/arch/arm/mach-imx/mach-imx27lite.c index 841140516ed..d81a769fe89 100644 --- a/arch/arm/mach-imx/mach-imx27lite.c +++ b/arch/arm/mach-imx/mach-imx27lite.c @@ -59,6 +59,8 @@ static const struct imxuart_platform_data uart_pdata __initconst = { static void __init mx27lite_init(void) { + imx27_soc_init(); + mxc_gpio_setup_multiple_pins(mx27lite_pins, ARRAY_SIZE(mx27lite_pins), "imx27lite"); imx27_add_imx_uart0(&uart_pdata); diff --git a/arch/arm/mach-imx/mach-kzm_arm11_01.c b/arch/arm/mach-imx/mach-kzm_arm11_01.c index 1ecae20cf4e..e472a1d8805 100644 --- a/arch/arm/mach-imx/mach-kzm_arm11_01.c +++ b/arch/arm/mach-imx/mach-kzm_arm11_01.c @@ -223,6 +223,8 @@ static int kzm_pins[] __initdata = { */ static void __init kzm_board_init(void) { + imx31_soc_init(); + mxc_iomux_setup_multiple_pins(kzm_pins, ARRAY_SIZE(kzm_pins), "kzm"); kzm_init_ext_uart(); diff --git a/arch/arm/mach-imx/mach-mx1ads.c b/arch/arm/mach-imx/mach-mx1ads.c index 38ec5cbbda9..5cd8bee4696 100644 --- a/arch/arm/mach-imx/mach-mx1ads.c +++ b/arch/arm/mach-imx/mach-mx1ads.c @@ -115,6 +115,8 @@ static struct i2c_board_info mx1ads_i2c_devices[] = { */ static void __init mx1ads_init(void) { + imx1_soc_init(); + mxc_gpio_setup_multiple_pins(mx1ads_pins, ARRAY_SIZE(mx1ads_pins), "mx1ads"); diff --git a/arch/arm/mach-imx/mach-mx21ads.c b/arch/arm/mach-imx/mach-mx21ads.c index 74ac88978dd..d389ecf9b5a 100644 --- a/arch/arm/mach-imx/mach-mx21ads.c +++ b/arch/arm/mach-imx/mach-mx21ads.c @@ -279,6 +279,8 @@ static struct platform_device *platform_devices[] __initdata = { static void __init mx21ads_board_init(void) { + imx21_soc_init(); + mxc_gpio_setup_multiple_pins(mx21ads_pins, ARRAY_SIZE(mx21ads_pins), "mx21ads"); diff --git a/arch/arm/mach-imx/mach-mx25_3ds.c b/arch/arm/mach-imx/mach-mx25_3ds.c index 58ea3fdf091..01534bb6130 100644 --- a/arch/arm/mach-imx/mach-mx25_3ds.c +++ b/arch/arm/mach-imx/mach-mx25_3ds.c @@ -219,6 +219,8 @@ static const struct esdhc_platform_data mx25pdk_esdhc_pdata __initconst = { static void __init mx25pdk_init(void) { + imx25_soc_init(); + mxc_iomux_v3_setup_multiple_pads(mx25pdk_pads, ARRAY_SIZE(mx25pdk_pads)); diff --git a/arch/arm/mach-imx/mach-mx27_3ds.c b/arch/arm/mach-imx/mach-mx27_3ds.c index 6e1accf93f8..117ce0a50f4 100644 --- a/arch/arm/mach-imx/mach-mx27_3ds.c +++ b/arch/arm/mach-imx/mach-mx27_3ds.c @@ -267,6 +267,8 @@ static const struct imxi2c_platform_data mx27_3ds_i2c0_data __initconst = { static void __init mx27pdk_init(void) { + imx27_soc_init(); + mxc_gpio_setup_multiple_pins(mx27pdk_pins, ARRAY_SIZE(mx27pdk_pins), "mx27pdk"); mx27_3ds_sdhc1_enable_level_translator(); diff --git a/arch/arm/mach-imx/mach-mx27ads.c b/arch/arm/mach-imx/mach-mx27ads.c index 1db79506f5e..fc26ed71b9e 100644 --- a/arch/arm/mach-imx/mach-mx27ads.c +++ b/arch/arm/mach-imx/mach-mx27ads.c @@ -288,6 +288,8 @@ static const struct imxuart_platform_data uart_pdata __initconst = { static void __init mx27ads_board_init(void) { + imx27_soc_init(); + mxc_gpio_setup_multiple_pins(mx27ads_pins, ARRAY_SIZE(mx27ads_pins), "mx27ads"); diff --git a/arch/arm/mach-imx/mach-mx31_3ds.c b/arch/arm/mach-imx/mach-mx31_3ds.c index 9b982449cb5..ab2a6268b50 100644 --- a/arch/arm/mach-imx/mach-mx31_3ds.c +++ b/arch/arm/mach-imx/mach-mx31_3ds.c @@ -689,6 +689,8 @@ static void __init mx31_3ds_init(void) { int ret; + imx31_soc_init(); + mxc_iomux_setup_multiple_pins(mx31_3ds_pins, ARRAY_SIZE(mx31_3ds_pins), "mx31_3ds"); diff --git a/arch/arm/mach-imx/mach-mx31ads.c b/arch/arm/mach-imx/mach-mx31ads.c index f4dee025463..0ce49478a47 100644 --- a/arch/arm/mach-imx/mach-mx31ads.c +++ b/arch/arm/mach-imx/mach-mx31ads.c @@ -516,6 +516,8 @@ static void __init mx31ads_init_irq(void) static void __init mx31ads_init(void) { + imx31_soc_init(); + mxc_init_extuart(); mxc_init_imx_uart(); mxc_init_i2c(); diff --git a/arch/arm/mach-imx/mach-mx31lilly.c b/arch/arm/mach-imx/mach-mx31lilly.c index 410e676ae08..750368ddf0f 100644 --- a/arch/arm/mach-imx/mach-mx31lilly.c +++ b/arch/arm/mach-imx/mach-mx31lilly.c @@ -243,6 +243,8 @@ core_param(mx31lilly_baseboard, mx31lilly_baseboard, int, 0444); static void __init mx31lilly_board_init(void) { + imx31_soc_init(); + switch (mx31lilly_baseboard) { case MX31LILLY_NOBOARD: break; diff --git a/arch/arm/mach-imx/mach-mx31lite.c b/arch/arm/mach-imx/mach-mx31lite.c index ac9b4cad320..4b47fd9fdd8 100644 --- a/arch/arm/mach-imx/mach-mx31lite.c +++ b/arch/arm/mach-imx/mach-mx31lite.c @@ -230,6 +230,8 @@ static void __init mx31lite_init(void) { int ret; + imx31_soc_init(); + switch (mx31lite_baseboard) { case MX31LITE_NOBOARD: break; diff --git a/arch/arm/mach-imx/mach-mx31moboard.c b/arch/arm/mach-imx/mach-mx31moboard.c index eaa51e49ca9..a52fd36e2b5 100644 --- a/arch/arm/mach-imx/mach-mx31moboard.c +++ b/arch/arm/mach-imx/mach-mx31moboard.c @@ -507,6 +507,8 @@ core_param(mx31moboard_baseboard, mx31moboard_baseboard, int, 0444); */ static void __init mx31moboard_init(void) { + imx31_soc_init(); + mxc_iomux_setup_multiple_pins(moboard_pins, ARRAY_SIZE(moboard_pins), "moboard"); diff --git a/arch/arm/mach-imx/mach-mx35_3ds.c b/arch/arm/mach-imx/mach-mx35_3ds.c index 882880ac1bb..48b3c6fd5cf 100644 --- a/arch/arm/mach-imx/mach-mx35_3ds.c +++ b/arch/arm/mach-imx/mach-mx35_3ds.c @@ -179,6 +179,8 @@ static const struct imxi2c_platform_data mx35_3ds_i2c0_data __initconst = { */ static void __init mx35_3ds_init(void) { + imx35_soc_init(); + mxc_iomux_v3_setup_multiple_pads(mx35pdk_pads, ARRAY_SIZE(mx35pdk_pads)); imx35_add_fec(NULL); diff --git a/arch/arm/mach-imx/mach-mxt_td60.c b/arch/arm/mach-imx/mach-mxt_td60.c index 2774541511e..c85876fed66 100644 --- a/arch/arm/mach-imx/mach-mxt_td60.c +++ b/arch/arm/mach-imx/mach-mxt_td60.c @@ -233,6 +233,8 @@ static const struct imxuart_platform_data uart_pdata __initconst = { static void __init mxt_td60_board_init(void) { + imx27_soc_init(); + mxc_gpio_setup_multiple_pins(mxt_td60_pins, ARRAY_SIZE(mxt_td60_pins), "MXT_TD60"); diff --git a/arch/arm/mach-imx/mach-pca100.c b/arch/arm/mach-imx/mach-pca100.c index bbddc5a11c4..71083aa1603 100644 --- a/arch/arm/mach-imx/mach-pca100.c +++ b/arch/arm/mach-imx/mach-pca100.c @@ -357,6 +357,8 @@ static void __init pca100_init(void) { int ret; + imx27_soc_init(); + /* SSI unit */ mxc_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, MXC_AUDMUX_V1_PCR_SYN | /* 4wire mode */ diff --git a/arch/arm/mach-imx/mach-pcm037.c b/arch/arm/mach-imx/mach-pcm037.c index 89c213b8129..f45b7cd72c8 100644 --- a/arch/arm/mach-imx/mach-pcm037.c +++ b/arch/arm/mach-imx/mach-pcm037.c @@ -576,6 +576,8 @@ static void __init pcm037_init(void) { int ret; + imx31_soc_init(); + mxc_iomux_set_gpr(MUX_PGP_UH2, 1); mxc_iomux_setup_multiple_pins(pcm037_pins, ARRAY_SIZE(pcm037_pins), diff --git a/arch/arm/mach-imx/mach-pcm038.c b/arch/arm/mach-imx/mach-pcm038.c index 853bb871c7e..2d6a64bbac4 100644 --- a/arch/arm/mach-imx/mach-pcm038.c +++ b/arch/arm/mach-imx/mach-pcm038.c @@ -295,6 +295,8 @@ static const struct mxc_usbh_platform_data usbh2_pdata __initconst = { static void __init pcm038_init(void) { + imx27_soc_init(); + mxc_gpio_setup_multiple_pins(pcm038_pins, ARRAY_SIZE(pcm038_pins), "PCM038"); diff --git a/arch/arm/mach-imx/mach-pcm043.c b/arch/arm/mach-imx/mach-pcm043.c index 026441628df..163cc318caf 100644 --- a/arch/arm/mach-imx/mach-pcm043.c +++ b/arch/arm/mach-imx/mach-pcm043.c @@ -356,6 +356,8 @@ static struct esdhc_platform_data sd1_pdata = { */ static void __init pcm043_init(void) { + imx35_soc_init(); + mxc_iomux_v3_setup_multiple_pads(pcm043_pads, ARRAY_SIZE(pcm043_pads)); mxc_audmux_v2_configure_port(3, diff --git a/arch/arm/mach-imx/mach-qong.c b/arch/arm/mach-imx/mach-qong.c index c1632871593..3626f486498 100644 --- a/arch/arm/mach-imx/mach-qong.c +++ b/arch/arm/mach-imx/mach-qong.c @@ -244,6 +244,8 @@ static void __init qong_init_fpga(void) */ static void __init qong_init(void) { + imx31_soc_init(); + mxc_init_imx_uart(); qong_init_nor_mtd(); qong_init_fpga(); diff --git a/arch/arm/mach-imx/mach-scb9328.c b/arch/arm/mach-imx/mach-scb9328.c index dcaee043628..82805260e19 100644 --- a/arch/arm/mach-imx/mach-scb9328.c +++ b/arch/arm/mach-imx/mach-scb9328.c @@ -129,6 +129,8 @@ static struct platform_device *devices[] __initdata = { */ static void __init scb9328_init(void) { + imx1_soc_init(); + imx1_add_imx_uart0(&uart_pdata); printk(KERN_INFO"Scb9328: Adding devices\n"); diff --git a/arch/arm/mach-imx/mach-vpr200.c b/arch/arm/mach-imx/mach-vpr200.c index d74e3473d23..7d8e012a633 100644 --- a/arch/arm/mach-imx/mach-vpr200.c +++ b/arch/arm/mach-imx/mach-vpr200.c @@ -267,6 +267,8 @@ static struct platform_device *devices[] __initdata = { */ static void __init vpr200_board_init(void) { + imx35_soc_init(); + mxc_iomux_v3_setup_multiple_pads(vpr200_pads, ARRAY_SIZE(vpr200_pads)); imx35_add_fec(NULL); diff --git a/arch/arm/mach-imx/mm-imx1.c b/arch/arm/mach-imx/mm-imx1.c index 2e482ba5a0e..b486595701b 100644 --- a/arch/arm/mach-imx/mm-imx1.c +++ b/arch/arm/mach-imx/mm-imx1.c @@ -23,7 +23,6 @@ #include #include -#include #include #include @@ -44,15 +43,15 @@ void __init imx1_init_early(void) MX1_NUM_GPIO_PORT); } -static struct mxc_gpio_port imx1_gpio_ports[] = { - DEFINE_IMX_GPIO_PORT_IRQ(MX1, 0, 1, MX1_GPIO_INT_PORTA), - DEFINE_IMX_GPIO_PORT_IRQ(MX1, 1, 2, MX1_GPIO_INT_PORTB), - DEFINE_IMX_GPIO_PORT_IRQ(MX1, 2, 3, MX1_GPIO_INT_PORTC), - DEFINE_IMX_GPIO_PORT_IRQ(MX1, 3, 4, MX1_GPIO_INT_PORTD), -}; - void __init mx1_init_irq(void) { mxc_init_irq(MX1_IO_ADDRESS(MX1_AVIC_BASE_ADDR)); - mxc_gpio_init(imx1_gpio_ports, ARRAY_SIZE(imx1_gpio_ports)); +} + +void __init imx1_soc_init(void) +{ + mxc_register_gpio(0, MX1_GPIO1_BASE_ADDR, SZ_4K, MX1_GPIO_INT_PORTA, 0); + mxc_register_gpio(1, MX1_GPIO2_BASE_ADDR, SZ_4K, MX1_GPIO_INT_PORTB, 0); + mxc_register_gpio(2, MX1_GPIO3_BASE_ADDR, SZ_4K, MX1_GPIO_INT_PORTC, 0); + mxc_register_gpio(3, MX1_GPIO4_BASE_ADDR, SZ_4K, MX1_GPIO_INT_PORTD, 0); } diff --git a/arch/arm/mach-imx/mm-imx21.c b/arch/arm/mach-imx/mm-imx21.c index 7a0c500ac2c..f0fb8bcce6f 100644 --- a/arch/arm/mach-imx/mm-imx21.c +++ b/arch/arm/mach-imx/mm-imx21.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include @@ -70,17 +69,17 @@ void __init imx21_init_early(void) MX21_NUM_GPIO_PORT); } -static struct mxc_gpio_port imx21_gpio_ports[] = { - DEFINE_IMX_GPIO_PORT_IRQ(MX21, 0, 1, MX21_INT_GPIO), - DEFINE_IMX_GPIO_PORT(MX21, 1, 2), - DEFINE_IMX_GPIO_PORT(MX21, 2, 3), - DEFINE_IMX_GPIO_PORT(MX21, 3, 4), - DEFINE_IMX_GPIO_PORT(MX21, 4, 5), - DEFINE_IMX_GPIO_PORT(MX21, 5, 6), -}; - void __init mx21_init_irq(void) { mxc_init_irq(MX21_IO_ADDRESS(MX21_AVIC_BASE_ADDR)); - mxc_gpio_init(imx21_gpio_ports, ARRAY_SIZE(imx21_gpio_ports)); +} + +void __init imx21_soc_init(void) +{ + mxc_register_gpio(0, MX21_GPIO1_BASE_ADDR, SZ_4K, MX21_INT_GPIO, 0); + mxc_register_gpio(1, MX21_GPIO2_BASE_ADDR, SZ_4K, MX21_INT_GPIO, 0); + mxc_register_gpio(2, MX21_GPIO3_BASE_ADDR, SZ_4K, MX21_INT_GPIO, 0); + mxc_register_gpio(3, MX21_GPIO4_BASE_ADDR, SZ_4K, MX21_INT_GPIO, 0); + mxc_register_gpio(4, MX21_GPIO5_BASE_ADDR, SZ_4K, MX21_INT_GPIO, 0); + mxc_register_gpio(5, MX21_GPIO6_BASE_ADDR, SZ_4K, MX21_INT_GPIO, 0); } diff --git a/arch/arm/mach-imx/mm-imx25.c b/arch/arm/mach-imx/mm-imx25.c index 02f7b5c7fa8..1b6d583f750 100644 --- a/arch/arm/mach-imx/mm-imx25.c +++ b/arch/arm/mach-imx/mm-imx25.c @@ -27,7 +27,6 @@ #include #include #include -#include #include /* @@ -57,16 +56,15 @@ void __init imx25_init_early(void) mxc_arch_reset_init(MX25_IO_ADDRESS(MX25_WDOG_BASE_ADDR)); } -static struct mxc_gpio_port imx25_gpio_ports[] = { - DEFINE_IMX_GPIO_PORT_IRQ(MX25, 0, 1, MX25_INT_GPIO1), - DEFINE_IMX_GPIO_PORT_IRQ(MX25, 1, 2, MX25_INT_GPIO2), - DEFINE_IMX_GPIO_PORT_IRQ(MX25, 2, 3, MX25_INT_GPIO3), - DEFINE_IMX_GPIO_PORT_IRQ(MX25, 3, 4, MX25_INT_GPIO4), -}; - void __init mx25_init_irq(void) { mxc_init_irq(MX25_IO_ADDRESS(MX25_AVIC_BASE_ADDR)); - mxc_gpio_init(imx25_gpio_ports, ARRAY_SIZE(imx25_gpio_ports)); } +void __init imx25_soc_init(void) +{ + mxc_register_gpio(0, MX25_GPIO1_BASE_ADDR, SZ_16K, MX25_INT_GPIO1, 0); + mxc_register_gpio(1, MX25_GPIO2_BASE_ADDR, SZ_16K, MX25_INT_GPIO2, 0); + mxc_register_gpio(2, MX25_GPIO3_BASE_ADDR, SZ_16K, MX25_INT_GPIO3, 0); + mxc_register_gpio(3, MX25_GPIO4_BASE_ADDR, SZ_16K, MX25_INT_GPIO4, 0); +} diff --git a/arch/arm/mach-imx/mm-imx27.c b/arch/arm/mach-imx/mm-imx27.c index a6761a39f08..d3700cec8ec 100644 --- a/arch/arm/mach-imx/mm-imx27.c +++ b/arch/arm/mach-imx/mm-imx27.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include @@ -70,17 +69,17 @@ void __init imx27_init_early(void) MX27_NUM_GPIO_PORT); } -static struct mxc_gpio_port imx27_gpio_ports[] = { - DEFINE_IMX_GPIO_PORT_IRQ(MX27, 0, 1, MX27_INT_GPIO), - DEFINE_IMX_GPIO_PORT(MX27, 1, 2), - DEFINE_IMX_GPIO_PORT(MX27, 2, 3), - DEFINE_IMX_GPIO_PORT(MX27, 3, 4), - DEFINE_IMX_GPIO_PORT(MX27, 4, 5), - DEFINE_IMX_GPIO_PORT(MX27, 5, 6), -}; - void __init mx27_init_irq(void) { mxc_init_irq(MX27_IO_ADDRESS(MX27_AVIC_BASE_ADDR)); - mxc_gpio_init(imx27_gpio_ports, ARRAY_SIZE(imx27_gpio_ports)); +} + +void __init imx27_soc_init(void) +{ + mxc_register_gpio(0, MX27_GPIO1_BASE_ADDR, SZ_4K, MX27_INT_GPIO, 0); + mxc_register_gpio(1, MX27_GPIO2_BASE_ADDR, SZ_4K, MX27_INT_GPIO, 0); + mxc_register_gpio(2, MX27_GPIO3_BASE_ADDR, SZ_4K, MX27_INT_GPIO, 0); + mxc_register_gpio(3, MX27_GPIO4_BASE_ADDR, SZ_4K, MX27_INT_GPIO, 0); + mxc_register_gpio(4, MX27_GPIO5_BASE_ADDR, SZ_4K, MX27_INT_GPIO, 0); + mxc_register_gpio(5, MX27_GPIO6_BASE_ADDR, SZ_4K, MX27_INT_GPIO, 0); } diff --git a/arch/arm/mach-imx/mm-imx31.c b/arch/arm/mach-imx/mm-imx31.c index 86b9b45864d..cb16ac66177 100644 --- a/arch/arm/mach-imx/mm-imx31.c +++ b/arch/arm/mach-imx/mm-imx31.c @@ -26,7 +26,6 @@ #include #include #include -#include #include static struct map_desc mx31_io_desc[] __initdata = { @@ -53,14 +52,14 @@ void __init imx31_init_early(void) mxc_arch_reset_init(MX31_IO_ADDRESS(MX31_WDOG_BASE_ADDR)); } -static struct mxc_gpio_port imx31_gpio_ports[] = { - DEFINE_IMX_GPIO_PORT_IRQ(MX31, 0, 1, MX31_INT_GPIO1), - DEFINE_IMX_GPIO_PORT_IRQ(MX31, 1, 2, MX31_INT_GPIO2), - DEFINE_IMX_GPIO_PORT_IRQ(MX31, 2, 3, MX31_INT_GPIO3), -}; - void __init mx31_init_irq(void) { mxc_init_irq(MX31_IO_ADDRESS(MX31_AVIC_BASE_ADDR)); - mxc_gpio_init(imx31_gpio_ports, ARRAY_SIZE(imx31_gpio_ports)); +} + +void __init imx31_soc_init(void) +{ + mxc_register_gpio(0, MX31_GPIO1_BASE_ADDR, SZ_16K, MX31_INT_GPIO1, 0); + mxc_register_gpio(1, MX31_GPIO2_BASE_ADDR, SZ_16K, MX31_INT_GPIO2, 0); + mxc_register_gpio(2, MX31_GPIO3_BASE_ADDR, SZ_16K, MX31_INT_GPIO3, 0); } diff --git a/arch/arm/mach-imx/mm-imx35.c b/arch/arm/mach-imx/mm-imx35.c index c880e6d1ae5..648bfca0163 100644 --- a/arch/arm/mach-imx/mm-imx35.c +++ b/arch/arm/mach-imx/mm-imx35.c @@ -27,7 +27,6 @@ #include #include #include -#include #include static struct map_desc mx35_io_desc[] __initdata = { @@ -50,14 +49,14 @@ void __init imx35_init_early(void) mxc_arch_reset_init(MX35_IO_ADDRESS(MX35_WDOG_BASE_ADDR)); } -static struct mxc_gpio_port imx35_gpio_ports[] = { - DEFINE_IMX_GPIO_PORT_IRQ(MX35, 0, 1, MX35_INT_GPIO1), - DEFINE_IMX_GPIO_PORT_IRQ(MX35, 1, 2, MX35_INT_GPIO2), - DEFINE_IMX_GPIO_PORT_IRQ(MX35, 2, 3, MX35_INT_GPIO3), -}; - void __init mx35_init_irq(void) { mxc_init_irq(MX35_IO_ADDRESS(MX35_AVIC_BASE_ADDR)); - mxc_gpio_init(imx35_gpio_ports, ARRAY_SIZE(imx35_gpio_ports)); +} + +void __init imx35_soc_init(void) +{ + mxc_register_gpio(0, MX35_GPIO1_BASE_ADDR, SZ_16K, MX35_INT_GPIO1, 0); + mxc_register_gpio(1, MX35_GPIO2_BASE_ADDR, SZ_16K, MX35_INT_GPIO2, 0); + mxc_register_gpio(2, MX35_GPIO3_BASE_ADDR, SZ_16K, MX35_INT_GPIO3, 0); } diff --git a/arch/arm/mach-mx5/board-cpuimx51.c b/arch/arm/mach-mx5/board-cpuimx51.c index 4efa02ee163..add0d42de7a 100644 --- a/arch/arm/mach-mx5/board-cpuimx51.c +++ b/arch/arm/mach-mx5/board-cpuimx51.c @@ -245,6 +245,8 @@ __setup("otg_mode=", eukrea_cpuimx51_otg_mode); */ static void __init eukrea_cpuimx51_init(void) { + imx51_soc_init(); + mxc_iomux_v3_setup_multiple_pads(eukrea_cpuimx51_pads, ARRAY_SIZE(eukrea_cpuimx51_pads)); diff --git a/arch/arm/mach-mx5/board-cpuimx51sd.c b/arch/arm/mach-mx5/board-cpuimx51sd.c index 5ef25a59614..ff096d58729 100644 --- a/arch/arm/mach-mx5/board-cpuimx51sd.c +++ b/arch/arm/mach-mx5/board-cpuimx51sd.c @@ -264,6 +264,8 @@ static struct platform_device *platform_devices[] __initdata = { static void __init eukrea_cpuimx51sd_init(void) { + imx51_soc_init(); + mxc_iomux_v3_setup_multiple_pads(eukrea_cpuimx51sd_pads, ARRAY_SIZE(eukrea_cpuimx51sd_pads)); diff --git a/arch/arm/mach-mx5/board-mx50_rdp.c b/arch/arm/mach-mx5/board-mx50_rdp.c index 11210e1ae42..7de25c6712e 100644 --- a/arch/arm/mach-mx5/board-mx50_rdp.c +++ b/arch/arm/mach-mx5/board-mx50_rdp.c @@ -192,6 +192,8 @@ static const struct imxi2c_platform_data i2c_data __initconst = { */ static void __init mx50_rdp_board_init(void) { + imx50_soc_init(); + mxc_iomux_v3_setup_multiple_pads(mx50_rdp_pads, ARRAY_SIZE(mx50_rdp_pads)); diff --git a/arch/arm/mach-mx5/board-mx51_3ds.c b/arch/arm/mach-mx5/board-mx51_3ds.c index 63dfbeafbc1..3112d15feeb 100644 --- a/arch/arm/mach-mx5/board-mx51_3ds.c +++ b/arch/arm/mach-mx5/board-mx51_3ds.c @@ -135,6 +135,8 @@ static struct spi_board_info mx51_3ds_spi_nor_device[] = { */ static void __init mx51_3ds_init(void) { + imx51_soc_init(); + mxc_iomux_v3_setup_multiple_pads(mx51_3ds_pads, ARRAY_SIZE(mx51_3ds_pads)); diff --git a/arch/arm/mach-mx5/board-mx51_babbage.c b/arch/arm/mach-mx5/board-mx51_babbage.c index c7b3fabf50f..6021dd00ec7 100644 --- a/arch/arm/mach-mx5/board-mx51_babbage.c +++ b/arch/arm/mach-mx5/board-mx51_babbage.c @@ -340,6 +340,8 @@ static void __init mx51_babbage_init(void) iomux_v3_cfg_t power_key = _MX51_PAD_EIM_A27__GPIO2_21 | MUX_PAD_CTRL(PAD_CTL_SRE_FAST | PAD_CTL_DSE_HIGH | PAD_CTL_PUS_100K_UP); + imx51_soc_init(); + #if defined(CONFIG_CPU_FREQ_IMX) get_cpu_op = mx51_get_cpu_op; #endif diff --git a/arch/arm/mach-mx5/board-mx51_efikamx.c b/arch/arm/mach-mx5/board-mx51_efikamx.c index 6e362315291..3be603b9075 100644 --- a/arch/arm/mach-mx5/board-mx51_efikamx.c +++ b/arch/arm/mach-mx5/board-mx51_efikamx.c @@ -236,6 +236,8 @@ late_initcall(mx51_efikamx_power_init); static void __init mx51_efikamx_init(void) { + imx51_soc_init(); + mxc_iomux_v3_setup_multiple_pads(mx51efikamx_pads, ARRAY_SIZE(mx51efikamx_pads)); efika_board_common_init(); diff --git a/arch/arm/mach-mx5/board-mx51_efikasb.c b/arch/arm/mach-mx5/board-mx51_efikasb.c index 474fc6e4c6d..4b2e522de0f 100644 --- a/arch/arm/mach-mx5/board-mx51_efikasb.c +++ b/arch/arm/mach-mx5/board-mx51_efikasb.c @@ -248,6 +248,8 @@ static void __init mx51_efikasb_board_id(void) static void __init efikasb_board_init(void) { + imx51_soc_init(); + mxc_iomux_v3_setup_multiple_pads(mx51efikasb_pads, ARRAY_SIZE(mx51efikasb_pads)); efika_board_common_init(); diff --git a/arch/arm/mach-mx5/board-mx53_evk.c b/arch/arm/mach-mx5/board-mx53_evk.c index f87d571882c..0d9218a6e2d 100644 --- a/arch/arm/mach-mx5/board-mx53_evk.c +++ b/arch/arm/mach-mx5/board-mx53_evk.c @@ -117,6 +117,8 @@ static const struct spi_imx_master mx53_evk_spi_data __initconst = { static void __init mx53_evk_board_init(void) { + imx53_soc_init(); + mxc_iomux_v3_setup_multiple_pads(mx53_evk_pads, ARRAY_SIZE(mx53_evk_pads)); mx53_evk_init_uart(); diff --git a/arch/arm/mach-mx5/board-mx53_loco.c b/arch/arm/mach-mx5/board-mx53_loco.c index 1b947e8c9c0..359c3e248ad 100644 --- a/arch/arm/mach-mx5/board-mx53_loco.c +++ b/arch/arm/mach-mx5/board-mx53_loco.c @@ -227,6 +227,8 @@ static const struct imxi2c_platform_data mx53_loco_i2c_data __initconst = { static void __init mx53_loco_board_init(void) { + imx53_soc_init(); + mxc_iomux_v3_setup_multiple_pads(mx53_loco_pads, ARRAY_SIZE(mx53_loco_pads)); imx53_add_imx_uart(0, NULL); diff --git a/arch/arm/mach-mx5/board-mx53_smd.c b/arch/arm/mach-mx5/board-mx53_smd.c index 817c08938f5..bc02894eafe 100644 --- a/arch/arm/mach-mx5/board-mx53_smd.c +++ b/arch/arm/mach-mx5/board-mx53_smd.c @@ -113,6 +113,8 @@ static const struct imxi2c_platform_data mx53_smd_i2c_data __initconst = { static void __init mx53_smd_board_init(void) { + imx53_soc_init(); + mxc_iomux_v3_setup_multiple_pads(mx53_smd_pads, ARRAY_SIZE(mx53_smd_pads)); mx53_smd_init_uart(); diff --git a/arch/arm/mach-mx5/devices.c b/arch/arm/mach-mx5/devices.c index 153ada53e57..371ca8c8414 100644 --- a/arch/arm/mach-mx5/devices.c +++ b/arch/arm/mach-mx5/devices.c @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -119,66 +118,3 @@ struct platform_device mxc_usbh2_device = { .coherent_dma_mask = DMA_BIT_MASK(32), }, }; - -static struct mxc_gpio_port mxc_gpio_ports[] = { - { - .chip.label = "gpio-0", - .base = MX51_IO_ADDRESS(MX51_GPIO1_BASE_ADDR), - .irq = MX51_MXC_INT_GPIO1_LOW, - .irq_high = MX51_MXC_INT_GPIO1_HIGH, - .virtual_irq_start = MXC_GPIO_IRQ_START - }, - { - .chip.label = "gpio-1", - .base = MX51_IO_ADDRESS(MX51_GPIO2_BASE_ADDR), - .irq = MX51_MXC_INT_GPIO2_LOW, - .irq_high = MX51_MXC_INT_GPIO2_HIGH, - .virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 1 - }, - { - .chip.label = "gpio-2", - .base = MX51_IO_ADDRESS(MX51_GPIO3_BASE_ADDR), - .irq = MX51_MXC_INT_GPIO3_LOW, - .irq_high = MX51_MXC_INT_GPIO3_HIGH, - .virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 2 - }, - { - .chip.label = "gpio-3", - .base = MX51_IO_ADDRESS(MX51_GPIO4_BASE_ADDR), - .irq = MX51_MXC_INT_GPIO4_LOW, - .irq_high = MX51_MXC_INT_GPIO4_HIGH, - .virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 3 - }, - { - .chip.label = "gpio-4", - .base = MX53_IO_ADDRESS(MX53_GPIO5_BASE_ADDR), - .irq = MX53_INT_GPIO5_LOW, - .irq_high = MX53_INT_GPIO5_HIGH, - .virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 4 - }, - { - .chip.label = "gpio-5", - .base = MX53_IO_ADDRESS(MX53_GPIO6_BASE_ADDR), - .irq = MX53_INT_GPIO6_LOW, - .irq_high = MX53_INT_GPIO6_HIGH, - .virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 5 - }, - { - .chip.label = "gpio-6", - .base = MX53_IO_ADDRESS(MX53_GPIO7_BASE_ADDR), - .irq = MX53_INT_GPIO7_LOW, - .irq_high = MX53_INT_GPIO7_HIGH, - .virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 6 - }, -}; - -int __init imx51_register_gpios(void) -{ - return mxc_gpio_init(mxc_gpio_ports, 4); -} - -int __init imx53_register_gpios(void) -{ - return mxc_gpio_init(mxc_gpio_ports, ARRAY_SIZE(mxc_gpio_ports)); -} - diff --git a/arch/arm/mach-mx5/mm-mx50.c b/arch/arm/mach-mx5/mm-mx50.c index b9c363b514a..28c3f60f734 100644 --- a/arch/arm/mach-mx5/mm-mx50.c +++ b/arch/arm/mach-mx5/mm-mx50.c @@ -26,7 +26,6 @@ #include #include #include -#include #include /* @@ -56,17 +55,17 @@ void __init imx50_init_early(void) mxc_arch_reset_init(MX50_IO_ADDRESS(MX50_WDOG_BASE_ADDR)); } -static struct mxc_gpio_port imx50_gpio_ports[] = { - DEFINE_IMX_GPIO_PORT_IRQ_HIGH(MX50, 0, 1, MX50_INT_GPIO1_LOW, MX50_INT_GPIO1_HIGH), - DEFINE_IMX_GPIO_PORT_IRQ_HIGH(MX50, 1, 2, MX50_INT_GPIO2_LOW, MX50_INT_GPIO2_HIGH), - DEFINE_IMX_GPIO_PORT_IRQ_HIGH(MX50, 2, 3, MX50_INT_GPIO3_LOW, MX50_INT_GPIO3_HIGH), - DEFINE_IMX_GPIO_PORT_IRQ_HIGH(MX50, 3, 4, MX50_INT_GPIO3_LOW, MX50_INT_GPIO3_HIGH), - DEFINE_IMX_GPIO_PORT_IRQ_HIGH(MX50, 4, 5, MX50_INT_GPIO3_LOW, MX50_INT_GPIO3_HIGH), - DEFINE_IMX_GPIO_PORT_IRQ_HIGH(MX50, 5, 6, MX50_INT_GPIO3_LOW, MX50_INT_GPIO3_HIGH), -}; - void __init mx50_init_irq(void) { tzic_init_irq(MX50_IO_ADDRESS(MX50_TZIC_BASE_ADDR)); - mxc_gpio_init(imx50_gpio_ports, ARRAY_SIZE(imx50_gpio_ports)); +} + +void __init imx50_soc_init(void) +{ + mxc_register_gpio(0, MX50_GPIO1_BASE_ADDR, SZ_16K, MX50_INT_GPIO1_LOW, MX50_INT_GPIO1_HIGH); + mxc_register_gpio(1, MX50_GPIO2_BASE_ADDR, SZ_16K, MX50_INT_GPIO2_LOW, MX50_INT_GPIO2_HIGH); + mxc_register_gpio(2, MX50_GPIO3_BASE_ADDR, SZ_16K, MX50_INT_GPIO3_LOW, MX50_INT_GPIO3_HIGH); + mxc_register_gpio(3, MX50_GPIO4_BASE_ADDR, SZ_16K, MX50_INT_GPIO4_LOW, MX50_INT_GPIO4_HIGH); + mxc_register_gpio(4, MX50_GPIO5_BASE_ADDR, SZ_16K, MX50_INT_GPIO5_LOW, MX50_INT_GPIO5_HIGH); + mxc_register_gpio(5, MX50_GPIO6_BASE_ADDR, SZ_16K, MX50_INT_GPIO6_LOW, MX50_INT_GPIO6_HIGH); } diff --git a/arch/arm/mach-mx5/mm.c b/arch/arm/mach-mx5/mm.c index ff557301b42..800bb8b2108 100644 --- a/arch/arm/mach-mx5/mm.c +++ b/arch/arm/mach-mx5/mm.c @@ -69,8 +69,6 @@ void __init imx53_init_early(void) mxc_arch_reset_init(MX53_IO_ADDRESS(MX53_WDOG1_BASE_ADDR)); } -int imx51_register_gpios(void); - void __init mx51_init_irq(void) { unsigned long tzic_addr; @@ -86,11 +84,8 @@ void __init mx51_init_irq(void) panic("unable to map TZIC interrupt controller\n"); tzic_init_irq(tzic_virt); - imx51_register_gpios(); } -int imx53_register_gpios(void); - void __init mx53_init_irq(void) { unsigned long tzic_addr; @@ -103,5 +98,23 @@ void __init mx53_init_irq(void) panic("unable to map TZIC interrupt controller\n"); tzic_init_irq(tzic_virt); - imx53_register_gpios(); +} + +void __init imx51_soc_init(void) +{ + mxc_register_gpio(0, MX51_GPIO1_BASE_ADDR, SZ_16K, MX51_MXC_INT_GPIO1_LOW, MX51_MXC_INT_GPIO1_HIGH); + mxc_register_gpio(1, MX51_GPIO2_BASE_ADDR, SZ_16K, MX51_MXC_INT_GPIO2_LOW, MX51_MXC_INT_GPIO2_HIGH); + mxc_register_gpio(2, MX51_GPIO3_BASE_ADDR, SZ_16K, MX51_MXC_INT_GPIO3_LOW, MX51_MXC_INT_GPIO3_HIGH); + mxc_register_gpio(3, MX51_GPIO4_BASE_ADDR, SZ_16K, MX51_MXC_INT_GPIO4_LOW, MX51_MXC_INT_GPIO4_HIGH); +} + +void __init imx53_soc_init(void) +{ + mxc_register_gpio(0, MX53_GPIO1_BASE_ADDR, SZ_16K, MX53_INT_GPIO1_LOW, MX53_INT_GPIO1_HIGH); + mxc_register_gpio(1, MX53_GPIO2_BASE_ADDR, SZ_16K, MX53_INT_GPIO2_LOW, MX53_INT_GPIO2_HIGH); + mxc_register_gpio(2, MX53_GPIO3_BASE_ADDR, SZ_16K, MX53_INT_GPIO3_LOW, MX53_INT_GPIO3_HIGH); + mxc_register_gpio(3, MX53_GPIO4_BASE_ADDR, SZ_16K, MX53_INT_GPIO4_LOW, MX53_INT_GPIO4_HIGH); + mxc_register_gpio(4, MX53_GPIO5_BASE_ADDR, SZ_16K, MX53_INT_GPIO5_LOW, MX53_INT_GPIO5_HIGH); + mxc_register_gpio(5, MX53_GPIO6_BASE_ADDR, SZ_16K, MX53_INT_GPIO6_LOW, MX53_INT_GPIO6_HIGH); + mxc_register_gpio(6, MX53_GPIO7_BASE_ADDR, SZ_16K, MX53_INT_GPIO7_LOW, MX53_INT_GPIO7_HIGH); } diff --git a/arch/arm/plat-mxc/devices.c b/arch/arm/plat-mxc/devices.c index eee1b6096a0..fb166b20f60 100644 --- a/arch/arm/plat-mxc/devices.c +++ b/arch/arm/plat-mxc/devices.c @@ -89,3 +89,14 @@ err: return pdev; } + +struct device mxc_aips_bus = { + .init_name = "mxc_aips", + .parent = &platform_bus, +}; + +static int __init mxc_device_init(void) +{ + return device_register(&mxc_aips_bus); +} +core_initcall(mxc_device_init); diff --git a/arch/arm/plat-mxc/devices/Makefile b/arch/arm/plat-mxc/devices/Makefile index ad2922acf48..b41bf972b54 100644 --- a/arch/arm/plat-mxc/devices/Makefile +++ b/arch/arm/plat-mxc/devices/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_FEC) += platform-fec.o obj-$(CONFIG_IMX_HAVE_PLATFORM_FLEXCAN) += platform-flexcan.o obj-$(CONFIG_IMX_HAVE_PLATFORM_FSL_USB2_UDC) += platform-fsl-usb2-udc.o obj-$(CONFIG_IMX_HAVE_PLATFORM_GPIO_KEYS) += platform-gpio_keys.o +obj-y += platform-gpio-mxc.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX21_HCD) += platform-imx21-hcd.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX2_WDT) += platform-imx2-wdt.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMXDI_RTC) += platform-imxdi_rtc.o diff --git a/arch/arm/plat-mxc/devices/platform-gpio-mxc.c b/arch/arm/plat-mxc/devices/platform-gpio-mxc.c new file mode 100644 index 00000000000..cf1b7fdfa20 --- /dev/null +++ b/arch/arm/plat-mxc/devices/platform-gpio-mxc.c @@ -0,0 +1,32 @@ +/* + * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2011 Linaro Limited + * + * 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 + +struct platform_device *__init mxc_register_gpio(int id, + resource_size_t iobase, resource_size_t iosize, int irq, int irq_high) +{ + struct resource res[] = { + { + .start = iobase, + .end = iobase + iosize - 1, + .flags = IORESOURCE_MEM, + }, { + .start = irq, + .end = irq, + .flags = IORESOURCE_IRQ, + }, { + .start = irq_high, + .end = irq_high, + .flags = IORESOURCE_IRQ, + }, + }; + + return platform_device_register_resndata(&mxc_aips_bus, + "gpio-mxc", id, res, ARRAY_SIZE(res), NULL, 0); +} diff --git a/arch/arm/plat-mxc/include/mach/common.h b/arch/arm/plat-mxc/include/mach/common.h index da7991832af..91fa2632aa5 100644 --- a/arch/arm/plat-mxc/include/mach/common.h +++ b/arch/arm/plat-mxc/include/mach/common.h @@ -43,6 +43,15 @@ extern void mx35_init_irq(void); extern void mx50_init_irq(void); extern void mx51_init_irq(void); extern void mx53_init_irq(void); +extern void imx1_soc_init(void); +extern void imx21_soc_init(void); +extern void imx25_soc_init(void); +extern void imx27_soc_init(void); +extern void imx31_soc_init(void); +extern void imx35_soc_init(void); +extern void imx50_soc_init(void); +extern void imx51_soc_init(void); +extern void imx53_soc_init(void); extern void epit_timer_init(struct clk *timer_clk, void __iomem *base, int irq); extern void mxc_timer_init(struct clk *timer_clk, void __iomem *, int); extern int mx1_clocks_init(unsigned long fref); @@ -55,7 +64,8 @@ extern int mx51_clocks_init(unsigned long ckil, unsigned long osc, unsigned long ckih1, unsigned long ckih2); extern int mx53_clocks_init(unsigned long ckil, unsigned long osc, unsigned long ckih1, unsigned long ckih2); -extern int mxc_register_gpios(void); +extern struct platform_device *mxc_register_gpio(int id, + resource_size_t iobase, resource_size_t iosize, int irq, int irq_high); extern int mxc_register_device(struct platform_device *pdev, void *data); extern void mxc_set_cpu_type(unsigned int type); extern void mxc_arch_reset_init(void __iomem *); diff --git a/arch/arm/plat-mxc/include/mach/devices-common.h b/arch/arm/plat-mxc/include/mach/devices-common.h index fa8477337f9..03f62664537 100644 --- a/arch/arm/plat-mxc/include/mach/devices-common.h +++ b/arch/arm/plat-mxc/include/mach/devices-common.h @@ -10,6 +10,8 @@ #include #include +extern struct device mxc_aips_bus; + struct platform_device *imx_add_platform_device_dmamask( const char *name, int id, const struct resource *res, unsigned int num_resources, diff --git a/arch/arm/plat-mxc/include/mach/gpio.h b/arch/arm/plat-mxc/include/mach/gpio.h index a2747f12813..31c820c1b79 100644 --- a/arch/arm/plat-mxc/include/mach/gpio.h +++ b/arch/arm/plat-mxc/include/mach/gpio.h @@ -36,31 +36,4 @@ #define gpio_to_irq(gpio) (MXC_GPIO_IRQ_START + (gpio)) #define irq_to_gpio(irq) ((irq) - MXC_GPIO_IRQ_START) -struct mxc_gpio_port { - void __iomem *base; - int irq; - int irq_high; - int virtual_irq_start; - struct gpio_chip chip; - u32 both_edges; - spinlock_t lock; -}; - -#define DEFINE_IMX_GPIO_PORT_IRQ_HIGH(soc, _id, _hwid, _irq, _irq_high) \ - { \ - .chip.label = "gpio-" #_id, \ - .irq = _irq, \ - .irq_high = _irq_high, \ - .base = soc ## _IO_ADDRESS( \ - soc ## _GPIO ## _hwid ## _BASE_ADDR), \ - .virtual_irq_start = MXC_GPIO_IRQ_START + (_id) * 32, \ - } - -#define DEFINE_IMX_GPIO_PORT_IRQ(soc, _id, _hwid, _irq) \ - DEFINE_IMX_GPIO_PORT_IRQ_HIGH(soc, _id, _hwid, _irq, 0) -#define DEFINE_IMX_GPIO_PORT(soc, _id, _hwid) \ - DEFINE_IMX_GPIO_PORT_IRQ(soc, _id, _hwid, 0) - -int mxc_gpio_init(struct mxc_gpio_port*, int); - #endif diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 6cd6d7f686f..844079a83f2 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -24,11 +24,28 @@ #include #include #include +#include +#include #include #include -static struct mxc_gpio_port *mxc_gpio_ports; -static int gpio_table_size; +struct mxc_gpio_port { + struct list_head node; + void __iomem *base; + int irq; + int irq_high; + int virtual_irq_start; + struct gpio_chip chip; + u32 both_edges; + spinlock_t lock; +}; + +/* + * 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()) @@ -50,7 +67,7 @@ static int gpio_table_size; static void _clear_gpio_irqstatus(struct mxc_gpio_port *port, u32 index) { - __raw_writel(1 << index, port->base + GPIO_ISR); + writel(1 << index, port->base + GPIO_ISR); } static void _set_gpio_irqenable(struct mxc_gpio_port *port, u32 index, @@ -58,27 +75,30 @@ static void _set_gpio_irqenable(struct mxc_gpio_port *port, u32 index, { u32 l; - l = __raw_readl(port->base + GPIO_IMR); + l = readl(port->base + GPIO_IMR); l = (l & (~(1 << index))) | (!!enable << index); - __raw_writel(l, port->base + GPIO_IMR); + writel(l, port->base + GPIO_IMR); } static void gpio_ack_irq(struct irq_data *d) { + struct mxc_gpio_port *port = irq_data_get_irq_chip_data(d); u32 gpio = irq_to_gpio(d->irq); - _clear_gpio_irqstatus(&mxc_gpio_ports[gpio / 32], gpio & 0x1f); + _clear_gpio_irqstatus(port, gpio & 0x1f); } static void gpio_mask_irq(struct irq_data *d) { + struct mxc_gpio_port *port = irq_data_get_irq_chip_data(d); u32 gpio = irq_to_gpio(d->irq); - _set_gpio_irqenable(&mxc_gpio_ports[gpio / 32], gpio & 0x1f, 0); + _set_gpio_irqenable(port, gpio & 0x1f, 0); } static void gpio_unmask_irq(struct irq_data *d) { + struct mxc_gpio_port *port = irq_data_get_irq_chip_data(d); u32 gpio = irq_to_gpio(d->irq); - _set_gpio_irqenable(&mxc_gpio_ports[gpio / 32], gpio & 0x1f, 1); + _set_gpio_irqenable(port, gpio & 0x1f, 1); } static int mxc_gpio_get(struct gpio_chip *chip, unsigned offset); @@ -86,7 +106,7 @@ static int mxc_gpio_get(struct gpio_chip *chip, unsigned offset); static int gpio_set_irq_type(struct irq_data *d, u32 type) { u32 gpio = irq_to_gpio(d->irq); - struct mxc_gpio_port *port = &mxc_gpio_ports[gpio / 32]; + struct mxc_gpio_port *port = irq_data_get_irq_chip_data(d); u32 bit, val; int edge; void __iomem *reg = port->base; @@ -122,8 +142,8 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type) reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */ bit = gpio & 0xf; - val = __raw_readl(reg) & ~(0x3 << (bit << 1)); - __raw_writel(val | (edge << (bit << 1)), reg); + val = readl(reg) & ~(0x3 << (bit << 1)); + writel(val | (edge << (bit << 1)), reg); _clear_gpio_irqstatus(port, gpio & 0x1f); return 0; @@ -137,7 +157,7 @@ static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio) reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */ bit = gpio & 0xf; - val = __raw_readl(reg); + val = readl(reg); edge = (val >> (bit << 1)) & 3; val &= ~(0x3 << (bit << 1)); if (edge == GPIO_INT_HIGH_LEV) { @@ -151,7 +171,7 @@ static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio) gpio, edge); return; } - __raw_writel(val | (edge << (bit << 1)), reg); + writel(val | (edge << (bit << 1)), reg); } /* handle 32 interrupts in one status register */ @@ -177,8 +197,7 @@ 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 = __raw_readl(port->base + GPIO_ISR) & - __raw_readl(port->base + GPIO_IMR); + irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR); mxc_gpio_irq_handler(port, irq_stat); } @@ -186,19 +205,18 @@ static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc) /* MX2 has one interrupt *for all* gpio ports */ static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc) { - int i; u32 irq_msk, irq_stat; - struct mxc_gpio_port *port = irq_get_handler_data(irq); + struct mxc_gpio_port *port; /* walk through all interrupt status registers */ - for (i = 0; i < gpio_table_size; i++) { - irq_msk = __raw_readl(port[i].base + GPIO_IMR); + list_for_each_entry(port, &mxc_gpio_ports, node) { + irq_msk = readl(port->base + GPIO_IMR); if (!irq_msk) continue; - irq_stat = __raw_readl(port[i].base + GPIO_ISR) & irq_msk; + irq_stat = readl(port->base + GPIO_ISR) & irq_msk; if (irq_stat) - mxc_gpio_irq_handler(&port[i], irq_stat); + mxc_gpio_irq_handler(port, irq_stat); } } @@ -215,7 +233,7 @@ 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 mxc_gpio_port *port = &mxc_gpio_ports[gpio / 32]; + struct mxc_gpio_port *port = irq_data_get_irq_chip_data(d); if (enable) { if (port->irq_high && (gpio_idx >= 16)) @@ -250,12 +268,12 @@ static void _set_gpio_direction(struct gpio_chip *chip, unsigned offset, unsigned long flags; spin_lock_irqsave(&port->lock, flags); - l = __raw_readl(port->base + GPIO_GDIR); + l = readl(port->base + GPIO_GDIR); if (dir) l |= 1 << offset; else l &= ~(1 << offset); - __raw_writel(l, port->base + GPIO_GDIR); + writel(l, port->base + GPIO_GDIR); spin_unlock_irqrestore(&port->lock, flags); } @@ -268,8 +286,8 @@ static void mxc_gpio_set(struct gpio_chip *chip, unsigned offset, int value) unsigned long flags; spin_lock_irqsave(&port->lock, flags); - l = (__raw_readl(reg) & (~(1 << offset))) | (!!value << offset); - __raw_writel(l, reg); + l = (readl(reg) & (~(1 << offset))) | (!!value << offset); + writel(l, reg); spin_unlock_irqrestore(&port->lock, flags); } @@ -278,7 +296,7 @@ static int mxc_gpio_get(struct gpio_chip *chip, unsigned offset) struct mxc_gpio_port *port = container_of(chip, struct mxc_gpio_port, chip); - return (__raw_readl(port->base + GPIO_PSR) >> offset) & 1; + return (readl(port->base + GPIO_PSR) >> offset) & 1; } static int mxc_gpio_direction_input(struct gpio_chip *chip, unsigned offset) @@ -301,61 +319,116 @@ static int mxc_gpio_direction_output(struct gpio_chip *chip, */ static struct lock_class_key gpio_lock_class; -int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt) +static int __devinit mxc_gpio_probe(struct platform_device *pdev) { - int i, j; - - /* save for local usage */ - mxc_gpio_ports = port; - gpio_table_size = cnt; - - printk(KERN_INFO "MXC GPIO hardware\n"); - - for (i = 0; i < cnt; i++) { - /* disable the interrupt and clear the status */ - __raw_writel(0, port[i].base + GPIO_IMR); - __raw_writel(~0, port[i].base + GPIO_ISR); - for (j = port[i].virtual_irq_start; - j < port[i].virtual_irq_start + 32; j++) { - irq_set_lockdep_class(j, &gpio_lock_class); - irq_set_chip_and_handler(j, &gpio_irq_chip, - handle_level_irq); - set_irq_flags(j, IRQF_VALID); - } + struct mxc_gpio_port *port; + struct resource *iores; + int err, i; + + port = kzalloc(sizeof(struct mxc_gpio_port), GFP_KERNEL); + if (!port) + return -ENOMEM; - /* register gpio chip */ - port[i].chip.direction_input = mxc_gpio_direction_input; - port[i].chip.direction_output = mxc_gpio_direction_output; - port[i].chip.get = mxc_gpio_get; - port[i].chip.set = mxc_gpio_set; - port[i].chip.base = i * 32; - port[i].chip.ngpio = 32; + port->virtual_irq_start = MXC_GPIO_IRQ_START + pdev->id * 32; - spin_lock_init(&port[i].lock); + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iores) { + err = -ENODEV; + goto out_kfree; + } - /* its a serious configuration bug when it fails */ - BUG_ON( gpiochip_add(&port[i].chip) < 0 ); + if (!request_mem_region(iores->start, resource_size(iores), + pdev->name)) { + err = -EBUSY; + goto out_kfree; + } - if (cpu_is_mx1() || cpu_is_mx3() || cpu_is_mx25() || cpu_is_mx51()) { - /* setup one handler for each entry */ - irq_set_chained_handler(port[i].irq, - mx3_gpio_irq_handler); - irq_set_handler_data(port[i].irq, &port[i]); - if (port[i].irq_high) { - /* setup handler for GPIO 16 to 31 */ - irq_set_chained_handler(port[i].irq_high, - mx3_gpio_irq_handler); - irq_set_handler_data(port[i].irq_high, - &port[i]); - } - } + 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); + + for (i = port->virtual_irq_start; + i < port->virtual_irq_start + 32; i++) { + irq_set_lockdep_class(i, &gpio_lock_class); + irq_set_chip_and_handler(i, &gpio_irq_chip, handle_level_irq); + set_irq_flags(i, IRQF_VALID); + irq_set_chip_data(i, port); } if (cpu_is_mx2()) { /* setup one handler for all GPIO interrupts */ - irq_set_chained_handler(port[0].irq, mx2_gpio_irq_handler); - irq_set_handler_data(port[0].irq, port); + 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); + } } + /* register gpio chip */ + port->chip.direction_input = mxc_gpio_direction_input; + port->chip.direction_output = mxc_gpio_direction_output; + port->chip.get = mxc_gpio_get; + port->chip.set = mxc_gpio_set; + port->chip.base = pdev->id * 32; + port->chip.ngpio = 32; + + spin_lock_init(&port->lock); + + err = gpiochip_add(&port->chip); + if (err) + goto out_iounmap; + + list_add_tail(&port->node, &mxc_gpio_ports); + return 0; + +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 , " + "Juergen Beisert "); +MODULE_DESCRIPTION("Freescale MXC GPIO"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 2ce420da39078a6135d1c004a0e4436fdc1458b4 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 6 Jun 2011 13:22:41 +0800 Subject: gpio/mxc: convert gpio-mxc to use basic_mmio_gpio library The gpio-mxc controller complies with basic_mmio_gpio library. The patch convert the driver to use the library. Signed-off-by: Shawn Guo Acked-by: Sascha Hauer Signed-off-by: Grant Likely --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-mxc.c | 83 +++++++++---------------------------------------- 2 files changed, 15 insertions(+), 69 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 1ee725aa021..57d7bc547f4 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -100,6 +100,7 @@ config GPIO_MXS config GPIO_MXC def_bool y depends on ARCH_MXC + select GPIO_BASIC_MMIO_CORE config GPIO_PLAT_SAMSUNG bool "Samsung SoCs GPIO library support" diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 844079a83f2..b351952893b 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -35,9 +36,8 @@ struct mxc_gpio_port { int irq; int irq_high; int virtual_irq_start; - struct gpio_chip chip; + struct bgpio_chip bgc; u32 both_edges; - spinlock_t lock; }; /* @@ -101,8 +101,6 @@ static void gpio_unmask_irq(struct irq_data *d) _set_gpio_irqenable(port, gpio & 0x1f, 1); } -static int mxc_gpio_get(struct gpio_chip *chip, unsigned offset); - static int gpio_set_irq_type(struct irq_data *d, u32 type) { u32 gpio = irq_to_gpio(d->irq); @@ -120,7 +118,7 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type) edge = GPIO_INT_FALL_EDGE; break; case IRQ_TYPE_EDGE_BOTH: - val = mxc_gpio_get(&port->chip, gpio & 31); + val = gpio_get_value(gpio & 31); if (val) { edge = GPIO_INT_LOW_LEV; pr_debug("mxc: set GPIO %d to low trigger\n", gpio); @@ -259,60 +257,6 @@ static struct irq_chip gpio_irq_chip = { .irq_set_wake = gpio_set_wake_irq, }; -static void _set_gpio_direction(struct gpio_chip *chip, unsigned offset, - int dir) -{ - struct mxc_gpio_port *port = - container_of(chip, struct mxc_gpio_port, chip); - u32 l; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - l = readl(port->base + GPIO_GDIR); - if (dir) - l |= 1 << offset; - else - l &= ~(1 << offset); - writel(l, port->base + GPIO_GDIR); - spin_unlock_irqrestore(&port->lock, flags); -} - -static void mxc_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct mxc_gpio_port *port = - container_of(chip, struct mxc_gpio_port, chip); - void __iomem *reg = port->base + GPIO_DR; - u32 l; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - l = (readl(reg) & (~(1 << offset))) | (!!value << offset); - writel(l, reg); - spin_unlock_irqrestore(&port->lock, flags); -} - -static int mxc_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct mxc_gpio_port *port = - container_of(chip, struct mxc_gpio_port, chip); - - return (readl(port->base + GPIO_PSR) >> offset) & 1; -} - -static int mxc_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - _set_gpio_direction(chip, offset, 0); - return 0; -} - -static int mxc_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - mxc_gpio_set(chip, offset, value); - _set_gpio_direction(chip, offset, 1); - return 0; -} - /* * This lock class tells lockdep that GPIO irqs are in a different * category than their parents, so it won't report false recursion. @@ -385,24 +329,25 @@ static int __devinit mxc_gpio_probe(struct platform_device *pdev) } } - /* register gpio chip */ - port->chip.direction_input = mxc_gpio_direction_input; - port->chip.direction_output = mxc_gpio_direction_output; - port->chip.get = mxc_gpio_get; - port->chip.set = mxc_gpio_set; - port->chip.base = pdev->id * 32; - port->chip.ngpio = 32; + 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; - spin_lock_init(&port->lock); + port->bgc.gc.base = pdev->id * 32; - err = gpiochip_add(&port->chip); + err = gpiochip_add(&port->bgc.gc); if (err) - goto out_iounmap; + 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: -- cgit v1.2.3 From c6b52c13de4415ed5774be8c076685715de56096 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 1 Jun 2011 10:09:19 -0700 Subject: gpio/ep93xx: move driver to drivers/gpio The GPIO driver should reside in drivers/gpio. v3: Change Kconfig option to def_bool y v2: Make the Kconfig symbol a silent option, dependent on ARCH_EP93XX Signed-off-by: H Hartley Sweeten Signed-off-by: Grant Likely --- arch/arm/mach-ep93xx/Makefile | 2 +- arch/arm/mach-ep93xx/gpio.c | 410 ------------------------------------------ drivers/gpio/Kconfig | 4 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-ep93xx.c | 410 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 416 insertions(+), 411 deletions(-) delete mode 100644 arch/arm/mach-ep93xx/gpio.c create mode 100644 drivers/gpio/gpio-ep93xx.c diff --git a/arch/arm/mach-ep93xx/Makefile b/arch/arm/mach-ep93xx/Makefile index 33ee2c863d1..6b7c41d155d 100644 --- a/arch/arm/mach-ep93xx/Makefile +++ b/arch/arm/mach-ep93xx/Makefile @@ -1,7 +1,7 @@ # # Makefile for the linux kernel. # -obj-y := core.o clock.o dma-m2p.o gpio.o +obj-y := core.o clock.o dma-m2p.o obj-m := obj-n := obj- := diff --git a/arch/arm/mach-ep93xx/gpio.c b/arch/arm/mach-ep93xx/gpio.c deleted file mode 100644 index 415dce37b88..00000000000 --- a/arch/arm/mach-ep93xx/gpio.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - * linux/arch/arm/mach-ep93xx/gpio.c - * - * Generic EP93xx GPIO handling - * - * Copyright (c) 2008 Ryan Mallon - * - * 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) "ep93xx " KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include - -#include - -/************************************************************************* - * 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, -}; - -void __init 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_chip { - struct gpio_chip chip; - - void __iomem *data_reg; - void __iomem *data_dir_reg; -}; - -#define to_ep93xx_gpio_chip(c) container_of(c, struct ep93xx_gpio_chip, chip) - -static int ep93xx_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip); - unsigned long flags; - u8 v; - - local_irq_save(flags); - v = __raw_readb(ep93xx_chip->data_dir_reg); - v &= ~(1 << offset); - __raw_writeb(v, ep93xx_chip->data_dir_reg); - local_irq_restore(flags); - - return 0; -} - -static int ep93xx_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int val) -{ - struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip); - unsigned long flags; - int line; - u8 v; - - local_irq_save(flags); - - /* Set the value */ - v = __raw_readb(ep93xx_chip->data_reg); - if (val) - v |= (1 << offset); - else - v &= ~(1 << offset); - __raw_writeb(v, ep93xx_chip->data_reg); - - /* Drive as an output */ - line = chip->base + offset; - if (line <= EP93XX_GPIO_LINE_MAX_IRQ) { - /* Ports A/B/F */ - ep93xx_gpio_int_mask(line); - ep93xx_gpio_update_int_params(line >> 3); - } - - v = __raw_readb(ep93xx_chip->data_dir_reg); - v |= (1 << offset); - __raw_writeb(v, ep93xx_chip->data_dir_reg); - - local_irq_restore(flags); - - return 0; -} - -static int ep93xx_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip); - - return !!(__raw_readb(ep93xx_chip->data_reg) & (1 << offset)); -} - -static void ep93xx_gpio_set(struct gpio_chip *chip, unsigned offset, int val) -{ - struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip); - unsigned long flags; - u8 v; - - local_irq_save(flags); - v = __raw_readb(ep93xx_chip->data_reg); - if (val) - v |= (1 << offset); - else - v &= ~(1 << offset); - __raw_writeb(v, ep93xx_chip->data_reg); - local_irq_restore(flags); -} - -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; -} - -#define EP93XX_GPIO_BANK(name, dr, ddr, base_gpio) \ - { \ - .chip = { \ - .label = name, \ - .direction_input = ep93xx_gpio_direction_input, \ - .direction_output = ep93xx_gpio_direction_output, \ - .get = ep93xx_gpio_get, \ - .set = ep93xx_gpio_set, \ - .base = base_gpio, \ - .ngpio = 8, \ - }, \ - .data_reg = EP93XX_GPIO_REG(dr), \ - .data_dir_reg = EP93XX_GPIO_REG(ddr), \ - } - -static struct ep93xx_gpio_chip ep93xx_gpio_banks[] = { - EP93XX_GPIO_BANK("A", 0x00, 0x10, 0), - EP93XX_GPIO_BANK("B", 0x04, 0x14, 8), - EP93XX_GPIO_BANK("C", 0x08, 0x18, 40), - EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 24), - EP93XX_GPIO_BANK("E", 0x20, 0x24, 32), - EP93XX_GPIO_BANK("F", 0x30, 0x34, 16), - EP93XX_GPIO_BANK("G", 0x38, 0x3c, 48), - EP93XX_GPIO_BANK("H", 0x40, 0x44, 56), -}; - -void __init ep93xx_gpio_init(void) -{ - int i; - - /* Set Ports C, D, E, G, and H for GPIO use */ - 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 gpio_chip *chip = &ep93xx_gpio_banks[i].chip; - - /* - * Ports A, B, and F support input debouncing when - * used as interrupts. - */ - if (!strcmp(chip->label, "A") || - !strcmp(chip->label, "B") || - !strcmp(chip->label, "F")) - chip->set_debounce = ep93xx_gpio_set_debounce; - - gpiochip_add(chip); - } -} diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 2967002a9f8..21271a5209a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -86,6 +86,10 @@ config GPIO_IT8761E help Say yes here to support GPIO functionality of IT8761E super I/O chip. +config GPIO_EP93XX + def_bool y + depends on ARCH_EP93XX + config GPIO_EXYNOS4 def_bool y depends on CPU_EXYNOS4210 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index b605f8ec6fb..e6e503229c8 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_EP93XX) += gpio-ep93xx.o obj-$(CONFIG_GPIO_EXYNOS4) += gpio-exynos4.o obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o obj-$(CONFIG_GPIO_S5PC100) += gpio-s5pc100.o diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c new file mode 100644 index 00000000000..415dce37b88 --- /dev/null +++ b/drivers/gpio/gpio-ep93xx.c @@ -0,0 +1,410 @@ +/* + * linux/arch/arm/mach-ep93xx/gpio.c + * + * Generic EP93xx GPIO handling + * + * Copyright (c) 2008 Ryan Mallon + * + * 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) "ep93xx " KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include + +/************************************************************************* + * 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, +}; + +void __init 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_chip { + struct gpio_chip chip; + + void __iomem *data_reg; + void __iomem *data_dir_reg; +}; + +#define to_ep93xx_gpio_chip(c) container_of(c, struct ep93xx_gpio_chip, chip) + +static int ep93xx_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip); + unsigned long flags; + u8 v; + + local_irq_save(flags); + v = __raw_readb(ep93xx_chip->data_dir_reg); + v &= ~(1 << offset); + __raw_writeb(v, ep93xx_chip->data_dir_reg); + local_irq_restore(flags); + + return 0; +} + +static int ep93xx_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int val) +{ + struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip); + unsigned long flags; + int line; + u8 v; + + local_irq_save(flags); + + /* Set the value */ + v = __raw_readb(ep93xx_chip->data_reg); + if (val) + v |= (1 << offset); + else + v &= ~(1 << offset); + __raw_writeb(v, ep93xx_chip->data_reg); + + /* Drive as an output */ + line = chip->base + offset; + if (line <= EP93XX_GPIO_LINE_MAX_IRQ) { + /* Ports A/B/F */ + ep93xx_gpio_int_mask(line); + ep93xx_gpio_update_int_params(line >> 3); + } + + v = __raw_readb(ep93xx_chip->data_dir_reg); + v |= (1 << offset); + __raw_writeb(v, ep93xx_chip->data_dir_reg); + + local_irq_restore(flags); + + return 0; +} + +static int ep93xx_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip); + + return !!(__raw_readb(ep93xx_chip->data_reg) & (1 << offset)); +} + +static void ep93xx_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +{ + struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip); + unsigned long flags; + u8 v; + + local_irq_save(flags); + v = __raw_readb(ep93xx_chip->data_reg); + if (val) + v |= (1 << offset); + else + v &= ~(1 << offset); + __raw_writeb(v, ep93xx_chip->data_reg); + local_irq_restore(flags); +} + +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; +} + +#define EP93XX_GPIO_BANK(name, dr, ddr, base_gpio) \ + { \ + .chip = { \ + .label = name, \ + .direction_input = ep93xx_gpio_direction_input, \ + .direction_output = ep93xx_gpio_direction_output, \ + .get = ep93xx_gpio_get, \ + .set = ep93xx_gpio_set, \ + .base = base_gpio, \ + .ngpio = 8, \ + }, \ + .data_reg = EP93XX_GPIO_REG(dr), \ + .data_dir_reg = EP93XX_GPIO_REG(ddr), \ + } + +static struct ep93xx_gpio_chip ep93xx_gpio_banks[] = { + EP93XX_GPIO_BANK("A", 0x00, 0x10, 0), + EP93XX_GPIO_BANK("B", 0x04, 0x14, 8), + EP93XX_GPIO_BANK("C", 0x08, 0x18, 40), + EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 24), + EP93XX_GPIO_BANK("E", 0x20, 0x24, 32), + EP93XX_GPIO_BANK("F", 0x30, 0x34, 16), + EP93XX_GPIO_BANK("G", 0x38, 0x3c, 48), + EP93XX_GPIO_BANK("H", 0x40, 0x44, 56), +}; + +void __init ep93xx_gpio_init(void) +{ + int i; + + /* Set Ports C, D, E, G, and H for GPIO use */ + 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 gpio_chip *chip = &ep93xx_gpio_banks[i].chip; + + /* + * Ports A, B, and F support input debouncing when + * used as interrupts. + */ + if (!strcmp(chip->label, "A") || + !strcmp(chip->label, "B") || + !strcmp(chip->label, "F")) + chip->set_debounce = ep93xx_gpio_set_debounce; + + gpiochip_add(chip); + } +} -- cgit v1.2.3 From 121a2dd860f8348fb014b660f133a0cb9a16273e Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 1 Jun 2011 10:57:27 +0200 Subject: gpio/pca953x.c: Interrupt pin is active-low The interrupt pin of the PCA953x is active low, and on the rising edge no interrupt should be produced. Signed-off-by: David Jander Signed-off-by: Grant Likely --- drivers/gpio/pca953x.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c index 0451d7ac94a..63022b17014 100644 --- a/drivers/gpio/pca953x.c +++ b/drivers/gpio/pca953x.c @@ -493,7 +493,6 @@ 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, dev_name(&client->dev), chip); if (ret) { -- cgit v1.2.3 From c103de240439dfee24ac50eb99c8be3a30d13323 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sat, 4 Jun 2011 18:38:28 -0600 Subject: gpio: reorganize drivers Sort the gpio makefile and enforce the naming convention gpio-*.c for gpio drivers. v2: cleaned up filenames in Kconfig and comment blocks v3: fixup use of BASIC_MMIO to GENERIC_GPIO for mxc Signed-off-by: Grant Likely --- drivers/gpio/74x164.c | 177 --------- drivers/gpio/Kconfig | 42 +-- drivers/gpio/Makefile | 81 +++-- drivers/gpio/ab8500-gpio.c | 521 -------------------------- drivers/gpio/adp5520-gpio.c | 211 ----------- drivers/gpio/adp5588-gpio.c | 503 ------------------------- drivers/gpio/basic_mmio_gpio.c | 548 ---------------------------- drivers/gpio/bt8xxgpio.c | 348 ------------------ drivers/gpio/cs5535-gpio.c | 401 -------------------- drivers/gpio/gpio-74x164.c | 177 +++++++++ drivers/gpio/gpio-ab8500.c | 521 ++++++++++++++++++++++++++ drivers/gpio/gpio-adp5520.c | 211 +++++++++++ drivers/gpio/gpio-adp5588.c | 503 +++++++++++++++++++++++++ drivers/gpio/gpio-bt8xx.c | 348 ++++++++++++++++++ drivers/gpio/gpio-cs5535.c | 401 ++++++++++++++++++++ drivers/gpio/gpio-ep93xx.c | 2 - drivers/gpio/gpio-exynos4.c | 5 +- drivers/gpio/gpio-generic.c | 548 ++++++++++++++++++++++++++++ drivers/gpio/gpio-it8761e.c | 234 ++++++++++++ drivers/gpio/gpio-janz-ttl.c | 258 +++++++++++++ drivers/gpio/gpio-langwell.c | 458 +++++++++++++++++++++++ drivers/gpio/gpio-max7300.c | 93 +++++ drivers/gpio/gpio-max7301.c | 116 ++++++ drivers/gpio/gpio-max730x.c | 255 +++++++++++++ drivers/gpio/gpio-max732x.c | 714 ++++++++++++++++++++++++++++++++++++ drivers/gpio/gpio-mc33880.c | 197 ++++++++++ drivers/gpio/gpio-mcp23s08.c | 530 +++++++++++++++++++++++++++ drivers/gpio/gpio-ml-ioh.c | 357 ++++++++++++++++++ drivers/gpio/gpio-pca953x.c | 768 +++++++++++++++++++++++++++++++++++++++ drivers/gpio/gpio-pcf857x.c | 369 +++++++++++++++++++ drivers/gpio/gpio-pch.c | 316 ++++++++++++++++ drivers/gpio/gpio-pl061.c | 358 ++++++++++++++++++ drivers/gpio/gpio-plat-samsung.c | 3 +- drivers/gpio/gpio-rdc321x.c | 246 +++++++++++++ drivers/gpio/gpio-s5pc100.c | 5 +- drivers/gpio/gpio-s5pv210.c | 5 +- drivers/gpio/gpio-sch.c | 316 ++++++++++++++++ drivers/gpio/gpio-stmpe.c | 404 ++++++++++++++++++++ drivers/gpio/gpio-sx150x.c | 680 ++++++++++++++++++++++++++++++++++ drivers/gpio/gpio-tc3589x.c | 389 ++++++++++++++++++++ drivers/gpio/gpio-timberdale.c | 379 +++++++++++++++++++ drivers/gpio/gpio-tps65910.c | 100 +++++ drivers/gpio/gpio-twl4030.c | 513 ++++++++++++++++++++++++++ drivers/gpio/gpio-u300.c | 5 +- drivers/gpio/gpio-ucb1400.c | 125 +++++++ drivers/gpio/gpio-vr41xx.c | 585 +++++++++++++++++++++++++++++ drivers/gpio/gpio-vx855.c | 333 +++++++++++++++++ drivers/gpio/gpio-wm831x.c | 317 ++++++++++++++++ drivers/gpio/gpio-wm8350.c | 182 ++++++++++ drivers/gpio/gpio-wm8994.c | 242 ++++++++++++ drivers/gpio/gpio-xilinx.c | 233 ++++++++++++ drivers/gpio/it8761e_gpio.c | 234 ------------ drivers/gpio/janz-ttl.c | 258 ------------- drivers/gpio/langwell_gpio.c | 456 ----------------------- drivers/gpio/max7300.c | 95 ----- drivers/gpio/max7301.c | 118 ------ drivers/gpio/max730x.c | 257 ------------- drivers/gpio/max732x.c | 714 ------------------------------------ drivers/gpio/mc33880.c | 197 ---------- drivers/gpio/mcp23s08.c | 530 --------------------------- drivers/gpio/ml_ioh_gpio.c | 357 ------------------ drivers/gpio/pca953x.c | 768 --------------------------------------- drivers/gpio/pcf857x.c | 369 ------------------- drivers/gpio/pch_gpio.c | 316 ---------------- drivers/gpio/pl061.c | 360 ------------------ drivers/gpio/rdc321x-gpio.c | 246 ------------- drivers/gpio/sch_gpio.c | 316 ---------------- drivers/gpio/stmpe-gpio.c | 404 -------------------- drivers/gpio/sx150x.c | 680 ---------------------------------- drivers/gpio/tc3589x-gpio.c | 389 -------------------- drivers/gpio/timbgpio.c | 379 ------------------- drivers/gpio/tps65910-gpio.c | 100 ----- drivers/gpio/twl4030-gpio.c | 513 -------------------------- drivers/gpio/ucb1400_gpio.c | 125 ------- drivers/gpio/vr41xx_giu.c | 585 ----------------------------- drivers/gpio/vx855_gpio.c | 333 ----------------- drivers/gpio/wm831x-gpio.c | 317 ---------------- drivers/gpio/wm8350-gpiolib.c | 182 ---------- drivers/gpio/wm8994-gpio.c | 242 ------------ drivers/gpio/xilinx_gpio.c | 233 ------------ 80 files changed, 12839 insertions(+), 12867 deletions(-) delete mode 100644 drivers/gpio/74x164.c delete mode 100644 drivers/gpio/ab8500-gpio.c delete mode 100644 drivers/gpio/adp5520-gpio.c delete mode 100644 drivers/gpio/adp5588-gpio.c delete mode 100644 drivers/gpio/basic_mmio_gpio.c delete mode 100644 drivers/gpio/bt8xxgpio.c delete mode 100644 drivers/gpio/cs5535-gpio.c create mode 100644 drivers/gpio/gpio-74x164.c create mode 100644 drivers/gpio/gpio-ab8500.c create mode 100644 drivers/gpio/gpio-adp5520.c create mode 100644 drivers/gpio/gpio-adp5588.c create mode 100644 drivers/gpio/gpio-bt8xx.c create mode 100644 drivers/gpio/gpio-cs5535.c create mode 100644 drivers/gpio/gpio-generic.c create mode 100644 drivers/gpio/gpio-it8761e.c create mode 100644 drivers/gpio/gpio-janz-ttl.c create mode 100644 drivers/gpio/gpio-langwell.c create mode 100644 drivers/gpio/gpio-max7300.c create mode 100644 drivers/gpio/gpio-max7301.c create mode 100644 drivers/gpio/gpio-max730x.c create mode 100644 drivers/gpio/gpio-max732x.c create mode 100644 drivers/gpio/gpio-mc33880.c create mode 100644 drivers/gpio/gpio-mcp23s08.c create mode 100644 drivers/gpio/gpio-ml-ioh.c create mode 100644 drivers/gpio/gpio-pca953x.c create mode 100644 drivers/gpio/gpio-pcf857x.c create mode 100644 drivers/gpio/gpio-pch.c create mode 100644 drivers/gpio/gpio-pl061.c create mode 100644 drivers/gpio/gpio-rdc321x.c create mode 100644 drivers/gpio/gpio-sch.c create mode 100644 drivers/gpio/gpio-stmpe.c create mode 100644 drivers/gpio/gpio-sx150x.c create mode 100644 drivers/gpio/gpio-tc3589x.c create mode 100644 drivers/gpio/gpio-timberdale.c create mode 100644 drivers/gpio/gpio-tps65910.c create mode 100644 drivers/gpio/gpio-twl4030.c create mode 100644 drivers/gpio/gpio-ucb1400.c create mode 100644 drivers/gpio/gpio-vr41xx.c create mode 100644 drivers/gpio/gpio-vx855.c create mode 100644 drivers/gpio/gpio-wm831x.c create mode 100644 drivers/gpio/gpio-wm8350.c create mode 100644 drivers/gpio/gpio-wm8994.c create mode 100644 drivers/gpio/gpio-xilinx.c delete mode 100644 drivers/gpio/it8761e_gpio.c delete mode 100644 drivers/gpio/janz-ttl.c delete mode 100644 drivers/gpio/langwell_gpio.c delete mode 100644 drivers/gpio/max7300.c delete mode 100644 drivers/gpio/max7301.c delete mode 100644 drivers/gpio/max730x.c delete mode 100644 drivers/gpio/max732x.c delete mode 100644 drivers/gpio/mc33880.c delete mode 100644 drivers/gpio/mcp23s08.c delete mode 100644 drivers/gpio/ml_ioh_gpio.c delete mode 100644 drivers/gpio/pca953x.c delete mode 100644 drivers/gpio/pcf857x.c delete mode 100644 drivers/gpio/pch_gpio.c delete mode 100644 drivers/gpio/pl061.c delete mode 100644 drivers/gpio/rdc321x-gpio.c delete mode 100644 drivers/gpio/sch_gpio.c delete mode 100644 drivers/gpio/stmpe-gpio.c delete mode 100644 drivers/gpio/sx150x.c delete mode 100644 drivers/gpio/tc3589x-gpio.c delete mode 100644 drivers/gpio/timbgpio.c delete mode 100644 drivers/gpio/tps65910-gpio.c delete mode 100644 drivers/gpio/twl4030-gpio.c delete mode 100644 drivers/gpio/ucb1400_gpio.c delete mode 100644 drivers/gpio/vr41xx_giu.c delete mode 100644 drivers/gpio/vx855_gpio.c delete mode 100644 drivers/gpio/wm831x-gpio.c delete mode 100644 drivers/gpio/wm8350-gpiolib.c delete mode 100644 drivers/gpio/wm8994-gpio.c delete mode 100644 drivers/gpio/xilinx_gpio.c diff --git a/drivers/gpio/74x164.c b/drivers/gpio/74x164.c deleted file mode 100644 index 7fb60b6bf52..00000000000 --- a/drivers/gpio/74x164.c +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 74Hx164 - Generic serial-in/parallel-out 8-bits shift register GPIO driver - * - * Copyright (C) 2010 Gabor Juhos - * Copyright (C) 2010 Miguel Gaio - * - * 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 - -struct gen_74x164_chip { - struct spi_device *spi; - struct gpio_chip gpio_chip; - struct mutex lock; - u8 port_config; -}; - -static struct gen_74x164_chip *gpio_to_chip(struct gpio_chip *gc) -{ - return container_of(gc, struct gen_74x164_chip, gpio_chip); -} - -static int __gen_74x164_write_config(struct gen_74x164_chip *chip) -{ - return spi_write(chip->spi, - &chip->port_config, sizeof(chip->port_config)); -} - -static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset) -{ - struct gen_74x164_chip *chip = gpio_to_chip(gc); - int ret; - - mutex_lock(&chip->lock); - ret = (chip->port_config >> offset) & 0x1; - mutex_unlock(&chip->lock); - - return ret; -} - -static void gen_74x164_set_value(struct gpio_chip *gc, - unsigned offset, int val) -{ - struct gen_74x164_chip *chip = gpio_to_chip(gc); - - mutex_lock(&chip->lock); - if (val) - chip->port_config |= (1 << offset); - else - chip->port_config &= ~(1 << offset); - - __gen_74x164_write_config(chip); - 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; - struct gen_74x164_chip_platform_data *pdata; - int ret; - - pdata = spi->dev.platform_data; - if (!pdata || !pdata->base) { - dev_dbg(&spi->dev, "incorrect or missing platform data\n"); - return -EINVAL; - } - - /* - * bits_per_word cannot be configured in platform data - */ - spi->bits_per_word = 8; - - ret = spi_setup(spi); - if (ret < 0) - return ret; - - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - - mutex_init(&chip->lock); - - dev_set_drvdata(&spi->dev, chip); - - chip->spi = spi; - - 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 = 8; - chip->gpio_chip.can_sleep = 1; - chip->gpio_chip.dev = &spi->dev; - chip->gpio_chip.owner = THIS_MODULE; - - ret = __gen_74x164_write_config(chip); - if (ret) { - dev_err(&spi->dev, "Failed writing: %d\n", ret); - goto exit_destroy; - } - - ret = gpiochip_add(&chip->gpio_chip); - if (ret) - goto exit_destroy; - - return ret; - -exit_destroy: - dev_set_drvdata(&spi->dev, NULL); - mutex_destroy(&chip->lock); - kfree(chip); - return ret; -} - -static int __devexit gen_74x164_remove(struct spi_device *spi) -{ - struct gen_74x164_chip *chip; - int ret; - - chip = dev_get_drvdata(&spi->dev); - if (chip == NULL) - return -ENODEV; - - dev_set_drvdata(&spi->dev, NULL); - - ret = gpiochip_remove(&chip->gpio_chip); - if (!ret) { - mutex_destroy(&chip->lock); - kfree(chip); - } else - dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n", - ret); - - return ret; -} - -static struct spi_driver gen_74x164_driver = { - .driver = { - .name = "74x164", - .owner = THIS_MODULE, - }, - .probe = gen_74x164_probe, - .remove = __devexit_p(gen_74x164_remove), -}; - -static int __init gen_74x164_init(void) -{ - return spi_register_driver(&gen_74x164_driver); -} -subsys_initcall(gen_74x164_init); - -static void __exit gen_74x164_exit(void) -{ - spi_unregister_driver(&gen_74x164_driver); -} -module_exit(gen_74x164_exit); - -MODULE_AUTHOR("Gabor Juhos "); -MODULE_AUTHOR("Miguel Gaio "); -MODULE_DESCRIPTION("GPIO expander driver for 74X164 8-bits shift register"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f8b6e7d27e4..89a6a34cc51 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,16 +73,11 @@ 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" @@ -94,14 +92,14 @@ config GPIO_EXYNOS4 def_bool y depends on CPU_EXYNOS4210 -config GPIO_MXS - def_bool y - depends on ARCH_MXS - config GPIO_MXC def_bool y depends on ARCH_MXC - select GPIO_BASIC_MMIO_CORE + select GPIO_GENERIC + +config GPIO_MXS + def_bool y + depends on ARCH_MXS config GPIO_PLAT_SAMSUNG def_bool y @@ -150,9 +148,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 @@ -215,9 +210,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 @@ -309,17 +301,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" @@ -441,9 +428,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 @@ -454,7 +438,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 66923cf3ad6..68e3bfd35e0 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -4,50 +4,53 @@ 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_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/ab8500-gpio.c b/drivers/gpio/ab8500-gpio.c deleted file mode 100644 index 970053c89ff..00000000000 --- a/drivers/gpio/ab8500-gpio.c +++ /dev/null @@ -1,521 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2011 - * - * Author: BIBEK BASU - * License terms: GNU General Public License (GPL) version 2 - * - * 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 - -/* - * GPIO registers offset - * Bank: 0x10 - */ -#define AB8500_GPIO_SEL1_REG 0x00 -#define AB8500_GPIO_SEL2_REG 0x01 -#define AB8500_GPIO_SEL3_REG 0x02 -#define AB8500_GPIO_SEL4_REG 0x03 -#define AB8500_GPIO_SEL5_REG 0x04 -#define AB8500_GPIO_SEL6_REG 0x05 - -#define AB8500_GPIO_DIR1_REG 0x10 -#define AB8500_GPIO_DIR2_REG 0x11 -#define AB8500_GPIO_DIR3_REG 0x12 -#define AB8500_GPIO_DIR4_REG 0x13 -#define AB8500_GPIO_DIR5_REG 0x14 -#define AB8500_GPIO_DIR6_REG 0x15 - -#define AB8500_GPIO_OUT1_REG 0x20 -#define AB8500_GPIO_OUT2_REG 0x21 -#define AB8500_GPIO_OUT3_REG 0x22 -#define AB8500_GPIO_OUT4_REG 0x23 -#define AB8500_GPIO_OUT5_REG 0x24 -#define AB8500_GPIO_OUT6_REG 0x25 - -#define AB8500_GPIO_PUD1_REG 0x30 -#define AB8500_GPIO_PUD2_REG 0x31 -#define AB8500_GPIO_PUD3_REG 0x32 -#define AB8500_GPIO_PUD4_REG 0x33 -#define AB8500_GPIO_PUD5_REG 0x34 -#define AB8500_GPIO_PUD6_REG 0x35 - -#define AB8500_GPIO_IN1_REG 0x40 -#define AB8500_GPIO_IN2_REG 0x41 -#define AB8500_GPIO_IN3_REG 0x42 -#define AB8500_GPIO_IN4_REG 0x43 -#define AB8500_GPIO_IN5_REG 0x44 -#define AB8500_GPIO_IN6_REG 0x45 -#define AB8500_GPIO_ALTFUN_REG 0x45 -#define ALTFUN_REG_INDEX 6 -#define AB8500_NUM_GPIO 42 -#define AB8500_NUM_VIR_GPIO_IRQ 16 - -enum ab8500_gpio_action { - NONE, - STARTUP, - SHUTDOWN, - MASK, - UNMASK -}; - -struct ab8500_gpio { - struct gpio_chip chip; - struct ab8500 *parent; - struct device *dev; - struct mutex lock; - u32 irq_base; - enum ab8500_gpio_action irq_action; - u16 rising; - u16 falling; -}; -/** - * to_ab8500_gpio() - get the pointer to ab8500_gpio - * @chip: Member of the structure ab8500_gpio - */ -static inline struct ab8500_gpio *to_ab8500_gpio(struct gpio_chip *chip) -{ - return container_of(chip, struct ab8500_gpio, chip); -} - -static int ab8500_gpio_set_bits(struct gpio_chip *chip, u8 reg, - unsigned offset, int val) -{ - struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip); - u8 pos = offset % 8; - int ret; - - reg = reg + (offset / 8); - ret = abx500_mask_and_set_register_interruptible(ab8500_gpio->dev, - AB8500_MISC, reg, 1 << pos, val << pos); - if (ret < 0) - dev_err(ab8500_gpio->dev, "%s write failed\n", __func__); - return ret; -} -/** - * ab8500_gpio_get() - Get the particular GPIO value - * @chip: Gpio device - * @offset: GPIO number to read - */ -static int ab8500_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip); - u8 mask = 1 << (offset % 8); - u8 reg = AB8500_GPIO_OUT1_REG + (offset / 8); - int ret; - u8 data; - ret = abx500_get_register_interruptible(ab8500_gpio->dev, AB8500_MISC, - reg, &data); - if (ret < 0) { - dev_err(ab8500_gpio->dev, "%s read failed\n", __func__); - return ret; - } - return (data & mask) >> (offset % 8); -} - -static void ab8500_gpio_set(struct gpio_chip *chip, unsigned offset, int val) -{ - struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip); - int ret; - /* Write the data */ - ret = ab8500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, 1); - if (ret < 0) - dev_err(ab8500_gpio->dev, "%s write failed\n", __func__); -} - -static int ab8500_gpio_direction_output(struct gpio_chip *chip, unsigned offset, - int val) -{ - int ret; - /* set direction as output */ - ret = ab8500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, 1); - if (ret < 0) - return ret; - /* disable pull down */ - ret = ab8500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG, offset, 1); - if (ret < 0) - return ret; - /* set the output as 1 or 0 */ - return ab8500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, val); - -} - -static int ab8500_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - /* set the register as input */ - return ab8500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, 0); -} - -static int ab8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - /* - * Only some GPIOs are interrupt capable, and they are - * organized in discontiguous clusters: - * - * GPIO6 to GPIO13 - * GPIO24 and GPIO25 - * GPIO36 to GPIO41 - */ - static struct ab8500_gpio_irq_cluster { - int start; - int end; - } clusters[] = { - {.start = 6, .end = 13}, - {.start = 24, .end = 25}, - {.start = 36, .end = 41}, - }; - struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip); - int base = ab8500_gpio->irq_base; - int i; - - for (i = 0; i < ARRAY_SIZE(clusters); i++) { - struct ab8500_gpio_irq_cluster *cluster = &clusters[i]; - - if (offset >= cluster->start && offset <= cluster->end) - return base + offset - cluster->start; - - /* Advance by the number of gpios in this cluster */ - base += cluster->end - cluster->start + 1; - } - - return -EINVAL; -} - -static struct gpio_chip ab8500gpio_chip = { - .label = "ab8500_gpio", - .owner = THIS_MODULE, - .direction_input = ab8500_gpio_direction_input, - .get = ab8500_gpio_get, - .direction_output = ab8500_gpio_direction_output, - .set = ab8500_gpio_set, - .to_irq = ab8500_gpio_to_irq, -}; - -static unsigned int irq_to_rising(unsigned int irq) -{ - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); - int offset = irq - ab8500_gpio->irq_base; - int new_irq = offset + AB8500_INT_GPIO6R - + ab8500_gpio->parent->irq_base; - return new_irq; -} - -static unsigned int irq_to_falling(unsigned int irq) -{ - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); - int offset = irq - ab8500_gpio->irq_base; - int new_irq = offset + AB8500_INT_GPIO6F - + ab8500_gpio->parent->irq_base; - return new_irq; - -} - -static unsigned int rising_to_irq(unsigned int irq, void *dev) -{ - struct ab8500_gpio *ab8500_gpio = dev; - int offset = irq - AB8500_INT_GPIO6R - - ab8500_gpio->parent->irq_base ; - int new_irq = offset + ab8500_gpio->irq_base; - return new_irq; -} - -static unsigned int falling_to_irq(unsigned int irq, void *dev) -{ - struct ab8500_gpio *ab8500_gpio = dev; - int offset = irq - AB8500_INT_GPIO6F - - ab8500_gpio->parent->irq_base ; - int new_irq = offset + ab8500_gpio->irq_base; - return new_irq; - -} - -/* - * IRQ handler - */ - -static irqreturn_t handle_rising(int irq, void *dev) -{ - - handle_nested_irq(rising_to_irq(irq , dev)); - return IRQ_HANDLED; -} - -static irqreturn_t handle_falling(int irq, void *dev) -{ - - handle_nested_irq(falling_to_irq(irq, dev)); - return IRQ_HANDLED; -} - -static void ab8500_gpio_irq_lock(unsigned int irq) -{ - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); - mutex_lock(&ab8500_gpio->lock); -} - -static void ab8500_gpio_irq_sync_unlock(unsigned int irq) -{ - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); - int offset = irq - ab8500_gpio->irq_base; - bool rising = ab8500_gpio->rising & BIT(offset); - bool falling = ab8500_gpio->falling & BIT(offset); - int ret; - - switch (ab8500_gpio->irq_action) { - case STARTUP: - if (rising) - ret = request_threaded_irq(irq_to_rising(irq), - NULL, handle_rising, - IRQF_TRIGGER_RISING, - "ab8500-gpio-r", ab8500_gpio); - if (falling) - ret = request_threaded_irq(irq_to_falling(irq), - NULL, handle_falling, - IRQF_TRIGGER_FALLING, - "ab8500-gpio-f", ab8500_gpio); - break; - case SHUTDOWN: - if (rising) - free_irq(irq_to_rising(irq), ab8500_gpio); - if (falling) - free_irq(irq_to_falling(irq), ab8500_gpio); - break; - case MASK: - if (rising) - disable_irq(irq_to_rising(irq)); - if (falling) - disable_irq(irq_to_falling(irq)); - break; - case UNMASK: - if (rising) - enable_irq(irq_to_rising(irq)); - if (falling) - enable_irq(irq_to_falling(irq)); - break; - case NONE: - break; - } - ab8500_gpio->irq_action = NONE; - ab8500_gpio->rising &= ~(BIT(offset)); - ab8500_gpio->falling &= ~(BIT(offset)); - mutex_unlock(&ab8500_gpio->lock); -} - - -static void ab8500_gpio_irq_mask(unsigned int irq) -{ - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); - ab8500_gpio->irq_action = MASK; -} - -static void ab8500_gpio_irq_unmask(unsigned int irq) -{ - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); - ab8500_gpio->irq_action = UNMASK; -} - -static int ab8500_gpio_irq_set_type(unsigned int irq, unsigned int type) -{ - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); - int offset = irq - ab8500_gpio->irq_base; - - if (type == IRQ_TYPE_EDGE_BOTH) { - ab8500_gpio->rising = BIT(offset); - ab8500_gpio->falling = BIT(offset); - } else if (type == IRQ_TYPE_EDGE_RISING) { - ab8500_gpio->rising = BIT(offset); - } else { - ab8500_gpio->falling = BIT(offset); - } - return 0; -} - -unsigned int ab8500_gpio_irq_startup(unsigned int irq) -{ - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); - ab8500_gpio->irq_action = STARTUP; - return 0; -} - -void ab8500_gpio_irq_shutdown(unsigned int irq) -{ - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); - ab8500_gpio->irq_action = SHUTDOWN; -} - -static struct irq_chip ab8500_gpio_irq_chip = { - .name = "ab8500-gpio", - .startup = ab8500_gpio_irq_startup, - .shutdown = ab8500_gpio_irq_shutdown, - .bus_lock = ab8500_gpio_irq_lock, - .bus_sync_unlock = ab8500_gpio_irq_sync_unlock, - .mask = ab8500_gpio_irq_mask, - .unmask = ab8500_gpio_irq_unmask, - .set_type = ab8500_gpio_irq_set_type, -}; - -static int ab8500_gpio_irq_init(struct ab8500_gpio *ab8500_gpio) -{ - u32 base = ab8500_gpio->irq_base; - int irq; - - for (irq = base; irq < base + AB8500_NUM_VIR_GPIO_IRQ ; irq++) { - set_irq_chip_data(irq, ab8500_gpio); - set_irq_chip_and_handler(irq, &ab8500_gpio_irq_chip, - handle_simple_irq); - set_irq_nested_thread(irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); -#else - set_irq_noprobe(irq); -#endif - } - - return 0; -} - -static void ab8500_gpio_irq_remove(struct ab8500_gpio *ab8500_gpio) -{ - int base = ab8500_gpio->irq_base; - int irq; - - for (irq = base; irq < base + AB8500_NUM_VIR_GPIO_IRQ; irq++) { -#ifdef CONFIG_ARM - set_irq_flags(irq, 0); -#endif - set_irq_chip_and_handler(irq, NULL, NULL); - set_irq_chip_data(irq, NULL); - } -} - -static int __devinit ab8500_gpio_probe(struct platform_device *pdev) -{ - struct ab8500_platform_data *ab8500_pdata = - dev_get_platdata(pdev->dev.parent); - struct ab8500_gpio_platform_data *pdata; - struct ab8500_gpio *ab8500_gpio; - int ret; - int i; - - pdata = ab8500_pdata->gpio; - if (!pdata) { - dev_err(&pdev->dev, "gpio platform data missing\n"); - return -ENODEV; - } - - ab8500_gpio = kzalloc(sizeof(struct ab8500_gpio), GFP_KERNEL); - if (ab8500_gpio == NULL) { - dev_err(&pdev->dev, "failed to allocate memory\n"); - return -ENOMEM; - } - ab8500_gpio->dev = &pdev->dev; - ab8500_gpio->parent = dev_get_drvdata(pdev->dev.parent); - ab8500_gpio->chip = ab8500gpio_chip; - ab8500_gpio->chip.ngpio = AB8500_NUM_GPIO; - ab8500_gpio->chip.dev = &pdev->dev; - ab8500_gpio->chip.base = pdata->gpio_base; - ab8500_gpio->irq_base = pdata->irq_base; - /* initialize the lock */ - mutex_init(&ab8500_gpio->lock); - /* - * AB8500 core will handle and clear the IRQ - * configre GPIO based on config-reg value. - * These values are for selecting the PINs as - * GPIO or alternate function - */ - for (i = AB8500_GPIO_SEL1_REG; i <= AB8500_GPIO_SEL6_REG; i++) { - ret = abx500_set_register_interruptible(ab8500_gpio->dev, - AB8500_MISC, i, - pdata->config_reg[i]); - if (ret < 0) - goto out_free; - } - ret = abx500_set_register_interruptible(ab8500_gpio->dev, AB8500_MISC, - AB8500_GPIO_ALTFUN_REG, - pdata->config_reg[ALTFUN_REG_INDEX]); - if (ret < 0) - goto out_free; - - ret = ab8500_gpio_irq_init(ab8500_gpio); - if (ret) - goto out_free; - ret = gpiochip_add(&ab8500_gpio->chip); - if (ret) { - dev_err(&pdev->dev, "unable to add gpiochip: %d\n", - ret); - goto out_rem_irq; - } - platform_set_drvdata(pdev, ab8500_gpio); - return 0; - -out_rem_irq: - ab8500_gpio_irq_remove(ab8500_gpio); -out_free: - mutex_destroy(&ab8500_gpio->lock); - kfree(ab8500_gpio); - return ret; -} - -/* - * ab8500_gpio_remove() - remove Ab8500-gpio driver - * @pdev : Platform device registered - */ -static int __devexit ab8500_gpio_remove(struct platform_device *pdev) -{ - struct ab8500_gpio *ab8500_gpio = platform_get_drvdata(pdev); - int ret; - - ret = gpiochip_remove(&ab8500_gpio->chip); - if (ret < 0) { - dev_err(ab8500_gpio->dev, "unable to remove gpiochip: %d\n", - ret); - return ret; - } - - platform_set_drvdata(pdev, NULL); - mutex_destroy(&ab8500_gpio->lock); - kfree(ab8500_gpio); - - return 0; -} - -static struct platform_driver ab8500_gpio_driver = { - .driver = { - .name = "ab8500-gpio", - .owner = THIS_MODULE, - }, - .probe = ab8500_gpio_probe, - .remove = __devexit_p(ab8500_gpio_remove), -}; - -static int __init ab8500_gpio_init(void) -{ - return platform_driver_register(&ab8500_gpio_driver); -} -arch_initcall(ab8500_gpio_init); - -static void __exit ab8500_gpio_exit(void) -{ - platform_driver_unregister(&ab8500_gpio_driver); -} -module_exit(ab8500_gpio_exit); - -MODULE_AUTHOR("BIBEK BASU "); -MODULE_DESCRIPTION("Driver allows to use AB8500 unused pins to be used as GPIO"); -MODULE_ALIAS("AB8500 GPIO driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/adp5520-gpio.c b/drivers/gpio/adp5520-gpio.c deleted file mode 100644 index 9f278153700..00000000000 --- a/drivers/gpio/adp5520-gpio.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * GPIO driver for Analog Devices ADP5520 MFD PMICs - * - * Copyright 2009 Analog Devices Inc. - * - * Licensed under the GPL-2 or later. - */ - -#include -#include -#include -#include -#include -#include - -#include - -struct adp5520_gpio { - struct device *master; - struct gpio_chip gpio_chip; - unsigned char lut[ADP5520_MAXGPIOS]; - unsigned long output; -}; - -static int adp5520_gpio_get_value(struct gpio_chip *chip, unsigned off) -{ - struct adp5520_gpio *dev; - uint8_t reg_val; - - dev = container_of(chip, struct adp5520_gpio, gpio_chip); - - /* - * There are dedicated registers for GPIO IN/OUT. - * Make sure we return the right value, even when configured as output - */ - - if (test_bit(off, &dev->output)) - adp5520_read(dev->master, ADP5520_GPIO_OUT, ®_val); - else - adp5520_read(dev->master, ADP5520_GPIO_IN, ®_val); - - return !!(reg_val & dev->lut[off]); -} - -static void adp5520_gpio_set_value(struct gpio_chip *chip, - unsigned off, int val) -{ - struct adp5520_gpio *dev; - dev = container_of(chip, struct adp5520_gpio, gpio_chip); - - if (val) - adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]); - else - adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]); -} - -static int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off) -{ - struct adp5520_gpio *dev; - dev = container_of(chip, struct adp5520_gpio, gpio_chip); - - clear_bit(off, &dev->output); - - return adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_2, - dev->lut[off]); -} - -static int adp5520_gpio_direction_output(struct gpio_chip *chip, - unsigned off, int val) -{ - struct adp5520_gpio *dev; - int ret = 0; - dev = container_of(chip, struct adp5520_gpio, gpio_chip); - - set_bit(off, &dev->output); - - if (val) - ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, - dev->lut[off]); - else - ret |= adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, - dev->lut[off]); - - ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_2, - dev->lut[off]); - - return ret; -} - -static int __devinit adp5520_gpio_probe(struct platform_device *pdev) -{ - struct adp5520_gpio_platform_data *pdata = pdev->dev.platform_data; - struct adp5520_gpio *dev; - struct gpio_chip *gc; - int ret, i, gpios; - unsigned char ctl_mask = 0; - - if (pdata == NULL) { - dev_err(&pdev->dev, "missing platform data\n"); - return -ENODEV; - } - - if (pdev->id != ID_ADP5520) { - dev_err(&pdev->dev, "only ADP5520 supports GPIO\n"); - return -ENODEV; - } - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) { - dev_err(&pdev->dev, "failed to alloc memory\n"); - return -ENOMEM; - } - - dev->master = pdev->dev.parent; - - for (gpios = 0, i = 0; i < ADP5520_MAXGPIOS; i++) - if (pdata->gpio_en_mask & (1 << i)) - dev->lut[gpios++] = 1 << i; - - if (gpios < 1) { - ret = -EINVAL; - goto err; - } - - gc = &dev->gpio_chip; - gc->direction_input = adp5520_gpio_direction_input; - gc->direction_output = adp5520_gpio_direction_output; - gc->get = adp5520_gpio_get_value; - gc->set = adp5520_gpio_set_value; - gc->can_sleep = 1; - - gc->base = pdata->gpio_start; - gc->ngpio = gpios; - gc->label = pdev->name; - gc->owner = THIS_MODULE; - - ret = adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_1, - pdata->gpio_en_mask); - - if (pdata->gpio_en_mask & ADP5520_GPIO_C3) - ctl_mask |= ADP5520_C3_MODE; - - if (pdata->gpio_en_mask & ADP5520_GPIO_R3) - ctl_mask |= ADP5520_R3_MODE; - - if (ctl_mask) - ret = adp5520_set_bits(dev->master, ADP5520_LED_CONTROL, - ctl_mask); - - ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP, - pdata->gpio_pullup_mask); - - if (ret) { - dev_err(&pdev->dev, "failed to write\n"); - goto err; - } - - ret = gpiochip_add(&dev->gpio_chip); - if (ret) - goto err; - - platform_set_drvdata(pdev, dev); - return 0; - -err: - kfree(dev); - return ret; -} - -static int __devexit adp5520_gpio_remove(struct platform_device *pdev) -{ - struct adp5520_gpio *dev; - int ret; - - dev = platform_get_drvdata(pdev); - ret = gpiochip_remove(&dev->gpio_chip); - if (ret) { - dev_err(&pdev->dev, "%s failed, %d\n", - "gpiochip_remove()", ret); - return ret; - } - - kfree(dev); - return 0; -} - -static struct platform_driver adp5520_gpio_driver = { - .driver = { - .name = "adp5520-gpio", - .owner = THIS_MODULE, - }, - .probe = adp5520_gpio_probe, - .remove = __devexit_p(adp5520_gpio_remove), -}; - -static int __init adp5520_gpio_init(void) -{ - return platform_driver_register(&adp5520_gpio_driver); -} -module_init(adp5520_gpio_init); - -static void __exit adp5520_gpio_exit(void) -{ - platform_driver_unregister(&adp5520_gpio_driver); -} -module_exit(adp5520_gpio_exit); - -MODULE_AUTHOR("Michael Hennerich "); -MODULE_DESCRIPTION("GPIO ADP5520 Driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:adp5520-gpio"); diff --git a/drivers/gpio/adp5588-gpio.c b/drivers/gpio/adp5588-gpio.c deleted file mode 100644 index 3525ad91877..00000000000 --- a/drivers/gpio/adp5588-gpio.c +++ /dev/null @@ -1,503 +0,0 @@ -/* - * GPIO Chip driver for Analog Devices - * ADP5588/ADP5587 I/O Expander and QWERTY Keypad Controller - * - * Copyright 2009-2010 Analog Devices Inc. - * - * Licensed under the GPL-2 or later. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define DRV_NAME "adp5588-gpio" - -/* - * Early pre 4.0 Silicon required to delay readout by at least 25ms, - * since the Event Counter Register updated 25ms after the interrupt - * asserted. - */ -#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4) - -struct adp5588_gpio { - struct i2c_client *client; - struct gpio_chip gpio_chip; - struct mutex lock; /* protect cached dir, dat_out */ - /* protect serialized access to the interrupt controller bus */ - struct mutex irq_lock; - unsigned gpio_start; - unsigned irq_base; - uint8_t dat_out[3]; - uint8_t dir[3]; - uint8_t int_lvl[3]; - uint8_t int_en[3]; - uint8_t irq_mask[3]; - uint8_t irq_stat[3]; -}; - -static int adp5588_gpio_read(struct i2c_client *client, u8 reg) -{ - int ret = i2c_smbus_read_byte_data(client, reg); - - if (ret < 0) - dev_err(&client->dev, "Read Error\n"); - - return ret; -} - -static int adp5588_gpio_write(struct i2c_client *client, u8 reg, u8 val) -{ - int ret = i2c_smbus_write_byte_data(client, reg, val); - - if (ret < 0) - dev_err(&client->dev, "Write Error\n"); - - return ret; -} - -static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off) -{ - struct adp5588_gpio *dev = - container_of(chip, struct adp5588_gpio, gpio_chip); - - return !!(adp5588_gpio_read(dev->client, - GPIO_DAT_STAT1 + ADP5588_BANK(off)) & ADP5588_BIT(off)); -} - -static void adp5588_gpio_set_value(struct gpio_chip *chip, - unsigned off, int val) -{ - unsigned bank, bit; - struct adp5588_gpio *dev = - container_of(chip, struct adp5588_gpio, gpio_chip); - - bank = ADP5588_BANK(off); - bit = ADP5588_BIT(off); - - mutex_lock(&dev->lock); - if (val) - dev->dat_out[bank] |= bit; - else - dev->dat_out[bank] &= ~bit; - - adp5588_gpio_write(dev->client, GPIO_DAT_OUT1 + bank, - dev->dat_out[bank]); - mutex_unlock(&dev->lock); -} - -static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off) -{ - int ret; - unsigned bank; - struct adp5588_gpio *dev = - container_of(chip, struct adp5588_gpio, gpio_chip); - - bank = ADP5588_BANK(off); - - mutex_lock(&dev->lock); - dev->dir[bank] &= ~ADP5588_BIT(off); - ret = adp5588_gpio_write(dev->client, GPIO_DIR1 + bank, dev->dir[bank]); - mutex_unlock(&dev->lock); - - return ret; -} - -static int adp5588_gpio_direction_output(struct gpio_chip *chip, - unsigned off, int val) -{ - int ret; - unsigned bank, bit; - struct adp5588_gpio *dev = - container_of(chip, struct adp5588_gpio, gpio_chip); - - bank = ADP5588_BANK(off); - bit = ADP5588_BIT(off); - - mutex_lock(&dev->lock); - dev->dir[bank] |= bit; - - if (val) - dev->dat_out[bank] |= bit; - else - dev->dat_out[bank] &= ~bit; - - ret = adp5588_gpio_write(dev->client, GPIO_DAT_OUT1 + bank, - dev->dat_out[bank]); - ret |= adp5588_gpio_write(dev->client, GPIO_DIR1 + bank, - dev->dir[bank]); - mutex_unlock(&dev->lock); - - return ret; -} - -#ifdef CONFIG_GPIO_ADP5588_IRQ -static int adp5588_gpio_to_irq(struct gpio_chip *chip, unsigned off) -{ - struct adp5588_gpio *dev = - container_of(chip, struct adp5588_gpio, gpio_chip); - return dev->irq_base + off; -} - -static void adp5588_irq_bus_lock(struct irq_data *d) -{ - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); - - mutex_lock(&dev->irq_lock); -} - - /* - * genirq core code can issue chip->mask/unmask from atomic context. - * This doesn't work for slow busses where an access needs to sleep. - * bus_sync_unlock() is therefore called outside the atomic context, - * syncs the current irq mask state with the slow external controller - * and unlocks the bus. - */ - -static void adp5588_irq_bus_sync_unlock(struct irq_data *d) -{ - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); - int i; - - for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) - if (dev->int_en[i] ^ dev->irq_mask[i]) { - dev->int_en[i] = dev->irq_mask[i]; - adp5588_gpio_write(dev->client, GPIO_INT_EN1 + i, - dev->int_en[i]); - } - - mutex_unlock(&dev->irq_lock); -} - -static void adp5588_irq_mask(struct irq_data *d) -{ - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); - unsigned gpio = d->irq - dev->irq_base; - - dev->irq_mask[ADP5588_BANK(gpio)] &= ~ADP5588_BIT(gpio); -} - -static void adp5588_irq_unmask(struct irq_data *d) -{ - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); - unsigned gpio = d->irq - dev->irq_base; - - dev->irq_mask[ADP5588_BANK(gpio)] |= ADP5588_BIT(gpio); -} - -static int adp5588_irq_set_type(struct irq_data *d, unsigned int type) -{ - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); - uint16_t gpio = d->irq - dev->irq_base; - unsigned bank, bit; - - if ((type & IRQ_TYPE_EDGE_BOTH)) { - dev_err(&dev->client->dev, "irq %d: unsupported type %d\n", - d->irq, type); - return -EINVAL; - } - - bank = ADP5588_BANK(gpio); - bit = ADP5588_BIT(gpio); - - if (type & IRQ_TYPE_LEVEL_HIGH) - dev->int_lvl[bank] |= bit; - else if (type & IRQ_TYPE_LEVEL_LOW) - dev->int_lvl[bank] &= ~bit; - else - return -EINVAL; - - adp5588_gpio_direction_input(&dev->gpio_chip, gpio); - adp5588_gpio_write(dev->client, GPIO_INT_LVL1 + bank, - dev->int_lvl[bank]); - - return 0; -} - -static struct irq_chip adp5588_irq_chip = { - .name = "adp5588", - .irq_mask = adp5588_irq_mask, - .irq_unmask = adp5588_irq_unmask, - .irq_bus_lock = adp5588_irq_bus_lock, - .irq_bus_sync_unlock = adp5588_irq_bus_sync_unlock, - .irq_set_type = adp5588_irq_set_type, -}; - -static int adp5588_gpio_read_intstat(struct i2c_client *client, u8 *buf) -{ - int ret = i2c_smbus_read_i2c_block_data(client, GPIO_INT_STAT1, 3, buf); - - if (ret < 0) - dev_err(&client->dev, "Read INT_STAT Error\n"); - - return ret; -} - -static irqreturn_t adp5588_irq_handler(int irq, void *devid) -{ - struct adp5588_gpio *dev = devid; - unsigned status, bank, bit, pending; - int ret; - status = adp5588_gpio_read(dev->client, INT_STAT); - - if (status & ADP5588_GPI_INT) { - ret = adp5588_gpio_read_intstat(dev->client, dev->irq_stat); - if (ret < 0) - memset(dev->irq_stat, 0, ARRAY_SIZE(dev->irq_stat)); - - for (bank = 0; bank <= ADP5588_BANK(ADP5588_MAXGPIO); - bank++, bit = 0) { - pending = dev->irq_stat[bank] & dev->irq_mask[bank]; - - while (pending) { - if (pending & (1 << bit)) { - handle_nested_irq(dev->irq_base + - (bank << 3) + bit); - pending &= ~(1 << bit); - - } - bit++; - } - } - } - - adp5588_gpio_write(dev->client, INT_STAT, status); /* Status is W1C */ - - return IRQ_HANDLED; -} - -static int adp5588_irq_setup(struct adp5588_gpio *dev) -{ - struct i2c_client *client = dev->client; - struct adp5588_gpio_platform_data *pdata = client->dev.platform_data; - unsigned gpio; - int ret; - - adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC); - adp5588_gpio_write(client, INT_STAT, -1); /* status is W1C */ - adp5588_gpio_read_intstat(client, dev->irq_stat); /* read to clear */ - - dev->irq_base = pdata->irq_base; - mutex_init(&dev->irq_lock); - - for (gpio = 0; gpio < dev->gpio_chip.ngpio; gpio++) { - int irq = gpio + dev->irq_base; - irq_set_chip_data(irq, dev); - irq_set_chip_and_handler(irq, &adp5588_irq_chip, - handle_level_irq); - irq_set_nested_thread(irq, 1); -#ifdef CONFIG_ARM - /* - * ARM needs us to explicitly flag the IRQ as VALID, - * once we do so, it will also set the noprobe. - */ - set_irq_flags(irq, IRQF_VALID); -#else - irq_set_noprobe(irq); -#endif - } - - ret = request_threaded_irq(client->irq, - NULL, - adp5588_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(&client->dev), dev); - if (ret) { - dev_err(&client->dev, "failed to request irq %d\n", - client->irq); - goto out; - } - - dev->gpio_chip.to_irq = adp5588_gpio_to_irq; - adp5588_gpio_write(client, CFG, - ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_GPI_INT); - - return 0; - -out: - dev->irq_base = 0; - return ret; -} - -static void adp5588_irq_teardown(struct adp5588_gpio *dev) -{ - if (dev->irq_base) - free_irq(dev->client->irq, dev); -} - -#else -static int adp5588_irq_setup(struct adp5588_gpio *dev) -{ - struct i2c_client *client = dev->client; - dev_warn(&client->dev, "interrupt support not compiled in\n"); - - return 0; -} - -static void adp5588_irq_teardown(struct adp5588_gpio *dev) -{ -} -#endif /* CONFIG_GPIO_ADP5588_IRQ */ - -static int __devinit adp5588_gpio_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct adp5588_gpio_platform_data *pdata = client->dev.platform_data; - struct adp5588_gpio *dev; - struct gpio_chip *gc; - int ret, i, revid; - - if (pdata == NULL) { - dev_err(&client->dev, "missing platform data\n"); - return -ENODEV; - } - - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); - return -EIO; - } - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) { - dev_err(&client->dev, "failed to alloc memory\n"); - return -ENOMEM; - } - - dev->client = client; - - gc = &dev->gpio_chip; - gc->direction_input = adp5588_gpio_direction_input; - gc->direction_output = adp5588_gpio_direction_output; - gc->get = adp5588_gpio_get_value; - gc->set = adp5588_gpio_set_value; - gc->can_sleep = 1; - - gc->base = pdata->gpio_start; - gc->ngpio = ADP5588_MAXGPIO; - gc->label = client->name; - gc->owner = THIS_MODULE; - - mutex_init(&dev->lock); - - ret = adp5588_gpio_read(dev->client, DEV_ID); - if (ret < 0) - goto err; - - revid = ret & ADP5588_DEVICE_ID_MASK; - - for (i = 0, ret = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) { - dev->dat_out[i] = adp5588_gpio_read(client, GPIO_DAT_OUT1 + i); - dev->dir[i] = adp5588_gpio_read(client, GPIO_DIR1 + i); - ret |= adp5588_gpio_write(client, KP_GPIO1 + i, 0); - ret |= adp5588_gpio_write(client, GPIO_PULL1 + i, - (pdata->pullup_dis_mask >> (8 * i)) & 0xFF); - ret |= adp5588_gpio_write(client, GPIO_INT_EN1 + i, 0); - if (ret) - goto err; - } - - if (pdata->irq_base) { - if (WA_DELAYED_READOUT_REVID(revid)) { - dev_warn(&client->dev, "GPIO int not supported\n"); - } else { - ret = adp5588_irq_setup(dev); - if (ret) - goto err; - } - } - - ret = gpiochip_add(&dev->gpio_chip); - if (ret) - goto err_irq; - - dev_info(&client->dev, "gpios %d..%d (IRQ Base %d) on a %s Rev. %d\n", - gc->base, gc->base + gc->ngpio - 1, - pdata->irq_base, client->name, revid); - - if (pdata->setup) { - ret = pdata->setup(client, gc->base, gc->ngpio, pdata->context); - if (ret < 0) - dev_warn(&client->dev, "setup failed, %d\n", ret); - } - - i2c_set_clientdata(client, dev); - - return 0; - -err_irq: - adp5588_irq_teardown(dev); -err: - kfree(dev); - return ret; -} - -static int __devexit adp5588_gpio_remove(struct i2c_client *client) -{ - struct adp5588_gpio_platform_data *pdata = client->dev.platform_data; - struct adp5588_gpio *dev = i2c_get_clientdata(client); - int ret; - - if (pdata->teardown) { - ret = pdata->teardown(client, - dev->gpio_chip.base, dev->gpio_chip.ngpio, - pdata->context); - if (ret < 0) { - dev_err(&client->dev, "teardown failed %d\n", ret); - return ret; - } - } - - if (dev->irq_base) - free_irq(dev->client->irq, dev); - - ret = gpiochip_remove(&dev->gpio_chip); - if (ret) { - dev_err(&client->dev, "gpiochip_remove failed %d\n", ret); - return ret; - } - - kfree(dev); - return 0; -} - -static const struct i2c_device_id adp5588_gpio_id[] = { - {DRV_NAME, 0}, - {} -}; - -MODULE_DEVICE_TABLE(i2c, adp5588_gpio_id); - -static struct i2c_driver adp5588_gpio_driver = { - .driver = { - .name = DRV_NAME, - }, - .probe = adp5588_gpio_probe, - .remove = __devexit_p(adp5588_gpio_remove), - .id_table = adp5588_gpio_id, -}; - -static int __init adp5588_gpio_init(void) -{ - return i2c_add_driver(&adp5588_gpio_driver); -} - -module_init(adp5588_gpio_init); - -static void __exit adp5588_gpio_exit(void) -{ - i2c_del_driver(&adp5588_gpio_driver); -} - -module_exit(adp5588_gpio_exit); - -MODULE_AUTHOR("Michael Hennerich "); -MODULE_DESCRIPTION("GPIO ADP5588 Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/basic_mmio_gpio.c b/drivers/gpio/basic_mmio_gpio.c deleted file mode 100644 index 8152e9f516b..00000000000 --- a/drivers/gpio/basic_mmio_gpio.c +++ /dev/null @@ -1,548 +0,0 @@ -/* - * Driver for basic memory-mapped GPIO controllers. - * - * Copyright 2008 MontaVista Software, Inc. - * Copyright 2008,2010 Anton Vorontsov - * - * 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. - * - * ....``.```~~~~````.`.`.`.`.```````'',,,.........`````......`....... - * ...`` ```````.. - * ..The simplest form of a GPIO controller that the driver supports is`` - * `.just a single "data" register, where GPIO state can be read and/or ` - * `,..written. ,,..``~~~~ .....``.`.`.~~.```.`.........``````.``````` - * ````````` - ___ -_/~~|___/~| . ```~~~~~~ ___/___\___ ,~.`.`.`.`````.~~...,,,,... -__________|~$@~~~ %~ /o*o*o*o*o*o\ .. Implementing such a GPIO . -o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` - `....trivial..'~`.```.``` - * ``````` - * .```````~~~~`..`.``.``. - * . The driver supports `... ,..```.`~~~```````````````....````.``,, - * . big-endian notation, just`. .. A bit more sophisticated controllers , - * . register the device with -be`. .with a pair of set/clear-bit registers , - * `.. suffix. ```~~`````....`.` . affecting the data register and the .` - * ``.`.``...``` ```.. output pins are also supported.` - * ^^ `````.`````````.,``~``~``~~`````` - * . ^^ - * ,..`.`.`...````````````......`.`.`.`.`.`..`.`.`.. - * .. The expectation is that in at least some cases . ,-~~~-, - * .this will be used with roll-your-own ASIC/FPGA .` \ / - * .logic in Verilog or VHDL. ~~~`````````..`````~~` \ / - * ..````````......``````````` \o_ - * | - * ^^ / \ - * - * ...`````~~`.....``.`..........``````.`.``.```........``. - * ` 8, 16, 32 and 64 bits registers are supported, and``. - * . the number of GPIOs is determined by the width of ~ - * .. the registers. ,............```.`.`..`.`.~~~.`.`.`~ - * `.......````.``` - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void bgpio_write8(void __iomem *reg, unsigned long data) -{ - writeb(data, reg); -} - -static unsigned long bgpio_read8(void __iomem *reg) -{ - return readb(reg); -} - -static void bgpio_write16(void __iomem *reg, unsigned long data) -{ - writew(data, reg); -} - -static unsigned long bgpio_read16(void __iomem *reg) -{ - return readw(reg); -} - -static void bgpio_write32(void __iomem *reg, unsigned long data) -{ - writel(data, reg); -} - -static unsigned long bgpio_read32(void __iomem *reg) -{ - return readl(reg); -} - -#if BITS_PER_LONG >= 64 -static void bgpio_write64(void __iomem *reg, unsigned long data) -{ - writeq(data, reg); -} - -static unsigned long bgpio_read64(void __iomem *reg) -{ - return readq(reg); -} -#endif /* BITS_PER_LONG >= 64 */ - -static unsigned long bgpio_pin2mask(struct bgpio_chip *bgc, unsigned int pin) -{ - return 1 << pin; -} - -static unsigned long bgpio_pin2mask_be(struct bgpio_chip *bgc, - unsigned int pin) -{ - return 1 << (bgc->bits - 1 - pin); -} - -static int bgpio_get(struct gpio_chip *gc, unsigned int gpio) -{ - struct bgpio_chip *bgc = to_bgpio_chip(gc); - - return bgc->read_reg(bgc->reg_dat) & bgc->pin2mask(bgc, gpio); -} - -static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) -{ - struct bgpio_chip *bgc = to_bgpio_chip(gc); - unsigned long mask = bgc->pin2mask(bgc, gpio); - unsigned long flags; - - spin_lock_irqsave(&bgc->lock, flags); - - if (val) - bgc->data |= mask; - else - bgc->data &= ~mask; - - bgc->write_reg(bgc->reg_dat, bgc->data); - - spin_unlock_irqrestore(&bgc->lock, flags); -} - -static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, - int val) -{ - struct bgpio_chip *bgc = to_bgpio_chip(gc); - unsigned long mask = bgc->pin2mask(bgc, gpio); - - if (val) - bgc->write_reg(bgc->reg_set, mask); - else - bgc->write_reg(bgc->reg_clr, mask); -} - -static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) -{ - struct bgpio_chip *bgc = to_bgpio_chip(gc); - unsigned long mask = bgc->pin2mask(bgc, gpio); - unsigned long flags; - - spin_lock_irqsave(&bgc->lock, flags); - - if (val) - bgc->data |= mask; - else - bgc->data &= ~mask; - - bgc->write_reg(bgc->reg_set, bgc->data); - - spin_unlock_irqrestore(&bgc->lock, flags); -} - -static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio) -{ - return 0; -} - -static int bgpio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio, - int val) -{ - gc->set(gc, gpio, val); - - return 0; -} - -static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) -{ - struct bgpio_chip *bgc = to_bgpio_chip(gc); - unsigned long flags; - - spin_lock_irqsave(&bgc->lock, flags); - - bgc->dir &= ~bgc->pin2mask(bgc, gpio); - bgc->write_reg(bgc->reg_dir, bgc->dir); - - spin_unlock_irqrestore(&bgc->lock, flags); - - return 0; -} - -static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) -{ - struct bgpio_chip *bgc = to_bgpio_chip(gc); - unsigned long flags; - - gc->set(gc, gpio, val); - - spin_lock_irqsave(&bgc->lock, flags); - - bgc->dir |= bgc->pin2mask(bgc, gpio); - bgc->write_reg(bgc->reg_dir, bgc->dir); - - spin_unlock_irqrestore(&bgc->lock, flags); - - return 0; -} - -static int bgpio_dir_in_inv(struct gpio_chip *gc, unsigned int gpio) -{ - struct bgpio_chip *bgc = to_bgpio_chip(gc); - unsigned long flags; - - spin_lock_irqsave(&bgc->lock, flags); - - bgc->dir |= bgc->pin2mask(bgc, gpio); - bgc->write_reg(bgc->reg_dir, bgc->dir); - - spin_unlock_irqrestore(&bgc->lock, flags); - - return 0; -} - -static int bgpio_dir_out_inv(struct gpio_chip *gc, unsigned int gpio, int val) -{ - struct bgpio_chip *bgc = to_bgpio_chip(gc); - unsigned long flags; - - gc->set(gc, gpio, val); - - spin_lock_irqsave(&bgc->lock, flags); - - bgc->dir &= ~bgc->pin2mask(bgc, gpio); - bgc->write_reg(bgc->reg_dir, bgc->dir); - - spin_unlock_irqrestore(&bgc->lock, flags); - - return 0; -} - -static int bgpio_setup_accessors(struct device *dev, - struct bgpio_chip *bgc, - bool be) -{ - - switch (bgc->bits) { - case 8: - bgc->read_reg = bgpio_read8; - bgc->write_reg = bgpio_write8; - break; - case 16: - bgc->read_reg = bgpio_read16; - bgc->write_reg = bgpio_write16; - break; - case 32: - bgc->read_reg = bgpio_read32; - bgc->write_reg = bgpio_write32; - break; -#if BITS_PER_LONG >= 64 - case 64: - bgc->read_reg = bgpio_read64; - bgc->write_reg = bgpio_write64; - break; -#endif /* BITS_PER_LONG >= 64 */ - default: - dev_err(dev, "unsupported data width %u bits\n", bgc->bits); - return -EINVAL; - } - - bgc->pin2mask = be ? bgpio_pin2mask_be : bgpio_pin2mask; - - return 0; -} - -/* - * Create the device and allocate the resources. For setting GPIO's there are - * three supported configurations: - * - * - single input/output register resource (named "dat"). - * - set/clear pair (named "set" and "clr"). - * - single output register resource and single input resource ("set" and - * dat"). - * - * For the single output register, this drives a 1 by setting a bit and a zero - * by clearing a bit. For the set clr pair, this drives a 1 by setting a bit - * in the set register and clears it by setting a bit in the clear register. - * The configuration is detected by which resources are present. - * - * For setting the GPIO direction, there are three supported configurations: - * - * - simple bidirection GPIO that requires no configuration. - * - an output direction register (named "dirout") where a 1 bit - * indicates the GPIO is an output. - * - an input direction register (named "dirin") where a 1 bit indicates - * the GPIO is an input. - */ -static int bgpio_setup_io(struct bgpio_chip *bgc, - void __iomem *dat, - void __iomem *set, - void __iomem *clr) -{ - - bgc->reg_dat = dat; - if (!bgc->reg_dat) - return -EINVAL; - - if (set && clr) { - bgc->reg_set = set; - bgc->reg_clr = clr; - bgc->gc.set = bgpio_set_with_clear; - } else if (set && !clr) { - bgc->reg_set = set; - bgc->gc.set = bgpio_set_set; - } else { - bgc->gc.set = bgpio_set; - } - - bgc->gc.get = bgpio_get; - - return 0; -} - -static int bgpio_setup_direction(struct bgpio_chip *bgc, - void __iomem *dirout, - void __iomem *dirin) -{ - if (dirout && dirin) { - return -EINVAL; - } else if (dirout) { - bgc->reg_dir = dirout; - bgc->gc.direction_output = bgpio_dir_out; - bgc->gc.direction_input = bgpio_dir_in; - } else if (dirin) { - bgc->reg_dir = dirin; - bgc->gc.direction_output = bgpio_dir_out_inv; - bgc->gc.direction_input = bgpio_dir_in_inv; - } else { - bgc->gc.direction_output = bgpio_simple_dir_out; - bgc->gc.direction_input = bgpio_simple_dir_in; - } - - return 0; -} - -int __devexit bgpio_remove(struct bgpio_chip *bgc) -{ - int err = gpiochip_remove(&bgc->gc); - - kfree(bgc); - - return err; -} -EXPORT_SYMBOL_GPL(bgpio_remove); - -int __devinit bgpio_init(struct bgpio_chip *bgc, - struct device *dev, - unsigned long sz, - void __iomem *dat, - void __iomem *set, - void __iomem *clr, - void __iomem *dirout, - void __iomem *dirin, - bool big_endian) -{ - int ret; - - if (!is_power_of_2(sz)) - return -EINVAL; - - bgc->bits = sz * 8; - if (bgc->bits > BITS_PER_LONG) - return -EINVAL; - - spin_lock_init(&bgc->lock); - bgc->gc.dev = dev; - bgc->gc.label = dev_name(dev); - bgc->gc.base = -1; - bgc->gc.ngpio = bgc->bits; - - ret = bgpio_setup_io(bgc, dat, set, clr); - if (ret) - return ret; - - ret = bgpio_setup_accessors(dev, bgc, big_endian); - if (ret) - return ret; - - ret = bgpio_setup_direction(bgc, dirout, dirin); - if (ret) - return ret; - - bgc->data = bgc->read_reg(bgc->reg_dat); - - return ret; -} -EXPORT_SYMBOL_GPL(bgpio_init); - -#ifdef CONFIG_GPIO_BASIC_MMIO - -static void __iomem *bgpio_map(struct platform_device *pdev, - const char *name, - resource_size_t sane_sz, - int *err) -{ - struct device *dev = &pdev->dev; - struct resource *r; - resource_size_t start; - resource_size_t sz; - void __iomem *ret; - - *err = 0; - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); - if (!r) - return NULL; - - sz = resource_size(r); - if (sz != sane_sz) { - *err = -EINVAL; - return NULL; - } - - start = r->start; - if (!devm_request_mem_region(dev, start, sz, r->name)) { - *err = -EBUSY; - return NULL; - } - - ret = devm_ioremap(dev, start, sz); - if (!ret) { - *err = -ENOMEM; - return NULL; - } - - return ret; -} - -static int __devinit bgpio_pdev_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct resource *r; - void __iomem *dat; - void __iomem *set; - void __iomem *clr; - void __iomem *dirout; - void __iomem *dirin; - unsigned long sz; - bool be; - int err; - struct bgpio_chip *bgc; - struct bgpio_pdata *pdata = dev_get_platdata(dev); - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); - if (!r) - return -EINVAL; - - sz = resource_size(r); - - dat = bgpio_map(pdev, "dat", sz, &err); - if (!dat) - return err ? err : -EINVAL; - - set = bgpio_map(pdev, "set", sz, &err); - if (err) - return err; - - clr = bgpio_map(pdev, "clr", sz, &err); - if (err) - return err; - - dirout = bgpio_map(pdev, "dirout", sz, &err); - if (err) - return err; - - dirin = bgpio_map(pdev, "dirin", sz, &err); - if (err) - return err; - - be = !strcmp(platform_get_device_id(pdev)->name, "basic-mmio-gpio-be"); - - bgc = devm_kzalloc(&pdev->dev, sizeof(*bgc), GFP_KERNEL); - if (!bgc) - return -ENOMEM; - - err = bgpio_init(bgc, dev, sz, dat, set, clr, dirout, dirin, be); - if (err) - return err; - - if (pdata) { - bgc->gc.base = pdata->base; - if (pdata->ngpio > 0) - bgc->gc.ngpio = pdata->ngpio; - } - - platform_set_drvdata(pdev, bgc); - - return gpiochip_add(&bgc->gc); -} - -static int __devexit bgpio_pdev_remove(struct platform_device *pdev) -{ - struct bgpio_chip *bgc = platform_get_drvdata(pdev); - - return bgpio_remove(bgc); -} - -static const struct platform_device_id bgpio_id_table[] = { - { "basic-mmio-gpio", }, - { "basic-mmio-gpio-be", }, - {}, -}; -MODULE_DEVICE_TABLE(platform, bgpio_id_table); - -static struct platform_driver bgpio_driver = { - .driver = { - .name = "basic-mmio-gpio", - }, - .id_table = bgpio_id_table, - .probe = bgpio_pdev_probe, - .remove = __devexit_p(bgpio_pdev_remove), -}; - -static int __init bgpio_platform_init(void) -{ - return platform_driver_register(&bgpio_driver); -} -module_init(bgpio_platform_init); - -static void __exit bgpio_platform_exit(void) -{ - platform_driver_unregister(&bgpio_driver); -} -module_exit(bgpio_platform_exit); - -#endif /* CONFIG_GPIO_BASIC_MMIO */ - -MODULE_DESCRIPTION("Driver for basic memory-mapped GPIO controllers"); -MODULE_AUTHOR("Anton Vorontsov "); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/bt8xxgpio.c b/drivers/gpio/bt8xxgpio.c deleted file mode 100644 index aa4f09ad3ce..00000000000 --- a/drivers/gpio/bt8xxgpio.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - - bt8xx GPIO abuser - - Copyright (C) 2008 Michael Buesch - - Please do _only_ contact the people listed _above_ with issues related to this driver. - All the other people listed below are not related to this driver. Their names - are only here, because this driver is derived from the bt848 driver. - - - Derived from the bt848 driver: - - Copyright (C) 1996,97,98 Ralph Metzler - & Marcus Metzler - (c) 1999-2002 Gerd Knorr - - some v4l2 code lines are taken from Justin's bttv2 driver which is - (c) 2000 Justin Schoeman - - V4L1 removal from: - (c) 2005-2006 Nickolay V. Shmyrev - - Fixes to be fully V4L2 compliant by - (c) 2006 Mauro Carvalho Chehab - - Cropping and overscan support - Copyright (C) 2005, 2006 Michael H. Schimek - Sponsored by OPQ Systems AB - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include - -/* Steal the hardware definitions from the bttv driver. */ -#include "../media/video/bt8xx/bt848.h" - - -#define BT8XXGPIO_NR_GPIOS 24 /* We have 24 GPIO pins */ - - -struct bt8xxgpio { - spinlock_t lock; - - void __iomem *mmio; - struct pci_dev *pdev; - struct gpio_chip gpio; - -#ifdef CONFIG_PM - u32 saved_outen; - u32 saved_data; -#endif -}; - -#define bgwrite(dat, adr) writel((dat), bg->mmio+(adr)) -#define bgread(adr) readl(bg->mmio+(adr)) - - -static int modparam_gpiobase = -1/* dynamic */; -module_param_named(gpiobase, modparam_gpiobase, int, 0444); -MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the default."); - - -static int bt8xxgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) -{ - struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); - unsigned long flags; - u32 outen, data; - - spin_lock_irqsave(&bg->lock, flags); - - data = bgread(BT848_GPIO_DATA); - data &= ~(1 << nr); - bgwrite(data, BT848_GPIO_DATA); - - outen = bgread(BT848_GPIO_OUT_EN); - outen &= ~(1 << nr); - bgwrite(outen, BT848_GPIO_OUT_EN); - - spin_unlock_irqrestore(&bg->lock, flags); - - return 0; -} - -static int bt8xxgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) -{ - struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); - unsigned long flags; - u32 val; - - spin_lock_irqsave(&bg->lock, flags); - val = bgread(BT848_GPIO_DATA); - spin_unlock_irqrestore(&bg->lock, flags); - - return !!(val & (1 << nr)); -} - -static int bt8xxgpio_gpio_direction_output(struct gpio_chip *gpio, - unsigned nr, int val) -{ - struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); - unsigned long flags; - u32 outen, data; - - spin_lock_irqsave(&bg->lock, flags); - - outen = bgread(BT848_GPIO_OUT_EN); - outen |= (1 << nr); - bgwrite(outen, BT848_GPIO_OUT_EN); - - data = bgread(BT848_GPIO_DATA); - if (val) - data |= (1 << nr); - else - data &= ~(1 << nr); - bgwrite(data, BT848_GPIO_DATA); - - spin_unlock_irqrestore(&bg->lock, flags); - - return 0; -} - -static void bt8xxgpio_gpio_set(struct gpio_chip *gpio, - unsigned nr, int val) -{ - struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); - unsigned long flags; - u32 data; - - spin_lock_irqsave(&bg->lock, flags); - - data = bgread(BT848_GPIO_DATA); - if (val) - data |= (1 << nr); - else - data &= ~(1 << nr); - bgwrite(data, BT848_GPIO_DATA); - - spin_unlock_irqrestore(&bg->lock, flags); -} - -static void bt8xxgpio_gpio_setup(struct bt8xxgpio *bg) -{ - struct gpio_chip *c = &bg->gpio; - - c->label = dev_name(&bg->pdev->dev); - c->owner = THIS_MODULE; - c->direction_input = bt8xxgpio_gpio_direction_input; - c->get = bt8xxgpio_gpio_get; - c->direction_output = bt8xxgpio_gpio_direction_output; - c->set = bt8xxgpio_gpio_set; - c->dbg_show = NULL; - c->base = modparam_gpiobase; - c->ngpio = BT8XXGPIO_NR_GPIOS; - c->can_sleep = 0; -} - -static int bt8xxgpio_probe(struct pci_dev *dev, - const struct pci_device_id *pci_id) -{ - struct bt8xxgpio *bg; - int err; - - bg = kzalloc(sizeof(*bg), GFP_KERNEL); - if (!bg) - return -ENOMEM; - - bg->pdev = dev; - spin_lock_init(&bg->lock); - - err = pci_enable_device(dev); - if (err) { - printk(KERN_ERR "bt8xxgpio: Can't enable device.\n"); - goto err_freebg; - } - if (!request_mem_region(pci_resource_start(dev, 0), - pci_resource_len(dev, 0), - "bt8xxgpio")) { - printk(KERN_WARNING "bt8xxgpio: Can't request iomem (0x%llx).\n", - (unsigned long long)pci_resource_start(dev, 0)); - err = -EBUSY; - goto err_disable; - } - pci_set_master(dev); - pci_set_drvdata(dev, bg); - - bg->mmio = ioremap(pci_resource_start(dev, 0), 0x1000); - if (!bg->mmio) { - printk(KERN_ERR "bt8xxgpio: ioremap() failed\n"); - err = -EIO; - goto err_release_mem; - } - - /* Disable interrupts */ - bgwrite(0, BT848_INT_MASK); - - /* gpio init */ - bgwrite(0, BT848_GPIO_DMA_CTL); - bgwrite(0, BT848_GPIO_REG_INP); - bgwrite(0, BT848_GPIO_OUT_EN); - - bt8xxgpio_gpio_setup(bg); - err = gpiochip_add(&bg->gpio); - if (err) { - printk(KERN_ERR "bt8xxgpio: Failed to register GPIOs\n"); - goto err_release_mem; - } - - printk(KERN_INFO "bt8xxgpio: Abusing BT8xx card for GPIOs %d to %d\n", - bg->gpio.base, bg->gpio.base + BT8XXGPIO_NR_GPIOS - 1); - - return 0; - -err_release_mem: - release_mem_region(pci_resource_start(dev, 0), - pci_resource_len(dev, 0)); - pci_set_drvdata(dev, NULL); -err_disable: - pci_disable_device(dev); -err_freebg: - kfree(bg); - - return err; -} - -static void bt8xxgpio_remove(struct pci_dev *pdev) -{ - struct bt8xxgpio *bg = pci_get_drvdata(pdev); - - gpiochip_remove(&bg->gpio); - - bgwrite(0, BT848_INT_MASK); - bgwrite(~0x0, BT848_INT_STAT); - bgwrite(0x0, BT848_GPIO_OUT_EN); - - iounmap(bg->mmio); - release_mem_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - pci_disable_device(pdev); - - pci_set_drvdata(pdev, NULL); - kfree(bg); -} - -#ifdef CONFIG_PM -static int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state) -{ - struct bt8xxgpio *bg = pci_get_drvdata(pdev); - unsigned long flags; - - spin_lock_irqsave(&bg->lock, flags); - - bg->saved_outen = bgread(BT848_GPIO_OUT_EN); - bg->saved_data = bgread(BT848_GPIO_DATA); - - bgwrite(0, BT848_INT_MASK); - bgwrite(~0x0, BT848_INT_STAT); - bgwrite(0x0, BT848_GPIO_OUT_EN); - - spin_unlock_irqrestore(&bg->lock, flags); - - pci_save_state(pdev); - pci_disable_device(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - - return 0; -} - -static int bt8xxgpio_resume(struct pci_dev *pdev) -{ - struct bt8xxgpio *bg = pci_get_drvdata(pdev); - unsigned long flags; - int err; - - pci_set_power_state(pdev, 0); - err = pci_enable_device(pdev); - if (err) - return err; - pci_restore_state(pdev); - - spin_lock_irqsave(&bg->lock, flags); - - bgwrite(0, BT848_INT_MASK); - bgwrite(0, BT848_GPIO_DMA_CTL); - bgwrite(0, BT848_GPIO_REG_INP); - bgwrite(bg->saved_outen, BT848_GPIO_OUT_EN); - bgwrite(bg->saved_data & bg->saved_outen, - BT848_GPIO_DATA); - - spin_unlock_irqrestore(&bg->lock, flags); - - return 0; -} -#else -#define bt8xxgpio_suspend NULL -#define bt8xxgpio_resume NULL -#endif /* CONFIG_PM */ - -static struct pci_device_id bt8xxgpio_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) }, - { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849) }, - { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878) }, - { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879) }, - { 0, }, -}; -MODULE_DEVICE_TABLE(pci, bt8xxgpio_pci_tbl); - -static struct pci_driver bt8xxgpio_pci_driver = { - .name = "bt8xxgpio", - .id_table = bt8xxgpio_pci_tbl, - .probe = bt8xxgpio_probe, - .remove = bt8xxgpio_remove, - .suspend = bt8xxgpio_suspend, - .resume = bt8xxgpio_resume, -}; - -static int __init bt8xxgpio_init(void) -{ - return pci_register_driver(&bt8xxgpio_pci_driver); -} -module_init(bt8xxgpio_init) - -static void __exit bt8xxgpio_exit(void) -{ - pci_unregister_driver(&bt8xxgpio_pci_driver); -} -module_exit(bt8xxgpio_exit) - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Michael Buesch"); -MODULE_DESCRIPTION("Abuse a BT8xx framegrabber card as generic GPIO card"); diff --git a/drivers/gpio/cs5535-gpio.c b/drivers/gpio/cs5535-gpio.c deleted file mode 100644 index 6e16cba56ad..00000000000 --- a/drivers/gpio/cs5535-gpio.c +++ /dev/null @@ -1,401 +0,0 @@ -/* - * AMD CS5535/CS5536 GPIO driver - * Copyright (C) 2006 Advanced Micro Devices, Inc. - * Copyright (C) 2007-2009 Andres Salomon - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public License - * as published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "cs5535-gpio" - -/* - * Some GPIO pins - * 31-29,23 : reserved (always mask out) - * 28 : Power Button - * 26 : PME# - * 22-16 : LPC - * 14,15 : SMBus - * 9,8 : UART1 - * 7 : PCI INTB - * 3,4 : UART2/DDC - * 2 : IDE_IRQ0 - * 1 : AC_BEEP - * 0 : PCI INTA - * - * If a mask was not specified, allow all except - * reserved and Power Button - */ -#define GPIO_DEFAULT_MASK 0x0F7FFFFF - -static ulong mask = GPIO_DEFAULT_MASK; -module_param_named(mask, mask, ulong, 0444); -MODULE_PARM_DESC(mask, "GPIO channel mask."); - -static struct cs5535_gpio_chip { - struct gpio_chip chip; - resource_size_t base; - - struct platform_device *pdev; - spinlock_t lock; -} cs5535_gpio_chip; - -/* - * The CS5535/CS5536 GPIOs support a number of extra features not defined - * by the gpio_chip API, so these are exported. For a full list of the - * registers, see include/linux/cs5535.h. - */ - -static void errata_outl(struct cs5535_gpio_chip *chip, u32 val, - unsigned int reg) -{ - unsigned long addr = chip->base + 0x80 + reg; - - /* - * According to the CS5536 errata (#36), after suspend - * a write to the high bank GPIO register will clear all - * non-selected bits; the recommended workaround is a - * read-modify-write operation. - * - * Don't apply this errata to the edge status GPIOs, as writing - * to their lower bits will clear them. - */ - if (reg != GPIO_POSITIVE_EDGE_STS && reg != GPIO_NEGATIVE_EDGE_STS) { - if (val & 0xffff) - val |= (inl(addr) & 0xffff); /* ignore the high bits */ - else - val |= (inl(addr) ^ (val >> 16)); - } - outl(val, addr); -} - -static void __cs5535_gpio_set(struct cs5535_gpio_chip *chip, unsigned offset, - unsigned int reg) -{ - if (offset < 16) - /* low bank register */ - outl(1 << offset, chip->base + reg); - else - /* high bank register */ - errata_outl(chip, 1 << (offset - 16), reg); -} - -void cs5535_gpio_set(unsigned offset, unsigned int reg) -{ - struct cs5535_gpio_chip *chip = &cs5535_gpio_chip; - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - __cs5535_gpio_set(chip, offset, reg); - spin_unlock_irqrestore(&chip->lock, flags); -} -EXPORT_SYMBOL_GPL(cs5535_gpio_set); - -static void __cs5535_gpio_clear(struct cs5535_gpio_chip *chip, unsigned offset, - unsigned int reg) -{ - if (offset < 16) - /* low bank register */ - outl(1 << (offset + 16), chip->base + reg); - else - /* high bank register */ - errata_outl(chip, 1 << offset, reg); -} - -void cs5535_gpio_clear(unsigned offset, unsigned int reg) -{ - struct cs5535_gpio_chip *chip = &cs5535_gpio_chip; - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - __cs5535_gpio_clear(chip, offset, reg); - spin_unlock_irqrestore(&chip->lock, flags); -} -EXPORT_SYMBOL_GPL(cs5535_gpio_clear); - -int cs5535_gpio_isset(unsigned offset, unsigned int reg) -{ - struct cs5535_gpio_chip *chip = &cs5535_gpio_chip; - unsigned long flags; - long val; - - spin_lock_irqsave(&chip->lock, flags); - if (offset < 16) - /* low bank register */ - val = inl(chip->base + reg); - else { - /* high bank register */ - val = inl(chip->base + 0x80 + reg); - offset -= 16; - } - spin_unlock_irqrestore(&chip->lock, flags); - - return (val & (1 << offset)) ? 1 : 0; -} -EXPORT_SYMBOL_GPL(cs5535_gpio_isset); - -int cs5535_gpio_set_irq(unsigned group, unsigned irq) -{ - uint32_t lo, hi; - - if (group > 7 || irq > 15) - return -EINVAL; - - rdmsr(MSR_PIC_ZSEL_HIGH, lo, hi); - - lo &= ~(0xF << (group * 4)); - lo |= (irq & 0xF) << (group * 4); - - wrmsr(MSR_PIC_ZSEL_HIGH, lo, hi); - return 0; -} -EXPORT_SYMBOL_GPL(cs5535_gpio_set_irq); - -void cs5535_gpio_setup_event(unsigned offset, int pair, int pme) -{ - struct cs5535_gpio_chip *chip = &cs5535_gpio_chip; - uint32_t shift = (offset % 8) * 4; - unsigned long flags; - uint32_t val; - - if (offset >= 24) - offset = GPIO_MAP_W; - else if (offset >= 16) - offset = GPIO_MAP_Z; - else if (offset >= 8) - offset = GPIO_MAP_Y; - else - offset = GPIO_MAP_X; - - spin_lock_irqsave(&chip->lock, flags); - val = inl(chip->base + offset); - - /* Clear whatever was there before */ - val &= ~(0xF << shift); - - /* Set the new value */ - val |= ((pair & 7) << shift); - - /* Set the PME bit if this is a PME event */ - if (pme) - val |= (1 << (shift + 3)); - - outl(val, chip->base + offset); - spin_unlock_irqrestore(&chip->lock, flags); -} -EXPORT_SYMBOL_GPL(cs5535_gpio_setup_event); - -/* - * Generic gpio_chip API support. - */ - -static int chip_gpio_request(struct gpio_chip *c, unsigned offset) -{ - struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c; - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - - /* check if this pin is available */ - if ((mask & (1 << offset)) == 0) { - dev_info(&chip->pdev->dev, - "pin %u is not available (check mask)\n", offset); - spin_unlock_irqrestore(&chip->lock, flags); - return -EINVAL; - } - - /* disable output aux 1 & 2 on this pin */ - __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX1); - __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX2); - - /* disable input aux 1 on this pin */ - __cs5535_gpio_clear(chip, offset, GPIO_INPUT_AUX1); - - spin_unlock_irqrestore(&chip->lock, flags); - - return 0; -} - -static int chip_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - return cs5535_gpio_isset(offset, GPIO_READ_BACK); -} - -static void chip_gpio_set(struct gpio_chip *chip, unsigned offset, int val) -{ - if (val) - cs5535_gpio_set(offset, GPIO_OUTPUT_VAL); - else - cs5535_gpio_clear(offset, GPIO_OUTPUT_VAL); -} - -static int chip_direction_input(struct gpio_chip *c, unsigned offset) -{ - struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c; - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE); - __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_ENABLE); - spin_unlock_irqrestore(&chip->lock, flags); - - return 0; -} - -static int chip_direction_output(struct gpio_chip *c, unsigned offset, int val) -{ - struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c; - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - - __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE); - __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_ENABLE); - if (val) - __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_VAL); - else - __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_VAL); - - spin_unlock_irqrestore(&chip->lock, flags); - - return 0; -} - -static const char * const cs5535_gpio_names[] = { - "GPIO0", "GPIO1", "GPIO2", "GPIO3", - "GPIO4", "GPIO5", "GPIO6", "GPIO7", - "GPIO8", "GPIO9", "GPIO10", "GPIO11", - "GPIO12", "GPIO13", "GPIO14", "GPIO15", - "GPIO16", "GPIO17", "GPIO18", "GPIO19", - "GPIO20", "GPIO21", "GPIO22", NULL, - "GPIO24", "GPIO25", "GPIO26", "GPIO27", - "GPIO28", NULL, NULL, NULL, -}; - -static struct cs5535_gpio_chip cs5535_gpio_chip = { - .chip = { - .owner = THIS_MODULE, - .label = DRV_NAME, - - .base = 0, - .ngpio = 32, - .names = cs5535_gpio_names, - .request = chip_gpio_request, - - .get = chip_gpio_get, - .set = chip_gpio_set, - - .direction_input = chip_direction_input, - .direction_output = chip_direction_output, - }, -}; - -static int __devinit cs5535_gpio_probe(struct platform_device *pdev) -{ - struct resource *res; - int err = -EIO; - ulong mask_orig = mask; - - /* There are two ways to get the GPIO base address; one is by - * fetching it from MSR_LBAR_GPIO, the other is by reading the - * PCI BAR info. The latter method is easier (especially across - * different architectures), so we'll stick with that for now. If - * it turns out to be unreliable in the face of crappy BIOSes, we - * can always go back to using MSRs.. */ - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!res) { - dev_err(&pdev->dev, "can't fetch device resource info\n"); - goto done; - } - - if (!request_region(res->start, resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "can't request region\n"); - goto done; - } - - /* set up the driver-specific struct */ - cs5535_gpio_chip.base = res->start; - cs5535_gpio_chip.pdev = pdev; - spin_lock_init(&cs5535_gpio_chip.lock); - - dev_info(&pdev->dev, "reserved resource region %pR\n", res); - - /* mask out reserved pins */ - mask &= 0x1F7FFFFF; - - /* do not allow pin 28, Power Button, as there's special handling - * in the PMC needed. (note 12, p. 48) */ - mask &= ~(1 << 28); - - if (mask_orig != mask) - dev_info(&pdev->dev, "mask changed from 0x%08lX to 0x%08lX\n", - mask_orig, mask); - - /* finally, register with the generic GPIO API */ - err = gpiochip_add(&cs5535_gpio_chip.chip); - if (err) - goto release_region; - - dev_info(&pdev->dev, "GPIO support successfully loaded.\n"); - return 0; - -release_region: - release_region(res->start, resource_size(res)); -done: - return err; -} - -static int __devexit cs5535_gpio_remove(struct platform_device *pdev) -{ - struct resource *r; - int err; - - err = gpiochip_remove(&cs5535_gpio_chip.chip); - if (err) { - /* uhh? */ - dev_err(&pdev->dev, "unable to remove gpio_chip?\n"); - return err; - } - - r = platform_get_resource(pdev, IORESOURCE_IO, 0); - release_region(r->start, resource_size(r)); - return 0; -} - -static struct platform_driver cs5535_gpio_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - }, - .probe = cs5535_gpio_probe, - .remove = __devexit_p(cs5535_gpio_remove), -}; - -static int __init cs5535_gpio_init(void) -{ - return platform_driver_register(&cs5535_gpio_driver); -} - -static void __exit cs5535_gpio_exit(void) -{ - platform_driver_unregister(&cs5535_gpio_driver); -} - -module_init(cs5535_gpio_init); -module_exit(cs5535_gpio_exit); - -MODULE_AUTHOR("Andres Salomon "); -MODULE_DESCRIPTION("AMD CS5535/CS5536 GPIO driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c new file mode 100644 index 00000000000..7fb60b6bf52 --- /dev/null +++ b/drivers/gpio/gpio-74x164.c @@ -0,0 +1,177 @@ +/* + * 74Hx164 - Generic serial-in/parallel-out 8-bits shift register GPIO driver + * + * Copyright (C) 2010 Gabor Juhos + * Copyright (C) 2010 Miguel Gaio + * + * 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 + +struct gen_74x164_chip { + struct spi_device *spi; + struct gpio_chip gpio_chip; + struct mutex lock; + u8 port_config; +}; + +static struct gen_74x164_chip *gpio_to_chip(struct gpio_chip *gc) +{ + return container_of(gc, struct gen_74x164_chip, gpio_chip); +} + +static int __gen_74x164_write_config(struct gen_74x164_chip *chip) +{ + return spi_write(chip->spi, + &chip->port_config, sizeof(chip->port_config)); +} + +static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset) +{ + struct gen_74x164_chip *chip = gpio_to_chip(gc); + int ret; + + mutex_lock(&chip->lock); + ret = (chip->port_config >> offset) & 0x1; + mutex_unlock(&chip->lock); + + return ret; +} + +static void gen_74x164_set_value(struct gpio_chip *gc, + unsigned offset, int val) +{ + struct gen_74x164_chip *chip = gpio_to_chip(gc); + + mutex_lock(&chip->lock); + if (val) + chip->port_config |= (1 << offset); + else + chip->port_config &= ~(1 << offset); + + __gen_74x164_write_config(chip); + 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; + struct gen_74x164_chip_platform_data *pdata; + int ret; + + pdata = spi->dev.platform_data; + if (!pdata || !pdata->base) { + dev_dbg(&spi->dev, "incorrect or missing platform data\n"); + return -EINVAL; + } + + /* + * bits_per_word cannot be configured in platform data + */ + spi->bits_per_word = 8; + + ret = spi_setup(spi); + if (ret < 0) + return ret; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + mutex_init(&chip->lock); + + dev_set_drvdata(&spi->dev, chip); + + chip->spi = spi; + + 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 = 8; + chip->gpio_chip.can_sleep = 1; + chip->gpio_chip.dev = &spi->dev; + chip->gpio_chip.owner = THIS_MODULE; + + ret = __gen_74x164_write_config(chip); + if (ret) { + dev_err(&spi->dev, "Failed writing: %d\n", ret); + goto exit_destroy; + } + + ret = gpiochip_add(&chip->gpio_chip); + if (ret) + goto exit_destroy; + + return ret; + +exit_destroy: + dev_set_drvdata(&spi->dev, NULL); + mutex_destroy(&chip->lock); + kfree(chip); + return ret; +} + +static int __devexit gen_74x164_remove(struct spi_device *spi) +{ + struct gen_74x164_chip *chip; + int ret; + + chip = dev_get_drvdata(&spi->dev); + if (chip == NULL) + return -ENODEV; + + dev_set_drvdata(&spi->dev, NULL); + + ret = gpiochip_remove(&chip->gpio_chip); + if (!ret) { + mutex_destroy(&chip->lock); + kfree(chip); + } else + dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n", + ret); + + return ret; +} + +static struct spi_driver gen_74x164_driver = { + .driver = { + .name = "74x164", + .owner = THIS_MODULE, + }, + .probe = gen_74x164_probe, + .remove = __devexit_p(gen_74x164_remove), +}; + +static int __init gen_74x164_init(void) +{ + return spi_register_driver(&gen_74x164_driver); +} +subsys_initcall(gen_74x164_init); + +static void __exit gen_74x164_exit(void) +{ + spi_unregister_driver(&gen_74x164_driver); +} +module_exit(gen_74x164_exit); + +MODULE_AUTHOR("Gabor Juhos "); +MODULE_AUTHOR("Miguel Gaio "); +MODULE_DESCRIPTION("GPIO expander driver for 74X164 8-bits shift register"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-ab8500.c b/drivers/gpio/gpio-ab8500.c new file mode 100644 index 00000000000..970053c89ff --- /dev/null +++ b/drivers/gpio/gpio-ab8500.c @@ -0,0 +1,521 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: BIBEK BASU + * License terms: GNU General Public License (GPL) version 2 + * + * 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 + +/* + * GPIO registers offset + * Bank: 0x10 + */ +#define AB8500_GPIO_SEL1_REG 0x00 +#define AB8500_GPIO_SEL2_REG 0x01 +#define AB8500_GPIO_SEL3_REG 0x02 +#define AB8500_GPIO_SEL4_REG 0x03 +#define AB8500_GPIO_SEL5_REG 0x04 +#define AB8500_GPIO_SEL6_REG 0x05 + +#define AB8500_GPIO_DIR1_REG 0x10 +#define AB8500_GPIO_DIR2_REG 0x11 +#define AB8500_GPIO_DIR3_REG 0x12 +#define AB8500_GPIO_DIR4_REG 0x13 +#define AB8500_GPIO_DIR5_REG 0x14 +#define AB8500_GPIO_DIR6_REG 0x15 + +#define AB8500_GPIO_OUT1_REG 0x20 +#define AB8500_GPIO_OUT2_REG 0x21 +#define AB8500_GPIO_OUT3_REG 0x22 +#define AB8500_GPIO_OUT4_REG 0x23 +#define AB8500_GPIO_OUT5_REG 0x24 +#define AB8500_GPIO_OUT6_REG 0x25 + +#define AB8500_GPIO_PUD1_REG 0x30 +#define AB8500_GPIO_PUD2_REG 0x31 +#define AB8500_GPIO_PUD3_REG 0x32 +#define AB8500_GPIO_PUD4_REG 0x33 +#define AB8500_GPIO_PUD5_REG 0x34 +#define AB8500_GPIO_PUD6_REG 0x35 + +#define AB8500_GPIO_IN1_REG 0x40 +#define AB8500_GPIO_IN2_REG 0x41 +#define AB8500_GPIO_IN3_REG 0x42 +#define AB8500_GPIO_IN4_REG 0x43 +#define AB8500_GPIO_IN5_REG 0x44 +#define AB8500_GPIO_IN6_REG 0x45 +#define AB8500_GPIO_ALTFUN_REG 0x45 +#define ALTFUN_REG_INDEX 6 +#define AB8500_NUM_GPIO 42 +#define AB8500_NUM_VIR_GPIO_IRQ 16 + +enum ab8500_gpio_action { + NONE, + STARTUP, + SHUTDOWN, + MASK, + UNMASK +}; + +struct ab8500_gpio { + struct gpio_chip chip; + struct ab8500 *parent; + struct device *dev; + struct mutex lock; + u32 irq_base; + enum ab8500_gpio_action irq_action; + u16 rising; + u16 falling; +}; +/** + * to_ab8500_gpio() - get the pointer to ab8500_gpio + * @chip: Member of the structure ab8500_gpio + */ +static inline struct ab8500_gpio *to_ab8500_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct ab8500_gpio, chip); +} + +static int ab8500_gpio_set_bits(struct gpio_chip *chip, u8 reg, + unsigned offset, int val) +{ + struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip); + u8 pos = offset % 8; + int ret; + + reg = reg + (offset / 8); + ret = abx500_mask_and_set_register_interruptible(ab8500_gpio->dev, + AB8500_MISC, reg, 1 << pos, val << pos); + if (ret < 0) + dev_err(ab8500_gpio->dev, "%s write failed\n", __func__); + return ret; +} +/** + * ab8500_gpio_get() - Get the particular GPIO value + * @chip: Gpio device + * @offset: GPIO number to read + */ +static int ab8500_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip); + u8 mask = 1 << (offset % 8); + u8 reg = AB8500_GPIO_OUT1_REG + (offset / 8); + int ret; + u8 data; + ret = abx500_get_register_interruptible(ab8500_gpio->dev, AB8500_MISC, + reg, &data); + if (ret < 0) { + dev_err(ab8500_gpio->dev, "%s read failed\n", __func__); + return ret; + } + return (data & mask) >> (offset % 8); +} + +static void ab8500_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +{ + struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip); + int ret; + /* Write the data */ + ret = ab8500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, 1); + if (ret < 0) + dev_err(ab8500_gpio->dev, "%s write failed\n", __func__); +} + +static int ab8500_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int val) +{ + int ret; + /* set direction as output */ + ret = ab8500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, 1); + if (ret < 0) + return ret; + /* disable pull down */ + ret = ab8500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG, offset, 1); + if (ret < 0) + return ret; + /* set the output as 1 or 0 */ + return ab8500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, val); + +} + +static int ab8500_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + /* set the register as input */ + return ab8500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, 0); +} + +static int ab8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + /* + * Only some GPIOs are interrupt capable, and they are + * organized in discontiguous clusters: + * + * GPIO6 to GPIO13 + * GPIO24 and GPIO25 + * GPIO36 to GPIO41 + */ + static struct ab8500_gpio_irq_cluster { + int start; + int end; + } clusters[] = { + {.start = 6, .end = 13}, + {.start = 24, .end = 25}, + {.start = 36, .end = 41}, + }; + struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip); + int base = ab8500_gpio->irq_base; + int i; + + for (i = 0; i < ARRAY_SIZE(clusters); i++) { + struct ab8500_gpio_irq_cluster *cluster = &clusters[i]; + + if (offset >= cluster->start && offset <= cluster->end) + return base + offset - cluster->start; + + /* Advance by the number of gpios in this cluster */ + base += cluster->end - cluster->start + 1; + } + + return -EINVAL; +} + +static struct gpio_chip ab8500gpio_chip = { + .label = "ab8500_gpio", + .owner = THIS_MODULE, + .direction_input = ab8500_gpio_direction_input, + .get = ab8500_gpio_get, + .direction_output = ab8500_gpio_direction_output, + .set = ab8500_gpio_set, + .to_irq = ab8500_gpio_to_irq, +}; + +static unsigned int irq_to_rising(unsigned int irq) +{ + struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + int offset = irq - ab8500_gpio->irq_base; + int new_irq = offset + AB8500_INT_GPIO6R + + ab8500_gpio->parent->irq_base; + return new_irq; +} + +static unsigned int irq_to_falling(unsigned int irq) +{ + struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + int offset = irq - ab8500_gpio->irq_base; + int new_irq = offset + AB8500_INT_GPIO6F + + ab8500_gpio->parent->irq_base; + return new_irq; + +} + +static unsigned int rising_to_irq(unsigned int irq, void *dev) +{ + struct ab8500_gpio *ab8500_gpio = dev; + int offset = irq - AB8500_INT_GPIO6R + - ab8500_gpio->parent->irq_base ; + int new_irq = offset + ab8500_gpio->irq_base; + return new_irq; +} + +static unsigned int falling_to_irq(unsigned int irq, void *dev) +{ + struct ab8500_gpio *ab8500_gpio = dev; + int offset = irq - AB8500_INT_GPIO6F + - ab8500_gpio->parent->irq_base ; + int new_irq = offset + ab8500_gpio->irq_base; + return new_irq; + +} + +/* + * IRQ handler + */ + +static irqreturn_t handle_rising(int irq, void *dev) +{ + + handle_nested_irq(rising_to_irq(irq , dev)); + return IRQ_HANDLED; +} + +static irqreturn_t handle_falling(int irq, void *dev) +{ + + handle_nested_irq(falling_to_irq(irq, dev)); + return IRQ_HANDLED; +} + +static void ab8500_gpio_irq_lock(unsigned int irq) +{ + struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + mutex_lock(&ab8500_gpio->lock); +} + +static void ab8500_gpio_irq_sync_unlock(unsigned int irq) +{ + struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + int offset = irq - ab8500_gpio->irq_base; + bool rising = ab8500_gpio->rising & BIT(offset); + bool falling = ab8500_gpio->falling & BIT(offset); + int ret; + + switch (ab8500_gpio->irq_action) { + case STARTUP: + if (rising) + ret = request_threaded_irq(irq_to_rising(irq), + NULL, handle_rising, + IRQF_TRIGGER_RISING, + "ab8500-gpio-r", ab8500_gpio); + if (falling) + ret = request_threaded_irq(irq_to_falling(irq), + NULL, handle_falling, + IRQF_TRIGGER_FALLING, + "ab8500-gpio-f", ab8500_gpio); + break; + case SHUTDOWN: + if (rising) + free_irq(irq_to_rising(irq), ab8500_gpio); + if (falling) + free_irq(irq_to_falling(irq), ab8500_gpio); + break; + case MASK: + if (rising) + disable_irq(irq_to_rising(irq)); + if (falling) + disable_irq(irq_to_falling(irq)); + break; + case UNMASK: + if (rising) + enable_irq(irq_to_rising(irq)); + if (falling) + enable_irq(irq_to_falling(irq)); + break; + case NONE: + break; + } + ab8500_gpio->irq_action = NONE; + ab8500_gpio->rising &= ~(BIT(offset)); + ab8500_gpio->falling &= ~(BIT(offset)); + mutex_unlock(&ab8500_gpio->lock); +} + + +static void ab8500_gpio_irq_mask(unsigned int irq) +{ + struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + ab8500_gpio->irq_action = MASK; +} + +static void ab8500_gpio_irq_unmask(unsigned int irq) +{ + struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + ab8500_gpio->irq_action = UNMASK; +} + +static int ab8500_gpio_irq_set_type(unsigned int irq, unsigned int type) +{ + struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + int offset = irq - ab8500_gpio->irq_base; + + if (type == IRQ_TYPE_EDGE_BOTH) { + ab8500_gpio->rising = BIT(offset); + ab8500_gpio->falling = BIT(offset); + } else if (type == IRQ_TYPE_EDGE_RISING) { + ab8500_gpio->rising = BIT(offset); + } else { + ab8500_gpio->falling = BIT(offset); + } + return 0; +} + +unsigned int ab8500_gpio_irq_startup(unsigned int irq) +{ + struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + ab8500_gpio->irq_action = STARTUP; + return 0; +} + +void ab8500_gpio_irq_shutdown(unsigned int irq) +{ + struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + ab8500_gpio->irq_action = SHUTDOWN; +} + +static struct irq_chip ab8500_gpio_irq_chip = { + .name = "ab8500-gpio", + .startup = ab8500_gpio_irq_startup, + .shutdown = ab8500_gpio_irq_shutdown, + .bus_lock = ab8500_gpio_irq_lock, + .bus_sync_unlock = ab8500_gpio_irq_sync_unlock, + .mask = ab8500_gpio_irq_mask, + .unmask = ab8500_gpio_irq_unmask, + .set_type = ab8500_gpio_irq_set_type, +}; + +static int ab8500_gpio_irq_init(struct ab8500_gpio *ab8500_gpio) +{ + u32 base = ab8500_gpio->irq_base; + int irq; + + for (irq = base; irq < base + AB8500_NUM_VIR_GPIO_IRQ ; irq++) { + set_irq_chip_data(irq, ab8500_gpio); + set_irq_chip_and_handler(irq, &ab8500_gpio_irq_chip, + handle_simple_irq); + set_irq_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + set_irq_noprobe(irq); +#endif + } + + return 0; +} + +static void ab8500_gpio_irq_remove(struct ab8500_gpio *ab8500_gpio) +{ + int base = ab8500_gpio->irq_base; + int irq; + + for (irq = base; irq < base + AB8500_NUM_VIR_GPIO_IRQ; irq++) { +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + set_irq_chip_and_handler(irq, NULL, NULL); + set_irq_chip_data(irq, NULL); + } +} + +static int __devinit ab8500_gpio_probe(struct platform_device *pdev) +{ + struct ab8500_platform_data *ab8500_pdata = + dev_get_platdata(pdev->dev.parent); + struct ab8500_gpio_platform_data *pdata; + struct ab8500_gpio *ab8500_gpio; + int ret; + int i; + + pdata = ab8500_pdata->gpio; + if (!pdata) { + dev_err(&pdev->dev, "gpio platform data missing\n"); + return -ENODEV; + } + + ab8500_gpio = kzalloc(sizeof(struct ab8500_gpio), GFP_KERNEL); + if (ab8500_gpio == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + ab8500_gpio->dev = &pdev->dev; + ab8500_gpio->parent = dev_get_drvdata(pdev->dev.parent); + ab8500_gpio->chip = ab8500gpio_chip; + ab8500_gpio->chip.ngpio = AB8500_NUM_GPIO; + ab8500_gpio->chip.dev = &pdev->dev; + ab8500_gpio->chip.base = pdata->gpio_base; + ab8500_gpio->irq_base = pdata->irq_base; + /* initialize the lock */ + mutex_init(&ab8500_gpio->lock); + /* + * AB8500 core will handle and clear the IRQ + * configre GPIO based on config-reg value. + * These values are for selecting the PINs as + * GPIO or alternate function + */ + for (i = AB8500_GPIO_SEL1_REG; i <= AB8500_GPIO_SEL6_REG; i++) { + ret = abx500_set_register_interruptible(ab8500_gpio->dev, + AB8500_MISC, i, + pdata->config_reg[i]); + if (ret < 0) + goto out_free; + } + ret = abx500_set_register_interruptible(ab8500_gpio->dev, AB8500_MISC, + AB8500_GPIO_ALTFUN_REG, + pdata->config_reg[ALTFUN_REG_INDEX]); + if (ret < 0) + goto out_free; + + ret = ab8500_gpio_irq_init(ab8500_gpio); + if (ret) + goto out_free; + ret = gpiochip_add(&ab8500_gpio->chip); + if (ret) { + dev_err(&pdev->dev, "unable to add gpiochip: %d\n", + ret); + goto out_rem_irq; + } + platform_set_drvdata(pdev, ab8500_gpio); + return 0; + +out_rem_irq: + ab8500_gpio_irq_remove(ab8500_gpio); +out_free: + mutex_destroy(&ab8500_gpio->lock); + kfree(ab8500_gpio); + return ret; +} + +/* + * ab8500_gpio_remove() - remove Ab8500-gpio driver + * @pdev : Platform device registered + */ +static int __devexit ab8500_gpio_remove(struct platform_device *pdev) +{ + struct ab8500_gpio *ab8500_gpio = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(&ab8500_gpio->chip); + if (ret < 0) { + dev_err(ab8500_gpio->dev, "unable to remove gpiochip: %d\n", + ret); + return ret; + } + + platform_set_drvdata(pdev, NULL); + mutex_destroy(&ab8500_gpio->lock); + kfree(ab8500_gpio); + + return 0; +} + +static struct platform_driver ab8500_gpio_driver = { + .driver = { + .name = "ab8500-gpio", + .owner = THIS_MODULE, + }, + .probe = ab8500_gpio_probe, + .remove = __devexit_p(ab8500_gpio_remove), +}; + +static int __init ab8500_gpio_init(void) +{ + return platform_driver_register(&ab8500_gpio_driver); +} +arch_initcall(ab8500_gpio_init); + +static void __exit ab8500_gpio_exit(void) +{ + platform_driver_unregister(&ab8500_gpio_driver); +} +module_exit(ab8500_gpio_exit); + +MODULE_AUTHOR("BIBEK BASU "); +MODULE_DESCRIPTION("Driver allows to use AB8500 unused pins to be used as GPIO"); +MODULE_ALIAS("AB8500 GPIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-adp5520.c b/drivers/gpio/gpio-adp5520.c new file mode 100644 index 00000000000..9f278153700 --- /dev/null +++ b/drivers/gpio/gpio-adp5520.c @@ -0,0 +1,211 @@ +/* + * GPIO driver for Analog Devices ADP5520 MFD PMICs + * + * Copyright 2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include + +#include + +struct adp5520_gpio { + struct device *master; + struct gpio_chip gpio_chip; + unsigned char lut[ADP5520_MAXGPIOS]; + unsigned long output; +}; + +static int adp5520_gpio_get_value(struct gpio_chip *chip, unsigned off) +{ + struct adp5520_gpio *dev; + uint8_t reg_val; + + dev = container_of(chip, struct adp5520_gpio, gpio_chip); + + /* + * There are dedicated registers for GPIO IN/OUT. + * Make sure we return the right value, even when configured as output + */ + + if (test_bit(off, &dev->output)) + adp5520_read(dev->master, ADP5520_GPIO_OUT, ®_val); + else + adp5520_read(dev->master, ADP5520_GPIO_IN, ®_val); + + return !!(reg_val & dev->lut[off]); +} + +static void adp5520_gpio_set_value(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5520_gpio *dev; + dev = container_of(chip, struct adp5520_gpio, gpio_chip); + + if (val) + adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]); + else + adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]); +} + +static int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off) +{ + struct adp5520_gpio *dev; + dev = container_of(chip, struct adp5520_gpio, gpio_chip); + + clear_bit(off, &dev->output); + + return adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_2, + dev->lut[off]); +} + +static int adp5520_gpio_direction_output(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5520_gpio *dev; + int ret = 0; + dev = container_of(chip, struct adp5520_gpio, gpio_chip); + + set_bit(off, &dev->output); + + if (val) + ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, + dev->lut[off]); + else + ret |= adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, + dev->lut[off]); + + ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_2, + dev->lut[off]); + + return ret; +} + +static int __devinit adp5520_gpio_probe(struct platform_device *pdev) +{ + struct adp5520_gpio_platform_data *pdata = pdev->dev.platform_data; + struct adp5520_gpio *dev; + struct gpio_chip *gc; + int ret, i, gpios; + unsigned char ctl_mask = 0; + + if (pdata == NULL) { + dev_err(&pdev->dev, "missing platform data\n"); + return -ENODEV; + } + + if (pdev->id != ID_ADP5520) { + dev_err(&pdev->dev, "only ADP5520 supports GPIO\n"); + return -ENODEV; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + dev_err(&pdev->dev, "failed to alloc memory\n"); + return -ENOMEM; + } + + dev->master = pdev->dev.parent; + + for (gpios = 0, i = 0; i < ADP5520_MAXGPIOS; i++) + if (pdata->gpio_en_mask & (1 << i)) + dev->lut[gpios++] = 1 << i; + + if (gpios < 1) { + ret = -EINVAL; + goto err; + } + + gc = &dev->gpio_chip; + gc->direction_input = adp5520_gpio_direction_input; + gc->direction_output = adp5520_gpio_direction_output; + gc->get = adp5520_gpio_get_value; + gc->set = adp5520_gpio_set_value; + gc->can_sleep = 1; + + gc->base = pdata->gpio_start; + gc->ngpio = gpios; + gc->label = pdev->name; + gc->owner = THIS_MODULE; + + ret = adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_1, + pdata->gpio_en_mask); + + if (pdata->gpio_en_mask & ADP5520_GPIO_C3) + ctl_mask |= ADP5520_C3_MODE; + + if (pdata->gpio_en_mask & ADP5520_GPIO_R3) + ctl_mask |= ADP5520_R3_MODE; + + if (ctl_mask) + ret = adp5520_set_bits(dev->master, ADP5520_LED_CONTROL, + ctl_mask); + + ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP, + pdata->gpio_pullup_mask); + + if (ret) { + dev_err(&pdev->dev, "failed to write\n"); + goto err; + } + + ret = gpiochip_add(&dev->gpio_chip); + if (ret) + goto err; + + platform_set_drvdata(pdev, dev); + return 0; + +err: + kfree(dev); + return ret; +} + +static int __devexit adp5520_gpio_remove(struct platform_device *pdev) +{ + struct adp5520_gpio *dev; + int ret; + + dev = platform_get_drvdata(pdev); + ret = gpiochip_remove(&dev->gpio_chip); + if (ret) { + dev_err(&pdev->dev, "%s failed, %d\n", + "gpiochip_remove()", ret); + return ret; + } + + kfree(dev); + return 0; +} + +static struct platform_driver adp5520_gpio_driver = { + .driver = { + .name = "adp5520-gpio", + .owner = THIS_MODULE, + }, + .probe = adp5520_gpio_probe, + .remove = __devexit_p(adp5520_gpio_remove), +}; + +static int __init adp5520_gpio_init(void) +{ + return platform_driver_register(&adp5520_gpio_driver); +} +module_init(adp5520_gpio_init); + +static void __exit adp5520_gpio_exit(void) +{ + platform_driver_unregister(&adp5520_gpio_driver); +} +module_exit(adp5520_gpio_exit); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("GPIO ADP5520 Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:adp5520-gpio"); diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c new file mode 100644 index 00000000000..3525ad91877 --- /dev/null +++ b/drivers/gpio/gpio-adp5588.c @@ -0,0 +1,503 @@ +/* + * GPIO Chip driver for Analog Devices + * ADP5588/ADP5587 I/O Expander and QWERTY Keypad Controller + * + * Copyright 2009-2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRV_NAME "adp5588-gpio" + +/* + * Early pre 4.0 Silicon required to delay readout by at least 25ms, + * since the Event Counter Register updated 25ms after the interrupt + * asserted. + */ +#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4) + +struct adp5588_gpio { + struct i2c_client *client; + struct gpio_chip gpio_chip; + struct mutex lock; /* protect cached dir, dat_out */ + /* protect serialized access to the interrupt controller bus */ + struct mutex irq_lock; + unsigned gpio_start; + unsigned irq_base; + uint8_t dat_out[3]; + uint8_t dir[3]; + uint8_t int_lvl[3]; + uint8_t int_en[3]; + uint8_t irq_mask[3]; + uint8_t irq_stat[3]; +}; + +static int adp5588_gpio_read(struct i2c_client *client, u8 reg) +{ + int ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) + dev_err(&client->dev, "Read Error\n"); + + return ret; +} + +static int adp5588_gpio_write(struct i2c_client *client, u8 reg, u8 val) +{ + int ret = i2c_smbus_write_byte_data(client, reg, val); + + if (ret < 0) + dev_err(&client->dev, "Write Error\n"); + + return ret; +} + +static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off) +{ + struct adp5588_gpio *dev = + container_of(chip, struct adp5588_gpio, gpio_chip); + + return !!(adp5588_gpio_read(dev->client, + GPIO_DAT_STAT1 + ADP5588_BANK(off)) & ADP5588_BIT(off)); +} + +static void adp5588_gpio_set_value(struct gpio_chip *chip, + unsigned off, int val) +{ + unsigned bank, bit; + struct adp5588_gpio *dev = + container_of(chip, struct adp5588_gpio, gpio_chip); + + bank = ADP5588_BANK(off); + bit = ADP5588_BIT(off); + + mutex_lock(&dev->lock); + if (val) + dev->dat_out[bank] |= bit; + else + dev->dat_out[bank] &= ~bit; + + adp5588_gpio_write(dev->client, GPIO_DAT_OUT1 + bank, + dev->dat_out[bank]); + mutex_unlock(&dev->lock); +} + +static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off) +{ + int ret; + unsigned bank; + struct adp5588_gpio *dev = + container_of(chip, struct adp5588_gpio, gpio_chip); + + bank = ADP5588_BANK(off); + + mutex_lock(&dev->lock); + dev->dir[bank] &= ~ADP5588_BIT(off); + ret = adp5588_gpio_write(dev->client, GPIO_DIR1 + bank, dev->dir[bank]); + mutex_unlock(&dev->lock); + + return ret; +} + +static int adp5588_gpio_direction_output(struct gpio_chip *chip, + unsigned off, int val) +{ + int ret; + unsigned bank, bit; + struct adp5588_gpio *dev = + container_of(chip, struct adp5588_gpio, gpio_chip); + + bank = ADP5588_BANK(off); + bit = ADP5588_BIT(off); + + mutex_lock(&dev->lock); + dev->dir[bank] |= bit; + + if (val) + dev->dat_out[bank] |= bit; + else + dev->dat_out[bank] &= ~bit; + + ret = adp5588_gpio_write(dev->client, GPIO_DAT_OUT1 + bank, + dev->dat_out[bank]); + ret |= adp5588_gpio_write(dev->client, GPIO_DIR1 + bank, + dev->dir[bank]); + mutex_unlock(&dev->lock); + + return ret; +} + +#ifdef CONFIG_GPIO_ADP5588_IRQ +static int adp5588_gpio_to_irq(struct gpio_chip *chip, unsigned off) +{ + struct adp5588_gpio *dev = + container_of(chip, struct adp5588_gpio, gpio_chip); + return dev->irq_base + off; +} + +static void adp5588_irq_bus_lock(struct irq_data *d) +{ + struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); + + mutex_lock(&dev->irq_lock); +} + + /* + * genirq core code can issue chip->mask/unmask from atomic context. + * This doesn't work for slow busses where an access needs to sleep. + * bus_sync_unlock() is therefore called outside the atomic context, + * syncs the current irq mask state with the slow external controller + * and unlocks the bus. + */ + +static void adp5588_irq_bus_sync_unlock(struct irq_data *d) +{ + struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); + int i; + + for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) + if (dev->int_en[i] ^ dev->irq_mask[i]) { + dev->int_en[i] = dev->irq_mask[i]; + adp5588_gpio_write(dev->client, GPIO_INT_EN1 + i, + dev->int_en[i]); + } + + mutex_unlock(&dev->irq_lock); +} + +static void adp5588_irq_mask(struct irq_data *d) +{ + struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); + unsigned gpio = d->irq - dev->irq_base; + + dev->irq_mask[ADP5588_BANK(gpio)] &= ~ADP5588_BIT(gpio); +} + +static void adp5588_irq_unmask(struct irq_data *d) +{ + struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); + unsigned gpio = d->irq - dev->irq_base; + + dev->irq_mask[ADP5588_BANK(gpio)] |= ADP5588_BIT(gpio); +} + +static int adp5588_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); + uint16_t gpio = d->irq - dev->irq_base; + unsigned bank, bit; + + if ((type & IRQ_TYPE_EDGE_BOTH)) { + dev_err(&dev->client->dev, "irq %d: unsupported type %d\n", + d->irq, type); + return -EINVAL; + } + + bank = ADP5588_BANK(gpio); + bit = ADP5588_BIT(gpio); + + if (type & IRQ_TYPE_LEVEL_HIGH) + dev->int_lvl[bank] |= bit; + else if (type & IRQ_TYPE_LEVEL_LOW) + dev->int_lvl[bank] &= ~bit; + else + return -EINVAL; + + adp5588_gpio_direction_input(&dev->gpio_chip, gpio); + adp5588_gpio_write(dev->client, GPIO_INT_LVL1 + bank, + dev->int_lvl[bank]); + + return 0; +} + +static struct irq_chip adp5588_irq_chip = { + .name = "adp5588", + .irq_mask = adp5588_irq_mask, + .irq_unmask = adp5588_irq_unmask, + .irq_bus_lock = adp5588_irq_bus_lock, + .irq_bus_sync_unlock = adp5588_irq_bus_sync_unlock, + .irq_set_type = adp5588_irq_set_type, +}; + +static int adp5588_gpio_read_intstat(struct i2c_client *client, u8 *buf) +{ + int ret = i2c_smbus_read_i2c_block_data(client, GPIO_INT_STAT1, 3, buf); + + if (ret < 0) + dev_err(&client->dev, "Read INT_STAT Error\n"); + + return ret; +} + +static irqreturn_t adp5588_irq_handler(int irq, void *devid) +{ + struct adp5588_gpio *dev = devid; + unsigned status, bank, bit, pending; + int ret; + status = adp5588_gpio_read(dev->client, INT_STAT); + + if (status & ADP5588_GPI_INT) { + ret = adp5588_gpio_read_intstat(dev->client, dev->irq_stat); + if (ret < 0) + memset(dev->irq_stat, 0, ARRAY_SIZE(dev->irq_stat)); + + for (bank = 0; bank <= ADP5588_BANK(ADP5588_MAXGPIO); + bank++, bit = 0) { + pending = dev->irq_stat[bank] & dev->irq_mask[bank]; + + while (pending) { + if (pending & (1 << bit)) { + handle_nested_irq(dev->irq_base + + (bank << 3) + bit); + pending &= ~(1 << bit); + + } + bit++; + } + } + } + + adp5588_gpio_write(dev->client, INT_STAT, status); /* Status is W1C */ + + return IRQ_HANDLED; +} + +static int adp5588_irq_setup(struct adp5588_gpio *dev) +{ + struct i2c_client *client = dev->client; + struct adp5588_gpio_platform_data *pdata = client->dev.platform_data; + unsigned gpio; + int ret; + + adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC); + adp5588_gpio_write(client, INT_STAT, -1); /* status is W1C */ + adp5588_gpio_read_intstat(client, dev->irq_stat); /* read to clear */ + + dev->irq_base = pdata->irq_base; + mutex_init(&dev->irq_lock); + + for (gpio = 0; gpio < dev->gpio_chip.ngpio; gpio++) { + int irq = gpio + dev->irq_base; + irq_set_chip_data(irq, dev); + irq_set_chip_and_handler(irq, &adp5588_irq_chip, + handle_level_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + /* + * ARM needs us to explicitly flag the IRQ as VALID, + * once we do so, it will also set the noprobe. + */ + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + } + + ret = request_threaded_irq(client->irq, + NULL, + adp5588_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(&client->dev), dev); + if (ret) { + dev_err(&client->dev, "failed to request irq %d\n", + client->irq); + goto out; + } + + dev->gpio_chip.to_irq = adp5588_gpio_to_irq; + adp5588_gpio_write(client, CFG, + ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_GPI_INT); + + return 0; + +out: + dev->irq_base = 0; + return ret; +} + +static void adp5588_irq_teardown(struct adp5588_gpio *dev) +{ + if (dev->irq_base) + free_irq(dev->client->irq, dev); +} + +#else +static int adp5588_irq_setup(struct adp5588_gpio *dev) +{ + struct i2c_client *client = dev->client; + dev_warn(&client->dev, "interrupt support not compiled in\n"); + + return 0; +} + +static void adp5588_irq_teardown(struct adp5588_gpio *dev) +{ +} +#endif /* CONFIG_GPIO_ADP5588_IRQ */ + +static int __devinit adp5588_gpio_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adp5588_gpio_platform_data *pdata = client->dev.platform_data; + struct adp5588_gpio *dev; + struct gpio_chip *gc; + int ret, i, revid; + + if (pdata == NULL) { + dev_err(&client->dev, "missing platform data\n"); + return -ENODEV; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); + return -EIO; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + dev_err(&client->dev, "failed to alloc memory\n"); + return -ENOMEM; + } + + dev->client = client; + + gc = &dev->gpio_chip; + gc->direction_input = adp5588_gpio_direction_input; + gc->direction_output = adp5588_gpio_direction_output; + gc->get = adp5588_gpio_get_value; + gc->set = adp5588_gpio_set_value; + gc->can_sleep = 1; + + gc->base = pdata->gpio_start; + gc->ngpio = ADP5588_MAXGPIO; + gc->label = client->name; + gc->owner = THIS_MODULE; + + mutex_init(&dev->lock); + + ret = adp5588_gpio_read(dev->client, DEV_ID); + if (ret < 0) + goto err; + + revid = ret & ADP5588_DEVICE_ID_MASK; + + for (i = 0, ret = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) { + dev->dat_out[i] = adp5588_gpio_read(client, GPIO_DAT_OUT1 + i); + dev->dir[i] = adp5588_gpio_read(client, GPIO_DIR1 + i); + ret |= adp5588_gpio_write(client, KP_GPIO1 + i, 0); + ret |= adp5588_gpio_write(client, GPIO_PULL1 + i, + (pdata->pullup_dis_mask >> (8 * i)) & 0xFF); + ret |= adp5588_gpio_write(client, GPIO_INT_EN1 + i, 0); + if (ret) + goto err; + } + + if (pdata->irq_base) { + if (WA_DELAYED_READOUT_REVID(revid)) { + dev_warn(&client->dev, "GPIO int not supported\n"); + } else { + ret = adp5588_irq_setup(dev); + if (ret) + goto err; + } + } + + ret = gpiochip_add(&dev->gpio_chip); + if (ret) + goto err_irq; + + dev_info(&client->dev, "gpios %d..%d (IRQ Base %d) on a %s Rev. %d\n", + gc->base, gc->base + gc->ngpio - 1, + pdata->irq_base, client->name, revid); + + if (pdata->setup) { + ret = pdata->setup(client, gc->base, gc->ngpio, pdata->context); + if (ret < 0) + dev_warn(&client->dev, "setup failed, %d\n", ret); + } + + i2c_set_clientdata(client, dev); + + return 0; + +err_irq: + adp5588_irq_teardown(dev); +err: + kfree(dev); + return ret; +} + +static int __devexit adp5588_gpio_remove(struct i2c_client *client) +{ + struct adp5588_gpio_platform_data *pdata = client->dev.platform_data; + struct adp5588_gpio *dev = i2c_get_clientdata(client); + int ret; + + if (pdata->teardown) { + ret = pdata->teardown(client, + dev->gpio_chip.base, dev->gpio_chip.ngpio, + pdata->context); + if (ret < 0) { + dev_err(&client->dev, "teardown failed %d\n", ret); + return ret; + } + } + + if (dev->irq_base) + free_irq(dev->client->irq, dev); + + ret = gpiochip_remove(&dev->gpio_chip); + if (ret) { + dev_err(&client->dev, "gpiochip_remove failed %d\n", ret); + return ret; + } + + kfree(dev); + return 0; +} + +static const struct i2c_device_id adp5588_gpio_id[] = { + {DRV_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, adp5588_gpio_id); + +static struct i2c_driver adp5588_gpio_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = adp5588_gpio_probe, + .remove = __devexit_p(adp5588_gpio_remove), + .id_table = adp5588_gpio_id, +}; + +static int __init adp5588_gpio_init(void) +{ + return i2c_add_driver(&adp5588_gpio_driver); +} + +module_init(adp5588_gpio_init); + +static void __exit adp5588_gpio_exit(void) +{ + i2c_del_driver(&adp5588_gpio_driver); +} + +module_exit(adp5588_gpio_exit); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("GPIO ADP5588 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c new file mode 100644 index 00000000000..aa4f09ad3ce --- /dev/null +++ b/drivers/gpio/gpio-bt8xx.c @@ -0,0 +1,348 @@ +/* + + bt8xx GPIO abuser + + Copyright (C) 2008 Michael Buesch + + Please do _only_ contact the people listed _above_ with issues related to this driver. + All the other people listed below are not related to this driver. Their names + are only here, because this driver is derived from the bt848 driver. + + + Derived from the bt848 driver: + + Copyright (C) 1996,97,98 Ralph Metzler + & Marcus Metzler + (c) 1999-2002 Gerd Knorr + + some v4l2 code lines are taken from Justin's bttv2 driver which is + (c) 2000 Justin Schoeman + + V4L1 removal from: + (c) 2005-2006 Nickolay V. Shmyrev + + Fixes to be fully V4L2 compliant by + (c) 2006 Mauro Carvalho Chehab + + Cropping and overscan support + Copyright (C) 2005, 2006 Michael H. Schimek + Sponsored by OPQ Systems AB + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include + +/* Steal the hardware definitions from the bttv driver. */ +#include "../media/video/bt8xx/bt848.h" + + +#define BT8XXGPIO_NR_GPIOS 24 /* We have 24 GPIO pins */ + + +struct bt8xxgpio { + spinlock_t lock; + + void __iomem *mmio; + struct pci_dev *pdev; + struct gpio_chip gpio; + +#ifdef CONFIG_PM + u32 saved_outen; + u32 saved_data; +#endif +}; + +#define bgwrite(dat, adr) writel((dat), bg->mmio+(adr)) +#define bgread(adr) readl(bg->mmio+(adr)) + + +static int modparam_gpiobase = -1/* dynamic */; +module_param_named(gpiobase, modparam_gpiobase, int, 0444); +MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the default."); + + +static int bt8xxgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) +{ + struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); + unsigned long flags; + u32 outen, data; + + spin_lock_irqsave(&bg->lock, flags); + + data = bgread(BT848_GPIO_DATA); + data &= ~(1 << nr); + bgwrite(data, BT848_GPIO_DATA); + + outen = bgread(BT848_GPIO_OUT_EN); + outen &= ~(1 << nr); + bgwrite(outen, BT848_GPIO_OUT_EN); + + spin_unlock_irqrestore(&bg->lock, flags); + + return 0; +} + +static int bt8xxgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) +{ + struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&bg->lock, flags); + val = bgread(BT848_GPIO_DATA); + spin_unlock_irqrestore(&bg->lock, flags); + + return !!(val & (1 << nr)); +} + +static int bt8xxgpio_gpio_direction_output(struct gpio_chip *gpio, + unsigned nr, int val) +{ + struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); + unsigned long flags; + u32 outen, data; + + spin_lock_irqsave(&bg->lock, flags); + + outen = bgread(BT848_GPIO_OUT_EN); + outen |= (1 << nr); + bgwrite(outen, BT848_GPIO_OUT_EN); + + data = bgread(BT848_GPIO_DATA); + if (val) + data |= (1 << nr); + else + data &= ~(1 << nr); + bgwrite(data, BT848_GPIO_DATA); + + spin_unlock_irqrestore(&bg->lock, flags); + + return 0; +} + +static void bt8xxgpio_gpio_set(struct gpio_chip *gpio, + unsigned nr, int val) +{ + struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); + unsigned long flags; + u32 data; + + spin_lock_irqsave(&bg->lock, flags); + + data = bgread(BT848_GPIO_DATA); + if (val) + data |= (1 << nr); + else + data &= ~(1 << nr); + bgwrite(data, BT848_GPIO_DATA); + + spin_unlock_irqrestore(&bg->lock, flags); +} + +static void bt8xxgpio_gpio_setup(struct bt8xxgpio *bg) +{ + struct gpio_chip *c = &bg->gpio; + + c->label = dev_name(&bg->pdev->dev); + c->owner = THIS_MODULE; + c->direction_input = bt8xxgpio_gpio_direction_input; + c->get = bt8xxgpio_gpio_get; + c->direction_output = bt8xxgpio_gpio_direction_output; + c->set = bt8xxgpio_gpio_set; + c->dbg_show = NULL; + c->base = modparam_gpiobase; + c->ngpio = BT8XXGPIO_NR_GPIOS; + c->can_sleep = 0; +} + +static int bt8xxgpio_probe(struct pci_dev *dev, + const struct pci_device_id *pci_id) +{ + struct bt8xxgpio *bg; + int err; + + bg = kzalloc(sizeof(*bg), GFP_KERNEL); + if (!bg) + return -ENOMEM; + + bg->pdev = dev; + spin_lock_init(&bg->lock); + + err = pci_enable_device(dev); + if (err) { + printk(KERN_ERR "bt8xxgpio: Can't enable device.\n"); + goto err_freebg; + } + if (!request_mem_region(pci_resource_start(dev, 0), + pci_resource_len(dev, 0), + "bt8xxgpio")) { + printk(KERN_WARNING "bt8xxgpio: Can't request iomem (0x%llx).\n", + (unsigned long long)pci_resource_start(dev, 0)); + err = -EBUSY; + goto err_disable; + } + pci_set_master(dev); + pci_set_drvdata(dev, bg); + + bg->mmio = ioremap(pci_resource_start(dev, 0), 0x1000); + if (!bg->mmio) { + printk(KERN_ERR "bt8xxgpio: ioremap() failed\n"); + err = -EIO; + goto err_release_mem; + } + + /* Disable interrupts */ + bgwrite(0, BT848_INT_MASK); + + /* gpio init */ + bgwrite(0, BT848_GPIO_DMA_CTL); + bgwrite(0, BT848_GPIO_REG_INP); + bgwrite(0, BT848_GPIO_OUT_EN); + + bt8xxgpio_gpio_setup(bg); + err = gpiochip_add(&bg->gpio); + if (err) { + printk(KERN_ERR "bt8xxgpio: Failed to register GPIOs\n"); + goto err_release_mem; + } + + printk(KERN_INFO "bt8xxgpio: Abusing BT8xx card for GPIOs %d to %d\n", + bg->gpio.base, bg->gpio.base + BT8XXGPIO_NR_GPIOS - 1); + + return 0; + +err_release_mem: + release_mem_region(pci_resource_start(dev, 0), + pci_resource_len(dev, 0)); + pci_set_drvdata(dev, NULL); +err_disable: + pci_disable_device(dev); +err_freebg: + kfree(bg); + + return err; +} + +static void bt8xxgpio_remove(struct pci_dev *pdev) +{ + struct bt8xxgpio *bg = pci_get_drvdata(pdev); + + gpiochip_remove(&bg->gpio); + + bgwrite(0, BT848_INT_MASK); + bgwrite(~0x0, BT848_INT_STAT); + bgwrite(0x0, BT848_GPIO_OUT_EN); + + iounmap(bg->mmio); + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + pci_disable_device(pdev); + + pci_set_drvdata(pdev, NULL); + kfree(bg); +} + +#ifdef CONFIG_PM +static int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct bt8xxgpio *bg = pci_get_drvdata(pdev); + unsigned long flags; + + spin_lock_irqsave(&bg->lock, flags); + + bg->saved_outen = bgread(BT848_GPIO_OUT_EN); + bg->saved_data = bgread(BT848_GPIO_DATA); + + bgwrite(0, BT848_INT_MASK); + bgwrite(~0x0, BT848_INT_STAT); + bgwrite(0x0, BT848_GPIO_OUT_EN); + + spin_unlock_irqrestore(&bg->lock, flags); + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int bt8xxgpio_resume(struct pci_dev *pdev) +{ + struct bt8xxgpio *bg = pci_get_drvdata(pdev); + unsigned long flags; + int err; + + pci_set_power_state(pdev, 0); + err = pci_enable_device(pdev); + if (err) + return err; + pci_restore_state(pdev); + + spin_lock_irqsave(&bg->lock, flags); + + bgwrite(0, BT848_INT_MASK); + bgwrite(0, BT848_GPIO_DMA_CTL); + bgwrite(0, BT848_GPIO_REG_INP); + bgwrite(bg->saved_outen, BT848_GPIO_OUT_EN); + bgwrite(bg->saved_data & bg->saved_outen, + BT848_GPIO_DATA); + + spin_unlock_irqrestore(&bg->lock, flags); + + return 0; +} +#else +#define bt8xxgpio_suspend NULL +#define bt8xxgpio_resume NULL +#endif /* CONFIG_PM */ + +static struct pci_device_id bt8xxgpio_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879) }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, bt8xxgpio_pci_tbl); + +static struct pci_driver bt8xxgpio_pci_driver = { + .name = "bt8xxgpio", + .id_table = bt8xxgpio_pci_tbl, + .probe = bt8xxgpio_probe, + .remove = bt8xxgpio_remove, + .suspend = bt8xxgpio_suspend, + .resume = bt8xxgpio_resume, +}; + +static int __init bt8xxgpio_init(void) +{ + return pci_register_driver(&bt8xxgpio_pci_driver); +} +module_init(bt8xxgpio_init) + +static void __exit bt8xxgpio_exit(void) +{ + pci_unregister_driver(&bt8xxgpio_pci_driver); +} +module_exit(bt8xxgpio_exit) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Buesch"); +MODULE_DESCRIPTION("Abuse a BT8xx framegrabber card as generic GPIO card"); diff --git a/drivers/gpio/gpio-cs5535.c b/drivers/gpio/gpio-cs5535.c new file mode 100644 index 00000000000..6e16cba56ad --- /dev/null +++ b/drivers/gpio/gpio-cs5535.c @@ -0,0 +1,401 @@ +/* + * AMD CS5535/CS5536 GPIO driver + * Copyright (C) 2006 Advanced Micro Devices, Inc. + * Copyright (C) 2007-2009 Andres Salomon + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "cs5535-gpio" + +/* + * Some GPIO pins + * 31-29,23 : reserved (always mask out) + * 28 : Power Button + * 26 : PME# + * 22-16 : LPC + * 14,15 : SMBus + * 9,8 : UART1 + * 7 : PCI INTB + * 3,4 : UART2/DDC + * 2 : IDE_IRQ0 + * 1 : AC_BEEP + * 0 : PCI INTA + * + * If a mask was not specified, allow all except + * reserved and Power Button + */ +#define GPIO_DEFAULT_MASK 0x0F7FFFFF + +static ulong mask = GPIO_DEFAULT_MASK; +module_param_named(mask, mask, ulong, 0444); +MODULE_PARM_DESC(mask, "GPIO channel mask."); + +static struct cs5535_gpio_chip { + struct gpio_chip chip; + resource_size_t base; + + struct platform_device *pdev; + spinlock_t lock; +} cs5535_gpio_chip; + +/* + * The CS5535/CS5536 GPIOs support a number of extra features not defined + * by the gpio_chip API, so these are exported. For a full list of the + * registers, see include/linux/cs5535.h. + */ + +static void errata_outl(struct cs5535_gpio_chip *chip, u32 val, + unsigned int reg) +{ + unsigned long addr = chip->base + 0x80 + reg; + + /* + * According to the CS5536 errata (#36), after suspend + * a write to the high bank GPIO register will clear all + * non-selected bits; the recommended workaround is a + * read-modify-write operation. + * + * Don't apply this errata to the edge status GPIOs, as writing + * to their lower bits will clear them. + */ + if (reg != GPIO_POSITIVE_EDGE_STS && reg != GPIO_NEGATIVE_EDGE_STS) { + if (val & 0xffff) + val |= (inl(addr) & 0xffff); /* ignore the high bits */ + else + val |= (inl(addr) ^ (val >> 16)); + } + outl(val, addr); +} + +static void __cs5535_gpio_set(struct cs5535_gpio_chip *chip, unsigned offset, + unsigned int reg) +{ + if (offset < 16) + /* low bank register */ + outl(1 << offset, chip->base + reg); + else + /* high bank register */ + errata_outl(chip, 1 << (offset - 16), reg); +} + +void cs5535_gpio_set(unsigned offset, unsigned int reg) +{ + struct cs5535_gpio_chip *chip = &cs5535_gpio_chip; + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + __cs5535_gpio_set(chip, offset, reg); + spin_unlock_irqrestore(&chip->lock, flags); +} +EXPORT_SYMBOL_GPL(cs5535_gpio_set); + +static void __cs5535_gpio_clear(struct cs5535_gpio_chip *chip, unsigned offset, + unsigned int reg) +{ + if (offset < 16) + /* low bank register */ + outl(1 << (offset + 16), chip->base + reg); + else + /* high bank register */ + errata_outl(chip, 1 << offset, reg); +} + +void cs5535_gpio_clear(unsigned offset, unsigned int reg) +{ + struct cs5535_gpio_chip *chip = &cs5535_gpio_chip; + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + __cs5535_gpio_clear(chip, offset, reg); + spin_unlock_irqrestore(&chip->lock, flags); +} +EXPORT_SYMBOL_GPL(cs5535_gpio_clear); + +int cs5535_gpio_isset(unsigned offset, unsigned int reg) +{ + struct cs5535_gpio_chip *chip = &cs5535_gpio_chip; + unsigned long flags; + long val; + + spin_lock_irqsave(&chip->lock, flags); + if (offset < 16) + /* low bank register */ + val = inl(chip->base + reg); + else { + /* high bank register */ + val = inl(chip->base + 0x80 + reg); + offset -= 16; + } + spin_unlock_irqrestore(&chip->lock, flags); + + return (val & (1 << offset)) ? 1 : 0; +} +EXPORT_SYMBOL_GPL(cs5535_gpio_isset); + +int cs5535_gpio_set_irq(unsigned group, unsigned irq) +{ + uint32_t lo, hi; + + if (group > 7 || irq > 15) + return -EINVAL; + + rdmsr(MSR_PIC_ZSEL_HIGH, lo, hi); + + lo &= ~(0xF << (group * 4)); + lo |= (irq & 0xF) << (group * 4); + + wrmsr(MSR_PIC_ZSEL_HIGH, lo, hi); + return 0; +} +EXPORT_SYMBOL_GPL(cs5535_gpio_set_irq); + +void cs5535_gpio_setup_event(unsigned offset, int pair, int pme) +{ + struct cs5535_gpio_chip *chip = &cs5535_gpio_chip; + uint32_t shift = (offset % 8) * 4; + unsigned long flags; + uint32_t val; + + if (offset >= 24) + offset = GPIO_MAP_W; + else if (offset >= 16) + offset = GPIO_MAP_Z; + else if (offset >= 8) + offset = GPIO_MAP_Y; + else + offset = GPIO_MAP_X; + + spin_lock_irqsave(&chip->lock, flags); + val = inl(chip->base + offset); + + /* Clear whatever was there before */ + val &= ~(0xF << shift); + + /* Set the new value */ + val |= ((pair & 7) << shift); + + /* Set the PME bit if this is a PME event */ + if (pme) + val |= (1 << (shift + 3)); + + outl(val, chip->base + offset); + spin_unlock_irqrestore(&chip->lock, flags); +} +EXPORT_SYMBOL_GPL(cs5535_gpio_setup_event); + +/* + * Generic gpio_chip API support. + */ + +static int chip_gpio_request(struct gpio_chip *c, unsigned offset) +{ + struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c; + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + /* check if this pin is available */ + if ((mask & (1 << offset)) == 0) { + dev_info(&chip->pdev->dev, + "pin %u is not available (check mask)\n", offset); + spin_unlock_irqrestore(&chip->lock, flags); + return -EINVAL; + } + + /* disable output aux 1 & 2 on this pin */ + __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX1); + __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX2); + + /* disable input aux 1 on this pin */ + __cs5535_gpio_clear(chip, offset, GPIO_INPUT_AUX1); + + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int chip_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + return cs5535_gpio_isset(offset, GPIO_READ_BACK); +} + +static void chip_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +{ + if (val) + cs5535_gpio_set(offset, GPIO_OUTPUT_VAL); + else + cs5535_gpio_clear(offset, GPIO_OUTPUT_VAL); +} + +static int chip_direction_input(struct gpio_chip *c, unsigned offset) +{ + struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c; + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE); + __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_ENABLE); + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int chip_direction_output(struct gpio_chip *c, unsigned offset, int val) +{ + struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c; + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE); + __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_ENABLE); + if (val) + __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_VAL); + else + __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_VAL); + + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static const char * const cs5535_gpio_names[] = { + "GPIO0", "GPIO1", "GPIO2", "GPIO3", + "GPIO4", "GPIO5", "GPIO6", "GPIO7", + "GPIO8", "GPIO9", "GPIO10", "GPIO11", + "GPIO12", "GPIO13", "GPIO14", "GPIO15", + "GPIO16", "GPIO17", "GPIO18", "GPIO19", + "GPIO20", "GPIO21", "GPIO22", NULL, + "GPIO24", "GPIO25", "GPIO26", "GPIO27", + "GPIO28", NULL, NULL, NULL, +}; + +static struct cs5535_gpio_chip cs5535_gpio_chip = { + .chip = { + .owner = THIS_MODULE, + .label = DRV_NAME, + + .base = 0, + .ngpio = 32, + .names = cs5535_gpio_names, + .request = chip_gpio_request, + + .get = chip_gpio_get, + .set = chip_gpio_set, + + .direction_input = chip_direction_input, + .direction_output = chip_direction_output, + }, +}; + +static int __devinit cs5535_gpio_probe(struct platform_device *pdev) +{ + struct resource *res; + int err = -EIO; + ulong mask_orig = mask; + + /* There are two ways to get the GPIO base address; one is by + * fetching it from MSR_LBAR_GPIO, the other is by reading the + * PCI BAR info. The latter method is easier (especially across + * different architectures), so we'll stick with that for now. If + * it turns out to be unreliable in the face of crappy BIOSes, we + * can always go back to using MSRs.. */ + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) { + dev_err(&pdev->dev, "can't fetch device resource info\n"); + goto done; + } + + if (!request_region(res->start, resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "can't request region\n"); + goto done; + } + + /* set up the driver-specific struct */ + cs5535_gpio_chip.base = res->start; + cs5535_gpio_chip.pdev = pdev; + spin_lock_init(&cs5535_gpio_chip.lock); + + dev_info(&pdev->dev, "reserved resource region %pR\n", res); + + /* mask out reserved pins */ + mask &= 0x1F7FFFFF; + + /* do not allow pin 28, Power Button, as there's special handling + * in the PMC needed. (note 12, p. 48) */ + mask &= ~(1 << 28); + + if (mask_orig != mask) + dev_info(&pdev->dev, "mask changed from 0x%08lX to 0x%08lX\n", + mask_orig, mask); + + /* finally, register with the generic GPIO API */ + err = gpiochip_add(&cs5535_gpio_chip.chip); + if (err) + goto release_region; + + dev_info(&pdev->dev, "GPIO support successfully loaded.\n"); + return 0; + +release_region: + release_region(res->start, resource_size(res)); +done: + return err; +} + +static int __devexit cs5535_gpio_remove(struct platform_device *pdev) +{ + struct resource *r; + int err; + + err = gpiochip_remove(&cs5535_gpio_chip.chip); + if (err) { + /* uhh? */ + dev_err(&pdev->dev, "unable to remove gpio_chip?\n"); + return err; + } + + r = platform_get_resource(pdev, IORESOURCE_IO, 0); + release_region(r->start, resource_size(r)); + return 0; +} + +static struct platform_driver cs5535_gpio_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = cs5535_gpio_probe, + .remove = __devexit_p(cs5535_gpio_remove), +}; + +static int __init cs5535_gpio_init(void) +{ + return platform_driver_register(&cs5535_gpio_driver); +} + +static void __exit cs5535_gpio_exit(void) +{ + platform_driver_unregister(&cs5535_gpio_driver); +} + +module_init(cs5535_gpio_init); +module_exit(cs5535_gpio_exit); + +MODULE_AUTHOR("Andres Salomon "); +MODULE_DESCRIPTION("AMD CS5535/CS5536 GPIO driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 415dce37b88..c33c9a80e14 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -1,6 +1,4 @@ /* - * linux/arch/arm/mach-ep93xx/gpio.c - * * Generic EP93xx GPIO handling * * Copyright (c) 2008 Ryan Mallon diff --git a/drivers/gpio/gpio-exynos4.c b/drivers/gpio/gpio-exynos4.c index d54ca6adb66..6093a14037c 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/gpio-generic.c b/drivers/gpio/gpio-generic.c new file mode 100644 index 00000000000..231714def4d --- /dev/null +++ b/drivers/gpio/gpio-generic.c @@ -0,0 +1,548 @@ +/* + * Generic driver for memory-mapped GPIO controllers. + * + * Copyright 2008 MontaVista Software, Inc. + * Copyright 2008,2010 Anton Vorontsov + * + * 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. + * + * ....``.```~~~~````.`.`.`.`.```````'',,,.........`````......`....... + * ...`` ```````.. + * ..The simplest form of a GPIO controller that the driver supports is`` + * `.just a single "data" register, where GPIO state can be read and/or ` + * `,..written. ,,..``~~~~ .....``.`.`.~~.```.`.........``````.``````` + * ````````` + ___ +_/~~|___/~| . ```~~~~~~ ___/___\___ ,~.`.`.`.`````.~~...,,,,... +__________|~$@~~~ %~ /o*o*o*o*o*o\ .. Implementing such a GPIO . +o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` + `....trivial..'~`.```.``` + * ``````` + * .```````~~~~`..`.``.``. + * . The driver supports `... ,..```.`~~~```````````````....````.``,, + * . big-endian notation, just`. .. A bit more sophisticated controllers , + * . register the device with -be`. .with a pair of set/clear-bit registers , + * `.. suffix. ```~~`````....`.` . affecting the data register and the .` + * ``.`.``...``` ```.. output pins are also supported.` + * ^^ `````.`````````.,``~``~``~~`````` + * . ^^ + * ,..`.`.`...````````````......`.`.`.`.`.`..`.`.`.. + * .. The expectation is that in at least some cases . ,-~~~-, + * .this will be used with roll-your-own ASIC/FPGA .` \ / + * .logic in Verilog or VHDL. ~~~`````````..`````~~` \ / + * ..````````......``````````` \o_ + * | + * ^^ / \ + * + * ...`````~~`.....``.`..........``````.`.``.```........``. + * ` 8, 16, 32 and 64 bits registers are supported, and``. + * . the number of GPIOs is determined by the width of ~ + * .. the registers. ,............```.`.`..`.`.~~~.`.`.`~ + * `.......````.``` + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void bgpio_write8(void __iomem *reg, unsigned long data) +{ + writeb(data, reg); +} + +static unsigned long bgpio_read8(void __iomem *reg) +{ + return readb(reg); +} + +static void bgpio_write16(void __iomem *reg, unsigned long data) +{ + writew(data, reg); +} + +static unsigned long bgpio_read16(void __iomem *reg) +{ + return readw(reg); +} + +static void bgpio_write32(void __iomem *reg, unsigned long data) +{ + writel(data, reg); +} + +static unsigned long bgpio_read32(void __iomem *reg) +{ + return readl(reg); +} + +#if BITS_PER_LONG >= 64 +static void bgpio_write64(void __iomem *reg, unsigned long data) +{ + writeq(data, reg); +} + +static unsigned long bgpio_read64(void __iomem *reg) +{ + return readq(reg); +} +#endif /* BITS_PER_LONG >= 64 */ + +static unsigned long bgpio_pin2mask(struct bgpio_chip *bgc, unsigned int pin) +{ + return 1 << pin; +} + +static unsigned long bgpio_pin2mask_be(struct bgpio_chip *bgc, + unsigned int pin) +{ + return 1 << (bgc->bits - 1 - pin); +} + +static int bgpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + + return bgc->read_reg(bgc->reg_dat) & bgc->pin2mask(bgc, gpio); +} + +static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + unsigned long mask = bgc->pin2mask(bgc, gpio); + unsigned long flags; + + spin_lock_irqsave(&bgc->lock, flags); + + if (val) + bgc->data |= mask; + else + bgc->data &= ~mask; + + bgc->write_reg(bgc->reg_dat, bgc->data); + + spin_unlock_irqrestore(&bgc->lock, flags); +} + +static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, + int val) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + unsigned long mask = bgc->pin2mask(bgc, gpio); + + if (val) + bgc->write_reg(bgc->reg_set, mask); + else + bgc->write_reg(bgc->reg_clr, mask); +} + +static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + unsigned long mask = bgc->pin2mask(bgc, gpio); + unsigned long flags; + + spin_lock_irqsave(&bgc->lock, flags); + + if (val) + bgc->data |= mask; + else + bgc->data &= ~mask; + + bgc->write_reg(bgc->reg_set, bgc->data); + + spin_unlock_irqrestore(&bgc->lock, flags); +} + +static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + return 0; +} + +static int bgpio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio, + int val) +{ + gc->set(gc, gpio, val); + + return 0; +} + +static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + unsigned long flags; + + spin_lock_irqsave(&bgc->lock, flags); + + bgc->dir &= ~bgc->pin2mask(bgc, gpio); + bgc->write_reg(bgc->reg_dir, bgc->dir); + + spin_unlock_irqrestore(&bgc->lock, flags); + + return 0; +} + +static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + unsigned long flags; + + gc->set(gc, gpio, val); + + spin_lock_irqsave(&bgc->lock, flags); + + bgc->dir |= bgc->pin2mask(bgc, gpio); + bgc->write_reg(bgc->reg_dir, bgc->dir); + + spin_unlock_irqrestore(&bgc->lock, flags); + + return 0; +} + +static int bgpio_dir_in_inv(struct gpio_chip *gc, unsigned int gpio) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + unsigned long flags; + + spin_lock_irqsave(&bgc->lock, flags); + + bgc->dir |= bgc->pin2mask(bgc, gpio); + bgc->write_reg(bgc->reg_dir, bgc->dir); + + spin_unlock_irqrestore(&bgc->lock, flags); + + return 0; +} + +static int bgpio_dir_out_inv(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + unsigned long flags; + + gc->set(gc, gpio, val); + + spin_lock_irqsave(&bgc->lock, flags); + + bgc->dir &= ~bgc->pin2mask(bgc, gpio); + bgc->write_reg(bgc->reg_dir, bgc->dir); + + spin_unlock_irqrestore(&bgc->lock, flags); + + return 0; +} + +static int bgpio_setup_accessors(struct device *dev, + struct bgpio_chip *bgc, + bool be) +{ + + switch (bgc->bits) { + case 8: + bgc->read_reg = bgpio_read8; + bgc->write_reg = bgpio_write8; + break; + case 16: + bgc->read_reg = bgpio_read16; + bgc->write_reg = bgpio_write16; + break; + case 32: + bgc->read_reg = bgpio_read32; + bgc->write_reg = bgpio_write32; + break; +#if BITS_PER_LONG >= 64 + case 64: + bgc->read_reg = bgpio_read64; + bgc->write_reg = bgpio_write64; + break; +#endif /* BITS_PER_LONG >= 64 */ + default: + dev_err(dev, "unsupported data width %u bits\n", bgc->bits); + return -EINVAL; + } + + bgc->pin2mask = be ? bgpio_pin2mask_be : bgpio_pin2mask; + + return 0; +} + +/* + * Create the device and allocate the resources. For setting GPIO's there are + * three supported configurations: + * + * - single input/output register resource (named "dat"). + * - set/clear pair (named "set" and "clr"). + * - single output register resource and single input resource ("set" and + * dat"). + * + * For the single output register, this drives a 1 by setting a bit and a zero + * by clearing a bit. For the set clr pair, this drives a 1 by setting a bit + * in the set register and clears it by setting a bit in the clear register. + * The configuration is detected by which resources are present. + * + * For setting the GPIO direction, there are three supported configurations: + * + * - simple bidirection GPIO that requires no configuration. + * - an output direction register (named "dirout") where a 1 bit + * indicates the GPIO is an output. + * - an input direction register (named "dirin") where a 1 bit indicates + * the GPIO is an input. + */ +static int bgpio_setup_io(struct bgpio_chip *bgc, + void __iomem *dat, + void __iomem *set, + void __iomem *clr) +{ + + bgc->reg_dat = dat; + if (!bgc->reg_dat) + return -EINVAL; + + if (set && clr) { + bgc->reg_set = set; + bgc->reg_clr = clr; + bgc->gc.set = bgpio_set_with_clear; + } else if (set && !clr) { + bgc->reg_set = set; + bgc->gc.set = bgpio_set_set; + } else { + bgc->gc.set = bgpio_set; + } + + bgc->gc.get = bgpio_get; + + return 0; +} + +static int bgpio_setup_direction(struct bgpio_chip *bgc, + void __iomem *dirout, + void __iomem *dirin) +{ + if (dirout && dirin) { + return -EINVAL; + } else if (dirout) { + bgc->reg_dir = dirout; + bgc->gc.direction_output = bgpio_dir_out; + bgc->gc.direction_input = bgpio_dir_in; + } else if (dirin) { + bgc->reg_dir = dirin; + bgc->gc.direction_output = bgpio_dir_out_inv; + bgc->gc.direction_input = bgpio_dir_in_inv; + } else { + bgc->gc.direction_output = bgpio_simple_dir_out; + bgc->gc.direction_input = bgpio_simple_dir_in; + } + + return 0; +} + +int __devexit bgpio_remove(struct bgpio_chip *bgc) +{ + int err = gpiochip_remove(&bgc->gc); + + kfree(bgc); + + return err; +} +EXPORT_SYMBOL_GPL(bgpio_remove); + +int __devinit bgpio_init(struct bgpio_chip *bgc, + struct device *dev, + unsigned long sz, + void __iomem *dat, + void __iomem *set, + void __iomem *clr, + void __iomem *dirout, + void __iomem *dirin, + bool big_endian) +{ + int ret; + + if (!is_power_of_2(sz)) + return -EINVAL; + + bgc->bits = sz * 8; + if (bgc->bits > BITS_PER_LONG) + return -EINVAL; + + spin_lock_init(&bgc->lock); + bgc->gc.dev = dev; + bgc->gc.label = dev_name(dev); + bgc->gc.base = -1; + bgc->gc.ngpio = bgc->bits; + + ret = bgpio_setup_io(bgc, dat, set, clr); + if (ret) + return ret; + + ret = bgpio_setup_accessors(dev, bgc, big_endian); + if (ret) + return ret; + + ret = bgpio_setup_direction(bgc, dirout, dirin); + if (ret) + return ret; + + bgc->data = bgc->read_reg(bgc->reg_dat); + + return ret; +} +EXPORT_SYMBOL_GPL(bgpio_init); + +#ifdef CONFIG_GPIO_GENERIC_PLATFORM + +static void __iomem *bgpio_map(struct platform_device *pdev, + const char *name, + resource_size_t sane_sz, + int *err) +{ + struct device *dev = &pdev->dev; + struct resource *r; + resource_size_t start; + resource_size_t sz; + void __iomem *ret; + + *err = 0; + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (!r) + return NULL; + + sz = resource_size(r); + if (sz != sane_sz) { + *err = -EINVAL; + return NULL; + } + + start = r->start; + if (!devm_request_mem_region(dev, start, sz, r->name)) { + *err = -EBUSY; + return NULL; + } + + ret = devm_ioremap(dev, start, sz); + if (!ret) { + *err = -ENOMEM; + return NULL; + } + + return ret; +} + +static int __devinit bgpio_pdev_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *r; + void __iomem *dat; + void __iomem *set; + void __iomem *clr; + void __iomem *dirout; + void __iomem *dirin; + unsigned long sz; + bool be; + int err; + struct bgpio_chip *bgc; + struct bgpio_pdata *pdata = dev_get_platdata(dev); + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); + if (!r) + return -EINVAL; + + sz = resource_size(r); + + dat = bgpio_map(pdev, "dat", sz, &err); + if (!dat) + return err ? err : -EINVAL; + + set = bgpio_map(pdev, "set", sz, &err); + if (err) + return err; + + clr = bgpio_map(pdev, "clr", sz, &err); + if (err) + return err; + + dirout = bgpio_map(pdev, "dirout", sz, &err); + if (err) + return err; + + dirin = bgpio_map(pdev, "dirin", sz, &err); + if (err) + return err; + + be = !strcmp(platform_get_device_id(pdev)->name, "basic-mmio-gpio-be"); + + bgc = devm_kzalloc(&pdev->dev, sizeof(*bgc), GFP_KERNEL); + if (!bgc) + return -ENOMEM; + + err = bgpio_init(bgc, dev, sz, dat, set, clr, dirout, dirin, be); + if (err) + return err; + + if (pdata) { + bgc->gc.base = pdata->base; + if (pdata->ngpio > 0) + bgc->gc.ngpio = pdata->ngpio; + } + + platform_set_drvdata(pdev, bgc); + + return gpiochip_add(&bgc->gc); +} + +static int __devexit bgpio_pdev_remove(struct platform_device *pdev) +{ + struct bgpio_chip *bgc = platform_get_drvdata(pdev); + + return bgpio_remove(bgc); +} + +static const struct platform_device_id bgpio_id_table[] = { + { "basic-mmio-gpio", }, + { "basic-mmio-gpio-be", }, + {}, +}; +MODULE_DEVICE_TABLE(platform, bgpio_id_table); + +static struct platform_driver bgpio_driver = { + .driver = { + .name = "basic-mmio-gpio", + }, + .id_table = bgpio_id_table, + .probe = bgpio_pdev_probe, + .remove = __devexit_p(bgpio_pdev_remove), +}; + +static int __init bgpio_platform_init(void) +{ + return platform_driver_register(&bgpio_driver); +} +module_init(bgpio_platform_init); + +static void __exit bgpio_platform_exit(void) +{ + platform_driver_unregister(&bgpio_driver); +} +module_exit(bgpio_platform_exit); + +#endif /* CONFIG_GPIO_GENERIC_PLATFORM */ + +MODULE_DESCRIPTION("Driver for basic memory-mapped GPIO controllers"); +MODULE_AUTHOR("Anton Vorontsov "); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-it8761e.c b/drivers/gpio/gpio-it8761e.c new file mode 100644 index 00000000000..278b8131701 --- /dev/null +++ b/drivers/gpio/gpio-it8761e.c @@ -0,0 +1,234 @@ +/* + * GPIO interface for IT8761E Super I/O chip + * + * Author: Denis Turischev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define SIO_CHIP_ID 0x8761 +#define CHIP_ID_HIGH_BYTE 0x20 +#define CHIP_ID_LOW_BYTE 0x21 + +static u8 ports[2] = { 0x2e, 0x4e }; +static u8 port; + +static DEFINE_SPINLOCK(sio_lock); + +#define GPIO_NAME "it8761-gpio" +#define GPIO_BA_HIGH_BYTE 0x60 +#define GPIO_BA_LOW_BYTE 0x61 +#define GPIO_IOSIZE 4 +#define GPIO1X_IO 0xf0 +#define GPIO2X_IO 0xf1 + +static u16 gpio_ba; + +static u8 read_reg(u8 addr, u8 port) +{ + outb(addr, port); + return inb(port + 1); +} + +static void write_reg(u8 data, u8 addr, u8 port) +{ + outb(addr, port); + outb(data, port + 1); +} + +static void enter_conf_mode(u8 port) +{ + outb(0x87, port); + outb(0x61, port); + outb(0x55, port); + outb((port == 0x2e) ? 0x55 : 0xaa, port); +} + +static void exit_conf_mode(u8 port) +{ + outb(0x2, port); + outb(0x2, port + 1); +} + +static void enter_gpio_mode(u8 port) +{ + write_reg(0x2, 0x7, port); +} + +static int it8761e_gpio_get(struct gpio_chip *gc, unsigned gpio_num) +{ + u16 reg; + u8 bit; + + bit = gpio_num % 8; + reg = (gpio_num >= 8) ? gpio_ba + 1 : gpio_ba; + + return !!(inb(reg) & (1 << bit)); +} + +static int it8761e_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num) +{ + u8 curr_dirs; + u8 io_reg, bit; + + bit = gpio_num % 8; + io_reg = (gpio_num >= 8) ? GPIO2X_IO : GPIO1X_IO; + + spin_lock(&sio_lock); + + enter_conf_mode(port); + enter_gpio_mode(port); + + curr_dirs = read_reg(io_reg, port); + + if (curr_dirs & (1 << bit)) + write_reg(curr_dirs & ~(1 << bit), io_reg, port); + + exit_conf_mode(port); + + spin_unlock(&sio_lock); + return 0; +} + +static void it8761e_gpio_set(struct gpio_chip *gc, + unsigned gpio_num, int val) +{ + u8 curr_vals, bit; + u16 reg; + + bit = gpio_num % 8; + reg = (gpio_num >= 8) ? gpio_ba + 1 : gpio_ba; + + spin_lock(&sio_lock); + + curr_vals = inb(reg); + if (val) + outb(curr_vals | (1 << bit) , reg); + else + outb(curr_vals & ~(1 << bit), reg); + + spin_unlock(&sio_lock); +} + +static int it8761e_gpio_direction_out(struct gpio_chip *gc, + unsigned gpio_num, int val) +{ + u8 curr_dirs, io_reg, bit; + + bit = gpio_num % 8; + io_reg = (gpio_num >= 8) ? GPIO2X_IO : GPIO1X_IO; + + it8761e_gpio_set(gc, gpio_num, val); + + spin_lock(&sio_lock); + + enter_conf_mode(port); + enter_gpio_mode(port); + + curr_dirs = read_reg(io_reg, port); + + if (!(curr_dirs & (1 << bit))) + write_reg(curr_dirs | (1 << bit), io_reg, port); + + exit_conf_mode(port); + + spin_unlock(&sio_lock); + return 0; +} + +static struct gpio_chip it8761e_gpio_chip = { + .label = GPIO_NAME, + .owner = THIS_MODULE, + .get = it8761e_gpio_get, + .direction_input = it8761e_gpio_direction_in, + .set = it8761e_gpio_set, + .direction_output = it8761e_gpio_direction_out, +}; + +static int __init it8761e_gpio_init(void) +{ + int i, id, err; + + /* chip and port detection */ + for (i = 0; i < ARRAY_SIZE(ports); i++) { + spin_lock(&sio_lock); + enter_conf_mode(ports[i]); + + id = (read_reg(CHIP_ID_HIGH_BYTE, ports[i]) << 8) + + read_reg(CHIP_ID_LOW_BYTE, ports[i]); + + exit_conf_mode(ports[i]); + spin_unlock(&sio_lock); + + if (id == SIO_CHIP_ID) { + port = ports[i]; + break; + } + } + + if (!port) + return -ENODEV; + + /* fetch GPIO base address */ + enter_conf_mode(port); + enter_gpio_mode(port); + gpio_ba = (read_reg(GPIO_BA_HIGH_BYTE, port) << 8) + + read_reg(GPIO_BA_LOW_BYTE, port); + exit_conf_mode(port); + + if (!request_region(gpio_ba, GPIO_IOSIZE, GPIO_NAME)) + return -EBUSY; + + it8761e_gpio_chip.base = -1; + it8761e_gpio_chip.ngpio = 16; + + err = gpiochip_add(&it8761e_gpio_chip); + if (err < 0) + goto gpiochip_add_err; + + return 0; + +gpiochip_add_err: + release_region(gpio_ba, GPIO_IOSIZE); + gpio_ba = 0; + return err; +} + +static void __exit it8761e_gpio_exit(void) +{ + if (gpio_ba) { + int ret = gpiochip_remove(&it8761e_gpio_chip); + + WARN(ret, "%s(): gpiochip_remove() failed, ret=%d\n", + __func__, ret); + + release_region(gpio_ba, GPIO_IOSIZE); + gpio_ba = 0; + } +} +module_init(it8761e_gpio_init); +module_exit(it8761e_gpio_exit); + +MODULE_AUTHOR("Denis Turischev "); +MODULE_DESCRIPTION("GPIO interface for IT8761E Super I/O chip"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-janz-ttl.c b/drivers/gpio/gpio-janz-ttl.c new file mode 100644 index 00000000000..813ac077e5d --- /dev/null +++ b/drivers/gpio/gpio-janz-ttl.c @@ -0,0 +1,258 @@ +/* + * Janz MODULbus VMOD-TTL GPIO Driver + * + * Copyright (c) 2010 Ira W. Snyder + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRV_NAME "janz-ttl" + +#define PORTA_DIRECTION 0x23 +#define PORTB_DIRECTION 0x2B +#define PORTC_DIRECTION 0x06 +#define PORTA_IOCTL 0x24 +#define PORTB_IOCTL 0x2C +#define PORTC_IOCTL 0x07 + +#define MASTER_INT_CTL 0x00 +#define MASTER_CONF_CTL 0x01 + +#define CONF_PAE (1 << 2) +#define CONF_PBE (1 << 7) +#define CONF_PCE (1 << 4) + +struct ttl_control_regs { + __be16 portc; + __be16 portb; + __be16 porta; + __be16 control; +}; + +struct ttl_module { + struct gpio_chip gpio; + + /* base address of registers */ + struct ttl_control_regs __iomem *regs; + + u8 portc_shadow; + u8 portb_shadow; + u8 porta_shadow; + + spinlock_t lock; +}; + +static int ttl_get_value(struct gpio_chip *gpio, unsigned offset) +{ + struct ttl_module *mod = dev_get_drvdata(gpio->dev); + u8 *shadow; + int ret; + + if (offset < 8) { + shadow = &mod->porta_shadow; + } else if (offset < 16) { + shadow = &mod->portb_shadow; + offset -= 8; + } else { + shadow = &mod->portc_shadow; + offset -= 16; + } + + spin_lock(&mod->lock); + ret = *shadow & (1 << offset); + spin_unlock(&mod->lock); + return ret; +} + +static void ttl_set_value(struct gpio_chip *gpio, unsigned offset, int value) +{ + struct ttl_module *mod = dev_get_drvdata(gpio->dev); + void __iomem *port; + u8 *shadow; + + if (offset < 8) { + port = &mod->regs->porta; + shadow = &mod->porta_shadow; + } else if (offset < 16) { + port = &mod->regs->portb; + shadow = &mod->portb_shadow; + offset -= 8; + } else { + port = &mod->regs->portc; + shadow = &mod->portc_shadow; + offset -= 16; + } + + spin_lock(&mod->lock); + if (value) + *shadow |= (1 << offset); + else + *shadow &= ~(1 << offset); + + iowrite16be(*shadow, port); + spin_unlock(&mod->lock); +} + +static void __devinit ttl_write_reg(struct ttl_module *mod, u8 reg, u16 val) +{ + iowrite16be(reg, &mod->regs->control); + iowrite16be(val, &mod->regs->control); +} + +static void __devinit ttl_setup_device(struct ttl_module *mod) +{ + /* reset the device to a known state */ + iowrite16be(0x0000, &mod->regs->control); + iowrite16be(0x0001, &mod->regs->control); + iowrite16be(0x0000, &mod->regs->control); + + /* put all ports in open-drain mode */ + ttl_write_reg(mod, PORTA_IOCTL, 0x00ff); + ttl_write_reg(mod, PORTB_IOCTL, 0x00ff); + ttl_write_reg(mod, PORTC_IOCTL, 0x000f); + + /* set all ports as outputs */ + ttl_write_reg(mod, PORTA_DIRECTION, 0x0000); + ttl_write_reg(mod, PORTB_DIRECTION, 0x0000); + ttl_write_reg(mod, PORTC_DIRECTION, 0x0000); + + /* set all ports to drive zeroes */ + iowrite16be(0x0000, &mod->regs->porta); + iowrite16be(0x0000, &mod->regs->portb); + iowrite16be(0x0000, &mod->regs->portc); + + /* enable all ports */ + ttl_write_reg(mod, MASTER_CONF_CTL, CONF_PAE | CONF_PBE | CONF_PCE); +} + +static int __devinit ttl_probe(struct platform_device *pdev) +{ + struct janz_platform_data *pdata; + struct device *dev = &pdev->dev; + struct ttl_module *mod; + struct gpio_chip *gpio; + struct resource *res; + int ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(dev, "no platform data\n"); + ret = -ENXIO; + goto out_return; + } + + mod = kzalloc(sizeof(*mod), GFP_KERNEL); + if (!mod) { + dev_err(dev, "unable to allocate private data\n"); + ret = -ENOMEM; + goto out_return; + } + + platform_set_drvdata(pdev, mod); + spin_lock_init(&mod->lock); + + /* get access to the MODULbus registers for this module */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "MODULbus registers not found\n"); + ret = -ENODEV; + goto out_free_mod; + } + + mod->regs = ioremap(res->start, resource_size(res)); + if (!mod->regs) { + dev_err(dev, "MODULbus registers not ioremap\n"); + ret = -ENOMEM; + goto out_free_mod; + } + + ttl_setup_device(mod); + + /* Initialize the GPIO data structures */ + gpio = &mod->gpio; + gpio->dev = &pdev->dev; + gpio->label = pdev->name; + gpio->get = ttl_get_value; + gpio->set = ttl_set_value; + gpio->owner = THIS_MODULE; + + /* request dynamic allocation */ + gpio->base = -1; + gpio->ngpio = 20; + + ret = gpiochip_add(gpio); + if (ret) { + dev_err(dev, "unable to add GPIO chip\n"); + goto out_iounmap_regs; + } + + dev_info(&pdev->dev, "module %d: registered GPIO device\n", + pdata->modno); + return 0; + +out_iounmap_regs: + iounmap(mod->regs); +out_free_mod: + kfree(mod); +out_return: + return ret; +} + +static int __devexit ttl_remove(struct platform_device *pdev) +{ + struct ttl_module *mod = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int ret; + + ret = gpiochip_remove(&mod->gpio); + if (ret) { + dev_err(dev, "unable to remove GPIO chip\n"); + return ret; + } + + iounmap(mod->regs); + kfree(mod); + return 0; +} + +static struct platform_driver ttl_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = ttl_probe, + .remove = __devexit_p(ttl_remove), +}; + +static int __init ttl_init(void) +{ + return platform_driver_register(&ttl_driver); +} + +static void __exit ttl_exit(void) +{ + platform_driver_unregister(&ttl_driver); +} + +MODULE_AUTHOR("Ira W. Snyder "); +MODULE_DESCRIPTION("Janz MODULbus VMOD-TTL Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:janz-ttl"); + +module_init(ttl_init); +module_exit(ttl_exit); diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c new file mode 100644 index 00000000000..e7a7ea760ef --- /dev/null +++ b/drivers/gpio/gpio-langwell.c @@ -0,0 +1,458 @@ +/* + * Moorestown platform Langwell chip GPIO driver + * + * Copyright (c) 2008 - 2009, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Moorestown platform Langwell chip. + * Medfield platform Penwell chip. + * Whitney point. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Langwell chip has 64 pins and thus there are 2 32bit registers to control + * each feature, while Penwell chip has 96 pins for each block, and need 3 32bit + * registers to control them, so we only define the order here instead of a + * structure, to get a bit offset for a pin (use GPDR as an example): + * + * nreg = ngpio / 32; + * reg = offset / 32; + * bit = offset % 32; + * reg_addr = reg_base + GPDR * nreg * 4 + reg * 4; + * + * so the bit of reg_addr is to control pin offset's GPDR feature +*/ + +enum GPIO_REG { + GPLR = 0, /* pin level read-only */ + GPDR, /* pin direction */ + GPSR, /* pin set */ + GPCR, /* pin clear */ + GRER, /* rising edge detect */ + GFER, /* falling edge detect */ + GEDR, /* edge detect result */ +}; + +struct lnw_gpio { + struct gpio_chip chip; + void *reg_base; + spinlock_t lock; + unsigned irq_base; + struct pci_dev *pdev; +}; + +static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset, + enum GPIO_REG reg_type) +{ + struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + unsigned nreg = chip->ngpio / 32; + u8 reg = offset / 32; + void __iomem *ptr; + + ptr = (void __iomem *)(lnw->reg_base + reg_type * nreg * 4 + reg * 4); + return ptr; +} + +static int lnw_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + void __iomem *gplr = gpio_reg(chip, offset, GPLR); + + return readl(gplr) & BIT(offset % 32); +} + +static void lnw_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + void __iomem *gpsr, *gpcr; + + if (value) { + gpsr = gpio_reg(chip, offset, GPSR); + writel(BIT(offset % 32), gpsr); + } else { + gpcr = gpio_reg(chip, offset, GPCR); + writel(BIT(offset % 32), gpcr); + } +} + +static int lnw_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + void __iomem *gpdr = gpio_reg(chip, offset, GPDR); + u32 value; + unsigned long flags; + + if (lnw->pdev) + pm_runtime_get(&lnw->pdev->dev); + + spin_lock_irqsave(&lnw->lock, flags); + value = readl(gpdr); + value &= ~BIT(offset % 32); + writel(value, gpdr); + spin_unlock_irqrestore(&lnw->lock, flags); + + if (lnw->pdev) + pm_runtime_put(&lnw->pdev->dev); + + return 0; +} + +static int lnw_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + void __iomem *gpdr = gpio_reg(chip, offset, GPDR); + unsigned long flags; + + lnw_gpio_set(chip, offset, value); + + if (lnw->pdev) + pm_runtime_get(&lnw->pdev->dev); + + spin_lock_irqsave(&lnw->lock, flags); + value = readl(gpdr); + value |= BIT(offset % 32); + writel(value, gpdr); + spin_unlock_irqrestore(&lnw->lock, flags); + + if (lnw->pdev) + pm_runtime_put(&lnw->pdev->dev); + + return 0; +} + +static int lnw_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + return lnw->irq_base + offset; +} + +static int lnw_irq_type(struct irq_data *d, unsigned type) +{ + struct lnw_gpio *lnw = irq_data_get_irq_chip_data(d); + u32 gpio = d->irq - lnw->irq_base; + unsigned long flags; + u32 value; + void __iomem *grer = gpio_reg(&lnw->chip, gpio, GRER); + void __iomem *gfer = gpio_reg(&lnw->chip, gpio, GFER); + + if (gpio >= lnw->chip.ngpio) + return -EINVAL; + + if (lnw->pdev) + pm_runtime_get(&lnw->pdev->dev); + + spin_lock_irqsave(&lnw->lock, flags); + if (type & IRQ_TYPE_EDGE_RISING) + value = readl(grer) | BIT(gpio % 32); + else + value = readl(grer) & (~BIT(gpio % 32)); + writel(value, grer); + + if (type & IRQ_TYPE_EDGE_FALLING) + value = readl(gfer) | BIT(gpio % 32); + else + value = readl(gfer) & (~BIT(gpio % 32)); + writel(value, gfer); + spin_unlock_irqrestore(&lnw->lock, flags); + + if (lnw->pdev) + pm_runtime_put(&lnw->pdev->dev); + + return 0; +} + +static void lnw_irq_unmask(struct irq_data *d) +{ +} + +static void lnw_irq_mask(struct irq_data *d) +{ +} + +static struct irq_chip lnw_irqchip = { + .name = "LNW-GPIO", + .irq_mask = lnw_irq_mask, + .irq_unmask = lnw_irq_unmask, + .irq_set_type = lnw_irq_type, +}; + +static DEFINE_PCI_DEVICE_TABLE(lnw_gpio_ids) = { /* pin number */ + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080f), .driver_data = 64 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081f), .driver_data = 96 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081a), .driver_data = 96 }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, lnw_gpio_ids); + +static void lnw_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct irq_data *data = irq_desc_get_irq_data(desc); + struct lnw_gpio *lnw = irq_data_get_irq_handler_data(data); + struct irq_chip *chip = irq_data_get_irq_chip(data); + u32 base, gpio, mask; + unsigned long pending; + void __iomem *gedr; + + /* check GPIO controller to check which pin triggered the interrupt */ + for (base = 0; base < lnw->chip.ngpio; base += 32) { + gedr = gpio_reg(&lnw->chip, base, GEDR); + pending = readl(gedr); + while (pending) { + gpio = __ffs(pending) - 1; + mask = BIT(gpio); + pending &= ~mask; + /* Clear before handling so we can't lose an edge */ + writel(mask, gedr); + generic_handle_irq(lnw->irq_base + base + gpio); + } + } + + chip->irq_eoi(data); +} + +#ifdef CONFIG_PM +static int lnw_gpio_runtime_resume(struct device *dev) +{ + return 0; +} + +static int lnw_gpio_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int lnw_gpio_runtime_idle(struct device *dev) +{ + int err = pm_schedule_suspend(dev, 500); + + if (!err) + return 0; + + return -EBUSY; +} + +#else +#define lnw_gpio_runtime_suspend NULL +#define lnw_gpio_runtime_resume NULL +#define lnw_gpio_runtime_idle NULL +#endif + +static const struct dev_pm_ops lnw_gpio_pm_ops = { + .runtime_suspend = lnw_gpio_runtime_suspend, + .runtime_resume = lnw_gpio_runtime_resume, + .runtime_idle = lnw_gpio_runtime_idle, +}; + +static int __devinit lnw_gpio_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + void *base; + int i; + resource_size_t start, len; + struct lnw_gpio *lnw; + u32 irq_base; + u32 gpio_base; + int retval = 0; + + retval = pci_enable_device(pdev); + if (retval) + goto done; + + retval = pci_request_regions(pdev, "langwell_gpio"); + if (retval) { + dev_err(&pdev->dev, "error requesting resources\n"); + goto err2; + } + /* get the irq_base from bar1 */ + start = pci_resource_start(pdev, 1); + len = pci_resource_len(pdev, 1); + base = ioremap_nocache(start, len); + if (!base) { + dev_err(&pdev->dev, "error mapping bar1\n"); + goto err3; + } + irq_base = *(u32 *)base; + gpio_base = *((u32 *)base + 1); + /* release the IO mapping, since we already get the info from bar1 */ + iounmap(base); + /* get the register base from bar0 */ + start = pci_resource_start(pdev, 0); + len = pci_resource_len(pdev, 0); + base = ioremap_nocache(start, len); + if (!base) { + dev_err(&pdev->dev, "error mapping bar0\n"); + retval = -EFAULT; + goto err3; + } + + lnw = kzalloc(sizeof(struct lnw_gpio), GFP_KERNEL); + if (!lnw) { + dev_err(&pdev->dev, "can't allocate langwell_gpio chip data\n"); + retval = -ENOMEM; + goto err4; + } + lnw->reg_base = base; + lnw->irq_base = irq_base; + lnw->chip.label = dev_name(&pdev->dev); + lnw->chip.direction_input = lnw_gpio_direction_input; + lnw->chip.direction_output = lnw_gpio_direction_output; + lnw->chip.get = lnw_gpio_get; + lnw->chip.set = lnw_gpio_set; + lnw->chip.to_irq = lnw_gpio_to_irq; + lnw->chip.base = gpio_base; + lnw->chip.ngpio = id->driver_data; + lnw->chip.can_sleep = 0; + lnw->pdev = pdev; + pci_set_drvdata(pdev, lnw); + retval = gpiochip_add(&lnw->chip); + if (retval) { + dev_err(&pdev->dev, "langwell gpiochip_add error %d\n", retval); + goto err5; + } + irq_set_handler_data(pdev->irq, lnw); + irq_set_chained_handler(pdev->irq, lnw_irq_handler); + for (i = 0; i < lnw->chip.ngpio; i++) { + irq_set_chip_and_handler_name(i + lnw->irq_base, &lnw_irqchip, + handle_simple_irq, "demux"); + irq_set_chip_data(i + lnw->irq_base, lnw); + } + + spin_lock_init(&lnw->lock); + + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_allow(&pdev->dev); + + goto done; +err5: + kfree(lnw); +err4: + iounmap(base); +err3: + pci_release_regions(pdev); +err2: + pci_disable_device(pdev); +done: + return retval; +} + +static struct pci_driver lnw_gpio_driver = { + .name = "langwell_gpio", + .id_table = lnw_gpio_ids, + .probe = lnw_gpio_probe, + .driver = { + .pm = &lnw_gpio_pm_ops, + }, +}; + + +static int __devinit wp_gpio_probe(struct platform_device *pdev) +{ + struct lnw_gpio *lnw; + struct gpio_chip *gc; + struct resource *rc; + int retval = 0; + + rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!rc) + return -EINVAL; + + lnw = kzalloc(sizeof(struct lnw_gpio), GFP_KERNEL); + if (!lnw) { + dev_err(&pdev->dev, + "can't allocate whitneypoint_gpio chip data\n"); + return -ENOMEM; + } + lnw->reg_base = ioremap_nocache(rc->start, resource_size(rc)); + if (lnw->reg_base == NULL) { + retval = -EINVAL; + goto err_kmalloc; + } + spin_lock_init(&lnw->lock); + gc = &lnw->chip; + gc->label = dev_name(&pdev->dev); + gc->owner = THIS_MODULE; + gc->direction_input = lnw_gpio_direction_input; + gc->direction_output = lnw_gpio_direction_output; + gc->get = lnw_gpio_get; + gc->set = lnw_gpio_set; + gc->to_irq = NULL; + gc->base = 0; + gc->ngpio = 64; + gc->can_sleep = 0; + retval = gpiochip_add(gc); + if (retval) { + dev_err(&pdev->dev, "whitneypoint gpiochip_add error %d\n", + retval); + goto err_ioremap; + } + platform_set_drvdata(pdev, lnw); + return 0; +err_ioremap: + iounmap(lnw->reg_base); +err_kmalloc: + kfree(lnw); + return retval; +} + +static int __devexit wp_gpio_remove(struct platform_device *pdev) +{ + struct lnw_gpio *lnw = platform_get_drvdata(pdev); + int err; + err = gpiochip_remove(&lnw->chip); + if (err) + dev_err(&pdev->dev, "failed to remove gpio_chip.\n"); + iounmap(lnw->reg_base); + kfree(lnw); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver wp_gpio_driver = { + .probe = wp_gpio_probe, + .remove = __devexit_p(wp_gpio_remove), + .driver = { + .name = "wp_gpio", + .owner = THIS_MODULE, + }, +}; + +static int __init lnw_gpio_init(void) +{ + int ret; + ret = pci_register_driver(&lnw_gpio_driver); + if (ret < 0) + return ret; + ret = platform_driver_register(&wp_gpio_driver); + if (ret < 0) + pci_unregister_driver(&lnw_gpio_driver); + return ret; +} + +device_initcall(lnw_gpio_init); diff --git a/drivers/gpio/gpio-max7300.c b/drivers/gpio/gpio-max7300.c new file mode 100644 index 00000000000..a5ca0ab1b37 --- /dev/null +++ b/drivers/gpio/gpio-max7300.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2009 Wolfram Sang, Pengutronix + * + * 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. + * + * Check max730x.c for further details. + */ + +#include +#include +#include +#include +#include +#include +#include + +static int max7300_i2c_write(struct device *dev, unsigned int reg, + unsigned int val) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_write_byte_data(client, reg, val); +} + +static int max7300_i2c_read(struct device *dev, unsigned int reg) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int __devinit max7300_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max7301 *ts; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + ts = kzalloc(sizeof(struct max7301), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + ts->read = max7300_i2c_read; + ts->write = max7300_i2c_write; + ts->dev = &client->dev; + + ret = __max730x_probe(ts); + if (ret) + kfree(ts); + return ret; +} + +static int __devexit max7300_remove(struct i2c_client *client) +{ + return __max730x_remove(&client->dev); +} + +static const struct i2c_device_id max7300_id[] = { + { "max7300", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max7300_id); + +static struct i2c_driver max7300_driver = { + .driver = { + .name = "max7300", + .owner = THIS_MODULE, + }, + .probe = max7300_probe, + .remove = __devexit_p(max7300_remove), + .id_table = max7300_id, +}; + +static int __init max7300_init(void) +{ + return i2c_add_driver(&max7300_driver); +} +subsys_initcall(max7300_init); + +static void __exit max7300_exit(void) +{ + i2c_del_driver(&max7300_driver); +} +module_exit(max7300_exit); + +MODULE_AUTHOR("Wolfram Sang"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MAX7300 GPIO-Expander"); diff --git a/drivers/gpio/gpio-max7301.c b/drivers/gpio/gpio-max7301.c new file mode 100644 index 00000000000..741acfcbe76 --- /dev/null +++ b/drivers/gpio/gpio-max7301.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2006 Juergen Beisert, Pengutronix + * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix + * Copyright (C) 2009 Wolfram Sang, Pengutronix + * + * 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. + * + * Check max730x.c for further details. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* A write to the MAX7301 means one message with one transfer */ +static int max7301_spi_write(struct device *dev, unsigned int reg, + unsigned int val) +{ + struct spi_device *spi = to_spi_device(dev); + u16 word = ((reg & 0x7F) << 8) | (val & 0xFF); + + return spi_write(spi, (const u8 *)&word, sizeof(word)); +} + +/* A read from the MAX7301 means two transfers; here, one message each */ + +static int max7301_spi_read(struct device *dev, unsigned int reg) +{ + int ret; + u16 word; + struct spi_device *spi = to_spi_device(dev); + + word = 0x8000 | (reg << 8); + ret = spi_write(spi, (const u8 *)&word, sizeof(word)); + if (ret) + return ret; + /* + * This relies on the fact, that a transfer with NULL tx_buf shifts out + * zero bytes (=NOOP for MAX7301) + */ + ret = spi_read(spi, (u8 *)&word, sizeof(word)); + if (ret) + return ret; + return word & 0xff; +} + +static int __devinit max7301_probe(struct spi_device *spi) +{ + struct max7301 *ts; + int ret; + + /* bits_per_word cannot be configured in platform data */ + spi->bits_per_word = 16; + ret = spi_setup(spi); + if (ret < 0) + return ret; + + ts = kzalloc(sizeof(struct max7301), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + ts->read = max7301_spi_read; + ts->write = max7301_spi_write; + ts->dev = &spi->dev; + + ret = __max730x_probe(ts); + if (ret) + kfree(ts); + return ret; +} + +static int __devexit max7301_remove(struct spi_device *spi) +{ + return __max730x_remove(&spi->dev); +} + +static const struct spi_device_id max7301_id[] = { + { "max7301", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, max7301_id); + +static struct spi_driver max7301_driver = { + .driver = { + .name = "max7301", + .owner = THIS_MODULE, + }, + .probe = max7301_probe, + .remove = __devexit_p(max7301_remove), + .id_table = max7301_id, +}; + +static int __init max7301_init(void) +{ + return spi_register_driver(&max7301_driver); +} +/* register after spi postcore initcall and before + * subsys initcalls that may rely on these GPIOs + */ +subsys_initcall(max7301_init); + +static void __exit max7301_exit(void) +{ + spi_unregister_driver(&max7301_driver); +} +module_exit(max7301_exit); + +MODULE_AUTHOR("Juergen Beisert, Wolfram Sang"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MAX7301 GPIO-Expander"); diff --git a/drivers/gpio/gpio-max730x.c b/drivers/gpio/gpio-max730x.c new file mode 100644 index 00000000000..05e2dac60b3 --- /dev/null +++ b/drivers/gpio/gpio-max730x.c @@ -0,0 +1,255 @@ +/** + * Copyright (C) 2006 Juergen Beisert, Pengutronix + * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix + * Copyright (C) 2009 Wolfram Sang, Pengutronix + * + * 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. + * + * The Maxim MAX7300/1 device is an I2C/SPI driven GPIO expander. There are + * 28 GPIOs. 8 of them can trigger an interrupt. See datasheet for more + * details + * Note: + * - DIN must be stable at the rising edge of clock. + * - when writing: + * - always clock in 16 clocks at once + * - at DIN: D15 first, D0 last + * - D0..D7 = databyte, D8..D14 = commandbyte + * - D15 = low -> write command + * - when reading + * - always clock in 16 clocks at once + * - at DIN: D15 first, D0 last + * - D0..D7 = dummy, D8..D14 = register address + * - D15 = high -> read command + * - raise CS and assert it again + * - always clock in 16 clocks at once + * - at DOUT: D15 first, D0 last + * - D0..D7 contains the data from the first cycle + * + * The driver exports a standard gpiochip interface + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Pin configurations, see MAX7301 datasheet page 6 + */ +#define PIN_CONFIG_MASK 0x03 +#define PIN_CONFIG_IN_PULLUP 0x03 +#define PIN_CONFIG_IN_WO_PULLUP 0x02 +#define PIN_CONFIG_OUT 0x01 + +#define PIN_NUMBER 28 + +static int max7301_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + u8 *config; + u8 offset_bits, pin_config; + int ret; + + /* First 4 pins are unused in the controller */ + offset += 4; + offset_bits = (offset & 3) << 1; + + config = &ts->port_config[offset >> 2]; + + if (ts->input_pullup_active & BIT(offset)) + pin_config = PIN_CONFIG_IN_PULLUP; + else + pin_config = PIN_CONFIG_IN_WO_PULLUP; + + mutex_lock(&ts->lock); + + *config = (*config & ~(PIN_CONFIG_MASK << offset_bits)) + | (pin_config << offset_bits); + + ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config); + + mutex_unlock(&ts->lock); + + return ret; +} + +static int __max7301_set(struct max7301 *ts, unsigned offset, int value) +{ + if (value) { + ts->out_level |= 1 << offset; + return ts->write(ts->dev, 0x20 + offset, 0x01); + } else { + ts->out_level &= ~(1 << offset); + return ts->write(ts->dev, 0x20 + offset, 0x00); + } +} + +static int max7301_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + u8 *config; + u8 offset_bits; + int ret; + + /* First 4 pins are unused in the controller */ + offset += 4; + offset_bits = (offset & 3) << 1; + + config = &ts->port_config[offset >> 2]; + + mutex_lock(&ts->lock); + + *config = (*config & ~(PIN_CONFIG_MASK << offset_bits)) + | (PIN_CONFIG_OUT << offset_bits); + + ret = __max7301_set(ts, offset, value); + + if (!ret) + ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config); + + mutex_unlock(&ts->lock); + + return ret; +} + +static int max7301_get(struct gpio_chip *chip, unsigned offset) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + int config, level = -EINVAL; + + /* First 4 pins are unused in the controller */ + offset += 4; + + mutex_lock(&ts->lock); + + config = (ts->port_config[offset >> 2] >> ((offset & 3) << 1)) + & PIN_CONFIG_MASK; + + switch (config) { + case PIN_CONFIG_OUT: + /* Output: return cached level */ + level = !!(ts->out_level & (1 << offset)); + break; + case PIN_CONFIG_IN_WO_PULLUP: + case PIN_CONFIG_IN_PULLUP: + /* Input: read out */ + level = ts->read(ts->dev, 0x20 + offset) & 0x01; + } + mutex_unlock(&ts->lock); + + return level; +} + +static void max7301_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + + /* First 4 pins are unused in the controller */ + offset += 4; + + mutex_lock(&ts->lock); + + __max7301_set(ts, offset, value); + + mutex_unlock(&ts->lock); +} + +int __devinit __max730x_probe(struct max7301 *ts) +{ + struct device *dev = ts->dev; + struct max7301_platform_data *pdata; + int i, ret; + + pdata = dev->platform_data; + if (!pdata || !pdata->base) { + dev_err(dev, "incorrect or missing platform data\n"); + return -EINVAL; + } + + mutex_init(&ts->lock); + dev_set_drvdata(dev, ts); + + /* Power up the chip and disable IRQ output */ + ts->write(dev, 0x04, 0x01); + + ts->input_pullup_active = pdata->input_pullup_active; + ts->chip.label = dev->driver->name; + + ts->chip.direction_input = max7301_direction_input; + ts->chip.get = max7301_get; + ts->chip.direction_output = max7301_direction_output; + ts->chip.set = max7301_set; + + ts->chip.base = pdata->base; + ts->chip.ngpio = PIN_NUMBER; + ts->chip.can_sleep = 1; + ts->chip.dev = dev; + ts->chip.owner = THIS_MODULE; + + /* + * initialize pullups according to platform data and cache the + * register values for later use. + */ + for (i = 1; i < 8; i++) { + int j; + /* + * initialize port_config with "0xAA", which means + * input with internal pullup disabled. This is needed + * to avoid writing zeros (in the inner for loop), + * which is not allowed according to the datasheet. + */ + ts->port_config[i] = 0xAA; + for (j = 0; j < 4; j++) { + int offset = (i - 1) * 4 + j; + ret = max7301_direction_input(&ts->chip, offset); + if (ret) + goto exit_destroy; + } + } + + ret = gpiochip_add(&ts->chip); + if (ret) + goto exit_destroy; + + return ret; + +exit_destroy: + dev_set_drvdata(dev, NULL); + mutex_destroy(&ts->lock); + return ret; +} +EXPORT_SYMBOL_GPL(__max730x_probe); + +int __devexit __max730x_remove(struct device *dev) +{ + struct max7301 *ts = dev_get_drvdata(dev); + int ret; + + if (ts == NULL) + return -ENODEV; + + dev_set_drvdata(dev, NULL); + + /* Power down the chip and disable IRQ output */ + ts->write(dev, 0x04, 0x00); + + ret = gpiochip_remove(&ts->chip); + if (!ret) { + mutex_destroy(&ts->lock); + kfree(ts); + } else + dev_err(dev, "Failed to remove GPIO controller: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(__max730x_remove); + +MODULE_AUTHOR("Juergen Beisert, Wolfram Sang"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MAX730x GPIO-Expanders, generic parts"); diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c new file mode 100644 index 00000000000..9504120812a --- /dev/null +++ b/drivers/gpio/gpio-max732x.c @@ -0,0 +1,714 @@ +/* + * MAX732x I2C Port Expander with 8/16 I/O + * + * Copyright (C) 2007 Marvell International Ltd. + * Copyright (C) 2008 Jack Ren + * Copyright (C) 2008 Eric Miao + * + * Derived from drivers/gpio/pca953x.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * Each port of MAX732x (including MAX7319) falls into one of the + * following three types: + * + * - Push Pull Output + * - Input + * - Open Drain I/O + * + * designated by 'O', 'I' and 'P' individually according to MAXIM's + * datasheets. 'I' and 'P' ports are interrupt capables, some with + * a dedicated interrupt mask. + * + * There are two groups of I/O ports, each group usually includes + * up to 8 I/O ports, and is accessed by a specific I2C address: + * + * - Group A : by I2C address 0b'110xxxx + * - Group B : by I2C address 0b'101xxxx + * + * where 'xxxx' is decided by the connections of pin AD2/AD0. The + * address used also affects the initial state of output signals. + * + * Within each group of ports, there are five known combinations of + * I/O ports: 4I4O, 4P4O, 8I, 8P, 8O, see the definitions below for + * the detailed organization of these ports. Only Goup A is interrupt + * capable. + * + * GPIO numbers start from 'gpio_base + 0' to 'gpio_base + 8/16', + * and GPIOs from GROUP_A are numbered before those from GROUP_B + * (if there are two groups). + * + * NOTE: MAX7328/MAX7329 are drop-in replacements for PCF8574/a, so + * they are not supported by this driver. + */ + +#define PORT_NONE 0x0 /* '/' No Port */ +#define PORT_OUTPUT 0x1 /* 'O' Push-Pull, Output Only */ +#define PORT_INPUT 0x2 /* 'I' Input Only */ +#define PORT_OPENDRAIN 0x3 /* 'P' Open-Drain, I/O */ + +#define IO_4I4O 0x5AA5 /* O7 O6 I5 I4 I3 I2 O1 O0 */ +#define IO_4P4O 0x5FF5 /* O7 O6 P5 P4 P3 P2 O1 O0 */ +#define IO_8I 0xAAAA /* I7 I6 I5 I4 I3 I2 I1 I0 */ +#define IO_8P 0xFFFF /* P7 P6 P5 P4 P3 P2 P1 P0 */ +#define IO_8O 0x5555 /* O7 O6 O5 O4 O3 O2 O1 O0 */ + +#define GROUP_A(x) ((x) & 0xffff) /* I2C Addr: 0b'110xxxx */ +#define GROUP_B(x) ((x) << 16) /* I2C Addr: 0b'101xxxx */ + +#define INT_NONE 0x0 /* No interrupt capability */ +#define INT_NO_MASK 0x1 /* Has interrupts, no mask */ +#define INT_INDEP_MASK 0x2 /* Has interrupts, independent mask */ +#define INT_MERGED_MASK 0x3 /* Has interrupts, merged mask */ + +#define INT_CAPS(x) (((uint64_t)(x)) << 32) + +enum { + MAX7319, + MAX7320, + MAX7321, + MAX7322, + MAX7323, + MAX7324, + MAX7325, + MAX7326, + MAX7327, +}; + +static uint64_t max732x_features[] = { + [MAX7319] = GROUP_A(IO_8I) | INT_CAPS(INT_MERGED_MASK), + [MAX7320] = GROUP_B(IO_8O), + [MAX7321] = GROUP_A(IO_8P) | INT_CAPS(INT_NO_MASK), + [MAX7322] = GROUP_A(IO_4I4O) | INT_CAPS(INT_MERGED_MASK), + [MAX7323] = GROUP_A(IO_4P4O) | INT_CAPS(INT_INDEP_MASK), + [MAX7324] = GROUP_A(IO_8I) | GROUP_B(IO_8O) | INT_CAPS(INT_MERGED_MASK), + [MAX7325] = GROUP_A(IO_8P) | GROUP_B(IO_8O) | INT_CAPS(INT_NO_MASK), + [MAX7326] = GROUP_A(IO_4I4O) | GROUP_B(IO_8O) | INT_CAPS(INT_MERGED_MASK), + [MAX7327] = GROUP_A(IO_4P4O) | GROUP_B(IO_8O) | INT_CAPS(INT_NO_MASK), +}; + +static const struct i2c_device_id max732x_id[] = { + { "max7319", MAX7319 }, + { "max7320", MAX7320 }, + { "max7321", MAX7321 }, + { "max7322", MAX7322 }, + { "max7323", MAX7323 }, + { "max7324", MAX7324 }, + { "max7325", MAX7325 }, + { "max7326", MAX7326 }, + { "max7327", MAX7327 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, max732x_id); + +struct max732x_chip { + struct gpio_chip gpio_chip; + + struct i2c_client *client; /* "main" client */ + struct i2c_client *client_dummy; + struct i2c_client *client_group_a; + struct i2c_client *client_group_b; + + unsigned int mask_group_a; + unsigned int dir_input; + unsigned int dir_output; + + struct mutex lock; + uint8_t reg_out[2]; + +#ifdef CONFIG_GPIO_MAX732X_IRQ + struct mutex irq_lock; + int irq_base; + uint8_t irq_mask; + uint8_t irq_mask_cur; + uint8_t irq_trig_raise; + uint8_t irq_trig_fall; + uint8_t irq_features; +#endif +}; + +static int max732x_writeb(struct max732x_chip *chip, int group_a, uint8_t val) +{ + struct i2c_client *client; + int ret; + + client = group_a ? chip->client_group_a : chip->client_group_b; + ret = i2c_smbus_write_byte(client, val); + if (ret < 0) { + dev_err(&client->dev, "failed writing\n"); + return ret; + } + + return 0; +} + +static int max732x_readb(struct max732x_chip *chip, int group_a, uint8_t *val) +{ + struct i2c_client *client; + int ret; + + client = group_a ? chip->client_group_a : chip->client_group_b; + ret = i2c_smbus_read_byte(client); + if (ret < 0) { + dev_err(&client->dev, "failed reading\n"); + return ret; + } + + *val = (uint8_t)ret; + return 0; +} + +static inline int is_group_a(struct max732x_chip *chip, unsigned off) +{ + return (1u << off) & chip->mask_group_a; +} + +static int max732x_gpio_get_value(struct gpio_chip *gc, unsigned off) +{ + struct max732x_chip *chip; + uint8_t reg_val; + int ret; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + ret = max732x_readb(chip, is_group_a(chip, off), ®_val); + if (ret < 0) + return 0; + + return reg_val & (1u << (off & 0x7)); +} + +static void max732x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) +{ + struct max732x_chip *chip; + uint8_t reg_out, mask = 1u << (off & 0x7); + int ret; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + mutex_lock(&chip->lock); + + reg_out = (off > 7) ? chip->reg_out[1] : chip->reg_out[0]; + reg_out = (val) ? reg_out | mask : reg_out & ~mask; + + ret = max732x_writeb(chip, is_group_a(chip, off), reg_out); + if (ret < 0) + goto out; + + /* update the shadow register then */ + if (off > 7) + chip->reg_out[1] = reg_out; + else + chip->reg_out[0] = reg_out; +out: + mutex_unlock(&chip->lock); +} + +static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off) +{ + struct max732x_chip *chip; + unsigned int mask = 1u << off; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + if ((mask & chip->dir_input) == 0) { + dev_dbg(&chip->client->dev, "%s port %d is output only\n", + chip->client->name, off); + return -EACCES; + } + + /* + * Open-drain pins must be set to high impedance (which is + * equivalent to output-high) to be turned into an input. + */ + if ((mask & chip->dir_output)) + max732x_gpio_set_value(gc, off, 1); + + return 0; +} + +static int max732x_gpio_direction_output(struct gpio_chip *gc, + unsigned off, int val) +{ + struct max732x_chip *chip; + unsigned int mask = 1u << off; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + if ((mask & chip->dir_output) == 0) { + dev_dbg(&chip->client->dev, "%s port %d is input only\n", + chip->client->name, off); + return -EACCES; + } + + max732x_gpio_set_value(gc, off, val); + return 0; +} + +#ifdef CONFIG_GPIO_MAX732X_IRQ +static int max732x_writew(struct max732x_chip *chip, uint16_t val) +{ + int ret; + + val = cpu_to_le16(val); + + ret = i2c_master_send(chip->client_group_a, (char *)&val, 2); + if (ret < 0) { + dev_err(&chip->client_group_a->dev, "failed writing\n"); + return ret; + } + + return 0; +} + +static int max732x_readw(struct max732x_chip *chip, uint16_t *val) +{ + int ret; + + ret = i2c_master_recv(chip->client_group_a, (char *)val, 2); + if (ret < 0) { + dev_err(&chip->client_group_a->dev, "failed reading\n"); + return ret; + } + + *val = le16_to_cpu(*val); + return 0; +} + +static void max732x_irq_update_mask(struct max732x_chip *chip) +{ + uint16_t msg; + + if (chip->irq_mask == chip->irq_mask_cur) + return; + + chip->irq_mask = chip->irq_mask_cur; + + if (chip->irq_features == INT_NO_MASK) + return; + + mutex_lock(&chip->lock); + + switch (chip->irq_features) { + case INT_INDEP_MASK: + msg = (chip->irq_mask << 8) | chip->reg_out[0]; + max732x_writew(chip, msg); + break; + + case INT_MERGED_MASK: + msg = chip->irq_mask | chip->reg_out[0]; + max732x_writeb(chip, 1, (uint8_t)msg); + break; + } + + mutex_unlock(&chip->lock); +} + +static int max732x_gpio_to_irq(struct gpio_chip *gc, unsigned off) +{ + struct max732x_chip *chip; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + return chip->irq_base + off; +} + +static void max732x_irq_mask(struct irq_data *d) +{ + struct max732x_chip *chip = irq_data_get_irq_chip_data(d); + + chip->irq_mask_cur &= ~(1 << (d->irq - chip->irq_base)); +} + +static void max732x_irq_unmask(struct irq_data *d) +{ + struct max732x_chip *chip = irq_data_get_irq_chip_data(d); + + chip->irq_mask_cur |= 1 << (d->irq - chip->irq_base); +} + +static void max732x_irq_bus_lock(struct irq_data *d) +{ + struct max732x_chip *chip = irq_data_get_irq_chip_data(d); + + mutex_lock(&chip->irq_lock); + chip->irq_mask_cur = chip->irq_mask; +} + +static void max732x_irq_bus_sync_unlock(struct irq_data *d) +{ + struct max732x_chip *chip = irq_data_get_irq_chip_data(d); + + max732x_irq_update_mask(chip); + mutex_unlock(&chip->irq_lock); +} + +static int max732x_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct max732x_chip *chip = irq_data_get_irq_chip_data(d); + uint16_t off = d->irq - chip->irq_base; + uint16_t mask = 1 << off; + + if (!(mask & chip->dir_input)) { + dev_dbg(&chip->client->dev, "%s port %d is output only\n", + chip->client->name, off); + return -EACCES; + } + + if (!(type & IRQ_TYPE_EDGE_BOTH)) { + dev_err(&chip->client->dev, "irq %d: unsupported type %d\n", + d->irq, type); + return -EINVAL; + } + + if (type & IRQ_TYPE_EDGE_FALLING) + chip->irq_trig_fall |= mask; + else + chip->irq_trig_fall &= ~mask; + + if (type & IRQ_TYPE_EDGE_RISING) + chip->irq_trig_raise |= mask; + else + chip->irq_trig_raise &= ~mask; + + return max732x_gpio_direction_input(&chip->gpio_chip, off); +} + +static struct irq_chip max732x_irq_chip = { + .name = "max732x", + .irq_mask = max732x_irq_mask, + .irq_unmask = max732x_irq_unmask, + .irq_bus_lock = max732x_irq_bus_lock, + .irq_bus_sync_unlock = max732x_irq_bus_sync_unlock, + .irq_set_type = max732x_irq_set_type, +}; + +static uint8_t max732x_irq_pending(struct max732x_chip *chip) +{ + uint8_t cur_stat; + uint8_t old_stat; + uint8_t trigger; + uint8_t pending; + uint16_t status; + int ret; + + ret = max732x_readw(chip, &status); + if (ret) + return 0; + + trigger = status >> 8; + trigger &= chip->irq_mask; + + if (!trigger) + return 0; + + cur_stat = status & 0xFF; + cur_stat &= chip->irq_mask; + + old_stat = cur_stat ^ trigger; + + pending = (old_stat & chip->irq_trig_fall) | + (cur_stat & chip->irq_trig_raise); + pending &= trigger; + + return pending; +} + +static irqreturn_t max732x_irq_handler(int irq, void *devid) +{ + struct max732x_chip *chip = devid; + uint8_t pending; + uint8_t level; + + pending = max732x_irq_pending(chip); + + if (!pending) + return IRQ_HANDLED; + + do { + level = __ffs(pending); + handle_nested_irq(level + chip->irq_base); + + pending &= ~(1 << level); + } while (pending); + + return IRQ_HANDLED; +} + +static int max732x_irq_setup(struct max732x_chip *chip, + const struct i2c_device_id *id) +{ + struct i2c_client *client = chip->client; + struct max732x_platform_data *pdata = client->dev.platform_data; + int has_irq = max732x_features[id->driver_data] >> 32; + int ret; + + if (pdata->irq_base && has_irq != INT_NONE) { + int lvl; + + chip->irq_base = pdata->irq_base; + chip->irq_features = has_irq; + mutex_init(&chip->irq_lock); + + for (lvl = 0; lvl < chip->gpio_chip.ngpio; lvl++) { + int irq = lvl + chip->irq_base; + + if (!(chip->dir_input & (1 << lvl))) + continue; + + irq_set_chip_data(irq, chip); + irq_set_chip_and_handler(irq, &max732x_irq_chip, + handle_edge_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + } + + ret = request_threaded_irq(client->irq, + NULL, + max732x_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(&client->dev), chip); + if (ret) { + dev_err(&client->dev, "failed to request irq %d\n", + client->irq); + goto out_failed; + } + + chip->gpio_chip.to_irq = max732x_gpio_to_irq; + } + + return 0; + +out_failed: + chip->irq_base = 0; + return ret; +} + +static void max732x_irq_teardown(struct max732x_chip *chip) +{ + if (chip->irq_base) + free_irq(chip->client->irq, chip); +} +#else /* CONFIG_GPIO_MAX732X_IRQ */ +static int max732x_irq_setup(struct max732x_chip *chip, + const struct i2c_device_id *id) +{ + struct i2c_client *client = chip->client; + struct max732x_platform_data *pdata = client->dev.platform_data; + int has_irq = max732x_features[id->driver_data] >> 32; + + if (pdata->irq_base && has_irq != INT_NONE) + dev_warn(&client->dev, "interrupt support not compiled in\n"); + + return 0; +} + +static void max732x_irq_teardown(struct max732x_chip *chip) +{ +} +#endif + +static int __devinit max732x_setup_gpio(struct max732x_chip *chip, + const struct i2c_device_id *id, + unsigned gpio_start) +{ + struct gpio_chip *gc = &chip->gpio_chip; + uint32_t id_data = (uint32_t)max732x_features[id->driver_data]; + int i, port = 0; + + for (i = 0; i < 16; i++, id_data >>= 2) { + unsigned int mask = 1 << port; + + switch (id_data & 0x3) { + case PORT_OUTPUT: + chip->dir_output |= mask; + break; + case PORT_INPUT: + chip->dir_input |= mask; + break; + case PORT_OPENDRAIN: + chip->dir_output |= mask; + chip->dir_input |= mask; + break; + default: + continue; + } + + if (i < 8) + chip->mask_group_a |= mask; + port++; + } + + if (chip->dir_input) + gc->direction_input = max732x_gpio_direction_input; + if (chip->dir_output) { + gc->direction_output = max732x_gpio_direction_output; + gc->set = max732x_gpio_set_value; + } + gc->get = max732x_gpio_get_value; + gc->can_sleep = 1; + + gc->base = gpio_start; + gc->ngpio = port; + gc->label = chip->client->name; + gc->owner = THIS_MODULE; + + return port; +} + +static int __devinit max732x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max732x_platform_data *pdata; + struct max732x_chip *chip; + struct i2c_client *c; + uint16_t addr_a, addr_b; + int ret, nr_port; + + pdata = client->dev.platform_data; + if (pdata == NULL) { + dev_dbg(&client->dev, "no platform data\n"); + return -EINVAL; + } + + chip = kzalloc(sizeof(struct max732x_chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->client = client; + + nr_port = max732x_setup_gpio(chip, id, pdata->gpio_base); + + addr_a = (client->addr & 0x0f) | 0x60; + addr_b = (client->addr & 0x0f) | 0x50; + + switch (client->addr & 0x70) { + case 0x60: + chip->client_group_a = client; + if (nr_port > 8) { + c = i2c_new_dummy(client->adapter, addr_b); + chip->client_group_b = chip->client_dummy = c; + } + break; + case 0x50: + chip->client_group_b = client; + if (nr_port > 8) { + c = i2c_new_dummy(client->adapter, addr_a); + chip->client_group_a = chip->client_dummy = c; + } + break; + default: + dev_err(&client->dev, "invalid I2C address specified %02x\n", + client->addr); + ret = -EINVAL; + goto out_failed; + } + + mutex_init(&chip->lock); + + max732x_readb(chip, is_group_a(chip, 0), &chip->reg_out[0]); + if (nr_port > 8) + max732x_readb(chip, is_group_a(chip, 8), &chip->reg_out[1]); + + ret = max732x_irq_setup(chip, id); + if (ret) + goto out_failed; + + ret = gpiochip_add(&chip->gpio_chip); + if (ret) + goto out_failed; + + if (pdata->setup) { + ret = pdata->setup(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) + dev_warn(&client->dev, "setup failed, %d\n", ret); + } + + i2c_set_clientdata(client, chip); + return 0; + +out_failed: + max732x_irq_teardown(chip); + kfree(chip); + return ret; +} + +static int __devexit max732x_remove(struct i2c_client *client) +{ + struct max732x_platform_data *pdata = client->dev.platform_data; + struct max732x_chip *chip = i2c_get_clientdata(client); + int ret; + + if (pdata->teardown) { + ret = pdata->teardown(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) { + dev_err(&client->dev, "%s failed, %d\n", + "teardown", ret); + return ret; + } + } + + ret = gpiochip_remove(&chip->gpio_chip); + if (ret) { + dev_err(&client->dev, "%s failed, %d\n", + "gpiochip_remove()", ret); + return ret; + } + + max732x_irq_teardown(chip); + + /* unregister any dummy i2c_client */ + if (chip->client_dummy) + i2c_unregister_device(chip->client_dummy); + + kfree(chip); + return 0; +} + +static struct i2c_driver max732x_driver = { + .driver = { + .name = "max732x", + .owner = THIS_MODULE, + }, + .probe = max732x_probe, + .remove = __devexit_p(max732x_remove), + .id_table = max732x_id, +}; + +static int __init max732x_init(void) +{ + return i2c_add_driver(&max732x_driver); +} +/* register after i2c postcore initcall and before + * subsys initcalls that may rely on these GPIOs + */ +subsys_initcall(max732x_init); + +static void __exit max732x_exit(void) +{ + i2c_del_driver(&max732x_driver); +} +module_exit(max732x_exit); + +MODULE_AUTHOR("Eric Miao "); +MODULE_DESCRIPTION("GPIO expander driver for MAX732X"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-mc33880.c b/drivers/gpio/gpio-mc33880.c new file mode 100644 index 00000000000..b3b4652e89e --- /dev/null +++ b/drivers/gpio/gpio-mc33880.c @@ -0,0 +1,197 @@ +/* + * 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 + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Freescale MC33880 high-side/low-side switch + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "mc33880" + +/* + * Pin configurations, see MAX7301 datasheet page 6 + */ +#define PIN_CONFIG_MASK 0x03 +#define PIN_CONFIG_IN_PULLUP 0x03 +#define PIN_CONFIG_IN_WO_PULLUP 0x02 +#define PIN_CONFIG_OUT 0x01 + +#define PIN_NUMBER 8 + + +/* + * Some registers must be read back to modify. + * To save time we cache them here in memory + */ +struct mc33880 { + struct mutex lock; /* protect from simultaneous accesses */ + u8 port_config; + struct gpio_chip chip; + struct spi_device *spi; +}; + +static int mc33880_write_config(struct mc33880 *mc) +{ + return spi_write(mc->spi, &mc->port_config, sizeof(mc->port_config)); +} + + +static int __mc33880_set(struct mc33880 *mc, unsigned offset, int value) +{ + if (value) + mc->port_config |= 1 << offset; + else + mc->port_config &= ~(1 << offset); + + return mc33880_write_config(mc); +} + + +static void mc33880_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct mc33880 *mc = container_of(chip, struct mc33880, chip); + + mutex_lock(&mc->lock); + + __mc33880_set(mc, offset, value); + + mutex_unlock(&mc->lock); +} + +static int __devinit mc33880_probe(struct spi_device *spi) +{ + struct mc33880 *mc; + struct mc33880_platform_data *pdata; + int ret; + + pdata = spi->dev.platform_data; + if (!pdata || !pdata->base) { + dev_dbg(&spi->dev, "incorrect or missing platform data\n"); + return -EINVAL; + } + + /* + * bits_per_word cannot be configured in platform data + */ + spi->bits_per_word = 8; + + ret = spi_setup(spi); + if (ret < 0) + return ret; + + mc = kzalloc(sizeof(struct mc33880), GFP_KERNEL); + if (!mc) + return -ENOMEM; + + mutex_init(&mc->lock); + + dev_set_drvdata(&spi->dev, mc); + + mc->spi = spi; + + mc->chip.label = DRIVER_NAME, + mc->chip.set = mc33880_set; + mc->chip.base = pdata->base; + mc->chip.ngpio = PIN_NUMBER; + mc->chip.can_sleep = 1; + mc->chip.dev = &spi->dev; + mc->chip.owner = THIS_MODULE; + + mc->port_config = 0x00; + /* write twice, because during initialisation the first setting + * is just for testing SPI communication, and the second is the + * "real" configuration + */ + ret = mc33880_write_config(mc); + mc->port_config = 0x00; + if (!ret) + ret = mc33880_write_config(mc); + + if (ret) { + printk(KERN_ERR "Failed writing to " DRIVER_NAME ": %d\n", ret); + goto exit_destroy; + } + + ret = gpiochip_add(&mc->chip); + if (ret) + goto exit_destroy; + + return ret; + +exit_destroy: + dev_set_drvdata(&spi->dev, NULL); + mutex_destroy(&mc->lock); + kfree(mc); + return ret; +} + +static int __devexit mc33880_remove(struct spi_device *spi) +{ + struct mc33880 *mc; + int ret; + + mc = dev_get_drvdata(&spi->dev); + if (mc == NULL) + return -ENODEV; + + dev_set_drvdata(&spi->dev, NULL); + + ret = gpiochip_remove(&mc->chip); + if (!ret) { + mutex_destroy(&mc->lock); + kfree(mc); + } else + dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n", + ret); + + return ret; +} + +static struct spi_driver mc33880_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = mc33880_probe, + .remove = __devexit_p(mc33880_remove), +}; + +static int __init mc33880_init(void) +{ + return spi_register_driver(&mc33880_driver); +} +/* register after spi postcore initcall and before + * subsys initcalls that may rely on these GPIOs + */ +subsys_initcall(mc33880_init); + +static void __exit mc33880_exit(void) +{ + spi_unregister_driver(&mc33880_driver); +} +module_exit(mc33880_exit); + +MODULE_AUTHOR("Mocean Laboratories "); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c new file mode 100644 index 00000000000..0083ec051de --- /dev/null +++ b/drivers/gpio/gpio-mcp23s08.c @@ -0,0 +1,530 @@ +/* + * MCP23S08 SPI gpio expander driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * MCP types supported by driver + */ +#define MCP_TYPE_S08 0 +#define MCP_TYPE_S17 1 + +/* Registers are all 8 bits wide. + * + * The mcp23s17 has twice as many bits, and can be configured to work + * with either 16 bit registers or with two adjacent 8 bit banks. + * + * Also, there are I2C versions of both chips. + */ +#define MCP_IODIR 0x00 /* init/reset: all ones */ +#define MCP_IPOL 0x01 +#define MCP_GPINTEN 0x02 +#define MCP_DEFVAL 0x03 +#define MCP_INTCON 0x04 +#define MCP_IOCON 0x05 +# define IOCON_SEQOP (1 << 5) +# define IOCON_HAEN (1 << 3) +# define IOCON_ODR (1 << 2) +# define IOCON_INTPOL (1 << 1) +#define MCP_GPPU 0x06 +#define MCP_INTF 0x07 +#define MCP_INTCAP 0x08 +#define MCP_GPIO 0x09 +#define MCP_OLAT 0x0a + +struct mcp23s08; + +struct mcp23s08_ops { + int (*read)(struct mcp23s08 *mcp, unsigned reg); + int (*write)(struct mcp23s08 *mcp, unsigned reg, unsigned val); + int (*read_regs)(struct mcp23s08 *mcp, unsigned reg, + u16 *vals, unsigned n); +}; + +struct mcp23s08 { + struct spi_device *spi; + u8 addr; + + u16 cache[11]; + /* lock protects the cached values */ + struct mutex lock; + + struct gpio_chip chip; + + struct work_struct work; + + const struct mcp23s08_ops *ops; +}; + +/* A given spi_device can represent up to eight mcp23sxx chips + * sharing the same chipselect but using different addresses + * (e.g. chips #0 and #3 might be populated, but not #1 or $2). + * Driver data holds all the per-chip data. + */ +struct mcp23s08_driver_data { + unsigned ngpio; + struct mcp23s08 *mcp[8]; + struct mcp23s08 chip[]; +}; + +static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg) +{ + u8 tx[2], rx[1]; + int status; + + tx[0] = mcp->addr | 0x01; + tx[1] = reg; + status = spi_write_then_read(mcp->spi, tx, sizeof tx, rx, sizeof rx); + return (status < 0) ? status : rx[0]; +} + +static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, unsigned val) +{ + u8 tx[3]; + + tx[0] = mcp->addr; + tx[1] = reg; + tx[2] = val; + return spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0); +} + +static int +mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n) +{ + u8 tx[2], *tmp; + int status; + + if ((n + reg) > sizeof mcp->cache) + return -EINVAL; + tx[0] = mcp->addr | 0x01; + tx[1] = reg; + + tmp = (u8 *)vals; + status = spi_write_then_read(mcp->spi, tx, sizeof tx, tmp, n); + if (status >= 0) { + while (n--) + vals[n] = tmp[n]; /* expand to 16bit */ + } + return status; +} + +static int mcp23s17_read(struct mcp23s08 *mcp, unsigned reg) +{ + u8 tx[2], rx[2]; + int status; + + tx[0] = mcp->addr | 0x01; + tx[1] = reg << 1; + status = spi_write_then_read(mcp->spi, tx, sizeof tx, rx, sizeof rx); + return (status < 0) ? status : (rx[0] | (rx[1] << 8)); +} + +static int mcp23s17_write(struct mcp23s08 *mcp, unsigned reg, unsigned val) +{ + u8 tx[4]; + + tx[0] = mcp->addr; + tx[1] = reg << 1; + tx[2] = val; + tx[3] = val >> 8; + return spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0); +} + +static int +mcp23s17_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n) +{ + u8 tx[2]; + int status; + + if ((n + reg) > sizeof mcp->cache) + return -EINVAL; + tx[0] = mcp->addr | 0x01; + tx[1] = reg << 1; + + status = spi_write_then_read(mcp->spi, tx, sizeof tx, + (u8 *)vals, n * 2); + if (status >= 0) { + while (n--) + vals[n] = __le16_to_cpu((__le16)vals[n]); + } + + return status; +} + +static const struct mcp23s08_ops mcp23s08_ops = { + .read = mcp23s08_read, + .write = mcp23s08_write, + .read_regs = mcp23s08_read_regs, +}; + +static const struct mcp23s08_ops mcp23s17_ops = { + .read = mcp23s17_read, + .write = mcp23s17_write, + .read_regs = mcp23s17_read_regs, +}; + + +/*----------------------------------------------------------------------*/ + +static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); + int status; + + mutex_lock(&mcp->lock); + mcp->cache[MCP_IODIR] |= (1 << offset); + status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); + mutex_unlock(&mcp->lock); + return status; +} + +static int mcp23s08_get(struct gpio_chip *chip, unsigned offset) +{ + struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); + int status; + + mutex_lock(&mcp->lock); + + /* REVISIT reading this clears any IRQ ... */ + status = mcp->ops->read(mcp, MCP_GPIO); + if (status < 0) + status = 0; + else { + mcp->cache[MCP_GPIO] = status; + status = !!(status & (1 << offset)); + } + mutex_unlock(&mcp->lock); + return status; +} + +static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value) +{ + unsigned olat = mcp->cache[MCP_OLAT]; + + if (value) + olat |= mask; + else + olat &= ~mask; + mcp->cache[MCP_OLAT] = olat; + return mcp->ops->write(mcp, MCP_OLAT, olat); +} + +static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); + unsigned mask = 1 << offset; + + mutex_lock(&mcp->lock); + __mcp23s08_set(mcp, mask, value); + mutex_unlock(&mcp->lock); +} + +static int +mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value) +{ + struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); + unsigned mask = 1 << offset; + int status; + + mutex_lock(&mcp->lock); + status = __mcp23s08_set(mcp, mask, value); + if (status == 0) { + mcp->cache[MCP_IODIR] &= ~mask; + status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); + } + mutex_unlock(&mcp->lock); + return status; +} + +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_DEBUG_FS + +#include + +/* + * This shows more info than the generic gpio dump code: + * pullups, deglitching, open drain drive. + */ +static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + struct mcp23s08 *mcp; + char bank; + int t; + unsigned mask; + + mcp = container_of(chip, struct mcp23s08, chip); + + /* NOTE: we only handle one bank for now ... */ + bank = '0' + ((mcp->addr >> 1) & 0x7); + + mutex_lock(&mcp->lock); + t = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache)); + if (t < 0) { + seq_printf(s, " I/O ERROR %d\n", t); + goto done; + } + + for (t = 0, mask = 1; t < chip->ngpio; t++, mask <<= 1) { + const char *label; + + label = gpiochip_is_requested(chip, t); + if (!label) + continue; + + seq_printf(s, " gpio-%-3d P%c.%d (%-12s) %s %s %s", + chip->base + t, bank, t, label, + (mcp->cache[MCP_IODIR] & mask) ? "in " : "out", + (mcp->cache[MCP_GPIO] & mask) ? "hi" : "lo", + (mcp->cache[MCP_GPPU] & mask) ? " " : "up"); + /* NOTE: ignoring the irq-related registers */ + seq_printf(s, "\n"); + } +done: + mutex_unlock(&mcp->lock); +} + +#else +#define mcp23s08_dbg_show NULL +#endif + +/*----------------------------------------------------------------------*/ + +static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr, + unsigned type, unsigned base, unsigned pullups) +{ + struct mcp23s08_driver_data *data = spi_get_drvdata(spi); + struct mcp23s08 *mcp = data->mcp[addr]; + int status; + + mutex_init(&mcp->lock); + + mcp->spi = spi; + mcp->addr = 0x40 | (addr << 1); + + mcp->chip.direction_input = mcp23s08_direction_input; + mcp->chip.get = mcp23s08_get; + mcp->chip.direction_output = mcp23s08_direction_output; + mcp->chip.set = mcp23s08_set; + mcp->chip.dbg_show = mcp23s08_dbg_show; + + if (type == MCP_TYPE_S17) { + mcp->ops = &mcp23s17_ops; + mcp->chip.ngpio = 16; + mcp->chip.label = "mcp23s17"; + } else { + mcp->ops = &mcp23s08_ops; + mcp->chip.ngpio = 8; + mcp->chip.label = "mcp23s08"; + } + mcp->chip.base = base; + mcp->chip.can_sleep = 1; + mcp->chip.dev = &spi->dev; + mcp->chip.owner = THIS_MODULE; + + /* verify MCP_IOCON.SEQOP = 0, so sequential reads work, + * and MCP_IOCON.HAEN = 1, so we work with all chips. + */ + status = mcp->ops->read(mcp, MCP_IOCON); + if (status < 0) + goto fail; + if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) { + /* mcp23s17 has IOCON twice, make sure they are in sync */ + status &= ~(IOCON_SEQOP | (IOCON_SEQOP << 8)); + status |= IOCON_HAEN | (IOCON_HAEN << 8); + status = mcp->ops->write(mcp, MCP_IOCON, status); + if (status < 0) + goto fail; + } + + /* configure ~100K pullups */ + status = mcp->ops->write(mcp, MCP_GPPU, pullups); + if (status < 0) + goto fail; + + status = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache)); + if (status < 0) + goto fail; + + /* disable inverter on input */ + if (mcp->cache[MCP_IPOL] != 0) { + mcp->cache[MCP_IPOL] = 0; + status = mcp->ops->write(mcp, MCP_IPOL, 0); + if (status < 0) + goto fail; + } + + /* disable irqs */ + if (mcp->cache[MCP_GPINTEN] != 0) { + mcp->cache[MCP_GPINTEN] = 0; + status = mcp->ops->write(mcp, MCP_GPINTEN, 0); + if (status < 0) + goto fail; + } + + status = gpiochip_add(&mcp->chip); +fail: + if (status < 0) + dev_dbg(&spi->dev, "can't setup chip %d, --> %d\n", + addr, status); + return status; +} + +static int mcp23s08_probe(struct spi_device *spi) +{ + struct mcp23s08_platform_data *pdata; + unsigned addr; + unsigned chips = 0; + struct mcp23s08_driver_data *data; + int status, type; + unsigned base; + + type = spi_get_device_id(spi)->driver_data; + + pdata = spi->dev.platform_data; + if (!pdata || !gpio_is_valid(pdata->base)) { + dev_dbg(&spi->dev, "invalid or missing platform data\n"); + return -EINVAL; + } + + for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { + if (!pdata->chip[addr].is_present) + continue; + chips++; + if ((type == MCP_TYPE_S08) && (addr > 3)) { + dev_err(&spi->dev, + "mcp23s08 only supports address 0..3\n"); + return -EINVAL; + } + } + if (!chips) + return -ENODEV; + + data = kzalloc(sizeof *data + chips * sizeof(struct mcp23s08), + GFP_KERNEL); + if (!data) + return -ENOMEM; + spi_set_drvdata(spi, data); + + base = pdata->base; + for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { + if (!pdata->chip[addr].is_present) + continue; + chips--; + data->mcp[addr] = &data->chip[chips]; + status = mcp23s08_probe_one(spi, addr, type, base, + pdata->chip[addr].pullups); + if (status < 0) + goto fail; + + base += (type == MCP_TYPE_S17) ? 16 : 8; + } + data->ngpio = base - pdata->base; + + /* NOTE: these chips have a relatively sane IRQ framework, with + * per-signal masking and level/edge triggering. It's not yet + * handled here... + */ + + if (pdata->setup) { + status = pdata->setup(spi, + pdata->base, data->ngpio, + pdata->context); + if (status < 0) + dev_dbg(&spi->dev, "setup --> %d\n", status); + } + + return 0; + +fail: + for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) { + int tmp; + + if (!data->mcp[addr]) + continue; + tmp = gpiochip_remove(&data->mcp[addr]->chip); + if (tmp < 0) + dev_err(&spi->dev, "%s --> %d\n", "remove", tmp); + } + kfree(data); + return status; +} + +static int mcp23s08_remove(struct spi_device *spi) +{ + struct mcp23s08_driver_data *data = spi_get_drvdata(spi); + struct mcp23s08_platform_data *pdata = spi->dev.platform_data; + unsigned addr; + int status = 0; + + if (pdata->teardown) { + status = pdata->teardown(spi, + pdata->base, data->ngpio, + pdata->context); + if (status < 0) { + dev_err(&spi->dev, "%s --> %d\n", "teardown", status); + return status; + } + } + + for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) { + int tmp; + + if (!data->mcp[addr]) + continue; + + tmp = gpiochip_remove(&data->mcp[addr]->chip); + if (tmp < 0) { + dev_err(&spi->dev, "%s --> %d\n", "remove", tmp); + status = tmp; + } + } + if (status == 0) + kfree(data); + return status; +} + +static const struct spi_device_id mcp23s08_ids[] = { + { "mcp23s08", MCP_TYPE_S08 }, + { "mcp23s17", MCP_TYPE_S17 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, mcp23s08_ids); + +static struct spi_driver mcp23s08_driver = { + .probe = mcp23s08_probe, + .remove = mcp23s08_remove, + .id_table = mcp23s08_ids, + .driver = { + .name = "mcp23s08", + .owner = THIS_MODULE, + }, +}; + +/*----------------------------------------------------------------------*/ + +static int __init mcp23s08_init(void) +{ + return spi_register_driver(&mcp23s08_driver); +} +/* register after spi postcore initcall and before + * subsys initcalls that may rely on these GPIOs + */ +subsys_initcall(mcp23s08_init); + +static void __exit mcp23s08_exit(void) +{ + spi_unregister_driver(&mcp23s08_driver); +} +module_exit(mcp23s08_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c new file mode 100644 index 00000000000..1bc621ac353 --- /dev/null +++ b/drivers/gpio/gpio-ml-ioh.c @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ +#include +#include +#include +#include + +#define PCI_VENDOR_ID_ROHM 0x10DB + +struct ioh_reg_comn { + u32 ien; + u32 istatus; + u32 idisp; + u32 iclr; + u32 imask; + u32 imaskclr; + u32 po; + u32 pi; + u32 pm; + u32 im_0; + u32 im_1; + u32 reserved; +}; + +struct ioh_regs { + struct ioh_reg_comn regs[8]; + u32 reserve1[16]; + u32 ioh_sel_reg[4]; + u32 reserve2[11]; + u32 srst; +}; + +/** + * struct ioh_gpio_reg_data - The register store data. + * @po_reg: To store contents of PO register. + * @pm_reg: To store contents of PM register. + */ +struct ioh_gpio_reg_data { + u32 po_reg; + u32 pm_reg; +}; + +/** + * struct ioh_gpio - GPIO private data structure. + * @base: PCI base address of Memory mapped I/O register. + * @reg: Memory mapped IOH GPIO register list. + * @dev: Pointer to device structure. + * @gpio: Data for GPIO infrastructure. + * @ioh_gpio_reg: Memory mapped Register data is saved here + * when suspend. + * @ch: Indicate GPIO channel + */ +struct ioh_gpio { + void __iomem *base; + struct ioh_regs __iomem *reg; + struct device *dev; + struct gpio_chip gpio; + struct ioh_gpio_reg_data ioh_gpio_reg; + struct mutex lock; + int ch; +}; + +static const int num_ports[] = {6, 12, 16, 16, 15, 16, 16, 12}; + +static void ioh_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) +{ + u32 reg_val; + struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio); + + mutex_lock(&chip->lock); + reg_val = ioread32(&chip->reg->regs[chip->ch].po); + if (val) + reg_val |= (1 << nr); + else + reg_val &= ~(1 << nr); + + iowrite32(reg_val, &chip->reg->regs[chip->ch].po); + mutex_unlock(&chip->lock); +} + +static int ioh_gpio_get(struct gpio_chip *gpio, unsigned nr) +{ + struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio); + + return ioread32(&chip->reg->regs[chip->ch].pi) & (1 << nr); +} + +static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, + int val) +{ + struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio); + u32 pm; + u32 reg_val; + + mutex_lock(&chip->lock); + pm = ioread32(&chip->reg->regs[chip->ch].pm) & + ((1 << num_ports[chip->ch]) - 1); + pm |= (1 << nr); + iowrite32(pm, &chip->reg->regs[chip->ch].pm); + + reg_val = ioread32(&chip->reg->regs[chip->ch].po); + if (val) + reg_val |= (1 << nr); + else + reg_val &= ~(1 << nr); + iowrite32(reg_val, &chip->reg->regs[chip->ch].po); + + mutex_unlock(&chip->lock); + + return 0; +} + +static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) +{ + struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio); + u32 pm; + + mutex_lock(&chip->lock); + pm = ioread32(&chip->reg->regs[chip->ch].pm) & + ((1 << num_ports[chip->ch]) - 1); + pm &= ~(1 << nr); + iowrite32(pm, &chip->reg->regs[chip->ch].pm); + mutex_unlock(&chip->lock); + + return 0; +} + +#ifdef CONFIG_PM +/* + * Save register configuration and disable interrupts. + */ +static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip) +{ + chip->ioh_gpio_reg.po_reg = ioread32(&chip->reg->regs[chip->ch].po); + chip->ioh_gpio_reg.pm_reg = ioread32(&chip->reg->regs[chip->ch].pm); +} + +/* + * This function restores the register configuration of the GPIO device. + */ +static void ioh_gpio_restore_reg_conf(struct ioh_gpio *chip) +{ + /* to store contents of PO register */ + iowrite32(chip->ioh_gpio_reg.po_reg, &chip->reg->regs[chip->ch].po); + /* to store contents of PM register */ + iowrite32(chip->ioh_gpio_reg.pm_reg, &chip->reg->regs[chip->ch].pm); +} +#endif + +static void ioh_gpio_setup(struct ioh_gpio *chip, int num_port) +{ + struct gpio_chip *gpio = &chip->gpio; + + gpio->label = dev_name(chip->dev); + gpio->owner = THIS_MODULE; + gpio->direction_input = ioh_gpio_direction_input; + gpio->get = ioh_gpio_get; + gpio->direction_output = ioh_gpio_direction_output; + gpio->set = ioh_gpio_set; + gpio->dbg_show = NULL; + gpio->base = -1; + gpio->ngpio = num_port; + gpio->can_sleep = 0; +} + +static int __devinit ioh_gpio_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int ret; + int i; + struct ioh_gpio *chip; + void __iomem *base; + void __iomem *chip_save; + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "%s : pci_enable_device failed", __func__); + goto err_pci_enable; + } + + ret = pci_request_regions(pdev, KBUILD_MODNAME); + if (ret) { + dev_err(&pdev->dev, "pci_request_regions failed-%d", ret); + goto err_request_regions; + } + + base = pci_iomap(pdev, 1, 0); + if (base == 0) { + dev_err(&pdev->dev, "%s : pci_iomap failed", __func__); + ret = -ENOMEM; + goto err_iomap; + } + + chip_save = kzalloc(sizeof(*chip) * 8, GFP_KERNEL); + if (chip_save == NULL) { + dev_err(&pdev->dev, "%s : kzalloc failed", __func__); + ret = -ENOMEM; + goto err_kzalloc; + } + + chip = chip_save; + for (i = 0; i < 8; i++, chip++) { + chip->dev = &pdev->dev; + chip->base = base; + chip->reg = chip->base; + chip->ch = i; + mutex_init(&chip->lock); + ioh_gpio_setup(chip, num_ports[i]); + ret = gpiochip_add(&chip->gpio); + if (ret) { + dev_err(&pdev->dev, "IOH gpio: Failed to register GPIO\n"); + goto err_gpiochip_add; + } + } + + chip = chip_save; + pci_set_drvdata(pdev, chip); + + return 0; + +err_gpiochip_add: + for (; i != 0; i--) { + chip--; + ret = gpiochip_remove(&chip->gpio); + if (ret) + dev_err(&pdev->dev, "Failed gpiochip_remove(%d)\n", i); + } + kfree(chip_save); + +err_kzalloc: + pci_iounmap(pdev, base); + +err_iomap: + pci_release_regions(pdev); + +err_request_regions: + pci_disable_device(pdev); + +err_pci_enable: + + dev_err(&pdev->dev, "%s Failed returns %d\n", __func__, ret); + return ret; +} + +static void __devexit ioh_gpio_remove(struct pci_dev *pdev) +{ + int err; + int i; + struct ioh_gpio *chip = pci_get_drvdata(pdev); + void __iomem *chip_save; + + chip_save = chip; + for (i = 0; i < 8; i++, chip++) { + err = gpiochip_remove(&chip->gpio); + if (err) + dev_err(&pdev->dev, "Failed gpiochip_remove\n"); + } + + chip = chip_save; + pci_iounmap(pdev, chip->base); + pci_release_regions(pdev); + pci_disable_device(pdev); + kfree(chip); +} + +#ifdef CONFIG_PM +static int ioh_gpio_suspend(struct pci_dev *pdev, pm_message_t state) +{ + s32 ret; + struct ioh_gpio *chip = pci_get_drvdata(pdev); + + ioh_gpio_save_reg_conf(chip); + ioh_gpio_restore_reg_conf(chip); + + ret = pci_save_state(pdev); + if (ret) { + dev_err(&pdev->dev, "pci_save_state Failed-%d\n", ret); + return ret; + } + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D0); + ret = pci_enable_wake(pdev, PCI_D0, 1); + if (ret) + dev_err(&pdev->dev, "pci_enable_wake Failed -%d\n", ret); + + return 0; +} + +static int ioh_gpio_resume(struct pci_dev *pdev) +{ + s32 ret; + struct ioh_gpio *chip = pci_get_drvdata(pdev); + + ret = pci_enable_wake(pdev, PCI_D0, 0); + + pci_set_power_state(pdev, PCI_D0); + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "pci_enable_device Failed-%d ", ret); + return ret; + } + pci_restore_state(pdev); + + iowrite32(0x01, &chip->reg->srst); + iowrite32(0x00, &chip->reg->srst); + ioh_gpio_restore_reg_conf(chip); + + return 0; +} +#else +#define ioh_gpio_suspend NULL +#define ioh_gpio_resume NULL +#endif + +static DEFINE_PCI_DEVICE_TABLE(ioh_gpio_pcidev_id) = { + { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x802E) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, ioh_gpio_pcidev_id); + +static struct pci_driver ioh_gpio_driver = { + .name = "ml_ioh_gpio", + .id_table = ioh_gpio_pcidev_id, + .probe = ioh_gpio_probe, + .remove = __devexit_p(ioh_gpio_remove), + .suspend = ioh_gpio_suspend, + .resume = ioh_gpio_resume +}; + +static int __init ioh_gpio_pci_init(void) +{ + return pci_register_driver(&ioh_gpio_driver); +} +module_init(ioh_gpio_pci_init); + +static void __exit ioh_gpio_pci_exit(void) +{ + pci_unregister_driver(&ioh_gpio_driver); +} +module_exit(ioh_gpio_pci_exit); + +MODULE_DESCRIPTION("OKI SEMICONDUCTOR ML-IOH series GPIO Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c new file mode 100644 index 00000000000..a610864b7e1 --- /dev/null +++ b/drivers/gpio/gpio-pca953x.c @@ -0,0 +1,768 @@ +/* + * PCA953x 4/8/16 bit I/O ports + * + * Copyright (C) 2005 Ben Gardner + * Copyright (C) 2007 Marvell International Ltd. + * + * Derived from drivers/i2c/chips/pca9539.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF_GPIO +#include +#include +#endif + +#define PCA953X_INPUT 0 +#define PCA953X_OUTPUT 1 +#define PCA953X_INVERT 2 +#define PCA953X_DIRECTION 3 + +#define PCA957X_IN 0 +#define PCA957X_INVRT 1 +#define PCA957X_BKEN 2 +#define PCA957X_PUPD 3 +#define PCA957X_CFG 4 +#define PCA957X_OUT 5 +#define PCA957X_MSK 6 +#define PCA957X_INTS 7 + +#define PCA_GPIO_MASK 0x00FF +#define PCA_INT 0x0100 +#define PCA953X_TYPE 0x1000 +#define PCA957X_TYPE 0x2000 + +static const struct i2c_device_id pca953x_id[] = { + { "pca9534", 8 | PCA953X_TYPE | PCA_INT, }, + { "pca9535", 16 | PCA953X_TYPE | PCA_INT, }, + { "pca9536", 4 | PCA953X_TYPE, }, + { "pca9537", 4 | PCA953X_TYPE | PCA_INT, }, + { "pca9538", 8 | PCA953X_TYPE | PCA_INT, }, + { "pca9539", 16 | PCA953X_TYPE | PCA_INT, }, + { "pca9554", 8 | PCA953X_TYPE | PCA_INT, }, + { "pca9555", 16 | PCA953X_TYPE | PCA_INT, }, + { "pca9556", 8 | PCA953X_TYPE, }, + { "pca9557", 8 | PCA953X_TYPE, }, + { "pca9574", 8 | PCA957X_TYPE | PCA_INT, }, + { "pca9575", 16 | PCA957X_TYPE | PCA_INT, }, + + { "max7310", 8 | PCA953X_TYPE, }, + { "max7312", 16 | PCA953X_TYPE | PCA_INT, }, + { "max7313", 16 | PCA953X_TYPE | PCA_INT, }, + { "max7315", 8 | PCA953X_TYPE | PCA_INT, }, + { "pca6107", 8 | PCA953X_TYPE | PCA_INT, }, + { "tca6408", 8 | PCA953X_TYPE | PCA_INT, }, + { "tca6416", 16 | PCA953X_TYPE | PCA_INT, }, + /* NYET: { "tca6424", 24, }, */ + { } +}; +MODULE_DEVICE_TABLE(i2c, pca953x_id); + +struct pca953x_chip { + unsigned gpio_start; + uint16_t reg_output; + uint16_t reg_direction; + struct mutex i2c_lock; + +#ifdef CONFIG_GPIO_PCA953X_IRQ + struct mutex irq_lock; + uint16_t irq_mask; + uint16_t irq_stat; + uint16_t irq_trig_raise; + uint16_t irq_trig_fall; + int irq_base; +#endif + + struct i2c_client *client; + struct pca953x_platform_data *dyn_pdata; + struct gpio_chip gpio_chip; + const char *const *names; + int chip_type; +}; + +static int pca953x_write_reg(struct pca953x_chip *chip, int reg, uint16_t val) +{ + int ret = 0; + + if (chip->gpio_chip.ngpio <= 8) + ret = i2c_smbus_write_byte_data(chip->client, reg, val); + else { + switch (chip->chip_type) { + case PCA953X_TYPE: + ret = i2c_smbus_write_word_data(chip->client, + reg << 1, val); + break; + case PCA957X_TYPE: + ret = i2c_smbus_write_byte_data(chip->client, reg << 1, + val & 0xff); + if (ret < 0) + break; + ret = i2c_smbus_write_byte_data(chip->client, + (reg << 1) + 1, + (val & 0xff00) >> 8); + break; + } + } + + if (ret < 0) { + dev_err(&chip->client->dev, "failed writing register\n"); + return ret; + } + + return 0; +} + +static int pca953x_read_reg(struct pca953x_chip *chip, int reg, uint16_t *val) +{ + int ret; + + if (chip->gpio_chip.ngpio <= 8) + ret = i2c_smbus_read_byte_data(chip->client, reg); + else + ret = i2c_smbus_read_word_data(chip->client, reg << 1); + + if (ret < 0) { + dev_err(&chip->client->dev, "failed reading register\n"); + return ret; + } + + *val = (uint16_t)ret; + return 0; +} + +static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off) +{ + struct pca953x_chip *chip; + uint16_t reg_val; + int ret, offset = 0; + + chip = container_of(gc, struct pca953x_chip, gpio_chip); + + mutex_lock(&chip->i2c_lock); + reg_val = chip->reg_direction | (1u << off); + + switch (chip->chip_type) { + case PCA953X_TYPE: + offset = PCA953X_DIRECTION; + break; + case PCA957X_TYPE: + offset = PCA957X_CFG; + break; + } + ret = pca953x_write_reg(chip, offset, reg_val); + if (ret) + goto exit; + + chip->reg_direction = reg_val; + ret = 0; +exit: + mutex_unlock(&chip->i2c_lock); + return ret; +} + +static int pca953x_gpio_direction_output(struct gpio_chip *gc, + unsigned off, int val) +{ + struct pca953x_chip *chip; + uint16_t reg_val; + int ret, offset = 0; + + chip = container_of(gc, struct pca953x_chip, gpio_chip); + + mutex_lock(&chip->i2c_lock); + /* set output level */ + if (val) + reg_val = chip->reg_output | (1u << off); + else + reg_val = chip->reg_output & ~(1u << off); + + switch (chip->chip_type) { + case PCA953X_TYPE: + offset = PCA953X_OUTPUT; + break; + case PCA957X_TYPE: + offset = PCA957X_OUT; + break; + } + ret = pca953x_write_reg(chip, offset, reg_val); + if (ret) + goto exit; + + chip->reg_output = reg_val; + + /* then direction */ + reg_val = chip->reg_direction & ~(1u << off); + switch (chip->chip_type) { + case PCA953X_TYPE: + offset = PCA953X_DIRECTION; + break; + case PCA957X_TYPE: + offset = PCA957X_CFG; + break; + } + ret = pca953x_write_reg(chip, offset, reg_val); + if (ret) + goto exit; + + chip->reg_direction = reg_val; + ret = 0; +exit: + mutex_unlock(&chip->i2c_lock); + return ret; +} + +static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) +{ + struct pca953x_chip *chip; + uint16_t reg_val; + int ret, offset = 0; + + chip = container_of(gc, struct pca953x_chip, gpio_chip); + + mutex_lock(&chip->i2c_lock); + switch (chip->chip_type) { + case PCA953X_TYPE: + offset = PCA953X_INPUT; + break; + case PCA957X_TYPE: + offset = PCA957X_IN; + break; + } + ret = pca953x_read_reg(chip, offset, ®_val); + mutex_unlock(&chip->i2c_lock); + if (ret < 0) { + /* NOTE: diagnostic already emitted; that's all we should + * do unless gpio_*_value_cansleep() calls become different + * from their nonsleeping siblings (and report faults). + */ + return 0; + } + + return (reg_val & (1u << off)) ? 1 : 0; +} + +static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) +{ + struct pca953x_chip *chip; + uint16_t reg_val; + int ret, offset = 0; + + chip = container_of(gc, struct pca953x_chip, gpio_chip); + + mutex_lock(&chip->i2c_lock); + if (val) + reg_val = chip->reg_output | (1u << off); + else + reg_val = chip->reg_output & ~(1u << off); + + switch (chip->chip_type) { + case PCA953X_TYPE: + offset = PCA953X_OUTPUT; + break; + case PCA957X_TYPE: + offset = PCA957X_OUT; + break; + } + ret = pca953x_write_reg(chip, offset, reg_val); + if (ret) + goto exit; + + chip->reg_output = reg_val; +exit: + mutex_unlock(&chip->i2c_lock); +} + +static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) +{ + struct gpio_chip *gc; + + gc = &chip->gpio_chip; + + gc->direction_input = pca953x_gpio_direction_input; + gc->direction_output = pca953x_gpio_direction_output; + gc->get = pca953x_gpio_get_value; + gc->set = pca953x_gpio_set_value; + gc->can_sleep = 1; + + gc->base = chip->gpio_start; + gc->ngpio = gpios; + gc->label = chip->client->name; + gc->dev = &chip->client->dev; + gc->owner = THIS_MODULE; + gc->names = chip->names; +} + +#ifdef CONFIG_GPIO_PCA953X_IRQ +static int pca953x_gpio_to_irq(struct gpio_chip *gc, unsigned off) +{ + struct pca953x_chip *chip; + + chip = container_of(gc, struct pca953x_chip, gpio_chip); + return chip->irq_base + off; +} + +static void pca953x_irq_mask(struct irq_data *d) +{ + struct pca953x_chip *chip = irq_data_get_irq_chip_data(d); + + chip->irq_mask &= ~(1 << (d->irq - chip->irq_base)); +} + +static void pca953x_irq_unmask(struct irq_data *d) +{ + struct pca953x_chip *chip = irq_data_get_irq_chip_data(d); + + chip->irq_mask |= 1 << (d->irq - chip->irq_base); +} + +static void pca953x_irq_bus_lock(struct irq_data *d) +{ + struct pca953x_chip *chip = irq_data_get_irq_chip_data(d); + + mutex_lock(&chip->irq_lock); +} + +static void pca953x_irq_bus_sync_unlock(struct irq_data *d) +{ + struct pca953x_chip *chip = irq_data_get_irq_chip_data(d); + uint16_t new_irqs; + uint16_t level; + + /* Look for any newly setup interrupt */ + new_irqs = chip->irq_trig_fall | chip->irq_trig_raise; + new_irqs &= ~chip->reg_direction; + + while (new_irqs) { + level = __ffs(new_irqs); + pca953x_gpio_direction_input(&chip->gpio_chip, level); + new_irqs &= ~(1 << level); + } + + mutex_unlock(&chip->irq_lock); +} + +static int pca953x_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct pca953x_chip *chip = irq_data_get_irq_chip_data(d); + uint16_t level = d->irq - chip->irq_base; + uint16_t mask = 1 << level; + + if (!(type & IRQ_TYPE_EDGE_BOTH)) { + dev_err(&chip->client->dev, "irq %d: unsupported type %d\n", + d->irq, type); + return -EINVAL; + } + + if (type & IRQ_TYPE_EDGE_FALLING) + chip->irq_trig_fall |= mask; + else + chip->irq_trig_fall &= ~mask; + + if (type & IRQ_TYPE_EDGE_RISING) + chip->irq_trig_raise |= mask; + else + chip->irq_trig_raise &= ~mask; + + return 0; +} + +static struct irq_chip pca953x_irq_chip = { + .name = "pca953x", + .irq_mask = pca953x_irq_mask, + .irq_unmask = pca953x_irq_unmask, + .irq_bus_lock = pca953x_irq_bus_lock, + .irq_bus_sync_unlock = pca953x_irq_bus_sync_unlock, + .irq_set_type = pca953x_irq_set_type, +}; + +static uint16_t pca953x_irq_pending(struct pca953x_chip *chip) +{ + uint16_t cur_stat; + uint16_t old_stat; + uint16_t pending; + uint16_t trigger; + int ret, offset = 0; + + switch (chip->chip_type) { + case PCA953X_TYPE: + offset = PCA953X_INPUT; + break; + case PCA957X_TYPE: + offset = PCA957X_IN; + break; + } + ret = pca953x_read_reg(chip, offset, &cur_stat); + if (ret) + return 0; + + /* Remove output pins from the equation */ + cur_stat &= chip->reg_direction; + + old_stat = chip->irq_stat; + trigger = (cur_stat ^ old_stat) & chip->irq_mask; + + if (!trigger) + return 0; + + chip->irq_stat = cur_stat; + + pending = (old_stat & chip->irq_trig_fall) | + (cur_stat & chip->irq_trig_raise); + pending &= trigger; + + return pending; +} + +static irqreturn_t pca953x_irq_handler(int irq, void *devid) +{ + struct pca953x_chip *chip = devid; + uint16_t pending; + uint16_t level; + + pending = pca953x_irq_pending(chip); + + if (!pending) + return IRQ_HANDLED; + + do { + level = __ffs(pending); + generic_handle_irq(level + chip->irq_base); + + pending &= ~(1 << level); + } while (pending); + + return IRQ_HANDLED; +} + +static int pca953x_irq_setup(struct pca953x_chip *chip, + const struct i2c_device_id *id) +{ + struct i2c_client *client = chip->client; + struct pca953x_platform_data *pdata = client->dev.platform_data; + int ret, offset = 0; + + if (pdata->irq_base != -1 + && (id->driver_data & PCA_INT)) { + int lvl; + + switch (chip->chip_type) { + case PCA953X_TYPE: + offset = PCA953X_INPUT; + break; + case PCA957X_TYPE: + offset = PCA957X_IN; + break; + } + ret = pca953x_read_reg(chip, offset, &chip->irq_stat); + if (ret) + goto out_failed; + + /* + * There is no way to know which GPIO line generated the + * interrupt. We have to rely on the previous read for + * this purpose. + */ + chip->irq_stat &= chip->reg_direction; + chip->irq_base = pdata->irq_base; + mutex_init(&chip->irq_lock); + + for (lvl = 0; lvl < chip->gpio_chip.ngpio; lvl++) { + int irq = lvl + chip->irq_base; + + irq_set_chip_data(irq, chip); + irq_set_chip_and_handler(irq, &pca953x_irq_chip, + handle_simple_irq); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + } + + ret = request_threaded_irq(client->irq, + NULL, + pca953x_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(&client->dev), chip); + if (ret) { + dev_err(&client->dev, "failed to request irq %d\n", + client->irq); + goto out_failed; + } + + chip->gpio_chip.to_irq = pca953x_gpio_to_irq; + } + + return 0; + +out_failed: + chip->irq_base = -1; + return ret; +} + +static void pca953x_irq_teardown(struct pca953x_chip *chip) +{ + if (chip->irq_base != -1) + free_irq(chip->client->irq, chip); +} +#else /* CONFIG_GPIO_PCA953X_IRQ */ +static int pca953x_irq_setup(struct pca953x_chip *chip, + const struct i2c_device_id *id) +{ + struct i2c_client *client = chip->client; + struct pca953x_platform_data *pdata = client->dev.platform_data; + + if (pdata->irq_base != -1 && (id->driver_data & PCA_INT)) + dev_warn(&client->dev, "interrupt support not compiled in\n"); + + return 0; +} + +static void pca953x_irq_teardown(struct pca953x_chip *chip) +{ +} +#endif + +/* + * Handlers for alternative sources of platform_data + */ +#ifdef CONFIG_OF_GPIO +/* + * Translate OpenFirmware node properties into platform_data + */ +static struct pca953x_platform_data * +pca953x_get_alt_pdata(struct i2c_client *client) +{ + struct pca953x_platform_data *pdata; + struct device_node *node; + const __be32 *val; + int size; + + node = client->dev.of_node; + if (node == NULL) + return NULL; + + pdata = kzalloc(sizeof(struct pca953x_platform_data), GFP_KERNEL); + if (pdata == NULL) { + dev_err(&client->dev, "Unable to allocate platform_data\n"); + return NULL; + } + + pdata->gpio_base = -1; + val = of_get_property(node, "linux,gpio-base", &size); + if (val) { + if (size != sizeof(*val)) + dev_warn(&client->dev, "%s: wrong linux,gpio-base\n", + node->full_name); + else + pdata->gpio_base = be32_to_cpup(val); + } + + val = of_get_property(node, "polarity", NULL); + if (val) + pdata->invert = *val; + + return pdata; +} +#else +static struct pca953x_platform_data * +pca953x_get_alt_pdata(struct i2c_client *client) +{ + return NULL; +} +#endif + +static int __devinit device_pca953x_init(struct pca953x_chip *chip, int invert) +{ + int ret; + + ret = pca953x_read_reg(chip, PCA953X_OUTPUT, &chip->reg_output); + if (ret) + goto out; + + ret = pca953x_read_reg(chip, PCA953X_DIRECTION, + &chip->reg_direction); + if (ret) + goto out; + + /* set platform specific polarity inversion */ + ret = pca953x_write_reg(chip, PCA953X_INVERT, invert); + if (ret) + goto out; + return 0; +out: + return ret; +} + +static int __devinit device_pca957x_init(struct pca953x_chip *chip, int invert) +{ + int ret; + uint16_t val = 0; + + /* Let every port in proper state, that could save power */ + pca953x_write_reg(chip, PCA957X_PUPD, 0x0); + pca953x_write_reg(chip, PCA957X_CFG, 0xffff); + pca953x_write_reg(chip, PCA957X_OUT, 0x0); + + ret = pca953x_read_reg(chip, PCA957X_IN, &val); + if (ret) + goto out; + ret = pca953x_read_reg(chip, PCA957X_OUT, &chip->reg_output); + if (ret) + goto out; + ret = pca953x_read_reg(chip, PCA957X_CFG, &chip->reg_direction); + if (ret) + goto out; + + /* set platform specific polarity inversion */ + pca953x_write_reg(chip, PCA957X_INVRT, invert); + + /* To enable register 6, 7 to controll pull up and pull down */ + pca953x_write_reg(chip, PCA957X_BKEN, 0x202); + + return 0; +out: + return ret; +} + +static int __devinit pca953x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pca953x_platform_data *pdata; + struct pca953x_chip *chip; + int ret = 0; + + chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + pdata = client->dev.platform_data; + if (pdata == NULL) { + pdata = pca953x_get_alt_pdata(client); + /* + * Unlike normal platform_data, this is allocated + * dynamically and must be freed in the driver + */ + chip->dyn_pdata = pdata; + } + + if (pdata == NULL) { + dev_dbg(&client->dev, "no platform data\n"); + ret = -EINVAL; + goto out_failed; + } + + chip->client = client; + + chip->gpio_start = pdata->gpio_base; + + chip->names = pdata->names; + chip->chip_type = id->driver_data & (PCA953X_TYPE | PCA957X_TYPE); + + mutex_init(&chip->i2c_lock); + + /* initialize cached registers from their original values. + * we can't share this chip with another i2c master. + */ + pca953x_setup_gpio(chip, id->driver_data & PCA_GPIO_MASK); + + if (chip->chip_type == PCA953X_TYPE) + device_pca953x_init(chip, pdata->invert); + else if (chip->chip_type == PCA957X_TYPE) + device_pca957x_init(chip, pdata->invert); + else + goto out_failed; + + ret = pca953x_irq_setup(chip, id); + if (ret) + goto out_failed; + + ret = gpiochip_add(&chip->gpio_chip); + if (ret) + goto out_failed_irq; + + if (pdata->setup) { + ret = pdata->setup(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) + dev_warn(&client->dev, "setup failed, %d\n", ret); + } + + i2c_set_clientdata(client, chip); + return 0; + +out_failed_irq: + pca953x_irq_teardown(chip); +out_failed: + kfree(chip->dyn_pdata); + kfree(chip); + return ret; +} + +static int pca953x_remove(struct i2c_client *client) +{ + struct pca953x_platform_data *pdata = client->dev.platform_data; + struct pca953x_chip *chip = i2c_get_clientdata(client); + int ret = 0; + + if (pdata->teardown) { + ret = pdata->teardown(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) { + dev_err(&client->dev, "%s failed, %d\n", + "teardown", ret); + return ret; + } + } + + ret = gpiochip_remove(&chip->gpio_chip); + if (ret) { + dev_err(&client->dev, "%s failed, %d\n", + "gpiochip_remove()", ret); + return ret; + } + + pca953x_irq_teardown(chip); + kfree(chip->dyn_pdata); + kfree(chip); + return 0; +} + +static struct i2c_driver pca953x_driver = { + .driver = { + .name = "pca953x", + }, + .probe = pca953x_probe, + .remove = pca953x_remove, + .id_table = pca953x_id, +}; + +static int __init pca953x_init(void) +{ + return i2c_add_driver(&pca953x_driver); +} +/* register after i2c postcore initcall and before + * subsys initcalls that may rely on these GPIOs + */ +subsys_initcall(pca953x_init); + +static void __exit pca953x_exit(void) +{ + i2c_del_driver(&pca953x_driver); +} +module_exit(pca953x_exit); + +MODULE_AUTHOR("eric miao "); +MODULE_DESCRIPTION("GPIO expander driver for PCA953x"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c new file mode 100644 index 00000000000..7369fdda92b --- /dev/null +++ b/drivers/gpio/gpio-pcf857x.c @@ -0,0 +1,369 @@ +/* + * Driver for pcf857x, pca857x, and pca967x I2C GPIO expanders + * + * Copyright (C) 2007 David Brownell + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + + +static const struct i2c_device_id pcf857x_id[] = { + { "pcf8574", 8 }, + { "pcf8574a", 8 }, + { "pca8574", 8 }, + { "pca9670", 8 }, + { "pca9672", 8 }, + { "pca9674", 8 }, + { "pcf8575", 16 }, + { "pca8575", 16 }, + { "pca9671", 16 }, + { "pca9673", 16 }, + { "pca9675", 16 }, + { "max7328", 8 }, + { "max7329", 8 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcf857x_id); + +/* + * The pcf857x, pca857x, and pca967x chips only expose one read and one + * write register. Writing a "one" bit (to match the reset state) lets + * that pin be used as an input; it's not an open-drain model, but acts + * a bit like one. This is described as "quasi-bidirectional"; read the + * chip documentation for details. + * + * Many other I2C GPIO expander chips (like the pca953x models) have + * more complex register models and more conventional circuitry using + * push/pull drivers. They often use the same 0x20..0x27 addresses as + * pcf857x parts, making the "legacy" I2C driver model problematic. + */ +struct pcf857x { + struct gpio_chip chip; + struct i2c_client *client; + struct mutex lock; /* protect 'out' */ + unsigned out; /* software latch */ +}; + +/*-------------------------------------------------------------------------*/ + +/* Talk to 8-bit I/O expander */ + +static int pcf857x_input8(struct gpio_chip *chip, unsigned offset) +{ + struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + int status; + + mutex_lock(&gpio->lock); + gpio->out |= (1 << offset); + status = i2c_smbus_write_byte(gpio->client, gpio->out); + mutex_unlock(&gpio->lock); + + return status; +} + +static int pcf857x_get8(struct gpio_chip *chip, unsigned offset) +{ + struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + s32 value; + + value = i2c_smbus_read_byte(gpio->client); + return (value < 0) ? 0 : (value & (1 << offset)); +} + +static int pcf857x_output8(struct gpio_chip *chip, unsigned offset, int value) +{ + struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + unsigned bit = 1 << offset; + int status; + + mutex_lock(&gpio->lock); + if (value) + gpio->out |= bit; + else + gpio->out &= ~bit; + status = i2c_smbus_write_byte(gpio->client, gpio->out); + mutex_unlock(&gpio->lock); + + return status; +} + +static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value) +{ + pcf857x_output8(chip, offset, value); +} + +/*-------------------------------------------------------------------------*/ + +/* Talk to 16-bit I/O expander */ + +static int i2c_write_le16(struct i2c_client *client, u16 word) +{ + u8 buf[2] = { word & 0xff, word >> 8, }; + int status; + + status = i2c_master_send(client, buf, 2); + return (status < 0) ? status : 0; +} + +static int i2c_read_le16(struct i2c_client *client) +{ + u8 buf[2]; + int status; + + status = i2c_master_recv(client, buf, 2); + if (status < 0) + return status; + return (buf[1] << 8) | buf[0]; +} + +static int pcf857x_input16(struct gpio_chip *chip, unsigned offset) +{ + struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + int status; + + mutex_lock(&gpio->lock); + gpio->out |= (1 << offset); + status = i2c_write_le16(gpio->client, gpio->out); + mutex_unlock(&gpio->lock); + + return status; +} + +static int pcf857x_get16(struct gpio_chip *chip, unsigned offset) +{ + struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + int value; + + value = i2c_read_le16(gpio->client); + return (value < 0) ? 0 : (value & (1 << offset)); +} + +static int pcf857x_output16(struct gpio_chip *chip, unsigned offset, int value) +{ + struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + unsigned bit = 1 << offset; + int status; + + mutex_lock(&gpio->lock); + if (value) + gpio->out |= bit; + else + gpio->out &= ~bit; + status = i2c_write_le16(gpio->client, gpio->out); + mutex_unlock(&gpio->lock); + + return status; +} + +static void pcf857x_set16(struct gpio_chip *chip, unsigned offset, int value) +{ + pcf857x_output16(chip, offset, value); +} + +/*-------------------------------------------------------------------------*/ + +static int pcf857x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pcf857x_platform_data *pdata; + struct pcf857x *gpio; + int status; + + pdata = client->dev.platform_data; + if (!pdata) { + dev_dbg(&client->dev, "no platform data\n"); + } + + /* Allocate, initialize, and register this gpio_chip. */ + gpio = kzalloc(sizeof *gpio, GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + mutex_init(&gpio->lock); + + gpio->chip.base = pdata ? pdata->gpio_base : -1; + gpio->chip.can_sleep = 1; + gpio->chip.dev = &client->dev; + gpio->chip.owner = THIS_MODULE; + + /* NOTE: the OnSemi jlc1562b is also largely compatible with + * these parts, notably for output. It has a low-resolution + * DAC instead of pin change IRQs; and its inputs can be the + * result of comparators. + */ + + /* 8574 addresses are 0x20..0x27; 8574a uses 0x38..0x3f; + * 9670, 9672, 9764, and 9764a use quite a variety. + * + * NOTE: we don't distinguish here between *4 and *4a parts. + */ + gpio->chip.ngpio = id->driver_data; + if (gpio->chip.ngpio == 8) { + gpio->chip.direction_input = pcf857x_input8; + gpio->chip.get = pcf857x_get8; + gpio->chip.direction_output = pcf857x_output8; + gpio->chip.set = pcf857x_set8; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE)) + status = -EIO; + + /* fail if there's no chip present */ + else + status = i2c_smbus_read_byte(client); + + /* '75/'75c addresses are 0x20..0x27, just like the '74; + * the '75c doesn't have a current source pulling high. + * 9671, 9673, and 9765 use quite a variety of addresses. + * + * NOTE: we don't distinguish here between '75 and '75c parts. + */ + } else if (gpio->chip.ngpio == 16) { + gpio->chip.direction_input = pcf857x_input16; + gpio->chip.get = pcf857x_get16; + gpio->chip.direction_output = pcf857x_output16; + gpio->chip.set = pcf857x_set16; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + status = -EIO; + + /* fail if there's no chip present */ + else + status = i2c_read_le16(client); + + } else { + dev_dbg(&client->dev, "unsupported number of gpios\n"); + status = -EINVAL; + } + + if (status < 0) + goto fail; + + gpio->chip.label = client->name; + + gpio->client = client; + i2c_set_clientdata(client, gpio); + + /* NOTE: these chips have strange "quasi-bidirectional" I/O pins. + * We can't actually know whether a pin is configured (a) as output + * and driving the signal low, or (b) as input and reporting a low + * value ... without knowing the last value written since the chip + * came out of reset (if any). We can't read the latched output. + * + * In short, the only reliable solution for setting up pin direction + * is to do it explicitly. The setup() method can do that, but it + * may cause transient glitching since it can't know the last value + * written (some pins may need to be driven low). + * + * Using pdata->n_latch avoids that trouble. When left initialized + * to zero, our software copy of the "latch" then matches the chip's + * all-ones reset state. Otherwise it flags pins to be driven low. + */ + gpio->out = pdata ? ~pdata->n_latch : ~0; + + status = gpiochip_add(&gpio->chip); + if (status < 0) + goto fail; + + /* NOTE: these chips can issue "some pin-changed" IRQs, which we + * don't yet even try to use. Among other issues, the relevant + * genirq state isn't available to modular drivers; and most irq + * methods can't be called from sleeping contexts. + */ + + dev_info(&client->dev, "gpios %d..%d on a %s%s\n", + gpio->chip.base, + gpio->chip.base + gpio->chip.ngpio - 1, + client->name, + client->irq ? " (irq ignored)" : ""); + + /* Let platform code set up the GPIOs and their users. + * Now is the first time anyone could use them. + */ + if (pdata && pdata->setup) { + status = pdata->setup(client, + gpio->chip.base, gpio->chip.ngpio, + pdata->context); + if (status < 0) + dev_warn(&client->dev, "setup --> %d\n", status); + } + + return 0; + +fail: + dev_dbg(&client->dev, "probe error %d for '%s'\n", + status, client->name); + kfree(gpio); + return status; +} + +static int pcf857x_remove(struct i2c_client *client) +{ + struct pcf857x_platform_data *pdata = client->dev.platform_data; + struct pcf857x *gpio = i2c_get_clientdata(client); + int status = 0; + + if (pdata && pdata->teardown) { + status = pdata->teardown(client, + gpio->chip.base, gpio->chip.ngpio, + pdata->context); + if (status < 0) { + dev_err(&client->dev, "%s --> %d\n", + "teardown", status); + return status; + } + } + + status = gpiochip_remove(&gpio->chip); + if (status == 0) + kfree(gpio); + else + dev_err(&client->dev, "%s --> %d\n", "remove", status); + return status; +} + +static struct i2c_driver pcf857x_driver = { + .driver = { + .name = "pcf857x", + .owner = THIS_MODULE, + }, + .probe = pcf857x_probe, + .remove = pcf857x_remove, + .id_table = pcf857x_id, +}; + +static int __init pcf857x_init(void) +{ + return i2c_add_driver(&pcf857x_driver); +} +/* register after i2c postcore initcall and before + * subsys initcalls that may rely on these GPIOs + */ +subsys_initcall(pcf857x_init); + +static void __exit pcf857x_exit(void) +{ + i2c_del_driver(&pcf857x_driver); +} +module_exit(pcf857x_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Brownell"); diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c new file mode 100644 index 00000000000..36919e77c49 --- /dev/null +++ b/drivers/gpio/gpio-pch.c @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ +#include +#include +#include + +#define PCH_GPIO_ALL_PINS 0xfff /* Mask for GPIO pins 0 to 11 */ +#define GPIO_NUM_PINS 12 /* Specifies number of GPIO PINS GPIO0-GPIO11 */ + +struct pch_regs { + u32 ien; + u32 istatus; + u32 idisp; + u32 iclr; + u32 imask; + u32 imaskclr; + u32 po; + u32 pi; + u32 pm; + u32 im0; + u32 im1; + u32 reserved[4]; + u32 reset; +}; + +/** + * struct pch_gpio_reg_data - The register store data. + * @po_reg: To store contents of PO register. + * @pm_reg: To store contents of PM register. + */ +struct pch_gpio_reg_data { + u32 po_reg; + u32 pm_reg; +}; + +/** + * struct pch_gpio - GPIO private data structure. + * @base: PCI base address of Memory mapped I/O register. + * @reg: Memory mapped PCH GPIO register list. + * @dev: Pointer to device structure. + * @gpio: Data for GPIO infrastructure. + * @pch_gpio_reg: Memory mapped Register data is saved here + * when suspend. + */ +struct pch_gpio { + void __iomem *base; + struct pch_regs __iomem *reg; + struct device *dev; + struct gpio_chip gpio; + struct pch_gpio_reg_data pch_gpio_reg; + struct mutex lock; +}; + +static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) +{ + u32 reg_val; + struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio); + + mutex_lock(&chip->lock); + reg_val = ioread32(&chip->reg->po); + if (val) + reg_val |= (1 << nr); + else + reg_val &= ~(1 << nr); + + iowrite32(reg_val, &chip->reg->po); + mutex_unlock(&chip->lock); +} + +static int pch_gpio_get(struct gpio_chip *gpio, unsigned nr) +{ + struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio); + + return ioread32(&chip->reg->pi) & (1 << nr); +} + +static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, + int val) +{ + struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio); + u32 pm; + u32 reg_val; + + mutex_lock(&chip->lock); + pm = ioread32(&chip->reg->pm) & PCH_GPIO_ALL_PINS; + pm |= (1 << nr); + iowrite32(pm, &chip->reg->pm); + + reg_val = ioread32(&chip->reg->po); + if (val) + reg_val |= (1 << nr); + else + reg_val &= ~(1 << nr); + iowrite32(reg_val, &chip->reg->po); + + mutex_unlock(&chip->lock); + + return 0; +} + +static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) +{ + struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio); + u32 pm; + + mutex_lock(&chip->lock); + pm = ioread32(&chip->reg->pm) & PCH_GPIO_ALL_PINS; /*bits 0-11*/ + pm &= ~(1 << nr); + iowrite32(pm, &chip->reg->pm); + mutex_unlock(&chip->lock); + + return 0; +} + +/* + * Save register configuration and disable interrupts. + */ +static void pch_gpio_save_reg_conf(struct pch_gpio *chip) +{ + chip->pch_gpio_reg.po_reg = ioread32(&chip->reg->po); + chip->pch_gpio_reg.pm_reg = ioread32(&chip->reg->pm); +} + +/* + * This function restores the register configuration of the GPIO device. + */ +static void pch_gpio_restore_reg_conf(struct pch_gpio *chip) +{ + /* to store contents of PO register */ + iowrite32(chip->pch_gpio_reg.po_reg, &chip->reg->po); + /* to store contents of PM register */ + iowrite32(chip->pch_gpio_reg.pm_reg, &chip->reg->pm); +} + +static void pch_gpio_setup(struct pch_gpio *chip) +{ + struct gpio_chip *gpio = &chip->gpio; + + gpio->label = dev_name(chip->dev); + gpio->owner = THIS_MODULE; + gpio->direction_input = pch_gpio_direction_input; + gpio->get = pch_gpio_get; + gpio->direction_output = pch_gpio_direction_output; + gpio->set = pch_gpio_set; + gpio->dbg_show = NULL; + gpio->base = -1; + gpio->ngpio = GPIO_NUM_PINS; + gpio->can_sleep = 0; +} + +static int __devinit pch_gpio_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + s32 ret; + struct pch_gpio *chip; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + chip->dev = &pdev->dev; + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "%s : pci_enable_device FAILED", __func__); + goto err_pci_enable; + } + + ret = pci_request_regions(pdev, KBUILD_MODNAME); + if (ret) { + dev_err(&pdev->dev, "pci_request_regions FAILED-%d", ret); + goto err_request_regions; + } + + chip->base = pci_iomap(pdev, 1, 0); + if (chip->base == 0) { + dev_err(&pdev->dev, "%s : pci_iomap FAILED", __func__); + ret = -ENOMEM; + goto err_iomap; + } + + chip->reg = chip->base; + pci_set_drvdata(pdev, chip); + mutex_init(&chip->lock); + pch_gpio_setup(chip); + ret = gpiochip_add(&chip->gpio); + if (ret) { + dev_err(&pdev->dev, "PCH gpio: Failed to register GPIO\n"); + goto err_gpiochip_add; + } + + return 0; + +err_gpiochip_add: + pci_iounmap(pdev, chip->base); + +err_iomap: + pci_release_regions(pdev); + +err_request_regions: + pci_disable_device(pdev); + +err_pci_enable: + kfree(chip); + dev_err(&pdev->dev, "%s Failed returns %d\n", __func__, ret); + return ret; +} + +static void __devexit pch_gpio_remove(struct pci_dev *pdev) +{ + int err; + struct pch_gpio *chip = pci_get_drvdata(pdev); + + err = gpiochip_remove(&chip->gpio); + if (err) + dev_err(&pdev->dev, "Failed gpiochip_remove\n"); + + pci_iounmap(pdev, chip->base); + pci_release_regions(pdev); + pci_disable_device(pdev); + kfree(chip); +} + +#ifdef CONFIG_PM +static int pch_gpio_suspend(struct pci_dev *pdev, pm_message_t state) +{ + s32 ret; + struct pch_gpio *chip = pci_get_drvdata(pdev); + + pch_gpio_save_reg_conf(chip); + pch_gpio_restore_reg_conf(chip); + + ret = pci_save_state(pdev); + if (ret) { + dev_err(&pdev->dev, "pci_save_state Failed-%d\n", ret); + return ret; + } + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D0); + ret = pci_enable_wake(pdev, PCI_D0, 1); + if (ret) + dev_err(&pdev->dev, "pci_enable_wake Failed -%d\n", ret); + + return 0; +} + +static int pch_gpio_resume(struct pci_dev *pdev) +{ + s32 ret; + struct pch_gpio *chip = pci_get_drvdata(pdev); + + ret = pci_enable_wake(pdev, PCI_D0, 0); + + pci_set_power_state(pdev, PCI_D0); + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "pci_enable_device Failed-%d ", ret); + return ret; + } + pci_restore_state(pdev); + + iowrite32(0x01, &chip->reg->reset); + iowrite32(0x00, &chip->reg->reset); + pch_gpio_restore_reg_conf(chip); + + return 0; +} +#else +#define pch_gpio_suspend NULL +#define pch_gpio_resume NULL +#endif + +#define PCI_VENDOR_ID_ROHM 0x10DB +static DEFINE_PCI_DEVICE_TABLE(pch_gpio_pcidev_id) = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8803) }, + { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8014) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, pch_gpio_pcidev_id); + +static struct pci_driver pch_gpio_driver = { + .name = "pch_gpio", + .id_table = pch_gpio_pcidev_id, + .probe = pch_gpio_probe, + .remove = __devexit_p(pch_gpio_remove), + .suspend = pch_gpio_suspend, + .resume = pch_gpio_resume +}; + +static int __init pch_gpio_pci_init(void) +{ + return pci_register_driver(&pch_gpio_driver); +} +module_init(pch_gpio_pci_init); + +static void __exit pch_gpio_pci_exit(void) +{ + pci_unregister_driver(&pch_gpio_driver); +} +module_exit(pch_gpio_pci_exit); + +MODULE_DESCRIPTION("PCH GPIO PCI Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c new file mode 100644 index 00000000000..2c5a18f32bf --- /dev/null +++ b/drivers/gpio/gpio-pl061.c @@ -0,0 +1,358 @@ +/* + * 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 + * published by the Free Software Foundation. + * + * Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061) + * + * Data sheet: ARM DDI 0190B, September 2000 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GPIODIR 0x400 +#define GPIOIS 0x404 +#define GPIOIBE 0x408 +#define GPIOIEV 0x40C +#define GPIOIE 0x410 +#define GPIORIS 0x414 +#define GPIOMIS 0x418 +#define GPIOIC 0x41C + +#define PL061_GPIO_NR 8 + +struct pl061_gpio { + /* We use a list of pl061_gpio structs for each trigger IRQ in the main + * interrupts controller of the system. We need this to support systems + * in which more that one PL061s are connected to the same IRQ. The ISR + * interates through this list to find the source of the interrupt. + */ + struct list_head list; + + /* Each of the two spinlocks protects a different set of hardware + * regiters and data structurs. This decouples the code of the IRQ from + * the GPIO code. This also makes the case of a GPIO routine call from + * the IRQ code simpler. + */ + spinlock_t lock; /* GPIO registers */ + spinlock_t irq_lock; /* IRQ registers */ + + void __iomem *base; + unsigned irq_base; + struct gpio_chip gc; +}; + +static int pl061_direction_input(struct gpio_chip *gc, unsigned offset) +{ + struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + unsigned long flags; + unsigned char gpiodir; + + if (offset >= gc->ngpio) + return -EINVAL; + + spin_lock_irqsave(&chip->lock, flags); + gpiodir = readb(chip->base + GPIODIR); + gpiodir &= ~(1 << offset); + writeb(gpiodir, chip->base + GPIODIR); + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int pl061_direction_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + unsigned long flags; + unsigned char gpiodir; + + if (offset >= gc->ngpio) + return -EINVAL; + + spin_lock_irqsave(&chip->lock, flags); + writeb(!!value << offset, chip->base + (1 << (offset + 2))); + gpiodir = readb(chip->base + GPIODIR); + gpiodir |= 1 << offset; + writeb(gpiodir, chip->base + GPIODIR); + + /* + * gpio value is set again, because pl061 doesn't allow to set value of + * a gpio pin before configuring it in OUT mode. + */ + writeb(!!value << offset, chip->base + (1 << (offset + 2))); + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int pl061_get_value(struct gpio_chip *gc, unsigned offset) +{ + struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + + return !!readb(chip->base + (1 << (offset + 2))); +} + +static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value) +{ + struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + + writeb(!!value << offset, chip->base + (1 << (offset + 2))); +} + +static int pl061_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + + if (chip->irq_base == (unsigned) -1) + return -EINVAL; + + return chip->irq_base + offset; +} + +/* + * PL061 GPIO IRQ + */ +static void pl061_irq_disable(struct irq_data *d) +{ + struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); + int offset = d->irq - chip->irq_base; + unsigned long flags; + u8 gpioie; + + spin_lock_irqsave(&chip->irq_lock, flags); + gpioie = readb(chip->base + GPIOIE); + gpioie &= ~(1 << offset); + writeb(gpioie, chip->base + GPIOIE); + spin_unlock_irqrestore(&chip->irq_lock, flags); +} + +static void pl061_irq_enable(struct irq_data *d) +{ + struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); + int offset = d->irq - chip->irq_base; + unsigned long flags; + u8 gpioie; + + spin_lock_irqsave(&chip->irq_lock, flags); + gpioie = readb(chip->base + GPIOIE); + gpioie |= 1 << offset; + writeb(gpioie, chip->base + GPIOIE); + spin_unlock_irqrestore(&chip->irq_lock, flags); +} + +static int pl061_irq_type(struct irq_data *d, unsigned trigger) +{ + struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); + int offset = d->irq - chip->irq_base; + unsigned long flags; + u8 gpiois, gpioibe, gpioiev; + + if (offset < 0 || offset >= PL061_GPIO_NR) + return -EINVAL; + + spin_lock_irqsave(&chip->irq_lock, flags); + + gpioiev = readb(chip->base + GPIOIEV); + + gpiois = readb(chip->base + GPIOIS); + if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { + gpiois |= 1 << offset; + if (trigger & IRQ_TYPE_LEVEL_HIGH) + gpioiev |= 1 << offset; + else + gpioiev &= ~(1 << offset); + } else + gpiois &= ~(1 << offset); + writeb(gpiois, chip->base + GPIOIS); + + gpioibe = readb(chip->base + GPIOIBE); + if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) + gpioibe |= 1 << offset; + else { + gpioibe &= ~(1 << offset); + if (trigger & IRQ_TYPE_EDGE_RISING) + gpioiev |= 1 << offset; + else if (trigger & IRQ_TYPE_EDGE_FALLING) + gpioiev &= ~(1 << offset); + } + writeb(gpioibe, chip->base + GPIOIBE); + + writeb(gpioiev, chip->base + GPIOIEV); + + spin_unlock_irqrestore(&chip->irq_lock, flags); + + return 0; +} + +static struct irq_chip pl061_irqchip = { + .name = "GPIO", + .irq_enable = pl061_irq_enable, + .irq_disable = pl061_irq_disable, + .irq_set_type = pl061_irq_type, +}; + +static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct list_head *chip_list = irq_get_handler_data(irq); + struct list_head *ptr; + struct pl061_gpio *chip; + + desc->irq_data.chip->irq_ack(&desc->irq_data); + list_for_each(ptr, chip_list) { + unsigned long pending; + int offset; + + chip = list_entry(ptr, struct pl061_gpio, list); + pending = readb(chip->base + GPIOMIS); + writeb(pending, chip->base + GPIOIC); + + if (pending == 0) + continue; + + for_each_set_bit(offset, &pending, PL061_GPIO_NR) + generic_handle_irq(pl061_to_irq(&chip->gc, offset)); + } + desc->irq_data.chip->irq_unmask(&desc->irq_data); +} + +static int pl061_probe(struct amba_device *dev, const struct amba_id *id) +{ + struct pl061_platform_data *pdata; + struct pl061_gpio *chip; + struct list_head *chip_list; + int ret, irq, i; + static DECLARE_BITMAP(init_irq, NR_IRQS); + + pdata = dev->dev.platform_data; + if (pdata == NULL) + return -ENODEV; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + if (!request_mem_region(dev->res.start, + resource_size(&dev->res), "pl061")) { + ret = -EBUSY; + goto free_mem; + } + + chip->base = ioremap(dev->res.start, resource_size(&dev->res)); + if (chip->base == NULL) { + ret = -ENOMEM; + goto release_region; + } + + spin_lock_init(&chip->lock); + spin_lock_init(&chip->irq_lock); + INIT_LIST_HEAD(&chip->list); + + chip->gc.direction_input = pl061_direction_input; + chip->gc.direction_output = pl061_direction_output; + chip->gc.get = pl061_get_value; + chip->gc.set = pl061_set_value; + chip->gc.to_irq = pl061_to_irq; + chip->gc.base = pdata->gpio_base; + chip->gc.ngpio = PL061_GPIO_NR; + chip->gc.label = dev_name(&dev->dev); + chip->gc.dev = &dev->dev; + chip->gc.owner = THIS_MODULE; + + chip->irq_base = pdata->irq_base; + + ret = gpiochip_add(&chip->gc); + if (ret) + goto iounmap; + + /* + * irq_chip support + */ + + if (chip->irq_base == (unsigned) -1) + return 0; + + writeb(0, chip->base + GPIOIE); /* disable irqs */ + irq = dev->irq[0]; + if (irq < 0) { + ret = -ENODEV; + goto iounmap; + } + irq_set_chained_handler(irq, pl061_irq_handler); + if (!test_and_set_bit(irq, init_irq)) { /* list initialized? */ + chip_list = kmalloc(sizeof(*chip_list), GFP_KERNEL); + if (chip_list == NULL) { + clear_bit(irq, init_irq); + ret = -ENOMEM; + goto iounmap; + } + INIT_LIST_HEAD(chip_list); + irq_set_handler_data(irq, chip_list); + } else + chip_list = irq_get_handler_data(irq); + list_add(&chip->list, chip_list); + + for (i = 0; i < PL061_GPIO_NR; i++) { + if (pdata->directions & (1 << i)) + pl061_direction_output(&chip->gc, i, + pdata->values & (1 << i)); + else + pl061_direction_input(&chip->gc, i); + + irq_set_chip_and_handler(i + chip->irq_base, &pl061_irqchip, + handle_simple_irq); + set_irq_flags(i+chip->irq_base, IRQF_VALID); + irq_set_chip_data(i + chip->irq_base, chip); + } + + return 0; + +iounmap: + iounmap(chip->base); +release_region: + release_mem_region(dev->res.start, resource_size(&dev->res)); +free_mem: + kfree(chip); + + return ret; +} + +static struct amba_id pl061_ids[] = { + { + .id = 0x00041061, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver pl061_gpio_driver = { + .drv = { + .name = "pl061_gpio", + }, + .id_table = pl061_ids, + .probe = pl061_probe, +}; + +static int __init pl061_gpio_init(void) +{ + return amba_driver_register(&pl061_gpio_driver); +} +subsys_initcall(pl061_gpio_init); + +MODULE_AUTHOR("Baruch Siach "); +MODULE_DESCRIPTION("PL061 GPIO driver"); +MODULE_LICENSE("GPL"); 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 diff --git a/drivers/gpio/gpio-rdc321x.c b/drivers/gpio/gpio-rdc321x.c new file mode 100644 index 00000000000..2762698e020 --- /dev/null +++ b/drivers/gpio/gpio-rdc321x.c @@ -0,0 +1,246 @@ +/* + * RDC321x GPIO driver + * + * Copyright (C) 2008, Volker Weiss + * Copyright (C) 2007-2010 Florian Fainelli + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rdc321x_gpio { + spinlock_t lock; + struct pci_dev *sb_pdev; + u32 data_reg[2]; + int reg1_ctrl_base; + int reg1_data_base; + int reg2_ctrl_base; + int reg2_data_base; + struct gpio_chip chip; +}; + +/* read GPIO pin */ +static int rdc_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +{ + struct rdc321x_gpio *gpch; + u32 value = 0; + int reg; + + gpch = container_of(chip, struct rdc321x_gpio, chip); + reg = gpio < 32 ? gpch->reg1_data_base : gpch->reg2_data_base; + + spin_lock(&gpch->lock); + pci_write_config_dword(gpch->sb_pdev, reg, + gpch->data_reg[gpio < 32 ? 0 : 1]); + pci_read_config_dword(gpch->sb_pdev, reg, &value); + spin_unlock(&gpch->lock); + + return (1 << (gpio & 0x1f)) & value ? 1 : 0; +} + +static void rdc_gpio_set_value_impl(struct gpio_chip *chip, + unsigned gpio, int value) +{ + struct rdc321x_gpio *gpch; + int reg = (gpio < 32) ? 0 : 1; + + gpch = container_of(chip, struct rdc321x_gpio, chip); + + if (value) + gpch->data_reg[reg] |= 1 << (gpio & 0x1f); + else + gpch->data_reg[reg] &= ~(1 << (gpio & 0x1f)); + + pci_write_config_dword(gpch->sb_pdev, + reg ? gpch->reg2_data_base : gpch->reg1_data_base, + gpch->data_reg[reg]); +} + +/* set GPIO pin to value */ +static void rdc_gpio_set_value(struct gpio_chip *chip, + unsigned gpio, int value) +{ + struct rdc321x_gpio *gpch; + + gpch = container_of(chip, struct rdc321x_gpio, chip); + spin_lock(&gpch->lock); + rdc_gpio_set_value_impl(chip, gpio, value); + spin_unlock(&gpch->lock); +} + +static int rdc_gpio_config(struct gpio_chip *chip, + unsigned gpio, int value) +{ + struct rdc321x_gpio *gpch; + int err; + u32 reg; + + gpch = container_of(chip, struct rdc321x_gpio, chip); + + spin_lock(&gpch->lock); + err = pci_read_config_dword(gpch->sb_pdev, gpio < 32 ? + gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, ®); + if (err) + goto unlock; + + reg |= 1 << (gpio & 0x1f); + + err = pci_write_config_dword(gpch->sb_pdev, gpio < 32 ? + gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, reg); + if (err) + goto unlock; + + rdc_gpio_set_value_impl(chip, gpio, value); + +unlock: + spin_unlock(&gpch->lock); + + return err; +} + +/* configure GPIO pin as input */ +static int rdc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +{ + return rdc_gpio_config(chip, gpio, 1); +} + +/* + * Cache the initial value of both GPIO data registers + */ +static int __devinit rdc321x_gpio_probe(struct platform_device *pdev) +{ + int err; + struct resource *r; + struct rdc321x_gpio *rdc321x_gpio_dev; + struct rdc321x_gpio_pdata *pdata; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data supplied\n"); + return -ENODEV; + } + + rdc321x_gpio_dev = kzalloc(sizeof(struct rdc321x_gpio), GFP_KERNEL); + if (!rdc321x_gpio_dev) { + dev_err(&pdev->dev, "failed to allocate private data\n"); + return -ENOMEM; + } + + r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg1"); + if (!r) { + dev_err(&pdev->dev, "failed to get gpio-reg1 resource\n"); + err = -ENODEV; + goto out_free; + } + + spin_lock_init(&rdc321x_gpio_dev->lock); + rdc321x_gpio_dev->sb_pdev = pdata->sb_pdev; + rdc321x_gpio_dev->reg1_ctrl_base = r->start; + rdc321x_gpio_dev->reg1_data_base = r->start + 0x4; + + r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg2"); + if (!r) { + dev_err(&pdev->dev, "failed to get gpio-reg2 resource\n"); + err = -ENODEV; + goto out_free; + } + + rdc321x_gpio_dev->reg2_ctrl_base = r->start; + rdc321x_gpio_dev->reg2_data_base = r->start + 0x4; + + rdc321x_gpio_dev->chip.label = "rdc321x-gpio"; + rdc321x_gpio_dev->chip.direction_input = rdc_gpio_direction_input; + rdc321x_gpio_dev->chip.direction_output = rdc_gpio_config; + rdc321x_gpio_dev->chip.get = rdc_gpio_get_value; + rdc321x_gpio_dev->chip.set = rdc_gpio_set_value; + rdc321x_gpio_dev->chip.base = 0; + rdc321x_gpio_dev->chip.ngpio = pdata->max_gpios; + + platform_set_drvdata(pdev, rdc321x_gpio_dev); + + /* This might not be, what others (BIOS, bootloader, etc.) + wrote to these registers before, but it's a good guess. Still + better than just using 0xffffffff. */ + err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev, + rdc321x_gpio_dev->reg1_data_base, + &rdc321x_gpio_dev->data_reg[0]); + if (err) + goto out_drvdata; + + err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev, + rdc321x_gpio_dev->reg2_data_base, + &rdc321x_gpio_dev->data_reg[1]); + if (err) + goto out_drvdata; + + dev_info(&pdev->dev, "registering %d GPIOs\n", + rdc321x_gpio_dev->chip.ngpio); + return gpiochip_add(&rdc321x_gpio_dev->chip); + +out_drvdata: + platform_set_drvdata(pdev, NULL); +out_free: + kfree(rdc321x_gpio_dev); + return err; +} + +static int __devexit rdc321x_gpio_remove(struct platform_device *pdev) +{ + int ret; + struct rdc321x_gpio *rdc321x_gpio_dev = platform_get_drvdata(pdev); + + ret = gpiochip_remove(&rdc321x_gpio_dev->chip); + if (ret) + dev_err(&pdev->dev, "failed to unregister chip\n"); + + kfree(rdc321x_gpio_dev); + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static struct platform_driver rdc321x_gpio_driver = { + .driver.name = "rdc321x-gpio", + .driver.owner = THIS_MODULE, + .probe = rdc321x_gpio_probe, + .remove = __devexit_p(rdc321x_gpio_remove), +}; + +static int __init rdc321x_gpio_init(void) +{ + return platform_driver_register(&rdc321x_gpio_driver); +} + +static void __exit rdc321x_gpio_exit(void) +{ + platform_driver_unregister(&rdc321x_gpio_driver); +} + +module_init(rdc321x_gpio_init); +module_exit(rdc321x_gpio_exit); + +MODULE_AUTHOR("Florian Fainelli "); +MODULE_DESCRIPTION("RDC321x GPIO driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rdc321x-gpio"); 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 * - * 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/gpio-sch.c b/drivers/gpio/gpio-sch.c new file mode 100644 index 00000000000..16351584549 --- /dev/null +++ b/drivers/gpio/gpio-sch.c @@ -0,0 +1,316 @@ +/* + * GPIO interface for Intel Poulsbo SCH + * + * Copyright (c) 2010 CompuLab Ltd + * Author: Denis Turischev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static DEFINE_SPINLOCK(gpio_lock); + +#define CGEN (0x00) +#define CGIO (0x04) +#define CGLV (0x08) + +#define RGEN (0x20) +#define RGIO (0x24) +#define RGLV (0x28) + +static unsigned short gpio_ba; + +static int sch_gpio_core_direction_in(struct gpio_chip *gc, unsigned gpio_num) +{ + u8 curr_dirs; + unsigned short offset, bit; + + spin_lock(&gpio_lock); + + offset = CGIO + gpio_num / 8; + bit = gpio_num % 8; + + curr_dirs = inb(gpio_ba + offset); + + if (!(curr_dirs & (1 << bit))) + outb(curr_dirs | (1 << bit), gpio_ba + offset); + + spin_unlock(&gpio_lock); + return 0; +} + +static int sch_gpio_core_get(struct gpio_chip *gc, unsigned gpio_num) +{ + int res; + unsigned short offset, bit; + + offset = CGLV + gpio_num / 8; + bit = gpio_num % 8; + + res = !!(inb(gpio_ba + offset) & (1 << bit)); + return res; +} + +static void sch_gpio_core_set(struct gpio_chip *gc, unsigned gpio_num, int val) +{ + u8 curr_vals; + unsigned short offset, bit; + + spin_lock(&gpio_lock); + + offset = CGLV + gpio_num / 8; + bit = gpio_num % 8; + + curr_vals = inb(gpio_ba + offset); + + if (val) + outb(curr_vals | (1 << bit), gpio_ba + offset); + else + outb((curr_vals & ~(1 << bit)), gpio_ba + offset); + spin_unlock(&gpio_lock); +} + +static int sch_gpio_core_direction_out(struct gpio_chip *gc, + unsigned gpio_num, int val) +{ + u8 curr_dirs; + unsigned short offset, bit; + + sch_gpio_core_set(gc, gpio_num, val); + + spin_lock(&gpio_lock); + + offset = CGIO + gpio_num / 8; + bit = gpio_num % 8; + + curr_dirs = inb(gpio_ba + offset); + if (curr_dirs & (1 << bit)) + outb(curr_dirs & ~(1 << bit), gpio_ba + offset); + + spin_unlock(&gpio_lock); + return 0; +} + +static struct gpio_chip sch_gpio_core = { + .label = "sch_gpio_core", + .owner = THIS_MODULE, + .direction_input = sch_gpio_core_direction_in, + .get = sch_gpio_core_get, + .direction_output = sch_gpio_core_direction_out, + .set = sch_gpio_core_set, +}; + +static int sch_gpio_resume_direction_in(struct gpio_chip *gc, + unsigned gpio_num) +{ + u8 curr_dirs; + + spin_lock(&gpio_lock); + + curr_dirs = inb(gpio_ba + RGIO); + + if (!(curr_dirs & (1 << gpio_num))) + outb(curr_dirs | (1 << gpio_num) , gpio_ba + RGIO); + + spin_unlock(&gpio_lock); + return 0; +} + +static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num) +{ + return !!(inb(gpio_ba + RGLV) & (1 << gpio_num)); +} + +static void sch_gpio_resume_set(struct gpio_chip *gc, + unsigned gpio_num, int val) +{ + u8 curr_vals; + + spin_lock(&gpio_lock); + + curr_vals = inb(gpio_ba + RGLV); + + if (val) + outb(curr_vals | (1 << gpio_num), gpio_ba + RGLV); + else + outb((curr_vals & ~(1 << gpio_num)), gpio_ba + RGLV); + + spin_unlock(&gpio_lock); +} + +static int sch_gpio_resume_direction_out(struct gpio_chip *gc, + unsigned gpio_num, int val) +{ + u8 curr_dirs; + + sch_gpio_resume_set(gc, gpio_num, val); + + spin_lock(&gpio_lock); + + curr_dirs = inb(gpio_ba + RGIO); + if (curr_dirs & (1 << gpio_num)) + outb(curr_dirs & ~(1 << gpio_num), gpio_ba + RGIO); + + spin_unlock(&gpio_lock); + return 0; +} + +static struct gpio_chip sch_gpio_resume = { + .label = "sch_gpio_resume", + .owner = THIS_MODULE, + .direction_input = sch_gpio_resume_direction_in, + .get = sch_gpio_resume_get, + .direction_output = sch_gpio_resume_direction_out, + .set = sch_gpio_resume_set, +}; + +static int __devinit sch_gpio_probe(struct platform_device *pdev) +{ + struct resource *res; + int err, id; + + id = pdev->id; + if (!id) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) + return -EBUSY; + + if (!request_region(res->start, resource_size(res), pdev->name)) + return -EBUSY; + + gpio_ba = res->start; + + switch (id) { + case PCI_DEVICE_ID_INTEL_SCH_LPC: + sch_gpio_core.base = 0; + sch_gpio_core.ngpio = 10; + + sch_gpio_resume.base = 10; + sch_gpio_resume.ngpio = 4; + + /* + * GPIO[6:0] enabled by default + * GPIO7 is configured by the CMC as SLPIOVR + * Enable GPIO[9:8] core powered gpios explicitly + */ + outb(0x3, gpio_ba + CGEN + 1); + /* + * SUS_GPIO[2:0] enabled by default + * Enable SUS_GPIO3 resume powered gpio explicitly + */ + outb(0x8, gpio_ba + RGEN); + break; + + case PCI_DEVICE_ID_INTEL_ITC_LPC: + sch_gpio_core.base = 0; + sch_gpio_core.ngpio = 5; + + sch_gpio_resume.base = 5; + sch_gpio_resume.ngpio = 9; + break; + + default: + return -ENODEV; + } + + sch_gpio_core.dev = &pdev->dev; + sch_gpio_resume.dev = &pdev->dev; + + err = gpiochip_add(&sch_gpio_core); + if (err < 0) + goto err_sch_gpio_core; + + err = gpiochip_add(&sch_gpio_resume); + if (err < 0) + goto err_sch_gpio_resume; + + return 0; + +err_sch_gpio_resume: + err = gpiochip_remove(&sch_gpio_core); + if (err) + dev_err(&pdev->dev, "%s failed, %d\n", + "gpiochip_remove()", err); + +err_sch_gpio_core: + release_region(res->start, resource_size(res)); + gpio_ba = 0; + + return err; +} + +static int __devexit sch_gpio_remove(struct platform_device *pdev) +{ + struct resource *res; + if (gpio_ba) { + int err; + + err = gpiochip_remove(&sch_gpio_core); + if (err) + dev_err(&pdev->dev, "%s failed, %d\n", + "gpiochip_remove()", err); + err = gpiochip_remove(&sch_gpio_resume); + if (err) + dev_err(&pdev->dev, "%s failed, %d\n", + "gpiochip_remove()", err); + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + + release_region(res->start, resource_size(res)); + gpio_ba = 0; + + return err; + } + + return 0; +} + +static struct platform_driver sch_gpio_driver = { + .driver = { + .name = "sch_gpio", + .owner = THIS_MODULE, + }, + .probe = sch_gpio_probe, + .remove = __devexit_p(sch_gpio_remove), +}; + +static int __init sch_gpio_init(void) +{ + return platform_driver_register(&sch_gpio_driver); +} + +static void __exit sch_gpio_exit(void) +{ + platform_driver_unregister(&sch_gpio_driver); +} + +module_init(sch_gpio_init); +module_exit(sch_gpio_exit); + +MODULE_AUTHOR("Denis Turischev "); +MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:sch_gpio"); diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c new file mode 100644 index 00000000000..4c980b57332 --- /dev/null +++ b/drivers/gpio/gpio-stmpe.c @@ -0,0 +1,404 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License, version 2 + * Author: Rabin Vincent for ST-Ericsson + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * These registers are modified under the irq bus lock and cached to avoid + * unnecessary writes in bus_sync_unlock. + */ +enum { REG_RE, REG_FE, REG_IE }; + +#define CACHE_NR_REGS 3 +#define CACHE_NR_BANKS (STMPE_NR_GPIOS / 8) + +struct stmpe_gpio { + struct gpio_chip chip; + struct stmpe *stmpe; + struct device *dev; + struct mutex irq_lock; + + int irq_base; + unsigned norequest_mask; + + /* Caches of interrupt control registers for bus_lock */ + u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS]; + u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS]; +}; + +static inline struct stmpe_gpio *to_stmpe_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct stmpe_gpio, chip); +} + +static int stmpe_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); + struct stmpe *stmpe = stmpe_gpio->stmpe; + u8 reg = stmpe->regs[STMPE_IDX_GPMR_LSB] - (offset / 8); + u8 mask = 1 << (offset % 8); + int ret; + + ret = stmpe_reg_read(stmpe, reg); + if (ret < 0) + return ret; + + return ret & mask; +} + +static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +{ + struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); + struct stmpe *stmpe = stmpe_gpio->stmpe; + int which = val ? STMPE_IDX_GPSR_LSB : STMPE_IDX_GPCR_LSB; + u8 reg = stmpe->regs[which] - (offset / 8); + u8 mask = 1 << (offset % 8); + + stmpe_reg_write(stmpe, reg, mask); +} + +static int stmpe_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int val) +{ + struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); + struct stmpe *stmpe = stmpe_gpio->stmpe; + u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8); + u8 mask = 1 << (offset % 8); + + stmpe_gpio_set(chip, offset, val); + + return stmpe_set_bits(stmpe, reg, mask, mask); +} + +static int stmpe_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); + struct stmpe *stmpe = stmpe_gpio->stmpe; + u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8); + u8 mask = 1 << (offset % 8); + + return stmpe_set_bits(stmpe, reg, mask, 0); +} + +static int stmpe_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); + + return stmpe_gpio->irq_base + offset; +} + +static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); + struct stmpe *stmpe = stmpe_gpio->stmpe; + + if (stmpe_gpio->norequest_mask & (1 << offset)) + return -EINVAL; + + return stmpe_set_altfunc(stmpe, 1 << offset, STMPE_BLOCK_GPIO); +} + +static struct gpio_chip template_chip = { + .label = "stmpe", + .owner = THIS_MODULE, + .direction_input = stmpe_gpio_direction_input, + .get = stmpe_gpio_get, + .direction_output = stmpe_gpio_direction_output, + .set = stmpe_gpio_set, + .to_irq = stmpe_gpio_to_irq, + .request = stmpe_gpio_request, + .can_sleep = 1, +}; + +static int stmpe_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d); + int offset = d->irq - stmpe_gpio->irq_base; + int regoffset = offset / 8; + int mask = 1 << (offset % 8); + + if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH) + return -EINVAL; + + if (type == IRQ_TYPE_EDGE_RISING) + stmpe_gpio->regs[REG_RE][regoffset] |= mask; + else + stmpe_gpio->regs[REG_RE][regoffset] &= ~mask; + + if (type == IRQ_TYPE_EDGE_FALLING) + stmpe_gpio->regs[REG_FE][regoffset] |= mask; + else + stmpe_gpio->regs[REG_FE][regoffset] &= ~mask; + + return 0; +} + +static void stmpe_gpio_irq_lock(struct irq_data *d) +{ + struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d); + + mutex_lock(&stmpe_gpio->irq_lock); +} + +static void stmpe_gpio_irq_sync_unlock(struct irq_data *d) +{ + struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d); + struct stmpe *stmpe = stmpe_gpio->stmpe; + int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8); + static const u8 regmap[] = { + [REG_RE] = STMPE_IDX_GPRER_LSB, + [REG_FE] = STMPE_IDX_GPFER_LSB, + [REG_IE] = STMPE_IDX_IEGPIOR_LSB, + }; + int i, j; + + for (i = 0; i < CACHE_NR_REGS; i++) { + for (j = 0; j < num_banks; j++) { + u8 old = stmpe_gpio->oldregs[i][j]; + u8 new = stmpe_gpio->regs[i][j]; + + if (new == old) + continue; + + stmpe_gpio->oldregs[i][j] = new; + stmpe_reg_write(stmpe, stmpe->regs[regmap[i]] - j, new); + } + } + + mutex_unlock(&stmpe_gpio->irq_lock); +} + +static void stmpe_gpio_irq_mask(struct irq_data *d) +{ + struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d); + int offset = d->irq - stmpe_gpio->irq_base; + int regoffset = offset / 8; + int mask = 1 << (offset % 8); + + stmpe_gpio->regs[REG_IE][regoffset] &= ~mask; +} + +static void stmpe_gpio_irq_unmask(struct irq_data *d) +{ + struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d); + int offset = d->irq - stmpe_gpio->irq_base; + int regoffset = offset / 8; + int mask = 1 << (offset % 8); + + stmpe_gpio->regs[REG_IE][regoffset] |= mask; +} + +static struct irq_chip stmpe_gpio_irq_chip = { + .name = "stmpe-gpio", + .irq_bus_lock = stmpe_gpio_irq_lock, + .irq_bus_sync_unlock = stmpe_gpio_irq_sync_unlock, + .irq_mask = stmpe_gpio_irq_mask, + .irq_unmask = stmpe_gpio_irq_unmask, + .irq_set_type = stmpe_gpio_irq_set_type, +}; + +static irqreturn_t stmpe_gpio_irq(int irq, void *dev) +{ + struct stmpe_gpio *stmpe_gpio = dev; + struct stmpe *stmpe = stmpe_gpio->stmpe; + u8 statmsbreg = stmpe->regs[STMPE_IDX_ISGPIOR_MSB]; + int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8); + u8 status[num_banks]; + int ret; + int i; + + ret = stmpe_block_read(stmpe, statmsbreg, num_banks, status); + if (ret < 0) + return IRQ_NONE; + + for (i = 0; i < num_banks; i++) { + int bank = num_banks - i - 1; + unsigned int enabled = stmpe_gpio->regs[REG_IE][bank]; + unsigned int stat = status[i]; + + stat &= enabled; + if (!stat) + continue; + + while (stat) { + int bit = __ffs(stat); + int line = bank * 8 + bit; + + handle_nested_irq(stmpe_gpio->irq_base + line); + stat &= ~(1 << bit); + } + + stmpe_reg_write(stmpe, statmsbreg + i, status[i]); + stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_GPEDR_MSB] + i, + status[i]); + } + + return IRQ_HANDLED; +} + +static int __devinit stmpe_gpio_irq_init(struct stmpe_gpio *stmpe_gpio) +{ + int base = stmpe_gpio->irq_base; + int irq; + + for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) { + irq_set_chip_data(irq, stmpe_gpio); + irq_set_chip_and_handler(irq, &stmpe_gpio_irq_chip, + handle_simple_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + } + + return 0; +} + +static void stmpe_gpio_irq_remove(struct stmpe_gpio *stmpe_gpio) +{ + int base = stmpe_gpio->irq_base; + int irq; + + for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) { +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); + } +} + +static int __devinit stmpe_gpio_probe(struct platform_device *pdev) +{ + struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); + struct stmpe_gpio_platform_data *pdata; + struct stmpe_gpio *stmpe_gpio; + int ret; + int irq; + + pdata = stmpe->pdata->gpio; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + stmpe_gpio = kzalloc(sizeof(struct stmpe_gpio), GFP_KERNEL); + if (!stmpe_gpio) + return -ENOMEM; + + mutex_init(&stmpe_gpio->irq_lock); + + stmpe_gpio->dev = &pdev->dev; + stmpe_gpio->stmpe = stmpe; + stmpe_gpio->norequest_mask = pdata ? pdata->norequest_mask : 0; + + stmpe_gpio->chip = template_chip; + stmpe_gpio->chip.ngpio = stmpe->num_gpios; + stmpe_gpio->chip.dev = &pdev->dev; + stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1; + + stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0); + + ret = stmpe_enable(stmpe, STMPE_BLOCK_GPIO); + if (ret) + goto out_free; + + ret = stmpe_gpio_irq_init(stmpe_gpio); + if (ret) + goto out_disable; + + ret = request_threaded_irq(irq, NULL, stmpe_gpio_irq, IRQF_ONESHOT, + "stmpe-gpio", stmpe_gpio); + if (ret) { + dev_err(&pdev->dev, "unable to get irq: %d\n", ret); + goto out_removeirq; + } + + ret = gpiochip_add(&stmpe_gpio->chip); + if (ret) { + dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret); + goto out_freeirq; + } + + if (pdata && pdata->setup) + pdata->setup(stmpe, stmpe_gpio->chip.base); + + platform_set_drvdata(pdev, stmpe_gpio); + + return 0; + +out_freeirq: + free_irq(irq, stmpe_gpio); +out_removeirq: + stmpe_gpio_irq_remove(stmpe_gpio); +out_disable: + stmpe_disable(stmpe, STMPE_BLOCK_GPIO); +out_free: + kfree(stmpe_gpio); + return ret; +} + +static int __devexit stmpe_gpio_remove(struct platform_device *pdev) +{ + struct stmpe_gpio *stmpe_gpio = platform_get_drvdata(pdev); + struct stmpe *stmpe = stmpe_gpio->stmpe; + struct stmpe_gpio_platform_data *pdata = stmpe->pdata->gpio; + int irq = platform_get_irq(pdev, 0); + int ret; + + if (pdata && pdata->remove) + pdata->remove(stmpe, stmpe_gpio->chip.base); + + ret = gpiochip_remove(&stmpe_gpio->chip); + if (ret < 0) { + dev_err(stmpe_gpio->dev, + "unable to remove gpiochip: %d\n", ret); + return ret; + } + + stmpe_disable(stmpe, STMPE_BLOCK_GPIO); + + free_irq(irq, stmpe_gpio); + stmpe_gpio_irq_remove(stmpe_gpio); + platform_set_drvdata(pdev, NULL); + kfree(stmpe_gpio); + + return 0; +} + +static struct platform_driver stmpe_gpio_driver = { + .driver.name = "stmpe-gpio", + .driver.owner = THIS_MODULE, + .probe = stmpe_gpio_probe, + .remove = __devexit_p(stmpe_gpio_remove), +}; + +static int __init stmpe_gpio_init(void) +{ + return platform_driver_register(&stmpe_gpio_driver); +} +subsys_initcall(stmpe_gpio_init); + +static void __exit stmpe_gpio_exit(void) +{ + platform_driver_unregister(&stmpe_gpio_driver); +} +module_exit(stmpe_gpio_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("STMPExxxx GPIO driver"); +MODULE_AUTHOR("Rabin Vincent "); diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c new file mode 100644 index 00000000000..a4f73534394 --- /dev/null +++ b/drivers/gpio/gpio-sx150x.c @@ -0,0 +1,680 @@ +/* Copyright (c) 2010, Code Aurora Forum. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NO_UPDATE_PENDING -1 + +struct sx150x_device_data { + u8 reg_pullup; + u8 reg_pulldn; + u8 reg_drain; + u8 reg_polarity; + u8 reg_dir; + u8 reg_data; + u8 reg_irq_mask; + u8 reg_irq_src; + u8 reg_sense; + u8 reg_clock; + u8 reg_misc; + u8 reg_reset; + u8 ngpios; +}; + +struct sx150x_chip { + struct gpio_chip gpio_chip; + struct i2c_client *client; + const struct sx150x_device_data *dev_cfg; + int irq_summary; + int irq_base; + int irq_update; + u32 irq_sense; + u32 irq_masked; + u32 dev_sense; + u32 dev_masked; + struct irq_chip irq_chip; + struct mutex lock; +}; + +static const struct sx150x_device_data sx150x_devices[] = { + [0] = { /* sx1508q */ + .reg_pullup = 0x03, + .reg_pulldn = 0x04, + .reg_drain = 0x05, + .reg_polarity = 0x06, + .reg_dir = 0x07, + .reg_data = 0x08, + .reg_irq_mask = 0x09, + .reg_irq_src = 0x0c, + .reg_sense = 0x0b, + .reg_clock = 0x0f, + .reg_misc = 0x10, + .reg_reset = 0x7d, + .ngpios = 8 + }, + [1] = { /* sx1509q */ + .reg_pullup = 0x07, + .reg_pulldn = 0x09, + .reg_drain = 0x0b, + .reg_polarity = 0x0d, + .reg_dir = 0x0f, + .reg_data = 0x11, + .reg_irq_mask = 0x13, + .reg_irq_src = 0x19, + .reg_sense = 0x17, + .reg_clock = 0x1e, + .reg_misc = 0x1f, + .reg_reset = 0x7d, + .ngpios = 16 + }, +}; + +static const struct i2c_device_id sx150x_id[] = { + {"sx1508q", 0}, + {"sx1509q", 1}, + {} +}; +MODULE_DEVICE_TABLE(i2c, sx150x_id); + +static s32 sx150x_i2c_write(struct i2c_client *client, u8 reg, u8 val) +{ + s32 err = i2c_smbus_write_byte_data(client, reg, val); + + if (err < 0) + dev_warn(&client->dev, + "i2c write fail: can't write %02x to %02x: %d\n", + val, reg, err); + return err; +} + +static s32 sx150x_i2c_read(struct i2c_client *client, u8 reg, u8 *val) +{ + s32 err = i2c_smbus_read_byte_data(client, reg); + + if (err >= 0) + *val = err; + else + dev_warn(&client->dev, + "i2c read fail: can't read from %02x: %d\n", + reg, err); + return err; +} + +static inline bool offset_is_oscio(struct sx150x_chip *chip, unsigned offset) +{ + return (chip->dev_cfg->ngpios == offset); +} + +/* + * These utility functions solve the common problem of locating and setting + * configuration bits. Configuration bits are grouped into registers + * whose indexes increase downwards. For example, with eight-bit registers, + * sixteen gpios would have their config bits grouped in the following order: + * REGISTER N-1 [ f e d c b a 9 8 ] + * N [ 7 6 5 4 3 2 1 0 ] + * + * For multi-bit configurations, the pattern gets wider: + * REGISTER N-3 [ f f e e d d c c ] + * N-2 [ b b a a 9 9 8 8 ] + * N-1 [ 7 7 6 6 5 5 4 4 ] + * N [ 3 3 2 2 1 1 0 0 ] + * + * Given the address of the starting register 'N', the index of the gpio + * whose configuration we seek to change, and the width in bits of that + * configuration, these functions allow us to locate the correct + * register and mask the correct bits. + */ +static inline void sx150x_find_cfg(u8 offset, u8 width, + u8 *reg, u8 *mask, u8 *shift) +{ + *reg -= offset * width / 8; + *mask = (1 << width) - 1; + *shift = (offset * width) % 8; + *mask <<= *shift; +} + +static s32 sx150x_write_cfg(struct sx150x_chip *chip, + u8 offset, u8 width, u8 reg, u8 val) +{ + u8 mask; + u8 data; + u8 shift; + s32 err; + + sx150x_find_cfg(offset, width, ®, &mask, &shift); + err = sx150x_i2c_read(chip->client, reg, &data); + if (err < 0) + return err; + + data &= ~mask; + data |= (val << shift) & mask; + return sx150x_i2c_write(chip->client, reg, data); +} + +static int sx150x_get_io(struct sx150x_chip *chip, unsigned offset) +{ + u8 reg = chip->dev_cfg->reg_data; + u8 mask; + u8 data; + u8 shift; + s32 err; + + sx150x_find_cfg(offset, 1, ®, &mask, &shift); + err = sx150x_i2c_read(chip->client, reg, &data); + if (err >= 0) + err = (data & mask) != 0 ? 1 : 0; + + return err; +} + +static void sx150x_set_oscio(struct sx150x_chip *chip, int val) +{ + sx150x_i2c_write(chip->client, + chip->dev_cfg->reg_clock, + (val ? 0x1f : 0x10)); +} + +static void sx150x_set_io(struct sx150x_chip *chip, unsigned offset, int val) +{ + sx150x_write_cfg(chip, + offset, + 1, + chip->dev_cfg->reg_data, + (val ? 1 : 0)); +} + +static int sx150x_io_input(struct sx150x_chip *chip, unsigned offset) +{ + return sx150x_write_cfg(chip, + offset, + 1, + chip->dev_cfg->reg_dir, + 1); +} + +static int sx150x_io_output(struct sx150x_chip *chip, unsigned offset, int val) +{ + int err; + + err = sx150x_write_cfg(chip, + offset, + 1, + chip->dev_cfg->reg_data, + (val ? 1 : 0)); + if (err >= 0) + err = sx150x_write_cfg(chip, + offset, + 1, + chip->dev_cfg->reg_dir, + 0); + return err; +} + +static int sx150x_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct sx150x_chip *chip; + int status = -EINVAL; + + chip = container_of(gc, struct sx150x_chip, gpio_chip); + + if (!offset_is_oscio(chip, offset)) { + mutex_lock(&chip->lock); + status = sx150x_get_io(chip, offset); + mutex_unlock(&chip->lock); + } + + return status; +} + +static void sx150x_gpio_set(struct gpio_chip *gc, unsigned offset, int val) +{ + struct sx150x_chip *chip; + + chip = container_of(gc, struct sx150x_chip, gpio_chip); + + mutex_lock(&chip->lock); + if (offset_is_oscio(chip, offset)) + sx150x_set_oscio(chip, val); + else + sx150x_set_io(chip, offset, val); + mutex_unlock(&chip->lock); +} + +static int sx150x_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + struct sx150x_chip *chip; + int status = -EINVAL; + + chip = container_of(gc, struct sx150x_chip, gpio_chip); + + if (!offset_is_oscio(chip, offset)) { + mutex_lock(&chip->lock); + status = sx150x_io_input(chip, offset); + mutex_unlock(&chip->lock); + } + return status; +} + +static int sx150x_gpio_direction_output(struct gpio_chip *gc, + unsigned offset, + int val) +{ + struct sx150x_chip *chip; + int status = 0; + + chip = container_of(gc, struct sx150x_chip, gpio_chip); + + if (!offset_is_oscio(chip, offset)) { + mutex_lock(&chip->lock); + status = sx150x_io_output(chip, offset, val); + mutex_unlock(&chip->lock); + } + return status; +} + +static int sx150x_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct sx150x_chip *chip; + + chip = container_of(gc, struct sx150x_chip, gpio_chip); + + if (offset >= chip->dev_cfg->ngpios) + return -EINVAL; + + if (chip->irq_base < 0) + return -EINVAL; + + return chip->irq_base + offset; +} + +static void sx150x_irq_mask(struct irq_data *d) +{ + struct irq_chip *ic = irq_data_get_irq_chip(d); + struct sx150x_chip *chip; + unsigned n; + + chip = container_of(ic, struct sx150x_chip, irq_chip); + n = d->irq - chip->irq_base; + chip->irq_masked |= (1 << n); + chip->irq_update = n; +} + +static void sx150x_irq_unmask(struct irq_data *d) +{ + struct irq_chip *ic = irq_data_get_irq_chip(d); + struct sx150x_chip *chip; + unsigned n; + + chip = container_of(ic, struct sx150x_chip, irq_chip); + n = d->irq - chip->irq_base; + + chip->irq_masked &= ~(1 << n); + chip->irq_update = n; +} + +static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type) +{ + struct irq_chip *ic = irq_data_get_irq_chip(d); + struct sx150x_chip *chip; + unsigned n, val = 0; + + if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) + return -EINVAL; + + chip = container_of(ic, struct sx150x_chip, irq_chip); + n = d->irq - chip->irq_base; + + if (flow_type & IRQ_TYPE_EDGE_RISING) + val |= 0x1; + if (flow_type & IRQ_TYPE_EDGE_FALLING) + val |= 0x2; + + chip->irq_sense &= ~(3UL << (n * 2)); + chip->irq_sense |= val << (n * 2); + chip->irq_update = n; + return 0; +} + +static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id) +{ + struct sx150x_chip *chip = (struct sx150x_chip *)dev_id; + unsigned nhandled = 0; + unsigned sub_irq; + unsigned n; + s32 err; + u8 val; + int i; + + for (i = (chip->dev_cfg->ngpios / 8) - 1; i >= 0; --i) { + err = sx150x_i2c_read(chip->client, + chip->dev_cfg->reg_irq_src - i, + &val); + if (err < 0) + continue; + + sx150x_i2c_write(chip->client, + chip->dev_cfg->reg_irq_src - i, + val); + for (n = 0; n < 8; ++n) { + if (val & (1 << n)) { + sub_irq = chip->irq_base + (i * 8) + n; + handle_nested_irq(sub_irq); + ++nhandled; + } + } + } + + return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); +} + +static void sx150x_irq_bus_lock(struct irq_data *d) +{ + struct irq_chip *ic = irq_data_get_irq_chip(d); + struct sx150x_chip *chip; + + chip = container_of(ic, struct sx150x_chip, irq_chip); + + mutex_lock(&chip->lock); +} + +static void sx150x_irq_bus_sync_unlock(struct irq_data *d) +{ + struct irq_chip *ic = irq_data_get_irq_chip(d); + struct sx150x_chip *chip; + unsigned n; + + chip = container_of(ic, struct sx150x_chip, irq_chip); + + if (chip->irq_update == NO_UPDATE_PENDING) + goto out; + + n = chip->irq_update; + chip->irq_update = NO_UPDATE_PENDING; + + /* Avoid updates if nothing changed */ + if (chip->dev_sense == chip->irq_sense && + chip->dev_sense == chip->irq_masked) + goto out; + + chip->dev_sense = chip->irq_sense; + chip->dev_masked = chip->irq_masked; + + if (chip->irq_masked & (1 << n)) { + sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 1); + sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, 0); + } else { + sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 0); + sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, + chip->irq_sense >> (n * 2)); + } +out: + mutex_unlock(&chip->lock); +} + +static void sx150x_init_chip(struct sx150x_chip *chip, + struct i2c_client *client, + kernel_ulong_t driver_data, + struct sx150x_platform_data *pdata) +{ + mutex_init(&chip->lock); + + chip->client = client; + chip->dev_cfg = &sx150x_devices[driver_data]; + chip->gpio_chip.label = client->name; + chip->gpio_chip.direction_input = sx150x_gpio_direction_input; + chip->gpio_chip.direction_output = sx150x_gpio_direction_output; + chip->gpio_chip.get = sx150x_gpio_get; + chip->gpio_chip.set = sx150x_gpio_set; + chip->gpio_chip.to_irq = sx150x_gpio_to_irq; + chip->gpio_chip.base = pdata->gpio_base; + chip->gpio_chip.can_sleep = 1; + chip->gpio_chip.ngpio = chip->dev_cfg->ngpios; + if (pdata->oscio_is_gpo) + ++chip->gpio_chip.ngpio; + + chip->irq_chip.name = client->name; + chip->irq_chip.irq_mask = sx150x_irq_mask; + chip->irq_chip.irq_unmask = sx150x_irq_unmask; + chip->irq_chip.irq_set_type = sx150x_irq_set_type; + chip->irq_chip.irq_bus_lock = sx150x_irq_bus_lock; + chip->irq_chip.irq_bus_sync_unlock = sx150x_irq_bus_sync_unlock; + chip->irq_summary = -1; + chip->irq_base = -1; + chip->irq_masked = ~0; + chip->irq_sense = 0; + chip->dev_masked = ~0; + chip->dev_sense = 0; + chip->irq_update = NO_UPDATE_PENDING; +} + +static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg) +{ + int err = 0; + unsigned n; + + for (n = 0; err >= 0 && n < (chip->dev_cfg->ngpios / 8); ++n) + err = sx150x_i2c_write(chip->client, base - n, cfg >> (n * 8)); + return err; +} + +static int sx150x_reset(struct sx150x_chip *chip) +{ + int err; + + err = i2c_smbus_write_byte_data(chip->client, + chip->dev_cfg->reg_reset, + 0x12); + if (err < 0) + return err; + + err = i2c_smbus_write_byte_data(chip->client, + chip->dev_cfg->reg_reset, + 0x34); + return err; +} + +static int sx150x_init_hw(struct sx150x_chip *chip, + struct sx150x_platform_data *pdata) +{ + int err = 0; + + if (pdata->reset_during_probe) { + err = sx150x_reset(chip); + if (err < 0) + return err; + } + + err = sx150x_i2c_write(chip->client, + chip->dev_cfg->reg_misc, + 0x01); + if (err < 0) + return err; + + err = sx150x_init_io(chip, chip->dev_cfg->reg_pullup, + pdata->io_pullup_ena); + if (err < 0) + return err; + + err = sx150x_init_io(chip, chip->dev_cfg->reg_pulldn, + pdata->io_pulldn_ena); + if (err < 0) + return err; + + err = sx150x_init_io(chip, chip->dev_cfg->reg_drain, + pdata->io_open_drain_ena); + if (err < 0) + return err; + + err = sx150x_init_io(chip, chip->dev_cfg->reg_polarity, + pdata->io_polarity); + if (err < 0) + return err; + + if (pdata->oscio_is_gpo) + sx150x_set_oscio(chip, 0); + + return err; +} + +static int sx150x_install_irq_chip(struct sx150x_chip *chip, + int irq_summary, + int irq_base) +{ + int err; + unsigned n; + unsigned irq; + + chip->irq_summary = irq_summary; + chip->irq_base = irq_base; + + for (n = 0; n < chip->dev_cfg->ngpios; ++n) { + irq = irq_base + n; + irq_set_chip_and_handler(irq, &chip->irq_chip, handle_edge_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + } + + err = request_threaded_irq(irq_summary, + NULL, + sx150x_irq_thread_fn, + IRQF_SHARED | IRQF_TRIGGER_FALLING, + chip->irq_chip.name, + chip); + if (err < 0) { + chip->irq_summary = -1; + chip->irq_base = -1; + } + + return err; +} + +static void sx150x_remove_irq_chip(struct sx150x_chip *chip) +{ + unsigned n; + unsigned irq; + + free_irq(chip->irq_summary, chip); + + for (n = 0; n < chip->dev_cfg->ngpios; ++n) { + irq = chip->irq_base + n; + irq_set_chip_and_handler(irq, NULL, NULL); + } +} + +static int __devinit sx150x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + static const u32 i2c_funcs = I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_WORD_DATA; + struct sx150x_platform_data *pdata; + struct sx150x_chip *chip; + int rc; + + pdata = client->dev.platform_data; + if (!pdata) + return -EINVAL; + + if (!i2c_check_functionality(client->adapter, i2c_funcs)) + return -ENOSYS; + + chip = kzalloc(sizeof(struct sx150x_chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + sx150x_init_chip(chip, client, id->driver_data, pdata); + rc = sx150x_init_hw(chip, pdata); + if (rc < 0) + goto probe_fail_pre_gpiochip_add; + + rc = gpiochip_add(&chip->gpio_chip); + if (rc < 0) + goto probe_fail_pre_gpiochip_add; + + if (pdata->irq_summary >= 0) { + rc = sx150x_install_irq_chip(chip, + pdata->irq_summary, + pdata->irq_base); + if (rc < 0) + goto probe_fail_post_gpiochip_add; + } + + i2c_set_clientdata(client, chip); + + return 0; +probe_fail_post_gpiochip_add: + WARN_ON(gpiochip_remove(&chip->gpio_chip) < 0); +probe_fail_pre_gpiochip_add: + kfree(chip); + return rc; +} + +static int __devexit sx150x_remove(struct i2c_client *client) +{ + struct sx150x_chip *chip; + int rc; + + chip = i2c_get_clientdata(client); + rc = gpiochip_remove(&chip->gpio_chip); + if (rc < 0) + return rc; + + if (chip->irq_summary >= 0) + sx150x_remove_irq_chip(chip); + + kfree(chip); + + return 0; +} + +static struct i2c_driver sx150x_driver = { + .driver = { + .name = "sx150x", + .owner = THIS_MODULE + }, + .probe = sx150x_probe, + .remove = __devexit_p(sx150x_remove), + .id_table = sx150x_id, +}; + +static int __init sx150x_init(void) +{ + return i2c_add_driver(&sx150x_driver); +} +subsys_initcall(sx150x_init); + +static void __exit sx150x_exit(void) +{ + return i2c_del_driver(&sx150x_driver); +} +module_exit(sx150x_exit); + +MODULE_AUTHOR("Gregory Bean "); +MODULE_DESCRIPTION("Driver for Semtech SX150X I2C GPIO Expanders"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("i2c:sx150x"); diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c new file mode 100644 index 00000000000..2a82e8999a4 --- /dev/null +++ b/drivers/gpio/gpio-tc3589x.c @@ -0,0 +1,389 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License, version 2 + * Author: Hanumath Prasad for ST-Ericsson + * Author: Rabin Vincent for ST-Ericsson + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * These registers are modified under the irq bus lock and cached to avoid + * unnecessary writes in bus_sync_unlock. + */ +enum { REG_IBE, REG_IEV, REG_IS, REG_IE }; + +#define CACHE_NR_REGS 4 +#define CACHE_NR_BANKS 3 + +struct tc3589x_gpio { + struct gpio_chip chip; + struct tc3589x *tc3589x; + struct device *dev; + struct mutex irq_lock; + + int irq_base; + + /* Caches of interrupt control registers for bus_lock */ + u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS]; + u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS]; +}; + +static inline struct tc3589x_gpio *to_tc3589x_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct tc3589x_gpio, chip); +} + +static int tc3589x_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip); + struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; + u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2; + u8 mask = 1 << (offset % 8); + int ret; + + ret = tc3589x_reg_read(tc3589x, reg); + if (ret < 0) + return ret; + + return ret & mask; +} + +static void tc3589x_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +{ + struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip); + struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; + u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2; + unsigned pos = offset % 8; + u8 data[] = {!!val << pos, 1 << pos}; + + tc3589x_block_write(tc3589x, reg, ARRAY_SIZE(data), data); +} + +static int tc3589x_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int val) +{ + struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip); + struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; + u8 reg = TC3589x_GPIODIR0 + offset / 8; + unsigned pos = offset % 8; + + tc3589x_gpio_set(chip, offset, val); + + return tc3589x_set_bits(tc3589x, reg, 1 << pos, 1 << pos); +} + +static int tc3589x_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip); + struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; + u8 reg = TC3589x_GPIODIR0 + offset / 8; + unsigned pos = offset % 8; + + return tc3589x_set_bits(tc3589x, reg, 1 << pos, 0); +} + +static int tc3589x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip); + + return tc3589x_gpio->irq_base + offset; +} + +static struct gpio_chip template_chip = { + .label = "tc3589x", + .owner = THIS_MODULE, + .direction_input = tc3589x_gpio_direction_input, + .get = tc3589x_gpio_get, + .direction_output = tc3589x_gpio_direction_output, + .set = tc3589x_gpio_set, + .to_irq = tc3589x_gpio_to_irq, + .can_sleep = 1, +}; + +static int tc3589x_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d); + int offset = d->irq - tc3589x_gpio->irq_base; + int regoffset = offset / 8; + int mask = 1 << (offset % 8); + + if (type == IRQ_TYPE_EDGE_BOTH) { + tc3589x_gpio->regs[REG_IBE][regoffset] |= mask; + return 0; + } + + tc3589x_gpio->regs[REG_IBE][regoffset] &= ~mask; + + if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH) + tc3589x_gpio->regs[REG_IS][regoffset] |= mask; + else + tc3589x_gpio->regs[REG_IS][regoffset] &= ~mask; + + if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH) + tc3589x_gpio->regs[REG_IEV][regoffset] |= mask; + else + tc3589x_gpio->regs[REG_IEV][regoffset] &= ~mask; + + return 0; +} + +static void tc3589x_gpio_irq_lock(struct irq_data *d) +{ + struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d); + + mutex_lock(&tc3589x_gpio->irq_lock); +} + +static void tc3589x_gpio_irq_sync_unlock(struct irq_data *d) +{ + struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d); + struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; + static const u8 regmap[] = { + [REG_IBE] = TC3589x_GPIOIBE0, + [REG_IEV] = TC3589x_GPIOIEV0, + [REG_IS] = TC3589x_GPIOIS0, + [REG_IE] = TC3589x_GPIOIE0, + }; + int i, j; + + for (i = 0; i < CACHE_NR_REGS; i++) { + for (j = 0; j < CACHE_NR_BANKS; j++) { + u8 old = tc3589x_gpio->oldregs[i][j]; + u8 new = tc3589x_gpio->regs[i][j]; + + if (new == old) + continue; + + tc3589x_gpio->oldregs[i][j] = new; + tc3589x_reg_write(tc3589x, regmap[i] + j * 8, new); + } + } + + mutex_unlock(&tc3589x_gpio->irq_lock); +} + +static void tc3589x_gpio_irq_mask(struct irq_data *d) +{ + struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d); + int offset = d->irq - tc3589x_gpio->irq_base; + int regoffset = offset / 8; + int mask = 1 << (offset % 8); + + tc3589x_gpio->regs[REG_IE][regoffset] &= ~mask; +} + +static void tc3589x_gpio_irq_unmask(struct irq_data *d) +{ + struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d); + int offset = d->irq - tc3589x_gpio->irq_base; + int regoffset = offset / 8; + int mask = 1 << (offset % 8); + + tc3589x_gpio->regs[REG_IE][regoffset] |= mask; +} + +static struct irq_chip tc3589x_gpio_irq_chip = { + .name = "tc3589x-gpio", + .irq_bus_lock = tc3589x_gpio_irq_lock, + .irq_bus_sync_unlock = tc3589x_gpio_irq_sync_unlock, + .irq_mask = tc3589x_gpio_irq_mask, + .irq_unmask = tc3589x_gpio_irq_unmask, + .irq_set_type = tc3589x_gpio_irq_set_type, +}; + +static irqreturn_t tc3589x_gpio_irq(int irq, void *dev) +{ + struct tc3589x_gpio *tc3589x_gpio = dev; + struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; + u8 status[CACHE_NR_BANKS]; + int ret; + int i; + + ret = tc3589x_block_read(tc3589x, TC3589x_GPIOMIS0, + ARRAY_SIZE(status), status); + if (ret < 0) + return IRQ_NONE; + + for (i = 0; i < ARRAY_SIZE(status); i++) { + unsigned int stat = status[i]; + if (!stat) + continue; + + while (stat) { + int bit = __ffs(stat); + int line = i * 8 + bit; + + handle_nested_irq(tc3589x_gpio->irq_base + line); + stat &= ~(1 << bit); + } + + tc3589x_reg_write(tc3589x, TC3589x_GPIOIC0 + i, status[i]); + } + + return IRQ_HANDLED; +} + +static int tc3589x_gpio_irq_init(struct tc3589x_gpio *tc3589x_gpio) +{ + int base = tc3589x_gpio->irq_base; + int irq; + + for (irq = base; irq < base + tc3589x_gpio->chip.ngpio; irq++) { + irq_set_chip_data(irq, tc3589x_gpio); + irq_set_chip_and_handler(irq, &tc3589x_gpio_irq_chip, + handle_simple_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + } + + return 0; +} + +static void tc3589x_gpio_irq_remove(struct tc3589x_gpio *tc3589x_gpio) +{ + int base = tc3589x_gpio->irq_base; + int irq; + + for (irq = base; irq < base + tc3589x_gpio->chip.ngpio; irq++) { +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); + } +} + +static int __devinit tc3589x_gpio_probe(struct platform_device *pdev) +{ + struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent); + struct tc3589x_gpio_platform_data *pdata; + struct tc3589x_gpio *tc3589x_gpio; + int ret; + int irq; + + pdata = tc3589x->pdata->gpio; + if (!pdata) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + tc3589x_gpio = kzalloc(sizeof(struct tc3589x_gpio), GFP_KERNEL); + if (!tc3589x_gpio) + return -ENOMEM; + + mutex_init(&tc3589x_gpio->irq_lock); + + tc3589x_gpio->dev = &pdev->dev; + tc3589x_gpio->tc3589x = tc3589x; + + tc3589x_gpio->chip = template_chip; + tc3589x_gpio->chip.ngpio = tc3589x->num_gpio; + tc3589x_gpio->chip.dev = &pdev->dev; + tc3589x_gpio->chip.base = pdata->gpio_base; + + tc3589x_gpio->irq_base = tc3589x->irq_base + TC3589x_INT_GPIO(0); + + /* Bring the GPIO module out of reset */ + ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, + TC3589x_RSTCTRL_GPIRST, 0); + if (ret < 0) + goto out_free; + + ret = tc3589x_gpio_irq_init(tc3589x_gpio); + if (ret) + goto out_free; + + ret = request_threaded_irq(irq, NULL, tc3589x_gpio_irq, IRQF_ONESHOT, + "tc3589x-gpio", tc3589x_gpio); + if (ret) { + dev_err(&pdev->dev, "unable to get irq: %d\n", ret); + goto out_removeirq; + } + + ret = gpiochip_add(&tc3589x_gpio->chip); + if (ret) { + dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret); + goto out_freeirq; + } + + if (pdata->setup) + pdata->setup(tc3589x, tc3589x_gpio->chip.base); + + platform_set_drvdata(pdev, tc3589x_gpio); + + return 0; + +out_freeirq: + free_irq(irq, tc3589x_gpio); +out_removeirq: + tc3589x_gpio_irq_remove(tc3589x_gpio); +out_free: + kfree(tc3589x_gpio); + return ret; +} + +static int __devexit tc3589x_gpio_remove(struct platform_device *pdev) +{ + struct tc3589x_gpio *tc3589x_gpio = platform_get_drvdata(pdev); + struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; + struct tc3589x_gpio_platform_data *pdata = tc3589x->pdata->gpio; + int irq = platform_get_irq(pdev, 0); + int ret; + + if (pdata->remove) + pdata->remove(tc3589x, tc3589x_gpio->chip.base); + + ret = gpiochip_remove(&tc3589x_gpio->chip); + if (ret < 0) { + dev_err(tc3589x_gpio->dev, + "unable to remove gpiochip: %d\n", ret); + return ret; + } + + free_irq(irq, tc3589x_gpio); + tc3589x_gpio_irq_remove(tc3589x_gpio); + + platform_set_drvdata(pdev, NULL); + kfree(tc3589x_gpio); + + return 0; +} + +static struct platform_driver tc3589x_gpio_driver = { + .driver.name = "tc3589x-gpio", + .driver.owner = THIS_MODULE, + .probe = tc3589x_gpio_probe, + .remove = __devexit_p(tc3589x_gpio_remove), +}; + +static int __init tc3589x_gpio_init(void) +{ + return platform_driver_register(&tc3589x_gpio_driver); +} +subsys_initcall(tc3589x_gpio_init); + +static void __exit tc3589x_gpio_exit(void) +{ + platform_driver_unregister(&tc3589x_gpio_driver); +} +module_exit(tc3589x_gpio_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TC3589x GPIO driver"); +MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent"); diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c new file mode 100644 index 00000000000..c593bd46bfb --- /dev/null +++ b/drivers/gpio/gpio-timberdale.c @@ -0,0 +1,379 @@ +/* + * Timberdale FPGA GPIO driver + * Copyright (c) 2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Timberdale FPGA GPIO + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "timb-gpio" + +#define TGPIOVAL 0x00 +#define TGPIODIR 0x04 +#define TGPIO_IER 0x08 +#define TGPIO_ISR 0x0c +#define TGPIO_IPR 0x10 +#define TGPIO_ICR 0x14 +#define TGPIO_FLR 0x18 +#define TGPIO_LVR 0x1c +#define TGPIO_VER 0x20 +#define TGPIO_BFLR 0x24 + +struct timbgpio { + void __iomem *membase; + spinlock_t lock; /* mutual exclusion */ + struct gpio_chip gpio; + int irq_base; + unsigned long last_ier; +}; + +static int timbgpio_update_bit(struct gpio_chip *gpio, unsigned index, + unsigned offset, bool enabled) +{ + struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); + u32 reg; + + spin_lock(&tgpio->lock); + reg = ioread32(tgpio->membase + offset); + + if (enabled) + reg |= (1 << index); + else + reg &= ~(1 << index); + + iowrite32(reg, tgpio->membase + offset); + spin_unlock(&tgpio->lock); + + return 0; +} + +static int timbgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) +{ + return timbgpio_update_bit(gpio, nr, TGPIODIR, true); +} + +static int timbgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) +{ + struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); + u32 value; + + value = ioread32(tgpio->membase + TGPIOVAL); + return (value & (1 << nr)) ? 1 : 0; +} + +static int timbgpio_gpio_direction_output(struct gpio_chip *gpio, + unsigned nr, int val) +{ + return timbgpio_update_bit(gpio, nr, TGPIODIR, false); +} + +static void timbgpio_gpio_set(struct gpio_chip *gpio, + unsigned nr, int val) +{ + timbgpio_update_bit(gpio, nr, TGPIOVAL, val != 0); +} + +static int timbgpio_to_irq(struct gpio_chip *gpio, unsigned offset) +{ + struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); + + if (tgpio->irq_base <= 0) + return -EINVAL; + + return tgpio->irq_base + offset; +} + +/* + * GPIO IRQ + */ +static void timbgpio_irq_disable(struct irq_data *d) +{ + struct timbgpio *tgpio = irq_data_get_irq_chip_data(d); + int offset = d->irq - tgpio->irq_base; + unsigned long flags; + + spin_lock_irqsave(&tgpio->lock, flags); + tgpio->last_ier &= ~(1 << offset); + iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); + spin_unlock_irqrestore(&tgpio->lock, flags); +} + +static void timbgpio_irq_enable(struct irq_data *d) +{ + struct timbgpio *tgpio = irq_data_get_irq_chip_data(d); + int offset = d->irq - tgpio->irq_base; + unsigned long flags; + + spin_lock_irqsave(&tgpio->lock, flags); + tgpio->last_ier |= 1 << offset; + iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); + spin_unlock_irqrestore(&tgpio->lock, flags); +} + +static int timbgpio_irq_type(struct irq_data *d, unsigned trigger) +{ + struct timbgpio *tgpio = irq_data_get_irq_chip_data(d); + int offset = d->irq - tgpio->irq_base; + unsigned long flags; + u32 lvr, flr, bflr = 0; + u32 ver; + int ret = 0; + + if (offset < 0 || offset > tgpio->gpio.ngpio) + return -EINVAL; + + ver = ioread32(tgpio->membase + TGPIO_VER); + + spin_lock_irqsave(&tgpio->lock, flags); + + lvr = ioread32(tgpio->membase + TGPIO_LVR); + flr = ioread32(tgpio->membase + TGPIO_FLR); + if (ver > 2) + bflr = ioread32(tgpio->membase + TGPIO_BFLR); + + if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { + bflr &= ~(1 << offset); + flr &= ~(1 << offset); + if (trigger & IRQ_TYPE_LEVEL_HIGH) + lvr |= 1 << offset; + else + lvr &= ~(1 << offset); + } + + if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { + if (ver < 3) { + ret = -EINVAL; + goto out; + } + else { + flr |= 1 << offset; + bflr |= 1 << offset; + } + } else { + bflr &= ~(1 << offset); + flr |= 1 << offset; + if (trigger & IRQ_TYPE_EDGE_FALLING) + lvr &= ~(1 << offset); + else + lvr |= 1 << offset; + } + + iowrite32(lvr, tgpio->membase + TGPIO_LVR); + iowrite32(flr, tgpio->membase + TGPIO_FLR); + if (ver > 2) + iowrite32(bflr, tgpio->membase + TGPIO_BFLR); + + iowrite32(1 << offset, tgpio->membase + TGPIO_ICR); + +out: + spin_unlock_irqrestore(&tgpio->lock, flags); + return ret; +} + +static void timbgpio_irq(unsigned int irq, struct irq_desc *desc) +{ + struct timbgpio *tgpio = irq_get_handler_data(irq); + unsigned long ipr; + int offset; + + desc->irq_data.chip->irq_ack(irq_get_irq_data(irq)); + ipr = ioread32(tgpio->membase + TGPIO_IPR); + iowrite32(ipr, tgpio->membase + TGPIO_ICR); + + /* + * Some versions of the hardware trash the IER register if more than + * one interrupt is received simultaneously. + */ + iowrite32(0, tgpio->membase + TGPIO_IER); + + for_each_set_bit(offset, &ipr, tgpio->gpio.ngpio) + generic_handle_irq(timbgpio_to_irq(&tgpio->gpio, offset)); + + iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); +} + +static struct irq_chip timbgpio_irqchip = { + .name = "GPIO", + .irq_enable = timbgpio_irq_enable, + .irq_disable = timbgpio_irq_disable, + .irq_set_type = timbgpio_irq_type, +}; + +static int __devinit timbgpio_probe(struct platform_device *pdev) +{ + int err, i; + struct gpio_chip *gc; + struct timbgpio *tgpio; + struct resource *iomem; + struct timbgpio_platform_data *pdata = pdev->dev.platform_data; + int irq = platform_get_irq(pdev, 0); + + if (!pdata || pdata->nr_pins > 32) { + err = -EINVAL; + goto err_mem; + } + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iomem) { + err = -EINVAL; + goto err_mem; + } + + tgpio = kzalloc(sizeof(*tgpio), GFP_KERNEL); + if (!tgpio) { + err = -EINVAL; + goto err_mem; + } + tgpio->irq_base = pdata->irq_base; + + spin_lock_init(&tgpio->lock); + + if (!request_mem_region(iomem->start, resource_size(iomem), + DRIVER_NAME)) { + err = -EBUSY; + goto err_request; + } + + tgpio->membase = ioremap(iomem->start, resource_size(iomem)); + if (!tgpio->membase) { + err = -ENOMEM; + goto err_ioremap; + } + + gc = &tgpio->gpio; + + gc->label = dev_name(&pdev->dev); + gc->owner = THIS_MODULE; + gc->dev = &pdev->dev; + gc->direction_input = timbgpio_gpio_direction_input; + gc->get = timbgpio_gpio_get; + gc->direction_output = timbgpio_gpio_direction_output; + gc->set = timbgpio_gpio_set; + gc->to_irq = (irq >= 0 && tgpio->irq_base > 0) ? timbgpio_to_irq : NULL; + gc->dbg_show = NULL; + gc->base = pdata->gpio_base; + gc->ngpio = pdata->nr_pins; + gc->can_sleep = 0; + + err = gpiochip_add(gc); + if (err) + goto err_chipadd; + + platform_set_drvdata(pdev, tgpio); + + /* make sure to disable interrupts */ + iowrite32(0x0, tgpio->membase + TGPIO_IER); + + if (irq < 0 || tgpio->irq_base <= 0) + return 0; + + for (i = 0; i < pdata->nr_pins; i++) { + irq_set_chip_and_handler_name(tgpio->irq_base + i, + &timbgpio_irqchip, handle_simple_irq, "mux"); + irq_set_chip_data(tgpio->irq_base + i, tgpio); +#ifdef CONFIG_ARM + set_irq_flags(tgpio->irq_base + i, IRQF_VALID | IRQF_PROBE); +#endif + } + + irq_set_handler_data(irq, tgpio); + irq_set_chained_handler(irq, timbgpio_irq); + + return 0; + +err_chipadd: + iounmap(tgpio->membase); +err_ioremap: + release_mem_region(iomem->start, resource_size(iomem)); +err_request: + kfree(tgpio); +err_mem: + printk(KERN_ERR DRIVER_NAME": Failed to register GPIOs: %d\n", err); + + return err; +} + +static int __devexit timbgpio_remove(struct platform_device *pdev) +{ + int err; + struct timbgpio_platform_data *pdata = pdev->dev.platform_data; + struct timbgpio *tgpio = platform_get_drvdata(pdev); + struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int irq = platform_get_irq(pdev, 0); + + if (irq >= 0 && tgpio->irq_base > 0) { + int i; + for (i = 0; i < pdata->nr_pins; i++) { + irq_set_chip(tgpio->irq_base + i, NULL); + irq_set_chip_data(tgpio->irq_base + i, NULL); + } + + irq_set_handler(irq, NULL); + irq_set_handler_data(irq, NULL); + } + + err = gpiochip_remove(&tgpio->gpio); + if (err) + printk(KERN_ERR DRIVER_NAME": failed to remove gpio_chip\n"); + + iounmap(tgpio->membase); + release_mem_region(iomem->start, resource_size(iomem)); + kfree(tgpio); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver timbgpio_platform_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = timbgpio_probe, + .remove = timbgpio_remove, +}; + +/*--------------------------------------------------------------------------*/ + +static int __init timbgpio_init(void) +{ + return platform_driver_register(&timbgpio_platform_driver); +} + +static void __exit timbgpio_exit(void) +{ + platform_driver_unregister(&timbgpio_platform_driver); +} + +module_init(timbgpio_init); +module_exit(timbgpio_exit); + +MODULE_DESCRIPTION("Timberdale GPIO driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Mocean Laboratories"); +MODULE_ALIAS("platform:"DRIVER_NAME); + diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c new file mode 100644 index 00000000000..41710332cb0 --- /dev/null +++ b/drivers/gpio/gpio-tps65910.c @@ -0,0 +1,100 @@ +/* + * TI TPS6591x GPIO driver + * + * Copyright 2010 Texas Instruments Inc. + * + * Author: Graeme Gregory + * Author: Jorge Eduardo Candelaria jedu@slimlogic.co.uk> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); + uint8_t val; + + tps65910->read(tps65910, TPS65910_GPIO0 + offset, 1, &val); + + if (val & GPIO_STS_MASK) + return 1; + + return 0; +} + +static void tps65910_gpio_set(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); + + if (value) + tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset, + GPIO_SET_MASK); + else + tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset, + GPIO_SET_MASK); +} + +static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); + + /* Set the initial value */ + tps65910_gpio_set(gc, 0, value); + + return tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset, + GPIO_CFG_MASK); +} + +static int tps65910_gpio_input(struct gpio_chip *gc, unsigned offset) +{ + struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); + + return tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset, + GPIO_CFG_MASK); +} + +void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base) +{ + int ret; + + if (!gpio_base) + return; + + tps65910->gpio.owner = THIS_MODULE; + tps65910->gpio.label = tps65910->i2c_client->name; + tps65910->gpio.dev = tps65910->dev; + tps65910->gpio.base = gpio_base; + + switch(tps65910_chip_id(tps65910)) { + case TPS65910: + tps65910->gpio.ngpio = 6; + case TPS65911: + tps65910->gpio.ngpio = 9; + default: + return; + } + tps65910->gpio.can_sleep = 1; + + tps65910->gpio.direction_input = tps65910_gpio_input; + tps65910->gpio.direction_output = tps65910_gpio_output; + tps65910->gpio.set = tps65910_gpio_set; + tps65910->gpio.get = tps65910_gpio_get; + + ret = gpiochip_add(&tps65910->gpio); + + if (ret) + dev_warn(tps65910->dev, "GPIO registration failed: %d\n", ret); +} diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c new file mode 100644 index 00000000000..b8b4f228757 --- /dev/null +++ b/drivers/gpio/gpio-twl4030.c @@ -0,0 +1,513 @@ +/* + * Access to GPIOs on TWL4030/TPS659x0 chips + * + * Copyright (C) 2006-2007 Texas Instruments, Inc. + * Copyright (C) 2006 MontaVista Software, Inc. + * + * Code re-arranged and cleaned up by: + * Syed Mohammed Khasim + * + * Initial Code: + * Andy Lowe / Nishanth Menon + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + + +/* + * The GPIO "subchip" supports 18 GPIOs which can be configured as + * inputs or outputs, with pullups or pulldowns on each pin. Each + * GPIO can trigger interrupts on either or both edges. + * + * GPIO interrupts can be fed to either of two IRQ lines; this is + * intended to support multiple hosts. + * + * There are also two LED pins used sometimes as output-only GPIOs. + */ + + +static struct gpio_chip twl_gpiochip; +static int twl4030_gpio_irq_base; + +/* genirq interfaces are not available to modules */ +#ifdef MODULE +#define is_module() true +#else +#define is_module() false +#endif + +/* GPIO_CTRL Fields */ +#define MASK_GPIO_CTRL_GPIO0CD1 BIT(0) +#define MASK_GPIO_CTRL_GPIO1CD2 BIT(1) +#define MASK_GPIO_CTRL_GPIO_ON BIT(2) + +/* Mask for GPIO registers when aggregated into a 32-bit integer */ +#define GPIO_32_MASK 0x0003ffff + +/* Data structures */ +static DEFINE_MUTEX(gpio_lock); + +/* store usage of each GPIO. - each bit represents one GPIO */ +static unsigned int gpio_usage_count; + +/*----------------------------------------------------------------------*/ + +/* + * To configure TWL4030 GPIO module registers + */ +static inline int gpio_twl4030_write(u8 address, u8 data) +{ + return twl_i2c_write_u8(TWL4030_MODULE_GPIO, data, address); +} + +/*----------------------------------------------------------------------*/ + +/* + * LED register offsets (use TWL4030_MODULE_{LED,PWMA,PWMB})) + * PWMs A and B are dedicated to LEDs A and B, respectively. + */ + +#define TWL4030_LED_LEDEN 0x0 + +/* LEDEN bits */ +#define LEDEN_LEDAON BIT(0) +#define LEDEN_LEDBON BIT(1) +#define LEDEN_LEDAEXT BIT(2) +#define LEDEN_LEDBEXT BIT(3) +#define LEDEN_LEDAPWM BIT(4) +#define LEDEN_LEDBPWM BIT(5) +#define LEDEN_PWM_LENGTHA BIT(6) +#define LEDEN_PWM_LENGTHB BIT(7) + +#define TWL4030_PWMx_PWMxON 0x0 +#define TWL4030_PWMx_PWMxOFF 0x1 + +#define PWMxON_LENGTH BIT(7) + +/*----------------------------------------------------------------------*/ + +/* + * To read a TWL4030 GPIO module register + */ +static inline int gpio_twl4030_read(u8 address) +{ + u8 data; + int ret = 0; + + ret = twl_i2c_read_u8(TWL4030_MODULE_GPIO, &data, address); + return (ret < 0) ? ret : data; +} + +/*----------------------------------------------------------------------*/ + +static u8 cached_leden; /* protected by gpio_lock */ + +/* The LED lines are open drain outputs ... a FET pulls to GND, so an + * external pullup is needed. We could also expose the integrated PWM + * as a LED brightness control; we initialize it as "always on". + */ +static void twl4030_led_set_value(int led, int value) +{ + u8 mask = LEDEN_LEDAON | LEDEN_LEDAPWM; + int status; + + if (led) + mask <<= 1; + + mutex_lock(&gpio_lock); + if (value) + cached_leden &= ~mask; + else + cached_leden |= mask; + status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden, + TWL4030_LED_LEDEN); + mutex_unlock(&gpio_lock); +} + +static int twl4030_set_gpio_direction(int gpio, int is_input) +{ + u8 d_bnk = gpio >> 3; + u8 d_msk = BIT(gpio & 0x7); + u8 reg = 0; + u8 base = REG_GPIODATADIR1 + d_bnk; + int ret = 0; + + mutex_lock(&gpio_lock); + ret = gpio_twl4030_read(base); + if (ret >= 0) { + if (is_input) + reg = ret & ~d_msk; + else + reg = ret | d_msk; + + ret = gpio_twl4030_write(base, reg); + } + mutex_unlock(&gpio_lock); + return ret; +} + +static int twl4030_set_gpio_dataout(int gpio, int enable) +{ + u8 d_bnk = gpio >> 3; + u8 d_msk = BIT(gpio & 0x7); + u8 base = 0; + + if (enable) + base = REG_SETGPIODATAOUT1 + d_bnk; + else + base = REG_CLEARGPIODATAOUT1 + d_bnk; + + return gpio_twl4030_write(base, d_msk); +} + +static int twl4030_get_gpio_datain(int gpio) +{ + u8 d_bnk = gpio >> 3; + u8 d_off = gpio & 0x7; + u8 base = 0; + int ret = 0; + + if (unlikely((gpio >= TWL4030_GPIO_MAX) + || !(gpio_usage_count & BIT(gpio)))) + return -EPERM; + + base = REG_GPIODATAIN1 + d_bnk; + ret = gpio_twl4030_read(base); + if (ret > 0) + ret = (ret >> d_off) & 0x1; + + return ret; +} + +/*----------------------------------------------------------------------*/ + +static int twl_request(struct gpio_chip *chip, unsigned offset) +{ + int status = 0; + + mutex_lock(&gpio_lock); + + /* Support the two LED outputs as output-only GPIOs. */ + if (offset >= TWL4030_GPIO_MAX) { + u8 ledclr_mask = LEDEN_LEDAON | LEDEN_LEDAEXT + | LEDEN_LEDAPWM | LEDEN_PWM_LENGTHA; + u8 module = TWL4030_MODULE_PWMA; + + offset -= TWL4030_GPIO_MAX; + if (offset) { + ledclr_mask <<= 1; + module = TWL4030_MODULE_PWMB; + } + + /* initialize PWM to always-drive */ + status = twl_i2c_write_u8(module, 0x7f, + TWL4030_PWMx_PWMxOFF); + if (status < 0) + goto done; + status = twl_i2c_write_u8(module, 0x7f, + TWL4030_PWMx_PWMxON); + if (status < 0) + goto done; + + /* init LED to not-driven (high) */ + module = TWL4030_MODULE_LED; + status = twl_i2c_read_u8(module, &cached_leden, + TWL4030_LED_LEDEN); + if (status < 0) + goto done; + cached_leden &= ~ledclr_mask; + status = twl_i2c_write_u8(module, cached_leden, + TWL4030_LED_LEDEN); + if (status < 0) + goto done; + + status = 0; + goto done; + } + + /* on first use, turn GPIO module "on" */ + if (!gpio_usage_count) { + struct twl4030_gpio_platform_data *pdata; + u8 value = MASK_GPIO_CTRL_GPIO_ON; + + /* optionally have the first two GPIOs switch vMMC1 + * and vMMC2 power supplies based on card presence. + */ + pdata = chip->dev->platform_data; + value |= pdata->mmc_cd & 0x03; + + status = gpio_twl4030_write(REG_GPIO_CTRL, value); + } + + if (!status) + gpio_usage_count |= (0x1 << offset); + +done: + mutex_unlock(&gpio_lock); + return status; +} + +static void twl_free(struct gpio_chip *chip, unsigned offset) +{ + if (offset >= TWL4030_GPIO_MAX) { + twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1); + return; + } + + mutex_lock(&gpio_lock); + + gpio_usage_count &= ~BIT(offset); + + /* on last use, switch off GPIO module */ + if (!gpio_usage_count) + gpio_twl4030_write(REG_GPIO_CTRL, 0x0); + + mutex_unlock(&gpio_lock); +} + +static int twl_direction_in(struct gpio_chip *chip, unsigned offset) +{ + return (offset < TWL4030_GPIO_MAX) + ? twl4030_set_gpio_direction(offset, 1) + : -EINVAL; +} + +static int twl_get(struct gpio_chip *chip, unsigned offset) +{ + int status = 0; + + if (offset < TWL4030_GPIO_MAX) + status = twl4030_get_gpio_datain(offset); + else if (offset == TWL4030_GPIO_MAX) + status = cached_leden & LEDEN_LEDAON; + else + status = cached_leden & LEDEN_LEDBON; + return (status < 0) ? 0 : status; +} + +static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value) +{ + if (offset < TWL4030_GPIO_MAX) { + twl4030_set_gpio_dataout(offset, value); + return twl4030_set_gpio_direction(offset, 0); + } else { + twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value); + return 0; + } +} + +static void twl_set(struct gpio_chip *chip, unsigned offset, int value) +{ + if (offset < TWL4030_GPIO_MAX) + twl4030_set_gpio_dataout(offset, value); + else + twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value); +} + +static int twl_to_irq(struct gpio_chip *chip, unsigned offset) +{ + return (twl4030_gpio_irq_base && (offset < TWL4030_GPIO_MAX)) + ? (twl4030_gpio_irq_base + offset) + : -EINVAL; +} + +static struct gpio_chip twl_gpiochip = { + .label = "twl4030", + .owner = THIS_MODULE, + .request = twl_request, + .free = twl_free, + .direction_input = twl_direction_in, + .get = twl_get, + .direction_output = twl_direction_out, + .set = twl_set, + .to_irq = twl_to_irq, + .can_sleep = 1, +}; + +/*----------------------------------------------------------------------*/ + +static int __devinit gpio_twl4030_pulls(u32 ups, u32 downs) +{ + u8 message[6]; + unsigned i, gpio_bit; + + /* For most pins, a pulldown was enabled by default. + * We should have data that's specific to this board. + */ + for (gpio_bit = 1, i = 1; i < 6; i++) { + u8 bit_mask; + unsigned j; + + for (bit_mask = 0, j = 0; j < 8; j += 2, gpio_bit <<= 1) { + if (ups & gpio_bit) + bit_mask |= 1 << (j + 1); + else if (downs & gpio_bit) + bit_mask |= 1 << (j + 0); + } + message[i] = bit_mask; + } + + return twl_i2c_write(TWL4030_MODULE_GPIO, message, + REG_GPIOPUPDCTR1, 5); +} + +static int __devinit gpio_twl4030_debounce(u32 debounce, u8 mmc_cd) +{ + u8 message[4]; + + /* 30 msec of debouncing is always used for MMC card detect, + * and is optional for everything else. + */ + message[1] = (debounce & 0xff) | (mmc_cd & 0x03); + debounce >>= 8; + message[2] = (debounce & 0xff); + debounce >>= 8; + message[3] = (debounce & 0x03); + + return twl_i2c_write(TWL4030_MODULE_GPIO, message, + REG_GPIO_DEBEN1, 3); +} + +static int gpio_twl4030_remove(struct platform_device *pdev); + +static int __devinit gpio_twl4030_probe(struct platform_device *pdev) +{ + struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data; + int ret; + + /* maybe setup IRQs */ + if (pdata->irq_base) { + if (is_module()) { + dev_err(&pdev->dev, + "can't dispatch IRQs from modules\n"); + goto no_irqs; + } + ret = twl4030_sih_setup(TWL4030_MODULE_GPIO); + if (ret < 0) + return ret; + WARN_ON(ret != pdata->irq_base); + twl4030_gpio_irq_base = ret; + } + +no_irqs: + /* + * NOTE: boards may waste power if they don't set pullups + * and pulldowns correctly ... default for non-ULPI pins is + * pulldown, and some other pins may have external pullups + * or pulldowns. Careful! + */ + ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns); + if (ret) + dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n", + pdata->pullups, pdata->pulldowns, + ret); + + ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd); + if (ret) + dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n", + pdata->debounce, pdata->mmc_cd, + ret); + + twl_gpiochip.base = pdata->gpio_base; + twl_gpiochip.ngpio = TWL4030_GPIO_MAX; + twl_gpiochip.dev = &pdev->dev; + + /* NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE, + * is (still) clear if use_leds is set. + */ + if (pdata->use_leds) + twl_gpiochip.ngpio += 2; + + ret = gpiochip_add(&twl_gpiochip); + if (ret < 0) { + dev_err(&pdev->dev, + "could not register gpiochip, %d\n", + ret); + twl_gpiochip.ngpio = 0; + gpio_twl4030_remove(pdev); + } else if (pdata->setup) { + int status; + + status = pdata->setup(&pdev->dev, + pdata->gpio_base, TWL4030_GPIO_MAX); + if (status) + dev_dbg(&pdev->dev, "setup --> %d\n", status); + } + + return ret; +} + +/* Cannot use __devexit as gpio_twl4030_probe() calls us */ +static int gpio_twl4030_remove(struct platform_device *pdev) +{ + struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data; + int status; + + if (pdata->teardown) { + status = pdata->teardown(&pdev->dev, + pdata->gpio_base, TWL4030_GPIO_MAX); + if (status) { + dev_dbg(&pdev->dev, "teardown --> %d\n", status); + return status; + } + } + + status = gpiochip_remove(&twl_gpiochip); + if (status < 0) + return status; + + if (is_module()) + return 0; + + /* REVISIT no support yet for deregistering all the IRQs */ + WARN_ON(1); + return -EIO; +} + +/* Note: this hardware lives inside an I2C-based multi-function device. */ +MODULE_ALIAS("platform:twl4030_gpio"); + +static struct platform_driver gpio_twl4030_driver = { + .driver.name = "twl4030_gpio", + .driver.owner = THIS_MODULE, + .probe = gpio_twl4030_probe, + .remove = gpio_twl4030_remove, +}; + +static int __init gpio_twl4030_init(void) +{ + return platform_driver_register(&gpio_twl4030_driver); +} +subsys_initcall(gpio_twl4030_init); + +static void __exit gpio_twl4030_exit(void) +{ + platform_driver_unregister(&gpio_twl4030_driver); +} +module_exit(gpio_twl4030_exit); + +MODULE_AUTHOR("Texas Instruments, Inc."); +MODULE_DESCRIPTION("GPIO interface for TWL4030"); +MODULE_LICENSE("GPL"); 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/gpio-ucb1400.c b/drivers/gpio/gpio-ucb1400.c new file mode 100644 index 00000000000..50e6bd1392c --- /dev/null +++ b/drivers/gpio/gpio-ucb1400.c @@ -0,0 +1,125 @@ +/* + * Philips UCB1400 GPIO driver + * + * Author: Marek Vasut + * + * 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 + +struct ucb1400_gpio_data *ucbdata; + +static int ucb1400_gpio_dir_in(struct gpio_chip *gc, unsigned off) +{ + struct ucb1400_gpio *gpio; + gpio = container_of(gc, struct ucb1400_gpio, gc); + ucb1400_gpio_set_direction(gpio->ac97, off, 0); + return 0; +} + +static int ucb1400_gpio_dir_out(struct gpio_chip *gc, unsigned off, int val) +{ + struct ucb1400_gpio *gpio; + gpio = container_of(gc, struct ucb1400_gpio, gc); + ucb1400_gpio_set_direction(gpio->ac97, off, 1); + ucb1400_gpio_set_value(gpio->ac97, off, val); + return 0; +} + +static int ucb1400_gpio_get(struct gpio_chip *gc, unsigned off) +{ + struct ucb1400_gpio *gpio; + gpio = container_of(gc, struct ucb1400_gpio, gc); + return ucb1400_gpio_get_value(gpio->ac97, off); +} + +static void ucb1400_gpio_set(struct gpio_chip *gc, unsigned off, int val) +{ + struct ucb1400_gpio *gpio; + gpio = container_of(gc, struct ucb1400_gpio, gc); + ucb1400_gpio_set_value(gpio->ac97, off, val); +} + +static int ucb1400_gpio_probe(struct platform_device *dev) +{ + struct ucb1400_gpio *ucb = dev->dev.platform_data; + int err = 0; + + if (!(ucbdata && ucbdata->gpio_offset)) { + err = -EINVAL; + goto err; + } + + platform_set_drvdata(dev, ucb); + + ucb->gc.label = "ucb1400_gpio"; + ucb->gc.base = ucbdata->gpio_offset; + ucb->gc.ngpio = 10; + ucb->gc.owner = THIS_MODULE; + + ucb->gc.direction_input = ucb1400_gpio_dir_in; + ucb->gc.direction_output = ucb1400_gpio_dir_out; + ucb->gc.get = ucb1400_gpio_get; + ucb->gc.set = ucb1400_gpio_set; + ucb->gc.can_sleep = 1; + + err = gpiochip_add(&ucb->gc); + if (err) + goto err; + + if (ucbdata && ucbdata->gpio_setup) + err = ucbdata->gpio_setup(&dev->dev, ucb->gc.ngpio); + +err: + return err; + +} + +static int ucb1400_gpio_remove(struct platform_device *dev) +{ + int err = 0; + struct ucb1400_gpio *ucb = platform_get_drvdata(dev); + + if (ucbdata && ucbdata->gpio_teardown) { + err = ucbdata->gpio_teardown(&dev->dev, ucb->gc.ngpio); + if (err) + return err; + } + + err = gpiochip_remove(&ucb->gc); + return err; +} + +static struct platform_driver ucb1400_gpio_driver = { + .probe = ucb1400_gpio_probe, + .remove = ucb1400_gpio_remove, + .driver = { + .name = "ucb1400_gpio" + }, +}; + +static int __init ucb1400_gpio_init(void) +{ + return platform_driver_register(&ucb1400_gpio_driver); +} + +static void __exit ucb1400_gpio_exit(void) +{ + platform_driver_unregister(&ucb1400_gpio_driver); +} + +void __init ucb1400_gpio_set_data(struct ucb1400_gpio_data *data) +{ + ucbdata = data; +} + +module_init(ucb1400_gpio_init); +module_exit(ucb1400_gpio_exit); + +MODULE_DESCRIPTION("Philips UCB1400 GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-vr41xx.c b/drivers/gpio/gpio-vr41xx.c new file mode 100644 index 00000000000..a365be040b3 --- /dev/null +++ b/drivers/gpio/gpio-vr41xx.c @@ -0,0 +1,585 @@ +/* + * Driver for NEC VR4100 series General-purpose I/O Unit. + * + * Copyright (C) 2002 MontaVista Software Inc. + * Author: Yoichi Yuasa + * Copyright (C) 2003-2009 Yoichi Yuasa + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_AUTHOR("Yoichi Yuasa "); +MODULE_DESCRIPTION("NEC VR4100 series General-purpose I/O Unit driver"); +MODULE_LICENSE("GPL"); + +#define GIUIOSELL 0x00 +#define GIUIOSELH 0x02 +#define GIUPIODL 0x04 +#define GIUPIODH 0x06 +#define GIUINTSTATL 0x08 +#define GIUINTSTATH 0x0a +#define GIUINTENL 0x0c +#define GIUINTENH 0x0e +#define GIUINTTYPL 0x10 +#define GIUINTTYPH 0x12 +#define GIUINTALSELL 0x14 +#define GIUINTALSELH 0x16 +#define GIUINTHTSELL 0x18 +#define GIUINTHTSELH 0x1a +#define GIUPODATL 0x1c +#define GIUPODATEN 0x1c +#define GIUPODATH 0x1e + #define PIOEN0 0x0100 + #define PIOEN1 0x0200 +#define GIUPODAT 0x1e +#define GIUFEDGEINHL 0x20 +#define GIUFEDGEINHH 0x22 +#define GIUREDGEINHL 0x24 +#define GIUREDGEINHH 0x26 + +#define GIUUSEUPDN 0x1e0 +#define GIUTERMUPDN 0x1e2 + +#define GPIO_HAS_PULLUPDOWN_IO 0x0001 +#define GPIO_HAS_OUTPUT_ENABLE 0x0002 +#define GPIO_HAS_INTERRUPT_EDGE_SELECT 0x0100 + +enum { + GPIO_INPUT, + GPIO_OUTPUT, +}; + +static DEFINE_SPINLOCK(giu_lock); +static unsigned long giu_flags; + +static void __iomem *giu_base; + +#define giu_read(offset) readw(giu_base + (offset)) +#define giu_write(offset, value) writew((value), giu_base + (offset)) + +#define GPIO_PIN_OF_IRQ(irq) ((irq) - GIU_IRQ_BASE) +#define GIUINT_HIGH_OFFSET 16 +#define GIUINT_HIGH_MAX 32 + +static inline u16 giu_set(u16 offset, u16 set) +{ + u16 data; + + data = giu_read(offset); + data |= set; + giu_write(offset, data); + + return data; +} + +static inline u16 giu_clear(u16 offset, u16 clear) +{ + u16 data; + + data = giu_read(offset); + data &= ~clear; + giu_write(offset, data); + + return data; +} + +static void ack_giuint_low(struct irq_data *d) +{ + giu_write(GIUINTSTATL, 1 << GPIO_PIN_OF_IRQ(d->irq)); +} + +static void mask_giuint_low(struct irq_data *d) +{ + giu_clear(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(d->irq)); +} + +static void mask_ack_giuint_low(struct irq_data *d) +{ + unsigned int pin; + + pin = GPIO_PIN_OF_IRQ(d->irq); + giu_clear(GIUINTENL, 1 << pin); + giu_write(GIUINTSTATL, 1 << pin); +} + +static void unmask_giuint_low(struct irq_data *d) +{ + giu_set(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(d->irq)); +} + +static struct irq_chip giuint_low_irq_chip = { + .name = "GIUINTL", + .irq_ack = ack_giuint_low, + .irq_mask = mask_giuint_low, + .irq_mask_ack = mask_ack_giuint_low, + .irq_unmask = unmask_giuint_low, +}; + +static void ack_giuint_high(struct irq_data *d) +{ + giu_write(GIUINTSTATH, + 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET)); +} + +static void mask_giuint_high(struct irq_data *d) +{ + giu_clear(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET)); +} + +static void mask_ack_giuint_high(struct irq_data *d) +{ + unsigned int pin; + + pin = GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET; + giu_clear(GIUINTENH, 1 << pin); + giu_write(GIUINTSTATH, 1 << pin); +} + +static void unmask_giuint_high(struct irq_data *d) +{ + giu_set(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET)); +} + +static struct irq_chip giuint_high_irq_chip = { + .name = "GIUINTH", + .irq_ack = ack_giuint_high, + .irq_mask = mask_giuint_high, + .irq_mask_ack = mask_ack_giuint_high, + .irq_unmask = unmask_giuint_high, +}; + +static int giu_get_irq(unsigned int irq) +{ + u16 pendl, pendh, maskl, maskh; + int i; + + pendl = giu_read(GIUINTSTATL); + pendh = giu_read(GIUINTSTATH); + maskl = giu_read(GIUINTENL); + maskh = giu_read(GIUINTENH); + + maskl &= pendl; + maskh &= pendh; + + if (maskl) { + for (i = 0; i < 16; i++) { + if (maskl & (1 << i)) + return GIU_IRQ(i); + } + } else if (maskh) { + for (i = 0; i < 16; i++) { + if (maskh & (1 << i)) + return GIU_IRQ(i + GIUINT_HIGH_OFFSET); + } + } + + printk(KERN_ERR "spurious GIU interrupt: %04x(%04x),%04x(%04x)\n", + maskl, pendl, maskh, pendh); + + atomic_inc(&irq_err_count); + + return -EINVAL; +} + +void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger, + irq_signal_t signal) +{ + u16 mask; + + if (pin < GIUINT_HIGH_OFFSET) { + mask = 1 << pin; + if (trigger != IRQ_TRIGGER_LEVEL) { + giu_set(GIUINTTYPL, mask); + if (signal == IRQ_SIGNAL_HOLD) + giu_set(GIUINTHTSELL, mask); + else + giu_clear(GIUINTHTSELL, mask); + if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) { + switch (trigger) { + case IRQ_TRIGGER_EDGE_FALLING: + giu_set(GIUFEDGEINHL, mask); + giu_clear(GIUREDGEINHL, mask); + break; + case IRQ_TRIGGER_EDGE_RISING: + giu_clear(GIUFEDGEINHL, mask); + giu_set(GIUREDGEINHL, mask); + break; + default: + giu_set(GIUFEDGEINHL, mask); + giu_set(GIUREDGEINHL, mask); + break; + } + } + irq_set_chip_and_handler(GIU_IRQ(pin), + &giuint_low_irq_chip, + handle_edge_irq); + } else { + giu_clear(GIUINTTYPL, mask); + giu_clear(GIUINTHTSELL, mask); + irq_set_chip_and_handler(GIU_IRQ(pin), + &giuint_low_irq_chip, + handle_level_irq); + } + giu_write(GIUINTSTATL, mask); + } else if (pin < GIUINT_HIGH_MAX) { + mask = 1 << (pin - GIUINT_HIGH_OFFSET); + if (trigger != IRQ_TRIGGER_LEVEL) { + giu_set(GIUINTTYPH, mask); + if (signal == IRQ_SIGNAL_HOLD) + giu_set(GIUINTHTSELH, mask); + else + giu_clear(GIUINTHTSELH, mask); + if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) { + switch (trigger) { + case IRQ_TRIGGER_EDGE_FALLING: + giu_set(GIUFEDGEINHH, mask); + giu_clear(GIUREDGEINHH, mask); + break; + case IRQ_TRIGGER_EDGE_RISING: + giu_clear(GIUFEDGEINHH, mask); + giu_set(GIUREDGEINHH, mask); + break; + default: + giu_set(GIUFEDGEINHH, mask); + giu_set(GIUREDGEINHH, mask); + break; + } + } + irq_set_chip_and_handler(GIU_IRQ(pin), + &giuint_high_irq_chip, + handle_edge_irq); + } else { + giu_clear(GIUINTTYPH, mask); + giu_clear(GIUINTHTSELH, mask); + irq_set_chip_and_handler(GIU_IRQ(pin), + &giuint_high_irq_chip, + handle_level_irq); + } + giu_write(GIUINTSTATH, mask); + } +} +EXPORT_SYMBOL_GPL(vr41xx_set_irq_trigger); + +void vr41xx_set_irq_level(unsigned int pin, irq_level_t level) +{ + u16 mask; + + if (pin < GIUINT_HIGH_OFFSET) { + mask = 1 << pin; + if (level == IRQ_LEVEL_HIGH) + giu_set(GIUINTALSELL, mask); + else + giu_clear(GIUINTALSELL, mask); + giu_write(GIUINTSTATL, mask); + } else if (pin < GIUINT_HIGH_MAX) { + mask = 1 << (pin - GIUINT_HIGH_OFFSET); + if (level == IRQ_LEVEL_HIGH) + giu_set(GIUINTALSELH, mask); + else + giu_clear(GIUINTALSELH, mask); + giu_write(GIUINTSTATH, mask); + } +} +EXPORT_SYMBOL_GPL(vr41xx_set_irq_level); + +static int giu_set_direction(struct gpio_chip *chip, unsigned pin, int dir) +{ + u16 offset, mask, reg; + unsigned long flags; + + if (pin >= chip->ngpio) + return -EINVAL; + + if (pin < 16) { + offset = GIUIOSELL; + mask = 1 << pin; + } else if (pin < 32) { + offset = GIUIOSELH; + mask = 1 << (pin - 16); + } else { + if (giu_flags & GPIO_HAS_OUTPUT_ENABLE) { + offset = GIUPODATEN; + mask = 1 << (pin - 32); + } else { + switch (pin) { + case 48: + offset = GIUPODATH; + mask = PIOEN0; + break; + case 49: + offset = GIUPODATH; + mask = PIOEN1; + break; + default: + return -EINVAL; + } + } + } + + spin_lock_irqsave(&giu_lock, flags); + + reg = giu_read(offset); + if (dir == GPIO_OUTPUT) + reg |= mask; + else + reg &= ~mask; + giu_write(offset, reg); + + spin_unlock_irqrestore(&giu_lock, flags); + + return 0; +} + +int vr41xx_gpio_pullupdown(unsigned int pin, gpio_pull_t pull) +{ + u16 reg, mask; + unsigned long flags; + + if ((giu_flags & GPIO_HAS_PULLUPDOWN_IO) != GPIO_HAS_PULLUPDOWN_IO) + return -EPERM; + + if (pin >= 15) + return -EINVAL; + + mask = 1 << pin; + + spin_lock_irqsave(&giu_lock, flags); + + if (pull == GPIO_PULL_UP || pull == GPIO_PULL_DOWN) { + reg = giu_read(GIUTERMUPDN); + if (pull == GPIO_PULL_UP) + reg |= mask; + else + reg &= ~mask; + giu_write(GIUTERMUPDN, reg); + + reg = giu_read(GIUUSEUPDN); + reg |= mask; + giu_write(GIUUSEUPDN, reg); + } else { + reg = giu_read(GIUUSEUPDN); + reg &= ~mask; + giu_write(GIUUSEUPDN, reg); + } + + spin_unlock_irqrestore(&giu_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(vr41xx_gpio_pullupdown); + +static int vr41xx_gpio_get(struct gpio_chip *chip, unsigned pin) +{ + u16 reg, mask; + + if (pin >= chip->ngpio) + return -EINVAL; + + if (pin < 16) { + reg = giu_read(GIUPIODL); + mask = 1 << pin; + } else if (pin < 32) { + reg = giu_read(GIUPIODH); + mask = 1 << (pin - 16); + } else if (pin < 48) { + reg = giu_read(GIUPODATL); + mask = 1 << (pin - 32); + } else { + reg = giu_read(GIUPODATH); + mask = 1 << (pin - 48); + } + + if (reg & mask) + return 1; + + return 0; +} + +static void vr41xx_gpio_set(struct gpio_chip *chip, unsigned pin, + int value) +{ + u16 offset, mask, reg; + unsigned long flags; + + if (pin >= chip->ngpio) + return; + + if (pin < 16) { + offset = GIUPIODL; + mask = 1 << pin; + } else if (pin < 32) { + offset = GIUPIODH; + mask = 1 << (pin - 16); + } else if (pin < 48) { + offset = GIUPODATL; + mask = 1 << (pin - 32); + } else { + offset = GIUPODATH; + mask = 1 << (pin - 48); + } + + spin_lock_irqsave(&giu_lock, flags); + + reg = giu_read(offset); + if (value) + reg |= mask; + else + reg &= ~mask; + giu_write(offset, reg); + + spin_unlock_irqrestore(&giu_lock, flags); +} + + +static int vr41xx_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + return giu_set_direction(chip, offset, GPIO_INPUT); +} + +static int vr41xx_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + vr41xx_gpio_set(chip, offset, value); + + return giu_set_direction(chip, offset, GPIO_OUTPUT); +} + +static int vr41xx_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + if (offset >= chip->ngpio) + return -EINVAL; + + return GIU_IRQ_BASE + offset; +} + +static struct gpio_chip vr41xx_gpio_chip = { + .label = "vr41xx", + .owner = THIS_MODULE, + .direction_input = vr41xx_gpio_direction_input, + .get = vr41xx_gpio_get, + .direction_output = vr41xx_gpio_direction_output, + .set = vr41xx_gpio_set, + .to_irq = vr41xx_gpio_to_irq, +}; + +static int __devinit giu_probe(struct platform_device *pdev) +{ + struct resource *res; + unsigned int trigger, i, pin; + struct irq_chip *chip; + int irq, retval; + + switch (pdev->id) { + case GPIO_50PINS_PULLUPDOWN: + giu_flags = GPIO_HAS_PULLUPDOWN_IO; + vr41xx_gpio_chip.ngpio = 50; + break; + case GPIO_36PINS: + vr41xx_gpio_chip.ngpio = 36; + break; + case GPIO_48PINS_EDGE_SELECT: + giu_flags = GPIO_HAS_INTERRUPT_EDGE_SELECT; + vr41xx_gpio_chip.ngpio = 48; + break; + default: + dev_err(&pdev->dev, "GIU: unknown ID %d\n", pdev->id); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EBUSY; + + giu_base = ioremap(res->start, res->end - res->start + 1); + if (!giu_base) + return -ENOMEM; + + vr41xx_gpio_chip.dev = &pdev->dev; + + retval = gpiochip_add(&vr41xx_gpio_chip); + + giu_write(GIUINTENL, 0); + giu_write(GIUINTENH, 0); + + trigger = giu_read(GIUINTTYPH) << 16; + trigger |= giu_read(GIUINTTYPL); + for (i = GIU_IRQ_BASE; i <= GIU_IRQ_LAST; i++) { + pin = GPIO_PIN_OF_IRQ(i); + if (pin < GIUINT_HIGH_OFFSET) + chip = &giuint_low_irq_chip; + else + chip = &giuint_high_irq_chip; + + if (trigger & (1 << pin)) + irq_set_chip_and_handler(i, chip, handle_edge_irq); + else + irq_set_chip_and_handler(i, chip, handle_level_irq); + + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0 || irq >= nr_irqs) + return -EBUSY; + + return cascade_irq(irq, giu_get_irq); +} + +static int __devexit giu_remove(struct platform_device *pdev) +{ + if (giu_base) { + iounmap(giu_base); + giu_base = NULL; + } + + return 0; +} + +static struct platform_driver giu_device_driver = { + .probe = giu_probe, + .remove = __devexit_p(giu_remove), + .driver = { + .name = "GIU", + .owner = THIS_MODULE, + }, +}; + +static int __init vr41xx_giu_init(void) +{ + return platform_driver_register(&giu_device_driver); +} + +static void __exit vr41xx_giu_exit(void) +{ + platform_driver_unregister(&giu_device_driver); +} + +module_init(vr41xx_giu_init); +module_exit(vr41xx_giu_exit); diff --git a/drivers/gpio/gpio-vx855.c b/drivers/gpio/gpio-vx855.c new file mode 100644 index 00000000000..ef5aabd8b8b --- /dev/null +++ b/drivers/gpio/gpio-vx855.c @@ -0,0 +1,333 @@ +/* + * Linux GPIOlib driver for the VIA VX855 integrated southbridge GPIO + * + * Copyright (C) 2009 VIA Technologies, Inc. + * Copyright (C) 2010 One Laptop per Child + * Author: Harald Welte + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "vx855_gpio" + +/* The VX855 south bridge has the following GPIO pins: + * GPI 0...13 General Purpose Input + * GPO 0...12 General Purpose Output + * GPIO 0...14 General Purpose I/O (Open-Drain) + */ + +#define NR_VX855_GPI 14 +#define NR_VX855_GPO 13 +#define NR_VX855_GPIO 15 + +#define NR_VX855_GPInO (NR_VX855_GPI + NR_VX855_GPO) +#define NR_VX855_GP (NR_VX855_GPI + NR_VX855_GPO + NR_VX855_GPIO) + +struct vx855_gpio { + struct gpio_chip gpio; + spinlock_t lock; + u32 io_gpi; + u32 io_gpo; + bool gpi_reserved; + bool gpo_reserved; +}; + +/* resolve a GPIx into the corresponding bit position */ +static inline u_int32_t gpi_i_bit(int i) +{ + if (i < 10) + return 1 << i; + else + return 1 << (i + 14); +} + +static inline u_int32_t gpo_o_bit(int i) +{ + if (i < 11) + return 1 << i; + else + return 1 << (i + 14); +} + +static inline u_int32_t gpio_i_bit(int i) +{ + if (i < 14) + return 1 << (i + 10); + else + return 1 << (i + 14); +} + +static inline u_int32_t gpio_o_bit(int i) +{ + if (i < 14) + return 1 << (i + 11); + else + return 1 << (i + 13); +} + +/* Mapping betwee numeric GPIO ID and the actual GPIO hardware numbering: + * 0..13 GPI 0..13 + * 14..26 GPO 0..12 + * 27..41 GPIO 0..14 + */ + +static int vx855gpio_direction_input(struct gpio_chip *gpio, + unsigned int nr) +{ + struct vx855_gpio *vg = container_of(gpio, struct vx855_gpio, gpio); + unsigned long flags; + u_int32_t reg_out; + + /* Real GPI bits are always in input direction */ + if (nr < NR_VX855_GPI) + return 0; + + /* Real GPO bits cannot be put in output direction */ + if (nr < NR_VX855_GPInO) + return -EINVAL; + + /* Open Drain GPIO have to be set to one */ + spin_lock_irqsave(&vg->lock, flags); + reg_out = inl(vg->io_gpo); + reg_out |= gpio_o_bit(nr - NR_VX855_GPInO); + outl(reg_out, vg->io_gpo); + spin_unlock_irqrestore(&vg->lock, flags); + + return 0; +} + +static int vx855gpio_get(struct gpio_chip *gpio, unsigned int nr) +{ + struct vx855_gpio *vg = container_of(gpio, struct vx855_gpio, gpio); + u_int32_t reg_in; + int ret = 0; + + if (nr < NR_VX855_GPI) { + reg_in = inl(vg->io_gpi); + if (reg_in & gpi_i_bit(nr)) + ret = 1; + } else if (nr < NR_VX855_GPInO) { + /* GPO don't have an input bit, we need to read it + * back from the output register */ + reg_in = inl(vg->io_gpo); + if (reg_in & gpo_o_bit(nr - NR_VX855_GPI)) + ret = 1; + } else { + reg_in = inl(vg->io_gpi); + if (reg_in & gpio_i_bit(nr - NR_VX855_GPInO)) + ret = 1; + } + + return ret; +} + +static void vx855gpio_set(struct gpio_chip *gpio, unsigned int nr, + int val) +{ + struct vx855_gpio *vg = container_of(gpio, struct vx855_gpio, gpio); + unsigned long flags; + u_int32_t reg_out; + + /* True GPI cannot be switched to output mode */ + if (nr < NR_VX855_GPI) + return; + + spin_lock_irqsave(&vg->lock, flags); + reg_out = inl(vg->io_gpo); + if (nr < NR_VX855_GPInO) { + if (val) + reg_out |= gpo_o_bit(nr - NR_VX855_GPI); + else + reg_out &= ~gpo_o_bit(nr - NR_VX855_GPI); + } else { + if (val) + reg_out |= gpio_o_bit(nr - NR_VX855_GPInO); + else + reg_out &= ~gpio_o_bit(nr - NR_VX855_GPInO); + } + outl(reg_out, vg->io_gpo); + spin_unlock_irqrestore(&vg->lock, flags); +} + +static int vx855gpio_direction_output(struct gpio_chip *gpio, + unsigned int nr, int val) +{ + /* True GPI cannot be switched to output mode */ + if (nr < NR_VX855_GPI) + return -EINVAL; + + /* True GPO don't need to be switched to output mode, + * and GPIO are open-drain, i.e. also need no switching, + * so all we do is set the level */ + vx855gpio_set(gpio, nr, val); + + return 0; +} + +static const char *vx855gpio_names[NR_VX855_GP] = { + "VX855_GPI0", "VX855_GPI1", "VX855_GPI2", "VX855_GPI3", "VX855_GPI4", + "VX855_GPI5", "VX855_GPI6", "VX855_GPI7", "VX855_GPI8", "VX855_GPI9", + "VX855_GPI10", "VX855_GPI11", "VX855_GPI12", "VX855_GPI13", + "VX855_GPO0", "VX855_GPO1", "VX855_GPO2", "VX855_GPO3", "VX855_GPO4", + "VX855_GPO5", "VX855_GPO6", "VX855_GPO7", "VX855_GPO8", "VX855_GPO9", + "VX855_GPO10", "VX855_GPO11", "VX855_GPO12", + "VX855_GPIO0", "VX855_GPIO1", "VX855_GPIO2", "VX855_GPIO3", + "VX855_GPIO4", "VX855_GPIO5", "VX855_GPIO6", "VX855_GPIO7", + "VX855_GPIO8", "VX855_GPIO9", "VX855_GPIO10", "VX855_GPIO11", + "VX855_GPIO12", "VX855_GPIO13", "VX855_GPIO14" +}; + +static void vx855gpio_gpio_setup(struct vx855_gpio *vg) +{ + struct gpio_chip *c = &vg->gpio; + + c->label = "VX855 South Bridge"; + c->owner = THIS_MODULE; + c->direction_input = vx855gpio_direction_input; + c->direction_output = vx855gpio_direction_output; + c->get = vx855gpio_get; + c->set = vx855gpio_set; + c->dbg_show = NULL; + c->base = 0; + c->ngpio = NR_VX855_GP; + c->can_sleep = 0; + c->names = vx855gpio_names; +} + +/* This platform device is ordinarily registered by the vx855 mfd driver */ +static __devinit int vx855gpio_probe(struct platform_device *pdev) +{ + struct resource *res_gpi; + struct resource *res_gpo; + struct vx855_gpio *vg; + int ret; + + res_gpi = platform_get_resource(pdev, IORESOURCE_IO, 0); + res_gpo = platform_get_resource(pdev, IORESOURCE_IO, 1); + if (!res_gpi || !res_gpo) + return -EBUSY; + + vg = kzalloc(sizeof(*vg), GFP_KERNEL); + if (!vg) + return -ENOMEM; + + platform_set_drvdata(pdev, vg); + + dev_info(&pdev->dev, "found VX855 GPIO controller\n"); + vg->io_gpi = res_gpi->start; + vg->io_gpo = res_gpo->start; + spin_lock_init(&vg->lock); + + /* + * A single byte is used to control various GPIO ports on the VX855, + * and in the case of the OLPC XO-1.5, some of those ports are used + * for switches that are interpreted and exposed through ACPI. ACPI + * will have reserved the region, so our own reservation will not + * succeed. Ignore and continue. + */ + + if (!request_region(res_gpi->start, resource_size(res_gpi), + MODULE_NAME "_gpi")) + dev_warn(&pdev->dev, + "GPI I/O resource busy, probably claimed by ACPI\n"); + else + vg->gpi_reserved = true; + + if (!request_region(res_gpo->start, resource_size(res_gpo), + MODULE_NAME "_gpo")) + dev_warn(&pdev->dev, + "GPO I/O resource busy, probably claimed by ACPI\n"); + else + vg->gpo_reserved = true; + + vx855gpio_gpio_setup(vg); + + ret = gpiochip_add(&vg->gpio); + if (ret) { + dev_err(&pdev->dev, "failed to register GPIOs\n"); + goto out_release; + } + + return 0; + +out_release: + if (vg->gpi_reserved) + release_region(res_gpi->start, resource_size(res_gpi)); + if (vg->gpo_reserved) + release_region(res_gpi->start, resource_size(res_gpo)); + platform_set_drvdata(pdev, NULL); + kfree(vg); + return ret; +} + +static int __devexit vx855gpio_remove(struct platform_device *pdev) +{ + struct vx855_gpio *vg = platform_get_drvdata(pdev); + struct resource *res; + + if (gpiochip_remove(&vg->gpio)) + dev_err(&pdev->dev, "unable to remove gpio_chip?\n"); + + if (vg->gpi_reserved) { + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + release_region(res->start, resource_size(res)); + } + if (vg->gpo_reserved) { + res = platform_get_resource(pdev, IORESOURCE_IO, 1); + release_region(res->start, resource_size(res)); + } + + platform_set_drvdata(pdev, NULL); + kfree(vg); + return 0; +} + +static struct platform_driver vx855gpio_driver = { + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + }, + .probe = vx855gpio_probe, + .remove = __devexit_p(vx855gpio_remove), +}; + +static int vx855gpio_init(void) +{ + return platform_driver_register(&vx855gpio_driver); +} +module_init(vx855gpio_init); + +static void vx855gpio_exit(void) +{ + platform_driver_unregister(&vx855gpio_driver); +} +module_exit(vx855gpio_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("GPIO driver for the VIA VX855 chipset"); +MODULE_ALIAS("platform:vx855_gpio"); diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c new file mode 100644 index 00000000000..31a9ed7bba8 --- /dev/null +++ b/drivers/gpio/gpio-wm831x.c @@ -0,0 +1,317 @@ +/* + * gpiolib support for Wolfson WM831x PMICs + * + * Copyright 2009 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct wm831x_gpio { + struct wm831x *wm831x; + struct gpio_chip gpio_chip; +}; + +static inline struct wm831x_gpio *to_wm831x_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct wm831x_gpio, gpio_chip); +} + +static int wm831x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); + struct wm831x *wm831x = wm831x_gpio->wm831x; + int val = WM831X_GPN_DIR; + + if (wm831x->has_gpio_ena) + val |= WM831X_GPN_TRI; + + return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset, + WM831X_GPN_DIR | WM831X_GPN_TRI | + WM831X_GPN_FN_MASK, val); +} + +static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); + struct wm831x *wm831x = wm831x_gpio->wm831x; + int ret; + + ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); + if (ret < 0) + return ret; + + if (ret & 1 << offset) + return 1; + else + return 0; +} + +static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); + struct wm831x *wm831x = wm831x_gpio->wm831x; + + wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, 1 << offset, + value << offset); +} + +static int wm831x_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); + struct wm831x *wm831x = wm831x_gpio->wm831x; + int val = 0; + int ret; + + if (wm831x->has_gpio_ena) + val |= WM831X_GPN_TRI; + + ret = wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset, + WM831X_GPN_DIR | WM831X_GPN_TRI | + WM831X_GPN_FN_MASK, val); + if (ret < 0) + return ret; + + /* Can only set GPIO state once it's in output mode */ + wm831x_gpio_set(chip, offset, value); + + return 0; +} + +static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); + struct wm831x *wm831x = wm831x_gpio->wm831x; + + if (!wm831x->irq_base) + return -EINVAL; + + return wm831x->irq_base + WM831X_IRQ_GPIO_1 + offset; +} + +static int wm831x_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, + unsigned debounce) +{ + struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); + struct wm831x *wm831x = wm831x_gpio->wm831x; + int reg = WM831X_GPIO1_CONTROL + offset; + int ret, fn; + + ret = wm831x_reg_read(wm831x, reg); + if (ret < 0) + return ret; + + switch (ret & WM831X_GPN_FN_MASK) { + case 0: + case 1: + break; + default: + /* Not in GPIO mode */ + return -EBUSY; + } + + if (debounce >= 32 && debounce <= 64) + fn = 0; + else if (debounce >= 4000 && debounce <= 8000) + fn = 1; + else + return -EINVAL; + + return wm831x_set_bits(wm831x, reg, WM831X_GPN_FN_MASK, fn); +} + +#ifdef CONFIG_DEBUG_FS +static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); + struct wm831x *wm831x = wm831x_gpio->wm831x; + int i, tristated; + + for (i = 0; i < chip->ngpio; i++) { + int gpio = i + chip->base; + int reg; + const char *label, *pull, *powerdomain; + + /* We report the GPIO even if it's not requested since + * we're also reporting things like alternate + * functions which apply even when the GPIO is not in + * use as a GPIO. + */ + label = gpiochip_is_requested(chip, i); + if (!label) + label = "Unrequested"; + + seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label); + + reg = wm831x_reg_read(wm831x, WM831X_GPIO1_CONTROL + i); + if (reg < 0) { + dev_err(wm831x->dev, + "GPIO control %d read failed: %d\n", + gpio, reg); + seq_printf(s, "\n"); + continue; + } + + switch (reg & WM831X_GPN_PULL_MASK) { + case WM831X_GPIO_PULL_NONE: + pull = "nopull"; + break; + case WM831X_GPIO_PULL_DOWN: + pull = "pulldown"; + break; + case WM831X_GPIO_PULL_UP: + pull = "pullup"; + default: + pull = "INVALID PULL"; + break; + } + + switch (i + 1) { + case 1 ... 3: + case 7 ... 9: + if (reg & WM831X_GPN_PWR_DOM) + powerdomain = "VPMIC"; + else + powerdomain = "DBVDD"; + break; + + case 4 ... 6: + case 10 ... 12: + if (reg & WM831X_GPN_PWR_DOM) + powerdomain = "SYSVDD"; + else + powerdomain = "DBVDD"; + break; + + case 13 ... 16: + powerdomain = "TPVDD"; + break; + + default: + BUG(); + break; + } + + tristated = reg & WM831X_GPN_TRI; + if (wm831x->has_gpio_ena) + tristated = !tristated; + + seq_printf(s, " %s %s %s %s%s\n" + " %s%s (0x%4x)\n", + reg & WM831X_GPN_DIR ? "in" : "out", + wm831x_gpio_get(chip, i) ? "high" : "low", + pull, + powerdomain, + reg & WM831X_GPN_POL ? "" : " inverted", + reg & WM831X_GPN_OD ? "open-drain" : "CMOS", + tristated ? " tristated" : "", + reg); + } +} +#else +#define wm831x_gpio_dbg_show NULL +#endif + +static struct gpio_chip template_chip = { + .label = "wm831x", + .owner = THIS_MODULE, + .direction_input = wm831x_gpio_direction_in, + .get = wm831x_gpio_get, + .direction_output = wm831x_gpio_direction_out, + .set = wm831x_gpio_set, + .to_irq = wm831x_gpio_to_irq, + .set_debounce = wm831x_gpio_set_debounce, + .dbg_show = wm831x_gpio_dbg_show, + .can_sleep = 1, +}; + +static int __devinit wm831x_gpio_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct wm831x_gpio *wm831x_gpio; + int ret; + + wm831x_gpio = kzalloc(sizeof(*wm831x_gpio), GFP_KERNEL); + if (wm831x_gpio == NULL) + return -ENOMEM; + + wm831x_gpio->wm831x = wm831x; + wm831x_gpio->gpio_chip = template_chip; + wm831x_gpio->gpio_chip.ngpio = wm831x->num_gpio; + wm831x_gpio->gpio_chip.dev = &pdev->dev; + if (pdata && pdata->gpio_base) + wm831x_gpio->gpio_chip.base = pdata->gpio_base; + else + wm831x_gpio->gpio_chip.base = -1; + + ret = gpiochip_add(&wm831x_gpio->gpio_chip); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", + ret); + goto err; + } + + platform_set_drvdata(pdev, wm831x_gpio); + + return ret; + +err: + kfree(wm831x_gpio); + return ret; +} + +static int __devexit wm831x_gpio_remove(struct platform_device *pdev) +{ + struct wm831x_gpio *wm831x_gpio = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(&wm831x_gpio->gpio_chip); + if (ret == 0) + kfree(wm831x_gpio); + + return ret; +} + +static struct platform_driver wm831x_gpio_driver = { + .driver.name = "wm831x-gpio", + .driver.owner = THIS_MODULE, + .probe = wm831x_gpio_probe, + .remove = __devexit_p(wm831x_gpio_remove), +}; + +static int __init wm831x_gpio_init(void) +{ + return platform_driver_register(&wm831x_gpio_driver); +} +subsys_initcall(wm831x_gpio_init); + +static void __exit wm831x_gpio_exit(void) +{ + platform_driver_unregister(&wm831x_gpio_driver); +} +module_exit(wm831x_gpio_exit); + +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("GPIO interface for WM831x PMICs"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm831x-gpio"); diff --git a/drivers/gpio/gpio-wm8350.c b/drivers/gpio/gpio-wm8350.c new file mode 100644 index 00000000000..a06af515483 --- /dev/null +++ b/drivers/gpio/gpio-wm8350.c @@ -0,0 +1,182 @@ +/* + * gpiolib support for Wolfson WM835x PMICs + * + * Copyright 2009 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct wm8350_gpio_data { + struct wm8350 *wm8350; + struct gpio_chip gpio_chip; +}; + +static inline struct wm8350_gpio_data *to_wm8350_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct wm8350_gpio_data, gpio_chip); +} + +static int wm8350_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip); + struct wm8350 *wm8350 = wm8350_gpio->wm8350; + + return wm8350_set_bits(wm8350, WM8350_GPIO_CONFIGURATION_I_O, + 1 << offset); +} + +static int wm8350_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip); + struct wm8350 *wm8350 = wm8350_gpio->wm8350; + int ret; + + ret = wm8350_reg_read(wm8350, WM8350_GPIO_LEVEL); + if (ret < 0) + return ret; + + if (ret & (1 << offset)) + return 1; + else + return 0; +} + +static void wm8350_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip); + struct wm8350 *wm8350 = wm8350_gpio->wm8350; + + if (value) + wm8350_set_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset); + else + wm8350_clear_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset); +} + +static int wm8350_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip); + struct wm8350 *wm8350 = wm8350_gpio->wm8350; + int ret; + + ret = wm8350_clear_bits(wm8350, WM8350_GPIO_CONFIGURATION_I_O, + 1 << offset); + if (ret < 0) + return ret; + + /* Don't have an atomic direction/value setup */ + wm8350_gpio_set(chip, offset, value); + + return 0; +} + +static int wm8350_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip); + struct wm8350 *wm8350 = wm8350_gpio->wm8350; + + if (!wm8350->irq_base) + return -EINVAL; + + return wm8350->irq_base + WM8350_IRQ_GPIO(offset); +} + +static struct gpio_chip template_chip = { + .label = "wm8350", + .owner = THIS_MODULE, + .direction_input = wm8350_gpio_direction_in, + .get = wm8350_gpio_get, + .direction_output = wm8350_gpio_direction_out, + .set = wm8350_gpio_set, + .to_irq = wm8350_gpio_to_irq, + .can_sleep = 1, +}; + +static int __devinit wm8350_gpio_probe(struct platform_device *pdev) +{ + struct wm8350 *wm8350 = dev_get_drvdata(pdev->dev.parent); + struct wm8350_platform_data *pdata = wm8350->dev->platform_data; + struct wm8350_gpio_data *wm8350_gpio; + int ret; + + wm8350_gpio = kzalloc(sizeof(*wm8350_gpio), GFP_KERNEL); + if (wm8350_gpio == NULL) + return -ENOMEM; + + wm8350_gpio->wm8350 = wm8350; + wm8350_gpio->gpio_chip = template_chip; + wm8350_gpio->gpio_chip.ngpio = 13; + wm8350_gpio->gpio_chip.dev = &pdev->dev; + if (pdata && pdata->gpio_base) + wm8350_gpio->gpio_chip.base = pdata->gpio_base; + else + wm8350_gpio->gpio_chip.base = -1; + + ret = gpiochip_add(&wm8350_gpio->gpio_chip); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", + ret); + goto err; + } + + platform_set_drvdata(pdev, wm8350_gpio); + + return ret; + +err: + kfree(wm8350_gpio); + return ret; +} + +static int __devexit wm8350_gpio_remove(struct platform_device *pdev) +{ + struct wm8350_gpio_data *wm8350_gpio = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(&wm8350_gpio->gpio_chip); + if (ret == 0) + kfree(wm8350_gpio); + + return ret; +} + +static struct platform_driver wm8350_gpio_driver = { + .driver.name = "wm8350-gpio", + .driver.owner = THIS_MODULE, + .probe = wm8350_gpio_probe, + .remove = __devexit_p(wm8350_gpio_remove), +}; + +static int __init wm8350_gpio_init(void) +{ + return platform_driver_register(&wm8350_gpio_driver); +} +subsys_initcall(wm8350_gpio_init); + +static void __exit wm8350_gpio_exit(void) +{ + platform_driver_unregister(&wm8350_gpio_driver); +} +module_exit(wm8350_gpio_exit); + +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("GPIO interface for WM8350 PMICs"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8350-gpio"); diff --git a/drivers/gpio/gpio-wm8994.c b/drivers/gpio/gpio-wm8994.c new file mode 100644 index 00000000000..96198f3fab7 --- /dev/null +++ b/drivers/gpio/gpio-wm8994.c @@ -0,0 +1,242 @@ +/* + * gpiolib support for Wolfson WM8994 + * + * Copyright 2009 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct wm8994_gpio { + struct wm8994 *wm8994; + struct gpio_chip gpio_chip; +}; + +static inline struct wm8994_gpio *to_wm8994_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct wm8994_gpio, gpio_chip); +} + +static int wm8994_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip); + struct wm8994 *wm8994 = wm8994_gpio->wm8994; + + switch (wm8994->type) { + case WM8958: + switch (offset) { + case 1: + case 2: + case 3: + case 4: + case 6: + return -EINVAL; + } + break; + default: + break; + } + + return 0; +} + +static int wm8994_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip); + struct wm8994 *wm8994 = wm8994_gpio->wm8994; + + return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, + WM8994_GPN_DIR, WM8994_GPN_DIR); +} + +static int wm8994_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip); + struct wm8994 *wm8994 = wm8994_gpio->wm8994; + int ret; + + ret = wm8994_reg_read(wm8994, WM8994_GPIO_1 + offset); + if (ret < 0) + return ret; + + if (ret & WM8994_GPN_LVL) + return 1; + else + return 0; +} + +static int wm8994_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip); + struct wm8994 *wm8994 = wm8994_gpio->wm8994; + + return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, + WM8994_GPN_DIR, 0); +} + +static void wm8994_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip); + struct wm8994 *wm8994 = wm8994_gpio->wm8994; + + if (value) + value = WM8994_GPN_LVL; + + wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, WM8994_GPN_LVL, value); +} + +static int wm8994_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip); + struct wm8994 *wm8994 = wm8994_gpio->wm8994; + + if (!wm8994->irq_base) + return -EINVAL; + + return wm8994->irq_base + offset; +} + + +#ifdef CONFIG_DEBUG_FS +static void wm8994_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip); + struct wm8994 *wm8994 = wm8994_gpio->wm8994; + int i; + + for (i = 0; i < chip->ngpio; i++) { + int gpio = i + chip->base; + int reg; + const char *label; + + /* We report the GPIO even if it's not requested since + * we're also reporting things like alternate + * functions which apply even when the GPIO is not in + * use as a GPIO. + */ + label = gpiochip_is_requested(chip, i); + if (!label) + label = "Unrequested"; + + seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label); + + reg = wm8994_reg_read(wm8994, WM8994_GPIO_1 + i); + if (reg < 0) { + dev_err(wm8994->dev, + "GPIO control %d read failed: %d\n", + gpio, reg); + seq_printf(s, "\n"); + continue; + } + + /* No decode yet; note that GPIO2 is special */ + seq_printf(s, "(%x)\n", reg); + } +} +#else +#define wm8994_gpio_dbg_show NULL +#endif + +static struct gpio_chip template_chip = { + .label = "wm8994", + .owner = THIS_MODULE, + .request = wm8994_gpio_request, + .direction_input = wm8994_gpio_direction_in, + .get = wm8994_gpio_get, + .direction_output = wm8994_gpio_direction_out, + .set = wm8994_gpio_set, + .to_irq = wm8994_gpio_to_irq, + .dbg_show = wm8994_gpio_dbg_show, + .can_sleep = 1, +}; + +static int __devinit wm8994_gpio_probe(struct platform_device *pdev) +{ + struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent); + struct wm8994_pdata *pdata = wm8994->dev->platform_data; + struct wm8994_gpio *wm8994_gpio; + int ret; + + wm8994_gpio = kzalloc(sizeof(*wm8994_gpio), GFP_KERNEL); + if (wm8994_gpio == NULL) + return -ENOMEM; + + wm8994_gpio->wm8994 = wm8994; + wm8994_gpio->gpio_chip = template_chip; + wm8994_gpio->gpio_chip.ngpio = WM8994_GPIO_MAX; + wm8994_gpio->gpio_chip.dev = &pdev->dev; + if (pdata && pdata->gpio_base) + wm8994_gpio->gpio_chip.base = pdata->gpio_base; + else + wm8994_gpio->gpio_chip.base = -1; + + ret = gpiochip_add(&wm8994_gpio->gpio_chip); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", + ret); + goto err; + } + + platform_set_drvdata(pdev, wm8994_gpio); + + return ret; + +err: + kfree(wm8994_gpio); + return ret; +} + +static int __devexit wm8994_gpio_remove(struct platform_device *pdev) +{ + struct wm8994_gpio *wm8994_gpio = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(&wm8994_gpio->gpio_chip); + if (ret == 0) + kfree(wm8994_gpio); + + return ret; +} + +static struct platform_driver wm8994_gpio_driver = { + .driver.name = "wm8994-gpio", + .driver.owner = THIS_MODULE, + .probe = wm8994_gpio_probe, + .remove = __devexit_p(wm8994_gpio_remove), +}; + +static int __init wm8994_gpio_init(void) +{ + return platform_driver_register(&wm8994_gpio_driver); +} +subsys_initcall(wm8994_gpio_init); + +static void __exit wm8994_gpio_exit(void) +{ + platform_driver_unregister(&wm8994_gpio_driver); +} +module_exit(wm8994_gpio_exit); + +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("GPIO interface for WM8994"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8994-gpio"); diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c new file mode 100644 index 00000000000..846fbd5e31b --- /dev/null +++ b/drivers/gpio/gpio-xilinx.c @@ -0,0 +1,233 @@ +/* + * Xilinx gpio driver + * + * Copyright 2008 Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register Offset Definitions */ +#define XGPIO_DATA_OFFSET (0x0) /* Data register */ +#define XGPIO_TRI_OFFSET (0x4) /* I/O direction register */ + +struct xgpio_instance { + struct of_mm_gpio_chip mmchip; + u32 gpio_state; /* GPIO state shadow register */ + u32 gpio_dir; /* GPIO direction shadow register */ + spinlock_t gpio_lock; /* Lock used for synchronization */ +}; + +/** + * xgpio_get - Read the specified signal of the GPIO device. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * + * This function reads the specified signal of the GPIO device. It returns 0 if + * the signal clear, 1 if signal is set or negative value on error. + */ +static int xgpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + + return (in_be32(mm_gc->regs + XGPIO_DATA_OFFSET) >> gpio) & 1; +} + +/** + * xgpio_set - Write the specified signal of the GPIO device. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * @val: Value to be written to specified signal. + * + * This function writes the specified value in to the specified signal of the + * GPIO device. + */ +static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + unsigned long flags; + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct xgpio_instance *chip = + container_of(mm_gc, struct xgpio_instance, mmchip); + + spin_lock_irqsave(&chip->gpio_lock, flags); + + /* Write to GPIO signal and set its direction to output */ + if (val) + chip->gpio_state |= 1 << gpio; + else + chip->gpio_state &= ~(1 << gpio); + out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); + + spin_unlock_irqrestore(&chip->gpio_lock, flags); +} + +/** + * xgpio_dir_in - Set the direction of the specified GPIO signal as input. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * + * This function sets the direction of specified GPIO signal as input. + * It returns 0 if direction of GPIO signals is set as input otherwise it + * returns negative error value. + */ +static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + unsigned long flags; + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct xgpio_instance *chip = + container_of(mm_gc, struct xgpio_instance, mmchip); + + spin_lock_irqsave(&chip->gpio_lock, flags); + + /* Set the GPIO bit in shadow register and set direction as input */ + chip->gpio_dir |= (1 << gpio); + out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); + + spin_unlock_irqrestore(&chip->gpio_lock, flags); + + return 0; +} + +/** + * xgpio_dir_out - Set the direction of the specified GPIO signal as output. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * @val: Value to be written to specified signal. + * + * This function sets the direction of specified GPIO signal as output. If all + * GPIO signals of GPIO chip is configured as input then it returns + * error otherwise it returns 0. + */ +static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + unsigned long flags; + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct xgpio_instance *chip = + container_of(mm_gc, struct xgpio_instance, mmchip); + + spin_lock_irqsave(&chip->gpio_lock, flags); + + /* Write state of GPIO signal */ + if (val) + chip->gpio_state |= 1 << gpio; + else + chip->gpio_state &= ~(1 << gpio); + out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); + + /* Clear the GPIO bit in shadow register and set direction as output */ + chip->gpio_dir &= (~(1 << gpio)); + out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); + + spin_unlock_irqrestore(&chip->gpio_lock, flags); + + return 0; +} + +/** + * xgpio_save_regs - Set initial values of GPIO pins + * @mm_gc: pointer to memory mapped GPIO chip structure + */ +static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc) +{ + struct xgpio_instance *chip = + container_of(mm_gc, struct xgpio_instance, mmchip); + + out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); + out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); +} + +/** + * xgpio_of_probe - Probe method for the GPIO device. + * @np: pointer to device tree node + * + * This function probes the GPIO device in the device tree. It initializes the + * driver data structure. It returns 0, if the driver is bound to the GPIO + * device, or a negative value if there is an error. + */ +static int __devinit xgpio_of_probe(struct device_node *np) +{ + struct xgpio_instance *chip; + int status = 0; + const u32 *tree_info; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + /* Update GPIO state shadow register with default value */ + tree_info = of_get_property(np, "xlnx,dout-default", NULL); + if (tree_info) + chip->gpio_state = be32_to_cpup(tree_info); + + /* Update GPIO direction shadow register with default value */ + chip->gpio_dir = 0xFFFFFFFF; /* By default, all pins are inputs */ + tree_info = of_get_property(np, "xlnx,tri-default", NULL); + if (tree_info) + chip->gpio_dir = be32_to_cpup(tree_info); + + /* Check device node and parent device node for device width */ + chip->mmchip.gc.ngpio = 32; /* By default assume full GPIO controller */ + tree_info = of_get_property(np, "xlnx,gpio-width", NULL); + if (!tree_info) + tree_info = of_get_property(np->parent, + "xlnx,gpio-width", NULL); + if (tree_info) + chip->mmchip.gc.ngpio = be32_to_cpup(tree_info); + + spin_lock_init(&chip->gpio_lock); + + chip->mmchip.gc.direction_input = xgpio_dir_in; + chip->mmchip.gc.direction_output = xgpio_dir_out; + chip->mmchip.gc.get = xgpio_get; + chip->mmchip.gc.set = xgpio_set; + + chip->mmchip.save_regs = xgpio_save_regs; + + /* Call the OF gpio helper to setup and register the GPIO device */ + status = of_mm_gpiochip_add(np, &chip->mmchip); + if (status) { + kfree(chip); + pr_err("%s: error in probe function with status %d\n", + np->full_name, status); + return status; + } + pr_info("XGpio: %s: registered\n", np->full_name); + return 0; +} + +static struct of_device_id xgpio_of_match[] __devinitdata = { + { .compatible = "xlnx,xps-gpio-1.00.a", }, + { /* end of list */ }, +}; + +static int __init xgpio_init(void) +{ + struct device_node *np; + + for_each_matching_node(np, xgpio_of_match) + xgpio_of_probe(np); + + return 0; +} + +/* Make sure we get initialized before anyone else tries to use us */ +subsys_initcall(xgpio_init); +/* No exit call at the moment as we cannot unregister of GPIO chips */ + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION("Xilinx GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/it8761e_gpio.c b/drivers/gpio/it8761e_gpio.c deleted file mode 100644 index 48fc43c4bdd..00000000000 --- a/drivers/gpio/it8761e_gpio.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * it8761_gpio.c - GPIO interface for IT8761E Super I/O chip - * - * Author: Denis Turischev - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License 2 as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include - -#include - -#define SIO_CHIP_ID 0x8761 -#define CHIP_ID_HIGH_BYTE 0x20 -#define CHIP_ID_LOW_BYTE 0x21 - -static u8 ports[2] = { 0x2e, 0x4e }; -static u8 port; - -static DEFINE_SPINLOCK(sio_lock); - -#define GPIO_NAME "it8761-gpio" -#define GPIO_BA_HIGH_BYTE 0x60 -#define GPIO_BA_LOW_BYTE 0x61 -#define GPIO_IOSIZE 4 -#define GPIO1X_IO 0xf0 -#define GPIO2X_IO 0xf1 - -static u16 gpio_ba; - -static u8 read_reg(u8 addr, u8 port) -{ - outb(addr, port); - return inb(port + 1); -} - -static void write_reg(u8 data, u8 addr, u8 port) -{ - outb(addr, port); - outb(data, port + 1); -} - -static void enter_conf_mode(u8 port) -{ - outb(0x87, port); - outb(0x61, port); - outb(0x55, port); - outb((port == 0x2e) ? 0x55 : 0xaa, port); -} - -static void exit_conf_mode(u8 port) -{ - outb(0x2, port); - outb(0x2, port + 1); -} - -static void enter_gpio_mode(u8 port) -{ - write_reg(0x2, 0x7, port); -} - -static int it8761e_gpio_get(struct gpio_chip *gc, unsigned gpio_num) -{ - u16 reg; - u8 bit; - - bit = gpio_num % 8; - reg = (gpio_num >= 8) ? gpio_ba + 1 : gpio_ba; - - return !!(inb(reg) & (1 << bit)); -} - -static int it8761e_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num) -{ - u8 curr_dirs; - u8 io_reg, bit; - - bit = gpio_num % 8; - io_reg = (gpio_num >= 8) ? GPIO2X_IO : GPIO1X_IO; - - spin_lock(&sio_lock); - - enter_conf_mode(port); - enter_gpio_mode(port); - - curr_dirs = read_reg(io_reg, port); - - if (curr_dirs & (1 << bit)) - write_reg(curr_dirs & ~(1 << bit), io_reg, port); - - exit_conf_mode(port); - - spin_unlock(&sio_lock); - return 0; -} - -static void it8761e_gpio_set(struct gpio_chip *gc, - unsigned gpio_num, int val) -{ - u8 curr_vals, bit; - u16 reg; - - bit = gpio_num % 8; - reg = (gpio_num >= 8) ? gpio_ba + 1 : gpio_ba; - - spin_lock(&sio_lock); - - curr_vals = inb(reg); - if (val) - outb(curr_vals | (1 << bit) , reg); - else - outb(curr_vals & ~(1 << bit), reg); - - spin_unlock(&sio_lock); -} - -static int it8761e_gpio_direction_out(struct gpio_chip *gc, - unsigned gpio_num, int val) -{ - u8 curr_dirs, io_reg, bit; - - bit = gpio_num % 8; - io_reg = (gpio_num >= 8) ? GPIO2X_IO : GPIO1X_IO; - - it8761e_gpio_set(gc, gpio_num, val); - - spin_lock(&sio_lock); - - enter_conf_mode(port); - enter_gpio_mode(port); - - curr_dirs = read_reg(io_reg, port); - - if (!(curr_dirs & (1 << bit))) - write_reg(curr_dirs | (1 << bit), io_reg, port); - - exit_conf_mode(port); - - spin_unlock(&sio_lock); - return 0; -} - -static struct gpio_chip it8761e_gpio_chip = { - .label = GPIO_NAME, - .owner = THIS_MODULE, - .get = it8761e_gpio_get, - .direction_input = it8761e_gpio_direction_in, - .set = it8761e_gpio_set, - .direction_output = it8761e_gpio_direction_out, -}; - -static int __init it8761e_gpio_init(void) -{ - int i, id, err; - - /* chip and port detection */ - for (i = 0; i < ARRAY_SIZE(ports); i++) { - spin_lock(&sio_lock); - enter_conf_mode(ports[i]); - - id = (read_reg(CHIP_ID_HIGH_BYTE, ports[i]) << 8) + - read_reg(CHIP_ID_LOW_BYTE, ports[i]); - - exit_conf_mode(ports[i]); - spin_unlock(&sio_lock); - - if (id == SIO_CHIP_ID) { - port = ports[i]; - break; - } - } - - if (!port) - return -ENODEV; - - /* fetch GPIO base address */ - enter_conf_mode(port); - enter_gpio_mode(port); - gpio_ba = (read_reg(GPIO_BA_HIGH_BYTE, port) << 8) + - read_reg(GPIO_BA_LOW_BYTE, port); - exit_conf_mode(port); - - if (!request_region(gpio_ba, GPIO_IOSIZE, GPIO_NAME)) - return -EBUSY; - - it8761e_gpio_chip.base = -1; - it8761e_gpio_chip.ngpio = 16; - - err = gpiochip_add(&it8761e_gpio_chip); - if (err < 0) - goto gpiochip_add_err; - - return 0; - -gpiochip_add_err: - release_region(gpio_ba, GPIO_IOSIZE); - gpio_ba = 0; - return err; -} - -static void __exit it8761e_gpio_exit(void) -{ - if (gpio_ba) { - int ret = gpiochip_remove(&it8761e_gpio_chip); - - WARN(ret, "%s(): gpiochip_remove() failed, ret=%d\n", - __func__, ret); - - release_region(gpio_ba, GPIO_IOSIZE); - gpio_ba = 0; - } -} -module_init(it8761e_gpio_init); -module_exit(it8761e_gpio_exit); - -MODULE_AUTHOR("Denis Turischev "); -MODULE_DESCRIPTION("GPIO interface for IT8761E Super I/O chip"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/janz-ttl.c b/drivers/gpio/janz-ttl.c deleted file mode 100644 index 813ac077e5d..00000000000 --- a/drivers/gpio/janz-ttl.c +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Janz MODULbus VMOD-TTL GPIO Driver - * - * Copyright (c) 2010 Ira W. Snyder - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define DRV_NAME "janz-ttl" - -#define PORTA_DIRECTION 0x23 -#define PORTB_DIRECTION 0x2B -#define PORTC_DIRECTION 0x06 -#define PORTA_IOCTL 0x24 -#define PORTB_IOCTL 0x2C -#define PORTC_IOCTL 0x07 - -#define MASTER_INT_CTL 0x00 -#define MASTER_CONF_CTL 0x01 - -#define CONF_PAE (1 << 2) -#define CONF_PBE (1 << 7) -#define CONF_PCE (1 << 4) - -struct ttl_control_regs { - __be16 portc; - __be16 portb; - __be16 porta; - __be16 control; -}; - -struct ttl_module { - struct gpio_chip gpio; - - /* base address of registers */ - struct ttl_control_regs __iomem *regs; - - u8 portc_shadow; - u8 portb_shadow; - u8 porta_shadow; - - spinlock_t lock; -}; - -static int ttl_get_value(struct gpio_chip *gpio, unsigned offset) -{ - struct ttl_module *mod = dev_get_drvdata(gpio->dev); - u8 *shadow; - int ret; - - if (offset < 8) { - shadow = &mod->porta_shadow; - } else if (offset < 16) { - shadow = &mod->portb_shadow; - offset -= 8; - } else { - shadow = &mod->portc_shadow; - offset -= 16; - } - - spin_lock(&mod->lock); - ret = *shadow & (1 << offset); - spin_unlock(&mod->lock); - return ret; -} - -static void ttl_set_value(struct gpio_chip *gpio, unsigned offset, int value) -{ - struct ttl_module *mod = dev_get_drvdata(gpio->dev); - void __iomem *port; - u8 *shadow; - - if (offset < 8) { - port = &mod->regs->porta; - shadow = &mod->porta_shadow; - } else if (offset < 16) { - port = &mod->regs->portb; - shadow = &mod->portb_shadow; - offset -= 8; - } else { - port = &mod->regs->portc; - shadow = &mod->portc_shadow; - offset -= 16; - } - - spin_lock(&mod->lock); - if (value) - *shadow |= (1 << offset); - else - *shadow &= ~(1 << offset); - - iowrite16be(*shadow, port); - spin_unlock(&mod->lock); -} - -static void __devinit ttl_write_reg(struct ttl_module *mod, u8 reg, u16 val) -{ - iowrite16be(reg, &mod->regs->control); - iowrite16be(val, &mod->regs->control); -} - -static void __devinit ttl_setup_device(struct ttl_module *mod) -{ - /* reset the device to a known state */ - iowrite16be(0x0000, &mod->regs->control); - iowrite16be(0x0001, &mod->regs->control); - iowrite16be(0x0000, &mod->regs->control); - - /* put all ports in open-drain mode */ - ttl_write_reg(mod, PORTA_IOCTL, 0x00ff); - ttl_write_reg(mod, PORTB_IOCTL, 0x00ff); - ttl_write_reg(mod, PORTC_IOCTL, 0x000f); - - /* set all ports as outputs */ - ttl_write_reg(mod, PORTA_DIRECTION, 0x0000); - ttl_write_reg(mod, PORTB_DIRECTION, 0x0000); - ttl_write_reg(mod, PORTC_DIRECTION, 0x0000); - - /* set all ports to drive zeroes */ - iowrite16be(0x0000, &mod->regs->porta); - iowrite16be(0x0000, &mod->regs->portb); - iowrite16be(0x0000, &mod->regs->portc); - - /* enable all ports */ - ttl_write_reg(mod, MASTER_CONF_CTL, CONF_PAE | CONF_PBE | CONF_PCE); -} - -static int __devinit ttl_probe(struct platform_device *pdev) -{ - struct janz_platform_data *pdata; - struct device *dev = &pdev->dev; - struct ttl_module *mod; - struct gpio_chip *gpio; - struct resource *res; - int ret; - - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(dev, "no platform data\n"); - ret = -ENXIO; - goto out_return; - } - - mod = kzalloc(sizeof(*mod), GFP_KERNEL); - if (!mod) { - dev_err(dev, "unable to allocate private data\n"); - ret = -ENOMEM; - goto out_return; - } - - platform_set_drvdata(pdev, mod); - spin_lock_init(&mod->lock); - - /* get access to the MODULbus registers for this module */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "MODULbus registers not found\n"); - ret = -ENODEV; - goto out_free_mod; - } - - mod->regs = ioremap(res->start, resource_size(res)); - if (!mod->regs) { - dev_err(dev, "MODULbus registers not ioremap\n"); - ret = -ENOMEM; - goto out_free_mod; - } - - ttl_setup_device(mod); - - /* Initialize the GPIO data structures */ - gpio = &mod->gpio; - gpio->dev = &pdev->dev; - gpio->label = pdev->name; - gpio->get = ttl_get_value; - gpio->set = ttl_set_value; - gpio->owner = THIS_MODULE; - - /* request dynamic allocation */ - gpio->base = -1; - gpio->ngpio = 20; - - ret = gpiochip_add(gpio); - if (ret) { - dev_err(dev, "unable to add GPIO chip\n"); - goto out_iounmap_regs; - } - - dev_info(&pdev->dev, "module %d: registered GPIO device\n", - pdata->modno); - return 0; - -out_iounmap_regs: - iounmap(mod->regs); -out_free_mod: - kfree(mod); -out_return: - return ret; -} - -static int __devexit ttl_remove(struct platform_device *pdev) -{ - struct ttl_module *mod = platform_get_drvdata(pdev); - struct device *dev = &pdev->dev; - int ret; - - ret = gpiochip_remove(&mod->gpio); - if (ret) { - dev_err(dev, "unable to remove GPIO chip\n"); - return ret; - } - - iounmap(mod->regs); - kfree(mod); - return 0; -} - -static struct platform_driver ttl_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - }, - .probe = ttl_probe, - .remove = __devexit_p(ttl_remove), -}; - -static int __init ttl_init(void) -{ - return platform_driver_register(&ttl_driver); -} - -static void __exit ttl_exit(void) -{ - platform_driver_unregister(&ttl_driver); -} - -MODULE_AUTHOR("Ira W. Snyder "); -MODULE_DESCRIPTION("Janz MODULbus VMOD-TTL Driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:janz-ttl"); - -module_init(ttl_init); -module_exit(ttl_exit); diff --git a/drivers/gpio/langwell_gpio.c b/drivers/gpio/langwell_gpio.c deleted file mode 100644 index bd6571e0097..00000000000 --- a/drivers/gpio/langwell_gpio.c +++ /dev/null @@ -1,456 +0,0 @@ -/* langwell_gpio.c Moorestown platform Langwell chip GPIO driver - * Copyright (c) 2008 - 2009, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* Supports: - * Moorestown platform Langwell chip. - * Medfield platform Penwell chip. - * Whitney point. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Langwell chip has 64 pins and thus there are 2 32bit registers to control - * each feature, while Penwell chip has 96 pins for each block, and need 3 32bit - * registers to control them, so we only define the order here instead of a - * structure, to get a bit offset for a pin (use GPDR as an example): - * - * nreg = ngpio / 32; - * reg = offset / 32; - * bit = offset % 32; - * reg_addr = reg_base + GPDR * nreg * 4 + reg * 4; - * - * so the bit of reg_addr is to control pin offset's GPDR feature -*/ - -enum GPIO_REG { - GPLR = 0, /* pin level read-only */ - GPDR, /* pin direction */ - GPSR, /* pin set */ - GPCR, /* pin clear */ - GRER, /* rising edge detect */ - GFER, /* falling edge detect */ - GEDR, /* edge detect result */ -}; - -struct lnw_gpio { - struct gpio_chip chip; - void *reg_base; - spinlock_t lock; - unsigned irq_base; - struct pci_dev *pdev; -}; - -static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset, - enum GPIO_REG reg_type) -{ - struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); - unsigned nreg = chip->ngpio / 32; - u8 reg = offset / 32; - void __iomem *ptr; - - ptr = (void __iomem *)(lnw->reg_base + reg_type * nreg * 4 + reg * 4); - return ptr; -} - -static int lnw_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - void __iomem *gplr = gpio_reg(chip, offset, GPLR); - - return readl(gplr) & BIT(offset % 32); -} - -static void lnw_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - void __iomem *gpsr, *gpcr; - - if (value) { - gpsr = gpio_reg(chip, offset, GPSR); - writel(BIT(offset % 32), gpsr); - } else { - gpcr = gpio_reg(chip, offset, GPCR); - writel(BIT(offset % 32), gpcr); - } -} - -static int lnw_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); - void __iomem *gpdr = gpio_reg(chip, offset, GPDR); - u32 value; - unsigned long flags; - - if (lnw->pdev) - pm_runtime_get(&lnw->pdev->dev); - - spin_lock_irqsave(&lnw->lock, flags); - value = readl(gpdr); - value &= ~BIT(offset % 32); - writel(value, gpdr); - spin_unlock_irqrestore(&lnw->lock, flags); - - if (lnw->pdev) - pm_runtime_put(&lnw->pdev->dev); - - return 0; -} - -static int lnw_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); - void __iomem *gpdr = gpio_reg(chip, offset, GPDR); - unsigned long flags; - - lnw_gpio_set(chip, offset, value); - - if (lnw->pdev) - pm_runtime_get(&lnw->pdev->dev); - - spin_lock_irqsave(&lnw->lock, flags); - value = readl(gpdr); - value |= BIT(offset % 32); - writel(value, gpdr); - spin_unlock_irqrestore(&lnw->lock, flags); - - if (lnw->pdev) - pm_runtime_put(&lnw->pdev->dev); - - return 0; -} - -static int lnw_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); - return lnw->irq_base + offset; -} - -static int lnw_irq_type(struct irq_data *d, unsigned type) -{ - struct lnw_gpio *lnw = irq_data_get_irq_chip_data(d); - u32 gpio = d->irq - lnw->irq_base; - unsigned long flags; - u32 value; - void __iomem *grer = gpio_reg(&lnw->chip, gpio, GRER); - void __iomem *gfer = gpio_reg(&lnw->chip, gpio, GFER); - - if (gpio >= lnw->chip.ngpio) - return -EINVAL; - - if (lnw->pdev) - pm_runtime_get(&lnw->pdev->dev); - - spin_lock_irqsave(&lnw->lock, flags); - if (type & IRQ_TYPE_EDGE_RISING) - value = readl(grer) | BIT(gpio % 32); - else - value = readl(grer) & (~BIT(gpio % 32)); - writel(value, grer); - - if (type & IRQ_TYPE_EDGE_FALLING) - value = readl(gfer) | BIT(gpio % 32); - else - value = readl(gfer) & (~BIT(gpio % 32)); - writel(value, gfer); - spin_unlock_irqrestore(&lnw->lock, flags); - - if (lnw->pdev) - pm_runtime_put(&lnw->pdev->dev); - - return 0; -} - -static void lnw_irq_unmask(struct irq_data *d) -{ -} - -static void lnw_irq_mask(struct irq_data *d) -{ -} - -static struct irq_chip lnw_irqchip = { - .name = "LNW-GPIO", - .irq_mask = lnw_irq_mask, - .irq_unmask = lnw_irq_unmask, - .irq_set_type = lnw_irq_type, -}; - -static DEFINE_PCI_DEVICE_TABLE(lnw_gpio_ids) = { /* pin number */ - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080f), .driver_data = 64 }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081f), .driver_data = 96 }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081a), .driver_data = 96 }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, lnw_gpio_ids); - -static void lnw_irq_handler(unsigned irq, struct irq_desc *desc) -{ - struct irq_data *data = irq_desc_get_irq_data(desc); - struct lnw_gpio *lnw = irq_data_get_irq_handler_data(data); - struct irq_chip *chip = irq_data_get_irq_chip(data); - u32 base, gpio, mask; - unsigned long pending; - void __iomem *gedr; - - /* check GPIO controller to check which pin triggered the interrupt */ - for (base = 0; base < lnw->chip.ngpio; base += 32) { - gedr = gpio_reg(&lnw->chip, base, GEDR); - pending = readl(gedr); - while (pending) { - gpio = __ffs(pending) - 1; - mask = BIT(gpio); - pending &= ~mask; - /* Clear before handling so we can't lose an edge */ - writel(mask, gedr); - generic_handle_irq(lnw->irq_base + base + gpio); - } - } - - chip->irq_eoi(data); -} - -#ifdef CONFIG_PM -static int lnw_gpio_runtime_resume(struct device *dev) -{ - return 0; -} - -static int lnw_gpio_runtime_suspend(struct device *dev) -{ - return 0; -} - -static int lnw_gpio_runtime_idle(struct device *dev) -{ - int err = pm_schedule_suspend(dev, 500); - - if (!err) - return 0; - - return -EBUSY; -} - -#else -#define lnw_gpio_runtime_suspend NULL -#define lnw_gpio_runtime_resume NULL -#define lnw_gpio_runtime_idle NULL -#endif - -static const struct dev_pm_ops lnw_gpio_pm_ops = { - .runtime_suspend = lnw_gpio_runtime_suspend, - .runtime_resume = lnw_gpio_runtime_resume, - .runtime_idle = lnw_gpio_runtime_idle, -}; - -static int __devinit lnw_gpio_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - void *base; - int i; - resource_size_t start, len; - struct lnw_gpio *lnw; - u32 irq_base; - u32 gpio_base; - int retval = 0; - - retval = pci_enable_device(pdev); - if (retval) - goto done; - - retval = pci_request_regions(pdev, "langwell_gpio"); - if (retval) { - dev_err(&pdev->dev, "error requesting resources\n"); - goto err2; - } - /* get the irq_base from bar1 */ - start = pci_resource_start(pdev, 1); - len = pci_resource_len(pdev, 1); - base = ioremap_nocache(start, len); - if (!base) { - dev_err(&pdev->dev, "error mapping bar1\n"); - goto err3; - } - irq_base = *(u32 *)base; - gpio_base = *((u32 *)base + 1); - /* release the IO mapping, since we already get the info from bar1 */ - iounmap(base); - /* get the register base from bar0 */ - start = pci_resource_start(pdev, 0); - len = pci_resource_len(pdev, 0); - base = ioremap_nocache(start, len); - if (!base) { - dev_err(&pdev->dev, "error mapping bar0\n"); - retval = -EFAULT; - goto err3; - } - - lnw = kzalloc(sizeof(struct lnw_gpio), GFP_KERNEL); - if (!lnw) { - dev_err(&pdev->dev, "can't allocate langwell_gpio chip data\n"); - retval = -ENOMEM; - goto err4; - } - lnw->reg_base = base; - lnw->irq_base = irq_base; - lnw->chip.label = dev_name(&pdev->dev); - lnw->chip.direction_input = lnw_gpio_direction_input; - lnw->chip.direction_output = lnw_gpio_direction_output; - lnw->chip.get = lnw_gpio_get; - lnw->chip.set = lnw_gpio_set; - lnw->chip.to_irq = lnw_gpio_to_irq; - lnw->chip.base = gpio_base; - lnw->chip.ngpio = id->driver_data; - lnw->chip.can_sleep = 0; - lnw->pdev = pdev; - pci_set_drvdata(pdev, lnw); - retval = gpiochip_add(&lnw->chip); - if (retval) { - dev_err(&pdev->dev, "langwell gpiochip_add error %d\n", retval); - goto err5; - } - irq_set_handler_data(pdev->irq, lnw); - irq_set_chained_handler(pdev->irq, lnw_irq_handler); - for (i = 0; i < lnw->chip.ngpio; i++) { - irq_set_chip_and_handler_name(i + lnw->irq_base, &lnw_irqchip, - handle_simple_irq, "demux"); - irq_set_chip_data(i + lnw->irq_base, lnw); - } - - spin_lock_init(&lnw->lock); - - pm_runtime_put_noidle(&pdev->dev); - pm_runtime_allow(&pdev->dev); - - goto done; -err5: - kfree(lnw); -err4: - iounmap(base); -err3: - pci_release_regions(pdev); -err2: - pci_disable_device(pdev); -done: - return retval; -} - -static struct pci_driver lnw_gpio_driver = { - .name = "langwell_gpio", - .id_table = lnw_gpio_ids, - .probe = lnw_gpio_probe, - .driver = { - .pm = &lnw_gpio_pm_ops, - }, -}; - - -static int __devinit wp_gpio_probe(struct platform_device *pdev) -{ - struct lnw_gpio *lnw; - struct gpio_chip *gc; - struct resource *rc; - int retval = 0; - - rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!rc) - return -EINVAL; - - lnw = kzalloc(sizeof(struct lnw_gpio), GFP_KERNEL); - if (!lnw) { - dev_err(&pdev->dev, - "can't allocate whitneypoint_gpio chip data\n"); - return -ENOMEM; - } - lnw->reg_base = ioremap_nocache(rc->start, resource_size(rc)); - if (lnw->reg_base == NULL) { - retval = -EINVAL; - goto err_kmalloc; - } - spin_lock_init(&lnw->lock); - gc = &lnw->chip; - gc->label = dev_name(&pdev->dev); - gc->owner = THIS_MODULE; - gc->direction_input = lnw_gpio_direction_input; - gc->direction_output = lnw_gpio_direction_output; - gc->get = lnw_gpio_get; - gc->set = lnw_gpio_set; - gc->to_irq = NULL; - gc->base = 0; - gc->ngpio = 64; - gc->can_sleep = 0; - retval = gpiochip_add(gc); - if (retval) { - dev_err(&pdev->dev, "whitneypoint gpiochip_add error %d\n", - retval); - goto err_ioremap; - } - platform_set_drvdata(pdev, lnw); - return 0; -err_ioremap: - iounmap(lnw->reg_base); -err_kmalloc: - kfree(lnw); - return retval; -} - -static int __devexit wp_gpio_remove(struct platform_device *pdev) -{ - struct lnw_gpio *lnw = platform_get_drvdata(pdev); - int err; - err = gpiochip_remove(&lnw->chip); - if (err) - dev_err(&pdev->dev, "failed to remove gpio_chip.\n"); - iounmap(lnw->reg_base); - kfree(lnw); - platform_set_drvdata(pdev, NULL); - return 0; -} - -static struct platform_driver wp_gpio_driver = { - .probe = wp_gpio_probe, - .remove = __devexit_p(wp_gpio_remove), - .driver = { - .name = "wp_gpio", - .owner = THIS_MODULE, - }, -}; - -static int __init lnw_gpio_init(void) -{ - int ret; - ret = pci_register_driver(&lnw_gpio_driver); - if (ret < 0) - return ret; - ret = platform_driver_register(&wp_gpio_driver); - if (ret < 0) - pci_unregister_driver(&lnw_gpio_driver); - return ret; -} - -device_initcall(lnw_gpio_init); diff --git a/drivers/gpio/max7300.c b/drivers/gpio/max7300.c deleted file mode 100644 index 962f661c18c..00000000000 --- a/drivers/gpio/max7300.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * drivers/gpio/max7300.c - * - * Copyright (C) 2009 Wolfram Sang, Pengutronix - * - * 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. - * - * Check max730x.c for further details. - */ - -#include -#include -#include -#include -#include -#include -#include - -static int max7300_i2c_write(struct device *dev, unsigned int reg, - unsigned int val) -{ - struct i2c_client *client = to_i2c_client(dev); - - return i2c_smbus_write_byte_data(client, reg, val); -} - -static int max7300_i2c_read(struct device *dev, unsigned int reg) -{ - struct i2c_client *client = to_i2c_client(dev); - - return i2c_smbus_read_byte_data(client, reg); -} - -static int __devinit max7300_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct max7301 *ts; - int ret; - - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - ts = kzalloc(sizeof(struct max7301), GFP_KERNEL); - if (!ts) - return -ENOMEM; - - ts->read = max7300_i2c_read; - ts->write = max7300_i2c_write; - ts->dev = &client->dev; - - ret = __max730x_probe(ts); - if (ret) - kfree(ts); - return ret; -} - -static int __devexit max7300_remove(struct i2c_client *client) -{ - return __max730x_remove(&client->dev); -} - -static const struct i2c_device_id max7300_id[] = { - { "max7300", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, max7300_id); - -static struct i2c_driver max7300_driver = { - .driver = { - .name = "max7300", - .owner = THIS_MODULE, - }, - .probe = max7300_probe, - .remove = __devexit_p(max7300_remove), - .id_table = max7300_id, -}; - -static int __init max7300_init(void) -{ - return i2c_add_driver(&max7300_driver); -} -subsys_initcall(max7300_init); - -static void __exit max7300_exit(void) -{ - i2c_del_driver(&max7300_driver); -} -module_exit(max7300_exit); - -MODULE_AUTHOR("Wolfram Sang"); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("MAX7300 GPIO-Expander"); diff --git a/drivers/gpio/max7301.c b/drivers/gpio/max7301.c deleted file mode 100644 index 92a100ddef6..00000000000 --- a/drivers/gpio/max7301.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * drivers/gpio/max7301.c - * - * Copyright (C) 2006 Juergen Beisert, Pengutronix - * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix - * Copyright (C) 2009 Wolfram Sang, Pengutronix - * - * 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. - * - * Check max730x.c for further details. - */ - -#include -#include -#include -#include -#include -#include -#include - -/* A write to the MAX7301 means one message with one transfer */ -static int max7301_spi_write(struct device *dev, unsigned int reg, - unsigned int val) -{ - struct spi_device *spi = to_spi_device(dev); - u16 word = ((reg & 0x7F) << 8) | (val & 0xFF); - - return spi_write(spi, (const u8 *)&word, sizeof(word)); -} - -/* A read from the MAX7301 means two transfers; here, one message each */ - -static int max7301_spi_read(struct device *dev, unsigned int reg) -{ - int ret; - u16 word; - struct spi_device *spi = to_spi_device(dev); - - word = 0x8000 | (reg << 8); - ret = spi_write(spi, (const u8 *)&word, sizeof(word)); - if (ret) - return ret; - /* - * This relies on the fact, that a transfer with NULL tx_buf shifts out - * zero bytes (=NOOP for MAX7301) - */ - ret = spi_read(spi, (u8 *)&word, sizeof(word)); - if (ret) - return ret; - return word & 0xff; -} - -static int __devinit max7301_probe(struct spi_device *spi) -{ - struct max7301 *ts; - int ret; - - /* bits_per_word cannot be configured in platform data */ - spi->bits_per_word = 16; - ret = spi_setup(spi); - if (ret < 0) - return ret; - - ts = kzalloc(sizeof(struct max7301), GFP_KERNEL); - if (!ts) - return -ENOMEM; - - ts->read = max7301_spi_read; - ts->write = max7301_spi_write; - ts->dev = &spi->dev; - - ret = __max730x_probe(ts); - if (ret) - kfree(ts); - return ret; -} - -static int __devexit max7301_remove(struct spi_device *spi) -{ - return __max730x_remove(&spi->dev); -} - -static const struct spi_device_id max7301_id[] = { - { "max7301", 0 }, - { } -}; -MODULE_DEVICE_TABLE(spi, max7301_id); - -static struct spi_driver max7301_driver = { - .driver = { - .name = "max7301", - .owner = THIS_MODULE, - }, - .probe = max7301_probe, - .remove = __devexit_p(max7301_remove), - .id_table = max7301_id, -}; - -static int __init max7301_init(void) -{ - return spi_register_driver(&max7301_driver); -} -/* register after spi postcore initcall and before - * subsys initcalls that may rely on these GPIOs - */ -subsys_initcall(max7301_init); - -static void __exit max7301_exit(void) -{ - spi_unregister_driver(&max7301_driver); -} -module_exit(max7301_exit); - -MODULE_AUTHOR("Juergen Beisert, Wolfram Sang"); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("MAX7301 GPIO-Expander"); diff --git a/drivers/gpio/max730x.c b/drivers/gpio/max730x.c deleted file mode 100644 index 94ce773f95f..00000000000 --- a/drivers/gpio/max730x.c +++ /dev/null @@ -1,257 +0,0 @@ -/** - * drivers/gpio/max7301.c - * - * Copyright (C) 2006 Juergen Beisert, Pengutronix - * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix - * Copyright (C) 2009 Wolfram Sang, Pengutronix - * - * 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. - * - * The Maxim MAX7300/1 device is an I2C/SPI driven GPIO expander. There are - * 28 GPIOs. 8 of them can trigger an interrupt. See datasheet for more - * details - * Note: - * - DIN must be stable at the rising edge of clock. - * - when writing: - * - always clock in 16 clocks at once - * - at DIN: D15 first, D0 last - * - D0..D7 = databyte, D8..D14 = commandbyte - * - D15 = low -> write command - * - when reading - * - always clock in 16 clocks at once - * - at DIN: D15 first, D0 last - * - D0..D7 = dummy, D8..D14 = register address - * - D15 = high -> read command - * - raise CS and assert it again - * - always clock in 16 clocks at once - * - at DOUT: D15 first, D0 last - * - D0..D7 contains the data from the first cycle - * - * The driver exports a standard gpiochip interface - */ - -#include -#include -#include -#include -#include -#include -#include - -/* - * Pin configurations, see MAX7301 datasheet page 6 - */ -#define PIN_CONFIG_MASK 0x03 -#define PIN_CONFIG_IN_PULLUP 0x03 -#define PIN_CONFIG_IN_WO_PULLUP 0x02 -#define PIN_CONFIG_OUT 0x01 - -#define PIN_NUMBER 28 - -static int max7301_direction_input(struct gpio_chip *chip, unsigned offset) -{ - struct max7301 *ts = container_of(chip, struct max7301, chip); - u8 *config; - u8 offset_bits, pin_config; - int ret; - - /* First 4 pins are unused in the controller */ - offset += 4; - offset_bits = (offset & 3) << 1; - - config = &ts->port_config[offset >> 2]; - - if (ts->input_pullup_active & BIT(offset)) - pin_config = PIN_CONFIG_IN_PULLUP; - else - pin_config = PIN_CONFIG_IN_WO_PULLUP; - - mutex_lock(&ts->lock); - - *config = (*config & ~(PIN_CONFIG_MASK << offset_bits)) - | (pin_config << offset_bits); - - ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config); - - mutex_unlock(&ts->lock); - - return ret; -} - -static int __max7301_set(struct max7301 *ts, unsigned offset, int value) -{ - if (value) { - ts->out_level |= 1 << offset; - return ts->write(ts->dev, 0x20 + offset, 0x01); - } else { - ts->out_level &= ~(1 << offset); - return ts->write(ts->dev, 0x20 + offset, 0x00); - } -} - -static int max7301_direction_output(struct gpio_chip *chip, unsigned offset, - int value) -{ - struct max7301 *ts = container_of(chip, struct max7301, chip); - u8 *config; - u8 offset_bits; - int ret; - - /* First 4 pins are unused in the controller */ - offset += 4; - offset_bits = (offset & 3) << 1; - - config = &ts->port_config[offset >> 2]; - - mutex_lock(&ts->lock); - - *config = (*config & ~(PIN_CONFIG_MASK << offset_bits)) - | (PIN_CONFIG_OUT << offset_bits); - - ret = __max7301_set(ts, offset, value); - - if (!ret) - ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config); - - mutex_unlock(&ts->lock); - - return ret; -} - -static int max7301_get(struct gpio_chip *chip, unsigned offset) -{ - struct max7301 *ts = container_of(chip, struct max7301, chip); - int config, level = -EINVAL; - - /* First 4 pins are unused in the controller */ - offset += 4; - - mutex_lock(&ts->lock); - - config = (ts->port_config[offset >> 2] >> ((offset & 3) << 1)) - & PIN_CONFIG_MASK; - - switch (config) { - case PIN_CONFIG_OUT: - /* Output: return cached level */ - level = !!(ts->out_level & (1 << offset)); - break; - case PIN_CONFIG_IN_WO_PULLUP: - case PIN_CONFIG_IN_PULLUP: - /* Input: read out */ - level = ts->read(ts->dev, 0x20 + offset) & 0x01; - } - mutex_unlock(&ts->lock); - - return level; -} - -static void max7301_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct max7301 *ts = container_of(chip, struct max7301, chip); - - /* First 4 pins are unused in the controller */ - offset += 4; - - mutex_lock(&ts->lock); - - __max7301_set(ts, offset, value); - - mutex_unlock(&ts->lock); -} - -int __devinit __max730x_probe(struct max7301 *ts) -{ - struct device *dev = ts->dev; - struct max7301_platform_data *pdata; - int i, ret; - - pdata = dev->platform_data; - if (!pdata || !pdata->base) { - dev_err(dev, "incorrect or missing platform data\n"); - return -EINVAL; - } - - mutex_init(&ts->lock); - dev_set_drvdata(dev, ts); - - /* Power up the chip and disable IRQ output */ - ts->write(dev, 0x04, 0x01); - - ts->input_pullup_active = pdata->input_pullup_active; - ts->chip.label = dev->driver->name; - - ts->chip.direction_input = max7301_direction_input; - ts->chip.get = max7301_get; - ts->chip.direction_output = max7301_direction_output; - ts->chip.set = max7301_set; - - ts->chip.base = pdata->base; - ts->chip.ngpio = PIN_NUMBER; - ts->chip.can_sleep = 1; - ts->chip.dev = dev; - ts->chip.owner = THIS_MODULE; - - /* - * initialize pullups according to platform data and cache the - * register values for later use. - */ - for (i = 1; i < 8; i++) { - int j; - /* - * initialize port_config with "0xAA", which means - * input with internal pullup disabled. This is needed - * to avoid writing zeros (in the inner for loop), - * which is not allowed according to the datasheet. - */ - ts->port_config[i] = 0xAA; - for (j = 0; j < 4; j++) { - int offset = (i - 1) * 4 + j; - ret = max7301_direction_input(&ts->chip, offset); - if (ret) - goto exit_destroy; - } - } - - ret = gpiochip_add(&ts->chip); - if (ret) - goto exit_destroy; - - return ret; - -exit_destroy: - dev_set_drvdata(dev, NULL); - mutex_destroy(&ts->lock); - return ret; -} -EXPORT_SYMBOL_GPL(__max730x_probe); - -int __devexit __max730x_remove(struct device *dev) -{ - struct max7301 *ts = dev_get_drvdata(dev); - int ret; - - if (ts == NULL) - return -ENODEV; - - dev_set_drvdata(dev, NULL); - - /* Power down the chip and disable IRQ output */ - ts->write(dev, 0x04, 0x00); - - ret = gpiochip_remove(&ts->chip); - if (!ret) { - mutex_destroy(&ts->lock); - kfree(ts); - } else - dev_err(dev, "Failed to remove GPIO controller: %d\n", ret); - - return ret; -} -EXPORT_SYMBOL_GPL(__max730x_remove); - -MODULE_AUTHOR("Juergen Beisert, Wolfram Sang"); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("MAX730x GPIO-Expanders, generic parts"); diff --git a/drivers/gpio/max732x.c b/drivers/gpio/max732x.c deleted file mode 100644 index ad6951edc16..00000000000 --- a/drivers/gpio/max732x.c +++ /dev/null @@ -1,714 +0,0 @@ -/* - * max732x.c - I2C Port Expander with 8/16 I/O - * - * Copyright (C) 2007 Marvell International Ltd. - * Copyright (C) 2008 Jack Ren - * Copyright (C) 2008 Eric Miao - * - * Derived from drivers/gpio/pca953x.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/* - * Each port of MAX732x (including MAX7319) falls into one of the - * following three types: - * - * - Push Pull Output - * - Input - * - Open Drain I/O - * - * designated by 'O', 'I' and 'P' individually according to MAXIM's - * datasheets. 'I' and 'P' ports are interrupt capables, some with - * a dedicated interrupt mask. - * - * There are two groups of I/O ports, each group usually includes - * up to 8 I/O ports, and is accessed by a specific I2C address: - * - * - Group A : by I2C address 0b'110xxxx - * - Group B : by I2C address 0b'101xxxx - * - * where 'xxxx' is decided by the connections of pin AD2/AD0. The - * address used also affects the initial state of output signals. - * - * Within each group of ports, there are five known combinations of - * I/O ports: 4I4O, 4P4O, 8I, 8P, 8O, see the definitions below for - * the detailed organization of these ports. Only Goup A is interrupt - * capable. - * - * GPIO numbers start from 'gpio_base + 0' to 'gpio_base + 8/16', - * and GPIOs from GROUP_A are numbered before those from GROUP_B - * (if there are two groups). - * - * NOTE: MAX7328/MAX7329 are drop-in replacements for PCF8574/a, so - * they are not supported by this driver. - */ - -#define PORT_NONE 0x0 /* '/' No Port */ -#define PORT_OUTPUT 0x1 /* 'O' Push-Pull, Output Only */ -#define PORT_INPUT 0x2 /* 'I' Input Only */ -#define PORT_OPENDRAIN 0x3 /* 'P' Open-Drain, I/O */ - -#define IO_4I4O 0x5AA5 /* O7 O6 I5 I4 I3 I2 O1 O0 */ -#define IO_4P4O 0x5FF5 /* O7 O6 P5 P4 P3 P2 O1 O0 */ -#define IO_8I 0xAAAA /* I7 I6 I5 I4 I3 I2 I1 I0 */ -#define IO_8P 0xFFFF /* P7 P6 P5 P4 P3 P2 P1 P0 */ -#define IO_8O 0x5555 /* O7 O6 O5 O4 O3 O2 O1 O0 */ - -#define GROUP_A(x) ((x) & 0xffff) /* I2C Addr: 0b'110xxxx */ -#define GROUP_B(x) ((x) << 16) /* I2C Addr: 0b'101xxxx */ - -#define INT_NONE 0x0 /* No interrupt capability */ -#define INT_NO_MASK 0x1 /* Has interrupts, no mask */ -#define INT_INDEP_MASK 0x2 /* Has interrupts, independent mask */ -#define INT_MERGED_MASK 0x3 /* Has interrupts, merged mask */ - -#define INT_CAPS(x) (((uint64_t)(x)) << 32) - -enum { - MAX7319, - MAX7320, - MAX7321, - MAX7322, - MAX7323, - MAX7324, - MAX7325, - MAX7326, - MAX7327, -}; - -static uint64_t max732x_features[] = { - [MAX7319] = GROUP_A(IO_8I) | INT_CAPS(INT_MERGED_MASK), - [MAX7320] = GROUP_B(IO_8O), - [MAX7321] = GROUP_A(IO_8P) | INT_CAPS(INT_NO_MASK), - [MAX7322] = GROUP_A(IO_4I4O) | INT_CAPS(INT_MERGED_MASK), - [MAX7323] = GROUP_A(IO_4P4O) | INT_CAPS(INT_INDEP_MASK), - [MAX7324] = GROUP_A(IO_8I) | GROUP_B(IO_8O) | INT_CAPS(INT_MERGED_MASK), - [MAX7325] = GROUP_A(IO_8P) | GROUP_B(IO_8O) | INT_CAPS(INT_NO_MASK), - [MAX7326] = GROUP_A(IO_4I4O) | GROUP_B(IO_8O) | INT_CAPS(INT_MERGED_MASK), - [MAX7327] = GROUP_A(IO_4P4O) | GROUP_B(IO_8O) | INT_CAPS(INT_NO_MASK), -}; - -static const struct i2c_device_id max732x_id[] = { - { "max7319", MAX7319 }, - { "max7320", MAX7320 }, - { "max7321", MAX7321 }, - { "max7322", MAX7322 }, - { "max7323", MAX7323 }, - { "max7324", MAX7324 }, - { "max7325", MAX7325 }, - { "max7326", MAX7326 }, - { "max7327", MAX7327 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, max732x_id); - -struct max732x_chip { - struct gpio_chip gpio_chip; - - struct i2c_client *client; /* "main" client */ - struct i2c_client *client_dummy; - struct i2c_client *client_group_a; - struct i2c_client *client_group_b; - - unsigned int mask_group_a; - unsigned int dir_input; - unsigned int dir_output; - - struct mutex lock; - uint8_t reg_out[2]; - -#ifdef CONFIG_GPIO_MAX732X_IRQ - struct mutex irq_lock; - int irq_base; - uint8_t irq_mask; - uint8_t irq_mask_cur; - uint8_t irq_trig_raise; - uint8_t irq_trig_fall; - uint8_t irq_features; -#endif -}; - -static int max732x_writeb(struct max732x_chip *chip, int group_a, uint8_t val) -{ - struct i2c_client *client; - int ret; - - client = group_a ? chip->client_group_a : chip->client_group_b; - ret = i2c_smbus_write_byte(client, val); - if (ret < 0) { - dev_err(&client->dev, "failed writing\n"); - return ret; - } - - return 0; -} - -static int max732x_readb(struct max732x_chip *chip, int group_a, uint8_t *val) -{ - struct i2c_client *client; - int ret; - - client = group_a ? chip->client_group_a : chip->client_group_b; - ret = i2c_smbus_read_byte(client); - if (ret < 0) { - dev_err(&client->dev, "failed reading\n"); - return ret; - } - - *val = (uint8_t)ret; - return 0; -} - -static inline int is_group_a(struct max732x_chip *chip, unsigned off) -{ - return (1u << off) & chip->mask_group_a; -} - -static int max732x_gpio_get_value(struct gpio_chip *gc, unsigned off) -{ - struct max732x_chip *chip; - uint8_t reg_val; - int ret; - - chip = container_of(gc, struct max732x_chip, gpio_chip); - - ret = max732x_readb(chip, is_group_a(chip, off), ®_val); - if (ret < 0) - return 0; - - return reg_val & (1u << (off & 0x7)); -} - -static void max732x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) -{ - struct max732x_chip *chip; - uint8_t reg_out, mask = 1u << (off & 0x7); - int ret; - - chip = container_of(gc, struct max732x_chip, gpio_chip); - - mutex_lock(&chip->lock); - - reg_out = (off > 7) ? chip->reg_out[1] : chip->reg_out[0]; - reg_out = (val) ? reg_out | mask : reg_out & ~mask; - - ret = max732x_writeb(chip, is_group_a(chip, off), reg_out); - if (ret < 0) - goto out; - - /* update the shadow register then */ - if (off > 7) - chip->reg_out[1] = reg_out; - else - chip->reg_out[0] = reg_out; -out: - mutex_unlock(&chip->lock); -} - -static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off) -{ - struct max732x_chip *chip; - unsigned int mask = 1u << off; - - chip = container_of(gc, struct max732x_chip, gpio_chip); - - if ((mask & chip->dir_input) == 0) { - dev_dbg(&chip->client->dev, "%s port %d is output only\n", - chip->client->name, off); - return -EACCES; - } - - /* - * Open-drain pins must be set to high impedance (which is - * equivalent to output-high) to be turned into an input. - */ - if ((mask & chip->dir_output)) - max732x_gpio_set_value(gc, off, 1); - - return 0; -} - -static int max732x_gpio_direction_output(struct gpio_chip *gc, - unsigned off, int val) -{ - struct max732x_chip *chip; - unsigned int mask = 1u << off; - - chip = container_of(gc, struct max732x_chip, gpio_chip); - - if ((mask & chip->dir_output) == 0) { - dev_dbg(&chip->client->dev, "%s port %d is input only\n", - chip->client->name, off); - return -EACCES; - } - - max732x_gpio_set_value(gc, off, val); - return 0; -} - -#ifdef CONFIG_GPIO_MAX732X_IRQ -static int max732x_writew(struct max732x_chip *chip, uint16_t val) -{ - int ret; - - val = cpu_to_le16(val); - - ret = i2c_master_send(chip->client_group_a, (char *)&val, 2); - if (ret < 0) { - dev_err(&chip->client_group_a->dev, "failed writing\n"); - return ret; - } - - return 0; -} - -static int max732x_readw(struct max732x_chip *chip, uint16_t *val) -{ - int ret; - - ret = i2c_master_recv(chip->client_group_a, (char *)val, 2); - if (ret < 0) { - dev_err(&chip->client_group_a->dev, "failed reading\n"); - return ret; - } - - *val = le16_to_cpu(*val); - return 0; -} - -static void max732x_irq_update_mask(struct max732x_chip *chip) -{ - uint16_t msg; - - if (chip->irq_mask == chip->irq_mask_cur) - return; - - chip->irq_mask = chip->irq_mask_cur; - - if (chip->irq_features == INT_NO_MASK) - return; - - mutex_lock(&chip->lock); - - switch (chip->irq_features) { - case INT_INDEP_MASK: - msg = (chip->irq_mask << 8) | chip->reg_out[0]; - max732x_writew(chip, msg); - break; - - case INT_MERGED_MASK: - msg = chip->irq_mask | chip->reg_out[0]; - max732x_writeb(chip, 1, (uint8_t)msg); - break; - } - - mutex_unlock(&chip->lock); -} - -static int max732x_gpio_to_irq(struct gpio_chip *gc, unsigned off) -{ - struct max732x_chip *chip; - - chip = container_of(gc, struct max732x_chip, gpio_chip); - return chip->irq_base + off; -} - -static void max732x_irq_mask(struct irq_data *d) -{ - struct max732x_chip *chip = irq_data_get_irq_chip_data(d); - - chip->irq_mask_cur &= ~(1 << (d->irq - chip->irq_base)); -} - -static void max732x_irq_unmask(struct irq_data *d) -{ - struct max732x_chip *chip = irq_data_get_irq_chip_data(d); - - chip->irq_mask_cur |= 1 << (d->irq - chip->irq_base); -} - -static void max732x_irq_bus_lock(struct irq_data *d) -{ - struct max732x_chip *chip = irq_data_get_irq_chip_data(d); - - mutex_lock(&chip->irq_lock); - chip->irq_mask_cur = chip->irq_mask; -} - -static void max732x_irq_bus_sync_unlock(struct irq_data *d) -{ - struct max732x_chip *chip = irq_data_get_irq_chip_data(d); - - max732x_irq_update_mask(chip); - mutex_unlock(&chip->irq_lock); -} - -static int max732x_irq_set_type(struct irq_data *d, unsigned int type) -{ - struct max732x_chip *chip = irq_data_get_irq_chip_data(d); - uint16_t off = d->irq - chip->irq_base; - uint16_t mask = 1 << off; - - if (!(mask & chip->dir_input)) { - dev_dbg(&chip->client->dev, "%s port %d is output only\n", - chip->client->name, off); - return -EACCES; - } - - if (!(type & IRQ_TYPE_EDGE_BOTH)) { - dev_err(&chip->client->dev, "irq %d: unsupported type %d\n", - d->irq, type); - return -EINVAL; - } - - if (type & IRQ_TYPE_EDGE_FALLING) - chip->irq_trig_fall |= mask; - else - chip->irq_trig_fall &= ~mask; - - if (type & IRQ_TYPE_EDGE_RISING) - chip->irq_trig_raise |= mask; - else - chip->irq_trig_raise &= ~mask; - - return max732x_gpio_direction_input(&chip->gpio_chip, off); -} - -static struct irq_chip max732x_irq_chip = { - .name = "max732x", - .irq_mask = max732x_irq_mask, - .irq_unmask = max732x_irq_unmask, - .irq_bus_lock = max732x_irq_bus_lock, - .irq_bus_sync_unlock = max732x_irq_bus_sync_unlock, - .irq_set_type = max732x_irq_set_type, -}; - -static uint8_t max732x_irq_pending(struct max732x_chip *chip) -{ - uint8_t cur_stat; - uint8_t old_stat; - uint8_t trigger; - uint8_t pending; - uint16_t status; - int ret; - - ret = max732x_readw(chip, &status); - if (ret) - return 0; - - trigger = status >> 8; - trigger &= chip->irq_mask; - - if (!trigger) - return 0; - - cur_stat = status & 0xFF; - cur_stat &= chip->irq_mask; - - old_stat = cur_stat ^ trigger; - - pending = (old_stat & chip->irq_trig_fall) | - (cur_stat & chip->irq_trig_raise); - pending &= trigger; - - return pending; -} - -static irqreturn_t max732x_irq_handler(int irq, void *devid) -{ - struct max732x_chip *chip = devid; - uint8_t pending; - uint8_t level; - - pending = max732x_irq_pending(chip); - - if (!pending) - return IRQ_HANDLED; - - do { - level = __ffs(pending); - handle_nested_irq(level + chip->irq_base); - - pending &= ~(1 << level); - } while (pending); - - return IRQ_HANDLED; -} - -static int max732x_irq_setup(struct max732x_chip *chip, - const struct i2c_device_id *id) -{ - struct i2c_client *client = chip->client; - struct max732x_platform_data *pdata = client->dev.platform_data; - int has_irq = max732x_features[id->driver_data] >> 32; - int ret; - - if (pdata->irq_base && has_irq != INT_NONE) { - int lvl; - - chip->irq_base = pdata->irq_base; - chip->irq_features = has_irq; - mutex_init(&chip->irq_lock); - - for (lvl = 0; lvl < chip->gpio_chip.ngpio; lvl++) { - int irq = lvl + chip->irq_base; - - if (!(chip->dir_input & (1 << lvl))) - continue; - - irq_set_chip_data(irq, chip); - irq_set_chip_and_handler(irq, &max732x_irq_chip, - handle_edge_irq); - irq_set_nested_thread(irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); -#else - irq_set_noprobe(irq); -#endif - } - - ret = request_threaded_irq(client->irq, - NULL, - max732x_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(&client->dev), chip); - if (ret) { - dev_err(&client->dev, "failed to request irq %d\n", - client->irq); - goto out_failed; - } - - chip->gpio_chip.to_irq = max732x_gpio_to_irq; - } - - return 0; - -out_failed: - chip->irq_base = 0; - return ret; -} - -static void max732x_irq_teardown(struct max732x_chip *chip) -{ - if (chip->irq_base) - free_irq(chip->client->irq, chip); -} -#else /* CONFIG_GPIO_MAX732X_IRQ */ -static int max732x_irq_setup(struct max732x_chip *chip, - const struct i2c_device_id *id) -{ - struct i2c_client *client = chip->client; - struct max732x_platform_data *pdata = client->dev.platform_data; - int has_irq = max732x_features[id->driver_data] >> 32; - - if (pdata->irq_base && has_irq != INT_NONE) - dev_warn(&client->dev, "interrupt support not compiled in\n"); - - return 0; -} - -static void max732x_irq_teardown(struct max732x_chip *chip) -{ -} -#endif - -static int __devinit max732x_setup_gpio(struct max732x_chip *chip, - const struct i2c_device_id *id, - unsigned gpio_start) -{ - struct gpio_chip *gc = &chip->gpio_chip; - uint32_t id_data = (uint32_t)max732x_features[id->driver_data]; - int i, port = 0; - - for (i = 0; i < 16; i++, id_data >>= 2) { - unsigned int mask = 1 << port; - - switch (id_data & 0x3) { - case PORT_OUTPUT: - chip->dir_output |= mask; - break; - case PORT_INPUT: - chip->dir_input |= mask; - break; - case PORT_OPENDRAIN: - chip->dir_output |= mask; - chip->dir_input |= mask; - break; - default: - continue; - } - - if (i < 8) - chip->mask_group_a |= mask; - port++; - } - - if (chip->dir_input) - gc->direction_input = max732x_gpio_direction_input; - if (chip->dir_output) { - gc->direction_output = max732x_gpio_direction_output; - gc->set = max732x_gpio_set_value; - } - gc->get = max732x_gpio_get_value; - gc->can_sleep = 1; - - gc->base = gpio_start; - gc->ngpio = port; - gc->label = chip->client->name; - gc->owner = THIS_MODULE; - - return port; -} - -static int __devinit max732x_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct max732x_platform_data *pdata; - struct max732x_chip *chip; - struct i2c_client *c; - uint16_t addr_a, addr_b; - int ret, nr_port; - - pdata = client->dev.platform_data; - if (pdata == NULL) { - dev_dbg(&client->dev, "no platform data\n"); - return -EINVAL; - } - - chip = kzalloc(sizeof(struct max732x_chip), GFP_KERNEL); - if (chip == NULL) - return -ENOMEM; - chip->client = client; - - nr_port = max732x_setup_gpio(chip, id, pdata->gpio_base); - - addr_a = (client->addr & 0x0f) | 0x60; - addr_b = (client->addr & 0x0f) | 0x50; - - switch (client->addr & 0x70) { - case 0x60: - chip->client_group_a = client; - if (nr_port > 8) { - c = i2c_new_dummy(client->adapter, addr_b); - chip->client_group_b = chip->client_dummy = c; - } - break; - case 0x50: - chip->client_group_b = client; - if (nr_port > 8) { - c = i2c_new_dummy(client->adapter, addr_a); - chip->client_group_a = chip->client_dummy = c; - } - break; - default: - dev_err(&client->dev, "invalid I2C address specified %02x\n", - client->addr); - ret = -EINVAL; - goto out_failed; - } - - mutex_init(&chip->lock); - - max732x_readb(chip, is_group_a(chip, 0), &chip->reg_out[0]); - if (nr_port > 8) - max732x_readb(chip, is_group_a(chip, 8), &chip->reg_out[1]); - - ret = max732x_irq_setup(chip, id); - if (ret) - goto out_failed; - - ret = gpiochip_add(&chip->gpio_chip); - if (ret) - goto out_failed; - - if (pdata->setup) { - ret = pdata->setup(client, chip->gpio_chip.base, - chip->gpio_chip.ngpio, pdata->context); - if (ret < 0) - dev_warn(&client->dev, "setup failed, %d\n", ret); - } - - i2c_set_clientdata(client, chip); - return 0; - -out_failed: - max732x_irq_teardown(chip); - kfree(chip); - return ret; -} - -static int __devexit max732x_remove(struct i2c_client *client) -{ - struct max732x_platform_data *pdata = client->dev.platform_data; - struct max732x_chip *chip = i2c_get_clientdata(client); - int ret; - - if (pdata->teardown) { - ret = pdata->teardown(client, chip->gpio_chip.base, - chip->gpio_chip.ngpio, pdata->context); - if (ret < 0) { - dev_err(&client->dev, "%s failed, %d\n", - "teardown", ret); - return ret; - } - } - - ret = gpiochip_remove(&chip->gpio_chip); - if (ret) { - dev_err(&client->dev, "%s failed, %d\n", - "gpiochip_remove()", ret); - return ret; - } - - max732x_irq_teardown(chip); - - /* unregister any dummy i2c_client */ - if (chip->client_dummy) - i2c_unregister_device(chip->client_dummy); - - kfree(chip); - return 0; -} - -static struct i2c_driver max732x_driver = { - .driver = { - .name = "max732x", - .owner = THIS_MODULE, - }, - .probe = max732x_probe, - .remove = __devexit_p(max732x_remove), - .id_table = max732x_id, -}; - -static int __init max732x_init(void) -{ - return i2c_add_driver(&max732x_driver); -} -/* register after i2c postcore initcall and before - * subsys initcalls that may rely on these GPIOs - */ -subsys_initcall(max732x_init); - -static void __exit max732x_exit(void) -{ - i2c_del_driver(&max732x_driver); -} -module_exit(max732x_exit); - -MODULE_AUTHOR("Eric Miao "); -MODULE_DESCRIPTION("GPIO expander driver for MAX732X"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/mc33880.c b/drivers/gpio/mc33880.c deleted file mode 100644 index 4ec797593bd..00000000000 --- a/drivers/gpio/mc33880.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * mc33880.c 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 - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* Supports: - * Freescale MC33880 high-side/low-side switch - */ - -#include -#include -#include -#include -#include -#include - -#define DRIVER_NAME "mc33880" - -/* - * Pin configurations, see MAX7301 datasheet page 6 - */ -#define PIN_CONFIG_MASK 0x03 -#define PIN_CONFIG_IN_PULLUP 0x03 -#define PIN_CONFIG_IN_WO_PULLUP 0x02 -#define PIN_CONFIG_OUT 0x01 - -#define PIN_NUMBER 8 - - -/* - * Some registers must be read back to modify. - * To save time we cache them here in memory - */ -struct mc33880 { - struct mutex lock; /* protect from simultaneous accesses */ - u8 port_config; - struct gpio_chip chip; - struct spi_device *spi; -}; - -static int mc33880_write_config(struct mc33880 *mc) -{ - return spi_write(mc->spi, &mc->port_config, sizeof(mc->port_config)); -} - - -static int __mc33880_set(struct mc33880 *mc, unsigned offset, int value) -{ - if (value) - mc->port_config |= 1 << offset; - else - mc->port_config &= ~(1 << offset); - - return mc33880_write_config(mc); -} - - -static void mc33880_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct mc33880 *mc = container_of(chip, struct mc33880, chip); - - mutex_lock(&mc->lock); - - __mc33880_set(mc, offset, value); - - mutex_unlock(&mc->lock); -} - -static int __devinit mc33880_probe(struct spi_device *spi) -{ - struct mc33880 *mc; - struct mc33880_platform_data *pdata; - int ret; - - pdata = spi->dev.platform_data; - if (!pdata || !pdata->base) { - dev_dbg(&spi->dev, "incorrect or missing platform data\n"); - return -EINVAL; - } - - /* - * bits_per_word cannot be configured in platform data - */ - spi->bits_per_word = 8; - - ret = spi_setup(spi); - if (ret < 0) - return ret; - - mc = kzalloc(sizeof(struct mc33880), GFP_KERNEL); - if (!mc) - return -ENOMEM; - - mutex_init(&mc->lock); - - dev_set_drvdata(&spi->dev, mc); - - mc->spi = spi; - - mc->chip.label = DRIVER_NAME, - mc->chip.set = mc33880_set; - mc->chip.base = pdata->base; - mc->chip.ngpio = PIN_NUMBER; - mc->chip.can_sleep = 1; - mc->chip.dev = &spi->dev; - mc->chip.owner = THIS_MODULE; - - mc->port_config = 0x00; - /* write twice, because during initialisation the first setting - * is just for testing SPI communication, and the second is the - * "real" configuration - */ - ret = mc33880_write_config(mc); - mc->port_config = 0x00; - if (!ret) - ret = mc33880_write_config(mc); - - if (ret) { - printk(KERN_ERR "Failed writing to " DRIVER_NAME ": %d\n", ret); - goto exit_destroy; - } - - ret = gpiochip_add(&mc->chip); - if (ret) - goto exit_destroy; - - return ret; - -exit_destroy: - dev_set_drvdata(&spi->dev, NULL); - mutex_destroy(&mc->lock); - kfree(mc); - return ret; -} - -static int __devexit mc33880_remove(struct spi_device *spi) -{ - struct mc33880 *mc; - int ret; - - mc = dev_get_drvdata(&spi->dev); - if (mc == NULL) - return -ENODEV; - - dev_set_drvdata(&spi->dev, NULL); - - ret = gpiochip_remove(&mc->chip); - if (!ret) { - mutex_destroy(&mc->lock); - kfree(mc); - } else - dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n", - ret); - - return ret; -} - -static struct spi_driver mc33880_driver = { - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - }, - .probe = mc33880_probe, - .remove = __devexit_p(mc33880_remove), -}; - -static int __init mc33880_init(void) -{ - return spi_register_driver(&mc33880_driver); -} -/* register after spi postcore initcall and before - * subsys initcalls that may rely on these GPIOs - */ -subsys_initcall(mc33880_init); - -static void __exit mc33880_exit(void) -{ - spi_unregister_driver(&mc33880_driver); -} -module_exit(mc33880_exit); - -MODULE_AUTHOR("Mocean Laboratories "); -MODULE_LICENSE("GPL v2"); - diff --git a/drivers/gpio/mcp23s08.c b/drivers/gpio/mcp23s08.c deleted file mode 100644 index 40e076083ec..00000000000 --- a/drivers/gpio/mcp23s08.c +++ /dev/null @@ -1,530 +0,0 @@ -/* - * mcp23s08.c - SPI gpio expander driver - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * MCP types supported by driver - */ -#define MCP_TYPE_S08 0 -#define MCP_TYPE_S17 1 - -/* Registers are all 8 bits wide. - * - * The mcp23s17 has twice as many bits, and can be configured to work - * with either 16 bit registers or with two adjacent 8 bit banks. - * - * Also, there are I2C versions of both chips. - */ -#define MCP_IODIR 0x00 /* init/reset: all ones */ -#define MCP_IPOL 0x01 -#define MCP_GPINTEN 0x02 -#define MCP_DEFVAL 0x03 -#define MCP_INTCON 0x04 -#define MCP_IOCON 0x05 -# define IOCON_SEQOP (1 << 5) -# define IOCON_HAEN (1 << 3) -# define IOCON_ODR (1 << 2) -# define IOCON_INTPOL (1 << 1) -#define MCP_GPPU 0x06 -#define MCP_INTF 0x07 -#define MCP_INTCAP 0x08 -#define MCP_GPIO 0x09 -#define MCP_OLAT 0x0a - -struct mcp23s08; - -struct mcp23s08_ops { - int (*read)(struct mcp23s08 *mcp, unsigned reg); - int (*write)(struct mcp23s08 *mcp, unsigned reg, unsigned val); - int (*read_regs)(struct mcp23s08 *mcp, unsigned reg, - u16 *vals, unsigned n); -}; - -struct mcp23s08 { - struct spi_device *spi; - u8 addr; - - u16 cache[11]; - /* lock protects the cached values */ - struct mutex lock; - - struct gpio_chip chip; - - struct work_struct work; - - const struct mcp23s08_ops *ops; -}; - -/* A given spi_device can represent up to eight mcp23sxx chips - * sharing the same chipselect but using different addresses - * (e.g. chips #0 and #3 might be populated, but not #1 or $2). - * Driver data holds all the per-chip data. - */ -struct mcp23s08_driver_data { - unsigned ngpio; - struct mcp23s08 *mcp[8]; - struct mcp23s08 chip[]; -}; - -static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg) -{ - u8 tx[2], rx[1]; - int status; - - tx[0] = mcp->addr | 0x01; - tx[1] = reg; - status = spi_write_then_read(mcp->spi, tx, sizeof tx, rx, sizeof rx); - return (status < 0) ? status : rx[0]; -} - -static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, unsigned val) -{ - u8 tx[3]; - - tx[0] = mcp->addr; - tx[1] = reg; - tx[2] = val; - return spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0); -} - -static int -mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n) -{ - u8 tx[2], *tmp; - int status; - - if ((n + reg) > sizeof mcp->cache) - return -EINVAL; - tx[0] = mcp->addr | 0x01; - tx[1] = reg; - - tmp = (u8 *)vals; - status = spi_write_then_read(mcp->spi, tx, sizeof tx, tmp, n); - if (status >= 0) { - while (n--) - vals[n] = tmp[n]; /* expand to 16bit */ - } - return status; -} - -static int mcp23s17_read(struct mcp23s08 *mcp, unsigned reg) -{ - u8 tx[2], rx[2]; - int status; - - tx[0] = mcp->addr | 0x01; - tx[1] = reg << 1; - status = spi_write_then_read(mcp->spi, tx, sizeof tx, rx, sizeof rx); - return (status < 0) ? status : (rx[0] | (rx[1] << 8)); -} - -static int mcp23s17_write(struct mcp23s08 *mcp, unsigned reg, unsigned val) -{ - u8 tx[4]; - - tx[0] = mcp->addr; - tx[1] = reg << 1; - tx[2] = val; - tx[3] = val >> 8; - return spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0); -} - -static int -mcp23s17_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n) -{ - u8 tx[2]; - int status; - - if ((n + reg) > sizeof mcp->cache) - return -EINVAL; - tx[0] = mcp->addr | 0x01; - tx[1] = reg << 1; - - status = spi_write_then_read(mcp->spi, tx, sizeof tx, - (u8 *)vals, n * 2); - if (status >= 0) { - while (n--) - vals[n] = __le16_to_cpu((__le16)vals[n]); - } - - return status; -} - -static const struct mcp23s08_ops mcp23s08_ops = { - .read = mcp23s08_read, - .write = mcp23s08_write, - .read_regs = mcp23s08_read_regs, -}; - -static const struct mcp23s08_ops mcp23s17_ops = { - .read = mcp23s17_read, - .write = mcp23s17_write, - .read_regs = mcp23s17_read_regs, -}; - - -/*----------------------------------------------------------------------*/ - -static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset) -{ - struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); - int status; - - mutex_lock(&mcp->lock); - mcp->cache[MCP_IODIR] |= (1 << offset); - status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); - mutex_unlock(&mcp->lock); - return status; -} - -static int mcp23s08_get(struct gpio_chip *chip, unsigned offset) -{ - struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); - int status; - - mutex_lock(&mcp->lock); - - /* REVISIT reading this clears any IRQ ... */ - status = mcp->ops->read(mcp, MCP_GPIO); - if (status < 0) - status = 0; - else { - mcp->cache[MCP_GPIO] = status; - status = !!(status & (1 << offset)); - } - mutex_unlock(&mcp->lock); - return status; -} - -static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value) -{ - unsigned olat = mcp->cache[MCP_OLAT]; - - if (value) - olat |= mask; - else - olat &= ~mask; - mcp->cache[MCP_OLAT] = olat; - return mcp->ops->write(mcp, MCP_OLAT, olat); -} - -static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); - unsigned mask = 1 << offset; - - mutex_lock(&mcp->lock); - __mcp23s08_set(mcp, mask, value); - mutex_unlock(&mcp->lock); -} - -static int -mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value) -{ - struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); - unsigned mask = 1 << offset; - int status; - - mutex_lock(&mcp->lock); - status = __mcp23s08_set(mcp, mask, value); - if (status == 0) { - mcp->cache[MCP_IODIR] &= ~mask; - status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); - } - mutex_unlock(&mcp->lock); - return status; -} - -/*----------------------------------------------------------------------*/ - -#ifdef CONFIG_DEBUG_FS - -#include - -/* - * This shows more info than the generic gpio dump code: - * pullups, deglitching, open drain drive. - */ -static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip) -{ - struct mcp23s08 *mcp; - char bank; - int t; - unsigned mask; - - mcp = container_of(chip, struct mcp23s08, chip); - - /* NOTE: we only handle one bank for now ... */ - bank = '0' + ((mcp->addr >> 1) & 0x7); - - mutex_lock(&mcp->lock); - t = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache)); - if (t < 0) { - seq_printf(s, " I/O ERROR %d\n", t); - goto done; - } - - for (t = 0, mask = 1; t < chip->ngpio; t++, mask <<= 1) { - const char *label; - - label = gpiochip_is_requested(chip, t); - if (!label) - continue; - - seq_printf(s, " gpio-%-3d P%c.%d (%-12s) %s %s %s", - chip->base + t, bank, t, label, - (mcp->cache[MCP_IODIR] & mask) ? "in " : "out", - (mcp->cache[MCP_GPIO] & mask) ? "hi" : "lo", - (mcp->cache[MCP_GPPU] & mask) ? " " : "up"); - /* NOTE: ignoring the irq-related registers */ - seq_printf(s, "\n"); - } -done: - mutex_unlock(&mcp->lock); -} - -#else -#define mcp23s08_dbg_show NULL -#endif - -/*----------------------------------------------------------------------*/ - -static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr, - unsigned type, unsigned base, unsigned pullups) -{ - struct mcp23s08_driver_data *data = spi_get_drvdata(spi); - struct mcp23s08 *mcp = data->mcp[addr]; - int status; - - mutex_init(&mcp->lock); - - mcp->spi = spi; - mcp->addr = 0x40 | (addr << 1); - - mcp->chip.direction_input = mcp23s08_direction_input; - mcp->chip.get = mcp23s08_get; - mcp->chip.direction_output = mcp23s08_direction_output; - mcp->chip.set = mcp23s08_set; - mcp->chip.dbg_show = mcp23s08_dbg_show; - - if (type == MCP_TYPE_S17) { - mcp->ops = &mcp23s17_ops; - mcp->chip.ngpio = 16; - mcp->chip.label = "mcp23s17"; - } else { - mcp->ops = &mcp23s08_ops; - mcp->chip.ngpio = 8; - mcp->chip.label = "mcp23s08"; - } - mcp->chip.base = base; - mcp->chip.can_sleep = 1; - mcp->chip.dev = &spi->dev; - mcp->chip.owner = THIS_MODULE; - - /* verify MCP_IOCON.SEQOP = 0, so sequential reads work, - * and MCP_IOCON.HAEN = 1, so we work with all chips. - */ - status = mcp->ops->read(mcp, MCP_IOCON); - if (status < 0) - goto fail; - if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) { - /* mcp23s17 has IOCON twice, make sure they are in sync */ - status &= ~(IOCON_SEQOP | (IOCON_SEQOP << 8)); - status |= IOCON_HAEN | (IOCON_HAEN << 8); - status = mcp->ops->write(mcp, MCP_IOCON, status); - if (status < 0) - goto fail; - } - - /* configure ~100K pullups */ - status = mcp->ops->write(mcp, MCP_GPPU, pullups); - if (status < 0) - goto fail; - - status = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache)); - if (status < 0) - goto fail; - - /* disable inverter on input */ - if (mcp->cache[MCP_IPOL] != 0) { - mcp->cache[MCP_IPOL] = 0; - status = mcp->ops->write(mcp, MCP_IPOL, 0); - if (status < 0) - goto fail; - } - - /* disable irqs */ - if (mcp->cache[MCP_GPINTEN] != 0) { - mcp->cache[MCP_GPINTEN] = 0; - status = mcp->ops->write(mcp, MCP_GPINTEN, 0); - if (status < 0) - goto fail; - } - - status = gpiochip_add(&mcp->chip); -fail: - if (status < 0) - dev_dbg(&spi->dev, "can't setup chip %d, --> %d\n", - addr, status); - return status; -} - -static int mcp23s08_probe(struct spi_device *spi) -{ - struct mcp23s08_platform_data *pdata; - unsigned addr; - unsigned chips = 0; - struct mcp23s08_driver_data *data; - int status, type; - unsigned base; - - type = spi_get_device_id(spi)->driver_data; - - pdata = spi->dev.platform_data; - if (!pdata || !gpio_is_valid(pdata->base)) { - dev_dbg(&spi->dev, "invalid or missing platform data\n"); - return -EINVAL; - } - - for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { - if (!pdata->chip[addr].is_present) - continue; - chips++; - if ((type == MCP_TYPE_S08) && (addr > 3)) { - dev_err(&spi->dev, - "mcp23s08 only supports address 0..3\n"); - return -EINVAL; - } - } - if (!chips) - return -ENODEV; - - data = kzalloc(sizeof *data + chips * sizeof(struct mcp23s08), - GFP_KERNEL); - if (!data) - return -ENOMEM; - spi_set_drvdata(spi, data); - - base = pdata->base; - for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { - if (!pdata->chip[addr].is_present) - continue; - chips--; - data->mcp[addr] = &data->chip[chips]; - status = mcp23s08_probe_one(spi, addr, type, base, - pdata->chip[addr].pullups); - if (status < 0) - goto fail; - - base += (type == MCP_TYPE_S17) ? 16 : 8; - } - data->ngpio = base - pdata->base; - - /* NOTE: these chips have a relatively sane IRQ framework, with - * per-signal masking and level/edge triggering. It's not yet - * handled here... - */ - - if (pdata->setup) { - status = pdata->setup(spi, - pdata->base, data->ngpio, - pdata->context); - if (status < 0) - dev_dbg(&spi->dev, "setup --> %d\n", status); - } - - return 0; - -fail: - for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) { - int tmp; - - if (!data->mcp[addr]) - continue; - tmp = gpiochip_remove(&data->mcp[addr]->chip); - if (tmp < 0) - dev_err(&spi->dev, "%s --> %d\n", "remove", tmp); - } - kfree(data); - return status; -} - -static int mcp23s08_remove(struct spi_device *spi) -{ - struct mcp23s08_driver_data *data = spi_get_drvdata(spi); - struct mcp23s08_platform_data *pdata = spi->dev.platform_data; - unsigned addr; - int status = 0; - - if (pdata->teardown) { - status = pdata->teardown(spi, - pdata->base, data->ngpio, - pdata->context); - if (status < 0) { - dev_err(&spi->dev, "%s --> %d\n", "teardown", status); - return status; - } - } - - for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) { - int tmp; - - if (!data->mcp[addr]) - continue; - - tmp = gpiochip_remove(&data->mcp[addr]->chip); - if (tmp < 0) { - dev_err(&spi->dev, "%s --> %d\n", "remove", tmp); - status = tmp; - } - } - if (status == 0) - kfree(data); - return status; -} - -static const struct spi_device_id mcp23s08_ids[] = { - { "mcp23s08", MCP_TYPE_S08 }, - { "mcp23s17", MCP_TYPE_S17 }, - { }, -}; -MODULE_DEVICE_TABLE(spi, mcp23s08_ids); - -static struct spi_driver mcp23s08_driver = { - .probe = mcp23s08_probe, - .remove = mcp23s08_remove, - .id_table = mcp23s08_ids, - .driver = { - .name = "mcp23s08", - .owner = THIS_MODULE, - }, -}; - -/*----------------------------------------------------------------------*/ - -static int __init mcp23s08_init(void) -{ - return spi_register_driver(&mcp23s08_driver); -} -/* register after spi postcore initcall and before - * subsys initcalls that may rely on these GPIOs - */ -subsys_initcall(mcp23s08_init); - -static void __exit mcp23s08_exit(void) -{ - spi_unregister_driver(&mcp23s08_driver); -} -module_exit(mcp23s08_exit); - -MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/ml_ioh_gpio.c b/drivers/gpio/ml_ioh_gpio.c deleted file mode 100644 index 1bc621ac353..00000000000 --- a/drivers/gpio/ml_ioh_gpio.c +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. - */ -#include -#include -#include -#include - -#define PCI_VENDOR_ID_ROHM 0x10DB - -struct ioh_reg_comn { - u32 ien; - u32 istatus; - u32 idisp; - u32 iclr; - u32 imask; - u32 imaskclr; - u32 po; - u32 pi; - u32 pm; - u32 im_0; - u32 im_1; - u32 reserved; -}; - -struct ioh_regs { - struct ioh_reg_comn regs[8]; - u32 reserve1[16]; - u32 ioh_sel_reg[4]; - u32 reserve2[11]; - u32 srst; -}; - -/** - * struct ioh_gpio_reg_data - The register store data. - * @po_reg: To store contents of PO register. - * @pm_reg: To store contents of PM register. - */ -struct ioh_gpio_reg_data { - u32 po_reg; - u32 pm_reg; -}; - -/** - * struct ioh_gpio - GPIO private data structure. - * @base: PCI base address of Memory mapped I/O register. - * @reg: Memory mapped IOH GPIO register list. - * @dev: Pointer to device structure. - * @gpio: Data for GPIO infrastructure. - * @ioh_gpio_reg: Memory mapped Register data is saved here - * when suspend. - * @ch: Indicate GPIO channel - */ -struct ioh_gpio { - void __iomem *base; - struct ioh_regs __iomem *reg; - struct device *dev; - struct gpio_chip gpio; - struct ioh_gpio_reg_data ioh_gpio_reg; - struct mutex lock; - int ch; -}; - -static const int num_ports[] = {6, 12, 16, 16, 15, 16, 16, 12}; - -static void ioh_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) -{ - u32 reg_val; - struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio); - - mutex_lock(&chip->lock); - reg_val = ioread32(&chip->reg->regs[chip->ch].po); - if (val) - reg_val |= (1 << nr); - else - reg_val &= ~(1 << nr); - - iowrite32(reg_val, &chip->reg->regs[chip->ch].po); - mutex_unlock(&chip->lock); -} - -static int ioh_gpio_get(struct gpio_chip *gpio, unsigned nr) -{ - struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio); - - return ioread32(&chip->reg->regs[chip->ch].pi) & (1 << nr); -} - -static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, - int val) -{ - struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio); - u32 pm; - u32 reg_val; - - mutex_lock(&chip->lock); - pm = ioread32(&chip->reg->regs[chip->ch].pm) & - ((1 << num_ports[chip->ch]) - 1); - pm |= (1 << nr); - iowrite32(pm, &chip->reg->regs[chip->ch].pm); - - reg_val = ioread32(&chip->reg->regs[chip->ch].po); - if (val) - reg_val |= (1 << nr); - else - reg_val &= ~(1 << nr); - iowrite32(reg_val, &chip->reg->regs[chip->ch].po); - - mutex_unlock(&chip->lock); - - return 0; -} - -static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) -{ - struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio); - u32 pm; - - mutex_lock(&chip->lock); - pm = ioread32(&chip->reg->regs[chip->ch].pm) & - ((1 << num_ports[chip->ch]) - 1); - pm &= ~(1 << nr); - iowrite32(pm, &chip->reg->regs[chip->ch].pm); - mutex_unlock(&chip->lock); - - return 0; -} - -#ifdef CONFIG_PM -/* - * Save register configuration and disable interrupts. - */ -static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip) -{ - chip->ioh_gpio_reg.po_reg = ioread32(&chip->reg->regs[chip->ch].po); - chip->ioh_gpio_reg.pm_reg = ioread32(&chip->reg->regs[chip->ch].pm); -} - -/* - * This function restores the register configuration of the GPIO device. - */ -static void ioh_gpio_restore_reg_conf(struct ioh_gpio *chip) -{ - /* to store contents of PO register */ - iowrite32(chip->ioh_gpio_reg.po_reg, &chip->reg->regs[chip->ch].po); - /* to store contents of PM register */ - iowrite32(chip->ioh_gpio_reg.pm_reg, &chip->reg->regs[chip->ch].pm); -} -#endif - -static void ioh_gpio_setup(struct ioh_gpio *chip, int num_port) -{ - struct gpio_chip *gpio = &chip->gpio; - - gpio->label = dev_name(chip->dev); - gpio->owner = THIS_MODULE; - gpio->direction_input = ioh_gpio_direction_input; - gpio->get = ioh_gpio_get; - gpio->direction_output = ioh_gpio_direction_output; - gpio->set = ioh_gpio_set; - gpio->dbg_show = NULL; - gpio->base = -1; - gpio->ngpio = num_port; - gpio->can_sleep = 0; -} - -static int __devinit ioh_gpio_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - int ret; - int i; - struct ioh_gpio *chip; - void __iomem *base; - void __iomem *chip_save; - - ret = pci_enable_device(pdev); - if (ret) { - dev_err(&pdev->dev, "%s : pci_enable_device failed", __func__); - goto err_pci_enable; - } - - ret = pci_request_regions(pdev, KBUILD_MODNAME); - if (ret) { - dev_err(&pdev->dev, "pci_request_regions failed-%d", ret); - goto err_request_regions; - } - - base = pci_iomap(pdev, 1, 0); - if (base == 0) { - dev_err(&pdev->dev, "%s : pci_iomap failed", __func__); - ret = -ENOMEM; - goto err_iomap; - } - - chip_save = kzalloc(sizeof(*chip) * 8, GFP_KERNEL); - if (chip_save == NULL) { - dev_err(&pdev->dev, "%s : kzalloc failed", __func__); - ret = -ENOMEM; - goto err_kzalloc; - } - - chip = chip_save; - for (i = 0; i < 8; i++, chip++) { - chip->dev = &pdev->dev; - chip->base = base; - chip->reg = chip->base; - chip->ch = i; - mutex_init(&chip->lock); - ioh_gpio_setup(chip, num_ports[i]); - ret = gpiochip_add(&chip->gpio); - if (ret) { - dev_err(&pdev->dev, "IOH gpio: Failed to register GPIO\n"); - goto err_gpiochip_add; - } - } - - chip = chip_save; - pci_set_drvdata(pdev, chip); - - return 0; - -err_gpiochip_add: - for (; i != 0; i--) { - chip--; - ret = gpiochip_remove(&chip->gpio); - if (ret) - dev_err(&pdev->dev, "Failed gpiochip_remove(%d)\n", i); - } - kfree(chip_save); - -err_kzalloc: - pci_iounmap(pdev, base); - -err_iomap: - pci_release_regions(pdev); - -err_request_regions: - pci_disable_device(pdev); - -err_pci_enable: - - dev_err(&pdev->dev, "%s Failed returns %d\n", __func__, ret); - return ret; -} - -static void __devexit ioh_gpio_remove(struct pci_dev *pdev) -{ - int err; - int i; - struct ioh_gpio *chip = pci_get_drvdata(pdev); - void __iomem *chip_save; - - chip_save = chip; - for (i = 0; i < 8; i++, chip++) { - err = gpiochip_remove(&chip->gpio); - if (err) - dev_err(&pdev->dev, "Failed gpiochip_remove\n"); - } - - chip = chip_save; - pci_iounmap(pdev, chip->base); - pci_release_regions(pdev); - pci_disable_device(pdev); - kfree(chip); -} - -#ifdef CONFIG_PM -static int ioh_gpio_suspend(struct pci_dev *pdev, pm_message_t state) -{ - s32 ret; - struct ioh_gpio *chip = pci_get_drvdata(pdev); - - ioh_gpio_save_reg_conf(chip); - ioh_gpio_restore_reg_conf(chip); - - ret = pci_save_state(pdev); - if (ret) { - dev_err(&pdev->dev, "pci_save_state Failed-%d\n", ret); - return ret; - } - pci_disable_device(pdev); - pci_set_power_state(pdev, PCI_D0); - ret = pci_enable_wake(pdev, PCI_D0, 1); - if (ret) - dev_err(&pdev->dev, "pci_enable_wake Failed -%d\n", ret); - - return 0; -} - -static int ioh_gpio_resume(struct pci_dev *pdev) -{ - s32 ret; - struct ioh_gpio *chip = pci_get_drvdata(pdev); - - ret = pci_enable_wake(pdev, PCI_D0, 0); - - pci_set_power_state(pdev, PCI_D0); - ret = pci_enable_device(pdev); - if (ret) { - dev_err(&pdev->dev, "pci_enable_device Failed-%d ", ret); - return ret; - } - pci_restore_state(pdev); - - iowrite32(0x01, &chip->reg->srst); - iowrite32(0x00, &chip->reg->srst); - ioh_gpio_restore_reg_conf(chip); - - return 0; -} -#else -#define ioh_gpio_suspend NULL -#define ioh_gpio_resume NULL -#endif - -static DEFINE_PCI_DEVICE_TABLE(ioh_gpio_pcidev_id) = { - { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x802E) }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, ioh_gpio_pcidev_id); - -static struct pci_driver ioh_gpio_driver = { - .name = "ml_ioh_gpio", - .id_table = ioh_gpio_pcidev_id, - .probe = ioh_gpio_probe, - .remove = __devexit_p(ioh_gpio_remove), - .suspend = ioh_gpio_suspend, - .resume = ioh_gpio_resume -}; - -static int __init ioh_gpio_pci_init(void) -{ - return pci_register_driver(&ioh_gpio_driver); -} -module_init(ioh_gpio_pci_init); - -static void __exit ioh_gpio_pci_exit(void) -{ - pci_unregister_driver(&ioh_gpio_driver); -} -module_exit(ioh_gpio_pci_exit); - -MODULE_DESCRIPTION("OKI SEMICONDUCTOR ML-IOH series GPIO Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c deleted file mode 100644 index 63022b17014..00000000000 --- a/drivers/gpio/pca953x.c +++ /dev/null @@ -1,768 +0,0 @@ -/* - * pca953x.c - 4/8/16 bit I/O ports - * - * Copyright (C) 2005 Ben Gardner - * Copyright (C) 2007 Marvell International Ltd. - * - * Derived from drivers/i2c/chips/pca9539.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_OF_GPIO -#include -#include -#endif - -#define PCA953X_INPUT 0 -#define PCA953X_OUTPUT 1 -#define PCA953X_INVERT 2 -#define PCA953X_DIRECTION 3 - -#define PCA957X_IN 0 -#define PCA957X_INVRT 1 -#define PCA957X_BKEN 2 -#define PCA957X_PUPD 3 -#define PCA957X_CFG 4 -#define PCA957X_OUT 5 -#define PCA957X_MSK 6 -#define PCA957X_INTS 7 - -#define PCA_GPIO_MASK 0x00FF -#define PCA_INT 0x0100 -#define PCA953X_TYPE 0x1000 -#define PCA957X_TYPE 0x2000 - -static const struct i2c_device_id pca953x_id[] = { - { "pca9534", 8 | PCA953X_TYPE | PCA_INT, }, - { "pca9535", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca9536", 4 | PCA953X_TYPE, }, - { "pca9537", 4 | PCA953X_TYPE | PCA_INT, }, - { "pca9538", 8 | PCA953X_TYPE | PCA_INT, }, - { "pca9539", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca9554", 8 | PCA953X_TYPE | PCA_INT, }, - { "pca9555", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca9556", 8 | PCA953X_TYPE, }, - { "pca9557", 8 | PCA953X_TYPE, }, - { "pca9574", 8 | PCA957X_TYPE | PCA_INT, }, - { "pca9575", 16 | PCA957X_TYPE | PCA_INT, }, - - { "max7310", 8 | PCA953X_TYPE, }, - { "max7312", 16 | PCA953X_TYPE | PCA_INT, }, - { "max7313", 16 | PCA953X_TYPE | PCA_INT, }, - { "max7315", 8 | PCA953X_TYPE | PCA_INT, }, - { "pca6107", 8 | PCA953X_TYPE | PCA_INT, }, - { "tca6408", 8 | PCA953X_TYPE | PCA_INT, }, - { "tca6416", 16 | PCA953X_TYPE | PCA_INT, }, - /* NYET: { "tca6424", 24, }, */ - { } -}; -MODULE_DEVICE_TABLE(i2c, pca953x_id); - -struct pca953x_chip { - unsigned gpio_start; - uint16_t reg_output; - uint16_t reg_direction; - struct mutex i2c_lock; - -#ifdef CONFIG_GPIO_PCA953X_IRQ - struct mutex irq_lock; - uint16_t irq_mask; - uint16_t irq_stat; - uint16_t irq_trig_raise; - uint16_t irq_trig_fall; - int irq_base; -#endif - - struct i2c_client *client; - struct pca953x_platform_data *dyn_pdata; - struct gpio_chip gpio_chip; - const char *const *names; - int chip_type; -}; - -static int pca953x_write_reg(struct pca953x_chip *chip, int reg, uint16_t val) -{ - int ret = 0; - - if (chip->gpio_chip.ngpio <= 8) - ret = i2c_smbus_write_byte_data(chip->client, reg, val); - else { - switch (chip->chip_type) { - case PCA953X_TYPE: - ret = i2c_smbus_write_word_data(chip->client, - reg << 1, val); - break; - case PCA957X_TYPE: - ret = i2c_smbus_write_byte_data(chip->client, reg << 1, - val & 0xff); - if (ret < 0) - break; - ret = i2c_smbus_write_byte_data(chip->client, - (reg << 1) + 1, - (val & 0xff00) >> 8); - break; - } - } - - if (ret < 0) { - dev_err(&chip->client->dev, "failed writing register\n"); - return ret; - } - - return 0; -} - -static int pca953x_read_reg(struct pca953x_chip *chip, int reg, uint16_t *val) -{ - int ret; - - if (chip->gpio_chip.ngpio <= 8) - ret = i2c_smbus_read_byte_data(chip->client, reg); - else - ret = i2c_smbus_read_word_data(chip->client, reg << 1); - - if (ret < 0) { - dev_err(&chip->client->dev, "failed reading register\n"); - return ret; - } - - *val = (uint16_t)ret; - return 0; -} - -static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off) -{ - struct pca953x_chip *chip; - uint16_t reg_val; - int ret, offset = 0; - - chip = container_of(gc, struct pca953x_chip, gpio_chip); - - mutex_lock(&chip->i2c_lock); - reg_val = chip->reg_direction | (1u << off); - - switch (chip->chip_type) { - case PCA953X_TYPE: - offset = PCA953X_DIRECTION; - break; - case PCA957X_TYPE: - offset = PCA957X_CFG; - break; - } - ret = pca953x_write_reg(chip, offset, reg_val); - if (ret) - goto exit; - - chip->reg_direction = reg_val; - ret = 0; -exit: - mutex_unlock(&chip->i2c_lock); - return ret; -} - -static int pca953x_gpio_direction_output(struct gpio_chip *gc, - unsigned off, int val) -{ - struct pca953x_chip *chip; - uint16_t reg_val; - int ret, offset = 0; - - chip = container_of(gc, struct pca953x_chip, gpio_chip); - - mutex_lock(&chip->i2c_lock); - /* set output level */ - if (val) - reg_val = chip->reg_output | (1u << off); - else - reg_val = chip->reg_output & ~(1u << off); - - switch (chip->chip_type) { - case PCA953X_TYPE: - offset = PCA953X_OUTPUT; - break; - case PCA957X_TYPE: - offset = PCA957X_OUT; - break; - } - ret = pca953x_write_reg(chip, offset, reg_val); - if (ret) - goto exit; - - chip->reg_output = reg_val; - - /* then direction */ - reg_val = chip->reg_direction & ~(1u << off); - switch (chip->chip_type) { - case PCA953X_TYPE: - offset = PCA953X_DIRECTION; - break; - case PCA957X_TYPE: - offset = PCA957X_CFG; - break; - } - ret = pca953x_write_reg(chip, offset, reg_val); - if (ret) - goto exit; - - chip->reg_direction = reg_val; - ret = 0; -exit: - mutex_unlock(&chip->i2c_lock); - return ret; -} - -static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) -{ - struct pca953x_chip *chip; - uint16_t reg_val; - int ret, offset = 0; - - chip = container_of(gc, struct pca953x_chip, gpio_chip); - - mutex_lock(&chip->i2c_lock); - switch (chip->chip_type) { - case PCA953X_TYPE: - offset = PCA953X_INPUT; - break; - case PCA957X_TYPE: - offset = PCA957X_IN; - break; - } - ret = pca953x_read_reg(chip, offset, ®_val); - mutex_unlock(&chip->i2c_lock); - if (ret < 0) { - /* NOTE: diagnostic already emitted; that's all we should - * do unless gpio_*_value_cansleep() calls become different - * from their nonsleeping siblings (and report faults). - */ - return 0; - } - - return (reg_val & (1u << off)) ? 1 : 0; -} - -static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) -{ - struct pca953x_chip *chip; - uint16_t reg_val; - int ret, offset = 0; - - chip = container_of(gc, struct pca953x_chip, gpio_chip); - - mutex_lock(&chip->i2c_lock); - if (val) - reg_val = chip->reg_output | (1u << off); - else - reg_val = chip->reg_output & ~(1u << off); - - switch (chip->chip_type) { - case PCA953X_TYPE: - offset = PCA953X_OUTPUT; - break; - case PCA957X_TYPE: - offset = PCA957X_OUT; - break; - } - ret = pca953x_write_reg(chip, offset, reg_val); - if (ret) - goto exit; - - chip->reg_output = reg_val; -exit: - mutex_unlock(&chip->i2c_lock); -} - -static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) -{ - struct gpio_chip *gc; - - gc = &chip->gpio_chip; - - gc->direction_input = pca953x_gpio_direction_input; - gc->direction_output = pca953x_gpio_direction_output; - gc->get = pca953x_gpio_get_value; - gc->set = pca953x_gpio_set_value; - gc->can_sleep = 1; - - gc->base = chip->gpio_start; - gc->ngpio = gpios; - gc->label = chip->client->name; - gc->dev = &chip->client->dev; - gc->owner = THIS_MODULE; - gc->names = chip->names; -} - -#ifdef CONFIG_GPIO_PCA953X_IRQ -static int pca953x_gpio_to_irq(struct gpio_chip *gc, unsigned off) -{ - struct pca953x_chip *chip; - - chip = container_of(gc, struct pca953x_chip, gpio_chip); - return chip->irq_base + off; -} - -static void pca953x_irq_mask(struct irq_data *d) -{ - struct pca953x_chip *chip = irq_data_get_irq_chip_data(d); - - chip->irq_mask &= ~(1 << (d->irq - chip->irq_base)); -} - -static void pca953x_irq_unmask(struct irq_data *d) -{ - struct pca953x_chip *chip = irq_data_get_irq_chip_data(d); - - chip->irq_mask |= 1 << (d->irq - chip->irq_base); -} - -static void pca953x_irq_bus_lock(struct irq_data *d) -{ - struct pca953x_chip *chip = irq_data_get_irq_chip_data(d); - - mutex_lock(&chip->irq_lock); -} - -static void pca953x_irq_bus_sync_unlock(struct irq_data *d) -{ - struct pca953x_chip *chip = irq_data_get_irq_chip_data(d); - uint16_t new_irqs; - uint16_t level; - - /* Look for any newly setup interrupt */ - new_irqs = chip->irq_trig_fall | chip->irq_trig_raise; - new_irqs &= ~chip->reg_direction; - - while (new_irqs) { - level = __ffs(new_irqs); - pca953x_gpio_direction_input(&chip->gpio_chip, level); - new_irqs &= ~(1 << level); - } - - mutex_unlock(&chip->irq_lock); -} - -static int pca953x_irq_set_type(struct irq_data *d, unsigned int type) -{ - struct pca953x_chip *chip = irq_data_get_irq_chip_data(d); - uint16_t level = d->irq - chip->irq_base; - uint16_t mask = 1 << level; - - if (!(type & IRQ_TYPE_EDGE_BOTH)) { - dev_err(&chip->client->dev, "irq %d: unsupported type %d\n", - d->irq, type); - return -EINVAL; - } - - if (type & IRQ_TYPE_EDGE_FALLING) - chip->irq_trig_fall |= mask; - else - chip->irq_trig_fall &= ~mask; - - if (type & IRQ_TYPE_EDGE_RISING) - chip->irq_trig_raise |= mask; - else - chip->irq_trig_raise &= ~mask; - - return 0; -} - -static struct irq_chip pca953x_irq_chip = { - .name = "pca953x", - .irq_mask = pca953x_irq_mask, - .irq_unmask = pca953x_irq_unmask, - .irq_bus_lock = pca953x_irq_bus_lock, - .irq_bus_sync_unlock = pca953x_irq_bus_sync_unlock, - .irq_set_type = pca953x_irq_set_type, -}; - -static uint16_t pca953x_irq_pending(struct pca953x_chip *chip) -{ - uint16_t cur_stat; - uint16_t old_stat; - uint16_t pending; - uint16_t trigger; - int ret, offset = 0; - - switch (chip->chip_type) { - case PCA953X_TYPE: - offset = PCA953X_INPUT; - break; - case PCA957X_TYPE: - offset = PCA957X_IN; - break; - } - ret = pca953x_read_reg(chip, offset, &cur_stat); - if (ret) - return 0; - - /* Remove output pins from the equation */ - cur_stat &= chip->reg_direction; - - old_stat = chip->irq_stat; - trigger = (cur_stat ^ old_stat) & chip->irq_mask; - - if (!trigger) - return 0; - - chip->irq_stat = cur_stat; - - pending = (old_stat & chip->irq_trig_fall) | - (cur_stat & chip->irq_trig_raise); - pending &= trigger; - - return pending; -} - -static irqreturn_t pca953x_irq_handler(int irq, void *devid) -{ - struct pca953x_chip *chip = devid; - uint16_t pending; - uint16_t level; - - pending = pca953x_irq_pending(chip); - - if (!pending) - return IRQ_HANDLED; - - do { - level = __ffs(pending); - generic_handle_irq(level + chip->irq_base); - - pending &= ~(1 << level); - } while (pending); - - return IRQ_HANDLED; -} - -static int pca953x_irq_setup(struct pca953x_chip *chip, - const struct i2c_device_id *id) -{ - struct i2c_client *client = chip->client; - struct pca953x_platform_data *pdata = client->dev.platform_data; - int ret, offset = 0; - - if (pdata->irq_base != -1 - && (id->driver_data & PCA_INT)) { - int lvl; - - switch (chip->chip_type) { - case PCA953X_TYPE: - offset = PCA953X_INPUT; - break; - case PCA957X_TYPE: - offset = PCA957X_IN; - break; - } - ret = pca953x_read_reg(chip, offset, &chip->irq_stat); - if (ret) - goto out_failed; - - /* - * There is no way to know which GPIO line generated the - * interrupt. We have to rely on the previous read for - * this purpose. - */ - chip->irq_stat &= chip->reg_direction; - chip->irq_base = pdata->irq_base; - mutex_init(&chip->irq_lock); - - for (lvl = 0; lvl < chip->gpio_chip.ngpio; lvl++) { - int irq = lvl + chip->irq_base; - - irq_set_chip_data(irq, chip); - irq_set_chip_and_handler(irq, &pca953x_irq_chip, - handle_simple_irq); -#ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); -#else - irq_set_noprobe(irq); -#endif - } - - ret = request_threaded_irq(client->irq, - NULL, - pca953x_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(&client->dev), chip); - if (ret) { - dev_err(&client->dev, "failed to request irq %d\n", - client->irq); - goto out_failed; - } - - chip->gpio_chip.to_irq = pca953x_gpio_to_irq; - } - - return 0; - -out_failed: - chip->irq_base = -1; - return ret; -} - -static void pca953x_irq_teardown(struct pca953x_chip *chip) -{ - if (chip->irq_base != -1) - free_irq(chip->client->irq, chip); -} -#else /* CONFIG_GPIO_PCA953X_IRQ */ -static int pca953x_irq_setup(struct pca953x_chip *chip, - const struct i2c_device_id *id) -{ - struct i2c_client *client = chip->client; - struct pca953x_platform_data *pdata = client->dev.platform_data; - - if (pdata->irq_base != -1 && (id->driver_data & PCA_INT)) - dev_warn(&client->dev, "interrupt support not compiled in\n"); - - return 0; -} - -static void pca953x_irq_teardown(struct pca953x_chip *chip) -{ -} -#endif - -/* - * Handlers for alternative sources of platform_data - */ -#ifdef CONFIG_OF_GPIO -/* - * Translate OpenFirmware node properties into platform_data - */ -static struct pca953x_platform_data * -pca953x_get_alt_pdata(struct i2c_client *client) -{ - struct pca953x_platform_data *pdata; - struct device_node *node; - const __be32 *val; - int size; - - node = client->dev.of_node; - if (node == NULL) - return NULL; - - pdata = kzalloc(sizeof(struct pca953x_platform_data), GFP_KERNEL); - if (pdata == NULL) { - dev_err(&client->dev, "Unable to allocate platform_data\n"); - return NULL; - } - - pdata->gpio_base = -1; - val = of_get_property(node, "linux,gpio-base", &size); - if (val) { - if (size != sizeof(*val)) - dev_warn(&client->dev, "%s: wrong linux,gpio-base\n", - node->full_name); - else - pdata->gpio_base = be32_to_cpup(val); - } - - val = of_get_property(node, "polarity", NULL); - if (val) - pdata->invert = *val; - - return pdata; -} -#else -static struct pca953x_platform_data * -pca953x_get_alt_pdata(struct i2c_client *client) -{ - return NULL; -} -#endif - -static int __devinit device_pca953x_init(struct pca953x_chip *chip, int invert) -{ - int ret; - - ret = pca953x_read_reg(chip, PCA953X_OUTPUT, &chip->reg_output); - if (ret) - goto out; - - ret = pca953x_read_reg(chip, PCA953X_DIRECTION, - &chip->reg_direction); - if (ret) - goto out; - - /* set platform specific polarity inversion */ - ret = pca953x_write_reg(chip, PCA953X_INVERT, invert); - if (ret) - goto out; - return 0; -out: - return ret; -} - -static int __devinit device_pca957x_init(struct pca953x_chip *chip, int invert) -{ - int ret; - uint16_t val = 0; - - /* Let every port in proper state, that could save power */ - pca953x_write_reg(chip, PCA957X_PUPD, 0x0); - pca953x_write_reg(chip, PCA957X_CFG, 0xffff); - pca953x_write_reg(chip, PCA957X_OUT, 0x0); - - ret = pca953x_read_reg(chip, PCA957X_IN, &val); - if (ret) - goto out; - ret = pca953x_read_reg(chip, PCA957X_OUT, &chip->reg_output); - if (ret) - goto out; - ret = pca953x_read_reg(chip, PCA957X_CFG, &chip->reg_direction); - if (ret) - goto out; - - /* set platform specific polarity inversion */ - pca953x_write_reg(chip, PCA957X_INVRT, invert); - - /* To enable register 6, 7 to controll pull up and pull down */ - pca953x_write_reg(chip, PCA957X_BKEN, 0x202); - - return 0; -out: - return ret; -} - -static int __devinit pca953x_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct pca953x_platform_data *pdata; - struct pca953x_chip *chip; - int ret = 0; - - chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL); - if (chip == NULL) - return -ENOMEM; - - pdata = client->dev.platform_data; - if (pdata == NULL) { - pdata = pca953x_get_alt_pdata(client); - /* - * Unlike normal platform_data, this is allocated - * dynamically and must be freed in the driver - */ - chip->dyn_pdata = pdata; - } - - if (pdata == NULL) { - dev_dbg(&client->dev, "no platform data\n"); - ret = -EINVAL; - goto out_failed; - } - - chip->client = client; - - chip->gpio_start = pdata->gpio_base; - - chip->names = pdata->names; - chip->chip_type = id->driver_data & (PCA953X_TYPE | PCA957X_TYPE); - - mutex_init(&chip->i2c_lock); - - /* initialize cached registers from their original values. - * we can't share this chip with another i2c master. - */ - pca953x_setup_gpio(chip, id->driver_data & PCA_GPIO_MASK); - - if (chip->chip_type == PCA953X_TYPE) - device_pca953x_init(chip, pdata->invert); - else if (chip->chip_type == PCA957X_TYPE) - device_pca957x_init(chip, pdata->invert); - else - goto out_failed; - - ret = pca953x_irq_setup(chip, id); - if (ret) - goto out_failed; - - ret = gpiochip_add(&chip->gpio_chip); - if (ret) - goto out_failed_irq; - - if (pdata->setup) { - ret = pdata->setup(client, chip->gpio_chip.base, - chip->gpio_chip.ngpio, pdata->context); - if (ret < 0) - dev_warn(&client->dev, "setup failed, %d\n", ret); - } - - i2c_set_clientdata(client, chip); - return 0; - -out_failed_irq: - pca953x_irq_teardown(chip); -out_failed: - kfree(chip->dyn_pdata); - kfree(chip); - return ret; -} - -static int pca953x_remove(struct i2c_client *client) -{ - struct pca953x_platform_data *pdata = client->dev.platform_data; - struct pca953x_chip *chip = i2c_get_clientdata(client); - int ret = 0; - - if (pdata->teardown) { - ret = pdata->teardown(client, chip->gpio_chip.base, - chip->gpio_chip.ngpio, pdata->context); - if (ret < 0) { - dev_err(&client->dev, "%s failed, %d\n", - "teardown", ret); - return ret; - } - } - - ret = gpiochip_remove(&chip->gpio_chip); - if (ret) { - dev_err(&client->dev, "%s failed, %d\n", - "gpiochip_remove()", ret); - return ret; - } - - pca953x_irq_teardown(chip); - kfree(chip->dyn_pdata); - kfree(chip); - return 0; -} - -static struct i2c_driver pca953x_driver = { - .driver = { - .name = "pca953x", - }, - .probe = pca953x_probe, - .remove = pca953x_remove, - .id_table = pca953x_id, -}; - -static int __init pca953x_init(void) -{ - return i2c_add_driver(&pca953x_driver); -} -/* register after i2c postcore initcall and before - * subsys initcalls that may rely on these GPIOs - */ -subsys_initcall(pca953x_init); - -static void __exit pca953x_exit(void) -{ - i2c_del_driver(&pca953x_driver); -} -module_exit(pca953x_exit); - -MODULE_AUTHOR("eric miao "); -MODULE_DESCRIPTION("GPIO expander driver for PCA953x"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c deleted file mode 100644 index 879b473aab5..00000000000 --- a/drivers/gpio/pcf857x.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * pcf857x - driver for pcf857x, pca857x, and pca967x I2C GPIO expanders - * - * Copyright (C) 2007 David Brownell - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include - - -static const struct i2c_device_id pcf857x_id[] = { - { "pcf8574", 8 }, - { "pcf8574a", 8 }, - { "pca8574", 8 }, - { "pca9670", 8 }, - { "pca9672", 8 }, - { "pca9674", 8 }, - { "pcf8575", 16 }, - { "pca8575", 16 }, - { "pca9671", 16 }, - { "pca9673", 16 }, - { "pca9675", 16 }, - { "max7328", 8 }, - { "max7329", 8 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, pcf857x_id); - -/* - * The pcf857x, pca857x, and pca967x chips only expose one read and one - * write register. Writing a "one" bit (to match the reset state) lets - * that pin be used as an input; it's not an open-drain model, but acts - * a bit like one. This is described as "quasi-bidirectional"; read the - * chip documentation for details. - * - * Many other I2C GPIO expander chips (like the pca953x models) have - * more complex register models and more conventional circuitry using - * push/pull drivers. They often use the same 0x20..0x27 addresses as - * pcf857x parts, making the "legacy" I2C driver model problematic. - */ -struct pcf857x { - struct gpio_chip chip; - struct i2c_client *client; - struct mutex lock; /* protect 'out' */ - unsigned out; /* software latch */ -}; - -/*-------------------------------------------------------------------------*/ - -/* Talk to 8-bit I/O expander */ - -static int pcf857x_input8(struct gpio_chip *chip, unsigned offset) -{ - struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); - int status; - - mutex_lock(&gpio->lock); - gpio->out |= (1 << offset); - status = i2c_smbus_write_byte(gpio->client, gpio->out); - mutex_unlock(&gpio->lock); - - return status; -} - -static int pcf857x_get8(struct gpio_chip *chip, unsigned offset) -{ - struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); - s32 value; - - value = i2c_smbus_read_byte(gpio->client); - return (value < 0) ? 0 : (value & (1 << offset)); -} - -static int pcf857x_output8(struct gpio_chip *chip, unsigned offset, int value) -{ - struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); - unsigned bit = 1 << offset; - int status; - - mutex_lock(&gpio->lock); - if (value) - gpio->out |= bit; - else - gpio->out &= ~bit; - status = i2c_smbus_write_byte(gpio->client, gpio->out); - mutex_unlock(&gpio->lock); - - return status; -} - -static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value) -{ - pcf857x_output8(chip, offset, value); -} - -/*-------------------------------------------------------------------------*/ - -/* Talk to 16-bit I/O expander */ - -static int i2c_write_le16(struct i2c_client *client, u16 word) -{ - u8 buf[2] = { word & 0xff, word >> 8, }; - int status; - - status = i2c_master_send(client, buf, 2); - return (status < 0) ? status : 0; -} - -static int i2c_read_le16(struct i2c_client *client) -{ - u8 buf[2]; - int status; - - status = i2c_master_recv(client, buf, 2); - if (status < 0) - return status; - return (buf[1] << 8) | buf[0]; -} - -static int pcf857x_input16(struct gpio_chip *chip, unsigned offset) -{ - struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); - int status; - - mutex_lock(&gpio->lock); - gpio->out |= (1 << offset); - status = i2c_write_le16(gpio->client, gpio->out); - mutex_unlock(&gpio->lock); - - return status; -} - -static int pcf857x_get16(struct gpio_chip *chip, unsigned offset) -{ - struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); - int value; - - value = i2c_read_le16(gpio->client); - return (value < 0) ? 0 : (value & (1 << offset)); -} - -static int pcf857x_output16(struct gpio_chip *chip, unsigned offset, int value) -{ - struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); - unsigned bit = 1 << offset; - int status; - - mutex_lock(&gpio->lock); - if (value) - gpio->out |= bit; - else - gpio->out &= ~bit; - status = i2c_write_le16(gpio->client, gpio->out); - mutex_unlock(&gpio->lock); - - return status; -} - -static void pcf857x_set16(struct gpio_chip *chip, unsigned offset, int value) -{ - pcf857x_output16(chip, offset, value); -} - -/*-------------------------------------------------------------------------*/ - -static int pcf857x_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct pcf857x_platform_data *pdata; - struct pcf857x *gpio; - int status; - - pdata = client->dev.platform_data; - if (!pdata) { - dev_dbg(&client->dev, "no platform data\n"); - } - - /* Allocate, initialize, and register this gpio_chip. */ - gpio = kzalloc(sizeof *gpio, GFP_KERNEL); - if (!gpio) - return -ENOMEM; - - mutex_init(&gpio->lock); - - gpio->chip.base = pdata ? pdata->gpio_base : -1; - gpio->chip.can_sleep = 1; - gpio->chip.dev = &client->dev; - gpio->chip.owner = THIS_MODULE; - - /* NOTE: the OnSemi jlc1562b is also largely compatible with - * these parts, notably for output. It has a low-resolution - * DAC instead of pin change IRQs; and its inputs can be the - * result of comparators. - */ - - /* 8574 addresses are 0x20..0x27; 8574a uses 0x38..0x3f; - * 9670, 9672, 9764, and 9764a use quite a variety. - * - * NOTE: we don't distinguish here between *4 and *4a parts. - */ - gpio->chip.ngpio = id->driver_data; - if (gpio->chip.ngpio == 8) { - gpio->chip.direction_input = pcf857x_input8; - gpio->chip.get = pcf857x_get8; - gpio->chip.direction_output = pcf857x_output8; - gpio->chip.set = pcf857x_set8; - - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE)) - status = -EIO; - - /* fail if there's no chip present */ - else - status = i2c_smbus_read_byte(client); - - /* '75/'75c addresses are 0x20..0x27, just like the '74; - * the '75c doesn't have a current source pulling high. - * 9671, 9673, and 9765 use quite a variety of addresses. - * - * NOTE: we don't distinguish here between '75 and '75c parts. - */ - } else if (gpio->chip.ngpio == 16) { - gpio->chip.direction_input = pcf857x_input16; - gpio->chip.get = pcf857x_get16; - gpio->chip.direction_output = pcf857x_output16; - gpio->chip.set = pcf857x_set16; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) - status = -EIO; - - /* fail if there's no chip present */ - else - status = i2c_read_le16(client); - - } else { - dev_dbg(&client->dev, "unsupported number of gpios\n"); - status = -EINVAL; - } - - if (status < 0) - goto fail; - - gpio->chip.label = client->name; - - gpio->client = client; - i2c_set_clientdata(client, gpio); - - /* NOTE: these chips have strange "quasi-bidirectional" I/O pins. - * We can't actually know whether a pin is configured (a) as output - * and driving the signal low, or (b) as input and reporting a low - * value ... without knowing the last value written since the chip - * came out of reset (if any). We can't read the latched output. - * - * In short, the only reliable solution for setting up pin direction - * is to do it explicitly. The setup() method can do that, but it - * may cause transient glitching since it can't know the last value - * written (some pins may need to be driven low). - * - * Using pdata->n_latch avoids that trouble. When left initialized - * to zero, our software copy of the "latch" then matches the chip's - * all-ones reset state. Otherwise it flags pins to be driven low. - */ - gpio->out = pdata ? ~pdata->n_latch : ~0; - - status = gpiochip_add(&gpio->chip); - if (status < 0) - goto fail; - - /* NOTE: these chips can issue "some pin-changed" IRQs, which we - * don't yet even try to use. Among other issues, the relevant - * genirq state isn't available to modular drivers; and most irq - * methods can't be called from sleeping contexts. - */ - - dev_info(&client->dev, "gpios %d..%d on a %s%s\n", - gpio->chip.base, - gpio->chip.base + gpio->chip.ngpio - 1, - client->name, - client->irq ? " (irq ignored)" : ""); - - /* Let platform code set up the GPIOs and their users. - * Now is the first time anyone could use them. - */ - if (pdata && pdata->setup) { - status = pdata->setup(client, - gpio->chip.base, gpio->chip.ngpio, - pdata->context); - if (status < 0) - dev_warn(&client->dev, "setup --> %d\n", status); - } - - return 0; - -fail: - dev_dbg(&client->dev, "probe error %d for '%s'\n", - status, client->name); - kfree(gpio); - return status; -} - -static int pcf857x_remove(struct i2c_client *client) -{ - struct pcf857x_platform_data *pdata = client->dev.platform_data; - struct pcf857x *gpio = i2c_get_clientdata(client); - int status = 0; - - if (pdata && pdata->teardown) { - status = pdata->teardown(client, - gpio->chip.base, gpio->chip.ngpio, - pdata->context); - if (status < 0) { - dev_err(&client->dev, "%s --> %d\n", - "teardown", status); - return status; - } - } - - status = gpiochip_remove(&gpio->chip); - if (status == 0) - kfree(gpio); - else - dev_err(&client->dev, "%s --> %d\n", "remove", status); - return status; -} - -static struct i2c_driver pcf857x_driver = { - .driver = { - .name = "pcf857x", - .owner = THIS_MODULE, - }, - .probe = pcf857x_probe, - .remove = pcf857x_remove, - .id_table = pcf857x_id, -}; - -static int __init pcf857x_init(void) -{ - return i2c_add_driver(&pcf857x_driver); -} -/* register after i2c postcore initcall and before - * subsys initcalls that may rely on these GPIOs - */ -subsys_initcall(pcf857x_init); - -static void __exit pcf857x_exit(void) -{ - i2c_del_driver(&pcf857x_driver); -} -module_exit(pcf857x_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Brownell"); diff --git a/drivers/gpio/pch_gpio.c b/drivers/gpio/pch_gpio.c deleted file mode 100644 index 36919e77c49..00000000000 --- a/drivers/gpio/pch_gpio.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. - */ -#include -#include -#include - -#define PCH_GPIO_ALL_PINS 0xfff /* Mask for GPIO pins 0 to 11 */ -#define GPIO_NUM_PINS 12 /* Specifies number of GPIO PINS GPIO0-GPIO11 */ - -struct pch_regs { - u32 ien; - u32 istatus; - u32 idisp; - u32 iclr; - u32 imask; - u32 imaskclr; - u32 po; - u32 pi; - u32 pm; - u32 im0; - u32 im1; - u32 reserved[4]; - u32 reset; -}; - -/** - * struct pch_gpio_reg_data - The register store data. - * @po_reg: To store contents of PO register. - * @pm_reg: To store contents of PM register. - */ -struct pch_gpio_reg_data { - u32 po_reg; - u32 pm_reg; -}; - -/** - * struct pch_gpio - GPIO private data structure. - * @base: PCI base address of Memory mapped I/O register. - * @reg: Memory mapped PCH GPIO register list. - * @dev: Pointer to device structure. - * @gpio: Data for GPIO infrastructure. - * @pch_gpio_reg: Memory mapped Register data is saved here - * when suspend. - */ -struct pch_gpio { - void __iomem *base; - struct pch_regs __iomem *reg; - struct device *dev; - struct gpio_chip gpio; - struct pch_gpio_reg_data pch_gpio_reg; - struct mutex lock; -}; - -static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) -{ - u32 reg_val; - struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio); - - mutex_lock(&chip->lock); - reg_val = ioread32(&chip->reg->po); - if (val) - reg_val |= (1 << nr); - else - reg_val &= ~(1 << nr); - - iowrite32(reg_val, &chip->reg->po); - mutex_unlock(&chip->lock); -} - -static int pch_gpio_get(struct gpio_chip *gpio, unsigned nr) -{ - struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio); - - return ioread32(&chip->reg->pi) & (1 << nr); -} - -static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, - int val) -{ - struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio); - u32 pm; - u32 reg_val; - - mutex_lock(&chip->lock); - pm = ioread32(&chip->reg->pm) & PCH_GPIO_ALL_PINS; - pm |= (1 << nr); - iowrite32(pm, &chip->reg->pm); - - reg_val = ioread32(&chip->reg->po); - if (val) - reg_val |= (1 << nr); - else - reg_val &= ~(1 << nr); - iowrite32(reg_val, &chip->reg->po); - - mutex_unlock(&chip->lock); - - return 0; -} - -static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) -{ - struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio); - u32 pm; - - mutex_lock(&chip->lock); - pm = ioread32(&chip->reg->pm) & PCH_GPIO_ALL_PINS; /*bits 0-11*/ - pm &= ~(1 << nr); - iowrite32(pm, &chip->reg->pm); - mutex_unlock(&chip->lock); - - return 0; -} - -/* - * Save register configuration and disable interrupts. - */ -static void pch_gpio_save_reg_conf(struct pch_gpio *chip) -{ - chip->pch_gpio_reg.po_reg = ioread32(&chip->reg->po); - chip->pch_gpio_reg.pm_reg = ioread32(&chip->reg->pm); -} - -/* - * This function restores the register configuration of the GPIO device. - */ -static void pch_gpio_restore_reg_conf(struct pch_gpio *chip) -{ - /* to store contents of PO register */ - iowrite32(chip->pch_gpio_reg.po_reg, &chip->reg->po); - /* to store contents of PM register */ - iowrite32(chip->pch_gpio_reg.pm_reg, &chip->reg->pm); -} - -static void pch_gpio_setup(struct pch_gpio *chip) -{ - struct gpio_chip *gpio = &chip->gpio; - - gpio->label = dev_name(chip->dev); - gpio->owner = THIS_MODULE; - gpio->direction_input = pch_gpio_direction_input; - gpio->get = pch_gpio_get; - gpio->direction_output = pch_gpio_direction_output; - gpio->set = pch_gpio_set; - gpio->dbg_show = NULL; - gpio->base = -1; - gpio->ngpio = GPIO_NUM_PINS; - gpio->can_sleep = 0; -} - -static int __devinit pch_gpio_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - s32 ret; - struct pch_gpio *chip; - - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (chip == NULL) - return -ENOMEM; - - chip->dev = &pdev->dev; - ret = pci_enable_device(pdev); - if (ret) { - dev_err(&pdev->dev, "%s : pci_enable_device FAILED", __func__); - goto err_pci_enable; - } - - ret = pci_request_regions(pdev, KBUILD_MODNAME); - if (ret) { - dev_err(&pdev->dev, "pci_request_regions FAILED-%d", ret); - goto err_request_regions; - } - - chip->base = pci_iomap(pdev, 1, 0); - if (chip->base == 0) { - dev_err(&pdev->dev, "%s : pci_iomap FAILED", __func__); - ret = -ENOMEM; - goto err_iomap; - } - - chip->reg = chip->base; - pci_set_drvdata(pdev, chip); - mutex_init(&chip->lock); - pch_gpio_setup(chip); - ret = gpiochip_add(&chip->gpio); - if (ret) { - dev_err(&pdev->dev, "PCH gpio: Failed to register GPIO\n"); - goto err_gpiochip_add; - } - - return 0; - -err_gpiochip_add: - pci_iounmap(pdev, chip->base); - -err_iomap: - pci_release_regions(pdev); - -err_request_regions: - pci_disable_device(pdev); - -err_pci_enable: - kfree(chip); - dev_err(&pdev->dev, "%s Failed returns %d\n", __func__, ret); - return ret; -} - -static void __devexit pch_gpio_remove(struct pci_dev *pdev) -{ - int err; - struct pch_gpio *chip = pci_get_drvdata(pdev); - - err = gpiochip_remove(&chip->gpio); - if (err) - dev_err(&pdev->dev, "Failed gpiochip_remove\n"); - - pci_iounmap(pdev, chip->base); - pci_release_regions(pdev); - pci_disable_device(pdev); - kfree(chip); -} - -#ifdef CONFIG_PM -static int pch_gpio_suspend(struct pci_dev *pdev, pm_message_t state) -{ - s32 ret; - struct pch_gpio *chip = pci_get_drvdata(pdev); - - pch_gpio_save_reg_conf(chip); - pch_gpio_restore_reg_conf(chip); - - ret = pci_save_state(pdev); - if (ret) { - dev_err(&pdev->dev, "pci_save_state Failed-%d\n", ret); - return ret; - } - pci_disable_device(pdev); - pci_set_power_state(pdev, PCI_D0); - ret = pci_enable_wake(pdev, PCI_D0, 1); - if (ret) - dev_err(&pdev->dev, "pci_enable_wake Failed -%d\n", ret); - - return 0; -} - -static int pch_gpio_resume(struct pci_dev *pdev) -{ - s32 ret; - struct pch_gpio *chip = pci_get_drvdata(pdev); - - ret = pci_enable_wake(pdev, PCI_D0, 0); - - pci_set_power_state(pdev, PCI_D0); - ret = pci_enable_device(pdev); - if (ret) { - dev_err(&pdev->dev, "pci_enable_device Failed-%d ", ret); - return ret; - } - pci_restore_state(pdev); - - iowrite32(0x01, &chip->reg->reset); - iowrite32(0x00, &chip->reg->reset); - pch_gpio_restore_reg_conf(chip); - - return 0; -} -#else -#define pch_gpio_suspend NULL -#define pch_gpio_resume NULL -#endif - -#define PCI_VENDOR_ID_ROHM 0x10DB -static DEFINE_PCI_DEVICE_TABLE(pch_gpio_pcidev_id) = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8803) }, - { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8014) }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, pch_gpio_pcidev_id); - -static struct pci_driver pch_gpio_driver = { - .name = "pch_gpio", - .id_table = pch_gpio_pcidev_id, - .probe = pch_gpio_probe, - .remove = __devexit_p(pch_gpio_remove), - .suspend = pch_gpio_suspend, - .resume = pch_gpio_resume -}; - -static int __init pch_gpio_pci_init(void) -{ - return pci_register_driver(&pch_gpio_driver); -} -module_init(pch_gpio_pci_init); - -static void __exit pch_gpio_pci_exit(void) -{ - pci_unregister_driver(&pch_gpio_driver); -} -module_exit(pch_gpio_pci_exit); - -MODULE_DESCRIPTION("PCH GPIO PCI Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/pl061.c b/drivers/gpio/pl061.c deleted file mode 100644 index 6fcb28cdd86..00000000000 --- a/drivers/gpio/pl061.c +++ /dev/null @@ -1,360 +0,0 @@ -/* - * linux/drivers/gpio/pl061.c - * - * 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 - * published by the Free Software Foundation. - * - * Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061) - * - * Data sheet: ARM DDI 0190B, September 2000 - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define GPIODIR 0x400 -#define GPIOIS 0x404 -#define GPIOIBE 0x408 -#define GPIOIEV 0x40C -#define GPIOIE 0x410 -#define GPIORIS 0x414 -#define GPIOMIS 0x418 -#define GPIOIC 0x41C - -#define PL061_GPIO_NR 8 - -struct pl061_gpio { - /* We use a list of pl061_gpio structs for each trigger IRQ in the main - * interrupts controller of the system. We need this to support systems - * in which more that one PL061s are connected to the same IRQ. The ISR - * interates through this list to find the source of the interrupt. - */ - struct list_head list; - - /* Each of the two spinlocks protects a different set of hardware - * regiters and data structurs. This decouples the code of the IRQ from - * the GPIO code. This also makes the case of a GPIO routine call from - * the IRQ code simpler. - */ - spinlock_t lock; /* GPIO registers */ - spinlock_t irq_lock; /* IRQ registers */ - - void __iomem *base; - unsigned irq_base; - struct gpio_chip gc; -}; - -static int pl061_direction_input(struct gpio_chip *gc, unsigned offset) -{ - struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); - unsigned long flags; - unsigned char gpiodir; - - if (offset >= gc->ngpio) - return -EINVAL; - - spin_lock_irqsave(&chip->lock, flags); - gpiodir = readb(chip->base + GPIODIR); - gpiodir &= ~(1 << offset); - writeb(gpiodir, chip->base + GPIODIR); - spin_unlock_irqrestore(&chip->lock, flags); - - return 0; -} - -static int pl061_direction_output(struct gpio_chip *gc, unsigned offset, - int value) -{ - struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); - unsigned long flags; - unsigned char gpiodir; - - if (offset >= gc->ngpio) - return -EINVAL; - - spin_lock_irqsave(&chip->lock, flags); - writeb(!!value << offset, chip->base + (1 << (offset + 2))); - gpiodir = readb(chip->base + GPIODIR); - gpiodir |= 1 << offset; - writeb(gpiodir, chip->base + GPIODIR); - - /* - * gpio value is set again, because pl061 doesn't allow to set value of - * a gpio pin before configuring it in OUT mode. - */ - writeb(!!value << offset, chip->base + (1 << (offset + 2))); - spin_unlock_irqrestore(&chip->lock, flags); - - return 0; -} - -static int pl061_get_value(struct gpio_chip *gc, unsigned offset) -{ - struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); - - return !!readb(chip->base + (1 << (offset + 2))); -} - -static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value) -{ - struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); - - writeb(!!value << offset, chip->base + (1 << (offset + 2))); -} - -static int pl061_to_irq(struct gpio_chip *gc, unsigned offset) -{ - struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); - - if (chip->irq_base == (unsigned) -1) - return -EINVAL; - - return chip->irq_base + offset; -} - -/* - * PL061 GPIO IRQ - */ -static void pl061_irq_disable(struct irq_data *d) -{ - struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); - int offset = d->irq - chip->irq_base; - unsigned long flags; - u8 gpioie; - - spin_lock_irqsave(&chip->irq_lock, flags); - gpioie = readb(chip->base + GPIOIE); - gpioie &= ~(1 << offset); - writeb(gpioie, chip->base + GPIOIE); - spin_unlock_irqrestore(&chip->irq_lock, flags); -} - -static void pl061_irq_enable(struct irq_data *d) -{ - struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); - int offset = d->irq - chip->irq_base; - unsigned long flags; - u8 gpioie; - - spin_lock_irqsave(&chip->irq_lock, flags); - gpioie = readb(chip->base + GPIOIE); - gpioie |= 1 << offset; - writeb(gpioie, chip->base + GPIOIE); - spin_unlock_irqrestore(&chip->irq_lock, flags); -} - -static int pl061_irq_type(struct irq_data *d, unsigned trigger) -{ - struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); - int offset = d->irq - chip->irq_base; - unsigned long flags; - u8 gpiois, gpioibe, gpioiev; - - if (offset < 0 || offset >= PL061_GPIO_NR) - return -EINVAL; - - spin_lock_irqsave(&chip->irq_lock, flags); - - gpioiev = readb(chip->base + GPIOIEV); - - gpiois = readb(chip->base + GPIOIS); - if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { - gpiois |= 1 << offset; - if (trigger & IRQ_TYPE_LEVEL_HIGH) - gpioiev |= 1 << offset; - else - gpioiev &= ~(1 << offset); - } else - gpiois &= ~(1 << offset); - writeb(gpiois, chip->base + GPIOIS); - - gpioibe = readb(chip->base + GPIOIBE); - if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) - gpioibe |= 1 << offset; - else { - gpioibe &= ~(1 << offset); - if (trigger & IRQ_TYPE_EDGE_RISING) - gpioiev |= 1 << offset; - else if (trigger & IRQ_TYPE_EDGE_FALLING) - gpioiev &= ~(1 << offset); - } - writeb(gpioibe, chip->base + GPIOIBE); - - writeb(gpioiev, chip->base + GPIOIEV); - - spin_unlock_irqrestore(&chip->irq_lock, flags); - - return 0; -} - -static struct irq_chip pl061_irqchip = { - .name = "GPIO", - .irq_enable = pl061_irq_enable, - .irq_disable = pl061_irq_disable, - .irq_set_type = pl061_irq_type, -}; - -static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) -{ - struct list_head *chip_list = irq_get_handler_data(irq); - struct list_head *ptr; - struct pl061_gpio *chip; - - desc->irq_data.chip->irq_ack(&desc->irq_data); - list_for_each(ptr, chip_list) { - unsigned long pending; - int offset; - - chip = list_entry(ptr, struct pl061_gpio, list); - pending = readb(chip->base + GPIOMIS); - writeb(pending, chip->base + GPIOIC); - - if (pending == 0) - continue; - - for_each_set_bit(offset, &pending, PL061_GPIO_NR) - generic_handle_irq(pl061_to_irq(&chip->gc, offset)); - } - desc->irq_data.chip->irq_unmask(&desc->irq_data); -} - -static int pl061_probe(struct amba_device *dev, const struct amba_id *id) -{ - struct pl061_platform_data *pdata; - struct pl061_gpio *chip; - struct list_head *chip_list; - int ret, irq, i; - static DECLARE_BITMAP(init_irq, NR_IRQS); - - pdata = dev->dev.platform_data; - if (pdata == NULL) - return -ENODEV; - - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (chip == NULL) - return -ENOMEM; - - if (!request_mem_region(dev->res.start, - resource_size(&dev->res), "pl061")) { - ret = -EBUSY; - goto free_mem; - } - - chip->base = ioremap(dev->res.start, resource_size(&dev->res)); - if (chip->base == NULL) { - ret = -ENOMEM; - goto release_region; - } - - spin_lock_init(&chip->lock); - spin_lock_init(&chip->irq_lock); - INIT_LIST_HEAD(&chip->list); - - chip->gc.direction_input = pl061_direction_input; - chip->gc.direction_output = pl061_direction_output; - chip->gc.get = pl061_get_value; - chip->gc.set = pl061_set_value; - chip->gc.to_irq = pl061_to_irq; - chip->gc.base = pdata->gpio_base; - chip->gc.ngpio = PL061_GPIO_NR; - chip->gc.label = dev_name(&dev->dev); - chip->gc.dev = &dev->dev; - chip->gc.owner = THIS_MODULE; - - chip->irq_base = pdata->irq_base; - - ret = gpiochip_add(&chip->gc); - if (ret) - goto iounmap; - - /* - * irq_chip support - */ - - if (chip->irq_base == (unsigned) -1) - return 0; - - writeb(0, chip->base + GPIOIE); /* disable irqs */ - irq = dev->irq[0]; - if (irq < 0) { - ret = -ENODEV; - goto iounmap; - } - irq_set_chained_handler(irq, pl061_irq_handler); - if (!test_and_set_bit(irq, init_irq)) { /* list initialized? */ - chip_list = kmalloc(sizeof(*chip_list), GFP_KERNEL); - if (chip_list == NULL) { - clear_bit(irq, init_irq); - ret = -ENOMEM; - goto iounmap; - } - INIT_LIST_HEAD(chip_list); - irq_set_handler_data(irq, chip_list); - } else - chip_list = irq_get_handler_data(irq); - list_add(&chip->list, chip_list); - - for (i = 0; i < PL061_GPIO_NR; i++) { - if (pdata->directions & (1 << i)) - pl061_direction_output(&chip->gc, i, - pdata->values & (1 << i)); - else - pl061_direction_input(&chip->gc, i); - - irq_set_chip_and_handler(i + chip->irq_base, &pl061_irqchip, - handle_simple_irq); - set_irq_flags(i+chip->irq_base, IRQF_VALID); - irq_set_chip_data(i + chip->irq_base, chip); - } - - return 0; - -iounmap: - iounmap(chip->base); -release_region: - release_mem_region(dev->res.start, resource_size(&dev->res)); -free_mem: - kfree(chip); - - return ret; -} - -static struct amba_id pl061_ids[] = { - { - .id = 0x00041061, - .mask = 0x000fffff, - }, - { 0, 0 }, -}; - -static struct amba_driver pl061_gpio_driver = { - .drv = { - .name = "pl061_gpio", - }, - .id_table = pl061_ids, - .probe = pl061_probe, -}; - -static int __init pl061_gpio_init(void) -{ - return amba_driver_register(&pl061_gpio_driver); -} -subsys_initcall(pl061_gpio_init); - -MODULE_AUTHOR("Baruch Siach "); -MODULE_DESCRIPTION("PL061 GPIO driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/rdc321x-gpio.c b/drivers/gpio/rdc321x-gpio.c deleted file mode 100644 index 2762698e020..00000000000 --- a/drivers/gpio/rdc321x-gpio.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * RDC321x GPIO driver - * - * Copyright (C) 2008, Volker Weiss - * Copyright (C) 2007-2010 Florian Fainelli - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct rdc321x_gpio { - spinlock_t lock; - struct pci_dev *sb_pdev; - u32 data_reg[2]; - int reg1_ctrl_base; - int reg1_data_base; - int reg2_ctrl_base; - int reg2_data_base; - struct gpio_chip chip; -}; - -/* read GPIO pin */ -static int rdc_gpio_get_value(struct gpio_chip *chip, unsigned gpio) -{ - struct rdc321x_gpio *gpch; - u32 value = 0; - int reg; - - gpch = container_of(chip, struct rdc321x_gpio, chip); - reg = gpio < 32 ? gpch->reg1_data_base : gpch->reg2_data_base; - - spin_lock(&gpch->lock); - pci_write_config_dword(gpch->sb_pdev, reg, - gpch->data_reg[gpio < 32 ? 0 : 1]); - pci_read_config_dword(gpch->sb_pdev, reg, &value); - spin_unlock(&gpch->lock); - - return (1 << (gpio & 0x1f)) & value ? 1 : 0; -} - -static void rdc_gpio_set_value_impl(struct gpio_chip *chip, - unsigned gpio, int value) -{ - struct rdc321x_gpio *gpch; - int reg = (gpio < 32) ? 0 : 1; - - gpch = container_of(chip, struct rdc321x_gpio, chip); - - if (value) - gpch->data_reg[reg] |= 1 << (gpio & 0x1f); - else - gpch->data_reg[reg] &= ~(1 << (gpio & 0x1f)); - - pci_write_config_dword(gpch->sb_pdev, - reg ? gpch->reg2_data_base : gpch->reg1_data_base, - gpch->data_reg[reg]); -} - -/* set GPIO pin to value */ -static void rdc_gpio_set_value(struct gpio_chip *chip, - unsigned gpio, int value) -{ - struct rdc321x_gpio *gpch; - - gpch = container_of(chip, struct rdc321x_gpio, chip); - spin_lock(&gpch->lock); - rdc_gpio_set_value_impl(chip, gpio, value); - spin_unlock(&gpch->lock); -} - -static int rdc_gpio_config(struct gpio_chip *chip, - unsigned gpio, int value) -{ - struct rdc321x_gpio *gpch; - int err; - u32 reg; - - gpch = container_of(chip, struct rdc321x_gpio, chip); - - spin_lock(&gpch->lock); - err = pci_read_config_dword(gpch->sb_pdev, gpio < 32 ? - gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, ®); - if (err) - goto unlock; - - reg |= 1 << (gpio & 0x1f); - - err = pci_write_config_dword(gpch->sb_pdev, gpio < 32 ? - gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, reg); - if (err) - goto unlock; - - rdc_gpio_set_value_impl(chip, gpio, value); - -unlock: - spin_unlock(&gpch->lock); - - return err; -} - -/* configure GPIO pin as input */ -static int rdc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) -{ - return rdc_gpio_config(chip, gpio, 1); -} - -/* - * Cache the initial value of both GPIO data registers - */ -static int __devinit rdc321x_gpio_probe(struct platform_device *pdev) -{ - int err; - struct resource *r; - struct rdc321x_gpio *rdc321x_gpio_dev; - struct rdc321x_gpio_pdata *pdata; - - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "no platform data supplied\n"); - return -ENODEV; - } - - rdc321x_gpio_dev = kzalloc(sizeof(struct rdc321x_gpio), GFP_KERNEL); - if (!rdc321x_gpio_dev) { - dev_err(&pdev->dev, "failed to allocate private data\n"); - return -ENOMEM; - } - - r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg1"); - if (!r) { - dev_err(&pdev->dev, "failed to get gpio-reg1 resource\n"); - err = -ENODEV; - goto out_free; - } - - spin_lock_init(&rdc321x_gpio_dev->lock); - rdc321x_gpio_dev->sb_pdev = pdata->sb_pdev; - rdc321x_gpio_dev->reg1_ctrl_base = r->start; - rdc321x_gpio_dev->reg1_data_base = r->start + 0x4; - - r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg2"); - if (!r) { - dev_err(&pdev->dev, "failed to get gpio-reg2 resource\n"); - err = -ENODEV; - goto out_free; - } - - rdc321x_gpio_dev->reg2_ctrl_base = r->start; - rdc321x_gpio_dev->reg2_data_base = r->start + 0x4; - - rdc321x_gpio_dev->chip.label = "rdc321x-gpio"; - rdc321x_gpio_dev->chip.direction_input = rdc_gpio_direction_input; - rdc321x_gpio_dev->chip.direction_output = rdc_gpio_config; - rdc321x_gpio_dev->chip.get = rdc_gpio_get_value; - rdc321x_gpio_dev->chip.set = rdc_gpio_set_value; - rdc321x_gpio_dev->chip.base = 0; - rdc321x_gpio_dev->chip.ngpio = pdata->max_gpios; - - platform_set_drvdata(pdev, rdc321x_gpio_dev); - - /* This might not be, what others (BIOS, bootloader, etc.) - wrote to these registers before, but it's a good guess. Still - better than just using 0xffffffff. */ - err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev, - rdc321x_gpio_dev->reg1_data_base, - &rdc321x_gpio_dev->data_reg[0]); - if (err) - goto out_drvdata; - - err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev, - rdc321x_gpio_dev->reg2_data_base, - &rdc321x_gpio_dev->data_reg[1]); - if (err) - goto out_drvdata; - - dev_info(&pdev->dev, "registering %d GPIOs\n", - rdc321x_gpio_dev->chip.ngpio); - return gpiochip_add(&rdc321x_gpio_dev->chip); - -out_drvdata: - platform_set_drvdata(pdev, NULL); -out_free: - kfree(rdc321x_gpio_dev); - return err; -} - -static int __devexit rdc321x_gpio_remove(struct platform_device *pdev) -{ - int ret; - struct rdc321x_gpio *rdc321x_gpio_dev = platform_get_drvdata(pdev); - - ret = gpiochip_remove(&rdc321x_gpio_dev->chip); - if (ret) - dev_err(&pdev->dev, "failed to unregister chip\n"); - - kfree(rdc321x_gpio_dev); - platform_set_drvdata(pdev, NULL); - - return ret; -} - -static struct platform_driver rdc321x_gpio_driver = { - .driver.name = "rdc321x-gpio", - .driver.owner = THIS_MODULE, - .probe = rdc321x_gpio_probe, - .remove = __devexit_p(rdc321x_gpio_remove), -}; - -static int __init rdc321x_gpio_init(void) -{ - return platform_driver_register(&rdc321x_gpio_driver); -} - -static void __exit rdc321x_gpio_exit(void) -{ - platform_driver_unregister(&rdc321x_gpio_driver); -} - -module_init(rdc321x_gpio_init); -module_exit(rdc321x_gpio_exit); - -MODULE_AUTHOR("Florian Fainelli "); -MODULE_DESCRIPTION("RDC321x GPIO driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:rdc321x-gpio"); diff --git a/drivers/gpio/sch_gpio.c b/drivers/gpio/sch_gpio.c deleted file mode 100644 index 56060421cdf..00000000000 --- a/drivers/gpio/sch_gpio.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * sch_gpio.c - GPIO interface for Intel Poulsbo SCH - * - * Copyright (c) 2010 CompuLab Ltd - * Author: Denis Turischev - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License 2 as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -static DEFINE_SPINLOCK(gpio_lock); - -#define CGEN (0x00) -#define CGIO (0x04) -#define CGLV (0x08) - -#define RGEN (0x20) -#define RGIO (0x24) -#define RGLV (0x28) - -static unsigned short gpio_ba; - -static int sch_gpio_core_direction_in(struct gpio_chip *gc, unsigned gpio_num) -{ - u8 curr_dirs; - unsigned short offset, bit; - - spin_lock(&gpio_lock); - - offset = CGIO + gpio_num / 8; - bit = gpio_num % 8; - - curr_dirs = inb(gpio_ba + offset); - - if (!(curr_dirs & (1 << bit))) - outb(curr_dirs | (1 << bit), gpio_ba + offset); - - spin_unlock(&gpio_lock); - return 0; -} - -static int sch_gpio_core_get(struct gpio_chip *gc, unsigned gpio_num) -{ - int res; - unsigned short offset, bit; - - offset = CGLV + gpio_num / 8; - bit = gpio_num % 8; - - res = !!(inb(gpio_ba + offset) & (1 << bit)); - return res; -} - -static void sch_gpio_core_set(struct gpio_chip *gc, unsigned gpio_num, int val) -{ - u8 curr_vals; - unsigned short offset, bit; - - spin_lock(&gpio_lock); - - offset = CGLV + gpio_num / 8; - bit = gpio_num % 8; - - curr_vals = inb(gpio_ba + offset); - - if (val) - outb(curr_vals | (1 << bit), gpio_ba + offset); - else - outb((curr_vals & ~(1 << bit)), gpio_ba + offset); - spin_unlock(&gpio_lock); -} - -static int sch_gpio_core_direction_out(struct gpio_chip *gc, - unsigned gpio_num, int val) -{ - u8 curr_dirs; - unsigned short offset, bit; - - sch_gpio_core_set(gc, gpio_num, val); - - spin_lock(&gpio_lock); - - offset = CGIO + gpio_num / 8; - bit = gpio_num % 8; - - curr_dirs = inb(gpio_ba + offset); - if (curr_dirs & (1 << bit)) - outb(curr_dirs & ~(1 << bit), gpio_ba + offset); - - spin_unlock(&gpio_lock); - return 0; -} - -static struct gpio_chip sch_gpio_core = { - .label = "sch_gpio_core", - .owner = THIS_MODULE, - .direction_input = sch_gpio_core_direction_in, - .get = sch_gpio_core_get, - .direction_output = sch_gpio_core_direction_out, - .set = sch_gpio_core_set, -}; - -static int sch_gpio_resume_direction_in(struct gpio_chip *gc, - unsigned gpio_num) -{ - u8 curr_dirs; - - spin_lock(&gpio_lock); - - curr_dirs = inb(gpio_ba + RGIO); - - if (!(curr_dirs & (1 << gpio_num))) - outb(curr_dirs | (1 << gpio_num) , gpio_ba + RGIO); - - spin_unlock(&gpio_lock); - return 0; -} - -static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num) -{ - return !!(inb(gpio_ba + RGLV) & (1 << gpio_num)); -} - -static void sch_gpio_resume_set(struct gpio_chip *gc, - unsigned gpio_num, int val) -{ - u8 curr_vals; - - spin_lock(&gpio_lock); - - curr_vals = inb(gpio_ba + RGLV); - - if (val) - outb(curr_vals | (1 << gpio_num), gpio_ba + RGLV); - else - outb((curr_vals & ~(1 << gpio_num)), gpio_ba + RGLV); - - spin_unlock(&gpio_lock); -} - -static int sch_gpio_resume_direction_out(struct gpio_chip *gc, - unsigned gpio_num, int val) -{ - u8 curr_dirs; - - sch_gpio_resume_set(gc, gpio_num, val); - - spin_lock(&gpio_lock); - - curr_dirs = inb(gpio_ba + RGIO); - if (curr_dirs & (1 << gpio_num)) - outb(curr_dirs & ~(1 << gpio_num), gpio_ba + RGIO); - - spin_unlock(&gpio_lock); - return 0; -} - -static struct gpio_chip sch_gpio_resume = { - .label = "sch_gpio_resume", - .owner = THIS_MODULE, - .direction_input = sch_gpio_resume_direction_in, - .get = sch_gpio_resume_get, - .direction_output = sch_gpio_resume_direction_out, - .set = sch_gpio_resume_set, -}; - -static int __devinit sch_gpio_probe(struct platform_device *pdev) -{ - struct resource *res; - int err, id; - - id = pdev->id; - if (!id) - return -ENODEV; - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!res) - return -EBUSY; - - if (!request_region(res->start, resource_size(res), pdev->name)) - return -EBUSY; - - gpio_ba = res->start; - - switch (id) { - case PCI_DEVICE_ID_INTEL_SCH_LPC: - sch_gpio_core.base = 0; - sch_gpio_core.ngpio = 10; - - sch_gpio_resume.base = 10; - sch_gpio_resume.ngpio = 4; - - /* - * GPIO[6:0] enabled by default - * GPIO7 is configured by the CMC as SLPIOVR - * Enable GPIO[9:8] core powered gpios explicitly - */ - outb(0x3, gpio_ba + CGEN + 1); - /* - * SUS_GPIO[2:0] enabled by default - * Enable SUS_GPIO3 resume powered gpio explicitly - */ - outb(0x8, gpio_ba + RGEN); - break; - - case PCI_DEVICE_ID_INTEL_ITC_LPC: - sch_gpio_core.base = 0; - sch_gpio_core.ngpio = 5; - - sch_gpio_resume.base = 5; - sch_gpio_resume.ngpio = 9; - break; - - default: - return -ENODEV; - } - - sch_gpio_core.dev = &pdev->dev; - sch_gpio_resume.dev = &pdev->dev; - - err = gpiochip_add(&sch_gpio_core); - if (err < 0) - goto err_sch_gpio_core; - - err = gpiochip_add(&sch_gpio_resume); - if (err < 0) - goto err_sch_gpio_resume; - - return 0; - -err_sch_gpio_resume: - err = gpiochip_remove(&sch_gpio_core); - if (err) - dev_err(&pdev->dev, "%s failed, %d\n", - "gpiochip_remove()", err); - -err_sch_gpio_core: - release_region(res->start, resource_size(res)); - gpio_ba = 0; - - return err; -} - -static int __devexit sch_gpio_remove(struct platform_device *pdev) -{ - struct resource *res; - if (gpio_ba) { - int err; - - err = gpiochip_remove(&sch_gpio_core); - if (err) - dev_err(&pdev->dev, "%s failed, %d\n", - "gpiochip_remove()", err); - err = gpiochip_remove(&sch_gpio_resume); - if (err) - dev_err(&pdev->dev, "%s failed, %d\n", - "gpiochip_remove()", err); - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - - release_region(res->start, resource_size(res)); - gpio_ba = 0; - - return err; - } - - return 0; -} - -static struct platform_driver sch_gpio_driver = { - .driver = { - .name = "sch_gpio", - .owner = THIS_MODULE, - }, - .probe = sch_gpio_probe, - .remove = __devexit_p(sch_gpio_remove), -}; - -static int __init sch_gpio_init(void) -{ - return platform_driver_register(&sch_gpio_driver); -} - -static void __exit sch_gpio_exit(void) -{ - platform_driver_unregister(&sch_gpio_driver); -} - -module_init(sch_gpio_init); -module_exit(sch_gpio_exit); - -MODULE_AUTHOR("Denis Turischev "); -MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:sch_gpio"); diff --git a/drivers/gpio/stmpe-gpio.c b/drivers/gpio/stmpe-gpio.c deleted file mode 100644 index 4c980b57332..00000000000 --- a/drivers/gpio/stmpe-gpio.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * License Terms: GNU General Public License, version 2 - * Author: Rabin Vincent for ST-Ericsson - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * These registers are modified under the irq bus lock and cached to avoid - * unnecessary writes in bus_sync_unlock. - */ -enum { REG_RE, REG_FE, REG_IE }; - -#define CACHE_NR_REGS 3 -#define CACHE_NR_BANKS (STMPE_NR_GPIOS / 8) - -struct stmpe_gpio { - struct gpio_chip chip; - struct stmpe *stmpe; - struct device *dev; - struct mutex irq_lock; - - int irq_base; - unsigned norequest_mask; - - /* Caches of interrupt control registers for bus_lock */ - u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS]; - u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS]; -}; - -static inline struct stmpe_gpio *to_stmpe_gpio(struct gpio_chip *chip) -{ - return container_of(chip, struct stmpe_gpio, chip); -} - -static int stmpe_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); - struct stmpe *stmpe = stmpe_gpio->stmpe; - u8 reg = stmpe->regs[STMPE_IDX_GPMR_LSB] - (offset / 8); - u8 mask = 1 << (offset % 8); - int ret; - - ret = stmpe_reg_read(stmpe, reg); - if (ret < 0) - return ret; - - return ret & mask; -} - -static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val) -{ - struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); - struct stmpe *stmpe = stmpe_gpio->stmpe; - int which = val ? STMPE_IDX_GPSR_LSB : STMPE_IDX_GPCR_LSB; - u8 reg = stmpe->regs[which] - (offset / 8); - u8 mask = 1 << (offset % 8); - - stmpe_reg_write(stmpe, reg, mask); -} - -static int stmpe_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int val) -{ - struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); - struct stmpe *stmpe = stmpe_gpio->stmpe; - u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8); - u8 mask = 1 << (offset % 8); - - stmpe_gpio_set(chip, offset, val); - - return stmpe_set_bits(stmpe, reg, mask, mask); -} - -static int stmpe_gpio_direction_input(struct gpio_chip *chip, - unsigned offset) -{ - struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); - struct stmpe *stmpe = stmpe_gpio->stmpe; - u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8); - u8 mask = 1 << (offset % 8); - - return stmpe_set_bits(stmpe, reg, mask, 0); -} - -static int stmpe_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); - - return stmpe_gpio->irq_base + offset; -} - -static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset) -{ - struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); - struct stmpe *stmpe = stmpe_gpio->stmpe; - - if (stmpe_gpio->norequest_mask & (1 << offset)) - return -EINVAL; - - return stmpe_set_altfunc(stmpe, 1 << offset, STMPE_BLOCK_GPIO); -} - -static struct gpio_chip template_chip = { - .label = "stmpe", - .owner = THIS_MODULE, - .direction_input = stmpe_gpio_direction_input, - .get = stmpe_gpio_get, - .direction_output = stmpe_gpio_direction_output, - .set = stmpe_gpio_set, - .to_irq = stmpe_gpio_to_irq, - .request = stmpe_gpio_request, - .can_sleep = 1, -}; - -static int stmpe_gpio_irq_set_type(struct irq_data *d, unsigned int type) -{ - struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d); - int offset = d->irq - stmpe_gpio->irq_base; - int regoffset = offset / 8; - int mask = 1 << (offset % 8); - - if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH) - return -EINVAL; - - if (type == IRQ_TYPE_EDGE_RISING) - stmpe_gpio->regs[REG_RE][regoffset] |= mask; - else - stmpe_gpio->regs[REG_RE][regoffset] &= ~mask; - - if (type == IRQ_TYPE_EDGE_FALLING) - stmpe_gpio->regs[REG_FE][regoffset] |= mask; - else - stmpe_gpio->regs[REG_FE][regoffset] &= ~mask; - - return 0; -} - -static void stmpe_gpio_irq_lock(struct irq_data *d) -{ - struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d); - - mutex_lock(&stmpe_gpio->irq_lock); -} - -static void stmpe_gpio_irq_sync_unlock(struct irq_data *d) -{ - struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d); - struct stmpe *stmpe = stmpe_gpio->stmpe; - int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8); - static const u8 regmap[] = { - [REG_RE] = STMPE_IDX_GPRER_LSB, - [REG_FE] = STMPE_IDX_GPFER_LSB, - [REG_IE] = STMPE_IDX_IEGPIOR_LSB, - }; - int i, j; - - for (i = 0; i < CACHE_NR_REGS; i++) { - for (j = 0; j < num_banks; j++) { - u8 old = stmpe_gpio->oldregs[i][j]; - u8 new = stmpe_gpio->regs[i][j]; - - if (new == old) - continue; - - stmpe_gpio->oldregs[i][j] = new; - stmpe_reg_write(stmpe, stmpe->regs[regmap[i]] - j, new); - } - } - - mutex_unlock(&stmpe_gpio->irq_lock); -} - -static void stmpe_gpio_irq_mask(struct irq_data *d) -{ - struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d); - int offset = d->irq - stmpe_gpio->irq_base; - int regoffset = offset / 8; - int mask = 1 << (offset % 8); - - stmpe_gpio->regs[REG_IE][regoffset] &= ~mask; -} - -static void stmpe_gpio_irq_unmask(struct irq_data *d) -{ - struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d); - int offset = d->irq - stmpe_gpio->irq_base; - int regoffset = offset / 8; - int mask = 1 << (offset % 8); - - stmpe_gpio->regs[REG_IE][regoffset] |= mask; -} - -static struct irq_chip stmpe_gpio_irq_chip = { - .name = "stmpe-gpio", - .irq_bus_lock = stmpe_gpio_irq_lock, - .irq_bus_sync_unlock = stmpe_gpio_irq_sync_unlock, - .irq_mask = stmpe_gpio_irq_mask, - .irq_unmask = stmpe_gpio_irq_unmask, - .irq_set_type = stmpe_gpio_irq_set_type, -}; - -static irqreturn_t stmpe_gpio_irq(int irq, void *dev) -{ - struct stmpe_gpio *stmpe_gpio = dev; - struct stmpe *stmpe = stmpe_gpio->stmpe; - u8 statmsbreg = stmpe->regs[STMPE_IDX_ISGPIOR_MSB]; - int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8); - u8 status[num_banks]; - int ret; - int i; - - ret = stmpe_block_read(stmpe, statmsbreg, num_banks, status); - if (ret < 0) - return IRQ_NONE; - - for (i = 0; i < num_banks; i++) { - int bank = num_banks - i - 1; - unsigned int enabled = stmpe_gpio->regs[REG_IE][bank]; - unsigned int stat = status[i]; - - stat &= enabled; - if (!stat) - continue; - - while (stat) { - int bit = __ffs(stat); - int line = bank * 8 + bit; - - handle_nested_irq(stmpe_gpio->irq_base + line); - stat &= ~(1 << bit); - } - - stmpe_reg_write(stmpe, statmsbreg + i, status[i]); - stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_GPEDR_MSB] + i, - status[i]); - } - - return IRQ_HANDLED; -} - -static int __devinit stmpe_gpio_irq_init(struct stmpe_gpio *stmpe_gpio) -{ - int base = stmpe_gpio->irq_base; - int irq; - - for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) { - irq_set_chip_data(irq, stmpe_gpio); - irq_set_chip_and_handler(irq, &stmpe_gpio_irq_chip, - handle_simple_irq); - irq_set_nested_thread(irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); -#else - irq_set_noprobe(irq); -#endif - } - - return 0; -} - -static void stmpe_gpio_irq_remove(struct stmpe_gpio *stmpe_gpio) -{ - int base = stmpe_gpio->irq_base; - int irq; - - for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) { -#ifdef CONFIG_ARM - set_irq_flags(irq, 0); -#endif - irq_set_chip_and_handler(irq, NULL, NULL); - irq_set_chip_data(irq, NULL); - } -} - -static int __devinit stmpe_gpio_probe(struct platform_device *pdev) -{ - struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); - struct stmpe_gpio_platform_data *pdata; - struct stmpe_gpio *stmpe_gpio; - int ret; - int irq; - - pdata = stmpe->pdata->gpio; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - stmpe_gpio = kzalloc(sizeof(struct stmpe_gpio), GFP_KERNEL); - if (!stmpe_gpio) - return -ENOMEM; - - mutex_init(&stmpe_gpio->irq_lock); - - stmpe_gpio->dev = &pdev->dev; - stmpe_gpio->stmpe = stmpe; - stmpe_gpio->norequest_mask = pdata ? pdata->norequest_mask : 0; - - stmpe_gpio->chip = template_chip; - stmpe_gpio->chip.ngpio = stmpe->num_gpios; - stmpe_gpio->chip.dev = &pdev->dev; - stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1; - - stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0); - - ret = stmpe_enable(stmpe, STMPE_BLOCK_GPIO); - if (ret) - goto out_free; - - ret = stmpe_gpio_irq_init(stmpe_gpio); - if (ret) - goto out_disable; - - ret = request_threaded_irq(irq, NULL, stmpe_gpio_irq, IRQF_ONESHOT, - "stmpe-gpio", stmpe_gpio); - if (ret) { - dev_err(&pdev->dev, "unable to get irq: %d\n", ret); - goto out_removeirq; - } - - ret = gpiochip_add(&stmpe_gpio->chip); - if (ret) { - dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret); - goto out_freeirq; - } - - if (pdata && pdata->setup) - pdata->setup(stmpe, stmpe_gpio->chip.base); - - platform_set_drvdata(pdev, stmpe_gpio); - - return 0; - -out_freeirq: - free_irq(irq, stmpe_gpio); -out_removeirq: - stmpe_gpio_irq_remove(stmpe_gpio); -out_disable: - stmpe_disable(stmpe, STMPE_BLOCK_GPIO); -out_free: - kfree(stmpe_gpio); - return ret; -} - -static int __devexit stmpe_gpio_remove(struct platform_device *pdev) -{ - struct stmpe_gpio *stmpe_gpio = platform_get_drvdata(pdev); - struct stmpe *stmpe = stmpe_gpio->stmpe; - struct stmpe_gpio_platform_data *pdata = stmpe->pdata->gpio; - int irq = platform_get_irq(pdev, 0); - int ret; - - if (pdata && pdata->remove) - pdata->remove(stmpe, stmpe_gpio->chip.base); - - ret = gpiochip_remove(&stmpe_gpio->chip); - if (ret < 0) { - dev_err(stmpe_gpio->dev, - "unable to remove gpiochip: %d\n", ret); - return ret; - } - - stmpe_disable(stmpe, STMPE_BLOCK_GPIO); - - free_irq(irq, stmpe_gpio); - stmpe_gpio_irq_remove(stmpe_gpio); - platform_set_drvdata(pdev, NULL); - kfree(stmpe_gpio); - - return 0; -} - -static struct platform_driver stmpe_gpio_driver = { - .driver.name = "stmpe-gpio", - .driver.owner = THIS_MODULE, - .probe = stmpe_gpio_probe, - .remove = __devexit_p(stmpe_gpio_remove), -}; - -static int __init stmpe_gpio_init(void) -{ - return platform_driver_register(&stmpe_gpio_driver); -} -subsys_initcall(stmpe_gpio_init); - -static void __exit stmpe_gpio_exit(void) -{ - platform_driver_unregister(&stmpe_gpio_driver); -} -module_exit(stmpe_gpio_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("STMPExxxx GPIO driver"); -MODULE_AUTHOR("Rabin Vincent "); diff --git a/drivers/gpio/sx150x.c b/drivers/gpio/sx150x.c deleted file mode 100644 index a4f73534394..00000000000 --- a/drivers/gpio/sx150x.c +++ /dev/null @@ -1,680 +0,0 @@ -/* Copyright (c) 2010, Code Aurora Forum. 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 version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define NO_UPDATE_PENDING -1 - -struct sx150x_device_data { - u8 reg_pullup; - u8 reg_pulldn; - u8 reg_drain; - u8 reg_polarity; - u8 reg_dir; - u8 reg_data; - u8 reg_irq_mask; - u8 reg_irq_src; - u8 reg_sense; - u8 reg_clock; - u8 reg_misc; - u8 reg_reset; - u8 ngpios; -}; - -struct sx150x_chip { - struct gpio_chip gpio_chip; - struct i2c_client *client; - const struct sx150x_device_data *dev_cfg; - int irq_summary; - int irq_base; - int irq_update; - u32 irq_sense; - u32 irq_masked; - u32 dev_sense; - u32 dev_masked; - struct irq_chip irq_chip; - struct mutex lock; -}; - -static const struct sx150x_device_data sx150x_devices[] = { - [0] = { /* sx1508q */ - .reg_pullup = 0x03, - .reg_pulldn = 0x04, - .reg_drain = 0x05, - .reg_polarity = 0x06, - .reg_dir = 0x07, - .reg_data = 0x08, - .reg_irq_mask = 0x09, - .reg_irq_src = 0x0c, - .reg_sense = 0x0b, - .reg_clock = 0x0f, - .reg_misc = 0x10, - .reg_reset = 0x7d, - .ngpios = 8 - }, - [1] = { /* sx1509q */ - .reg_pullup = 0x07, - .reg_pulldn = 0x09, - .reg_drain = 0x0b, - .reg_polarity = 0x0d, - .reg_dir = 0x0f, - .reg_data = 0x11, - .reg_irq_mask = 0x13, - .reg_irq_src = 0x19, - .reg_sense = 0x17, - .reg_clock = 0x1e, - .reg_misc = 0x1f, - .reg_reset = 0x7d, - .ngpios = 16 - }, -}; - -static const struct i2c_device_id sx150x_id[] = { - {"sx1508q", 0}, - {"sx1509q", 1}, - {} -}; -MODULE_DEVICE_TABLE(i2c, sx150x_id); - -static s32 sx150x_i2c_write(struct i2c_client *client, u8 reg, u8 val) -{ - s32 err = i2c_smbus_write_byte_data(client, reg, val); - - if (err < 0) - dev_warn(&client->dev, - "i2c write fail: can't write %02x to %02x: %d\n", - val, reg, err); - return err; -} - -static s32 sx150x_i2c_read(struct i2c_client *client, u8 reg, u8 *val) -{ - s32 err = i2c_smbus_read_byte_data(client, reg); - - if (err >= 0) - *val = err; - else - dev_warn(&client->dev, - "i2c read fail: can't read from %02x: %d\n", - reg, err); - return err; -} - -static inline bool offset_is_oscio(struct sx150x_chip *chip, unsigned offset) -{ - return (chip->dev_cfg->ngpios == offset); -} - -/* - * These utility functions solve the common problem of locating and setting - * configuration bits. Configuration bits are grouped into registers - * whose indexes increase downwards. For example, with eight-bit registers, - * sixteen gpios would have their config bits grouped in the following order: - * REGISTER N-1 [ f e d c b a 9 8 ] - * N [ 7 6 5 4 3 2 1 0 ] - * - * For multi-bit configurations, the pattern gets wider: - * REGISTER N-3 [ f f e e d d c c ] - * N-2 [ b b a a 9 9 8 8 ] - * N-1 [ 7 7 6 6 5 5 4 4 ] - * N [ 3 3 2 2 1 1 0 0 ] - * - * Given the address of the starting register 'N', the index of the gpio - * whose configuration we seek to change, and the width in bits of that - * configuration, these functions allow us to locate the correct - * register and mask the correct bits. - */ -static inline void sx150x_find_cfg(u8 offset, u8 width, - u8 *reg, u8 *mask, u8 *shift) -{ - *reg -= offset * width / 8; - *mask = (1 << width) - 1; - *shift = (offset * width) % 8; - *mask <<= *shift; -} - -static s32 sx150x_write_cfg(struct sx150x_chip *chip, - u8 offset, u8 width, u8 reg, u8 val) -{ - u8 mask; - u8 data; - u8 shift; - s32 err; - - sx150x_find_cfg(offset, width, ®, &mask, &shift); - err = sx150x_i2c_read(chip->client, reg, &data); - if (err < 0) - return err; - - data &= ~mask; - data |= (val << shift) & mask; - return sx150x_i2c_write(chip->client, reg, data); -} - -static int sx150x_get_io(struct sx150x_chip *chip, unsigned offset) -{ - u8 reg = chip->dev_cfg->reg_data; - u8 mask; - u8 data; - u8 shift; - s32 err; - - sx150x_find_cfg(offset, 1, ®, &mask, &shift); - err = sx150x_i2c_read(chip->client, reg, &data); - if (err >= 0) - err = (data & mask) != 0 ? 1 : 0; - - return err; -} - -static void sx150x_set_oscio(struct sx150x_chip *chip, int val) -{ - sx150x_i2c_write(chip->client, - chip->dev_cfg->reg_clock, - (val ? 0x1f : 0x10)); -} - -static void sx150x_set_io(struct sx150x_chip *chip, unsigned offset, int val) -{ - sx150x_write_cfg(chip, - offset, - 1, - chip->dev_cfg->reg_data, - (val ? 1 : 0)); -} - -static int sx150x_io_input(struct sx150x_chip *chip, unsigned offset) -{ - return sx150x_write_cfg(chip, - offset, - 1, - chip->dev_cfg->reg_dir, - 1); -} - -static int sx150x_io_output(struct sx150x_chip *chip, unsigned offset, int val) -{ - int err; - - err = sx150x_write_cfg(chip, - offset, - 1, - chip->dev_cfg->reg_data, - (val ? 1 : 0)); - if (err >= 0) - err = sx150x_write_cfg(chip, - offset, - 1, - chip->dev_cfg->reg_dir, - 0); - return err; -} - -static int sx150x_gpio_get(struct gpio_chip *gc, unsigned offset) -{ - struct sx150x_chip *chip; - int status = -EINVAL; - - chip = container_of(gc, struct sx150x_chip, gpio_chip); - - if (!offset_is_oscio(chip, offset)) { - mutex_lock(&chip->lock); - status = sx150x_get_io(chip, offset); - mutex_unlock(&chip->lock); - } - - return status; -} - -static void sx150x_gpio_set(struct gpio_chip *gc, unsigned offset, int val) -{ - struct sx150x_chip *chip; - - chip = container_of(gc, struct sx150x_chip, gpio_chip); - - mutex_lock(&chip->lock); - if (offset_is_oscio(chip, offset)) - sx150x_set_oscio(chip, val); - else - sx150x_set_io(chip, offset, val); - mutex_unlock(&chip->lock); -} - -static int sx150x_gpio_direction_input(struct gpio_chip *gc, unsigned offset) -{ - struct sx150x_chip *chip; - int status = -EINVAL; - - chip = container_of(gc, struct sx150x_chip, gpio_chip); - - if (!offset_is_oscio(chip, offset)) { - mutex_lock(&chip->lock); - status = sx150x_io_input(chip, offset); - mutex_unlock(&chip->lock); - } - return status; -} - -static int sx150x_gpio_direction_output(struct gpio_chip *gc, - unsigned offset, - int val) -{ - struct sx150x_chip *chip; - int status = 0; - - chip = container_of(gc, struct sx150x_chip, gpio_chip); - - if (!offset_is_oscio(chip, offset)) { - mutex_lock(&chip->lock); - status = sx150x_io_output(chip, offset, val); - mutex_unlock(&chip->lock); - } - return status; -} - -static int sx150x_gpio_to_irq(struct gpio_chip *gc, unsigned offset) -{ - struct sx150x_chip *chip; - - chip = container_of(gc, struct sx150x_chip, gpio_chip); - - if (offset >= chip->dev_cfg->ngpios) - return -EINVAL; - - if (chip->irq_base < 0) - return -EINVAL; - - return chip->irq_base + offset; -} - -static void sx150x_irq_mask(struct irq_data *d) -{ - struct irq_chip *ic = irq_data_get_irq_chip(d); - struct sx150x_chip *chip; - unsigned n; - - chip = container_of(ic, struct sx150x_chip, irq_chip); - n = d->irq - chip->irq_base; - chip->irq_masked |= (1 << n); - chip->irq_update = n; -} - -static void sx150x_irq_unmask(struct irq_data *d) -{ - struct irq_chip *ic = irq_data_get_irq_chip(d); - struct sx150x_chip *chip; - unsigned n; - - chip = container_of(ic, struct sx150x_chip, irq_chip); - n = d->irq - chip->irq_base; - - chip->irq_masked &= ~(1 << n); - chip->irq_update = n; -} - -static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type) -{ - struct irq_chip *ic = irq_data_get_irq_chip(d); - struct sx150x_chip *chip; - unsigned n, val = 0; - - if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) - return -EINVAL; - - chip = container_of(ic, struct sx150x_chip, irq_chip); - n = d->irq - chip->irq_base; - - if (flow_type & IRQ_TYPE_EDGE_RISING) - val |= 0x1; - if (flow_type & IRQ_TYPE_EDGE_FALLING) - val |= 0x2; - - chip->irq_sense &= ~(3UL << (n * 2)); - chip->irq_sense |= val << (n * 2); - chip->irq_update = n; - return 0; -} - -static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id) -{ - struct sx150x_chip *chip = (struct sx150x_chip *)dev_id; - unsigned nhandled = 0; - unsigned sub_irq; - unsigned n; - s32 err; - u8 val; - int i; - - for (i = (chip->dev_cfg->ngpios / 8) - 1; i >= 0; --i) { - err = sx150x_i2c_read(chip->client, - chip->dev_cfg->reg_irq_src - i, - &val); - if (err < 0) - continue; - - sx150x_i2c_write(chip->client, - chip->dev_cfg->reg_irq_src - i, - val); - for (n = 0; n < 8; ++n) { - if (val & (1 << n)) { - sub_irq = chip->irq_base + (i * 8) + n; - handle_nested_irq(sub_irq); - ++nhandled; - } - } - } - - return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); -} - -static void sx150x_irq_bus_lock(struct irq_data *d) -{ - struct irq_chip *ic = irq_data_get_irq_chip(d); - struct sx150x_chip *chip; - - chip = container_of(ic, struct sx150x_chip, irq_chip); - - mutex_lock(&chip->lock); -} - -static void sx150x_irq_bus_sync_unlock(struct irq_data *d) -{ - struct irq_chip *ic = irq_data_get_irq_chip(d); - struct sx150x_chip *chip; - unsigned n; - - chip = container_of(ic, struct sx150x_chip, irq_chip); - - if (chip->irq_update == NO_UPDATE_PENDING) - goto out; - - n = chip->irq_update; - chip->irq_update = NO_UPDATE_PENDING; - - /* Avoid updates if nothing changed */ - if (chip->dev_sense == chip->irq_sense && - chip->dev_sense == chip->irq_masked) - goto out; - - chip->dev_sense = chip->irq_sense; - chip->dev_masked = chip->irq_masked; - - if (chip->irq_masked & (1 << n)) { - sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 1); - sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, 0); - } else { - sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 0); - sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, - chip->irq_sense >> (n * 2)); - } -out: - mutex_unlock(&chip->lock); -} - -static void sx150x_init_chip(struct sx150x_chip *chip, - struct i2c_client *client, - kernel_ulong_t driver_data, - struct sx150x_platform_data *pdata) -{ - mutex_init(&chip->lock); - - chip->client = client; - chip->dev_cfg = &sx150x_devices[driver_data]; - chip->gpio_chip.label = client->name; - chip->gpio_chip.direction_input = sx150x_gpio_direction_input; - chip->gpio_chip.direction_output = sx150x_gpio_direction_output; - chip->gpio_chip.get = sx150x_gpio_get; - chip->gpio_chip.set = sx150x_gpio_set; - chip->gpio_chip.to_irq = sx150x_gpio_to_irq; - chip->gpio_chip.base = pdata->gpio_base; - chip->gpio_chip.can_sleep = 1; - chip->gpio_chip.ngpio = chip->dev_cfg->ngpios; - if (pdata->oscio_is_gpo) - ++chip->gpio_chip.ngpio; - - chip->irq_chip.name = client->name; - chip->irq_chip.irq_mask = sx150x_irq_mask; - chip->irq_chip.irq_unmask = sx150x_irq_unmask; - chip->irq_chip.irq_set_type = sx150x_irq_set_type; - chip->irq_chip.irq_bus_lock = sx150x_irq_bus_lock; - chip->irq_chip.irq_bus_sync_unlock = sx150x_irq_bus_sync_unlock; - chip->irq_summary = -1; - chip->irq_base = -1; - chip->irq_masked = ~0; - chip->irq_sense = 0; - chip->dev_masked = ~0; - chip->dev_sense = 0; - chip->irq_update = NO_UPDATE_PENDING; -} - -static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg) -{ - int err = 0; - unsigned n; - - for (n = 0; err >= 0 && n < (chip->dev_cfg->ngpios / 8); ++n) - err = sx150x_i2c_write(chip->client, base - n, cfg >> (n * 8)); - return err; -} - -static int sx150x_reset(struct sx150x_chip *chip) -{ - int err; - - err = i2c_smbus_write_byte_data(chip->client, - chip->dev_cfg->reg_reset, - 0x12); - if (err < 0) - return err; - - err = i2c_smbus_write_byte_data(chip->client, - chip->dev_cfg->reg_reset, - 0x34); - return err; -} - -static int sx150x_init_hw(struct sx150x_chip *chip, - struct sx150x_platform_data *pdata) -{ - int err = 0; - - if (pdata->reset_during_probe) { - err = sx150x_reset(chip); - if (err < 0) - return err; - } - - err = sx150x_i2c_write(chip->client, - chip->dev_cfg->reg_misc, - 0x01); - if (err < 0) - return err; - - err = sx150x_init_io(chip, chip->dev_cfg->reg_pullup, - pdata->io_pullup_ena); - if (err < 0) - return err; - - err = sx150x_init_io(chip, chip->dev_cfg->reg_pulldn, - pdata->io_pulldn_ena); - if (err < 0) - return err; - - err = sx150x_init_io(chip, chip->dev_cfg->reg_drain, - pdata->io_open_drain_ena); - if (err < 0) - return err; - - err = sx150x_init_io(chip, chip->dev_cfg->reg_polarity, - pdata->io_polarity); - if (err < 0) - return err; - - if (pdata->oscio_is_gpo) - sx150x_set_oscio(chip, 0); - - return err; -} - -static int sx150x_install_irq_chip(struct sx150x_chip *chip, - int irq_summary, - int irq_base) -{ - int err; - unsigned n; - unsigned irq; - - chip->irq_summary = irq_summary; - chip->irq_base = irq_base; - - for (n = 0; n < chip->dev_cfg->ngpios; ++n) { - irq = irq_base + n; - irq_set_chip_and_handler(irq, &chip->irq_chip, handle_edge_irq); - irq_set_nested_thread(irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); -#else - irq_set_noprobe(irq); -#endif - } - - err = request_threaded_irq(irq_summary, - NULL, - sx150x_irq_thread_fn, - IRQF_SHARED | IRQF_TRIGGER_FALLING, - chip->irq_chip.name, - chip); - if (err < 0) { - chip->irq_summary = -1; - chip->irq_base = -1; - } - - return err; -} - -static void sx150x_remove_irq_chip(struct sx150x_chip *chip) -{ - unsigned n; - unsigned irq; - - free_irq(chip->irq_summary, chip); - - for (n = 0; n < chip->dev_cfg->ngpios; ++n) { - irq = chip->irq_base + n; - irq_set_chip_and_handler(irq, NULL, NULL); - } -} - -static int __devinit sx150x_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - static const u32 i2c_funcs = I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_WRITE_WORD_DATA; - struct sx150x_platform_data *pdata; - struct sx150x_chip *chip; - int rc; - - pdata = client->dev.platform_data; - if (!pdata) - return -EINVAL; - - if (!i2c_check_functionality(client->adapter, i2c_funcs)) - return -ENOSYS; - - chip = kzalloc(sizeof(struct sx150x_chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - - sx150x_init_chip(chip, client, id->driver_data, pdata); - rc = sx150x_init_hw(chip, pdata); - if (rc < 0) - goto probe_fail_pre_gpiochip_add; - - rc = gpiochip_add(&chip->gpio_chip); - if (rc < 0) - goto probe_fail_pre_gpiochip_add; - - if (pdata->irq_summary >= 0) { - rc = sx150x_install_irq_chip(chip, - pdata->irq_summary, - pdata->irq_base); - if (rc < 0) - goto probe_fail_post_gpiochip_add; - } - - i2c_set_clientdata(client, chip); - - return 0; -probe_fail_post_gpiochip_add: - WARN_ON(gpiochip_remove(&chip->gpio_chip) < 0); -probe_fail_pre_gpiochip_add: - kfree(chip); - return rc; -} - -static int __devexit sx150x_remove(struct i2c_client *client) -{ - struct sx150x_chip *chip; - int rc; - - chip = i2c_get_clientdata(client); - rc = gpiochip_remove(&chip->gpio_chip); - if (rc < 0) - return rc; - - if (chip->irq_summary >= 0) - sx150x_remove_irq_chip(chip); - - kfree(chip); - - return 0; -} - -static struct i2c_driver sx150x_driver = { - .driver = { - .name = "sx150x", - .owner = THIS_MODULE - }, - .probe = sx150x_probe, - .remove = __devexit_p(sx150x_remove), - .id_table = sx150x_id, -}; - -static int __init sx150x_init(void) -{ - return i2c_add_driver(&sx150x_driver); -} -subsys_initcall(sx150x_init); - -static void __exit sx150x_exit(void) -{ - return i2c_del_driver(&sx150x_driver); -} -module_exit(sx150x_exit); - -MODULE_AUTHOR("Gregory Bean "); -MODULE_DESCRIPTION("Driver for Semtech SX150X I2C GPIO Expanders"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("i2c:sx150x"); diff --git a/drivers/gpio/tc3589x-gpio.c b/drivers/gpio/tc3589x-gpio.c deleted file mode 100644 index 2a82e8999a4..00000000000 --- a/drivers/gpio/tc3589x-gpio.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * License Terms: GNU General Public License, version 2 - * Author: Hanumath Prasad for ST-Ericsson - * Author: Rabin Vincent for ST-Ericsson - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * These registers are modified under the irq bus lock and cached to avoid - * unnecessary writes in bus_sync_unlock. - */ -enum { REG_IBE, REG_IEV, REG_IS, REG_IE }; - -#define CACHE_NR_REGS 4 -#define CACHE_NR_BANKS 3 - -struct tc3589x_gpio { - struct gpio_chip chip; - struct tc3589x *tc3589x; - struct device *dev; - struct mutex irq_lock; - - int irq_base; - - /* Caches of interrupt control registers for bus_lock */ - u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS]; - u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS]; -}; - -static inline struct tc3589x_gpio *to_tc3589x_gpio(struct gpio_chip *chip) -{ - return container_of(chip, struct tc3589x_gpio, chip); -} - -static int tc3589x_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip); - struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; - u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2; - u8 mask = 1 << (offset % 8); - int ret; - - ret = tc3589x_reg_read(tc3589x, reg); - if (ret < 0) - return ret; - - return ret & mask; -} - -static void tc3589x_gpio_set(struct gpio_chip *chip, unsigned offset, int val) -{ - struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip); - struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; - u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2; - unsigned pos = offset % 8; - u8 data[] = {!!val << pos, 1 << pos}; - - tc3589x_block_write(tc3589x, reg, ARRAY_SIZE(data), data); -} - -static int tc3589x_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int val) -{ - struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip); - struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; - u8 reg = TC3589x_GPIODIR0 + offset / 8; - unsigned pos = offset % 8; - - tc3589x_gpio_set(chip, offset, val); - - return tc3589x_set_bits(tc3589x, reg, 1 << pos, 1 << pos); -} - -static int tc3589x_gpio_direction_input(struct gpio_chip *chip, - unsigned offset) -{ - struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip); - struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; - u8 reg = TC3589x_GPIODIR0 + offset / 8; - unsigned pos = offset % 8; - - return tc3589x_set_bits(tc3589x, reg, 1 << pos, 0); -} - -static int tc3589x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip); - - return tc3589x_gpio->irq_base + offset; -} - -static struct gpio_chip template_chip = { - .label = "tc3589x", - .owner = THIS_MODULE, - .direction_input = tc3589x_gpio_direction_input, - .get = tc3589x_gpio_get, - .direction_output = tc3589x_gpio_direction_output, - .set = tc3589x_gpio_set, - .to_irq = tc3589x_gpio_to_irq, - .can_sleep = 1, -}; - -static int tc3589x_gpio_irq_set_type(struct irq_data *d, unsigned int type) -{ - struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d); - int offset = d->irq - tc3589x_gpio->irq_base; - int regoffset = offset / 8; - int mask = 1 << (offset % 8); - - if (type == IRQ_TYPE_EDGE_BOTH) { - tc3589x_gpio->regs[REG_IBE][regoffset] |= mask; - return 0; - } - - tc3589x_gpio->regs[REG_IBE][regoffset] &= ~mask; - - if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH) - tc3589x_gpio->regs[REG_IS][regoffset] |= mask; - else - tc3589x_gpio->regs[REG_IS][regoffset] &= ~mask; - - if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH) - tc3589x_gpio->regs[REG_IEV][regoffset] |= mask; - else - tc3589x_gpio->regs[REG_IEV][regoffset] &= ~mask; - - return 0; -} - -static void tc3589x_gpio_irq_lock(struct irq_data *d) -{ - struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d); - - mutex_lock(&tc3589x_gpio->irq_lock); -} - -static void tc3589x_gpio_irq_sync_unlock(struct irq_data *d) -{ - struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d); - struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; - static const u8 regmap[] = { - [REG_IBE] = TC3589x_GPIOIBE0, - [REG_IEV] = TC3589x_GPIOIEV0, - [REG_IS] = TC3589x_GPIOIS0, - [REG_IE] = TC3589x_GPIOIE0, - }; - int i, j; - - for (i = 0; i < CACHE_NR_REGS; i++) { - for (j = 0; j < CACHE_NR_BANKS; j++) { - u8 old = tc3589x_gpio->oldregs[i][j]; - u8 new = tc3589x_gpio->regs[i][j]; - - if (new == old) - continue; - - tc3589x_gpio->oldregs[i][j] = new; - tc3589x_reg_write(tc3589x, regmap[i] + j * 8, new); - } - } - - mutex_unlock(&tc3589x_gpio->irq_lock); -} - -static void tc3589x_gpio_irq_mask(struct irq_data *d) -{ - struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d); - int offset = d->irq - tc3589x_gpio->irq_base; - int regoffset = offset / 8; - int mask = 1 << (offset % 8); - - tc3589x_gpio->regs[REG_IE][regoffset] &= ~mask; -} - -static void tc3589x_gpio_irq_unmask(struct irq_data *d) -{ - struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d); - int offset = d->irq - tc3589x_gpio->irq_base; - int regoffset = offset / 8; - int mask = 1 << (offset % 8); - - tc3589x_gpio->regs[REG_IE][regoffset] |= mask; -} - -static struct irq_chip tc3589x_gpio_irq_chip = { - .name = "tc3589x-gpio", - .irq_bus_lock = tc3589x_gpio_irq_lock, - .irq_bus_sync_unlock = tc3589x_gpio_irq_sync_unlock, - .irq_mask = tc3589x_gpio_irq_mask, - .irq_unmask = tc3589x_gpio_irq_unmask, - .irq_set_type = tc3589x_gpio_irq_set_type, -}; - -static irqreturn_t tc3589x_gpio_irq(int irq, void *dev) -{ - struct tc3589x_gpio *tc3589x_gpio = dev; - struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; - u8 status[CACHE_NR_BANKS]; - int ret; - int i; - - ret = tc3589x_block_read(tc3589x, TC3589x_GPIOMIS0, - ARRAY_SIZE(status), status); - if (ret < 0) - return IRQ_NONE; - - for (i = 0; i < ARRAY_SIZE(status); i++) { - unsigned int stat = status[i]; - if (!stat) - continue; - - while (stat) { - int bit = __ffs(stat); - int line = i * 8 + bit; - - handle_nested_irq(tc3589x_gpio->irq_base + line); - stat &= ~(1 << bit); - } - - tc3589x_reg_write(tc3589x, TC3589x_GPIOIC0 + i, status[i]); - } - - return IRQ_HANDLED; -} - -static int tc3589x_gpio_irq_init(struct tc3589x_gpio *tc3589x_gpio) -{ - int base = tc3589x_gpio->irq_base; - int irq; - - for (irq = base; irq < base + tc3589x_gpio->chip.ngpio; irq++) { - irq_set_chip_data(irq, tc3589x_gpio); - irq_set_chip_and_handler(irq, &tc3589x_gpio_irq_chip, - handle_simple_irq); - irq_set_nested_thread(irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); -#else - irq_set_noprobe(irq); -#endif - } - - return 0; -} - -static void tc3589x_gpio_irq_remove(struct tc3589x_gpio *tc3589x_gpio) -{ - int base = tc3589x_gpio->irq_base; - int irq; - - for (irq = base; irq < base + tc3589x_gpio->chip.ngpio; irq++) { -#ifdef CONFIG_ARM - set_irq_flags(irq, 0); -#endif - irq_set_chip_and_handler(irq, NULL, NULL); - irq_set_chip_data(irq, NULL); - } -} - -static int __devinit tc3589x_gpio_probe(struct platform_device *pdev) -{ - struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent); - struct tc3589x_gpio_platform_data *pdata; - struct tc3589x_gpio *tc3589x_gpio; - int ret; - int irq; - - pdata = tc3589x->pdata->gpio; - if (!pdata) - return -ENODEV; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - tc3589x_gpio = kzalloc(sizeof(struct tc3589x_gpio), GFP_KERNEL); - if (!tc3589x_gpio) - return -ENOMEM; - - mutex_init(&tc3589x_gpio->irq_lock); - - tc3589x_gpio->dev = &pdev->dev; - tc3589x_gpio->tc3589x = tc3589x; - - tc3589x_gpio->chip = template_chip; - tc3589x_gpio->chip.ngpio = tc3589x->num_gpio; - tc3589x_gpio->chip.dev = &pdev->dev; - tc3589x_gpio->chip.base = pdata->gpio_base; - - tc3589x_gpio->irq_base = tc3589x->irq_base + TC3589x_INT_GPIO(0); - - /* Bring the GPIO module out of reset */ - ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, - TC3589x_RSTCTRL_GPIRST, 0); - if (ret < 0) - goto out_free; - - ret = tc3589x_gpio_irq_init(tc3589x_gpio); - if (ret) - goto out_free; - - ret = request_threaded_irq(irq, NULL, tc3589x_gpio_irq, IRQF_ONESHOT, - "tc3589x-gpio", tc3589x_gpio); - if (ret) { - dev_err(&pdev->dev, "unable to get irq: %d\n", ret); - goto out_removeirq; - } - - ret = gpiochip_add(&tc3589x_gpio->chip); - if (ret) { - dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret); - goto out_freeirq; - } - - if (pdata->setup) - pdata->setup(tc3589x, tc3589x_gpio->chip.base); - - platform_set_drvdata(pdev, tc3589x_gpio); - - return 0; - -out_freeirq: - free_irq(irq, tc3589x_gpio); -out_removeirq: - tc3589x_gpio_irq_remove(tc3589x_gpio); -out_free: - kfree(tc3589x_gpio); - return ret; -} - -static int __devexit tc3589x_gpio_remove(struct platform_device *pdev) -{ - struct tc3589x_gpio *tc3589x_gpio = platform_get_drvdata(pdev); - struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; - struct tc3589x_gpio_platform_data *pdata = tc3589x->pdata->gpio; - int irq = platform_get_irq(pdev, 0); - int ret; - - if (pdata->remove) - pdata->remove(tc3589x, tc3589x_gpio->chip.base); - - ret = gpiochip_remove(&tc3589x_gpio->chip); - if (ret < 0) { - dev_err(tc3589x_gpio->dev, - "unable to remove gpiochip: %d\n", ret); - return ret; - } - - free_irq(irq, tc3589x_gpio); - tc3589x_gpio_irq_remove(tc3589x_gpio); - - platform_set_drvdata(pdev, NULL); - kfree(tc3589x_gpio); - - return 0; -} - -static struct platform_driver tc3589x_gpio_driver = { - .driver.name = "tc3589x-gpio", - .driver.owner = THIS_MODULE, - .probe = tc3589x_gpio_probe, - .remove = __devexit_p(tc3589x_gpio_remove), -}; - -static int __init tc3589x_gpio_init(void) -{ - return platform_driver_register(&tc3589x_gpio_driver); -} -subsys_initcall(tc3589x_gpio_init); - -static void __exit tc3589x_gpio_exit(void) -{ - platform_driver_unregister(&tc3589x_gpio_driver); -} -module_exit(tc3589x_gpio_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("TC3589x GPIO driver"); -MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent"); diff --git a/drivers/gpio/timbgpio.c b/drivers/gpio/timbgpio.c deleted file mode 100644 index 0265872e57d..00000000000 --- a/drivers/gpio/timbgpio.c +++ /dev/null @@ -1,379 +0,0 @@ -/* - * timbgpio.c timberdale FPGA GPIO driver - * Copyright (c) 2009 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* Supports: - * Timberdale FPGA GPIO - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRIVER_NAME "timb-gpio" - -#define TGPIOVAL 0x00 -#define TGPIODIR 0x04 -#define TGPIO_IER 0x08 -#define TGPIO_ISR 0x0c -#define TGPIO_IPR 0x10 -#define TGPIO_ICR 0x14 -#define TGPIO_FLR 0x18 -#define TGPIO_LVR 0x1c -#define TGPIO_VER 0x20 -#define TGPIO_BFLR 0x24 - -struct timbgpio { - void __iomem *membase; - spinlock_t lock; /* mutual exclusion */ - struct gpio_chip gpio; - int irq_base; - unsigned long last_ier; -}; - -static int timbgpio_update_bit(struct gpio_chip *gpio, unsigned index, - unsigned offset, bool enabled) -{ - struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); - u32 reg; - - spin_lock(&tgpio->lock); - reg = ioread32(tgpio->membase + offset); - - if (enabled) - reg |= (1 << index); - else - reg &= ~(1 << index); - - iowrite32(reg, tgpio->membase + offset); - spin_unlock(&tgpio->lock); - - return 0; -} - -static int timbgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) -{ - return timbgpio_update_bit(gpio, nr, TGPIODIR, true); -} - -static int timbgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) -{ - struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); - u32 value; - - value = ioread32(tgpio->membase + TGPIOVAL); - return (value & (1 << nr)) ? 1 : 0; -} - -static int timbgpio_gpio_direction_output(struct gpio_chip *gpio, - unsigned nr, int val) -{ - return timbgpio_update_bit(gpio, nr, TGPIODIR, false); -} - -static void timbgpio_gpio_set(struct gpio_chip *gpio, - unsigned nr, int val) -{ - timbgpio_update_bit(gpio, nr, TGPIOVAL, val != 0); -} - -static int timbgpio_to_irq(struct gpio_chip *gpio, unsigned offset) -{ - struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); - - if (tgpio->irq_base <= 0) - return -EINVAL; - - return tgpio->irq_base + offset; -} - -/* - * GPIO IRQ - */ -static void timbgpio_irq_disable(struct irq_data *d) -{ - struct timbgpio *tgpio = irq_data_get_irq_chip_data(d); - int offset = d->irq - tgpio->irq_base; - unsigned long flags; - - spin_lock_irqsave(&tgpio->lock, flags); - tgpio->last_ier &= ~(1 << offset); - iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); - spin_unlock_irqrestore(&tgpio->lock, flags); -} - -static void timbgpio_irq_enable(struct irq_data *d) -{ - struct timbgpio *tgpio = irq_data_get_irq_chip_data(d); - int offset = d->irq - tgpio->irq_base; - unsigned long flags; - - spin_lock_irqsave(&tgpio->lock, flags); - tgpio->last_ier |= 1 << offset; - iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); - spin_unlock_irqrestore(&tgpio->lock, flags); -} - -static int timbgpio_irq_type(struct irq_data *d, unsigned trigger) -{ - struct timbgpio *tgpio = irq_data_get_irq_chip_data(d); - int offset = d->irq - tgpio->irq_base; - unsigned long flags; - u32 lvr, flr, bflr = 0; - u32 ver; - int ret = 0; - - if (offset < 0 || offset > tgpio->gpio.ngpio) - return -EINVAL; - - ver = ioread32(tgpio->membase + TGPIO_VER); - - spin_lock_irqsave(&tgpio->lock, flags); - - lvr = ioread32(tgpio->membase + TGPIO_LVR); - flr = ioread32(tgpio->membase + TGPIO_FLR); - if (ver > 2) - bflr = ioread32(tgpio->membase + TGPIO_BFLR); - - if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { - bflr &= ~(1 << offset); - flr &= ~(1 << offset); - if (trigger & IRQ_TYPE_LEVEL_HIGH) - lvr |= 1 << offset; - else - lvr &= ~(1 << offset); - } - - if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { - if (ver < 3) { - ret = -EINVAL; - goto out; - } - else { - flr |= 1 << offset; - bflr |= 1 << offset; - } - } else { - bflr &= ~(1 << offset); - flr |= 1 << offset; - if (trigger & IRQ_TYPE_EDGE_FALLING) - lvr &= ~(1 << offset); - else - lvr |= 1 << offset; - } - - iowrite32(lvr, tgpio->membase + TGPIO_LVR); - iowrite32(flr, tgpio->membase + TGPIO_FLR); - if (ver > 2) - iowrite32(bflr, tgpio->membase + TGPIO_BFLR); - - iowrite32(1 << offset, tgpio->membase + TGPIO_ICR); - -out: - spin_unlock_irqrestore(&tgpio->lock, flags); - return ret; -} - -static void timbgpio_irq(unsigned int irq, struct irq_desc *desc) -{ - struct timbgpio *tgpio = irq_get_handler_data(irq); - unsigned long ipr; - int offset; - - desc->irq_data.chip->irq_ack(irq_get_irq_data(irq)); - ipr = ioread32(tgpio->membase + TGPIO_IPR); - iowrite32(ipr, tgpio->membase + TGPIO_ICR); - - /* - * Some versions of the hardware trash the IER register if more than - * one interrupt is received simultaneously. - */ - iowrite32(0, tgpio->membase + TGPIO_IER); - - for_each_set_bit(offset, &ipr, tgpio->gpio.ngpio) - generic_handle_irq(timbgpio_to_irq(&tgpio->gpio, offset)); - - iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); -} - -static struct irq_chip timbgpio_irqchip = { - .name = "GPIO", - .irq_enable = timbgpio_irq_enable, - .irq_disable = timbgpio_irq_disable, - .irq_set_type = timbgpio_irq_type, -}; - -static int __devinit timbgpio_probe(struct platform_device *pdev) -{ - int err, i; - struct gpio_chip *gc; - struct timbgpio *tgpio; - struct resource *iomem; - struct timbgpio_platform_data *pdata = pdev->dev.platform_data; - int irq = platform_get_irq(pdev, 0); - - if (!pdata || pdata->nr_pins > 32) { - err = -EINVAL; - goto err_mem; - } - - iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iomem) { - err = -EINVAL; - goto err_mem; - } - - tgpio = kzalloc(sizeof(*tgpio), GFP_KERNEL); - if (!tgpio) { - err = -EINVAL; - goto err_mem; - } - tgpio->irq_base = pdata->irq_base; - - spin_lock_init(&tgpio->lock); - - if (!request_mem_region(iomem->start, resource_size(iomem), - DRIVER_NAME)) { - err = -EBUSY; - goto err_request; - } - - tgpio->membase = ioremap(iomem->start, resource_size(iomem)); - if (!tgpio->membase) { - err = -ENOMEM; - goto err_ioremap; - } - - gc = &tgpio->gpio; - - gc->label = dev_name(&pdev->dev); - gc->owner = THIS_MODULE; - gc->dev = &pdev->dev; - gc->direction_input = timbgpio_gpio_direction_input; - gc->get = timbgpio_gpio_get; - gc->direction_output = timbgpio_gpio_direction_output; - gc->set = timbgpio_gpio_set; - gc->to_irq = (irq >= 0 && tgpio->irq_base > 0) ? timbgpio_to_irq : NULL; - gc->dbg_show = NULL; - gc->base = pdata->gpio_base; - gc->ngpio = pdata->nr_pins; - gc->can_sleep = 0; - - err = gpiochip_add(gc); - if (err) - goto err_chipadd; - - platform_set_drvdata(pdev, tgpio); - - /* make sure to disable interrupts */ - iowrite32(0x0, tgpio->membase + TGPIO_IER); - - if (irq < 0 || tgpio->irq_base <= 0) - return 0; - - for (i = 0; i < pdata->nr_pins; i++) { - irq_set_chip_and_handler_name(tgpio->irq_base + i, - &timbgpio_irqchip, handle_simple_irq, "mux"); - irq_set_chip_data(tgpio->irq_base + i, tgpio); -#ifdef CONFIG_ARM - set_irq_flags(tgpio->irq_base + i, IRQF_VALID | IRQF_PROBE); -#endif - } - - irq_set_handler_data(irq, tgpio); - irq_set_chained_handler(irq, timbgpio_irq); - - return 0; - -err_chipadd: - iounmap(tgpio->membase); -err_ioremap: - release_mem_region(iomem->start, resource_size(iomem)); -err_request: - kfree(tgpio); -err_mem: - printk(KERN_ERR DRIVER_NAME": Failed to register GPIOs: %d\n", err); - - return err; -} - -static int __devexit timbgpio_remove(struct platform_device *pdev) -{ - int err; - struct timbgpio_platform_data *pdata = pdev->dev.platform_data; - struct timbgpio *tgpio = platform_get_drvdata(pdev); - struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - int irq = platform_get_irq(pdev, 0); - - if (irq >= 0 && tgpio->irq_base > 0) { - int i; - for (i = 0; i < pdata->nr_pins; i++) { - irq_set_chip(tgpio->irq_base + i, NULL); - irq_set_chip_data(tgpio->irq_base + i, NULL); - } - - irq_set_handler(irq, NULL); - irq_set_handler_data(irq, NULL); - } - - err = gpiochip_remove(&tgpio->gpio); - if (err) - printk(KERN_ERR DRIVER_NAME": failed to remove gpio_chip\n"); - - iounmap(tgpio->membase); - release_mem_region(iomem->start, resource_size(iomem)); - kfree(tgpio); - - platform_set_drvdata(pdev, NULL); - - return 0; -} - -static struct platform_driver timbgpio_platform_driver = { - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - }, - .probe = timbgpio_probe, - .remove = timbgpio_remove, -}; - -/*--------------------------------------------------------------------------*/ - -static int __init timbgpio_init(void) -{ - return platform_driver_register(&timbgpio_platform_driver); -} - -static void __exit timbgpio_exit(void) -{ - platform_driver_unregister(&timbgpio_platform_driver); -} - -module_init(timbgpio_init); -module_exit(timbgpio_exit); - -MODULE_DESCRIPTION("Timberdale GPIO driver"); -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Mocean Laboratories"); -MODULE_ALIAS("platform:"DRIVER_NAME); - diff --git a/drivers/gpio/tps65910-gpio.c b/drivers/gpio/tps65910-gpio.c deleted file mode 100644 index 8d1ddfdd63e..00000000000 --- a/drivers/gpio/tps65910-gpio.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * tps65910-gpio.c -- TI TPS6591x - * - * Copyright 2010 Texas Instruments Inc. - * - * Author: Graeme Gregory - * Author: Jorge Eduardo Candelaria jedu@slimlogic.co.uk> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include - -static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset) -{ - struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); - uint8_t val; - - tps65910->read(tps65910, TPS65910_GPIO0 + offset, 1, &val); - - if (val & GPIO_STS_MASK) - return 1; - - return 0; -} - -static void tps65910_gpio_set(struct gpio_chip *gc, unsigned offset, - int value) -{ - struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); - - if (value) - tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset, - GPIO_SET_MASK); - else - tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset, - GPIO_SET_MASK); -} - -static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset, - int value) -{ - struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); - - /* Set the initial value */ - tps65910_gpio_set(gc, 0, value); - - return tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset, - GPIO_CFG_MASK); -} - -static int tps65910_gpio_input(struct gpio_chip *gc, unsigned offset) -{ - struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); - - return tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset, - GPIO_CFG_MASK); -} - -void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base) -{ - int ret; - - if (!gpio_base) - return; - - tps65910->gpio.owner = THIS_MODULE; - tps65910->gpio.label = tps65910->i2c_client->name; - tps65910->gpio.dev = tps65910->dev; - tps65910->gpio.base = gpio_base; - - switch(tps65910_chip_id(tps65910)) { - case TPS65910: - tps65910->gpio.ngpio = 6; - case TPS65911: - tps65910->gpio.ngpio = 9; - default: - return; - } - tps65910->gpio.can_sleep = 1; - - tps65910->gpio.direction_input = tps65910_gpio_input; - tps65910->gpio.direction_output = tps65910_gpio_output; - tps65910->gpio.set = tps65910_gpio_set; - tps65910->gpio.get = tps65910_gpio_get; - - ret = gpiochip_add(&tps65910->gpio); - - if (ret) - dev_warn(tps65910->dev, "GPIO registration failed: %d\n", ret); -} diff --git a/drivers/gpio/twl4030-gpio.c b/drivers/gpio/twl4030-gpio.c deleted file mode 100644 index 57635ac35a7..00000000000 --- a/drivers/gpio/twl4030-gpio.c +++ /dev/null @@ -1,513 +0,0 @@ -/* - * twl4030_gpio.c -- access to GPIOs on TWL4030/TPS659x0 chips - * - * Copyright (C) 2006-2007 Texas Instruments, Inc. - * Copyright (C) 2006 MontaVista Software, Inc. - * - * Code re-arranged and cleaned up by: - * Syed Mohammed Khasim - * - * Initial Code: - * Andy Lowe / Nishanth Menon - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - - -/* - * The GPIO "subchip" supports 18 GPIOs which can be configured as - * inputs or outputs, with pullups or pulldowns on each pin. Each - * GPIO can trigger interrupts on either or both edges. - * - * GPIO interrupts can be fed to either of two IRQ lines; this is - * intended to support multiple hosts. - * - * There are also two LED pins used sometimes as output-only GPIOs. - */ - - -static struct gpio_chip twl_gpiochip; -static int twl4030_gpio_irq_base; - -/* genirq interfaces are not available to modules */ -#ifdef MODULE -#define is_module() true -#else -#define is_module() false -#endif - -/* GPIO_CTRL Fields */ -#define MASK_GPIO_CTRL_GPIO0CD1 BIT(0) -#define MASK_GPIO_CTRL_GPIO1CD2 BIT(1) -#define MASK_GPIO_CTRL_GPIO_ON BIT(2) - -/* Mask for GPIO registers when aggregated into a 32-bit integer */ -#define GPIO_32_MASK 0x0003ffff - -/* Data structures */ -static DEFINE_MUTEX(gpio_lock); - -/* store usage of each GPIO. - each bit represents one GPIO */ -static unsigned int gpio_usage_count; - -/*----------------------------------------------------------------------*/ - -/* - * To configure TWL4030 GPIO module registers - */ -static inline int gpio_twl4030_write(u8 address, u8 data) -{ - return twl_i2c_write_u8(TWL4030_MODULE_GPIO, data, address); -} - -/*----------------------------------------------------------------------*/ - -/* - * LED register offsets (use TWL4030_MODULE_{LED,PWMA,PWMB})) - * PWMs A and B are dedicated to LEDs A and B, respectively. - */ - -#define TWL4030_LED_LEDEN 0x0 - -/* LEDEN bits */ -#define LEDEN_LEDAON BIT(0) -#define LEDEN_LEDBON BIT(1) -#define LEDEN_LEDAEXT BIT(2) -#define LEDEN_LEDBEXT BIT(3) -#define LEDEN_LEDAPWM BIT(4) -#define LEDEN_LEDBPWM BIT(5) -#define LEDEN_PWM_LENGTHA BIT(6) -#define LEDEN_PWM_LENGTHB BIT(7) - -#define TWL4030_PWMx_PWMxON 0x0 -#define TWL4030_PWMx_PWMxOFF 0x1 - -#define PWMxON_LENGTH BIT(7) - -/*----------------------------------------------------------------------*/ - -/* - * To read a TWL4030 GPIO module register - */ -static inline int gpio_twl4030_read(u8 address) -{ - u8 data; - int ret = 0; - - ret = twl_i2c_read_u8(TWL4030_MODULE_GPIO, &data, address); - return (ret < 0) ? ret : data; -} - -/*----------------------------------------------------------------------*/ - -static u8 cached_leden; /* protected by gpio_lock */ - -/* The LED lines are open drain outputs ... a FET pulls to GND, so an - * external pullup is needed. We could also expose the integrated PWM - * as a LED brightness control; we initialize it as "always on". - */ -static void twl4030_led_set_value(int led, int value) -{ - u8 mask = LEDEN_LEDAON | LEDEN_LEDAPWM; - int status; - - if (led) - mask <<= 1; - - mutex_lock(&gpio_lock); - if (value) - cached_leden &= ~mask; - else - cached_leden |= mask; - status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden, - TWL4030_LED_LEDEN); - mutex_unlock(&gpio_lock); -} - -static int twl4030_set_gpio_direction(int gpio, int is_input) -{ - u8 d_bnk = gpio >> 3; - u8 d_msk = BIT(gpio & 0x7); - u8 reg = 0; - u8 base = REG_GPIODATADIR1 + d_bnk; - int ret = 0; - - mutex_lock(&gpio_lock); - ret = gpio_twl4030_read(base); - if (ret >= 0) { - if (is_input) - reg = ret & ~d_msk; - else - reg = ret | d_msk; - - ret = gpio_twl4030_write(base, reg); - } - mutex_unlock(&gpio_lock); - return ret; -} - -static int twl4030_set_gpio_dataout(int gpio, int enable) -{ - u8 d_bnk = gpio >> 3; - u8 d_msk = BIT(gpio & 0x7); - u8 base = 0; - - if (enable) - base = REG_SETGPIODATAOUT1 + d_bnk; - else - base = REG_CLEARGPIODATAOUT1 + d_bnk; - - return gpio_twl4030_write(base, d_msk); -} - -static int twl4030_get_gpio_datain(int gpio) -{ - u8 d_bnk = gpio >> 3; - u8 d_off = gpio & 0x7; - u8 base = 0; - int ret = 0; - - if (unlikely((gpio >= TWL4030_GPIO_MAX) - || !(gpio_usage_count & BIT(gpio)))) - return -EPERM; - - base = REG_GPIODATAIN1 + d_bnk; - ret = gpio_twl4030_read(base); - if (ret > 0) - ret = (ret >> d_off) & 0x1; - - return ret; -} - -/*----------------------------------------------------------------------*/ - -static int twl_request(struct gpio_chip *chip, unsigned offset) -{ - int status = 0; - - mutex_lock(&gpio_lock); - - /* Support the two LED outputs as output-only GPIOs. */ - if (offset >= TWL4030_GPIO_MAX) { - u8 ledclr_mask = LEDEN_LEDAON | LEDEN_LEDAEXT - | LEDEN_LEDAPWM | LEDEN_PWM_LENGTHA; - u8 module = TWL4030_MODULE_PWMA; - - offset -= TWL4030_GPIO_MAX; - if (offset) { - ledclr_mask <<= 1; - module = TWL4030_MODULE_PWMB; - } - - /* initialize PWM to always-drive */ - status = twl_i2c_write_u8(module, 0x7f, - TWL4030_PWMx_PWMxOFF); - if (status < 0) - goto done; - status = twl_i2c_write_u8(module, 0x7f, - TWL4030_PWMx_PWMxON); - if (status < 0) - goto done; - - /* init LED to not-driven (high) */ - module = TWL4030_MODULE_LED; - status = twl_i2c_read_u8(module, &cached_leden, - TWL4030_LED_LEDEN); - if (status < 0) - goto done; - cached_leden &= ~ledclr_mask; - status = twl_i2c_write_u8(module, cached_leden, - TWL4030_LED_LEDEN); - if (status < 0) - goto done; - - status = 0; - goto done; - } - - /* on first use, turn GPIO module "on" */ - if (!gpio_usage_count) { - struct twl4030_gpio_platform_data *pdata; - u8 value = MASK_GPIO_CTRL_GPIO_ON; - - /* optionally have the first two GPIOs switch vMMC1 - * and vMMC2 power supplies based on card presence. - */ - pdata = chip->dev->platform_data; - value |= pdata->mmc_cd & 0x03; - - status = gpio_twl4030_write(REG_GPIO_CTRL, value); - } - - if (!status) - gpio_usage_count |= (0x1 << offset); - -done: - mutex_unlock(&gpio_lock); - return status; -} - -static void twl_free(struct gpio_chip *chip, unsigned offset) -{ - if (offset >= TWL4030_GPIO_MAX) { - twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1); - return; - } - - mutex_lock(&gpio_lock); - - gpio_usage_count &= ~BIT(offset); - - /* on last use, switch off GPIO module */ - if (!gpio_usage_count) - gpio_twl4030_write(REG_GPIO_CTRL, 0x0); - - mutex_unlock(&gpio_lock); -} - -static int twl_direction_in(struct gpio_chip *chip, unsigned offset) -{ - return (offset < TWL4030_GPIO_MAX) - ? twl4030_set_gpio_direction(offset, 1) - : -EINVAL; -} - -static int twl_get(struct gpio_chip *chip, unsigned offset) -{ - int status = 0; - - if (offset < TWL4030_GPIO_MAX) - status = twl4030_get_gpio_datain(offset); - else if (offset == TWL4030_GPIO_MAX) - status = cached_leden & LEDEN_LEDAON; - else - status = cached_leden & LEDEN_LEDBON; - return (status < 0) ? 0 : status; -} - -static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value) -{ - if (offset < TWL4030_GPIO_MAX) { - twl4030_set_gpio_dataout(offset, value); - return twl4030_set_gpio_direction(offset, 0); - } else { - twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value); - return 0; - } -} - -static void twl_set(struct gpio_chip *chip, unsigned offset, int value) -{ - if (offset < TWL4030_GPIO_MAX) - twl4030_set_gpio_dataout(offset, value); - else - twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value); -} - -static int twl_to_irq(struct gpio_chip *chip, unsigned offset) -{ - return (twl4030_gpio_irq_base && (offset < TWL4030_GPIO_MAX)) - ? (twl4030_gpio_irq_base + offset) - : -EINVAL; -} - -static struct gpio_chip twl_gpiochip = { - .label = "twl4030", - .owner = THIS_MODULE, - .request = twl_request, - .free = twl_free, - .direction_input = twl_direction_in, - .get = twl_get, - .direction_output = twl_direction_out, - .set = twl_set, - .to_irq = twl_to_irq, - .can_sleep = 1, -}; - -/*----------------------------------------------------------------------*/ - -static int __devinit gpio_twl4030_pulls(u32 ups, u32 downs) -{ - u8 message[6]; - unsigned i, gpio_bit; - - /* For most pins, a pulldown was enabled by default. - * We should have data that's specific to this board. - */ - for (gpio_bit = 1, i = 1; i < 6; i++) { - u8 bit_mask; - unsigned j; - - for (bit_mask = 0, j = 0; j < 8; j += 2, gpio_bit <<= 1) { - if (ups & gpio_bit) - bit_mask |= 1 << (j + 1); - else if (downs & gpio_bit) - bit_mask |= 1 << (j + 0); - } - message[i] = bit_mask; - } - - return twl_i2c_write(TWL4030_MODULE_GPIO, message, - REG_GPIOPUPDCTR1, 5); -} - -static int __devinit gpio_twl4030_debounce(u32 debounce, u8 mmc_cd) -{ - u8 message[4]; - - /* 30 msec of debouncing is always used for MMC card detect, - * and is optional for everything else. - */ - message[1] = (debounce & 0xff) | (mmc_cd & 0x03); - debounce >>= 8; - message[2] = (debounce & 0xff); - debounce >>= 8; - message[3] = (debounce & 0x03); - - return twl_i2c_write(TWL4030_MODULE_GPIO, message, - REG_GPIO_DEBEN1, 3); -} - -static int gpio_twl4030_remove(struct platform_device *pdev); - -static int __devinit gpio_twl4030_probe(struct platform_device *pdev) -{ - struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data; - int ret; - - /* maybe setup IRQs */ - if (pdata->irq_base) { - if (is_module()) { - dev_err(&pdev->dev, - "can't dispatch IRQs from modules\n"); - goto no_irqs; - } - ret = twl4030_sih_setup(TWL4030_MODULE_GPIO); - if (ret < 0) - return ret; - WARN_ON(ret != pdata->irq_base); - twl4030_gpio_irq_base = ret; - } - -no_irqs: - /* - * NOTE: boards may waste power if they don't set pullups - * and pulldowns correctly ... default for non-ULPI pins is - * pulldown, and some other pins may have external pullups - * or pulldowns. Careful! - */ - ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns); - if (ret) - dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n", - pdata->pullups, pdata->pulldowns, - ret); - - ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd); - if (ret) - dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n", - pdata->debounce, pdata->mmc_cd, - ret); - - twl_gpiochip.base = pdata->gpio_base; - twl_gpiochip.ngpio = TWL4030_GPIO_MAX; - twl_gpiochip.dev = &pdev->dev; - - /* NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE, - * is (still) clear if use_leds is set. - */ - if (pdata->use_leds) - twl_gpiochip.ngpio += 2; - - ret = gpiochip_add(&twl_gpiochip); - if (ret < 0) { - dev_err(&pdev->dev, - "could not register gpiochip, %d\n", - ret); - twl_gpiochip.ngpio = 0; - gpio_twl4030_remove(pdev); - } else if (pdata->setup) { - int status; - - status = pdata->setup(&pdev->dev, - pdata->gpio_base, TWL4030_GPIO_MAX); - if (status) - dev_dbg(&pdev->dev, "setup --> %d\n", status); - } - - return ret; -} - -/* Cannot use __devexit as gpio_twl4030_probe() calls us */ -static int gpio_twl4030_remove(struct platform_device *pdev) -{ - struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data; - int status; - - if (pdata->teardown) { - status = pdata->teardown(&pdev->dev, - pdata->gpio_base, TWL4030_GPIO_MAX); - if (status) { - dev_dbg(&pdev->dev, "teardown --> %d\n", status); - return status; - } - } - - status = gpiochip_remove(&twl_gpiochip); - if (status < 0) - return status; - - if (is_module()) - return 0; - - /* REVISIT no support yet for deregistering all the IRQs */ - WARN_ON(1); - return -EIO; -} - -/* Note: this hardware lives inside an I2C-based multi-function device. */ -MODULE_ALIAS("platform:twl4030_gpio"); - -static struct platform_driver gpio_twl4030_driver = { - .driver.name = "twl4030_gpio", - .driver.owner = THIS_MODULE, - .probe = gpio_twl4030_probe, - .remove = gpio_twl4030_remove, -}; - -static int __init gpio_twl4030_init(void) -{ - return platform_driver_register(&gpio_twl4030_driver); -} -subsys_initcall(gpio_twl4030_init); - -static void __exit gpio_twl4030_exit(void) -{ - platform_driver_unregister(&gpio_twl4030_driver); -} -module_exit(gpio_twl4030_exit); - -MODULE_AUTHOR("Texas Instruments, Inc."); -MODULE_DESCRIPTION("GPIO interface for TWL4030"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/ucb1400_gpio.c b/drivers/gpio/ucb1400_gpio.c deleted file mode 100644 index 50e6bd1392c..00000000000 --- a/drivers/gpio/ucb1400_gpio.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Philips UCB1400 GPIO driver - * - * Author: Marek Vasut - * - * 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 - -struct ucb1400_gpio_data *ucbdata; - -static int ucb1400_gpio_dir_in(struct gpio_chip *gc, unsigned off) -{ - struct ucb1400_gpio *gpio; - gpio = container_of(gc, struct ucb1400_gpio, gc); - ucb1400_gpio_set_direction(gpio->ac97, off, 0); - return 0; -} - -static int ucb1400_gpio_dir_out(struct gpio_chip *gc, unsigned off, int val) -{ - struct ucb1400_gpio *gpio; - gpio = container_of(gc, struct ucb1400_gpio, gc); - ucb1400_gpio_set_direction(gpio->ac97, off, 1); - ucb1400_gpio_set_value(gpio->ac97, off, val); - return 0; -} - -static int ucb1400_gpio_get(struct gpio_chip *gc, unsigned off) -{ - struct ucb1400_gpio *gpio; - gpio = container_of(gc, struct ucb1400_gpio, gc); - return ucb1400_gpio_get_value(gpio->ac97, off); -} - -static void ucb1400_gpio_set(struct gpio_chip *gc, unsigned off, int val) -{ - struct ucb1400_gpio *gpio; - gpio = container_of(gc, struct ucb1400_gpio, gc); - ucb1400_gpio_set_value(gpio->ac97, off, val); -} - -static int ucb1400_gpio_probe(struct platform_device *dev) -{ - struct ucb1400_gpio *ucb = dev->dev.platform_data; - int err = 0; - - if (!(ucbdata && ucbdata->gpio_offset)) { - err = -EINVAL; - goto err; - } - - platform_set_drvdata(dev, ucb); - - ucb->gc.label = "ucb1400_gpio"; - ucb->gc.base = ucbdata->gpio_offset; - ucb->gc.ngpio = 10; - ucb->gc.owner = THIS_MODULE; - - ucb->gc.direction_input = ucb1400_gpio_dir_in; - ucb->gc.direction_output = ucb1400_gpio_dir_out; - ucb->gc.get = ucb1400_gpio_get; - ucb->gc.set = ucb1400_gpio_set; - ucb->gc.can_sleep = 1; - - err = gpiochip_add(&ucb->gc); - if (err) - goto err; - - if (ucbdata && ucbdata->gpio_setup) - err = ucbdata->gpio_setup(&dev->dev, ucb->gc.ngpio); - -err: - return err; - -} - -static int ucb1400_gpio_remove(struct platform_device *dev) -{ - int err = 0; - struct ucb1400_gpio *ucb = platform_get_drvdata(dev); - - if (ucbdata && ucbdata->gpio_teardown) { - err = ucbdata->gpio_teardown(&dev->dev, ucb->gc.ngpio); - if (err) - return err; - } - - err = gpiochip_remove(&ucb->gc); - return err; -} - -static struct platform_driver ucb1400_gpio_driver = { - .probe = ucb1400_gpio_probe, - .remove = ucb1400_gpio_remove, - .driver = { - .name = "ucb1400_gpio" - }, -}; - -static int __init ucb1400_gpio_init(void) -{ - return platform_driver_register(&ucb1400_gpio_driver); -} - -static void __exit ucb1400_gpio_exit(void) -{ - platform_driver_unregister(&ucb1400_gpio_driver); -} - -void __init ucb1400_gpio_set_data(struct ucb1400_gpio_data *data) -{ - ucbdata = data; -} - -module_init(ucb1400_gpio_init); -module_exit(ucb1400_gpio_exit); - -MODULE_DESCRIPTION("Philips UCB1400 GPIO driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/vr41xx_giu.c b/drivers/gpio/vr41xx_giu.c deleted file mode 100644 index a365be040b3..00000000000 --- a/drivers/gpio/vr41xx_giu.c +++ /dev/null @@ -1,585 +0,0 @@ -/* - * Driver for NEC VR4100 series General-purpose I/O Unit. - * - * Copyright (C) 2002 MontaVista Software Inc. - * Author: Yoichi Yuasa - * Copyright (C) 2003-2009 Yoichi Yuasa - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -MODULE_AUTHOR("Yoichi Yuasa "); -MODULE_DESCRIPTION("NEC VR4100 series General-purpose I/O Unit driver"); -MODULE_LICENSE("GPL"); - -#define GIUIOSELL 0x00 -#define GIUIOSELH 0x02 -#define GIUPIODL 0x04 -#define GIUPIODH 0x06 -#define GIUINTSTATL 0x08 -#define GIUINTSTATH 0x0a -#define GIUINTENL 0x0c -#define GIUINTENH 0x0e -#define GIUINTTYPL 0x10 -#define GIUINTTYPH 0x12 -#define GIUINTALSELL 0x14 -#define GIUINTALSELH 0x16 -#define GIUINTHTSELL 0x18 -#define GIUINTHTSELH 0x1a -#define GIUPODATL 0x1c -#define GIUPODATEN 0x1c -#define GIUPODATH 0x1e - #define PIOEN0 0x0100 - #define PIOEN1 0x0200 -#define GIUPODAT 0x1e -#define GIUFEDGEINHL 0x20 -#define GIUFEDGEINHH 0x22 -#define GIUREDGEINHL 0x24 -#define GIUREDGEINHH 0x26 - -#define GIUUSEUPDN 0x1e0 -#define GIUTERMUPDN 0x1e2 - -#define GPIO_HAS_PULLUPDOWN_IO 0x0001 -#define GPIO_HAS_OUTPUT_ENABLE 0x0002 -#define GPIO_HAS_INTERRUPT_EDGE_SELECT 0x0100 - -enum { - GPIO_INPUT, - GPIO_OUTPUT, -}; - -static DEFINE_SPINLOCK(giu_lock); -static unsigned long giu_flags; - -static void __iomem *giu_base; - -#define giu_read(offset) readw(giu_base + (offset)) -#define giu_write(offset, value) writew((value), giu_base + (offset)) - -#define GPIO_PIN_OF_IRQ(irq) ((irq) - GIU_IRQ_BASE) -#define GIUINT_HIGH_OFFSET 16 -#define GIUINT_HIGH_MAX 32 - -static inline u16 giu_set(u16 offset, u16 set) -{ - u16 data; - - data = giu_read(offset); - data |= set; - giu_write(offset, data); - - return data; -} - -static inline u16 giu_clear(u16 offset, u16 clear) -{ - u16 data; - - data = giu_read(offset); - data &= ~clear; - giu_write(offset, data); - - return data; -} - -static void ack_giuint_low(struct irq_data *d) -{ - giu_write(GIUINTSTATL, 1 << GPIO_PIN_OF_IRQ(d->irq)); -} - -static void mask_giuint_low(struct irq_data *d) -{ - giu_clear(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(d->irq)); -} - -static void mask_ack_giuint_low(struct irq_data *d) -{ - unsigned int pin; - - pin = GPIO_PIN_OF_IRQ(d->irq); - giu_clear(GIUINTENL, 1 << pin); - giu_write(GIUINTSTATL, 1 << pin); -} - -static void unmask_giuint_low(struct irq_data *d) -{ - giu_set(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(d->irq)); -} - -static struct irq_chip giuint_low_irq_chip = { - .name = "GIUINTL", - .irq_ack = ack_giuint_low, - .irq_mask = mask_giuint_low, - .irq_mask_ack = mask_ack_giuint_low, - .irq_unmask = unmask_giuint_low, -}; - -static void ack_giuint_high(struct irq_data *d) -{ - giu_write(GIUINTSTATH, - 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET)); -} - -static void mask_giuint_high(struct irq_data *d) -{ - giu_clear(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET)); -} - -static void mask_ack_giuint_high(struct irq_data *d) -{ - unsigned int pin; - - pin = GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET; - giu_clear(GIUINTENH, 1 << pin); - giu_write(GIUINTSTATH, 1 << pin); -} - -static void unmask_giuint_high(struct irq_data *d) -{ - giu_set(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET)); -} - -static struct irq_chip giuint_high_irq_chip = { - .name = "GIUINTH", - .irq_ack = ack_giuint_high, - .irq_mask = mask_giuint_high, - .irq_mask_ack = mask_ack_giuint_high, - .irq_unmask = unmask_giuint_high, -}; - -static int giu_get_irq(unsigned int irq) -{ - u16 pendl, pendh, maskl, maskh; - int i; - - pendl = giu_read(GIUINTSTATL); - pendh = giu_read(GIUINTSTATH); - maskl = giu_read(GIUINTENL); - maskh = giu_read(GIUINTENH); - - maskl &= pendl; - maskh &= pendh; - - if (maskl) { - for (i = 0; i < 16; i++) { - if (maskl & (1 << i)) - return GIU_IRQ(i); - } - } else if (maskh) { - for (i = 0; i < 16; i++) { - if (maskh & (1 << i)) - return GIU_IRQ(i + GIUINT_HIGH_OFFSET); - } - } - - printk(KERN_ERR "spurious GIU interrupt: %04x(%04x),%04x(%04x)\n", - maskl, pendl, maskh, pendh); - - atomic_inc(&irq_err_count); - - return -EINVAL; -} - -void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger, - irq_signal_t signal) -{ - u16 mask; - - if (pin < GIUINT_HIGH_OFFSET) { - mask = 1 << pin; - if (trigger != IRQ_TRIGGER_LEVEL) { - giu_set(GIUINTTYPL, mask); - if (signal == IRQ_SIGNAL_HOLD) - giu_set(GIUINTHTSELL, mask); - else - giu_clear(GIUINTHTSELL, mask); - if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) { - switch (trigger) { - case IRQ_TRIGGER_EDGE_FALLING: - giu_set(GIUFEDGEINHL, mask); - giu_clear(GIUREDGEINHL, mask); - break; - case IRQ_TRIGGER_EDGE_RISING: - giu_clear(GIUFEDGEINHL, mask); - giu_set(GIUREDGEINHL, mask); - break; - default: - giu_set(GIUFEDGEINHL, mask); - giu_set(GIUREDGEINHL, mask); - break; - } - } - irq_set_chip_and_handler(GIU_IRQ(pin), - &giuint_low_irq_chip, - handle_edge_irq); - } else { - giu_clear(GIUINTTYPL, mask); - giu_clear(GIUINTHTSELL, mask); - irq_set_chip_and_handler(GIU_IRQ(pin), - &giuint_low_irq_chip, - handle_level_irq); - } - giu_write(GIUINTSTATL, mask); - } else if (pin < GIUINT_HIGH_MAX) { - mask = 1 << (pin - GIUINT_HIGH_OFFSET); - if (trigger != IRQ_TRIGGER_LEVEL) { - giu_set(GIUINTTYPH, mask); - if (signal == IRQ_SIGNAL_HOLD) - giu_set(GIUINTHTSELH, mask); - else - giu_clear(GIUINTHTSELH, mask); - if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) { - switch (trigger) { - case IRQ_TRIGGER_EDGE_FALLING: - giu_set(GIUFEDGEINHH, mask); - giu_clear(GIUREDGEINHH, mask); - break; - case IRQ_TRIGGER_EDGE_RISING: - giu_clear(GIUFEDGEINHH, mask); - giu_set(GIUREDGEINHH, mask); - break; - default: - giu_set(GIUFEDGEINHH, mask); - giu_set(GIUREDGEINHH, mask); - break; - } - } - irq_set_chip_and_handler(GIU_IRQ(pin), - &giuint_high_irq_chip, - handle_edge_irq); - } else { - giu_clear(GIUINTTYPH, mask); - giu_clear(GIUINTHTSELH, mask); - irq_set_chip_and_handler(GIU_IRQ(pin), - &giuint_high_irq_chip, - handle_level_irq); - } - giu_write(GIUINTSTATH, mask); - } -} -EXPORT_SYMBOL_GPL(vr41xx_set_irq_trigger); - -void vr41xx_set_irq_level(unsigned int pin, irq_level_t level) -{ - u16 mask; - - if (pin < GIUINT_HIGH_OFFSET) { - mask = 1 << pin; - if (level == IRQ_LEVEL_HIGH) - giu_set(GIUINTALSELL, mask); - else - giu_clear(GIUINTALSELL, mask); - giu_write(GIUINTSTATL, mask); - } else if (pin < GIUINT_HIGH_MAX) { - mask = 1 << (pin - GIUINT_HIGH_OFFSET); - if (level == IRQ_LEVEL_HIGH) - giu_set(GIUINTALSELH, mask); - else - giu_clear(GIUINTALSELH, mask); - giu_write(GIUINTSTATH, mask); - } -} -EXPORT_SYMBOL_GPL(vr41xx_set_irq_level); - -static int giu_set_direction(struct gpio_chip *chip, unsigned pin, int dir) -{ - u16 offset, mask, reg; - unsigned long flags; - - if (pin >= chip->ngpio) - return -EINVAL; - - if (pin < 16) { - offset = GIUIOSELL; - mask = 1 << pin; - } else if (pin < 32) { - offset = GIUIOSELH; - mask = 1 << (pin - 16); - } else { - if (giu_flags & GPIO_HAS_OUTPUT_ENABLE) { - offset = GIUPODATEN; - mask = 1 << (pin - 32); - } else { - switch (pin) { - case 48: - offset = GIUPODATH; - mask = PIOEN0; - break; - case 49: - offset = GIUPODATH; - mask = PIOEN1; - break; - default: - return -EINVAL; - } - } - } - - spin_lock_irqsave(&giu_lock, flags); - - reg = giu_read(offset); - if (dir == GPIO_OUTPUT) - reg |= mask; - else - reg &= ~mask; - giu_write(offset, reg); - - spin_unlock_irqrestore(&giu_lock, flags); - - return 0; -} - -int vr41xx_gpio_pullupdown(unsigned int pin, gpio_pull_t pull) -{ - u16 reg, mask; - unsigned long flags; - - if ((giu_flags & GPIO_HAS_PULLUPDOWN_IO) != GPIO_HAS_PULLUPDOWN_IO) - return -EPERM; - - if (pin >= 15) - return -EINVAL; - - mask = 1 << pin; - - spin_lock_irqsave(&giu_lock, flags); - - if (pull == GPIO_PULL_UP || pull == GPIO_PULL_DOWN) { - reg = giu_read(GIUTERMUPDN); - if (pull == GPIO_PULL_UP) - reg |= mask; - else - reg &= ~mask; - giu_write(GIUTERMUPDN, reg); - - reg = giu_read(GIUUSEUPDN); - reg |= mask; - giu_write(GIUUSEUPDN, reg); - } else { - reg = giu_read(GIUUSEUPDN); - reg &= ~mask; - giu_write(GIUUSEUPDN, reg); - } - - spin_unlock_irqrestore(&giu_lock, flags); - - return 0; -} -EXPORT_SYMBOL_GPL(vr41xx_gpio_pullupdown); - -static int vr41xx_gpio_get(struct gpio_chip *chip, unsigned pin) -{ - u16 reg, mask; - - if (pin >= chip->ngpio) - return -EINVAL; - - if (pin < 16) { - reg = giu_read(GIUPIODL); - mask = 1 << pin; - } else if (pin < 32) { - reg = giu_read(GIUPIODH); - mask = 1 << (pin - 16); - } else if (pin < 48) { - reg = giu_read(GIUPODATL); - mask = 1 << (pin - 32); - } else { - reg = giu_read(GIUPODATH); - mask = 1 << (pin - 48); - } - - if (reg & mask) - return 1; - - return 0; -} - -static void vr41xx_gpio_set(struct gpio_chip *chip, unsigned pin, - int value) -{ - u16 offset, mask, reg; - unsigned long flags; - - if (pin >= chip->ngpio) - return; - - if (pin < 16) { - offset = GIUPIODL; - mask = 1 << pin; - } else if (pin < 32) { - offset = GIUPIODH; - mask = 1 << (pin - 16); - } else if (pin < 48) { - offset = GIUPODATL; - mask = 1 << (pin - 32); - } else { - offset = GIUPODATH; - mask = 1 << (pin - 48); - } - - spin_lock_irqsave(&giu_lock, flags); - - reg = giu_read(offset); - if (value) - reg |= mask; - else - reg &= ~mask; - giu_write(offset, reg); - - spin_unlock_irqrestore(&giu_lock, flags); -} - - -static int vr41xx_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - return giu_set_direction(chip, offset, GPIO_INPUT); -} - -static int vr41xx_gpio_direction_output(struct gpio_chip *chip, unsigned offset, - int value) -{ - vr41xx_gpio_set(chip, offset, value); - - return giu_set_direction(chip, offset, GPIO_OUTPUT); -} - -static int vr41xx_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - if (offset >= chip->ngpio) - return -EINVAL; - - return GIU_IRQ_BASE + offset; -} - -static struct gpio_chip vr41xx_gpio_chip = { - .label = "vr41xx", - .owner = THIS_MODULE, - .direction_input = vr41xx_gpio_direction_input, - .get = vr41xx_gpio_get, - .direction_output = vr41xx_gpio_direction_output, - .set = vr41xx_gpio_set, - .to_irq = vr41xx_gpio_to_irq, -}; - -static int __devinit giu_probe(struct platform_device *pdev) -{ - struct resource *res; - unsigned int trigger, i, pin; - struct irq_chip *chip; - int irq, retval; - - switch (pdev->id) { - case GPIO_50PINS_PULLUPDOWN: - giu_flags = GPIO_HAS_PULLUPDOWN_IO; - vr41xx_gpio_chip.ngpio = 50; - break; - case GPIO_36PINS: - vr41xx_gpio_chip.ngpio = 36; - break; - case GPIO_48PINS_EDGE_SELECT: - giu_flags = GPIO_HAS_INTERRUPT_EDGE_SELECT; - vr41xx_gpio_chip.ngpio = 48; - break; - default: - dev_err(&pdev->dev, "GIU: unknown ID %d\n", pdev->id); - return -ENODEV; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EBUSY; - - giu_base = ioremap(res->start, res->end - res->start + 1); - if (!giu_base) - return -ENOMEM; - - vr41xx_gpio_chip.dev = &pdev->dev; - - retval = gpiochip_add(&vr41xx_gpio_chip); - - giu_write(GIUINTENL, 0); - giu_write(GIUINTENH, 0); - - trigger = giu_read(GIUINTTYPH) << 16; - trigger |= giu_read(GIUINTTYPL); - for (i = GIU_IRQ_BASE; i <= GIU_IRQ_LAST; i++) { - pin = GPIO_PIN_OF_IRQ(i); - if (pin < GIUINT_HIGH_OFFSET) - chip = &giuint_low_irq_chip; - else - chip = &giuint_high_irq_chip; - - if (trigger & (1 << pin)) - irq_set_chip_and_handler(i, chip, handle_edge_irq); - else - irq_set_chip_and_handler(i, chip, handle_level_irq); - - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0 || irq >= nr_irqs) - return -EBUSY; - - return cascade_irq(irq, giu_get_irq); -} - -static int __devexit giu_remove(struct platform_device *pdev) -{ - if (giu_base) { - iounmap(giu_base); - giu_base = NULL; - } - - return 0; -} - -static struct platform_driver giu_device_driver = { - .probe = giu_probe, - .remove = __devexit_p(giu_remove), - .driver = { - .name = "GIU", - .owner = THIS_MODULE, - }, -}; - -static int __init vr41xx_giu_init(void) -{ - return platform_driver_register(&giu_device_driver); -} - -static void __exit vr41xx_giu_exit(void) -{ - platform_driver_unregister(&giu_device_driver); -} - -module_init(vr41xx_giu_init); -module_exit(vr41xx_giu_exit); diff --git a/drivers/gpio/vx855_gpio.c b/drivers/gpio/vx855_gpio.c deleted file mode 100644 index ef5aabd8b8b..00000000000 --- a/drivers/gpio/vx855_gpio.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Linux GPIOlib driver for the VIA VX855 integrated southbridge GPIO - * - * Copyright (C) 2009 VIA Technologies, Inc. - * Copyright (C) 2010 One Laptop per Child - * Author: Harald Welte - * 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., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define MODULE_NAME "vx855_gpio" - -/* The VX855 south bridge has the following GPIO pins: - * GPI 0...13 General Purpose Input - * GPO 0...12 General Purpose Output - * GPIO 0...14 General Purpose I/O (Open-Drain) - */ - -#define NR_VX855_GPI 14 -#define NR_VX855_GPO 13 -#define NR_VX855_GPIO 15 - -#define NR_VX855_GPInO (NR_VX855_GPI + NR_VX855_GPO) -#define NR_VX855_GP (NR_VX855_GPI + NR_VX855_GPO + NR_VX855_GPIO) - -struct vx855_gpio { - struct gpio_chip gpio; - spinlock_t lock; - u32 io_gpi; - u32 io_gpo; - bool gpi_reserved; - bool gpo_reserved; -}; - -/* resolve a GPIx into the corresponding bit position */ -static inline u_int32_t gpi_i_bit(int i) -{ - if (i < 10) - return 1 << i; - else - return 1 << (i + 14); -} - -static inline u_int32_t gpo_o_bit(int i) -{ - if (i < 11) - return 1 << i; - else - return 1 << (i + 14); -} - -static inline u_int32_t gpio_i_bit(int i) -{ - if (i < 14) - return 1 << (i + 10); - else - return 1 << (i + 14); -} - -static inline u_int32_t gpio_o_bit(int i) -{ - if (i < 14) - return 1 << (i + 11); - else - return 1 << (i + 13); -} - -/* Mapping betwee numeric GPIO ID and the actual GPIO hardware numbering: - * 0..13 GPI 0..13 - * 14..26 GPO 0..12 - * 27..41 GPIO 0..14 - */ - -static int vx855gpio_direction_input(struct gpio_chip *gpio, - unsigned int nr) -{ - struct vx855_gpio *vg = container_of(gpio, struct vx855_gpio, gpio); - unsigned long flags; - u_int32_t reg_out; - - /* Real GPI bits are always in input direction */ - if (nr < NR_VX855_GPI) - return 0; - - /* Real GPO bits cannot be put in output direction */ - if (nr < NR_VX855_GPInO) - return -EINVAL; - - /* Open Drain GPIO have to be set to one */ - spin_lock_irqsave(&vg->lock, flags); - reg_out = inl(vg->io_gpo); - reg_out |= gpio_o_bit(nr - NR_VX855_GPInO); - outl(reg_out, vg->io_gpo); - spin_unlock_irqrestore(&vg->lock, flags); - - return 0; -} - -static int vx855gpio_get(struct gpio_chip *gpio, unsigned int nr) -{ - struct vx855_gpio *vg = container_of(gpio, struct vx855_gpio, gpio); - u_int32_t reg_in; - int ret = 0; - - if (nr < NR_VX855_GPI) { - reg_in = inl(vg->io_gpi); - if (reg_in & gpi_i_bit(nr)) - ret = 1; - } else if (nr < NR_VX855_GPInO) { - /* GPO don't have an input bit, we need to read it - * back from the output register */ - reg_in = inl(vg->io_gpo); - if (reg_in & gpo_o_bit(nr - NR_VX855_GPI)) - ret = 1; - } else { - reg_in = inl(vg->io_gpi); - if (reg_in & gpio_i_bit(nr - NR_VX855_GPInO)) - ret = 1; - } - - return ret; -} - -static void vx855gpio_set(struct gpio_chip *gpio, unsigned int nr, - int val) -{ - struct vx855_gpio *vg = container_of(gpio, struct vx855_gpio, gpio); - unsigned long flags; - u_int32_t reg_out; - - /* True GPI cannot be switched to output mode */ - if (nr < NR_VX855_GPI) - return; - - spin_lock_irqsave(&vg->lock, flags); - reg_out = inl(vg->io_gpo); - if (nr < NR_VX855_GPInO) { - if (val) - reg_out |= gpo_o_bit(nr - NR_VX855_GPI); - else - reg_out &= ~gpo_o_bit(nr - NR_VX855_GPI); - } else { - if (val) - reg_out |= gpio_o_bit(nr - NR_VX855_GPInO); - else - reg_out &= ~gpio_o_bit(nr - NR_VX855_GPInO); - } - outl(reg_out, vg->io_gpo); - spin_unlock_irqrestore(&vg->lock, flags); -} - -static int vx855gpio_direction_output(struct gpio_chip *gpio, - unsigned int nr, int val) -{ - /* True GPI cannot be switched to output mode */ - if (nr < NR_VX855_GPI) - return -EINVAL; - - /* True GPO don't need to be switched to output mode, - * and GPIO are open-drain, i.e. also need no switching, - * so all we do is set the level */ - vx855gpio_set(gpio, nr, val); - - return 0; -} - -static const char *vx855gpio_names[NR_VX855_GP] = { - "VX855_GPI0", "VX855_GPI1", "VX855_GPI2", "VX855_GPI3", "VX855_GPI4", - "VX855_GPI5", "VX855_GPI6", "VX855_GPI7", "VX855_GPI8", "VX855_GPI9", - "VX855_GPI10", "VX855_GPI11", "VX855_GPI12", "VX855_GPI13", - "VX855_GPO0", "VX855_GPO1", "VX855_GPO2", "VX855_GPO3", "VX855_GPO4", - "VX855_GPO5", "VX855_GPO6", "VX855_GPO7", "VX855_GPO8", "VX855_GPO9", - "VX855_GPO10", "VX855_GPO11", "VX855_GPO12", - "VX855_GPIO0", "VX855_GPIO1", "VX855_GPIO2", "VX855_GPIO3", - "VX855_GPIO4", "VX855_GPIO5", "VX855_GPIO6", "VX855_GPIO7", - "VX855_GPIO8", "VX855_GPIO9", "VX855_GPIO10", "VX855_GPIO11", - "VX855_GPIO12", "VX855_GPIO13", "VX855_GPIO14" -}; - -static void vx855gpio_gpio_setup(struct vx855_gpio *vg) -{ - struct gpio_chip *c = &vg->gpio; - - c->label = "VX855 South Bridge"; - c->owner = THIS_MODULE; - c->direction_input = vx855gpio_direction_input; - c->direction_output = vx855gpio_direction_output; - c->get = vx855gpio_get; - c->set = vx855gpio_set; - c->dbg_show = NULL; - c->base = 0; - c->ngpio = NR_VX855_GP; - c->can_sleep = 0; - c->names = vx855gpio_names; -} - -/* This platform device is ordinarily registered by the vx855 mfd driver */ -static __devinit int vx855gpio_probe(struct platform_device *pdev) -{ - struct resource *res_gpi; - struct resource *res_gpo; - struct vx855_gpio *vg; - int ret; - - res_gpi = platform_get_resource(pdev, IORESOURCE_IO, 0); - res_gpo = platform_get_resource(pdev, IORESOURCE_IO, 1); - if (!res_gpi || !res_gpo) - return -EBUSY; - - vg = kzalloc(sizeof(*vg), GFP_KERNEL); - if (!vg) - return -ENOMEM; - - platform_set_drvdata(pdev, vg); - - dev_info(&pdev->dev, "found VX855 GPIO controller\n"); - vg->io_gpi = res_gpi->start; - vg->io_gpo = res_gpo->start; - spin_lock_init(&vg->lock); - - /* - * A single byte is used to control various GPIO ports on the VX855, - * and in the case of the OLPC XO-1.5, some of those ports are used - * for switches that are interpreted and exposed through ACPI. ACPI - * will have reserved the region, so our own reservation will not - * succeed. Ignore and continue. - */ - - if (!request_region(res_gpi->start, resource_size(res_gpi), - MODULE_NAME "_gpi")) - dev_warn(&pdev->dev, - "GPI I/O resource busy, probably claimed by ACPI\n"); - else - vg->gpi_reserved = true; - - if (!request_region(res_gpo->start, resource_size(res_gpo), - MODULE_NAME "_gpo")) - dev_warn(&pdev->dev, - "GPO I/O resource busy, probably claimed by ACPI\n"); - else - vg->gpo_reserved = true; - - vx855gpio_gpio_setup(vg); - - ret = gpiochip_add(&vg->gpio); - if (ret) { - dev_err(&pdev->dev, "failed to register GPIOs\n"); - goto out_release; - } - - return 0; - -out_release: - if (vg->gpi_reserved) - release_region(res_gpi->start, resource_size(res_gpi)); - if (vg->gpo_reserved) - release_region(res_gpi->start, resource_size(res_gpo)); - platform_set_drvdata(pdev, NULL); - kfree(vg); - return ret; -} - -static int __devexit vx855gpio_remove(struct platform_device *pdev) -{ - struct vx855_gpio *vg = platform_get_drvdata(pdev); - struct resource *res; - - if (gpiochip_remove(&vg->gpio)) - dev_err(&pdev->dev, "unable to remove gpio_chip?\n"); - - if (vg->gpi_reserved) { - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - release_region(res->start, resource_size(res)); - } - if (vg->gpo_reserved) { - res = platform_get_resource(pdev, IORESOURCE_IO, 1); - release_region(res->start, resource_size(res)); - } - - platform_set_drvdata(pdev, NULL); - kfree(vg); - return 0; -} - -static struct platform_driver vx855gpio_driver = { - .driver = { - .name = MODULE_NAME, - .owner = THIS_MODULE, - }, - .probe = vx855gpio_probe, - .remove = __devexit_p(vx855gpio_remove), -}; - -static int vx855gpio_init(void) -{ - return platform_driver_register(&vx855gpio_driver); -} -module_init(vx855gpio_init); - -static void vx855gpio_exit(void) -{ - platform_driver_unregister(&vx855gpio_driver); -} -module_exit(vx855gpio_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("GPIO driver for the VIA VX855 chipset"); -MODULE_ALIAS("platform:vx855_gpio"); diff --git a/drivers/gpio/wm831x-gpio.c b/drivers/gpio/wm831x-gpio.c deleted file mode 100644 index 309644cf4d9..00000000000 --- a/drivers/gpio/wm831x-gpio.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * wm831x-gpio.c -- gpiolib support for Wolfson WM831x PMICs - * - * Copyright 2009 Wolfson Microelectronics PLC. - * - * Author: Mark Brown - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -struct wm831x_gpio { - struct wm831x *wm831x; - struct gpio_chip gpio_chip; -}; - -static inline struct wm831x_gpio *to_wm831x_gpio(struct gpio_chip *chip) -{ - return container_of(chip, struct wm831x_gpio, gpio_chip); -} - -static int wm831x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) -{ - struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); - struct wm831x *wm831x = wm831x_gpio->wm831x; - int val = WM831X_GPN_DIR; - - if (wm831x->has_gpio_ena) - val |= WM831X_GPN_TRI; - - return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset, - WM831X_GPN_DIR | WM831X_GPN_TRI | - WM831X_GPN_FN_MASK, val); -} - -static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); - struct wm831x *wm831x = wm831x_gpio->wm831x; - int ret; - - ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); - if (ret < 0) - return ret; - - if (ret & 1 << offset) - return 1; - else - return 0; -} - -static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); - struct wm831x *wm831x = wm831x_gpio->wm831x; - - wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, 1 << offset, - value << offset); -} - -static int wm831x_gpio_direction_out(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); - struct wm831x *wm831x = wm831x_gpio->wm831x; - int val = 0; - int ret; - - if (wm831x->has_gpio_ena) - val |= WM831X_GPN_TRI; - - ret = wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset, - WM831X_GPN_DIR | WM831X_GPN_TRI | - WM831X_GPN_FN_MASK, val); - if (ret < 0) - return ret; - - /* Can only set GPIO state once it's in output mode */ - wm831x_gpio_set(chip, offset, value); - - return 0; -} - -static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); - struct wm831x *wm831x = wm831x_gpio->wm831x; - - if (!wm831x->irq_base) - return -EINVAL; - - return wm831x->irq_base + WM831X_IRQ_GPIO_1 + offset; -} - -static int wm831x_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, - unsigned debounce) -{ - struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); - struct wm831x *wm831x = wm831x_gpio->wm831x; - int reg = WM831X_GPIO1_CONTROL + offset; - int ret, fn; - - ret = wm831x_reg_read(wm831x, reg); - if (ret < 0) - return ret; - - switch (ret & WM831X_GPN_FN_MASK) { - case 0: - case 1: - break; - default: - /* Not in GPIO mode */ - return -EBUSY; - } - - if (debounce >= 32 && debounce <= 64) - fn = 0; - else if (debounce >= 4000 && debounce <= 8000) - fn = 1; - else - return -EINVAL; - - return wm831x_set_bits(wm831x, reg, WM831X_GPN_FN_MASK, fn); -} - -#ifdef CONFIG_DEBUG_FS -static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) -{ - struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); - struct wm831x *wm831x = wm831x_gpio->wm831x; - int i, tristated; - - for (i = 0; i < chip->ngpio; i++) { - int gpio = i + chip->base; - int reg; - const char *label, *pull, *powerdomain; - - /* We report the GPIO even if it's not requested since - * we're also reporting things like alternate - * functions which apply even when the GPIO is not in - * use as a GPIO. - */ - label = gpiochip_is_requested(chip, i); - if (!label) - label = "Unrequested"; - - seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label); - - reg = wm831x_reg_read(wm831x, WM831X_GPIO1_CONTROL + i); - if (reg < 0) { - dev_err(wm831x->dev, - "GPIO control %d read failed: %d\n", - gpio, reg); - seq_printf(s, "\n"); - continue; - } - - switch (reg & WM831X_GPN_PULL_MASK) { - case WM831X_GPIO_PULL_NONE: - pull = "nopull"; - break; - case WM831X_GPIO_PULL_DOWN: - pull = "pulldown"; - break; - case WM831X_GPIO_PULL_UP: - pull = "pullup"; - default: - pull = "INVALID PULL"; - break; - } - - switch (i + 1) { - case 1 ... 3: - case 7 ... 9: - if (reg & WM831X_GPN_PWR_DOM) - powerdomain = "VPMIC"; - else - powerdomain = "DBVDD"; - break; - - case 4 ... 6: - case 10 ... 12: - if (reg & WM831X_GPN_PWR_DOM) - powerdomain = "SYSVDD"; - else - powerdomain = "DBVDD"; - break; - - case 13 ... 16: - powerdomain = "TPVDD"; - break; - - default: - BUG(); - break; - } - - tristated = reg & WM831X_GPN_TRI; - if (wm831x->has_gpio_ena) - tristated = !tristated; - - seq_printf(s, " %s %s %s %s%s\n" - " %s%s (0x%4x)\n", - reg & WM831X_GPN_DIR ? "in" : "out", - wm831x_gpio_get(chip, i) ? "high" : "low", - pull, - powerdomain, - reg & WM831X_GPN_POL ? "" : " inverted", - reg & WM831X_GPN_OD ? "open-drain" : "CMOS", - tristated ? " tristated" : "", - reg); - } -} -#else -#define wm831x_gpio_dbg_show NULL -#endif - -static struct gpio_chip template_chip = { - .label = "wm831x", - .owner = THIS_MODULE, - .direction_input = wm831x_gpio_direction_in, - .get = wm831x_gpio_get, - .direction_output = wm831x_gpio_direction_out, - .set = wm831x_gpio_set, - .to_irq = wm831x_gpio_to_irq, - .set_debounce = wm831x_gpio_set_debounce, - .dbg_show = wm831x_gpio_dbg_show, - .can_sleep = 1, -}; - -static int __devinit wm831x_gpio_probe(struct platform_device *pdev) -{ - struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); - struct wm831x_pdata *pdata = wm831x->dev->platform_data; - struct wm831x_gpio *wm831x_gpio; - int ret; - - wm831x_gpio = kzalloc(sizeof(*wm831x_gpio), GFP_KERNEL); - if (wm831x_gpio == NULL) - return -ENOMEM; - - wm831x_gpio->wm831x = wm831x; - wm831x_gpio->gpio_chip = template_chip; - wm831x_gpio->gpio_chip.ngpio = wm831x->num_gpio; - wm831x_gpio->gpio_chip.dev = &pdev->dev; - if (pdata && pdata->gpio_base) - wm831x_gpio->gpio_chip.base = pdata->gpio_base; - else - wm831x_gpio->gpio_chip.base = -1; - - ret = gpiochip_add(&wm831x_gpio->gpio_chip); - if (ret < 0) { - dev_err(&pdev->dev, "Could not register gpiochip, %d\n", - ret); - goto err; - } - - platform_set_drvdata(pdev, wm831x_gpio); - - return ret; - -err: - kfree(wm831x_gpio); - return ret; -} - -static int __devexit wm831x_gpio_remove(struct platform_device *pdev) -{ - struct wm831x_gpio *wm831x_gpio = platform_get_drvdata(pdev); - int ret; - - ret = gpiochip_remove(&wm831x_gpio->gpio_chip); - if (ret == 0) - kfree(wm831x_gpio); - - return ret; -} - -static struct platform_driver wm831x_gpio_driver = { - .driver.name = "wm831x-gpio", - .driver.owner = THIS_MODULE, - .probe = wm831x_gpio_probe, - .remove = __devexit_p(wm831x_gpio_remove), -}; - -static int __init wm831x_gpio_init(void) -{ - return platform_driver_register(&wm831x_gpio_driver); -} -subsys_initcall(wm831x_gpio_init); - -static void __exit wm831x_gpio_exit(void) -{ - platform_driver_unregister(&wm831x_gpio_driver); -} -module_exit(wm831x_gpio_exit); - -MODULE_AUTHOR("Mark Brown "); -MODULE_DESCRIPTION("GPIO interface for WM831x PMICs"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:wm831x-gpio"); diff --git a/drivers/gpio/wm8350-gpiolib.c b/drivers/gpio/wm8350-gpiolib.c deleted file mode 100644 index 359999290f5..00000000000 --- a/drivers/gpio/wm8350-gpiolib.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * wm835x-gpiolib.c -- gpiolib support for Wolfson WM835x PMICs - * - * Copyright 2009 Wolfson Microelectronics PLC. - * - * Author: Mark Brown - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -struct wm8350_gpio_data { - struct wm8350 *wm8350; - struct gpio_chip gpio_chip; -}; - -static inline struct wm8350_gpio_data *to_wm8350_gpio(struct gpio_chip *chip) -{ - return container_of(chip, struct wm8350_gpio_data, gpio_chip); -} - -static int wm8350_gpio_direction_in(struct gpio_chip *chip, unsigned offset) -{ - struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip); - struct wm8350 *wm8350 = wm8350_gpio->wm8350; - - return wm8350_set_bits(wm8350, WM8350_GPIO_CONFIGURATION_I_O, - 1 << offset); -} - -static int wm8350_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip); - struct wm8350 *wm8350 = wm8350_gpio->wm8350; - int ret; - - ret = wm8350_reg_read(wm8350, WM8350_GPIO_LEVEL); - if (ret < 0) - return ret; - - if (ret & (1 << offset)) - return 1; - else - return 0; -} - -static void wm8350_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip); - struct wm8350 *wm8350 = wm8350_gpio->wm8350; - - if (value) - wm8350_set_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset); - else - wm8350_clear_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset); -} - -static int wm8350_gpio_direction_out(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip); - struct wm8350 *wm8350 = wm8350_gpio->wm8350; - int ret; - - ret = wm8350_clear_bits(wm8350, WM8350_GPIO_CONFIGURATION_I_O, - 1 << offset); - if (ret < 0) - return ret; - - /* Don't have an atomic direction/value setup */ - wm8350_gpio_set(chip, offset, value); - - return 0; -} - -static int wm8350_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip); - struct wm8350 *wm8350 = wm8350_gpio->wm8350; - - if (!wm8350->irq_base) - return -EINVAL; - - return wm8350->irq_base + WM8350_IRQ_GPIO(offset); -} - -static struct gpio_chip template_chip = { - .label = "wm8350", - .owner = THIS_MODULE, - .direction_input = wm8350_gpio_direction_in, - .get = wm8350_gpio_get, - .direction_output = wm8350_gpio_direction_out, - .set = wm8350_gpio_set, - .to_irq = wm8350_gpio_to_irq, - .can_sleep = 1, -}; - -static int __devinit wm8350_gpio_probe(struct platform_device *pdev) -{ - struct wm8350 *wm8350 = dev_get_drvdata(pdev->dev.parent); - struct wm8350_platform_data *pdata = wm8350->dev->platform_data; - struct wm8350_gpio_data *wm8350_gpio; - int ret; - - wm8350_gpio = kzalloc(sizeof(*wm8350_gpio), GFP_KERNEL); - if (wm8350_gpio == NULL) - return -ENOMEM; - - wm8350_gpio->wm8350 = wm8350; - wm8350_gpio->gpio_chip = template_chip; - wm8350_gpio->gpio_chip.ngpio = 13; - wm8350_gpio->gpio_chip.dev = &pdev->dev; - if (pdata && pdata->gpio_base) - wm8350_gpio->gpio_chip.base = pdata->gpio_base; - else - wm8350_gpio->gpio_chip.base = -1; - - ret = gpiochip_add(&wm8350_gpio->gpio_chip); - if (ret < 0) { - dev_err(&pdev->dev, "Could not register gpiochip, %d\n", - ret); - goto err; - } - - platform_set_drvdata(pdev, wm8350_gpio); - - return ret; - -err: - kfree(wm8350_gpio); - return ret; -} - -static int __devexit wm8350_gpio_remove(struct platform_device *pdev) -{ - struct wm8350_gpio_data *wm8350_gpio = platform_get_drvdata(pdev); - int ret; - - ret = gpiochip_remove(&wm8350_gpio->gpio_chip); - if (ret == 0) - kfree(wm8350_gpio); - - return ret; -} - -static struct platform_driver wm8350_gpio_driver = { - .driver.name = "wm8350-gpio", - .driver.owner = THIS_MODULE, - .probe = wm8350_gpio_probe, - .remove = __devexit_p(wm8350_gpio_remove), -}; - -static int __init wm8350_gpio_init(void) -{ - return platform_driver_register(&wm8350_gpio_driver); -} -subsys_initcall(wm8350_gpio_init); - -static void __exit wm8350_gpio_exit(void) -{ - platform_driver_unregister(&wm8350_gpio_driver); -} -module_exit(wm8350_gpio_exit); - -MODULE_AUTHOR("Mark Brown "); -MODULE_DESCRIPTION("GPIO interface for WM8350 PMICs"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:wm8350-gpio"); diff --git a/drivers/gpio/wm8994-gpio.c b/drivers/gpio/wm8994-gpio.c deleted file mode 100644 index c822baacd8f..00000000000 --- a/drivers/gpio/wm8994-gpio.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * wm8994-gpio.c -- gpiolib support for Wolfson WM8994 - * - * Copyright 2009 Wolfson Microelectronics PLC. - * - * Author: Mark Brown - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -struct wm8994_gpio { - struct wm8994 *wm8994; - struct gpio_chip gpio_chip; -}; - -static inline struct wm8994_gpio *to_wm8994_gpio(struct gpio_chip *chip) -{ - return container_of(chip, struct wm8994_gpio, gpio_chip); -} - -static int wm8994_gpio_request(struct gpio_chip *chip, unsigned offset) -{ - struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip); - struct wm8994 *wm8994 = wm8994_gpio->wm8994; - - switch (wm8994->type) { - case WM8958: - switch (offset) { - case 1: - case 2: - case 3: - case 4: - case 6: - return -EINVAL; - } - break; - default: - break; - } - - return 0; -} - -static int wm8994_gpio_direction_in(struct gpio_chip *chip, unsigned offset) -{ - struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip); - struct wm8994 *wm8994 = wm8994_gpio->wm8994; - - return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, - WM8994_GPN_DIR, WM8994_GPN_DIR); -} - -static int wm8994_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip); - struct wm8994 *wm8994 = wm8994_gpio->wm8994; - int ret; - - ret = wm8994_reg_read(wm8994, WM8994_GPIO_1 + offset); - if (ret < 0) - return ret; - - if (ret & WM8994_GPN_LVL) - return 1; - else - return 0; -} - -static int wm8994_gpio_direction_out(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip); - struct wm8994 *wm8994 = wm8994_gpio->wm8994; - - return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, - WM8994_GPN_DIR, 0); -} - -static void wm8994_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip); - struct wm8994 *wm8994 = wm8994_gpio->wm8994; - - if (value) - value = WM8994_GPN_LVL; - - wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, WM8994_GPN_LVL, value); -} - -static int wm8994_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip); - struct wm8994 *wm8994 = wm8994_gpio->wm8994; - - if (!wm8994->irq_base) - return -EINVAL; - - return wm8994->irq_base + offset; -} - - -#ifdef CONFIG_DEBUG_FS -static void wm8994_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) -{ - struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip); - struct wm8994 *wm8994 = wm8994_gpio->wm8994; - int i; - - for (i = 0; i < chip->ngpio; i++) { - int gpio = i + chip->base; - int reg; - const char *label; - - /* We report the GPIO even if it's not requested since - * we're also reporting things like alternate - * functions which apply even when the GPIO is not in - * use as a GPIO. - */ - label = gpiochip_is_requested(chip, i); - if (!label) - label = "Unrequested"; - - seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label); - - reg = wm8994_reg_read(wm8994, WM8994_GPIO_1 + i); - if (reg < 0) { - dev_err(wm8994->dev, - "GPIO control %d read failed: %d\n", - gpio, reg); - seq_printf(s, "\n"); - continue; - } - - /* No decode yet; note that GPIO2 is special */ - seq_printf(s, "(%x)\n", reg); - } -} -#else -#define wm8994_gpio_dbg_show NULL -#endif - -static struct gpio_chip template_chip = { - .label = "wm8994", - .owner = THIS_MODULE, - .request = wm8994_gpio_request, - .direction_input = wm8994_gpio_direction_in, - .get = wm8994_gpio_get, - .direction_output = wm8994_gpio_direction_out, - .set = wm8994_gpio_set, - .to_irq = wm8994_gpio_to_irq, - .dbg_show = wm8994_gpio_dbg_show, - .can_sleep = 1, -}; - -static int __devinit wm8994_gpio_probe(struct platform_device *pdev) -{ - struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent); - struct wm8994_pdata *pdata = wm8994->dev->platform_data; - struct wm8994_gpio *wm8994_gpio; - int ret; - - wm8994_gpio = kzalloc(sizeof(*wm8994_gpio), GFP_KERNEL); - if (wm8994_gpio == NULL) - return -ENOMEM; - - wm8994_gpio->wm8994 = wm8994; - wm8994_gpio->gpio_chip = template_chip; - wm8994_gpio->gpio_chip.ngpio = WM8994_GPIO_MAX; - wm8994_gpio->gpio_chip.dev = &pdev->dev; - if (pdata && pdata->gpio_base) - wm8994_gpio->gpio_chip.base = pdata->gpio_base; - else - wm8994_gpio->gpio_chip.base = -1; - - ret = gpiochip_add(&wm8994_gpio->gpio_chip); - if (ret < 0) { - dev_err(&pdev->dev, "Could not register gpiochip, %d\n", - ret); - goto err; - } - - platform_set_drvdata(pdev, wm8994_gpio); - - return ret; - -err: - kfree(wm8994_gpio); - return ret; -} - -static int __devexit wm8994_gpio_remove(struct platform_device *pdev) -{ - struct wm8994_gpio *wm8994_gpio = platform_get_drvdata(pdev); - int ret; - - ret = gpiochip_remove(&wm8994_gpio->gpio_chip); - if (ret == 0) - kfree(wm8994_gpio); - - return ret; -} - -static struct platform_driver wm8994_gpio_driver = { - .driver.name = "wm8994-gpio", - .driver.owner = THIS_MODULE, - .probe = wm8994_gpio_probe, - .remove = __devexit_p(wm8994_gpio_remove), -}; - -static int __init wm8994_gpio_init(void) -{ - return platform_driver_register(&wm8994_gpio_driver); -} -subsys_initcall(wm8994_gpio_init); - -static void __exit wm8994_gpio_exit(void) -{ - platform_driver_unregister(&wm8994_gpio_driver); -} -module_exit(wm8994_gpio_exit); - -MODULE_AUTHOR("Mark Brown "); -MODULE_DESCRIPTION("GPIO interface for WM8994"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:wm8994-gpio"); diff --git a/drivers/gpio/xilinx_gpio.c b/drivers/gpio/xilinx_gpio.c deleted file mode 100644 index 846fbd5e31b..00000000000 --- a/drivers/gpio/xilinx_gpio.c +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Xilinx gpio driver - * - * Copyright 2008 Xilinx, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* Register Offset Definitions */ -#define XGPIO_DATA_OFFSET (0x0) /* Data register */ -#define XGPIO_TRI_OFFSET (0x4) /* I/O direction register */ - -struct xgpio_instance { - struct of_mm_gpio_chip mmchip; - u32 gpio_state; /* GPIO state shadow register */ - u32 gpio_dir; /* GPIO direction shadow register */ - spinlock_t gpio_lock; /* Lock used for synchronization */ -}; - -/** - * xgpio_get - Read the specified signal of the GPIO device. - * @gc: Pointer to gpio_chip device structure. - * @gpio: GPIO signal number. - * - * This function reads the specified signal of the GPIO device. It returns 0 if - * the signal clear, 1 if signal is set or negative value on error. - */ -static int xgpio_get(struct gpio_chip *gc, unsigned int gpio) -{ - struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); - - return (in_be32(mm_gc->regs + XGPIO_DATA_OFFSET) >> gpio) & 1; -} - -/** - * xgpio_set - Write the specified signal of the GPIO device. - * @gc: Pointer to gpio_chip device structure. - * @gpio: GPIO signal number. - * @val: Value to be written to specified signal. - * - * This function writes the specified value in to the specified signal of the - * GPIO device. - */ -static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) -{ - unsigned long flags; - struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); - struct xgpio_instance *chip = - container_of(mm_gc, struct xgpio_instance, mmchip); - - spin_lock_irqsave(&chip->gpio_lock, flags); - - /* Write to GPIO signal and set its direction to output */ - if (val) - chip->gpio_state |= 1 << gpio; - else - chip->gpio_state &= ~(1 << gpio); - out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); - - spin_unlock_irqrestore(&chip->gpio_lock, flags); -} - -/** - * xgpio_dir_in - Set the direction of the specified GPIO signal as input. - * @gc: Pointer to gpio_chip device structure. - * @gpio: GPIO signal number. - * - * This function sets the direction of specified GPIO signal as input. - * It returns 0 if direction of GPIO signals is set as input otherwise it - * returns negative error value. - */ -static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) -{ - unsigned long flags; - struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); - struct xgpio_instance *chip = - container_of(mm_gc, struct xgpio_instance, mmchip); - - spin_lock_irqsave(&chip->gpio_lock, flags); - - /* Set the GPIO bit in shadow register and set direction as input */ - chip->gpio_dir |= (1 << gpio); - out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); - - spin_unlock_irqrestore(&chip->gpio_lock, flags); - - return 0; -} - -/** - * xgpio_dir_out - Set the direction of the specified GPIO signal as output. - * @gc: Pointer to gpio_chip device structure. - * @gpio: GPIO signal number. - * @val: Value to be written to specified signal. - * - * This function sets the direction of specified GPIO signal as output. If all - * GPIO signals of GPIO chip is configured as input then it returns - * error otherwise it returns 0. - */ -static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) -{ - unsigned long flags; - struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); - struct xgpio_instance *chip = - container_of(mm_gc, struct xgpio_instance, mmchip); - - spin_lock_irqsave(&chip->gpio_lock, flags); - - /* Write state of GPIO signal */ - if (val) - chip->gpio_state |= 1 << gpio; - else - chip->gpio_state &= ~(1 << gpio); - out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); - - /* Clear the GPIO bit in shadow register and set direction as output */ - chip->gpio_dir &= (~(1 << gpio)); - out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); - - spin_unlock_irqrestore(&chip->gpio_lock, flags); - - return 0; -} - -/** - * xgpio_save_regs - Set initial values of GPIO pins - * @mm_gc: pointer to memory mapped GPIO chip structure - */ -static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc) -{ - struct xgpio_instance *chip = - container_of(mm_gc, struct xgpio_instance, mmchip); - - out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); - out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); -} - -/** - * xgpio_of_probe - Probe method for the GPIO device. - * @np: pointer to device tree node - * - * This function probes the GPIO device in the device tree. It initializes the - * driver data structure. It returns 0, if the driver is bound to the GPIO - * device, or a negative value if there is an error. - */ -static int __devinit xgpio_of_probe(struct device_node *np) -{ - struct xgpio_instance *chip; - int status = 0; - const u32 *tree_info; - - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - - /* Update GPIO state shadow register with default value */ - tree_info = of_get_property(np, "xlnx,dout-default", NULL); - if (tree_info) - chip->gpio_state = be32_to_cpup(tree_info); - - /* Update GPIO direction shadow register with default value */ - chip->gpio_dir = 0xFFFFFFFF; /* By default, all pins are inputs */ - tree_info = of_get_property(np, "xlnx,tri-default", NULL); - if (tree_info) - chip->gpio_dir = be32_to_cpup(tree_info); - - /* Check device node and parent device node for device width */ - chip->mmchip.gc.ngpio = 32; /* By default assume full GPIO controller */ - tree_info = of_get_property(np, "xlnx,gpio-width", NULL); - if (!tree_info) - tree_info = of_get_property(np->parent, - "xlnx,gpio-width", NULL); - if (tree_info) - chip->mmchip.gc.ngpio = be32_to_cpup(tree_info); - - spin_lock_init(&chip->gpio_lock); - - chip->mmchip.gc.direction_input = xgpio_dir_in; - chip->mmchip.gc.direction_output = xgpio_dir_out; - chip->mmchip.gc.get = xgpio_get; - chip->mmchip.gc.set = xgpio_set; - - chip->mmchip.save_regs = xgpio_save_regs; - - /* Call the OF gpio helper to setup and register the GPIO device */ - status = of_mm_gpiochip_add(np, &chip->mmchip); - if (status) { - kfree(chip); - pr_err("%s: error in probe function with status %d\n", - np->full_name, status); - return status; - } - pr_info("XGpio: %s: registered\n", np->full_name); - return 0; -} - -static struct of_device_id xgpio_of_match[] __devinitdata = { - { .compatible = "xlnx,xps-gpio-1.00.a", }, - { /* end of list */ }, -}; - -static int __init xgpio_init(void) -{ - struct device_node *np; - - for_each_matching_node(np, xgpio_of_match) - xgpio_of_probe(np); - - return 0; -} - -/* Make sure we get initialized before anyone else tries to use us */ -subsys_initcall(xgpio_init); -/* No exit call at the moment as we cannot unregister of GPIO chips */ - -MODULE_AUTHOR("Xilinx, Inc."); -MODULE_DESCRIPTION("Xilinx GPIO driver"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From e4ea933363d39b5858f55682550314b4ea1285cc Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 7 Jun 2011 16:25:37 +0800 Subject: gpio/mxc: convert gpio-mxc to use generic irq chip The patch converts gpio-mxc driver to use generic irq chip. Signed-off-by: Shawn Guo Acked-by: Sascha Hauer Signed-off-by: Grant Likely --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-mxc.c | 90 ++++++++++++++++--------------------------------- 2 files changed, 30 insertions(+), 61 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 89a6a34cc51..4e2d612590f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -96,6 +96,7 @@ config GPIO_MXC def_bool y depends on ARCH_MXC select GPIO_GENERIC + select GENERIC_IRQ_CHIP config GPIO_MXS def_bool y diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index b351952893b..950e53a636f 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -65,46 +65,11 @@ static LIST_HEAD(mxc_gpio_ports); /* Note: This driver assumes 32 GPIOs are handled in one register */ -static void _clear_gpio_irqstatus(struct mxc_gpio_port *port, u32 index) -{ - writel(1 << index, port->base + GPIO_ISR); -} - -static void _set_gpio_irqenable(struct mxc_gpio_port *port, u32 index, - int enable) -{ - u32 l; - - l = readl(port->base + GPIO_IMR); - l = (l & (~(1 << index))) | (!!enable << index); - writel(l, port->base + GPIO_IMR); -} - -static void gpio_ack_irq(struct irq_data *d) -{ - struct mxc_gpio_port *port = irq_data_get_irq_chip_data(d); - u32 gpio = irq_to_gpio(d->irq); - _clear_gpio_irqstatus(port, gpio & 0x1f); -} - -static void gpio_mask_irq(struct irq_data *d) -{ - struct mxc_gpio_port *port = irq_data_get_irq_chip_data(d); - u32 gpio = irq_to_gpio(d->irq); - _set_gpio_irqenable(port, gpio & 0x1f, 0); -} - -static void gpio_unmask_irq(struct irq_data *d) -{ - struct mxc_gpio_port *port = irq_data_get_irq_chip_data(d); - u32 gpio = irq_to_gpio(d->irq); - _set_gpio_irqenable(port, gpio & 0x1f, 1); -} - static int gpio_set_irq_type(struct irq_data *d, u32 type) { u32 gpio = irq_to_gpio(d->irq); - struct mxc_gpio_port *port = irq_data_get_irq_chip_data(d); + 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; @@ -142,7 +107,7 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type) bit = gpio & 0xf; val = readl(reg) & ~(0x3 << (bit << 1)); writel(val | (edge << (bit << 1)), reg); - _clear_gpio_irqstatus(port, gpio & 0x1f); + writel(1 << (gpio & 0x1f), port->base + GPIO_ISR); return 0; } @@ -231,7 +196,8 @@ 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 mxc_gpio_port *port = irq_data_get_irq_chip_data(d); + 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)) @@ -248,26 +214,33 @@ static int gpio_set_wake_irq(struct irq_data *d, u32 enable) return 0; } -static struct irq_chip gpio_irq_chip = { - .name = "GPIO", - .irq_ack = gpio_ack_irq, - .irq_mask = gpio_mask_irq, - .irq_unmask = gpio_unmask_irq, - .irq_set_type = gpio_set_irq_type, - .irq_set_wake = gpio_set_wake_irq, -}; - -/* - * 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 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, i; + int err; port = kzalloc(sizeof(struct mxc_gpio_port), GFP_KERNEL); if (!port) @@ -304,13 +277,8 @@ static int __devinit mxc_gpio_probe(struct platform_device *pdev) writel(0, port->base + GPIO_IMR); writel(~0, port->base + GPIO_ISR); - for (i = port->virtual_irq_start; - i < port->virtual_irq_start + 32; i++) { - irq_set_lockdep_class(i, &gpio_lock_class); - irq_set_chip_and_handler(i, &gpio_irq_chip, handle_level_irq); - set_irq_flags(i, IRQF_VALID); - irq_set_chip_data(i, port); - } + /* gpio-mxc can be a generic irq chip */ + mxc_gpio_init_gc(port); if (cpu_is_mx2()) { /* setup one handler for all GPIO interrupts */ -- cgit v1.2.3 From 06f88a8ae9085e555baf81edbf967764d87dc12f Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 6 Jun 2011 22:31:29 +0800 Subject: gpio/mxs: convert gpio-mxs to use basic_mmio_gpio library The gpio-mxs controller complies with basic_mmio_gpio library. This patch converts the driver to use the library. Signed-off-by: Shawn Guo Signed-off-by: Grant Likely --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-mxs.c | 80 +++++++++++-------------------------------------- 2 files changed, 19 insertions(+), 62 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4e2d612590f..531bfb6c874 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -101,6 +101,7 @@ config GPIO_MXC config GPIO_MXS def_bool y depends on ARCH_MXS + select GPIO_GENERIC config GPIO_PLAT_SAMSUNG def_bool y diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index a28761428bb..9d2dba7199c 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #define MXS_SET 0x4 @@ -54,7 +55,7 @@ struct mxs_gpio_port { int irq; int irq_high; int virtual_irq_start; - struct gpio_chip chip; + struct bgpio_chip bgc; }; /* Note: This driver assumes 32 GPIOs are handled in one register */ @@ -99,8 +100,6 @@ static void mxs_gpio_unmask_irq(struct irq_data *d) set_gpio_irqenable(port, gpio & 0x1f, 1); } -static int mxs_gpio_get(struct gpio_chip *chip, unsigned offset); - static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type) { u32 gpio = irq_to_gpio(d->irq); @@ -203,61 +202,15 @@ static struct irq_chip gpio_irq_chip = { .irq_set_wake = mxs_gpio_set_wake_irq, }; -static void mxs_set_gpio_direction(struct gpio_chip *chip, unsigned offset, - int dir) -{ - struct mxs_gpio_port *port = - container_of(chip, struct mxs_gpio_port, chip); - void __iomem *pin_addr = port->base + PINCTRL_DOE(port->id); - - if (dir) - writel(1 << offset, pin_addr + MXS_SET); - else - writel(1 << offset, pin_addr + MXS_CLR); -} - -static int mxs_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct mxs_gpio_port *port = - container_of(chip, struct mxs_gpio_port, chip); - - return (readl(port->base + PINCTRL_DIN(port->id)) >> offset) & 1; -} - -static void mxs_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct mxs_gpio_port *port = - container_of(chip, struct mxs_gpio_port, chip); - void __iomem *pin_addr = port->base + PINCTRL_DOUT(port->id); - - if (value) - writel(1 << offset, pin_addr + MXS_SET); - else - writel(1 << offset, pin_addr + MXS_CLR); -} - -static int mxs_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +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(chip, struct mxs_gpio_port, chip); + container_of(bgc, struct mxs_gpio_port, bgc); return port->virtual_irq_start + offset; } -static int mxs_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - mxs_set_gpio_direction(chip, offset, 0); - return 0; -} - -static int mxs_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - mxs_gpio_set(chip, offset, value); - mxs_set_gpio_direction(chip, offset, 1); - return 0; -} - static int __devinit mxs_gpio_probe(struct platform_device *pdev) { static void __iomem *base; @@ -322,21 +275,24 @@ static int __devinit mxs_gpio_probe(struct platform_device *pdev) irq_set_chained_handler(port->irq, mxs_gpio_irq_handler); irq_set_handler_data(port->irq, port); - /* register gpio chip */ - port->chip.direction_input = mxs_gpio_direction_input; - port->chip.direction_output = mxs_gpio_direction_output; - port->chip.get = mxs_gpio_get; - port->chip.set = mxs_gpio_set; - port->chip.to_irq = mxs_gpio_to_irq; - port->chip.base = port->id * 32; - port->chip.ngpio = 32; - - err = gpiochip_add(&port->chip); + 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); -- cgit v1.2.3 From 6161715e3f1adecae9e7c160930add4040fa10e9 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 7 Jun 2011 22:00:53 +0800 Subject: gpio/mxs: remove irq_high related implementation The irq_high stuff was mistakenly copied from mxc gpio driver. The mxs gpio controller has only one irq line for each 32-pin gpio port. The patch remove irq_high related codes. Signed-off-by: Shawn Guo Signed-off-by: Grant Likely --- drivers/gpio/gpio-mxs.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index 9d2dba7199c..ddde0f44ba7 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -53,7 +53,6 @@ struct mxs_gpio_port { void __iomem *base; int id; int irq; - int irq_high; int virtual_irq_start; struct bgpio_chip bgc; }; @@ -174,21 +173,12 @@ static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc) */ static int mxs_gpio_set_wake_irq(struct irq_data *d, unsigned int enable) { - u32 gpio = irq_to_gpio(d->irq); - u32 gpio_idx = gpio & 0x1f; struct mxs_gpio_port *port = irq_data_get_irq_chip_data(d); - 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); - } + if (enable) + enable_irq_wake(port->irq); + else + disable_irq_wake(port->irq); return 0; } -- cgit v1.2.3 From 498c17cf6a935335c8652165cae5dc196340b3e9 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 7 Jun 2011 22:00:54 +0800 Subject: gpio/mxs: convert gpio-mxs to use generic irq chip The patch converts gpio-mxs driver to use generic irq chip. Signed-off-by: Shawn Guo Signed-off-by: Grant Likely --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-mxs.c | 95 ++++++++++++++++++------------------------------- 2 files changed, 35 insertions(+), 61 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 531bfb6c874..b84666a69af 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -102,6 +102,7 @@ config GPIO_MXS def_bool y depends on ARCH_MXS select GPIO_GENERIC + select GENERIC_IRQ_CHIP config GPIO_PLAT_SAMSUNG def_bool y diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index ddde0f44ba7..d8cafba8c82 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -59,51 +59,12 @@ struct mxs_gpio_port { /* Note: This driver assumes 32 GPIOs are handled in one register */ -static void clear_gpio_irqstatus(struct mxs_gpio_port *port, u32 index) -{ - writel(1 << index, port->base + PINCTRL_IRQSTAT(port->id) + MXS_CLR); -} - -static void set_gpio_irqenable(struct mxs_gpio_port *port, u32 index, - int enable) -{ - if (enable) { - writel(1 << index, - port->base + PINCTRL_IRQEN(port->id) + MXS_SET); - writel(1 << index, - port->base + PINCTRL_PIN2IRQ(port->id) + MXS_SET); - } else { - writel(1 << index, - port->base + PINCTRL_IRQEN(port->id) + MXS_CLR); - } -} - -static void mxs_gpio_ack_irq(struct irq_data *d) -{ - struct mxs_gpio_port *port = irq_data_get_irq_chip_data(d); - u32 gpio = irq_to_gpio(d->irq); - clear_gpio_irqstatus(port, gpio & 0x1f); -} - -static void mxs_gpio_mask_irq(struct irq_data *d) -{ - struct mxs_gpio_port *port = irq_data_get_irq_chip_data(d); - u32 gpio = irq_to_gpio(d->irq); - set_gpio_irqenable(port, gpio & 0x1f, 0); -} - -static void mxs_gpio_unmask_irq(struct irq_data *d) -{ - struct mxs_gpio_port *port = irq_data_get_irq_chip_data(d); - u32 gpio = irq_to_gpio(d->irq); - set_gpio_irqenable(port, gpio & 0x1f, 1); -} - 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 mxs_gpio_port *port = irq_data_get_irq_chip_data(d); + 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; @@ -138,7 +99,8 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type) else writel(pin_mask, pin_addr + MXS_CLR); - clear_gpio_irqstatus(port, gpio & 0x1f); + writel(1 << (gpio & 0x1f), + port->base + PINCTRL_IRQSTAT(port->id) + MXS_CLR); return 0; } @@ -173,7 +135,8 @@ static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc) */ static int mxs_gpio_set_wake_irq(struct irq_data *d, unsigned int enable) { - struct mxs_gpio_port *port = irq_data_get_irq_chip_data(d); + 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); @@ -183,14 +146,26 @@ static int mxs_gpio_set_wake_irq(struct irq_data *d, unsigned int enable) return 0; } -static struct irq_chip gpio_irq_chip = { - .name = "mxs gpio", - .irq_ack = mxs_gpio_ack_irq, - .irq_mask = mxs_gpio_mask_irq, - .irq_unmask = mxs_gpio_unmask_irq, - .irq_set_type = mxs_gpio_set_irq_type, - .irq_set_wake = mxs_gpio_set_wake_irq, -}; +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) { @@ -206,7 +181,7 @@ 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, i; + int err; port = kzalloc(sizeof(struct mxs_gpio_port), GFP_KERNEL); if (!port) @@ -246,20 +221,18 @@ static int __devinit mxs_gpio_probe(struct platform_device *pdev) goto out_iounmap; } - /* disable the interrupt and clear the status */ - writel(0, port->base + PINCTRL_PIN2IRQ(port->id)); + /* + * 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); - for (i = port->virtual_irq_start; - i < port->virtual_irq_start + 32; i++) { - irq_set_chip_and_handler(i, &gpio_irq_chip, - handle_level_irq); - set_irq_flags(i, IRQF_VALID); - irq_set_chip_data(i, port); - } + /* 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); -- cgit v1.2.3 From 47732cb4fdee72e6ea4d2ae40d72da4d2cedf97e Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Tue, 7 Jun 2011 13:52:01 -0700 Subject: gpio/ep93xx: fix pr_fmt output With this driver now being moved from arch/arm/mach-ep93xx/gpio.c to drivers/gpio/gpio-ep93xx.c the prefix "ep93xx" is now redundant in the pr_* output. Using KBUILD_MODNAME is sufficient. Signed-off-by: H Hartley Sweeten Signed-off-by: Grant Likely --- drivers/gpio/gpio-ep93xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index c33c9a80e14..3d427b905c0 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -11,7 +11,7 @@ * published by the Free Software Foundation. */ -#define pr_fmt(fmt) "ep93xx " KBUILD_MODNAME ": " fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include -- cgit v1.2.3 From 910c8fb6b4b8c9e0be576b6a78f4cccf228a4bfd Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 8 Jun 2011 11:34:37 -0600 Subject: gpio/pca953x: Fix IRQ support. It seems that in the normal case, IRQ_NOREQUEST needs to be explicitly cleared, otherwise claiming the interrupt fails. In the case of sparse interrupts, the descriptor needs to be allocated first. Signed-off-by: David Jander Signed-off-by: Grant Likely --- drivers/gpio/gpio-pca953x.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index a610864b7e1..ce5736e8362 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -474,12 +474,16 @@ 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); -- cgit v1.2.3 From 17e8b42c17c3e9735ea03c4e6b67cedf5de170f4 Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 8 Jun 2011 11:34:41 -0600 Subject: gpio/pca953x: Interrupt pin is active-low The interrupt pin of the PCA953x is active low, and on the rising edge no interrupt should be produced. Signed-off-by: David Jander Signed-off-by: Grant Likely --- drivers/gpio/gpio-pca953x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index ce5736e8362..17e55701394 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -497,7 +497,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, ret = request_threaded_irq(client->irq, NULL, pca953x_irq_handler, - 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", -- cgit v1.2.3 From 6dd599f8af0166805951f4421a78ba716d78321a Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 8 Jun 2011 11:37:45 -0600 Subject: gpio/pca953x: Fix warning of enabled interrupts in handler When using nested threaded irqs, use handle_nested_irq(). This function does not call the chip handler, so no handler is set. Signed-off-by: David Jander Signed-off-by: Grant Likely --- drivers/gpio/gpio-pca953x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 17e55701394..4b8446e98de 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -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); @@ -485,8 +485,8 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, 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 -- cgit v1.2.3 From 1e4c88420e0da3c855c17770a807cabd5b16dd7a Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 8 Jun 2011 14:35:33 -0700 Subject: gpio/ep93xx: convert to platform_driver and use basic_mmio_gpio library This converts the gpio-ep93xx driver into a platform_driver and uses the basic_mmio_gpio library. Signed-off-by: H Hartley Sweeten Signed-off-by: Grant Likely --- arch/arm/mach-ep93xx/core.c | 27 +++-- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-ep93xx.c | 240 ++++++++++++++++++++++---------------------- 3 files changed, 140 insertions(+), 128 deletions(-) diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index 82079545adc..a6b3fb20877 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -174,14 +174,10 @@ struct sys_timer ep93xx_timer = { /************************************************************************* * EP93xx IRQ handling *************************************************************************/ -extern void ep93xx_gpio_init_irq(void); - void __init ep93xx_init_irq(void) { vic_init(EP93XX_VIC1_BASE, 0, EP93XX_VIC1_VALID_IRQ_MASK, 0); vic_init(EP93XX_VIC2_BASE, 32, EP93XX_VIC2_VALID_IRQ_MASK, 0); - - ep93xx_gpio_init_irq(); } @@ -240,6 +236,24 @@ unsigned int ep93xx_chip_revision(void) return v; } +/************************************************************************* + * EP93xx GPIO + *************************************************************************/ +static struct resource ep93xx_gpio_resource[] = { + { + .start = EP93XX_GPIO_PHYS_BASE, + .end = EP93XX_GPIO_PHYS_BASE + 0xcc - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device ep93xx_gpio_device = { + .name = "gpio-ep93xx", + .id = -1, + .num_resources = ARRAY_SIZE(ep93xx_gpio_resource), + .resource = ep93xx_gpio_resource, +}; + /************************************************************************* * EP93xx peripheral handling *************************************************************************/ @@ -866,14 +880,13 @@ void __init ep93xx_register_ac97(void) platform_device_register(&ep93xx_pcm_device); } -extern void ep93xx_gpio_init(void); - void __init ep93xx_init_devices(void) { /* Disallow access to MaverickCrunch initially */ ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_CPENA); - ep93xx_gpio_init(); + /* Get the GPIO working early, other devices need it */ + platform_device_register(&ep93xx_gpio_device); amba_device_register(&uart1_device, &iomem_resource); amba_device_register(&uart2_device, &iomem_resource); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b84666a69af..9f06e63fbc8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -87,6 +87,7 @@ config GPIO_IT8761E config GPIO_EP93XX def_bool y depends on ARCH_EP93XX + select GPIO_GENERIC config GPIO_EXYNOS4 def_bool y diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 3d427b905c0..dc3870d4a4e 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -2,6 +2,7 @@ * Generic EP93xx GPIO handling * * Copyright (c) 2008 Ryan Mallon + * Copyright (c) 2011 H Hartley Sweeten * * Based on code originally from: * linux/arch/arm/mach-ep93xx/core.c @@ -14,14 +15,20 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include -#include -#include +#include #include #include #include +#include +#include #include +struct ep93xx_gpio { + void __iomem *mmio_base; + struct bgpio_chip bgc[8]; +}; + /************************************************************************* * Interrupt handling for EP93xx on-chip GPIOs *************************************************************************/ @@ -223,7 +230,7 @@ static struct irq_chip ep93xx_gpio_irq_chip = { .irq_set_type = ep93xx_gpio_irq_type, }; -void __init ep93xx_gpio_init_irq(void) +static void ep93xx_gpio_init_irq(void) { int gpio_irq; @@ -258,151 +265,142 @@ void __init ep93xx_gpio_init_irq(void) /************************************************************************* * gpiolib interface for EP93xx on-chip GPIOs *************************************************************************/ -struct ep93xx_gpio_chip { - struct gpio_chip chip; - - void __iomem *data_reg; - void __iomem *data_dir_reg; +struct ep93xx_gpio_bank { + const char *label; + int data; + int dir; + int base; + bool has_debounce; }; -#define to_ep93xx_gpio_chip(c) container_of(c, struct ep93xx_gpio_chip, chip) +#define EP93XX_GPIO_BANK(_label, _data, _dir, _base, _debounce) \ + { \ + .label = _label, \ + .data = _data, \ + .dir = _dir, \ + .base = _base, \ + .has_debounce = _debounce, \ + } -static int ep93xx_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +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) { - struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip); - unsigned long flags; - u8 v; + int gpio = chip->base + offset; + int irq = gpio_to_irq(gpio); - local_irq_save(flags); - v = __raw_readb(ep93xx_chip->data_dir_reg); - v &= ~(1 << offset); - __raw_writeb(v, ep93xx_chip->data_dir_reg); - local_irq_restore(flags); + if (irq < 0) + return -EINVAL; + + ep93xx_gpio_int_debounce(irq, debounce ? true : false); return 0; } -static int ep93xx_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int val) +static int ep93xx_gpio_add_bank(struct bgpio_chip *bgc, struct device *dev, + void __iomem *mmio_base, struct ep93xx_gpio_bank *bank) { - struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip); - unsigned long flags; - int line; - u8 v; - - local_irq_save(flags); + void __iomem *data = mmio_base + bank->data; + void __iomem *dir = mmio_base + bank->dir; + int err; - /* Set the value */ - v = __raw_readb(ep93xx_chip->data_reg); - if (val) - v |= (1 << offset); - else - v &= ~(1 << offset); - __raw_writeb(v, ep93xx_chip->data_reg); - - /* Drive as an output */ - line = chip->base + offset; - if (line <= EP93XX_GPIO_LINE_MAX_IRQ) { - /* Ports A/B/F */ - ep93xx_gpio_int_mask(line); - ep93xx_gpio_update_int_params(line >> 3); - } + err = bgpio_init(bgc, dev, 1, data, NULL, NULL, dir, NULL, false); + if (err) + return err; - v = __raw_readb(ep93xx_chip->data_dir_reg); - v |= (1 << offset); - __raw_writeb(v, ep93xx_chip->data_dir_reg); + bgc->gc.label = bank->label; + bgc->gc.base = bank->base; - local_irq_restore(flags); + if (bank->has_debounce) + bgc->gc.set_debounce = ep93xx_gpio_set_debounce; - return 0; + return gpiochip_add(&bgc->gc); } -static int ep93xx_gpio_get(struct gpio_chip *chip, unsigned offset) +static int __devinit ep93xx_gpio_probe(struct platform_device *pdev) { - struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip); + struct ep93xx_gpio *ep93xx_gpio; + struct resource *res; + void __iomem *mmio; + int i; + int ret; - return !!(__raw_readb(ep93xx_chip->data_reg) & (1 << offset)); -} + ep93xx_gpio = kzalloc(sizeof(*ep93xx_gpio), GFP_KERNEL); + if (!ep93xx_gpio) + return -ENOMEM; -static void ep93xx_gpio_set(struct gpio_chip *chip, unsigned offset, int val) -{ - struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip); - unsigned long flags; - u8 v; - - local_irq_save(flags); - v = __raw_readb(ep93xx_chip->data_reg); - if (val) - v |= (1 << offset); - else - v &= ~(1 << offset); - __raw_writeb(v, ep93xx_chip->data_reg); - local_irq_restore(flags); -} + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENXIO; + goto exit_free; + } -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 (!request_mem_region(res->start, resource_size(res), pdev->name)) { + ret = -EBUSY; + goto exit_free; + } - if (irq < 0) - return -EINVAL; + mmio = ioremap(res->start, resource_size(res)); + if (!mmio) { + ret = -ENXIO; + goto exit_release; + } + ep93xx_gpio->mmio_base = mmio; - ep93xx_gpio_int_debounce(irq, debounce ? true : false); + /* 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); - return 0; -} + 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]; -#define EP93XX_GPIO_BANK(name, dr, ddr, base_gpio) \ - { \ - .chip = { \ - .label = name, \ - .direction_input = ep93xx_gpio_direction_input, \ - .direction_output = ep93xx_gpio_direction_output, \ - .get = ep93xx_gpio_get, \ - .set = ep93xx_gpio_set, \ - .base = base_gpio, \ - .ngpio = 8, \ - }, \ - .data_reg = EP93XX_GPIO_REG(dr), \ - .data_dir_reg = EP93XX_GPIO_REG(ddr), \ + if (ep93xx_gpio_add_bank(bgc, &pdev->dev, mmio, bank)) + dev_warn(&pdev->dev, "Unable to add gpio bank %s\n", + bank->label); } -static struct ep93xx_gpio_chip ep93xx_gpio_banks[] = { - EP93XX_GPIO_BANK("A", 0x00, 0x10, 0), - EP93XX_GPIO_BANK("B", 0x04, 0x14, 8), - EP93XX_GPIO_BANK("C", 0x08, 0x18, 40), - EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 24), - EP93XX_GPIO_BANK("E", 0x20, 0x24, 32), - EP93XX_GPIO_BANK("F", 0x30, 0x34, 16), - EP93XX_GPIO_BANK("G", 0x38, 0x3c, 48), - EP93XX_GPIO_BANK("H", 0x40, 0x44, 56), -}; + ep93xx_gpio_init_irq(); -void __init ep93xx_gpio_init(void) -{ - int i; + return 0; - /* Set Ports C, D, E, G, and H for GPIO use */ - ep93xx_devcfg_set_bits(EP93XX_SYSCON_DEVCFG_KEYS | - EP93XX_SYSCON_DEVCFG_GONK | - EP93XX_SYSCON_DEVCFG_EONIDE | - EP93XX_SYSCON_DEVCFG_GONIDE | - EP93XX_SYSCON_DEVCFG_HONIDE); +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; +} - for (i = 0; i < ARRAY_SIZE(ep93xx_gpio_banks); i++) { - struct gpio_chip *chip = &ep93xx_gpio_banks[i].chip; - - /* - * Ports A, B, and F support input debouncing when - * used as interrupts. - */ - if (!strcmp(chip->label, "A") || - !strcmp(chip->label, "B") || - !strcmp(chip->label, "F")) - chip->set_debounce = ep93xx_gpio_set_debounce; - - gpiochip_add(chip); - } +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) +{ + pr_info("%s\n", __func__); + return platform_driver_register(&ep93xx_gpio_driver); } +postcore_initcall(ep93xx_gpio_init); + +MODULE_AUTHOR("Ryan Mallon " + "H Hartley Sweeten "); +MODULE_DESCRIPTION("EP93XX GPIO driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ab1c847af5271495c9c00c931284d7cd46840bd0 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 9 Jun 2011 12:40:21 -0600 Subject: gpio/ep93xx: Remove superfluous debug pr_info statement Reported-by: H Hartley Sweeten Signed-off-by: Grant Likely --- drivers/gpio/gpio-ep93xx.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index dc3870d4a4e..3bfd3417ab1 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -395,7 +395,6 @@ static struct platform_driver ep93xx_gpio_driver = { static int __init ep93xx_gpio_init(void) { - pr_info("%s\n", __func__); return platform_driver_register(&ep93xx_gpio_driver); } postcore_initcall(ep93xx_gpio_init); -- cgit v1.2.3 From b57e3c2692b04ac0b2f14bb94c63291fad1d7309 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 10 Jun 2011 09:08:17 -0700 Subject: gpio/ep93xx: Add missing EP93XX_GPIO_PHYS_BASE define The patch to convert of the ep93xx gpio driver to a platform_driver is missing the define for the memory resource that is passed to the driver. This is needed to prevent a build error. Signed-off-by: H Hartley Sweeten Signed-off-by: Grant Likely --- arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h index 9ac4d105509..c4a7b84ef06 100644 --- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h +++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h @@ -98,6 +98,7 @@ #define EP93XX_SECURITY_BASE EP93XX_APB_IOMEM(0x00030000) +#define EP93XX_GPIO_PHYS_BASE EP93XX_APB_PHYS(0x00040000) #define EP93XX_GPIO_BASE EP93XX_APB_IOMEM(0x00040000) #define EP93XX_GPIO_REG(x) (EP93XX_GPIO_BASE + (x)) #define EP93XX_GPIO_F_INT_STATUS EP93XX_GPIO_REG(0x5c) -- cgit v1.2.3 From 27810c5e7f8f2ddf036c16b5e40fd22abdc041f9 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 10 Jun 2011 18:16:15 -0700 Subject: gpio/vr41xx: Convert use of struct resource to resource_size(ptr) Remove miscellaneous use of direct calculation by using resource_size(). Signed-off-by: Joe Perches Signed-off-by: Grant Likely --- drivers/gpio/gpio-vr41xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-vr41xx.c b/drivers/gpio/gpio-vr41xx.c index a365be040b3..98723cb9ac6 100644 --- a/drivers/gpio/gpio-vr41xx.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; -- cgit v1.2.3 From 5523f86beab2b87d42cd58aca9bd5cee5466c6e3 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 12 Jun 2011 01:33:29 +0800 Subject: gpio/mxc: fix a bug with gpio_get_value calling When calling gpio_get_value, the gpio number other than bit offset should be passed as the argument. Signed-off-by: Shawn Guo Signed-off-by: Grant Likely --- drivers/gpio/gpio-mxc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 950e53a636f..2f6a81b8f12 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -83,7 +83,7 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type) edge = GPIO_INT_FALL_EDGE; break; case IRQ_TYPE_EDGE_BOTH: - val = gpio_get_value(gpio & 31); + val = gpio_get_value(gpio); if (val) { edge = GPIO_INT_LOW_LEV; pr_debug("mxc: set GPIO %d to low trigger\n", gpio); -- cgit v1.2.3 From 12610be33df5563d3f8cb141f231d4dc5b2a317f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 13 Jun 2011 10:47:15 +0200 Subject: gpio/74x164: fix clash with gpiolib namespace The 74x164 GPIO driver has a static inline helper called gpio_to_chip which clashes with the gpiolib namespace if we try to expose the function with the same name from gpiolib, and it's still confusing even if we don't do that. So rename it to gpio_to_74x164_chip(). Reported-by: H Hartley Sweeten Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpio-74x164.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index 7fb60b6bf52..ff525c0958d 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -23,7 +23,7 @@ struct gen_74x164_chip { u8 port_config; }; -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); } @@ -36,7 +36,7 @@ static int __gen_74x164_write_config(struct gen_74x164_chip *chip) 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); @@ -49,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) -- cgit v1.2.3 From 367f971ea45b87fae5714e5bcfecdba3a4871135 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 27 May 2011 22:25:26 -0400 Subject: ARM: zImage: ensure it is always a multiple of 64 bits in size This is needed for proper alignment when the DTB appending feature is used. Signed-off-by: Nicolas Pitre Acked-by: Tony Lindgren --- arch/arm/boot/compressed/vmlinux.lds.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/boot/compressed/vmlinux.lds.in b/arch/arm/boot/compressed/vmlinux.lds.in index ea80abe7884..6c02db134f2 100644 --- a/arch/arm/boot/compressed/vmlinux.lds.in +++ b/arch/arm/boot/compressed/vmlinux.lds.in @@ -47,6 +47,9 @@ SECTIONS .got : { *(.got) } _got_end = .; .got.plt : { *(.got.plt) } + + /* ensure the zImage file size is always a multiple of 64 bits */ + .pad : { BYTE(0); . = ALIGN(8); } _edata = .; . = BSS_START; -- cgit v1.2.3 From cb7715318d192eca7d0cbfd8dff5f9e1c7cb1d6a Mon Sep 17 00:00:00 2001 From: John Bonesio Date: Fri, 27 May 2011 18:45:50 -0400 Subject: ARM: zImage: Allow the appending of a device tree binary This patch provides the ability to boot using a device tree that is appended to the raw binary zImage (e.g. cat zImage .dtb > zImage_w_dtb). Signed-off-by: John Bonesio [nico: adjusted to latest zImage changes plus additional cleanups] Signed-off-by: Nicolas Pitre Acked-by: Grant Likely Acked-by: Tony Lindgren --- arch/arm/Kconfig | 8 +++++ arch/arm/boot/compressed/head.S | 70 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 9adc278a22a..66b7d1e0957 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1727,6 +1727,14 @@ config ZBOOT_ROM_MMCIF which in turn loads the rest the kernel image to RAM using the MMCIF hardware block. +config ARM_APPENDED_DTB + bool "Use appended device tree blob to zImage" + depends on OF && !ZBOOT_ROM + help + With this option, the boot code will look for a device tree binary + (dtb) appended to zImage + (e.g. cat zImage .dtb > zImage_w_dtb). + config CMDLINE string "Default kernel command string" default "" diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index f9da41921c5..b791b5e0d78 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -216,6 +216,59 @@ restart: adr r0, LC0 mov r10, r6 #endif + mov r5, #0 @ init dtb size to 0 +#ifdef CONFIG_ARM_APPENDED_DTB +/* + * r0 = delta + * r2 = BSS start + * r3 = BSS end + * r4 = final kernel address + * r5 = appended dtb size (still unknown) + * r6 = _edata + * r7 = architecture ID + * r8 = atags/device tree pointer + * r9 = size of decompressed image + * r10 = end of this image, including bss/stack/malloc space if non XIP + * r11 = GOT start + * r12 = GOT end + * sp = stack pointer + * + * if there are device trees (dtb) appended to zImage, advance r10 so that the + * dtb data will get relocated along with the kernel if necessary. + */ + + ldr lr, [r6, #0] +#ifndef __ARMEB__ + ldr r1, =0xedfe0dd0 @ sig is 0xd00dfeed big endian +#else + ldr r1, =0xd00dfeed +#endif + cmp lr, r1 + bne dtb_check_done @ not found + + mov r8, r6 @ use the appended device tree + + /* Get the dtb's size */ + ldr r5, [r6, #4] +#ifndef __ARMEB__ + /* convert r5 (dtb size) to little endian */ + eor r1, r5, r5, ror #16 + bic r1, r1, #0x00ff0000 + mov r5, r5, ror #8 + eor r5, r5, r1, lsr #8 +#endif + + /* preserve 64-bit alignment */ + add r5, r5, #7 + bic r5, r5, #7 + + /* relocate some pointers past the appended dtb */ + add r6, r6, r5 + add r10, r10, r5 + add sp, sp, r5 +dtb_check_done: +#endif + /* * Check to see if we will overwrite ourselves. * r4 = final kernel address @@ -285,14 +338,16 @@ wont_overwrite: * r2 = BSS start * r3 = BSS end * r4 = kernel execution address + * r5 = appended dtb size (0 if not present) * r7 = architecture ID * r8 = atags pointer * r11 = GOT start * r12 = GOT end * sp = stack pointer */ - teq r0, #0 + orrs r1, r0, r5 beq not_relocated + add r11, r11, r0 add r12, r12, r0 @@ -307,12 +362,21 @@ wont_overwrite: /* * Relocate all entries in the GOT table. + * Bump bss entries to _edata + dtb size */ 1: ldr r1, [r11, #0] @ relocate entries in the GOT - add r1, r1, r0 @ table. This fixes up the - str r1, [r11], #4 @ C references. + add r1, r1, r0 @ This fixes up C references + cmp r1, r2 @ if entry >= bss_start && + cmphs r3, r1 @ bss_end > entry + addhi r1, r1, r5 @ entry += dtb size + str r1, [r11], #4 @ next entry cmp r11, r12 blo 1b + + /* bump our bss pointers too */ + add r2, r2, r5 + add r3, r3, r5 + #else /* -- cgit v1.2.3 From d22ebec9fbae7c59ce941957408af7e12f773073 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Sun, 12 Jun 2011 01:07:33 -0400 Subject: ARM: zImage: make sure appended DTB doesn't get overwritten by kernel .bss The appended DTB gets relocated with the decompressor code to get out of the way of the decompressed kernel. However the kernel's .bss section may be larger than the relocated code and data, and then the DTB gets overwritten. Let's make sure the relocation takes care of moving zImage far enough so no such conflict with .bss occurs. Thanks to Tony Lindgren for figuring out this issue. While at it, let's clean up the code a bit so that the wont_overwrite symbol is used while determining if a conflict exists, making the above change more precise as well as eliminating some ARM/THUMB alternates. Signed-off-by: Nicolas Pitre Acked-by: Tony Lindgren --- arch/arm/boot/compressed/Makefile | 3 +++ arch/arm/boot/compressed/head.S | 19 +++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index 23aad072230..48bead99ea4 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -98,6 +98,9 @@ endif ccflags-y := -fpic -fno-builtin asflags-y := -Wa,-march=all +# Supply kernel BSS size to the decompressor via a linker symbol. +KBSS_SZ = $(shell size $(obj)/../../../../vmlinux | awk 'END{print $$3}') +LDFLAGS_vmlinux = --defsym _kernel_bss_size=$(KBSS_SZ) # Supply ZRELADDR to the decompressor via a linker symbol. ifneq ($(CONFIG_AUTO_ZRELADDR),y) LDFLAGS_vmlinux += --defsym zreladdr=$(ZRELADDR) diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index b791b5e0d78..d4f8db2f1c4 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -248,6 +248,18 @@ restart: adr r0, LC0 mov r8, r6 @ use the appended device tree + /* + * Make sure that the DTB doesn't end up in the final + * kernel's .bss area. To do so, we adjust the decompressed + * kernel size to compensate if that .bss size is larger + * than the relocated code. + */ + ldr r5, =_kernel_bss_size + adr r1, wont_overwrite + sub r1, r6, r1 + subs r1, r5, r1 + addhi r9, r9, r1 + /* Get the dtb's size */ ldr r5, [r6, #4] #ifndef __ARMEB__ @@ -276,15 +288,14 @@ dtb_check_done: * r10 = end of this image, including bss/stack/malloc space if non XIP * We basically want: * r4 - 16k page directory >= r10 -> OK - * r4 + image length <= current position (pc) -> OK + * r4 + image length <= address of wont_overwrite -> OK */ add r10, r10, #16384 cmp r4, r10 bhs wont_overwrite add r10, r4, r9 - ARM( cmp r10, pc ) - THUMB( mov lr, pc ) - THUMB( cmp r10, lr ) + adr r9, wont_overwrite + cmp r10, r9 bls wont_overwrite /* -- cgit v1.2.3 From 786b01a8c1db0c0decca55d660a2a3ebd7cfb26b Mon Sep 17 00:00:00 2001 From: Oleg Drokin Date: Mon, 6 Jun 2011 18:57:07 +0000 Subject: cleanup regulator supply definitions in mach-omap2 to use REGULATOR_SUPPLY arrays. CC: Mark Brown CC: Mike Rapoport CC: Nishant Kamat CC: Steve Sakoman CC: Felipe Balbi CC: Santosh Shilimkar CC: peter.barada@logicpd.com Signed-off-by: Oleg Drokin Acked-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/board-4430sdp.c | 13 ++---- arch/arm/mach-omap2/board-cm-t35.c | 34 ++++++++------- arch/arm/mach-omap2/board-devkit8000.c | 28 +++++++------ arch/arm/mach-omap2/board-igep0020.c | 27 ++++++------ arch/arm/mach-omap2/board-ldp.c | 8 ++-- arch/arm/mach-omap2/board-omap3beagle.c | 25 +++++------ arch/arm/mach-omap2/board-omap3evm.c | 41 +++++++++--------- arch/arm/mach-omap2/board-omap3logic.c | 8 ++-- arch/arm/mach-omap2/board-omap3pandora.c | 63 +++++++++++++++------------- arch/arm/mach-omap2/board-omap3stalker.c | 25 +++++------ arch/arm/mach-omap2/board-omap3touchbook.c | 32 +++++++------- arch/arm/mach-omap2/board-omap4panda.c | 16 +++---- arch/arm/mach-omap2/board-overo.c | 26 ++++++------ arch/arm/mach-omap2/board-rx51-peripherals.c | 29 +++++++------ arch/arm/mach-omap2/board-zoom-peripherals.c | 42 +++++++++---------- 15 files changed, 218 insertions(+), 199 deletions(-) diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index 63de2d396e2..39a80624177 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -333,16 +333,11 @@ static struct omap2_hsmmc_info mmc[] = { }; static struct regulator_consumer_supply sdp4430_vaux_supply[] = { - { - .supply = "vmmc", - .dev_name = "omap_hsmmc.1", - }, + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.1"), }; + static struct regulator_consumer_supply sdp4430_vmmc_supply[] = { - { - .supply = "vmmc", - .dev_name = "omap_hsmmc.0", - }, + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"), }; static int omap4_twl6030_hsmmc_late_init(struct device *dev) @@ -399,7 +394,7 @@ static struct regulator_init_data sdp4430_vaux1 = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, + .num_consumer_supplies = ARRAY_SIZE(sdp4430_vaux_supply), .consumer_supplies = sdp4430_vaux_supply, }; diff --git a/arch/arm/mach-omap2/board-cm-t35.c b/arch/arm/mach-omap2/board-cm-t35.c index 77456dec93e..e7bf32de753 100644 --- a/arch/arm/mach-omap2/board-cm-t35.c +++ b/arch/arm/mach-omap2/board-cm-t35.c @@ -337,19 +337,21 @@ static void __init cm_t35_init_display(void) } } -static struct regulator_consumer_supply cm_t35_vmmc1_supply = { - .supply = "vmmc", +static struct regulator_consumer_supply cm_t35_vmmc1_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"), }; -static struct regulator_consumer_supply cm_t35_vsim_supply = { - .supply = "vmmc_aux", +static struct regulator_consumer_supply cm_t35_vsim_supply[] = { + REGULATOR_SUPPLY("vmmc_aux", "omap_hsmmc.0"), }; -static struct regulator_consumer_supply cm_t35_vdac_supply = - REGULATOR_SUPPLY("vdda_dac", "omapdss_venc"); +static struct regulator_consumer_supply cm_t35_vdac_supply[] = { + REGULATOR_SUPPLY("vdda_dac", "omapdss_venc"), +}; -static struct regulator_consumer_supply cm_t35_vdvi_supply = - REGULATOR_SUPPLY("vdvi", "omapdss"); +static struct regulator_consumer_supply cm_t35_vdvi_supply[] = { + REGULATOR_SUPPLY("vdvi", "omapdss"), +}; /* VMMC1 for MMC1 pins CMD, CLK, DAT0..DAT3 (20 mA, plus card == max 220 mA) */ static struct regulator_init_data cm_t35_vmmc1 = { @@ -362,8 +364,8 @@ static struct regulator_init_data cm_t35_vmmc1 = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &cm_t35_vmmc1_supply, + .num_consumer_supplies = ARRAY_SIZE(cm_t35_vmmc1_supply), + .consumer_supplies = cm_t35_vmmc1_supply, }; /* VSIM for MMC1 pins DAT4..DAT7 (2 mA, plus card == max 50 mA) */ @@ -377,8 +379,8 @@ static struct regulator_init_data cm_t35_vsim = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &cm_t35_vsim_supply, + .num_consumer_supplies = ARRAY_SIZE(cm_t35_vsim_supply), + .consumer_supplies = cm_t35_vsim_supply, }; /* VDAC for DSS driving S-Video (8 mA unloaded, max 65 mA) */ @@ -391,8 +393,8 @@ static struct regulator_init_data cm_t35_vdac = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &cm_t35_vdac_supply, + .num_consumer_supplies = ARRAY_SIZE(cm_t35_vdac_supply), + .consumer_supplies = cm_t35_vdac_supply, }; /* VPLL2 for digital video outputs */ @@ -406,8 +408,8 @@ static struct regulator_init_data cm_t35_vpll2 = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &cm_t35_vdvi_supply, + .num_consumer_supplies = ARRAY_SIZE(cm_t35_vdvi_supply), + .consumer_supplies = cm_t35_vdvi_supply, }; static struct twl4030_usb_data cm_t35_usb_data = { diff --git a/arch/arm/mach-omap2/board-devkit8000.c b/arch/arm/mach-omap2/board-devkit8000.c index 34956ec8329..ead9c1d1940 100644 --- a/arch/arm/mach-omap2/board-devkit8000.c +++ b/arch/arm/mach-omap2/board-devkit8000.c @@ -130,13 +130,14 @@ static void devkit8000_panel_disable_dvi(struct omap_dss_device *dssdev) gpio_set_value_cansleep(dssdev->reset_gpio, 0); } -static struct regulator_consumer_supply devkit8000_vmmc1_supply = - REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"); - +static struct regulator_consumer_supply devkit8000_vmmc1_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"), +}; /* ads7846 on SPI */ -static struct regulator_consumer_supply devkit8000_vio_supply = - REGULATOR_SUPPLY("vcc", "spi2.0"); +static struct regulator_consumer_supply devkit8000_vio_supply[] = { + REGULATOR_SUPPLY("vcc", "spi2.0"), +}; static struct panel_generic_dpi_data lcd_panel = { .name = "generic", @@ -186,8 +187,9 @@ static struct omap_dss_board_info devkit8000_dss_data = { .default_device = &devkit8000_lcd_device, }; -static struct regulator_consumer_supply devkit8000_vdda_dac_supply = - REGULATOR_SUPPLY("vdda_dac", "omapdss_venc"); +static struct regulator_consumer_supply devkit8000_vdda_dac_supply[] = { + REGULATOR_SUPPLY("vdda_dac", "omapdss_venc"), +}; static uint32_t board_keymap[] = { KEY(0, 0, KEY_1), @@ -284,8 +286,8 @@ static struct regulator_init_data devkit8000_vmmc1 = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &devkit8000_vmmc1_supply, + .num_consumer_supplies = ARRAY_SIZE(devkit8000_vmmc1_supply), + .consumer_supplies = devkit8000_vmmc1_supply, }; /* VDAC for DSS driving S-Video (8 mA unloaded, max 65 mA) */ @@ -298,8 +300,8 @@ static struct regulator_init_data devkit8000_vdac = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &devkit8000_vdda_dac_supply, + .num_consumer_supplies = ARRAY_SIZE(devkit8000_vdda_dac_supply), + .consumer_supplies = devkit8000_vdda_dac_supply, }; /* VPLL1 for digital video outputs */ @@ -327,8 +329,8 @@ static struct regulator_init_data devkit8000_vio = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &devkit8000_vio_supply, + .num_consumer_supplies = ARRAY_SIZE(devkit8000_vio_supply), + .consumer_supplies = devkit8000_vio_supply, }; static struct twl4030_usb_data devkit8000_usb_data = { diff --git a/arch/arm/mach-omap2/board-igep0020.c b/arch/arm/mach-omap2/board-igep0020.c index 0c1bfca3f73..84d28460833 100644 --- a/arch/arm/mach-omap2/board-igep0020.c +++ b/arch/arm/mach-omap2/board-igep0020.c @@ -222,8 +222,9 @@ static inline void __init igep2_init_smsc911x(void) static inline void __init igep2_init_smsc911x(void) { } #endif -static struct regulator_consumer_supply igep_vmmc1_supply = - REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"); +static struct regulator_consumer_supply igep_vmmc1_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"), +}; /* VMMC1 for OMAP VDD_MMC1 (i/o) and MMC1 card */ static struct regulator_init_data igep_vmmc1 = { @@ -236,12 +237,13 @@ static struct regulator_init_data igep_vmmc1 = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &igep_vmmc1_supply, + .num_consumer_supplies = ARRAY_SIZE(igep_vmmc1_supply), + .consumer_supplies = igep_vmmc1_supply, }; -static struct regulator_consumer_supply igep_vio_supply = - REGULATOR_SUPPLY("vmmc_aux", "omap_hsmmc.1"); +static struct regulator_consumer_supply igep_vio_supply[] = { + REGULATOR_SUPPLY("vmmc_aux", "omap_hsmmc.1"), +}; static struct regulator_init_data igep_vio = { .constraints = { @@ -254,20 +256,21 @@ static struct regulator_init_data igep_vio = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &igep_vio_supply, + .num_consumer_supplies = ARRAY_SIZE(igep_vio_supply), + .consumer_supplies = igep_vio_supply, }; -static struct regulator_consumer_supply igep_vmmc2_supply = - REGULATOR_SUPPLY("vmmc", "omap_hsmmc.1"); +static struct regulator_consumer_supply igep_vmmc2_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.1"), +}; static struct regulator_init_data igep_vmmc2 = { .constraints = { .valid_modes_mask = REGULATOR_MODE_NORMAL, .always_on = 1, }, - .num_consumer_supplies = 1, - .consumer_supplies = &igep_vmmc2_supply, + .num_consumer_supplies = ARRAY_SIZE(igep_vmmc2_supply), + .consumer_supplies = igep_vmmc2_supply, }; static struct fixed_voltage_config igep_vwlan = { diff --git a/arch/arm/mach-omap2/board-ldp.c b/arch/arm/mach-omap2/board-ldp.c index f7d6038075f..069bc9f0cf3 100644 --- a/arch/arm/mach-omap2/board-ldp.c +++ b/arch/arm/mach-omap2/board-ldp.c @@ -213,8 +213,8 @@ static struct twl4030_madc_platform_data ldp_madc_data = { .irq_line = 1, }; -static struct regulator_consumer_supply ldp_vmmc1_supply = { - .supply = "vmmc", +static struct regulator_consumer_supply ldp_vmmc1_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"), }; /* VMMC1 for MMC1 pins CMD, CLK, DAT0..DAT3 (20 mA, plus card == max 220 mA) */ @@ -228,8 +228,8 @@ static struct regulator_init_data ldp_vmmc1 = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &ldp_vmmc1_supply, + .num_consumer_supplies = ARRAY_SIZE(ldp_vmmc1_supply), + .consumer_supplies = ldp_vmmc1_supply, }; /* ads7846 on SPI */ diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c index 7f21d24bd43..4cf7c19ea0e 100644 --- a/arch/arm/mach-omap2/board-omap3beagle.c +++ b/arch/arm/mach-omap2/board-omap3beagle.c @@ -210,8 +210,9 @@ static struct omap_dss_board_info beagle_dss_data = { .default_device = &beagle_dvi_device, }; -static struct regulator_consumer_supply beagle_vdac_supply = - REGULATOR_SUPPLY("vdda_dac", "omapdss_venc"); +static struct regulator_consumer_supply beagle_vdac_supply[] = { + REGULATOR_SUPPLY("vdda_dac", "omapdss_venc"), +}; static struct regulator_consumer_supply beagle_vdvi_supplies[] = { REGULATOR_SUPPLY("vdds_dsi", "omapdss"), @@ -239,12 +240,12 @@ static struct omap2_hsmmc_info mmc[] = { {} /* Terminator */ }; -static struct regulator_consumer_supply beagle_vmmc1_supply = { - .supply = "vmmc", +static struct regulator_consumer_supply beagle_vmmc1_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"), }; -static struct regulator_consumer_supply beagle_vsim_supply = { - .supply = "vmmc_aux", +static struct regulator_consumer_supply beagle_vsim_supply[] = { + REGULATOR_SUPPLY("vmmc_aux", "omap_hsmmc.0"), }; static struct gpio_led gpio_leds[]; @@ -336,8 +337,8 @@ static struct regulator_init_data beagle_vmmc1 = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &beagle_vmmc1_supply, + .num_consumer_supplies = ARRAY_SIZE(beagle_vmmc1_supply), + .consumer_supplies = beagle_vmmc1_supply, }; /* VSIM for MMC1 pins DAT4..DAT7 (2 mA, plus card == max 50 mA) */ @@ -351,8 +352,8 @@ static struct regulator_init_data beagle_vsim = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &beagle_vsim_supply, + .num_consumer_supplies = ARRAY_SIZE(beagle_vsim_supply), + .consumer_supplies = beagle_vsim_supply, }; /* VDAC for DSS driving S-Video (8 mA unloaded, max 65 mA) */ @@ -365,8 +366,8 @@ static struct regulator_init_data beagle_vdac = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &beagle_vdac_supply, + .num_consumer_supplies = ARRAY_SIZE(beagle_vdac_supply), + .consumer_supplies = beagle_vdac_supply, }; /* VPLL2 for digital video outputs */ diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c index b4d43464a30..fc7a23add5f 100644 --- a/arch/arm/mach-omap2/board-omap3evm.c +++ b/arch/arm/mach-omap2/board-omap3evm.c @@ -273,12 +273,12 @@ static struct omap_dss_board_info omap3_evm_dss_data = { .default_device = &omap3_evm_lcd_device, }; -static struct regulator_consumer_supply omap3evm_vmmc1_supply = { - .supply = "vmmc", +static struct regulator_consumer_supply omap3evm_vmmc1_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"), }; -static struct regulator_consumer_supply omap3evm_vsim_supply = { - .supply = "vmmc_aux", +static struct regulator_consumer_supply omap3evm_vsim_supply[] = { + REGULATOR_SUPPLY("vmmc_aux", "omap_hsmmc.0"), }; /* VMMC1 for MMC1 pins CMD, CLK, DAT0..DAT3 (20 mA, plus card == max 220 mA) */ @@ -292,8 +292,8 @@ static struct regulator_init_data omap3evm_vmmc1 = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &omap3evm_vmmc1_supply, + .num_consumer_supplies = ARRAY_SIZE(omap3evm_vmmc1_supply), + .consumer_supplies = omap3evm_vmmc1_supply, }; /* VSIM for MMC1 pins DAT4..DAT7 (2 mA, plus card == max 50 mA) */ @@ -307,8 +307,8 @@ static struct regulator_init_data omap3evm_vsim = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &omap3evm_vsim_supply, + .num_consumer_supplies = ARRAY_SIZE(omap3evm_vsim_supply), + .consumer_supplies = omap3evm_vsim_supply, }; static struct omap2_hsmmc_info mmc[] = { @@ -449,8 +449,9 @@ static struct twl4030_codec_data omap3evm_codec_data = { .audio = &omap3evm_audio_data, }; -static struct regulator_consumer_supply omap3_evm_vdda_dac_supply = - REGULATOR_SUPPLY("vdda_dac", "omapdss_venc"); +static struct regulator_consumer_supply omap3_evm_vdda_dac_supply[] = { + REGULATOR_SUPPLY("vdda_dac", "omapdss_venc"), +}; /* VDAC for DSS driving S-Video */ static struct regulator_init_data omap3_evm_vdac = { @@ -463,8 +464,8 @@ static struct regulator_init_data omap3_evm_vdac = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &omap3_evm_vdda_dac_supply, + .num_consumer_supplies = ARRAY_SIZE(omap3_evm_vdda_dac_supply), + .consumer_supplies = omap3_evm_vdda_dac_supply, }; /* VPLL2 for digital video outputs */ @@ -488,8 +489,9 @@ static struct regulator_init_data omap3_evm_vpll2 = { }; /* ads7846 on SPI */ -static struct regulator_consumer_supply omap3evm_vio_supply = - REGULATOR_SUPPLY("vcc", "spi1.0"); +static struct regulator_consumer_supply omap3evm_vio_supply[] = { + REGULATOR_SUPPLY("vcc", "spi1.0"), +}; /* VIO for ads7846 */ static struct regulator_init_data omap3evm_vio = { @@ -502,8 +504,8 @@ static struct regulator_init_data omap3evm_vio = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &omap3evm_vio_supply, + .num_consumer_supplies = ARRAY_SIZE(omap3evm_vio_supply), + .consumer_supplies = omap3evm_vio_supply, }; #ifdef CONFIG_WL12XX_PLATFORM_DATA @@ -511,16 +513,17 @@ static struct regulator_init_data omap3evm_vio = { #define OMAP3EVM_WLAN_PMENA_GPIO (150) #define OMAP3EVM_WLAN_IRQ_GPIO (149) -static struct regulator_consumer_supply omap3evm_vmmc2_supply = +static struct regulator_consumer_supply omap3evm_vmmc2_supply[] = { REGULATOR_SUPPLY("vmmc", "omap_hsmmc.1"); +}; /* VMMC2 for driving the WL12xx module */ static struct regulator_init_data omap3evm_vmmc2 = { .constraints = { .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &omap3evm_vmmc2_supply, + .num_consumer_supplies = ARRAY_SIZE(omap3evm_vmmc2_supply);, + .consumer_supplies = omap3evm_vmmc2_supply, }; static struct fixed_voltage_config omap3evm_vwlan = { diff --git a/arch/arm/mach-omap2/board-omap3logic.c b/arch/arm/mach-omap2/board-omap3logic.c index 60d9be49dba..ec18435bc5d 100644 --- a/arch/arm/mach-omap2/board-omap3logic.c +++ b/arch/arm/mach-omap2/board-omap3logic.c @@ -55,8 +55,8 @@ #define OMAP3_TORPEDO_MMC_GPIO_CD 127 #define OMAP3_TORPEDO_SMSC911X_GPIO_IRQ 129 -static struct regulator_consumer_supply omap3logic_vmmc1_supply = { - .supply = "vmmc", +static struct regulator_consumer_supply omap3logic_vmmc1_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"), }; /* VMMC1 for MMC1 pins CMD, CLK, DAT0..DAT3 (20 mA, plus card == max 220 mA) */ @@ -71,8 +71,8 @@ static struct regulator_init_data omap3logic_vmmc1 = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &omap3logic_vmmc1_supply, + .num_consumer_supplies = ARRAY_SIZE(omap3logic_vmmc1_supply), + .consumer_supplies = omap3logic_vmmc1_supply, }; static struct twl4030_gpio_platform_data omap3logic_gpio_data = { diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c index 2a0bb4818ca..e95bba281a0 100644 --- a/arch/arm/mach-omap2/board-omap3pandora.c +++ b/arch/arm/mach-omap2/board-omap3pandora.c @@ -319,17 +319,21 @@ static struct twl4030_gpio_platform_data omap3pandora_gpio_data = { .setup = omap3pandora_twl_gpio_setup, }; -static struct regulator_consumer_supply pandora_vmmc1_supply = - REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"); +static struct regulator_consumer_supply pandora_vmmc1_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"), +}; -static struct regulator_consumer_supply pandora_vmmc2_supply = - REGULATOR_SUPPLY("vmmc", "omap_hsmmc.1"); +static struct regulator_consumer_supply pandora_vmmc2_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.1") +}; -static struct regulator_consumer_supply pandora_vmmc3_supply = - REGULATOR_SUPPLY("vmmc", "omap_hsmmc.2"); +static struct regulator_consumer_supply pandora_vmmc3_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.2"), +}; -static struct regulator_consumer_supply pandora_vdda_dac_supply = - REGULATOR_SUPPLY("vdda_dac", "omapdss_venc"); +static struct regulator_consumer_supply pandora_vdda_dac_supply[] = { + REGULATOR_SUPPLY("vdda_dac", "omapdss_venc"), +}; static struct regulator_consumer_supply pandora_vdds_supplies[] = { REGULATOR_SUPPLY("vdds_sdi", "omapdss"), @@ -337,11 +341,13 @@ static struct regulator_consumer_supply pandora_vdds_supplies[] = { REGULATOR_SUPPLY("vdds_dsi", "omapdss_dsi1"), }; -static struct regulator_consumer_supply pandora_vcc_lcd_supply = - REGULATOR_SUPPLY("vcc", "display0"); +static struct regulator_consumer_supply pandora_vcc_lcd_supply[] = { + REGULATOR_SUPPLY("vcc", "display0"), +}; -static struct regulator_consumer_supply pandora_usb_phy_supply = - REGULATOR_SUPPLY("hsusb0", "ehci-omap.0"); +static struct regulator_consumer_supply pandora_usb_phy_supply[] = { + REGULATOR_SUPPLY("hsusb0", "ehci-omap.0"), +}; /* ads7846 on SPI and 2 nub controllers on I2C */ static struct regulator_consumer_supply pandora_vaux4_supplies[] = { @@ -350,8 +356,9 @@ static struct regulator_consumer_supply pandora_vaux4_supplies[] = { REGULATOR_SUPPLY("vcc", "3-0067"), }; -static struct regulator_consumer_supply pandora_adac_supply = - REGULATOR_SUPPLY("vcc", "soc-audio"); +static struct regulator_consumer_supply pandora_adac_supply[] = { + REGULATOR_SUPPLY("vcc", "soc-audio"), +}; /* VMMC1 for MMC1 pins CMD, CLK, DAT0..DAT3 (20 mA, plus card == max 220 mA) */ static struct regulator_init_data pandora_vmmc1 = { @@ -364,8 +371,8 @@ static struct regulator_init_data pandora_vmmc1 = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &pandora_vmmc1_supply, + .num_consumer_supplies = ARRAY_SIZE(pandora_vmmc1_supply), + .consumer_supplies = pandora_vmmc1_supply, }; /* VMMC2 for MMC2 pins CMD, CLK, DAT0..DAT3 (max 100 mA) */ @@ -379,8 +386,8 @@ static struct regulator_init_data pandora_vmmc2 = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &pandora_vmmc2_supply, + .num_consumer_supplies = ARRAY_SIZE(pandora_vmmc2_supply), + .consumer_supplies = pandora_vmmc2_supply, }; /* VDAC for DSS driving S-Video */ @@ -394,8 +401,8 @@ static struct regulator_init_data pandora_vdac = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &pandora_vdda_dac_supply, + .num_consumer_supplies = ARRAY_SIZE(pandora_vdda_dac_supply), + .consumer_supplies = pandora_vdda_dac_supply, }; /* VPLL2 for digital video outputs */ @@ -424,8 +431,8 @@ static struct regulator_init_data pandora_vaux1 = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &pandora_vcc_lcd_supply, + .num_consumer_supplies = ARRAY_SIZE(pandora_vcc_lcd_supply), + .consumer_supplies = pandora_vcc_lcd_supply, }; /* VAUX2 for USB host PHY */ @@ -439,8 +446,8 @@ static struct regulator_init_data pandora_vaux2 = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &pandora_usb_phy_supply, + .num_consumer_supplies = ARRAY_SIZE(pandora_usb_phy_supply), + .consumer_supplies = pandora_usb_phy_supply, }; /* VAUX4 for ads7846 and nubs */ @@ -469,8 +476,8 @@ static struct regulator_init_data pandora_vsim = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &pandora_adac_supply, + .num_consumer_supplies = ARRAY_SIZE(pandora_adac_supply), + .consumer_supplies = pandora_adac_supply, }; /* Fixed regulator internal to Wifi module */ @@ -478,8 +485,8 @@ static struct regulator_init_data pandora_vmmc3 = { .constraints = { .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &pandora_vmmc3_supply, + .num_consumer_supplies = ARRAY_SIZE(pandora_vmmc3_supply), + .consumer_supplies = pandora_vmmc3_supply, }; static struct fixed_voltage_config pandora_vwlan = { diff --git a/arch/arm/mach-omap2/board-omap3stalker.c b/arch/arm/mach-omap2/board-omap3stalker.c index 0c108a212ea..99be540dbab 100644 --- a/arch/arm/mach-omap2/board-omap3stalker.c +++ b/arch/arm/mach-omap2/board-omap3stalker.c @@ -206,12 +206,12 @@ static struct omap_dss_board_info omap3_stalker_dss_data = { .default_device = &omap3_stalker_dvi_device, }; -static struct regulator_consumer_supply omap3stalker_vmmc1_supply = { - .supply = "vmmc", +static struct regulator_consumer_supply omap3stalker_vmmc1_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"), }; -static struct regulator_consumer_supply omap3stalker_vsim_supply = { - .supply = "vmmc_aux", +static struct regulator_consumer_supply omap3stalker_vsim_supply[] = { + REGULATOR_SUPPLY("vmmc_aux", "omap_hsmmc.0"), }; /* VMMC1 for MMC1 pins CMD, CLK, DAT0..DAT3 (20 mA, plus card == max 220 mA) */ @@ -224,8 +224,8 @@ static struct regulator_init_data omap3stalker_vmmc1 = { .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &omap3stalker_vmmc1_supply, + .num_consumer_supplies = ARRAY_SIZE(omap3stalker_vmmc1_supply), + .consumer_supplies = omap3stalker_vmmc1_supply, }; /* VSIM for MMC1 pins DAT4..DAT7 (2 mA, plus card == max 50 mA) */ @@ -238,8 +238,8 @@ static struct regulator_init_data omap3stalker_vsim = { .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &omap3stalker_vsim_supply, + .num_consumer_supplies = ARRAY_SIZE(omap3stalker_vsim_supply), + .consumer_supplies = omap3stalker_vsim_supply, }; static struct omap2_hsmmc_info mmc[] = { @@ -403,8 +403,9 @@ static struct twl4030_codec_data omap3stalker_codec_data = { .audio = &omap3stalker_audio_data, }; -static struct regulator_consumer_supply omap3_stalker_vdda_dac_supply = - REGULATOR_SUPPLY("vdda_dac", "omapdss_venc"); +static struct regulator_consumer_supply omap3_stalker_vdda_dac_supply[] = { + REGULATOR_SUPPLY("vdda_dac", "omapdss_venc"), +}; /* VDAC for DSS driving S-Video */ static struct regulator_init_data omap3_stalker_vdac = { @@ -417,8 +418,8 @@ static struct regulator_init_data omap3_stalker_vdac = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &omap3_stalker_vdda_dac_supply, + .num_consumer_supplies = ARRAY_SIZE(omap3_stalker_vdda_dac_supply), + .consumer_supplies = omap3_stalker_vdda_dac_supply, }; /* VPLL2 for digital video outputs */ diff --git a/arch/arm/mach-omap2/board-omap3touchbook.c b/arch/arm/mach-omap2/board-omap3touchbook.c index 5f649faf737..ab5c37d9540 100644 --- a/arch/arm/mach-omap2/board-omap3touchbook.c +++ b/arch/arm/mach-omap2/board-omap3touchbook.c @@ -114,12 +114,12 @@ static struct omap_lcd_config omap3_touchbook_lcd_config __initdata = { .ctrl_name = "internal", }; -static struct regulator_consumer_supply touchbook_vmmc1_supply = { - .supply = "vmmc", +static struct regulator_consumer_supply touchbook_vmmc1_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"), }; -static struct regulator_consumer_supply touchbook_vsim_supply = { - .supply = "vmmc_aux", +static struct regulator_consumer_supply touchbook_vsim_supply[] = { + REGULATOR_SUPPLY("vmmc_aux", "omap_hsmmc.0"), }; static struct gpio_led gpio_leds[]; @@ -167,14 +167,18 @@ static struct twl4030_gpio_platform_data touchbook_gpio_data = { .setup = touchbook_twl_gpio_setup, }; -static struct regulator_consumer_supply touchbook_vdac_supply = { +static struct regulator_consumer_supply touchbook_vdac_supply[] = { +{ .supply = "vdac", .dev = &omap3_touchbook_lcd_device.dev, +}, }; -static struct regulator_consumer_supply touchbook_vdvi_supply = { +static struct regulator_consumer_supply touchbook_vdvi_supply[] = { +{ .supply = "vdvi", .dev = &omap3_touchbook_lcd_device.dev, +}, }; /* VMMC1 for MMC1 pins CMD, CLK, DAT0..DAT3 (20 mA, plus card == max 220 mA) */ @@ -188,8 +192,8 @@ static struct regulator_init_data touchbook_vmmc1 = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &touchbook_vmmc1_supply, + .num_consumer_supplies = ARRAY_SIZE(touchbook_vmmc1_supply), + .consumer_supplies = touchbook_vmmc1_supply, }; /* VSIM for MMC1 pins DAT4..DAT7 (2 mA, plus card == max 50 mA) */ @@ -203,8 +207,8 @@ static struct regulator_init_data touchbook_vsim = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &touchbook_vsim_supply, + .num_consumer_supplies = ARRAY_SIZE(touchbook_vsim_supply), + .consumer_supplies = touchbook_vsim_supply, }; /* VDAC for DSS driving S-Video (8 mA unloaded, max 65 mA) */ @@ -217,8 +221,8 @@ static struct regulator_init_data touchbook_vdac = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &touchbook_vdac_supply, + .num_consumer_supplies = ARRAY_SIZE(touchbook_vdac_supply), + .consumer_supplies = touchbook_vdac_supply, }; /* VPLL2 for digital video outputs */ @@ -232,8 +236,8 @@ static struct regulator_init_data touchbook_vpll2 = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &touchbook_vdvi_supply, + .num_consumer_supplies = ARRAY_SIZE(touchbook_vdvi_supply), + .consumer_supplies = touchbook_vdvi_supply, }; static struct twl4030_usb_data touchbook_usb_data = { diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c index 0cfe2005cb5..6d2372b98e4 100644 --- a/arch/arm/mach-omap2/board-omap4panda.c +++ b/arch/arm/mach-omap2/board-omap4panda.c @@ -183,23 +183,19 @@ static struct omap2_hsmmc_info mmc[] = { }; static struct regulator_consumer_supply omap4_panda_vmmc_supply[] = { - { - .supply = "vmmc", - .dev_name = "omap_hsmmc.0", - }, + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"), }; -static struct regulator_consumer_supply omap4_panda_vmmc5_supply = { - .supply = "vmmc", - .dev_name = "omap_hsmmc.4", +static struct regulator_consumer_supply omap4_panda_vmmc5_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.4"), }; static struct regulator_init_data panda_vmmc5 = { .constraints = { .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &omap4_panda_vmmc5_supply, + .num_consumer_supplies = ARRAY_SIZE(omap4_panda_vmmc5_supply), + .consumer_supplies = omap4_panda_vmmc5_supply, }; static struct fixed_voltage_config panda_vwlan = { @@ -312,7 +308,7 @@ static struct regulator_init_data omap4_panda_vmmc = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, + .num_consumer_supplies = ARRAY_SIZE(omap4_panda_vmmc_supply), .consumer_supplies = omap4_panda_vmmc_supply, }; diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c index 175e1ab2b04..30c7556d251 100644 --- a/arch/arm/mach-omap2/board-overo.c +++ b/arch/arm/mach-omap2/board-overo.c @@ -74,15 +74,16 @@ defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE) /* fixed regulator for ads7846 */ -static struct regulator_consumer_supply ads7846_supply = - REGULATOR_SUPPLY("vcc", "spi1.0"); +static struct regulator_consumer_supply ads7846_supply[] = { + REGULATOR_SUPPLY("vcc", "spi1.0"), +}; static struct regulator_init_data vads7846_regulator = { .constraints = { .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &ads7846_supply, + .num_consumer_supplies = ARRAY_SIZE(ads7846_supply), + .consumer_supplies = ads7846_supply, }; static struct fixed_voltage_config vads7846 = { @@ -264,8 +265,9 @@ static struct omap_dss_board_info overo_dss_data = { .default_device = &overo_dvi_device, }; -static struct regulator_consumer_supply overo_vdda_dac_supply = - REGULATOR_SUPPLY("vdda_dac", "omapdss_venc"); +static struct regulator_consumer_supply overo_vdda_dac_supply[] = { + REGULATOR_SUPPLY("vdda_dac", "omapdss_venc"), +}; static struct regulator_consumer_supply overo_vdds_dsi_supply[] = { REGULATOR_SUPPLY("vdds_dsi", "omapdss"), @@ -319,8 +321,8 @@ static struct omap2_hsmmc_info mmc[] = { {} /* Terminator */ }; -static struct regulator_consumer_supply overo_vmmc1_supply = { - .supply = "vmmc", +static struct regulator_consumer_supply overo_vmmc1_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"), }; #if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE) @@ -447,8 +449,8 @@ static struct regulator_init_data overo_vmmc1 = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &overo_vmmc1_supply, + .num_consumer_supplies = ARRAY_SIZE(overo_vmmc1_supply), + .consumer_supplies = overo_vmmc1_supply, }; /* VDAC for DSS driving S-Video (8 mA unloaded, max 65 mA) */ @@ -461,8 +463,8 @@ static struct regulator_init_data overo_vdac = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &overo_vdda_dac_supply, + .num_consumer_supplies = ARRAY_SIZE(overo_vdda_dac_supply), + .consumer_supplies = overo_vdda_dac_supply, }; /* VPLL2 for digital video outputs */ diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c index 990366726c5..5e559dda3cc 100644 --- a/arch/arm/mach-omap2/board-rx51-peripherals.c +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c @@ -358,14 +358,17 @@ static struct omap2_hsmmc_info mmc[] __initdata = { {} /* Terminator */ }; -static struct regulator_consumer_supply rx51_vmmc1_supply = - REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"); +static struct regulator_consumer_supply rx51_vmmc1_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"), +}; -static struct regulator_consumer_supply rx51_vaux3_supply = - REGULATOR_SUPPLY("vmmc", "omap_hsmmc.1"); +static struct regulator_consumer_supply rx51_vaux3_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.1"), +}; -static struct regulator_consumer_supply rx51_vsim_supply = - REGULATOR_SUPPLY("vmmc_aux", "omap_hsmmc.1"); +static struct regulator_consumer_supply rx51_vsim_supply[] = { + REGULATOR_SUPPLY("vmmc_aux", "omap_hsmmc.1"), +}; static struct regulator_consumer_supply rx51_vmmc2_supplies[] = { /* tlv320aic3x analog supplies */ @@ -452,8 +455,8 @@ static struct regulator_init_data rx51_vaux3_mmc = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &rx51_vaux3_supply, + .num_consumer_supplies = ARRAY_SIZE(rx51_vaux3_supply), + .consumer_supplies = rx51_vaux3_supply, }; static struct regulator_init_data rx51_vaux4 = { @@ -479,8 +482,8 @@ static struct regulator_init_data rx51_vmmc1 = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &rx51_vmmc1_supply, + .num_consumer_supplies = ARRAY_SIZE(rx51_vmmc1_supply), + .consumer_supplies = rx51_vmmc1_supply, }; static struct regulator_init_data rx51_vmmc2 = { @@ -511,8 +514,8 @@ static struct regulator_init_data rx51_vsim = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &rx51_vsim_supply, + .num_consumer_supplies = ARRAY_SIZE(rx51_vsim_supply), + .consumer_supplies = rx51_vsim_supply, }; static struct regulator_init_data rx51_vdac = { @@ -526,7 +529,7 @@ static struct regulator_init_data rx51_vdac = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, + .num_consumer_supplies = ARRAY_SIZE(rx51_vdac_supply), .consumer_supplies = rx51_vdac_supply, }; diff --git a/arch/arm/mach-omap2/board-zoom-peripherals.c b/arch/arm/mach-omap2/board-zoom-peripherals.c index 118c6f53c5e..cb012e16ba0 100644 --- a/arch/arm/mach-omap2/board-zoom-peripherals.c +++ b/arch/arm/mach-omap2/board-zoom-peripherals.c @@ -105,21 +105,20 @@ static struct twl4030_keypad_data zoom_kp_twl4030_data = { .rep = 1, }; -static struct regulator_consumer_supply zoom_vmmc1_supply = { - .supply = "vmmc", +static struct regulator_consumer_supply zoom_vmmc1_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"), }; -static struct regulator_consumer_supply zoom_vsim_supply = { - .supply = "vmmc_aux", +static struct regulator_consumer_supply zoom_vsim_supply[] = { + REGULATOR_SUPPLY("vmmc_aux", "omap_hsmmc.0"), }; -static struct regulator_consumer_supply zoom_vmmc2_supply = { - .supply = "vmmc", +static struct regulator_consumer_supply zoom_vmmc2_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.1"), }; -static struct regulator_consumer_supply zoom_vmmc3_supply = { - .supply = "vmmc", - .dev_name = "omap_hsmmc.2", +static struct regulator_consumer_supply zoom_vmmc3_supply[] = { + REGULATOR_SUPPLY("vmmc", "omap_hsmmc.2"), }; /* VMMC1 for OMAP VDD_MMC1 (i/o) and MMC1 card */ @@ -133,8 +132,8 @@ static struct regulator_init_data zoom_vmmc1 = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &zoom_vmmc1_supply, + .num_consumer_supplies = ARRAY_SIZE(zoom_vmmc1_supply), + .consumer_supplies = zoom_vmmc1_supply, }; /* VMMC2 for MMC2 card */ @@ -148,8 +147,8 @@ static struct regulator_init_data zoom_vmmc2 = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &zoom_vmmc2_supply, + .num_consumer_supplies = ARRAY_SIZE(zoom_vmmc2_supply), + .consumer_supplies = zoom_vmmc2_supply, }; /* VSIM for OMAP VDD_MMC1A (i/o for DAT4..DAT7) */ @@ -163,16 +162,16 @@ static struct regulator_init_data zoom_vsim = { | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &zoom_vsim_supply, + .num_consumer_supplies = ARRAY_SIZE(zoom_vsim_supply), + .consumer_supplies = zoom_vsim_supply, }; static struct regulator_init_data zoom_vmmc3 = { .constraints = { .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &zoom_vmmc3_supply, + .num_consumer_supplies = ARRAY_SIZE(zoom_vmmc3_supply), + .consumer_supplies = zoom_vmmc3_supply, }; static struct fixed_voltage_config zoom_vwlan = { @@ -232,8 +231,9 @@ static struct regulator_consumer_supply zoom_vpll2_supplies[] = { REGULATOR_SUPPLY("vdds_dsi", "omapdss_dsi1"), }; -static struct regulator_consumer_supply zoom_vdda_dac_supply = - REGULATOR_SUPPLY("vdda_dac", "omapdss_venc"); +static struct regulator_consumer_supply zoom_vdda_dac_supply[] = { + REGULATOR_SUPPLY("vdda_dac", "omapdss_venc"), +}; static struct regulator_init_data zoom_vpll2 = { .constraints = { @@ -257,8 +257,8 @@ static struct regulator_init_data zoom_vdac = { .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, }, - .num_consumer_supplies = 1, - .consumer_supplies = &zoom_vdda_dac_supply, + .num_consumer_supplies = ARRAY_SIZE(zoom_vdda_dac_supply), + .consumer_supplies = zoom_vdda_dac_supply, }; static int zoom_twl_gpio_setup(struct device *dev, -- cgit v1.2.3 From fd4a0286cecd3b1e15771e02bc36bd8494a4a1d8 Mon Sep 17 00:00:00 2001 From: Oleg Drokin Date: Mon, 6 Jun 2011 18:57:08 +0000 Subject: Remove old-style supply.dev assignments common in hsmmc init CC: Mark Brown CC: Mike Rapoport CC: Nishant Kamat CC: Steve Sakoman CC: Felipe Balbi Signed-off-by: Oleg Drokin Acked-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/board-cm-t35.c | 4 ---- arch/arm/mach-omap2/board-ldp.c | 2 -- arch/arm/mach-omap2/board-omap3beagle.c | 4 ---- arch/arm/mach-omap2/board-omap3evm.c | 4 ---- arch/arm/mach-omap2/board-omap3logic.c | 2 -- arch/arm/mach-omap2/board-omap3stalker.c | 4 ---- arch/arm/mach-omap2/board-omap3touchbook.c | 4 ---- arch/arm/mach-omap2/board-overo.c | 2 -- arch/arm/mach-omap2/board-zoom-peripherals.c | 7 ------- 9 files changed, 33 deletions(-) diff --git a/arch/arm/mach-omap2/board-cm-t35.c b/arch/arm/mach-omap2/board-cm-t35.c index e7bf32de753..ceb581e2b1d 100644 --- a/arch/arm/mach-omap2/board-cm-t35.c +++ b/arch/arm/mach-omap2/board-cm-t35.c @@ -483,10 +483,6 @@ static int cm_t35_twl_gpio_setup(struct device *dev, unsigned gpio, mmc[0].gpio_cd = gpio + 0; omap2_hsmmc_init(mmc); - /* link regulators to MMC adapters */ - cm_t35_vmmc1_supply.dev = mmc[0].dev; - cm_t35_vsim_supply.dev = mmc[0].dev; - return 0; } diff --git a/arch/arm/mach-omap2/board-ldp.c b/arch/arm/mach-omap2/board-ldp.c index 069bc9f0cf3..2d7e0aed605 100644 --- a/arch/arm/mach-omap2/board-ldp.c +++ b/arch/arm/mach-omap2/board-ldp.c @@ -341,8 +341,6 @@ static void __init omap_ldp_init(void) ARRAY_SIZE(ldp_nand_partitions), ZOOM_NAND_CS, 0); omap2_hsmmc_init(mmc); - /* link regulators to MMC adapters */ - ldp_vmmc1_supply.dev = mmc[0].dev; } MACHINE_START(OMAP_LDP, "OMAP LDP board") diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c index 4cf7c19ea0e..8ef0e19127a 100644 --- a/arch/arm/mach-omap2/board-omap3beagle.c +++ b/arch/arm/mach-omap2/board-omap3beagle.c @@ -268,10 +268,6 @@ static int beagle_twl_gpio_setup(struct device *dev, mmc[0].gpio_cd = gpio + 0; omap2_hsmmc_init(mmc); - /* link regulators to MMC adapters */ - beagle_vmmc1_supply.dev = mmc[0].dev; - beagle_vsim_supply.dev = mmc[0].dev; - /* * TWL4030_GPIO_MAX + 0 == ledA, EHCI nEN_USB_PWR (out, XM active * high / others active low) diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c index fc7a23add5f..e2202ddae66 100644 --- a/arch/arm/mach-omap2/board-omap3evm.c +++ b/arch/arm/mach-omap2/board-omap3evm.c @@ -365,10 +365,6 @@ static int omap3evm_twl_gpio_setup(struct device *dev, mmc[0].gpio_cd = gpio + 0; omap2_hsmmc_init(mmc); - /* link regulators to MMC adapters */ - omap3evm_vmmc1_supply.dev = mmc[0].dev; - omap3evm_vsim_supply.dev = mmc[0].dev; - /* * Most GPIOs are for USB OTG. Some are mostly sent to * the P2 connector; notably LEDA for the LCD backlight. diff --git a/arch/arm/mach-omap2/board-omap3logic.c b/arch/arm/mach-omap2/board-omap3logic.c index ec18435bc5d..eaefb591659 100644 --- a/arch/arm/mach-omap2/board-omap3logic.c +++ b/arch/arm/mach-omap2/board-omap3logic.c @@ -130,8 +130,6 @@ static void __init board_mmc_init(void) } omap2_hsmmc_init(board_mmc_info); - /* link regulators to MMC adapters */ - omap3logic_vmmc1_supply.dev = board_mmc_info[0].dev; } static struct omap_smsc911x_platform_data __initdata board_smsc911x_data = { diff --git a/arch/arm/mach-omap2/board-omap3stalker.c b/arch/arm/mach-omap2/board-omap3stalker.c index 99be540dbab..63d12a3df30 100644 --- a/arch/arm/mach-omap2/board-omap3stalker.c +++ b/arch/arm/mach-omap2/board-omap3stalker.c @@ -321,10 +321,6 @@ omap3stalker_twl_gpio_setup(struct device *dev, mmc[0].gpio_cd = gpio + 0; omap2_hsmmc_init(mmc); - /* link regulators to MMC adapters */ - omap3stalker_vmmc1_supply.dev = mmc[0].dev; - omap3stalker_vsim_supply.dev = mmc[0].dev; - /* * Most GPIOs are for USB OTG. Some are mostly sent to * the P2 connector; notably LEDA for the LCD backlight. diff --git a/arch/arm/mach-omap2/board-omap3touchbook.c b/arch/arm/mach-omap2/board-omap3touchbook.c index ab5c37d9540..c80e2c3a919 100644 --- a/arch/arm/mach-omap2/board-omap3touchbook.c +++ b/arch/arm/mach-omap2/board-omap3touchbook.c @@ -137,10 +137,6 @@ static int touchbook_twl_gpio_setup(struct device *dev, mmc[0].gpio_cd = gpio + 0; omap2_hsmmc_init(mmc); - /* link regulators to MMC adapters */ - touchbook_vmmc1_supply.dev = mmc[0].dev; - touchbook_vsim_supply.dev = mmc[0].dev; - /* REVISIT: need ehci-omap hooks for external VBUS * power switch and overcurrent detect */ diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c index 30c7556d251..031a9a68156 100644 --- a/arch/arm/mach-omap2/board-overo.c +++ b/arch/arm/mach-omap2/board-overo.c @@ -417,8 +417,6 @@ static int overo_twl_gpio_setup(struct device *dev, { omap2_hsmmc_init(mmc); - overo_vmmc1_supply.dev = mmc[0].dev; - #if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE) /* TWL4030_GPIO_MAX + 1 == ledB, PMU_STAT (out, active low LED) */ gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1; diff --git a/arch/arm/mach-omap2/board-zoom-peripherals.c b/arch/arm/mach-omap2/board-zoom-peripherals.c index cb012e16ba0..8495f82fcba 100644 --- a/arch/arm/mach-omap2/board-zoom-peripherals.c +++ b/arch/arm/mach-omap2/board-zoom-peripherals.c @@ -270,13 +270,6 @@ static int zoom_twl_gpio_setup(struct device *dev, mmc[0].gpio_cd = gpio + 0; omap2_hsmmc_init(mmc); - /* link regulators to MMC adapters ... we "know" the - * regulators will be set up only *after* we return. - */ - zoom_vmmc1_supply.dev = mmc[0].dev; - zoom_vsim_supply.dev = mmc[0].dev; - zoom_vmmc2_supply.dev = mmc[1].dev; - ret = gpio_request_one(LCD_PANEL_ENABLE_GPIO, GPIOF_OUT_INIT_LOW, "lcd enable"); if (ret) -- cgit v1.2.3 From f75f92d7684c8f2c831217fcf7306dee535c529b Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 23 May 2011 11:51:18 +0300 Subject: OMAP: DSS2: remove extra includes from include/video/omapdss.h omapdss.h included platform_device.h and atomic.h, neither of which is needed by omapdss.h. Remove those includes from omapdss.h, and fix the affected .c files which did not include platform_device.h even though they should. Signed-off-by: Tomi Valkeinen --- drivers/video/omap2/dss/dispc.c | 1 + drivers/video/omap2/dss/dss.c | 1 + drivers/video/omap2/dss/hdmi.c | 1 + drivers/video/omap2/dss/rfbi.c | 1 + include/video/omapdss.h | 2 -- 5 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index 7a9a2e7d968..a9eebd8e79f 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index d9489d5c4f0..d0b3f81b372 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -28,6 +28,7 @@ #include #include #include +#include #include