aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.txt4
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-clps711x.txt4
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-max77620.txt25
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-pca953x.txt1
-rw-r--r--Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt1
-rw-r--r--Documentation/gpio/drivers-on-gpio.txt13
-rw-r--r--MAINTAINERS6
-rw-r--r--arch/hexagon/Kconfig3
-rw-r--r--arch/ia64/Kconfig1
-rw-r--r--arch/microblaze/Kconfig1
-rw-r--r--arch/openrisc/Kconfig2
-rw-r--r--arch/powerpc/Kconfig1
-rw-r--r--arch/powerpc/platforms/40x/Kconfig2
-rw-r--r--arch/powerpc/platforms/44x/Kconfig2
-rw-r--r--arch/powerpc/platforms/512x/Kconfig1
-rw-r--r--arch/powerpc/platforms/83xx/Kconfig3
-rw-r--r--arch/powerpc/platforms/85xx/Kconfig4
-rw-r--r--arch/powerpc/platforms/86xx/Kconfig7
-rw-r--r--arch/powerpc/platforms/8xx/Kconfig2
-rw-r--r--arch/powerpc/platforms/Kconfig8
-rw-r--r--arch/sh/Kconfig11
-rw-r--r--arch/sh/boards/Kconfig17
-rw-r--r--arch/sh/boards/mach-highlander/Kconfig2
-rw-r--r--arch/sh/boards/mach-rsk/Kconfig6
-rw-r--r--arch/unicore32/Kconfig2
-rw-r--r--drivers/Makefile2
-rw-r--r--drivers/gpio/Kconfig24
-rw-r--r--drivers/gpio/Makefile2
-rw-r--r--drivers/gpio/gpio-74x164.c9
-rw-r--r--drivers/gpio/gpio-clps711x.c10
-rw-r--r--drivers/gpio/gpio-dwapb.c1
-rw-r--r--drivers/gpio/gpio-f7188x.c22
-rw-r--r--drivers/gpio/gpio-intel-mid.c37
-rw-r--r--drivers/gpio/gpio-lynxpoint.c1
-rw-r--r--drivers/gpio/gpio-max77620.c315
-rw-r--r--drivers/gpio/gpio-menz127.c1
-rw-r--r--drivers/gpio/gpio-merrifield.c444
-rw-r--r--drivers/gpio/gpio-mmio.c51
-rw-r--r--drivers/gpio/gpio-palmas.c1
-rw-r--r--drivers/gpio/gpio-pca953x.c23
-rw-r--r--drivers/gpio/gpio-pcf857x.c9
-rw-r--r--drivers/gpio/gpio-rcar.c3
-rw-r--r--drivers/gpio/gpio-rdc321x.c1
-rw-r--r--drivers/gpio/gpio-sch311x.c1
-rw-r--r--drivers/gpio/gpio-stmpe.c18
-rw-r--r--drivers/gpio/gpio-syscon.c4
-rw-r--r--drivers/gpio/gpio-tc3589x.c1
-rw-r--r--drivers/gpio/gpio-tps65218.c7
-rw-r--r--drivers/gpio/gpio-tps6586x.c1
-rw-r--r--drivers/gpio/gpio-tps65910.c1
-rw-r--r--drivers/gpio/gpio-viperboard.c1
-rw-r--r--drivers/gpio/gpio-wm831x.c1
-rw-r--r--drivers/gpio/gpio-wm8350.c1
-rw-r--r--drivers/gpio/gpio-wm8994.c1
-rw-r--r--drivers/gpio/gpio-xilinx.c48
-rw-r--r--drivers/gpio/gpio-xlp.c52
-rw-r--r--drivers/gpio/gpiolib-acpi.c1
-rw-r--r--drivers/gpio/gpiolib-of.c127
-rw-r--r--drivers/gpio/gpiolib.c511
-rw-r--r--drivers/pinctrl/pinctrl-xway.c5
-rw-r--r--include/uapi/linux/gpio.h105
-rw-r--r--tools/Makefile7
-rw-r--r--tools/gpio/Build3
-rw-r--r--tools/gpio/Makefile75
-rw-r--r--tools/gpio/gpio-event-mon.c192
-rw-r--r--tools/gpio/gpio-hammer.c189
66 files changed, 2207 insertions, 230 deletions
diff --git a/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.txt b/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.txt
index 94ae9f82dcf8..fd42e7280f72 100644
--- a/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.txt
+++ b/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.txt
@@ -1,7 +1,7 @@
* ARM Cirrus Logic CLPS711X SYSFLG1 MCTRL GPIOs
Required properties:
-- compatible: Should contain "cirrus,clps711x-mctrl-gpio".
+- compatible: Should contain "cirrus,ep7209-mctrl-gpio".
- gpio-controller: Marks the device node as a gpio controller.
- #gpio-cells: Should be two. The first cell is the pin number and
the second cell is used to specify the gpio polarity:
@@ -11,7 +11,7 @@ Required properties:
Example:
sysgpio: sysgpio {
compatible = "cirrus,ep7312-mctrl-gpio",
- "cirrus,clps711x-mctrl-gpio";
+ "cirrus,ep7209-mctrl-gpio";
gpio-controller;
#gpio-cells = <2>;
};
diff --git a/Documentation/devicetree/bindings/gpio/gpio-clps711x.txt b/Documentation/devicetree/bindings/gpio/gpio-clps711x.txt
index e0d0446a6b78..0a304ad29d81 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-clps711x.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio-clps711x.txt
@@ -1,7 +1,7 @@
Cirrus Logic CLPS711X GPIO controller
Required properties:
-- compatible: Should be "cirrus,clps711x-gpio"
+- compatible: Should be "cirrus,ep7209-gpio"
- reg: Physical base GPIO controller registers location and length.
There should be two registers, first is DATA register, the second
is DIRECTION.
@@ -21,7 +21,7 @@ aliases {
};
porta: gpio@80000000 {
- compatible = "cirrus,clps711x-gpio";
+ compatible = "cirrus,ep7312-gpio","cirrus,ep7209-gpio";
reg = <0x80000000 0x1>, <0x80000040 0x1>;
gpio-controller;
#gpio-cells = <2>;
diff --git a/Documentation/devicetree/bindings/gpio/gpio-max77620.txt b/Documentation/devicetree/bindings/gpio/gpio-max77620.txt
new file mode 100644
index 000000000000..410e716fd3d2
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-max77620.txt
@@ -0,0 +1,25 @@
+GPIO driver for MAX77620 Power management IC from Maxim Semiconductor.
+
+Device has 8 GPIO pins which can be configured as GPIO as well as the
+special IO functions.
+
+Required properties:
+-------------------
+- gpio-controller : Marks the device node as a gpio controller.
+- #gpio-cells : Should be two. The first cell is the pin number and
+ the second cell is used to specify the gpio polarity:
+ 0 = active high
+ 1 = active low
+For more details, please refer generic GPIO DT binding document
+<devicetree/bindings/gpio/gpio.txt>.
+
+Example:
+--------
+#include <dt-bindings/mfd/max77620.h>
+...
+max77620@3c {
+ compatible = "maxim,max77620";
+
+ gpio-controller;
+ #gpio-cells = <2>;
+};
diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt
index 6b4a98f74be3..08dd15f89ba9 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt
@@ -21,6 +21,7 @@ Required properties:
maxim,max7313
maxim,max7315
ti,pca6107
+ ti,pca9536
ti,tca6408
ti,tca6416
ti,tca6424
diff --git a/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt b/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt
index f60e2f477e93..8da26b35b5c3 100644
--- a/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt
+++ b/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt
@@ -7,6 +7,7 @@ Required Properties:
- "renesas,gpio-r8a7779": for R8A7779 (R-Car H1) compatible GPIO controller.
- "renesas,gpio-r8a7790": for R8A7790 (R-Car H2) compatible GPIO controller.
- "renesas,gpio-r8a7791": for R8A7791 (R-Car M2-W) compatible GPIO controller.
+ - "renesas,gpio-r8a7792": for R8A7792 (R-Car V2H) compatible GPIO controller.
- "renesas,gpio-r8a7793": for R8A7793 (R-Car M2-N) compatible GPIO controller.
- "renesas,gpio-r8a7794": for R8A7794 (R-Car E2) compatible GPIO controller.
- "renesas,gpio-r8a7795": for R8A7795 (R-Car H3) compatible GPIO controller.
diff --git a/Documentation/gpio/drivers-on-gpio.txt b/Documentation/gpio/drivers-on-gpio.txt
index 14bf95a13bae..306513251713 100644
--- a/Documentation/gpio/drivers-on-gpio.txt
+++ b/Documentation/gpio/drivers-on-gpio.txt
@@ -37,15 +37,16 @@ hardware descriptions such as device tree or ACPI:
external connector status, such as a headset line for an audio driver or an
HDMI connector. It will provide a better userspace sysfs interface than GPIO.
-- restart-gpio: drivers/power/gpio-restart.c is used to restart/reboot the
- system by pulling a GPIO line and will register a restart handler so
+- restart-gpio: drivers/power/reset/gpio-restart.c is used to restart/reboot
+ the system by pulling a GPIO line and will register a restart handler so
userspace can issue the right system call to restart the system.
-- poweroff-gpio: drivers/power/gpio-poweroff.c is used to power the system down
- by pulling a GPIO line and will register a pm_power_off() callback so that
- userspace can issue the right system call to power down the system.
+- poweroff-gpio: drivers/power/reset/gpio-poweroff.c is used to power the
+ system down by pulling a GPIO line and will register a pm_power_off()
+ callback so that userspace can issue the right system call to power down the
+ system.
-- gpio-gate-clock: drivers/clk/clk-gpio-gate.c is used to control a gated clock
+- gpio-gate-clock: drivers/clk/clk-gpio.c is used to control a gated clock
(off/on) that uses a GPIO, and integrated with the clock subsystem.
- i2c-gpio: drivers/i2c/busses/i2c-gpio.c is used to drive an I2C bus
diff --git a/MAINTAINERS b/MAINTAINERS
index 0a3827b722b0..f4698b1cf847 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6060,6 +6060,12 @@ L: linux-rdma@vger.kernel.org
S: Supported
F: drivers/infiniband/hw/i40iw/
+INTEL MERRIFIELD GPIO DRIVER
+M: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+L: linux-gpio@vger.kernel.org
+S: Maintained
+F: drivers/gpio/gpio-merrifield.c
+
INTEL-MID GPIO DRIVER
M: David Cohen <david.a.cohen@linux.intel.com>
L: linux-gpio@vger.kernel.org
diff --git a/arch/hexagon/Kconfig b/arch/hexagon/Kconfig
index 57298e7b4867..1941e4baaee6 100644
--- a/arch/hexagon/Kconfig
+++ b/arch/hexagon/Kconfig
@@ -8,8 +8,7 @@ config HEXAGON
# select HAVE_REGS_AND_STACK_ACCESS_API
# select HAVE_HW_BREAKPOINT if PERF_EVENTS
# select ARCH_HAS_CPU_IDLE_WAIT
- # select ARCH_WANT_OPTIONAL_GPIOLIB
- # select ARCH_REQUIRE_GPIOLIB
+ # select GPIOLIB
# select HAVE_CLK
# select GENERIC_PENDING_IRQ if SMP
select GENERIC_ATOMIC64
diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig
index e109ee95e919..6a15083cc366 100644
--- a/arch/ia64/Kconfig
+++ b/arch/ia64/Kconfig
@@ -39,7 +39,6 @@ config IA64
select GENERIC_PENDING_IRQ if SMP
select GENERIC_IRQ_SHOW
select GENERIC_IRQ_LEGACY
- select ARCH_WANT_OPTIONAL_GPIOLIB
select ARCH_HAVE_NMI_SAFE_CMPXCHG
select GENERIC_IOMAP
select GENERIC_SMP_IDLE_THREAD
diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig
index 636e0720fb20..86f65721e629 100644
--- a/arch/microblaze/Kconfig
+++ b/arch/microblaze/Kconfig
@@ -3,7 +3,6 @@ config MICROBLAZE
select ARCH_HAS_GCOV_PROFILE_ALL
select ARCH_MIGHT_HAVE_PC_PARPORT
select ARCH_WANT_IPC_PARSE_VERSION
- select ARCH_WANT_OPTIONAL_GPIOLIB
select BUILDTIME_EXTABLE_SORT
select CLKSRC_OF
select CLONE_BACKWARDS3
diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig
index 142cb057c41b..489e7f909286 100644
--- a/arch/openrisc/Kconfig
+++ b/arch/openrisc/Kconfig
@@ -10,7 +10,7 @@ config OPENRISC
select IRQ_DOMAIN
select HANDLE_DOMAIN_IRQ
select HAVE_MEMBLOCK
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
select HAVE_ARCH_TRACEHOOK
select GENERIC_IRQ_CHIP
select GENERIC_IRQ_PROBE
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 0a9d439bcda6..d111044f41a2 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -98,7 +98,6 @@ config PPC
select HAVE_FUNCTION_TRACER
select HAVE_FUNCTION_GRAPH_TRACER
select SYSCTL_EXCEPTION_TRACE
- select ARCH_WANT_OPTIONAL_GPIOLIB
select VIRT_TO_BUS if !PPC64
select HAVE_IDE
select HAVE_IOREMAP_PROT
diff --git a/arch/powerpc/platforms/40x/Kconfig b/arch/powerpc/platforms/40x/Kconfig
index 6e287f1294fa..e3257f24a8a1 100644
--- a/arch/powerpc/platforms/40x/Kconfig
+++ b/arch/powerpc/platforms/40x/Kconfig
@@ -137,7 +137,7 @@ config STB03xxx
config PPC4xx_GPIO
bool "PPC4xx GPIO support"
depends on 40x
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
help
Enable gpiolib support for ppc40x based boards
diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig
index 5538e57c36c1..48fc18041ff6 100644
--- a/arch/powerpc/platforms/44x/Kconfig
+++ b/arch/powerpc/platforms/44x/Kconfig
@@ -273,7 +273,7 @@ config PPC44x_SIMPLE
config PPC4xx_GPIO
bool "PPC4xx GPIO support"
depends on 44x
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
help
Enable gpiolib support for ppc440 based boards
diff --git a/arch/powerpc/platforms/512x/Kconfig b/arch/powerpc/platforms/512x/Kconfig
index f09016f6b3a6..bf7ae5cbd07a 100644
--- a/arch/powerpc/platforms/512x/Kconfig
+++ b/arch/powerpc/platforms/512x/Kconfig
@@ -6,7 +6,6 @@ config PPC_MPC512x
select IPIC
select PPC_PCI_CHOICE
select FSL_PCI if PCI
- select ARCH_WANT_OPTIONAL_GPIOLIB
select USB_EHCI_BIG_ENDIAN_MMIO if USB_EHCI_HCD
select USB_EHCI_BIG_ENDIAN_DESC if USB_EHCI_HCD
diff --git a/arch/powerpc/platforms/83xx/Kconfig b/arch/powerpc/platforms/83xx/Kconfig
index 2bdc8c862c46..4ef7f1cd05b7 100644
--- a/arch/powerpc/platforms/83xx/Kconfig
+++ b/arch/powerpc/platforms/83xx/Kconfig
@@ -116,7 +116,6 @@ endif
# used for usb & gpio
config PPC_MPC831x
bool
- select ARCH_WANT_OPTIONAL_GPIOLIB
# used for math-emu
config PPC_MPC832x
@@ -125,9 +124,7 @@ config PPC_MPC832x
# used for usb & gpio
config PPC_MPC834x
bool
- select ARCH_WANT_OPTIONAL_GPIOLIB
# used for usb & gpio
config PPC_MPC837x
bool
- select ARCH_WANT_OPTIONAL_GPIOLIB
diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig
index e626461a63bd..df25a3ed489d 100644
--- a/arch/powerpc/platforms/85xx/Kconfig
+++ b/arch/powerpc/platforms/85xx/Kconfig
@@ -225,7 +225,7 @@ config GE_IMP3A
select DEFAULT_UIMAGE
select SWIOTLB
select MMIO_NVRAM
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
select GE_FPGA
help
This option enables support for the GE Intelligent Platforms IMP3A
@@ -272,7 +272,7 @@ config CORENET_GENERIC
select PPC_E500MC
select PHYS_64BIT
select SWIOTLB
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
select GPIO_MPC8XXX
select HAS_RAPIDIO
select PPC_EPAPR_HV_PIC
diff --git a/arch/powerpc/platforms/86xx/Kconfig b/arch/powerpc/platforms/86xx/Kconfig
index 1afd1e4a2dd2..3988f16e46c1 100644
--- a/arch/powerpc/platforms/86xx/Kconfig
+++ b/arch/powerpc/platforms/86xx/Kconfig
@@ -4,7 +4,6 @@ menuconfig PPC_86xx
depends on 6xx
select FSL_SOC
select ALTIVEC
- select ARCH_WANT_OPTIONAL_GPIOLIB
help
The Freescale E600 SoCs have 74xx cores.
@@ -37,7 +36,7 @@ config GEF_PPC9A
bool "GE PPC9A"
select DEFAULT_UIMAGE
select MMIO_NVRAM
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
select GE_FPGA
help
This option enables support for the GE PPC9A.
@@ -46,7 +45,7 @@ config GEF_SBC310
bool "GE SBC310"
select DEFAULT_UIMAGE
select MMIO_NVRAM
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
select GE_FPGA
help
This option enables support for the GE SBC310.
@@ -55,7 +54,7 @@ config GEF_SBC610
bool "GE SBC610"
select DEFAULT_UIMAGE
select MMIO_NVRAM
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
select GE_FPGA
select HAS_RAPIDIO
help
diff --git a/arch/powerpc/platforms/8xx/Kconfig b/arch/powerpc/platforms/8xx/Kconfig
index 157250426b56..564d99bb2a26 100644
--- a/arch/powerpc/platforms/8xx/Kconfig
+++ b/arch/powerpc/platforms/8xx/Kconfig
@@ -109,7 +109,7 @@ config 8xx_COPYBACK
config 8xx_GPIO
bool "GPIO API Support"
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
help
Saying Y here will cause the ports on an MPC8xx processor to be used
with the GPIO API. If you say N here, the kernel needs less memory.
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index 46a3533d3acb..3663f71fd913 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -275,7 +275,7 @@ config TAU_AVERAGE
config QE_GPIO
bool "QE GPIO support"
depends on QUICC_ENGINE
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
help
Say Y here if you're going to use hardware that connects to the
QE GPIOs.
@@ -285,7 +285,7 @@ config CPM2
depends on (FSL_SOC_BOOKE && PPC32) || 8260
select CPM
select PPC_PCI_CHOICE
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
help
The CPM2 (Communications Processor Module) is a coprocessor on
embedded CPUs made by Freescale. Selecting this option means that
@@ -324,7 +324,7 @@ config OF_RTC
config SIMPLE_GPIO
bool "Support for simple, memory-mapped GPIO controllers"
depends on PPC
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
help
Say Y here to support simple, memory-mapped GPIO controllers.
These are usually BCSRs used to control board's switches, LEDs,
@@ -334,7 +334,7 @@ config SIMPLE_GPIO
config MCU_MPC8349EMITX
bool "MPC8349E-mITX MCU driver"
depends on I2C=y && PPC_83xx
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
help
Say Y here to enable soft power-off functionality on the Freescale
boards with the MPC8349E-mITX-compatible MCU chips. This driver will
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index e803a836cb7c..0d5f3a9bb315 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -264,7 +264,6 @@ config CPU_SUBTYPE_SH7203
select CPU_HAS_FPU
select SYS_SUPPORTS_SH_CMT
select SYS_SUPPORTS_SH_MTU2
- select ARCH_WANT_OPTIONAL_GPIOLIB
select PINCTRL
config CPU_SUBTYPE_SH7206
@@ -353,7 +352,6 @@ config CPU_SUBTYPE_SH7720
select CPU_SH3
select CPU_HAS_DSP
select SYS_SUPPORTS_SH_CMT
- select ARCH_WANT_OPTIONAL_GPIOLIB
select USB_OHCI_SH if USB_OHCI_HCD
select PINCTRL
help
@@ -419,7 +417,6 @@ config CPU_SUBTYPE_SH7723
select ARCH_SHMOBILE
select ARCH_SPARSEMEM_ENABLE
select SYS_SUPPORTS_SH_CMT
- select ARCH_WANT_OPTIONAL_GPIOLIB
select PINCTRL
help
Select SH7723 if you have an SH-MobileR2 CPU.
@@ -431,7 +428,6 @@ config CPU_SUBTYPE_SH7724
select ARCH_SHMOBILE
select ARCH_SPARSEMEM_ENABLE
select SYS_SUPPORTS_SH_CMT
- select ARCH_WANT_OPTIONAL_GPIOLIB
select PINCTRL
help
Select SH7724 if you have an SH-MobileR2R CPU.
@@ -440,7 +436,6 @@ config CPU_SUBTYPE_SH7734
bool "Support SH7734 processor"
select CPU_SH4A
select CPU_SHX2
- select ARCH_WANT_OPTIONAL_GPIOLIB
select PINCTRL
help
Select SH7734 if you have a SH4A SH7734 CPU.
@@ -449,7 +444,6 @@ config CPU_SUBTYPE_SH7757
bool "Support SH7757 processor"
select CPU_SH4A
select CPU_SHX2
- select ARCH_WANT_OPTIONAL_GPIOLIB
select PINCTRL
help
Select SH7757 if you have a SH4A SH7757 CPU.
@@ -475,7 +469,6 @@ config CPU_SUBTYPE_SH7785
select CPU_SHX2
select ARCH_SPARSEMEM_ENABLE
select SYS_SUPPORTS_NUMA
- select ARCH_WANT_OPTIONAL_GPIOLIB
select PINCTRL
config CPU_SUBTYPE_SH7786
@@ -484,7 +477,6 @@ config CPU_SUBTYPE_SH7786
select CPU_SHX3
select CPU_HAS_PTEAEX
select GENERIC_CLOCKEVENTS_BROADCAST if SMP
- select ARCH_WANT_OPTIONAL_GPIOLIB
select USB_OHCI_SH if USB_OHCI_HCD
select USB_EHCI_SH if USB_EHCI_HCD
select PINCTRL
@@ -494,7 +486,7 @@ config CPU_SUBTYPE_SHX3
select CPU_SH4A
select CPU_SHX3
select GENERIC_CLOCKEVENTS_BROADCAST if SMP
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
select PINCTRL
# SH4AL-DSP Processor Support
@@ -513,7 +505,6 @@ config CPU_SUBTYPE_SH7722
select ARCH_SPARSEMEM_ENABLE
select SYS_SUPPORTS_NUMA
select SYS_SUPPORTS_SH_CMT
- select ARCH_WANT_OPTIONAL_GPIOLIB
select PINCTRL
config CPU_SUBTYPE_SH7366
diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig
index 5e52d5362292..e0db04664e2e 100644
--- a/arch/sh/boards/Kconfig
+++ b/arch/sh/boards/Kconfig
@@ -70,7 +70,7 @@ config SH_7724_SOLUTION_ENGINE
bool "SolutionEngine7724"
select SOLUTION_ENGINE
depends on CPU_SUBTYPE_SH7724
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
select SND_SOC_AK4642 if SND_SIMPLE_CARD
select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
@@ -174,7 +174,6 @@ config SH_SDK7786
depends on CPU_SUBTYPE_SH7786
select SYS_SUPPORTS_PCI
select NO_IOPORT_MAP if !PCI
- select ARCH_WANT_OPTIONAL_GPIOLIB
select HAVE_SRAM_POOL
select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
@@ -190,7 +189,7 @@ config SH_HIGHLANDER
config SH_SH7757LCR
bool "SH7757LCR"
depends on CPU_SUBTYPE_SH7757
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
select REGULATOR_FIXED_VOLTAGE if REGULATOR
config SH_SH7785LCR
@@ -217,14 +216,14 @@ config SH_SH7785LCR_PT
config SH_URQUELL
bool "Urquell"
depends on CPU_SUBTYPE_SH7786
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
select SYS_SUPPORTS_PCI
select NO_IOPORT_MAP if !PCI
config SH_MIGOR
bool "Migo-R"
depends on CPU_SUBTYPE_SH7722
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
Select Migo-R if configuring for the SH7722 Migo-R platform
@@ -233,7 +232,7 @@ config SH_MIGOR
config SH_AP325RXA
bool "AP-325RXA"
depends on CPU_SUBTYPE_SH7723
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
Renesas "AP-325RXA" support.
@@ -242,7 +241,7 @@ config SH_AP325RXA
config SH_KFR2R09
bool "KFR2R09"
depends on CPU_SUBTYPE_SH7724
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
"Kit For R2R for 2009" support.
@@ -250,7 +249,7 @@ config SH_KFR2R09
config SH_ECOVEC
bool "EcoVec"
depends on CPU_SUBTYPE_SH7724
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
select SND_SOC_DA7210 if SND_SIMPLE_CARD
select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
@@ -327,7 +326,7 @@ config SH_X3PROTO
config SH_MAGIC_PANEL_R2
bool "Magic Panel R2"
depends on CPU_SUBTYPE_SH7720
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
Select Magic Panel R2 if configuring for Magic Panel R2.
diff --git a/arch/sh/boards/mach-highlander/Kconfig b/arch/sh/boards/mach-highlander/Kconfig
index def49cc0a7b9..42f5589b4bf3 100644
--- a/arch/sh/boards/mach-highlander/Kconfig
+++ b/arch/sh/boards/mach-highlander/Kconfig
@@ -18,7 +18,7 @@ config SH_R7780MP
config SH_R7785RP
bool "R7785RP board support"
depends on CPU_SUBTYPE_SH7785
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
endchoice
diff --git a/arch/sh/boards/mach-rsk/Kconfig b/arch/sh/boards/mach-rsk/Kconfig
index 458a11ffd022..0b9b2c4952c1 100644
--- a/arch/sh/boards/mach-rsk/Kconfig
+++ b/arch/sh/boards/mach-rsk/Kconfig
@@ -10,17 +10,17 @@ config SH_RSK7201
config SH_RSK7203
bool "RSK7203"
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
depends on CPU_SUBTYPE_SH7203
config SH_RSK7264
bool "RSK2+SH7264"
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
depends on CPU_SUBTYPE_SH7264
config SH_RSK7269
bool "RSK2+SH7269"
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
depends on CPU_SUBTYPE_SH7269
endchoice
diff --git a/arch/unicore32/Kconfig b/arch/unicore32/Kconfig
index e5602ee9c610..0769066929c6 100644
--- a/arch/unicore32/Kconfig
+++ b/arch/unicore32/Kconfig
@@ -80,7 +80,7 @@ config ARCH_PUV3
select CPU_UCV2
select GENERIC_CLOCKEVENTS
select HAVE_CLK
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
# CONFIGs for ARCH_PUV3
diff --git a/drivers/Makefile b/drivers/Makefile
index a7187b999c5e..f93771346218 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -12,7 +12,7 @@ obj-$(CONFIG_GENERIC_PHY) += phy/
# GPIO must come after pinctrl as gpios may need to mux pins etc
obj-$(CONFIG_PINCTRL) += pinctrl/
-obj-y += gpio/
+obj-$(CONFIG_GPIOLIB) += gpio/
obj-y += pwm/
obj-$(CONFIG_PCI) += pci/
obj-$(CONFIG_PARISC) += parisc/
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index d7860614f87f..98dd47a30fc7 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -250,7 +250,7 @@ config GPIO_LOONGSON
driver for GPIO functionality on Loongson-2F/3A/3B processors.
config GPIO_LPC18XX
- bool "NXP LPC18XX/43XX GPIO support"
+ tristate "NXP LPC18XX/43XX GPIO support"
default y if ARCH_LPC18XX
depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST)
help
@@ -874,6 +874,15 @@ config GPIO_LP3943
LP3943 can be used as a GPIO expander which provides up to 16 GPIOs.
Open drain outputs are required for this usage.
+config GPIO_MAX77620
+ tristate "GPIO support for PMIC MAX77620 and MAX20024"
+ depends on MFD_MAX77620
+ help
+ GPIO driver for MAX77620 and MAX20024 PMIC from Maxim Semiconductor.
+ MAX77620 PMIC has 8 pins that can be configured as GPIOs. The
+ driver also provides interrupt support for each of the gpios.
+ Say yes here to enable the max77620 to be used as gpio controller.
+
config GPIO_MSIC
bool "Intel MSIC mixed signal gpio support"
depends on MFD_INTEL_MSIC
@@ -1029,11 +1038,18 @@ config GPIO_BT8XX
If unsure, say N.
config GPIO_INTEL_MID
- bool "Intel Mid GPIO support"
- depends on X86
+ bool "Intel MID GPIO support"
+ depends on X86_INTEL_MID
+ select GPIOLIB_IRQCHIP
+ help
+ Say Y here to support Intel MID GPIO.
+
+config GPIO_MERRIFIELD
+ tristate "Intel Merrifield GPIO support"
+ depends on X86_INTEL_MID
select GPIOLIB_IRQCHIP
help
- Say Y here to support Intel Mid GPIO.
+ Say Y here to support Intel Merrifield GPIO.
config GPIO_ML_IOH
tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support"
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 991598ea3fba..2a035ed8f168 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -61,8 +61,10 @@ obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o
obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o
obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o
obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o
+obj-$(CONFIG_GPIO_MAX77620) += gpio-max77620.o
obj-$(CONFIG_GPIO_MB86S7X) += gpio-mb86s7x.o
obj-$(CONFIG_GPIO_MENZ127) += gpio-menz127.o
+obj-$(CONFIG_GPIO_MERRIFIELD) += gpio-merrifield.o
obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o
obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o
obj-$(CONFIG_GPIO_MCP23S08) += gpio-mcp23s08.o
diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c
index 80f9ddf13343..a6607faf2fdf 100644
--- a/drivers/gpio/gpio-74x164.c
+++ b/drivers/gpio/gpio-74x164.c
@@ -35,13 +35,8 @@ struct gen_74x164_chip {
static int __gen_74x164_write_config(struct gen_74x164_chip *chip)
{
- struct spi_transfer xfer = {
- .tx_buf = chip->buffer,
- .len = chip->registers,
- };
-
- return spi_sync_transfer(to_spi_device(chip->gpio_chip.parent),
- &xfer, 1);
+ return spi_write(to_spi_device(chip->gpio_chip.parent), chip->buffer,
+ chip->registers);
}
static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset)
diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c
index 5a690256af9b..52fd63f02134 100644
--- a/drivers/gpio/gpio-clps711x.c
+++ b/drivers/gpio/gpio-clps711x.c
@@ -20,8 +20,12 @@ static int clps711x_gpio_probe(struct platform_device *pdev)
void __iomem *dat, *dir;
struct gpio_chip *gc;
struct resource *res;
- int err, id = np ? of_alias_get_id(np, "gpio") : pdev->id;
+ int err, id;
+ if (!np)
+ return -ENODEV;
+
+ id = of_alias_get_id(np, "gpio");
if ((id < 0) || (id > 4))
return -ENODEV;
@@ -63,7 +67,7 @@ static int clps711x_gpio_probe(struct platform_device *pdev)
break;
}
- gc->base = id * 8;
+ gc->base = -1;
gc->owner = THIS_MODULE;
platform_set_drvdata(pdev, gc);
@@ -71,7 +75,7 @@ static int clps711x_gpio_probe(struct platform_device *pdev)
}
static const struct of_device_id __maybe_unused clps711x_gpio_ids[] = {
- { .compatible = "cirrus,clps711x-gpio" },
+ { .compatible = "cirrus,ep7209-gpio" },
{ }
};
MODULE_DEVICE_TABLE(of, clps711x_gpio_ids);
diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c
index 34779bb375de..6193f62c0df4 100644
--- a/drivers/gpio/gpio-dwapb.c
+++ b/drivers/gpio/gpio-dwapb.c
@@ -486,6 +486,7 @@ dwapb_gpio_get_pdata(struct device *dev)
pp->idx >= DWAPB_MAX_PORTS) {
dev_err(dev,
"missing/invalid port index for port%d\n", i);
+ fwnode_handle_put(fwnode);
return ERR_PTR(-EINVAL);
}
diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c
index 05aa538c3767..600be8418707 100644
--- a/drivers/gpio/gpio-f7188x.c
+++ b/drivers/gpio/gpio-f7188x.c
@@ -125,6 +125,7 @@ static inline void superio_exit(int base)
* GPIO chip.
*/
+static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset);
static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset);
static int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset);
static int f7188x_gpio_direction_out(struct gpio_chip *chip,
@@ -139,6 +140,7 @@ static int f7188x_gpio_set_single_ended(struct gpio_chip *gc,
.chip = { \
.label = DRVNAME, \
.owner = THIS_MODULE, \
+ .get_direction = f7188x_gpio_get_direction, \
.direction_input = f7188x_gpio_direction_in, \
.get = f7188x_gpio_get, \
.direction_output = f7188x_gpio_direction_out, \
@@ -209,6 +211,26 @@ static struct f7188x_gpio_bank f81866_gpio_bank[] = {
F7188X_GPIO_BANK(80, 8, 0x88),
};
+static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+ int err;
+ struct f7188x_gpio_bank *bank =
+ container_of(chip, struct f7188x_gpio_bank, chip);
+ struct f7188x_sio *sio = bank->data->sio;
+ u8 dir;
+
+ err = superio_enter(sio->addr);
+ if (err)
+ return err;
+ superio_select(sio->addr, SIO_LD_GPIO);
+
+ dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
+
+ superio_exit(sio->addr);
+
+ return !(dir & 1 << offset);
+}
+
static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
int err;
diff --git a/drivers/gpio/gpio-intel-mid.c b/drivers/gpio/gpio-intel-mid.c
index cdaba13cb8e8..164de64b11fc 100644
--- a/drivers/gpio/gpio-intel-mid.c
+++ b/drivers/gpio/gpio-intel-mid.c
@@ -1,7 +1,7 @@
/*
* Intel MID GPIO driver
*
- * Copyright (c) 2008-2014 Intel Corporation.
+ * Copyright (c) 2008-2014,2016 Intel Corporation.
*
* 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
@@ -17,21 +17,20 @@
* Moorestown platform Langwell chip.
* Medfield platform Penwell chip.
* Clovertrail platform Cloverview chip.
- * Merrifield platform Tangier chip.
*/
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-#include <linux/kernel.h>
#include <linux/delay.h>
-#include <linux/stddef.h>
-#include <linux/interrupt.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/gpio/driver.h>
-#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/stddef.h>
#define INTEL_MID_IRQ_TYPE_EDGE (1 << 0)
#define INTEL_MID_IRQ_TYPE_LEVEL (1 << 1)
@@ -64,10 +63,6 @@ enum GPIO_REG {
/* intel_mid gpio driver data */
struct intel_mid_gpio_ddata {
u16 ngpio; /* number of gpio pins */
- u32 gplr_offset; /* offset of first GPLR register from base */
- u32 flis_base; /* base address of FLIS registers */
- u32 flis_len; /* length of FLIS registers */
- u32 (*get_flis_offset)(int gpio);
u32 chip_irq_type; /* chip interrupt type */
};
@@ -252,15 +247,6 @@ static const struct intel_mid_gpio_ddata gpio_cloverview_core = {
.chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE,
};
-static const struct intel_mid_gpio_ddata gpio_tangier = {
- .ngpio = 192,
- .gplr_offset = 4,
- .flis_base = 0xff0c0000,
- .flis_len = 0x8000,
- .get_flis_offset = NULL,
- .chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE,
-};
-
static const struct pci_device_id intel_gpio_ids[] = {
{
/* Lincroft */
@@ -287,11 +273,6 @@ static const struct pci_device_id intel_gpio_ids[] = {
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08f7),
.driver_data = (kernel_ulong_t)&gpio_cloverview_core,
},
- {
- /* Tangier */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x1199),
- .driver_data = (kernel_ulong_t)&gpio_tangier,
- },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, intel_gpio_ids);
@@ -401,7 +382,7 @@ static int intel_gpio_probe(struct pci_dev *pdev,
spin_lock_init(&priv->lock);
pci_set_drvdata(pdev, priv);
- retval = gpiochip_add_data(&priv->chip, priv);
+ retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv);
if (retval) {
dev_err(&pdev->dev, "gpiochip_add error %d\n", retval);
return retval;
diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c
index 9df015e85ad9..fbd393b46ce0 100644
--- a/drivers/gpio/gpio-lynxpoint.c
+++ b/drivers/gpio/gpio-lynxpoint.c
@@ -383,7 +383,6 @@ static int lp_gpio_probe(struct platform_device *pdev)
handle_simple_irq, IRQ_TYPE_NONE);
if (ret) {
dev_err(dev, "failed to add irqchip\n");
- gpiochip_remove(gc);
return ret;
}
diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c
new file mode 100644
index 000000000000..b46b436cb97f
--- /dev/null
+++ b/drivers/gpio/gpio-max77620.c
@@ -0,0 +1,315 @@
+/*
+ * MAXIM MAX77620 GPIO driver
+ *
+ * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/max77620.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define GPIO_REG_ADDR(offset) (MAX77620_REG_GPIO0 + offset)
+
+struct max77620_gpio {
+ struct gpio_chip gpio_chip;
+ struct regmap *rmap;
+ struct device *dev;
+ int gpio_irq;
+ int irq_base;
+ int gpio_base;
+};
+
+static const struct regmap_irq max77620_gpio_irqs[] = {
+ [0] = {
+ .mask = MAX77620_IRQ_LVL2_GPIO_EDGE0,
+ .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING,
+ .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,
+ .reg_offset = 0,
+ .type_reg_offset = 0,
+ },
+ [1] = {
+ .mask = MAX77620_IRQ_LVL2_GPIO_EDGE1,
+ .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING,
+ .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,
+ .reg_offset = 0,
+ .type_reg_offset = 1,
+ },
+ [2] = {
+ .mask = MAX77620_IRQ_LVL2_GPIO_EDGE2,
+ .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING,
+ .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,
+ .reg_offset = 0,
+ .type_reg_offset = 2,
+ },
+ [3] = {
+ .mask = MAX77620_IRQ_LVL2_GPIO_EDGE3,
+ .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING,
+ .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,
+ .reg_offset = 0,
+ .type_reg_offset = 3,
+ },
+ [4] = {
+ .mask = MAX77620_IRQ_LVL2_GPIO_EDGE4,
+ .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING,
+ .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,
+ .reg_offset = 0,
+ .type_reg_offset = 4,
+ },
+ [5] = {
+ .mask = MAX77620_IRQ_LVL2_GPIO_EDGE5,
+ .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING,
+ .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,
+ .reg_offset = 0,
+ .type_reg_offset = 5,
+ },
+ [6] = {
+ .mask = MAX77620_IRQ_LVL2_GPIO_EDGE6,
+ .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING,
+ .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,
+ .reg_offset = 0,
+ .type_reg_offset = 6,
+ },
+ [7] = {
+ .mask = MAX77620_IRQ_LVL2_GPIO_EDGE7,
+ .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING,
+ .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,
+ .reg_offset = 0,
+ .type_reg_offset = 7,
+ },
+};
+
+static struct regmap_irq_chip max77620_gpio_irq_chip = {
+ .name = "max77620-gpio",
+ .irqs = max77620_gpio_irqs,
+ .num_irqs = ARRAY_SIZE(max77620_gpio_irqs),
+ .num_regs = 1,
+ .num_type_reg = 8,
+ .irq_reg_stride = 1,
+ .type_reg_stride = 1,
+ .status_base = MAX77620_REG_IRQ_LVL2_GPIO,
+ .type_base = MAX77620_REG_GPIO0,
+};
+
+static int max77620_gpio_dir_input(struct gpio_chip *gc, unsigned int offset)
+{
+ struct max77620_gpio *mgpio = gpiochip_get_data(gc);
+ int ret;
+
+ ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset),
+ MAX77620_CNFG_GPIO_DIR_MASK,
+ MAX77620_CNFG_GPIO_DIR_INPUT);
+ if (ret < 0)
+ dev_err(mgpio->dev, "CNFG_GPIOx dir update failed: %d\n", ret);
+
+ return ret;
+}
+
+static int max77620_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct max77620_gpio *mgpio = gpiochip_get_data(gc);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(mgpio->rmap, GPIO_REG_ADDR(offset), &val);
+ if (ret < 0) {
+ dev_err(mgpio->dev, "CNFG_GPIOx read failed: %d\n", ret);
+ return ret;
+ }
+
+ if (val & MAX77620_CNFG_GPIO_DIR_MASK)
+ return !!(val & MAX77620_CNFG_GPIO_INPUT_VAL_MASK);
+ else
+ return !!(val & MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK);
+}
+
+static int max77620_gpio_dir_output(struct gpio_chip *gc, unsigned int offset,
+ int value)
+{
+ struct max77620_gpio *mgpio = gpiochip_get_data(gc);
+ u8 val;
+ int ret;
+
+ val = (value) ? MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH :
+ MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW;
+
+ ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset),
+ MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK, val);
+ if (ret < 0) {
+ dev_err(mgpio->dev, "CNFG_GPIOx val update failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset),
+ MAX77620_CNFG_GPIO_DIR_MASK,
+ MAX77620_CNFG_GPIO_DIR_OUTPUT);
+ if (ret < 0)
+ dev_err(mgpio->dev, "CNFG_GPIOx dir update failed: %d\n", ret);
+
+ return ret;
+}
+
+static int max77620_gpio_set_debounce(struct gpio_chip *gc,
+ unsigned int offset,
+ unsigned int debounce)
+{
+ struct max77620_gpio *mgpio = gpiochip_get_data(gc);
+ u8 val;
+ int ret;
+
+ switch (debounce) {
+ case 0:
+ val = MAX77620_CNFG_GPIO_DBNC_None;
+ break;
+ case 1 ... 8:
+ val = MAX77620_CNFG_GPIO_DBNC_8ms;
+ break;
+ case 9 ... 16:
+ val = MAX77620_CNFG_GPIO_DBNC_16ms;
+ break;
+ case 17 ... 32:
+ val = MAX77620_CNFG_GPIO_DBNC_32ms;
+ break;
+ default:
+ dev_err(mgpio->dev, "Illegal value %u\n", debounce);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset),
+ MAX77620_CNFG_GPIO_DBNC_MASK, val);
+ if (ret < 0)
+ dev_err(mgpio->dev, "CNFG_GPIOx_DBNC update failed: %d\n", ret);
+
+ return ret;
+}
+
+static void max77620_gpio_set(struct gpio_chip *gc, unsigned int offset,
+ int value)
+{
+ struct max77620_gpio *mgpio = gpiochip_get_data(gc);
+ u8 val;
+ int ret;
+
+ val = (value) ? MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH :
+ MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW;
+
+ ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset),
+ MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK, val);
+ if (ret < 0)
+ dev_err(mgpio->dev, "CNFG_GPIO_OUT update failed: %d\n", ret);
+}
+
+static int max77620_gpio_set_single_ended(struct gpio_chip *gc,
+ unsigned int offset,
+ enum single_ended_mode mode)
+{
+ struct max77620_gpio *mgpio = gpiochip_get_data(gc);
+
+ switch (mode) {
+ case LINE_MODE_OPEN_DRAIN:
+ return regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset),
+ MAX77620_CNFG_GPIO_DRV_MASK,
+ MAX77620_CNFG_GPIO_DRV_OPENDRAIN);
+ case LINE_MODE_PUSH_PULL:
+ return regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset),
+ MAX77620_CNFG_GPIO_DRV_MASK,
+ MAX77620_CNFG_GPIO_DRV_PUSHPULL);
+ default:
+ break;
+ }
+
+ return -ENOTSUPP;
+}
+
+static int max77620_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
+{
+ struct max77620_gpio *mgpio = gpiochip_get_data(gc);
+ struct max77620_chip *chip = dev_get_drvdata(mgpio->dev->parent);
+
+ return regmap_irq_get_virq(chip->gpio_irq_data, offset);
+}
+
+static int max77620_gpio_probe(struct platform_device *pdev)
+{
+ struct max77620_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct max77620_gpio *mgpio;
+ int gpio_irq;
+ int ret;
+
+ gpio_irq = platform_get_irq(pdev, 0);
+ if (gpio_irq <= 0) {
+ dev_err(&pdev->dev, "GPIO irq not available %d\n", gpio_irq);
+ return -ENODEV;
+ }
+
+ mgpio = devm_kzalloc(&pdev->dev, sizeof(*mgpio), GFP_KERNEL);
+ if (!mgpio)
+ return -ENOMEM;
+
+ mgpio->rmap = chip->rmap;
+ mgpio->dev = &pdev->dev;
+ mgpio->gpio_irq = gpio_irq;
+
+ mgpio->gpio_chip.label = pdev->name;
+ mgpio->gpio_chip.parent = &pdev->dev;
+ mgpio->gpio_chip.direction_input = max77620_gpio_dir_input;
+ mgpio->gpio_chip.get = max77620_gpio_get;
+ mgpio->gpio_chip.direction_output = max77620_gpio_dir_output;
+ mgpio->gpio_chip.set_debounce = max77620_gpio_set_debounce;
+ mgpio->gpio_chip.set = max77620_gpio_set;
+ mgpio->gpio_chip.set_single_ended = max77620_gpio_set_single_ended;
+ mgpio->gpio_chip.to_irq = max77620_gpio_to_irq;
+ mgpio->gpio_chip.ngpio = MAX77620_GPIO_NR;
+ mgpio->gpio_chip.can_sleep = 1;
+ mgpio->gpio_chip.base = -1;
+ mgpio->irq_base = -1;
+#ifdef CONFIG_OF_GPIO
+ mgpio->gpio_chip.of_node = pdev->dev.parent->of_node;
+#endif
+
+ platform_set_drvdata(pdev, mgpio);
+
+ ret = devm_gpiochip_add_data(&pdev->dev, &mgpio->gpio_chip, mgpio);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "gpio_init: Failed to add max77620_gpio\n");
+ return ret;
+ }
+
+ mgpio->gpio_base = mgpio->gpio_chip.base;
+ ret = devm_regmap_add_irq_chip(&pdev->dev, chip->rmap, mgpio->gpio_irq,
+ IRQF_ONESHOT, mgpio->irq_base,
+ &max77620_gpio_irq_chip,
+ &chip->gpio_irq_data);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to add gpio irq_chip %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id max77620_gpio_devtype[] = {
+ { .name = "max77620-gpio", },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, max77620_gpio_devtype);
+
+static struct platform_driver max77620_gpio_driver = {
+ .driver.name = "max77620-gpio",
+ .probe = max77620_gpio_probe,
+ .id_table = max77620_gpio_devtype,
+};
+
+module_platform_driver(max77620_gpio_driver);
+
+MODULE_DESCRIPTION("GPIO interface for MAX77620 and MAX20024 PMIC");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_AUTHOR("Chaitanya Bandi <bandik@nvidia.com>");
+MODULE_ALIAS("platform:max77620-gpio");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-menz127.c b/drivers/gpio/gpio-menz127.c
index cc103aff45e4..a1210e330571 100644
--- a/drivers/gpio/gpio-menz127.c
+++ b/drivers/gpio/gpio-menz127.c
@@ -187,7 +187,6 @@ 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,
diff --git a/drivers/gpio/gpio-merrifield.c b/drivers/gpio/gpio-merrifield.c
new file mode 100644
index 000000000000..45b51278b8ee
--- /dev/null
+++ b/drivers/gpio/gpio-merrifield.c
@@ -0,0 +1,444 @@
+/*
+ * Intel Merrifield SoC GPIO driver
+ *
+ * Copyright (c) 2016 Intel Corporation.
+ * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pinctrl/consumer.h>
+
+#define GCCR 0x000 /* controller configuration */
+#define GPLR 0x004 /* pin level r/o */
+#define GPDR 0x01c /* pin direction */
+#define GPSR 0x034 /* pin set w/o */
+#define GPCR 0x04c /* pin clear w/o */
+#define GRER 0x064 /* rising edge detect */
+#define GFER 0x07c /* falling edge detect */
+#define GFBR 0x094 /* glitch filter bypass */
+#define GIMR 0x0ac /* interrupt mask */
+#define GISR 0x0c4 /* interrupt source */
+#define GITR 0x300 /* input type */
+#define GLPR 0x318 /* level input polarity */
+#define GWMR 0x400 /* wake mask */
+#define GWSR 0x418 /* wake source */
+#define GSIR 0xc00 /* secure input */
+
+/* Intel Merrifield has 192 GPIO pins */
+#define MRFLD_NGPIO 192
+
+struct mrfld_gpio_pinrange {
+ unsigned int gpio_base;
+ unsigned int pin_base;
+ unsigned int npins;
+};
+
+#define GPIO_PINRANGE(gstart, gend, pstart) \
+ { \
+ .gpio_base = (gstart), \
+ .pin_base = (pstart), \
+ .npins = (gend) - (gstart) + 1, \
+ }
+
+struct mrfld_gpio {
+ struct gpio_chip chip;
+ void __iomem *reg_base;
+ raw_spinlock_t lock;
+ struct device *dev;
+};
+
+static const struct mrfld_gpio_pinrange mrfld_gpio_ranges[] = {
+ GPIO_PINRANGE(0, 11, 146),
+ GPIO_PINRANGE(12, 13, 144),
+ GPIO_PINRANGE(14, 15, 35),
+ GPIO_PINRANGE(16, 16, 164),
+ GPIO_PINRANGE(17, 18, 105),
+ GPIO_PINRANGE(19, 22, 101),
+ GPIO_PINRANGE(23, 30, 107),
+ GPIO_PINRANGE(32, 43, 67),
+ GPIO_PINRANGE(44, 63, 195),
+ GPIO_PINRANGE(64, 67, 140),
+ GPIO_PINRANGE(68, 69, 165),
+ GPIO_PINRANGE(70, 71, 65),
+ GPIO_PINRANGE(72, 76, 228),
+ GPIO_PINRANGE(77, 86, 37),
+ GPIO_PINRANGE(87, 87, 48),
+ GPIO_PINRANGE(88, 88, 47),
+ GPIO_PINRANGE(89, 96, 49),
+ GPIO_PINRANGE(97, 97, 34),
+ GPIO_PINRANGE(102, 119, 83),
+ GPIO_PINRANGE(120, 123, 79),
+ GPIO_PINRANGE(124, 135, 115),
+ GPIO_PINRANGE(137, 142, 158),
+ GPIO_PINRANGE(154, 163, 24),
+ GPIO_PINRANGE(164, 176, 215),
+ GPIO_PINRANGE(177, 189, 127),
+ GPIO_PINRANGE(190, 191, 178),
+};
+
+static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned int offset,
+ unsigned int reg_type_offset)
+{
+ struct mrfld_gpio *priv = gpiochip_get_data(chip);
+ u8 reg = offset / 32;
+
+ return priv->reg_base + reg_type_offset + reg * 4;
+}
+
+static int mrfld_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ void __iomem *gplr = gpio_reg(chip, offset, GPLR);
+
+ return !!(readl(gplr) & BIT(offset % 32));
+}
+
+static void mrfld_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ struct mrfld_gpio *priv = gpiochip_get_data(chip);
+ void __iomem *gpsr, *gpcr;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&priv->lock, flags);
+
+ if (value) {
+ gpsr = gpio_reg(chip, offset, GPSR);
+ writel(BIT(offset % 32), gpsr);
+ } else {
+ gpcr = gpio_reg(chip, offset, GPCR);
+ writel(BIT(offset % 32), gpcr);
+ }
+
+ raw_spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int mrfld_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct mrfld_gpio *priv = gpiochip_get_data(chip);
+ void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
+ unsigned long flags;
+ u32 value;
+
+ raw_spin_lock_irqsave(&priv->lock, flags);
+
+ value = readl(gpdr);
+ value &= ~BIT(offset % 32);
+ writel(value, gpdr);
+
+ raw_spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int mrfld_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct mrfld_gpio *priv = gpiochip_get_data(chip);
+ void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
+ unsigned long flags;
+
+ mrfld_gpio_set(chip, offset, value);
+
+ raw_spin_lock_irqsave(&priv->lock, flags);
+
+ value = readl(gpdr);
+ value |= BIT(offset % 32);
+ writel(value, gpdr);
+
+ raw_spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static void mrfld_irq_ack(struct irq_data *d)
+{
+ struct mrfld_gpio *priv = irq_data_get_irq_chip_data(d);
+ u32 gpio = irqd_to_hwirq(d);
+ void __iomem *gisr = gpio_reg(&priv->chip, gpio, GISR);
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&priv->lock, flags);
+
+ writel(BIT(gpio % 32), gisr);
+
+ raw_spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void mrfld_irq_unmask_mask(struct irq_data *d, bool unmask)
+{
+ struct mrfld_gpio *priv = irq_data_get_irq_chip_data(d);
+ u32 gpio = irqd_to_hwirq(d);
+ void __iomem *gimr = gpio_reg(&priv->chip, gpio, GIMR);
+ unsigned long flags;
+ u32 value;
+
+ raw_spin_lock_irqsave(&priv->lock, flags);
+
+ if (unmask)
+ value = readl(gimr) | BIT(gpio % 32);
+ else
+ value = readl(gimr) & ~BIT(gpio % 32);
+ writel(value, gimr);
+
+ raw_spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void mrfld_irq_mask(struct irq_data *d)
+{
+ mrfld_irq_unmask_mask(d, false);
+}
+
+static void mrfld_irq_unmask(struct irq_data *d)
+{
+ mrfld_irq_unmask_mask(d, true);
+}
+
+static int mrfld_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct mrfld_gpio *priv = gpiochip_get_data(gc);
+ u32 gpio = irqd_to_hwirq(d);
+ void __iomem *grer = gpio_reg(&priv->chip, gpio, GRER);
+ void __iomem *gfer = gpio_reg(&priv->chip, gpio, GFER);
+ void __iomem *gitr = gpio_reg(&priv->chip, gpio, GITR);
+ void __iomem *glpr = gpio_reg(&priv->chip, gpio, GLPR);
+ unsigned long flags;
+ u32 value;
+
+ raw_spin_lock_irqsave(&priv->lock, flags);
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ value = readl(grer) | BIT(gpio % 32);
+ else
+ value = readl(grer) & ~BIT(gpio % 32);
+ writel(value, grer);
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ value = readl(gfer) | BIT(gpio % 32);
+ else
+ value = readl(gfer) & ~BIT(gpio % 32);
+ writel(value, gfer);
+
+ /*
+ * To prevent glitches from triggering an unintended level interrupt,
+ * configure GLPR register first and then configure GITR.
+ */
+ if (type & IRQ_TYPE_LEVEL_LOW)
+ value = readl(glpr) | BIT(gpio % 32);
+ else
+ value = readl(glpr) & ~BIT(gpio % 32);
+ writel(value, glpr);
+
+ if (type & IRQ_TYPE_LEVEL_MASK) {
+ value = readl(gitr) | BIT(gpio % 32);
+ writel(value, gitr);
+
+ irq_set_handler_locked(d, handle_level_irq);
+ } else if (type & IRQ_TYPE_EDGE_BOTH) {
+ value = readl(gitr) & ~BIT(gpio % 32);
+ writel(value, gitr);
+
+ irq_set_handler_locked(d, handle_edge_irq);
+ }
+
+ raw_spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int mrfld_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct mrfld_gpio *priv = gpiochip_get_data(gc);
+ u32 gpio = irqd_to_hwirq(d);
+ void __iomem *gwmr = gpio_reg(&priv->chip, gpio, GWMR);
+ void __iomem *gwsr = gpio_reg(&priv->chip, gpio, GWSR);
+ unsigned long flags;
+ u32 value;
+
+ raw_spin_lock_irqsave(&priv->lock, flags);
+
+ /* Clear the existing wake status */
+ writel(BIT(gpio % 32), gwsr);
+
+ if (on)
+ value = readl(gwmr) | BIT(gpio % 32);
+ else
+ value = readl(gwmr) & ~BIT(gpio % 32);
+ writel(value, gwmr);
+
+ raw_spin_unlock_irqrestore(&priv->lock, flags);
+
+ dev_dbg(priv->dev, "%sable wake for gpio %u\n", on ? "en" : "dis", gpio);
+ return 0;
+}
+
+static struct irq_chip mrfld_irqchip = {
+ .name = "gpio-merrifield",
+ .irq_ack = mrfld_irq_ack,
+ .irq_mask = mrfld_irq_mask,
+ .irq_unmask = mrfld_irq_unmask,
+ .irq_set_type = mrfld_irq_set_type,
+ .irq_set_wake = mrfld_irq_set_wake,
+};
+
+static void mrfld_irq_handler(struct irq_desc *desc)
+{
+ struct gpio_chip *gc = irq_desc_get_handler_data(desc);
+ struct mrfld_gpio *priv = gpiochip_get_data(gc);
+ struct irq_chip *irqchip = irq_desc_get_chip(desc);
+ unsigned long base, gpio;
+
+ chained_irq_enter(irqchip, desc);
+
+ /* Check GPIO controller to check which pin triggered the interrupt */
+ for (base = 0; base < priv->chip.ngpio; base += 32) {
+ void __iomem *gisr = gpio_reg(&priv->chip, base, GISR);
+ void __iomem *gimr = gpio_reg(&priv->chip, base, GIMR);
+ unsigned long pending, enabled;
+
+ pending = readl(gisr);
+ enabled = readl(gimr);
+
+ /* Only interrupts that are enabled */
+ pending &= enabled;
+
+ for_each_set_bit(gpio, &pending, 32) {
+ unsigned int irq;
+
+ irq = irq_find_mapping(gc->irqdomain, base + gpio);
+ generic_handle_irq(irq);
+ }
+ }
+
+ chained_irq_exit(irqchip, desc);
+}
+
+static void mrfld_irq_init_hw(struct mrfld_gpio *priv)
+{
+ void __iomem *reg;
+ unsigned int base;
+
+ for (base = 0; base < priv->chip.ngpio; base += 32) {
+ /* Clear the rising-edge detect register */
+ reg = gpio_reg(&priv->chip, base, GRER);
+ writel(0, reg);
+ /* Clear the falling-edge detect register */
+ reg = gpio_reg(&priv->chip, base, GFER);
+ writel(0, reg);
+ }
+}
+
+static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ const struct mrfld_gpio_pinrange *range;
+ struct mrfld_gpio *priv;
+ u32 gpio_base, irq_base;
+ void __iomem *base;
+ unsigned int i;
+ int retval;
+
+ retval = pcim_enable_device(pdev);
+ if (retval)
+ return retval;
+
+ retval = pcim_iomap_regions(pdev, BIT(1) | BIT(0), pci_name(pdev));
+ if (retval) {
+ dev_err(&pdev->dev, "I/O memory mapping error\n");
+ return retval;
+ }
+
+ base = pcim_iomap_table(pdev)[1];
+
+ irq_base = readl(base);
+ gpio_base = readl(sizeof(u32) + base);
+
+ /* Release the IO mapping, since we already get the info from BAR1 */
+ pcim_iounmap_regions(pdev, BIT(1));
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&pdev->dev, "can't allocate chip data\n");
+ return -ENOMEM;
+ }
+
+ priv->dev = &pdev->dev;
+ priv->reg_base = pcim_iomap_table(pdev)[0];
+
+ priv->chip.label = dev_name(&pdev->dev);
+ priv->chip.parent = &pdev->dev;
+ priv->chip.request = gpiochip_generic_request;
+ priv->chip.free = gpiochip_generic_free;
+ priv->chip.direction_input = mrfld_gpio_direction_input;
+ priv->chip.direction_output = mrfld_gpio_direction_output;
+ priv->chip.get = mrfld_gpio_get;
+ priv->chip.set = mrfld_gpio_set;
+ priv->chip.base = gpio_base;
+ priv->chip.ngpio = MRFLD_NGPIO;
+ priv->chip.can_sleep = false;
+
+ raw_spin_lock_init(&priv->lock);
+
+ pci_set_drvdata(pdev, priv);
+ retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv);
+ if (retval) {
+ dev_err(&pdev->dev, "gpiochip_add error %d\n", retval);
+ return retval;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mrfld_gpio_ranges); i++) {
+ range = &mrfld_gpio_ranges[i];
+ retval = gpiochip_add_pin_range(&priv->chip,
+ "pinctrl-merrifield",
+ range->gpio_base,
+ range->pin_base,
+ range->npins);
+ if (retval) {
+ dev_err(&pdev->dev, "failed to add GPIO pin range\n");
+ return retval;
+ }
+ }
+
+ retval = gpiochip_irqchip_add(&priv->chip, &mrfld_irqchip, irq_base,
+ handle_simple_irq, IRQ_TYPE_NONE);
+ if (retval) {
+ dev_err(&pdev->dev, "could not connect irqchip to gpiochip\n");
+ return retval;
+ }
+
+ mrfld_irq_init_hw(priv);
+
+ gpiochip_set_chained_irqchip(&priv->chip, &mrfld_irqchip, pdev->irq,
+ mrfld_irq_handler);
+
+ return 0;
+}
+
+static const struct pci_device_id mrfld_gpio_ids[] = {
+ { PCI_VDEVICE(INTEL, 0x1199) },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, mrfld_gpio_ids);
+
+static struct pci_driver mrfld_gpio_driver = {
+ .name = "gpio-merrifield",
+ .id_table = mrfld_gpio_ids,
+ .probe = mrfld_gpio_probe,
+};
+
+module_pci_driver(mrfld_gpio_driver);
+
+MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
+MODULE_DESCRIPTION("Intel Merrifield SoC GPIO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c
index 6c1cb3b8c02c..6ec144baeb11 100644
--- a/drivers/gpio/gpio-mmio.c
+++ b/drivers/gpio/gpio-mmio.c
@@ -61,6 +61,8 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
#include <linux/bitops.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
static void bgpio_write8(void __iomem *reg, unsigned long data)
{
@@ -569,6 +571,41 @@ static void __iomem *bgpio_map(struct platform_device *pdev,
return devm_ioremap_resource(&pdev->dev, r);
}
+#ifdef CONFIG_OF
+static const struct of_device_id bgpio_of_match[] = {
+ { .compatible = "wd,mbl-gpio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bgpio_of_match);
+
+static struct bgpio_pdata *bgpio_parse_dt(struct platform_device *pdev,
+ unsigned long *flags)
+{
+ struct bgpio_pdata *pdata;
+
+ if (!of_match_device(bgpio_of_match, &pdev->dev))
+ return NULL;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(struct bgpio_pdata),
+ GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->base = -1;
+
+ if (of_property_read_bool(pdev->dev.of_node, "no-output"))
+ *flags |= BGPIOF_NO_OUTPUT;
+
+ return pdata;
+}
+#else
+static struct bgpio_pdata *bgpio_parse_dt(struct platform_device *pdev,
+ unsigned long *flags)
+{
+ return NULL;
+}
+#endif /* CONFIG_OF */
+
static int bgpio_pdev_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -579,10 +616,19 @@ static int bgpio_pdev_probe(struct platform_device *pdev)
void __iomem *dirout;
void __iomem *dirin;
unsigned long sz;
- unsigned long flags = pdev->id_entry->driver_data;
+ unsigned long flags = 0;
int err;
struct gpio_chip *gc;
- struct bgpio_pdata *pdata = dev_get_platdata(dev);
+ struct bgpio_pdata *pdata;
+
+ pdata = bgpio_parse_dt(pdev, &flags);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+
+ if (!pdata) {
+ pdata = dev_get_platdata(dev);
+ flags = pdev->id_entry->driver_data;
+ }
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
if (!r)
@@ -646,6 +692,7 @@ MODULE_DEVICE_TABLE(platform, bgpio_id_table);
static struct platform_driver bgpio_driver = {
.driver = {
.name = "basic-mmio-gpio",
+ .of_match_table = of_match_ptr(bgpio_of_match),
},
.id_table = bgpio_id_table,
.probe = bgpio_pdev_probe,
diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c
index e248707ca39e..839474430229 100644
--- a/drivers/gpio/gpio-palmas.c
+++ b/drivers/gpio/gpio-palmas.c
@@ -208,7 +208,6 @@ static int palmas_gpio_probe(struct platform_device *pdev)
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,
};
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index 5e3be32ebb8d..02f2a5621bb0 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -44,7 +44,7 @@
#define PCA_GPIO_MASK 0x00FF
#define PCA_INT 0x0100
-#define PCA_PCAL 0x0200
+#define PCA_PCAL 0x0200
#define PCA953X_TYPE 0x1000
#define PCA957X_TYPE 0x2000
#define PCA_TYPE_MASK 0xF000
@@ -67,6 +67,8 @@ static const struct i2c_device_id pca953x_id[] = {
{ "pca9575", 16 | PCA957X_TYPE | PCA_INT, },
{ "pca9698", 40 | PCA953X_TYPE, },
+ { "pcal9555a", 16 | PCA953X_TYPE | PCA_INT | PCA_PCAL, },
+
{ "max7310", 8 | PCA953X_TYPE, },
{ "max7312", 16 | PCA953X_TYPE | PCA_INT, },
{ "max7313", 16 | PCA953X_TYPE | PCA_INT, },
@@ -90,7 +92,7 @@ MODULE_DEVICE_TABLE(acpi, pca953x_acpi_ids);
#define MAX_BANK 5
#define BANK_SZ 8
-#define NBANK(chip) (chip->gpio_chip.ngpio / BANK_SZ)
+#define NBANK(chip) DIV_ROUND_UP(chip->gpio_chip.ngpio, BANK_SZ)
struct pca953x_chip {
unsigned gpio_start;
@@ -135,7 +137,7 @@ static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val,
static int pca953x_write_single(struct pca953x_chip *chip, int reg, u32 val,
int off)
{
- int ret = 0;
+ int ret;
int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
int offset = off / BANK_SZ;
@@ -163,10 +165,13 @@ static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)
NBANK(chip), val);
} else {
switch (chip->chip_type) {
- case PCA953X_TYPE:
- ret = i2c_smbus_write_word_data(chip->client,
- reg << 1, cpu_to_le16(get_unaligned((u16 *)val)));
+ case PCA953X_TYPE: {
+ __le16 word = cpu_to_le16(get_unaligned((u16 *)val));
+
+ ret = i2c_smbus_write_word_data(chip->client, reg << 1,
+ (__force u16)word);
break;
+ }
case PCA957X_TYPE:
ret = i2c_smbus_write_byte_data(chip->client, reg << 1,
val[0]);
@@ -235,7 +240,6 @@ static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
goto exit;
chip->reg_direction[off / BANK_SZ] = reg_val;
- ret = 0;
exit:
mutex_unlock(&chip->i2c_lock);
return ret;
@@ -286,7 +290,6 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
goto exit;
chip->reg_direction[off / BANK_SZ] = reg_val;
- ret = 0;
exit:
mutex_unlock(&chip->i2c_lock);
return ret;
@@ -351,7 +354,6 @@ exit:
mutex_unlock(&chip->i2c_lock);
}
-
static void pca953x_gpio_set_multiple(struct gpio_chip *gc,
unsigned long *mask, unsigned long *bits)
{
@@ -820,7 +822,7 @@ static int pca953x_remove(struct i2c_client *client)
{
struct pca953x_platform_data *pdata = dev_get_platdata(&client->dev);
struct pca953x_chip *chip = i2c_get_clientdata(client);
- int ret = 0;
+ int ret;
if (pdata && pdata->teardown) {
ret = pdata->teardown(client, chip->gpio_chip.base,
@@ -861,6 +863,7 @@ static const struct of_device_id pca953x_dt_ids[] = {
{ .compatible = "maxim,max7315", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "ti,pca6107", .data = OF_953X( 8, PCA_INT), },
+ { .compatible = "ti,pca9536", .data = OF_953X( 4, 0), },
{ .compatible = "ti,tca6408", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), },
{ .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), },
diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c
index 169c09aa33c8..d168410e2338 100644
--- a/drivers/gpio/gpio-pcf857x.c
+++ b/drivers/gpio/gpio-pcf857x.c
@@ -440,6 +440,14 @@ static int pcf857x_remove(struct i2c_client *client)
return status;
}
+static void pcf857x_shutdown(struct i2c_client *client)
+{
+ struct pcf857x *gpio = i2c_get_clientdata(client);
+
+ /* Drive all the I/O lines high */
+ gpio->write(gpio->client, BIT(gpio->chip.ngpio) - 1);
+}
+
static struct i2c_driver pcf857x_driver = {
.driver = {
.name = "pcf857x",
@@ -447,6 +455,7 @@ static struct i2c_driver pcf857x_driver = {
},
.probe = pcf857x_probe,
.remove = pcf857x_remove,
+ .shutdown = pcf857x_shutdown,
.id_table = pcf857x_id,
};
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
index 681c93fb9e70..b96e0b466f74 100644
--- a/drivers/gpio/gpio-rcar.c
+++ b/drivers/gpio/gpio-rcar.c
@@ -335,6 +335,9 @@ static const struct of_device_id gpio_rcar_of_table[] = {
.compatible = "renesas,gpio-r8a7791",
.data = &gpio_rcar_info_gen2,
}, {
+ .compatible = "renesas,gpio-r8a7792",
+ .data = &gpio_rcar_info_gen2,
+ }, {
.compatible = "renesas,gpio-r8a7793",
.data = &gpio_rcar_info_gen2,
}, {
diff --git a/drivers/gpio/gpio-rdc321x.c b/drivers/gpio/gpio-rdc321x.c
index ec945b90f54d..cbf0f9e6465b 100644
--- a/drivers/gpio/gpio-rdc321x.c
+++ b/drivers/gpio/gpio-rdc321x.c
@@ -200,7 +200,6 @@ static int rdc321x_gpio_probe(struct platform_device *pdev)
static struct platform_driver rdc321x_gpio_driver = {
.driver.name = "rdc321x-gpio",
- .driver.owner = THIS_MODULE,
.probe = rdc321x_gpio_probe,
};
diff --git a/drivers/gpio/gpio-sch311x.c b/drivers/gpio/gpio-sch311x.c
index a03b38ee2e02..b96990c262a1 100644
--- a/drivers/gpio/gpio-sch311x.c
+++ b/drivers/gpio/gpio-sch311x.c
@@ -296,7 +296,6 @@ static int sch311x_gpio_remove(struct platform_device *pdev)
static struct platform_driver sch311x_gpio_driver = {
.driver.name = DRV_NAME,
- .driver.owner = THIS_MODULE,
.probe = sch311x_gpio_probe,
.remove = sch311x_gpio_remove,
};
diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c
index 6f7af28b8966..f675132de10e 100644
--- a/drivers/gpio/gpio-stmpe.c
+++ b/drivers/gpio/gpio-stmpe.c
@@ -68,6 +68,22 @@ static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
stmpe_reg_write(stmpe, reg, mask);
}
+static int stmpe_gpio_get_direction(struct gpio_chip *chip,
+ unsigned offset)
+{
+ struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+ int ret;
+
+ ret = stmpe_reg_read(stmpe, reg);
+ if (ret < 0)
+ return ret;
+
+ return !(ret & mask);
+}
+
static int stmpe_gpio_direction_output(struct gpio_chip *chip,
unsigned offset, int val)
{
@@ -106,6 +122,7 @@ static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset)
static struct gpio_chip template_chip = {
.label = "stmpe",
.owner = THIS_MODULE,
+ .get_direction = stmpe_gpio_get_direction,
.direction_input = stmpe_gpio_direction_input,
.get = stmpe_gpio_get,
.direction_output = stmpe_gpio_direction_output,
@@ -416,7 +433,6 @@ static struct platform_driver stmpe_gpio_driver = {
.driver = {
.suppress_bind_attrs = true,
.name = "stmpe-gpio",
- .owner = THIS_MODULE,
},
.probe = stmpe_gpio_probe,
};
diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c
index 24b6d643ecdb..537cec7583fc 100644
--- a/drivers/gpio/gpio-syscon.c
+++ b/drivers/gpio/gpio-syscon.c
@@ -129,7 +129,7 @@ static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val)
static const struct syscon_gpio_data clps711x_mctrl_gpio = {
/* ARM CLPS711X SYSFLG1 Bits 8-10 */
- .compatible = "cirrus,clps711x-syscon1",
+ .compatible = "cirrus,ep7209-syscon1",
.flags = GPIO_SYSCON_FEAT_IN,
.bit_count = 3,
.dat_bit_offset = 0x40 * 8 + 8,
@@ -168,7 +168,7 @@ static const struct syscon_gpio_data keystone_dsp_gpio = {
static const struct of_device_id syscon_gpio_ids[] = {
{
- .compatible = "cirrus,clps711x-mctrl-gpio",
+ .compatible = "cirrus,ep7209-mctrl-gpio",
.data = &clps711x_mctrl_gpio,
},
{
diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c
index 2e35ed3abbcf..8b3659352e49 100644
--- a/drivers/gpio/gpio-tc3589x.c
+++ b/drivers/gpio/gpio-tc3589x.c
@@ -343,7 +343,6 @@ static int tc3589x_gpio_probe(struct platform_device *pdev)
static struct platform_driver tc3589x_gpio_driver = {
.driver.name = "tc3589x-gpio",
- .driver.owner = THIS_MODULE,
.probe = tc3589x_gpio_probe,
};
diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c
index 0eaeac8de9de..1c09a19ae10c 100644
--- a/drivers/gpio/gpio-tps65218.c
+++ b/drivers/gpio/gpio-tps65218.c
@@ -230,6 +230,12 @@ static const struct of_device_id tps65218_dt_match[] = {
};
MODULE_DEVICE_TABLE(of, tps65218_dt_match);
+static const struct platform_device_id tps65218_gpio_id_table[] = {
+ { "tps65218-gpio", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, tps65218_gpio_id_table);
+
static struct platform_driver tps65218_gpio_driver = {
.driver = {
.name = "tps65218-gpio",
@@ -237,6 +243,7 @@ static struct platform_driver tps65218_gpio_driver = {
},
.probe = tps65218_gpio_probe,
.remove = tps65218_gpio_remove,
+ .id_table = tps65218_gpio_id_table,
};
module_platform_driver(tps65218_gpio_driver);
diff --git a/drivers/gpio/gpio-tps6586x.c b/drivers/gpio/gpio-tps6586x.c
index 6b15e68a314f..042b9a20781a 100644
--- a/drivers/gpio/gpio-tps6586x.c
+++ b/drivers/gpio/gpio-tps6586x.c
@@ -131,7 +131,6 @@ static int tps6586x_gpio_probe(struct platform_device *pdev)
static struct platform_driver tps6586x_gpio_driver = {
.driver.name = "tps6586x-gpio",
- .driver.owner = THIS_MODULE,
.probe = tps6586x_gpio_probe,
};
diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c
index 0ae6a5a54ea8..e63d7dabf78b 100644
--- a/drivers/gpio/gpio-tps65910.c
+++ b/drivers/gpio/gpio-tps65910.c
@@ -184,7 +184,6 @@ skip_init:
static struct platform_driver tps65910_gpio_driver = {
.driver.name = "tps65910-gpio",
- .driver.owner = THIS_MODULE,
.probe = tps65910_gpio_probe,
};
diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c
index dec47aafd5cd..e6d1328dddfa 100644
--- a/drivers/gpio/gpio-viperboard.c
+++ b/drivers/gpio/gpio-viperboard.c
@@ -440,7 +440,6 @@ static int vprbrd_gpio_probe(struct platform_device *pdev)
static struct platform_driver vprbrd_gpio_driver = {
.driver.name = "viperboard-gpio",
- .driver.owner = THIS_MODULE,
.probe = vprbrd_gpio_probe,
};
diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c
index 41ec7834059a..21f97bcd0062 100644
--- a/drivers/gpio/gpio-wm831x.c
+++ b/drivers/gpio/gpio-wm831x.c
@@ -296,7 +296,6 @@ static int wm831x_gpio_probe(struct platform_device *pdev)
static struct platform_driver wm831x_gpio_driver = {
.driver.name = "wm831x-gpio",
- .driver.owner = THIS_MODULE,
.probe = wm831x_gpio_probe,
};
diff --git a/drivers/gpio/gpio-wm8350.c b/drivers/gpio/gpio-wm8350.c
index 07d45a3b205a..e9765707d5c1 100644
--- a/drivers/gpio/gpio-wm8350.c
+++ b/drivers/gpio/gpio-wm8350.c
@@ -139,7 +139,6 @@ static int wm8350_gpio_probe(struct platform_device *pdev)
static struct platform_driver wm8350_gpio_driver = {
.driver.name = "wm8350-gpio",
- .driver.owner = THIS_MODULE,
.probe = wm8350_gpio_probe,
};
diff --git a/drivers/gpio/gpio-wm8994.c b/drivers/gpio/gpio-wm8994.c
index 744af388c949..2457aac8592e 100644
--- a/drivers/gpio/gpio-wm8994.c
+++ b/drivers/gpio/gpio-wm8994.c
@@ -299,7 +299,6 @@ static int wm8994_gpio_probe(struct platform_device *pdev)
static struct platform_driver wm8994_gpio_driver = {
.driver.name = "wm8994-gpio",
- .driver.owner = THIS_MODULE,
.probe = wm8994_gpio_probe,
};
diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c
index d0fbb7f99523..14b2a62338ea 100644
--- a/drivers/gpio/gpio-xilinx.c
+++ b/drivers/gpio/gpio-xilinx.c
@@ -133,6 +133,53 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
}
/**
+ * xgpio_set_multiple - Write the specified signals of the GPIO device.
+ * @gc: Pointer to gpio_chip device structure.
+ * @mask: Mask of the GPIOS to modify.
+ * @bits: Value to be wrote on each GPIO
+ *
+ * This function writes the specified values into the specified signals of the
+ * GPIO devices.
+ */
+static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
+ unsigned long *bits)
+{
+ unsigned long flags;
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct xgpio_instance *chip = gpiochip_get_data(gc);
+ int index = xgpio_index(chip, 0);
+ int offset, i;
+
+ spin_lock_irqsave(&chip->gpio_lock[index], flags);
+
+ /* Write to GPIO signals */
+ for (i = 0; i < gc->ngpio; i++) {
+ if (*mask == 0)
+ break;
+ if (index != xgpio_index(chip, i)) {
+ xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET +
+ xgpio_regoffset(chip, i),
+ chip->gpio_state[index]);
+ spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
+ index = xgpio_index(chip, i);
+ spin_lock_irqsave(&chip->gpio_lock[index], flags);
+ }
+ if (__test_and_clear_bit(i, mask)) {
+ offset = xgpio_offset(chip, i);
+ if (test_bit(i, bits))
+ chip->gpio_state[index] |= BIT(offset);
+ else
+ chip->gpio_state[index] &= ~BIT(offset);
+ }
+ }
+
+ xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET +
+ xgpio_regoffset(chip, i), chip->gpio_state[index]);
+
+ spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
+}
+
+/**
* xgpio_dir_in - Set the direction of the specified GPIO signal as input.
* @gc: Pointer to gpio_chip device structure.
* @gpio: GPIO signal number.
@@ -306,6 +353,7 @@ static int xgpio_probe(struct platform_device *pdev)
chip->mmchip.gc.direction_output = xgpio_dir_out;
chip->mmchip.gc.get = xgpio_get;
chip->mmchip.gc.set = xgpio_set;
+ chip->mmchip.gc.set_multiple = xgpio_set_multiple;
chip->mmchip.save_regs = xgpio_save_regs;
diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c
index 1a33a19d95b9..4620d050e5a8 100644
--- a/drivers/gpio/gpio-xlp.c
+++ b/drivers/gpio/gpio-xlp.c
@@ -19,6 +19,7 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/irqchip/chained_irq.h>
+#include <linux/acpi.h>
/*
* XLP GPIO has multiple 32 bit registers for each feature where each register
@@ -299,7 +300,6 @@ static int xlp_gpio_probe(struct platform_device *pdev)
struct gpio_chip *gc;
struct resource *iores;
struct xlp_gpio_priv *priv;
- const struct of_device_id *of_id;
void __iomem *gpio_base;
int irq_base, irq, err;
int ngpio;
@@ -321,13 +321,26 @@ static int xlp_gpio_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- of_id = of_match_device(xlp_gpio_of_ids, &pdev->dev);
- if (!of_id) {
- dev_err(&pdev->dev, "Failed to get soc type!\n");
- return -ENODEV;
- }
+ if (pdev->dev.of_node) {
+ const struct of_device_id *of_id;
- soc_type = (uintptr_t) of_id->data;
+ of_id = of_match_device(xlp_gpio_of_ids, &pdev->dev);
+ if (!of_id) {
+ dev_err(&pdev->dev, "Unable to match OF ID\n");
+ return -ENODEV;
+ }
+ soc_type = (uintptr_t) of_id->data;
+ } else {
+ const struct acpi_device_id *acpi_id;
+
+ acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table,
+ &pdev->dev);
+ if (!acpi_id || !acpi_id->driver_data) {
+ dev_err(&pdev->dev, "Unable to match ACPI ID\n");
+ return -ENODEV;
+ }
+ soc_type = (uintptr_t) acpi_id->driver_data;
+ }
switch (soc_type) {
case XLP_GPIO_VARIANT_XLP832:
@@ -388,14 +401,16 @@ static int xlp_gpio_probe(struct platform_device *pdev)
gc->get = xlp_gpio_get;
spin_lock_init(&priv->lock);
- /* XLP has fixed IRQ range for GPIO interrupts */
- if (soc_type == GPIO_VARIANT_VULCAN)
- irq_base = irq_alloc_descs(-1, 0, gc->ngpio, 0);
- else
+
+ /* XLP(MIPS) has fixed range for GPIO IRQs, Vulcan(ARM64) does not */
+ if (soc_type != GPIO_VARIANT_VULCAN) {
irq_base = irq_alloc_descs(-1, XLP_GPIO_IRQ_BASE, gc->ngpio, 0);
- if (irq_base < 0) {
- dev_err(&pdev->dev, "Failed to allocate IRQ numbers\n");
- return irq_base;
+ if (irq_base < 0) {
+ dev_err(&pdev->dev, "Failed to allocate IRQ numbers\n");
+ return irq_base;
+ }
+ } else {
+ irq_base = 0;
}
err = gpiochip_add_data(gc, priv);
@@ -423,10 +438,19 @@ out_free_desc:
return err;
}
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id xlp_gpio_acpi_match[] = {
+ { "BRCM9006", GPIO_VARIANT_VULCAN },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, xlp_gpio_acpi_match);
+#endif
+
static struct platform_driver xlp_gpio_driver = {
.driver = {
.name = "xlp-gpio",
.of_match_table = xlp_gpio_of_ids,
+ .acpi_match_table = ACPI_PTR(xlp_gpio_acpi_match),
},
.probe = xlp_gpio_probe,
};
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index 2dc52585e3f2..af514618d7fb 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -836,6 +836,7 @@ void acpi_gpiochip_add(struct gpio_chip *chip)
}
acpi_gpiochip_request_regions(acpi_gpio);
+ acpi_walk_dep_device_list(handle);
}
void acpi_gpiochip_remove(struct gpio_chip *chip)
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 4aabddb38b59..75e7b3919ea7 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -27,38 +27,30 @@
#include "gpiolib.h"
-/* Private data structure for of_gpiochip_find_and_xlate */
-struct gg_data {
- enum of_gpio_flags *flags;
- struct of_phandle_args gpiospec;
+static int of_gpiochip_match_node(struct gpio_chip *chip, void *data)
+{
+ return chip->gpiodev->dev.of_node == data;
+}
- struct gpio_desc *out_gpio;
-};
+static struct gpio_chip *of_find_gpiochip_by_node(struct device_node *np)
+{
+ return gpiochip_find(np, of_gpiochip_match_node);
+}
-/* Private function for resolving node pointer to gpio_chip */
-static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data)
+static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
+ struct of_phandle_args *gpiospec,
+ enum of_gpio_flags *flags)
{
- struct gg_data *gg_data = data;
int ret;
- if ((gc->of_node != gg_data->gpiospec.np) ||
- (gc->of_gpio_n_cells != gg_data->gpiospec.args_count) ||
- (!gc->of_xlate))
- return false;
-
- ret = gc->of_xlate(gc, &gg_data->gpiospec, gg_data->flags);
- if (ret < 0) {
- /* We've found a gpio chip, but the translation failed.
- * Store translation error in out_gpio.
- * Return false to keep looking, as more than one gpio chip
- * could be registered per of-node.
- */
- gg_data->out_gpio = ERR_PTR(ret);
- return false;
- }
-
- gg_data->out_gpio = gpiochip_get_desc(gc, ret);
- return true;
+ if (chip->of_gpio_n_cells != gpiospec->args_count)
+ return ERR_PTR(-EINVAL);
+
+ ret = chip->of_xlate(chip, gpiospec, flags);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ return gpiochip_get_desc(chip, ret);
}
/**
@@ -75,34 +67,37 @@ static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data)
struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
const char *propname, int index, enum of_gpio_flags *flags)
{
- /* Return -EPROBE_DEFER to support probe() functions to be called
- * later when the GPIO actually becomes available
- */
- struct gg_data gg_data = {
- .flags = flags,
- .out_gpio = ERR_PTR(-EPROBE_DEFER)
- };
+ struct of_phandle_args gpiospec;
+ struct gpio_chip *chip;
+ struct gpio_desc *desc;
int ret;
- /* .of_xlate might decide to not fill in the flags, so clear it. */
- if (flags)
- *flags = 0;
-
ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index,
- &gg_data.gpiospec);
+ &gpiospec);
if (ret) {
pr_debug("%s: can't parse '%s' property of node '%s[%d]'\n",
__func__, propname, np->full_name, index);
return ERR_PTR(ret);
}
- gpiochip_find(&gg_data, of_gpiochip_find_and_xlate);
+ chip = of_find_gpiochip_by_node(gpiospec.np);
+ if (!chip) {
+ desc = ERR_PTR(-EPROBE_DEFER);
+ goto out;
+ }
+
+ desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags);
+ if (IS_ERR(desc))
+ goto out;
- of_node_put(gg_data.gpiospec.np);
pr_debug("%s: parsed '%s' property of node '%s[%d]' - status (%d)\n",
__func__, propname, np->full_name, index,
- PTR_ERR_OR_ZERO(gg_data.out_gpio));
- return gg_data.out_gpio;
+ PTR_ERR_OR_ZERO(desc));
+
+out:
+ of_node_put(gpiospec.np);
+
+ return desc;
}
int of_get_named_gpio_flags(struct device_node *np, const char *list_name,
@@ -122,6 +117,7 @@ EXPORT_SYMBOL(of_get_named_gpio_flags);
/**
* of_parse_own_gpio() - Get a GPIO hog descriptor, names and flags for GPIO API
* @np: device node to get GPIO from
+ * @chip: GPIO chip whose hog is parsed
* @name: GPIO line name
* @lflags: gpio_lookup_flags - returned from of_find_gpio() or
* of_parse_own_gpio()
@@ -131,19 +127,19 @@ EXPORT_SYMBOL(of_get_named_gpio_flags);
* value on the error condition.
*/
static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
+ struct gpio_chip *chip,
const char **name,
enum gpio_lookup_flags *lflags,
enum gpiod_flags *dflags)
{
struct device_node *chip_np;
enum of_gpio_flags xlate_flags;
- struct gg_data gg_data = {
- .flags = &xlate_flags,
- };
+ struct of_phandle_args gpiospec;
+ struct gpio_desc *desc;
u32 tmp;
- int i, ret;
+ int ret;
- chip_np = np->parent;
+ chip_np = chip->of_node;
if (!chip_np)
return ERR_PTR(-EINVAL);
@@ -155,25 +151,16 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
if (ret)
return ERR_PTR(ret);
- if (tmp > MAX_PHANDLE_ARGS)
- return ERR_PTR(-EINVAL);
+ gpiospec.np = chip_np;
+ gpiospec.args_count = tmp;
- gg_data.gpiospec.args_count = tmp;
- gg_data.gpiospec.np = chip_np;
- for (i = 0; i < tmp; i++) {
- ret = of_property_read_u32_index(np, "gpios", i,
- &gg_data.gpiospec.args[i]);
- if (ret)
- return ERR_PTR(ret);
- }
+ ret = of_property_read_u32_array(np, "gpios", gpiospec.args, tmp);
+ if (ret)
+ return ERR_PTR(ret);
- gpiochip_find(&gg_data, of_gpiochip_find_and_xlate);
- if (!gg_data.out_gpio) {
- if (np->parent == np)
- return ERR_PTR(-ENXIO);
- else
- return ERR_PTR(-EINVAL);
- }
+ desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, &xlate_flags);
+ if (IS_ERR(desc))
+ return desc;
if (xlate_flags & OF_GPIO_ACTIVE_LOW)
*lflags |= GPIO_ACTIVE_LOW;
@@ -186,14 +173,14 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
*dflags |= GPIOD_OUT_HIGH;
else {
pr_warn("GPIO line %d (%s): no hogging state specified, bailing out\n",
- desc_to_gpio(gg_data.out_gpio), np->name);
+ desc_to_gpio(desc), np->name);
return ERR_PTR(-EINVAL);
}
if (name && of_property_read_string(np, "line-name", name))
*name = np->name;
- return gg_data.out_gpio;
+ return desc;
}
/**
@@ -262,7 +249,7 @@ static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
if (!of_property_read_bool(np, "gpio-hog"))
continue;
- desc = of_parse_own_gpio(np, &name, &lflags, &dflags);
+ desc = of_parse_own_gpio(np, chip, &name, &lflags, &dflags);
if (IS_ERR(desc))
continue;
@@ -410,6 +397,7 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip)
break;
pctldev = of_pinctrl_get(pinspec.np);
+ of_node_put(pinspec.np);
if (!pctldev)
return -EPROBE_DEFER;
@@ -487,6 +475,9 @@ int of_gpiochip_add(struct gpio_chip *chip)
chip->of_xlate = of_gpio_simple_xlate;
}
+ if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS)
+ return -EINVAL;
+
status = of_gpiochip_add_pin_range(chip);
if (status)
return status;
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index be74bd370f1f..53ff25ac66d8 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -16,11 +16,14 @@
#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 <linux/compat.h>
+#include <linux/anon_inodes.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/timekeeping.h>
#include <uapi/linux/gpio.h>
#include "gpiolib.h"
@@ -310,6 +313,497 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
return 0;
}
+/*
+ * GPIO line handle management
+ */
+
+/**
+ * struct linehandle_state - contains the state of a userspace handle
+ * @gdev: the GPIO device the handle pertains to
+ * @label: consumer label used to tag descriptors
+ * @descs: the GPIO descriptors held by this handle
+ * @numdescs: the number of descriptors held in the descs array
+ */
+struct linehandle_state {
+ struct gpio_device *gdev;
+ const char *label;
+ struct gpio_desc *descs[GPIOHANDLES_MAX];
+ u32 numdescs;
+};
+
+static long linehandle_ioctl(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ struct linehandle_state *lh = filep->private_data;
+ void __user *ip = (void __user *)arg;
+ struct gpiohandle_data ghd;
+ int i;
+
+ if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
+ int val;
+
+ /* TODO: check if descriptors are really input */
+ for (i = 0; i < lh->numdescs; i++) {
+ val = gpiod_get_value_cansleep(lh->descs[i]);
+ if (val < 0)
+ return val;
+ ghd.values[i] = val;
+ }
+
+ if (copy_to_user(ip, &ghd, sizeof(ghd)))
+ return -EFAULT;
+
+ return 0;
+ } else if (cmd == GPIOHANDLE_SET_LINE_VALUES_IOCTL) {
+ int vals[GPIOHANDLES_MAX];
+
+ /* TODO: check if descriptors are really output */
+ if (copy_from_user(&ghd, ip, sizeof(ghd)))
+ return -EFAULT;
+
+ /* Clamp all values to [0,1] */
+ for (i = 0; i < lh->numdescs; i++)
+ vals[i] = !!ghd.values[i];
+
+ /* Reuse the array setting function */
+ gpiod_set_array_value_complex(false,
+ true,
+ lh->numdescs,
+ lh->descs,
+ vals);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+#ifdef CONFIG_COMPAT
+static long linehandle_ioctl_compat(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ return linehandle_ioctl(filep, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static int linehandle_release(struct inode *inode, struct file *filep)
+{
+ struct linehandle_state *lh = filep->private_data;
+ struct gpio_device *gdev = lh->gdev;
+ int i;
+
+ for (i = 0; i < lh->numdescs; i++)
+ gpiod_free(lh->descs[i]);
+ kfree(lh->label);
+ kfree(lh);
+ put_device(&gdev->dev);
+ return 0;
+}
+
+static const struct file_operations linehandle_fileops = {
+ .release = linehandle_release,
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+ .unlocked_ioctl = linehandle_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = linehandle_ioctl_compat,
+#endif
+};
+
+static int linehandle_create(struct gpio_device *gdev, void __user *ip)
+{
+ struct gpiohandle_request handlereq;
+ struct linehandle_state *lh;
+ int fd, i, ret;
+
+ if (copy_from_user(&handlereq, ip, sizeof(handlereq)))
+ return -EFAULT;
+ if ((handlereq.lines == 0) || (handlereq.lines > GPIOHANDLES_MAX))
+ return -EINVAL;
+
+ lh = kzalloc(sizeof(*lh), GFP_KERNEL);
+ if (!lh)
+ return -ENOMEM;
+ lh->gdev = gdev;
+ get_device(&gdev->dev);
+
+ /* Make sure this is terminated */
+ handlereq.consumer_label[sizeof(handlereq.consumer_label)-1] = '\0';
+ if (strlen(handlereq.consumer_label)) {
+ lh->label = kstrdup(handlereq.consumer_label,
+ GFP_KERNEL);
+ if (!lh->label) {
+ ret = -ENOMEM;
+ goto out_free_lh;
+ }
+ }
+
+ /* Request each GPIO */
+ for (i = 0; i < handlereq.lines; i++) {
+ u32 offset = handlereq.lineoffsets[i];
+ u32 lflags = handlereq.flags;
+ struct gpio_desc *desc;
+
+ desc = &gdev->descs[offset];
+ ret = gpiod_request(desc, lh->label);
+ if (ret)
+ goto out_free_descs;
+ lh->descs[i] = desc;
+
+ if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW)
+ set_bit(FLAG_ACTIVE_LOW, &desc->flags);
+ if (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN)
+ set_bit(FLAG_OPEN_DRAIN, &desc->flags);
+ if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)
+ set_bit(FLAG_OPEN_SOURCE, &desc->flags);
+
+ /*
+ * Lines have to be requested explicitly for input
+ * or output, else the line will be treated "as is".
+ */
+ if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
+ int val = !!handlereq.default_values[i];
+
+ ret = gpiod_direction_output(desc, val);
+ if (ret)
+ goto out_free_descs;
+ } else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
+ ret = gpiod_direction_input(desc);
+ if (ret)
+ goto out_free_descs;
+ }
+ dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
+ offset);
+ }
+ /* Let i point at the last handle */
+ i--;
+ lh->numdescs = handlereq.lines;
+
+ fd = anon_inode_getfd("gpio-linehandle",
+ &linehandle_fileops,
+ lh,
+ O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ ret = fd;
+ goto out_free_descs;
+ }
+
+ handlereq.fd = fd;
+ if (copy_to_user(ip, &handlereq, sizeof(handlereq))) {
+ ret = -EFAULT;
+ goto out_free_descs;
+ }
+
+ dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
+ lh->numdescs);
+
+ return 0;
+
+out_free_descs:
+ for (; i >= 0; i--)
+ gpiod_free(lh->descs[i]);
+ kfree(lh->label);
+out_free_lh:
+ kfree(lh);
+ put_device(&gdev->dev);
+ return ret;
+}
+
+/*
+ * GPIO line event management
+ */
+
+/**
+ * struct lineevent_state - contains the state of a userspace event
+ * @gdev: the GPIO device the event pertains to
+ * @label: consumer label used to tag descriptors
+ * @desc: the GPIO descriptor held by this event
+ * @eflags: the event flags this line was requested with
+ * @irq: the interrupt that trigger in response to events on this GPIO
+ * @wait: wait queue that handles blocking reads of events
+ * @events: KFIFO for the GPIO events
+ * @read_lock: mutex lock to protect reads from colliding with adding
+ * new events to the FIFO
+ */
+struct lineevent_state {
+ struct gpio_device *gdev;
+ const char *label;
+ struct gpio_desc *desc;
+ u32 eflags;
+ int irq;
+ wait_queue_head_t wait;
+ DECLARE_KFIFO(events, struct gpioevent_data, 16);
+ struct mutex read_lock;
+};
+
+static unsigned int lineevent_poll(struct file *filep,
+ struct poll_table_struct *wait)
+{
+ struct lineevent_state *le = filep->private_data;
+ unsigned int events = 0;
+
+ poll_wait(filep, &le->wait, wait);
+
+ if (!kfifo_is_empty(&le->events))
+ events = POLLIN | POLLRDNORM;
+
+ return events;
+}
+
+
+static ssize_t lineevent_read(struct file *filep,
+ char __user *buf,
+ size_t count,
+ loff_t *f_ps)
+{
+ struct lineevent_state *le = filep->private_data;
+ unsigned int copied;
+ int ret;
+
+ if (count < sizeof(struct gpioevent_data))
+ return -EINVAL;
+
+ do {
+ if (kfifo_is_empty(&le->events)) {
+ if (filep->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ ret = wait_event_interruptible(le->wait,
+ !kfifo_is_empty(&le->events));
+ if (ret)
+ return ret;
+ }
+
+ if (mutex_lock_interruptible(&le->read_lock))
+ return -ERESTARTSYS;
+ ret = kfifo_to_user(&le->events, buf, count, &copied);
+ mutex_unlock(&le->read_lock);
+
+ if (ret)
+ return ret;
+
+ /*
+ * If we couldn't read anything from the fifo (a different
+ * thread might have been faster) we either return -EAGAIN if
+ * the file descriptor is non-blocking, otherwise we go back to
+ * sleep and wait for more data to arrive.
+ */
+ if (copied == 0 && (filep->f_flags & O_NONBLOCK))
+ return -EAGAIN;
+
+ } while (copied == 0);
+
+ return copied;
+}
+
+static int lineevent_release(struct inode *inode, struct file *filep)
+{
+ struct lineevent_state *le = filep->private_data;
+ struct gpio_device *gdev = le->gdev;
+
+ free_irq(le->irq, le);
+ gpiod_free(le->desc);
+ kfree(le->label);
+ kfree(le);
+ put_device(&gdev->dev);
+ return 0;
+}
+
+static long lineevent_ioctl(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ struct lineevent_state *le = filep->private_data;
+ void __user *ip = (void __user *)arg;
+ struct gpiohandle_data ghd;
+
+ /*
+ * We can get the value for an event line but not set it,
+ * because it is input by definition.
+ */
+ if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
+ int val;
+
+ val = gpiod_get_value_cansleep(le->desc);
+ if (val < 0)
+ return val;
+ ghd.values[0] = val;
+
+ if (copy_to_user(ip, &ghd, sizeof(ghd)))
+ return -EFAULT;
+
+ return 0;
+ }
+ return -EINVAL;
+}
+
+#ifdef CONFIG_COMPAT
+static long lineevent_ioctl_compat(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ return lineevent_ioctl(filep, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static const struct file_operations lineevent_fileops = {
+ .release = lineevent_release,
+ .read = lineevent_read,
+ .poll = lineevent_poll,
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+ .unlocked_ioctl = lineevent_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = lineevent_ioctl_compat,
+#endif
+};
+
+static irqreturn_t lineevent_irq_thread(int irq, void *p)
+{
+ struct lineevent_state *le = p;
+ struct gpioevent_data ge;
+ int ret;
+
+ ge.timestamp = ktime_get_real_ns();
+
+ if (le->eflags & GPIOEVENT_REQUEST_BOTH_EDGES) {
+ int level = gpiod_get_value_cansleep(le->desc);
+
+ if (level)
+ /* Emit low-to-high event */
+ ge.id = GPIOEVENT_EVENT_RISING_EDGE;
+ else
+ /* Emit high-to-low event */
+ ge.id = GPIOEVENT_EVENT_FALLING_EDGE;
+ } else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE) {
+ /* Emit low-to-high event */
+ ge.id = GPIOEVENT_EVENT_RISING_EDGE;
+ } else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) {
+ /* Emit high-to-low event */
+ ge.id = GPIOEVENT_EVENT_FALLING_EDGE;
+ } else {
+ return IRQ_NONE;
+ }
+
+ ret = kfifo_put(&le->events, ge);
+ if (ret != 0)
+ wake_up_poll(&le->wait, POLLIN);
+
+ return IRQ_HANDLED;
+}
+
+static int lineevent_create(struct gpio_device *gdev, void __user *ip)
+{
+ struct gpioevent_request eventreq;
+ struct lineevent_state *le;
+ struct gpio_desc *desc;
+ u32 offset;
+ u32 lflags;
+ u32 eflags;
+ int fd;
+ int ret;
+ int irqflags = 0;
+
+ if (copy_from_user(&eventreq, ip, sizeof(eventreq)))
+ return -EFAULT;
+
+ le = kzalloc(sizeof(*le), GFP_KERNEL);
+ if (!le)
+ return -ENOMEM;
+ le->gdev = gdev;
+ get_device(&gdev->dev);
+
+ /* Make sure this is terminated */
+ eventreq.consumer_label[sizeof(eventreq.consumer_label)-1] = '\0';
+ if (strlen(eventreq.consumer_label)) {
+ le->label = kstrdup(eventreq.consumer_label,
+ GFP_KERNEL);
+ if (!le->label) {
+ ret = -ENOMEM;
+ goto out_free_le;
+ }
+ }
+
+ offset = eventreq.lineoffset;
+ lflags = eventreq.handleflags;
+ eflags = eventreq.eventflags;
+
+ /* This is just wrong: we don't look for events on output lines */
+ if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
+ ret = -EINVAL;
+ goto out_free_label;
+ }
+
+ desc = &gdev->descs[offset];
+ ret = gpiod_request(desc, le->label);
+ if (ret)
+ goto out_free_desc;
+ le->desc = desc;
+ le->eflags = eflags;
+
+ if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW)
+ set_bit(FLAG_ACTIVE_LOW, &desc->flags);
+ if (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN)
+ set_bit(FLAG_OPEN_DRAIN, &desc->flags);
+ if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)
+ set_bit(FLAG_OPEN_SOURCE, &desc->flags);
+
+ ret = gpiod_direction_input(desc);
+ if (ret)
+ goto out_free_desc;
+
+ le->irq = gpiod_to_irq(desc);
+ if (le->irq <= 0) {
+ ret = -ENODEV;
+ goto out_free_desc;
+ }
+
+ if (eflags & GPIOEVENT_REQUEST_RISING_EDGE)
+ irqflags |= IRQF_TRIGGER_RISING;
+ if (eflags & GPIOEVENT_REQUEST_FALLING_EDGE)
+ irqflags |= IRQF_TRIGGER_FALLING;
+ irqflags |= IRQF_ONESHOT;
+ irqflags |= IRQF_SHARED;
+
+ INIT_KFIFO(le->events);
+ init_waitqueue_head(&le->wait);
+ mutex_init(&le->read_lock);
+
+ /* Request a thread to read the events */
+ ret = request_threaded_irq(le->irq,
+ NULL,
+ lineevent_irq_thread,
+ irqflags,
+ le->label,
+ le);
+ if (ret)
+ goto out_free_desc;
+
+ fd = anon_inode_getfd("gpio-event",
+ &lineevent_fileops,
+ le,
+ O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ ret = fd;
+ goto out_free_irq;
+ }
+
+ eventreq.fd = fd;
+ if (copy_to_user(ip, &eventreq, sizeof(eventreq))) {
+ ret = -EFAULT;
+ goto out_free_irq;
+ }
+
+ return 0;
+
+out_free_irq:
+ free_irq(le->irq, le);
+out_free_desc:
+ gpiod_free(le->desc);
+out_free_label:
+ kfree(le->label);
+out_free_le:
+ kfree(le);
+ put_device(&gdev->dev);
+ return ret;
+}
+
/**
* gpio_ioctl() - ioctl handler for the GPIO chardev
*/
@@ -385,6 +879,10 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (copy_to_user(ip, &lineinfo, sizeof(lineinfo)))
return -EFAULT;
return 0;
+ } else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) {
+ return linehandle_create(gdev, ip);
+ } else if (cmd == GPIO_GET_LINEEVENT_IOCTL) {
+ return lineevent_create(gdev, ip);
}
return -EINVAL;
}
@@ -548,13 +1046,14 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
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;
+ 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;
@@ -2333,7 +2832,7 @@ static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx,
&of_flags);
- if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER))
+ if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT))
break;
}
diff --git a/drivers/pinctrl/pinctrl-xway.c b/drivers/pinctrl/pinctrl-xway.c
index a13f2b6f6fc0..b9375544dff0 100644
--- a/drivers/pinctrl/pinctrl-xway.c
+++ b/drivers/pinctrl/pinctrl-xway.c
@@ -1724,9 +1724,9 @@ static int pinmux_xway_probe(struct platform_device *pdev)
}
xway_pctrl_desc.pins = xway_info.pads;
- /* load the gpio chip */
+ /* register the gpio chip */
xway_chip.parent = &pdev->dev;
- ret = gpiochip_add(&xway_chip);
+ ret = devm_gpiochip_add_data(&pdev->dev, &xway_chip, NULL);
if (ret) {
dev_err(&pdev->dev, "Failed to register gpio chip\n");
return ret;
@@ -1749,7 +1749,6 @@ static int pinmux_xway_probe(struct platform_device *pdev)
/* register with the generic lantiq layer */
ret = ltq_pinctrl_register(pdev, &xway_info);
if (ret) {
- gpiochip_remove(&xway_chip);
dev_err(&pdev->dev, "Failed to register pinctrl driver\n");
return ret;
}
diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h
index d0a3cac72250..333d3544c964 100644
--- a/include/uapi/linux/gpio.h
+++ b/include/uapi/linux/gpio.h
@@ -1,7 +1,7 @@
/*
* <linux/gpio.h> - userspace ABI for the GPIO character devices
*
- * Copyright (C) 2015 Linus Walleij
+ * Copyright (C) 2016 Linus Walleij
*
* 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
@@ -26,8 +26,8 @@ struct gpiochip_info {
__u32 lines;
};
-/* Line is in use by the kernel */
-#define GPIOLINE_FLAG_KERNEL (1UL << 0)
+/* Informational flags */
+#define GPIOLINE_FLAG_KERNEL (1UL << 0) /* Line used by the kernel */
#define GPIOLINE_FLAG_IS_OUT (1UL << 1)
#define GPIOLINE_FLAG_ACTIVE_LOW (1UL << 2)
#define GPIOLINE_FLAG_OPEN_DRAIN (1UL << 3)
@@ -52,7 +52,106 @@ struct gpioline_info {
char consumer[32];
};
+/* Maximum number of requested handles */
+#define GPIOHANDLES_MAX 64
+
+/* Linerequest flags */
+#define GPIOHANDLE_REQUEST_INPUT (1UL << 0)
+#define GPIOHANDLE_REQUEST_OUTPUT (1UL << 1)
+#define GPIOHANDLE_REQUEST_ACTIVE_LOW (1UL << 2)
+#define GPIOHANDLE_REQUEST_OPEN_DRAIN (1UL << 3)
+#define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4)
+
+/**
+ * struct gpiohandle_request - Information about a GPIO handle request
+ * @lineoffsets: an array desired lines, specified by offset index for the
+ * associated GPIO device
+ * @flags: desired flags for the desired GPIO lines, such as
+ * GPIOHANDLE_REQUEST_OUTPUT, GPIOHANDLE_REQUEST_ACTIVE_LOW etc, OR:ed
+ * together. Note that even if multiple lines are requested, the same flags
+ * must be applicable to all of them, if you want lines with individual
+ * flags set, request them one by one. It is possible to select
+ * a batch of input or output lines, but they must all have the same
+ * characteristics, i.e. all inputs or all outputs, all active low etc
+ * @default_values: if the GPIOHANDLE_REQUEST_OUTPUT is set for a requested
+ * line, this specifies the default output value, should be 0 (low) or
+ * 1 (high), anything else than 0 or 1 will be interpreted as 1 (high)
+ * @consumer_label: a desired consumer label for the selected GPIO line(s)
+ * such as "my-bitbanged-relay"
+ * @lines: number of lines requested in this request, i.e. the number of
+ * valid fields in the above arrays, set to 1 to request a single line
+ * @fd: if successful this field will contain a valid anonymous file handle
+ * after a GPIO_GET_LINEHANDLE_IOCTL operation, zero or negative value
+ * means error
+ */
+struct gpiohandle_request {
+ __u32 lineoffsets[GPIOHANDLES_MAX];
+ __u32 flags;
+ __u8 default_values[GPIOHANDLES_MAX];
+ char consumer_label[32];
+ __u32 lines;
+ int fd;
+};
+
+/**
+ * struct gpiohandle_data - Information of values on a GPIO handle
+ * @values: when getting the state of lines this contains the current
+ * state of a line, when setting the state of lines these should contain
+ * the desired target state
+ */
+struct gpiohandle_data {
+ __u8 values[GPIOHANDLES_MAX];
+};
+
+#define GPIOHANDLE_GET_LINE_VALUES_IOCTL _IOWR(0xB4, 0x08, struct gpiohandle_data)
+#define GPIOHANDLE_SET_LINE_VALUES_IOCTL _IOWR(0xB4, 0x09, struct gpiohandle_data)
+
+/* Eventrequest flags */
+#define GPIOEVENT_REQUEST_RISING_EDGE (1UL << 0)
+#define GPIOEVENT_REQUEST_FALLING_EDGE (1UL << 1)
+#define GPIOEVENT_REQUEST_BOTH_EDGES ((1UL << 0) | (1UL << 1))
+
+/**
+ * struct gpioevent_request - Information about a GPIO event request
+ * @lineoffset: the desired line to subscribe to events from, specified by
+ * offset index for the associated GPIO device
+ * @handleflags: desired handle flags for the desired GPIO line, such as
+ * GPIOHANDLE_REQUEST_ACTIVE_LOW or GPIOHANDLE_REQUEST_OPEN_DRAIN
+ * @eventflags: desired flags for the desired GPIO event line, such as
+ * GPIOEVENT_REQUEST_RISING_EDGE or GPIOEVENT_REQUEST_FALLING_EDGE
+ * @consumer_label: a desired consumer label for the selected GPIO line(s)
+ * such as "my-listener"
+ * @fd: if successful this field will contain a valid anonymous file handle
+ * after a GPIO_GET_LINEEVENT_IOCTL operation, zero or negative value
+ * means error
+ */
+struct gpioevent_request {
+ __u32 lineoffset;
+ __u32 handleflags;
+ __u32 eventflags;
+ char consumer_label[32];
+ int fd;
+};
+
+/**
+ * GPIO event types
+ */
+#define GPIOEVENT_EVENT_RISING_EDGE 0x01
+#define GPIOEVENT_EVENT_FALLING_EDGE 0x02
+
+/**
+ * struct gpioevent_data - The actual event being pushed to userspace
+ * @timestamp: best estimate of time of event occurrence, in nanoseconds
+ * @id: event identifier
+ */
+struct gpioevent_data {
+ __u64 timestamp;
+ __u32 id;
+};
+
#define GPIO_GET_CHIPINFO_IOCTL _IOR(0xB4, 0x01, struct gpiochip_info)
#define GPIO_GET_LINEINFO_IOCTL _IOWR(0xB4, 0x02, struct gpioline_info)
+#define GPIO_GET_LINEHANDLE_IOCTL _IOWR(0xB4, 0x03, struct gpiohandle_request)
+#define GPIO_GET_LINEEVENT_IOCTL _IOWR(0xB4, 0x04, struct gpioevent_request)
#endif /* _UAPI_GPIO_H_ */
diff --git a/tools/Makefile b/tools/Makefile
index f10b64d8c674..daa8fb3e4363 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -85,7 +85,7 @@ tmon: FORCE
freefall: FORCE
$(call descend,laptop/$@)
-all: acpi cgroup cpupower hv firewire lguest \
+all: acpi cgroup cpupower gpio hv firewire lguest \
perf selftests turbostat usb \
virtio vm net x86_energy_perf_policy \
tmon freefall objtool
@@ -96,7 +96,7 @@ acpi_install:
cpupower_install:
$(call descend,power/$(@:_install=),install)
-cgroup_install firewire_install hv_install lguest_install perf_install usb_install virtio_install vm_install net_install objtool_install:
+cgroup_install firewire_install gpio_install hv_install lguest_install perf_install usb_install virtio_install vm_install net_install objtool_install:
$(call descend,$(@:_install=),install)
selftests_install:
@@ -114,7 +114,8 @@ freefall_install:
kvm_stat_install:
$(call descend,kvm/$(@:_install=),install)
-install: acpi_install cgroup_install cpupower_install hv_install firewire_install lguest_install \
+install: acpi_install cgroup_install cpupower_install gpio_install \
+ hv_install firewire_install lguest_install \
perf_install selftests_install turbostat_install usb_install \
virtio_install vm_install net_install x86_energy_perf_policy_install \
tmon_install freefall_install objtool_install kvm_stat_install
diff --git a/tools/gpio/Build b/tools/gpio/Build
new file mode 100644
index 000000000000..620c1937d957
--- /dev/null
+++ b/tools/gpio/Build
@@ -0,0 +1,3 @@
+lsgpio-y += lsgpio.o gpio-utils.o
+gpio-hammer-y += gpio-hammer.o gpio-utils.o
+gpio-event-mon-y += gpio-event-mon.o gpio-utils.o
diff --git a/tools/gpio/Makefile b/tools/gpio/Makefile
index c155d6bc47a7..250a891e6ef0 100644
--- a/tools/gpio/Makefile
+++ b/tools/gpio/Makefile
@@ -1,12 +1,75 @@
+include ../scripts/Makefile.include
+
+bindir ?= /usr/bin
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(shell pwd)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+endif
+
+# Do not use make's built-in rules
+# (this improves performance and avoids hard-to-debug behaviour);
+MAKEFLAGS += -r
+
CC = $(CROSS_COMPILE)gcc
-CFLAGS += -O2 -Wall -g -D_GNU_SOURCE
+LD = $(CROSS_COMPILE)ld
+CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
+
+ALL_TARGETS := lsgpio gpio-hammer gpio-event-mon
+ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
+
+all: $(ALL_PROGRAMS)
-all: lsgpio
+export srctree OUTPUT CC LD CFLAGS
+include $(srctree)/tools/build/Makefile.include
-lsgpio: lsgpio.o gpio-utils.o
+#
+# We need the following to be outside of kernel tree
+#
+$(OUTPUT)include/linux/gpio.h: ../../include/uapi/linux/gpio.h
+ mkdir -p $(OUTPUT)include/linux 2>&1 || true
+ ln -sf $(CURDIR)/../../include/uapi/linux/gpio.h $@
-%.o: %.c gpio-utils.h
+prepare: $(OUTPUT)include/linux/gpio.h
+
+#
+# lsgpio
+#
+LSGPIO_IN := $(OUTPUT)lsgpio-in.o
+$(LSGPIO_IN): prepare FORCE
+ $(Q)$(MAKE) $(build)=lsgpio
+$(OUTPUT)lsgpio: $(LSGPIO_IN)
+ $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
+
+#
+# gpio-hammer
+#
+GPIO_HAMMER_IN := $(OUTPUT)gpio-hammer-in.o
+$(GPIO_HAMMER_IN): prepare FORCE
+ $(Q)$(MAKE) $(build)=gpio-hammer
+$(OUTPUT)gpio-hammer: $(GPIO_HAMMER_IN)
+ $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
+
+#
+# gpio-event-mon
+#
+GPIO_EVENT_MON_IN := $(OUTPUT)gpio-event-mon-in.o
+$(GPIO_EVENT_MON_IN): prepare FORCE
+ $(Q)$(MAKE) $(build)=gpio-event-mon
+$(OUTPUT)gpio-event-mon: $(GPIO_EVENT_MON_IN)
+ $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
-.PHONY: clean
clean:
- rm -f *.o lsgpio
+ rm -f $(ALL_PROGRAMS)
+ rm -f $(OUTPUT)include/linux/gpio.h
+ find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete
+
+install: $(ALL_PROGRAMS)
+ install -d -m 755 $(DESTDIR)$(bindir); \
+ for program in $(ALL_PROGRAMS); do \
+ install $$program $(DESTDIR)$(bindir); \
+ done
+
+FORCE:
+
+.PHONY: all install clean FORCE prepare
diff --git a/tools/gpio/gpio-event-mon.c b/tools/gpio/gpio-event-mon.c
new file mode 100644
index 000000000000..448ed96b3b4f
--- /dev/null
+++ b/tools/gpio/gpio-event-mon.c
@@ -0,0 +1,192 @@
+/*
+ * gpio-hammer - example swiss army knife to shake GPIO lines on a system
+ *
+ * Copyright (C) 2016 Linus Walleij
+ *
+ * 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.
+ *
+ * Usage:
+ * gpio-event-mon -n <device-name> -o <offset>
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <sys/ioctl.h>
+#include <linux/gpio.h>
+
+int monitor_device(const char *device_name,
+ unsigned int line,
+ u_int32_t handleflags,
+ u_int32_t eventflags,
+ unsigned int loops)
+{
+ struct gpioevent_request req;
+ struct gpiohandle_data data;
+ char *chrdev_name;
+ int fd;
+ int ret;
+ int i = 0;
+
+ ret = asprintf(&chrdev_name, "/dev/%s", device_name);
+ if (ret < 0)
+ return -ENOMEM;
+
+ fd = open(chrdev_name, 0);
+ if (fd == -1) {
+ ret = -errno;
+ fprintf(stderr, "Failed to open %s\n", chrdev_name);
+ goto exit_close_error;
+ }
+
+ req.lineoffset = line;
+ req.handleflags = handleflags;
+ req.eventflags = eventflags;
+ strcpy(req.consumer_label, "gpio-event-mon");
+
+ ret = ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &req);
+ if (ret == -1) {
+ ret = -errno;
+ fprintf(stderr, "Failed to issue GET EVENT "
+ "IOCTL (%d)\n",
+ ret);
+ goto exit_close_error;
+ }
+
+ /* Read initial states */
+ ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
+ if (ret == -1) {
+ ret = -errno;
+ fprintf(stderr, "Failed to issue GPIOHANDLE GET LINE "
+ "VALUES IOCTL (%d)\n",
+ ret);
+ goto exit_close_error;
+ }
+
+ fprintf(stdout, "Monitoring line %d on %s\n", line, device_name);
+ fprintf(stdout, "Initial line value: %d\n", data.values[0]);
+
+ while (1) {
+ struct gpioevent_data event;
+
+ ret = read(req.fd, &event, sizeof(event));
+ if (ret == -1) {
+ if (errno == -EAGAIN) {
+ fprintf(stderr, "nothing available\n");
+ continue;
+ } else {
+ ret = -errno;
+ fprintf(stderr, "Failed to read event (%d)\n",
+ ret);
+ break;
+ }
+ }
+
+ if (ret != sizeof(event)) {
+ fprintf(stderr, "Reading event failed\n");
+ ret = -EIO;
+ break;
+ }
+ fprintf(stdout, "GPIO EVENT %" PRIu64 ": ", event.timestamp);
+ switch (event.id) {
+ case GPIOEVENT_EVENT_RISING_EDGE:
+ fprintf(stdout, "rising edge");
+ break;
+ case GPIOEVENT_EVENT_FALLING_EDGE:
+ fprintf(stdout, "falling edge");
+ break;
+ default:
+ fprintf(stdout, "unknown event");
+ }
+ fprintf(stdout, "\n");
+
+ i++;
+ if (i == loops)
+ break;
+ }
+
+exit_close_error:
+ if (close(fd) == -1)
+ perror("Failed to close GPIO character device file");
+ free(chrdev_name);
+ return ret;
+}
+
+void print_usage(void)
+{
+ fprintf(stderr, "Usage: gpio-event-mon [options]...\n"
+ "Listen to events on GPIO lines, 0->1 1->0\n"
+ " -n <name> Listen on GPIOs on a named device (must be stated)\n"
+ " -o <n> Offset to monitor\n"
+ " -d Set line as open drain\n"
+ " -s Set line as open source\n"
+ " -r Listen for rising edges\n"
+ " -f Listen for falling edges\n"
+ " [-c <n>] Do <n> loops (optional, infinite loop if not stated)\n"
+ " -? This helptext\n"
+ "\n"
+ "Example:\n"
+ "gpio-event-mon -n gpiochip0 -o 4 -r -f\n"
+ );
+}
+
+int main(int argc, char **argv)
+{
+ const char *device_name = NULL;
+ unsigned int line = -1;
+ unsigned int loops = 0;
+ u_int32_t handleflags = GPIOHANDLE_REQUEST_INPUT;
+ u_int32_t eventflags = 0;
+ int c;
+
+ while ((c = getopt(argc, argv, "c:n:o:dsrf?")) != -1) {
+ switch (c) {
+ case 'c':
+ loops = strtoul(optarg, NULL, 10);
+ break;
+ case 'n':
+ device_name = optarg;
+ break;
+ case 'o':
+ line = strtoul(optarg, NULL, 10);
+ break;
+ case 'd':
+ handleflags |= GPIOHANDLE_REQUEST_OPEN_DRAIN;
+ break;
+ case 's':
+ handleflags |= GPIOHANDLE_REQUEST_OPEN_SOURCE;
+ break;
+ case 'r':
+ eventflags |= GPIOEVENT_REQUEST_RISING_EDGE;
+ break;
+ case 'f':
+ eventflags |= GPIOEVENT_REQUEST_FALLING_EDGE;
+ break;
+ case '?':
+ print_usage();
+ return -1;
+ }
+ }
+
+ if (!device_name || line == -1) {
+ print_usage();
+ return -1;
+ }
+ if (!eventflags) {
+ printf("No flags specified, listening on both rising and "
+ "falling edges\n");
+ eventflags = GPIOEVENT_REQUEST_BOTH_EDGES;
+ }
+ return monitor_device(device_name, line, handleflags,
+ eventflags, loops);
+}
diff --git a/tools/gpio/gpio-hammer.c b/tools/gpio/gpio-hammer.c
new file mode 100644
index 000000000000..37b3f141053d
--- /dev/null
+++ b/tools/gpio/gpio-hammer.c
@@ -0,0 +1,189 @@
+/*
+ * gpio-hammer - example swiss army knife to shake GPIO lines on a system
+ *
+ * Copyright (C) 2016 Linus Walleij
+ *
+ * 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.
+ *
+ * Usage:
+ * gpio-hammer -n <device-name> -o <offset1> -o <offset2>
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <linux/gpio.h>
+
+int hammer_device(const char *device_name, unsigned int *lines, int nlines,
+ unsigned int loops)
+{
+ struct gpiohandle_request req;
+ struct gpiohandle_data data;
+ char *chrdev_name;
+ char swirr[] = "-\\|/";
+ int fd;
+ int ret;
+ int i, j;
+ unsigned int iteration = 0;
+
+ ret = asprintf(&chrdev_name, "/dev/%s", device_name);
+ if (ret < 0)
+ return -ENOMEM;
+
+ fd = open(chrdev_name, 0);
+ if (fd == -1) {
+ ret = -errno;
+ fprintf(stderr, "Failed to open %s\n", chrdev_name);
+ goto exit_close_error;
+ }
+
+ /* Request lines as output */
+ for (i = 0; i < nlines; i++)
+ req.lineoffsets[i] = lines[i];
+ req.flags = GPIOHANDLE_REQUEST_OUTPUT; /* Request as output */
+ strcpy(req.consumer_label, "gpio-hammer");
+ req.lines = nlines;
+ ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
+ if (ret == -1) {
+ ret = -errno;
+ fprintf(stderr, "Failed to issue GET LINEHANDLE "
+ "IOCTL (%d)\n",
+ ret);
+ goto exit_close_error;
+ }
+
+ /* Read initial states */
+ ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
+ if (ret == -1) {
+ ret = -errno;
+ fprintf(stderr, "Failed to issue GPIOHANDLE GET LINE "
+ "VALUES IOCTL (%d)\n",
+ ret);
+ goto exit_close_error;
+ }
+ fprintf(stdout, "Hammer lines [");
+ for (i = 0; i < nlines; i++) {
+ fprintf(stdout, "%d", lines[i]);
+ if (i != (nlines - 1))
+ fprintf(stdout, ", ");
+ }
+ fprintf(stdout, "] on %s, initial states: [", device_name);
+ for (i = 0; i < nlines; i++) {
+ fprintf(stdout, "%d", data.values[i]);
+ if (i != (nlines - 1))
+ fprintf(stdout, ", ");
+ }
+ fprintf(stdout, "]\n");
+
+ /* Hammertime! */
+ j = 0;
+ while (1) {
+ /* Invert all lines so we blink */
+ for (i = 0; i < nlines; i++)
+ data.values[i] = !data.values[i];
+
+ ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
+ if (ret == -1) {
+ ret = -errno;
+ fprintf(stderr, "Failed to issue GPIOHANDLE SET LINE "
+ "VALUES IOCTL (%d)\n",
+ ret);
+ goto exit_close_error;
+ }
+ /* Re-read values to get status */
+ ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
+ if (ret == -1) {
+ ret = -errno;
+ fprintf(stderr, "Failed to issue GPIOHANDLE GET LINE "
+ "VALUES IOCTL (%d)\n",
+ ret);
+ goto exit_close_error;
+ }
+
+ fprintf(stdout, "[%c] ", swirr[j]);
+ j++;
+ if (j == sizeof(swirr)-1)
+ j = 0;
+
+ fprintf(stdout, "[");
+ for (i = 0; i < nlines; i++) {
+ fprintf(stdout, "%d: %d", lines[i], data.values[i]);
+ if (i != (nlines - 1))
+ fprintf(stdout, ", ");
+ }
+ fprintf(stdout, "]\r");
+ fflush(stdout);
+ sleep(1);
+ iteration++;
+ if (loops && iteration == loops)
+ break;
+ }
+ fprintf(stdout, "\n");
+ ret = 0;
+
+exit_close_error:
+ if (close(fd) == -1)
+ perror("Failed to close GPIO character device file");
+ free(chrdev_name);
+ return ret;
+}
+
+void print_usage(void)
+{
+ fprintf(stderr, "Usage: gpio-hammer [options]...\n"
+ "Hammer GPIO lines, 0->1->0->1...\n"
+ " -n <name> Hammer GPIOs on a named device (must be stated)\n"
+ " -o <n> Offset[s] to hammer, at least one, several can be stated\n"
+ " [-c <n>] Do <n> loops (optional, infinite loop if not stated)\n"
+ " -? This helptext\n"
+ "\n"
+ "Example:\n"
+ "gpio-hammer -n gpiochip0 -o 4\n"
+ );
+}
+
+int main(int argc, char **argv)
+{
+ const char *device_name = NULL;
+ unsigned int lines[GPIOHANDLES_MAX];
+ unsigned int loops = 0;
+ int nlines;
+ int c;
+ int i;
+
+ i = 0;
+ while ((c = getopt(argc, argv, "c:n:o:?")) != -1) {
+ switch (c) {
+ case 'c':
+ loops = strtoul(optarg, NULL, 10);
+ break;
+ case 'n':
+ device_name = optarg;
+ break;
+ case 'o':
+ lines[i] = strtoul(optarg, NULL, 10);
+ i++;
+ break;
+ case '?':
+ print_usage();
+ return -1;
+ }
+ }
+ nlines = i;
+
+ if (!device_name || !nlines) {
+ print_usage();
+ return -1;
+ }
+ return hammer_device(device_name, lines, nlines, loops);
+}