aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-10-02 16:05:10 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-02 16:05:10 -0700
commitdff8360a4a079692e65e55fbaa6c5dc605528403 (patch)
tree0ab8ef7595cdfb918b3fd9a8364c6ea6c9c2798f /drivers
parent916082b073ebb7f4e064cebce0768e34cacde508 (diff)
parent901acf5b2910434501c221a363bb3486b647b5c4 (diff)
downloadlinux-linaro-stable-dff8360a4a079692e65e55fbaa6c5dc605528403.tar.gz
Merge tag 'gpio-for-v3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio
Pull GPIO changes from Linus Walleij: "So this is the LW GPIO patch stack for v3.7: - refactoring from Thierry Redding at Arnd Bergmann's request to use the seq_file iterator interface in gpiolib. - A new driver for Avionic Design's N-bit GPIO expander. - Two instances of mutexes replaced by spinlocks from Axel Lin to code that is supposed to be fastpath compliant. - IRQ demuxer and gpio_to_irq() support for pcf857x by Kuninori Morimoto. - Dynamic GPIO numbers, device tree support, daisy chaining and some other fixes for the 74x164 driver by Maxime Ripard. - IRQ domain and device tree support for the tc3589x driver by Lee Jones. - Some conversion to use managed resources devm_* code. - Some instances of clk_prepare() or clk_prepare_enable() added to support the new, stricter common clock framework. - Some for_each_set_bit() simplifications. - Then a lot of fixes as we fixed up all of the above tripping over our own shoelaces and that kind of thing." * tag 'gpio-for-v3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (34 commits) gpio: pcf857x: select IRQ_DOMAIN gpio: Document device_node's det_debounce gpio-lpc32xx: Add GPI_28 gpio: adnp: dt: Reference generic interrupt binding gpio: Add Avionic Design N-bit GPIO expander support gpio: pxa: using for_each_set_bit to simplify the code gpio_msm: using for_each_set_bit to simplify the code gpio: Enable the tc3298x GPIO expander driver for Device Tree gpio: Provide the tc3589x GPIO expander driver with an IRQ domain ARM: shmobile: kzm9g: use gpio-keys instead of gpio-keys-polled gpio: pcf857x: fixup smatch WARNING gpio: 74x164: Add support for the daisy-chaining gpio: 74x164: dts: Add documentation for the dt binding dt: Fix incorrect reference in gpio-led documentation gpio: 74x164: Add device tree support gpio: 74x164: Use dynamic gpio number assignment if no pdata is present gpio: 74x164: Use devm_kzalloc gpio: 74x164: Use module_spi_driver boiler plate function gpio: sx150x: Use irq_data_get_irq_chip_data() at appropriate places gpio: em: Use irq_data_get_irq_chip_data() at appropriate places ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpio/Kconfig14
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-74x164.c103
-rw-r--r--drivers/gpio/gpio-adnp.c611
-rw-r--r--drivers/gpio/gpio-adp5588.c14
-rw-r--r--drivers/gpio/gpio-bt8xx.c2
-rw-r--r--drivers/gpio/gpio-da9052.c15
-rw-r--r--drivers/gpio/gpio-davinci.c2
-rw-r--r--drivers/gpio/gpio-em.c12
-rw-r--r--drivers/gpio/gpio-lpc32xx.c3
-rw-r--r--drivers/gpio/gpio-mc9s08dz60.c21
-rw-r--r--drivers/gpio/gpio-ml-ioh.c20
-rw-r--r--drivers/gpio/gpio-msm-v2.c4
-rw-r--r--drivers/gpio/gpio-pcf857x.c122
-rw-r--r--drivers/gpio/gpio-pch.c22
-rw-r--r--drivers/gpio/gpio-pxa.c13
-rw-r--r--drivers/gpio/gpio-sodaville.c2
-rw-r--r--drivers/gpio/gpio-sx150x.c24
-rw-r--r--drivers/gpio/gpio-tc3589x.c120
-rw-r--r--drivers/gpio/gpio-tps65912.c17
-rw-r--r--drivers/gpio/gpio-wm831x.c19
-rw-r--r--drivers/gpio/gpio-wm8350.c19
-rw-r--r--drivers/gpio/gpiolib.c102
23 files changed, 1039 insertions, 243 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index a00b828b1643..8382dc832929 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -82,7 +82,7 @@ config GPIO_GENERIC
config GPIO_DA9052
tristate "Dialog DA9052 GPIO"
- depends on PMIC_DA9052 && BROKEN
+ depends on PMIC_DA9052
help
Say yes here to enable the GPIO driver for the DA9052 chip.
@@ -330,6 +330,7 @@ config GPIO_PCA953X_IRQ
config GPIO_PCF857X
tristate "PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders"
depends on I2C
+ select IRQ_DOMAIN
help
Say yes here to provide access to most "quasi-bidirectional" I2C
GPIO expanders used for additional digital outputs or inputs.
@@ -450,6 +451,17 @@ config GPIO_ADP5588_IRQ
Say yes here to enable the adp5588 to be used as an interrupt
controller. It requires the driver to be built in the kernel.
+config GPIO_ADNP
+ tristate "Avionic Design N-bit GPIO expander"
+ depends on I2C && OF
+ help
+ This option enables support for N GPIOs found on Avionic Design
+ I2C GPIO expanders. The register space will be extended by powers
+ of two, so the controller will need to accomodate for that. For
+ example: if a controller provides 48 pins, 6 registers will be
+ enough to represent all pins, but the driver will assume a
+ register layout for 64 pins (8 registers).
+
comment "PCI GPIO expanders:"
config GPIO_CS5535
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index a288142ad998..0ffaa8423e87 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o
obj-$(CONFIG_GPIO_AB8500) += gpio-ab8500.o
+obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c
index a31ad6f5d910..ed3e55161bdc 100644
--- a/drivers/gpio/gpio-74x164.c
+++ b/drivers/gpio/gpio-74x164.c
@@ -14,14 +14,18 @@
#include <linux/spi/spi.h>
#include <linux/spi/74x164.h>
#include <linux/gpio.h>
+#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/module.h>
+#define GEN_74X164_NUMBER_GPIOS 8
+
struct gen_74x164_chip {
struct spi_device *spi;
+ u8 *buffer;
struct gpio_chip gpio_chip;
struct mutex lock;
- u8 port_config;
+ u32 registers;
};
static struct gen_74x164_chip *gpio_to_74x164_chip(struct gpio_chip *gc)
@@ -31,17 +35,47 @@ static struct gen_74x164_chip *gpio_to_74x164_chip(struct gpio_chip *gc)
static int __gen_74x164_write_config(struct gen_74x164_chip *chip)
{
- return spi_write(chip->spi,
- &chip->port_config, sizeof(chip->port_config));
+ struct spi_message message;
+ struct spi_transfer *msg_buf;
+ int i, ret = 0;
+
+ msg_buf = kzalloc(chip->registers * sizeof(struct spi_transfer),
+ GFP_KERNEL);
+ if (!msg_buf)
+ return -ENOMEM;
+
+ spi_message_init(&message);
+
+ /*
+ * Since the registers are chained, every byte sent will make
+ * the previous byte shift to the next register in the
+ * chain. Thus, the first byte send will end up in the last
+ * register at the end of the transfer. So, to have a logical
+ * numbering, send the bytes in reverse order so that the last
+ * byte of the buffer will end up in the last register.
+ */
+ for (i = chip->registers - 1; i >= 0; i--) {
+ msg_buf[i].tx_buf = chip->buffer +i;
+ msg_buf[i].len = sizeof(u8);
+ spi_message_add_tail(msg_buf + i, &message);
+ }
+
+ ret = spi_sync(chip->spi, &message);
+
+ kfree(msg_buf);
+
+ return ret;
}
static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset)
{
struct gen_74x164_chip *chip = gpio_to_74x164_chip(gc);
+ u8 bank = offset / 8;
+ u8 pin = offset % 8;
int ret;
mutex_lock(&chip->lock);
- ret = (chip->port_config >> offset) & 0x1;
+ ret = (chip->buffer[bank] >> pin) & 0x1;
mutex_unlock(&chip->lock);
return ret;
@@ -51,12 +85,14 @@ static void gen_74x164_set_value(struct gpio_chip *gc,
unsigned offset, int val)
{
struct gen_74x164_chip *chip = gpio_to_74x164_chip(gc);
+ u8 bank = offset / 8;
+ u8 pin = offset % 8;
mutex_lock(&chip->lock);
if (val)
- chip->port_config |= (1 << offset);
+ chip->buffer[bank] |= (1 << pin);
else
- chip->port_config &= ~(1 << offset);
+ chip->buffer[bank] &= ~(1 << pin);
__gen_74x164_write_config(chip);
mutex_unlock(&chip->lock);
@@ -75,9 +111,8 @@ static int __devinit gen_74x164_probe(struct spi_device *spi)
struct gen_74x164_chip_platform_data *pdata;
int ret;
- pdata = spi->dev.platform_data;
- if (!pdata || !pdata->base) {
- dev_dbg(&spi->dev, "incorrect or missing platform data\n");
+ if (!spi->dev.of_node) {
+ dev_err(&spi->dev, "No device tree data available.\n");
return -EINVAL;
}
@@ -90,10 +125,16 @@ static int __devinit gen_74x164_probe(struct spi_device *spi)
if (ret < 0)
return ret;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ chip = devm_kzalloc(&spi->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
+ pdata = spi->dev.platform_data;
+ if (pdata && pdata->base)
+ chip->gpio_chip.base = pdata->base;
+ else
+ chip->gpio_chip.base = -1;
+
mutex_init(&chip->lock);
dev_set_drvdata(&spi->dev, chip);
@@ -104,8 +145,20 @@ static int __devinit gen_74x164_probe(struct spi_device *spi)
chip->gpio_chip.direction_output = gen_74x164_direction_output;
chip->gpio_chip.get = gen_74x164_get_value;
chip->gpio_chip.set = gen_74x164_set_value;
- chip->gpio_chip.base = pdata->base;
- chip->gpio_chip.ngpio = 8;
+
+ if (of_property_read_u32(spi->dev.of_node, "registers-number", &chip->registers)) {
+ dev_err(&spi->dev, "Missing registers-number property in the DT.\n");
+ ret = -EINVAL;
+ goto exit_destroy;
+ }
+
+ chip->gpio_chip.ngpio = GEN_74X164_NUMBER_GPIOS * chip->registers;
+ chip->buffer = devm_kzalloc(&spi->dev, chip->gpio_chip.ngpio, GFP_KERNEL);
+ if (!chip->buffer) {
+ ret = -ENOMEM;
+ goto exit_destroy;
+ }
+
chip->gpio_chip.can_sleep = 1;
chip->gpio_chip.dev = &spi->dev;
chip->gpio_chip.owner = THIS_MODULE;
@@ -125,7 +178,6 @@ static int __devinit gen_74x164_probe(struct spi_device *spi)
exit_destroy:
dev_set_drvdata(&spi->dev, NULL);
mutex_destroy(&chip->lock);
- kfree(chip);
return ret;
}
@@ -141,36 +193,31 @@ static int __devexit gen_74x164_remove(struct spi_device *spi)
dev_set_drvdata(&spi->dev, NULL);
ret = gpiochip_remove(&chip->gpio_chip);
- if (!ret) {
+ if (!ret)
mutex_destroy(&chip->lock);
- kfree(chip);
- } else
+ else
dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n",
ret);
return ret;
}
+static const struct of_device_id gen_74x164_dt_ids[] = {
+ { .compatible = "fairchild,74hc595" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, gen_74x164_dt_ids);
+
static struct spi_driver gen_74x164_driver = {
.driver = {
.name = "74x164",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(gen_74x164_dt_ids),
},
.probe = gen_74x164_probe,
.remove = __devexit_p(gen_74x164_remove),
};
-
-static int __init gen_74x164_init(void)
-{
- return spi_register_driver(&gen_74x164_driver);
-}
-subsys_initcall(gen_74x164_init);
-
-static void __exit gen_74x164_exit(void)
-{
- spi_unregister_driver(&gen_74x164_driver);
-}
-module_exit(gen_74x164_exit);
+module_spi_driver(gen_74x164_driver);
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c
new file mode 100644
index 000000000000..3df88336415e
--- /dev/null
+++ b/drivers/gpio/gpio-adnp.c
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2011-2012 Avionic Design GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+
+#define GPIO_DDR(gpio) (0x00 << (gpio)->reg_shift)
+#define GPIO_PLR(gpio) (0x01 << (gpio)->reg_shift)
+#define GPIO_IER(gpio) (0x02 << (gpio)->reg_shift)
+#define GPIO_ISR(gpio) (0x03 << (gpio)->reg_shift)
+#define GPIO_PTR(gpio) (0x04 << (gpio)->reg_shift)
+
+struct adnp {
+ struct i2c_client *client;
+ struct gpio_chip gpio;
+ unsigned int reg_shift;
+
+ struct mutex i2c_lock;
+
+ struct irq_domain *domain;
+ struct mutex irq_lock;
+
+ u8 *irq_enable;
+ u8 *irq_level;
+ u8 *irq_rise;
+ u8 *irq_fall;
+ u8 *irq_high;
+ u8 *irq_low;
+};
+
+static inline struct adnp *to_adnp(struct gpio_chip *chip)
+{
+ return container_of(chip, struct adnp, gpio);
+}
+
+static int adnp_read(struct adnp *adnp, unsigned offset, uint8_t *value)
+{
+ int err;
+
+ err = i2c_smbus_read_byte_data(adnp->client, offset);
+ if (err < 0) {
+ dev_err(adnp->gpio.dev, "%s failed: %d\n",
+ "i2c_smbus_read_byte_data()", err);
+ return err;
+ }
+
+ *value = err;
+ return 0;
+}
+
+static int adnp_write(struct adnp *adnp, unsigned offset, uint8_t value)
+{
+ int err;
+
+ err = i2c_smbus_write_byte_data(adnp->client, offset, value);
+ if (err < 0) {
+ dev_err(adnp->gpio.dev, "%s failed: %d\n",
+ "i2c_smbus_write_byte_data()", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int adnp_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct adnp *adnp = to_adnp(chip);
+ unsigned int reg = offset >> adnp->reg_shift;
+ unsigned int pos = offset & 7;
+ u8 value;
+ int err;
+
+ err = adnp_read(adnp, GPIO_PLR(adnp) + reg, &value);
+ if (err < 0)
+ return err;
+
+ return (value & BIT(pos)) ? 1 : 0;
+}
+
+static void __adnp_gpio_set(struct adnp *adnp, unsigned offset, int value)
+{
+ unsigned int reg = offset >> adnp->reg_shift;
+ unsigned int pos = offset & 7;
+ int err;
+ u8 val;
+
+ err = adnp_read(adnp, GPIO_PLR(adnp) + reg, &val);
+ if (err < 0)
+ return;
+
+ if (value)
+ val |= BIT(pos);
+ else
+ val &= ~BIT(pos);
+
+ adnp_write(adnp, GPIO_PLR(adnp) + reg, val);
+}
+
+static void adnp_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct adnp *adnp = to_adnp(chip);
+
+ mutex_lock(&adnp->i2c_lock);
+ __adnp_gpio_set(adnp, offset, value);
+ mutex_unlock(&adnp->i2c_lock);
+}
+
+static int adnp_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct adnp *adnp = to_adnp(chip);
+ unsigned int reg = offset >> adnp->reg_shift;
+ unsigned int pos = offset & 7;
+ u8 value;
+ int err;
+
+ mutex_lock(&adnp->i2c_lock);
+
+ err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &value);
+ if (err < 0)
+ goto out;
+
+ value &= ~BIT(pos);
+
+ err = adnp_write(adnp, GPIO_DDR(adnp) + reg, value);
+ if (err < 0)
+ goto out;
+
+ err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &value);
+ if (err < 0)
+ goto out;
+
+ if (err & BIT(pos))
+ err = -EACCES;
+
+ err = 0;
+
+out:
+ mutex_unlock(&adnp->i2c_lock);
+ return err;
+}
+
+static int adnp_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct adnp *adnp = to_adnp(chip);
+ unsigned int reg = offset >> adnp->reg_shift;
+ unsigned int pos = offset & 7;
+ int err;
+ u8 val;
+
+ mutex_lock(&adnp->i2c_lock);
+
+ err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &val);
+ if (err < 0)
+ goto out;
+
+ val |= BIT(pos);
+
+ err = adnp_write(adnp, GPIO_DDR(adnp) + reg, val);
+ if (err < 0)
+ goto out;
+
+ err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &val);
+ if (err < 0)
+ goto out;
+
+ if (!(val & BIT(pos))) {
+ err = -EPERM;
+ goto out;
+ }
+
+ __adnp_gpio_set(adnp, offset, value);
+ err = 0;
+
+out:
+ mutex_unlock(&adnp->i2c_lock);
+ return err;
+}
+
+static void adnp_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ struct adnp *adnp = to_adnp(chip);
+ unsigned int num_regs = 1 << adnp->reg_shift, i, j;
+ int err;
+
+ for (i = 0; i < num_regs; i++) {
+ u8 ddr, plr, ier, isr;
+
+ mutex_lock(&adnp->i2c_lock);
+
+ err = adnp_read(adnp, GPIO_DDR(adnp) + i, &ddr);
+ if (err < 0) {
+ mutex_unlock(&adnp->i2c_lock);
+ return;
+ }
+
+ err = adnp_read(adnp, GPIO_PLR(adnp) + i, &plr);
+ if (err < 0) {
+ mutex_unlock(&adnp->i2c_lock);
+ return;
+ }
+
+ err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier);
+ if (err < 0) {
+ mutex_unlock(&adnp->i2c_lock);
+ return;
+ }
+
+ err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr);
+ if (err < 0) {
+ mutex_unlock(&adnp->i2c_lock);
+ return;
+ }
+
+ mutex_unlock(&adnp->i2c_lock);
+
+ for (j = 0; j < 8; j++) {
+ unsigned int bit = (i << adnp->reg_shift) + j;
+ const char *direction = "input ";
+ const char *level = "low ";
+ const char *interrupt = "disabled";
+ const char *pending = "";
+
+ if (ddr & BIT(j))
+ direction = "output";
+
+ if (plr & BIT(j))
+ level = "high";
+
+ if (ier & BIT(j))
+ interrupt = "enabled ";
+
+ if (isr & BIT(j))
+ pending = "pending";
+
+ seq_printf(s, "%2u: %s %s IRQ %s %s\n", bit,
+ direction, level, interrupt, pending);
+ }
+ }
+}
+
+static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios)
+{
+ struct gpio_chip *chip = &adnp->gpio;
+
+ adnp->reg_shift = get_count_order(num_gpios) - 3;
+
+ chip->direction_input = adnp_gpio_direction_input;
+ chip->direction_output = adnp_gpio_direction_output;
+ chip->get = adnp_gpio_get;
+ chip->set = adnp_gpio_set;
+ chip->can_sleep = 1;
+
+ if (IS_ENABLED(CONFIG_DEBUG_FS))
+ chip->dbg_show = adnp_gpio_dbg_show;
+
+ chip->base = -1;
+ chip->ngpio = num_gpios;
+ chip->label = adnp->client->name;
+ chip->dev = &adnp->client->dev;
+ chip->of_node = chip->dev->of_node;
+ chip->owner = THIS_MODULE;
+
+ return 0;
+}
+
+static irqreturn_t adnp_irq(int irq, void *data)
+{
+ struct adnp *adnp = data;
+ unsigned int num_regs, i;
+
+ num_regs = 1 << adnp->reg_shift;
+
+ for (i = 0; i < num_regs; i++) {
+ unsigned int base = i << adnp->reg_shift, bit;
+ u8 changed, level, isr, ier;
+ unsigned long pending;
+ int err;
+
+ mutex_lock(&adnp->i2c_lock);
+
+ err = adnp_read(adnp, GPIO_PLR(adnp) + i, &level);
+ if (err < 0) {
+ mutex_unlock(&adnp->i2c_lock);
+ continue;
+ }
+
+ err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr);
+ if (err < 0) {
+ mutex_unlock(&adnp->i2c_lock);
+ continue;
+ }
+
+ err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier);
+ if (err < 0) {
+ mutex_unlock(&adnp->i2c_lock);
+ continue;
+ }
+
+ mutex_unlock(&adnp->i2c_lock);
+
+ /* determine pins that changed levels */
+ changed = level ^ adnp->irq_level[i];
+
+ /* compute edge-triggered interrupts */
+ pending = changed & ((adnp->irq_fall[i] & ~level) |
+ (adnp->irq_rise[i] & level));
+
+ /* add in level-triggered interrupts */
+ pending |= (adnp->irq_high[i] & level) |
+ (adnp->irq_low[i] & ~level);
+
+ /* mask out non-pending and disabled interrupts */
+ pending &= isr & ier;
+
+ for_each_set_bit(bit, &pending, 8) {
+ unsigned int virq;
+ virq = irq_find_mapping(adnp->domain, base + bit);
+ handle_nested_irq(virq);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int adnp_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct adnp *adnp = to_adnp(chip);
+ return irq_create_mapping(adnp->domain, offset);
+}
+
+static void adnp_irq_mask(struct irq_data *data)
+{
+ struct adnp *adnp = irq_data_get_irq_chip_data(data);
+ unsigned int reg = data->hwirq >> adnp->reg_shift;
+ unsigned int pos = data->hwirq & 7;
+
+ adnp->irq_enable[reg] &= ~BIT(pos);
+}
+
+static void adnp_irq_unmask(struct irq_data *data)
+{
+ struct adnp *adnp = irq_data_get_irq_chip_data(data);
+ unsigned int reg = data->hwirq >> adnp->reg_shift;
+ unsigned int pos = data->hwirq & 7;
+
+ adnp->irq_enable[reg] |= BIT(pos);
+}
+
+static int adnp_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ struct adnp *adnp = irq_data_get_irq_chip_data(data);
+ unsigned int reg = data->hwirq >> adnp->reg_shift;
+ unsigned int pos = data->hwirq & 7;
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ adnp->irq_rise[reg] |= BIT(pos);
+ else
+ adnp->irq_rise[reg] &= ~BIT(pos);
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ adnp->irq_fall[reg] |= BIT(pos);
+ else
+ adnp->irq_fall[reg] &= ~BIT(pos);
+
+ if (type & IRQ_TYPE_LEVEL_HIGH)
+ adnp->irq_high[reg] |= BIT(pos);
+ else
+ adnp->irq_high[reg] &= ~BIT(pos);
+
+ if (type & IRQ_TYPE_LEVEL_LOW)
+ adnp->irq_low[reg] |= BIT(pos);
+ else
+ adnp->irq_low[reg] &= ~BIT(pos);
+
+ return 0;
+}
+
+static void adnp_irq_bus_lock(struct irq_data *data)
+{
+ struct adnp *adnp = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&adnp->irq_lock);
+}
+
+static void adnp_irq_bus_unlock(struct irq_data *data)
+{
+ struct adnp *adnp = irq_data_get_irq_chip_data(data);
+ unsigned int num_regs = 1 << adnp->reg_shift, i;
+
+ mutex_lock(&adnp->i2c_lock);
+
+ for (i = 0; i < num_regs; i++)
+ adnp_write(adnp, GPIO_IER(adnp) + i, adnp->irq_enable[i]);
+
+ mutex_unlock(&adnp->i2c_lock);
+ mutex_unlock(&adnp->irq_lock);
+}
+
+static struct irq_chip adnp_irq_chip = {
+ .name = "gpio-adnp",
+ .irq_mask = adnp_irq_mask,
+ .irq_unmask = adnp_irq_unmask,
+ .irq_set_type = adnp_irq_set_type,
+ .irq_bus_lock = adnp_irq_bus_lock,
+ .irq_bus_sync_unlock = adnp_irq_bus_unlock,
+};
+
+static int adnp_irq_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_data(irq, domain->host_data);
+ irq_set_chip(irq, &adnp_irq_chip);
+ irq_set_nested_thread(irq, true);
+
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
+
+ return 0;
+}
+
+static const struct irq_domain_ops adnp_irq_domain_ops = {
+ .map = adnp_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int adnp_irq_setup(struct adnp *adnp)
+{
+ unsigned int num_regs = 1 << adnp->reg_shift, i;
+ struct gpio_chip *chip = &adnp->gpio;
+ int err;
+
+ mutex_init(&adnp->irq_lock);
+
+ /*
+ * Allocate memory to keep track of the current level and trigger
+ * modes of the interrupts. To avoid multiple allocations, a single
+ * large buffer is allocated and pointers are setup to point at the
+ * corresponding offsets. For consistency, the layout of the buffer
+ * is chosen to match the register layout of the hardware in that
+ * each segment contains the corresponding bits for all interrupts.
+ */
+ adnp->irq_enable = devm_kzalloc(chip->dev, num_regs * 6, GFP_KERNEL);
+ if (!adnp->irq_enable)
+ return -ENOMEM;
+
+ adnp->irq_level = adnp->irq_enable + (num_regs * 1);
+ adnp->irq_rise = adnp->irq_enable + (num_regs * 2);
+ adnp->irq_fall = adnp->irq_enable + (num_regs * 3);
+ adnp->irq_high = adnp->irq_enable + (num_regs * 4);
+ adnp->irq_low = adnp->irq_enable + (num_regs * 5);
+
+ for (i = 0; i < num_regs; i++) {
+ /*
+ * Read the initial level of all pins to allow the emulation
+ * of edge triggered interrupts.
+ */
+ err = adnp_read(adnp, GPIO_PLR(adnp) + i, &adnp->irq_level[i]);
+ if (err < 0)
+ return err;
+
+ /* disable all interrupts */
+ err = adnp_write(adnp, GPIO_IER(adnp) + i, 0);
+ if (err < 0)
+ return err;
+
+ adnp->irq_enable[i] = 0x00;
+ }
+
+ adnp->domain = irq_domain_add_linear(chip->of_node, chip->ngpio,
+ &adnp_irq_domain_ops, adnp);
+
+ err = request_threaded_irq(adnp->client->irq, NULL, adnp_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dev_name(chip->dev), adnp);
+ if (err != 0) {
+ dev_err(chip->dev, "can't request IRQ#%d: %d\n",
+ adnp->client->irq, err);
+ goto error;
+ }
+
+ chip->to_irq = adnp_gpio_to_irq;
+ return 0;
+
+error:
+ irq_domain_remove(adnp->domain);
+ return err;
+}
+
+static void adnp_irq_teardown(struct adnp *adnp)
+{
+ unsigned int irq, i;
+
+ free_irq(adnp->client->irq, adnp);
+
+ for (i = 0; i < adnp->gpio.ngpio; i++) {
+ irq = irq_find_mapping(adnp->domain, i);
+ if (irq > 0)
+ irq_dispose_mapping(irq);
+ }
+
+ irq_domain_remove(adnp->domain);
+}
+
+static __devinit int adnp_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device_node *np = client->dev.of_node;
+ struct adnp *adnp;
+ u32 num_gpios;
+ int err;
+
+ err = of_property_read_u32(np, "nr-gpios", &num_gpios);
+ if (err < 0)
+ return err;
+
+ client->irq = irq_of_parse_and_map(np, 0);
+ if (!client->irq)
+ return -EPROBE_DEFER;
+
+ adnp = devm_kzalloc(&client->dev, sizeof(*adnp), GFP_KERNEL);
+ if (!adnp)
+ return -ENOMEM;
+
+ mutex_init(&adnp->i2c_lock);
+ adnp->client = client;
+
+ err = adnp_gpio_setup(adnp, num_gpios);
+ if (err < 0)
+ return err;
+
+ if (of_find_property(np, "interrupt-controller", NULL)) {
+ err = adnp_irq_setup(adnp);
+ if (err < 0)
+ goto teardown;
+ }
+
+ err = gpiochip_add(&adnp->gpio);
+ if (err < 0)
+ goto teardown;
+
+ i2c_set_clientdata(client, adnp);
+ return 0;
+
+teardown:
+ if (of_find_property(np, "interrupt-controller", NULL))
+ adnp_irq_teardown(adnp);
+
+ return err;
+}
+
+static __devexit int adnp_i2c_remove(struct i2c_client *client)
+{
+ struct adnp *adnp = i2c_get_clientdata(client);
+ struct device_node *np = client->dev.of_node;
+ int err;
+
+ err = gpiochip_remove(&adnp->gpio);
+ if (err < 0) {
+ dev_err(&client->dev, "%s failed: %d\n", "gpiochip_remove()",
+ err);
+ return err;
+ }
+
+ if (of_find_property(np, "interrupt-controller", NULL))
+ adnp_irq_teardown(adnp);
+
+ return 0;
+}
+
+static const struct i2c_device_id adnp_i2c_id[] __devinitconst = {
+ { "gpio-adnp" },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, adnp_i2c_id);
+
+static const struct of_device_id adnp_of_match[] __devinitconst = {
+ { .compatible = "ad,gpio-adnp", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adnp_of_match);
+
+static struct i2c_driver adnp_i2c_driver = {
+ .driver = {
+ .name = "gpio-adnp",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(adnp_of_match),
+ },
+ .probe = adnp_i2c_probe,
+ .remove = __devexit_p(adnp_i2c_remove),
+ .id_table = adnp_i2c_id,
+};
+module_i2c_driver(adnp_i2c_driver);
+
+MODULE_DESCRIPTION("Avionic Design N-bit GPIO expander");
+MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c
index ae5d7f12ce66..eeedad42913e 100644
--- a/drivers/gpio/gpio-adp5588.c
+++ b/drivers/gpio/gpio-adp5588.c
@@ -483,19 +483,7 @@ static struct i2c_driver adp5588_gpio_driver = {
.id_table = adp5588_gpio_id,
};
-static int __init adp5588_gpio_init(void)
-{
- return i2c_add_driver(&adp5588_gpio_driver);
-}
-
-module_init(adp5588_gpio_init);
-
-static void __exit adp5588_gpio_exit(void)
-{
- i2c_del_driver(&adp5588_gpio_driver);
-}
-
-module_exit(adp5588_gpio_exit);
+module_i2c_driver(adp5588_gpio_driver);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("GPIO ADP5588 Driver");
diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c
index e4cc7eb69bb2..aba97abda77c 100644
--- a/drivers/gpio/gpio-bt8xx.c
+++ b/drivers/gpio/gpio-bt8xx.c
@@ -310,7 +310,7 @@ static int bt8xxgpio_resume(struct pci_dev *pdev)
#define bt8xxgpio_resume NULL
#endif /* CONFIG_PM */
-static struct pci_device_id bt8xxgpio_pci_tbl[] = {
+static DEFINE_PCI_DEVICE_TABLE(bt8xxgpio_pci_tbl) = {
{ PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878) },
diff --git a/drivers/gpio/gpio-da9052.c b/drivers/gpio/gpio-da9052.c
index 56dd047d5844..24b8c2974047 100644
--- a/drivers/gpio/gpio-da9052.c
+++ b/drivers/gpio/gpio-da9052.c
@@ -207,7 +207,7 @@ static int __devinit da9052_gpio_probe(struct platform_device *pdev)
struct da9052_pdata *pdata;
int ret;
- gpio = kzalloc(sizeof(*gpio), GFP_KERNEL);
+ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
if (gpio == NULL)
return -ENOMEM;
@@ -221,28 +221,19 @@ static int __devinit da9052_gpio_probe(struct platform_device *pdev)
ret = gpiochip_add(&gpio->gp);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
- goto err_mem;
+ return ret;
}
platform_set_drvdata(pdev, gpio);
return 0;
-
-err_mem:
- kfree(gpio);
- return ret;
}
static int __devexit da9052_gpio_remove(struct platform_device *pdev)
{
struct da9052_gpio *gpio = platform_get_drvdata(pdev);
- int ret;
-
- ret = gpiochip_remove(&gpio->gp);
- if (ret == 0)
- kfree(gpio);
- return ret;
+ return gpiochip_remove(&gpio->gp);
}
static struct platform_driver da9052_gpio_driver = {
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
index 3d000169285d..17df6db5dca7 100644
--- a/drivers/gpio/gpio-davinci.c
+++ b/drivers/gpio/gpio-davinci.c
@@ -366,7 +366,7 @@ static int __init davinci_gpio_irq_setup(void)
PTR_ERR(clk));
return PTR_ERR(clk);
}
- clk_enable(clk);
+ clk_prepare_enable(clk);
/* Arrange gpio_to_irq() support, handling either direct IRQs or
* banked IRQs. Having GPIOs in the first GPIO bank use direct
diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c
index ec48ed512628..efb4c2d0d132 100644
--- a/drivers/gpio/gpio-em.c
+++ b/drivers/gpio/gpio-em.c
@@ -85,22 +85,16 @@ static inline void em_gio_write(struct em_gio_priv *p, int offs,
iowrite32(value, p->base1 + (offs - GIO_IDT0));
}
-static inline struct em_gio_priv *irq_to_priv(struct irq_data *d)
-{
- struct irq_chip *chip = irq_data_get_irq_chip(d);
- return container_of(chip, struct em_gio_priv, irq_chip);
-}
-
static void em_gio_irq_disable(struct irq_data *d)
{
- struct em_gio_priv *p = irq_to_priv(d);
+ struct em_gio_priv *p = irq_data_get_irq_chip_data(d);
em_gio_write(p, GIO_IDS, BIT(irqd_to_hwirq(d)));
}
static void em_gio_irq_enable(struct irq_data *d)
{
- struct em_gio_priv *p = irq_to_priv(d);
+ struct em_gio_priv *p = irq_data_get_irq_chip_data(d);
em_gio_write(p, GIO_IEN, BIT(irqd_to_hwirq(d)));
}
@@ -118,7 +112,7 @@ static unsigned char em_gio_sense_table[IRQ_TYPE_SENSE_MASK + 1] = {
static int em_gio_irq_set_type(struct irq_data *d, unsigned int type)
{
unsigned char value = em_gio_sense_table[type & IRQ_TYPE_SENSE_MASK];
- struct em_gio_priv *p = irq_to_priv(d);
+ struct em_gio_priv *p = irq_data_get_irq_chip_data(d);
unsigned int reg, offset, shift;
unsigned long flags;
unsigned long tmp;
diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c
index ed94b4ea72e9..3644e0dcb3dd 100644
--- a/drivers/gpio/gpio-lpc32xx.c
+++ b/drivers/gpio/gpio-lpc32xx.c
@@ -113,7 +113,8 @@ static const char *gpi_p3_names[LPC32XX_GPI_P3_MAX] = {
NULL, NULL, NULL, "gpi15",
"gpi16", "gpi17", "gpi18", "gpi19",
"gpi20", "gpi21", "gpi22", "gpi23",
- "gpi24", "gpi25", "gpi26", "gpi27"
+ "gpi24", "gpi25", "gpi26", "gpi27",
+ "gpi28"
};
static const char *gpo_p3_names[LPC32XX_GPO_P3_MAX] = {
diff --git a/drivers/gpio/gpio-mc9s08dz60.c b/drivers/gpio/gpio-mc9s08dz60.c
index 2738cc44d636..0ab700046a23 100644
--- a/drivers/gpio/gpio-mc9s08dz60.c
+++ b/drivers/gpio/gpio-mc9s08dz60.c
@@ -91,10 +91,9 @@ static int mc9s08dz60_direction_output(struct gpio_chip *gc,
static int mc9s08dz60_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- int ret = 0;
struct mc9s08dz60 *mc9s;
- mc9s = kzalloc(sizeof(*mc9s), GFP_KERNEL);
+ mc9s = devm_kzalloc(&client->dev, sizeof(*mc9s), GFP_KERNEL);
if (!mc9s)
return -ENOMEM;
@@ -110,30 +109,16 @@ static int mc9s08dz60_probe(struct i2c_client *client,
mc9s->client = client;
i2c_set_clientdata(client, mc9s);
- ret = gpiochip_add(&mc9s->chip);
- if (ret)
- goto error;
-
- return 0;
-
- error:
- kfree(mc9s);
- return ret;
+ return gpiochip_add(&mc9s->chip);
}
static int mc9s08dz60_remove(struct i2c_client *client)
{
struct mc9s08dz60 *mc9s;
- int ret;
mc9s = i2c_get_clientdata(client);
- ret = gpiochip_remove(&mc9s->chip);
- if (!ret)
- kfree(mc9s);
-
- return ret;
-
+ return gpiochip_remove(&mc9s->chip);
}
static const struct i2c_device_id mc9s08dz60_id[] = {
diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c
index db01f151d41c..6a29ee1847be 100644
--- a/drivers/gpio/gpio-ml-ioh.c
+++ b/drivers/gpio/gpio-ml-ioh.c
@@ -87,8 +87,7 @@ struct ioh_gpio_reg_data {
* @gpio_use_sel: Save GPIO_USE_SEL1~4 register for PM
* @ch: Indicate GPIO channel
* @irq_base: Save base of IRQ number for interrupt
- * @spinlock: Used for register access protection in
- * interrupt context ioh_irq_type and PM;
+ * @spinlock: Used for register access protection
*/
struct ioh_gpio {
void __iomem *base;
@@ -97,7 +96,6 @@ struct ioh_gpio {
struct gpio_chip gpio;
struct ioh_gpio_reg_data ioh_gpio_reg;
u32 gpio_use_sel;
- struct mutex lock;
int ch;
int irq_base;
spinlock_t spinlock;
@@ -109,8 +107,9 @@ static void ioh_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
{
u32 reg_val;
struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
+ unsigned long flags;
- mutex_lock(&chip->lock);
+ spin_lock_irqsave(&chip->spinlock, flags);
reg_val = ioread32(&chip->reg->regs[chip->ch].po);
if (val)
reg_val |= (1 << nr);
@@ -118,7 +117,7 @@ static void ioh_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
reg_val &= ~(1 << nr);
iowrite32(reg_val, &chip->reg->regs[chip->ch].po);
- mutex_unlock(&chip->lock);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
}
static int ioh_gpio_get(struct gpio_chip *gpio, unsigned nr)
@@ -134,8 +133,9 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
u32 pm;
u32 reg_val;
+ unsigned long flags;
- mutex_lock(&chip->lock);
+ spin_lock_irqsave(&chip->spinlock, flags);
pm = ioread32(&chip->reg->regs[chip->ch].pm) &
((1 << num_ports[chip->ch]) - 1);
pm |= (1 << nr);
@@ -148,7 +148,7 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
reg_val &= ~(1 << nr);
iowrite32(reg_val, &chip->reg->regs[chip->ch].po);
- mutex_unlock(&chip->lock);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
return 0;
}
@@ -157,13 +157,14 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
{
struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
u32 pm;
+ unsigned long flags;
- mutex_lock(&chip->lock);
+ spin_lock_irqsave(&chip->spinlock, flags);
pm = ioread32(&chip->reg->regs[chip->ch].pm) &
((1 << num_ports[chip->ch]) - 1);
pm &= ~(1 << nr);
iowrite32(pm, &chip->reg->regs[chip->ch].pm);
- mutex_unlock(&chip->lock);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
return 0;
}
@@ -447,7 +448,6 @@ static int __devinit ioh_gpio_probe(struct pci_dev *pdev,
chip->base = base;
chip->reg = chip->base;
chip->ch = i;
- mutex_init(&chip->lock);
spin_lock_init(&chip->spinlock);
ioh_gpio_setup(chip, num_ports[i]);
ret = gpiochip_add(&chip->gpio);
diff --git a/drivers/gpio/gpio-msm-v2.c b/drivers/gpio/gpio-msm-v2.c
index 5cb1227d69cf..38305beb4375 100644
--- a/drivers/gpio/gpio-msm-v2.c
+++ b/drivers/gpio/gpio-msm-v2.c
@@ -317,9 +317,7 @@ static void msm_summary_irq_handler(unsigned int irq, struct irq_desc *desc)
chained_irq_enter(chip, desc);
- for (i = find_first_bit(msm_gpio.enabled_irqs, NR_GPIO_IRQS);
- i < NR_GPIO_IRQS;
- i = find_next_bit(msm_gpio.enabled_irqs, NR_GPIO_IRQS, i + 1)) {
+ for_each_set_bit(i, msm_gpio.enabled_irqs, NR_GPIO_IRQS) {
if (readl(GPIO_INTR_STATUS(i)) & BIT(INTR_STATUS))
generic_handle_irq(msm_gpio_to_irq(&msm_gpio.gpio_chip,
i));
diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c
index 076e236d0da7..16af35cd2b10 100644
--- a/drivers/gpio/gpio-pcf857x.c
+++ b/drivers/gpio/gpio-pcf857x.c
@@ -23,7 +23,12 @@
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/i2c/pcf857x.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
static const struct i2c_device_id pcf857x_id[] = {
@@ -60,7 +65,12 @@ struct pcf857x {
struct gpio_chip chip;
struct i2c_client *client;
struct mutex lock; /* protect 'out' */
+ struct work_struct work; /* irq demux work */
+ struct irq_domain *irq_domain; /* for irq demux */
+ spinlock_t slock; /* protect irq demux */
unsigned out; /* software latch */
+ unsigned status; /* current status */
+ int irq; /* real irq number */
int (*write)(struct i2c_client *client, unsigned data);
int (*read)(struct i2c_client *client);
@@ -150,6 +160,100 @@ static void pcf857x_set(struct gpio_chip *chip, unsigned offset, int value)
/*-------------------------------------------------------------------------*/
+static int pcf857x_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct pcf857x *gpio = container_of(chip, struct pcf857x, chip);
+
+ return irq_create_mapping(gpio->irq_domain, offset);
+}
+
+static void pcf857x_irq_demux_work(struct work_struct *work)
+{
+ struct pcf857x *gpio = container_of(work,
+ struct pcf857x,
+ work);
+ unsigned long change, i, status, flags;
+
+ status = gpio->read(gpio->client);
+
+ spin_lock_irqsave(&gpio->slock, flags);
+
+ change = gpio->status ^ status;
+ for_each_set_bit(i, &change, gpio->chip.ngpio)
+ generic_handle_irq(irq_find_mapping(gpio->irq_domain, i));
+ gpio->status = status;
+
+ spin_unlock_irqrestore(&gpio->slock, flags);
+}
+
+static irqreturn_t pcf857x_irq_demux(int irq, void *data)
+{
+ struct pcf857x *gpio = data;
+
+ /*
+ * pcf857x can't read/write data here,
+ * since i2c data access might go to sleep.
+ */
+ schedule_work(&gpio->work);
+
+ return IRQ_HANDLED;
+}
+
+static int pcf857x_irq_domain_map(struct irq_domain *domain, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(virq,
+ &dummy_irq_chip,
+ handle_level_irq);
+ return 0;
+}
+
+static struct irq_domain_ops pcf857x_irq_domain_ops = {
+ .map = pcf857x_irq_domain_map,
+};
+
+static void pcf857x_irq_domain_cleanup(struct pcf857x *gpio)
+{
+ if (gpio->irq_domain)
+ irq_domain_remove(gpio->irq_domain);
+
+ if (gpio->irq)
+ free_irq(gpio->irq, gpio);
+}
+
+static int pcf857x_irq_domain_init(struct pcf857x *gpio,
+ struct pcf857x_platform_data *pdata,
+ struct device *dev)
+{
+ int status;
+
+ gpio->irq_domain = irq_domain_add_linear(dev->of_node,
+ gpio->chip.ngpio,
+ &pcf857x_irq_domain_ops,
+ NULL);
+ if (!gpio->irq_domain)
+ goto fail;
+
+ /* enable real irq */
+ status = request_irq(pdata->irq, pcf857x_irq_demux, 0,
+ dev_name(dev), gpio);
+ if (status)
+ goto fail;
+
+ /* enable gpio_to_irq() */
+ INIT_WORK(&gpio->work, pcf857x_irq_demux_work);
+ gpio->chip.to_irq = pcf857x_to_irq;
+ gpio->irq = pdata->irq;
+
+ return 0;
+
+fail:
+ pcf857x_irq_domain_cleanup(gpio);
+ return -EINVAL;
+}
+
+/*-------------------------------------------------------------------------*/
+
static int pcf857x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -168,6 +272,7 @@ static int pcf857x_probe(struct i2c_client *client,
return -ENOMEM;
mutex_init(&gpio->lock);
+ spin_lock_init(&gpio->slock);
gpio->chip.base = pdata ? pdata->gpio_base : -1;
gpio->chip.can_sleep = 1;
@@ -179,6 +284,15 @@ static int pcf857x_probe(struct i2c_client *client,
gpio->chip.direction_output = pcf857x_output;
gpio->chip.ngpio = id->driver_data;
+ /* enable gpio_to_irq() if platform has settings */
+ if (pdata && pdata->irq) {
+ status = pcf857x_irq_domain_init(gpio, pdata, &client->dev);
+ if (status < 0) {
+ dev_err(&client->dev, "irq_domain init failed\n");
+ goto fail;
+ }
+ }
+
/* NOTE: the OnSemi jlc1562b is also largely compatible with
* these parts, notably for output. It has a low-resolution
* DAC instead of pin change IRQs; and its inputs can be the
@@ -248,6 +362,7 @@ static int pcf857x_probe(struct i2c_client *client,
* all-ones reset state. Otherwise it flags pins to be driven low.
*/
gpio->out = pdata ? ~pdata->n_latch : ~0;
+ gpio->status = gpio->out;
status = gpiochip_add(&gpio->chip);
if (status < 0)
@@ -278,6 +393,10 @@ static int pcf857x_probe(struct i2c_client *client,
fail:
dev_dbg(&client->dev, "probe error %d for '%s'\n",
status, client->name);
+
+ if (pdata && pdata->irq)
+ pcf857x_irq_domain_cleanup(gpio);
+
kfree(gpio);
return status;
}
@@ -299,6 +418,9 @@ static int pcf857x_remove(struct i2c_client *client)
}
}
+ if (pdata && pdata->irq)
+ pcf857x_irq_domain_cleanup(gpio);
+
status = gpiochip_remove(&gpio->chip);
if (status == 0)
kfree(gpio);
diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c
index 139ad3e20011..4ad0c4f9171c 100644
--- a/drivers/gpio/gpio-pch.c
+++ b/drivers/gpio/gpio-pch.c
@@ -92,9 +92,7 @@ struct pch_gpio_reg_data {
* @lock: Used for register access protection
* @irq_base: Save base of IRQ number for interrupt
* @ioh: IOH ID
- * @spinlock: Used for register access protection in
- * interrupt context pch_irq_mask,
- * pch_irq_unmask and pch_irq_type;
+ * @spinlock: Used for register access protection
*/
struct pch_gpio {
void __iomem *base;
@@ -102,7 +100,6 @@ struct pch_gpio {
struct device *dev;
struct gpio_chip gpio;
struct pch_gpio_reg_data pch_gpio_reg;
- struct mutex lock;
int irq_base;
enum pch_type_t ioh;
spinlock_t spinlock;
@@ -112,8 +109,9 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
{
u32 reg_val;
struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
+ unsigned long flags;
- mutex_lock(&chip->lock);
+ spin_lock_irqsave(&chip->spinlock, flags);
reg_val = ioread32(&chip->reg->po);
if (val)
reg_val |= (1 << nr);
@@ -121,7 +119,7 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
reg_val &= ~(1 << nr);
iowrite32(reg_val, &chip->reg->po);
- mutex_unlock(&chip->lock);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
}
static int pch_gpio_get(struct gpio_chip *gpio, unsigned nr)
@@ -137,8 +135,9 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
u32 pm;
u32 reg_val;
+ unsigned long flags;
- mutex_lock(&chip->lock);
+ spin_lock_irqsave(&chip->spinlock, flags);
pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1);
pm |= (1 << nr);
iowrite32(pm, &chip->reg->pm);
@@ -149,8 +148,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
else
reg_val &= ~(1 << nr);
iowrite32(reg_val, &chip->reg->po);
-
- mutex_unlock(&chip->lock);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
return 0;
}
@@ -159,12 +157,13 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
{
struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
u32 pm;
+ unsigned long flags;
- mutex_lock(&chip->lock);
+ spin_lock_irqsave(&chip->spinlock, flags);
pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1);
pm &= ~(1 << nr);
iowrite32(pm, &chip->reg->pm);
- mutex_unlock(&chip->lock);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
return 0;
}
@@ -387,7 +386,6 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev,
chip->reg = chip->base;
pci_set_drvdata(pdev, chip);
- mutex_init(&chip->lock);
spin_lock_init(&chip->spinlock);
pch_gpio_setup(chip);
ret = gpiochip_add(&chip->gpio);
diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c
index 9528779ca463..98d52cb3fd1a 100644
--- a/drivers/gpio/gpio-pxa.c
+++ b/drivers/gpio/gpio-pxa.c
@@ -370,12 +370,10 @@ static void pxa_gpio_demux_handler(unsigned int irq, struct irq_desc *desc)
gedr = gedr & c->irq_mask;
writel_relaxed(gedr, c->regbase + GEDR_OFFSET);
- n = find_first_bit(&gedr, BITS_PER_LONG);
- while (n < BITS_PER_LONG) {
+ for_each_set_bit(n, &gedr, BITS_PER_LONG) {
loop = 1;
generic_handle_irq(gpio_to_irq(gpio_base + n));
- n = find_next_bit(&gedr, BITS_PER_LONG, n + 1);
}
}
} while (loop);
@@ -589,19 +587,12 @@ static int __devinit pxa_gpio_probe(struct platform_device *pdev)
iounmap(gpio_reg_base);
return PTR_ERR(clk);
}
- ret = clk_prepare(clk);
+ ret = clk_prepare_enable(clk);
if (ret) {
clk_put(clk);
iounmap(gpio_reg_base);
return ret;
}
- ret = clk_enable(clk);
- if (ret) {
- clk_unprepare(clk);
- clk_put(clk);
- iounmap(gpio_reg_base);
- return ret;
- }
/* Initialize GPIO chips */
info = dev_get_platdata(&pdev->dev);
diff --git a/drivers/gpio/gpio-sodaville.c b/drivers/gpio/gpio-sodaville.c
index 9d9891f7a607..e25f73130b40 100644
--- a/drivers/gpio/gpio-sodaville.c
+++ b/drivers/gpio/gpio-sodaville.c
@@ -270,7 +270,7 @@ static void sdv_gpio_remove(struct pci_dev *pdev)
kfree(sd);
}
-static struct pci_device_id sdv_gpio_pci_ids[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(sdv_gpio_pci_ids) = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_SDV_GPIO) },
{ 0, },
};
diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c
index a4f73534394e..eb3e215d2396 100644
--- a/drivers/gpio/gpio-sx150x.c
+++ b/drivers/gpio/gpio-sx150x.c
@@ -311,11 +311,9 @@ static int sx150x_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
static void sx150x_irq_mask(struct irq_data *d)
{
- struct irq_chip *ic = irq_data_get_irq_chip(d);
- struct sx150x_chip *chip;
+ struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
unsigned n;
- chip = container_of(ic, struct sx150x_chip, irq_chip);
n = d->irq - chip->irq_base;
chip->irq_masked |= (1 << n);
chip->irq_update = n;
@@ -323,27 +321,22 @@ static void sx150x_irq_mask(struct irq_data *d)
static void sx150x_irq_unmask(struct irq_data *d)
{
- struct irq_chip *ic = irq_data_get_irq_chip(d);
- struct sx150x_chip *chip;
+ struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
unsigned n;
- chip = container_of(ic, struct sx150x_chip, irq_chip);
n = d->irq - chip->irq_base;
-
chip->irq_masked &= ~(1 << n);
chip->irq_update = n;
}
static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
{
- struct irq_chip *ic = irq_data_get_irq_chip(d);
- struct sx150x_chip *chip;
+ struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
unsigned n, val = 0;
if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
return -EINVAL;
- chip = container_of(ic, struct sx150x_chip, irq_chip);
n = d->irq - chip->irq_base;
if (flow_type & IRQ_TYPE_EDGE_RISING)
@@ -391,22 +384,16 @@ static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id)
static void sx150x_irq_bus_lock(struct irq_data *d)
{
- struct irq_chip *ic = irq_data_get_irq_chip(d);
- struct sx150x_chip *chip;
-
- chip = container_of(ic, struct sx150x_chip, irq_chip);
+ struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
mutex_lock(&chip->lock);
}
static void sx150x_irq_bus_sync_unlock(struct irq_data *d)
{
- struct irq_chip *ic = irq_data_get_irq_chip(d);
- struct sx150x_chip *chip;
+ struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
unsigned n;
- chip = container_of(ic, struct sx150x_chip, irq_chip);
-
if (chip->irq_update == NO_UPDATE_PENDING)
goto out;
@@ -551,6 +538,7 @@ static int sx150x_install_irq_chip(struct sx150x_chip *chip,
for (n = 0; n < chip->dev_cfg->ngpios; ++n) {
irq = irq_base + n;
+ irq_set_chip_data(irq, chip);
irq_set_chip_and_handler(irq, &chip->irq_chip, handle_edge_irq);
irq_set_nested_thread(irq, 1);
#ifdef CONFIG_ARM
diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c
index 2a82e8999a42..1e48317e70fb 100644
--- a/drivers/gpio/gpio-tc3589x.c
+++ b/drivers/gpio/gpio-tc3589x.c
@@ -11,7 +11,9 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/gpio.h>
+#include <linux/of.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/mfd/tc3589x.h>
@@ -29,6 +31,7 @@ struct tc3589x_gpio {
struct tc3589x *tc3589x;
struct device *dev;
struct mutex irq_lock;
+ struct irq_domain *domain;
int irq_base;
@@ -92,11 +95,28 @@ static int tc3589x_gpio_direction_input(struct gpio_chip *chip,
return tc3589x_set_bits(tc3589x, reg, 1 << pos, 0);
}
+/**
+ * tc3589x_gpio_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ
+ *
+ * @tc3589x_gpio: tc3589x_gpio_irq controller to operate on.
+ * @irq: index of the interrupt requested in the chip IRQs
+ *
+ * Useful for drivers to request their own IRQs.
+ */
+static int tc3589x_gpio_irq_get_virq(struct tc3589x_gpio *tc3589x_gpio,
+ int irq)
+{
+ if (!tc3589x_gpio)
+ return -EINVAL;
+
+ return irq_create_mapping(tc3589x_gpio->domain, irq);
+}
+
static int tc3589x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
- return tc3589x_gpio->irq_base + offset;
+ return tc3589x_gpio_irq_get_virq(tc3589x_gpio, offset);
}
static struct gpio_chip template_chip = {
@@ -113,7 +133,7 @@ static struct gpio_chip template_chip = {
static int tc3589x_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{
struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
- int offset = d->irq - tc3589x_gpio->irq_base;
+ int offset = d->hwirq;
int regoffset = offset / 8;
int mask = 1 << (offset % 8);
@@ -175,7 +195,7 @@ static void tc3589x_gpio_irq_sync_unlock(struct irq_data *d)
static void tc3589x_gpio_irq_mask(struct irq_data *d)
{
struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
- int offset = d->irq - tc3589x_gpio->irq_base;
+ int offset = d->hwirq;
int regoffset = offset / 8;
int mask = 1 << (offset % 8);
@@ -185,7 +205,7 @@ static void tc3589x_gpio_irq_mask(struct irq_data *d)
static void tc3589x_gpio_irq_unmask(struct irq_data *d)
{
struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
- int offset = d->irq - tc3589x_gpio->irq_base;
+ int offset = d->hwirq;
int regoffset = offset / 8;
int mask = 1 << (offset % 8);
@@ -222,8 +242,9 @@ static irqreturn_t tc3589x_gpio_irq(int irq, void *dev)
while (stat) {
int bit = __ffs(stat);
int line = i * 8 + bit;
+ int virq = tc3589x_gpio_irq_get_virq(tc3589x_gpio, line);
- handle_nested_irq(tc3589x_gpio->irq_base + line);
+ handle_nested_irq(virq);
stat &= ~(1 << bit);
}
@@ -233,51 +254,78 @@ static irqreturn_t tc3589x_gpio_irq(int irq, void *dev)
return IRQ_HANDLED;
}
-static int tc3589x_gpio_irq_init(struct tc3589x_gpio *tc3589x_gpio)
+static int tc3589x_gpio_irq_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hwirq)
{
- int base = tc3589x_gpio->irq_base;
- int irq;
+ struct tc3589x *tc3589x_gpio = d->host_data;
- for (irq = base; irq < base + tc3589x_gpio->chip.ngpio; irq++) {
- irq_set_chip_data(irq, tc3589x_gpio);
- irq_set_chip_and_handler(irq, &tc3589x_gpio_irq_chip,
- handle_simple_irq);
- irq_set_nested_thread(irq, 1);
+ irq_set_chip_data(virq, tc3589x_gpio);
+ irq_set_chip_and_handler(virq, &tc3589x_gpio_irq_chip,
+ handle_simple_irq);
+ irq_set_nested_thread(virq, 1);
#ifdef CONFIG_ARM
- set_irq_flags(irq, IRQF_VALID);
+ set_irq_flags(virq, IRQF_VALID);
#else
- irq_set_noprobe(irq);
+ irq_set_noprobe(virq);
#endif
- }
return 0;
}
-static void tc3589x_gpio_irq_remove(struct tc3589x_gpio *tc3589x_gpio)
+static void tc3589x_gpio_irq_unmap(struct irq_domain *d, unsigned int virq)
{
- int base = tc3589x_gpio->irq_base;
- int irq;
-
- for (irq = base; irq < base + tc3589x_gpio->chip.ngpio; irq++) {
#ifdef CONFIG_ARM
- set_irq_flags(irq, 0);
+ set_irq_flags(virq, 0);
#endif
- irq_set_chip_and_handler(irq, NULL, NULL);
- irq_set_chip_data(irq, NULL);
+ irq_set_chip_and_handler(virq, NULL, NULL);
+ irq_set_chip_data(virq, NULL);
+}
+
+static struct irq_domain_ops tc3589x_irq_ops = {
+ .map = tc3589x_gpio_irq_map,
+ .unmap = tc3589x_gpio_irq_unmap,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int tc3589x_gpio_irq_init(struct tc3589x_gpio *tc3589x_gpio,
+ struct device_node *np)
+{
+ int base = tc3589x_gpio->irq_base;
+
+ if (base) {
+ tc3589x_gpio->domain = irq_domain_add_legacy(
+ NULL, tc3589x_gpio->chip.ngpio, base,
+ 0, &tc3589x_irq_ops, tc3589x_gpio);
+ }
+ else {
+ tc3589x_gpio->domain = irq_domain_add_linear(
+ np, tc3589x_gpio->chip.ngpio,
+ &tc3589x_irq_ops, tc3589x_gpio);
+ }
+
+ if (!tc3589x_gpio->domain) {
+ dev_err(tc3589x_gpio->dev, "Failed to create irqdomain\n");
+ return -ENOSYS;
}
+
+ return 0;
}
static int __devinit tc3589x_gpio_probe(struct platform_device *pdev)
{
struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent);
struct tc3589x_gpio_platform_data *pdata;
+ struct device_node *np = pdev->dev.of_node;
struct tc3589x_gpio *tc3589x_gpio;
int ret;
int irq;
pdata = tc3589x->pdata->gpio;
- if (!pdata)
- return -ENODEV;
+
+ if (!(pdata || np)) {
+ dev_err(&pdev->dev, "No platform data or Device Tree found\n");
+ return -EINVAL;
+ }
irq = platform_get_irq(pdev, 0);
if (irq < 0)
@@ -295,9 +343,14 @@ static int __devinit tc3589x_gpio_probe(struct platform_device *pdev)
tc3589x_gpio->chip = template_chip;
tc3589x_gpio->chip.ngpio = tc3589x->num_gpio;
tc3589x_gpio->chip.dev = &pdev->dev;
- tc3589x_gpio->chip.base = pdata->gpio_base;
+ tc3589x_gpio->chip.base = (pdata) ? pdata->gpio_base : -1;
- tc3589x_gpio->irq_base = tc3589x->irq_base + TC3589x_INT_GPIO(0);
+#ifdef CONFIG_OF_GPIO
+ tc3589x_gpio->chip.of_node = np;
+#endif
+
+ tc3589x_gpio->irq_base = tc3589x->irq_base ?
+ tc3589x->irq_base + TC3589x_INT_GPIO(0) : 0;
/* Bring the GPIO module out of reset */
ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL,
@@ -305,7 +358,7 @@ static int __devinit tc3589x_gpio_probe(struct platform_device *pdev)
if (ret < 0)
goto out_free;
- ret = tc3589x_gpio_irq_init(tc3589x_gpio);
+ ret = tc3589x_gpio_irq_init(tc3589x_gpio, np);
if (ret)
goto out_free;
@@ -313,7 +366,7 @@ static int __devinit tc3589x_gpio_probe(struct platform_device *pdev)
"tc3589x-gpio", tc3589x_gpio);
if (ret) {
dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
- goto out_removeirq;
+ goto out_free;
}
ret = gpiochip_add(&tc3589x_gpio->chip);
@@ -322,7 +375,7 @@ static int __devinit tc3589x_gpio_probe(struct platform_device *pdev)
goto out_freeirq;
}
- if (pdata->setup)
+ if (pdata && pdata->setup)
pdata->setup(tc3589x, tc3589x_gpio->chip.base);
platform_set_drvdata(pdev, tc3589x_gpio);
@@ -331,8 +384,6 @@ static int __devinit tc3589x_gpio_probe(struct platform_device *pdev)
out_freeirq:
free_irq(irq, tc3589x_gpio);
-out_removeirq:
- tc3589x_gpio_irq_remove(tc3589x_gpio);
out_free:
kfree(tc3589x_gpio);
return ret;
@@ -346,7 +397,7 @@ static int __devexit tc3589x_gpio_remove(struct platform_device *pdev)
int irq = platform_get_irq(pdev, 0);
int ret;
- if (pdata->remove)
+ if (pdata && pdata->remove)
pdata->remove(tc3589x, tc3589x_gpio->chip.base);
ret = gpiochip_remove(&tc3589x_gpio->chip);
@@ -357,7 +408,6 @@ static int __devexit tc3589x_gpio_remove(struct platform_device *pdev)
}
free_irq(irq, tc3589x_gpio);
- tc3589x_gpio_irq_remove(tc3589x_gpio);
platform_set_drvdata(pdev, NULL);
kfree(tc3589x_gpio);
diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c
index 79e66c002350..99106d1e2e55 100644
--- a/drivers/gpio/gpio-tps65912.c
+++ b/drivers/gpio/gpio-tps65912.c
@@ -70,7 +70,6 @@ static int tps65912_gpio_input(struct gpio_chip *gc, unsigned offset)
return tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset,
GPIO_CFG_MASK);
-
}
static struct gpio_chip template_chip = {
@@ -92,7 +91,8 @@ static int __devinit tps65912_gpio_probe(struct platform_device *pdev)
struct tps65912_gpio_data *tps65912_gpio;
int ret;
- tps65912_gpio = kzalloc(sizeof(*tps65912_gpio), GFP_KERNEL);
+ tps65912_gpio = devm_kzalloc(&pdev->dev, sizeof(*tps65912_gpio),
+ GFP_KERNEL);
if (tps65912_gpio == NULL)
return -ENOMEM;
@@ -105,28 +105,19 @@ static int __devinit tps65912_gpio_probe(struct platform_device *pdev)
ret = gpiochip_add(&tps65912_gpio->gpio_chip);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register gpiochip, %d\n", ret);
- goto err;
+ return ret;
}
platform_set_drvdata(pdev, tps65912_gpio);
return ret;
-
-err:
- kfree(tps65912_gpio);
- return ret;
}
static int __devexit tps65912_gpio_remove(struct platform_device *pdev)
{
struct tps65912_gpio_data *tps65912_gpio = platform_get_drvdata(pdev);
- int ret;
-
- ret = gpiochip_remove(&tps65912_gpio->gpio_chip);
- if (ret == 0)
- kfree(tps65912_gpio);
- return ret;
+ return gpiochip_remove(&tps65912_gpio->gpio_chip);
}
static struct platform_driver tps65912_gpio_driver = {
diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c
index e56a2165641c..b6eda35089d5 100644
--- a/drivers/gpio/gpio-wm831x.c
+++ b/drivers/gpio/gpio-wm831x.c
@@ -250,7 +250,8 @@ static int __devinit wm831x_gpio_probe(struct platform_device *pdev)
struct wm831x_gpio *wm831x_gpio;
int ret;
- wm831x_gpio = kzalloc(sizeof(*wm831x_gpio), GFP_KERNEL);
+ wm831x_gpio = devm_kzalloc(&pdev->dev, sizeof(*wm831x_gpio),
+ GFP_KERNEL);
if (wm831x_gpio == NULL)
return -ENOMEM;
@@ -265,30 +266,20 @@ static int __devinit wm831x_gpio_probe(struct platform_device *pdev)
ret = gpiochip_add(&wm831x_gpio->gpio_chip);
if (ret < 0) {
- dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
- ret);
- goto err;
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+ return ret;
}
platform_set_drvdata(pdev, wm831x_gpio);
return ret;
-
-err:
- kfree(wm831x_gpio);
- return ret;
}
static int __devexit wm831x_gpio_remove(struct platform_device *pdev)
{
struct wm831x_gpio *wm831x_gpio = platform_get_drvdata(pdev);
- int ret;
-
- ret = gpiochip_remove(&wm831x_gpio->gpio_chip);
- if (ret == 0)
- kfree(wm831x_gpio);
- return ret;
+ return gpiochip_remove(&wm831x_gpio->gpio_chip);
}
static struct platform_driver wm831x_gpio_driver = {
diff --git a/drivers/gpio/gpio-wm8350.c b/drivers/gpio/gpio-wm8350.c
index a06af5154838..fb4293889392 100644
--- a/drivers/gpio/gpio-wm8350.c
+++ b/drivers/gpio/gpio-wm8350.c
@@ -116,7 +116,8 @@ static int __devinit wm8350_gpio_probe(struct platform_device *pdev)
struct wm8350_gpio_data *wm8350_gpio;
int ret;
- wm8350_gpio = kzalloc(sizeof(*wm8350_gpio), GFP_KERNEL);
+ wm8350_gpio = devm_kzalloc(&pdev->dev, sizeof(*wm8350_gpio),
+ GFP_KERNEL);
if (wm8350_gpio == NULL)
return -ENOMEM;
@@ -131,30 +132,20 @@ static int __devinit wm8350_gpio_probe(struct platform_device *pdev)
ret = gpiochip_add(&wm8350_gpio->gpio_chip);
if (ret < 0) {
- dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
- ret);
- goto err;
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+ return ret;
}
platform_set_drvdata(pdev, wm8350_gpio);
return ret;
-
-err:
- kfree(wm8350_gpio);
- return ret;
}
static int __devexit wm8350_gpio_remove(struct platform_device *pdev)
{
struct wm8350_gpio_data *wm8350_gpio = platform_get_drvdata(pdev);
- int ret;
-
- ret = gpiochip_remove(&wm8350_gpio->gpio_chip);
- if (ret == 0)
- kfree(wm8350_gpio);
- return ret;
+ return gpiochip_remove(&wm8350_gpio->gpio_chip);
}
static struct platform_driver wm8350_gpio_driver = {
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index de0213c9d11c..5d6c71edc739 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1773,56 +1773,102 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
}
}
-static int gpiolib_show(struct seq_file *s, void *unused)
+static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos)
{
- struct gpio_chip *chip = NULL;
- unsigned gpio;
- int started = 0;
+ struct gpio_chip *chip = NULL;
+ unsigned int gpio;
+ void *ret = NULL;
+ loff_t index = 0;
/* REVISIT this isn't locked against gpio_chip removal ... */
for (gpio = 0; gpio_is_valid(gpio); gpio++) {
- struct device *dev;
-
- if (chip == gpio_desc[gpio].chip)
+ if (gpio_desc[gpio].chip == chip)
continue;
+
chip = gpio_desc[gpio].chip;
if (!chip)
continue;
- seq_printf(s, "%sGPIOs %d-%d",
- started ? "\n" : "",
- chip->base, chip->base + chip->ngpio - 1);
- dev = chip->dev;
- if (dev)
- seq_printf(s, ", %s/%s",
- dev->bus ? dev->bus->name : "no-bus",
- dev_name(dev));
- if (chip->label)
- seq_printf(s, ", %s", chip->label);
- if (chip->can_sleep)
- seq_printf(s, ", can sleep");
- seq_printf(s, ":\n");
-
- started = 1;
- if (chip->dbg_show)
- chip->dbg_show(s, chip);
- else
- gpiolib_dbg_show(s, chip);
+ if (index++ >= *pos) {
+ ret = chip;
+ break;
+ }
}
+
+ s->private = "";
+
+ return ret;
+}
+
+static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ struct gpio_chip *chip = v;
+ unsigned int gpio;
+ void *ret = NULL;
+
+ /* skip GPIOs provided by the current chip */
+ for (gpio = chip->base + chip->ngpio; gpio_is_valid(gpio); gpio++) {
+ chip = gpio_desc[gpio].chip;
+ if (chip) {
+ ret = chip;
+ break;
+ }
+ }
+
+ s->private = "\n";
+ ++*pos;
+
+ return ret;
+}
+
+static void gpiolib_seq_stop(struct seq_file *s, void *v)
+{
+}
+
+static int gpiolib_seq_show(struct seq_file *s, void *v)
+{
+ struct gpio_chip *chip = v;
+ struct device *dev;
+
+ seq_printf(s, "%sGPIOs %d-%d", (char *)s->private,
+ chip->base, chip->base + chip->ngpio - 1);
+ dev = chip->dev;
+ if (dev)
+ seq_printf(s, ", %s/%s", dev->bus ? dev->bus->name : "no-bus",
+ dev_name(dev));
+ if (chip->label)
+ seq_printf(s, ", %s", chip->label);
+ if (chip->can_sleep)
+ seq_printf(s, ", can sleep");
+ seq_printf(s, ":\n");
+
+ if (chip->dbg_show)
+ chip->dbg_show(s, chip);
+ else
+ gpiolib_dbg_show(s, chip);
+
return 0;
}
+static const struct seq_operations gpiolib_seq_ops = {
+ .start = gpiolib_seq_start,
+ .next = gpiolib_seq_next,
+ .stop = gpiolib_seq_stop,
+ .show = gpiolib_seq_show,
+};
+
static int gpiolib_open(struct inode *inode, struct file *file)
{
- return single_open(file, gpiolib_show, NULL);
+ return seq_open(file, &gpiolib_seq_ops);
}
static const struct file_operations gpiolib_operations = {
+ .owner = THIS_MODULE,
.open = gpiolib_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = single_release,
+ .release = seq_release,
};
static int __init gpiolib_debugfs_init(void)