diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-26 09:35:29 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-26 09:35:29 -0800 |
commit | 4c8c225abf972ce422c241579ce1d4d27eaeb166 (patch) | |
tree | 77bc67defdc53c494b20632e66b82ce9be3c06af /drivers/gpio | |
parent | 3eb05225ee8efb81fe50558f5f9d94e7477ade8f (diff) | |
parent | 9170100ee46402af6d318134525c728027318d67 (diff) |
Merge tag 'gpio-for-linus' of git://git.secretlab.ca/git/linux
Pull GPIO changes from Grant Likely:
"This branch contains the usual set of individual driver improvements
and bug fixes, as well as updates to the core code. The more notable
changes include:
- Internally add new API for referencing GPIOs by gpio_desc instead
of number. Eventually this will become a public API
- ACPI GPIO binding support"
* tag 'gpio-for-linus' of git://git.secretlab.ca/git/linux: (33 commits)
arm64: select ARCH_WANT_OPTIONAL_GPIOLIB
gpio: em: Use irq_domain_add_simple() to fix runtime error
gpio: using common order: let 'static const' instead of 'const static'
gpio/vt8500: memory cleanup missing
gpiolib: Fix locking on gpio debugfs files
gpiolib: let gpio_chip reference its descriptors
gpiolib: use descriptors internally
gpiolib: use gpio_chips list in gpiochip_find_base
gpiolib: use gpio_chips list in sysfs ops
gpiolib: use gpio_chips list in gpiochip_find
gpiolib: use gpio_chips list in gpiolib_sysfs_init
gpiolib: link all gpio_chips using a list
gpio/langwell: cleanup driver
gpio/langwell: Add Cloverview ids to pci device table
gpio/lynxpoint: add chipset gpio driver.
gpiolib: add missing braces in gpio_direction_show
gpiolib-acpi: Fix error checks in interrupt requesting
gpio: mpc8xxx: don't set IRQ_TYPE_NONE when creating irq mapping
gpiolib: remove gpiochip_reserve()
arm: pxa: tosa: do not use gpiochip_reserve()
...
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 11 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 4 | ||||
-rw-r--r-- | drivers/gpio/gpio-em.c | 3 | ||||
-rw-r--r-- | drivers/gpio/gpio-langwell.c | 53 | ||||
-rw-r--r-- | drivers/gpio/gpio-lynxpoint.c | 469 | ||||
-rw-r--r-- | drivers/gpio/gpio-mpc8xxx.c | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-mxs.c | 31 | ||||
-rw-r--r-- | drivers/gpio/gpio-omap.c | 6 | ||||
-rw-r--r-- | drivers/gpio/gpio-pca953x.c | 380 | ||||
-rw-r--r-- | drivers/gpio/gpio-pl061.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-pxa.c | 7 | ||||
-rw-r--r-- | drivers/gpio/gpio-twl4030.c | 176 | ||||
-rw-r--r-- | drivers/gpio/gpio-vt8500.c | 65 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-acpi.c | 87 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.c | 759 |
15 files changed, 1454 insertions, 600 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 74e17f19cc33..93aaadf99f28 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -30,6 +30,9 @@ config ARCH_REQUIRE_GPIOLIB Selecting this from the architecture code will cause the gpiolib code to always get built in. +config GPIO_DEVRES + def_bool y + depends on HAS_IOMEM menuconfig GPIOLIB @@ -298,6 +301,14 @@ config GPIO_GE_FPGA and write pin state) for GPIO implemented in a number of GE single board computers. +config GPIO_LYNXPOINT + bool "Intel Lynxpoint GPIO support" + depends on ACPI + select IRQ_DOMAIN + help + driver for GPIO functionality on Intel Lynxpoint PCH chipset + Requires ACPI device enumeration code to set up a platform device. + comment "I2C GPIO expanders:" config GPIO_ARIZONA diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 6dbcba2e5cac..22e07bc9fcb5 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -2,7 +2,8 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG -obj-$(CONFIG_GPIOLIB) += gpiolib.o devres.o +obj-$(CONFIG_GPIO_DEVRES) += devres.o +obj-$(CONFIG_GPIOLIB) += gpiolib.o obj-$(CONFIG_OF_GPIO) += gpiolib-of.o obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o @@ -30,6 +31,7 @@ obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o obj-$(CONFIG_GPIO_LANGWELL) += gpio-langwell.o obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o +obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index bdc8302e711a..deca78f99316 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -299,8 +299,9 @@ static int em_gio_probe(struct platform_device *pdev) irq_chip->irq_set_type = em_gio_irq_set_type; irq_chip->flags = IRQCHIP_SKIP_SET_WAKE; - p->irq_domain = irq_domain_add_linear(pdev->dev.of_node, + p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, pdata->number_of_pins, + pdata->irq_base, &em_gio_irq_domain_ops, p); if (!p->irq_domain) { ret = -ENXIO; diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c index e77b2b3e94af..634c3d37f7b5 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -71,10 +71,12 @@ struct lnw_gpio { struct irq_domain *domain; }; +#define to_lnw_priv(chip) container_of(chip, struct lnw_gpio, chip) + static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset, enum GPIO_REG reg_type) { - struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + struct lnw_gpio *lnw = to_lnw_priv(chip); unsigned nreg = chip->ngpio / 32; u8 reg = offset / 32; void __iomem *ptr; @@ -86,7 +88,7 @@ static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset, static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset, enum GPIO_REG reg_type) { - struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + struct lnw_gpio *lnw = to_lnw_priv(chip); unsigned nreg = chip->ngpio / 32; u8 reg = offset / 16; void __iomem *ptr; @@ -130,7 +132,7 @@ static void lnw_gpio_set(struct gpio_chip *chip, unsigned offset, int value) static int lnw_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { - struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + struct lnw_gpio *lnw = to_lnw_priv(chip); void __iomem *gpdr = gpio_reg(chip, offset, GPDR); u32 value; unsigned long flags; @@ -153,7 +155,7 @@ static int lnw_gpio_direction_input(struct gpio_chip *chip, unsigned offset) static int lnw_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { - struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + struct lnw_gpio *lnw = to_lnw_priv(chip); void __iomem *gpdr = gpio_reg(chip, offset, GPDR); unsigned long flags; @@ -176,7 +178,7 @@ 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); + struct lnw_gpio *lnw = to_lnw_priv(chip); return irq_create_mapping(lnw->domain, offset); } @@ -234,6 +236,8 @@ static DEFINE_PCI_DEVICE_TABLE(lnw_gpio_ids) = { /* pin number */ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080f), .driver_data = 64 }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081f), .driver_data = 96 }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081a), .driver_data = 96 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08eb), .driver_data = 96 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08f7), .driver_data = 96 }, { 0, } }; MODULE_DEVICE_TABLE(pci, lnw_gpio_ids); @@ -299,17 +303,6 @@ static const struct irq_domain_ops lnw_gpio_irq_ops = { .xlate = irq_domain_xlate_twocell, }; -#ifdef CONFIG_PM -static int lnw_gpio_runtime_resume(struct device *dev) -{ - return 0; -} - -static int lnw_gpio_runtime_suspend(struct device *dev) -{ - return 0; -} - static int lnw_gpio_runtime_idle(struct device *dev) { int err = pm_schedule_suspend(dev, 500); @@ -320,16 +313,8 @@ static int lnw_gpio_runtime_idle(struct device *dev) return -EBUSY; } -#else -#define lnw_gpio_runtime_suspend NULL -#define lnw_gpio_runtime_resume NULL -#define lnw_gpio_runtime_idle NULL -#endif - static const struct dev_pm_ops lnw_gpio_pm_ops = { - .runtime_suspend = lnw_gpio_runtime_suspend, - .runtime_resume = lnw_gpio_runtime_resume, - .runtime_idle = lnw_gpio_runtime_idle, + SET_RUNTIME_PM_OPS(NULL, NULL, lnw_gpio_runtime_idle) }; static int lnw_gpio_probe(struct pci_dev *pdev, @@ -349,7 +334,7 @@ static int lnw_gpio_probe(struct pci_dev *pdev, retval = pci_request_regions(pdev, "langwell_gpio"); if (retval) { dev_err(&pdev->dev, "error requesting resources\n"); - goto err2; + goto err_pci_req_region; } /* get the gpio_base from bar1 */ start = pci_resource_start(pdev, 1); @@ -358,7 +343,7 @@ static int lnw_gpio_probe(struct pci_dev *pdev, if (!base) { dev_err(&pdev->dev, "error mapping bar1\n"); retval = -EFAULT; - goto err3; + goto err_ioremap; } gpio_base = *((u32 *)base + 1); /* release the IO mapping, since we already get the info from bar1 */ @@ -370,21 +355,21 @@ static int lnw_gpio_probe(struct pci_dev *pdev, if (!base) { dev_err(&pdev->dev, "error mapping bar0\n"); retval = -EFAULT; - goto err3; + goto err_ioremap; } - lnw = devm_kzalloc(&pdev->dev, sizeof(struct lnw_gpio), GFP_KERNEL); + lnw = devm_kzalloc(&pdev->dev, sizeof(*lnw), GFP_KERNEL); if (!lnw) { dev_err(&pdev->dev, "can't allocate langwell_gpio chip data\n"); retval = -ENOMEM; - goto err3; + goto err_ioremap; } lnw->domain = irq_domain_add_linear(pdev->dev.of_node, ngpio, &lnw_gpio_irq_ops, lnw); if (!lnw->domain) { retval = -ENOMEM; - goto err3; + goto err_ioremap; } lnw->reg_base = base; @@ -403,7 +388,7 @@ static int 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 err3; + goto err_ioremap; } lnw_irq_init_hw(lnw); @@ -418,9 +403,9 @@ static int lnw_gpio_probe(struct pci_dev *pdev, return 0; -err3: +err_ioremap: pci_release_regions(pdev); -err2: +err_pci_req_region: pci_disable_device(pdev); return retval; } diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c new file mode 100644 index 000000000000..3472b05ac512 --- /dev/null +++ b/drivers/gpio/gpio-lynxpoint.c @@ -0,0 +1,469 @@ +/* + * GPIO controller driver for Intel Lynxpoint PCH chipset> + * Copyright (c) 2012, Intel Corporation. + * + * Author: Mathias Nyman <mathias.nyman@linux.intel.com> + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/bitops.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/irqdomain.h> +#include <linux/slab.h> +#include <linux/acpi.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> + +/* LynxPoint chipset has support for 94 gpio pins */ + +#define LP_NUM_GPIO 94 + +/* Bitmapped register offsets */ +#define LP_ACPI_OWNED 0x00 /* Bitmap, set by bios, 0: pin reserved for ACPI */ +#define LP_GC 0x7C /* set APIC IRQ to IRQ14 or IRQ15 for all pins */ +#define LP_INT_STAT 0x80 +#define LP_INT_ENABLE 0x90 + +/* Each pin has two 32 bit config registers, starting at 0x100 */ +#define LP_CONFIG1 0x100 +#define LP_CONFIG2 0x104 + +/* LP_CONFIG1 reg bits */ +#define OUT_LVL_BIT BIT(31) +#define IN_LVL_BIT BIT(30) +#define TRIG_SEL_BIT BIT(4) /* 0: Edge, 1: Level */ +#define INT_INV_BIT BIT(3) /* Invert interrupt triggering */ +#define DIR_BIT BIT(2) /* 0: Output, 1: Input */ +#define USE_SEL_BIT BIT(0) /* 0: Native, 1: GPIO */ + +/* LP_CONFIG2 reg bits */ +#define GPINDIS_BIT BIT(2) /* disable input sensing */ +#define GPIWP_BIT (BIT(0) | BIT(1)) /* weak pull options */ + +struct lp_gpio { + struct gpio_chip chip; + struct irq_domain *domain; + struct platform_device *pdev; + spinlock_t lock; + unsigned long reg_base; +}; + +/* + * Lynxpoint gpios are controlled through both bitmapped registers and + * per gpio specific registers. The bitmapped registers are in chunks of + * 3 x 32bit registers to cover all 94 gpios + * + * per gpio specific registers consist of two 32bit registers per gpio + * (LP_CONFIG1 and LP_CONFIG2), with 94 gpios there's a total of + * 188 config registes. + * + * A simplified view of the register layout look like this: + * + * LP_ACPI_OWNED[31:0] gpio ownerships for gpios 0-31 (bitmapped registers) + * LP_ACPI_OWNED[63:32] gpio ownerships for gpios 32-63 + * LP_ACPI_OWNED[94:64] gpio ownerships for gpios 63-94 + * ... + * LP_INT_ENABLE[31:0] ... + * LP_INT_ENABLE[63:31] ... + * LP_INT_ENABLE[94:64] ... + * LP0_CONFIG1 (gpio 0) config1 reg for gpio 0 (per gpio registers) + * LP0_CONFIG2 (gpio 0) config2 reg for gpio 0 + * LP1_CONFIG1 (gpio 1) config1 reg for gpio 1 + * LP1_CONFIG2 (gpio 1) config2 reg for gpio 1 + * LP2_CONFIG1 (gpio 2) ... + * LP2_CONFIG2 (gpio 2) ... + * ... + * LP94_CONFIG1 (gpio 94) ... + * LP94_CONFIG2 (gpio 94) ... + */ + +static unsigned long lp_gpio_reg(struct gpio_chip *chip, unsigned offset, + int reg) +{ + struct lp_gpio *lg = container_of(chip, struct lp_gpio, chip); + int reg_offset; + + if (reg == LP_CONFIG1 || reg == LP_CONFIG2) + /* per gpio specific config registers */ + reg_offset = offset * 8; + else + /* bitmapped registers */ + reg_offset = (offset / 32) * 4; + + return lg->reg_base + reg + reg_offset; +} + +static int lp_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct lp_gpio *lg = container_of(chip, struct lp_gpio, chip); + unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1); + unsigned long conf2 = lp_gpio_reg(chip, offset, LP_CONFIG2); + unsigned long acpi_use = lp_gpio_reg(chip, offset, LP_ACPI_OWNED); + + pm_runtime_get(&lg->pdev->dev); /* should we put if failed */ + + /* Fail if BIOS reserved pin for ACPI use */ + if (!(inl(acpi_use) & BIT(offset % 32))) { + dev_err(&lg->pdev->dev, "gpio %d reserved for ACPI\n", offset); + return -EBUSY; + } + /* Fail if pin is in alternate function mode (not GPIO mode) */ + if (!(inl(reg) & USE_SEL_BIT)) + return -ENODEV; + + /* enable input sensing */ + outl(inl(conf2) & ~GPINDIS_BIT, conf2); + + + return 0; +} + +static void lp_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct lp_gpio *lg = container_of(chip, struct lp_gpio, chip); + unsigned long conf2 = lp_gpio_reg(chip, offset, LP_CONFIG2); + + /* disable input sensing */ + outl(inl(conf2) | GPINDIS_BIT, conf2); + + pm_runtime_put(&lg->pdev->dev); +} + +static int lp_irq_type(struct irq_data *d, unsigned type) +{ + struct lp_gpio *lg = irq_data_get_irq_chip_data(d); + u32 hwirq = irqd_to_hwirq(d); + unsigned long flags; + u32 value; + unsigned long reg = lp_gpio_reg(&lg->chip, hwirq, LP_CONFIG1); + + if (hwirq >= lg->chip.ngpio) + return -EINVAL; + + spin_lock_irqsave(&lg->lock, flags); + value = inl(reg); + + /* set both TRIG_SEL and INV bits to 0 for rising edge */ + if (type & IRQ_TYPE_EDGE_RISING) + value &= ~(TRIG_SEL_BIT | INT_INV_BIT); + + /* TRIG_SEL bit 0, INV bit 1 for falling edge */ + if (type & IRQ_TYPE_EDGE_FALLING) + value = (value | INT_INV_BIT) & ~TRIG_SEL_BIT; + + /* TRIG_SEL bit 1, INV bit 0 for level low */ + if (type & IRQ_TYPE_LEVEL_LOW) + value = (value | TRIG_SEL_BIT) & ~INT_INV_BIT; + + /* TRIG_SEL bit 1, INV bit 1 for level high */ + if (type & IRQ_TYPE_LEVEL_HIGH) + value |= TRIG_SEL_BIT | INT_INV_BIT; + + outl(value, reg); + spin_unlock_irqrestore(&lg->lock, flags); + + return 0; +} + +static int lp_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1); + return inl(reg) & IN_LVL_BIT; +} + +static void lp_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct lp_gpio *lg = container_of(chip, struct lp_gpio, chip); + unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1); + unsigned long flags; + + spin_lock_irqsave(&lg->lock, flags); + + if (value) + outl(inl(reg) | OUT_LVL_BIT, reg); + else + outl(inl(reg) & ~OUT_LVL_BIT, reg); + + spin_unlock_irqrestore(&lg->lock, flags); +} + +static int lp_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct lp_gpio *lg = container_of(chip, struct lp_gpio, chip); + unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1); + unsigned long flags; + + spin_lock_irqsave(&lg->lock, flags); + outl(inl(reg) | DIR_BIT, reg); + spin_unlock_irqrestore(&lg->lock, flags); + + return 0; +} + +static int lp_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct lp_gpio *lg = container_of(chip, struct lp_gpio, chip); + unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1); + unsigned long flags; + + lp_gpio_set(chip, offset, value); + + spin_lock_irqsave(&lg->lock, flags); + outl(inl(reg) & ~DIR_BIT, reg); + spin_unlock_irqrestore(&lg->lock, flags); + + return 0; +} + +static int lp_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct lp_gpio *lg = container_of(chip, struct lp_gpio, chip); + return irq_create_mapping(lg->domain, offset); +} + +static void lp_gpio_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct irq_data *data = irq_desc_get_irq_data(desc); + struct lp_gpio *lg = irq_data_get_irq_handler_data(data); + struct irq_chip *chip = irq_data_get_irq_chip(data); + u32 base, pin, mask; + unsigned long reg, pending; + unsigned virq; + + /* check from GPIO controller which pin triggered the interrupt */ + for (base = 0; base < lg->chip.ngpio; base += 32) { + reg = lp_gpio_reg(&lg->chip, base, LP_INT_STAT); + + while ((pending = inl(reg))) { + pin = __ffs(pending); + mask = BIT(pin); + /* Clear before handling so we don't lose an edge */ + outl(mask, reg); + virq = irq_find_mapping(lg->domain, base + pin); + generic_handle_irq(virq); + } + } + chip->irq_eoi(data); +} + +static void lp_irq_unmask(struct irq_data *d) +{ +} + +static void lp_irq_mask(struct irq_data *d) +{ +} + +static void lp_irq_enable(struct irq_data *d) +{ + struct lp_gpio *lg = irq_data_get_irq_chip_data(d); + u32 hwirq = irqd_to_hwirq(d); + unsigned long reg = lp_gpio_reg(&lg->chip, hwirq, LP_INT_ENABLE); + unsigned long flags; + + spin_lock_irqsave(&lg->lock, flags); + outl(inl(reg) | BIT(hwirq % 32), reg); + spin_unlock_irqrestore(&lg->lock, flags); +} + +static void lp_irq_disable(struct irq_data *d) +{ + struct lp_gpio *lg = irq_data_get_irq_chip_data(d); + u32 hwirq = irqd_to_hwirq(d); + unsigned long reg = lp_gpio_reg(&lg->chip, hwirq, LP_INT_ENABLE); + unsigned long flags; + + spin_lock_irqsave(&lg->lock, flags); + outl(inl(reg) & ~BIT(hwirq % 32), reg); + spin_unlock_irqrestore(&lg->lock, flags); +} + +static struct irq_chip lp_irqchip = { + .name = "LP-GPIO", + .irq_mask = lp_irq_mask, + .irq_unmask = lp_irq_unmask, + .irq_enable = lp_irq_enable, + .irq_disable = lp_irq_disable, + .irq_set_type = lp_irq_type, + .flags = IRQCHIP_SKIP_SET_WAKE, +}; + +static void lp_gpio_irq_init_hw(struct lp_gpio *lg) +{ + unsigned long reg; + unsigned base; + + for (base = 0; base < lg->chip.ngpio; base += 32) { + /* disable gpio pin interrupts */ + reg = lp_gpio_reg(&lg->chip, base, LP_INT_ENABLE); + outl(0, reg); + /* Clear interrupt status register */ + reg = lp_gpio_reg(&lg->chip, base, LP_INT_STAT); + outl(0xffffffff, reg); + } +} + +static int lp_gpio_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + struct lp_gpio *lg = d->host_data; + + irq_set_chip_and_handler_name(virq, &lp_irqchip, handle_simple_irq, + "demux"); + irq_set_chip_data(virq, lg); + irq_set_irq_type(virq, IRQ_TYPE_NONE); + + return 0; +} + +static const struct irq_domain_ops lp_gpio_irq_ops = { + .map = lp_gpio_irq_map, +}; + +static int lp_gpio_probe(struct platform_device *pdev) +{ + struct lp_gpio *lg; + struct gpio_chip *gc; + struct resource *io_rc, *irq_rc; + struct device *dev = &pdev->dev; + unsigned long reg_len; + unsigned hwirq; + int ret = -ENODEV; + + lg = devm_kzalloc(dev, sizeof(struct lp_gpio), GFP_KERNEL); + if (!lg) { + dev_err(dev, "can't allocate lp_gpio chip data\n"); + return -ENOMEM; + } + + lg->pdev = pdev; + platform_set_drvdata(pdev, lg); + + io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0); + irq_rc = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + + if (!io_rc) { + dev_err(dev, "missing IO resources\n"); + return -EINVAL; + } + + lg->reg_base = io_rc->start; + reg_len = resource_size(io_rc); + + if (!devm_request_region(dev, lg->reg_base, reg_len, "lp-gpio")) { + dev_err(dev, "failed requesting IO region 0x%x\n", + (unsigned int)lg->reg_base); + return -EBUSY; + } + + spin_lock_init(&lg->lock); + + gc = &lg->chip; + gc->label = dev_name(dev); + gc->owner = THIS_MODULE; + gc->request = lp_gpio_request; + gc->free = lp_gpio_free; + gc->direction_input = lp_gpio_direction_input; + gc->direction_output = lp_gpio_direction_output; + gc->get = lp_gpio_get; + gc->set = lp_gpio_set; + gc->base = -1; + gc->ngpio = LP_NUM_GPIO; + gc->can_sleep = 0; + gc->dev = dev; + + /* set up interrupts */ + if (irq_rc && irq_rc->start) { + hwirq = irq_rc->start; + gc->to_irq = lp_gpio_to_irq; + + lg->domain = irq_domain_add_linear(NULL, LP_NUM_GPIO, + &lp_gpio_irq_ops, lg); + if (!lg->domain) + return -ENXIO; + + lp_gpio_irq_init_hw(lg); + + irq_set_handler_data(hwirq, lg); + irq_set_chained_handler(hwirq, lp_gpio_irq_handler); + } + + ret = gpiochip_add(gc); + if (ret) { + dev_err(dev, "failed adding lp-gpio chip\n"); + return ret; + } + pm_runtime_enable(dev); + + return 0; +} + +static int lp_gpio_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int lp_gpio_runtime_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops lp_gpio_pm_ops = { + .runtime_suspend = lp_gpio_runtime_suspend, + .runtime_resume = lp_gpio_runtime_resume, +}; + +static const struct acpi_device_id lynxpoint_gpio_acpi_match[] = { + { "INT33C7", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, lynxpoint_gpio_acpi_match); + +static int lp_gpio_remove(struct platform_device *pdev) +{ + struct lp_gpio *lg = platform_get_drvdata(pdev); + int err; + err = gpiochip_remove(&lg->chip); + if (err) + dev_warn(&pdev->dev, "failed to remove gpio_chip.\n"); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver lp_gpio_driver = { + .probe = lp_gpio_probe, + .remove = lp_gpio_remove, + .driver = { + .name = "lp_gpio", + .owner = THIS_MODULE, + .pm = &lp_gpio_pm_ops, + .acpi_match_table = ACPI_PTR(lynxpoint_gpio_acpi_match), + }, +}; + +static int __init lp_gpio_init(void) +{ + return platform_driver_register(&lp_gpio_driver); +} + +subsys_initcall(lp_gpio_init); diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 9ae29cc0d17f..a0b33a216d4a 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -292,7 +292,6 @@ static int mpc8xxx_gpio_irq_map(struct irq_domain *h, unsigned int virq, irq_set_chip_data(virq, h->host_data); irq_set_chip_and_handler(virq, &mpc8xxx_irq_chip, handle_level_irq); - irq_set_irq_type(virq, IRQ_TYPE_NONE); return 0; } diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index 45d97c46831a..25000b0f8453 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -66,6 +66,7 @@ struct mxs_gpio_port { struct irq_domain *domain; struct bgpio_chip bgc; enum mxs_gpio_id devid; + u32 both_edges; }; static inline int is_imx23_gpio(struct mxs_gpio_port *port) @@ -82,13 +83,23 @@ static inline int is_imx28_gpio(struct mxs_gpio_port *port) static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type) { + u32 val; u32 pin_mask = 1 << d->hwirq; struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct mxs_gpio_port *port = gc->private; void __iomem *pin_addr; int edge; + port->both_edges &= ~pin_mask; switch (type) { + case IRQ_TYPE_EDGE_BOTH: + val = gpio_get_value(port->bgc.gc.base + d->hwirq); + if (val) + edge = GPIO_INT_FALL_EDGE; + else + edge = GPIO_INT_RISE_EDGE; + port->both_edges |= pin_mask; + break; case IRQ_TYPE_EDGE_RISING: edge = GPIO_INT_RISE_EDGE; break; @@ -125,6 +136,23 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type) return 0; } +static void mxs_flip_edge(struct mxs_gpio_port *port, u32 gpio) +{ + u32 bit, val, edge; + void __iomem *pin_addr; + + bit = 1 << gpio; + + pin_addr = port->base + PINCTRL_IRQPOL(port); + val = readl(pin_addr); + edge = val & bit; + + if (edge) + writel(bit, pin_addr + MXS_CLR); + else + writel(bit, pin_addr + MXS_SET); +} + /* MXS has one interrupt *per* gpio port */ static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc) { @@ -138,6 +166,9 @@ static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc) while (irq_stat != 0) { int irqoffset = fls(irq_stat) - 1; + if (port->both_edges & (1 << irqoffset)) + mxs_flip_edge(port, irqoffset); + generic_handle_irq(irq_find_mapping(port->domain, irqoffset)); irq_stat &= ~(1 << irqoffset); } diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index f1fbedb2a6f9..159f5c57eb45 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1476,19 +1476,19 @@ static struct omap_gpio_reg_offs omap4_gpio_regs = { .fallingdetect = OMAP4_GPIO_FALLINGDETECT, }; -const static struct omap_gpio_platform_data omap2_pdata = { +static const struct omap_gpio_platform_data omap2_pdata = { .regs = &omap2_gpio_regs, .bank_width = 32, .dbck_flag = false, }; -const static struct omap_gpio_platform_data omap3_pdata = { +static const struct omap_gpio_platform_data omap3_pdata = { .regs = &omap2_gpio_regs, .bank_width = 32, .dbck_flag = true, }; -const static struct omap_gpio_platform_data omap4_pdata = { +static const struct omap_gpio_platform_data omap4_pdata = { .regs = &omap4_gpio_regs, .bank_width = 32, .dbck_flag = true, diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index cc102d25ee24..24059462c87f 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -46,6 +46,7 @@ #define PCA957X_TYPE 0x2000 static const struct i2c_device_id pca953x_id[] = { + { "pca9505", 40 | PCA953X_TYPE | PCA_INT, }, { "pca9534", 8 | PCA953X_TYPE | PCA_INT, }, { "pca9535", 16 | PCA953X_TYPE | PCA_INT, }, { "pca9536", 4 | PCA953X_TYPE, }, @@ -71,19 +72,23 @@ static const struct i2c_device_id pca953x_id[] = { }; MODULE_DEVICE_TABLE(i2c, pca953x_id); +#define MAX_BANK 5 +#define BANK_SZ 8 + +#define NBANK(chip) (chip->gpio_chip.ngpio / BANK_SZ) + struct pca953x_chip { unsigned gpio_start; - u32 reg_output; - u32 reg_direction; + u8 reg_output[MAX_BANK]; + u8 reg_direction[MAX_BANK]; struct mutex i2c_lock; #ifdef CONFIG_GPIO_PCA953X_IRQ struct mutex irq_lock; - u32 irq_mask; - u32 irq_stat; - u32 irq_trig_raise; - u32 irq_trig_fall; - int irq_base; + u8 irq_mask[MAX_BANK]; + u8 irq_stat[MAX_BANK]; + u8 irq_trig_raise[MAX_BANK]; + u8 irq_trig_fall[MAX_BANK]; struct irq_domain *domain; #endif @@ -93,33 +98,69 @@ struct pca953x_chip { int chip_type; }; -static int pca953x_write_reg(struct pca953x_chip *chip, int reg, u32 val) +static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val, + int off) +{ + int ret; + int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); + int offset = off / BANK_SZ; + + ret = i2c_smbus_read_byte_data(chip->client, + (reg << bank_shift) + offset); + *val = ret; + + if (ret < 0) { + dev_err(&chip->client->dev, "failed reading register\n"); + return ret; + } + + return 0; +} + +static int pca953x_write_single(struct pca953x_chip *chip, int reg, u32 val, + int off) +{ + int ret = 0; + int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); + int offset = off / BANK_SZ; + + ret = i2c_smbus_write_byte_data(chip->client, + (reg << bank_shift) + offset, val); + + if (ret < 0) { + dev_err(&chip->client->dev, "failed writing register\n"); + return ret; + } + + return 0; +} + +static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *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) { - cpu_to_le32s(&val); + ret = i2c_smbus_write_byte_data(chip->client, reg, *val); + else if (chip->gpio_chip.ngpio >= 24) { + int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); ret = i2c_smbus_write_i2c_block_data(chip->client, - (reg << 2) | REG_ADDR_AI, - 3, - (u8 *) &val); + (reg << bank_shift) | REG_ADDR_AI, + NBANK(chip), val); } else { switch (chip->chip_type) { case PCA953X_TYPE: ret = i2c_smbus_write_word_data(chip->client, - reg << 1, val); + reg << 1, (u16) *val); break; case PCA957X_TYPE: ret = i2c_smbus_write_byte_data(chip->client, reg << 1, - val & 0xff); + val[0]); if (ret < 0) break; ret = i2c_smbus_write_byte_data(chip->client, (reg << 1) + 1, - (val & 0xff00) >> 8); + val[1]); break; } } @@ -132,26 +173,24 @@ static int pca953x_write_reg(struct pca953x_chip *chip, int reg, u32 val) return 0; } -static int pca953x_read_reg(struct pca953x_chip *chip, int reg, u32 *val) +static int pca953x_read_regs(struct pca953x_chip *chip, int reg, u8 *val) { int ret; if (chip->gpio_chip.ngpio <= 8) { ret = i2c_smbus_read_byte_data(chip->client, reg); *val = ret; - } - else if (chip->gpio_chip.ngpio == 24) { - *val = 0; + } else if (chip->gpio_chip.ngpio >= 24) { + int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); + ret = i2c_smbus_read_i2c_block_data(chip->client, - (reg << 2) | REG_ADDR_AI, - 3, - (u8 *) val); - le32_to_cpus(val); + (reg << bank_shift) | REG_ADDR_AI, + NBANK(chip), val); } else { ret = i2c_smbus_read_word_data(chip->client, reg << 1); - *val = ret; + val[0] = (u16)ret & 0xFF; + val[1] = (u16)ret >> 8; } - if (ret < 0) { dev_err(&chip->client->dev, "failed reading register\n"); return ret; @@ -163,13 +202,13 @@ static int pca953x_read_reg(struct pca953x_chip *chip, int reg, u32 *val) static int pca953x_gpio_direction_input |