whiterose

linux unikernel
Log | Files | Refs | README | LICENSE | git clone https://git.ne02ptzero.me/git/whiterose

commit 3601fe43e8164f67a8de3de8e988bfcb3a94af46
parent cf2e8c544cd3b33e9e403b7b72404c221bf888d1
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Fri,  8 Mar 2019 10:09:53 -0800

Merge tag 'gpio-v5.1-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio

Pull GPIO updates from Linus Walleij:
 "This is the bulk of GPIO changes for the v5.1 cycle:

  Core changes:

   - The big change this time around is the irqchip handling in the
     qualcomm pin controllers, closely coupled with the gpiochip. This
     rework, in a classic fall-between-the-chairs fashion has been
     sidestepped for too long.

     The Qualcomm IRQchips using the SPMI and SSBI transport mechanisms
     have been rewritten to use hierarchical irqchip. This creates the
     base from which I intend to gradually pull support for hierarchical
     irqchips into the gpiolib irqchip helpers to cut down on duplicate
     code.

     We have too many hacks in the kernel because people have been
     working around the missing hierarchical irqchip for years, and once
     it was there, noone understood it for a while. We are now slowly
     adapting to using it.

     This is why this pull requests include changes to MFD, SPMI,
     IRQchip core and some ARM Device Trees pertaining to the Qualcomm
     chip family. Since Qualcomm have so many chips and such large
     deployments it is paramount that this platform gets this right, and
     now it (hopefully) does.

   - Core support for pull-up and pull-down configuration, also from the
     device tree. When a simple GPIO chip supports an "off or on" pull-up
     or pull-down resistor, we provide a way to set this up using
     machine descriptors or device tree.

     If more elaborate control of pull up/down (such as resistance shunt
     setting) is required, drivers should be phased over to use pin
     control. We do not yet provide a userspace ABI for this pull
     up-down setting but I suspect the makers are going to ask for it
     soon enough. PCA953x is the first user of this new API.

   - The GPIO mockup driver has been revamped after some discussion
     improving the IRQ simulator in the process.

     The idea is to make it possible to use the mockup for both testing
     and virtual prototyping, e.g. when you do not yet have a GPIO
     expander to play with but really want to get something to develop
     code around before hardware is available. It's neat. The blackbox
     testing usecase is currently making its way into kernelci.

   - ACPI GPIO core preserves non direction flags when updating flags.

   - A new device core helper for devm_platform_ioremap_resource() is
     funneled through the GPIO tree with Greg's ACK.

  New drivers:

   - TQ-Systems QTMX86 GPIO controllers (using port-mapped I/O)

   - Gateworks PLD GPIO driver (vaccumed up from OpenWrt)

   - AMD G-Series PCH (Platform Controller Hub) GPIO driver.

   - Fintek F81804 & F81966 subvariants.

   - PCA953x now supports NXP PCAL6416.

  Driver improvements:

   - IRQ support on the Nintendo Wii (Hollywood) GPIO.

   - get_direction() support for the MVEBU driver.

   - Set the right output level on SAMA5D2.

   - Drop the unused irq trigger setting on the Spreadtrum driver.

   - Wakeup support for PCA953x.

   - A slew of cleanups in the various Intel drivers"

* tag 'gpio-v5.1-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (110 commits)
  gpio: gpio-omap: fix level interrupt idling
  gpio: amd-fch: Set proper output level for direction_output
  x86: apuv2: remove unused variable
  gpio: pca953x: Use PCA_LATCH_INT
  platform/x86: fix PCENGINES_APU2 Kconfig warning
  gpio: pca953x: Fix dereference of irq data in shutdown
  gpio: amd-fch: Fix type error found by sparse
  gpio: amd-fch: Drop const from resource
  gpio: mxc: add check to return defer probe if clock tree NOT ready
  gpio: ftgpio: Register per-instance irqchip
  gpio: ixp4xx: Add DT bindings
  x86: pcengines apuv2 gpio/leds/keys platform driver
  gpio: AMD G-Series PCH gpio driver
  drivers: depend on HAS_IOMEM for devm_platform_ioremap_resource()
  gpio: tqmx86: Set proper output level for direction_output
  gpio: sprd: Change to use SoC compatible string
  gpio: sprd: Use SoC compatible string instead of wildcard string
  gpio: of: Handle both enable-gpio{,s}
  gpio: of: Restrict enable-gpio quirk to regulator-gpio
  gpio: davinci: use devm_platform_ioremap_resource()
  ...

Diffstat:
ADocumentation/devicetree/bindings/gpio/gateworks,pld-gpio.txt | 20++++++++++++++++++++
MDocumentation/devicetree/bindings/gpio/gpio-eic-sprd.txt | 4++--
MDocumentation/devicetree/bindings/gpio/gpio-pca953x.txt | 1+
MDocumentation/devicetree/bindings/gpio/gpio.txt | 12++++++++++++
ADocumentation/devicetree/bindings/gpio/intel,ixp4xx-gpio.txt | 38++++++++++++++++++++++++++++++++++++++
MDocumentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt | 1+
MDocumentation/devicetree/bindings/vendor-prefixes.txt | 1+
MDocumentation/driver-api/gpio/board.rst | 1+
MDocumentation/driver-api/gpio/driver.rst | 2+-
MDocumentation/driver-api/gpio/legacy.rst | 9++++-----
MMAINTAINERS | 12++++++++++++
March/arm/boot/dts/qcom-apq8060-dragonboard.dts | 21++++++++-------------
March/arm/boot/dts/qcom-apq8064.dtsi | 46++--------------------------------------------
March/arm/boot/dts/qcom-mdm9615.dtsi | 9++-------
March/arm/boot/dts/qcom-msm8660.dtsi | 47++---------------------------------------------
March/arm/boot/dts/qcom-pm8941.dtsi | 38++------------------------------------
March/arm/boot/dts/qcom-pma8084.dtsi | 24++----------------------
March/arm/mach-sa1100/simpad.c | 1-
March/arm64/boot/dts/qcom/pm8005.dtsi | 6++----
March/arm64/boot/dts/qcom/pm8998.dtsi | 28++--------------------------
March/arm64/boot/dts/qcom/pmi8994.dtsi | 12++----------
March/arm64/boot/dts/qcom/pmi8998.dtsi | 16++--------------
Mdrivers/base/platform.c | 20++++++++++++++++++++
Mdrivers/gpio/Kconfig | 24++++++++++++++++++++++++
Mdrivers/gpio/Makefile | 3+++
Mdrivers/gpio/gpio-adp5588.c | 234++++++++++++++++++++++++++++++++++----------------------------------------------
Mdrivers/gpio/gpio-altera-a10sr.c | 17+++++++++--------
Mdrivers/gpio/gpio-altera.c | 4++--
Adrivers/gpio/gpio-amd-fch.c | 194+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/gpio/gpio-crystalcove.c | 30++++++++++--------------------
Mdrivers/gpio/gpio-davinci.c | 4+---
Mdrivers/gpio/gpio-eic-sprd.c | 1+
Mdrivers/gpio/gpio-f7188x.c | 24+++++++++++++++++++++++-
Mdrivers/gpio/gpio-ftgpio010.c | 20++++++++++----------
Adrivers/gpio/gpio-gw-pld.c | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/gpio/gpio-hlwd.c | 192++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mdrivers/gpio/gpio-madera.c | 2+-
Mdrivers/gpio/gpio-mockup.c | 189+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mdrivers/gpio/gpio-msic.c | 34+++++++++-------------------------
Mdrivers/gpio/gpio-mvebu.c | 11+++++++++++
Mdrivers/gpio/gpio-mxc.c | 5++++-
Mdrivers/gpio/gpio-omap.c | 14++++++++------
Mdrivers/gpio/gpio-pca953x.c | 190++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Mdrivers/gpio/gpio-pcf857x.c | 15+--------------
Mdrivers/gpio/gpio-pmic-eic-sprd.c | 3+--
Mdrivers/gpio/gpio-rcar.c | 12+++++++++++-
Mdrivers/gpio/gpio-sama5d2-piobu.c | 20++++++++------------
Mdrivers/gpio/gpio-tegra.c | 25+++++++++++++++++++++----
Mdrivers/gpio/gpio-tegra186.c | 70+++++++++++++++++++++++++++++++++++-----------------------------------
Adrivers/gpio/gpio-tqmx86.c | 332+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/gpio/gpio-wcove.c | 29++++++++++-------------------
Mdrivers/gpio/gpio-zynq.c | 24++++++++++++++++++++++++
Mdrivers/gpio/gpiolib-acpi.c | 7+++++--
Mdrivers/gpio/gpiolib-of.c | 9++++++++-
Mdrivers/gpio/gpiolib.c | 87++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mdrivers/gpio/gpiolib.h | 2++
Mdrivers/mfd/Kconfig | 2+-
Mdrivers/mfd/qcom-pm8xxx.c | 75++++++++++++++++++++++++++++++++++++++-------------------------------------
Mdrivers/pinctrl/qcom/Kconfig | 2++
Mdrivers/pinctrl/qcom/pinctrl-spmi-gpio.c | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mdrivers/pinctrl/qcom/pinctrl-ssbi-gpio.c | 157++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Mdrivers/platform/x86/Kconfig | 14++++++++++++++
Mdrivers/platform/x86/Makefile | 1+
Adrivers/platform/x86/pcengines-apuv2.c | 260+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/spmi/Kconfig | 2+-
Mdrivers/spmi/spmi-pmic-arb.c | 67+++++++++++++++++++++++++++++++++++++++++++++++--------------------
Minclude/dt-bindings/gpio/gpio.h | 6++++++
Minclude/linux/gpio/driver.h | 5+++++
Minclude/linux/gpio/machine.h | 2++
Minclude/linux/irq.h | 1+
Minclude/linux/irqdomain.h | 5+++++
Minclude/linux/of_gpio.h | 2++
Ainclude/linux/platform_data/gpio/gpio-amd-fch.h | 46++++++++++++++++++++++++++++++++++++++++++++++
Minclude/linux/platform_device.h | 3+++
Mkernel/irq/chip.c | 12++++++++++++
Mkernel/irq/irq_sim.c | 12++++++++++++
Mkernel/irq/irqdomain.c | 45++++++++++++++++++++++++++++++++++-----------
77 files changed, 2438 insertions(+), 755 deletions(-)

