aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig14
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-adnp.c155
-rw-r--r--drivers/gpio/gpio-bcm-kona.c2
-rw-r--r--drivers/gpio/gpio-crystalcove.c28
-rw-r--r--drivers/gpio/gpio-cs5535.c9
-rw-r--r--drivers/gpio/gpio-dwapb.c410
-rw-r--r--drivers/gpio/gpio-ks8695.c30
-rw-r--r--drivers/gpio/gpio-mcp23s08.c64
-rw-r--r--drivers/gpio/gpio-omap.c33
-rw-r--r--drivers/gpio/gpio-pca953x.c54
-rw-r--r--drivers/gpio/gpio-pch.c2
-rw-r--r--drivers/gpio/gpio-samsung.c50
-rw-r--r--drivers/gpio/gpio-stmpe.c99
-rw-r--r--drivers/gpio/gpio-stp-xway.c2
-rw-r--r--drivers/gpio/gpio-syscon.c94
-rw-r--r--drivers/gpio/gpio-tc3589x.c5
-rw-r--r--drivers/gpio/gpio-xgene.c244
-rw-r--r--drivers/gpio/gpio-xilinx.c27
-rw-r--r--drivers/gpio/gpio-zynq.c89
-rw-r--r--drivers/gpio/gpiolib-acpi.c35
-rw-r--r--drivers/gpio/gpiolib.c92
22 files changed, 1059 insertions, 480 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 9de1515e5808..0959ca9b6b27 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -136,7 +136,6 @@ config GPIO_DWAPB
tristate "Synopsys DesignWare APB GPIO driver"
select GPIO_GENERIC
select GENERIC_IRQ_CHIP
- depends on OF_GPIO
help
Say Y or M here to build support for the Synopsys DesignWare APB
GPIO block.
@@ -334,6 +333,15 @@ config GPIO_TZ1090_PDC
help
Say yes here to support Toumaz Xenif TZ1090 PDC GPIOs.
+config GPIO_XGENE
+ bool "APM X-Gene GPIO controller support"
+ depends on ARM64 && OF_GPIO
+ help
+ This driver is to support the GPIO block within the APM X-Gene SoC
+ platform's generic flash controller. The GPIO pins are muxed with
+ the generic flash controller's address and data pins. Say yes
+ here to enable the GFC GPIO functionality.
+
config GPIO_XILINX
bool "Xilinx GPIO support"
depends on PPC_OF || MICROBLAZE || ARCH_ZYNQ
@@ -681,6 +689,7 @@ config GPIO_ADP5588_IRQ
config GPIO_ADNP
tristate "Avionic Design N-bit GPIO expander"
depends on I2C && OF_GPIO
+ select GPIOLIB_IRQCHIP
help
This option enables support for N GPIOs found on Avionic Design
I2C GPIO expanders. The register space will be extended by powers
@@ -796,7 +805,6 @@ config GPIO_MAX7301
config GPIO_MCP23S08
tristate "Microchip MCP23xxx I/O expander"
- depends on OF_GPIO
depends on (SPI_MASTER && !I2C) || I2C
help
SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017
@@ -880,7 +888,7 @@ config GPIO_MSIC
config GPIO_BCM_KONA
bool "Broadcom Kona GPIO"
- depends on OF_GPIO
+ depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST)
help
Turn on GPIO support for Broadcom "Kona" chips.
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 5d024e396622..e5d346cf3b6e 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -101,6 +101,7 @@ obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o
obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o
obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o
+obj-$(CONFIG_GPIO_XGENE) += gpio-xgene.o
obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o
obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o
obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c
index 416b2200d4f1..d3d0a90fe542 100644
--- a/drivers/gpio/gpio-adnp.c
+++ b/drivers/gpio/gpio-adnp.c
@@ -6,10 +6,9 @@
* published by the Free Software Foundation.
*/
-#include <linux/gpio.h>
+#include <linux/gpio/driver.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>
@@ -27,8 +26,6 @@ struct adnp {
unsigned int reg_shift;
struct mutex i2c_lock;
-
- struct irq_domain *domain;
struct mutex irq_lock;
u8 *irq_enable;
@@ -253,6 +250,7 @@ static void adnp_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios)
{
struct gpio_chip *chip = &adnp->gpio;
+ int err;
adnp->reg_shift = get_count_order(num_gpios) - 3;
@@ -272,6 +270,10 @@ static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios)
chip->of_node = chip->dev->of_node;
chip->owner = THIS_MODULE;
+ err = gpiochip_add(chip);
+ if (err)
+ return err;
+
return 0;
}
@@ -326,7 +328,8 @@ static irqreturn_t adnp_irq(int irq, void *data)
for_each_set_bit(bit, &pending, 8) {
unsigned int child_irq;
- child_irq = irq_find_mapping(adnp->domain, base + bit);
+ child_irq = irq_find_mapping(adnp->gpio.irqdomain,
+ base + bit);
handle_nested_irq(child_irq);
}
}
@@ -334,35 +337,32 @@ static irqreturn_t adnp_irq(int irq, void *data)
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)
+static void adnp_irq_mask(struct irq_data *d)
{
- struct adnp *adnp = irq_data_get_irq_chip_data(data);
- unsigned int reg = data->hwirq >> adnp->reg_shift;
- unsigned int pos = data->hwirq & 7;
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adnp *adnp = to_adnp(gc);
+ unsigned int reg = d->hwirq >> adnp->reg_shift;
+ unsigned int pos = d->hwirq & 7;
adnp->irq_enable[reg] &= ~BIT(pos);
}
-static void adnp_irq_unmask(struct irq_data *data)
+static void adnp_irq_unmask(struct irq_data *d)
{
- struct adnp *adnp = irq_data_get_irq_chip_data(data);
- unsigned int reg = data->hwirq >> adnp->reg_shift;
- unsigned int pos = data->hwirq & 7;
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adnp *adnp = to_adnp(gc);
+ unsigned int reg = d->hwirq >> adnp->reg_shift;
+ unsigned int pos = d->hwirq & 7;
adnp->irq_enable[reg] |= BIT(pos);
}
-static int adnp_irq_set_type(struct irq_data *data, unsigned int type)
+static int adnp_irq_set_type(struct irq_data *d, 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;
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adnp *adnp = to_adnp(gc);
+ unsigned int reg = d->hwirq >> adnp->reg_shift;
+ unsigned int pos = d->hwirq & 7;
if (type & IRQ_TYPE_EDGE_RISING)
adnp->irq_rise[reg] |= BIT(pos);
@@ -387,16 +387,18 @@ static int adnp_irq_set_type(struct irq_data *data, unsigned int type)
return 0;
}
-static void adnp_irq_bus_lock(struct irq_data *data)
+static void adnp_irq_bus_lock(struct irq_data *d)
{
- struct adnp *adnp = irq_data_get_irq_chip_data(data);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adnp *adnp = to_adnp(gc);
mutex_lock(&adnp->irq_lock);
}
-static void adnp_irq_bus_unlock(struct irq_data *data)
+static void adnp_irq_bus_unlock(struct irq_data *d)
{
- struct adnp *adnp = irq_data_get_irq_chip_data(data);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adnp *adnp = to_adnp(gc);
unsigned int num_regs = 1 << adnp->reg_shift, i;
mutex_lock(&adnp->i2c_lock);
@@ -408,26 +410,6 @@ static void adnp_irq_bus_unlock(struct irq_data *data)
mutex_unlock(&adnp->irq_lock);
}
-static int adnp_irq_reqres(struct irq_data *data)
-{
- struct adnp *adnp = irq_data_get_irq_chip_data(data);
-
- if (gpio_lock_as_irq(&adnp->gpio, data->hwirq)) {
- dev_err(adnp->gpio.dev,
- "unable to lock HW IRQ %lu for IRQ\n",
- data->hwirq);
- return -EINVAL;
- }
- return 0;
-}
-
-static void adnp_irq_relres(struct irq_data *data)
-{
- struct adnp *adnp = irq_data_get_irq_chip_data(data);
-
- gpio_unlock_as_irq(&adnp->gpio, data->hwirq);
-}
-
static struct irq_chip adnp_irq_chip = {
.name = "gpio-adnp",
.irq_mask = adnp_irq_mask,
@@ -435,29 +417,6 @@ static struct irq_chip adnp_irq_chip = {
.irq_set_type = adnp_irq_set_type,
.irq_bus_lock = adnp_irq_bus_lock,
.irq_bus_sync_unlock = adnp_irq_bus_unlock,
- .irq_request_resources = adnp_irq_reqres,
- .irq_release_resources = adnp_irq_relres,
-};
-
-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)
@@ -503,35 +462,28 @@ static int adnp_irq_setup(struct adnp *adnp)
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);
+ err = devm_request_threaded_irq(chip->dev, 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);
return err;
}
- chip->to_irq = adnp_gpio_to_irq;
- return 0;
-}
-
-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);
+ err = gpiochip_irqchip_add(chip,
+ &adnp_irq_chip,
+ 0,
+ handle_simple_irq,
+ IRQ_TYPE_NONE);
+ if (err) {
+ dev_err(chip->dev,
+ "could not connect irqchip to gpiochip\n");
+ return err;
}
- irq_domain_remove(adnp->domain);
+ return 0;
}
static int adnp_i2c_probe(struct i2c_client *client,
@@ -558,38 +510,25 @@ static int adnp_i2c_probe(struct i2c_client *client,
adnp->client = client;
err = adnp_gpio_setup(adnp, num_gpios);
- if (err < 0)
+ if (err)
return err;
if (of_find_property(np, "interrupt-controller", NULL)) {
err = adnp_irq_setup(adnp);
- if (err < 0)
- goto teardown;
+ if (err)
+ return err;
}
- 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;
+ return 0;
}
static int adnp_i2c_remove(struct i2c_client *client)
{
struct adnp *adnp = i2c_get_clientdata(client);
- struct device_node *np = client->dev.of_node;
gpiochip_remove(&adnp->gpio);
- if (of_find_property(np, "interrupt-controller", NULL))
- adnp_irq_teardown(adnp);
-
return 0;
}
diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c
index 3f6b33ce9bd4..de0801e9767a 100644
--- a/drivers/gpio/gpio-bcm-kona.c
+++ b/drivers/gpio/gpio-bcm-kona.c
@@ -496,7 +496,7 @@ static struct irq_chip bcm_gpio_irq_chip = {
.irq_release_resources = bcm_kona_gpio_irq_relres,
};
-static struct __initconst of_device_id bcm_kona_gpio_of_match[] = {
+static struct of_device_id const bcm_kona_gpio_of_match[] = {
{ .compatible = "brcm,kona-gpio" },
{}
};
diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c
index 934462f5bd22..bbfe7f508502 100644
--- a/drivers/gpio/gpio-crystalcove.c
+++ b/drivers/gpio/gpio-crystalcove.c
@@ -24,6 +24,7 @@
#include <linux/mfd/intel_soc_pmic.h>
#define CRYSTALCOVE_GPIO_NUM 16
+#define CRYSTALCOVE_VGPIO_NUM 94
#define UPDATE_IRQ_TYPE BIT(0)
#define UPDATE_IRQ_MASK BIT(1)
@@ -130,6 +131,9 @@ static int crystalcove_gpio_dir_in(struct gpio_chip *chip, unsigned gpio)
{
struct crystalcove_gpio *cg = to_cg(chip);
+ if (gpio > CRYSTALCOVE_VGPIO_NUM)
+ return 0;
+
return regmap_write(cg->regmap, to_reg(gpio, CTRL_OUT),
CTLO_INPUT_SET);
}
@@ -139,6 +143,9 @@ static int crystalcove_gpio_dir_out(struct gpio_chip *chip, unsigned gpio,
{
struct crystalcove_gpio *cg = to_cg(chip);
+ if (gpio > CRYSTALCOVE_VGPIO_NUM)
+ return 0;
+
return regmap_write(cg->regmap, to_reg(gpio, CTRL_OUT),
CTLO_OUTPUT_SET | value);
}
@@ -149,6 +156,9 @@ static int crystalcove_gpio_get(struct gpio_chip *chip, unsigned gpio)
int ret;
unsigned int val;
+ if (gpio > CRYSTALCOVE_VGPIO_NUM)
+ return 0;
+
ret = regmap_read(cg->regmap, to_reg(gpio, CTRL_IN), &val);
if (ret)
return ret;
@@ -161,6 +171,9 @@ static void crystalcove_gpio_set(struct gpio_chip *chip,
{
struct crystalcove_gpio *cg = to_cg(chip);
+ if (gpio > CRYSTALCOVE_VGPIO_NUM)
+ return;
+
if (value)
regmap_update_bits(cg->regmap, to_reg(gpio, CTRL_OUT), 1, 1);
else
@@ -256,7 +269,7 @@ static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data)
pending = p0 | p1 << 8;
- for (gpio = 0; gpio < cg->chip.ngpio; gpio++) {
+ for (gpio = 0; gpio < CRYSTALCOVE_GPIO_NUM; gpio++) {
if (pending & BIT(gpio)) {
virq = irq_find_mapping(cg->chip.irqdomain, gpio);
generic_handle_irq(virq);
@@ -273,7 +286,7 @@ static void crystalcove_gpio_dbg_show(struct seq_file *s,
int gpio, offset;
unsigned int ctlo, ctli, mirqs0, mirqsx, irq;
- for (gpio = 0; gpio < cg->chip.ngpio; gpio++) {
+ for (gpio = 0; gpio < CRYSTALCOVE_GPIO_NUM; gpio++) {
regmap_read(cg->regmap, to_reg(gpio, CTRL_OUT), &ctlo);
regmap_read(cg->regmap, to_reg(gpio, CTRL_IN), &ctli);
regmap_read(cg->regmap, gpio < 8 ? MGPIO0IRQS0 : MGPIO1IRQS0,
@@ -320,7 +333,7 @@ static int crystalcove_gpio_probe(struct platform_device *pdev)
cg->chip.get = crystalcove_gpio_get;
cg->chip.set = crystalcove_gpio_set;
cg->chip.base = -1;
- cg->chip.ngpio = CRYSTALCOVE_GPIO_NUM;
+ cg->chip.ngpio = CRYSTALCOVE_VGPIO_NUM;
cg->chip.can_sleep = true;
cg->chip.dev = dev;
cg->chip.dbg_show = crystalcove_gpio_dbg_show;
@@ -346,7 +359,7 @@ static int crystalcove_gpio_probe(struct platform_device *pdev)
return 0;
out_remove_gpio:
- WARN_ON(gpiochip_remove(&cg->chip));
+ gpiochip_remove(&cg->chip);
return retval;
}
@@ -354,14 +367,11 @@ static int crystalcove_gpio_remove(struct platform_device *pdev)
{
struct crystalcove_gpio *cg = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
- int err;
-
- err = gpiochip_remove(&cg->chip);
+ gpiochip_remove(&cg->chip);
if (irq >= 0)
free_irq(irq, cg);
-
- return err;
+ return 0;
}
static struct platform_driver crystalcove_gpio_driver = {
diff --git a/drivers/gpio/gpio-cs5535.c b/drivers/gpio/gpio-cs5535.c
index 92ec58fa9236..668127fe90ef 100644
--- a/drivers/gpio/gpio-cs5535.c
+++ b/drivers/gpio/gpio-cs5535.c
@@ -201,7 +201,8 @@ EXPORT_SYMBOL_GPL(cs5535_gpio_setup_event);
static int chip_gpio_request(struct gpio_chip *c, unsigned offset)
{
- struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
+ struct cs5535_gpio_chip *chip =
+ container_of(c, struct cs5535_gpio_chip, chip);
unsigned long flags;
spin_lock_irqsave(&chip->lock, flags);
@@ -241,7 +242,8 @@ static void chip_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
static int chip_direction_input(struct gpio_chip *c, unsigned offset)
{
- struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
+ struct cs5535_gpio_chip *chip =
+ container_of(c, struct cs5535_gpio_chip, chip);
unsigned long flags;
spin_lock_irqsave(&chip->lock, flags);
@@ -254,7 +256,8 @@ static int chip_direction_input(struct gpio_chip *c, unsigned offset)
static int chip_direction_output(struct gpio_chip *c, unsigned offset, int val)
{
- struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
+ struct cs5535_gpio_chip *chip =
+ container_of(c, struct cs5535_gpio_chip, chip);
unsigned long flags;
spin_lock_irqsave(&chip->lock, flags);
diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c
index d6618a6e2399..b43cd84b61f1 100644
--- a/drivers/gpio/gpio-dwapb.c
+++ b/drivers/gpio/gpio-dwapb.c
@@ -21,6 +21,8 @@
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
+#include <linux/platform_data/gpio-dwapb.h>
+#include <linux/slab.h>
#define GPIO_SWPORTA_DR 0x00
#define GPIO_SWPORTA_DDR 0x04
@@ -35,6 +37,7 @@
#define GPIO_INTTYPE_LEVEL 0x38
#define GPIO_INT_POLARITY 0x3c
#define GPIO_INTSTATUS 0x40
+#define GPIO_PORTA_DEBOUNCE 0x48
#define GPIO_PORTA_EOI 0x4c
#define GPIO_EXT_PORTA 0x50
#define GPIO_EXT_PORTB 0x54
@@ -48,10 +51,28 @@
struct dwapb_gpio;
+#ifdef CONFIG_PM_SLEEP
+/* Store GPIO context across system-wide suspend/resume transitions */
+struct dwapb_context {
+ u32 data;
+ u32 dir;
+ u32 ext;
+ u32 int_en;
+ u32 int_mask;
+ u32 int_type;
+ u32 int_pol;
+ u32 int_deb;
+};
+#endif
+
struct dwapb_gpio_port {
struct bgpio_chip bgc;
bool is_registered;
struct dwapb_gpio *gpio;
+#ifdef CONFIG_PM_SLEEP
+ struct dwapb_context *ctx;
+#endif
+ unsigned int idx;
};
struct dwapb_gpio {
@@ -62,11 +83,33 @@ struct dwapb_gpio {
struct irq_domain *domain;
};
+static inline struct dwapb_gpio_port *
+to_dwapb_gpio_port(struct bgpio_chip *bgc)
+{
+ return container_of(bgc, struct dwapb_gpio_port, bgc);
+}
+
+static inline u32 dwapb_read(struct dwapb_gpio *gpio, unsigned int offset)
+{
+ struct bgpio_chip *bgc = &gpio->ports[0].bgc;
+ void __iomem *reg_base = gpio->regs;
+
+ return bgc->read_reg(reg_base + offset);
+}
+
+static inline void dwapb_write(struct dwapb_gpio *gpio, unsigned int offset,
+ u32 val)
+{
+ struct bgpio_chip *bgc = &gpio->ports[0].bgc;
+ void __iomem *reg_base = gpio->regs;
+
+ bgc->write_reg(reg_base + offset, val);
+}
+
static int dwapb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
{
struct bgpio_chip *bgc = to_bgpio_chip(gc);
- struct dwapb_gpio_port *port = container_of(bgc, struct
- dwapb_gpio_port, bgc);
+ struct dwapb_gpio_port *port = to_dwapb_gpio_port(bgc);
struct dwapb_gpio *gpio = port->gpio;
return irq_find_mapping(gpio->domain, offset);
@@ -74,21 +117,20 @@ static int dwapb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs)
{
- u32 v = readl(gpio->regs + GPIO_INT_POLARITY);
+ u32 v = dwapb_read(gpio, GPIO_INT_POLARITY);
if (gpio_get_value(gpio->ports[0].bgc.gc.base + offs))
v &= ~BIT(offs);
else
v |= BIT(offs);
- writel(v, gpio->regs + GPIO_INT_POLARITY);
+ dwapb_write(gpio, GPIO_INT_POLARITY, v);
}
-static void dwapb_irq_handler(u32 irq, struct irq_desc *desc)
+static u32 dwapb_do_irq(struct dwapb_gpio *gpio)
{
- struct dwapb_gpio *gpio = irq_get_handler_data(irq);
- struct irq_chip *chip = irq_desc_get_chip(desc);
u32 irq_status = readl_relaxed(gpio->regs + GPIO_INTSTATUS);
+ u32 ret = irq_status;
while (irq_status) {
int hwirq = fls(irq_status) - 1;
@@ -102,6 +144,16 @@ static void dwapb_irq_handler(u32 irq, struct irq_desc *desc)
dwapb_toggle_trigger(gpio, hwirq);
}
+ return ret;
+}
+
+static void dwapb_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ struct dwapb_gpio *gpio = irq_get_handler_data(irq);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+
+ dwapb_do_irq(gpio);
+
if (chip->irq_eoi)
chip->irq_eoi(irq_desc_get_irq_data(desc));
}
@@ -115,9 +167,9 @@ static void dwapb_irq_enable(struct irq_data *d)
u32 val;
spin_lock_irqsave(&bgc->lock, flags);
- val = readl(gpio->regs + GPIO_INTEN);
+ val = dwapb_read(gpio, GPIO_INTEN);
val |= BIT(d->hwirq);
- writel(val, gpio->regs + GPIO_INTEN);
+ dwapb_write(gpio, GPIO_INTEN, val);
spin_unlock_irqrestore(&bgc->lock, flags);
}
@@ -130,9 +182,9 @@ static void dwapb_irq_disable(struct irq_data *d)
u32 val;
spin_lock_irqsave(&bgc->lock, flags);
- val = readl(gpio->regs + GPIO_INTEN);
+ val = dwapb_read(gpio, GPIO_INTEN);
val &= ~BIT(d->hwirq);
- writel(val, gpio->regs + GPIO_INTEN);
+ dwapb_write(gpio, GPIO_INTEN, val);
spin_unlock_irqrestore(&bgc->lock, flags);
}
@@ -172,8 +224,8 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type)
return -EINVAL;
spin_lock_irqsave(&bgc->lock, flags);
- level = readl(gpio->regs + GPIO_INTTYPE_LEVEL);
- polarity = readl(gpio->regs + GPIO_INT_POLARITY);
+ level = dwapb_read(gpio, GPIO_INTTYPE_LEVEL);
+ polarity = dwapb_read(gpio, GPIO_INT_POLARITY);
switch (type) {
case IRQ_TYPE_EDGE_BOTH:
@@ -200,29 +252,55 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type)
irq_setup_alt_chip(d, type);
- writel(level, gpio->regs + GPIO_INTTYPE_LEVEL);
- writel(polarity, gpio->regs + GPIO_INT_POLARITY);
+ dwapb_write(gpio, GPIO_INTTYPE_LEVEL, level);
+ dwapb_write(gpio, GPIO_INT_POLARITY, polarity);
+ spin_unlock_irqrestore(&bgc->lock, flags);
+
+ return 0;
+}
+
+static int dwapb_gpio_set_debounce(struct gpio_chip *gc,
+ unsigned offset, unsigned debounce)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ struct dwapb_gpio_port *port = to_dwapb_gpio_port(bgc);
+ struct dwapb_gpio *gpio = port->gpio;
+ unsigned long flags, val_deb;
+ unsigned long mask = bgc->pin2mask(bgc, offset);
+
+ spin_lock_irqsave(&bgc->lock, flags);
+
+ val_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE);
+ if (debounce)
+ dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, val_deb | mask);
+ else
+ dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, val_deb & ~mask);
+
spin_unlock_irqrestore(&bgc->lock, flags);
return 0;
}
+static irqreturn_t dwapb_irq_handler_mfd(int irq, void *dev_id)
+{
+ u32 worked;
+ struct dwapb_gpio *gpio = dev_id;
+
+ worked = dwapb_do_irq(gpio);
+
+ return worked ? IRQ_HANDLED : IRQ_NONE;
+}
+
static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
- struct dwapb_gpio_port *port)
+ struct dwapb_gpio_port *port,
+ struct dwapb_port_property *pp)
{
struct gpio_chip *gc = &port->bgc.gc;
- struct device_node *node = gc->of_node;
- struct irq_chip_generic *irq_gc;
+ struct device_node *node = pp->node;
+ struct irq_chip_generic *irq_gc = NULL;
unsigned int hwirq, ngpio = gc->ngpio;
struct irq_chip_type *ct;
- int err, irq, i;
-
- irq = irq_of_parse_and_map(node, 0);
- if (!irq) {
- dev_warn(gpio->dev, "no irq for bank %s\n",
- port->bgc.gc.of_node->full_name);
- return;
- }
+ int err, i;
gpio->domain = irq_domain_add_linear(node, ngpio,
&irq_generic_chip_ops, gpio);
@@ -269,8 +347,24 @@ static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
irq_gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH;
irq_gc->chip_types[1].handler = handle_edge_irq;
- irq_set_chained_handler(irq, dwapb_irq_handler);
- irq_set_handler_data(irq, gpio);
+ if (!pp->irq_shared) {
+ irq_set_chained_handler(pp->irq, dwapb_irq_handler);
+ irq_set_handler_data(pp->irq, gpio);
+ } else {
+ /*
+ * Request a shared IRQ since where MFD would have devices
+ * using the same irq pin
+ */
+ err = devm_request_irq(gpio->dev, pp->irq,
+ dwapb_irq_handler_mfd,
+ IRQF_SHARED, "gpio-dwapb-mfd", gpio);
+ if (err) {
+ dev_err(gpio->dev, "error requesting IRQ\n");
+ irq_domain_remove(gpio->domain);
+ gpio->domain = NULL;
+ return;
+ }
+ }
for (hwirq = 0 ; hwirq < ngpio ; hwirq++)
irq_create_mapping(gpio->domain, hwirq);
@@ -296,57 +390,53 @@ static void dwapb_irq_teardown(struct dwapb_gpio *gpio)
}
static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
- struct device_node *port_np,
+ struct dwapb_port_property *pp,
unsigned int offs)
{
struct dwapb_gpio_port *port;
- u32 port_idx, ngpio;
void __iomem *dat, *set, *dirout;
int err;
- if (of_property_read_u32(port_np, "reg", &port_idx) ||
- port_idx >= DWAPB_MAX_PORTS) {
- dev_err(gpio->dev, "missing/invalid port index for %s\n",
- port_np->full_name);
- return -EINVAL;
- }
-
port = &gpio->ports[offs];
port->gpio = gpio;
+ port->idx = pp->idx;
- if (of_property_read_u32(port_np, "snps,nr-gpios", &ngpio)) {
- dev_info(gpio->dev, "failed to get number of gpios for %s\n",
- port_np->full_name);
- ngpio = 32;
- }
+#ifdef CONFIG_PM_SLEEP
+ port->ctx = devm_kzalloc(gpio->dev, sizeof(*port->ctx), GFP_KERNEL);
+ if (!port->ctx)
+ return -ENOMEM;
+#endif
- dat = gpio->regs + GPIO_EXT_PORTA + (port_idx * GPIO_EXT_PORT_SIZE);
- set = gpio->regs + GPIO_SWPORTA_DR + (port_idx * GPIO_SWPORT_DR_SIZE);
+ dat = gpio->regs + GPIO_EXT_PORTA + (pp->idx * GPIO_EXT_PORT_SIZE);
+ set = gpio->regs + GPIO_SWPORTA_DR + (pp->idx * GPIO_SWPORT_DR_SIZE);
dirout = gpio->regs + GPIO_SWPORTA_DDR +
- (port_idx * GPIO_SWPORT_DDR_SIZE);
+ (pp->idx * GPIO_SWPORT_DDR_SIZE);
err = bgpio_init(&port->bgc, gpio->dev, 4, dat, set, NULL, dirout,
NULL, false);
if (err) {
dev_err(gpio->dev, "failed to init gpio chip for %s\n",
- port_np->full_name);
+ pp->name);
return err;
}
- port->bgc.gc.ngpio = ngpio;
- port->bgc.gc.of_node = port_np;
+#ifdef CONFIG_OF_GPIO
+ port->bgc.gc.of_node = pp->node;
+#endif
+ port->bgc.gc.ngpio = pp->ngpio;
+ port->bgc.gc.base = pp->gpio_base;
- /*
- * Only port A can provide interrupts in all configurations of the IP.
- */
- if (port_idx == 0 &&
- of_property_read_bool(port_np, "interrupt-controller"))
- dwapb_configure_irqs(gpio, port);
+ /* Only port A support debounce */
+ if (pp->idx == 0)
+ port->bgc.gc.set_debounce = dwapb_gpio_set_debounce;
+
+ if (pp->irq)
+ dwapb_configure_irqs(gpio, port, pp);
err = gpiochip_add(&port->bgc.gc);
if (err)
dev_err(gpio->dev, "failed to register gpiochip for %s\n",
- port_np->full_name);
+ pp->name);
else
port->is_registered = true;
@@ -362,25 +452,116 @@ static void dwapb_gpio_unregister(struct dwapb_gpio *gpio)
gpiochip_remove(&gpio->ports[m].bgc.gc);
}
+static struct dwapb_platform_data *
+dwapb_gpio_get_pdata_of(struct device *dev)
+{
+ struct device_node *node, *port_np;
+ struct dwapb_platform_data *pdata;
+ struct dwapb_port_property *pp;
+ int nports;
+ int i;
+
+ node = dev->of_node;
+ if (!IS_ENABLED(CONFIG_OF_GPIO) || !node)
+ return ERR_PTR(-ENODEV);
+
+ nports = of_get_child_count(node);
+ if (nports == 0)
+ return ERR_PTR(-ENODEV);
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->properties = kcalloc(nports, sizeof(*pp), GFP_KERNEL);
+ if (!pdata->properties) {
+ kfree(pdata);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ pdata->nports = nports;
+
+ i = 0;
+ for_each_child_of_node(node, port_np) {
+ pp = &pdata->properties[i++];
+ pp->node = port_np;
+
+ if (of_property_read_u32(port_np, "reg", &pp->idx) ||
+ pp->idx >= DWAPB_MAX_PORTS) {
+ dev_err(dev, "missing/invalid port index for %s\n",
+ port_np->full_name);
+ kfree(pdata->properties);
+ kfree(pdata);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_u32(port_np, "snps,nr-gpios",
+ &pp->ngpio)) {
+ dev_info(dev, "failed to get number of gpios for %s\n",
+ port_np->full_name);
+ pp->ngpio = 32;
+ }
+
+ /*
+ * Only port A can provide interrupts in all configurations of
+ * the IP.
+ */
+ if (pp->idx == 0 &&
+ of_property_read_bool(port_np, "interrupt-controller")) {
+ pp->irq = irq_of_parse_and_map(port_np, 0);
+ if (!pp->irq) {
+ dev_warn(dev, "no irq for bank %s\n",
+ port_np->full_name);
+ }
+ }
+
+ pp->irq_shared = false;
+ pp->gpio_base = -1;
+ pp->name = port_np->full_name;
+ }
+
+ return pdata;
+}
+
+static inline void dwapb_free_pdata_of(struct dwapb_platform_data *pdata)
+{
+ if (!IS_ENABLED(CONFIG_OF_GPIO) || !pdata)
+ return;
+
+ kfree(pdata->properties);
+ kfree(pdata);
+}
+
static int dwapb_gpio_probe(struct platform_device *pdev)
{
+ unsigned int i;
struct resource *res;
struct dwapb_gpio *gpio;
- struct device_node *np;
int err;
- unsigned int offs = 0;
+ struct device *dev = &pdev->dev;
+ struct dwapb_platform_data *pdata = dev_get_platdata(dev);
+ bool is_pdata_alloc = !pdata;
+
+ if (is_pdata_alloc) {
+ pdata = dwapb_gpio_get_pdata_of(dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ }
- gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
- if (!gpio)
- return -ENOMEM;
- gpio->dev = &pdev->dev;
+ if (!pdata->nports) {
+ err = -ENODEV;
+ goto out_err;
+ }
- gpio->nr_ports = of_get_child_count(pdev->dev.of_node);
- if (!gpio->nr_ports) {
- err = -EINVAL;
+ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio) {
+ err = -ENOMEM;
goto out_err;
}
- gpio->ports = devm_kzalloc(&pdev->dev, gpio->nr_ports *
+ gpio->dev = &pdev->dev;
+ gpio->nr_ports = pdata->nports;
+
+ gpio->ports = devm_kcalloc(&pdev->dev, gpio->nr_ports,
sizeof(*gpio->ports), GFP_KERNEL);
if (!gpio->ports) {
err = -ENOMEM;
@@ -394,20 +575,23 @@ static int dwapb_gpio_probe(struct platform_device *pdev)
goto out_err;
}
- for_each_child_of_node(pdev->dev.of_node, np) {
- err = dwapb_gpio_add_port(gpio, np, offs++);
+ for (i = 0; i < gpio->nr_ports; i++) {
+ err = dwapb_gpio_add_port(gpio, &pdata->properties[i], i);
if (err)
goto out_unregister;
}
platform_set_drvdata(pdev, gpio);
- return 0;
+ goto out_err;
out_unregister:
dwapb_gpio_unregister(gpio);
dwapb_irq_teardown(gpio);
out_err:
+ if (is_pdata_alloc)
+ dwapb_free_pdata_of(pdata);
+
return err;
}
@@ -427,10 +611,100 @@ static const struct of_device_id dwapb_of_match[] = {
};
MODULE_DEVICE_TABLE(of, dwapb_of_match);
+#ifdef CONFIG_PM_SLEEP
+static int dwapb_gpio_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dwapb_gpio *gpio = platform_get_drvdata(pdev);
+ struct bgpio_chip *bgc = &gpio->ports[0].bgc;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+ for (i = 0; i < gpio->nr_ports; i++) {
+ unsigned int offset;
+ unsigned int idx = gpio->ports[i].idx;
+ struct dwapb_context *ctx = gpio->ports[i].ctx;
+
+ BUG_ON(!ctx);
+
+ offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_SIZE;
+ ctx->dir = dwapb_read(gpio, offset);
+
+ offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_SIZE;
+ ctx->data = dwapb_read(gpio, offset);
+
+ offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_SIZE;
+ ctx->ext = dwapb_read(gpio, offset);
+
+ /* Only port A can provide interrupts */
+ if (idx == 0) {
+ ctx->int_mask = dwapb_read(gpio, GPIO_INTMASK);
+ ctx->int_en = dwapb_read(gpio, GPIO_INTEN);
+ ctx->int_pol = dwapb_read(gpio, GPIO_INT_POLARITY);
+ ctx->int_type = dwapb_read(gpio, GPIO_INTTYPE_LEVEL);
+ ctx->int_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE);
+
+ /* Mask out interrupts */
+ dwapb_write(gpio, GPIO_INTMASK, 0xffffffff);
+ }
+ }
+ spin_unlock_irqrestore(&bgc->lock, flags);
+
+ return 0;
+}
+
+static int dwapb_gpio_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dwapb_gpio *gpio = platform_get_drvdata(pdev);
+ struct bgpio_chip *bgc = &gpio->ports[0].bgc;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+ for (i = 0; i < gpio->nr_ports; i++) {
+ unsigned int offset;
+ unsigned int idx = gpio->ports[i].idx;
+ struct dwapb_context *ctx = gpio->ports[i].ctx;
+
+ BUG_ON(!ctx);
+
+ offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_SIZE;
+ dwapb_write(gpio, offset, ctx->data);
+
+ offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_SIZE;
+ dwapb_write(gpio, offset, ctx->dir);
+
+ offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_SIZE;
+ dwapb_write(gpio, offset, ctx->ext);
+
+ /* Only port A can provide interrupts */
+ if (idx == 0) {
+ dwapb_write(gpio, GPIO_INTTYPE_LEVEL, ctx->int_type);
+ dwapb_write(gpio, GPIO_INT_POLARITY, ctx->int_pol);
+ dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, ctx->int_deb);
+ dwapb_write(gpio, GPIO_INTEN, ctx->int_en);
+ dwapb_write(gpio, GPIO_INTMASK, ctx->int_mask);
+
+ /* Clear out spurious interrupts */
+ dwapb_write(gpio, GPIO_PORTA_EOI, 0xffffffff);
+ }
+ }
+ spin_unlock_irqrestore(&bgc->lock, flags);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, dwapb_gpio_suspend,
+ dwapb_gpio_resume);
+
static struct platform_driver dwapb_gpio_driver = {
.driver = {
.name = "gpio-dwapb",
.owner = THIS_MODULE,
+ .pm = &dwapb_gpio_pm_ops,
.of_match_table = of_match_ptr(dwapb_of_match),
},
.probe = dwapb_gpio_probe,
diff --git a/drivers/gpio/gpio-ks8695.c b/drivers/gpio/gpio-ks8695.c
index 464a83de0d6a..cc09b237e88c 100644
--- a/drivers/gpio/gpio-ks8695.c
+++ b/drivers/gpio/gpio-ks8695.c
@@ -265,29 +265,27 @@ static int ks8695_gpio_show(struct seq_file *s, void *unused)
seq_printf(s, "EXT%i ", i);
switch ((ctrl & intmask[i]) >> (4 * i)) {
- case IOPC_TM_LOW:
- seq_printf(s, "(Low)"); break;
- case IOPC_TM_HIGH:
- seq_printf(s, "(High)"); break;
- case IOPC_TM_RISING:
- seq_printf(s, "(Rising)"); break;
- case IOPC_TM_FALLING:
- seq_printf(s, "(Falling)"); break;
- case IOPC_TM_EDGE:
- seq_printf(s, "(Edges)"); break;
+ case IOPC_TM_LOW:
+ seq_printf(s, "(Low)"); break;
+ case IOPC_TM_HIGH:
+ seq_printf(s, "(High)"); break;
+ case IOPC_TM_RISING:
+ seq_printf(s, "(Rising)"); break;
+ case IOPC_TM_FALLING:
+ seq_printf(s, "(Falling)"); break;
+ case IOPC_TM_EDGE:
+ seq_printf(s, "(Edges)"); break;
}
- }
- else
+ } else
seq_printf(s, "GPIO\t");
- }
- else if (i <= KS8695_GPIO_5) {
+ } else if (i <= KS8695_GPIO_5) {
if (ctrl & enable[i])
seq_printf(s, "TOUT%i\t", i - KS8695_GPIO_4);
else
seq_printf(s, "GPIO\t");
- }
- else
+ } else {
seq_printf(s, "GPIO\t");
+ }
seq_printf(s, "\t");
diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c
index 6f183d9b487e..8488e2fd307c 100644
--- a/drivers/gpio/gpio-mcp23s08.c
+++ b/drivers/gpio/gpio-mcp23s08.c
@@ -479,7 +479,7 @@ static int mcp23s08_irq_setup(struct mcp23s08 *mcp)
mutex_init(&mcp->irq_lock);
- mcp->irq_domain = irq_domain_add_linear(chip->of_node, chip->ngpio,
+ mcp->irq_domain = irq_domain_add_linear(chip->dev->of_node, chip->ngpio,
&irq_domain_simple_ops, mcp);
if (!mcp->irq_domain)
return -ENODEV;
@@ -581,7 +581,7 @@ done:
static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
void *data, unsigned addr, unsigned type,
- unsigned base, unsigned pullups)
+ struct mcp23s08_platform_data *pdata, int cs)
{
int status;
bool mirror = false;
@@ -635,7 +635,7 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
return -EINVAL;
}
- mcp->chip.base = base;
+ mcp->chip.base = pdata->base;
mcp->chip.can_sleep = true;
mcp->chip.dev = dev;
mcp->chip.owner = THIS_MODULE;
@@ -648,11 +648,9 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
if (status < 0)
goto fail;
- mcp->irq_controller = of_property_read_bool(mcp->chip.of_node,
- "interrupt-controller");
+ mcp->irq_controller = pdata->irq_controller;
if (mcp->irq && mcp->irq_controller && (type == MCP_TYPE_017))
- mirror = of_property_read_bool(mcp->chip.of_node,
- "microchip,irq-mirror");
+ mirror = pdata->mirror;
if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN) || mirror) {
/* mcp23s17 has IOCON twice, make sure they are in sync */
@@ -668,7 +666,7 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
}
/* configure ~100K pullups */
- status = mcp->ops->write(mcp, MCP_GPPU, pullups);
+ status = mcp->ops->write(mcp, MCP_GPPU, pdata->chip[cs].pullups);
if (status < 0)
goto fail;
@@ -768,25 +766,29 @@ MODULE_DEVICE_TABLE(of, mcp23s08_i2c_of_match);
static int mcp230xx_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct mcp23s08_platform_data *pdata;
+ struct mcp23s08_platform_data *pdata, local_pdata;
struct mcp23s08 *mcp;
- int status, base, pullups;
+ int status;
const struct of_device_id *match;
match = of_match_device(of_match_ptr(mcp23s08_i2c_of_match),
&client->dev);
- pdata = dev_get_platdata(&client->dev);
- if (match || !pdata) {
- base = -1;
- pullups = 0;
+ if (match) {
+ pdata = &local_pdata;
+ pdata->base = -1;
+ pdata->chip[0].pullups = 0;
+ pdata->irq_controller = of_property_read_bool(
+ client->dev.of_node,
+ "interrupt-controller");
+ pdata->mirror = of_property_read_bool(client->dev.of_node,
+ "microchip,irq-mirror");
client->irq = irq_of_parse_and_map(client->dev.of_node, 0);
} else {
- if (!gpio_is_valid(pdata->base)) {
+ pdata = dev_get_platdata(&client->dev);
+ if (!pdata || !gpio_is_valid(pdata->base)) {
dev_dbg(&client->dev, "invalid platform data\n");
return -EINVAL;
}
- base = pdata->base;
- pullups = pdata->chip[0].pullups;
}
mcp = kzalloc(sizeof(*mcp), GFP_KERNEL);
@@ -795,7 +797,7 @@ static int mcp230xx_probe(struct i2c_client *client,
mcp->irq = client->irq;
status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr,
- id->driver_data, base, pullups);
+ id->driver_data, pdata, 0);
if (status)
goto fail;
@@ -863,14 +865,12 @@ static void mcp23s08_i2c_exit(void) { }
static int mcp23s08_probe(struct spi_device *spi)
{
- struct mcp23s08_platform_data *pdata;
+ struct mcp23s08_platform_data *pdata, local_pdata;
unsigned addr;
int chips = 0;
struct mcp23s08_driver_data *data;
int status, type;
- unsigned base = -1,
- ngpio = 0,
- pullups[ARRAY_SIZE(pdata->chip)];
+ unsigned ngpio = 0;
const struct of_device_id *match;
u32 spi_present_mask = 0;
@@ -893,11 +893,18 @@ static int mcp23s08_probe(struct spi_device *spi)
return -ENODEV;
}
+ pdata = &local_pdata;
+ pdata->base = -1;
for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
- pullups[addr] = 0;
+ pdata->chip[addr].pullups = 0;
if (spi_present_mask & (1 << addr))
chips++;
}
+ pdata->irq_controller = of_property_read_bool(
+ spi->dev.of_node,
+ "interrupt-controller");
+ pdata->mirror = of_property_read_bool(spi->dev.of_node,
+ "microchip,irq-mirror");
} else {
type = spi_get_device_id(spi)->driver_data;
pdata = dev_get_platdata(&spi->dev);
@@ -917,10 +924,7 @@ static int mcp23s08_probe(struct spi_device *spi)
return -EINVAL;
}
spi_present_mask |= 1 << addr;
- pullups[addr] = pdata->chip[addr].pullups;
}
-
- base = pdata->base;
}
if (!chips)
@@ -938,13 +942,13 @@ static int mcp23s08_probe(struct spi_device *spi)
chips--;
data->mcp[addr] = &data->chip[chips];
status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi,
- 0x40 | (addr << 1), type, base,
- pullups[addr]);
+ 0x40 | (addr << 1), type, pdata,
+ addr);
if (status < 0)
goto fail;
- if (base != -1)
- base += (type == MCP_TYPE_S17) ? 16 : 8;
+ if (pdata->base != -1)
+ pdata->base += (type == MCP_TYPE_S17) ? 16 : 8;
ngpio += (type == MCP_TYPE_S17) ? 16 : 8;
}
data->ngpio = ngpio;
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index 174932165fcb..415682f69214 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -857,16 +857,6 @@ static void omap_gpio_unmask_irq(struct irq_data *d)
spin_unlock_irqrestore(&bank->lock, flags);
}
-static struct irq_chip gpio_irq_chip = {
- .name = "GPIO",
- .irq_shutdown = omap_gpio_irq_shutdown,
- .irq_ack = omap_gpio_ack_irq,
- .irq_mask = omap_gpio_mask_irq,
- .irq_unmask = omap_gpio_unmask_irq,
- .irq_set_type = omap_gpio_irq_type,
- .irq_set_wake = omap_gpio_wake_enable,
-};
-
/*---------------------------------------------------------------------*/
static int omap_mpuio_suspend_noirq(struct device *dev)
@@ -1088,7 +1078,7 @@ omap_mpuio_alloc_gc(struct gpio_bank *bank, unsigned int irq_start,
IRQ_NOREQUEST | IRQ_NOPROBE, 0);
}
-static int omap_gpio_chip_init(struct gpio_bank *bank)
+static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
{
int j;
static int gpio;
@@ -1137,17 +1127,17 @@ static int omap_gpio_chip_init(struct gpio_bank *bank)
}
#endif
- ret = gpiochip_irqchip_add(&bank->chip, &gpio_irq_chip,
+ ret = gpiochip_irqchip_add(&bank->chip, irqc,
irq_base, omap_gpio_irq_handler,
IRQ_TYPE_NONE);
if (ret) {
dev_err(bank->dev, "Couldn't add irqchip to gpiochip %d\n", ret);
- ret = gpiochip_remove(&bank->chip);
+ gpiochip_remove(&bank->chip);
return -ENODEV;
}
- gpiochip_set_chained_irqchip(&bank->chip, &gpio_irq_chip,
+ gpiochip_set_chained_irqchip(&bank->chip, irqc,
bank->irq, omap_gpio_irq_handler);
for (j = 0; j < bank->width; j++) {
@@ -1172,6 +1162,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
const struct omap_gpio_platform_data *pdata;
struct resource *res;
struct gpio_bank *bank;
+ struct irq_chip *irqc;
int ret;
match = of_match_device(of_match_ptr(omap_gpio_match), dev);
@@ -1186,6 +1177,18 @@ static int omap_gpio_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ irqc = devm_kzalloc(dev, sizeof(*irqc), GFP_KERNEL);
+ if (!irqc)
+ return -ENOMEM;
+
+ irqc->irq_shutdown = omap_gpio_irq_shutdown,
+ irqc->irq_ack = omap_gpio_ack_irq,
+ irqc->irq_mask = omap_gpio_mask_irq,
+ irqc->irq_unmask = omap_gpio_unmask_irq,
+ irqc->irq_set_type = omap_gpio_irq_type,
+ irqc->irq_set_wake = omap_gpio_wake_enable,
+ irqc->name = dev_name(&pdev->dev);
+
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (unlikely(!res)) {
dev_err(dev, "Invalid IRQ resource\n");
@@ -1241,7 +1244,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
omap_gpio_mod_init(bank);
- ret = omap_gpio_chip_init(bank);
+ ret = omap_gpio_chip_init(bank, irqc);
if (ret)
return ret;
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index f9961eea2120..e2da64abbccd 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -520,7 +520,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
struct i2c_client *client = chip->client;
int ret, i, offset = 0;
- if (irq_base != -1
+ if (client->irq && irq_base != -1
&& (id->driver_data & PCA_INT)) {
switch (chip->chip_type) {
@@ -586,50 +586,6 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
}
#endif
-/*
- * Handlers for alternative sources of platform_data
- */
-#ifdef CONFIG_OF_GPIO
-/*
- * Translate OpenFirmware node properties into platform_data
- * WARNING: This is DEPRECATED and will be removed eventually!
- */
-static void
-pca953x_get_alt_pdata(struct i2c_client *client, int *gpio_base, u32 *invert)
-{
- struct device_node *node;
- const __be32 *val;
- int size;
-
- *gpio_base = -1;
-
- node = client->dev.of_node;
- if (node == NULL)
- return;
-
- val = of_get_property(node, "linux,gpio-base", &size);
- WARN(val, "%s: device-tree property 'linux,gpio-base' is deprecated!", __func__);
- if (val) {
- if (size != sizeof(*val))
- dev_warn(&client->dev, "%s: wrong linux,gpio-base\n",
- node->full_name);
- else
- *gpio_base = be32_to_cpup(val);
- }
-
- val = of_get_property(node, "polarity", NULL);
- WARN(val, "%s: device-tree property 'polarity' is deprecated!", __func__);
- if (val)
- *invert = *val;
-}
-#else
-static void
-pca953x_get_alt_pdata(struct i2c_client *client, int *gpio_base, u32 *invert)
-{
- *gpio_base = -1;
-}
-#endif
-
static int device_pca953x_init(struct pca953x_chip *chip, u32 invert)
{
int ret;
@@ -704,12 +660,8 @@ static int pca953x_probe(struct i2c_client *client,
invert = pdata->invert;
chip->names = pdata->names;
} else {
- pca953x_get_alt_pdata(client, &chip->gpio_start, &invert);
-#ifdef CONFIG_OF_GPIO
- /* If I2C node has no interrupts property, disable GPIO interrupts */
- if (of_find_property(client->dev.of_node, "interrupts", NULL) == NULL)
- irq_base = -1;
-#endif
+ chip->gpio_start = -1;
+ irq_base = 0;
}
chip->client = client;
diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c
index e0ac549dccb5..2d9a950ca2d4 100644
--- a/drivers/gpio/gpio-pch.c
+++ b/drivers/gpio/gpio-pch.c
@@ -171,6 +171,7 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
return 0;
}
+#ifdef CONFIG_PM
/*
* Save register configuration and disable interrupts.
*/
@@ -206,6 +207,7 @@ static void pch_gpio_restore_reg_conf(struct pch_gpio *chip)
iowrite32(chip->pch_gpio_reg.gpio_use_sel_reg,
&chip->reg->gpio_use_sel);
}
+#endif
static int pch_gpio_to_irq(struct gpio_chip *gpio, unsigned offset)
{
diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c
index 3810da47043f..7c288ba4dc87 100644
--- a/drivers/gpio/gpio-samsung.c
+++ b/drivers/gpio/gpio-samsung.c
@@ -1309,56 +1309,6 @@ samsung_gpio_pull_t s3c_gpio_getpull(unsigned int pin)
}
EXPORT_SYMBOL(s3c_gpio_getpull);
-#ifdef CONFIG_S5P_GPIO_DRVSTR
-s5p_gpio_drvstr_t s5p_gpio_get_drvstr(unsigned int pin)
-{
- struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
- unsigned int off;
- void __iomem *reg;
- int shift;
- u32 drvstr;
-
- if (!chip)
- return -EINVAL;
-
- off = pin - chip->chip.base;
- shift = off * 2;
- reg = chip->base + 0x0C;
-
- drvstr = __raw_readl(reg);
- drvstr = drvstr >> shift;
- drvstr &= 0x3;
-
- return (__force s5p_gpio_drvstr_t)drvstr;
-}
-EXPORT_SYMBOL(s5p_gpio_get_drvstr);
-
-int s5p_gpio_set_drvstr(unsigned int pin, s5p_gpio_drvstr_t drvstr)
-{
- struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
- unsigned int off;
- void __iomem *reg;
- int shift;
- u32 tmp;
-
- if (!chip)
- return -EINVAL;
-
- off = pin - chip->chip.base;
- shift = off * 2;
- reg = chip->base + 0x0C;
-
- tmp = __raw_readl(reg);
- tmp &= ~(0x3 << shift);
- tmp |= drvstr << shift;
-
- __raw_writel(tmp, reg);
-
- return 0;
-}
-EXPORT_SYMBOL(s5p_gpio_set_drvstr);
-#endif /* CONFIG_S5P_GPIO_DRVSTR */
-
#ifdef CONFIG_PLAT_S3C24XX
unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change)
{
diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c
index 845025a57240..85c5b1974294 100644
--- a/drivers/gpio/gpio-stmpe.c
+++ b/drivers/gpio/gpio-stmpe.c
@@ -13,6 +13,7 @@
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/mfd/stmpe.h>
+#include <linux/seq_file.h>
/*
* These registers are modified under the irq bus lock and cached to avoid
@@ -127,19 +128,19 @@ static int stmpe_gpio_irq_set_type(struct irq_data *d, unsigned int type)
int regoffset = offset / 8;
int mask = 1 << (offset % 8);
- if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
+ if (type & IRQ_TYPE_LEVEL_LOW || type & IRQ_TYPE_LEVEL_HIGH)
return -EINVAL;
/* STMPE801 doesn't have RE and FE registers */
if (stmpe_gpio->stmpe->partnum == STMPE801)
return 0;
- if (type == IRQ_TYPE_EDGE_RISING)
+ if (type & IRQ_TYPE_EDGE_RISING)
stmpe_gpio->regs[REG_RE][regoffset] |= mask;
else
stmpe_gpio->regs[REG_RE][regoffset] &= ~mask;
- if (type == IRQ_TYPE_EDGE_FALLING)
+ if (type & IRQ_TYPE_EDGE_FALLING)
stmpe_gpio->regs[REG_FE][regoffset] |= mask;
else
stmpe_gpio->regs[REG_FE][regoffset] &= ~mask;
@@ -211,6 +212,77 @@ static void stmpe_gpio_irq_unmask(struct irq_data *d)
stmpe_gpio->regs[REG_IE][regoffset] |= mask;
}
+static void stmpe_dbg_show_one(struct seq_file *s,
+ struct gpio_chip *gc,
+ unsigned offset, unsigned gpio)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(gc);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ const char *label = gpiochip_is_requested(gc, offset);
+ int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8);
+ bool val = !!stmpe_gpio_get(gc, offset);
+ u8 dir_reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+ int ret;
+ u8 dir;
+
+ ret = stmpe_reg_read(stmpe, dir_reg);
+ if (ret < 0)
+ return;
+ dir = !!(ret & mask);
+
+ if (dir) {
+ seq_printf(s, " gpio-%-3d (%-20.20s) out %s",
+ gpio, label ?: "(none)",
+ val ? "hi" : "lo");
+ } else {
+ u8 edge_det_reg = stmpe->regs[STMPE_IDX_GPEDR_MSB] + num_banks - 1 - (offset / 8);
+ u8 rise_reg = stmpe->regs[STMPE_IDX_GPRER_LSB] - (offset / 8);
+ u8 fall_reg = stmpe->regs[STMPE_IDX_GPFER_LSB] - (offset / 8);
+ u8 irqen_reg = stmpe->regs[STMPE_IDX_IEGPIOR_LSB] - (offset / 8);
+ bool edge_det;
+ bool rise;
+ bool fall;
+ bool irqen;
+
+ ret = stmpe_reg_read(stmpe, edge_det_reg);
+ if (ret < 0)
+ return;
+ edge_det = !!(ret & mask);
+ ret = stmpe_reg_read(stmpe, rise_reg);
+ if (ret < 0)
+ return;
+ rise = !!(ret & mask);
+ ret = stmpe_reg_read(stmpe, fall_reg);
+ if (ret < 0)
+ return;
+ fall = !!(ret & mask);
+ ret = stmpe_reg_read(stmpe, irqen_reg);
+ if (ret < 0)
+ return;
+ irqen = !!(ret & mask);
+
+ seq_printf(s, " gpio-%-3d (%-20.20s) in %s %s %s%s%s",
+ gpio, label ?: "(none)",
+ val ? "hi" : "lo",
+ edge_det ? "edge-asserted" : "edge-inactive",
+ irqen ? "IRQ-enabled" : "",
+ rise ? " rising-edge-detection" : "",
+ fall ? " falling-edge-detection" : "");
+ }
+}
+
+static void stmpe_dbg_show(struct seq_file *s, struct gpio_chip *gc)
+{
+ unsigned i;
+ unsigned gpio = gc->base;
+
+ for (i = 0; i < gc->ngpio; i++, gpio++) {
+ stmpe_dbg_show_one(s, gc, i, gpio);
+ seq_printf(s, "\n");
+ }
+}
+
static struct irq_chip stmpe_gpio_irq_chip = {
.name = "stmpe-gpio",
.irq_bus_lock = stmpe_gpio_irq_lock,
@@ -293,6 +365,9 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
#endif
stmpe_gpio->chip.base = -1;
+ if (IS_ENABLED(CONFIG_DEBUG_FS))
+ stmpe_gpio->chip.dbg_show = stmpe_dbg_show;
+
if (pdata)
stmpe_gpio->norequest_mask = pdata->norequest_mask;
else if (np)
@@ -308,6 +383,12 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
if (ret)
goto out_free;
+ ret = gpiochip_add(&stmpe_gpio->chip);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
+ goto out_disable;
+ }
+
if (irq > 0) {
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
stmpe_gpio_irq, IRQF_ONESHOT,
@@ -324,14 +405,13 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev,
"could not connect irqchip to gpiochip\n");
- return ret;
+ goto out_disable;
}
- }
- ret = gpiochip_add(&stmpe_gpio->chip);
- if (ret) {
- dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
- goto out_disable;
+ gpiochip_set_chained_irqchip(&stmpe_gpio->chip,
+ &stmpe_gpio_irq_chip,
+ irq,
+ NULL);
}
if (pdata && pdata->setup)
@@ -343,6 +423,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
out_disable:
stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
+ gpiochip_remove(&stmpe_gpio->chip);
out_free:
kfree(stmpe_gpio);
return ret;
diff --git a/drivers/gpio/gpio-stp-xway.c b/drivers/gpio/gpio-stp-xway.c
index 04882a911b65..7e359b7cce1b 100644
--- a/drivers/gpio/gpio-stp-xway.c
+++ b/drivers/gpio/gpio-stp-xway.c
@@ -292,7 +292,7 @@ static struct platform_driver xway_stp_driver = {
},
};
-int __init xway_stp_init(void)
+static int __init xway_stp_init(void)
{
return platform_driver_register(&xway_stp_driver);
}
diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c
index 30884fbc750d..e82fde4b6898 100644
--- a/drivers/gpio/gpio-syscon.c
+++ b/drivers/gpio/gpio-syscon.c
@@ -37,6 +37,8 @@
* dat_bit_offset: Offset (in bits) to the first GPIO bit.
* dir_bit_offset: Optional offset (in bits) to the first bit to switch
* GPIO direction (Used with GPIO_SYSCON_FEAT_DIR flag).
+ * set: HW specific callback to assigns output value
+ * for signal "offset"
*/
struct syscon_gpio_data {
@@ -45,12 +47,16 @@ struct syscon_gpio_data {
unsigned int bit_count;
unsigned int dat_bit_offset;
unsigned int dir_bit_offset;
+ void (*set)(struct gpio_chip *chip,
+ unsigned offset, int value);
};
struct syscon_gpio_priv {
struct gpio_chip chip;
struct regmap *syscon;
const struct syscon_gpio_data *data;
+ u32 dreg_offset;
+ u32 dir_reg_offset;
};
static inline struct syscon_gpio_priv *to_syscon_gpio(struct gpio_chip *chip)
@@ -61,9 +67,11 @@ static inline struct syscon_gpio_priv *to_syscon_gpio(struct gpio_chip *chip)
static int syscon_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
- unsigned int val, offs = priv->data->dat_bit_offset + offset;
+ unsigned int val, offs;
int ret;
+ offs = priv->dreg_offset + priv->data->dat_bit_offset + offset;
+
ret = regmap_read(priv->syscon,
(offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, &val);
if (ret)
@@ -75,7 +83,9 @@ static int syscon_gpio_get(struct gpio_chip *chip, unsigned offset)
static void syscon_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
{
struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
- unsigned int offs = priv->data->dat_bit_offset + offset;
+ unsigned int offs;
+
+ offs = priv->dreg_offset + priv->data->dat_bit_offset + offset;
regmap_update_bits(priv->syscon,
(offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
@@ -88,7 +98,10 @@ static int syscon_gpio_dir_in(struct gpio_chip *chip, unsigned offset)
struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
if (priv->data->flags & GPIO_SYSCON_FEAT_DIR) {
- unsigned int offs = priv->data->dir_bit_offset + offset;
+ unsigned int offs;
+
+ offs = priv->dir_reg_offset +
+ priv->data->dir_bit_offset + offset;
regmap_update_bits(priv->syscon,
(offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
@@ -103,7 +116,10 @@ static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val)
struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
if (priv->data->flags & GPIO_SYSCON_FEAT_DIR) {
- unsigned int offs = priv->data->dir_bit_offset + offset;
+ unsigned int offs;
+
+ offs = priv->dir_reg_offset +
+ priv->data->dir_bit_offset + offset;
regmap_update_bits(priv->syscon,
(offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
@@ -111,7 +127,7 @@ static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val)
BIT(offs % SYSCON_REG_BITS));
}
- syscon_gpio_set(chip, offset, val);
+ priv->data->set(chip, offset, val);
return 0;
}
@@ -124,11 +140,46 @@ static const struct syscon_gpio_data clps711x_mctrl_gpio = {
.dat_bit_offset = 0x40 * 8 + 8,
};
+#define KEYSTONE_LOCK_BIT BIT(0)
+
+static void keystone_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
+ unsigned int offs;
+ int ret;
+
+ offs = priv->dreg_offset + priv->data->dat_bit_offset + offset;
+
+ if (!val)
+ return;
+
+ ret = regmap_update_bits(
+ priv->syscon,
+ (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
+ BIT(offs % SYSCON_REG_BITS) | KEYSTONE_LOCK_BIT,
+ BIT(offs % SYSCON_REG_BITS) | KEYSTONE_LOCK_BIT);
+ if (ret < 0)
+ dev_err(chip->dev, "gpio write failed ret(%d)\n", ret);
+}
+
+static const struct syscon_gpio_data keystone_dsp_gpio = {
+ /* ARM Keystone 2 */
+ .compatible = NULL,
+ .flags = GPIO_SYSCON_FEAT_OUT,
+ .bit_count = 28,
+ .dat_bit_offset = 4,
+ .set = keystone_gpio_set,
+};
+
static const struct of_device_id syscon_gpio_ids[] = {
{
.compatible = "cirrus,clps711x-mctrl-gpio",
.data = &clps711x_mctrl_gpio,
},
+ {
+ .compatible = "ti,keystone-dsp-gpio",
+ .data = &keystone_dsp_gpio,
+ },
{ }
};
MODULE_DEVICE_TABLE(of, syscon_gpio_ids);
@@ -138,6 +189,8 @@ static int syscon_gpio_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
const struct of_device_id *of_id = of_match_device(syscon_gpio_ids, dev);
struct syscon_gpio_priv *priv;
+ struct device_node *np = dev->of_node;
+ int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -145,10 +198,31 @@ static int syscon_gpio_probe(struct platform_device *pdev)
priv->data = of_id->data;
- priv->syscon =
- syscon_regmap_lookup_by_compatible(priv->data->compatible);
- if (IS_ERR(priv->syscon))
- return PTR_ERR(priv->syscon);
+ if (priv->data->compatible) {
+ priv->syscon = syscon_regmap_lookup_by_compatible(
+ priv->data->compatible);
+ if (IS_ERR(priv->syscon))
+ return PTR_ERR(priv->syscon);
+ } else {
+ priv->syscon =
+ syscon_regmap_lookup_by_phandle(np, "gpio,syscon-dev");
+ if (IS_ERR(priv->syscon))
+ return PTR_ERR(priv->syscon);
+
+ ret = of_property_read_u32_index(np, "gpio,syscon-dev", 1,
+ &priv->dreg_offset);
+ if (ret)
+ dev_err(dev, "can't read the data register offset!\n");
+
+ priv->dreg_offset <<= 3;
+
+ ret = of_property_read_u32_index(np, "gpio,syscon-dev", 2,
+ &priv->dir_reg_offset);
+ if (ret)
+ dev_err(dev, "can't read the dir register offset!\n");
+
+ priv->dir_reg_offset <<= 3;
+ }
priv->chip.dev = dev;
priv->chip.owner = THIS_MODULE;
@@ -159,7 +233,7 @@ static int syscon_gpio_probe(struct platform_device *pdev)
if (priv->data->flags & GPIO_SYSCON_FEAT_IN)
priv->chip.direction_input = syscon_gpio_dir_in;
if (priv->data->flags & GPIO_SYSCON_FEAT_OUT) {
- priv->chip.set = syscon_gpio_set;
+ priv->chip.set = priv->data->set ? : syscon_gpio_set;
priv->chip.direction_output = syscon_gpio_dir_out;
}
diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c
index 7324869c38e0..ae0f6466eb09 100644
--- a/drivers/gpio/gpio-tc3589x.c
+++ b/drivers/gpio/gpio-tc3589x.c
@@ -300,6 +300,11 @@ static int tc3589x_gpio_probe(struct platform_device *pdev)
return ret;
}
+ gpiochip_set_chained_irqchip(&tc3589x_gpio->chip,
+ &tc3589x_gpio_irq_chip,
+ irq,
+ NULL);
+
if (pdata && pdata->setup)
pdata->setup(tc3589x, tc3589x_gpio->chip.base);
diff --git a/drivers/gpio/gpio-xgene.c b/drivers/gpio/gpio-xgene.c
new file mode 100644
index 000000000000..7d489221dc1f
--- /dev/null
+++ b/drivers/gpio/gpio-xgene.c
@@ -0,0 +1,244 @@
+/*
+ * AppliedMicro X-Gene SoC GPIO Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Feng Kan <fkan@apm.com>.
+ *
+ * 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/driver.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+
+#define GPIO_SET_DR_OFFSET 0x0C
+#define GPIO_DATA_OFFSET 0x14
+#define GPIO_BANK_STRIDE 0x0C
+
+#define XGENE_GPIOS_PER_BANK 16
+#define XGENE_MAX_GPIO_BANKS 3
+#define XGENE_MAX_GPIOS (XGENE_GPIOS_PER_BANK * XGENE_MAX_GPIO_BANKS)
+
+#define GPIO_BIT_OFFSET(x) (x % XGENE_GPIOS_PER_BANK)
+#define GPIO_BANK_OFFSET(x) ((x / XGENE_GPIOS_PER_BANK) * GPIO_BANK_STRIDE)
+
+struct xgene_gpio {
+ struct gpio_chip chip;
+ void __iomem *base;
+ spinlock_t lock;
+#ifdef CONFIG_PM
+ u32 set_dr_val[XGENE_MAX_GPIO_BANKS];
+#endif
+};
+
+static inline struct xgene_gpio *to_xgene_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct xgene_gpio, chip);
+}
+
+static int xgene_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct xgene_gpio *chip = to_xgene_gpio(gc);
+ unsigned long bank_offset;
+ u32 bit_offset;
+
+ bank_offset = GPIO_DATA_OFFSET + GPIO_BANK_OFFSET(offset);
+ bit_offset = GPIO_BIT_OFFSET(offset);
+ return !!(ioread32(chip->base + bank_offset) & BIT(bit_offset));
+}
+
+static void __xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val)
+{
+ struct xgene_gpio *chip = to_xgene_gpio(gc);
+ unsigned long bank_offset;
+ u32 setval, bit_offset;
+
+ bank_offset = GPIO_SET_DR_OFFSET + GPIO_BANK_OFFSET(offset);
+ bit_offset = GPIO_BIT_OFFSET(offset) + XGENE_GPIOS_PER_BANK;
+
+ setval = ioread32(chip->base + bank_offset);
+ if (val)
+ setval |= BIT(bit_offset);
+ else
+ setval &= ~BIT(bit_offset);
+ iowrite32(setval, chip->base + bank_offset);
+}
+
+static void xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val)
+{
+ struct xgene_gpio *chip = to_xgene_gpio(gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ __xgene_gpio_set(gc, offset, val);
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int xgene_gpio_dir_in(struct gpio_chip *gc, unsigned int offset)
+{
+ struct xgene_gpio *chip = to_xgene_gpio(gc);
+ unsigned long flags, bank_offset;
+ u32 dirval, bit_offset;
+
+ bank_offset = GPIO_SET_DR_OFFSET + GPIO_BANK_OFFSET(offset);
+ bit_offset = GPIO_BIT_OFFSET(offset);
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ dirval = ioread32(chip->base + bank_offset);
+ dirval |= BIT(bit_offset);
+ iowrite32(dirval, chip->base + bank_offset);
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+static int xgene_gpio_dir_out(struct gpio_chip *gc,
+ unsigned int offset, int val)
+{
+ struct xgene_gpio *chip = to_xgene_gpio(gc);
+ unsigned long flags, bank_offset;
+ u32 dirval, bit_offset;
+
+ bank_offset = GPIO_SET_DR_OFFSET + GPIO_BANK_OFFSET(offset);
+ bit_offset = GPIO_BIT_OFFSET(offset);
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ dirval = ioread32(chip->base + bank_offset);
+ dirval &= ~BIT(bit_offset);
+ iowrite32(dirval, chip->base + bank_offset);
+ __xgene_gpio_set(gc, offset, val);
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int xgene_gpio_suspend(struct device *dev)
+{
+ struct xgene_gpio *gpio = dev_get_drvdata(dev);
+ unsigned long bank_offset;
+ unsigned int bank;
+
+ for (bank = 0; bank < XGENE_MAX_GPIO_BANKS; bank++) {
+ bank_offset = GPIO_SET_DR_OFFSET + bank * GPIO_BANK_STRIDE;
+ gpio->set_dr_val[bank] = ioread32(gpio->base + bank_offset);
+ }
+ return 0;
+}
+
+static int xgene_gpio_resume(struct device *dev)
+{
+ struct xgene_gpio *gpio = dev_get_drvdata(dev);
+ unsigned long bank_offset;
+ unsigned int bank;
+
+ for (bank = 0; bank < XGENE_MAX_GPIO_BANKS; bank++) {
+ bank_offset = GPIO_SET_DR_OFFSET + bank * GPIO_BANK_STRIDE;
+ iowrite32(gpio->set_dr_val[bank], gpio->base + bank_offset);
+ }
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(xgene_gpio_pm, xgene_gpio_suspend, xgene_gpio_resume);
+#define XGENE_GPIO_PM_OPS (&xgene_gpio_pm)
+#else
+#define XGENE_GPIO_PM_OPS NULL
+#endif
+
+static int xgene_gpio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct xgene_gpio *gpio;
+ int err = 0;
+
+ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ gpio->base = devm_ioremap_nocache(&pdev->dev, res->start,
+ resource_size(res));
+ if (!gpio->base) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ gpio->chip.ngpio = XGENE_MAX_GPIOS;
+
+ spin_lock_init(&gpio->lock);
+ gpio->chip.dev = &pdev->dev;
+ gpio->chip.direction_input = xgene_gpio_dir_in;
+ gpio->chip.direction_output = xgene_gpio_dir_out;
+ gpio->chip.get = xgene_gpio_get;
+ gpio->chip.set = xgene_gpio_set;
+ gpio->chip.label = dev_name(&pdev->dev);
+ gpio->chip.base = -1;
+
+ platform_set_drvdata(pdev, gpio);
+
+ err = gpiochip_add(&gpio->chip);
+ if (err) {
+ dev_err(&pdev->dev,
+ "failed to register gpiochip.\n");
+ goto err;
+ }
+
+ dev_info(&pdev->dev, "X-Gene GPIO driver registered.\n");
+ return 0;
+err:
+ dev_err(&pdev->dev, "X-Gene GPIO driver registration failed.\n");
+ return err;
+}
+
+static int xgene_gpio_remove(struct platform_device *pdev)
+{
+ struct xgene_gpio *gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&gpio->chip);
+ return 0;
+}
+
+static const struct of_device_id xgene_gpio_of_match[] = {
+ { .compatible = "apm,xgene-gpio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, xgene_gpio_of_match);
+
+static struct platform_driver xgene_gpio_driver = {
+ .driver = {
+ .name = "xgene-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = xgene_gpio_of_match,
+ .pm = XGENE_GPIO_PM_OPS,
+ },
+ .probe = xgene_gpio_probe,
+ .remove = xgene_gpio_remove,
+};
+
+module_platform_driver(xgene_gpio_driver);
+
+MODULE_AUTHOR("Feng Kan <fkan@apm.com>");
+MODULE_DESCRIPTION("APM X-Gene GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c
index 12481867daf1..ba18b06c9a21 100644
--- a/drivers/gpio/gpio-xilinx.c
+++ b/drivers/gpio/gpio-xilinx.c
@@ -197,6 +197,7 @@ static int xgpio_of_probe(struct device_node *np)
struct xgpio_instance *chip;
int status = 0;
const u32 *tree_info;
+ u32 ngpio;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (!chip)
@@ -211,12 +212,13 @@ static int xgpio_of_probe(struct device_node *np)
/* Update GPIO direction shadow register with default value */
of_property_read_u32(np, "xlnx,tri-default", &chip->gpio_dir);
- /* By default assume full GPIO controller */
- chip->mmchip.gc.ngpio = 32;
-
- /* Check device node and parent device node for device width */
- of_property_read_u32(np, "xlnx,gpio-width",
- (u32 *)&chip->mmchip.gc.ngpio);
+ /*
+ * Check device node and parent device node for device width
+ * and assume default width of 32
+ */
+ if (of_property_read_u32(np, "xlnx,gpio-width", &ngpio))
+ ngpio = 32;
+ chip->mmchip.gc.ngpio = (u16)ngpio;
spin_lock_init(&chip->gpio_lock);
@@ -258,12 +260,13 @@ static int xgpio_of_probe(struct device_node *np)
/* Update GPIO direction shadow register with default value */
of_property_read_u32(np, "xlnx,tri-default-2", &chip->gpio_dir);
- /* By default assume full GPIO controller */
- chip->mmchip.gc.ngpio = 32;
-
- /* Check device node and parent device node for device width */
- of_property_read_u32(np, "xlnx,gpio2-width",
- (u32 *)&chip->mmchip.gc.ngpio);
+ /*
+ * Check device node and parent device node for device width
+ * and assume default width of 32
+ */
+ if (of_property_read_u32(np, "xlnx,gpio2-width", &ngpio))
+ ngpio = 32;
+ chip->mmchip.gc.ngpio = (u16)ngpio;
spin_lock_init(&chip->gpio_lock);
diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c
index 31ad5df5dbc9..74cd480bf8de 100644
--- a/drivers/gpio/gpio-zynq.c
+++ b/drivers/gpio/gpio-zynq.c
@@ -88,16 +88,17 @@
* @chip: instance of the gpio_chip
* @base_addr: base address of the GPIO device
* @clk: clock resource for this controller
+ * @irq: interrupt for the GPIO device
*/
struct zynq_gpio {
struct gpio_chip chip;
void __iomem *base_addr;
struct clk *clk;
+ int irq;
};
static struct irq_chip zynq_gpio_level_irqchip;
static struct irq_chip zynq_gpio_edge_irqchip;
-
/**
* zynq_gpio_get_bank_pin - Get the bank number and pin number within that bank
* for a given pin in the GPIO device
@@ -138,6 +139,13 @@ static inline void zynq_gpio_get_bank_pin(unsigned int pin_num,
}
}
+static const unsigned int zynq_gpio_bank_offset[] = {
+ ZYNQ_GPIO_BANK0_PIN_MIN,
+ ZYNQ_GPIO_BANK1_PIN_MIN,
+ ZYNQ_GPIO_BANK2_PIN_MIN,
+ ZYNQ_GPIO_BANK3_PIN_MIN,
+};
+
/**
* zynq_gpio_get_value - Get the state of the specified pin of GPIO device
* @chip: gpio_chip instance to be worked on
@@ -427,10 +435,9 @@ static int zynq_gpio_set_irq_type(struct irq_data *irq_data, unsigned int type)
static int zynq_gpio_set_wake(struct irq_data *data, unsigned int on)
{
- if (on)
- zynq_gpio_irq_unmask(data);
- else
- zynq_gpio_irq_mask(data);
+ struct zynq_gpio *gpio = irq_data_get_irq_chip_data(data);
+
+ irq_set_irq_wake(gpio->irq, on);
return 0;
}
@@ -444,7 +451,8 @@ static struct irq_chip zynq_gpio_level_irqchip = {
.irq_unmask = zynq_gpio_irq_unmask,
.irq_set_type = zynq_gpio_set_irq_type,
.irq_set_wake = zynq_gpio_set_wake,
- .flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED,
+ .flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED |
+ IRQCHIP_MASK_ON_SUSPEND,
};
static struct irq_chip zynq_gpio_edge_irqchip = {
@@ -455,8 +463,28 @@ static struct irq_chip zynq_gpio_edge_irqchip = {
.irq_unmask = zynq_gpio_irq_unmask,
.irq_set_type = zynq_gpio_set_irq_type,
.irq_set_wake = zynq_gpio_set_wake,
+ .flags = IRQCHIP_MASK_ON_SUSPEND,
};
+static void zynq_gpio_handle_bank_irq(struct zynq_gpio *gpio,
+ unsigned int bank_num,
+ unsigned long pending)
+{
+ unsigned int bank_offset = zynq_gpio_bank_offset[bank_num];
+ struct irq_domain *irqdomain = gpio->chip.irqdomain;
+ int offset;
+
+ if (!pending)
+ return;
+
+ for_each_set_bit(offset, &pending, 32) {
+ unsigned int gpio_irq;
+
+ gpio_irq = irq_find_mapping(irqdomain, offset + bank_offset);
+ generic_handle_irq(gpio_irq);
+ }
+}
+
/**
* zynq_gpio_irqhandler - IRQ handler for the gpio banks of a gpio device
* @irq: irq number of the gpio bank where interrupt has occurred
@@ -482,18 +510,7 @@ static void zynq_gpio_irqhandler(unsigned int irq, struct irq_desc *desc)
ZYNQ_GPIO_INTSTS_OFFSET(bank_num));
int_enb = readl_relaxed(gpio->base_addr +
ZYNQ_GPIO_INTMASK_OFFSET(bank_num));
- int_sts &= ~int_enb;
- if (int_sts) {
- int offset;
- unsigned long pending = int_sts;
-
- for_each_set_bit(offset, &pending, 32) {
- unsigned int gpio_irq =
- irq_find_mapping(gpio->chip.irqdomain,
- offset);
- generic_handle_irq(gpio_irq);
- }
- }
+ zynq_gpio_handle_bank_irq(gpio, bank_num, int_sts & ~int_enb);
}
chained_irq_exit(irqchip, desc);
@@ -501,7 +518,11 @@ static void zynq_gpio_irqhandler(unsigned int irq, struct irq_desc *desc)
static int __maybe_unused zynq_gpio_suspend(struct device *dev)
{
- if (!device_may_wakeup(dev))
+ struct platform_device *pdev = to_platform_device(dev);
+ int irq = platform_get_irq(pdev, 0);
+ struct irq_data *data = irq_get_irq_data(irq);
+
+ if (!irqd_is_wakeup_set(data))
return pm_runtime_force_suspend(dev);
return 0;
@@ -509,7 +530,11 @@ static int __maybe_unused zynq_gpio_suspend(struct device *dev)
static int __maybe_unused zynq_gpio_resume(struct device *dev)
{
- if (!device_may_wakeup(dev))
+ struct platform_device *pdev = to_platform_device(dev);
+ int irq = platform_get_irq(pdev, 0);
+ struct irq_data *data = irq_get_irq_data(irq);
+
+ if (!irqd_is_wakeup_set(data))
return pm_runtime_force_resume(dev);
return 0;
@@ -570,7 +595,7 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = {
*/
static int zynq_gpio_probe(struct platform_device *pdev)
{
- int ret, bank_num, irq;
+ int ret, bank_num;
struct zynq_gpio *gpio;
struct gpio_chip *chip;
struct resource *res;
@@ -586,10 +611,10 @@ static int zynq_gpio_probe(struct platform_device *pdev)
if (IS_ERR(gpio->base_addr))
return PTR_ERR(gpio->base_addr);
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
+ gpio->irq = platform_get_irq(pdev, 0);
+ if (gpio->irq < 0) {
dev_err(&pdev->dev, "invalid IRQ\n");
- return irq;
+ return gpio->irq;
}
/* configure the gpio chip */
@@ -637,19 +662,16 @@ static int zynq_gpio_probe(struct platform_device *pdev)
goto err_rm_gpiochip;
}
- gpiochip_set_chained_irqchip(chip, &zynq_gpio_edge_irqchip, irq,
+ gpiochip_set_chained_irqchip(chip, &zynq_gpio_edge_irqchip, gpio->irq,
zynq_gpio_irqhandler);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
- device_set_wakeup_capable(&pdev->dev, 1);
-
return 0;
err_rm_gpiochip:
- if (gpiochip_remove(chip))
- dev_err(&pdev->dev, "Failed to remove gpio chip\n");
+ gpiochip_remove(chip);
err_disable_clk:
clk_disable_unprepare(gpio->clk);
@@ -664,16 +686,10 @@ err_disable_clk:
*/
static int zynq_gpio_remove(struct platform_device *pdev)
{
- int ret;
struct zynq_gpio *gpio = platform_get_drvdata(pdev);
pm_runtime_get_sync(&pdev->dev);
-
- ret = gpiochip_remove(&gpio->chip);
- if (ret) {
- dev_err(&pdev->dev, "Failed to remove gpio chip\n");
- return ret;
- }
+ gpiochip_remove(&gpio->chip);
clk_disable_unprepare(gpio->clk);
device_set_wakeup_capable(&pdev->dev, 0);
return 0;
@@ -688,7 +704,6 @@ MODULE_DEVICE_TABLE(of, zynq_gpio_of_match);
static struct platform_driver zynq_gpio_driver = {
.driver = {
.name = DRIVER_NAME,
- .owner = THIS_MODULE,
.pm = &zynq_gpio_dev_pm_ops,
.of_match_table = zynq_gpio_of_match,
},
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index d62eaaa75397..05c6275da224 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -25,10 +25,12 @@ struct acpi_gpio_event {
acpi_handle handle;
unsigned int pin;
unsigned int irq;
+ struct gpio_desc *desc;
};
struct acpi_gpio_connection {
struct list_head node;
+ unsigned int pin;
struct gpio_desc *desc;
};
@@ -143,14 +145,8 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares,
if (!handler)
return AE_BAD_PARAMETER;
- desc = gpiochip_get_desc(chip, pin);
+ desc = gpiochip_request_own_desc(chip, pin, "ACPI:Event");
if (IS_ERR(desc)) {
- dev_err(chip->dev, "Failed to get GPIO descriptor\n");
- return AE_ERROR;
- }
-
- ret = gpiochip_request_own_desc(desc, "ACPI:Event");
- if (ret) {
dev_err(chip->dev, "Failed to request GPIO\n");
return AE_ERROR;
}
@@ -197,6 +193,7 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares,
event->handle = evt_handle;
event->irq = irq;
event->pin = pin;
+ event->desc = desc;
ret = request_threaded_irq(event->irq, NULL, handler, irqflags,
"ACPI:Event", event);
@@ -280,7 +277,7 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip)
struct gpio_desc *desc;
free_irq(event->irq, event);
- desc = gpiochip_get_desc(chip, event->pin);
+ desc = event->desc;
if (WARN_ON(IS_ERR(desc)))
continue;
gpio_unlock_as_irq(chip, event->pin);
@@ -377,8 +374,10 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
struct gpio_chip *chip = achip->chip;
struct acpi_resource_gpio *agpio;
struct acpi_resource *ares;
+ int pin_index = (int)address;
acpi_status status;
bool pull_up;
+ int length;
int i;
status = acpi_buffer_to_resource(achip->conn_info.connection,
@@ -400,32 +399,27 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
return AE_BAD_PARAMETER;
}
- for (i = 0; i < agpio->pin_table_length; i++) {
+ length = min(agpio->pin_table_length, (u16)(pin_index + bits));
+ for (i = pin_index; i < length; ++i) {
unsigned pin = agpio->pin_table[i];
struct acpi_gpio_connection *conn;
struct gpio_desc *desc;
bool found;
- desc = gpiochip_get_desc(chip, pin);
- if (IS_ERR(desc)) {
- status = AE_ERROR;
- goto out;
- }
-
mutex_lock(&achip->conn_lock);
found = false;
list_for_each_entry(conn, &achip->conns, node) {
- if (conn->desc == desc) {
+ if (conn->pin == pin) {
found = true;
+ desc = conn->desc;
break;
}
}
if (!found) {
- int ret;
-
- ret = gpiochip_request_own_desc(desc, "ACPI:OpRegion");
- if (ret) {
+ desc = gpiochip_request_own_desc(chip, pin,
+ "ACPI:OpRegion");
+ if (IS_ERR(desc)) {
status = AE_ERROR;
mutex_unlock(&achip->conn_lock);
goto out;
@@ -462,6 +456,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
goto out;
}
+ conn->pin = pin;
conn->desc = desc;
list_add_tail(&conn->node, &achip->conns);
}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 15cc0bb65dda..e8e98ca25ec7 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -308,10 +308,9 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);
*
* A gpio_chip with any GPIOs still requested may not be removed.
*/
-int gpiochip_remove(struct gpio_chip *chip)
+void gpiochip_remove(struct gpio_chip *chip)
{
unsigned long flags;
- int status = 0;
unsigned id;
acpi_gpiochip_remove(chip);
@@ -323,24 +322,15 @@ int gpiochip_remove(struct gpio_chip *chip)
of_gpiochip_remove(chip);
for (id = 0; id < chip->ngpio; id++) {
- if (test_bit(FLAG_REQUESTED, &chip->desc[id].flags)) {
- status = -EBUSY;
- break;
- }
- }
- if (status == 0) {
- for (id = 0; id < chip->ngpio; id++)
- chip->desc[id].chip = NULL;
-
- list_del(&chip->list);
+ if (test_bit(FLAG_REQUESTED, &chip->desc[id].flags))
+ dev_crit(chip->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
}
+ for (id = 0; id < chip->ngpio; id++)
+ chip->desc[id].chip = NULL;
+ list_del(&chip->list);
spin_unlock_irqrestore(&gpio_lock, flags);
-
- if (status == 0)
- gpiochip_unexport(chip);
-
- return status;
+ gpiochip_unexport(chip);
}
EXPORT_SYMBOL_GPL(gpiochip_remove);
@@ -395,30 +385,47 @@ static struct gpio_chip *find_chip_by_name(const char *name)
*/
/**
- * gpiochip_add_chained_irqchip() - adds a chained irqchip to a gpiochip
- * @gpiochip: the gpiochip to add the irqchip to
- * @irqchip: the irqchip to add to the gpiochip
+ * gpiochip_set_chained_irqchip() - sets a chained irqchip to a gpiochip
+ * @gpiochip: the gpiochip to set the irqchip chain to
+ * @irqchip: the irqchip to chain to the gpiochip
* @parent_irq: the irq number corresponding to the parent IRQ for this
* chained irqchip
* @parent_handler: the parent interrupt handler for the accumulated IRQ
- * coming out of the gpiochip
+ * coming out of the gpiochip. If the interrupt is nested rather than
+ * cascaded, pass NULL in this handler argument
*/
void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
struct irq_chip *irqchip,
int parent_irq,
irq_flow_handler_t parent_handler)
{
- if (gpiochip->can_sleep) {
- chip_err(gpiochip, "you cannot have chained interrupts on a chip that may sleep\n");
+ unsigned int offset;
+
+ if (!gpiochip->irqdomain) {
+ chip_err(gpiochip, "called %s before setting up irqchip\n",
+ __func__);
return;
}
- irq_set_chained_handler(parent_irq, parent_handler);
- /*
- * The parent irqchip is already using the chip_data for this
- * irqchip, so our callbacks simply use the handler_data.
- */
- irq_set_handler_data(parent_irq, gpiochip);
+ if (parent_handler) {
+ if (gpiochip->can_sleep) {
+ chip_err(gpiochip,
+ "you cannot have chained interrupts on a "
+ "chip that may sleep\n");
+ return;
+ }
+ /*
+ * The parent irqchip is already using the chip_data for this
+ * irqchip, so our callbacks simply use the handler_data.
+ */
+ irq_set_handler_data(parent_irq, gpiochip);
+ irq_set_chained_handler(parent_irq, parent_handler);
+ }
+
+ /* Set the parent IRQ for all affected IRQs */
+ for (offset = 0; offset < gpiochip->ngpio; offset++)
+ irq_set_parent(irq_find_mapping(gpiochip->irqdomain, offset),
+ parent_irq);
}
EXPORT_SYMBOL_GPL(gpiochip_set_chained_irqchip);
@@ -447,7 +454,7 @@ static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,
irq_set_lockdep_class(irq, &gpiochip_irq_lock_class);
irq_set_chip_and_handler(irq, chip->irqchip, chip->irq_handler);
/* Chips that can sleep need nested thread handlers */
- if (chip->can_sleep)
+ if (chip->can_sleep && !chip->irq_not_threaded)
irq_set_nested_thread(irq, 1);
#ifdef CONFIG_ARM
set_irq_flags(irq, IRQF_VALID);
@@ -524,7 +531,8 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
/* Remove all IRQ mappings and delete the domain */
if (gpiochip->irqdomain) {
for (offset = 0; offset < gpiochip->ngpio; offset++)
- irq_dispose_mapping(gpiochip->irq_base + offset);
+ irq_dispose_mapping(
+ irq_find_mapping(gpiochip->irqdomain, offset));
irq_domain_remove(gpiochip->irqdomain);
}
@@ -895,12 +903,22 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested);
* allows the GPIO chip module to be unloaded as needed (we assume that the
* GPIO chip driver handles freeing the GPIOs it has requested).
*/
-int gpiochip_request_own_desc(struct gpio_desc *desc, const char *label)
+struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum,
+ const char *label)
{
- if (!desc || !desc->chip)
- return -EINVAL;
+ struct gpio_desc *desc = gpiochip_get_desc(chip, hwnum);
+ int err;
- return __gpiod_request(desc, label);
+ if (IS_ERR(desc)) {
+ chip_err(chip, "failed to get GPIO descriptor\n");
+ return desc;
+ }
+
+ err = __gpiod_request(desc, label);
+ if (err < 0)
+ return ERR_PTR(err);
+
+ return desc;
}
EXPORT_SYMBOL_GPL(gpiochip_request_own_desc);
@@ -1652,7 +1670,7 @@ struct gpio_desc *__must_check __gpiod_get_index(struct device *dev,
* a result. In that case, use platform lookup as a fallback.
*/
if (!desc || desc == ERR_PTR(-ENOENT)) {
- dev_dbg(dev, "using lookup tables for GPIO lookup");
+ dev_dbg(dev, "using lookup tables for GPIO lookup\n");
desc = gpiod_find(dev, con_id, idx, &lookupflags);
}
@@ -1674,7 +1692,7 @@ struct gpio_desc *__must_check __gpiod_get_index(struct device *dev,
set_bit(FLAG_OPEN_SOURCE, &desc->flags);
/* No particular flag request, return here... */
- if (flags & GPIOD_FLAGS_BIT_DIR_SET)
+ if (!(flags & GPIOD_FLAGS_BIT_DIR_SET))
return desc;
/* Process flags */