Merge tag 'gpio-v4.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 27 Jul 2016 02:16:01 +0000 (19:16 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 27 Jul 2016 02:16:01 +0000 (19:16 -0700)
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:
Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.txt
Documentation/devicetree/bindings/gpio/gpio-clps711x.txt
Documentation/devicetree/bindings/gpio/gpio-max77620.txt [new file with mode: 0644]
Documentation/devicetree/bindings/gpio/gpio-pca953x.txt
Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt
Documentation/gpio/drivers-on-gpio.txt
MAINTAINERS
arch/hexagon/Kconfig
arch/ia64/Kconfig
arch/microblaze/Kconfig
arch/openrisc/Kconfig
arch/powerpc/Kconfig
arch/powerpc/platforms/40x/Kconfig
arch/powerpc/platforms/44x/Kconfig
arch/powerpc/platforms/512x/Kconfig
arch/powerpc/platforms/83xx/Kconfig
arch/powerpc/platforms/85xx/Kconfig
arch/powerpc/platforms/86xx/Kconfig
arch/powerpc/platforms/8xx/Kconfig
arch/powerpc/platforms/Kconfig
arch/sh/Kconfig
arch/sh/boards/Kconfig
arch/sh/boards/mach-highlander/Kconfig
arch/sh/boards/mach-rsk/Kconfig
arch/unicore32/Kconfig
drivers/Makefile
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-74x164.c
drivers/gpio/gpio-clps711x.c
drivers/gpio/gpio-dwapb.c
drivers/gpio/gpio-f7188x.c
drivers/gpio/gpio-intel-mid.c
drivers/gpio/gpio-lynxpoint.c
drivers/gpio/gpio-max77620.c [new file with mode: 0644]
drivers/gpio/gpio-menz127.c
drivers/gpio/gpio-merrifield.c [new file with mode: 0644]
drivers/gpio/gpio-mmio.c
drivers/gpio/gpio-palmas.c
drivers/gpio/gpio-pca953x.c
drivers/gpio/gpio-pcf857x.c
drivers/gpio/gpio-rcar.c
drivers/gpio/gpio-rdc321x.c
drivers/gpio/gpio-sch311x.c
drivers/gpio/gpio-stmpe.c
drivers/gpio/gpio-syscon.c
drivers/gpio/gpio-tc3589x.c
drivers/gpio/gpio-tps65218.c
drivers/gpio/gpio-tps6586x.c
drivers/gpio/gpio-tps65910.c
drivers/gpio/gpio-viperboard.c
drivers/gpio/gpio-wm831x.c
drivers/gpio/gpio-wm8350.c
drivers/gpio/gpio-wm8994.c
drivers/gpio/gpio-xilinx.c
drivers/gpio/gpio-xlp.c
drivers/gpio/gpiolib-acpi.c
drivers/gpio/gpiolib-of.c
drivers/gpio/gpiolib.c
drivers/pinctrl/pinctrl-xway.c
include/uapi/linux/gpio.h
tools/Makefile
tools/gpio/Build [new file with mode: 0644]
tools/gpio/Makefile
tools/gpio/gpio-event-mon.c [new file with mode: 0644]
tools/gpio/gpio-hammer.c [new file with mode: 0644]

index 94ae9f8..fd42e72 100644 (file)
@@ -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>;
        };
index e0d0446..0a304ad 100644 (file)
@@ -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 (file)
index 0000000..410e716
--- /dev/null
@@ -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>;
+};
index 6b4a98f..08dd15f 100644 (file)
@@ -21,6 +21,7 @@ Required properties:
        maxim,max7313
        maxim,max7315
        ti,pca6107
+       ti,pca9536
        ti,tca6408
        ti,tca6416
        ti,tca6424
index f60e2f4..8da26b3 100644 (file)
@@ -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.
index 14bf95a..3065132 100644 (file)
@@ -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
index 0a3827b..f4698b1 100644 (file)
@@ -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
index 57298e7..1941e4b 100644 (file)
@@ -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
index e109ee9..6a15083 100644 (file)
@@ -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
index 636e072..86f6572 100644 (file)
@@ -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
index 142cb05..489e7f9 100644 (file)
@@ -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
index 0a9d439..d111044 100644 (file)
@@ -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
index 6e287f1..e3257f2 100644 (file)
@@ -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
 
