From c61d8b5791abbc8b1aa51593d45dc88ef8804e4a Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Fri, 12 Nov 2021 15:18:58 +0800 Subject: dt-bindings: gpio: gpio-vf610: Add imx8ulp compatible string Add the compatible string for i.MX8ULP. Reviewed-by: Linus Walleij Reviewed-by: Dong Aisheng Acked-by: Rob Herring Signed-off-by: Jacky Bai Signed-off-by: Peng Fan Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/gpio-vf610.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio-vf610.yaml b/Documentation/devicetree/bindings/gpio/gpio-vf610.yaml index 19738a457a58..e1359391d3a4 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-vf610.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-vf610.yaml @@ -24,6 +24,9 @@ properties: - items: - const: fsl,imx7ulp-gpio - const: fsl,vf610-gpio + - items: + - const: fsl,imx8ulp-gpio + - const: fsl,imx7ulp-gpio reg: description: The first reg tuple represents the PORT module, the second tuple -- cgit v1.2.3 From ea708ac5bf419d9735354f9deada384c1059700f Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 9 Nov 2021 10:12:53 -0600 Subject: gpio: xlp: Remove Netlogic XLP variants Netlogic XLP was removed in commit 95b8a5e0111a ("MIPS: Remove NETLOGIC support"). With those gone, the single platform left to support is Cavium ThunderX2. Remove all the Netlogic variants and DT support. For simplicity, the existing kconfig name is retained. Cc: Linus Walleij Cc: Bartosz Golaszewski Cc: linux-gpio@vger.kernel.org Signed-off-by: Rob Herring Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 10 ++-- drivers/gpio/gpio-xlp.c | 142 +++--------------------------------------------- 2 files changed, 13 insertions(+), 139 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 072ed610f9c6..e05f226b3d5d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -730,14 +730,12 @@ config GPIO_XILINX Say yes here to support the Xilinx FPGA GPIO device. config GPIO_XLP - tristate "Netlogic XLP GPIO support" - depends on OF_GPIO && (CPU_XLP || ARCH_THUNDER2 || COMPILE_TEST) + tristate "Cavium ThunderX2 GPIO support" + depends on ARCH_THUNDER2 || COMPILE_TEST select GPIOLIB_IRQCHIP help - This driver provides support for GPIO interface on Netlogic XLP MIPS64 - SoCs. Currently supported XLP variants are XLP8XX, XLP3XX, XLP2XX, - XLP9XX and XLP5XX. The same GPIO controller block is also present in - Cavium's ThunderX2 CN99XX SoCs. + This driver provides support for GPIO interface on Cavium's ThunderX2 + CN99XX SoCs (Originally from Netlogic XLP). If unsure, say N. diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c index 0d94d3aef752..814cc34aef97 100644 --- a/drivers/gpio/gpio-xlp.c +++ b/drivers/gpio/gpio-xlp.c @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -26,16 +25,6 @@ * * where addr is base address of the that feature register and gpio is the pin. */ -#define GPIO_OUTPUT_EN 0x00 -#define GPIO_PADDRV 0x08 -#define GPIO_INT_EN00 0x18 -#define GPIO_INT_EN10 0x20 -#define GPIO_INT_EN20 0x28 -#define GPIO_INT_EN30 0x30 -#define GPIO_INT_POL 0x38 -#define GPIO_INT_TYPE 0x40 -#define GPIO_INT_STAT 0x48 - #define GPIO_9XX_BYTESWAP 0X00 #define GPIO_9XX_CTRL 0X04 #define GPIO_9XX_OUTPUT_EN 0x14 @@ -52,14 +41,6 @@ #define GPIO_9XX_INT_TYPE 0x114 #define GPIO_9XX_INT_STAT 0x124 -#define GPIO_3XX_INT_EN00 0x18 -#define GPIO_3XX_INT_EN10 0x20 -#define GPIO_3XX_INT_EN20 0x28 -#define GPIO_3XX_INT_EN30 0x30 -#define GPIO_3XX_INT_POL 0x78 -#define GPIO_3XX_INT_TYPE 0x80 -#define GPIO_3XX_INT_STAT 0x88 - /* Interrupt type register mask */ #define XLP_GPIO_IRQ_TYPE_LVL 0x0 #define XLP_GPIO_IRQ_TYPE_EDGE 0x1 @@ -72,16 +53,6 @@ #define XLP_GPIO_IRQ_BASE 768 #define XLP_MAX_NR_GPIO 96 -/* XLP variants supported by this driver */ -enum { - XLP_GPIO_VARIANT_XLP832 = 1, - XLP_GPIO_VARIANT_XLP316, - XLP_GPIO_VARIANT_XLP208, - XLP_GPIO_VARIANT_XLP980, - XLP_GPIO_VARIANT_XLP532, - GPIO_VARIANT_VULCAN -}; - struct xlp_gpio_priv { struct gpio_chip chip; DECLARE_BITMAP(gpio_enabled_mask, XLP_MAX_NR_GPIO); @@ -257,44 +228,13 @@ static void xlp_gpio_set(struct gpio_chip *gc, unsigned gpio, int state) xlp_gpio_set_reg(priv->gpio_paddrv, gpio, state); } -static const struct of_device_id xlp_gpio_of_ids[] = { - { - .compatible = "netlogic,xlp832-gpio", - .data = (void *)XLP_GPIO_VARIANT_XLP832, - }, - { - .compatible = "netlogic,xlp316-gpio", - .data = (void *)XLP_GPIO_VARIANT_XLP316, - }, - { - .compatible = "netlogic,xlp208-gpio", - .data = (void *)XLP_GPIO_VARIANT_XLP208, - }, - { - .compatible = "netlogic,xlp980-gpio", - .data = (void *)XLP_GPIO_VARIANT_XLP980, - }, - { - .compatible = "netlogic,xlp532-gpio", - .data = (void *)XLP_GPIO_VARIANT_XLP532, - }, - { - .compatible = "brcm,vulcan-gpio", - .data = (void *)GPIO_VARIANT_VULCAN, - }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, xlp_gpio_of_ids); - static int xlp_gpio_probe(struct platform_device *pdev) { struct gpio_chip *gc; struct gpio_irq_chip *girq; struct xlp_gpio_priv *priv; void __iomem *gpio_base; - int irq_base, irq, err; - int ngpio; - u32 soc_type; + int irq, err; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -308,62 +248,12 @@ static int xlp_gpio_probe(struct platform_device *pdev) if (irq < 0) return irq; - if (pdev->dev.of_node) { - soc_type = (uintptr_t)of_device_get_match_data(&pdev->dev); - } else { - const struct acpi_device_id *acpi_id; - - acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table, - &pdev->dev); - if (!acpi_id || !acpi_id->driver_data) { - dev_err(&pdev->dev, "Unable to match ACPI ID\n"); - return -ENODEV; - } - soc_type = (uintptr_t) acpi_id->driver_data; - } - - switch (soc_type) { - case XLP_GPIO_VARIANT_XLP832: - priv->gpio_out_en = gpio_base + GPIO_OUTPUT_EN; - priv->gpio_paddrv = gpio_base + GPIO_PADDRV; - priv->gpio_intr_stat = gpio_base + GPIO_INT_STAT; - priv->gpio_intr_type = gpio_base + GPIO_INT_TYPE; - priv->gpio_intr_pol = gpio_base + GPIO_INT_POL; - priv->gpio_intr_en = gpio_base + GPIO_INT_EN00; - ngpio = 41; - break; - case XLP_GPIO_VARIANT_XLP208: - case XLP_GPIO_VARIANT_XLP316: - priv->gpio_out_en = gpio_base + GPIO_OUTPUT_EN; - priv->gpio_paddrv = gpio_base + GPIO_PADDRV; - priv->gpio_intr_stat = gpio_base + GPIO_3XX_INT_STAT; - priv->gpio_intr_type = gpio_base + GPIO_3XX_INT_TYPE; - priv->gpio_intr_pol = gpio_base + GPIO_3XX_INT_POL; - priv->gpio_intr_en = gpio_base + GPIO_3XX_INT_EN00; - - ngpio = (soc_type == XLP_GPIO_VARIANT_XLP208) ? 42 : 57; - break; - case XLP_GPIO_VARIANT_XLP980: - case XLP_GPIO_VARIANT_XLP532: - case GPIO_VARIANT_VULCAN: - priv->gpio_out_en = gpio_base + GPIO_9XX_OUTPUT_EN; - priv->gpio_paddrv = gpio_base + GPIO_9XX_PADDRV; - priv->gpio_intr_stat = gpio_base + GPIO_9XX_INT_STAT; - priv->gpio_intr_type = gpio_base + GPIO_9XX_INT_TYPE; - priv->gpio_intr_pol = gpio_base + GPIO_9XX_INT_POL; - priv->gpio_intr_en = gpio_base + GPIO_9XX_INT_EN00; - - if (soc_type == XLP_GPIO_VARIANT_XLP980) - ngpio = 66; - else if (soc_type == XLP_GPIO_VARIANT_XLP532) - ngpio = 67; - else - ngpio = 70; - break; - default: - dev_err(&pdev->dev, "Unknown Processor type!\n"); - return -ENODEV; - } + priv->gpio_out_en = gpio_base + GPIO_9XX_OUTPUT_EN; + priv->gpio_paddrv = gpio_base + GPIO_9XX_PADDRV; + priv->gpio_intr_stat = gpio_base + GPIO_9XX_INT_STAT; + priv->gpio_intr_type = gpio_base + GPIO_9XX_INT_TYPE; + priv->gpio_intr_pol = gpio_base + GPIO_9XX_INT_POL; + priv->gpio_intr_en = gpio_base + GPIO_9XX_INT_EN00; bitmap_zero(priv->gpio_enabled_mask, XLP_MAX_NR_GPIO); @@ -373,7 +263,7 @@ static int xlp_gpio_probe(struct platform_device *pdev) gc->label = dev_name(&pdev->dev); gc->base = 0; gc->parent = &pdev->dev; - gc->ngpio = ngpio; + gc->ngpio = 70; gc->of_node = pdev->dev.of_node; gc->direction_output = xlp_gpio_dir_output; gc->direction_input = xlp_gpio_dir_input; @@ -382,19 +272,6 @@ static int xlp_gpio_probe(struct platform_device *pdev) spin_lock_init(&priv->lock); - /* XLP(MIPS) has fixed range for GPIO IRQs, Vulcan(ARM64) does not */ - if (soc_type != GPIO_VARIANT_VULCAN) { - irq_base = devm_irq_alloc_descs(&pdev->dev, -1, - XLP_GPIO_IRQ_BASE, - gc->ngpio, 0); - if (irq_base < 0) { - dev_err(&pdev->dev, "Failed to allocate IRQ numbers\n"); - return irq_base; - } - } else { - irq_base = 0; - } - girq = &gc->irq; girq->chip = &xlp_gpio_irq_chip; girq->parent_handler = xlp_gpio_generic_handler; @@ -405,7 +282,7 @@ static int xlp_gpio_probe(struct platform_device *pdev) if (!girq->parents) return -ENOMEM; girq->parents[0] = irq; - girq->first = irq_base; + girq->first = 0; girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_level_irq; @@ -430,7 +307,6 @@ MODULE_DEVICE_TABLE(acpi, xlp_gpio_acpi_match); static struct platform_driver xlp_gpio_driver = { .driver = { .name = "xlp-gpio", - .of_match_table = xlp_gpio_of_ids, .acpi_match_table = ACPI_PTR(xlp_gpio_acpi_match), }, .probe = xlp_gpio_probe, -- cgit v1.2.3 From 507805b83ff108473dba9d4909e41abd50cf07f5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 10 Nov 2021 15:47:42 +0200 Subject: gpiolib: acpi: Remove never used devm_acpi_dev_remove_driver_gpios() Remove never used devm_acpi_dev_remove_driver_gpios(). Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib-acpi.c | 6 ------ include/linux/gpio/consumer.h | 2 -- 2 files changed, 8 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 985e8589c58b..25ecc0a37054 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -604,12 +604,6 @@ int devm_acpi_dev_add_driver_gpios(struct device *dev, } EXPORT_SYMBOL_GPL(devm_acpi_dev_add_driver_gpios); -void devm_acpi_dev_remove_driver_gpios(struct device *dev) -{ - WARN_ON(devres_release(dev, devm_acpi_dev_release_driver_gpios, NULL, NULL)); -} -EXPORT_SYMBOL_GPL(devm_acpi_dev_remove_driver_gpios); - static bool acpi_get_driver_gpio_data(struct acpi_device *adev, const char *name, int index, struct fwnode_reference_args *args, diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 97a28ad3393b..3ad67b4a72be 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -690,7 +690,6 @@ void acpi_dev_remove_driver_gpios(struct acpi_device *adev); int devm_acpi_dev_add_driver_gpios(struct device *dev, const struct acpi_gpio_mapping *gpios); -void devm_acpi_dev_remove_driver_gpios(struct device *dev); struct gpio_desc *acpi_get_and_request_gpiod(char *path, int pin, char *label); @@ -708,7 +707,6 @@ static inline int devm_acpi_dev_add_driver_gpios(struct device *dev, { return -ENXIO; } -static inline void devm_acpi_dev_remove_driver_gpios(struct device *dev) {} #endif /* CONFIG_GPIOLIB && CONFIG_ACPI */ -- cgit v1.2.3 From 2ff64a84bbb3ea0281899766d9a944fd18db7013 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 10 Nov 2021 15:47:43 +0200 Subject: gpiolib: acpi: shrink devm_acpi_dev_add_driver_gpios() If all we want to manage is a single pointer, there's no need to manually allocate and add a new devres. We can simply use devm_add_action_or_reset() and shrink the code by a good bit. Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib-acpi.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 25ecc0a37054..7dd0484b89c6 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -579,28 +579,22 @@ void acpi_dev_remove_driver_gpios(struct acpi_device *adev) } EXPORT_SYMBOL_GPL(acpi_dev_remove_driver_gpios); -static void devm_acpi_dev_release_driver_gpios(struct device *dev, void *res) +static void acpi_dev_release_driver_gpios(void *adev) { - acpi_dev_remove_driver_gpios(ACPI_COMPANION(dev)); + acpi_dev_remove_driver_gpios(adev); } int devm_acpi_dev_add_driver_gpios(struct device *dev, const struct acpi_gpio_mapping *gpios) { - void *res; + struct acpi_device *adev = ACPI_COMPANION(dev); int ret; - res = devres_alloc(devm_acpi_dev_release_driver_gpios, 0, GFP_KERNEL); - if (!res) - return -ENOMEM; - - ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(dev), gpios); - if (ret) { - devres_free(res); + ret = acpi_dev_add_driver_gpios(adev, gpios); + if (ret) return ret; - } - devres_add(dev, res); - return 0; + + return devm_add_action_or_reset(dev, acpi_dev_release_driver_gpios, adev); } EXPORT_SYMBOL_GPL(devm_acpi_dev_add_driver_gpios); -- cgit v1.2.3 From bdfd6ab8fdccd8b138837efff66f4a1911496378 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 25 Nov 2021 21:30:10 +0100 Subject: gpiolib: acpi: Do not set the IRQ type if the IRQ is already in use If the IRQ is already in use, then acpi_dev_gpio_irq_get_by() really should not change the type underneath the current owner. I specifically hit an issue with this an a Chuwi Hi8 Super (CWI509) Bay Trail tablet, when the Boot OS selection in the BIOS is set to Android. In this case _STA for a MAX17047 ACPI I2C device wrongly returns 0xf and the _CRS resources for this device include a GpioInt pointing to a GPIO already in use by an _AEI handler, with a different type then specified in the _CRS for the MAX17047 device. Leading to the acpi_dev_gpio_irq_get() call done by the i2c-core-acpi.c code changing the type breaking the _AEI handler. Now this clearly is a bug in the DSDT of this tablet (in Android mode), but in general calling irq_set_irq_type() on an IRQ which already is in use seems like a bad idea. Signed-off-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/gpio/gpiolib-acpi.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 7dd0484b89c6..02b06e69b50b 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -1044,10 +1044,17 @@ int acpi_dev_gpio_irq_get_by(struct acpi_device *adev, const char *name, int ind irq_flags = acpi_dev_get_irq_type(info.triggering, info.polarity); - /* Set type if specified and different than the current one */ - if (irq_flags != IRQ_TYPE_NONE && - irq_flags != irq_get_trigger_type(irq)) - irq_set_irq_type(irq, irq_flags); + /* + * If the IRQ is not already in use then set type + * if specified and different than the current one. + */ + if (can_request_irq(irq, irq_flags)) { + if (irq_flags != IRQ_TYPE_NONE && + irq_flags != irq_get_trigger_type(irq)) + irq_set_irq_type(irq, irq_flags); + } else { + dev_dbg(&adev->dev, "IRQ %d already in use\n", irq); + } return irq; } -- cgit v1.2.3 From be3dc15ffe644d1b8bfae4a05eae3dc413a7c5e7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 Nov 2021 12:18:37 +0200 Subject: gpiolib: acpi: Unify debug and other messages format When ACPI device pointer available use it, otherwise take parent of GPIO chip. Signed-off-by: Andy Shevchenko --- drivers/gpio/gpiolib-acpi.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 02b06e69b50b..c7a0e56593e7 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -219,14 +219,13 @@ EXPORT_SYMBOL_GPL(acpi_gpio_get_io_resource); static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio, struct acpi_gpio_event *event) { + struct device *parent = acpi_gpio->chip->parent; int ret, value; ret = request_threaded_irq(event->irq, NULL, event->handler, event->irqflags | IRQF_ONESHOT, "ACPI:Event", event); if (ret) { - dev_err(acpi_gpio->chip->parent, - "Failed to setup interrupt handler for %d\n", - event->irq); + dev_err(parent, "Failed to setup interrupt handler for %d\n", event->irq); return; } @@ -347,8 +346,7 @@ static bool acpi_gpio_in_ignore_list(const char *controller_in, int pin_in) return false; err: - pr_err_once("Error invalid value for gpiolib_acpi.ignore_wake: %s\n", - ignore_wake); + pr_err_once("Error: Invalid value for gpiolib_acpi.ignore_wake: %s\n", ignore_wake); return false; } @@ -929,7 +927,7 @@ struct gpio_desc *acpi_find_gpio(struct device *dev, if (info.gpioint && (*dflags == GPIOD_OUT_LOW || *dflags == GPIOD_OUT_HIGH)) { - dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n"); + dev_dbg(&adev->dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n"); return ERR_PTR(-ENOENT); } -- cgit v1.2.3 From 4f45348934074553681a8964bae740a22599cf2e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 25 Nov 2021 16:20:43 +0100 Subject: dt-bindings: gpio: sifive,gpio: Group interrupt tuples To improve human readability and enable automatic validation, the tuples in "interrupts" properties should be grouped using angle brackets. Signed-off-by: Geert Uytterhoeven Acked-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/sifive,gpio.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/gpio/sifive,gpio.yaml b/Documentation/devicetree/bindings/gpio/sifive,gpio.yaml index c2902aac2514..e04349567eeb 100644 --- a/Documentation/devicetree/bindings/gpio/sifive,gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/sifive,gpio.yaml @@ -77,7 +77,8 @@ examples: gpio@10060000 { compatible = "sifive,fu540-c000-gpio", "sifive,gpio0"; interrupt-parent = <&plic>; - interrupts = <7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22>; + interrupts = <7>, <8>, <9>, <10>, <11>, <12>, <13>, <14>, <15>, <16>, + <17>, <18>, <19>, <20>, <21>, <22>; reg = <0x10060000 0x1000>; clocks = <&tlclk PRCI_CLK_TLCLK>; gpio-controller; -- cgit v1.2.3 From 82b2cd4c8caebf0b61b39daf5e0ed6be170a4ae1 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 30 Nov 2021 16:08:37 -0600 Subject: gpio: pch: Use .driver_data instead of checking Device IDs again Previously, pch_gpio_probe() tested the Device ID to determine the type of IOH. But the driver core has already matched the Device ID with one of the IDs in the pch_gpio_pcidev_id[] table, and we can supply the IOH type there as .driver_data. Use the pci_device_id.driver_data to learn the IOH type instead of testing the Device ID again. No functional change intended. Signed-off-by: Bjorn Helgaas Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-pch.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index a552df298a97..625920421990 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -368,14 +368,7 @@ static int pch_gpio_probe(struct pci_dev *pdev, } chip->base = pcim_iomap_table(pdev)[1]; - - if (pdev->device == 0x8803) - chip->ioh = INTEL_EG20T_PCH; - else if (pdev->device == 0x8014) - chip->ioh = OKISEMI_ML7223m_IOH; - else if (pdev->device == 0x8043) - chip->ioh = OKISEMI_ML7223n_IOH; - + chip->ioh = id->driver_data; chip->reg = chip->base; pci_set_drvdata(pdev, chip); spin_lock_init(&chip->spinlock); @@ -439,10 +432,14 @@ 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) }, - { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8014) }, - { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8043) }, - { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8803) }, + { 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, } }; MODULE_DEVICE_TABLE(pci, pch_gpio_pcidev_id); -- cgit v1.2.3 From 2822b02765ed0609825d3532ea15de3914b59f09 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 30 Nov 2021 16:08:38 -0600 Subject: gpio: pch: Cache &pdev->dev to reduce repetition pch_gpio_probe() repeats the "&pdev->dev" expression several times. Cache the result as "struct device *dev" to reduce the repetition. No functional change intended. Signed-off-by: Bjorn Helgaas Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-pch.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 625920421990..3a0bd8795741 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -346,24 +346,25 @@ static int pch_gpio_alloc_generic_chip(struct pch_gpio *chip, static int pch_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id) { + struct device *dev = &pdev->dev; s32 ret; struct pch_gpio *chip; int irq_base; - chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; - chip->dev = &pdev->dev; + chip->dev = dev; ret = pcim_enable_device(pdev); if (ret) { - dev_err(&pdev->dev, "pci_enable_device FAILED"); + dev_err(dev, "pci_enable_device FAILED"); return ret; } ret = pcim_iomap_regions(pdev, BIT(1), KBUILD_MODNAME); if (ret) { - dev_err(&pdev->dev, "pci_request_regions FAILED-%d", ret); + dev_err(dev, "pci_request_regions FAILED-%d", ret); return ret; } @@ -374,16 +375,16 @@ static int pch_gpio_probe(struct pci_dev *pdev, spin_lock_init(&chip->spinlock); pch_gpio_setup(chip); - ret = devm_gpiochip_add_data(&pdev->dev, &chip->gpio, chip); + ret = devm_gpiochip_add_data(dev, &chip->gpio, chip); if (ret) { - dev_err(&pdev->dev, "PCH gpio: Failed to register GPIO\n"); + dev_err(dev, "PCH gpio: Failed to register GPIO\n"); return ret; } - irq_base = devm_irq_alloc_descs(&pdev->dev, -1, 0, + irq_base = devm_irq_alloc_descs(dev, -1, 0, gpio_pins[chip->ioh], NUMA_NO_NODE); if (irq_base < 0) { - dev_warn(&pdev->dev, "PCH gpio: Failed to get IRQ base num\n"); + dev_warn(dev, "PCH gpio: Failed to get IRQ base num\n"); chip->irq_base = -1; return 0; } @@ -393,10 +394,10 @@ static int pch_gpio_probe(struct pci_dev *pdev, iowrite32(BIT(gpio_pins[chip->ioh]) - 1, &chip->reg->imask); iowrite32(BIT(gpio_pins[chip->ioh]) - 1, &chip->reg->ien); - ret = devm_request_irq(&pdev->dev, pdev->irq, pch_gpio_handler, + ret = devm_request_irq(dev, pdev->irq, pch_gpio_handler, IRQF_SHARED, KBUILD_MODNAME, chip); if (ret) { - dev_err(&pdev->dev, "request_irq failed\n"); + dev_err(dev, "request_irq failed\n"); return ret; } -- cgit v1.2.3 From 06939f22ae5f7abf80d9a6ff5e43b4a916256f44 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 30 Nov 2021 16:08:39 -0600 Subject: gpio: ml-ioh: Cache &pdev->dev to reduce repetition ioh_gpio_probe() repeats the "&pdev->dev" expression several times. Cache the result as "struct device *dev" to reduce the repetition. No functional change intended. Signed-off-by: Bjorn Helgaas Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-ml-ioh.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index efa9acdc320a..4e9528dd1152 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -401,6 +401,7 @@ static int ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip, static int ioh_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id) { + struct device *dev = &pdev->dev; int ret; int i, j; struct ioh_gpio *chip; @@ -410,19 +411,19 @@ static int ioh_gpio_probe(struct pci_dev *pdev, ret = pci_enable_device(pdev); if (ret) { - dev_err(&pdev->dev, "%s : pci_enable_device failed", __func__); + dev_err(dev, "%s : pci_enable_device failed", __func__); goto err_pci_enable; } ret = pci_request_regions(pdev, KBUILD_MODNAME); if (ret) { - dev_err(&pdev->dev, "pci_request_regions failed-%d", ret); + dev_err(dev, "pci_request_regions failed-%d", ret); goto err_request_regions; } base = pci_iomap(pdev, 1, 0); if (!base) { - dev_err(&pdev->dev, "%s : pci_iomap failed", __func__); + dev_err(dev, "%s : pci_iomap failed", __func__); ret = -ENOMEM; goto err_iomap; } @@ -435,7 +436,7 @@ static int ioh_gpio_probe(struct pci_dev *pdev, chip = chip_save; for (i = 0; i < 8; i++, chip++) { - chip->dev = &pdev->dev; + chip->dev = dev; chip->base = base; chip->reg = chip->base; chip->ch = i; @@ -443,17 +444,17 @@ static int ioh_gpio_probe(struct pci_dev *pdev, ioh_gpio_setup(chip, num_ports[i]); ret = gpiochip_add_data(&chip->gpio, chip); if (ret) { - dev_err(&pdev->dev, "IOH gpio: Failed to register GPIO\n"); + dev_err(dev, "IOH gpio: Failed to register GPIO\n"); goto err_gpiochip_add; } } chip = chip_save; for (j = 0; j < 8; j++, chip++) { - irq_base = devm_irq_alloc_descs(&pdev->dev, -1, IOH_IRQ_BASE, + irq_base = devm_irq_alloc_descs(dev, -1, IOH_IRQ_BASE, num_ports[j], NUMA_NO_NODE); if (irq_base < 0) { - dev_warn(&pdev->dev, + dev_warn(dev, "ml_ioh_gpio: Failed to get IRQ base num\n"); ret = irq_base; goto err_gpiochip_add; @@ -467,11 +468,10 @@ static int ioh_gpio_probe(struct pci_dev *pdev, } chip = chip_save; - ret = devm_request_irq(&pdev->dev, pdev->irq, ioh_gpio_handler, + ret = devm_request_irq(dev, pdev->irq, ioh_gpio_handler, IRQF_SHARED, KBUILD_MODNAME, chip); if (ret != 0) { - dev_err(&pdev->dev, - "%s request_irq failed\n", __func__); + dev_err(dev, "%s request_irq failed\n", __func__); goto err_gpiochip_add; } @@ -498,7 +498,7 @@ err_request_regions: err_pci_enable: - dev_err(&pdev->dev, "%s Failed returns %d\n", __func__, ret); + dev_err(dev, "%s Failed returns %d\n", __func__, ret); return ret; } -- cgit v1.2.3 From 46155a0c55eb9c64da619e4f3a03537f47fbe583 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 30 Nov 2021 16:08:40 -0600 Subject: gpio: ml-ioh: Use BIT() to match gpio-pch.c The ML IOH driver is very similar to the PCH driver. To make it more similar, replace "1 << nr" with "BIT(nr)". No functional change intended. Signed-off-by: Bjorn Helgaas Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-ml-ioh.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 4e9528dd1152..0fb9c8bc9b2d 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -98,9 +98,9 @@ static void ioh_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) spin_lock_irqsave(&chip->spinlock, flags); reg_val = ioread32(&chip->reg->regs[chip->ch].po); if (val) - reg_val |= (1 << nr); + reg_val |= BIT(nr); else - reg_val &= ~(1 << nr); + reg_val &= ~BIT(nr); iowrite32(reg_val, &chip->reg->regs[chip->ch].po); spin_unlock_irqrestore(&chip->spinlock, flags); @@ -110,7 +110,7 @@ static int ioh_gpio_get(struct gpio_chip *gpio, unsigned nr) { struct ioh_gpio *chip = gpiochip_get_data(gpio); - return !!(ioread32(&chip->reg->regs[chip->ch].pi) & (1 << nr)); + return !!(ioread32(&chip->reg->regs[chip->ch].pi) & BIT(nr)); } static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, @@ -123,15 +123,15 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, spin_lock_irqsave(&chip->spinlock, flags); pm = ioread32(&chip->reg->regs[chip->ch].pm) & - ((1 << num_ports[chip->ch]) - 1); - pm |= (1 << nr); + (BIT(num_ports[chip->ch]) - 1); + pm |= BIT(nr); iowrite32(pm, &chip->reg->regs[chip->ch].pm); reg_val = ioread32(&chip->reg->regs[chip->ch].po); if (val) - reg_val |= (1 << nr); + reg_val |= BIT(nr); else - reg_val &= ~(1 << nr); + reg_val &= ~BIT(nr); iowrite32(reg_val, &chip->reg->regs[chip->ch].po); spin_unlock_irqrestore(&chip->spinlock, flags); @@ -147,8 +147,8 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) spin_lock_irqsave(&chip->spinlock, flags); pm = ioread32(&chip->reg->regs[chip->ch].pm) & - ((1 << num_ports[chip->ch]) - 1); - pm &= ~(1 << nr); + (BIT(num_ports[chip->ch]) - 1); + pm &= ~BIT(nr); iowrite32(pm, &chip->reg->regs[chip->ch].pm); spin_unlock_irqrestore(&chip->spinlock, flags); @@ -304,7 +304,7 @@ static void ioh_irq_unmask(struct irq_data *d) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct ioh_gpio *chip = gc->private; - iowrite32(1 << (d->irq - chip->irq_base), + iowrite32(BIT(d->irq - chip->irq_base), &chip->reg->regs[chip->ch].imaskclr); } @@ -313,7 +313,7 @@ static void ioh_irq_mask(struct irq_data *d) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct ioh_gpio *chip = gc->private; - iowrite32(1 << (d->irq - chip->irq_base), + iowrite32(BIT(d->irq - chip->irq_base), &chip->reg->regs[chip->ch].imask); } @@ -326,7 +326,7 @@ static void ioh_irq_disable(struct irq_data *d) spin_lock_irqsave(&chip->spinlock, flags); ien = ioread32(&chip->reg->regs[chip->ch].ien); - ien &= ~(1 << (d->irq - chip->irq_base)); + ien &= ~BIT(d->irq - chip->irq_base); iowrite32(ien, &chip->reg->regs[chip->ch].ien); spin_unlock_irqrestore(&chip->spinlock, flags); } @@ -340,7 +340,7 @@ static void ioh_irq_enable(struct irq_data *d) spin_lock_irqsave(&chip->spinlock, flags); ien = ioread32(&chip->reg->regs[chip->ch].ien); - ien |= 1 << (d->irq - chip->irq_base); + ien |= BIT(d->irq - chip->irq_base); iowrite32(ien, &chip->reg->regs[chip->ch].ien); spin_unlock_irqrestore(&chip->spinlock, flags); } -- cgit v1.2.3 From 7bc14ff2952da56d445efab50256569fc96aa95b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 30 Nov 2021 16:08:41 -0600 Subject: gpio: ml-ioh: Change whitespace to match gpio-pch.c The ML IOH driver is very similar to the PCH driver. To make it more similar, tweak the whitespace in ioh_gpio_direction_output() and ioh_gpio_direction_input(). No functional change intended. Signed-off-by: Bjorn Helgaas Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-ml-ioh.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 0fb9c8bc9b2d..b060c4773698 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -122,8 +122,8 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, unsigned long flags; spin_lock_irqsave(&chip->spinlock, flags); - pm = ioread32(&chip->reg->regs[chip->ch].pm) & - (BIT(num_ports[chip->ch]) - 1); + pm = ioread32(&chip->reg->regs[chip->ch].pm); + pm &= BIT(num_ports[chip->ch]) - 1; pm |= BIT(nr); iowrite32(pm, &chip->reg->regs[chip->ch].pm); @@ -146,8 +146,8 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) unsigned long flags; spin_lock_irqsave(&chip->spinlock, flags); - pm = ioread32(&chip->reg->regs[chip->ch].pm) & - (BIT(num_ports[chip->ch]) - 1); + pm = ioread32(&chip->reg->regs[chip->ch].pm); + pm &= BIT(num_ports[chip->ch]) - 1; pm &= ~BIT(nr); iowrite32(pm, &chip->reg->regs[chip->ch].pm); spin_unlock_irqrestore(&chip->spinlock, flags); -- cgit v1.2.3 From e1610431b95ccbada74e1393b0944ef4c2750624 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 30 Nov 2021 18:49:56 +0200 Subject: gpio: dwapb: clarify usage of the register file version First of all, it's obvious that different versions can't be provided simultaneously. Hence, versions can't be bit masks. Second, due to above we have to mask out the version field in the flags and only that can be evaluated against the certain version. Clarify all above by: - introducing GPIO_REG_OFFSET_V1 and GPIO_REG_OFFSET_MASK - replacing conditional to mask out bits and compare to a version Luckily there is no functional change, so no need to backport this. Signed-off-by: Andy Shevchenko Acked-by: Serge Semin Reviewed-by: Bartosz Golaszewski --- drivers/gpio/gpio-dwapb.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index f98fa33e1679..ec0767d7800d 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -53,7 +53,9 @@ #define GPIO_SWPORT_DR_STRIDE 0x0c /* register stride 3*32 bits */ #define GPIO_SWPORT_DDR_STRIDE 0x0c /* register stride 3*32 bits */ +#define GPIO_REG_OFFSET_V1 0 #define GPIO_REG_OFFSET_V2 1 +#define GPIO_REG_OFFSET_MASK BIT(0) #define GPIO_INTMASK_V2 0x44 #define GPIO_INTTYPE_LEVEL_V2 0x34 @@ -141,7 +143,7 @@ static inline u32 gpio_reg_v2_convert(unsigned int offset) static inline u32 gpio_reg_convert(struct dwapb_gpio *gpio, unsigned int offset) { - if (gpio->flags & GPIO_REG_OFFSET_V2) + if ((gpio->flags & GPIO_REG_OFFSET_MASK) == GPIO_REG_OFFSET_V2) return gpio_reg_v2_convert(offset); return offset; @@ -668,15 +670,15 @@ static int dwapb_get_clks(struct dwapb_gpio *gpio) } static const struct of_device_id dwapb_of_match[] = { - { .compatible = "snps,dw-apb-gpio", .data = (void *)0}, + { .compatible = "snps,dw-apb-gpio", .data = (void *)GPIO_REG_OFFSET_V1}, { .compatible = "apm,xgene-gpio-v2", .data = (void *)GPIO_REG_OFFSET_V2}, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, dwapb_of_match); static const struct acpi_device_id dwapb_acpi_match[] = { - {"HISI0181", 0}, - {"APMC0D07", 0}, + {"HISI0181", GPIO_REG_OFFSET_V1}, + {"APMC0D07", GPIO_REG_OFFSET_V1}, {"APMC0D81", GPIO_REG_OFFSET_V2}, { } }; -- cgit v1.2.3 From 49fdfe66400614ee1c484057c79dd6642c535fd4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 3 Dec 2021 14:48:17 +0100 Subject: gpiolib: Let gpiod_add_lookup_table() call gpiod_add_lookup_tables() This saves 20 bytes on arm32, and 44 bytes on arm64. Signed-off-by: Geert Uytterhoeven Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index abfbf546d159..85168f88a7fe 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3487,11 +3487,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); */ void gpiod_add_lookup_table(struct gpiod_lookup_table *table) { - mutex_lock(&gpio_lookup_lock); - - list_add_tail(&table->list, &gpio_lookup_list); - - mutex_unlock(&gpio_lookup_lock); + gpiod_add_lookup_tables(&table, 1); } EXPORT_SYMBOL_GPL(gpiod_add_lookup_table); -- cgit v1.2.3 From a00128dfc8fc0cc8848d9168d6e7cfff99bd46f0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 3 Dec 2021 15:06:44 +0100 Subject: gpio: aggregator: Add interrupt support Currently the GPIO Aggregator does not support interrupts. This means that kernel drivers going from a GPIO to an IRQ using gpiod_to_irq(), and userspace applications using line events do not work. Add interrupt support by providing a gpio_chip.to_irq() callback, which just calls into the parent GPIO controller. Note that this does not implement full interrupt controller (irq_chip) support, so using e.g. gpio-keys with "interrupts" instead of "gpios" still does not work. Signed-off-by: Geert Uytterhoeven Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-aggregator.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index e9671d1660ef..869dc952cf45 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -371,6 +371,13 @@ static int gpio_fwd_set_config(struct gpio_chip *chip, unsigned int offset, return gpiod_set_config(fwd->descs[offset], config); } +static int gpio_fwd_to_irq(struct gpio_chip *chip, unsigned int offset) +{ + struct gpiochip_fwd *fwd = gpiochip_get_data(chip); + + return gpiod_to_irq(fwd->descs[offset]); +} + /** * gpiochip_fwd_create() - Create a new GPIO forwarder * @dev: Parent device pointer @@ -411,7 +418,8 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev, for (i = 0; i < ngpios; i++) { struct gpio_chip *parent = gpiod_to_chip(descs[i]); - dev_dbg(dev, "%u => gpio-%d\n", i, desc_to_gpio(descs[i])); + dev_dbg(dev, "%u => gpio %d irq %d\n", i, + desc_to_gpio(descs[i]), gpiod_to_irq(descs[i])); if (gpiod_cansleep(descs[i])) chip->can_sleep = true; @@ -429,6 +437,7 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev, chip->get_multiple = gpio_fwd_get_multiple_locked; chip->set = gpio_fwd_set; chip->set_multiple = gpio_fwd_set_multiple_locked; + chip->to_irq = gpio_fwd_to_irq; chip->base = -1; chip->ngpio = ngpios; fwd->descs = descs; -- cgit v1.2.3 From adc8b4bf2a7f6f513c560bb8e225d4750ce5e872 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Thu, 2 Dec 2021 15:50:21 +0000 Subject: gpio: rockchip: lock GPIOs used as interrupts Use the standard gpiochip request/release resource functions so that any GPIOs used as interrupts are properly locked. This allows libgpiod to correctly show these GPIOs as in-use. Signed-off-by: John Keeping Tested-by: Nicolas Frattaroli Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rockchip.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index ce63cbd14d69..c1b8e5dbbcc4 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -465,6 +465,22 @@ out: return ret; } +static int rockchip_irq_reqres(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct rockchip_pin_bank *bank = gc->private; + + return gpiochip_reqres_irq(&bank->gpio_chip, d->hwirq); +} + +static void rockchip_irq_relres(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct rockchip_pin_bank *bank = gc->private; + + gpiochip_relres_irq(&bank->gpio_chip, d->hwirq); +} + static void rockchip_irq_suspend(struct irq_data *d) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); @@ -536,6 +552,8 @@ static int rockchip_interrupts_register(struct rockchip_pin_bank *bank) gc->chip_types[0].chip.irq_suspend = rockchip_irq_suspend; gc->chip_types[0].chip.irq_resume = rockchip_irq_resume; gc->chip_types[0].chip.irq_set_type = rockchip_irq_set_type; + gc->chip_types[0].chip.irq_request_resources = rockchip_irq_reqres; + gc->chip_types[0].chip.irq_release_resources = rockchip_irq_relres; gc->wake_enabled = IRQ_MSK(bank->nr_pins); /* -- cgit v1.2.3 From e320d9c2e900d988c82021c80a3cbff488977946 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 1 Dec 2021 15:43:05 -0600 Subject: gpio: xlp: Fix build errors from Netlogic XLP removal Commit ea708ac5bf41 ("gpio: xlp: Remove Netlogic XLP variants") has build errors with CONFIG_ACPI: drivers/gpio/gpio-xlp.c:300:23: error: 'GPIO_VARIANT_VULCAN' undeclared here (not in a function) and !CONFIG_OF: drivers/gpio/gpio-xlp.c:267:11: error: 'struct gpio_chip' has no member named 'of_node' Fix these errors. Fixes: ea708ac5bf41 ("gpio: xlp: Remove Netlogic XLP variants") Reported-by: Stephen Rothwell Reported-by: kernel test robot Cc: Linus Walleij Cc: Bartosz Golaszewski Cc: linux-gpio@vger.kernel.org Signed-off-by: Rob Herring Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xlp.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c index 814cc34aef97..0199f545335f 100644 --- a/drivers/gpio/gpio-xlp.c +++ b/drivers/gpio/gpio-xlp.c @@ -264,7 +264,6 @@ static int xlp_gpio_probe(struct platform_device *pdev) gc->base = 0; gc->parent = &pdev->dev; gc->ngpio = 70; - gc->of_node = pdev->dev.of_node; gc->direction_output = xlp_gpio_dir_output; gc->direction_input = xlp_gpio_dir_input; gc->set = xlp_gpio_set; @@ -297,8 +296,8 @@ static int xlp_gpio_probe(struct platform_device *pdev) #ifdef CONFIG_ACPI static const struct acpi_device_id xlp_gpio_acpi_match[] = { - { "BRCM9006", GPIO_VARIANT_VULCAN }, - { "CAV9006", GPIO_VARIANT_VULCAN }, + { "BRCM9006" }, + { "CAV9006" }, {}, }; MODULE_DEVICE_TABLE(acpi, xlp_gpio_acpi_match); -- cgit v1.2.3 From e5ab49cd3d6937b1818b80cb5eb09dc018ae0718 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 2 Dec 2021 14:40:33 +0100 Subject: gpiolib: improve coding style for local variables Drop unneeded whitespaces and put the variables of the same type together for consistency with the rest of the code. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko --- drivers/gpio/gpiolib.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 85168f88a7fe..bc5ba52865ec 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -594,11 +594,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, struct lock_class_key *request_key) { struct fwnode_handle *fwnode = gc->parent ? dev_fwnode(gc->parent) : NULL; - unsigned long flags; - int ret = 0; - unsigned i; - int base = gc->base; struct gpio_device *gdev; + unsigned long flags; + int base = gc->base; + unsigned int i; + int ret = 0; /* * First: allocate and populate the internal stat container, and -- cgit v1.2.3 From 9dbd1ab20509e85cd3fac9479a00c59e83c08196 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 2 Dec 2021 14:40:34 +0100 Subject: gpiolib: check the 'ngpios' property in core gpiolib code Several drivers read the 'ngpios' device property on their own, but since it's defined as a standard GPIO property in the device tree bindings anyway, it's a good candidate for generalization. If the driver didn't set its gc->ngpio, try to read the 'ngpios' property from the GPIO device's firmware node before bailing out. Signed-off-by: Bartosz Golaszewski Suggested-by: Andy Shevchenko Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko --- drivers/gpio/gpiolib.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index bc5ba52865ec..535eb1b509c1 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -599,6 +599,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, int base = gc->base; unsigned int i; int ret = 0; + u32 ngpios; /* * First: allocate and populate the internal stat container, and @@ -646,6 +647,26 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, goto err_free_dev_name; } + /* + * Try the device properties if the driver didn't supply the number + * of GPIO lines. + */ + if (gc->ngpio == 0) { + ret = device_property_read_u32(&gdev->dev, "ngpios", &ngpios); + if (ret == -ENODATA) + /* + * -ENODATA means that there is no property found and + * we want to issue the error message to the user. + * Besides that, we want to return different error code + * to state that supplied value is not valid. + */ + ngpios = 0; + else if (ret) + goto err_free_descs; + + gc->ngpio = ngpios; + } + if (gc->ngpio == 0) { chip_err(gc, "tried to insert a GPIO chip with zero lines\n"); ret = -EINVAL; -- cgit v1.2.3 From 61a7904b6ace99b1bde0d0e867fa3097f5c8cee2 Mon Sep 17 00:00:00 2001 From: Iwona Winiarska Date: Sat, 4 Dec 2021 18:10:26 +0100 Subject: gpio: aspeed: Convert aspeed_gpio.lock to raw_spinlock The gpio-aspeed driver implements an irq_chip which need to be invoked from hardirq context. Since spin_lock() can sleep with PREEMPT_RT, it is no longer legal to invoke it while interrupts are disabled. This also causes lockdep to complain about: [ 0.649797] [ BUG: Invalid wait context ] because aspeed_gpio.lock (spin_lock_t) is taken under irq_desc.lock (raw_spinlock_t). Let's use of raw_spinlock_t instead of spinlock_t. Signed-off-by: Iwona Winiarska Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-aspeed.c | 52 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 3c8f20c57695..318a7d95a1a8 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -53,7 +53,7 @@ struct aspeed_gpio_config { struct aspeed_gpio { struct gpio_chip chip; struct irq_chip irqc; - spinlock_t lock; + raw_spinlock_t lock; void __iomem *base; int irq; const struct aspeed_gpio_config *config; @@ -413,14 +413,14 @@ static void aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset, unsigned long flags; bool copro; - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); copro = aspeed_gpio_copro_request(gpio, offset); __aspeed_gpio_set(gc, offset, val); if (copro) aspeed_gpio_copro_release(gpio, offset); - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); } static int aspeed_gpio_dir_in(struct gpio_chip *gc, unsigned int offset) @@ -435,7 +435,7 @@ static int aspeed_gpio_dir_in(struct gpio_chip *gc, unsigned int offset) if (!have_input(gpio, offset)) return -ENOTSUPP; - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); reg = ioread32(addr); reg &= ~GPIO_BIT(offset); @@ -445,7 +445,7 @@ static int aspeed_gpio_dir_in(struct gpio_chip *gc, unsigned int offset) if (copro) aspeed_gpio_copro_release(gpio, offset); - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); return 0; } @@ -463,7 +463,7 @@ static int aspeed_gpio_dir_out(struct gpio_chip *gc, if (!have_output(gpio, offset)) return -ENOTSUPP; - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); reg = ioread32(addr); reg |= GPIO_BIT(offset); @@ -474,7 +474,7 @@ static int aspeed_gpio_dir_out(struct gpio_chip *gc, if (copro) aspeed_gpio_copro_release(gpio, offset); - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); return 0; } @@ -492,11 +492,11 @@ static int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) if (!have_output(gpio, offset)) return GPIO_LINE_DIRECTION_IN; - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); val = ioread32(bank_reg(gpio, bank, reg_dir)) & GPIO_BIT(offset); - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); return val ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; } @@ -539,14 +539,14 @@ static void aspeed_gpio_irq_ack(struct irq_data *d) status_addr = bank_reg(gpio, bank, reg_irq_status); - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); copro = aspeed_gpio_copro_request(gpio, offset); iowrite32(bit, status_addr); if (copro) aspeed_gpio_copro_release(gpio, offset); - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); } static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set) @@ -565,7 +565,7 @@ static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set) addr = bank_reg(gpio, bank, reg_irq_enable); - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); copro = aspeed_gpio_copro_request(gpio, offset); reg = ioread32(addr); @@ -577,7 +577,7 @@ static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set) if (copro) aspeed_gpio_copro_release(gpio, offset); - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); } static void aspeed_gpio_irq_mask(struct irq_data *d) @@ -629,7 +629,7 @@ static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type) return -EINVAL; } - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); copro = aspeed_gpio_copro_request(gpio, offset); addr = bank_reg(gpio, bank, reg_irq_type0); @@ -649,7 +649,7 @@ static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type) if (copro) aspeed_gpio_copro_release(gpio, offset); - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); irq_set_handler_locked(d, handler); @@ -716,7 +716,7 @@ static int aspeed_gpio_reset_tolerance(struct gpio_chip *chip, treg = bank_reg(gpio, to_bank(offset), reg_tolerance); - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); copro = aspeed_gpio_copro_request(gpio, offset); val = readl(treg); @@ -730,7 +730,7 @@ static int aspeed_gpio_reset_tolerance(struct gpio_chip *chip, if (copro) aspeed_gpio_copro_release(gpio, offset); - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); return 0; } @@ -856,7 +856,7 @@ static int enable_debounce(struct gpio_chip *chip, unsigned int offset, return rc; } - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); if (timer_allocation_registered(gpio, offset)) { rc = unregister_allocated_timer(gpio, offset); @@ -916,7 +916,7 @@ static int enable_debounce(struct gpio_chip *chip, unsigned int offset, configure_timer(gpio, offset, i); out: - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); return rc; } @@ -927,13 +927,13 @@ static int disable_debounce(struct gpio_chip *chip, unsigned int offset) unsigned long flags; int rc; - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); rc = unregister_allocated_timer(gpio, offset); if (!rc) configure_timer(gpio, offset, 0); - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); return rc; } @@ -1015,7 +1015,7 @@ int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc, return -EINVAL; bindex = offset >> 3; - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); /* Sanity check, this shouldn't happen */ if (gpio->cf_copro_bankmap[bindex] == 0xff) { @@ -1036,7 +1036,7 @@ int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc, if (bit) *bit = GPIO_OFFSET(offset); bail: - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); return rc; } EXPORT_SYMBOL_GPL(aspeed_gpio_copro_grab_gpio); @@ -1060,7 +1060,7 @@ int aspeed_gpio_copro_release_gpio(struct gpio_desc *desc) return -EINVAL; bindex = offset >> 3; - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); /* Sanity check, this shouldn't happen */ if (gpio->cf_copro_bankmap[bindex] == 0) { @@ -1074,7 +1074,7 @@ int aspeed_gpio_copro_release_gpio(struct gpio_desc *desc) aspeed_gpio_change_cmd_source(gpio, bank, bindex, GPIO_CMDSRC_ARM); bail: - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); return rc; } EXPORT_SYMBOL_GPL(aspeed_gpio_copro_release_gpio); @@ -1148,7 +1148,7 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) if (IS_ERR(gpio->base)) return PTR_ERR(gpio->base); - spin_lock_init(&gpio->lock); + raw_spin_lock_init(&gpio->lock); gpio_id = of_match_node(aspeed_gpio_of_table, pdev->dev.of_node); if (!gpio_id) -- cgit v1.2.3 From ab39d6988dd53f354130438d8afa5596a2440fed Mon Sep 17 00:00:00 2001 From: Iwona Winiarska Date: Sat, 4 Dec 2021 18:10:27 +0100 Subject: gpio: aspeed-sgpio: Convert aspeed_sgpio.lock to raw_spinlock The gpio-aspeed-sgpio driver implements an irq_chip which need to be invoked from hardirq context. Since spin_lock() can sleep with PREEMPT_RT, it is no longer legal to invoke it while interrupts are disabled. This also causes lockdep to complain about: [ 25.919465] [ BUG: Invalid wait context ] because aspeed_sgpio.lock (spin_lock_t) is taken under irq_desc.lock (raw_spinlock_t). Let's use of raw_spinlock_t instead of spinlock_t. Signed-off-by: Iwona Winiarska Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-aspeed-sgpio.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c index 3d6ef37a7702..931d5c38d7de 100644 --- a/drivers/gpio/gpio-aspeed-sgpio.c +++ b/drivers/gpio/gpio-aspeed-sgpio.c @@ -31,7 +31,7 @@ struct aspeed_sgpio { struct gpio_chip chip; struct irq_chip intc; struct clk *pclk; - spinlock_t lock; + raw_spinlock_t lock; void __iomem *base; int irq; }; @@ -173,12 +173,12 @@ static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset) enum aspeed_sgpio_reg reg; int rc = 0; - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); reg = aspeed_sgpio_is_input(offset) ? reg_val : reg_rdata; rc = !!(ioread32(bank_reg(gpio, bank, reg)) & GPIO_BIT(offset)); - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); return rc; } @@ -215,11 +215,11 @@ static void aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val) struct aspeed_sgpio *gpio = gpiochip_get_data(gc); unsigned long flags; - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); sgpio_set_value(gc, offset, val); - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); } static int aspeed_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset) @@ -236,9 +236,9 @@ static int aspeed_sgpio_dir_out(struct gpio_chip *gc, unsigned int offset, int v /* No special action is required for setting the direction; we'll * error-out in sgpio_set_value if this isn't an output GPIO */ - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); rc = sgpio_set_value(gc, offset, val); - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); return rc; } @@ -277,11 +277,11 @@ static void aspeed_sgpio_irq_ack(struct irq_data *d) status_addr = bank_reg(gpio, bank, reg_irq_status); - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); iowrite32(bit, status_addr); - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); } static void aspeed_sgpio_irq_set_mask(struct irq_data *d, bool set) @@ -296,7 +296,7 @@ static void aspeed_sgpio_irq_set_mask(struct irq_data *d, bool set) irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); addr = bank_reg(gpio, bank, reg_irq_enable); - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); reg = ioread32(addr); if (set) @@ -306,7 +306,7 @@ static void aspeed_sgpio_irq_set_mask(struct irq_data *d, bool set) iowrite32(reg, addr); - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); } static void aspeed_sgpio_irq_mask(struct irq_data *d) @@ -355,7 +355,7 @@ static int aspeed_sgpio_set_type(struct irq_data *d, unsigned int type) return -EINVAL; } - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); addr = bank_reg(gpio, bank, reg_irq_type0); reg = ioread32(addr); @@ -372,7 +372,7 @@ static int aspeed_sgpio_set_type(struct irq_data *d, unsigned int type) reg = (reg & ~bit) | type2; iowrite32(reg, addr); - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); irq_set_handler_locked(d, handler); @@ -467,7 +467,7 @@ static int aspeed_sgpio_reset_tolerance(struct gpio_chip *chip, reg = bank_reg(gpio, to_bank(offset), reg_tolerance); - spin_lock_irqsave(&gpio->lock, flags); + raw_spin_lock_irqsave(&gpio->lock, flags); val = readl(reg); @@ -478,7 +478,7 @@ static int aspeed_sgpio_reset_tolerance(struct gpio_chip *chip, writel(val, reg); - spin_unlock_irqrestore(&gpio->lock, flags); + raw_spin_unlock_irqrestore(&gpio->lock, flags); return 0; } @@ -575,7 +575,7 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev) iowrite32(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, sgpio_clk_div) | gpio_cnt_regval | ASPEED_SGPIO_ENABLE, gpio->base + ASPEED_SGPIO_CTRL); - spin_lock_init(&gpio->lock); + raw_spin_lock_init(&gpio->lock); gpio->chip.parent = &pdev->dev; gpio->chip.ngpio = nr_gpios * 2; -- cgit v1.2.3 From 9d5f0f6644b1404f40266a2682add712dc9931f5 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Sun, 12 Dec 2021 11:11:08 +0800 Subject: gpio: sch: fix typo in a comment The double `the' in the comment in line 142 is repeated. Remove one of them from the comment. Signed-off-by: Jason Wang Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-sch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c index 0600f71462b5..acda4c5052d3 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -139,7 +139,7 @@ static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num, /* * according to the datasheet, writing to the level register has no * effect when GPIO is programmed as input. - * Actually the the level register is read-only when configured as input. + * Actually the level register is read-only when configured as input. * Thus presetting the output level before switching to output is _NOT_ possible. * Hence we set the level after configuring the GPIO as output. * But we cannot prevent a short low pulse if direction is set to high -- cgit v1.2.3 From dd61b29207ca4f346fbd9c06bc49f093e3369185 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 7 Dec 2021 10:34:06 +0100 Subject: gpiolib: provide gpiod_remove_hogs() Currently all users of gpiod_add_hogs() call it only once at system init so there never was any need for a mechanism allowing to remove them. Now the upcoming gpio-sim will need to tear down chips with hogged lines so provide a function that allows to remove hogs. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Acked-by: Linus Walleij --- drivers/gpio/gpiolib.c | 11 +++++++++++ include/linux/gpio/machine.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 535eb1b509c1..467f4a8586a0 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3557,6 +3557,17 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) } EXPORT_SYMBOL_GPL(gpiod_add_hogs); +void gpiod_remove_hogs(struct gpiod_hog *hogs) +{ + struct gpiod_hog *hog; + + mutex_lock(&gpio_machine_hogs_mutex); + for (hog = &hogs[0]; hog->chip_label; hog++) + list_del(&hog->list); + mutex_unlock(&gpio_machine_hogs_mutex); +} +EXPORT_SYMBOL_GPL(gpiod_remove_hogs); + static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) { const char *dev_id = dev ? dev_name(dev) : NULL; diff --git a/include/linux/gpio/machine.h b/include/linux/gpio/machine.h index d755e529c1e3..2647dd10b541 100644 --- a/include/linux/gpio/machine.h +++ b/include/linux/gpio/machine.h @@ -100,6 +100,7 @@ void gpiod_add_lookup_table(struct gpiod_lookup_table *table); void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n); void gpiod_remove_lookup_table(struct gpiod_lookup_table *table); void gpiod_add_hogs(struct gpiod_hog *hogs); +void gpiod_remove_hogs(struct gpiod_hog *hogs); #else /* ! CONFIG_GPIOLIB */ static inline void gpiod_add_lookup_table(struct gpiod_lookup_table *table) {} @@ -108,6 +109,7 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) {} static inline void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) {} static inline void gpiod_add_hogs(struct gpiod_hog *hogs) {} +static inline void gpiod_remove_hogs(struct gpiod_hog *hogs) {} #endif /* CONFIG_GPIOLIB */ #endif /* __LINUX_GPIO_MACHINE_H */ -- cgit v1.2.3 From 990f6756bb64756d2d1033118cded6333b43397d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 13 Dec 2021 11:16:41 +0100 Subject: gpiolib: allow to specify the firmware node in struct gpio_chip Software nodes allow us to represent hierarchies for device components that don't have their struct device representation yet - for instance: banks of GPIOs under a common GPIO expander. The core gpiolib core however doesn't offer any way of passing this information from the drivers. This extends struct gpio_chip with a pointer to fwnode that can be set by the driver and used to pass device properties for child nodes. This is similar to how we handle device-tree sub-nodes with CONFIG_OF_GPIO enabled. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Acked-by: Linus Walleij --- drivers/gpio/gpiolib.c | 7 ++++++- include/linux/gpio/driver.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 467f4a8586a0..4d666767c9ab 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -593,7 +593,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, struct lock_class_key *lock_key, struct lock_class_key *request_key) { - struct fwnode_handle *fwnode = gc->parent ? dev_fwnode(gc->parent) : NULL; + struct fwnode_handle *fwnode = NULL; struct gpio_device *gdev; unsigned long flags; int base = gc->base; @@ -601,6 +601,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, int ret = 0; u32 ngpios; + if (gc->fwnode) + fwnode = gc->fwnode; + else if (gc->parent) + fwnode = dev_fwnode(gc->parent); + /* * First: allocate and populate the internal stat container, and * set up the struct device. diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index a673a359e20b..b0728c8ad90c 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -289,6 +289,7 @@ struct gpio_irq_chip { * number or the name of the SoC IP-block implementing it. * @gpiodev: the internal state holder, opaque struct * @parent: optional parent device providing the GPIOs + * @fwnode: optional fwnode providing this controller's properties * @owner: helps prevent removal of modules exporting active GPIOs * @request: optional hook for chip-specific activation, such as * enabling module power and clock; may sleep @@ -377,6 +378,7 @@ struct gpio_chip { const char *label; struct gpio_device *gpiodev; struct device *parent; + struct fwnode_handle *fwnode; struct module *owner; int (*request)(struct gpio_chip *gc, -- cgit v1.2.3 From ac627260cf525300d5d13e67279a89911f1ad928 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 7 Dec 2021 10:34:08 +0100 Subject: gpiolib: of: make fwnode take precedence in struct gpio_chip If the driver sets the fwnode in struct gpio_chip, let it take precedence over the of_node. This only affects OF-based systems, ACPI needs to be converted separately. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Acked-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 0ad288ab6262..91dcf2c6cdd8 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -1046,6 +1046,9 @@ void of_gpio_dev_init(struct gpio_chip *gc, struct gpio_device *gdev) if (gc->parent) gdev->dev.of_node = gc->parent->of_node; + if (gc->fwnode) + gc->of_node = to_of_node(gc->fwnode); + /* If the gpiochip has an assigned OF node this takes precedence */ if (gc->of_node) gdev->dev.of_node = gc->of_node; -- cgit v1.2.3 From cb8c474e79be458f58e9df073f51ca159f3a2aa0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 7 Dec 2021 10:34:09 +0100 Subject: gpio: sim: new testing module Implement a new, modern GPIO testing module controlled by configfs attributes instead of module parameters. The goal of this driver is to provide a replacement for gpio-mockup that will be easily extensible with new features and doesn't require reloading the module to change the setup. Signed-off-by: Bartosz Golaszewski Acked-by: Linus Walleij --- Documentation/admin-guide/gpio/gpio-sim.rst | 134 +++ drivers/gpio/Kconfig | 8 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-sim.c | 1589 +++++++++++++++++++++++++++ 4 files changed, 1732 insertions(+) create mode 100644 Documentation/admin-guide/gpio/gpio-sim.rst create mode 100644 drivers/gpio/gpio-sim.c diff --git a/Documentation/admin-guide/gpio/gpio-sim.rst b/Documentation/admin-guide/gpio/gpio-sim.rst new file mode 100644 index 000000000000..d8a90c81b9ee --- /dev/null +++ b/Documentation/admin-guide/gpio/gpio-sim.rst @@ -0,0 +1,134 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Configfs GPIO Simulator +======================= + +The configfs GPIO Simulator (gpio-sim) provides a way to create simulated GPIO +chips for testing purposes. The lines exposed by these chips can be accessed +using the standard GPIO character device interface as well as manipulated +using sysfs attributes. + +Creating simulated chips +------------------------ + +The gpio-sim module registers a configfs subsystem called ``'gpio-sim'``. For +details of the configfs filesystem, please refer to the configfs documentation. + +The user can create a hierarchy of configfs groups and items as well as modify +values of exposed attributes. Once the chip is instantiated, this hierarchy +will be translated to appropriate device properties. The general structure is: + +**Group:** ``/config/gpio-sim`` + +This is the top directory of the gpio-sim configfs tree. + +**Group:** ``/config/gpio-sim/gpio-device`` + +**Attribute:** ``/config/gpio-sim/gpio-device/dev_name`` + +**Attribute:** ``/config/gpio-sim/gpio-device/live`` + +This is a directory representing a GPIO platform device. The ``'dev_name'`` +attribute is read-only and allows the user-space to read the platform device +name (e.g. ``'gpio-sim.0'``). The ``'live'`` attribute allows to trigger the +actual creation of the device once it's fully configured. The accepted values +are: ``'1'`` to enable the simulated device and ``'0'`` to disable and tear +it down. + +**Group:** ``/config/gpio-sim/gpio-device/gpio-bankX`` + +**Attribute:** ``/config/gpio-sim/gpio-device/gpio-bankX/chip_name`` + +**Attribute:** ``/config/gpio-sim/gpio-device/gpio-bankX/num_lines`` + +This group represents a bank of GPIOs under the top platform device. The +``'chip_name'`` attribute is read-only and allows the user-space to read the +device name of the bank device. The ``'num_lines'`` attribute allows to specify +the number of lines exposed by this bank. + +**Group:** ``/config/gpio-sim/gpio-device/gpio-bankX/lineY`` + +**Attribute:** ``/config/gpio-sim/gpio-device/gpio-bankX/lineY/name`` + +This group represents a single line at the offset Y. The 'name' attribute +allows to set the line name as represented by the 'gpio-line-names' property. + +**Item:** ``/config/gpio-sim/gpio-device/gpio-bankX/lineY/hog`` + +**Attribute:** ``/config/gpio-sim/gpio-device/gpio-bankX/lineY/hog/name`` + +**Attribute:** ``/config/gpio-sim/gpio-device/gpio-bankX/lineY/hog/direction`` + +This item makes the gpio-sim module hog the associated line. The ``'name'`` +attribute specifies the in-kernel consumer name to use. The ``'direction'`` +attribute specifies the hog direction and must be one of: ``'input'``, +``'output-high'`` and ``'output-low'``. + +Inside each bank directory, there's a set of attributes that can be used to +configure the new chip. Additionally the user can ``mkdir()`` subdirectories +inside the chip's directory that allow to pass additional configuration for +specific lines. The name of those subdirectories must take the form of: +``'line'`` (e.g. ``'line0'``, ``'line20'``, etc.) as the name will be +used by the module to assign the config to the specific line at given offset. + +Once the confiuration is complete, the ``'live'`` attribute must be set to 1 in +order to instantiate the chip. It can be set back to 0 to destroy the simulated +chip. The module will synchronously wait for the new simulated device to be +successfully probed and if this doesn't happen, writing to ``'live'`` will +result in an error. + +Simulated GPIO chips can also be defined in device-tree. The compatible string +must be: ``"gpio-simulator"``. Supported properties are: + + ``"gpio-sim,label"`` - chip label + +Other standard GPIO properties (like ``"gpio-line-names"``, ``"ngpios"`` or +``"gpio-hog"``) are also supported. Please refer to the GPIO documentation for +details. + +An example device-tree code defining a GPIO simulator: + +.. code-block :: none + + gpio-sim { + compatible = "gpio-simulator"; + + bank0 { + gpio-controller; + #gpio-cells = <2>; + ngpios = <16>; + gpio-sim,label = "dt-bank0"; + gpio-line-names = "", "sim-foo", "", "sim-bar"; + }; + + bank1 { + gpio-controller; + #gpio-cells = <2>; + ngpios = <8>; + gpio-sim,label = "dt-bank1"; + + line3 { + gpio-hog; + gpios = <3 0>; + output-high; + line-name = "sim-hog-from-dt"; + }; + }; + }; + +Manipulating simulated lines +---------------------------- + +Each simulated GPIO chip creates a separate sysfs group under its device +directory for each exposed line +(e.g. ``/sys/devices/platform/gpio-sim.X/gpiochipY/``). The name of each group +is of the form: ``'sim_gpioX'`` where X is the offset of the line. Inside each +group there are two attibutes: + + ``pull`` - allows to read and set the current simulated pull setting for + every line, when writing the value must be one of: ``'pull-up'``, + ``'pull-down'`` + + ``value`` - allows to read the current value of the line which may be + different from the pull if the line is being driven from + user-space diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index e05f226b3d5d..83df9df7c595 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1691,6 +1691,14 @@ config GPIO_VIRTIO These virtual GPIOs can be routed to real GPIOs or attached to simulators on the host (like QEMU). +config GPIO_SIM + tristate "GPIO Simulator Module" + select IRQ_SIM + select CONFIGFS_FS + help + This enables the GPIO simulator - a configfs-based GPIO testing + driver. + endmenu endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 71ee9fc2ff83..f21577de2474 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -133,6 +133,7 @@ obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o obj-$(CONFIG_GPIO_SCH) += gpio-sch.o obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o +obj-$(CONFIG_GPIO_SIM) += gpio-sim.o obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o obj-$(CONFIG_GPIO_SL28CPLD) += gpio-sl28cpld.o obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c new file mode 100644 index 000000000000..ef6145f51c8a --- /dev/null +++ b/drivers/gpio/gpio-sim.c @@ -0,0 +1,1589 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * GPIO testing driver based on configfs. + * + * Copyright (C) 2021 Bartosz Golaszewski + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gpiolib.h" + +#define GPIO_SIM_PROP_MAX 4 /* Max 3 properties + sentinel. */ +#define GPIO_SIM_NUM_ATTRS 3 /* value, pull and sentinel */ + +static DEFINE_IDA(gpio_sim_ida); + +struct gpio_sim_chip { + struct gpio_chip gc; + unsigned long *direction_map; + unsigned long *value_map; + unsigned long *pull_map; + struct irq_domain *irq_sim; + struct mutex lock; + const struct attribute_group **attr_groups; +}; + +struct gpio_sim_attribute { + struct device_attribute dev_attr; + unsigned int offset; +}; + +static struct gpio_sim_attribute * +to_gpio_sim_attr(struct device_attribute *dev_attr) +{ + return container_of(dev_attr, struct gpio_sim_attribute, dev_attr); +} + +static int gpio_sim_apply_pull(struct gpio_sim_chip *chip, + unsigned int offset, int value) +{ + int irq, irq_type, ret; + struct gpio_desc *desc; + struct gpio_chip *gc; + + gc = &chip->gc; + desc = &gc->gpiodev->descs[offset]; + + mutex_lock(&chip->lock); + + if (test_bit(FLAG_REQUESTED, &desc->flags) && + !test_bit(FLAG_IS_OUT, &desc->flags)) { + if (value == !!test_bit(offset, chip->value_map)) + goto set_pull; + + /* + * This is fine - it just means, nobody is listening + * for interrupts on this line, otherwise + * irq_create_mapping() would have been called from + * the to_irq() callback. + */ + irq = irq_find_mapping(chip->irq_sim, offset); + if (!irq) + goto set_value; + + irq_type = irq_get_trigger_type(irq); + + if ((value && (irq_type & IRQ_TYPE_EDGE_RISING)) || + (!value && (irq_type & IRQ_TYPE_EDGE_FALLING))) { + ret = irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING, + true); + if (ret) + goto set_pull; + } + } + +set_value: + /* Change the value unless we're actively driving the line. */ + if (!test_bit(FLAG_REQUESTED, &desc->flags) || + !test_bit(FLAG_IS_OUT, &desc->flags)) + __assign_bit(offset, chip->value_map, value); + +set_pull: + __assign_bit(offset, chip->pull_map, value); + mutex_unlock(&chip->lock); + return 0; +} + +static int gpio_sim_get(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_sim_chip *chip = gpiochip_get_data(gc); + int ret; + + mutex_lock(&chip->lock); + ret = !!test_bit(offset, chip->value_map); + mutex_unlock(&chip->lock); + + return ret; +} + +static void gpio_sim_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct gpio_sim_chip *chip = gpiochip_get_data(gc); + + mutex_lock(&chip->lock); + __assign_bit(offset, chip->value_map, value); + mutex_unlock(&chip->lock); +} + +static int gpio_sim_get_multiple(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits) +{ + struct gpio_sim_chip *chip = gpiochip_get_data(gc); + + mutex_lock(&chip->lock); + bitmap_copy(bits, chip->value_map, gc->ngpio); + mutex_unlock(&chip->lock); + + return 0; +} + +static void gpio_sim_set_multiple(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits) +{ + struct gpio_sim_chip *chip = gpiochip_get_data(gc); + + mutex_lock(&chip->lock); + bitmap_copy(chip->value_map, bits, gc->ngpio); + mutex_unlock(&chip->lock); +} + +static int gpio_sim_direction_output(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct gpio_sim_chip *chip = gpiochip_get_data(gc); + + mutex_lock(&chip->lock); + __clear_bit(offset, chip->direction_map); + __assign_bit(offset, chip->value_map, value); + mutex_unlock(&chip->lock); + + return 0; +} + +static int gpio_sim_direction_input(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_sim_chip *chip = gpiochip_get_data(gc); + + mutex_lock(&chip->lock); + __set_bit(offset, chip->direction_map); + mutex_unlock(&chip->lock); + + return 0; +} + +static int gpio_sim_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_sim_chip *chip = gpiochip_get_data(gc); + int direction; + + mutex_lock(&chip->lock); + direction = !!test_bit(offset, chip->direction_map); + mutex_unlock(&chip->lock); + + return direction ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; +} + +static int gpio_sim_set_config(struct gpio_chip *gc, + unsigned int offset, unsigned long config) +{ + struct gpio_sim_chip *chip = gpiochip_get_data(gc); + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_BIAS_PULL_UP: + return gpio_sim_apply_pull(chip, offset, 1); + case PIN_CONFIG_BIAS_PULL_DOWN: + return gpio_sim_apply_pull(chip, offset, 0); + default: + break; + } + + return -ENOTSUPP; +} + +static int gpio_sim_to_irq(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_sim_chip *chip = gpiochip_get_data(gc); + + return irq_create_mapping(chip->irq_sim, offset); +} + +static void gpio_sim_free(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_sim_chip *chip = gpiochip_get_data(gc); + + mutex_lock(&chip->lock); + __assign_bit(offset, chip->value_map, !!test_bit(offset, chip->pull_map)); + mutex_unlock(&chip->lock); +} + +static ssize_t gpio_sim_sysfs_val_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_sim_attribute *line_attr = to_gpio_sim_attr(attr); + struct gpio_sim_chip *chip = dev_get_drvdata(dev); + int val; + + mutex_lock(&chip->lock); + val = !!test_bit(line_attr->offset, chip->value_map); + mutex_unlock(&chip->lock); + + return sysfs_emit(buf, "%d\n", val); +} + +static ssize_t gpio_sim_sysfs_val_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + /* + * Not assigning this function will result in write() returning -EIO + * which is confusing. Return -EPERM explicitly. + */ + return -EPERM; +} + +static const char *const gpio_sim_sysfs_pull_strings[] = { + [0] = "pull-down", + [1] = "pull-up", +}; + +static ssize_t gpio_sim_sysfs_pull_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct gpio_sim_attribute *line_attr = to_gpio_sim_attr(attr); + struct gpio_sim_chip *chip = dev_get_drvdata(dev); + int pull; + + mutex_lock(&chip->lock); + pull = !!test_bit(line_attr->offset, chip->pull_map); + mutex_unlock(&chip->lock); + + return sysfs_emit(buf, "%s\n", gpio_sim_sysfs_pull_strings[pull]); +} + +static ssize_t gpio_sim_sysfs_pull_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct gpio_sim_attribute *line_attr = to_gpio_sim_attr(attr); + struct gpio_sim_chip *chip = dev_get_drvdata(dev); + int ret, pull; + + pull = sysfs_match_string(gpio_sim_sysfs_pull_strings, buf); + if (pull < 0) + return pull; + + ret = gpio_sim_apply_pull(chip, line_attr->offset, pull); + if (ret) + return ret; + + return len; +} + +static void gpio_sim_mutex_destroy(void *data) +{ + struct mutex *lock = data; + + mutex_destroy(lock); +} + +static void gpio_sim_sysfs_remove(void *data) +{ + struct gpio_sim_chip *chip = data; + + sysfs_remove_groups(&chip->gc.gpiodev->dev.kobj, chip->attr_groups); +} + +static int gpio_sim_setup_sysfs(struct gpio_sim_chip *chip) +{ + struct device_attribute *val_dev_attr, *pull_dev_attr; + struct gpio_sim_attribute *val_attr, *pull_attr; + unsigned int num_lines = chip->gc.ngpio; + struct device *dev = chip->gc.parent; + struct attribute_group *attr_group; + struct attribute **attrs; + int i, ret; + + chip->attr_groups = devm_kcalloc(dev, sizeof(*chip->attr_groups), + num_lines + 1, GFP_KERNEL); + if (!chip->attr_groups) + return -ENOMEM; + + for (i = 0; i < num_lines; i++) { + attr_group = devm_kzalloc(dev, sizeof(*attr_group), GFP_KERNEL); + attrs = devm_kcalloc(dev, sizeof(*attrs), + GPIO_SIM_NUM_ATTRS, GFP_KERNEL); + val_attr = devm_kzalloc(dev, sizeof(*val_attr), GFP_KERNEL); + pull_attr = devm_kzalloc(dev, sizeof(*pull_attr), GFP_KERNEL); + if (!attr_group || !attrs || !val_attr || !pull_attr) + return -ENOMEM; + + attr_group->name = devm_kasprintf(dev, GFP_KERNEL, + "sim_gpio%u", i); + if (!attr_group->name) + return -ENOMEM; + + val_attr->offset = pull_attr->offset = i; + + val_dev_attr = &val_attr->dev_attr; + pull_dev_attr = &pull_attr->dev_attr; + + sysfs_attr_init(&val_dev_attr->attr); + sysfs_attr_init(&pull_dev_attr->attr); + + val_dev_attr->attr.name = "value"; + pull_dev_attr->attr.name = "pull"; + + val_dev_attr->attr.mode = pull_dev_attr->attr.mode = 0644; + + val_dev_attr->show = gpio_sim_sysfs_val_show; + val_dev_attr->store = gpio_sim_sysfs_val_store; + pull_dev_attr->show = gpio_sim_sysfs_pull_show; + pull_dev_attr->store = gpio_sim_sysfs_pull_store; + + attrs[0] = &val_dev_attr->attr; + attrs[1] = &pull_dev_attr->attr; + + attr_group->attrs = attrs; + chip->attr_groups[i] = attr_group; + } + + ret = sysfs_create_groups(&chip->gc.gpiodev->dev.kobj, + chip->attr_groups); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, gpio_sim_sysfs_remove, chip); +} + +static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) +{ + struct gpio_sim_chip *chip; + struct gpio_chip *gc; + const char *label; + u32 num_lines; + int ret; + + ret = fwnode_property_read_u32(swnode, "ngpios", &num_lines); + if (ret) + return ret; + + ret = fwnode_property_read_string(swnode, "gpio-sim,label", &label); + if (ret) { + label = devm_kasprintf(dev, GFP_KERNEL, "%s-%s", + dev_name(dev), fwnode_get_name(swnode)); + if (!label) + return -ENOMEM; + } + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->direction_map = devm_bitmap_alloc(dev, num_lines, GFP_KERNEL); + if (!chip->direction_map) + return -ENOMEM; + + /* Default to input mode. */ + bitmap_fill(chip->direction_map, num_lines); + + chip->value_map = devm_bitmap_zalloc(dev, num_lines, GFP_KERNEL); + if (!chip->value_map) + return -ENOMEM; + + chip->pull_map = devm_bitmap_zalloc(dev, num_lines, GFP_KERNEL); + if (!chip->pull_map) + return -ENOMEM; + + chip->irq_sim = devm_irq_domain_create_sim(dev, NULL, num_lines); + if (IS_ERR(chip->irq_sim)) + return PTR_ERR(chip->irq_sim); + + mutex_init(&chip->lock); + ret = devm_add_action_or_reset(dev, gpio_sim_mutex_destroy, + &chip->lock); + if (ret) + return ret; + + gc = &chip->gc; + gc->base = -1; + gc->ngpio = num_lines; + gc->label = label; + gc->owner = THIS_MODULE; + gc->parent = dev; + gc->fwnode = swnode; + gc->get = gpio_sim_get; + gc->set = gpio_sim_set; + gc->get_multiple = gpio_sim_get_multiple; + gc->set_multiple = gpio_sim_set_multiple; + gc->direction_output = gpio_sim_direction_output; + gc->direction_input = gpio_sim_direction_input; + gc->get_direction = gpio_sim_get_direction; + gc->set_config = gpio_sim_set_config; + gc->to_irq = gpio_sim_to_irq; + gc->free = gpio_sim_free; + + ret = devm_gpiochip_add_data(dev, gc, chip); + if (ret) + return ret; + + /* Used by sysfs and configfs callbacks. */ + dev_set_drvdata(&gc->gpiodev->dev, chip); + + return gpio_sim_setup_sysfs(chip); +} + +static int gpio_sim_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fwnode_handle *swnode; + int ret; + + device_for_each_child_node(dev, swnode) { + ret = gpio_sim_add_bank(swnode, dev); + if (ret) + return ret; + } + + return 0; +} + +static const struct of_device_id gpio_sim_of_match[] = { + { .compatible = "gpio-simulator" }, + { } +}; +MODULE_DEVICE_TABLE(of, gpio_sim_of_match); + +static struct platform_driver gpio_sim_driver = { + .driver = { + .name = "gpio-sim", + .of_match_table = gpio_sim_of_match, + }, + .probe = gpio_sim_probe, +}; + +struct gpio_sim_device { + struct config_group group; + + /* + * If pdev is NULL, the device is 'pending' (waiting for configuration). + * Once the pointer is assigned, the device has been created and the + * item is 'live'. + */ + struct platform_device *pdev; + int id; + + /* + * Each configfs filesystem operation is protected with the subsystem + * mutex. Each separate attribute is protected with the buffer mutex. + * This structure however can be modified by callbacks of different + * attributes so we need another lock. + * + * We use this lock fo protecting all data structures owned by this + * object too. + */ + struct mutex lock; + + /* + * This is used to synchronously wait for the driver's probe to complete + * and notify the user-space about any errors. + */ + struct notifier_block bus_notifier; + struct completion probe_completion; + bool driver_bound; + + struct gpiod_hog *hogs; + + struct list_head bank_list; +}; + +/* This is called with dev->lock already taken. */ +static int gpio_sim_bus_notifier_call(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct gpio_sim_device *simdev = container_of(nb, + struct gpio_sim_device, + bus_notifier); + struct device *dev = data; + char devname[32]; + + snprintf(devname, sizeof(devname), "gpio-sim.%u", simdev->id); + + if (strcmp(dev_name(dev), devname) == 0) { + if (action == BUS_NOTIFY_BOUND_DRIVER) + simdev->driver_bound = true; + else if (action == BUS_NOTIFY_DRIVER_NOT_BOUND) + simdev->driver_bound = false; + else + return NOTIFY_DONE; + + complete(&simdev->probe_completion); + return NOTIFY_OK; + } + + return NOTIFY_DONE; +} + +static struct gpio_sim_device *to_gpio_sim_device(struct config_item *item) +{ + struct config_group *group = to_config_group(item); + + return container_of(group, struct gpio_sim_device, group); +} + +struct gpio_sim_bank { + struct config_group group; + + /* + * We could have used the ci_parent field of the config_item but + * configfs is stupid and calls the item's release callback after + * already having cleared the parent pointer even though the parent + * is guaranteed to survive the child... + * + * So we need to store the pointer to the parent struct here. We can + * dereference it anywhere we need with no checks and no locking as + * it's guaranteed to survive the childred and protected by configfs + * locks. + * + * Same for other structures. + */ + struct gpio_sim_device *parent; + struct list_head siblings; + + char *label; + unsigned int num_lines; + + struct list_head line_list; + + struct fwnode_handle *swnode; +}; + +static struct gpio_sim_bank *to_gpio_sim_bank(struct config_item *item) +{ + struct config_group *group = to_config_group(item); + + return container_of(group, struct gpio_sim_bank, group); +} + +static struct gpio_sim_device * +gpio_sim_bank_get_device(struct gpio_sim_bank *bank) +{ + return bank->parent; +} + +struct gpio_sim_hog; + +struct gpio_sim_line { + struct config_group group; + + struct gpio_sim_bank *parent; + struct list_head siblings; + + unsigned int offset; + char *name; + + /* There can only be one hog per line. */ + struct gpio_sim_hog *hog; +}; + +static struct gpio_sim_line *to_gpio_sim_line(struct config_item *item) +{ + struct config_group *group = to_config_group(item); + + return container_of(group, struct gpio_sim_line, group); +} + +static struct gpio_sim_device * +gpio_sim_line_get_device(struct gpio_sim_line *line) +{ + struct gpio_sim_bank *bank = line->parent; + + return gpio_sim_bank_get_device(bank); +} + +struct gpio_sim_hog { + struct config_item item; + struct gpio_sim_line *parent; + + char *name; + int dir; +}; + +static struct gpio_sim_hog *to_gpio_sim_hog(struct config_item *item) +{ + return container_of(item, struct gpio_sim_hog, item); +} + +static struct gpio_sim_device *gpio_sim_hog_get_device(struct gpio_sim_hog *hog) +{ + struct gpio_sim_line *line = hog->parent; + + return gpio_sim_line_get_device(line); +} + +static bool gpio_sim_device_is_live_unlocked(struct gpio_sim_device *dev) +{ + return !!dev->pdev; +} + +static char *gpio_sim_strdup_trimmed(const char *str, size_t count) +{ + char *dup, *trimmed; + + dup = kstrndup(str, count, GFP_KERNEL); + if (!dup) + return NULL; + + trimmed = strstrip(dup); + memmove(dup, trimmed, strlen(trimmed) + 1); + + return dup; +} + +static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item, + char *page) +{ + struct gpio_sim_device *dev = to_gpio_sim_device(item); + struct platform_device *pdev; + int ret; + + mutex_lock(&dev->lock); + pdev = dev->pdev; + if (pdev) + ret = sprintf(page, "%s\n", dev_name(&pdev->dev)); + else + ret = sprintf(page, "gpio-sim.%d\n", dev->id); + mutex_unlock(&dev->lock); + + return ret; +} + +CONFIGFS_ATTR_RO(gpio_sim_device_config_, dev_name); + +static ssize_t +gpio_sim_device_config_live_show(struct config_item *item, char *page) +{ + struct gpio_sim_device *dev = to_gpio_sim_device(item); + bool live; + + mutex_lock(&dev->lock); + live = gpio_sim_device_is_live_unlocked(dev); + mutex_unlock(&dev->lock); + + return sprintf(page, "%c\n", live ? '1' : '0'); +} + +static char **gpio_sim_make_line_names(struct gpio_sim_bank *bank, + unsigned int *line_names_size) +{ + unsigned int max_offset = 0; + bool has_line_names = false; + struct gpio_sim_line *line; + char **line_names; + + list_for_each_entry(line, &bank->line_list, siblings) { + if (line->name) { + if (line->offset > max_offset) + max_offset = line->offset; + + /* + * max_offset can stay at 0 so it's not an indicator + * of whether line names were configured at all. + */ + has_line_names = true; + } + } + + if (!has_line_names) + /* + * This is not an error - NULL means, there are no line + * names configured. + */ + return NULL; + + *line_names_size = max_offset + 1; + + line_names = kcalloc(*line_names_size, sizeof(*line_names), GFP_KERNEL); + if (!line_names) + return ERR_PTR(-ENOMEM); + + list_for_each_entry(line, &bank->line_list, siblings) + line_names[line->offset] = line->name; + + return line_names; +} + +static void gpio_sim_remove_hogs(struct gpio_sim_device *dev) +{ + struct gpiod_hog *hog; + + if (!dev->hogs) + return; + + gpiod_remove_hogs(dev->hogs); + + for (hog = dev->hogs; !hog->chip_label; hog++) { + kfree(hog->chip_label); + kfree(hog->line_name); + } + + kfree(dev->hogs); + dev->hogs = NULL; +} + +static int gpio_sim_add_hogs(struct gpio_sim_device *dev) +{ + unsigned int num_hogs = 0, idx = 0; + struct gpio_sim_bank *bank; + struct gpio_sim_line *line; + struct gpiod_hog *hog; + + list_for_each_entry(bank, &dev->bank_list, siblings) { + list_for_each_entry(line, &bank->line_list, siblings) { + if (line->hog) + num_hogs++; + } + } + + if (!num_hogs) + return 0; + + /* Allocate one more for the sentinel. */ + dev->hogs = kcalloc(num_hogs + 1, sizeof(*dev->hogs), GFP_KERNEL); + if (!dev->hogs) + return -ENOMEM; + + list_for_each_entry(bank, &dev->bank_list, siblings) { + list_for_each_entry(line, &bank->line_list, siblings) { + if (!line->hog) + continue; + + hog = &dev->hogs[idx++]; + + /* + * We need to make this string manually because at this + * point the device doesn't exist yet and so dev_name() + * is not available. + */ + hog->chip_label = kasprintf(GFP_KERNEL, + "gpio-sim.%u-%s", dev->id, + fwnode_get_name(bank->swnode)); + if (!hog->chip_label) { + gpio_sim_remove_hogs(dev); + return -ENOMEM; + } + + /* + * We need to duplicate this because the hog config + * item can be removed at any time (and we can't block + * it) and gpiolib doesn't make a deep copy of the hog + * data. + */ + if (line->hog->name) { + hog->line_name = kstrdup(line->hog->name, + GFP_KERNEL); + if (!hog->line_name) { + gpio_sim_remove_hogs(dev); + return -ENOMEM; + } + } + + hog->chip_hwnum = line->offset; + hog->dflags = line->hog->dir; + } + } + + gpiod_add_hogs(dev->hogs); + + return 0; +} + +static struct fwnode_handle * +gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank, + struct fwnode_handle *parent) +{ + struct property_entry properties[GPIO_SIM_PROP_MAX]; + unsigned int prop_idx = 0, line_names_size = 0; + struct fwnode_handle *swnode; + char **line_names; + + memset(properties, 0, sizeof(properties)); + + properties[prop_idx++] = PROPERTY_ENTRY_U32("ngpios", bank->num_lines); + + if (bank->label) + properties[prop_idx++] = PROPERTY_ENTRY_STRING("gpio-sim,label", + bank->label); + + line_names = gpio_sim_make_line_names(bank, &line_names_size); + if (IS_ERR(line_names)) + return ERR_CAST(line_names); + + if (line_names) + properties[prop_idx++] = PROPERTY_ENTRY_STRING_ARRAY_LEN( + "gpio-line-names", + line_names, line_names_size); + + swnode = fwnode_create_software_node(properties, parent); + kfree(line_names); + return swnode; +} + +static void gpio_sim_remove_swnode_recursive(struct fwnode_handle *swnode) +{ + struct fwnode_handle *child; + + fwnode_for_each_child_node(swnode, child) + fwnode_remove_software_node(child); + + fwnode_remove_software_node(swnode); +} + +static bool gpio_sim_bank_labels_non_unique(struct gpio_sim_device *dev) +{ + struct gpio_sim_bank *this, *pos; + + list_for_each_entry(this, &dev->bank_list, siblings) { + list_for_each_entry(pos, &dev->bank_list, siblings) { + if (this == pos || (!this->label || !pos->label)) + continue; + + if (strcmp(this->label, pos->label) == 0) + return true; + } + } + + return false; +} + +static int gpio_sim_device_activate_unlocked(struct gpio_sim_device *dev) +{ + struct platform_device_info pdevinfo; + struct fwnode_handle *swnode; + struct platform_device *pdev; + struct gpio_sim_bank *bank; + int ret; + + if (list_empty(&dev->bank_list)) + return -ENODATA; + + /* + * Non-unique GPIO device labels are a corner-case we don't support + * as it would interfere with machine hogging mechanism and has little + * use in real life. + */ + if (gpio_sim_bank_labels_non_unique(dev)) + return -EINVAL; + + memset(&pdevinfo, 0, sizeof(pdevinfo)); + + swnode = fwnode_create_software_node(NULL, NULL); + if (IS_ERR(swnode)) + return PTR_ERR(swnode); + + list_for_each_entry(bank, &dev->bank_list, siblings) { + bank->swnode = gpio_sim_make_bank_swnode(bank, swnode); + if (ret) { + gpio_sim_remove_swnode_recursive(swnode); + return ret; + } + } + + ret = gpio_sim_add_hogs(dev); + if (ret) { + gpio_sim_remove_swnode_recursive(swnode); + return ret; + } + + pdevinfo.name = "gpio-sim"; + pdevinfo.fwnode = swnode; + pdevinfo.id = dev->id; + + reinit_completion(&dev->probe_completion); + dev->driver_bound = false; + bus_register_notifier(&platform_bus_type, &dev->bus_notifier); + + pdev = platform_device_register_full(&pdevinfo); + if (IS_ERR(pdev)) { + bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier); + gpio_sim_remove_hogs(dev); + gpio_sim_remove_swnode_recursive(swnode); + return PTR_ERR(pdev); + } + + wait_for_completion(&dev->probe_completion); + bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier); + + if (!dev->driver_bound) { + /* Probe failed, check kernel log. */ + platform_device_unregister(pdev); + gpio_sim_remove_hogs(dev); + gpio_sim_remove_swnode_recursive(swnode); + return -ENXIO; + } + + dev->pdev = pdev; + + return 0; +} + +static void gpio_sim_device_deactivate_unlocked(struct gpio_sim_device *dev) +{ + struct fwnode_handle *swnode; + + swnode = dev_fwnode(&dev->pdev->dev); + platform_device_unregister(dev->pdev); + gpio_sim_remove_swnode_recursive(swnode); + dev->pdev = NULL; + gpio_sim_remove_hogs(dev); +} + +static ssize_t +gpio_sim_device_config_live_store(struct config_item *item, + const char *page, size_t count) +{ + struct gpio_sim_device *dev = to_gpio_sim_device(item); + bool live; + int ret; + + ret = kstrtobool(page, &live); + if (ret) + return ret; + + mutex_lock(&dev->lock); + + if ((!live && !gpio_sim_device_is_live_unlocked(dev)) || + (live && gpio_sim_device_is_live_unlocked(dev))) + ret = -EPERM; + else if (live) + ret = gpio_sim_device_activate_unlocked(dev); + else + gpio_sim_device_deactivate_unlocked(dev); + + mutex_unlock(&dev->lock); + + return ret ?: count; +} + +CONFIGFS_ATTR(gpio_sim_device_config_, live); + +static struct configfs_attribute *gpio_sim_device_config_attrs[] = { + &gpio_sim_device_config_attr_dev_name, + &gpio_sim_device_config_attr_live, + NULL +}; + +struct gpio_sim_chip_name_ctx { + struct gpio_sim_device *dev; + char *page; +}; + +static int gpio_sim_emit_chip_name(struct device *dev, void *data) +{ + struct gpio_sim_chip_name_ctx *ctx = data; + struct fwnode_handle *swnode; + struct gpio_sim_bank *bank; + + /* This would be the sysfs device exported in /sys/class/gpio. */ + if (dev->class) + return 0; + + swnode = dev_fwnode(dev); + + list_for_each_entry(bank, &ctx->dev->bank_list, siblings) { + if (bank->swnode == swnode) + return sprintf(ctx->page, "%s\n", dev_name(dev)); + } + + return -ENODATA; +} + +static ssize_t gpio_sim_bank_config_chip_name_show(struct config_item *item, + char *page) +{ + struct gpio_sim_bank *bank = to_gpio_sim_bank(item); + struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); + struct gpio_sim_chip_name_ctx ctx = { dev, page }; + int ret; + + mutex_lock(&dev->lock); + if (gpio_sim_device_is_live_unlocked(dev)) + ret = device_for_each_child(&dev->pdev->dev, &ctx, + gpio_sim_emit_chip_name); + else + ret = sprintf(page, "none\n"); + mutex_unlock(&dev->lock); + + return ret; +} + +CONFIGFS_ATTR_RO(gpio_sim_bank_config_, chip_name); + +static ssize_t +gpio_sim_bank_config_label_show(struct config_item *item, char *page) +{ + struct gpio_sim_bank *bank = to_gpio_sim_bank(item); + struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); + int ret; + + mutex_lock(&dev->lock); + ret = sprintf(page, "%s\n", bank->label ?: ""); + mutex_unlock(&dev->lock); + + return ret; +} + +static ssize_t gpio_sim_bank_config_label_store(struct config_item *item, + const char *page, size_t count) +{ + struct gpio_sim_bank *bank = to_gpio_sim_bank(item); + struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); + char *trimmed; + + mutex_lock(&dev->lock); + + if (gpio_sim_device_is_live_unlocked(dev)) { + mutex_unlock(&dev->lock); + return -EBUSY; + } + + trimmed = gpio_sim_strdup_trimmed(page, count); + if (!trimmed) { + mutex_unlock(&dev->lock); + return -ENOMEM; + } + + kfree(bank->label); + bank->label = trimmed; + + mutex_unlock(&dev->lock); + return count; +} + +CONFIGFS_ATTR(gpio_sim_bank_config_, label); + +static ssize_t +gpio_sim_bank_config_num_lines_show(struct config_item *item, char *page) +{ + struct gpio_sim_bank *bank = to_gpio_sim_bank(item); + struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); + int ret; + + mutex_lock(&dev->lock); + ret = sprintf(page, "%u\n", bank->num_lines); + mutex_unlock(&dev->lock); + + return ret; +} + +static ssize_t +gpio_sim_bank_config_num_lines_store(struct config_item *item, + const char *page, size_t count) +{ + struct gpio_sim_bank *bank = to_gpio_sim_bank(item); + struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); + unsigned int num_lines; + int ret; + + ret = kstrtouint(page, 0, &num_lines); + if (ret) + return ret; + + if (num_lines == 0) + return -EINVAL; + + mutex_lock(&dev->lock); + + if (gpio_sim_device_is_live_unlocked(dev)) { + mutex_unlock(&dev->lock); + return -EBUSY; + } + + bank->num_lines = num_lines; + + mutex_unlock(&dev->lock); + return count; +} + +CONFIGFS_ATTR(gpio_sim_bank_config_, num_lines); + +static struct configfs_attribute *gpio_sim_bank_config_attrs[] = { + &gpio_sim_bank_config_attr_chip_name, + &gpio_sim_bank_config_attr_label, + &gpio_sim_bank_config_attr_num_lines, + NULL +}; + +static ssize_t +gpio_sim_line_config_name_show(struct config_item *item, char *page) +{ + struct gpio_sim_line *line = to_gpio_sim_line(item); + struct gpio_sim_device *dev = gpio_sim_line_get_device(line); + int ret; + + mutex_lock(&dev->lock); + ret = sprintf(page, "%s\n", line->name ?: ""); + mutex_unlock(&dev->lock); + + return ret; +} + +static ssize_t gpio_sim_line_config_name_store(struct config_item *item, + const char *page, size_t count) +{ + struct gpio_sim_line *line = to_gpio_sim_line(item); + struct gpio_sim_device *dev = gpio_sim_line_get_device(line); + char *trimmed; + + mutex_lock(&dev->lock); + + if (gpio_sim_device_is_live_unlocked(dev)) { + mutex_unlock(&dev->lock); + return -EBUSY; + } + + trimmed = gpio_sim_strdup_trimmed(page, count); + if (!trimmed) { + mutex_unlock(&dev->lock); + return -ENOMEM; + } + + kfree(line->name); + line->name = trimmed; + + mutex_unlock(&dev->lock); + + return count; +} + +CONFIGFS_ATTR(gpio_sim_line_config_, name); + +static struct configfs_attribute *gpio_sim_line_config_attrs[] = { + &gpio_sim_line_config_attr_name, + NULL +}; + +static ssize_t gpio_sim_hog_config_name_show(struct config_item *item, + char *page) +{ + struct gpio_sim_hog *hog = to_gpio_sim_hog(item); + struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); + int ret; + + mutex_lock(&dev->lock); + ret = sprintf(page, "%s\n", hog->name ?: ""); + mutex_unlock(&dev->lock); + + return ret; +} + +static ssize_t gpio_sim_hog_config_name_store(struct config_item *item, + const char *page, size_t count) +{ + struct gpio_sim_hog *hog = to_gpio_sim_hog(item); + struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); + char *trimmed; + + mutex_lock(&dev->lock); + + if (gpio_sim_device_is_live_unlocked(dev)) { + mutex_unlock(&dev->lock); + return -EBUSY; + } + + trimmed = gpio_sim_strdup_trimmed(page, count); + if (!trimmed) { + mutex_unlock(&dev->lock); + return -ENOMEM; + } + + kfree(hog->name); + hog->name = trimmed; + + mutex_unlock(&dev->lock); + + return count; +} + +CONFIGFS_ATTR(gpio_sim_hog_config_, name); + +static ssize_t gpio_sim_hog_config_direction_show(struct config_item *item, + char *page) +{ + struct gpio_sim_hog *hog = to_gpio_sim_hog(item); + struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); + char *repr; + int dir; + + mutex_lock(&dev->lock); + dir = hog->dir; + mutex_unlock(&dev->lock); + + switch (dir) { + case GPIOD_IN: + repr = "input"; + break; + case GPIOD_OUT_HIGH: + repr = "output-high"; + break; + case GPIOD_OUT_LOW: + repr = "output-low"; + break; + default: + /* This would be a programmer bug. */ + WARN(1, "Unexpected hog direction value: %d", dir); + return -EINVAL; + } + + return sprintf(page, "%s\n", repr); +} + +static ssize_t +gpio_sim_hog_config_direction_store(struct config_item *item, + const char *page, size_t count) +{ + struct gpio_sim_hog *hog = to_gpio_sim_hog(item); + struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); + char *trimmed; + int dir; + + mutex_lock(&dev->lock); + + if (gpio_sim_device_is_live_unlocked(dev)) { + mutex_unlock(&dev->lock); + return -EBUSY; + } + + trimmed = gpio_sim_strdup_trimmed(page, count); + if (!trimmed) { + mutex_unlock(&dev->lock); + return -ENOMEM; + } + + if (strcmp(trimmed, "input") == 0) + dir = GPIOD_IN; + else if (strcmp(trimmed, "output-high") == 0) + dir = GPIOD_OUT_HIGH; + else if (strcmp(trimmed, "output-low") == 0) + dir = GPIOD_OUT_LOW; + else + dir = -EINVAL; + + kfree(trimmed); + + if (dir < 0) { + mutex_unlock(&dev->lock); + return dir; + } + + hog->dir = dir; + + mutex_unlock(&dev->lock); + + return count; +} + +CONFIGFS_ATTR(gpio_sim_hog_config_, direction); + +static struct configfs_attribute *gpio_sim_hog_config_attrs[] = { + &gpio_sim_hog_config_attr_name, + &gpio_sim_hog_config_attr_direction, + NULL +}; + +static void gpio_sim_hog_config_item_release(struct config_item *item) +{ + struct gpio_sim_hog *hog = to_gpio_sim_hog(item); + struct gpio_sim_line *line = hog->parent; + struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); + + mutex_lock(&dev->lock); + line->hog = NULL; + mutex_unlock(&dev->lock); + + kfree(hog->name); + kfree(hog); +} + +struct configfs_item_operations gpio_sim_hog_config_item_ops = { + .release = gpio_sim_hog_config_item_release, +}; + +static const struct config_item_type gpio_sim_hog_config_type = { + .ct_item_ops = &gpio_sim_hog_config_item_ops, + .ct_attrs = gpio_sim_hog_config_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item * +gpio_sim_line_config_make_hog_item(struct config_group *group, const char *name) +{ + struct gpio_sim_line *line = to_gpio_sim_line(&group->cg_item); + struct gpio_sim_device *dev = gpio_sim_line_get_device(line); + struct gpio_sim_hog *hog; + + if (strcmp(name, "hog") != 0) + return ERR_PTR(-EINVAL); + + mutex_lock(&dev->lock); + + hog = kzalloc(sizeof(*hog), GFP_KERNEL); + if (!hog) { + mutex_unlock(&dev->lock); + return ERR_PTR(-ENOMEM); + } + + config_item_init_type_name(&hog->item, name, + &gpio_sim_hog_config_type); + + hog->dir = GPIOD_IN; + hog->name = NULL; + hog->parent = line; + line->hog = hog; + + mutex_unlock(&dev->lock); + + return &hog->item; +} + +static void gpio_sim_line_config_group_release(struct config_item *item) +{ + struct gpio_sim_line *line = to_gpio_sim_line(item); + struct gpio_sim_device *dev = gpio_sim_line_get_device(line); + + mutex_lock(&dev->lock); + list_del(&line->siblings); + mutex_unlock(&dev->lock); + + kfree(line->name); + kfree(line); +} + +static struct configfs_item_operations gpio_sim_line_config_item_ops = { + .release = gpio_sim_line_config_group_release, +}; + +static struct configfs_group_operations gpio_sim_line_config_group_ops = { + .make_item = gpio_sim_line_config_make_hog_item, +}; + +static const struct config_item_type gpio_sim_line_config_type = { + .ct_item_ops = &gpio_sim_line_config_item_ops, + .ct_group_ops = &gpio_sim_line_config_group_ops, + .ct_attrs = gpio_sim_line_config_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group * +gpio_sim_bank_config_make_line_group(struct config_group *group, + const char *name) +{ + struct gpio_sim_bank *bank = to_gpio_sim_bank(&group->cg_item); + struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); + struct gpio_sim_line *line; + unsigned int offset; + int ret, nchar; + + ret = sscanf(name, "line%u%n", &offset, &nchar); + if (ret != 1 || nchar != strlen(name)) + return ERR_PTR(-EINVAL); + + mutex_lock(&dev->lock); + + if (gpio_sim_device_is_live_unlocked(dev)) { + mutex_unlock(&dev->lock); + return ERR_PTR(-EBUSY); + } + + line = kzalloc(sizeof(*line), GFP_KERNEL); + if (!line) { + mutex_unlock(&dev->lock); + return ERR_PTR(-ENOMEM); + } + + config_group_init_type_name(&line->group, name, + &gpio_sim_line_config_type); + + line->parent = bank; + line->offset = offset; + list_add_tail(&line->siblings, &bank->line_list); + + mutex_unlock(&dev->lock); + + return &line->group; +} + +static void gpio_sim_bank_config_group_release(struct config_item *item) +{ + struct gpio_sim_bank *bank = to_gpio_sim_bank(item); + struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); + + mutex_lock(&dev->lock); + list_del(&bank->siblings); + mutex_unlock(&dev->lock); + + kfree(bank->label); + kfree(bank); +} + +static struct configfs_item_operations gpio_sim_bank_config_item_ops = { + .release = gpio_sim_bank_config_group_release, +}; + +static struct configfs_group_operations gpio_sim_bank_config_group_ops = { + .make_group = gpio_sim_bank_config_make_line_group, +}; + +static const struct config_item_type gpio_sim_bank_config_group_type = { + .ct_item_ops = &gpio_sim_bank_config_item_ops, + .ct_group_ops = &gpio_sim_bank_config_group_ops, + .ct_attrs = gpio_sim_bank_config_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group * +gpio_sim_device_config_make_bank_group(struct config_group *group, + const char *name) +{ + struct gpio_sim_device *dev = to_gpio_sim_device(&group->cg_item); + struct gpio_sim_bank *bank; + + mutex_lock(&dev->lock); + + if (gpio_sim_device_is_live_unlocked(dev)) { + mutex_unlock(&dev->lock); + return ERR_PTR(-EBUSY); + } + + bank = kzalloc(sizeof(*bank), GFP_KERNEL); + if (!bank) { + mutex_unlock(&dev->lock); + return ERR_PTR(-ENOMEM); + } + + config_group_init_type_name(&bank->group, name, + &gpio_sim_bank_config_group_type); + bank->num_lines = 1; + bank->parent = dev; + INIT_LIST_HEAD(&bank->line_list); + list_add_tail(&bank->siblings, &dev->bank_list); + + mutex_unlock(&dev->lock); + + return &bank->group; +} + +static void gpio_sim_device_config_group_release(struct config_item *item) +{ + struct gpio_sim_device *dev = to_gpio_sim_device(item); + + mutex_lock(&dev->lock); + if (gpio_sim_device_is_live_unlocked(dev)) + gpio_sim_device_deactivate_unlocked(dev); + mutex_unlock(&dev->lock); + + mutex_destroy(&dev->lock); + ida_free(&gpio_sim_ida, dev->id); + kfree(dev); +} + +static struct configfs_item_operations gpio_sim_device_config_item_ops = { + .release = gpio_sim_device_config_group_release, +}; + +static struct configfs_group_operations gpio_sim_device_config_group_ops = { + .make_group = gpio_sim_device_config_make_bank_group, +}; + +static const struct config_item_type gpio_sim_device_config_group_type = { + .ct_item_ops = &gpio_sim_device_config_item_ops, + .ct_group_ops = &gpio_sim_device_config_group_ops, + .ct_attrs = gpio_sim_device_config_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group * +gpio_sim_config_make_device_group(struct config_group *group, const char *name) +{ + struct gpio_sim_device *dev; + int id; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return ERR_PTR(-ENOMEM); + + id = ida_alloc(&gpio_sim_ida, GFP_KERNEL); + if (id < 0) { + kfree(dev); + return ERR_PTR(id); + } + + config_group_init_type_name(&dev->group, name, + &gpio_sim_device_config_group_type); + dev->id = id; + mutex_init(&dev->lock); + INIT_LIST_HEAD(&dev->bank_list); + + dev->bus_notifier.notifier_call = gpio_sim_bus_notifier_call; + init_completion(&dev->probe_completion); + + return &dev->group; +} + +static struct configfs_group_operations gpio_sim_config_group_ops = { + .make_group = gpio_sim_config_make_device_group, +}; + +static const struct config_item_type gpio_sim_config_type = { + .ct_group_ops = &gpio_sim_config_group_ops, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem gpio_sim_config_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "gpio-sim", + .ci_type = &gpio_sim_config_type, + }, + }, +}; + +static int __init gpio_sim_init(void) +{ + int ret; + + ret = platform_driver_register(&gpio_sim_driver); + if (ret) { + pr_err("Error %d while registering the platform driver\n", ret); + return ret; + } + + config_group_init(&gpio_sim_config_subsys.su_group); + mutex_init(&gpio_sim_config_subsys.su_mutex); + ret = configfs_register_subsystem(&gpio_sim_config_subsys); + if (ret) { + pr_err("Error %d while registering the configfs subsystem %s\n", + ret, gpio_sim_config_subsys.su_group.cg_item.ci_namebuf); + mutex_destroy(&gpio_sim_config_subsys.su_mutex); + platform_driver_unregister(&gpio_sim_driver); + return ret; + } + + return 0; +} +module_init(gpio_sim_init); + +static void __exit gpio_sim_exit(void) +{ + configfs_unregister_subsystem(&gpio_sim_config_subsys); + mutex_destroy(&gpio_sim_config_subsys.su_mutex); + platform_driver_unregister(&gpio_sim_driver); +} +module_exit(gpio_sim_exit); + +MODULE_AUTHOR("Bartosz Golaszewski Date: Mon, 13 Dec 2021 11:21:56 +0100 Subject: selftests: gpio: provide a helper for reading chip info Add a simple program that allows to retrieve chip properties from the GPIO character device. This will be used in gpio-sim selftests. Signed-off-by: Bartosz Golaszewski --- tools/testing/selftests/gpio/.gitignore | 1 + tools/testing/selftests/gpio/Makefile | 2 +- tools/testing/selftests/gpio/gpio-chip-info.c | 57 +++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/gpio/gpio-chip-info.c diff --git a/tools/testing/selftests/gpio/.gitignore b/tools/testing/selftests/gpio/.gitignore index a4969f7ee020..4ea4f58dab1a 100644 --- a/tools/testing/selftests/gpio/.gitignore +++ b/tools/testing/selftests/gpio/.gitignore @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only gpio-mockup-cdev +gpio-chip-info diff --git a/tools/testing/selftests/gpio/Makefile b/tools/testing/selftests/gpio/Makefile index 39f2bbe8dd3d..84b48547f94c 100644 --- a/tools/testing/selftests/gpio/Makefile +++ b/tools/testing/selftests/gpio/Makefile @@ -2,6 +2,6 @@ TEST_PROGS := gpio-mockup.sh TEST_FILES := gpio-mockup-sysfs.sh -TEST_GEN_PROGS_EXTENDED := gpio-mockup-cdev +TEST_GEN_PROGS_EXTENDED := gpio-mockup-cdev gpio-chip-info include ../lib.mk diff --git a/tools/testing/selftests/gpio/gpio-chip-info.c b/tools/testing/selftests/gpio/gpio-chip-info.c new file mode 100644 index 000000000000..fdc07e742fba --- /dev/null +++ b/tools/testing/selftests/gpio/gpio-chip-info.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * GPIO character device helper for reading chip information. + * + * Copyright (C) 2021 Bartosz Golaszewski + */ + +#include +#include +#include +#include +#include +#include +#include + +static void print_usage(void) +{ + printf("usage:\n"); + printf(" gpio-chip-info [name|label|num-lines]\n"); +} + +int main(int argc, char **argv) +{ + struct gpiochip_info info; + int fd, ret; + + if (argc != 3) { + print_usage(); + return EXIT_FAILURE; + } + + fd = open(argv[1], O_RDWR); + if (fd < 0) { + perror("unable to open the GPIO chip"); + return EXIT_FAILURE; + } + + memset(&info, 0, sizeof(info)); + ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info); + if (ret) { + perror("chip info ioctl failed"); + return EXIT_FAILURE; + } + + if (strcmp(argv[2], "name") == 0) { + printf("%s\n", info.name); + } else if (strcmp(argv[2], "label") == 0) { + printf("%s\n", info.label); + } else if (strcmp(argv[2], "num-lines") == 0) { + printf("%u\n", info.lines); + } else { + fprintf(stderr, "unknown command: %s\n", argv[2]); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} -- cgit v1.2.3 From b2bb90c80a3e9ba6d479aaf130277d6f96a87161 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 13 Dec 2021 13:42:20 +0100 Subject: selftests: gpio: add a helper for reading GPIO line names Add a simple program that allows to read GPIO line names from the character device. This will be used in gpio-sim selftests. Signed-off-by: Bartosz Golaszewski --- tools/testing/selftests/gpio/.gitignore | 1 + tools/testing/selftests/gpio/Makefile | 2 +- tools/testing/selftests/gpio/gpio-line-name.c | 55 +++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/gpio/gpio-line-name.c diff --git a/tools/testing/selftests/gpio/.gitignore b/tools/testing/selftests/gpio/.gitignore index 4ea4f58dab1a..ededb077a3a6 100644 --- a/tools/testing/selftests/gpio/.gitignore +++ b/tools/testing/selftests/gpio/.gitignore @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only gpio-mockup-cdev gpio-chip-info +gpio-line-name diff --git a/tools/testing/selftests/gpio/Makefile b/tools/testing/selftests/gpio/Makefile index 84b48547f94c..d7d8f1985d99 100644 --- a/tools/testing/selftests/gpio/Makefile +++ b/tools/testing/selftests/gpio/Makefile @@ -2,6 +2,6 @@ TEST_PROGS := gpio-mockup.sh TEST_FILES := gpio-mockup-sysfs.sh -TEST_GEN_PROGS_EXTENDED := gpio-mockup-cdev gpio-chip-info +TEST_GEN_PROGS_EXTENDED := gpio-mockup-cdev gpio-chip-info gpio-line-name include ../lib.mk diff --git a/tools/testing/selftests/gpio/gpio-line-name.c b/tools/testing/selftests/gpio/gpio-line-name.c new file mode 100644 index 000000000000..e635cfadbded --- /dev/null +++ b/tools/testing/selftests/gpio/gpio-line-name.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * GPIO character device helper for reading line names. + * + * Copyright (C) 2021 Bartosz Golaszewski + */ + +#include +#include +#include +#include +#include +#include +#include + +static void print_usage(void) +{ + printf("usage:\n"); + printf(" gpio-line-name \n"); +} + +int main(int argc, char **argv) +{ + struct gpio_v2_line_info info; + int fd, ret; + char *endp; + + if (argc != 3) { + print_usage(); + return EXIT_FAILURE; + } + + fd = open(argv[1], O_RDWR); + if (fd < 0) { + perror("unable to open the GPIO chip"); + return EXIT_FAILURE; + } + + memset(&info, 0, sizeof(info)); + info.offset = strtoul(argv[2], &endp, 10); + if (*endp != '\0') { + print_usage(); + return EXIT_FAILURE; + } + + ret = ioctl(fd, GPIO_V2_GET_LINEINFO_IOCTL, &info); + if (ret) { + perror("line info ioctl failed"); + return EXIT_FAILURE; + } + + printf("%s\n", info.name); + + return EXIT_SUCCESS; +} -- cgit v1.2.3 From 1d96b8f635d98748e251e8e9625cf42d81514425 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 13 Dec 2021 13:43:14 +0100 Subject: selftests: gpio: add test cases for gpio-sim Add a set of tests for the new gpio-sim module. This is a pure shell test-suite and uses the helper programs available in the gpio selftests directory. These test-cases only test the functionalities exposed by the gpio-sim driver, not those handled by core gpiolib code. Signed-off-by: Bartosz Golaszewski --- tools/testing/selftests/gpio/Makefile | 2 +- tools/testing/selftests/gpio/config | 1 + tools/testing/selftests/gpio/gpio-sim.sh | 396 +++++++++++++++++++++++++++++++ 3 files changed, 398 insertions(+), 1 deletion(-) create mode 100755 tools/testing/selftests/gpio/gpio-sim.sh diff --git a/tools/testing/selftests/gpio/Makefile b/tools/testing/selftests/gpio/Makefile index d7d8f1985d99..4c6df61c76a8 100644 --- a/tools/testing/selftests/gpio/Makefile +++ b/tools/testing/selftests/gpio/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -TEST_PROGS := gpio-mockup.sh +TEST_PROGS := gpio-mockup.sh gpio-sim.sh TEST_FILES := gpio-mockup-sysfs.sh TEST_GEN_PROGS_EXTENDED := gpio-mockup-cdev gpio-chip-info gpio-line-name diff --git a/tools/testing/selftests/gpio/config b/tools/testing/selftests/gpio/config index ce100342c20b..409a8532facc 100644 --- a/tools/testing/selftests/gpio/config +++ b/tools/testing/selftests/gpio/config @@ -1,3 +1,4 @@ CONFIG_GPIOLIB=y CONFIG_GPIO_CDEV=y CONFIG_GPIO_MOCKUP=m +CONFIG_GPIO_SIM=m diff --git a/tools/testing/selftests/gpio/gpio-sim.sh b/tools/testing/selftests/gpio/gpio-sim.sh new file mode 100755 index 000000000000..d335a975890c --- /dev/null +++ b/tools/testing/selftests/gpio/gpio-sim.sh @@ -0,0 +1,396 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2021 Bartosz Golaszewski + +BASE_DIR=`dirname $0` +CONFIGFS_DIR="/sys/kernel/config/gpio-sim" +MODULE="gpio-sim" + +fail() { + echo "$*" >&2 + echo "GPIO $MODULE test FAIL" + exit 1 +} + +skip() { + echo "$*" >&2 + echo "GPIO $MODULE test SKIP" + exit 4 +} + +remove_chip() { + local CHIP=$1 + + for FILE in $CONFIGFS_DIR/$CHIP/*; do + BANK=`basename $FILE` + if [ "$BANK" == "live" ] || [ "$BANK" == "dev_name" ]; then + continue + fi + + LINES=`ls $CONFIGFS_DIR/$CHIP/$BANK/ | egrep ^line` + if [ "$?" == 0 ]; then + for LINE in $LINES; do + if [ -e $CONFIGFS_DIR/$CHIP/$BANK/$LINE/hog ]; then + rmdir $CONFIGFS_DIR/$CHIP/$BANK/$LINE/hog || \ + fail "Unable to remove the hog" + fi + + rmdir $CONFIGFS_DIR/$CHIP/$BANK/$LINE || \ + fail "Unable to remove the line" + done + fi + + rmdir $CONFIGFS_DIR/$CHIP/$BANK + done + + rmdir $CONFIGFS_DIR/$CHIP || fail "Unable to remove the chip" +} + +configfs_cleanup() { + for CHIP in `ls $CONFIGFS_DIR/`; do + remove_chip $CHIP + done +} + +create_chip() { + local CHIP=$1 + + mkdir $CONFIGFS_DIR/$CHIP +} + +create_bank() { + local CHIP=$1 + local BANK=$2 + + mkdir $CONFIGFS_DIR/$CHIP/$BANK +} + +set_label() { + local CHIP=$1 + local BANK=$2 + local LABEL=$3 + + echo $LABEL > $CONFIGFS_DIR/$CHIP/$BANK/label || fail "Unable to set the chip label" +} + +set_num_lines() { + local CHIP=$1 + local BANK=$2 + local NUM_LINES=$3 + + echo $NUM_LINES > $CONFIGFS_DIR/$CHIP/$BANK/num_lines || \ + fail "Unable to set the number of lines" +} + +set_line_name() { + local CHIP=$1 + local BANK=$2 + local OFFSET=$3 + local NAME=$4 + local LINE_DIR=$CONFIGFS_DIR/$CHIP/$BANK/line$OFFSET + + test -d $LINE_DIR || mkdir $LINE_DIR + echo $NAME > $LINE_DIR/name || fail "Unable to set the line name" +} + +enable_chip() { + local CHIP=$1 + + echo 1 > $CONFIGFS_DIR/$CHIP/live || fail "Unable to enable the chip" +} + +disable_chip() { + local CHIP=$1 + + echo 0 > $CONFIGFS_DIR/$CHIP/live || fail "Unable to disable the chip" +} + +configfs_chip_name() { + local CHIP=$1 + local BANK=$2 + + cat $CONFIGFS_DIR/$CHIP/$BANK/chip_name 2> /dev/null || \ + fail "unable to read the chip name from configfs" +} + +configfs_dev_name() { + local CHIP=$1 + + cat $CONFIGFS_DIR/$CHIP/dev_name 2> /dev/null || \ + fail "unable to read the device name from configfs" +} + +get_chip_num_lines() { + local CHIP=$1 + local BANK=$2 + + $BASE_DIR/gpio-chip-info /dev/`configfs_chip_name $CHIP $BANK` num-lines || \ + fail "unable to read the number of lines from the character device" +} + +get_chip_label() { + local CHIP=$1 + local BANK=$2 + + $BASE_DIR/gpio-chip-info /dev/`configfs_chip_name $CHIP $BANK` label || \ + fail "unable to read the chip label from the character device" +} + +get_line_name() { + local CHIP=$1 + local BANK=$2 + local OFFSET=$3 + + $BASE_DIR/gpio-line-name /dev/`configfs_chip_name $CHIP $BANK` $OFFSET || \ + fail "unable to read the line name from the character device" +} + +sysfs_set_pull() { + local DEV=$1 + local BANK=$2 + local OFFSET=$3 + local PULL=$4 + local DEVNAME=`configfs_dev_name $DEV` + local CHIPNAME=`configfs_chip_name $DEV $BANK` + local SYSFSPATH="/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio$OFFSET/pull" + + echo $PULL > $SYSFSPATH || fail "Unable to set line pull in sysfs" +} + +# Load the gpio-sim module. This will pull in configfs if needed too. +modprobe gpio-sim || skip "unable to load the gpio-sim module" +# Make sure configfs is mounted at /sys/kernel/config. Wait a bit if needed. +for IDX in `seq 5`; do + if [ "$IDX" -eq "5" ]; then + skip "configfs not mounted at /sys/kernel/config" + fi + + mountpoint -q /sys/kernel/config && break + sleep 0.1 +done +# If the module was already loaded: remove all previous chips +configfs_cleanup + +trap "exit 1" SIGTERM SIGINT +trap configfs_cleanup EXIT + +echo "1. chip_name and dev_name attributes" + +echo "1.1. Chip name is communicated to user" +create_chip chip +create_bank chip bank +enable_chip chip +test -n `cat $CONFIGFS_DIR/chip/bank/chip_name` || fail "chip_name doesn't work" +remove_chip chip + +echo "1.2. chip_name returns 'none' if the chip is still pending" +create_chip chip +create_bank chip bank +test "`cat $CONFIGFS_DIR/chip/bank/chip_name`" = "none" || \ + fail "chip_name doesn't return 'none' for a pending chip" +remove_chip chip + +echo "1.3. Device name is communicated to user" +create_chip chip +create_bank chip bank +enable_chip chip +test -n `cat $CONFIGFS_DIR/chip/dev_name` || fail "dev_name doesn't work" +remove_chip chip + +echo "2. Creating and configuring simulated chips" + +echo "2.1. Default number of lines is 1" +create_chip chip +create_bank chip bank +enable_chip chip +test "`get_chip_num_lines chip bank`" = "1" || fail "default number of lines is not 1" +remove_chip chip + +echo "2.2. Number of lines can be specified" +create_chip chip +create_bank chip bank +set_num_lines chip bank 16 +enable_chip chip +test "`get_chip_num_lines chip bank`" = "16" || fail "number of lines is not 16" +remove_chip chip + +echo "2.3. Label can be set" +create_chip chip +create_bank chip bank +set_label chip bank foobar +enable_chip chip +test "`get_chip_label chip bank`" = "foobar" || fail "label is incorrect" +remove_chip chip + +echo "2.4. Label can be left empty" +create_chip chip +create_bank chip bank +enable_chip chip +test -z "`cat $CONFIGFS_DIR/chip/bank/label`" || fail "label is not empty" +remove_chip chip + +echo "2.5. Line names can be configured" +create_chip chip +create_bank chip bank +set_num_lines chip bank 16 +set_line_name chip bank 0 foo +set_line_name chip bank 2 bar +enable_chip chip +test "`get_line_name chip bank 0`" = "foo" || fail "line name is incorrect" +test "`get_line_name chip bank 2`" = "bar" || fail "line name is incorrect" +remove_chip chip + +echo "2.6. Line config can remain unused if offset is greater than number of lines" +create_chip chip +create_bank chip bank +set_num_lines chip bank 2 +set_line_name chip bank 5 foobar +enable_chip chip +test "`get_line_name chip bank 0`" = "" || fail "line name is incorrect" +test "`get_line_name chip bank 1`" = "" || fail "line name is incorrect" +remove_chip chip + +echo "2.7. Line configfs directory names are sanitized" +create_chip chip +create_bank chip bank +mkdir $CONFIGFS_DIR/chip/bank/line12foobar 2> /dev/null && \ + fail "invalid configfs line name accepted" +mkdir $CONFIGFS_DIR/chip/bank/line_no_offset 2> /dev/null && \ + fail "invalid configfs line name accepted" +remove_chip chip + +echo "2.8. Multiple chips can be created" +CHIPS="chip0 chip1 chip2" +for CHIP in $CHIPS; do + create_chip $CHIP + create_bank $CHIP bank + enable_chip $CHIP +done +for CHIP in $CHIPS; do + remove_chip $CHIP +done + +echo "2.9. Can't modify settings when chip is live" +create_chip chip +create_bank chip bank +enable_chip chip +echo foobar > $CONFIGFS_DIR/chip/bank/label 2> /dev/null && \ + fail "Setting label of a live chip should fail" +echo 8 > $CONFIGFS_DIR/chip/bank/num_lines 2> /dev/null && \ + fail "Setting number of lines of a live chip should fail" +remove_chip chip + +echo "2.10. Can't create line items when chip is live" +create_chip chip +create_bank chip bank +enable_chip chip +mkdir $CONFIGFS_DIR/chip/bank/line0 2> /dev/null && fail "Creating line item should fail" +remove_chip chip + +echo "2.11. Probe errors are propagated to user-space" +create_chip chip +create_bank chip bank +set_num_lines chip bank 99999 +echo 1 > $CONFIGFS_DIR/chip/live 2> /dev/null && fail "Probe error was not propagated" +remove_chip chip + +echo "2.12. Cannot enable a chip without any GPIO banks" +create_chip chip +echo 1 > $CONFIGFS_DIR/chip/live 2> /dev/null && fail "Chip enabled without any GPIO banks" +remove_chip chip + +echo "2.13. Duplicate chip labels are not allowed" +create_chip chip +create_bank chip bank0 +set_label chip bank0 foobar +create_bank chip bank1 +set_label chip bank1 foobar +echo 1 > $CONFIGFS_DIR/chip/live 2> /dev/null && fail "Duplicate chip labels were not rejected" +remove_chip chip + +echo "2.14. Lines can be hogged" +create_chip chip +create_bank chip bank +set_num_lines chip bank 8 +mkdir -p $CONFIGFS_DIR/chip/bank/line4/hog +enable_chip chip +$BASE_DIR/gpio-mockup-cdev -s 1 /dev/`configfs_chip_name chip bank` 4 2> /dev/null && \ + fail "Setting the value of a hogged line shouldn't succeed" +remove_chip chip + +echo "3. Controlling simulated chips" + +echo "3.1. Pull can be set over sysfs" +create_chip chip +create_bank chip bank +set_num_lines chip bank 8 +enable_chip chip +sysfs_set_pull chip bank 0 pull-up +$BASE_DIR/gpio-mockup-cdev /dev/`configfs_chip_name chip bank` 0 +test "$?" = "1" || fail "pull set incorrectly" +sysfs_set_pull chip bank 0 pull-down +$BASE_DIR/gpio-mockup-cdev /dev/`configfs_chip_name chip bank` 1 +test "$?" = "0" || fail "pull set incorrectly" +remove_chip chip + +echo "3.2. Pull can be read from sysfs" +create_chip chip +create_bank chip bank +set_num_lines chip bank 8 +enable_chip chip +DEVNAME=`configfs_dev_name chip` +CHIPNAME=`configfs_chip_name chip bank` +SYSFS_PATH=/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio0/pull +test `cat $SYSFS_PATH` = "pull-down" || fail "reading the pull failed" +sysfs_set_pull chip bank 0 pull-up +test `cat $SYSFS_PATH` = "pull-up" || fail "reading the pull failed" +remove_chip chip + +echo "3.3. Incorrect input in sysfs is rejected" +create_chip chip +create_bank chip bank +set_num_lines chip bank 8 +enable_chip chip +DEVNAME=`configfs_dev_name chip` +CHIPNAME=`configfs_chip_name chip bank` +SYSFS_PATH="/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio0/pull" +echo foobar > $SYSFS_PATH 2> /dev/null && fail "invalid input not detected" +remove_chip chip + +echo "3.4. Can't write to value" +create_chip chip +create_bank chip bank +enable_chip chip +DEVNAME=`configfs_dev_name chip` +CHIPNAME=`configfs_chip_name chip bank` +SYSFS_PATH="/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio0/value" +echo 1 > $SYSFS_PATH 2> /dev/null && fail "writing to 'value' succeeded unexpectedly" +remove_chip chip + +echo "4. Simulated GPIO chips are functional" + +echo "4.1. Values can be read from sysfs" +create_chip chip +create_bank chip bank +set_num_lines chip bank 8 +enable_chip chip +DEVNAME=`configfs_dev_name chip` +CHIPNAME=`configfs_chip_name chip bank` +SYSFS_PATH="/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio0/value" +test `cat $SYSFS_PATH` = "0" || fail "incorrect value read from sysfs" +$BASE_DIR/gpio-mockup-cdev -s 1 /dev/`configfs_chip_name chip bank` 0 & +sleep 0.1 # FIXME Any better way? +test `cat $SYSFS_PATH` = "1" || fail "incorrect value read from sysfs" +kill $! +remove_chip chip + +echo "4.2. Bias settings work correctly" +create_chip chip +create_bank chip bank +set_num_lines chip bank 8 +enable_chip chip +$BASE_DIR/gpio-mockup-cdev -b pull-up /dev/`configfs_chip_name chip bank` 0 +test `cat $SYSFS_PATH` = "1" || fail "bias setting does not work" +remove_chip chip + +echo "GPIO $MODULE test PASS" -- cgit v1.2.3 From 34d9841b4b7bbfa60149417605f6368b4cfb4523 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Sun, 12 Dec 2021 11:13:35 +0800 Subject: gpio: sta2x11: fix typo in a comment The double `all' in the comment in line 327 is repeated. Remove one of them from the comment. Signed-off-by: Jason Wang Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sta2x11.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-sta2x11.c b/drivers/gpio/gpio-sta2x11.c index 392fcab06ab8..e07cca0f8d35 100644 --- a/drivers/gpio/gpio-sta2x11.c +++ b/drivers/gpio/gpio-sta2x11.c @@ -324,7 +324,7 @@ static int gsta_alloc_irq_chip(struct gsta_gpio *chip) if (rv) return rv; - /* Set up all all 128 interrupts: code from setup_generic_chip */ + /* Set up all 128 interrupts: code from setup_generic_chip */ { struct irq_chip_type *ct = gc->chip_types; int i, j; -- cgit v1.2.3 From 40dc227031a65ea2005377ca3baa90d9c677c35c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Dec 2021 17:02:01 +0100 Subject: dt-bindings: gpio: tegra: Convert to json-schema Convert the NVIDIA Tegra GPIO controller device tree bindings from free-form text format to json-schema. Reviewed-by: Linus Walleij Reviewed-by: Rob Herring Signed-off-by: Thierry Reding Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/nvidia,tegra20-gpio.txt | 40 -------- .../bindings/gpio/nvidia,tegra20-gpio.yaml | 110 +++++++++++++++++++++ 2 files changed, 110 insertions(+), 40 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/nvidia,tegra20-gpio.txt create mode 100644 Documentation/devicetree/bindings/gpio/nvidia,tegra20-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/nvidia,tegra20-gpio.txt b/Documentation/devicetree/bindings/gpio/nvidia,tegra20-gpio.txt deleted file mode 100644 index 023c9526e5f8..000000000000 --- a/Documentation/devicetree/bindings/gpio/nvidia,tegra20-gpio.txt +++ /dev/null @@ -1,40 +0,0 @@ -NVIDIA Tegra GPIO controller - -Required properties: -- compatible : "nvidia,tegra-gpio" -- reg : Physical base address and length of the controller's registers. -- interrupts : The interrupt outputs from the controller. For Tegra20, - there should be 7 interrupts specified, and for Tegra30, there should - be 8 interrupts specified. -- #gpio-cells : Should be two. The first cell is the pin number and the - second cell is used to specify optional parameters: - - bit 0 specifies polarity (0 for normal, 1 for inverted) -- gpio-controller : Marks the device node as a GPIO controller. -- #interrupt-cells : Should be 2. - 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. - Valid combinations are 1, 2, 3, 4, 8. -- interrupt-controller : Marks the device node as an interrupt controller. - -Example: - -gpio: gpio@6000d000 { - compatible = "nvidia,tegra20-gpio"; - reg = < 0x6000d000 0x1000 >; - interrupts = < 0 32 0x04 - 0 33 0x04 - 0 34 0x04 - 0 35 0x04 - 0 55 0x04 - 0 87 0x04 - 0 89 0x04 >; - #gpio-cells = <2>; - gpio-controller; - #interrupt-cells = <2>; - interrupt-controller; -}; diff --git a/Documentation/devicetree/bindings/gpio/nvidia,tegra20-gpio.yaml b/Documentation/devicetree/bindings/gpio/nvidia,tegra20-gpio.yaml new file mode 100644 index 000000000000..94b51749ee76 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/nvidia,tegra20-gpio.yaml @@ -0,0 +1,110 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/nvidia,tegra20-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra GPIO Controller (Tegra20 - Tegra210) + +maintainers: + - Thierry Reding + - Jon Hunter + +properties: + compatible: + oneOf: + - enum: + - nvidia,tegra20-gpio + - nvidia,tegra30-gpio + + - items: + - enum: + - nvidia,tegra114-gpio + - nvidia,tegra124-gpio + - nvidia,tegra210-gpio + - const: nvidia,tegra30-gpio + + reg: + maxItems: 1 + + interrupts: + description: The interrupt outputs from the controller. For Tegra20, + there should be 7 interrupts specified, and for Tegra30, there should + be 8 interrupts specified. + + "#gpio-cells": + description: The first cell is the pin number and the second cell is used + to specify the GPIO polarity (0 = active high, 1 = active low). + const: 2 + + gpio-controller: true + + gpio-ranges: + maxItems: 1 + + "#interrupt-cells": + description: | + Should be 2. 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. + + Valid combinations are 1, 2, 3, 4, 8. + const: 2 + + interrupt-controller: true + +allOf: + - if: + properties: + compatible: + contains: + const: nvidia,tegra30-gpio + then: + properties: + interrupts: + minItems: 8 + maxItems: 8 + else: + properties: + interrupts: + minItems: 7 + maxItems: 7 + +required: + - compatible + - reg + - interrupts + - "#gpio-cells" + - gpio-controller + - "#interrupt-cells" + - interrupt-controller + +additionalProperties: + type: object + required: + - gpio-hog + +examples: + - | + #include + + gpio: gpio@6000d000 { + compatible = "nvidia,tegra20-gpio"; + reg = <0x6000d000 0x1000>; + interrupts = , + , + , + , + , + , + ; + #gpio-cells = <2>; + gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + }; -- cgit v1.2.3 From 7501815ffda8f7d46dc077e3c4eb2a814ef6bc18 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Dec 2021 17:02:02 +0100 Subject: dt-bindings: gpio: tegra186: Convert to json-schema Convert the Tegra186 GPIO controller device tree bindings from free-form text format to json-schema. Reviewed-by: Linus Walleij Reviewed-by: Rob Herring Signed-off-by: Thierry Reding Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/nvidia,tegra186-gpio.txt | 165 ---------------- .../bindings/gpio/nvidia,tegra186-gpio.yaml | 210 +++++++++++++++++++++ 2 files changed, 210 insertions(+), 165 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.txt create mode 100644 Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.txt b/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.txt deleted file mode 100644 index adff16c71d21..000000000000 --- a/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.txt +++ /dev/null @@ -1,165 +0,0 @@ -NVIDIA Tegra186 GPIO controllers - -Tegra186 contains two GPIO controllers; a main controller and an "AON" -controller. This binding document applies to both controllers. The register -layouts for the controllers share many similarities, but also some significant -differences. Hence, this document describes closely related but different -bindings and compatible values. - -The Tegra186 GPIO controller allows software to set the IO direction of, and -read/write the value of, numerous GPIO signals. Routing of GPIO signals to -package balls is under the control of a separate pin controller HW block. Two -major sets of registers exist: - -a) Security registers, which allow configuration of allowed access to the GPIO -register set. These registers exist in a single contiguous block of physical -address space. The size of this block, and the security features available, -varies between the different GPIO controllers. - -Access to this set of registers is not necessary in all circumstances. Code -that wishes to configure access to the GPIO registers needs access to these -registers to do so. Code which simply wishes to read or write GPIO data does not -need access to these registers. - -b) GPIO registers, which allow manipulation of the GPIO signals. In some GPIO -controllers, these registers are exposed via multiple "physical aliases" in -address space, each of which access the same underlying state. See the hardware -documentation for rationale. Any particular GPIO client is expected to access -just one of these physical aliases. - -Tegra HW documentation describes a unified naming convention for all GPIOs -implemented by the SoC. Each GPIO is assigned to a port, and a port may control -a number of GPIOs. Thus, each GPIO is named according to an alphabetical port -name and an integer GPIO name within the port. For example, GPIO_PA0, GPIO_PN6, -or GPIO_PCC3. - -The number of ports implemented by each GPIO controller varies. The number of -implemented GPIOs within each port varies. GPIO registers within a controller -are grouped and laid out according to the port they affect. - -The mapping from port name to the GPIO controller that implements that port, and -the mapping from port name to register offset within a controller, are both -extremely non-linear. The header file -describes the port-level mapping. In that file, the naming convention for ports -matches the HW documentation. The values chosen for the names are alphabetically -sorted within a particular controller. Drivers need to map between the DT GPIO -IDs and HW register offsets using a lookup table. - -Each GPIO controller can generate a number of interrupt signals. Each signal -represents the aggregate status for all GPIOs within a set of ports. Thus, the -number of interrupt signals generated by a controller varies as a rough function -of the number of ports it implements. Note that the HW documentation refers to -both the overall controller HW module and the sets-of-ports as "controllers". - -Each GPIO controller in fact generates multiple interrupts signals for each set -of ports. Each GPIO may be configured to feed into a specific one of the -interrupt signals generated by a set-of-ports. The intent is for each generated -signal to be routed to a different CPU, thus allowing different CPUs to each -handle subsets of the interrupts within a port. The status of each of these -per-port-set signals is reported via a separate register. Thus, a driver needs -to know which status register to observe. This binding currently defines no -configuration mechanism for this. By default, drivers should use register -GPIO_${port}_INTERRUPT_STATUS_G1_0. Future revisions to the binding could -define a property to configure this. - -Required properties: -- compatible - Array of strings. - One of: - - "nvidia,tegra186-gpio". - - "nvidia,tegra186-gpio-aon". - - "nvidia,tegra194-gpio". - - "nvidia,tegra194-gpio-aon". -- reg-names - Array of strings. - Contains a list of names for the register spaces described by the reg - property. May contain the following entries, in any order: - - "gpio": Mandatory. GPIO control registers. This may cover either: - a) The single physical alias that this OS should use. - b) All physical aliases that exist in the controller. This is - appropriate when the OS is responsible for managing assignment of - the physical aliases. - - "security": Optional. Security configuration registers. - Users of this binding MUST look up entries in the reg property by name, - using this reg-names property to do so. -- reg - Array of (physical base address, length) tuples. - Must contain one entry per entry in the reg-names property, in a matching - order. -- interrupts - Array of interrupt specifiers. - The interrupt outputs from the HW block, one per set of ports, in the - order the HW manual describes them. The number of entries required varies - depending on compatible value: - - "nvidia,tegra186-gpio": 6 entries. - - "nvidia,tegra186-gpio-aon": 1 entry. - - "nvidia,tegra194-gpio": 6 entries. - - "nvidia,tegra194-gpio-aon": 1 entry. -- gpio-controller - Boolean. - Marks the device node as a GPIO controller/provider. -- #gpio-cells - Single-cell integer. - Must be <2>. - Indicates how many cells are used in a consumer's GPIO specifier. - In the specifier: - - The first cell is the pin number. - See . - - The second cell contains flags: - - Bit 0 specifies polarity - - 0: Active-high (normal). - - 1: Active-low (inverted). -- interrupt-controller - Boolean. - Marks the device node as an interrupt controller/provider. -- #interrupt-cells - Single-cell integer. - Must be <2>. - Indicates how many cells are used in a consumer's interrupt specifier. - In the specifier: - - The first cell is the GPIO number. - See . - - The second cell is contains flags: - - Bits [3:0] indicate trigger type and level: - - 1: Low-to-high edge triggered. - - 2: High-to-low edge triggered. - - 4: Active high level-sensitive. - - 8: Active low level-sensitive. - Valid combinations are 1, 2, 3, 4, 8. - -Example: - -#include - -gpio@2200000 { - compatible = "nvidia,tegra186-gpio"; - reg-names = "security", "gpio"; - reg = - <0x0 0x2200000 0x0 0x10000>, - <0x0 0x2210000 0x0 0x10000>; - interrupts = - <0 47 IRQ_TYPE_LEVEL_HIGH>, - <0 50 IRQ_TYPE_LEVEL_HIGH>, - <0 53 IRQ_TYPE_LEVEL_HIGH>, - <0 56 IRQ_TYPE_LEVEL_HIGH>, - <0 59 IRQ_TYPE_LEVEL_HIGH>, - <0 180 IRQ_TYPE_LEVEL_HIGH>; - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; -}; - -gpio@c2f0000 { - compatible = "nvidia,tegra186-gpio-aon"; - reg-names = "security", "gpio"; - reg = - <0x0 0xc2f0000 0x0 0x1000>, - <0x0 0xc2f1000 0x0 0x1000>; - interrupts = - <0 60 IRQ_TYPE_LEVEL_HIGH>; - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; -}; diff --git a/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.yaml b/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.yaml new file mode 100644 index 000000000000..cad05ec682d4 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.yaml @@ -0,0 +1,210 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/nvidia,tegra186-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra GPIO Controller (Tegra186 and later) + +maintainers: + - Thierry Reding + - Jon Hunter + +description: | + Tegra186 contains two GPIO controllers; a main controller and an "AON" + controller. This binding document applies to both controllers. The register + layouts for the controllers share many similarities, but also some + significant differences. Hence, this document describes closely related but + different bindings and compatible values. + + The Tegra186 GPIO controller allows software to set the IO direction of, + and read/write the value of, numerous GPIO signals. Routing of GPIO signals + to package balls is under the control of a separate pin controller hardware + block. Two major sets of registers exist: + + a) Security registers, which allow configuration of allowed access to the + GPIO register set. These registers exist in a single contiguous block + of physical address space. The size of this block, and the security + features available, varies between the different GPIO controllers. + + Access to this set of registers is not necessary in all circumstances. + Code that wishes to configure access to the GPIO registers needs access + to these registers to do so. Code which simply wishes to read or write + GPIO data does not need access to these registers. + + b) GPIO registers, which allow manipulation of the GPIO signals. In some + GPIO controllers, these registers are exposed via multiple "physical + aliases" in address space, each of which access the same underlying + state. See the hardware documentation for rationale. Any particular + GPIO client is expected to access just one of these physical aliases. + + Tegra HW documentation describes a unified naming convention for all GPIOs + implemented by the SoC. Each GPIO is assigned to a port, and a port may + control a number of GPIOs. Thus, each GPIO is named according to an + alphabetical port name and an integer GPIO name within the port. For + example, GPIO_PA0, GPIO_PN6, or GPIO_PCC3. + + The number of ports implemented by each GPIO controller varies. The number + of implemented GPIOs within each port varies. GPIO registers within a + controller are grouped and laid out according to the port they affect. + + The mapping from port name to the GPIO controller that implements that + port, and the mapping from port name to register offset within a + controller, are both extremely non-linear. The header file + describes the port-level mapping. In + that file, the naming convention for ports matches the HW documentation. + The values chosen for the names are alphabetically sorted within a + particular controller. Drivers need to map between the DT GPIO IDs and HW + register offsets using a lookup table. + + Each GPIO controller can generate a number of interrupt signals. Each + signal represents the aggregate status for all GPIOs within a set of + ports. Thus, the number of interrupt signals generated by a controller + varies as a rough function of the number of ports it implements. Note + that the HW documentation refers to both the overall controller HW + module and the sets-of-ports as "controllers". + + Each GPIO controller in fact generates multiple interrupts signals for + each set of ports. Each GPIO may be configured to feed into a specific + one of the interrupt signals generated by a set-of-ports. The intent is + for each generated signal to be routed to a different CPU, thus allowing + different CPUs to each handle subsets of the interrupts within a port. + The status of each of these per-port-set signals is reported via a + separate register. Thus, a driver needs to know which status register to + observe. This binding currently defines no configuration mechanism for + this. By default, drivers should use register + GPIO_${port}_INTERRUPT_STATUS_G1_0. Future revisions to the binding could + define a property to configure this. + +properties: + compatible: + enum: + - nvidia,tegra186-gpio + - nvidia,tegra186-gpio-aon + - nvidia,tegra194-gpio + - nvidia,tegra194-gpio-aon + + reg-names: + items: + - const: security + - const: gpio + minItems: 1 + + reg: + items: + - description: Security configuration registers. + - description: | + GPIO control registers. This may cover either: + + a) The single physical alias that this OS should use. + b) All physical aliases that exist in the controller. This is + appropriate when the OS is responsible for managing assignment + of the physical aliases. + minItems: 1 + + interrupts: + description: The interrupt outputs from the HW block, one per set of + ports, in the order the HW manual describes them. The number of entries + required varies depending on compatible value. + + gpio-controller: true + + "#gpio-cells": + description: | + Indicates how many cells are used in a consumer's GPIO specifier. In the + specifier: + + - The first cell is the pin number. + See . + - The second cell contains flags: + - Bit 0 specifies polarity + - 0: Active-high (normal). + - 1: Active-low (inverted). + const: 2 + + interrupt-controller: true + + "#interrupt-cells": + description: | + Indicates how many cells are used in a consumer's interrupt specifier. + In the specifier: + + - The first cell is the GPIO number. + See . + - The second cell is contains flags: + - Bits [3:0] indicate trigger type and level: + - 1: Low-to-high edge triggered. + - 2: High-to-low edge triggered. + - 4: Active high level-sensitive. + - 8: Active low level-sensitive. + + Valid combinations are 1, 2, 3, 4, 8. + const: 2 + +allOf: + - if: + properties: + compatible: + contains: + enum: + - nvidia,tegra186-gpio + - nvidia,tegra194-gpio + then: + properties: + interrupts: + minItems: 6 + maxItems: 48 + + - if: + properties: + compatible: + contains: + enum: + - nvidia,tegra186-gpio-aon + - nvidia,tegra194-gpio-aon + then: + properties: + interrupts: + minItems: 1 + maxItems: 4 + +required: + - compatible + - reg + - reg-names + - interrupts + +additionalProperties: false + +examples: + - | + #include + + gpio@2200000 { + compatible = "nvidia,tegra186-gpio"; + reg-names = "security", "gpio"; + reg = <0x2200000 0x10000>, + <0x2210000 0x10000>; + interrupts = <0 47 IRQ_TYPE_LEVEL_HIGH>, + <0 50 IRQ_TYPE_LEVEL_HIGH>, + <0 53 IRQ_TYPE_LEVEL_HIGH>, + <0 56 IRQ_TYPE_LEVEL_HIGH>, + <0 59 IRQ_TYPE_LEVEL_HIGH>, + <0 180 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpio@c2f0000 { + compatible = "nvidia,tegra186-gpio-aon"; + reg-names = "security", "gpio"; + reg = <0xc2f0000 0x1000>, + <0xc2f1000 0x1000>; + interrupts = <0 60 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; -- cgit v1.2.3 From a8b10f3d12cfc168a389f3d97e1f762732d4d849 Mon Sep 17 00:00:00 2001 From: Prathamesh Shete Date: Fri, 10 Dec 2021 17:02:03 +0100 Subject: dt-bindings: gpio: Add Tegra234 support Extend the existing Tegra186 GPIO controller device tree bindings with support for the GPIO controller found on Tegra234. The number of pins is slightly different, but the programming model remains the same. Signed-off-by: Prathamesh Shete Reviewed-by: Linus Walleij Reviewed-by: Rob Herring [treding@nvidia.com: update device tree bindings] Signed-off-by: Thierry Reding Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/nvidia,tegra186-gpio.yaml | 4 ++ include/dt-bindings/gpio/tegra234-gpio.h | 63 ++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 include/dt-bindings/gpio/tegra234-gpio.h diff --git a/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.yaml b/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.yaml index cad05ec682d4..4ef06b2ff1ff 100644 --- a/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.yaml @@ -83,6 +83,8 @@ properties: - nvidia,tegra186-gpio-aon - nvidia,tegra194-gpio - nvidia,tegra194-gpio-aon + - nvidia,tegra234-gpio + - nvidia,tegra234-gpio-aon reg-names: items: @@ -149,6 +151,7 @@ allOf: enum: - nvidia,tegra186-gpio - nvidia,tegra194-gpio + - nvidia,tegra234-gpio then: properties: interrupts: @@ -162,6 +165,7 @@ allOf: enum: - nvidia,tegra186-gpio-aon - nvidia,tegra194-gpio-aon + - nvidia,tegra234-gpio-aon then: properties: interrupts: diff --git a/include/dt-bindings/gpio/tegra234-gpio.h b/include/dt-bindings/gpio/tegra234-gpio.h new file mode 100644 index 000000000000..d7a1f2e298e8 --- /dev/null +++ b/include/dt-bindings/gpio/tegra234-gpio.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. */ + +/* + * This header provides constants for binding nvidia,tegra234-gpio*. + * + * The first cell in Tegra's GPIO specifier is the GPIO ID. The macros below + * provide names for this. + * + * The second cell contains standard flag values specified in gpio.h. + */ + +#ifndef _DT_BINDINGS_GPIO_TEGRA234_GPIO_H +#define _DT_BINDINGS_GPIO_TEGRA234_GPIO_H + +#include + +/* GPIOs implemented by main GPIO controller */ +#define TEGRA234_MAIN_GPIO_PORT_A 0 +#define TEGRA234_MAIN_GPIO_PORT_B 1 +#define TEGRA234_MAIN_GPIO_PORT_C 2 +#define TEGRA234_MAIN_GPIO_PORT_D 3 +#define TEGRA234_MAIN_GPIO_PORT_E 4 +#define TEGRA234_MAIN_GPIO_PORT_F 5 +#define TEGRA234_MAIN_GPIO_PORT_G 6 +#define TEGRA234_MAIN_GPIO_PORT_H 7 +#define TEGRA234_MAIN_GPIO_PORT_I 8 +#define TEGRA234_MAIN_GPIO_PORT_J 9 +#define TEGRA234_MAIN_GPIO_PORT_K 10 +#define TEGRA234_MAIN_GPIO_PORT_L 11 +#define TEGRA234_MAIN_GPIO_PORT_M 12 +#define TEGRA234_MAIN_GPIO_PORT_N 13 +#define TEGRA234_MAIN_GPIO_PORT_P 14 +#define TEGRA234_MAIN_GPIO_PORT_Q 15 +#define TEGRA234_MAIN_GPIO_PORT_R 16 +#define TEGRA234_MAIN_GPIO_PORT_S 17 +#define TEGRA234_MAIN_GPIO_PORT_T 18 +#define TEGRA234_MAIN_GPIO_PORT_U 19 +#define TEGRA234_MAIN_GPIO_PORT_V 20 +#define TEGRA234_MAIN_GPIO_PORT_X 21 +#define TEGRA234_MAIN_GPIO_PORT_Y 22 +#define TEGRA234_MAIN_GPIO_PORT_Z 23 +#define TEGRA234_MAIN_GPIO_PORT_AC 24 +#define TEGRA234_MAIN_GPIO_PORT_AD 25 +#define TEGRA234_MAIN_GPIO_PORT_AE 26 +#define TEGRA234_MAIN_GPIO_PORT_AF 27 +#define TEGRA234_MAIN_GPIO_PORT_AG 28 + +#define TEGRA234_MAIN_GPIO(port, offset) \ + ((TEGRA234_MAIN_GPIO_PORT_##port * 8) + offset) + +/* GPIOs implemented by AON GPIO controller */ +#define TEGRA234_AON_GPIO_PORT_AA 0 +#define TEGRA234_AON_GPIO_PORT_BB 1 +#define TEGRA234_AON_GPIO_PORT_CC 2 +#define TEGRA234_AON_GPIO_PORT_DD 3 +#define TEGRA234_AON_GPIO_PORT_EE 4 +#define TEGRA234_AON_GPIO_PORT_GG 5 + +#define TEGRA234_AON_GPIO(port, offset) \ + ((TEGRA234_AON_GPIO_PORT_##port * 8) + offset) + +#endif -- cgit v1.2.3 From 1db9b241bb56c6498766015049097df965ff8df2 Mon Sep 17 00:00:00 2001 From: Prathamesh Shete Date: Fri, 10 Dec 2021 17:02:04 +0100 Subject: gpio: tegra186: Add support for Tegra234 Extend the existing Tegra186 GPIO controller driver with support for the GPIO controller found on Tegra234. While the programming model remains the same, the number of pins has slightly changed. Signed-off-by: Prathamesh Shete Reviewed-by: Linus Walleij Signed-off-by: Thierry Reding Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra186.c | 76 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index c026e7141e4e..91c77fccc1e6 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -14,6 +14,7 @@ #include #include +#include /* security registers */ #define TEGRA186_GPIO_CTL_SCR 0x0c @@ -972,6 +973,75 @@ static const struct tegra_gpio_soc tegra194_aon_soc = { .num_irqs_per_bank = 8, }; +#define TEGRA234_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + [TEGRA234_MAIN_GPIO_PORT_##_name] = { \ + .name = #_name, \ + .bank = _bank, \ + .port = _port, \ + .pins = _pins, \ + } + +static const struct tegra_gpio_port tegra234_main_ports[] = { + TEGRA234_MAIN_GPIO_PORT( A, 0, 0, 8), + TEGRA234_MAIN_GPIO_PORT( B, 0, 3, 1), + TEGRA234_MAIN_GPIO_PORT( C, 5, 1, 8), + TEGRA234_MAIN_GPIO_PORT( D, 5, 2, 4), + TEGRA234_MAIN_GPIO_PORT( E, 5, 3, 8), + TEGRA234_MAIN_GPIO_PORT( F, 5, 4, 6), + TEGRA234_MAIN_GPIO_PORT( G, 4, 0, 8), + TEGRA234_MAIN_GPIO_PORT( H, 4, 1, 8), + TEGRA234_MAIN_GPIO_PORT( I, 4, 2, 7), + TEGRA234_MAIN_GPIO_PORT( J, 5, 0, 6), + TEGRA234_MAIN_GPIO_PORT( K, 3, 0, 8), + TEGRA234_MAIN_GPIO_PORT( L, 3, 1, 4), + TEGRA234_MAIN_GPIO_PORT( M, 2, 0, 8), + TEGRA234_MAIN_GPIO_PORT( N, 2, 1, 8), + TEGRA234_MAIN_GPIO_PORT( P, 2, 2, 8), + TEGRA234_MAIN_GPIO_PORT( Q, 2, 3, 8), + TEGRA234_MAIN_GPIO_PORT( R, 2, 4, 6), + TEGRA234_MAIN_GPIO_PORT( X, 1, 0, 8), + TEGRA234_MAIN_GPIO_PORT( Y, 1, 1, 8), + TEGRA234_MAIN_GPIO_PORT( Z, 1, 2, 8), + TEGRA234_MAIN_GPIO_PORT(AC, 0, 1, 8), + TEGRA234_MAIN_GPIO_PORT(AD, 0, 2, 4), + TEGRA234_MAIN_GPIO_PORT(AE, 3, 3, 2), + TEGRA234_MAIN_GPIO_PORT(AF, 3, 4, 4), + TEGRA234_MAIN_GPIO_PORT(AG, 3, 2, 8), +}; + +static const struct tegra_gpio_soc tegra234_main_soc = { + .num_ports = ARRAY_SIZE(tegra234_main_ports), + .ports = tegra234_main_ports, + .name = "tegra234-gpio", + .instance = 0, + .num_irqs_per_bank = 8, +}; + +#define TEGRA234_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + [TEGRA234_AON_GPIO_PORT_##_name] = { \ + .name = #_name, \ + .bank = _bank, \ + .port = _port, \ + .pins = _pins, \ + } + +static const struct tegra_gpio_port tegra234_aon_ports[] = { + TEGRA234_AON_GPIO_PORT(AA, 0, 4, 8), + TEGRA234_AON_GPIO_PORT(BB, 0, 5, 4), + TEGRA234_AON_GPIO_PORT(CC, 0, 2, 8), + TEGRA234_AON_GPIO_PORT(DD, 0, 3, 3), + TEGRA234_AON_GPIO_PORT(EE, 0, 0, 8), + TEGRA234_AON_GPIO_PORT(GG, 0, 1, 1), +}; + +static const struct tegra_gpio_soc tegra234_aon_soc = { + .num_ports = ARRAY_SIZE(tegra234_aon_ports), + .ports = tegra234_aon_ports, + .name = "tegra234-gpio-aon", + .instance = 1, + .num_irqs_per_bank = 8, +}; + static const struct of_device_id tegra186_gpio_of_match[] = { { .compatible = "nvidia,tegra186-gpio", @@ -985,6 +1055,12 @@ static const struct of_device_id tegra186_gpio_of_match[] = { }, { .compatible = "nvidia,tegra194-gpio-aon", .data = &tegra194_aon_soc + }, { + .compatible = "nvidia,tegra234-gpio", + .data = &tegra234_main_soc + }, { + .compatible = "nvidia,tegra234-gpio-aon", + .data = &tegra234_aon_soc }, { /* sentinel */ } -- cgit v1.2.3 From 2ac5eb840f1da2243551c280f1e55f7a069db3da Mon Sep 17 00:00:00 2001 From: Hsu Yuchang Date: Fri, 10 Dec 2021 17:03:15 +0800 Subject: gpio: amdpt: add new device ID and 24-pin support Add an ACPI HID(AMDIF031) and pin number in the pt_gpio_acpi_match. Signed-off-by: Yuchang Hsu Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-amdpt.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-amdpt.c b/drivers/gpio/gpio-amdpt.c index bbf53e289141..13f4e2af3800 100644 --- a/drivers/gpio/gpio-amdpt.c +++ b/drivers/gpio/gpio-amdpt.c @@ -14,6 +14,7 @@ #include #define PT_TOTAL_GPIO 8 +#define PT_TOTAL_GPIO_EX 24 /* PCI-E MMIO register offsets */ #define PT_DIRECTION_REG 0x00 @@ -103,7 +104,7 @@ static int pt_gpio_probe(struct platform_device *pdev) pt_gpio->gc.owner = THIS_MODULE; pt_gpio->gc.request = pt_gpio_request; pt_gpio->gc.free = pt_gpio_free; - pt_gpio->gc.ngpio = PT_TOTAL_GPIO; + pt_gpio->gc.ngpio = (uintptr_t)device_get_match_data(dev); #if defined(CONFIG_OF_GPIO) pt_gpio->gc.of_node = dev->of_node; #endif @@ -133,8 +134,9 @@ static int pt_gpio_remove(struct platform_device *pdev) } static const struct acpi_device_id pt_gpio_acpi_match[] = { - { "AMDF030", 0 }, - { "AMDIF030", 0 }, + { "AMDF030", PT_TOTAL_GPIO }, + { "AMDIF030", PT_TOTAL_GPIO }, + { "AMDIF031", PT_TOTAL_GPIO_EX }, { }, }; MODULE_DEVICE_TABLE(acpi, pt_gpio_acpi_match); -- cgit v1.2.3 From f7eda6fe03226b8a608a5d52a1e8273a14fba07c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 13 Dec 2021 17:23:55 +0100 Subject: selftests: gpio: gpio-sim: remove bashisms '==' is a bashisms and not understood by POSIX shell. Drop it from gpio-sim selftests. Signed-off-by: Bartosz Golaszewski --- tools/testing/selftests/gpio/gpio-sim.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/gpio/gpio-sim.sh b/tools/testing/selftests/gpio/gpio-sim.sh index d335a975890c..c913d5aec768 100755 --- a/tools/testing/selftests/gpio/gpio-sim.sh +++ b/tools/testing/selftests/gpio/gpio-sim.sh @@ -23,12 +23,12 @@ remove_chip() { for FILE in $CONFIGFS_DIR/$CHIP/*; do BANK=`basename $FILE` - if [ "$BANK" == "live" ] || [ "$BANK" == "dev_name" ]; then + if [ "$BANK" = "live" ] || [ "$BANK" = "dev_name" ]; then continue fi LINES=`ls $CONFIGFS_DIR/$CHIP/$BANK/ | egrep ^line` - if [ "$?" == 0 ]; then + if [ "$?" = 0 ]; then for LINE in $LINES; do if [ -e $CONFIGFS_DIR/$CHIP/$BANK/$LINE/hog ]; then rmdir $CONFIGFS_DIR/$CHIP/$BANK/$LINE/hog || \ -- cgit v1.2.3 From 36ccddf80e56b8c51604bafd449522a5271bfd35 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 14 Dec 2021 09:06:51 +0100 Subject: selftests: gpio: gpio-sim: avoid forking test twice Use '-o' within [] in order to avoid spawning two processes for test. Suggested-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko --- tools/testing/selftests/gpio/gpio-sim.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/gpio/gpio-sim.sh b/tools/testing/selftests/gpio/gpio-sim.sh index c913d5aec768..341e3de00896 100755 --- a/tools/testing/selftests/gpio/gpio-sim.sh +++ b/tools/testing/selftests/gpio/gpio-sim.sh @@ -23,7 +23,7 @@ remove_chip() { for FILE in $CONFIGFS_DIR/$CHIP/*; do BANK=`basename $FILE` - if [ "$BANK" = "live" ] || [ "$BANK" = "dev_name" ]; then + if [ "$BANK" = "live" -o "$BANK" = "dev_name" ]; then continue fi -- cgit v1.2.3 From c73960bb0a4320b9ed0d5f202fa0a2bc7373c214 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Tue, 14 Dec 2021 22:59:56 +0100 Subject: gpiolib: allow line names from device props to override driver names Some GPIO providers set names for GPIO lines that match the names of the pins on the SoC, or variations on that theme. These names are generic more often that not, such as pioC12 in the at91 case. These generic names block the possibility to set more useful GPIO line names with device properties (i.e. gpio-line-names). Allow overriding a generic name given by the GPIO driver if there is a name given to the GPIO line using device properties, but leave the generic name alone if no better name is available. However, there is a risk. If user space is depending on the above mentioned fixed GPIO names, AND there are device properties that previously did not reach the surface, the name change might cause regressions. But hopefully this stays below the radar... Reviewed-by: Andy Shevchenko Tested-by: Alexander Dahl Signed-off-by: Peter Rosin Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4d666767c9ab..3859911b61e9 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -422,8 +422,16 @@ static int devprop_gpiochip_set_names(struct gpio_chip *chip) if (count > chip->ngpio) count = chip->ngpio; - for (i = 0; i < count; i++) - gdev->descs[i].name = names[chip->offset + i]; + for (i = 0; i < count; i++) { + /* + * Allow overriding "fixed" names provided by the GPIO + * provider. The "fixed" names are more often than not + * generic and less informative than the names given in + * device properties. + */ + if (names[chip->offset + i] && names[chip->offset + i][0]) + gdev->descs[i].name = names[chip->offset + i]; + } kfree(names); @@ -734,10 +742,12 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, INIT_LIST_HEAD(&gdev->pin_ranges); #endif - if (gc->names) + if (gc->names) { ret = gpiochip_set_desc_names(gc); - else - ret = devprop_gpiochip_set_names(gc); + if (ret) + goto err_remove_from_list; + } + ret = devprop_gpiochip_set_names(gc); if (ret) goto err_remove_from_list; -- cgit v1.2.3 From 448cf90513d954ba5a61ce392809d6936902a9e6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 17 Dec 2021 17:39:35 +0100 Subject: gpio: Get rid of duplicate of_node assignment in the drivers GPIO library does copy the of_node from the parent device of the GPIO chip, there is no need to repeat this in the individual drivers. Remove these assignment all at once. For the details one may look into the of_gpio_dev_init() implementation. While at it, remove duplicate parent device assignment where it is the case. Signed-off-by: Andy Shevchenko Reviewed-By: Matti Vaittinen Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-adnp.c | 1 - drivers/gpio/gpio-amdpt.c | 4 +--- drivers/gpio/gpio-bd71828.c | 1 - drivers/gpio/gpio-brcmstb.c | 1 - drivers/gpio/gpio-davinci.c | 1 - drivers/gpio/gpio-eic-sprd.c | 1 - drivers/gpio/gpio-em.c | 1 - drivers/gpio/gpio-ge.c | 1 - drivers/gpio/gpio-grgpio.c | 1 - drivers/gpio/gpio-gw-pld.c | 1 - drivers/gpio/gpio-mt7621.c | 1 - drivers/gpio/gpio-mvebu.c | 1 - drivers/gpio/gpio-omap.c | 3 --- drivers/gpio/gpio-palmas.c | 4 +--- drivers/gpio/gpio-pmic-eic-sprd.c | 1 - drivers/gpio/gpio-raspberrypi-exp.c | 1 - drivers/gpio/gpio-rda.c | 2 -- drivers/gpio/gpio-rockchip.c | 3 --- drivers/gpio/gpio-sama5d2-piobu.c | 1 - drivers/gpio/gpio-sprd.c | 1 - drivers/gpio/gpio-stmpe.c | 1 - drivers/gpio/gpio-tc3589x.c | 1 - drivers/gpio/gpio-tegra186.c | 1 - drivers/gpio/gpio-tps65218.c | 3 --- drivers/gpio/gpio-vf610.c | 1 - 25 files changed, 2 insertions(+), 36 deletions(-) diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index 8eedfc6451df..cc349d4e4973 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -458,7 +458,6 @@ static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios, chip->ngpio = num_gpios; chip->label = adnp->client->name; chip->parent = &adnp->client->dev; - chip->of_node = chip->parent->of_node; chip->owner = THIS_MODULE; if (is_irq_controller) { diff --git a/drivers/gpio/gpio-amdpt.c b/drivers/gpio/gpio-amdpt.c index 13f4e2af3800..8cfb353c3abb 100644 --- a/drivers/gpio/gpio-amdpt.c +++ b/drivers/gpio/gpio-amdpt.c @@ -105,9 +105,7 @@ static int pt_gpio_probe(struct platform_device *pdev) pt_gpio->gc.request = pt_gpio_request; pt_gpio->gc.free = pt_gpio_free; pt_gpio->gc.ngpio = (uintptr_t)device_get_match_data(dev); -#if defined(CONFIG_OF_GPIO) - pt_gpio->gc.of_node = dev->of_node; -#endif + ret = gpiochip_add_data(&pt_gpio->gc, pt_gpio); if (ret) { dev_err(dev, "Failed to register GPIO lib\n"); diff --git a/drivers/gpio/gpio-bd71828.c b/drivers/gpio/gpio-bd71828.c index c8e382b53f2f..b2ccc320c7b5 100644 --- a/drivers/gpio/gpio-bd71828.c +++ b/drivers/gpio/gpio-bd71828.c @@ -121,7 +121,6 @@ static int bd71828_probe(struct platform_device *pdev) * "gpio-reserved-ranges" and exclude them from control */ bdgpio->gpio.ngpio = 4; - bdgpio->gpio.of_node = dev->parent->of_node; bdgpio->regmap = dev_get_regmap(dev->parent, NULL); if (!bdgpio->regmap) return -ENODEV; diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index 895a79936248..176c264bb959 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -703,7 +703,6 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) goto fail; } - gc->of_node = np; gc->owner = THIS_MODULE; gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pOF", dev->of_node); if (!gc->label) { diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index cb5afaa7ed48..f960587f86a3 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -254,7 +254,6 @@ static int davinci_gpio_probe(struct platform_device *pdev) #ifdef CONFIG_OF_GPIO chips->chip.of_gpio_n_cells = 2; chips->chip.parent = dev; - chips->chip.of_node = dev->of_node; chips->chip.request = gpiochip_generic_request; chips->chip.free = gpiochip_generic_free; #endif diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index 865ab2b34fdd..8d722e026e9c 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -609,7 +609,6 @@ static int sprd_eic_probe(struct platform_device *pdev) sprd_eic->chip.ngpio = pdata->num_eics; sprd_eic->chip.base = -1; sprd_eic->chip.parent = &pdev->dev; - sprd_eic->chip.of_node = pdev->dev.of_node; sprd_eic->chip.direction_input = sprd_eic_direction_input; switch (sprd_eic->type) { case SPRD_EIC_DEBOUNCE: diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index 90b336e6ee27..858e6ebbb584 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -306,7 +306,6 @@ static int em_gio_probe(struct platform_device *pdev) } gpio_chip = &p->gpio_chip; - gpio_chip->of_node = dev->of_node; gpio_chip->direction_input = em_gio_direction_input; gpio_chip->get = em_gio_get; gpio_chip->direction_output = em_gio_direction_output; diff --git a/drivers/gpio/gpio-ge.c b/drivers/gpio/gpio-ge.c index 636952769bc8..f6a3de99f7db 100644 --- a/drivers/gpio/gpio-ge.c +++ b/drivers/gpio/gpio-ge.c @@ -82,7 +82,6 @@ static int __init gef_gpio_probe(struct platform_device *pdev) gc->base = -1; gc->ngpio = (u16)(uintptr_t)of_device_get_match_data(&pdev->dev); gc->of_gpio_n_cells = 2; - gc->of_node = pdev->dev.of_node; /* This function adds a memory mapped GPIO chip */ ret = devm_gpiochip_add_data(&pdev->dev, gc, NULL); diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index f954359c9544..23d447e17a67 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -358,7 +358,6 @@ static int grgpio_probe(struct platform_device *ofdev) priv->imask = gc->read_reg(regs + GRGPIO_IMASK); priv->dev = &ofdev->dev; - gc->of_node = np; gc->owner = THIS_MODULE; gc->to_irq = grgpio_to_irq; gc->label = devm_kasprintf(&ofdev->dev, GFP_KERNEL, "%pOF", np); diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c index 242112ff60ee..77a3fbd46111 100644 --- a/drivers/gpio/gpio-gw-pld.c +++ b/drivers/gpio/gpio-gw-pld.c @@ -82,7 +82,6 @@ static int gw_pld_probe(struct i2c_client *client, gw->chip.base = -1; gw->chip.can_sleep = true; gw->chip.parent = dev; - gw->chip.of_node = np; gw->chip.owner = THIS_MODULE; gw->chip.label = dev_name(dev); gw->chip.ngpio = 8; diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index c3658a597a80..52b49e7a0a80 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -217,7 +217,6 @@ mediatek_gpio_bank_probe(struct device *dev, memset(rg, 0, sizeof(*rg)); spin_lock_init(&rg->lock); - rg->chip.of_node = node; rg->bank = bank; dat = mtk->base + GPIO_REG_DATA + (rg->bank * GPIO_BANK_STRIDE); diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 8f429d9f3661..4c1f9e1091b7 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -1183,7 +1183,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev) mvchip->chip.base = id * MVEBU_MAX_GPIO_PER_BANK; mvchip->chip.ngpio = ngpios; mvchip->chip.can_sleep = false; - mvchip->chip.of_node = np; mvchip->chip.dbg_show = mvebu_gpio_dbg_show; if (soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 415e8df89d6f..e099c39e0355 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1419,9 +1419,6 @@ static int omap_gpio_probe(struct platform_device *pdev) bank->is_mpuio = pdata->is_mpuio; bank->non_wakeup_gpios = pdata->non_wakeup_gpios; bank->regs = pdata->regs; -#ifdef CONFIG_OF_GPIO - bank->chip.of_node = of_node_get(node); -#endif if (node) { if (!of_property_read_bool(node, "ti,gpio-always-on")) diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c index e8e9029ba5bd..bac10c2faf56 100644 --- a/drivers/gpio/gpio-palmas.c +++ b/drivers/gpio/gpio-palmas.c @@ -170,9 +170,7 @@ static int palmas_gpio_probe(struct platform_device *pdev) palmas_gpio->gpio_chip.set = palmas_gpio_set; palmas_gpio->gpio_chip.get = palmas_gpio_get; palmas_gpio->gpio_chip.parent = &pdev->dev; -#ifdef CONFIG_OF_GPIO - palmas_gpio->gpio_chip.of_node = pdev->dev.of_node; -#endif + palmas_pdata = dev_get_platdata(palmas->dev); if (palmas_pdata && palmas_pdata->gpio_base) palmas_gpio->gpio_chip.base = palmas_pdata->gpio_base; diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c index 938285190566..e518490c4b68 100644 --- a/drivers/gpio/gpio-pmic-eic-sprd.c +++ b/drivers/gpio/gpio-pmic-eic-sprd.c @@ -331,7 +331,6 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev) pmic_eic->chip.ngpio = SPRD_PMIC_EIC_NR; pmic_eic->chip.base = -1; pmic_eic->chip.parent = &pdev->dev; - pmic_eic->chip.of_node = pdev->dev.of_node; pmic_eic->chip.direction_input = sprd_pmic_eic_direction_input; pmic_eic->chip.request = sprd_pmic_eic_request; pmic_eic->chip.free = sprd_pmic_eic_free; diff --git a/drivers/gpio/gpio-raspberrypi-exp.c b/drivers/gpio/gpio-raspberrypi-exp.c index 64a552ecc2ad..3c414e0005fc 100644 --- a/drivers/gpio/gpio-raspberrypi-exp.c +++ b/drivers/gpio/gpio-raspberrypi-exp.c @@ -221,7 +221,6 @@ static int rpi_exp_gpio_probe(struct platform_device *pdev) rpi_gpio->gc.parent = dev; rpi_gpio->gc.label = MODULE_NAME; rpi_gpio->gc.owner = THIS_MODULE; - rpi_gpio->gc.of_node = np; rpi_gpio->gc.base = -1; rpi_gpio->gc.ngpio = NUM_GPIO; diff --git a/drivers/gpio/gpio-rda.c b/drivers/gpio/gpio-rda.c index 463846431183..0d03f525dcd3 100644 --- a/drivers/gpio/gpio-rda.c +++ b/drivers/gpio/gpio-rda.c @@ -240,8 +240,6 @@ static int rda_gpio_probe(struct platform_device *pdev) rda_gpio->chip.label = dev_name(dev); rda_gpio->chip.ngpio = ngpios; rda_gpio->chip.base = -1; - rda_gpio->chip.parent = dev; - rda_gpio->chip.of_node = np; if (rda_gpio->irq >= 0) { rda_gpio->irq_chip.name = "rda-gpio", diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index c1b8e5dbbcc4..a4c4e4584f5b 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -584,9 +584,6 @@ static int rockchip_gpiolib_register(struct rockchip_pin_bank *bank) gc->ngpio = bank->nr_pins; gc->label = bank->name; gc->parent = bank->dev; -#ifdef CONFIG_OF_GPIO - gc->of_node = of_node_get(bank->of_node); -#endif ret = gpiochip_add_data(gc, bank); if (ret) { diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c index b7c950658170..3e95da717fc9 100644 --- a/drivers/gpio/gpio-sama5d2-piobu.c +++ b/drivers/gpio/gpio-sama5d2-piobu.c @@ -192,7 +192,6 @@ static int sama5d2_piobu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, piobu); piobu->chip.label = pdev->name; piobu->chip.parent = &pdev->dev; - piobu->chip.of_node = pdev->dev.of_node; piobu->chip.owner = THIS_MODULE, piobu->chip.get_direction = sama5d2_piobu_get_direction, piobu->chip.direction_input = sama5d2_piobu_direction_input, diff --git a/drivers/gpio/gpio-sprd.c b/drivers/gpio/gpio-sprd.c index 9dd9dabb579e..9bff63990eee 100644 --- a/drivers/gpio/gpio-sprd.c +++ b/drivers/gpio/gpio-sprd.c @@ -237,7 +237,6 @@ static int sprd_gpio_probe(struct platform_device *pdev) sprd_gpio->chip.ngpio = SPRD_GPIO_NR; sprd_gpio->chip.base = -1; sprd_gpio->chip.parent = &pdev->dev; - sprd_gpio->chip.of_node = pdev->dev.of_node; sprd_gpio->chip.request = sprd_gpio_request; sprd_gpio->chip.free = sprd_gpio_free; sprd_gpio->chip.get = sprd_gpio_get; diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index dd4d58b4ae49..0fa4f0a93378 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -477,7 +477,6 @@ static int stmpe_gpio_probe(struct platform_device *pdev) stmpe_gpio->chip = template_chip; stmpe_gpio->chip.ngpio = stmpe->num_gpios; stmpe_gpio->chip.parent = &pdev->dev; - stmpe_gpio->chip.of_node = np; stmpe_gpio->chip.base = -1; if (IS_ENABLED(CONFIG_DEBUG_FS)) diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index 8d158492488f..443fe975bf13 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -319,7 +319,6 @@ static int tc3589x_gpio_probe(struct platform_device *pdev) tc3589x_gpio->chip.ngpio = tc3589x->num_gpio; tc3589x_gpio->chip.parent = &pdev->dev; tc3589x_gpio->chip.base = -1; - tc3589x_gpio->chip.of_node = np; girq = &tc3589x_gpio->chip.irq; girq->chip = &tc3589x_gpio_irq_chip; diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 91c77fccc1e6..ce892c84ad27 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -749,7 +749,6 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->gpio.names = (const char * const *)names; #if defined(CONFIG_OF_GPIO) - gpio->gpio.of_node = pdev->dev.of_node; gpio->gpio.of_gpio_n_cells = 2; gpio->gpio.of_xlate = tegra186_gpio_of_xlate; #endif /* CONFIG_OF_GPIO */ diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c index 912382be48e1..e1d425a18854 100644 --- a/drivers/gpio/gpio-tps65218.c +++ b/drivers/gpio/gpio-tps65218.c @@ -196,9 +196,6 @@ static int tps65218_gpio_probe(struct platform_device *pdev) tps65218_gpio->tps65218 = tps65218; tps65218_gpio->gpio_chip = template_chip; tps65218_gpio->gpio_chip.parent = &pdev->dev; -#ifdef CONFIG_OF_GPIO - tps65218_gpio->gpio_chip.of_node = pdev->dev.of_node; -#endif return devm_gpiochip_add_data(&pdev->dev, &tps65218_gpio->gpio_chip, tps65218_gpio); diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index e0f2b67558e7..20780c35da1b 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -298,7 +298,6 @@ static int vf610_gpio_probe(struct platform_device *pdev) } gc = &port->gc; - gc->of_node = np; gc->parent = dev; gc->label = "vf610-gpio"; gc->ngpio = VF610_GPIO_PER_PORT; -- cgit v1.2.3 From 45a541a610af8156ee623b5906515796405ae9f5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 6 Dec 2021 15:18:51 +0200 Subject: gpio: Setup parent device and get rid of unnecessary of_node assignment Some of the drivers do not set parent device. This may lead to obstacles during debugging or understanding the device relations from the Linux point of view. Assign parent device for GPIO chips created by these drivers. While at it, let GPIO library to assign of_node from the parent device. Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-bcm-kona.c | 2 +- drivers/gpio/gpio-creg-snps.c | 2 +- drivers/gpio/gpio-lpc32xx.c | 2 +- drivers/gpio/gpio-pxa.c | 8 +++----- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index d329a143f5ec..e84474494429 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -606,7 +606,7 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev) kona_gpio->pdev = pdev; platform_set_drvdata(pdev, kona_gpio); - chip->of_node = dev->of_node; + chip->parent = dev; chip->ngpio = kona_gpio->num_bank * GPIO_PER_BANK; kona_gpio->irq_domain = irq_domain_add_linear(dev->of_node, diff --git a/drivers/gpio/gpio-creg-snps.c b/drivers/gpio/gpio-creg-snps.c index 1d0827e79703..789384c6e178 100644 --- a/drivers/gpio/gpio-creg-snps.c +++ b/drivers/gpio/gpio-creg-snps.c @@ -163,12 +163,12 @@ static int creg_gpio_probe(struct platform_device *pdev) spin_lock_init(&hcg->lock); + hcg->gc.parent = dev; hcg->gc.label = dev_name(dev); hcg->gc.base = -1; hcg->gc.ngpio = ngpios; hcg->gc.set = creg_gpio_set; hcg->gc.direction_output = creg_gpio_dir_out; - hcg->gc.of_node = dev->of_node; ret = devm_gpiochip_add_data(dev, &hcg->gc, hcg); if (ret) diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c index 4e626c4235c2..d2b65cfb336e 100644 --- a/drivers/gpio/gpio-lpc32xx.c +++ b/drivers/gpio/gpio-lpc32xx.c @@ -512,10 +512,10 @@ static int lpc32xx_gpio_probe(struct platform_device *pdev) return PTR_ERR(reg_base); for (i = 0; i < ARRAY_SIZE(lpc32xx_gpiochip); i++) { + lpc32xx_gpiochip[i].chip.parent = &pdev->dev; if (pdev->dev.of_node) { lpc32xx_gpiochip[i].chip.of_xlate = lpc32xx_of_xlate; lpc32xx_gpiochip[i].chip.of_gpio_n_cells = 3; - lpc32xx_gpiochip[i].chip.of_node = pdev->dev.of_node; lpc32xx_gpiochip[i].reg_base = reg_base; } devm_gpiochip_add_data(&pdev->dev, &lpc32xx_gpiochip[i].chip, diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 382468e294e1..c7fbfa3ae43b 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -343,8 +343,7 @@ static int pxa_gpio_of_xlate(struct gpio_chip *gc, } #endif -static int pxa_init_gpio_chip(struct pxa_gpio_chip *pchip, int ngpio, - struct device_node *np, void __iomem *regbase) +static int pxa_init_gpio_chip(struct pxa_gpio_chip *pchip, int ngpio, void __iomem *regbase) { int i, gpio, nbanks = DIV_ROUND_UP(ngpio, 32); struct pxa_gpio_bank *bank; @@ -354,6 +353,7 @@ static int pxa_init_gpio_chip(struct pxa_gpio_chip *pchip, int ngpio, if (!pchip->banks) return -ENOMEM; + pchip->chip.parent = pchip->dev; pchip->chip.label = "gpio-pxa"; pchip->chip.direction_input = pxa_gpio_direction_input; pchip->chip.direction_output = pxa_gpio_direction_output; @@ -365,7 +365,6 @@ static int pxa_init_gpio_chip(struct pxa_gpio_chip *pchip, int ngpio, pchip->chip.free = gpiochip_generic_free; #ifdef CONFIG_OF_GPIO - pchip->chip.of_node = np; pchip->chip.of_xlate = pxa_gpio_of_xlate; pchip->chip.of_gpio_n_cells = 2; #endif @@ -675,8 +674,7 @@ static int pxa_gpio_probe(struct platform_device *pdev) } /* Initialize GPIO chips */ - ret = pxa_init_gpio_chip(pchip, pxa_last_gpio + 1, pdev->dev.of_node, - gpio_reg_base); + ret = pxa_init_gpio_chip(pchip, pxa_last_gpio + 1, gpio_reg_base); if (ret) { clk_put(clk); return ret; -- cgit v1.2.3 From 6dbe6c07f94f349098b512d88a1e1c5e2312b13d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 6 Dec 2021 15:18:52 +0200 Subject: gpio: Propagate firmware node from a parent device When creating MFD platform devices the firmware node is left unset. This, in particular, prevents GPIO library to use it for different purposes. Propagate firmware node from the parent device and let GPIO library do the right thing. Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-arizona.c | 5 ++--- drivers/gpio/gpio-tps6586x.c | 5 ++--- drivers/gpio/gpio-tps65910.c | 6 +++--- drivers/gpio/gpio-twl6040.c | 5 ++--- drivers/gpio/gpio-wm831x.c | 5 ++--- 5 files changed, 11 insertions(+), 15 deletions(-) diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c index 2bc173c352ce..02f9ae19cd44 100644 --- a/drivers/gpio/gpio-arizona.c +++ b/drivers/gpio/gpio-arizona.c @@ -151,6 +151,8 @@ static int arizona_gpio_probe(struct platform_device *pdev) struct arizona_gpio *arizona_gpio; int ret; + device_set_node(&pdev->dev, dev_fwnode(pdev->dev.parent)); + arizona_gpio = devm_kzalloc(&pdev->dev, sizeof(*arizona_gpio), GFP_KERNEL); if (!arizona_gpio) @@ -159,9 +161,6 @@ static int arizona_gpio_probe(struct platform_device *pdev) arizona_gpio->arizona = arizona; arizona_gpio->gpio_chip = template_chip; arizona_gpio->gpio_chip.parent = &pdev->dev; -#ifdef CONFIG_OF_GPIO - arizona_gpio->gpio_chip.of_node = arizona->dev->of_node; -#endif switch (arizona->type) { case WM5102: diff --git a/drivers/gpio/gpio-tps6586x.c b/drivers/gpio/gpio-tps6586x.c index da0304b764a5..c5713524b581 100644 --- a/drivers/gpio/gpio-tps6586x.c +++ b/drivers/gpio/gpio-tps6586x.c @@ -77,6 +77,8 @@ static int tps6586x_gpio_probe(struct platform_device *pdev) struct tps6586x_platform_data *pdata; struct tps6586x_gpio *tps6586x_gpio; + device_set_node(&pdev->dev, dev_fwnode(pdev->dev.parent)); + pdata = dev_get_platdata(pdev->dev.parent); tps6586x_gpio = devm_kzalloc(&pdev->dev, sizeof(*tps6586x_gpio), GFP_KERNEL); @@ -97,9 +99,6 @@ static int tps6586x_gpio_probe(struct platform_device *pdev) tps6586x_gpio->gpio_chip.get = tps6586x_gpio_get; tps6586x_gpio->gpio_chip.to_irq = tps6586x_gpio_to_irq; -#ifdef CONFIG_OF_GPIO - tps6586x_gpio->gpio_chip.of_node = pdev->dev.parent->of_node; -#endif if (pdata && pdata->gpio_base) tps6586x_gpio->gpio_chip.base = pdata->gpio_base; else diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c index 7fa8c841081f..321e6945f0be 100644 --- a/drivers/gpio/gpio-tps65910.c +++ b/drivers/gpio/gpio-tps65910.c @@ -111,6 +111,8 @@ static int tps65910_gpio_probe(struct platform_device *pdev) int ret; int i; + device_set_node(&pdev->dev, dev_fwnode(pdev->dev.parent)); + tps65910_gpio = devm_kzalloc(&pdev->dev, sizeof(*tps65910_gpio), GFP_KERNEL); if (!tps65910_gpio) @@ -137,9 +139,7 @@ static int tps65910_gpio_probe(struct platform_device *pdev) tps65910_gpio->gpio_chip.set = tps65910_gpio_set; tps65910_gpio->gpio_chip.get = tps65910_gpio_get; tps65910_gpio->gpio_chip.parent = &pdev->dev; -#ifdef CONFIG_OF_GPIO - tps65910_gpio->gpio_chip.of_node = tps65910->dev->of_node; -#endif + if (pdata && pdata->gpio_base) tps65910_gpio->gpio_chip.base = pdata->gpio_base; else diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c index 648fb418d775..6c3fbf382dba 100644 --- a/drivers/gpio/gpio-twl6040.c +++ b/drivers/gpio/gpio-twl6040.c @@ -80,6 +80,8 @@ static int gpo_twl6040_probe(struct platform_device *pdev) struct twl6040 *twl6040 = dev_get_drvdata(twl6040_core_dev); int ret; + device_set_node(&pdev->dev, dev_fwnode(pdev->dev.parent)); + twl6040gpo_chip.base = -1; if (twl6040_get_revid(twl6040) < TWL6041_REV_ES2_0) @@ -88,9 +90,6 @@ static int gpo_twl6040_probe(struct platform_device *pdev) twl6040gpo_chip.ngpio = 1; /* twl6041 have 1 GPO */ twl6040gpo_chip.parent = &pdev->dev; -#ifdef CONFIG_OF_GPIO - twl6040gpo_chip.of_node = twl6040_core_dev->of_node; -#endif ret = devm_gpiochip_add_data(&pdev->dev, &twl6040gpo_chip, NULL); if (ret < 0) { diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c index 9cf1e5ebb352..7eaf8a28638c 100644 --- a/drivers/gpio/gpio-wm831x.c +++ b/drivers/gpio/gpio-wm831x.c @@ -262,6 +262,8 @@ static int wm831x_gpio_probe(struct platform_device *pdev) struct wm831x_pdata *pdata = &wm831x->pdata; struct wm831x_gpio *wm831x_gpio; + device_set_node(&pdev->dev, dev_fwnode(pdev->dev.parent)); + wm831x_gpio = devm_kzalloc(&pdev->dev, sizeof(*wm831x_gpio), GFP_KERNEL); if (wm831x_gpio == NULL) @@ -275,9 +277,6 @@ static int wm831x_gpio_probe(struct platform_device *pdev) wm831x_gpio->gpio_chip.base = pdata->gpio_base; else wm831x_gpio->gpio_chip.base = -1; -#ifdef CONFIG_OF_GPIO - wm831x_gpio->gpio_chip.of_node = wm831x->dev->of_node; -#endif return devm_gpiochip_add_data(&pdev->dev, &wm831x_gpio->gpio_chip, wm831x_gpio); } -- cgit v1.2.3 From c08995bff202f102c0eff7b799b6b91b748a02a2 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Sat, 18 Dec 2021 07:27:12 -0800 Subject: gpio: sim: fix uninitialized ret variable Building with clang returns this error: gpio-sim.c:889:7: error: variable 'ret' is uninitialized when used here ret should be the status of the call to gpio_sim_make_bank_swnode stored in bank->swnode. Fixes: 83960fcf4818 ("gpio: sim: new testing module") Signed-off-by: Tom Rix Reviewed-by: Nathan Chancellor Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sim.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index ef6145f51c8a..bef00dcc4dc8 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -886,7 +886,8 @@ static int gpio_sim_device_activate_unlocked(struct gpio_sim_device *dev) list_for_each_entry(bank, &dev->bank_list, siblings) { bank->swnode = gpio_sim_make_bank_swnode(bank, swnode); - if (ret) { + if (IS_ERR(bank->swnode)) { + ret = PTR_ERR(bank->swnode); gpio_sim_remove_swnode_recursive(swnode); return ret; } -- cgit v1.2.3 From 8e6458cd8ce827f1c2d0230f155a1579064f0727 Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Mon, 13 Dec 2021 18:40:32 +0900 Subject: dt-bindings: gpio: msc313: Add compatible for ssd20xd Add a compatible string for "ssd20xd" for the SigmaStar SSD201 and SSD202D chips. These chips are the same die with different memory bonded so they don't need their own strings. Signed-off-by: Daniel Palmer Acked-by: Rob Herring Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml b/Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml index fe1e1c63ffe3..18fe90387b87 100644 --- a/Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml @@ -14,7 +14,9 @@ properties: pattern: "^gpio@[0-9a-f]+$" compatible: - const: mstar,msc313-gpio + enum: + - mstar,msc313-gpio + - sstar,ssd20xd-gpio reg: maxItems: 1 -- cgit v1.2.3 From e82513696eadcf71048be174b4b71f9903ba4afd Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Mon, 13 Dec 2021 18:40:33 +0900 Subject: dt-bindings: gpio: msc313: Add offsets for ssd20xd Add the gpio offsets for the SSD201 and SSD202D chips. Signed-off-by: Daniel Palmer Acked-by: Rob Herring Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- include/dt-bindings/gpio/msc313-gpio.h | 71 ++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/include/dt-bindings/gpio/msc313-gpio.h b/include/dt-bindings/gpio/msc313-gpio.h index 2dd56683d3c1..5458c6580a02 100644 --- a/include/dt-bindings/gpio/msc313-gpio.h +++ b/include/dt-bindings/gpio/msc313-gpio.h @@ -50,4 +50,75 @@ #define MSC313_GPIO_SPI0_DI (MSC313_GPIO_SPI0 + 2) #define MSC313_GPIO_SPI0_DO (MSC313_GPIO_SPI0 + 3) +/* SSD20x */ +#define SSD20XD_GPIO_FUART 0 +#define SSD20XD_GPIO_FUART_RX (SSD20XD_GPIO_FUART + 0) +#define SSD20XD_GPIO_FUART_TX (SSD20XD_GPIO_FUART + 1) +#define SSD20XD_GPIO_FUART_CTS (SSD20XD_GPIO_FUART + 2) +#define SSD20XD_GPIO_FUART_RTS (SSD20XD_GPIO_FUART + 3) + +#define SSD20XD_GPIO_SD (SSD20XD_GPIO_FUART_RTS + 1) +#define SSD20XD_GPIO_SD_CLK (SSD20XD_GPIO_SD + 0) +#define SSD20XD_GPIO_SD_CMD (SSD20XD_GPIO_SD + 1) +#define SSD20XD_GPIO_SD_D0 (SSD20XD_GPIO_SD + 2) +#define SSD20XD_GPIO_SD_D1 (SSD20XD_GPIO_SD + 3) +#define SSD20XD_GPIO_SD_D2 (SSD20XD_GPIO_SD + 4) +#define SSD20XD_GPIO_SD_D3 (SSD20XD_GPIO_SD + 5) + +#define SSD20XD_GPIO_UART0 (SSD20XD_GPIO_SD_D3 + 1) +#define SSD20XD_GPIO_UART0_RX (SSD20XD_GPIO_UART0 + 0) +#define SSD20XD_GPIO_UART0_TX (SSD20XD_GPIO_UART0 + 1) + +#define SSD20XD_GPIO_UART1 (SSD20XD_GPIO_UART0_TX + 1) +#define SSD20XD_GPIO_UART1_RX (SSD20XD_GPIO_UART1 + 0) +#define SSD20XD_GPIO_UART1_TX (SSD20XD_GPIO_UART1 + 1) + +#define SSD20XD_GPIO_TTL (SSD20XD_GPIO_UART1_TX + 1) +#define SSD20XD_GPIO_TTL0 (SSD20XD_GPIO_TTL + 0) +#define SSD20XD_GPIO_TTL1 (SSD20XD_GPIO_TTL + 1) +#define SSD20XD_GPIO_TTL2 (SSD20XD_GPIO_TTL + 2) +#define SSD20XD_GPIO_TTL3 (SSD20XD_GPIO_TTL + 3) +#define SSD20XD_GPIO_TTL4 (SSD20XD_GPIO_TTL + 4) +#define SSD20XD_GPIO_TTL5 (SSD20XD_GPIO_TTL + 5) +#define SSD20XD_GPIO_TTL6 (SSD20XD_GPIO_TTL + 6) +#define SSD20XD_GPIO_TTL7 (SSD20XD_GPIO_TTL + 7) +#define SSD20XD_GPIO_TTL8 (SSD20XD_GPIO_TTL + 8) +#define SSD20XD_GPIO_TTL9 (SSD20XD_GPIO_TTL + 9) +#define SSD20XD_GPIO_TTL10 (SSD20XD_GPIO_TTL + 10) +#define SSD20XD_GPIO_TTL11 (SSD20XD_GPIO_TTL + 11) +#define SSD20XD_GPIO_TTL12 (SSD20XD_GPIO_TTL + 12) +#define SSD20XD_GPIO_TTL13 (SSD20XD_GPIO_TTL + 13) +#define SSD20XD_GPIO_TTL14 (SSD20XD_GPIO_TTL + 14) +#define SSD20XD_GPIO_TTL15 (SSD20XD_GPIO_TTL + 15) +#define SSD20XD_GPIO_TTL16 (SSD20XD_GPIO_TTL + 16) +#define SSD20XD_GPIO_TTL17 (SSD20XD_GPIO_TTL + 17) +#define SSD20XD_GPIO_TTL18 (SSD20XD_GPIO_TTL + 18) +#define SSD20XD_GPIO_TTL19 (SSD20XD_GPIO_TTL + 19) +#define SSD20XD_GPIO_TTL20 (SSD20XD_GPIO_TTL + 20) +#define SSD20XD_GPIO_TTL21 (SSD20XD_GPIO_TTL + 21) +#define SSD20XD_GPIO_TTL22 (SSD20XD_GPIO_TTL + 22) +#define SSD20XD_GPIO_TTL23 (SSD20XD_GPIO_TTL + 23) +#define SSD20XD_GPIO_TTL24 (SSD20XD_GPIO_TTL + 24) +#define SSD20XD_GPIO_TTL25 (SSD20XD_GPIO_TTL + 25) +#define SSD20XD_GPIO_TTL26 (SSD20XD_GPIO_TTL + 26) +#define SSD20XD_GPIO_TTL27 (SSD20XD_GPIO_TTL + 27) + +#define SSD20XD_GPIO_GPIO (SSD20XD_GPIO_TTL27 + 1) +#define SSD20XD_GPIO_GPIO0 (SSD20XD_GPIO_GPIO + 0) +#define SSD20XD_GPIO_GPIO1 (SSD20XD_GPIO_GPIO + 1) +#define SSD20XD_GPIO_GPIO2 (SSD20XD_GPIO_GPIO + 2) +#define SSD20XD_GPIO_GPIO3 (SSD20XD_GPIO_GPIO + 3) +#define SSD20XD_GPIO_GPIO4 (SSD20XD_GPIO_GPIO + 4) +#define SSD20XD_GPIO_GPIO5 (SSD20XD_GPIO_GPIO + 5) +#define SSD20XD_GPIO_GPIO6 (SSD20XD_GPIO_GPIO + 6) +#define SSD20XD_GPIO_GPIO7 (SSD20XD_GPIO_GPIO + 7) +#define SSD20XD_GPIO_GPIO10 (SSD20XD_GPIO_GPIO + 8) +#define SSD20XD_GPIO_GPIO11 (SSD20XD_GPIO_GPIO + 9) +#define SSD20XD_GPIO_GPIO12 (SSD20XD_GPIO_GPIO + 10) +#define SSD20XD_GPIO_GPIO13 (SSD20XD_GPIO_GPIO + 11) +#define SSD20XD_GPIO_GPIO14 (SSD20XD_GPIO_GPIO + 12) +#define SSD20XD_GPIO_GPIO85 (SSD20XD_GPIO_GPIO + 13) +#define SSD20XD_GPIO_GPIO86 (SSD20XD_GPIO_GPIO + 14) +#define SSD20XD_GPIO_GPIO90 (SSD20XD_GPIO_GPIO + 15) + #endif /* _DT_BINDINGS_MSC313_GPIO_H */ -- cgit v1.2.3 From bef4460b85501759d0441125c739dff8b088d11e Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Mon, 13 Dec 2021 18:40:34 +0900 Subject: gpio: msc313: Code clean ups - Remove the unneeded assignment of ret before returning it. - Remove an unneeded blank line Signed-off-by: Daniel Palmer Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-msc313.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-msc313.c b/drivers/gpio/gpio-msc313.c index da31a5ff7a2b..a894bafbd4c3 100644 --- a/drivers/gpio/gpio-msc313.c +++ b/drivers/gpio/gpio-msc313.c @@ -344,7 +344,6 @@ static int msc313_gpio_probe(struct platform_device *pdev) struct irq_domain *parent_domain; struct device_node *parent_node; struct device *dev = &pdev->dev; - int ret; match_data = of_device_get_match_data(dev); if (!match_data) @@ -399,8 +398,7 @@ static int msc313_gpio_probe(struct platform_device *pdev) gpioirqchip->handler = handle_bad_irq; gpioirqchip->default_type = IRQ_TYPE_NONE; - ret = devm_gpiochip_add_data(dev, gpiochip, gpio); - return ret; + return devm_gpiochip_add_data(dev, gpiochip, gpio); } static int msc313_gpio_remove(struct platform_device *pdev) @@ -456,5 +454,4 @@ static struct platform_driver msc313_gpio_driver = { .probe = msc313_gpio_probe, .remove = msc313_gpio_remove, }; - builtin_platform_driver(msc313_gpio_driver); -- cgit v1.2.3 From 572006bce34caee37af84a8946895a495c56624a Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Mon, 13 Dec 2021 18:40:35 +0900 Subject: gpio: msc313: Add support for SSD201 and SSD202D This adds GPIO support for the SSD201 and SSD202D chips. Signed-off-by: Daniel Palmer Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-msc313.c | 261 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) diff --git a/drivers/gpio/gpio-msc313.c b/drivers/gpio/gpio-msc313.c index a894bafbd4c3..b2c90bdd39d0 100644 --- a/drivers/gpio/gpio-msc313.c +++ b/drivers/gpio/gpio-msc313.c @@ -221,6 +221,263 @@ static const unsigned int msc313_offsets[] = { }; MSC313_GPIO_CHIPDATA(msc313); + +/* + * Unlike the msc313(e) the ssd20xd have a bunch of pins + * that are actually called gpio probably because they + * have no dedicated function. + */ +#define SSD20XD_PINNAME_GPIO0 "gpio0" +#define SSD20XD_PINNAME_GPIO1 "gpio1" +#define SSD20XD_PINNAME_GPIO2 "gpio2" +#define SSD20XD_PINNAME_GPIO3 "gpio3" +#define SSD20XD_PINNAME_GPIO4 "gpio4" +#define SSD20XD_PINNAME_GPIO5 "gpio5" +#define SSD20XD_PINNAME_GPIO6 "gpio6" +#define SSD20XD_PINNAME_GPIO7 "gpio7" +#define SSD20XD_PINNAME_GPIO10 "gpio10" +#define SSD20XD_PINNAME_GPIO11 "gpio11" +#define SSD20XD_PINNAME_GPIO12 "gpio12" +#define SSD20XD_PINNAME_GPIO13 "gpio13" +#define SSD20XD_PINNAME_GPIO14 "gpio14" +#define SSD20XD_PINNAME_GPIO85 "gpio85" +#define SSD20XD_PINNAME_GPIO86 "gpio86" +#define SSD20XD_PINNAME_GPIO90 "gpio90" + +#define SSD20XD_GPIO_NAMES SSD20XD_PINNAME_GPIO0, \ + SSD20XD_PINNAME_GPIO1, \ + SSD20XD_PINNAME_GPIO2, \ + SSD20XD_PINNAME_GPIO3, \ + SSD20XD_PINNAME_GPIO4, \ + SSD20XD_PINNAME_GPIO5, \ + SSD20XD_PINNAME_GPIO6, \ + SSD20XD_PINNAME_GPIO7, \ + SSD20XD_PINNAME_GPIO10, \ + SSD20XD_PINNAME_GPIO11, \ + SSD20XD_PINNAME_GPIO12, \ + SSD20XD_PINNAME_GPIO13, \ + SSD20XD_PINNAME_GPIO14, \ + SSD20XD_PINNAME_GPIO85, \ + SSD20XD_PINNAME_GPIO86, \ + SSD20XD_PINNAME_GPIO90 + +#define SSD20XD_GPIO_OFF_GPIO0 0x0 +#define SSD20XD_GPIO_OFF_GPIO1 0x4 +#define SSD20XD_GPIO_OFF_GPIO2 0x8 +#define SSD20XD_GPIO_OFF_GPIO3 0xc +#define SSD20XD_GPIO_OFF_GPIO4 0x10 +#define SSD20XD_GPIO_OFF_GPIO5 0x14 +#define SSD20XD_GPIO_OFF_GPIO6 0x18 +#define SSD20XD_GPIO_OFF_GPIO7 0x1c +#define SSD20XD_GPIO_OFF_GPIO10 0x28 +#define SSD20XD_GPIO_OFF_GPIO11 0x2c +#define SSD20XD_GPIO_OFF_GPIO12 0x30 +#define SSD20XD_GPIO_OFF_GPIO13 0x34 +#define SSD20XD_GPIO_OFF_GPIO14 0x38 +#define SSD20XD_GPIO_OFF_GPIO85 0x100 +#define SSD20XD_GPIO_OFF_GPIO86 0x104 +#define SSD20XD_GPIO_OFF_GPIO90 0x114 + +#define SSD20XD_GPIO_OFFSETS SSD20XD_GPIO_OFF_GPIO0, \ + SSD20XD_GPIO_OFF_GPIO1, \ + SSD20XD_GPIO_OFF_GPIO2, \ + SSD20XD_GPIO_OFF_GPIO3, \ + SSD20XD_GPIO_OFF_GPIO4, \ + SSD20XD_GPIO_OFF_GPIO5, \ + SSD20XD_GPIO_OFF_GPIO6, \ + SSD20XD_GPIO_OFF_GPIO7, \ + SSD20XD_GPIO_OFF_GPIO10, \ + SSD20XD_GPIO_OFF_GPIO11, \ + SSD20XD_GPIO_OFF_GPIO12, \ + SSD20XD_GPIO_OFF_GPIO13, \ + SSD20XD_GPIO_OFF_GPIO14, \ + SSD20XD_GPIO_OFF_GPIO85, \ + SSD20XD_GPIO_OFF_GPIO86, \ + SSD20XD_GPIO_OFF_GPIO90 + +/* "ttl" pins lcd interface pins */ +#define SSD20XD_PINNAME_TTL0 "ttl0" +#define SSD20XD_PINNAME_TTL1 "ttl1" +#define SSD20XD_PINNAME_TTL2 "ttl2" +#define SSD20XD_PINNAME_TTL3 "ttl3" +#define SSD20XD_PINNAME_TTL4 "ttl4" +#define SSD20XD_PINNAME_TTL5 "ttl5" +#define SSD20XD_PINNAME_TTL6 "ttl6" +#define SSD20XD_PINNAME_TTL7 "ttl7" +#define SSD20XD_PINNAME_TTL8 "ttl8" +#define SSD20XD_PINNAME_TTL9 "ttl9" +#define SSD20XD_PINNAME_TTL10 "ttl10" +#define SSD20XD_PINNAME_TTL11 "ttl11" +#define SSD20XD_PINNAME_TTL12 "ttl12" +#define SSD20XD_PINNAME_TTL13 "ttl13" +#define SSD20XD_PINNAME_TTL14 "ttl14" +#define SSD20XD_PINNAME_TTL15 "ttl15" +#define SSD20XD_PINNAME_TTL16 "ttl16" +#define SSD20XD_PINNAME_TTL17 "ttl17" +#define SSD20XD_PINNAME_TTL18 "ttl18" +#define SSD20XD_PINNAME_TTL19 "ttl19" +#define SSD20XD_PINNAME_TTL20 "ttl20" +#define SSD20XD_PINNAME_TTL21 "ttl21" +#define SSD20XD_PINNAME_TTL22 "ttl22" +#define SSD20XD_PINNAME_TTL23 "ttl23" +#define SSD20XD_PINNAME_TTL24 "ttl24" +#define SSD20XD_PINNAME_TTL25 "ttl25" +#define SSD20XD_PINNAME_TTL26 "ttl26" +#define SSD20XD_PINNAME_TTL27 "ttl27" + +#define SSD20XD_TTL_PINNAMES SSD20XD_PINNAME_TTL0, \ + SSD20XD_PINNAME_TTL1, \ + SSD20XD_PINNAME_TTL2, \ + SSD20XD_PINNAME_TTL3, \ + SSD20XD_PINNAME_TTL4, \ + SSD20XD_PINNAME_TTL5, \ + SSD20XD_PINNAME_TTL6, \ + SSD20XD_PINNAME_TTL7, \ + SSD20XD_PINNAME_TTL8, \ + SSD20XD_PINNAME_TTL9, \ + SSD20XD_PINNAME_TTL10, \ + SSD20XD_PINNAME_TTL11, \ + SSD20XD_PINNAME_TTL12, \ + SSD20XD_PINNAME_TTL13, \ + SSD20XD_PINNAME_TTL14, \ + SSD20XD_PINNAME_TTL15, \ + SSD20XD_PINNAME_TTL16, \ + SSD20XD_PINNAME_TTL17, \ + SSD20XD_PINNAME_TTL18, \ + SSD20XD_PINNAME_TTL19, \ + SSD20XD_PINNAME_TTL20, \ + SSD20XD_PINNAME_TTL21, \ + SSD20XD_PINNAME_TTL22, \ + SSD20XD_PINNAME_TTL23, \ + SSD20XD_PINNAME_TTL24, \ + SSD20XD_PINNAME_TTL25, \ + SSD20XD_PINNAME_TTL26, \ + SSD20XD_PINNAME_TTL27 + +#define SSD20XD_TTL_OFFSET_TTL0 0x80 +#define SSD20XD_TTL_OFFSET_TTL1 0x84 +#define SSD20XD_TTL_OFFSET_TTL2 0x88 +#define SSD20XD_TTL_OFFSET_TTL3 0x8c +#define SSD20XD_TTL_OFFSET_TTL4 0x90 +#define SSD20XD_TTL_OFFSET_TTL5 0x94 +#define SSD20XD_TTL_OFFSET_TTL6 0x98 +#define SSD20XD_TTL_OFFSET_TTL7 0x9c +#define SSD20XD_TTL_OFFSET_TTL8 0xa0 +#define SSD20XD_TTL_OFFSET_TTL9 0xa4 +#define SSD20XD_TTL_OFFSET_TTL10 0xa8 +#define SSD20XD_TTL_OFFSET_TTL11 0xac +#define SSD20XD_TTL_OFFSET_TTL12 0xb0 +#define SSD20XD_TTL_OFFSET_TTL13 0xb4 +#define SSD20XD_TTL_OFFSET_TTL14 0xb8 +#define SSD20XD_TTL_OFFSET_TTL15 0xbc +#define SSD20XD_TTL_OFFSET_TTL16 0xc0 +#define SSD20XD_TTL_OFFSET_TTL17 0xc4 +#define SSD20XD_TTL_OFFSET_TTL18 0xc8 +#define SSD20XD_TTL_OFFSET_TTL19 0xcc +#define SSD20XD_TTL_OFFSET_TTL20 0xd0 +#define SSD20XD_TTL_OFFSET_TTL21 0xd4 +#define SSD20XD_TTL_OFFSET_TTL22 0xd8 +#define SSD20XD_TTL_OFFSET_TTL23 0xdc +#define SSD20XD_TTL_OFFSET_TTL24 0xe0 +#define SSD20XD_TTL_OFFSET_TTL25 0xe4 +#define SSD20XD_TTL_OFFSET_TTL26 0xe8 +#define SSD20XD_TTL_OFFSET_TTL27 0xec + +#define SSD20XD_TTL_OFFSETS SSD20XD_TTL_OFFSET_TTL0, \ + SSD20XD_TTL_OFFSET_TTL1, \ + SSD20XD_TTL_OFFSET_TTL2, \ + SSD20XD_TTL_OFFSET_TTL3, \ + SSD20XD_TTL_OFFSET_TTL4, \ + SSD20XD_TTL_OFFSET_TTL5, \ + SSD20XD_TTL_OFFSET_TTL6, \ + SSD20XD_TTL_OFFSET_TTL7, \ + SSD20XD_TTL_OFFSET_TTL8, \ + SSD20XD_TTL_OFFSET_TTL9, \ + SSD20XD_TTL_OFFSET_TTL10, \ + SSD20XD_TTL_OFFSET_TTL11, \ + SSD20XD_TTL_OFFSET_TTL12, \ + SSD20XD_TTL_OFFSET_TTL13, \ + SSD20XD_TTL_OFFSET_TTL14, \ + SSD20XD_TTL_OFFSET_TTL15, \ + SSD20XD_TTL_OFFSET_TTL16, \ + SSD20XD_TTL_OFFSET_TTL17, \ + SSD20XD_TTL_OFFSET_TTL18, \ + SSD20XD_TTL_OFFSET_TTL19, \ + SSD20XD_TTL_OFFSET_TTL20, \ + SSD20XD_TTL_OFFSET_TTL21, \ + SSD20XD_TTL_OFFSET_TTL22, \ + SSD20XD_TTL_OFFSET_TTL23, \ + SSD20XD_TTL_OFFSET_TTL24, \ + SSD20XD_TTL_OFFSET_TTL25, \ + SSD20XD_TTL_OFFSET_TTL26, \ + SSD20XD_TTL_OFFSET_TTL27 + +/* On the ssd20xd the two normal uarts have dedicated pins */ +#define SSD20XD_PINNAME_UART0_RX "uart0_rx" +#define SSD20XD_PINNAME_UART0_TX "uart0_tx" + +#define SSD20XD_UART0_NAMES \ + SSD20XD_PINNAME_UART0_RX, \ + SSD20XD_PINNAME_UART0_TX + +#define SSD20XD_PINNAME_UART1_RX "uart1_rx" +#define SSD20XD_PINNAME_UART1_TX "uart1_tx" + +#define SSD20XD_UART1_NAMES \ + SSD20XD_PINNAME_UART1_RX, \ + SSD20XD_PINNAME_UART1_TX + +#define SSD20XD_OFF_UART0_RX 0x60 +#define SSD20XD_OFF_UART0_TX 0x64 + +#define SSD20XD_UART0_OFFSETS \ + SSD20XD_OFF_UART0_RX, \ + SSD20XD_OFF_UART0_TX + +#define SSD20XD_OFF_UART1_RX 0x68 +#define SSD20XD_OFF_UART1_TX 0x6c + +#define SSD20XD_UART1_OFFSETS \ + SSD20XD_OFF_UART1_RX, \ + SSD20XD_OFF_UART1_TX + +/* + * ssd20x has the same pin names but different ordering + * of the registers that control the gpio. + */ +#define SSD20XD_OFF_SD_D0 0x140 +#define SSD20XD_OFF_SD_D1 0x144 +#define SSD20XD_OFF_SD_D2 0x148 +#define SSD20XD_OFF_SD_D3 0x14c +#define SSD20XD_OFF_SD_CMD 0x150 +#define SSD20XD_OFF_SD_CLK 0x154 + +#define SSD20XD_SD_OFFSETS SSD20XD_OFF_SD_CLK, \ + SSD20XD_OFF_SD_CMD, \ + SSD20XD_OFF_SD_D0, \ + SSD20XD_OFF_SD_D1, \ + SSD20XD_OFF_SD_D2, \ + SSD20XD_OFF_SD_D3 + +static const char * const ssd20xd_names[] = { + FUART_NAMES, + SD_NAMES, + SSD20XD_UART0_NAMES, + SSD20XD_UART1_NAMES, + SSD20XD_TTL_PINNAMES, + SSD20XD_GPIO_NAMES, +}; + +static const unsigned int ssd20xd_offsets[] = { + FUART_OFFSETS, + SSD20XD_SD_OFFSETS, + SSD20XD_UART0_OFFSETS, + SSD20XD_UART1_OFFSETS, + SSD20XD_TTL_OFFSETS, + SSD20XD_GPIO_OFFSETS, +}; + +MSC313_GPIO_CHIPDATA(ssd20xd); #endif struct msc313_gpio { @@ -412,6 +669,10 @@ static const struct of_device_id msc313_gpio_of_match[] = { .compatible = "mstar,msc313-gpio", .data = &msc313_data, }, + { + .compatible = "sstar,ssd20xd-gpio", + .data = &ssd20xd_data, + }, #endif { } }; -- cgit v1.2.3 From a2d05fb73493ca89b36c7d2c3ffc76effbfcd763 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Tue, 21 Dec 2021 19:38:25 +0800 Subject: gpio: sim: add missing fwnode_handle_put() in gpio_sim_probe() Calling fwnode_handle_put() when break out of device_for_each_child_node(), or the device node reference will be leakd. Fixes: 83960fcf4818 ("gpio: sim: new testing module") Reported-by: Hulk Robot Signed-off-by: Yang Yingliang Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sim.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index bef00dcc4dc8..838bbfed11d3 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -444,8 +444,10 @@ static int gpio_sim_probe(struct platform_device *pdev) device_for_each_child_node(dev, swnode) { ret = gpio_sim_add_bank(swnode, dev); - if (ret) + if (ret) { + fwnode_handle_put(swnode); return ret; + } } return 0; -- cgit v1.2.3 From 9804456e60670cc73f13361a3e628ecdf1bf7ee1 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 20 Dec 2021 15:24:39 +0200 Subject: gpio: Remove unused local OF node pointers After the commit 448cf90513d9 ("gpio: Get rid of duplicate of_node assignment in the drivers") the OF node local pointers become unused. Remove them for good and make compiler happy about. Fixes: 448cf90513d9 ("gpio: Get rid of duplicate of_node assignment in the drivers") Reported-by: Stephen Rothwell Signed-off-by: Andy Shevchenko [Bart: removed unrelated change in gpio-brcmstb] Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-gw-pld.c | 1 - drivers/gpio/gpio-mt7621.c | 5 ++--- drivers/gpio/gpio-rda.c | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c index 77a3fbd46111..2109803ffb38 100644 --- a/drivers/gpio/gpio-gw-pld.c +++ b/drivers/gpio/gpio-gw-pld.c @@ -71,7 +71,6 @@ static int gw_pld_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; - struct device_node *np = dev->of_node; struct gw_pld *gw; int ret; diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index 52b49e7a0a80..ccaad1cb3c2e 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -205,8 +205,7 @@ mediatek_gpio_xlate(struct gpio_chip *chip, } static int -mediatek_gpio_bank_probe(struct device *dev, - struct device_node *node, int bank) +mediatek_gpio_bank_probe(struct device *dev, int bank) { struct mtk *mtk = dev_get_drvdata(dev); struct mtk_gc *rg; @@ -310,7 +309,7 @@ mediatek_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mtk); for (i = 0; i < MTK_BANK_CNT; i++) { - ret = mediatek_gpio_bank_probe(dev, np, i); + ret = mediatek_gpio_bank_probe(dev, i); if (ret) return ret; } diff --git a/drivers/gpio/gpio-rda.c b/drivers/gpio/gpio-rda.c index 0d03f525dcd3..62ba18b3a602 100644 --- a/drivers/gpio/gpio-rda.c +++ b/drivers/gpio/gpio-rda.c @@ -197,7 +197,6 @@ static void rda_gpio_irq_handler(struct irq_desc *desc) static int rda_gpio_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; struct gpio_irq_chip *girq; struct rda_gpio *rda_gpio; -- cgit v1.2.3 From e85dd53a38bc32f411f90bd095b738fee46785f8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 23 Dec 2021 15:15:38 +0200 Subject: gpio: brcmstb: Use local variable to access OF node We already have a local variable that contains a copy of OF node pointer. Use it instead of dereferencing it again from struct device. Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-brcmstb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index 176c264bb959..74ef89248867 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -704,7 +704,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) } gc->owner = THIS_MODULE; - gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pOF", dev->of_node); + gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pOF", np); if (!gc->label) { err = -ENOMEM; goto fail; -- cgit v1.2.3 From 9f01881beae9e40c87f3edfb81e87e973f85d272 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Thu, 16 Dec 2021 15:10:08 +0100 Subject: dt-bindings: gpio: Add Tegra241 support Add the port definitions for the main and AON GPIO controllers found on Tegra241 (Grace). Signed-off-by: Akhil R Signed-off-by: Thierry Reding Signed-off-by: Bartosz Golaszewski --- include/dt-bindings/gpio/tegra241-gpio.h | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 include/dt-bindings/gpio/tegra241-gpio.h diff --git a/include/dt-bindings/gpio/tegra241-gpio.h b/include/dt-bindings/gpio/tegra241-gpio.h new file mode 100644 index 000000000000..80cee3016be6 --- /dev/null +++ b/include/dt-bindings/gpio/tegra241-gpio.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. */ + +/* + * This header provides constants for the nvidia,tegra241-gpio DT binding. + * + * The first cell in Tegra's GPIO specifier is the GPIO ID. The macros below + * provide names for this. + * + * The second cell contains standard flag values specified in gpio.h. + */ + +#ifndef _DT_BINDINGS_GPIO_TEGRA241_GPIO_H +#define _DT_BINDINGS_GPIO_TEGRA241_GPIO_H + +#include + +/* GPIOs implemented by main GPIO controller */ +#define TEGRA241_MAIN_GPIO_PORT_A 0 +#define TEGRA241_MAIN_GPIO_PORT_B 1 +#define TEGRA241_MAIN_GPIO_PORT_C 2 +#define TEGRA241_MAIN_GPIO_PORT_D 3 +#define TEGRA241_MAIN_GPIO_PORT_E 4 +#define TEGRA241_MAIN_GPIO_PORT_F 5 +#define TEGRA241_MAIN_GPIO_PORT_G 6 +#define TEGRA241_MAIN_GPIO_PORT_H 7 +#define TEGRA241_MAIN_GPIO_PORT_I 8 +#define TEGRA241_MAIN_GPIO_PORT_J 9 +#define TEGRA241_MAIN_GPIO_PORT_K 10 +#define TEGRA241_MAIN_GPIO_PORT_L 11 + +#define TEGRA241_MAIN_GPIO(port, offset) \ + ((TEGRA241_MAIN_GPIO_PORT_##port * 8) + (offset)) + +/* GPIOs implemented by AON GPIO controller */ +#define TEGRA241_AON_GPIO_PORT_AA 0 +#define TEGRA241_AON_GPIO_PORT_BB 1 + +#define TEGRA241_AON_GPIO(port, offset) \ + ((TEGRA241_AON_GPIO_PORT_##port * 8) + (offset)) + +#endif -- cgit v1.2.3 From d1056b771ddbe119f733fd980c854d1bcf33d13e Mon Sep 17 00:00:00 2001 From: Akhil R Date: Thu, 16 Dec 2021 15:10:09 +0100 Subject: gpio: tegra186: Add support for Tegra241 Extend the existing Tegra186 GPIO controller driver with support for the GPIO controller found on Tegra241 (Grace). While the programming model remains the same, the number of pins has slightly changed. Signed-off-by: Akhil R Signed-off-by: Thierry Reding Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra186.c | 52 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index ce892c84ad27..34b36a8c035f 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -15,6 +15,7 @@ #include #include #include +#include /* security registers */ #define TEGRA186_GPIO_CTL_SCR 0x0c @@ -1041,6 +1042,55 @@ static const struct tegra_gpio_soc tegra234_aon_soc = { .num_irqs_per_bank = 8, }; +#define TEGRA241_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + [TEGRA241_MAIN_GPIO_PORT_##_name] = { \ + .name = #_name, \ + .bank = _bank, \ + .port = _port, \ + .pins = _pins, \ + } + +static const struct tegra_gpio_port tegra241_main_ports[] = { + TEGRA241_MAIN_GPIO_PORT(A, 0, 0, 8), + TEGRA241_MAIN_GPIO_PORT(B, 0, 1, 8), + TEGRA241_MAIN_GPIO_PORT(C, 0, 2, 2), + TEGRA241_MAIN_GPIO_PORT(D, 0, 3, 6), + TEGRA241_MAIN_GPIO_PORT(E, 0, 4, 8), + TEGRA241_MAIN_GPIO_PORT(F, 1, 0, 8), + TEGRA241_MAIN_GPIO_PORT(G, 1, 1, 8), + TEGRA241_MAIN_GPIO_PORT(H, 1, 2, 8), + TEGRA241_MAIN_GPIO_PORT(J, 1, 3, 8), + TEGRA241_MAIN_GPIO_PORT(K, 1, 4, 4), + TEGRA241_MAIN_GPIO_PORT(L, 1, 5, 6), +}; + +static const struct tegra_gpio_soc tegra241_main_soc = { + .num_ports = ARRAY_SIZE(tegra241_main_ports), + .ports = tegra241_main_ports, + .name = "tegra241-gpio", + .instance = 0, +}; + +#define TEGRA241_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + [TEGRA241_AON_GPIO_PORT_##_name] = { \ + .name = #_name, \ + .bank = _bank, \ + .port = _port, \ + .pins = _pins, \ + } + +static const struct tegra_gpio_port tegra241_aon_ports[] = { + TEGRA241_AON_GPIO_PORT(AA, 0, 0, 8), + TEGRA241_AON_GPIO_PORT(BB, 0, 0, 4), +}; + +static const struct tegra_gpio_soc tegra241_aon_soc = { + .num_ports = ARRAY_SIZE(tegra241_aon_ports), + .ports = tegra241_aon_ports, + .name = "tegra241-gpio-aon", + .instance = 1, +}; + static const struct of_device_id tegra186_gpio_of_match[] = { { .compatible = "nvidia,tegra186-gpio", @@ -1071,6 +1121,8 @@ static const struct acpi_device_id tegra186_gpio_acpi_match[] = { { .id = "NVDA0208", .driver_data = (kernel_ulong_t)&tegra186_aon_soc }, { .id = "NVDA0308", .driver_data = (kernel_ulong_t)&tegra194_main_soc }, { .id = "NVDA0408", .driver_data = (kernel_ulong_t)&tegra194_aon_soc }, + { .id = "NVDA0508", .driver_data = (kernel_ulong_t)&tegra241_main_soc }, + { .id = "NVDA0608", .driver_data = (kernel_ulong_t)&tegra241_aon_soc }, {} }; MODULE_DEVICE_TABLE(acpi, tegra186_gpio_acpi_match); -- cgit v1.2.3 From f21ecad451c9b33d56165da61b0f5a9a535bfd3c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 23 Dec 2021 14:16:06 +0200 Subject: gpio: regmap: Switch to use fwnode instead of of_node GPIO library now accepts fwnode as a firmware node, so switch the driver to use it. Signed-off-by: Andy Shevchenko Reviewed-by: Michael Walle Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-regmap.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index 69c219742083..6383136cbe59 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -244,16 +244,12 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config chip = &gpio->gpio_chip; chip->parent = config->parent; + chip->fwnode = config->fwnode; chip->base = -1; chip->ngpio = config->ngpio; chip->names = config->names; chip->label = config->label ?: dev_name(config->parent); -#if defined(CONFIG_OF_GPIO) - /* gpiolib will use of_node of the parent if chip->of_node is NULL */ - chip->of_node = to_of_node(config->fwnode); -#endif /* CONFIG_OF_GPIO */ - /* * If our regmap is fast_io we should probably set can_sleep to false. * Right now, the regmap doesn't save this property, nor is there any -- cgit v1.2.3 From 01d130a31adeeee09fb27294751e2adad2100c50 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 26 Dec 2021 15:19:38 +0100 Subject: gpio: max3191x: Use bitmap_free() to free bitmap kfree() and bitmap_free() are the same. But using the later is more consistent when freeing memory allocated with bitmap_alloc(). Signed-off-by: Christophe JAILLET Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-max3191x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c index 310d1a248cae..51cd6f98d1c7 100644 --- a/drivers/gpio/gpio-max3191x.c +++ b/drivers/gpio/gpio-max3191x.c @@ -326,7 +326,7 @@ static void gpiod_set_array_single_value_cansleep(unsigned int ndescs, bitmap_zero(values, ndescs); gpiod_set_array_value_cansleep(ndescs, desc, info, values); - kfree(values); + bitmap_free(values); } static struct gpio_descs *devm_gpiod_get_array_optional_count( -- cgit v1.2.3 From 0f7b1d1a5998c469347b0c13ef929cd8b5c71166 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 2 Jan 2022 16:56:53 +0100 Subject: dt-bindings: gpio: samsung: drop unused bindings The commit 6c56c6cd8031 ("gpio: samsung: Drop support for Exynos SoCs") removed support for the Samsung Exynos SoC in lrgacy GPIO driver, since it was moved to new pinctrl driver. Remove old, unused bindings. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/gpio-samsung.txt | 41 ---------------------- 1 file changed, 41 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-samsung.txt diff --git a/Documentation/devicetree/bindings/gpio/gpio-samsung.txt b/Documentation/devicetree/bindings/gpio/gpio-samsung.txt deleted file mode 100644 index 5375625e8cd2..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-samsung.txt +++ /dev/null @@ -1,41 +0,0 @@ -Samsung Exynos4 GPIO Controller - -Required properties: -- compatible: Compatible property value should be "samsung,exynos4-gpio>". - -- reg: Physical base address of the controller and length of memory mapped - region. - -- #gpio-cells: Should be 4. The syntax of the gpio specifier used by client nodes - should be the following with values derived from the SoC user manual. - <[phandle of the gpio controller node] - [pin number within the gpio controller] - [mux function] - [flags and pull up/down] - [drive strength]> - - Values for gpio specifier: - - Pin number: is a value between 0 to 7. - - Flags and Pull Up/Down: 0 - Pull Up/Down Disabled. - 1 - Pull Down Enabled. - 3 - Pull Up Enabled. - Bit 16 (0x00010000) - Input is active low. - - Drive Strength: 0 - 1x, - 1 - 3x, - 2 - 2x, - 3 - 4x - -- gpio-controller: Specifies that the node is a gpio controller. -- #address-cells: should be 1. -- #size-cells: should be 1. - -Example: - - gpa0: gpio-controller@11400000 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "samsung,exynos4-gpio"; - reg = <0x11400000 0x20>; - #gpio-cells = <4>; - gpio-controller; - }; -- cgit v1.2.3 From 4a08d63c243ae8525c40016ce57b50df515fafaf Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 23 Dec 2021 12:38:08 +0200 Subject: gpiolib: acpi: make fwnode take precedence in struct gpio_chip If the driver sets the fwnode in struct gpio_chip, let it take precedence over the parent's fwnode. This is a follow up to the commit 9126a738edc1 ("gpiolib: of: make fwnode take precedence in struct gpio_chip"). Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg 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 c7a0e56593e7..c0f6a25c3279 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -1339,6 +1339,9 @@ void acpi_gpio_dev_init(struct gpio_chip *gc, struct gpio_device *gdev) /* Set default fwnode to parent's one if present */ if (gc->parent) ACPI_COMPANION_SET(&gdev->dev, ACPI_COMPANION(gc->parent)); + + if (gc->fwnode) + device_set_node(&gdev->dev, gc->fwnode); } static int acpi_gpio_package_count(const union acpi_object *obj) -- cgit v1.2.3 From 80f60eba9ceea670092f599a6c90ca2cd7794004 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 23 Dec 2021 12:38:09 +0200 Subject: gpio: dwapb: Switch to use fwnode instead of of_node GPIO library now accepts fwnode as a firmware node, so switch the driver to use it and hence rectify the ACPI case which uses software nodes. Note, in this case it's rather logical fix that doesn't affect functionality, thus no backporting required. Signed-off-by: Andy Shevchenko Acked-by: Serge Semin Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-dwapb.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index ec0767d7800d..b0f3aca61974 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -515,9 +514,7 @@ static int dwapb_gpio_add_port(struct dwapb_gpio *gpio, return err; } -#ifdef CONFIG_OF_GPIO - port->gc.of_node = to_of_node(pp->fwnode); -#endif + port->gc.fwnode = pp->fwnode; port->gc.ngpio = pp->ngpio; port->gc.base = pp->gpio_base; -- cgit v1.2.3 From 6408693f95275ffec5327d97a4ed22def8b20692 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 22 Dec 2021 17:19:14 +0000 Subject: gpio: ts5500: Use platform_get_irq() to get the interrupt platform_get_resource(pdev, IORESOURCE_IRQ, ..) relies on static allocation of IRQ resources in DT core code, this causes an issue when using hierarchical interrupt domains using "interrupts" property in the node as this bypassed the hierarchical setup and messed up the irq chaining. In preparation for removal of static setup of IRQ resource from DT core code use platform_get_irq(). Signed-off-by: Lad Prabhakar Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ts5500.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c index c91890488402..b159e92a3612 100644 --- a/drivers/gpio/gpio-ts5500.c +++ b/drivers/gpio/gpio-ts5500.c @@ -317,22 +317,19 @@ static int ts5500_dio_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; const char *name = dev_name(dev); struct ts5500_priv *priv; - struct resource *res; unsigned long flags; int ret; - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(dev, "missing IRQ resource\n"); - return -EINVAL; - } + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; priv = devm_kzalloc(dev, sizeof(struct ts5500_priv), GFP_KERNEL); if (!priv) return -ENOMEM; platform_set_drvdata(pdev, priv); - priv->hwirq = res->start; + priv->hwirq = ret; spin_lock_init(&priv->lock); priv->gpio_chip.owner = THIS_MODULE; -- cgit v1.2.3 From f1ff272c60eded6ba1aa4c64a313910ce6e85bff Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 22 Dec 2021 17:19:15 +0000 Subject: gpio: rcar: Use platform_get_irq() to get the interrupt platform_get_resource(pdev, IORESOURCE_IRQ, ..) relies on static allocation of IRQ resources in DT core code, this causes an issue when using hierarchical interrupt domains using "interrupts" property in the node as this bypassed the hierarchical setup and messed up the irq chaining. In preparation for removal of static setup of IRQ resource from DT core code use platform_get_irq(). Signed-off-by: Lad Prabhakar Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rcar.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index f7b653314e7e..437baecc434e 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -477,7 +477,6 @@ static void gpio_rcar_enable_inputs(struct gpio_rcar_priv *p) static int gpio_rcar_probe(struct platform_device *pdev) { struct gpio_rcar_priv *p; - struct resource *irq; struct gpio_chip *gpio_chip; struct irq_chip *irq_chip; struct gpio_irq_chip *girq; @@ -502,12 +501,10 @@ static int gpio_rcar_probe(struct platform_device *pdev) pm_runtime_enable(dev); - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!irq) { - dev_err(dev, "missing IRQ\n"); - ret = -EINVAL; + ret = platform_get_irq(pdev, 0); + if (ret < 0) goto err0; - } + p->irq_parent = ret; p->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(p->base)) { @@ -555,8 +552,7 @@ static int gpio_rcar_probe(struct platform_device *pdev) goto err0; } - p->irq_parent = irq->start; - if (devm_request_irq(dev, irq->start, gpio_rcar_irq_handler, + if (devm_request_irq(dev, p->irq_parent, gpio_rcar_irq_handler, IRQF_SHARED, name, p)) { dev_err(dev, "failed to request IRQ\n"); ret = -ENOENT; -- cgit v1.2.3 From ffe31c9ed35d70069ee76d6b6d41ac86a17d7a07 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 4 Jan 2022 15:36:15 +0000 Subject: gpio: rcar: Propagate errors from devm_request_irq() The driver overrides the error code returned by devm_request_irq() to -ENOENT. Switch to propagating the error code upstream. Suggested-by: Andy Shevchenko Signed-off-by: Lad Prabhakar Reviewed-by: Andy Shevchenko Reviewed-by: Sergey Shtylyov Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rcar.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 437baecc434e..bd2e16d6e21c 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -552,10 +552,10 @@ static int gpio_rcar_probe(struct platform_device *pdev) goto err0; } - if (devm_request_irq(dev, p->irq_parent, gpio_rcar_irq_handler, - IRQF_SHARED, name, p)) { + ret = devm_request_irq(dev, p->irq_parent, gpio_rcar_irq_handler, + IRQF_SHARED, name, p); + if (ret) { dev_err(dev, "failed to request IRQ\n"); - ret = -ENOENT; goto err1; } -- cgit v1.2.3