summaryrefslogtreecommitdiff
path: root/drivers/gpio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig91
-rw-r--r--drivers/gpio/Makefile10
-rw-r--r--drivers/gpio/devres.c2
-rw-r--r--drivers/gpio/gpio-104-dio-48e.c430
-rw-r--r--drivers/gpio/gpio-104-idi-48.c30
-rw-r--r--drivers/gpio/gpio-104-idio-16.c27
-rw-r--r--drivers/gpio/gpio-74xx-mmio.c11
-rw-r--r--drivers/gpio/gpio-adnp.c11
-rw-r--r--drivers/gpio/gpio-adp5520.c13
-rw-r--r--drivers/gpio/gpio-adp5588.c4
-rw-r--r--drivers/gpio/gpio-amd8111.c7
-rw-r--r--drivers/gpio/gpio-arizona.c12
-rw-r--r--drivers/gpio/gpio-ath79.c264
-rw-r--r--drivers/gpio/gpio-bcm-kona.c2
-rw-r--r--drivers/gpio/gpio-brcmstb.c13
-rw-r--r--drivers/gpio/gpio-clps711x.c11
-rw-r--r--drivers/gpio/gpio-crystalcove.c9
-rw-r--r--drivers/gpio/gpio-cs5535.c20
-rw-r--r--drivers/gpio/gpio-da9052.c11
-rw-r--r--drivers/gpio/gpio-da9055.c16
-rw-r--r--drivers/gpio/gpio-davinci.c5
-rw-r--r--drivers/gpio/gpio-dln2.c16
-rw-r--r--drivers/gpio/gpio-ep93xx.c2
-rw-r--r--drivers/gpio/gpio-f7188x.c53
-rw-r--r--drivers/gpio/gpio-ge.c2
-rw-r--r--drivers/gpio/gpio-generic.c11
-rw-r--r--drivers/gpio/gpio-ich.c51
-rw-r--r--drivers/gpio/gpio-iop.c2
-rw-r--r--drivers/gpio/gpio-janz-ttl.c12
-rw-r--r--drivers/gpio/gpio-kempld.c11
-rw-r--r--drivers/gpio/gpio-ks8695.c12
-rw-r--r--drivers/gpio/gpio-lp3943.c12
-rw-r--r--drivers/gpio/gpio-lpc32xx.c2
-rw-r--r--drivers/gpio/gpio-lynxpoint.c4
-rw-r--r--drivers/gpio/gpio-mc9s08dz60.c13
-rw-r--r--drivers/gpio/gpio-mcp23s08.c25
-rw-r--r--drivers/gpio/gpio-menz127.c199
-rw-r--r--drivers/gpio/gpio-moxart.c5
-rw-r--r--drivers/gpio/gpio-mpc5200.c1
-rw-r--r--drivers/gpio/gpio-mpc8xxx.c255
-rw-r--r--drivers/gpio/gpio-mvebu.c11
-rw-r--r--drivers/gpio/gpio-mxc.c6
-rw-r--r--drivers/gpio/gpio-octeon.c10
-rw-r--r--drivers/gpio/gpio-omap.c57
-rw-r--r--drivers/gpio/gpio-palmas.c12
-rw-r--r--drivers/gpio/gpio-pca953x.c4
-rw-r--r--drivers/gpio/gpio-pcf857x.c10
-rw-r--r--drivers/gpio/gpio-pisosr.c183
-rw-r--r--drivers/gpio/gpio-rc5t583.c12
-rw-r--r--drivers/gpio/gpio-rcar.c65
-rw-r--r--drivers/gpio/gpio-rdc321x.c13
-rw-r--r--drivers/gpio/gpio-sch.c11
-rw-r--r--drivers/gpio/gpio-sch311x.c8
-rw-r--r--drivers/gpio/gpio-spear-spics.c2
-rw-r--r--drivers/gpio/gpio-sta2x11.c2
-rw-r--r--drivers/gpio/gpio-stp-xway.c2
-rw-r--r--drivers/gpio/gpio-sx150x.c18
-rw-r--r--drivers/gpio/gpio-syscon.c11
-rw-r--r--drivers/gpio/gpio-tb10x.c22
-rw-r--r--drivers/gpio/gpio-tc3589x.c13
-rw-r--r--drivers/gpio/gpio-tegra.c2
-rw-r--r--drivers/gpio/gpio-timberdale.c25
-rw-r--r--drivers/gpio/gpio-tpic2810.c170
-rw-r--r--drivers/gpio/gpio-tps65086.c139
-rw-r--r--drivers/gpio/gpio-tps65218.c222
-rw-r--r--drivers/gpio/gpio-tps6586x.c12
-rw-r--r--drivers/gpio/gpio-tps65910.c12
-rw-r--r--drivers/gpio/gpio-tps65912.c174
-rw-r--r--drivers/gpio/gpio-ts4800.c81
-rw-r--r--drivers/gpio/gpio-ts5500.c9
-rw-r--r--drivers/gpio/gpio-twl6040.c9
-rw-r--r--drivers/gpio/gpio-ucb1400.c3
-rw-r--r--drivers/gpio/gpio-viperboard.c24
-rw-r--r--drivers/gpio/gpio-vx855.c12
-rw-r--r--drivers/gpio/gpio-wm831x.c12
-rw-r--r--drivers/gpio/gpio-wm8350.c12
-rw-r--r--drivers/gpio/gpio-wm8994.c17
-rw-r--r--drivers/gpio/gpio-ws16c48.c427
-rw-r--r--drivers/gpio/gpio-xgene-sb.c266
-rw-r--r--drivers/gpio/gpio-xgene.c16
-rw-r--r--drivers/gpio/gpiolib-acpi.c18
-rw-r--r--drivers/gpio/gpiolib-sysfs.c51
-rw-r--r--drivers/gpio/gpiolib.c883
-rw-r--r--drivers/gpio/gpiolib.h79
84 files changed, 3539 insertions, 1295 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index c88dd24a4..5f3429f0b 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -126,6 +126,16 @@ config GPIO_AMDPT
driver for GPIO functionality on Promontory IOHub
Require ACPI ASL code to enumerate as a platform device.
+config GPIO_ATH79
+ tristate "Atheros AR71XX/AR724X/AR913X GPIO support"
+ default y if ATH79
+ depends on ATH79 || COMPILE_TEST
+ select GPIO_GENERIC
+ select GPIOLIB_IRQCHIP
+ help
+ Select this option to enable GPIO driver for
+ Atheros AR71XX/AR724X/AR913X SoC devices.
+
config GPIO_BCM_KONA
bool "Broadcom Kona GPIO"
depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST)
@@ -256,10 +266,17 @@ config GPIO_LYNXPOINT
config GPIO_MB86S7X
bool "GPIO support for Fujitsu MB86S7x Platforms"
- depends on ARCH_MB86S7X
+ depends on ARCH_MB86S7X || COMPILE_TEST
help
Say yes here to support the GPIO controller in Fujitsu MB86S70 SoCs.
+config GPIO_MENZ127
+ tristate "MEN 16Z127 GPIO support"
+ depends on MCB
+ select GPIO_GENERIC
+ help
+ Say yes here to support the MEN 16Z127 GPIO Controller
+
config GPIO_MM_LANTIQ
bool "Lantiq Memory mapped GPIOs"
depends on LANTIQ && SOC_XWAY
@@ -270,7 +287,7 @@ config GPIO_MM_LANTIQ
config GPIO_MOXART
bool "MOXART GPIO support"
- depends on ARCH_MOXART
+ depends on ARCH_MOXART || COMPILE_TEST
select GPIO_GENERIC
help
Select this option to enable GPIO driver for
@@ -281,12 +298,14 @@ config GPIO_MPC5200
depends on PPC_MPC52xx
config GPIO_MPC8XXX
- bool "MPC512x/MPC8xxx GPIO support"
+ bool "MPC512x/MPC8xxx/QorIQ GPIO support"
depends on PPC_MPC512x || PPC_MPC831x || PPC_MPC834x || PPC_MPC837x || \
- FSL_SOC_BOOKE || PPC_86xx
+ FSL_SOC_BOOKE || PPC_86xx || ARCH_LAYERSCAPE || ARM || \
+ COMPILE_TEST
+ select GPIO_GENERIC
help
Say Y here if you're going to use hardware that connects to the
- MPC512x/831x/834x/837x/8572/8610 GPIOs.
+ MPC512x/831x/834x/837x/8572/8610/QorIQ GPIOs.
config GPIO_MVEBU
def_bool y
@@ -339,7 +358,7 @@ config GPIO_PXA
config GPIO_RCAR
tristate "Renesas R-Car GPIO"
- depends on ARCH_SHMOBILE || COMPILE_TEST
+ depends on ARCH_RENESAS || COMPILE_TEST
select GPIOLIB_IRQCHIP
help
Say yes here to support GPIO on Renesas R-Car SoCs.
@@ -380,6 +399,14 @@ config GPIO_TB10X
select GENERIC_IRQ_CHIP
select OF_GPIO
+config GPIO_TS4800
+ tristate "TS-4800 DIO blocks and compatibles"
+ depends on OF_GPIO
+ depends on SOC_IMX51 || COMPILE_TEST
+ select GPIO_GENERIC
+ help
+ This driver support TS-4800 FPGA GPIO controllers.
+
config GPIO_TZ1090
bool "Toumaz Xenif TZ1090 GPIO support"
depends on SOC_TZ1090
@@ -433,6 +460,7 @@ config GPIO_XGENE_SB
tristate "APM X-Gene GPIO standby controller support"
depends on ARCH_XGENE && OF_GPIO
select GPIO_GENERIC
+ select GPIOLIB_IRQCHIP
help
This driver supports the GPIO block within the APM X-Gene
Standby Domain. Say yes here to enable the GPIO functionality.
@@ -487,6 +515,15 @@ endmenu
menu "Port-mapped I/O GPIO drivers"
depends on X86 # Unconditional I/O space access
+config GPIO_104_DIO_48E
+ tristate "ACCES 104-DIO-48E GPIO support"
+ select GPIOLIB_IRQCHIP
+ help
+ Enables GPIO support for the ACCES 104-DIO-48E family. The base port
+ address for the device may be configured via the dio_48e_base module
+ parameter. The interrupt line number for the device may be configured
+ via the dio_48e_irq module parameter.
+
config GPIO_104_IDIO_16
tristate "ACCES 104-IDIO-16 GPIO support"
select GPIOLIB_IRQCHIP
@@ -506,10 +543,10 @@ config GPIO_104_IDI_48
via the idi_48_irq module parameter.
config GPIO_F7188X
- tristate "F71869, F71869A, F71882FG and F71889F GPIO support"
+ tristate "F71869, F71869A, F71882FG, F71889F and F81866 GPIO support"
help
This option enables support for GPIOs found on Fintek Super-I/O
- chips F71869, F71869A, F71882FG and F71889F.
+ chips F71869, F71869A, F71882FG, F71889F and F81866.
To compile this driver as a module, choose M here: the module will
be called f7188x-gpio.
@@ -570,6 +607,15 @@ config GPIO_TS5500
blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600
LCD port.
+config GPIO_WS16C48
+ tristate "WinSystems WS16C48 GPIO support"
+ select GPIOLIB_IRQCHIP
+ help
+ Enables GPIO support for the WinSystems WS16C48. The base port address
+ for the device may be configured via the ws16c48_base module
+ parameter. The interrupt line number for the device may be configured
+ via the ws16c48_irq module parameter.
+
endmenu
menu "I2C GPIO expanders"
@@ -702,6 +748,14 @@ config GPIO_SX150X
8 bits: sx1508q
16 bits: sx1509q
+config GPIO_TPIC2810
+ tristate "TPIC2810 8-Bit I2C GPO expander"
+ help
+ Say yes here to enable the GPO driver for the TI TPIC2810 chip.
+
+ To compile this driver as a module, choose M here: the module will
+ be called gpio-tpic2810.
+
endmenu
menu "MFD GPIO expanders"
@@ -844,6 +898,19 @@ config GPIO_TIMBERDALE
---help---
Add support for the GPIO IP in the timberdale FPGA.
+config GPIO_TPS65086
+ tristate "TI TPS65086 GPO"
+ depends on MFD_TPS65086
+ help
+ This driver supports the GPO on TI TPS65086x PMICs.
+
+config GPIO_TPS65218
+ tristate "TPS65218 GPIO"
+ depends on MFD_TPS65218
+ help
+ Select this option to enable GPIO driver for the TPS65218
+ chip family.
+
config GPIO_TPS6586X
bool "TPS6586X GPIO"
depends on MFD_TPS6586X
@@ -860,7 +927,7 @@ config GPIO_TPS65910
config GPIO_TPS65912
tristate "TI TPS65912 GPIO"
- depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI)
+ depends on MFD_TPS65912
help
This driver supports TPS65912 gpio chip
@@ -1011,6 +1078,12 @@ config GPIO_MC33880
SPI driver for Freescale MC33880 high-side/low-side switch.
This provides GPIO interface supporting inputs and outputs.
+config GPIO_PISOSR
+ tristate "Generic parallel-in/serial-out shift register"
+ help
+ GPIO driver for SPI compatible parallel-in/serial-out shift
+ registers. These are input only devices.
+
endmenu
menu "SPI or I2C GPIO expanders"
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index ece7d7cbd..1e0b74f3b 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o
# Device drivers. Generally keep list sorted alphabetically
obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
+obj-$(CONFIG_GPIO_104_DIO_48E) += gpio-104-dio-48e.o
obj-$(CONFIG_GPIO_104_IDIO_16) += gpio-104-idio-16.o
obj-$(CONFIG_GPIO_104_IDI_48) += gpio-104-idi-48.o
obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o
@@ -23,7 +24,7 @@ obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o
obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o
obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o
-obj-$(CONFIG_ATH79) += gpio-ath79.o
+obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o
obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
@@ -58,6 +59,7 @@ obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o
obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o
obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o
obj-$(CONFIG_GPIO_MB86S7X) += gpio-mb86s7x.o
+obj-$(CONFIG_GPIO_MENZ127) += gpio-menz127.o
obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o
obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o
obj-$(CONFIG_GPIO_MCP23S08) += gpio-mcp23s08.o
@@ -75,6 +77,7 @@ obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o
obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o
obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o
obj-$(CONFIG_GPIO_PCH) += gpio-pch.o
+obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o
obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o
obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o
@@ -95,9 +98,13 @@ obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o
+obj-$(CONFIG_GPIO_TPIC2810) += gpio-tpic2810.o
+obj-$(CONFIG_GPIO_TPS65086) += gpio-tps65086.o
+obj-$(CONFIG_GPIO_TPS65218) += gpio-tps65218.o
obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o
obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
+obj-$(CONFIG_GPIO_TS4800) += gpio-ts4800.o
obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
@@ -111,6 +118,7 @@ obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o
obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o
obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o
+obj-$(CONFIG_GPIO_WS16C48) += gpio-ws16c48.o
obj-$(CONFIG_GPIO_XGENE) += gpio-xgene.o
obj-$(CONFIG_GPIO_XGENE_SB) += gpio-xgene-sb.o
obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o
diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c
index 903fcf4d0..b760cbbb4 100644
--- a/drivers/gpio/devres.c
+++ b/drivers/gpio/devres.c
@@ -155,7 +155,7 @@ struct gpio_desc *devm_get_gpiod_from_child(struct device *dev,
suffixes[i]);
desc = fwnode_get_named_gpiod(child, prop_name);
- if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER))
+ if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT))
break;
}
if (IS_ERR(desc)) {
diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c
new file mode 100644
index 000000000..448a90308
--- /dev/null
+++ b/drivers/gpio/gpio-104-dio-48e.c
@@ -0,0 +1,430 @@
+/*
+ * GPIO driver for the ACCES 104-DIO-48E
+ * Copyright (C) 2016 William Breathitt Gray
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/gpio/driver.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/irqdesc.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+static unsigned dio_48e_base;
+module_param(dio_48e_base, uint, 0);
+MODULE_PARM_DESC(dio_48e_base, "ACCES 104-DIO-48E base address");
+static unsigned dio_48e_irq;
+module_param(dio_48e_irq, uint, 0);
+MODULE_PARM_DESC(dio_48e_irq, "ACCES 104-DIO-48E interrupt line number");
+
+/**
+ * struct dio48e_gpio - GPIO device private data structure
+ * @chip: instance of the gpio_chip
+ * @io_state: bit I/O state (whether bit is set to input or output)
+ * @out_state: output bits state
+ * @control: Control registers state
+ * @lock: synchronization lock to prevent I/O race conditions
+ * @base: base port address of the GPIO device
+ * @irq: Interrupt line number
+ * @irq_mask: I/O bits affected by interrupts
+ */
+struct dio48e_gpio {
+ struct gpio_chip chip;
+ unsigned char io_state[6];
+ unsigned char out_state[6];
+ unsigned char control[2];
+ spinlock_t lock;
+ unsigned base;
+ unsigned irq;
+ unsigned char irq_mask;
+};
+
+static int dio48e_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+ struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
+ const unsigned port = offset / 8;
+ const unsigned mask = BIT(offset % 8);
+
+ return !!(dio48egpio->io_state[port] & mask);
+}
+
+static int dio48e_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
+ const unsigned io_port = offset / 8;
+ const unsigned control_port = io_port / 2;
+ const unsigned control_addr = dio48egpio->base + 3 + control_port*4;
+ unsigned long flags;
+ unsigned control;
+
+ spin_lock_irqsave(&dio48egpio->lock, flags);
+
+ /* Check if configuring Port C */
+ if (io_port == 2 || io_port == 5) {
+ /* Port C can be configured by nibble */
+ if (offset % 8 > 3) {
+ dio48egpio->io_state[io_port] |= 0xF0;
+ dio48egpio->control[control_port] |= BIT(3);
+ } else {
+ dio48egpio->io_state[io_port] |= 0x0F;
+ dio48egpio->control[control_port] |= BIT(0);
+ }
+ } else {
+ dio48egpio->io_state[io_port] |= 0xFF;
+ if (io_port == 0 || io_port == 3)
+ dio48egpio->control[control_port] |= BIT(4);
+ else
+ dio48egpio->control[control_port] |= BIT(1);
+ }
+
+ control = BIT(7) | dio48egpio->control[control_port];
+ outb(control, control_addr);
+ control &= ~BIT(7);
+ outb(control, control_addr);
+
+ spin_unlock_irqrestore(&dio48egpio->lock, flags);
+
+ return 0;
+}
+
+static int dio48e_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
+ const unsigned io_port = offset / 8;
+ const unsigned control_port = io_port / 2;
+ const unsigned mask = BIT(offset % 8);
+ const unsigned control_addr = dio48egpio->base + 3 + control_port*4;
+ const unsigned out_port = (io_port > 2) ? io_port + 1 : io_port;
+ unsigned long flags;
+ unsigned control;
+
+ spin_lock_irqsave(&dio48egpio->lock, flags);
+
+ /* Check if configuring Port C */
+ if (io_port == 2 || io_port == 5) {
+ /* Port C can be configured by nibble */
+ if (offset % 8 > 3) {
+ dio48egpio->io_state[io_port] &= 0x0F;
+ dio48egpio->control[control_port] &= ~BIT(3);
+ } else {
+ dio48egpio->io_state[io_port] &= 0xF0;
+ dio48egpio->control[control_port] &= ~BIT(0);
+ }
+ } else {
+ dio48egpio->io_state[io_port] &= 0x00;
+ if (io_port == 0 || io_port == 3)
+ dio48egpio->control[control_port] &= ~BIT(4);
+ else
+ dio48egpio->control[control_port] &= ~BIT(1);
+ }
+
+ if (value)
+ dio48egpio->out_state[io_port] |= mask;
+ else
+ dio48egpio->out_state[io_port] &= ~mask;
+
+ control = BIT(7) | dio48egpio->control[control_port];
+ outb(control, control_addr);
+
+ outb(dio48egpio->out_state[io_port], dio48egpio->base + out_port);
+
+ control &= ~BIT(7);
+ outb(control, control_addr);
+
+ spin_unlock_irqrestore(&dio48egpio->lock, flags);
+
+ return 0;
+}
+
+static int dio48e_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
+ const unsigned port = offset / 8;
+ const unsigned mask = BIT(offset % 8);
+ const unsigned in_port = (port > 2) ? port + 1 : port;
+ unsigned long flags;
+ unsigned port_state;
+
+ spin_lock_irqsave(&dio48egpio->lock, flags);
+
+ /* ensure that GPIO is set for input */
+ if (!(dio48egpio->io_state[port] & mask)) {
+ spin_unlock_irqrestore(&dio48egpio->lock, flags);
+ return -EINVAL;
+ }
+
+ port_state = inb(dio48egpio->base + in_port);
+
+ spin_unlock_irqrestore(&dio48egpio->lock, flags);
+
+ return !!(port_state & mask);
+}
+
+static void dio48e_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
+ const unsigned port = offset / 8;
+ const unsigned mask = BIT(offset % 8);
+ const unsigned out_port = (port > 2) ? port + 1 : port;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dio48egpio->lock, flags);
+
+ if (value)
+ dio48egpio->out_state[port] |= mask;
+ else
+ dio48egpio->out_state[port] &= ~mask;
+
+ outb(dio48egpio->out_state[port], dio48egpio->base + out_port);
+
+ spin_unlock_irqrestore(&dio48egpio->lock, flags);
+}
+
+static void dio48e_irq_ack(struct irq_data *data)
+{
+}
+
+static void dio48e_irq_mask(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
+ const unsigned long offset = irqd_to_hwirq(data);
+ unsigned long flags;
+
+ /* only bit 3 on each respective Port C supports interrupts */
+ if (offset != 19 && offset != 43)
+ return;
+
+ spin_lock_irqsave(&dio48egpio->lock, flags);
+
+ if (offset == 19)
+ dio48egpio->irq_mask &= ~BIT(0);
+ else
+ dio48egpio->irq_mask &= ~BIT(1);
+
+ if (!dio48egpio->irq_mask)
+ /* disable interrupts */
+ inb(dio48egpio->base + 0xB);
+
+ spin_unlock_irqrestore(&dio48egpio->lock, flags);
+}
+
+static void dio48e_irq_unmask(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
+ const unsigned long offset = irqd_to_hwirq(data);
+ unsigned long flags;
+
+ /* only bit 3 on each respective Port C supports interrupts */
+ if (offset != 19 && offset != 43)
+ return;
+
+ spin_lock_irqsave(&dio48egpio->lock, flags);
+
+ if (!dio48egpio->irq_mask) {
+ /* enable interrupts */
+ outb(0x00, dio48egpio->base + 0xF);
+ outb(0x00, dio48egpio->base + 0xB);
+ }
+
+ if (offset == 19)
+ dio48egpio->irq_mask |= BIT(0);
+ else
+ dio48egpio->irq_mask |= BIT(1);
+
+ spin_unlock_irqrestore(&dio48egpio->lock, flags);
+}
+
+static int dio48e_irq_set_type(struct irq_data *data, unsigned flow_type)
+{
+ const unsigned long offset = irqd_to_hwirq(data);
+
+ /* only bit 3 on each respective Port C supports interrupts */
+ if (offset != 19 && offset != 43)
+ return -EINVAL;
+
+ if (flow_type != IRQ_TYPE_NONE && flow_type != IRQ_TYPE_EDGE_RISING)
+ return -EINVAL;
+
+ return 0;
+}
+
+static struct irq_chip dio48e_irqchip = {
+ .name = "104-dio-48e",
+ .irq_ack = dio48e_irq_ack,
+ .irq_mask = dio48e_irq_mask,
+ .irq_unmask = dio48e_irq_unmask,
+ .irq_set_type = dio48e_irq_set_type
+};
+
+static irqreturn_t dio48e_irq_handler(int irq, void *dev_id)
+{
+ struct dio48e_gpio *const dio48egpio = dev_id;
+ struct gpio_chip *const chip = &dio48egpio->chip;
+ const unsigned long irq_mask = dio48egpio->irq_mask;
+ unsigned long gpio;
+
+ for_each_set_bit(gpio, &irq_mask, 2)
+ generic_handle_irq(irq_find_mapping(chip->irqdomain,
+ 19 + gpio*24));
+
+ spin_lock(&dio48egpio->lock);
+
+ outb(0x00, dio48egpio->base + 0xF);
+
+ spin_unlock(&dio48egpio->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int __init dio48e_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dio48e_gpio *dio48egpio;
+ const unsigned base = dio_48e_base;
+ const unsigned extent = 16;
+ const char *const name = dev_name(dev);
+ int err;
+ const unsigned irq = dio_48e_irq;
+
+ dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL);
+ if (!dio48egpio)
+ return -ENOMEM;
+
+ if (!devm_request_region(dev, base, extent, name)) {
+ dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
+ base, base + extent);
+ return -EBUSY;
+ }
+
+ dio48egpio->chip.label = name;
+ dio48egpio->chip.parent = dev;
+ dio48egpio->chip.owner = THIS_MODULE;
+ dio48egpio->chip.base = -1;
+ dio48egpio->chip.ngpio = 48;
+ dio48egpio->chip.get_direction = dio48e_gpio_get_direction;
+ dio48egpio->chip.direction_input = dio48e_gpio_direction_input;
+ dio48egpio->chip.direction_output = dio48e_gpio_direction_output;
+ dio48egpio->chip.get = dio48e_gpio_get;
+ dio48egpio->chip.set = dio48e_gpio_set;
+ dio48egpio->base = base;
+ dio48egpio->irq = irq;
+
+ spin_lock_init(&dio48egpio->lock);
+
+ dev_set_drvdata(dev, dio48egpio);
+
+ err = gpiochip_add_data(&dio48egpio->chip, dio48egpio);
+ if (err) {
+ dev_err(dev, "GPIO registering failed (%d)\n", err);
+ return err;
+ }
+
+ /* initialize all GPIO as output */
+ outb(0x80, base + 3);
+ outb(0x00, base);
+ outb(0x00, base + 1);
+ outb(0x00, base + 2);
+ outb(0x00, base + 3);
+ outb(0x80, base + 7);
+ outb(0x00, base + 4);
+ outb(0x00, base + 5);
+ outb(0x00, base + 6);
+ outb(0x00, base + 7);
+
+ /* disable IRQ by default */
+ inb(base + 0xB);
+
+ err = gpiochip_irqchip_add(&dio48egpio->chip, &dio48e_irqchip, 0,
+ handle_edge_irq, IRQ_TYPE_NONE);
+ if (err) {
+ dev_err(dev, "Could not add irqchip (%d)\n", err);
+ goto err_gpiochip_remove;
+ }
+
+ err = request_irq(irq, dio48e_irq_handler, 0, name, dio48egpio);
+ if (err) {
+ dev_err(dev, "IRQ handler registering failed (%d)\n", err);
+ goto err_gpiochip_remove;
+ }
+
+ return 0;
+
+err_gpiochip_remove:
+ gpiochip_remove(&dio48egpio->chip);
+ return err;
+}
+
+static int dio48e_remove(struct platform_device *pdev)
+{
+ struct dio48e_gpio *const dio48egpio = platform_get_drvdata(pdev);
+
+ free_irq(dio48egpio->irq, dio48egpio);
+ gpiochip_remove(&dio48egpio->chip);
+
+ return 0;
+}
+
+static struct platform_device *dio48e_device;
+
+static struct platform_driver dio48e_driver = {
+ .driver = {
+ .name = "104-dio-48e"
+ },
+ .remove = dio48e_remove
+};
+
+static void __exit dio48e_exit(void)
+{
+ platform_device_unregister(dio48e_device);
+ platform_driver_unregister(&dio48e_driver);
+}
+
+static int __init dio48e_init(void)
+{
+ int err;
+
+ dio48e_device = platform_device_alloc(dio48e_driver.driver.name, -1);
+ if (!dio48e_device)
+ return -ENOMEM;
+
+ err = platform_device_add(dio48e_device);
+ if (err)
+ goto err_platform_device;
+
+ err = platform_driver_probe(&dio48e_driver, dio48e_probe);
+ if (err)
+ goto err_platform_driver;
+
+ return 0;
+
+err_platform_driver:
+ platform_device_del(dio48e_device);
+err_platform_device:
+ platform_device_put(dio48e_device);
+ return err;
+}
+
+module_init(dio48e_init);
+module_exit(dio48e_exit);
+
+MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
+MODULE_DESCRIPTION("ACCES 104-DIO-48E GPIO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c
index 52eed328c..e37cd4cdd 100644
--- a/drivers/gpio/gpio-104-idi-48.c
+++ b/drivers/gpio/gpio-104-idi-48.c
@@ -39,7 +39,6 @@ MODULE_PARM_DESC(idi_48_irq, "ACCES 104-IDI-48 interrupt line number");
* @ack_lock: synchronization lock to prevent IRQ handler race conditions
* @irq_mask: input bits affected by interrupts
* @base: base port address of the GPIO device
- * @extent: extent of port address region of the GPIO device
* @irq: Interrupt line number
* @cos_enb: Change-Of-State IRQ enable boundaries mask
*/
@@ -49,7 +48,6 @@ struct idi_48_gpio {
spinlock_t ack_lock;
unsigned char irq_mask[6];
unsigned base;
- unsigned extent;
unsigned irq;
unsigned char cos_enb;
};
@@ -227,11 +225,10 @@ static int __init idi_48_probe(struct platform_device *pdev)
if (!idi48gpio)
return -ENOMEM;
- if (!request_region(base, extent, name)) {
- dev_err(dev, "Unable to lock %s port addresses (0x%X-0x%X)\n",
- name, base, base + extent);
- err = -EBUSY;
- goto err_lock_io_port;
+ if (!devm_request_region(dev, base, extent, name)) {
+ dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
+ base, base + extent);
+ return -EBUSY;
}
idi48gpio->chip.label = name;
@@ -243,7 +240,6 @@ static int __init idi_48_probe(struct platform_device *pdev)
idi48gpio->chip.direction_input = idi_48_gpio_direction_input;
idi48gpio->chip.get = idi_48_gpio_get;
idi48gpio->base = base;
- idi48gpio->extent = extent;
idi48gpio->irq = irq;
spin_lock_init(&idi48gpio->lock);
@@ -253,7 +249,7 @@ static int __init idi_48_probe(struct platform_device *pdev)
err = gpiochip_add_data(&idi48gpio->chip, idi48gpio);
if (err) {
dev_err(dev, "GPIO registering failed (%d)\n", err);
- goto err_gpio_register;
+ return err;
}
/* Disable IRQ by default */
@@ -264,23 +260,20 @@ static int __init idi_48_probe(struct platform_device *pdev)
handle_edge_irq, IRQ_TYPE_NONE);
if (err) {
dev_err(dev, "Could not add irqchip (%d)\n", err);
- goto err_gpiochip_irqchip_add;
+ goto err_gpiochip_remove;
}
- err = request_irq(irq, idi_48_irq_handler, 0, name, idi48gpio);
+ err = request_irq(irq, idi_48_irq_handler, IRQF_SHARED, name,
+ idi48gpio);
if (err) {
dev_err(dev, "IRQ handler registering failed (%d)\n", err);
- goto err_request_irq;
+ goto err_gpiochip_remove;
}
return 0;
-err_request_irq:
-err_gpiochip_irqchip_add:
+err_gpiochip_remove:
gpiochip_remove(&idi48gpio->chip);
-err_gpio_register:
- release_region(base, extent);
-err_lock_io_port:
return err;
}
@@ -290,7 +283,6 @@ static int idi_48_remove(struct platform_device *pdev)
free_irq(idi48gpio->irq, idi48gpio);
gpiochip_remove(&idi48gpio->chip);
- release_region(idi48gpio->base, idi48gpio->extent);
return 0;
}
@@ -340,4 +332,4 @@ module_exit(idi_48_exit);
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("ACCES 104-IDI-48 GPIO driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c
index 4d69b50b2..ecc85fe93 100644
--- a/drivers/gpio/gpio-104-idio-16.c
+++ b/drivers/gpio/gpio-104-idio-16.c
@@ -38,7 +38,6 @@ MODULE_PARM_DESC(idio_16_irq, "ACCES 104-IDIO-16 interrupt line number");
* @lock: synchronization lock to prevent I/O race conditions
* @irq_mask: I/O bits affected by interrupts
* @base: base port address of the GPIO device
- * @extent: extent of port address region of the GPIO device
* @irq: Interrupt line number
* @out_state: output bits state
*/
@@ -47,7 +46,6 @@ struct idio_16_gpio {
spinlock_t lock;
unsigned long irq_mask;
unsigned base;
- unsigned extent;
unsigned irq;
unsigned out_state;
};
@@ -201,11 +199,10 @@ static int __init idio_16_probe(struct platform_device *pdev)
if (!idio16gpio)
return -ENOMEM;
- if (!request_region(base, extent, name)) {
- dev_err(dev, "Unable to lock %s port addresses (0x%X-0x%X)\n",
- name, base, base + extent);
- err = -EBUSY;
- goto err_lock_io_port;
+ if (!devm_request_region(dev, base, extent, name)) {
+ dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
+ base, base + extent);
+ return -EBUSY;
}
idio16gpio->chip.label = name;
@@ -219,7 +216,6 @@ static int __init idio_16_probe(struct platform_device *pdev)
idio16gpio->chip.get = idio_16_gpio_get;
idio16gpio->chip.set = idio_16_gpio_set;
idio16gpio->base = base;
- idio16gpio->extent = extent;
idio16gpio->irq = irq;
idio16gpio->out_state = 0xFFFF;
@@ -230,7 +226,7 @@ static int __init idio_16_probe(struct platform_device *pdev)
err = gpiochip_add_data(&idio16gpio->chip, idio16gpio);
if (err) {
dev_err(dev, "GPIO registering failed (%d)\n", err);
- goto err_gpio_register;
+ return err;
}
/* Disable IRQ by default */
@@ -241,23 +237,19 @@ static int __init idio_16_probe(struct platform_device *pdev)
handle_edge_irq, IRQ_TYPE_NONE);
if (err) {
dev_err(dev, "Could not add irqchip (%d)\n", err);
- goto err_gpiochip_irqchip_add;
+ goto err_gpiochip_remove;
}
err = request_irq(irq, idio_16_irq_handler, 0, name, idio16gpio);
if (err) {
dev_err(dev, "IRQ handler registering failed (%d)\n", err);
- goto err_request_irq;
+ goto err_gpiochip_remove;
}
return 0;
-err_request_irq:
-err_gpiochip_irqchip_add:
+err_gpiochip_remove:
gpiochip_remove(&idio16gpio->chip);
-err_gpio_register:
- release_region(base, extent);
-err_lock_io_port:
return err;
}
@@ -267,7 +259,6 @@ static int idio_16_remove(struct platform_device *pdev)
free_irq(idio16gpio->irq, idio16gpio);
gpiochip_remove(&idio16gpio->chip);
- release_region(idio16gpio->base, idio16gpio->extent);
return 0;
}
@@ -317,4 +308,4 @@ module_exit(idio_16_exit);
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c
index 372b0e01a..0475e8ec9 100644
--- a/drivers/gpio/gpio-74xx-mmio.c
+++ b/drivers/gpio/gpio-74xx-mmio.c
@@ -140,15 +140,7 @@ static int mmio_74xx_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
- return gpiochip_add_data(&priv->gc, priv);
-}
-
-static int mmio_74xx_gpio_remove(struct platform_device *pdev)
-{
- struct mmio_74xx_gpio_priv *priv = platform_get_drvdata(pdev);
-
- gpiochip_remove(&priv->gc);
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv);
}
static struct platform_driver mmio_74xx_gpio_driver = {
@@ -157,7 +149,6 @@ static struct platform_driver mmio_74xx_gpio_driver = {
.of_match_table = mmio_74xx_gpio_ids,
},
.probe = mmio_74xx_gpio_probe,
- .remove = mmio_74xx_gpio_remove,
};
module_platform_driver(mmio_74xx_gpio_driver);
diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c
index fb5b47b69..8ff7b0d3e 100644
--- a/drivers/gpio/gpio-adnp.c
+++ b/drivers/gpio/gpio-adnp.c
@@ -265,7 +265,7 @@ static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios)
chip->of_node = chip->parent->of_node;
chip->owner = THIS_MODULE;
- err = gpiochip_add_data(chip, adnp);
+ err = devm_gpiochip_add_data(&adnp->client->dev, chip, adnp);
if (err)
return err;
@@ -520,14 +520,6 @@ static int adnp_i2c_probe(struct i2c_client *client,
return 0;
}
-static int adnp_i2c_remove(struct i2c_client *client)
-{
- struct adnp *adnp = i2c_get_clientdata(client);
-
- gpiochip_remove(&adnp->gpio);
- return 0;
-}
-
static const struct i2c_device_id adnp_i2c_id[] = {
{ "gpio-adnp" },
{ },
@@ -546,7 +538,6 @@ static struct i2c_driver adnp_i2c_driver = {
.of_match_table = adnp_of_match,
},
.probe = adnp_i2c_probe,
- .remove = adnp_i2c_remove,
.id_table = adnp_i2c_id,
};
module_i2c_driver(adnp_i2c_driver);
diff --git a/drivers/gpio/gpio-adp5520.c b/drivers/gpio/gpio-adp5520.c
index 4fa7ff1fe..abf199609 100644
--- a/drivers/gpio/gpio-adp5520.c
+++ b/drivers/gpio/gpio-adp5520.c
@@ -153,7 +153,7 @@ static int adp5520_gpio_probe(struct platform_device *pdev)
goto err;
}
- ret = gpiochip_add_data(&dev->gpio_chip, dev);
+ ret = devm_gpiochip_add_data(&pdev->dev, &dev->gpio_chip, dev);
if (ret)
goto err;
@@ -164,22 +164,11 @@ err:
return ret;
}
-static int adp5520_gpio_remove(struct platform_device *pdev)
-{
- struct adp5520_gpio *dev;
-
- dev = platform_get_drvdata(pdev);
- gpiochip_remove(&dev->gpio_chip);
-
- return 0;
-}
-
static struct platform_driver adp5520_gpio_driver = {
.driver = {
.name = "adp5520-gpio",
},
.probe = adp5520_gpio_probe,
- .remove = adp5520_gpio_remove,
};
module_platform_driver(adp5520_gpio_driver);
diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c
index 19a0eba1e..c0f718b12 100644
--- a/drivers/gpio/gpio-adp5588.c
+++ b/drivers/gpio/gpio-adp5588.c
@@ -414,7 +414,7 @@ static int adp5588_gpio_probe(struct i2c_client *client,
}
}
- ret = gpiochip_add_data(&dev->gpio_chip, dev);
+ ret = devm_gpiochip_add_data(&client->dev, &dev->gpio_chip, dev);
if (ret)
goto err_irq;
@@ -457,8 +457,6 @@ static int adp5588_gpio_remove(struct i2c_client *client)
if (dev->irq_base)
free_irq(dev->client->irq, dev);
- gpiochip_remove(&dev->gpio_chip);
-
return 0;
}
diff --git a/drivers/gpio/gpio-amd8111.c b/drivers/gpio/gpio-amd8111.c
index c7040fffc..30ad7d7c1 100644
--- a/drivers/gpio/gpio-amd8111.c
+++ b/drivers/gpio/gpio-amd8111.c
@@ -25,6 +25,7 @@
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
+#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
@@ -204,7 +205,8 @@ found:
gp.pmbase &= 0x0000FF00;
if (gp.pmbase == 0)
goto out;
- if (!request_region(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE, "AMD GPIO")) {
+ if (!devm_request_region(&pdev->dev, gp.pmbase + PMBASE_OFFSET,
+ PMBASE_SIZE, "AMD GPIO")) {
dev_err(&pdev->dev, "AMD GPIO region 0x%x already in use!\n",
gp.pmbase + PMBASE_OFFSET);
err = -EBUSY;
@@ -213,7 +215,6 @@ found:
gp.pm = ioport_map(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
if (!gp.pm) {
dev_err(&pdev->dev, "Couldn't map io port into io memory\n");
- release_region(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
err = -ENOMEM;
goto out;
}
@@ -228,7 +229,6 @@ found:
printk(KERN_ERR "GPIO registering failed (%d)\n",
err);
ioport_unmap(gp.pm);
- release_region(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
goto out;
}
out:
@@ -239,7 +239,6 @@ static void __exit amd_gpio_exit(void)
{
gpiochip_remove(&gp.chip);
ioport_unmap(gp.pm);
- release_region(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
}
module_init(amd_gpio_init);
diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c
index e910c1f41..991370494 100644
--- a/drivers/gpio/gpio-arizona.c
+++ b/drivers/gpio/gpio-arizona.c
@@ -132,7 +132,8 @@ static int arizona_gpio_probe(struct platform_device *pdev)
else
arizona_gpio->gpio_chip.base = -1;
- ret = gpiochip_add_data(&arizona_gpio->gpio_chip, arizona_gpio);
+ ret = devm_gpiochip_add_data(&pdev->dev, &arizona_gpio->gpio_chip,
+ arizona_gpio);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
ret);
@@ -147,18 +148,9 @@ err:
return ret;
}
-static int arizona_gpio_remove(struct platform_device *pdev)
-{
- struct arizona_gpio *arizona_gpio = platform_get_drvdata(pdev);
-
- gpiochip_remove(&arizona_gpio->gpio_chip);
- return 0;
-}
-
static struct platform_driver arizona_gpio_driver = {
.driver.name = "arizona-gpio",
.probe = arizona_gpio_probe,
- .remove = arizona_gpio_remove,
};
module_platform_driver(arizona_gpio_driver);
diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c
index d13dd133a..c4f4cddc7 100644
--- a/drivers/gpio/gpio-ath79.c
+++ b/drivers/gpio/gpio-ath79.c
@@ -1,12 +1,11 @@
/*
* Atheros AR71XX/AR724X/AR913X GPIO API support
*
+ * Copyright (C) 2015 Alban Bedel <albeu@free.fr>
* Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
* Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
- * Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP
- *
* 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.
@@ -15,118 +14,204 @@
#include <linux/gpio/driver.h>
#include <linux/platform_data/gpio-ath79.h>
#include <linux/of_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#define AR71XX_GPIO_REG_OE 0x00
+#define AR71XX_GPIO_REG_IN 0x04
+#define AR71XX_GPIO_REG_SET 0x0c
+#define AR71XX_GPIO_REG_CLEAR 0x10
-#include <asm/mach-ath79/ar71xx_regs.h>
+#define AR71XX_GPIO_REG_INT_ENABLE 0x14
+#define AR71XX_GPIO_REG_INT_TYPE 0x18
+#define AR71XX_GPIO_REG_INT_POLARITY 0x1c
+#define AR71XX_GPIO_REG_INT_PENDING 0x20
+#define AR71XX_GPIO_REG_INT_MASK 0x24
struct ath79_gpio_ctrl {
- struct gpio_chip chip;
+ struct gpio_chip gc;
void __iomem *base;
spinlock_t lock;
+ unsigned long both_edges;
};
-static void ath79_gpio_set_value(struct gpio_chip *chip,
- unsigned gpio, int value)
+static struct ath79_gpio_ctrl *irq_data_to_ath79_gpio(struct irq_data *data)
{
- struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
- if (value)
- __raw_writel(BIT(gpio), ctrl->base + AR71XX_GPIO_REG_SET);
- else
- __raw_writel(BIT(gpio), ctrl->base + AR71XX_GPIO_REG_CLEAR);
+ return container_of(gc, struct ath79_gpio_ctrl, gc);
}
-static int ath79_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+static u32 ath79_gpio_read(struct ath79_gpio_ctrl *ctrl, unsigned reg)
{
- struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip);
+ return readl(ctrl->base + reg);
+}
- return (__raw_readl(ctrl->base + AR71XX_GPIO_REG_IN) >> gpio) & 1;
+static void ath79_gpio_write(struct ath79_gpio_ctrl *ctrl,
+ unsigned reg, u32 val)
+{
+ return writel(val, ctrl->base + reg);
}
-static int ath79_gpio_direction_input(struct gpio_chip *chip,
- unsigned offset)
+static bool ath79_gpio_update_bits(
+ struct ath79_gpio_ctrl *ctrl, unsigned reg, u32 mask, u32 bits)
{
- struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip);
+ u32 old_val, new_val;
+
+ old_val = ath79_gpio_read(ctrl, reg);
+ new_val = (old_val & ~mask) | (bits & mask);
+
+ if (new_val != old_val)
+ ath79_gpio_write(ctrl, reg, new_val);
+
+ return new_val != old_val;
+}
+
+static void ath79_gpio_irq_unmask(struct irq_data *data)
+{
+ struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
+ u32 mask = BIT(irqd_to_hwirq(data));
unsigned long flags;
spin_lock_irqsave(&ctrl->lock, flags);
+ ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, mask);
+ spin_unlock_irqrestore(&ctrl->lock, flags);
+}
- __raw_writel(
- __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) & ~BIT(offset),
- ctrl->base + AR71XX_GPIO_REG_OE);
+static void ath79_gpio_irq_mask(struct irq_data *data)
+{
+ struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
+ u32 mask = BIT(irqd_to_hwirq(data));
+ unsigned long flags;
+ spin_lock_irqsave(&ctrl->lock, flags);
+ ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, 0);
spin_unlock_irqrestore(&ctrl->lock, flags);
-
- return 0;
}
-static int ath79_gpio_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
+static void ath79_gpio_irq_enable(struct irq_data *data)
{
- struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip);
+ struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
+ u32 mask = BIT(irqd_to_hwirq(data));
unsigned long flags;
spin_lock_irqsave(&ctrl->lock, flags);
+ ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, mask);
+ ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, mask);
+ spin_unlock_irqrestore(&ctrl->lock, flags);
+}
- if (value)
- __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_SET);
- else
- __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_CLEAR);
-
- __raw_writel(
- __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) | BIT(offset),
- ctrl->base + AR71XX_GPIO_REG_OE);
+static void ath79_gpio_irq_disable(struct irq_data *data)
+{
+ struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
+ u32 mask = BIT(irqd_to_hwirq(data));
+ unsigned long flags;
+ spin_lock_irqsave(&ctrl->lock, flags);
+ ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, 0);
+ ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, 0);
spin_unlock_irqrestore(&ctrl->lock, flags);
-
- return 0;
}
-static int ar934x_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+static int ath79_gpio_irq_set_type(struct irq_data *data,
+ unsigned int flow_type)
{
- struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip);
+ struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
+ u32 mask = BIT(irqd_to_hwirq(data));
+ u32 type = 0, polarity = 0;
unsigned long flags;
+ bool disabled;
+
+ switch (flow_type) {
+ case IRQ_TYPE_EDGE_RISING:
+ polarity |= mask;
+ case IRQ_TYPE_EDGE_FALLING:
+ case IRQ_TYPE_EDGE_BOTH:
+ break;
+
+ case IRQ_TYPE_LEVEL_HIGH:
+ polarity |= mask;
+ case IRQ_TYPE_LEVEL_LOW:
+ type |= mask;
+ break;
+
+ default:
+ return -EINVAL;
+ }
spin_lock_irqsave(&ctrl->lock, flags);
- __raw_writel(
- __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) | BIT(offset),
- ctrl->base + AR71XX_GPIO_REG_OE);
+ if (flow_type == IRQ_TYPE_EDGE_BOTH) {
+ ctrl->both_edges |= mask;
+ polarity = ~ath79_gpio_read(ctrl, AR71XX_GPIO_REG_IN);
+ } else {
+ ctrl->both_edges &= ~mask;
+ }
+
+ /* As the IRQ configuration can't be loaded atomically we
+ * have to disable the interrupt while the configuration state
+ * is invalid.
+ */
+ disabled = ath79_gpio_update_bits(
+ ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, 0);
+
+ ath79_gpio_update_bits(
+ ctrl, AR71XX_GPIO_REG_INT_TYPE, mask, type);
+ ath79_gpio_update_bits(
+ ctrl, AR71XX_GPIO_REG_INT_POLARITY, mask, polarity);
+
+ if (disabled)
+ ath79_gpio_update_bits(
+ ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, mask);
spin_unlock_irqrestore(&ctrl->lock, flags);
return 0;
}
-static int ar934x_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
- int value)
+static struct irq_chip ath79_gpio_irqchip = {
+ .name = "gpio-ath79",
+ .irq_enable = ath79_gpio_irq_enable,
+ .irq_disable = ath79_gpio_irq_disable,
+ .irq_mask = ath79_gpio_irq_mask,
+ .irq_unmask = ath79_gpio_irq_unmask,
+ .irq_set_type = ath79_gpio_irq_set_type,
+};
+
+static void ath79_gpio_irq_handler(struct irq_desc *desc)
{
- struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip);
- unsigned long flags;
+ struct gpio_chip *gc = irq_desc_get_handler_data(desc);
+ struct irq_chip *irqchip = irq_desc_get_chip(desc);
+ struct ath79_gpio_ctrl *ctrl =
+ container_of(gc, struct ath79_gpio_ctrl, gc);
+ unsigned long flags, pending;
+ u32 both_edges, state;
+ int irq;
+
+ chained_irq_enter(irqchip, desc);
spin_lock_irqsave(&ctrl->lock, flags);
- if (value)
- __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_SET);
- else
- __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_CLEAR);
+ pending = ath79_gpio_read(ctrl, AR71XX_GPIO_REG_INT_PENDING);
- __raw_writel(
- __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) & ~BIT(offset),
- ctrl->base + AR71XX_GPIO_REG_OE);
+ /* Update the polarity of the both edges irqs */
+ both_edges = ctrl->both_edges & pending;
+ if (both_edges) {
+ state = ath79_gpio_read(ctrl, AR71XX_GPIO_REG_IN);
+ ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_POLARITY,
+ both_edges, ~state);
+ }
spin_unlock_irqrestore(&ctrl->lock, flags);
- return 0;
-}
+ if (pending) {
+ for_each_set_bit(irq, &pending, gc->ngpio)
+ generic_handle_irq(
+ irq_linear_revmap(gc->irqdomain, irq));
+ }
-static const struct gpio_chip ath79_gpio_chip = {
- .label = "ath79",
- .get = ath79_gpio_get_value,
- .set = ath79_gpio_set_value,
- .direction_input = ath79_gpio_direction_input,
- .direction_output = ath79_gpio_direction_output,
- .base = 0,
-};
+ chained_irq_exit(irqchip, desc);
+}
static const struct of_device_id ath79_gpio_of_match[] = {
{ .compatible = "qca,ar7100-gpio" },
@@ -147,6 +232,7 @@ static int ath79_gpio_probe(struct platform_device *pdev)
ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
if (!ctrl)
return -ENOMEM;
+ platform_set_drvdata(pdev, ctrl);
if (np) {
err = of_property_read_u32(np, "ngpios", &ath79_gpio_count);
@@ -154,10 +240,6 @@ static int ath79_gpio_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "ngpios property is not valid\n");
return err;
}
- if (ath79_gpio_count >= 32) {
- dev_err(&pdev->dev, "ngpios must be less than 32\n");
- return -EINVAL;
- }
oe_inverted = of_device_is_compatible(np, "qca,ar9340-gpio");
} else if (pdata) {
ath79_gpio_count = pdata->ngpios;
@@ -167,6 +249,11 @@ static int ath79_gpio_probe(struct platform_device *pdev)
return -EINVAL;
}
+ if (ath79_gpio_count >= 32) {
+ dev_err(&pdev->dev, "ngpios must be less than 32\n");
+ return -EINVAL;
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ctrl->base = devm_ioremap_nocache(
&pdev->dev, res->start, resource_size(res));
@@ -174,21 +261,53 @@ static int ath79_gpio_probe(struct platform_device *pdev)
return -ENOMEM;
spin_lock_init(&ctrl->lock);
- memcpy(&ctrl->chip, &ath79_gpio_chip, sizeof(ctrl->chip));
- ctrl->chip.parent = &pdev->dev;
- ctrl->chip.ngpio = ath79_gpio_count;
- if (oe_inverted) {
- ctrl->chip.direction_input = ar934x_gpio_direction_input;
- ctrl->chip.direction_output = ar934x_gpio_direction_output;
+ err = bgpio_init(&ctrl->gc, &pdev->dev, 4,
+ ctrl->base + AR71XX_GPIO_REG_IN,
+ ctrl->base + AR71XX_GPIO_REG_SET,
+ ctrl->base + AR71XX_GPIO_REG_CLEAR,
+ oe_inverted ? NULL : ctrl->base + AR71XX_GPIO_REG_OE,
+ oe_inverted ? ctrl->base + AR71XX_GPIO_REG_OE : NULL,
+ 0);
+ if (err) {
+ dev_err(&pdev->dev, "bgpio_init failed\n");
+ return err;
}
+ /* Use base 0 to stay compatible with legacy platforms */
+ ctrl->gc.base = 0;
- err = gpiochip_add_data(&ctrl->chip, ctrl);
+ err = gpiochip_add_data(&ctrl->gc, ctrl);
if (err) {
dev_err(&pdev->dev,
"cannot add AR71xx GPIO chip, error=%d", err);
return err;
}
+ if (np && !of_property_read_bool(np, "interrupt-controller"))
+ return 0;
+
+ err = gpiochip_irqchip_add(&ctrl->gc, &ath79_gpio_irqchip, 0,
+ handle_simple_irq, IRQ_TYPE_NONE);
+ if (err) {
+ dev_err(&pdev->dev, "failed to add gpiochip_irqchip\n");
+ goto gpiochip_remove;
+ }
+
+ gpiochip_set_chained_irqchip(&ctrl->gc, &ath79_gpio_irqchip,
+ platform_get_irq(pdev, 0),
+ ath79_gpio_irq_handler);
+
+ return 0;
+
+gpiochip_remove:
+ gpiochip_remove(&ctrl->gc);
+ return err;
+}
+
+static int ath79_gpio_remove(struct platform_device *pdev)
+{
+ struct ath79_gpio_ctrl *ctrl = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&ctrl->gc);
return 0;
}
@@ -198,6 +317,7 @@ static struct platform_driver ath79_gpio_driver = {
.of_match_table = ath79_gpio_of_match,
},
.probe = ath79_gpio_probe,
+ .remove = ath79_gpio_remove,
};
module_platform_driver(ath79_gpio_driver);
diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c
index b6c5abe85..2fd38d598 100644
--- a/drivers/gpio/gpio-bcm-kona.c
+++ b/drivers/gpio/gpio-bcm-kona.c
@@ -630,7 +630,7 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev)
bcm_kona_gpio_reset(kona_gpio);
- ret = gpiochip_add_data(chip, kona_gpio);
+ ret = devm_gpiochip_add_data(dev, chip, kona_gpio);
if (ret < 0) {
dev_err(dev, "Couldn't add GPIO chip -- %d\n", ret);
goto err_irq_domain;
diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c
index d7644251e..42d51c59e 100644
--- a/drivers/gpio/gpio-brcmstb.c
+++ b/drivers/gpio/gpio-brcmstb.c
@@ -233,17 +233,14 @@ static void brcmstb_gpio_irq_handler(struct irq_desc *desc)
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc);
struct irq_chip *chip = irq_desc_get_chip(desc);
- struct list_head *pos;
+ struct brcmstb_gpio_bank *bank;
/* Interrupts weren't properly cleared during probe */
BUG_ON(!priv || !chip);
chained_irq_enter(chip, desc);
- list_for_each(pos, &priv->bank_list) {
- struct brcmstb_gpio_bank *bank =
- list_entry(pos, struct brcmstb_gpio_bank, node);
+ list_for_each_entry(bank, &priv->bank_list, node)
brcmstb_gpio_irq_bank_handler(bank);
- }
chained_irq_exit(chip, desc);
}
@@ -280,7 +277,6 @@ static int brcmstb_gpio_sanity_check_banks(struct device *dev,
static int brcmstb_gpio_remove(struct platform_device *pdev)
{
struct brcmstb_gpio_priv *priv = platform_get_drvdata(pdev);
- struct list_head *pos;
struct brcmstb_gpio_bank *bank;
int ret = 0;
@@ -293,10 +289,9 @@ static int brcmstb_gpio_remove(struct platform_device *pdev)
* You can lose return values below, but we report all errors, and it's
* more important to actually perform all of the steps.
*/
- list_for_each(pos, &priv->bank_list) {
- bank = list_entry(pos, struct brcmstb_gpio_bank, node);
+ list_for_each_entry(bank, &priv->bank_list, node)
gpiochip_remove(&bank->gc);
- }
+
if (priv->reboot_notifier.notifier_call) {
ret = unregister_reboot_notifier(&priv->reboot_notifier);
if (ret)
diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c
index c84f9551f..5a690256a 100644
--- a/drivers/gpio/gpio-clps711x.c
+++ b/drivers/gpio/gpio-clps711x.c
@@ -67,15 +67,7 @@ static int clps711x_gpio_probe(struct platform_device *pdev)
gc->owner = THIS_MODULE;
platform_set_drvdata(pdev, gc);
- return gpiochip_add_data(gc, NULL);
-}
-
-static int clps711x_gpio_remove(struct platform_device *pdev)
-{
- struct gpio_chip *gc = platform_get_drvdata(pdev);
-
- gpiochip_remove(gc);
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, gc, NULL);
}
static const struct of_device_id __maybe_unused clps711x_gpio_ids[] = {
@@ -90,7 +82,6 @@ static struct platform_driver clps711x_gpio_driver = {
.of_match_table = of_match_ptr(clps711x_gpio_ids),
},
.probe = clps711x_gpio_probe,
- .remove = clps711x_gpio_remove,
};
module_platform_driver(clps711x_gpio_driver);
diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c
index 7865ef0d3..7c446d118 100644
--- a/drivers/gpio/gpio-crystalcove.c
+++ b/drivers/gpio/gpio-crystalcove.c
@@ -345,7 +345,7 @@ static int crystalcove_gpio_probe(struct platform_device *pdev)
cg->chip.dbg_show = crystalcove_gpio_dbg_show;
cg->regmap = pmic->regmap;
- retval = gpiochip_add_data(&cg->chip, cg);
+ retval = devm_gpiochip_add_data(&pdev->dev, &cg->chip, cg);
if (retval) {
dev_warn(&pdev->dev, "add gpio chip error: %d\n", retval);
return retval;
@@ -359,14 +359,10 @@ static int crystalcove_gpio_probe(struct platform_device *pdev)
if (retval) {
dev_warn(&pdev->dev, "request irq failed: %d\n", retval);
- goto out_remove_gpio;
+ return retval;
}
return 0;
-
-out_remove_gpio:
- gpiochip_remove(&cg->chip);
- return retval;
}
static int crystalcove_gpio_remove(struct platform_device *pdev)
@@ -374,7 +370,6 @@ static int crystalcove_gpio_remove(struct platform_device *pdev)
struct crystalcove_gpio *cg = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
- gpiochip_remove(&cg->chip);
if (irq >= 0)
free_irq(irq, cg);
return 0;
diff --git a/drivers/gpio/gpio-cs5535.c b/drivers/gpio/gpio-cs5535.c
index eccb712e0..90278b19a 100644
--- a/drivers/gpio/gpio-cs5535.c
+++ b/drivers/gpio/gpio-cs5535.c
@@ -320,13 +320,13 @@ static int cs5535_gpio_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!res) {
dev_err(&pdev->dev, "can't fetch device resource info\n");
- goto done;
+ return err;
}
if (!devm_request_region(&pdev->dev, res->start, resource_size(res),
pdev->name)) {
dev_err(&pdev->dev, "can't request region\n");
- goto done;
+ return err;
}
/* set up the driver-specific struct */
@@ -348,19 +348,10 @@ static int cs5535_gpio_probe(struct platform_device *pdev)
mask_orig, mask);
/* finally, register with the generic GPIO API */
- err = gpiochip_add_data(&cs5535_gpio_chip.chip, &cs5535_gpio_chip);
+ err = devm_gpiochip_add_data(&pdev->dev, &cs5535_gpio_chip.chip,
+ &cs5535_gpio_chip);
if (err)
- goto done;
-
- return 0;
-
-done:
- return err;
-}
-
-static int cs5535_gpio_remove(struct platform_device *pdev)
-{
- gpiochip_remove(&cs5535_gpio_chip.chip);
+ return err;
return 0;
}
@@ -370,7 +361,6 @@ static struct platform_driver cs5535_gpio_driver = {
.name = DRV_NAME,
},
.probe = cs5535_gpio_probe,
- .remove = cs5535_gpio_remove,
};
module_platform_driver(cs5535_gpio_driver);
diff --git a/drivers/gpio/gpio-da9052.c b/drivers/gpio/gpio-da9052.c
index f9b3247ad..e29553b7c 100644
--- a/drivers/gpio/gpio-da9052.c
+++ b/drivers/gpio/gpio-da9052.c
@@ -214,7 +214,7 @@ static int da9052_gpio_probe(struct platform_device *pdev)
if (pdata && pdata->gpio_base)
gpio->gp.base = pdata->gpio_base;
- ret = gpiochip_add_data(&gpio->gp, gpio);
+ ret = devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
return ret;
@@ -225,17 +225,8 @@ static int da9052_gpio_probe(struct platform_device *pdev)
return 0;
}
-static int da9052_gpio_remove(struct platform_device *pdev)
-{
- struct da9052_gpio *gpio = platform_get_drvdata(pdev);
-
- gpiochip_remove(&gpio->gp);
- return 0;
-}
-
static struct platform_driver da9052_gpio_driver = {
.probe = da9052_gpio_probe,
- .remove = da9052_gpio_remove,
.driver = {
.name = "da9052-gpio",
},
diff --git a/drivers/gpio/gpio-da9055.c b/drivers/gpio/gpio-da9055.c
index 18210fb2c..2c2c18dc6 100644
--- a/drivers/gpio/gpio-da9055.c
+++ b/drivers/gpio/gpio-da9055.c
@@ -151,31 +151,19 @@ static int da9055_gpio_probe(struct platform_device *pdev)
if (pdata && pdata->gpio_base)
gpio->gp.base = pdata->gpio_base;
- ret = gpiochip_add_data(&gpio->gp, gpio);
+ ret = devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
- goto err_mem;
+ return ret;
}
platform_set_drvdata(pdev, gpio);
return 0;
-
-err_mem:
- return ret;
-}
-
-static int da9055_gpio_remove(struct platform_device *pdev)
-{
- struct da9055_gpio *gpio = platform_get_drvdata(pdev);
-
- gpiochip_remove(&gpio->gp);
- return 0;
}
static struct platform_driver da9055_gpio_driver = {
.probe = da9055_gpio_probe,
- .remove = da9055_gpio_remove,
.driver = {
.name = "da9055-gpio",
},
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
index cd007a67b..dd262f002 100644
--- a/drivers/gpio/gpio-davinci.c
+++ b/drivers/gpio/gpio-davinci.c
@@ -258,6 +258,8 @@ static int davinci_gpio_probe(struct platform_device *pdev)
spin_lock_init(&chips[i].lock);
regs = gpio2regs(base);
+ if (!regs)
+ return -ENXIO;
chips[i].regs = regs;
chips[i].set_data = &regs->set_data;
chips[i].clr_data = &regs->clr_data;
@@ -433,8 +435,7 @@ static struct irq_chip *davinci_gpio_get_irq_chip(unsigned int irq)
{
static struct irq_chip_type gpio_unbanked;
- gpio_unbanked = *container_of(irq_get_chip(irq),
- struct irq_chip_type, chip);
+ gpio_unbanked = *irq_data_get_chip_type(irq_get_irq_data(irq));
return &gpio_unbanked.chip;
};
diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c
index e11a7d126..f7a60a441 100644
--- a/drivers/gpio/gpio-dln2.c
+++ b/drivers/gpio/gpio-dln2.c
@@ -479,40 +479,32 @@ static int dln2_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dln2);
- ret = gpiochip_add_data(&dln2->gpio, dln2);
+ ret = devm_gpiochip_add_data(dev, &dln2->gpio, dln2);
if (ret < 0) {
dev_err(dev, "failed to add gpio chip: %d\n", ret);
- goto out;
+ return ret;
}
ret = gpiochip_irqchip_add(&dln2->gpio, &dln2_gpio_irqchip, 0,
handle_simple_irq, IRQ_TYPE_NONE);
if (ret < 0) {
dev_err(dev, "failed to add irq chip: %d\n", ret);
- goto out_gpiochip_remove;
+ return ret;
}
ret = dln2_register_event_cb(pdev, DLN2_GPIO_CONDITION_MET_EV,
dln2_gpio_event);
if (ret) {
dev_err(dev, "failed to register event cb: %d\n", ret);
- goto out_gpiochip_remove;
+ return ret;
}
return 0;
-
-out_gpiochip_remove:
- gpiochip_remove(&dln2->gpio);
-out:
- return ret;
}
static int dln2_gpio_remove(struct platform_device *pdev)
{
- struct dln2_gpio *dln2 = platform_get_drvdata(pdev);
-
dln2_unregister_event_cb(pdev, DLN2_GPIO_CONDITION_MET_EV);
- gpiochip_remove(&dln2->gpio);
return 0;
}
diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c
index ad279078f..d054219e1 100644
--- a/drivers/gpio/gpio-ep93xx.c
+++ b/drivers/gpio/gpio-ep93xx.c
@@ -339,7 +339,7 @@ static int ep93xx_gpio_add_bank(struct gpio_chip *gc, struct device *dev,
gc->to_irq = ep93xx_gpio_to_irq;
}
- return gpiochip_add_data(gc, NULL);
+ return devm_gpiochip_add_data(dev, gc, NULL);
}
static int ep93xx_gpio_probe(struct platform_device *pdev)
diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c
index d62fd6bba..daac2d480 100644
--- a/drivers/gpio/gpio-f7188x.c
+++ b/drivers/gpio/gpio-f7188x.c
@@ -1,5 +1,5 @@
/*
- * GPIO driver for Fintek Super-I/O F71869, F71869A, F71882 and F71889
+ * GPIO driver for Fintek Super-I/O F71869, F71869A, F71882, F71889 and F81866
*
* Copyright (C) 2010-2013 LaCie
*
@@ -36,14 +36,16 @@
#define SIO_F71869A_ID 0x1007 /* F71869A chipset ID */
#define SIO_F71882_ID 0x0541 /* F71882 chipset ID */
#define SIO_F71889_ID 0x0909 /* F71889 chipset ID */
+#define SIO_F81866_ID 0x1010 /* F81866 chipset ID */
-enum chips { f71869, f71869a, f71882fg, f71889f };
+enum chips { f71869, f71869a, f71882fg, f71889f, f81866 };
static const char * const f7188x_names[] = {
"f71869",
"f71869a",
"f71882fg",
"f71889f",
+ "f81866",
};
struct f7188x_sio {
@@ -190,6 +192,18 @@ static struct f7188x_gpio_bank f71889_gpio_bank[] = {
F7188X_GPIO_BANK(70, 8, 0x80),
};
+static struct f7188x_gpio_bank f81866_gpio_bank[] = {
+ F7188X_GPIO_BANK(0, 8, 0xF0),
+ F7188X_GPIO_BANK(10, 8, 0xE0),
+ F7188X_GPIO_BANK(20, 8, 0xD0),
+ F7188X_GPIO_BANK(30, 8, 0xC0),
+ F7188X_GPIO_BANK(40, 8, 0xB0),
+ F7188X_GPIO_BANK(50, 8, 0xA0),
+ F7188X_GPIO_BANK(60, 8, 0x90),
+ F7188X_GPIO_BANK(70, 8, 0x80),
+ F7188X_GPIO_BANK(80, 8, 0x88),
+};
+
static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
int err;
@@ -318,6 +332,10 @@ static int f7188x_gpio_probe(struct platform_device *pdev)
data->nr_bank = ARRAY_SIZE(f71889_gpio_bank);
data->bank = f71889_gpio_bank;
break;
+ case f81866:
+ data->nr_bank = ARRAY_SIZE(f81866_gpio_bank);
+ data->bank = f81866_gpio_bank;
+ break;
default:
return -ENODEV;
}
@@ -332,37 +350,16 @@ static int f7188x_gpio_probe(struct platform_device *pdev)
bank->chip.parent = &pdev->dev;
bank->data = data;
- err = gpiochip_add_data(&bank->chip, bank);
+ err = devm_gpiochip_add_data(&pdev->dev, &bank->chip, bank);
if (err) {
dev_err(&pdev->dev,
"Failed to register gpiochip %d: %d\n",
i, err);
- goto err_gpiochip;
+ return err;
}
}
return 0;
-
-err_gpiochip:
- for (i = i - 1; i >= 0; i--) {
- struct f7188x_gpio_bank *bank = &data->bank[i];
- gpiochip_remove(&bank->chip);
- }
-
- return err;
-}
-
-static int f7188x_gpio_remove(struct platform_device *pdev)
-{
- 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];
- gpiochip_remove(&bank->chip);
- }
-
- return 0;
}
static int __init f7188x_find(int addr, struct f7188x_sio *sio)
@@ -395,6 +392,9 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio)
case SIO_F71889_ID:
sio->type = f71889f;
break;
+ case SIO_F81866_ID:
+ sio->type = f81866;
+ break;
default:
pr_info(DRVNAME ": Unsupported Fintek device 0x%04x\n", devid);
goto err;
@@ -455,7 +455,6 @@ static struct platform_driver f7188x_gpio_driver = {
.name = DRVNAME,
},
.probe = f7188x_gpio_probe,
- .remove = f7188x_gpio_remove,
};
static int __init f7188x_gpio_init(void)
@@ -485,6 +484,6 @@ static void __exit f7188x_gpio_exit(void)
}
module_exit(f7188x_gpio_exit);
-MODULE_DESCRIPTION("GPIO driver for Super-I/O chips F71869, F71869A, F71882FG and F71889F");
+MODULE_DESCRIPTION("GPIO driver for Super-I/O chips F71869, F71869A, F71882FG, F71889F and F81866");
MODULE_AUTHOR("Simon Guinot <simon.guinot@sequanux.org>");
MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-ge.c b/drivers/gpio/gpio-ge.c
index cbbec838a..8650b2916 100644
--- a/drivers/gpio/gpio-ge.c
+++ b/drivers/gpio/gpio-ge.c
@@ -89,7 +89,7 @@ static int __init gef_gpio_probe(struct platform_device *pdev)
gc->of_node = pdev->dev.of_node;
/* This function adds a memory mapped GPIO chip */
- ret = gpiochip_add_data(gc, NULL);
+ ret = devm_gpiochip_add_data(&pdev->dev, gc, NULL);
if (ret)
goto err0;
diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c
index 2a4f2333a..54cddfa98 100644
--- a/drivers/gpio/gpio-generic.c
+++ b/drivers/gpio/gpio-generic.c
@@ -628,15 +628,7 @@ static int bgpio_pdev_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, gc);
- return gpiochip_add_data(gc, NULL);
-}
-
-static int bgpio_pdev_remove(struct platform_device *pdev)
-{
- struct gpio_chip *gc = platform_get_drvdata(pdev);
-
- gpiochip_remove(gc);
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, gc, NULL);
}
static const struct platform_device_id bgpio_id_table[] = {
@@ -657,7 +649,6 @@ static struct platform_driver bgpio_driver = {
},
.id_table = bgpio_id_table,
.probe = bgpio_pdev_probe,
- .remove = bgpio_pdev_remove,
};
module_platform_driver(bgpio_driver);
diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c
index a4893386a..4f6d64351 100644
--- a/drivers/gpio/gpio-ich.c
+++ b/drivers/gpio/gpio-ich.c
@@ -20,6 +20,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/gpio.h>
@@ -384,8 +385,8 @@ static struct ichx_desc avoton_desc = {
.use_outlvl_cache = true,
};
-static int ichx_gpio_request_regions(struct resource *res_base,
- const char *name, u8 use_gpio)
+static int ichx_gpio_request_regions(struct device *dev,
+ struct resource *res_base, const char *name, u8 use_gpio)
{
int i;
@@ -395,34 +396,12 @@ static int ichx_gpio_request_regions(struct resource *res_base,
for (i = 0; i < ARRAY_SIZE(ichx_priv.desc->regs[0]); i++) {
if (!(use_gpio & (1 << i)))
continue;
- if (!request_region(
+ if (!devm_request_region(dev,
res_base->start + ichx_priv.desc->regs[0][i],
ichx_priv.desc->reglen[i], name))
- goto request_err;
+ return -EBUSY;
}
return 0;
-
-request_err:
- /* Clean up: release already requested regions, if any */
- for (i--; i >= 0; i--) {
- if (!(use_gpio & (1 << i)))
- continue;
- release_region(res_base->start + ichx_priv.desc->regs[0][i],
- ichx_priv.desc->reglen[i]);
- }
- return -EBUSY;
-}
-
-static void ichx_gpio_release_regions(struct resource *res_base, u8 use_gpio)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(ichx_priv.desc->regs[0]); i++) {
- if (!(use_gpio & (1 << i)))
- continue;
- release_region(res_base->start + ichx_priv.desc->regs[0][i],
- ichx_priv.desc->reglen[i]);
- }
}
static int ichx_gpio_probe(struct platform_device *pdev)
@@ -468,7 +447,7 @@ static int ichx_gpio_probe(struct platform_device *pdev)
spin_lock_init(&ichx_priv.lock);
res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO);
ichx_priv.use_gpio = ich_info->use_gpio;
- err = ichx_gpio_request_regions(res_base, pdev->name,
+ err = ichx_gpio_request_regions(&pdev->dev, res_base, pdev->name,
ichx_priv.use_gpio);
if (err)
return err;
@@ -489,8 +468,8 @@ static int ichx_gpio_probe(struct platform_device *pdev)
goto init;
}
- if (!request_region(res_pm->start, resource_size(res_pm),
- pdev->name)) {
+ if (!devm_request_region(&pdev->dev, res_pm->start,
+ resource_size(res_pm), pdev->name)) {
pr_warn("ACPI BAR is busy, GPI 0 - 15 unavailable\n");
goto init;
}
@@ -502,31 +481,19 @@ init:
err = gpiochip_add_data(&ichx_priv.chip, NULL);
if (err) {
pr_err("Failed to register GPIOs\n");
- goto add_err;
+ return err;
}
pr_info("GPIO from %d to %d on %s\n", ichx_priv.chip.base,
ichx_priv.chip.base + ichx_priv.chip.ngpio - 1, DRV_NAME);
return 0;
-
-add_err:
- ichx_gpio_release_regions(ichx_priv.gpio_base, ichx_priv.use_gpio);
- if (ichx_priv.pm_base)
- release_region(ichx_priv.pm_base->start,
- resource_size(ichx_priv.pm_base));
- return err;
}
static int ichx_gpio_remove(struct platform_device *pdev)
{
gpiochip_remove(&ichx_priv.chip);
- ichx_gpio_release_regions(ichx_priv.gpio_base, ichx_priv.use_gpio);
- if (ichx_priv.pm_base)
- release_region(ichx_priv.pm_base->start,
- resource_size(ichx_priv.pm_base));
-
return 0;
}
diff --git a/drivers/gpio/gpio-iop.c b/drivers/gpio/gpio-iop.c
index fb65e5850..860c53592 100644
--- a/drivers/gpio/gpio-iop.c
+++ b/drivers/gpio/gpio-iop.c
@@ -114,7 +114,7 @@ static int iop3xx_gpio_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
- return gpiochip_add_data(&iop3xx_chip, NULL);
+ return devm_gpiochip_add_data(&pdev->dev, &iop3xx_chip, NULL);
}
static struct platform_driver iop3xx_gpio_driver = {
diff --git a/drivers/gpio/gpio-janz-ttl.c b/drivers/gpio/gpio-janz-ttl.c
index 482aa0353..a8d0a6b80 100644
--- a/drivers/gpio/gpio-janz-ttl.c
+++ b/drivers/gpio/gpio-janz-ttl.c
@@ -182,7 +182,7 @@ static int ttl_probe(struct platform_device *pdev)
gpio->base = -1;
gpio->ngpio = 20;
- ret = gpiochip_add_data(gpio, NULL);
+ ret = devm_gpiochip_add_data(dev, gpio, NULL);
if (ret) {
dev_err(dev, "unable to add GPIO chip\n");
return ret;
@@ -191,21 +191,11 @@ static int ttl_probe(struct platform_device *pdev)
return 0;
}
-static int ttl_remove(struct platform_device *pdev)
-{
- struct ttl_module *mod = platform_get_drvdata(pdev);
-
- gpiochip_remove(&mod->gpio);
-
- return 0;
-}
-
static struct platform_driver ttl_driver = {
.driver = {
.name = DRV_NAME,
},
.probe = ttl_probe,
- .remove = ttl_remove,
};
module_platform_driver(ttl_driver);
diff --git a/drivers/gpio/gpio-kempld.c b/drivers/gpio/gpio-kempld.c
index 01117747b..701f15103 100644
--- a/drivers/gpio/gpio-kempld.c
+++ b/drivers/gpio/gpio-kempld.c
@@ -178,7 +178,7 @@ static int kempld_gpio_probe(struct platform_device *pdev)
return -ENODEV;
}
- ret = gpiochip_add_data(chip, gpio);
+ ret = devm_gpiochip_add_data(dev, chip, gpio);
if (ret) {
dev_err(dev, "Could not register GPIO chip\n");
return ret;
@@ -190,20 +190,11 @@ static int kempld_gpio_probe(struct platform_device *pdev)
return 0;
}
-static int kempld_gpio_remove(struct platform_device *pdev)
-{
- struct kempld_gpio_data *gpio = platform_get_drvdata(pdev);
-
- gpiochip_remove(&gpio->chip);
- return 0;
-}
-
static struct platform_driver kempld_gpio_driver = {
.driver = {
.name = "kempld-gpio",
},
.probe = kempld_gpio_probe,
- .remove = kempld_gpio_remove,
};
module_platform_driver(kempld_gpio_driver);
diff --git a/drivers/gpio/gpio-ks8695.c b/drivers/gpio/gpio-ks8695.c
index 9f86ed9c7..179723d02 100644
--- a/drivers/gpio/gpio-ks8695.c
+++ b/drivers/gpio/gpio-ks8695.c
@@ -205,18 +205,6 @@ static int ks8695_gpio_to_irq(struct gpio_chip *gc, unsigned int pin)
return gpio_irq[pin];
}
-/*
- * Map IRQ number to GPIO line.
- */
-int irq_to_gpio(unsigned int irq)
-{
- if ((irq < KS8695_IRQ_EXTERN0) || (irq > KS8695_IRQ_EXTERN3))
- return -EINVAL;
-
- return (irq - KS8695_IRQ_EXTERN0);
-}
-EXPORT_SYMBOL(irq_to_gpio);
-
/* GPIOLIB interface */
static struct gpio_chip ks8695_gpio_chip = {
diff --git a/drivers/gpio/gpio-lp3943.c b/drivers/gpio/gpio-lp3943.c
index 1c8e2ae26..6dc672540 100644
--- a/drivers/gpio/gpio-lp3943.c
+++ b/drivers/gpio/gpio-lp3943.c
@@ -204,15 +204,8 @@ static int lp3943_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, lp3943_gpio);
- return gpiochip_add_data(&lp3943_gpio->chip, lp3943_gpio);
-}
-
-static int lp3943_gpio_remove(struct platform_device *pdev)
-{
- struct lp3943_gpio *lp3943_gpio = platform_get_drvdata(pdev);
-
- gpiochip_remove(&lp3943_gpio->chip);
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &lp3943_gpio->chip,
+ lp3943_gpio);
}
static const struct of_device_id lp3943_gpio_of_match[] = {
@@ -223,7 +216,6 @@ MODULE_DEVICE_TABLE(of, lp3943_gpio_of_match);
static struct platform_driver lp3943_gpio_driver = {
.probe = lp3943_gpio_probe,
- .remove = lp3943_gpio_remove,
.driver = {
.name = "lp3943-gpio",
.of_match_table = lp3943_gpio_of_match,
diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c
index 4cecf4ce9..d39014dae 100644
--- a/drivers/gpio/gpio-lpc32xx.c
+++ b/drivers/gpio/gpio-lpc32xx.c
@@ -547,7 +547,7 @@ static int lpc32xx_gpio_probe(struct platform_device *pdev)
lpc32xx_gpiochip[i].chip.of_gpio_n_cells = 3;
lpc32xx_gpiochip[i].chip.of_node = pdev->dev.of_node;
}
- gpiochip_add_data(&lpc32xx_gpiochip[i].chip,
+ devm_gpiochip_add_data(&pdev->dev, &lpc32xx_gpiochip[i].chip,
&lpc32xx_gpiochip[i]);
}
diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c
index 13107772b..9df015e85 100644
--- a/drivers/gpio/gpio-lynxpoint.c
+++ b/drivers/gpio/gpio-lynxpoint.c
@@ -370,7 +370,7 @@ static int lp_gpio_probe(struct platform_device *pdev)
gc->can_sleep = false;
gc->parent = dev;
- ret = gpiochip_add_data(gc, lg);
+ ret = devm_gpiochip_add_data(dev, gc, lg);
if (ret) {
dev_err(dev, "failed adding lp-gpio chip\n");
return ret;
@@ -439,9 +439,7 @@ MODULE_DEVICE_TABLE(acpi, lynxpoint_gpio_acpi_match);
static int lp_gpio_remove(struct platform_device *pdev)
{
- struct lp_gpio *lg = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
- gpiochip_remove(&lg->chip);
return 0;
}
diff --git a/drivers/gpio/gpio-mc9s08dz60.c b/drivers/gpio/gpio-mc9s08dz60.c
index ba22fb92a..14f252f9e 100644
--- a/drivers/gpio/gpio-mc9s08dz60.c
+++ b/drivers/gpio/gpio-mc9s08dz60.c
@@ -103,17 +103,7 @@ static int mc9s08dz60_probe(struct i2c_client *client,
mc9s->client = client;
i2c_set_clientdata(client, mc9s);
- return gpiochip_add_data(&mc9s->chip, mc9s);
-}
-
-static int mc9s08dz60_remove(struct i2c_client *client)
-{
- struct mc9s08dz60 *mc9s;
-
- mc9s = i2c_get_clientdata(client);
-
- gpiochip_remove(&mc9s->chip);
- return 0;
+ return devm_gpiochip_add_data(&client->dev, &mc9s->chip, mc9s);
}
static const struct i2c_device_id mc9s08dz60_id[] = {
@@ -128,7 +118,6 @@ static struct i2c_driver mc9s08dz60_i2c_driver = {
.name = "mc9s08dz60",
},
.probe = mc9s08dz60_probe,
- .remove = mc9s08dz60_remove,
.id_table = mc9s08dz60_id,
};
diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c
index c767879e4..47e486910 100644
--- a/drivers/gpio/gpio-mcp23s08.c
+++ b/drivers/gpio/gpio-mcp23s08.c
@@ -31,6 +31,7 @@
#define MCP_TYPE_S17 1
#define MCP_TYPE_008 2
#define MCP_TYPE_017 3
+#define MCP_TYPE_S18 4
/* Registers are all 8 bits wide.
*
@@ -48,6 +49,7 @@
# define IOCON_HAEN (1 << 3)
# define IOCON_ODR (1 << 2)
# define IOCON_INTPOL (1 << 1)
+# define IOCON_INTCC (1)
#define MCP_GPPU 0x06
#define MCP_INTF 0x07
#define MCP_INTCAP 0x08
@@ -617,6 +619,12 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
mcp->chip.ngpio = 16;
mcp->chip.label = "mcp23s17";
break;
+
+ case MCP_TYPE_S18:
+ mcp->ops = &mcp23s17_ops;
+ mcp->chip.ngpio = 16;
+ mcp->chip.label = "mcp23s18";
+ break;
#endif /* CONFIG_SPI_MASTER */
#if IS_ENABLED(CONFIG_I2C)
@@ -657,8 +665,7 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
of_property_read_bool(mcp->chip.parent->of_node,
"microchip,irq-active-high");
- if (type == MCP_TYPE_017)
- mirror = pdata->mirror;
+ mirror = pdata->mirror;
}
if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN) || mirror ||
@@ -674,6 +681,9 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
if (mirror)
status |= IOCON_MIRROR | (IOCON_MIRROR << 8);
+ if (type == MCP_TYPE_S18)
+ status |= IOCON_INTCC | (IOCON_INTCC << 8);
+
status = mcp->ops->write(mcp, MCP_IOCON, status);
if (status < 0)
goto fail;
@@ -735,6 +745,10 @@ static const struct of_device_id mcp23s08_spi_of_match[] = {
.compatible = "microchip,mcp23s17",
.data = (void *) MCP_TYPE_S17,
},
+ {
+ .compatible = "microchip,mcp23s18",
+ .data = (void *) MCP_TYPE_S18,
+ },
/* NOTE: The use of the mcp prefix is deprecated and will be removed. */
{
.compatible = "mcp,mcp23s08",
@@ -803,6 +817,8 @@ static int mcp230xx_probe(struct i2c_client *client,
pdata = devm_kzalloc(&client->dev,
sizeof(struct mcp23s08_platform_data),
GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
pdata->base = -1;
}
}
@@ -969,8 +985,8 @@ static int mcp23s08_probe(struct spi_device *spi)
goto fail;
if (pdata->base != -1)
- pdata->base += (type == MCP_TYPE_S17) ? 16 : 8;
- ngpio += (type == MCP_TYPE_S17) ? 16 : 8;
+ pdata->base += data->mcp[addr]->chip.ngpio;
+ ngpio += data->mcp[addr]->chip.ngpio;
}
data->ngpio = ngpio;
@@ -1012,6 +1028,7 @@ static int mcp23s08_remove(struct spi_device *spi)
static const struct spi_device_id mcp23s08_ids[] = {
{ "mcp23s08", MCP_TYPE_S08 },
{ "mcp23s17", MCP_TYPE_S17 },
+ { "mcp23s18", MCP_TYPE_S18 },
{ },
};
MODULE_DEVICE_TABLE(spi, mcp23s08_ids);
diff --git a/drivers/gpio/gpio-menz127.c b/drivers/gpio/gpio-menz127.c
new file mode 100644
index 000000000..c5c9599a3
--- /dev/null
+++ b/drivers/gpio/gpio-menz127.c
@@ -0,0 +1,199 @@
+/*
+ * MEN 16Z127 GPIO driver
+ *
+ * Copyright (C) 2016 MEN Mikroelektronik GmbH (www.men.de)
+ *
+ * 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; version 2 of the License.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/mcb.h>
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
+
+#define MEN_Z127_CTRL 0x00
+#define MEN_Z127_PSR 0x04
+#define MEN_Z127_IRQR 0x08
+#define MEN_Z127_GPIODR 0x0c
+#define MEN_Z127_IER1 0x10
+#define MEN_Z127_IER2 0x14
+#define MEN_Z127_DBER 0x18
+#define MEN_Z127_ODER 0x1C
+#define GPIO_TO_DBCNT_REG(gpio) ((gpio * 4) + 0x80)
+
+#define MEN_Z127_DB_MIN_US 50
+/* 16 bit compare register. Each bit represents 50us */
+#define MEN_Z127_DB_MAX_US (0xffff * MEN_Z127_DB_MIN_US)
+#define MEN_Z127_DB_IN_RANGE(db) ((db >= MEN_Z127_DB_MIN_US) && \
+ (db <= MEN_Z127_DB_MAX_US))
+
+struct men_z127_gpio {
+ struct gpio_chip gc;
+ void __iomem *reg_base;
+ struct mcb_device *mdev;
+ struct resource *mem;
+};
+
+static int men_z127_debounce(struct gpio_chip *gc, unsigned gpio,
+ unsigned debounce)
+{
+ struct men_z127_gpio *priv = gpiochip_get_data(gc);
+ struct device *dev = &priv->mdev->dev;
+ unsigned int rnd;
+ u32 db_en, db_cnt;
+
+ if (!MEN_Z127_DB_IN_RANGE(debounce)) {
+ dev_err(dev, "debounce value %u out of range", debounce);
+ return -EINVAL;
+ }
+
+ if (debounce > 0) {
+ /* round up or down depending on MSB-1 */
+ rnd = fls(debounce) - 1;
+
+ if (rnd && (debounce & BIT(rnd - 1)))
+ debounce = round_up(debounce, MEN_Z127_DB_MIN_US);
+ else
+ debounce = round_down(debounce, MEN_Z127_DB_MIN_US);
+
+ if (debounce > MEN_Z127_DB_MAX_US)
+ debounce = MEN_Z127_DB_MAX_US;
+
+ /* 50us per register unit */
+ debounce /= 50;
+ }
+
+ spin_lock(&gc->bgpio_lock);
+
+ db_en = readl(priv->reg_base + MEN_Z127_DBER);
+
+ if (debounce == 0) {
+ db_en &= ~BIT(gpio);
+ db_cnt = 0;
+ } else {
+ db_en |= BIT(gpio);
+ db_cnt = debounce;
+ }
+
+ writel(db_en, priv->reg_base + MEN_Z127_DBER);
+ writel(db_cnt, priv->reg_base + GPIO_TO_DBCNT_REG(gpio));
+
+ spin_unlock(&gc->bgpio_lock);
+
+ return 0;
+}
+
+static int men_z127_request(struct gpio_chip *gc, unsigned gpio_pin)
+{
+ struct men_z127_gpio *priv = gpiochip_get_data(gc);
+ u32 od_en;
+
+ if (gpio_pin >= gc->ngpio)
+ return -EINVAL;
+
+ spin_lock(&gc->bgpio_lock);
+ od_en = readl(priv->reg_base + MEN_Z127_ODER);
+
+ if (gpiochip_line_is_open_drain(gc, gpio_pin))
+ od_en |= BIT(gpio_pin);
+ else
+ od_en &= ~BIT(gpio_pin);
+
+ writel(od_en, priv->reg_base + MEN_Z127_ODER);
+ spin_unlock(&gc->bgpio_lock);
+
+ return 0;
+}
+
+static int men_z127_probe(struct mcb_device *mdev,
+ const struct mcb_device_id *id)
+{
+ struct men_z127_gpio *men_z127_gpio;
+ struct device *dev = &mdev->dev;
+ int ret;
+
+ men_z127_gpio = devm_kzalloc(dev, sizeof(struct men_z127_gpio),
+ GFP_KERNEL);
+ if (!men_z127_gpio)
+ return -ENOMEM;
+
+ men_z127_gpio->mem = mcb_request_mem(mdev, dev_name(dev));
+ if (IS_ERR(men_z127_gpio->mem)) {
+ dev_err(dev, "failed to request device memory");
+ return PTR_ERR(men_z127_gpio->mem);
+ }
+
+ men_z127_gpio->reg_base = ioremap(men_z127_gpio->mem->start,
+ resource_size(men_z127_gpio->mem));
+ if (men_z127_gpio->reg_base == NULL) {
+ ret = -ENXIO;
+ goto err_release;
+ }
+
+ men_z127_gpio->mdev = mdev;
+ mcb_set_drvdata(mdev, men_z127_gpio);
+
+ ret = bgpio_init(&men_z127_gpio->gc, &mdev->dev, 4,
+ men_z127_gpio->reg_base + MEN_Z127_PSR,
+ men_z127_gpio->reg_base + MEN_Z127_CTRL,
+ NULL,
+ men_z127_gpio->reg_base + MEN_Z127_GPIODR,
+ NULL, 0);
+ if (ret)
+ goto err_unmap;
+
+ men_z127_gpio->gc.set_debounce = men_z127_debounce;
+ men_z127_gpio->gc.request = men_z127_request;
+
+ ret = gpiochip_add_data(&men_z127_gpio->gc, men_z127_gpio);
+ if (ret) {
+ dev_err(dev, "failed to register MEN 16Z127 GPIO controller");
+ goto err_unmap;
+ }
+
+ dev_info(dev, "MEN 16Z127 GPIO driver registered");
+
+ return 0;
+
+err_unmap:
+ iounmap(men_z127_gpio->reg_base);
+err_release:
+ mcb_release_mem(men_z127_gpio->mem);
+ return ret;
+}
+
+static void men_z127_remove(struct mcb_device *mdev)
+{
+ struct men_z127_gpio *men_z127_gpio = mcb_get_drvdata(mdev);
+
+ gpiochip_remove(&men_z127_gpio->gc);
+ iounmap(men_z127_gpio->reg_base);
+ mcb_release_mem(men_z127_gpio->mem);
+}
+
+static const struct mcb_device_id men_z127_ids[] = {
+ { .device = 0x7f },
+ { }
+};
+MODULE_DEVICE_TABLE(mcb, men_z127_ids);
+
+static struct mcb_driver men_z127_driver = {
+ .driver = {
+ .name = "z127-gpio",
+ .owner = THIS_MODULE,
+ },
+ .probe = men_z127_probe,
+ .remove = men_z127_remove,
+ .id_table = men_z127_ids,
+};
+module_mcb_driver(men_z127_driver);
+
+MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
+MODULE_DESCRIPTION("MEN 16z127 GPIO Controller");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mcb:16z127");
diff --git a/drivers/gpio/gpio-moxart.c b/drivers/gpio/gpio-moxart.c
index ca604538e..f02d0b490 100644
--- a/drivers/gpio/gpio-moxart.c
+++ b/drivers/gpio/gpio-moxart.c
@@ -57,13 +57,10 @@ static int moxart_gpio_probe(struct platform_device *pdev)
gc->label = "moxart-gpio";
gc->request = gpiochip_generic_request;
gc->free = gpiochip_generic_free;
- gc->bgpio_data = gc->read_reg(gc->reg_set);
gc->base = 0;
- gc->ngpio = 32;
- gc->parent = dev;
gc->owner = THIS_MODULE;
- ret = gpiochip_add_data(gc, NULL);
+ ret = devm_gpiochip_add_data(dev, gc, NULL);
if (ret) {
dev_err(dev, "%s: gpiochip_add failed\n",
dev->of_node->full_name);
diff --git a/drivers/gpio/gpio-mpc5200.c b/drivers/gpio/gpio-mpc5200.c
index 0e5a6709f..fc10cf596 100644
--- a/drivers/gpio/gpio-mpc5200.c
+++ b/drivers/gpio/gpio-mpc5200.c
@@ -25,7 +25,6 @@
#include <linux/of_platform.h>
#include <linux/module.h>
-#include <asm/gpio.h>
#include <asm/mpc52xx.h>
#include <sysdev/fsl_soc.h>
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
index 9d40787e6..425501c39 100644
--- a/drivers/gpio/gpio-mpc8xxx.c
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -1,7 +1,8 @@
/*
- * GPIOs on MPC512x/8349/8572/8610 and compatible
+ * GPIOs on MPC512x/8349/8572/8610/QorIQ and compatible
*
* Copyright (C) 2008 Peter Korsgaard <jacmet@sunsite.dk>
+ * Copyright (C) 2016 Freescale Semiconductor Inc.
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
@@ -14,11 +15,12 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
+#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
-#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/irq.h>
+#include <linux/gpio/driver.h>
#define MPC8XXX_GPIO_PINS 32
@@ -31,32 +33,17 @@
#define GPIO_ICR2 0x18
struct mpc8xxx_gpio_chip {
- struct of_mm_gpio_chip mm_gc;
+ struct gpio_chip gc;
+ void __iomem *regs;
raw_spinlock_t lock;
- /*
- * shadowed data register to be able to clear/set output pins in
- * open drain mode safely
- */
- u32 data;
+ int (*direction_output)(struct gpio_chip *chip,
+ unsigned offset, int value);
+
struct irq_domain *irq;
unsigned int irqn;
- const void *of_dev_id_data;
};
-static inline u32 mpc8xxx_gpio2mask(unsigned int gpio)
-{
- return 1u << (MPC8XXX_GPIO_PINS - 1 - gpio);
-}
-
-static void mpc8xxx_gpio_save_regs(struct of_mm_gpio_chip *mm)
-{
- struct mpc8xxx_gpio_chip *mpc8xxx_gc =
- container_of(mm, struct mpc8xxx_gpio_chip, mm_gc);
-
- mpc8xxx_gc->data = in_be32(mm->regs + GPIO_DAT);
-}
-
/* Workaround GPIO 1 errata on MPC8572/MPC8536. The status of GPIOs
* defined as output cannot be determined by reading GPDAT register,
* so we use shadow data register instead. The status of input pins
@@ -65,117 +52,36 @@ static void mpc8xxx_gpio_save_regs(struct of_mm_gpio_chip *mm)
static int mpc8572_gpio_get(struct gpio_chip *gc, unsigned int gpio)
{
u32 val;
- struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
struct mpc8xxx_gpio_chip *mpc8xxx_gc = gpiochip_get_data(gc);
u32 out_mask, out_shadow;
- out_mask = in_be32(mm->regs + GPIO_DIR);
-
- val = in_be32(mm->regs + GPIO_DAT) & ~out_mask;
- out_shadow = mpc8xxx_gc->data & out_mask;
-
- return !!((val | out_shadow) & mpc8xxx_gpio2mask(gpio));
-}
-
-static int mpc8xxx_gpio_get(struct gpio_chip *gc, unsigned int gpio)
-{
- struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
-
- return in_be32(mm->regs + GPIO_DAT) & mpc8xxx_gpio2mask(gpio);
-}
-
-static void mpc8xxx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
-{
- struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
- struct mpc8xxx_gpio_chip *mpc8xxx_gc = gpiochip_get_data(gc);
- unsigned long flags;
-
- raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
-
- if (val)
- mpc8xxx_gc->data |= mpc8xxx_gpio2mask(gpio);
- else
- mpc8xxx_gc->data &= ~mpc8xxx_gpio2mask(gpio);
-
- out_be32(mm->regs + GPIO_DAT, mpc8xxx_gc->data);
-
- raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
-}
-
-static void mpc8xxx_gpio_set_multiple(struct gpio_chip *gc,
- unsigned long *mask, unsigned long *bits)
-{
- struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
- struct mpc8xxx_gpio_chip *mpc8xxx_gc = gpiochip_get_data(gc);
- unsigned long flags;
- int i;
-
- raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
-
- for (i = 0; i < gc->ngpio; i++) {
- if (*mask == 0)
- break;
- if (__test_and_clear_bit(i, mask)) {
- if (test_bit(i, bits))
- mpc8xxx_gc->data |= mpc8xxx_gpio2mask(i);
- else
- mpc8xxx_gc->data &= ~mpc8xxx_gpio2mask(i);
- }
- }
-
- out_be32(mm->regs + GPIO_DAT, mpc8xxx_gc->data);
-
- raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
-}
-
-static int mpc8xxx_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
-{
- struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
- struct mpc8xxx_gpio_chip *mpc8xxx_gc = gpiochip_get_data(gc);
- unsigned long flags;
+ out_mask = gc->read_reg(mpc8xxx_gc->regs + GPIO_DIR);
+ val = gc->read_reg(mpc8xxx_gc->regs + GPIO_DAT) & ~out_mask;
+ out_shadow = gc->bgpio_data & out_mask;
- raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
-
- clrbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio));
-
- raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
-
- return 0;
+ return !!((val | out_shadow) & gc->pin2mask(gc, gpio));
}
-static int mpc8xxx_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+static int mpc5121_gpio_dir_out(struct gpio_chip *gc,
+ unsigned int gpio, int val)
{
- struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
struct mpc8xxx_gpio_chip *mpc8xxx_gc = gpiochip_get_data(gc);
- unsigned long flags;
-
- mpc8xxx_gpio_set(gc, gpio, val);
-
- raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
-
- setbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio));
-
- raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
-
- return 0;
-}
-
-static int mpc5121_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
-{
/* GPIO 28..31 are input only on MPC5121 */
if (gpio >= 28)
return -EINVAL;
- return mpc8xxx_gpio_dir_out(gc, gpio, val);
+ return mpc8xxx_gc->direction_output(gc, gpio, val);
}
-static int mpc5125_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+static int mpc5125_gpio_dir_out(struct gpio_chip *gc,
+ unsigned int gpio, int val)
{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = gpiochip_get_data(gc);
/* GPIO 0..3 are input only on MPC5125 */
if (gpio <= 3)
return -EINVAL;
- return mpc8xxx_gpio_dir_out(gc, gpio, val);
+ return mpc8xxx_gc->direction_output(gc, gpio, val);
}
static int mpc8xxx_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
@@ -192,10 +98,11 @@ static void mpc8xxx_gpio_irq_cascade(struct irq_desc *desc)
{
struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
- struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+ struct gpio_chip *gc = &mpc8xxx_gc->gc;
unsigned int mask;
- mask = in_be32(mm->regs + GPIO_IER) & in_be32(mm->regs + GPIO_IMR);
+ mask = gc->read_reg(mpc8xxx_gc->regs + GPIO_IER)
+ & gc->read_reg(mpc8xxx_gc->regs + GPIO_IMR);
if (mask)
generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq,
32 - ffs(mask)));
@@ -206,12 +113,14 @@ static void mpc8xxx_gpio_irq_cascade(struct irq_desc *desc)
static void mpc8xxx_irq_unmask(struct irq_data *d)
{
struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
- struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+ struct gpio_chip *gc = &mpc8xxx_gc->gc;
unsigned long flags;
raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
- setbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+ gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR,
+ gc->read_reg(mpc8xxx_gc->regs + GPIO_IMR)
+ | gc->pin2mask(gc, irqd_to_hwirq(d)));
raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
}
@@ -219,12 +128,14 @@ static void mpc8xxx_irq_unmask(struct irq_data *d)
static void mpc8xxx_irq_mask(struct irq_data *d)
{
struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
- struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+ struct gpio_chip *gc = &mpc8xxx_gc->gc;
unsigned long flags;
raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
- clrbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+ gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR,
+ gc->read_reg(mpc8xxx_gc->regs + GPIO_IMR)
+ & ~(gc->pin2mask(gc, irqd_to_hwirq(d))));
raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
}
@@ -232,29 +143,32 @@ static void mpc8xxx_irq_mask(struct irq_data *d)
static void mpc8xxx_irq_ack(struct irq_data *d)
{
struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
- struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+ struct gpio_chip *gc = &mpc8xxx_gc->gc;
- out_be32(mm->regs + GPIO_IER, mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+ gc->write_reg(mpc8xxx_gc->regs + GPIO_IER,
+ gc->pin2mask(gc, irqd_to_hwirq(d)));
}
static int mpc8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
{
struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
- struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+ struct gpio_chip *gc = &mpc8xxx_gc->gc;
unsigned long flags;
switch (flow_type) {
case IRQ_TYPE_EDGE_FALLING:
raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
- setbits32(mm->regs + GPIO_ICR,
- mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+ gc->write_reg(mpc8xxx_gc->regs + GPIO_ICR,
+ gc->read_reg(mpc8xxx_gc->regs + GPIO_ICR)
+ | gc->pin2mask(gc, irqd_to_hwirq(d)));
raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
break;
case IRQ_TYPE_EDGE_BOTH:
raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
- clrbits32(mm->regs + GPIO_ICR,
- mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+ gc->write_reg(mpc8xxx_gc->regs + GPIO_ICR,
+ gc->read_reg(mpc8xxx_gc->regs + GPIO_ICR)
+ & ~(gc->pin2mask(gc, irqd_to_hwirq(d))));
raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
break;
@@ -268,17 +182,17 @@ static int mpc8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
static int mpc512x_irq_set_type(struct irq_data *d, unsigned int flow_type)
{
struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
- struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+ struct gpio_chip *gc = &mpc8xxx_gc->gc;
unsigned long gpio = irqd_to_hwirq(d);
void __iomem *reg;
unsigned int shift;
unsigned long flags;
if (gpio < 16) {
- reg = mm->regs + GPIO_ICR;
+ reg = mpc8xxx_gc->regs + GPIO_ICR;
shift = (15 - gpio) * 2;
} else {
- reg = mm->regs + GPIO_ICR2;
+ reg = mpc8xxx_gc->regs + GPIO_ICR2;
shift = (15 - (gpio % 16)) * 2;
}
@@ -286,20 +200,22 @@ static int mpc512x_irq_set_type(struct irq_data *d, unsigned int flow_type)
case IRQ_TYPE_EDGE_FALLING:
case IRQ_TYPE_LEVEL_LOW:
raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
- clrsetbits_be32(reg, 3 << shift, 2 << shift);
+ gc->write_reg(reg, (gc->read_reg(reg) & ~(3 << shift))
+ | (2 << shift));
raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
break;
case IRQ_TYPE_EDGE_RISING:
case IRQ_TYPE_LEVEL_HIGH:
raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
- clrsetbits_be32(reg, 3 << shift, 1 << shift);
+ gc->write_reg(reg, (gc->read_reg(reg) & ~(3 << shift))
+ | (1 << shift));
raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
break;
case IRQ_TYPE_EDGE_BOTH:
raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
- clrbits32(reg, 3 << shift);
+ gc->write_reg(reg, (gc->read_reg(reg) & ~(3 << shift)));
raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
break;
@@ -354,8 +270,6 @@ static const struct mpc8xxx_gpio_devtype mpc8572_gpio_devtype = {
};
static const struct mpc8xxx_gpio_devtype mpc8xxx_gpio_devtype_default = {
- .gpio_dir_out = mpc8xxx_gpio_dir_out,
- .gpio_get = mpc8xxx_gpio_get,
.irq_set_type = mpc8xxx_irq_set_type,
};
@@ -374,9 +288,7 @@ static int mpc8xxx_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct mpc8xxx_gpio_chip *mpc8xxx_gc;
- struct of_mm_gpio_chip *mm_gc;
- struct gpio_chip *gc;
- const struct of_device_id *id;
+ struct gpio_chip *gc;
const struct mpc8xxx_gpio_devtype *devtype =
of_device_get_match_data(&pdev->dev);
int ret;
@@ -389,12 +301,34 @@ static int mpc8xxx_probe(struct platform_device *pdev)
raw_spin_lock_init(&mpc8xxx_gc->lock);
- mm_gc = &mpc8xxx_gc->mm_gc;
- gc = &mm_gc->gc;
+ mpc8xxx_gc->regs = of_iomap(np, 0);
+ if (!mpc8xxx_gc->regs)
+ return -ENOMEM;
+
+ gc = &mpc8xxx_gc->gc;
+
+ if (of_property_read_bool(np, "little-endian")) {
+ ret = bgpio_init(gc, &pdev->dev, 4,
+ mpc8xxx_gc->regs + GPIO_DAT,
+ NULL, NULL,
+ mpc8xxx_gc->regs + GPIO_DIR, NULL,
+ BGPIOF_BIG_ENDIAN);
+ if (ret)
+ goto err;
+ dev_dbg(&pdev->dev, "GPIO registers are LITTLE endian\n");
+ } else {
+ ret = bgpio_init(gc, &pdev->dev, 4,
+ mpc8xxx_gc->regs + GPIO_DAT,
+ NULL, NULL,
+ mpc8xxx_gc->regs + GPIO_DIR, NULL,
+ BGPIOF_BIG_ENDIAN
+ | BGPIOF_BIG_ENDIAN_BYTE_ORDER);
+ if (ret)
+ goto err;
+ dev_dbg(&pdev->dev, "GPIO registers are BIG endian\n");
+ }
- mm_gc->save_regs = mpc8xxx_gpio_save_regs;
- gc->ngpio = MPC8XXX_GPIO_PINS;
- gc->direction_input = mpc8xxx_gpio_dir_in;
+ mpc8xxx_gc->direction_output = gc->direction_output;
if (!devtype)
devtype = &mpc8xxx_gpio_devtype_default;
@@ -405,18 +339,22 @@ static int mpc8xxx_probe(struct platform_device *pdev)
*/
mpc8xxx_irq_chip.irq_set_type = devtype->irq_set_type;
- gc->direction_output = devtype->gpio_dir_out ?: mpc8xxx_gpio_dir_out;
- gc->get = devtype->gpio_get ?: mpc8xxx_gpio_get;
- gc->set = mpc8xxx_gpio_set;
- gc->set_multiple = mpc8xxx_gpio_set_multiple;
+ if (devtype->gpio_dir_out)
+ gc->direction_output = devtype->gpio_dir_out;
+ if (devtype->gpio_get)
+ gc->get = devtype->gpio_get;
+
gc->to_irq = mpc8xxx_gpio_to_irq;
- ret = of_mm_gpiochip_add_data(np, mm_gc, mpc8xxx_gc);
- if (ret)
- return ret;
+ ret = gpiochip_add_data(gc, mpc8xxx_gc);
+ if (ret) {
+ pr_err("%s: GPIO chip registration failed with status %d\n",
+ np->full_name, ret);
+ goto err;
+ }
mpc8xxx_gc->irqn = irq_of_parse_and_map(np, 0);
- if (mpc8xxx_gc->irqn == NO_IRQ)
+ if (!mpc8xxx_gc->irqn)
return 0;
mpc8xxx_gc->irq = irq_domain_add_linear(np, MPC8XXX_GPIO_PINS,
@@ -424,18 +362,16 @@ static int mpc8xxx_probe(struct platform_device *pdev)
if (!mpc8xxx_gc->irq)
return 0;
- id = of_match_node(mpc8xxx_gpio_ids, np);
- if (id)
- mpc8xxx_gc->of_dev_id_data = id->data;
-
/* ack and mask all irqs */
- out_be32(mm_gc->regs + GPIO_IER, 0xffffffff);
- out_be32(mm_gc->regs + GPIO_IMR, 0);
+ gc->write_reg(mpc8xxx_gc->regs + GPIO_IER, 0xffffffff);
+ gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR, 0);
irq_set_chained_handler_and_data(mpc8xxx_gc->irqn,
mpc8xxx_gpio_irq_cascade, mpc8xxx_gc);
-
return 0;
+err:
+ iounmap(mpc8xxx_gc->regs);
+ return ret;
}
static int mpc8xxx_remove(struct platform_device *pdev)
@@ -447,7 +383,8 @@ static int mpc8xxx_remove(struct platform_device *pdev)
irq_domain_remove(mpc8xxx_gc->irq);
}
- of_mm_gpiochip_remove(&mpc8xxx_gc->mm_gc);
+ gpiochip_remove(&mpc8xxx_gc->gc);
+ iounmap(mpc8xxx_gc->regs);
return 0;
}
diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
index a5eacc1df..11c6582ef 100644
--- a/drivers/gpio/gpio-mvebu.c
+++ b/drivers/gpio/gpio-mvebu.c
@@ -756,7 +756,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
BUG();
}
- gpiochip_add_data(&mvchip->chip, mvchip);
+ devm_gpiochip_add_data(&pdev->dev, &mvchip->chip, mvchip);
/* Some gpio controllers do not provide irq support */
if (!of_irq_count(np))
@@ -777,16 +777,14 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
mvchip->irqbase = irq_alloc_descs(-1, 0, ngpios, -1);
if (mvchip->irqbase < 0) {
dev_err(&pdev->dev, "no irqs\n");
- err = mvchip->irqbase;
- goto err_gpiochip_add;
+ return mvchip->irqbase;
}
gc = irq_alloc_generic_chip("mvebu_gpio_irq", 2, mvchip->irqbase,
mvchip->membase, handle_level_irq);
if (!gc) {
dev_err(&pdev->dev, "Cannot allocate generic irq_chip\n");
- err = -ENOMEM;
- goto err_gpiochip_add;
+ return -ENOMEM;
}
gc->private = mvchip;
@@ -828,9 +826,6 @@ err_generic_chip:
IRQ_LEVEL | IRQ_NOPROBE);
kfree(gc);
-err_gpiochip_add:
- gpiochip_remove(&mvchip->chip);
-
return err;
}
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
index 7fd21cb53..1b342a384 100644
--- a/drivers/gpio/gpio-mxc.c
+++ b/drivers/gpio/gpio-mxc.c
@@ -462,14 +462,14 @@ static int mxc_gpio_probe(struct platform_device *pdev)
port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
pdev->id * 32;
- err = gpiochip_add_data(&port->gc, port);
+ err = devm_gpiochip_add_data(&pdev->dev, &port->gc, port);
if (err)
goto out_bgio;
irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id());
if (irq_base < 0) {
err = irq_base;
- goto out_gpiochip_remove;
+ goto out_bgio;
}
port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
@@ -492,8 +492,6 @@ out_irqdomain_remove:
irq_domain_remove(port->domain);
out_irqdesc_free:
irq_free_descs(irq_base, 32);
-out_gpiochip_remove:
- gpiochip_remove(&port->gc);
out_bgio:
dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err);
return err;
diff --git a/drivers/gpio/gpio-octeon.c b/drivers/gpio/gpio-octeon.c
index 7665ebcd0..47aead1ed 100644
--- a/drivers/gpio/gpio-octeon.c
+++ b/drivers/gpio/gpio-octeon.c
@@ -117,7 +117,7 @@ static int octeon_gpio_probe(struct platform_device *pdev)
chip->get = octeon_gpio_get;
chip->direction_output = octeon_gpio_dir_out;
chip->set = octeon_gpio_set;
- err = gpiochip_add_data(chip, gpio);
+ err = devm_gpiochip_add_data(&pdev->dev, chip, gpio);
if (err)
goto out;
@@ -126,13 +126,6 @@ out:
return err;
}
-static int octeon_gpio_remove(struct platform_device *pdev)
-{
- struct gpio_chip *chip = dev_get_platdata(&pdev->dev);
- gpiochip_remove(chip);
- return 0;
-}
-
static struct of_device_id octeon_gpio_match[] = {
{
.compatible = "cavium,octeon-3860-gpio",
@@ -147,7 +140,6 @@ static struct platform_driver octeon_gpio_driver = {
.of_match_table = octeon_gpio_match,
},
.probe = octeon_gpio_probe,
- .remove = octeon_gpio_remove,
};
module_platform_driver(octeon_gpio_driver);
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index 189f672be..551dfa9d9 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -66,7 +66,6 @@ struct gpio_bank {
u32 irq_usage;
u32 dbck_enable_mask;
bool dbck_enabled;
- struct device *dev;
bool is_mpuio;
bool dbck_flag;
bool loses_context;
@@ -627,7 +626,7 @@ static int omap_set_gpio_wakeup(struct gpio_bank *bank, unsigned offset,
unsigned long flags;
if (bank->non_wakeup_gpios & gpio_bit) {
- dev_err(bank->dev,
+ dev_err(bank->chip.parent,
"Unable to modify wakeup on non-wakeup GPIO%d\n",
offset);
return -EINVAL;
@@ -669,7 +668,7 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
* enable the bank module.
*/
if (!BANK_USED(bank))
- pm_runtime_get_sync(bank->dev);
+ pm_runtime_get_sync(chip->parent);
raw_spin_lock_irqsave(&bank->lock, flags);
omap_enable_gpio_module(bank, offset);
@@ -698,7 +697,7 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
* disable the bank module.
*/
if (!BANK_USED(bank))
- pm_runtime_put(bank->dev);
+ pm_runtime_put(chip->parent);
}
/*
@@ -723,7 +722,7 @@ static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
if (WARN_ON(!isr_reg))
goto exit;
- pm_runtime_get_sync(bank->dev);
+ pm_runtime_get_sync(bank->chip.parent);
while (1) {
u32 isr_saved, level_mask = 0;
@@ -776,7 +775,7 @@ static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
}
}
exit:
- pm_runtime_put(bank->dev);
+ pm_runtime_put(bank->chip.parent);
return IRQ_HANDLED;
}
@@ -826,7 +825,7 @@ static void omap_gpio_irq_bus_lock(struct irq_data *data)
struct gpio_bank *bank = omap_irq_data_get_bank(data);
if (!BANK_USED(bank))
- pm_runtime_get_sync(bank->dev);
+ pm_runtime_get_sync(bank->chip.parent);
}
static void gpio_irq_bus_sync_unlock(struct irq_data *data)
@@ -838,7 +837,7 @@ static void gpio_irq_bus_sync_unlock(struct irq_data *data)
* disable the bank module.
*/
if (!BANK_USED(bank))
- pm_runtime_put(bank->dev);
+ pm_runtime_put(bank->chip.parent);
}
static void omap_gpio_ack_irq(struct irq_data *d)
@@ -1100,7 +1099,8 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
ret = gpiochip_add_data(&bank->chip, bank);
if (ret) {
- dev_err(bank->dev, "Could not register gpio chip %d\n", ret);
+ dev_err(bank->chip.parent,
+ "Could not register gpio chip %d\n", ret);
return ret;
}
@@ -1114,7 +1114,7 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
*/
irq_base = irq_alloc_descs(-1, 0, bank->width, 0);
if (irq_base < 0) {
- dev_err(bank->dev, "Couldn't allocate IRQ numbers\n");
+ dev_err(bank->chip.parent, "Couldn't allocate IRQ numbers\n");
return -ENODEV;
}
#endif
@@ -1131,15 +1131,17 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
IRQ_TYPE_NONE);
if (ret) {
- dev_err(bank->dev, "Couldn't add irqchip to gpiochip %d\n", ret);
+ dev_err(bank->chip.parent,
+ "Couldn't add irqchip to gpiochip %d\n", ret);
gpiochip_remove(&bank->chip);
return -ENODEV;
}
gpiochip_set_chained_irqchip(&bank->chip, irqc, bank->irq, NULL);
- ret = devm_request_irq(bank->dev, bank->irq, omap_gpio_irq_handler,
- 0, dev_name(bank->dev), bank);
+ ret = devm_request_irq(bank->chip.parent, bank->irq,
+ omap_gpio_irq_handler,
+ 0, dev_name(bank->chip.parent), bank);
if (ret)
gpiochip_remove(&bank->chip);
@@ -1196,7 +1198,6 @@ static int omap_gpio_probe(struct platform_device *pdev)
return bank->irq;
}
- bank->dev = dev;
bank->chip.parent = dev;
bank->chip.owner = THIS_MODULE;
bank->dbck_flag = pdata->dbck_flag;
@@ -1235,9 +1236,9 @@ static int omap_gpio_probe(struct platform_device *pdev)
}
if (bank->dbck_flag) {
- bank->dbck = devm_clk_get(bank->dev, "dbclk");
+ bank->dbck = devm_clk_get(dev, "dbclk");
if (IS_ERR(bank->dbck)) {
- dev_err(bank->dev,
+ dev_err(dev,
"Could not get gpio dbck. Disable debounce\n");
bank->dbck_flag = false;
} else {
@@ -1247,9 +1248,9 @@ static int omap_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, bank);
- pm_runtime_enable(bank->dev);
- pm_runtime_irq_safe(bank->dev);
- pm_runtime_get_sync(bank->dev);
+ pm_runtime_enable(dev);
+ pm_runtime_irq_safe(dev);
+ pm_runtime_get_sync(dev);
if (bank->is_mpuio)
omap_mpuio_init(bank);
@@ -1258,14 +1259,14 @@ static int omap_gpio_probe(struct platform_device *pdev)
ret = omap_gpio_chip_init(bank, irqc);
if (ret) {
- pm_runtime_put_sync(bank->dev);
- pm_runtime_disable(bank->dev);
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
return ret;
}
omap_gpio_show_rev(bank);
- pm_runtime_put(bank->dev);
+ pm_runtime_put(dev);
list_add_tail(&bank->node, &omap_gpio_list);
@@ -1278,7 +1279,7 @@ static int omap_gpio_remove(struct platform_device *pdev)
list_del(&bank->node);
gpiochip_remove(&bank->chip);
- pm_runtime_disable(bank->dev);
+ pm_runtime_disable(&pdev->dev);
if (bank->dbck_flag)
clk_unprepare(bank->dbck);
@@ -1348,7 +1349,7 @@ static int omap_gpio_runtime_suspend(struct device *dev)
update_gpio_context_count:
if (bank->get_context_loss_count)
bank->context_loss_count =
- bank->get_context_loss_count(bank->dev);
+ bank->get_context_loss_count(dev);
omap_gpio_dbck_disable(bank);
raw_spin_unlock_irqrestore(&bank->lock, flags);
@@ -1378,7 +1379,7 @@ static int omap_gpio_runtime_resume(struct device *dev)
if (bank->get_context_loss_count)
bank->context_loss_count =
- bank->get_context_loss_count(bank->dev);
+ bank->get_context_loss_count(dev);
}
omap_gpio_dbck_enable(bank);
@@ -1398,7 +1399,7 @@ static int omap_gpio_runtime_resume(struct device *dev)
if (!bank->get_context_loss_count) {
omap_gpio_restore_context(bank);
} else {
- c = bank->get_context_loss_count(bank->dev);
+ c = bank->get_context_loss_count(dev);
if (c != bank->context_loss_count) {
omap_gpio_restore_context(bank);
} else {
@@ -1481,7 +1482,7 @@ void omap2_gpio_prepare_for_idle(int pwr_mode)
bank->power_mode = pwr_mode;
- pm_runtime_put_sync_suspend(bank->dev);
+ pm_runtime_put_sync_suspend(bank->chip.parent);
}
}
@@ -1493,7 +1494,7 @@ void omap2_gpio_resume_after_idle(void)
if (!BANK_USED(bank) || !bank->loses_context)
continue;
- pm_runtime_get_sync(bank->dev);
+ pm_runtime_get_sync(bank->chip.parent);
}
}
#endif
diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c
index fdfb3b1e0..6f27b3d94 100644
--- a/drivers/gpio/gpio-palmas.c
+++ b/drivers/gpio/gpio-palmas.c
@@ -195,7 +195,8 @@ static int palmas_gpio_probe(struct platform_device *pdev)
else
palmas_gpio->gpio_chip.base = -1;
- ret = gpiochip_add_data(&palmas_gpio->gpio_chip, palmas_gpio);
+ ret = devm_gpiochip_add_data(&pdev->dev, &palmas_gpio->gpio_chip,
+ palmas_gpio);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
return ret;
@@ -205,20 +206,11 @@ static int palmas_gpio_probe(struct platform_device *pdev)
return ret;
}
-static int palmas_gpio_remove(struct platform_device *pdev)
-{
- struct palmas_gpio *palmas_gpio = platform_get_drvdata(pdev);
-
- gpiochip_remove(&palmas_gpio->gpio_chip);
- return 0;
-}
-
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,
};
static int __init palmas_gpio_init(void)
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index 90c1511d7..e66084c29 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -757,7 +757,7 @@ static int pca953x_probe(struct i2c_client *client,
if (ret)
return ret;
- ret = gpiochip_add_data(&chip->gpio_chip, chip);
+ ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip);
if (ret)
return ret;
@@ -792,8 +792,6 @@ static int pca953x_remove(struct i2c_client *client)
}
}
- gpiochip_remove(&chip->gpio_chip);
-
return 0;
}
diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c
index 709cd3fc2..169c09aa3 100644
--- a/drivers/gpio/gpio-pcf857x.c
+++ b/drivers/gpio/gpio-pcf857x.c
@@ -372,7 +372,7 @@ static int pcf857x_probe(struct i2c_client *client,
gpio->out = ~n_latch;
gpio->status = gpio->out;
- status = gpiochip_add_data(&gpio->chip, gpio);
+ status = devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio);
if (status < 0)
goto fail;
@@ -383,7 +383,7 @@ static int pcf857x_probe(struct i2c_client *client,
IRQ_TYPE_NONE);
if (status) {
dev_err(&client->dev, "cannot add irqchip\n");
- goto fail_irq;
+ goto fail;
}
status = devm_request_threaded_irq(&client->dev, client->irq,
@@ -391,7 +391,7 @@ static int pcf857x_probe(struct i2c_client *client,
IRQF_TRIGGER_FALLING | IRQF_SHARED,
dev_name(&client->dev), gpio);
if (status)
- goto fail_irq;
+ goto fail;
gpiochip_set_chained_irqchip(&gpio->chip, &pcf857x_irq_chip,
client->irq, NULL);
@@ -413,9 +413,6 @@ static int pcf857x_probe(struct i2c_client *client,
return 0;
-fail_irq:
- gpiochip_remove(&gpio->chip);
-
fail:
dev_dbg(&client->dev, "probe error %d for '%s'\n", status,
client->name);
@@ -440,7 +437,6 @@ static int pcf857x_remove(struct i2c_client *client)
}
}
- gpiochip_remove(&gpio->chip);
return status;
}
diff --git a/drivers/gpio/gpio-pisosr.c b/drivers/gpio/gpio-pisosr.c
new file mode 100644
index 000000000..cb14b8d1d
--- /dev/null
+++ b/drivers/gpio/gpio-pisosr.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ * Andrew F. Davis <afd@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether expressed or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/spi/spi.h>
+
+#define DEFAULT_NGPIO 8
+
+/**
+ * struct pisosr_gpio - GPIO driver data
+ * @chip: GPIO controller chip
+ * @spi: SPI device pointer
+ * @buffer: Buffer for device reads
+ * @buffer_size: Size of buffer
+ * @load_gpio: GPIO pin used to load input into device
+ * @lock: Protects read sequences
+ */
+struct pisosr_gpio {
+ struct gpio_chip chip;
+ struct spi_device *spi;
+ u8 *buffer;
+ size_t buffer_size;
+ struct gpio_desc *load_gpio;
+ struct mutex lock;
+};
+
+static int pisosr_gpio_refresh(struct pisosr_gpio *gpio)
+{
+ int ret;
+
+ mutex_lock(&gpio->lock);
+
+ if (gpio->load_gpio) {
+ gpiod_set_value_cansleep(gpio->load_gpio, 1);
+ udelay(1); /* registers load time (~10ns) */
+ gpiod_set_value_cansleep(gpio->load_gpio, 0);
+ udelay(1); /* registers recovery time (~5ns) */
+ }
+
+ ret = spi_read(gpio->spi, gpio->buffer, gpio->buffer_size);
+
+ mutex_unlock(&gpio->lock);
+
+ return ret;
+}
+
+static int pisosr_gpio_get_direction(struct gpio_chip *chip,
+ unsigned offset)
+{
+ /* This device always input */
+ return 1;
+}
+
+static int pisosr_gpio_direction_input(struct gpio_chip *chip,
+ unsigned offset)
+{
+ /* This device always input */
+ return 0;
+}
+
+static int pisosr_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ /* This device is input only */
+ return -EINVAL;
+}
+
+static int pisosr_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct pisosr_gpio *gpio = gpiochip_get_data(chip);
+
+ /* Refresh may not always be needed */
+ pisosr_gpio_refresh(gpio);
+
+ return (gpio->buffer[offset / 8] >> (offset % 8)) & 0x1;
+}
+
+static struct gpio_chip template_chip = {
+ .label = "pisosr-gpio",
+ .owner = THIS_MODULE,
+ .get_direction = pisosr_gpio_get_direction,
+ .direction_input = pisosr_gpio_direction_input,
+ .direction_output = pisosr_gpio_direction_output,
+ .get = pisosr_gpio_get,
+ .base = -1,
+ .ngpio = DEFAULT_NGPIO,
+ .can_sleep = true,
+};
+
+static int pisosr_gpio_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct pisosr_gpio *gpio;
+ int ret;
+
+ gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, gpio);
+
+ gpio->chip = template_chip;
+ gpio->chip.parent = dev;
+ of_property_read_u16(dev->of_node, "ngpios", &gpio->chip.ngpio);
+
+ gpio->spi = spi;
+
+ gpio->buffer_size = DIV_ROUND_UP(gpio->chip.ngpio, 8);
+ gpio->buffer = devm_kzalloc(dev, gpio->buffer_size, GFP_KERNEL);
+ if (!gpio->buffer)
+ return -ENOMEM;
+
+ gpio->load_gpio = devm_gpiod_get_optional(dev, "load", GPIOD_OUT_LOW);
+ if (IS_ERR(gpio->load_gpio)) {
+ ret = PTR_ERR(gpio->load_gpio);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Unable to allocate load GPIO\n");
+ return ret;
+ }
+
+ mutex_init(&gpio->lock);
+
+ ret = gpiochip_add_data(&gpio->chip, gpio);
+ if (ret < 0) {
+ dev_err(dev, "Unable to register gpiochip\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pisosr_gpio_remove(struct spi_device *spi)
+{
+ struct pisosr_gpio *gpio = spi_get_drvdata(spi);
+
+ gpiochip_remove(&gpio->chip);
+
+ mutex_destroy(&gpio->lock);
+
+ return 0;
+}
+
+static const struct spi_device_id pisosr_gpio_id_table[] = {
+ { "pisosr-gpio", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(spi, pisosr_gpio_id_table);
+
+static const struct of_device_id pisosr_gpio_of_match_table[] = {
+ { .compatible = "pisosr-gpio", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pisosr_gpio_of_match_table);
+
+static struct spi_driver pisosr_gpio_driver = {
+ .driver = {
+ .name = "pisosr-gpio",
+ .of_match_table = pisosr_gpio_of_match_table,
+ },
+ .probe = pisosr_gpio_probe,
+ .remove = pisosr_gpio_remove,
+ .id_table = pisosr_gpio_id_table,
+};
+module_spi_driver(pisosr_gpio_driver);
+
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("SPI Compatible PISO Shift Register GPIO Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-rc5t583.c b/drivers/gpio/gpio-rc5t583.c
index 1e2d210b3..1d6100fa3 100644
--- a/drivers/gpio/gpio-rc5t583.c
+++ b/drivers/gpio/gpio-rc5t583.c
@@ -136,15 +136,8 @@ static int rc5t583_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, rc5t583_gpio);
- return gpiochip_add_data(&rc5t583_gpio->gpio_chip, rc5t583_gpio);
-}
-
-static int rc5t583_gpio_remove(struct platform_device *pdev)
-{
- struct rc5t583_gpio *rc5t583_gpio = platform_get_drvdata(pdev);
-
- gpiochip_remove(&rc5t583_gpio->gpio_chip);
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &rc5t583_gpio->gpio_chip,
+ rc5t583_gpio);
}
static struct platform_driver rc5t583_gpio_driver = {
@@ -152,7 +145,6 @@ static struct platform_driver rc5t583_gpio_driver = {
.name = "rc5t583-gpio",
},
.probe = rc5t583_gpio_probe,
- .remove = rc5t583_gpio_remove,
};
static int __init rc5t583_gpio_init(void)
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
index d9ab0cd1d..4d9a315cf 100644
--- a/drivers/gpio/gpio-rcar.c
+++ b/drivers/gpio/gpio-rcar.c
@@ -196,44 +196,6 @@ static int gpio_rcar_irq_set_wake(struct irq_data *d, unsigned int on)
return 0;
}
-static void gpio_rcar_irq_bus_lock(struct irq_data *d)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct gpio_rcar_priv *p = gpiochip_get_data(gc);
-
- pm_runtime_get_sync(&p->pdev->dev);
-}
-
-static void gpio_rcar_irq_bus_sync_unlock(struct irq_data *d)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct gpio_rcar_priv *p = gpiochip_get_data(gc);
-
- pm_runtime_put(&p->pdev->dev);
-}
-
-
-static int gpio_rcar_irq_request_resources(struct irq_data *d)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct gpio_rcar_priv *p = gpiochip_get_data(gc);
- int error;
-
- error = pm_runtime_get_sync(&p->pdev->dev);
- if (error < 0)
- return error;
-
- return 0;
-}
-
-static void gpio_rcar_irq_release_resources(struct irq_data *d)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct gpio_rcar_priv *p = gpiochip_get_data(gc);
-
- pm_runtime_put(&p->pdev->dev);
-}
-
static irqreturn_t gpio_rcar_irq_handler(int irq, void *dev_id)
{
struct gpio_rcar_priv *p = dev_id;
@@ -280,32 +242,18 @@ static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip,
static int gpio_rcar_request(struct gpio_chip *chip, unsigned offset)
{
- struct gpio_rcar_priv *p = gpiochip_get_data(chip);
- int error;
-
- error = pm_runtime_get_sync(&p->pdev->dev);
- if (error < 0)
- return error;
-
- error = pinctrl_request_gpio(chip->base + offset);
- if (error)
- pm_runtime_put(&p->pdev->dev);
-
- return error;
+ return pinctrl_request_gpio(chip->base + offset);
}
static void gpio_rcar_free(struct gpio_chip *chip, unsigned offset)
{
- struct gpio_rcar_priv *p = gpiochip_get_data(chip);
-
pinctrl_free_gpio(chip->base + offset);
- /* Set the GPIO as an input to ensure that the next GPIO request won't
+ /*
+ * Set the GPIO as an input to ensure that the next GPIO request won't
* drive the GPIO pin as an output.
*/
gpio_rcar_config_general_input_output_mode(chip, offset, false);
-
- pm_runtime_put(&p->pdev->dev);
}
static int gpio_rcar_direction_input(struct gpio_chip *chip, unsigned offset)
@@ -452,6 +400,7 @@ static int gpio_rcar_probe(struct platform_device *pdev)
}
pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
@@ -488,10 +437,6 @@ static int gpio_rcar_probe(struct platform_device *pdev)
irq_chip->irq_unmask = gpio_rcar_irq_enable;
irq_chip->irq_set_type = gpio_rcar_irq_set_type;
irq_chip->irq_set_wake = gpio_rcar_irq_set_wake;
- irq_chip->irq_bus_lock = gpio_rcar_irq_bus_lock;
- irq_chip->irq_bus_sync_unlock = gpio_rcar_irq_bus_sync_unlock;
- irq_chip->irq_request_resources = gpio_rcar_irq_request_resources;
- irq_chip->irq_release_resources = gpio_rcar_irq_release_resources;
irq_chip->flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND;
ret = gpiochip_add_data(gpio_chip, p);
@@ -522,6 +467,7 @@ static int gpio_rcar_probe(struct platform_device *pdev)
err1:
gpiochip_remove(gpio_chip);
err0:
+ pm_runtime_put(dev);
pm_runtime_disable(dev);
return ret;
}
@@ -532,6 +478,7 @@ static int gpio_rcar_remove(struct platform_device *pdev)
gpiochip_remove(&p->gpio_chip);
+ pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
diff --git a/drivers/gpio/gpio-rdc321x.c b/drivers/gpio/gpio-rdc321x.c
index 96ddee3f4..ec945b90f 100644
--- a/drivers/gpio/gpio-rdc321x.c
+++ b/drivers/gpio/gpio-rdc321x.c
@@ -194,23 +194,14 @@ static int rdc321x_gpio_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "registering %d GPIOs\n",
rdc321x_gpio_dev->chip.ngpio);
- return gpiochip_add_data(&rdc321x_gpio_dev->chip, rdc321x_gpio_dev);
-}
-
-static int rdc321x_gpio_remove(struct platform_device *pdev)
-{
- struct rdc321x_gpio *rdc321x_gpio_dev = platform_get_drvdata(pdev);
-
- gpiochip_remove(&rdc321x_gpio_dev->chip);
-
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &rdc321x_gpio_dev->chip,
+ rdc321x_gpio_dev);
}
static struct platform_driver rdc321x_gpio_driver = {
.driver.name = "rdc321x-gpio",
.driver.owner = THIS_MODULE,
.probe = rdc321x_gpio_probe,
- .remove = rdc321x_gpio_remove,
};
module_platform_driver(rdc321x_gpio_driver);
diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c
index 5314ee4b9..e85e7539c 100644
--- a/drivers/gpio/gpio-sch.c
+++ b/drivers/gpio/gpio-sch.c
@@ -215,15 +215,7 @@ static int sch_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, sch);
- return gpiochip_add_data(&sch->chip, sch);
-}
-
-static int sch_gpio_remove(struct platform_device *pdev)
-{
- struct sch_gpio *sch = platform_get_drvdata(pdev);
-
- gpiochip_remove(&sch->chip);
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &sch->chip, sch);
}
static struct platform_driver sch_gpio_driver = {
@@ -231,7 +223,6 @@ static struct platform_driver sch_gpio_driver = {
.name = "sch_gpio",
},
.probe = sch_gpio_probe,
- .remove = sch_gpio_remove,
};
module_platform_driver(sch_gpio_driver);
diff --git a/drivers/gpio/gpio-sch311x.c b/drivers/gpio/gpio-sch311x.c
index 1cbd77a04..a03b38ee2 100644
--- a/drivers/gpio/gpio-sch311x.c
+++ b/drivers/gpio/gpio-sch311x.c
@@ -12,6 +12,7 @@
* (at your option) any later version.
*/
+#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
@@ -228,7 +229,8 @@ static int sch311x_gpio_probe(struct platform_device *pdev)
int err, i;
/* we can register all GPIO data registers at once */
- if (!request_region(pdata->runtime_reg + GP1, 6, DRV_NAME)) {
+ if (!devm_request_region(&pdev->dev, pdata->runtime_reg + GP1, 6,
+ DRV_NAME)) {
dev_err(&pdev->dev, "Failed to request region 0x%04x-0x%04x.\n",
pdata->runtime_reg + GP1, pdata->runtime_reg + GP1 + 5);
return -EBUSY;
@@ -273,7 +275,6 @@ static int sch311x_gpio_probe(struct platform_device *pdev)
return 0;
exit_err:
- release_region(pdata->runtime_reg + GP1, 6);
/* release already registered chips */
for (--i; i >= 0; i--)
gpiochip_remove(&priv->blocks[i].chip);
@@ -282,12 +283,9 @@ exit_err:
static int sch311x_gpio_remove(struct platform_device *pdev)
{
- struct sch311x_pdev_data *pdata = dev_get_platdata(&pdev->dev);
struct sch311x_gpio_priv *priv = platform_get_drvdata(pdev);
int i;
- release_region(pdata->runtime_reg + GP1, 6);
-
for (i = 0; i < ARRAY_SIZE(priv->blocks); i++) {
gpiochip_remove(&priv->blocks[i].chip);
dev_info(&pdev->dev,
diff --git a/drivers/gpio/gpio-spear-spics.c b/drivers/gpio/gpio-spear-spics.c
index 50fb09080..7ffd16495 100644
--- a/drivers/gpio/gpio-spear-spics.c
+++ b/drivers/gpio/gpio-spear-spics.c
@@ -165,7 +165,7 @@ static int spics_gpio_probe(struct platform_device *pdev)
spics->chip.owner = THIS_MODULE;
spics->last_off = -1;
- ret = gpiochip_add_data(&spics->chip, spics);
+ ret = devm_gpiochip_add_data(&pdev->dev, &spics->chip, spics);
if (ret) {
dev_err(&pdev->dev, "unable to add gpio chip\n");
return ret;
diff --git a/drivers/gpio/gpio-sta2x11.c b/drivers/gpio/gpio-sta2x11.c
index 83af1cb36..0d5b8c525 100644
--- a/drivers/gpio/gpio-sta2x11.c
+++ b/drivers/gpio/gpio-sta2x11.c
@@ -409,7 +409,7 @@ static int gsta_probe(struct platform_device *dev)
goto err_free_descs;
}
- err = gpiochip_add_data(&chip->gpio, chip);
+ err = devm_gpiochip_add_data(&dev->dev, &chip->gpio, chip);
if (err < 0) {
dev_err(&dev->dev, "sta2x11 gpio: Can't register (%i)\n",
-err);
diff --git a/drivers/gpio/gpio-stp-xway.c b/drivers/gpio/gpio-stp-xway.c
index d11dd4857..19e654f88 100644
--- a/drivers/gpio/gpio-stp-xway.c
+++ b/drivers/gpio/gpio-stp-xway.c
@@ -258,7 +258,7 @@ static int xway_stp_probe(struct platform_device *pdev)
ret = xway_stp_hw_init(chip);
if (!ret)
- ret = gpiochip_add_data(&chip->gc, chip);
+ ret = devm_gpiochip_add_data(&pdev->dev, &chip->gc, chip);
if (!ret)
dev_info(&pdev->dev, "Init done\n");
diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c
index e6cff1cab..d387eb524 100644
--- a/drivers/gpio/gpio-sx150x.c
+++ b/drivers/gpio/gpio-sx150x.c
@@ -687,7 +687,7 @@ static int sx150x_probe(struct i2c_client *client,
if (rc < 0)
return rc;
- rc = gpiochip_add_data(&chip->gpio_chip, chip);
+ rc = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip);
if (rc)
return rc;
@@ -696,25 +696,12 @@ static int sx150x_probe(struct i2c_client *client,
pdata->irq_summary,
pdata->irq_base);
if (rc < 0)
- goto probe_fail_post_gpiochip_add;
+ return rc;
}
i2c_set_clientdata(client, chip);
return 0;
-probe_fail_post_gpiochip_add:
- gpiochip_remove(&chip->gpio_chip);
- return rc;
-}
-
-static int sx150x_remove(struct i2c_client *client)
-{
- struct sx150x_chip *chip;
-
- chip = i2c_get_clientdata(client);
- gpiochip_remove(&chip->gpio_chip);
-
- return 0;
}
static struct i2c_driver sx150x_driver = {
@@ -723,7 +710,6 @@ static struct i2c_driver sx150x_driver = {
.of_match_table = of_match_ptr(sx150x_of_match),
},
.probe = sx150x_probe,
- .remove = sx150x_remove,
.id_table = sx150x_id,
};
diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c
index e5c5b6205..24b6d643e 100644
--- a/drivers/gpio/gpio-syscon.c
+++ b/drivers/gpio/gpio-syscon.c
@@ -238,15 +238,7 @@ static int syscon_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
- return gpiochip_add_data(&priv->chip, priv);
-}
-
-static int syscon_gpio_remove(struct platform_device *pdev)
-{
- struct syscon_gpio_priv *priv = platform_get_drvdata(pdev);
-
- gpiochip_remove(&priv->chip);
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv);
}
static struct platform_driver syscon_gpio_driver = {
@@ -255,7 +247,6 @@ static struct platform_driver syscon_gpio_driver = {
.of_match_table = syscon_gpio_ids,
},
.probe = syscon_gpio_probe,
- .remove = syscon_gpio_remove,
};
module_platform_driver(syscon_gpio_driver);
diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c
index 5eaec20dd..80b6959ae 100644
--- a/drivers/gpio/gpio-tb10x.c
+++ b/drivers/gpio/gpio-tb10x.c
@@ -205,10 +205,10 @@ static int tb10x_gpio_probe(struct platform_device *pdev)
tb10x_gpio->gc.can_sleep = false;
- ret = gpiochip_add_data(&tb10x_gpio->gc, tb10x_gpio);
+ ret = devm_gpiochip_add_data(&pdev->dev, &tb10x_gpio->gc, tb10x_gpio);
if (ret < 0) {
dev_err(&pdev->dev, "Could not add gpiochip.\n");
- goto fail_gpiochip_registration;
+ return ret;
}
platform_set_drvdata(pdev, tb10x_gpio);
@@ -219,7 +219,7 @@ static int tb10x_gpio_probe(struct platform_device *pdev)
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
dev_err(&pdev->dev, "No interrupt specified.\n");
- goto fail_get_irq;
+ return ret;
}
tb10x_gpio->gc.to_irq = tb10x_gpio_to_irq;
@@ -229,14 +229,13 @@ static int tb10x_gpio_probe(struct platform_device *pdev)
IRQF_TRIGGER_NONE | IRQF_SHARED,
dev_name(&pdev->dev), tb10x_gpio);
if (ret != 0)
- goto fail_request_irq;
+ return ret;
tb10x_gpio->domain = irq_domain_add_linear(dn,
tb10x_gpio->gc.ngpio,
&irq_generic_chip_ops, NULL);
if (!tb10x_gpio->domain) {
- ret = -ENOMEM;
- goto fail_irq_domain;
+ return -ENOMEM;
}
ret = irq_alloc_domain_generic_chips(tb10x_gpio->domain,
@@ -244,7 +243,7 @@ static int tb10x_gpio_probe(struct platform_device *pdev)
handle_edge_irq, IRQ_NOREQUEST, IRQ_NOPROBE,
IRQ_GC_INIT_MASK_CACHE);
if (ret)
- goto fail_irq_domain;
+ return ret;
gc = tb10x_gpio->domain->gc->gc[0];
gc->reg_base = tb10x_gpio->base;
@@ -258,14 +257,6 @@ static int tb10x_gpio_probe(struct platform_device *pdev)
}
return 0;
-
-fail_irq_domain:
-fail_request_irq:
-fail_get_irq:
- gpiochip_remove(&tb10x_gpio->gc);
-fail_gpiochip_registration:
-fail_ioremap:
- return ret;
}
static int tb10x_gpio_remove(struct platform_device *pdev)
@@ -278,7 +269,6 @@ static int tb10x_gpio_remove(struct platform_device *pdev)
kfree(tb10x_gpio->domain->gc);
irq_domain_remove(tb10x_gpio->domain);
}
- gpiochip_remove(&tb10x_gpio->gc);
return 0;
}
diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c
index 05a27ec55..4f566e6b8 100644
--- a/drivers/gpio/gpio-tc3589x.c
+++ b/drivers/gpio/gpio-tc3589x.c
@@ -272,7 +272,8 @@ static int tc3589x_gpio_probe(struct platform_device *pdev)
return ret;
}
- ret = gpiochip_add_data(&tc3589x_gpio->chip, tc3589x_gpio);
+ ret = devm_gpiochip_add_data(&pdev->dev, &tc3589x_gpio->chip,
+ tc3589x_gpio);
if (ret) {
dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
return ret;
@@ -299,20 +300,10 @@ static int tc3589x_gpio_probe(struct platform_device *pdev)
return 0;
}
-static int tc3589x_gpio_remove(struct platform_device *pdev)
-{
- struct tc3589x_gpio *tc3589x_gpio = platform_get_drvdata(pdev);
-
- gpiochip_remove(&tc3589x_gpio->chip);
-
- return 0;
-}
-
static struct platform_driver tc3589x_gpio_driver = {
.driver.name = "tc3589x-gpio",
.driver.owner = THIS_MODULE,
.probe = tc3589x_gpio_probe,
- .remove = tc3589x_gpio_remove,
};
static int __init tc3589x_gpio_init(void)
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index 9a1a7e2ef..790bb111b 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -545,7 +545,7 @@ static int tegra_gpio_probe(struct platform_device *pdev)
tegra_gpio_chip.of_node = pdev->dev.of_node;
- ret = gpiochip_add_data(&tegra_gpio_chip, NULL);
+ ret = devm_gpiochip_add_data(&pdev->dev, &tegra_gpio_chip, NULL);
if (ret < 0) {
irq_domain_remove(irq_domain);
return ret;
diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c
index a6de10c52..85ed608c2 100644
--- a/drivers/gpio/gpio-timberdale.c
+++ b/drivers/gpio/gpio-timberdale.c
@@ -237,12 +237,6 @@ static int timbgpio_probe(struct platform_device *pdev)
return -EINVAL;
}
- iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!iomem) {
- dev_err(dev, "Unable to get resource\n");
- return -EINVAL;
- }
-
tgpio = devm_kzalloc(dev, sizeof(struct timbgpio), GFP_KERNEL);
if (!tgpio) {
dev_err(dev, "Memory alloc failed\n");
@@ -252,17 +246,10 @@ static int timbgpio_probe(struct platform_device *pdev)
spin_lock_init(&tgpio->lock);
- if (!devm_request_mem_region(dev, iomem->start, resource_size(iomem),
- DRIVER_NAME)) {
- dev_err(dev, "Region already claimed\n");
- return -EBUSY;
- }
-
- tgpio->membase = devm_ioremap(dev, iomem->start, resource_size(iomem));
- if (!tgpio->membase) {
- dev_err(dev, "Cannot ioremap\n");
- return -ENOMEM;
- }
+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ tgpio->membase = devm_ioremap_resource(dev, iomem);
+ if (IS_ERR(tgpio->membase))
+ return PTR_ERR(tgpio->membase);
gc = &tgpio->gpio;
@@ -279,7 +266,7 @@ static int timbgpio_probe(struct platform_device *pdev)
gc->ngpio = pdata->nr_pins;
gc->can_sleep = false;
- err = gpiochip_add_data(gc, tgpio);
+ err = devm_gpiochip_add_data(&pdev->dev, gc, tgpio);
if (err)
return err;
@@ -320,8 +307,6 @@ static int timbgpio_remove(struct platform_device *pdev)
irq_set_handler_data(irq, NULL);
}
- gpiochip_remove(&tgpio->gpio);
-
return 0;
}
diff --git a/drivers/gpio/gpio-tpic2810.c b/drivers/gpio/gpio-tpic2810.c
new file mode 100644
index 000000000..9f020aa4b
--- /dev/null
+++ b/drivers/gpio/gpio-tpic2810.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ * Andrew F. Davis <afd@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether expressed or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+#define TPIC2810_WS_COMMAND 0x44
+
+/**
+ * struct tpic2810 - GPIO driver data
+ * @chip: GPIO controller chip
+ * @client: I2C device pointer
+ * @buffer: Buffer for device register
+ * @lock: Protects write sequences
+ */
+struct tpic2810 {
+ struct gpio_chip chip;
+ struct i2c_client *client;
+ u8 buffer;
+ struct mutex lock;
+};
+
+static void tpic2810_set(struct gpio_chip *chip, unsigned offset, int value);
+
+static int tpic2810_get_direction(struct gpio_chip *chip,
+ unsigned offset)
+{
+ /* This device always output */
+ return 0;
+}
+
+static int tpic2810_direction_input(struct gpio_chip *chip,
+ unsigned offset)
+{
+ /* This device is output only */
+ return -EINVAL;
+}
+
+static int tpic2810_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ /* This device always output */
+ tpic2810_set(chip, offset, value);
+ return 0;
+}
+
+static void tpic2810_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct tpic2810 *gpio = gpiochip_get_data(chip);
+
+ mutex_lock(&gpio->lock);
+
+ if (value)
+ gpio->buffer |= BIT(offset);
+ else
+ gpio->buffer &= ~BIT(offset);
+
+ i2c_smbus_write_byte_data(gpio->client, TPIC2810_WS_COMMAND,
+ gpio->buffer);
+
+ mutex_unlock(&gpio->lock);
+}
+
+static void tpic2810_set_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct tpic2810 *gpio = gpiochip_get_data(chip);
+
+ mutex_lock(&gpio->lock);
+
+ /* clear bits under mask */
+ gpio->buffer &= ~(*mask);
+ /* set bits under mask */
+ gpio->buffer |= ((*mask) & (*bits));
+
+ i2c_smbus_write_byte_data(gpio->client, TPIC2810_WS_COMMAND,
+ gpio->buffer);
+
+ mutex_unlock(&gpio->lock);
+}
+
+static struct gpio_chip template_chip = {
+ .label = "tpic2810",
+ .owner = THIS_MODULE,
+ .get_direction = tpic2810_get_direction,
+ .direction_input = tpic2810_direction_input,
+ .direction_output = tpic2810_direction_output,
+ .set = tpic2810_set,
+ .set_multiple = tpic2810_set_multiple,
+ .base = -1,
+ .ngpio = 8,
+ .can_sleep = true,
+};
+
+static const struct of_device_id tpic2810_of_match_table[] = {
+ { .compatible = "ti,tpic2810" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tpic2810_of_match_table);
+
+static int tpic2810_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tpic2810 *gpio;
+ int ret;
+
+ gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, gpio);
+
+ gpio->chip = template_chip;
+ gpio->chip.parent = &client->dev;
+
+ gpio->client = client;
+
+ mutex_init(&gpio->lock);
+
+ ret = gpiochip_add_data(&gpio->chip, gpio);
+ if (ret < 0) {
+ dev_err(&client->dev, "Unable to register gpiochip\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tpic2810_remove(struct i2c_client *client)
+{
+ struct tpic2810 *gpio = i2c_get_clientdata(client);
+
+ gpiochip_remove(&gpio->chip);
+
+ return 0;
+}
+
+static const struct i2c_device_id tpic2810_id_table[] = {
+ { "tpic2810", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, tpic2810_id_table);
+
+static struct i2c_driver tpic2810_driver = {
+ .driver = {
+ .name = "tpic2810",
+ .of_match_table = tpic2810_of_match_table,
+ },
+ .probe = tpic2810_probe,
+ .remove = tpic2810_remove,
+ .id_table = tpic2810_id_table,
+};
+module_i2c_driver(tpic2810_driver);
+
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("TPIC2810 8-Bit LED Driver GPIO Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c
new file mode 100644
index 000000000..8e25f01ac
--- /dev/null
+++ b/drivers/gpio/gpio-tps65086.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ * Andrew F. Davis <afd@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether expressed or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ *
+ * Based on the TPS65912 driver
+ */
+
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/tps65086.h>
+
+struct tps65086_gpio {
+ struct gpio_chip chip;
+ struct tps65086 *tps;
+};
+
+static int tps65086_gpio_get_direction(struct gpio_chip *chip,
+ unsigned offset)
+{
+ /* This device is output only */
+ return 0;
+}
+
+static int tps65086_gpio_direction_input(struct gpio_chip *chip,
+ unsigned offset)
+{
+ /* This device is output only */
+ return -EINVAL;
+}
+
+static int tps65086_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct tps65086_gpio *gpio = gpiochip_get_data(chip);
+
+ /* Set the initial value */
+ regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL,
+ BIT(4 + offset), value ? BIT(4 + offset) : 0);
+
+ return 0;
+}
+
+static int tps65086_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct tps65086_gpio *gpio = gpiochip_get_data(chip);
+ int ret, val;
+
+ ret = regmap_read(gpio->tps->regmap, TPS65086_GPOCTRL, &val);
+ if (ret < 0)
+ return ret;
+
+ return val & BIT(4 + offset);
+}
+
+static void tps65086_gpio_set(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct tps65086_gpio *gpio = gpiochip_get_data(chip);
+
+ regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL,
+ BIT(4 + offset), value ? BIT(4 + offset) : 0);
+}
+
+static struct gpio_chip template_chip = {
+ .label = "tps65086-gpio",
+ .owner = THIS_MODULE,
+ .get_direction = tps65086_gpio_get_direction,
+ .direction_input = tps65086_gpio_direction_input,
+ .direction_output = tps65086_gpio_direction_output,
+ .get = tps65086_gpio_get,
+ .set = tps65086_gpio_set,
+ .base = -1,
+ .ngpio = 4,
+ .can_sleep = true,
+};
+
+static int tps65086_gpio_probe(struct platform_device *pdev)
+{
+ struct tps65086_gpio *gpio;
+ int ret;
+
+ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, gpio);
+
+ gpio->tps = dev_get_drvdata(pdev->dev.parent);
+ gpio->chip = template_chip;
+ gpio->chip.parent = gpio->tps->dev;
+
+ ret = gpiochip_add_data(&gpio->chip, gpio);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tps65086_gpio_remove(struct platform_device *pdev)
+{
+ struct tps65086_gpio *gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&gpio->chip);
+
+ return 0;
+}
+
+static const struct platform_device_id tps65086_gpio_id_table[] = {
+ { "tps65086-gpio", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, tps65086_gpio_id_table);
+
+static struct platform_driver tps65086_gpio_driver = {
+ .driver = {
+ .name = "tps65086-gpio",
+ },
+ .probe = tps65086_gpio_probe,
+ .remove = tps65086_gpio_remove,
+ .id_table = tps65086_gpio_id_table,
+};
+module_platform_driver(tps65086_gpio_driver);
+
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("TPS65086 GPIO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c
new file mode 100644
index 000000000..313c0e484
--- /dev/null
+++ b/drivers/gpio/gpio-tps65218.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2015 Verifone Int.
+ *
+ * Author: Nicolas Saenz Julienne <nicolassaenzj@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify i t
+ * under the terms of the GNU General Public License as published by th e
+ * Free Software Foundation; either version 2 of the License, or (at you r
+ * option) any later version.
+ *
+ * This driver is based on the gpio-tps65912 implementation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/gpio/driver.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/tps65218.h>
+
+struct tps65218_gpio {
+ struct tps65218 *tps65218;
+ struct gpio_chip gpio_chip;
+};
+
+static int tps65218_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc);
+ struct tps65218 *tps65218 = tps65218_gpio->tps65218;
+ unsigned int val;
+ int ret;
+
+ ret = tps65218_reg_read(tps65218, TPS65218_REG_ENABLE2, &val);
+ if (ret)
+ return ret;
+
+ return !!(val & (TPS65218_ENABLE2_GPIO1 << offset));
+}
+
+static void tps65218_gpio_set(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc);
+ struct tps65218 *tps65218 = tps65218_gpio->tps65218;
+
+ if (value)
+ tps65218_set_bits(tps65218, TPS65218_REG_ENABLE2,
+ TPS65218_ENABLE2_GPIO1 << offset,
+ TPS65218_ENABLE2_GPIO1 << offset,
+ TPS65218_PROTECT_L1);
+ else
+ tps65218_clear_bits(tps65218, TPS65218_REG_ENABLE2,
+ TPS65218_ENABLE2_GPIO1 << offset,
+ TPS65218_PROTECT_L1);
+}
+
+static int tps65218_gpio_output(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ /* Only drives GPOs */
+ tps65218_gpio_set(gc, offset, value);
+ return 0;
+}
+
+static int tps65218_gpio_input(struct gpio_chip *gc, unsigned offset)
+{
+ return -EPERM;
+}
+
+static int tps65218_gpio_request(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc);
+ struct tps65218 *tps65218 = tps65218_gpio->tps65218;
+ int ret;
+
+ if (gpiochip_line_is_open_source(gc, offset)) {
+ dev_err(gc->parent, "can't work as open source\n");
+ return -EINVAL;
+ }
+
+ switch (offset) {
+ case 0:
+ if (!gpiochip_line_is_open_drain(gc, offset)) {
+ dev_err(gc->parent, "GPO1 works only as open drain\n");
+ return -EINVAL;
+ }
+
+ /* Disable sequencer for GPO1 */
+ ret = tps65218_clear_bits(tps65218, TPS65218_REG_SEQ7,
+ TPS65218_SEQ7_GPO1_SEQ_MASK,
+ TPS65218_PROTECT_L1);
+ if (ret)
+ return ret;
+
+ /* Setup GPO1 */
+ ret = tps65218_clear_bits(tps65218, TPS65218_REG_CONFIG1,
+ TPS65218_CONFIG1_IO1_SEL,
+ TPS65218_PROTECT_L1);
+ if (ret)
+ return ret;
+
+ break;
+ case 1:
+ /* GP02 is push-pull by default, can be set as open drain. */
+ if (gpiochip_line_is_open_drain(gc, offset)) {
+ ret = tps65218_clear_bits(tps65218,
+ TPS65218_REG_CONFIG1,
+ TPS65218_CONFIG1_GPO2_BUF,
+ TPS65218_PROTECT_L1);
+ if (ret)
+ return ret;
+ }
+
+ /* Setup GPO2 */
+ ret = tps65218_clear_bits(tps65218, TPS65218_REG_CONFIG1,
+ TPS65218_CONFIG1_IO1_SEL,
+ TPS65218_PROTECT_L1);
+ if (ret)
+ return ret;
+
+ break;
+
+ case 2:
+ if (!gpiochip_line_is_open_drain(gc, offset)) {
+ dev_err(gc->parent, "GPO3 works only as open drain\n");
+ return -EINVAL;
+ }
+
+ /* Disable sequencer for GPO3 */
+ ret = tps65218_clear_bits(tps65218, TPS65218_REG_SEQ7,
+ TPS65218_SEQ7_GPO3_SEQ_MASK,
+ TPS65218_PROTECT_L1);
+ if (ret)
+ return ret;
+
+ /* Setup GPO3 */
+ ret = tps65218_clear_bits(tps65218, TPS65218_REG_CONFIG2,
+ TPS65218_CONFIG2_DC12_RST,
+ TPS65218_PROTECT_L1);
+ if (ret)
+ return ret;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct gpio_chip template_chip = {
+ .label = "gpio-tps65218",
+ .owner = THIS_MODULE,
+ .request = tps65218_gpio_request,
+ .direction_output = tps65218_gpio_output,
+ .direction_input = tps65218_gpio_input,
+ .get = tps65218_gpio_get,
+ .set = tps65218_gpio_set,
+ .can_sleep = true,
+ .ngpio = 3,
+ .base = -1,
+};
+
+static int tps65218_gpio_probe(struct platform_device *pdev)
+{
+ struct tps65218 *tps65218 = dev_get_drvdata(pdev->dev.parent);
+ struct tps65218_gpio *tps65218_gpio;
+ int ret;
+
+ tps65218_gpio = devm_kzalloc(&pdev->dev, sizeof(*tps65218_gpio),
+ GFP_KERNEL);
+ if (!tps65218_gpio)
+ return -ENOMEM;
+
+ tps65218_gpio->tps65218 = tps65218;
+ tps65218_gpio->gpio_chip = template_chip;
+ tps65218_gpio->gpio_chip.parent = &pdev->dev;
+#ifdef CONFIG_OF_GPIO
+ tps65218_gpio->gpio_chip.of_node = pdev->dev.of_node;
+#endif
+
+ ret = gpiochip_add_data(&tps65218_gpio->gpio_chip, tps65218_gpio);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register gpiochip, %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, tps65218_gpio);
+
+ return ret;
+}
+
+static int tps65218_gpio_remove(struct platform_device *pdev)
+{
+ struct tps65218_gpio *tps65218_gpio = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&tps65218_gpio->gpio_chip);
+
+ return 0;
+}
+
+static const struct of_device_id tps65218_dt_match[] = {
+ { .compatible = "ti,tps65218-gpio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tps65218_dt_match);
+
+static struct platform_driver tps65218_gpio_driver = {
+ .driver = {
+ .name = "tps65218-gpio",
+ .of_match_table = of_match_ptr(tps65218_dt_match)
+ },
+ .probe = tps65218_gpio_probe,
+ .remove = tps65218_gpio_remove,
+};
+
+module_platform_driver(tps65218_gpio_driver);
+
+MODULE_AUTHOR("Nicolas Saenz Julienne <nicolassaenzj@gmail.com>");
+MODULE_DESCRIPTION("GPO interface for TPS65218 PMICs");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps65218-gpio");
diff --git a/drivers/gpio/gpio-tps6586x.c b/drivers/gpio/gpio-tps6586x.c
index 87de5486a..c88bdc8ee 100644
--- a/drivers/gpio/gpio-tps6586x.c
+++ b/drivers/gpio/gpio-tps6586x.c
@@ -117,7 +117,8 @@ static int tps6586x_gpio_probe(struct platform_device *pdev)
else
tps6586x_gpio->gpio_chip.base = -1;
- ret = gpiochip_add_data(&tps6586x_gpio->gpio_chip, tps6586x_gpio);
+ ret = devm_gpiochip_add_data(&pdev->dev, &tps6586x_gpio->gpio_chip,
+ tps6586x_gpio);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
return ret;
@@ -128,19 +129,10 @@ static int tps6586x_gpio_probe(struct platform_device *pdev)
return ret;
}
-static int tps6586x_gpio_remove(struct platform_device *pdev)
-{
- struct tps6586x_gpio *tps6586x_gpio = platform_get_drvdata(pdev);
-
- gpiochip_remove(&tps6586x_gpio->gpio_chip);
- return 0;
-}
-
static struct platform_driver tps6586x_gpio_driver = {
.driver.name = "tps6586x-gpio",
.driver.owner = THIS_MODULE,
.probe = tps6586x_gpio_probe,
- .remove = tps6586x_gpio_remove,
};
static int __init tps6586x_gpio_init(void)
diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c
index e81eee762..cdbd7c740 100644
--- a/drivers/gpio/gpio-tps65910.c
+++ b/drivers/gpio/gpio-tps65910.c
@@ -170,7 +170,8 @@ static int tps65910_gpio_probe(struct platform_device *pdev)
}
skip_init:
- ret = gpiochip_add_data(&tps65910_gpio->gpio_chip, tps65910_gpio);
+ ret = devm_gpiochip_add_data(&pdev->dev, &tps65910_gpio->gpio_chip,
+ tps65910_gpio);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
return ret;
@@ -181,19 +182,10 @@ skip_init:
return ret;
}
-static int tps65910_gpio_remove(struct platform_device *pdev)
-{
- struct tps65910_gpio *tps65910_gpio = platform_get_drvdata(pdev);
-
- gpiochip_remove(&tps65910_gpio->gpio_chip);
- return 0;
-}
-
static struct platform_driver tps65910_gpio_driver = {
.driver.name = "tps65910-gpio",
.driver.owner = THIS_MODULE,
.probe = tps65910_gpio_probe,
- .remove = tps65910_gpio_remove,
};
static int __init tps65910_gpio_init(void)
diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c
index 4f2029c7d..acfd30a13 100644
--- a/drivers/gpio/gpio-tps65912.c
+++ b/drivers/gpio/gpio-tps65912.c
@@ -1,151 +1,149 @@
/*
- * Copyright 2011 Texas Instruments Inc.
+ * GPIO driver for TI TPS65912x PMICs
*
- * Author: Margarita Olaya <magi@slimlogic.co.uk>
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ * Andrew F. Davis <afd@ti.com>
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*
- * This driver is based on wm8350 implementation.
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether expressed or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ *
+ * Based on the Arizona GPIO driver and the previous TPS65912 driver by
+ * Margarita Olaya Cabrera <magi@slimlogic.co.uk>
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/errno.h>
#include <linux/gpio.h>
-#include <linux/mfd/core.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
+
#include <linux/mfd/tps65912.h>
-struct tps65912_gpio_data {
- struct tps65912 *tps65912;
+struct tps65912_gpio {
struct gpio_chip gpio_chip;
+ struct tps65912 *tps;
};
-static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset)
+static int tps65912_gpio_get_direction(struct gpio_chip *gc,
+ unsigned offset)
{
- struct tps65912_gpio_data *tps65912_gpio = gpiochip_get_data(gc);
- struct tps65912 *tps65912 = tps65912_gpio->tps65912;
- int val;
+ struct tps65912_gpio *gpio = gpiochip_get_data(gc);
- val = tps65912_reg_read(tps65912, TPS65912_GPIO1 + offset);
+ int ret, val;
- if (val & GPIO_STS_MASK)
- return 1;
+ ret = regmap_read(gpio->tps->regmap, TPS65912_GPIO1 + offset, &val);
+ if (ret)
+ return ret;
- return 0;
+ if (val & GPIO_CFG_MASK)
+ return GPIOF_DIR_OUT;
+ else
+ return GPIOF_DIR_IN;
}
-static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset,
- int value)
+static int tps65912_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
{
- struct tps65912_gpio_data *tps65912_gpio = gpiochip_get_data(gc);
- struct tps65912 *tps65912 = tps65912_gpio->tps65912;
+ struct tps65912_gpio *gpio = gpiochip_get_data(gc);
- if (value)
- tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset,
- GPIO_SET_MASK);
- else
- tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset,
- GPIO_SET_MASK);
+ return regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset,
+ GPIO_CFG_MASK, 0);
}
-static int tps65912_gpio_output(struct gpio_chip *gc, unsigned offset,
- int value)
+static int tps65912_gpio_direction_output(struct gpio_chip *gc,
+ unsigned offset, int value)
{
- struct tps65912_gpio_data *tps65912_gpio = gpiochip_get_data(gc);
- struct tps65912 *tps65912 = tps65912_gpio->tps65912;
+ struct tps65912_gpio *gpio = gpiochip_get_data(gc);
/* Set the initial value */
- tps65912_gpio_set(gc, offset, value);
+ regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset,
+ GPIO_SET_MASK, value ? GPIO_SET_MASK : 0);
+
+ return regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset,
+ GPIO_CFG_MASK, GPIO_CFG_MASK);
+}
+
+static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps65912_gpio *gpio = gpiochip_get_data(gc);
+ int ret, val;
+
+ ret = regmap_read(gpio->tps->regmap, TPS65912_GPIO1 + offset, &val);
+ if (ret)
+ return ret;
+
+ if (val & GPIO_STS_MASK)
+ return 1;
- return tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset,
- GPIO_CFG_MASK);
+ return 0;
}
-static int tps65912_gpio_input(struct gpio_chip *gc, unsigned offset)
+static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset,
+ int value)
{
- struct tps65912_gpio_data *tps65912_gpio = gpiochip_get_data(gc);
- struct tps65912 *tps65912 = tps65912_gpio->tps65912;
+ struct tps65912_gpio *gpio = gpiochip_get_data(gc);
- return tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset,
- GPIO_CFG_MASK);
+ regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset,
+ GPIO_SET_MASK, value ? GPIO_SET_MASK : 0);
}
static struct gpio_chip template_chip = {
- .label = "tps65912",
+ .label = "tps65912-gpio",
.owner = THIS_MODULE,
- .direction_input = tps65912_gpio_input,
- .direction_output = tps65912_gpio_output,
+ .get_direction = tps65912_gpio_get_direction,
+ .direction_input = tps65912_gpio_direction_input,
+ .direction_output = tps65912_gpio_direction_output,
.get = tps65912_gpio_get,
.set = tps65912_gpio_set,
- .can_sleep = true,
- .ngpio = 5,
.base = -1,
+ .ngpio = 5,
+ .can_sleep = true,
};
static int tps65912_gpio_probe(struct platform_device *pdev)
{
- struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent);
- struct tps65912_board *pdata = dev_get_platdata(tps65912->dev);
- struct tps65912_gpio_data *tps65912_gpio;
+ struct tps65912 *tps = dev_get_drvdata(pdev->dev.parent);
+ struct tps65912_gpio *gpio;
int ret;
- tps65912_gpio = devm_kzalloc(&pdev->dev, sizeof(*tps65912_gpio),
- GFP_KERNEL);
- if (tps65912_gpio == NULL)
+ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
return -ENOMEM;
- tps65912_gpio->tps65912 = tps65912;
- tps65912_gpio->gpio_chip = template_chip;
- tps65912_gpio->gpio_chip.parent = &pdev->dev;
- if (pdata && pdata->gpio_base)
- tps65912_gpio->gpio_chip.base = pdata->gpio_base;
+ gpio->tps = dev_get_drvdata(pdev->dev.parent);
+ gpio->gpio_chip = template_chip;
+ gpio->gpio_chip.parent = tps->dev;
- ret = gpiochip_add_data(&tps65912_gpio->gpio_chip, tps65912_gpio);
+ ret = devm_gpiochip_add_data(&pdev->dev, &gpio->gpio_chip,
+ gpio);
if (ret < 0) {
- dev_err(&pdev->dev, "Failed to register gpiochip, %d\n", ret);
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
return ret;
}
- platform_set_drvdata(pdev, tps65912_gpio);
-
- return ret;
-}
+ platform_set_drvdata(pdev, gpio);
-static int tps65912_gpio_remove(struct platform_device *pdev)
-{
- struct tps65912_gpio_data *tps65912_gpio = platform_get_drvdata(pdev);
-
- gpiochip_remove(&tps65912_gpio->gpio_chip);
return 0;
}
+static const struct platform_device_id tps65912_gpio_id_table[] = {
+ { "tps65912-gpio", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, tps65912_gpio_id_table);
+
static struct platform_driver tps65912_gpio_driver = {
.driver = {
.name = "tps65912-gpio",
},
.probe = tps65912_gpio_probe,
- .remove = tps65912_gpio_remove,
+ .id_table = tps65912_gpio_id_table,
};
+module_platform_driver(tps65912_gpio_driver);
-static int __init tps65912_gpio_init(void)
-{
- return platform_driver_register(&tps65912_gpio_driver);
-}
-subsys_initcall(tps65912_gpio_init);
-
-static void __exit tps65912_gpio_exit(void)
-{
- platform_driver_unregister(&tps65912_gpio_driver);
-}
-module_exit(tps65912_gpio_exit);
-
-MODULE_AUTHOR("Margarita Olaya Cabrera <magi@slimlogic.co.uk>");
-MODULE_DESCRIPTION("GPIO interface for TPS65912 PMICs");
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("TPS65912 GPIO driver");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:tps65912-gpio");
diff --git a/drivers/gpio/gpio-ts4800.c b/drivers/gpio/gpio-ts4800.c
new file mode 100644
index 000000000..0c144a72f
--- /dev/null
+++ b/drivers/gpio/gpio-ts4800.c
@@ -0,0 +1,81 @@
+/*
+ * GPIO driver for the TS-4800 board
+ *
+ * Copyright (c) 2016 - Savoir-faire Linux
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#define DEFAULT_PIN_NUMBER 16
+#define INPUT_REG_OFFSET 0x00
+#define OUTPUT_REG_OFFSET 0x02
+#define DIRECTION_REG_OFFSET 0x04
+
+static int ts4800_gpio_probe(struct platform_device *pdev)
+{
+ struct device_node *node;
+ struct gpio_chip *chip;
+ struct resource *res;
+ void __iomem *base_addr;
+ int retval;
+ u32 ngpios;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(struct gpio_chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base_addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base_addr))
+ return PTR_ERR(base_addr);
+
+ node = pdev->dev.of_node;
+ if (!node)
+ return -EINVAL;
+
+ retval = of_property_read_u32(node, "ngpios", &ngpios);
+ if (retval == -EINVAL)
+ ngpios = DEFAULT_PIN_NUMBER;
+ else if (retval)
+ return retval;
+
+ retval = bgpio_init(chip, &pdev->dev, 2, base_addr + INPUT_REG_OFFSET,
+ base_addr + OUTPUT_REG_OFFSET, NULL,
+ base_addr + DIRECTION_REG_OFFSET, NULL, 0);
+ if (retval) {
+ dev_err(&pdev->dev, "bgpio_init failed\n");
+ return retval;
+ }
+
+ chip->ngpio = ngpios;
+
+ platform_set_drvdata(pdev, chip);
+
+ return devm_gpiochip_add_data(&pdev->dev, chip, NULL);
+}
+
+static const struct of_device_id ts4800_gpio_of_match[] = {
+ { .compatible = "technologic,ts4800-gpio", },
+ {},
+};
+
+static struct platform_driver ts4800_gpio_driver = {
+ .driver = {
+ .name = "ts4800-gpio",
+ .of_match_table = ts4800_gpio_of_match,
+ },
+ .probe = ts4800_gpio_probe,
+};
+
+module_platform_driver_probe(ts4800_gpio_driver, ts4800_gpio_probe);
+
+MODULE_AUTHOR("Julien Grossholtz <julien.grossholtz@savoirfairelinux.com>");
+MODULE_DESCRIPTION("TS4800 FPGA GPIO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c
index 5f945083f..6cfeba07f 100644
--- a/drivers/gpio/gpio-ts5500.c
+++ b/drivers/gpio/gpio-ts5500.c
@@ -409,7 +409,7 @@ static int ts5500_dio_probe(struct platform_device *pdev)
break;
}
- ret = gpiochip_add_data(&priv->gpio_chip, priv);
+ ret = devm_gpiochip_add_data(dev, &priv->gpio_chip, priv);
if (ret) {
dev_err(dev, "failed to register the gpio chip\n");
return ret;
@@ -418,13 +418,10 @@ static int ts5500_dio_probe(struct platform_device *pdev)
ret = ts5500_enable_irq(priv);
if (ret) {
dev_err(dev, "invalid interrupt %d\n", priv->hwirq);
- goto cleanup;
+ return ret;
}
return 0;
-cleanup:
- gpiochip_remove(&priv->gpio_chip);
- return ret;
}
static int ts5500_dio_remove(struct platform_device *pdev)
@@ -432,7 +429,7 @@ static int ts5500_dio_remove(struct platform_device *pdev)
struct ts5500_priv *priv = platform_get_drvdata(pdev);
ts5500_disable_irq(priv);
- gpiochip_remove(&priv->gpio_chip);
+
return 0;
}
diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c
index 8e9e9853f..b780314cd 100644
--- a/drivers/gpio/gpio-twl6040.c
+++ b/drivers/gpio/gpio-twl6040.c
@@ -100,7 +100,7 @@ static int gpo_twl6040_probe(struct platform_device *pdev)
twl6040gpo_chip.of_node = twl6040_core_dev->of_node;
#endif
- ret = gpiochip_add_data(&twl6040gpo_chip, NULL);
+ ret = devm_gpiochip_add_data(&pdev->dev, &twl6040gpo_chip, NULL);
if (ret < 0) {
dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret);
twl6040gpo_chip.ngpio = 0;
@@ -109,12 +109,6 @@ static int gpo_twl6040_probe(struct platform_device *pdev)
return ret;
}
-static int gpo_twl6040_remove(struct platform_device *pdev)
-{
- gpiochip_remove(&twl6040gpo_chip);
- return 0;
-}
-
/* Note: this hardware lives inside an I2C-based multi-function device. */
MODULE_ALIAS("platform:twl6040-gpo");
@@ -123,7 +117,6 @@ static struct platform_driver gpo_twl6040_driver = {
.name = "twl6040-gpo",
},
.probe = gpo_twl6040_probe,
- .remove = gpo_twl6040_remove,
};
module_platform_driver(gpo_twl6040_driver);
diff --git a/drivers/gpio/gpio-ucb1400.c b/drivers/gpio/gpio-ucb1400.c
index 2c5cd46bf..5dbe31bf6 100644
--- a/drivers/gpio/gpio-ucb1400.c
+++ b/drivers/gpio/gpio-ucb1400.c
@@ -67,7 +67,7 @@ static int ucb1400_gpio_probe(struct platform_device *dev)
ucb->gc.set = ucb1400_gpio_set;
ucb->gc.can_sleep = true;
- err = gpiochip_add_data(&ucb->gc, ucb);
+ err = devm_gpiochip_add_data(&dev->dev, &ucb->gc, ucb);
if (err)
goto err;
@@ -90,7 +90,6 @@ static int ucb1400_gpio_remove(struct platform_device *dev)
return err;
}
- gpiochip_remove(&ucb->gc);
return err;
}
diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c
index 1170b035c..dec47aafd 100644
--- a/drivers/gpio/gpio-viperboard.c
+++ b/drivers/gpio/gpio-viperboard.c
@@ -410,10 +410,10 @@ static int vprbrd_gpio_probe(struct platform_device *pdev)
vb_gpio->gpioa.get = vprbrd_gpioa_get;
vb_gpio->gpioa.direction_input = vprbrd_gpioa_direction_input;
vb_gpio->gpioa.direction_output = vprbrd_gpioa_direction_output;
- ret = gpiochip_add_data(&vb_gpio->gpioa, vb_gpio);
+ ret = devm_gpiochip_add_data(&pdev->dev, &vb_gpio->gpioa, vb_gpio);
if (ret < 0) {
dev_err(vb_gpio->gpioa.parent, "could not add gpio a");
- goto err_gpioa;
+ return ret;
}
/* registering gpio b */
@@ -427,37 +427,21 @@ static int vprbrd_gpio_probe(struct platform_device *pdev)
vb_gpio->gpiob.get = vprbrd_gpiob_get;
vb_gpio->gpiob.direction_input = vprbrd_gpiob_direction_input;
vb_gpio->gpiob.direction_output = vprbrd_gpiob_direction_output;
- ret = gpiochip_add_data(&vb_gpio->gpiob, vb_gpio);
+ ret = devm_gpiochip_add_data(&pdev->dev, &vb_gpio->gpiob, vb_gpio);
if (ret < 0) {
dev_err(vb_gpio->gpiob.parent, "could not add gpio b");
- goto err_gpiob;
+ return ret;
}
platform_set_drvdata(pdev, vb_gpio);
return ret;
-
-err_gpiob:
- gpiochip_remove(&vb_gpio->gpioa);
-
-err_gpioa:
- return ret;
-}
-
-static int vprbrd_gpio_remove(struct platform_device *pdev)
-{
- struct vprbrd_gpio *vb_gpio = platform_get_drvdata(pdev);
-
- gpiochip_remove(&vb_gpio->gpiob);
-
- return 0;
}
static struct platform_driver vprbrd_gpio_driver = {
.driver.name = "viperboard-gpio",
.driver.owner = THIS_MODULE,
.probe = vprbrd_gpio_probe,
- .remove = vprbrd_gpio_remove,
};
static int __init vprbrd_gpio_init(void)
diff --git a/drivers/gpio/gpio-vx855.c b/drivers/gpio/gpio-vx855.c
index 764999cc0..8cdb9f7ec 100644
--- a/drivers/gpio/gpio-vx855.c
+++ b/drivers/gpio/gpio-vx855.c
@@ -259,16 +259,7 @@ static int vx855gpio_probe(struct platform_device *pdev)
vx855gpio_gpio_setup(vg);
- return gpiochip_add_data(&vg->gpio, vg);
-}
-
-static int vx855gpio_remove(struct platform_device *pdev)
-{
- struct vx855_gpio *vg = platform_get_drvdata(pdev);
-
- gpiochip_remove(&vg->gpio);
-
- return 0;
+ return devm_gpiochip_add_data(&pdev->dev, &vg->gpio, vg);
}
static struct platform_driver vx855gpio_driver = {
@@ -276,7 +267,6 @@ static struct platform_driver vx855gpio_driver = {
.name = MODULE_NAME,
},
.probe = vx855gpio_probe,
- .remove = vx855gpio_remove,
};
module_platform_driver(vx855gpio_driver);
diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c
index 98390070f..18cb0f534 100644
--- a/drivers/gpio/gpio-wm831x.c
+++ b/drivers/gpio/gpio-wm831x.c
@@ -259,7 +259,8 @@ static int wm831x_gpio_probe(struct platform_device *pdev)
else
wm831x_gpio->gpio_chip.base = -1;
- ret = gpiochip_add_data(&wm831x_gpio->gpio_chip, wm831x_gpio);
+ ret = devm_gpiochip_add_data(&pdev->dev, &wm831x_gpio->gpio_chip,
+ wm831x_gpio);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
return ret;
@@ -270,19 +271,10 @@ static int wm831x_gpio_probe(struct platform_device *pdev)
return ret;
}
-static int wm831x_gpio_remove(struct platform_device *pdev)
-{
- struct wm831x_gpio *wm831x_gpio = platform_get_drvdata(pdev);
-
- gpiochip_remove(&wm831x_gpio->gpio_chip);
- return 0;
-}
-
static struct platform_driver wm831x_gpio_driver = {
.driver.name = "wm831x-gpio",
.driver.owner = THIS_MODULE,
.probe = wm831x_gpio_probe,
- .remove = wm831x_gpio_remove,
};
static int __init wm831x_gpio_init(void)
diff --git a/drivers/gpio/gpio-wm8350.c b/drivers/gpio/gpio-wm8350.c
index 0a306b4ba..07d45a3b2 100644
--- a/drivers/gpio/gpio-wm8350.c
+++ b/drivers/gpio/gpio-wm8350.c
@@ -125,7 +125,8 @@ static int wm8350_gpio_probe(struct platform_device *pdev)
else
wm8350_gpio->gpio_chip.base = -1;
- ret = gpiochip_add_data(&wm8350_gpio->gpio_chip, wm8350_gpio);
+ ret = devm_gpiochip_add_data(&pdev->dev, &wm8350_gpio->gpio_chip,
+ wm8350_gpio);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
return ret;
@@ -136,19 +137,10 @@ static int wm8350_gpio_probe(struct platform_device *pdev)
return ret;
}
-static int wm8350_gpio_remove(struct platform_device *pdev)
-{
- struct wm8350_gpio_data *wm8350_gpio = platform_get_drvdata(pdev);
-
- gpiochip_remove(&wm8350_gpio->gpio_chip);
- return 0;
-}
-
static struct platform_driver wm8350_gpio_driver = {
.driver.name = "wm8350-gpio",
.driver.owner = THIS_MODULE,
.probe = wm8350_gpio_probe,
- .remove = wm8350_gpio_remove,
};
static int __init wm8350_gpio_init(void)
diff --git a/drivers/gpio/gpio-wm8994.c b/drivers/gpio/gpio-wm8994.c
index 3ae4c1597..b089df99a 100644
--- a/drivers/gpio/gpio-wm8994.c
+++ b/drivers/gpio/gpio-wm8994.c
@@ -261,34 +261,23 @@ static int wm8994_gpio_probe(struct platform_device *pdev)
else
wm8994_gpio->gpio_chip.base = -1;
- ret = gpiochip_add_data(&wm8994_gpio->gpio_chip, wm8994_gpio);
+ ret = devm_gpiochip_add_data(&pdev->dev, &wm8994_gpio->gpio_chip,
+ wm8994_gpio);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
ret);
- goto err;
+ return ret;
}
platform_set_drvdata(pdev, wm8994_gpio);
return ret;
-
-err:
- return ret;
-}
-
-static int wm8994_gpio_remove(struct platform_device *pdev)
-{
- struct wm8994_gpio *wm8994_gpio = platform_get_drvdata(pdev);
-
- gpiochip_remove(&wm8994_gpio->gpio_chip);
- return 0;
}
static struct platform_driver wm8994_gpio_driver = {
.driver.name = "wm8994-gpio",
.driver.owner = THIS_MODULE,
.probe = wm8994_gpio_probe,
- .remove = wm8994_gpio_remove,
};
static int __init wm8994_gpio_init(void)
diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c
new file mode 100644
index 000000000..51f41e8fd
--- /dev/null
+++ b/drivers/gpio/gpio-ws16c48.c
@@ -0,0 +1,427 @@
+/*
+ * GPIO driver for the WinSystems WS16C48
+ * Copyright (C) 2016 William Breathitt Gray
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/gpio/driver.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/irqdesc.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+static unsigned ws16c48_base;
+module_param(ws16c48_base, uint, 0);
+MODULE_PARM_DESC(ws16c48_base, "WinSystems WS16C48 base address");
+static unsigned ws16c48_irq;
+module_param(ws16c48_irq, uint, 0);
+MODULE_PARM_DESC(ws16c48_irq, "WinSystems WS16C48 interrupt line number");
+
+/**
+ * struct ws16c48_gpio - GPIO device private data structure
+ * @chip: instance of the gpio_chip
+ * @io_state: bit I/O state (whether bit is set to input or output)
+ * @out_state: output bits state
+ * @lock: synchronization lock to prevent I/O race conditions
+ * @irq_mask: I/O bits affected by interrupts
+ * @flow_mask: IRQ flow type mask for the respective I/O bits
+ * @base: base port address of the GPIO device
+ * @irq: Interrupt line number
+ */
+struct ws16c48_gpio {
+ struct gpio_chip chip;
+ unsigned char io_state[6];
+ unsigned char out_state[6];
+ spinlock_t lock;
+ unsigned long irq_mask;
+ unsigned long flow_mask;
+ unsigned base;
+ unsigned irq;
+};
+
+static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+ struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
+ const unsigned port = offset / 8;
+ const unsigned mask = BIT(offset % 8);
+
+ return !!(ws16c48gpio->io_state[port] & mask);
+}
+
+static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
+ const unsigned port = offset / 8;
+ const unsigned mask = BIT(offset % 8);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ws16c48gpio->lock, flags);
+
+ ws16c48gpio->io_state[port] |= mask;
+ ws16c48gpio->out_state[port] &= ~mask;
+ outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
+
+ spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
+
+ return 0;
+}
+
+static int ws16c48_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
+ const unsigned port = offset / 8;
+ const unsigned mask = BIT(offset % 8);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ws16c48gpio->lock, flags);
+
+ ws16c48gpio->io_state[port] &= ~mask;
+ if (value)
+ ws16c48gpio->out_state[port] |= mask;
+ else
+ ws16c48gpio->out_state[port] &= ~mask;
+ outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
+
+ spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
+
+ return 0;
+}
+
+static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
+ const unsigned port = offset / 8;
+ const unsigned mask = BIT(offset % 8);
+ unsigned long flags;
+ unsigned port_state;
+
+ spin_lock_irqsave(&ws16c48gpio->lock, flags);
+
+ /* ensure that GPIO is set for input */
+ if (!(ws16c48gpio->io_state[port] & mask)) {
+ spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
+ return -EINVAL;
+ }
+
+ port_state = inb(ws16c48gpio->base + port);
+
+ spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
+
+ return !!(port_state & mask);
+}
+
+static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
+ const unsigned port = offset / 8;
+ const unsigned mask = BIT(offset % 8);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ws16c48gpio->lock, flags);
+
+ /* ensure that GPIO is set for output */
+ if (ws16c48gpio->io_state[port] & mask) {
+ spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
+ return;
+ }
+
+ if (value)
+ ws16c48gpio->out_state[port] |= mask;
+ else
+ ws16c48gpio->out_state[port] &= ~mask;
+ outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
+
+ spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
+}
+
+static void ws16c48_irq_ack(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
+ const unsigned long offset = irqd_to_hwirq(data);
+ const unsigned port = offset / 8;
+ const unsigned mask = BIT(offset % 8);
+ unsigned long flags;
+ unsigned port_state;
+
+ /* only the first 3 ports support interrupts */
+ if (port > 2)
+ return;
+
+ spin_lock_irqsave(&ws16c48gpio->lock, flags);
+
+ port_state = ws16c48gpio->irq_mask >> (8*port);
+
+ outb(0x80, ws16c48gpio->base + 7);
+ outb(port_state & ~mask, ws16c48gpio->base + 8 + port);
+ outb(port_state | mask, ws16c48gpio->base + 8 + port);
+ outb(0xC0, ws16c48gpio->base + 7);
+
+ spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
+}
+
+static void ws16c48_irq_mask(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
+ const unsigned long offset = irqd_to_hwirq(data);
+ const unsigned long mask = BIT(offset);
+ const unsigned port = offset / 8;
+ unsigned long flags;
+
+ /* only the first 3 ports support interrupts */
+ if (port > 2)
+ return;
+
+ spin_lock_irqsave(&ws16c48gpio->lock, flags);
+
+ ws16c48gpio->irq_mask &= ~mask;
+
+ outb(0x80, ws16c48gpio->base + 7);
+ outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port);
+ outb(0xC0, ws16c48gpio->base + 7);
+
+ spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
+}
+
+static void ws16c48_irq_unmask(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
+ const unsigned long offset = irqd_to_hwirq(data);
+ const unsigned long mask = BIT(offset);
+ const unsigned port = offset / 8;
+ unsigned long flags;
+
+ /* only the first 3 ports support interrupts */
+ if (port > 2)
+ return;
+
+ spin_lock_irqsave(&ws16c48gpio->lock, flags);
+
+ ws16c48gpio->irq_mask |= mask;
+
+ outb(0x80, ws16c48gpio->base + 7);
+ outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port);
+ outb(0xC0, ws16c48gpio->base + 7);
+
+ spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
+}
+
+static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
+ const unsigned long offset = irqd_to_hwirq(data);
+ const unsigned long mask = BIT(offset);
+ const unsigned port = offset / 8;
+ unsigned long flags;
+
+ /* only the first 3 ports support interrupts */
+ if (port > 2)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ws16c48gpio->lock, flags);
+
+ switch (flow_type) {
+ case IRQ_TYPE_NONE:
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ ws16c48gpio->flow_mask |= mask;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ ws16c48gpio->flow_mask &= ~mask;
+ break;
+ default:
+ spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
+ return -EINVAL;
+ }
+
+ outb(0x40, ws16c48gpio->base + 7);
+ outb(ws16c48gpio->flow_mask >> (8*port), ws16c48gpio->base + 8 + port);
+ outb(0xC0, ws16c48gpio->base + 7);
+
+ spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
+
+ return 0;
+}
+
+static struct irq_chip ws16c48_irqchip = {
+ .name = "ws16c48",
+ .irq_ack = ws16c48_irq_ack,
+ .irq_mask = ws16c48_irq_mask,
+ .irq_unmask = ws16c48_irq_unmask,
+ .irq_set_type = ws16c48_irq_set_type
+};
+
+static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id)
+{
+ struct ws16c48_gpio *const ws16c48gpio = dev_id;
+ struct gpio_chip *const chip = &ws16c48gpio->chip;
+ unsigned long int_pending;
+ unsigned long port;
+ unsigned long int_id;
+ unsigned long gpio;
+
+ int_pending = inb(ws16c48gpio->base + 6) & 0x7;
+ if (!int_pending)
+ return IRQ_NONE;
+
+ /* loop until all pending interrupts are handled */
+ do {
+ for_each_set_bit(port, &int_pending, 3) {
+ int_id = inb(ws16c48gpio->base + 8 + port);
+ for_each_set_bit(gpio, &int_id, 8)
+ generic_handle_irq(irq_find_mapping(
+ chip->irqdomain, gpio + 8*port));
+ }
+
+ int_pending = inb(ws16c48gpio->base + 6) & 0x7;
+ } while (int_pending);
+
+ return IRQ_HANDLED;
+}
+
+static int __init ws16c48_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ws16c48_gpio *ws16c48gpio;
+ const unsigned base = ws16c48_base;
+ const unsigned extent = 16;
+ const char *const name = dev_name(dev);
+ int err;
+ const unsigned irq = ws16c48_irq;
+
+ ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL);
+ if (!ws16c48gpio)
+ return -ENOMEM;
+
+ if (!devm_request_region(dev, base, extent, name)) {
+ dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
+ base, base + extent);
+ return -EBUSY;
+ }
+
+ ws16c48gpio->chip.label = name;
+ ws16c48gpio->chip.parent = dev;
+ ws16c48gpio->chip.owner = THIS_MODULE;
+ ws16c48gpio->chip.base = -1;
+ ws16c48gpio->chip.ngpio = 48;
+ ws16c48gpio->chip.get_direction = ws16c48_gpio_get_direction;
+ ws16c48gpio->chip.direction_input = ws16c48_gpio_direction_input;
+ ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output;
+ ws16c48gpio->chip.get = ws16c48_gpio_get;
+ ws16c48gpio->chip.set = ws16c48_gpio_set;
+ ws16c48gpio->base = base;
+ ws16c48gpio->irq = irq;
+
+ spin_lock_init(&ws16c48gpio->lock);
+
+ dev_set_drvdata(dev, ws16c48gpio);
+
+ err = gpiochip_add_data(&ws16c48gpio->chip, ws16c48gpio);
+ if (err) {
+ dev_err(dev, "GPIO registering failed (%d)\n", err);
+ return err;
+ }
+
+ /* Disable IRQ by default */
+ outb(0x80, base + 7);
+ outb(0, base + 8);
+ outb(0, base + 9);
+ outb(0, base + 10);
+ outb(0xC0, base + 7);
+
+ err = gpiochip_irqchip_add(&ws16c48gpio->chip, &ws16c48_irqchip, 0,
+ handle_edge_irq, IRQ_TYPE_NONE);
+ if (err) {
+ dev_err(dev, "Could not add irqchip (%d)\n", err);
+ goto err_gpiochip_remove;
+ }
+
+ err = request_irq(irq, ws16c48_irq_handler, IRQF_SHARED, name,
+ ws16c48gpio);
+ if (err) {
+ dev_err(dev, "IRQ handler registering failed (%d)\n", err);
+ goto err_gpiochip_remove;
+ }
+
+ return 0;
+
+err_gpiochip_remove:
+ gpiochip_remove(&ws16c48gpio->chip);
+ return err;
+}
+
+static int ws16c48_remove(struct platform_device *pdev)
+{
+ struct ws16c48_gpio *const ws16c48gpio = platform_get_drvdata(pdev);
+
+ free_irq(ws16c48gpio->irq, ws16c48gpio);
+ gpiochip_remove(&ws16c48gpio->chip);
+
+ return 0;
+}
+
+static struct platform_device *ws16c48_device;
+
+static struct platform_driver ws16c48_driver = {
+ .driver = {
+ .name = "ws16c48"
+ },
+ .remove = ws16c48_remove
+};
+
+static void __exit ws16c48_exit(void)
+{
+ platform_device_unregister(ws16c48_device);
+ platform_driver_unregister(&ws16c48_driver);
+}
+
+static int __init ws16c48_init(void)
+{
+ int err;
+
+ ws16c48_device = platform_device_alloc(ws16c48_driver.driver.name, -1);
+ if (!ws16c48_device)
+ return -ENOMEM;
+
+ err = platform_device_add(ws16c48_device);
+ if (err)
+ goto err_platform_device;
+
+ err = platform_driver_probe(&ws16c48_driver, ws16c48_probe);
+ if (err)
+ goto err_platform_driver;
+
+ return 0;
+
+err_platform_driver:
+ platform_device_del(ws16c48_device);
+err_platform_device:
+ platform_device_put(ws16c48_device);
+ return err;
+}
+
+module_init(ws16c48_init);
+module_exit(ws16c48_exit);
+
+MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
+MODULE_DESCRIPTION("WinSystems WS16C48 GPIO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-xgene-sb.c b/drivers/gpio/gpio-xgene-sb.c
index 282004deb..31cbcb84c 100644
--- a/drivers/gpio/gpio-xgene-sb.c
+++ b/drivers/gpio/gpio-xgene-sb.c
@@ -2,8 +2,9 @@
* AppliedMicro X-Gene SoC GPIO-Standby Driver
*
* Copyright (c) 2014, Applied Micro Circuits Corporation
- * Author: Tin Huynh <tnhuynh@apm.com>.
- * Y Vo <yvo@apm.com>.
+ * Author: Tin Huynh <tnhuynh@apm.com>.
+ * Y Vo <yvo@apm.com>.
+ * Quan Nguyen <qnguyen@apm.com>.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -28,9 +29,14 @@
#include "gpiolib.h"
-#define XGENE_MAX_GPIO_DS 22
-#define XGENE_MAX_GPIO_DS_IRQ 6
+/* Common property names */
+#define XGENE_NIRQ_PROPERTY "apm,nr-irqs"
+#define XGENE_NGPIO_PROPERTY "apm,nr-gpios"
+#define XGENE_IRQ_START_PROPERTY "apm,irq-start"
+#define XGENE_DFLT_MAX_NGPIO 22
+#define XGENE_DFLT_MAX_NIRQ 6
+#define XGENE_DFLT_IRQ_START_PIN 8
#define GPIO_MASK(x) (1U << ((x) % 32))
#define MPA_GPIO_INT_LVL 0x0290
@@ -39,19 +45,32 @@
#define MPA_GPIO_IN_ADDR 0x02a4
#define MPA_GPIO_SEL_LO 0x0294
+#define GPIO_INT_LEVEL_H 0x000001
+#define GPIO_INT_LEVEL_L 0x000000
+
/**
* struct xgene_gpio_sb - GPIO-Standby private data structure.
* @gc: memory-mapped GPIO controllers.
- * @irq: Mapping GPIO pins and interrupt number
- * nirq: Number of GPIO pins that supports interrupt
+ * @regs: GPIO register base offset
+ * @irq_domain: GPIO interrupt domain
+ * @irq_start: GPIO pin that start support interrupt
+ * @nirq: Number of GPIO pins that supports interrupt
+ * @parent_irq_base: Start parent HWIRQ
*/
struct xgene_gpio_sb {
struct gpio_chip gc;
- u32 *irq;
- u32 nirq;
+ void __iomem *regs;
+ struct irq_domain *irq_domain;
+ u16 irq_start;
+ u16 nirq;
+ u16 parent_irq_base;
};
-static void xgene_gpio_set_bit(struct gpio_chip *gc, void __iomem *reg, u32 gpio, int val)
+#define HWIRQ_TO_GPIO(priv, hwirq) ((hwirq) + (priv)->irq_start)
+#define GPIO_TO_HWIRQ(priv, gpio) ((gpio) - (priv)->irq_start)
+
+static void xgene_gpio_set_bit(struct gpio_chip *gc,
+ void __iomem *reg, u32 gpio, int val)
{
u32 data;
@@ -63,23 +82,170 @@ static void xgene_gpio_set_bit(struct gpio_chip *gc, void __iomem *reg, u32 gpio
gc->write_reg(reg, data);
}
-static int apm_gpio_sb_to_irq(struct gpio_chip *gc, u32 gpio)
+static int xgene_gpio_sb_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct xgene_gpio_sb *priv = irq_data_get_irq_chip_data(d);
+ int gpio = HWIRQ_TO_GPIO(priv, d->hwirq);
+ int lvl_type = GPIO_INT_LEVEL_H;
+
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_RISING:
+ case IRQ_TYPE_LEVEL_HIGH:
+ lvl_type = GPIO_INT_LEVEL_H;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ case IRQ_TYPE_LEVEL_LOW:
+ lvl_type = GPIO_INT_LEVEL_L;
+ break;
+ default:
+ break;
+ }
+
+ xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_SEL_LO,
+ gpio * 2, 1);
+ xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_INT_LVL,
+ d->hwirq, lvl_type);
+
+ /* Propagate IRQ type setting to parent */
+ if (type & IRQ_TYPE_EDGE_BOTH)
+ return irq_chip_set_type_parent(d, IRQ_TYPE_EDGE_RISING);
+ else
+ return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
+}
+
+static struct irq_chip xgene_gpio_sb_irq_chip = {
+ .name = "sbgpio",
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_set_type = xgene_gpio_sb_irq_set_type,
+};
+
+static int xgene_gpio_sb_to_irq(struct gpio_chip *gc, u32 gpio)
{
struct xgene_gpio_sb *priv = gpiochip_get_data(gc);
+ struct irq_fwspec fwspec;
+
+ if ((gpio < priv->irq_start) ||
+ (gpio > HWIRQ_TO_GPIO(priv, priv->nirq)))
+ return -ENXIO;
+
+ if (gc->parent->of_node)
+ fwspec.fwnode = of_node_to_fwnode(gc->parent->of_node);
+ else
+ fwspec.fwnode = gc->parent->fwnode;
+ fwspec.param_count = 2;
+ fwspec.param[0] = GPIO_TO_HWIRQ(priv, gpio);
+ fwspec.param[1] = IRQ_TYPE_NONE;
+ return irq_create_fwspec_mapping(&fwspec);
+}
+
+static void xgene_gpio_sb_domain_activate(struct irq_domain *d,
+ struct irq_data *irq_data)
+{
+ struct xgene_gpio_sb *priv = d->host_data;
+ u32 gpio = HWIRQ_TO_GPIO(priv, irq_data->hwirq);
+
+ if (gpiochip_lock_as_irq(&priv->gc, gpio)) {
+ dev_err(priv->gc.parent,
+ "Unable to configure XGene GPIO standby pin %d as IRQ\n",
+ gpio);
+ return;
+ }
+
+ xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_SEL_LO,
+ gpio * 2, 1);
+}
+
+static void xgene_gpio_sb_domain_deactivate(struct irq_domain *d,
+ struct irq_data *irq_data)
+{
+ struct xgene_gpio_sb *priv = d->host_data;
+ u32 gpio = HWIRQ_TO_GPIO(priv, irq_data->hwirq);
+
+ gpiochip_unlock_as_irq(&priv->gc, gpio);
+ xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_SEL_LO,
+ gpio * 2, 0);
+}
+
+static int xgene_gpio_sb_domain_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
+{
+ struct xgene_gpio_sb *priv = d->host_data;
+
+ if ((fwspec->param_count != 2) ||
+ (fwspec->param[0] >= priv->nirq))
+ return -EINVAL;
+ *hwirq = fwspec->param[0];
+ *type = fwspec->param[1];
+ return 0;
+}
+
+static int xgene_gpio_sb_domain_alloc(struct irq_domain *domain,
+ unsigned int virq,
+ unsigned int nr_irqs, void *data)
+{
+ struct irq_fwspec *fwspec = data;
+ struct irq_fwspec parent_fwspec;
+ struct xgene_gpio_sb *priv = domain->host_data;
+ irq_hw_number_t hwirq;
+ unsigned int i;
+
+ hwirq = fwspec->param[0];
+ for (i = 0; i < nr_irqs; i++)
+ irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+ &xgene_gpio_sb_irq_chip, priv);
- if (priv->irq[gpio])
- return priv->irq[gpio];
+ parent_fwspec.fwnode = domain->parent->fwnode;
+ if (is_of_node(parent_fwspec.fwnode)) {
+ parent_fwspec.param_count = 3;
+ parent_fwspec.param[0] = 0;/* SPI */
+ /* Skip SGIs and PPIs*/
+ parent_fwspec.param[1] = hwirq + priv->parent_irq_base - 32;
+ parent_fwspec.param[2] = fwspec->param[1];
+ } else if (is_fwnode_irqchip(parent_fwspec.fwnode)) {
+ parent_fwspec.param_count = 2;
+ parent_fwspec.param[0] = hwirq + priv->parent_irq_base;
+ parent_fwspec.param[1] = fwspec->param[1];
+ } else
+ return -EINVAL;
- return -ENXIO;
+ return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+ &parent_fwspec);
}
+static void xgene_gpio_sb_domain_free(struct irq_domain *domain,
+ unsigned int virq,
+ unsigned int nr_irqs)
+{
+ struct irq_data *d;
+ unsigned int i;
+
+ for (i = 0; i < nr_irqs; i++) {
+ d = irq_domain_get_irq_data(domain, virq + i);
+ irq_domain_reset_irq_data(d);
+ }
+}
+
+static const struct irq_domain_ops xgene_gpio_sb_domain_ops = {
+ .translate = xgene_gpio_sb_domain_translate,
+ .alloc = xgene_gpio_sb_domain_alloc,
+ .free = xgene_gpio_sb_domain_free,
+ .activate = xgene_gpio_sb_domain_activate,
+ .deactivate = xgene_gpio_sb_domain_deactivate,
+};
+
static int xgene_gpio_sb_probe(struct platform_device *pdev)
{
struct xgene_gpio_sb *priv;
- u32 ret, i;
- u32 default_lines[] = {0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D};
+ int ret;
struct resource *res;
void __iomem *regs;
+ struct irq_domain *parent_domain = NULL;
+ struct fwnode_handle *fwnode;
+ u32 val32;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -90,6 +256,18 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev)
if (IS_ERR(regs))
return PTR_ERR(regs);
+ priv->regs = regs;
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret > 0) {
+ priv->parent_irq_base = irq_get_irq_data(ret)->hwirq;
+ parent_domain = irq_get_irq_data(ret)->domain;
+ }
+ if (!parent_domain) {
+ dev_err(&pdev->dev, "unable to obtain parent domain\n");
+ return -ENODEV;
+ }
+
ret = bgpio_init(&priv->gc, &pdev->dev, 4,
regs + MPA_GPIO_IN_ADDR,
regs + MPA_GPIO_OUT_ADDR, NULL,
@@ -97,30 +275,51 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev)
if (ret)
return ret;
- priv->gc.to_irq = apm_gpio_sb_to_irq;
- priv->gc.ngpio = XGENE_MAX_GPIO_DS;
+ priv->gc.to_irq = xgene_gpio_sb_to_irq;
- priv->nirq = XGENE_MAX_GPIO_DS_IRQ;
+ /* Retrieve start irq pin, use default if property not found */
+ priv->irq_start = XGENE_DFLT_IRQ_START_PIN;
+ if (!device_property_read_u32(&pdev->dev,
+ XGENE_IRQ_START_PROPERTY, &val32))
+ priv->irq_start = val32;
- priv->irq = devm_kzalloc(&pdev->dev, sizeof(u32) * XGENE_MAX_GPIO_DS,
- GFP_KERNEL);
- if (!priv->irq)
- return -ENOMEM;
+ /* Retrieve number irqs, use default if property not found */
+ priv->nirq = XGENE_DFLT_MAX_NIRQ;
+ if (!device_property_read_u32(&pdev->dev, XGENE_NIRQ_PROPERTY, &val32))
+ priv->nirq = val32;
- for (i = 0; i < priv->nirq; i++) {
- priv->irq[default_lines[i]] = platform_get_irq(pdev, i);
- xgene_gpio_set_bit(&priv->gc, regs + MPA_GPIO_SEL_LO,
- default_lines[i] * 2, 1);
- xgene_gpio_set_bit(&priv->gc, regs + MPA_GPIO_INT_LVL, i, 1);
- }
+ /* Retrieve number gpio, use default if property not found */
+ priv->gc.ngpio = XGENE_DFLT_MAX_NGPIO;
+ if (!device_property_read_u32(&pdev->dev, XGENE_NGPIO_PROPERTY, &val32))
+ priv->gc.ngpio = val32;
+
+ dev_info(&pdev->dev, "Support %d gpios, %d irqs start from pin %d\n",
+ priv->gc.ngpio, priv->nirq, priv->irq_start);
platform_set_drvdata(pdev, priv);
- ret = gpiochip_add_data(&priv->gc, priv);
- if (ret)
- dev_err(&pdev->dev, "failed to register X-Gene GPIO Standby driver\n");
+ if (pdev->dev.of_node)
+ fwnode = of_node_to_fwnode(pdev->dev.of_node);
else
- dev_info(&pdev->dev, "X-Gene GPIO Standby driver registered\n");
+ fwnode = pdev->dev.fwnode;
+
+ priv->irq_domain = irq_domain_create_hierarchy(parent_domain,
+ 0, priv->nirq, fwnode,
+ &xgene_gpio_sb_domain_ops, priv);
+ if (!priv->irq_domain)
+ return -ENODEV;
+
+ priv->gc.irqdomain = priv->irq_domain;
+
+ ret = devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to register X-Gene GPIO Standby driver\n");
+ irq_domain_remove(priv->irq_domain);
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "X-Gene GPIO Standby driver registered\n");
if (priv->nirq > 0) {
/* Register interrupt handlers for gpio signaled acpi events */
@@ -138,7 +337,8 @@ static int xgene_gpio_sb_remove(struct platform_device *pdev)
acpi_gpiochip_free_interrupts(&priv->gc);
}
- gpiochip_remove(&priv->gc);
+ irq_domain_remove(priv->irq_domain);
+
return 0;
}
diff --git a/drivers/gpio/gpio-xgene.c b/drivers/gpio/gpio-xgene.c
index 592e9cdf9..0dc916191 100644
--- a/drivers/gpio/gpio-xgene.c
+++ b/drivers/gpio/gpio-xgene.c
@@ -173,6 +173,11 @@ static int xgene_gpio_probe(struct platform_device *pdev)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ err = -EINVAL;
+ goto err;
+ }
+
gpio->base = devm_ioremap_nocache(&pdev->dev, res->start,
resource_size(res));
if (!gpio->base) {
@@ -193,7 +198,7 @@ static int xgene_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, gpio);
- err = gpiochip_add_data(&gpio->chip, gpio);
+ err = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
if (err) {
dev_err(&pdev->dev,
"failed to register gpiochip.\n");
@@ -207,14 +212,6 @@ err:
return err;
}
-static int xgene_gpio_remove(struct platform_device *pdev)
-{
- struct xgene_gpio *gpio = platform_get_drvdata(pdev);
-
- gpiochip_remove(&gpio->chip);
- return 0;
-}
-
static const struct of_device_id xgene_gpio_of_match[] = {
{ .compatible = "apm,xgene-gpio", },
{},
@@ -228,7 +225,6 @@ static struct platform_driver xgene_gpio_driver = {
.pm = XGENE_GPIO_PM_OPS,
},
.probe = xgene_gpio_probe,
- .remove = xgene_gpio_remove,
};
module_platform_driver(xgene_gpio_driver);
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index cc4d9bd08..2dc52585e 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -71,29 +71,29 @@ static int acpi_gpiochip_find(struct gpio_chip *gc, void *data)
* controller uses pin controller and the mapping is not contiguous the
* offset might be different.
*/
-static int acpi_gpiochip_pin_to_gpio_offset(struct gpio_chip *chip, int pin)
+static int acpi_gpiochip_pin_to_gpio_offset(struct gpio_device *gdev, int pin)
{
struct gpio_pin_range *pin_range;
/* If there are no ranges in this chip, use 1:1 mapping */
- if (list_empty(&chip->pin_ranges))
+ if (list_empty(&gdev->pin_ranges))
return pin;
- list_for_each_entry(pin_range, &chip->pin_ranges, node) {
+ list_for_each_entry(pin_range, &gdev->pin_ranges, node) {
const struct pinctrl_gpio_range *range = &pin_range->range;
int i;
if (range->pins) {
for (i = 0; i < range->npins; i++) {
if (range->pins[i] == pin)
- return range->base + i - chip->base;
+ return range->base + i - gdev->base;
}
} else {
if (pin >= range->pin_base &&
pin < range->pin_base + range->npins) {
unsigned gpio_base;
- gpio_base = range->base - chip->base;
+ gpio_base = range->base - gdev->base;
return gpio_base + pin - range->pin_base;
}
}
@@ -102,7 +102,7 @@ static int acpi_gpiochip_pin_to_gpio_offset(struct gpio_chip *chip, int pin)
return -EINVAL;
}
#else
-static inline int acpi_gpiochip_pin_to_gpio_offset(struct gpio_chip *chip,
+static inline int acpi_gpiochip_pin_to_gpio_offset(struct gpio_device *gdev,
int pin)
{
return pin;
@@ -134,7 +134,7 @@ static struct gpio_desc *acpi_get_gpiod(char *path, int pin)
if (!chip)
return ERR_PTR(-EPROBE_DEFER);
- offset = acpi_gpiochip_pin_to_gpio_offset(chip, pin);
+ offset = acpi_gpiochip_pin_to_gpio_offset(chip->gpiodev, pin);
if (offset < 0)
return ERR_PTR(offset);
@@ -202,7 +202,7 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares,
if (!handler)
return AE_BAD_PARAMETER;
- pin = acpi_gpiochip_pin_to_gpio_offset(chip, pin);
+ pin = acpi_gpiochip_pin_to_gpio_offset(chip->gpiodev, pin);
if (pin < 0)
return AE_BAD_PARAMETER;
@@ -673,7 +673,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
struct gpio_desc *desc;
bool found;
- pin = acpi_gpiochip_pin_to_gpio_offset(chip, pin);
+ pin = acpi_gpiochip_pin_to_gpio_offset(chip->gpiodev, pin);
if (pin < 0) {
status = AE_BAD_PARAMETER;
goto out;
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 405dfcaad..932e510ae 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -180,7 +180,7 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
* Remove this redundant call (along with the corresponding
* unlock) when those drivers have been fixed.
*/
- ret = gpiochip_lock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
+ ret = gpiochip_lock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc));
if (ret < 0)
goto err_put_kn;
@@ -194,7 +194,7 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
return 0;
err_unlock:
- gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
+ gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc));
err_put_kn:
sysfs_put(data->value_kn);
@@ -212,7 +212,7 @@ static void gpio_sysfs_free_irq(struct device *dev)
data->irq_flags = 0;
free_irq(data->irq, data);
- gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
+ gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc));
sysfs_put(data->value_kn);
}
@@ -547,6 +547,7 @@ static struct class gpio_class = {
int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
{
struct gpio_chip *chip;
+ struct gpio_device *gdev;
struct gpiod_data *data;
unsigned long flags;
int status;
@@ -565,12 +566,13 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
return -EINVAL;
}
- chip = desc->chip;
+ gdev = desc->gdev;
+ chip = gdev->chip;
mutex_lock(&sysfs_lock);
/* check if chip is being removed */
- if (!chip || !chip->cdev) {
+ if (!chip || !gdev->mockdev) {
status = -ENODEV;
goto err_unlock;
}
@@ -605,7 +607,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
if (chip->names && chip->names[offset])
ioname = chip->names[offset];
- dev = device_create_with_groups(&gpio_class, chip->parent,
+ dev = device_create_with_groups(&gpio_class, &gdev->dev,
MKDEV(0, 0), data, gpio_groups,
ioname ? ioname : "gpio%u",
desc_to_gpio(desc));
@@ -716,9 +718,11 @@ err_unlock:
}
EXPORT_SYMBOL_GPL(gpiod_unexport);
-int gpiochip_sysfs_register(struct gpio_chip *chip)
+int gpiochip_sysfs_register(struct gpio_device *gdev)
{
struct device *dev;
+ struct device *parent;
+ struct gpio_chip *chip = gdev->chip;
/*
* Many systems add gpio chips for SOC support very early,
@@ -729,8 +733,17 @@ int gpiochip_sysfs_register(struct gpio_chip *chip)
if (!gpio_class.p)
return 0;
+ /*
+ * For sysfs backward compatibility we need to preserve this
+ * preferred parenting to the gpio_chip parent field, if set.
+ */
+ if (chip->parent)
+ parent = chip->parent;
+ else
+ parent = &gdev->dev;
+
/* use chip->base for the ID; it's already known to be unique */
- dev = device_create_with_groups(&gpio_class, chip->parent,
+ dev = device_create_with_groups(&gpio_class, parent,
MKDEV(0, 0),
chip, gpiochip_groups,
"gpiochip%d", chip->base);
@@ -738,30 +751,31 @@ int gpiochip_sysfs_register(struct gpio_chip *chip)
return PTR_ERR(dev);
mutex_lock(&sysfs_lock);
- chip->cdev = dev;
+ gdev->mockdev = dev;
mutex_unlock(&sysfs_lock);
return 0;
}
-void gpiochip_sysfs_unregister(struct gpio_chip *chip)
+void gpiochip_sysfs_unregister(struct gpio_device *gdev)
{
struct gpio_desc *desc;
+ struct gpio_chip *chip = gdev->chip;
unsigned int i;
- if (!chip->cdev)
+ if (!gdev->mockdev)
return;
- device_unregister(chip->cdev);
+ device_unregister(gdev->mockdev);
/* prevent further gpiod exports */
mutex_lock(&sysfs_lock);
- chip->cdev = NULL;
+ gdev->mockdev = NULL;
mutex_unlock(&sysfs_lock);
/* unregister gpiod class devices owned by sysfs */
for (i = 0; i < chip->ngpio; i++) {
- desc = &chip->desc[i];
+ desc = &gdev->descs[i];
if (test_and_clear_bit(FLAG_SYSFS, &desc->flags))
gpiod_free(desc);
}
@@ -771,7 +785,7 @@ static int __init gpiolib_sysfs_init(void)
{
int status;
unsigned long flags;
- struct gpio_chip *chip;
+ struct gpio_device *gdev;
status = class_register(&gpio_class);
if (status < 0)
@@ -784,8 +798,8 @@ static int __init gpiolib_sysfs_init(void)
* registered, and so arch_initcall() can always gpio_export().
*/
spin_lock_irqsave(&gpio_lock, flags);
- list_for_each_entry(chip, &gpio_chips, list) {
- if (chip->cdev)
+ list_for_each_entry(gdev, &gpio_devices, list) {
+ if (gdev->mockdev)
continue;
/*
@@ -798,12 +812,11 @@ static int __init gpiolib_sysfs_init(void)
* gpio_lock prevents us from doing this.
*/
spin_unlock_irqrestore(&gpio_lock, flags);
- status = gpiochip_sysfs_register(chip);
+ status = gpiochip_sysfs_register(gdev);
spin_lock_irqsave(&gpio_lock, flags);
}
spin_unlock_irqrestore(&gpio_lock, flags);
-
return status;
}
postcore_initcall(gpiolib_sysfs_init);
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 5c1ba879f..b747c76fd 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -16,6 +16,11 @@
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/idr.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/gpio.h>
#include "gpiolib.h"
@@ -42,6 +47,14 @@
#define extra_checks 0
#endif
+/* Device and char device-related information */
+static DEFINE_IDA(gpio_ida);
+static dev_t gpio_devt;
+#define GPIO_DEV_MAX 256 /* 256 GPIO chip devices supported */
+static struct bus_type gpio_bus_type = {
+ .name = "gpio",
+};
+
/* gpio_lock prevents conflicts during gpio_desc[] table updates.
* While any GPIO is requested, its gpio_chip is not removable;
* each GPIO's "requested" flag serves as a lock and refcount.
@@ -50,12 +63,12 @@ DEFINE_SPINLOCK(gpio_lock);
static DEFINE_MUTEX(gpio_lookup_lock);
static LIST_HEAD(gpio_lookup_list);
-LIST_HEAD(gpio_chips);
-
+LIST_HEAD(gpio_devices);
static void gpiochip_free_hogs(struct gpio_chip *chip);
static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);
+static bool gpiolib_initialized;
static inline void desc_set_label(struct gpio_desc *d, const char *label)
{
@@ -67,15 +80,16 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label)
*/
struct gpio_desc *gpio_to_desc(unsigned gpio)
{
- struct gpio_chip *chip;
+ struct gpio_device *gdev;
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
- list_for_each_entry(chip, &gpio_chips, list) {
- if (chip->base <= gpio && chip->base + chip->ngpio > gpio) {
+ list_for_each_entry(gdev, &gpio_devices, list) {
+ if (gdev->base <= gpio &&
+ gdev->base + gdev->ngpio > gpio) {
spin_unlock_irqrestore(&gpio_lock, flags);
- return &chip->desc[gpio - chip->base];
+ return &gdev->descs[gpio - gdev->base];
}
}
@@ -94,10 +108,12 @@ EXPORT_SYMBOL_GPL(gpio_to_desc);
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip,
u16 hwnum)
{
- if (hwnum >= chip->ngpio)
+ struct gpio_device *gdev = chip->gpiodev;
+
+ if (hwnum >= gdev->ngpio)
return ERR_PTR(-EINVAL);
- return &chip->desc[hwnum];
+ return &gdev->descs[hwnum];
}
/**
@@ -107,7 +123,7 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip,
*/
int desc_to_gpio(const struct gpio_desc *desc)
{
- return desc->chip->base + (desc - &desc->chip->desc[0]);
+ return desc->gdev->base + (desc - &desc->gdev->descs[0]);
}
EXPORT_SYMBOL_GPL(desc_to_gpio);
@@ -118,23 +134,25 @@ EXPORT_SYMBOL_GPL(desc_to_gpio);
*/
struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc)
{
- return desc ? desc->chip : NULL;
+ if (!desc || !desc->gdev || !desc->gdev->chip)
+ return NULL;
+ return desc->gdev->chip;
}
EXPORT_SYMBOL_GPL(gpiod_to_chip);
/* dynamic allocation of GPIOs, e.g. on a hotplugged device */
static int gpiochip_find_base(int ngpio)
{
- struct gpio_chip *chip;
+ struct gpio_device *gdev;
int base = ARCH_NR_GPIOS - ngpio;
- list_for_each_entry_reverse(chip, &gpio_chips, list) {
+ list_for_each_entry_reverse(gdev, &gpio_devices, list) {
/* found a free space? */
- if (chip->base + chip->ngpio <= base)
+ if (gdev->base + gdev->ngpio <= base)
break;
else
/* nope, check the space right before the chip */
- base = chip->base - ngpio;
+ base = gdev->base - ngpio;
}
if (gpio_is_valid(base)) {
@@ -187,57 +205,45 @@ EXPORT_SYMBOL_GPL(gpiod_get_direction);
* Return -EBUSY if the new chip overlaps with some other chip's integer
* space.
*/
-static int gpiochip_add_to_list(struct gpio_chip *chip)
+static int gpiodev_add_to_list(struct gpio_device *gdev)
{
- struct gpio_chip *iterator;
- struct gpio_chip *previous = NULL;
+ struct gpio_device *prev, *next;
- if (list_empty(&gpio_chips)) {
- list_add_tail(&chip->list, &gpio_chips);
+ if (list_empty(&gpio_devices)) {
+ /* initial entry in list */
+ list_add_tail(&gdev->list, &gpio_devices);
return 0;
}
- list_for_each_entry(iterator, &gpio_chips, list) {
- if (iterator->base >= chip->base + chip->ngpio) {
- /*
- * Iterator is the first GPIO chip so there is no
- * previous one
- */
- if (!previous) {
- goto found;
- } else {
- /*
- * We found a valid range(means
- * [base, base + ngpio - 1]) between previous
- * and iterator chip.
- */
- if (previous->base + previous->ngpio
- <= chip->base)
- goto found;
- }
- }
- previous = iterator;
+ next = list_entry(gpio_devices.next, struct gpio_device, list);
+ if (gdev->base + gdev->ngpio <= next->base) {
+ /* add before first entry */
+ list_add(&gdev->list, &gpio_devices);
+ return 0;
}
- /*
- * We are beyond the last chip in the list and iterator now
- * points to the head.
- * Let iterator point to the last chip in the list.
- */
-
- iterator = list_last_entry(&gpio_chips, struct gpio_chip, list);
- if (iterator->base + iterator->ngpio <= chip->base) {
- list_add(&chip->list, &iterator->list);
+ prev = list_entry(gpio_devices.prev, struct gpio_device, list);
+ if (prev->base + prev->ngpio <= gdev->base) {
+ /* add behind last entry */
+ list_add_tail(&gdev->list, &gpio_devices);
return 0;
}
- dev_err(chip->parent,
- "GPIO integer space overlap, cannot add chip\n");
- return -EBUSY;
+ list_for_each_entry_safe(prev, next, &gpio_devices, list) {
+ /* at the end of the list */
+ if (&next->list == &gpio_devices)
+ break;
-found:
- list_add_tail(&chip->list, &iterator->list);
- return 0;
+ /* add between prev and next */
+ if (prev->base + prev->ngpio <= gdev->base
+ && gdev->base + gdev->ngpio <= next->base) {
+ list_add(&gdev->list, &prev->list);
+ return 0;
+ }
+ }
+
+ dev_err(&gdev->dev, "GPIO integer space overlap, cannot add chip\n");
+ return -EBUSY;
}
/**
@@ -245,23 +251,23 @@ found:
*/
static struct gpio_desc *gpio_name_to_desc(const char * const name)
{
- struct gpio_chip *chip;
+ struct gpio_device *gdev;
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
- list_for_each_entry(chip, &gpio_chips, list) {
+ list_for_each_entry(gdev, &gpio_devices, list) {
int i;
- for (i = 0; i != chip->ngpio; ++i) {
- struct gpio_desc *gpio = &chip->desc[i];
+ for (i = 0; i != gdev->ngpio; ++i) {
+ struct gpio_desc *desc = &gdev->descs[i];
- if (!gpio->name || !name)
+ if (!desc->name || !name)
continue;
- if (!strcmp(gpio->name, name)) {
+ if (!strcmp(desc->name, name)) {
spin_unlock_irqrestore(&gpio_lock, flags);
- return gpio;
+ return desc;
}
}
}
@@ -279,6 +285,7 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name)
*/
static int gpiochip_set_desc_names(struct gpio_chip *gc)
{
+ struct gpio_device *gdev = gc->gpiodev;
int i;
if (!gc->names)
@@ -290,19 +297,208 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
gpio = gpio_name_to_desc(gc->names[i]);
if (gpio)
- dev_warn(gc->parent, "Detected name collision for "
- "GPIO name '%s'\n",
+ dev_warn(&gdev->dev,
+ "Detected name collision for GPIO name '%s'\n",
gc->names[i]);
}
/* Then add all names to the GPIO descriptors */
for (i = 0; i != gc->ngpio; ++i)
- gc->desc[i].name = gc->names[i];
+ gdev->descs[i].name = gc->names[i];
+
+ return 0;
+}
+
+/**
+ * gpio_ioctl() - ioctl handler for the GPIO chardev
+ */
+static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct gpio_device *gdev = filp->private_data;
+ struct gpio_chip *chip = gdev->chip;
+ int __user *ip = (int __user *)arg;
+
+ /* We fail any subsequent ioctl():s when the chip is gone */
+ if (!chip)
+ return -ENODEV;
+
+ /* Fill in the struct and pass to userspace */
+ if (cmd == GPIO_GET_CHIPINFO_IOCTL) {
+ struct gpiochip_info chipinfo;
+
+ strncpy(chipinfo.name, dev_name(&gdev->dev),
+ sizeof(chipinfo.name));
+ chipinfo.name[sizeof(chipinfo.name)-1] = '\0';
+ strncpy(chipinfo.label, gdev->label,
+ sizeof(chipinfo.label));
+ chipinfo.label[sizeof(chipinfo.label)-1] = '\0';
+ chipinfo.lines = gdev->ngpio;
+ if (copy_to_user(ip, &chipinfo, sizeof(chipinfo)))
+ return -EFAULT;
+ return 0;
+ } else if (cmd == GPIO_GET_LINEINFO_IOCTL) {
+ struct gpioline_info lineinfo;
+ struct gpio_desc *desc;
+
+ if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
+ return -EFAULT;
+ if (lineinfo.line_offset > gdev->ngpio)
+ return -EINVAL;
+
+ desc = &gdev->descs[lineinfo.line_offset];
+ if (desc->name) {
+ strncpy(lineinfo.name, desc->name,
+ sizeof(lineinfo.name));
+ lineinfo.name[sizeof(lineinfo.name)-1] = '\0';
+ } else {
+ lineinfo.name[0] = '\0';
+ }
+ if (desc->label) {
+ strncpy(lineinfo.consumer, desc->label,
+ sizeof(lineinfo.consumer));
+ lineinfo.consumer[sizeof(lineinfo.consumer)-1] = '\0';
+ } else {
+ lineinfo.consumer[0] = '\0';
+ }
+
+ /*
+ * Userspace only need to know that the kernel is using
+ * this GPIO so it can't use it.
+ */
+ lineinfo.flags = 0;
+ if (test_bit(FLAG_REQUESTED, &desc->flags) ||
+ test_bit(FLAG_IS_HOGGED, &desc->flags) ||
+ test_bit(FLAG_USED_AS_IRQ, &desc->flags) ||
+ test_bit(FLAG_EXPORT, &desc->flags) ||
+ test_bit(FLAG_SYSFS, &desc->flags))
+ lineinfo.flags |= GPIOLINE_FLAG_KERNEL;
+ if (test_bit(FLAG_IS_OUT, &desc->flags))
+ lineinfo.flags |= GPIOLINE_FLAG_IS_OUT;
+ if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+ lineinfo.flags |= GPIOLINE_FLAG_ACTIVE_LOW;
+ if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
+ lineinfo.flags |= GPIOLINE_FLAG_OPEN_DRAIN;
+ if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
+ lineinfo.flags |= GPIOLINE_FLAG_OPEN_SOURCE;
+
+ if (copy_to_user(ip, &lineinfo, sizeof(lineinfo)))
+ return -EFAULT;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/**
+ * gpio_chrdev_open() - open the chardev for ioctl operations
+ * @inode: inode for this chardev
+ * @filp: file struct for storing private data
+ * Returns 0 on success
+ */
+static int gpio_chrdev_open(struct inode *inode, struct file *filp)
+{
+ struct gpio_device *gdev = container_of(inode->i_cdev,
+ struct gpio_device, chrdev);
+ /* Fail on open if the backing gpiochip is gone */
+ if (!gdev || !gdev->chip)
+ return -ENODEV;
+ get_device(&gdev->dev);
+ filp->private_data = gdev;
return 0;
}
/**
+ * gpio_chrdev_release() - close chardev after ioctl operations
+ * @inode: inode for this chardev
+ * @filp: file struct for storing private data
+ * Returns 0 on success
+ */
+static int gpio_chrdev_release(struct inode *inode, struct file *filp)
+{
+ struct gpio_device *gdev = container_of(inode->i_cdev,
+ struct gpio_device, chrdev);
+
+ if (!gdev)
+ return -ENODEV;
+ put_device(&gdev->dev);
+ return 0;
+}
+
+
+static const struct file_operations gpio_fileops = {
+ .release = gpio_chrdev_release,
+ .open = gpio_chrdev_open,
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+ .unlocked_ioctl = gpio_ioctl,
+ .compat_ioctl = gpio_ioctl,
+};
+
+static void gpiodevice_release(struct device *dev)
+{
+ struct gpio_device *gdev = dev_get_drvdata(dev);
+
+ cdev_del(&gdev->chrdev);
+ list_del(&gdev->list);
+ ida_simple_remove(&gpio_ida, gdev->id);
+ kfree(gdev->label);
+ kfree(gdev->descs);
+ kfree(gdev);
+}
+
+static int gpiochip_setup_dev(struct gpio_device *gdev)
+{
+ int status;
+
+ cdev_init(&gdev->chrdev, &gpio_fileops);
+ gdev->chrdev.owner = THIS_MODULE;
+ gdev->chrdev.kobj.parent = &gdev->dev.kobj;
+ gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id);
+ status = cdev_add(&gdev->chrdev, gdev->dev.devt, 1);
+ if (status < 0)
+ chip_warn(gdev->chip, "failed to add char device %d:%d\n",
+ MAJOR(gpio_devt), gdev->id);
+ else
+ chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n",
+ MAJOR(gpio_devt), gdev->id);
+ status = device_add(&gdev->dev);
+ if (status)
+ goto err_remove_chardev;
+
+ status = gpiochip_sysfs_register(gdev);
+ if (status)
+ goto err_remove_device;
+
+ /* From this point, the .release() function cleans up gpio_device */
+ gdev->dev.release = gpiodevice_release;
+ get_device(&gdev->dev);
+ pr_debug("%s: registered GPIOs %d to %d on device: %s (%s)\n",
+ __func__, gdev->base, gdev->base + gdev->ngpio - 1,
+ dev_name(&gdev->dev), gdev->chip->label ? : "generic");
+
+ return 0;
+
+err_remove_device:
+ device_del(&gdev->dev);
+err_remove_chardev:
+ cdev_del(&gdev->chrdev);
+ return status;
+}
+
+static void gpiochip_setup_devs(void)
+{
+ struct gpio_device *gdev;
+ int err;
+
+ list_for_each_entry(gdev, &gpio_devices, list) {
+ err = gpiochip_setup_dev(gdev);
+ if (err)
+ pr_err("%s: Failed to initialize gpio device (%d)\n",
+ dev_name(&gdev->dev), err);
+ }
+}
+
+/**
* gpiochip_add_data() - register a gpio_chip
* @chip: the chip to register, with chip->base initialized
* Context: potentially before irqs will work
@@ -316,6 +512,9 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
* the gpio framework's arch_initcall(). Otherwise sysfs initialization
* for GPIOs will fail rudely.
*
+ * gpiochip_add_data() must only be called after gpiolib initialization,
+ * ie after core_initcall().
+ *
* If chip->base is negative, this requests dynamic assignment of
* a range of valid GPIOs.
*/
@@ -323,43 +522,106 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
{
unsigned long flags;
int status = 0;
- unsigned id;
+ unsigned i;
int base = chip->base;
- struct gpio_desc *descs;
+ struct gpio_device *gdev;
- descs = kcalloc(chip->ngpio, sizeof(descs[0]), GFP_KERNEL);
- if (!descs)
+ /*
+ * First: allocate and populate the internal stat container, and
+ * set up the struct device.
+ */
+ gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
+ if (!gdev)
return -ENOMEM;
+ gdev->dev.bus = &gpio_bus_type;
+ gdev->chip = chip;
+ chip->gpiodev = gdev;
+ if (chip->parent) {
+ gdev->dev.parent = chip->parent;
+ gdev->dev.of_node = chip->parent->of_node;
+ } else {
+#ifdef CONFIG_OF_GPIO
+ /* If the gpiochip has an assigned OF node this takes precedence */
+ if (chip->of_node)
+ gdev->dev.of_node = chip->of_node;
+#endif
+ }
+ gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL);
+ if (gdev->id < 0) {
+ status = gdev->id;
+ goto err_free_gdev;
+ }
+ dev_set_name(&gdev->dev, "gpiochip%d", gdev->id);
+ device_initialize(&gdev->dev);
+ dev_set_drvdata(&gdev->dev, gdev);
+ if (chip->parent && chip->parent->driver)
+ gdev->owner = chip->parent->driver->owner;
+ else if (chip->owner)
+ /* TODO: remove chip->owner */
+ gdev->owner = chip->owner;
+ else
+ gdev->owner = THIS_MODULE;
- chip->data = data;
+ gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
+ if (!gdev->descs) {
+ status = -ENOMEM;
+ goto err_free_gdev;
+ }
if (chip->ngpio == 0) {
chip_err(chip, "tried to insert a GPIO chip with zero lines\n");
- return -EINVAL;
+ status = -EINVAL;
+ goto err_free_descs;
+ }
+
+ if (chip->label)
+ gdev->label = kstrdup(chip->label, GFP_KERNEL);
+ else
+ gdev->label = kstrdup("unknown", GFP_KERNEL);
+ if (!gdev->label) {
+ status = -ENOMEM;
+ goto err_free_descs;
}
+ gdev->ngpio = chip->ngpio;
+ gdev->data = data;
+
spin_lock_irqsave(&gpio_lock, flags);
+ /*
+ * TODO: this allocates a Linux GPIO number base in the global
+ * GPIO numberspace for this chip. In the long run we want to
+ * get *rid* of this numberspace and use only descriptors, but
+ * it may be a pipe dream. It will not happen before we get rid
+ * of the sysfs interface anyways.
+ */
if (base < 0) {
base = gpiochip_find_base(chip->ngpio);
if (base < 0) {
status = base;
spin_unlock_irqrestore(&gpio_lock, flags);
- goto err_free_descs;
+ goto err_free_label;
}
+ /*
+ * TODO: it should not be necessary to reflect the assigned
+ * base outside of the GPIO subsystem. Go over drivers and
+ * see if anyone makes use of this, else drop this and assign
+ * a poison instead.
+ */
chip->base = base;
}
+ gdev->base = base;
- status = gpiochip_add_to_list(chip);
+ status = gpiodev_add_to_list(gdev);
if (status) {
spin_unlock_irqrestore(&gpio_lock, flags);
- goto err_free_descs;
+ goto err_free_label;
}
- for (id = 0; id < chip->ngpio; id++) {
- struct gpio_desc *desc = &descs[id];
+ for (i = 0; i < chip->ngpio; i++) {
+ struct gpio_desc *desc = &gdev->descs[i];
- desc->chip = chip;
+ desc->gdev = gdev;
/* REVISIT: most hardware initializes GPIOs as inputs (often
* with pullups enabled) so power usage is minimized. Linux
@@ -370,17 +632,12 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0;
}
- chip->desc = descs;
-
spin_unlock_irqrestore(&gpio_lock, flags);
#ifdef CONFIG_PINCTRL
- INIT_LIST_HEAD(&chip->pin_ranges);
+ INIT_LIST_HEAD(&gdev->pin_ranges);
#endif
- if (!chip->owner && chip->parent && chip->parent->driver)
- chip->owner = chip->parent->driver->owner;
-
status = gpiochip_set_desc_names(chip);
if (status)
goto err_remove_from_list;
@@ -391,14 +648,19 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
acpi_gpiochip_add(chip);
- status = gpiochip_sysfs_register(chip);
- if (status)
- goto err_remove_chip;
-
- pr_debug("%s: registered GPIOs %d to %d on device: %s\n", __func__,
- chip->base, chip->base + chip->ngpio - 1,
- chip->label ? : "generic");
-
+ /*
+ * By first adding the chardev, and then adding the device,
+ * we get a device node entry in sysfs under
+ * /sys/bus/gpio/devices/gpiochipN/dev that can be used for
+ * coldplug of device nodes and other udev business.
+ * We can do this only if gpiolib has been initialized.
+ * Otherwise, defer until later.
+ */
+ if (gpiolib_initialized) {
+ status = gpiochip_setup_dev(gdev);
+ if (status)
+ goto err_remove_chip;
+ }
return 0;
err_remove_chip:
@@ -407,21 +669,33 @@ err_remove_chip:
of_gpiochip_remove(chip);
err_remove_from_list:
spin_lock_irqsave(&gpio_lock, flags);
- list_del(&chip->list);
+ list_del(&gdev->list);
spin_unlock_irqrestore(&gpio_lock, flags);
- chip->desc = NULL;
+err_free_label:
+ kfree(gdev->label);
err_free_descs:
- kfree(descs);
-
+ kfree(gdev->descs);
+err_free_gdev:
+ ida_simple_remove(&gpio_ida, gdev->id);
/* failures here can mean systems won't boot... */
pr_err("%s: GPIOs %d..%d (%s) failed to register\n", __func__,
- chip->base, chip->base + chip->ngpio - 1,
- chip->label ? : "generic");
+ gdev->base, gdev->base + gdev->ngpio - 1,
+ chip->label ? : "generic");
+ kfree(gdev);
return status;
}
EXPORT_SYMBOL_GPL(gpiochip_add_data);
/**
+ * gpiochip_get_data() - get per-subdriver data for the chip
+ */
+void *gpiochip_get_data(struct gpio_chip *chip)
+{
+ return chip->gpiodev->data;
+}
+EXPORT_SYMBOL_GPL(gpiochip_get_data);
+
+/**
* gpiochip_remove() - unregister a gpio_chip
* @chip: the chip to unregister
*
@@ -429,39 +703,123 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data);
*/
void gpiochip_remove(struct gpio_chip *chip)
{
+ struct gpio_device *gdev = chip->gpiodev;
struct gpio_desc *desc;
unsigned long flags;
- unsigned id;
+ unsigned i;
bool requested = false;
- gpiochip_sysfs_unregister(chip);
-
+ /* FIXME: should the legacy sysfs handling be moved to gpio_device? */
+ gpiochip_sysfs_unregister(gdev);
+ /* Numb the device, cancelling all outstanding operations */
+ gdev->chip = NULL;
gpiochip_irqchip_remove(chip);
-
acpi_gpiochip_remove(chip);
gpiochip_remove_pin_ranges(chip);
gpiochip_free_hogs(chip);
of_gpiochip_remove(chip);
+ /*
+ * We accept no more calls into the driver from this point, so
+ * NULL the driver data pointer
+ */
+ gdev->data = NULL;
spin_lock_irqsave(&gpio_lock, flags);
- for (id = 0; id < chip->ngpio; id++) {
- desc = &chip->desc[id];
- desc->chip = NULL;
+ for (i = 0; i < gdev->ngpio; i++) {
+ desc = &gdev->descs[i];
if (test_bit(FLAG_REQUESTED, &desc->flags))
requested = true;
}
- list_del(&chip->list);
spin_unlock_irqrestore(&gpio_lock, flags);
if (requested)
- dev_crit(chip->parent,
+ dev_crit(&gdev->dev,
"REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
- kfree(chip->desc);
- chip->desc = NULL;
+ /*
+ * The gpiochip side puts its use of the device to rest here:
+ * if there are no userspace clients, the chardev and device will
+ * be removed, else it will be dangling until the last user is
+ * gone.
+ */
+ put_device(&gdev->dev);
}
EXPORT_SYMBOL_GPL(gpiochip_remove);
+static void devm_gpio_chip_release(struct device *dev, void *res)
+{
+ struct gpio_chip *chip = *(struct gpio_chip **)res;
+
+ gpiochip_remove(chip);
+}
+
+static int devm_gpio_chip_match(struct device *dev, void *res, void *data)
+
+{
+ struct gpio_chip **r = res;
+
+ if (!r || !*r) {
+ WARN_ON(!r || !*r);
+ return 0;
+ }
+
+ return *r == data;
+}
+
+/**
+ * devm_gpiochip_add_data() - Resource manager piochip_add_data()
+ * @dev: the device pointer on which irq_chip belongs to.
+ * @chip: the chip to register, with chip->base initialized
+ * Context: potentially before irqs will work
+ *
+ * Returns a negative errno if the chip can't be registered, such as
+ * because the chip->base is invalid or already associated with a
+ * different chip. Otherwise it returns zero as a success code.
+ *
+ * The gpio chip automatically be released when the device is unbound.
+ */
+int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip,
+ void *data)
+{
+ struct gpio_chip **ptr;
+ int ret;
+
+ ptr = devres_alloc(devm_gpio_chip_release, sizeof(*ptr),
+ GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ ret = gpiochip_add_data(chip, data);
+ if (ret < 0) {
+ devres_free(ptr);
+ return ret;
+ }
+
+ *ptr = chip;
+ devres_add(dev, ptr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_gpiochip_add_data);
+
+/**
+ * devm_gpiochip_remove() - Resource manager of gpiochip_remove()
+ * @dev: device for which which resource was allocated
+ * @chip: the chip to remove
+ *
+ * A gpio_chip with any GPIOs still requested may not be removed.
+ */
+void devm_gpiochip_remove(struct device *dev, struct gpio_chip *chip)
+{
+ int ret;
+
+ ret = devres_release(dev, devm_gpio_chip_release,
+ devm_gpio_chip_match, chip);
+ if (!ret)
+ WARN_ON(ret);
+}
+EXPORT_SYMBOL_GPL(devm_gpiochip_remove);
+
/**
* gpiochip_find() - iterator for locating a specific gpio_chip
* @data: data to pass to match function
@@ -477,17 +835,21 @@ struct gpio_chip *gpiochip_find(void *data,
int (*match)(struct gpio_chip *chip,
void *data))
{
+ struct gpio_device *gdev;
struct gpio_chip *chip;
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
- list_for_each_entry(chip, &gpio_chips, list)
- if (match(chip, data))
+ list_for_each_entry(gdev, &gpio_devices, list)
+ if (match(gdev->chip, data))
break;
/* No match? */
- if (&chip->list == &gpio_chips)
+ if (&gdev->list == &gpio_devices)
chip = NULL;
+ else
+ chip = gdev->chip;
+
spin_unlock_irqrestore(&gpio_lock, flags);
return chip;
@@ -617,14 +979,14 @@ static int gpiochip_irq_reqres(struct irq_data *d)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
- if (!try_module_get(chip->owner))
+ if (!try_module_get(chip->gpiodev->owner))
return -ENODEV;
if (gpiochip_lock_as_irq(chip, d->hwirq)) {
chip_err(chip,
"unable to lock HW IRQ %lu for IRQ\n",
d->hwirq);
- module_put(chip->owner);
+ module_put(chip->gpiodev->owner);
return -EINVAL;
}
return 0;
@@ -635,7 +997,7 @@ static void gpiochip_irq_relres(struct irq_data *d)
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
gpiochip_unlock_as_irq(chip, d->hwirq);
- module_put(chip->owner);
+ module_put(chip->gpiodev->owner);
}
static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset)
@@ -785,7 +1147,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) {}
*/
int gpiochip_generic_request(struct gpio_chip *chip, unsigned offset)
{
- return pinctrl_request_gpio(chip->base + offset);
+ return pinctrl_request_gpio(chip->gpiodev->base + offset);
}
EXPORT_SYMBOL_GPL(gpiochip_generic_request);
@@ -796,7 +1158,7 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request);
*/
void gpiochip_generic_free(struct gpio_chip *chip, unsigned offset)
{
- pinctrl_free_gpio(chip->base + offset);
+ pinctrl_free_gpio(chip->gpiodev->base + offset);
}
EXPORT_SYMBOL_GPL(gpiochip_generic_free);
@@ -814,6 +1176,7 @@ int gpiochip_add_pingroup_range(struct gpio_chip *chip,
unsigned int gpio_offset, const char *pin_group)
{
struct gpio_pin_range *pin_range;
+ struct gpio_device *gdev = chip->gpiodev;
int ret;
pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
@@ -826,7 +1189,7 @@ int gpiochip_add_pingroup_range(struct gpio_chip *chip,
pin_range->range.id = gpio_offset;
pin_range->range.gc = chip;
pin_range->range.name = chip->label;
- pin_range->range.base = chip->base + gpio_offset;
+ pin_range->range.base = gdev->base + gpio_offset;
pin_range->pctldev = pctldev;
ret = pinctrl_get_group_pins(pctldev, pin_group,
@@ -843,7 +1206,7 @@ int gpiochip_add_pingroup_range(struct gpio_chip *chip,
gpio_offset, gpio_offset + pin_range->range.npins - 1,
pinctrl_dev_get_devname(pctldev), pin_group);
- list_add_tail(&pin_range->node, &chip->pin_ranges);
+ list_add_tail(&pin_range->node, &gdev->pin_ranges);
return 0;
}
@@ -863,6 +1226,7 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
unsigned int npins)
{
struct gpio_pin_range *pin_range;
+ struct gpio_device *gdev = chip->gpiodev;
int ret;
pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
@@ -875,7 +1239,7 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
pin_range->range.id = gpio_offset;
pin_range->range.gc = chip;
pin_range->range.name = chip->label;
- pin_range->range.base = chip->base + gpio_offset;
+ pin_range->range.base = gdev->base + gpio_offset;
pin_range->range.pin_base = pin_offset;
pin_range->range.npins = npins;
pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name,
@@ -891,7 +1255,7 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
pinctl_name,
pin_offset, pin_offset + npins - 1);
- list_add_tail(&pin_range->node, &chip->pin_ranges);
+ list_add_tail(&pin_range->node, &gdev->pin_ranges);
return 0;
}
@@ -904,8 +1268,9 @@ EXPORT_SYMBOL_GPL(gpiochip_add_pin_range);
void gpiochip_remove_pin_ranges(struct gpio_chip *chip)
{
struct gpio_pin_range *pin_range, *tmp;
+ struct gpio_device *gdev = chip->gpiodev;
- list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) {
+ list_for_each_entry_safe(pin_range, tmp, &gdev->pin_ranges, node) {
list_del(&pin_range->node);
pinctrl_remove_gpio_range(pin_range->pctldev,
&pin_range->range);
@@ -922,7 +1287,7 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges);
*/
static int __gpiod_request(struct gpio_desc *desc, const char *label)
{
- struct gpio_chip *chip = desc->chip;
+ struct gpio_chip *chip = desc->gdev->chip;
int status;
unsigned long flags;
@@ -971,27 +1336,50 @@ done:
return status;
}
+/*
+ * This descriptor validation needs to be inserted verbatim into each
+ * function taking a descriptor, so we need to use a preprocessor
+ * macro to avoid endless duplication.
+ */
+#define VALIDATE_DESC(desc) do { \
+ if (!desc || !desc->gdev) { \
+ pr_warn("%s: invalid GPIO\n", __func__); \
+ return -EINVAL; \
+ } \
+ if ( !desc->gdev->chip ) { \
+ dev_warn(&desc->gdev->dev, \
+ "%s: backing chip is gone\n", __func__); \
+ return 0; \
+ } } while (0)
+
+#define VALIDATE_DESC_VOID(desc) do { \
+ if (!desc || !desc->gdev) { \
+ pr_warn("%s: invalid GPIO\n", __func__); \
+ return; \
+ } \
+ if (!desc->gdev->chip) { \
+ dev_warn(&desc->gdev->dev, \
+ "%s: backing chip is gone\n", __func__); \
+ return; \
+ } } while (0)
+
+
int gpiod_request(struct gpio_desc *desc, const char *label)
{
int status = -EPROBE_DEFER;
- struct gpio_chip *chip;
-
- if (!desc) {
- pr_warn("%s: invalid GPIO\n", __func__);
- return -EINVAL;
- }
+ struct gpio_device *gdev;
- chip = desc->chip;
- if (!chip)
- goto done;
+ VALIDATE_DESC(desc);
+ gdev = desc->gdev;
- if (try_module_get(chip->owner)) {
+ if (try_module_get(gdev->owner)) {
status = __gpiod_request(desc, label);
if (status < 0)
- module_put(chip->owner);
+ module_put(gdev->owner);
+ else
+ get_device(&gdev->dev);
}
-done:
if (status)
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
@@ -1010,7 +1398,7 @@ static bool __gpiod_free(struct gpio_desc *desc)
spin_lock_irqsave(&gpio_lock, flags);
- chip = desc->chip;
+ chip = desc->gdev->chip;
if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) {
if (chip->free) {
spin_unlock_irqrestore(&gpio_lock, flags);
@@ -1033,10 +1421,12 @@ static bool __gpiod_free(struct gpio_desc *desc)
void gpiod_free(struct gpio_desc *desc)
{
- if (desc && __gpiod_free(desc))
- module_put(desc->chip->owner);
- else
+ if (desc && desc->gdev && __gpiod_free(desc)) {
+ module_put(desc->gdev->owner);
+ put_device(&desc->gdev->dev);
+ } else {
WARN_ON(extra_checks);
+ }
}
/**
@@ -1059,7 +1449,7 @@ const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
if (offset >= chip->ngpio)
return NULL;
- desc = &chip->desc[offset];
+ desc = &chip->gpiodev->descs[offset];
if (test_bit(FLAG_REQUESTED, &desc->flags) == 0)
return NULL;
@@ -1111,7 +1501,8 @@ void gpiochip_free_own_desc(struct gpio_desc *desc)
}
EXPORT_SYMBOL_GPL(gpiochip_free_own_desc);
-/* Drivers MUST set GPIO direction before making get/set calls. In
+/*
+ * Drivers MUST set GPIO direction before making get/set calls. In
* some cases this is done in early boot, before IRQs are enabled.
*
* As a rule these aren't called more than once (except for drivers
@@ -1134,12 +1525,9 @@ int gpiod_direction_input(struct gpio_desc *desc)
struct gpio_chip *chip;
int status = -EINVAL;
- if (!desc || !desc->chip) {
- pr_warn("%s: invalid GPIO\n", __func__);
- return -EINVAL;
- }
+ VALIDATE_DESC(desc);
+ chip = desc->gdev->chip;
- chip = desc->chip;
if (!chip->get || !chip->direction_input) {
gpiod_warn(desc,
"%s: missing get() or direction_input() operations\n",
@@ -1178,7 +1566,7 @@ static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value)
if (!value && test_bit(FLAG_OPEN_SOURCE, &desc->flags))
return gpiod_direction_input(desc);
- chip = desc->chip;
+ chip = desc->gdev->chip;
if (!chip->set || !chip->direction_output) {
gpiod_warn(desc,
"%s: missing set() or direction_output() operations\n",
@@ -1207,10 +1595,7 @@ static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value)
*/
int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
{
- if (!desc || !desc->chip) {
- pr_warn("%s: invalid GPIO\n", __func__);
- return -EINVAL;
- }
+ VALIDATE_DESC(desc);
return _gpiod_direction_output_raw(desc, value);
}
EXPORT_SYMBOL_GPL(gpiod_direction_output_raw);
@@ -1229,10 +1614,7 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output_raw);
*/
int gpiod_direction_output(struct gpio_desc *desc, int value)
{
- if (!desc || !desc->chip) {
- pr_warn("%s: invalid GPIO\n", __func__);
- return -EINVAL;
- }
+ VALIDATE_DESC(desc);
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
return _gpiod_direction_output_raw(desc, value);
@@ -1251,12 +1633,8 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
{
struct gpio_chip *chip;
- if (!desc || !desc->chip) {
- pr_warn("%s: invalid GPIO\n", __func__);
- return -EINVAL;
- }
-
- chip = desc->chip;
+ VALIDATE_DESC(desc);
+ chip = desc->gdev->chip;
if (!chip->set || !chip->set_debounce) {
gpiod_dbg(desc,
"%s: missing set() or set_debounce() operations\n",
@@ -1276,6 +1654,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_debounce);
*/
int gpiod_is_active_low(const struct gpio_desc *desc)
{
+ VALIDATE_DESC(desc);
return test_bit(FLAG_ACTIVE_LOW, &desc->flags);
}
EXPORT_SYMBOL_GPL(gpiod_is_active_low);
@@ -1308,7 +1687,7 @@ static int _gpiod_get_raw_value(const struct gpio_desc *desc)
int offset;
int value;
- chip = desc->chip;
+ chip = desc->gdev->chip;
offset = gpio_chip_hwgpio(desc);
value = chip->get ? chip->get(chip, offset) : -EIO;
value = value < 0 ? value : !!value;
@@ -1328,10 +1707,9 @@ static int _gpiod_get_raw_value(const struct gpio_desc *desc)
*/
int gpiod_get_raw_value(const struct gpio_desc *desc)
{
- if (!desc)
- return 0;
+ VALIDATE_DESC(desc);
/* Should be using gpio_get_value_cansleep() */
- WARN_ON(desc->chip->can_sleep);
+ WARN_ON(desc->gdev->chip->can_sleep);
return _gpiod_get_raw_value(desc);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_value);
@@ -1349,10 +1727,10 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_value);
int gpiod_get_value(const struct gpio_desc *desc)
{
int value;
- if (!desc)
- return 0;
+
+ VALIDATE_DESC(desc);
/* Should be using gpio_get_value_cansleep() */
- WARN_ON(desc->chip->can_sleep);
+ WARN_ON(desc->gdev->chip->can_sleep);
value = _gpiod_get_raw_value(desc);
if (value < 0)
@@ -1373,7 +1751,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
static void _gpio_set_open_drain_value(struct gpio_desc *desc, bool value)
{
int err = 0;
- struct gpio_chip *chip = desc->chip;
+ struct gpio_chip *chip = desc->gdev->chip;
int offset = gpio_chip_hwgpio(desc);
if (value) {
@@ -1400,7 +1778,7 @@ static void _gpio_set_open_drain_value(struct gpio_desc *desc, bool value)
static void _gpio_set_open_source_value(struct gpio_desc *desc, bool value)
{
int err = 0;
- struct gpio_chip *chip = desc->chip;
+ struct gpio_chip *chip = desc->gdev->chip;
int offset = gpio_chip_hwgpio(desc);
if (value) {
@@ -1423,7 +1801,7 @@ static void _gpiod_set_raw_value(struct gpio_desc *desc, bool value)
{
struct gpio_chip *chip;
- chip = desc->chip;
+ chip = desc->gdev->chip;
trace_gpio_value(desc_to_gpio(desc), 0, value);
if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
_gpio_set_open_drain_value(desc, value);
@@ -1471,7 +1849,7 @@ static void gpiod_set_array_value_priv(bool raw, bool can_sleep,
int i = 0;
while (i < array_size) {
- struct gpio_chip *chip = desc_array[i]->chip;
+ struct gpio_chip *chip = desc_array[i]->gdev->chip;
unsigned long mask[BITS_TO_LONGS(chip->ngpio)];
unsigned long bits[BITS_TO_LONGS(chip->ngpio)];
int count = 0;
@@ -1505,7 +1883,8 @@ static void gpiod_set_array_value_priv(bool raw, bool can_sleep,
count++;
}
i++;
- } while ((i < array_size) && (desc_array[i]->chip == chip));
+ } while ((i < array_size) &&
+ (desc_array[i]->gdev->chip == chip));
/* push collected bits to outputs */
if (count != 0)
gpio_chip_set_multiple(chip, mask, bits);
@@ -1525,10 +1904,9 @@ static void gpiod_set_array_value_priv(bool raw, bool can_sleep,
*/
void gpiod_set_raw_value(struct gpio_desc *desc, int value)
{
- if (!desc)
- return;
- /* Should be using gpio_set_value_cansleep() */
- WARN_ON(desc->chip->can_sleep);
+ VALIDATE_DESC_VOID(desc);
+ /* Should be using gpiod_set_value_cansleep() */
+ WARN_ON(desc->gdev->chip->can_sleep);
_gpiod_set_raw_value(desc, value);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_value);
@@ -1546,10 +1924,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value);
*/
void gpiod_set_value(struct gpio_desc *desc, int value)
{
- if (!desc)
- return;
- /* Should be using gpio_set_value_cansleep() */
- WARN_ON(desc->chip->can_sleep);
+ VALIDATE_DESC_VOID(desc);
+ /* Should be using gpiod_set_value_cansleep() */
+ WARN_ON(desc->gdev->chip->can_sleep);
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
_gpiod_set_raw_value(desc, value);
@@ -1607,9 +1984,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value);
*/
int gpiod_cansleep(const struct gpio_desc *desc)
{
- if (!desc)
- return 0;
- return desc->chip->can_sleep;
+ VALIDATE_DESC(desc);
+ return desc->gdev->chip->can_sleep;
}
EXPORT_SYMBOL_GPL(gpiod_cansleep);
@@ -1625,9 +2001,8 @@ int gpiod_to_irq(const struct gpio_desc *desc)
struct gpio_chip *chip;
int offset;
- if (!desc)
- return -EINVAL;
- chip = desc->chip;
+ VALIDATE_DESC(desc);
+ chip = desc->gdev->chip;
offset = gpio_chip_hwgpio(desc);
return chip->to_irq ? chip->to_irq(chip, offset) : -ENXIO;
}
@@ -1646,14 +2021,14 @@ int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset)
if (offset >= chip->ngpio)
return -EINVAL;
- if (test_bit(FLAG_IS_OUT, &chip->desc[offset].flags)) {
+ if (test_bit(FLAG_IS_OUT, &chip->gpiodev->descs[offset].flags)) {
chip_err(chip,
"%s: tried to flag a GPIO set as output for IRQ\n",
__func__);
return -EIO;
}
- set_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags);
+ set_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags);
return 0;
}
EXPORT_SYMBOL_GPL(gpiochip_lock_as_irq);
@@ -1671,10 +2046,37 @@ void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset)
if (offset >= chip->ngpio)
return;
- clear_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags);
+ clear_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags);
}
EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq);
+bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset)
+{
+ if (offset >= chip->ngpio)
+ return false;
+
+ return test_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags);
+}
+EXPORT_SYMBOL_GPL(gpiochip_line_is_irq);
+
+bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset)
+{
+ if (offset >= chip->ngpio)
+ return false;
+
+ return test_bit(FLAG_OPEN_DRAIN, &chip->gpiodev->descs[offset].flags);
+}
+EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain);
+
+bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset)
+{
+ if (offset >= chip->ngpio)
+ return false;
+
+ return test_bit(FLAG_OPEN_SOURCE, &chip->gpiodev->descs[offset].flags);
+}
+EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source);
+
/**
* gpiod_get_raw_value_cansleep() - return a gpio's raw value
* @desc: gpio whose value will be returned
@@ -1687,8 +2089,7 @@ EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq);
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
{
might_sleep_if(extra_checks);
- if (!desc)
- return 0;
+ VALIDATE_DESC(desc);
return _gpiod_get_raw_value(desc);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_value_cansleep);
@@ -1707,9 +2108,7 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc)
int value;
might_sleep_if(extra_checks);
- if (!desc)
- return 0;
-
+ VALIDATE_DESC(desc);
value = _gpiod_get_raw_value(desc);
if (value < 0)
return value;
@@ -1734,8 +2133,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value)
{
might_sleep_if(extra_checks);
- if (!desc)
- return;
+ VALIDATE_DESC_VOID(desc);
_gpiod_set_raw_value(desc, value);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep);
@@ -1753,9 +2151,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep);
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
{
might_sleep_if(extra_checks);
- if (!desc)
- return;
-
+ VALIDATE_DESC_VOID(desc);
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
_gpiod_set_raw_value(desc, value);
@@ -1873,9 +2269,11 @@ static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
return desc;
}
-static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
+static struct gpio_desc *acpi_find_gpio(struct device *dev,
+ const char *con_id,
unsigned int idx,
- enum gpio_lookup_flags *flags)
+ enum gpiod_flags flags,
+ enum gpio_lookup_flags *lookupflags)
{
struct acpi_device *adev = ACPI_COMPANION(dev);
struct acpi_gpio_info info;
@@ -1906,10 +2304,16 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info);
if (IS_ERR(desc))
return desc;
+
+ if ((flags == GPIOD_OUT_LOW || flags == GPIOD_OUT_HIGH) &&
+ info.gpioint) {
+ dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n");
+ return ERR_PTR(-ENOENT);
+ }
}
if (info.polarity == GPIO_ACTIVE_LOW)
- *flags |= GPIO_ACTIVE_LOW;
+ *lookupflags |= GPIO_ACTIVE_LOW;
return desc;
}
@@ -2172,7 +2576,7 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
desc = of_find_gpio(dev, con_id, idx, &lookupflags);
} else if (ACPI_COMPANION(dev)) {
dev_dbg(dev, "using ACPI for GPIO lookup\n");
- desc = acpi_find_gpio(dev, con_id, idx, &lookupflags);
+ desc = acpi_find_gpio(dev, con_id, idx, flags, &lookupflags);
}
}
@@ -2358,8 +2762,8 @@ static void gpiochip_free_hogs(struct gpio_chip *chip)
int id;
for (id = 0; id < chip->ngpio; id++) {
- if (test_bit(FLAG_IS_HOGGED, &chip->desc[id].flags))
- gpiochip_free_own_desc(&chip->desc[id]);
+ if (test_bit(FLAG_IS_HOGGED, &chip->gpiodev->descs[id].flags))
+ gpiochip_free_own_desc(&chip->gpiodev->descs[id]);
}
}
@@ -2456,17 +2860,41 @@ void gpiod_put_array(struct gpio_descs *descs)
}
EXPORT_SYMBOL_GPL(gpiod_put_array);
+static int __init gpiolib_dev_init(void)
+{
+ int ret;
+
+ /* Register GPIO sysfs bus */
+ ret = bus_register(&gpio_bus_type);
+ if (ret < 0) {
+ pr_err("gpiolib: could not register GPIO bus type\n");
+ return ret;
+ }
+
+ ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, "gpiochip");
+ if (ret < 0) {
+ pr_err("gpiolib: failed to allocate char dev region\n");
+ bus_unregister(&gpio_bus_type);
+ } else {
+ gpiolib_initialized = true;
+ gpiochip_setup_devs();
+ }
+ return ret;
+}
+core_initcall(gpiolib_dev_init);
+
#ifdef CONFIG_DEBUG_FS
-static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
{
unsigned i;
- unsigned gpio = chip->base;
- struct gpio_desc *gdesc = &chip->desc[0];
+ struct gpio_chip *chip = gdev->chip;
+ unsigned gpio = gdev->base;
+ struct gpio_desc *gdesc = &gdev->descs[0];
int is_out;
int is_irq;
- for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) {
+ for (i = 0; i < gdev->ngpio; i++, gpio++, gdesc++) {
if (!test_bit(FLAG_REQUESTED, &gdesc->flags)) {
if (gdesc->name) {
seq_printf(s, " gpio-%-3d (%-20.20s)\n",
@@ -2492,16 +2920,16 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos)
{
unsigned long flags;
- struct gpio_chip *chip = NULL;
+ struct gpio_device *gdev = NULL;
loff_t index = *pos;
s->private = "";
spin_lock_irqsave(&gpio_lock, flags);
- list_for_each_entry(chip, &gpio_chips, list)
+ list_for_each_entry(gdev, &gpio_devices, list)
if (index-- == 0) {
spin_unlock_irqrestore(&gpio_lock, flags);
- return chip;
+ return gdev;
}
spin_unlock_irqrestore(&gpio_lock, flags);
@@ -2511,14 +2939,14 @@ static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos)
static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
unsigned long flags;
- struct gpio_chip *chip = v;
+ struct gpio_device *gdev = v;
void *ret = NULL;
spin_lock_irqsave(&gpio_lock, flags);
- if (list_is_last(&chip->list, &gpio_chips))
+ if (list_is_last(&gdev->list, &gpio_devices))
ret = NULL;
else
- ret = list_entry(chip->list.next, struct gpio_chip, list);
+ ret = list_entry(gdev->list.next, struct gpio_device, list);
spin_unlock_irqrestore(&gpio_lock, flags);
s->private = "\n";
@@ -2533,15 +2961,24 @@ static void gpiolib_seq_stop(struct seq_file *s, void *v)
static int gpiolib_seq_show(struct seq_file *s, void *v)
{
- struct gpio_chip *chip = v;
- struct device *dev;
+ struct gpio_device *gdev = v;
+ struct gpio_chip *chip = gdev->chip;
+ struct device *parent;
+
+ if (!chip) {
+ seq_printf(s, "%s%s: (dangling chip)", (char *)s->private,
+ dev_name(&gdev->dev));
+ return 0;
+ }
- seq_printf(s, "%sGPIOs %d-%d", (char *)s->private,
- chip->base, chip->base + chip->ngpio - 1);
- dev = chip->parent;
- if (dev)
- seq_printf(s, ", %s/%s", dev->bus ? dev->bus->name : "no-bus",
- dev_name(dev));
+ seq_printf(s, "%s%s: GPIOs %d-%d", (char *)s->private,
+ dev_name(&gdev->dev),
+ gdev->base, gdev->base + gdev->ngpio - 1);
+ parent = chip->parent;
+ if (parent)
+ seq_printf(s, ", parent: %s/%s",
+ parent->bus ? parent->bus->name : "no-bus",
+ dev_name(parent));
if (chip->label)
seq_printf(s, ", %s", chip->label);
if (chip->can_sleep)
@@ -2551,7 +2988,7 @@ static int gpiolib_seq_show(struct seq_file *s, void *v)
if (chip->dbg_show)
chip->dbg_show(s, chip);
else
- gpiolib_dbg_show(s, chip);
+ gpiolib_dbg_show(s, gdev);
return 0;
}
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 99ed3b00f..e30e5fdb1 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -12,14 +12,67 @@
#ifndef GPIOLIB_H
#define GPIOLIB_H
+#include <linux/gpio/driver.h>
#include <linux/err.h>
#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/cdev.h>
enum of_gpio_flags;
enum gpiod_flags;
struct acpi_device;
/**
+ * struct gpio_device - internal state container for GPIO devices
+ * @id: numerical ID number for the GPIO chip
+ * @dev: the GPIO device struct
+ * @chrdev: character device for the GPIO device
+ * @mockdev: class device used by the deprecated sysfs interface (may be
+ * NULL)
+ * @owner: helps prevent removal of modules exporting active GPIOs
+ * @chip: pointer to the corresponding gpiochip, holding static
+ * data for this device
+ * @descs: array of ngpio descriptors.
+ * @ngpio: the number of GPIO lines on this GPIO device, equal to the size
+ * of the @descs array.
+ * @base: GPIO base in the DEPRECATED global Linux GPIO numberspace, assigned
+ * at device creation time.
+ * @label: a descriptive name for the GPIO device, such as the part number
+ * or name of the IP component in a System on Chip.
+ * @data: per-instance data assigned by the driver
+ * @list: links gpio_device:s together for traversal
+ *
+ * This state container holds most of the runtime variable data
+ * for a GPIO device and can hold references and live on after the
+ * GPIO chip has been removed, if it is still being used from
+ * userspace.
+ */
+struct gpio_device {
+ int id;
+ struct device dev;
+ struct cdev chrdev;
+ struct device *mockdev;
+ struct module *owner;
+ struct gpio_chip *chip;
+ struct gpio_desc *descs;
+ int base;
+ u16 ngpio;
+ char *label;
+ void *data;
+ struct list_head list;
+
+#ifdef CONFIG_PINCTRL
+ /*
+ * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
+ * describe the actual pin range which they serve in an SoC. This
+ * information would be used by pinctrl subsystem to configure
+ * corresponding pins for gpio usage.
+ */
+ struct list_head pin_ranges;
+#endif
+};
+
+/**
* struct acpi_gpio_info - ACPI GPIO specific information
* @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo
* @active_low: in case of @gpioint, the pin is active low
@@ -90,10 +143,10 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
extern struct spinlock gpio_lock;
-extern struct list_head gpio_chips;
+extern struct list_head gpio_devices;
struct gpio_desc {
- struct gpio_chip *chip;
+ struct gpio_device *gdev;
unsigned long flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
@@ -122,7 +175,7 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
*/
static int __maybe_unused gpio_chip_hwgpio(const struct gpio_desc *desc)
{
- return desc - &desc->chip->desc[0];
+ return desc - &desc->gdev->descs[0];
}
/* With descriptor prefix */
@@ -149,31 +202,31 @@ static int __maybe_unused gpio_chip_hwgpio(const struct gpio_desc *desc)
/* With chip prefix */
#define chip_emerg(chip, fmt, ...) \
- pr_emerg("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+ dev_emerg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#define chip_crit(chip, fmt, ...) \
- pr_crit("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+ dev_crit(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#define chip_err(chip, fmt, ...) \
- pr_err("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+ dev_err(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#define chip_warn(chip, fmt, ...) \
- pr_warn("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+ dev_warn(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#define chip_info(chip, fmt, ...) \
- pr_info("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+ dev_info(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#define chip_dbg(chip, fmt, ...) \
- pr_debug("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+ dev_dbg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#ifdef CONFIG_GPIO_SYSFS
-int gpiochip_sysfs_register(struct gpio_chip *chip);
-void gpiochip_sysfs_unregister(struct gpio_chip *chip);
+int gpiochip_sysfs_register(struct gpio_device *gdev);
+void gpiochip_sysfs_unregister(struct gpio_device *gdev);
#else
-static inline int gpiochip_sysfs_register(struct gpio_chip *chip)
+static inline int gpiochip_sysfs_register(struct gpio_device *gdev)
{
return 0;
}
-static inline void gpiochip_sysfs_unregister(struct gpio_chip *chip)
+static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev)
{
}