diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-24 14:01:46 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-24 14:01:46 -0700 |
commit | b1bf7d4d1b3911352cf1ec63c1de06214288ccd0 (patch) | |
tree | 0fd27c638977cb5c7e6e3f95085ce842b57a4ae3 /drivers/gpio | |
parent | 0708500d49e8439d9fe5529795bdc1485f0f46c3 (diff) | |
parent | 3e11f7b840b4671213c66817294ad7dd0b572756 (diff) |
Merge tag 'gpio-for-linus' of git://git.secretlab.ca/git/linux-2.6
Pull GPIO driver changes from Grant Likely:
"Lots of gpio changes, both to core code and drivers.
Changes do touch architecture code to remove the need for separate
arm/gpio.h includes in most architectures.
Some new drivers are added, and a number of gpio drivers are converted
to use irq_domains for gpio inputs used as interrupts. Device tree
support has been amended to allow multiple gpio_chips to use the same
device tree node.
Remaining changes are primarily bug fixes."
* tag 'gpio-for-linus' of git://git.secretlab.ca/git/linux-2.6: (33 commits)
gpio/generic: initialize basic_mmio_gpio shadow variables properly
gpiolib: Remove 'const' from data argument of gpiochip_find()
gpio/rc5t583: add gpio driver for RICOH PMIC RC5T583
gpiolib: quiet gpiochip_add boot message noise
gpio: mpc8xxx: Prevent NULL pointer deref in demux handler
gpio/lpc32xx: Add device tree support
gpio: Adjust of_xlate API to support multiple GPIO chips
gpiolib: Implement devm_gpio_request_one()
gpio-mcp23s08: dbg_show: fix pullup configuration display
Add support for TCA6424A
gpio/omap: (re)fix wakeups on level-triggered GPIOs
gpio/omap: fix broken context restore for non-OFF mode transitions
gpio/omap: fix missing check in *_runtime_suspend()
gpio/omap: remove cpu_is_omapxxxx() checks from *_runtime_resume()
gpio/omap: remove suspend/resume callbacks
gpio/omap: remove retrigger variable in gpio_irq_handler
gpio/omap: remove saved_wakeup field from struct gpio_bank
gpio/omap: remove suspend_wakeup field from struct gpio_bank
gpio/omap: remove saved_fallingdetect, saved_risingdetect
gpio/omap: remove virtual_irq_start variable
...
Conflicts:
drivers/gpio/gpio-samsung.c
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 39 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 3 | ||||
-rw-r--r-- | drivers/gpio/devres.c | 29 | ||||
-rw-r--r-- | drivers/gpio/gpio-bt8xx.c | 12 | ||||
-rw-r--r-- | drivers/gpio/gpio-ep93xx.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-generic.c | 16 | ||||
-rw-r--r-- | drivers/gpio/gpio-langwell.c | 91 | ||||
-rw-r--r-- | drivers/gpio/gpio-lpc32xx.c | 52 | ||||
-rw-r--r-- | drivers/gpio/gpio-mcp23s08.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-ml-ioh.c | 12 | ||||
-rw-r--r-- | drivers/gpio/gpio-mpc8xxx.c | 3 | ||||
-rw-r--r-- | drivers/gpio/gpio-msic.c | 339 | ||||
-rw-r--r-- | drivers/gpio/gpio-mxc.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-mxs.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-omap.c | 106 | ||||
-rw-r--r-- | drivers/gpio/gpio-pca953x.c | 43 | ||||
-rw-r--r-- | drivers/gpio/gpio-pch.c | 12 | ||||
-rw-r--r-- | drivers/gpio/gpio-rc5t583.c | 180 | ||||
-rw-r--r-- | drivers/gpio/gpio-samsung.c | 407 | ||||
-rw-r--r-- | drivers/gpio/gpio-sodaville.c | 14 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-of.c | 236 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.c | 18 |
22 files changed, 1240 insertions, 380 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index eb80ba30045..aa3642cb820 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 @@ -37,6 +45,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 @@ -249,7 +261,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 @@ -258,10 +270,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" @@ -294,6 +307,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 @@ -405,6 +427,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. @@ -520,4 +543,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 708ffb2165e..07a79e24540 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 @@ -33,6 +34,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 @@ -43,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/devres.c b/drivers/gpio/devres.c index 8950f6261bb..9e9947cb86a 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -71,6 +71,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 * @gpio: gpio to free diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c index 5ca4098ba09..e4cc7eb69bb 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-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 776b772523e..9fe5b8fe9be 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 e38dd0c3197..82e2e4fe599 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-langwell.c b/drivers/gpio/gpio-langwell.c index 00692e89ef8..a1c8754f52c 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -36,6 +36,7 @@ #include <linux/gpio.h> #include <linux/slab.h> #include <linux/pm_runtime.h> +#include <linux/irqdomain.h> /* * 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); @@ -249,20 +250,55 @@ 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(lnw->irq_base + base + gpio); + generic_handle_irq(irq_find_mapping(lnw->domain, + base + gpio)); } } 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); + } +} + +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) { @@ -300,23 +336,22 @@ 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; retval = pci_enable_device(pdev); if (retval) - goto done; + return retval; retval = pci_request_regions(pdev, "langwell_gpio"); if (retval) { dev_err(&pdev->dev, "error requesting resources\n"); goto err2; } - /* get the irq_base from bar1 */ + /* get the gpio_base from bar1 */ start = pci_resource_start(pdev, 1); len = pci_resource_len(pdev, 1); base = ioremap_nocache(start, len); @@ -324,28 +359,32 @@ 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); /* 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->domain = irq_domain_add_linear(pdev->dev.of_node, ngpio, + &lnw_gpio_irq_ops, lnw); + if (!lnw->domain) + goto err3; + 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,38 +393,32 @@ 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 err5; + 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); 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; } diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c index 61c2d08d37b..c2199beca98 100644 --- a/drivers/gpio/gpio-lpc32xx.c +++ b/drivers/gpio/gpio-lpc32xx.c @@ -21,6 +21,9 @@ #include <linux/io.h> #include <linux/errno.h> #include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/platform_device.h> +#include <linux/module.h> #include <mach/hardware.h> #include <mach/platform.h> @@ -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); diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c index c5d83a8a91c..0f425189de1 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"); } diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index f0febe5b822..db01f151d41 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-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index e6568c19c93..5a1817eedd1 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) diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c new file mode 100644 index 00000000000..71a838f4450 --- /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 <mathias.nyman@linux.intel.com> + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/mfd/intel_msic.h> + +/* 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); + |