From fc3a1f04f5040255cbc086c419e4237f29f89f88 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 13 Dec 2011 18:34:01 +0100 Subject: [PATCH 01/33] gpio: add flags to export GPIOs when requesting Introduce new flags to automatically export GPIOs when using the convenience functions gpio_request_one() or gpio_request_array(). This eases support for custom boards where lots of GPIOs need to be exported for customer applications. Signed-off-by: Wolfram Sang Signed-off-by: Grant Likely --- Documentation/gpio.txt | 3 +++ drivers/gpio/gpiolib.c | 12 +++++++++++- include/linux/gpio.h | 5 +++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt index 620a07844e8c..e08a883de36e 100644 --- a/Documentation/gpio.txt +++ b/Documentation/gpio.txt @@ -322,6 +322,9 @@ where 'flags' is currently defined to specify the following properties: * GPIOF_OPEN_DRAIN - gpio pin is open drain type. * GPIOF_OPEN_SOURCE - gpio pin is open source type. + * GPIOF_EXPORT_DIR_FIXED - export gpio to sysfs, keep direction + * GPIOF_EXPORT_DIR_CHANGEABLE - also export, allow changing direction + since GPIOF_INIT_* are only valid when configured as output, so group valid combinations as: diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5a75510d66bb..566d0122d832 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1302,8 +1302,18 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) (flags & GPIOF_INIT_HIGH) ? 1 : 0); if (err) - gpio_free(gpio); + goto free_gpio; + if (flags & GPIOF_EXPORT) { + err = gpio_export(gpio, flags & GPIOF_EXPORT_CHANGEABLE); + if (err) + goto free_gpio; + } + + return 0; + + free_gpio: + gpio_free(gpio); return err; } EXPORT_SYMBOL_GPL(gpio_request_one); diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 6155ecf192b0..af511a682925 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -20,6 +20,11 @@ /* Gpio pin is open source */ #define GPIOF_OPEN_SOURCE (1 << 3) +#define GPIOF_EXPORT (1 << 2) +#define GPIOF_EXPORT_CHANGEABLE (1 << 3) +#define GPIOF_EXPORT_DIR_FIXED (GPIOF_EXPORT) +#define GPIOF_EXPORT_DIR_CHANGEABLE (GPIOF_EXPORT | GPIOF_EXPORT_CHANGEABLE) + /** * struct gpio - a structure describing a GPIO with configuration * @gpio: the GPIO number From 8302c7413814e26959f69d36a0dcc1f945573bc9 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 5 Apr 2012 12:15:15 +0300 Subject: [PATCH 02/33] gpio/langwell: use devm_* helpers to simplify probe Use devm_* helper functions where possible to make the error handling in the probe function simpler. Signed-off-by: Mika Westerberg Signed-off-by: Grant Likely --- drivers/gpio/gpio-langwell.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c index 00692e89ef87..0bea41b8a226 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -309,7 +309,7 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev, retval = pci_enable_device(pdev); if (retval) - goto done; + return retval; retval = pci_request_regions(pdev, "langwell_gpio"); if (retval) { @@ -331,18 +331,18 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev, /* get the register base from bar0 */ start = pci_resource_start(pdev, 0); len = pci_resource_len(pdev, 0); - base = ioremap_nocache(start, len); + base = devm_ioremap_nocache(&pdev->dev, start, len); if (!base) { dev_err(&pdev->dev, "error mapping bar0\n"); retval = -EFAULT; goto err3; } - lnw = kzalloc(sizeof(struct lnw_gpio), GFP_KERNEL); + lnw = devm_kzalloc(&pdev->dev, sizeof(struct lnw_gpio), GFP_KERNEL); if (!lnw) { dev_err(&pdev->dev, "can't allocate langwell_gpio chip data\n"); retval = -ENOMEM; - goto err4; + goto err3; } lnw->reg_base = base; lnw->irq_base = irq_base; @@ -361,7 +361,7 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev, retval = gpiochip_add(&lnw->chip); if (retval) { dev_err(&pdev->dev, "langwell gpiochip_add error %d\n", retval); - goto err5; + goto err3; } irq_set_handler_data(pdev->irq, lnw); irq_set_chained_handler(pdev->irq, lnw_irq_handler); @@ -376,16 +376,12 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev, pm_runtime_put_noidle(&pdev->dev); pm_runtime_allow(&pdev->dev); - goto done; -err5: - kfree(lnw); -err4: - iounmap(base); + return 0; + err3: pci_release_regions(pdev); err2: pci_disable_device(pdev); -done: return retval; } From b3e35af2b0ea9ad1618e01f40a1ffee83333ef35 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 5 Apr 2012 12:15:16 +0300 Subject: [PATCH 03/33] gpio/langwell: allocate IRQ descriptors dynamically for SPARSE_IRQ Since x86 is using SPARSE_IRQ by default nowadays it means that we need to allocate IRQ descriptors dynamically using irq_alloc_descs() otherwise the genirq code fails to convert our irq numbers to suitable descriptors. Signed-off-by: Mika Westerberg Signed-off-by: Grant Likely --- drivers/gpio/gpio-langwell.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c index 0bea41b8a226..bc15ae3d7cf2 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -306,6 +306,7 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev, u32 irq_base; u32 gpio_base; int retval = 0; + int ngpio = id->driver_data; retval = pci_enable_device(pdev); if (retval) @@ -344,8 +345,15 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev, retval = -ENOMEM; goto err3; } + + retval = irq_alloc_descs(-1, irq_base, ngpio, 0); + if (retval < 0) { + dev_err(&pdev->dev, "can't allocate IRQ descs\n"); + goto err3; + } + lnw->irq_base = retval; + lnw->reg_base = base; - lnw->irq_base = irq_base; lnw->chip.label = dev_name(&pdev->dev); lnw->chip.request = lnw_gpio_request; lnw->chip.direction_input = lnw_gpio_direction_input; @@ -354,14 +362,14 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev, 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.ngpio = ngpio; 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 err3; + goto err4; } irq_set_handler_data(pdev->irq, lnw); irq_set_chained_handler(pdev->irq, lnw_irq_handler); @@ -378,6 +386,8 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev, return 0; +err4: + irq_free_descs(lnw->irq_base, ngpio); err3: pci_release_regions(pdev); err2: From f5f93117f4ac24b8493cda67e6a1443517d26845 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 5 Apr 2012 12:15:17 +0300 Subject: [PATCH 04/33] gpio/langwell: clear IRQ edge detect registers at init The boot firmware might leave the registers configured causing interrupts to happen even when no handler for them is yet registered. Fix this by clearing the IRQ edge detect registers at init. Signed-off-by: Mika Westerberg Signed-off-by: Grant Likely --- drivers/gpio/gpio-langwell.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c index bc15ae3d7cf2..52f00d3cf667 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -263,6 +263,24 @@ static void lnw_irq_handler(unsigned irq, struct irq_desc *desc) chip->irq_eoi(data); } +static void lnw_irq_init_hw(struct lnw_gpio *lnw) +{ + void __iomem *reg; + unsigned base; + + for (base = 0; base < lnw->chip.ngpio; base += 32) { + /* Clear the rising-edge detect register */ + reg = gpio_reg(&lnw->chip, base, GRER); + writel(0, reg); + /* Clear the falling-edge detect register */ + reg = gpio_reg(&lnw->chip, base, GFER); + writel(0, reg); + /* Clear the edge detect status register */ + reg = gpio_reg(&lnw->chip, base, GEDR); + writel(~0, reg); + } +} + #ifdef CONFIG_PM static int lnw_gpio_runtime_resume(struct device *dev) { @@ -371,6 +389,9 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev, dev_err(&pdev->dev, "langwell gpiochip_add error %d\n", retval); goto err4; } + + lnw_irq_init_hw(lnw); + irq_set_handler_data(pdev->irq, lnw); irq_set_chained_handler(pdev->irq, lnw_irq_handler); for (i = 0; i < lnw->chip.ngpio; i++) { From 2c96922ae3f0bfb7324a7a433d96d319fe6de729 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 4 Apr 2012 16:14:48 +0100 Subject: [PATCH 05/33] gpiolib: Add !CONFIG_GPIOLIB definitions of devm_ functions Currently the managed gpio_request() and gpio_free() are not stubbed out for configurations not using gpiolib - do that to aid use in drivers. Signed-off-by: Mark Brown Signed-off-by: Grant Likely --- include/linux/gpio.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/linux/gpio.h b/include/linux/gpio.h index af511a682925..d1890d46b6ce 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -60,6 +60,12 @@ static inline int gpio_request(unsigned gpio, const char *label) return -ENOSYS; } +static inline int devm_gpio_request(struct device *dev, unsigned gpio, + const char *label) +{ + return -ENOSYS; +} + static inline int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) { @@ -79,6 +85,14 @@ static inline void gpio_free(unsigned gpio) WARN_ON(1); } +static inline void devm_gpio_free(struct device *dev, unsigned gpio) +{ + might_sleep(); + + /* GPIO can never have been requested */ + WARN_ON(1); +} + static inline void gpio_free_array(const struct gpio *array, size_t num) { might_sleep(); From c29985dd3e26118c8ba64105e09b85b714462765 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 6 Apr 2012 17:11:46 +0800 Subject: [PATCH 06/33] gpio/pca953x: Update GPIO_PCA953X Kconfig entry to include more supported devices The Kconfig description and help text doesn't list all of the devices supported by this driver. This patch adds the PCA957x devices. Signed-off-by: Axel Lin Signed-off-by: Grant Likely --- drivers/gpio/Kconfig | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index edadbdad31d0..e6862f134084 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -243,7 +243,7 @@ config GPIO_MC9S08DZ60 Select this to enable the MC9S08DZ60 GPIO driver config GPIO_PCA953X - tristate "PCA953x, PCA955x, TCA64xx, and MAX7310 I/O ports" + tristate "PCA953x, PCA955x, PCA957x, TCA64xx, and MAX7310 I/O ports" depends on I2C help Say yes here to provide access to several register-oriented @@ -252,10 +252,11 @@ config GPIO_PCA953X 4 bits: pca9536, pca9537 - 8 bits: max7310, pca9534, pca9538, pca9554, pca9557, - tca6408 + 8 bits: max7310, max7315, pca6107, pca9534, pca9538, pca9554, + pca9556, pca9557, pca9574, tca6408 - 16 bits: pca9535, pca9539, pca9555, tca6416 + 16 bits: max7312, max7313, pca9535, pca9539, pca9555, pca9575, + tca6416 config GPIO_PCA953X_IRQ bool "Interrupt controller support for PCA953x" From 93baa65fe50a83056c97973de2300337b000472e Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 6 Apr 2012 20:13:30 +0800 Subject: [PATCH 07/33] gpio: Convert drivers to use module_pci_driver() This patch converts the drivers in drivers/gpio/* to use module_pci_driver() macro which makes the code smaller and a bit simpler by having less boilerplate. Signed-off-by: Axel Lin Signed-off-by: Grant Likely --- drivers/gpio/gpio-bt8xx.c | 12 +----------- drivers/gpio/gpio-ml-ioh.c | 12 +----------- drivers/gpio/gpio-pch.c | 12 +----------- drivers/gpio/gpio-sodaville.c | 12 +----------- 4 files changed, 4 insertions(+), 44 deletions(-) diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c index 5ca4098ba092..e4cc7eb69bb2 100644 --- a/drivers/gpio/gpio-bt8xx.c +++ b/drivers/gpio/gpio-bt8xx.c @@ -328,17 +328,7 @@ static struct pci_driver bt8xxgpio_pci_driver = { .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_pci_driver(bt8xxgpio_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Michael Buesch"); diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index f0febe5b8221..db01f151d41c 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -611,17 +611,7 @@ static struct pci_driver ioh_gpio_driver = { .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_pci_driver(ioh_gpio_driver); MODULE_DESCRIPTION("OKI SEMICONDUCTOR ML-IOH series GPIO Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index e8729cc2ba2b..a05fdb6c464c 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -539,17 +539,7 @@ static struct pci_driver pch_gpio_driver = { .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_pci_driver(pch_gpio_driver); MODULE_DESCRIPTION("PCH GPIO PCI Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-sodaville.c b/drivers/gpio/gpio-sodaville.c index 9ba15d31d242..e20dc737dd4e 100644 --- a/drivers/gpio/gpio-sodaville.c +++ b/drivers/gpio/gpio-sodaville.c @@ -285,17 +285,7 @@ static struct pci_driver sdv_gpio_driver = { .remove = sdv_gpio_remove, }; -static int __init sdv_gpio_init(void) -{ - return pci_register_driver(&sdv_gpio_driver); -} -module_init(sdv_gpio_init); - -static void __exit sdv_gpio_exit(void) -{ - pci_unregister_driver(&sdv_gpio_driver); -} -module_exit(sdv_gpio_exit); +module_pci_driver(sdv_gpio_driver); MODULE_AUTHOR("Hans J. Koch "); MODULE_DESCRIPTION("GPIO interface for Intel Sodaville SoCs"); From f141ed65f256ec036c7fba604da6b7c448096ef9 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sat, 7 Apr 2012 14:31:33 -0600 Subject: [PATCH 08/33] gpio: Move DT support code into drivers/gpio The code in drivers/of/gpio.c isn't shared by any other subsystem since it is all gpiolib specific. drivers/gpio is a better place to maintain these functions. Signed-off-by: Grant Likely Cc: Rob Herring Cc: Linus Walleij --- drivers/gpio/Kconfig | 4 ++++ drivers/gpio/Makefile | 1 + drivers/{of/gpio.c => gpio/gpiolib-of.c} | 0 drivers/of/Kconfig | 6 ------ drivers/of/Makefile | 1 - 5 files changed, 5 insertions(+), 7 deletions(-) rename drivers/{of/gpio.c => gpio/gpiolib-of.c} (100%) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index e6862f134084..1042c3f534f4 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -37,6 +37,10 @@ menuconfig GPIOLIB if GPIOLIB +config OF_GPIO + def_bool y + depends on OF && !SPARC + config DEBUG_GPIO bool "Debug GPIO calls" depends on DEBUG_KERNEL diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 007f54bd0081..1c2f6c0a835e 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -3,6 +3,7 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG obj-$(CONFIG_GPIOLIB) += gpiolib.o devres.o +obj-$(CONFIG_OF_GPIO) += gpiolib-of.o # Device drivers. Generally keep list sorted alphabetically obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o diff --git a/drivers/of/gpio.c b/drivers/gpio/gpiolib-of.c similarity index 100% rename from drivers/of/gpio.c rename to drivers/gpio/gpiolib-of.c diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 8e84ce9765a9..ce00d11144de 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -51,12 +51,6 @@ config OF_IRQ config OF_DEVICE def_bool y -config OF_GPIO - def_bool y - depends on GPIOLIB && !SPARC - help - OpenFirmware GPIO accessors - config OF_I2C def_tristate I2C depends on I2C && !SPARC diff --git a/drivers/of/Makefile b/drivers/of/Makefile index aa90e602c8a7..aff2c6230390 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -4,7 +4,6 @@ obj-$(CONFIG_OF_PROMTREE) += pdt.o obj-$(CONFIG_OF_ADDRESS) += address.o obj-$(CONFIG_OF_IRQ) += irq.o obj-$(CONFIG_OF_DEVICE) += device.o platform.o -obj-$(CONFIG_OF_GPIO) += gpio.o obj-$(CONFIG_OF_I2C) += of_i2c.o obj-$(CONFIG_OF_NET) += of_net.o obj-$(CONFIG_OF_SPI) += of_spi.o From 465f2bd459c3143a4f93c2cf2de2c6ebb8f94947 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 2 May 2012 11:15:50 +0300 Subject: [PATCH 09/33] gpio/langwell: convert to use irq_domain irq_domain already provides a facility to translate from hardware IRQ numbers to Linux IRQ numbers so use that instead of open-coding the logic in the driver. Signed-off-by: Mika Westerberg Signed-off-by: Grant Likely --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-langwell.c | 50 +++++++++++++++++++++--------------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 7875c3f93662..8ee6d29c3714 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -404,6 +404,7 @@ config GPIO_BT8XX config GPIO_LANGWELL bool "Intel Langwell/Penwell GPIO support" depends on PCI && X86 + select IRQ_DOMAIN help Say Y here to support Intel Langwell/Penwell GPIO. diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c index 52f00d3cf667..b0673574dc70 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -36,6 +36,7 @@ #include #include #include +#include /* * Langwell chip has 64 pins and thus there are 2 32bit registers to control @@ -66,8 +67,8 @@ struct lnw_gpio { struct gpio_chip chip; void *reg_base; spinlock_t lock; - unsigned irq_base; struct pci_dev *pdev; + struct irq_domain *domain; }; static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset, @@ -176,13 +177,13 @@ static int lnw_gpio_direction_output(struct gpio_chip *chip, 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; + return irq_create_mapping(lnw->domain, 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; + u32 gpio = irqd_to_hwirq(d); unsigned long flags; u32 value; void __iomem *grer = gpio_reg(&lnw->chip, gpio, GRER); @@ -256,7 +257,8 @@ static void lnw_irq_handler(unsigned irq, struct irq_desc *desc) pending &= ~mask; /* Clear before handling so we can't lose an edge */ writel(mask, gedr); - generic_handle_irq(lnw->irq_base + base + gpio); + generic_handle_irq(irq_find_mapping(lnw->domain, + base + gpio)); } } @@ -281,6 +283,24 @@ static void lnw_irq_init_hw(struct lnw_gpio *lnw) } } +static int lnw_gpio_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + struct lnw_gpio *lnw = d->host_data; + + irq_set_chip_and_handler_name(virq, &lnw_irqchip, handle_simple_irq, + "demux"); + irq_set_chip_data(virq, lnw); + irq_set_irq_type(virq, IRQ_TYPE_NONE); + + return 0; +} + +static const struct irq_domain_ops lnw_gpio_irq_ops = { + .map = lnw_gpio_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + #ifdef CONFIG_PM static int lnw_gpio_runtime_resume(struct device *dev) { @@ -318,10 +338,8 @@ 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; int ngpio = id->driver_data; @@ -335,7 +353,7 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev, dev_err(&pdev->dev, "error requesting resources\n"); goto err2; } - /* get the irq_base from bar1 */ + /* get the gpio_base from bar1 */ start = pci_resource_start(pdev, 1); len = pci_resource_len(pdev, 1); base = ioremap_nocache(start, len); @@ -343,7 +361,6 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev, 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); @@ -364,12 +381,10 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev, goto err3; } - retval = irq_alloc_descs(-1, irq_base, ngpio, 0); - if (retval < 0) { - dev_err(&pdev->dev, "can't allocate IRQ descs\n"); + lnw->domain = irq_domain_add_linear(pdev->dev.of_node, ngpio, + &lnw_gpio_irq_ops, lnw); + if (!lnw->domain) goto err3; - } - lnw->irq_base = retval; lnw->reg_base = base; lnw->chip.label = dev_name(&pdev->dev); @@ -387,18 +402,13 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev, retval = gpiochip_add(&lnw->chip); if (retval) { dev_err(&pdev->dev, "langwell gpiochip_add error %d\n", retval); - goto err4; + goto err3; } lnw_irq_init_hw(lnw); 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); @@ -407,8 +417,6 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev, return 0; -err4: - irq_free_descs(lnw->irq_base, ngpio); err3: pci_release_regions(pdev); err2: From c8f925b69fec7d147cb22cbeec50fbcb2ec5580b Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 10 May 2012 13:01:22 +0300 Subject: [PATCH 10/33] gpio/langwell: re-read the IRQ status register after each iteration The IRQ status register should be re-read after each iteration. Otherwise the loop misses the interrupt if it gets raised immediately after handled. Reported-by: Grant Likely Signed-off-by: Mika Westerberg Acked-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpio-langwell.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c index b0673574dc70..a1c8754f52cf 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -250,11 +250,9 @@ static void lnw_irq_handler(unsigned irq, struct irq_desc *desc) /* 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) { + while ((pending = readl(gedr))) { gpio = __ffs(pending); mask = BIT(gpio); - pending &= ~mask; /* Clear before handling so we can't lose an edge */ writel(mask, gedr); generic_handle_irq(irq_find_mapping(lnw->domain, From fd454997d6873ef7ba668200f4278e006139187e Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Sun, 8 Apr 2012 12:23:27 -0700 Subject: [PATCH 11/33] gpio: samsung: refactor gpiolib init for exynos4/5 Only code move, no functional change. Main reason to do this was to get rid of the warnings: drivers/gpio/gpio-samsung.c: In function 'samsung_gpiolib_init': drivers/gpio/gpio-samsung.c:2974:1: warning: label 'err_ioremap4' defined but not used [-Wunused-label] drivers/gpio/gpio-samsung.c:2722:47: warning: unused variable 'gpio_base4' [-Wunused-variable] without adding more ifdef mess. I think this whole file would do well being coverted over to a platform driver and moving most of the tables out to SoC code and/or device trees, but since that changes init ordering it needs to be done with some care, i.e. not at this time. Signed-off-by: Olof Johansson Acked-by: Kukjin Kim Signed-off-by: Grant Likely --- drivers/gpio/gpio-samsung.c | 403 +++++++++++++++++++----------------- 1 file changed, 215 insertions(+), 188 deletions(-) diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index 19d6fc0229c3..5d49aff3bb9e 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -2714,12 +2714,224 @@ static __init void exynos_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip, } #endif /* defined(CONFIG_ARCH_EXYNOS) && defined(CONFIG_OF) */ +static __init void exynos4_gpiolib_init(void) +{ +#ifdef CONFIG_CPU_EXYNOS4210 + struct samsung_gpio_chip *chip; + int i, nr_chips; + void __iomem *gpio_base1, *gpio_base2, *gpio_base3; + int group = 0; + void __iomem *gpx_base; + + /* gpio part1 */ + gpio_base1 = ioremap(EXYNOS4_PA_GPIO1, SZ_4K); + if (gpio_base1 == NULL) { + pr_err("unable to ioremap for gpio_base1\n"); + goto err_ioremap1; + } + + chip = exynos4_gpios_1; + nr_chips = ARRAY_SIZE(exynos4_gpios_1); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &exynos_gpio_cfg; + chip->group = group++; + } + exynos_gpiolib_attach_ofnode(chip, + EXYNOS4_PA_GPIO1, i * 0x20); + } + samsung_gpiolib_add_4bit_chips(exynos4_gpios_1, + nr_chips, gpio_base1); + + /* gpio part2 */ + gpio_base2 = ioremap(EXYNOS4_PA_GPIO2, SZ_4K); + if (gpio_base2 == NULL) { + pr_err("unable to ioremap for gpio_base2\n"); + goto err_ioremap2; + } + + /* need to set base address for gpx */ + chip = &exynos4_gpios_2[16]; + gpx_base = gpio_base2 + 0xC00; + for (i = 0; i < 4; i++, chip++, gpx_base += 0x20) + chip->base = gpx_base; + + chip = exynos4_gpios_2; + nr_chips = ARRAY_SIZE(exynos4_gpios_2); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &exynos_gpio_cfg; + chip->group = group++; + } + exynos_gpiolib_attach_ofnode(chip, + EXYNOS4_PA_GPIO2, i * 0x20); + } + samsung_gpiolib_add_4bit_chips(exynos4_gpios_2, + nr_chips, gpio_base2); + + /* gpio part3 */ + gpio_base3 = ioremap(EXYNOS4_PA_GPIO3, SZ_256); + if (gpio_base3 == NULL) { + pr_err("unable to ioremap for gpio_base3\n"); + goto err_ioremap3; + } + + chip = exynos4_gpios_3; + nr_chips = ARRAY_SIZE(exynos4_gpios_3); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &exynos_gpio_cfg; + chip->group = group++; + } + exynos_gpiolib_attach_ofnode(chip, + EXYNOS4_PA_GPIO3, i * 0x20); + } + samsung_gpiolib_add_4bit_chips(exynos4_gpios_3, + nr_chips, gpio_base3); + +#if defined(CONFIG_CPU_EXYNOS4210) && defined(CONFIG_S5P_GPIO_INT) + s5p_register_gpioint_bank(IRQ_GPIO_XA, 0, IRQ_GPIO1_NR_GROUPS); + s5p_register_gpioint_bank(IRQ_GPIO_XB, IRQ_GPIO1_NR_GROUPS, IRQ_GPIO2_NR_GROUPS); +#endif + + return; + +err_ioremap3: + iounmap(gpio_base2); +err_ioremap2: + iounmap(gpio_base1); +err_ioremap1: + return; +#endif /* CONFIG_CPU_EXYNOS4210 */ +} + +static __init void exynos5_gpiolib_init(void) +{ +#ifdef CONFIG_SOC_EXYNOS5250 + struct samsung_gpio_chip *chip; + int i, nr_chips; + void __iomem *gpio_base1, *gpio_base2, *gpio_base3, *gpio_base4; + int group = 0; + void __iomem *gpx_base; + + /* gpio part1 */ + gpio_base1 = ioremap(EXYNOS5_PA_GPIO1, SZ_4K); + if (gpio_base1 == NULL) { + pr_err("unable to ioremap for gpio_base1\n"); + goto err_ioremap1; + } + + /* need to set base address for gpx */ + chip = &exynos5_gpios_1[20]; + gpx_base = gpio_base1 + 0xC00; + for (i = 0; i < 4; i++, chip++, gpx_base += 0x20) + chip->base = gpx_base; + + chip = exynos5_gpios_1; + nr_chips = ARRAY_SIZE(exynos5_gpios_1); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &exynos_gpio_cfg; + chip->group = group++; + } + exynos_gpiolib_attach_ofnode(chip, + EXYNOS5_PA_GPIO1, i * 0x20); + } + samsung_gpiolib_add_4bit_chips(exynos5_gpios_1, + nr_chips, gpio_base1); + + /* gpio part2 */ + gpio_base2 = ioremap(EXYNOS5_PA_GPIO2, SZ_4K); + if (gpio_base2 == NULL) { + pr_err("unable to ioremap for gpio_base2\n"); + goto err_ioremap2; + } + + chip = exynos5_gpios_2; + nr_chips = ARRAY_SIZE(exynos5_gpios_2); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &exynos_gpio_cfg; + chip->group = group++; + } + exynos_gpiolib_attach_ofnode(chip, + EXYNOS5_PA_GPIO2, i * 0x20); + } + samsung_gpiolib_add_4bit_chips(exynos5_gpios_2, + nr_chips, gpio_base2); + + /* gpio part3 */ + gpio_base3 = ioremap(EXYNOS5_PA_GPIO3, SZ_4K); + if (gpio_base3 == NULL) { + pr_err("unable to ioremap for gpio_base3\n"); + goto err_ioremap3; + } + + /* need to set base address for gpv */ + exynos5_gpios_3[0].base = gpio_base3; + exynos5_gpios_3[1].base = gpio_base3 + 0x20; + exynos5_gpios_3[2].base = gpio_base3 + 0x60; + exynos5_gpios_3[3].base = gpio_base3 + 0x80; + exynos5_gpios_3[4].base = gpio_base3 + 0xC0; + + chip = exynos5_gpios_3; + nr_chips = ARRAY_SIZE(exynos5_gpios_3); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &exynos_gpio_cfg; + chip->group = group++; + } + exynos_gpiolib_attach_ofnode(chip, + EXYNOS5_PA_GPIO3, i * 0x20); + } + samsung_gpiolib_add_4bit_chips(exynos5_gpios_3, + nr_chips, gpio_base3); + + /* gpio part4 */ + gpio_base4 = ioremap(EXYNOS5_PA_GPIO4, SZ_4K); + if (gpio_base4 == NULL) { + pr_err("unable to ioremap for gpio_base4\n"); + goto err_ioremap4; + } + + chip = exynos5_gpios_4; + nr_chips = ARRAY_SIZE(exynos5_gpios_4); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &exynos_gpio_cfg; + chip->group = group++; + } + exynos_gpiolib_attach_ofnode(chip, + EXYNOS5_PA_GPIO4, i * 0x20); + } + samsung_gpiolib_add_4bit_chips(exynos5_gpios_4, + nr_chips, gpio_base4); + return; + +err_ioremap4: + iounmap(gpio_base3); +err_ioremap3: + iounmap(gpio_base2); +err_ioremap2: + iounmap(gpio_base1); +err_ioremap1: + return; + +#endif /* CONFIG_SOC_EXYNOS5250 */ +} + /* TODO: cleanup soc_is_* */ static __init int samsung_gpiolib_init(void) { struct samsung_gpio_chip *chip; int i, nr_chips; - void __iomem *gpio_base1, *gpio_base2, *gpio_base3, *gpio_base4; int group = 0; samsung_gpiolib_set_cfg(samsung_gpio_cfgs, ARRAY_SIZE(samsung_gpio_cfgs)); @@ -2785,200 +2997,15 @@ static __init int samsung_gpiolib_init(void) s5p_register_gpioint_bank(IRQ_GPIOINT, 0, S5P_GPIOINT_GROUP_MAXNR); #endif } else if (soc_is_exynos4210()) { -#ifdef CONFIG_CPU_EXYNOS4210 - void __iomem *gpx_base; - - /* gpio part1 */ - gpio_base1 = ioremap(EXYNOS4_PA_GPIO1, SZ_4K); - if (gpio_base1 == NULL) { - pr_err("unable to ioremap for gpio_base1\n"); - goto err_ioremap1; - } - - chip = exynos4_gpios_1; - nr_chips = ARRAY_SIZE(exynos4_gpios_1); - - for (i = 0; i < nr_chips; i++, chip++) { - if (!chip->config) { - chip->config = &exynos_gpio_cfg; - chip->group = group++; - } - exynos_gpiolib_attach_ofnode(chip, - EXYNOS4_PA_GPIO1, i * 0x20); - } - samsung_gpiolib_add_4bit_chips(exynos4_gpios_1, - nr_chips, gpio_base1); - - /* gpio part2 */ - gpio_base2 = ioremap(EXYNOS4_PA_GPIO2, SZ_4K); - if (gpio_base2 == NULL) { - pr_err("unable to ioremap for gpio_base2\n"); - goto err_ioremap2; - } - - /* need to set base address for gpx */ - chip = &exynos4_gpios_2[16]; - gpx_base = gpio_base2 + 0xC00; - for (i = 0; i < 4; i++, chip++, gpx_base += 0x20) - chip->base = gpx_base; - - chip = exynos4_gpios_2; - nr_chips = ARRAY_SIZE(exynos4_gpios_2); - - for (i = 0; i < nr_chips; i++, chip++) { - if (!chip->config) { - chip->config = &exynos_gpio_cfg; - chip->group = group++; - } - exynos_gpiolib_attach_ofnode(chip, - EXYNOS4_PA_GPIO2, i * 0x20); - } - samsung_gpiolib_add_4bit_chips(exynos4_gpios_2, - nr_chips, gpio_base2); - - /* gpio part3 */ - gpio_base3 = ioremap(EXYNOS4_PA_GPIO3, SZ_256); - if (gpio_base3 == NULL) { - pr_err("unable to ioremap for gpio_base3\n"); - goto err_ioremap3; - } - - chip = exynos4_gpios_3; - nr_chips = ARRAY_SIZE(exynos4_gpios_3); - - for (i = 0; i < nr_chips; i++, chip++) { - if (!chip->config) { - chip->config = &exynos_gpio_cfg; - chip->group = group++; - } - exynos_gpiolib_attach_ofnode(chip, - EXYNOS4_PA_GPIO3, i * 0x20); - } - samsung_gpiolib_add_4bit_chips(exynos4_gpios_3, - nr_chips, gpio_base3); - -#if defined(CONFIG_CPU_EXYNOS4210) && defined(CONFIG_S5P_GPIO_INT) - s5p_register_gpioint_bank(IRQ_GPIO_XA, 0, IRQ_GPIO1_NR_GROUPS); - s5p_register_gpioint_bank(IRQ_GPIO_XB, IRQ_GPIO1_NR_GROUPS, IRQ_GPIO2_NR_GROUPS); -#endif - -#endif /* CONFIG_CPU_EXYNOS4210 */ + exynos4_gpiolib_init(); } else if (soc_is_exynos5250()) { -#ifdef CONFIG_SOC_EXYNOS5250 - void __iomem *gpx_base; - - /* gpio part1 */ - gpio_base1 = ioremap(EXYNOS5_PA_GPIO1, SZ_4K); - if (gpio_base1 == NULL) { - pr_err("unable to ioremap for gpio_base1\n"); - goto err_ioremap1; - } - - /* need to set base address for gpx */ - chip = &exynos5_gpios_1[20]; - gpx_base = gpio_base1 + 0xC00; - for (i = 0; i < 4; i++, chip++, gpx_base += 0x20) - chip->base = gpx_base; - - chip = exynos5_gpios_1; - nr_chips = ARRAY_SIZE(exynos5_gpios_1); - - for (i = 0; i < nr_chips; i++, chip++) { - if (!chip->config) { - chip->config = &exynos_gpio_cfg; - chip->group = group++; - } - exynos_gpiolib_attach_ofnode(chip, - EXYNOS5_PA_GPIO1, i * 0x20); - } - samsung_gpiolib_add_4bit_chips(exynos5_gpios_1, - nr_chips, gpio_base1); - - /* gpio part2 */ - gpio_base2 = ioremap(EXYNOS5_PA_GPIO2, SZ_4K); - if (gpio_base2 == NULL) { - pr_err("unable to ioremap for gpio_base2\n"); - goto err_ioremap2; - } - - chip = exynos5_gpios_2; - nr_chips = ARRAY_SIZE(exynos5_gpios_2); - - for (i = 0; i < nr_chips; i++, chip++) { - if (!chip->config) { - chip->config = &exynos_gpio_cfg; - chip->group = group++; - } - exynos_gpiolib_attach_ofnode(chip, - EXYNOS5_PA_GPIO2, i * 0x20); - } - samsung_gpiolib_add_4bit_chips(exynos5_gpios_2, - nr_chips, gpio_base2); - - /* gpio part3 */ - gpio_base3 = ioremap(EXYNOS5_PA_GPIO3, SZ_4K); - if (gpio_base3 == NULL) { - pr_err("unable to ioremap for gpio_base3\n"); - goto err_ioremap3; - } - - /* need to set base address for gpv */ - exynos5_gpios_3[0].base = gpio_base3; - exynos5_gpios_3[1].base = gpio_base3 + 0x20; - exynos5_gpios_3[2].base = gpio_base3 + 0x60; - exynos5_gpios_3[3].base = gpio_base3 + 0x80; - exynos5_gpios_3[4].base = gpio_base3 + 0xC0; - - chip = exynos5_gpios_3; - nr_chips = ARRAY_SIZE(exynos5_gpios_3); - - for (i = 0; i < nr_chips; i++, chip++) { - if (!chip->config) { - chip->config = &exynos_gpio_cfg; - chip->group = group++; - } - exynos_gpiolib_attach_ofnode(chip, - EXYNOS5_PA_GPIO3, i * 0x20); - } - samsung_gpiolib_add_4bit_chips(exynos5_gpios_3, - nr_chips, gpio_base3); - - /* gpio part4 */ - gpio_base4 = ioremap(EXYNOS5_PA_GPIO4, SZ_4K); - if (gpio_base4 == NULL) { - pr_err("unable to ioremap for gpio_base4\n"); - goto err_ioremap4; - } - - chip = exynos5_gpios_4; - nr_chips = ARRAY_SIZE(exynos5_gpios_4); - - for (i = 0; i < nr_chips; i++, chip++) { - if (!chip->config) { - chip->config = &exynos_gpio_cfg; - chip->group = group++; - } - exynos_gpiolib_attach_ofnode(chip, - EXYNOS5_PA_GPIO4, i * 0x20); - } - samsung_gpiolib_add_4bit_chips(exynos5_gpios_4, - nr_chips, gpio_base4); -#endif /* CONFIG_SOC_EXYNOS5250 */ + exynos5_gpiolib_init(); } else { WARN(1, "Unknown SoC in gpio-samsung, no GPIOs added\n"); return -ENODEV; } return 0; - -err_ioremap4: - iounmap(gpio_base3); -err_ioremap3: - iounmap(gpio_base2); -err_ioremap2: - iounmap(gpio_base1); -err_ioremap1: - return -ENOMEM; } core_initcall(samsung_gpiolib_init); From 25cf25073a4e1e0563c288908481f10f98acb19a Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Tue, 24 Apr 2012 11:02:49 +0100 Subject: [PATCH 12/33] gpio: add MSIC gpio driver Add gpio support for Intel MSIC chips found in Intel Medfield platforms. MSIC supports totally 24 GPIOs with 16 low voltage and 8 high voltage pins. Driver uses MSIC mfd interface for MSIC access. (Updated comment to indicate why locking is actually safe) Signed-off-by: Mathias Nyman Signed-off-by: Alan Cox Signed-off-by: Grant Likely --- drivers/gpio/Kconfig | 8 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-msic.c | 339 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 348 insertions(+) create mode 100644 drivers/gpio/gpio-msic.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8ee6d29c3714..5169a99e9f61 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -520,4 +520,12 @@ config GPIO_TPS65910 help Select this option to enable GPIO driver for the TPS65910 chip family. + +config GPIO_MSIC + bool "Intel MSIC mixed signal gpio support" + depends on MFD_INTEL_MSIC + help + Enable support for GPIO on intel MSIC controllers found in + intel MID devices + endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 1c2f6c0a835e..7862f49b4d05 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_GPIO_MCP23S08) += gpio-mcp23s08.o obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o +obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o obj-$(CONFIG_GPIO_MSM_V1) += gpio-msm-v1.o obj-$(CONFIG_GPIO_MSM_V2) += gpio-msm-v2.o obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c new file mode 100644 index 000000000000..71a838f44501 --- /dev/null +++ b/drivers/gpio/gpio-msic.c @@ -0,0 +1,339 @@ +/* + * Intel Medfield MSIC GPIO driver> + * Copyright (c) 2011, Intel Corporation. + * + * Author: Mathias Nyman + * Based on intel_pmic_gpio.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* the offset for the mapping of global gpio pin to irq */ +#define MSIC_GPIO_IRQ_OFFSET 0x100 + +#define MSIC_GPIO_DIR_IN 0 +#define MSIC_GPIO_DIR_OUT BIT(5) +#define MSIC_GPIO_TRIG_FALL BIT(1) +#define MSIC_GPIO_TRIG_RISE BIT(2) + +/* masks for msic gpio output GPIOxxxxCTLO registers */ +#define MSIC_GPIO_DIR_MASK BIT(5) +#define MSIC_GPIO_DRV_MASK BIT(4) +#define MSIC_GPIO_REN_MASK BIT(3) +#define MSIC_GPIO_RVAL_MASK (BIT(2) | BIT(1)) +#define MSIC_GPIO_DOUT_MASK BIT(0) + +/* masks for msic gpio input GPIOxxxxCTLI registers */ +#define MSIC_GPIO_GLBYP_MASK BIT(5) +#define MSIC_GPIO_DBNC_MASK (BIT(4) | BIT(3)) +#define MSIC_GPIO_INTCNT_MASK (BIT(2) | BIT(1)) +#define MSIC_GPIO_DIN_MASK BIT(0) + +#define MSIC_NUM_GPIO 24 + +struct msic_gpio { + struct platform_device *pdev; + struct mutex buslock; + struct gpio_chip chip; + int irq; + unsigned irq_base; + unsigned long trig_change_mask; + unsigned trig_type; +}; + +/* + * MSIC has 24 gpios, 16 low voltage (1.2-1.8v) and 8 high voltage (3v). + * Both the high and low voltage gpios are divided in two banks. + * GPIOs are numbered with GPIO0LV0 as gpio_base in the following order: + * GPIO0LV0..GPIO0LV7: low voltage, bank 0, gpio_base + * GPIO1LV0..GPIO1LV7: low voltage, bank 1, gpio_base + 8 + * GPIO0HV0..GPIO0HV3: high voltage, bank 0, gpio_base + 16 + * GPIO1HV0..GPIO1HV3: high voltage, bank 1, gpio_base + 20 + */ + +static int msic_gpio_to_ireg(unsigned offset) +{ + if (offset >= MSIC_NUM_GPIO) + return -EINVAL; + + if (offset < 8) + return INTEL_MSIC_GPIO0LV0CTLI - offset; + if (offset < 16) + return INTEL_MSIC_GPIO1LV0CTLI - offset + 8; + if (offset < 20) + return INTEL_MSIC_GPIO0HV0CTLI - offset + 16; + + return INTEL_MSIC_GPIO1HV0CTLI - offset + 20; +} + +static int msic_gpio_to_oreg(unsigned offset) +{ + if (offset >= MSIC_NUM_GPIO) + return -EINVAL; + + if (offset < 8) + return INTEL_MSIC_GPIO0LV0CTLO - offset; + if (offset < 16) + return INTEL_MSIC_GPIO1LV0CTLO - offset + 8; + if (offset < 20) + return INTEL_MSIC_GPIO0HV0CTLO - offset + 16; + + return INTEL_MSIC_GPIO1HV0CTLO + offset + 20; +} + +static int msic_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + int reg; + + reg = msic_gpio_to_oreg(offset); + if (reg < 0) + return reg; + + return intel_msic_reg_update(reg, MSIC_GPIO_DIR_IN, MSIC_GPIO_DIR_MASK); +} + +static int msic_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + int reg; + unsigned mask; + + value = (!!value) | MSIC_GPIO_DIR_OUT; + mask = MSIC_GPIO_DIR_MASK | MSIC_GPIO_DOUT_MASK; + + reg = msic_gpio_to_oreg(offset); + if (reg < 0) + return reg; + + return intel_msic_reg_update(reg, value, mask); +} + +static int msic_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + u8 r; + int ret; + int reg; + + reg = msic_gpio_to_ireg(offset); + if (reg < 0) + return reg; + + ret = intel_msic_reg_read(reg, &r); + if (ret < 0) + return ret; + + return r & MSIC_GPIO_DIN_MASK; +} + +static void msic_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + int reg; + + reg = msic_gpio_to_oreg(offset); + if (reg < 0) + return; + + intel_msic_reg_update(reg, !!value , MSIC_GPIO_DOUT_MASK); +} + +/* + * This is called from genirq with mg->buslock locked and + * irq_desc->lock held. We can not access the scu bus here, so we + * store the change and update in the bus_sync_unlock() function below + */ +static int msic_irq_type(struct irq_data *data, unsigned type) +{ + struct msic_gpio *mg = irq_data_get_irq_chip_data(data); + u32 gpio = data->irq - mg->irq_base; + + if (gpio >= mg->chip.ngpio) + return -EINVAL; + + /* mark for which gpio the trigger changed, protected by buslock */ + mg->trig_change_mask |= (1 << gpio); + mg->trig_type = type; + + return 0; +} + +static int msic_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct msic_gpio *mg = container_of(chip, struct msic_gpio, chip); + return mg->irq_base + offset; +} + +static void msic_bus_lock(struct irq_data *data) +{ + struct msic_gpio *mg = irq_data_get_irq_chip_data(data); + mutex_lock(&mg->buslock); +} + +static void msic_bus_sync_unlock(struct irq_data *data) +{ + struct msic_gpio *mg = irq_data_get_irq_chip_data(data); + int offset; + int reg; + u8 trig = 0; + + /* We can only get one change at a time as the buslock covers the + entire transaction. The irq_desc->lock is dropped before we are + called but that is fine */ + if (mg->trig_change_mask) { + offset = __ffs(mg->trig_change_mask); + + reg = msic_gpio_to_ireg(offset); + if (reg < 0) + goto out; + + if (mg->trig_type & IRQ_TYPE_EDGE_RISING) + trig |= MSIC_GPIO_TRIG_RISE; + if (mg->trig_type & IRQ_TYPE_EDGE_FALLING) + trig |= MSIC_GPIO_TRIG_FALL; + + intel_msic_reg_update(reg, trig, MSIC_GPIO_INTCNT_MASK); + mg->trig_change_mask = 0; + } +out: + mutex_unlock(&mg->buslock); +} + +/* Firmware does all the masking and unmasking for us, no masking here. */ +static void msic_irq_unmask(struct irq_data *data) { } + +static void msic_irq_mask(struct irq_data *data) { } + +static struct irq_chip msic_irqchip = { + .name = "MSIC-GPIO", + .irq_mask = msic_irq_mask, + .irq_unmask = msic_irq_unmask, + .irq_set_type = msic_irq_type, + .irq_bus_lock = msic_bus_lock, + .irq_bus_sync_unlock = msic_bus_sync_unlock, +}; + +static void msic_gpio_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct irq_data *data = irq_desc_get_irq_data(desc); + struct msic_gpio *mg = irq_data_get_irq_handler_data(data); + struct irq_chip *chip = irq_data_get_irq_chip(data); + struct intel_msic *msic = pdev_to_intel_msic(mg->pdev); + int i; + int bitnr; + u8 pin; + unsigned long pending = 0; + + for (i = 0; i < (mg->chip.ngpio / BITS_PER_BYTE); i++) { + intel_msic_irq_read(msic, INTEL_MSIC_GPIO0LVIRQ + i, &pin); + pending = pin; + + if (pending) { + for_each_set_bit(bitnr, &pending, BITS_PER_BYTE) + generic_handle_irq(mg->irq_base + + (i * BITS_PER_BYTE) + bitnr); + } + } + chip->irq_eoi(data); +} + +static int __devinit platform_msic_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct intel_msic_gpio_pdata *pdata = dev->platform_data; + struct msic_gpio *mg; + int irq = platform_get_irq(pdev, 0); + int retval; + int i; + + if (irq < 0) { + dev_err(dev, "no IRQ line\n"); + return -EINVAL; + } + + if (!pdata || !pdata->gpio_base) { + dev_err(dev, "incorrect or missing platform data\n"); + return -EINVAL; + } + + mg = kzalloc(sizeof(*mg), GFP_KERNEL); + if (!mg) + return -ENOMEM; + + dev_set_drvdata(dev, mg); + + mg->pdev = pdev; + mg->irq = irq; + mg->irq_base = pdata->gpio_base + MSIC_GPIO_IRQ_OFFSET; + mg->chip.label = "msic_gpio"; + mg->chip.direction_input = msic_gpio_direction_input; + mg->chip.direction_output = msic_gpio_direction_output; + mg->chip.get = msic_gpio_get; + mg->chip.set = msic_gpio_set; + mg->chip.to_irq = msic_gpio_to_irq; + mg->chip.base = pdata->gpio_base; + mg->chip.ngpio = MSIC_NUM_GPIO; + mg->chip.can_sleep = 1; + mg->chip.dev = dev; + + mutex_init(&mg->buslock); + + retval = gpiochip_add(&mg->chip); + if (retval) { + dev_err(dev, "Adding MSIC gpio chip failed\n"); + goto err; + } + + for (i = 0; i < mg->chip.ngpio; i++) { + irq_set_chip_data(i + mg->irq_base, mg); + irq_set_chip_and_handler_name(i + mg->irq_base, + &msic_irqchip, + handle_simple_irq, + "demux"); + } + irq_set_chained_handler(mg->irq, msic_gpio_irq_handler); + irq_set_handler_data(mg->irq, mg); + + return 0; +err: + kfree(mg); + return retval; +} + +static struct platform_driver platform_msic_gpio_driver = { + .driver = { + .name = "msic_gpio", + .owner = THIS_MODULE, + }, + .probe = platform_msic_gpio_probe, +}; + +static int __init platform_msic_gpio_init(void) +{ + return platform_driver_register(&platform_msic_gpio_driver); +} + +subsys_initcall(platform_msic_gpio_init); + +MODULE_AUTHOR("Mathias Nyman "); +MODULE_DESCRIPTION("Intel Medfield MSIC GPIO driver"); +MODULE_LICENSE("GPL v2"); From 7563bbf89d065a2c3f05059ecbcc805645edcc62 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 15 Apr 2012 10:52:54 +0100 Subject: [PATCH 13/33] gpiolib/arches: Centralise bolierplate asm/gpio.h Rather than requiring architectures that use gpiolib but don't have any need to define anything custom to copy an asm/gpio.h provide a Kconfig symbol which architectures must select in order to include gpio.h and for other architectures just provide the trivial implementation directly. This makes it much easier to do gpiolib updates and is also a step towards making gpiolib APIs available on every architecture. For architectures with existing boilerplate code leave a stub header in place which warns on direct inclusion of asm/gpio.h and includes linux/gpio.h to catch code that's doing this. Direct inclusion of asm/gpio.h has long been deprecated. Signed-off-by: Mark Brown Acked-by: Jonas Bonn Acked-by: Tony Luck Acked-by: Linus Walleij Signed-off-by: Grant Likely --- arch/alpha/include/asm/gpio.h | 59 ++----------------------- arch/arm/Kconfig | 1 + arch/avr32/Kconfig | 1 + arch/blackfin/Kconfig | 1 + arch/ia64/include/asm/gpio.h | 59 ++----------------------- arch/m68k/Kconfig.cpu | 1 + arch/microblaze/include/asm/gpio.h | 57 ++---------------------- arch/mips/Kconfig | 1 + arch/openrisc/include/asm/gpio.h | 69 ++---------------------------- arch/powerpc/include/asm/gpio.h | 57 ++---------------------- arch/sh/Kconfig | 1 + arch/sparc/include/asm/gpio.h | 40 ++--------------- arch/unicore32/Kconfig | 1 + arch/x86/include/asm/gpio.h | 57 ++---------------------- arch/xtensa/include/asm/gpio.h | 60 ++------------------------ drivers/gpio/Kconfig | 8 ++++ include/linux/gpio.h | 34 +++++++++++++++ 17 files changed, 81 insertions(+), 426 deletions(-) diff --git a/arch/alpha/include/asm/gpio.h b/arch/alpha/include/asm/gpio.h index 7dc6a6343c06..b3799d88ffcf 100644 --- a/arch/alpha/include/asm/gpio.h +++ b/arch/alpha/include/asm/gpio.h @@ -1,55 +1,4 @@ -/* - * Generic GPIO API implementation for Alpha. - * - * A stright copy of that for PowerPC which was: - * - * Copyright (c) 2007-2008 MontaVista Software, Inc. - * - * Author: 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. - */ - -#ifndef _ASM_ALPHA_GPIO_H -#define _ASM_ALPHA_GPIO_H - -#include -#include - -#ifdef CONFIG_GPIOLIB - -/* - * We don't (yet) implement inlined/rapid versions for on-chip gpios. - * Just call gpiolib. - */ -static inline int gpio_get_value(unsigned int gpio) -{ - return __gpio_get_value(gpio); -} - -static inline void gpio_set_value(unsigned int gpio, int value) -{ - __gpio_set_value(gpio, value); -} - -static inline int gpio_cansleep(unsigned int gpio) -{ - return __gpio_cansleep(gpio); -} - -static inline int gpio_to_irq(unsigned int gpio) -{ - return __gpio_to_irq(gpio); -} - -static inline int irq_to_gpio(unsigned int irq) -{ - return -EINVAL; -} - -#endif /* CONFIG_GPIOLIB */ - -#endif /* _ASM_ALPHA_GPIO_H */ +#ifndef __LINUX_GPIO_H +#warning Include linux/gpio.h instead of asm/gpio.h +#include +#endif diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 36586dba6fa6..777025e773d9 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1,6 +1,7 @@ config ARM bool default y + select ARCH_HAVE_CUSTOM_GPIO_H select HAVE_AOUT select HAVE_DMA_API_DEBUG select HAVE_IDE if PCI || ISA || PCMCIA diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig index 3dea7231f637..859b2de4a624 100644 --- a/arch/avr32/Kconfig +++ b/arch/avr32/Kconfig @@ -11,6 +11,7 @@ config AVR32 select GENERIC_ATOMIC64 select HARDIRQS_SW_RESEND select GENERIC_IRQ_SHOW + select ARCH_HAVE_CUSTOM_GPIO_H select ARCH_HAVE_NMI_SAFE_CMPXCHG help AVR32 is a high-performance 32-bit RISC microprocessor core, diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index 373a6902d8fa..bf3d80f9738b 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -31,6 +31,7 @@ config BLACKFIN select HAVE_KERNEL_LZO if RAMKERNEL select HAVE_OPROFILE select HAVE_PERF_EVENTS + select ARCH_HAVE_CUSTOM_GPIO_H select ARCH_WANT_OPTIONAL_GPIOLIB select HAVE_GENERIC_HARDIRQS select GENERIC_ATOMIC64 diff --git a/arch/ia64/include/asm/gpio.h b/arch/ia64/include/asm/gpio.h index 590a20debc4e..b3799d88ffcf 100644 --- a/arch/ia64/include/asm/gpio.h +++ b/arch/ia64/include/asm/gpio.h @@ -1,55 +1,4 @@ -/* - * Generic GPIO API implementation for IA-64. - * - * A stright copy of that for PowerPC which was: - * - * Copyright (c) 2007-2008 MontaVista Software, Inc. - * - * Author: 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. - */ - -#ifndef _ASM_IA64_GPIO_H -#define _ASM_IA64_GPIO_H - -#include -#include - -#ifdef CONFIG_GPIOLIB - -/* - * We don't (yet) implement inlined/rapid versions for on-chip gpios. - * Just call gpiolib. - */ -static inline int gpio_get_value(unsigned int gpio) -{ - return __gpio_get_value(gpio); -} - -static inline void gpio_set_value(unsigned int gpio, int value) -{ - __gpio_set_value(gpio, value); -} - -static inline int gpio_cansleep(unsigned int gpio) -{ - return __gpio_cansleep(gpio); -} - -static inline int gpio_to_irq(unsigned int gpio) -{ - return __gpio_to_irq(gpio); -} - -static inline int irq_to_gpio(unsigned int irq) -{ - return -EINVAL; -} - -#endif /* CONFIG_GPIOLIB */ - -#endif /* _ASM_IA64_GPIO_H */ +#ifndef __LINUX_GPIO_H +#warning Include linux/gpio.h instead of asm/gpio.h +#include +#endif diff --git a/arch/m68k/Kconfig.cpu b/arch/m68k/Kconfig.cpu index 8a9c767125a4..8941af1d3ad2 100644 --- a/arch/m68k/Kconfig.cpu +++ b/arch/m68k/Kconfig.cpu @@ -24,6 +24,7 @@ config COLDFIRE bool "Coldfire CPU family support" select GENERIC_GPIO select ARCH_REQUIRE_GPIOLIB + select ARCH_HAVE_CUSTOM_GPIO_H select CPU_HAS_NO_BITFIELDS select CPU_HAS_NO_MULDIV64 select GENERIC_CSUM diff --git a/arch/microblaze/include/asm/gpio.h b/arch/microblaze/include/asm/gpio.h index 2b2c18be71c6..b3799d88ffcf 100644 --- a/arch/microblaze/include/asm/gpio.h +++ b/arch/microblaze/include/asm/gpio.h @@ -1,53 +1,4 @@ -/* - * Generic GPIO API implementation for PowerPC. - * - * Copyright (c) 2007-2008 MontaVista Software, Inc. - * - * Author: 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. - */ - -#ifndef _ASM_MICROBLAZE_GPIO_H -#define _ASM_MICROBLAZE_GPIO_H - -#include -#include - -#ifdef CONFIG_GPIOLIB - -/* - * We don't (yet) implement inlined/rapid versions for on-chip gpios. - * Just call gpiolib. - */ -static inline int gpio_get_value(unsigned int gpio) -{ - return __gpio_get_value(gpio); -} - -static inline void gpio_set_value(unsigned int gpio, int value) -{ - __gpio_set_value(gpio, value); -} - -static inline int gpio_cansleep(unsigned int gpio) -{ - return __gpio_cansleep(gpio); -} - -static inline int gpio_to_irq(unsigned int gpio) -{ - return __gpio_to_irq(gpio); -} - -static inline int irq_to_gpio(unsigned int irq) -{ - return -EINVAL; -} - -#endif /* CONFIG_GPIOLIB */ - -#endif /* _ASM_MICROBLAZE_GPIO_H */ +#ifndef __LINUX_GPIO_H +#warning Include linux/gpio.h instead of asm/gpio.h +#include +#endif diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index ce30e2f91d77..63321b283fe4 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -8,6 +8,7 @@ config MIPS select HAVE_PERF_EVENTS select PERF_USE_VMALLOC select HAVE_ARCH_KGDB + select ARCH_HAVE_CUSTOM_GPIO_H select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_TRACE_MCOUNT_TEST select HAVE_DYNAMIC_FTRACE diff --git a/arch/openrisc/include/asm/gpio.h b/arch/openrisc/include/asm/gpio.h index 0b0d174f47cd..b3799d88ffcf 100644 --- a/arch/openrisc/include/asm/gpio.h +++ b/arch/openrisc/include/asm/gpio.h @@ -1,65 +1,4 @@ -/* - * OpenRISC Linux - * - * Linux architectural port borrowing liberally from similar works of - * others. All original copyrights apply as per the original source - * declaration. - * - * OpenRISC implementation: - * Copyright (C) 2003 Matjaz Breskvar - * Copyright (C) 2010-2011 Jonas Bonn - * et al. - * - * 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. - */ - -#ifndef __ASM_OPENRISC_GPIO_H -#define __ASM_OPENRISC_GPIO_H - -#include -#include - -#ifdef CONFIG_GPIOLIB - -/* - * OpenRISC (or1k) does not have on-chip GPIO's so there is not really - * any standardized implementation that makes sense here. If passing - * through gpiolib becomes a bottleneck then it may make sense, on a - * case-by-case basis, to implement these inlined/rapid versions. - * - * Just call gpiolib. - */ -static inline int gpio_get_value(unsigned int gpio) -{ - return __gpio_get_value(gpio); -} - -static inline void gpio_set_value(unsigned int gpio, int value) -{ - __gpio_set_value(gpio, value); -} - -static inline int gpio_cansleep(unsigned int gpio) -{ - return __gpio_cansleep(gpio); -} - -/* - * Not implemented, yet. - */ -static inline int gpio_to_irq(unsigned int gpio) -{ - return -ENOSYS; -} - -static inline int irq_to_gpio(unsigned int irq) -{ - return -EINVAL; -} - -#endif /* CONFIG_GPIOLIB */ - -#endif /* __ASM_OPENRISC_GPIO_H */ +#ifndef __LINUX_GPIO_H +#warning Include linux/gpio.h instead of asm/gpio.h +#include +#endif diff --git a/arch/powerpc/include/asm/gpio.h b/arch/powerpc/include/asm/gpio.h index 38762edb5e58..b3799d88ffcf 100644 --- a/arch/powerpc/include/asm/gpio.h +++ b/arch/powerpc/include/asm/gpio.h @@ -1,53 +1,4 @@ -/* - * Generic GPIO API implementation for PowerPC. - * - * Copyright (c) 2007-2008 MontaVista Software, Inc. - * - * Author: 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. - */ - -#ifndef __ASM_POWERPC_GPIO_H -#define __ASM_POWERPC_GPIO_H - -#include -#include - -#ifdef CONFIG_GPIOLIB - -/* - * We don't (yet) implement inlined/rapid versions for on-chip gpios. - * Just call gpiolib. - */ -static inline int gpio_get_value(unsigned int gpio) -{ - return __gpio_get_value(gpio); -} - -static inline void gpio_set_value(unsigned int gpio, int value) -{ - __gpio_set_value(gpio, value); -} - -static inline int gpio_cansleep(unsigned int gpio) -{ - return __gpio_cansleep(gpio); -} - -static inline int gpio_to_irq(unsigned int gpio) -{ - return __gpio_to_irq(gpio); -} - -static inline int irq_to_gpio(unsigned int irq) -{ - return -EINVAL; -} - -#endif /* CONFIG_GPIOLIB */ - -#endif /* __ASM_POWERPC_GPIO_H */ +#ifndef __LINUX_GPIO_H +#warning Include linux/gpio.h instead of asm/gpio.h +#include +#endif diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index ff9e033ce626..c40b29ac3644 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -13,6 +13,7 @@ config SUPERH select HAVE_DMA_ATTRS select HAVE_IRQ_WORK select HAVE_PERF_EVENTS + select ARCH_HAVE_CUSTOM_GPIO_H select ARCH_HAVE_NMI_SAFE_CMPXCHG if (GUSA_RB || CPU_SH4A) select PERF_USE_VMALLOC select HAVE_KERNEL_GZIP diff --git a/arch/sparc/include/asm/gpio.h b/arch/sparc/include/asm/gpio.h index a0e3ac0af599..b3799d88ffcf 100644 --- a/arch/sparc/include/asm/gpio.h +++ b/arch/sparc/include/asm/gpio.h @@ -1,36 +1,4 @@ -#ifndef __ASM_SPARC_GPIO_H -#define __ASM_SPARC_GPIO_H - -#include -#include - -#ifdef CONFIG_GPIOLIB - -static inline int gpio_get_value(unsigned int gpio) -{ - return __gpio_get_value(gpio); -} - -static inline void gpio_set_value(unsigned int gpio, int value) -{ - __gpio_set_value(gpio, value); -} - -static inline int gpio_cansleep(unsigned int gpio) -{ - return __gpio_cansleep(gpio); -} - -static inline int gpio_to_irq(unsigned int gpio) -{ - return -ENOSYS; -} - -static inline int irq_to_gpio(unsigned int irq) -{ - return -EINVAL; -} - -#endif /* CONFIG_GPIOLIB */ - -#endif /* __ASM_SPARC_GPIO_H */ +#ifndef __LINUX_GPIO_H +#warning Include linux/gpio.h instead of asm/gpio.h +#include +#endif diff --git a/arch/unicore32/Kconfig b/arch/unicore32/Kconfig index eeb8054c7cd8..7ff6d10c0be2 100644 --- a/arch/unicore32/Kconfig +++ b/arch/unicore32/Kconfig @@ -8,6 +8,7 @@ config UNICORE32 select HAVE_KERNEL_BZIP2 select HAVE_KERNEL_LZO select HAVE_KERNEL_LZMA + select ARCH_HAVE_CUSTOM_GPIO_H select GENERIC_FIND_FIRST_BIT select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW diff --git a/arch/x86/include/asm/gpio.h b/arch/x86/include/asm/gpio.h index 91d915a65259..b3799d88ffcf 100644 --- a/arch/x86/include/asm/gpio.h +++ b/arch/x86/include/asm/gpio.h @@ -1,53 +1,4 @@ -/* - * Generic GPIO API implementation for x86. - * - * Derived from the generic GPIO API for powerpc: - * - * Copyright (c) 2007-2008 MontaVista Software, Inc. - * - * Author: 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. - */ - -#ifndef _ASM_X86_GPIO_H -#define _ASM_X86_GPIO_H - -#include - -#ifdef CONFIG_GPIOLIB - -/* - * Just call gpiolib. - */ -static inline int gpio_get_value(unsigned int gpio) -{ - return __gpio_get_value(gpio); -} - -static inline void gpio_set_value(unsigned int gpio, int value) -{ - __gpio_set_value(gpio, value); -} - -static inline int gpio_cansleep(unsigned int gpio) -{ - return __gpio_cansleep(gpio); -} - -static inline int gpio_to_irq(unsigned int gpio) -{ - return __gpio_to_irq(gpio); -} - -static inline int irq_to_gpio(unsigned int irq) -{ - return -EINVAL; -} - -#endif /* CONFIG_GPIOLIB */ - -#endif /* _ASM_X86_GPIO_H */ +#ifndef __LINUX_GPIO_H +#warning Include linux/gpio.h instead of asm/gpio.h +#include +#endif diff --git a/arch/xtensa/include/asm/gpio.h b/arch/xtensa/include/asm/gpio.h index a8c9fc46c790..b3799d88ffcf 100644 --- a/arch/xtensa/include/asm/gpio.h +++ b/arch/xtensa/include/asm/gpio.h @@ -1,56 +1,4 @@ -/* - * Generic GPIO API implementation for xtensa. - * - * Stolen from x86, which is derived from the generic GPIO API for powerpc: - * - * Copyright (c) 2007-2008 MontaVista Software, Inc. - * - * Author: 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. - */ - -#ifndef _ASM_XTENSA_GPIO_H -#define _ASM_XTENSA_GPIO_H - -#include - -#ifdef CONFIG_GPIOLIB - -/* - * Just call gpiolib. - */ -static inline int gpio_get_value(unsigned int gpio) -{ - return __gpio_get_value(gpio); -} - -static inline void gpio_set_value(unsigned int gpio, int value) -{ - __gpio_set_value(gpio, value); -} - -static inline int gpio_cansleep(unsigned int gpio) -{ - return __gpio_cansleep(gpio); -} - -static inline int gpio_to_irq(unsigned int gpio) -{ - return __gpio_to_irq(gpio); -} - -/* - * Not implemented, yet. - */ -static inline int irq_to_gpio(unsigned int irq) -{ - return -EINVAL; -} - -#endif /* CONFIG_GPIOLIB */ - -#endif /* _ASM_XTENSA_GPIO_H */ +#ifndef __LINUX_GPIO_H +#warning Include linux/gpio.h instead of asm/gpio.h +#include +#endif diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5169a99e9f61..25535ebf4f90 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -2,6 +2,14 @@ # GPIO infrastructure and drivers # +config ARCH_HAVE_CUSTOM_GPIO_H + bool + help + Selecting this config option from the architecture Kconfig allows + the architecture to provide a custom asm/gpio.h implementation + overriding the default implementations. New uses of this are + strongly discouraged. + config ARCH_WANT_OPTIONAL_GPIOLIB bool help diff --git a/include/linux/gpio.h b/include/linux/gpio.h index d1890d46b6ce..7a8816a1a0d8 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -1,6 +1,8 @@ #ifndef __LINUX_GPIO_H #define __LINUX_GPIO_H +#include + /* see Documentation/gpio.txt */ /* make these flag values available regardless of GPIO kconfig options */ @@ -38,7 +40,39 @@ struct gpio { }; #ifdef CONFIG_GENERIC_GPIO + +#ifdef CONFIG_ARCH_HAVE_CUSTOM_GPIO_H #include +#else + +#include + +static inline int gpio_get_value(unsigned int gpio) +{ + return __gpio_get_value(gpio); +} + +static inline void gpio_set_value(unsigned int gpio, int value) +{ + __gpio_set_value(gpio, value); +} + +static inline int gpio_cansleep(unsigned int gpio) +{ + return __gpio_cansleep(gpio); +} + +static inline int gpio_to_irq(unsigned int gpio) +{ + return __gpio_to_irq(gpio); +} + +static inline int irq_to_gpio(unsigned int irq) +{ + return -EINVAL; +} + +#endif #else From d13710a7e6241f1725da34004928b93a71b05508 Mon Sep 17 00:00:00 2001 From: Tarun Kanti DebBarma Date: Fri, 27 Apr 2012 19:43:31 +0530 Subject: [PATCH 14/33] gpio/omap: remove virtual_irq_start variable This cleanup got missed while implementing following: 25db711 gpio/omap: Fix IRQ handling for SPARSE_IRQ 384ebe1 gpio/omap: Add DT support to GPIO driver Cc: Kevin Hilman Cc: Tony Lindgren Cc: Santosh Shilimkar Cc: Cousson, Benoit Cc: Grant Likely Signed-off-by: Tarun Kanti DebBarma Reviewed-by: Santosh Shilimkar Tested-by: Govindraj.R Signed-off-by: Kevin Hilman --- arch/arm/mach-omap1/gpio15xx.c | 2 -- arch/arm/mach-omap1/gpio16xx.c | 5 ----- arch/arm/mach-omap1/gpio7xx.c | 7 ------- arch/arm/mach-omap2/gpio.c | 1 - arch/arm/plat-omap/include/plat/gpio.h | 1 - 5 files changed, 16 deletions(-) diff --git a/arch/arm/mach-omap1/gpio15xx.c b/arch/arm/mach-omap1/gpio15xx.c index 634903ef8292..ebef15e5e7b7 100644 --- a/arch/arm/mach-omap1/gpio15xx.c +++ b/arch/arm/mach-omap1/gpio15xx.c @@ -46,7 +46,6 @@ static struct omap_gpio_reg_offs omap15xx_mpuio_regs = { }; static struct __initdata omap_gpio_platform_data omap15xx_mpu_gpio_config = { - .virtual_irq_start = IH_MPUIO_BASE, .is_mpuio = true, .bank_width = 16, .bank_stride = 1, @@ -89,7 +88,6 @@ static struct omap_gpio_reg_offs omap15xx_gpio_regs = { }; static struct __initdata omap_gpio_platform_data omap15xx_gpio_config = { - .virtual_irq_start = IH_GPIO_BASE, .bank_width = 16, .regs = &omap15xx_gpio_regs, }; diff --git a/arch/arm/mach-omap1/gpio16xx.c b/arch/arm/mach-omap1/gpio16xx.c index 1fb3b9ad496e..2a48cd2e1754 100644 --- a/arch/arm/mach-omap1/gpio16xx.c +++ b/arch/arm/mach-omap1/gpio16xx.c @@ -52,7 +52,6 @@ static struct omap_gpio_reg_offs omap16xx_mpuio_regs = { }; static struct __initdata omap_gpio_platform_data omap16xx_mpu_gpio_config = { - .virtual_irq_start = IH_MPUIO_BASE, .is_mpuio = true, .bank_width = 16, .bank_stride = 1, @@ -99,7 +98,6 @@ static struct omap_gpio_reg_offs omap16xx_gpio_regs = { }; static struct __initdata omap_gpio_platform_data omap16xx_gpio1_config = { - .virtual_irq_start = IH_GPIO_BASE, .bank_width = 16, .regs = &omap16xx_gpio_regs, }; @@ -128,7 +126,6 @@ static struct __initdata resource omap16xx_gpio2_resources[] = { }; static struct __initdata omap_gpio_platform_data omap16xx_gpio2_config = { - .virtual_irq_start = IH_GPIO_BASE + 16, .bank_width = 16, .regs = &omap16xx_gpio_regs, }; @@ -157,7 +154,6 @@ static struct __initdata resource omap16xx_gpio3_resources[] = { }; static struct __initdata omap_gpio_platform_data omap16xx_gpio3_config = { - .virtual_irq_start = IH_GPIO_BASE + 32, .bank_width = 16, .regs = &omap16xx_gpio_regs, }; @@ -186,7 +182,6 @@ static struct __initdata resource omap16xx_gpio4_resources[] = { }; static struct __initdata omap_gpio_platform_data omap16xx_gpio4_config = { - .virtual_irq_start = IH_GPIO_BASE + 48, .bank_width = 16, .regs = &omap16xx_gpio_regs, }; diff --git a/arch/arm/mach-omap1/gpio7xx.c b/arch/arm/mach-omap1/gpio7xx.c index 4771d6b68b96..acf12b73eace 100644 --- a/arch/arm/mach-omap1/gpio7xx.c +++ b/arch/arm/mach-omap1/gpio7xx.c @@ -51,7 +51,6 @@ static struct omap_gpio_reg_offs omap7xx_mpuio_regs = { }; static struct __initdata omap_gpio_platform_data omap7xx_mpu_gpio_config = { - .virtual_irq_start = IH_MPUIO_BASE, .is_mpuio = true, .bank_width = 16, .bank_stride = 2, @@ -93,7 +92,6 @@ static struct omap_gpio_reg_offs omap7xx_gpio_regs = { }; static struct __initdata omap_gpio_platform_data omap7xx_gpio1_config = { - .virtual_irq_start = IH_GPIO_BASE, .bank_width = 32, .regs = &omap7xx_gpio_regs, }; @@ -122,7 +120,6 @@ static struct __initdata resource omap7xx_gpio2_resources[] = { }; static struct __initdata omap_gpio_platform_data omap7xx_gpio2_config = { - .virtual_irq_start = IH_GPIO_BASE + 32, .bank_width = 32, .regs = &omap7xx_gpio_regs, }; @@ -151,7 +148,6 @@ static struct __initdata resource omap7xx_gpio3_resources[] = { }; static struct __initdata omap_gpio_platform_data omap7xx_gpio3_config = { - .virtual_irq_start = IH_GPIO_BASE + 64, .bank_width = 32, .regs = &omap7xx_gpio_regs, }; @@ -180,7 +176,6 @@ static struct __initdata resource omap7xx_gpio4_resources[] = { }; static struct __initdata omap_gpio_platform_data omap7xx_gpio4_config = { - .virtual_irq_start = IH_GPIO_BASE + 96, .bank_width = 32, .regs = &omap7xx_gpio_regs, }; @@ -209,7 +204,6 @@ static struct __initdata resource omap7xx_gpio5_resources[] = { }; static struct __initdata omap_gpio_platform_data omap7xx_gpio5_config = { - .virtual_irq_start = IH_GPIO_BASE + 128, .bank_width = 32, .regs = &omap7xx_gpio_regs, }; @@ -238,7 +232,6 @@ static struct __initdata resource omap7xx_gpio6_resources[] = { }; static struct __initdata omap_gpio_platform_data omap7xx_gpio6_config = { - .virtual_irq_start = IH_GPIO_BASE + 160, .bank_width = 32, .regs = &omap7xx_gpio_regs, }; diff --git a/arch/arm/mach-omap2/gpio.c b/arch/arm/mach-omap2/gpio.c index 2f994e5194e8..86f91a6ed4e4 100644 --- a/arch/arm/mach-omap2/gpio.c +++ b/arch/arm/mach-omap2/gpio.c @@ -55,7 +55,6 @@ static int __init omap2_gpio_dev_init(struct omap_hwmod *oh, void *unused) dev_attr = (struct omap_gpio_dev_attr *)oh->dev_attr; pdata->bank_width = dev_attr->bank_width; pdata->dbck_flag = dev_attr->dbck_flag; - pdata->virtual_irq_start = IH_GPIO_BASE + 32 * (id - 1); pdata->get_context_loss_count = omap_pm_get_dev_context_loss_count; pdata->regs = kzalloc(sizeof(struct omap_gpio_reg_offs), GFP_KERNEL); if (!pdata) { diff --git a/arch/arm/plat-omap/include/plat/gpio.h b/arch/arm/plat-omap/include/plat/gpio.h index 2f6e9924a814..d903e7df3fb1 100644 --- a/arch/arm/plat-omap/include/plat/gpio.h +++ b/arch/arm/plat-omap/include/plat/gpio.h @@ -193,7 +193,6 @@ struct omap_gpio_reg_offs { }; struct omap_gpio_platform_data { - u16 virtual_irq_start; int bank_type; int bank_width; /* GPIO bank width */ int bank_stride; /* Only needed for omap1 MPUIO */ From c6f31c9ec264233cb6a28053a1739425b18fd581 Mon Sep 17 00:00:00 2001 From: Tarun Kanti DebBarma Date: Fri, 27 Apr 2012 19:43:32 +0530 Subject: [PATCH 15/33] gpio/omap: remove saved_fallingdetect, saved_risingdetect Since we already have context.fallingdetect and context.risingdetect there is no more need to have these additional fields. Also, getting rid of extra reads associated with them. Cc: Kevin Hilman Cc: Tony Lindgren Cc: Santosh Shilimkar Cc: Cousson, Benoit Cc: Grant Likely Signed-off-by: Tarun Kanti DebBarma Reviewed-by: Santosh Shilimkar Acked-by: Felipe Balbi Tested-by: Govindraj.R Signed-off-by: Kevin Hilman --- drivers/gpio/gpio-omap.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 4461540653a8..7d47c6e76d74 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -63,8 +63,6 @@ struct gpio_bank { u32 enabled_non_wakeup_gpios; struct gpio_regs context; u32 saved_datain; - u32 saved_fallingdetect; - u32 saved_risingdetect; u32 level_mask; u32 toggle_mask; spinlock_t lock; @@ -1244,11 +1242,9 @@ static int omap_gpio_runtime_suspend(struct device *dev) */ bank->saved_datain = __raw_readl(bank->base + bank->regs->datain); - l1 = __raw_readl(bank->base + bank->regs->fallingdetect); - l2 = __raw_readl(bank->base + bank->regs->risingdetect); + l1 = bank->context.fallingdetect; + l2 = bank->context.risingdetect; - bank->saved_fallingdetect = l1; - bank->saved_risingdetect = l2; l1 &= ~bank->enabled_non_wakeup_gpios; l2 &= ~bank->enabled_non_wakeup_gpios; @@ -1307,9 +1303,9 @@ static int omap_gpio_runtime_resume(struct device *dev) } } - __raw_writel(bank->saved_fallingdetect, + __raw_writel(bank->context.fallingdetect, bank->base + bank->regs->fallingdetect); - __raw_writel(bank->saved_risingdetect, + __raw_writel(bank->context.risingdetect, bank->base + bank->regs->risingdetect); l = __raw_readl(bank->base + bank->regs->datain); @@ -1326,14 +1322,15 @@ static int omap_gpio_runtime_resume(struct device *dev) * No need to generate IRQs for the rising edge for gpio IRQs * configured with falling edge only; and vice versa. */ - gen0 = l & bank->saved_fallingdetect; + gen0 = l & bank->context.fallingdetect; gen0 &= bank->saved_datain; - gen1 = l & bank->saved_risingdetect; + gen1 = l & bank->context.risingdetect; gen1 &= ~(bank->saved_datain); /* FIXME: Consider GPIO IRQs with level detections properly! */ - gen = l & (~(bank->saved_fallingdetect) & ~(bank->saved_risingdetect)); + gen = l & (~(bank->context.fallingdetect) & + ~(bank->context.risingdetect)); /* Consider all GPIO IRQs needed to be updated */ gen |= gen0 | gen1; From 0aa2727399c0b78225021413022c164cb99fbc5e Mon Sep 17 00:00:00 2001 From: Tarun Kanti DebBarma Date: Fri, 27 Apr 2012 19:43:33 +0530 Subject: [PATCH 16/33] gpio/omap: remove suspend_wakeup field from struct gpio_bank Since we already have bank->context.wake_en to keep track of gpios which are wakeup enabled, there is no need to have this field any more. Cc: Kevin Hilman Cc: Tony Lindgren Cc: Santosh Shilimkar Cc: Cousson, Benoit Cc: Grant Likely Signed-off-by: Tarun Kanti DebBarma Reviewed-by: Santosh Shilimkar Acked-by: Felipe Balbi Tested-by: Govindraj.R Signed-off-by: Kevin Hilman --- drivers/gpio/gpio-omap.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 7d47c6e76d74..c45bc16f0003 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -57,7 +57,6 @@ struct gpio_bank { u16 irq; int irq_base; struct irq_domain *domain; - u32 suspend_wakeup; u32 saved_wakeup; u32 non_wakeup_gpios; u32 enabled_non_wakeup_gpios; @@ -514,11 +513,11 @@ static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable) spin_lock_irqsave(&bank->lock, flags); if (enable) - bank->suspend_wakeup |= gpio_bit; + bank->context.wake_en |= gpio_bit; else - bank->suspend_wakeup &= ~gpio_bit; + bank->context.wake_en &= ~gpio_bit; - __raw_writel(bank->suspend_wakeup, bank->base + bank->regs->wkup_en); + __raw_writel(bank->context.wake_en, bank->base + bank->regs->wkup_en); spin_unlock_irqrestore(&bank->lock, flags); return 0; @@ -788,7 +787,7 @@ static int omap_mpuio_suspend_noirq(struct device *dev) spin_lock_irqsave(&bank->lock, flags); bank->saved_wakeup = __raw_readl(mask_reg); - __raw_writel(0xffff & ~bank->suspend_wakeup, mask_reg); + __raw_writel(0xffff & ~bank->context.wake_en, mask_reg); spin_unlock_irqrestore(&bank->lock, flags); return 0; @@ -1162,7 +1161,7 @@ static int omap_gpio_suspend(struct device *dev) if (!bank->mod_usage || !bank->loses_context) return 0; - if (!bank->regs->wkup_en || !bank->suspend_wakeup) + if (!bank->regs->wkup_en || !bank->context.wake_en) return 0; wakeup_enable = bank->base + bank->regs->wkup_en; @@ -1170,7 +1169,7 @@ static int omap_gpio_suspend(struct device *dev) spin_lock_irqsave(&bank->lock, flags); bank->saved_wakeup = __raw_readl(wakeup_enable); _gpio_rmw(base, bank->regs->wkup_en, 0xffffffff, 0); - _gpio_rmw(base, bank->regs->wkup_en, bank->suspend_wakeup, 1); + _gpio_rmw(base, bank->regs->wkup_en, bank->context.wake_en, 1); spin_unlock_irqrestore(&bank->lock, flags); return 0; From 499fa2871d95049c12362c27075672e8af988eb6 Mon Sep 17 00:00:00 2001 From: Tarun Kanti DebBarma Date: Fri, 27 Apr 2012 19:43:34 +0530 Subject: [PATCH 17/33] gpio/omap: remove saved_wakeup field from struct gpio_bank There is no more need to have saved_wakeup because bank->context.wake_en already holds that value. So getting rid of read/write operation associated with this field. Cc: Kevin Hilman Cc: Tony Lindgren Cc: Santosh Shilimkar Cc: Cousson, Benoit Cc: Grant Likely Signed-off-by: Tarun Kanti DebBarma Reviewed-by: Santosh Shilimkar Acked-by: Felipe Balbi Tested-by: Govindraj.R Signed-off-by: Kevin Hilman --- drivers/gpio/gpio-omap.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index c45bc16f0003..8b6f1b9510eb 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -57,7 +57,6 @@ struct gpio_bank { u16 irq; int irq_base; struct irq_domain *domain; - u32 saved_wakeup; u32 non_wakeup_gpios; u32 enabled_non_wakeup_gpios; struct gpio_regs context; @@ -786,7 +785,6 @@ static int omap_mpuio_suspend_noirq(struct device *dev) unsigned long flags; spin_lock_irqsave(&bank->lock, flags); - bank->saved_wakeup = __raw_readl(mask_reg); __raw_writel(0xffff & ~bank->context.wake_en, mask_reg); spin_unlock_irqrestore(&bank->lock, flags); @@ -802,7 +800,7 @@ static int omap_mpuio_resume_noirq(struct device *dev) unsigned long flags; spin_lock_irqsave(&bank->lock, flags); - __raw_writel(bank->saved_wakeup, mask_reg); + __raw_writel(bank->context.wake_en, mask_reg); spin_unlock_irqrestore(&bank->lock, flags); return 0; @@ -1155,7 +1153,6 @@ static int omap_gpio_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct gpio_bank *bank = platform_get_drvdata(pdev); void __iomem *base = bank->base; - void __iomem *wakeup_enable; unsigned long flags; if (!bank->mod_usage || !bank->loses_context) @@ -1164,10 +1161,7 @@ static int omap_gpio_suspend(struct device *dev) if (!bank->regs->wkup_en || !bank->context.wake_en) return 0; - wakeup_enable = bank->base + bank->regs->wkup_en; - spin_lock_irqsave(&bank->lock, flags); - bank->saved_wakeup = __raw_readl(wakeup_enable); _gpio_rmw(base, bank->regs->wkup_en, 0xffffffff, 0); _gpio_rmw(base, bank->regs->wkup_en, bank->context.wake_en, 1); spin_unlock_irqrestore(&bank->lock, flags); @@ -1185,12 +1179,12 @@ static int omap_gpio_resume(struct device *dev) if (!bank->mod_usage || !bank->loses_context) return 0; - if (!bank->regs->wkup_en || !bank->saved_wakeup) + if (!bank->regs->wkup_en || !bank->context.wake_en) return 0; spin_lock_irqsave(&bank->lock, flags); _gpio_rmw(base, bank->regs->wkup_en, 0xffffffff, 0); - _gpio_rmw(base, bank->regs->wkup_en, bank->saved_wakeup, 1); + _gpio_rmw(base, bank->regs->wkup_en, bank->context.wake_en, 1); spin_unlock_irqrestore(&bank->lock, flags); return 0; From b1a8e3d2d198319e40318563ff80f95605da5a0e Mon Sep 17 00:00:00 2001 From: Tarun Kanti DebBarma Date: Fri, 27 Apr 2012 19:43:35 +0530 Subject: [PATCH 18/33] gpio/omap: remove retrigger variable in gpio_irq_handler commit 672e302e3c (ARM: OMAP: use edge/level handlers from generic IRQ framework) removed retrigger support in favor of using generic IRQ framework. This patch cleans up some unused remnants of that removal. Cc: Kevin Hilman Cc: Tony Lindgren Cc: Santosh Shilimkar Cc: Cousson, Benoit Cc: Grant Likely Signed-off-by: Tarun Kanti DebBarma Reviewed-by: Santosh Shilimkar Acked-by: Felipe Balbi Tested-by: Govindraj.R Signed-off-by: Kevin Hilman --- drivers/gpio/gpio-omap.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 8b6f1b9510eb..96d2aef409b9 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -636,7 +636,6 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) u32 isr; unsigned int gpio_irq, gpio_index; struct gpio_bank *bank; - u32 retrigger = 0; int unmasked = 0; struct irq_chip *chip = irq_desc_get_chip(desc); @@ -673,8 +672,6 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) chained_irq_exit(chip, desc); } - isr |= retrigger; - retrigger = 0; if (!isr) break; From 9c4ed9e6c01e7a8bd9079da8267e1f03cb4761fc Mon Sep 17 00:00:00 2001 From: Tarun Kanti DebBarma Date: Fri, 27 Apr 2012 19:43:36 +0530 Subject: [PATCH 19/33] gpio/omap: remove suspend/resume callbacks Both omap_gpio_suspend() and omap_gpio_resume() does programming of wakeup_en register. _gpio_rmw(base, bank->regs->wkup_en, 0xffffffff, 0); _gpio_rmw(base, bank->regs->wkup_en, bank->context.wake_en, 1); This is redundant in omap_gpio_suspend() because wakeup_en register automatically gets initialized in _set_gpio_wakeup() and set_gpio_trigger() while being called either from chip.irq_set_wake() or chip.irq_set_type(). This is also redundant in omap_gpio_resume() because wakeup_en register is programmed in omap_gpio_restore_context() called which is called from runtime resume callback. Cc: Kevin Hilman Cc: Tony Lindgren Cc: Santosh Shilimkar Cc: Cousson, Benoit Cc: Grant Likely Signed-off-by: Tarun Kanti DebBarma Reviewed-by: Santosh Shilimkar Tested-by: Govindraj.R Signed-off-by: Kevin Hilman --- drivers/gpio/gpio-omap.c | 47 ---------------------------------------- 1 file changed, 47 deletions(-) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 96d2aef409b9..c89c38869c0c 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1144,50 +1144,6 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev) #ifdef CONFIG_ARCH_OMAP2PLUS -#if defined(CONFIG_PM_SLEEP) -static int omap_gpio_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct gpio_bank *bank = platform_get_drvdata(pdev); - void __iomem *base = bank->base; - unsigned long flags; - - if (!bank->mod_usage || !bank->loses_context) - return 0; - - if (!bank->regs->wkup_en || !bank->context.wake_en) - return 0; - - spin_lock_irqsave(&bank->lock, flags); - _gpio_rmw(base, bank->regs->wkup_en, 0xffffffff, 0); - _gpio_rmw(base, bank->regs->wkup_en, bank->context.wake_en, 1); - spin_unlock_irqrestore(&bank->lock, flags); - - return 0; -} - -static int omap_gpio_resume(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct gpio_bank *bank = platform_get_drvdata(pdev); - void __iomem *base = bank->base; - unsigned long flags; - - if (!bank->mod_usage || !bank->loses_context) - return 0; - - if (!bank->regs->wkup_en || !bank->context.wake_en) - return 0; - - spin_lock_irqsave(&bank->lock, flags); - _gpio_rmw(base, bank->regs->wkup_en, 0xffffffff, 0); - _gpio_rmw(base, bank->regs->wkup_en, bank->context.wake_en, 1); - spin_unlock_irqrestore(&bank->lock, flags); - - return 0; -} -#endif /* CONFIG_PM_SLEEP */ - #if defined(CONFIG_PM_RUNTIME) static void omap_gpio_restore_context(struct gpio_bank *bank); @@ -1416,14 +1372,11 @@ static void omap_gpio_restore_context(struct gpio_bank *bank) } #endif /* CONFIG_PM_RUNTIME */ #else -#define omap_gpio_suspend NULL -#define omap_gpio_resume NULL #define omap_gpio_runtime_suspend NULL #define omap_gpio_runtime_resume NULL #endif static const struct dev_pm_ops gpio_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume) SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume, NULL) }; From 4e962e8998cc6cb5e58beae5feb6a65cb1a27f26 Mon Sep 17 00:00:00 2001 From: Tarun Kanti DebBarma Date: Fri, 27 Apr 2012 19:43:37 +0530 Subject: [PATCH 20/33] gpio/omap: remove cpu_is_omapxxxx() checks from *_runtime_resume() Add register offsets for GPIO_IRQSTATUS_RAW_0, GPIO_IRQSTATUS_RAW_0 which are present on OMAP4+ processors. Now we can distinguish conditions applicable to OMAP4,5 and those specific to OMAP24xx and OMAP3xxx. Cc: Kevin Hilman Cc: Tony Lindgren Cc: Santosh Shilimkar Cc: Cousson, Benoit Cc: Grant Likely Signed-off-by: Tarun Kanti DebBarma Reviewed-by: Santosh Shilimkar Tested-by: Govindraj.R Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/gpio.c | 2 ++ arch/arm/plat-omap/include/plat/gpio.h | 2 ++ drivers/gpio/gpio-omap.c | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-omap2/gpio.c b/arch/arm/mach-omap2/gpio.c index 86f91a6ed4e4..a144e22969a3 100644 --- a/arch/arm/mach-omap2/gpio.c +++ b/arch/arm/mach-omap2/gpio.c @@ -101,6 +101,8 @@ static int __init omap2_gpio_dev_init(struct omap_hwmod *oh, void *unused) pdata->regs->dataout = OMAP4_GPIO_DATAOUT; pdata->regs->set_dataout = OMAP4_GPIO_SETDATAOUT; pdata->regs->clr_dataout = OMAP4_GPIO_CLEARDATAOUT; + pdata->regs->irqstatus_raw0 = OMAP4_GPIO_IRQSTATUSRAW0; + pdata->regs->irqstatus_raw1 = OMAP4_GPIO_IRQSTATUSRAW1; pdata->regs->irqstatus = OMAP4_GPIO_IRQSTATUS0; pdata->regs->irqstatus2 = OMAP4_GPIO_IRQSTATUS1; pdata->regs->irqenable = OMAP4_GPIO_IRQSTATUSSET0; diff --git a/arch/arm/plat-omap/include/plat/gpio.h b/arch/arm/plat-omap/include/plat/gpio.h index d903e7df3fb1..50fb7cc000ea 100644 --- a/arch/arm/plat-omap/include/plat/gpio.h +++ b/arch/arm/plat-omap/include/plat/gpio.h @@ -172,6 +172,8 @@ struct omap_gpio_reg_offs { u16 clr_dataout; u16 irqstatus; u16 irqstatus2; + u16 irqstatus_raw0; + u16 irqstatus_raw1; u16 irqenable; u16 irqenable2; u16 set_irqenable; diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index c89c38869c0c..2c70617e6b45 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1286,14 +1286,14 @@ static int omap_gpio_runtime_resume(struct device *dev) old0 = __raw_readl(bank->base + bank->regs->leveldetect0); old1 = __raw_readl(bank->base + bank->regs->leveldetect1); - if (cpu_is_omap24xx() || cpu_is_omap34xx()) { + if (!bank->regs->irqstatus_raw0) { __raw_writel(old0 | gen, bank->base + bank->regs->leveldetect0); __raw_writel(old1 | gen, bank->base + bank->regs->leveldetect1); } - if (cpu_is_omap44xx()) { + if (bank->regs->irqstatus_raw0) { __raw_writel(old0 | l, bank->base + bank->regs->leveldetect0); __raw_writel(old1 | l, bank->base + From 1b1287032df3a69d3ef9a486b444f4ffcca50d01 Mon Sep 17 00:00:00 2001 From: Tarun Kanti DebBarma Date: Fri, 27 Apr 2012 19:43:38 +0530 Subject: [PATCH 21/33] gpio/omap: fix missing check in *_runtime_suspend() We do checking for bank->enabled_non_wakeup_gpios in order to skip redundant operations. Somehow, the check got missed while doing the cleanup series. Just to make sure that we do context restore correctly in *_runtime_resume(), the bank->workaround_enabled check is moved after context restore. Otherwise, it would prevent context restore when bank->enabled_non_wakeup_gpios is 0. Cc: Kevin Hilman Cc: Tony Lindgren Cc: Santosh Shilimkar Cc: Cousson, Benoit Cc: Grant Likely Signed-off-by: Tarun Kanti DebBarma Reviewed-by: Santosh Shilimkar Tested-by: Govindraj.R Signed-off-by: Kevin Hilman --- drivers/gpio/gpio-omap.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 2c70617e6b45..9b71f04538aa 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1157,6 +1157,9 @@ static int omap_gpio_runtime_suspend(struct device *dev) spin_lock_irqsave(&bank->lock, flags); + if (!bank->enabled_non_wakeup_gpios) + goto update_gpio_context_count; + /* * Only edges can generate a wakeup event to the PRCM. * @@ -1232,11 +1235,6 @@ static int omap_gpio_runtime_resume(struct device *dev) __raw_writel(bank->context.risingdetect, bank->base + bank->regs->risingdetect); - if (!bank->workaround_enabled) { - spin_unlock_irqrestore(&bank->lock, flags); - return 0; - } - if (bank->get_context_loss_count) { context_lost_cnt_after = bank->get_context_loss_count(bank->dev); @@ -1249,6 +1247,11 @@ static int omap_gpio_runtime_resume(struct device *dev) } } + if (!bank->workaround_enabled) { + spin_unlock_irqrestore(&bank->lock, flags); + return 0; + } + __raw_writel(bank->context.fallingdetect, bank->base + bank->regs->fallingdetect); __raw_writel(bank->context.risingdetect, From 22770de11cb13e7120f973bca6c800de371a6717 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Thu, 17 May 2012 14:52:56 -0700 Subject: [PATCH 22/33] gpio/omap: fix broken context restore for non-OFF mode transitions The fix in commit 1b1287032 (gpio/omap: fix missing check in *_runtime_suspend()) exposed another bug in the context restore path. Currently, the per-bank context restore happens whenever the context loss count is different in runtime suspend and runtime resume *and* whenever the per-bank contex_loss_count == 0: if (context_lost_cnt_after != bank->context_loss_count || !context_lost_cnt_after) { omap_gpio_restore_context(bank); Restoring context when the context_lost_cnt_after == 0 is clearly wrong, since this will be true until the first off-mode transition (which could be never, if off-mode is never enabled.) This check causes the context to be restored on *every* runtime PM transition. Before commit 1b1287032 (gpio/omap: fix missing check in *_runtime_suspend()), this code was never executed in non-OFF mode, so there were never spurious context restores happening. After that change though, spurious context restores could happen. To fix, simply remove the !context_lost_cnt_after check. It is not needed. This bug was found when noticing that the smc911x NIC on 3530/Overo was not working, and git bisect tracked it down to this patch. It seems that the spurious context restore was causing the smsc911x to not be properly probed on this platform. Tested-by: Tony Lindgren Acked-by: Tarun Kanti DebBarma Cc: Santosh Shilimkar Signed-off-by: Kevin Hilman --- drivers/gpio/gpio-omap.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 9b71f04538aa..b570a6aae6e6 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1238,8 +1238,7 @@ static int omap_gpio_runtime_resume(struct device *dev) if (bank->get_context_loss_count) { context_lost_cnt_after = bank->get_context_loss_count(bank->dev); - if (context_lost_cnt_after != bank->context_loss_count || - !context_lost_cnt_after) { + if (context_lost_cnt_after != bank->context_loss_count) { omap_gpio_restore_context(bank); } else { spin_unlock_irqrestore(&bank->lock, flags); From b3c64bc30af67ed328a8d919e41160942b870451 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Thu, 17 May 2012 16:42:16 -0700 Subject: [PATCH 23/33] gpio/omap: (re)fix wakeups on level-triggered GPIOs commit 1b1287032 (gpio/omap: fix missing check in *_runtime_suspend()) broke wakeups on level-triggered GPIOs by adding the enabled non-wakeup GPIO check before the workaround that enables wakeups on level-triggered IRQs, effectively disabling that workaround. To fix, move the enabled non-wakeup GPIO check after the level-triggered IRQ workaround. Reported-by: Tony Lindgren Tested-by: Tony Lindgren Acked-by: Santosh Shilimkar Acked-by: Tarun Kanti DebBarma Tested-by: Tarun Kanti DebBarma Signed-off-by: Kevin Hilman --- drivers/gpio/gpio-omap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index b570a6aae6e6..c4ed1722734c 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1157,9 +1157,6 @@ static int omap_gpio_runtime_suspend(struct device *dev) spin_lock_irqsave(&bank->lock, flags); - if (!bank->enabled_non_wakeup_gpios) - goto update_gpio_context_count; - /* * Only edges can generate a wakeup event to the PRCM. * @@ -1180,6 +1177,9 @@ static int omap_gpio_runtime_suspend(struct device *dev) __raw_writel(wake_hi | bank->context.risingdetect, bank->base + bank->regs->risingdetect); + if (!bank->enabled_non_wakeup_gpios) + goto update_gpio_context_count; + if (bank->power_mode != OFF_MODE) { bank->power_mode = 0; goto update_gpio_context_count; From ae79c19049b75af476adcbcfda6fc29bd93fc6f8 Mon Sep 17 00:00:00 2001 From: Andreas Schallenberg Date: Wed, 9 May 2012 09:46:17 +0200 Subject: [PATCH 24/33] Add support for TCA6424A This patch extends the PCA953x driver to support TI's TCA6424A 24 bit I2C I/O expander. The patch is based on code by Michele Bevilacqua. Changes in v2: - Compare ngpio against 24 in both places, not >16 - Larger datatype now u32 instead of uint. Bit fields not used for struct members since their address is taken. - Be precise: TCA6424A (untested for older TCA6424) Signed-off-by: Andreas Schallenberg Acked-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpio-pca953x.c | 43 ++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index d3f3e8f54561..1c313c710be3 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -28,6 +28,8 @@ #define PCA953X_INVERT 2 #define PCA953X_DIRECTION 3 +#define REG_ADDR_AI 0x80 + #define PCA957X_IN 0 #define PCA957X_INVRT 1 #define PCA957X_BKEN 2 @@ -63,15 +65,15 @@ static const struct i2c_device_id pca953x_id[] = { { "pca6107", 8 | PCA953X_TYPE | PCA_INT, }, { "tca6408", 8 | PCA953X_TYPE | PCA_INT, }, { "tca6416", 16 | PCA953X_TYPE | PCA_INT, }, - /* NYET: { "tca6424", 24, }, */ + { "tca6424", 24 | PCA953X_TYPE | PCA_INT, }, { } }; MODULE_DEVICE_TABLE(i2c, pca953x_id); struct pca953x_chip { unsigned gpio_start; - uint16_t reg_output; - uint16_t reg_direction; + u32 reg_output; + u32 reg_direction; struct mutex i2c_lock; #ifdef CONFIG_GPIO_PCA953X_IRQ @@ -89,12 +91,20 @@ struct pca953x_chip { int chip_type; }; -static int pca953x_write_reg(struct pca953x_chip *chip, int reg, uint16_t val) +static int pca953x_write_reg(struct pca953x_chip *chip, int reg, u32 val) { int ret = 0; if (chip->gpio_chip.ngpio <= 8) ret = i2c_smbus_write_byte_data(chip->client, reg, val); + else if (chip->gpio_chip.ngpio == 24) { + ret = i2c_smbus_write_word_data(chip->client, + (reg << 2) | REG_ADDR_AI, + val & 0xffff); + ret = i2c_smbus_write_byte_data(chip->client, + (reg << 2) + 2, + (val & 0xff0000) >> 16); + } else { switch (chip->chip_type) { case PCA953X_TYPE: @@ -121,12 +131,17 @@ static int pca953x_write_reg(struct pca953x_chip *chip, int reg, uint16_t val) return 0; } -static int pca953x_read_reg(struct pca953x_chip *chip, int reg, uint16_t *val) +static int pca953x_read_reg(struct pca953x_chip *chip, int reg, u32 *val) { int ret; if (chip->gpio_chip.ngpio <= 8) ret = i2c_smbus_read_byte_data(chip->client, reg); + else if (chip->gpio_chip.ngpio == 24) { + ret = i2c_smbus_read_word_data(chip->client, reg << 2); + ret |= (i2c_smbus_read_byte_data(chip->client, + (reg << 2) + 2)<<16); + } else ret = i2c_smbus_read_word_data(chip->client, reg << 1); @@ -135,14 +150,14 @@ static int pca953x_read_reg(struct pca953x_chip *chip, int reg, uint16_t *val) return ret; } - *val = (uint16_t)ret; + *val = (u32)ret; return 0; } static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off) { struct pca953x_chip *chip; - uint16_t reg_val; + uint reg_val; int ret, offset = 0; chip = container_of(gc, struct pca953x_chip, gpio_chip); @@ -173,7 +188,7 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc, unsigned off, int val) { struct pca953x_chip *chip; - uint16_t reg_val; + uint reg_val; int ret, offset = 0; chip = container_of(gc, struct pca953x_chip, gpio_chip); @@ -223,7 +238,7 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc, static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) { struct pca953x_chip *chip; - uint16_t reg_val; + u32 reg_val; int ret, offset = 0; chip = container_of(gc, struct pca953x_chip, gpio_chip); @@ -253,7 +268,7 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) { struct pca953x_chip *chip; - uint16_t reg_val; + u32 reg_val; int ret, offset = 0; chip = container_of(gc, struct pca953x_chip, gpio_chip); @@ -386,7 +401,7 @@ static struct irq_chip pca953x_irq_chip = { static uint16_t pca953x_irq_pending(struct pca953x_chip *chip) { - uint16_t cur_stat; + u32 cur_stat; uint16_t old_stat; uint16_t pending; uint16_t trigger; @@ -449,6 +464,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, { struct i2c_client *client = chip->client; int ret, offset = 0; + u32 temporary; if (irq_base != -1 && (id->driver_data & PCA_INT)) { @@ -462,7 +478,8 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, offset = PCA957X_IN; break; } - ret = pca953x_read_reg(chip, offset, &chip->irq_stat); + ret = pca953x_read_reg(chip, offset, &temporary); + chip->irq_stat = temporary; if (ret) goto out_failed; @@ -603,7 +620,7 @@ static int __devinit device_pca953x_init(struct pca953x_chip *chip, int invert) static int __devinit device_pca957x_init(struct pca953x_chip *chip, int invert) { int ret; - uint16_t val = 0; + u32 val = 0; /* Let every port in proper state, that could save power */ pca953x_write_reg(chip, PCA957X_PUPD, 0x0); From eb1567f7ad0032ba7b02b878e352f191d53dc228 Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Wed, 25 Apr 2012 11:51:53 +0200 Subject: [PATCH 25/33] gpio-mcp23s08: dbg_show: fix pullup configuration display Pullups are enabled when bits are set, not when cleared. Signed-off-by: Peter Korsgaard Signed-off-by: Grant Likely --- drivers/gpio/gpio-mcp23s08.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c index c5d83a8a91c2..0f425189de11 100644 --- a/drivers/gpio/gpio-mcp23s08.c +++ b/drivers/gpio/gpio-mcp23s08.c @@ -353,7 +353,7 @@ static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip) 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"); + (mcp->cache[MCP_GPPU] & mask) ? "up" : " "); /* NOTE: ignoring the irq-related registers */ seq_printf(s, "\n"); } From 09d71ff19404b3957fab6de942fb8026ccfd8524 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 May 2012 12:46:46 +0100 Subject: [PATCH 26/33] gpiolib: Implement devm_gpio_request_one() Allow drivers to use the modern request and configure idiom together with devres. As with plain gpio_request() and gpio_request_one() we can't implement the old school version in terms of _one() as this would force the explicit selection of a direction in gpio_request() which could break systems if we pick the wrong one. Implementing devm_gpio_request_one() in terms of devm_gpio_request() would needlessly complicate things or lead to duplication from the unmanaged version depending on how it's done. Signed-off-by: Mark Brown Acked-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/devres.c | 29 +++++++++++++++++++++++++++++ include/asm-generic/gpio.h | 2 ++ include/linux/gpio.h | 6 ++++++ 3 files changed, 37 insertions(+) diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c index 3dd29399cef5..d21a9ff38551 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -70,6 +70,35 @@ int devm_gpio_request(struct device *dev, unsigned gpio, const char *label) } EXPORT_SYMBOL(devm_gpio_request); +/** + * devm_gpio_request_one - request a single GPIO with initial setup + * @dev: device to request for + * @gpio: the GPIO number + * @flags: GPIO configuration as specified by GPIOF_* + * @label: a literal description string of this GPIO + */ +int devm_gpio_request_one(struct device *dev, unsigned gpio, + unsigned long flags, const char *label) +{ + unsigned *dr; + int rc; + + dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + rc = gpio_request_one(gpio, flags, label); + if (rc) { + devres_free(dr); + return rc; + } + + *dr = gpio; + devres_add(dev, dr); + + return 0; +} + /** * devm_gpio_free - free an interrupt * @dev: device to free gpio for diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 5f52690c3c8f..4ead12332c66 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -179,6 +179,8 @@ extern void gpio_free_array(const struct gpio *array, size_t num); /* bindings for managed devices that want to request gpios */ int devm_gpio_request(struct device *dev, unsigned gpio, const char *label); +int devm_gpio_request_one(struct device *dev, unsigned gpio, + unsigned long flags, const char *label); void devm_gpio_free(struct device *dev, unsigned int gpio); #ifdef CONFIG_GPIO_SYSFS diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 7a8816a1a0d8..f07fc2d08159 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -106,6 +106,12 @@ static inline int gpio_request_one(unsigned gpio, return -ENOSYS; } +static inline int devm_gpio_request_one(struct device *dev, unsigned gpio, + unsigned long flags, const char *label) +{ + return -ENOSYS; +} + static inline int gpio_request_array(const struct gpio *array, size_t num) { return -ENOSYS; From 3d0f7cf0f3633f92ddeb767eb59cab73963d4dee Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 17 May 2012 13:54:40 -0600 Subject: [PATCH 27/33] gpio: Adjust of_xlate API to support multiple GPIO chips This patch changes the of_xlate API to make it possible for multiple gpio_chips to refer to the same device tree node. This is useful for banked GPIO controllers that use multiple gpio_chips for a single device. With this change the core code will try calling of_xlate on each gpio_chip that references the device_node and will return the gpio number for the first one to return 'true'. Tested-by: Roland Stigge Acked-by: Arnd Bergmann Signed-off-by: Grant Likely --- arch/arm/mach-imx/mach-mx35_3ds.c | 3 +- drivers/gpio/gpiolib-of.c | 84 +++++++++++++++---------------- drivers/gpio/gpiolib.c | 2 +- include/asm-generic/gpio.h | 2 +- include/linux/of_gpio.h | 1 - 5 files changed, 43 insertions(+), 49 deletions(-) diff --git a/arch/arm/mach-imx/mach-mx35_3ds.c b/arch/arm/mach-imx/mach-mx35_3ds.c index 6ae51c6b95b7..6a7cf91ee819 100644 --- a/arch/arm/mach-imx/mach-mx35_3ds.c +++ b/arch/arm/mach-imx/mach-mx35_3ds.c @@ -96,8 +96,7 @@ static struct i2c_board_info __initdata i2c_devices_3ds[] = { static int lcd_power_gpio = -ENXIO; -static int mc9s08dz60_gpiochip_match(struct gpio_chip *chip, - const void *data) +static int mc9s08dz60_gpiochip_match(struct gpio_chip *chip, void *data) { return !strcmp(chip->label, data); } diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index bf984b6dc477..d18068a9f3ec 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -15,11 +15,39 @@ #include #include #include +#include #include #include #include #include +/* Private data structure for of_gpiochip_is_match */ +struct gg_data { + enum of_gpio_flags *flags; + struct of_phandle_args gpiospec; + + int out_gpio; +}; + +/* Private function for resolving node pointer to gpio_chip */ +static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data) +{ + struct gg_data *gg_data = data; + int ret; + + if ((gc->of_node != gg_data->gpiospec.np) || + (gc->of_gpio_n_cells != gg_data->gpiospec.args_count) || + (!gc->of_xlate)) + return false; + + ret = gc->of_xlate(gc, &gg_data->gpiospec, gg_data->flags); + if (ret < 0) + return false; + + gg_data->out_gpio = ret + gc->base; + return true; +} + /** * of_get_named_gpio_flags() - Get a GPIO number and flags to use with GPIO API * @np: device node to get GPIO from @@ -34,46 +62,25 @@ int of_get_named_gpio_flags(struct device_node *np, const char *propname, int index, enum of_gpio_flags *flags) { + struct gg_data gg_data = { .flags = flags, .out_gpio = -ENODEV }; int ret; - struct gpio_chip *gc; - struct of_phandle_args gpiospec; - ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index, - &gpiospec); - if (ret) { - pr_debug("%s: can't parse gpios property\n", __func__); - goto err0; - } - - gc = of_node_to_gpiochip(gpiospec.np); - if (!gc) { - pr_debug("%s: gpio controller %s isn't registered\n", - np->full_name, gpiospec.np->full_name); - ret = -ENODEV; - goto err1; - } - - if (gpiospec.args_count != gc->of_gpio_n_cells) { - pr_debug("%s: wrong #gpio-cells for %s\n", - np->full_name, gpiospec.np->full_name); - ret = -EINVAL; - goto err1; - } - - /* .xlate might decide to not fill in the flags, so clear it. */ + /* .of_xlate might decide to not fill in the flags, so clear it. */ if (flags) *flags = 0; - ret = gc->of_xlate(gc, &gpiospec, flags); - if (ret < 0) - goto err1; + ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index, + &gg_data.gpiospec); + if (ret) { + pr_debug("%s: can't parse gpios property\n", __func__); + return -EINVAL; + } - ret += gc->base; -err1: - of_node_put(gpiospec.np); -err0: + gpiochip_find(&gg_data, of_gpiochip_find_and_xlate); + + of_node_put(gg_data.gpiospec.np); pr_debug("%s exited with status %d\n", __func__, ret); - return ret; + return gg_data.out_gpio; } EXPORT_SYMBOL(of_get_named_gpio_flags); @@ -227,14 +234,3 @@ void of_gpiochip_remove(struct gpio_chip *chip) if (chip->of_node) of_node_put(chip->of_node); } - -/* Private function for resolving node pointer to gpio_chip */ -static int of_gpiochip_is_match(struct gpio_chip *chip, const void *data) -{ - return chip->of_node == data; -} - -struct gpio_chip *of_node_to_gpiochip(struct device_node *np) -{ - return gpiochip_find(np, of_gpiochip_is_match); -} diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 566d0122d832..38353c028fdd 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1156,7 +1156,7 @@ EXPORT_SYMBOL_GPL(gpiochip_remove); */ struct gpio_chip *gpiochip_find(const void *data, int (*match)(struct gpio_chip *chip, - const void *data)) + void *data)) { struct gpio_chip *chip = NULL; unsigned long flags; diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 4ead12332c66..1ba08f0e49a3 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -144,7 +144,7 @@ extern int gpiochip_add(struct gpio_chip *chip); extern int __must_check gpiochip_remove(struct gpio_chip *chip); extern struct gpio_chip *gpiochip_find(const void *data, int (*match)(struct gpio_chip *chip, - const void *data)); + void *data)); /* Always use the library code for GPIO management calls, diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h index 81733d12cbea..c454f5796747 100644 --- a/include/linux/of_gpio.h +++ b/include/linux/of_gpio.h @@ -58,7 +58,6 @@ extern int of_mm_gpiochip_add(struct device_node *np, extern void of_gpiochip_add(struct gpio_chip *gc); extern void of_gpiochip_remove(struct gpio_chip *gc); -extern struct gpio_chip *of_node_to_gpiochip(struct device_node *np); extern int of_gpio_simple_xlate(struct gpio_chip *gc, const struct of_phandle_args *gpiospec, u32 *flags); From e92935e13a052df7e6bc274e00fc91b80531f1e4 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Fri, 18 May 2012 10:19:52 +0200 Subject: [PATCH 28/33] gpio/lpc32xx: Add device tree support This patch adds device tree support for gpio-lpc32xx.c. To register the various GPIO banks as (struct) gpio_chips via the same DT gpio-controller, we utilize the adjusted of_xlate API to manipulate the actually used struct gpio_chip. Signed-off-by: Roland Stigge Reviewed-by: Arnd Bergmann Signed-off-by: Grant Likely --- .../devicetree/bindings/gpio/gpio_lpc32xx.txt | 43 +++++++++++++++ arch/arm/mach-lpc32xx/include/mach/gpio.h | 9 +++- drivers/gpio/gpio-lpc32xx.c | 52 ++++++++++++++++++- 3 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/gpio_lpc32xx.txt diff --git a/Documentation/devicetree/bindings/gpio/gpio_lpc32xx.txt b/Documentation/devicetree/bindings/gpio/gpio_lpc32xx.txt new file mode 100644 index 000000000000..49819367a011 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio_lpc32xx.txt @@ -0,0 +1,43 @@ +NXP LPC32xx SoC GPIO controller + +Required properties: +- compatible: must be "nxp,lpc3220-gpio" +- reg: Physical base address and length of the controller's registers. +- gpio-controller: Marks the device node as a GPIO controller. +- #gpio-cells: Should be 3: + 1) bank: + 0: GPIO P0 + 1: GPIO P1 + 2: GPIO P2 + 3: GPIO P3 + 4: GPI P3 + 5: GPO P3 + 2) pin number + 3) optional parameters: + - bit 0 specifies polarity (0 for normal, 1 for inverted) +- reg: Index of the GPIO group + +Example: + + gpio: gpio@40028000 { + compatible = "nxp,lpc3220-gpio"; + reg = <0x40028000 0x1000>; + gpio-controller; + #gpio-cells = <3>; /* bank, pin, flags */ + }; + + leds { + compatible = "gpio-leds"; + + led0 { + gpios = <&gpio 5 1 1>; /* GPO_P3 1, active low */ + linux,default-trigger = "heartbeat"; + default-state = "off"; + }; + + led1 { + gpios = <&gpio 5 14 1>; /* GPO_P3 14, active low */ + linux,default-trigger = "timer"; + default-state = "off"; + }; + }; diff --git a/arch/arm/mach-lpc32xx/include/mach/gpio.h b/arch/arm/mach-lpc32xx/include/mach/gpio.h index 40a8c178f10d..2ba6ca412bef 100644 --- a/arch/arm/mach-lpc32xx/include/mach/gpio.h +++ b/arch/arm/mach-lpc32xx/include/mach/gpio.h @@ -1 +1,8 @@ -/* empty */ +#ifndef __MACH_GPIO_H +#define __MACH_GPIO_H + +#include "gpio-lpc32xx.h" + +#define ARCH_NR_GPIOS (LPC32XX_GPO_P3_GRP + LPC32XX_GPO_P3_MAX) + +#endif /* __MACH_GPIO_H */ diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c index 61c2d08d37b6..c2199beca98a 100644 --- a/drivers/gpio/gpio-lpc32xx.c +++ b/drivers/gpio/gpio-lpc32xx.c @@ -21,6 +21,9 @@ #include #include #include +#include +#include +#include #include #include @@ -454,10 +457,57 @@ static struct lpc32xx_gpio_chip lpc32xx_gpiochip[] = { }, }; +/* Empty now, can be removed later when mach-lpc32xx is finally switched over + * to DT support + */ void __init lpc32xx_gpio_init(void) +{ +} + +static int lpc32xx_of_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, u32 *flags) +{ + /* Is this the correct bank? */ + u32 bank = gpiospec->args[0]; + if ((bank > ARRAY_SIZE(lpc32xx_gpiochip) || + (gc != &lpc32xx_gpiochip[bank].chip))) + return -EINVAL; + + if (flags) + *flags = gpiospec->args[2]; + return gpiospec->args[1]; +} + +static int __devinit lpc32xx_gpio_probe(struct platform_device *pdev) { int i; - for (i = 0; i < ARRAY_SIZE(lpc32xx_gpiochip); i++) + for (i = 0; i < ARRAY_SIZE(lpc32xx_gpiochip); i++) { + if (pdev->dev.of_node) { + lpc32xx_gpiochip[i].chip.of_xlate = lpc32xx_of_xlate; + lpc32xx_gpiochip[i].chip.of_gpio_n_cells = 3; + lpc32xx_gpiochip[i].chip.of_node = pdev->dev.of_node; + } gpiochip_add(&lpc32xx_gpiochip[i].chip); + } + + return 0; } + +#ifdef CONFIG_OF +static struct of_device_id lpc32xx_gpio_of_match[] __devinitdata = { + { .compatible = "nxp,lpc3220-gpio", }, + { }, +}; +#endif + +static struct platform_driver lpc32xx_gpio_driver = { + .driver = { + .name = "lpc32xx-gpio", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(lpc32xx_gpio_of_match), + }, + .probe = lpc32xx_gpio_probe, +}; + +module_platform_driver(lpc32xx_gpio_driver); From d6de85e85edcc38c9edcde45a0a568818fcddc13 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 3 May 2012 12:22:06 +0200 Subject: [PATCH 29/33] gpio: mpc8xxx: Prevent NULL pointer deref in demux handler commit cfadd838(powerpc/8xxx: Fix interrupt handling in MPC8xxx GPIO driver) added an unconditional call of chip->irq_eoi() to the demux handler. This leads to a NULL pointer derefernce on MPC512x platforms which use this driver as well. Make it conditional. Reported-by: Thomas Wucher Signed-off-by: Thomas Gleixner Cc: Felix Radensky Cc: Kumar Gala Cc: Grant Likely Cc: stable@vger.kernel.org Signed-off-by: Grant Likely --- drivers/gpio/gpio-mpc8xxx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index e6568c19c939..5a1817eedd1b 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -163,7 +163,8 @@ static void mpc8xxx_gpio_irq_cascade(unsigned int irq, struct irq_desc *desc) if (mask) generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq, 32 - ffs(mask))); - chip->irq_eoi(&desc->irq_data); + if (chip->irq_eoi) + chip->irq_eoi(&desc->irq_data); } static void mpc8xxx_irq_unmask(struct irq_data *d) From ee1c1e7d6451204e71ae6d815bfa918c57450391 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 11 May 2012 16:58:33 -0700 Subject: [PATCH 30/33] gpiolib: quiet gpiochip_add boot message noise The pr_info message in gpiochip_add gets displayed for every gpiochip registered. When first bringing up a system this information could be helpful but for normal use it's just a bunch of noise. Change the message to a pr_debug. Signed-off-by: H Hartley Sweeten Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 38353c028fdd..e48b70c6ddf1 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1093,7 +1093,7 @@ int gpiochip_add(struct gpio_chip *chip) if (status) goto fail; - pr_info("gpiochip_add: registered GPIOs %d to %d on device: %s\n", + pr_debug("gpiochip_add: registered GPIOs %d to %d on device: %s\n", chip->base, chip->base + chip->ngpio - 1, chip->label ? : "generic"); From e9fe32bcadb8a7a40411d77f168abd45941b049b Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Mon, 14 May 2012 12:46:12 +0530 Subject: [PATCH 31/33] gpio/rc5t583: add gpio driver for RICOH PMIC RC5T583 The PMIC device RC5T583 from RICOH supports 8 gpios. Adding gpio driver for this device to access the pins control through gpio library. Signed-off-by: Laxman Dewangan [grant.likely: slight cosmetic changes] Signed-off-by: Grant Likely --- drivers/gpio/Kconfig | 9 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-rc5t583.c | 180 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/rc5t583.h | 2 + 4 files changed, 192 insertions(+) create mode 100644 drivers/gpio/gpio-rc5t583.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 25535ebf4f90..7028f7bee7fc 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -301,6 +301,15 @@ config GPIO_PCF857X This driver provides an in-kernel interface to those GPIOs using platform-neutral GPIO calls. +config GPIO_RC5T583 + bool "RICOH RC5T583 GPIO" + depends on MFD_RC5T583 + help + Select this option to enable GPIO driver for the Ricoh RC5T583 + chip family. + This driver provides the support for driving/reading the gpio pins + of RC5T583 device through standard gpio library. + config GPIO_SX150X bool "Semtech SX150x I2C GPIO expander" depends on I2C=y diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 7862f49b4d05..8f36637b4ae3 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o obj-$(CONFIG_GPIO_PCH) += gpio-pch.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o +obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o diff --git a/drivers/gpio/gpio-rc5t583.c b/drivers/gpio/gpio-rc5t583.c new file mode 100644 index 000000000000..08428bf17718 --- /dev/null +++ b/drivers/gpio/gpio-rc5t583.c @@ -0,0 +1,180 @@ +/* + * GPIO driver for RICOH583 power management chip. + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Author: Laxman dewangan + * + * Based on code + * Copyright (C) 2011 RICOH COMPANY,LTD + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +struct rc5t583_gpio { + struct gpio_chip gpio_chip; + struct rc5t583 *rc5t583; +}; + +static inline struct rc5t583_gpio *to_rc5t583_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct rc5t583_gpio, gpio_chip); +} + +static int rc5t583_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + struct device *parent = rc5t583_gpio->rc5t583->dev; + uint8_t val = 0; + int ret; + + ret = rc5t583_read(parent, RC5T583_GPIO_MON_IOIN, &val); + if (ret < 0) + return ret; + + return !!(val & BIT(offset)); +} + +static void rc5t583_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + struct device *parent = rc5t583_gpio->rc5t583->dev; + if (val) + rc5t583_set_bits(parent, RC5T583_GPIO_IOOUT, BIT(offset)); + else + rc5t583_clear_bits(parent, RC5T583_GPIO_IOOUT, BIT(offset)); +} + +static int rc5t583_gpio_dir_input(struct gpio_chip *gc, unsigned int offset) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + struct device *parent = rc5t583_gpio->rc5t583->dev; + int ret; + + ret = rc5t583_clear_bits(parent, RC5T583_GPIO_IOSEL, BIT(offset)); + if (ret < 0) + return ret; + + /* Set pin to gpio mode */ + return rc5t583_clear_bits(parent, RC5T583_GPIO_PGSEL, BIT(offset)); +} + +static int rc5t583_gpio_dir_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + struct device *parent = rc5t583_gpio->rc5t583->dev; + int ret; + + rc5t583_gpio_set(gc, offset, value); + ret = rc5t583_set_bits(parent, RC5T583_GPIO_IOSEL, BIT(offset)); + if (ret < 0) + return ret; + + /* Set pin to gpio mode */ + return rc5t583_clear_bits(parent, RC5T583_GPIO_PGSEL, BIT(offset)); +} + +static int rc5t583_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + + if ((offset >= 0) && (offset < 8)) + return rc5t583_gpio->rc5t583->irq_base + + RC5T583_IRQ_GPIO0 + offset; + return -EINVAL; +} + +static void rc5t583_gpio_free(struct gpio_chip *gc, unsigned offset) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + struct device *parent = rc5t583_gpio->rc5t583->dev; + + rc5t583_set_bits(parent, RC5T583_GPIO_PGSEL, BIT(offset)); +} + +static int __devinit rc5t583_gpio_probe(struct platform_device *pdev) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent); + struct rc5t583_platform_data *pdata = dev_get_platdata(rc5t583->dev); + struct rc5t583_gpio *rc5t583_gpio; + + rc5t583_gpio = devm_kzalloc(&pdev->dev, sizeof(*rc5t583_gpio), + GFP_KERNEL); + if (!rc5t583_gpio) { + dev_warn(&pdev->dev, "Mem allocation for rc5t583_gpio failed"); + return -ENOMEM; + } + + rc5t583_gpio->gpio_chip.label = "gpio-rc5t583", + rc5t583_gpio->gpio_chip.owner = THIS_MODULE, + rc5t583_gpio->gpio_chip.free = rc5t583_gpio_free, + rc5t583_gpio->gpio_chip.direction_input = rc5t583_gpio_dir_input, + rc5t583_gpio->gpio_chip.direction_output = rc5t583_gpio_dir_output, + rc5t583_gpio->gpio_chip.set = rc5t583_gpio_set, + rc5t583_gpio->gpio_chip.get = rc5t583_gpio_get, + rc5t583_gpio->gpio_chip.to_irq = rc5t583_gpio_to_irq, + rc5t583_gpio->gpio_chip.ngpio = RC5T583_MAX_GPIO, + rc5t583_gpio->gpio_chip.can_sleep = 1, + rc5t583_gpio->gpio_chip.dev = &pdev->dev; + rc5t583_gpio->gpio_chip.base = -1; + rc5t583_gpio->rc5t583 = rc5t583; + + if (pdata && pdata->gpio_base) + rc5t583_gpio->gpio_chip.base = pdata->gpio_base; + + platform_set_drvdata(pdev, rc5t583_gpio); + + return gpiochip_add(&rc5t583_gpio->gpio_chip); +} + +static int __devexit rc5t583_gpio_remove(struct platform_device *pdev) +{ + struct rc5t583_gpio *rc5t583_gpio = platform_get_drvdata(pdev); + + return gpiochip_remove(&rc5t583_gpio->gpio_chip); +} + +static struct platform_driver rc5t583_gpio_driver = { + .driver = { + .name = "rc5t583-gpio", + .owner = THIS_MODULE, + }, + .probe = rc5t583_gpio_probe, + .remove = __devexit_p(rc5t583_gpio_remove), +}; + +static int __init rc5t583_gpio_init(void) +{ + return platform_driver_register(&rc5t583_gpio_driver); +} +subsys_initcall(rc5t583_gpio_init); + +static void __exit rc5t583_gpio_exit(void) +{ + platform_driver_unregister(&rc5t583_gpio_driver); +} +module_exit(rc5t583_gpio_exit); + +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_DESCRIPTION("GPIO interface for RC5T583"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:rc5t583-gpio"); diff --git a/include/linux/mfd/rc5t583.h b/include/linux/mfd/rc5t583.h index 0b64b19d81ab..7875370f7bca 100644 --- a/include/linux/mfd/rc5t583.h +++ b/include/linux/mfd/rc5t583.h @@ -272,11 +272,13 @@ struct rc5t583 { * rc5t583_platform_data: Platform data for ricoh rc5t583 pmu. * The board specific data is provided through this structure. * @irq_base: Irq base number on which this device registers their interrupts. + * @gpio_base: GPIO base from which gpio of this device will start. * @enable_shutdown: Enable shutdown through the input pin "shutdown". */ struct rc5t583_platform_data { int irq_base; + int gpio_base; bool enable_shutdown; }; From 07ce8ec7308ab3fa55fe2861671b157f857fff58 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 18 May 2012 23:01:05 -0600 Subject: [PATCH 32/33] gpiolib: Remove 'const' from data argument of gpiochip_find() Commit 3d0f7cf0 "gpio: Adjust of_xlate API to support multiple GPIO chips" changed the api of gpiochip_find to drop const from the data parameter of the match hook, but didn't also drop const from data causing a build warning. Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 2 +- include/asm-generic/gpio.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e48b70c6ddf1..120b2a0e3167 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1154,7 +1154,7 @@ EXPORT_SYMBOL_GPL(gpiochip_remove); * non-zero, this function will return to the caller and not iterate over any * more gpio_chips. */ -struct gpio_chip *gpiochip_find(const void *data, +struct gpio_chip *gpiochip_find(void *data, int (*match)(struct gpio_chip *chip, void *data)) { diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 1ba08f0e49a3..365ea09ed3b0 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -142,7 +142,7 @@ extern int __must_check gpiochip_reserve(int start, int ngpio); /* add/remove chips */ extern int gpiochip_add(struct gpio_chip *chip); extern int __must_check gpiochip_remove(struct gpio_chip *chip); -extern struct gpio_chip *gpiochip_find(const void *data, +extern struct gpio_chip *gpiochip_find(void *data, int (*match)(struct gpio_chip *chip, void *data)); From 3e11f7b840b4671213c66817294ad7dd0b572756 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sat, 19 May 2012 21:34:58 +0800 Subject: [PATCH 33/33] gpio/generic: initialize basic_mmio_gpio shadow variables properly It fixes the issue in gpio-generic that commit fb14921 (gpio/mxc: add missing initialization of basic_mmio_gpio shadow variables) manged to fix in gpio-mxc driver, so that other platform specific drivers do not suffer from the same problem over and over again. Changes since v1: * Turn the last parameter of bgpio_init() "bool big_endian" into "unsigned long flags" and give those really quirky hardwares a chance to tell that reg_set and reg_dir are unreadable. Signed-off-by: Shawn Guo [grant.likely: Fix big-endian usage to explicitly set BBGPIOF_BIG_ENDIAN] Signed-off-by: Grant Likely --- drivers/gpio/gpio-ep93xx.c | 2 +- drivers/gpio/gpio-generic.c | 16 +++++++++++----- drivers/gpio/gpio-mxc.c | 2 +- drivers/gpio/gpio-mxs.c | 2 +- drivers/gpio/gpio-sodaville.c | 2 +- include/linux/basic_mmio_gpio.h | 6 +++++- 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 776b772523e5..9fe5b8fe9be8 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -325,7 +325,7 @@ static int ep93xx_gpio_add_bank(struct bgpio_chip *bgc, struct device *dev, void __iomem *dir = mmio_base + bank->dir; int err; - err = bgpio_init(bgc, dev, 1, data, NULL, NULL, dir, NULL, false); + err = bgpio_init(bgc, dev, 1, data, NULL, NULL, dir, NULL, 0); if (err) return err; diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c index e38dd0c31973..82e2e4fe599e 100644 --- a/drivers/gpio/gpio-generic.c +++ b/drivers/gpio/gpio-generic.c @@ -364,7 +364,7 @@ EXPORT_SYMBOL_GPL(bgpio_remove); int bgpio_init(struct bgpio_chip *bgc, struct device *dev, unsigned long sz, void __iomem *dat, void __iomem *set, void __iomem *clr, void __iomem *dirout, void __iomem *dirin, - bool big_endian) + unsigned long flags) { int ret; @@ -385,7 +385,7 @@ int bgpio_init(struct bgpio_chip *bgc, struct device *dev, if (ret) return ret; - ret = bgpio_setup_accessors(dev, bgc, big_endian); + ret = bgpio_setup_accessors(dev, bgc, flags & BGPIOF_BIG_ENDIAN); if (ret) return ret; @@ -394,6 +394,11 @@ int bgpio_init(struct bgpio_chip *bgc, struct device *dev, return ret; bgc->data = bgc->read_reg(bgc->reg_dat); + if (bgc->gc.set == bgpio_set_set && + !(flags & BGPIOF_UNREADABLE_REG_SET)) + bgc->data = bgc->read_reg(bgc->reg_set); + if (bgc->reg_dir && !(flags & BGPIOF_UNREADABLE_REG_DIR)) + bgc->dir = bgc->read_reg(bgc->reg_dir); return ret; } @@ -449,7 +454,7 @@ static int __devinit bgpio_pdev_probe(struct platform_device *pdev) void __iomem *dirout; void __iomem *dirin; unsigned long sz; - bool be; + unsigned long flags = 0; int err; struct bgpio_chip *bgc; struct bgpio_pdata *pdata = dev_get_platdata(dev); @@ -480,13 +485,14 @@ static int __devinit bgpio_pdev_probe(struct platform_device *pdev) if (err) return err; - be = !strcmp(platform_get_device_id(pdev)->name, "basic-mmio-gpio-be"); + if (!strcmp(platform_get_device_id(pdev)->name, "basic-mmio-gpio-be")) + flags |= BGPIOF_BIG_ENDIAN; 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); + err = bgpio_init(bgc, dev, sz, dat, set, clr, dirout, dirin, flags); if (err) return err; diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index e79147634573..c337143b18f8 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -417,7 +417,7 @@ static int __devinit mxc_gpio_probe(struct platform_device *pdev) err = bgpio_init(&port->bgc, &pdev->dev, 4, port->base + GPIO_PSR, port->base + GPIO_DR, NULL, - port->base + GPIO_GDIR, NULL, false); + port->base + GPIO_GDIR, NULL, 0); if (err) goto out_iounmap; diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index 385c58e8405b..b4136501abd8 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -244,7 +244,7 @@ static int __devinit mxs_gpio_probe(struct platform_device *pdev) 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); + port->base + PINCTRL_DOE(port->id), NULL, 0); if (err) goto out_iounmap; diff --git a/drivers/gpio/gpio-sodaville.c b/drivers/gpio/gpio-sodaville.c index 820209c420e3..9d9891f7a607 100644 --- a/drivers/gpio/gpio-sodaville.c +++ b/drivers/gpio/gpio-sodaville.c @@ -224,7 +224,7 @@ static int __devinit sdv_gpio_probe(struct pci_dev *pdev, ret = bgpio_init(&sd->bgpio, &pdev->dev, 4, sd->gpio_pub_base + GPINR, sd->gpio_pub_base + GPOUTR, - NULL, sd->gpio_pub_base + GPOER, NULL, false); + NULL, sd->gpio_pub_base + GPOER, NULL, 0); if (ret) goto unmap; sd->bgpio.gc.ngpio = SDV_NUM_PUB_GPIOS; diff --git a/include/linux/basic_mmio_gpio.h b/include/linux/basic_mmio_gpio.h index feb912196745..1c504ca5bdb3 100644 --- a/include/linux/basic_mmio_gpio.h +++ b/include/linux/basic_mmio_gpio.h @@ -67,6 +67,10 @@ int bgpio_remove(struct bgpio_chip *bgc); int bgpio_init(struct bgpio_chip *bgc, struct device *dev, unsigned long sz, void __iomem *dat, void __iomem *set, void __iomem *clr, void __iomem *dirout, void __iomem *dirin, - bool big_endian); + unsigned long flags); + +#define BGPIOF_BIG_ENDIAN BIT(0) +#define BGPIOF_UNREADABLE_REG_SET BIT(1) /* reg_set is unreadable */ +#define BGPIOF_UNREADABLE_REG_DIR BIT(2) /* reg_dir is unreadable */ #endif /* __BASIC_MMIO_GPIO_H */