summaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-09-07 19:53:00 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2013-09-07 19:53:00 +0200
commit27c7651a6a5f143eccd66db38c7a3035e1f8bcfb (patch)
treec20d3525a2933a8ea7e9ab03022c432b18e0e48a /drivers/gpio
parentMerge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dto... (diff)
parentgpio: return -ENOTSUPP if debounce cannot be set (diff)
downloadlinux-27c7651a6a5f143eccd66db38c7a3035e1f8bcfb.tar.xz
linux-27c7651a6a5f143eccd66db38c7a3035e1f8bcfb.zip
Merge tag 'gpio-v3.12-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio
Pull GPIO updates from Linus Walleij: "This is the bulk of GPIO changes for the v3.12 series: - A new driver for the TZ1090 PDC which is used on the metag architecture. - A new driver for the Kontron ETX or COMexpress GPIO block. This is found on some ETX x86 devices. - A new driver for the Fintek Super-I/O chips, used on some x86 boards. - Added device tree probing on a few select GPIO blocks. - Drop the Exynos support from the Samsung GPIO driver. The Samsung maintainers have moved over to use the modernized pin control driver to provide GPIO for the modern platforms instead. - The usual bunch of non-critical fixes and cleanups" * tag 'gpio-v3.12-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (36 commits) gpio: return -ENOTSUPP if debounce cannot be set gpio: improve error path in gpiolib gpio: add GPIO support for F71882FG and F71889F of: add vendor prefix for Microchip Technology Inc gpio: mcp23s08: rename the device tree property gpio: samsung: Drop support for Exynos SoCs gpio: pcf857x: Remove pdata argument to pcf857x_irq_domain_init() gpio: pcf857x: Sort headers alphabetically gpio: max7301: Reverting "Do not force SPI speed when using OF Platform" gpio: Fix bit masking in Kontron PLD GPIO driver gpio: pca953x: fix gpio input on gpio offsets >= 8 drivers/gpio: simplify use of devm_ioremap_resource drivers/gpio/gpio-omap.c: convert comma to semicolon gpio-lynxpoint: Fix warning about unbalanced pm_runtime_enable gpio: Fix platform driver name in Kontron PLD GPIO driver gpio: adnp: Fix segfault if request_threaded_irq fails gpio: msm: Staticize local variable 'msm_gpio' gpio: gpiolib-of.c: make error message more meaningful by adding the node name and index gpio: use dev_get_platdata() gpio/mxc: add chained_irq_enter/exit() to mx2_gpio_irq_handler ...
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig37
-rw-r--r--drivers/gpio/Makefile4
-rw-r--r--drivers/gpio/gpio-74x164.c2
-rw-r--r--drivers/gpio/gpio-adnp.c6
-rw-r--r--drivers/gpio/gpio-adp5520.c2
-rw-r--r--drivers/gpio/gpio-adp5588.c9
-rw-r--r--drivers/gpio/gpio-arizona.c2
-rw-r--r--drivers/gpio/gpio-da9052.c2
-rw-r--r--drivers/gpio/gpio-da9055.c2
-rw-r--r--drivers/gpio/gpio-em.c27
-rw-r--r--drivers/gpio/gpio-f7188x.c469
-rw-r--r--drivers/gpio/gpio-ich.c2
-rw-r--r--drivers/gpio/gpio-janz-ttl.c2
-rw-r--r--drivers/gpio/gpio-kempld.c219
-rw-r--r--drivers/gpio/gpio-lynxpoint.c1
-rw-r--r--drivers/gpio/gpio-max7301.c3
-rw-r--r--drivers/gpio/gpio-max730x.c2
-rw-r--r--drivers/gpio/gpio-max732x.c8
-rw-r--r--drivers/gpio/gpio-mc33880.c2
-rw-r--r--drivers/gpio/gpio-mcp23s08.c52
-rw-r--r--drivers/gpio/gpio-msic.c2
-rw-r--r--drivers/gpio/gpio-msm-v2.c2
-rw-r--r--drivers/gpio/gpio-mvebu.c7
-rw-r--r--drivers/gpio/gpio-mxc.c41
-rw-r--r--drivers/gpio/gpio-omap.c4
-rw-r--r--drivers/gpio/gpio-palmas.c29
-rw-r--r--drivers/gpio/gpio-pca953x.c6
-rw-r--r--drivers/gpio/gpio-pcf857x.c17
-rw-r--r--drivers/gpio/gpio-pl061.c2
-rw-r--r--drivers/gpio/gpio-pxa.c13
-rw-r--r--drivers/gpio/gpio-rcar.c2
-rw-r--r--drivers/gpio/gpio-rdc321x.c2
-rw-r--r--drivers/gpio/gpio-samsung.c871
-rw-r--r--drivers/gpio/gpio-spear-spics.c7
-rw-r--r--drivers/gpio/gpio-sta2x11.c2
-rw-r--r--drivers/gpio/gpio-sx150x.c2
-rw-r--r--drivers/gpio/gpio-timberdale.c4
-rw-r--r--drivers/gpio/gpio-tps65912.c2
-rw-r--r--drivers/gpio/gpio-ts5500.c2
-rw-r--r--drivers/gpio/gpio-twl4030.c6
-rw-r--r--drivers/gpio/gpio-twl6040.c6
-rw-r--r--drivers/gpio/gpio-tz1090-pdc.c243
-rw-r--r--drivers/gpio/gpio-tz1090.c606
-rw-r--r--drivers/gpio/gpio-ucb1400.c2
-rw-r--r--drivers/gpio/gpio-wm831x.c2
-rw-r--r--drivers/gpio/gpio-wm8350.c2
-rw-r--r--drivers/gpio/gpio-wm8994.c2
-rw-r--r--drivers/gpio/gpiolib-of.c3
-rw-r--r--drivers/gpio/gpiolib.c52
49 files changed, 1775 insertions, 1019 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b2450ba14138..349b16160ac9 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -146,6 +146,16 @@ config GPIO_MM_LANTIQ
(EBU) found on Lantiq SoCs. The gpios are output only as they are
created by attaching a 16bit latch to the bus.
+config GPIO_F7188X
+ tristate "F71882FG and F71889F GPIO support"
+ depends on X86
+ help
+ This option enables support for GPIOs found on Fintek Super-I/O
+ chips F71882FG and F71889F.
+
+ To compile this driver as a module, choose M here: the module will
+ be called f7188x-gpio.
+
config GPIO_MPC5200
def_bool y
depends on PPC_MPC52xx
@@ -242,6 +252,21 @@ config GPIO_TS5500
blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600
LCD port.
+config GPIO_TZ1090
+ bool "Toumaz Xenif TZ1090 GPIO support"
+ depends on SOC_TZ1090
+ select GENERIC_IRQ_CHIP
+ default y
+ help
+ Say yes here to support Toumaz Xenif TZ1090 GPIOs.
+
+config GPIO_TZ1090_PDC
+ bool "Toumaz Xenif TZ1090 PDC GPIO support"
+ depends on SOC_TZ1090
+ default y
+ help
+ Say yes here to support Toumaz Xenif TZ1090 PDC GPIOs.
+
config GPIO_XILINX
bool "Xilinx GPIO support"
depends on PPC_OF || MICROBLAZE || ARCH_ZYNQ
@@ -676,6 +701,18 @@ config GPIO_UCB1400
This enables support for the Philips UCB1400 GPIO pins.
The UCB1400 is an AC97 audio codec.
+comment "LPC GPIO expanders:"
+
+config GPIO_KEMPLD
+ tristate "Kontron ETX / COMexpress GPIO"
+ depends on MFD_KEMPLD
+ help
+ This enables support for the PLD GPIO interface on some Kontron ETX
+ and COMexpress (ETXexpress) modules.
+
+ This driver can also be built as a module. If so, the module will be
+ called gpio-kempld.
+
comment "MODULbus GPIO expanders:"
config GPIO_JANZ_TTL
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index ef3e983a2f1e..97438bf8434a 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -24,11 +24,13 @@ obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o
obj-$(CONFIG_ARCH_DAVINCI) += gpio-davinci.o
obj-$(CONFIG_GPIO_EM) += gpio-em.o
obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
+obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o
obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o
obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o
obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o
obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o
+obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o
obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o
obj-$(CONFIG_GPIO_LANGWELL) += gpio-langwell.o
obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o
@@ -79,6 +81,8 @@ obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
+obj-$(CONFIG_GPIO_TZ1090) += gpio-tz1090.o
+obj-$(CONFIG_GPIO_TZ1090_PDC) += gpio-tz1090-pdc.o
obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o
obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o
diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c
index 721607904d0a..5d518d5db7a0 100644
--- a/drivers/gpio/gpio-74x164.c
+++ b/drivers/gpio/gpio-74x164.c
@@ -129,7 +129,7 @@ static int gen_74x164_probe(struct spi_device *spi)
if (!chip)
return -ENOMEM;
- pdata = spi->dev.platform_data;
+ pdata = dev_get_platdata(&spi->dev);
if (pdata && pdata->base)
chip->gpio_chip.base = pdata->base;
else
diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c
index e60567fc5073..c0f3fc44ab0e 100644
--- a/drivers/gpio/gpio-adnp.c
+++ b/drivers/gpio/gpio-adnp.c
@@ -490,15 +490,11 @@ static int adnp_irq_setup(struct adnp *adnp)
if (err != 0) {
dev_err(chip->dev, "can't request IRQ#%d: %d\n",
adnp->client->irq, err);
- goto error;
+ return err;
}
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)
diff --git a/drivers/gpio/gpio-adp5520.c b/drivers/gpio/gpio-adp5520.c
index f33f78dcadaa..084337d5514d 100644
--- a/drivers/gpio/gpio-adp5520.c
+++ b/drivers/gpio/gpio-adp5520.c
@@ -89,7 +89,7 @@ static int adp5520_gpio_direction_output(struct gpio_chip *chip,
static int adp5520_gpio_probe(struct platform_device *pdev)
{
- struct adp5520_gpio_platform_data *pdata = pdev->dev.platform_data;
+ struct adp5520_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct adp5520_gpio *dev;
struct gpio_chip *gc;
int ret, i, gpios;
diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c
index 2ba56987db04..90fc4c99c024 100644
--- a/drivers/gpio/gpio-adp5588.c
+++ b/drivers/gpio/gpio-adp5588.c
@@ -276,7 +276,8 @@ static irqreturn_t adp5588_irq_handler(int irq, void *devid)
static int adp5588_irq_setup(struct adp5588_gpio *dev)
{
struct i2c_client *client = dev->client;
- struct adp5588_gpio_platform_data *pdata = client->dev.platform_data;
+ struct adp5588_gpio_platform_data *pdata =
+ dev_get_platdata(&client->dev);
unsigned gpio;
int ret;
@@ -349,7 +350,8 @@ static void adp5588_irq_teardown(struct adp5588_gpio *dev)
static int adp5588_gpio_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct adp5588_gpio_platform_data *pdata = client->dev.platform_data;
+ struct adp5588_gpio_platform_data *pdata =
+ dev_get_platdata(&client->dev);
struct adp5588_gpio *dev;
struct gpio_chip *gc;
int ret, i, revid;
@@ -440,7 +442,8 @@ err:
static int adp5588_gpio_remove(struct i2c_client *client)
{
- struct adp5588_gpio_platform_data *pdata = client->dev.platform_data;
+ struct adp5588_gpio_platform_data *pdata =
+ dev_get_platdata(&client->dev);
struct adp5588_gpio *dev = i2c_get_clientdata(client);
int ret;
diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c
index 0ea853f68db2..fa8b6a762761 100644
--- a/drivers/gpio/gpio-arizona.c
+++ b/drivers/gpio/gpio-arizona.c
@@ -97,7 +97,7 @@ static struct gpio_chip template_chip = {
static int arizona_gpio_probe(struct platform_device *pdev)
{
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
- struct arizona_pdata *pdata = arizona->dev->platform_data;
+ struct arizona_pdata *pdata = dev_get_platdata(arizona->dev);
struct arizona_gpio *arizona_gpio;
int ret;
diff --git a/drivers/gpio/gpio-da9052.c b/drivers/gpio/gpio-da9052.c
index 29b11e9b6a78..9b77dc05d4ad 100644
--- a/drivers/gpio/gpio-da9052.c
+++ b/drivers/gpio/gpio-da9052.c
@@ -216,7 +216,7 @@ static int da9052_gpio_probe(struct platform_device *pdev)
return -ENOMEM;
gpio->da9052 = dev_get_drvdata(pdev->dev.parent);
- pdata = gpio->da9052->dev->platform_data;
+ pdata = dev_get_platdata(gpio->da9052->dev);
gpio->gp = reference_gp;
if (pdata && pdata->gpio_base)
diff --git a/drivers/gpio/gpio-da9055.c b/drivers/gpio/gpio-da9055.c
index fd6dfe382f13..7ef0820032bd 100644
--- a/drivers/gpio/gpio-da9055.c
+++ b/drivers/gpio/gpio-da9055.c
@@ -150,7 +150,7 @@ static int da9055_gpio_probe(struct platform_device *pdev)
return -ENOMEM;
gpio->da9055 = dev_get_drvdata(pdev->dev.parent);
- pdata = gpio->da9055->dev->platform_data;
+ pdata = dev_get_platdata(gpio->da9055->dev);
gpio->gp = reference_gp;
if (pdata && pdata->gpio_base)
diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c
index 5cba855638bf..c6e1f086efe8 100644
--- a/drivers/gpio/gpio-em.c
+++ b/drivers/gpio/gpio-em.c
@@ -30,6 +30,7 @@
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_data/gpio-em.h>
struct em_gio_priv {
@@ -216,6 +217,21 @@ static int em_gio_to_irq(struct gpio_chip *chip, unsigned offset)
return irq_create_mapping(gpio_to_priv(chip)->irq_domain, offset);
}
+static int em_gio_request(struct gpio_chip *chip, unsigned offset)
+{
+ return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void em_gio_free(struct gpio_chip *chip, unsigned offset)
+{
+ pinctrl_free_gpio(chip->base + offset);
+
+ /* Set the GPIO as an input to ensure that the next GPIO request won't
+ * drive the GPIO pin as an output.
+ */
+ em_gio_direction_input(chip, offset);
+}
+
static int em_gio_irq_domain_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
@@ -237,7 +253,7 @@ static struct irq_domain_ops em_gio_irq_domain_ops = {
static int em_gio_probe(struct platform_device *pdev)
{
struct gpio_em_config pdata_dt;
- struct gpio_em_config *pdata = pdev->dev.platform_data;
+ struct gpio_em_config *pdata = dev_get_platdata(&pdev->dev);
struct em_gio_priv *p;
struct resource *io[2], *irq[2];
struct gpio_chip *gpio_chip;
@@ -308,6 +324,8 @@ static int em_gio_probe(struct platform_device *pdev)
gpio_chip->direction_output = em_gio_direction_output;
gpio_chip->set = em_gio_set;
gpio_chip->to_irq = em_gio_to_irq;
+ gpio_chip->request = em_gio_request;
+ gpio_chip->free = em_gio_free;
gpio_chip->label = name;
gpio_chip->owner = THIS_MODULE;
gpio_chip->base = pdata->gpio_base;
@@ -351,6 +369,13 @@ static int em_gio_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to add GPIO controller\n");
goto err1;
}
+
+ if (pdata->pctl_name) {
+ ret = gpiochip_add_pin_range(gpio_chip, pdata->pctl_name, 0,
+ gpio_chip->base, gpio_chip->ngpio);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "failed to add pin range\n");
+ }
return 0;
err1:
diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c
new file mode 100644
index 000000000000..9cb8320e1181
--- /dev/null
+++ b/drivers/gpio/gpio-f7188x.c
@@ -0,0 +1,469 @@
+/*
+ * GPIO driver for Fintek Super-I/O F71882 and F71889
+ *
+ * Copyright (C) 2010-2013 LaCie
+ *
+ * Author: Simon Guinot <simon.guinot@sequanux.org>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+
+#define DRVNAME "gpio-f7188x"
+
+/*
+ * Super-I/O registers
+ */
+#define SIO_LDSEL 0x07 /* Logical device select */
+#define SIO_DEVID 0x20 /* Device ID (2 bytes) */
+#define SIO_DEVREV 0x22 /* Device revision */
+#define SIO_MANID 0x23 /* Fintek ID (2 bytes) */
+
+#define SIO_LD_GPIO 0x06 /* GPIO logical device */
+#define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */
+#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */
+
+#define SIO_FINTEK_ID 0x1934 /* Manufacturer ID */
+#define SIO_F71882_ID 0x0541 /* F71882 chipset ID */
+#define SIO_F71889_ID 0x0909 /* F71889 chipset ID */
+
+enum chips { f71882fg, f71889f };
+
+static const char * const f7188x_names[] = {
+ "f71882fg",
+ "f71889f",
+};
+
+struct f7188x_sio {
+ int addr;
+ enum chips type;
+};
+
+struct f7188x_gpio_bank {
+ struct gpio_chip chip;
+ unsigned int regbase;
+ struct f7188x_gpio_data *data;
+};
+
+struct f7188x_gpio_data {
+ struct f7188x_sio *sio;
+ int nr_bank;
+ struct f7188x_gpio_bank *bank;
+};
+
+/*
+ * Super-I/O functions.
+ */
+
+static inline int superio_inb(int base, int reg)
+{
+ outb(reg, base);
+ return inb(base + 1);
+}
+
+static int superio_inw(int base, int reg)
+{
+ int val;
+
+ outb(reg++, base);
+ val = inb(base + 1) << 8;
+ outb(reg, base);
+ val |= inb(base + 1);
+
+ return val;
+}
+
+static inline void superio_outb(int base, int reg, int val)
+{
+ outb(reg, base);
+ outb(val, base + 1);
+}
+
+static inline int superio_enter(int base)
+{
+ /* Don't step on other drivers' I/O space by accident. */
+ if (!request_muxed_region(base, 2, DRVNAME)) {
+ pr_err(DRVNAME "I/O address 0x%04x already in use\n", base);
+ return -EBUSY;
+ }
+
+ /* According to the datasheet the key must be send twice. */
+ outb(SIO_UNLOCK_KEY, base);
+ outb(SIO_UNLOCK_KEY, base);
+
+ return 0;
+}
+
+static inline void superio_select(int base, int ld)
+{
+ outb(SIO_LDSEL, base);
+ outb(ld, base + 1);
+}
+
+static inline void superio_exit(int base)
+{
+ outb(SIO_LOCK_KEY, base);
+ release_region(base, 2);
+}
+
+/*
+ * GPIO chip.
+ */
+
+static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset);
+static int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset);
+static int f7188x_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value);
+static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value);
+
+#define F7188X_GPIO_BANK(_base, _ngpio, _regbase) \
+ { \
+ .chip = { \
+ .label = DRVNAME, \
+ .owner = THIS_MODULE, \
+ .direction_input = f7188x_gpio_direction_in, \
+ .get = f7188x_gpio_get, \
+ .direction_output = f7188x_gpio_direction_out, \
+ .set = f7188x_gpio_set, \
+ .base = _base, \
+ .ngpio = _ngpio, \
+ }, \
+ .regbase = _regbase, \
+ }
+
+#define gpio_dir(base) (base + 0)
+#define gpio_data_out(base) (base + 1)
+#define gpio_data_in(base) (base + 2)
+/* Output mode register (0:open drain 1:push-pull). */
+#define gpio_out_mode(base) (base + 3)
+
+static struct f7188x_gpio_bank f71882_gpio_bank[] = {
+ F7188X_GPIO_BANK(0 , 8, 0xF0),
+ F7188X_GPIO_BANK(10, 8, 0xE0),
+ F7188X_GPIO_BANK(20, 8, 0xD0),
+ F7188X_GPIO_BANK(30, 4, 0xC0),
+ F7188X_GPIO_BANK(40, 4, 0xB0),
+};
+
+static struct f7188x_gpio_bank f71889_gpio_bank[] = {
+ F7188X_GPIO_BANK(0 , 7, 0xF0),
+ F7188X_GPIO_BANK(10, 7, 0xE0),
+ F7188X_GPIO_BANK(20, 8, 0xD0),
+ F7188X_GPIO_BANK(30, 8, 0xC0),
+ F7188X_GPIO_BANK(40, 8, 0xB0),
+ F7188X_GPIO_BANK(50, 5, 0xA0),
+ F7188X_GPIO_BANK(60, 8, 0x90),
+ F7188X_GPIO_BANK(70, 8, 0x80),
+};
+
+static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ int err;
+ struct f7188x_gpio_bank *bank =
+ container_of(chip, struct f7188x_gpio_bank, chip);
+ struct f7188x_sio *sio = bank->data->sio;
+ u8 dir;
+
+ err = superio_enter(sio->addr);
+ if (err)
+ return err;
+ superio_select(sio->addr, SIO_LD_GPIO);
+
+ dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
+ dir &= ~(1 << offset);
+ superio_outb(sio->addr, gpio_dir(bank->regbase), dir);
+
+ superio_exit(sio->addr);
+
+ return 0;
+}
+
+static int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ int err;
+ struct f7188x_gpio_bank *bank =
+ container_of(chip, struct f7188x_gpio_bank, chip);
+ struct f7188x_sio *sio = bank->data->sio;
+ u8 dir, data;
+
+ err = superio_enter(sio->addr);
+ if (err)
+ return err;
+ superio_select(sio->addr, SIO_LD_GPIO);
+
+ dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
+ dir = !!(dir & (1 << offset));
+ if (dir)
+ data = superio_inb(sio->addr, gpio_data_out(bank->regbase));
+ else
+ data = superio_inb(sio->addr, gpio_data_in(bank->regbase));
+
+ superio_exit(sio->addr);
+
+ return !!(data & 1 << offset);
+}
+
+static int f7188x_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ int err;
+ struct f7188x_gpio_bank *bank =
+ container_of(chip, struct f7188x_gpio_bank, chip);
+ struct f7188x_sio *sio = bank->data->sio;
+ u8 dir, data_out;
+
+ err = superio_enter(sio->addr);
+ if (err)
+ return err;
+ superio_select(sio->addr, SIO_LD_GPIO);
+
+ data_out = superio_inb(sio->addr, gpio_data_out(bank->regbase));
+ if (value)
+ data_out |= (1 << offset);
+ else
+ data_out &= ~(1 << offset);
+ superio_outb(sio->addr, gpio_data_out(bank->regbase), data_out);
+
+ dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
+ dir |= (1 << offset);
+ superio_outb(sio->addr, gpio_dir(bank->regbase), dir);
+
+ superio_exit(sio->addr);
+
+ return 0;
+}
+
+static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ int err;
+ struct f7188x_gpio_bank *bank =
+ container_of(chip, struct f7188x_gpio_bank, chip);
+ struct f7188x_sio *sio = bank->data->sio;
+ u8 data_out;
+
+ err = superio_enter(sio->addr);
+ if (err)
+ return;
+ superio_select(sio->addr, SIO_LD_GPIO);
+
+ data_out = superio_inb(sio->addr, gpio_data_out(bank->regbase));
+ if (value)
+ data_out |= (1 << offset);
+ else
+ data_out &= ~(1 << offset);
+ superio_outb(sio->addr, gpio_data_out(bank->regbase), data_out);
+
+ superio_exit(sio->addr);
+}
+
+/*
+ * Platform device and driver.
+ */
+
+static int f7188x_gpio_probe(struct platform_device *pdev)
+{
+ int err;
+ int i;
+ struct f7188x_sio *sio = pdev->dev.platform_data;
+ struct f7188x_gpio_data *data;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ switch (sio->type) {
+ case f71882fg:
+ data->nr_bank = ARRAY_SIZE(f71882_gpio_bank);
+ data->bank = f71882_gpio_bank;
+ break;
+ case f71889f:
+ data->nr_bank = ARRAY_SIZE(f71889_gpio_bank);
+ data->bank = f71889_gpio_bank;
+ break;
+ default:
+ return -ENODEV;
+ }
+ data->sio = sio;
+
+ platform_set_drvdata(pdev, data);
+
+ /* For each GPIO bank, register a GPIO chip. */
+ for (i = 0; i < data->nr_bank; i++) {
+ struct f7188x_gpio_bank *bank = &data->bank[i];
+
+ bank->chip.dev = &pdev->dev;
+ bank->data = data;
+
+ err = gpiochip_add(&bank->chip);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Failed to register gpiochip %d: %d\n",
+ i, err);
+ goto err_gpiochip;
+ }
+ }
+
+ return 0;
+
+err_gpiochip:
+ for (i = i - 1; i >= 0; i--) {
+ struct f7188x_gpio_bank *bank = &data->bank[i];
+ int tmp;
+
+ tmp = gpiochip_remove(&bank->chip);
+ if (tmp < 0)
+ dev_err(&pdev->dev,
+ "Failed to remove gpiochip %d: %d\n",
+ i, tmp);
+ }
+
+ return err;
+}
+
+static int f7188x_gpio_remove(struct platform_device *pdev)
+{
+ int err;
+ int i;
+ struct f7188x_gpio_data *data = platform_get_drvdata(pdev);
+
+ for (i = 0; i < data->nr_bank; i++) {
+ struct f7188x_gpio_bank *bank = &data->bank[i];
+
+ err = gpiochip_remove(&bank->chip);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Failed to remove GPIO gpiochip %d: %d\n",
+ i, err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int __init f7188x_find(int addr, struct f7188x_sio *sio)
+{
+ int err;
+ u16 devid;
+
+ err = superio_enter(addr);
+ if (err)
+ return err;
+
+ err = -ENODEV;
+ devid = superio_inw(addr, SIO_MANID);
+ if (devid != SIO_FINTEK_ID) {
+ pr_debug(DRVNAME ": Not a Fintek device at 0x%08x\n", addr);
+ goto err;
+ }
+
+ devid = superio_inw(addr, SIO_DEVID);
+ switch (devid) {
+ case SIO_F71882_ID:
+ sio->type = f71882fg;
+ break;
+ case SIO_F71889_ID:
+ sio->type = f71889f;
+ break;
+ default:
+ pr_info(DRVNAME ": Unsupported Fintek device 0x%04x\n", devid);
+ goto err;
+ }
+ sio->addr = addr;
+ err = 0;
+
+ pr_info(DRVNAME ": Found %s at %#x, revision %d\n",
+ f7188x_names[sio->type],
+ (unsigned int) addr,
+ (int) superio_inb(addr, SIO_DEVREV));
+
+err:
+ superio_exit(addr);
+ return err;
+}
+
+static struct platform_device *f7188x_gpio_pdev;
+
+static int __init
+f7188x_gpio_device_add(const struct f7188x_sio *sio)
+{
+ int err;
+
+ f7188x_gpio_pdev = platform_device_alloc(DRVNAME, -1);
+ if (!f7188x_gpio_pdev)
+ return -ENOMEM;
+
+ err = platform_device_add_data(f7188x_gpio_pdev,
+ sio, sizeof(*sio));
+ if (err) {
+ pr_err(DRVNAME "Platform data allocation failed\n");
+ goto err;
+ }
+
+ err = platform_device_add(f7188x_gpio_pdev);
+ if (err) {
+ pr_err(DRVNAME "Device addition failed\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ platform_device_put(f7188x_gpio_pdev);
+
+ return err;
+}
+
+/*
+ * Try to match a supported Fintech device by reading the (hard-wired)
+ * configuration I/O ports. If available, then register both the platform
+ * device and driver to support the GPIOs.
+ */
+
+static struct platform_driver f7188x_gpio_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRVNAME,
+ },
+ .probe = f7188x_gpio_probe,
+ .remove = f7188x_gpio_remove,
+};
+
+static int __init f7188x_gpio_init(void)
+{
+ int err;
+ struct f7188x_sio sio;
+
+ if (f7188x_find(0x2e, &sio) &&
+ f7188x_find(0x4e, &sio))
+ return -ENODEV;
+
+ err = platform_driver_register(&f7188x_gpio_driver);
+ if (!err) {
+ err = f7188x_gpio_device_add(&sio);
+ if (err)
+ platform_driver_unregister(&f7188x_gpio_driver);
+ }
+
+ return err;
+}
+subsys_initcall(f7188x_gpio_init);
+
+static void __exit f7188x_gpio_exit(void)
+{
+ platform_device_unregister(f7188x_gpio_pdev);
+ platform_driver_unregister(&f7188x_gpio_driver);
+}
+module_exit(f7188x_gpio_exit);
+
+MODULE_DESCRIPTION("GPIO driver for Super-I/O chips F71882FG and F71889F");
+MODULE_AUTHOR("Simon Guinot <simon.guinot@sequanux.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c
index 2729e3d2d5bb..814addb62d2c 100644
--- a/drivers/gpio/gpio-ich.c
+++ b/drivers/gpio/gpio-ich.c
@@ -354,7 +354,7 @@ static int ichx_gpio_probe(struct platform_device *pdev)
{
struct resource *res_base, *res_pm;
int err;
- struct lpc_ich_info *ich_info = pdev->dev.platform_data;
+ struct lpc_ich_info *ich_info = dev_get_platdata(&pdev->dev);
if (!ich_info)
return -ENODEV;
diff --git a/drivers/gpio/gpio-janz-ttl.c b/drivers/gpio/gpio-janz-ttl.c
index 7d0a04169a35..2ecd3a09c743 100644
--- a/drivers/gpio/gpio-janz-ttl.c
+++ b/drivers/gpio/gpio-janz-ttl.c
@@ -149,7 +149,7 @@ static int ttl_probe(struct platform_device *pdev)
struct resource *res;
int ret;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(dev, "no platform data\n");
ret = -ENXIO;
diff --git a/drivers/gpio/gpio-kempld.c b/drivers/gpio/gpio-kempld.c
new file mode 100644
index 000000000000..efdc3924d7df
--- /dev/null
+++ b/drivers/gpio/gpio-kempld.c
@@ -0,0 +1,219 @@
+/*
+ * Kontron PLD GPIO driver
+ *
+ * Copyright (c) 2010-2013 Kontron Europe GmbH
+ * Author: Michael Brunner <michael.brunner@kontron.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/mfd/kempld.h>
+
+#define KEMPLD_GPIO_MAX_NUM 16
+#define KEMPLD_GPIO_MASK(x) (1 << ((x) % 8))
+#define KEMPLD_GPIO_DIR_NUM(x) (0x40 + (x) / 8)
+#define KEMPLD_GPIO_LVL_NUM(x) (0x42 + (x) / 8)
+#define KEMPLD_GPIO_EVT_LVL_EDGE 0x46
+#define KEMPLD_GPIO_IEN 0x4A
+
+struct kempld_gpio_data {
+ struct gpio_chip chip;
+ struct kempld_device_data *pld;
+};
+
+/*
+ * Set or clear GPIO bit
+ * kempld_get_mutex must be called prior to calling this function.
+ */
+static void kempld_gpio_bitop(struct kempld_device_data *pld,
+ u8 reg, u8 bit, u8 val)
+{
+ u8 status;
+
+ status = kempld_read8(pld, reg);
+ if (val)
+ status |= KEMPLD_GPIO_MASK(bit);
+ else
+ status &= ~KEMPLD_GPIO_MASK(bit);
+ kempld_write8(pld, reg, status);
+}
+
+static int kempld_gpio_get_bit(struct kempld_device_data *pld, u8 reg, u8 bit)
+{
+ u8 status;
+
+ kempld_get_mutex(pld);
+ status = kempld_read8(pld, reg);
+ kempld_release_mutex(pld);
+
+ return !!(status & KEMPLD_GPIO_MASK(bit));
+}
+
+static int kempld_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct kempld_gpio_data *gpio
+ = container_of(chip, struct kempld_gpio_data, chip);
+ struct kempld_device_data *pld = gpio->pld;
+
+ return kempld_gpio_get_bit(pld, KEMPLD_GPIO_LVL_NUM(offset), offset);
+}
+
+static void kempld_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct kempld_gpio_data *gpio
+ = container_of(chip, struct kempld_gpio_data, chip);
+ struct kempld_device_data *pld = gpio->pld;
+
+ kempld_get_mutex(pld);
+ kempld_gpio_bitop(pld, KEMPLD_GPIO_LVL_NUM(offset), offset, value);
+ kempld_release_mutex(pld);
+}
+
+static int kempld_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct kempld_gpio_data *gpio
+ = container_of(chip, struct kempld_gpio_data, chip);
+ struct kempld_device_data *pld = gpio->pld;
+
+ kempld_get_mutex(pld);
+ kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR_NUM(offset), offset, 0);
+ kempld_release_mutex(pld);
+
+ return 0;
+}
+
+static int kempld_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct kempld_gpio_data *gpio
+ = container_of(chip, struct kempld_gpio_data, chip);
+ struct kempld_device_data *pld = gpio->pld;
+
+ kempld_get_mutex(pld);
+ kempld_gpio_bitop(pld, KEMPLD_GPIO_LVL_NUM(offset), offset, value);
+ kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR_NUM(offset), offset, 1);
+ kempld_release_mutex(pld);
+
+ return 0;
+}
+
+static int kempld_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+ struct kempld_gpio_data *gpio
+ = container_of(chip, struct kempld_gpio_data, chip);
+ struct kempld_device_data *pld = gpio->pld;
+
+ return kempld_gpio_get_bit(pld, KEMPLD_GPIO_DIR_NUM(offset), offset);
+}
+
+static int kempld_gpio_pincount(struct kempld_device_data *pld)
+{
+ u16 evt, evt_back;
+
+ kempld_get_mutex(pld);
+
+ /* Backup event register as it might be already initialized */
+ evt_back = kempld_read16(pld, KEMPLD_GPIO_EVT_LVL_EDGE);
+ /* Clear event register */
+ kempld_write16(pld, KEMPLD_GPIO_EVT_LVL_EDGE, 0x0000);
+ /* Read back event register */
+ evt = kempld_read16(pld, KEMPLD_GPIO_EVT_LVL_EDGE);
+ /* Restore event register */
+ kempld_write16(pld, KEMPLD_GPIO_EVT_LVL_EDGE, evt_back);
+
+ kempld_release_mutex(pld);
+
+ return evt ? __ffs(evt) : 16;
+}
+
+static int kempld_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct kempld_device_data *pld = dev_get_drvdata(dev->parent);
+ struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
+ struct kempld_gpio_data *gpio;
+ struct gpio_chip *chip;
+ int ret;
+
+ if (pld->info.spec_major < 2) {
+ dev_err(dev,
+ "Driver only supports GPIO devices compatible to PLD spec. rev. 2.0 or higher\n");
+ return -ENODEV;
+ }
+
+ gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL);
+ if (gpio == NULL)
+ return -ENOMEM;
+
+ gpio->pld = pld;
+
+ platform_set_drvdata(pdev, gpio);
+
+ chip = &gpio->chip;
+ chip->label = "gpio-kempld";
+ chip->owner = THIS_MODULE;
+ chip->dev = dev;
+ chip->can_sleep = 1;
+ if (pdata && pdata->gpio_base)
+ chip->base = pdata->gpio_base;
+ else
+ chip->base = -1;
+ chip->direction_input = kempld_gpio_direction_input;
+ chip->direction_output = kempld_gpio_direction_output;
+ chip->get_direction = kempld_gpio_get_direction;
+ chip->get = kempld_gpio_get;
+ chip->set = kempld_gpio_set;
+ chip->ngpio = kempld_gpio_pincount(pld);
+ if (chip->ngpio == 0) {
+ dev_err(dev, "No GPIO pins detected\n");
+ return -ENODEV;
+ }
+
+ ret = gpiochip_add(chip);
+ if (ret) {
+ dev_err(dev, "Could not register GPIO chip\n");
+ return ret;
+ }
+
+ dev_info(dev, "GPIO functionality initialized with %d pins\n",
+ chip->ngpio);
+
+ return 0;
+}
+
+static int kempld_gpio_remove(struct platform_device *pdev)
+{
+ struct kempld_gpio_data *gpio = platform_get_drvdata(pdev);
+
+ return gpiochip_remove(&gpio->chip);
+}
+
+static struct platform_driver kempld_gpio_driver = {
+ .driver = {
+ .name = "kempld-gpio",
+ .owner = THIS_MODULE,
+ },
+ .probe = kempld_gpio_probe,
+ .remove = kempld_gpio_remove,
+};
+
+module_platform_driver(kempld_gpio_driver);
+
+MODULE_DESCRIPTION("KEM PLD GPIO Driver");
+MODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpio-kempld");
diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c
index 761c4705dfbb..2d9ca6055e5e 100644
--- a/drivers/gpio/gpio-lynxpoint.c
+++ b/drivers/gpio/gpio-lynxpoint.c
@@ -444,6 +444,7 @@ static int lp_gpio_remove(struct platform_device *pdev)
{
struct lp_gpio *lg = platform_get_drvdata(pdev);
int err;
+ pm_runtime_disable(&pdev->dev);
err = gpiochip_remove(&lg->chip);
if (err)
dev_warn(&pdev->dev, "failed to remove gpio_chip.\n");
diff --git a/drivers/gpio/gpio-max7301.c b/drivers/gpio/gpio-max7301.c
index 3b16ab701630..6e1c984a75d4 100644
--- a/drivers/gpio/gpio-max7301.c
+++ b/drivers/gpio/gpio-max7301.c
@@ -56,8 +56,7 @@ static int max7301_probe(struct spi_device *spi)
int ret;
/* bits_per_word cannot be configured in platform data */
- if (spi->dev.platform_data)
- spi->bits_per_word = 16;
+ spi->bits_per_word = 16;
ret = spi_setup(spi);
if (ret < 0)
return ret;
diff --git a/drivers/gpio/gpio-max730x.c b/drivers/gpio/gpio-max730x.c
index 00092342b84c..f4f4ed19bdc1 100644
--- a/drivers/gpio/gpio-max730x.c
+++ b/drivers/gpio/gpio-max730x.c
@@ -166,7 +166,7 @@ int __max730x_probe(struct max7301 *ts)
struct max7301_platform_data *pdata;
int i, ret;
- pdata = dev->platform_data;
+ pdata = dev_get_platdata(dev);
mutex_init(&ts->lock);
dev_set_drvdata(dev, ts);
diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c
index d4b51b163b03..91ad74dea8ce 100644
--- a/drivers/gpio/gpio-max732x.c
+++ b/drivers/gpio/gpio-max732x.c
@@ -453,7 +453,7 @@ static int max732x_irq_setup(struct max732x_chip *chip,
const struct i2c_device_id *id)
{
struct i2c_client *client = chip->client;
- struct max732x_platform_data *pdata = client->dev.platform_data;
+ struct max732x_platform_data *pdata = dev_get_platdata(&client->dev);
int has_irq = max732x_features[id->driver_data] >> 32;
int ret;
@@ -512,7 +512,7 @@ static int max732x_irq_setup(struct max732x_chip *chip,
const struct i2c_device_id *id)
{
struct i2c_client *client = chip->client;
- struct max732x_platform_data *pdata = client->dev.platform_data;
+ struct max732x_platform_data *pdata = dev_get_platdata(&client->dev);
int has_irq = max732x_features[id->driver_data] >> 32;
if (pdata->irq_base && has_irq != INT_NONE)
@@ -583,7 +583,7 @@ static int max732x_probe(struct i2c_client *client,
uint16_t addr_a, addr_b;
int ret, nr_port;
- pdata = client->dev.platform_data;
+ pdata = dev_get_platdata(&client->dev);
if (pdata == NULL) {
dev_dbg(&client->dev, "no platform data\n");
return -EINVAL;
@@ -653,7 +653,7 @@ out_failed:
static int max732x_remove(struct i2c_client *client)
{
- struct max732x_platform_data *pdata = client->dev.platform_data;
+ struct max732x_platform_data *pdata = dev_get_platdata(&client->dev);
struct max732x_chip *chip = i2c_get_clientdata(client);
int ret;
diff --git a/drivers/gpio/gpio-mc33880.c b/drivers/gpio/gpio-mc33880.c
index 63a7a1bfb2d9..3fd2caa4a2e0 100644
--- a/drivers/gpio/gpio-mc33880.c
+++ b/drivers/gpio/gpio-mc33880.c
@@ -86,7 +86,7 @@ static int mc33880_probe(struct spi_device *spi)
struct mc33880_platform_data *pdata;
int ret;
- pdata = spi->dev.platform_data;
+ pdata = dev_get_platdata(&spi->dev);
if (!pdata || !pdata->base) {
dev_dbg(&spi->dev, "incorrect or missing platform data\n");
return -EINVAL;
diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c
index 6a4470b84488..2deb0c5e54a4 100644
--- a/drivers/gpio/gpio-mcp23s08.c
+++ b/drivers/gpio/gpio-mcp23s08.c
@@ -483,10 +483,21 @@ fail:
#ifdef CONFIG_SPI_MASTER
static struct of_device_id mcp23s08_spi_of_match[] = {
{
- .compatible = "mcp,mcp23s08", .data = (void *) MCP_TYPE_S08,
+ .compatible = "microchip,mcp23s08",
+ .data = (void *) MCP_TYPE_S08,
},
{
- .compatible = "mcp,mcp23s17", .data = (void *) MCP_TYPE_S17,
+ .compatible = "microchip,mcp23s17",
+ .data = (void *) MCP_TYPE_S17,
+ },
+/* NOTE: The use of the mcp prefix is deprecated and will be removed. */
+ {
+ .compatible = "mcp,mcp23s08",
+ .data = (void *) MCP_TYPE_S08,
+ },
+ {
+ .compatible = "mcp,mcp23s17",
+ .data = (void *) MCP_TYPE_S17,
},
{ },
};
@@ -496,10 +507,21 @@ MODULE_DEVICE_TABLE(of, mcp23s08_spi_of_match);
#if IS_ENABLED(CONFIG_I2C)
static struct of_device_id mcp23s08_i2c_of_match[] = {
{
- .compatible = "mcp,mcp23008", .data = (void *) MCP_TYPE_008,
+ .compatible = "microchip,mcp23008",
+ .data = (void *) MCP_TYPE_008,
+ },
+ {
+ .compatible = "microchip,mcp23017",
+ .data = (void *) MCP_TYPE_017,
+ },
+/* NOTE: The use of the mcp prefix is deprecated and will be removed. */
+ {
+ .compatible = "mcp,mcp23008",
+ .data = (void *) MCP_TYPE_008,
},
{
- .compatible = "mcp,mcp23017", .data = (void *) MCP_TYPE_017,
+ .compatible = "mcp,mcp23017",
+ .data = (void *) MCP_TYPE_017,
},
{ },
};
@@ -520,14 +542,13 @@ static int mcp230xx_probe(struct i2c_client *client,
match = of_match_device(of_match_ptr(mcp23s08_i2c_of_match),
&client->dev);
- if (match) {
+ pdata = dev_get_platdata(&client->dev);
+ if (match || !pdata) {
base = -1;
pullups = 0;
} else {
- pdata = client->dev.platform_data;
- if (!pdata || !gpio_is_valid(pdata->base)) {
- dev_dbg(&client->dev,
- "invalid or missing platform data\n");
+ if (!gpio_is_valid(pdata->base)) {
+ dev_dbg(&client->dev, "invalid platform data\n");
return -EINVAL;
}
base = pdata->base;
@@ -621,10 +642,15 @@ static int mcp23s08_probe(struct spi_device *spi)
if (match) {
type = (int)match->data;
status = of_property_read_u32(spi->dev.of_node,
- "mcp,spi-present-mask", &spi_present_mask);
+ "microchip,spi-present-mask", &spi_present_mask);
if (status) {
- dev_err(&spi->dev, "DT has no spi-present-mask\n");
- return -ENODEV;
+ status = of_property_read_u32(spi->dev.of_node,
+ "mcp,spi-present-mask", &spi_present_mask);
+ if (status) {
+ dev_err(&spi->dev,
+ "DT has no spi-present-mask\n");
+ return -ENODEV;
+ }
}
if ((spi_present_mask <= 0) || (spi_present_mask >= 256)) {
dev_err(&spi->dev, "invalid spi-present-mask\n");
@@ -635,7 +661,7 @@ static int mcp23s08_probe(struct spi_device *spi)
pullups[addr] = 0;
} else {
type = spi_get_device_id(spi)->driver_data;
- pdata = spi->dev.platform_data;
+ pdata = dev_get_platdata(&spi->dev);
if (!pdata || !gpio_is_valid(pdata->base)) {
dev_dbg(&spi->dev,
"invalid or missing platform data\n");
diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c
index 27ea7b9257ff..d75eaa3a1dcc 100644
--- a/drivers/gpio/gpio-msic.c
+++ b/drivers/gpio/gpio-msic.c
@@ -259,7 +259,7 @@ static void msic_gpio_irq_handler(unsigned irq, struct irq_desc *desc)
static int platform_msic_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct intel_msic_gpio_pdata *pdata = dev->platform_data;
+ struct intel_msic_gpio_pdata *pdata = dev_get_platdata(dev);
struct msic_gpio *mg;
int irq = platform_get_irq(pdev, 0);
int retval;
diff --git a/drivers/gpio/gpio-msm-v2.c b/drivers/gpio/gpio-msm-v2.c
index c2fa77086eb5..f7a0cc4da950 100644
--- a/drivers/gpio/gpio-msm-v2.c
+++ b/drivers/gpio/gpio-msm-v2.c
@@ -106,7 +106,7 @@ struct msm_gpio_dev {
void __iomem *msm_tlmm_base;
};
-struct msm_gpio_dev msm_gpio;
+static struct msm_gpio_dev msm_gpio;
#define GPIO_INTR_CFG_SU(gpio) (msm_gpio.msm_tlmm_base + 0x0400 + \
(0x04 * (gpio)))
diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
index 80ad35e2a8cd..3c3321f94053 100644
--- a/drivers/gpio/gpio-mvebu.c
+++ b/drivers/gpio/gpio-mvebu.c
@@ -566,12 +566,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
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");
@@ -611,6 +605,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
mvchip->chip.dbg_show = mvebu_gpio_dbg_show;
spin_lock_init(&mvchip->lock);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mvchip->membase = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mvchip->membase))
return PTR_ERR(mvchip->membase);
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
index 7176743915d3..3307f6db3a92 100644
--- a/drivers/gpio/gpio-mxc.c
+++ b/drivers/gpio/gpio-mxc.c
@@ -19,6 +19,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -291,6 +292,9 @@ static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc)
{
u32 irq_msk, irq_stat;
struct mxc_gpio_port *port;
+ struct irq_chip *chip = irq_get_chip(irq);
+
+ chained_irq_enter(chip, desc);
/* walk through all interrupt status registers */
list_for_each_entry(port, &mxc_gpio_ports, node) {
@@ -302,6 +306,7 @@ static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc)
if (irq_stat)
mxc_gpio_irq_handler(port, irq_stat);
}
+ chained_irq_exit(chip, desc);
}
/*
@@ -405,34 +410,19 @@ static int mxc_gpio_probe(struct platform_device *pdev)
mxc_gpio_get_hw(pdev);
- port = kzalloc(sizeof(struct mxc_gpio_port), GFP_KERNEL);
+ port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
if (!port)
return -ENOMEM;
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!iores) {
- err = -ENODEV;
- goto out_kfree;
- }
-
- if (!request_mem_region(iores->start, resource_size(iores),
- pdev->name)) {
- err = -EBUSY;
- goto out_kfree;
- }
-
- port->base = ioremap(iores->start, resource_size(iores));
- if (!port->base) {
- err = -ENOMEM;
- goto out_release_mem;
- }
+ port->base = devm_ioremap_resource(&pdev->dev, iores);
+ if (IS_ERR(port->base))
+ return PTR_ERR(port->base);
port->irq_high = platform_get_irq(pdev, 1);
port->irq = platform_get_irq(pdev, 0);
- if (port->irq < 0) {
- err = -EINVAL;
- goto out_iounmap;
- }
+ if (port->irq < 0)
+ return -EINVAL;
/* disable the interrupt and clear the status */
writel(0, port->base + GPIO_IMR);
@@ -462,7 +452,7 @@ static int mxc_gpio_probe(struct platform_device *pdev)
port->base + GPIO_DR, NULL,
port->base + GPIO_GDIR, NULL, 0);
if (err)
- goto out_iounmap;
+ goto out_bgio;
port->bgc.gc.to_irq = mxc_gpio_to_irq;
port->bgc.gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
@@ -498,12 +488,7 @@ out_gpiochip_remove:
WARN_ON(gpiochip_remove(&port->bgc.gc) < 0);
out_bgpio_remove:
bgpio_remove(&port->bgc);
-out_iounmap:
- iounmap(port->base);
-out_release_mem:
- release_mem_region(iores->start, resource_size(iores));
-out_kfree:
- kfree(port);
+out_bgio:
dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err);
return err;
}
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index dfeb3a3a8f20..0ff43552d472 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -1030,7 +1030,7 @@ omap_mpuio_alloc_gc(struct gpio_bank *bank, unsigned int irq_start,
ct->chip.irq_set_type = gpio_irq_type;
if (bank->regs->wkup_en)
- ct->chip.irq_set_wake = gpio_wake_enable,
+ ct->chip.irq_set_wake = gpio_wake_enable;
ct->regs.mask = OMAP_MPUIO_GPIO_INT / bank->stride;
irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
@@ -1100,7 +1100,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
match = of_match_device(of_match_ptr(omap_gpio_match), dev);
- pdata = match ? match->data : dev->platform_data;
+ pdata = match ? match->data : dev_get_platdata(dev);
if (!pdata)
return -EINVAL;
diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c
index e3a4e56f5a42..8588af0f7661 100644
--- a/drivers/gpio/gpio-palmas.c
+++ b/drivers/gpio/gpio-palmas.c
@@ -43,9 +43,22 @@ static int palmas_gpio_get(struct gpio_chip *gc, unsigned offset)
unsigned int val;
int ret;
- ret = palmas_read(palmas, PALMAS_GPIO_BASE, PALMAS_GPIO_DATA_IN, &val);
+ ret = palmas_read(palmas, PALMAS_GPIO_BASE, PALMAS_GPIO_DATA_DIR, &val);
if (ret < 0) {
- dev_err(gc->dev, "GPIO_DATA_IN read failed, err = %d\n", ret);
+ dev_err(gc->dev, "GPIO_DATA_DIR read failed, err = %d\n", ret);
+ return ret;
+ }
+
+ if (val & (1 << offset)) {
+ ret = palmas_read(palmas, PALMAS_GPIO_BASE,
+ PALMAS_GPIO_DATA_OUT, &val);
+ } else {
+ ret = palmas_read(palmas, PALMAS_GPIO_BASE,
+ PALMAS_GPIO_DATA_IN, &val);
+ }
+ if (ret < 0) {
+ dev_err(gc->dev, "GPIO_DATA_IN/OUT read failed, err = %d\n",
+ ret);
return ret;
}
return !!(val & BIT(offset));
@@ -134,7 +147,7 @@ static int palmas_gpio_probe(struct platform_device *pdev)
palmas_gpio->gpio_chip.get = palmas_gpio_get;
palmas_gpio->gpio_chip.dev = &pdev->dev;
#ifdef CONFIG_OF_GPIO
- palmas_gpio->gpio_chip.of_node = palmas->dev->of_node;
+ palmas_gpio->gpio_chip.of_node = pdev->dev.of_node;
#endif
palmas_pdata = dev_get_platdata(palmas->dev);
if (palmas_pdata && palmas_pdata->gpio_base)
@@ -159,9 +172,19 @@ static int palmas_gpio_remove(struct platform_device *pdev)
return gpiochip_remove(&palmas_gpio->gpio_chip);
}
+static struct of_device_id of_palmas_gpio_match[] = {
+ { .compatible = "ti,palmas-gpio"},
+ { .compatible = "ti,tps65913-gpio"},
+ { .compatible = "ti,tps65914-gpio"},
+ { .compatible = "ti,tps80036-gpio"},
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_palmas_gpio_match);
+
static struct platform_driver palmas_gpio_driver = {
.driver.name = "palmas-gpio",
.driver.owner = THIS_MODULE,
+ .driver.of_match_table = of_palmas_gpio_match,
.probe = palmas_gpio_probe,
.remove = palmas_gpio_remove,
};
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index 8804aec2950f..cdd1aa12b895 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -308,7 +308,7 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
return 0;
}
- return (reg_val & (1u << off)) ? 1 : 0;
+ return (reg_val & (1u << (off % BANK_SZ))) ? 1 : 0;
}
static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
@@ -731,7 +731,7 @@ static int pca953x_probe(struct i2c_client *client,
if (chip == NULL)
return -ENOMEM;
- pdata = client->dev.platform_data;
+ pdata = dev_get_platdata(&client->dev);
if (pdata) {
irq_base = pdata->irq_base;
chip->gpio_start = pdata->gpio_base;
@@ -785,7 +785,7 @@ static int pca953x_probe(struct i2c_client *client,
static int pca953x_remove(struct i2c_client *client)
{
- struct pca953x_platform_data *pdata = client->dev.platform_data;
+ struct pca953x_platform_data *pdata = dev_get_platdata(&client->dev);
struct pca953x_chip *chip = i2c_get_clientdata(client);
int ret = 0;
diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c
index e8faf53f3875..9e61bb0719d0 100644
--- a/drivers/gpio/gpio-pcf857x.c
+++ b/drivers/gpio/gpio-pcf857x.c
@@ -18,15 +18,15 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/kernel.h>
-#include <linux/slab.h>
#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/kernel.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
@@ -223,7 +223,6 @@ static void pcf857x_irq_domain_cleanup(struct pcf857x *gpio)
}
static int pcf857x_irq_domain_init(struct pcf857x *gpio,
- struct pcf857x_platform_data *pdata,
struct i2c_client *client)
{
int status;
@@ -262,7 +261,7 @@ static int pcf857x_probe(struct i2c_client *client,
struct pcf857x *gpio;
int status;
- pdata = client->dev.platform_data;
+ pdata = dev_get_platdata(&client->dev);
if (!pdata) {
dev_dbg(&client->dev, "no platform data\n");
}
@@ -286,8 +285,8 @@ static int pcf857x_probe(struct i2c_client *client,
gpio->chip.ngpio = id->driver_data;
/* enable gpio_to_irq() if platform has settings */
- if (pdata && client->irq) {
- status = pcf857x_irq_domain_init(gpio, pdata, client);
+ if (client->irq) {
+ status = pcf857x_irq_domain_init(gpio, client);
if (status < 0) {
dev_err(&client->dev, "irq_domain init failed\n");
goto fail;
@@ -388,7 +387,7 @@ fail:
dev_dbg(&client->dev, "probe error %d for '%s'\n",
status, client->name);
- if (pdata && client->irq)
+ if (client->irq)
pcf857x_irq_domain_cleanup(gpio);
return status;
@@ -396,7 +395,7 @@ fail:
static int pcf857x_remove(struct i2c_client *client)
{
- struct pcf857x_platform_data *pdata = client->dev.platform_data;
+ struct pcf857x_platform_data *pdata = dev_get_platdata(&client->dev);
struct pcf857x *gpio = i2c_get_clientdata(client);
int status = 0;
@@ -411,7 +410,7 @@ static int pcf857x_remove(struct i2c_client *client)
}
}
- if (pdata && client->irq)
+ if (client->irq)
pcf857x_irq_domain_cleanup(gpio);
status = gpiochip_remove(&gpio->chip);
diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c
index 6a4bd0dae0ce..4274e2e70ef8 100644
--- a/drivers/gpio/gpio-pl061.c
+++ b/drivers/gpio/gpio-pl061.c
@@ -259,7 +259,7 @@ static const struct irq_domain_ops pl061_domain_ops = {
static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
{
struct device *dev = &adev->dev;
- struct pl061_platform_data *pdata = dev->platform_data;
+ struct pl061_platform_data *pdata = dev_get_platdata(dev);
struct pl061_gpio *chip;
int ret, irq, i, irq_base;
diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c
index df2199dd1499..cc13d1b74fad 100644
--- a/drivers/gpio/gpio-pxa.c
+++ b/drivers/gpio/gpio-pxa.c
@@ -524,8 +524,8 @@ const struct irq_domain_ops pxa_irq_domain_ops = {
static int pxa_gpio_probe_dt(struct platform_device *pdev)
{
- int ret, nr_gpios;
- struct device_node *prev, *next, *np = pdev->dev.of_node;
+ int ret = 0, nr_gpios;
+ struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id =
of_match_device(pxa_gpio_dt_ids, &pdev->dev);
const struct pxa_gpio_id *gpio_id;
@@ -537,20 +537,13 @@ static int pxa_gpio_probe_dt(struct platform_device *pdev)
gpio_id = of_id->data;
gpio_type = gpio_id->type;
- next = of_get_next_child(np, NULL);
- prev = next;
- if (!next) {
- dev_err(&pdev->dev, "Failed to find child gpio node\n");
- ret = -EINVAL;
- goto err;
- }
- of_node_put(prev);
nr_gpios = gpio_id->gpio_nums;
pxa_last_gpio = nr_gpios - 1;
irq_base = irq_alloc_descs(-1, 0, nr_gpios, 0);
if (irq_base < 0) {
dev_err(&pdev->dev, "Failed to allocate IRQ numbers\n");
+ ret = irq_base;
goto err;
}
domain = irq_domain_add_legacy(np, nr_gpios, irq_base, 0,
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
index e8198dd68615..e3745eb07570 100644
--- a/drivers/gpio/gpio-rcar.c
+++ b/drivers/gpio/gpio-rcar.c
@@ -285,7 +285,7 @@ static struct irq_domain_ops gpio_rcar_irq_domain_ops = {
static void gpio_rcar_parse_pdata(struct gpio_rcar_priv *p)
{
- struct gpio_rcar_config *pdata = p->pdev->dev.platform_data;
+ struct gpio_rcar_config *pdata = dev_get_platdata(&p->pdev->dev);
struct device_node *np = p->pdev->dev.of_node;
struct of_phandle_args args;
int ret;
diff --git a/drivers/gpio/gpio-rdc321x.c b/drivers/gpio/gpio-rdc321x.c
index 368c3c00fca5..88577c3272a5 100644
--- a/drivers/gpio/gpio-rdc321x.c
+++ b/drivers/gpio/gpio-rdc321x.c
@@ -135,7 +135,7 @@ static int rdc321x_gpio_probe(struct platform_device *pdev)
struct rdc321x_gpio *rdc321x_gpio_dev;
struct rdc321x_gpio_pdata *pdata;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(&pdev->dev, "no platform data supplied\n");
return -ENODEV;
diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c
index a1392f47bbda..358a21c2d811 100644
--- a/drivers/gpio/gpio-samsung.c
+++ b/drivers/gpio/gpio-samsung.c
@@ -161,28 +161,6 @@ int s3c24xx_gpio_setpull_1down(struct samsung_gpio_chip *chip,
return s3c24xx_gpio_setpull_1(chip, off, pull, S3C_GPIO_PULL_DOWN);
}
-static int exynos_gpio_setpull(struct samsung_gpio_chip *chip,
- unsigned int off, samsung_gpio_pull_t pull)
-{
- if (pull == S3C_GPIO_PULL_UP)
- pull = 3;
-
- return samsung_gpio_setpull_updown(chip, off, pull);
-}
-
-static samsung_gpio_pull_t exynos_gpio_getpull(struct samsung_gpio_chip *chip,
- unsigned int off)
-{
- samsung_gpio_pull_t pull;
-
- pull = samsung_gpio_getpull_updown(chip, off);
-
- if (pull == 3)
- pull = S3C_GPIO_PULL_UP;
-
- return pull;
-}
-
/*
* samsung_gpio_setcfg_2bit - Samsung 2bit style GPIO configuration.
* @chip: The gpio chip that is being configured.
@@ -444,15 +422,6 @@ static struct samsung_gpio_cfg s3c24xx_gpiocfg_banka = {
};
#endif
-#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_SOC_EXYNOS5250)
-static struct samsung_gpio_cfg exynos_gpio_cfg = {
- .set_pull = exynos_gpio_setpull,
- .get_pull = exynos_gpio_getpull,
- .set_config = samsung_gpio_setcfg_4bit,
- .get_config = samsung_gpio_getcfg_4bit,
-};
-#endif
-
#if defined(CONFIG_CPU_S5P6440) || defined(CONFIG_CPU_S5P6450)
static struct samsung_gpio_cfg s5p64x0_gpio_cfg_rbank = {
.cfg_eint = 0x3,
@@ -495,15 +464,6 @@ static struct samsung_gpio_cfg samsung_gpio_cfgs[] = {
.set_config = samsung_gpio_setcfg_2bit,
.get_config = samsung_gpio_getcfg_2bit,
},
- [8] = {
- .set_pull = exynos_gpio_setpull,
- .get_pull = exynos_gpio_getpull,
- },
- [9] = {
- .cfg_eint = 0x3,
- .set_pull = exynos_gpio_setpull,
- .get_pull = exynos_gpio_getpull,
- }
};
/*
@@ -2115,833 +2075,6 @@ static struct samsung_gpio_chip s5pv210_gpios_4bit[] = {
#endif
};
-/*
- * Followings are the gpio banks in EXYNOS SoCs
- *
- * The 'config' member when left to NULL, is initialized to the default
- * structure exynos_gpio_cfg in the init function below.
- *
- * The 'base' member is also initialized in the init function below.
- * Note: The initialization of 'base' member of samsung_gpio_chip structure
- * uses the above macro and depends on the banks being listed in order here.
- */
-
-#ifdef CONFIG_ARCH_EXYNOS4
-static struct samsung_gpio_chip exynos4_gpios_1[] = {
- {
- .chip = {
- .base = EXYNOS4_GPA0(0),
- .ngpio = EXYNOS4_GPIO_A0_NR,
- .label = "GPA0",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPA1(0),
- .ngpio = EXYNOS4_GPIO_A1_NR,
- .label = "GPA1",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPB(0),
- .ngpio = EXYNOS4_GPIO_B_NR,
- .label = "GPB",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPC0(0),
- .ngpio = EXYNOS4_GPIO_C0_NR,
- .label = "GPC0",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPC1(0),
- .ngpio = EXYNOS4_GPIO_C1_NR,
- .label = "GPC1",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPD0(0),
- .ngpio = EXYNOS4_GPIO_D0_NR,
- .label = "GPD0",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPD1(0),
- .ngpio = EXYNOS4_GPIO_D1_NR,
- .label = "GPD1",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPE0(0),
- .ngpio = EXYNOS4_GPIO_E0_NR,
- .label = "GPE0",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPE1(0),
- .ngpio = EXYNOS4_GPIO_E1_NR,
- .label = "GPE1",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPE2(0),
- .ngpio = EXYNOS4_GPIO_E2_NR,
- .label = "GPE2",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPE3(0),
- .ngpio = EXYNOS4_GPIO_E3_NR,
- .label = "GPE3",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPE4(0),
- .ngpio = EXYNOS4_GPIO_E4_NR,
- .label = "GPE4",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPF0(0),
- .ngpio = EXYNOS4_GPIO_F0_NR,
- .label = "GPF0",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPF1(0),
- .ngpio = EXYNOS4_GPIO_F1_NR,
- .label = "GPF1",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPF2(0),
- .ngpio = EXYNOS4_GPIO_F2_NR,
- .label = "GPF2",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPF3(0),
- .ngpio = EXYNOS4_GPIO_F3_NR,
- .label = "GPF3",
- },
- },
-};
-#endif
-
-#ifdef CONFIG_ARCH_EXYNOS4
-static struct samsung_gpio_chip exynos4_gpios_2[] = {
- {
- .chip = {
- .base = EXYNOS4_GPJ0(0),
- .ngpio = EXYNOS4_GPIO_J0_NR,
- .label = "GPJ0",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPJ1(0),
- .ngpio = EXYNOS4_GPIO_J1_NR,
- .label = "GPJ1",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPK0(0),
- .ngpio = EXYNOS4_GPIO_K0_NR,
- .label = "GPK0",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPK1(0),
- .ngpio = EXYNOS4_GPIO_K1_NR,
- .label = "GPK1",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPK2(0),
- .ngpio = EXYNOS4_GPIO_K2_NR,
- .label = "GPK2",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPK3(0),
- .ngpio = EXYNOS4_GPIO_K3_NR,
- .label = "GPK3",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPL0(0),
- .ngpio = EXYNOS4_GPIO_L0_NR,
- .label = "GPL0",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPL1(0),
- .ngpio = EXYNOS4_GPIO_L1_NR,
- .label = "GPL1",
- },
- }, {
- .chip = {
- .base = EXYNOS4_GPL2(0),
- .ngpio = EXYNOS4_GPIO_L2_NR,
- .label = "GPL2",
- },
- }, {
- .config = &samsung_gpio_cfgs[8],
- .chip = {
- .base = EXYNOS4_GPY0(0),
- .ngpio = EXYNOS4_GPIO_Y0_NR,
- .label = "GPY0",
- },
- }, {
- .config = &samsung_gpio_cfgs[8],
- .chip = {
- .base = EXYNOS4_GPY1(0),
- .ngpio = EXYNOS4_GPIO_Y1_NR,
- .label = "GPY1",
- },
- }, {
- .config = &samsung_gpio_cfgs[8],
- .chip = {
- .base = EXYNOS4_GPY2(0),
- .ngpio = EXYNOS4_GPIO_Y2_NR,
- .label = "GPY2",
- },
- }, {
- .config = &samsung_gpio_cfgs[8],
- .chip = {
- .base = EXYNOS4_GPY3(0),
- .ngpio = EXYNOS4_GPIO_Y3_NR,
- .label = "GPY3",
- },
- }, {
- .config = &samsung_gpio_cfgs[8],
- .chip = {
- .base = EXYNOS4_GPY4(0),
- .ngpio = EXYNOS4_GPIO_Y4_NR,
- .label = "GPY4",
- },
- }, {
- .config = &samsung_gpio_cfgs[8],
- .chip = {
- .base = EXYNOS4_GPY5(0),
- .ngpio = EXYNOS4_GPIO_Y5_NR,
- .label = "GPY5",
- },
- }, {
- .config = &samsung_gpio_cfgs[8],
- .chip = {
- .base = EXYNOS4_GPY6(0),
- .ngpio = EXYNOS4_GPIO_Y6_NR,
- .label = "GPY6",
- },
- }, {
- .config = &samsung_gpio_cfgs[9],
- .irq_base = IRQ_EINT(0),
- .chip = {
- .base = EXYNOS4_GPX0(0),
- .ngpio = EXYNOS4_GPIO_X0_NR,
- .label = "GPX0",
- .to_irq = samsung_gpiolib_to_irq,
- },
- }, {
- .config = &samsung_gpio_cfgs[9],
- .irq_base = IRQ_EINT(8),
- .chip = {
- .base = EXYNOS4_GPX1(0),
- .ngpio = EXYNOS4_GPIO_X1_NR,
- .label = "GPX1",
- .to_irq = samsung_gpiolib_to_irq,
- },
- }, {
- .config = &samsung_gpio_cfgs[9],
- .irq_base = IRQ_EINT(16),
- .chip = {
- .base = EXYNOS4_GPX2(0),
- .ngpio = EXYNOS4_GPIO_X2_NR,
- .label = "GPX2",
- .to_irq = samsung_gpiolib_to_irq,
- },
- }, {
- .config = &samsung_gpio_cfgs[9],
- .irq_base = IRQ_EINT(24),
- .chip = {
- .base = EXYNOS4_GPX3(0),
- .ngpio = EXYNOS4_GPIO_X3_NR,
- .label = "GPX3",
- .to_irq = samsung_gpiolib_to_irq,
- },
- },
-};
-#endif
-
-#ifdef CONFIG_ARCH_EXYNOS4
-static struct samsung_gpio_chip exynos4_gpios_3[] = {
- {
- .chip = {
- .base = EXYNOS4_GPZ(0),
- .ngpio = EXYNOS4_GPIO_Z_NR,
- .label = "GPZ",
- },
- },
-};
-#endif
-
-#ifdef CONFIG_SOC_EXYNOS5250
-static struct samsung_gpio_chip exynos5_gpios_1[] = {
- {
- .chip = {
- .base = EXYNOS5_GPA0(0),
- .ngpio = EXYNOS5_GPIO_A0_NR,
- .label = "GPA0",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPA1(0),
- .ngpio = EXYNOS5_GPIO_A1_NR,
- .label = "GPA1",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPA2(0),
- .ngpio = EXYNOS5_GPIO_A2_NR,
- .label = "GPA2",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPB0(0),
- .ngpio = EXYNOS5_GPIO_B0_NR,
- .label = "GPB0",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPB1(0),
- .ngpio = EXYNOS5_GPIO_B1_NR,
- .label = "GPB1",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPB2(0),
- .ngpio = EXYNOS5_GPIO_B2_NR,
- .label = "GPB2",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPB3(0),
- .ngpio = EXYNOS5_GPIO_B3_NR,
- .label = "GPB3",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPC0(0),
- .ngpio = EXYNOS5_GPIO_C0_NR,
- .label = "GPC0",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPC1(0),
- .ngpio = EXYNOS5_GPIO_C1_NR,
- .label = "GPC1",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPC2(0),
- .ngpio = EXYNOS5_GPIO_C2_NR,
- .label = "GPC2",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPC3(0),
- .ngpio = EXYNOS5_GPIO_C3_NR,
- .label = "GPC3",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPD0(0),
- .ngpio = EXYNOS5_GPIO_D0_NR,
- .label = "GPD0",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPD1(0),
- .ngpio = EXYNOS5_GPIO_D1_NR,
- .label = "GPD1",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPY0(0),
- .ngpio = EXYNOS5_GPIO_Y0_NR,
- .label = "GPY0",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPY1(0),
- .ngpio = EXYNOS5_GPIO_Y1_NR,
- .label = "GPY1",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPY2(0),
- .ngpio = EXYNOS5_GPIO_Y2_NR,
- .label = "GPY2",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPY3(0),
- .ngpio = EXYNOS5_GPIO_Y3_NR,
- .label = "GPY3",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPY4(0),
- .ngpio = EXYNOS5_GPIO_Y4_NR,
- .label = "GPY4",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPY5(0),
- .ngpio = EXYNOS5_GPIO_Y5_NR,
- .label = "GPY5",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPY6(0),
- .ngpio = EXYNOS5_GPIO_Y6_NR,
- .label = "GPY6",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPC4(0),
- .ngpio = EXYNOS5_GPIO_C4_NR,
- .label = "GPC4",
- },
- }, {
- .config = &samsung_gpio_cfgs[9],
- .irq_base = IRQ_EINT(0),
- .chip = {
- .base = EXYNOS5_GPX0(0),
- .ngpio = EXYNOS5_GPIO_X0_NR,
- .label = "GPX0",
- .to_irq = samsung_gpiolib_to_irq,
- },
- }, {
- .config = &samsung_gpio_cfgs[9],
- .irq_base = IRQ_EINT(8),
- .chip = {
- .base = EXYNOS5_GPX1(0),
- .ngpio = EXYNOS5_GPIO_X1_NR,
- .label = "GPX1",
- .to_irq = samsung_gpiolib_to_irq,
- },
- }, {
- .config = &samsung_gpio_cfgs[9],
- .irq_base = IRQ_EINT(16),
- .chip = {
- .base = EXYNOS5_GPX2(0),
- .ngpio = EXYNOS5_GPIO_X2_NR,
- .label = "GPX2",
- .to_irq = samsung_gpiolib_to_irq,
- },
- }, {
- .config = &samsung_gpio_cfgs[9],
- .irq_base = IRQ_EINT(24),
- .chip = {
- .base = EXYNOS5_GPX3(0),
- .ngpio = EXYNOS5_GPIO_X3_NR,
- .label = "GPX3",
- .to_irq = samsung_gpiolib_to_irq,
- },
- },
-};
-#endif
-
-#ifdef CONFIG_SOC_EXYNOS5250
-static struct samsung_gpio_chip exynos5_gpios_2[] = {
- {
- .chip = {
- .base = EXYNOS5_GPE0(0),
- .ngpio = EXYNOS5_GPIO_E0_NR,
- .label = "GPE0",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPE1(0),
- .ngpio = EXYNOS5_GPIO_E1_NR,
- .label = "GPE1",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPF0(0),
- .ngpio = EXYNOS5_GPIO_F0_NR,
- .label = "GPF0",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPF1(0),
- .ngpio = EXYNOS5_GPIO_F1_NR,
- .label = "GPF1",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPG0(0),
- .ngpio = EXYNOS5_GPIO_G0_NR,
- .label = "GPG0",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPG1(0),
- .ngpio = EXYNOS5_GPIO_G1_NR,
- .label = "GPG1",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPG2(0),
- .ngpio = EXYNOS5_GPIO_G2_NR,
- .label = "GPG2",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPH0(0),
- .ngpio = EXYNOS5_GPIO_H0_NR,
- .label = "GPH0",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPH1(0),
- .ngpio = EXYNOS5_GPIO_H1_NR,
- .label = "GPH1",
-
- },
- },
-};
-#endif
-
-#ifdef CONFIG_SOC_EXYNOS5250
-static struct samsung_gpio_chip exynos5_gpios_3[] = {
- {
- .chip = {
- .base = EXYNOS5_GPV0(0),
- .ngpio = EXYNOS5_GPIO_V0_NR,
- .label = "GPV0",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPV1(0),
- .ngpio = EXYNOS5_GPIO_V1_NR,
- .label = "GPV1",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPV2(0),
- .ngpio = EXYNOS5_GPIO_V2_NR,
- .label = "GPV2",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPV3(0),
- .ngpio = EXYNOS5_GPIO_V3_NR,
- .label = "GPV3",
- },
- }, {
- .chip = {
- .base = EXYNOS5_GPV4(0),
- .ngpio = EXYNOS5_GPIO_V4_NR,
- .label = "GPV4",
- },
- },
-};
-#endif
-
-#ifdef CONFIG_SOC_EXYNOS5250
-static struct samsung_gpio_chip exynos5_gpios_4[] = {
- {
- .chip = {
- .base = EXYNOS5_GPZ(0),
- .ngpio = EXYNOS5_GPIO_Z_NR,
- .label = "GPZ",
- },
- },
-};
-#endif
-
-
-#if defined(CONFIG_ARCH_EXYNOS) && defined(CONFIG_OF)
-static int exynos_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 < 4))
- 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 (s5p_gpio_set_drvstr(pin, gpiospec->args[3]))
- pr_warn("gpio_xlate: failed to set pin drive strength\n");
-
- if (flags)
- *flags = gpiospec->args[2] >> 16;
-
- return gpiospec->args[0];
-}
-
-static const struct of_device_id exynos_gpio_dt_match[] __initdata = {
- { .compatible = "samsung,exynos4-gpio", },
- {}
-};
-
-static __init void exynos_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,
- exynos_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 = 4;
- gc->of_xlate = exynos_gpio_xlate;
-}
-#elif defined(CONFIG_ARCH_EXYNOS)
-static __init void exynos_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip,
- u64 base, u64 offset)
-{
- return;
-}
-#endif /* defined(CONFIG_ARCH_EXYNOS) && defined(CONFIG_OF) */
-
-static __init void exynos4_gpiolib_init(void)
-{
-#ifdef CONFIG_CPU_EXYNOS4210
- struct samsung_gpio_chip *chip;
- int i, nr_chips;
- void __iomem *gpio_base1, *gpio_base2, *gpio_base3;
- int group = 0;
- void __iomem *gpx_base;
-
- /* gpio part1 */
- gpio_base1 = ioremap(EXYNOS4_PA_GPIO1, SZ_4K);
- if (gpio_base1 == NULL) {
- pr_err("unable to ioremap for gpio_base1\n");
- goto err_ioremap1;
- }
-
- chip = exynos4_gpios_1;
- nr_chips = ARRAY_SIZE(exynos4_gpios_1);
-
- for (i = 0; i < nr_chips; i++, chip++) {
- if (!chip->config) {
- chip->config = &exynos_gpio_cfg;
- chip->group = group++;
- }
- exynos_gpiolib_attach_ofnode(chip,
- EXYNOS4_PA_GPIO1, i * 0x20);
- }
- samsung_gpiolib_add_4bit_chips(exynos4_gpios_1,
- nr_chips, gpio_base1);
-
- /* gpio part2 */
- gpio_base2 = ioremap(EXYNOS4_PA_GPIO2, SZ_4K);
- if (gpio_base2 == NULL) {
- pr_err("unable to ioremap for gpio_base2\n");
- goto err_ioremap2;
- }
-
- /* need to set base address for gpx */
- chip = &exynos4_gpios_2[16];
- gpx_base = gpio_base2 + 0xC00;
- for (i = 0; i < 4; i++, chip++, gpx_base += 0x20)
- chip->base = gpx_base;
-
- chip = exynos4_gpios_2;
- nr_chips = ARRAY_SIZE(exynos4_gpios_2);
-
- for (i = 0; i < nr_chips; i++, chip++) {
- if (!chip->config) {
- chip->config = &exynos_gpio_cfg;
- chip->group = group++;
- }
- exynos_gpiolib_attach_ofnode(chip,
- EXYNOS4_PA_GPIO2, i * 0x20);
- }
- samsung_gpiolib_add_4bit_chips(exynos4_gpios_2,
- nr_chips, gpio_base2);
-
- /* gpio part3 */
- gpio_base3 = ioremap(EXYNOS4_PA_GPIO3, SZ_256);
- if (gpio_base3 == NULL) {
- pr_err("unable to ioremap for gpio_base3\n");
- goto err_ioremap3;
- }
-
- chip = exynos4_gpios_3;
- nr_chips = ARRAY_SIZE(exynos4_gpios_3);
-
- for (i = 0; i < nr_chips; i++, chip++) {
- if (!chip->config) {
- chip->config = &exynos_gpio_cfg;
- chip->group = group++;
- }
- exynos_gpiolib_attach_ofnode(chip,
- EXYNOS4_PA_GPIO3, i * 0x20);
- }
- samsung_gpiolib_add_4bit_chips(exynos4_gpios_3,
- nr_chips, gpio_base3);
-
-#if defined(CONFIG_CPU_EXYNOS4210) && defined(CONFIG_S5P_GPIO_INT)
- s5p_register_gpioint_bank(IRQ_GPIO_XA, 0, IRQ_GPIO1_NR_GROUPS);
- s5p_register_gpioint_bank(IRQ_GPIO_XB, IRQ_GPIO1_NR_GROUPS, IRQ_GPIO2_NR_GROUPS);
-#endif
-
- return;
-
-err_ioremap3:
- iounmap(gpio_base2);
-err_ioremap2:
- iounmap(gpio_base1);
-err_ioremap1:
- return;
-#endif /* CONFIG_CPU_EXYNOS4210 */
-}
-
-static __init void exynos5_gpiolib_init(void)
-{
-#ifdef CONFIG_SOC_EXYNOS5250
- struct samsung_gpio_chip *chip;
- int i, nr_chips;
- void __iomem *gpio_base1, *gpio_base2, *gpio_base3, *gpio_base4;
- int group = 0;
- void __iomem *gpx_base;
-
- /* gpio part1 */
- gpio_base1 = ioremap(EXYNOS5_PA_GPIO1, SZ_4K);
- if (gpio_base1 == NULL) {
- pr_err("unable to ioremap for gpio_base1\n");
- goto err_ioremap1;
- }
-
- /* need to set base address for gpc4 */
- exynos5_gpios_1[20].base = gpio_base1 + 0x2E0;
-
- /* need to set base address for gpx */
- chip = &exynos5_gpios_1[21];
- gpx_base = gpio_base1 + 0xC00;
- for (i = 0; i < 4; i++, chip++, gpx_base += 0x20)
- chip->base = gpx_base;
-
- chip = exynos5_gpios_1;
- nr_chips = ARRAY_SIZE(exynos5_gpios_1);
-
- for (i = 0; i < nr_chips; i++, chip++) {
- if (!chip->config) {
- chip->config = &exynos_gpio_cfg;
- chip->group = group++;
- }
- exynos_gpiolib_attach_ofnode(chip,
- EXYNOS5_PA_GPIO1, i * 0x20);
- }
- samsung_gpiolib_add_4bit_chips(exynos5_gpios_1,
- nr_chips, gpio_base1);
-
- /* gpio part2 */
- gpio_base2 = ioremap(EXYNOS5_PA_GPIO2, SZ_4K);
- if (gpio_base2 == NULL) {
- pr_err("unable to ioremap for gpio_base2\n");
- goto err_ioremap2;
- }
-
- chip = exynos5_gpios_2;
- nr_chips = ARRAY_SIZE(exynos5_gpios_2);
-
- for (i = 0; i < nr_chips; i++, chip++) {
- if (!chip->config) {
- chip->config = &exynos_gpio_cfg;
- chip->group = group++;
- }
- exynos_gpiolib_attach_ofnode(chip,
- EXYNOS5_PA_GPIO2, i * 0x20);
- }
- samsung_gpiolib_add_4bit_chips(exynos5_gpios_2,
- nr_chips, gpio_base2);
-
- /* gpio part3 */
- gpio_base3 = ioremap(EXYNOS5_PA_GPIO3, SZ_4K);
- if (gpio_base3 == NULL) {
- pr_err("unable to ioremap for gpio_base3\n");
- goto err_ioremap3;
- }
-
- /* need to set base address for gpv */
- exynos5_gpios_3[0].base = gpio_base3;
- exynos5_gpios_3[1].base = gpio_base3 + 0x20;
- exynos5_gpios_3[2].base = gpio_base3 + 0x60;
- exynos5_gpios_3[3].base = gpio_base3 + 0x80;
- exynos5_gpios_3[4].base = gpio_base3 + 0xC0;
-
- chip = exynos5_gpios_3;
- nr_chips = ARRAY_SIZE(exynos5_gpios_3);
-
- for (i = 0; i < nr_chips; i++, chip++) {
- if (!chip->config) {
- chip->config = &exynos_gpio_cfg;
- chip->group = group++;
- }
- exynos_gpiolib_attach_ofnode(chip,
- EXYNOS5_PA_GPIO3, i * 0x20);
- }
- samsung_gpiolib_add_4bit_chips(exynos5_gpios_3,
- nr_chips, gpio_base3);
-
- /* gpio part4 */
- gpio_base4 = ioremap(EXYNOS5_PA_GPIO4, SZ_4K);
- if (gpio_base4 == NULL) {
- pr_err("unable to ioremap for gpio_base4\n");
- goto err_ioremap4;
- }
-
- chip = exynos5_gpios_4;
- nr_chips = ARRAY_SIZE(exynos5_gpios_4);
-
- for (i = 0; i < nr_chips; i++, chip++) {
- if (!chip->config) {
- chip->config = &exynos_gpio_cfg;
- chip->group = group++;
- }
- exynos_gpiolib_attach_ofnode(chip,
- EXYNOS5_PA_GPIO4, i * 0x20);
- }
- samsung_gpiolib_add_4bit_chips(exynos5_gpios_4,
- nr_chips, gpio_base4);
- return;
-
-err_ioremap4:
- iounmap(gpio_base3);
-err_ioremap3:
- iounmap(gpio_base2);
-err_ioremap2:
- iounmap(gpio_base1);
-err_ioremap1:
- return;
-
-#endif /* CONFIG_SOC_EXYNOS5250 */
-}
-
/* TODO: cleanup soc_is_* */
static __init int samsung_gpiolib_init(void)
{
@@ -3040,10 +2173,6 @@ static __init int samsung_gpiolib_init(void)
#if defined(CONFIG_CPU_S5PV210) && defined(CONFIG_S5P_GPIO_INT)
s5p_register_gpioint_bank(IRQ_GPIOINT, 0, S5P_GPIOINT_GROUP_MAXNR);
#endif
- } else if (soc_is_exynos4210()) {
- exynos4_gpiolib_init();
- } else if (soc_is_exynos5250()) {
- exynos5_gpiolib_init();
} else {
WARN(1, "Unknown SoC in gpio-samsung, no GPIOs added\n");
return -ENODEV;
diff --git a/drivers/gpio/gpio-spear-spics.c b/drivers/gpio/gpio-spear-spics.c
index 7a4bf7c0d98f..e9a0415834ea 100644
--- a/drivers/gpio/gpio-spear-spics.c
+++ b/drivers/gpio/gpio-spear-spics.c
@@ -128,18 +128,13 @@ static int spics_gpio_probe(struct platform_device *pdev)
struct resource *res;
int ret;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "invalid IORESOURCE_MEM\n");
- return -EBUSY;
- }
-
spics = devm_kzalloc(&pdev->dev, sizeof(*spics), GFP_KERNEL);
if (!spics) {
dev_err(&pdev->dev, "memory allocation fail\n");
return -ENOMEM;
}
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
spics->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(spics->base))
return PTR_ERR(spics->base);
diff --git a/drivers/gpio/gpio-sta2x11.c b/drivers/gpio/gpio-sta2x11.c
index f43ab6aea281..f2fb12c18da9 100644
--- a/drivers/gpio/gpio-sta2x11.c
+++ b/drivers/gpio/gpio-sta2x11.c
@@ -361,7 +361,7 @@ static int gsta_probe(struct platform_device *dev)
struct gsta_gpio *chip;
struct resource *res;
- pdev = *(struct pci_dev **)(dev->dev.platform_data);
+ pdev = *(struct pci_dev **)dev_get_platdata(&dev->dev);
gpio_pdata = dev_get_platdata(&pdev->dev);
if (gpio_pdata == NULL)
diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c
index f371732591d2..d2983e9ad6af 100644
--- a/drivers/gpio/gpio-sx150x.c
+++ b/drivers/gpio/gpio-sx150x.c
@@ -583,7 +583,7 @@ static int sx150x_probe(struct i2c_client *client,
struct sx150x_chip *chip;
int rc;
- pdata = client->dev.platform_data;
+ pdata = dev_get_platdata(&client->dev);
if (!pdata)
return -EINVAL;
diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c
index 4c65f8883204..7a0e956ef1ed 100644
--- a/drivers/gpio/gpio-timberdale.c
+++ b/drivers/gpio/gpio-timberdale.c
@@ -227,7 +227,7 @@ static int timbgpio_probe(struct platform_device *pdev)
struct gpio_chip *gc;
struct timbgpio *tgpio;
struct resource *iomem;
- struct timbgpio_platform_data *pdata = pdev->dev.platform_data;
+ struct timbgpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
int irq = platform_get_irq(pdev, 0);
if (!pdata || pdata->nr_pins > 32) {
@@ -318,7 +318,7 @@ err_mem:
static int timbgpio_remove(struct platform_device *pdev)
{
int err;
- struct timbgpio_platform_data *pdata = pdev->dev.platform_data;
+ struct timbgpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct timbgpio *tgpio = platform_get_drvdata(pdev);
struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
int irq = platform_get_irq(pdev, 0);
diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c
index 30a5844a7dca..276a4229b032 100644
--- a/drivers/gpio/gpio-tps65912.c
+++ b/drivers/gpio/gpio-tps65912.c
@@ -87,7 +87,7 @@ static struct gpio_chip template_chip = {
static int tps65912_gpio_probe(struct platform_device *pdev)
{
struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent);
- struct tps65912_board *pdata = tps65912->dev->platform_data;
+ struct tps65912_board *pdata = dev_get_platdata(tps65912->dev);
struct tps65912_gpio_data *tps65912_gpio;
int ret;
diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c
index cc53cab8df2a..3df3ebdb3e52 100644
--- a/drivers/gpio/gpio-ts5500.c
+++ b/drivers/gpio/gpio-ts5500.c
@@ -322,7 +322,7 @@ static void ts5500_disable_irq(struct ts5500_priv *priv)
static int ts5500_dio_probe(struct platform_device *pdev)
{
enum ts5500_blocks block = platform_get_device_id(pdev)->driver_data;
- struct ts5500_dio_platform_data *pdata = pdev->dev.platform_data;
+ struct ts5500_dio_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct device *dev = &pdev->dev;
const char *name = dev_name(dev);
struct ts5500_priv *priv;
diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c
index 4d330e36da1d..d8e4f6efcb29 100644
--- a/drivers/gpio/gpio-twl4030.c
+++ b/drivers/gpio/gpio-twl4030.c
@@ -256,7 +256,7 @@ static int twl_request(struct gpio_chip *chip, unsigned offset)
/* optionally have the first two GPIOs switch vMMC1
* and vMMC2 power supplies based on card presence.
*/
- pdata = chip->dev->platform_data;
+ pdata = dev_get_platdata(chip->dev);
if (pdata)
value |= pdata->mmc_cd & 0x03;
@@ -460,7 +460,7 @@ static struct twl4030_gpio_platform_data *of_gpio_twl4030(struct device *dev)
static int gpio_twl4030_probe(struct platform_device *pdev)
{
- struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data;
+ struct twl4030_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct device_node *node = pdev->dev.of_node;
struct gpio_twl4030_priv *priv;
int ret, irq_base;
@@ -556,7 +556,7 @@ out:
/* Cannot use as gpio_twl4030_probe() calls us */
static int gpio_twl4030_remove(struct platform_device *pdev)
{
- struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data;
+ struct twl4030_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct gpio_twl4030_priv *priv = platform_get_drvdata(pdev);
int status;
diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c
index 0be82c6dd796..d420d30b86e7 100644
--- a/drivers/gpio/gpio-twl6040.c
+++ b/drivers/gpio/gpio-twl6040.c
@@ -84,15 +84,11 @@ static struct gpio_chip twl6040gpo_chip = {
static int 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;
+ twl6040gpo_chip.base = -1;
if (twl6040_get_revid(twl6040) < TWL6041_REV_ES2_0)
twl6040gpo_chip.ngpio = 3; /* twl6040 have 3 GPO */
diff --git a/drivers/gpio/gpio-tz1090-pdc.c b/drivers/gpio/gpio-tz1090-pdc.c
new file mode 100644
index 000000000000..f512da299b3d
--- /dev/null
+++ b/drivers/gpio/gpio-tz1090-pdc.c
@@ -0,0 +1,243 @@
+/*
+ * Toumaz Xenif TZ1090 PDC GPIO handling.
+ *
+ * Copyright (C) 2012-2013 Imagination Technologies Ltd.
+ *
+ * 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/bitops.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/syscore_ops.h>
+#include <asm/global_lock.h>
+
+/* Register offsets from SOC_GPIO_CONTROL0 */
+#define REG_SOC_GPIO_CONTROL0 0x00
+#define REG_SOC_GPIO_CONTROL1 0x04
+#define REG_SOC_GPIO_CONTROL2 0x08
+#define REG_SOC_GPIO_CONTROL3 0x0c
+#define REG_SOC_GPIO_STATUS 0x80
+
+/* PDC GPIOs go after normal GPIOs */
+#define GPIO_PDC_BASE 90
+#define GPIO_PDC_NGPIO 7
+
+/* Out of PDC gpios, only syswakes have irqs */
+#define GPIO_PDC_IRQ_FIRST 2
+#define GPIO_PDC_NIRQ 3
+
+/**
+ * struct tz1090_pdc_gpio - GPIO bank private data
+ * @chip: Generic GPIO chip for GPIO bank
+ * @reg: Base of registers, offset for this GPIO bank
+ * @irq: IRQ numbers for Syswake GPIOs
+ *
+ * This is the main private data for the PDC GPIO driver. It encapsulates a
+ * gpio_chip, and the callbacks for the gpio_chip can access the private data
+ * with the to_pdc() macro below.
+ */
+struct tz1090_pdc_gpio {
+ struct gpio_chip chip;
+ void __iomem *reg;
+ int irq[GPIO_PDC_NIRQ];
+};
+#define to_pdc(c) container_of(c, struct tz1090_pdc_gpio, chip)
+
+/* Register accesses into the PDC MMIO area */
+
+static inline void pdc_write(struct tz1090_pdc_gpio *priv, unsigned int reg_offs,
+ unsigned int data)
+{
+ writel(data, priv->reg + reg_offs);
+}
+
+static inline unsigned int pdc_read(struct tz1090_pdc_gpio *priv,
+ unsigned int reg_offs)
+{
+ return readl(priv->reg + reg_offs);
+}
+
+/* Generic GPIO interface */
+
+static int tz1090_pdc_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct tz1090_pdc_gpio *priv = to_pdc(chip);
+ u32 value;
+ int lstat;
+
+ __global_lock2(lstat);
+ value = pdc_read(priv, REG_SOC_GPIO_CONTROL1);
+ value |= BIT(offset);
+ pdc_write(priv, REG_SOC_GPIO_CONTROL1, value);
+ __global_unlock2(lstat);
+
+ return 0;
+}
+
+static int tz1090_pdc_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset,
+ int output_value)
+{
+ struct tz1090_pdc_gpio *priv = to_pdc(chip);
+ u32 value;
+ int lstat;
+
+ __global_lock2(lstat);
+ /* EXT_POWER doesn't seem to have an output value bit */
+ if (offset < 6) {
+ value = pdc_read(priv, REG_SOC_GPIO_CONTROL0);
+ if (output_value)
+ value |= BIT(offset);
+ else
+ value &= ~BIT(offset);
+ pdc_write(priv, REG_SOC_GPIO_CONTROL0, value);
+ }
+
+ value = pdc_read(priv, REG_SOC_GPIO_CONTROL1);
+ value &= ~BIT(offset);
+ pdc_write(priv, REG_SOC_GPIO_CONTROL1, value);
+ __global_unlock2(lstat);
+
+ return 0;
+}
+
+static int tz1090_pdc_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct tz1090_pdc_gpio *priv = to_pdc(chip);
+ return pdc_read(priv, REG_SOC_GPIO_STATUS) & BIT(offset);
+}
+
+static void tz1090_pdc_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int output_value)
+{
+ struct tz1090_pdc_gpio *priv = to_pdc(chip);
+ u32 value;
+ int lstat;
+
+ /* EXT_POWER doesn't seem to have an output value bit */
+ if (offset >= 6)
+ return;
+
+ __global_lock2(lstat);
+ value = pdc_read(priv, REG_SOC_GPIO_CONTROL0);
+ if (output_value)
+ value |= BIT(offset);
+ else
+ value &= ~BIT(offset);
+ pdc_write(priv, REG_SOC_GPIO_CONTROL0, value);
+ __global_unlock2(lstat);
+}
+
+static int tz1090_pdc_gpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+ return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void tz1090_pdc_gpio_free(struct gpio_chip *chip, unsigned int offset)
+{
+ pinctrl_free_gpio(chip->base + offset);
+}
+
+static int tz1090_pdc_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
+{
+ struct tz1090_pdc_gpio *priv = to_pdc(chip);
+ unsigned int syswake = offset - GPIO_PDC_IRQ_FIRST;
+ int irq;
+
+ /* only syswakes have irqs */
+ if (syswake >= GPIO_PDC_NIRQ)
+ return -EINVAL;
+
+ irq = priv->irq[syswake];
+ if (!irq)
+ return -EINVAL;
+
+ return irq;
+}
+
+static int tz1090_pdc_gpio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res_regs;
+ struct tz1090_pdc_gpio *priv;
+ unsigned int i;
+
+ if (!np) {
+ dev_err(&pdev->dev, "must be instantiated via devicetree\n");
+ return -ENOENT;
+ }
+
+ res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res_regs) {
+ dev_err(&pdev->dev, "cannot find registers resource\n");
+ return -ENOENT;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&pdev->dev, "unable to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ /* Ioremap the registers */
+ priv->reg = devm_ioremap(&pdev->dev, res_regs->start,
+ res_regs->end - res_regs->start);
+ if (!priv->reg) {
+ dev_err(&pdev->dev, "unable to ioremap registers\n");
+ return -ENOMEM;
+ }
+
+ /* Set up GPIO chip */
+ priv->chip.label = "tz1090-pdc-gpio";
+ priv->chip.dev = &pdev->dev;
+ priv->chip.direction_input = tz1090_pdc_gpio_direction_input;
+ priv->chip.direction_output = tz1090_pdc_gpio_direction_output;
+ priv->chip.get = tz1090_pdc_gpio_get;
+ priv->chip.set = tz1090_pdc_gpio_set;
+ priv->chip.free = tz1090_pdc_gpio_free;
+ priv->chip.request = tz1090_pdc_gpio_request;
+ priv->chip.to_irq = tz1090_pdc_gpio_to_irq;
+ priv->chip.of_node = np;
+
+ /* GPIO numbering */
+ priv->chip.base = GPIO_PDC_BASE;
+ priv->chip.ngpio = GPIO_PDC_NGPIO;
+
+ /* Map the syswake irqs */
+ for (i = 0; i < GPIO_PDC_NIRQ; ++i)
+ priv->irq[i] = irq_of_parse_and_map(np, i);
+
+ /* Add the GPIO bank */
+ gpiochip_add(&priv->chip);
+
+ return 0;
+}
+
+static struct of_device_id tz1090_pdc_gpio_of_match[] = {
+ { .compatible = "img,tz1090-pdc-gpio" },
+ { },
+};
+
+static struct platform_driver tz1090_pdc_gpio_driver = {
+ .driver = {
+ .name = "tz1090-pdc-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = tz1090_pdc_gpio_of_match,
+ },
+ .probe = tz1090_pdc_gpio_probe,
+};
+
+static int __init tz1090_pdc_gpio_init(void)
+{
+ return platform_driver_register(&tz1090_pdc_gpio_driver);
+}
+subsys_initcall(tz1090_pdc_gpio_init);
diff --git a/drivers/gpio/gpio-tz1090.c b/drivers/gpio/gpio-tz1090.c
new file mode 100644
index 000000000000..23e061392411
--- /dev/null
+++ b/drivers/gpio/gpio-tz1090.c
@@ -0,0 +1,606 @@
+/*
+ * Toumaz Xenif TZ1090 GPIO handling.
+ *
+ * Copyright (C) 2008-2013 Imagination Technologies Ltd.
+ *
+ * Based on ARM PXA code and others.
+ *
+ * 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/bitops.h>
+#include <linux/export.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/syscore_ops.h>
+#include <asm/global_lock.h>
+
+/* Register offsets from bank base address */
+#define REG_GPIO_DIR 0x00
+#define REG_GPIO_IRQ_PLRT 0x20
+#define REG_GPIO_IRQ_TYPE 0x30
+#define REG_GPIO_IRQ_EN 0x40
+#define REG_GPIO_IRQ_STS 0x50
+#define REG_GPIO_BIT_EN 0x60
+#define REG_GPIO_DIN 0x70
+#define REG_GPIO_DOUT 0x80
+
+/* REG_GPIO_IRQ_PLRT */
+#define REG_GPIO_IRQ_PLRT_LOW 0
+#define REG_GPIO_IRQ_PLRT_HIGH 1
+
+/* REG_GPIO_IRQ_TYPE */
+#define REG_GPIO_IRQ_TYPE_LEVEL 0
+#define REG_GPIO_IRQ_TYPE_EDGE 1
+
+/**
+ * struct tz1090_gpio_bank - GPIO bank private data
+ * @chip: Generic GPIO chip for GPIO bank
+ * @domain: IRQ domain for GPIO bank (may be NULL)
+ * @reg: Base of registers, offset for this GPIO bank
+ * @irq: IRQ number for GPIO bank
+ * @label: Debug GPIO bank label, used for storage of chip->label
+ *
+ * This is the main private data for a GPIO bank. It encapsulates a gpio_chip,
+ * and the callbacks for the gpio_chip can access the private data with the
+ * to_bank() macro below.
+ */
+struct tz1090_gpio_bank {
+ struct gpio_chip chip;
+ struct irq_domain *domain;
+ void __iomem *reg;
+ int irq;
+ char label[16];
+};
+#define to_bank(c) container_of(c, struct tz1090_gpio_bank, chip)
+
+/**
+ * struct tz1090_gpio - Overall GPIO device private data
+ * @dev: Device (from platform device)
+ * @reg: Base of GPIO registers
+ *
+ * Represents the overall GPIO device. This structure is actually only
+ * temporary, and used during init.
+ */
+struct tz1090_gpio {
+ struct device *dev;
+ void __iomem *reg;
+};
+
+/**
+ * struct tz1090_gpio_bank_info - Temporary registration info for GPIO bank
+ * @priv: Overall GPIO device private data
+ * @node: Device tree node specific to this GPIO bank
+ * @index: Index of bank in range 0-2
+ */
+struct tz1090_gpio_bank_info {
+ struct tz1090_gpio *priv;
+ struct device_node *node;
+ unsigned int index;
+};
+
+/* Convenience register accessors */
+static inline void tz1090_gpio_write(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs, u32 data)
+{
+ iowrite32(data, bank->reg + reg_offs);
+}
+
+static inline u32 tz1090_gpio_read(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs)
+{
+ return ioread32(bank->reg + reg_offs);
+}
+
+/* caller must hold LOCK2 */
+static inline void _tz1090_gpio_clear_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset)
+{
+ u32 value;
+
+ value = tz1090_gpio_read(bank, reg_offs);
+ value &= ~BIT(offset);
+ tz1090_gpio_write(bank, reg_offs, value);
+}
+
+static void tz1090_gpio_clear_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset)
+{
+ int lstat;
+
+ __global_lock2(lstat);
+ _tz1090_gpio_clear_bit(bank, reg_offs, offset);
+ __global_unlock2(lstat);
+}
+
+/* caller must hold LOCK2 */
+static inline void _tz1090_gpio_set_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset)
+{
+ u32 value;
+
+ value = tz1090_gpio_read(bank, reg_offs);
+ value |= BIT(offset);
+ tz1090_gpio_write(bank, reg_offs, value);
+}
+
+static void tz1090_gpio_set_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset)
+{
+ int lstat;
+
+ __global_lock2(lstat);
+ _tz1090_gpio_set_bit(bank, reg_offs, offset);
+ __global_unlock2(lstat);
+}
+
+/* caller must hold LOCK2 */
+static inline void _tz1090_gpio_mod_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset,
+ bool val)
+{
+ u32 value;
+
+ value = tz1090_gpio_read(bank, reg_offs);
+ value &= ~BIT(offset);
+ if (val)
+ value |= BIT(offset);
+ tz1090_gpio_write(bank, reg_offs, value);
+}
+
+static void tz1090_gpio_mod_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset,
+ bool val)
+{
+ int lstat;
+
+ __global_lock2(lstat);
+ _tz1090_gpio_mod_bit(bank, reg_offs, offset, val);
+ __global_unlock2(lstat);
+}
+
+static inline int tz1090_gpio_read_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset)
+{
+ return tz1090_gpio_read(bank, reg_offs) & BIT(offset);
+}
+
+/* GPIO chip callbacks */
+
+static int tz1090_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+ tz1090_gpio_set_bit(bank, REG_GPIO_DIR, offset);
+
+ return 0;
+}
+
+static int tz1090_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int output_value)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+ int lstat;
+
+ __global_lock2(lstat);
+ _tz1090_gpio_mod_bit(bank, REG_GPIO_DOUT, offset, output_value);
+ _tz1090_gpio_clear_bit(bank, REG_GPIO_DIR, offset);
+ __global_unlock2(lstat);
+
+ return 0;
+}
+
+/*
+ * Return GPIO level
+ */
+static int tz1090_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+
+ return tz1090_gpio_read_bit(bank, REG_GPIO_DIN, offset);
+}
+
+/*
+ * Set output GPIO level
+ */
+static void tz1090_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int output_value)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+
+ tz1090_gpio_mod_bit(bank, REG_GPIO_DOUT, offset, output_value);
+}
+
+static int tz1090_gpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+ int ret;
+
+ ret = pinctrl_request_gpio(chip->base + offset);
+ if (ret)
+ return ret;
+
+ tz1090_gpio_set_bit(bank, REG_GPIO_DIR, offset);
+ tz1090_gpio_set_bit(bank, REG_GPIO_BIT_EN, offset);
+
+ return 0;
+}
+
+static void tz1090_gpio_free(struct gpio_chip *chip, unsigned int offset)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+
+ pinctrl_free_gpio(chip->base + offset);
+
+ tz1090_gpio_clear_bit(bank, REG_GPIO_BIT_EN, offset);
+}
+
+static int tz1090_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+
+ if (!bank->domain)
+ return -EINVAL;
+
+ return irq_create_mapping(bank->domain, offset);
+}
+
+/* IRQ chip handlers */
+
+/* Get TZ1090 GPIO chip from irq data provided to generic IRQ callbacks */
+static inline struct tz1090_gpio_bank *irqd_to_gpio_bank(struct irq_data *data)
+{
+ return (struct tz1090_gpio_bank *)data->domain->host_data;
+}
+
+static void tz1090_gpio_irq_polarity(struct tz1090_gpio_bank *bank,
+ unsigned int offset, unsigned int polarity)
+{
+ tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_PLRT, offset, polarity);
+}
+
+static void tz1090_gpio_irq_type(struct tz1090_gpio_bank *bank,
+ unsigned int offset, unsigned int type)
+{
+ tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_TYPE, offset, type);
+}
+
+/* set polarity to trigger on next edge, whether rising or falling */
+static void tz1090_gpio_irq_next_edge(struct tz1090_gpio_bank *bank,
+ unsigned int offset)
+{
+ unsigned int value_p, value_i;
+ int lstat;
+
+ /*
+ * Set the GPIO's interrupt polarity to the opposite of the current
+ * input value so that the next edge triggers an interrupt.
+ */
+ __global_lock2(lstat);
+ value_i = ~tz1090_gpio_read(bank, REG_GPIO_DIN);
+ value_p = tz1090_gpio_read(bank, REG_GPIO_IRQ_PLRT);
+ value_p &= ~BIT(offset);
+ value_p |= value_i & BIT(offset);
+ tz1090_gpio_write(bank, REG_GPIO_IRQ_PLRT, value_p);
+ __global_unlock2(lstat);
+}
+
+static unsigned int gpio_startup_irq(struct irq_data *data)
+{
+ /*
+ * This warning indicates that the type of the irq hasn't been set
+ * before enabling the irq. This would normally be done by passing some
+ * trigger flags to request_irq().
+ */
+ WARN(irqd_get_trigger_type(data) == IRQ_TYPE_NONE,
+ "irq type not set before enabling gpio irq %d", data->irq);
+
+ irq_gc_ack_clr_bit(data);
+ irq_gc_mask_set_bit(data);
+ return 0;
+}
+
+static int gpio_set_irq_type(struct irq_data *data, unsigned int flow_type)
+{
+ struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
+ unsigned int type;
+ unsigned int polarity;
+
+ switch (flow_type) {
+ case IRQ_TYPE_EDGE_BOTH:
+ type = REG_GPIO_IRQ_TYPE_EDGE;
+ polarity = REG_GPIO_IRQ_PLRT_LOW;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ type = REG_GPIO_IRQ_TYPE_EDGE;
+ polarity = REG_GPIO_IRQ_PLRT_HIGH;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ type = REG_GPIO_IRQ_TYPE_EDGE;
+ polarity = REG_GPIO_IRQ_PLRT_LOW;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ type = REG_GPIO_IRQ_TYPE_LEVEL;
+ polarity = REG_GPIO_IRQ_PLRT_HIGH;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ type = REG_GPIO_IRQ_TYPE_LEVEL;
+ polarity = REG_GPIO_IRQ_PLRT_LOW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tz1090_gpio_irq_type(bank, data->hwirq, type);
+ irq_setup_alt_chip(data, flow_type);
+
+ if (flow_type == IRQ_TYPE_EDGE_BOTH)
+ tz1090_gpio_irq_next_edge(bank, data->hwirq);
+ else
+ tz1090_gpio_irq_polarity(bank, data->hwirq, polarity);
+
+ return 0;
+}
+
+#ifdef CONFIG_SUSPEND
+static int gpio_set_irq_wake(struct irq_data *data, unsigned int on)
+{
+ struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
+
+#ifdef CONFIG_PM_DEBUG
+ pr_info("irq_wake irq%d state:%d\n", data->irq, on);
+#endif
+
+ /* wake on gpio block interrupt */
+ return irq_set_irq_wake(bank->irq, on);
+}
+#else
+#define gpio_set_irq_wake NULL
+#endif
+
+static void tz1090_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ irq_hw_number_t hw;
+ unsigned int irq_stat, irq_no;
+ struct tz1090_gpio_bank *bank;
+ struct irq_desc *child_desc;
+
+ bank = (struct tz1090_gpio_bank *)irq_desc_get_handler_data(desc);
+ irq_stat = tz1090_gpio_read(bank, REG_GPIO_DIR) &
+ tz1090_gpio_read(bank, REG_GPIO_IRQ_STS) &
+ tz1090_gpio_read(bank, REG_GPIO_IRQ_EN) &
+ 0x3FFFFFFF; /* 30 bits only */
+
+ for (hw = 0; irq_stat; irq_stat >>= 1, ++hw) {
+ if (!(irq_stat & 1))
+ continue;
+
+ irq_no = irq_linear_revmap(bank->domain, hw);
+ child_desc = irq_to_desc(irq_no);
+
+ /* Toggle edge for pin with both edges triggering enabled */
+ if (irqd_get_trigger_type(&child_desc->irq_data)
+ == IRQ_TYPE_EDGE_BOTH)
+ tz1090_gpio_irq_next_edge(bank, hw);
+
+ generic_handle_irq_desc(irq_no, child_desc);
+ }
+}
+
+static int tz1090_gpio_bank_probe(struct tz1090_gpio_bank_info *info)
+{
+ struct device_node *np = info->node;
+ struct device *dev = info->priv->dev;
+ struct tz1090_gpio_bank *bank;
+ struct irq_chip_generic *gc;
+ int err;
+
+ bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL);
+ if (!bank) {
+ dev_err(dev, "unable to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ /* Offset the main registers to the first register in this bank */
+ bank->reg = info->priv->reg + info->index * 4;
+
+ /* Set up GPIO chip */
+ snprintf(bank->label, sizeof(bank->label), "tz1090-gpio-%u",
+ info->index);
+ bank->chip.label = bank->label;
+ bank->chip.dev = dev;
+ bank->chip.direction_input = tz1090_gpio_direction_input;
+ bank->chip.direction_output = tz1090_gpio_direction_output;
+ bank->chip.get = tz1090_gpio_get;
+ bank->chip.set = tz1090_gpio_set;
+ bank->chip.free = tz1090_gpio_free;
+ bank->chip.request = tz1090_gpio_request;
+ bank->chip.to_irq = tz1090_gpio_to_irq;
+ bank->chip.of_node = np;
+
+ /* GPIO numbering from 0 */
+ bank->chip.base = info->index * 30;
+ bank->chip.ngpio = 30;
+
+ /* Add the GPIO bank */
+ gpiochip_add(&bank->chip);
+
+ /* Get the GPIO bank IRQ if provided */
+ bank->irq = irq_of_parse_and_map(np, 0);
+
+ /* The interrupt is optional (it may be used by another core on chip) */
+ if (bank->irq < 0) {
+ dev_info(dev, "IRQ not provided for bank %u, IRQs disabled\n",
+ info->index);
+ return 0;
+ }
+
+ dev_info(dev, "Setting up IRQs for GPIO bank %u\n",
+ info->index);
+
+ /*
+ * Initialise all interrupts to disabled so we don't get
+ * spurious ones on a dirty boot and hit the BUG_ON in the
+ * handler.
+ */
+ tz1090_gpio_write(bank, REG_GPIO_IRQ_EN, 0);
+
+ /* Add a virtual IRQ for each GPIO */
+ bank->domain = irq_domain_add_linear(np,
+ bank->chip.ngpio,
+ &irq_generic_chip_ops,
+ bank);
+
+ /* Set up a generic irq chip with 2 chip types (level and edge) */
+ err = irq_alloc_domain_generic_chips(bank->domain, bank->chip.ngpio, 2,
+ bank->label, handle_bad_irq, 0, 0,
+ IRQ_GC_INIT_NESTED_LOCK);
+ if (err) {
+ dev_info(dev,
+ "irq_alloc_domain_generic_chips failed for bank %u, IRQs disabled\n",
+ info->index);
+ irq_domain_remove(bank->domain);
+ return 0;
+ }
+
+ gc = irq_get_domain_generic_chip(bank->domain, 0);
+ gc->reg_base = bank->reg;
+
+ /* level chip type */
+ gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK;
+ gc->chip_types[0].handler = handle_level_irq;
+ gc->chip_types[0].regs.ack = REG_GPIO_IRQ_STS;
+ gc->chip_types[0].regs.mask = REG_GPIO_IRQ_EN;
+ gc->chip_types[0].chip.irq_startup = gpio_startup_irq,
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_clr_bit,
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit,
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit,
+ gc->chip_types[0].chip.irq_set_type = gpio_set_irq_type,
+ gc->chip_types[0].chip.irq_set_wake = gpio_set_irq_wake,
+ gc->chip_types[0].chip.flags = IRQCHIP_MASK_ON_SUSPEND,
+
+ /* edge chip type */
+ gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH;
+ gc->chip_types[1].handler = handle_edge_irq;
+ gc->chip_types[1].regs.ack = REG_GPIO_IRQ_STS;
+ gc->chip_types[1].regs.mask = REG_GPIO_IRQ_EN;
+ gc->chip_types[1].chip.irq_startup = gpio_startup_irq,
+ gc->chip_types[1].chip.irq_ack = irq_gc_ack_clr_bit,
+ gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit,
+ gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit,
+ gc->chip_types[1].chip.irq_set_type = gpio_set_irq_type,
+ gc->chip_types[1].chip.irq_set_wake = gpio_set_irq_wake,
+ gc->chip_types[1].chip.flags = IRQCHIP_MASK_ON_SUSPEND,
+
+ /* Setup chained handler for this GPIO bank */
+ irq_set_handler_data(bank->irq, bank);
+ irq_set_chained_handler(bank->irq, tz1090_gpio_irq_handler);
+
+ return 0;
+}
+
+static void tz1090_gpio_register_banks(struct tz1090_gpio *priv)
+{
+ struct device_node *np = priv->dev->of_node;
+ struct device_node *node;
+
+ for_each_available_child_of_node(np, node) {
+ struct tz1090_gpio_bank_info info;
+ u32 addr;
+ int ret;
+
+ ret = of_property_read_u32(node, "reg", &addr);
+ if (ret) {
+ dev_err(priv->dev, "invalid reg on %s\n",
+ node->full_name);
+ continue;
+ }
+ if (addr >= 3) {
+ dev_err(priv->dev, "index %u in %s out of range\n",
+ addr, node->full_name);
+ continue;
+ }
+
+ info.index = addr;
+ info.node = of_node_get(node);
+ info.priv = priv;
+
+ ret = tz1090_gpio_bank_probe(&info);
+ if (ret) {
+ dev_err(priv->dev, "failure registering %s\n",
+ node->full_name);
+ of_node_put(node);
+ continue;
+ }
+ }
+}
+
+static int tz1090_gpio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res_regs;
+ struct tz1090_gpio priv;
+
+ if (!np) {
+ dev_err(&pdev->dev, "must be instantiated via devicetree\n");
+ return -ENOENT;
+ }
+
+ res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res_regs) {
+ dev_err(&pdev->dev, "cannot find registers resource\n");
+ return -ENOENT;
+ }
+
+ priv.dev = &pdev->dev;
+
+ /* Ioremap the registers */
+ priv.reg = devm_ioremap(&pdev->dev, res_regs->start,
+ res_regs->end - res_regs->start);
+ if (!priv.reg) {
+ dev_err(&pdev->dev, "unable to ioremap registers\n");
+ return -ENOMEM;
+ }
+
+ /* Look for banks */
+ tz1090_gpio_register_banks(&priv);
+
+ return 0;
+}
+
+static struct of_device_id tz1090_gpio_of_match[] = {
+ { .compatible = "img,tz1090-gpio" },
+ { },
+};
+
+static struct platform_driver tz1090_gpio_driver = {
+ .driver = {
+ .name = "tz1090-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = tz1090_gpio_of_match,
+ },
+ .probe = tz1090_gpio_probe,
+};
+
+static int __init tz1090_gpio_init(void)
+{
+ return platform_driver_register(&tz1090_gpio_driver);
+}
+subsys_initcall(tz1090_gpio_init);
diff --git a/drivers/gpio/gpio-ucb1400.c b/drivers/gpio/gpio-ucb1400.c
index 6d0feb234d3c..1a605f2a0f55 100644
--- a/drivers/gpio/gpio-ucb1400.c
+++ b/drivers/gpio/gpio-ucb1400.c
@@ -45,7 +45,7 @@ static void ucb1400_gpio_set(struct gpio_chip *gc, unsigned off, int val)
static int ucb1400_gpio_probe(struct platform_device *dev)
{
- struct ucb1400_gpio *ucb = dev->dev.platform_data;
+ struct ucb1400_gpio *ucb = dev_get_platdata(&dev->dev);
int err = 0;
if (!(ucb && ucb->gpio_offset)) {
diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c
index 2a743e10ecb6..456000c5c457 100644
--- a/drivers/gpio/gpio-wm831x.c
+++ b/drivers/gpio/gpio-wm831x.c
@@ -246,7 +246,7 @@ static struct gpio_chip template_chip = {
static int wm831x_gpio_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
- struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
struct wm831x_gpio *wm831x_gpio;
int ret;
diff --git a/drivers/gpio/gpio-wm8350.c b/drivers/gpio/gpio-wm8350.c
index 0b598cf3df9d..fc49154be7b1 100644
--- a/drivers/gpio/gpio-wm8350.c
+++ b/drivers/gpio/gpio-wm8350.c
@@ -112,7 +112,7 @@ static struct gpio_chip template_chip = {
static int wm8350_gpio_probe(struct platform_device *pdev)
{
struct wm8350 *wm8350 = dev_get_drvdata(pdev->dev.parent);
- struct wm8350_platform_data *pdata = wm8350->dev->platform_data;
+ struct wm8350_platform_data *pdata = dev_get_platdata(wm8350->dev);
struct wm8350_gpio_data *wm8350_gpio;
int ret;
diff --git a/drivers/gpio/gpio-wm8994.c b/drivers/gpio/gpio-wm8994.c
index ae409fd94af7..a53dbdefc7ee 100644
--- a/drivers/gpio/gpio-wm8994.c
+++ b/drivers/gpio/gpio-wm8994.c
@@ -248,7 +248,7 @@ static struct gpio_chip template_chip = {
static int wm8994_gpio_probe(struct platform_device *pdev)
{
struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent);
- struct wm8994_pdata *pdata = wm8994->dev->platform_data;
+ struct wm8994_pdata *pdata = dev_get_platdata(wm8994->dev);
struct wm8994_gpio *wm8994_gpio;
int ret;
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 665f9530c950..ba9876ffb017 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -76,7 +76,8 @@ int of_get_named_gpio_flags(struct device_node *np, const char *propname,
ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index,
&gg_data.gpiospec);
if (ret) {
- pr_debug("%s: can't parse gpios property\n", __func__);
+ pr_debug("%s: can't parse gpios property of node '%s[%d]'\n",
+ __func__, np->full_name, index);
return ret;
}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index ff0fd655729f..86ef3461ec06 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -349,7 +349,7 @@ static ssize_t gpio_value_store(struct device *dev,
else {
long value;
- status = strict_strtol(buf, 0, &value);
+ status = kstrtol(buf, 0, &value);
if (status == 0) {
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
@@ -570,7 +570,7 @@ static ssize_t gpio_active_low_store(struct device *dev,
} else {
long value;
- status = strict_strtol(buf, 0, &value);
+ status = kstrtol(buf, 0, &value);
if (status == 0)
status = sysfs_set_active_low(desc, dev, value != 0);
}
@@ -652,7 +652,7 @@ static ssize_t export_store(struct class *class,
struct gpio_desc *desc;
int status;
- status = strict_strtol(buf, 0, &gpio);
+ status = kstrtol(buf, 0, &gpio);
if (status < 0)
goto done;
@@ -694,7 +694,7 @@ static ssize_t unexport_store(struct class *class,
struct gpio_desc *desc;
int status;
- status = strict_strtol(buf, 0, &gpio);
+ status = kstrtol(buf, 0, &gpio);
if (status < 0)
goto done;
@@ -1398,7 +1398,7 @@ static int gpiod_request(struct gpio_desc *desc, const char *label)
int status = -EPROBE_DEFER;
unsigned long flags;
- if (!desc) {
+ if (!desc || !desc->chip) {
pr_warn("%s: invalid GPIO\n", __func__);
return -EINVAL;
}
@@ -1406,8 +1406,6 @@ static int gpiod_request(struct gpio_desc *desc, const char *label)
spin_lock_irqsave(&gpio_lock, flags);
chip = desc->chip;
- if (chip == NULL)
- goto done;
if (!try_module_get(chip->owner))
goto done;
@@ -1630,16 +1628,20 @@ static int gpiod_direction_input(struct gpio_desc *desc)
int status = -EINVAL;
int offset;
- if (!desc) {
+ if (!desc || !desc->chip) {
pr_warn("%s: invalid GPIO\n", __func__);
return -EINVAL;
}
+ chip = desc->chip;
+ if (!chip->get || !chip->direction_input) {
+ pr_warn("%s: missing get() or direction_input() operations\n",
+ __func__);
+ return -EIO;
+ }
+
spin_lock_irqsave(&gpio_lock, flags);
- chip = desc->chip;
- if (!chip || !chip->get || !chip->direction_input)
- goto fail;
status = gpio_ensure_requested(desc);
if (status < 0)
goto fail;
@@ -1691,7 +1693,7 @@ static int gpiod_direction_output(struct gpio_desc *desc, int value)
int status = -EINVAL;
int offset;
- if (!desc) {
+ if (!desc || !desc->chip) {
pr_warn("%s: invalid GPIO\n", __func__);
return -EINVAL;
}
@@ -1704,11 +1706,15 @@ static int gpiod_direction_output(struct gpio_desc *desc, int value)
if (!value && test_bit(FLAG_OPEN_SOURCE, &desc->flags))
return gpiod_direction_input(desc);
+ chip = desc->chip;
+ if (!chip->set || !chip->direction_output) {
+ pr_warn("%s: missing set() or direction_output() operations\n",
+ __func__);
+ return -EIO;
+ }
+
spin_lock_irqsave(&gpio_lock, flags);
- chip = desc->chip;
- if (!chip || !chip->set || !chip->direction_output)
- goto fail;
status = gpio_ensure_requested(desc);
if (status < 0)
goto fail;
@@ -1757,6 +1763,9 @@ EXPORT_SYMBOL_GPL(gpio_direction_output);
* gpio_set_debounce - sets @debounce time for a @gpio
* @gpio: the gpio to set debounce time
* @debounce: debounce time is microseconds
+ *
+ * returns -ENOTSUPP if the controller does not support setting
+ * debounce.
*/
static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
{
@@ -1765,16 +1774,19 @@ static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
int status = -EINVAL;
int offset;
- if (!desc) {
+ if (!desc || !desc->chip) {
pr_warn("%s: invalid GPIO\n", __func__);
return -EINVAL;
}
- spin_lock_irqsave(&gpio_lock, flags);
-
chip = desc->chip;
- if (!chip || !chip->set || !chip->set_debounce)
- goto fail;
+ if (!chip->set || !chip->set_debounce) {
+ pr_debug("%s: missing set() or set_debounce() operations\n",
+ __func__);
+ return -ENOTSUPP;
+ }
+
+ spin_lock_irqsave(&gpio_lock, flags);
status = gpio_ensure_requested(desc);
if (status < 0)