index 5538e57..48fc180 100644 (file)
@@ -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
 
index f09016f..bf7ae5c 100644 (file)
@@ -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
 
index 2bdc8c8..4ef7f1c 100644 (file)
@@ -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
index e626461..df25a3e 100644 (file)
@@ -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
index 1afd1e4..3988f16 100644 (file)
@@ -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
index 1572504..564d99b 100644 (file)
@@ -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.
index 46a3533..3663f71 100644 (file)
@@ -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
index e803a83..0d5f3a9 100644 (file)
@@ -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
index 5e52d53..e0db046 100644 (file)
@@ -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.
index def49cc..42f5589 100644 (file)
@@ -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
 
index 458a11f..0b9b2c4 100644 (file)
@@ -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
index e5602ee..0769066 100644 (file)
@@ -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
 
index a7187b9..f937713 100644 (file)
@@ -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/
index d786061..98dd47a 100644 (file)
@@ -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"
index 991598e..2a035ed 100644 (file)
@@ -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
index 80f9ddf..a6607fa 100644 (file)
@@ -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)
index 5a69025..52fd63f 100644 (file)
@@ -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);
index 34779bb..6193f62 100644 (file)
@@ -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);
                }
 
index 05aa538..600be84 100644 (file)
@@ -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;
index cdaba13..164de64 100644 (file)
@@ -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
  * 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;
index 9df015e..fbd393b 100644 (file)
@@ -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 (file)
index 0000000..b46b436
--- /dev/null
@@ -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");
index cc103af..a1210e3 100644 (file)
@@ -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 (file)
index 0000000..45b5127
--- /dev/null
@@ -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");
index 6c1cb3b..6ec144b 100644 (file)
@@ -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,
index e248707..8394744 100644 (file)
@@ -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,
 };
index 5e3be32..02f2a56 100644 (file)
@@ -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), },
index 169c09a..d168410 100644 (file)
@@ -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,
 };
 
index 681c93f..b96e0b4 100644 (file)
@@ -334,6 +334,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,
index ec945b9..cbf0f9e 100644 (file)
@@ -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,
 };
 
index a03b38e..b96990c 100644 (file)
@@ -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,
 };
index 6f7af28..f675132 100644 (file)
@@ -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,
 };
index 24b6d64..537cec7 100644 (file)
@@ -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,
        },
        {
index 2e35ed3..8b36593 100644 (file)
@@ -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,
 };
 
index 0eaeac8..1c09a19 100644 (file)
@@ -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);
index 6b15e68..042b9a2 100644 (file)
@@ -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,
 };
 
index 0ae6a5a..e63d7da 100644 (file)
@@ -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,
 };
 
index dec47aa..e6d1328 100644 (file)
@@ -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,
 };
 
index 41ec783..21f97bc 100644 (file)
@@ -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,
 };
 
index 07d45a3..e976570 100644 (file)
@@ -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,
 };
 
index 744af38..2457aac 100644 (file)
@@ -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,
 };
 
index d0fbb7f..14b2a62 100644 (file)
@@ -132,6 +132,53 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
        spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
 }
 
+/**
+ * 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.
@@ -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;
 
index 1a33a19..4620d05 100644 (file)
@@ -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,
 };
index 2dc5258..af51461 100644 (file)
@@ -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)
index 4aabddb..75e7b39 100644 (file)
 
 #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;
index be74bd3..53ff25a 100644 (file)
 #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;
        }
 
index a13f2b6..b937554 100644 (file)
@@ -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;
        }
index d0a3cac..333d354 100644 (file)
@@ -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_ */
index f10b64d..daa8fb3 100644 (file)
@@ -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 (file)
index 0000000..620c193
--- /dev/null
@@ -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
index c155d6b..250a891 100644 (file)
@@ -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 (file)
index 0000000..448ed96
--- /dev/null
@@ -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 (file)
index 0000000..37b3f14
--- /dev/null
@@ -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);
+}