From d3bb436deb78478bde129a322ca2f6e75716f4de Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 May 2022 20:21:29 +0300 Subject: gpio: pch: Use dev_err_probe() Simplify the error path in ->probe() a bit by using dev_err_probe(). While at it, correct the messages since the called function were changed in the past. Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-pch.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 3a0bd8795741..dfd651e04831 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -357,16 +357,12 @@ static int pch_gpio_probe(struct pci_dev *pdev, chip->dev = dev; ret = pcim_enable_device(pdev); - if (ret) { - dev_err(dev, "pci_enable_device FAILED"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to enable PCI device\n"); ret = pcim_iomap_regions(pdev, BIT(1), KBUILD_MODNAME); - if (ret) { - dev_err(dev, "pci_request_regions FAILED-%d", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to request and map PCI regions\n"); chip->base = pcim_iomap_table(pdev)[1]; chip->ioh = id->driver_data; @@ -376,10 +372,8 @@ static int pch_gpio_probe(struct pci_dev *pdev, pch_gpio_setup(chip); ret = devm_gpiochip_add_data(dev, &chip->gpio, chip); - if (ret) { - dev_err(dev, "PCH gpio: Failed to register GPIO\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to register GPIO\n"); irq_base = devm_irq_alloc_descs(dev, -1, 0, gpio_pins[chip->ioh], NUMA_NO_NODE); @@ -396,10 +390,8 @@ static int pch_gpio_probe(struct pci_dev *pdev, ret = devm_request_irq(dev, pdev->irq, pch_gpio_handler, IRQF_SHARED, KBUILD_MODNAME, chip); - if (ret) { - dev_err(dev, "request_irq failed\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to request IRQ\n"); return pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]); } -- cgit v1.2.3 From b65bb2c148913c4020d06c9798e5e1bc18105f4f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 1 Jun 2022 20:10:13 +0300 Subject: gpio: pch: Change PCI device macros Use PCI_DEVICE_DATA macro. No functional changes are expected. Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-pch.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index dfd651e04831..ee37ecb615cb 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -37,6 +37,11 @@ struct pch_regs { u32 reset; }; +#define PCI_DEVICE_ID_INTEL_EG20T_PCH 0x8803 +#define PCI_DEVICE_ID_ROHM_ML7223m_IOH 0x8014 +#define PCI_DEVICE_ID_ROHM_ML7223n_IOH 0x8043 +#define PCI_DEVICE_ID_ROHM_EG20T_PCH 0x8803 + enum pch_type_t { INTEL_EG20T_PCH, OKISEMI_ML7223m_IOH, /* LAPIS Semiconductor ML7223 IOH PCIe Bus-m */ @@ -425,15 +430,11 @@ static int __maybe_unused pch_gpio_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pch_gpio_pm_ops, pch_gpio_suspend, pch_gpio_resume); static const struct pci_device_id pch_gpio_pcidev_id[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8803), - .driver_data = INTEL_EG20T_PCH }, - { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8014), - .driver_data = OKISEMI_ML7223m_IOH }, - { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8043), - .driver_data = OKISEMI_ML7223n_IOH }, - { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8803), - .driver_data = INTEL_EG20T_PCH }, - { 0, } + { PCI_DEVICE_DATA(INTEL, EG20T_PCH, INTEL_EG20T_PCH) }, + { PCI_DEVICE_DATA(ROHM, ML7223m_IOH, OKISEMI_ML7223m_IOH) }, + { PCI_DEVICE_DATA(ROHM, ML7223n_IOH, OKISEMI_ML7223n_IOH) }, + { PCI_DEVICE_DATA(ROHM, EG20T_PCH, INTEL_EG20T_PCH) }, + { } }; MODULE_DEVICE_TABLE(pci, pch_gpio_pcidev_id); -- cgit v1.2.3 From 2b038e786f8338a3bc22d791000753e0ec113e00 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 22 Jun 2022 20:28:42 +0300 Subject: gpiolib: devres: Get rid of unused devm_gpio_free() The last user, which in fact was a dead code, has gone a year ago, previous one 3 years ago. On top of that we want to drop away the legacy GPIO APIs in the kernel, so take a chance to get rid of unused devm_gpio_free() and accompanying stuff. Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- Documentation/driver-api/driver-model/devres.rst | 1 - drivers/gpio/gpiolib-devres.c | 32 ------------------------ include/linux/gpio.h | 6 ----- 3 files changed, 39 deletions(-) diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index 2d39967bafcc..55272942e721 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -277,7 +277,6 @@ GPIO devm_gpiochip_add_data() devm_gpio_request() devm_gpio_request_one() - devm_gpio_free() I2C devm_i2c_new_dummy_device() diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index 79da85d17b71..16a696249229 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -375,9 +375,6 @@ void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs) } EXPORT_SYMBOL_GPL(devm_gpiod_put_array); - - - static void devm_gpio_release(struct device *dev, void *res) { unsigned *gpio = res; @@ -385,13 +382,6 @@ static void devm_gpio_release(struct device *dev, void *res) gpio_free(*gpio); } -static int devm_gpio_match(struct device *dev, void *res, void *data) -{ - unsigned *this = res, *gpio = data; - - return *this == *gpio; -} - /** * devm_gpio_request - request a GPIO for a managed device * @dev: device to request the GPIO for @@ -402,11 +392,7 @@ static int devm_gpio_match(struct device *dev, void *res, void *data) * same arguments and performs the same function as * gpio_request(). GPIOs requested with this function will be * automatically freed on driver detach. - * - * If an GPIO allocated with this function needs to be freed - * separately, devm_gpio_free() must be used. */ - int devm_gpio_request(struct device *dev, unsigned gpio, const char *label) { unsigned *dr; @@ -459,24 +445,6 @@ int devm_gpio_request_one(struct device *dev, unsigned gpio, } EXPORT_SYMBOL_GPL(devm_gpio_request_one); -/** - * devm_gpio_free - free a GPIO - * @dev: device to free GPIO for - * @gpio: GPIO to free - * - * Except for the extra @dev argument, this function takes the - * same arguments and performs the same function as gpio_free(). - * This function instead of gpio_free() should be used to manually - * free GPIOs allocated with devm_gpio_request(). - */ -void devm_gpio_free(struct device *dev, unsigned int gpio) -{ - - WARN_ON(devres_release(dev, devm_gpio_release, devm_gpio_match, - &gpio)); -} -EXPORT_SYMBOL_GPL(devm_gpio_free); - static void devm_gpio_chip_release(void *data) { struct gpio_chip *gc = data; diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 008ad3ee56b7..a370387fa406 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -95,7 +95,6 @@ struct device; int devm_gpio_request(struct device *dev, unsigned gpio, const char *label); int devm_gpio_request_one(struct device *dev, unsigned gpio, unsigned long flags, const char *label); -void devm_gpio_free(struct device *dev, unsigned int gpio); #else /* ! CONFIG_GPIOLIB */ @@ -240,11 +239,6 @@ static inline int devm_gpio_request_one(struct device *dev, unsigned gpio, return -EINVAL; } -static inline void devm_gpio_free(struct device *dev, unsigned int gpio) -{ - WARN_ON(1); -} - #endif /* ! CONFIG_GPIOLIB */ #endif /* __LINUX_GPIO_H */ -- cgit v1.2.3 From 1282157a6cf48993d90347fdc5bcb93e00b58f2b Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Thu, 26 May 2022 13:29:44 +1200 Subject: dt-bindings: gpio: gpio-mvebu: convert txt binding to DT schema format Convert the existing device tree binding to DT schema format. The old binding listed the interrupt-controller and related properties as required but there are sufficiently many existing usages without it that the YAML binding does not make the interrupt properties required. Signed-off-by: Chris Packham Reviewed-by: Andrew Lunn Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski --- .../arm/marvell/ap80x-system-controller.txt | 2 +- .../arm/marvell/cp110-system-controller.txt | 2 +- .../devicetree/bindings/gpio/gpio-mvebu.txt | 93 ------------- .../devicetree/bindings/gpio/gpio-mvebu.yaml | 146 +++++++++++++++++++++ MAINTAINERS | 2 +- 5 files changed, 149 insertions(+), 96 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-mvebu.txt create mode 100644 Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml diff --git a/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt b/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt index 052a967c1f28..c83245065d44 100644 --- a/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt +++ b/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt @@ -72,7 +72,7 @@ mpp19 19 gpio, uart0(rxd), sdio(pw_off) GPIO: ----- For common binding part and usage, refer to -Documentation/devicetree/bindings/gpio/gpio-mvebu.txt. +Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml. Required properties: diff --git a/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt b/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt index 0705e765f432..d84105c7c935 100644 --- a/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt +++ b/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt @@ -156,7 +156,7 @@ GPIO: ----- For common binding part and usage, refer to -Documentation/devicetree/bindings/gpio/gpio-mvebu.txt. +Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml. Required properties: diff --git a/Documentation/devicetree/bindings/gpio/gpio-mvebu.txt b/Documentation/devicetree/bindings/gpio/gpio-mvebu.txt deleted file mode 100644 index 0fc6700ed800..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-mvebu.txt +++ /dev/null @@ -1,93 +0,0 @@ -* Marvell EBU GPIO controller - -Required properties: - -- compatible : Should be "marvell,orion-gpio", "marvell,mv78200-gpio", - "marvell,armadaxp-gpio" or "marvell,armada-8k-gpio". - - "marvell,orion-gpio" should be used for Orion, Kirkwood, Dove, - Discovery (except MV78200) and Armada 370. "marvell,mv78200-gpio" - should be used for the Discovery MV78200. - - "marvel,armadaxp-gpio" should be used for all Armada XP SoCs - (MV78230, MV78260, MV78460). - - "marvell,armada-8k-gpio" should be used for the Armada 7K and 8K - SoCs (either from AP or CP), see - Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt - for specific details about the offset property. - -- reg: Address and length of the register set for the device. Only one - entry is expected, except for the "marvell,armadaxp-gpio" variant - for which two entries are expected: one for the general registers, - one for the per-cpu registers. Not used for marvell,armada-8k-gpio. - -- interrupts: The list of interrupts that are used for all the pins - managed by this GPIO bank. There can be more than one interrupt - (example: 1 interrupt per 8 pins on Armada XP, which means 4 - interrupts per bank of 32 GPIOs). - -- interrupt-controller: identifies the node as an interrupt controller - -- #interrupt-cells: specifies the number of cells needed to encode an - interrupt source. Should be two. - The first cell is the GPIO number. - The second cell is used to specify flags: - bits[3:0] trigger type and level flags: - 1 = low-to-high edge triggered. - 2 = high-to-low edge triggered. - 4 = active high level-sensitive. - 8 = active low level-sensitive. - -- gpio-controller: marks the device node as a gpio controller - -- ngpios: number of GPIOs this controller has - -- #gpio-cells: Should be two. The first cell is the pin number. The - second cell is reserved for flags, unused at the moment. - -Optional properties: - -In order to use the GPIO lines in PWM mode, some additional optional -properties are required. - -- compatible: Must contain "marvell,armada-370-gpio" - -- reg: an additional register set is needed, for the GPIO Blink - Counter on/off registers. - -- reg-names: Must contain an entry "pwm" corresponding to the - additional register range needed for PWM operation. - -- #pwm-cells: Should be two. The first cell is the GPIO line number. The - second cell is the period in nanoseconds. - -- clocks: Must be a phandle to the clock for the GPIO controller. - -Example: - - gpio0: gpio@d0018100 { - compatible = "marvell,armadaxp-gpio"; - reg = <0xd0018100 0x40>, - <0xd0018800 0x30>; - ngpios = <32>; - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - interrupts = <16>, <17>, <18>, <19>; - }; - - gpio1: gpio@18140 { - compatible = "marvell,armada-370-gpio"; - reg = <0x18140 0x40>, <0x181c8 0x08>; - reg-names = "gpio", "pwm"; - ngpios = <17>; - gpio-controller; - #gpio-cells = <2>; - #pwm-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - interrupts = <87>, <88>, <89>; - clocks = <&coreclk 0>; - }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml b/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml new file mode 100644 index 000000000000..d1695e7bd825 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml @@ -0,0 +1,146 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/gpio-mvebu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell EBU GPIO controller + +maintainers: + - Thomas Petazzoni + - Andrew Lunn + +properties: + compatible: + oneOf: + - enum: + - marvell,armada-8k-gpio + - marvell,orion-gpio + + - items: + - enum: + - marvell,mv78200-gpio + - marvell,armada-370-gpio + - marvell,armadaxp-gpio + - const: marvell,orion-gpio + + reg: + description: | + Address and length of the register set for the device. Not used for + marvell,armada-8k-gpio. + + For the "marvell,armadaxp-gpio" variant a second entry is expected for + the per-cpu registers. For other variants second entry can be provided, + for the PWM function using the GPIO Blink Counter on/off registers. + minItems: 1 + maxItems: 2 + + reg-names: + items: + - const: gpio + - const: pwm + minItems: 1 + + interrupts: + description: | + The list of interrupts that are used for all the pins managed by this + GPIO bank. There can be more than one interrupt (example: 1 interrupt + per 8 pins on Armada XP, which means 4 interrupts per bank of 32 + GPIOs). + minItems: 1 + maxItems: 4 + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + + gpio-controller: true + + ngpios: + minimum: 1 + maximum: 32 + + "#gpio-cells": + const: 2 + + "#pwm-cells": + description: + The first cell is the GPIO line number. The second cell is the period + in nanoseconds. + const: 2 + + clocks: + description: + Clock(s) used for PWM function. + items: + - description: Core clock + - description: AXI bus clock + minItems: 1 + + clock-names: + items: + - const: core + - const: axi + minItems: 1 + +required: + - compatible + - gpio-controller + - ngpios + - "#gpio-cells" + +allOf: + - if: + properties: + compatible: + contains: + const: marvell,armada-8k-gpio + then: + required: + - offset + else: + required: + - reg + + - if: + properties: + compatible: + contains: + const: marvell,armadaxp-gpio + then: + properties: + reg: + minItems: 2 + reg-names: + minItems: 2 + +unevaluatedProperties: true + +examples: + - | + gpio@d0018100 { + compatible = "marvell,armadaxp-gpio", "marvell,orion-gpio"; + reg = <0xd0018100 0x40>, <0xd0018800 0x30>; + ngpios = <32>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <16>, <17>, <18>, <19>; + }; + + - | + gpio@18140 { + compatible = "marvell,armada-370-gpio", "marvell,orion-gpio"; + reg = <0x18140 0x40>, <0x181c8 0x08>; + reg-names = "gpio", "pwm"; + ngpios = <17>; + gpio-controller; + #gpio-cells = <2>; + #pwm-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <87>, <88>, <89>; + clocks = <&coreclk 0>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 651616ed8ae2..3405cb36cf02 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16330,7 +16330,7 @@ L: linux-pwm@vger.kernel.org S: Maintained Q: https://patchwork.ozlabs.org/project/linux-pwm/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm.git -F: Documentation/devicetree/bindings/gpio/gpio-mvebu.txt +F: Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml F: Documentation/devicetree/bindings/pwm/ F: Documentation/driver-api/pwm.rst F: drivers/gpio/gpio-mvebu.c -- cgit v1.2.3 From 988c8c0cd04de81c7cde5b0a55cabf0c67136340 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Thu, 26 May 2022 13:29:45 +1200 Subject: dt-bindings: gpio: gpio-mvebu: deprecate armadaxp-gpio Commit 5f79c651e81e ("arm: mvebu: use global interrupts for GPIOs on Armada XP") the marvell,armadaxp-gpio compatible obsolete. The driver code still exists to handle the armadaxp behaviour but all the in-tree boards use the marvell,armada-370-gpio. Document the marvell,armadaxp-gpio compatible as deprecated. Signed-off-by: Chris Packham Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/gpio-mvebu.yaml | 24 ++++++++-------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml b/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml index d1695e7bd825..459ec35864fe 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml @@ -21,17 +21,21 @@ properties: - enum: - marvell,mv78200-gpio - marvell,armada-370-gpio - - marvell,armadaxp-gpio - const: marvell,orion-gpio + - description: Deprecated binding + items: + - const: marvell,armadaxp-gpio + - const: marvell,orion-gpio + deprecated: true + reg: description: | Address and length of the register set for the device. Not used for marvell,armada-8k-gpio. - For the "marvell,armadaxp-gpio" variant a second entry is expected for - the per-cpu registers. For other variants second entry can be provided, - for the PWM function using the GPIO Blink Counter on/off registers. + A second entry can be provided, for the PWM function using the GPIO Blink + Counter on/off registers. minItems: 1 maxItems: 2 @@ -103,18 +107,6 @@ allOf: required: - reg - - if: - properties: - compatible: - contains: - const: marvell,armadaxp-gpio - then: - properties: - reg: - minItems: 2 - reg-names: - minItems: 2 - unevaluatedProperties: true examples: -- cgit v1.2.3 From df08a6fc0d5d7dd579dd0902893a433765d9f4c5 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Thu, 26 May 2022 13:29:46 +1200 Subject: dt-bindings: gpio: gpio-mvebu: document offset and marvell,pwm-offset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The offset and marvell,pwm-offset properties weren't in the old binding. Add them based on the existing usage in the driver and board DTS when the marvell,armada-8k-gpio compatible is used. Signed-off-by: Chris Packham Reviewed-by: Krzysztof Kozlowski Acked-by: Uwe Kleine-König Acked-by: Thierry Reding Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml b/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml index 459ec35864fe..f1bd1e6b2e1f 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml @@ -45,6 +45,10 @@ properties: - const: pwm minItems: 1 + offset: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Offset in the register map for the gpio registers (in bytes) + interrupts: description: | The list of interrupts that are used for all the pins managed by this @@ -68,6 +72,10 @@ properties: "#gpio-cells": const: 2 + marvell,pwm-offset: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Offset in the register map for the pwm registers (in bytes) + "#pwm-cells": description: The first cell is the GPIO line number. The second cell is the period -- cgit v1.2.3 From 0651a730924b172476f67c7c6e01e898f84cd8f3 Mon Sep 17 00:00:00 2001 From: Devarsh Thakkar Date: Mon, 13 Jun 2022 11:13:10 +0530 Subject: gpio: davinci: Add support for system suspend/resume PM Add support for system suspend/resume PM hooks, save the register context of all the required gpio registers on suspend and restore context on the resume. Signed-off-by: Devarsh Thakkar Signed-off-by: Aswath Govindraju Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-davinci.c | 83 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index f960587f86a3..59c4c48d8296 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -62,6 +63,8 @@ struct davinci_gpio_controller { void __iomem *regs[MAX_REGS_BANKS]; int gpio_unbanked; int irqs[MAX_INT_PER_BANK]; + struct davinci_gpio_regs context[MAX_REGS_BANKS]; + u32 binten_context; }; static inline u32 __gpio_mask(unsigned gpio) @@ -622,6 +625,85 @@ done: return 0; } +static void davinci_gpio_save_context(struct davinci_gpio_controller *chips, + u32 nbank) +{ + struct davinci_gpio_regs __iomem *g; + struct davinci_gpio_regs *context; + u32 bank; + void __iomem *base; + + base = chips->regs[0] - offset_array[0]; + chips->binten_context = readl_relaxed(base + BINTEN); + + for (bank = 0; bank < nbank; bank++) { + g = chips->regs[bank]; + context = &chips->context[bank]; + context->dir = readl_relaxed(&g->dir); + context->set_data = readl_relaxed(&g->set_data); + context->set_rising = readl_relaxed(&g->set_rising); + context->set_falling = readl_relaxed(&g->set_falling); + } + + /* Clear Bank interrupt enable bit */ + writel_relaxed(0, base + BINTEN); + + /* Clear all interrupt status registers */ + writel_relaxed(GENMASK(31, 0), &g->intstat); +} + +static void davinci_gpio_restore_context(struct davinci_gpio_controller *chips, + u32 nbank) +{ + struct davinci_gpio_regs __iomem *g; + struct davinci_gpio_regs *context; + u32 bank; + void __iomem *base; + + base = chips->regs[0] - offset_array[0]; + + if (readl_relaxed(base + BINTEN) != chips->binten_context) + writel_relaxed(chips->binten_context, base + BINTEN); + + for (bank = 0; bank < nbank; bank++) { + g = chips->regs[bank]; + context = &chips->context[bank]; + if (readl_relaxed(&g->dir) != context->dir) + writel_relaxed(context->dir, &g->dir); + if (readl_relaxed(&g->set_data) != context->set_data) + writel_relaxed(context->set_data, &g->set_data); + if (readl_relaxed(&g->set_rising) != context->set_rising) + writel_relaxed(context->set_rising, &g->set_rising); + if (readl_relaxed(&g->set_falling) != context->set_falling) + writel_relaxed(context->set_falling, &g->set_falling); + } +} + +static int davinci_gpio_suspend(struct device *dev) +{ + struct davinci_gpio_controller *chips = dev_get_drvdata(dev); + struct davinci_gpio_platform_data *pdata = dev_get_platdata(dev); + u32 nbank = DIV_ROUND_UP(pdata->ngpio, 32); + + davinci_gpio_save_context(chips, nbank); + + return 0; +} + +static int davinci_gpio_resume(struct device *dev) +{ + struct davinci_gpio_controller *chips = dev_get_drvdata(dev); + struct davinci_gpio_platform_data *pdata = dev_get_platdata(dev); + u32 nbank = DIV_ROUND_UP(pdata->ngpio, 32); + + davinci_gpio_restore_context(chips, nbank); + + return 0; +} + +DEFINE_SIMPLE_DEV_PM_OPS(davinci_gpio_dev_pm_ops, davinci_gpio_suspend, + davinci_gpio_resume); + static const struct of_device_id davinci_gpio_ids[] = { { .compatible = "ti,keystone-gpio", keystone_gpio_get_irq_chip}, { .compatible = "ti,am654-gpio", keystone_gpio_get_irq_chip}, @@ -634,6 +716,7 @@ static struct platform_driver davinci_gpio_driver = { .probe = davinci_gpio_probe, .driver = { .name = "davinci_gpio", + .pm = pm_sleep_ptr(&davinci_gpio_dev_pm_ops), .of_match_table = of_match_ptr(davinci_gpio_ids), }, }; -- cgit v1.2.3 From 31212be403585d118ec6c97c3abbf36e88439b30 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 10 Jun 2022 12:08:22 +0200 Subject: dt-bindings: gpio: renesas,rcar-gpio: R-Car V3U is R-Car Gen4 Despite the name, R-Car V3U is the first member of the R-Car Gen4 family. Hence move its compatible value to the R-Car Gen4 section. Signed-off-by: Geert Uytterhoeven Acked-by: Krzysztof Kozlowski Reviewed-by: Wolfram Sang Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml b/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml index 0681a4790cd6..75e5da6a7cc0 100644 --- a/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml @@ -48,11 +48,9 @@ properties: - renesas,gpio-r8a77995 # R-Car D3 - const: renesas,rcar-gen3-gpio # R-Car Gen3 or RZ/G2 - - items: - - const: renesas,gpio-r8a779a0 # R-Car V3U - - items: - enum: + - renesas,gpio-r8a779a0 # R-Car V3U - renesas,gpio-r8a779f0 # R-Car S4-8 - const: renesas,rcar-gen4-gpio # R-Car Gen4 -- cgit v1.2.3 From 2a1192ff0835cb4b0ccd6f6e85c93aa0dc6f66b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 14 Jun 2022 17:23:38 +0200 Subject: gpio: twl4030: Drop platform teardown callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no machine providing a teardown callback, so drop the unused code. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-twl4030.c | 11 ----------- include/linux/mfd/twl.h | 2 -- 2 files changed, 13 deletions(-) diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index de249726230e..e2cb7cb90c8c 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -593,18 +593,7 @@ out: /* Cannot use as gpio_twl4030_probe() calls us */ static int gpio_twl4030_remove(struct platform_device *pdev) { - struct twl4030_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev); struct gpio_twl4030_priv *priv = platform_get_drvdata(pdev); - int status; - - if (pdata && pdata->teardown) { - status = pdata->teardown(&pdev->dev, priv->gpio_chip.base, - TWL4030_GPIO_MAX); - if (status) { - dev_dbg(&pdev->dev, "teardown --> %d\n", status); - return status; - } - } gpiochip_remove(&priv->gpio_chip); diff --git a/include/linux/mfd/twl.h b/include/linux/mfd/twl.h index 8871cc5188a0..c8cd31756037 100644 --- a/include/linux/mfd/twl.h +++ b/include/linux/mfd/twl.h @@ -594,8 +594,6 @@ struct twl4030_gpio_platform_data { int (*setup)(struct device *dev, unsigned gpio, unsigned ngpio); - int (*teardown)(struct device *dev, - unsigned gpio, unsigned ngpio); }; struct twl4030_madc_platform_data { -- cgit v1.2.3 From 421e3c16c32ad37b7124ae663d61e50546a57f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 14 Jun 2022 17:23:39 +0200 Subject: gpio: twl4030: Don't return an error after WARN in .remove MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Returning a non-zero value in a platform driver's remove callback only results in an error message ("remove callback returned a non-zero value. This will be ignored.", see platform_remove()), and then the device is removed anyhow. As there was just a WARN_ON triggered, return 0 to drop the follow up warning. The latter output is hardly relevant after the big WARN splat. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-twl4030.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index e2cb7cb90c8c..5046e51af8df 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -597,12 +597,9 @@ static int gpio_twl4030_remove(struct platform_device *pdev) gpiochip_remove(&priv->gpio_chip); - if (is_module()) - return 0; - /* REVISIT no support yet for deregistering all the IRQs */ - WARN_ON(1); - return -EIO; + WARN_ON(!is_module()); + return 0; } static const struct of_device_id twl_gpio_match[] = { -- cgit v1.2.3 From 7e55b33d3f18fde5c7a57b6c52d80499485c737f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 14 Jun 2022 21:48:02 +0200 Subject: gpio: ucb1400: Remove platform setup and teardown support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no user of these callbacks. The motivation for this change is to stop returning an error code from the remove callback. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ucb1400.c | 20 -------------------- drivers/mfd/ucb1400_core.c | 6 ++---- include/linux/ucb1400.h | 2 -- 3 files changed, 2 insertions(+), 26 deletions(-) diff --git a/drivers/gpio/gpio-ucb1400.c b/drivers/gpio/gpio-ucb1400.c index d2a8644864c3..386e69300332 100644 --- a/drivers/gpio/gpio-ucb1400.c +++ b/drivers/gpio/gpio-ucb1400.c @@ -64,34 +64,14 @@ static int ucb1400_gpio_probe(struct platform_device *dev) ucb->gc.can_sleep = true; err = devm_gpiochip_add_data(&dev->dev, &ucb->gc, ucb); - if (err) - goto err; - - if (ucb->gpio_setup) - err = ucb->gpio_setup(&dev->dev, ucb->gc.ngpio); err: return err; } -static int ucb1400_gpio_remove(struct platform_device *dev) -{ - int err = 0; - struct ucb1400_gpio *ucb = platform_get_drvdata(dev); - - if (ucb && ucb->gpio_teardown) { - err = ucb->gpio_teardown(&dev->dev, ucb->gc.ngpio); - if (err) - return err; - } - - return err; -} - static struct platform_driver ucb1400_gpio_driver = { .probe = ucb1400_gpio_probe, - .remove = ucb1400_gpio_remove, .driver = { .name = "ucb1400_gpio" }, diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c index 8c3832a58ef6..ac1d18039568 100644 --- a/drivers/mfd/ucb1400_core.c +++ b/drivers/mfd/ucb1400_core.c @@ -72,11 +72,9 @@ static int ucb1400_core_probe(struct device *dev) /* GPIO */ ucb_gpio.ac97 = ac97; - if (pdata) { - ucb_gpio.gpio_setup = pdata->gpio_setup; - ucb_gpio.gpio_teardown = pdata->gpio_teardown; + if (pdata) ucb_gpio.gpio_offset = pdata->gpio_offset; - } + ucb->ucb1400_gpio = platform_device_alloc("ucb1400_gpio", -1); if (!ucb->ucb1400_gpio) { err = -ENOMEM; diff --git a/include/linux/ucb1400.h b/include/linux/ucb1400.h index 0968ef458447..22345391350b 100644 --- a/include/linux/ucb1400.h +++ b/include/linux/ucb1400.h @@ -84,8 +84,6 @@ struct ucb1400_gpio { struct gpio_chip gc; struct snd_ac97 *ac97; int gpio_offset; - int (*gpio_setup)(struct device *dev, int ngpio); - int (*gpio_teardown)(struct device *dev, int ngpio); }; struct ucb1400_ts { -- cgit v1.2.3 From 1923433cd660fda6315b8eb1bb8372cf7e85acf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 21 Jun 2022 08:35:24 +0200 Subject: gpio: brcmstb: Make .remove() obviously always return 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit priv cannot be NULL because brcmstb_gpio_probe() calls platform_set_drvdata() with a non-NULL argument, so the check for !priv can be dropped. Also remove the variable ret that is only used to hide a bit that in the end zero is returned. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-brcmstb.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index 6b7439b44690..f620f0712134 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -385,12 +385,7 @@ static int brcmstb_gpio_remove(struct platform_device *pdev) { struct brcmstb_gpio_priv *priv = platform_get_drvdata(pdev); struct brcmstb_gpio_bank *bank; - int offset, ret = 0, virq; - - if (!priv) { - dev_err(&pdev->dev, "called %s without drvdata!\n", __func__); - return -EFAULT; - } + int offset, virq; if (priv->parent_irq > 0) irq_set_chained_handler_and_data(priv->parent_irq, NULL, NULL); @@ -411,7 +406,7 @@ static int brcmstb_gpio_remove(struct platform_device *pdev) list_for_each_entry(bank, &priv->bank_list, node) gpiochip_remove(&bank->gc); - return ret; + return 0; } static int brcmstb_gpio_of_xlate(struct gpio_chip *gc, -- cgit v1.2.3 From 3d57fa2a228c973032e94aceeef92a3e9704b9ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 15 Jun 2022 14:47:18 +0200 Subject: gpio: xgs-iproc: Drop if with an always false condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The remove callback is only called after probe completed successfully. In this case platform_set_drvdata() was called with a non-NULL argument and so chip is never NULL. Also note that returning an error code from a remove callback doesn't result in the device staying bound. It's still removed and devm callbacks are called. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xgs-iproc.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-xgs-iproc.c b/drivers/gpio/gpio-xgs-iproc.c index 43ca52fa6f9a..fd88500399c6 100644 --- a/drivers/gpio/gpio-xgs-iproc.c +++ b/drivers/gpio/gpio-xgs-iproc.c @@ -281,11 +281,7 @@ static int iproc_gpio_probe(struct platform_device *pdev) static int iproc_gpio_remove(struct platform_device *pdev) { - struct iproc_gpio_chip *chip; - - chip = platform_get_drvdata(pdev); - if (!chip) - return -ENODEV; + struct iproc_gpio_chip *chip = platform_get_drvdata(pdev); if (chip->intr) { u32 val; -- cgit v1.2.3 From 91e9111bdd088993948f8c215a8a01acc67a7a86 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 29 Jun 2022 19:04:19 +0200 Subject: gpio: pca9570: Add DT bindings for NXP PCA9571 This patch adds device tree bindings for the NXP PCA9571, a 8-bit I2C GPIO expander. Signed-off-by: Lucas Stach Acked-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml b/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml index 338c5312a106..1acaa0a3d35a 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml @@ -13,6 +13,7 @@ properties: compatible: enum: - nxp,pca9570 + - nxp,pca9571 reg: maxItems: 1 -- cgit v1.2.3 From d274f02eb98be8ec1d13bb9d16493eb1711a3d37 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 29 Jun 2022 19:04:20 +0200 Subject: gpio: pca9570: add pca9571 support The PCA9571 very similar to the PCA9570, it only differs in the number of GPIOs. Signed-off-by: Lucas Stach Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca9570.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c index cb2b2f735c15..ab2a652964ec 100644 --- a/drivers/gpio/gpio-pca9570.c +++ b/drivers/gpio/gpio-pca9570.c @@ -121,12 +121,14 @@ static int pca9570_probe(struct i2c_client *client) static const struct i2c_device_id pca9570_id_table[] = { { "pca9570", 4 }, + { "pca9571", 8 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, pca9570_id_table); static const struct of_device_id pca9570_of_match_table[] = { { .compatible = "nxp,pca9570", .data = (void *)4 }, + { .compatible = "nxp,pca9571", .data = (void *)8 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, pca9570_of_match_table); -- cgit v1.2.3 From a2a15e1263ab1c17f798eab1290180ffa4011223 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 28 Jun 2022 22:59:12 +0300 Subject: gpio: adnp: use simple i2c probe function The i2c probe functions here don't use the id information provided in their second argument, so the single-parameter i2c probe function ("probe_new") can be used instead. Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-adnp.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index cc349d4e4973..075782831044 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -485,8 +485,7 @@ static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios, return 0; } -static int adnp_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adnp_i2c_probe(struct i2c_client *client) { struct device_node *np = client->dev.of_node; struct adnp *adnp; @@ -535,7 +534,7 @@ static struct i2c_driver adnp_i2c_driver = { .name = "gpio-adnp", .of_match_table = adnp_of_match, }, - .probe = adnp_i2c_probe, + .probe_new = adnp_i2c_probe, .id_table = adnp_i2c_id, }; module_i2c_driver(adnp_i2c_driver); -- cgit v1.2.3 From 8773bacefcd7a5c742437974771976a9e79288f7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 28 Jun 2022 22:59:13 +0300 Subject: gpio: adnp: Make use of device properties Convert the module to be property provider agnostic and allow it to be used on non-OF platforms. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-adnp.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index 075782831044..a6439e3daff0 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -6,8 +6,9 @@ #include #include #include +#include #include -#include +#include #include #include @@ -487,19 +488,15 @@ static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios, static int adnp_i2c_probe(struct i2c_client *client) { - struct device_node *np = client->dev.of_node; + struct device *dev = &client->dev; struct adnp *adnp; u32 num_gpios; int err; - err = of_property_read_u32(np, "nr-gpios", &num_gpios); + err = device_property_read_u32(dev, "nr-gpios", &num_gpios); if (err < 0) return err; - client->irq = irq_of_parse_and_map(np, 0); - if (!client->irq) - return -EPROBE_DEFER; - adnp = devm_kzalloc(&client->dev, sizeof(*adnp), GFP_KERNEL); if (!adnp) return -ENOMEM; @@ -507,8 +504,7 @@ static int adnp_i2c_probe(struct i2c_client *client) mutex_init(&adnp->i2c_lock); adnp->client = client; - err = adnp_gpio_setup(adnp, num_gpios, - of_property_read_bool(np, "interrupt-controller")); + err = adnp_gpio_setup(adnp, num_gpios, device_property_read_bool(dev, "interrupt-controller")); if (err) return err; -- cgit v1.2.3 From 036e9cf35689b9f6a0d1bb8d34ec7ddf44714942 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Thu, 23 Jun 2022 18:08:00 +0200 Subject: dt-bindings: gpio: rockchip: add gpio-ranges Allow usage of gpio-ranges with the rockchip gpio controller. The driver already had support for this since it has been added to the mainline kernel in the first place. Acked-by: Rob Herring Signed-off-by: Sebastian Reichel Reviewed-by: Heiko Stuebner Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml b/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml index d4e42c2b995b..affd823c881d 100644 --- a/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml +++ b/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml @@ -27,6 +27,8 @@ properties: - description: APB interface clock source - description: GPIO debounce reference clock source + gpio-ranges: true + gpio-controller: true gpio-line-names: true -- cgit v1.2.3 From cc165ba48aaf7d792e99d0c7e4b12e9625bc73e3 Mon Sep 17 00:00:00 2001 From: Jianqun Xu Date: Thu, 23 Jun 2022 18:08:01 +0200 Subject: gpio: rockchip: add support for rk3588 Add V2.1 rockchip gpio controller type, which is part of the RK3588 SoC. Signed-off-by: Jianqun Xu Reviewed-by: Linus Walleij Signed-off-by: Sebastian Reichel Reviewed-by: Heiko Stuebner --- drivers/gpio/gpio-rockchip.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index e342a6dc4c6c..f91e876fd969 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -27,6 +27,7 @@ #define GPIO_TYPE_V1 (0) /* GPIO Version ID reserved */ #define GPIO_TYPE_V2 (0x01000C2B) /* GPIO Version ID 0x01000C2B */ +#define GPIO_TYPE_V2_1 (0x0101157C) /* GPIO Version ID 0x0101157C */ static const struct rockchip_gpio_regs gpio_regs_v1 = { .port_dr = 0x00, @@ -664,7 +665,7 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank) id = readl(bank->reg_base + gpio_regs_v2.version_id); /* If not gpio v2, that is default to v1. */ - if (id == GPIO_TYPE_V2) { + if (id == GPIO_TYPE_V2 || id == GPIO_TYPE_V2_1) { bank->gpio_regs = &gpio_regs_v2; bank->gpio_type = GPIO_TYPE_V2; bank->db_clk = of_clk_get(bank->of_node, 1); -- cgit v1.2.3 From 4a40ccccb660d9e62fe9f4a157be8d3c3a5ca013 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 28 Jun 2022 22:39:03 +0300 Subject: gpio: adp5588: Switch from of headers to mod_devicetable.h There is nothing directly using of specific interfaces in this driver, so lets not include the headers. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-adp5588.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c index e388e75103f4..51ed23ba4645 100644 --- a/drivers/gpio/gpio-adp5588.c +++ b/drivers/gpio/gpio-adp5588.c @@ -6,7 +6,6 @@ * Copyright 2009-2010 Analog Devices Inc. */ -#include #include #include #include @@ -14,7 +13,8 @@ #include #include #include -#include +#include +#include #include @@ -427,18 +427,16 @@ static const struct i2c_device_id adp5588_gpio_id[] = { }; MODULE_DEVICE_TABLE(i2c, adp5588_gpio_id); -#ifdef CONFIG_OF static const struct of_device_id adp5588_gpio_of_id[] = { { .compatible = "adi," DRV_NAME, }, {}, }; MODULE_DEVICE_TABLE(of, adp5588_gpio_of_id); -#endif static struct i2c_driver adp5588_gpio_driver = { .driver = { .name = DRV_NAME, - .of_match_table = of_match_ptr(adp5588_gpio_of_id), + .of_match_table = adp5588_gpio_of_id, }, .probe_new = adp5588_gpio_probe, .remove = adp5588_gpio_remove, -- cgit v1.2.3 From 669f5618feb8b4847f3a64d541cfae22e999a91a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 28 Jun 2022 22:39:04 +0300 Subject: gpio: adp5588: Do not use defined value for driver name and compatible It's wrong to use defined string literal for three semantically different cases, i.e.: 1) compatible string, which is part of ABI and has to have specific format; 2) I2C ID, which is user space visible and also ABI; 3) driver name, that can be changed. Drop the define and use appropriate string literals in place. While at it, drop comma at terminator entry of OF ID table. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-adp5588.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c index 51ed23ba4645..d6a229a67044 100644 --- a/drivers/gpio/gpio-adp5588.c +++ b/drivers/gpio/gpio-adp5588.c @@ -18,8 +18,6 @@ #include -#define DRV_NAME "adp5588-gpio" - /* * Early pre 4.0 Silicon required to delay readout by at least 25ms, * since the Event Counter Register updated 25ms after the interrupt @@ -422,20 +420,20 @@ static int adp5588_gpio_remove(struct i2c_client *client) } static const struct i2c_device_id adp5588_gpio_id[] = { - {DRV_NAME, 0}, + { "adp5588-gpio" }, {} }; MODULE_DEVICE_TABLE(i2c, adp5588_gpio_id); static const struct of_device_id adp5588_gpio_of_id[] = { - { .compatible = "adi," DRV_NAME, }, - {}, + { .compatible = "adi,adp5588-gpio" }, + {} }; MODULE_DEVICE_TABLE(of, adp5588_gpio_of_id); static struct i2c_driver adp5588_gpio_driver = { .driver = { - .name = DRV_NAME, + .name = "adp5588-gpio", .of_match_table = adp5588_gpio_of_id, }, .probe_new = adp5588_gpio_probe, -- cgit v1.2.3 From 6169d76c56b30e57fcb080206edd5324d9cea296 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 28 Jun 2022 22:39:05 +0300 Subject: gpio: adp5588: sort header inclusion alphabetically Sort header inclusion alphabetically. Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-adp5588.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c index d6a229a67044..d49f12560cde 100644 --- a/drivers/gpio/gpio-adp5588.c +++ b/drivers/gpio/gpio-adp5588.c @@ -6,15 +6,15 @@ * Copyright 2009-2010 Analog Devices Inc. */ -#include -#include -#include -#include #include +#include +#include #include #include +#include #include #include +#include #include -- cgit v1.2.3 From 597a8a888d349c3804e92ef087646bd9a89fe53d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 29 Jun 2022 14:30:58 +0300 Subject: gpiolib: of: Use device_match_of_node() helper Instead of open coding, use device_match_of_node() helper. Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 3d6c3ffd5576..23b8de98bf7c 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -720,7 +720,7 @@ static void of_gpiochip_remove_hog(struct gpio_chip *chip, static int of_gpiochip_match_node(struct gpio_chip *chip, void *data) { - return chip->gpiodev->dev.of_node == data; + return device_match_of_node(&chip->gpiodev->dev, data); } static struct gpio_chip *of_find_gpiochip_by_node(struct device_node *np) -- cgit v1.2.3 From 13e024b66ca1f72b383f141b43cfa4e1140cedd1 Mon Sep 17 00:00:00 2001 From: Shinyzenith Date: Mon, 11 Jul 2022 10:59:35 +0530 Subject: gpio: lp3943: unsigned to unsigned int cleanup Getting rid of checkpatch findings. No functional changes. Signed-off-by: Shinyzenith Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-lp3943.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-lp3943.c b/drivers/gpio/gpio-lp3943.c index 8a30fb185aab..79edd5db49d2 100644 --- a/drivers/gpio/gpio-lp3943.c +++ b/drivers/gpio/gpio-lp3943.c @@ -42,7 +42,7 @@ struct lp3943_gpio { u16 input_mask; /* 1 = GPIO is input direction, 0 = output */ }; -static int lp3943_gpio_request(struct gpio_chip *chip, unsigned offset) +static int lp3943_gpio_request(struct gpio_chip *chip, unsigned int offset) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); struct lp3943 *lp3943 = lp3943_gpio->lp3943; @@ -54,7 +54,7 @@ static int lp3943_gpio_request(struct gpio_chip *chip, unsigned offset) return 0; } -static void lp3943_gpio_free(struct gpio_chip *chip, unsigned offset) +static void lp3943_gpio_free(struct gpio_chip *chip, unsigned int offset) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); struct lp3943 *lp3943 = lp3943_gpio->lp3943; @@ -72,7 +72,7 @@ static int lp3943_gpio_set_mode(struct lp3943_gpio *lp3943_gpio, u8 offset, val << mux[offset].shift); } -static int lp3943_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +static int lp3943_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); @@ -82,7 +82,7 @@ static int lp3943_gpio_direction_input(struct gpio_chip *chip, unsigned offset) } static int lp3943_get_gpio_in_status(struct lp3943_gpio *lp3943_gpio, - struct gpio_chip *chip, unsigned offset) + struct gpio_chip *chip, unsigned int offset) { u8 addr, read; int err; @@ -107,7 +107,7 @@ static int lp3943_get_gpio_in_status(struct lp3943_gpio *lp3943_gpio, } static int lp3943_get_gpio_out_status(struct lp3943_gpio *lp3943_gpio, - struct gpio_chip *chip, unsigned offset) + struct gpio_chip *chip, unsigned int offset) { struct lp3943 *lp3943 = lp3943_gpio->lp3943; const struct lp3943_reg_cfg *mux = lp3943->mux_cfg; @@ -128,7 +128,7 @@ static int lp3943_get_gpio_out_status(struct lp3943_gpio *lp3943_gpio, return -EINVAL; } -static int lp3943_gpio_get(struct gpio_chip *chip, unsigned offset) +static int lp3943_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); @@ -147,7 +147,7 @@ static int lp3943_gpio_get(struct gpio_chip *chip, unsigned offset) return lp3943_get_gpio_out_status(lp3943_gpio, chip, offset); } -static void lp3943_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static void lp3943_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); u8 data; @@ -160,7 +160,7 @@ static void lp3943_gpio_set(struct gpio_chip *chip, unsigned offset, int value) lp3943_gpio_set_mode(lp3943_gpio, offset, data); } -static int lp3943_gpio_direction_output(struct gpio_chip *chip, unsigned offset, +static int lp3943_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); -- cgit v1.2.3 From 6b7e8c796de0a0f1089c818097b9cd65817a2834 Mon Sep 17 00:00:00 2001 From: Aparna M Date: Fri, 8 Jul 2022 21:28:19 +0530 Subject: dt-bindings: gpio: Convert TI TPIC2810 GPIO Controller bindings to YAML Convert gpio-tpic2810 bindings to yaml format and remove outdated bindings in .txt format. Signed-off-by: Aparna M Reviewed-by: Rob Herring Reviewed-by: Linus Walleij Signed-off-by: Vignesh Raghavendra Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/gpio-tpic2810.txt | 16 ------- .../devicetree/bindings/gpio/gpio-tpic2810.yaml | 51 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 16 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-tpic2810.txt create mode 100644 Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml diff --git a/Documentation/devicetree/bindings/gpio/gpio-tpic2810.txt b/Documentation/devicetree/bindings/gpio/gpio-tpic2810.txt deleted file mode 100644 index 1afc2de7a537..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-tpic2810.txt +++ /dev/null @@ -1,16 +0,0 @@ -TPIC2810 GPIO controller bindings - -Required properties: - - compatible : Should be "ti,tpic2810". - - reg : The I2C address of the device - - gpio-controller : Marks the device node as a GPIO controller. - - #gpio-cells : Should be two. For consumer use see gpio.txt. - -Example: - - gpio@60 { - compatible = "ti,tpic2810"; - reg = <0x60>; - gpio-controller; - #gpio-cells = <2>; - }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml b/Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml new file mode 100644 index 000000000000..cb8a5c376e1e --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/gpio-tpic2810.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TPIC2810 GPIO controller bindings + +maintainers: + - Aswath Govindraju + +properties: + compatible: + enum: + - ti,tpic2810 + + reg: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + + gpio-line-names: + minItems: 1 + maxItems: 32 + +required: + - compatible + - reg + - gpio-controller + - "#gpio-cells" + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + gpio@60 { + compatible = "ti,tpic2810"; + reg = <0x60>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "LED A", "LED B", "LED C"; + }; + }; -- cgit v1.2.3 From 5d07a692f9562f9c06e62cce369e9dd108173a0f Mon Sep 17 00:00:00 2001 From: Liang He Date: Mon, 11 Jul 2022 20:52:38 +0800 Subject: gpio: gpiolib-of: Fix refcount bugs in of_mm_gpiochip_add_data() We should use of_node_get() when a new reference of device_node is created. It is noted that the old reference stored in 'mm_gc->gc.of_node' should also be decreased. This patch is based on the fact that there is a call site in function 'qe_add_gpiochips()' of src file 'drivers\soc\fsl\qe\gpio.c'. In this function, of_mm_gpiochip_add_data() is contained in an iteration of for_each_compatible_node() which will automatically increase and decrease the refcount. So we need additional of_node_get() for the reference escape in of_mm_gpiochip_add_data(). Fixes: a19e3da5bc5f ("of/gpio: Kill of_gpio_chip and add members directly to gpio_chip") Signed-off-by: Liang He Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-of.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 23b8de98bf7c..f80307be37d5 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -860,7 +860,8 @@ int of_mm_gpiochip_add_data(struct device_node *np, if (mm_gc->save_regs) mm_gc->save_regs(mm_gc); - mm_gc->gc.of_node = np; + of_node_put(mm_gc->gc.of_node); + mm_gc->gc.of_node = of_node_get(np); ret = gpiochip_add_data(gc, data); if (ret) @@ -868,6 +869,7 @@ int of_mm_gpiochip_add_data(struct device_node *np, return 0; err2: + of_node_put(np); iounmap(mm_gc->regs); err1: kfree(gc->label); -- cgit v1.2.3 From b06d7b458a6eace1cebd0c1e4a235407824125e8 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 11 Jul 2022 10:36:35 +0200 Subject: gpio: GPIO_SAMA5D2_PIOBU should depend on ARCH_AT91 The SAMA5D2 PIOBU is only present on some AT91/Microchip SoCs. Hence add a dependency on ARCH_AT91, to prevent asking the user about this driver when configuring a kernel without AT91/Microchip SoC support. Signed-off-by: Geert Uytterhoeven Acked-by: Nicolas Ferre Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b01961999ced..1d9316cecacd 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -544,6 +544,7 @@ config GPIO_SAMA5D2_PIOBU tristate "SAMA5D2 PIOBU GPIO support" depends on MFD_SYSCON depends on OF_GPIO + depends on ARCH_AT91 || COMPILE_TEST select GPIO_SYSCON help Say yes here to use the PIOBU pins as GPIOs. -- cgit v1.2.3 From 3e2d53b2734bea05dba4f8fa94332ad6dd343712 Mon Sep 17 00:00:00 2001 From: Aakash Sen Sharma Date: Mon, 11 Jul 2022 19:47:53 +0530 Subject: gpio: 104-idi-48: unsigned to unsigned int cleanup Remove checkpatch warnings. No functional changes. Signed-off-by: Aakash Sen Sharma Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-104-idi-48.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c index 9521ece3ebef..c1e4c3629e17 100644 --- a/drivers/gpio/gpio-104-idi-48.c +++ b/drivers/gpio/gpio-104-idi-48.c @@ -51,23 +51,23 @@ struct idi_48_gpio { unsigned char cos_enb; }; -static int idi_48_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +static int idi_48_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { return GPIO_LINE_DIRECTION_IN; } -static int idi_48_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +static int idi_48_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { return 0; } -static int idi_48_gpio_get(struct gpio_chip *chip, unsigned offset) +static int idi_48_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); - unsigned i; + unsigned int i; static const unsigned int register_offset[6] = { 0, 1, 2, 4, 5, 6 }; void __iomem *port_addr; - unsigned mask; + unsigned int mask; for (i = 0; i < 48; i += 8) if (offset < i + 8) { @@ -112,10 +112,10 @@ static void idi_48_irq_mask(struct irq_data *data) { struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); - const unsigned offset = irqd_to_hwirq(data); - unsigned i; - unsigned mask; - unsigned boundary; + const unsigned int offset = irqd_to_hwirq(data); + unsigned int i; + unsigned int mask; + unsigned int boundary; unsigned long flags; for (i = 0; i < 48; i += 8) @@ -143,11 +143,11 @@ static void idi_48_irq_unmask(struct irq_data *data) { struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); - const unsigned offset = irqd_to_hwirq(data); - unsigned i; - unsigned mask; - unsigned boundary; - unsigned prev_irq_mask; + const unsigned int offset = irqd_to_hwirq(data); + unsigned int i; + unsigned int mask; + unsigned int boundary; + unsigned int prev_irq_mask; unsigned long flags; for (i = 0; i < 48; i += 8) @@ -172,7 +172,7 @@ static void idi_48_irq_unmask(struct irq_data *data) } } -static int idi_48_irq_set_type(struct irq_data *data, unsigned flow_type) +static int idi_48_irq_set_type(struct irq_data *data, unsigned int flow_type) { /* The only valid irq types are none and both-edges */ if (flow_type != IRQ_TYPE_NONE && -- cgit v1.2.3 From 160d6e402900672d4b0689a7c939be487460a1fe Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Thu, 14 Jul 2022 10:03:14 +0800 Subject: gpiolib: cdev: simplify linereq_free The edge detector is only ever started after the line desc has been determined, so move edge_detector_stop() inside the line desc check, and merge the two checked regions into one. Signed-off-by: Kent Gibson Reviewed-by: Andy Shevchenko Acked-by: Dipen Patel Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 0c9a63becfef..b44526e3630e 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -1460,15 +1460,15 @@ static ssize_t linereq_read(struct file *file, static void linereq_free(struct linereq *lr) { unsigned int i; - bool hte = false; + bool hte; for (i = 0; i < lr->num_lines; i++) { - if (lr->lines[i].desc) + if (lr->lines[i].desc) { hte = !!test_bit(FLAG_EVENT_CLOCK_HTE, &lr->lines[i].desc->flags); - edge_detector_stop(&lr->lines[i], hte); - if (lr->lines[i].desc) + edge_detector_stop(&lr->lines[i], hte); gpiod_free(lr->lines[i].desc); + } } kfifo_free(&lr->events); kfree(lr->label); -- cgit v1.2.3 From 2487a812236cdeecd0729cfc673fa352db44da5d Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Thu, 14 Jul 2022 10:03:15 +0800 Subject: gpiolib: cdev: simplify parameter in call to hte_edge_setup Improve readability by using the GPIO_V2_LINE_FLAG_EDGE_BOTH instead of combining the rising and falling edge flags. Signed-off-by: Kent Gibson Reviewed-by: Andy Shevchenko Acked-by: Dipen Patel Tested-by: Dipen Patel Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index b44526e3630e..f635bbbb6a6d 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -885,9 +885,7 @@ static int debounce_setup(struct line *line, return ret; line->irq = irq; } else { - ret = hte_edge_setup(line, - GPIO_V2_LINE_FLAG_EDGE_RISING | - GPIO_V2_LINE_FLAG_EDGE_FALLING); + ret = hte_edge_setup(line, GPIO_V2_LINE_FLAG_EDGE_BOTH); if (ret) return ret; } -- cgit v1.2.3 From cfa53463ac62d30e4f30ec6e171b9228689d5a83 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Thu, 14 Jul 2022 10:03:16 +0800 Subject: gpiolib: cdev: replace if-else chains with switches Improve readability by replacing if-else chains with switch statements. Signed-off-by: Kent Gibson Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index f635bbbb6a6d..bc7c8822ede0 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -588,7 +588,8 @@ static enum hte_return process_hw_ts_thread(void *p) le.timestamp_ns = line->timestamp_ns; eflags = READ_ONCE(line->eflags); - if (eflags == GPIO_V2_LINE_FLAG_EDGE_BOTH) { + switch (eflags) { + case GPIO_V2_LINE_FLAG_EDGE_BOTH: if (line->raw_level >= 0) { if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags)) level = !line->raw_level; @@ -602,13 +603,16 @@ static enum hte_return process_hw_ts_thread(void *p) le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; else le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; - } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) { + break; + case GPIO_V2_LINE_FLAG_EDGE_RISING: /* Emit low-to-high event */ le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; - } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) { + break; + case GPIO_V2_LINE_FLAG_EDGE_FALLING: /* Emit high-to-low event */ le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; - } else { + break; + default: return HTE_CB_HANDLED; } le.line_seqno = line->line_seqno; @@ -660,7 +664,6 @@ static irqreturn_t edge_irq_thread(int irq, void *p) struct line *line = p; struct linereq *lr = line->req; struct gpio_v2_line_event le; - u64 eflags; /* Do not leak kernel stack to userspace */ memset(&le, 0, sizeof(le)); @@ -679,23 +682,25 @@ static irqreturn_t edge_irq_thread(int irq, void *p) } line->timestamp_ns = 0; - eflags = READ_ONCE(line->eflags); - if (eflags == GPIO_V2_LINE_FLAG_EDGE_BOTH) { - int level = gpiod_get_value_cansleep(line->desc); - - if (level) + switch (READ_ONCE(line->eflags)) { + case GPIO_V2_LINE_FLAG_EDGE_BOTH: + if (gpiod_get_value_cansleep(line->desc)) /* Emit low-to-high event */ le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; else /* Emit high-to-low event */ le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; - } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) { + + break; + case GPIO_V2_LINE_FLAG_EDGE_RISING: /* Emit low-to-high event */ le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; - } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) { + break; + case GPIO_V2_LINE_FLAG_EDGE_FALLING: /* Emit high-to-low event */ le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; - } else { + break; + default: return IRQ_NONE; } line->line_seqno++; -- cgit v1.2.3 From 242202329f0d851f64901111217ca4d52795a4b6 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Thu, 14 Jul 2022 10:03:17 +0800 Subject: gpiolib: cdev: simplify line event identification Reorganise line event identification code to reduce code duplication, and replace if-else initializers with a helper function to improve readability. Signed-off-by: Kent Gibson Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij --- drivers/gpio/gpiolib-cdev.c | 44 +++++++++++++++----------------------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index bc7c8822ede0..5765379f4b54 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -569,6 +569,12 @@ static u64 line_event_timestamp(struct line *line) return ktime_get_ns(); } +static u32 line_event_id(int level) +{ + return level ? GPIO_V2_LINE_EVENT_RISING_EDGE : + GPIO_V2_LINE_EVENT_FALLING_EDGE; +} + static enum hte_return process_hw_ts_thread(void *p) { struct line *line; @@ -590,26 +596,19 @@ static enum hte_return process_hw_ts_thread(void *p) switch (eflags) { case GPIO_V2_LINE_FLAG_EDGE_BOTH: - if (line->raw_level >= 0) { - if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags)) - level = !line->raw_level; - else - level = line->raw_level; - } else { - level = gpiod_get_value_cansleep(line->desc); - } + level = (line->raw_level >= 0) ? + line->raw_level : + gpiod_get_raw_value_cansleep(line->desc); - if (level) - le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; - else - le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; + if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags)) + level = !level; + + le.id = line_event_id(level); break; case GPIO_V2_LINE_FLAG_EDGE_RISING: - /* Emit low-to-high event */ le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; break; case GPIO_V2_LINE_FLAG_EDGE_FALLING: - /* Emit high-to-low event */ le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; break; default: @@ -684,20 +683,12 @@ static irqreturn_t edge_irq_thread(int irq, void *p) switch (READ_ONCE(line->eflags)) { case GPIO_V2_LINE_FLAG_EDGE_BOTH: - if (gpiod_get_value_cansleep(line->desc)) - /* Emit low-to-high event */ - le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; - else - /* Emit high-to-low event */ - le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; - + le.id = line_event_id(gpiod_get_value_cansleep(line->desc)); break; case GPIO_V2_LINE_FLAG_EDGE_RISING: - /* Emit low-to-high event */ le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; break; case GPIO_V2_LINE_FLAG_EDGE_FALLING: - /* Emit high-to-low event */ le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; break; default: @@ -821,12 +812,7 @@ static void debounce_work_func(struct work_struct *work) le.line_seqno : atomic_inc_return(&lr->seqno); } - if (level) - /* Emit low-to-high event */ - le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; - else - /* Emit high-to-low event */ - le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; + le.id = line_event_id(level); linereq_put_event(lr, &le); } -- cgit v1.2.3 From b1a92e94560def63b71057d770e9db2f798ab58c Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Thu, 14 Jul 2022 10:03:18 +0800 Subject: gpiolib: cdev: consolidate edge detector configuration flags Combine the polarity_change flag, struct line eflags, and hte enable flag into a single flag variable. The combination of these flags describes the configuration state of the edge detector, so formalize and clarify that by combining them into a single variable, edflags, in struct line. The edflags is a subset of the GPIO_V2_LINE_FLAGsb relevant to the edge detector, and is also a superset of the eflags it replaces. The eflags name is still used to describe the subset of edflags corresponding to the rising/falling edge flags where edflags is masked down to that subset. This consolidation reduces the number of variables being passed, simplifies state comparisons, and provides a more extensible foundation should additional edge sources be integrated in the future. Signed-off-by: Kent Gibson Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 126 +++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 66 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 5765379f4b54..01c76aa00701 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -430,12 +430,15 @@ struct line { struct linereq *req; unsigned int irq; /* - * eflags is set by edge_detector_setup(), edge_detector_stop() and - * edge_detector_update(), which are themselves mutually exclusive, - * and is accessed by edge_irq_thread() and debounce_work_func(), - * which can both live with a slightly stale value. + * The flags for the active edge detector configuration. + * + * edflags is set by linereq_create(), linereq_free(), and + * linereq_set_config_unlocked(), which are themselves mutually + * exclusive, and is accessed by edge_irq_thread(), + * process_hw_ts_thread() and debounce_work_func(), + * which can all live with a slightly stale value. */ - u64 eflags; + u64 edflags; /* * timestamp_ns and req_seqno are accessed only by * edge_irq_handler() and edge_irq_thread(), which are themselves @@ -541,6 +544,12 @@ struct linereq { GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE | \ GPIO_V2_LINE_BIAS_FLAGS) +/* subset of flags relevant for edge detector configuration */ +#define GPIO_V2_LINE_EDGE_DETECTOR_FLAGS \ + (GPIO_V2_LINE_FLAG_ACTIVE_LOW | \ + GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE | \ + GPIO_V2_LINE_EDGE_FLAGS) + static void linereq_put_event(struct linereq *lr, struct gpio_v2_line_event *le) { @@ -580,8 +589,8 @@ static enum hte_return process_hw_ts_thread(void *p) struct line *line; struct linereq *lr; struct gpio_v2_line_event le; + u64 edflags; int level; - u64 eflags; if (!p) return HTE_CB_HANDLED; @@ -592,15 +601,15 @@ static enum hte_return process_hw_ts_thread(void *p) memset(&le, 0, sizeof(le)); le.timestamp_ns = line->timestamp_ns; - eflags = READ_ONCE(line->eflags); + edflags = READ_ONCE(line->edflags); - switch (eflags) { + switch (edflags & GPIO_V2_LINE_EDGE_FLAGS) { case GPIO_V2_LINE_FLAG_EDGE_BOTH: level = (line->raw_level >= 0) ? line->raw_level : gpiod_get_raw_value_cansleep(line->desc); - if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags)) + if (edflags & GPIO_V2_LINE_FLAG_ACTIVE_LOW) level = !level; le.id = line_event_id(level); @@ -681,7 +690,7 @@ static irqreturn_t edge_irq_thread(int irq, void *p) } line->timestamp_ns = 0; - switch (READ_ONCE(line->eflags)) { + switch (READ_ONCE(line->edflags) & GPIO_V2_LINE_EDGE_FLAGS) { case GPIO_V2_LINE_FLAG_EDGE_BOTH: le.id = line_event_id(gpiod_get_value_cansleep(line->desc)); break; @@ -756,16 +765,13 @@ static void debounce_work_func(struct work_struct *work) struct gpio_v2_line_event le; struct line *line = container_of(work, struct line, work.work); struct linereq *lr; - int level, diff_seqno; - u64 eflags; + u64 eflags, edflags = READ_ONCE(line->edflags); + int level = -1, diff_seqno; - if (test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) { + if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) level = line->raw_level; - if (level < 0) - level = gpiod_get_raw_value_cansleep(line->desc); - } else { + if (level < 0) level = gpiod_get_raw_value_cansleep(line->desc); - } if (level < 0) { pr_debug_ratelimited("debouncer failed to read line value\n"); return; @@ -777,12 +783,12 @@ static void debounce_work_func(struct work_struct *work) WRITE_ONCE(line->level, level); /* -- edge detection -- */ - eflags = READ_ONCE(line->eflags); + eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS; if (!eflags) return; /* switch from physical level to logical - if they differ */ - if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags)) + if (edflags & GPIO_V2_LINE_FLAG_ACTIVE_LOW) level = !level; /* ignore edges that are not being monitored */ @@ -796,7 +802,7 @@ static void debounce_work_func(struct work_struct *work) lr = line->req; le.timestamp_ns = line_event_timestamp(line); le.offset = gpio_chip_hwgpio(line->desc); - if (test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) { + if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) { /* discard events except the last one */ line->total_discard_seq -= 1; diff_seqno = line->last_seqno - line->total_discard_seq - @@ -843,8 +849,7 @@ static int hte_edge_setup(struct line *line, u64 eflags) process_hw_ts_thread, line); } -static int debounce_setup(struct line *line, - unsigned int debounce_period_us, bool hte_req) +static int debounce_setup(struct line *line, unsigned int debounce_period_us) { unsigned long irqflags; int ret, level, irq; @@ -864,7 +869,7 @@ static int debounce_setup(struct line *line, if (level < 0) return level; - if (!hte_req) { + if (!test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) { irq = gpiod_to_irq(line->desc); if (irq < 0) return -ENXIO; @@ -915,19 +920,19 @@ static u32 gpio_v2_line_config_debounce_period(struct gpio_v2_line_config *lc, return 0; } -static void edge_detector_stop(struct line *line, bool hte_en) +static void edge_detector_stop(struct line *line) { - if (line->irq && !hte_en) { + if (line->irq) { free_irq(line->irq, line); line->irq = 0; } - if (hte_en) + if (READ_ONCE(line->edflags) & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) hte_ts_put(&line->hdesc); cancel_delayed_work_sync(&line->work); WRITE_ONCE(line->sw_debounced, 0); - WRITE_ONCE(line->eflags, 0); + WRITE_ONCE(line->edflags, 0); if (line->desc) WRITE_ONCE(line->desc->debounce_period_us, 0); /* do not change line->level - see comment in debounced_value() */ @@ -935,23 +940,23 @@ static void edge_detector_stop(struct line *line, bool hte_en) static int edge_detector_setup(struct line *line, struct gpio_v2_line_config *lc, - unsigned int line_idx, - u64 eflags, bool hte_req) + unsigned int line_idx, u64 edflags) { u32 debounce_period_us; unsigned long irqflags = 0; + u64 eflags; int irq, ret; + eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS; if (eflags && !kfifo_initialized(&line->req->events)) { ret = kfifo_alloc(&line->req->events, line->req->event_buffer_size, GFP_KERNEL); if (ret) return ret; } - WRITE_ONCE(line->eflags, eflags); if (gpio_v2_line_config_debounced(lc, line_idx)) { debounce_period_us = gpio_v2_line_config_debounce_period(lc, line_idx); - ret = debounce_setup(line, debounce_period_us, hte_req); + ret = debounce_setup(line, debounce_period_us); if (ret) return ret; WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us); @@ -961,8 +966,8 @@ static int edge_detector_setup(struct line *line, if (!eflags || READ_ONCE(line->sw_debounced)) return 0; - if (hte_req) - return hte_edge_setup(line, eflags); + if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) + return hte_edge_setup(line, edflags); irq = gpiod_to_irq(line->desc); if (irq < 0) @@ -988,35 +993,29 @@ static int edge_detector_setup(struct line *line, static int edge_detector_update(struct line *line, struct gpio_v2_line_config *lc, - unsigned int line_idx, - u64 flags, bool polarity_change, - bool prev_hte_flag) + unsigned int line_idx, u64 edflags) { - u64 eflags = flags & GPIO_V2_LINE_EDGE_FLAGS; + u64 active_edflags = READ_ONCE(line->edflags); unsigned int debounce_period_us = gpio_v2_line_config_debounce_period(lc, line_idx); - bool hte_change = (prev_hte_flag != - ((flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) != 0)); - if ((READ_ONCE(line->eflags) == eflags) && !polarity_change && - (READ_ONCE(line->desc->debounce_period_us) == debounce_period_us) - && !hte_change) + if ((active_edflags == edflags) && + (READ_ONCE(line->desc->debounce_period_us) == debounce_period_us)) return 0; /* sw debounced and still will be...*/ if (debounce_period_us && READ_ONCE(line->sw_debounced)) { - WRITE_ONCE(line->eflags, eflags); WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us); return 0; } /* reconfiguring edge detection or sw debounce being disabled */ - if ((line->irq && !READ_ONCE(line->sw_debounced)) || prev_hte_flag || + if ((line->irq && !READ_ONCE(line->sw_debounced)) || + (active_edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) || (!debounce_period_us && READ_ONCE(line->sw_debounced))) - edge_detector_stop(line, prev_hte_flag); + edge_detector_stop(line); - return edge_detector_setup(line, lc, line_idx, eflags, - flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE); + return edge_detector_setup(line, lc, line_idx, edflags); } static u64 gpio_v2_line_config_flags(struct gpio_v2_line_config *lc, @@ -1285,22 +1284,17 @@ static long linereq_set_config_unlocked(struct linereq *lr, struct gpio_v2_line_config *lc) { struct gpio_desc *desc; + struct line *line; unsigned int i; - u64 flags; - bool polarity_change; - bool prev_hte_flag; + u64 flags, edflags; int ret; for (i = 0; i < lr->num_lines; i++) { + line = &lr->lines[i]; desc = lr->lines[i].desc; flags = gpio_v2_line_config_flags(lc, i); - polarity_change = - (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) != - ((flags & GPIO_V2_LINE_FLAG_ACTIVE_LOW) != 0)); - - prev_hte_flag = !!test_bit(FLAG_EVENT_CLOCK_HTE, &desc->flags); - gpio_v2_line_config_flags_to_desc_flags(flags, &desc->flags); + edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS; /* * Lines have to be requested explicitly for input * or output, else the line will be treated "as is". @@ -1308,7 +1302,7 @@ static long linereq_set_config_unlocked(struct linereq *lr, if (flags & GPIO_V2_LINE_FLAG_OUTPUT) { int val = gpio_v2_line_config_output_value(lc, i); - edge_detector_stop(&lr->lines[i], prev_hte_flag); + edge_detector_stop(line); ret = gpiod_direction_output(desc, val); if (ret) return ret; @@ -1317,12 +1311,13 @@ static long linereq_set_config_unlocked(struct linereq *lr, if (ret) return ret; - ret = edge_detector_update(&lr->lines[i], lc, i, - flags, polarity_change, prev_hte_flag); + ret = edge_detector_update(line, lc, i, edflags); if (ret) return ret; } + WRITE_ONCE(line->edflags, edflags); + blocking_notifier_call_chain(&desc->gdev->notifier, GPIO_V2_LINE_CHANGED_CONFIG, desc); @@ -1449,13 +1444,10 @@ static ssize_t linereq_read(struct file *file, static void linereq_free(struct linereq *lr) { unsigned int i; - bool hte; for (i = 0; i < lr->num_lines; i++) { if (lr->lines[i].desc) { - hte = !!test_bit(FLAG_EVENT_CLOCK_HTE, - &lr->lines[i].desc->flags); - edge_detector_stop(&lr->lines[i], hte); + edge_detector_stop(&lr->lines[i]); gpiod_free(lr->lines[i].desc); } } @@ -1491,7 +1483,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) struct gpio_v2_line_config *lc; struct linereq *lr; struct file *file; - u64 flags; + u64 flags, edflags; unsigned int i; int fd, ret; @@ -1565,6 +1557,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) if (ret < 0) goto out_free_linereq; + edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS; /* * Lines have to be requested explicitly for input * or output, else the line will be treated "as is". @@ -1581,12 +1574,13 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) goto out_free_linereq; ret = edge_detector_setup(&lr->lines[i], lc, i, - flags & GPIO_V2_LINE_EDGE_FLAGS, - flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE); + edflags); if (ret) goto out_free_linereq; } + lr->lines[i].edflags = edflags; + blocking_notifier_call_chain(&desc->gdev->notifier, GPIO_V2_LINE_CHANGED_REQUESTED, desc); -- cgit v1.2.3 From 272ddba004731503cb6c30f2336dd501eaf25955 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Thu, 14 Jul 2022 10:03:19 +0800 Subject: gpiolib: cdev: compile out HTE unless CONFIG_HTE selected The majority of builds do not include HTE, so compile out hte functionality unless CONFIG_HTE is selected. Signed-off-by: Kent Gibson Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 98 +++++++++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 35 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 01c76aa00701..7e4670a286b2 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -468,9 +468,7 @@ struct line { * stale value. */ unsigned int level; - /* - * -- hte specific fields -- - */ +#ifdef CONFIG_HTE struct hte_ts_desc hdesc; /* * HTE provider sets line level at the time of event. The valid @@ -487,6 +485,7 @@ struct line { * last sequence number before debounce period expires. */ u32 last_seqno; +#endif /* CONFIG_HTE */ }; /** @@ -572,7 +571,8 @@ static u64 line_event_timestamp(struct line *line) { if (test_bit(FLAG_EVENT_CLOCK_REALTIME, &line->desc->flags)) return ktime_get_real_ns(); - else if (test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) + else if (IS_ENABLED(CONFIG_HTE) && + test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) return line->timestamp_ns; return ktime_get_ns(); @@ -584,6 +584,8 @@ static u32 line_event_id(int level) GPIO_V2_LINE_EVENT_FALLING_EDGE; } +#ifdef CONFIG_HTE + static enum hte_return process_hw_ts_thread(void *p) { struct line *line; @@ -667,6 +669,42 @@ static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p) return HTE_CB_HANDLED; } +static int hte_edge_setup(struct line *line, u64 eflags) +{ + int ret; + unsigned long flags = 0; + struct hte_ts_desc *hdesc = &line->hdesc; + + if (eflags & GPIO_V2_LINE_FLAG_EDGE_RISING) + flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ? + HTE_FALLING_EDGE_TS : + HTE_RISING_EDGE_TS; + if (eflags & GPIO_V2_LINE_FLAG_EDGE_FALLING) + flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ? + HTE_RISING_EDGE_TS : + HTE_FALLING_EDGE_TS; + + line->total_discard_seq = 0; + + hte_init_line_attr(hdesc, desc_to_gpio(line->desc), flags, NULL, + line->desc); + + ret = hte_ts_get(NULL, hdesc, 0); + if (ret) + return ret; + + return hte_request_ts_ns(hdesc, process_hw_ts, process_hw_ts_thread, + line); +} + +#else + +static int hte_edge_setup(struct line *line, u64 eflags) +{ + return 0; +} +#endif /* CONFIG_HTE */ + static irqreturn_t edge_irq_thread(int irq, void *p) { struct line *line = p; @@ -766,10 +804,13 @@ static void debounce_work_func(struct work_struct *work) struct line *line = container_of(work, struct line, work.work); struct linereq *lr; u64 eflags, edflags = READ_ONCE(line->edflags); - int level = -1, diff_seqno; + int level = -1; +#ifdef CONFIG_HTE + int diff_seqno; if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) level = line->raw_level; +#endif if (level < 0) level = gpiod_get_raw_value_cansleep(line->desc); if (level < 0) { @@ -802,6 +843,7 @@ static void debounce_work_func(struct work_struct *work) lr = line->req; le.timestamp_ns = line_event_timestamp(line); le.offset = gpio_chip_hwgpio(line->desc); +#ifdef CONFIG_HTE if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) { /* discard events except the last one */ line->total_discard_seq -= 1; @@ -811,7 +853,9 @@ static void debounce_work_func(struct work_struct *work) le.line_seqno = line->line_seqno; le.seqno = (lr->num_lines == 1) ? le.line_seqno : atomic_add_return(diff_seqno, &lr->seqno); - } else { + } else +#endif /* CONFIG_HTE */ + { line->line_seqno++; le.line_seqno = line->line_seqno; le.seqno = (lr->num_lines == 1) ? @@ -823,32 +867,6 @@ static void debounce_work_func(struct work_struct *work) linereq_put_event(lr, &le); } -static int hte_edge_setup(struct line *line, u64 eflags) -{ - int ret; - unsigned long flags = 0; - struct hte_ts_desc *hdesc = &line->hdesc; - - if (eflags & GPIO_V2_LINE_FLAG_EDGE_RISING) - flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ? - HTE_FALLING_EDGE_TS : HTE_RISING_EDGE_TS; - if (eflags & GPIO_V2_LINE_FLAG_EDGE_FALLING) - flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ? - HTE_RISING_EDGE_TS : HTE_FALLING_EDGE_TS; - - line->total_discard_seq = 0; - - hte_init_line_attr(hdesc, desc_to_gpio(line->desc), flags, - NULL, line->desc); - - ret = hte_ts_get(NULL, hdesc, 0); - if (ret) - return ret; - - return hte_request_ts_ns(hdesc, process_hw_ts, - process_hw_ts_thread, line); -} - static int debounce_setup(struct line *line, unsigned int debounce_period_us) { unsigned long irqflags; @@ -869,7 +887,8 @@ static int debounce_setup(struct line *line, unsigned int debounce_period_us) if (level < 0) return level; - if (!test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) { + if (!(IS_ENABLED(CONFIG_HTE) && + test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags))) { irq = gpiod_to_irq(line->desc); if (irq < 0) return -ENXIO; @@ -927,8 +946,10 @@ static void edge_detector_stop(struct line *line) line->irq = 0; } +#ifdef CONFIG_HTE if (READ_ONCE(line->edflags) & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) hte_ts_put(&line->hdesc); +#endif cancel_delayed_work_sync(&line->work); WRITE_ONCE(line->sw_debounced, 0); @@ -966,7 +987,8 @@ static int edge_detector_setup(struct line *line, if (!eflags || READ_ONCE(line->sw_debounced)) return 0; - if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) + if (IS_ENABLED(CONFIG_HTE) && + (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)) return hte_edge_setup(line, edflags); irq = gpiod_to_irq(line->desc); @@ -1051,6 +1073,11 @@ static int gpio_v2_line_flags_validate(u64 flags) /* Return an error if an unknown flag is set */ if (flags & ~GPIO_V2_LINE_VALID_FLAGS) return -EINVAL; + + if (!IS_ENABLED(CONFIG_HTE) && + (flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)) + return -EOPNOTSUPP; + /* * Do not allow both INPUT and OUTPUT flags to be set as they are * contradictory. @@ -1060,7 +1087,8 @@ static int gpio_v2_line_flags_validate(u64 flags) return -EINVAL; /* Only allow one event clock source */ - if ((flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME) && + if (IS_ENABLED(CONFIG_HTE) && + (flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME) && (flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)) return -EINVAL; -- cgit v1.2.3 From 10fcd2f180bcbfb3392fcc98670baff81c61bcf2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 Jul 2022 01:02:50 +0300 Subject: gpio: 74xx-mmio: Make use of device properties Convert the module to be property provider agnostic and allow it to be used on non-OF platforms. Add mod_devicetable.h include. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-74xx-mmio.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c index 173e06758e6c..5e3c948ddb73 100644 --- a/drivers/gpio/gpio-74xx-mmio.c +++ b/drivers/gpio/gpio-74xx-mmio.c @@ -6,10 +6,11 @@ */ #include -#include -#include #include +#include +#include #include +#include #define MMIO_74XX_DIR_IN (0 << 8) #define MMIO_74XX_DIR_OUT (1 << 8) @@ -112,7 +113,7 @@ static int mmio_74xx_gpio_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->flags = (uintptr_t)of_device_get_match_data(&pdev->dev); + priv->flags = (uintptr_t)device_get_match_data(&pdev->dev); dat = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(dat)) -- cgit v1.2.3 From d3054ba1db6270e14245efa28dbd1cf10207d4f2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 Jul 2022 01:02:51 +0300 Subject: gpio: 74xx-mmio: Check MMIO_74XX_DIR_IN flag in mmio_74xx_dir_in() It's logically better to check the IN in ->direction_input() and _OUT in ->direction_output(). While at it, replace ternary with plain if-conditional for the sake of consistency with mmio_74xx_dir_out(). Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-74xx-mmio.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c index 5e3c948ddb73..b2cc8a55c257 100644 --- a/drivers/gpio/gpio-74xx-mmio.c +++ b/drivers/gpio/gpio-74xx-mmio.c @@ -88,7 +88,10 @@ static int mmio_74xx_dir_in(struct gpio_chip *gc, unsigned int gpio) { struct mmio_74xx_gpio_priv *priv = gpiochip_get_data(gc); - return (priv->flags & MMIO_74XX_DIR_OUT) ? -ENOTSUPP : 0; + if (priv->flags & MMIO_74XX_DIR_IN) + return 0; + + return -ENOTSUPP; } static int mmio_74xx_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) -- cgit v1.2.3 From 28ba057456412794198ba6c953aa2042147c2de5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 Jul 2022 01:02:52 +0300 Subject: gpio: 74xx-mmio: use bits.h macros for all masks Make use of the GENMASK() (far less error-prone, far more concise). Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-74xx-mmio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c index b2cc8a55c257..cd399898ed12 100644 --- a/drivers/gpio/gpio-74xx-mmio.c +++ b/drivers/gpio/gpio-74xx-mmio.c @@ -5,6 +5,7 @@ * Copyright (C) 2014 Alexander Shiyan */ +#include #include #include #include @@ -14,7 +15,7 @@ #define MMIO_74XX_DIR_IN (0 << 8) #define MMIO_74XX_DIR_OUT (1 << 8) -#define MMIO_74XX_BIT_CNT(x) ((x) & 0xff) +#define MMIO_74XX_BIT_CNT(x) ((x) & GENMASK(7, 0)) struct mmio_74xx_gpio_priv { struct gpio_chip gc; -- cgit v1.2.3 From c269df8c5ad316eac47a9078e7a2339e1956637a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Wed, 13 Jul 2022 15:14:18 +0200 Subject: gpiolib: add support for bias pull disable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change prepares the gpio core to look at firmware flags and set 'FLAG_BIAS_DISABLE' if necessary. It works in similar way to 'GPIO_PULL_DOWN' and 'GPIO_PULL_UP'. Signed-off-by: Nuno Sá Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 8 ++++++-- include/linux/gpio/machine.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 9535f48e18d1..0692ec84d3b0 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3945,9 +3945,11 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, if (lflags & GPIO_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); - if ((lflags & GPIO_PULL_UP) && (lflags & GPIO_PULL_DOWN)) { + if (((lflags & GPIO_PULL_UP) && (lflags & GPIO_PULL_DOWN)) || + ((lflags & GPIO_PULL_UP) && (lflags & GPIO_PULL_DISABLE)) || + ((lflags & GPIO_PULL_DOWN) && (lflags & GPIO_PULL_DISABLE))) { gpiod_err(desc, - "both pull-up and pull-down enabled, invalid configuration\n"); + "multiple pull-up, pull-down or pull-disable enabled, invalid configuration\n"); return -EINVAL; } @@ -3955,6 +3957,8 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, set_bit(FLAG_PULL_UP, &desc->flags); else if (lflags & GPIO_PULL_DOWN) set_bit(FLAG_PULL_DOWN, &desc->flags); + else if (lflags & GPIO_PULL_DISABLE) + set_bit(FLAG_BIAS_DISABLE, &desc->flags); ret = gpiod_set_transitory(desc, (lflags & GPIO_TRANSITORY)); if (ret < 0) diff --git a/include/linux/gpio/machine.h b/include/linux/gpio/machine.h index 4d55da28e664..0b619eb7ae83 100644 --- a/include/linux/gpio/machine.h +++ b/include/linux/gpio/machine.h @@ -14,6 +14,7 @@ enum gpio_lookup_flags { GPIO_TRANSITORY = (1 << 3), GPIO_PULL_UP = (1 << 4), GPIO_PULL_DOWN = (1 << 5), + GPIO_PULL_DISABLE = (1 << 6), GPIO_LOOKUP_FLAGS_DEFAULT = GPIO_ACTIVE_HIGH | GPIO_PERSISTENT, }; -- cgit v1.2.3 From 31bea23119cda87088c6bd4085a1e442c6c5974c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Wed, 13 Jul 2022 15:14:19 +0200 Subject: gpiolib: of: support bias pull disable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On top of looking at PULL_UP and PULL_DOWN flags, also look at PULL_DISABLE and set the appropriate GPIO flag. The GPIO core will then pass down this to controllers that support it. Signed-off-by: Nuno Sá Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-of.c | 7 +++++++ include/linux/of_gpio.h | 1 + 2 files changed, 8 insertions(+) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index f80307be37d5..a037b50bef33 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -354,6 +354,9 @@ struct gpio_desc *gpiod_get_from_of_node(const struct device_node *node, if (flags & OF_GPIO_PULL_DOWN) lflags |= GPIO_PULL_DOWN; + if (flags & OF_GPIO_PULL_DISABLE) + lflags |= GPIO_PULL_DISABLE; + ret = gpiod_configure_flags(desc, propname, lflags, dflags); if (ret < 0) { gpiod_put(desc); @@ -556,6 +559,8 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, *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; return desc; } @@ -621,6 +626,8 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np, *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; if (of_property_read_bool(np, "input")) *dflags |= GPIOD_IN; diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h index 8bf2ea859653..a5166eb93437 100644 --- a/include/linux/of_gpio.h +++ b/include/linux/of_gpio.h @@ -29,6 +29,7 @@ enum of_gpio_flags { OF_GPIO_TRANSITORY = 0x8, OF_GPIO_PULL_UP = 0x10, OF_GPIO_PULL_DOWN = 0x20, + OF_GPIO_PULL_DISABLE = 0x40, }; #ifdef CONFIG_OF_GPIO -- cgit v1.2.3 From 6fd03f0248281b690e168a5a5c22cb3c98b96326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Wed, 13 Jul 2022 15:14:20 +0200 Subject: gpiolib: acpi: support bias pull disable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On top of looking at PULL_UP and PULL_DOWN flags, also look at PULL_DISABLE and set the appropriate GPIO flag. The GPIO core will then pass down this to controllers that support it. Signed-off-by: Nuno Sá Acked-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-acpi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index c2523ac26fac..9be1376f9a62 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -687,6 +687,9 @@ int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags, case ACPI_PIN_CONFIG_PULLDOWN: *lookupflags |= GPIO_PULL_DOWN; break; + case ACPI_PIN_CONFIG_NOPULL: + *lookupflags |= GPIO_PULL_DISABLE; + break; default: break; } -- cgit v1.2.3 From d042656a2170ce1b0d1a72d81a99bc93f0170358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Wed, 13 Jul 2022 15:14:21 +0200 Subject: dt-bindings: gpio: add pull-disable flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This extends the flags that can be used in GPIO specifiers to indicate that no bias is intended in the pin. Signed-off-by: Nuno Sá Reviewed-by: Linus Walleij Acked-by: Rob Herring Signed-off-by: Bartosz Golaszewski --- include/dt-bindings/gpio/gpio.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/dt-bindings/gpio/gpio.h b/include/dt-bindings/gpio/gpio.h index c029467e828b..5566e58196a2 100644 --- a/include/dt-bindings/gpio/gpio.h +++ b/include/dt-bindings/gpio/gpio.h @@ -39,4 +39,7 @@ /* Bit 5 express pull down */ #define GPIO_PULL_DOWN 32 +/* Bit 6 express pull disable */ +#define GPIO_PULL_DISABLE 64 + #endif -- cgit v1.2.3 From c4371c56397340134c1827ab70dbf817dbb1b99b Mon Sep 17 00:00:00 2001 From: Thomas Bogendoerfer Date: Fri, 15 Jul 2022 16:19:59 +0200 Subject: gpio: remove VR41XX related gpio driver Commit d3164e2f3b0a ("MIPS: Remove VR41xx support") removed support for MIPS VR41xx platform, so remove exclusive drivers for this platform, too. Signed-off-by: Thomas Bogendoerfer Acked-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 6 - drivers/gpio/Makefile | 1 - drivers/gpio/gpio-vr41xx.c | 541 --------------------------------------------- 3 files changed, 548 deletions(-) delete mode 100644 drivers/gpio/gpio-vr41xx.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 1d9316cecacd..cf6a2c745f3f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -691,12 +691,6 @@ config GPIO_VISCONTI help Say yes here to support GPIO on Tohisba Visconti. -config GPIO_VR41XX - tristate "NEC VR4100 series General-purpose I/O Unit support" - depends on CPU_VR41XX - help - Say yes here to support the NEC VR4100 series General-purpose I/O Unit. - config GPIO_VX855 tristate "VIA VX855/VX875 GPIO" depends on (X86 || COMPILE_TEST) && PCI diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 14352f6dfe8e..9d4805b2b60b 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -169,7 +169,6 @@ obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o obj-$(CONFIG_GPIO_VIRTIO) += gpio-virtio.o obj-$(CONFIG_GPIO_VISCONTI) += gpio-visconti.o -obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o diff --git a/drivers/gpio/gpio-vr41xx.c b/drivers/gpio/gpio-vr41xx.c deleted file mode 100644 index 8d09b619c166..000000000000 --- a/drivers/gpio/gpio-vr41xx.c +++ /dev/null @@ -1,541 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Driver for NEC VR4100 series General-purpose I/O Unit. - * - * Copyright (C) 2002 MontaVista Software Inc. - * Author: Yoichi Yuasa - * Copyright (C) 2003-2009 Yoichi Yuasa - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -MODULE_AUTHOR("Yoichi Yuasa "); -MODULE_DESCRIPTION("NEC VR4100 series General-purpose I/O Unit driver"); -MODULE_LICENSE("GPL"); - -#define GIUIOSELL 0x00 -#define GIUIOSELH 0x02 -#define GIUPIODL 0x04 -#define GIUPIODH 0x06 -#define GIUINTSTATL 0x08 -#define GIUINTSTATH 0x0a -#define GIUINTENL 0x0c -#define GIUINTENH 0x0e -#define GIUINTTYPL 0x10 -#define GIUINTTYPH 0x12 -#define GIUINTALSELL 0x14 -#define GIUINTALSELH 0x16 -#define GIUINTHTSELL 0x18 -#define GIUINTHTSELH 0x1a -#define GIUPODATL 0x1c -#define GIUPODATEN 0x1c -#define GIUPODATH 0x1e - #define PIOEN0 0x0100 - #define PIOEN1 0x0200 -#define GIUPODAT 0x1e -#define GIUFEDGEINHL 0x20 -#define GIUFEDGEINHH 0x22 -#define GIUREDGEINHL 0x24 -#define GIUREDGEINHH 0x26 - -#define GIUUSEUPDN 0x1e0 -#define GIUTERMUPDN 0x1e2 - -#define GPIO_HAS_PULLUPDOWN_IO 0x0001 -#define GPIO_HAS_OUTPUT_ENABLE 0x0002 -#define GPIO_HAS_INTERRUPT_EDGE_SELECT 0x0100 - -enum { - GPIO_INPUT, - GPIO_OUTPUT, -}; - -static DEFINE_SPINLOCK(giu_lock); -static unsigned long giu_flags; - -static void __iomem *giu_base; -static struct gpio_chip vr41xx_gpio_chip; - -#define giu_read(offset) readw(giu_base + (offset)) -#define giu_write(offset, value) writew((value), giu_base + (offset)) - -#define GPIO_PIN_OF_IRQ(irq) ((irq) - GIU_IRQ_BASE) -#define GIUINT_HIGH_OFFSET 16 -#define GIUINT_HIGH_MAX 32 - -static inline u16 giu_set(u16 offset, u16 set) -{ - u16 data; - - data = giu_read(offset); - data |= set; - giu_write(offset, data); - - return data; -} - -static inline u16 giu_clear(u16 offset, u16 clear) -{ - u16 data; - - data = giu_read(offset); - data &= ~clear; - giu_write(offset, data); - - return data; -} - -static void ack_giuint_low(struct irq_data *d) -{ - giu_write(GIUINTSTATL, 1 << GPIO_PIN_OF_IRQ(d->irq)); -} - -static void mask_giuint_low(struct irq_data *d) -{ - giu_clear(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(d->irq)); -} - -static void mask_ack_giuint_low(struct irq_data *d) -{ - unsigned int pin; - - pin = GPIO_PIN_OF_IRQ(d->irq); - giu_clear(GIUINTENL, 1 << pin); - giu_write(GIUINTSTATL, 1 << pin); -} - -static void unmask_giuint_low(struct irq_data *d) -{ - giu_set(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(d->irq)); -} - -static unsigned int startup_giuint(struct irq_data *data) -{ - int ret; - - ret = gpiochip_lock_as_irq(&vr41xx_gpio_chip, irqd_to_hwirq(data)); - if (ret) { - dev_err(vr41xx_gpio_chip.parent, - "unable to lock HW IRQ %lu for IRQ\n", - data->hwirq); - return ret; - } - - /* Satisfy the .enable semantics by unmasking the line */ - unmask_giuint_low(data); - return 0; -} - -static void shutdown_giuint(struct irq_data *data) -{ - mask_giuint_low(data); - gpiochip_unlock_as_irq(&vr41xx_gpio_chip, data->hwirq); -} - -static struct irq_chip giuint_low_irq_chip = { - .name = "GIUINTL", - .irq_ack = ack_giuint_low, - .irq_mask = mask_giuint_low, - .irq_mask_ack = mask_ack_giuint_low, - .irq_unmask = unmask_giuint_low, - .irq_startup = startup_giuint, - .irq_shutdown = shutdown_giuint, -}; - -static void ack_giuint_high(struct irq_data *d) -{ - giu_write(GIUINTSTATH, - 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET)); -} - -static void mask_giuint_high(struct irq_data *d) -{ - giu_clear(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET)); -} - -static void mask_ack_giuint_high(struct irq_data *d) -{ - unsigned int pin; - - pin = GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET; - giu_clear(GIUINTENH, 1 << pin); - giu_write(GIUINTSTATH, 1 << pin); -} - -static void unmask_giuint_high(struct irq_data *d) -{ - giu_set(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET)); -} - -static struct irq_chip giuint_high_irq_chip = { - .name = "GIUINTH", - .irq_ack = ack_giuint_high, - .irq_mask = mask_giuint_high, - .irq_mask_ack = mask_ack_giuint_high, - .irq_unmask = unmask_giuint_high, -}; - -static int giu_get_irq(unsigned int irq) -{ - u16 pendl, pendh, maskl, maskh; - int i; - - pendl = giu_read(GIUINTSTATL); - pendh = giu_read(GIUINTSTATH); - maskl = giu_read(GIUINTENL); - maskh = giu_read(GIUINTENH); - - maskl &= pendl; - maskh &= pendh; - - if (maskl) { - for (i = 0; i < 16; i++) { - if (maskl & (1 << i)) - return GIU_IRQ(i); - } - } else if (maskh) { - for (i = 0; i < 16; i++) { - if (maskh & (1 << i)) - return GIU_IRQ(i + GIUINT_HIGH_OFFSET); - } - } - - printk(KERN_ERR "spurious GIU interrupt: %04x(%04x),%04x(%04x)\n", - maskl, pendl, maskh, pendh); - - return -EINVAL; -} - -void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger, - irq_signal_t signal) -{ - u16 mask; - - if (pin < GIUINT_HIGH_OFFSET) { - mask = 1 << pin; - if (trigger != IRQ_TRIGGER_LEVEL) { - giu_set(GIUINTTYPL, mask); - if (signal == IRQ_SIGNAL_HOLD) - giu_set(GIUINTHTSELL, mask); - else - giu_clear(GIUINTHTSELL, mask); - if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) { - switch (trigger) { - case IRQ_TRIGGER_EDGE_FALLING: - giu_set(GIUFEDGEINHL, mask); - giu_clear(GIUREDGEINHL, mask); - break; - case IRQ_TRIGGER_EDGE_RISING: - giu_clear(GIUFEDGEINHL, mask); - giu_set(GIUREDGEINHL, mask); - break; - default: - giu_set(GIUFEDGEINHL, mask); - giu_set(GIUREDGEINHL, mask); - break; - } - } - irq_set_chip_and_handler(GIU_IRQ(pin), - &giuint_low_irq_chip, - handle_edge_irq); - } else { - giu_clear(GIUINTTYPL, mask); - giu_clear(GIUINTHTSELL, mask); - irq_set_chip_and_handler(GIU_IRQ(pin), - &giuint_low_irq_chip, - handle_level_irq); - } - giu_write(GIUINTSTATL, mask); - } else if (pin < GIUINT_HIGH_MAX) { - mask = 1 << (pin - GIUINT_HIGH_OFFSET); - if (trigger != IRQ_TRIGGER_LEVEL) { - giu_set(GIUINTTYPH, mask); - if (signal == IRQ_SIGNAL_HOLD) - giu_set(GIUINTHTSELH, mask); - else - giu_clear(GIUINTHTSELH, mask); - if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) { - switch (trigger) { - case IRQ_TRIGGER_EDGE_FALLING: - giu_set(GIUFEDGEINHH, mask); - giu_clear(GIUREDGEINHH, mask); - break; - case IRQ_TRIGGER_EDGE_RISING: - giu_clear(GIUFEDGEINHH, mask); - giu_set(GIUREDGEINHH, mask); - break; - default: - giu_set(GIUFEDGEINHH, mask); - giu_set(GIUREDGEINHH, mask); - break; - } - } - irq_set_chip_and_handler(GIU_IRQ(pin), - &giuint_high_irq_chip, - handle_edge_irq); - } else { - giu_clear(GIUINTTYPH, mask); - giu_clear(GIUINTHTSELH, mask); - irq_set_chip_and_handler(GIU_IRQ(pin), - &giuint_high_irq_chip, - handle_level_irq); - } - giu_write(GIUINTSTATH, mask); - } -} -EXPORT_SYMBOL_GPL(vr41xx_set_irq_trigger); - -void vr41xx_set_irq_level(unsigned int pin, irq_level_t level) -{ - u16 mask; - - if (pin < GIUINT_HIGH_OFFSET) { - mask = 1 << pin; - if (level == IRQ_LEVEL_HIGH) - giu_set(GIUINTALSELL, mask); - else - giu_clear(GIUINTALSELL, mask); - giu_write(GIUINTSTATL, mask); - } else if (pin < GIUINT_HIGH_MAX) { - mask = 1 << (pin - GIUINT_HIGH_OFFSET); - if (level == IRQ_LEVEL_HIGH) - giu_set(GIUINTALSELH, mask); - else - giu_clear(GIUINTALSELH, mask); - giu_write(GIUINTSTATH, mask); - } -} -EXPORT_SYMBOL_GPL(vr41xx_set_irq_level); - -static int giu_set_direction(struct gpio_chip *chip, unsigned pin, int dir) -{ - u16 offset, mask, reg; - unsigned long flags; - - if (pin >= chip->ngpio) - return -EINVAL; - - if (pin < 16) { - offset = GIUIOSELL; - mask = 1 << pin; - } else if (pin < 32) { - offset = GIUIOSELH; - mask = 1 << (pin - 16); - } else { - if (giu_flags & GPIO_HAS_OUTPUT_ENABLE) { - offset = GIUPODATEN; - mask = 1 << (pin - 32); - } else { - switch (pin) { - case 48: - offset = GIUPODATH; - mask = PIOEN0; - break; - case 49: - offset = GIUPODATH; - mask = PIOEN1; - break; - default: - return -EINVAL; - } - } - } - - spin_lock_irqsave(&giu_lock, flags); - - reg = giu_read(offset); - if (dir == GPIO_OUTPUT) - reg |= mask; - else - reg &= ~mask; - giu_write(offset, reg); - - spin_unlock_irqrestore(&giu_lock, flags); - - return 0; -} - -static int vr41xx_gpio_get(struct gpio_chip *chip, unsigned pin) -{ - u16 reg, mask; - - if (pin >= chip->ngpio) - return -EINVAL; - - if (pin < 16) { - reg = giu_read(GIUPIODL); - mask = 1 << pin; - } else if (pin < 32) { - reg = giu_read(GIUPIODH); - mask = 1 << (pin - 16); - } else if (pin < 48) { - reg = giu_read(GIUPODATL); - mask = 1 << (pin - 32); - } else { - reg = giu_read(GIUPODATH); - mask = 1 << (pin - 48); - } - - if (reg & mask) - return 1; - - return 0; -} - -static void vr41xx_gpio_set(struct gpio_chip *chip, unsigned pin, - int value) -{ - u16 offset, mask, reg; - unsigned long flags; - - if (pin >= chip->ngpio) - return; - - if (pin < 16) { - offset = GIUPIODL; - mask = 1 << pin; - } else if (pin < 32) { - offset = GIUPIODH; - mask = 1 << (pin - 16); - } else if (pin < 48) { - offset = GIUPODATL; - mask = 1 << (pin - 32); - } else { - offset = GIUPODATH; - mask = 1 << (pin - 48); - } - - spin_lock_irqsave(&giu_lock, flags); - - reg = giu_read(offset); - if (value) - reg |= mask; - else - reg &= ~mask; - giu_write(offset, reg); - - spin_unlock_irqrestore(&giu_lock, flags); -} - - -static int vr41xx_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - return giu_set_direction(chip, offset, GPIO_INPUT); -} - -static int vr41xx_gpio_direction_output(struct gpio_chip *chip, unsigned offset, - int value) -{ - vr41xx_gpio_set(chip, offset, value); - - return giu_set_direction(chip, offset, GPIO_OUTPUT); -} - -static int vr41xx_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - if (offset >= chip->ngpio) - return -EINVAL; - - return GIU_IRQ_BASE + offset; -} - -static struct gpio_chip vr41xx_gpio_chip = { - .label = "vr41xx", - .owner = THIS_MODULE, - .direction_input = vr41xx_gpio_direction_input, - .get = vr41xx_gpio_get, - .direction_output = vr41xx_gpio_direction_output, - .set = vr41xx_gpio_set, - .to_irq = vr41xx_gpio_to_irq, -}; - -static int giu_probe(struct platform_device *pdev) -{ - unsigned int trigger, i, pin; - struct irq_chip *chip; - int irq; - - switch (pdev->id) { - case GPIO_50PINS_PULLUPDOWN: - giu_flags = GPIO_HAS_PULLUPDOWN_IO; - vr41xx_gpio_chip.ngpio = 50; - break; - case GPIO_36PINS: - vr41xx_gpio_chip.ngpio = 36; - break; - case GPIO_48PINS_EDGE_SELECT: - giu_flags = GPIO_HAS_INTERRUPT_EDGE_SELECT; - vr41xx_gpio_chip.ngpio = 48; - break; - default: - dev_err(&pdev->dev, "GIU: unknown ID %d\n", pdev->id); - return -ENODEV; - } - - giu_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(giu_base)) - return PTR_ERR(giu_base); - - vr41xx_gpio_chip.parent = &pdev->dev; - - if (gpiochip_add_data(&vr41xx_gpio_chip, NULL)) - return -ENODEV; - - giu_write(GIUINTENL, 0); - giu_write(GIUINTENH, 0); - - trigger = giu_read(GIUINTTYPH) << 16; - trigger |= giu_read(GIUINTTYPL); - for (i = GIU_IRQ_BASE; i <= GIU_IRQ_LAST; i++) { - pin = GPIO_PIN_OF_IRQ(i); - if (pin < GIUINT_HIGH_OFFSET) - chip = &giuint_low_irq_chip; - else - chip = &giuint_high_irq_chip; - - if (trigger & (1 << pin)) - irq_set_chip_and_handler(i, chip, handle_edge_irq); - else - irq_set_chip_and_handler(i, chip, handle_level_irq); - - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0 || irq >= nr_irqs) - return -EBUSY; - - return cascade_irq(irq, giu_get_irq); -} - -static int giu_remove(struct platform_device *pdev) -{ - if (giu_base) { - giu_base = NULL; - } - - return 0; -} - -static struct platform_driver giu_device_driver = { - .probe = giu_probe, - .remove = giu_remove, - .driver = { - .name = "GIU", - }, -}; - -module_platform_driver(giu_device_driver); -- cgit v1.2.3 From 2c05a0f29f41cbb1e31e94a53e7dacf0622706bb Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 20 Jul 2022 09:45:57 -0400 Subject: gpio: ws16c48: Implement and utilize register structures Reduce magic numbers and improve code readability by implementing and utilizing named register data structures. Reviewed-by: Linus Walleij Cc: Paul Demetrotion Signed-off-by: William Breathitt Gray Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ws16c48.c | 120 +++++++++++++++++++++++++++++++------------- 1 file changed, 84 insertions(+), 36 deletions(-) diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c index 5078631d8014..b098f2dc196b 100644 --- a/drivers/gpio/gpio-ws16c48.c +++ b/drivers/gpio/gpio-ws16c48.c @@ -4,7 +4,6 @@ * Copyright (C) 2016 William Breathitt Gray */ #include -#include #include #include #include @@ -17,8 +16,9 @@ #include #include #include +#include -#define WS16C48_EXTENT 16 +#define WS16C48_EXTENT 10 #define MAX_NUM_WS16C48 max_num_isa_dev(WS16C48_EXTENT) static unsigned int base[MAX_NUM_WS16C48]; @@ -30,6 +30,20 @@ static unsigned int irq[MAX_NUM_WS16C48]; module_param_hw_array(irq, uint, irq, NULL, 0); MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers"); +/** + * struct ws16c48_reg - device register structure + * @port: Port 0 through 5 I/O + * @int_pending: Interrupt Pending + * @page_lock: Register page (Bits 7-6) and I/O port lock (Bits 5-0) + * @pol_enab_int_id: Interrupt polarity, enable, and ID + */ +struct ws16c48_reg { + u8 port[6]; + u8 int_pending; + u8 page_lock; + u8 pol_enab_int_id[3]; +}; + /** * struct ws16c48_gpio - GPIO device private data structure * @chip: instance of the gpio_chip @@ -38,7 +52,7 @@ MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers"); * @lock: synchronization lock to prevent I/O race conditions * @irq_mask: I/O bits affected by interrupts * @flow_mask: IRQ flow type mask for the respective I/O bits - * @base: base port address of the GPIO device + * @reg: I/O address offset for the device registers */ struct ws16c48_gpio { struct gpio_chip chip; @@ -47,7 +61,7 @@ struct ws16c48_gpio { raw_spinlock_t lock; unsigned long irq_mask; unsigned long flow_mask; - void __iomem *base; + struct ws16c48_reg __iomem *reg; }; static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset) @@ -73,7 +87,7 @@ static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset) ws16c48gpio->io_state[port] |= mask; ws16c48gpio->out_state[port] &= ~mask; - iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->base + port); + iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); @@ -95,7 +109,7 @@ static int ws16c48_gpio_direction_output(struct gpio_chip *chip, ws16c48gpio->out_state[port] |= mask; else ws16c48gpio->out_state[port] &= ~mask; - iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->base + port); + iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); @@ -118,7 +132,7 @@ static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset) return -EINVAL; } - port_state = ioread8(ws16c48gpio->base + port); + port_state = ioread8(ws16c48gpio->reg->port + port); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); @@ -131,14 +145,16 @@ static int ws16c48_gpio_get_multiple(struct gpio_chip *chip, struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); unsigned long offset; unsigned long gpio_mask; - void __iomem *port_addr; + size_t index; + u8 __iomem *port_addr; unsigned long port_state; /* clear bits array to a clean slate */ bitmap_zero(bits, chip->ngpio); for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) { - port_addr = ws16c48gpio->base + offset / 8; + index = offset / 8; + port_addr = ws16c48gpio->reg->port + index; port_state = ioread8(port_addr) & gpio_mask; bitmap_set_value8(bits, port_state, offset); @@ -166,7 +182,7 @@ static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value) ws16c48gpio->out_state[port] |= mask; else ws16c48gpio->out_state[port] &= ~mask; - iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->base + port); + iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); } @@ -178,13 +194,13 @@ static void ws16c48_gpio_set_multiple(struct gpio_chip *chip, unsigned long offset; unsigned long gpio_mask; size_t index; - void __iomem *port_addr; + u8 __iomem *port_addr; unsigned long bitmask; unsigned long flags; for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) { index = offset / 8; - port_addr = ws16c48gpio->base + index; + port_addr = ws16c48gpio->reg->port + index; /* mask out GPIO configured for input */ gpio_mask &= ~ws16c48gpio->io_state[index]; @@ -219,10 +235,15 @@ static void ws16c48_irq_ack(struct irq_data *data) port_state = ws16c48gpio->irq_mask >> (8*port); - iowrite8(0x80, ws16c48gpio->base + 7); - iowrite8(port_state & ~mask, ws16c48gpio->base + 8 + port); - iowrite8(port_state | mask, ws16c48gpio->base + 8 + port); - iowrite8(0xC0, ws16c48gpio->base + 7); + /* Select Register Page 2; Unlock all I/O ports */ + iowrite8(0x80, &ws16c48gpio->reg->page_lock); + + /* Clear pending interrupt */ + iowrite8(port_state & ~mask, ws16c48gpio->reg->pol_enab_int_id + port); + iowrite8(port_state | mask, ws16c48gpio->reg->pol_enab_int_id + port); + + /* Select Register Page 3; Unlock all I/O ports */ + iowrite8(0xC0, &ws16c48gpio->reg->page_lock); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); } @@ -235,6 +256,7 @@ static void ws16c48_irq_mask(struct irq_data *data) const unsigned long mask = BIT(offset); const unsigned port = offset / 8; unsigned long flags; + unsigned long port_state; /* only the first 3 ports support interrupts */ if (port > 2) @@ -243,10 +265,16 @@ static void ws16c48_irq_mask(struct irq_data *data) raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); ws16c48gpio->irq_mask &= ~mask; + port_state = ws16c48gpio->irq_mask >> (8 * port); + + /* Select Register Page 2; Unlock all I/O ports */ + iowrite8(0x80, &ws16c48gpio->reg->page_lock); - iowrite8(0x80, ws16c48gpio->base + 7); - iowrite8(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port); - iowrite8(0xC0, ws16c48gpio->base + 7); + /* Disable interrupt */ + iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port); + + /* Select Register Page 3; Unlock all I/O ports */ + iowrite8(0xC0, &ws16c48gpio->reg->page_lock); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); } @@ -259,6 +287,7 @@ static void ws16c48_irq_unmask(struct irq_data *data) const unsigned long mask = BIT(offset); const unsigned port = offset / 8; unsigned long flags; + unsigned long port_state; /* only the first 3 ports support interrupts */ if (port > 2) @@ -267,10 +296,16 @@ static void ws16c48_irq_unmask(struct irq_data *data) raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); ws16c48gpio->irq_mask |= mask; + port_state = ws16c48gpio->irq_mask >> (8 * port); + + /* Select Register Page 2; Unlock all I/O ports */ + iowrite8(0x80, &ws16c48gpio->reg->page_lock); - iowrite8(0x80, ws16c48gpio->base + 7); - iowrite8(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port); - iowrite8(0xC0, ws16c48gpio->base + 7); + /* Enable interrupt */ + iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port); + + /* Select Register Page 3; Unlock all I/O ports */ + iowrite8(0xC0, &ws16c48gpio->reg->page_lock); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); } @@ -283,6 +318,7 @@ static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type) const unsigned long mask = BIT(offset); const unsigned port = offset / 8; unsigned long flags; + unsigned long port_state; /* only the first 3 ports support interrupts */ if (port > 2) @@ -304,9 +340,16 @@ static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type) return -EINVAL; } - iowrite8(0x40, ws16c48gpio->base + 7); - iowrite8(ws16c48gpio->flow_mask >> (8*port), ws16c48gpio->base + 8 + port); - iowrite8(0xC0, ws16c48gpio->base + 7); + port_state = ws16c48gpio->flow_mask >> (8 * port); + + /* Select Register Page 1; Unlock all I/O ports */ + iowrite8(0x40, &ws16c48gpio->reg->page_lock); + + /* Set interrupt polarity */ + iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port); + + /* Select Register Page 3; Unlock all I/O ports */ + iowrite8(0xC0, &ws16c48gpio->reg->page_lock); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); @@ -325,25 +368,26 @@ static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id) { struct ws16c48_gpio *const ws16c48gpio = dev_id; struct gpio_chip *const chip = &ws16c48gpio->chip; + struct ws16c48_reg __iomem *const reg = ws16c48gpio->reg; unsigned long int_pending; unsigned long port; unsigned long int_id; unsigned long gpio; - int_pending = ioread8(ws16c48gpio->base + 6) & 0x7; + int_pending = ioread8(®->int_pending) & 0x7; if (!int_pending) return IRQ_NONE; /* loop until all pending interrupts are handled */ do { for_each_set_bit(port, &int_pending, 3) { - int_id = ioread8(ws16c48gpio->base + 8 + port); + int_id = ioread8(reg->pol_enab_int_id + port); for_each_set_bit(gpio, &int_id, 8) generic_handle_domain_irq(chip->irq.domain, gpio + 8*port); } - int_pending = ioread8(ws16c48gpio->base + 6) & 0x7; + int_pending = ioread8(®->int_pending) & 0x7; } while (int_pending); return IRQ_HANDLED; @@ -369,12 +413,16 @@ static int ws16c48_irq_init_hw(struct gpio_chip *gc) { struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(gc); - /* Disable IRQ by default */ - iowrite8(0x80, ws16c48gpio->base + 7); - iowrite8(0, ws16c48gpio->base + 8); - iowrite8(0, ws16c48gpio->base + 9); - iowrite8(0, ws16c48gpio->base + 10); - iowrite8(0xC0, ws16c48gpio->base + 7); + /* Select Register Page 2; Unlock all I/O ports */ + iowrite8(0x80, &ws16c48gpio->reg->page_lock); + + /* Disable interrupts for all lines */ + iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[0]); + iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[1]); + iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[2]); + + /* Select Register Page 3; Unlock all I/O ports */ + iowrite8(0xC0, &ws16c48gpio->reg->page_lock); return 0; } @@ -396,8 +444,8 @@ static int ws16c48_probe(struct device *dev, unsigned int id) return -EBUSY; } - ws16c48gpio->base = devm_ioport_map(dev, base[id], WS16C48_EXTENT); - if (!ws16c48gpio->base) + ws16c48gpio->reg = devm_ioport_map(dev, base[id], WS16C48_EXTENT); + if (!ws16c48gpio->reg) return -ENOMEM; ws16c48gpio->chip.label = name; -- cgit v1.2.3 From cc442e4db9cb851f18cd3ea0c933e016b29fafc3 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 20 Jul 2022 09:45:58 -0400 Subject: gpio: 104-idio-16: Implement and utilize register structures Reduce magic numbers and improve code readability by implementing and utilizing named register data structures. Tested-by: Fred Eckert Reviewed-by: Linus Walleij Cc: John Hentges Cc: Jay Dolan Signed-off-by: William Breathitt Gray Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-104-idio-16.c | 60 ++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c index 45f7ad8573e1..65a5f581d981 100644 --- a/drivers/gpio/gpio-104-idio-16.c +++ b/drivers/gpio/gpio-104-idio-16.c @@ -6,7 +6,7 @@ * This driver supports the following ACCES devices: 104-IDIO-16, * 104-IDIO-16E, 104-IDO-16, 104-IDIO-8, 104-IDIO-8E, and 104-IDO-8. */ -#include +#include #include #include #include @@ -19,6 +19,7 @@ #include #include #include +#include #define IDIO_16_EXTENT 8 #define MAX_NUM_IDIO_16 max_num_isa_dev(IDIO_16_EXTENT) @@ -32,19 +33,42 @@ static unsigned int irq[MAX_NUM_IDIO_16]; module_param_hw_array(irq, uint, irq, NULL, 0); MODULE_PARM_DESC(irq, "ACCES 104-IDIO-16 interrupt line numbers"); +/** + * struct idio_16_reg - device registers structure + * @out0_7: Read: N/A + * Write: FET Drive Outputs 0-7 + * @in0_7: Read: Isolated Inputs 0-7 + * Write: Clear Interrupt + * @irq_ctl: Read: Enable IRQ + * Write: Disable IRQ + * @unused: N/A + * @out8_15: Read: N/A + * Write: FET Drive Outputs 8-15 + * @in8_15: Read: Isolated Inputs 8-15 + * Write: N/A + */ +struct idio_16_reg { + u8 out0_7; + u8 in0_7; + u8 irq_ctl; + u8 unused; + u8 out8_15; + u8 in8_15; +}; + /** * struct idio_16_gpio - GPIO device private data structure * @chip: instance of the gpio_chip * @lock: synchronization lock to prevent I/O race conditions * @irq_mask: I/O bits affected by interrupts - * @base: base port address of the GPIO device + * @reg: I/O address offset for the device registers * @out_state: output bits state */ struct idio_16_gpio { struct gpio_chip chip; raw_spinlock_t lock; unsigned long irq_mask; - void __iomem *base; + struct idio_16_reg __iomem *reg; unsigned int out_state; }; @@ -79,9 +103,9 @@ static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset) return -EINVAL; if (offset < 24) - return !!(ioread8(idio16gpio->base + 1) & mask); + return !!(ioread8(&idio16gpio->reg->in0_7) & mask); - return !!(ioread8(idio16gpio->base + 5) & (mask>>8)); + return !!(ioread8(&idio16gpio->reg->in8_15) & (mask>>8)); } static int idio_16_gpio_get_multiple(struct gpio_chip *chip, @@ -91,9 +115,9 @@ static int idio_16_gpio_get_multiple(struct gpio_chip *chip, *bits = 0; if (*mask & GENMASK(23, 16)) - *bits |= (unsigned long)ioread8(idio16gpio->base + 1) << 16; + *bits |= (unsigned long)ioread8(&idio16gpio->reg->in0_7) << 16; if (*mask & GENMASK(31, 24)) - *bits |= (unsigned long)ioread8(idio16gpio->base + 5) << 24; + *bits |= (unsigned long)ioread8(&idio16gpio->reg->in8_15) << 24; return 0; } @@ -116,9 +140,9 @@ static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset, idio16gpio->out_state &= ~mask; if (offset > 7) - iowrite8(idio16gpio->out_state >> 8, idio16gpio->base + 4); + iowrite8(idio16gpio->out_state >> 8, &idio16gpio->reg->out8_15); else - iowrite8(idio16gpio->out_state, idio16gpio->base); + iowrite8(idio16gpio->out_state, &idio16gpio->reg->out0_7); raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); } @@ -135,9 +159,9 @@ static void idio_16_gpio_set_multiple(struct gpio_chip *chip, idio16gpio->out_state |= *mask & *bits; if (*mask & 0xFF) - iowrite8(idio16gpio->out_state, idio16gpio->base); + iowrite8(idio16gpio->out_state, &idio16gpio->reg->out0_7); if ((*mask >> 8) & 0xFF) - iowrite8(idio16gpio->out_state >> 8, idio16gpio->base + 4); + iowrite8(idio16gpio->out_state >> 8, &idio16gpio->reg->out8_15); raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); } @@ -158,7 +182,7 @@ static void idio_16_irq_mask(struct irq_data *data) if (!idio16gpio->irq_mask) { raw_spin_lock_irqsave(&idio16gpio->lock, flags); - iowrite8(0, idio16gpio->base + 2); + iowrite8(0, &idio16gpio->reg->irq_ctl); raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); } @@ -177,7 +201,7 @@ static void idio_16_irq_unmask(struct irq_data *data) if (!prev_irq_mask) { raw_spin_lock_irqsave(&idio16gpio->lock, flags); - ioread8(idio16gpio->base + 2); + ioread8(&idio16gpio->reg->irq_ctl); raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); } @@ -212,7 +236,7 @@ static irqreturn_t idio_16_irq_handler(int irq, void *dev_id) raw_spin_lock(&idio16gpio->lock); - iowrite8(0, idio16gpio->base + 1); + iowrite8(0, &idio16gpio->reg->in0_7); raw_spin_unlock(&idio16gpio->lock); @@ -232,8 +256,8 @@ static int idio_16_irq_init_hw(struct gpio_chip *gc) struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc); /* Disable IRQ by default */ - iowrite8(0, idio16gpio->base + 2); - iowrite8(0, idio16gpio->base + 1); + iowrite8(0, &idio16gpio->reg->irq_ctl); + iowrite8(0, &idio16gpio->reg->in0_7); return 0; } @@ -255,8 +279,8 @@ static int idio_16_probe(struct device *dev, unsigned int id) return -EBUSY; } - idio16gpio->base = devm_ioport_map(dev, base[id], IDIO_16_EXTENT); - if (!idio16gpio->base) + idio16gpio->reg = devm_ioport_map(dev, base[id], IDIO_16_EXTENT); + if (!idio16gpio->reg) return -ENOMEM; idio16gpio->chip.label = name; -- cgit v1.2.3 From fb38af4a3a79c354771f335c671b144348d6d6fa Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 20 Jul 2022 09:45:59 -0400 Subject: gpio: i8255: Introduce the Intel 8255 interface library module Exposes consumer library functions providing support for interfaces compatible with the venerable Intel 8255 Programmable Peripheral Interface (PPI). The Intel 8255 PPI first appeared in the early 1970s, initially for the Intel 8080 and later appearing in the original IBM-PC. The popularity of the original Intel 8255 chip led to many subsequent variants and clones of the interface in various chips and integrated circuits. Although still popular, interfaces compatible with the Intel 8255 PPI are nowdays typically found embedded in larger VLSI processing chips and FPGA components rather than as discrete ICs. A CONFIG_GPIO_I8255 Kconfig option is introduced by this patch. Modules wanting access to these i8255 library functions should select this Kconfig option, and import the I8255 symbol namespace. Tested-by: Fred Eckert Reviewed-by: Linus Walleij Cc: John Hentges Cc: Jay Dolan Cc: Andy Shevchenko Signed-off-by: William Breathitt Gray Signed-off-by: Bartosz Golaszewski --- MAINTAINERS | 6 + drivers/gpio/Kconfig | 12 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-i8255.c | 287 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/gpio/gpio-i8255.h | 46 ++++++++ 5 files changed, 352 insertions(+) create mode 100644 drivers/gpio/gpio-i8255.c create mode 100644 drivers/gpio/gpio-i8255.h diff --git a/MAINTAINERS b/MAINTAINERS index 3405cb36cf02..960abc07304e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9902,6 +9902,12 @@ L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/fbdev/i810/ +INTEL 8255 GPIO DRIVER +M: William Breathitt Gray +L: linux-gpio@vger.kernel.org +S: Maintained +F: drivers/gpio/gpio-i8255.c + INTEL ASoC DRIVERS M: Cezary Rojewski M: Pierre-Louis Bossart diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index cf6a2c745f3f..df484b72000d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -824,6 +824,18 @@ endmenu menu "Port-mapped I/O GPIO drivers" depends on X86 # Unconditional I/O space access +config GPIO_I8255 + tristate + help + Enables support for the i8255 interface library functions. The i8255 + interface library provides functions to facilitate communication with + interfaces compatible with the venerable Intel 8255 Programmable + Peripheral Interface (PPI). The Intel 8255 PPI chip was first released + in the early 1970s but compatible interfaces are nowadays typically + found embedded in larger VLSI processing chips and FPGA components. + + If built as a module its name will be gpio-i8255. + config GPIO_104_DIO_48E tristate "ACCES 104-DIO-48E GPIO support" depends on PC104 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9d4805b2b60b..a0985d30f51b 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_GPIO_GW_PLD) += gpio-gw-pld.o obj-$(CONFIG_GPIO_HISI) += gpio-hisi.o obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o 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_IOP) += gpio-iop.o diff --git a/drivers/gpio/gpio-i8255.c b/drivers/gpio/gpio-i8255.c new file mode 100644 index 000000000000..9b97db418df1 --- /dev/null +++ b/drivers/gpio/gpio-i8255.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel 8255 Programmable Peripheral Interface + * Copyright (C) 2022 William Breathitt Gray + */ +#include +#include +#include +#include +#include +#include +#include + +#include "gpio-i8255.h" + +#define I8255_CONTROL_PORTC_LOWER_DIRECTION BIT(0) +#define I8255_CONTROL_PORTB_DIRECTION BIT(1) +#define I8255_CONTROL_PORTC_UPPER_DIRECTION BIT(3) +#define I8255_CONTROL_PORTA_DIRECTION BIT(4) +#define I8255_CONTROL_MODE_SET BIT(7) +#define I8255_PORTA 0 +#define I8255_PORTB 1 +#define I8255_PORTC 2 + +static int i8255_get_port(struct i8255 __iomem *const ppi, + const unsigned long io_port, const unsigned long mask) +{ + const unsigned long bank = io_port / 3; + const unsigned long ppi_port = io_port % 3; + + return ioread8(&ppi[bank].port[ppi_port]) & mask; +} + +static u8 i8255_direction_mask(const unsigned long offset) +{ + const unsigned long port_offset = offset % 8; + const unsigned long io_port = offset / 8; + const unsigned long ppi_port = io_port % 3; + + switch (ppi_port) { + case I8255_PORTA: + return I8255_CONTROL_PORTA_DIRECTION; + case I8255_PORTB: + return I8255_CONTROL_PORTB_DIRECTION; + case I8255_PORTC: + /* Port C can be configured by nibble */ + if (port_offset >= 4) + return I8255_CONTROL_PORTC_UPPER_DIRECTION; + return I8255_CONTROL_PORTC_LOWER_DIRECTION; + default: + /* Should never reach this path */ + return 0; + } +} + +static void i8255_set_port(struct i8255 __iomem *const ppi, + struct i8255_state *const state, + const unsigned long io_port, + const unsigned long mask, const unsigned long bits) +{ + const unsigned long bank = io_port / 3; + const unsigned long ppi_port = io_port % 3; + unsigned long flags; + unsigned long out_state; + + spin_lock_irqsave(&state[bank].lock, flags); + + out_state = ioread8(&ppi[bank].port[ppi_port]); + out_state = (out_state & ~mask) | (bits & mask); + iowrite8(out_state, &ppi[bank].port[ppi_port]); + + spin_unlock_irqrestore(&state[bank].lock, flags); +} + +/** + * i8255_direction_input - configure signal offset as input + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @state: devices states of the respective PPI banks + * @offset: signal offset to configure as input + * + * Configures a signal @offset as input for the respective Intel 8255 + * Programmable Peripheral Interface (@ppi) banks. The @state control_state + * values are updated to reflect the new configuration. + */ +void i8255_direction_input(struct i8255 __iomem *const ppi, + struct i8255_state *const state, + const unsigned long offset) +{ + const unsigned long io_port = offset / 8; + const unsigned long bank = io_port / 3; + unsigned long flags; + + spin_lock_irqsave(&state[bank].lock, flags); + + state[bank].control_state |= I8255_CONTROL_MODE_SET; + state[bank].control_state |= i8255_direction_mask(offset); + + iowrite8(state[bank].control_state, &ppi[bank].control); + + spin_unlock_irqrestore(&state[bank].lock, flags); +} +EXPORT_SYMBOL_NS_GPL(i8255_direction_input, I8255); + +/** + * i8255_direction_output - configure signal offset as output + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @state: devices states of the respective PPI banks + * @offset: signal offset to configure as output + * @value: signal value to output + * + * Configures a signal @offset as output for the respective Intel 8255 + * Programmable Peripheral Interface (@ppi) banks and sets the respective signal + * output to the desired @value. The @state control_state values are updated to + * reflect the new configuration. + */ +void i8255_direction_output(struct i8255 __iomem *const ppi, + struct i8255_state *const state, + const unsigned long offset, + const unsigned long value) +{ + const unsigned long io_port = offset / 8; + const unsigned long bank = io_port / 3; + unsigned long flags; + + spin_lock_irqsave(&state[bank].lock, flags); + + state[bank].control_state |= I8255_CONTROL_MODE_SET; + state[bank].control_state &= ~i8255_direction_mask(offset); + + iowrite8(state[bank].control_state, &ppi[bank].control); + + spin_unlock_irqrestore(&state[bank].lock, flags); + + i8255_set(ppi, state, offset, value); +} +EXPORT_SYMBOL_NS_GPL(i8255_direction_output, I8255); + +/** + * i8255_get - get signal value at signal offset + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @offset: offset of signal to get + * + * Returns the signal value (0=low, 1=high) for the signal at @offset for the + * respective Intel 8255 Programmable Peripheral Interface (@ppi) banks. + */ +int i8255_get(struct i8255 __iomem *const ppi, const unsigned long offset) +{ + const unsigned long io_port = offset / 8; + const unsigned long offset_mask = BIT(offset % 8); + + return !!i8255_get_port(ppi, io_port, offset_mask); +} +EXPORT_SYMBOL_NS_GPL(i8255_get, I8255); + +/** + * i8255_get_direction - get the I/O direction for a signal offset + * @state: devices states of the respective PPI banks + * @offset: offset of signal to get direction + * + * Returns the signal direction (0=output, 1=input) for the signal at @offset. + */ +int i8255_get_direction(const struct i8255_state *const state, + const unsigned long offset) +{ + const unsigned long io_port = offset / 8; + const unsigned long bank = io_port / 3; + + return !!(state[bank].control_state & i8255_direction_mask(offset)); +} +EXPORT_SYMBOL_NS_GPL(i8255_get_direction, I8255); + +/** + * i8255_get_multiple - get multiple signal values at multiple signal offsets + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @mask: mask of signals to get + * @bits: bitmap to store signal values + * @ngpio: number of GPIO signals of the respective PPI banks + * + * Stores in @bits the values (0=low, 1=high) for the signals defined by @mask + * for the respective Intel 8255 Programmable Peripheral Interface (@ppi) banks. + */ +void i8255_get_multiple(struct i8255 __iomem *const ppi, + const unsigned long *const mask, + unsigned long *const bits, const unsigned long ngpio) +{ + unsigned long offset; + unsigned long port_mask; + unsigned long io_port; + unsigned long port_state; + + bitmap_zero(bits, ngpio); + + for_each_set_clump8(offset, port_mask, mask, ngpio) { + io_port = offset / 8; + port_state = i8255_get_port(ppi, io_port, port_mask); + + bitmap_set_value8(bits, port_state, offset); + } +} +EXPORT_SYMBOL_NS_GPL(i8255_get_multiple, I8255); + +/** + * i8255_mode0_output - configure all PPI ports to MODE 0 output mode + * @ppi: Intel 8255 Programmable Peripheral Interface bank + * + * Configures all Intel 8255 Programmable Peripheral Interface (@ppi) ports to + * MODE 0 (Basic Input/Output) output mode. + */ +void i8255_mode0_output(struct i8255 __iomem *const ppi) +{ + iowrite8(I8255_CONTROL_MODE_SET, &ppi->control); +} +EXPORT_SYMBOL_NS_GPL(i8255_mode0_output, I8255); + +/** + * i8255_set - set signal value at signal offset + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @state: devices states of the respective PPI banks + * @offset: offset of signal to set + * @value: value of signal to set + * + * Assigns output @value for the signal at @offset for the respective Intel 8255 + * Programmable Peripheral Interface (@ppi) banks. + */ +void i8255_set(struct i8255 __iomem *const ppi, struct i8255_state *const state, + const unsigned long offset, const unsigned long value) +{ + const unsigned long io_port = offset / 8; + const unsigned long port_offset = offset % 8; + const unsigned long mask = BIT(port_offset); + const unsigned long bits = value << port_offset; + + i8255_set_port(ppi, state, io_port, mask, bits); +} +EXPORT_SYMBOL_NS_GPL(i8255_set, I8255); + +/** + * i8255_set_multiple - set signal values at multiple signal offsets + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @state: devices states of the respective PPI banks + * @mask: mask of signals to set + * @bits: bitmap of signal output values + * @ngpio: number of GPIO signals of the respective PPI banks + * + * Assigns output values defined by @bits for the signals defined by @mask for + * the respective Intel 8255 Programmable Peripheral Interface (@ppi) banks. + */ +void i8255_set_multiple(struct i8255 __iomem *const ppi, + struct i8255_state *const state, + const unsigned long *const mask, + const unsigned long *const bits, + const unsigned long ngpio) +{ + unsigned long offset; + unsigned long port_mask; + unsigned long io_port; + unsigned long value; + + for_each_set_clump8(offset, port_mask, mask, ngpio) { + io_port = offset / 8; + value = bitmap_get_value8(bits, offset); + i8255_set_port(ppi, state, io_port, port_mask, value); + } +} +EXPORT_SYMBOL_NS_GPL(i8255_set_multiple, I8255); + +/** + * i8255_state_init - initialize i8255_state structure + * @state: devices states of the respective PPI banks + * @nbanks: number of Intel 8255 Programmable Peripheral Interface banks + * + * Initializes the @state of each Intel 8255 Programmable Peripheral Interface + * bank for use in i8255 library functions. + */ +void i8255_state_init(struct i8255_state *const state, + const unsigned long nbanks) +{ + unsigned long bank; + + for (bank = 0; bank < nbanks; bank++) + spin_lock_init(&state[bank].lock); +} +EXPORT_SYMBOL_NS_GPL(i8255_state_init, I8255); + +MODULE_AUTHOR("William Breathitt Gray"); +MODULE_DESCRIPTION("Intel 8255 Programmable Peripheral Interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-i8255.h b/drivers/gpio/gpio-i8255.h new file mode 100644 index 000000000000..d9084aae9446 --- /dev/null +++ b/drivers/gpio/gpio-i8255.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright 2022 William Breathitt Gray */ +#ifndef _I8255_H_ +#define _I8255_H_ + +#include +#include + +/** + * struct i8255 - Intel 8255 register structure + * @port: Port A, B, and C + * @control: Control register + */ +struct i8255 { + u8 port[3]; + u8 control; +}; + +/** + * struct i8255_state - Intel 8255 state structure + * @lock: synchronization lock for accessing device state + * @control_state: Control register state + */ +struct i8255_state { + spinlock_t lock; + u8 control_state; +}; + +void i8255_direction_input(struct i8255 __iomem *ppi, struct i8255_state *state, + unsigned long offset); +void i8255_direction_output(struct i8255 __iomem *ppi, + struct i8255_state *state, unsigned long offset, + unsigned long value); +int i8255_get(struct i8255 __iomem *ppi, unsigned long offset); +int i8255_get_direction(const struct i8255_state *state, unsigned long offset); +void i8255_get_multiple(struct i8255 __iomem *ppi, const unsigned long *mask, + unsigned long *bits, unsigned long ngpio); +void i8255_mode0_output(struct i8255 __iomem *const ppi); +void i8255_set(struct i8255 __iomem *ppi, struct i8255_state *state, + unsigned long offset, unsigned long value); +void i8255_set_multiple(struct i8255 __iomem *ppi, struct i8255_state *state, + const unsigned long *mask, const unsigned long *bits, + unsigned long ngpio); +void i8255_state_init(struct i8255_state *const state, unsigned long nbanks); + +#endif /* _I8255_H_ */ -- cgit v1.2.3 From 71b7b3972590908fd724f4622cd908ad02464ad8 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 20 Jul 2022 09:46:00 -0400 Subject: gpio: 104-dio-48e: Implement and utilize register structures Reduce magic numbers and improve code readability by implementing and utilizing named register data structures. The 104-DIO-48E device features an Intel 8255 compatible GPIO interface, so the i8255 GPIO module is selected and utilized as well. Reviewed-by: Linus Walleij Cc: John Hentges Cc: Jay Dolan Signed-off-by: William Breathitt Gray Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-104-dio-48e.c | 249 ++++++++++++---------------------------- 2 files changed, 75 insertions(+), 175 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index df484b72000d..d070bd9f4f94 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -841,6 +841,7 @@ config GPIO_104_DIO_48E depends on PC104 select ISA_BUS_API select GPIOLIB_IRQCHIP + select GPIO_I8255 help Enables GPIO support for the ACCES 104-DIO-48E series (104-DIO-48E, 104-DIO-24E). The base port addresses for the devices may be diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c index f118ad9bcd33..a41551870759 100644 --- a/drivers/gpio/gpio-104-dio-48e.c +++ b/drivers/gpio/gpio-104-dio-48e.c @@ -6,8 +6,7 @@ * This driver supports the following ACCES devices: 104-DIO-48E and * 104-DIO-24E. */ -#include -#include +#include #include #include #include @@ -20,6 +19,11 @@ #include #include #include +#include + +#include "gpio-i8255.h" + +MODULE_IMPORT_NS(I8255); #define DIO48E_EXTENT 16 #define MAX_NUM_DIO48E max_num_isa_dev(DIO48E_EXTENT) @@ -33,34 +37,54 @@ static unsigned int irq[MAX_NUM_DIO48E]; module_param_hw_array(irq, uint, irq, NULL, 0); MODULE_PARM_DESC(irq, "ACCES 104-DIO-48E interrupt line numbers"); +#define DIO48E_NUM_PPI 2 + +/** + * struct dio48e_reg - device register structure + * @ppi: Programmable Peripheral Interface groups + * @enable_buffer: Enable/Disable Buffer groups + * @unused1: Unused + * @enable_interrupt: Write: Enable Interrupt + * Read: Disable Interrupt + * @unused2: Unused + * @enable_counter: Write: Enable Counter/Timer Addressing + * Read: Disable Counter/Timer Addressing + * @unused3: Unused + * @clear_interrupt: Clear Interrupt + */ +struct dio48e_reg { + struct i8255 ppi[DIO48E_NUM_PPI]; + u8 enable_buffer[DIO48E_NUM_PPI]; + u8 unused1; + u8 enable_interrupt; + u8 unused2; + u8 enable_counter; + u8 unused3; + u8 clear_interrupt; +}; + /** * struct dio48e_gpio - GPIO device private data structure - * @chip: instance of the gpio_chip - * @io_state: bit I/O state (whether bit is set to input or output) - * @out_state: output bits state - * @control: Control registers state - * @lock: synchronization lock to prevent I/O race conditions - * @base: base port address of the GPIO device - * @irq_mask: I/O bits affected by interrupts + * @chip: instance of the gpio_chip + * @ppi_state: PPI device states + * @lock: synchronization lock to prevent I/O race conditions + * @reg: I/O address offset for the device registers + * @irq_mask: I/O bits affected by interrupts */ struct dio48e_gpio { struct gpio_chip chip; - unsigned char io_state[6]; - unsigned char out_state[6]; - unsigned char control[2]; + struct i8255_state ppi_state[DIO48E_NUM_PPI]; raw_spinlock_t lock; - void __iomem *base; + struct dio48e_reg __iomem *reg; unsigned char irq_mask; }; static int dio48e_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - if (dio48egpio->io_state[port] & mask) - return GPIO_LINE_DIRECTION_IN; + if (i8255_get_direction(dio48egpio->ppi_state, offset)) + return GPIO_LINE_DIRECTION_IN; return GPIO_LINE_DIRECTION_OUT; } @@ -68,38 +92,9 @@ static int dio48e_gpio_get_direction(struct gpio_chip *chip, unsigned int offset static int dio48e_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - const unsigned int io_port = offset / 8; - const unsigned int control_port = io_port / 3; - void __iomem *const control_addr = dio48egpio->base + 3 + control_port * 4; - unsigned long flags; - unsigned int control; - - raw_spin_lock_irqsave(&dio48egpio->lock, flags); - /* Check if configuring Port C */ - if (io_port == 2 || io_port == 5) { - /* Port C can be configured by nibble */ - if (offset % 8 > 3) { - dio48egpio->io_state[io_port] |= 0xF0; - dio48egpio->control[control_port] |= BIT(3); - } else { - dio48egpio->io_state[io_port] |= 0x0F; - dio48egpio->control[control_port] |= BIT(0); - } - } else { - dio48egpio->io_state[io_port] |= 0xFF; - if (io_port == 0 || io_port == 3) - dio48egpio->control[control_port] |= BIT(4); - else - dio48egpio->control[control_port] |= BIT(1); - } - - control = BIT(7) | dio48egpio->control[control_port]; - iowrite8(control, control_addr); - control &= ~BIT(7); - iowrite8(control, control_addr); - - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); + i8255_direction_input(dio48egpio->reg->ppi, dio48egpio->ppi_state, + offset); return 0; } @@ -108,48 +103,9 @@ static int dio48e_gpio_direction_output(struct gpio_chip *chip, unsigned int off int value) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - const unsigned int io_port = offset / 8; - const unsigned int control_port = io_port / 3; - const unsigned int mask = BIT(offset % 8); - void __iomem *const control_addr = dio48egpio->base + 3 + control_port * 4; - const unsigned int out_port = (io_port > 2) ? io_port + 1 : io_port; - unsigned long flags; - unsigned int control; - - raw_spin_lock_irqsave(&dio48egpio->lock, flags); - - /* Check if configuring Port C */ - if (io_port == 2 || io_port == 5) { - /* Port C can be configured by nibble */ - if (offset % 8 > 3) { - dio48egpio->io_state[io_port] &= 0x0F; - dio48egpio->control[control_port] &= ~BIT(3); - } else { - dio48egpio->io_state[io_port] &= 0xF0; - dio48egpio->control[control_port] &= ~BIT(0); - } - } else { - dio48egpio->io_state[io_port] &= 0x00; - if (io_port == 0 || io_port == 3) - dio48egpio->control[control_port] &= ~BIT(4); - else - dio48egpio->control[control_port] &= ~BIT(1); - } - - if (value) - dio48egpio->out_state[io_port] |= mask; - else - dio48egpio->out_state[io_port] &= ~mask; - control = BIT(7) | dio48egpio->control[control_port]; - iowrite8(control, control_addr); - - iowrite8(dio48egpio->out_state[io_port], dio48egpio->base + out_port); - - control &= ~BIT(7); - iowrite8(control, control_addr); - - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); + i8255_direction_output(dio48egpio->reg->ppi, dio48egpio->ppi_state, + offset, value); return 0; } @@ -157,47 +113,16 @@ static int dio48e_gpio_direction_output(struct gpio_chip *chip, unsigned int off static int dio48e_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - const unsigned int in_port = (port > 2) ? port + 1 : port; - unsigned long flags; - unsigned int port_state; - - raw_spin_lock_irqsave(&dio48egpio->lock, flags); - /* ensure that GPIO is set for input */ - if (!(dio48egpio->io_state[port] & mask)) { - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); - return -EINVAL; - } - - port_state = ioread8(dio48egpio->base + in_port); - - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); - - return !!(port_state & mask); + return i8255_get(dio48egpio->reg->ppi, offset); } -static const size_t ports[] = { 0, 1, 2, 4, 5, 6 }; - static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - void __iomem *port_addr; - unsigned long port_state; - /* clear bits array to a clean slate */ - bitmap_zero(bits, chip->ngpio); - - for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { - port_addr = dio48egpio->base + ports[offset / 8]; - port_state = ioread8(port_addr) & gpio_mask; - - bitmap_set_value8(bits, port_state, offset); - } + i8255_get_multiple(dio48egpio->reg->ppi, mask, bits, chip->ngpio); return 0; } @@ -205,49 +130,17 @@ static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, static void dio48e_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - const unsigned int out_port = (port > 2) ? port + 1 : port; - unsigned long flags; - raw_spin_lock_irqsave(&dio48egpio->lock, flags); - - if (value) - dio48egpio->out_state[port] |= mask; - else - dio48egpio->out_state[port] &= ~mask; - - iowrite8(dio48egpio->out_state[port], dio48egpio->base + out_port); - - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); + i8255_set(dio48egpio->reg->ppi, dio48egpio->ppi_state, offset, value); } static void dio48e_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - size_t index; - void __iomem *port_addr; - unsigned long bitmask; - unsigned long flags; - - for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { - index = offset / 8; - port_addr = dio48egpio->base + ports[index]; - - bitmask = bitmap_get_value8(bits, offset) & gpio_mask; - - raw_spin_lock_irqsave(&dio48egpio->lock, flags); - /* update output state data and set device gpio register */ - dio48egpio->out_state[index] &= ~gpio_mask; - dio48egpio->out_state[index] |= bitmask; - iowrite8(dio48egpio->out_state[index], port_addr); - - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); - } + i8255_set_multiple(dio48egpio->reg->ppi, dio48egpio->ppi_state, mask, + bits, chip->ngpio); } static void dio48e_irq_ack(struct irq_data *data) @@ -274,7 +167,7 @@ static void dio48e_irq_mask(struct irq_data *data) if (!dio48egpio->irq_mask) /* disable interrupts */ - ioread8(dio48egpio->base + 0xB); + ioread8(&dio48egpio->reg->enable_interrupt); raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); } @@ -294,8 +187,8 @@ static void dio48e_irq_unmask(struct irq_data *data) if (!dio48egpio->irq_mask) { /* enable interrupts */ - iowrite8(0x00, dio48egpio->base + 0xF); - iowrite8(0x00, dio48egpio->base + 0xB); + iowrite8(0x00, &dio48egpio->reg->clear_interrupt); + iowrite8(0x00, &dio48egpio->reg->enable_interrupt); } if (offset == 19) @@ -341,7 +234,7 @@ static irqreturn_t dio48e_irq_handler(int irq, void *dev_id) raw_spin_lock(&dio48egpio->lock); - iowrite8(0x00, dio48egpio->base + 0xF); + iowrite8(0x00, &dio48egpio->reg->clear_interrupt); raw_spin_unlock(&dio48egpio->lock); @@ -373,11 +266,26 @@ static int dio48e_irq_init_hw(struct gpio_chip *gc) struct dio48e_gpio *const dio48egpio = gpiochip_get_data(gc); /* Disable IRQ by default */ - ioread8(dio48egpio->base + 0xB); + ioread8(&dio48egpio->reg->enable_interrupt); return 0; } +static void dio48e_init_ppi(struct i8255 __iomem *const ppi, + struct i8255_state *const ppi_state) +{ + const unsigned long ngpio = 24; + const unsigned long mask = GENMASK(ngpio - 1, 0); + const unsigned long bits = 0; + unsigned long i; + + /* Initialize all GPIO to output 0 */ + for (i = 0; i < DIO48E_NUM_PPI; i++) { + i8255_mode0_output(&ppi[i]); + i8255_set_multiple(&ppi[i], &ppi_state[i], &mask, &bits, ngpio); + } +} + static int dio48e_probe(struct device *dev, unsigned int id) { struct dio48e_gpio *dio48egpio; @@ -395,8 +303,8 @@ static int dio48e_probe(struct device *dev, unsigned int id) return -EBUSY; } - dio48egpio->base = devm_ioport_map(dev, base[id], DIO48E_EXTENT); - if (!dio48egpio->base) + dio48egpio->reg = devm_ioport_map(dev, base[id], DIO48E_EXTENT); + if (!dio48egpio->reg) return -ENOMEM; dio48egpio->chip.label = name; @@ -425,17 +333,8 @@ static int dio48e_probe(struct device *dev, unsigned int id) raw_spin_lock_init(&dio48egpio->lock); - /* initialize all GPIO as output */ - iowrite8(0x80, dio48egpio->base + 3); - iowrite8(0x00, dio48egpio->base); - iowrite8(0x00, dio48egpio->base + 1); - iowrite8(0x00, dio48egpio->base + 2); - iowrite8(0x00, dio48egpio->base + 3); - iowrite8(0x80, dio48egpio->base + 7); - iowrite8(0x00, dio48egpio->base + 4); - iowrite8(0x00, dio48egpio->base + 5); - iowrite8(0x00, dio48egpio->base + 6); - iowrite8(0x00, dio48egpio->base + 7); + i8255_state_init(dio48egpio->ppi_state, DIO48E_NUM_PPI); + dio48e_init_ppi(dio48egpio->reg->ppi, dio48egpio->ppi_state); err = devm_gpiochip_add_data(dev, &dio48egpio->chip, dio48egpio); if (err) { -- cgit v1.2.3 From 3ce632fdd13abd23686c29e81a924d47365b2eca Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 20 Jul 2022 09:46:01 -0400 Subject: gpio: 104-idi-48: Implement and utilize register structures Reduce magic numbers and improve code readability by implementing and utilizing named register data structures. The 104-IDI-48 device features an Intel 8255 compatible GPIO interface, so the i8255 GPIO module is selected and utilized as well. Reviewed-by: Linus Walleij Cc: John Hentges Cc: Jay Dolan Signed-off-by: William Breathitt Gray Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-104-idi-48.c | 143 +++++++++++++++++------------------------ 2 files changed, 61 insertions(+), 83 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d070bd9f4f94..8f1d4d56f0aa 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -865,6 +865,7 @@ config GPIO_104_IDI_48 depends on PC104 select ISA_BUS_API select GPIOLIB_IRQCHIP + select GPIO_I8255 help Enables GPIO support for the ACCES 104-IDI-48 family (104-IDI-48A, 104-IDI-48AC, 104-IDI-48B, 104-IDI-48BC). The base port addresses for diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c index c1e4c3629e17..40be76efeed7 100644 --- a/drivers/gpio/gpio-104-idi-48.c +++ b/drivers/gpio/gpio-104-idi-48.c @@ -6,8 +6,7 @@ * This driver supports the following ACCES devices: 104-IDI-48A, * 104-IDI-48AC, 104-IDI-48B, and 104-IDI-48BC. */ -#include -#include +#include #include #include #include @@ -20,6 +19,11 @@ #include #include #include +#include + +#include "gpio-i8255.h" + +MODULE_IMPORT_NS(I8255); #define IDI_48_EXTENT 8 #define MAX_NUM_IDI_48 max_num_isa_dev(IDI_48_EXTENT) @@ -33,21 +37,34 @@ static unsigned int irq[MAX_NUM_IDI_48]; module_param_hw_array(irq, uint, irq, NULL, 0); MODULE_PARM_DESC(irq, "ACCES 104-IDI-48 interrupt line numbers"); +/** + * struct idi_48_reg - device register structure + * @port0: Port 0 Inputs + * @unused: Unused + * @port1: Port 1 Inputs + * @irq: Read: IRQ Status Register/IRQ Clear + * Write: IRQ Enable/Disable + */ +struct idi_48_reg { + u8 port0[3]; + u8 unused; + u8 port1[3]; + u8 irq; +}; + /** * struct idi_48_gpio - GPIO device private data structure * @chip: instance of the gpio_chip * @lock: synchronization lock to prevent I/O race conditions - * @ack_lock: synchronization lock to prevent IRQ handler race conditions * @irq_mask: input bits affected by interrupts - * @base: base port address of the GPIO device + * @reg: I/O address offset for the device registers * @cos_enb: Change-Of-State IRQ enable boundaries mask */ struct idi_48_gpio { struct gpio_chip chip; - raw_spinlock_t lock; - spinlock_t ack_lock; + spinlock_t lock; unsigned char irq_mask[6]; - void __iomem *base; + struct idi_48_reg __iomem *reg; unsigned char cos_enb; }; @@ -64,42 +81,18 @@ static int idi_48_gpio_direction_input(struct gpio_chip *chip, unsigned int offs static int idi_48_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); - unsigned int i; - static const unsigned int register_offset[6] = { 0, 1, 2, 4, 5, 6 }; - void __iomem *port_addr; - unsigned int mask; - - for (i = 0; i < 48; i += 8) - if (offset < i + 8) { - port_addr = idi48gpio->base + register_offset[i / 8]; - mask = BIT(offset - i); - - return !!(ioread8(port_addr) & mask); - } + void __iomem *const ppi = idi48gpio->reg; - /* The following line should never execute since offset < 48 */ - return 0; + return i8255_get(ppi, offset); } static int idi_48_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - static const size_t ports[] = { 0, 1, 2, 4, 5, 6 }; - void __iomem *port_addr; - unsigned long port_state; - - /* clear bits array to a clean slate */ - bitmap_zero(bits, chip->ngpio); + void __iomem *const ppi = idi48gpio->reg; - for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { - port_addr = idi48gpio->base + ports[offset / 8]; - port_state = ioread8(port_addr) & gpio_mask; - - bitmap_set_value8(bits, port_state, offset); - } + i8255_get_multiple(ppi, mask, bits, chip->ngpio); return 0; } @@ -113,30 +106,24 @@ static void idi_48_irq_mask(struct irq_data *data) struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); const unsigned int offset = irqd_to_hwirq(data); - unsigned int i; - unsigned int mask; - unsigned int boundary; + const unsigned long boundary = offset / 8; + const unsigned long mask = BIT(offset % 8); unsigned long flags; - for (i = 0; i < 48; i += 8) - if (offset < i + 8) { - mask = BIT(offset - i); - boundary = i / 8; - - idi48gpio->irq_mask[boundary] &= ~mask; + spin_lock_irqsave(&idi48gpio->lock, flags); - if (!idi48gpio->irq_mask[boundary]) { - idi48gpio->cos_enb &= ~BIT(boundary); + idi48gpio->irq_mask[boundary] &= ~mask; - raw_spin_lock_irqsave(&idi48gpio->lock, flags); + /* Exit early if there are still input lines with IRQ unmasked */ + if (idi48gpio->irq_mask[boundary]) + goto exit; - iowrite8(idi48gpio->cos_enb, idi48gpio->base + 7); + idi48gpio->cos_enb &= ~BIT(boundary); - raw_spin_unlock_irqrestore(&idi48gpio->lock, flags); - } + iowrite8(idi48gpio->cos_enb, &idi48gpio->reg->irq); - return; - } +exit: + spin_unlock_irqrestore(&idi48gpio->lock, flags); } static void idi_48_irq_unmask(struct irq_data *data) @@ -144,32 +131,27 @@ static void idi_48_irq_unmask(struct irq_data *data) struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); const unsigned int offset = irqd_to_hwirq(data); - unsigned int i; - unsigned int mask; - unsigned int boundary; + const unsigned long boundary = offset / 8; + const unsigned long mask = BIT(offset % 8); unsigned int prev_irq_mask; unsigned long flags; - for (i = 0; i < 48; i += 8) - if (offset < i + 8) { - mask = BIT(offset - i); - boundary = i / 8; - prev_irq_mask = idi48gpio->irq_mask[boundary]; + spin_lock_irqsave(&idi48gpio->lock, flags); - idi48gpio->irq_mask[boundary] |= mask; + prev_irq_mask = idi48gpio->irq_mask[boundary]; - if (!prev_irq_mask) { - idi48gpio->cos_enb |= BIT(boundary); + idi48gpio->irq_mask[boundary] |= mask; - raw_spin_lock_irqsave(&idi48gpio->lock, flags); + /* Exit early if IRQ was already unmasked for this boundary */ + if (prev_irq_mask) + goto exit; - iowrite8(idi48gpio->cos_enb, idi48gpio->base + 7); + idi48gpio->cos_enb |= BIT(boundary); - raw_spin_unlock_irqrestore(&idi48gpio->lock, flags); - } + iowrite8(idi48gpio->cos_enb, &idi48gpio->reg->irq); - return; - } +exit: + spin_unlock_irqrestore(&idi48gpio->lock, flags); } static int idi_48_irq_set_type(struct irq_data *data, unsigned int flow_type) @@ -200,17 +182,13 @@ static irqreturn_t idi_48_irq_handler(int irq, void *dev_id) unsigned long gpio; struct gpio_chip *const chip = &idi48gpio->chip; - spin_lock(&idi48gpio->ack_lock); - - raw_spin_lock(&idi48gpio->lock); - - cos_status = ioread8(idi48gpio->base + 7); + spin_lock(&idi48gpio->lock); - raw_spin_unlock(&idi48gpio->lock); + cos_status = ioread8(&idi48gpio->reg->irq); /* IRQ Status (bit 6) is active low (0 = IRQ generated by device) */ if (cos_status & BIT(6)) { - spin_unlock(&idi48gpio->ack_lock); + spin_unlock(&idi48gpio->lock); return IRQ_NONE; } @@ -228,7 +206,7 @@ static irqreturn_t idi_48_irq_handler(int irq, void *dev_id) } } - spin_unlock(&idi48gpio->ack_lock); + spin_unlock(&idi48gpio->lock); return IRQ_HANDLED; } @@ -250,8 +228,8 @@ static int idi_48_irq_init_hw(struct gpio_chip *gc) struct idi_48_gpio *const idi48gpio = gpiochip_get_data(gc); /* Disable IRQ by default */ - iowrite8(0, idi48gpio->base + 7); - ioread8(idi48gpio->base + 7); + iowrite8(0, &idi48gpio->reg->irq); + ioread8(&idi48gpio->reg->irq); return 0; } @@ -273,8 +251,8 @@ static int idi_48_probe(struct device *dev, unsigned int id) return -EBUSY; } - idi48gpio->base = devm_ioport_map(dev, base[id], IDI_48_EXTENT); - if (!idi48gpio->base) + idi48gpio->reg = devm_ioport_map(dev, base[id], IDI_48_EXTENT); + if (!idi48gpio->reg) return -ENOMEM; idi48gpio->chip.label = name; @@ -298,8 +276,7 @@ static int idi_48_probe(struct device *dev, unsigned int id) girq->handler = handle_edge_irq; girq->init_hw = idi_48_irq_init_hw; - raw_spin_lock_init(&idi48gpio->lock); - spin_lock_init(&idi48gpio->ack_lock); + spin_lock_init(&idi48gpio->lock); err = devm_gpiochip_add_data(dev, &idi48gpio->chip, idi48gpio); if (err) { -- cgit v1.2.3 From 949506dc608f9820e0cee51b106c35fd3503bbb2 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 20 Jul 2022 09:46:02 -0400 Subject: gpio: gpio-mm: Implement and utilize register structures Reduce magic numbers and improve code readability by implementing and utilizing named register data structures. The GPIO-MM device features an Intel 8255 compatible GPIO interface, so the i8255 GPIO module is selected and utilized as well. Tested-by: Fred Eckert Reviewed-by: Linus Walleij Signed-off-by: William Breathitt Gray Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-gpio-mm.c | 202 +++++++++----------------------------------- 2 files changed, 40 insertions(+), 163 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8f1d4d56f0aa..0642f579196f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -886,6 +886,7 @@ config GPIO_GPIO_MM tristate "Diamond Systems GPIO-MM GPIO support" depends on PC104 select ISA_BUS_API + select GPIO_I8255 help Enables GPIO support for the Diamond Systems GPIO-MM and GPIO-MM-12. diff --git a/drivers/gpio/gpio-gpio-mm.c b/drivers/gpio/gpio-gpio-mm.c index 097a06463d01..2689671b6b01 100644 --- a/drivers/gpio/gpio-gpio-mm.c +++ b/drivers/gpio/gpio-gpio-mm.c @@ -6,8 +6,6 @@ * This driver supports the following Diamond Systems devices: GPIO-MM and * GPIO-MM-12. */ -#include -#include #include #include #include @@ -17,7 +15,10 @@ #include #include #include -#include + +#include "gpio-i8255.h" + +MODULE_IMPORT_NS(I8255); #define GPIOMM_EXTENT 8 #define MAX_NUM_GPIOMM max_num_isa_dev(GPIOMM_EXTENT) @@ -27,32 +28,26 @@ static unsigned int num_gpiomm; module_param_hw_array(base, uint, ioport, &num_gpiomm, 0); MODULE_PARM_DESC(base, "Diamond Systems GPIO-MM base addresses"); +#define GPIOMM_NUM_PPI 2 + /** * struct gpiomm_gpio - GPIO device private data structure - * @chip: instance of the gpio_chip - * @io_state: bit I/O state (whether bit is set to input or output) - * @out_state: output bits state - * @control: Control registers state - * @lock: synchronization lock to prevent I/O race conditions - * @base: base port address of the GPIO device + * @chip: instance of the gpio_chip + * @ppi_state: Programmable Peripheral Interface group states + * @ppi: Programmable Peripheral Interface groups */ struct gpiomm_gpio { struct gpio_chip chip; - unsigned char io_state[6]; - unsigned char out_state[6]; - unsigned char control[2]; - spinlock_t lock; - void __iomem *base; + struct i8255_state ppi_state[GPIOMM_NUM_PPI]; + struct i8255 __iomem *ppi; }; static int gpiomm_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - if (gpiommgpio->io_state[port] & mask) + if (i8255_get_direction(gpiommgpio->ppi_state, offset)) return GPIO_LINE_DIRECTION_IN; return GPIO_LINE_DIRECTION_OUT; @@ -62,35 +57,8 @@ static int gpiomm_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - const unsigned int io_port = offset / 8; - const unsigned int control_port = io_port / 3; - unsigned long flags; - unsigned int control; - - spin_lock_irqsave(&gpiommgpio->lock, flags); - - /* Check if configuring Port C */ - if (io_port == 2 || io_port == 5) { - /* Port C can be configured by nibble */ - if (offset % 8 > 3) { - gpiommgpio->io_state[io_port] |= 0xF0; - gpiommgpio->control[control_port] |= BIT(3); - } else { - gpiommgpio->io_state[io_port] |= 0x0F; - gpiommgpio->control[control_port] |= BIT(0); - } - } else { - gpiommgpio->io_state[io_port] |= 0xFF; - if (io_port == 0 || io_port == 3) - gpiommgpio->control[control_port] |= BIT(4); - else - gpiommgpio->control[control_port] |= BIT(1); - } - control = BIT(7) | gpiommgpio->control[control_port]; - iowrite8(control, gpiommgpio->base + 3 + control_port*4); - - spin_unlock_irqrestore(&gpiommgpio->lock, flags); + i8255_direction_input(gpiommgpio->ppi, gpiommgpio->ppi_state, offset); return 0; } @@ -99,44 +67,9 @@ static int gpiomm_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - const unsigned int io_port = offset / 8; - const unsigned int control_port = io_port / 3; - const unsigned int mask = BIT(offset % 8); - const unsigned int out_port = (io_port > 2) ? io_port + 1 : io_port; - unsigned long flags; - unsigned int control; - - spin_lock_irqsave(&gpiommgpio->lock, flags); - - /* Check if configuring Port C */ - if (io_port == 2 || io_port == 5) { - /* Port C can be configured by nibble */ - if (offset % 8 > 3) { - gpiommgpio->io_state[io_port] &= 0x0F; - gpiommgpio->control[control_port] &= ~BIT(3); - } else { - gpiommgpio->io_state[io_port] &= 0xF0; - gpiommgpio->control[control_port] &= ~BIT(0); - } - } else { - gpiommgpio->io_state[io_port] &= 0x00; - if (io_port == 0 || io_port == 3) - gpiommgpio->control[control_port] &= ~BIT(4); - else - gpiommgpio->control[control_port] &= ~BIT(1); - } - - if (value) - gpiommgpio->out_state[io_port] |= mask; - else - gpiommgpio->out_state[io_port] &= ~mask; - - control = BIT(7) | gpiommgpio->control[control_port]; - iowrite8(control, gpiommgpio->base + 3 + control_port*4); - iowrite8(gpiommgpio->out_state[io_port], gpiommgpio->base + out_port); - - spin_unlock_irqrestore(&gpiommgpio->lock, flags); + i8255_direction_output(gpiommgpio->ppi, gpiommgpio->ppi_state, offset, + value); return 0; } @@ -144,47 +77,16 @@ static int gpiomm_gpio_direction_output(struct gpio_chip *chip, static int gpiomm_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - const unsigned int in_port = (port > 2) ? port + 1 : port; - unsigned long flags; - unsigned int port_state; - - spin_lock_irqsave(&gpiommgpio->lock, flags); - - /* ensure that GPIO is set for input */ - if (!(gpiommgpio->io_state[port] & mask)) { - spin_unlock_irqrestore(&gpiommgpio->lock, flags); - return -EINVAL; - } - - port_state = ioread8(gpiommgpio->base + in_port); - - spin_unlock_irqrestore(&gpiommgpio->lock, flags); - return !!(port_state & mask); + return i8255_get(gpiommgpio->ppi, offset); } -static const size_t ports[] = { 0, 1, 2, 4, 5, 6 }; - static int gpiomm_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - void __iomem *port_addr; - unsigned long port_state; - - /* clear bits array to a clean slate */ - bitmap_zero(bits, chip->ngpio); - for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { - port_addr = gpiommgpio->base + ports[offset / 8]; - port_state = ioread8(port_addr) & gpio_mask; - - bitmap_set_value8(bits, port_state, offset); - } + i8255_get_multiple(gpiommgpio->ppi, mask, bits, chip->ngpio); return 0; } @@ -193,49 +95,17 @@ static void gpiomm_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - const unsigned int out_port = (port > 2) ? port + 1 : port; - unsigned long flags; - - spin_lock_irqsave(&gpiommgpio->lock, flags); - - if (value) - gpiommgpio->out_state[port] |= mask; - else - gpiommgpio->out_state[port] &= ~mask; - - iowrite8(gpiommgpio->out_state[port], gpiommgpio->base + out_port); - spin_unlock_irqrestore(&gpiommgpio->lock, flags); + i8255_set(gpiommgpio->ppi, gpiommgpio->ppi_state, offset, value); } static void gpiomm_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - size_t index; - void __iomem *port_addr; - unsigned long bitmask; - unsigned long flags; - - for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { - index = offset / 8; - port_addr = gpiommgpio->base + ports[index]; - - bitmask = bitmap_get_value8(bits, offset) & gpio_mask; - - spin_lock_irqsave(&gpiommgpio->lock, flags); - /* update output state data and set device gpio register */ - gpiommgpio->out_state[index] &= ~gpio_mask; - gpiommgpio->out_state[index] |= bitmask; - iowrite8(gpiommgpio->out_state[index], port_addr); - - spin_unlock_irqrestore(&gpiommgpio->lock, flags); - } + i8255_set_multiple(gpiommgpio->ppi, gpiommgpio->ppi_state, mask, bits, + chip->ngpio); } #define GPIOMM_NGPIO 48 @@ -250,6 +120,21 @@ static const char *gpiomm_names[GPIOMM_NGPIO] = { "Port 2C2", "Port 2C3", "Port 2C4", "Port 2C5", "Port 2C6", "Port 2C7", }; +static void gpiomm_init_dio(struct i8255 __iomem *const ppi, + struct i8255_state *const ppi_state) +{ + const unsigned long ngpio = 24; + const unsigned long mask = GENMASK(ngpio - 1, 0); + const unsigned long bits = 0; + unsigned long i; + + /* Initialize all GPIO to output 0 */ + for (i = 0; i < GPIOMM_NUM_PPI; i++) { + i8255_mode0_output(&ppi[i]); + i8255_set_multiple(&ppi[i], &ppi_state[i], &mask, &bits, ngpio); + } +} + static int gpiomm_probe(struct device *dev, unsigned int id) { struct gpiomm_gpio *gpiommgpio; @@ -266,8 +151,8 @@ static int gpiomm_probe(struct device *dev, unsigned int id) return -EBUSY; } - gpiommgpio->base = devm_ioport_map(dev, base[id], GPIOMM_EXTENT); - if (!gpiommgpio->base) + gpiommgpio->ppi = devm_ioport_map(dev, base[id], GPIOMM_EXTENT); + if (!gpiommgpio->ppi) return -ENOMEM; gpiommgpio->chip.label = name; @@ -284,7 +169,8 @@ static int gpiomm_probe(struct device *dev, unsigned int id) gpiommgpio->chip.set = gpiomm_gpio_set; gpiommgpio->chip.set_multiple = gpiomm_gpio_set_multiple; - spin_lock_init(&gpiommgpio->lock); + i8255_state_init(gpiommgpio->ppi_state, GPIOMM_NUM_PPI); + gpiomm_init_dio(gpiommgpio->ppi, gpiommgpio->ppi_state); err = devm_gpiochip_add_data(dev, &gpiommgpio->chip, gpiommgpio); if (err) { @@ -292,16 +178,6 @@ static int gpiomm_probe(struct device *dev, unsigned int id) return err; } - /* initialize all GPIO as output */ - iowrite8(0x80, gpiommgpio->base + 3); - iowrite8(0x00, gpiommgpio->base); - iowrite8(0x00, gpiommgpio->base + 1); - iowrite8(0x00, gpiommgpio->base + 2); - iowrite8(0x80, gpiommgpio->base + 7); - iowrite8(0x00, gpiommgpio->base + 4); - iowrite8(0x00, gpiommgpio->base + 5); - iowrite8(0x00, gpiommgpio->base + 6); - return 0; } -- cgit v1.2.3 From a0a2d10aad65afb1558d7b8ad5770dff75493d45 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 21 Jul 2022 11:25:08 -0400 Subject: MAINTAINERS: Update Intel 8255 GPIO driver file list The drivers/gpio/gpio-i8255.h header file is also maintained. Signed-off-by: William Breathitt Gray Signed-off-by: Bartosz Golaszewski --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 960abc07304e..2bd82ce171ca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9907,6 +9907,7 @@ M: William Breathitt Gray L: linux-gpio@vger.kernel.org S: Maintained F: drivers/gpio/gpio-i8255.c +F: drivers/gpio/gpio-i8255.h INTEL ASoC DRIVERS M: Cezary Rojewski -- cgit v1.2.3 From f2a2f2c9aed21944095c2f867640a9089759c7e7 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Thu, 21 Jul 2022 14:30:21 +0530 Subject: gpio: xilinx: add missing blank line after declarations Add a missing blank line. No functional changes. WARNING: Missing a blank line after declarations 128: FILE: drivers/gpio/gpio-xilinx.c:120: + void __iomem *addr = chip->regs + reg + xgpio_regoffset(chip, bit / 32); + xgpio_set_value32(a, bit, xgpio_readreg(addr)); WARNING: Missing a blank line after declarations 136: FILE: drivers/gpio/gpio-xilinx.c:126: + void __iomem *addr = chip->regs + reg + xgpio_regoffset(chip, bit / 32); + xgpio_writereg(addr, xgpio_get_value32(a, bit)); Signed-off-by: Shubhrajyoti Datta Reviewed-by: Michal Simek Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xilinx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index b6d3a57e27ed..7f129e7eae78 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -117,12 +117,14 @@ static inline int xgpio_regoffset(struct xgpio_instance *chip, int ch) static void xgpio_read_ch(struct xgpio_instance *chip, int reg, int bit, unsigned long *a) { void __iomem *addr = chip->regs + reg + xgpio_regoffset(chip, bit / 32); + xgpio_set_value32(a, bit, xgpio_readreg(addr)); } static void xgpio_write_ch(struct xgpio_instance *chip, int reg, int bit, unsigned long *a) { void __iomem *addr = chip->regs + reg + xgpio_regoffset(chip, bit / 32); + xgpio_writereg(addr, xgpio_get_value32(a, bit)); } -- cgit v1.2.3 From e3c1d6c8c59ae542e53c6ef4602eabc39c108575 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 26 Jul 2022 12:14:48 +0300 Subject: gpio: 74xx-mmio: Use bits instead of plain numbers for flags The initial code was misleading to use bitwise AND against plain number, and the commit d3054ba1db62 ("gpio: 74xx-mmio: Check MMIO_74XX_DIR_IN flag in mmio_74xx_dir_in()") missed that. Switch definitions to be defined bits for the correct comparison. Fixes: d3054ba1db62 ("gpio: 74xx-mmio: Check MMIO_74XX_DIR_IN flag in mmio_74xx_dir_in()") Reported-by: Dan Carpenter Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-74xx-mmio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c index cd399898ed12..0464f1ecd20d 100644 --- a/drivers/gpio/gpio-74xx-mmio.c +++ b/drivers/gpio/gpio-74xx-mmio.c @@ -13,8 +13,8 @@ #include #include -#define MMIO_74XX_DIR_IN (0 << 8) -#define MMIO_74XX_DIR_OUT (1 << 8) +#define MMIO_74XX_DIR_IN BIT(8) +#define MMIO_74XX_DIR_OUT BIT(9) #define MMIO_74XX_BIT_CNT(x) ((x) & GENMASK(7, 0)) struct mmio_74xx_gpio_priv { -- cgit v1.2.3 From c4f0d16daa6d1c5d862d063379c03310387095d5 Mon Sep 17 00:00:00 2001 From: Jesse Taube Date: Sat, 23 Jul 2022 12:05:04 -0400 Subject: dt-bindings: gpio: fsl-imx-gpio: Add i.MXRT compatibles Both the i.MXRT1170 and 1050 have the same gpio controller as "fsl,imx35-gpio". Add i.MXRT to the compatible list. Signed-off-by: Jesse Taube Acked-by: Rob Herring Acked-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml b/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml index f57d22d1ebd6..ae18603697d7 100644 --- a/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml @@ -37,6 +37,8 @@ properties: - fsl,imx8mp-gpio - fsl,imx8mq-gpio - fsl,imx8qxp-gpio + - fsl,imxrt1050-gpio + - fsl,imxrt1170-gpio - const: fsl,imx35-gpio reg: -- cgit v1.2.3