From ba96b2e7974b9b5283b52dd5dc8bb3d15be7734c Mon Sep 17 00:00:00 2001 From: Srinivas Neeli Date: Wed, 10 Aug 2022 18:11:09 +0530 Subject: dt-bindings: gpio: gpio-xilinx: Convert Xilinx axi gpio binding to YAML Convert Xilinx axi gpio binding documentation to YAML. Signed-off-by: Srinivas Neeli Reviewed-by: Rob Herring Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/gpio-xilinx.txt | 48 ------- .../devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml | 154 +++++++++++++++++++++ 2 files changed, 154 insertions(+), 48 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-xilinx.txt create mode 100644 Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml 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 = <µblaze_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/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 + +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 + + 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>; + }; + +... -- cgit v1.2.3-59-g8ed1b From 1e65d136d956e4d33bd0a5d48e3e32d7d951c156 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 27 Jul 2022 18:42:51 +0200 Subject: dt-bindings: gpio: fairchild,74hc595: use spi-peripheral-props.yaml Instead of listing directly properties typical for SPI peripherals, reference the spi-peripheral-props.yaml schema. This allows using all properties typical for SPI-connected devices, even these which device bindings author did not tried yet. Remove the spi-* properties which now come via spi-peripheral-props.yaml schema, except for the cases when device schema adds some constraints like maximum frequency. While changing additionalProperties->unevaluatedProperties, put it in typical place, just before example DTS. Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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: - | -- cgit v1.2.3-59-g8ed1b From 66df18b3bd74107dd7c196e75ce00d64d7553152 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 31 Aug 2022 10:47:50 +0200 Subject: gpio: ucb1400: Use proper header The UCB1400 implements a GPIO driver so it needs to include the header, not the legacy header. Compile tested on pxa_defconfig. Signed-off-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ucb1400.c | 1 + include/linux/ucb1400.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) 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 #include +#include static int ucb1400_gpio_dir_in(struct gpio_chip *gc, unsigned off) { 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 #include #include -#include +#include /* * UCB1400 AC-link registers -- cgit v1.2.3-59-g8ed1b From b98dbd82ee319d74103510f953a1ca2cd9202614 Mon Sep 17 00:00:00 2001 From: Jeffy Chen Date: Wed, 24 Aug 2022 09:06:05 +0800 Subject: gpio/rockchip: Convert to generic_handle_domain_irq() Follow commit dbd1c54fc820 ("gpio: Bulk conversion to generic_handle_domain_irq()"). Signed-off-by: Jeffy Chen Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rockchip.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index f91e876fd969..ebb50c25a461 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); -- cgit v1.2.3-59-g8ed1b From f799206943434ac0484d997d17b9be5bcbf594ab Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Thu, 25 Aug 2022 15:35:23 +0100 Subject: dt-bindings: gpio: mpfs-gpio: allow parsing of hog child nodes. The SD card and eMMC on PolarFire SoC are sometimes muxed using a GPIO by the bootloader. Add a hog child property to facilitate this. Signed-off-by: Conor Dooley Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/microchip,mpfs-gpio.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) 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 -- cgit v1.2.3-59-g8ed1b From 2d6f58a1e9a001902a072d8f3d99c96b9c7a1454 Mon Sep 17 00:00:00 2001 From: Shenwei Wang Date: Fri, 12 Aug 2022 08:30:10 -0500 Subject: dt-bindings: gpio: Add imx scu gpio driver bindings Add binding document for the imx scu gpio driver. Signed-off-by: Shenwei Wang Reviewed-by: Krzysztof Kozlowski Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/fsl,imx8qxp-sc-gpio.yaml | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/fsl,imx8qxp-sc-gpio.yaml 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 + +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>; + }; -- cgit v1.2.3-59-g8ed1b From 3bdd1afa3e223f99bfb5fa5051da843dff7eca12 Mon Sep 17 00:00:00 2001 From: Shenwei Wang Date: Fri, 12 Aug 2022 08:30:11 -0500 Subject: dt-bindings: firmware: imx: Add imx-scu gpio node Add the description for imx-scu gpio subnode. Signed-off-by: Shenwei Wang Acked-by: Krzysztof Kozlowski Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/firmware/fsl,scu.yaml | 5 +++++ 1 file changed, 5 insertions(+) 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 -- cgit v1.2.3-59-g8ed1b From 0bd459ddf9e4f1af3ee3148eb319d1f8747f02ba Mon Sep 17 00:00:00 2001 From: Shenwei Wang Date: Fri, 12 Aug 2022 08:30:12 -0500 Subject: gpio: imx-scu: add imx-scu GPIO driver The SCU firmware on i.MX8 platforms provides a set of APIs to control the GPIO PINs on the SCU domain. This patch implements the standard GPIO driver interface to access those PINs on the SCU domain over the SCU APIs. Signed-off-by: Shenwei Wang Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 4 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-imx-scu.c | 139 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 drivers/gpio/gpio-imx-scu.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 0642f579196f..75364c45cb9f 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-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 +#include +#include +#include +#include + +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 "); +MODULE_DESCRIPTION("NXP GPIO over IMX SCU API"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 0e056f414e5b1b797886a90e77f6576f121c2a09 Mon Sep 17 00:00:00 2001 From: Francesco Dolcini Date: Tue, 12 Jul 2022 18:33:43 +0200 Subject: dt-bindings: gpio: stmpe: Remove node name requirement STMPE driver does not require a specific node name anymore, only the compatible is checked, update binding according to this. Signed-off-by: Francesco Dolcini Acked-by: Krzysztof Kozlowski Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/gpio-stmpe.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 { -- cgit v1.2.3-59-g8ed1b From 0eadd36d9123745f70e233a9d93951d05ca1916a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 3 Sep 2022 23:18:47 -0700 Subject: gpiolib: make fwnode_get_named_gpiod() static There are no external users of fwnode_get_named_gpiod() anymore, so let's stop exporting it and mark it as static. Signed-off-by: Dmitry Torokhov Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 132 +++++++++++++++++++++--------------------- include/linux/gpio/consumer.h | 13 ----- 2 files changed, 66 insertions(+), 79 deletions(-) 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 @@ -3798,6 +3798,72 @@ static int platform_gpio_count(struct device *dev, const char *con_id) return count; } +/** + * 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 @@ -4063,72 +4129,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 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, @@ -553,15 +549,6 @@ static inline int desc_to_gpio(const struct gpio_desc *desc) /* Child properties interface */ 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, -- cgit v1.2.3-59-g8ed1b From 5134272f9f3f71d4e1f3aa15cb09321af49b3646 Mon Sep 17 00:00:00 2001 From: Qingtao Cao Date: Fri, 2 Sep 2022 16:14:34 +1000 Subject: gpio: exar: access MPIO registers on cascaded chips When EXAR xr17v35x chips are cascaded in order to access the MPIO registers (part of the Device Configuration Registers) of the secondary chips, an offset needs to be applied based on the number of primary chip's UART channels. Signed-off-by: Qingtao Cao Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-exar.c | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) 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) @@ -153,6 +172,17 @@ static int gpio_exar_probe(struct platform_device *pdev) if (!exar_gpio) 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). -- cgit v1.2.3-59-g8ed1b From 6b6af7bd5718f4e45a9b930533aec1158387d552 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 2 Aug 2022 23:24:59 -0500 Subject: gpiolib: acpi: Add support to ignore programming an interrupt gpiolib-acpi already had support for ignoring a pin for wakeup, but if an OEM configures a floating pin as an interrupt source then stopping it from being a wakeup won't do much good to stop the interrupt storm. Add support for a module parameter and quirk infrastructure to ignore interrupts as well. Signed-off-by: Mario Limonciello Reviewed-by: Hans de Goede Reviewed-by: Mika Westerberg Signed-off-by: Andy Shevchenko --- drivers/gpio/gpiolib-acpi.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 9be1376f9a62..116faf8bda64 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; @@ -1585,6 +1598,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; } -- cgit v1.2.3-59-g8ed1b From 0ea76c401f9245ac209f1b1ce03a7e1fb9de36e5 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 2 Aug 2022 23:25:00 -0500 Subject: gpiolib: acpi: Add a quirk for Asus UM325UAZ Asus UM325UAZ has GPIO 18 programmed as both an interrupt and a wake source, but confirmed with internal team on this design this pin is floating and shouldn't have been programmed. This causes lots of spurious IRQs on the system and horrendous battery life. Add a quirk to ignore attempts to program this pin on this system. Reported-by: Pavel Krc Link: https://bugzilla.kernel.org/show_bug.cgi?id=216208 Reviewed-by: Hans de Goede Signed-off-by: Mario Limonciello Reviewed-by: Mika Westerberg Signed-off-by: Andy Shevchenko --- drivers/gpio/gpiolib-acpi.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 116faf8bda64..285ecbf107c9 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -1576,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 */ }; -- cgit v1.2.3-59-g8ed1b From 3af20d2723be5f70e1ce818504a4c093a81b21f5 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 9 Sep 2022 11:09:53 +0200 Subject: dt-bindings: gpio: renesas,rcar-gpio: Add r8a779g0 support Document support for the GPIO controller blocks in the Renesas R-Car V4H (R8A779G0) SoC. Signed-off-by: Geert Uytterhoeven Acked-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml | 1 + 1 file changed, 1 insertion(+) 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: -- cgit v1.2.3-59-g8ed1b From 7bbf66e1b7b76d2487ceed51522669ab64057c06 Mon Sep 17 00:00:00 2001 From: Nate Drude Date: Tue, 13 Sep 2022 13:24:35 -0500 Subject: dt-bindings: gpio: pca95xx: add entry for pcal6408 The NXP PCAL6408 is the 8-bit version of PCAL6416. Signed-off-by: Nate Drude Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml index 977b14db09b0..05a9fa92283f 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml @@ -47,6 +47,7 @@ properties: - nxp,pca9574 - nxp,pca9575 - nxp,pca9698 + - nxp,pcal6408 - nxp,pcal6416 - nxp,pcal6524 - nxp,pcal9535 -- cgit v1.2.3-59-g8ed1b From 6d50b79051edc298aba7f60184d9b2fb673f0628 Mon Sep 17 00:00:00 2001 From: Nate Drude Date: Tue, 13 Sep 2022 13:24:36 -0500 Subject: gpio: pca953x: introduce support for nxp,pcal6408 The NXP PCAL6408 is the 8-bit version of PCAL6416. Signed-off-by: Nate Drude Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca953x.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index ecd7d169470b..00c1e2f105ad 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -89,6 +89,7 @@ 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, }, { "pcal9535", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, @@ -1237,6 +1238,7 @@ 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,pcal9535", .data = OF_953X(16, PCA_LATCH_INT), }, -- cgit v1.2.3-59-g8ed1b From f7d619e9ab851eb89ab50c9265504ed732d5bee2 Mon Sep 17 00:00:00 2001 From: Sergio Paracuellos Date: Tue, 13 Sep 2022 18:46:39 +0200 Subject: gpio: mt7621: Switch to use platform_get_irq() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mt7621 SoC GPIO driver is a platform driver so we can directly use 'platform_get_irq' instead of 'irq_of_parse_and_map'. Tested-by: Arınç ÜNAL Signed-off-by: Sergio Paracuellos Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mt7621.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index d8a26e503ca5..340d17a5b6ec 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include @@ -290,7 +289,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; @@ -303,7 +301,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); -- cgit v1.2.3-59-g8ed1b From ce0cb8fb967ec62ff000826fd469511ee2d3fcb2 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Wed, 14 Sep 2022 16:15:53 +0100 Subject: dt-bindings: vendor-prefixes: add Diodes Diodes Incorporated is a manufacturer of application specific standard products within the discrete, logic, analog, and mixed-signal semiconductor markets. https://www.diodes.com/ Signed-off-by: Martyn Welch Acked-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 2f0151e9f6be..7ee9b7692ed1 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -328,6 +328,8 @@ patternProperties: description: Digi International Inc. "^digilent,.*": description: Diglent, Inc. + "^diodes,.*": + description: Diodes, Inc. "^dioo,.*": description: Dioo Microcircuit Co., Ltd "^dlc,.*": -- cgit v1.2.3-59-g8ed1b From 9f7fed73072e34b123f8c856ae21a2154c0e9781 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Wed, 14 Sep 2022 16:15:54 +0100 Subject: dt-bindings: gpio: pca95xx: add entry for pcal6534 and PI4IOE5V6534Q The NXP PCAL6534 is a 34-bit I2C I/O expander similar to the PCAL6524. The Diodes PI4IOE5V6534Q is a functionally identical chip provided by Diodes Inc. Signed-off-by: Martyn Welch Reviewed-by: Krzysztof Kozlowski Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/gpio-pca95xx.yaml | 100 +++++++++++---------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml index 05a9fa92283f..1b70e9f308f3 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml @@ -15,53 +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,pcal6408 - - 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 -- cgit v1.2.3-59-g8ed1b From b122624ab91705d30354f0397b1eea931c6b1933 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Wed, 14 Sep 2022 16:15:55 +0100 Subject: gpio: pca953x: Fix pca953x_gpio_set_pull_up_down() A previous fix, commit dc87f6dd058a ("gpio: pca953x: Fix pca953x_gpio_set_config"), identified that pinconf_to_config_param() needed to be used to isolate the config_param from the pinconf in pca953x_gpio_set_config(). This fix however did not consider that this would also be needed in pca953x_gpio_set_pull_up_down() to which it passes this config. Perform a similar call in pca953x_gpio_set_pull_up_down() to isolate the configuration parameter there as well, rather than passing it from pca953x_gpio_set_config() as the configuration argument may also be needed in pca953x_gpio_set_pull_up_down() at a later date. Signed-off-by: Martyn Welch Acked-by: Linus Walleij Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca953x.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 00c1e2f105ad..a6081d93d4fa 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -549,6 +549,8 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip, unsigned int offset, unsigned long config) { + enum pin_config_param param = pinconf_to_config_param(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); u8 bit = BIT(offset % BANK_SZ); @@ -564,9 +566,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; @@ -574,7 +576,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); -- cgit v1.2.3-59-g8ed1b From 5faf9801d4be7a079062b2ece493ae6e9e38c828 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Wed, 14 Sep 2022 16:15:56 +0100 Subject: gpio: pca953x: Swap if statements to save later complexity A later patch in the series adds support for a further chip type that shares some similarity with the PCA953X_TYPE. In order to keep the logic simple, swap over the if and else portions where checks are made against PCA953X_TYPE and instead check for PCA957X_TYPE. Signed-off-by: Martyn Welch Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca953x.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index a6081d93d4fa..c27b83bbbfe1 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -294,13 +294,13 @@ 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) { @@ -317,12 +317,12 @@ 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) @@ -337,10 +337,10 @@ 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; @@ -1071,13 +1071,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; -- cgit v1.2.3-59-g8ed1b From 13c5d4ce806026a3d06efa2fcc1e5a0cb875df29 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Wed, 14 Sep 2022 16:15:57 +0100 Subject: gpio: pca953x: Add support for PCAL6534 Add support for the NXP PCAL6534. This device is broadly a 34-bit version of the PCAL6524. However, whilst the registers are broadly what you'd expect for a 34-bit version of the PCAL6524, the spacing of the registers has been compacted. This has the unfortunate effect of breaking the bit shift based mechanism that is employed to work out register locations used by the other chips supported by this driver. To accommodate ths, callback functions have been added to allow alterate implementations of pca953x_recalc_addr() and pca953x_check_register() for the PCAL6534. Datasheet: https://www.nxp.com/docs/en/data-sheet/PCAL6534.pdf Datasheet: https://www.diodes.com/assets/Datasheets/PI4IOE5V6534Q.pdf Signed-off-by: Martyn Welch Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca953x.c | 136 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 117 insertions(+), 19 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index c27b83bbbfe1..61e874c0cde4 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) @@ -92,6 +93,7 @@ static const struct i2c_device_id pca953x_id[] = { { "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, }, @@ -212,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) @@ -289,6 +295,55 @@ 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); @@ -309,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) @@ -329,7 +384,7 @@ static bool pca953x_writeable_register(struct device *dev, unsigned int reg) 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) @@ -345,7 +400,7 @@ static bool pca953x_volatile_register(struct device *dev, unsigned int reg) 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 = { @@ -390,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; @@ -410,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; @@ -429,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; @@ -443,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; @@ -464,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; @@ -481,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); @@ -492,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; @@ -551,8 +639,8 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip, { enum pin_config_param param = pinconf_to_config_param(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); + 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; @@ -915,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) @@ -1040,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); @@ -1134,14 +1230,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); @@ -1150,7 +1246,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) { @@ -1159,7 +1255,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) { @@ -1217,6 +1313,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) @@ -1242,6 +1339,7 @@ static const struct of_device_id pca953x_dt_ids[] = { { .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), }, -- cgit v1.2.3-59-g8ed1b From 85ebe0afd3f8377a9b506321314f4b8344caa8ec Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 18 Aug 2022 12:28:10 -0400 Subject: isa: Introduce the module_isa_driver_with_irq helper macro Several ISA drivers feature IRQ support that can configured via an "irq" array module parameter. This array typically matches directly with the respective "base" array module parameter. To reduce code repetition, a module_isa_driver_with_irq helper macro is introduced to provide a check ensuring that the number of "irq" passed to the module matches with the respective number of "base". Signed-off-by: William Breathitt Gray Acked-by: William Breathitt Gray Signed-off-by: Bartosz Golaszewski --- include/linux/isa.h | 52 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 10 deletions(-) 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 -- cgit v1.2.3-59-g8ed1b From 0c83a280dc11238f69d9fe2e15186a4aeb1adfe3 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 18 Aug 2022 12:28:11 -0400 Subject: counter: 104-quad-8: Ensure number of irq matches number of base The 104-quad-8 module calls devm_request_irq() for each device. If the number of irq passed to the module does not match the number of base, a default value of 0 is passed to devm_request_irq(). IRQ 0 is probably not what the user wants, so utilize the module_isa_driver_with_irq macro to ensure the number of irq matches the number of base. Signed-off-by: William Breathitt Gray Acked-by: William Breathitt Gray Signed-off-by: Bartosz Golaszewski --- drivers/counter/104-quad-8.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index 62c2b7ac4339..3f8d1910a9bb 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 @@ -1236,7 +1237,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 "); MODULE_DESCRIPTION("ACCES 104-QUAD-8 driver"); -- cgit v1.2.3-59-g8ed1b From 443ad0f730172f05fd78c9e9e68c3dfa5555b061 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 18 Aug 2022 12:28:12 -0400 Subject: gpio: 104-dio-48e: Ensure number of irq matches number of base The 104-dio-48e module calls devm_request_irq() for each device. If the number of irq passed to the module does not match the number of base, a default value of 0 is passed to devm_request_irq(). IRQ 0 is probably not what the user wants, so utilize the module_isa_driver_with_irq macro to ensure the number of irq matches the number of base. Signed-off-by: William Breathitt Gray Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-104-dio-48e.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c index a41551870759..8b5172802188 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 @@ -358,7 +359,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 "); MODULE_DESCRIPTION("ACCES 104-DIO-48E GPIO driver"); -- cgit v1.2.3-59-g8ed1b From 99c3ac85cb7e7b9251f2fcff725fa2c4f4e33a67 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 18 Aug 2022 12:28:13 -0400 Subject: gpio: 104-idi-48: Ensure number of irq matches number of base The 104-idi-48 module calls devm_request_irq() for each device. If the number of irq passed to the module does not match the number of base, a default value of 0 is passed to devm_request_irq(). IRQ 0 is probably not what the user wants, so utilize the module_isa_driver_with_irq macro to ensure the number of irq matches the number of base. Signed-off-by: William Breathitt Gray Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-104-idi-48.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c index 40be76efeed7..2b0256eefb70 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"); /** @@ -300,7 +301,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 "); MODULE_DESCRIPTION("ACCES 104-IDI-48 GPIO driver"); -- cgit v1.2.3-59-g8ed1b From c6074f3fcf8b67acf8e3bdf678ace1f9c18dfb9e Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 18 Aug 2022 12:28:14 -0400 Subject: gpio: 104-idio-16: Ensure number of irq matches number of base The 104-idio-16 module calls devm_request_irq() for each device. If the number of irq passed to the module does not match the number of base, a default value of 0 is passed to devm_request_irq(). IRQ 0 is probably not what the user wants, so utilize the module_isa_driver_with_irq macro to ensure the number of irq matches the number of base. Signed-off-by: William Breathitt Gray Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-104-idio-16.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c index 65a5f581d981..73d95b55a8c5 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"); /** @@ -333,7 +334,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 "); MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver"); -- cgit v1.2.3-59-g8ed1b From c95671a3e77ca6f1ed42e891d1fef2ef166a7fdc Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 18 Aug 2022 12:28:15 -0400 Subject: gpio: ws16c48: Ensure number of irq matches number of base The ws16c48 module calls devm_request_irq() for each device. If the number of irq passed to the module does not match the number of base, a default value of 0 is passed to devm_request_irq(). IRQ 0 is probably not what the user wants, so utilize the module_isa_driver_with_irq macro to ensure the number of irq matches the number of base. Signed-off-by: William Breathitt Gray Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ws16c48.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c index b098f2dc196b..88410da91aaf 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"); /** @@ -497,7 +498,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 "); MODULE_DESCRIPTION("WinSystems WS16C48 GPIO driver"); -- cgit v1.2.3-59-g8ed1b From 98c3c940ea5c3957056717e8b77a91c7d94536ad Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 7 Sep 2022 22:39:46 -0700 Subject: gpiolib: of: do not ignore requested index when applying quirks We should not ignore index passed into of_find_gpio() when handling quirks. While in practice this change will not have any effect, it will allow consolidate quirk handling. Signed-off-by: Dmitry Torokhov Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-of.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index a037b50bef33..c08564948bf9 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -372,7 +372,9 @@ 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 *dev, + const char *con_id, + unsigned int idx, enum of_gpio_flags *of_flags) { char prop_name[32]; /* 32 is max size of property name */ @@ -393,7 +395,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); + desc = of_get_named_gpiod_flags(np, prop_name, idx, of_flags); return desc; } @@ -434,7 +436,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 *dev, + const char *con_id, + unsigned int idx, enum of_gpio_flags *of_flags) { /* These are the connection IDs we accept as legacy GPIO phandles */ @@ -457,12 +461,13 @@ 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); + desc = of_get_named_gpiod_flags(np, con_id, idx, of_flags); return desc; } static struct gpio_desc *of_find_arizona_gpio(struct device *dev, const char *con_id, + unsigned int idx, enum of_gpio_flags *of_flags) { if (!IS_ENABLED(CONFIG_MFD_ARIZONA)) @@ -471,17 +476,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(dev->of_node, con_id, idx, of_flags); } static struct gpio_desc *of_find_usb_gpio(struct device *dev, 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,7 +495,7 @@ 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(dev->of_node, con_id, idx, of_flags); } struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, @@ -518,7 +524,7 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, if (gpiod_not_found(desc)) { /* Special handling for SPI GPIOs if used */ - desc = of_find_spi_gpio(dev, con_id, &of_flags); + desc = of_find_spi_gpio(dev, con_id, idx, &of_flags); } if (gpiod_not_found(desc)) { @@ -530,14 +536,14 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, if (gpiod_not_found(desc)) { /* Special handling for regulator GPIOs if used */ - desc = of_find_regulator_gpio(dev, con_id, &of_flags); + desc = of_find_regulator_gpio(dev, con_id, idx, &of_flags); } if (gpiod_not_found(desc)) - desc = of_find_arizona_gpio(dev, con_id, &of_flags); + desc = of_find_arizona_gpio(dev, con_id, idx, &of_flags); if (gpiod_not_found(desc)) - desc = of_find_usb_gpio(dev, con_id, &of_flags); + desc = of_find_usb_gpio(dev, con_id, idx, &of_flags); if (IS_ERR(desc)) return desc; -- cgit v1.2.3-59-g8ed1b From 984914ec4f4bfa9ee8f067b06293bc12bef20137 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 7 Sep 2022 22:39:47 -0700 Subject: gpiolib: of: make Freescale SPI quirk similar to all others There is no need for of_find_spi_cs_gpio() to be different from other quirks: the only variant of property actually used in DTS is "gpios" (plural) so we can use of_get_named_gpiod_flags() instead of recursing into of_find_gpio() again. This will allow us consolidate quirk handling down the road. Signed-off-by: Dmitry Torokhov Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-of.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index c08564948bf9..30b89b694530 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -407,7 +407,7 @@ static struct gpio_desc *of_find_spi_gpio(struct device *dev, static struct gpio_desc *of_find_spi_cs_gpio(struct device *dev, const char *con_id, unsigned int idx, - unsigned long *flags) + enum of_gpio_flags *of_flags) { const struct device_node *np = dev->of_node; @@ -428,7 +428,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(dev->of_node, "gpios", idx, of_flags); } /* @@ -527,12 +527,8 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, desc = of_find_spi_gpio(dev, con_id, idx, &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)) + desc = of_find_spi_cs_gpio(dev, con_id, idx, &of_flags); if (gpiod_not_found(desc)) { /* Special handling for regulator GPIOs if used */ -- cgit v1.2.3-59-g8ed1b From a2b5e207cade33b4d2dfd920f783f13b1f173e78 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 7 Sep 2022 22:39:48 -0700 Subject: gpiolib: rework quirk handling in of_find_gpio() Instead of having a string of "if" statements let's put all quirks into an array and iterate over them. Signed-off-by: Dmitry Torokhov Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-of.c | 62 ++++++++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 30b89b694530..097e948c1d49 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -372,14 +372,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, +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 @@ -395,8 +393,7 @@ static struct gpio_desc *of_find_spi_gpio(struct device *dev, /* 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, idx, of_flags); - return desc; + return of_get_named_gpiod_flags(np, prop_name, idx, of_flags); } /* @@ -404,13 +401,11 @@ static struct gpio_desc *of_find_spi_gpio(struct device *dev, * 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, enum of_gpio_flags *of_flags) { - const struct device_node *np = dev->of_node; - if (!IS_ENABLED(CONFIG_SPI_MASTER)) return ERR_PTR(-ENOENT); @@ -428,7 +423,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_get_named_gpiod_flags(dev->of_node, "gpios", idx, of_flags); + return of_get_named_gpiod_flags(np, "gpios", idx, of_flags); } /* @@ -436,7 +431,7 @@ 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, +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) @@ -447,8 +442,6 @@ static struct gpio_desc *of_find_regulator_gpio(struct device *dev, "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)) @@ -461,11 +454,10 @@ static struct gpio_desc *of_find_regulator_gpio(struct device *dev, if (i < 0) return ERR_PTR(-ENOENT); - desc = of_get_named_gpiod_flags(np, con_id, idx, 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) @@ -476,10 +468,10 @@ 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, idx, 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) @@ -495,14 +487,27 @@ 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, idx, 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, +}; + 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; @@ -522,24 +527,9 @@ 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, idx, &of_flags); - } - - if (gpiod_not_found(desc)) - desc = of_find_spi_cs_gpio(dev, con_id, idx, &of_flags); - - if (gpiod_not_found(desc)) { - /* Special handling for regulator GPIOs if used */ - desc = of_find_regulator_gpio(dev, con_id, idx, &of_flags); - } - - if (gpiod_not_found(desc)) - desc = of_find_arizona_gpio(dev, con_id, idx, &of_flags); - - if (gpiod_not_found(desc)) - desc = of_find_usb_gpio(dev, con_id, idx, &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; -- cgit v1.2.3-59-g8ed1b From d9e7f0e320516c660d6f33e6c16a3d99970eb14e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 7 Sep 2022 22:39:49 -0700 Subject: gpiolib: of: factor out conversion from OF flags There are several places where we need to convert from OF flags to "normal" GPIO_* flags, so let's introduce a helper and use it. Signed-off-by: Dmitry Torokhov Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-of.c | 103 +++++++++++++++++----------------------------- 1 file changed, 37 insertions(+), 66 deletions(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 097e948c1d49..95be5f0d2623 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) { @@ -534,25 +532,7 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, 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; } @@ -610,16 +590,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; -- cgit v1.2.3-59-g8ed1b From 8b10ca2f7551e024b60ab5e27d3e3630c029000a Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Fri, 16 Sep 2022 13:01:18 +0200 Subject: gpiolib: fix OOB access in quirk callbacks Commit a2b5e207cade ("gpiolib: rework quirk handling in of_find_gpio()") introduced an array of quirk functions which get iterated over. But a sentinal value is missing. Add it. Fixes: a2b5e207cade ("gpiolib: rework quirk handling in of_find_gpio()") Signed-off-by: Michael Walle Reviewed-by: Linus Walleij Tested-by: Conor Dooley Reviewed-by: Dmitry Torokhov Tested-by: Marek Szyprowski Tested-by: Biju Das Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-of.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 95be5f0d2623..0e4e1291604d 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -498,6 +498,7 @@ static const of_find_gpio_quirk of_find_gpio_quirks[] = { 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, -- cgit v1.2.3-59-g8ed1b From 971479115444e9f575226e76d5443e6e90954e78 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sat, 17 Sep 2022 23:37:29 +0200 Subject: gpio: twl4030: Reorder functions which allows to drop a forward declaraion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no cyclic dependency between gpio_twl4030_probe() and gpio_twl4030_remove(), so by moving the latter before the former the forward declaration can be dropped. Signed-off-by: Uwe Kleine-König Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-twl4030.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) 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", }, { }, -- cgit v1.2.3-59-g8ed1b From 0ae3109a839192920f09338e9abc4d5321107865 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 22 Sep 2022 11:51:24 +0200 Subject: gpiolib: cdev: add fdinfo output for line request file descriptors Add fdinfo output for file descriptors created for user-space line requests in GPIO uAPI v2. The fdinfo file now contains the name of the GPIO chip that is the "parent" of the request as well as offsets of the lines requested. This allows user-space to parse the /proc/$PID/fdinfo entries and deduce the PID of the process that requested a specific line. Signed-off-by: Bartosz Golaszewski Reviewed-by: Kent Gibson --- drivers/gpio/gpiolib-cdev.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index f8041d4898d1..01c15e9e6896 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) -- cgit v1.2.3-59-g8ed1b From 3c92506d86785967fd7e7933e04491b9276c2f00 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 3 Oct 2022 09:45:20 +0200 Subject: gpio: tc3589x: Make irqchip immutable This turns the Toshiba tc3589x gpio irqchip immutable. Cc: Marc Zyngier Signed-off-by: Linus Walleij Acked-by: Marc Zyngier Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tc3589x.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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; -- cgit v1.2.3-59-g8ed1b