From e2051394a50c7dd49e9b56bfaf049a03972362ae Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 5 Jul 2023 09:42:19 +0200 Subject: [PATCH 01/94] gpiolib: add missing include gpiolib.h uses notifiers but doesn't include . Signed-off-by: Bartosz Golaszewski Reviewed-by: Kent Gibson Reviewed-by: Andy Shevchenko --- drivers/gpio/gpiolib.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index cca81375f127..1409d52487c0 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include From 3283d820dce649ad6d5eaa531d76251b4e22ad40 Mon Sep 17 00:00:00 2001 From: Shenwei Wang Date: Thu, 6 Jul 2023 09:56:11 -0500 Subject: [PATCH 02/94] gpio: mxc: add runtime pm support Add runtime PM support and allow the GPIO controller to enter into runtime suspend automatically when not in use to save power. However, it will automatically resume and enable clocks when a GPIO or IRQ is requested. While putting the GPIO module itself into power saving mode may not have an obvious impact on current dissipation, the function is necessary because the GPIO module disables its clock when idle. This enables the system an opportunity to power off the parent subsystem, and this conserves more power. The typical i.MX8 SoC features up to 8 GPIO controllers, but most of the controllers often remain unused. Signed-off-by: Shenwei Wang Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mxc.c | 61 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 9d0cec4b82a3..a9fb6bd9aa6f 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -382,6 +383,23 @@ static int mxc_gpio_to_irq(struct gpio_chip *gc, unsigned offset) return irq_find_mapping(port->domain, offset); } +static int mxc_gpio_request(struct gpio_chip *chip, unsigned int offset) +{ + int ret; + + ret = gpiochip_generic_request(chip, offset); + if (ret) + return ret; + + return pm_runtime_resume_and_get(chip->parent); +} + +static void mxc_gpio_free(struct gpio_chip *chip, unsigned int offset) +{ + gpiochip_generic_free(chip, offset); + pm_runtime_put(chip->parent); +} + static int mxc_gpio_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -429,6 +447,10 @@ static int mxc_gpio_probe(struct platform_device *pdev) if (of_device_is_compatible(np, "fsl,imx7d-gpio")) port->power_off = true; + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + /* disable the interrupt and clear the status */ writel(0, port->base + GPIO_IMR); writel(~0, port->base + GPIO_ISR); @@ -459,8 +481,8 @@ static int mxc_gpio_probe(struct platform_device *pdev) if (err) goto out_bgio; - port->gc.request = gpiochip_generic_request; - port->gc.free = gpiochip_generic_free; + port->gc.request = mxc_gpio_request; + port->gc.free = mxc_gpio_free; port->gc.to_irq = mxc_gpio_to_irq; port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 : pdev->id * 32; @@ -482,6 +504,8 @@ static int mxc_gpio_probe(struct platform_device *pdev) goto out_bgio; } + irq_domain_set_pm_device(port->domain, &pdev->dev); + /* gpio-mxc can be a generic irq chip */ err = mxc_gpio_init_gc(port, irq_base); if (err < 0) @@ -490,12 +514,15 @@ static int mxc_gpio_probe(struct platform_device *pdev) list_add_tail(&port->node, &mxc_gpio_ports); platform_set_drvdata(pdev, port); + pm_runtime_put_autosuspend(&pdev->dev); return 0; out_irqdomain_remove: irq_domain_remove(port->domain); out_bgio: + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); clk_disable_unprepare(port->clk); dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err); return err; @@ -572,6 +599,30 @@ static bool mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable) return ret; } +static int mxc_gpio_runtime_suspend(struct device *dev) +{ + struct mxc_gpio_port *port = dev_get_drvdata(dev); + + mxc_gpio_save_regs(port); + clk_disable_unprepare(port->clk); + + return 0; +} + +static int mxc_gpio_runtime_resume(struct device *dev) +{ + struct mxc_gpio_port *port = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(port->clk); + if (ret) + return ret; + + mxc_gpio_restore_regs(port); + + return 0; +} + static int __maybe_unused mxc_gpio_noirq_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -597,14 +648,19 @@ static int __maybe_unused mxc_gpio_noirq_resume(struct device *dev) static const struct dev_pm_ops mxc_gpio_dev_pm_ops = { SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mxc_gpio_noirq_suspend, mxc_gpio_noirq_resume) + SET_RUNTIME_PM_OPS(mxc_gpio_runtime_suspend, mxc_gpio_runtime_resume, NULL) }; static int mxc_gpio_syscore_suspend(void) { struct mxc_gpio_port *port; + int ret; /* walk through all ports */ list_for_each_entry(port, &mxc_gpio_ports, node) { + ret = clk_prepare_enable(port->clk); + if (ret) + return ret; mxc_gpio_save_regs(port); clk_disable_unprepare(port->clk); } @@ -625,6 +681,7 @@ static void mxc_gpio_syscore_resume(void) return; } mxc_gpio_restore_regs(port); + clk_disable_unprepare(port->clk); } } From 59a4a3512c94c2e59757cd9119fcf4faf53fe198 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 3 Jul 2023 17:23:06 +0300 Subject: [PATCH 03/94] gpiolib: of: Don't use GPIO chip fwnode in of_gpiochip_*() GPIO library should rely only on the GPIO device's fwnode. Hence, replace GPIO chip fwnode usage by respective OF node of the GPIO device. JFYI, this is partial revert of 8afe82550240 ("gpiolib: of: Prepare of_gpiochip_add() / of_gpiochip_remove() for fwnode"). Signed-off-by: Andy Shevchenko Tested-by: Benjamin Tissoires Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-of.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 1436cdb5fa26..5fde5a3f5118 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -1078,16 +1078,16 @@ int of_gpiochip_add(struct gpio_chip *chip) if (ret) return ret; - fwnode_handle_get(chip->fwnode); + of_node_get(np); ret = of_gpiochip_scan_gpios(chip); if (ret) - fwnode_handle_put(chip->fwnode); + of_node_put(np); return ret; } void of_gpiochip_remove(struct gpio_chip *chip) { - fwnode_handle_put(chip->fwnode); + of_node_put(dev_of_node(&chip->gpiodev->dev)); } From 067dbc1ea5ce61ba174d0c5f2e375defe4d562e6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 3 Jul 2023 17:23:07 +0300 Subject: [PATCH 04/94] gpiolib: acpi: Don't use GPIO chip fwnode in acpi_gpiochip_find() GPIO library should rely only on the GPIO device's fwnode. Hence, replace GPIO chip fwnode usage by respective handle of the GPIO device. Signed-off-by: Andy Shevchenko Tested-by: Benjamin Tissoires Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 97496c0f9133..fbda452fb4d6 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -128,7 +128,7 @@ static bool acpi_gpio_deferred_req_irqs_done; static int acpi_gpiochip_find(struct gpio_chip *gc, void *data) { - return ACPI_HANDLE_FWNODE(gc->fwnode) == data; + return device_match_acpi_handle(&gc->gpiodev->dev, data); } /** From daecca4b8433d47f0db4933bcc0f283d530ba22e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 3 Jul 2023 17:23:08 +0300 Subject: [PATCH 05/94] gpiolib: Do not alter GPIO chip fwnode member Ideally we should not touch data in the given GPIO chip structure. Let's become closer to it by avoiding altering fwnode member. The GPIO library must use fwnode from GPIO device and the drivers might use one from GPIO chip in case they initialized it. Signed-off-by: Andy Shevchenko Tested-by: Benjamin Tissoires Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 251c875b5c34..826d927c2076 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -711,13 +711,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, int base = 0; int ret = 0; - /* - * If the calling driver did not initialize firmware node, do it here - * using the parent device, if any. - */ - if (!gc->fwnode && gc->parent) - gc->fwnode = dev_fwnode(gc->parent); - /* * First: allocate and populate the internal stat container, and * set up the struct device. @@ -732,7 +725,14 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, gc->gpiodev = gdev; gpiochip_set_data(gc, data); - device_set_node(&gdev->dev, gc->fwnode); + /* + * If the calling driver did not initialize firmware node, + * do it here using the parent device, if any. + */ + if (gc->fwnode) + device_set_node(&gdev->dev, gc->fwnode); + else if (gc->parent) + device_set_node(&gdev->dev, dev_fwnode(gc->parent)); gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL); if (gdev->id < 0) { From b683b487dce74bd709aa21aa1056bcc9462d1dfc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 21 Jun 2023 20:49:39 +0300 Subject: [PATCH 06/94] gpiolib: Make gpiochip_hierarchy_add_domain() return domain As a preparatory patch and for the sake of consistency, make gpiochip_hierarchy_add_domain() return IRQ domain. While at it, rename it to gpiochip_hierarchy_create_domain() to show the change. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 826d927c2076..1c0d0e33c6bd 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1292,12 +1292,14 @@ static void gpiochip_hierarchy_setup_domain_ops(struct irq_domain_ops *ops) ops->free = irq_domain_free_irqs_common; } -static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc) +static struct irq_domain *gpiochip_hierarchy_create_domain(struct gpio_chip *gc) { + struct irq_domain *domain; + if (!gc->irq.child_to_parent_hwirq || !gc->irq.fwnode) { chip_err(gc, "missing irqdomain vital data\n"); - return -EINVAL; + return ERR_PTR(-EINVAL); } if (!gc->irq.child_offset_to_irq) @@ -1309,7 +1311,7 @@ static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc) gpiochip_hierarchy_setup_domain_ops(&gc->irq.child_irq_domain_ops); - gc->irq.domain = irq_domain_create_hierarchy( + domain = irq_domain_create_hierarchy( gc->irq.parent_domain, 0, gc->ngpio, @@ -1317,12 +1319,12 @@ static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc) &gc->irq.child_irq_domain_ops, gc); - if (!gc->irq.domain) - return -ENOMEM; + if (!domain) + return ERR_PTR(-ENOMEM); gpiochip_set_hierarchical_irqchip(gc, gc->irq.chip); - return 0; + return domain; } static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) @@ -1366,9 +1368,9 @@ EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_fourcell); #else -static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc) +static struct irq_domain *gpiochip_hierarchy_create_domain(struct gpio_chip *gc) { - return -EINVAL; + return ERR_PTR(-EINVAL); } static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) @@ -1667,9 +1669,9 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc, /* If a parent irqdomain is provided, let's build a hierarchy */ if (gpiochip_hierarchy_is_hierarchical(gc)) { - int ret = gpiochip_hierarchy_add_domain(gc); - if (ret) - return ret; + gc->irq.domain = gpiochip_hierarchy_create_domain(gc); + if (IS_ERR(gc->irq.domain)) + return PTR_ERR(gc->irq.domain); } else { gc->irq.domain = irq_domain_create_simple(fwnode, gc->ngpio, From 1efc43de1781bbb8780ee6f6f2291073003f1ff1 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 21 Jun 2023 20:49:40 +0300 Subject: [PATCH 07/94] gpiolib: Factor out gpiochip_simple_create_domain() As a preparatory patch and for the sake of consistency, factor out gpiochip_simple_create_domain(). Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1c0d0e33c6bd..796d2802ebb9 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1447,6 +1447,19 @@ static const struct irq_domain_ops gpiochip_domain_ops = { .xlate = irq_domain_xlate_twocell, }; +static struct irq_domain *gpiochip_simple_create_domain(struct gpio_chip *gc) +{ + struct fwnode_handle *fwnode = dev_fwnode(&gc->gpiodev->dev); + struct irq_domain *domain; + + domain = irq_domain_create_simple(fwnode, gc->ngpio, gc->irq.first, + &gpiochip_domain_ops, gc); + if (!domain) + return ERR_PTR(-EINVAL); + + return domain; +} + /* * TODO: move these activate/deactivate in under the hierarchicial * irqchip implementation as static once SPMI and SSBI (all external @@ -1673,13 +1686,9 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc, if (IS_ERR(gc->irq.domain)) return PTR_ERR(gc->irq.domain); } else { - gc->irq.domain = irq_domain_create_simple(fwnode, - gc->ngpio, - gc->irq.first, - &gpiochip_domain_ops, - gc); - if (!gc->irq.domain) - return -EINVAL; + gc->irq.domain = gpiochip_simple_create_domain(gc); + if (IS_ERR(gc->irq.domain)) + return PTR_ERR(gc->irq.domain); } if (gc->irq.parent_handler) { From 39f3ad73d4465d0333444bd5adce052f8e1c2783 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 21 Jun 2023 20:49:41 +0300 Subject: [PATCH 08/94] gpiolib: Do not assign error pointer to the GPIO IRQ chip domain Check domain for being an error pointer before assigning it to the GPIO IRQ chip domain. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 796d2802ebb9..e4300b99bfa5 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1650,6 +1650,7 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc, { struct fwnode_handle *fwnode = dev_fwnode(&gc->gpiodev->dev); struct irq_chip *irqchip = gc->irq.chip; + struct irq_domain *domain; unsigned int type; unsigned int i; @@ -1682,14 +1683,13 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc, /* If a parent irqdomain is provided, let's build a hierarchy */ if (gpiochip_hierarchy_is_hierarchical(gc)) { - gc->irq.domain = gpiochip_hierarchy_create_domain(gc); - if (IS_ERR(gc->irq.domain)) - return PTR_ERR(gc->irq.domain); + domain = gpiochip_hierarchy_create_domain(gc); } else { - gc->irq.domain = gpiochip_simple_create_domain(gc); - if (IS_ERR(gc->irq.domain)) - return PTR_ERR(gc->irq.domain); + domain = gpiochip_simple_create_domain(gc); } + if (IS_ERR(domain)) + return PTR_ERR(domain); + gc->irq.domain = domain; if (gc->irq.parent_handler) { for (i = 0; i < gc->irq.num_parents; i++) { From 081bfdb303abaf5c818de5a444dd899c7de3fab0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 21 Jun 2023 20:49:42 +0300 Subject: [PATCH 09/94] gpiolib: Split out gpiochip_irqchip_add_allocated_domain() helper The gpiochip_irqchip_add_allocated_domain() can be used in another place in the code. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e4300b99bfa5..0179902d3b74 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1638,6 +1638,28 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gc) } } +static int gpiochip_irqchip_add_allocated_domain(struct gpio_chip *gc, + struct irq_domain *domain, + bool allocated_externally) +{ + if (!domain) + return -EINVAL; + + gc->to_irq = gpiochip_to_irq; + gc->irq.domain = domain; + gc->irq.domain_is_allocated_externally = allocated_externally; + + /* + * Using barrier() here to prevent compiler from reordering + * gc->irq.initialized before adding irqdomain. + */ + barrier(); + + gc->irq.initialized = true; + + return 0; +} + /** * gpiochip_add_irqchip() - adds an IRQ chip to a GPIO chip * @gc: the GPIO chip to add the IRQ chip to @@ -1791,22 +1813,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gc) int gpiochip_irqchip_add_domain(struct gpio_chip *gc, struct irq_domain *domain) { - if (!domain) - return -EINVAL; - - gc->to_irq = gpiochip_to_irq; - gc->irq.domain = domain; - gc->irq.domain_is_allocated_externally = true; - - /* - * Using barrier() here to prevent compiler from reordering - * gc->irq.initialized before adding irqdomain. - */ - barrier(); - - gc->irq.initialized = true; - - return 0; + return gpiochip_irqchip_add_allocated_domain(gc, domain, true); } EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_domain); From eec349dbe4fa2712d4933d674531778a93c50b28 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 21 Jun 2023 20:49:43 +0300 Subject: [PATCH 10/94] gpiolib: Replace open coded gpiochip_irqchip_add_allocated_domain() Replace open coded variant of gpiochip_irqchip_add_allocated_domain() in gpiochip_add_irqchip(). Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 0179902d3b74..62199ec96db2 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1645,6 +1645,9 @@ static int gpiochip_irqchip_add_allocated_domain(struct gpio_chip *gc, if (!domain) return -EINVAL; + if (gc->to_irq) + chip_warn(gc, "to_irq is redefined in %s and you shouldn't rely on it\n", __func__); + gc->to_irq = gpiochip_to_irq; gc->irq.domain = domain; gc->irq.domain_is_allocated_externally = allocated_externally; @@ -1675,6 +1678,7 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc, struct irq_domain *domain; unsigned int type; unsigned int i; + int ret; if (!irqchip) return 0; @@ -1695,10 +1699,6 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc, "%pfw: Ignoring %u default trigger\n", fwnode, type)) type = IRQ_TYPE_NONE; - if (gc->to_irq) - chip_warn(gc, "to_irq is redefined in %s and you shouldn't rely on it\n", __func__); - - gc->to_irq = gpiochip_to_irq; gc->irq.default_type = type; gc->irq.lock_key = lock_key; gc->irq.request_key = request_key; @@ -1711,7 +1711,6 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc, } if (IS_ERR(domain)) return PTR_ERR(domain); - gc->irq.domain = domain; if (gc->irq.parent_handler) { for (i = 0; i < gc->irq.num_parents; i++) { @@ -1735,14 +1734,9 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc, gpiochip_set_irq_hooks(gc); - /* - * Using barrier() here to prevent compiler from reordering - * gc->irq.initialized before initialization of above - * GPIO chip irq members. - */ - barrier(); - - gc->irq.initialized = true; + ret = gpiochip_irqchip_add_allocated_domain(gc, domain, false); + if (ret) + return ret; acpi_gpiochip_request_interrupts(gc); From d16e0b0e798700b036ad2701ce70525a6fbea8ea Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Mon, 26 Jun 2023 10:26:08 -0700 Subject: [PATCH 11/94] gpio: sifive: Support IRQ wake Each pin drives a separate interrupt in the parent IRQ domain, so there is no need to set IRQCHIP_MASK_ON_SUSPEND. Signed-off-by: Samuel Holland Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sifive.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c index 745e5f67254e..efab7b58f739 100644 --- a/drivers/gpio/gpio-sifive.c +++ b/drivers/gpio/gpio-sifive.c @@ -150,6 +150,7 @@ static const struct irq_chip sifive_gpio_irqchip = { .irq_disable = sifive_gpio_irq_disable, .irq_eoi = sifive_gpio_irq_eoi, .irq_set_affinity = sifive_gpio_irq_set_affinity, + .irq_set_wake = irq_chip_set_wake_parent, .flags = IRQCHIP_IMMUTABLE, GPIOCHIP_IRQ_RESOURCE_HELPERS, }; From 43818a4bf8ef8f38253bfb851ae64f3cdf45fdf4 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 12 Jul 2023 12:01:05 +0200 Subject: [PATCH 12/94] gpio: sim: fix a typo in comment It was supposed to say 'for' not 'fo'. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko --- drivers/gpio/gpio-sim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index 8b49b0abacd5..cfbdade841e8 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -488,7 +488,7 @@ struct gpio_sim_device { * 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 + * We use this lock for protecting all data structures owned by this * object too. */ struct mutex lock; From 0f93a345aa42c40e2145c3719d878e7daa4eb6ee Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 12 Jul 2023 11:59:55 +0200 Subject: [PATCH 13/94] gpiolib: order includes alphabetically in gpiolib.h After adding the missing notifier.h header, let's order all includes alphabetically. Suggested-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko --- drivers/gpio/gpiolib.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 1409d52487c0..7c562fbb9fb0 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -9,13 +9,13 @@ #ifndef GPIOLIB_H #define GPIOLIB_H -#include -#include /* for enum gpiod_flags */ -#include +#include #include +#include +#include /* for enum gpiod_flags */ +#include #include #include -#include #include #define GPIOCHIP_NAME "gpiochip" From 55b2395e4e92adc492c6b30ac109eb78250dcd9d Mon Sep 17 00:00:00 2001 From: Asmaa Mnebhi Date: Tue, 11 Jul 2023 11:12:48 -0400 Subject: [PATCH 14/94] gpio: mmio: handle "ngpios" properly in bgpio_init() bgpio_init() uses "sz" argument to populate ngpio, which is not accurate. Instead, read the "ngpios" property from the DT and if it doesn't exist, use the "sz" argument. With this change, drivers no longer need to overwrite the ngpio variable after calling bgpio_init(). If the "ngpios" property is specified, bgpio_bits is calculated as the round up value of ngpio. At the moment, the only requirement specified is that the round up value must be a multiple of 8 but it should also be a power of 2 because we provide accessors based on the bank size in bgpio_setup_accessors(). Signed-off-by: Asmaa Mnebhi Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mmio.c | 9 +++++- drivers/gpio/gpiolib.c | 68 ++++++++++++++++++++++------------------ drivers/gpio/gpiolib.h | 1 + 3 files changed, 46 insertions(+), 32 deletions(-) diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index d9dff3dc92ae..74fdf0d87b2c 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -60,6 +60,8 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` #include #include +#include "gpiolib.h" + static void bgpio_write8(void __iomem *reg, unsigned long data) { writeb(data, reg); @@ -614,10 +616,15 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev, gc->parent = dev; gc->label = dev_name(dev); gc->base = -1; - gc->ngpio = gc->bgpio_bits; gc->request = bgpio_request; gc->be_bits = !!(flags & BGPIOF_BIG_ENDIAN); + ret = gpiochip_get_ngpios(gc, dev); + if (ret) + gc->ngpio = gc->bgpio_bits; + else + gc->bgpio_bits = roundup_pow_of_two(round_up(gc->ngpio, 8)); + ret = bgpio_setup_io(gc, dat, set, clr, flags); if (ret) return ret; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 62199ec96db2..edab00c9cb3c 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -700,6 +700,40 @@ void *gpiochip_get_data(struct gpio_chip *gc) } EXPORT_SYMBOL_GPL(gpiochip_get_data); +int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev) +{ + u32 ngpios = gc->ngpio; + int ret; + + if (ngpios == 0) { + ret = device_property_read_u32(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) + return ret; + + gc->ngpio = ngpios; + } + + if (gc->ngpio == 0) { + chip_err(gc, "tried to insert a GPIO chip with zero lines\n"); + return -EINVAL; + } + + if (gc->ngpio > FASTPATH_NGPIO) + chip_warn(gc, "line cnt %u is greater than fast path cnt %u\n", + gc->ngpio, FASTPATH_NGPIO); + + return 0; +} +EXPORT_SYMBOL_GPL(gpiochip_get_ngpios); + int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, struct lock_class_key *lock_key, struct lock_class_key *request_key) @@ -707,7 +741,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, struct gpio_device *gdev; unsigned long flags; unsigned int i; - u32 ngpios = 0; int base = 0; int ret = 0; @@ -753,36 +786,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, else gdev->owner = THIS_MODULE; - /* - * Try the device properties if the driver didn't supply the number - * of GPIO lines. - */ - ngpios = gc->ngpio; - if (ngpios == 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_dev_name; - - gc->ngpio = ngpios; - } - - if (gc->ngpio == 0) { - chip_err(gc, "tried to insert a GPIO chip with zero lines\n"); - ret = -EINVAL; + ret = gpiochip_get_ngpios(gc, &gdev->dev); + if (ret) goto err_free_dev_name; - } - - if (gc->ngpio > FASTPATH_NGPIO) - chip_warn(gc, "line cnt %u is greater than fast path cnt %u\n", - gc->ngpio, FASTPATH_NGPIO); gdev->descs = kcalloc(gc->ngpio, sizeof(*gdev->descs), GFP_KERNEL); if (!gdev->descs) { @@ -947,7 +953,7 @@ err_print_message: /* failures here can mean systems won't boot... */ if (ret != -EPROBE_DEFER) { pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__, - base, base + (int)ngpios - 1, + base, base + (int)gc->ngpio - 1, gc->label ? : "generic", ret); } return ret; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 7c562fbb9fb0..08e8e8274883 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -218,6 +218,7 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce); int gpiod_hog(struct gpio_desc *desc, const char *name, unsigned long lflags, enum gpiod_flags dflags); +int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev); /* * Return the GPIO number of the passed descriptor relative to its chip From e91d0f05e66afba9a3804bccf3d0a4310bb30024 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 14 Jul 2023 11:44:58 -0600 Subject: [PATCH 15/94] gpio: Explicitly include correct DT includes The DT of_device.h and of_platform.h date back to the separate of_platform_bus_type before it as merged into the regular platform bus. As part of that merge prepping Arm DT support 13 years ago, they "temporarily" include each other. They also include platform_device.h and of.h. As a result, there's a pretty much random mix of those include files used throughout the tree. In order to detangle these headers and replace the implicit includes with struct declarations, users need to explicitly include the correct includes. Signed-off-by: Rob Herring Reviewed-by: Andy Shevchenko Acked-by: Romain Perier Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ath79.c | 3 ++- drivers/gpio/gpio-brcmstb.c | 4 ++-- drivers/gpio/gpio-creg-snps.c | 2 +- drivers/gpio/gpio-eic-sprd.c | 2 +- drivers/gpio/gpio-ge.c | 2 +- drivers/gpio/gpio-grgpio.c | 2 +- drivers/gpio/gpio-hlwd.c | 2 +- drivers/gpio/gpio-logicvc.c | 1 - drivers/gpio/gpio-mb86s7x.c | 2 +- drivers/gpio/gpio-mpc5200.c | 2 +- drivers/gpio/gpio-mpc8xxx.c | 4 +--- drivers/gpio/gpio-msc313.c | 1 - drivers/gpio/gpio-mxc.c | 1 - drivers/gpio/gpio-mxs.c | 1 - drivers/gpio/gpio-omap.c | 1 - drivers/gpio/gpio-palmas.c | 1 - drivers/gpio/gpio-pmic-eic-sprd.c | 2 +- drivers/gpio/gpio-pxa.c | 1 - drivers/gpio/gpio-rcar.c | 1 - drivers/gpio/gpio-rockchip.c | 2 +- drivers/gpio/gpio-sprd.c | 2 +- drivers/gpio/gpio-stp-xway.c | 3 ++- drivers/gpio/gpio-syscon.c | 1 - drivers/gpio/gpio-tegra.c | 2 +- drivers/gpio/gpio-tegra186.c | 2 +- drivers/gpio/gpio-tps6586x.c | 2 +- drivers/gpio/gpio-tps65910.c | 2 +- drivers/gpio/gpio-ts4800.c | 3 +-- drivers/gpio/gpio-ts4900.c | 2 +- drivers/gpio/gpio-uniphier.c | 1 - drivers/gpio/gpio-vf610.c | 1 - drivers/gpio/gpio-wcd934x.c | 3 ++- drivers/gpio/gpio-xilinx.c | 4 ++-- drivers/gpio/gpio-xra1403.c | 2 +- 34 files changed, 28 insertions(+), 39 deletions(-) diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c index aa0a954b8392..f0c0c0f77eb0 100644 --- a/drivers/gpio/gpio-ath79.c +++ b/drivers/gpio/gpio-ath79.c @@ -9,8 +9,9 @@ */ #include +#include #include -#include +#include #include #include #include diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index 6566517fe0d8..bccdbfd5ec80 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -3,12 +3,12 @@ #include #include -#include -#include +#include #include #include #include #include +#include enum gio_reg_index { GIO_REG_ODEN = 0, diff --git a/drivers/gpio/gpio-creg-snps.c b/drivers/gpio/gpio-creg-snps.c index 789384c6e178..4968232f70f2 100644 --- a/drivers/gpio/gpio-creg-snps.c +++ b/drivers/gpio/gpio-creg-snps.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #define MAX_GPIO 32 diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index 84352a6f4973..67b1e09e8985 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/gpio/gpio-ge.c b/drivers/gpio/gpio-ge.c index 7bd4c2a4cc11..4eecbc862abc 100644 --- a/drivers/gpio/gpio-ge.c +++ b/drivers/gpio/gpio-ge.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index bea0e32c195d..0163c95f6dd7 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -19,10 +19,10 @@ #include #include #include +#include #include #include #include -#include #include #include #include diff --git a/drivers/gpio/gpio-hlwd.c b/drivers/gpio/gpio-hlwd.c index c208ac1c54a6..1bcfc1835dae 100644 --- a/drivers/gpio/gpio-hlwd.c +++ b/drivers/gpio/gpio-hlwd.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/gpio/gpio-logicvc.c b/drivers/gpio/gpio-logicvc.c index 992cc958a43f..2b9876bc1383 100644 --- a/drivers/gpio/gpio-logicvc.c +++ b/drivers/gpio/gpio-logicvc.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/gpio/gpio-mb86s7x.c b/drivers/gpio/gpio-mb86s7x.c index 37c5363e391e..ca7eb5e8bfaa 100644 --- a/drivers/gpio/gpio-mb86s7x.c +++ b/drivers/gpio/gpio-mb86s7x.c @@ -10,11 +10,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include diff --git a/drivers/gpio/gpio-mpc5200.c b/drivers/gpio/gpio-mpc5200.c index 3b0bfff8c778..b49e3ca64015 100644 --- a/drivers/gpio/gpio-mpc5200.c +++ b/drivers/gpio/gpio-mpc5200.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 5979a36bf754..ebf2f511df59 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -9,12 +9,10 @@ #include #include #include +#include #include #include #include -#include -#include -#include #include #include #include diff --git a/drivers/gpio/gpio-msc313.c b/drivers/gpio/gpio-msc313.c index 036ad2324892..2f448eb23abb 100644 --- a/drivers/gpio/gpio-msc313.c +++ b/drivers/gpio/gpio-msc313.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index a9fb6bd9aa6f..443f3e4d9aff 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #define IMX_SCU_WAKEUP_OFF 0 diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index 390e619a2831..8e04c9c4b5a2 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index a08be5bf6808..2b78fde74e30 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c index bac10c2faf56..6140e87c6754 100644 --- a/drivers/gpio/gpio-palmas.c +++ b/drivers/gpio/gpio-palmas.c @@ -12,7 +12,6 @@ #include #include #include -#include #include struct palmas_gpio { diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c index c3e4d90f6b18..dabb0da3bd33 100644 --- a/drivers/gpio/gpio-pmic-eic-sprd.c +++ b/drivers/gpio/gpio-pmic-eic-sprd.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index a1630ed4b741..7e9f7a32d3ee 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 2525adb52f4f..86e69cde04da 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index e5de15a2ab9a..b35b9604413f 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -17,10 +17,10 @@ #include #include #include -#include #include #include #include +#include #include #include "../pinctrl/core.h" diff --git a/drivers/gpio/gpio-sprd.c b/drivers/gpio/gpio-sprd.c index 072b4e653216..c117c11bfb29 100644 --- a/drivers/gpio/gpio-sprd.c +++ b/drivers/gpio/gpio-sprd.c @@ -7,8 +7,8 @@ #include #include #include +#include #include -#include #include #include diff --git a/drivers/gpio/gpio-stp-xway.c b/drivers/gpio/gpio-stp-xway.c index 4750ea34204c..053d616f2e02 100644 --- a/drivers/gpio/gpio-stp-xway.c +++ b/drivers/gpio/gpio-stp-xway.c @@ -4,11 +4,12 @@ * Copyright (C) 2012 John Crispin */ +#include #include #include #include #include -#include +#include #include #include #include diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c index 6076937b18e7..41bf47c8c983 100644 --- a/drivers/gpio/gpio-syscon.c +++ b/drivers/gpio/gpio-syscon.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 5b265a6fd3c1..ea715582bcf3 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 80d08ddde40e..d87dd06db40d 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/gpio/gpio-tps6586x.c b/drivers/gpio/gpio-tps6586x.c index c5713524b581..d277aa951143 100644 --- a/drivers/gpio/gpio-tps6586x.c +++ b/drivers/gpio/gpio-tps6586x.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include /* GPIO control registers */ diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c index 321e6945f0be..187d21580573 100644 --- a/drivers/gpio/gpio-tps65910.c +++ b/drivers/gpio/gpio-tps65910.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include struct tps65910_gpio { struct gpio_chip gpio_chip; diff --git a/drivers/gpio/gpio-ts4800.c b/drivers/gpio/gpio-ts4800.c index 95d80ba14bee..4748e3d47106 100644 --- a/drivers/gpio/gpio-ts4800.c +++ b/drivers/gpio/gpio-ts4800.c @@ -7,8 +7,7 @@ #include #include -#include -#include +#include #include #define DEFAULT_PIN_NUMBER 16 diff --git a/drivers/gpio/gpio-ts4900.c b/drivers/gpio/gpio-ts4900.c index eba96319dac2..0f6397b77c9d 100644 --- a/drivers/gpio/gpio-ts4900.c +++ b/drivers/gpio/gpio-ts4900.c @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c index 19ce6675cbc0..9725b7aa18a7 100644 --- a/drivers/gpio/gpio-uniphier.c +++ b/drivers/gpio/gpio-uniphier.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index d3f3a69d4907..54e7c51f48c8 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include diff --git a/drivers/gpio/gpio-wcd934x.c b/drivers/gpio/gpio-wcd934x.c index 817750e4e033..2bba27b13947 100644 --- a/drivers/gpio/gpio-wcd934x.c +++ b/drivers/gpio/gpio-wcd934x.c @@ -1,11 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2019, Linaro Limited +#include #include #include +#include #include #include -#include #define WCD_PIN_MASK(p) BIT(p) #define WCD_REG_DIR_CTL_OFFSET 0x42 diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 1fa66f2a667f..a16945e8319e 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -15,8 +15,8 @@ #include #include #include -#include -#include +#include +#include #include #include diff --git a/drivers/gpio/gpio-xra1403.c b/drivers/gpio/gpio-xra1403.c index bbc06cdd9634..eed8a1684830 100644 --- a/drivers/gpio/gpio-xra1403.c +++ b/drivers/gpio/gpio-xra1403.c @@ -8,9 +8,9 @@ #include #include #include +#include #include #include -#include #include #include #include From c9ab610e5cd98f7fe0213b382870437368f11457 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 19 Jul 2023 01:30:53 +0300 Subject: [PATCH 16/94] gpio: bcm-kona: Make driver OF-independent There is nothing in the driver that requires OF APIs, make the driver OF independent. Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- drivers/gpio/gpio-bcm-kona.c | 21 ++++++++------------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index e382dfebad7c..1cf267a3ed8b 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -191,7 +191,7 @@ config GPIO_RASPBERRYPI_EXP config GPIO_BCM_KONA bool "Broadcom Kona GPIO" - depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST) + depends on ARCH_BCM_MOBILE || COMPILE_TEST help Turn on GPIO support for Broadcom "Kona" chips. diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index 70770429ba48..c977144eff10 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -8,12 +8,14 @@ #include #include -#include #include -#include #include +#include #include #include +#include +#include +#include #define BCM_GPIO_PASSWD 0x00a5a501 #define GPIO_PER_BANK 32 @@ -556,19 +558,12 @@ static void bcm_kona_gpio_reset(struct bcm_kona_gpio *kona_gpio) static int bcm_kona_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - const struct of_device_id *match; struct bcm_kona_gpio_bank *bank; struct bcm_kona_gpio *kona_gpio; struct gpio_chip *chip; int ret; int i; - match = of_match_device(bcm_kona_gpio_of_match, dev); - if (!match) { - dev_err(dev, "Failed to find gpio controller\n"); - return -ENODEV; - } - kona_gpio = devm_kzalloc(dev, sizeof(*kona_gpio), GFP_KERNEL); if (!kona_gpio) return -ENOMEM; @@ -601,10 +596,10 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev) chip->parent = dev; chip->ngpio = kona_gpio->num_bank * GPIO_PER_BANK; - kona_gpio->irq_domain = irq_domain_add_linear(dev->of_node, - chip->ngpio, - &bcm_kona_irq_ops, - kona_gpio); + kona_gpio->irq_domain = irq_domain_create_linear(dev_fwnode(dev), + chip->ngpio, + &bcm_kona_irq_ops, + kona_gpio); if (!kona_gpio->irq_domain) { dev_err(dev, "Couldn't allocate IRQ domain\n"); return -ENXIO; From ec72293cc2099859f79485861fbced41ae3fde60 Mon Sep 17 00:00:00 2001 From: Andrei Coardos Date: Wed, 19 Jul 2023 01:30:54 +0300 Subject: [PATCH 17/94] gpio: bcm-kona: remove unneeded platform_set_drvdata() call The platform_set_drvdata() call was never used, ever since the driver was originally added. It looks like this copy+paste left-over. Possibly the author copied from a driver that had this line, but also had a remove hook. Reviewed-by: Alexandru Ardelean Signed-off-by: Andrei Coardos Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-bcm-kona.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index c977144eff10..f67c0f76a196 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -592,7 +592,6 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev) return -ENOMEM; kona_gpio->pdev = pdev; - platform_set_drvdata(pdev, kona_gpio); chip->parent = dev; chip->ngpio = kona_gpio->num_bank * GPIO_PER_BANK; From 91093b57ee3b2080a1f30895f862690f8d794161 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 19 Jul 2023 01:30:55 +0300 Subject: [PATCH 18/94] gpio: bcm-kona: Drop unused pdev member in private data structure The pdev member is assigned and not used, drop it. Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-bcm-kona.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index f67c0f76a196..5321ef98f442 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -64,7 +64,6 @@ struct bcm_kona_gpio { struct gpio_chip gpio_chip; struct irq_domain *irq_domain; struct bcm_kona_gpio_bank *banks; - struct platform_device *pdev; }; struct bcm_kona_gpio_bank { @@ -591,7 +590,6 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev) if (!kona_gpio->banks) return -ENOMEM; - kona_gpio->pdev = pdev; chip->parent = dev; chip->ngpio = kona_gpio->num_bank * GPIO_PER_BANK; From 212892b89d50cf4fd750f999a0c1b167708d45cd Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 13 Jul 2023 15:20:46 -0500 Subject: [PATCH 19/94] gpio: pisosr: Use devm_gpiochip_add_data() to simplify remove path Use devm version of gpiochip add function to handle removal for us. While here update copyright and module author. Signed-off-by: Andrew Davis Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pisosr.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpio-pisosr.c b/drivers/gpio/gpio-pisosr.c index 67071bea08c2..e3013e778e15 100644 --- a/drivers/gpio/gpio-pisosr.c +++ b/drivers/gpio/gpio-pisosr.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ - * Andrew F. Davis + * Copyright (C) 2015-2023 Texas Instruments Incorporated - https://www.ti.com/ + * Andrew Davis */ #include @@ -116,6 +116,11 @@ static const struct gpio_chip template_chip = { .can_sleep = true, }; +static void pisosr_mutex_destroy(void *lock) +{ + mutex_destroy(lock); +} + static int pisosr_gpio_probe(struct spi_device *spi) { struct device *dev = &spi->dev; @@ -126,8 +131,6 @@ static int pisosr_gpio_probe(struct spi_device *spi) if (!gpio) return -ENOMEM; - spi_set_drvdata(spi, gpio); - gpio->chip = template_chip; gpio->chip.parent = dev; of_property_read_u16(dev->of_node, "ngpios", &gpio->chip.ngpio); @@ -145,8 +148,11 @@ static int pisosr_gpio_probe(struct spi_device *spi) "Unable to allocate load GPIO\n"); mutex_init(&gpio->lock); + ret = devm_add_action_or_reset(dev, pisosr_mutex_destroy, &gpio->lock); + if (ret) + return ret; - ret = gpiochip_add_data(&gpio->chip, gpio); + ret = devm_gpiochip_add_data(dev, &gpio->chip, gpio); if (ret < 0) { dev_err(dev, "Unable to register gpiochip\n"); return ret; @@ -155,15 +161,6 @@ static int pisosr_gpio_probe(struct spi_device *spi) return 0; } -static void pisosr_gpio_remove(struct spi_device *spi) -{ - struct pisosr_gpio *gpio = spi_get_drvdata(spi); - - gpiochip_remove(&gpio->chip); - - mutex_destroy(&gpio->lock); -} - static const struct spi_device_id pisosr_gpio_id_table[] = { { "pisosr-gpio", }, { /* sentinel */ } @@ -182,11 +179,10 @@ static struct spi_driver pisosr_gpio_driver = { .of_match_table = pisosr_gpio_of_match_table, }, .probe = pisosr_gpio_probe, - .remove = pisosr_gpio_remove, .id_table = pisosr_gpio_id_table, }; module_spi_driver(pisosr_gpio_driver); -MODULE_AUTHOR("Andrew F. Davis "); +MODULE_AUTHOR("Andrew Davis "); MODULE_DESCRIPTION("SPI Compatible PISO Shift Register GPIO Driver"); MODULE_LICENSE("GPL v2"); From 320630c45e6668c7b9b59489068cd1f85637a6ff Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jul 2023 09:45:53 +0200 Subject: [PATCH 20/94] dt-bindings: gpio: snps,dw-apb: allow gpio-line-names Allow the GPIO controller subnode to define GPIO names. Already used in at least on DTS: bitmain/bm1880-sophon-edge.dtb: gpio@50027000: gpio-controller@0: 'gpio-line-names' does not match any of the regexes: 'pinctrl-[0-9]+' Signed-off-by: Krzysztof Kozlowski Acked-by: Serge Semin Acked-by: Rob Herring Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml b/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml index b391cc1b4590..209f03bba0a7 100644 --- a/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml @@ -61,6 +61,10 @@ patternProperties: '#gpio-cells': const: 2 + gpio-line-names: + minItems: 1 + maxItems: 32 + ngpios: default: 32 minimum: 1 From 6a270bbd3a74c432b867214044377ccfcdb5ddc4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 11 Jul 2023 09:20:53 +0200 Subject: [PATCH 21/94] gpio: mxc: Improve PM configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If CONFIG_PM=n (e.g. m68k/allmodconfig): drivers/gpio/gpio-mxc.c:612:12: error: ‘mxc_gpio_runtime_resume’ defined but not used [-Werror=unused-function] 612 | static int mxc_gpio_runtime_resume(struct device *dev) | ^~~~~~~~~~~~~~~~~~~~~~~ drivers/gpio/gpio-mxc.c:602:12: error: ‘mxc_gpio_runtime_suspend’ defined but not used [-Werror=unused-function] 602 | static int mxc_gpio_runtime_suspend(struct device *dev) | ^~~~~~~~~~~~~~~~~~~~~~~~ Fix this by using the non-SET *_PM_OPS to configure the dev_pm_ops callbacks, and by wrapping the driver.pm initializer insider pm_ptr(). As NOIRQ_SYSTEM_SLEEP_PM_OPS() uses pm_sleep_ptr() internally, the __maybe_unused annotations for the noirq callbacks are no longer needed, and can be removed. Fixes: 3283d820dce649ad ("gpio: mxc: add runtime pm support") Reported-by: noreply@ellerman.id.au Signed-off-by: Geert Uytterhoeven Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mxc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 443f3e4d9aff..377d3ab8d626 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -622,7 +622,7 @@ static int mxc_gpio_runtime_resume(struct device *dev) return 0; } -static int __maybe_unused mxc_gpio_noirq_suspend(struct device *dev) +static int mxc_gpio_noirq_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct mxc_gpio_port *port = platform_get_drvdata(pdev); @@ -633,7 +633,7 @@ static int __maybe_unused mxc_gpio_noirq_suspend(struct device *dev) return 0; } -static int __maybe_unused mxc_gpio_noirq_resume(struct device *dev) +static int mxc_gpio_noirq_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct mxc_gpio_port *port = platform_get_drvdata(pdev); @@ -646,8 +646,8 @@ static int __maybe_unused mxc_gpio_noirq_resume(struct device *dev) } static const struct dev_pm_ops mxc_gpio_dev_pm_ops = { - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mxc_gpio_noirq_suspend, mxc_gpio_noirq_resume) - SET_RUNTIME_PM_OPS(mxc_gpio_runtime_suspend, mxc_gpio_runtime_resume, NULL) + NOIRQ_SYSTEM_SLEEP_PM_OPS(mxc_gpio_noirq_suspend, mxc_gpio_noirq_resume) + RUNTIME_PM_OPS(mxc_gpio_runtime_suspend, mxc_gpio_runtime_resume, NULL) }; static int mxc_gpio_syscore_suspend(void) @@ -694,7 +694,7 @@ static struct platform_driver mxc_gpio_driver = { .name = "gpio-mxc", .of_match_table = mxc_gpio_dt_ids, .suppress_bind_attrs = true, - .pm = &mxc_gpio_dev_pm_ops, + .pm = pm_ptr(&mxc_gpio_dev_pm_ops), }, .probe = mxc_gpio_probe, }; From 5d472a7ef0f35e39906ef0eafa0c76727949f56b Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Mon, 24 Jul 2023 17:40:39 -0700 Subject: [PATCH 22/94] gpio: sifive: Directly use the device's fwnode There is no need to convert dev->of_node back to a fwnode_handle. Signed-off-by: Samuel Holland Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sifive.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c index efab7b58f739..a1b5bae05a35 100644 --- a/drivers/gpio/gpio-sifive.c +++ b/drivers/gpio/gpio-sifive.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -255,7 +256,7 @@ static int sifive_gpio_probe(struct platform_device *pdev) chip->gc.owner = THIS_MODULE; girq = &chip->gc.irq; gpio_irq_chip_set_chip(girq, &sifive_gpio_irqchip); - girq->fwnode = of_node_to_fwnode(node); + girq->fwnode = dev_fwnode(dev); girq->parent_domain = parent; girq->child_to_parent_hwirq = sifive_gpio_child_to_parent_hwirq; girq->handler = handle_bad_irq; From 1cd9cee75f99cfe31a6e339af518679649812838 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Mon, 24 Jul 2023 17:40:40 -0700 Subject: [PATCH 23/94] gpio: sifive: Look up IRQs only once during probe of_irq_count(), or eqivalently platform_irq_count(), simply looks up successively-numbered IRQs until that fails. Since this driver needs to look up each IRQ anyway to get its virq number, use that existing loop to count the IRQs at the same time. The check against SIFIVE_GPIO_MAX functioned as a bounds check for chip->irq_number. That is now handled by the loop condition. Signed-off-by: Samuel Holland Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sifive.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c index a1b5bae05a35..e292c333f8dd 100644 --- a/drivers/gpio/gpio-sifive.c +++ b/drivers/gpio/gpio-sifive.c @@ -187,7 +187,7 @@ static int sifive_gpio_probe(struct platform_device *pdev) struct irq_domain *parent; struct gpio_irq_chip *girq; struct sifive_gpio *chip; - int ret, ngpio, i; + int ret, ngpio; chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -204,13 +204,6 @@ static int sifive_gpio_probe(struct platform_device *pdev) if (IS_ERR(chip->regs)) return PTR_ERR(chip->regs); - ngpio = of_irq_count(node); - if (ngpio > SIFIVE_GPIO_MAX) { - dev_err(dev, "Too many GPIO interrupts (max=%d)\n", - SIFIVE_GPIO_MAX); - return -ENXIO; - } - irq_parent = of_irq_find_parent(node); if (!irq_parent) { dev_err(dev, "no IRQ parent node\n"); @@ -223,11 +216,11 @@ static int sifive_gpio_probe(struct platform_device *pdev) return -ENODEV; } - for (i = 0; i < ngpio; i++) { - ret = platform_get_irq(pdev, i); + for (ngpio = 0; ngpio < SIFIVE_GPIO_MAX; ngpio++) { + ret = platform_get_irq_optional(pdev, ngpio); if (ret < 0) - return ret; - chip->irq_number[i] = ret; + break; + chip->irq_number[ngpio] = ret; } ret = bgpio_init(&chip->gc, dev, 4, From 3b5560c8f074aee8839b66093b4d565702a6921d Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Mon, 24 Jul 2023 17:40:41 -0700 Subject: [PATCH 24/94] gpio: sifive: Get the parent IRQ's domain from its irq_data Do not parse the devicetree again when the data is already available from the IRQ subsystem. This follows the example of the ThunderX and X-Gene GPIO drivers. The ngpio check is needed to avoid a possible out-of-bounds read. Signed-off-by: Samuel Holland Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sifive.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c index e292c333f8dd..8033bb8246c6 100644 --- a/drivers/gpio/gpio-sifive.c +++ b/drivers/gpio/gpio-sifive.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -182,8 +181,6 @@ static const struct regmap_config sifive_gpio_regmap_config = { static int sifive_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *node = pdev->dev.of_node; - struct device_node *irq_parent; struct irq_domain *parent; struct gpio_irq_chip *girq; struct sifive_gpio *chip; @@ -204,24 +201,22 @@ static int sifive_gpio_probe(struct platform_device *pdev) if (IS_ERR(chip->regs)) return PTR_ERR(chip->regs); - irq_parent = of_irq_find_parent(node); - if (!irq_parent) { - dev_err(dev, "no IRQ parent node\n"); - return -ENODEV; - } - parent = irq_find_host(irq_parent); - of_node_put(irq_parent); - if (!parent) { - dev_err(dev, "no IRQ parent domain\n"); - return -ENODEV; - } - for (ngpio = 0; ngpio < SIFIVE_GPIO_MAX; ngpio++) { ret = platform_get_irq_optional(pdev, ngpio); if (ret < 0) break; chip->irq_number[ngpio] = ret; } + if (!ngpio) { + dev_err(dev, "no IRQ found\n"); + return -ENODEV; + } + + /* + * The check above ensures at least one parent IRQ is valid. + * Assume all parent IRQs belong to the same domain. + */ + parent = irq_get_irq_data(chip->irq_number[0])->domain; ret = bgpio_init(&chip->gc, dev, 4, chip->base + SIFIVE_GPIO_INPUT_VAL, From 6b4c76ded3582651afca319f2ca58c22ec908529 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Mon, 24 Jul 2023 17:40:42 -0700 Subject: [PATCH 25/94] gpio: sifive: Allow building the driver as a module This can reduce the kernel image size in multiplatform configurations. Acked-by: Palmer Dabbelt Reviewed-by: Andy Shevchenko Signed-off-by: Samuel Holland Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- drivers/gpio/gpio-sifive.c | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 1cf267a3ed8b..398d9ef7e680 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -564,7 +564,7 @@ config GPIO_SAMA5D2_PIOBU maintain their value during backup/self-refresh. config GPIO_SIFIVE - bool "SiFive GPIO support" + tristate "SiFive GPIO support" depends on OF_GPIO select IRQ_DOMAIN_HIERARCHY select GPIO_GENERIC diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c index 8033bb8246c6..8decd9b5d229 100644 --- a/drivers/gpio/gpio-sifive.c +++ b/drivers/gpio/gpio-sifive.c @@ -267,4 +267,8 @@ static struct platform_driver sifive_gpio_driver = { .of_match_table = sifive_gpio_match, }, }; -builtin_platform_driver(sifive_gpio_driver) +module_platform_driver(sifive_gpio_driver) + +MODULE_AUTHOR("Yash Shah "); +MODULE_DESCRIPTION("SiFive GPIO driver"); +MODULE_LICENSE("GPL"); From 905c50cd15c1256a869b0a98da5025db0be89b9e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 24 Jul 2023 19:13:15 +0300 Subject: [PATCH 26/94] gpio: ge: Add missing header Add missing platform_device.h that used to be implied by of_device.h. While at it, sort headers alphabetically for better maintenance. Reported-by: Randy Dunlap Fixes: e91d0f05e66a ("gpio: Explicitly include correct DT includes") Closes: https://lore.kernel.org/r/65b4ac1a-1128-6e2a-92c0-9bbcca4b760a@infradead.org Signed-off-by: Andy Shevchenko Reviewed-by: Rob Herring Acked-by: Randy Dunlap Tested-by: Randy Dunlap # build-tested Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ge.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-ge.c b/drivers/gpio/gpio-ge.c index 4eecbc862abc..ecadb81ce7f8 100644 --- a/drivers/gpio/gpio-ge.c +++ b/drivers/gpio/gpio-ge.c @@ -17,13 +17,14 @@ * the I/O interrupt controllers mask to stop them propergating */ -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include #define GEF_GPIO_DIRECT 0x00 #define GEF_GPIO_IN 0x04 From 94484a7935161795b3d8ff0168684821083e8ddf Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 24 Jul 2023 19:13:16 +0300 Subject: [PATCH 27/94] gpio: ge: Fix English spelling and grammar Fix English spelling and grammar in the comments. While at it, fix the MODULE_AUTHOR() email address format. Reported-by: Randy Dunlap Closes: https://lore.kernel.org/r/65b4ac1a-1128-6e2a-92c0-9bbcca4b760a@infradead.org Signed-off-by: Andy Shevchenko Acked-by: Randy Dunlap Tested-by: Randy Dunlap # build-tested Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ge.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-ge.c b/drivers/gpio/gpio-ge.c index ecadb81ce7f8..f92b3c8a3a8a 100644 --- a/drivers/gpio/gpio-ge.c +++ b/drivers/gpio/gpio-ge.c @@ -10,11 +10,12 @@ * kind, whether express or implied. */ -/* TODO +/* + * TODO: * - * Configuration of output modes (totem-pole/open-drain) - * Interrupt configuration - interrupts are always generated the FPGA relies on - * the I/O interrupt controllers mask to stop them propergating + * Configuration of output modes (totem-pole/open-drain). + * Interrupt configuration - interrupts are always generated, the FPGA relies + * on the I/O interrupt controllers mask to stop them from being propagated. */ #include @@ -104,5 +105,5 @@ static struct platform_driver gef_gpio_driver = { module_platform_driver_probe(gef_gpio_driver, gef_gpio_probe); MODULE_DESCRIPTION("GE I/O FPGA GPIO driver"); -MODULE_AUTHOR("Martyn Welch "); MODULE_LICENSE("GPL"); From 0cf2b4f550fd6e2e7378a9f13a216a784ebcd7c0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 24 Jul 2023 19:13:17 +0300 Subject: [PATCH 28/94] gpio: ge: Make driver OF-independent There is nothing in the driver that requires OF APIs, make the driver OF independent. Signed-off-by: Andy Shevchenko Acked-by: Randy Dunlap Tested-by: Randy Dunlap # build-tested Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ge.c | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/drivers/gpio/gpio-ge.c b/drivers/gpio/gpio-ge.c index f92b3c8a3a8a..d019669048e6 100644 --- a/drivers/gpio/gpio-ge.c +++ b/drivers/gpio/gpio-ge.c @@ -21,10 +21,10 @@ #include #include #include +#include #include -#include -#include #include +#include #include #define GEF_GPIO_DIRECT 0x00 @@ -54,6 +54,7 @@ MODULE_DEVICE_TABLE(of, gef_gpio_ids); static int __init gef_gpio_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct gpio_chip *gc; void __iomem *regs; int ret; @@ -62,38 +63,30 @@ static int __init gef_gpio_probe(struct platform_device *pdev) if (!gc) return -ENOMEM; - regs = of_iomap(pdev->dev.of_node, 0); - if (!regs) - return -ENOMEM; + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); ret = bgpio_init(gc, &pdev->dev, 4, regs + GEF_GPIO_IN, regs + GEF_GPIO_OUT, NULL, NULL, regs + GEF_GPIO_DIRECT, BGPIOF_BIG_ENDIAN_BYTE_ORDER); - if (ret) { - dev_err(&pdev->dev, "bgpio_init failed\n"); - goto err0; - } + if (ret) + return dev_err_probe(dev, ret, "bgpio_init failed\n"); /* Setup pointers to chip functions */ - gc->label = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%pOF", pdev->dev.of_node); - if (!gc->label) { - ret = -ENOMEM; - goto err0; - } + gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev)); + if (!gc->label) + return -ENOMEM; gc->base = -1; - gc->ngpio = (u16)(uintptr_t)of_device_get_match_data(&pdev->dev); + gc->ngpio = (uintptr_t)device_get_match_data(dev); /* This function adds a memory mapped GPIO chip */ ret = devm_gpiochip_add_data(&pdev->dev, gc, NULL); if (ret) - goto err0; + return dev_err_probe(dev, ret, "GPIO chip registration failed\n"); return 0; -err0: - iounmap(regs); - pr_err("%pOF: GPIO chip registration failed\n", pdev->dev.of_node); - return ret; }; static struct platform_driver gef_gpio_driver = { From 806693e6cb8cb96349e482670cf0c82fe6359c4a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 24 Jul 2023 19:13:18 +0300 Subject: [PATCH 29/94] gpio: ge: Utilise temporary variable for struct device We have a temporary variable to keep pointer to struct device. Utilise it inside the ->probe() implementation. Signed-off-by: Andy Shevchenko Acked-by: Randy Dunlap Tested-by: Randy Dunlap # build-tested Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ge.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-ge.c b/drivers/gpio/gpio-ge.c index d019669048e6..268de5496fcb 100644 --- a/drivers/gpio/gpio-ge.c +++ b/drivers/gpio/gpio-ge.c @@ -59,7 +59,7 @@ static int __init gef_gpio_probe(struct platform_device *pdev) void __iomem *regs; int ret; - gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL); + gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); if (!gc) return -ENOMEM; @@ -67,9 +67,9 @@ static int __init gef_gpio_probe(struct platform_device *pdev) if (IS_ERR(regs)) return PTR_ERR(regs); - ret = bgpio_init(gc, &pdev->dev, 4, regs + GEF_GPIO_IN, - regs + GEF_GPIO_OUT, NULL, NULL, - regs + GEF_GPIO_DIRECT, BGPIOF_BIG_ENDIAN_BYTE_ORDER); + ret = bgpio_init(gc, dev, 4, regs + GEF_GPIO_IN, regs + GEF_GPIO_OUT, + NULL, NULL, regs + GEF_GPIO_DIRECT, + BGPIOF_BIG_ENDIAN_BYTE_ORDER); if (ret) return dev_err_probe(dev, ret, "bgpio_init failed\n"); @@ -82,7 +82,7 @@ static int __init gef_gpio_probe(struct platform_device *pdev) gc->ngpio = (uintptr_t)device_get_match_data(dev); /* This function adds a memory mapped GPIO chip */ - ret = devm_gpiochip_add_data(&pdev->dev, gc, NULL); + ret = devm_gpiochip_add_data(dev, gc, NULL); if (ret) return dev_err_probe(dev, ret, "GPIO chip registration failed\n"); From a13f5e77a510af20800829190a282dfe78c83787 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 24 Jul 2023 19:13:19 +0300 Subject: [PATCH 30/94] gpio: ge: Replace GPLv2 boilerplate with SPDX Replace the GPLv2 boilerplate text with a nice and short SPDX header. Signed-off-by: Andy Shevchenko Acked-by: Randy Dunlap Tested-by: Randy Dunlap # build-tested Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ge.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-ge.c b/drivers/gpio/gpio-ge.c index 268de5496fcb..5dc49648d8e3 100644 --- a/drivers/gpio/gpio-ge.c +++ b/drivers/gpio/gpio-ge.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Driver for GE FPGA based GPIO * * Author: Martyn Welch * * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ /* From 55b473538247550305abe0ae931d31249c6b1a47 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 24 Jul 2023 19:13:20 +0300 Subject: [PATCH 31/94] gpio: ge: Enable COMPILE_TEST for the driver Driver is so simple, yet there was a room for mistakes. Reduce their appearance in the future by enabling COMPILE_TEST option. Signed-off-by: Andy Shevchenko Acked-by: Randy Dunlap Tested-by: Randy Dunlap # build-tested Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 398d9ef7e680..8b41893bc5cd 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -283,7 +283,7 @@ config GPIO_EXAR config GPIO_GE_FPGA bool "GE FPGA based GPIO" - depends on GE_FPGA + depends on GE_FPGA || COMPILE_TEST select GPIO_GENERIC help Support for common GPIO functionality provided on some GE Single Board From 27d5a3cc2137f9e7bf03e4420ef01653d913d3b1 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Tue, 25 Jul 2023 18:23:30 +0800 Subject: [PATCH 32/94] dt-bindings: gpio: fsl-imx-gpio: support i.MX8QM/DXL Add i.MX8QM/DXL gpio compatible which is compatible with i.MX35. Signed-off-by: Peng Fan Acked-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml b/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml index ae18603697d7..d0ca2af89f1e 100644 --- a/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml @@ -32,10 +32,12 @@ properties: - fsl,imx6sx-gpio - fsl,imx6ul-gpio - fsl,imx7d-gpio + - fsl,imx8dxl-gpio - fsl,imx8mm-gpio - fsl,imx8mn-gpio - fsl,imx8mp-gpio - fsl,imx8mq-gpio + - fsl,imx8qm-gpio - fsl,imx8qxp-gpio - fsl,imxrt1050-gpio - fsl,imxrt1170-gpio From 92f7a35836c2e13ae5f0dc8c7f889e92f66a9d19 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 20 Jul 2023 14:49:43 -0400 Subject: [PATCH 33/94] gpio: 104-dio-48e: Add Counter/Timer support The 104-DIO-48E features an 8254 Counter/Timer chip providing three counter/timers which can be used for frequency measurement, frequency output, pulse width modulation, pulse width measurement, event count, etc. The counter/timers use the same addresses as PPI 0 (addresses 0x0 to 0x3), so a raw_spinlock_t is used to synchronize operations between the two regmap mappings to prevent clobbering. Reviewed-by: Linus Walleij Signed-off-by: William Breathitt Gray Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-104-dio-48e.c | 127 ++++++++++++++++++++++++++++---- 2 files changed, 112 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8b41893bc5cd..61a8d07e5a1c 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -858,6 +858,7 @@ config GPIO_104_DIO_48E select REGMAP_IRQ select GPIOLIB_IRQCHIP select GPIO_I8255 + select I8254 help Enables GPIO support for the ACCES 104-DIO-48E series (104-DIO-48E, 104-DIO-24E). The base port addresses for the devices may be diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c index 8ff5f4ff5958..4df9becaf349 100644 --- a/drivers/gpio/gpio-104-dio-48e.c +++ b/drivers/gpio/gpio-104-dio-48e.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #include "gpio-i8255.h" @@ -37,6 +39,8 @@ MODULE_PARM_DESC(irq, "ACCES 104-DIO-48E interrupt line numbers"); #define DIO48E_ENABLE_INTERRUPT 0xB #define DIO48E_DISABLE_INTERRUPT DIO48E_ENABLE_INTERRUPT +#define DIO48E_ENABLE_COUNTER_TIMER_ADDRESSING 0xD +#define DIO48E_DISABLE_COUNTER_TIMER_ADDRESSING DIO48E_ENABLE_COUNTER_TIMER_ADDRESSING #define DIO48E_CLEAR_INTERRUPT 0xF #define DIO48E_NUM_PPI 2 @@ -75,18 +79,20 @@ static const struct regmap_access_table dio48e_precious_table = { .yes_ranges = dio48e_precious_ranges, .n_yes_ranges = ARRAY_SIZE(dio48e_precious_ranges), }; -static const struct regmap_config dio48e_regmap_config = { - .reg_bits = 8, - .reg_stride = 1, - .val_bits = 8, - .io_port = true, - .max_register = 0xF, - .wr_table = &dio48e_wr_table, - .rd_table = &dio48e_rd_table, - .volatile_table = &dio48e_volatile_table, - .precious_table = &dio48e_precious_table, - .cache_type = REGCACHE_FLAT, - .use_raw_spinlock = true, + +static const struct regmap_range pit_wr_ranges[] = { + regmap_reg_range(0x0, 0x3), +}; +static const struct regmap_range pit_rd_ranges[] = { + regmap_reg_range(0x0, 0x2), +}; +static const struct regmap_access_table pit_wr_table = { + .yes_ranges = pit_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(pit_wr_ranges), +}; +static const struct regmap_access_table pit_rd_table = { + .yes_ranges = pit_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(pit_rd_ranges), }; /* only bit 3 on each respective Port C supports interrupts */ @@ -102,14 +108,56 @@ static const struct regmap_irq dio48e_regmap_irqs[] = { /** * struct dio48e_gpio - GPIO device private data structure + * @lock: synchronization lock to prevent I/O race conditions * @map: Regmap for the device + * @regs: virtual mapping for device registers + * @flags: IRQ flags saved during locking * @irq_mask: Current IRQ mask state on the device */ struct dio48e_gpio { + raw_spinlock_t lock; struct regmap *map; + void __iomem *regs; + unsigned long flags; unsigned int irq_mask; }; +static void dio48e_regmap_lock(void *lock_arg) __acquires(&dio48egpio->lock) +{ + struct dio48e_gpio *const dio48egpio = lock_arg; + unsigned long flags; + + raw_spin_lock_irqsave(&dio48egpio->lock, flags); + dio48egpio->flags = flags; +} + +static void dio48e_regmap_unlock(void *lock_arg) __releases(&dio48egpio->lock) +{ + struct dio48e_gpio *const dio48egpio = lock_arg; + + raw_spin_unlock_irqrestore(&dio48egpio->lock, dio48egpio->flags); +} + +static void pit_regmap_lock(void *lock_arg) __acquires(&dio48egpio->lock) +{ + struct dio48e_gpio *const dio48egpio = lock_arg; + unsigned long flags; + + raw_spin_lock_irqsave(&dio48egpio->lock, flags); + dio48egpio->flags = flags; + + iowrite8(0x00, dio48egpio->regs + DIO48E_ENABLE_COUNTER_TIMER_ADDRESSING); +} + +static void pit_regmap_unlock(void *lock_arg) __releases(&dio48egpio->lock) +{ + struct dio48e_gpio *const dio48egpio = lock_arg; + + ioread8(dio48egpio->regs + DIO48E_DISABLE_COUNTER_TIMER_ADDRESSING); + + raw_spin_unlock_irqrestore(&dio48egpio->lock, dio48egpio->flags); +} + static int dio48e_handle_mask_sync(const int index, const unsigned int mask_buf_def, const unsigned int mask_buf, @@ -176,6 +224,9 @@ static int dio48e_probe(struct device *dev, unsigned int id) struct i8255_regmap_config config = {}; void __iomem *regs; struct regmap *map; + struct regmap_config dio48e_regmap_config; + struct regmap_config pit_regmap_config; + struct i8254_regmap_config pit_config; int err; struct regmap_irq_chip *chip; struct dio48e_gpio *dio48egpio; @@ -187,21 +238,58 @@ static int dio48e_probe(struct device *dev, unsigned int id) return -EBUSY; } + dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL); + if (!dio48egpio) + return -ENOMEM; + regs = devm_ioport_map(dev, base[id], DIO48E_EXTENT); if (!regs) return -ENOMEM; + dio48egpio->regs = regs; + + raw_spin_lock_init(&dio48egpio->lock); + + dio48e_regmap_config = (struct regmap_config) { + .reg_bits = 8, + .reg_stride = 1, + .val_bits = 8, + .lock = dio48e_regmap_lock, + .unlock = dio48e_regmap_unlock, + .lock_arg = dio48egpio, + .io_port = true, + .wr_table = &dio48e_wr_table, + .rd_table = &dio48e_rd_table, + .volatile_table = &dio48e_volatile_table, + .precious_table = &dio48e_precious_table, + .cache_type = REGCACHE_FLAT, + }; + map = devm_regmap_init_mmio(dev, regs, &dio48e_regmap_config); if (IS_ERR(map)) return dev_err_probe(dev, PTR_ERR(map), "Unable to initialize register map\n"); - dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL); - if (!dio48egpio) - return -ENOMEM; - dio48egpio->map = map; + pit_regmap_config = (struct regmap_config) { + .name = "i8254", + .reg_bits = 8, + .reg_stride = 1, + .val_bits = 8, + .lock = pit_regmap_lock, + .unlock = pit_regmap_unlock, + .lock_arg = dio48egpio, + .io_port = true, + .wr_table = &pit_wr_table, + .rd_table = &pit_rd_table, + }; + + pit_config.map = devm_regmap_init_mmio(dev, regs, &pit_regmap_config); + if (IS_ERR(pit_config.map)) + return dev_err_probe(dev, PTR_ERR(pit_config.map), + "Unable to initialize i8254 register map\n"); + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; @@ -225,6 +313,12 @@ static int dio48e_probe(struct device *dev, unsigned int id) if (err) return dev_err_probe(dev, err, "IRQ registration failed\n"); + pit_config.parent = dev; + + err = devm_i8254_regmap_register(dev, &pit_config); + if (err) + return err; + config.parent = dev; config.map = map; config.num_ppi = DIO48E_NUM_PPI; @@ -245,3 +339,4 @@ module_isa_driver_with_irq(dio48e_driver, num_dio48e, num_irq); MODULE_AUTHOR("William Breathitt Gray "); MODULE_DESCRIPTION("ACCES 104-DIO-48E GPIO driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(I8254); From 291bc793a0083ce69acc7ceb77c9ec7af4401093 Mon Sep 17 00:00:00 2001 From: Ruan Jinjie Date: Thu, 27 Jul 2023 20:40:43 +0800 Subject: [PATCH 34/94] gpio: omap: Remove redundant dev_err_probe() and zero value handle code There is no need to call the dev_err_probe() function directly to print a custom message when handling an error from platform_get_irq() function as it is going to display an appropriate error message in case of a failure. And the code to handle bank->irq = 0 is redundant because platform_get_irq() do not return 0. Signed-off-by: Ruan Jinjie Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-omap.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 2b78fde74e30..a927680c66f8 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1412,11 +1412,8 @@ static int omap_gpio_probe(struct platform_device *pdev) bank->dev = dev; bank->irq = platform_get_irq(pdev, 0); - if (bank->irq <= 0) { - if (!bank->irq) - bank->irq = -ENXIO; - return dev_err_probe(dev, bank->irq, "can't get irq resource\n"); - } + if (bank->irq < 0) + return bank->irq; bank->chip.parent = dev; bank->chip.owner = THIS_MODULE; From 451c923d4c638a2f8215fea35e10f2dcd921a577 Mon Sep 17 00:00:00 2001 From: Okan Sahin Date: Thu, 27 Jul 2023 12:54:29 +0300 Subject: [PATCH 35/94] dt-bindings: gpio: ds4520: Add ADI DS4520 Add ADI DS4520 devicetree document. Signed-off-by: Okan Sahin Reviewed-by: Krzysztof Kozlowski Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/adi,ds4520-gpio.yaml | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/adi,ds4520-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/adi,ds4520-gpio.yaml b/Documentation/devicetree/bindings/gpio/adi,ds4520-gpio.yaml new file mode 100644 index 000000000000..25b3198c4d3e --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/adi,ds4520-gpio.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/adi,ds4520-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: DS4520 I2C GPIO expander + +maintainers: + - Okan Sahin + +properties: + compatible: + enum: + - adi,ds4520-gpio + + reg: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + + ngpios: + minimum: 1 + maximum: 9 + +required: + - compatible + - reg + - gpio-controller + - "#gpio-cells" + - ngpios + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + gpio@50 { + compatible = "adi,ds4520-gpio"; + reg = <0x50>; + ngpios = <9>; + gpio-controller; + #gpio-cells = <2>; + }; + }; From 659ad5f7efece8f92213ca069c494a37507c8c67 Mon Sep 17 00:00:00 2001 From: Okan Sahin Date: Sat, 29 Jul 2023 15:56:33 +0200 Subject: [PATCH 36/94] gpio: ds4520: Add ADI DS4520 GPIO Expander Support The DS4520 is a 9-bit nonvolatile (NV) I/O expander. It offers users a digitally programmable alternative to hardware jumpers and mechanical switches that are being used to control digital logic node. Signed-off-by: Okan Sahin Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 11 ++++++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-ds4520.c | 80 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 drivers/gpio/gpio-ds4520.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 61a8d07e5a1c..c5324e44b74e 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1029,6 +1029,17 @@ config GPIO_FXL6408 To compile this driver as a module, choose M here: the module will be called gpio-fxl6408. +config GPIO_DS4520 + tristate "DS4520 I2C GPIO expander" + select REGMAP_I2C + select GPIO_REGMAP + help + GPIO driver for ADI DS4520 I2C-based GPIO expander. + Say yes here to enable the GPIO driver for the ADI DS4520 chip. + + To compile this driver as a module, choose M here: the module will + be called gpio-ds4520. + config GPIO_GW_PLD tristate "Gateworks PLD GPIO Expander" depends on OF_GPIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index c3ac51d47aa9..eb73b5d633eb 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o +obj-$(CONFIG_GPIO_DS4520) += gpio-ds4520.o obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o obj-$(CONFIG_GPIO_EIC_SPRD) += gpio-eic-sprd.o obj-$(CONFIG_GPIO_ELKHARTLAKE) += gpio-elkhartlake.o diff --git a/drivers/gpio/gpio-ds4520.c b/drivers/gpio/gpio-ds4520.c new file mode 100644 index 000000000000..1903deaef3e9 --- /dev/null +++ b/drivers/gpio/gpio-ds4520.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Analog Devices, Inc. + * Driver for the DS4520 I/O Expander + */ + +#include +#include +#include +#include +#include +#include + +#define DS4520_PULLUP0 0xF0 +#define DS4520_IO_CONTROL0 0xF2 +#define DS4520_IO_STATUS0 0xF8 + +static const struct regmap_config ds4520_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int ds4520_gpio_probe(struct i2c_client *client) +{ + struct gpio_regmap_config config = { }; + struct device *dev = &client->dev; + struct regmap *regmap; + u32 ngpio; + u32 base; + int ret; + + ret = device_property_read_u32(dev, "reg", &base); + if (ret) + return dev_err_probe(dev, ret, "Missing 'reg' property.\n"); + + ret = device_property_read_u32(dev, "ngpios", &ngpio); + if (ret) + return dev_err_probe(dev, ret, "Missing 'ngpios' property.\n"); + + regmap = devm_regmap_init_i2c(client, &ds4520_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to allocate register map\n"); + + config.regmap = regmap; + config.parent = dev; + config.ngpio = ngpio; + + config.reg_dat_base = base + DS4520_IO_STATUS0; + config.reg_set_base = base + DS4520_PULLUP0; + config.reg_dir_out_base = base + DS4520_IO_CONTROL0; + + return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &config)); +} + +static const struct of_device_id ds4520_gpio_of_match_table[] = { + { .compatible = "adi,ds4520-gpio" }, + { } +}; +MODULE_DEVICE_TABLE(of, ds4520_gpio_of_match_table); + +static const struct i2c_device_id ds4520_gpio_id_table[] = { + { "ds4520-gpio" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ds4520_gpio_id_table); + +static struct i2c_driver ds4520_gpio_driver = { + .driver = { + .name = "ds4520-gpio", + .of_match_table = ds4520_gpio_of_match_table, + }, + .probe = ds4520_gpio_probe, + .id_table = ds4520_gpio_id_table, +}; +module_i2c_driver(ds4520_gpio_driver); + +MODULE_DESCRIPTION("DS4520 I/O Expander"); +MODULE_AUTHOR("Okan Sahin "); +MODULE_LICENSE("GPL"); From 73561d281631630748fd94bca120a79076b52266 Mon Sep 17 00:00:00 2001 From: Ruan Jinjie Date: Thu, 27 Jul 2023 19:57:26 +0800 Subject: [PATCH 37/94] gpio: davinci: Remove redundant dev_err_probe() There is no need to call the dev_err_probe() function directly to print a custom message when handling an error from platform_get_irq() function as it is going to display an appropriate error message in case of a failure. Signed-off-by: Ruan Jinjie Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-davinci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index fff510d86e31..8db5717bdabe 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -236,7 +236,7 @@ static int davinci_gpio_probe(struct platform_device *pdev) for (i = 0; i < nirq; i++) { chips->irqs[i] = platform_get_irq(pdev, i); if (chips->irqs[i] < 0) - return dev_err_probe(dev, chips->irqs[i], "IRQ not populated\n"); + return chips->irqs[i]; } chips->chip.label = dev_name(dev); From e5780d80ce1d86298e5ad5a91264276bff8c9179 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Mon, 24 Jul 2023 08:35:19 +0200 Subject: [PATCH 38/94] dt-bindings: gpio: Add gpio-line-names to STMPE GPIO This is a gpio-controller, so gpio-line-names should be allowed as well. stmpe2403 supports up to 24 GPIOs. Signed-off-by: Alexander Stein Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/st,stmpe-gpio.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/st,stmpe-gpio.yaml b/Documentation/devicetree/bindings/gpio/st,stmpe-gpio.yaml index 22c0cae73425..4555f1644a4d 100644 --- a/Documentation/devicetree/bindings/gpio/st,stmpe-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/st,stmpe-gpio.yaml @@ -28,6 +28,10 @@ properties: gpio-controller: true + gpio-line-names: + minItems: 1 + maxItems: 24 + interrupt-controller: true st,norequest-mask: From 28e6c5b86ac3756235b9a0ae6b1409f6ac33cd09 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Wed, 12 Jul 2023 12:48:57 +0300 Subject: [PATCH 39/94] gpio: 74xx-mmio: remove unneeded platform_set_drvdata() call The platform_set_drvdata() was needed when the driver had an explicit remove function. That function got removed a while back, so we don't need to keep a pointer (on 'dev->driver_data') for the private data of the driver anymore. Signed-off-by: Alexandru Ardelean Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-74xx-mmio.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c index 0464f1ecd20d..c7ac5a9ffb1f 100644 --- a/drivers/gpio/gpio-74xx-mmio.c +++ b/drivers/gpio/gpio-74xx-mmio.c @@ -135,8 +135,6 @@ static int mmio_74xx_gpio_probe(struct platform_device *pdev) priv->gc.ngpio = MMIO_74XX_BIT_CNT(priv->flags); priv->gc.owner = THIS_MODULE; - platform_set_drvdata(pdev, priv); - return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv); } From ba8a90e8cb8ca4db4e57be6b53aa8c364137b38c Mon Sep 17 00:00:00 2001 From: Andrei Coardos Date: Wed, 19 Jul 2023 12:39:23 +0300 Subject: [PATCH 40/94] gpio: exar: remove unneeded platform_set_drvdata() call The platform_set_drvdata() was needed when the driver had an explicit remove function. That function got removed a while back, so we don't need to keep a pointer (on 'dev->driver_data') for the private data of the driver anymore. Reviewed-by: Alexandru Ardelean Signed-off-by: Andrei Coardos Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-exar.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index df1bdaae441c..5170fe7599cd 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -217,8 +217,6 @@ static int gpio_exar_probe(struct platform_device *pdev) if (ret) return ret; - platform_set_drvdata(pdev, exar_gpio); - return 0; } From 8020619ad785c87e807ca9f782238294c2a602e0 Mon Sep 17 00:00:00 2001 From: Andrei Coardos Date: Wed, 19 Jul 2023 12:48:03 +0300 Subject: [PATCH 41/94] gpio: logicvc: remove unneeded platform_set_drvdata() call The platform_set_drvdata() isn't needed for anything. The function is a simple setter that doesn't change anything in the code. That is because there isn't a get function and since it has no dependencies it can be removed. Reviewed-by: Alexandru Ardelean Signed-off-by: Andrei Coardos Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-logicvc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-logicvc.c b/drivers/gpio/gpio-logicvc.c index 2b9876bc1383..05d62011f335 100644 --- a/drivers/gpio/gpio-logicvc.c +++ b/drivers/gpio/gpio-logicvc.c @@ -138,8 +138,6 @@ static int logicvc_gpio_probe(struct platform_device *pdev) logicvc->chip.set = logicvc_gpio_set; logicvc->chip.direction_output = logicvc_gpio_direction_output; - platform_set_drvdata(pdev, logicvc); - return devm_gpiochip_add_data(dev, &logicvc->chip, logicvc); } From c456c4d9707a0bd484a1af30188f5c416fc394f0 Mon Sep 17 00:00:00 2001 From: Andrei Coardos Date: Mon, 17 Jul 2023 15:10:04 +0300 Subject: [PATCH 42/94] gpio: eic-sprd: remove unneeded platform_set_drvdata() call The platform_set_drvdata() call was never used, ever since the driver was originally added. It looks like this copy+paste left-over. Possibly the author copied from a driver that had this line, but also had a remove hook. Reviewed-by: Alexandru Ardelean Signed-off-by: Andrei Coardos Reviewed-by: Andy Shevchenko Reviewed-by: Baolin Wang Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-eic-sprd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index 67b1e09e8985..5320cf1de89c 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -653,7 +653,6 @@ static int sprd_eic_probe(struct platform_device *pdev) return ret; } - platform_set_drvdata(pdev, sprd_eic); return 0; } From c518e7dc64573e3e3a196e8b79caa4f77c612366 Mon Sep 17 00:00:00 2001 From: Andrei Coardos Date: Tue, 25 Jul 2023 13:25:32 +0300 Subject: [PATCH 43/94] gpio: lp3943: remove unneeded platform_set_drvdata() call This function call was found to be unnecessary as there is no equivalent platform_get_drvdata() call, to access the private data of the driver. Also, the private data is defined in this driver, so there is no risk of it being accessed outside of this driver file. Reviewed-by: Alexandru Ardelean Signed-off-by: Andrei Coardos Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-lp3943.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-lp3943.c b/drivers/gpio/gpio-lp3943.c index 79edd5db49d2..8e58242f5123 100644 --- a/drivers/gpio/gpio-lp3943.c +++ b/drivers/gpio/gpio-lp3943.c @@ -199,8 +199,6 @@ static int lp3943_gpio_probe(struct platform_device *pdev) lp3943_gpio->chip = lp3943_gpio_chip; lp3943_gpio->chip.parent = &pdev->dev; - platform_set_drvdata(pdev, lp3943_gpio); - return devm_gpiochip_add_data(&pdev->dev, &lp3943_gpio->chip, lp3943_gpio); } From ceac51b1ee43532d35d0182117b326306bcfe7f4 Mon Sep 17 00:00:00 2001 From: Andrei Coardos Date: Tue, 25 Jul 2023 15:36:23 +0300 Subject: [PATCH 44/94] gpio: max77620: remove unneeded platform_set_drvdata() call This function call is not required because no counterpart platform_get_drvdata() call is present to leverage the private data of the driver. Since the private data is confined to this driver file, external access is not feasible. The use of this function appears redundant in the current context of the driver's implementation. Reviewed-by: Alexandru Ardelean Signed-off-by: Andrei Coardos Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-max77620.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index c18b60e39a94..8c2a5609161f 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -331,8 +331,6 @@ static int max77620_gpio_probe(struct platform_device *pdev) girq->init_hw = max77620_gpio_irq_init_hw; girq->threaded = true; - platform_set_drvdata(pdev, mgpio); - ret = devm_gpiochip_add_data(&pdev->dev, &mgpio->gpio_chip, mgpio); if (ret < 0) { dev_err(&pdev->dev, "gpio_init: Failed to add max77620_gpio\n"); From 5f57665a217e82ca5e5362829f64b526d2f245c5 Mon Sep 17 00:00:00 2001 From: Andrei Coardos Date: Fri, 28 Jul 2023 20:59:14 +0300 Subject: [PATCH 45/94] gpio: palmas: remove unnecessary call to platform_set_drvdata() This function call was found to be unnecessary as there is no equivalent platform_get_drvdata() call to access the private data of the driver. Also, the private data is defined in this driver, so there is no risk of it being accessed outside of this driver file. Reviewed-by: Alexandru Ardelean Signed-off-by: Andrei Coardos Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-palmas.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c index 6140e87c6754..28dba7048509 100644 --- a/drivers/gpio/gpio-palmas.c +++ b/drivers/gpio/gpio-palmas.c @@ -183,7 +183,6 @@ static int palmas_gpio_probe(struct platform_device *pdev) return ret; } - platform_set_drvdata(pdev, palmas_gpio); return ret; } From b0c488615eb147d3f680ad3c2e2200a1502da1fc Mon Sep 17 00:00:00 2001 From: Stanislav Jakubek Date: Sun, 30 Jul 2023 17:23:32 +0200 Subject: [PATCH 46/94] dt-bindings: gpio: brcm,kona-gpio: convert to YAML Convert Broadcom Kona family GPIO controller bindings to DT schema. Changes during conversion: - add used, but previously undocumented SoC-specific compatibles Signed-off-by: Stanislav Jakubek Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/brcm,kona-gpio.txt | 52 --------- .../bindings/gpio/brcm,kona-gpio.yaml | 100 ++++++++++++++++++ MAINTAINERS | 2 +- 3 files changed, 101 insertions(+), 53 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/brcm,kona-gpio.txt create mode 100644 Documentation/devicetree/bindings/gpio/brcm,kona-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/brcm,kona-gpio.txt b/Documentation/devicetree/bindings/gpio/brcm,kona-gpio.txt deleted file mode 100644 index 4a63bc96b687..000000000000 --- a/Documentation/devicetree/bindings/gpio/brcm,kona-gpio.txt +++ /dev/null @@ -1,52 +0,0 @@ -Broadcom Kona Family GPIO -========================= - -This GPIO driver is used in the following Broadcom SoCs: - BCM11130, BCM11140, BCM11351, BCM28145, BCM28155 - -The Broadcom GPIO Controller IP can be configured prior to synthesis to -support up to 8 banks of 32 GPIOs where each bank has its own IRQ. The -GPIO controller only supports edge, not level, triggering of interrupts. - -Required properties -------------------- - -- compatible: "brcm,bcm11351-gpio", "brcm,kona-gpio" -- reg: Physical base address and length of the controller's registers. -- interrupts: The interrupt outputs from the controller. There is one GPIO - interrupt per GPIO bank. The number of interrupts listed depends on the - number of GPIO banks on the SoC. The interrupts must be ordered by bank, - starting with bank 0. There is always a 1:1 mapping between banks and - IRQs. -- #gpio-cells: Should be <2>. The first cell is the pin number, the second - cell is used to specify optional parameters: - - bit 0 specifies polarity (0 for normal, 1 for inverted) - See also "gpio-specifier" in .../devicetree/bindings/gpio/gpio.txt. -- #interrupt-cells: Should be <2>. The first cell is the GPIO number. The - second cell is used to specify flags. The following subset of flags is - supported: - - trigger type (bits[1:0]): - 1 = low-to-high edge triggered. - 2 = high-to-low edge triggered. - 3 = low-to-high or high-to-low edge triggered - Valid values are 1, 2, 3 - See also .../devicetree/bindings/interrupt-controller/interrupts.txt. -- gpio-controller: Marks the device node as a GPIO controller. -- interrupt-controller: Marks the device node as an interrupt controller. - -Example: - gpio: gpio@35003000 { - compatible = "brcm,bcm11351-gpio", "brcm,kona-gpio"; - reg = <0x35003000 0x800>; - interrupts = - ; - #gpio-cells = <2>; - #interrupt-cells = <2>; - gpio-controller; - interrupt-controller; - }; diff --git a/Documentation/devicetree/bindings/gpio/brcm,kona-gpio.yaml b/Documentation/devicetree/bindings/gpio/brcm,kona-gpio.yaml new file mode 100644 index 000000000000..296fdd6b8f38 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/brcm,kona-gpio.yaml @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/brcm,kona-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom Kona family GPIO controller + +description: + The Broadcom GPIO Controller IP can be configured prior to synthesis to + support up to 8 banks of 32 GPIOs where each bank has its own IRQ. The + GPIO controller only supports edge, not level, triggering of interrupts. + +maintainers: + - Ray Jui + +properties: + compatible: + items: + - enum: + - brcm,bcm11351-gpio + - brcm,bcm21664-gpio + - brcm,bcm23550-gpio + - const: brcm,kona-gpio + + reg: + maxItems: 1 + + interrupts: + minItems: 4 + maxItems: 6 + description: + The interrupt outputs from the controller. There is one GPIO interrupt + per GPIO bank. The number of interrupts listed depends on the number of + GPIO banks on the SoC. The interrupts must be ordered by bank, starting + with bank 0. There is always a 1:1 mapping between banks and IRQs. + + '#gpio-cells': + const: 2 + + '#interrupt-cells': + const: 2 + + gpio-controller: true + + interrupt-controller: true + +required: + - compatible + - reg + - interrupts + - '#gpio-cells' + - '#interrupt-cells' + - gpio-controller + - interrupt-controller + +allOf: + - if: + properties: + compatible: + contains: + const: brcm,bcm11351-gpio + then: + properties: + interrupts: + minItems: 6 + - if: + properties: + compatible: + contains: + enum: + - brcm,bcm21664-gpio + - brcm,bcm23550-gpio + then: + properties: + interrupts: + maxItems: 4 + +additionalProperties: false + +examples: + - | + #include + #include + + gpio@35003000 { + compatible = "brcm,bcm11351-gpio", "brcm,kona-gpio"; + reg = <0x35003000 0x800>; + interrupts = , + , + , + , + , + ; + #gpio-cells = <2>; + #interrupt-cells = <2>; + gpio-controller; + interrupt-controller; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 3be1bdfe8ecc..e423e15f48f1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4187,7 +4187,7 @@ BROADCOM KONA GPIO DRIVER M: Ray Jui R: Broadcom internal kernel review list S: Supported -F: Documentation/devicetree/bindings/gpio/brcm,kona-gpio.txt +F: Documentation/devicetree/bindings/gpio/brcm,kona-gpio.yaml F: drivers/gpio/gpio-bcm-kona.c BROADCOM MPI3 STORAGE CONTROLLER DRIVER From e91e8b537a6fa3ed5bbd53e0504b326edfaafb49 Mon Sep 17 00:00:00 2001 From: Andrei Coardos Date: Fri, 28 Jul 2023 21:08:33 +0300 Subject: [PATCH 47/94] gpio: pmic-eic-sprd: remove unnecessary call to platform_set_drvdata() This function call was found to be unnecessary as there is no equivalent platform_get_drvdata() call to access the private data of the driver. Also, the private data is defined in this driver, so there is no risk of it being accessed outside of this driver file. Reviewed-by: Alexandru Ardelean Signed-off-by: Andrei Coardos Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pmic-eic-sprd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c index dabb0da3bd33..2b9b7be9b8fd 100644 --- a/drivers/gpio/gpio-pmic-eic-sprd.c +++ b/drivers/gpio/gpio-pmic-eic-sprd.c @@ -363,7 +363,6 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev) return ret; } - platform_set_drvdata(pdev, pmic_eic); return 0; } From 8e85d6af1b3ceb619098c6b8a9f38e9e006e9c09 Mon Sep 17 00:00:00 2001 From: Andrei Coardos Date: Fri, 28 Jul 2023 21:20:09 +0300 Subject: [PATCH 48/94] gpio: rc5t583: remove unneeded call to platform_set_drvdata() This function call was found to be unnecessary as there is no equivalent platform_get_drvdata() call to access the private data of the driver. Also, the private data is defined in this driver, so there is no risk of it being accessed outside of this driver file. Reviewed-by: Alexandru Ardelean Signed-off-by: Andrei Coardos Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rc5t583.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-rc5t583.c b/drivers/gpio/gpio-rc5t583.c index 4fae3ebea790..c34dcadaee36 100644 --- a/drivers/gpio/gpio-rc5t583.c +++ b/drivers/gpio/gpio-rc5t583.c @@ -121,8 +121,6 @@ static int rc5t583_gpio_probe(struct platform_device *pdev) if (pdata && pdata->gpio_base) rc5t583_gpio->gpio_chip.base = pdata->gpio_base; - platform_set_drvdata(pdev, rc5t583_gpio); - return devm_gpiochip_add_data(&pdev->dev, &rc5t583_gpio->gpio_chip, rc5t583_gpio); } From c975cc599eb7336e0fad3cd81bbebcbe40e24c4e Mon Sep 17 00:00:00 2001 From: Andrei Coardos Date: Fri, 28 Jul 2023 21:25:22 +0300 Subject: [PATCH 49/94] gpio: sama5d2-piobu: remove unneeded call to platform_set_drvdata() This function call was found to be unnecessary as there is no equivalent platform_get_drvdata() call to access the private data of the driver. Also, the private data is defined in this driver, so there is no risk of it being accessed outside of this driver file. Reviewed-by: Alexandru Ardelean Signed-off-by: Andrei Coardos Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sama5d2-piobu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c index 767c33ae3213..d89da7300ddd 100644 --- a/drivers/gpio/gpio-sama5d2-piobu.c +++ b/drivers/gpio/gpio-sama5d2-piobu.c @@ -189,7 +189,6 @@ static int sama5d2_piobu_probe(struct platform_device *pdev) if (!piobu) return -ENOMEM; - platform_set_drvdata(pdev, piobu); piobu->chip.label = pdev->name; piobu->chip.parent = &pdev->dev; piobu->chip.owner = THIS_MODULE, From b11eba8b02e592b69b41af01ab7325a853441908 Mon Sep 17 00:00:00 2001 From: Andrei Coardos Date: Tue, 1 Aug 2023 10:28:00 +0300 Subject: [PATCH 50/94] gpio: sch: remove unneeded call to platform_set_drvdata() This function call was found to be unnecessary as there is no equivalent platform_get_drvdata() call to access the private data of the driver. Also, the private data is defined in this driver, so there is no risk of it being accessed outside of this driver file. Reviewed-by: Alexandru Ardelean Signed-off-by: Andrei Coardos Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sch.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c index 8a83f7bf4382..e48392074e4b 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -380,8 +380,6 @@ static int sch_gpio_probe(struct platform_device *pdev) return -ENODEV; } - platform_set_drvdata(pdev, sch); - girq = &sch->chip.irq; gpio_irq_chip_set_chip(girq, &sch_irqchip); girq->num_parents = 0; From 41eb8510f18250ced8bef44f0dead759f502fb9e Mon Sep 17 00:00:00 2001 From: Andrei Coardos Date: Tue, 1 Aug 2023 10:30:44 +0300 Subject: [PATCH 51/94] gpio: syscon: remove unneeded call to platform_set_drvdata() This function call was found to be unnecessary as there is no equivalent platform_get_drvdata() call to access the private data of the driver. Also, the private data is defined in this driver, so there is no risk of it being accessed outside of this driver file. Reviewed-by: Alexandru Ardelean Signed-off-by: Andrei Coardos Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-syscon.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c index 41bf47c8c983..6e1a2581e6ae 100644 --- a/drivers/gpio/gpio-syscon.c +++ b/drivers/gpio/gpio-syscon.c @@ -248,8 +248,6 @@ static int syscon_gpio_probe(struct platform_device *pdev) priv->chip.direction_output = syscon_gpio_dir_out; } - platform_set_drvdata(pdev, priv); - return devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv); } From 02840579a94da4fb73292464c34c47222050923d Mon Sep 17 00:00:00 2001 From: Andrei Coardos Date: Tue, 1 Aug 2023 10:32:11 +0300 Subject: [PATCH 52/94] gpio: timberdale: remove unneeded call to platform_set_drvdata() This function call was found to be unnecessary as there is no equivalent platform_get_drvdata() call to access the private data of the driver. Also, the private data is defined in this driver, so there is no risk of it being accessed outside of this driver file. Reviewed-by: Alexandru Ardelean Signed-off-by: Andrei Coardos Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-timberdale.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c index de14949a3fe5..bbd9e9191199 100644 --- a/drivers/gpio/gpio-timberdale.c +++ b/drivers/gpio/gpio-timberdale.c @@ -256,8 +256,6 @@ static int timbgpio_probe(struct platform_device *pdev) if (err) return err; - platform_set_drvdata(pdev, tgpio); - /* make sure to disable interrupts */ iowrite32(0x0, tgpio->membase + TGPIO_IER); From 0a5e9306b812fe3517548fab92b3d3d6ce7576e5 Mon Sep 17 00:00:00 2001 From: Andrei Coardos Date: Tue, 1 Aug 2023 23:38:39 +0300 Subject: [PATCH 53/94] gpio: tqmx86: remove unneeded call to platform_set_drvdata() This function call was found to be unnecessary as there is no equivalent platform_get_drvdata() call to access the private data of the driver. Also, the private data is defined in this driver, so there is no risk of it being accessed outside of this driver file. Reviewed-by: Alexandru Ardelean Signed-off-by: Andrei Coardos Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tqmx86.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index 6f8bd1155db7..3a28c1f273c3 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -277,8 +277,6 @@ static int tqmx86_gpio_probe(struct platform_device *pdev) tqmx86_gpio_write(gpio, (u8)~TQMX86_DIR_INPUT_MASK, TQMX86_GPIODD); - platform_set_drvdata(pdev, gpio); - chip = &gpio->chip; chip->label = "gpio-tqmx86"; chip->owner = THIS_MODULE; From 872982cecd2f1f1dc67502732f5126267a22da3f Mon Sep 17 00:00:00 2001 From: Andrei Coardos Date: Tue, 1 Aug 2023 23:44:45 +0300 Subject: [PATCH 54/94] gpio: vx855: remove unneeded call to platform_set_drvdata() This function call was found to be unnecessary as there is no equivalent platform_get_drvdata() call to access the private data of the driver. Also, the private data is defined in this driver, so there is no risk of it being accessed outside of this driver file. Reviewed-by: Alexandru Ardelean Signed-off-by: Andrei Coardos Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-vx855.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-vx855.c b/drivers/gpio/gpio-vx855.c index 69713fd5485b..8fd6c3913d69 100644 --- a/drivers/gpio/gpio-vx855.c +++ b/drivers/gpio/gpio-vx855.c @@ -240,8 +240,6 @@ static int vx855gpio_probe(struct platform_device *pdev) if (!vg) return -ENOMEM; - platform_set_drvdata(pdev, vg); - dev_info(&pdev->dev, "found VX855 GPIO controller\n"); vg->io_gpi = res_gpi->start; vg->io_gpo = res_gpo->start; From 146bf98e00886420199d8892d3684c9736789b0c Mon Sep 17 00:00:00 2001 From: Andrei Coardos Date: Tue, 1 Aug 2023 23:46:06 +0300 Subject: [PATCH 55/94] gpio: zevio: remove unneeded call to platform_set_drvdata() This function call was found to be unnecessary as there is no equivalent platform_get_drvdata() call to access the private data of the driver. Also, the private data is defined in this driver, so there is no risk of it being accessed outside of this driver file. Reviewed-by: Alexandru Ardelean Signed-off-by: Andrei Coardos Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-zevio.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-zevio.c b/drivers/gpio/gpio-zevio.c index f0f571b323f2..2de61337ad3b 100644 --- a/drivers/gpio/gpio-zevio.c +++ b/drivers/gpio/gpio-zevio.c @@ -176,8 +176,6 @@ static int zevio_gpio_probe(struct platform_device *pdev) if (!controller) return -ENOMEM; - platform_set_drvdata(pdev, controller); - /* Copy our reference */ controller->chip = zevio_gpio_chip; controller->chip.parent = &pdev->dev; From 455d39ec96f0d8020adf465ab1f864a722d96eb3 Mon Sep 17 00:00:00 2001 From: Ruan Jinjie Date: Thu, 3 Aug 2023 11:39:37 +0800 Subject: [PATCH 56/94] gpio: ftgpio010: Do not check for 0 return after calling platform_get_irq() Since commit a85a6c86c25b ("driver core: platform: Clarify that IRQ 0 is invalid"), there is no possible for platform_get_irq() to return 0. And the return value of platform_get_irq() is more sensible to show the error reason. Signed-off-by: Ruan Jinjie Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ftgpio010.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-ftgpio010.c b/drivers/gpio/gpio-ftgpio010.c index 31e26072f6ae..5ce59dcf02e3 100644 --- a/drivers/gpio/gpio-ftgpio010.c +++ b/drivers/gpio/gpio-ftgpio010.c @@ -250,8 +250,8 @@ static int ftgpio_gpio_probe(struct platform_device *pdev) return PTR_ERR(g->base); irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return irq ? irq : -EINVAL; + if (irq < 0) + return irq; g->clk = devm_clk_get(dev, NULL); if (!IS_ERR(g->clk)) { From c4dc167c684b8d9e58d3e4bde5b7eaef5d4e8cf5 Mon Sep 17 00:00:00 2001 From: Zhu Wang Date: Thu, 3 Aug 2023 10:28:44 +0800 Subject: [PATCH 57/94] gpio: tps65218: remove redundant of_match_ptr() The driver depends on CONFIG_OF, so it is not necessary to use of_match_ptr() here. Even for drivers that do not depend on CONFIG_OF, it's almost always better to leave out the of_match_ptr(), since the only thing it can possibly do is to save a few bytes of .text if a driver can be used both with and without it. Hence we remove of_match_ptr(). Signed-off-by: Zhu Wang Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tps65218.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c index e1d425a18854..d7d9d50dcddf 100644 --- a/drivers/gpio/gpio-tps65218.c +++ b/drivers/gpio/gpio-tps65218.c @@ -216,7 +216,7 @@ MODULE_DEVICE_TABLE(platform, tps65218_gpio_id_table); static struct platform_driver tps65218_gpio_driver = { .driver = { .name = "tps65218-gpio", - .of_match_table = of_match_ptr(tps65218_dt_match) + .of_match_table = tps65218_dt_match, }, .probe = tps65218_gpio_probe, .id_table = tps65218_gpio_id_table, From 9c573074895f9e2da3cbe92605dce5d765b311b7 Mon Sep 17 00:00:00 2001 From: Zhu Wang Date: Thu, 3 Aug 2023 10:20:56 +0800 Subject: [PATCH 58/94] gpio: max732x: remove redundant CONFIG_OF and of_match_ptr() The driver depends on CONFIG_OF, so it is not necessary to use of_match_ptr() here. We remove both CONFIG_OF and of_match_ptr() here. Even for drivers that do not depend on CONFIG_OF, it's almost always better to leave out the of_match_ptr(), since the only thing it can possibly do is to save a few bytes of .text if a driver can be used both with and without it. Signed-off-by: Zhu Wang Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-max732x.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index fca9ca68e387..49d362907bc7 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -18,8 +18,6 @@ #include #include #include -#include - /* * Each port of MAX732x (including MAX7319) falls into one of the @@ -114,7 +112,6 @@ static const struct i2c_device_id max732x_id[] = { }; MODULE_DEVICE_TABLE(i2c, max732x_id); -#ifdef CONFIG_OF static const struct of_device_id max732x_of_table[] = { { .compatible = "maxim,max7319" }, { .compatible = "maxim,max7320" }, @@ -128,7 +125,6 @@ static const struct of_device_id max732x_of_table[] = { { } }; MODULE_DEVICE_TABLE(of, max732x_of_table); -#endif struct max732x_chip { struct gpio_chip gpio_chip; @@ -709,7 +705,7 @@ static int max732x_probe(struct i2c_client *client) static struct i2c_driver max732x_driver = { .driver = { .name = "max732x", - .of_match_table = of_match_ptr(max732x_of_table), + .of_match_table = max732x_of_table, }, .probe = max732x_probe, .id_table = max732x_id, From a374467ae68c23d4c9e9d5009207c1414469c5b0 Mon Sep 17 00:00:00 2001 From: Zhu Wang Date: Wed, 2 Aug 2023 10:24:03 +0800 Subject: [PATCH 59/94] gpio: altera-a10sr: remove redundant of_match_ptr The driver depends on CONFIG_OF, so it is not necessary to use of_match_ptr() here. Even for drivers that do not depend on CONFIG_OF, it's almost always better to leave out the of_match_ptr(), since the only thing it can possibly do is to save a few bytes of .text if a driver can be used both with and without it. Hence we remove of_match_ptr(). Signed-off-by: Zhu Wang Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-altera-a10sr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-altera-a10sr.c b/drivers/gpio/gpio-altera-a10sr.c index be1ed7ee5225..11edf1fe6c90 100644 --- a/drivers/gpio/gpio-altera-a10sr.c +++ b/drivers/gpio/gpio-altera-a10sr.c @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -104,7 +105,7 @@ static struct platform_driver altr_a10sr_gpio_driver = { .probe = altr_a10sr_gpio_probe, .driver = { .name = "altr_a10sr_gpio", - .of_match_table = of_match_ptr(altr_a10sr_gpio_of_match), + .of_match_table = altr_a10sr_gpio_of_match, }, }; module_platform_driver(altr_a10sr_gpio_driver); From a0d22277ba13b609a6f792b61aa1798c3c653676 Mon Sep 17 00:00:00 2001 From: Zhu Wang Date: Wed, 2 Aug 2023 10:54:31 +0800 Subject: [PATCH 60/94] gpio: clps711x: remove redundant of_match_ptr() The driver depends on CONFIG_OF, so it is not necessary to use of_match_ptr() here. Even for drivers that do not depend on CONFIG_OF, it's almost always better to leave out the of_match_ptr(), since the only thing it can possibly do is to save a few bytes of .text if a driver can be used both with and without it. Hence we remove of_match_ptr(). Signed-off-by: Zhu Wang Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-clps711x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c index 75f6f8d4323e..d69a24dd4828 100644 --- a/drivers/gpio/gpio-clps711x.c +++ b/drivers/gpio/gpio-clps711x.c @@ -67,7 +67,7 @@ static int clps711x_gpio_probe(struct platform_device *pdev) return devm_gpiochip_add_data(&pdev->dev, gc, NULL); } -static const struct of_device_id __maybe_unused clps711x_gpio_ids[] = { +static const struct of_device_id clps711x_gpio_ids[] = { { .compatible = "cirrus,ep7209-gpio" }, { } }; @@ -76,7 +76,7 @@ MODULE_DEVICE_TABLE(of, clps711x_gpio_ids); static struct platform_driver clps711x_gpio_driver = { .driver = { .name = "clps711x-gpio", - .of_match_table = of_match_ptr(clps711x_gpio_ids), + .of_match_table = clps711x_gpio_ids, }, .probe = clps711x_gpio_probe, }; From 07d93cbb3dc01e8cf2c39e996f60cbff3a77ebb2 Mon Sep 17 00:00:00 2001 From: Zhu Wang Date: Wed, 2 Aug 2023 10:44:55 +0800 Subject: [PATCH 61/94] gpio: ixp4xx: remove redundant of_match_ptr() The driver depends on CONFIG_OF, so it is not necessary to use of_match_ptr() here. Even for drivers that do not depend on CONFIG_OF, it's almost always better to leave out the of_match_ptr(), since the only thing it can possibly do is to save a few bytes of .text if a driver can be used both with and without it. Hence we remove of_match_ptr(). Signed-off-by: Zhu Wang Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ixp4xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-ixp4xx.c b/drivers/gpio/gpio-ixp4xx.c index 1e29de1671d4..dde6cf3a5779 100644 --- a/drivers/gpio/gpio-ixp4xx.c +++ b/drivers/gpio/gpio-ixp4xx.c @@ -302,7 +302,7 @@ static const struct of_device_id ixp4xx_gpio_of_match[] = { static struct platform_driver ixp4xx_gpio_driver = { .driver = { .name = "ixp4xx-gpio", - .of_match_table = of_match_ptr(ixp4xx_gpio_of_match), + .of_match_table = ixp4xx_gpio_of_match, }, .probe = ixp4xx_gpio_probe, }; From bcb6b9e50df8367696ab0704b3858790e5589983 Mon Sep 17 00:00:00 2001 From: Zhu Wang Date: Wed, 2 Aug 2023 11:17:49 +0800 Subject: [PATCH 62/94] gpio: lpc32xx: remove redundant CONFIG_OF and of_match_ptr() The driver depends on CONFIG_OF, so it is not necessary to use CONFIG_OF and of_match_ptr() here, so we remove them all. Even for drivers that do not depend on CONFIG_OF, it's almost always better to leave out the of_match_ptr(), since the only thing it can possibly do is to save a few bytes of .text if a driver can be used both with and without it. Signed-off-by: Zhu Wang Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-lpc32xx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c index d2b65cfb336e..5ef8af824980 100644 --- a/drivers/gpio/gpio-lpc32xx.c +++ b/drivers/gpio/gpio-lpc32xx.c @@ -525,17 +525,15 @@ static int lpc32xx_gpio_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_OF static const struct of_device_id lpc32xx_gpio_of_match[] = { { .compatible = "nxp,lpc3220-gpio", }, { }, }; -#endif static struct platform_driver lpc32xx_gpio_driver = { .driver = { .name = "lpc32xx-gpio", - .of_match_table = of_match_ptr(lpc32xx_gpio_of_match), + .of_match_table = lpc32xx_gpio_of_match, }, .probe = lpc32xx_gpio_probe, }; From 5878753886c30c041110368982f34a494997501d Mon Sep 17 00:00:00 2001 From: Zhu Wang Date: Wed, 2 Aug 2023 11:29:57 +0800 Subject: [PATCH 63/94] gpio: max3191x: remove redundant of_match_ptr() The driver depends on CONFIG_OF, so it is not necessary to use CONFIG_OF and of_match_ptr() here, we remove them all. Even for drivers that do not depend on CONFIG_OF, it's almost always better to leave out the of_match_ptr(), since the only thing it can possibly do is to save a few bytes of .text if a driver can be used both with and without it. Signed-off-by: Zhu Wang Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-max3191x.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c index 161c4751c5f7..bbacc714632b 100644 --- a/drivers/gpio/gpio-max3191x.c +++ b/drivers/gpio/gpio-max3191x.c @@ -457,7 +457,6 @@ static int __init max3191x_register_driver(struct spi_driver *sdrv) return spi_register_driver(sdrv); } -#ifdef CONFIG_OF static const struct of_device_id max3191x_of_id[] = { { .compatible = "maxim,max31910" }, { .compatible = "maxim,max31911" }, @@ -468,7 +467,6 @@ static const struct of_device_id max3191x_of_id[] = { { } }; MODULE_DEVICE_TABLE(of, max3191x_of_id); -#endif static const struct spi_device_id max3191x_spi_id[] = { { "max31910" }, @@ -484,7 +482,7 @@ MODULE_DEVICE_TABLE(spi, max3191x_spi_id); static struct spi_driver max3191x_driver = { .driver = { .name = "max3191x", - .of_match_table = of_match_ptr(max3191x_of_id), + .of_match_table = max3191x_of_id, }, .probe = max3191x_probe, .remove = max3191x_remove, From 30531e14c9490f08392001f91d6a6baeae1cbacf Mon Sep 17 00:00:00 2001 From: Zhu Wang Date: Wed, 2 Aug 2023 11:48:55 +0800 Subject: [PATCH 64/94] gpio: raspberrypi-exp: remove redundant of_match_ptr() The driver depends on CONFIG_OF, so it is not necessary to use of_match_ptr() here, and __maybe_unused can also be removed. Even for drivers that do not depend on CONFIG_OF, it's almost always better to leave out the of_match_ptr(), since the only thing it can possibly do is to save a few bytes of .text if a driver can be used both with and without it. Signed-off-by: Zhu Wang Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-raspberrypi-exp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-raspberrypi-exp.c b/drivers/gpio/gpio-raspberrypi-exp.c index ecb0d3800dfe..9d1b95e429f1 100644 --- a/drivers/gpio/gpio-raspberrypi-exp.c +++ b/drivers/gpio/gpio-raspberrypi-exp.c @@ -234,7 +234,7 @@ static int rpi_exp_gpio_probe(struct platform_device *pdev) return devm_gpiochip_add_data(dev, &rpi_gpio->gc, rpi_gpio); } -static const struct of_device_id rpi_exp_gpio_ids[] __maybe_unused = { +static const struct of_device_id rpi_exp_gpio_ids[] = { { .compatible = "raspberrypi,firmware-gpio" }, { } }; @@ -243,7 +243,7 @@ MODULE_DEVICE_TABLE(of, rpi_exp_gpio_ids); static struct platform_driver rpi_exp_gpio_driver = { .driver = { .name = MODULE_NAME, - .of_match_table = of_match_ptr(rpi_exp_gpio_ids), + .of_match_table = rpi_exp_gpio_ids, }, .probe = rpi_exp_gpio_probe, }; From 87d0688483f56c748b37a5298bdc382df5cf8f74 Mon Sep 17 00:00:00 2001 From: Zhu Wang Date: Thu, 3 Aug 2023 10:13:17 +0800 Subject: [PATCH 65/94] gpio: xra1403: remove redundant of_match_ptr() The driver depends on CONFIG_OF, so it is not necessary to use of_match_ptr() here, and __maybe_unused can also be removed. Even for drivers that do not depend on CONFIG_OF, it's almost always better to leave out the of_match_ptr(), since the only thing it can possibly do is to save a few bytes of .text if a driver can be used both with and without it. Signed-off-by: Zhu Wang Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xra1403.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-xra1403.c b/drivers/gpio/gpio-xra1403.c index eed8a1684830..dc2710c21c50 100644 --- a/drivers/gpio/gpio-xra1403.c +++ b/drivers/gpio/gpio-xra1403.c @@ -194,7 +194,7 @@ static const struct spi_device_id xra1403_ids[] = { }; MODULE_DEVICE_TABLE(spi, xra1403_ids); -static const struct of_device_id xra1403_spi_of_match[] __maybe_unused = { +static const struct of_device_id xra1403_spi_of_match[] = { { .compatible = "exar,xra1403" }, {}, }; @@ -205,7 +205,7 @@ static struct spi_driver xra1403_driver = { .id_table = xra1403_ids, .driver = { .name = "xra1403", - .of_match_table = of_match_ptr(xra1403_spi_of_match), + .of_match_table = xra1403_spi_of_match, }, }; From 39df52ddd932244f2cc08ce520ad064b7dc8c278 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Aug 2023 15:14:41 +0200 Subject: [PATCH 66/94] gpio: sim: use sysfs_streq() and avoid an strdup() When comparing strings passed to us from configfs, we can pass the page argument directly to sysfs_streq() and avoid manual string trimming. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko --- drivers/gpio/gpio-sim.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index cfbdade841e8..1a3729eb44eb 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -1272,7 +1272,6 @@ gpio_sim_hog_config_direction_store(struct config_item *item, { 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); @@ -1282,23 +1281,15 @@ gpio_sim_hog_config_direction_store(struct config_item *item, return -EBUSY; } - trimmed = gpio_sim_strdup_trimmed(page, count); - if (!trimmed) { - mutex_unlock(&dev->lock); - return -ENOMEM; - } - - if (strcmp(trimmed, "input") == 0) + if (sysfs_streq(page, "input")) dir = GPIOD_IN; - else if (strcmp(trimmed, "output-high") == 0) + else if (sysfs_streq(page, "output-high")) dir = GPIOD_OUT_HIGH; - else if (strcmp(trimmed, "output-low") == 0) + else if (sysfs_streq(page, "output-low")) dir = GPIOD_OUT_LOW; else dir = -EINVAL; - kfree(trimmed); - if (dir < 0) { mutex_unlock(&dev->lock); return dir; From db02247827ef2adc1617839b4bdcc5e1cef3e1ed Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 10 Aug 2023 18:00:38 -0400 Subject: [PATCH 67/94] gpio: idio-16: Migrate to the regmap API The regmap API supports IO port accessors so we can take advantage of regmap abstractions rather than handling access to the device registers directly in the driver. By leveraging the regmap API, the idio-16 library is reduced to simply a devm_idio_16_regmap_register() function and a configuration structure struct idio_16_regmap_config. Legacy functions and code will be removed once all consumers have migrated to the new idio-16 library interface. For IDIO-16 devices we have the following IRQ registers: Base Address +1 (Write): Clear Interrupt Base Address +2 (Read): Enable Interrupt Base Address +2 (Write): Disable Interrupt An interrupt is asserted whenever a change-of-state is detected on any of the inputs. Any write to 0x2 will disable interrupts, while any read will enable interrupts. Interrupts are cleared by a write to 0x1. For 104-IDIO-16 devices, there is no IRQ status register, so software has to assume that if an interrupt is raised then it was for the 104-IDIO-16 device. For PCI-IDIO-16 devices, there is an additional IRQ register: Base Address +6 (Read): Interrupt Status Interrupt status can be read from 0x6 where bit 2 set indicates that an IRQ has been generated. Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/b45081958ab53dfa697f4a8b15f1bfba46718068.1680618405.git.william.gray@linaro.org/ Signed-off-by: William Breathitt Gray Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 3 + drivers/gpio/gpio-idio-16.c | 155 ++++++++++++++++++++++++++++++++++++ drivers/gpio/gpio-idio-16.h | 26 ++++++ 3 files changed, 184 insertions(+) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c5324e44b74e..7f5cc69fff23 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -111,6 +111,9 @@ config GPIO_MAX730X config GPIO_IDIO_16 tristate + select REGMAP_IRQ + select GPIOLIB_IRQCHIP + select GPIO_REGMAP help Enables support for the idio-16 library functions. The idio-16 library provides functions to facilitate communication with devices within the diff --git a/drivers/gpio/gpio-idio-16.c b/drivers/gpio/gpio-idio-16.c index 13315242d220..f9349e8d7fdc 100644 --- a/drivers/gpio/gpio-idio-16.c +++ b/drivers/gpio/gpio-idio-16.c @@ -4,9 +4,13 @@ * Copyright (C) 2022 William Breathitt Gray */ #include +#include +#include #include +#include #include #include +#include #include #include @@ -14,6 +18,157 @@ #define DEFAULT_SYMBOL_NAMESPACE GPIO_IDIO_16 +#define IDIO_16_DAT_BASE 0x0 +#define IDIO_16_OUT_BASE IDIO_16_DAT_BASE +#define IDIO_16_IN_BASE (IDIO_16_DAT_BASE + 1) +#define IDIO_16_CLEAR_INTERRUPT 0x1 +#define IDIO_16_ENABLE_IRQ 0x2 +#define IDIO_16_DEACTIVATE_INPUT_FILTERS 0x3 +#define IDIO_16_DISABLE_IRQ IDIO_16_ENABLE_IRQ +#define IDIO_16_INTERRUPT_STATUS 0x6 + +#define IDIO_16_NGPIO 32 +#define IDIO_16_NGPIO_PER_REG 8 +#define IDIO_16_REG_STRIDE 4 + +struct idio_16_data { + struct regmap *map; + unsigned int irq_mask; +}; + +static int idio_16_handle_mask_sync(const int index, const unsigned int mask_buf_def, + const unsigned int mask_buf, void *const irq_drv_data) +{ + struct idio_16_data *const data = irq_drv_data; + const unsigned int prev_mask = data->irq_mask; + int err; + unsigned int val; + + /* exit early if no change since the previous mask */ + if (mask_buf == prev_mask) + return 0; + + /* remember the current mask for the next mask sync */ + data->irq_mask = mask_buf; + + /* if all previously masked, enable interrupts when unmasking */ + if (prev_mask == mask_buf_def) { + err = regmap_write(data->map, IDIO_16_CLEAR_INTERRUPT, 0x00); + if (err) + return err; + return regmap_read(data->map, IDIO_16_ENABLE_IRQ, &val); + } + + /* if all are currently masked, disable interrupts */ + if (mask_buf == mask_buf_def) + return regmap_write(data->map, IDIO_16_DISABLE_IRQ, 0x00); + + return 0; +} + +static int idio_16_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned int base, + const unsigned int offset, unsigned int *const reg, + unsigned int *const mask) +{ + unsigned int stride; + + /* Input lines start at GPIO 16 */ + if (offset < 16) { + stride = offset / IDIO_16_NGPIO_PER_REG; + *reg = IDIO_16_OUT_BASE + stride * IDIO_16_REG_STRIDE; + } else { + stride = (offset - 16) / IDIO_16_NGPIO_PER_REG; + *reg = IDIO_16_IN_BASE + stride * IDIO_16_REG_STRIDE; + } + + *mask = BIT(offset % IDIO_16_NGPIO_PER_REG); + + return 0; +} + +static const char *idio_16_names[IDIO_16_NGPIO] = { + "OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7", + "OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15", + "IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7", + "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15", +}; + +/** + * devm_idio_16_regmap_register - Register an IDIO-16 GPIO device + * @dev: device that is registering this IDIO-16 GPIO device + * @config: configuration for idio_16_regmap_config + * + * Registers an IDIO-16 GPIO device. Returns 0 on success and negative error number on failure. + */ +int devm_idio_16_regmap_register(struct device *const dev, + const struct idio_16_regmap_config *const config) +{ + struct gpio_regmap_config gpio_config = {}; + int err; + struct idio_16_data *data; + struct regmap_irq_chip *chip; + struct regmap_irq_chip_data *chip_data; + + if (!config->parent) + return -EINVAL; + + if (!config->map) + return -EINVAL; + + if (!config->regmap_irqs) + return -EINVAL; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + data->map = config->map; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->name = dev_name(dev); + chip->status_base = IDIO_16_INTERRUPT_STATUS; + chip->mask_base = IDIO_16_ENABLE_IRQ; + chip->ack_base = IDIO_16_CLEAR_INTERRUPT; + chip->no_status = config->no_status; + chip->num_regs = 1; + chip->irqs = config->regmap_irqs; + chip->num_irqs = config->num_regmap_irqs; + chip->handle_mask_sync = idio_16_handle_mask_sync; + chip->irq_drv_data = data; + + /* Disable IRQ to prevent spurious interrupts before we're ready */ + err = regmap_write(data->map, IDIO_16_DISABLE_IRQ, 0x00); + if (err) + return err; + + err = devm_regmap_add_irq_chip(dev, data->map, config->irq, 0, 0, chip, &chip_data); + if (err) + return dev_err_probe(dev, err, "IRQ registration failed\n"); + + if (config->filters) { + /* Deactivate input filters */ + err = regmap_write(data->map, IDIO_16_DEACTIVATE_INPUT_FILTERS, 0x00); + if (err) + return err; + } + + gpio_config.parent = config->parent; + gpio_config.regmap = data->map; + gpio_config.ngpio = IDIO_16_NGPIO; + gpio_config.names = idio_16_names; + gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(IDIO_16_DAT_BASE); + gpio_config.reg_set_base = GPIO_REGMAP_ADDR(IDIO_16_DAT_BASE); + gpio_config.ngpio_per_reg = IDIO_16_NGPIO_PER_REG; + gpio_config.reg_stride = IDIO_16_REG_STRIDE; + gpio_config.irq_domain = regmap_irq_get_domain(chip_data); + gpio_config.reg_mask_xlate = idio_16_reg_mask_xlate; + + return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config)); +} +EXPORT_SYMBOL_GPL(devm_idio_16_regmap_register); + /** * idio_16_get - get signal value at signal offset * @reg: ACCES IDIO-16 device registers diff --git a/drivers/gpio/gpio-idio-16.h b/drivers/gpio/gpio-idio-16.h index 928f8251a2bd..255bd8504ed7 100644 --- a/drivers/gpio/gpio-idio-16.h +++ b/drivers/gpio/gpio-idio-16.h @@ -6,6 +6,30 @@ #include #include +struct device; +struct regmap; +struct regmap_irq; + +/** + * struct idio_16_regmap_config - Configuration for the IDIO-16 register map + * @parent: parent device + * @map: regmap for the IDIO-16 device + * @regmap_irqs: descriptors for individual IRQs + * @num_regmap_irqs: number of IRQ descriptors + * @irq: IRQ number for the IDIO-16 device + * @no_status: device has no status register + * @filters: device has input filters + */ +struct idio_16_regmap_config { + struct device *parent; + struct regmap *map; + const struct regmap_irq *regmap_irqs; + int num_regmap_irqs; + unsigned int irq; + bool no_status; + bool filters; +}; + /** * struct idio_16 - IDIO-16 registers structure * @out0_7: Read: FET Drive Outputs 0-7 @@ -68,4 +92,6 @@ void idio_16_set_multiple(struct idio_16 __iomem *reg, const unsigned long *mask, const unsigned long *bits); void idio_16_state_init(struct idio_16_state *state); +int devm_idio_16_regmap_register(struct device *dev, const struct idio_16_regmap_config *config); + #endif /* _IDIO_16_H_ */ From 2c210c9a34a31076e03afee3eae7a748e56a75e9 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 10 Aug 2023 18:00:39 -0400 Subject: [PATCH 68/94] gpio: 104-idio-16: Migrate to the regmap API The regmap API supports IO port accessors so we can take advantage of regmap abstractions rather than handling access to the device registers directly in the driver. Migrate the 104-idio-16 module to the new idio-16 library interface leveraging the gpio-regmap API. Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/1f24a1f18c9a9daa4983713e0a5b53e838d624a8.1680618405.git.william.gray@linaro.org/ Signed-off-by: William Breathitt Gray Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- drivers/gpio/gpio-104-idio-16.c | 286 +++++++------------------------- 2 files changed, 64 insertions(+), 224 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 7f5cc69fff23..8bc60a6ce3a9 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -872,7 +872,7 @@ config GPIO_104_IDIO_16 tristate "ACCES 104-IDIO-16 GPIO support" depends on PC104 select ISA_BUS_API - select GPIOLIB_IRQCHIP + select REGMAP_MMIO select GPIO_IDIO_16 help Enables GPIO support for the ACCES 104-IDIO-16 family (104-IDIO-16, diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c index 098fbefdbe22..f03ccd0f534c 100644 --- a/drivers/gpio/gpio-104-idio-16.c +++ b/drivers/gpio/gpio-104-idio-16.c @@ -6,19 +6,16 @@ * This driver supports the following ACCES devices: 104-IDIO-16, * 104-IDIO-16E, 104-IDO-16, 104-IDIO-8, 104-IDIO-8E, and 104-IDO-8. */ -#include +#include #include -#include -#include -#include +#include #include -#include -#include +#include #include #include #include #include -#include +#include #include #include "gpio-idio-16.h" @@ -36,187 +33,62 @@ static unsigned int num_irq; module_param_hw_array(irq, uint, irq, &num_irq, 0); MODULE_PARM_DESC(irq, "ACCES 104-IDIO-16 interrupt line numbers"); -/** - * struct idio_16_gpio - GPIO device private data structure - * @chip: instance of the gpio_chip - * @lock: synchronization lock to prevent I/O race conditions - * @irq_mask: I/O bits affected by interrupts - * @reg: I/O address offset for the device registers - * @state: ACCES IDIO-16 device state - */ -struct idio_16_gpio { - struct gpio_chip chip; - raw_spinlock_t lock; - unsigned long irq_mask; - struct idio_16 __iomem *reg; - struct idio_16_state state; +static const struct regmap_range idio_16_wr_ranges[] = { + regmap_reg_range(0x0, 0x2), regmap_reg_range(0x4, 0x4), +}; +static const struct regmap_range idio_16_rd_ranges[] = { + regmap_reg_range(0x1, 0x2), regmap_reg_range(0x5, 0x5), +}; +static const struct regmap_range idio_16_precious_ranges[] = { + regmap_reg_range(0x2, 0x2), +}; +static const struct regmap_access_table idio_16_wr_table = { + .yes_ranges = idio_16_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(idio_16_wr_ranges), +}; +static const struct regmap_access_table idio_16_rd_table = { + .yes_ranges = idio_16_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(idio_16_rd_ranges), +}; +static const struct regmap_access_table idio_16_precious_table = { + .yes_ranges = idio_16_precious_ranges, + .n_yes_ranges = ARRAY_SIZE(idio_16_precious_ranges), +}; +static const struct regmap_config idio_16_regmap_config = { + .reg_bits = 8, + .reg_stride = 1, + .val_bits = 8, + .io_port = true, + .wr_table = &idio_16_wr_table, + .rd_table = &idio_16_rd_table, + .volatile_table = &idio_16_rd_table, + .precious_table = &idio_16_precious_table, + .cache_type = REGCACHE_FLAT, + .use_raw_spinlock = true, }; -static int idio_16_gpio_get_direction(struct gpio_chip *chip, - unsigned int offset) -{ - if (idio_16_get_direction(offset)) - return GPIO_LINE_DIRECTION_IN; - - return GPIO_LINE_DIRECTION_OUT; -} - -static int idio_16_gpio_direction_input(struct gpio_chip *chip, - unsigned int offset) -{ - return 0; -} - -static int idio_16_gpio_direction_output(struct gpio_chip *chip, - unsigned int offset, int value) -{ - chip->set(chip, offset, value); - return 0; -} - -static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset) -{ - struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); - - return idio_16_get(idio16gpio->reg, &idio16gpio->state, offset); -} - -static int idio_16_gpio_get_multiple(struct gpio_chip *chip, - unsigned long *mask, unsigned long *bits) -{ - struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); - - idio_16_get_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits); - - return 0; -} - -static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) -{ - struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); - - idio_16_set(idio16gpio->reg, &idio16gpio->state, offset, value); -} - -static void idio_16_gpio_set_multiple(struct gpio_chip *chip, - unsigned long *mask, unsigned long *bits) -{ - struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); - - idio_16_set_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits); -} - -static void idio_16_irq_ack(struct irq_data *data) -{ -} - -static void idio_16_irq_mask(struct irq_data *data) -{ - struct gpio_chip *chip = irq_data_get_irq_chip_data(data); - struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); - const unsigned long offset = irqd_to_hwirq(data); - unsigned long flags; - - idio16gpio->irq_mask &= ~BIT(offset); - gpiochip_disable_irq(chip, offset); - - if (!idio16gpio->irq_mask) { - raw_spin_lock_irqsave(&idio16gpio->lock, flags); - - iowrite8(0, &idio16gpio->reg->irq_ctl); - - raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); +/* Only input lines (GPIO 16-31) support interrupts */ +#define IDIO_16_REGMAP_IRQ(_id) \ + [16 + _id] = { \ + .mask = BIT(_id), \ + .type = { .types_supported = IRQ_TYPE_EDGE_BOTH }, \ } -} -static void idio_16_irq_unmask(struct irq_data *data) -{ - struct gpio_chip *chip = irq_data_get_irq_chip_data(data); - struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); - const unsigned long offset = irqd_to_hwirq(data); - const unsigned long prev_irq_mask = idio16gpio->irq_mask; - unsigned long flags; - - gpiochip_enable_irq(chip, offset); - idio16gpio->irq_mask |= BIT(offset); - - if (!prev_irq_mask) { - raw_spin_lock_irqsave(&idio16gpio->lock, flags); - - ioread8(&idio16gpio->reg->irq_ctl); - - raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); - } -} - -static int idio_16_irq_set_type(struct irq_data *data, unsigned int flow_type) -{ - /* The only valid irq types are none and both-edges */ - if (flow_type != IRQ_TYPE_NONE && - (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH) - return -EINVAL; - - return 0; -} - -static const struct irq_chip idio_16_irqchip = { - .name = "104-idio-16", - .irq_ack = idio_16_irq_ack, - .irq_mask = idio_16_irq_mask, - .irq_unmask = idio_16_irq_unmask, - .irq_set_type = idio_16_irq_set_type, - .flags = IRQCHIP_IMMUTABLE, - GPIOCHIP_IRQ_RESOURCE_HELPERS, +static const struct regmap_irq idio_16_regmap_irqs[] = { + IDIO_16_REGMAP_IRQ(0), IDIO_16_REGMAP_IRQ(1), IDIO_16_REGMAP_IRQ(2), /* 0-2 */ + IDIO_16_REGMAP_IRQ(3), IDIO_16_REGMAP_IRQ(4), IDIO_16_REGMAP_IRQ(5), /* 3-5 */ + IDIO_16_REGMAP_IRQ(6), IDIO_16_REGMAP_IRQ(7), IDIO_16_REGMAP_IRQ(8), /* 6-8 */ + IDIO_16_REGMAP_IRQ(9), IDIO_16_REGMAP_IRQ(10), IDIO_16_REGMAP_IRQ(11), /* 9-11 */ + IDIO_16_REGMAP_IRQ(12), IDIO_16_REGMAP_IRQ(13), IDIO_16_REGMAP_IRQ(14), /* 12-14 */ + IDIO_16_REGMAP_IRQ(15), /* 15 */ }; -static irqreturn_t idio_16_irq_handler(int irq, void *dev_id) -{ - struct idio_16_gpio *const idio16gpio = dev_id; - struct gpio_chip *const chip = &idio16gpio->chip; - int gpio; - - for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio) - generic_handle_domain_irq(chip->irq.domain, gpio); - - raw_spin_lock(&idio16gpio->lock); - - iowrite8(0, &idio16gpio->reg->in0_7); - - raw_spin_unlock(&idio16gpio->lock); - - return IRQ_HANDLED; -} - -#define IDIO_16_NGPIO 32 -static const char *idio_16_names[IDIO_16_NGPIO] = { - "OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7", - "OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15", - "IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7", - "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15" -}; - -static int idio_16_irq_init_hw(struct gpio_chip *gc) -{ - struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc); - - /* Disable IRQ by default */ - iowrite8(0, &idio16gpio->reg->irq_ctl); - iowrite8(0, &idio16gpio->reg->in0_7); - - return 0; -} - static int idio_16_probe(struct device *dev, unsigned int id) { - struct idio_16_gpio *idio16gpio; const char *const name = dev_name(dev); - struct gpio_irq_chip *girq; - int err; - - idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL); - if (!idio16gpio) - return -ENOMEM; + struct idio_16_regmap_config config = {}; + void __iomem *regs; + struct regmap *map; if (!devm_request_region(dev, base[id], IDIO_16_EXTENT, name)) { dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", @@ -224,54 +96,22 @@ static int idio_16_probe(struct device *dev, unsigned int id) return -EBUSY; } - idio16gpio->reg = devm_ioport_map(dev, base[id], IDIO_16_EXTENT); - if (!idio16gpio->reg) + regs = devm_ioport_map(dev, base[id], IDIO_16_EXTENT); + if (!regs) return -ENOMEM; - idio16gpio->chip.label = name; - idio16gpio->chip.parent = dev; - idio16gpio->chip.owner = THIS_MODULE; - idio16gpio->chip.base = -1; - idio16gpio->chip.ngpio = IDIO_16_NGPIO; - idio16gpio->chip.names = idio_16_names; - idio16gpio->chip.get_direction = idio_16_gpio_get_direction; - idio16gpio->chip.direction_input = idio_16_gpio_direction_input; - idio16gpio->chip.direction_output = idio_16_gpio_direction_output; - idio16gpio->chip.get = idio_16_gpio_get; - idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple; - idio16gpio->chip.set = idio_16_gpio_set; - idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple; + map = devm_regmap_init_mmio(dev, regs, &idio_16_regmap_config); + if (IS_ERR(map)) + return dev_err_probe(dev, PTR_ERR(map), "Unable to initialize register map\n"); - idio_16_state_init(&idio16gpio->state); - /* FET off states are represented by bit values of "1" */ - bitmap_fill(idio16gpio->state.out_state, IDIO_16_NOUT); + config.parent = dev; + config.map = map; + config.regmap_irqs = idio_16_regmap_irqs; + config.num_regmap_irqs = ARRAY_SIZE(idio_16_regmap_irqs); + config.irq = irq[id]; + config.no_status = true; - girq = &idio16gpio->chip.irq; - gpio_irq_chip_set_chip(girq, &idio_16_irqchip); - /* This will let us handle the parent IRQ in the driver */ - girq->parent_handler = NULL; - girq->num_parents = 0; - girq->parents = NULL; - girq->default_type = IRQ_TYPE_NONE; - girq->handler = handle_edge_irq; - girq->init_hw = idio_16_irq_init_hw; - - raw_spin_lock_init(&idio16gpio->lock); - - err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio); - if (err) { - dev_err(dev, "GPIO registering failed (%d)\n", err); - return err; - } - - err = devm_request_irq(dev, irq[id], idio_16_irq_handler, 0, name, - idio16gpio); - if (err) { - dev_err(dev, "IRQ handler registering failed (%d)\n", err); - return err; - } - - return 0; + return devm_idio_16_regmap_register(dev, &config); } static struct isa_driver idio_16_driver = { From 73d8f3efc5c2b757ab06685741df01eaed8090c4 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 10 Aug 2023 18:00:40 -0400 Subject: [PATCH 69/94] gpio: pci-idio-16: Migrate to the regmap API The regmap API supports IO port accessors so we can take advantage of regmap abstractions rather than handling access to the device registers directly in the driver. Migrate the pci-idio-16 module to the new idio-16 library interface leveraging the gpio-regmap API. Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/5ba5405c64aca984d5cf3bdbdffa04c325e5a147.1680618405.git.william.gray@linaro.org/ Signed-off-by: William Breathitt Gray Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- drivers/gpio/gpio-pci-idio-16.c | 294 +++++++------------------------- 2 files changed, 62 insertions(+), 234 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8bc60a6ce3a9..864b221252b2 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1655,7 +1655,7 @@ config GPIO_PCH config GPIO_PCI_IDIO_16 tristate "ACCES PCI-IDIO-16 GPIO support" - select GPIOLIB_IRQCHIP + select REGMAP_MMIO select GPIO_IDIO_16 help Enables GPIO support for the ACCES PCI-IDIO-16. An interrupt is diff --git a/drivers/gpio/gpio-pci-idio-16.c b/drivers/gpio/gpio-pci-idio-16.c index 6726c32e31e6..44c0a21b1d1d 100644 --- a/drivers/gpio/gpio-pci-idio-16.c +++ b/drivers/gpio/gpio-pci-idio-16.c @@ -5,214 +5,75 @@ */ #include #include -#include -#include -#include -#include +#include +#include #include #include #include -#include +#include #include #include "gpio-idio-16.h" -/** - * struct idio_16_gpio - GPIO device private data structure - * @chip: instance of the gpio_chip - * @lock: synchronization lock to prevent I/O race conditions - * @reg: I/O address offset for the GPIO device registers - * @state: ACCES IDIO-16 device state - * @irq_mask: I/O bits affected by interrupts - */ -struct idio_16_gpio { - struct gpio_chip chip; - raw_spinlock_t lock; - struct idio_16 __iomem *reg; - struct idio_16_state state; - unsigned long irq_mask; +static const struct regmap_range idio_16_wr_ranges[] = { + regmap_reg_range(0x0, 0x2), regmap_reg_range(0x3, 0x4), +}; +static const struct regmap_range idio_16_rd_ranges[] = { + regmap_reg_range(0x1, 0x2), regmap_reg_range(0x5, 0x6), +}; +static const struct regmap_range idio_16_precious_ranges[] = { + regmap_reg_range(0x2, 0x2), +}; +static const struct regmap_access_table idio_16_wr_table = { + .yes_ranges = idio_16_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(idio_16_wr_ranges), +}; +static const struct regmap_access_table idio_16_rd_table = { + .yes_ranges = idio_16_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(idio_16_rd_ranges), +}; +static const struct regmap_access_table idio_16_precious_table = { + .yes_ranges = idio_16_precious_ranges, + .n_yes_ranges = ARRAY_SIZE(idio_16_precious_ranges), +}; +static const struct regmap_config idio_16_regmap_config = { + .reg_bits = 8, + .reg_stride = 1, + .val_bits = 8, + .io_port = true, + .wr_table = &idio_16_wr_table, + .rd_table = &idio_16_rd_table, + .volatile_table = &idio_16_rd_table, + .precious_table = &idio_16_precious_table, + .cache_type = REGCACHE_FLAT, + .use_raw_spinlock = true, }; -static int idio_16_gpio_get_direction(struct gpio_chip *chip, - unsigned int offset) -{ - if (idio_16_get_direction(offset)) - return GPIO_LINE_DIRECTION_IN; - - return GPIO_LINE_DIRECTION_OUT; -} - -static int idio_16_gpio_direction_input(struct gpio_chip *chip, - unsigned int offset) -{ - return 0; -} - -static int idio_16_gpio_direction_output(struct gpio_chip *chip, - unsigned int offset, int value) -{ - chip->set(chip, offset, value); - return 0; -} - -static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset) -{ - struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); - - return idio_16_get(idio16gpio->reg, &idio16gpio->state, offset); -} - -static int idio_16_gpio_get_multiple(struct gpio_chip *chip, - unsigned long *mask, unsigned long *bits) -{ - struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); - - idio_16_get_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits); - return 0; -} - -static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) -{ - struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); - - idio_16_set(idio16gpio->reg, &idio16gpio->state, offset, value); -} - -static void idio_16_gpio_set_multiple(struct gpio_chip *chip, - unsigned long *mask, unsigned long *bits) -{ - struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); - - idio_16_set_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits); -} - -static void idio_16_irq_ack(struct irq_data *data) -{ -} - -static void idio_16_irq_mask(struct irq_data *data) -{ - struct gpio_chip *chip = irq_data_get_irq_chip_data(data); - struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); - const unsigned long mask = BIT(irqd_to_hwirq(data)); - unsigned long flags; - - idio16gpio->irq_mask &= ~mask; - - if (!idio16gpio->irq_mask) { - raw_spin_lock_irqsave(&idio16gpio->lock, flags); - - iowrite8(0, &idio16gpio->reg->irq_ctl); - - raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); +/* Only input lines (GPIO 16-31) support interrupts */ +#define IDIO_16_REGMAP_IRQ(_id) \ + [16 + _id] = { \ + .mask = BIT(2), \ + .type = { .types_supported = IRQ_TYPE_EDGE_BOTH }, \ } - gpiochip_disable_irq(chip, irqd_to_hwirq(data)); -} - -static void idio_16_irq_unmask(struct irq_data *data) -{ - struct gpio_chip *chip = irq_data_get_irq_chip_data(data); - struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); - const unsigned long mask = BIT(irqd_to_hwirq(data)); - const unsigned long prev_irq_mask = idio16gpio->irq_mask; - unsigned long flags; - - gpiochip_enable_irq(chip, irqd_to_hwirq(data)); - - idio16gpio->irq_mask |= mask; - - if (!prev_irq_mask) { - raw_spin_lock_irqsave(&idio16gpio->lock, flags); - - ioread8(&idio16gpio->reg->irq_ctl); - - raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); - } -} - -static int idio_16_irq_set_type(struct irq_data *data, unsigned int flow_type) -{ - /* The only valid irq types are none and both-edges */ - if (flow_type != IRQ_TYPE_NONE && - (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH) - return -EINVAL; - - return 0; -} - -static const struct irq_chip idio_16_irqchip = { - .name = "pci-idio-16", - .irq_ack = idio_16_irq_ack, - .irq_mask = idio_16_irq_mask, - .irq_unmask = idio_16_irq_unmask, - .irq_set_type = idio_16_irq_set_type, - .flags = IRQCHIP_IMMUTABLE, - GPIOCHIP_IRQ_RESOURCE_HELPERS, +static const struct regmap_irq idio_16_regmap_irqs[] = { + IDIO_16_REGMAP_IRQ(0), IDIO_16_REGMAP_IRQ(1), IDIO_16_REGMAP_IRQ(2), /* 0-2 */ + IDIO_16_REGMAP_IRQ(3), IDIO_16_REGMAP_IRQ(4), IDIO_16_REGMAP_IRQ(5), /* 3-5 */ + IDIO_16_REGMAP_IRQ(6), IDIO_16_REGMAP_IRQ(7), IDIO_16_REGMAP_IRQ(8), /* 6-8 */ + IDIO_16_REGMAP_IRQ(9), IDIO_16_REGMAP_IRQ(10), IDIO_16_REGMAP_IRQ(11), /* 9-11 */ + IDIO_16_REGMAP_IRQ(12), IDIO_16_REGMAP_IRQ(13), IDIO_16_REGMAP_IRQ(14), /* 12-14 */ + IDIO_16_REGMAP_IRQ(15), /* 15 */ }; -static irqreturn_t idio_16_irq_handler(int irq, void *dev_id) -{ - struct idio_16_gpio *const idio16gpio = dev_id; - unsigned int irq_status; - struct gpio_chip *const chip = &idio16gpio->chip; - int gpio; - - raw_spin_lock(&idio16gpio->lock); - - irq_status = ioread8(&idio16gpio->reg->irq_status); - - raw_spin_unlock(&idio16gpio->lock); - - /* Make sure our device generated IRQ */ - if (!(irq_status & 0x3) || !(irq_status & 0x4)) - return IRQ_NONE; - - for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio) - generic_handle_domain_irq(chip->irq.domain, gpio); - - raw_spin_lock(&idio16gpio->lock); - - /* Clear interrupt */ - iowrite8(0, &idio16gpio->reg->in0_7); - - raw_spin_unlock(&idio16gpio->lock); - - return IRQ_HANDLED; -} - -#define IDIO_16_NGPIO 32 -static const char *idio_16_names[IDIO_16_NGPIO] = { - "OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7", - "OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15", - "IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7", - "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15" -}; - -static int idio_16_irq_init_hw(struct gpio_chip *gc) -{ - struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc); - - /* Disable IRQ by default and clear any pending interrupt */ - iowrite8(0, &idio16gpio->reg->irq_ctl); - iowrite8(0, &idio16gpio->reg->in0_7); - - return 0; -} - static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct device *const dev = &pdev->dev; - struct idio_16_gpio *idio16gpio; int err; const size_t pci_bar_index = 2; const char *const name = pci_name(pdev); - struct gpio_irq_chip *girq; - - idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL); - if (!idio16gpio) - return -ENOMEM; + struct idio_16_regmap_config config = {}; + void __iomem *regs; + struct regmap *map; err = pcim_enable_device(pdev); if (err) { @@ -226,53 +87,20 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id) return err; } - idio16gpio->reg = pcim_iomap_table(pdev)[pci_bar_index]; + regs = pcim_iomap_table(pdev)[pci_bar_index]; - /* Deactivate input filters */ - iowrite8(0, &idio16gpio->reg->filter_ctl); + map = devm_regmap_init_mmio(dev, regs, &idio_16_regmap_config); + if (IS_ERR(map)) + return dev_err_probe(dev, PTR_ERR(map), "Unable to initialize register map\n"); - idio16gpio->chip.label = name; - idio16gpio->chip.parent = dev; - idio16gpio->chip.owner = THIS_MODULE; - idio16gpio->chip.base = -1; - idio16gpio->chip.ngpio = IDIO_16_NGPIO; - idio16gpio->chip.names = idio_16_names; - idio16gpio->chip.get_direction = idio_16_gpio_get_direction; - idio16gpio->chip.direction_input = idio_16_gpio_direction_input; - idio16gpio->chip.direction_output = idio_16_gpio_direction_output; - idio16gpio->chip.get = idio_16_gpio_get; - idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple; - idio16gpio->chip.set = idio_16_gpio_set; - idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple; + config.parent = dev; + config.map = map; + config.regmap_irqs = idio_16_regmap_irqs; + config.num_regmap_irqs = ARRAY_SIZE(idio_16_regmap_irqs); + config.irq = pdev->irq; + config.filters = true; - idio_16_state_init(&idio16gpio->state); - - girq = &idio16gpio->chip.irq; - gpio_irq_chip_set_chip(girq, &idio_16_irqchip); - /* This will let us handle the parent IRQ in the driver */ - girq->parent_handler = NULL; - girq->num_parents = 0; - girq->parents = NULL; - girq->default_type = IRQ_TYPE_NONE; - girq->handler = handle_edge_irq; - girq->init_hw = idio_16_irq_init_hw; - - raw_spin_lock_init(&idio16gpio->lock); - - err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio); - if (err) { - dev_err(dev, "GPIO registering failed (%d)\n", err); - return err; - } - - err = devm_request_irq(dev, pdev->irq, idio_16_irq_handler, IRQF_SHARED, - name, idio16gpio); - if (err) { - dev_err(dev, "IRQ handler registering failed (%d)\n", err); - return err; - } - - return 0; + return devm_idio_16_regmap_register(dev, &config); } static const struct pci_device_id idio_16_pci_dev_id[] = { From 98aaff7c4e65a448545cd46d93e06cb8042f4409 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 10 Aug 2023 18:00:41 -0400 Subject: [PATCH 70/94] gpio: idio-16: Remove unused legacy interface All idio-16 library consumers have migrated to the new interface leveraging the gpio-regmap API. Legacy interface functions and code are removed as no longer needed. Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/651cff1cc3eb57b455a8048121cf6a4d4367f018.1680618405.git.william.gray@linaro.org/ Signed-off-by: William Breathitt Gray Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-idio-16.c | 131 +----------------------------------- drivers/gpio/gpio-idio-16.h | 65 ------------------ 2 files changed, 1 insertion(+), 195 deletions(-) diff --git a/drivers/gpio/gpio-idio-16.c b/drivers/gpio/gpio-idio-16.c index f9349e8d7fdc..53b1eb876a12 100644 --- a/drivers/gpio/gpio-idio-16.c +++ b/drivers/gpio/gpio-idio-16.c @@ -3,15 +3,13 @@ * GPIO library for the ACCES IDIO-16 family * Copyright (C) 2022 William Breathitt Gray */ -#include +#include #include #include #include #include -#include #include #include -#include #include #include "gpio-idio-16.h" @@ -169,133 +167,6 @@ int devm_idio_16_regmap_register(struct device *const dev, } EXPORT_SYMBOL_GPL(devm_idio_16_regmap_register); -/** - * idio_16_get - get signal value at signal offset - * @reg: ACCES IDIO-16 device registers - * @state: ACCES IDIO-16 device state - * @offset: offset of signal to get - * - * Returns the signal value (0=low, 1=high) for the signal at @offset. - */ -int idio_16_get(struct idio_16 __iomem *const reg, - struct idio_16_state *const state, const unsigned long offset) -{ - const unsigned long mask = BIT(offset); - - if (offset < IDIO_16_NOUT) - return test_bit(offset, state->out_state); - - if (offset < 24) - return !!(ioread8(®->in0_7) & (mask >> IDIO_16_NOUT)); - - if (offset < 32) - return !!(ioread8(®->in8_15) & (mask >> 24)); - - return -EINVAL; -} -EXPORT_SYMBOL_GPL(idio_16_get); - -/** - * idio_16_get_multiple - get multiple signal values at multiple signal offsets - * @reg: ACCES IDIO-16 device registers - * @state: ACCES IDIO-16 device state - * @mask: mask of signals to get - * @bits: bitmap to store signal values - * - * Stores in @bits the values (0=low, 1=high) for the signals defined by @mask. - */ -void idio_16_get_multiple(struct idio_16 __iomem *const reg, - struct idio_16_state *const state, - const unsigned long *const mask, - unsigned long *const bits) -{ - unsigned long flags; - const unsigned long out_mask = GENMASK(IDIO_16_NOUT - 1, 0); - - spin_lock_irqsave(&state->lock, flags); - - bitmap_replace(bits, bits, state->out_state, &out_mask, IDIO_16_NOUT); - if (*mask & GENMASK(23, 16)) - bitmap_set_value8(bits, ioread8(®->in0_7), 16); - if (*mask & GENMASK(31, 24)) - bitmap_set_value8(bits, ioread8(®->in8_15), 24); - - spin_unlock_irqrestore(&state->lock, flags); -} -EXPORT_SYMBOL_GPL(idio_16_get_multiple); - -/** - * idio_16_set - set signal value at signal offset - * @reg: ACCES IDIO-16 device registers - * @state: ACCES IDIO-16 device state - * @offset: offset of signal to set - * @value: value of signal to set - * - * Assigns output @value for the signal at @offset. - */ -void idio_16_set(struct idio_16 __iomem *const reg, - struct idio_16_state *const state, const unsigned long offset, - const unsigned long value) -{ - unsigned long flags; - - if (offset >= IDIO_16_NOUT) - return; - - spin_lock_irqsave(&state->lock, flags); - - __assign_bit(offset, state->out_state, value); - if (offset < 8) - iowrite8(bitmap_get_value8(state->out_state, 0), ®->out0_7); - else - iowrite8(bitmap_get_value8(state->out_state, 8), ®->out8_15); - - spin_unlock_irqrestore(&state->lock, flags); -} -EXPORT_SYMBOL_GPL(idio_16_set); - -/** - * idio_16_set_multiple - set signal values at multiple signal offsets - * @reg: ACCES IDIO-16 device registers - * @state: ACCES IDIO-16 device state - * @mask: mask of signals to set - * @bits: bitmap of signal output values - * - * Assigns output values defined by @bits for the signals defined by @mask. - */ -void idio_16_set_multiple(struct idio_16 __iomem *const reg, - struct idio_16_state *const state, - const unsigned long *const mask, - const unsigned long *const bits) -{ - unsigned long flags; - - spin_lock_irqsave(&state->lock, flags); - - bitmap_replace(state->out_state, state->out_state, bits, mask, - IDIO_16_NOUT); - if (*mask & GENMASK(7, 0)) - iowrite8(bitmap_get_value8(state->out_state, 0), ®->out0_7); - if (*mask & GENMASK(15, 8)) - iowrite8(bitmap_get_value8(state->out_state, 8), ®->out8_15); - - spin_unlock_irqrestore(&state->lock, flags); -} -EXPORT_SYMBOL_GPL(idio_16_set_multiple); - -/** - * idio_16_state_init - initialize idio_16_state structure - * @state: ACCES IDIO-16 device state - * - * Initializes the ACCES IDIO-16 device @state for use in idio-16 library - * functions. - */ -void idio_16_state_init(struct idio_16_state *const state) -{ - spin_lock_init(&state->lock); -} -EXPORT_SYMBOL_GPL(idio_16_state_init); - MODULE_AUTHOR("William Breathitt Gray"); MODULE_DESCRIPTION("ACCES IDIO-16 GPIO Library"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-idio-16.h b/drivers/gpio/gpio-idio-16.h index 255bd8504ed7..93b08ad73065 100644 --- a/drivers/gpio/gpio-idio-16.h +++ b/drivers/gpio/gpio-idio-16.h @@ -3,9 +3,6 @@ #ifndef _IDIO_16_H_ #define _IDIO_16_H_ -#include -#include - struct device; struct regmap; struct regmap_irq; @@ -30,68 +27,6 @@ struct idio_16_regmap_config { bool filters; }; -/** - * struct idio_16 - IDIO-16 registers structure - * @out0_7: Read: FET Drive Outputs 0-7 - * Write: FET Drive Outputs 0-7 - * @in0_7: Read: Isolated Inputs 0-7 - * Write: Clear Interrupt - * @irq_ctl: Read: Enable IRQ - * Write: Disable IRQ - * @filter_ctl: Read: Activate Input Filters 0-15 - * Write: Deactivate Input Filters 0-15 - * @out8_15: Read: FET Drive Outputs 8-15 - * Write: FET Drive Outputs 8-15 - * @in8_15: Read: Isolated Inputs 8-15 - * Write: Unused - * @irq_status: Read: Interrupt status - * Write: Unused - */ -struct idio_16 { - u8 out0_7; - u8 in0_7; - u8 irq_ctl; - u8 filter_ctl; - u8 out8_15; - u8 in8_15; - u8 irq_status; -}; - -#define IDIO_16_NOUT 16 - -/** - * struct idio_16_state - IDIO-16 state structure - * @lock: synchronization lock for accessing device state - * @out_state: output signals state - */ -struct idio_16_state { - spinlock_t lock; - DECLARE_BITMAP(out_state, IDIO_16_NOUT); -}; - -/** - * idio_16_get_direction - get the I/O direction for a signal offset - * @offset: offset of signal to get direction - * - * Returns the signal direction (0=output, 1=input) for the signal at @offset. - */ -static inline int idio_16_get_direction(const unsigned long offset) -{ - return (offset >= IDIO_16_NOUT) ? 1 : 0; -} - -int idio_16_get(struct idio_16 __iomem *reg, struct idio_16_state *state, - unsigned long offset); -void idio_16_get_multiple(struct idio_16 __iomem *reg, - struct idio_16_state *state, - const unsigned long *mask, unsigned long *bits); -void idio_16_set(struct idio_16 __iomem *reg, struct idio_16_state *state, - unsigned long offset, unsigned long value); -void idio_16_set_multiple(struct idio_16 __iomem *reg, - struct idio_16_state *state, - const unsigned long *mask, const unsigned long *bits); -void idio_16_state_init(struct idio_16_state *state); - int devm_idio_16_regmap_register(struct device *dev, const struct idio_16_regmap_config *config); #endif /* _IDIO_16_H_ */ From 1a200a3966176b89aa038713a00367c9f57f1dff Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 10 Aug 2023 18:00:42 -0400 Subject: [PATCH 71/94] gpio: pcie-idio-24: Migrate to the regmap API The regmap API supports IO port accessors so we can take advantage of regmap abstractions rather than handling access to the device registers directly in the driver. For the PCIe-IDIO-24 series of devices, the following BARs are available: BAR[0]: memory mapped PEX8311 BAR[1]: I/O mapped PEX8311 BAR[2]: I/O mapped card registers There are 24 FET Output lines, 24 Isolated Input lines, and 8 TTL/CMOS lines (which may be configured for either output or input). The GPIO lines are exposed by the following card registers: Base +0x0-0x2 (Read/Write): FET Outputs Base +0xB (Read/Write): TTL/CMOS Base +0x4-0x6 (Read): Isolated Inputs Base +0x7 (Read): TTL/CMOS In order for the device to support interrupts, the PLX PEX8311 internal PCI wire interrupt and local interrupt input must first be enabled. The following card registers for Change-Of-State may be used: Base +0x8-0xA (Read): COS Status Inputs Base +0x8-0xA (Write): COS Clear Inputs Base +0xB (Read): COS Status TTL/CMOS Base +0xB (Write): COS Clear TTL/CMOS Base +0xE (Read/Write): COS Enable The COS Enable register is used to enable/disable interrupts and configure the interrupt levels; each bit maps to a group of eight inputs as described below: Bit 0: IRQ EN Rising Edge IN0-7 Bit 1: IRQ EN Rising Edge IN8-15 Bit 2: IRQ EN Rising Edge IN16-23 Bit 3: IRQ EN Rising Edge TTL0-7 Bit 4: IRQ EN Falling Edge IN0-7 Bit 5: IRQ EN Falling Edge IN8-15 Bit 6: IRQ EN Falling Edge IN16-23 Bit 7: IRQ EN Falling Edge TTL0-7 An interrupt is asserted when a change-of-state matching the interrupt level configuration respective for a particular group of eight inputs with enabled COS is detected. The COS Status registers may be read to determine which inputs have changed; if interrupts were enabled, an IRQ will be generated for the set bits in these registers. Writing the value read from the COS Status register back to the respective COS Clear register will clear just those interrupts. Reviewed-by: Michael Walle Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/3091e387b1d2eac011a1d84e493663aa2acf982e.1680708357.git.william.gray@linaro.org/ Signed-off-by: William Breathitt Gray Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 3 + drivers/gpio/gpio-pcie-idio-24.c | 669 +++++++++++-------------------- 2 files changed, 246 insertions(+), 426 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 864b221252b2..3cccc6cc315f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1665,7 +1665,10 @@ config GPIO_PCI_IDIO_16 config GPIO_PCIE_IDIO_24 tristate "ACCES PCIe-IDIO-24 GPIO support" + select REGMAP_IRQ + select REGMAP_MMIO select GPIOLIB_IRQCHIP + select GPIO_REGMAP help Enables GPIO support for the ACCES PCIe-IDIO-24 family (PCIe-IDIO-24, PCIe-IDI-24, PCIe-IDO-24, PCIe-IDIO-12). An interrupt is generated diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c index 463c0613abb9..2efd1b1a0805 100644 --- a/drivers/gpio/gpio-pcie-idio-24.c +++ b/drivers/gpio/gpio-pcie-idio-24.c @@ -6,16 +6,15 @@ * This driver supports the following ACCES devices: PCIe-IDIO-24, * PCIe-IDI-24, PCIe-IDO-24, and PCIe-IDIO-12. */ -#include -#include +#include #include -#include -#include -#include -#include +#include +#include +#include #include #include #include +#include #include #include @@ -59,422 +58,224 @@ #define PLX_PEX8311_PCI_LCS_INTCSR 0x68 #define INTCSR_INTERNAL_PCI_WIRE BIT(8) #define INTCSR_LOCAL_INPUT BIT(11) +#define IDIO_24_ENABLE_IRQ (INTCSR_INTERNAL_PCI_WIRE | INTCSR_LOCAL_INPUT) -/** - * struct idio_24_gpio_reg - GPIO device registers structure - * @out0_7: Read: FET Outputs 0-7 - * Write: FET Outputs 0-7 - * @out8_15: Read: FET Outputs 8-15 - * Write: FET Outputs 8-15 - * @out16_23: Read: FET Outputs 16-23 - * Write: FET Outputs 16-23 - * @ttl_out0_7: Read: TTL/CMOS Outputs 0-7 - * Write: TTL/CMOS Outputs 0-7 - * @in0_7: Read: Isolated Inputs 0-7 - * Write: Reserved - * @in8_15: Read: Isolated Inputs 8-15 - * Write: Reserved - * @in16_23: Read: Isolated Inputs 16-23 - * Write: Reserved - * @ttl_in0_7: Read: TTL/CMOS Inputs 0-7 - * Write: Reserved - * @cos0_7: Read: COS Status Inputs 0-7 - * Write: COS Clear Inputs 0-7 - * @cos8_15: Read: COS Status Inputs 8-15 - * Write: COS Clear Inputs 8-15 - * @cos16_23: Read: COS Status Inputs 16-23 - * Write: COS Clear Inputs 16-23 - * @cos_ttl0_7: Read: COS Status TTL/CMOS 0-7 - * Write: COS Clear TTL/CMOS 0-7 - * @ctl: Read: Control Register - * Write: Control Register - * @reserved: Read: Reserved - * Write: Reserved - * @cos_enable: Read: COS Enable - * Write: COS Enable - * @soft_reset: Read: IRQ Output Pin Status - * Write: Software Board Reset - */ -struct idio_24_gpio_reg { - u8 out0_7; - u8 out8_15; - u8 out16_23; - u8 ttl_out0_7; - u8 in0_7; - u8 in8_15; - u8 in16_23; - u8 ttl_in0_7; - u8 cos0_7; - u8 cos8_15; - u8 cos16_23; - u8 cos_ttl0_7; - u8 ctl; - u8 reserved; - u8 cos_enable; - u8 soft_reset; +#define IDIO_24_OUT_BASE 0x0 +#define IDIO_24_TTLCMOS_OUT_REG 0x3 +#define IDIO_24_IN_BASE 0x4 +#define IDIO_24_TTLCMOS_IN_REG 0x7 +#define IDIO_24_COS_STATUS_BASE 0x8 +#define IDIO_24_CONTROL_REG 0xC +#define IDIO_24_COS_ENABLE 0xE +#define IDIO_24_SOFT_RESET 0xF + +#define CONTROL_REG_OUT_MODE BIT(1) + +#define COS_ENABLE_RISING BIT(1) +#define COS_ENABLE_FALLING BIT(4) +#define COS_ENABLE_BOTH (COS_ENABLE_RISING | COS_ENABLE_FALLING) + +static const struct regmap_config pex8311_intcsr_regmap_config = { + .name = "pex8311_intcsr", + .reg_bits = 32, + .reg_stride = 1, + .reg_base = PLX_PEX8311_PCI_LCS_INTCSR, + .val_bits = 32, + .io_port = true, +}; + +static const struct regmap_range idio_24_wr_ranges[] = { + regmap_reg_range(0x0, 0x3), regmap_reg_range(0x8, 0xC), + regmap_reg_range(0xE, 0xF), +}; +static const struct regmap_range idio_24_rd_ranges[] = { + regmap_reg_range(0x0, 0xC), regmap_reg_range(0xE, 0xF), +}; +static const struct regmap_range idio_24_volatile_ranges[] = { + regmap_reg_range(0x4, 0xB), regmap_reg_range(0xF, 0xF), +}; +static const struct regmap_access_table idio_24_wr_table = { + .yes_ranges = idio_24_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(idio_24_wr_ranges), +}; +static const struct regmap_access_table idio_24_rd_table = { + .yes_ranges = idio_24_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(idio_24_rd_ranges), +}; +static const struct regmap_access_table idio_24_volatile_table = { + .yes_ranges = idio_24_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(idio_24_volatile_ranges), +}; + +static const struct regmap_config idio_24_regmap_config = { + .reg_bits = 8, + .reg_stride = 1, + .val_bits = 8, + .io_port = true, + .wr_table = &idio_24_wr_table, + .rd_table = &idio_24_rd_table, + .volatile_table = &idio_24_volatile_table, + .cache_type = REGCACHE_FLAT, + .use_raw_spinlock = true, +}; + +#define IDIO_24_NGPIO_PER_REG 8 +#define IDIO_24_REGMAP_IRQ(_id) \ + [24 + _id] = { \ + .reg_offset = (_id) / IDIO_24_NGPIO_PER_REG, \ + .mask = BIT((_id) % IDIO_24_NGPIO_PER_REG), \ + .type = { .types_supported = IRQ_TYPE_EDGE_BOTH }, \ + } +#define IDIO_24_IIN_IRQ(_id) IDIO_24_REGMAP_IRQ(_id) +#define IDIO_24_TTL_IRQ(_id) IDIO_24_REGMAP_IRQ(24 + _id) + +static const struct regmap_irq idio_24_regmap_irqs[] = { + IDIO_24_IIN_IRQ(0), IDIO_24_IIN_IRQ(1), IDIO_24_IIN_IRQ(2), /* IIN 0-2 */ + IDIO_24_IIN_IRQ(3), IDIO_24_IIN_IRQ(4), IDIO_24_IIN_IRQ(5), /* IIN 3-5 */ + IDIO_24_IIN_IRQ(6), IDIO_24_IIN_IRQ(7), IDIO_24_IIN_IRQ(8), /* IIN 6-8 */ + IDIO_24_IIN_IRQ(9), IDIO_24_IIN_IRQ(10), IDIO_24_IIN_IRQ(11), /* IIN 9-11 */ + IDIO_24_IIN_IRQ(12), IDIO_24_IIN_IRQ(13), IDIO_24_IIN_IRQ(14), /* IIN 12-14 */ + IDIO_24_IIN_IRQ(15), IDIO_24_IIN_IRQ(16), IDIO_24_IIN_IRQ(17), /* IIN 15-17 */ + IDIO_24_IIN_IRQ(18), IDIO_24_IIN_IRQ(19), IDIO_24_IIN_IRQ(20), /* IIN 18-20 */ + IDIO_24_IIN_IRQ(21), IDIO_24_IIN_IRQ(22), IDIO_24_IIN_IRQ(23), /* IIN 21-23 */ + IDIO_24_TTL_IRQ(0), IDIO_24_TTL_IRQ(1), IDIO_24_TTL_IRQ(2), /* TTL 0-2 */ + IDIO_24_TTL_IRQ(3), IDIO_24_TTL_IRQ(4), IDIO_24_TTL_IRQ(5), /* TTL 3-5 */ + IDIO_24_TTL_IRQ(6), IDIO_24_TTL_IRQ(7), /* TTL 6-7 */ }; /** * struct idio_24_gpio - GPIO device private data structure - * @chip: instance of the gpio_chip + * @map: regmap for the device * @lock: synchronization lock to prevent I/O race conditions - * @reg: I/O address offset for the GPIO device registers - * @irq_mask: I/O bits affected by interrupts + * @irq_type: type configuration for IRQs */ struct idio_24_gpio { - struct gpio_chip chip; + struct regmap *map; raw_spinlock_t lock; - __u8 __iomem *plx; - struct idio_24_gpio_reg __iomem *reg; - unsigned long irq_mask; + u8 irq_type; }; -static int idio_24_gpio_get_direction(struct gpio_chip *chip, - unsigned int offset) +static int idio_24_handle_mask_sync(const int index, const unsigned int mask_buf_def, + const unsigned int mask_buf, void *const irq_drv_data) { - struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); - const unsigned long out_mode_mask = BIT(1); + const unsigned int type_mask = COS_ENABLE_BOTH << index; + struct idio_24_gpio *const idio24gpio = irq_drv_data; + u8 type; + int ret; - /* FET Outputs */ - if (offset < 24) - return GPIO_LINE_DIRECTION_OUT; + raw_spin_lock(&idio24gpio->lock); - /* Isolated Inputs */ - if (offset < 48) - return GPIO_LINE_DIRECTION_IN; + /* if all are masked, then disable interrupts, else set to type */ + type = (mask_buf == mask_buf_def) ? ~type_mask : idio24gpio->irq_type; - /* TTL/CMOS I/O */ - /* OUT MODE = 1 when TTL/CMOS Output Mode is set */ - if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask) - return GPIO_LINE_DIRECTION_OUT; + ret = regmap_update_bits(idio24gpio->map, IDIO_24_COS_ENABLE, type_mask, type); - return GPIO_LINE_DIRECTION_IN; + raw_spin_unlock(&idio24gpio->lock); + + return ret; } -static int idio_24_gpio_direction_input(struct gpio_chip *chip, - unsigned int offset) +static int idio_24_set_type_config(unsigned int **const buf, const unsigned int type, + const struct regmap_irq *const irq_data, const int idx, + void *const irq_drv_data) { - struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); - unsigned long flags; - unsigned int ctl_state; - const unsigned long out_mode_mask = BIT(1); + const unsigned int offset = irq_data->reg_offset; + const unsigned int rising = COS_ENABLE_RISING << offset; + const unsigned int falling = COS_ENABLE_FALLING << offset; + const unsigned int mask = COS_ENABLE_BOTH << offset; + struct idio_24_gpio *const idio24gpio = irq_drv_data; + unsigned int new; + unsigned int cos_enable; + int ret; - /* TTL/CMOS I/O */ - if (offset > 47) { - raw_spin_lock_irqsave(&idio24gpio->lock, flags); - - /* Clear TTL/CMOS Output Mode */ - ctl_state = ioread8(&idio24gpio->reg->ctl) & ~out_mode_mask; - iowrite8(ctl_state, &idio24gpio->reg->ctl); - - raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); + switch (type) { + case IRQ_TYPE_EDGE_RISING: + new = rising; + break; + case IRQ_TYPE_EDGE_FALLING: + new = falling; + break; + case IRQ_TYPE_EDGE_BOTH: + new = mask; + break; + default: + return -EINVAL; } - return 0; -} + raw_spin_lock(&idio24gpio->lock); -static int idio_24_gpio_direction_output(struct gpio_chip *chip, - unsigned int offset, int value) -{ - struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); - unsigned long flags; - unsigned int ctl_state; - const unsigned long out_mode_mask = BIT(1); + /* replace old bitmap with new bitmap */ + idio24gpio->irq_type = (idio24gpio->irq_type & ~mask) | (new & mask); - /* TTL/CMOS I/O */ - if (offset > 47) { - raw_spin_lock_irqsave(&idio24gpio->lock, flags); + ret = regmap_read(idio24gpio->map, IDIO_24_COS_ENABLE, &cos_enable); + if (ret) + goto exit_unlock; - /* Set TTL/CMOS Output Mode */ - ctl_state = ioread8(&idio24gpio->reg->ctl) | out_mode_mask; - iowrite8(ctl_state, &idio24gpio->reg->ctl); - - raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); + /* if COS is currently enabled then update the edge type */ + if (cos_enable & mask) { + ret = regmap_update_bits(idio24gpio->map, IDIO_24_COS_ENABLE, mask, + idio24gpio->irq_type); + if (ret) + goto exit_unlock; } - chip->set(chip, offset, value); - return 0; +exit_unlock: + raw_spin_unlock(&idio24gpio->lock); + + return ret; } -static int idio_24_gpio_get(struct gpio_chip *chip, unsigned int offset) +static int idio_24_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned int base, + const unsigned int offset, unsigned int *const reg, + unsigned int *const mask) { - struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); - const unsigned long offset_mask = BIT(offset % 8); - const unsigned long out_mode_mask = BIT(1); + const unsigned int out_stride = offset / IDIO_24_NGPIO_PER_REG; + const unsigned int in_stride = (offset - 24) / IDIO_24_NGPIO_PER_REG; + struct regmap *const map = gpio_regmap_get_drvdata(gpio); + int err; + unsigned int ctrl_reg; - /* FET Outputs */ - if (offset < 8) - return !!(ioread8(&idio24gpio->reg->out0_7) & offset_mask); + switch (base) { + case IDIO_24_OUT_BASE: + *mask = BIT(offset % IDIO_24_NGPIO_PER_REG); - if (offset < 16) - return !!(ioread8(&idio24gpio->reg->out8_15) & offset_mask); - - if (offset < 24) - return !!(ioread8(&idio24gpio->reg->out16_23) & offset_mask); - - /* Isolated Inputs */ - if (offset < 32) - return !!(ioread8(&idio24gpio->reg->in0_7) & offset_mask); - - if (offset < 40) - return !!(ioread8(&idio24gpio->reg->in8_15) & offset_mask); - - if (offset < 48) - return !!(ioread8(&idio24gpio->reg->in16_23) & offset_mask); - - /* TTL/CMOS Outputs */ - if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask) - return !!(ioread8(&idio24gpio->reg->ttl_out0_7) & offset_mask); - - /* TTL/CMOS Inputs */ - return !!(ioread8(&idio24gpio->reg->ttl_in0_7) & offset_mask); -} - -static int idio_24_gpio_get_multiple(struct gpio_chip *chip, - unsigned long *mask, unsigned long *bits) -{ - struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - void __iomem *ports[] = { - &idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15, - &idio24gpio->reg->out16_23, &idio24gpio->reg->in0_7, - &idio24gpio->reg->in8_15, &idio24gpio->reg->in16_23, - }; - size_t index; - unsigned long port_state; - const unsigned long out_mode_mask = BIT(1); - - /* clear bits array to a clean slate */ - bitmap_zero(bits, chip->ngpio); - - for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { - index = offset / 8; - - /* read bits from current gpio port (port 6 is TTL GPIO) */ - if (index < 6) - port_state = ioread8(ports[index]); - else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask) - port_state = ioread8(&idio24gpio->reg->ttl_out0_7); - else - port_state = ioread8(&idio24gpio->reg->ttl_in0_7); - - port_state &= gpio_mask; - - bitmap_set_value8(bits, port_state, offset); - } - - return 0; -} - -static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) -{ - struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); - const unsigned long out_mode_mask = BIT(1); - void __iomem *base; - const unsigned int mask = BIT(offset % 8); - unsigned long flags; - unsigned int out_state; - - /* Isolated Inputs */ - if (offset > 23 && offset < 48) - return; - - /* TTL/CMOS Inputs */ - if (offset > 47 && !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask)) - return; - - /* TTL/CMOS Outputs */ - if (offset > 47) - base = &idio24gpio->reg->ttl_out0_7; - /* FET Outputs */ - else if (offset > 15) - base = &idio24gpio->reg->out16_23; - else if (offset > 7) - base = &idio24gpio->reg->out8_15; - else - base = &idio24gpio->reg->out0_7; - - raw_spin_lock_irqsave(&idio24gpio->lock, flags); - - if (value) - out_state = ioread8(base) | mask; - else - out_state = ioread8(base) & ~mask; - - iowrite8(out_state, base); - - raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); -} - -static void idio_24_gpio_set_multiple(struct gpio_chip *chip, - unsigned long *mask, unsigned long *bits) -{ - struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - void __iomem *ports[] = { - &idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15, - &idio24gpio->reg->out16_23 - }; - size_t index; - unsigned long bitmask; - unsigned long flags; - unsigned long out_state; - const unsigned long out_mode_mask = BIT(1); - - for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { - index = offset / 8; - - bitmask = bitmap_get_value8(bits, offset) & gpio_mask; - - raw_spin_lock_irqsave(&idio24gpio->lock, flags); - - /* read bits from current gpio port (port 6 is TTL GPIO) */ - if (index < 6) { - out_state = ioread8(ports[index]); - } else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask) { - out_state = ioread8(&idio24gpio->reg->ttl_out0_7); - } else { - /* skip TTL GPIO if set for input */ - raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); - continue; + /* FET Outputs */ + if (offset < 24) { + *reg = IDIO_24_OUT_BASE + out_stride; + return 0; } - /* set requested bit states */ - out_state &= ~gpio_mask; - out_state |= bitmask; + /* Isolated Inputs */ + if (offset < 48) { + *reg = IDIO_24_IN_BASE + in_stride; + return 0; + } - /* write bits for current gpio port (port 6 is TTL GPIO) */ - if (index < 6) - iowrite8(out_state, ports[index]); - else - iowrite8(out_state, &idio24gpio->reg->ttl_out0_7); + err = regmap_read(map, IDIO_24_CONTROL_REG, &ctrl_reg); + if (err) + return err; - raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); - } -} + /* TTL/CMOS Outputs */ + if (ctrl_reg & CONTROL_REG_OUT_MODE) { + *reg = IDIO_24_TTLCMOS_OUT_REG; + return 0; + } -static void idio_24_irq_ack(struct irq_data *data) -{ -} + /* TTL/CMOS Inputs */ + *reg = IDIO_24_TTLCMOS_IN_REG; + return 0; + case IDIO_24_CONTROL_REG: + /* We can only set direction for TTL/CMOS lines */ + if (offset < 48) + return -EOPNOTSUPP; -static void idio_24_irq_mask(struct irq_data *data) -{ - struct gpio_chip *const chip = irq_data_get_irq_chip_data(data); - struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); - unsigned long flags; - const unsigned long bit_offset = irqd_to_hwirq(data) - 24; - unsigned char new_irq_mask; - const unsigned long bank_offset = bit_offset / 8; - unsigned char cos_enable_state; - - raw_spin_lock_irqsave(&idio24gpio->lock, flags); - - idio24gpio->irq_mask &= ~BIT(bit_offset); - new_irq_mask = idio24gpio->irq_mask >> bank_offset * 8; - - if (!new_irq_mask) { - cos_enable_state = ioread8(&idio24gpio->reg->cos_enable); - - /* Disable Rising Edge detection */ - cos_enable_state &= ~BIT(bank_offset); - /* Disable Falling Edge detection */ - cos_enable_state &= ~BIT(bank_offset + 4); - - iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable); - } - - raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); - - gpiochip_disable_irq(chip, irqd_to_hwirq(data)); -} - -static void idio_24_irq_unmask(struct irq_data *data) -{ - struct gpio_chip *const chip = irq_data_get_irq_chip_data(data); - struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); - unsigned long flags; - unsigned char prev_irq_mask; - const unsigned long bit_offset = irqd_to_hwirq(data) - 24; - const unsigned long bank_offset = bit_offset / 8; - unsigned char cos_enable_state; - - gpiochip_enable_irq(chip, irqd_to_hwirq(data)); - - raw_spin_lock_irqsave(&idio24gpio->lock, flags); - - prev_irq_mask = idio24gpio->irq_mask >> bank_offset * 8; - idio24gpio->irq_mask |= BIT(bit_offset); - - if (!prev_irq_mask) { - cos_enable_state = ioread8(&idio24gpio->reg->cos_enable); - - /* Enable Rising Edge detection */ - cos_enable_state |= BIT(bank_offset); - /* Enable Falling Edge detection */ - cos_enable_state |= BIT(bank_offset + 4); - - iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable); - } - - raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); -} - -static int idio_24_irq_set_type(struct irq_data *data, unsigned int flow_type) -{ - /* The only valid irq types are none and both-edges */ - if (flow_type != IRQ_TYPE_NONE && - (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH) + *reg = IDIO_24_CONTROL_REG; + *mask = CONTROL_REG_OUT_MODE; + return 0; + default: + /* Should never reach this path */ return -EINVAL; - - return 0; -} - -static const struct irq_chip idio_24_irqchip = { - .name = "pcie-idio-24", - .irq_ack = idio_24_irq_ack, - .irq_mask = idio_24_irq_mask, - .irq_unmask = idio_24_irq_unmask, - .irq_set_type = idio_24_irq_set_type, - .flags = IRQCHIP_IMMUTABLE, - GPIOCHIP_IRQ_RESOURCE_HELPERS, -}; - -static irqreturn_t idio_24_irq_handler(int irq, void *dev_id) -{ - struct idio_24_gpio *const idio24gpio = dev_id; - unsigned long irq_status; - struct gpio_chip *const chip = &idio24gpio->chip; - unsigned long irq_mask; - int gpio; - - raw_spin_lock(&idio24gpio->lock); - - /* Read Change-Of-State status */ - irq_status = ioread32(&idio24gpio->reg->cos0_7); - - raw_spin_unlock(&idio24gpio->lock); - - /* Make sure our device generated IRQ */ - if (!irq_status) - return IRQ_NONE; - - /* Handle only unmasked IRQ */ - irq_mask = idio24gpio->irq_mask & irq_status; - - for_each_set_bit(gpio, &irq_mask, chip->ngpio - 24) - generic_handle_domain_irq(chip->irq.domain, gpio + 24); - - raw_spin_lock(&idio24gpio->lock); - - /* Clear Change-Of-State status */ - iowrite32(irq_status, &idio24gpio->reg->cos0_7); - - raw_spin_unlock(&idio24gpio->lock); - - return IRQ_HANDLED; + } } #define IDIO_24_NGPIO 56 @@ -496,11 +297,12 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id) const size_t pci_plx_bar_index = 1; const size_t pci_bar_index = 2; const char *const name = pci_name(pdev); - struct gpio_irq_chip *girq; - - idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL); - if (!idio24gpio) - return -ENOMEM; + struct gpio_regmap_config gpio_config = {}; + void __iomem *pex8311_regs; + void __iomem *idio_24_regs; + struct regmap *intcsr_map; + struct regmap_irq_chip *chip; + struct regmap_irq_chip_data *chip_data; err = pcim_enable_device(pdev); if (err) { @@ -514,57 +316,72 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id) return err; } - idio24gpio->plx = pcim_iomap_table(pdev)[pci_plx_bar_index]; - idio24gpio->reg = pcim_iomap_table(pdev)[pci_bar_index]; + pex8311_regs = pcim_iomap_table(pdev)[pci_plx_bar_index]; + idio_24_regs = pcim_iomap_table(pdev)[pci_bar_index]; - idio24gpio->chip.label = name; - idio24gpio->chip.parent = dev; - idio24gpio->chip.owner = THIS_MODULE; - idio24gpio->chip.base = -1; - idio24gpio->chip.ngpio = IDIO_24_NGPIO; - idio24gpio->chip.names = idio_24_names; - idio24gpio->chip.get_direction = idio_24_gpio_get_direction; - idio24gpio->chip.direction_input = idio_24_gpio_direction_input; - idio24gpio->chip.direction_output = idio_24_gpio_direction_output; - idio24gpio->chip.get = idio_24_gpio_get; - idio24gpio->chip.get_multiple = idio_24_gpio_get_multiple; - idio24gpio->chip.set = idio_24_gpio_set; - idio24gpio->chip.set_multiple = idio_24_gpio_set_multiple; + intcsr_map = devm_regmap_init_mmio(dev, pex8311_regs, &pex8311_intcsr_regmap_config); + if (IS_ERR(intcsr_map)) + return dev_err_probe(dev, PTR_ERR(intcsr_map), + "Unable to initialize PEX8311 register map\n"); - girq = &idio24gpio->chip.irq; - gpio_irq_chip_set_chip(girq, &idio_24_irqchip); - /* This will let us handle the parent IRQ in the driver */ - girq->parent_handler = NULL; - girq->num_parents = 0; - girq->parents = NULL; - girq->default_type = IRQ_TYPE_NONE; - girq->handler = handle_edge_irq; + idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL); + if (!idio24gpio) + return -ENOMEM; + + idio24gpio->map = devm_regmap_init_mmio(dev, idio_24_regs, &idio_24_regmap_config); + if (IS_ERR(idio24gpio->map)) + return dev_err_probe(dev, PTR_ERR(idio24gpio->map), + "Unable to initialize register map\n"); raw_spin_lock_init(&idio24gpio->lock); + /* Initialize all IRQ type configuration to IRQ_TYPE_EDGE_BOTH */ + idio24gpio->irq_type = GENMASK(7, 0); + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->name = name; + chip->status_base = IDIO_24_COS_STATUS_BASE; + chip->mask_base = IDIO_24_COS_ENABLE; + chip->ack_base = IDIO_24_COS_STATUS_BASE; + chip->num_regs = 4; + chip->irqs = idio_24_regmap_irqs; + chip->num_irqs = ARRAY_SIZE(idio_24_regmap_irqs); + chip->handle_mask_sync = idio_24_handle_mask_sync; + chip->set_type_config = idio_24_set_type_config; + chip->irq_drv_data = idio24gpio; + /* Software board reset */ - iowrite8(0, &idio24gpio->reg->soft_reset); + err = regmap_write(idio24gpio->map, IDIO_24_SOFT_RESET, 0); + if (err) + return err; /* * enable PLX PEX8311 internal PCI wire interrupt and local interrupt * input */ - iowrite8((INTCSR_INTERNAL_PCI_WIRE | INTCSR_LOCAL_INPUT) >> 8, - idio24gpio->plx + PLX_PEX8311_PCI_LCS_INTCSR + 1); - - err = devm_gpiochip_add_data(dev, &idio24gpio->chip, idio24gpio); - if (err) { - dev_err(dev, "GPIO registering failed (%d)\n", err); + err = regmap_update_bits(intcsr_map, 0x0, IDIO_24_ENABLE_IRQ, IDIO_24_ENABLE_IRQ); + if (err) return err; - } - err = devm_request_irq(dev, pdev->irq, idio_24_irq_handler, IRQF_SHARED, - name, idio24gpio); - if (err) { - dev_err(dev, "IRQ handler registering failed (%d)\n", err); - return err; - } + err = devm_regmap_add_irq_chip(dev, idio24gpio->map, pdev->irq, 0, 0, chip, &chip_data); + if (err) + return dev_err_probe(dev, err, "IRQ registration failed\n"); - return 0; + gpio_config.parent = dev; + gpio_config.regmap = idio24gpio->map; + gpio_config.ngpio = IDIO_24_NGPIO; + gpio_config.names = idio_24_names; + gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(IDIO_24_OUT_BASE); + gpio_config.reg_set_base = GPIO_REGMAP_ADDR(IDIO_24_OUT_BASE); + gpio_config.reg_dir_out_base = GPIO_REGMAP_ADDR(IDIO_24_CONTROL_REG); + gpio_config.ngpio_per_reg = IDIO_24_NGPIO_PER_REG; + gpio_config.irq_domain = regmap_irq_get_domain(chip_data); + gpio_config.reg_mask_xlate = idio_24_reg_mask_xlate; + gpio_config.drvdata = idio24gpio->map; + + return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config)); } static const struct pci_device_id idio_24_pci_dev_id[] = { From 0988ffa09630d4f2f66e345d1a44ffd3f1edb8e0 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 10 Aug 2023 18:00:43 -0400 Subject: [PATCH 72/94] gpio: ws16c48: Migrate to the regmap API The regmap API supports IO port accessors so we can take advantage of regmap abstractions rather than handling access to the device registers directly in the driver. The WinSystems WS16C48 provides the following registers: Offset 0x0-0x5: Port 0-5 I/O Offset 0x6: Int_Pending Offset 0x7: Page/Lock Offset 0x8-0xA (Page 1): Pol_0-Pol_2 Offset 0x8-0xA (Page 2): Enab_0-Enab_2 Offset 0x8-0xA (Page 3): Int_ID0-Int_ID2 Port 0-5 I/O provides access to 48 lines of digital I/O across six registers, each bit position corresponding to the respective line. Writing a 1 to a respective bit position causes that output pin to sink current, while writing a 0 to the same bit position causes that output pin to go to a high-impedance state and allows it to be used an input. Reads on a port report the inverted state (0 = high, 1 = low) of an I/O pin when used in input mode. Interrupts are supported on Port 0-2. Int_Pending is a read-only register that reports the combined state of the INT_ID0 through INT_ID2 registers; an interrupt pending is indicated when any of the low three bits are set. The Page/Lock register provides the following bits: Bit 0-5: Port 0-5 I/O Lock Bit 6-7: Page 0-3 Selection For Bits 0-5, writing a 1 to a respective bit position locks the output state of the corresponding I/O port. Writing the page number to Bits 6-7 selects that respective register page for use. Pol_0-Pol_2 are accessible when Page 1 is selected. Writing a 1 to a respective bit position selects the rising edge detection interrupts for that input line, while writing a 0 to the same bit position selects the falling edge detection interrupts. Enab_0-Enab_2 are accessible when Page 2 is selected. Writing a 1 to a respective bit position enables interrupts for that input line, while writing a 0 to that same bit position clears and disables interrupts for that input line. Int_ID0-Int_ID2 are accessible when Page 3 is selected. A respective bit when read as a 1 indicates that an edge of the polarity set in the corresponding polarity register was detected for the corresponding input line. Writing any value to this register clears all pending interrupts for the register. Suggested-by: Andy Shevchenko Reviewed-by: Michael Walle Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/f59de81e80f7198bcfa9a15615c459c38b5d0e08.1680708357.git.william.gray@linaro.org/ Signed-off-by: William Breathitt Gray Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 3 + drivers/gpio/gpio-ws16c48.c | 552 ++++++++++++------------------------ 2 files changed, 188 insertions(+), 367 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3cccc6cc315f..673bafb8be58 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -998,7 +998,10 @@ config GPIO_WINBOND config GPIO_WS16C48 tristate "WinSystems WS16C48 GPIO support" select ISA_BUS_API + select REGMAP_IRQ + select REGMAP_MMIO select GPIOLIB_IRQCHIP + select GPIO_REGMAP help Enables GPIO support for the WinSystems WS16C48. The base port addresses for the devices may be configured via the base module diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c index e73885a4dc32..701847508e94 100644 --- a/drivers/gpio/gpio-ws16c48.c +++ b/drivers/gpio/gpio-ws16c48.c @@ -3,19 +3,18 @@ * GPIO driver for the WinSystems WS16C48 * Copyright (C) 2016 William Breathitt Gray */ -#include +#include +#include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include #include #include #include #include #include +#include #include #define WS16C48_EXTENT 10 @@ -31,371 +30,178 @@ static unsigned int num_irq; module_param_hw_array(irq, uint, irq, &num_irq, 0); MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers"); -/** - * struct ws16c48_reg - device register structure - * @port: Port 0 through 5 I/O - * @int_pending: Interrupt Pending - * @page_lock: Register page (Bits 7-6) and I/O port lock (Bits 5-0) - * @pol_enab_int_id: Interrupt polarity, enable, and ID - */ -struct ws16c48_reg { - u8 port[6]; - u8 int_pending; - u8 page_lock; - u8 pol_enab_int_id[3]; +#define WS16C48_DAT_BASE 0x0 +#define WS16C48_PAGE_LOCK 0x7 +#define WS16C48_PAGE_BASE 0x8 +#define WS16C48_POL WS16C48_PAGE_BASE +#define WS16C48_ENAB WS16C48_PAGE_BASE +#define WS16C48_INT_ID WS16C48_PAGE_BASE + +#define PAGE_LOCK_PAGE_FIELD GENMASK(7, 6) +#define POL_PAGE u8_encode_bits(1, PAGE_LOCK_PAGE_FIELD) +#define ENAB_PAGE u8_encode_bits(2, PAGE_LOCK_PAGE_FIELD) +#define INT_ID_PAGE u8_encode_bits(3, PAGE_LOCK_PAGE_FIELD) + +static const struct regmap_range ws16c48_wr_ranges[] = { + regmap_reg_range(0x0, 0x5), regmap_reg_range(0x7, 0xA), +}; +static const struct regmap_range ws16c48_rd_ranges[] = { + regmap_reg_range(0x0, 0xA), +}; +static const struct regmap_range ws16c48_volatile_ranges[] = { + regmap_reg_range(0x0, 0x6), regmap_reg_range(0x8, 0xA), +}; +static const struct regmap_access_table ws16c48_wr_table = { + .yes_ranges = ws16c48_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(ws16c48_wr_ranges), +}; +static const struct regmap_access_table ws16c48_rd_table = { + .yes_ranges = ws16c48_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(ws16c48_rd_ranges), +}; +static const struct regmap_access_table ws16c48_volatile_table = { + .yes_ranges = ws16c48_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(ws16c48_volatile_ranges), +}; +static const struct regmap_config ws16c48_regmap_config = { + .reg_bits = 8, + .reg_stride = 1, + .val_bits = 8, + .io_port = true, + .wr_table = &ws16c48_wr_table, + .rd_table = &ws16c48_rd_table, + .volatile_table = &ws16c48_volatile_table, + .cache_type = REGCACHE_FLAT, + .use_raw_spinlock = true, +}; + +#define WS16C48_NGPIO_PER_REG 8 +#define WS16C48_REGMAP_IRQ(_id) \ + [_id] = { \ + .reg_offset = (_id) / WS16C48_NGPIO_PER_REG, \ + .mask = BIT((_id) % WS16C48_NGPIO_PER_REG), \ + .type = { \ + .type_reg_offset = (_id) / WS16C48_NGPIO_PER_REG, \ + .types_supported = IRQ_TYPE_EDGE_BOTH, \ + }, \ + } + +/* Only the first 24 lines (Port 0-2) support interrupts */ +#define WS16C48_NUM_IRQS 24 +static const struct regmap_irq ws16c48_regmap_irqs[WS16C48_NUM_IRQS] = { + WS16C48_REGMAP_IRQ(0), WS16C48_REGMAP_IRQ(1), WS16C48_REGMAP_IRQ(2), /* 0-2 */ + WS16C48_REGMAP_IRQ(3), WS16C48_REGMAP_IRQ(4), WS16C48_REGMAP_IRQ(5), /* 3-5 */ + WS16C48_REGMAP_IRQ(6), WS16C48_REGMAP_IRQ(7), WS16C48_REGMAP_IRQ(8), /* 6-8 */ + WS16C48_REGMAP_IRQ(9), WS16C48_REGMAP_IRQ(10), WS16C48_REGMAP_IRQ(11), /* 9-11 */ + WS16C48_REGMAP_IRQ(12), WS16C48_REGMAP_IRQ(13), WS16C48_REGMAP_IRQ(14), /* 12-14 */ + WS16C48_REGMAP_IRQ(15), WS16C48_REGMAP_IRQ(16), WS16C48_REGMAP_IRQ(17), /* 15-17 */ + WS16C48_REGMAP_IRQ(18), WS16C48_REGMAP_IRQ(19), WS16C48_REGMAP_IRQ(20), /* 18-20 */ + WS16C48_REGMAP_IRQ(21), WS16C48_REGMAP_IRQ(22), WS16C48_REGMAP_IRQ(23), /* 21-23 */ }; /** * struct ws16c48_gpio - GPIO device private data structure - * @chip: instance of the gpio_chip - * @io_state: bit I/O state (whether bit is set to input or output) - * @out_state: output bits state + * @map: regmap for the device * @lock: synchronization lock to prevent I/O race conditions * @irq_mask: I/O bits affected by interrupts - * @flow_mask: IRQ flow type mask for the respective I/O bits - * @reg: I/O address offset for the device registers */ struct ws16c48_gpio { - struct gpio_chip chip; - unsigned char io_state[6]; - unsigned char out_state[6]; + struct regmap *map; raw_spinlock_t lock; - unsigned long irq_mask; - unsigned long flow_mask; - struct ws16c48_reg __iomem *reg; + u8 irq_mask[WS16C48_NUM_IRQS / WS16C48_NGPIO_PER_REG]; }; -static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +static int ws16c48_handle_pre_irq(void *const irq_drv_data) __acquires(&ws16c48gpio->lock) { - struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); - const unsigned port = offset / 8; - const unsigned mask = BIT(offset % 8); + struct ws16c48_gpio *const ws16c48gpio = irq_drv_data; - if (ws16c48gpio->io_state[port] & mask) - return GPIO_LINE_DIRECTION_IN; - - return GPIO_LINE_DIRECTION_OUT; -} - -static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); - const unsigned port = offset / 8; - const unsigned mask = BIT(offset % 8); - unsigned long flags; - - raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); - - ws16c48gpio->io_state[port] |= mask; - ws16c48gpio->out_state[port] &= ~mask; - iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port); - - raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); + /* Lock to prevent Page/Lock register change while we handle IRQ */ + raw_spin_lock(&ws16c48gpio->lock); return 0; } -static int ws16c48_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) +static int ws16c48_handle_post_irq(void *const irq_drv_data) __releases(&ws16c48gpio->lock) { - struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); - const unsigned port = offset / 8; - const unsigned mask = BIT(offset % 8); - unsigned long flags; + struct ws16c48_gpio *const ws16c48gpio = irq_drv_data; - raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); - - ws16c48gpio->io_state[port] &= ~mask; - if (value) - ws16c48gpio->out_state[port] |= mask; - else - ws16c48gpio->out_state[port] &= ~mask; - iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port); - - raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); + raw_spin_unlock(&ws16c48gpio->lock); return 0; } -static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset) +static int ws16c48_handle_mask_sync(const int index, const unsigned int mask_buf_def, + const unsigned int mask_buf, void *const irq_drv_data) { - struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); - const unsigned port = offset / 8; - const unsigned mask = BIT(offset % 8); + struct ws16c48_gpio *const ws16c48gpio = irq_drv_data; unsigned long flags; - unsigned port_state; + int ret = 0; raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); - /* ensure that GPIO is set for input */ - if (!(ws16c48gpio->io_state[port] & mask)) { - raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); - return -EINVAL; - } + /* exit early if no change since the last mask sync */ + if (mask_buf == ws16c48gpio->irq_mask[index]) + goto exit_unlock; + ws16c48gpio->irq_mask[index] = mask_buf; - port_state = ioread8(ws16c48gpio->reg->port + port); + ret = regmap_write(ws16c48gpio->map, WS16C48_PAGE_LOCK, ENAB_PAGE); + if (ret) + goto exit_unlock; + /* Update ENAB register (inverted mask) */ + ret = regmap_write(ws16c48gpio->map, WS16C48_ENAB + index, ~mask_buf); + if (ret) + goto exit_unlock; + + ret = regmap_write(ws16c48gpio->map, WS16C48_PAGE_LOCK, INT_ID_PAGE); + if (ret) + goto exit_unlock; + +exit_unlock: raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); - return !!(port_state & mask); + return ret; } -static int ws16c48_gpio_get_multiple(struct gpio_chip *chip, - unsigned long *mask, unsigned long *bits) +static int ws16c48_set_type_config(unsigned int **const buf, const unsigned int type, + const struct regmap_irq *const irq_data, const int idx, + void *const irq_drv_data) { - struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - size_t index; - u8 __iomem *port_addr; - unsigned long port_state; - - /* clear bits array to a clean slate */ - bitmap_zero(bits, chip->ngpio); - - for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) { - index = offset / 8; - port_addr = ws16c48gpio->reg->port + index; - port_state = ioread8(port_addr) & gpio_mask; - - bitmap_set_value8(bits, port_state, offset); - } - - return 0; -} - -static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); - const unsigned port = offset / 8; - const unsigned mask = BIT(offset % 8); + struct ws16c48_gpio *const ws16c48gpio = irq_drv_data; + unsigned int polarity; unsigned long flags; + int ret; - raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); - - /* ensure that GPIO is set for output */ - if (ws16c48gpio->io_state[port] & mask) { - raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); - return; - } - - if (value) - ws16c48gpio->out_state[port] |= mask; - else - ws16c48gpio->out_state[port] &= ~mask; - iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port); - - raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); -} - -static void ws16c48_gpio_set_multiple(struct gpio_chip *chip, - unsigned long *mask, unsigned long *bits) -{ - struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - size_t index; - u8 __iomem *port_addr; - unsigned long bitmask; - unsigned long flags; - - for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) { - index = offset / 8; - port_addr = ws16c48gpio->reg->port + index; - - /* mask out GPIO configured for input */ - gpio_mask &= ~ws16c48gpio->io_state[index]; - bitmask = bitmap_get_value8(bits, offset) & gpio_mask; - - raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); - - /* update output state data and set device gpio register */ - ws16c48gpio->out_state[index] &= ~gpio_mask; - ws16c48gpio->out_state[index] |= bitmask; - iowrite8(ws16c48gpio->out_state[index], port_addr); - - raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); - } -} - -static void ws16c48_irq_ack(struct irq_data *data) -{ - struct gpio_chip *chip = irq_data_get_irq_chip_data(data); - struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); - const unsigned long offset = irqd_to_hwirq(data); - const unsigned port = offset / 8; - const unsigned mask = BIT(offset % 8); - unsigned long flags; - unsigned port_state; - - /* only the first 3 ports support interrupts */ - if (port > 2) - return; - - raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); - - port_state = ws16c48gpio->irq_mask >> (8*port); - - /* Select Register Page 2; Unlock all I/O ports */ - iowrite8(0x80, &ws16c48gpio->reg->page_lock); - - /* Clear pending interrupt */ - iowrite8(port_state & ~mask, ws16c48gpio->reg->pol_enab_int_id + port); - iowrite8(port_state | mask, ws16c48gpio->reg->pol_enab_int_id + port); - - /* Select Register Page 3; Unlock all I/O ports */ - iowrite8(0xC0, &ws16c48gpio->reg->page_lock); - - raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); -} - -static void ws16c48_irq_mask(struct irq_data *data) -{ - struct gpio_chip *chip = irq_data_get_irq_chip_data(data); - struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); - const unsigned long offset = irqd_to_hwirq(data); - const unsigned long mask = BIT(offset); - const unsigned port = offset / 8; - unsigned long flags; - unsigned long port_state; - - /* only the first 3 ports support interrupts */ - if (port > 2) - return; - - raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); - - ws16c48gpio->irq_mask &= ~mask; - gpiochip_disable_irq(chip, offset); - port_state = ws16c48gpio->irq_mask >> (8 * port); - - /* Select Register Page 2; Unlock all I/O ports */ - iowrite8(0x80, &ws16c48gpio->reg->page_lock); - - /* Disable interrupt */ - iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port); - - /* Select Register Page 3; Unlock all I/O ports */ - iowrite8(0xC0, &ws16c48gpio->reg->page_lock); - - raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); -} - -static void ws16c48_irq_unmask(struct irq_data *data) -{ - struct gpio_chip *chip = irq_data_get_irq_chip_data(data); - struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); - const unsigned long offset = irqd_to_hwirq(data); - const unsigned long mask = BIT(offset); - const unsigned port = offset / 8; - unsigned long flags; - unsigned long port_state; - - /* only the first 3 ports support interrupts */ - if (port > 2) - return; - - raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); - - gpiochip_enable_irq(chip, offset); - ws16c48gpio->irq_mask |= mask; - port_state = ws16c48gpio->irq_mask >> (8 * port); - - /* Select Register Page 2; Unlock all I/O ports */ - iowrite8(0x80, &ws16c48gpio->reg->page_lock); - - /* Enable interrupt */ - iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port); - - /* Select Register Page 3; Unlock all I/O ports */ - iowrite8(0xC0, &ws16c48gpio->reg->page_lock); - - raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); -} - -static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type) -{ - struct gpio_chip *chip = irq_data_get_irq_chip_data(data); - struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); - const unsigned long offset = irqd_to_hwirq(data); - const unsigned long mask = BIT(offset); - const unsigned port = offset / 8; - unsigned long flags; - unsigned long port_state; - - /* only the first 3 ports support interrupts */ - if (port > 2) - return -EINVAL; - - raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); - - switch (flow_type) { - case IRQ_TYPE_NONE: - break; + switch (type) { case IRQ_TYPE_EDGE_RISING: - ws16c48gpio->flow_mask |= mask; + polarity = irq_data->mask; break; case IRQ_TYPE_EDGE_FALLING: - ws16c48gpio->flow_mask &= ~mask; + polarity = 0; break; default: - raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); return -EINVAL; } - port_state = ws16c48gpio->flow_mask >> (8 * port); + raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); - /* Select Register Page 1; Unlock all I/O ports */ - iowrite8(0x40, &ws16c48gpio->reg->page_lock); + ret = regmap_write(ws16c48gpio->map, WS16C48_PAGE_LOCK, POL_PAGE); + if (ret) + goto exit_unlock; /* Set interrupt polarity */ - iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port); + ret = regmap_update_bits(ws16c48gpio->map, WS16C48_POL + idx, irq_data->mask, polarity); + if (ret) + goto exit_unlock; - /* Select Register Page 3; Unlock all I/O ports */ - iowrite8(0xC0, &ws16c48gpio->reg->page_lock); + ret = regmap_write(ws16c48gpio->map, WS16C48_PAGE_LOCK, INT_ID_PAGE); + if (ret) + goto exit_unlock; +exit_unlock: raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); - return 0; -} - -static const struct irq_chip ws16c48_irqchip = { - .name = "ws16c48", - .irq_ack = ws16c48_irq_ack, - .irq_mask = ws16c48_irq_mask, - .irq_unmask = ws16c48_irq_unmask, - .irq_set_type = ws16c48_irq_set_type, - .flags = IRQCHIP_IMMUTABLE, - GPIOCHIP_IRQ_RESOURCE_HELPERS, -}; - -static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id) -{ - struct ws16c48_gpio *const ws16c48gpio = dev_id; - struct gpio_chip *const chip = &ws16c48gpio->chip; - struct ws16c48_reg __iomem *const reg = ws16c48gpio->reg; - unsigned long int_pending; - unsigned long port; - unsigned long int_id; - unsigned long gpio; - - int_pending = ioread8(®->int_pending) & 0x7; - if (!int_pending) - return IRQ_NONE; - - /* loop until all pending interrupts are handled */ - do { - for_each_set_bit(port, &int_pending, 3) { - int_id = ioread8(reg->pol_enab_int_id + port); - for_each_set_bit(gpio, &int_id, 8) - generic_handle_domain_irq(chip->irq.domain, - gpio + 8*port); - } - - int_pending = ioread8(®->int_pending) & 0x7; - } while (int_pending); - - return IRQ_HANDLED; + return ret; } #define WS16C48_NGPIO 48 @@ -414,30 +220,37 @@ static const char *ws16c48_names[WS16C48_NGPIO] = { "Port 5 Bit 4", "Port 5 Bit 5", "Port 5 Bit 6", "Port 5 Bit 7" }; -static int ws16c48_irq_init_hw(struct gpio_chip *gc) +static int ws16c48_irq_init_hw(struct regmap *const map) { - struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(gc); + int err; - /* Select Register Page 2; Unlock all I/O ports */ - iowrite8(0x80, &ws16c48gpio->reg->page_lock); + err = regmap_write(map, WS16C48_PAGE_LOCK, ENAB_PAGE); + if (err) + return err; /* Disable interrupts for all lines */ - iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[0]); - iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[1]); - iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[2]); + err = regmap_write(map, WS16C48_ENAB + 0, 0x00); + if (err) + return err; + err = regmap_write(map, WS16C48_ENAB + 1, 0x00); + if (err) + return err; + err = regmap_write(map, WS16C48_ENAB + 2, 0x00); + if (err) + return err; - /* Select Register Page 3; Unlock all I/O ports */ - iowrite8(0xC0, &ws16c48gpio->reg->page_lock); - - return 0; + return regmap_write(map, WS16C48_PAGE_LOCK, INT_ID_PAGE); } static int ws16c48_probe(struct device *dev, unsigned int id) { struct ws16c48_gpio *ws16c48gpio; const char *const name = dev_name(dev); - struct gpio_irq_chip *girq; int err; + struct gpio_regmap_config gpio_config = {}; + void __iomem *regs; + struct regmap_irq_chip *chip; + struct regmap_irq_chip_data *chip_data; ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL); if (!ws16c48gpio) @@ -449,50 +262,55 @@ static int ws16c48_probe(struct device *dev, unsigned int id) return -EBUSY; } - ws16c48gpio->reg = devm_ioport_map(dev, base[id], WS16C48_EXTENT); - if (!ws16c48gpio->reg) + regs = devm_ioport_map(dev, base[id], WS16C48_EXTENT); + if (!regs) return -ENOMEM; - ws16c48gpio->chip.label = name; - ws16c48gpio->chip.parent = dev; - ws16c48gpio->chip.owner = THIS_MODULE; - ws16c48gpio->chip.base = -1; - ws16c48gpio->chip.ngpio = WS16C48_NGPIO; - ws16c48gpio->chip.names = ws16c48_names; - ws16c48gpio->chip.get_direction = ws16c48_gpio_get_direction; - ws16c48gpio->chip.direction_input = ws16c48_gpio_direction_input; - ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output; - ws16c48gpio->chip.get = ws16c48_gpio_get; - ws16c48gpio->chip.get_multiple = ws16c48_gpio_get_multiple; - ws16c48gpio->chip.set = ws16c48_gpio_set; - ws16c48gpio->chip.set_multiple = ws16c48_gpio_set_multiple; + ws16c48gpio->map = devm_regmap_init_mmio(dev, regs, &ws16c48_regmap_config); + if (IS_ERR(ws16c48gpio->map)) + return dev_err_probe(dev, PTR_ERR(ws16c48gpio->map), + "Unable to initialize register map\n"); - girq = &ws16c48gpio->chip.irq; - gpio_irq_chip_set_chip(girq, &ws16c48_irqchip); - /* This will let us handle the parent IRQ in the driver */ - girq->parent_handler = NULL; - girq->num_parents = 0; - girq->parents = NULL; - girq->default_type = IRQ_TYPE_NONE; - girq->handler = handle_edge_irq; - girq->init_hw = ws16c48_irq_init_hw; + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->name = name; + chip->status_base = WS16C48_INT_ID; + chip->mask_base = WS16C48_ENAB; + chip->ack_base = WS16C48_INT_ID; + chip->num_regs = 3; + chip->irqs = ws16c48_regmap_irqs; + chip->num_irqs = ARRAY_SIZE(ws16c48_regmap_irqs); + chip->handle_pre_irq = ws16c48_handle_pre_irq; + chip->handle_post_irq = ws16c48_handle_post_irq; + chip->handle_mask_sync = ws16c48_handle_mask_sync; + chip->set_type_config = ws16c48_set_type_config; + chip->irq_drv_data = ws16c48gpio; raw_spin_lock_init(&ws16c48gpio->lock); - err = devm_gpiochip_add_data(dev, &ws16c48gpio->chip, ws16c48gpio); - if (err) { - dev_err(dev, "GPIO registering failed (%d)\n", err); + /* Initialize to prevent spurious interrupts before we're ready */ + err = ws16c48_irq_init_hw(ws16c48gpio->map); + if (err) return err; - } - err = devm_request_irq(dev, irq[id], ws16c48_irq_handler, IRQF_SHARED, - name, ws16c48gpio); - if (err) { - dev_err(dev, "IRQ handler registering failed (%d)\n", err); - return err; - } + err = devm_regmap_add_irq_chip(dev, ws16c48gpio->map, irq[id], 0, 0, chip, &chip_data); + if (err) + return dev_err_probe(dev, err, "IRQ registration failed\n"); - return 0; + gpio_config.parent = dev; + gpio_config.regmap = ws16c48gpio->map; + gpio_config.ngpio = WS16C48_NGPIO; + gpio_config.names = ws16c48_names; + gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(WS16C48_DAT_BASE); + gpio_config.reg_set_base = GPIO_REGMAP_ADDR(WS16C48_DAT_BASE); + /* Setting a GPIO to 0 allows it to be used as an input */ + gpio_config.reg_dir_out_base = GPIO_REGMAP_ADDR(WS16C48_DAT_BASE); + gpio_config.ngpio_per_reg = WS16C48_NGPIO_PER_REG; + gpio_config.irq_domain = regmap_irq_get_domain(chip_data); + + return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config)); } static struct isa_driver ws16c48_driver = { From a0e3b8e2acd0f6e30fe68dd74116596bcf59aa63 Mon Sep 17 00:00:00 2001 From: Rajeshwar R Shinde Date: Thu, 10 Aug 2023 18:36:00 +0530 Subject: [PATCH 73/94] gpio: imx-scu: Use ARRAY_SIZE for array length Use of macro ARRAY_SIZE to calculate array size minimizes the redundant code and improves code reusability. This fixes warnings reported by Coccinelle: drivers/gpio/gpio-imx-scu.c:106:32-33: WARNING: Use ARRAY_SIZE Signed-off-by: Rajeshwar R Shinde Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-imx-scu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-imx-scu.c b/drivers/gpio/gpio-imx-scu.c index e190bde5397d..13baf465aedf 100644 --- a/drivers/gpio/gpio-imx-scu.c +++ b/drivers/gpio/gpio-imx-scu.c @@ -6,6 +6,7 @@ * to control the PIN resources on SCU domain. */ +#include #include #include #include @@ -103,7 +104,7 @@ static int imx_scu_gpio_probe(struct platform_device *pdev) gc = &priv->chip; gc->base = -1; gc->parent = dev; - gc->ngpio = sizeof(scu_rsrc_arr)/sizeof(unsigned int); + gc->ngpio = ARRAY_SIZE(scu_rsrc_arr); gc->label = dev_name(dev); gc->get = imx_scu_gpio_get; gc->set = imx_scu_gpio_set; From b7df0f340b64c543bb84c42698347f08b6e18fe2 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 10 Aug 2023 11:59:49 +0200 Subject: [PATCH 74/94] gpio: mxs: fix Wvoid-pointer-to-enum-cast warning 'devid' is an enum, thus cast of pointer on 64-bit compile test with W=1 causes: gpio-mxs.c:274:16: error: cast to smaller integer type 'enum mxs_gpio_id' from 'const void *' [-Werror,-Wvoid-pointer-to-enum-cast] Signed-off-by: Krzysztof Kozlowski Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mxs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index 8e04c9c4b5a2..024ad077e98d 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -271,7 +271,7 @@ static int mxs_gpio_probe(struct platform_device *pdev) port->id = of_alias_get_id(np, "gpio"); if (port->id < 0) return port->id; - port->devid = (enum mxs_gpio_id)of_device_get_match_data(&pdev->dev); + port->devid = (uintptr_t)of_device_get_match_data(&pdev->dev); port->dev = &pdev->dev; port->irq = platform_get_irq(pdev, 0); if (port->irq < 0) From 5f6d1998adeb5374ef248b5ba2e2a0c30ab0f60b Mon Sep 17 00:00:00 2001 From: Shenwei Wang Date: Wed, 9 Aug 2023 09:13:24 -0500 Subject: [PATCH 75/94] gpio: mxc: release the parent IRQ in runtime suspend Release the parent interrupt request during runtime suspend, allowing the parent interrupt controller to enter runtime suspend if there are no active users. This change may not have a visible impact if the parent controller is the GIC, but it can enable significant power savings for parent IRQ controllers like IRQSteer inside a subsystem on i.MX8 SoCs. Releasing the parent IRQ provides an opportunity for the subsystem to enter suspend states if there are no active users. Signed-off-by: Shenwei Wang Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mxc.c | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 377d3ab8d626..004c6ad7ce52 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -62,6 +62,7 @@ struct mxc_gpio_port { struct clk *clk; int irq; int irq_high; + void (*mx_irq_handler)(struct irq_desc *desc); struct irq_domain *domain; struct gpio_chip gc; struct device *dev; @@ -399,6 +400,24 @@ static void mxc_gpio_free(struct gpio_chip *chip, unsigned int offset) pm_runtime_put(chip->parent); } +static void mxc_update_irq_chained_handler(struct mxc_gpio_port *port, bool enable) +{ + if (enable) + irq_set_chained_handler_and_data(port->irq, port->mx_irq_handler, port); + else + irq_set_chained_handler_and_data(port->irq, NULL, NULL); + + /* setup handler for GPIO 16 to 31 */ + if (port->irq_high > 0) { + if (enable) + irq_set_chained_handler_and_data(port->irq_high, + port->mx_irq_handler, + port); + else + irq_set_chained_handler_and_data(port->irq_high, NULL, NULL); + } +} + static int mxc_gpio_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -460,18 +479,12 @@ static int mxc_gpio_probe(struct platform_device *pdev) * the handler is needed only once, but doing it for every port * is more robust and easier. */ - irq_set_chained_handler(port->irq, mx2_gpio_irq_handler); - } else { - /* setup one handler for each entry */ - irq_set_chained_handler_and_data(port->irq, - mx3_gpio_irq_handler, port); - if (port->irq_high > 0) - /* setup handler for GPIO 16 to 31 */ - irq_set_chained_handler_and_data(port->irq_high, - mx3_gpio_irq_handler, - port); - } + port->irq_high = -1; + port->mx_irq_handler = mx2_gpio_irq_handler; + } else + port->mx_irq_handler = mx3_gpio_irq_handler; + mxc_update_irq_chained_handler(port, true); err = bgpio_init(&port->gc, &pdev->dev, 4, port->base + GPIO_PSR, port->base + GPIO_DR, NULL, @@ -604,6 +617,7 @@ static int mxc_gpio_runtime_suspend(struct device *dev) mxc_gpio_save_regs(port); clk_disable_unprepare(port->clk); + mxc_update_irq_chained_handler(port, false); return 0; } @@ -613,9 +627,12 @@ static int mxc_gpio_runtime_resume(struct device *dev) struct mxc_gpio_port *port = dev_get_drvdata(dev); int ret; + mxc_update_irq_chained_handler(port, true); ret = clk_prepare_enable(port->clk); - if (ret) + if (ret) { + mxc_update_irq_chained_handler(port, false); return ret; + } mxc_gpio_restore_regs(port); From a40fe1ffb69b709835a0623959f95d6c81ff38c1 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 11 Aug 2023 15:14:26 +0200 Subject: [PATCH 76/94] gpio: sim: simplify gpio_sim_device_config_live_store() Simplify the logic when checking the current live value against the user input. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko --- drivers/gpio/gpio-sim.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index 1a3729eb44eb..1fbbb49985f6 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -986,8 +986,7 @@ gpio_sim_device_config_live_store(struct config_item *item, mutex_lock(&dev->lock); - if ((!live && !gpio_sim_device_is_live_unlocked(dev)) || - (live && gpio_sim_device_is_live_unlocked(dev))) + if (live == gpio_sim_device_is_live_unlocked(dev)) ret = -EPERM; else if (live) ret = gpio_sim_device_activate_unlocked(dev); From ba0294df2dbdc006ffbf037da28bba64e9f6d709 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Sat, 12 Aug 2023 20:57:48 +0200 Subject: [PATCH 77/94] gpio: sim: replace memmove() + strstrip() with skip_spaces() + strim() Turns out we can avoid the memmove() by using skip_spaces() and strim(). We did that in gpio-consumer, let's do it in gpio-sim. Suggested-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko --- drivers/gpio/gpio-sim.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index 1fbbb49985f6..b7161c83c104 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -642,16 +642,13 @@ static bool gpio_sim_device_is_live_unlocked(struct gpio_sim_device *dev) static char *gpio_sim_strdup_trimmed(const char *str, size_t count) { - char *dup, *trimmed; + char *trimmed; - dup = kstrndup(str, count, GFP_KERNEL); - if (!dup) + trimmed = kstrndup(skip_spaces(str), count, GFP_KERNEL); + if (!trimmed) return NULL; - trimmed = strstrip(dup); - memmove(dup, trimmed, strlen(trimmed) + 1); - - return dup; + return strim(trimmed); } static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item, From 3faf89f27aab1ef484e088c3b126126a3199615c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Sat, 12 Aug 2023 20:36:35 +0200 Subject: [PATCH 78/94] gpio: sim: simplify code with cleanup helpers Use macros defined in linux/cleanup.h to automate resource lifetime control in gpio-sim. Signed-off-by: Bartosz Golaszewski Acked-by: Linus Walleij --- drivers/gpio/gpio-sim.c | 254 ++++++++++++++-------------------------- 1 file changed, 85 insertions(+), 169 deletions(-) diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index b7161c83c104..141249f2bf40 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -8,6 +8,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include @@ -68,7 +69,7 @@ static int gpio_sim_apply_pull(struct gpio_sim_chip *chip, gc = &chip->gc; desc = &gc->gpiodev->descs[offset]; - mutex_lock(&chip->lock); + guard(mutex)(&chip->lock); if (test_bit(FLAG_REQUESTED, &desc->flags) && !test_bit(FLAG_IS_OUT, &desc->flags)) { @@ -104,29 +105,24 @@ set_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); + guard(mutex)(&chip->lock); - return ret; + return !!test_bit(offset, chip->value_map); } 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); + scoped_guard(mutex, &chip->lock) + __assign_bit(offset, chip->value_map, value); } static int gpio_sim_get_multiple(struct gpio_chip *gc, @@ -134,9 +130,8 @@ static int gpio_sim_get_multiple(struct gpio_chip *gc, { struct gpio_sim_chip *chip = gpiochip_get_data(gc); - mutex_lock(&chip->lock); - bitmap_replace(bits, bits, chip->value_map, mask, gc->ngpio); - mutex_unlock(&chip->lock); + scoped_guard(mutex, &chip->lock) + bitmap_replace(bits, bits, chip->value_map, mask, gc->ngpio); return 0; } @@ -146,9 +141,9 @@ static void gpio_sim_set_multiple(struct gpio_chip *gc, { struct gpio_sim_chip *chip = gpiochip_get_data(gc); - mutex_lock(&chip->lock); - bitmap_replace(chip->value_map, chip->value_map, bits, mask, gc->ngpio); - mutex_unlock(&chip->lock); + scoped_guard(mutex, &chip->lock) + bitmap_replace(chip->value_map, chip->value_map, bits, mask, + gc->ngpio); } static int gpio_sim_direction_output(struct gpio_chip *gc, @@ -156,10 +151,10 @@ static int gpio_sim_direction_output(struct gpio_chip *gc, { 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); + scoped_guard(mutex, &chip->lock) { + __clear_bit(offset, chip->direction_map); + __assign_bit(offset, chip->value_map, value); + } return 0; } @@ -168,9 +163,8 @@ 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); + scoped_guard(mutex, &chip->lock) + __set_bit(offset, chip->direction_map); return 0; } @@ -180,9 +174,8 @@ 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); + scoped_guard(mutex, &chip->lock) + direction = !!test_bit(offset, chip->direction_map); return direction ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; } @@ -215,9 +208,9 @@ 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); + scoped_guard(mutex, &chip->lock) + __assign_bit(offset, chip->value_map, + !!test_bit(offset, chip->pull_map)); } static ssize_t gpio_sim_sysfs_val_show(struct device *dev, @@ -227,9 +220,8 @@ static ssize_t gpio_sim_sysfs_val_show(struct device *dev, 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); + scoped_guard(mutex, &chip->lock) + val = !!test_bit(line_attr->offset, chip->value_map); return sysfs_emit(buf, "%d\n", val); } @@ -258,9 +250,8 @@ static ssize_t gpio_sim_sysfs_pull_show(struct device *dev, 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); + scoped_guard(mutex, &chip->lock) + pull = !!test_bit(line_attr->offset, chip->pull_map); return sysfs_emit(buf, "%s\n", gpio_sim_sysfs_pull_strings[pull]); } @@ -656,17 +647,14 @@ static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item, { struct gpio_sim_device *dev = to_gpio_sim_device(item); struct platform_device *pdev; - int ret; - mutex_lock(&dev->lock); + guard(mutex)(&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 sprintf(page, "%s\n", dev_name(&pdev->dev)); - return ret; + return sprintf(page, "gpio-sim.%d\n", dev->id); } CONFIGFS_ATTR_RO(gpio_sim_device_config_, dev_name); @@ -677,9 +665,8 @@ 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); + scoped_guard(mutex, &dev->lock) + live = gpio_sim_device_is_live_unlocked(dev); return sprintf(page, "%c\n", live ? '1' : '0'); } @@ -834,8 +821,7 @@ gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank, { struct property_entry properties[GPIO_SIM_PROP_MAX]; unsigned int prop_idx = 0, line_names_size = 0; - struct fwnode_handle *swnode; - char **line_names; + char **line_names __free(kfree) = NULL; memset(properties, 0, sizeof(properties)); @@ -854,9 +840,7 @@ gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank, "gpio-line-names", line_names, line_names_size); - swnode = fwnode_create_software_node(properties, parent); - kfree(line_names); - return swnode; + return fwnode_create_software_node(properties, parent); } static void gpio_sim_remove_swnode_recursive(struct fwnode_handle *swnode) @@ -981,7 +965,7 @@ gpio_sim_device_config_live_store(struct config_item *item, if (ret) return ret; - mutex_lock(&dev->lock); + guard(mutex)(&dev->lock); if (live == gpio_sim_device_is_live_unlocked(dev)) ret = -EPERM; @@ -990,8 +974,6 @@ gpio_sim_device_config_live_store(struct config_item *item, else gpio_sim_device_deactivate_unlocked(dev); - mutex_unlock(&dev->lock); - return ret ?: count; } @@ -1028,17 +1010,14 @@ static ssize_t gpio_sim_bank_config_chip_name_show(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); struct gpio_sim_chip_name_ctx ctx = { bank->swnode, page }; - int ret; - mutex_lock(&dev->lock); + guard(mutex)(&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 device_for_each_child(&dev->pdev->dev, &ctx, + gpio_sim_emit_chip_name); - return ret; + return sprintf(page, "none\n"); } CONFIGFS_ATTR_RO(gpio_sim_bank_config_, chip_name); @@ -1048,13 +1027,10 @@ 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); + guard(mutex)(&dev->lock); - return ret; + return sprintf(page, "%s\n", bank->label ?: ""); } static ssize_t gpio_sim_bank_config_label_store(struct config_item *item, @@ -1064,23 +1040,18 @@ static ssize_t gpio_sim_bank_config_label_store(struct config_item *item, struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); char *trimmed; - mutex_lock(&dev->lock); + guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) { - mutex_unlock(&dev->lock); + if (gpio_sim_device_is_live_unlocked(dev)) return -EBUSY; - } trimmed = gpio_sim_strdup_trimmed(page, count); - if (!trimmed) { - mutex_unlock(&dev->lock); + if (!trimmed) return -ENOMEM; - } kfree(bank->label); bank->label = trimmed; - mutex_unlock(&dev->lock); return count; } @@ -1091,13 +1062,10 @@ 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); + guard(mutex)(&dev->lock); - return ret; + return sprintf(page, "%u\n", bank->num_lines); } static ssize_t @@ -1116,16 +1084,13 @@ gpio_sim_bank_config_num_lines_store(struct config_item *item, if (num_lines == 0) return -EINVAL; - mutex_lock(&dev->lock); + guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) { - mutex_unlock(&dev->lock); + if (gpio_sim_device_is_live_unlocked(dev)) return -EBUSY; - } bank->num_lines = num_lines; - mutex_unlock(&dev->lock); return count; } @@ -1143,13 +1108,10 @@ 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); + guard(mutex)(&dev->lock); - return ret; + return sprintf(page, "%s\n", line->name ?: ""); } static ssize_t gpio_sim_line_config_name_store(struct config_item *item, @@ -1159,24 +1121,18 @@ static ssize_t gpio_sim_line_config_name_store(struct config_item *item, struct gpio_sim_device *dev = gpio_sim_line_get_device(line); char *trimmed; - mutex_lock(&dev->lock); + guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) { - mutex_unlock(&dev->lock); + if (gpio_sim_device_is_live_unlocked(dev)) return -EBUSY; - } trimmed = gpio_sim_strdup_trimmed(page, count); - if (!trimmed) { - mutex_unlock(&dev->lock); + if (!trimmed) return -ENOMEM; - } kfree(line->name); line->name = trimmed; - mutex_unlock(&dev->lock); - return count; } @@ -1192,13 +1148,10 @@ static ssize_t gpio_sim_hog_config_name_show(struct config_item *item, { 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); + guard(mutex)(&dev->lock); - return ret; + return sprintf(page, "%s\n", hog->name ?: ""); } static ssize_t gpio_sim_hog_config_name_store(struct config_item *item, @@ -1208,24 +1161,18 @@ static ssize_t gpio_sim_hog_config_name_store(struct config_item *item, struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); char *trimmed; - mutex_lock(&dev->lock); + guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) { - mutex_unlock(&dev->lock); + if (gpio_sim_device_is_live_unlocked(dev)) return -EBUSY; - } trimmed = gpio_sim_strdup_trimmed(page, count); - if (!trimmed) { - mutex_unlock(&dev->lock); + if (!trimmed) return -ENOMEM; - } kfree(hog->name); hog->name = trimmed; - mutex_unlock(&dev->lock); - return count; } @@ -1239,9 +1186,8 @@ static ssize_t gpio_sim_hog_config_direction_show(struct config_item *item, char *repr; int dir; - mutex_lock(&dev->lock); - dir = hog->dir; - mutex_unlock(&dev->lock); + scoped_guard(mutex, &dev->lock) + dir = hog->dir; switch (dir) { case GPIOD_IN: @@ -1270,12 +1216,10 @@ gpio_sim_hog_config_direction_store(struct config_item *item, struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); int dir; - mutex_lock(&dev->lock); + guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) { - mutex_unlock(&dev->lock); + if (gpio_sim_device_is_live_unlocked(dev)) return -EBUSY; - } if (sysfs_streq(page, "input")) dir = GPIOD_IN; @@ -1284,17 +1228,10 @@ gpio_sim_hog_config_direction_store(struct config_item *item, else if (sysfs_streq(page, "output-low")) dir = GPIOD_OUT_LOW; else - dir = -EINVAL; - - if (dir < 0) { - mutex_unlock(&dev->lock); - return dir; - } + return -EINVAL; hog->dir = dir; - mutex_unlock(&dev->lock); - return count; } @@ -1312,9 +1249,8 @@ static void gpio_sim_hog_config_item_release(struct config_item *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); + scoped_guard(mutex, &dev->lock) + line->hog = NULL; kfree(hog->name); kfree(hog); @@ -1340,13 +1276,11 @@ gpio_sim_line_config_make_hog_item(struct config_group *group, const char *name) if (strcmp(name, "hog") != 0) return ERR_PTR(-EINVAL); - mutex_lock(&dev->lock); + guard(mutex)(&dev->lock); hog = kzalloc(sizeof(*hog), GFP_KERNEL); - if (!hog) { - mutex_unlock(&dev->lock); + if (!hog) return ERR_PTR(-ENOMEM); - } config_item_init_type_name(&hog->item, name, &gpio_sim_hog_config_type); @@ -1356,8 +1290,6 @@ gpio_sim_line_config_make_hog_item(struct config_group *group, const char *name) hog->parent = line; line->hog = hog; - mutex_unlock(&dev->lock); - return &hog->item; } @@ -1366,9 +1298,8 @@ 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); + scoped_guard(mutex, &dev->lock) + list_del(&line->siblings); kfree(line->name); kfree(line); @@ -1403,18 +1334,14 @@ gpio_sim_bank_config_make_line_group(struct config_group *group, if (ret != 1 || nchar != strlen(name)) return ERR_PTR(-EINVAL); - mutex_lock(&dev->lock); + guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) { - mutex_unlock(&dev->lock); + if (gpio_sim_device_is_live_unlocked(dev)) return ERR_PTR(-EBUSY); - } line = kzalloc(sizeof(*line), GFP_KERNEL); - if (!line) { - mutex_unlock(&dev->lock); + if (!line) return ERR_PTR(-ENOMEM); - } config_group_init_type_name(&line->group, name, &gpio_sim_line_config_type); @@ -1423,8 +1350,6 @@ gpio_sim_bank_config_make_line_group(struct config_group *group, line->offset = offset; list_add_tail(&line->siblings, &bank->line_list); - mutex_unlock(&dev->lock); - return &line->group; } @@ -1433,9 +1358,8 @@ 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); + scoped_guard(mutex, &dev->lock) + list_del(&bank->siblings); kfree(bank->label); kfree(bank); @@ -1463,18 +1387,14 @@ gpio_sim_device_config_make_bank_group(struct config_group *group, struct gpio_sim_device *dev = to_gpio_sim_device(&group->cg_item); struct gpio_sim_bank *bank; - mutex_lock(&dev->lock); + guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) { - mutex_unlock(&dev->lock); + if (gpio_sim_device_is_live_unlocked(dev)) return ERR_PTR(-EBUSY); - } bank = kzalloc(sizeof(*bank), GFP_KERNEL); - if (!bank) { - mutex_unlock(&dev->lock); + if (!bank) return ERR_PTR(-ENOMEM); - } config_group_init_type_name(&bank->group, name, &gpio_sim_bank_config_group_type); @@ -1483,8 +1403,6 @@ gpio_sim_device_config_make_bank_group(struct config_group *group, INIT_LIST_HEAD(&bank->line_list); list_add_tail(&bank->siblings, &dev->bank_list); - mutex_unlock(&dev->lock); - return &bank->group; } @@ -1492,10 +1410,10 @@ 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); + scoped_guard(mutex, &dev->lock) { + if (gpio_sim_device_is_live_unlocked(dev)) + gpio_sim_device_deactivate_unlocked(dev); + } mutex_destroy(&dev->lock); ida_free(&gpio_sim_ida, dev->id); @@ -1520,7 +1438,7 @@ static const struct config_item_type gpio_sim_device_config_group_type = { static struct config_group * gpio_sim_config_make_device_group(struct config_group *group, const char *name) { - struct gpio_sim_device *dev; + struct gpio_sim_device *dev __free(kfree) = NULL; int id; dev = kzalloc(sizeof(*dev), GFP_KERNEL); @@ -1528,10 +1446,8 @@ gpio_sim_config_make_device_group(struct config_group *group, const char *name) return ERR_PTR(-ENOMEM); id = ida_alloc(&gpio_sim_ida, GFP_KERNEL); - if (id < 0) { - kfree(dev); + if (id < 0) return ERR_PTR(id); - } config_group_init_type_name(&dev->group, name, &gpio_sim_device_config_group_type); @@ -1542,7 +1458,7 @@ gpio_sim_config_make_device_group(struct config_group *group, const char *name) dev->bus_notifier.notifier_call = gpio_sim_bus_notifier_call; init_completion(&dev->probe_completion); - return &dev->group; + return &no_free_ptr(dev)->group; } static struct configfs_group_operations gpio_sim_config_group_ops = { From 82dbbfdf8f7ecc7381ce2adb22778418d520d84c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 17 Aug 2023 21:44:49 +0200 Subject: [PATCH 79/94] gpio: pca9570: fix kerneldoc While renaming one of the fields in the driver data struct, the kerneldoc was not updated which apparently angers the test robot now. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202308171538.nKKUOtbg-lkp@intel.com/ Fixes: a3f7c1d6ddcb ("gpio: pca9570: rename platform_data to chip_data") Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko --- drivers/gpio/gpio-pca9570.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c index d8db80ef1293..d37ba4049368 100644 --- a/drivers/gpio/gpio-pca9570.c +++ b/drivers/gpio/gpio-pca9570.c @@ -30,7 +30,7 @@ struct pca9570_chip_data { /** * struct pca9570 - GPIO driver data * @chip: GPIO controller chip - * @p_data: GPIO controller platform data + * @chip_data: GPIO controller platform data * @lock: Protects write sequences * @out: Buffer for device register */ From 9e0fa5d85a4f49496e1a92889418c9d7767c1ab9 Mon Sep 17 00:00:00 2001 From: Li Zetao Date: Fri, 18 Aug 2023 17:30:14 +0800 Subject: [PATCH 80/94] gpio: mxc: Use helper function devm_clk_get_optional_enabled() Since commit 7ef9651e9792 ("clk: Provide new devm_clk helpers for prepared and enabled clocks"), devm_clk_get_optional() and clk_prepare_enable() can now be replaced by devm_clk_get_optional_enabled() when the driver enables (and possibly prepares) the clocks for the whole lifetime of the device. Moreover, it is no longer necessary to unprepare and disable the clocks explicitly. Signed-off-by: Li Zetao Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mxc.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 004c6ad7ce52..4cb455b2bdee 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -452,16 +452,10 @@ static int mxc_gpio_probe(struct platform_device *pdev) return port->irq; /* the controller clock is optional */ - port->clk = devm_clk_get_optional(&pdev->dev, NULL); + port->clk = devm_clk_get_optional_enabled(&pdev->dev, NULL); if (IS_ERR(port->clk)) return PTR_ERR(port->clk); - err = clk_prepare_enable(port->clk); - if (err) { - dev_err(&pdev->dev, "Unable to enable clock.\n"); - return err; - } - if (of_device_is_compatible(np, "fsl,imx7d-gpio")) port->power_off = true; @@ -535,7 +529,6 @@ out_irqdomain_remove: out_bgio: pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); - clk_disable_unprepare(port->clk); dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err); return err; } From 38a700efc51080c7184f71edbf5e49561da9754f Mon Sep 17 00:00:00 2001 From: Asmaa Mnebhi Date: Fri, 18 Aug 2023 12:43:14 -0400 Subject: [PATCH 81/94] gpio: mlxbf3: Support add_pin_ranges() Support add_pin_ranges() so that pinctrl_gpio_request() can be called. The GPIO value is not modified when the user runs the "gpioset" tool. This is because when gpiochip_generic_request is invoked by the gpio-mlxbf3 driver, "pin_ranges" is empty so it skips "pinctrl_gpio_request()". pinctrl_gpio_request() is essential in the code flow because it changes the mux value so that software has control over modifying the GPIO value. Adding add_pin_ranges() creates a dependency on the pinctrl-mlxbf3.c driver. Fixes: cd33f216d24 ("gpio: mlxbf3: Add gpio driver support") Signed-off-by: Asmaa Mnebhi Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mlxbf3.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c index e30cee108986..0a5f241a8352 100644 --- a/drivers/gpio/gpio-mlxbf3.c +++ b/drivers/gpio/gpio-mlxbf3.c @@ -19,6 +19,8 @@ * gpio[1]: HOST_GPIO32->HOST_GPIO55 */ #define MLXBF3_GPIO_MAX_PINS_PER_BLOCK 32 +#define MLXBF3_GPIO_MAX_PINS_BLOCK0 32 +#define MLXBF3_GPIO_MAX_PINS_BLOCK1 24 /* * fw_gpio[x] block registers and their offset @@ -158,6 +160,26 @@ static const struct irq_chip gpio_mlxbf3_irqchip = { GPIOCHIP_IRQ_RESOURCE_HELPERS, }; +static int mlxbf3_gpio_add_pin_ranges(struct gpio_chip *chip) +{ + unsigned int id; + + switch(chip->ngpio) { + case MLXBF3_GPIO_MAX_PINS_BLOCK0: + id = 0; + break; + case MLXBF3_GPIO_MAX_PINS_BLOCK1: + id = 1; + break; + default: + return -EINVAL; + } + + return gpiochip_add_pin_range(chip, "MLNXBF34:00", + chip->base, id * MLXBF3_GPIO_MAX_PINS_PER_BLOCK, + chip->ngpio); +} + static int mlxbf3_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -197,6 +219,7 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev) gc->request = gpiochip_generic_request; gc->free = gpiochip_generic_free; gc->owner = THIS_MODULE; + gc->add_pin_ranges = mlxbf3_gpio_add_pin_ranges; irq = platform_get_irq(pdev, 0); if (irq >= 0) { @@ -243,6 +266,7 @@ static struct platform_driver mlxbf3_gpio_driver = { }; module_platform_driver(mlxbf3_gpio_driver); +MODULE_SOFTDEP("pre: pinctrl-mlxbf3"); MODULE_DESCRIPTION("NVIDIA BlueField-3 GPIO Driver"); MODULE_AUTHOR("Asmaa Mnebhi "); MODULE_LICENSE("Dual BSD/GPL"); From 17a7ca35890b411414a71fbeef13cb65fe9511df Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 17 Aug 2023 10:52:28 +0200 Subject: [PATCH 82/94] gpiolib: rename the gpio_device notifier Change the generic "notifier" name to "line_state_notifier" in order to reflect its purpose in preparation for adding a second notifier which will be used to notify wait queues about device unregistering. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Reviewed-by: Kent Gibson --- drivers/gpio/gpiolib-cdev.c | 16 ++++++++-------- drivers/gpio/gpiolib.c | 6 +++--- drivers/gpio/gpiolib.h | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 0a33971c964c..9ee8604f32e1 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -230,7 +230,7 @@ static long linehandle_set_config(struct linehandle_state *lh, return ret; } - blocking_notifier_call_chain(&desc->gdev->notifier, + blocking_notifier_call_chain(&desc->gdev->line_state_notifier, GPIO_V2_LINE_CHANGED_CONFIG, desc); } @@ -414,7 +414,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) goto out_free_lh; } - blocking_notifier_call_chain(&desc->gdev->notifier, + blocking_notifier_call_chain(&desc->gdev->line_state_notifier, GPIO_V2_LINE_CHANGED_REQUESTED, desc); dev_dbg(&gdev->dev, "registered chardev handle for line %d\n", @@ -1407,7 +1407,7 @@ static long linereq_set_config_unlocked(struct linereq *lr, WRITE_ONCE(line->edflags, edflags); - blocking_notifier_call_chain(&desc->gdev->notifier, + blocking_notifier_call_chain(&desc->gdev->line_state_notifier, GPIO_V2_LINE_CHANGED_CONFIG, desc); } @@ -1720,7 +1720,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) lr->lines[i].edflags = edflags; - blocking_notifier_call_chain(&desc->gdev->notifier, + blocking_notifier_call_chain(&desc->gdev->line_state_notifier, GPIO_V2_LINE_CHANGED_REQUESTED, desc); dev_dbg(&gdev->dev, "registered chardev handle for line %d\n", @@ -2117,7 +2117,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) if (ret) goto out_free_le; - blocking_notifier_call_chain(&desc->gdev->notifier, + blocking_notifier_call_chain(&desc->gdev->line_state_notifier, GPIO_V2_LINE_CHANGED_REQUESTED, desc); irq = gpiod_to_irq(desc); @@ -2671,7 +2671,7 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) cdev->gdev = gpio_device_get(gdev); cdev->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify; - ret = blocking_notifier_chain_register(&gdev->notifier, + ret = blocking_notifier_chain_register(&gdev->line_state_notifier, &cdev->lineinfo_changed_nb); if (ret) goto out_free_bitmap; @@ -2687,7 +2687,7 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) return ret; out_unregister_notifier: - blocking_notifier_chain_unregister(&gdev->notifier, + blocking_notifier_chain_unregister(&gdev->line_state_notifier, &cdev->lineinfo_changed_nb); out_free_bitmap: gpio_device_put(gdev); @@ -2711,7 +2711,7 @@ static int gpio_chrdev_release(struct inode *inode, struct file *file) struct gpio_device *gdev = cdev->gdev; bitmap_free(cdev->watched_lines); - blocking_notifier_chain_unregister(&gdev->notifier, + blocking_notifier_chain_unregister(&gdev->line_state_notifier, &cdev->lineinfo_changed_nb); gpio_device_put(gdev); kfree(cdev); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index edab00c9cb3c..38640df1d798 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -847,7 +847,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, spin_unlock_irqrestore(&gpio_lock, flags); - BLOCKING_INIT_NOTIFIER_HEAD(&gdev->notifier); + BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier); init_rwsem(&gdev->sem); #ifdef CONFIG_PINCTRL @@ -2177,7 +2177,7 @@ static bool gpiod_free_commit(struct gpio_desc *desc) } spin_unlock_irqrestore(&gpio_lock, flags); - blocking_notifier_call_chain(&desc->gdev->notifier, + blocking_notifier_call_chain(&desc->gdev->line_state_notifier, GPIOLINE_CHANGED_RELEASED, desc); return ret; @@ -4007,7 +4007,7 @@ static struct gpio_desc *gpiod_find_and_request(struct device *consumer, return ERR_PTR(ret); } - blocking_notifier_call_chain(&desc->gdev->notifier, + blocking_notifier_call_chain(&desc->gdev->line_state_notifier, GPIOLINE_CHANGED_REQUESTED, desc); return desc; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 08e8e8274883..11a5e02f317d 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -39,8 +39,8 @@ * or name of the IP component in a System on Chip. * @data: per-instance data assigned by the driver * @list: links gpio_device:s together for traversal - * @notifier: used to notify subscribers about lines being requested, released - * or reconfigured + * @line_state_notifier: used to notify subscribers about lines being + * requested, released or reconfigured * @sem: protects the structure from a NULL-pointer dereference of @chip by * user-space operations when the device gets unregistered during * a hot-unplug event @@ -64,7 +64,7 @@ struct gpio_device { const char *label; void *data; struct list_head list; - struct blocking_notifier_head notifier; + struct blocking_notifier_head line_state_notifier; struct rw_semaphore sem; #ifdef CONFIG_PINCTRL From e82bbd6761f7e313141c4033ebb79e3d397b6a9c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 17 Aug 2023 13:42:34 +0200 Subject: [PATCH 83/94] gpio: cdev: open-code to_gpio_chardev_data() This function is a wrapper around container_of(). It's used only once and we will have a second notifier soon, so instead of having two flavors of this helper, let's just open-code where needed. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Reviewed-by: Kent Gibson --- drivers/gpio/gpiolib-cdev.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 9ee8604f32e1..91bdd3b928b5 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -2491,16 +2491,11 @@ static long gpio_ioctl_compat(struct file *file, unsigned int cmd, } #endif -static struct gpio_chardev_data * -to_gpio_chardev_data(struct notifier_block *nb) -{ - return container_of(nb, struct gpio_chardev_data, lineinfo_changed_nb); -} - static int lineinfo_changed_notify(struct notifier_block *nb, unsigned long action, void *data) { - struct gpio_chardev_data *cdev = to_gpio_chardev_data(nb); + struct gpio_chardev_data *cdev = + container_of(nb, struct gpio_chardev_data, lineinfo_changed_nb); struct gpio_v2_line_info_changed chg; struct gpio_desc *desc = data; int ret; From a067419ba77da830939852758702388f0fba09a1 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 17 Aug 2023 13:59:05 +0200 Subject: [PATCH 84/94] gpiolib: add a second blocking notifier to struct gpio_device Add a new blocking notifier to struct gpio_device and use it to notify subscribers about the GPIO device being unregistered from the device model. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Reviewed-by: Kent Gibson --- drivers/gpio/gpiolib-cdev.c | 1 + drivers/gpio/gpiolib.c | 1 + drivers/gpio/gpiolib.h | 3 +++ 3 files changed, 5 insertions(+) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 91bdd3b928b5..fec446510028 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -2748,4 +2748,5 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt) void gpiolib_cdev_unregister(struct gpio_device *gdev) { cdev_device_del(&gdev->chrdev, &gdev->dev); + blocking_notifier_call_chain(&gdev->device_notifier, 0, NULL); } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 38640df1d798..8f74e0bb2a5c 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -848,6 +848,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, spin_unlock_irqrestore(&gpio_lock, flags); BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier); + BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier); init_rwsem(&gdev->sem); #ifdef CONFIG_PINCTRL diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 11a5e02f317d..e860ed434bc2 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -41,6 +41,8 @@ * @list: links gpio_device:s together for traversal * @line_state_notifier: used to notify subscribers about lines being * requested, released or reconfigured + * @device_notifier: used to notify character device wait queues about the GPIO + * device being unregistered * @sem: protects the structure from a NULL-pointer dereference of @chip by * user-space operations when the device gets unregistered during * a hot-unplug event @@ -65,6 +67,7 @@ struct gpio_device { void *data; struct list_head list; struct blocking_notifier_head line_state_notifier; + struct blocking_notifier_head device_notifier; struct rw_semaphore sem; #ifdef CONFIG_PINCTRL From d2e2586a3292e05d4872e9111e8b005bc4ff4d09 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 17 Aug 2023 14:42:24 +0200 Subject: [PATCH 85/94] gpio: cdev: wake up chardev poll() on device unbind Add a notifier block to the gpio_chardev_data structure and register it with the gpio_device's device notifier. Upon reception of an event, wake up the wait queue so that the user-space be forced out of poll() and need to go into a new system call which will then fail due to the chip being gone. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Reviewed-by: Kent Gibson --- drivers/gpio/gpiolib-cdev.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index fec446510028..bc1542544e49 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -2320,6 +2320,7 @@ struct gpio_chardev_data { wait_queue_head_t wait; DECLARE_KFIFO(events, struct gpio_v2_line_info_changed, 32); struct notifier_block lineinfo_changed_nb; + struct notifier_block device_unregistered_nb; unsigned long *watched_lines; #ifdef CONFIG_GPIO_CDEV_V1 atomic_t watch_abi_version; @@ -2517,6 +2518,18 @@ static int lineinfo_changed_notify(struct notifier_block *nb, return NOTIFY_OK; } +static int gpio_device_unregistered_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct gpio_chardev_data *cdev = container_of(nb, + struct gpio_chardev_data, + device_unregistered_nb); + + wake_up_poll(&cdev->wait, EPOLLIN | EPOLLERR); + + return NOTIFY_OK; +} + static __poll_t lineinfo_watch_poll_unlocked(struct file *file, struct poll_table_struct *pollt) { @@ -2671,17 +2684,27 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) if (ret) goto out_free_bitmap; + cdev->device_unregistered_nb.notifier_call = + gpio_device_unregistered_notify; + ret = blocking_notifier_chain_register(&gdev->device_notifier, + &cdev->device_unregistered_nb); + if (ret) + goto out_unregister_line_notifier; + file->private_data = cdev; ret = nonseekable_open(inode, file); if (ret) - goto out_unregister_notifier; + goto out_unregister_device_notifier; up_read(&gdev->sem); return ret; -out_unregister_notifier: +out_unregister_device_notifier: + blocking_notifier_chain_unregister(&gdev->device_notifier, + &cdev->device_unregistered_nb); +out_unregister_line_notifier: blocking_notifier_chain_unregister(&gdev->line_state_notifier, &cdev->lineinfo_changed_nb); out_free_bitmap: @@ -2706,6 +2729,8 @@ static int gpio_chrdev_release(struct inode *inode, struct file *file) struct gpio_device *gdev = cdev->gdev; bitmap_free(cdev->watched_lines); + blocking_notifier_chain_unregister(&gdev->device_notifier, + &cdev->device_unregistered_nb); blocking_notifier_chain_unregister(&gdev->line_state_notifier, &cdev->lineinfo_changed_nb); gpio_device_put(gdev); From a0dda508bd66b9eeeedc6fe0597aa8613f4b3c5d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 17 Aug 2023 15:18:58 +0200 Subject: [PATCH 86/94] gpio: cdev: wake up linereq poll() on device unbind Add a notifier block to the linereq structure and register it with the gpio_device's device notifier. Upon reception of an event, wake up the wait queue so that the user-space be forced out of poll() and need to go into a new system call which will then fail due to the chip being gone. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Reviewed-by: Kent Gibson --- drivers/gpio/gpiolib-cdev.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index bc1542544e49..7d0c83127ccb 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -555,6 +555,7 @@ struct line { * @label: consumer label used to tag GPIO descriptors * @num_lines: the number of lines in the lines array * @wait: wait queue that handles blocking reads of events + * @device_unregistered_nb: notifier block for receiving gdev unregister events * @event_buffer_size: the number of elements allocated in @events * @events: KFIFO for the GPIO events * @seqno: the sequence number for edge events generated on all lines in @@ -569,6 +570,7 @@ struct linereq { const char *label; u32 num_lines; wait_queue_head_t wait; + struct notifier_block device_unregistered_nb; u32 event_buffer_size; DECLARE_KFIFO_PTR(events, struct gpio_v2_line_event); atomic_t seqno; @@ -610,6 +612,17 @@ struct linereq { GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE | \ GPIO_V2_LINE_EDGE_FLAGS) +static int linereq_unregistered_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct linereq *lr = container_of(nb, struct linereq, + device_unregistered_nb); + + wake_up_poll(&lr->wait, EPOLLIN | EPOLLERR); + + return NOTIFY_OK; +} + static void linereq_put_event(struct linereq *lr, struct gpio_v2_line_event *le) { @@ -1567,6 +1580,10 @@ static void linereq_free(struct linereq *lr) { unsigned int i; + if (lr->device_unregistered_nb.notifier_call) + blocking_notifier_chain_unregister(&lr->gdev->device_notifier, + &lr->device_unregistered_nb); + for (i = 0; i < lr->num_lines; i++) { if (lr->lines[i].desc) { edge_detector_stop(&lr->lines[i]); @@ -1727,6 +1744,12 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) offset); } + lr->device_unregistered_nb.notifier_call = linereq_unregistered_notify; + ret = blocking_notifier_chain_register(&gdev->device_notifier, + &lr->device_unregistered_nb); + if (ret) + goto out_free_linereq; + fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); if (fd < 0) { ret = fd; From 91043f559313fb04ac18253fcf18561a40c1b045 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 17 Aug 2023 15:28:31 +0200 Subject: [PATCH 87/94] gpio: cdev: wake up lineevent poll() on device unbind Add a notifier block to the lineevent_state structure and register it with the gpio_device's device notifier. Upon reception of an event, wake up the wait queue so that the user-space be forced out of poll() and need to go into a new system call which will then fail due to the chip being gone. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Reviewed-by: Kent Gibson --- drivers/gpio/gpiolib-cdev.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 7d0c83127ccb..35dcaf78aed6 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -1802,6 +1802,7 @@ out_free_linereq: * @eflags: the event flags this line was requested with * @irq: the interrupt that trigger in response to events on this GPIO * @wait: wait queue that handles blocking reads of events + * @device_unregistered_nb: notifier block for receiving gdev unregister events * @events: KFIFO for the GPIO events * @timestamp: cache for the timestamp storing it between hardirq * and IRQ thread, used to bring the timestamp close to the actual @@ -1814,6 +1815,7 @@ struct lineevent_state { u32 eflags; int irq; wait_queue_head_t wait; + struct notifier_block device_unregistered_nb; DECLARE_KFIFO(events, struct gpioevent_data, 16); u64 timestamp; }; @@ -1847,6 +1849,17 @@ static __poll_t lineevent_poll(struct file *file, return call_poll_locked(file, wait, le->gdev, lineevent_poll_unlocked); } +static int lineevent_unregistered_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct lineevent_state *le = container_of(nb, struct lineevent_state, + device_unregistered_nb); + + wake_up_poll(&le->wait, EPOLLIN | EPOLLERR); + + return NOTIFY_OK; +} + struct compat_gpioeevent_data { compat_u64 timestamp; u32 id; @@ -1932,6 +1945,9 @@ static ssize_t lineevent_read(struct file *file, char __user *buf, static void lineevent_free(struct lineevent_state *le) { + if (le->device_unregistered_nb.notifier_call) + blocking_notifier_chain_unregister(&le->gdev->device_notifier, + &le->device_unregistered_nb); if (le->irq) free_irq(le->irq, le); if (le->desc) @@ -2160,6 +2176,12 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) INIT_KFIFO(le->events); init_waitqueue_head(&le->wait); + le->device_unregistered_nb.notifier_call = lineevent_unregistered_notify; + ret = blocking_notifier_chain_register(&gdev->device_notifier, + &le->device_unregistered_nb); + if (ret) + goto out_free_le; + /* Request a thread to read the events */ ret = request_threaded_irq(irq, lineevent_irq_handler, From 9ce4ed5b4db13633f9934a4eac02c4cde04d5c63 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 21 Aug 2023 16:18:27 +0200 Subject: [PATCH 88/94] gpiolib: provide and use gpiod_line_state_notify() Wrap the calls to blocking_notifier_call_chain() for the line state notifier with a helper that allows us to use fewer lines of code and simpler syntax. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko --- drivers/gpio/gpiolib-cdev.c | 17 +++++------------ drivers/gpio/gpiolib.c | 12 ++++++++---- drivers/gpio/gpiolib.h | 1 + 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 35dcaf78aed6..e39d344feb28 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -230,9 +230,7 @@ static long linehandle_set_config(struct linehandle_state *lh, return ret; } - blocking_notifier_call_chain(&desc->gdev->line_state_notifier, - GPIO_V2_LINE_CHANGED_CONFIG, - desc); + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG); } return 0; } @@ -414,8 +412,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) goto out_free_lh; } - blocking_notifier_call_chain(&desc->gdev->line_state_notifier, - GPIO_V2_LINE_CHANGED_REQUESTED, desc); + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED); dev_dbg(&gdev->dev, "registered chardev handle for line %d\n", offset); @@ -1420,9 +1417,7 @@ static long linereq_set_config_unlocked(struct linereq *lr, WRITE_ONCE(line->edflags, edflags); - blocking_notifier_call_chain(&desc->gdev->line_state_notifier, - GPIO_V2_LINE_CHANGED_CONFIG, - desc); + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG); } return 0; } @@ -1737,8 +1732,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) lr->lines[i].edflags = edflags; - blocking_notifier_call_chain(&desc->gdev->line_state_notifier, - GPIO_V2_LINE_CHANGED_REQUESTED, desc); + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED); dev_dbg(&gdev->dev, "registered chardev handle for line %d\n", offset); @@ -2156,8 +2150,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) if (ret) goto out_free_le; - blocking_notifier_call_chain(&desc->gdev->line_state_notifier, - GPIO_V2_LINE_CHANGED_REQUESTED, desc); + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED); irq = gpiod_to_irq(desc); if (irq <= 0) { diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 8f74e0bb2a5c..810bc09467a9 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2178,8 +2178,7 @@ static bool gpiod_free_commit(struct gpio_desc *desc) } spin_unlock_irqrestore(&gpio_lock, flags); - blocking_notifier_call_chain(&desc->gdev->line_state_notifier, - GPIOLINE_CHANGED_RELEASED, desc); + gpiod_line_state_notify(desc, GPIOLINE_CHANGED_RELEASED); return ret; } @@ -3741,6 +3740,12 @@ int gpiod_set_array_value_cansleep(unsigned int array_size, } EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); +void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action) +{ + blocking_notifier_call_chain(&desc->gdev->line_state_notifier, + action, desc); +} + /** * gpiod_add_lookup_table() - register GPIO device consumers * @table: table of consumers to register @@ -4008,8 +4013,7 @@ static struct gpio_desc *gpiod_find_and_request(struct device *consumer, return ERR_PTR(ret); } - blocking_notifier_call_chain(&desc->gdev->line_state_notifier, - GPIOLINE_CHANGED_REQUESTED, desc); + gpiod_line_state_notify(desc, GPIOLINE_CHANGED_REQUESTED); return desc; } diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index e860ed434bc2..a0a67569300b 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -147,6 +147,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, extern spinlock_t gpio_lock; extern struct list_head gpio_devices; +void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action); /** * struct gpio_desc - Opaque descriptor for a GPIO From bb5ad5ef7493ed21abc2c8b890204c8f79df86a8 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Sat, 6 May 2023 16:59:27 +0800 Subject: [PATCH 89/94] gpio: vf610: switch to dynamic allocat GPIO base gpiolib want to get completely rid of static gpiobase allocation, so switch to dynamic allocat GPIO base, also can avoid warning message: [ 1.529974] gpio gpiochip0: Static allocation of GPIO base is deprecated, use dynamic allocation. Signed-off-by: Haibo Chen Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-vf610.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index 54e7c51f48c8..dbc7ba0ee72c 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -258,7 +258,6 @@ static void vf610_gpio_disable_clk(void *data) static int vf610_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; struct vf610_gpio_port *port; struct gpio_chip *gc; struct gpio_irq_chip *girq; @@ -318,7 +317,7 @@ static int vf610_gpio_probe(struct platform_device *pdev) gc->parent = dev; gc->label = dev_name(dev); gc->ngpio = VF610_GPIO_PER_PORT; - gc->base = of_alias_get_id(np, "gpio") * VF610_GPIO_PER_PORT; + gc->base = -1; gc->request = gpiochip_generic_request; gc->free = gpiochip_generic_free; From db3b16dcc7e4f5bfbe35806a54fb15dc858d19f8 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 19 Aug 2023 19:04:43 +0100 Subject: [PATCH 90/94] gpio: pcf857x: Extend match data support for OF tables The driver has OF match table, but still it uses an ID lookup table for retrieving match data. Currently, the driver is working on the assumption that an I2C device registered via OF will always match a legacy I2C device ID. Extend match data support for OF tables by using i2c_get_match_data() instead of the ID lookup for both OF/ID matches by making similar OF/ID tables. Signed-off-by: Biju Das Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pcf857x.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index c4c785548408..53b69abe6787 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -36,19 +36,19 @@ static const struct i2c_device_id pcf857x_id[] = { MODULE_DEVICE_TABLE(i2c, pcf857x_id); static const struct of_device_id pcf857x_of_table[] = { - { .compatible = "nxp,pcf8574" }, - { .compatible = "nxp,pcf8574a" }, - { .compatible = "nxp,pca8574" }, - { .compatible = "nxp,pca9670" }, - { .compatible = "nxp,pca9672" }, - { .compatible = "nxp,pca9674" }, - { .compatible = "nxp,pcf8575" }, - { .compatible = "nxp,pca8575" }, - { .compatible = "nxp,pca9671" }, - { .compatible = "nxp,pca9673" }, - { .compatible = "nxp,pca9675" }, - { .compatible = "maxim,max7328" }, - { .compatible = "maxim,max7329" }, + { .compatible = "nxp,pcf8574", (void *)8 }, + { .compatible = "nxp,pcf8574a", (void *)8 }, + { .compatible = "nxp,pca8574", (void *)8 }, + { .compatible = "nxp,pca9670", (void *)8 }, + { .compatible = "nxp,pca9672", (void *)8 }, + { .compatible = "nxp,pca9674", (void *)8 }, + { .compatible = "nxp,pcf8575", (void *)16 }, + { .compatible = "nxp,pca8575", (void *)16 }, + { .compatible = "nxp,pca9671", (void *)16 }, + { .compatible = "nxp,pca9673", (void *)16 }, + { .compatible = "nxp,pca9675", (void *)16 }, + { .compatible = "maxim,max7328", (void *)8 }, + { .compatible = "maxim,max7329", (void *)8 }, { } }; MODULE_DEVICE_TABLE(of, pcf857x_of_table); @@ -272,7 +272,6 @@ static const struct irq_chip pcf857x_irq_chip = { static int pcf857x_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); struct pcf857x *gpio; unsigned int n_latch = 0; int status; @@ -296,7 +295,7 @@ static int pcf857x_probe(struct i2c_client *client) gpio->chip.set_multiple = pcf857x_set_multiple; gpio->chip.direction_input = pcf857x_input; gpio->chip.direction_output = pcf857x_output; - gpio->chip.ngpio = id->driver_data; + gpio->chip.ngpio = (uintptr_t)i2c_get_match_data(client); /* NOTE: the OnSemi jlc1562b is also largely compatible with * these parts, notably for output. It has a low-resolution From 1d2a22fa6d2511d5871d87c15b4fe7a944fe3b2a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 23 Aug 2023 10:52:58 +0200 Subject: [PATCH 91/94] gpio: mlxbf3: use capital "OR" for multiple licenses in SPDX Documentation/process/license-rules.rst and checkpatch expect the SPDX identifier syntax for multiple licenses to use capital "OR". Correct it to keep consistent format and avoid copy-paste issues. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mlxbf3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c index 0a5f241a8352..7a3e1760fc5b 100644 --- a/drivers/gpio/gpio-mlxbf3.c +++ b/drivers/gpio/gpio-mlxbf3.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only or BSD-3-Clause +// SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause /* Copyright (C) 2022 NVIDIA CORPORATION & AFFILIATES */ #include From ea05787136256b078fab3cc7db33caf24678ee47 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 24 Aug 2023 17:00:17 +0100 Subject: [PATCH 92/94] gpio: pca953x: Use i2c_get_match_data() Replace device_get_match_data() and id lookup for retrieving match data by i2c_get_match_data(). Signed-off-by: Biju Das Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca953x.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index a806a3c1b801..929a2d075fe4 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -1051,7 +1051,6 @@ out: static int pca953x_probe(struct i2c_client *client) { - const struct i2c_device_id *i2c_id = i2c_client_get_device_id(client); struct pca953x_platform_data *pdata; struct pca953x_chip *chip; int irq_base = 0; @@ -1090,6 +1089,9 @@ static int pca953x_probe(struct i2c_client *client) } chip->client = client; + chip->driver_data = (uintptr_t)i2c_get_match_data(client); + if (!chip->driver_data) + return -ENODEV; reg = devm_regulator_get(&client->dev, "vcc"); if (IS_ERR(reg)) @@ -1102,20 +1104,6 @@ static int pca953x_probe(struct i2c_client *client) } chip->regulator = reg; - if (i2c_id) { - chip->driver_data = i2c_id->driver_data; - } else { - const void *match; - - match = device_get_match_data(&client->dev); - if (!match) { - ret = -ENODEV; - goto err_exit; - } - - chip->driver_data = (uintptr_t)match; - } - i2c_set_clientdata(client, chip); pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK); From 1b961a75abfc043b2e5a8f97e1939d12242f4346 Mon Sep 17 00:00:00 2001 From: Liam Beguin Date: Thu, 24 Aug 2023 19:16:26 -0400 Subject: [PATCH 93/94] dt-bindings: gpio: pca95xx: document new tca9538 chip Add a compatible for tca9538. Signed-off-by: Liam Beguin Acked-by: Krzysztof Kozlowski [Bartosz: tweaked the commit message] Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml index fa116148ee90..99febb8ea1b6 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml @@ -66,6 +66,7 @@ properties: - ti,tca6408 - ti,tca6416 - ti,tca6424 + - ti,tca9538 - ti,tca9539 - ti,tca9554 From 3d0957b07e27abd3237b1fe0c7f06485ba80852f Mon Sep 17 00:00:00 2001 From: Liam Beguin Date: Thu, 24 Aug 2023 19:16:25 -0400 Subject: [PATCH 94/94] gpio: pca953x: add support for TCA9538 The TCA9538 is an 8 bit version of the already supported TCA9539. This chip also has interrupt support. Signed-off-by: Liam Beguin Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca953x.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 929a2d075fe4..bdd50a78e414 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -108,6 +108,7 @@ static const struct i2c_device_id pca953x_id[] = { { "tca6408", 8 | PCA953X_TYPE | PCA_INT, }, { "tca6416", 16 | PCA953X_TYPE | PCA_INT, }, { "tca6424", 24 | PCA953X_TYPE | PCA_INT, }, + { "tca9538", 8 | PCA953X_TYPE | PCA_INT, }, { "tca9539", 16 | PCA953X_TYPE | PCA_INT, }, { "tca9554", 8 | PCA953X_TYPE | PCA_INT, }, { "xra1202", 8 | PCA953X_TYPE }, @@ -1342,6 +1343,7 @@ static const struct of_device_id pca953x_dt_ids[] = { { .compatible = "ti,tca6408", .data = OF_953X( 8, PCA_INT), }, { .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), }, { .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), }, + { .compatible = "ti,tca9538", .data = OF_953X( 8, PCA_INT), }, { .compatible = "ti,tca9539", .data = OF_953X(16, PCA_INT), }, { .compatible = "onnn,cat9554", .data = OF_953X( 8, PCA_INT), },