summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-10-08 09:46:29 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2022-10-08 09:46:29 -0700
commitf01603979a4afaad7504a728918b678d572cda9e (patch)
treec88ced8221cbc6eba9e1ea8a421d7cae7c9f8cd5
parent3002b7a31894cfa0e57080f7e0961b5fee788aa3 (diff)
parent3c92506d86785967fd7e7933e04491b9276c2f00 (diff)
Merge tag 'gpio-updates-for-v6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux
Pull gpio updates from Bartosz Golaszewski: "We have a single new driver, support for a bunch of new models, improvements in drivers and core gpiolib code as well device-tree bindings changes. Summary: New driver: - IMX System Controller Unit GPIOs GPIO core: - add fdinfo output for the GPIO character device file descriptors (allows user-space to determine which processes own which GPIO lines) - improvements to OF GPIO code - new quirk for Asus UM325UAZ in gpiolib-acpi - new quirk for Freescale SPI in gpiolib-of Driver improvements: - add a new macro that reduces the amount of boilerplate code in ISA drivers and use it in relevant drivers - support two new models in gpio-pca953x - support new model in gpio-f7188x - convert more drivers to use immutable irq chips - other minor tweaks Device-tree bindings: - add DT bindings for gpio-imx-scu - convert Xilinx GPIO bindings to YAML - reference the properties from the SPI peripheral device-tree bindings instead of providing custom ones in the GPIO controller document - add parsing of GPIO hog nodes to the DT bindings for gpio-mpfs-gpio - relax the node name requirements in gpio-stmpe - add new models for gpio-rcar and gpio-pxa95xx - add a new vendor prefix: Diodes (for Diodes, Inc.) Misc: - pulled in the immutable branch from the x86 platform drivers tree including support for a new simatic board that depends on GPIO changes" * tag 'gpio-updates-for-v6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (36 commits) gpio: tc3589x: Make irqchip immutable gpiolib: cdev: add fdinfo output for line request file descriptors gpio: twl4030: Reorder functions which allows to drop a forward declaraion gpiolib: fix OOB access in quirk callbacks gpiolib: of: factor out conversion from OF flags gpiolib: rework quirk handling in of_find_gpio() gpiolib: of: make Freescale SPI quirk similar to all others gpiolib: of: do not ignore requested index when applying quirks gpio: ws16c48: Ensure number of irq matches number of base gpio: 104-idio-16: Ensure number of irq matches number of base gpio: 104-idi-48: Ensure number of irq matches number of base gpio: 104-dio-48e: Ensure number of irq matches number of base counter: 104-quad-8: Ensure number of irq matches number of base isa: Introduce the module_isa_driver_with_irq helper macro gpio: pca953x: Add support for PCAL6534 gpio: pca953x: Swap if statements to save later complexity gpio: pca953x: Fix pca953x_gpio_set_pull_up_down() dt-bindings: gpio: pca95xx: add entry for pcal6534 and PI4IOE5V6534Q dt-bindings: vendor-prefixes: add Diodes gpio: mt7621: Switch to use platform_get_irq() function ...
-rw-r--r--Documentation/devicetree/bindings/firmware/fsl,scu.yaml5
-rw-r--r--Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml7
-rw-r--r--Documentation/devicetree/bindings/gpio/fsl,imx8qxp-sc-gpio.yaml39
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml99
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-stmpe.txt3
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-xilinx.txt48
-rw-r--r--Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml18
-rw-r--r--Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml1
-rw-r--r--Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml154
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.yaml2
-rw-r--r--drivers/counter/104-quad-8.c5
-rw-r--r--drivers/gpio/Kconfig4
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-104-dio-48e.c5
-rw-r--r--drivers/gpio/gpio-104-idi-48.c5
-rw-r--r--drivers/gpio/gpio-104-idio-16.c5
-rw-r--r--drivers/gpio/gpio-exar.c40
-rw-r--r--drivers/gpio/gpio-imx-scu.c139
-rw-r--r--drivers/gpio/gpio-mt7621.c7
-rw-r--r--drivers/gpio/gpio-pca953x.c177
-rw-r--r--drivers/gpio/gpio-rockchip.c21
-rw-r--r--drivers/gpio/gpio-tc3589x.c8
-rw-r--r--drivers/gpio/gpio-twl4030.c26
-rw-r--r--drivers/gpio/gpio-ucb1400.c1
-rw-r--r--drivers/gpio/gpio-ws16c48.c5
-rw-r--r--drivers/gpio/gpiolib-acpi.c38
-rw-r--r--drivers/gpio/gpiolib-cdev.c18
-rw-r--r--drivers/gpio/gpiolib-of.c184
-rw-r--r--drivers/gpio/gpiolib.c132
-rw-r--r--include/linux/gpio/consumer.h13
-rw-r--r--include/linux/isa.h52
-rw-r--r--include/linux/ucb1400.h2
32 files changed, 873 insertions, 391 deletions
diff --git a/Documentation/devicetree/bindings/firmware/fsl,scu.yaml b/Documentation/devicetree/bindings/firmware/fsl,scu.yaml
index b40b0ef56978..557e524786c2 100644
--- a/Documentation/devicetree/bindings/firmware/fsl,scu.yaml
+++ b/Documentation/devicetree/bindings/firmware/fsl,scu.yaml
@@ -30,6 +30,11 @@ properties:
Clock controller node that provides the clocks controlled by the SCU
$ref: /schemas/clock/fsl,scu-clk.yaml
+ gpio:
+ description:
+ Control the GPIO PINs on SCU domain over the firmware APIs
+ $ref: /schemas/gpio/fsl,imx8qxp-sc-gpio.yaml
+
ocotp:
description:
OCOTP controller node provided by the SCU
diff --git a/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml b/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml
index a99e7842ca17..c0ad70e66f76 100644
--- a/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml
+++ b/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml
@@ -33,8 +33,6 @@ properties:
description: GPIO connected to the OE (Output Enable) pin.
maxItems: 1
- spi-max-frequency: true
-
patternProperties:
"^(hog-[0-9]+|.+-hog(-[0-9]+)?)$":
type: object
@@ -59,7 +57,10 @@ required:
- '#gpio-cells'
- registers-number
-additionalProperties: false
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
examples:
- |
diff --git a/Documentation/devicetree/bindings/gpio/fsl,imx8qxp-sc-gpio.yaml b/Documentation/devicetree/bindings/gpio/fsl,imx8qxp-sc-gpio.yaml
new file mode 100644
index 000000000000..b7b32220935d
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/fsl,imx8qxp-sc-gpio.yaml
@@ -0,0 +1,39 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/gpio/fsl,imx8qxp-sc-gpio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: GPIO driver over IMX SCU firmware API
+
+maintainers:
+ - Shenwei Wang <shenwei.wang@nxp.com>
+
+description: |
+ This module provides the standard interface to control the
+ resource pins in SCU domain on i.MX8 platforms.
+
+properties:
+ compatible:
+ enum:
+ - fsl,imx8qxp-sc-gpio
+
+ "#gpio-cells":
+ const: 2
+
+ gpio-controller: true
+
+required:
+ - compatible
+ - "#gpio-cells"
+ - gpio-controller
+
+additionalProperties: false
+
+examples:
+ - |
+ gpio0: gpio {
+ compatible = "fsl,imx8qxp-sc-gpio";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml
index 977b14db09b0..1b70e9f308f3 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml
+++ b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml
@@ -15,52 +15,59 @@ description: |+
properties:
compatible:
- enum:
- - exar,xra1202
- - maxim,max7310
- - maxim,max7312
- - maxim,max7313
- - maxim,max7315
- - maxim,max7319
- - maxim,max7320
- - maxim,max7321
- - maxim,max7322
- - maxim,max7323
- - maxim,max7324
- - maxim,max7325
- - maxim,max7326
- - maxim,max7327
- - nxp,pca6408
- - nxp,pca6416
- - nxp,pca9505
- - nxp,pca9506
- - nxp,pca9534
- - nxp,pca9535
- - nxp,pca9536
- - nxp,pca9537
- - nxp,pca9538
- - nxp,pca9539
- - nxp,pca9554
- - nxp,pca9555
- - nxp,pca9556
- - nxp,pca9557
- - nxp,pca9574
- - nxp,pca9575
- - nxp,pca9698
- - nxp,pcal6416
- - nxp,pcal6524
- - nxp,pcal9535
- - nxp,pcal9554b
- - nxp,pcal9555a
- - onnn,cat9554
- - onnn,pca9654
- - ti,pca6107
- - ti,pca9536
- - ti,tca6408
- - ti,tca6416
- - ti,tca6424
- - ti,tca9539
- - ti,tca9554
+ oneOf:
+ - items:
+ - const: diodes,pi4ioe5v6534q
+ - const: nxp,pcal6534
+ - items:
+ - enum:
+ - exar,xra1202
+ - maxim,max7310
+ - maxim,max7312
+ - maxim,max7313
+ - maxim,max7315
+ - maxim,max7319
+ - maxim,max7320
+ - maxim,max7321
+ - maxim,max7322
+ - maxim,max7323
+ - maxim,max7324
+ - maxim,max7325
+ - maxim,max7326
+ - maxim,max7327
+ - nxp,pca6408
+ - nxp,pca6416
+ - nxp,pca9505
+ - nxp,pca9506
+ - nxp,pca9534
+ - nxp,pca9535
+ - nxp,pca9536
+ - nxp,pca9537
+ - nxp,pca9538
+ - nxp,pca9539
+ - nxp,pca9554
+ - nxp,pca9555
+ - nxp,pca9556
+ - nxp,pca9557
+ - nxp,pca9574
+ - nxp,pca9575
+ - nxp,pca9698
+ - nxp,pcal6408
+ - nxp,pcal6416
+ - nxp,pcal6524
+ - nxp,pcal6534
+ - nxp,pcal9535
+ - nxp,pcal9554b
+ - nxp,pcal9555a
+ - onnn,cat9554
+ - onnn,pca9654
+ - ti,pca6107
+ - ti,pca9536
+ - ti,tca6408
+ - ti,tca6416
+ - ti,tca6424
+ - ti,tca9539
+ - ti,tca9554
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/gpio/gpio-stmpe.txt b/Documentation/devicetree/bindings/gpio/gpio-stmpe.txt
index a0e4cf885213..b33f8f02c0d7 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-stmpe.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio-stmpe.txt
@@ -8,8 +8,7 @@ Optional properties:
- st,norequest-mask: bitmask specifying which GPIOs should _not_ be requestable
due to different usage (e.g. touch, keypad)
-Node name must be stmpe_gpio and should be child node of stmpe node to which it
-belongs.
+Node should be child node of stmpe node to which it belongs.
Example:
stmpe_gpio {
diff --git a/Documentation/devicetree/bindings/gpio/gpio-xilinx.txt b/Documentation/devicetree/bindings/gpio/gpio-xilinx.txt
deleted file mode 100644
index e506f30e1a95..000000000000
--- a/Documentation/devicetree/bindings/gpio/gpio-xilinx.txt
+++ /dev/null
@@ -1,48 +0,0 @@
-Xilinx plb/axi GPIO controller
-
-Dual channel GPIO controller with configurable number of pins
-(from 1 to 32 per channel). Every pin can be configured as
-input/output/tristate. Both channels share the same global IRQ but
-local interrupts can be enabled on channel basis.
-
-Required properties:
-- compatible : Should be "xlnx,xps-gpio-1.00.a"
-- reg : Address and length of the register set for the device
-- #gpio-cells : Should be two. The first cell is the pin number and the
- second cell is used to specify optional parameters (currently unused).
-- gpio-controller : Marks the device node as a GPIO controller.
-
-Optional properties:
-- clocks : Input clock specifier. Refer to common clock bindings.
-- interrupts : Interrupt mapping for GPIO IRQ.
-- xlnx,all-inputs : if n-th bit is setup, GPIO-n is input
-- xlnx,dout-default : if n-th bit is 1, GPIO-n default value is 1
-- xlnx,gpio-width : gpio width
-- xlnx,tri-default : if n-th bit is 1, GPIO-n is in tristate mode
-- xlnx,is-dual : if 1, controller also uses the second channel
-- xlnx,all-inputs-2 : as above but for the second channel
-- xlnx,dout-default-2 : as above but the second channel
-- xlnx,gpio2-width : as above but for the second channel
-- xlnx,tri-default-2 : as above but for the second channel
-
-
-Example:
-gpio: gpio@40000000 {
- #gpio-cells = <2>;
- compatible = "xlnx,xps-gpio-1.00.a";
- clocks = <&clkc25>;
- gpio-controller ;
- interrupt-parent = <&microblaze_0_intc>;
- interrupts = < 6 2 >;
- reg = < 0x40000000 0x10000 >;
- xlnx,all-inputs = <0x0>;
- xlnx,all-inputs-2 = <0x0>;
- xlnx,dout-default = <0x0>;
- xlnx,dout-default-2 = <0x0>;
- xlnx,gpio-width = <0x2>;
- xlnx,gpio2-width = <0x2>;
- xlnx,interrupt-present = <0x1>;
- xlnx,is-dual = <0x1>;
- xlnx,tri-default = <0xffffffff>;
- xlnx,tri-default-2 = <0xffffffff>;
-} ;
diff --git a/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml b/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml
index 110651eafa70..fdc16822fd4b 100644
--- a/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml
+++ b/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml
@@ -44,6 +44,24 @@ properties:
gpio-controller: true
+patternProperties:
+ "^.+-hog(-[0-9]+)?$":
+ type: object
+
+ additionalProperties: false
+
+ properties:
+ gpio-hog: true
+ gpios: true
+ input: true
+ output-high: true
+ output-low: true
+ line-name: true
+
+ required:
+ - gpio-hog
+ - gpios
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml b/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml
index 75e5da6a7cc0..aa424e2b95f8 100644
--- a/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml
+++ b/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml
@@ -52,6 +52,7 @@ properties:
- enum:
- renesas,gpio-r8a779a0 # R-Car V3U
- renesas,gpio-r8a779f0 # R-Car S4-8
+ - renesas,gpio-r8a779g0 # R-Car V4H
- const: renesas,rcar-gen4-gpio # R-Car Gen4
reg:
diff --git a/Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml b/Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml
new file mode 100644
index 000000000000..f333ee2288e7
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml
@@ -0,0 +1,154 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/gpio/xlnx,gpio-xilinx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Xilinx AXI GPIO controller
+
+maintainers:
+ - Neeli Srinivas <srinivas.neeli@xilinx.com>
+
+description:
+ The AXI GPIO design provides a general purpose input/output interface
+ to an AXI4-Lite interface. The AXI GPIO can be configured as either
+ a single or a dual-channel device. The width of each channel is
+ independently configurable. The channels can be configured to
+ generate an interrupt when a transition on any of their inputs occurs.
+
+properties:
+ compatible:
+ enum:
+ - xlnx,xps-gpio-1.00.a
+
+ reg:
+ maxItems: 1
+
+ "#gpio-cells":
+ const: 2
+
+ interrupts:
+ maxItems: 1
+
+ gpio-controller: true
+
+ gpio-line-names:
+ description: strings describing the names of each gpio line
+ minItems: 1
+ maxItems: 64
+
+ interrupt-controller: true
+
+ "#interrupt-cells":
+ const: 2
+
+ clocks:
+ maxItems: 1
+
+ interrupt-names: true
+
+ xlnx,all-inputs:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: This option sets this GPIO channel1 bits in input mode.
+
+ xlnx,all-inputs-2:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: This option sets this GPIO channel2 bits in input mode.
+
+ xlnx,all-outputs:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: This option sets this GPIO channel1 bits in output mode.
+
+ xlnx,all-outputs-2:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: This option sets this GPIO channel2 bits in output mode.
+
+ xlnx,dout-default:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: Sets the default value of all the enabled bits of
+ channel1.
+ default: 0
+
+ xlnx,dout-default-2:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: Sets the default value of all the enabled bits of
+ channel2.
+ default: 0
+
+ xlnx,gpio-width:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: The value defines the bit width of the GPIO channel1.
+ minimum: 1
+ maximum: 32
+ default: 32
+
+ xlnx,gpio2-width:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: The value defines the bit width of the GPIO channel2.
+ minimum: 1
+ maximum: 32
+ default: 32
+
+ xlnx,interrupt-present:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: This parameter enables interrupt control logic
+ and interrupt registers in GPIO module.
+ minimum: 0
+ maximum: 1
+ default: 0
+
+ xlnx,is-dual:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: This parameter enables a second GPIO channel (GPIO2).
+ minimum: 0
+ maximum: 1
+ default: 0
+
+ xlnx,tri-default:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: This value configures the input or output mode
+ of each bit of GPIO channel1.
+
+ xlnx,tri-default-2:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: This value configures the input or output mode
+ of each bit of GPIO channel2.
+
+required:
+ - reg
+ - compatible
+ - gpio-controller
+ - "#gpio-cells"
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ gpio@e000a000 {
+ compatible = "xlnx,xps-gpio-1.00.a";
+ reg = <0xa0020000 0x10000>;
+ #gpio-cells = <2>;
+ #interrupt-cells = <0x2>;
+ clocks = <&zynqmp_clk 71>;
+ gpio-controller;
+ interrupt-controller;
+ interrupt-names = "ip2intc_irpt";
+ interrupt-parent = <&gic>;
+ interrupts = <0 89 4>;
+ xlnx,all-inputs = <0x0>;
+ xlnx,all-inputs-2 = <0x0>;
+ xlnx,all-outputs = <0x0>;
+ xlnx,all-outputs-2 = <0x0>;
+ xlnx,dout-default = <0x0>;
+ xlnx,dout-default-2 = <0x0>;
+ xlnx,gpio-width = <0x20>;
+ xlnx,gpio2-width = <0x20>;
+ xlnx,interrupt-present = <0x1>;
+ xlnx,is-dual = <0x1>;
+ xlnx,tri-default = <0xFFFFFFFF>;
+ xlnx,tri-default-2 = <0xFFFFFFFF>;
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 48b65069e6f2..6e323a380294 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -330,6 +330,8 @@ patternProperties:
description: Digi International Inc.
"^digilent,.*":
description: Diglent, Inc.
+ "^diodes,.*":
+ description: Diodes, Inc.
"^dioo,.*":
description: Dioo Microcircuit Co., Ltd
"^dlc,.*":
diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c
index 1188673d8521..77a863b7eefe 100644
--- a/drivers/counter/104-quad-8.c
+++ b/drivers/counter/104-quad-8.c
@@ -28,7 +28,8 @@ module_param_hw_array(base, uint, ioport, &num_quad8, 0);
MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
static unsigned int irq[max_num_isa_dev(QUAD8_EXTENT)];
-module_param_hw_array(irq, uint, irq, NULL, 0);
+static unsigned int num_irq;
+module_param_hw_array(irq, uint, irq, &num_irq, 0);
MODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers");
#define QUAD8_NUM_COUNTERS 8
@@ -1271,7 +1272,7 @@ static struct isa_driver quad8_driver = {
}
};
-module_isa_driver(quad8_driver, num_quad8);
+module_isa_driver_with_irq(quad8_driver, num_quad8, num_irq);
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("ACCES 104-QUAD-8 driver");
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 3f64345fe40b..ed9e71d6713e 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -341,6 +341,10 @@ config GPIO_ICH
If unsure, say N.
+config GPIO_IMX_SCU
+ def_bool y
+ depends on IMX_SCU
+
config GPIO_IOP
tristate "Intel IOP GPIO"
depends on ARCH_IOP32X || COMPILE_TEST
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index a0985d30f51b..b67e29d348cf 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o
obj-$(CONFIG_GPIO_I8255) += gpio-i8255.o
obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
obj-$(CONFIG_GPIO_IDT3243X) += gpio-idt3243x.o
+obj-$(CONFIG_GPIO_IMX_SCU) += gpio-imx-scu.o
obj-$(CONFIG_GPIO_IOP) += gpio-iop.o
obj-$(CONFIG_GPIO_IT87) += gpio-it87.o
obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4xx.o
diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c
index 74cc71bb3984..7b8829c8e423 100644
--- a/drivers/gpio/gpio-104-dio-48e.c
+++ b/drivers/gpio/gpio-104-dio-48e.c
@@ -34,7 +34,8 @@ module_param_hw_array(base, uint, ioport, &num_dio48e, 0);
MODULE_PARM_DESC(base, "ACCES 104-DIO-48E base addresses");
static unsigned int irq[MAX_NUM_DIO48E];
-module_param_hw_array(irq, uint, irq, NULL, 0);
+static unsigned int num_irq;
+module_param_hw_array(irq, uint, irq, &num_irq, 0);
MODULE_PARM_DESC(irq, "ACCES 104-DIO-48E interrupt line numbers");
#define DIO48E_NUM_PPI 2
@@ -362,7 +363,7 @@ static struct isa_driver dio48e_driver = {
.name = "104-dio-48e"
},
};
-module_isa_driver(dio48e_driver, num_dio48e);
+module_isa_driver_with_irq(dio48e_driver, num_dio48e, num_irq);
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("ACCES 104-DIO-48E GPIO driver");
diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c
index 3286b914a2cf..c5e231fde1af 100644
--- a/drivers/gpio/gpio-104-idi-48.c
+++ b/drivers/gpio/gpio-104-idi-48.c
@@ -34,7 +34,8 @@ module_param_hw_array(base, uint, ioport, &num_idi_48, 0);
MODULE_PARM_DESC(base, "ACCES 104-IDI-48 base addresses");
static unsigned int irq[MAX_NUM_IDI_48];
-module_param_hw_array(irq, uint, irq, NULL, 0);
+static unsigned int num_irq;
+module_param_hw_array(irq, uint, irq, &num_irq, 0);
MODULE_PARM_DESC(irq, "ACCES 104-IDI-48 interrupt line numbers");
/**
@@ -304,7 +305,7 @@ static struct isa_driver idi_48_driver = {
.name = "104-idi-48"
},
};
-module_isa_driver(idi_48_driver, num_idi_48);
+module_isa_driver_with_irq(idi_48_driver, num_idi_48, num_irq);
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("ACCES 104-IDI-48 GPIO driver");
diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c
index 4756e583f223..718bd54e2a25 100644
--- a/drivers/gpio/gpio-104-idio-16.c
+++ b/drivers/gpio/gpio-104-idio-16.c
@@ -30,7 +30,8 @@ module_param_hw_array(base, uint, ioport, &num_idio_16, 0);
MODULE_PARM_DESC(base, "ACCES 104-IDIO-16 base addresses");
static unsigned int irq[MAX_NUM_IDIO_16];
-module_param_hw_array(irq, uint, irq, NULL, 0);
+static unsigned int num_irq;
+module_param_hw_array(irq, uint, irq, &num_irq, 0);
MODULE_PARM_DESC(irq, "ACCES 104-IDIO-16 interrupt line numbers");
/**
@@ -337,7 +338,7 @@ static struct isa_driver idio_16_driver = {
},
};
-module_isa_driver(idio_16_driver, num_idio_16);
+module_isa_driver_with_irq(idio_16_driver, num_idio_16, num_irq);
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver");
diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c
index d37de78247a6..482f678c893e 100644
--- a/drivers/gpio/gpio-exar.c
+++ b/drivers/gpio/gpio-exar.c
@@ -21,6 +21,12 @@
#define EXAR_OFFSET_MPIOLVL_HI 0x96
#define EXAR_OFFSET_MPIOSEL_HI 0x99
+/*
+ * The Device Configuration and UART Configuration Registers
+ * for each UART channel take 1KB of memory address space.
+ */
+#define EXAR_UART_CHANNEL_SIZE 0x400
+
#define DRIVER_NAME "gpio_exar"
static DEFINE_IDA(ida_index);
@@ -31,26 +37,39 @@ struct exar_gpio_chip {
int index;
char name[20];
unsigned int first_pin;
+ /*
+ * The offset to the cascaded device's (if existing)
+ * Device Configuration Registers.
+ */
+ unsigned int cascaded_offset;
};
static unsigned int
exar_offset_to_sel_addr(struct exar_gpio_chip *exar_gpio, unsigned int offset)
{
- return (offset + exar_gpio->first_pin) / 8 ? EXAR_OFFSET_MPIOSEL_HI
- : EXAR_OFFSET_MPIOSEL_LO;
+ unsigned int pin = exar_gpio->first_pin + (offset % 16);
+ unsigned int cascaded = offset / 16;
+ unsigned int addr = pin / 8 ? EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO;
+
+ return addr + (cascaded ? exar_gpio->cascaded_offset : 0);
}
static unsigned int
exar_offset_to_lvl_addr(struct exar_gpio_chip *exar_gpio, unsigned int offset)
{
- return (offset + exar_gpio->first_pin) / 8 ? EXAR_OFFSET_MPIOLVL_HI
- : EXAR_OFFSET_MPIOLVL_LO;
+ unsigned int pin = exar_gpio->first_pin + (offset % 16);
+ unsigned int cascaded = offset / 16;
+ unsigned int addr = pin / 8 ? EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO;
+
+ return addr + (cascaded ? exar_gpio->cascaded_offset : 0);
}
static unsigned int
exar_offset_to_bit(struct exar_gpio_chip *exar_gpio, unsigned int offset)
{
- return (offset + exar_gpio->first_pin) % 8;
+ unsigned int pin = exar_gpio->first_pin + (offset % 16);
+
+ return pin % 8;
}
static int exar_get_direction(struct gpio_chip *chip, unsigned int offset)
@@ -154,6 +173,17 @@ static int gpio_exar_probe(struct platform_device *pdev)
return -ENOMEM;
/*
+ * If cascaded, secondary xr17v354 or xr17v358 have the same amount
+ * of MPIOs as their primaries and the last 4 bits of the primary's
+ * PCI Device ID is the number of its UART channels.
+ */
+ if (pcidev->device & GENMASK(15, 12)) {
+ ngpios += ngpios;
+ exar_gpio->cascaded_offset = (pcidev->device & GENMASK(3, 0)) *
+ EXAR_UART_CHANNEL_SIZE;
+ }
+
+ /*
* We don't need to check the return values of mmio regmap operations (unless
* the regmap has a clock attached which is not the case here).
*/
diff --git a/drivers/gpio/gpio-imx-scu.c b/drivers/gpio/gpio-imx-scu.c
new file mode 100644
index 000000000000..17be21b8f3b7
--- /dev/null
+++ b/drivers/gpio/gpio-imx-scu.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2021~2022 NXP
+ *
+ * The driver exports a standard gpiochip interface
+ * to control the PIN resources on SCU domain.
+ */
+
+#include <linux/module.h>
+#include <linux/gpio/driver.h>
+#include <linux/platform_device.h>
+#include <linux/firmware/imx/svc/rm.h>
+#include <dt-bindings/firmware/imx/rsrc.h>
+
+struct scu_gpio_priv {
+ struct gpio_chip chip;
+ struct mutex lock;
+ struct device *dev;
+ struct imx_sc_ipc *handle;
+};
+
+static unsigned int scu_rsrc_arr[] = {
+ IMX_SC_R_BOARD_R0,
+ IMX_SC_R_BOARD_R1,
+ IMX_SC_R_BOARD_R2,
+ IMX_SC_R_BOARD_R3,
+ IMX_SC_R_BOARD_R4,
+ IMX_SC_R_BOARD_R5,
+ IMX_SC_R_BOARD_R6,
+ IMX_SC_R_BOARD_R7,
+};
+
+static int imx_scu_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct scu_gpio_priv *priv = gpiochip_get_data(chip);
+ int level;
+ int err;
+
+ if (offset >= chip->ngpio)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ /* to read PIN state via scu api */
+ err = imx_sc_misc_get_control(priv->handle,
+ scu_rsrc_arr[offset], 0, &level);
+ mutex_unlock(&priv->lock);
+
+ if (err) {
+ dev_err(priv->dev, "SCU get failed: %d\n", err);
+ return err;
+ }
+
+ return level;
+}
+
+static void imx_scu_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
+{
+ struct scu_gpio_priv *priv = gpiochip_get_data(chip);
+ int err;
+
+ if (offset >= chip->ngpio)
+ return;
+
+ mutex_lock(&priv->lock);
+
+ /* to set PIN output level via scu api */
+ err = imx_sc_misc_set_control(priv->handle,
+ scu_rsrc_arr[offset], 0, value);
+ mutex_unlock(&priv->lock);
+
+ if (err)
+ dev_err(priv->dev, "SCU set (%d) failed: %d\n",
+ scu_rsrc_arr[offset], err);
+}
+
+static int imx_scu_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+ if (offset >= chip->ngpio)
+ return -EINVAL;
+
+ return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int imx_scu_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct scu_gpio_priv *priv;
+ struct gpio_chip *gc;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ ret = imx_scu_get_handle(&priv->handle);
+ if (ret)
+ return ret;
+
+ priv->dev = dev;
+ mutex_init(&priv->lock);
+
+ gc = &priv->chip;
+ gc->base = -1;
+ gc->parent = dev;
+ gc->ngpio = sizeof(scu_rsrc_arr)/sizeof(unsigned int);
+ gc->label = dev_name(dev);
+ gc->get = imx_scu_gpio_get;
+ gc->set = imx_scu_gpio_set;
+ gc->get_direction = imx_scu_gpio_get_direction;
+
+ platform_set_drvdata(pdev, priv);
+
+ return devm_gpiochip_add_data(dev, gc, priv);
+}
+
+static const struct of_device_id imx_scu_gpio_dt_ids[] = {
+ { .compatible = "fsl,imx8qxp-sc-gpio" },
+ { /* sentinel */ }
+};
+
+static struct platform_driver imx_scu_gpio_driver = {
+ .driver = {
+ .name = "gpio-imx-scu",
+ .of_match_table = imx_scu_gpio_dt_ids,
+ },
+ .probe = imx_scu_gpio_probe,
+};
+
+static int __init _imx_scu_gpio_init(void)
+{
+ return platform_driver_register(&imx_scu_gpio_driver);
+}
+
+subsys_initcall_sync(_imx_scu_gpio_init);
+
+MODULE_AUTHOR("Shenwei Wang <shenwei.wang@nxp.com>");
+MODULE_DESCRIPTION("NXP GPIO over IMX SCU API");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c
index f163f5ca857b..93facbebb80e 100644
--- a/drivers/gpio/gpio-mt7621.c
+++ b/drivers/gpio/gpio-mt7621.c
@@ -9,7 +9,6 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
@@ -299,7 +298,6 @@ static int
mediatek_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
struct mtk *mtk;
int i;
int ret;
@@ -312,7 +310,10 @@ mediatek_gpio_probe(struct platform_device *pdev)
if (IS_ERR(mtk->base))
return PTR_ERR(mtk->base);
- mtk->gpio_irq = irq_of_parse_and_map(np, 0);
+ mtk->gpio_irq = platform_get_irq(pdev, 0);
+ if (mtk->gpio_irq < 0)
+ return mtk->gpio_irq;
+
mtk->dev = dev;
platform_set_drvdata(pdev, mtk);
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index cf9bf3fcaee2..ebe1943b85dd 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -66,6 +66,7 @@
#define PCA_LATCH_INT (PCA_PCAL | PCA_INT)
#define PCA953X_TYPE BIT(12)
#define PCA957X_TYPE BIT(13)
+#define PCAL653X_TYPE BIT(14)
#define PCA_TYPE_MASK GENMASK(15, 12)
#define PCA_CHIP_TYPE(x) ((x) & PCA_TYPE_MASK)
@@ -89,8 +90,10 @@ static const struct i2c_device_id pca953x_id[] = {
{ "pca9575", 16 | PCA957X_TYPE | PCA_INT, },
{ "pca9698", 40 | PCA953X_TYPE, },
+ { "pcal6408", 8 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "pcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "pcal6524", 24 | PCA953X_TYPE | PCA_LATCH_INT, },
+ { "pcal6534", 34 | PCAL653X_TYPE | PCA_LATCH_INT, },
{ "pcal9535", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "pcal9554b", 8 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "pcal9555a", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
@@ -211,6 +214,10 @@ struct pca953x_chip {
struct regulator *regulator;
const struct pca953x_reg_config *regs;
+
+ u8 (*recalc_addr)(struct pca953x_chip *chip, int reg, int off);
+ bool (*check_reg)(struct pca953x_chip *chip, unsigned int reg,
+ u32 checkbank);
};
static int pca953x_bank_shift(struct pca953x_chip *chip)
@@ -288,18 +295,67 @@ static bool pca953x_check_register(struct pca953x_chip *chip, unsigned int reg,
return true;
}
+/*
+ * Unfortunately, whilst the PCAL6534 chip (and compatibles) broadly follow the
+ * same register layout as the PCAL6524, the spacing of the registers has been
+ * fundamentally altered by compacting them and thus does not obey the same
+ * rules, including being able to use bit shifting to determine bank. These
+ * chips hence need special handling here.
+ */
+static bool pcal6534_check_register(struct pca953x_chip *chip, unsigned int reg,
+ u32 checkbank)
+{
+ int bank;
+ int offset;
+
+ if (reg >= 0x30) {
+ /*
+ * Reserved block between 14h and 2Fh does not align on
+ * expected bank boundaries like other devices.
+ */
+ int temp = reg - 0x30;
+
+ bank = temp / NBANK(chip);
+ offset = temp - (bank * NBANK(chip));
+ bank += 8;
+ } else if (reg >= 0x54) {
+ /*
+ * Handle lack of reserved registers after output port
+ * configuration register to form a bank.
+ */
+ int temp = reg - 0x54;
+
+ bank = temp / NBANK(chip);
+ offset = temp - (bank * NBANK(chip));
+ bank += 16;
+ } else {
+ bank = reg / NBANK(chip);
+ offset = reg - (bank * NBANK(chip));
+ }
+
+ /* Register is not in the matching bank. */
+ if (!(BIT(bank) & checkbank))
+ return false;
+
+ /* Register is not within allowed range of bank. */
+ if (offset >= NBANK(chip))
+ return false;
+
+ return true;
+}
+
static bool pca953x_readable_register(struct device *dev, unsigned int reg)
{
struct pca953x_chip *chip = dev_get_drvdata(dev);
u32 bank;
- if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) {
- bank = PCA953x_BANK_INPUT | PCA953x_BANK_OUTPUT |
- PCA953x_BANK_POLARITY | PCA953x_BANK_CONFIG;
- } else {
+ if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) {
bank = PCA957x_BANK_INPUT | PCA957x_BANK_OUTPUT |
PCA957x_BANK_POLARITY | PCA957x_BANK_CONFIG |
PCA957x_BANK_BUSHOLD;
+ } else {
+ bank = PCA953x_BANK_INPUT | PCA953x_BANK_OUTPUT |
+ PCA953x_BANK_POLARITY | PCA953x_BANK_CONFIG;
}
if (chip->driver_data & PCA_PCAL) {
@@ -308,7 +364,7 @@ static bool pca953x_readable_register(struct device *dev, unsigned int reg)
PCAL9xxx_BANK_IRQ_STAT;
}
- return pca953x_check_register(chip, reg, bank);
+ return chip->check_reg(chip, reg, bank);
}
static bool pca953x_writeable_register(struct device *dev, unsigned int reg)
@@ -316,19 +372,19 @@ static bool pca953x_writeable_register(struct device *dev, unsigned int reg)
struct pca953x_chip *chip = dev_get_drvdata(dev);
u32 bank;
- if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) {
- bank = PCA953x_BANK_OUTPUT | PCA953x_BANK_POLARITY |
- PCA953x_BANK_CONFIG;
- } else {
+ if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) {
bank = PCA957x_BANK_OUTPUT | PCA957x_BANK_POLARITY |
PCA957x_BANK_CONFIG | PCA957x_BANK_BUSHOLD;
+ } else {
+ bank = PCA953x_BANK_OUTPUT | PCA953x_BANK_POLARITY |
+ PCA953x_BANK_CONFIG;
}
if (chip->driver_data & PCA_PCAL)
bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_PULL_EN |
PCAL9xxx_BANK_PULL_SEL | PCAL9xxx_BANK_IRQ_MASK;
- return pca953x_check_register(chip, reg, bank);
+ return chip->check_reg(chip, reg, bank);
}
static bool pca953x_volatile_register(struct device *dev, unsigned int reg)
@@ -336,15 +392,15 @@ static bool pca953x_volatile_register(struct device *dev, unsigned int reg)
struct pca953x_chip *chip = dev_get_drvdata(dev);
u32 bank;
- if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE)
- bank = PCA953x_BANK_INPUT;
- else
+ if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE)
bank = PCA957x_BANK_INPUT;
+ else
+ bank = PCA953x_BANK_INPUT;
if (chip->driver_data & PCA_PCAL)
bank |= PCAL9xxx_BANK_IRQ_STAT;
- return pca953x_check_register(chip, reg, bank);
+ return chip->check_reg(chip, reg, bank);
}
static const struct regmap_config pca953x_i2c_regmap = {
@@ -389,9 +445,42 @@ static u8 pca953x_recalc_addr(struct pca953x_chip *chip, int reg, int off)
return regaddr;
}
+/*
+ * The PCAL6534 and compatible chips have altered bank alignment that doesn't
+ * fit within the bit shifting scheme used for other devices.
+ */
+static u8 pcal6534_recalc_addr(struct pca953x_chip *chip, int reg, int off)
+{
+ int addr;
+ int pinctrl;
+
+ addr = (reg & PCAL_GPIO_MASK) * NBANK(chip);
+
+ switch (reg) {
+ case PCAL953X_OUT_STRENGTH:
+ case PCAL953X_IN_LATCH:
+ case PCAL953X_PULL_EN:
+ case PCAL953X_PULL_SEL:
+ case PCAL953X_INT_MASK:
+ case PCAL953X_INT_STAT:
+ case PCAL953X_OUT_CONF:
+ pinctrl = ((reg & PCAL_PINCTRL_MASK) >> 1) + 0x20;
+ break;
+ case PCAL6524_INT_EDGE:
+ case PCAL6524_INT_CLR:
+ case PCAL6524_IN_STATUS:
+ case PCAL6524_OUT_INDCONF:
+ case PCAL6524_DEBOUNCE:
+ pinctrl = ((reg & PCAL_PINCTRL_MASK) >> 1) + 0x1c;
+ break;
+ }
+
+ return pinctrl + addr + (off / BANK_SZ);
+}
+
static int pca953x_write_regs(struct pca953x_chip *chip, int reg, unsigned long *val)
{
- u8 regaddr = pca953x_recalc_addr(chip, reg, 0);
+ u8 regaddr = chip->recalc_addr(chip, reg, 0);
u8 value[MAX_BANK];
int i, ret;
@@ -409,7 +498,7 @@ static int pca953x_write_regs(struct pca953x_chip *chip, int reg, unsigned long
static int pca953x_read_regs(struct pca953x_chip *chip, int reg, unsigned long *val)
{
- u8 regaddr = pca953x_recalc_addr(chip, reg, 0);
+ u8 regaddr = chip->recalc_addr(chip, reg, 0);
u8 value[MAX_BANK];
int i, ret;
@@ -428,7 +517,7 @@ static int pca953x_read_regs(struct pca953x_chip *chip, int reg, unsigned long *
static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
- u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off);
+ u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off);
u8 bit = BIT(off % BANK_SZ);
int ret;
@@ -442,8 +531,8 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
unsigned off, int val)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
- u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off);
- u8 outreg = pca953x_recalc_addr(chip, chip->regs->output, off);
+ u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off);
+ u8 outreg = chip->recalc_addr(chip, chip->regs->output, off);
u8 bit = BIT(off % BANK_SZ);
int ret;
@@ -463,7 +552,7 @@ exit:
static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
- u8 inreg = pca953x_recalc_addr(chip, chip->regs->input, off);
+ u8 inreg = chip->recalc_addr(chip, chip->regs->input, off);
u8 bit = BIT(off % BANK_SZ);
u32 reg_val;
int ret;
@@ -480,7 +569,7 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
- u8 outreg = pca953x_recalc_addr(chip, chip->regs->output, off);
+ u8 outreg = chip->recalc_addr(chip, chip->regs->output, off);
u8 bit = BIT(off % BANK_SZ);
mutex_lock(&chip->i2c_lock);
@@ -491,7 +580,7 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
- u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off);
+ u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off);
u8 bit = BIT(off % BANK_SZ);
u32 reg_val;
int ret;
@@ -548,8 +637,10 @@ 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);
- u8 pull_sel_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_SEL, offset);
+ enum pin_config_param param = pinconf_to_config_param(config);
+
+ u8 pull_en_reg = chip->recalc_addr(chip, PCAL953X_PULL_EN, offset);
+ u8 pull_sel_reg = chip->recalc_addr(chip, PCAL953X_PULL_SEL, offset);
u8 bit = BIT(offset % BANK_SZ);
int ret;
@@ -563,9 +654,9 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip,
mutex_lock(&chip->i2c_lock);
/* Configure pull-up/pull-down */
- if (config == PIN_CONFIG_BIAS_PULL_UP)
+ if (param == PIN_CONFIG_BIAS_PULL_UP)
ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, bit);
- else if (config == PIN_CONFIG_BIAS_PULL_DOWN)
+ else if (param == PIN_CONFIG_BIAS_PULL_DOWN)
ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, 0);
else
ret = 0;
@@ -573,7 +664,7 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip,
goto exit;
/* Disable/Enable pull-up/pull-down */
- if (config == PIN_CONFIG_BIAS_DISABLE)
+ if (param == PIN_CONFIG_BIAS_DISABLE)
ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, 0);
else
ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, bit);
@@ -912,13 +1003,13 @@ static int device_pca95xx_init(struct pca953x_chip *chip, u32 invert)
u8 regaddr;
int ret;
- regaddr = pca953x_recalc_addr(chip, chip->regs->output, 0);
+ regaddr = chip->recalc_addr(chip, chip->regs->output, 0);
ret = regcache_sync_region(chip->regmap, regaddr,
regaddr + NBANK(chip) - 1);
if (ret)
goto out;
- regaddr = pca953x_recalc_addr(chip, chip->regs->direction, 0);
+ regaddr = chip->recalc_addr(chip, chip->regs->direction, 0);
ret = regcache_sync_region(chip->regmap, regaddr,
regaddr + NBANK(chip) - 1);
if (ret)
@@ -1037,6 +1128,14 @@ static int pca953x_probe(struct i2c_client *client,
regmap_config = &pca953x_i2c_regmap;
}
+ if (PCA_CHIP_TYPE(chip->driver_data) == PCAL653X_TYPE) {
+ chip->recalc_addr = pcal6534_recalc_addr;
+ chip->check_reg = pcal6534_check_register;
+ } else {
+ chip->recalc_addr = pca953x_recalc_addr;
+ chip->check_reg = pca953x_check_register;
+ }
+
chip->regmap = devm_regmap_init_i2c(client, regmap_config);
if (IS_ERR(chip->regmap)) {
ret = PTR_ERR(chip->regmap);
@@ -1068,13 +1167,12 @@ static int pca953x_probe(struct i2c_client *client,
/* initialize cached registers from their original values.
* we can't share this chip with another i2c master.
*/
-
- if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) {
- chip->regs = &pca953x_regs;
- ret = device_pca95xx_init(chip, invert);
- } else {
+ if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) {
chip->regs = &pca957x_regs;
ret = device_pca957x_init(chip, invert);
+ } else {
+ chip->regs = &pca953x_regs;
+ ret = device_pca95xx_init(chip, invert);
}
if (ret)
goto err_exit;
@@ -1125,14 +1223,14 @@ static int pca953x_regcache_sync(struct device *dev)
* The ordering between direction and output is important,
* sync these registers first and only then sync the rest.
*/
- regaddr = pca953x_recalc_addr(chip, chip->regs->direction, 0);
+ regaddr = chip->recalc_addr(chip, chip->regs->direction, 0);
ret = regcache_sync_region(chip->regmap, regaddr, regaddr + NBANK(chip) - 1);
if (ret) {
dev_err(dev, "Failed to sync GPIO dir registers: %d\n", ret);
return ret;
}
- regaddr = pca953x_recalc_addr(chip, chip->regs->output, 0);
+ regaddr = chip->recalc_addr(chip, chip->regs->output, 0);
ret = regcache_sync_region(chip->regmap, regaddr, regaddr + NBANK(chip) - 1);
if (ret) {
dev_err(dev, "Failed to sync GPIO out registers: %d\n", ret);
@@ -1141,7 +1239,7 @@ static int pca953x_regcache_sync(struct device *dev)
#ifdef CONFIG_GPIO_PCA953X_IRQ
if (chip->driver_data & PCA_PCAL) {
- regaddr = pca953x_recalc_addr(chip, PCAL953X_IN_LATCH, 0);
+ regaddr = chip->recalc_addr(chip, PCAL953X_IN_LATCH, 0);
ret = regcache_sync_region(chip->regmap, regaddr,
regaddr + NBANK(chip) - 1);
if (ret) {
@@ -1150,7 +1248,7 @@ static int pca953x_regcache_sync(struct device *dev)
return ret;
}
- regaddr = pca953x_recalc_addr(chip, PCAL953X_INT_MASK, 0);
+ regaddr = chip->recalc_addr(chip, PCAL953X_INT_MASK, 0);
ret = regcache_sync_region(chip->regmap, regaddr,
regaddr + NBANK(chip) - 1);
if (ret) {
@@ -1214,6 +1312,7 @@ static int pca953x_resume(struct device *dev)
#endif
/* convenience to stop overlong match-table lines */
+#define OF_653X(__nrgpio, __int) ((void *)(__nrgpio | PCAL653X_TYPE | __int))
#define OF_953X(__nrgpio, __int) (void *)(__nrgpio | PCA953X_TYPE | __int)
#define OF_957X(__nrgpio, __int) (void *)(__nrgpio | PCA957X_TYPE | __int)
@@ -1236,8 +1335,10 @@ static const struct of_device_id pca953x_dt_ids[] = {
{ .compatible = "nxp,pca9575", .data = OF_957X(16, PCA_INT), },
{ .compatible = "nxp,pca9698", .data = OF_953X(40, 0), },
+ { .compatible = "nxp,pcal6408", .data = OF_953X(8, PCA_LATCH_INT), },
{ .compatible = "nxp,pcal6416", .data = OF_953X(16, PCA_LATCH_INT), },
{ .compatible = "nxp,pcal6524", .data = OF_953X(24, PCA_LATCH_INT), },
+ { .compatible = "nxp,pcal6534", .data = OF_653X(34, PCA_LATCH_INT), },
{ .compatible = "nxp,pcal9535", .data = OF_953X(16, PCA_LATCH_INT), },
{ .compatible = "nxp,pcal9554b", .data = OF_953X( 8, PCA_LATCH_INT), },
{ .compatible = "nxp,pcal9555a", .data = OF_953X(16, PCA_LATCH_INT), },
diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c
index bb50335239ac..6765477edb06 100644
--- a/drivers/gpio/gpio-rockchip.c
+++ b/drivers/gpio/gpio-rockchip.c
@@ -325,26 +325,15 @@ static void rockchip_irq_demux(struct irq_desc *desc)
{
struct irq_chip *chip = irq_desc_get_chip(desc);
struct rockchip_pin_bank *bank = irq_desc_get_handler_data(desc);
- u32 pend;
+ unsigned long pending;
+ unsigned int irq;
dev_dbg(bank->dev, "got irq for bank %s\n", bank->name);
chained_irq_enter(chip, desc);
- pend = readl_relaxed(bank->reg_base + bank->gpio_regs->int_status);
-
- while (pend) {
- unsigned int irq, virq;
-
- irq = __ffs(pend);
- pend &= ~BIT(irq);
- virq = irq_find_mapping(bank->domain, irq);
-
- if (!virq) {
- dev_err(bank->dev, "unmapped irq %d\n", irq);
- continue;
- }
-
+ pending = readl_relaxed(bank->reg_base + bank->gpio_regs->int_status);
+ for_each_set_bit(irq, &pending, 32) {
dev_dbg(bank->dev, "handling irq %d\n", irq);
/*
@@ -378,7 +367,7 @@ static void rockchip_irq_demux(struct irq_desc *desc)
} while ((data & BIT(irq)) != (data_old & BIT(irq)));
}
- generic_handle_irq(virq);
+ generic_handle_domain_irq(bank->domain, irq);
}
chained_irq_exit(chip, desc);
diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c
index 443fe975bf13..e62ee7e56908 100644
--- a/drivers/gpio/gpio-tc3589x.c
+++ b/drivers/gpio/gpio-tc3589x.c
@@ -230,6 +230,7 @@ static void tc3589x_gpio_irq_mask(struct irq_data *d)
tc3589x_gpio->regs[REG_IE][regoffset] &= ~mask;
tc3589x_gpio->regs[REG_DIRECT][regoffset] |= mask;
+ gpiochip_disable_irq(gc, offset);
}
static void tc3589x_gpio_irq_unmask(struct irq_data *d)
@@ -240,17 +241,20 @@ static void tc3589x_gpio_irq_unmask(struct irq_data *d)
int regoffset = offset / 8;
int mask = BIT(offset % 8);
+ gpiochip_enable_irq(gc, offset);
tc3589x_gpio->regs[REG_IE][regoffset] |= mask;
tc3589x_gpio->regs[REG_DIRECT][regoffset] &= ~mask;
}
-static struct irq_chip tc3589x_gpio_irq_chip = {
+static const struct irq_chip tc3589x_gpio_irq_chip = {
.name = "tc3589x-gpio",
.irq_bus_lock = tc3589x_gpio_irq_lock,
.irq_bus_sync_unlock = tc3589x_gpio_irq_sync_unlock,
.irq_mask = tc3589x_gpio_irq_mask,
.irq_unmask = tc3589x_gpio_irq_unmask,
.irq_set_type = tc3589x_gpio_irq_set_type,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static irqreturn_t tc3589x_gpio_irq(int irq, void *dev)
@@ -321,7 +325,7 @@ static int tc3589x_gpio_probe(struct platform_device *pdev)
tc3589x_gpio->chip.base = -1;
girq = &tc3589x_gpio->chip.irq;
- girq->chip = &tc3589x_gpio_irq_chip;
+ gpio_irq_chip_set_chip(girq, &tc3589x_gpio_irq_chip);
/* This will let us handle the parent IRQ in the driver */
girq->parent_handler = NULL;
girq->num_parents = 0;
diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c
index 5046e51af8df..c1bb2c3ca6f2 100644
--- a/drivers/gpio/gpio-twl4030.c
+++ b/drivers/gpio/gpio-twl4030.c
@@ -465,8 +465,6 @@ static int gpio_twl4030_debounce(u32 debounce, u8 mmc_cd)
REG_GPIO_DEBEN1, 3);
}
-static int gpio_twl4030_remove(struct platform_device *pdev);
-
static struct twl4030_gpio_platform_data *of_gpio_twl4030(struct device *dev,
struct twl4030_gpio_platform_data *pdata)
{
@@ -494,6 +492,18 @@ static struct twl4030_gpio_platform_data *of_gpio_twl4030(struct device *dev,
return omap_twl_info;
}
+/* Cannot use as gpio_twl4030_probe() calls us */
+static int gpio_twl4030_remove(struct platform_device *pdev)
+{
+ struct gpio_twl4030_priv *priv = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&priv->gpio_chip);
+
+ /* REVISIT no support yet for deregistering all the IRQs */
+ WARN_ON(!is_module());
+ return 0;
+}
+
static int gpio_twl4030_probe(struct platform_device *pdev)
{
struct twl4030_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
@@ -590,18 +600,6 @@ out:
return ret;
}
-/* Cannot use as gpio_twl4030_probe() calls us */
-static int gpio_twl4030_remove(struct platform_device *pdev)
-{
- struct gpio_twl4030_priv *priv = platform_get_drvdata(pdev);
-
- gpiochip_remove(&priv->gpio_chip);
-
- /* REVISIT no support yet for deregistering all the IRQs */
- WARN_ON(!is_module());
- return 0;
-}
-
static const struct of_device_id twl_gpio_match[] = {
{ .compatible = "ti,twl4030-gpio", },
{ },
diff --git a/drivers/gpio/gpio-ucb1400.c b/drivers/gpio/gpio-ucb1400.c
index 386e69300332..676adf1f198a 100644
--- a/drivers/gpio/gpio-ucb1400.c
+++ b/drivers/gpio/gpio-ucb1400.c
@@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/ucb1400.h>
+#include <linux/gpio/driver.h>
static int ucb1400_gpio_dir_in(struct gpio_chip *gc, unsigned off)
{
diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c
index 59fb10641598..e73885a4dc32 100644
--- a/drivers/gpio/gpio-ws16c48.c
+++ b/drivers/gpio/gpio-ws16c48.c
@@ -27,7 +27,8 @@ module_param_hw_array(base, uint, ioport, &num_ws16c48, 0);
MODULE_PARM_DESC(base, "WinSystems WS16C48 base addresses");
static unsigned int irq[MAX_NUM_WS16C48];
-module_param_hw_array(irq, uint, irq, NULL, 0);
+static unsigned int num_irq;
+module_param_hw_array(irq, uint, irq, &num_irq, 0);
MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers");
/**
@@ -501,7 +502,7 @@ static struct isa_driver ws16c48_driver = {
},
};
-module_isa_driver(ws16c48_driver, num_ws16c48);
+module_isa_driver_with_irq(ws16c48_driver, num_ws16c48, num_irq);
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("WinSystems WS16C48 GPIO driver");
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index 9be1376f9a62..285ecbf107c9 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -32,9 +32,16 @@ MODULE_PARM_DESC(ignore_wake,
"controller@pin combos on which to ignore the ACPI wake flag "
"ignore_wake=controller@pin[,controller@pin[,...]]");
+static char *ignore_interrupt;
+module_param(ignore_interrupt, charp, 0444);
+MODULE_PARM_DESC(ignore_interrupt,
+ "controller@pin combos on which to ignore interrupt "
+ "ignore_interrupt=controller@pin[,controller@pin[,...]]");
+
struct acpi_gpiolib_dmi_quirk {
bool no_edge_events_on_boot;
char *ignore_wake;
+ char *ignore_interrupt;
};
/**
@@ -317,14 +324,15 @@ static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip,
return desc;
}
-static bool acpi_gpio_in_ignore_list(const char *controller_in, unsigned int pin_in)
+static bool acpi_gpio_in_ignore_list(const char *ignore_list, const char *controller_in,
+ unsigned int pin_in)
{
const char *controller, *pin_str;
unsigned int pin;
char *endp;
int len;
- controller = ignore_wake;
+ controller = ignore_list;
while (controller) {
pin_str = strchr(controller, '@');
if (!pin_str)
@@ -348,7 +356,7 @@ static bool acpi_gpio_in_ignore_list(const char *controller_in, unsigned int pin
return false;
err:
- pr_err_once("Error: Invalid value for gpiolib_acpi.ignore_wake: %s\n", ignore_wake);
+ pr_err_once("Error: Invalid value for gpiolib_acpi.ignore_...: %s\n", ignore_list);
return false;
}
@@ -360,7 +368,7 @@ static bool acpi_gpio_irq_is_wake(struct device *parent,
if (agpio->wake_capable != ACPI_WAKE_CAPABLE)
return false;
- if (acpi_gpio_in_ignore_list(dev_name(parent), pin)) {
+ if (acpi_gpio_in_ignore_list(ignore_wake, dev_name(parent), pin)) {
dev_info(parent, "Ignoring wakeup on pin %u\n", pin);
return false;
}
@@ -427,6 +435,11 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares,
goto fail_unlock_irq;
}
+ if (acpi_gpio_in_ignore_list(ignore_interrupt, dev_name(chip->parent), pin)) {
+ dev_info(chip->parent, "Ignoring interrupt on pin %u\n", pin);
+ return AE_OK;
+ }
+
event = kzalloc(sizeof(*event), GFP_KERNEL);
if (!event)
goto fail_unlock_irq;
@@ -1563,6 +1576,20 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = {
.ignore_wake = "INT33FF:01@0",
},
},
+ {
+ /*
+ * Interrupt storm caused from edge triggered floating pin
+ * Found in BIOS UX325UAZ.300
+ * https://bugzilla.kernel.org/show_bug.cgi?id=216208
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UAZ_UM325UAZ"),
+ },
+ .driver_data = &(struct acpi_gpiolib_dmi_quirk) {
+ .ignore_interrupt = "AMDI0030:00@18",
+ },
+ },
{} /* Terminating entry */
};
@@ -1585,6 +1612,9 @@ static int __init acpi_gpio_setup_params(void)
if (ignore_wake == NULL && quirk && quirk->ignore_wake)
ignore_wake = quirk->ignore_wake;
+ if (ignore_interrupt == NULL && quirk && quirk->ignore_interrupt)
+ ignore_interrupt = quirk->ignore_interrupt;
+
return 0;
}
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 92f185575e94..0cb6b468f364 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -1497,6 +1497,21 @@ static int linereq_release(struct inode *inode, struct file *file)
return 0;
}
+#ifdef CONFIG_PROC_FS
+static void linereq_show_fdinfo(struct seq_file *out, struct file *file)
+{
+ struct linereq *lr = file->private_data;
+ struct device *dev = &lr->gdev->dev;
+ u16 i;
+
+ seq_printf(out, "gpio-chip:\t%s\n", dev_name(dev));
+
+ for (i = 0; i < lr->num_lines; i++)
+ seq_printf(out, "gpio-line:\t%d\n",
+ gpio_chip_hwgpio(lr->lines[i].desc));
+}
+#endif
+
static const struct file_operations line_fileops = {
.release = linereq_release,
.read = linereq_read,
@@ -1507,6 +1522,9 @@ static const struct file_operations line_fileops = {
#ifdef CONFIG_COMPAT
.compat_ioctl = linereq_ioctl_compat,
#endif
+#ifdef CONFIG_PROC_FS
+ .show_fdinfo = linereq_show_fdinfo,
+#endif
};
static int linereq_create(struct gpio_device *gdev, void __user *ip)
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index a037b50bef33..0e4e1291604d 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -289,6 +289,36 @@ int of_get_named_gpio_flags(const struct device_node *np, const char *list_name,
}
EXPORT_SYMBOL_GPL(of_get_named_gpio_flags);
+/* Converts gpio_lookup_flags into bitmask of GPIO_* values */
+static unsigned long of_convert_gpio_flags(enum of_gpio_flags flags)
+{
+ unsigned long lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
+
+ if (flags & OF_GPIO_ACTIVE_LOW)
+ lflags |= GPIO_ACTIVE_LOW;
+
+ if (flags & OF_GPIO_SINGLE_ENDED) {
+ if (flags & OF_GPIO_OPEN_DRAIN)
+ lflags |= GPIO_OPEN_DRAIN;
+ else
+ lflags |= GPIO_OPEN_SOURCE;
+ }
+
+ if (flags & OF_GPIO_TRANSITORY)
+ lflags |= GPIO_TRANSITORY;
+
+ if (flags & OF_GPIO_PULL_UP)
+ lflags |= GPIO_PULL_UP;
+
+ if (flags & OF_GPIO_PULL_DOWN)
+ lflags |= GPIO_PULL_DOWN;
+
+ if (flags & OF_GPIO_PULL_DISABLE)
+ lflags |= GPIO_PULL_DISABLE;
+
+ return lflags;
+}
+
/**
* gpiod_get_from_of_node() - obtain a GPIO from an OF node
* @node: handle of the OF node
@@ -308,26 +338,14 @@ struct gpio_desc *gpiod_get_from_of_node(const struct device_node *node,
enum gpiod_flags dflags,
const char *label)
{
- unsigned long lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
+ unsigned long lflags;
struct gpio_desc *desc;
- enum of_gpio_flags flags;
- bool active_low = false;
- bool single_ended = false;
- bool open_drain = false;
- bool transitory = false;
+ enum of_gpio_flags of_flags;
int ret;
- desc = of_get_named_gpiod_flags(node, propname,
- index, &flags);
-
- if (!desc || IS_ERR(desc)) {
+ desc = of_get_named_gpiod_flags(node, propname, index, &of_flags);
+ if (!desc || IS_ERR(desc))
return desc;
- }
-
- active_low = flags & OF_GPIO_ACTIVE_LOW;
- single_ended = flags & OF_GPIO_SINGLE_ENDED;
- open_drain = flags & OF_GPIO_OPEN_DRAIN;
- transitory = flags & OF_GPIO_TRANSITORY;
ret = gpiod_request(desc, label);
if (ret == -EBUSY && (dflags & GPIOD_FLAGS_BIT_NONEXCLUSIVE))
@@ -335,27 +353,7 @@ struct gpio_desc *gpiod_get_from_of_node(const struct device_node *node,
if (ret)
return ERR_PTR(ret);
- if (active_low)
- lflags |= GPIO_ACTIVE_LOW;
-
- if (single_ended) {
- if (open_drain)
- lflags |= GPIO_OPEN_DRAIN;
- else
- lflags |= GPIO_OPEN_SOURCE;
- }
-
- if (transitory)
- lflags |= GPIO_TRANSITORY;
-
- if (flags & OF_GPIO_PULL_UP)
- lflags |= GPIO_PULL_UP;
-
- if (flags & OF_GPIO_PULL_DOWN)
- lflags |= GPIO_PULL_DOWN;
-
- if (flags & OF_GPIO_PULL_DISABLE)
- lflags |= GPIO_PULL_DISABLE;
+ lflags = of_convert_gpio_flags(of_flags);
ret = gpiod_configure_flags(desc, propname, lflags, dflags);
if (ret < 0) {
@@ -372,12 +370,12 @@ EXPORT_SYMBOL_GPL(gpiod_get_from_of_node);
* properties should be named "foo-gpios" so we have this special kludge for
* them.
*/
-static struct gpio_desc *of_find_spi_gpio(struct device *dev, const char *con_id,
+static struct gpio_desc *of_find_spi_gpio(struct device_node *np,
+ const char *con_id,
+ unsigned int idx,
enum of_gpio_flags *of_flags)
{
char prop_name[32]; /* 32 is max size of property name */
- const struct device_node *np = dev->of_node;
- struct gpio_desc *desc;
/*
* Hopefully the compiler stubs the rest of the function if this
@@ -393,8 +391,7 @@ static struct gpio_desc *of_find_spi_gpio(struct device *dev, const char *con_id
/* Will be "gpio-sck", "gpio-mosi" or "gpio-miso" */
snprintf(prop_name, sizeof(prop_name), "%s-%s", "gpio", con_id);
- desc = of_get_named_gpiod_flags(np, prop_name, 0, of_flags);
- return desc;
+ return of_get_named_gpiod_flags(np, prop_name, idx, of_flags);
}
/*
@@ -402,13 +399,11 @@ static struct gpio_desc *of_find_spi_gpio(struct device *dev, const char *con_id
* lines rather than "cs-gpios" like all other SPI hardware. Account for this
* with a special quirk.
*/
-static struct gpio_desc *of_find_spi_cs_gpio(struct device *dev,
+static struct gpio_desc *of_find_spi_cs_gpio(struct device_node *np,
const char *con_id,
unsigned int idx,
- unsigned long *flags)
+ enum of_gpio_flags *of_flags)
{
- const struct device_node *np = dev->of_node;
-
if (!IS_ENABLED(CONFIG_SPI_MASTER))
return ERR_PTR(-ENOENT);
@@ -426,7 +421,7 @@ static struct gpio_desc *of_find_spi_cs_gpio(struct device *dev,
* uses just "gpios" so translate to that when "cs-gpios" is
* requested.
*/
- return of_find_gpio(dev, NULL, idx, flags);
+ return of_get_named_gpiod_flags(np, "gpios", idx, of_flags);
}
/*
@@ -434,7 +429,9 @@ static struct gpio_desc *of_find_spi_cs_gpio(struct device *dev,
* properties should be named "foo-gpios" so we have this special kludge for
* them.
*/
-static struct gpio_desc *of_find_regulator_gpio(struct device *dev, const char *con_id,
+static struct gpio_desc *of_find_regulator_gpio(struct device_node *np,
+ const char *con_id,
+ unsigned int idx,
enum of_gpio_flags *of_flags)
{
/* These are the connection IDs we accept as legacy GPIO phandles */
@@ -443,8 +440,6 @@ static struct gpio_desc *of_find_regulator_gpio(struct device *dev, const char *
"wlf,ldo1ena", /* WM8994 */
"wlf,ldo2ena", /* WM8994 */
};
- const struct device_node *np = dev->of_node;
- struct gpio_desc *desc;
int i;
if (!IS_ENABLED(CONFIG_REGULATOR))
@@ -457,12 +452,12 @@ static struct gpio_desc *of_find_regulator_gpio(struct device *dev, const char *
if (i < 0)
return ERR_PTR(-ENOENT);
- desc = of_get_named_gpiod_flags(np, con_id, 0, of_flags);
- return desc;
+ return of_get_named_gpiod_flags(np, con_id, idx, of_flags);
}
-static struct gpio_desc *of_find_arizona_gpio(struct device *dev,
+static struct gpio_desc *of_find_arizona_gpio(struct device_node *np,
const char *con_id,
+ unsigned int idx,
enum of_gpio_flags *of_flags)
{
if (!IS_ENABLED(CONFIG_MFD_ARIZONA))
@@ -471,17 +466,18 @@ static struct gpio_desc *of_find_arizona_gpio(struct device *dev,
if (!con_id || strcmp(con_id, "wlf,reset"))
return ERR_PTR(-ENOENT);
- return of_get_named_gpiod_flags(dev->of_node, con_id, 0, of_flags);
+ return of_get_named_gpiod_flags(np, con_id, idx, of_flags);
}
-static struct gpio_desc *of_find_usb_gpio(struct device *dev,
+static struct gpio_desc *of_find_usb_gpio(struct device_node *np,
const char *con_id,
+ unsigned int idx,
enum of_gpio_flags *of_flags)
{
/*
- * Currently this USB quirk is only for the Fairchild FUSB302 host which is using
- * an undocumented DT GPIO line named "fcs,int_n" without the compulsory "-gpios"
- * suffix.
+ * Currently this USB quirk is only for the Fairchild FUSB302 host
+ * which is using an undocumented DT GPIO line named "fcs,int_n"
+ * without the compulsory "-gpios" suffix.
*/
if (!IS_ENABLED(CONFIG_TYPEC_FUSB302))
return ERR_PTR(-ENOENT);
@@ -489,14 +485,28 @@ static struct gpio_desc *of_find_usb_gpio(struct device *dev,
if (!con_id || strcmp(con_id, "fcs,int_n"))
return ERR_PTR(-ENOENT);
- return of_get_named_gpiod_flags(dev->of_node, con_id, 0, of_flags);
+ return of_get_named_gpiod_flags(np, con_id, idx, of_flags);
}
+typedef struct gpio_desc *(*of_find_gpio_quirk)(struct device_node *np,
+ const char *con_id,
+ unsigned int idx,
+ enum of_gpio_flags *of_flags);
+static const of_find_gpio_quirk of_find_gpio_quirks[] = {
+ of_find_spi_gpio,
+ of_find_spi_cs_gpio,
+ of_find_regulator_gpio,
+ of_find_arizona_gpio,
+ of_find_usb_gpio,
+ NULL
+};
+
struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
unsigned int idx, unsigned long *flags)
{
char prop_name[32]; /* 32 is max size of property name */
enum of_gpio_flags of_flags;
+ const of_find_gpio_quirk *q;
struct gpio_desc *desc;
unsigned int i;
@@ -516,51 +526,14 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
break;
}
- if (gpiod_not_found(desc)) {
- /* Special handling for SPI GPIOs if used */
- desc = of_find_spi_gpio(dev, con_id, &of_flags);
- }
-
- if (gpiod_not_found(desc)) {
- /* This quirk looks up flags and all */
- desc = of_find_spi_cs_gpio(dev, con_id, idx, flags);
- if (!IS_ERR(desc))
- return desc;
- }
-
- if (gpiod_not_found(desc)) {
- /* Special handling for regulator GPIOs if used */
- desc = of_find_regulator_gpio(dev, con_id, &of_flags);
- }
-
- if (gpiod_not_found(desc))
- desc = of_find_arizona_gpio(dev, con_id, &of_flags);
-
- if (gpiod_not_found(desc))
- desc = of_find_usb_gpio(dev, con_id, &of_flags);
+ /* Properly named GPIO was not found, try workarounds */
+ for (q = of_find_gpio_quirks; gpiod_not_found(desc) && *q; q++)
+ desc = (*q)(dev->of_node, con_id, idx, &of_flags);
if (IS_ERR(desc))
return desc;
- if (of_flags & OF_GPIO_ACTIVE_LOW)
- *flags |= GPIO_ACTIVE_LOW;
-
- if (of_flags & OF_GPIO_SINGLE_ENDED) {
- if (of_flags & OF_GPIO_OPEN_DRAIN)
- *flags |= GPIO_OPEN_DRAIN;
- else
- *flags |= GPIO_OPEN_SOURCE;
- }
-
- 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;
- if (of_flags & OF_GPIO_PULL_DISABLE)
- *flags |= GPIO_PULL_DISABLE;
+ *flags = of_convert_gpio_flags(of_flags);
return desc;
}
@@ -618,16 +591,7 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
if (IS_ERR(desc))
return desc;
- if (xlate_flags & OF_GPIO_ACTIVE_LOW)
- *lflags |= GPIO_ACTIVE_LOW;
- if (xlate_flags & OF_GPIO_TRANSITORY)
- *lflags |= GPIO_TRANSITORY;
- if (xlate_flags & OF_GPIO_PULL_UP)
- *lflags |= GPIO_PULL_UP;
- if (xlate_flags & OF_GPIO_PULL_DOWN)
- *lflags |= GPIO_PULL_DOWN;
- if (xlate_flags & OF_GPIO_PULL_DISABLE)
- *lflags |= GPIO_PULL_DISABLE;
+ *lflags = of_convert_gpio_flags(xlate_flags);
if (of_property_read_bool(np, "input"))
*dflags |= GPIOD_IN;
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index cc9c0a12259e..4756ea08894f 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -3799,6 +3799,72 @@ static int platform_gpio_count(struct device *dev, const char *con_id)
}
/**
+ * fwnode_get_named_gpiod - obtain a GPIO from firmware node
+ * @fwnode: handle of the firmware node
+ * @propname: name of the firmware property representing the GPIO
+ * @index: index of the GPIO to obtain for the consumer
+ * @dflags: GPIO initialization flags
+ * @label: label to attach to the requested GPIO
+ *
+ * This function can be used for drivers that get their configuration
+ * from opaque firmware.
+ *
+ * The function properly finds the corresponding GPIO using whatever is the
+ * underlying firmware interface and then makes sure that the GPIO
+ * descriptor is requested before it is returned to the caller.
+ *
+ * Returns:
+ * On successful request the GPIO pin is configured in accordance with
+ * provided @dflags.
+ *
+ * In case of error an ERR_PTR() is returned.
+ */
+static struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
+ const char *propname, int index,
+ enum gpiod_flags dflags,
+ const char *label)
+{
+ unsigned long lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
+ struct gpio_desc *desc = ERR_PTR(-ENODEV);
+ int ret;
+
+ if (is_of_node(fwnode)) {
+ desc = gpiod_get_from_of_node(to_of_node(fwnode),
+ propname, index,
+ dflags,
+ label);
+ return desc;
+ } else if (is_acpi_node(fwnode)) {
+ struct acpi_gpio_info info;
+
+ desc = acpi_node_get_gpiod(fwnode, propname, index, &info);
+ if (IS_ERR(desc))
+ return desc;
+
+ acpi_gpio_update_gpiod_flags(&dflags, &info);
+ acpi_gpio_update_gpiod_lookup_flags(&lflags, &info);
+ } else {
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* Currently only ACPI takes this path */
+ ret = gpiod_request(desc, label);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = gpiod_configure_flags(desc, propname, lflags, dflags);
+ if (ret < 0) {
+ gpiod_put(desc);
+ return ERR_PTR(ret);
+ }
+
+ blocking_notifier_call_chain(&desc->gdev->notifier,
+ GPIOLINE_CHANGED_REQUESTED, desc);
+
+ return desc;
+}
+
+/**
* fwnode_gpiod_get_index - obtain a GPIO from firmware node
* @fwnode: handle of the firmware node
* @con_id: function within the GPIO consumer
@@ -4064,72 +4130,6 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
EXPORT_SYMBOL_GPL(gpiod_get_index);
/**
- * fwnode_get_named_gpiod - obtain a GPIO from firmware node
- * @fwnode: handle of the firmware node
- * @propname: name of the firmware property representing the GPIO
- * @index: index of the GPIO to obtain for the consumer
- * @dflags: GPIO initialization flags
- * @label: label to attach to the requested GPIO
- *
- * This function can be used for drivers that get their configuration
- * from opaque firmware.
- *
- * The function properly finds the corresponding GPIO using whatever is the
- * underlying firmware interface and then makes sure that the GPIO
- * descriptor is requested before it is returned to the caller.
- *
- * Returns:
- * On successful request the GPIO pin is configured in accordance with
- * provided @dflags.
- *
- * In case of error an ERR_PTR() is returned.
- */
-struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
- const char *propname, int index,
- enum gpiod_flags dflags,
- const char *label)
-{
- unsigned long lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
- struct gpio_desc *desc = ERR_PTR(-ENODEV);
- int ret;
-
- if (is_of_node(fwnode)) {
- desc = gpiod_get_from_of_node(to_of_node(fwnode),
- propname, index,
- dflags,
- label);
- return desc;
- } else if (is_acpi_node(fwnode)) {
- struct acpi_gpio_info info;
-
- desc = acpi_node_get_gpiod(fwnode, propname, index, &info);
- if (IS_ERR(desc))
- return desc;
-
- acpi_gpio_update_gpiod_flags(&dflags, &info);
- acpi_gpio_update_gpiod_lookup_flags(&lflags, &info);
- } else
- return ERR_PTR(-EINVAL);
-
- /* Currently only ACPI takes this path */
- ret = gpiod_request(desc, label);
- if (ret)
- return ERR_PTR(ret);
-
- ret = gpiod_configure_flags(desc, propname, lflags, dflags);
- if (ret < 0) {
- gpiod_put(desc);
- return ERR_PTR(ret);
- }
-
- blocking_notifier_call_chain(&desc->gdev->notifier,
- GPIOLINE_CHANGED_REQUESTED, desc);
-
- return desc;
-}
-EXPORT_SYMBOL_GPL(fwnode_get_named_gpiod);
-
-/**
* gpiod_get_index_optional - obtain an optional GPIO from a multi-index GPIO
* function
* @dev: GPIO consumer, can be NULL for system-global GPIOs
diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index fe0f460d9a3b..36460ced060b 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -174,10 +174,6 @@ int desc_to_gpio(const struct gpio_desc *desc);
/* Child properties interface */
struct fwnode_handle;
-struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
- const char *propname, int index,
- enum gpiod_flags dflags,
- const char *label);
struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode,
const char *con_id, int index,
enum gpiod_flags flags,
@@ -554,15 +550,6 @@ static inline int desc_to_gpio(const struct gpio_desc *desc)
struct fwnode_handle;
static inline
-struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
- const char *propname, int index,
- enum gpiod_flags dflags,
- const char *label)
-{
- return ERR_PTR(-ENOSYS);
-}
-
-static inline
struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode,
const char *con_id, int index,
enum gpiod_flags flags,
diff --git a/include/linux/isa.h b/include/linux/isa.h
index e30963190968..4fbbf5e36e08 100644
--- a/include/linux/isa.h
+++ b/include/linux/isa.h
@@ -38,6 +38,32 @@ static inline void isa_unregister_driver(struct isa_driver *d)
}
#endif
+#define module_isa_driver_init(__isa_driver, __num_isa_dev) \
+static int __init __isa_driver##_init(void) \
+{ \
+ return isa_register_driver(&(__isa_driver), __num_isa_dev); \
+} \
+module_init(__isa_driver##_init)
+
+#define module_isa_driver_with_irq_init(__isa_driver, __num_isa_dev, __num_irq) \
+static int __init __isa_driver##_init(void) \
+{ \
+ if (__num_irq != __num_isa_dev) { \
+ pr_err("%s: Number of irq (%u) does not match number of base (%u)\n", \
+ __isa_driver.driver.name, __num_irq, __num_isa_dev); \
+ return -EINVAL; \
+ } \
+ return isa_register_driver(&(__isa_driver), __num_isa_dev); \
+} \
+module_init(__isa_driver##_init)
+
+#define module_isa_driver_exit(__isa_driver) \
+static void __exit __isa_driver##_exit(void) \
+{ \
+ isa_unregister_driver(&(__isa_driver)); \
+} \
+module_exit(__isa_driver##_exit)
+
/**
* module_isa_driver() - Helper macro for registering a ISA driver
* @__isa_driver: isa_driver struct
@@ -48,16 +74,22 @@ static inline void isa_unregister_driver(struct isa_driver *d)
* use this macro once, and calling it replaces module_init and module_exit.
*/
#define module_isa_driver(__isa_driver, __num_isa_dev) \
-static int __init __isa_driver##_init(void) \
-{ \
- return isa_register_driver(&(__isa_driver), __num_isa_dev); \
-} \
-module_init(__isa_driver##_init); \
-static void __exit __isa_driver##_exit(void) \
-{ \
- isa_unregister_driver(&(__isa_driver)); \
-} \
-module_exit(__isa_driver##_exit);
+module_isa_driver_init(__isa_driver, __num_isa_dev); \
+module_isa_driver_exit(__isa_driver)
+
+/**
+ * module_isa_driver_with_irq() - Helper macro for registering an ISA driver with irq
+ * @__isa_driver: isa_driver struct
+ * @__num_isa_dev: number of devices to register
+ * @__num_irq: number of IRQ to register
+ *
+ * Helper macro for ISA drivers with irq that do not do anything special in
+ * module init/exit. Each module may only use this macro once, and calling it
+ * replaces module_init and module_exit.
+ */
+#define module_isa_driver_with_irq(__isa_driver, __num_isa_dev, __num_irq) \
+module_isa_driver_with_irq_init(__isa_driver, __num_isa_dev, __num_irq); \
+module_isa_driver_exit(__isa_driver)
/**
* max_num_isa_dev() - Maximum possible number registered of an ISA device
diff --git a/include/linux/ucb1400.h b/include/linux/ucb1400.h
index 22345391350b..2516082cd3a9 100644
--- a/include/linux/ucb1400.h
+++ b/include/linux/ucb1400.h
@@ -23,7 +23,7 @@
#include <sound/ac97_codec.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
/*
* UCB1400 AC-link registers