summaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig33
-rw-r--r--drivers/gpio/Makefile4
-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.c4
-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-ich.c79
-rw-r--r--drivers/gpio/gpio-lpc32xx.c8
-rw-r--r--drivers/gpio/gpio-mc9s08dz60.c21
-rw-r--r--drivers/gpio/gpio-ml-ioh.c20
-rw-r--r--drivers/gpio/gpio-mpc8xxx.c2
-rw-r--r--drivers/gpio/gpio-msm-v2.c4
-rw-r--r--drivers/gpio/gpio-mvebu.c682
-rw-r--r--drivers/gpio/gpio-mxs.c48
-rw-r--r--drivers/gpio/gpio-omap.c23
-rw-r--r--drivers/gpio/gpio-pcf857x.c122
-rw-r--r--drivers/gpio/gpio-pch.c22
-rw-r--r--drivers/gpio/gpio-pxa.c90
-rw-r--r--drivers/gpio/gpio-samsung.c124
-rw-r--r--drivers/gpio/gpio-sodaville.c2
-rw-r--r--drivers/gpio/gpio-stp-xway.c2
-rw-r--r--drivers/gpio/gpio-sx150x.c24
-rw-r--r--drivers/gpio/gpio-tc3589x.c120
-rw-r--r--drivers/gpio/gpio-tegra.c3
-rw-r--r--drivers/gpio/gpio-tps65912.c17
-rw-r--r--drivers/gpio/gpio-twl4030.c90
-rw-r--r--drivers/gpio/gpio-twl6040.c137
-rw-r--r--drivers/gpio/gpio-vt8500.c316
-rw-r--r--drivers/gpio/gpio-wm831x.c19
-rw-r--r--drivers/gpio/gpio-wm8350.c19
-rw-r--r--drivers/gpio/gpiolib.c102
34 files changed, 2483 insertions, 411 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index ba7926f5c09..d055cee3694 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.
@@ -150,6 +150,12 @@ config GPIO_MSM_V2
Qualcomm MSM chips. Most of the pins on the MSM can be
selected for GPIO, and are controlled by this driver.
+config GPIO_MVEBU
+ def_bool y
+ depends on ARCH_MVEBU
+ select GPIO_GENERIC
+ select GENERIC_IRQ_CHIP
+
config GPIO_MXC
def_bool y
depends on ARCH_MXC
@@ -183,6 +189,12 @@ config GPIO_STA2X11
Say yes here to support the STA2x11/ConneXt GPIO device.
The GPIO module has 128 GPIO pins with alternate functions.
+config GPIO_VT8500
+ bool "VIA/Wondermedia SoC GPIO Support"
+ depends on ARCH_VT8500
+ help
+ Say yes here to support the VT8500/WM8505/WM8650 GPIO controller.
+
config GPIO_XILINX
bool "Xilinx GPIO support"
depends on PPC_OF || MICROBLAZE
@@ -324,6 +336,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.
@@ -402,6 +415,13 @@ config GPIO_TWL4030
Say yes here to access the GPIO signals of various multi-function
power management chips from Texas Instruments.
+config GPIO_TWL6040
+ tristate "TWL6040 GPO"
+ depends on TWL6040_CORE
+ help
+ Say yes here to access the GPO signals of twl6040
+ audio chip from Texas Instruments.
+
config GPIO_WM831X
tristate "WM831x GPIOs"
depends on MFD_WM831X
@@ -444,6 +464,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 153caceeb05..9aeed670732 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
@@ -41,6 +42,7 @@ 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_MVEBU) += gpio-mvebu.o
obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o
obj-$(CONFIG_ARCH_OMAP) += gpio-omap.o
@@ -67,8 +69,10 @@ obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o
obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
+obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o
+obj-$(CONFIG_GPIO_VT8500) += gpio-vt8500.o
obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o
obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o
diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c
index a31ad6f5d91..ed3e55161bd 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 00000000000..3df88336415
--- /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 ae5d7f12ce6..eeedad42913 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 e4cc7eb69bb..7d9d7cb35f2 100644
--- a/drivers/gpio/gpio-bt8xx.c
+++ b/drivers/gpio/gpio-bt8xx.c
@@ -50,7 +50,7 @@
#include <linux/slab.h>
/* Steal the hardware definitions from the bttv driver. */
-#include "../media/video/bt8xx/bt848.h"
+#include "../media/pci/bt8xx/bt848.h"
#define BT8XXGPIO_NR_GPIOS 24 /* We have 24 GPIO pins */
@@ -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 56dd047d584..24b8c297404 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 3d000169285..17df6db5dca 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 ec48ed51262..efb4c2d0d13 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-ich.c b/drivers/gpio/gpio-ich.c
index b7c06517403..d4d61796669 100644
--- a/drivers/gpio/gpio-ich.c
+++ b/drivers/gpio/gpio-ich.c
@@ -49,6 +49,10 @@ static const u8 ichx_regs[3][3] = {
{0x0c, 0x38, 0x48}, /* LVL[1-3] offsets */
};
+static const u8 ichx_reglen[3] = {
+ 0x30, 0x10, 0x10,
+};
+
#define ICHX_WRITE(val, reg, base_res) outl(val, (reg) + (base_res)->start)
#define ICHX_READ(reg, base_res) inl((reg) + (base_res)->start)
@@ -75,6 +79,7 @@ static struct {
struct resource *pm_base; /* Power Mangagment IO base */
struct ichx_desc *desc; /* Pointer to chipset-specific description */
u32 orig_gpio_ctrl; /* Orig CTRL value, used to restore on exit */
+ u8 use_gpio; /* Which GPIO groups are usable */
} ichx_priv;
static int modparam_gpiobase = -1; /* dynamic */
@@ -123,8 +128,16 @@ static int ichx_read_bit(int reg, unsigned nr)
return data & (1 << bit) ? 1 : 0;
}
+static int ichx_gpio_check_available(struct gpio_chip *gpio, unsigned nr)
+{
+ return (ichx_priv.use_gpio & (1 << (nr / 32))) ? 0 : -ENXIO;
+}
+
static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
{
+ if (!ichx_gpio_check_available(gpio, nr))
+ return -ENXIO;
+
/*
* Try setting pin as an input and verify it worked since many pins
* are output-only.
@@ -138,6 +151,9 @@ static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
int val)
{
+ if (!ichx_gpio_check_available(gpio, nr))
+ return -ENXIO;
+
/* Set GPIO output value. */
ichx_write_bit(GPIO_LVL, nr, val, 0);
@@ -153,6 +169,9 @@ static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr)
{
+ if (!ichx_gpio_check_available(chip, nr))
+ return -ENXIO;
+
return ichx_read_bit(GPIO_LVL, nr);
}
@@ -161,6 +180,9 @@ static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr)
unsigned long flags;
u32 data;
+ if (!ichx_gpio_check_available(chip, nr))
+ return -ENXIO;
+
/*
* GPI 0 - 15 need to be read from the power management registers on
* a ICH6/3100 bridge.
@@ -291,6 +313,46 @@ static struct ichx_desc intel5_desc = {
.ngpio = 76,
};
+static int __devinit ichx_gpio_request_regions(struct resource *res_base,
+ const char *name, u8 use_gpio)
+{
+ int i;
+
+ if (!res_base || !res_base->start || !res_base->end)
+ return -ENODEV;
+
+ for (i = 0; i < ARRAY_SIZE(ichx_regs[0]); i++) {
+ if (!(use_gpio & (1 << i)))
+ continue;
+ if (!request_region(res_base->start + ichx_regs[0][i],
+ ichx_reglen[i], name))
+ goto request_err;
+ }
+ return 0;
+
+request_err:
+ /* Clean up: release already requested regions, if any */
+ for (i--; i >= 0; i--) {
+ if (!(use_gpio & (1 << i)))
+ continue;
+ release_region(res_base->start + ichx_regs[0][i],
+ ichx_reglen[i]);
+ }
+ return -EBUSY;
+}
+
+static void ichx_gpio_release_regions(struct resource *res_base, u8 use_gpio)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ichx_regs[0]); i++) {
+ if (!(use_gpio & (1 << i)))
+ continue;
+ release_region(res_base->start + ichx_regs[0][i],
+ ichx_reglen[i]);
+ }
+}
+
static int __devinit ichx_gpio_probe(struct platform_device *pdev)
{
struct resource *res_base, *res_pm;
@@ -329,12 +391,11 @@ static int __devinit ichx_gpio_probe(struct platform_device *pdev)
}
res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO);
- if (!res_base || !res_base->start || !res_base->end)
- return -ENODEV;
-
- if (!request_region(res_base->start, resource_size(res_base),
- pdev->name))
- return -EBUSY;
+ ichx_priv.use_gpio = ich_info->use_gpio;
+ err = ichx_gpio_request_regions(res_base, pdev->name,
+ ichx_priv.use_gpio);
+ if (err)
+ return err;
ichx_priv.gpio_base = res_base;
@@ -374,8 +435,7 @@ init:
return 0;
add_err:
- release_region(ichx_priv.gpio_base->start,
- resource_size(ichx_priv.gpio_base));
+ ichx_gpio_release_regions(ichx_priv.gpio_base, ichx_priv.use_gpio);
if (ichx_priv.pm_base)
release_region(ichx_priv.pm_base->start,
resource_size(ichx_priv.pm_base));
@@ -393,8 +453,7 @@ static int __devexit ichx_gpio_remove(struct platform_device *pdev)
return err;
}
- release_region(ichx_priv.gpio_base->start,
- resource_size(ichx_priv.gpio_base));
+ ichx_gpio_release_regions(ichx_priv.gpio_base, ichx_priv.use_gpio);
if (ichx_priv.pm_base)
release_region(ichx_priv.pm_base->start,
resource_size(ichx_priv.pm_base));
diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c
index 8a420f13905..3644e0dcb3d 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] = {
@@ -308,6 +309,7 @@ static int lpc32xx_gpio_dir_output_p012(struct gpio_chip *chip, unsigned pin,
{
struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+ __set_gpio_level_p012(group, pin, value);
__set_gpio_dir_p012(group, pin, 0);
return 0;
@@ -318,6 +320,7 @@ static int lpc32xx_gpio_dir_output_p3(struct gpio_chip *chip, unsigned pin,
{
struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+ __set_gpio_level_p3(group, pin, value);
__set_gpio_dir_p3(group, pin, 0);
return 0;
@@ -326,6 +329,9 @@ static int lpc32xx_gpio_dir_output_p3(struct gpio_chip *chip, unsigned pin,
static int lpc32xx_gpio_dir_out_always(struct gpio_chip *chip, unsigned pin,
int value)
{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpo_level_p3(group, pin, value);
return 0;
}
diff --git a/drivers/gpio/gpio-mc9s08dz60.c b/drivers/gpio/gpio-mc9s08dz60.c
index 2738cc44d63..0ab700046a2 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 db01f151d41..6a29ee1847b 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-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
index 5a1817eedd1..9ae29cc0d17 100644
--- a/drivers/gpio/gpio-mpc8xxx.c
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -38,7 +38,7 @@ struct mpc8xxx_gpio_chip {
*/
u32 data;
struct irq_domain *irq;
- void *of_dev_id_data;
+ const void *of_dev_id_data;
};
static inline u32 mpc8xxx_gpio2mask(unsigned int gpio)
diff --git a/drivers/gpio/gpio-msm-v2.c b/drivers/gpio/gpio-msm-v2.c
index 5cb1227d69c..38305beb437 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-mvebu.c b/drivers/gpio/gpio-mvebu.c
new file mode 100644
index 00000000000..7a874129e5d
--- /dev/null
+++ b/drivers/gpio/gpio-mvebu.c
@@ -0,0 +1,682 @@
+/*
+ * GPIO driver for Marvell SoCs
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+ * Andrew Lunn <andrew@lunn.ch>
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * This driver is a fairly straightforward GPIO driver for the
+ * complete family of Marvell EBU SoC platforms (Orion, Dove,
+ * Kirkwood, Discovery, Armada 370/XP). The only complexity of this
+ * driver is the different register layout that exists between the
+ * non-SMP platforms (Orion, Dove, Kirkwood, Armada 370) and the SMP
+ * platforms (MV78200 from the Discovery family and the Armada
+ * XP). Therefore, this driver handles three variants of the GPIO
+ * block:
+ * - the basic variant, called "orion-gpio", with the simplest
+ * register set. Used on Orion, Dove, Kirkwoord, Armada 370 and
+ * non-SMP Discovery systems
+ * - the mv78200 variant for MV78200 Discovery systems. This variant
+ * turns the edge mask and level mask registers into CPU0 edge
+ * mask/level mask registers, and adds CPU1 edge mask/level mask
+ * registers.
+ * - the armadaxp variant for Armada XP systems. This variant keeps
+ * the normal cause/edge mask/level mask registers when the global
+ * interrupts are used, but adds per-CPU cause/edge mask/level mask
+ * registers n a separate memory area for the per-CPU GPIO
+ * interrupts.
+ */
+
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/irqdomain.h>
+#include <linux/io.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+
+/*
+ * GPIO unit register offsets.
+ */
+#define GPIO_OUT_OFF 0x0000
+#define GPIO_IO_CONF_OFF 0x0004
+#define GPIO_BLINK_EN_OFF 0x0008
+#define GPIO_IN_POL_OFF 0x000c
+#define GPIO_DATA_IN_OFF 0x0010
+#define GPIO_EDGE_CAUSE_OFF 0x0014
+#define GPIO_EDGE_MASK_OFF 0x0018
+#define GPIO_LEVEL_MASK_OFF 0x001c
+
+/* The MV78200 has per-CPU registers for edge mask and level mask */
+#define GPIO_EDGE_MASK_MV78200_OFF(cpu) ((cpu) ? 0x30 : 0x18)
+#define GPIO_LEVEL_MASK_MV78200_OFF(cpu) ((cpu) ? 0x34 : 0x1C)
+
+/* The Armada XP has per-CPU registers for interrupt cause, interrupt
+ * mask and interrupt level mask. Those are relative to the
+ * percpu_membase. */
+#define GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu) ((cpu) * 0x4)
+#define GPIO_EDGE_MASK_ARMADAXP_OFF(cpu) (0x10 + (cpu) * 0x4)
+#define GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu) (0x20 + (cpu) * 0x4)
+
+#define MVEBU_GPIO_SOC_VARIANT_ORION 0x1
+#define MVEBU_GPIO_SOC_VARIANT_MV78200 0x2
+#define MVEBU_GPIO_SOC_VARIANT_ARMADAXP 0x3
+
+#define MVEBU_MAX_GPIO_PER_BANK 32
+
+struct mvebu_gpio_chip {
+ struct gpio_chip chip;
+ spinlock_t lock;
+ void __iomem *membase;
+ void __iomem *percpu_membase;
+ unsigned int irqbase;
+ struct irq_domain *domain;
+ int soc_variant;
+};
+
+/*
+ * Functions returning addresses of individual registers for a given
+ * GPIO controller.
+ */
+static inline void __iomem *mvebu_gpioreg_out(struct mvebu_gpio_chip *mvchip)
+{
+ return mvchip->membase + GPIO_OUT_OFF;
+}
+
+static inline void __iomem *mvebu_gpioreg_io_conf(struct mvebu_gpio_chip *mvchip)
+{
+ return mvchip->membase + GPIO_IO_CONF_OFF;
+}
+
+static inline void __iomem *mvebu_gpioreg_in_pol(struct mvebu_gpio_chip *mvchip)
+{
+ return mvchip->membase + GPIO_IN_POL_OFF;
+}
+
+static inline void __iomem *mvebu_gpioreg_data_in(struct mvebu_gpio_chip *mvchip)
+{
+ return mvchip->membase + GPIO_DATA_IN_OFF;
+}
+
+static inline void __iomem *mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip)
+{
+ int cpu;
+
+ switch(mvchip->soc_variant) {
+ case MVEBU_GPIO_SOC_VARIANT_ORION:
+ case MVEBU_GPIO_SOC_VARIANT_MV78200:
+ return mvchip->membase + GPIO_EDGE_CAUSE_OFF;
+ case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
+ cpu = smp_processor_id();
+ return mvchip->percpu_membase + GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu);
+ default:
+ BUG();
+ }
+}
+
+static inline void __iomem *mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip)
+{
+ int cpu;
+
+ switch(mvchip->soc_variant) {
+ case MVEBU_GPIO_SOC_VARIANT_ORION:
+ return mvchip->membase + GPIO_EDGE_MASK_OFF;
+ case MVEBU_GPIO_SOC_VARIANT_MV78200:
+ cpu = smp_processor_id();
+ return mvchip->membase + GPIO_EDGE_MASK_MV78200_OFF(cpu);
+ case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
+ cpu = smp_processor_id();
+ return mvchip->percpu_membase + GPIO_EDGE_MASK_ARMADAXP_OFF(cpu);
+ default:
+ BUG();
+ }
+}
+
+static void __iomem *mvebu_gpioreg_level_mask(struct mvebu_gpio_chip *mvchip)
+{
+ int cpu;
+
+ switch(mvchip->soc_variant) {
+ case MVEBU_GPIO_SOC_VARIANT_ORION:
+ return mvchip->membase + GPIO_LEVEL_MASK_OFF;
+ case MVEBU_GPIO_SOC_VARIANT_MV78200:
+ cpu = smp_processor_id();
+ return mvchip->membase + GPIO_LEVEL_MASK_MV78200_OFF(cpu);
+ case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
+ cpu = smp_processor_id();
+ return mvchip->percpu_membase + GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu);
+ default:
+ BUG();
+ }
+}
+
+/*
+ * Functions implementing the gpio_chip methods
+ */
+
+int mvebu_gpio_request(struct gpio_chip *chip, unsigned pin)
+{
+ return pinctrl_request_gpio(chip->base + pin);
+}
+
+void mvebu_gpio_free(struct gpio_chip *chip, unsigned pin)
+{
+ pinctrl_free_gpio(chip->base + pin);
+}
+
+static void mvebu_gpio_set(struct gpio_chip *chip, unsigned pin, int value)
+{
+ struct mvebu_gpio_chip *mvchip =
+ container_of(chip, struct mvebu_gpio_chip, chip);
+ unsigned long flags;
+ u32 u;
+
+ spin_lock_irqsave(&mvchip->lock, flags);
+ u = readl_relaxed(mvebu_gpioreg_out(mvchip));
+ if (value)
+ u |= 1 << pin;
+ else
+ u &= ~(1 << pin);
+ writel_relaxed(u, mvebu_gpioreg_out(mvchip));
+ spin_unlock_irqrestore(&mvchip->lock, flags);
+}
+
+static int mvebu_gpio_get(struct gpio_chip *chip, unsigned pin)
+{
+ struct mvebu_gpio_chip *mvchip =
+ container_of(chip, struct mvebu_gpio_chip, chip);
+ u32 u;
+
+ if (readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & (1 << pin)) {
+ u = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) ^
+ readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
+ } else {
+ u = readl_relaxed(mvebu_gpioreg_out(mvchip));
+ }
+
+ return (u >> pin) & 1;
+}
+
+static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned pin)
+{
+ struct mvebu_gpio_chip *mvchip =
+ container_of(chip, struct mvebu_gpio_chip, chip);
+ unsigned long flags;
+ int ret;
+ u32 u;
+
+ /* Check with the pinctrl driver whether this pin is usable as
+ * an input GPIO */
+ ret = pinctrl_gpio_direction_input(chip->base + pin);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&mvchip->lock, flags);
+ u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip));
+ u |= 1 << pin;
+ writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip));
+ spin_unlock_irqrestore(&mvchip->lock, flags);
+
+ return 0;
+}
+
+static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned pin,
+ int value)
+{
+ struct mvebu_gpio_chip *mvchip =
+ container_of(chip, struct mvebu_gpio_chip, chip);
+ unsigned long flags;
+ int ret;
+ u32 u;
+
+ /* Check with the pinctrl driver whether this pin is usable as
+ * an output GPIO */
+ ret = pinctrl_gpio_direction_output(chip->base + pin);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&mvchip->lock, flags);
+ u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip));
+ u &= ~(1 << pin);
+ writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip));
+ spin_unlock_irqrestore(&mvchip->lock, flags);
+
+ return 0;
+}
+
+static int mvebu_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
+{
+ struct mvebu_gpio_chip *mvchip =
+ container_of(chip, struct mvebu_gpio_chip, chip);
+ return irq_create_mapping(mvchip->domain, pin);
+}
+
+/*
+ * Functions implementing the irq_chip methods
+ */
+static void mvebu_gpio_irq_ack(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mvebu_gpio_chip *mvchip = gc->private;
+ u32 mask = ~(1 << (d->irq - gc->irq_base));
+
+ irq_gc_lock(gc);
+ writel_relaxed(mask, mvebu_gpioreg_edge_cause(mvchip));
+ irq_gc_unlock(gc);
+}
+
+static void mvebu_gpio_edge_irq_mask(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mvebu_gpio_chip *mvchip = gc->private;
+ u32 mask = 1 << (d->irq - gc->irq_base);
+
+ irq_gc_lock(gc);
+ gc->mask_cache &= ~mask;
+ writel_relaxed(gc->mask_cache, mvebu_gpioreg_edge_mask(mvchip));
+ irq_gc_unlock(gc);
+}
+
+static void mvebu_gpio_edge_irq_unmask(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mvebu_gpio_chip *mvchip = gc->private;
+ u32 mask = 1 << (d->irq - gc->irq_base);
+
+ irq_gc_lock(gc);
+ gc->mask_cache |= mask;
+ writel_relaxed(gc->mask_cache, mvebu_gpioreg_edge_mask(mvchip));
+ irq_gc_unlock(gc);
+}
+
+static void mvebu_gpio_level_irq_mask(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mvebu_gpio_chip *mvchip = gc->private;
+ u32 mask = 1 << (d->irq - gc->irq_base);
+
+ irq_gc_lock(gc);
+ gc->mask_cache &= ~mask;
+ writel_relaxed(gc->mask_cache, mvebu_gpioreg_level_mask(mvchip));
+ irq_gc_unlock(gc);
+}
+
+static void mvebu_gpio_level_irq_unmask(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mvebu_gpio_chip *mvchip = gc->private;
+ u32 mask = 1 << (d->irq - gc->irq_base);
+
+ irq_gc_lock(gc);
+ gc->mask_cache |= mask;
+ writel_relaxed(gc->mask_cache, mvebu_gpioreg_level_mask(mvchip));
+ irq_gc_unlock(gc);
+}
+
+/*****************************************************************************
+ * MVEBU GPIO IRQ
+ *
+ * GPIO_IN_POL register controls whether GPIO_DATA_IN will hold the same
+ * value of the line or the opposite value.
+ *
+ * Level IRQ handlers: DATA_IN is used directly as cause register.
+ * Interrupt are masked by LEVEL_MASK registers.
+ * Edge IRQ handlers: Change in DATA_IN are latched in EDGE_CAUSE.
+ * Interrupt are masked by EDGE_MASK registers.
+ * Both-edge handlers: Similar to regular Edge handlers, but also swaps
+ * the polarity to catch the next line transaction.
+ * This is a race condition that might not perfectly
+ * work on some use cases.
+ *
+ * Every eight GPIO lines are grouped (OR'ed) before going up to main
+ * cause register.
+ *
+ * EDGE cause mask
+ * data-in /--------| |-----| |----\
+ * -----| |----- ---- to main cause reg
+ * X \----------------| |----/
+ * polarity LEVEL mask
+ *
+ ****************************************************************************/
+
+static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct irq_chip_type *ct = irq_data_get_chip_type(d);
+ struct mvebu_gpio_chip *mvchip = gc->private;
+ int pin;
+ u32 u;
+
+ pin = d->hwirq;
+
+ u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & (1 << pin);
+ if (!u) {
+ return -EINVAL;
+ }
+
+ type &= IRQ_TYPE_SENSE_MASK;
+ if (type == IRQ_TYPE_NONE)
+ return -EINVAL;
+
+ /* Check if we need to change chip and handler */
+ if (!(ct->type & type))
+ if (irq_setup_alt_chip(d, type))
+ return -EINVAL;
+
+ /*
+ * Configure interrupt polarity.
+ */
+ switch(type) {
+ case IRQ_TYPE_EDGE_RISING:
+ case IRQ_TYPE_LEVEL_HIGH:
+ u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
+ u &= ~(1 << pin);
+ writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip));
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ case IRQ_TYPE_LEVEL_LOW:
+ u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
+ u |= 1 << pin;
+ writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip));
+ break;
+ case IRQ_TYPE_EDGE_BOTH: {
+ u32 v;
+
+ v = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)) ^
+ readl_relaxed(mvebu_gpioreg_data_in(mvchip));
+
+ /*
+ * set initial polarity based on current input level
+ */
+ u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
+ if (v & (1 << pin))
+ u |= 1 << pin; /* falling */
+ else
+ u &= ~(1 << pin); /* rising */
+ writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip));
+ break;
+ }
+ }
+ return 0;
+}
+
+static void mvebu_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct mvebu_gpio_chip *mvchip = irq_get_handler_data(irq);
+ u32 cause, type;
+ int i;
+
+ if (mvchip == NULL)
+ return;
+
+ cause = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) &
+ readl_relaxed(mvebu_gpioreg_level_mask(mvchip));
+ cause |= readl_relaxed(mvebu_gpioreg_edge_cause(mvchip)) &
+ readl_relaxed(mvebu_gpioreg_edge_mask(mvchip));
+
+ for (i = 0; i < mvchip->chip.ngpio; i++) {
+ int irq;
+
+ irq = mvchip->irqbase + i;
+
+ if (!(cause & (1 << i)))
+ continue;
+
+ type = irqd_get_trigger_type(irq_get_irq_data(irq));
+ if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) {
+ /* Swap polarity (race with GPIO line) */
+ u32 polarity;
+
+ polarity = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
+ polarity ^= 1 << i;
+ writel_relaxed(polarity, mvebu_gpioreg_in_pol(mvchip));
+ }
+ generic_handle_irq(irq);
+ }
+}
+
+static struct platform_device_id mvebu_gpio_ids[] = {
+ {
+ .name = "orion-gpio",
+ }, {
+ .name = "mv78200-gpio",
+ }, {
+ .name = "armadaxp-gpio",
+ }, {
+ /* sentinel */
+ },
+};
+MODULE_DEVICE_TABLE(platform, mvebu_gpio_ids);
+
+static struct of_device_id mvebu_gpio_of_match[] __devinitdata = {
+ {
+ .compatible = "marvell,orion-gpio",
+ .data = (void*) MVEBU_GPIO_SOC_VARIANT_ORION,
+ },
+ {
+ .compatible = "marvell,mv78200-gpio",
+ .data = (void*) MVEBU_GPIO_SOC_VARIANT_MV78200,
+ },
+ {
+ .compatible = "marvell,armadaxp-gpio",
+ .data = (void*) MVEBU_GPIO_SOC_VARIANT_ARMADAXP,
+ },
+ {
+ /* sentinel */
+ },
+};
+MODULE_DEVICE_TABLE(of, mvebu_gpio_of_match);
+
+static int __devinit mvebu_gpio_probe(struct platform_device *pdev)
+{
+ struct mvebu_gpio_chip *mvchip;
+ const struct of_device_id *match;
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res;
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+ unsigned int ngpios;
+ int soc_variant;
+ int i, cpu, id;
+
+ match = of_match_device(mvebu_gpio_of_match, &pdev->dev);
+ if (match)
+ soc_variant = (int) match->data;
+ else
+ soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (! res) {
+ dev_err(&pdev->dev, "Cannot get memory resource\n");
+ return -ENODEV;
+ }
+
+ mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip), GFP_KERNEL);
+ if (! mvchip){
+ dev_err(&pdev->dev, "Cannot allocate memory\n");
+ return -ENOMEM;
+ }
+
+ if (of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpios)) {
+ dev_err(&pdev->dev, "Missing ngpios OF property\n");
+ return -ENODEV;
+ }
+
+ id = of_alias_get_id(pdev->dev.of_node, "gpio");
+ if (id < 0) {
+ dev_err(&pdev->dev, "Couldn't get OF id\n");
+ return id;
+ }
+
+ mvchip->soc_variant = soc_variant;
+ mvchip->chip.label = dev_name(&pdev->dev);
+ mvchip->chip.dev = &pdev->dev;
+ mvchip->chip.request = mvebu_gpio_request;
+ mvchip->chip.direction_input = mvebu_gpio_direction_input;
+ mvchip->chip.get = mvebu_gpio_get;
+ mvchip->chip.direction_output = mvebu_gpio_direction_output;
+ mvchip->chip.set = mvebu_gpio_set;
+ mvchip->chip.to_irq = mvebu_gpio_to_irq;
+ mvchip->chip.base = id * MVEBU_MAX_GPIO_PER_BANK;
+ mvchip->chip.ngpio = ngpios;
+ mvchip->chip.can_sleep = 0;
+#ifdef CONFIG_OF
+ mvchip->chip.of_node = np;
+#endif
+
+ spin_lock_init(&mvchip->lock);
+ mvchip->membase = devm_request_and_ioremap(&pdev->dev, res);
+ if (! mvchip->membase) {
+ dev_err(&pdev->dev, "Cannot ioremap\n");
+ kfree(mvchip->chip.label);
+ return -ENOMEM;
+ }
+
+ /* The Armada XP has a second range of registers for the
+ * per-CPU registers */
+ if (soc_variant == MVEBU_GPIO_SOC_VARIANT_ARMADAXP) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (! res) {
+ dev_err(&pdev->dev, "Cannot get memory resource\n");
+ kfree(mvchip->chip.label);
+ return -ENODEV;
+ }
+
+ mvchip->percpu_membase = devm_request_and_ioremap(&pdev->dev, res);
+ if (! mvchip->percpu_membase) {
+ dev_err(&pdev->dev, "Cannot ioremap\n");
+ kfree(mvchip->chip.label);
+ return -ENOMEM;
+ }
+ }
+
+ /*
+ * Mask and clear GPIO interrupts.
+ */
+ switch(soc_variant) {
+ case MVEBU_GPIO_SOC_VARIANT_ORION:
+ writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF);
+ writel_relaxed(0, mvchip->membase + GPIO_EDGE_MASK_OFF);
+ writel_relaxed(0, mvchip->membase + GPIO_LEVEL_MASK_OFF);
+ break;
+ case MVEBU_GPIO_SOC_VARIANT_MV78200:
+ writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF);
+ for (cpu = 0; cpu < 2; cpu++) {
+ writel_relaxed(0, mvchip->membase +
+ GPIO_EDGE_MASK_MV78200_OFF(cpu));
+ writel_relaxed(0, mvchip->membase +
+ GPIO_LEVEL_MASK_MV78200_OFF(cpu));
+ }
+ break;
+ case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
+ writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF);
+ writel_relaxed(0, mvchip->membase + GPIO_EDGE_MASK_OFF);
+ writel_relaxed(0, mvchip->membase + GPIO_LEVEL_MASK_OFF);
+ for (cpu = 0; cpu < 4; cpu++) {
+ writel_relaxed(0, mvchip->percpu_membase +
+ GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu));
+ writel_relaxed(0, mvchip->percpu_membase +
+ GPIO_EDGE_MASK_ARMADAXP_OFF(cpu));
+ writel_relaxed(0, mvchip->percpu_membase +
+ GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu));
+ }
+ break;
+ default:
+ BUG();
+ }
+
+ gpiochip_add(&mvchip->chip);
+
+ /* Some gpio controllers do not provide irq support */
+ if (!of_irq_count(np))
+ return 0;
+
+ /* Setup the interrupt handlers. Each chip can have up to 4
+ * interrupt handlers, with each handler dealing with 8 GPIO
+ * pins. */
+ for (i = 0; i < 4; i++) {
+ int irq;
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0)
+ continue;
+ irq_set_handler_data(irq, mvchip);
+ irq_set_chained_handler(irq, mvebu_gpio_irq_handler);
+ }
+
+ mvchip->irqbase = irq_alloc_descs(-1, 0, ngpios, -1);
+ if (mvchip->irqbase < 0) {
+ dev_err(&pdev->dev, "no irqs\n");
+ kfree(mvchip->chip.label);
+ return -ENOMEM;
+ }
+
+ gc = irq_alloc_generic_chip("mvebu_gpio_irq", 2, mvchip->irqbase,
+ mvchip->membase, handle_level_irq);
+ if (! gc) {
+ dev_err(&pdev->dev, "Cannot allocate generic irq_chip\n");
+ kfree(mvchip->chip.label);
+ return -ENOMEM;
+ }
+
+ gc->private = mvchip;
+ ct = &gc->chip_types[0];
+ ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
+ ct->chip.irq_mask = mvebu_gpio_level_irq_mask;
+ ct->chip.irq_unmask = mvebu_gpio_level_irq_unmask;
+ ct->chip.irq_set_type = mvebu_gpio_irq_set_type;
+ ct->chip.name = mvchip->chip.label;
+
+ ct = &gc->chip_types[1];
+ ct->type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
+ ct->chip.irq_ack = mvebu_gpio_irq_ack;
+ ct->chip.irq_mask = mvebu_gpio_edge_irq_mask;
+ ct->chip.irq_unmask = mvebu_gpio_edge_irq_unmask;
+ ct->chip.irq_set_type = mvebu_gpio_irq_set_type;
+ ct->handler = handle_edge_irq;
+ ct->chip.name = mvchip->chip.label;
+
+ irq_setup_generic_chip(gc, IRQ_MSK(ngpios), IRQ_GC_INIT_MASK_CACHE,
+ IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE);
+
+ /* Setup irq domain on top of the generic chip. */
+ mvchip->domain = irq_domain_add_legacy(np, mvchip->chip.ngpio,
+ mvchip->irqbase, 0,
+ &irq_domain_simple_ops,
+ mvchip);
+ if (!mvchip->domain) {
+ dev_err(&pdev->dev, "couldn't allocate irq domain %s (DT).\n",
+ mvchip->chip.label);
+ irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST,
+ IRQ_LEVEL | IRQ_NOPROBE);
+ kfree(gc);
+ kfree(mvchip->chip.label);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static struct platform_driver mvebu_gpio_driver = {
+ .driver = {
+ .name = "mvebu-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = mvebu_gpio_of_match,
+ },
+ .probe = mvebu_gpio_probe,
+ .id_table = mvebu_gpio_ids,
+};
+
+static int __init mvebu_gpio_init(void)
+{
+ return platform_driver_register(&mvebu_gpio_driver);
+}
+postcore_initcall(mvebu_gpio_init);
diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c
index 39e49566996..796fb13e481 100644
--- a/drivers/gpio/gpio-mxs.c
+++ b/drivers/gpio/gpio-mxs.c
@@ -24,6 +24,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -52,8 +53,6 @@
#define GPIO_INT_LEV_MASK (1 << 0)
#define GPIO_INT_POL_MASK (1 << 1)
-#define irq_to_gpio(irq) ((irq) - MXS_GPIO_IRQ_START)
-
enum mxs_gpio_id {
IMX23_GPIO,
IMX28_GPIO,
@@ -63,7 +62,7 @@ struct mxs_gpio_port {
void __iomem *base;
int id;
int irq;
- int virtual_irq_start;
+ struct irq_domain *domain;
struct bgpio_chip bgc;
enum mxs_gpio_id devid;
};
@@ -82,8 +81,7 @@ 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 gpio = irq_to_gpio(d->irq);
- u32 pin_mask = 1 << (gpio & 31);
+ 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;
@@ -120,7 +118,7 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type)
else
writel(pin_mask, pin_addr + MXS_CLR);
- writel(1 << (gpio & 0x1f),
+ writel(pin_mask,
port->base + PINCTRL_IRQSTAT(port) + MXS_CLR);
return 0;
@@ -131,7 +129,6 @@ static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc)
{
u32 irq_stat;
struct mxs_gpio_port *port = irq_get_handler_data(irq);
- u32 gpio_irq_no_base = port->virtual_irq_start;
desc->irq_data.chip->irq_ack(&desc->irq_data);
@@ -140,7 +137,7 @@ static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc)
while (irq_stat != 0) {
int irqoffset = fls(irq_stat) - 1;
- generic_handle_irq(gpio_irq_no_base + irqoffset);
+ generic_handle_irq(irq_find_mapping(port->domain, irqoffset));
irq_stat &= ~(1 << irqoffset);
}
}
@@ -167,12 +164,12 @@ static int mxs_gpio_set_wake_irq(struct irq_data *d, unsigned int enable)
return 0;
}
-static void __init mxs_gpio_init_gc(struct mxs_gpio_port *port)
+static void __init mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base)
{
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
- gc = irq_alloc_generic_chip("gpio-mxs", 1, port->virtual_irq_start,
+ gc = irq_alloc_generic_chip("gpio-mxs", 1, irq_base,
port->base, handle_level_irq);
gc->private = port;
@@ -194,7 +191,7 @@ static int mxs_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
struct mxs_gpio_port *port =
container_of(bgc, struct mxs_gpio_port, bgc);
- return port->virtual_irq_start + offset;
+ return irq_find_mapping(port->domain, offset);
}
static struct platform_device_id mxs_gpio_ids[] = {
@@ -226,6 +223,7 @@ static int __devinit mxs_gpio_probe(struct platform_device *pdev)
static void __iomem *base;
struct mxs_gpio_port *port;
struct resource *iores = NULL;
+ int irq_base;
int err;
port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
@@ -241,7 +239,6 @@ static int __devinit mxs_gpio_probe(struct platform_device *pdev)
port->id = pdev->id;
port->devid = pdev->id_entry->driver_data;
}
- port->virtual_irq_start = MXS_GPIO_IRQ_START + port->id * 32;
port->irq = platform_get_irq(pdev, 0);
if (port->irq < 0)
@@ -275,8 +272,19 @@ static int __devinit mxs_gpio_probe(struct platform_device *pdev)
/* clear address has to be used to clear IRQSTAT bits */
writel(~0U, port->base + PINCTRL_IRQSTAT(port) + MXS_CLR);
+ irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id());
+ if (irq_base < 0)
+ return irq_base;
+
+ port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
+ &irq_domain_simple_ops, NULL);
+ if (!port->domain) {
+ err = -ENODEV;
+ goto out_irqdesc_free;
+ }
+
/* gpio-mxs can be a generic irq chip */
- mxs_gpio_init_gc(port);
+ mxs_gpio_init_gc(port, irq_base);
/* setup one handler for each entry */
irq_set_chained_handler(port->irq, mxs_gpio_irq_handler);
@@ -287,18 +295,22 @@ static int __devinit mxs_gpio_probe(struct platform_device *pdev)
port->base + PINCTRL_DOUT(port), NULL,
port->base + PINCTRL_DOE(port), NULL, 0);
if (err)
- return err;
+ goto out_irqdesc_free;
port->bgc.gc.to_irq = mxs_gpio_to_irq;
port->bgc.gc.base = port->id * 32;
err = gpiochip_add(&port->bgc.gc);
- if (err) {
- bgpio_remove(&port->bgc);
- return err;
- }
+ if (err)
+ goto out_bgpio_remove;
return 0;
+
+out_bgpio_remove:
+ bgpio_remove(&port->bgc);
+out_irqdesc_free:
+ irq_free_descs(irq_base, 32);
+ return err;
}
static struct platform_driver mxs_gpio_driver = {
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index e6efd77668f..94cbc842fbc 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -25,11 +25,9 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/irqdomain.h>
+#include <linux/gpio.h>
+#include <linux/platform_data/gpio-omap.h>
-#include <mach/hardware.h>
-#include <asm/irq.h>
-#include <mach/irqs.h>
-#include <asm/gpio.h>
#include <asm/mach/irq.h>
#define OFF_MODE 1
@@ -385,13 +383,16 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio,
static int gpio_irq_type(struct irq_data *d, unsigned type)
{
struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
- unsigned gpio;
+ unsigned gpio = 0;
int retval;
unsigned long flags;
- if (!cpu_class_is_omap2() && d->irq > IH_MPUIO_BASE)
+#ifdef CONFIG_ARCH_OMAP1
+ if (d->irq > IH_MPUIO_BASE)
gpio = OMAP_MPUIO(d->irq - IH_MPUIO_BASE);
- else
+#endif
+
+ if (!gpio)
gpio = irq_to_gpio(bank, d->irq);
if (type & ~IRQ_TYPE_SENSE_MASK)
@@ -1058,7 +1059,7 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
const struct of_device_id *match;
- struct omap_gpio_platform_data *pdata;
+ const struct omap_gpio_platform_data *pdata;
struct resource *res;
struct gpio_bank *bank;
int ret = 0;
@@ -1440,19 +1441,19 @@ static struct omap_gpio_reg_offs omap4_gpio_regs = {
.fallingdetect = OMAP4_GPIO_FALLINGDETECT,
};
-static struct omap_gpio_platform_data omap2_pdata = {
+const static struct omap_gpio_platform_data omap2_pdata = {
.regs = &omap2_gpio_regs,
.bank_width = 32,
.dbck_flag = false,
};
-static struct omap_gpio_platform_data omap3_pdata = {
+const static struct omap_gpio_platform_data omap3_pdata = {
.regs = &omap2_gpio_regs,
.bank_width = 32,
.dbck_flag = true,
};
-static struct omap_gpio_platform_data omap4_pdata = {
+const static struct omap_gpio_platform_data omap4_pdata = {
.regs = &omap4_gpio_regs,
.bank_width = 32,
.dbck_flag = true,
diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c
index 076e236d0da..16af35cd2b1 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 139ad3e2001..4ad0c4f9171 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 9cac88a65f7..98d52cb3fd1 100644
--- a/drivers/gpio/gpio-pxa.c
+++ b/drivers/gpio/gpio-pxa.c
@@ -26,6 +26,8 @@
#include <linux/syscore_ops.h>
#include <linux/slab.h>
+#include <asm/mach/irq.h>
+
#include <mach/irqs.h>
/*
@@ -59,6 +61,7 @@
#define BANK_OFF(n) (((n) < 3) ? (n) << 2 : 0x100 + (((n) - 3) << 2))
int pxa_last_gpio;
+static int irq_base;
#ifdef CONFIG_OF
static struct irq_domain *domain;
@@ -167,63 +170,14 @@ static inline int __gpio_is_occupied(unsigned gpio)
return ret;
}
-#ifdef CONFIG_ARCH_PXA
-static inline int __pxa_gpio_to_irq(int gpio)
-{
- if (gpio_is_pxa_type(gpio_type))
- return PXA_GPIO_TO_IRQ(gpio);
- return -1;
-}
-
-static inline int __pxa_irq_to_gpio(int irq)
-{
- if (gpio_is_pxa_type(gpio_type))
- return irq - PXA_GPIO_TO_IRQ(0);
- return -1;
-}
-#else
-static inline int __pxa_gpio_to_irq(int gpio) { return -1; }
-static inline int __pxa_irq_to_gpio(int irq) { return -1; }
-#endif
-
-#ifdef CONFIG_ARCH_MMP
-static inline int __mmp_gpio_to_irq(int gpio)
-{
- if (gpio_is_mmp_type(gpio_type))
- return MMP_GPIO_TO_IRQ(gpio);
- return -1;
-}
-
-static inline int __mmp_irq_to_gpio(int irq)
-{
- if (gpio_is_mmp_type(gpio_type))
- return irq - MMP_GPIO_TO_IRQ(0);
- return -1;
-}
-#else
-static inline int __mmp_gpio_to_irq(int gpio) { return -1; }
-static inline int __mmp_irq_to_gpio(int irq) { return -1; }
-#endif
-
static int pxa_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
- int gpio, ret;
-
- gpio = chip->base + offset;
- ret = __pxa_gpio_to_irq(gpio);
- if (ret >= 0)
- return ret;
- return __mmp_gpio_to_irq(gpio);
+ return chip->base + offset + irq_base;
}
int pxa_irq_to_gpio(int irq)
{
- int ret;
-
- ret = __pxa_irq_to_gpio(irq);
- if (ret >= 0)
- return ret;
- return __mmp_irq_to_gpio(irq);
+ return irq - irq_base;
}
static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
@@ -403,6 +357,9 @@ static void pxa_gpio_demux_handler(unsigned int irq, struct irq_desc *desc)
struct pxa_gpio_chip *c;
int loop, gpio, gpio_base, n;
unsigned long gedr;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+
+ chained_irq_enter(chip, desc);
do {
loop = 0;
@@ -413,15 +370,15 @@ 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);
+
+ chained_irq_exit(chip, desc);
}
static void pxa_ack_muxed_gpio(struct irq_data *d)
@@ -535,7 +492,7 @@ const struct irq_domain_ops pxa_irq_domain_ops = {
static int __devinit pxa_gpio_probe_dt(struct platform_device *pdev)
{
- int ret, nr_banks, nr_gpios, irq_base;
+ int ret, nr_banks, nr_gpios;
struct device_node *prev, *next, *np = pdev->dev.of_node;
const struct of_device_id *of_id =
of_match_device(pxa_gpio_dt_ids, &pdev->dev);
@@ -590,10 +547,20 @@ static int __devinit pxa_gpio_probe(struct platform_device *pdev)
int irq0 = 0, irq1 = 0, irq_mux, gpio_offset = 0;
ret = pxa_gpio_probe_dt(pdev);
- if (ret < 0)
+ if (ret < 0) {
pxa_last_gpio = pxa_gpio_nums();
- else
+#ifdef CONFIG_ARCH_PXA
+ if (gpio_is_pxa_type(gpio_type))
+ irq_base = PXA_GPIO_TO_IRQ(0);
+#endif
+#ifdef CONFIG_ARCH_MMP
+ if (gpio_is_mmp_type(gpio_type))
+ irq_base = MMP_GPIO_TO_IRQ(0);
+#endif
+ } else {
use_of = 1;
+ }
+
if (!pxa_last_gpio)
return -EINVAL;
@@ -620,15 +587,8 @@ static int __devinit pxa_gpio_probe(struct platform_device *pdev)
iounmap(gpio_reg_base);
return PTR_ERR(clk);
}
- ret = clk_prepare(clk);
- if (ret) {
- clk_put(clk);
- iounmap(gpio_reg_base);
- return ret;
- }
- ret = clk_enable(clk);
+ ret = clk_prepare_enable(clk);
if (ret) {
- clk_unprepare(clk);
clk_put(clk);
iounmap(gpio_reg_base);
return ret;
diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c
index ba126cc0407..a006f0db15a 100644
--- a/drivers/gpio/gpio-samsung.c
+++ b/drivers/gpio/gpio-samsung.c
@@ -938,6 +938,67 @@ static void __init samsung_gpiolib_add(struct samsung_gpio_chip *chip)
s3c_gpiolib_track(chip);
}
+#if defined(CONFIG_PLAT_S3C24XX) && defined(CONFIG_OF)
+static int s3c24xx_gpio_xlate(struct gpio_chip *gc,
+ const struct of_phandle_args *gpiospec, u32 *flags)
+{
+ unsigned int pin;
+
+ if (WARN_ON(gc->of_gpio_n_cells < 3))
+ return -EINVAL;
+
+ if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells))
+ return -EINVAL;
+
+ if (gpiospec->args[0] > gc->ngpio)
+ return -EINVAL;
+
+ pin = gc->base + gpiospec->args[0];
+
+ if (s3c_gpio_cfgpin(pin, S3C_GPIO_SFN(gpiospec->args[1])))
+ pr_warn("gpio_xlate: failed to set pin function\n");
+ if (s3c_gpio_setpull(pin, gpiospec->args[2] & 0xffff))
+ pr_warn("gpio_xlate: failed to set pin pull up/down\n");
+
+ if (flags)
+ *flags = gpiospec->args[2] >> 16;
+
+ return gpiospec->args[0];
+}
+
+static const struct of_device_id s3c24xx_gpio_dt_match[] __initdata = {
+ { .compatible = "samsung,s3c24xx-gpio", },
+ {}
+};
+
+static __init void s3c24xx_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip,
+ u64 base, u64 offset)
+{
+ struct gpio_chip *gc = &chip->chip;
+ u64 address;
+
+ if (!of_have_populated_dt())
+ return;
+
+ address = chip->base ? base + ((u32)chip->base & 0xfff) : base + offset;
+ gc->of_node = of_find_matching_node_by_address(NULL,
+ s3c24xx_gpio_dt_match, address);
+ if (!gc->of_node) {
+ pr_info("gpio: device tree node not found for gpio controller"
+ " with base address %08llx\n", address);
+ return;
+ }
+ gc->of_gpio_n_cells = 3;
+ gc->of_xlate = s3c24xx_gpio_xlate;
+}
+#else
+static __init void s3c24xx_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip,
+ u64 base, u64 offset)
+{
+ return;
+}
+#endif /* defined(CONFIG_PLAT_S3C24XX) && defined(CONFIG_OF) */
+
static void __init s3c24xx_gpiolib_add_chips(struct samsung_gpio_chip *chip,
int nr_chips, void __iomem *base)
{
@@ -962,6 +1023,8 @@ static void __init s3c24xx_gpiolib_add_chips(struct samsung_gpio_chip *chip,
gc->direction_output = samsung_gpiolib_2bit_output;
samsung_gpiolib_add(chip);
+
+ s3c24xx_gpiolib_attach_ofnode(chip, S3C24XX_PA_GPIO, i * 0x10);
}
}
@@ -2734,6 +2797,27 @@ static __init void exynos4_gpiolib_init(void)
int group = 0;
void __iomem *gpx_base;
+#ifdef CONFIG_PINCTRL_SAMSUNG
+ /*
+ * This gpio driver includes support for device tree support and
+ * there are platforms using it. In order to maintain
+ * compatibility with those platforms, and to allow non-dt
+ * Exynos4210 platforms to use this gpiolib support, a check
+ * is added to find out if there is a active pin-controller
+ * driver support available. If it is available, this gpiolib
+ * support is ignored and the gpiolib support available in
+ * pin-controller driver is used. This is a temporary check and
+ * will go away when all of the Exynos4210 platforms have
+ * switched to using device tree and the pin-ctrl driver.
+ */
+ struct device_node *pctrl_np;
+ const char *pctrl_compat = "samsung,pinctrl-exynos4210";
+ pctrl_np = of_find_compatible_node(NULL, NULL, pctrl_compat);
+ if (pctrl_np)
+ if (of_device_is_available(pctrl_np))
+ return;
+#endif
+
/* gpio part1 */
gpio_base1 = ioremap(EXYNOS4_PA_GPIO1, SZ_4K);
if (gpio_base1 == NULL) {
@@ -3131,46 +3215,6 @@ samsung_gpio_pull_t s3c_gpio_getpull(unsigned int pin)
}
EXPORT_SYMBOL(s3c_gpio_getpull);
-/* gpiolib wrappers until these are totally eliminated */
-
-void s3c2410_gpio_pullup(unsigned int pin, unsigned int to)
-{
- int ret;
-
- WARN_ON(to); /* should be none of these left */
-
- if (!to) {
- /* if pull is enabled, try first with up, and if that
- * fails, try using down */
-
- ret = s3c_gpio_setpull(pin, S3C_GPIO_PULL_UP);
- if (ret)
- s3c_gpio_setpull(pin, S3C_GPIO_PULL_DOWN);
- } else {
- s3c_gpio_setpull(pin, S3C_GPIO_PULL_NONE);
- }
-}
-EXPORT_SYMBOL(s3c2410_gpio_pullup);
-
-void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
-{
- /* do this via gpiolib until all users removed */
-
- gpio_request(pin, "temporary");
- gpio_set_value(pin, to);
- gpio_free(pin);
-}
-EXPORT_SYMBOL(s3c2410_gpio_setpin);
-
-unsigned int s3c2410_gpio_getpin(unsigned int pin)
-{
- struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
- unsigned long offs = pin - chip->chip.base;
-
- return __raw_readl(chip->base + 0x04) & (1 << offs);
-}
-EXPORT_SYMBOL(s3c2410_gpio_getpin);
-
#ifdef CONFIG_S5P_GPIO_DRVSTR
s5p_gpio_drvstr_t s5p_gpio_get_drvstr(unsigned int pin)
{
diff --git a/drivers/gpio/gpio-sodaville.c b/drivers/gpio/gpio-sodaville.c
index 9d9891f7a60..e25f73130b4 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-stp-xway.c b/drivers/gpio/gpio-stp-xway.c
index e35096bf3cf..8bead0bb645 100644
--- a/drivers/gpio/gpio-stp-xway.c
+++ b/drivers/gpio/gpio-stp-xway.c
@@ -82,7 +82,7 @@ struct xway_stp {
struct gpio_chip gc;
void __iomem *virt;
u32 edge; /* rising or falling edge triggered shift register */
- u16 shadow; /* shadow the shift registers state */
+ u32 shadow; /* shadow the shift registers state */
u8 groups; /* we can drive 1-3 groups of 8bit each */
u8 dsl; /* the 2 LSBs can be driven by the dsl core */
u8 phy1; /* 3 bits can be driven by phy1 */
diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c
index a4f73534394..eb3e215d239 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 2a82e8999a4..1e48317e70f 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-tegra.c b/drivers/gpio/gpio-tegra.c
index dc5184d5789..d982593d756 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -30,9 +30,6 @@
#include <asm/mach/irq.h>
-#include <mach/iomap.h>
-#include <mach/suspend.h>
-
#define GPIO_BANK(x) ((x) >> 5)
#define GPIO_PORT(x) (((x) >> 3) & 0x3)
#define GPIO_BIT(x) ((x) & 0x7)
diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c
index 79e66c00235..99106d1e2e5 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-twl4030.c b/drivers/gpio/gpio-twl4030.c
index 94256fe7bf3..c5f8ca233e1 100644
--- a/drivers/gpio/gpio-twl4030.c
+++ b/drivers/gpio/gpio-twl4030.c
@@ -51,6 +51,7 @@
static struct gpio_chip twl_gpiochip;
+static int twl4030_gpio_base;
static int twl4030_gpio_irq_base;
/* genirq interfaces are not available to modules */
@@ -395,6 +396,29 @@ static int __devinit gpio_twl4030_debounce(u32 debounce, u8 mmc_cd)
static int gpio_twl4030_remove(struct platform_device *pdev);
+static struct twl4030_gpio_platform_data *of_gpio_twl4030(struct device *dev)
+{
+ struct twl4030_gpio_platform_data *omap_twl_info;
+
+ omap_twl_info = devm_kzalloc(dev, sizeof(*omap_twl_info), GFP_KERNEL);
+ if (!omap_twl_info)
+ return NULL;
+
+ omap_twl_info->use_leds = of_property_read_bool(dev->of_node,
+ "ti,use-leds");
+
+ of_property_read_u32(dev->of_node, "ti,debounce",
+ &omap_twl_info->debounce);
+ of_property_read_u32(dev->of_node, "ti,mmc-cd",
+ (u32 *)&omap_twl_info->mmc_cd);
+ of_property_read_u32(dev->of_node, "ti,pullups",
+ &omap_twl_info->pullups);
+ of_property_read_u32(dev->of_node, "ti,pulldowns",
+ &omap_twl_info->pulldowns);
+
+ return omap_twl_info;
+}
+
static int __devinit gpio_twl4030_probe(struct platform_device *pdev)
{
struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data;
@@ -427,49 +451,57 @@ no_irqs:
twl_gpiochip.ngpio = TWL4030_GPIO_MAX;
twl_gpiochip.dev = &pdev->dev;
- if (pdata) {
- twl_gpiochip.base = pdata->gpio_base;
+ if (node)
+ pdata = of_gpio_twl4030(&pdev->dev);
- /*
- * NOTE: boards may waste power if they don't set pullups
- * and pulldowns correctly ... default for non-ULPI pins is
- * pulldown, and some other pins may have external pullups
- * or pulldowns. Careful!
- */
- ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns);
- if (ret)
- dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n",
- pdata->pullups, pdata->pulldowns,
- ret);
-
- ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd);
- if (ret)
- dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n",
- pdata->debounce, pdata->mmc_cd,
- ret);
-
- /*
- * NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE,
- * is (still) clear if use_leds is set.
- */
- if (pdata->use_leds)
- twl_gpiochip.ngpio += 2;
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "Platform data is missing\n");
+ return -ENXIO;
}
+ /*
+ * NOTE: boards may waste power if they don't set pullups
+ * and pulldowns correctly ... default for non-ULPI pins is
+ * pulldown, and some other pins may have external pullups
+ * or pulldowns. Careful!
+ */
+ ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns);
+ if (ret)
+ dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n",
+ pdata->pullups, pdata->pulldowns, ret);
+
+ ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd);
+ if (ret)
+ dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n",
+ pdata->debounce, pdata->mmc_cd, ret);
+
+ /*
+ * NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE,
+ * is (still) clear if use_leds is set.
+ */
+ if (pdata->use_leds)
+ twl_gpiochip.ngpio += 2;
+
ret = gpiochip_add(&twl_gpiochip);
if (ret < 0) {
dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret);
twl_gpiochip.ngpio = 0;
gpio_twl4030_remove(pdev);
- } else if (pdata && pdata->setup) {
+ goto out;
+ }
+
+ twl4030_gpio_base = twl_gpiochip.base;
+
+ if (pdata && pdata->setup) {
int status;
status = pdata->setup(&pdev->dev,
- pdata->gpio_base, TWL4030_GPIO_MAX);
+ twl4030_gpio_base, TWL4030_GPIO_MAX);
if (status)
dev_dbg(&pdev->dev, "setup --> %d\n", status);
}
+out:
return ret;
}
@@ -481,7 +513,7 @@ static int gpio_twl4030_remove(struct platform_device *pdev)
if (pdata && pdata->teardown) {
status = pdata->teardown(&pdev->dev,
- pdata->gpio_base, TWL4030_GPIO_MAX);
+ twl4030_gpio_base, TWL4030_GPIO_MAX);
if (status) {
dev_dbg(&pdev->dev, "teardown --> %d\n", status);
return status;
diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c
new file mode 100644
index 00000000000..dd58e8b2504
--- /dev/null
+++ b/drivers/gpio/gpio-twl6040.c
@@ -0,0 +1,137 @@
+/*
+ * Access to GPOs on TWL6040 chip
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Authors:
+ * Sergio Aguirre <saaguirre@ti.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kthread.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+
+#include <linux/mfd/twl6040.h>
+
+static struct gpio_chip twl6040gpo_chip;
+
+static int twl6040gpo_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent);
+ int ret = 0;
+
+ ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL);
+ if (ret < 0)
+ return ret;
+
+ return (ret >> offset) & 1;
+}
+
+static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ /* This only drives GPOs, and can't change direction */
+ return 0;
+}
+
+static void twl6040gpo_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent);
+ int ret;
+ u8 gpoctl;
+
+ ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL);
+ if (ret < 0)
+ return;
+
+ if (value)
+ gpoctl = ret | (1 << offset);
+ else
+ gpoctl = ret & ~(1 << offset);
+
+ twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl);
+}
+
+static struct gpio_chip twl6040gpo_chip = {
+ .label = "twl6040",
+ .owner = THIS_MODULE,
+ .get = twl6040gpo_get,
+ .direction_output = twl6040gpo_direction_out,
+ .set = twl6040gpo_set,
+ .can_sleep = 1,
+};
+
+/*----------------------------------------------------------------------*/
+
+static int __devinit gpo_twl6040_probe(struct platform_device *pdev)
+{
+ struct twl6040_gpo_data *pdata = pdev->dev.platform_data;
+ struct device *twl6040_core_dev = pdev->dev.parent;
+ struct twl6040 *twl6040 = dev_get_drvdata(twl6040_core_dev);
+ int ret;
+
+ if (pdata)
+ twl6040gpo_chip.base = pdata->gpio_base;
+ else
+ twl6040gpo_chip.base = -1;
+
+ if (twl6040_get_revid(twl6040) < TWL6041_REV_ES2_0)
+ twl6040gpo_chip.ngpio = 3; /* twl6040 have 3 GPO */
+ else
+ twl6040gpo_chip.ngpio = 1; /* twl6041 have 1 GPO */
+
+ twl6040gpo_chip.dev = &pdev->dev;
+#ifdef CONFIG_OF_GPIO
+ twl6040gpo_chip.of_node = twl6040_core_dev->of_node;
+#endif
+
+ ret = gpiochip_add(&twl6040gpo_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret);
+ twl6040gpo_chip.ngpio = 0;
+ }
+
+ return ret;
+}
+
+static int __devexit gpo_twl6040_remove(struct platform_device *pdev)
+{
+ return gpiochip_remove(&twl6040gpo_chip);
+}
+
+/* Note: this hardware lives inside an I2C-based multi-function device. */
+MODULE_ALIAS("platform:twl6040-gpo");
+
+static struct platform_driver gpo_twl6040_driver = {
+ .driver = {
+ .name = "twl6040-gpo",
+ .owner = THIS_MODULE,
+ },
+ .probe = gpo_twl6040_probe,
+ .remove = gpo_twl6040_remove,
+};
+
+module_platform_driver(gpo_twl6040_driver);
+
+MODULE_AUTHOR("Texas Instruments, Inc.");
+MODULE_DESCRIPTION("GPO interface for TWL6040");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-vt8500.c b/drivers/gpio/gpio-vt8500.c
new file mode 100644
index 00000000000..bcd8e4aa7c7
--- /dev/null
+++ b/drivers/gpio/gpio-vt8500.c
@@ -0,0 +1,316 @@
+/* drivers/gpio/gpio-vt8500.c
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ * Based on arch/arm/mach-vt8500/gpio.c:
+ * - Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+
+/*
+ We handle GPIOs by bank, each bank containing up to 32 GPIOs covered
+ by one set of registers (although not all may be valid).
+
+ Because different SoC's have different register offsets, we pass the
+ register offsets as data in vt8500_gpio_dt_ids[].
+
+ A value of NO_REG is used to indicate that this register is not
+ supported. Only used for ->en at the moment.
+*/
+
+#define NO_REG 0xFFFF
+
+/*
+ * struct vt8500_gpio_bank_regoffsets
+ * @en: offset to enable register of the bank
+ * @dir: offset to direction register of the bank
+ * @data_out: offset to the data out register of the bank
+ * @data_in: offset to the data in register of the bank
+ * @ngpio: highest valid pin in this bank
+ */
+
+struct vt8500_gpio_bank_regoffsets {
+ unsigned int en;
+ unsigned int dir;
+ unsigned int data_out;
+ unsigned int data_in;
+ unsigned char ngpio;
+};
+
+struct vt8500_gpio_data {
+ unsigned int num_banks;
+ struct vt8500_gpio_bank_regoffsets banks[];
+};
+
+#define VT8500_BANK(__en, __dir, __out, __in, __ngpio) \
+{ \
+ .en = __en, \
+ .dir = __dir, \
+ .data_out = __out, \
+ .data_in = __in, \
+ .ngpio = __ngpio, \
+}
+
+static struct vt8500_gpio_data vt8500_data = {
+ .num_banks = 7,
+ .banks = {
+ VT8500_BANK(0x00, 0x20, 0x40, 0x60, 26),
+ VT8500_BANK(0x04, 0x24, 0x44, 0x64, 28),
+ VT8500_BANK(0x08, 0x28, 0x48, 0x68, 31),
+ VT8500_BANK(0x0C, 0x2C, 0x4C, 0x6C, 19),
+ VT8500_BANK(0x10, 0x30, 0x50, 0x70, 19),
+ VT8500_BANK(0x14, 0x34, 0x54, 0x74, 23),
+ VT8500_BANK(NO_REG, 0x3C, 0x5C, 0x7C, 9),
+ },
+};
+
+static struct vt8500_gpio_data wm8505_data = {
+ .num_banks = 10,
+ .banks = {
+ VT8500_BANK(0x40, 0x68, 0x90, 0xB8, 8),
+ VT8500_BANK(0x44, 0x6C, 0x94, 0xBC, 32),
+ VT8500_BANK(0x48, 0x70, 0x98, 0xC0, 6),
+ VT8500_BANK(0x4C, 0x74, 0x9C, 0xC4, 16),
+ VT8500_BANK(0x50, 0x78, 0xA0, 0xC8, 25),
+ VT8500_BANK(0x54, 0x7C, 0xA4, 0xCC, 5),
+ VT8500_BANK(0x58, 0x80, 0xA8, 0xD0, 5),
+ VT8500_BANK(0x5C, 0x84, 0xAC, 0xD4, 12),
+ VT8500_BANK(0x60, 0x88, 0xB0, 0xD8, 16),
+ VT8500_BANK(0x64, 0x8C, 0xB4, 0xDC, 22),
+ },
+};
+
+/*
+ * No information about which bits are valid so we just make
+ * them all available until its figured out.
+ */
+static struct vt8500_gpio_data wm8650_data = {
+ .num_banks = 9,
+ .banks = {
+ VT8500_BANK(0x40, 0x80, 0xC0, 0x00, 32),
+ VT8500_BANK(0x44, 0x84, 0xC4, 0x04, 32),
+ VT8500_BANK(0x48, 0x88, 0xC8, 0x08, 32),
+ VT8500_BANK(0x4C, 0x8C, 0xCC, 0x0C, 32),
+ VT8500_BANK(0x50, 0x90, 0xD0, 0x10, 32),
+ VT8500_BANK(0x54, 0x94, 0xD4, 0x14, 32),
+ VT8500_BANK(0x58, 0x98, 0xD8, 0x18, 32),
+ VT8500_BANK(0x5C, 0x9C, 0xDC, 0x1C, 32),
+ VT8500_BANK(0x7C, 0xBC, 0xFC, 0x3C, 32),
+ },
+};
+
+struct vt8500_gpio_chip {
+ struct gpio_chip chip;
+
+ const struct vt8500_gpio_bank_regoffsets *regs;
+ void __iomem *base;
+};
+
+
+#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip)
+
+static int vt8500_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ u32 val;
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+ if (vt8500_chip->regs->en == NO_REG)
+ return 0;
+
+ val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->en);
+ val |= BIT(offset);
+ writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->en);
+
+ return 0;
+}
+
+static void vt8500_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+ u32 val;
+
+ if (vt8500_chip->regs->en == NO_REG)
+ return;
+
+ val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->en);
+ val &= ~BIT(offset);
+ writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->en);
+}
+
+static int vt8500_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+ u32 val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->dir);
+ val &= ~BIT(offset);
+ writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->dir);
+
+ return 0;
+}
+
+static int vt8500_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+ u32 val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->dir);
+ val |= BIT(offset);
+ writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->dir);
+
+ if (value) {
+ val = readl_relaxed(vt8500_chip->base +
+ vt8500_chip->regs->data_out);
+ val |= BIT(offset);
+ writel_relaxed(val, vt8500_chip->base +
+ vt8500_chip->regs->data_out);
+ }
+ return 0;
+}
+
+static int vt8500_gpio_get_value(struct gpio_chip *chip, unsigned offset)
+{
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+ return (readl_relaxed(vt8500_chip->base + vt8500_chip->regs->data_in) >>
+ offset) & 1;
+}
+
+static void vt8500_gpio_set_value(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+ u32 val = readl_relaxed(vt8500_chip->base +
+ vt8500_chip->regs->data_out);
+ if (value)
+ val |= BIT(offset);
+ else
+ val &= ~BIT(offset);
+
+ writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->data_out);
+}
+
+static int vt8500_of_xlate(struct gpio_chip *gc,
+ const struct of_phandle_args *gpiospec, u32 *flags)
+{
+ /* bank if specificed in gpiospec->args[0] */
+ if (flags)
+ *flags = gpiospec->args[2];
+
+ return gpiospec->args[1];
+}
+
+static int vt8500_add_chips(struct platform_device *pdev, void __iomem *base,
+ const struct vt8500_gpio_data *data)
+{
+ struct vt8500_gpio_chip *vtchip;
+ struct gpio_chip *chip;
+ int i;
+ int pin_cnt = 0;
+
+ vtchip = devm_kzalloc(&pdev->dev,
+ sizeof(struct vt8500_gpio_chip) * data->num_banks,
+ GFP_KERNEL);
+ if (!vtchip) {
+ pr_err("%s: failed to allocate chip memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < data->num_banks; i++) {
+ vtchip[i].base = base;
+ vtchip[i].regs = &data->banks[i];
+
+ chip = &vtchip[i].chip;
+
+ chip->of_xlate = vt8500_of_xlate;
+ chip->of_gpio_n_cells = 3;
+ chip->of_node = pdev->dev.of_node;
+
+ chip->request = vt8500_gpio_request;
+ chip->free = vt8500_gpio_free;
+ chip->direction_input = vt8500_gpio_direction_input;
+ chip->direction_output = vt8500_gpio_direction_output;
+ chip->get = vt8500_gpio_get_value;
+ chip->set = vt8500_gpio_set_value;
+ chip->can_sleep = 0;
+ chip->base = pin_cnt;
+ chip->ngpio = data->banks[i].ngpio;
+
+ pin_cnt += data->banks[i].ngpio;
+
+ gpiochip_add(chip);
+ }
+ return 0;
+}
+
+static struct of_device_id vt8500_gpio_dt_ids[] = {
+ { .compatible = "via,vt8500-gpio", .data = &vt8500_data, },
+ { .compatible = "wm,wm8505-gpio", .data = &wm8505_data, },
+ { .compatible = "wm,wm8650-gpio", .data = &wm8650_data, },
+ { /* Sentinel */ },
+};
+
+static int __devinit vt8500_gpio_probe(struct platform_device *pdev)
+{
+ void __iomem *gpio_base;
+ struct device_node *np;
+ const struct of_device_id *of_id =
+ of_match_device(vt8500_gpio_dt_ids, &pdev->dev);
+
+ if (!of_id) {
+ dev_err(&pdev->dev, "Failed to find gpio controller\n");
+ return -ENODEV;
+ }
+
+ np = pdev->dev.of_node;
+ if (!np) {
+ dev_err(&pdev->dev, "Missing GPIO description in devicetree\n");
+ return -EFAULT;
+ }
+
+ gpio_base = of_iomap(np, 0);
+ if (!gpio_base) {
+ dev_err(&pdev->dev, "Unable to map GPIO registers\n");
+ of_node_put(np);
+ return -ENOMEM;
+ }
+
+ vt8500_add_chips(pdev, gpio_base, of_id->data);
+
+ return 0;
+}
+
+static struct platform_driver vt8500_gpio_driver = {
+ .probe = vt8500_gpio_probe,
+ .driver = {
+ .name = "vt8500-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = vt8500_gpio_dt_ids,
+ },
+};
+
+module_platform_driver(vt8500_gpio_driver);
+
+MODULE_DESCRIPTION("VT8500 GPIO Driver");
+MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, vt8500_gpio_dt_ids);
diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c
index e56a2165641..b6eda35089d 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 a06af515483..fb429388939 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 de0213c9d11..5d6c71edc73 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)