diff --git a/Documentation/devicetree/bindings/gpio/gateworks,pld-gpio.txt b/Documentation/devicetree/bindings/gpio/gateworks,pld-gpio.txt @@ -0,0 +1,20 @@ +Gateworks PLD GPIO controller bindings + +The GPIO controller should be a child node on an I2C bus, +see: i2c/i2c.txt for details. + +Required properties: +- compatible: Should be "gateworks,pld-gpio" +- reg: I2C slave address +- gpio-controller: Marks the device node as a GPIO controller. +- #gpio-cells: Should be <2>. The first cell is the gpio number and + the second cell is used to specify optional parameters. + +Example: + +pld@56 { + compatible = "gateworks,pld-gpio"; + reg = <0x56>; + gpio-controller; + #gpio-cells = <2>; +}; diff --git a/Documentation/devicetree/bindings/gpio/gpio-eic-sprd.txt b/Documentation/devicetree/bindings/gpio/gpio-eic-sprd.txt @@ -33,7 +33,7 @@ Required properties: "sprd,sc9860-eic-latch", "sprd,sc9860-eic-async", "sprd,sc9860-eic-sync", - "sprd,sc27xx-eic". + "sprd,sc2731-eic". - reg: Define the base and range of the I/O address space containing the GPIO controller registers. - gpio-controller: Marks the device node as a GPIO controller. @@ -86,7 +86,7 @@ Example: }; pmic_eic: gpio@300 { - compatible = "sprd,sc27xx-eic"; + compatible = "sprd,sc2731-eic"; reg = <0x300>; interrupt-parent = <&sc2731_pmic>; interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt @@ -16,6 +16,7 @@ Required properties: nxp,pca9574 nxp,pca9575 nxp,pca9698 + nxp,pcal6416 nxp,pcal6524 nxp,pcal9555a maxim,max7310 diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt @@ -67,6 +67,18 @@ Optional standard bitfield specifiers for the last cell: https://en.wikipedia.org/wiki/Open_collector - Bit 3: 0 means the output should be maintained during sleep/low-power mode 1 means the output state can be lost during sleep/low-power mode +- Bit 4: 0 means no pull-up resistor should be enabled + 1 means a pull-up resistor should be enabled + This setting only applies to hardware with a simple on/off + control for pull-up configuration. If the hardware has more + elaborate pull-up configuration, it should be represented + using a pin control binding. +- Bit 5: 0 means no pull-down resistor should be enabled + 1 means a pull-down resistor should be enabled + This setting only applies to hardware with a simple on/off + control for pull-down configuration. If the hardware has more + elaborate pull-down configuration, it should be represented + using a pin control binding. 1.1) GPIO specifier best practices ---------------------------------- diff --git a/Documentation/devicetree/bindings/gpio/intel,ixp4xx-gpio.txt b/Documentation/devicetree/bindings/gpio/intel,ixp4xx-gpio.txt @@ -0,0 +1,38 @@ +Intel IXP4xx XScale Networking Processors GPIO + +This GPIO controller is found in the Intel IXP4xx processors. +It supports 16 GPIO lines. + +The interrupt portions of the GPIO controller is hierarchical: +the synchronous edge detector is part of the GPIO block, but the +actual enabling/disabling of the interrupt line is done in the +main IXP4xx interrupt controller which has a 1:1 mapping for +the first 12 GPIO lines to 12 system interrupts. + +The remaining 4 GPIO lines can not be used for receiving +interrupts. + +The interrupt parent of this GPIO controller must be the +IXP4xx interrupt controller. + +Required properties: + +- compatible : Should be + "intel,ixp4xx-gpio" +- reg : Should contain registers location and length +- gpio-controller : marks this as a GPIO controller +- #gpio-cells : Should be 2, see gpio/gpio.txt +- interrupt-controller : marks this as an interrupt controller +- #interrupt-cells : a standard two-cell interrupt, see + interrupt-controller/interrupts.txt + +Example: + +gpio0: gpio@c8004000 { + compatible = "intel,ixp4xx-gpio"; + reg = <0xc8004000 0x1000>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; +}; diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt @@ -19,6 +19,7 @@ PMIC's from Qualcomm. "qcom,pm8998-gpio" "qcom,pma8084-gpio" "qcom,pmi8994-gpio" + "qcom,pmi8998-gpio" "qcom,pms405-gpio" And must contain either "qcom,spmi-gpio" or "qcom,ssbi-gpio" diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -145,6 +145,7 @@ focaltech FocalTech Systems Co.,Ltd friendlyarm Guangzhou FriendlyARM Computer Tech Co., Ltd fsl Freescale Semiconductor fujitsu Fujitsu Ltd. +gateworks Gateworks Corporation gcw Game Consoles Worldwide ge General Electric Company geekbuying GeekBuying diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst @@ -204,6 +204,7 @@ between a caller and a respective .get/set_multiple() callback of a GPIO chip. In order to qualify for fast bitmap processing, the array must meet the following requirements: + - pin hardware number of array member 0 must also be 0, - pin hardware numbers of consecutive array members which belong to the same chip as member 0 does must also match their array indexes. diff --git a/Documentation/driver-api/gpio/driver.rst b/Documentation/driver-api/gpio/driver.rst @@ -135,7 +135,7 @@ This configuration is normally used as a way to achieve one of two things: - inverse wire-OR on an I/O line, for example a GPIO line, making it possible for any driving stage on the line to drive it low even if any other output to the same line is simultaneously driving it high. A special case of this - is driving the SCL and SCA lines of an I2C bus, which is by definition a + is driving the SCL and SDA lines of an I2C bus, which is by definition a wire-OR bus. Both usecases require that the line be equipped with a pull-up resistor. This diff --git a/Documentation/driver-api/gpio/legacy.rst b/Documentation/driver-api/gpio/legacy.rst @@ -690,11 +690,10 @@ and have the following read/write attributes: and if it has been configured to generate interrupts (see the description of "edge"), you can poll(2) on that file and poll(2) will return whenever the interrupt was triggered. If - you use poll(2), set the events POLLPRI and POLLERR. If you - use select(2), set the file descriptor in exceptfds. After - poll(2) returns, either lseek(2) to the beginning of the sysfs - file and read the new value or close the file and re-open it - to read the value. + you use poll(2), set the events POLLPRI. If you use select(2), + set the file descriptor in exceptfds. After poll(2) returns, + either lseek(2) to the beginning of the sysfs file and read the + new value or close the file and re-open it to read the value. "edge" ... reads as either "none", "rising", "falling", or "both". Write these strings to select the signal edge(s) diff --git a/MAINTAINERS b/MAINTAINERS @@ -766,6 +766,13 @@ S: Supported F: Documentation/hwmon/fam15h_power F: drivers/hwmon/fam15h_power.c +AMD FCH GPIO DRIVER +M: Enrico Weigelt, metux IT consult <info@metux.net> +L: linux-gpio@vger.kernel.org +S: Maintained +F: drivers/gpio/gpio-amd-fch.c +F: include/linux/platform_data/gpio/gpio-amd-fch.h + AMD GEODE CS5536 USB DEVICE CONTROLLER DRIVER L: linux-geode@lists.infradead.org (moderated for non-subscribers) S: Orphan @@ -11716,6 +11723,11 @@ F: lib/parman.c F: lib/test_parman.c F: include/linux/parman.h +PC ENGINES APU BOARD DRIVER +M: Enrico Weigelt, metux IT consult <info@metux.net> +S: Maintained +F: drivers/platform/x86/pcengines-apuv2.c + PC87360 HARDWARE MONITORING DRIVER M: Jim Cromie <jim.cromie@gmail.com> L: linux-hwmon@vger.kernel.org diff --git a/arch/arm/boot/dts/qcom-apq8060-dragonboard.dts b/arch/arm/boot/dts/qcom-apq8060-dragonboard.dts @@ -93,9 +93,8 @@ vdd-supply = <&pm8058_l14>; // 2.85V aset-gpios = <&pm8058_gpio 35 GPIO_ACTIVE_LOW>; capella,aset-resistance-ohms = <100000>; - /* GPIO34 has interrupt 225 on the PM8058 */ /* Trig on both edges - getting close or far away */ - interrupts-extended = <&pm8058 225 IRQ_TYPE_EDGE_BOTH>; + interrupts-extended = <&pm8058_gpio 34 IRQ_TYPE_EDGE_BOTH>; /* MPP05 analog input to the XOADC */ io-channels = <&xoadc 0x00 0x05>; io-channel-names = "aout"; @@ -515,9 +514,8 @@ ak8975@c { compatible = "asahi-kasei,ak8975"; reg = <0x0c>; - /* FIXME: GPIO33 has interrupt 224 on the PM8058 */ - interrupt-parent = <&pm8058>; - interrupts = <224 IRQ_TYPE_EDGE_RISING>; + interrupt-parent = <&pm8058_gpio>; + interrupts = <33 IRQ_TYPE_EDGE_RISING>; pinctrl-names = "default"; pinctrl-0 = <&dragon_ak8975_gpios>; vid-supply = <&pm8058_lvs0>; // 1.8V @@ -526,9 +524,8 @@ bmp085@77 { compatible = "bosch,bmp085"; reg = <0x77>; - /* FIXME: GPIO16 has interrupt 207 on the PM8058 */ - interrupt-parent = <&pm8058>; - interrupts = <207 IRQ_TYPE_EDGE_RISING>; + interrupt-parent = <&pm8058_gpio>; + interrupts = <16 IRQ_TYPE_EDGE_RISING>; reset-gpios = <&tlmm 86 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; pinctrl-0 = <&dragon_bmp085_gpios>; @@ -539,12 +536,11 @@ compatible = "invensense,mpu3050"; reg = <0x68>; /* - * GPIO17 has interrupt 208 on the - * PM8058, it is pulled high by a 10k + * GPIO17 is pulled high by a 10k * resistor to VLOGIC so needs to be * active low/falling edge. */ - interrupts-extended = <&pm8058 208 IRQ_TYPE_EDGE_FALLING>; + interrupts-extended = <&pm8058_gpio 17 IRQ_TYPE_EDGE_FALLING>; pinctrl-names = "default"; pinctrl-0 = <&dragon_mpu3050_gpios>; vlogic-supply = <&pm8058_lvs0>; // 1.8V @@ -589,11 +585,10 @@ compatible = "smsc,lan9221", "smsc,lan9115"; reg = <2 0x0 0x100>; /* - * GPIO7 has interrupt 198 on the PM8058 * The second interrupt is the PME interrupt * for network wakeup, connected to the TLMM. */ - interrupts-extended = <&pm8058 198 IRQ_TYPE_EDGE_FALLING>, + interrupts-extended = <&pm8058_gpio 7 IRQ_TYPE_EDGE_FALLING>, <&tlmm 29 IRQ_TYPE_EDGE_RISING>; reset-gpios = <&tlmm 30 GPIO_ACTIVE_LOW>; vdd33a-supply = <&dragon_veth>; diff --git a/arch/arm/boot/dts/qcom-apq8064.dtsi b/arch/arm/boot/dts/qcom-apq8064.dtsi @@ -711,50 +711,8 @@ compatible = "qcom,pm8921-gpio", "qcom,ssbi-gpio"; reg = <0x150>; - interrupts = <192 IRQ_TYPE_NONE>, - <193 IRQ_TYPE_NONE>, - <194 IRQ_TYPE_NONE>, - <195 IRQ_TYPE_NONE>, - <196 IRQ_TYPE_NONE>, - <197 IRQ_TYPE_NONE>, - <198 IRQ_TYPE_NONE>, - <199 IRQ_TYPE_NONE>, - <200 IRQ_TYPE_NONE>, - <201 IRQ_TYPE_NONE>, - <202 IRQ_TYPE_NONE>, - <203 IRQ_TYPE_NONE>, - <204 IRQ_TYPE_NONE>, - <205 IRQ_TYPE_NONE>, - <206 IRQ_TYPE_NONE>, - <207 IRQ_TYPE_NONE>, - <208 IRQ_TYPE_NONE>, - <209 IRQ_TYPE_NONE>, - <210 IRQ_TYPE_NONE>, - <211 IRQ_TYPE_NONE>, - <212 IRQ_TYPE_NONE>, - <213 IRQ_TYPE_NONE>, - <214 IRQ_TYPE_NONE>, - <215 IRQ_TYPE_NONE>, - <216 IRQ_TYPE_NONE>, - <217 IRQ_TYPE_NONE>, - <218 IRQ_TYPE_NONE>, - <219 IRQ_TYPE_NONE>, - <220 IRQ_TYPE_NONE>, - <221 IRQ_TYPE_NONE>, - <222 IRQ_TYPE_NONE>, - <223 IRQ_TYPE_NONE>, - <224 IRQ_TYPE_NONE>, - <225 IRQ_TYPE_NONE>, - <226 IRQ_TYPE_NONE>, - <227 IRQ_TYPE_NONE>, - <228 IRQ_TYPE_NONE>, - <229 IRQ_TYPE_NONE>, - <230 IRQ_TYPE_NONE>, - <231 IRQ_TYPE_NONE>, - <232 IRQ_TYPE_NONE>, - <233 IRQ_TYPE_NONE>, - <234 IRQ_TYPE_NONE>, - <235 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; gpio-controller; #gpio-cells = <2>; diff --git a/arch/arm/boot/dts/qcom-mdm9615.dtsi b/arch/arm/boot/dts/qcom-mdm9615.dtsi @@ -323,13 +323,8 @@ pmicgpio: gpio@150 { compatible = "qcom,pm8018-gpio", "qcom,ssbi-gpio"; - interrupt-parent = <&pmicintc>; - interrupts = <24 IRQ_TYPE_NONE>, - <25 IRQ_TYPE_NONE>, - <26 IRQ_TYPE_NONE>, - <27 IRQ_TYPE_NONE>, - <28 IRQ_TYPE_NONE>, - <29 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; gpio-controller; #gpio-cells = <2>; }; diff --git a/arch/arm/boot/dts/qcom-msm8660.dtsi b/arch/arm/boot/dts/qcom-msm8660.dtsi @@ -292,51 +292,8 @@ compatible = "qcom,pm8058-gpio", "qcom,ssbi-gpio"; reg = <0x150>; - interrupt-parent = <&pm8058>; - interrupts = <192 IRQ_TYPE_NONE>, - <193 IRQ_TYPE_NONE>, - <194 IRQ_TYPE_NONE>, - <195 IRQ_TYPE_NONE>, - <196 IRQ_TYPE_NONE>, - <197 IRQ_TYPE_NONE>, - <198 IRQ_TYPE_NONE>, - <199 IRQ_TYPE_NONE>, - <200 IRQ_TYPE_NONE>, - <201 IRQ_TYPE_NONE>, - <202 IRQ_TYPE_NONE>, - <203 IRQ_TYPE_NONE>, - <204 IRQ_TYPE_NONE>, - <205 IRQ_TYPE_NONE>, - <206 IRQ_TYPE_NONE>, - <207 IRQ_TYPE_NONE>, - <208 IRQ_TYPE_NONE>, - <209 IRQ_TYPE_NONE>, - <210 IRQ_TYPE_NONE>, - <211 IRQ_TYPE_NONE>, - <212 IRQ_TYPE_NONE>, - <213 IRQ_TYPE_NONE>, - <214 IRQ_TYPE_NONE>, - <215 IRQ_TYPE_NONE>, - <216 IRQ_TYPE_NONE>, - <217 IRQ_TYPE_NONE>, - <218 IRQ_TYPE_NONE>, - <219 IRQ_TYPE_NONE>, - <220 IRQ_TYPE_NONE>, - <221 IRQ_TYPE_NONE>, - <222 IRQ_TYPE_NONE>, - <223 IRQ_TYPE_NONE>, - <224 IRQ_TYPE_NONE>, - <225 IRQ_TYPE_NONE>, - <226 IRQ_TYPE_NONE>, - <227 IRQ_TYPE_NONE>, - <228 IRQ_TYPE_NONE>, - <229 IRQ_TYPE_NONE>, - <230 IRQ_TYPE_NONE>, - <231 IRQ_TYPE_NONE>, - <232 IRQ_TYPE_NONE>, - <233 IRQ_TYPE_NONE>, - <234 IRQ_TYPE_NONE>, - <235 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; gpio-controller; #gpio-cells = <2>; diff --git a/arch/arm/boot/dts/qcom-pm8941.dtsi b/arch/arm/boot/dts/qcom-pm8941.dtsi @@ -65,42 +65,8 @@ gpio-controller; gpio-ranges = <&pm8941_gpios 0 0 36>; #gpio-cells = <2>; - interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, - <0 0xc1 0 IRQ_TYPE_NONE>, - <0 0xc2 0 IRQ_TYPE_NONE>, - <0 0xc3 0 IRQ_TYPE_NONE>, - <0 0xc4 0 IRQ_TYPE_NONE>, - <0 0xc5 0 IRQ_TYPE_NONE>, - <0 0xc6 0 IRQ_TYPE_NONE>, - <0 0xc7 0 IRQ_TYPE_NONE>, - <0 0xc8 0 IRQ_TYPE_NONE>, - <0 0xc9 0 IRQ_TYPE_NONE>, - <0 0xca 0 IRQ_TYPE_NONE>, - <0 0xcb 0 IRQ_TYPE_NONE>, - <0 0xcc 0 IRQ_TYPE_NONE>, - <0 0xcd 0 IRQ_TYPE_NONE>, - <0 0xce 0 IRQ_TYPE_NONE>, - <0 0xcf 0 IRQ_TYPE_NONE>, - <0 0xd0 0 IRQ_TYPE_NONE>, - <0 0xd1 0 IRQ_TYPE_NONE>, - <0 0xd2 0 IRQ_TYPE_NONE>, - <0 0xd3 0 IRQ_TYPE_NONE>, - <0 0xd4 0 IRQ_TYPE_NONE>, - <0 0xd5 0 IRQ_TYPE_NONE>, - <0 0xd6 0 IRQ_TYPE_NONE>, - <0 0xd7 0 IRQ_TYPE_NONE>, - <0 0xd8 0 IRQ_TYPE_NONE>, - <0 0xd9 0 IRQ_TYPE_NONE>, - <0 0xda 0 IRQ_TYPE_NONE>, - <0 0xdb 0 IRQ_TYPE_NONE>, - <0 0xdc 0 IRQ_TYPE_NONE>, - <0 0xdd 0 IRQ_TYPE_NONE>, - <0 0xde 0 IRQ_TYPE_NONE>, - <0 0xdf 0 IRQ_TYPE_NONE>, - <0 0xe0 0 IRQ_TYPE_NONE>, - <0 0xe1 0 IRQ_TYPE_NONE>, - <0 0xe2 0 IRQ_TYPE_NONE>, - <0 0xe3 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; boost_bypass_n_pin: boost-bypass { pins = "gpio21"; diff --git a/arch/arm/boot/dts/qcom-pma8084.dtsi b/arch/arm/boot/dts/qcom-pma8084.dtsi @@ -32,28 +32,8 @@ reg = <0xc000>; gpio-controller; #gpio-cells = <2>; - interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, - <0 0xc1 0 IRQ_TYPE_NONE>, - <0 0xc2 0 IRQ_TYPE_NONE>, - <0 0xc3 0 IRQ_TYPE_NONE>, - <0 0xc4 0 IRQ_TYPE_NONE>, - <0 0xc5 0 IRQ_TYPE_NONE>, - <0 0xc6 0 IRQ_TYPE_NONE>, - <0 0xc7 0 IRQ_TYPE_NONE>, - <0 0xc8 0 IRQ_TYPE_NONE>, - <0 0xc9 0 IRQ_TYPE_NONE>, - <0 0xca 0 IRQ_TYPE_NONE>, - <0 0xcb 0 IRQ_TYPE_NONE>, - <0 0xcc 0 IRQ_TYPE_NONE>, - <0 0xcd 0 IRQ_TYPE_NONE>, - <0 0xce 0 IRQ_TYPE_NONE>, - <0 0xcf 0 IRQ_TYPE_NONE>, - <0 0xd0 0 IRQ_TYPE_NONE>, - <0 0xd1 0 IRQ_TYPE_NONE>, - <0 0xd2 0 IRQ_TYPE_NONE>, - <0 0xd3 0 IRQ_TYPE_NONE>, - <0 0xd4 0 IRQ_TYPE_NONE>, - <0 0xd5 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; }; pma8084_mpps: mpps@a000 { diff --git a/arch/arm/mach-sa1100/simpad.c b/arch/arm/mach-sa1100/simpad.c @@ -18,7 +18,6 @@ #include <linux/mtd/partitions.h> #include <linux/io.h> #include <linux/gpio/driver.h> -#include <linux/gpio/machine.h> #include <mach/hardware.h> #include <asm/setup.h> diff --git a/arch/arm64/boot/dts/qcom/pm8005.dtsi b/arch/arm64/boot/dts/qcom/pm8005.dtsi @@ -16,10 +16,8 @@ reg = <0xc000>; gpio-controller; #gpio-cells = <2>; - interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, - <0 0xc1 0 IRQ_TYPE_NONE>, - <0 0xc2 0 IRQ_TYPE_NONE>, - <0 0xc3 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; }; }; diff --git a/arch/arm64/boot/dts/qcom/pm8998.dtsi b/arch/arm64/boot/dts/qcom/pm8998.dtsi @@ -94,32 +94,8 @@ reg = <0xc000>; gpio-controller; #gpio-cells = <2>; - interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, - <0 0xc1 0 IRQ_TYPE_NONE>, - <0 0xc2 0 IRQ_TYPE_NONE>, - <0 0xc3 0 IRQ_TYPE_NONE>, - <0 0xc4 0 IRQ_TYPE_NONE>, - <0 0xc5 0 IRQ_TYPE_NONE>, - <0 0xc6 0 IRQ_TYPE_NONE>, - <0 0xc7 0 IRQ_TYPE_NONE>, - <0 0xc8 0 IRQ_TYPE_NONE>, - <0 0xc9 0 IRQ_TYPE_NONE>, - <0 0xca 0 IRQ_TYPE_NONE>, - <0 0xcb 0 IRQ_TYPE_NONE>, - <0 0xcc 0 IRQ_TYPE_NONE>, - <0 0xcd 0 IRQ_TYPE_NONE>, - <0 0xce 0 IRQ_TYPE_NONE>, - <0 0xcf 0 IRQ_TYPE_NONE>, - <0 0xd0 0 IRQ_TYPE_NONE>, - <0 0xd1 0 IRQ_TYPE_NONE>, - <0 0xd2 0 IRQ_TYPE_NONE>, - <0 0xd3 0 IRQ_TYPE_NONE>, - <0 0xd4 0 IRQ_TYPE_NONE>, - <0 0xd5 0 IRQ_TYPE_NONE>, - <0 0xd6 0 IRQ_TYPE_NONE>, - <0 0xd7 0 IRQ_TYPE_NONE>, - <0 0xd8 0 IRQ_TYPE_NONE>, - <0 0xd9 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; }; }; diff --git a/arch/arm64/boot/dts/qcom/pmi8994.dtsi b/arch/arm64/boot/dts/qcom/pmi8994.dtsi @@ -15,16 +15,8 @@ reg = <0xc000>; gpio-controller; #gpio-cells = <2>; - interrupts = <2 0xc0 0 IRQ_TYPE_NONE>, - <2 0xc1 0 IRQ_TYPE_NONE>, - <2 0xc2 0 IRQ_TYPE_NONE>, - <2 0xc3 0 IRQ_TYPE_NONE>, - <2 0xc4 0 IRQ_TYPE_NONE>, - <2 0xc5 0 IRQ_TYPE_NONE>, - <2 0xc6 0 IRQ_TYPE_NONE>, - <2 0xc7 0 IRQ_TYPE_NONE>, - <2 0xc8 0 IRQ_TYPE_NONE>, - <2 0xc9 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; }; }; diff --git a/arch/arm64/boot/dts/qcom/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi @@ -14,20 +14,8 @@ reg = <0xc000>; gpio-controller; #gpio-cells = <2>; - interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, - <0 0xc1 0 IRQ_TYPE_NONE>, - <0 0xc2 0 IRQ_TYPE_NONE>, - <0 0xc3 0 IRQ_TYPE_NONE>, - <0 0xc4 0 IRQ_TYPE_NONE>, - <0 0xc5 0 IRQ_TYPE_NONE>, - <0 0xc6 0 IRQ_TYPE_NONE>, - <0 0xc7 0 IRQ_TYPE_NONE>, - <0 0xc8 0 IRQ_TYPE_NONE>, - <0 0xc9 0 IRQ_TYPE_NONE>, - <0 0xca 0 IRQ_TYPE_NONE>, - <0 0xcb 0 IRQ_TYPE_NONE>, - <0 0xcc 0 IRQ_TYPE_NONE>, - <0 0xcd 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; }; }; diff --git a/drivers/base/platform.c b/drivers/base/platform.c @@ -80,6 +80,26 @@ struct resource *platform_get_resource(struct platform_device *dev, EXPORT_SYMBOL_GPL(platform_get_resource); /** + * devm_platform_ioremap_resource - call devm_ioremap_resource() for a platform + * device + * + * @pdev: platform device to use both for memory resource lookup as well as + * resource managemend + * @index: resource index + */ +#ifdef CONFIG_HAS_IOMEM +void __iomem *devm_platform_ioremap_resource(struct platform_device *pdev, + unsigned int index) +{ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, index); + return devm_ioremap_resource(&pdev->dev, res); +} +EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource); +#endif /* CONFIG_HAS_IOMEM */ + +/** * platform_get_irq - get an IRQ for a device * @dev: platform device * @num: IRQ number index diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig @@ -258,6 +258,7 @@ config GPIO_HLWD tristate "Nintendo Wii (Hollywood) GPIO" depends on OF_GPIO select GPIO_GENERIC + select GPIOLIB_IRQCHIP help Select this to support the GPIO controller of the Nintendo Wii. @@ -654,6 +655,15 @@ config GPIO_LOONGSON1 help Say Y or M here to support GPIO on Loongson1 SoCs. +config GPIO_AMD_FCH + tristate "GPIO support for AMD Fusion Controller Hub (G-series SOCs)" + help + This option enables driver for GPIO on AMDs Fusion Controller Hub, + as found on G-series SOCs (eg. GX-412TC) + + Note: This driver doesn't registers itself automatically, as it + needs to be provided with platform specific configuration. + (See eg. CONFIG_PCENGINES_APU2.) endmenu menu "Port-mapped I/O GPIO drivers" @@ -830,6 +840,13 @@ config GPIO_ADNP enough to represent all pins, but the driver will assume a register layout for 64 pins (8 registers). +config GPIO_GW_PLD + tristate "Gateworks PLD GPIO Expander" + depends on OF_GPIO + help + Say yes here to provide access to the Gateworks I2C PLD GPIO + Expander. This is used at least on the Cambria GW2358-4. + config GPIO_MAX7300 tristate "Maxim MAX7300 GPIO expander" select GPIO_MAX730X @@ -1190,6 +1207,13 @@ config GPIO_TPS68470 of the TPS68470 must be available before dependent drivers are loaded. +config GPIO_TQMX86 + tristate "TQ-Systems QTMX86 GPIO" + depends on MFD_TQMX86 || COMPILE_TEST + select GPIOLIB_IRQCHIP + help + This driver supports GPIO on the TQMX86 IO controller. + config GPIO_TWL4030 tristate "TWL4030, TWL5030, and TPS659x0 GPIOs" depends on TWL4030_CORE diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o +obj-$(CONFIG_GPIO_AMD_FCH) += gpio-amd-fch.o obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o @@ -55,6 +56,7 @@ obj-$(CONFIG_GPIO_FTGPIO010) += gpio-ftgpio010.o obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o +obj-$(CONFIG_GPIO_GW_PLD) += gpio-gw-pld.o obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o @@ -135,6 +137,7 @@ obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o obj-$(CONFIG_GPIO_TPS68470) += gpio-tps68470.o +obj-$(CONFIG_GPIO_TQMX86) += gpio-tqmx86.o obj-$(CONFIG_GPIO_TS4800) += gpio-ts4800.o obj-$(CONFIG_GPIO_TS4900) += gpio-ts4900.o obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c @@ -15,6 +15,7 @@ #include <linux/gpio/driver.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/of_device.h> #include <linux/platform_data/adp5588.h> @@ -33,16 +34,13 @@ struct adp5588_gpio { struct mutex lock; /* protect cached dir, dat_out */ /* protect serialized access to the interrupt controller bus */ struct mutex irq_lock; - unsigned gpio_start; - unsigned irq_base; uint8_t dat_out[3]; uint8_t dir[3]; - uint8_t int_lvl[3]; + uint8_t int_lvl_low[3]; + uint8_t int_lvl_high[3]; uint8_t int_en[3]; uint8_t irq_mask[3]; - uint8_t irq_stat[3]; uint8_t int_input_en[3]; - uint8_t int_lvl_cached[3]; }; static int adp5588_gpio_read(struct i2c_client *client, u8 reg) @@ -148,16 +146,11 @@ static int adp5588_gpio_direction_output(struct gpio_chip *chip, } #ifdef CONFIG_GPIO_ADP5588_IRQ -static int adp5588_gpio_to_irq(struct gpio_chip *chip, unsigned off) -{ - struct adp5588_gpio *dev = gpiochip_get_data(chip); - - return dev->irq_base + off; -} static void adp5588_irq_bus_lock(struct irq_data *d) { - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5588_gpio *dev = gpiochip_get_data(gc); mutex_lock(&dev->irq_lock); } @@ -172,7 +165,8 @@ static void adp5588_irq_bus_lock(struct irq_data *d) static void adp5588_irq_bus_sync_unlock(struct irq_data *d) { - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5588_gpio *dev = gpiochip_get_data(gc); int i; for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) { @@ -185,15 +179,9 @@ static void adp5588_irq_bus_sync_unlock(struct irq_data *d) mutex_unlock(&dev->lock); } - if (dev->int_lvl_cached[i] != dev->int_lvl[i]) { - dev->int_lvl_cached[i] = dev->int_lvl[i]; - adp5588_gpio_write(dev->client, GPIO_INT_LVL1 + i, - dev->int_lvl[i]); - } - if (dev->int_en[i] ^ dev->irq_mask[i]) { dev->int_en[i] = dev->irq_mask[i]; - adp5588_gpio_write(dev->client, GPIO_INT_EN1 + i, + adp5588_gpio_write(dev->client, GPI_EM1 + i, dev->int_en[i]); } } @@ -203,41 +191,38 @@ static void adp5588_irq_bus_sync_unlock(struct irq_data *d) static void adp5588_irq_mask(struct irq_data *d) { - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); - unsigned gpio = d->irq - dev->irq_base; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5588_gpio *dev = gpiochip_get_data(gc); - dev->irq_mask[ADP5588_BANK(gpio)] &= ~ADP5588_BIT(gpio); + dev->irq_mask[ADP5588_BANK(d->hwirq)] &= ~ADP5588_BIT(d->hwirq); } static void adp5588_irq_unmask(struct irq_data *d) { - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); - unsigned gpio = d->irq - dev->irq_base; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5588_gpio *dev = gpiochip_get_data(gc); - dev->irq_mask[ADP5588_BANK(gpio)] |= ADP5588_BIT(gpio); + dev->irq_mask[ADP5588_BANK(d->hwirq)] |= ADP5588_BIT(d->hwirq); } static int adp5588_irq_set_type(struct irq_data *d, unsigned int type) { - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); - uint16_t gpio = d->irq - dev->irq_base; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5588_gpio *dev = gpiochip_get_data(gc); + uint16_t gpio = d->hwirq; unsigned bank, bit; - if ((type & IRQ_TYPE_EDGE_BOTH)) { - dev_err(&dev->client->dev, "irq %d: unsupported type %d\n", - d->irq, type); - return -EINVAL; - } - bank = ADP5588_BANK(gpio); bit = ADP5588_BIT(gpio); - if (type & IRQ_TYPE_LEVEL_HIGH) - dev->int_lvl[bank] |= bit; - else if (type & IRQ_TYPE_LEVEL_LOW) - dev->int_lvl[bank] &= ~bit; - else - return -EINVAL; + dev->int_lvl_low[bank] &= ~bit; + dev->int_lvl_high[bank] &= ~bit; + + if (type & IRQ_TYPE_EDGE_BOTH || type & IRQ_TYPE_LEVEL_HIGH) + dev->int_lvl_high[bank] |= bit; + + if (type & IRQ_TYPE_EDGE_BOTH || type & IRQ_TYPE_LEVEL_LOW) + dev->int_lvl_low[bank] |= bit; dev->int_input_en[bank] |= bit; @@ -253,40 +238,32 @@ static struct irq_chip adp5588_irq_chip = { .irq_set_type = adp5588_irq_set_type, }; -static int adp5588_gpio_read_intstat(struct i2c_client *client, u8 *buf) -{ - int ret = i2c_smbus_read_i2c_block_data(client, GPIO_INT_STAT1, 3, buf); - - if (ret < 0) - dev_err(&client->dev, "Read INT_STAT Error\n"); - - return ret; -} - static irqreturn_t adp5588_irq_handler(int irq, void *devid) { struct adp5588_gpio *dev = devid; - unsigned status, bank, bit, pending; - int ret; - status = adp5588_gpio_read(dev->client, INT_STAT); - - if (status & ADP5588_GPI_INT) { - ret = adp5588_gpio_read_intstat(dev->client, dev->irq_stat); - if (ret < 0) - memset(dev->irq_stat, 0, ARRAY_SIZE(dev->irq_stat)); - - for (bank = 0, bit = 0; bank <= ADP5588_BANK(ADP5588_MAXGPIO); - bank++, bit = 0) { - pending = dev->irq_stat[bank] & dev->irq_mask[bank]; - - while (pending) { - if (pending & (1 << bit)) { - handle_nested_irq(dev->irq_base + - (bank << 3) + bit); - pending &= ~(1 << bit); - - } - bit++; + int status = adp5588_gpio_read(dev->client, INT_STAT); + + if (status & ADP5588_KE_INT) { + int ev_cnt = adp5588_gpio_read(dev->client, KEY_LCK_EC_STAT); + + if (ev_cnt > 0) { + int i; + + for (i = 0; i < (ev_cnt & ADP5588_KEC); i++) { + int key = adp5588_gpio_read(dev->client, + Key_EVENTA + i); + /* GPIN events begin at 97, + * bit 7 indicates logic level + */ + int gpio = (key & 0x7f) - 97; + int lvl = key & (1 << 7); + int bank = ADP5588_BANK(gpio); + int bit = ADP5588_BIT(gpio); + + if ((lvl && dev->int_lvl_high[bank] & bit) || + (!lvl && dev->int_lvl_low[bank] & bit)) + handle_nested_irq(irq_find_mapping( + dev->gpio_chip.irq.domain, gpio)); } } } @@ -299,53 +276,42 @@ static irqreturn_t adp5588_irq_handler(int irq, void *devid) static int adp5588_irq_setup(struct adp5588_gpio *dev) { struct i2c_client *client = dev->client; + int ret; struct adp5588_gpio_platform_data *pdata = dev_get_platdata(&client->dev); - unsigned gpio; - int ret; + int irq_base = pdata ? pdata->irq_base : 0; adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC); adp5588_gpio_write(client, INT_STAT, -1); /* status is W1C */ - adp5588_gpio_read_intstat(client, dev->irq_stat); /* read to clear */ - dev->irq_base = pdata->irq_base; mutex_init(&dev->irq_lock); - for (gpio = 0; gpio < dev->gpio_chip.ngpio; gpio++) { - int irq = gpio + dev->irq_base; - irq_set_chip_data(irq, dev); - irq_set_chip_and_handler(irq, &adp5588_irq_chip, - handle_level_irq); - irq_set_nested_thread(irq, 1); - irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE); - } - - ret = request_threaded_irq(client->irq, - NULL, - adp5588_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(&client->dev), dev); + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, adp5588_irq_handler, IRQF_ONESHOT + | IRQF_TRIGGER_FALLING | IRQF_SHARED, + dev_name(&client->dev), dev); if (ret) { dev_err(&client->dev, "failed to request irq %d\n", client->irq); - goto out; + return ret; } + ret = gpiochip_irqchip_add_nested(&dev->gpio_chip, + &adp5588_irq_chip, irq_base, + handle_simple_irq, + IRQ_TYPE_NONE); + if (ret) { + dev_err(&client->dev, + "could not connect irqchip to gpiochip\n"); + return ret; + } + gpiochip_set_nested_irqchip(&dev->gpio_chip, + &adp5588_irq_chip, + client->irq); - dev->gpio_chip.to_irq = adp5588_gpio_to_irq; adp5588_gpio_write(client, CFG, - ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_GPI_INT); + ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_KE_IEN); return 0; - -out: - dev->irq_base = 0; - return ret; -} - -static void adp5588_irq_teardown(struct adp5588_gpio *dev) -{ - if (dev->irq_base) - free_irq(dev->client->irq, dev); } #else @@ -357,24 +323,16 @@ static int adp5588_irq_setup(struct adp5588_gpio *dev) return 0; } -static void adp5588_irq_teardown(struct adp5588_gpio *dev) -{ -} #endif /* CONFIG_GPIO_ADP5588_IRQ */ -static int adp5588_gpio_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adp5588_gpio_probe(struct i2c_client *client) { struct adp5588_gpio_platform_data *pdata = dev_get_platdata(&client->dev); struct adp5588_gpio *dev; struct gpio_chip *gc; int ret, i, revid; - - if (!pdata) { - dev_err(&client->dev, "missing platform data\n"); - return -ENODEV; - } + unsigned int pullup_dis_mask = 0; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -394,18 +352,24 @@ static int adp5588_gpio_probe(struct i2c_client *client, gc->get = adp5588_gpio_get_value; gc->set = adp5588_gpio_set_value; gc->can_sleep = true; + gc->base = -1; + gc->parent = &client->dev; + + if (pdata) { + gc->base = pdata->gpio_start; + gc->names = pdata->names; + pullup_dis_mask = pdata->pullup_dis_mask; + } - gc->base = pdata->gpio_start; gc->ngpio = ADP5588_MAXGPIO; gc->label = client->name; gc->owner = THIS_MODULE; - gc->names = pdata->names; mutex_init(&dev->lock); ret = adp5588_gpio_read(dev->client, DEV_ID); if (ret < 0) - goto err; + return ret; revid = ret & ADP5588_DEVICE_ID_MASK; @@ -414,30 +378,27 @@ static int adp5588_gpio_probe(struct i2c_client *client, dev->dir[i] = adp5588_gpio_read(client, GPIO_DIR1 + i); ret |= adp5588_gpio_write(client, KP_GPIO1 + i, 0); ret |= adp5588_gpio_write(client, GPIO_PULL1 + i, - (pdata->pullup_dis_mask >> (8 * i)) & 0xFF); + (pullup_dis_mask >> (8 * i)) & 0xFF); ret |= adp5588_gpio_write(client, GPIO_INT_EN1 + i, 0); if (ret) - goto err; + return ret; } - if (pdata->irq_base) { + if (client->irq) { if (WA_DELAYED_READOUT_REVID(revid)) { dev_warn(&client->dev, "GPIO int not supported\n"); } else { ret = adp5588_irq_setup(dev); if (ret) - goto err; + return ret; } } ret = devm_gpiochip_add_data(&client->dev, &dev->gpio_chip, dev); if (ret) - goto err_irq; + return ret; - dev_info(&client->dev, "IRQ Base: %d Rev.: %d\n", - pdata->irq_base, revid); - - if (pdata->setup) { + if (pdata && pdata->setup) { ret = pdata->setup(client, gc->base, gc->ngpio, pdata->context); if (ret < 0) dev_warn(&client->dev, "setup failed, %d\n", ret); @@ -446,11 +407,6 @@ static int adp5588_gpio_probe(struct i2c_client *client, i2c_set_clientdata(client, dev); return 0; - -err_irq: - adp5588_irq_teardown(dev); -err: - return ret; } static int adp5588_gpio_remove(struct i2c_client *client) @@ -460,7 +416,7 @@ static int adp5588_gpio_remove(struct i2c_client *client) struct adp5588_gpio *dev = i2c_get_clientdata(client); int ret; - if (pdata->teardown) { + if (pdata && pdata->teardown) { ret = pdata->teardown(client, dev->gpio_chip.base, dev->gpio_chip.ngpio, pdata->context); @@ -470,7 +426,7 @@ static int adp5588_gpio_remove(struct i2c_client *client) } } - if (dev->irq_base) + if (dev->client->irq) free_irq(dev->client->irq, dev); return 0; @@ -480,14 +436,22 @@ static const struct i2c_device_id adp5588_gpio_id[] = { {DRV_NAME, 0}, {} }; - MODULE_DEVICE_TABLE(i2c, adp5588_gpio_id); +#ifdef CONFIG_OF +static const struct of_device_id adp5588_gpio_of_id[] = { + { .compatible = "adi," DRV_NAME, }, + {}, +}; +MODULE_DEVICE_TABLE(of, adp5588_gpio_of_id); +#endif + static struct i2c_driver adp5588_gpio_driver = { .driver = { - .name = DRV_NAME, - }, - .probe = adp5588_gpio_probe, + .name = DRV_NAME, + .of_match_table = of_match_ptr(adp5588_gpio_of_id), + }, + .probe_new = adp5588_gpio_probe, .remove = adp5588_gpio_remove, .id_table = adp5588_gpio_id, }; diff --git a/drivers/gpio/gpio-altera-a10sr.c b/drivers/gpio/gpio-altera-a10sr.c @@ -58,19 +58,20 @@ static void altr_a10sr_gpio_set(struct gpio_chip *chip, unsigned int offset, static int altr_a10sr_gpio_direction_input(struct gpio_chip *gc, unsigned int nr) { - if (nr >= (ALTR_A10SR_IN_VALID_RANGE_LO - ALTR_A10SR_LED_VALID_SHIFT)) - return 0; - return -EINVAL; + if (nr < (ALTR_A10SR_IN_VALID_RANGE_LO - ALTR_A10SR_LED_VALID_SHIFT)) + return -EINVAL; + + return 0; } static int altr_a10sr_gpio_direction_output(struct gpio_chip *gc, unsigned int nr, int value) { - if (nr <= (ALTR_A10SR_OUT_VALID_RANGE_HI - ALTR_A10SR_LED_VALID_SHIFT)) { - altr_a10sr_gpio_set(gc, nr, value); - return 0; - } - return -EINVAL; + if (nr > (ALTR_A10SR_OUT_VALID_RANGE_HI - ALTR_A10SR_LED_VALID_SHIFT)) + return -EINVAL; + + altr_a10sr_gpio_set(gc, nr, value); + return 0; } static const struct gpio_chip altr_a10sr_gc = { diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c @@ -32,9 +32,9 @@ * struct altera_gpio_chip * @mmchip : memory mapped chip structure. * @gpio_lock : synchronization lock so that new irq/set/get requests - will be blocked until the current one completes. +* will be blocked until the current one completes. * @interrupt_trigger : specifies the hardware configured IRQ trigger type - (rising, falling, both, high) +* (rising, falling, both, high) * @mapped_irq : kernel mapped irq number. */ struct altera_gpio_chip { diff --git a/drivers/gpio/gpio-amd-fch.c b/drivers/gpio/gpio-amd-fch.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * GPIO driver for the AMD G series FCH (eg. GX-412TC) + * + * Copyright (C) 2018 metux IT consult + * Author: Enrico Weigelt, metux IT consult <info@metux.net> + * + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio/driver.h> +#include <linux/platform_data/gpio/gpio-amd-fch.h> +#include <linux/spinlock.h> + +#define AMD_FCH_MMIO_BASE 0xFED80000 +#define AMD_FCH_GPIO_BANK0_BASE 0x1500 +#define AMD_FCH_GPIO_SIZE 0x0300 + +#define AMD_FCH_GPIO_FLAG_DIRECTION BIT(23) +#define AMD_FCH_GPIO_FLAG_WRITE BIT(22) +#define AMD_FCH_GPIO_FLAG_READ BIT(16) + +static struct resource amd_fch_gpio_iores = + DEFINE_RES_MEM_NAMED( + AMD_FCH_MMIO_BASE + AMD_FCH_GPIO_BANK0_BASE, + AMD_FCH_GPIO_SIZE, + "amd-fch-gpio-iomem"); + +struct amd_fch_gpio_priv { + struct platform_device *pdev; + struct gpio_chip gc; + void __iomem *base; + struct amd_fch_gpio_pdata *pdata; + spinlock_t lock; +}; + +static void __iomem *amd_fch_gpio_addr(struct amd_fch_gpio_priv *priv, + unsigned int gpio) +{ + return priv->base + priv->pdata->gpio_reg[gpio]*sizeof(u32); +} + +static int amd_fch_gpio_direction_input(struct gpio_chip *gc, + unsigned int offset) +{ + unsigned long flags; + struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); + void __iomem *ptr = amd_fch_gpio_addr(priv, offset); + + spin_lock_irqsave(&priv->lock, flags); + writel_relaxed(readl_relaxed(ptr) & ~AMD_FCH_GPIO_FLAG_DIRECTION, ptr); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int amd_fch_gpio_direction_output(struct gpio_chip *gc, + unsigned int gpio, int value) +{ + unsigned long flags; + struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); + void __iomem *ptr = amd_fch_gpio_addr(priv, gpio); + u32 val; + + spin_lock_irqsave(&priv->lock, flags); + + val = readl_relaxed(ptr); + if (value) + val |= AMD_FCH_GPIO_FLAG_WRITE; + else + val &= ~AMD_FCH_GPIO_FLAG_WRITE; + + writel_relaxed(val | AMD_FCH_GPIO_FLAG_DIRECTION, ptr); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int amd_fch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + int ret; + unsigned long flags; + struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); + void __iomem *ptr = amd_fch_gpio_addr(priv, gpio); + + spin_lock_irqsave(&priv->lock, flags); + ret = (readl_relaxed(ptr) & AMD_FCH_GPIO_FLAG_DIRECTION); + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +static void amd_fch_gpio_set(struct gpio_chip *gc, + unsigned int gpio, int value) +{ + unsigned long flags; + struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); + void __iomem *ptr = amd_fch_gpio_addr(priv, gpio); + u32 mask; + + spin_lock_irqsave(&priv->lock, flags); + + mask = readl_relaxed(ptr); + if (value) + mask |= AMD_FCH_GPIO_FLAG_WRITE; + else + mask &= ~AMD_FCH_GPIO_FLAG_WRITE; + writel_relaxed(mask, ptr); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int amd_fch_gpio_get(struct gpio_chip *gc, + unsigned int offset) +{ + unsigned long flags; + int ret; + struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); + void __iomem *ptr = amd_fch_gpio_addr(priv, offset); + + spin_lock_irqsave(&priv->lock, flags); + ret = (readl_relaxed(ptr) & AMD_FCH_GPIO_FLAG_READ); + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +static int amd_fch_gpio_request(struct gpio_chip *chip, + unsigned int gpio_pin) +{ + return 0; +} + +static int amd_fch_gpio_probe(struct platform_device *pdev) +{ + struct amd_fch_gpio_priv *priv; + struct amd_fch_gpio_pdata *pdata; + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "no platform_data\n"); + return -ENOENT; + } + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->pdata = pdata; + priv->pdev = pdev; + + priv->gc.owner = THIS_MODULE; + priv->gc.parent = &pdev->dev; + priv->gc.label = dev_name(&pdev->dev); + priv->gc.ngpio = priv->pdata->gpio_num; + priv->gc.names = priv->pdata->gpio_names; + priv->gc.base = -1; + priv->gc.request = amd_fch_gpio_request; + priv->gc.direction_input = amd_fch_gpio_direction_input; + priv->gc.direction_output = amd_fch_gpio_direction_output; + priv->gc.get_direction = amd_fch_gpio_get_direction; + priv->gc.get = amd_fch_gpio_get; + priv->gc.set = amd_fch_gpio_set; + + spin_lock_init(&priv->lock); + + priv->base = devm_ioremap_resource(&pdev->dev, &amd_fch_gpio_iores); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + platform_set_drvdata(pdev, priv); + + return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv); +} + +static struct platform_driver amd_fch_gpio_driver = { + .driver = { + .name = AMD_FCH_GPIO_DRIVER_NAME, + }, + .probe = amd_fch_gpio_probe, +}; + +module_platform_driver(amd_fch_gpio_driver); + +MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>"); +MODULE_DESCRIPTION("AMD G-series FCH GPIO driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" AMD_FCH_GPIO_DRIVER_NAME); diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c @@ -1,28 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * gpio-crystalcove.c - Intel Crystal Cove GPIO Driver + * Intel Crystal Cove GPIO Driver * * Copyright (C) 2012, 2014 Intel Corporation. All rights reserved. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * * Author: Yang, Bin <bin.yang@intel.com> */ +#include <linux/bitops.h> +#include <linux/gpio/driver.h> #include <linux/interrupt.h> +#include <linux/mfd/intel_soc_pmic.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/gpio/driver.h> -#include <linux/seq_file.h> -#include <linux/bitops.h> #include <linux/regmap.h> -#include <linux/mfd/intel_soc_pmic.h> +#include <linux/seq_file.h> #define CRYSTALCOVE_GPIO_NUM 16 #define CRYSTALCOVE_VGPIO_NUM 95 @@ -279,8 +271,8 @@ static struct irq_chip crystalcove_irqchip = { static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data) { struct crystalcove_gpio *cg = data; + unsigned long pending; unsigned int p0, p1; - int pending; int gpio; unsigned int virq; @@ -293,11 +285,9 @@ static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data) pending = p0 | p1 << 8; - for (gpio = 0; gpio < CRYSTALCOVE_GPIO_NUM; gpio++) { - if (pending & BIT(gpio)) { - virq = irq_find_mapping(cg->chip.irq.domain, gpio); - handle_nested_irq(virq); - } + for_each_set_bit(gpio, &pending, CRYSTALCOVE_GPIO_NUM) { + virq = irq_find_mapping(cg->chip.irq.domain, gpio); + handle_nested_irq(virq); } return IRQ_HANDLED; diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c @@ -198,7 +198,6 @@ static int davinci_gpio_probe(struct platform_device *pdev) struct davinci_gpio_controller *chips; struct davinci_gpio_platform_data *pdata; struct device *dev = &pdev->dev; - struct resource *res; pdata = davinci_gpio_get_pdata(pdev); if (!pdata) { @@ -236,8 +235,7 @@ static int davinci_gpio_probe(struct platform_device *pdev) if (!chips) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - gpio_base = devm_ioremap_resource(dev, res); + gpio_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(gpio_base)) return PTR_ERR(gpio_base); diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c @@ -432,6 +432,7 @@ static int sprd_eic_irq_set_type(struct irq_data *data, unsigned int flow_type) default: return -ENOTSUPP; } + break; default: dev_err(chip->parent, "Unsupported EIC type.\n"); return -ENOTSUPP; diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c @@ -39,8 +39,10 @@ #define SIO_F71889_ID 0x0909 /* F71889 chipset ID */ #define SIO_F71889A_ID 0x1005 /* F71889A chipset ID */ #define SIO_F81866_ID 0x1010 /* F81866 chipset ID */ +#define SIO_F81804_ID 0x1502 /* F81804 chipset ID, same for f81966 */ -enum chips { f71869, f71869a, f71882fg, f71889a, f71889f, f81866 }; + +enum chips { f71869, f71869a, f71882fg, f71889a, f71889f, f81866, f81804 }; static const char * const f7188x_names[] = { "f71869", @@ -49,6 +51,7 @@ static const char * const f7188x_names[] = { "f71889a", "f71889f", "f81866", + "f81804", }; struct f7188x_sio { @@ -223,6 +226,18 @@ static struct f7188x_gpio_bank f81866_gpio_bank[] = { F7188X_GPIO_BANK(80, 8, 0x88), }; + +static struct f7188x_gpio_bank f81804_gpio_bank[] = { + F7188X_GPIO_BANK(0, 8, 0xF0), + F7188X_GPIO_BANK(10, 8, 0xE0), + F7188X_GPIO_BANK(20, 8, 0xD0), + F7188X_GPIO_BANK(50, 8, 0xA0), + F7188X_GPIO_BANK(60, 8, 0x90), + F7188X_GPIO_BANK(70, 8, 0x80), + F7188X_GPIO_BANK(90, 8, 0x98), +}; + + static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset) { int err; @@ -407,6 +422,10 @@ static int f7188x_gpio_probe(struct platform_device *pdev) data->nr_bank = ARRAY_SIZE(f81866_gpio_bank); data->bank = f81866_gpio_bank; break; + case f81804: + data->nr_bank = ARRAY_SIZE(f81804_gpio_bank); + data->bank = f81804_gpio_bank; + break; default: return -ENODEV; } @@ -469,6 +488,9 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio) case SIO_F81866_ID: sio->type = f81866; break; + case SIO_F81804_ID: + sio->type = f81804; + break; default: pr_info(DRVNAME ": Unsupported Fintek device 0x%04x\n", devid); goto err; diff --git a/drivers/gpio/gpio-ftgpio010.c b/drivers/gpio/gpio-ftgpio010.c @@ -41,12 +41,14 @@ * struct ftgpio_gpio - Gemini GPIO state container * @dev: containing device for this instance * @gc: gpiochip for this instance + * @irq: irqchip for this instance * @base: remapped I/O-memory base * @clk: silicon clock */ struct ftgpio_gpio { struct device *dev; struct gpio_chip gc; + struct irq_chip irq; void __iomem *base; struct clk *clk; }; @@ -134,14 +136,6 @@ static int ftgpio_gpio_set_irq_type(struct irq_data *d, unsigned int type) return 0; } -static struct irq_chip ftgpio_gpio_irqchip = { - .name = "FTGPIO010", - .irq_ack = ftgpio_gpio_ack_irq, - .irq_mask = ftgpio_gpio_mask_irq, - .irq_unmask = ftgpio_gpio_unmask_irq, - .irq_set_type = ftgpio_gpio_set_irq_type, -}; - static void ftgpio_gpio_irq_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); @@ -297,14 +291,20 @@ static int ftgpio_gpio_probe(struct platform_device *pdev) /* Clear any use of debounce */ writel(0x0, g->base + GPIO_DEBOUNCE_EN); - ret = gpiochip_irqchip_add(&g->gc, &ftgpio_gpio_irqchip, + g->irq.name = "FTGPIO010"; + g->irq.irq_ack = ftgpio_gpio_ack_irq; + g->irq.irq_mask = ftgpio_gpio_mask_irq; + g->irq.irq_unmask = ftgpio_gpio_unmask_irq; + g->irq.irq_set_type = ftgpio_gpio_set_irq_type; + + ret = gpiochip_irqchip_add(&g->gc, &g->irq, 0, handle_bad_irq, IRQ_TYPE_NONE); if (ret) { dev_info(dev, "could not add irqchip\n"); goto dis_clk; } - gpiochip_set_chained_irqchip(&g->gc, &ftgpio_gpio_irqchip, + gpiochip_set_chained_irqchip(&g->gc, &g->irq, irq, ftgpio_gpio_irq_handler); platform_set_drvdata(pdev, g); diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Gateworks I2C PLD GPIO expander +// +// Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org> +// +// Based on code and know-how from the OpenWrt driver: +// Copyright (C) 2009 Gateworks Corporation +// Authors: Chris Lang, Imre Kaloz + +#include <linux/bits.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/gpio/driver.h> +#include <linux/i2c.h> +#include <linux/module.h> + +/** + * struct gw_pld - State container for Gateworks PLD + * @chip: GPIO chip instance + * @client: I2C client + * @out: shadow register for the output bute + */ +struct gw_pld { + struct gpio_chip chip; + struct i2c_client *client; + u8 out; +}; + +/* + * The Gateworks I2C PLD chip only expose one read and one write register. + * Writing a "one" bit (to match the reset state) lets that pin be used as an + * input. It is an open-drain model. + */ +static int gw_pld_input8(struct gpio_chip *gc, unsigned offset) +{ + struct gw_pld *gw = gpiochip_get_data(gc); + + gw->out |= BIT(offset); + return i2c_smbus_write_byte(gw->client, gw->out); +} + +static int gw_pld_get8(struct gpio_chip *gc, unsigned offset) +{ + struct gw_pld *gw = gpiochip_get_data(gc); + s32 val; + + val = i2c_smbus_read_byte(gw->client); + + return (val < 0) ? 0 : !!(val & BIT(offset)); +} + +static int gw_pld_output8(struct gpio_chip *gc, unsigned offset, int value) +{ + struct gw_pld *gw = gpiochip_get_data(gc); + + if (value) + gw->out |= BIT(offset); + else + gw->out &= ~BIT(offset); + + return i2c_smbus_write_byte(gw->client, gw->out); +} + +static void gw_pld_set8(struct gpio_chip *gc, unsigned offset, int value) +{ + gw_pld_output8(gc, offset, value); +} + +static int gw_pld_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *np = dev->of_node; + struct gw_pld *gw; + int ret; + + gw = devm_kzalloc(dev, sizeof(*gw), GFP_KERNEL); + if (!gw) + return -ENOMEM; + + gw->chip.base = -1; + gw->chip.can_sleep = true; + gw->chip.parent = dev; + gw->chip.of_node = np; + gw->chip.owner = THIS_MODULE; + gw->chip.label = dev_name(dev); + gw->chip.ngpio = 8; + gw->chip.direction_input = gw_pld_input8; + gw->chip.get = gw_pld_get8; + gw->chip.direction_output = gw_pld_output8; + gw->chip.set = gw_pld_set8; + gw->client = client; + + /* + * The Gateworks I2C PLD chip does not properly send the acknowledge + * bit at all times, but we can still use the standard i2c_smbus + * functions by simply ignoring this bit. + */ + client->flags |= I2C_M_IGNORE_NAK; + gw->out = 0xFF; + + i2c_set_clientdata(client, gw); + + ret = devm_gpiochip_add_data(dev, &gw->chip, gw); + if (ret) + return ret; + + dev_info(dev, "registered Gateworks PLD GPIO device\n"); + + return 0; +} + +static const struct i2c_device_id gw_pld_id[] = { + { "gw-pld", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, gw_pld_id); + +static const struct of_device_id gw_pld_dt_ids[] = { + { .compatible = "gateworks,pld-gpio", }, + { }, +}; +MODULE_DEVICE_TABLE(of, gw_pld_dt_ids); + +static struct i2c_driver gw_pld_driver = { + .driver = { + .name = "gw_pld", + .of_match_table = gw_pld_dt_ids, + }, + .probe = gw_pld_probe, + .id_table = gw_pld_id, +}; +module_i2c_driver(gw_pld_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); diff --git a/drivers/gpio/gpio-hlwd.c b/drivers/gpio/gpio-hlwd.c @@ -48,9 +48,163 @@ struct hlwd_gpio { struct gpio_chip gpioc; + struct irq_chip irqc; void __iomem *regs; + int irq; + u32 edge_emulation; + u32 rising_edge, falling_edge; }; +static void hlwd_gpio_irqhandler(struct irq_desc *desc) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_desc_get_handler_data(desc)); + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long flags; + unsigned long pending; + int hwirq; + u32 emulated_pending; + + spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + pending = ioread32be(hlwd->regs + HW_GPIOB_INTFLAG); + pending &= ioread32be(hlwd->regs + HW_GPIOB_INTMASK); + + /* Treat interrupts due to edge trigger emulation separately */ + emulated_pending = hlwd->edge_emulation & pending; + pending &= ~emulated_pending; + if (emulated_pending) { + u32 level, rising, falling; + + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); + rising = level & emulated_pending; + falling = ~level & emulated_pending; + + /* Invert the levels */ + iowrite32be(level ^ emulated_pending, + hlwd->regs + HW_GPIOB_INTLVL); + + /* Ack all emulated-edge interrupts */ + iowrite32be(emulated_pending, hlwd->regs + HW_GPIOB_INTFLAG); + + /* Signal interrupts only on the correct edge */ + rising &= hlwd->rising_edge; + falling &= hlwd->falling_edge; + + /* Mark emulated interrupts as pending */ + pending |= rising | falling; + } + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); + + chained_irq_enter(chip, desc); + + for_each_set_bit(hwirq, &pending, 32) { + int irq = irq_find_mapping(hlwd->gpioc.irq.domain, hwirq); + + generic_handle_irq(irq); + } + + chained_irq_exit(chip, desc); +} + +static void hlwd_gpio_irq_ack(struct irq_data *data) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + + iowrite32be(BIT(data->hwirq), hlwd->regs + HW_GPIOB_INTFLAG); +} + +static void hlwd_gpio_irq_mask(struct irq_data *data) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + unsigned long flags; + u32 mask; + + spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK); + mask &= ~BIT(data->hwirq); + iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK); + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); +} + +static void hlwd_gpio_irq_unmask(struct irq_data *data) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + unsigned long flags; + u32 mask; + + spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK); + mask |= BIT(data->hwirq); + iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK); + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); +} + +static void hlwd_gpio_irq_enable(struct irq_data *data) +{ + hlwd_gpio_irq_ack(data); + hlwd_gpio_irq_unmask(data); +} + +static void hlwd_gpio_irq_setup_emulation(struct hlwd_gpio *hlwd, int hwirq, + unsigned int flow_type) +{ + u32 level, state; + + /* Set the trigger level to the inactive level */ + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); + state = ioread32be(hlwd->regs + HW_GPIOB_IN) & BIT(hwirq); + level &= ~BIT(hwirq); + level |= state ^ BIT(hwirq); + iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); + + hlwd->edge_emulation |= BIT(hwirq); + hlwd->rising_edge &= ~BIT(hwirq); + hlwd->falling_edge &= ~BIT(hwirq); + if (flow_type & IRQ_TYPE_EDGE_RISING) + hlwd->rising_edge |= BIT(hwirq); + if (flow_type & IRQ_TYPE_EDGE_FALLING) + hlwd->falling_edge |= BIT(hwirq); +} + +static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + unsigned long flags; + u32 level; + + spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + + hlwd->edge_emulation &= ~BIT(data->hwirq); + + switch (flow_type) { + case IRQ_TYPE_LEVEL_HIGH: + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); + level |= BIT(data->hwirq); + iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); + break; + case IRQ_TYPE_LEVEL_LOW: + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); + level &= ~BIT(data->hwirq); + iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); + break; + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_EDGE_BOTH: + hlwd_gpio_irq_setup_emulation(hlwd, data->hwirq, flow_type); + break; + default: + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); + return -EINVAL; + } + + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); + return 0; +} + static int hlwd_gpio_probe(struct platform_device *pdev) { struct hlwd_gpio *hlwd; @@ -92,7 +246,43 @@ static int hlwd_gpio_probe(struct platform_device *pdev) ngpios = 32; hlwd->gpioc.ngpio = ngpios; - return devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd); + res = devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd); + if (res) + return res; + + /* Mask and ack all interrupts */ + iowrite32be(0, hlwd->regs + HW_GPIOB_INTMASK); + iowrite32be(0xffffffff, hlwd->regs + HW_GPIOB_INTFLAG); + + /* + * If this GPIO controller is not marked as an interrupt controller in + * the DT, return. + */ + if (!of_property_read_bool(pdev->dev.of_node, "interrupt-controller")) + return 0; + + hlwd->irq = platform_get_irq(pdev, 0); + if (hlwd->irq < 0) { + dev_info(&pdev->dev, "platform_get_irq returned %d\n", + hlwd->irq); + return hlwd->irq; + } + + hlwd->irqc.name = dev_name(&pdev->dev); + hlwd->irqc.irq_mask = hlwd_gpio_irq_mask; + hlwd->irqc.irq_unmask = hlwd_gpio_irq_unmask; + hlwd->irqc.irq_enable = hlwd_gpio_irq_enable; + hlwd->irqc.irq_set_type = hlwd_gpio_irq_set_type; + + res = gpiochip_irqchip_add(&hlwd->gpioc, &hlwd->irqc, 0, + handle_level_irq, IRQ_TYPE_NONE); + if (res) + return res; + + gpiochip_set_chained_irqchip(&hlwd->gpioc, &hlwd->irqc, + hlwd->irq, hlwd_gpio_irqhandler); + + return 0; } static const struct of_device_id hlwd_gpio_match[] = { diff --git a/drivers/gpio/gpio-madera.c b/drivers/gpio/gpio-madera.c @@ -107,7 +107,7 @@ static void madera_gpio_set(struct gpio_chip *chip, unsigned int offset, MADERA_GPIO1_CTRL_1 + reg_offset, ret); } -static struct gpio_chip madera_gpio_chip = { +static const struct gpio_chip madera_gpio_chip = { .label = "madera", .owner = THIS_MODULE, .request = gpiochip_generic_request, diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c @@ -47,6 +47,7 @@ enum { struct gpio_mockup_line_status { int dir; int value; + int pull; }; struct gpio_mockup_chip { @@ -54,12 +55,13 @@ struct gpio_mockup_chip { struct gpio_mockup_line_status *lines; struct irq_sim irqsim; struct dentry *dbg_dir; + struct mutex lock; }; struct gpio_mockup_dbgfs_private { struct gpio_mockup_chip *chip; struct gpio_desc *desc; - int offset; + unsigned int offset; }; static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_RANGES]; @@ -82,29 +84,66 @@ static int gpio_mockup_range_ngpio(unsigned int index) return gpio_mockup_ranges[index * 2 + 1]; } +static int __gpio_mockup_get(struct gpio_mockup_chip *chip, + unsigned int offset) +{ + return chip->lines[offset].value; +} + static int gpio_mockup_get(struct gpio_chip *gc, unsigned int offset) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + int val; - return chip->lines[offset].value; + mutex_lock(&chip->lock); + val = __gpio_mockup_get(chip, offset); + mutex_unlock(&chip->lock); + + return val; } -static void gpio_mockup_set(struct gpio_chip *gc, - unsigned int offset, int value) +static int gpio_mockup_get_multiple(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + unsigned int bit, val; + + mutex_lock(&chip->lock); + for_each_set_bit(bit, mask, gc->ngpio) { + val = __gpio_mockup_get(chip, bit); + __assign_bit(bit, bits, val); + } + mutex_unlock(&chip->lock); + + return 0; +} +static void __gpio_mockup_set(struct gpio_mockup_chip *chip, + unsigned int offset, int value) +{ chip->lines[offset].value = !!value; } +static void gpio_mockup_set(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + + mutex_lock(&chip->lock); + __gpio_mockup_set(chip, offset, value); + mutex_unlock(&chip->lock); +} + static void gpio_mockup_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { + struct gpio_mockup_chip *chip = gpiochip_get_data(gc); unsigned int bit; + mutex_lock(&chip->lock); for_each_set_bit(bit, mask, gc->ngpio) - gpio_mockup_set(gc, bit, test_bit(bit, bits)); - + __gpio_mockup_set(chip, bit, test_bit(bit, bits)); + mutex_unlock(&chip->lock); } static int gpio_mockup_dirout(struct gpio_chip *gc, @@ -112,8 +151,10 @@ static int gpio_mockup_dirout(struct gpio_chip *gc, { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); - gpio_mockup_set(gc, offset, value); + mutex_lock(&chip->lock); chip->lines[offset].dir = GPIO_MOCKUP_DIR_OUT; + __gpio_mockup_set(chip, offset, value); + mutex_unlock(&chip->lock); return 0; } @@ -122,7 +163,9 @@ static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + mutex_lock(&chip->lock); chip->lines[offset].dir = GPIO_MOCKUP_DIR_IN; + mutex_unlock(&chip->lock); return 0; } @@ -130,8 +173,13 @@ static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset) static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + int direction; - return !chip->lines[offset].dir; + mutex_lock(&chip->lock); + direction = !chip->lines[offset].dir; + mutex_unlock(&chip->lock); + + return direction; } static int gpio_mockup_to_irq(struct gpio_chip *gc, unsigned int offset) @@ -141,15 +189,56 @@ static int gpio_mockup_to_irq(struct gpio_chip *gc, unsigned int offset) return irq_sim_irqnum(&chip->irqsim, offset); } -static ssize_t gpio_mockup_event_write(struct file *file, - const char __user *usr_buf, - size_t size, loff_t *ppos) +static void gpio_mockup_free(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + + __gpio_mockup_set(chip, offset, chip->lines[offset].pull); +} + +static ssize_t gpio_mockup_debugfs_read(struct file *file, + char __user *usr_buf, + size_t size, loff_t *ppos) { struct gpio_mockup_dbgfs_private *priv; struct gpio_mockup_chip *chip; struct seq_file *sfile; + struct gpio_chip *gc; + char buf[3]; + int val, rv; + + if (*ppos != 0) + return 0; + + sfile = file->private_data; + priv = sfile->private; + chip = priv->chip; + gc = &chip->gc; + + val = gpio_mockup_get(gc, priv->offset); + snprintf(buf, sizeof(buf), "%d\n", val); + + rv = copy_to_user(usr_buf, buf, sizeof(buf)); + if (rv) + return rv; + + return sizeof(buf) - 1; +} + +static ssize_t gpio_mockup_debugfs_write(struct file *file, + const char __user *usr_buf, + size_t size, loff_t *ppos) +{ + struct gpio_mockup_dbgfs_private *priv; + int rv, val, curr, irq, irq_type; + struct gpio_mockup_chip *chip; + struct seq_file *sfile; struct gpio_desc *desc; - int rv, val; + struct gpio_chip *gc; + struct irq_sim *sim; + + if (*ppos != 0) + return -EINVAL; rv = kstrtoint_from_user(usr_buf, size, 0, &val); if (rv) @@ -159,24 +248,70 @@ static ssize_t gpio_mockup_event_write(struct file *file, sfile = file->private_data; priv = sfile->private; - desc = priv->desc; chip = priv->chip; + gc = &chip->gc; + desc = &gc->gpiodev->descs[priv->offset]; + sim = &chip->irqsim; + + mutex_lock(&chip->lock); + + if (test_bit(FLAG_REQUESTED, &desc->flags) && + !test_bit(FLAG_IS_OUT, &desc->flags)) { + curr = __gpio_mockup_get(chip, priv->offset); + if (curr == val) + goto out; + + irq = irq_sim_irqnum(sim, priv->offset); + irq_type = irq_get_trigger_type(irq); + + if ((val == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) || + (val == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING))) + irq_sim_fire(sim, priv->offset); + } + + /* Change the value unless we're actively driving the line. */ + if (!test_bit(FLAG_REQUESTED, &desc->flags) || + !test_bit(FLAG_IS_OUT, &desc->flags)) + __gpio_mockup_set(chip, priv->offset, val); - gpiod_set_value_cansleep(desc, val); - irq_sim_fire(&chip->irqsim, priv->offset); +out: + chip->lines[priv->offset].pull = val; + mutex_unlock(&chip->lock); return size; } -static int gpio_mockup_event_open(struct inode *inode, struct file *file) +static int gpio_mockup_debugfs_open(struct inode *inode, struct file *file) { return single_open(file, NULL, inode->i_private); } -static const struct file_operations gpio_mockup_event_ops = { +/* + * Each mockup chip is represented by a directory named after the chip's device + * name under /sys/kernel/debug/gpio-mockup/. Each line is represented by + * a file using the line's offset as the name under the chip's directory. + * + * Reading from the line's file yields the current *value*, writing to the + * line's file changes the current *pull*. Default pull for mockup lines is + * down. + * + * Examples: + * - when a line pulled down is requested in output mode and driven high, its + * value will return to 0 once it's released + * - when the line is requested in output mode and driven high, writing 0 to + * the corresponding debugfs file will change the pull to down but the + * reported value will still be 1 until the line is released + * - line requested in input mode always reports the same value as its pull + * configuration + * - when the line is requested in input mode and monitored for events, writing + * the same value to the debugfs file will be a noop, while writing the + * opposite value will generate a dummy interrupt with an appropriate edge + */ +static const struct file_operations gpio_mockup_debugfs_ops = { .owner = THIS_MODULE, - .open = gpio_mockup_event_open, - .write = gpio_mockup_event_write, + .open = gpio_mockup_debugfs_open, + .read = gpio_mockup_debugfs_read, + .write = gpio_mockup_debugfs_write, .llseek = no_llseek, }; @@ -184,7 +319,7 @@ static void gpio_mockup_debugfs_setup(struct device *dev, struct gpio_mockup_chip *chip) { struct gpio_mockup_dbgfs_private *priv; - struct dentry *evfile, *link; + struct dentry *evfile; struct gpio_chip *gc; const char *devname; char *name; @@ -197,10 +332,6 @@ static void gpio_mockup_debugfs_setup(struct device *dev, if (IS_ERR_OR_NULL(chip->dbg_dir)) goto err; - link = debugfs_create_symlink(gc->label, gpio_mockup_dbg_dir, devname); - if (IS_ERR_OR_NULL(link)) - goto err; - for (i = 0; i < gc->ngpio; i++) { name = devm_kasprintf(dev, GFP_KERNEL, "%d", i); if (!name) @@ -215,7 +346,7 @@ static void gpio_mockup_debugfs_setup(struct device *dev, priv->desc = &gc->gpiodev->descs[i]; evfile = debugfs_create_file(name, 0200, chip->dbg_dir, priv, - &gpio_mockup_event_ops); + &gpio_mockup_debugfs_ops); if (IS_ERR_OR_NULL(evfile)) goto err; } @@ -223,7 +354,7 @@ static void gpio_mockup_debugfs_setup(struct device *dev, return; err: - dev_err(dev, "error creating debugfs event files\n"); + dev_err(dev, "error creating debugfs files\n"); } static int gpio_mockup_name_lines(struct device *dev, @@ -283,6 +414,8 @@ static int gpio_mockup_probe(struct platform_device *pdev) return -ENOMEM; } + mutex_init(&chip->lock); + gc = &chip->gc; gc->base = base; gc->ngpio = ngpio; @@ -291,11 +424,13 @@ static int gpio_mockup_probe(struct platform_device *pdev) gc->parent = dev; gc->get = gpio_mockup_get; gc->set = gpio_mockup_set; + gc->get_multiple = gpio_mockup_get_multiple; gc->set_multiple = gpio_mockup_set_multiple; gc->direction_output = gpio_mockup_dirout; gc->direction_input = gpio_mockup_dirin; gc->get_direction = gpio_mockup_get_direction; gc->to_irq = gpio_mockup_to_irq; + gc->free = gpio_mockup_free; chip->lines = devm_kcalloc(dev, gc->ngpio, sizeof(*chip->lines), GFP_KERNEL); @@ -369,7 +504,7 @@ static int __init gpio_mockup_init(void) return -EINVAL; } - gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup-event", NULL); + gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup", NULL); if (IS_ERR_OR_NULL(gpio_mockup_dbg_dir)) gpio_mockup_err("error creating debugfs directory\n"); diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c @@ -1,32 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel Medfield MSIC GPIO driver> * Copyright (c) 2011, Intel Corporation. * * Author: Mathias Nyman <mathias.nyman@linux.intel.com> * Based on intel_pmic_gpio.c - * - * 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. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * */ -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/init.h> #include <linux/gpio/driver.h> -#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> #include <linux/mfd/intel_msic.h> +#include <linux/platform_device.h> +#include <linux/slab.h> /* the offset for the mapping of global gpio pin to irq */ #define MSIC_GPIO_IRQ_OFFSET 0x100 @@ -237,20 +224,17 @@ static void msic_gpio_irq_handler(struct irq_desc *desc) struct msic_gpio *mg = irq_data_get_irq_handler_data(data); struct irq_chip *chip = irq_data_get_irq_chip(data); struct intel_msic *msic = pdev_to_intel_msic(mg->pdev); + unsigned long pending; int i; int bitnr; u8 pin; - unsigned long pending = 0; for (i = 0; i < (mg->chip.ngpio / BITS_PER_BYTE); i++) { intel_msic_irq_read(msic, INTEL_MSIC_GPIO0LVIRQ + i, &pin); pending = pin; - if (pending) { - for_each_set_bit(bitnr, &pending, BITS_PER_BYTE) - generic_handle_irq(mg->irq_base + - (i * BITS_PER_BYTE) + bitnr); - } + for_each_set_bit(bitnr, &pending, BITS_PER_BYTE) + generic_handle_irq(mg->irq_base + i * BITS_PER_BYTE + bitnr); } chip->irq_eoi(data); } diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c @@ -376,6 +376,16 @@ static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned int pin, return 0; } +static int mvebu_gpio_get_direction(struct gpio_chip *chip, unsigned int pin) +{ + struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); + u32 u; + + regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &u); + + return !!(u & BIT(pin)); +} + static int mvebu_gpio_to_irq(struct gpio_chip *chip, unsigned int pin) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); @@ -1130,6 +1140,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev) mvchip->chip.parent = &pdev->dev; mvchip->chip.request = gpiochip_generic_request; mvchip->chip.free = gpiochip_generic_free; + mvchip->chip.get_direction = mvebu_gpio_get_direction; mvchip->chip.direction_input = mvebu_gpio_direction_input; mvchip->chip.get = mvebu_gpio_get; mvchip->chip.direction_output = mvebu_gpio_direction_output; diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c @@ -438,8 +438,11 @@ static int mxc_gpio_probe(struct platform_device *pdev) /* the controller clock is optional */ port->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(port->clk)) + if (IS_ERR(port->clk)) { + if (PTR_ERR(port->clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; port->clk = NULL; + } err = clk_prepare_enable(port->clk); if (err) { diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c @@ -883,14 +883,16 @@ static void omap_gpio_unmask_irq(struct irq_data *d) if (trigger) omap_set_gpio_triggering(bank, offset, trigger); - /* For level-triggered GPIOs, the clearing must be done after - * the HW source is cleared, thus after the handler has run */ - if (bank->level_mask & BIT(offset)) { - omap_set_gpio_irqenable(bank, offset, 0); + omap_set_gpio_irqenable(bank, offset, 1); + + /* + * For level-triggered GPIOs, clearing must be done after the source + * is cleared, thus after the handler has run. OMAP4 needs this done + * after enabing the interrupt to clear the wakeup status. + */ + if (bank->level_mask & BIT(offset)) omap_clear_gpio_irqstatus(bank, offset); - } - omap_set_gpio_irqenable(bank, offset, 1); raw_spin_unlock_irqrestore(&bank->lock, flags); } diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c @@ -65,7 +65,7 @@ #define PCA_INT 0x0100 #define PCA_PCAL 0x0200 -#define PCA_LATCH_INT (PCA_PCAL | PCA_INT) +#define PCA_LATCH_INT (PCA_PCAL | PCA_INT) #define PCA953X_TYPE 0x1000 #define PCA957X_TYPE 0x2000 #define PCA_TYPE_MASK 0xF000 @@ -88,8 +88,9 @@ static const struct i2c_device_id pca953x_id[] = { { "pca9575", 16 | PCA957X_TYPE | PCA_INT, }, { "pca9698", 40 | PCA953X_TYPE, }, - { "pcal6524", 24 | PCA953X_TYPE | PCA_INT | PCA_PCAL, }, - { "pcal9555a", 16 | PCA953X_TYPE | PCA_INT | PCA_PCAL, }, + { "pcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, + { "pcal6524", 24 | PCA953X_TYPE | PCA_LATCH_INT, }, + { "pcal9555a", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { "max7310", 8 | PCA953X_TYPE, }, { "max7312", 16 | PCA953X_TYPE | PCA_INT, }, @@ -108,7 +109,7 @@ static const struct i2c_device_id pca953x_id[] = { MODULE_DEVICE_TABLE(i2c, pca953x_id); static const struct acpi_device_id pca953x_acpi_ids[] = { - { "INT3491", 16 | PCA953X_TYPE | PCA_INT | PCA_PCAL, }, + { "INT3491", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { } }; MODULE_DEVICE_TABLE(acpi, pca953x_acpi_ids); @@ -150,6 +151,7 @@ struct pca953x_chip { u8 irq_stat[MAX_BANK]; u8 irq_trig_raise[MAX_BANK]; u8 irq_trig_fall[MAX_BANK]; + struct irq_chip irq_chip; #endif struct i2c_client *client; @@ -178,6 +180,8 @@ static int pca953x_bank_shift(struct pca953x_chip *chip) #define PCA957x_BANK_OUTPUT BIT(5) #define PCAL9xxx_BANK_IN_LATCH BIT(8 + 2) +#define PCAL9xxx_BANK_PULL_EN BIT(8 + 3) +#define PCAL9xxx_BANK_PULL_SEL BIT(8 + 4) #define PCAL9xxx_BANK_IRQ_MASK BIT(8 + 5) #define PCAL9xxx_BANK_IRQ_STAT BIT(8 + 6) @@ -199,6 +203,8 @@ static int pca953x_bank_shift(struct pca953x_chip *chip) * - Extended set, above 0x40, often chip specific. * - PCAL6524/PCAL9555A with custom PCAL IRQ handling: * Input latch register 0x40 + 2 * bank_size RW + * Pull-up/pull-down enable reg 0x40 + 3 * bank_size RW + * Pull-up/pull-down select reg 0x40 + 4 * bank_size RW * Interrupt mask register 0x40 + 5 * bank_size RW * Interrupt status register 0x40 + 6 * bank_size R * @@ -247,7 +253,8 @@ static bool pca953x_readable_register(struct device *dev, unsigned int reg) } if (chip->driver_data & PCA_PCAL) { - bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_IRQ_MASK | + bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_PULL_EN | + PCAL9xxx_BANK_PULL_SEL | PCAL9xxx_BANK_IRQ_MASK | PCAL9xxx_BANK_IRQ_STAT; } @@ -268,7 +275,8 @@ static bool pca953x_writeable_register(struct device *dev, unsigned int reg) } if (chip->driver_data & PCA_PCAL) - bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_IRQ_MASK; + bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_PULL_EN | + PCAL9xxx_BANK_PULL_SEL | PCAL9xxx_BANK_IRQ_MASK; return pca953x_check_register(chip, reg, bank); } @@ -473,6 +481,61 @@ exit: mutex_unlock(&chip->i2c_lock); } +static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip, + unsigned int offset, + unsigned long config) +{ + u8 pull_en_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_EN, offset, + true, false); + u8 pull_sel_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_SEL, offset, + true, false); + u8 bit = BIT(offset % BANK_SZ); + int ret; + + /* + * pull-up/pull-down configuration requires PCAL extended + * registers + */ + if (!(chip->driver_data & PCA_PCAL)) + return -ENOTSUPP; + + mutex_lock(&chip->i2c_lock); + + /* Disable pull-up/pull-down */ + ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, 0); + if (ret) + goto exit; + + /* Configure pull-up/pull-down */ + if (config == PIN_CONFIG_BIAS_PULL_UP) + ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, bit); + else if (config == PIN_CONFIG_BIAS_PULL_DOWN) + ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, 0); + if (ret) + goto exit; + + /* Enable pull-up/pull-down */ + ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, bit); + +exit: + mutex_unlock(&chip->i2c_lock); + return ret; +} + +static int pca953x_gpio_set_config(struct gpio_chip *gc, unsigned int offset, + unsigned long config) +{ + struct pca953x_chip *chip = gpiochip_get_data(gc); + + switch (config) { + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + return pca953x_gpio_set_pull_up_down(chip, offset, config); + default: + return -ENOTSUPP; + } +} + static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) { struct gpio_chip *gc; @@ -485,6 +548,7 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) gc->set = pca953x_gpio_set_value; gc->get_direction = pca953x_gpio_get_direction; gc->set_multiple = pca953x_gpio_set_multiple; + gc->set_config = pca953x_gpio_set_config; gc->can_sleep = true; gc->base = chip->gpio_start; @@ -512,6 +576,14 @@ static void pca953x_irq_unmask(struct irq_data *d) chip->irq_mask[d->hwirq / BANK_SZ] |= 1 << (d->hwirq % BANK_SZ); } +static int pca953x_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct pca953x_chip *chip = gpiochip_get_data(gc); + + return irq_set_irq_wake(chip->client->irq, on); +} + static void pca953x_irq_bus_lock(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); @@ -587,23 +659,14 @@ static int pca953x_irq_set_type(struct irq_data *d, unsigned int type) static void pca953x_irq_shutdown(struct irq_data *d) { - struct pca953x_chip *chip = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct pca953x_chip *chip = gpiochip_get_data(gc); u8 mask = 1 << (d->hwirq % BANK_SZ); chip->irq_trig_raise[d->hwirq / BANK_SZ] &= ~mask; chip->irq_trig_fall[d->hwirq / BANK_SZ] &= ~mask; } -static struct irq_chip pca953x_irq_chip = { - .name = "pca953x", - .irq_mask = pca953x_irq_mask, - .irq_unmask = pca953x_irq_unmask, - .irq_bus_lock = pca953x_irq_bus_lock, - .irq_bus_sync_unlock = pca953x_irq_bus_sync_unlock, - .irq_set_type = pca953x_irq_set_type, - .irq_shutdown = pca953x_irq_shutdown, -}; - static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending) { u8 cur_stat[MAX_BANK]; @@ -699,56 +762,65 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) { struct i2c_client *client = chip->client; + struct irq_chip *irq_chip = &chip->irq_chip; int reg_direction[MAX_BANK]; int ret, i; - if (client->irq && irq_base != -1 - && (chip->driver_data & PCA_INT)) { - ret = pca953x_read_regs(chip, - chip->regs->input, chip->irq_stat); - if (ret) - return ret; + if (!client->irq) + return 0; - /* - * There is no way to know which GPIO line generated the - * interrupt. We have to rely on the previous read for - * this purpose. - */ - regmap_bulk_read(chip->regmap, chip->regs->direction, - reg_direction, NBANK(chip)); - for (i = 0; i < NBANK(chip); i++) - chip->irq_stat[i] &= reg_direction[i]; - mutex_init(&chip->irq_lock); - - ret = devm_request_threaded_irq(&client->dev, - client->irq, - NULL, - pca953x_irq_handler, - IRQF_TRIGGER_LOW | IRQF_ONESHOT | - IRQF_SHARED, - dev_name(&client->dev), chip); - if (ret) { - dev_err(&client->dev, "failed to request irq %d\n", - client->irq); - return ret; - } + if (irq_base == -1) + return 0; - ret = gpiochip_irqchip_add_nested(&chip->gpio_chip, - &pca953x_irq_chip, - irq_base, - handle_simple_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(&client->dev, - "could not connect irqchip to gpiochip\n"); - return ret; - } + if (!(chip->driver_data & PCA_INT)) + return 0; - gpiochip_set_nested_irqchip(&chip->gpio_chip, - &pca953x_irq_chip, - client->irq); + ret = pca953x_read_regs(chip, chip->regs->input, chip->irq_stat); + if (ret) + return ret; + + /* + * There is no way to know which GPIO line generated the + * interrupt. We have to rely on the previous read for + * this purpose. + */ + regmap_bulk_read(chip->regmap, chip->regs->direction, reg_direction, + NBANK(chip)); + for (i = 0; i < NBANK(chip); i++) + chip->irq_stat[i] &= reg_direction[i]; + mutex_init(&chip->irq_lock); + + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, pca953x_irq_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT | + IRQF_SHARED, + dev_name(&client->dev), chip); + if (ret) { + dev_err(&client->dev, "failed to request irq %d\n", + client->irq); + return ret; } + irq_chip->name = dev_name(&chip->client->dev); + irq_chip->irq_mask = pca953x_irq_mask; + irq_chip->irq_unmask = pca953x_irq_unmask; + irq_chip->irq_set_wake = pca953x_irq_set_wake; + irq_chip->irq_bus_lock = pca953x_irq_bus_lock; + irq_chip->irq_bus_sync_unlock = pca953x_irq_bus_sync_unlock; + irq_chip->irq_set_type = pca953x_irq_set_type; + irq_chip->irq_shutdown = pca953x_irq_shutdown; + + ret = gpiochip_irqchip_add_nested(&chip->gpio_chip, irq_chip, + irq_base, handle_simple_irq, + IRQ_TYPE_NONE); + if (ret) { + dev_err(&client->dev, + "could not connect irqchip to gpiochip\n"); + return ret; + } + + gpiochip_set_nested_irqchip(&chip->gpio_chip, irq_chip, client->irq); + return 0; } diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c @@ -89,7 +89,6 @@ struct pcf857x { struct mutex lock; /* protect 'out' */ unsigned out; /* software latch */ unsigned status; /* current status */ - unsigned int irq_parent; unsigned irq_enabled; /* enabled irqs */ int (*write)(struct i2c_client *client, unsigned data); @@ -211,18 +210,7 @@ static int pcf857x_irq_set_wake(struct irq_data *data, unsigned int on) { struct pcf857x *gpio = irq_data_get_irq_chip_data(data); - int error = 0; - - if (gpio->irq_parent) { - error = irq_set_irq_wake(gpio->irq_parent, on); - if (error) { - dev_dbg(&gpio->client->dev, - "irq %u doesn't support irq_set_wake\n", - gpio->irq_parent); - gpio->irq_parent = 0; - } - } - return error; + return irq_set_irq_wake(gpio->client->irq, on); } static void pcf857x_irq_enable(struct irq_data *data) @@ -392,7 +380,6 @@ static int pcf857x_probe(struct i2c_client *client, gpiochip_set_nested_irqchip(&gpio->chip, &gpio->irqchip, client->irq); - gpio->irq_parent = client->irq; } /* Let platform code set up the GPIOs and their users. diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c @@ -322,7 +322,6 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(&pdev->dev, pmic_eic->irq, NULL, sprd_pmic_eic_irq_handler, - IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_NO_SUSPEND, dev_name(&pdev->dev), pmic_eic); if (ret) { @@ -365,7 +364,7 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev) } static const struct of_device_id sprd_pmic_eic_of_match[] = { - { .compatible = "sprd,sc27xx-eic", }, + { .compatible = "sprd,sc2731-eic", }, { /* end of list */ } }; MODULE_DEVICE_TABLE(of, sprd_pmic_eic_of_match); diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c @@ -40,6 +40,7 @@ struct gpio_rcar_priv { struct irq_chip irq_chip; unsigned int irq_parent; atomic_t wakeup_path; + bool has_outdtsel; bool has_both_edge_trigger; struct gpio_rcar_bank_info bank_info; }; @@ -55,6 +56,7 @@ struct gpio_rcar_priv { #define POSNEG 0x20 /* Positive/Negative Logic Select Register */ #define EDGLEVEL 0x24 /* Edge/level Select Register */ #define FILONOFF 0x28 /* Chattering Prevention On/Off Register */ +#define OUTDTSEL 0x40 /* Output Data Select Register */ #define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */ #define RCAR_MAX_GPIO_PER_BANK 32 @@ -235,6 +237,10 @@ static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip, /* Select Input Mode or Output Mode in INOUTSEL */ gpio_rcar_modify_bit(p, INOUTSEL, gpio, output); + /* Select General Output Register to output data in OUTDTSEL */ + if (p->has_outdtsel && output) + gpio_rcar_modify_bit(p, OUTDTSEL, gpio, false); + spin_unlock_irqrestore(&p->lock, flags); } @@ -336,14 +342,17 @@ static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset, } struct gpio_rcar_info { + bool has_outdtsel; bool has_both_edge_trigger; }; static const struct gpio_rcar_info gpio_rcar_info_gen1 = { + .has_outdtsel = false, .has_both_edge_trigger = false, }; static const struct gpio_rcar_info gpio_rcar_info_gen2 = { + .has_outdtsel = true, .has_both_edge_trigger = true, }; @@ -403,10 +412,11 @@ static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins) int ret; info = of_device_get_match_data(p->dev); + p->has_outdtsel = info->has_outdtsel; + p->has_both_edge_trigger = info->has_both_edge_trigger; ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args); *npins = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK; - p->has_both_edge_trigger = info->has_both_edge_trigger; if (*npins == 0 || *npins > RCAR_MAX_GPIO_PER_BANK) { dev_warn(p->dev, "Invalid number of gpio lines %u, using %u\n", diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c @@ -109,16 +109,6 @@ static int sama5d2_piobu_read_value(struct gpio_chip *chip, unsigned int pin, } /** - * sama5d2_piobu_set_direction() - mark pin as input or output - */ -static int sama5d2_piobu_set_direction(struct gpio_chip *chip, - unsigned int direction, - unsigned int pin) -{ - return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, direction); -} - -/** * sama5d2_piobu_get_direction() - gpiochip get_direction */ static int sama5d2_piobu_get_direction(struct gpio_chip *chip, @@ -138,7 +128,7 @@ static int sama5d2_piobu_get_direction(struct gpio_chip *chip, static int sama5d2_piobu_direction_input(struct gpio_chip *chip, unsigned int pin) { - return sama5d2_piobu_set_direction(chip, PIOBU_IN, pin); + return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, PIOBU_IN); } /** @@ -147,7 +137,13 @@ static int sama5d2_piobu_direction_input(struct gpio_chip *chip, static int sama5d2_piobu_direction_output(struct gpio_chip *chip, unsigned int pin, int value) { - return sama5d2_piobu_set_direction(chip, PIOBU_OUT, pin); + unsigned int val = PIOBU_OUT; + + if (value) + val |= PIOBU_HIGH; + + return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION | PIOBU_SOD, + val); } /** diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c @@ -2,6 +2,7 @@ * arch/arm/mach-tegra/gpio.c * * Copyright (c) 2010 Google, Inc + * Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved. * * Author: * Erik Gilling <konkers@google.com> @@ -141,14 +142,14 @@ static void tegra_gpio_disable(struct tegra_gpio_info *tgi, unsigned int gpio) static int tegra_gpio_request(struct gpio_chip *chip, unsigned int offset) { - return pinctrl_gpio_request(offset); + return pinctrl_gpio_request(chip->base + offset); } static void tegra_gpio_free(struct gpio_chip *chip, unsigned int offset) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); - pinctrl_gpio_free(offset); + pinctrl_gpio_free(chip->base + offset); tegra_gpio_disable(tgi, offset); } @@ -176,10 +177,18 @@ static int tegra_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); + int ret; tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 0); tegra_gpio_enable(tgi, offset); - return 0; + + ret = pinctrl_gpio_direction_input(chip->base + offset); + if (ret < 0) + dev_err(tgi->dev, + "Failed to set pinctrl input direction of GPIO %d: %d", + chip->base + offset, ret); + + return ret; } static int tegra_gpio_direction_output(struct gpio_chip *chip, @@ -187,11 +196,19 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip, int value) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); + int ret; tegra_gpio_set(chip, offset, value); tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 1); tegra_gpio_enable(tgi, offset); - return 0; + + ret = pinctrl_gpio_direction_output(chip->base + offset); + if (ret < 0) + dev_err(tgi->dev, + "Failed to set pinctrl output direction of GPIO %d: %d", + chip->base + offset, ret); + + return ret; } static int tegra_gpio_get_direction(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c @@ -529,8 +529,8 @@ static int tegra186_gpio_remove(struct platform_device *pdev) return 0; } -#define TEGRA_MAIN_GPIO_PORT(port, base, count, controller) \ - [TEGRA_MAIN_GPIO_PORT_##port] = { \ +#define TEGRA186_MAIN_GPIO_PORT(port, base, count, controller) \ + [TEGRA186_MAIN_GPIO_PORT_##port] = { \ .name = #port, \ .offset = base, \ .pins = count, \ @@ -538,29 +538,29 @@ static int tegra186_gpio_remove(struct platform_device *pdev) } static const struct tegra_gpio_port tegra186_main_ports[] = { - TEGRA_MAIN_GPIO_PORT( A, 0x2000, 7, 2), - TEGRA_MAIN_GPIO_PORT( B, 0x3000, 7, 3), - TEGRA_MAIN_GPIO_PORT( C, 0x3200, 7, 3), - TEGRA_MAIN_GPIO_PORT( D, 0x3400, 6, 3), - TEGRA_MAIN_GPIO_PORT( E, 0x2200, 8, 2), - TEGRA_MAIN_GPIO_PORT( F, 0x2400, 6, 2), - TEGRA_MAIN_GPIO_PORT( G, 0x4200, 6, 4), - TEGRA_MAIN_GPIO_PORT( H, 0x1000, 7, 1), - TEGRA_MAIN_GPIO_PORT( I, 0x0800, 8, 0), - TEGRA_MAIN_GPIO_PORT( J, 0x5000, 8, 5), - TEGRA_MAIN_GPIO_PORT( K, 0x5200, 1, 5), - TEGRA_MAIN_GPIO_PORT( L, 0x1200, 8, 1), - TEGRA_MAIN_GPIO_PORT( M, 0x5600, 6, 5), - TEGRA_MAIN_GPIO_PORT( N, 0x0000, 7, 0), - TEGRA_MAIN_GPIO_PORT( O, 0x0200, 4, 0), - TEGRA_MAIN_GPIO_PORT( P, 0x4000, 7, 4), - TEGRA_MAIN_GPIO_PORT( Q, 0x0400, 6, 0), - TEGRA_MAIN_GPIO_PORT( R, 0x0a00, 6, 0), - TEGRA_MAIN_GPIO_PORT( T, 0x0600, 4, 0), - TEGRA_MAIN_GPIO_PORT( X, 0x1400, 8, 1), - TEGRA_MAIN_GPIO_PORT( Y, 0x1600, 7, 1), - TEGRA_MAIN_GPIO_PORT(BB, 0x2600, 2, 2), - TEGRA_MAIN_GPIO_PORT(CC, 0x5400, 4, 5), + TEGRA186_MAIN_GPIO_PORT( A, 0x2000, 7, 2), + TEGRA186_MAIN_GPIO_PORT( B, 0x3000, 7, 3), + TEGRA186_MAIN_GPIO_PORT( C, 0x3200, 7, 3), + TEGRA186_MAIN_GPIO_PORT( D, 0x3400, 6, 3), + TEGRA186_MAIN_GPIO_PORT( E, 0x2200, 8, 2), + TEGRA186_MAIN_GPIO_PORT( F, 0x2400, 6, 2), + TEGRA186_MAIN_GPIO_PORT( G, 0x4200, 6, 4), + TEGRA186_MAIN_GPIO_PORT( H, 0x1000, 7, 1), + TEGRA186_MAIN_GPIO_PORT( I, 0x0800, 8, 0), + TEGRA186_MAIN_GPIO_PORT( J, 0x5000, 8, 5), + TEGRA186_MAIN_GPIO_PORT( K, 0x5200, 1, 5), + TEGRA186_MAIN_GPIO_PORT( L, 0x1200, 8, 1), + TEGRA186_MAIN_GPIO_PORT( M, 0x5600, 6, 5), + TEGRA186_MAIN_GPIO_PORT( N, 0x0000, 7, 0), + TEGRA186_MAIN_GPIO_PORT( O, 0x0200, 4, 0), + TEGRA186_MAIN_GPIO_PORT( P, 0x4000, 7, 4), + TEGRA186_MAIN_GPIO_PORT( Q, 0x0400, 6, 0), + TEGRA186_MAIN_GPIO_PORT( R, 0x0a00, 6, 0), + TEGRA186_MAIN_GPIO_PORT( T, 0x0600, 4, 0), + TEGRA186_MAIN_GPIO_PORT( X, 0x1400, 8, 1), + TEGRA186_MAIN_GPIO_PORT( Y, 0x1600, 7, 1), + TEGRA186_MAIN_GPIO_PORT(BB, 0x2600, 2, 2), + TEGRA186_MAIN_GPIO_PORT(CC, 0x5400, 4, 5), }; static const struct tegra_gpio_soc tegra186_main_soc = { @@ -569,8 +569,8 @@ static const struct tegra_gpio_soc tegra186_main_soc = { .name = "tegra186-gpio", }; -#define TEGRA_AON_GPIO_PORT(port, base, count, controller) \ - [TEGRA_AON_GPIO_PORT_##port] = { \ +#define TEGRA186_AON_GPIO_PORT(port, base, count, controller) \ + [TEGRA186_AON_GPIO_PORT_##port] = { \ .name = #port, \ .offset = base, \ .pins = count, \ @@ -578,14 +578,14 @@ static const struct tegra_gpio_soc tegra186_main_soc = { } static const struct tegra_gpio_port tegra186_aon_ports[] = { - TEGRA_AON_GPIO_PORT( S, 0x0200, 5, 0), - TEGRA_AON_GPIO_PORT( U, 0x0400, 6, 0), - TEGRA_AON_GPIO_PORT( V, 0x0800, 8, 0), - TEGRA_AON_GPIO_PORT( W, 0x0a00, 8, 0), - TEGRA_AON_GPIO_PORT( Z, 0x0e00, 4, 0), - TEGRA_AON_GPIO_PORT(AA, 0x0c00, 8, 0), - TEGRA_AON_GPIO_PORT(EE, 0x0600, 3, 0), - TEGRA_AON_GPIO_PORT(FF, 0x0000, 5, 0), + TEGRA186_AON_GPIO_PORT( S, 0x0200, 5, 0), + TEGRA186_AON_GPIO_PORT( U, 0x0400, 6, 0), + TEGRA186_AON_GPIO_PORT( V, 0x0800, 8, 0), + TEGRA186_AON_GPIO_PORT( W, 0x0a00, 8, 0), + TEGRA186_AON_GPIO_PORT( Z, 0x0e00, 4, 0), + TEGRA186_AON_GPIO_PORT(AA, 0x0c00, 8, 0), + TEGRA186_AON_GPIO_PORT(EE, 0x0600, 3, 0), + TEGRA186_AON_GPIO_PORT(FF, 0x0000, 5, 0), }; static const struct tegra_gpio_soc tegra186_aon_soc = { diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TQ-Systems TQMx86 PLD GPIO driver + * + * Based on vendor driver by: + * Vadim V.Vlasov <vvlasov@dev.rtsoft.ru> + */ + +#include <linux/bitops.h> +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> + +#define TQMX86_NGPIO 8 +#define TQMX86_NGPO 4 /* 0-3 - output */ +#define TQMX86_NGPI 4 /* 4-7 - input */ +#define TQMX86_DIR_INPUT_MASK 0xf0 /* 0-3 - output, 4-7 - input */ + +#define TQMX86_GPIODD 0 /* GPIO Data Direction Register */ +#define TQMX86_GPIOD 1 /* GPIO Data Register */ +#define TQMX86_GPIIC 3 /* GPI Interrupt Configuration Register */ +#define TQMX86_GPIIS 4 /* GPI Interrupt Status Register */ + +#define TQMX86_GPII_FALLING BIT(0) +#define TQMX86_GPII_RISING BIT(1) +#define TQMX86_GPII_MASK (BIT(0) | BIT(1)) +#define TQMX86_GPII_BITS 2 + +struct tqmx86_gpio_data { + struct gpio_chip chip; + struct irq_chip irq_chip; + void __iomem *io_base; + int irq; + raw_spinlock_t spinlock; + u8 irq_type[TQMX86_NGPI]; +}; + +static u8 tqmx86_gpio_read(struct tqmx86_gpio_data *gd, unsigned int reg) +{ + return ioread8(gd->io_base + reg); +} + +static void tqmx86_gpio_write(struct tqmx86_gpio_data *gd, u8 val, + unsigned int reg) +{ + iowrite8(val, gd->io_base + reg); +} + +static int tqmx86_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + + return !!(tqmx86_gpio_read(gpio, TQMX86_GPIOD) & BIT(offset)); +} + +static void tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + unsigned long flags; + u8 val; + + raw_spin_lock_irqsave(&gpio->spinlock, flags); + val = tqmx86_gpio_read(gpio, TQMX86_GPIOD); + if (value) + val |= BIT(offset); + else + val &= ~BIT(offset); + tqmx86_gpio_write(gpio, val, TQMX86_GPIOD); + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); +} + +static int tqmx86_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + /* Direction cannot be changed. Validate is an input. */ + if (BIT(offset) & TQMX86_DIR_INPUT_MASK) + return 0; + else + return -EINVAL; +} + +static int tqmx86_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, + int value) +{ + /* Direction cannot be changed, validate is an output */ + if (BIT(offset) & TQMX86_DIR_INPUT_MASK) + return -EINVAL; + + tqmx86_gpio_set(chip, offset, value); + return 0; +} + +static int tqmx86_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + return !!(TQMX86_DIR_INPUT_MASK & BIT(offset)); +} + +static void tqmx86_gpio_irq_mask(struct irq_data *data) +{ + unsigned int offset = (data->hwirq - TQMX86_NGPO); + struct tqmx86_gpio_data *gpio = gpiochip_get_data( + irq_data_get_irq_chip_data(data)); + unsigned long flags; + u8 gpiic, mask; + + mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS); + + raw_spin_lock_irqsave(&gpio->spinlock, flags); + gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); + gpiic &= ~mask; + tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); +} + +static void tqmx86_gpio_irq_unmask(struct irq_data *data) +{ + unsigned int offset = (data->hwirq - TQMX86_NGPO); + struct tqmx86_gpio_data *gpio = gpiochip_get_data( + irq_data_get_irq_chip_data(data)); + unsigned long flags; + u8 gpiic, mask; + + mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS); + + raw_spin_lock_irqsave(&gpio->spinlock, flags); + gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); + gpiic &= ~mask; + gpiic |= gpio->irq_type[offset] << (offset * TQMX86_GPII_BITS); + tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); +} + +static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) +{ + struct tqmx86_gpio_data *gpio = gpiochip_get_data( + irq_data_get_irq_chip_data(data)); + unsigned int offset = (data->hwirq - TQMX86_NGPO); + unsigned int edge_type = type & IRQF_TRIGGER_MASK; + unsigned long flags; + u8 new_type, gpiic; + + switch (edge_type) { + case IRQ_TYPE_EDGE_RISING: + new_type = TQMX86_GPII_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + new_type = TQMX86_GPII_FALLING; + break; + case IRQ_TYPE_EDGE_BOTH: + new_type = TQMX86_GPII_FALLING | TQMX86_GPII_RISING; + break; + default: + return -EINVAL; /* not supported */ + } + + gpio->irq_type[offset] = new_type; + + raw_spin_lock_irqsave(&gpio->spinlock, flags); + gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); + gpiic &= ~((TQMX86_GPII_MASK) << (offset * TQMX86_GPII_BITS)); + gpiic |= new_type << (offset * TQMX86_GPII_BITS); + tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + + return 0; +} + +static void tqmx86_gpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *chip = irq_desc_get_handler_data(desc); + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + struct irq_chip *irq_chip = irq_desc_get_chip(desc); + unsigned long irq_bits; + int i = 0, child_irq; + u8 irq_status; + + chained_irq_enter(irq_chip, desc); + + irq_status = tqmx86_gpio_read(gpio, TQMX86_GPIIS); + tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS); + + irq_bits = irq_status; + for_each_set_bit(i, &irq_bits, TQMX86_NGPI) { + child_irq = irq_find_mapping(gpio->chip.irq.domain, + i + TQMX86_NGPO); + generic_handle_irq(child_irq); + } + + chained_irq_exit(irq_chip, desc); +} + +/* Minimal runtime PM is needed by the IRQ subsystem */ +static int __maybe_unused tqmx86_gpio_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int __maybe_unused tqmx86_gpio_runtime_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops tqmx86_gpio_dev_pm_ops = { + SET_RUNTIME_PM_OPS(tqmx86_gpio_runtime_suspend, + tqmx86_gpio_runtime_resume, NULL) +}; + +static int tqmx86_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tqmx86_gpio_data *gpio; + struct gpio_chip *chip; + void __iomem *io_base; + struct resource *res; + int ret, irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) { + dev_err(&pdev->dev, "Cannot get I/O\n"); + return -ENODEV; + } + + io_base = devm_ioport_map(&pdev->dev, res->start, resource_size(res)); + if (!io_base) + return -ENOMEM; + + gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + raw_spin_lock_init(&gpio->spinlock); + gpio->io_base = io_base; + + tqmx86_gpio_write(gpio, (u8)~TQMX86_DIR_INPUT_MASK, TQMX86_GPIODD); + + platform_set_drvdata(pdev, gpio); + + chip = &gpio->chip; + chip->label = "gpio-tqmx86"; + chip->owner = THIS_MODULE; + chip->can_sleep = false; + chip->base = -1; + chip->direction_input = tqmx86_gpio_direction_input; + chip->direction_output = tqmx86_gpio_direction_output; + chip->get_direction = tqmx86_gpio_get_direction; + chip->get = tqmx86_gpio_get; + chip->set = tqmx86_gpio_set; + chip->ngpio = TQMX86_NGPIO; + chip->irq.need_valid_mask = true; + chip->parent = pdev->dev.parent; + + pm_runtime_enable(&pdev->dev); + + ret = devm_gpiochip_add_data(dev, chip, gpio); + if (ret) { + dev_err(dev, "Could not register GPIO chip\n"); + goto out_pm_dis; + } + + if (irq) { + struct irq_chip *irq_chip = &gpio->irq_chip; + u8 irq_status; + + irq_chip->name = chip->label; + irq_chip->parent_device = &pdev->dev; + irq_chip->irq_mask = tqmx86_gpio_irq_mask; + irq_chip->irq_unmask = tqmx86_gpio_irq_unmask; + irq_chip->irq_set_type = tqmx86_gpio_irq_set_type; + + /* Mask all interrupts */ + tqmx86_gpio_write(gpio, 0, TQMX86_GPIIC); + + /* Clear all pending interrupts */ + irq_status = tqmx86_gpio_read(gpio, TQMX86_GPIIS); + tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS); + + ret = gpiochip_irqchip_add(chip, irq_chip, + 0, handle_simple_irq, + IRQ_TYPE_EDGE_BOTH); + if (ret) { + dev_err(dev, "Could not add irq chip\n"); + goto out_pm_dis; + } + + gpiochip_set_chained_irqchip(chip, irq_chip, + irq, tqmx86_gpio_irq_handler); + } + + /* Only GPIOs 4-7 are valid for interrupts. Clear the others */ + clear_bit(0, chip->irq.valid_mask); + clear_bit(1, chip->irq.valid_mask); + clear_bit(2, chip->irq.valid_mask); + clear_bit(3, chip->irq.valid_mask); + + dev_info(dev, "GPIO functionality initialized with %d pins\n", + chip->ngpio); + + return 0; + +out_pm_dis: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static struct platform_driver tqmx86_gpio_driver = { + .driver = { + .name = "tqmx86-gpio", + .pm = &tqmx86_gpio_dev_pm_ops, + }, + .probe = tqmx86_gpio_probe, +}; + +module_platform_driver(tqmx86_gpio_driver); + +MODULE_DESCRIPTION("TQMx86 PLD GPIO Driver"); +MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:tqmx86-gpio"); diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c @@ -1,34 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel Whiskey Cove PMIC GPIO Driver * * This driver is written based on gpio-crystalcove.c * * Copyright (C) 2016 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/bitops.h> -#include <linux/module.h> -#include <linux/interrupt.h> #include <linux/gpio/driver.h> +#include <linux/interrupt.h> #include <linux/mfd/intel_soc_pmic.h> +#include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/seq_file.h> /* * Whiskey Cove PMIC has 13 physical GPIO pins divided into 3 banks: - * Bank 0: Pin 0 - 6 - * Bank 1: Pin 7 - 10 - * Bank 2: Pin 11 -12 + * Bank 0: Pin 0 - 6 + * Bank 1: Pin 7 - 10 + * Bank 2: Pin 11 - 12 * Each pin has one output control register and one input control register. */ #define BANK0_NR_PINS 7 @@ -75,8 +67,8 @@ #define CTLO_RVAL_50KDOWN (2 << 1) #define CTLO_RVAL_50KUP (3 << 1) -#define CTLO_INPUT_SET (CTLO_DRV_CMOS | CTLO_DRV_REN | CTLO_RVAL_2KUP) -#define CTLO_OUTPUT_SET (CTLO_DIR_OUT | CTLO_INPUT_SET) +#define CTLO_INPUT_SET (CTLO_DRV_CMOS | CTLO_DRV_REN | CTLO_RVAL_2KUP) +#define CTLO_OUTPUT_SET (CTLO_DIR_OUT | CTLO_INPUT_SET) enum ctrl_register { CTRL_IN, @@ -105,7 +97,7 @@ struct wcove_gpio { bool set_irq_mask; }; -static inline unsigned int to_reg(int gpio, enum ctrl_register reg_type) +static inline int to_reg(int gpio, enum ctrl_register reg_type) { unsigned int reg; @@ -203,8 +195,7 @@ static int wcove_gpio_get(struct gpio_chip *chip, unsigned int gpio) return val & 0x1; } -static void wcove_gpio_set(struct gpio_chip *chip, - unsigned int gpio, int value) +static void wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) { struct wcove_gpio *wg = gpiochip_get_data(chip); int reg = to_reg(gpio, CTRL_OUT); diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c @@ -555,6 +555,26 @@ static int zynq_gpio_set_wake(struct irq_data *data, unsigned int on) return 0; } +static int zynq_gpio_irq_reqres(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + int ret; + + ret = pm_runtime_get_sync(chip->parent); + if (ret < 0) + return ret; + + return gpiochip_reqres_irq(chip, d->hwirq); +} + +static void zynq_gpio_irq_relres(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + + gpiochip_relres_irq(chip, d->hwirq); + pm_runtime_put(chip->parent); +} + /* irq chip descriptor */ static struct irq_chip zynq_gpio_level_irqchip = { .name = DRIVER_NAME, @@ -564,6 +584,8 @@ static struct irq_chip zynq_gpio_level_irqchip = { .irq_unmask = zynq_gpio_irq_unmask, .irq_set_type = zynq_gpio_set_irq_type, .irq_set_wake = zynq_gpio_set_wake, + .irq_request_resources = zynq_gpio_irq_reqres, + .irq_release_resources = zynq_gpio_irq_relres, .flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED | IRQCHIP_MASK_ON_SUSPEND, }; @@ -576,6 +598,8 @@ static struct irq_chip zynq_gpio_edge_irqchip = { .irq_unmask = zynq_gpio_irq_unmask, .irq_set_type = zynq_gpio_set_irq_type, .irq_set_wake = zynq_gpio_set_wake, + .irq_request_resources = zynq_gpio_irq_reqres, + .irq_release_resources = zynq_gpio_irq_relres, .flags = IRQCHIP_MASK_ON_SUSPEND, }; diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c @@ -29,7 +29,7 @@ * @irq: Linux IRQ number for the event, for request_ / free_irq * @irqflags: flags to pass to request_irq when requesting the IRQ * @irq_is_wake: If the ACPI flags indicate the IRQ is a wakeup source - * @is_requested: True if request_irq has been done + * @irq_requested:True if request_irq has been done * @desc: gpio_desc for the GPIO pin for this event */ struct acpi_gpio_event { @@ -469,6 +469,9 @@ acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio) static int __acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) { + const enum gpiod_flags mask = + GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT | + GPIOD_FLAGS_BIT_DIR_VAL; int ret = 0; /* @@ -489,7 +492,7 @@ __acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) if (((*flags & GPIOD_FLAGS_BIT_DIR_SET) && (diff & GPIOD_FLAGS_BIT_DIR_OUT)) || ((*flags & GPIOD_FLAGS_BIT_DIR_OUT) && (diff & GPIOD_FLAGS_BIT_DIR_VAL))) ret = -EINVAL; - *flags = update; + *flags = (*flags & ~mask) | (update & mask); } return ret; } diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c @@ -86,7 +86,9 @@ static void of_gpio_flags_quirks(struct device_node *np, if (IS_ENABLED(CONFIG_REGULATOR) && (of_device_is_compatible(np, "regulator-fixed") || of_device_is_compatible(np, "reg-fixed-voltage") || - of_device_is_compatible(np, "regulator-gpio"))) { + (of_device_is_compatible(np, "regulator-gpio") && + !(strcmp(propname, "enable-gpio") && + strcmp(propname, "enable-gpios"))))) { /* * The regulator GPIO handles are specified such that the * presence or absence of "enable-active-high" solely controls @@ -345,6 +347,11 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, if (of_flags & OF_GPIO_TRANSITORY) *flags |= GPIO_TRANSITORY; + if (of_flags & OF_GPIO_PULL_UP) + *flags |= GPIO_PULL_UP; + if (of_flags & OF_GPIO_PULL_DOWN) + *flags |= GPIO_PULL_DOWN; + return desc; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c @@ -1782,6 +1782,43 @@ static const struct irq_domain_ops gpiochip_domain_ops = { .xlate = irq_domain_xlate_twocell, }; +/** + * gpiochip_irq_domain_activate() - Lock a GPIO to be used as an IRQ + * @domain: The IRQ domain used by this IRQ chip + * @data: Outermost irq_data associated with the IRQ + * @reserve: If set, only reserve an interrupt vector instead of assigning one + * + * This function is a wrapper that calls gpiochip_lock_as_irq() and is to be + * used as the activate function for the &struct irq_domain_ops. The host_data + * for the IRQ domain must be the &struct gpio_chip. + */ +int gpiochip_irq_domain_activate(struct irq_domain *domain, + struct irq_data *data, bool reserve) +{ + struct gpio_chip *chip = domain->host_data; + + return gpiochip_lock_as_irq(chip, data->hwirq); +} +EXPORT_SYMBOL_GPL(gpiochip_irq_domain_activate); + +/** + * gpiochip_irq_domain_deactivate() - Unlock a GPIO used as an IRQ + * @domain: The IRQ domain used by this IRQ chip + * @data: Outermost irq_data associated with the IRQ + * + * This function is a wrapper that will call gpiochip_unlock_as_irq() and is to + * be used as the deactivate function for the &struct irq_domain_ops. The + * host_data for the IRQ domain must be the &struct gpio_chip. + */ +void gpiochip_irq_domain_deactivate(struct irq_domain *domain, + struct irq_data *data) +{ + struct gpio_chip *chip = domain->host_data; + + return gpiochip_unlock_as_irq(chip, data->hwirq); +} +EXPORT_SYMBOL_GPL(gpiochip_irq_domain_deactivate); + static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset) { if (!gpiochip_irqchip_irq_valid(chip, offset)) @@ -2525,6 +2562,14 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); * rely on gpio_request() having been called beforehand. */ +static int gpio_set_config(struct gpio_chip *gc, unsigned offset, + enum pin_config_param mode) +{ + unsigned long config = { PIN_CONF_PACKED(mode, 0) }; + + return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP; +} + /** * gpiod_direction_input - set the GPIO direction to input * @desc: GPIO to set to input @@ -2572,20 +2617,19 @@ int gpiod_direction_input(struct gpio_desc *desc) if (status == 0) clear_bit(FLAG_IS_OUT, &desc->flags); + if (test_bit(FLAG_PULL_UP, &desc->flags)) + gpio_set_config(chip, gpio_chip_hwgpio(desc), + PIN_CONFIG_BIAS_PULL_UP); + else if (test_bit(FLAG_PULL_DOWN, &desc->flags)) + gpio_set_config(chip, gpio_chip_hwgpio(desc), + PIN_CONFIG_BIAS_PULL_DOWN); + trace_gpio_direction(desc_to_gpio(desc), 1, status); return status; } EXPORT_SYMBOL_GPL(gpiod_direction_input); -static int gpio_set_drive_single_ended(struct gpio_chip *gc, unsigned offset, - enum pin_config_param mode) -{ - unsigned long config = { PIN_CONF_PACKED(mode, 0) }; - - return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP; -} - static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) { struct gpio_chip *gc = desc->gdev->chip; @@ -2682,8 +2726,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) gc = desc->gdev->chip; if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) { /* First see if we can enable open drain in hardware */ - ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), - PIN_CONFIG_DRIVE_OPEN_DRAIN); + ret = gpio_set_config(gc, gpio_chip_hwgpio(desc), + PIN_CONFIG_DRIVE_OPEN_DRAIN); if (!ret) goto set_output_value; /* Emulate open drain by not actively driving the line high */ @@ -2691,16 +2735,16 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) return gpiod_direction_input(desc); } else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) { - ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), - PIN_CONFIG_DRIVE_OPEN_SOURCE); + ret = gpio_set_config(gc, gpio_chip_hwgpio(desc), + PIN_CONFIG_DRIVE_OPEN_SOURCE); if (!ret) goto set_output_value; /* Emulate open source by not actively driving the line low */ if (!value) return gpiod_direction_input(desc); } else { - gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), - PIN_CONFIG_DRIVE_PUSH_PULL); + gpio_set_config(gc, gpio_chip_hwgpio(desc), + PIN_CONFIG_DRIVE_PUSH_PULL); } set_output_value: @@ -2732,7 +2776,7 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) } config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce); - return chip->set_config(chip, gpio_chip_hwgpio(desc), config); + return gpio_set_config(chip, gpio_chip_hwgpio(desc), config); } EXPORT_SYMBOL_GPL(gpiod_set_debounce); @@ -2769,7 +2813,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) packed = pinconf_to_config_packed(PIN_CONFIG_PERSIST_STATE, !transitory); gpio = gpio_chip_hwgpio(desc); - rc = chip->set_config(chip, gpio, packed); + rc = gpio_set_config(chip, gpio, packed); if (rc == -ENOTSUPP) { dev_dbg(&desc->gdev->dev, "Persistence not supported for GPIO %d\n", gpio); @@ -4057,6 +4101,17 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, if (lflags & GPIO_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); + if ((lflags & GPIO_PULL_UP) && (lflags & GPIO_PULL_DOWN)) { + gpiod_err(desc, + "both pull-up and pull-down enabled, invalid configuration\n"); + return -EINVAL; + } + + if (lflags & GPIO_PULL_UP) + set_bit(FLAG_PULL_UP, &desc->flags); + else if (lflags & GPIO_PULL_DOWN) + set_bit(FLAG_PULL_DOWN, &desc->flags); + status = gpiod_set_transitory(desc, (lflags & GPIO_TRANSITORY)); if (status < 0) return status; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h @@ -219,6 +219,8 @@ struct gpio_desc { #define FLAG_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */ #define FLAG_IS_HOGGED 11 /* GPIO is hogged */ #define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */ +#define FLAG_PULL_UP 13 /* GPIO has pull up enabled */ +#define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */ /* Connection label */ const char *label; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig @@ -927,7 +927,7 @@ config UCB1400_CORE config MFD_PM8XXX tristate "Qualcomm PM8xxx PMIC chips driver" depends on (ARM || HEXAGON || COMPILE_TEST) - select IRQ_DOMAIN + select IRQ_DOMAIN_HIERARCHY select MFD_CORE select REGMAP help diff --git a/drivers/mfd/qcom-pm8xxx.c b/drivers/mfd/qcom-pm8xxx.c @@ -70,22 +70,23 @@ #define PM8XXX_NR_IRQS 256 #define PM8821_NR_IRQS 112 +struct pm_irq_data { + int num_irqs; + struct irq_chip *irq_chip; + void (*irq_handler)(struct irq_desc *desc); +}; + struct pm_irq_chip { struct regmap *regmap; spinlock_t pm_irq_lock; struct irq_domain *irqdomain; - unsigned int num_irqs; unsigned int num_blocks; unsigned int num_masters; + const struct pm_irq_data *pm_irq_data; + /* MUST BE AT THE END OF THIS STRUCT */ u8 config[0]; }; -struct pm_irq_data { - int num_irqs; - const struct irq_domain_ops *irq_domain_ops; - void (*irq_handler)(struct irq_desc *desc); -}; - static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp, unsigned int *ip) { @@ -375,21 +376,38 @@ static struct irq_chip pm8xxx_irq_chip = { .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, }; -static int pm8xxx_irq_domain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) +static void pm8xxx_irq_domain_map(struct pm_irq_chip *chip, + struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq, unsigned int type) { - struct pm_irq_chip *chip = d->host_data; - - irq_set_chip_and_handler(irq, &pm8xxx_irq_chip, handle_level_irq); - irq_set_chip_data(irq, chip); + irq_domain_set_info(domain, irq, hwirq, chip->pm_irq_data->irq_chip, + chip, handle_level_irq, NULL, NULL); irq_set_noprobe(irq); +} + +static int pm8xxx_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct pm_irq_chip *chip = domain->host_data; + struct irq_fwspec *fwspec = data; + irq_hw_number_t hwirq; + unsigned int type; + int ret, i; + + ret = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) + pm8xxx_irq_domain_map(chip, domain, virq + i, hwirq + i, type); return 0; } static const struct irq_domain_ops pm8xxx_irq_domain_ops = { - .xlate = irq_domain_xlate_twocell, - .map = pm8xxx_irq_domain_map, + .alloc = pm8xxx_irq_domain_alloc, + .free = irq_domain_free_irqs_common, + .translate = irq_domain_translate_twocell, }; static void pm8821_irq_mask_ack(struct irq_data *d) @@ -473,23 +491,6 @@ static struct irq_chip pm8821_irq_chip = { .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, }; -static int pm8821_irq_domain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) -{ - struct pm_irq_chip *chip = d->host_data; - - irq_set_chip_and_handler(irq, &pm8821_irq_chip, handle_level_irq); - irq_set_chip_data(irq, chip); - irq_set_noprobe(irq); - - return 0; -} - -static const struct irq_domain_ops pm8821_irq_domain_ops = { - .xlate = irq_domain_xlate_twocell, - .map = pm8821_irq_domain_map, -}; - static const struct regmap_config ssbi_regmap_config = { .reg_bits = 16, .val_bits = 8, @@ -501,13 +502,13 @@ static const struct regmap_config ssbi_regmap_config = { static const struct pm_irq_data pm8xxx_data = { .num_irqs = PM8XXX_NR_IRQS, - .irq_domain_ops = &pm8xxx_irq_domain_ops, + .irq_chip = &pm8xxx_irq_chip, .irq_handler = pm8xxx_irq_handler, }; static const struct pm_irq_data pm8821_data = { .num_irqs = PM8821_NR_IRQS, - .irq_domain_ops = &pm8821_irq_domain_ops, + .irq_chip = &pm8821_irq_chip, .irq_handler = pm8821_irq_handler, }; @@ -571,14 +572,14 @@ static int pm8xxx_probe(struct platform_device *pdev) platform_set_drvdata(pdev, chip); chip->regmap = regmap; - chip->num_irqs = data->num_irqs; - chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8); + chip->num_blocks = DIV_ROUND_UP(data->num_irqs, 8); chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8); + chip->pm_irq_data = data; spin_lock_init(&chip->pm_irq_lock); chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node, data->num_irqs, - data->irq_domain_ops, + &pm8xxx_irq_domain_ops, chip); if (!chip->irqdomain) return -ENODEV; diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig @@ -137,6 +137,7 @@ config PINCTRL_QCOM_SPMI_PMIC select PINMUX select PINCONF select GENERIC_PINCONF + select IRQ_DOMAIN_HIERARCHY help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm GPIO and MPP blocks found in the Qualcomm PMIC's chips, @@ -149,6 +150,7 @@ config PINCTRL_QCOM_SSBI_PMIC select PINMUX select PINCONF select GENERIC_PINCONF + select IRQ_DOMAIN_HIERARCHY help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm GPIO and MPP blocks found in the Qualcomm PMIC's chips, diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -12,6 +12,7 @@ */ #include <linux/gpio/driver.h> +#include <linux/interrupt.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> @@ -136,7 +137,6 @@ enum pmic_gpio_func_index { /** * struct pmic_gpio_pad - keep current GPIO settings * @base: Address base in SPMI device. - * @irq: IRQ number which this GPIO generate. * @is_enabled: Set to false when GPIO should be put in high Z state. * @out_value: Cached pin output value * @have_buffer: Set to true if GPIO output could be configured in push-pull, @@ -156,7 +156,6 @@ enum pmic_gpio_func_index { */ struct pmic_gpio_pad { u16 base; - int irq; bool is_enabled; bool out_value; bool have_buffer; @@ -179,6 +178,8 @@ struct pmic_gpio_state { struct regmap *map; struct pinctrl_dev *ctrl; struct gpio_chip chip; + struct fwnode_handle *fwnode; + struct irq_domain *domain; }; static const struct pinconf_generic_params pmic_gpio_bindings[] = { @@ -761,11 +762,18 @@ static int pmic_gpio_of_xlate(struct gpio_chip *chip, static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned pin) { struct pmic_gpio_state *state = gpiochip_get_data(chip); - struct pmic_gpio_pad *pad; + struct irq_fwspec fwspec; - pad = state->ctrl->desc->pins[pin].drv_data; + fwspec.fwnode = state->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = pin + PMIC_GPIO_PHYSICAL_OFFSET; + /* + * Set the type to a safe value temporarily. This will be overwritten + * later with the proper value by irq_set_type. + */ + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; - return pad->irq; + return irq_create_fwspec_mapping(&fwspec); } static void pmic_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) @@ -935,8 +943,79 @@ static int pmic_gpio_populate(struct pmic_gpio_state *state, return 0; } +static struct irq_chip pmic_gpio_irq_chip = { + .name = "spmi-gpio", + .irq_ack = irq_chip_ack_parent, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_wake = irq_chip_set_wake_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND, +}; + +static int pmic_gpio_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + struct pmic_gpio_state *state = container_of(domain->host_data, + struct pmic_gpio_state, + chip); + + if (fwspec->param_count != 2 || + fwspec->param[0] < 1 || fwspec->param[0] > state->chip.ngpio) + return -EINVAL; + + *hwirq = fwspec->param[0] - PMIC_GPIO_PHYSICAL_OFFSET; + *type = fwspec->param[1]; + + return 0; +} + +static int pmic_gpio_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct pmic_gpio_state *state = container_of(domain->host_data, + struct pmic_gpio_state, + chip); + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + irq_hw_number_t hwirq; + unsigned int type; + int ret, i; + + ret = pmic_gpio_domain_translate(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) + irq_domain_set_info(domain, virq + i, hwirq + i, + &pmic_gpio_irq_chip, state, + handle_level_irq, NULL, NULL); + + parent_fwspec.fwnode = domain->parent->fwnode; + parent_fwspec.param_count = 4; + parent_fwspec.param[0] = 0; + parent_fwspec.param[1] = hwirq + 0xc0; + parent_fwspec.param[2] = 0; + parent_fwspec.param[3] = fwspec->param[1]; + + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, + &parent_fwspec); +} + +static const struct irq_domain_ops pmic_gpio_domain_ops = { + .activate = gpiochip_irq_domain_activate, + .alloc = pmic_gpio_domain_alloc, + .deactivate = gpiochip_irq_domain_deactivate, + .free = irq_domain_free_irqs_common, + .translate = pmic_gpio_domain_translate, +}; + static int pmic_gpio_probe(struct platform_device *pdev) { + struct irq_domain *parent_domain; + struct device_node *parent_node; struct device *dev = &pdev->dev; struct pinctrl_pin_desc *pindesc; struct pinctrl_desc *pctrldesc; @@ -951,13 +1030,7 @@ static int pmic_gpio_probe(struct platform_device *pdev) return ret; } - npins = platform_irq_count(pdev); - if (!npins) - return -EINVAL; - if (npins < 0) - return npins; - - BUG_ON(npins > ARRAY_SIZE(pmic_gpio_groups)); + npins = (uintptr_t) device_get_match_data(&pdev->dev); state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); if (!state) @@ -999,10 +1072,6 @@ static int pmic_gpio_probe(struct platform_device *pdev) pindesc->number = i; pindesc->name = pmic_gpio_groups[i]; - pad->irq = platform_get_irq(pdev, i); - if (pad->irq < 0) - return pad->irq; - pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE; ret = pmic_gpio_populate(state, pad); @@ -1022,10 +1091,28 @@ static int pmic_gpio_probe(struct platform_device *pdev) if (IS_ERR(state->ctrl)) return PTR_ERR(state->ctrl); + parent_node = of_irq_find_parent(state->dev->of_node); + if (!parent_node) + return -ENXIO; + + parent_domain = irq_find_host(parent_node); + of_node_put(parent_node); + if (!parent_domain) + return -ENXIO; + + state->fwnode = of_node_to_fwnode(state->dev->of_node); + state->domain = irq_domain_create_hierarchy(parent_domain, 0, + state->chip.ngpio, + state->fwnode, + &pmic_gpio_domain_ops, + &state->chip); + if (!state->domain) + return -ENODEV; + ret = gpiochip_add_data(&state->chip, state); if (ret) { dev_err(state->dev, "can't add gpio chip\n"); - return ret; + goto err_chip_add_data; } /* @@ -1051,6 +1138,8 @@ static int pmic_gpio_probe(struct platform_device *pdev) err_range: gpiochip_remove(&state->chip); +err_chip_add_data: + irq_domain_remove(state->domain); return ret; } @@ -1059,17 +1148,21 @@ static int pmic_gpio_remove(struct platform_device *pdev) struct pmic_gpio_state *state = platform_get_drvdata(pdev); gpiochip_remove(&state->chip); + irq_domain_remove(state->domain); return 0; } static const struct of_device_id pmic_gpio_of_match[] = { - { .compatible = "qcom,pm8916-gpio" }, /* 4 GPIO's */ - { .compatible = "qcom,pm8941-gpio" }, /* 36 GPIO's */ - { .compatible = "qcom,pm8994-gpio" }, /* 22 GPIO's */ - { .compatible = "qcom,pmi8994-gpio" }, /* 10 GPIO's */ - { .compatible = "qcom,pma8084-gpio" }, /* 22 GPIO's */ - { .compatible = "qcom,pms405-gpio" }, /* 12 GPIO's, holes on 1 9 10 */ - { .compatible = "qcom,spmi-gpio" }, /* Generic */ + { .compatible = "qcom,pm8005-gpio", .data = (void *) 4 }, + { .compatible = "qcom,pm8916-gpio", .data = (void *) 4 }, + { .compatible = "qcom,pm8941-gpio", .data = (void *) 36 }, + { .compatible = "qcom,pm8994-gpio", .data = (void *) 22 }, + { .compatible = "qcom,pmi8994-gpio", .data = (void *) 10 }, + { .compatible = "qcom,pm8998-gpio", .data = (void *) 26 }, + { .compatible = "qcom,pmi8998-gpio", .data = (void *) 14 }, + { .compatible = "qcom,pma8084-gpio", .data = (void *) 22 }, + /* pms405 has 12 GPIOs with holes on 1, 9, and 10 */ + { .compatible = "qcom,pms405-gpio", .data = (void *) 12 }, { }, }; diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c @@ -55,6 +55,8 @@ #define PM8XXX_MAX_GPIOS 44 +#define PM8XXX_GPIO_PHYSICAL_OFFSET 1 + /* custom pinconf parameters */ #define PM8XXX_QCOM_DRIVE_STRENGH (PIN_CONFIG_END + 1) #define PM8XXX_QCOM_PULL_UP_STRENGTH (PIN_CONFIG_END + 2) @@ -99,6 +101,9 @@ struct pm8xxx_gpio { struct pinctrl_desc desc; unsigned npins; + + struct fwnode_handle *fwnode; + struct irq_domain *domain; }; static const struct pinconf_generic_params pm8xxx_gpio_bindings[] = { @@ -499,11 +504,12 @@ static int pm8xxx_gpio_get(struct gpio_chip *chip, unsigned offset) if (pin->mode == PM8XXX_GPIO_MODE_OUTPUT) { ret = pin->output_value; - } else { + } else if (pin->irq >= 0) { ret = irq_get_irqchip_state(pin->irq, IRQCHIP_STATE_LINE_LEVEL, &state); if (!ret) ret = !!state; - } + } else + ret = -EINVAL; return ret; } @@ -533,7 +539,7 @@ static int pm8xxx_gpio_of_xlate(struct gpio_chip *chip, if (flags) *flags = gpio_desc->args[1]; - return gpio_desc->args[0] - 1; + return gpio_desc->args[0] - PM8XXX_GPIO_PHYSICAL_OFFSET; } @@ -541,8 +547,31 @@ static int pm8xxx_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; + struct irq_fwspec fwspec; + int ret; + + fwspec.fwnode = pctrl->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = offset + PM8XXX_GPIO_PHYSICAL_OFFSET; + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; + + ret = irq_create_fwspec_mapping(&fwspec); + + /* + * Cache the IRQ since pm8xxx_gpio_get() needs this to get determine the + * line level. + */ + pin->irq = ret; + + return ret; +} + +static void pm8xxx_gpio_free(struct gpio_chip *chip, unsigned int offset) +{ + struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); + struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; - return pin->irq; + pin->irq = -1; } #ifdef CONFIG_DEBUG_FS @@ -571,7 +600,7 @@ static void pm8xxx_gpio_dbg_show_one(struct seq_file *s, "no", "high", "medium", "low" }; - seq_printf(s, " gpio%-2d:", offset + 1); + seq_printf(s, " gpio%-2d:", offset + PM8XXX_GPIO_PHYSICAL_OFFSET); if (pin->disable) { seq_puts(s, " ---"); } else { @@ -603,6 +632,7 @@ static void pm8xxx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) #endif static const struct gpio_chip pm8xxx_gpio_template = { + .free = pm8xxx_gpio_free, .direction_input = pm8xxx_gpio_direction_input, .direction_output = pm8xxx_gpio_direction_output, .get = pm8xxx_gpio_get, @@ -664,13 +694,75 @@ static int pm8xxx_pin_populate(struct pm8xxx_gpio *pctrl, return 0; } +static struct irq_chip pm8xxx_irq_chip = { + .name = "ssbi-gpio", + .irq_mask_ack = irq_chip_mask_ack_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = irq_chip_set_type_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, +}; + +static int pm8xxx_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + struct pm8xxx_gpio *pctrl = container_of(domain->host_data, + struct pm8xxx_gpio, chip); + + if (fwspec->param_count != 2 || fwspec->param[0] < 1 || + fwspec->param[0] > pctrl->chip.ngpio) + return -EINVAL; + + *hwirq = fwspec->param[0] - PM8XXX_GPIO_PHYSICAL_OFFSET; + *type = fwspec->param[1]; + + return 0; +} + +static int pm8xxx_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct pm8xxx_gpio *pctrl = container_of(domain->host_data, + struct pm8xxx_gpio, chip); + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + irq_hw_number_t hwirq; + unsigned int type; + int ret, i; + + ret = pm8xxx_domain_translate(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) + irq_domain_set_info(domain, virq + i, hwirq + i, + &pm8xxx_irq_chip, pctrl, handle_level_irq, + NULL, NULL); + + parent_fwspec.fwnode = domain->parent->fwnode; + parent_fwspec.param_count = 2; + parent_fwspec.param[0] = hwirq + 0xc0; + parent_fwspec.param[1] = fwspec->param[1]; + + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, + &parent_fwspec); +} + +static const struct irq_domain_ops pm8xxx_domain_ops = { + .activate = gpiochip_irq_domain_activate, + .alloc = pm8xxx_domain_alloc, + .deactivate = gpiochip_irq_domain_deactivate, + .free = irq_domain_free_irqs_common, + .translate = pm8xxx_domain_translate, +}; + static const struct of_device_id pm8xxx_gpio_of_match[] = { - { .compatible = "qcom,pm8018-gpio" }, - { .compatible = "qcom,pm8038-gpio" }, - { .compatible = "qcom,pm8058-gpio" }, - { .compatible = "qcom,pm8917-gpio" }, - { .compatible = "qcom,pm8921-gpio" }, - { .compatible = "qcom,ssbi-gpio" }, + { .compatible = "qcom,pm8018-gpio", .data = (void *) 6 }, + { .compatible = "qcom,pm8038-gpio", .data = (void *) 12 }, + { .compatible = "qcom,pm8058-gpio", .data = (void *) 44 }, + { .compatible = "qcom,pm8917-gpio", .data = (void *) 38 }, + { .compatible = "qcom,pm8921-gpio", .data = (void *) 44 }, { }, }; MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match); @@ -678,22 +770,18 @@ MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match); static int pm8xxx_gpio_probe(struct platform_device *pdev) { struct pm8xxx_pin_data *pin_data; + struct irq_domain *parent_domain; + struct device_node *parent_node; struct pinctrl_pin_desc *pins; struct pm8xxx_gpio *pctrl; - int ret; - int i, npins; + int ret, i; pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); if (!pctrl) return -ENOMEM; pctrl->dev = &pdev->dev; - npins = platform_irq_count(pdev); - if (!npins) - return -EINVAL; - if (npins < 0) - return npins; - pctrl->npins = npins; + pctrl->npins = (uintptr_t) device_get_match_data(&pdev->dev); pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!pctrl->regmap) { @@ -720,12 +808,7 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) for (i = 0; i < pctrl->desc.npins; i++) { pin_data[i].reg = SSBI_REG_ADDR_GPIO(i); - pin_data[i].irq = platform_get_irq(pdev, i); - if (pin_data[i].irq < 0) { - dev_err(&pdev->dev, - "missing interrupts for pin %d\n", i); - return pin_data[i].irq; - } + pin_data[i].irq = -1; ret = pm8xxx_pin_populate(pctrl, &pin_data[i]); if (ret) @@ -756,10 +839,29 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) pctrl->chip.of_gpio_n_cells = 2; pctrl->chip.label = dev_name(pctrl->dev); pctrl->chip.ngpio = pctrl->npins; + + parent_node = of_irq_find_parent(pctrl->dev->of_node); + if (!parent_node) + return -ENXIO; + + parent_domain = irq_find_host(parent_node); + of_node_put(parent_node); + if (!parent_domain) + return -ENXIO; + + pctrl->fwnode = of_node_to_fwnode(pctrl->dev->of_node); + pctrl->domain = irq_domain_create_hierarchy(parent_domain, 0, + pctrl->chip.ngpio, + pctrl->fwnode, + &pm8xxx_domain_ops, + &pctrl->chip); + if (!pctrl->domain) + return -ENODEV; + ret = gpiochip_add_data(&pctrl->chip, pctrl); if (ret) { dev_err(&pdev->dev, "failed register gpiochip\n"); - return ret; + goto err_chip_add_data; } /* @@ -789,6 +891,8 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) unregister_gpiochip: gpiochip_remove(&pctrl->chip); +err_chip_add_data: + irq_domain_remove(pctrl->domain); return ret; } @@ -798,6 +902,7 @@ static int pm8xxx_gpio_remove(struct platform_device *pdev) struct pm8xxx_gpio *pctrl = platform_get_drvdata(pdev); gpiochip_remove(&pctrl->chip); + irq_domain_remove(pctrl->domain); return 0; } diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig @@ -1303,6 +1303,20 @@ config HUAWEI_WMI To compile this driver as a module, choose M here: the module will be called huawei-wmi. +config PCENGINES_APU2 + tristate "PC Engines APUv2/3 front button and LEDs driver" + depends on INPUT && INPUT_KEYBOARD + depends on LEDS_CLASS + select GPIO_AMD_FCH + select KEYBOARD_GPIO_POLLED + select LEDS_GPIO + help + This driver provides support for the front button and LEDs on + PC Engines APUv2/APUv3 board. + + To compile this driver as a module, choose M here: the module + will be called pcengines-apuv2. + endif # X86_PLATFORM_DEVICES config PMC_ATOM diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile @@ -96,3 +96,4 @@ obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o obj-$(CONFIG_INTEL_ATOMISP2_PM) += intel_atomisp2_pm.o +obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o diff --git a/drivers/platform/x86/pcengines-apuv2.c b/drivers/platform/x86/pcengines-apuv2.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * PC-Engines APUv2/APUv3 board platform driver + * for gpio buttons and LEDs + * + * Copyright (C) 2018 metux IT consult + * Author: Enrico Weigelt <info@metux.net> + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/dmi.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio_keys.h> +#include <linux/gpio/machine.h> +#include <linux/input.h> +#include <linux/platform_data/gpio/gpio-amd-fch.h> + +/* + * NOTE: this driver only supports APUv2/3 - not APUv1, as this one + * has completely different register layouts + */ + +/* register mappings */ +#define APU2_GPIO_REG_LED1 AMD_FCH_GPIO_REG_GPIO57 +#define APU2_GPIO_REG_LED2 AMD_FCH_GPIO_REG_GPIO58 +#define APU2_GPIO_REG_LED3 AMD_FCH_GPIO_REG_GPIO59_DEVSLP1 +#define APU2_GPIO_REG_MODESW AMD_FCH_GPIO_REG_GPIO32_GE1 +#define APU2_GPIO_REG_SIMSWAP AMD_FCH_GPIO_REG_GPIO33_GE2 + +/* order in which the gpio lines are defined in the register list */ +#define APU2_GPIO_LINE_LED1 0 +#define APU2_GPIO_LINE_LED2 1 +#define APU2_GPIO_LINE_LED3 2 +#define APU2_GPIO_LINE_MODESW 3 +#define APU2_GPIO_LINE_SIMSWAP 4 + +/* gpio device */ + +static int apu2_gpio_regs[] = { + [APU2_GPIO_LINE_LED1] = APU2_GPIO_REG_LED1, + [APU2_GPIO_LINE_LED2] = APU2_GPIO_REG_LED2, + [APU2_GPIO_LINE_LED3] = APU2_GPIO_REG_LED3, + [APU2_GPIO_LINE_MODESW] = APU2_GPIO_REG_MODESW, + [APU2_GPIO_LINE_SIMSWAP] = APU2_GPIO_REG_SIMSWAP, +}; + +static const char * const apu2_gpio_names[] = { + [APU2_GPIO_LINE_LED1] = "front-led1", + [APU2_GPIO_LINE_LED2] = "front-led2", + [APU2_GPIO_LINE_LED3] = "front-led3", + [APU2_GPIO_LINE_MODESW] = "front-button", + [APU2_GPIO_LINE_SIMSWAP] = "simswap", +}; + +static const struct amd_fch_gpio_pdata board_apu2 = { + .gpio_num = ARRAY_SIZE(apu2_gpio_regs), + .gpio_reg = apu2_gpio_regs, + .gpio_names = apu2_gpio_names, +}; + +/* gpio leds device */ + +static const struct gpio_led apu2_leds[] = { + { .name = "apu:green:1" }, + { .name = "apu:green:2" }, + { .name = "apu:green:3" } +}; + +static const struct gpio_led_platform_data apu2_leds_pdata = { + .num_leds = ARRAY_SIZE(apu2_leds), + .leds = apu2_leds, +}; + +struct gpiod_lookup_table gpios_led_table = { + .dev_id = "leds-gpio", + .table = { + GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED1, + NULL, 0, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED2, + NULL, 1, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED3, + NULL, 2, GPIO_ACTIVE_LOW), + } +}; + +/* gpio keyboard device */ + +static struct gpio_keys_button apu2_keys_buttons[] = { + { + .code = KEY_SETUP, + .active_low = 1, + .desc = "front button", + .type = EV_KEY, + .debounce_interval = 10, + .value = 1, + }, +}; + +static const struct gpio_keys_platform_data apu2_keys_pdata = { + .buttons = apu2_keys_buttons, + .nbuttons = ARRAY_SIZE(apu2_keys_buttons), + .poll_interval = 100, + .rep = 0, + .name = "apu2-keys", +}; + +struct gpiod_lookup_table gpios_key_table = { + .dev_id = "gpio-keys-polled", + .table = { + GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_MODESW, + NULL, 0, GPIO_ACTIVE_LOW), + } +}; + +/* board setup */ + +/* note: matching works on string prefix, so "apu2" must come before "apu" */ +static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = { + + /* APU2 w/ legacy bios < 4.0.8 */ + { + .ident = "apu2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), + DMI_MATCH(DMI_BOARD_NAME, "APU2") + }, + .driver_data = (void *)&board_apu2, + }, + /* APU2 w/ legacy bios >= 4.0.8 */ + { + .ident = "apu2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), + DMI_MATCH(DMI_BOARD_NAME, "apu2") + }, + .driver_data = (void *)&board_apu2, + }, + /* APU2 w/ maainline bios */ + { + .ident = "apu2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), + DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu2") + }, + .driver_data = (void *)&board_apu2, + }, + + /* APU3 w/ legacy bios < 4.0.8 */ + { + .ident = "apu3", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), + DMI_MATCH(DMI_BOARD_NAME, "APU3") + }, + .driver_data = (void *)&board_apu2, + }, + /* APU3 w/ legacy bios >= 4.0.8 */ + { + .ident = "apu3", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), + DMI_MATCH(DMI_BOARD_NAME, "apu3") + }, + .driver_data = (void *)&board_apu2, + }, + /* APU3 w/ mainline bios */ + { + .ident = "apu3", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), + DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu3") + }, + .driver_data = (void *)&board_apu2, + }, + {} +}; + +static struct platform_device *apu_gpio_pdev; +static struct platform_device *apu_leds_pdev; +static struct platform_device *apu_keys_pdev; + +static struct platform_device * __init apu_create_pdev( + const char *name, + const void *pdata, + size_t sz) +{ + struct platform_device *pdev; + + pdev = platform_device_register_resndata(NULL, + name, + PLATFORM_DEVID_NONE, + NULL, + 0, + pdata, + sz); + + if (IS_ERR(pdev)) + pr_err("failed registering %s: %ld\n", name, PTR_ERR(pdev)); + + return pdev; +} + +static int __init apu_board_init(void) +{ + const struct dmi_system_id *id; + + id = dmi_first_match(apu_gpio_dmi_table); + if (!id) { + pr_err("failed to detect apu board via dmi\n"); + return -ENODEV; + } + + gpiod_add_lookup_table(&gpios_led_table); + gpiod_add_lookup_table(&gpios_key_table); + + apu_gpio_pdev = apu_create_pdev( + AMD_FCH_GPIO_DRIVER_NAME, + id->driver_data, + sizeof(struct amd_fch_gpio_pdata)); + + apu_leds_pdev = apu_create_pdev( + "leds-gpio", + &apu2_leds_pdata, + sizeof(apu2_leds_pdata)); + + apu_keys_pdev = apu_create_pdev( + "gpio-keys-polled", + &apu2_keys_pdata, + sizeof(apu2_keys_pdata)); + + return 0; +} + +static void __exit apu_board_exit(void) +{ + gpiod_remove_lookup_table(&gpios_led_table); + gpiod_remove_lookup_table(&gpios_key_table); + + platform_device_unregister(apu_keys_pdev); + platform_device_unregister(apu_leds_pdev); + platform_device_unregister(apu_gpio_pdev); +} + +module_init(apu_board_init); +module_exit(apu_board_exit); + +MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>"); +MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LED/keys driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table); +MODULE_ALIAS("platform:pcengines-apuv2"); +MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME); +MODULE_SOFTDEP("pre: platform:leds-gpio"); +MODULE_SOFTDEP("pre: platform:gpio_keys_polled"); diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig @@ -12,7 +12,7 @@ if SPMI config SPMI_MSM_PMIC_ARB tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)" - select IRQ_DOMAIN + select IRQ_DOMAIN_HIERARCHY depends on ARCH_QCOM || COMPILE_TEST depends on HAS_IOMEM default ARCH_QCOM diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c @@ -666,7 +666,8 @@ static int qpnpint_get_irqchip_state(struct irq_data *d, return 0; } -static int qpnpint_irq_request_resources(struct irq_data *d) +static int qpnpint_irq_domain_activate(struct irq_domain *domain, + struct irq_data *d, bool reserve) { struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); u16 periph = hwirq_to_per(d->hwirq); @@ -692,27 +693,25 @@ static struct irq_chip pmic_arb_irqchip = { .irq_set_type = qpnpint_irq_set_type, .irq_set_wake = qpnpint_irq_set_wake, .irq_get_irqchip_state = qpnpint_get_irqchip_state, - .irq_request_resources = qpnpint_irq_request_resources, .flags = IRQCHIP_MASK_ON_SUSPEND, }; -static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, - struct device_node *controller, - const u32 *intspec, - unsigned int intsize, - unsigned long *out_hwirq, - unsigned int *out_type) +static int qpnpint_irq_domain_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *out_hwirq, + unsigned int *out_type) { struct spmi_pmic_arb *pmic_arb = d->host_data; + u32 *intspec = fwspec->param; u16 apid, ppid; int rc; dev_dbg(&pmic_arb->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n", intspec[0], intspec[1], intspec[2]); - if (irq_domain_get_of_node(d) != controller) + if (irq_domain_get_of_node(d) != pmic_arb->spmic->dev.of_node) return -EINVAL; - if (intsize != 4) + if (fwspec->param_count != 4) return -EINVAL; if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7) return -EINVAL; @@ -740,17 +739,43 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, return 0; } -static int qpnpint_irq_domain_map(struct irq_domain *d, - unsigned int virq, - irq_hw_number_t hwirq) + +static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, + struct irq_domain *domain, unsigned int virq, + irq_hw_number_t hwirq, unsigned int type) { - struct spmi_pmic_arb *pmic_arb = d->host_data; + irq_flow_handler_t handler; + + dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu, type = %u\n", + virq, hwirq, type); + + if (type & IRQ_TYPE_EDGE_BOTH) + handler = handle_edge_irq; + else + handler = handle_level_irq; + + irq_domain_set_info(domain, virq, hwirq, &pmic_arb_irqchip, pmic_arb, + handler, NULL, NULL); +} + +static int qpnpint_irq_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *data) +{ + struct spmi_pmic_arb *pmic_arb = domain->host_data; + struct irq_fwspec *fwspec = data; + irq_hw_number_t hwirq; + unsigned int type; + int ret, i; + + ret = qpnpint_irq_domain_translate(domain, fwspec, &hwirq, &type); + if (ret) + return ret; - dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq); + for (i = 0; i < nr_irqs; i++) + qpnpint_irq_domain_map(pmic_arb, domain, virq + i, hwirq + i, + type); - irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq); - irq_set_chip_data(virq, d->host_data); - irq_set_noprobe(virq); return 0; } @@ -1126,8 +1151,10 @@ static const struct pmic_arb_ver_ops pmic_arb_v5 = { }; static const struct irq_domain_ops pmic_arb_irq_domain_ops = { - .map = qpnpint_irq_domain_map, - .xlate = qpnpint_irq_domain_dt_translate, + .activate = qpnpint_irq_domain_activate, + .alloc = qpnpint_irq_domain_alloc, + .free = irq_domain_free_irqs_common, + .translate = qpnpint_irq_domain_translate, }; static int spmi_pmic_arb_probe(struct platform_device *pdev) diff --git a/include/dt-bindings/gpio/gpio.h b/include/dt-bindings/gpio/gpio.h @@ -33,4 +33,10 @@ #define GPIO_PERSISTENT 0 #define GPIO_TRANSITORY 8 +/* Bit 4 express pull up */ +#define GPIO_PULL_UP 16 + +/* Bit 5 express pull down */ +#define GPIO_PULL_DOWN 32 + #endif diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h @@ -472,6 +472,11 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq); void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq); +int gpiochip_irq_domain_activate(struct irq_domain *domain, + struct irq_data *data, bool reserve); +void gpiochip_irq_domain_deactivate(struct irq_domain *domain, + struct irq_data *data); + void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip, struct irq_chip *irqchip, unsigned int parent_irq, diff --git a/include/linux/gpio/machine.h b/include/linux/gpio/machine.h @@ -12,6 +12,8 @@ enum gpio_lookup_flags { GPIO_OPEN_SOURCE = (1 << 2), GPIO_PERSISTENT = (0 << 3), GPIO_TRANSITORY = (1 << 3), + GPIO_PULL_UP = (1 << 4), + GPIO_PULL_DOWN = (1 << 5), }; /** diff --git a/include/linux/irq.h b/include/linux/irq.h @@ -615,6 +615,7 @@ extern void irq_chip_disable_parent(struct irq_data *data); extern void irq_chip_ack_parent(struct irq_data *data); extern int irq_chip_retrigger_hierarchy(struct irq_data *data); extern void irq_chip_mask_parent(struct irq_data *data); +extern void irq_chip_mask_ack_parent(struct irq_data *data); extern void irq_chip_unmask_parent(struct irq_data *data); extern void irq_chip_eoi_parent(struct irq_data *data); extern int irq_chip_set_affinity_parent(struct irq_data *data, diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h @@ -420,6 +420,11 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr, const u32 *intspec, unsigned int intsize, irq_hw_number_t *out_hwirq, unsigned int *out_type); +int irq_domain_translate_twocell(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *out_hwirq, + unsigned int *out_type); + /* IPI functions */ int irq_reserve_ipi(struct irq_domain *domain, const struct cpumask *dest); int irq_destroy_ipi(unsigned int irq, const struct cpumask *dest); diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h @@ -28,6 +28,8 @@ enum of_gpio_flags { OF_GPIO_SINGLE_ENDED = 0x2, OF_GPIO_OPEN_DRAIN = 0x4, OF_GPIO_TRANSITORY = 0x8, + OF_GPIO_PULL_UP = 0x10, + OF_GPIO_PULL_DOWN = 0x20, }; #ifdef CONFIG_OF_GPIO diff --git a/include/linux/platform_data/gpio/gpio-amd-fch.h b/include/linux/platform_data/gpio/gpio-amd-fch.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL+ */ + +/* + * AMD FCH gpio driver platform-data + * + * Copyright (C) 2018 metux IT consult + * Author: Enrico Weigelt <info@metux.net> + * + */ + +#ifndef __LINUX_PLATFORM_DATA_GPIO_AMD_FCH_H +#define __LINUX_PLATFORM_DATA_GPIO_AMD_FCH_H + +#define AMD_FCH_GPIO_DRIVER_NAME "gpio_amd_fch" + +/* + * gpio register index definitions + */ +#define AMD_FCH_GPIO_REG_GPIO49 0x40 +#define AMD_FCH_GPIO_REG_GPIO50 0x41 +#define AMD_FCH_GPIO_REG_GPIO51 0x42 +#define AMD_FCH_GPIO_REG_GPIO59_DEVSLP0 0x43 +#define AMD_FCH_GPIO_REG_GPIO57 0x44 +#define AMD_FCH_GPIO_REG_GPIO58 0x45 +#define AMD_FCH_GPIO_REG_GPIO59_DEVSLP1 0x46 +#define AMD_FCH_GPIO_REG_GPIO64 0x47 +#define AMD_FCH_GPIO_REG_GPIO68 0x48 +#define AMD_FCH_GPIO_REG_GPIO66_SPKR 0x5B +#define AMD_FCH_GPIO_REG_GPIO71 0x4D +#define AMD_FCH_GPIO_REG_GPIO32_GE1 0x59 +#define AMD_FCH_GPIO_REG_GPIO33_GE2 0x5A +#define AMT_FCH_GPIO_REG_GEVT22 0x09 + +/* + * struct amd_fch_gpio_pdata - GPIO chip platform data + * @gpio_num: number of entries + * @gpio_reg: array of gpio registers + * @gpio_names: array of gpio names + */ +struct amd_fch_gpio_pdata { + int gpio_num; + int *gpio_reg; + const char * const *gpio_names; +}; + +#endif /* __LINUX_PLATFORM_DATA_GPIO_AMD_FCH_H */ diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h @@ -52,6 +52,9 @@ extern struct device platform_bus; extern void arch_setup_pdev_archdata(struct platform_device *); extern struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int); +extern void __iomem * +devm_platform_ioremap_resource(struct platform_device *pdev, + unsigned int index); extern int platform_get_irq(struct platform_device *, unsigned int); extern int platform_irq_count(struct platform_device *); extern struct resource *platform_get_resource_byname(struct platform_device *, diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c @@ -1340,6 +1340,17 @@ void irq_chip_mask_parent(struct irq_data *data) EXPORT_SYMBOL_GPL(irq_chip_mask_parent); /** + * irq_chip_mask_ack_parent - Mask and acknowledge the parent interrupt + * @data: Pointer to interrupt specific data + */ +void irq_chip_mask_ack_parent(struct irq_data *data) +{ + data = data->parent_data; + data->chip->irq_mask_ack(data); +} +EXPORT_SYMBOL_GPL(irq_chip_mask_ack_parent); + +/** * irq_chip_unmask_parent - Unmask the parent interrupt * @data: Pointer to interrupt specific data */ @@ -1443,6 +1454,7 @@ int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on) return -ENOSYS; } +EXPORT_SYMBOL_GPL(irq_chip_set_wake_parent); #endif /** diff --git a/kernel/irq/irq_sim.c b/kernel/irq/irq_sim.c @@ -25,10 +25,22 @@ static void irq_sim_irqunmask(struct irq_data *data) irq_ctx->enabled = true; } +static int irq_sim_set_type(struct irq_data *data, unsigned int type) +{ + /* We only support rising and falling edge trigger types. */ + if (type & ~IRQ_TYPE_EDGE_BOTH) + return -EINVAL; + + irqd_set_trigger_type(data, type); + + return 0; +} + static struct irq_chip irq_sim_irqchip = { .name = "irq_sim", .irq_mask = irq_sim_irqmask, .irq_unmask = irq_sim_irqunmask, + .irq_set_type = irq_sim_set_type, }; static void irq_sim_handle_irq(struct irq_work *work) diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c @@ -743,16 +743,17 @@ static int irq_domain_translate(struct irq_domain *d, return 0; } -static void of_phandle_args_to_fwspec(struct of_phandle_args *irq_data, +static void of_phandle_args_to_fwspec(struct device_node *np, const u32 *args, + unsigned int count, struct irq_fwspec *fwspec) { int i; - fwspec->fwnode = irq_data->np ? &irq_data->np->fwnode : NULL; - fwspec->param_count = irq_data->args_count; + fwspec->fwnode = np ? &np->fwnode : NULL; + fwspec->param_count = count; - for (i = 0; i < irq_data->args_count; i++) - fwspec->param[i] = irq_data->args[i]; + for (i = 0; i < count; i++) + fwspec->param[i] = args[i]; } unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec) @@ -850,7 +851,9 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data) { struct irq_fwspec fwspec; - of_phandle_args_to_fwspec(irq_data, &fwspec); + of_phandle_args_to_fwspec(irq_data->np, irq_data->args, + irq_data->args_count, &fwspec); + return irq_create_fwspec_mapping(&fwspec); } EXPORT_SYMBOL_GPL(irq_create_of_mapping); @@ -942,11 +945,10 @@ int irq_domain_xlate_twocell(struct irq_domain *d, struct device_node *ctrlr, const u32 *intspec, unsigned int intsize, irq_hw_number_t *out_hwirq, unsigned int *out_type) { - if (WARN_ON(intsize < 2)) - return -EINVAL; - *out_hwirq = intspec[0]; - *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; - return 0; + struct irq_fwspec fwspec; + + of_phandle_args_to_fwspec(ctrlr, intspec, intsize, &fwspec); + return irq_domain_translate_twocell(d, &fwspec, out_hwirq, out_type); } EXPORT_SYMBOL_GPL(irq_domain_xlate_twocell); @@ -982,6 +984,27 @@ const struct irq_domain_ops irq_domain_simple_ops = { }; EXPORT_SYMBOL_GPL(irq_domain_simple_ops); +/** + * irq_domain_translate_twocell() - Generic translate for direct two cell + * bindings + * + * Device Tree IRQ specifier translation function which works with two cell + * bindings where the cell values map directly to the hwirq number + * and linux irq flags. + */ +int irq_domain_translate_twocell(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *out_hwirq, + unsigned int *out_type) +{ + if (WARN_ON(fwspec->param_count < 2)) + return -EINVAL; + *out_hwirq = fwspec->param[0]; + *out_type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; + return 0; +} +EXPORT_SYMBOL_GPL(irq_domain_translate_twocell); + int irq_domain_alloc_descs(int virq, unsigned int cnt, irq_hw_number_t hwirq, int node, const struct irq_affinity_desc *affinity) {