diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-07-26 19:16:01 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-07-26 19:16:01 -0700 |
commit | 1cd04d293c818687795b83cd8f2626bd4662feeb (patch) | |
tree | dcbaadd82c02204114b99c418bfae1ee57b2c4ca | |
parent | 9c1958fc326a0a0a533ec8e86ea6fa30977207de (diff) | |
parent | 224f9e6d538c4cfb2fa8dc4206fceb9431271388 (diff) |
Merge tag 'gpio-v4.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio
Pull GPIO updates from Linus Walleij:
"This is the bulk of GPIO changes for the v4.8 kernel cycle. The big
news is the completion of the chardev ABI which I'm very happy about
and apart from that it's an ordinary, quite busy cycle. The details
are below.
The patches are tested in linux-next for some time, patches to other
subsystem mostly have ACKs.
I got overly ambitious with configureing lines as input for IRQ lines
but it turns out that some controllers have their interrupt-enable and
input-enabling in orthogonal settings so the assumption that all IRQ
lines are input lines does not hold. Oh well, revert and back to the
drawing board with that.
Core changes:
- The big item is of course the completion of the character device
ABI. It has now replaced and surpassed the former unmaintainable
sysfs ABI: we can now hammer (bitbang) individual lines or sets of
lines and read individual lines or sets of lines from userspace,
and we can also register to listen to GPIO events from userspace.
As a tie-in we have two new tools in tools/gpio: gpio-hammer and
gpio-event-mon that illustrate the proper use of the new ABI. As
someone said: the wild west days of GPIO are now over.
- Continued to remove the pointless ARCH_[WANT_OPTIONAL|REQUIRE]_GPIOLIB
Kconfig symbols. I'm patching hexagon, openrisc, powerpc, sh,
unicore, ia64 and microblaze. These are either ACKed by their
maintainers or patched anyways after a grace period and no response
from maintainers.
Some archs (ARM) come in from their trees, and others (x86) are
still not fixed, so I might send a second pull request to root it
out later in this merge window, or just defer to v4.9.
- The GPIO tools are moved to the tools build system.
New drivers:
- New driver for the MAX77620/MAX20024.
- New driver for the Intel Merrifield.
- Enabled PCA953x for the TI PCA9536.
- Enabled PCA953x for the Intel Edison.
- Enabled R8A7792 in the RCAR driver.
Driver improvements:
- The STMPE and F7188x now supports the .get_direction() callback.
- The Xilinx driver supports setting multiple lines at once.
- ACPI support for the Vulcan GPIO controller.
- The MMIO GPIO driver supports device tree probing.
- The Acer One 10 is supported through the _DEP ACPI attribute.
Cleanups:
- A major cleanup of the OF/DT support code. It is way easier to
read and understand now, probably this improves performance too.
- Drop a few redundant .owner assignments.
- Remove CLPS711x boardfile support: we are 100% DT"
* tag 'gpio-v4.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (67 commits)
MAINTAINERS: Add INTEL MERRIFIELD GPIO entry
gpio: dwapb: add missing fwnode_handle_put() in dwapb_gpio_get_pdata()
gpio: merrifield: Protect irq_ack() and gpio_set() by lock
gpio: merrifield: Introduce GPIO driver to support Merrifield
gpio: intel-mid: Make it depend to X86_INTEL_MID
gpio: intel-mid: Sort header block alphabetically
gpio: intel-mid: Remove potentially harmful code
gpio: rcar: add R8A7792 support
gpiolib: remove duplicated include from gpiolib.c
Revert "gpio: convince line to become input in irq helper"
gpiolib: of_find_gpio(): Don't discard errors
gpio: of: Allow overriding the device node
gpio: free handles in fringe cases
gpio: tps65218: Add platform_device_id table
gpio: max77620: get gpio value based on direction
gpio: lynxpoint: avoid potential warning on error path
tools/gpio: add install section
tools/gpio: move to tools buildsystem
gpio: intel-mid: switch to devm_gpiochip_add_data()
gpio: 74x164: Use spi_write() helper instead of open coding
...
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); +} |