From de454ac4fc5a117a4264e8bdf60fca58021574b1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 14 Jan 2025 20:14:38 +0100 Subject: [PATCH 001/119] gpio: Use str_enable_disable-like helpers Replace ternary (condition ? "enable" : "disable") syntax with helpers from string_choices.h because: 1. Simple function call with one argument is easier to read. Ternary operator has three arguments and with wrapping might lead to quite long code. 2. Is slightly shorter thus also easier to read. 3. It brings uniformity in the text - same string. 4. Allows deduping by the linker, which results in a smaller binary file. Reviewed-by: Florian Fainelli Signed-off-by: Krzysztof Kozlowski Acked-by: Doug Berger Reviewed-by: Charles Keepax Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250114191438.857656-1-krzysztof.kozlowski@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-brcmstb.c | 3 ++- drivers/gpio/gpio-crystalcove.c | 3 ++- drivers/gpio/gpio-grgpio.c | 3 ++- drivers/gpio/gpio-mvebu.c | 7 ++++--- drivers/gpio/gpio-nomadik.c | 3 ++- drivers/gpio/gpio-stmpe.c | 6 +++--- drivers/gpio/gpio-wcove.c | 3 ++- drivers/gpio/gpio-wm831x.c | 3 ++- drivers/gpio/gpio-xra1403.c | 3 ++- drivers/gpio/gpiolib.c | 3 ++- 10 files changed, 23 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index 491b529d25f8..ca3472977431 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -9,6 +9,7 @@ #include #include #include +#include enum gio_reg_index { GIO_REG_ODEN = 0, @@ -224,7 +225,7 @@ static int brcmstb_gpio_priv_set_wake(struct brcmstb_gpio_priv *priv, ret = disable_irq_wake(priv->parent_wake_irq); if (ret) dev_err(&priv->pdev->dev, "failed to %s wake-up interrupt\n", - enable ? "enable" : "disable"); + str_enable_disable(enable)); return ret; } diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 25db014494a4..56effd0f50c7 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #define CRYSTALCOVE_GPIO_NUM 16 @@ -317,7 +318,7 @@ static void crystalcove_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip offset = gpio % 8; seq_printf(s, " gpio-%-2d %s %s %s %s ctlo=%2x,%s %s %s\n", gpio, ctlo & CTLO_DIR_OUT ? "out" : "in ", - ctli & 0x1 ? "hi" : "lo", + str_hi_lo(ctli & 0x1), ctli & CTLI_INTCNT_NE ? "fall" : " ", ctli & CTLI_INTCNT_PE ? "rise" : " ", ctlo, diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 169f33c41c59..30a0522ae735 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -30,6 +30,7 @@ #include #include #include +#include #define GRGPIO_MAX_NGPIO 32 @@ -438,7 +439,7 @@ static int grgpio_probe(struct platform_device *ofdev) } dev_info(dev, "regs=0x%p, base=%d, ngpio=%d, irqs=%s\n", - priv->regs, gc->base, gc->ngpio, priv->domain ? "on" : "off"); + priv->regs, gc->base, gc->ngpio, str_on_off(priv->domain)); return 0; } diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 5ffb332e9849..363bad286c32 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -49,6 +49,7 @@ #include #include #include +#include /* * GPIO unit register offsets. @@ -907,14 +908,14 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) if (is_out) { seq_printf(s, " out %s %s\n", - out & msk ? "hi" : "lo", + str_hi_lo(out & msk), blink & msk ? "(blink )" : ""); continue; } seq_printf(s, " in %s (act %s) - IRQ", - (data_in ^ in_pol) & msk ? "hi" : "lo", - in_pol & msk ? "lo" : "hi"); + str_hi_lo((data_in ^ in_pol) & msk), + str_lo_hi(in_pol & msk)); if (!((edg_msk | lvl_msk) & msk)) { seq_puts(s, " disabled\n"); continue; diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c index 836f1cc760c2..fa19a44943fd 100644 --- a/drivers/gpio/gpio-nomadik.c +++ b/drivers/gpio/gpio-nomadik.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -430,7 +431,7 @@ void nmk_gpio_dbg_show_one(struct seq_file *s, struct pinctrl_dev *pctldev, seq_printf(s, " gpio-%-3d (%-20.20s) out %s %s", gpio, label ?: "(none)", - data_out ? "hi" : "lo", + str_hi_lo(data_out), (mode < 0) ? "unknown" : modes[mode]); } else { int irq = chip->to_irq(chip, offset); diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 75a3633ceddb..2e22e1eb7495 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -15,6 +15,7 @@ #include #include #include +#include /* * These registers are modified under the irq bus lock and cached to avoid @@ -273,8 +274,7 @@ static void stmpe_dbg_show_one(struct seq_file *s, if (dir) { seq_printf(s, " gpio-%-3d (%-20.20s) out %s", - gpio, label ?: "(none)", - val ? "hi" : "lo"); + gpio, label ?: "(none)", str_hi_lo(val)); } else { u8 edge_det_reg; u8 rise_reg; @@ -343,7 +343,7 @@ static void stmpe_dbg_show_one(struct seq_file *s, seq_printf(s, " gpio-%-3d (%-20.20s) in %s %13s %13s %25s %25s", gpio, label ?: "(none)", - val ? "hi" : "lo", + str_hi_lo(val), edge_det_values[edge_det], irqen ? "IRQ-enabled" : "IRQ-disabled", rise_values[rise], diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index 94ca9d03c094..1ec24f6f9300 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -15,6 +15,7 @@ #include #include #include +#include /* * Whiskey Cove PMIC has 13 physical GPIO pins divided into 3 banks: @@ -393,7 +394,7 @@ static void wcove_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) seq_printf(s, " gpio-%-2d %s %s %s %s ctlo=%2x,%s %s\n", gpio, ctlo & CTLO_DIR_OUT ? "out" : "in ", - ctli & 0x1 ? "hi" : "lo", + str_hi_lo(ctli & 0x1), ctli & CTLI_INTCNT_NE ? "fall" : " ", ctli & CTLI_INTCNT_PE ? "rise" : " ", ctlo, diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c index f7d5120ff8f1..61bb83a1e8ae 100644 --- a/drivers/gpio/gpio-wm831x.c +++ b/drivers/gpio/gpio-wm831x.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -234,7 +235,7 @@ static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) seq_printf(s, " %s %s %s %s%s\n" " %s%s (0x%4x)\n", reg & WM831X_GPN_DIR ? "in" : "out", - wm831x_gpio_get(chip, i) ? "high" : "low", + str_high_low(wm831x_gpio_get(chip, i)), pull, powerdomain, reg & WM831X_GPN_POL ? "" : " inverted", diff --git a/drivers/gpio/gpio-xra1403.c b/drivers/gpio/gpio-xra1403.c index dc2710c21c50..842cf875bb92 100644 --- a/drivers/gpio/gpio-xra1403.c +++ b/drivers/gpio/gpio-xra1403.c @@ -13,6 +13,7 @@ #include #include #include +#include #include /* XRA1403 registers */ @@ -140,7 +141,7 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip) seq_printf(s, " gpio-%-3d (%-12s) %s %s\n", chip->base + i, label, (gcr & BIT(i)) ? "in" : "out", - (gsr & BIT(i)) ? "hi" : "lo"); + str_hi_lo(gsr & BIT(i))); } } #else diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 679ed764cb14..be3351583508 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -5007,7 +5008,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) seq_printf(s, " gpio-%-3u (%-20.20s|%-20.20s) %s %s %s%s\n", gpio, desc->name ?: "", gpiod_get_label(desc), is_out ? "out" : "in ", - value >= 0 ? (value ? "hi" : "lo") : "? ", + value >= 0 ? str_hi_lo(value) : "? ", is_irq ? "IRQ " : "", active_low ? "ACTIVE LOW" : ""); } else if (desc->name) { From e8f2ca6be61f1cae2ff12932fa03224581b6b231 Mon Sep 17 00:00:00 2001 From: Ninad Palsule Date: Tue, 4 Feb 2025 13:41:05 -0600 Subject: [PATCH 002/119] dt-bindings: gpio: ast2400-gpio: Add hogs parsing Allow parsing GPIO controller children nodes with GPIO hogs. Reviewed-by: Rob Herring (Arm) Signed-off-by: Ninad Palsule Link: https://lore.kernel.org/r/20250204194115.3899174-3-ninad@linux.ibm.com Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/aspeed,ast2400-gpio.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/aspeed,ast2400-gpio.yaml b/Documentation/devicetree/bindings/gpio/aspeed,ast2400-gpio.yaml index b9afd07a9d24..b16273e69dfe 100644 --- a/Documentation/devicetree/bindings/gpio/aspeed,ast2400-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/aspeed,ast2400-gpio.yaml @@ -46,6 +46,12 @@ properties: minimum: 12 maximum: 232 +patternProperties: + "-hog(-[0-9]+)?$": + type: object + required: + - gpio-hog + required: - compatible - reg From 84693df49dac458e980eaf37f26ae6e23ead98d5 Mon Sep 17 00:00:00 2001 From: hlleng Date: Mon, 10 Feb 2025 19:49:35 +0800 Subject: [PATCH 003/119] gpio: virtio: support multiple virtio-gpio controller instances Modify the virtio-gpio driver to support multiple virtual GPIO controller instances. The previous static global irq_chip structure caused conflicts between multiple virtio-gpio device instances as they shared the same interrupt controller configuration. Fix this by: 1. Remove the static global vgpio_irq_chip structure 2. Dynamically allocate a dedicated irq_chip for each virtio-gpio instance 3. Use device-specific names for each instance's irq_chip Signed-off-by: hlleng Acked-by: Viresh Kumar Link: https://lore.kernel.org/r/20250210114935.204309-1-a909204013@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-virtio.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-virtio.c b/drivers/gpio/gpio-virtio.c index 93544ff62513..ac39da17a29b 100644 --- a/drivers/gpio/gpio-virtio.c +++ b/drivers/gpio/gpio-virtio.c @@ -350,19 +350,6 @@ static void virtio_gpio_irq_bus_sync_unlock(struct irq_data *d) mutex_unlock(&vgpio->irq_lock); } -static struct irq_chip vgpio_irq_chip = { - .name = "virtio-gpio", - .irq_enable = virtio_gpio_irq_enable, - .irq_disable = virtio_gpio_irq_disable, - .irq_mask = virtio_gpio_irq_mask, - .irq_unmask = virtio_gpio_irq_unmask, - .irq_set_type = virtio_gpio_irq_set_type, - - /* These are required to implement irqchip for slow busses */ - .irq_bus_lock = virtio_gpio_irq_bus_lock, - .irq_bus_sync_unlock = virtio_gpio_irq_bus_sync_unlock, -}; - static bool ignore_irq(struct virtio_gpio *vgpio, int gpio, struct vgpio_irq_line *irq_line) { @@ -542,6 +529,7 @@ static int virtio_gpio_probe(struct virtio_device *vdev) struct virtio_gpio_config config; struct device *dev = &vdev->dev; struct virtio_gpio *vgpio; + struct irq_chip *gpio_irq_chip; u32 gpio_names_size; u16 ngpio; int ret, i; @@ -591,13 +579,26 @@ static int virtio_gpio_probe(struct virtio_device *vdev) if (!vgpio->irq_lines) return -ENOMEM; + gpio_irq_chip = devm_kzalloc(dev, sizeof(*gpio_irq_chip), GFP_KERNEL); + if (!gpio_irq_chip) + return -ENOMEM; + + gpio_irq_chip->name = dev_name(dev); + gpio_irq_chip->irq_enable = virtio_gpio_irq_enable; + gpio_irq_chip->irq_disable = virtio_gpio_irq_disable; + gpio_irq_chip->irq_mask = virtio_gpio_irq_mask; + gpio_irq_chip->irq_unmask = virtio_gpio_irq_unmask; + gpio_irq_chip->irq_set_type = virtio_gpio_irq_set_type; + gpio_irq_chip->irq_bus_lock = virtio_gpio_irq_bus_lock; + gpio_irq_chip->irq_bus_sync_unlock = virtio_gpio_irq_bus_sync_unlock; + /* The event comes from the outside so no parent handler */ vgpio->gc.irq.parent_handler = NULL; vgpio->gc.irq.num_parents = 0; vgpio->gc.irq.parents = NULL; vgpio->gc.irq.default_type = IRQ_TYPE_NONE; vgpio->gc.irq.handler = handle_level_irq; - vgpio->gc.irq.chip = &vgpio_irq_chip; + vgpio->gc.irq.chip = gpio_irq_chip; for (i = 0; i < ngpio; i++) { vgpio->irq_lines[i].type = VIRTIO_GPIO_IRQ_TYPE_NONE; From cd323c6e62dd98035c141b6f751e5b2b7d490b2e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 7 Feb 2025 17:17:08 +0200 Subject: [PATCH 004/119] gpio: 74x164: Remove unneeded dependency to OF_GPIO Remove unneeded dependency to OF_GPIO which driver does not use. Fixes: 3c7469514dbe ("gpio: 74x164: Make use of device properties") Reviewed-by: Geert Uytterhoeven Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250207151825.2122419-2-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index add5ad29a673..56c1f30ac195 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1790,7 +1790,6 @@ menu "SPI GPIO expanders" config GPIO_74X164 tristate "74x164 serial-in/parallel-out 8-bits shift register" - depends on OF_GPIO help Driver for 74x164 compatible serial-in/parallel-out 8-outputs shift registers. This driver can be used to provide access From bdd603acf6a2b5056dc174e52bc8b285da529dc4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 7 Feb 2025 17:17:09 +0200 Subject: [PATCH 005/119] gpio: 74x164: Simplify code with cleanup helpers Use macros defined in linux/cleanup.h to automate resource lifetime control in the driver. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250207151825.2122419-3-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-74x164.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index fca6cd2eb1dd..70c662bbca7b 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -43,13 +44,10 @@ static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset) struct gen_74x164_chip *chip = gpiochip_get_data(gc); u8 bank = chip->registers - 1 - offset / 8; u8 pin = offset % 8; - int ret; - mutex_lock(&chip->lock); - ret = (chip->buffer[bank] >> pin) & 0x1; - mutex_unlock(&chip->lock); + guard(mutex)(&chip->lock); - return ret; + return (chip->buffer[bank] >> pin) & 0x1; } static void gen_74x164_set_value(struct gpio_chip *gc, @@ -59,14 +57,14 @@ static void gen_74x164_set_value(struct gpio_chip *gc, u8 bank = chip->registers - 1 - offset / 8; u8 pin = offset % 8; - mutex_lock(&chip->lock); + guard(mutex)(&chip->lock); + if (val) chip->buffer[bank] |= (1 << pin); else chip->buffer[bank] &= ~(1 << pin); __gen_74x164_write_config(chip); - mutex_unlock(&chip->lock); } static void gen_74x164_set_multiple(struct gpio_chip *gc, unsigned long *mask, @@ -78,7 +76,8 @@ static void gen_74x164_set_multiple(struct gpio_chip *gc, unsigned long *mask, size_t bank; unsigned long bitmask; - mutex_lock(&chip->lock); + guard(mutex)(&chip->lock); + for_each_set_clump8(offset, bankmask, mask, chip->registers * 8) { bank = chip->registers - 1 - offset / 8; bitmask = bitmap_get_value8(bits, offset) & bankmask; @@ -87,7 +86,6 @@ static void gen_74x164_set_multiple(struct gpio_chip *gc, unsigned long *mask, chip->buffer[bank] |= bitmask; } __gen_74x164_write_config(chip); - mutex_unlock(&chip->lock); } static int gen_74x164_direction_output(struct gpio_chip *gc, From d746cc6e64027e331769f871a595a3dd2c6b30ff Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 7 Feb 2025 17:17:10 +0200 Subject: [PATCH 006/119] gpio: 74x164: Annotate buffer with __counted_by() Add the __counted_by() compiler attribute to the flexible array member volumes to improve access bounds-checking via CONFIG_UBSAN_BOUNDS and CONFIG_FORTIFY_SOURCE. Use struct_size() instead of manually calculating the number of bytes to allocate the private structure with a buffer. Reviewed-by: Gustavo A. R. Silva Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250207151825.2122419-4-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-74x164.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index 70c662bbca7b..7844f8a58834 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -30,7 +30,7 @@ struct gen_74x164_chip { * register at the end of the transfer. So, to have a logical * numbering, store the bytes in reverse order. */ - u8 buffer[]; + u8 buffer[] __counted_by(registers); }; static int __gen_74x164_write_config(struct gen_74x164_chip *chip) @@ -97,6 +97,7 @@ static int gen_74x164_direction_output(struct gpio_chip *gc, static int gen_74x164_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; struct gen_74x164_chip *chip; u32 nregs; int ret; @@ -116,10 +117,12 @@ static int gen_74x164_probe(struct spi_device *spi) return -EINVAL; } - chip = devm_kzalloc(&spi->dev, sizeof(*chip) + nregs, GFP_KERNEL); + chip = devm_kzalloc(dev, struct_size(chip, buffer, nregs), GFP_KERNEL); if (!chip) return -ENOMEM; + chip->registers = nregs; + chip->gpiod_oe = devm_gpiod_get_optional(&spi->dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(chip->gpiod_oe)) @@ -133,10 +136,7 @@ static int gen_74x164_probe(struct spi_device *spi) chip->gpio_chip.set = gen_74x164_set_value; chip->gpio_chip.set_multiple = gen_74x164_set_multiple; chip->gpio_chip.base = -1; - - chip->registers = nregs; chip->gpio_chip.ngpio = GEN_74X164_NUMBER_GPIOS * chip->registers; - chip->gpio_chip.can_sleep = true; chip->gpio_chip.parent = &spi->dev; chip->gpio_chip.owner = THIS_MODULE; From e742e6b02d858ff9f6a7b43d0b1b5aae9c7e5cf5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 7 Feb 2025 17:17:11 +0200 Subject: [PATCH 007/119] gpio: 74x164: Make use of the macros from bits.h Make use of BIT() and GENMASK() where it makes sense. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250207151825.2122419-5-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-74x164.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index 7844f8a58834..0f720d539fa7 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -47,7 +47,7 @@ static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset) guard(mutex)(&chip->lock); - return (chip->buffer[bank] >> pin) & 0x1; + return !!(chip->buffer[bank] & BIT(pin)); } static void gen_74x164_set_value(struct gpio_chip *gc, @@ -60,9 +60,9 @@ static void gen_74x164_set_value(struct gpio_chip *gc, guard(mutex)(&chip->lock); if (val) - chip->buffer[bank] |= (1 << pin); + chip->buffer[bank] |= BIT(pin); else - chip->buffer[bank] &= ~(1 << pin); + chip->buffer[bank] &= ~BIT(pin); __gen_74x164_write_config(chip); } From abe3817fa1dcae480ec7b71ec1608454cb65d0b8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 7 Feb 2025 17:17:12 +0200 Subject: [PATCH 008/119] gpio: 74x164: Fully convert to use managed resources Convert the driver probe stage to use managed resources. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250207151825.2122419-6-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-74x164.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index 0f720d539fa7..ecd691de8539 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -95,6 +95,19 @@ static int gen_74x164_direction_output(struct gpio_chip *gc, return 0; } +static void gen_74x164_deactivate(void *data) +{ + struct gen_74x164_chip *chip = data; + + gpiod_set_value_cansleep(chip->gpiod_oe, 0); +} + +static int gen_74x164_activate(struct device *dev, struct gen_74x164_chip *chip) +{ + gpiod_set_value_cansleep(chip->gpiod_oe, 1); + return devm_add_action_or_reset(dev, gen_74x164_deactivate, chip); +} + static int gen_74x164_probe(struct spi_device *spi) { struct device *dev = &spi->dev; @@ -128,8 +141,6 @@ static int gen_74x164_probe(struct spi_device *spi) if (IS_ERR(chip->gpiod_oe)) return PTR_ERR(chip->gpiod_oe); - spi_set_drvdata(spi, chip); - chip->gpio_chip.label = spi->modalias; chip->gpio_chip.direction_output = gen_74x164_direction_output; chip->gpio_chip.get = gen_74x164_get_value; @@ -149,18 +160,13 @@ static int gen_74x164_probe(struct spi_device *spi) if (ret) return dev_err_probe(&spi->dev, ret, "Config write failed\n"); - gpiod_set_value_cansleep(chip->gpiod_oe, 1); + ret = gen_74x164_activate(dev, chip); + if (ret) + return ret; return devm_gpiochip_add_data(&spi->dev, &chip->gpio_chip, chip); } -static void gen_74x164_remove(struct spi_device *spi) -{ - struct gen_74x164_chip *chip = spi_get_drvdata(spi); - - gpiod_set_value_cansleep(chip->gpiod_oe, 0); -} - static const struct spi_device_id gen_74x164_spi_ids[] = { { .name = "74hc595" }, { .name = "74lvc594" }, @@ -181,7 +187,6 @@ static struct spi_driver gen_74x164_driver = { .of_match_table = gen_74x164_dt_ids, }, .probe = gen_74x164_probe, - .remove = gen_74x164_remove, .id_table = gen_74x164_spi_ids, }; module_spi_driver(gen_74x164_driver); From 9bd2dbe4066b3821b68f3e18ba91a3a1cd6354df Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 7 Feb 2025 17:17:13 +0200 Subject: [PATCH 009/119] gpio: 74x164: Switch to use dev_err_probe() Switch to use dev_err_probe() to simplify the error path and unify a message template. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250207151825.2122419-7-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-74x164.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index ecd691de8539..bbeef03e3797 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -125,10 +125,8 @@ static int gen_74x164_probe(struct spi_device *spi) return ret; ret = device_property_read_u32(&spi->dev, "registers-number", &nregs); - if (ret) { - dev_err(&spi->dev, "Missing 'registers-number' property.\n"); - return -EINVAL; - } + if (ret) + return dev_err_probe(dev, ret, "Missing 'registers-number' property.\n"); chip = devm_kzalloc(dev, struct_size(chip, buffer, nregs), GFP_KERNEL); if (!chip) From 5892cfc7db9814a71c991da7024fa03384e48924 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 7 Feb 2025 17:17:14 +0200 Subject: [PATCH 010/119] gpio: 74x164: Utilise temporary variable for struct device We have a temporary variable to keep a pointer to struct device. Utilise it where it makes sense. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250207151825.2122419-8-andriy.shevchenko@linux.intel.com [Bartosz: finish the job by converting three more instances] Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-74x164.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index bbeef03e3797..640ac24b72a2 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -124,7 +124,7 @@ static int gen_74x164_probe(struct spi_device *spi) if (ret < 0) return ret; - ret = device_property_read_u32(&spi->dev, "registers-number", &nregs); + ret = device_property_read_u32(dev, "registers-number", &nregs); if (ret) return dev_err_probe(dev, ret, "Missing 'registers-number' property.\n"); @@ -134,8 +134,7 @@ static int gen_74x164_probe(struct spi_device *spi) chip->registers = nregs; - chip->gpiod_oe = devm_gpiod_get_optional(&spi->dev, "enable", - GPIOD_OUT_LOW); + chip->gpiod_oe = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(chip->gpiod_oe)) return PTR_ERR(chip->gpiod_oe); @@ -147,22 +146,22 @@ static int gen_74x164_probe(struct spi_device *spi) chip->gpio_chip.base = -1; chip->gpio_chip.ngpio = GEN_74X164_NUMBER_GPIOS * chip->registers; chip->gpio_chip.can_sleep = true; - chip->gpio_chip.parent = &spi->dev; + chip->gpio_chip.parent = dev; chip->gpio_chip.owner = THIS_MODULE; - ret = devm_mutex_init(&spi->dev, &chip->lock); + ret = devm_mutex_init(dev, &chip->lock); if (ret) return ret; ret = __gen_74x164_write_config(chip); if (ret) - return dev_err_probe(&spi->dev, ret, "Config write failed\n"); + return dev_err_probe(dev, ret, "Config write failed\n"); ret = gen_74x164_activate(dev, chip); if (ret) return ret; - return devm_gpiochip_add_data(&spi->dev, &chip->gpio_chip, chip); + return devm_gpiochip_add_data(dev, &chip->gpio_chip, chip); } static const struct spi_device_id gen_74x164_spi_ids[] = { From 5f05e9194ada5609edbefb542c0dbfdbae984958 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 5 Feb 2025 13:29:36 +0200 Subject: [PATCH 011/119] gpiolib: Even more opportunities to use str_high_low() helper The one of previous changes modified the library code to use helpers from string_choices.h. Nevertheless it misses more opportunities to convert the code. Here is the second part of the conversion. Cc: Krzysztof Kozlowski Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250205112936.575493-1-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index be3351583508..618fe2eebf38 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -4712,10 +4712,10 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, return ret; } - gpiod_dbg(desc, "hogged as %s%s\n", + gpiod_dbg(desc, "hogged as %s/%s\n", (dflags & GPIOD_FLAGS_BIT_DIR_OUT) ? "output" : "input", (dflags & GPIOD_FLAGS_BIT_DIR_OUT) ? - (dflags & GPIOD_FLAGS_BIT_DIR_VAL) ? "/high" : "/low" : ""); + str_high_low(dflags & GPIOD_FLAGS_BIT_DIR_VAL) : "?"); return 0; } From 91931af18bd22437e08e2471f5484d6fbdd8ab93 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 10 Feb 2025 16:33:27 -0600 Subject: [PATCH 012/119] gpiolib: add gpiod_multi_set_value_cansleep() Add a new gpiod_multi_set_value_cansleep() helper function with fewer parameters than gpiod_set_array_value_cansleep(). Calling gpiod_set_array_value_cansleep() can get quite verbose. In many cases, the first arguments all come from the same struct gpio_descs, so having a separate function where we can just pass that cuts down on the boilerplate. Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20250210-gpio-set-array-helper-v3-1-d6a673674da8@baylibre.com Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/consumer.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index db2dfbae8edb..5cbd4afd7862 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -3,6 +3,7 @@ #define __LINUX_GPIO_CONSUMER_H #include +#include #include struct acpi_device; @@ -655,4 +656,14 @@ static inline void gpiod_unexport(struct gpio_desc *desc) #endif /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */ +static inline int gpiod_multi_set_value_cansleep(struct gpio_descs *descs, + unsigned long *value_bitmap) +{ + if (IS_ERR_OR_NULL(descs)) + return PTR_ERR_OR_ZERO(descs); + + return gpiod_set_array_value_cansleep(descs->ndescs, descs->desc, + descs->info, value_bitmap); +} + #endif From eb2e9c308d2882d9d364af048eb3d8336d41c4bb Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 10 Feb 2025 16:33:32 -0600 Subject: [PATCH 013/119] gpio: max3191x: use gpiod_multi_set_value_cansleep Reduce verbosity by using gpiod_multi_set_value_cansleep() instead of gpiod_set_array_value_cansleep(). Also add max3191x_ namespace prefix to the driver's helper function since we are changing the function signature anyway. Reviewed-by: Linus Walleij Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250210-gpio-set-array-helper-v3-6-d6a673674da8@baylibre.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-max3191x.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c index bbacc714632b..fc0708ab5192 100644 --- a/drivers/gpio/gpio-max3191x.c +++ b/drivers/gpio/gpio-max3191x.c @@ -309,23 +309,21 @@ static int max3191x_set_config(struct gpio_chip *gpio, unsigned int offset, return 0; } -static void gpiod_set_array_single_value_cansleep(unsigned int ndescs, - struct gpio_desc **desc, - struct gpio_array *info, +static void max3191x_gpiod_multi_set_single_value(struct gpio_descs *descs, int value) { unsigned long *values; - values = bitmap_alloc(ndescs, GFP_KERNEL); + values = bitmap_alloc(descs->ndescs, GFP_KERNEL); if (!values) return; if (value) - bitmap_fill(values, ndescs); + bitmap_fill(values, descs->ndescs); else - bitmap_zero(values, ndescs); + bitmap_zero(values, descs->ndescs); - gpiod_set_array_value_cansleep(ndescs, desc, info, values); + gpiod_multi_set_value_cansleep(descs, values); bitmap_free(values); } @@ -396,10 +394,8 @@ static int max3191x_probe(struct spi_device *spi) max3191x->mode = device_property_read_bool(dev, "maxim,modesel-8bit") ? STATUS_BYTE_DISABLED : STATUS_BYTE_ENABLED; if (max3191x->modesel_pins) - gpiod_set_array_single_value_cansleep( - max3191x->modesel_pins->ndescs, - max3191x->modesel_pins->desc, - max3191x->modesel_pins->info, max3191x->mode); + max3191x_gpiod_multi_set_single_value(max3191x->modesel_pins, + max3191x->mode); max3191x->ignore_uv = device_property_read_bool(dev, "maxim,ignore-undervoltage"); From 8beaf839018096cd20e427e68645b4fbecdcb1f0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 4 Feb 2025 19:56:46 +0200 Subject: [PATCH 014/119] gpiolib: Deduplicate gpiod_direction_input_nonotify() call Deduplicate gpiod_direction_input_nonotify() call in gpiod_direction_output_nonotify() when emulating open-drain or open-source behaviour. It also aligns the error check approaches in set_output_value and set_output_flag labels. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250204175646.150577-1-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 618fe2eebf38..f261f7893f85 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2873,19 +2873,15 @@ int gpiod_direction_output_nonotify(struct gpio_desc *desc, int value) if (!ret) goto set_output_value; /* Emulate open drain by not actively driving the line high */ - if (value) { - ret = gpiod_direction_input_nonotify(desc); + if (value) goto set_output_flag; - } } else if (test_bit(FLAG_OPEN_SOURCE, &flags)) { ret = gpio_set_config(desc, PIN_CONFIG_DRIVE_OPEN_SOURCE); if (!ret) goto set_output_value; /* Emulate open source by not actively driving the line low */ - if (!value) { - ret = gpiod_direction_input_nonotify(desc); + if (!value) goto set_output_flag; - } } else { gpio_set_config(desc, PIN_CONFIG_DRIVE_PUSH_PULL); } @@ -2897,15 +2893,17 @@ int gpiod_direction_output_nonotify(struct gpio_desc *desc, int value) return gpiod_direction_output_raw_commit(desc, value); set_output_flag: + ret = gpiod_direction_input_nonotify(desc); + if (ret) + return ret; /* * When emulating open-source or open-drain functionalities by not * actively driving the line (setting mode to input) we still need to * set the IS_OUT flag or otherwise we won't be able to set the line * value anymore. */ - if (ret == 0) - set_bit(FLAG_IS_OUT, &desc->flags); - return ret; + set_bit(FLAG_IS_OUT, &desc->flags); + return 0; } /** From 2af1f667532013eb354c783514839f89d9923240 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 5 Feb 2025 11:31:10 +0200 Subject: [PATCH 015/119] gpio: xilinx: Use better bitmap APIs where appropriate There are bitmap_gather() and bitmap_scatter() that are factually simplified version of the APIs used in the driver. Use them where appropriate. While at it, replace bitmap_bitremap() with find_nth_bit() for the sake of simplification. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250205093200.373709-2-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xilinx.c | 68 +++++++++++++++----------------------- 1 file changed, 27 insertions(+), 41 deletions(-) diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 792d94c49077..1ff527ccf6c7 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -45,8 +45,7 @@ * struct xgpio_instance - Stores information about GPIO device * @gc: GPIO chip * @regs: register block - * @hw_map: GPIO pin mapping on hardware side - * @sw_map: GPIO pin mapping on software side + * @map: GPIO pin mapping on hardware side * @state: GPIO write state shadow register * @last_irq_read: GPIO read state register from last interrupt * @dir: GPIO direction shadow register @@ -60,8 +59,7 @@ struct xgpio_instance { struct gpio_chip gc; void __iomem *regs; - DECLARE_BITMAP(hw_map, 64); - DECLARE_BITMAP(sw_map, 64); + DECLARE_BITMAP(map, 64); DECLARE_BITMAP(state, 64); DECLARE_BITMAP(last_irq_read, 64); DECLARE_BITMAP(dir, 64); @@ -73,16 +71,6 @@ struct xgpio_instance { struct clk *clk; }; -static inline int xgpio_from_bit(struct xgpio_instance *chip, int bit) -{ - return bitmap_bitremap(bit, chip->hw_map, chip->sw_map, 64); -} - -static inline int xgpio_to_bit(struct xgpio_instance *chip, int gpio) -{ - return bitmap_bitremap(gpio, chip->sw_map, chip->hw_map, 64); -} - static inline u32 xgpio_get_value32(const unsigned long *map, int bit) { const size_t index = BIT_WORD(bit); @@ -128,7 +116,8 @@ static void xgpio_write_ch(struct xgpio_instance *chip, int reg, int bit, unsign static void xgpio_read_ch_all(struct xgpio_instance *chip, int reg, unsigned long *a) { - int bit, lastbit = xgpio_to_bit(chip, chip->gc.ngpio - 1); + unsigned long lastbit = find_nth_bit(chip->map, 64, chip->gc.ngpio - 1); + int bit; for (bit = 0; bit <= lastbit ; bit += 32) xgpio_read_ch(chip, reg, bit, a); @@ -136,7 +125,8 @@ static void xgpio_read_ch_all(struct xgpio_instance *chip, int reg, unsigned lon static void xgpio_write_ch_all(struct xgpio_instance *chip, int reg, unsigned long *a) { - int bit, lastbit = xgpio_to_bit(chip, chip->gc.ngpio - 1); + unsigned long lastbit = find_nth_bit(chip->map, 64, chip->gc.ngpio - 1); + int bit; for (bit = 0; bit <= lastbit ; bit += 32) xgpio_write_ch(chip, reg, bit, a); @@ -156,7 +146,7 @@ static void xgpio_write_ch_all(struct xgpio_instance *chip, int reg, unsigned lo static int xgpio_get(struct gpio_chip *gc, unsigned int gpio) { struct xgpio_instance *chip = gpiochip_get_data(gc); - int bit = xgpio_to_bit(chip, gpio); + unsigned long bit = find_nth_bit(chip->map, 64, gpio); DECLARE_BITMAP(state, 64); xgpio_read_ch(chip, XGPIO_DATA_OFFSET, bit, state); @@ -177,7 +167,7 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) { unsigned long flags; struct xgpio_instance *chip = gpiochip_get_data(gc); - int bit = xgpio_to_bit(chip, gpio); + unsigned long bit = find_nth_bit(chip->map, 64, gpio); raw_spin_lock_irqsave(&chip->gpio_lock, flags); @@ -207,8 +197,8 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long flags; struct xgpio_instance *chip = gpiochip_get_data(gc); - bitmap_remap(hw_mask, mask, chip->sw_map, chip->hw_map, 64); - bitmap_remap(hw_bits, bits, chip->sw_map, chip->hw_map, 64); + bitmap_scatter(hw_mask, mask, chip->map, 64); + bitmap_scatter(hw_bits, bits, chip->map, 64); raw_spin_lock_irqsave(&chip->gpio_lock, flags); @@ -234,7 +224,7 @@ static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) { unsigned long flags; struct xgpio_instance *chip = gpiochip_get_data(gc); - int bit = xgpio_to_bit(chip, gpio); + unsigned long bit = find_nth_bit(chip->map, 64, gpio); raw_spin_lock_irqsave(&chip->gpio_lock, flags); @@ -263,7 +253,7 @@ static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) { unsigned long flags; struct xgpio_instance *chip = gpiochip_get_data(gc); - int bit = xgpio_to_bit(chip, gpio); + unsigned long bit = find_nth_bit(chip->map, 64, gpio); raw_spin_lock_irqsave(&chip->gpio_lock, flags); @@ -395,7 +385,7 @@ static void xgpio_irq_mask(struct irq_data *irq_data) unsigned long flags; struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data); int irq_offset = irqd_to_hwirq(irq_data); - int bit = xgpio_to_bit(chip, irq_offset); + unsigned long bit = find_nth_bit(chip->map, 64, irq_offset); u32 mask = BIT(bit / 32), temp; raw_spin_lock_irqsave(&chip->gpio_lock, flags); @@ -422,7 +412,7 @@ static void xgpio_irq_unmask(struct irq_data *irq_data) unsigned long flags; struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data); int irq_offset = irqd_to_hwirq(irq_data); - int bit = xgpio_to_bit(chip, irq_offset); + unsigned long bit = find_nth_bit(chip->map, 64, irq_offset); u32 old_enable = xgpio_get_value32(chip->enable, bit); u32 mask = BIT(bit / 32), val; @@ -462,7 +452,7 @@ static int xgpio_set_irq_type(struct irq_data *irq_data, unsigned int type) { struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data); int irq_offset = irqd_to_hwirq(irq_data); - int bit = xgpio_to_bit(chip, irq_offset); + unsigned long bit = find_nth_bit(chip->map, 64, irq_offset); /* * The Xilinx GPIO hardware provides a single interrupt status @@ -502,10 +492,10 @@ static void xgpio_irqhandler(struct irq_desc *desc) struct irq_chip *irqchip = irq_desc_get_chip(desc); DECLARE_BITMAP(rising, 64); DECLARE_BITMAP(falling, 64); - DECLARE_BITMAP(all, 64); + DECLARE_BITMAP(hw, 64); + DECLARE_BITMAP(sw, 64); int irq_offset; u32 status; - u32 bit; status = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET); xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, status); @@ -514,29 +504,28 @@ static void xgpio_irqhandler(struct irq_desc *desc) raw_spin_lock(&chip->gpio_lock); - xgpio_read_ch_all(chip, XGPIO_DATA_OFFSET, all); + xgpio_read_ch_all(chip, XGPIO_DATA_OFFSET, hw); bitmap_complement(rising, chip->last_irq_read, 64); - bitmap_and(rising, rising, all, 64); + bitmap_and(rising, rising, hw, 64); bitmap_and(rising, rising, chip->enable, 64); bitmap_and(rising, rising, chip->rising_edge, 64); - bitmap_complement(falling, all, 64); + bitmap_complement(falling, hw, 64); bitmap_and(falling, falling, chip->last_irq_read, 64); bitmap_and(falling, falling, chip->enable, 64); bitmap_and(falling, falling, chip->falling_edge, 64); - bitmap_copy(chip->last_irq_read, all, 64); - bitmap_or(all, rising, falling, 64); + bitmap_copy(chip->last_irq_read, hw, 64); + bitmap_or(hw, rising, falling, 64); raw_spin_unlock(&chip->gpio_lock); dev_dbg(gc->parent, "IRQ rising %*pb falling %*pb\n", 64, rising, 64, falling); - for_each_set_bit(bit, all, 64) { - irq_offset = xgpio_from_bit(chip, bit); + bitmap_gather(sw, hw, chip->map, 64); + for_each_set_bit(irq_offset, sw, 64) generic_handle_domain_irq(gc->irq.domain, irq_offset); - } chained_irq_exit(irqchip, desc); } @@ -613,17 +602,14 @@ static int xgpio_probe(struct platform_device *pdev) if (width[1] > 32) return -EINVAL; - /* Setup software pin mapping */ - bitmap_set(chip->sw_map, 0, width[0] + width[1]); - /* Setup hardware pin mapping */ - bitmap_set(chip->hw_map, 0, width[0]); - bitmap_set(chip->hw_map, 32, width[1]); + bitmap_set(chip->map, 0, width[0]); + bitmap_set(chip->map, 32, width[1]); raw_spin_lock_init(&chip->gpio_lock); chip->gc.base = -1; - chip->gc.ngpio = bitmap_weight(chip->hw_map, 64); + chip->gc.ngpio = bitmap_weight(chip->map, 64); chip->gc.parent = dev; chip->gc.direction_input = xgpio_dir_in; chip->gc.direction_output = xgpio_dir_out; From c11708e2b66b56f102bac83980a52661996c2a21 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 5 Feb 2025 11:31:11 +0200 Subject: [PATCH 016/119] gpio: xilinx: Replace custom variants of bitmap_read()/bitmap_write() Relatively recently bitmap APIs were expanded by introduction of bitmap_read() and bitmap_write(). These APIs are generic ones that may replace custom functions in this driver, i.e. xgpio_get_value32() and xgpio_set_value32(). Do replace them. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250205093200.373709-3-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xilinx.c | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 1ff527ccf6c7..c58a7e1349b4 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -71,23 +71,6 @@ struct xgpio_instance { struct clk *clk; }; -static inline u32 xgpio_get_value32(const unsigned long *map, int bit) -{ - const size_t index = BIT_WORD(bit); - const unsigned long offset = (bit % BITS_PER_LONG) & BIT(5); - - return (map[index] >> offset) & 0xFFFFFFFFul; -} - -static inline void xgpio_set_value32(unsigned long *map, int bit, u32 v) -{ - const size_t index = BIT_WORD(bit); - const unsigned long offset = (bit % BITS_PER_LONG) & BIT(5); - - map[index] &= ~(0xFFFFFFFFul << offset); - map[index] |= (unsigned long)v << offset; -} - static inline int xgpio_regoffset(struct xgpio_instance *chip, int ch) { switch (ch) { @@ -103,15 +86,17 @@ static inline int xgpio_regoffset(struct xgpio_instance *chip, int ch) static void xgpio_read_ch(struct xgpio_instance *chip, int reg, int bit, unsigned long *a) { void __iomem *addr = chip->regs + reg + xgpio_regoffset(chip, bit / 32); + unsigned long value = xgpio_readreg(addr); - xgpio_set_value32(a, bit, xgpio_readreg(addr)); + bitmap_write(a, value, round_down(bit, 32), 32); } static void xgpio_write_ch(struct xgpio_instance *chip, int reg, int bit, unsigned long *a) { void __iomem *addr = chip->regs + reg + xgpio_regoffset(chip, bit / 32); + unsigned long value = bitmap_read(a, round_down(bit, 32), 32); - xgpio_writereg(addr, xgpio_get_value32(a, bit)); + xgpio_writereg(addr, value); } static void xgpio_read_ch_all(struct xgpio_instance *chip, int reg, unsigned long *a) @@ -385,14 +370,15 @@ static void xgpio_irq_mask(struct irq_data *irq_data) unsigned long flags; struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data); int irq_offset = irqd_to_hwirq(irq_data); - unsigned long bit = find_nth_bit(chip->map, 64, irq_offset); + unsigned long bit = find_nth_bit(chip->map, 64, irq_offset), enable; u32 mask = BIT(bit / 32), temp; raw_spin_lock_irqsave(&chip->gpio_lock, flags); __clear_bit(bit, chip->enable); - if (xgpio_get_value32(chip->enable, bit) == 0) { + enable = bitmap_read(chip->enable, round_down(bit, 32), 32); + if (enable == 0) { /* Disable per channel interrupt */ temp = xgpio_readreg(chip->regs + XGPIO_IPIER_OFFSET); temp &= ~mask; @@ -412,17 +398,15 @@ static void xgpio_irq_unmask(struct irq_data *irq_data) unsigned long flags; struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data); int irq_offset = irqd_to_hwirq(irq_data); - unsigned long bit = find_nth_bit(chip->map, 64, irq_offset); - u32 old_enable = xgpio_get_value32(chip->enable, bit); + unsigned long bit = find_nth_bit(chip->map, 64, irq_offset), enable; u32 mask = BIT(bit / 32), val; gpiochip_enable_irq(&chip->gc, irq_offset); raw_spin_lock_irqsave(&chip->gpio_lock, flags); - __set_bit(bit, chip->enable); - - if (old_enable == 0) { + enable = bitmap_read(chip->enable, round_down(bit, 32), 32); + if (enable == 0) { /* Clear any existing per-channel interrupts */ val = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET); val &= mask; @@ -437,6 +421,8 @@ static void xgpio_irq_unmask(struct irq_data *irq_data) xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, val); } + __set_bit(bit, chip->enable); + raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); } From 8893516000b247f91fa2cef34f2a77b609e661a4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 7 Feb 2025 17:07:34 +0200 Subject: [PATCH 017/119] gpiolib: Deduplicate some code in for_each_requested_gpio_in_range() Refactor for_each_requested_gpio_in_range() to deduplicate some code which is basically repeats the for_each_hwgpio(). In order to achieve this, split the latter to two, for_each_hwgpio_in_range() and for_each_hwgpio(). Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250207151149.2119765-2-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/driver.h | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 2dd7cb9cc270..314f4241e306 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -549,6 +549,23 @@ DEFINE_CLASS(_gpiochip_for_each_data, }), const char **label, int *i) +/** + * for_each_hwgpio_in_range - Iterates over all GPIOs in a given range + * @_chip: Chip to iterate over. + * @_i: Loop counter. + * @_base: First GPIO in the ranger. + * @_size: Amount of GPIOs to check starting from @base. + * @_label: Place to store the address of the label if the GPIO is requested. + * Set to NULL for unused GPIOs. + */ +#define for_each_hwgpio_in_range(_chip, _i, _base, _size, _label) \ + for (CLASS(_gpiochip_for_each_data, _data)(&_label, &_i); \ + *_data.i < _size; \ + (*_data.i)++, kfree(*(_data.label)), *_data.label = NULL) \ + if (IS_ERR(*_data.label = \ + gpiochip_dup_line_label(_chip, _base + *_data.i))) {} \ + else + /** * for_each_hwgpio - Iterates over all GPIOs for given chip. * @_chip: Chip to iterate over. @@ -556,13 +573,8 @@ DEFINE_CLASS(_gpiochip_for_each_data, * @_label: Place to store the address of the label if the GPIO is requested. * Set to NULL for unused GPIOs. */ -#define for_each_hwgpio(_chip, _i, _label) \ - for (CLASS(_gpiochip_for_each_data, _data)(&_label, &_i); \ - *_data.i < _chip->ngpio; \ - (*_data.i)++, kfree(*(_data.label)), *_data.label = NULL) \ - if (IS_ERR(*_data.label = \ - gpiochip_dup_line_label(_chip, *_data.i))) {} \ - else +#define for_each_hwgpio(_chip, _i, _label) \ + for_each_hwgpio_in_range(_chip, _i, 0, _chip->ngpio, _label) /** * for_each_requested_gpio_in_range - iterates over requested GPIOs in a given range @@ -573,13 +585,8 @@ DEFINE_CLASS(_gpiochip_for_each_data, * @_label: label of current GPIO */ #define for_each_requested_gpio_in_range(_chip, _i, _base, _size, _label) \ - for (CLASS(_gpiochip_for_each_data, _data)(&_label, &_i); \ - *_data.i < _size; \ - (*_data.i)++, kfree(*(_data.label)), *_data.label = NULL) \ - if ((*_data.label = \ - gpiochip_dup_line_label(_chip, _base + *_data.i)) == NULL) {} \ - else if (IS_ERR(*_data.label)) {} \ - else + for_each_hwgpio_in_range(_chip, _i, _base, _size, _label) \ + if (_label == NULL) {} else /* Iterates over all requested GPIO of the given @chip */ #define for_each_requested_gpio(chip, i, label) \ From 767412f092fc6e04147305acd70f15770ece47ec Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 7 Feb 2025 17:07:35 +0200 Subject: [PATCH 018/119] gpiolib: Simplify implementation of for_each_hwgpio_in_range() The whole purpose of the custom CLASS() is to have possibility to initialise the counter variable _i to 0. This can't be done with simple __free() macro as it will be not allowed by C language. OTOH, the CLASS() operates with the pointers and explicit usage of the scoped variable _data is not needed, since the pointers are kept the same over the iterations. Simplify the implementation of for_each_hwgpio_in_range(). Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250207151149.2119765-3-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/driver.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 314f4241e306..89439be2ddaa 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -560,11 +560,9 @@ DEFINE_CLASS(_gpiochip_for_each_data, */ #define for_each_hwgpio_in_range(_chip, _i, _base, _size, _label) \ for (CLASS(_gpiochip_for_each_data, _data)(&_label, &_i); \ - *_data.i < _size; \ - (*_data.i)++, kfree(*(_data.label)), *_data.label = NULL) \ - if (IS_ERR(*_data.label = \ - gpiochip_dup_line_label(_chip, _base + *_data.i))) {} \ - else + _i < _size; \ + _i++, kfree(_label), _label = NULL) \ + if (IS_ERR(_label = gpiochip_dup_line_label(_chip, _base + _i))) {} else /** * for_each_hwgpio - Iterates over all GPIOs for given chip. From aac4be9341ddfd853e3aa16fe020475c205b6995 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 10 Feb 2025 16:33:37 -0600 Subject: [PATCH 019/119] mmc: pwrseq_simple: use gpiod_multi_set_value_cansleep Reduce verbosity by using gpiod_multi_set_value_cansleep() instead of gpiod_set_array_value_cansleep(). Acked-by: Ulf Hansson Reviewed-by: Linus Walleij Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20250210-gpio-set-array-helper-v3-11-d6a673674da8@baylibre.com Signed-off-by: Bartosz Golaszewski --- drivers/mmc/core/pwrseq_simple.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c index 37cd858df0f4..4b47e6c3b04b 100644 --- a/drivers/mmc/core/pwrseq_simple.c +++ b/drivers/mmc/core/pwrseq_simple.c @@ -54,8 +54,7 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq, else bitmap_zero(values, nvalues); - gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, - reset_gpios->info, values); + gpiod_multi_set_value_cansleep(reset_gpios, values); bitmap_free(values); } From 35d950a66f1f403ebf1d4c5a50b15c25e146d6fd Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 10 Feb 2025 16:33:38 -0600 Subject: [PATCH 020/119] mux: gpio: use gpiod_multi_set_value_cansleep Reduce verbosity by using gpiod_multi_set_value_cansleep() instead of gpiod_set_array_value_cansleep(). Acked-by: Peter Rosin Reviewed-by: Linus Walleij Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20250210-gpio-set-array-helper-v3-12-d6a673674da8@baylibre.com Signed-off-by: Bartosz Golaszewski --- drivers/mux/gpio.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c index cc5f2c1861d4..5710879cd47f 100644 --- a/drivers/mux/gpio.c +++ b/drivers/mux/gpio.c @@ -28,9 +28,7 @@ static int mux_gpio_set(struct mux_control *mux, int state) bitmap_from_arr32(values, &value, BITS_PER_TYPE(value)); - gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs, - mux_gpio->gpios->desc, - mux_gpio->gpios->info, values); + gpiod_multi_set_value_cansleep(mux_gpio->gpios, values); return 0; } From d50a7908df16aa5640fe7c36f19f21d443d9eae9 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 10 Feb 2025 16:33:40 -0600 Subject: [PATCH 021/119] phy: mapphone-mdm6600: use gpiod_multi_set_value_cansleep Reduce verbosity by using gpiod_multi_set_value_cansleep() instead of gpiod_set_array_value_cansleep(). ddata->cmd_gpios->ndescs is validated to be equal to PHY_MDM6600_NR_CMD_LINES during driver probe, so it will have the same value as the previously hard-coded argument. Reviewed-by: Linus Walleij Signed-off-by: David Lechner Acked-by: Vinod Koul Link: https://lore.kernel.org/r/20250210-gpio-set-array-helper-v3-14-d6a673674da8@baylibre.com Signed-off-by: Bartosz Golaszewski --- drivers/phy/motorola/phy-mapphone-mdm6600.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c index 152344e4f7e4..fd0e0cd1c1cf 100644 --- a/drivers/phy/motorola/phy-mapphone-mdm6600.c +++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c @@ -177,9 +177,7 @@ static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val) values[0] = val; - gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES, - ddata->cmd_gpios->desc, - ddata->cmd_gpios->info, values); + gpiod_multi_set_value_cansleep(ddata->cmd_gpios, values); } /** From 3150619d1a14d920463107bd18fe4d566f2ee05a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 11 Feb 2025 13:08:46 +0100 Subject: [PATCH 022/119] gpio: latch: use generic device properties Replace calls to OF-specific interface with generic device property getters. This is good practice and also drops implicit run-time dependency on CONFIG_OF. Link: https://lore.kernel.org/r/20250211120847.42437-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-latch.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c index d7c3b20c8482..722eb5b045f5 100644 --- a/drivers/gpio/gpio-latch.c +++ b/drivers/gpio/gpio-latch.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include "gpiolib.h" @@ -138,9 +139,9 @@ static bool gpio_latch_can_sleep(struct gpio_latch_priv *priv, unsigned int n_la static int gpio_latch_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct gpio_latch_priv *priv; unsigned int n_latches; - struct device_node *np = pdev->dev.of_node; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -172,14 +173,16 @@ static int gpio_latch_probe(struct platform_device *pdev) spin_lock_init(&priv->spinlock); } - of_property_read_u32(np, "setup-duration-ns", &priv->setup_duration_ns); + device_property_read_u32(dev, "setup-duration-ns", + &priv->setup_duration_ns); if (priv->setup_duration_ns > DURATION_NS_MAX) { dev_warn(&pdev->dev, "setup-duration-ns too high, limit to %d\n", DURATION_NS_MAX); priv->setup_duration_ns = DURATION_NS_MAX; } - of_property_read_u32(np, "clock-duration-ns", &priv->clock_duration_ns); + device_property_read_u32(dev, "clock-duration-ns", + &priv->clock_duration_ns); if (priv->clock_duration_ns > DURATION_NS_MAX) { dev_warn(&pdev->dev, "clock-duration-ns too high, limit to %d\n", DURATION_NS_MAX); From 8efee9c43127662b7642e0146aba8d31606277e1 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 11 Feb 2025 13:08:47 +0100 Subject: [PATCH 023/119] gpio: latch: store the address of pdev->dev in a helper variable Make the code a bit more readable by using a helper variable to store the address of pdev->dev in probe(). Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250211120847.42437-2-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-latch.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c index 722eb5b045f5..46cdfb08747a 100644 --- a/drivers/gpio/gpio-latch.c +++ b/drivers/gpio/gpio-latch.c @@ -143,22 +143,22 @@ static int gpio_latch_probe(struct platform_device *pdev) struct gpio_latch_priv *priv; unsigned int n_latches; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - priv->clk_gpios = devm_gpiod_get_array(&pdev->dev, "clk", GPIOD_OUT_LOW); + priv->clk_gpios = devm_gpiod_get_array(dev, "clk", GPIOD_OUT_LOW); if (IS_ERR(priv->clk_gpios)) return PTR_ERR(priv->clk_gpios); - priv->latched_gpios = devm_gpiod_get_array(&pdev->dev, "latched", GPIOD_OUT_LOW); + priv->latched_gpios = devm_gpiod_get_array(dev, "latched", GPIOD_OUT_LOW); if (IS_ERR(priv->latched_gpios)) return PTR_ERR(priv->latched_gpios); n_latches = priv->clk_gpios->ndescs; priv->n_latched_gpios = priv->latched_gpios->ndescs; - priv->shadow = devm_bitmap_zalloc(&pdev->dev, n_latches * priv->n_latched_gpios, + priv->shadow = devm_bitmap_zalloc(dev, n_latches * priv->n_latched_gpios, GFP_KERNEL); if (!priv->shadow) return -ENOMEM; @@ -176,7 +176,7 @@ static int gpio_latch_probe(struct platform_device *pdev) device_property_read_u32(dev, "setup-duration-ns", &priv->setup_duration_ns); if (priv->setup_duration_ns > DURATION_NS_MAX) { - dev_warn(&pdev->dev, "setup-duration-ns too high, limit to %d\n", + dev_warn(dev, "setup-duration-ns too high, limit to %d\n", DURATION_NS_MAX); priv->setup_duration_ns = DURATION_NS_MAX; } @@ -184,7 +184,7 @@ static int gpio_latch_probe(struct platform_device *pdev) device_property_read_u32(dev, "clock-duration-ns", &priv->clock_duration_ns); if (priv->clock_duration_ns > DURATION_NS_MAX) { - dev_warn(&pdev->dev, "clock-duration-ns too high, limit to %d\n", + dev_warn(dev, "clock-duration-ns too high, limit to %d\n", DURATION_NS_MAX); priv->clock_duration_ns = DURATION_NS_MAX; } @@ -193,11 +193,11 @@ static int gpio_latch_probe(struct platform_device *pdev) priv->gc.ngpio = n_latches * priv->n_latched_gpios; priv->gc.owner = THIS_MODULE; priv->gc.base = -1; - priv->gc.parent = &pdev->dev; + priv->gc.parent = dev; platform_set_drvdata(pdev, priv); - return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv); + return devm_gpiochip_add_data(dev, &priv->gc, priv); } static const struct of_device_id gpio_latch_ids[] = { From b2108fc82a0acda34388bff3e3ee3544013b1623 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 20:24:00 +0200 Subject: [PATCH 024/119] drm: Move for_each_if() to util_macros.h for wider use Other subsystem(s) may want to reuse the for_each_if() macro. Move it to util_macros.h to make it globally available. Suggested-by: Bartosz Golaszewski Signed-off-by: Andy Shevchenko Acked-by: Jani Nikula Reviewed-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250213182527.3092371-2-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- include/drm/drm_util.h | 16 +--------------- include/linux/util_macros.h | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/include/drm/drm_util.h b/include/drm/drm_util.h index 79952d8c4bba..440199618620 100644 --- a/include/drm/drm_util.h +++ b/include/drm/drm_util.h @@ -36,6 +36,7 @@ #include #include #include +#include /* * Use EXPORT_SYMBOL_FOR_TESTS_ONLY() for functions that shall @@ -47,21 +48,6 @@ #define EXPORT_SYMBOL_FOR_TESTS_ONLY(x) #endif -/** - * for_each_if - helper for handling conditionals in various for_each macros - * @condition: The condition to check - * - * Typical use:: - * - * #define for_each_foo_bar(x, y) \' - * list_for_each_entry(x, y->list, head) \' - * for_each_if(x->something == SOMETHING) - * - * The for_each_if() macro makes the use of for_each_foo_bar() less error - * prone. - */ -#define for_each_if(condition) if (!(condition)) {} else - /** * drm_can_sleep - returns true if currently okay to sleep * diff --git a/include/linux/util_macros.h b/include/linux/util_macros.h index 825487fb66fa..3b570b765b75 100644 --- a/include/linux/util_macros.h +++ b/include/linux/util_macros.h @@ -4,6 +4,21 @@ #include +/** + * for_each_if - helper for handling conditionals in various for_each macros + * @condition: The condition to check + * + * Typical use:: + * + * #define for_each_foo_bar(x, y) \' + * list_for_each_entry(x, y->list, head) \' + * for_each_if(x->something == SOMETHING) + * + * The for_each_if() macro makes the use of for_each_foo_bar() less error + * prone. + */ +#define for_each_if(condition) if (!(condition)) {} else + /** * find_closest - locate the closest element in a sorted array * @x: The reference value. From 23318614f8c146d440e57a69d7171dd8c0d249b7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 20:24:01 +0200 Subject: [PATCH 025/119] gpiolib: Switch to use for_each_if() helper The for_each_*() APIs that are conditional can be written shorter and less error prone with for_each_if() helper in use. Switch them to use this helper. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250213182527.3092371-3-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/driver.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 89439be2ddaa..10544f4a03e5 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -14,6 +14,7 @@ #include #include #include +#include #ifdef CONFIG_GENERIC_MSI_IRQ #include @@ -562,7 +563,7 @@ DEFINE_CLASS(_gpiochip_for_each_data, for (CLASS(_gpiochip_for_each_data, _data)(&_label, &_i); \ _i < _size; \ _i++, kfree(_label), _label = NULL) \ - if (IS_ERR(_label = gpiochip_dup_line_label(_chip, _base + _i))) {} else + for_each_if(!IS_ERR(_label = gpiochip_dup_line_label(_chip, _base + _i))) /** * for_each_hwgpio - Iterates over all GPIOs for given chip. @@ -584,7 +585,7 @@ DEFINE_CLASS(_gpiochip_for_each_data, */ #define for_each_requested_gpio_in_range(_chip, _i, _base, _size, _label) \ for_each_hwgpio_in_range(_chip, _i, _base, _size, _label) \ - if (_label == NULL) {} else + for_each_if(_label) /* Iterates over all requested GPIO of the given @chip */ #define for_each_requested_gpio(chip, i, label) \ @@ -870,7 +871,7 @@ static inline void gpiochip_unlock_as_irq(struct gpio_chip *gc, #define for_each_gpiochip_node(dev, child) \ device_for_each_child_node(dev, child) \ - if (!fwnode_property_present(child, "gpio-controller")) {} else + for_each_if(fwnode_property_present(child, "gpio-controller")) static inline unsigned int gpiochip_node_count(struct device *dev) { From f04867a5d0d3a42fd3b7471343091a22363139e3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 20:48:33 +0200 Subject: [PATCH 026/119] gpio: loongson-64bit: Remove unneeded ngpio assignment The GPIO library will parse the respective property to fill ngpio. No need to repeat it in the driver. While at it, drop unused fwnode field. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250213184833.3109038-1-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-loongson-64bit.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpio/gpio-loongson-64bit.c b/drivers/gpio/gpio-loongson-64bit.c index 7f4d78fd800e..f000cc0356c7 100644 --- a/drivers/gpio/gpio-loongson-64bit.c +++ b/drivers/gpio/gpio-loongson-64bit.c @@ -31,7 +31,6 @@ struct loongson_gpio_chip_data { struct loongson_gpio_chip { struct gpio_chip chip; - struct fwnode_handle *fwnode; spinlock_t lock; void __iomem *reg_base; const struct loongson_gpio_chip_data *chip_data; @@ -138,7 +137,6 @@ static int loongson_gpio_init(struct device *dev, struct loongson_gpio_chip *lgp void __iomem *reg_base) { int ret; - u32 ngpios; lgpio->reg_base = reg_base; if (lgpio->chip_data->mode == BIT_CTRL_MODE) { @@ -159,8 +157,6 @@ static int loongson_gpio_init(struct device *dev, struct loongson_gpio_chip *lgp lgpio->chip.direction_output = loongson_gpio_direction_output; lgpio->chip.set = loongson_gpio_set; lgpio->chip.parent = dev; - device_property_read_u32(dev, "ngpios", &ngpios); - lgpio->chip.ngpio = ngpios; spin_lock_init(&lgpio->lock); } From dea69f2d1cc8d9ecdc72ba350d10a1e71940ef18 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 17 Feb 2025 11:39:21 +0100 Subject: [PATCH 027/119] gpiolib: move all includes to the top of gpio/consumer.h We have several conditional includes depending on !CONFIG_GPIOLIB. This is supposed to reduce compilation time with CONFIG_GPIOLIB=y but in practice there's no difference on modern machines. It makes adding new stubs that depend on more than just GPIOLIB harder so move them all to the top, unduplicate them and replace asm/ with preferred linux/ alternatives. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250217103922.151047-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/consumer.h | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 5cbd4afd7862..0dc49b5fca5c 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -3,7 +3,10 @@ #define __LINUX_GPIO_CONSUMER_H #include +#include #include +#include +#include #include struct acpi_device; @@ -184,11 +187,6 @@ struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev, #else /* CONFIG_GPIOLIB */ -#include -#include - -#include - static inline int gpiod_count(struct device *dev, const char *con_id) { return 0; @@ -609,8 +607,6 @@ int devm_acpi_dev_add_driver_gpios(struct device *dev, #else /* CONFIG_GPIOLIB && CONFIG_ACPI */ -#include - static inline int acpi_dev_add_driver_gpios(struct acpi_device *adev, const struct acpi_gpio_mapping *gpios) { @@ -636,8 +632,6 @@ void gpiod_unexport(struct gpio_desc *desc); #else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */ -#include - static inline int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { From 63cdf6241ac7edd94cb4cd9a8f1ba2c3c61ed219 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 17 Feb 2025 11:39:22 +0100 Subject: [PATCH 028/119] gpiolib: don't build HTE code with CONFIG_HTE disabled Hardware timestamping is only used on tegra186 platforms but we include the code and export the symbols everywhere. Shrink the binary a bit by compiling the relevant functions conditionally. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250217103922.151047-2-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 2 ++ include/linux/gpio/consumer.h | 36 +++++++++++++++++++++-------------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f261f7893f85..65ca749a1078 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2906,6 +2906,7 @@ int gpiod_direction_output_nonotify(struct gpio_desc *desc, int value) return 0; } +#if IS_ENABLED(CONFIG_HTE) /** * gpiod_enable_hw_timestamp_ns - Enable hardware timestamp in nanoseconds. * @@ -2971,6 +2972,7 @@ int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags) return ret; } EXPORT_SYMBOL_GPL(gpiod_disable_hw_timestamp_ns); +#endif /* CONFIG_HTE */ /** * gpiod_set_config - sets @config for a GPIO diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 0dc49b5fca5c..0b2b56199c36 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -114,8 +114,6 @@ int gpiod_get_direction(struct gpio_desc *desc); int gpiod_direction_input(struct gpio_desc *desc); int gpiod_direction_output(struct gpio_desc *desc, int value); int gpiod_direction_output_raw(struct gpio_desc *desc, int value); -int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags); -int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags); /* Value get/set from non-sleeping context */ int gpiod_get_value(const struct gpio_desc *desc); @@ -347,18 +345,6 @@ static inline int gpiod_direction_output_raw(struct gpio_desc *desc, int value) WARN_ON(desc); return -ENOSYS; } -static inline int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, - unsigned long flags) -{ - WARN_ON(desc); - return -ENOSYS; -} -static inline int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, - unsigned long flags) -{ - WARN_ON(desc); - return -ENOSYS; -} static inline int gpiod_get_value(const struct gpio_desc *desc) { /* GPIO can never have been requested */ @@ -559,6 +545,28 @@ struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev, #endif /* CONFIG_GPIOLIB */ +#if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_HTE) +int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags); +int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags); +#else +static inline int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, + unsigned long flags) +{ + if (!IS_ENABLED(CONFIG_GPIOLIB)) + WARN_ON(desc); + + return -ENOSYS; +} +static inline int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, + unsigned long flags) +{ + if (!IS_ENABLED(CONFIG_GPIOLIB)) + WARN_ON(desc); + + return -ENOSYS; +} +#endif /* CONFIG_GPIOLIB && CONFIG_HTE */ + static inline struct gpio_desc *devm_fwnode_gpiod_get(struct device *dev, struct fwnode_handle *fwnode, From 375790f18396b2ba706e031b150c58cd37b45a11 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 21:48:46 +0200 Subject: [PATCH 029/119] gpiolib: Extract gpiochip_choose_fwnode() for wider use Extract gpiochip_choose_fwnode() for the future use in another function. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Tested-by: Mathieu Dubois-Briand Reviewed-by: Mathieu Dubois-Briand Link: https://lore.kernel.org/r/20250213195621.3133406-2-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 65ca749a1078..ba3de5f7d203 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -883,6 +883,21 @@ void *gpiochip_get_data(struct gpio_chip *gc) } EXPORT_SYMBOL_GPL(gpiochip_get_data); +/* + * If the calling driver provides the specific firmware node, + * use it. Otherwise use the one from the parent device, if any. + */ +static struct fwnode_handle *gpiochip_choose_fwnode(struct gpio_chip *gc) +{ + if (gc->fwnode) + return gc->fwnode; + + if (gc->parent) + return dev_fwnode(gc->parent); + + return NULL; +} + int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev) { u32 ngpios = gc->ngpio; @@ -942,14 +957,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, gc->gpiodev = gdev; gpiochip_set_data(gc, data); - /* - * 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)); + device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc)); gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL); if (gdev->id < 0) { From 6f077e575893214136f9739f993bd9fedf61731a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 21:48:47 +0200 Subject: [PATCH 030/119] gpiolib: Use fwnode instead of device in gpiochip_get_ngpios() The gpiochip_get_ngpios() can be used in the cases where passed device is not a provider of the certain property. Use fwnode instead. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Tested-by: Mathieu Dubois-Briand Reviewed-by: Mathieu Dubois-Briand Link: https://lore.kernel.org/r/20250213195621.3133406-3-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index ba3de5f7d203..637553378235 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -900,11 +900,12 @@ static struct fwnode_handle *gpiochip_choose_fwnode(struct gpio_chip *gc) int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev) { + struct fwnode_handle *fwnode = gpiochip_choose_fwnode(gc); u32 ngpios = gc->ngpio; int ret; if (ngpios == 0) { - ret = device_property_read_u32(dev, "ngpios", &ngpios); + ret = fwnode_property_read_u32(fwnode, "ngpios", &ngpios); if (ret == -ENODATA) /* * -ENODATA means that there is no property found and From 97673ea38a77e42eaafcf5181c84f6c8d40b97e7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 21:48:48 +0200 Subject: [PATCH 031/119] gpio: regmap: Group optional assignments together for better understanding Group ngpio_per_reg, reg_stride, and reg_mask_xlate assignments together with the respective conditional for better understanding what's going on in the code. While at it, mark ngpio_per_reg as (Optional) in the kernel-doc in accordance with what code actually does. Signed-off-by: Andy Shevchenko Reviewed-by: Michael Walle Reviewed-by: Linus Walleij Tested-by: Mathieu Dubois-Briand Reviewed-by: Mathieu Dubois-Briand Link: https://lore.kernel.org/r/20250213195621.3133406-4-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-regmap.c | 6 +++--- include/linux/gpio/regmap.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index 05f8781b5204..7775b0c56602 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -233,9 +233,6 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config gpio->parent = config->parent; gpio->driver_data = config->drvdata; gpio->regmap = config->regmap; - gpio->ngpio_per_reg = config->ngpio_per_reg; - gpio->reg_stride = config->reg_stride; - gpio->reg_mask_xlate = config->reg_mask_xlate; gpio->reg_dat_base = config->reg_dat_base; gpio->reg_set_base = config->reg_set_base; gpio->reg_clr_base = config->reg_clr_base; @@ -243,13 +240,16 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config gpio->reg_dir_out_base = config->reg_dir_out_base; /* if not set, assume there is only one register */ + gpio->ngpio_per_reg = config->ngpio_per_reg; if (!gpio->ngpio_per_reg) gpio->ngpio_per_reg = config->ngpio; /* if not set, assume they are consecutive */ + gpio->reg_stride = config->reg_stride; if (!gpio->reg_stride) gpio->reg_stride = 1; + gpio->reg_mask_xlate = config->reg_mask_xlate; if (!gpio->reg_mask_xlate) gpio->reg_mask_xlate = gpio_regmap_simple_xlate; diff --git a/include/linux/gpio/regmap.h b/include/linux/gpio/regmap.h index a9f7b7faf57b..b9240e4156cc 100644 --- a/include/linux/gpio/regmap.h +++ b/include/linux/gpio/regmap.h @@ -30,7 +30,7 @@ struct regmap; * @reg_dir_out_base: (Optional) out setting register base address * @reg_stride: (Optional) May be set if the registers (of the * same type, dat, set, etc) are not consecutive. - * @ngpio_per_reg: Number of GPIOs per register + * @ngpio_per_reg: (Optional) Number of GPIOs per register * @irq_domain: (Optional) IRQ domain if the controller is * interrupt-capable * @reg_mask_xlate: (Optional) Translates base address and GPIO From a630d3960b6ac3c37cb0789605056e8845ffbf16 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 21:48:49 +0200 Subject: [PATCH 032/119] gpio: regmap: Move optional assignments down in the code Move optional assignments down in the code, so they may use some values from the (updated) struct gpio_chip later on. Signed-off-by: Andy Shevchenko Reviewed-by: Michael Walle Reviewed-by: Linus Walleij Tested-by: Mathieu Dubois-Briand Reviewed-by: Mathieu Dubois-Briand Link: https://lore.kernel.org/r/20250213195621.3133406-5-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-regmap.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index 7775b0c56602..41ee576e7cd0 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -239,20 +239,6 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config gpio->reg_dir_in_base = config->reg_dir_in_base; gpio->reg_dir_out_base = config->reg_dir_out_base; - /* if not set, assume there is only one register */ - gpio->ngpio_per_reg = config->ngpio_per_reg; - if (!gpio->ngpio_per_reg) - gpio->ngpio_per_reg = config->ngpio; - - /* if not set, assume they are consecutive */ - gpio->reg_stride = config->reg_stride; - if (!gpio->reg_stride) - gpio->reg_stride = 1; - - gpio->reg_mask_xlate = config->reg_mask_xlate; - if (!gpio->reg_mask_xlate) - gpio->reg_mask_xlate = gpio_regmap_simple_xlate; - chip = &gpio->gpio_chip; chip->parent = config->parent; chip->fwnode = config->fwnode; @@ -276,6 +262,20 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config chip->direction_output = gpio_regmap_direction_output; } + /* if not set, assume there is only one register */ + gpio->ngpio_per_reg = config->ngpio_per_reg; + if (!gpio->ngpio_per_reg) + gpio->ngpio_per_reg = config->ngpio; + + /* if not set, assume they are consecutive */ + gpio->reg_stride = config->reg_stride; + if (!gpio->reg_stride) + gpio->reg_stride = 1; + + gpio->reg_mask_xlate = config->reg_mask_xlate; + if (!gpio->reg_mask_xlate) + gpio->reg_mask_xlate = gpio_regmap_simple_xlate; + ret = gpiochip_add_data(chip, gpio); if (ret < 0) goto err_free_gpio; From db305161880a024a43f4b1cbafa7a294793d7a9e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 21:48:50 +0200 Subject: [PATCH 033/119] gpio: regmap: Allow ngpio to be read from the property GPIOLIB supports the case when number of supported GPIOs can be read from the device property. Enable this for drivers that are using GPIO regmap layer. Signed-off-by: Andy Shevchenko Reviewed-by: Michael Walle Reviewed-by: Linus Walleij Tested-by: Mathieu Dubois-Briand Reviewed-by: Mathieu Dubois-Briand Link: https://lore.kernel.org/r/20250213195621.3133406-6-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-regmap.c | 13 +++++++++---- include/linux/gpio/regmap.h | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index 41ee576e7cd0..856f8569566e 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -17,6 +17,8 @@ #include #include +#include "gpiolib.h" + struct gpio_regmap { struct device *parent; struct regmap *regmap; @@ -210,9 +212,6 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config if (!config->parent) return ERR_PTR(-EINVAL); - if (!config->ngpio) - return ERR_PTR(-EINVAL); - /* we need at least one */ if (!config->reg_dat_base && !config->reg_set_base) return ERR_PTR(-EINVAL); @@ -243,7 +242,6 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config chip->parent = config->parent; chip->fwnode = config->fwnode; chip->base = -1; - chip->ngpio = config->ngpio; chip->names = config->names; chip->label = config->label ?: dev_name(config->parent); chip->can_sleep = regmap_might_sleep(config->regmap); @@ -262,6 +260,13 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config chip->direction_output = gpio_regmap_direction_output; } + chip->ngpio = config->ngpio; + if (!chip->ngpio) { + ret = gpiochip_get_ngpios(chip, chip->parent); + if (ret) + return ERR_PTR(ret); + } + /* if not set, assume there is only one register */ gpio->ngpio_per_reg = config->ngpio_per_reg; if (!gpio->ngpio_per_reg) diff --git a/include/linux/gpio/regmap.h b/include/linux/gpio/regmap.h index b9240e4156cc..c722c67668c6 100644 --- a/include/linux/gpio/regmap.h +++ b/include/linux/gpio/regmap.h @@ -21,7 +21,7 @@ struct regmap; * If not given, the fwnode of the parent is used. * @label: (Optional) Descriptive name for GPIO controller. * If not given, the name of the device is used. - * @ngpio: Number of GPIOs + * @ngpio: (Optional) Number of GPIOs * @names: (Optional) Array of names for gpios * @reg_dat_base: (Optional) (in) register base address * @reg_set_base: (Optional) set register base address From 69920338f8130da929ade6f93e6fa3e0e68433ee Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Feb 2025 11:51:56 +0100 Subject: [PATCH 034/119] gpiolib: sanitize the return value of gpio_chip::request() The return value of the request() callback may be propagated to user-space. If a bad driver returns a positive number, it may confuse user programs. Tighten the API contract and check for positive numbers returned by GPIO controllers. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250210-gpio-sanitize-retvals-v1-2-12ea88506cb2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 2 ++ include/linux/gpio/driver.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index fea0cdec0b26..a98025b0ecf7 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2349,6 +2349,8 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) ret = guard.gc->request(guard.gc, offset); else ret = -EINVAL; + if (ret > 0) + ret = -EBADE; if (ret) goto out_clear_bit; } diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 10544f4a03e5..ce22c072337c 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -329,7 +329,8 @@ struct gpio_irq_chip { * @fwnode: optional fwnode providing this controller's properties * @owner: helps prevent removal of modules exporting active GPIOs * @request: optional hook for chip-specific activation, such as - * enabling module power and clock; may sleep + * enabling module power and clock; may sleep; must return 0 on success + * or negative error number on failure * @free: optional hook for chip-specific deactivation, such as * disabling module power and clock; may sleep * @get_direction: returns direction for signal "offset", 0=out, 1=in, From dcf8f3bffa2de2c7f3b5771b63605194ccd2286f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Feb 2025 11:51:57 +0100 Subject: [PATCH 035/119] gpiolib: sanitize the return value of gpio_chip::set_config() The return value of the set_config() callback may be propagated to user-space. If a bad driver returns a positive number, it may confuse user programs. Tighten the API contract and check for positive numbers returned by GPIO controllers. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250210-gpio-sanitize-retvals-v1-3-12ea88506cb2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 3 +++ include/linux/gpio/driver.h | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index a98025b0ecf7..67a735d57942 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2593,6 +2593,9 @@ int gpio_do_set_config(struct gpio_desc *desc, unsigned long config) return -ENOTSUPP; ret = guard.gc->set_config(guard.gc, gpio_chip_hwgpio(desc), config); + if (ret > 0) + ret = -EBADE; + #ifdef CONFIG_GPIO_CDEV /* * Special case - if we're setting debounce period, we need to store diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index ce22c072337c..f2145e938b29 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -349,7 +349,8 @@ struct gpio_irq_chip { * @set: assigns output value for signal "offset" * @set_multiple: assigns output values for multiple signals defined by "mask" * @set_config: optional hook for all kinds of settings. Uses the same - * packed config format as generic pinconf. + * packed config format as generic pinconf. Must return 0 on success and + * a negative error number on failure. * @to_irq: optional hook supporting non-static gpiod_to_irq() mappings; * implementation may not sleep * @dbg_show: optional routine to show contents in debugfs; default code From 86ef402d805d606a10e6da8e5a64a51f6f5fb7e2 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Feb 2025 11:51:58 +0100 Subject: [PATCH 036/119] gpiolib: sanitize the return value of gpio_chip::get() As per the API contract, the get() callback is only allowed to return 0, 1 or a negative error number. Add a wrapper around the callback calls that filters out anything else. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250210-gpio-sanitize-retvals-v1-4-12ea88506cb2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 67a735d57942..1a3f527aba0e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3115,9 +3115,25 @@ void gpiod_toggle_active_low(struct gpio_desc *desc) } EXPORT_SYMBOL_GPL(gpiod_toggle_active_low); +static int gpiochip_get(struct gpio_chip *gc, unsigned int offset) +{ + int ret; + + lockdep_assert_held(&gc->gpiodev->srcu); + + if (!gc->get) + return -EIO; + + ret = gc->get(gc, offset); + if (ret > 1) + ret = -EBADE; + + return ret; +} + static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *desc) { - return gc->get ? gc->get(gc, gpio_chip_hwgpio(desc)) : -EIO; + return gpiochip_get(gc, gpio_chip_hwgpio(desc)); } /* I/O calls are only valid after configuration completed; the relevant @@ -3174,7 +3190,7 @@ static int gpio_chip_get_multiple(struct gpio_chip *gc, int i, value; for_each_set_bit(i, mask, gc->ngpio) { - value = gc->get(gc, i); + value = gpiochip_get(gc, i); if (value < 0) return value; __assign_bit(i, bits, value); From 74abd086d2ee5ef10f68848cfe39e271ac798685 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Feb 2025 11:51:59 +0100 Subject: [PATCH 037/119] gpiolib: sanitize the return value of gpio_chip::get_multiple() As per the API contract, the get_multiple() callback is only allowed to return 0 or a negative error number. Filter out anything else. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250210-gpio-sanitize-retvals-v1-5-12ea88506cb2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1a3f527aba0e..9ac2dad0ad34 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3182,10 +3182,16 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) static int gpio_chip_get_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { + int ret; + lockdep_assert_held(&gc->gpiodev->srcu); - if (gc->get_multiple) - return gc->get_multiple(gc, mask, bits); + if (gc->get_multiple) { + ret = gc->get_multiple(gc, mask, bits); + if (ret > 0) + return -EBADE; + } + if (gc->get) { int i, value; From dfeb70c86d637d49af9313245e6b1f2ead08ce9b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Feb 2025 11:52:00 +0100 Subject: [PATCH 038/119] gpiolib: sanitize the return value of gpio_chip::direction_output() The return value of the direction_output() callback may be propagated to user-space. As per the API contract it can only return 0 or a negative error number. Add a wrapper around the callback calls that filters out anything else. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250210-gpio-sanitize-retvals-v1-6-12ea88506cb2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 9ac2dad0ad34..2c8c4183f6f0 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2701,6 +2701,23 @@ int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce) return ret; } +static int gpiochip_direction_output(struct gpio_chip *gc, unsigned int offset, + int value) +{ + int ret; + + lockdep_assert_held(&gc->gpiodev->srcu); + + if (WARN_ON(!gc->direction_output)) + return -EOPNOTSUPP; + + ret = gc->direction_output(gc, offset, value); + if (ret > 0) + ret = -EBADE; + + return ret; +} + /** * gpiod_direction_input - set the GPIO direction to input * @desc: GPIO to set to input @@ -2798,8 +2815,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) } if (guard.gc->direction_output) { - ret = guard.gc->direction_output(guard.gc, - gpio_chip_hwgpio(desc), val); + ret = gpiochip_direction_output(guard.gc, + gpio_chip_hwgpio(desc), val); } else { /* Check that we are in output mode if we can */ if (guard.gc->get_direction) { @@ -3460,7 +3477,7 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) if (value) { ret = guard.gc->direction_input(guard.gc, offset); } else { - ret = guard.gc->direction_output(guard.gc, offset, 0); + ret = gpiochip_direction_output(guard.gc, offset, 0); if (!ret) set_bit(FLAG_IS_OUT, &desc->flags); } @@ -3485,7 +3502,7 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value return; if (value) { - ret = guard.gc->direction_output(guard.gc, offset, 1); + ret = gpiochip_direction_output(guard.gc, offset, 1); if (!ret) set_bit(FLAG_IS_OUT, &desc->flags); } else { From 4750ddce95ae8be618e31b0aa51efcf50e23a78e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Feb 2025 11:52:01 +0100 Subject: [PATCH 039/119] gpiolib: sanitize the return value of gpio_chip::direction_input() The return value of the direction_input() callback may be propagated to user-space. As per the API contract it can only return 0 or a negative error number. Add a wrapper around the callback calls that filters out anything else. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250210-gpio-sanitize-retvals-v1-7-12ea88506cb2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 2c8c4183f6f0..e720a1d2f343 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2701,6 +2701,22 @@ int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce) return ret; } +static int gpiochip_direction_input(struct gpio_chip *gc, unsigned int offset) +{ + int ret; + + lockdep_assert_held(&gc->gpiodev->srcu); + + if (WARN_ON(!gc->direction_input)) + return -EOPNOTSUPP; + + ret = gc->direction_input(gc, offset); + if (ret > 0) + ret = -EBADE; + + return ret; +} + static int gpiochip_direction_output(struct gpio_chip *gc, unsigned int offset, int value) { @@ -2769,8 +2785,8 @@ int gpiod_direction_input_nonotify(struct gpio_desc *desc) * assume we are in input mode after this. */ if (guard.gc->direction_input) { - ret = guard.gc->direction_input(guard.gc, - gpio_chip_hwgpio(desc)); + ret = gpiochip_direction_input(guard.gc, + gpio_chip_hwgpio(desc)); } else if (guard.gc->get_direction) { ret = guard.gc->get_direction(guard.gc, gpio_chip_hwgpio(desc)); @@ -3475,7 +3491,7 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) return; if (value) { - ret = guard.gc->direction_input(guard.gc, offset); + ret = gpiochip_direction_input(guard.gc, offset); } else { ret = gpiochip_direction_output(guard.gc, offset, 0); if (!ret) @@ -3506,7 +3522,7 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value if (!ret) set_bit(FLAG_IS_OUT, &desc->flags); } else { - ret = guard.gc->direction_input(guard.gc, offset); + ret = gpiochip_direction_input(guard.gc, offset); } trace_gpio_direction(desc_to_gpio(desc), !value, ret); if (ret < 0) From e623c4303ed112a1fc20aec8427ba8407e2842e6 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Feb 2025 11:52:02 +0100 Subject: [PATCH 040/119] gpiolib: sanitize the return value of gpio_chip::get_direction() As per the API contract, the get_direction() callback can only return 0, 1 or a negative error number. Add a wrapper around the callback calls that filters out anything else. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250210-gpio-sanitize-retvals-v1-8-12ea88506cb2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e720a1d2f343..9103384f4a5e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -342,6 +342,22 @@ static int gpiochip_find_base_unlocked(u16 ngpio) } } +static int gpiochip_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + int ret; + + lockdep_assert_held(&gc->gpiodev->srcu); + + if (WARN_ON(!gc->get_direction)) + return -EOPNOTSUPP; + + ret = gc->get_direction(gc, offset); + if (ret > 1) + ret = -EBADE; + + return ret; +} + /** * gpiod_get_direction - return the current direction of a GPIO * @desc: GPIO to get the direction of @@ -382,7 +398,7 @@ int gpiod_get_direction(struct gpio_desc *desc) if (!guard.gc->get_direction) return -ENOTSUPP; - ret = guard.gc->get_direction(guard.gc, offset); + ret = gpiochip_get_direction(guard.gc, offset); if (ret < 0) return ret; @@ -1067,7 +1083,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, desc->gdev = gdev; if (gc->get_direction && gpiochip_line_is_valid(gc, desc_index)) { - ret = gc->get_direction(gc, desc_index); + ret = gpiochip_get_direction(gc, desc_index); if (ret < 0) /* * FIXME: Bail-out here once all GPIO drivers @@ -2788,8 +2804,7 @@ int gpiod_direction_input_nonotify(struct gpio_desc *desc) ret = gpiochip_direction_input(guard.gc, gpio_chip_hwgpio(desc)); } else if (guard.gc->get_direction) { - ret = guard.gc->get_direction(guard.gc, - gpio_chip_hwgpio(desc)); + ret = gpiochip_get_direction(guard.gc, gpio_chip_hwgpio(desc)); if (ret < 0) return ret; @@ -2836,8 +2851,8 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) } else { /* Check that we are in output mode if we can */ if (guard.gc->get_direction) { - ret = guard.gc->get_direction(guard.gc, - gpio_chip_hwgpio(desc)); + ret = gpiochip_get_direction(guard.gc, + gpio_chip_hwgpio(desc)); if (ret < 0) return ret; From 11067f50458a5bb3b72f83c508e03f321e0c0c34 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Sat, 15 Feb 2025 11:08:47 +0100 Subject: [PATCH 041/119] gpiolib: read descriptor flags once in gpiolib_dbg_show() For consistency with most other code that can access requested descriptors: read the flags once atomically and then test individual bits from the helper variable. This avoids any potential discrepancies should flags change during the debug print. Link: https://lore.kernel.org/r/20250215100847.30136-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 9103384f4a5e..e8678a6c82ea 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -5111,6 +5111,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) unsigned int gpio = gdev->base; struct gpio_desc *desc; struct gpio_chip *gc; + unsigned long flags; int value; guard(srcu)(&gdev->srcu); @@ -5123,12 +5124,13 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) for_each_gpio_desc(gc, desc) { guard(srcu)(&desc->gdev->desc_srcu); - is_irq = test_bit(FLAG_USED_AS_IRQ, &desc->flags); - if (is_irq || test_bit(FLAG_REQUESTED, &desc->flags)) { + flags = READ_ONCE(desc->flags); + is_irq = test_bit(FLAG_USED_AS_IRQ, &flags); + if (is_irq || test_bit(FLAG_REQUESTED, &flags)) { gpiod_get_direction(desc); - is_out = test_bit(FLAG_IS_OUT, &desc->flags); + is_out = test_bit(FLAG_IS_OUT, &flags); value = gpio_chip_get_value(gc, desc); - active_low = test_bit(FLAG_ACTIVE_LOW, &desc->flags); + active_low = test_bit(FLAG_ACTIVE_LOW, &flags); seq_printf(s, " gpio-%-3u (%-20.20s|%-20.20s) %s %s %s%s\n", gpio, desc->name ?: "", gpiod_get_label(desc), is_out ? "out" : "in ", From eb5ab6ffb4ca2d28121455dd7452061367ed5588 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Fri, 21 Feb 2025 22:34:59 +0900 Subject: [PATCH 042/119] gpio: introduce utilities for synchronous fake device creation Both gpio-sim and gpio-virtuser share a mechanism to instantiate a platform device, wait for probe completion, and retrieve the probe success or error status synchronously. With gpio-aggregator planned to adopt this approach for its configfs interface, it's time to factor out the common code. Add dev-sync-probe.[ch] to house helper functions used by all such implementations. No functional change. Signed-off-by: Koichiro Den Link: https://lore.kernel.org/r/20250221133501.2203897-2-koichiro.den@canonical.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 3 ++ drivers/gpio/Makefile | 3 ++ drivers/gpio/dev-sync-probe.c | 97 +++++++++++++++++++++++++++++++++++ drivers/gpio/dev-sync-probe.h | 25 +++++++++ 4 files changed, 128 insertions(+) create mode 100644 drivers/gpio/dev-sync-probe.c create mode 100644 drivers/gpio/dev-sync-probe.h diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f518f5458446..d6ac1a01c636 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1948,3 +1948,6 @@ config GPIO_VIRTUSER endmenu endif + +config DEV_SYNC_PROBE + tristate diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index af3ba4d81b58..af130882ffee 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -19,6 +19,9 @@ obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o # directly supported by gpio-generic gpio-generic-$(CONFIG_GPIO_GENERIC) += gpio-mmio.o +# Utilities for drivers that need synchronous fake device creation +obj-$(CONFIG_DEV_SYNC_PROBE) += dev-sync-probe.o + obj-$(CONFIG_GPIO_104_DIO_48E) += gpio-104-dio-48e.o obj-$(CONFIG_GPIO_104_IDI_48) += gpio-104-idi-48.o obj-$(CONFIG_GPIO_104_IDIO_16) += gpio-104-idio-16.o diff --git a/drivers/gpio/dev-sync-probe.c b/drivers/gpio/dev-sync-probe.c new file mode 100644 index 000000000000..9ea733b863b2 --- /dev/null +++ b/drivers/gpio/dev-sync-probe.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Common code for drivers creating fake platform devices. + * + * Provides synchronous device creation: waits for probe completion and + * returns the probe success or error status to the device creator. + * + * Copyright (C) 2021 Bartosz Golaszewski + * Copyright (C) 2025 Koichiro Den + */ + +#include +#include + +#include "dev-sync-probe.h" + +static int dev_sync_probe_notifier_call(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct dev_sync_probe_data *pdata; + struct device *dev = data; + + pdata = container_of(nb, struct dev_sync_probe_data, bus_notifier); + if (!device_match_name(dev, pdata->name)) + return NOTIFY_DONE; + + switch (action) { + case BUS_NOTIFY_BOUND_DRIVER: + pdata->driver_bound = true; + break; + case BUS_NOTIFY_DRIVER_NOT_BOUND: + pdata->driver_bound = false; + break; + default: + return NOTIFY_DONE; + } + + complete(&pdata->probe_completion); + return NOTIFY_OK; +} + +void dev_sync_probe_init(struct dev_sync_probe_data *data) +{ + memset(data, 0, sizeof(*data)); + init_completion(&data->probe_completion); + data->bus_notifier.notifier_call = dev_sync_probe_notifier_call; +} +EXPORT_SYMBOL_GPL(dev_sync_probe_init); + +int dev_sync_probe_register(struct dev_sync_probe_data *data, + struct platform_device_info *pdevinfo) +{ + struct platform_device *pdev; + char *name; + + name = kasprintf(GFP_KERNEL, "%s.%d", pdevinfo->name, pdevinfo->id); + if (!name) + return -ENOMEM; + + data->driver_bound = false; + data->name = name; + reinit_completion(&data->probe_completion); + bus_register_notifier(&platform_bus_type, &data->bus_notifier); + + pdev = platform_device_register_full(pdevinfo); + if (IS_ERR(pdev)) { + bus_unregister_notifier(&platform_bus_type, &data->bus_notifier); + kfree(data->name); + return PTR_ERR(pdev); + } + + wait_for_completion(&data->probe_completion); + bus_unregister_notifier(&platform_bus_type, &data->bus_notifier); + + if (!data->driver_bound) { + platform_device_unregister(pdev); + kfree(data->name); + return -ENXIO; + } + + data->pdev = pdev; + return 0; +} +EXPORT_SYMBOL_GPL(dev_sync_probe_register); + +void dev_sync_probe_unregister(struct dev_sync_probe_data *data) +{ + platform_device_unregister(data->pdev); + kfree(data->name); + data->pdev = NULL; +} +EXPORT_SYMBOL_GPL(dev_sync_probe_unregister); + +MODULE_AUTHOR("Bartosz Golaszewski "); +MODULE_AUTHOR("Koichiro Den "); +MODULE_DESCRIPTION("Utilities for synchronous fake device creation"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/dev-sync-probe.h b/drivers/gpio/dev-sync-probe.h new file mode 100644 index 000000000000..4b3d52b70519 --- /dev/null +++ b/drivers/gpio/dev-sync-probe.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef DEV_SYNC_PROBE_H +#define DEV_SYNC_PROBE_H + +#include +#include +#include + +struct dev_sync_probe_data { + struct platform_device *pdev; + const char *name; + + /* Synchronize with probe */ + struct notifier_block bus_notifier; + struct completion probe_completion; + bool driver_bound; +}; + +void dev_sync_probe_init(struct dev_sync_probe_data *data); +int dev_sync_probe_register(struct dev_sync_probe_data *data, + struct platform_device_info *pdevinfo); +void dev_sync_probe_unregister(struct dev_sync_probe_data *data); + +#endif /* DEV_SYNC_PROBE_H */ From 2f41dbf9cb84349f510ebf2165c13102f79a550b Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Fri, 21 Feb 2025 22:35:00 +0900 Subject: [PATCH 043/119] gpio: sim: convert to use dev-sync-probe utilities Update gpio-sim to use the new dev-sync-probe helper functions for synchronized platform device creation, reducing code duplication. No functional change. Signed-off-by: Koichiro Den Link: https://lore.kernel.org/r/20250221133501.2203897-3-koichiro.den@canonical.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-sim.c | 84 ++++++----------------------------------- 2 files changed, 13 insertions(+), 72 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d6ac1a01c636..5b167c413422 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1910,6 +1910,7 @@ config GPIO_SIM tristate "GPIO Simulator Module" select IRQ_SIM select CONFIGFS_FS + select DEV_SYNC_PROBE help This enables the GPIO simulator - a configfs-based GPIO testing driver. diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index b6c230fab840..758933fbd85b 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -37,6 +36,8 @@ #include #include +#include "dev-sync-probe.h" + #define GPIO_SIM_NGPIO_MAX 1024 #define GPIO_SIM_PROP_MAX 4 /* Max 3 properties + sentinel. */ #define GPIO_SIM_NUM_ATTRS 3 /* value, pull and sentinel */ @@ -541,14 +542,9 @@ static struct platform_driver gpio_sim_driver = { }; struct gpio_sim_device { + struct dev_sync_probe_data probe_data; struct config_group group; - /* - * If pdev is NULL, the device is 'pending' (waiting for configuration). - * Once the pointer is assigned, the device has been created and the - * item is 'live'. - */ - struct platform_device *pdev; int id; /* @@ -562,46 +558,11 @@ struct gpio_sim_device { */ struct mutex lock; - /* - * This is used to synchronously wait for the driver's probe to complete - * and notify the user-space about any errors. - */ - struct notifier_block bus_notifier; - struct completion probe_completion; - bool driver_bound; - struct gpiod_hog *hogs; struct list_head bank_list; }; -/* This is called with dev->lock already taken. */ -static int gpio_sim_bus_notifier_call(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct gpio_sim_device *simdev = container_of(nb, - struct gpio_sim_device, - bus_notifier); - struct device *dev = data; - char devname[32]; - - snprintf(devname, sizeof(devname), "gpio-sim.%u", simdev->id); - - if (!device_match_name(dev, devname)) - return NOTIFY_DONE; - - if (action == BUS_NOTIFY_BOUND_DRIVER) - simdev->driver_bound = true; - else if (action == BUS_NOTIFY_DRIVER_NOT_BOUND) - simdev->driver_bound = false; - else - return NOTIFY_DONE; - - complete(&simdev->probe_completion); - - return NOTIFY_OK; -} - static struct gpio_sim_device *to_gpio_sim_device(struct config_item *item) { struct config_group *group = to_config_group(item); @@ -708,7 +669,7 @@ static bool gpio_sim_device_is_live(struct gpio_sim_device *dev) { lockdep_assert_held(&dev->lock); - return !!dev->pdev; + return !!dev->probe_data.pdev; } static char *gpio_sim_strdup_trimmed(const char *str, size_t count) @@ -730,7 +691,7 @@ static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item, guard(mutex)(&dev->lock); - pdev = dev->pdev; + pdev = dev->probe_data.pdev; if (pdev) return sprintf(page, "%s\n", dev_name(&pdev->dev)); @@ -939,7 +900,6 @@ static int gpio_sim_device_activate(struct gpio_sim_device *dev) { struct platform_device_info pdevinfo; struct fwnode_handle *swnode; - struct platform_device *pdev; struct gpio_sim_bank *bank; int ret; @@ -981,31 +941,13 @@ static int gpio_sim_device_activate(struct gpio_sim_device *dev) pdevinfo.fwnode = swnode; pdevinfo.id = dev->id; - reinit_completion(&dev->probe_completion); - dev->driver_bound = false; - bus_register_notifier(&platform_bus_type, &dev->bus_notifier); - - pdev = platform_device_register_full(&pdevinfo); - if (IS_ERR(pdev)) { - bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier); + ret = dev_sync_probe_register(&dev->probe_data, &pdevinfo); + if (ret) { gpio_sim_remove_hogs(dev); gpio_sim_remove_swnode_recursive(swnode); - return PTR_ERR(pdev); + return ret; } - wait_for_completion(&dev->probe_completion); - bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier); - - if (!dev->driver_bound) { - /* Probe failed, check kernel log. */ - platform_device_unregister(pdev); - gpio_sim_remove_hogs(dev); - gpio_sim_remove_swnode_recursive(swnode); - return -ENXIO; - } - - dev->pdev = pdev; - return 0; } @@ -1015,11 +957,10 @@ static void gpio_sim_device_deactivate(struct gpio_sim_device *dev) lockdep_assert_held(&dev->lock); - swnode = dev_fwnode(&dev->pdev->dev); - platform_device_unregister(dev->pdev); + swnode = dev_fwnode(&dev->probe_data.pdev->dev); + dev_sync_probe_unregister(&dev->probe_data); gpio_sim_remove_hogs(dev); gpio_sim_remove_swnode_recursive(swnode); - dev->pdev = NULL; } static void @@ -1120,7 +1061,7 @@ static ssize_t gpio_sim_bank_config_chip_name_show(struct config_item *item, guard(mutex)(&dev->lock); if (gpio_sim_device_is_live(dev)) - return device_for_each_child(&dev->pdev->dev, &ctx, + return device_for_each_child(&dev->probe_data.pdev->dev, &ctx, gpio_sim_emit_chip_name); return sprintf(page, "none\n"); @@ -1561,8 +1502,7 @@ gpio_sim_config_make_device_group(struct config_group *group, const char *name) mutex_init(&dev->lock); INIT_LIST_HEAD(&dev->bank_list); - dev->bus_notifier.notifier_call = gpio_sim_bus_notifier_call; - init_completion(&dev->probe_completion); + dev_sync_probe_init(&dev->probe_data); return &no_free_ptr(dev)->group; } From 45af02f06f6943d73cf9309fd2a63a908b587f57 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Fri, 21 Feb 2025 22:35:01 +0900 Subject: [PATCH 044/119] gpio: virtuser: convert to use dev-sync-probe utilities Update gpio-virtuser to use the new dev-sync-probe helper functions for synchronized platform device creation, reducing code duplication. No functional change. Signed-off-by: Koichiro Den Link: https://lore.kernel.org/r/20250221133501.2203897-4-koichiro.den@canonical.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-virtuser.c | 73 +++++------------------------------- 2 files changed, 11 insertions(+), 63 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5b167c413422..06cc9b826483 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1939,6 +1939,7 @@ config GPIO_VIRTUSER select DEBUG_FS select CONFIGFS_FS select IRQ_WORK + select DEV_SYNC_PROBE help Say Y here to enable the configurable, configfs-based virtual GPIO consumer testing driver. diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c index e89f299f2140..13407fd4f0eb 100644 --- a/drivers/gpio/gpio-virtuser.c +++ b/drivers/gpio/gpio-virtuser.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -37,6 +36,8 @@ #include #include +#include "dev-sync-probe.h" + #define GPIO_VIRTUSER_NAME_BUF_LEN 32 static DEFINE_IDA(gpio_virtuser_ida); @@ -973,49 +974,17 @@ static struct platform_driver gpio_virtuser_driver = { }; struct gpio_virtuser_device { + struct dev_sync_probe_data probe_data; struct config_group group; - struct platform_device *pdev; int id; struct mutex lock; - struct notifier_block bus_notifier; - struct completion probe_completion; - bool driver_bound; - struct gpiod_lookup_table *lookup_table; struct list_head lookup_list; }; -static int gpio_virtuser_bus_notifier_call(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct gpio_virtuser_device *vdev; - struct device *dev = data; - char devname[32]; - - vdev = container_of(nb, struct gpio_virtuser_device, bus_notifier); - snprintf(devname, sizeof(devname), "gpio-virtuser.%d", vdev->id); - - if (!device_match_name(dev, devname)) - return NOTIFY_DONE; - - switch (action) { - case BUS_NOTIFY_BOUND_DRIVER: - vdev->driver_bound = true; - break; - case BUS_NOTIFY_DRIVER_NOT_BOUND: - vdev->driver_bound = false; - break; - default: - return NOTIFY_DONE; - } - - complete(&vdev->probe_completion); - return NOTIFY_OK; -} - static struct gpio_virtuser_device * to_gpio_virtuser_device(struct config_item *item) { @@ -1029,7 +998,7 @@ gpio_virtuser_device_is_live(struct gpio_virtuser_device *dev) { lockdep_assert_held(&dev->lock); - return !!dev->pdev; + return !!dev->probe_data.pdev; } struct gpio_virtuser_lookup { @@ -1369,7 +1338,7 @@ gpio_virtuser_device_config_dev_name_show(struct config_item *item, guard(mutex)(&dev->lock); - pdev = dev->pdev; + pdev = dev->probe_data.pdev; if (pdev) return sprintf(page, "%s\n", dev_name(&pdev->dev)); @@ -1478,7 +1447,6 @@ gpio_virtuser_device_activate(struct gpio_virtuser_device *dev) { struct platform_device_info pdevinfo; struct fwnode_handle *swnode; - struct platform_device *pdev; int ret; lockdep_assert_held(&dev->lock); @@ -1499,31 +1467,12 @@ gpio_virtuser_device_activate(struct gpio_virtuser_device *dev) if (ret) goto err_remove_swnode; - reinit_completion(&dev->probe_completion); - dev->driver_bound = false; - bus_register_notifier(&platform_bus_type, &dev->bus_notifier); - - pdev = platform_device_register_full(&pdevinfo); - if (IS_ERR(pdev)) { - ret = PTR_ERR(pdev); - bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier); + ret = dev_sync_probe_register(&dev->probe_data, &pdevinfo); + if (ret) goto err_remove_lookup_table; - } - - wait_for_completion(&dev->probe_completion); - bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier); - - if (!dev->driver_bound) { - ret = -ENXIO; - goto err_unregister_pdev; - } - - dev->pdev = pdev; return 0; -err_unregister_pdev: - platform_device_unregister(pdev); err_remove_lookup_table: gpio_virtuser_remove_lookup_table(dev); err_remove_swnode: @@ -1539,11 +1488,10 @@ gpio_virtuser_device_deactivate(struct gpio_virtuser_device *dev) lockdep_assert_held(&dev->lock); - swnode = dev_fwnode(&dev->pdev->dev); - platform_device_unregister(dev->pdev); + swnode = dev_fwnode(&dev->probe_data.pdev->dev); + dev_sync_probe_unregister(&dev->probe_data); gpio_virtuser_remove_lookup_table(dev); fwnode_remove_software_node(swnode); - dev->pdev = NULL; } static void @@ -1772,8 +1720,7 @@ gpio_virtuser_config_make_device_group(struct config_group *group, &gpio_virtuser_device_config_group_type); mutex_init(&dev->lock); INIT_LIST_HEAD(&dev->lookup_list); - dev->bus_notifier.notifier_call = gpio_virtuser_bus_notifier_call; - init_completion(&dev->probe_completion); + dev_sync_probe_init(&dev->probe_data); return &no_free_ptr(dev)->group; } From 2145ba374069ee8edc9d29c2a6b56fe4a28a6e2d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 19 Feb 2025 22:04:33 +0100 Subject: [PATCH 045/119] gpio: mmio: Add flag for calling pinctrl back-end It turns out that with this flag we can switch over an entire driver to use gpio-mmio instead of a bunch of custom code, also providing get/set_multiple() to it in the process, so it seems like a reasonable feature to add. The generic pin control backend requires us to call the gpiochip_generic_request(), gpiochip_generic_free(), pinctrl_gpio_direction_output() and pinctrl_gpio_direction_input() callbacks, so if the new flag for a pin control back-end is set, we make sure these functions get called as expected. Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20250219-vf610-mmio-v3-1-588b64f0b689@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mmio.c | 37 +++++++++++++++++++++++++++++-------- include/linux/gpio/driver.h | 3 +++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index d89e78f0ead3..4841e4ebe7a6 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -49,6 +49,7 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` #include #include #include +#include #include #include #include @@ -323,9 +324,20 @@ static void bgpio_set_multiple_with_clear(struct gpio_chip *gc, gc->write_reg(gc->reg_clr, clear_mask); } +static int bgpio_dir_return(struct gpio_chip *gc, unsigned int gpio, bool dir_out) +{ + if (!gc->bgpio_pinctrl) + return 0; + + if (dir_out) + return pinctrl_gpio_direction_output(gc, gpio); + else + return pinctrl_gpio_direction_input(gc, gpio); +} + static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio) { - return 0; + return bgpio_dir_return(gc, gpio, false); } static int bgpio_dir_out_err(struct gpio_chip *gc, unsigned int gpio, @@ -339,7 +351,7 @@ static int bgpio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio, { gc->set(gc, gpio, val); - return 0; + return bgpio_dir_return(gc, gpio, true); } static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) @@ -357,7 +369,7 @@ static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); - return 0; + return bgpio_dir_return(gc, gpio, false); } static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio) @@ -403,7 +415,7 @@ static int bgpio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio, { bgpio_dir_out(gc, gpio, val); gc->set(gc, gpio, val); - return 0; + return bgpio_dir_return(gc, gpio, true); } static int bgpio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio, @@ -411,7 +423,7 @@ static int bgpio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio, { gc->set(gc, gpio, val); bgpio_dir_out(gc, gpio, val); - return 0; + return bgpio_dir_return(gc, gpio, true); } static int bgpio_setup_accessors(struct device *dev, @@ -562,10 +574,13 @@ static int bgpio_setup_direction(struct gpio_chip *gc, static int bgpio_request(struct gpio_chip *chip, unsigned gpio_pin) { - if (gpio_pin < chip->ngpio) - return 0; + if (gpio_pin >= chip->ngpio) + return -EINVAL; - return -EINVAL; + if (chip->bgpio_pinctrl) + return gpiochip_generic_request(chip, gpio_pin); + + return 0; } /** @@ -632,6 +647,12 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev, if (ret) return ret; + if (flags & BGPIOF_PINCTRL_BACKEND) { + gc->bgpio_pinctrl = true; + /* Currently this callback is only used for pincontrol */ + gc->free = gpiochip_generic_free; + } + gc->bgpio_data = gc->read_reg(gc->reg_dat); if (gc->set == bgpio_set_set && !(flags & BGPIOF_UNREADABLE_REG_SET)) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index f2145e938b29..ae96a2f260fb 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -397,6 +397,7 @@ struct gpio_irq_chip { * @reg_dir_in: direction in setting register for generic GPIO * @bgpio_dir_unreadable: indicates that the direction register(s) cannot * be read and we need to rely on out internal state tracking. + * @bgpio_pinctrl: the generic GPIO uses a pin control backend. * @bgpio_bits: number of register bits used for a generic GPIO i.e. * * 8 * @bgpio_lock: used to lock chip->bgpio_data. Also, this is needed to keep @@ -481,6 +482,7 @@ struct gpio_chip { void __iomem *reg_dir_out; void __iomem *reg_dir_in; bool bgpio_dir_unreadable; + bool bgpio_pinctrl; int bgpio_bits; raw_spinlock_t bgpio_lock; unsigned long bgpio_data; @@ -721,6 +723,7 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev, #define BGPIOF_READ_OUTPUT_REG_SET BIT(4) /* reg_set stores output value */ #define BGPIOF_NO_OUTPUT BIT(5) /* only input */ #define BGPIOF_NO_SET_ON_INPUT BIT(6) +#define BGPIOF_PINCTRL_BACKEND BIT(7) /* Call pinctrl direction setters */ #ifdef CONFIG_GPIOLIB_IRQCHIP int gpiochip_irqchip_add_domain(struct gpio_chip *gc, From da5dd31efd2465ccc9a70a85bdc325e394256689 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 19 Feb 2025 22:04:34 +0100 Subject: [PATCH 046/119] gpio: vf610: Switch to gpio-mmio After adding a pinctrl flag to gpio-mmio we can use it for driving gpio-vf610. The existing code has the same semantics and the generic gpio-mmio, including reading from the data out register when the direction is set to input, and it can also handle the absence of the direction register better than the current driver: we get the direction from the shadow direction registers in gpio-mmio instead. Since gpio-mmio has an internal spinlock we can drop the direction-protecting spinlock from the driver. Signed-off-by: Linus Walleij Reviewed-by: Haibo Chen Tested-by: Haibo Chen Link: https://lore.kernel.org/r/20250219-vf610-mmio-v3-2-588b64f0b689@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-vf610.c | 105 ++++++-------------------------------- 2 files changed, 18 insertions(+), 88 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 06cc9b826483..3e9b174fee84 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -757,6 +757,7 @@ config GPIO_VF610 default y if SOC_VF610 depends on ARCH_MXC || COMPILE_TEST select GPIOLIB_IRQCHIP + select GPIO_GENERIC help Say yes here to support i.MX or Vybrid vf610 GPIOs. diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index c36a9dbccd4d..4dad7ce0c4dc 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -36,7 +36,6 @@ struct vf610_gpio_port { struct clk *clk_port; struct clk *clk_gpio; int irq; - spinlock_t lock; /* protect gpio direction registers */ }; #define GPIO_PDOR 0x00 @@ -94,78 +93,6 @@ static inline u32 vf610_gpio_readl(void __iomem *reg) return readl_relaxed(reg); } -static int vf610_gpio_get(struct gpio_chip *gc, unsigned int gpio) -{ - struct vf610_gpio_port *port = gpiochip_get_data(gc); - u32 mask = BIT(gpio); - unsigned long offset = GPIO_PDIR; - - if (port->sdata->have_paddr) { - mask &= vf610_gpio_readl(port->gpio_base + GPIO_PDDR); - if (mask) - offset = GPIO_PDOR; - } - - return !!(vf610_gpio_readl(port->gpio_base + offset) & BIT(gpio)); -} - -static void vf610_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) -{ - struct vf610_gpio_port *port = gpiochip_get_data(gc); - u32 mask = BIT(gpio); - unsigned long offset = val ? GPIO_PSOR : GPIO_PCOR; - - vf610_gpio_writel(mask, port->gpio_base + offset); -} - -static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned int gpio) -{ - struct vf610_gpio_port *port = gpiochip_get_data(chip); - u32 mask = BIT(gpio); - u32 val; - - if (port->sdata->have_paddr) { - guard(spinlock_irqsave)(&port->lock); - val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR); - val &= ~mask; - vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR); - } - - return pinctrl_gpio_direction_input(chip, gpio); -} - -static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned int gpio, - int value) -{ - struct vf610_gpio_port *port = gpiochip_get_data(chip); - u32 mask = BIT(gpio); - u32 val; - - vf610_gpio_set(chip, gpio, value); - - if (port->sdata->have_paddr) { - guard(spinlock_irqsave)(&port->lock); - val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR); - val |= mask; - vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR); - } - - return pinctrl_gpio_direction_output(chip, gpio); -} - -static int vf610_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) -{ - struct vf610_gpio_port *port = gpiochip_get_data(gc); - u32 mask = BIT(gpio); - - mask &= vf610_gpio_readl(port->gpio_base + GPIO_PDDR); - - if (mask) - return GPIO_LINE_DIRECTION_OUT; - - return GPIO_LINE_DIRECTION_IN; -} - static void vf610_gpio_irq_handler(struct irq_desc *desc) { struct vf610_gpio_port *port = @@ -291,6 +218,7 @@ static int vf610_gpio_probe(struct platform_device *pdev) struct vf610_gpio_port *port; struct gpio_chip *gc; struct gpio_irq_chip *girq; + unsigned long flags; int i; int ret; bool dual_base; @@ -300,7 +228,6 @@ static int vf610_gpio_probe(struct platform_device *pdev) return -ENOMEM; port->sdata = device_get_match_data(dev); - spin_lock_init(&port->lock); dual_base = port->sdata->have_dual_base; @@ -367,23 +294,25 @@ static int vf610_gpio_probe(struct platform_device *pdev) } gc = &port->gc; - gc->parent = dev; - gc->label = dev_name(dev); - gc->ngpio = VF610_GPIO_PER_PORT; - gc->base = -1; - - gc->request = gpiochip_generic_request; - gc->free = gpiochip_generic_free; - gc->direction_input = vf610_gpio_direction_input; - gc->get = vf610_gpio_get; - gc->direction_output = vf610_gpio_direction_output; - gc->set = vf610_gpio_set; + flags = BGPIOF_PINCTRL_BACKEND; /* - * only IP has Port Data Direction Register(PDDR) can - * support get direction + * We only read the output register for current value on output + * lines if the direction register is available so we can switch + * direction. */ if (port->sdata->have_paddr) - gc->get_direction = vf610_gpio_get_direction; + flags |= BGPIOF_READ_OUTPUT_REG_SET; + ret = bgpio_init(gc, dev, 4, + port->gpio_base + GPIO_PDIR, + port->gpio_base + GPIO_PDOR, + NULL, + port->sdata->have_paddr ? port->gpio_base + GPIO_PDDR : NULL, + NULL, + flags); + if (ret) + return dev_err_probe(dev, ret, "unable to init generic GPIO\n"); + gc->label = dev_name(dev); + gc->base = -1; /* Mask all GPIO interrupts */ for (i = 0; i < gc->ngpio; i++) From 007094c83872ed33c1d9e39b3ef7168d85a3f214 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 25 Feb 2025 10:52:10 +0100 Subject: [PATCH 047/119] gpiolib: use the required minimum set of headers Andy suggested we should keep a fine-grained scheme for includes and only pull in stuff required within individual ifdef sections. Let's revert commit dea69f2d1cc8 ("gpiolib: move all includes to the top of gpio/consumer.h") and make the headers situation even more fine-grained by only including the first level headers containing requireded symbols except for bug.h where checkpatch.pl warns against including asm/bug.h. Fixes: dea69f2d1cc8 ("gpiolib: move all includes to the top of gpio/consumer.h") Suggested-by: Andy Shevchenko Closes: https://lore.kernel.org/all/Z7XPcYtaA4COHDYj@smile.fi.intel.com/ Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250225095210.25910-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/consumer.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 0b2b56199c36..824a1717e6d2 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -3,10 +3,7 @@ #define __LINUX_GPIO_CONSUMER_H #include -#include #include -#include -#include #include struct acpi_device; @@ -185,6 +182,9 @@ struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev, #else /* CONFIG_GPIOLIB */ +#include +#include + static inline int gpiod_count(struct device *dev, const char *con_id) { return 0; @@ -549,6 +549,9 @@ struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev, int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags); int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags); #else + +#include + static inline int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags) { From 129fdfe25ac513018d5fe85b0c493025193ef19f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 20 Feb 2025 10:56:58 +0100 Subject: [PATCH 048/119] leds: aw200xx: don't use return with gpiod_set_value() variants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While gpiod_set_value() currently returns void, it will soon be converted to return an integer instead. Don't do `return gpiod_set...`. Cc: Lee Jones Cc: Pavel Machek Cc: linux-leds@vger.kernel.org Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202502121512.CmoMg9Q7-lkp@intel.com/ Acked-by: Lee Jones Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250220-gpio-set-retval-v2-1-bc4cfd38dae3@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/leds/leds-aw200xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c index 08cca128458c..fe223d363a5d 100644 --- a/drivers/leds/leds-aw200xx.c +++ b/drivers/leds/leds-aw200xx.c @@ -379,7 +379,7 @@ static void aw200xx_enable(const struct aw200xx *const chip) static void aw200xx_disable(const struct aw200xx *const chip) { - return gpiod_set_value_cansleep(chip->hwen, 0); + gpiod_set_value_cansleep(chip->hwen, 0); } static int aw200xx_probe_get_display_rows(struct device *dev, From 8ce258f62f90cb2d339cc39fa43e5634594a9dfb Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 20 Feb 2025 10:56:59 +0100 Subject: [PATCH 049/119] gpiolib: make value setters have return values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the in-kernel consumer interface for GPIOs: make all variants of value setters that don't have a return value, return a signed integer instead. That will allow these routines to indicate failures to callers. This doesn't change the implementation just yet, we'll do it in subsequent commits. We need to update the gpio-latch module as it passes the address of value setters as a function pointer argument and thus cares about its type. Reviewed-by: Linus Walleij Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250220-gpio-set-retval-v2-2-bc4cfd38dae3@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-latch.c | 2 +- drivers/gpio/gpiolib.c | 53 ++++++++++++++++++++--------------- include/linux/gpio.h | 4 +-- include/linux/gpio/consumer.h | 22 +++++++++------ 4 files changed, 46 insertions(+), 35 deletions(-) diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c index 46cdfb08747a..64174ea7d008 100644 --- a/drivers/gpio/gpio-latch.c +++ b/drivers/gpio/gpio-latch.c @@ -73,7 +73,7 @@ static int gpio_latch_get_direction(struct gpio_chip *gc, unsigned int offset) } static void gpio_latch_set_unlocked(struct gpio_latch_priv *priv, - void (*set)(struct gpio_desc *desc, int value), + int (*set)(struct gpio_desc *desc, int value), unsigned int offset, bool val) { int latch = offset / priv->n_latched_gpios; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e8678a6c82ea..f659437710c1 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3497,13 +3497,13 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value); * @desc: gpio descriptor whose state need to be set. * @value: Non-zero for setting it HIGH otherwise it will set to LOW. */ -static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) +static int gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) { int ret = 0, offset = gpio_chip_hwgpio(desc); CLASS(gpio_chip_guard, guard)(desc); if (!guard.gc) - return; + return -ENODEV; if (value) { ret = gpiochip_direction_input(guard.gc, offset); @@ -3517,6 +3517,8 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) gpiod_err(desc, "%s: Error in set_value for open drain err %d\n", __func__, ret); + + return ret; } /* @@ -3524,13 +3526,13 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) * @desc: gpio descriptor whose state need to be set. * @value: Non-zero for setting it HIGH otherwise it will set to LOW. */ -static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value) +static int gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value) { int ret = 0, offset = gpio_chip_hwgpio(desc); CLASS(gpio_chip_guard, guard)(desc); if (!guard.gc) - return; + return -ENODEV; if (value) { ret = gpiochip_direction_output(guard.gc, offset, 1); @@ -3544,16 +3546,20 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value gpiod_err(desc, "%s: Error in set_value for open source err %d\n", __func__, ret); + + return ret; } -static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) +static int gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) { CLASS(gpio_chip_guard, guard)(desc); if (!guard.gc) - return; + return -ENODEV; trace_gpio_value(desc_to_gpio(desc), 0, value); guard.gc->set(guard.gc, gpio_chip_hwgpio(desc), value); + + return 0; } /* @@ -3711,12 +3717,12 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, * This function can be called from contexts where we cannot sleep, and will * complain if the GPIO chip functions potentially sleep. */ -void gpiod_set_raw_value(struct gpio_desc *desc, int value) +int gpiod_set_raw_value(struct gpio_desc *desc, int value) { - VALIDATE_DESC_VOID(desc); + VALIDATE_DESC(desc); /* Should be using gpiod_set_raw_value_cansleep() */ WARN_ON(desc->gdev->can_sleep); - gpiod_set_raw_value_commit(desc, value); + return gpiod_set_raw_value_commit(desc, value); } EXPORT_SYMBOL_GPL(gpiod_set_raw_value); @@ -3729,16 +3735,17 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value); * different semantic quirks like active low and open drain/source * handling. */ -static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) +static int gpiod_set_value_nocheck(struct gpio_desc *desc, int value) { if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; + if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) - gpio_set_open_drain_value_commit(desc, value); + return gpio_set_open_drain_value_commit(desc, value); else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) - gpio_set_open_source_value_commit(desc, value); - else - gpiod_set_raw_value_commit(desc, value); + return gpio_set_open_source_value_commit(desc, value); + + return gpiod_set_raw_value_commit(desc, value); } /** @@ -3752,12 +3759,12 @@ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) * This function can be called from contexts where we cannot sleep, and will * complain if the GPIO chip functions potentially sleep. */ -void gpiod_set_value(struct gpio_desc *desc, int value) +int gpiod_set_value(struct gpio_desc *desc, int value) { - VALIDATE_DESC_VOID(desc); + VALIDATE_DESC(desc); /* Should be using gpiod_set_value_cansleep() */ WARN_ON(desc->gdev->can_sleep); - gpiod_set_value_nocheck(desc, value); + return gpiod_set_value_nocheck(desc, value); } EXPORT_SYMBOL_GPL(gpiod_set_value); @@ -4176,11 +4183,11 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); * * This function is to be called from contexts that can sleep. */ -void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) +int gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) { might_sleep(); - VALIDATE_DESC_VOID(desc); - gpiod_set_raw_value_commit(desc, value); + VALIDATE_DESC(desc); + return gpiod_set_raw_value_commit(desc, value); } EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); @@ -4194,11 +4201,11 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); * * This function is to be called from contexts that can sleep. */ -void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) +int gpiod_set_value_cansleep(struct gpio_desc *desc, int value) { might_sleep(); - VALIDATE_DESC_VOID(desc); - gpiod_set_value_nocheck(desc, value); + VALIDATE_DESC(desc); + return gpiod_set_value_nocheck(desc, value); } EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep); diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 6270150f4e29..c1ec62c11ed3 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -91,7 +91,7 @@ static inline int gpio_get_value_cansleep(unsigned gpio) } static inline void gpio_set_value_cansleep(unsigned gpio, int value) { - return gpiod_set_raw_value_cansleep(gpio_to_desc(gpio), value); + gpiod_set_raw_value_cansleep(gpio_to_desc(gpio), value); } static inline int gpio_get_value(unsigned gpio) @@ -100,7 +100,7 @@ static inline int gpio_get_value(unsigned gpio) } static inline void gpio_set_value(unsigned gpio, int value) { - return gpiod_set_raw_value(gpio_to_desc(gpio), value); + gpiod_set_raw_value(gpio_to_desc(gpio), value); } static inline int gpio_to_irq(unsigned gpio) diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 824a1717e6d2..45b651c05b9c 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -118,7 +118,7 @@ int gpiod_get_array_value(unsigned int array_size, struct gpio_desc **desc_array, struct gpio_array *array_info, unsigned long *value_bitmap); -void gpiod_set_value(struct gpio_desc *desc, int value); +int gpiod_set_value(struct gpio_desc *desc, int value); int gpiod_set_array_value(unsigned int array_size, struct gpio_desc **desc_array, struct gpio_array *array_info, @@ -128,7 +128,7 @@ int gpiod_get_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, struct gpio_array *array_info, unsigned long *value_bitmap); -void gpiod_set_raw_value(struct gpio_desc *desc, int value); +int gpiod_set_raw_value(struct gpio_desc *desc, int value); int gpiod_set_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, struct gpio_array *array_info, @@ -140,7 +140,7 @@ int gpiod_get_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, struct gpio_array *array_info, unsigned long *value_bitmap); -void gpiod_set_value_cansleep(struct gpio_desc *desc, int value); +int gpiod_set_value_cansleep(struct gpio_desc *desc, int value); int gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, struct gpio_array *array_info, @@ -150,7 +150,7 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, struct gpio_array *array_info, unsigned long *value_bitmap); -void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value); +int gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value); int gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, struct gpio_array *array_info, @@ -360,10 +360,11 @@ static inline int gpiod_get_array_value(unsigned int array_size, WARN_ON(desc_array); return 0; } -static inline void gpiod_set_value(struct gpio_desc *desc, int value) +static inline int gpiod_set_value(struct gpio_desc *desc, int value) { /* GPIO can never have been requested */ WARN_ON(desc); + return 0; } static inline int gpiod_set_array_value(unsigned int array_size, struct gpio_desc **desc_array, @@ -389,10 +390,11 @@ static inline int gpiod_get_raw_array_value(unsigned int array_size, WARN_ON(desc_array); return 0; } -static inline void gpiod_set_raw_value(struct gpio_desc *desc, int value) +static inline int gpiod_set_raw_value(struct gpio_desc *desc, int value) { /* GPIO can never have been requested */ WARN_ON(desc); + return 0; } static inline int gpiod_set_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, @@ -419,10 +421,11 @@ static inline int gpiod_get_array_value_cansleep(unsigned int array_size, WARN_ON(desc_array); return 0; } -static inline void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) +static inline int gpiod_set_value_cansleep(struct gpio_desc *desc, int value) { /* GPIO can never have been requested */ WARN_ON(desc); + return 0; } static inline int gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, @@ -448,11 +451,12 @@ static inline int gpiod_get_raw_array_value_cansleep(unsigned int array_size, WARN_ON(desc_array); return 0; } -static inline void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, - int value) +static inline int gpiod_set_raw_value_cansleep(struct gpio_desc *desc, + int value) { /* GPIO can never have been requested */ WARN_ON(desc); + return 0; } static inline int gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, From d36058b89a4aa30865d4cfeb101bbfd1d1dcb22f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 20 Feb 2025 10:57:00 +0100 Subject: [PATCH 050/119] gpiolib: wrap gpio_chip::set() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have three places where we dereference the gpio_chip::set() callback. In order to make it easier to incorporate the upcoming new variant of this callback (one returning an integer value), wrap it in a helper so that the dereferencing only happens once. Reviewed-by: Linus Walleij Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250220-gpio-set-retval-v2-3-bc4cfd38dae3@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f659437710c1..a83494dd3e12 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2825,6 +2825,17 @@ int gpiod_direction_input_nonotify(struct gpio_desc *desc) return ret; } +static int gpiochip_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + lockdep_assert_held(&gc->gpiodev->srcu); + + if (WARN_ON(unlikely(!gc->set))) + return -EOPNOTSUPP; + + gc->set(gc, offset, value); + return 0; +} + static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) { int val = !!value, ret = 0; @@ -2867,7 +2878,9 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) * If we can't actively set the direction, we are some * output-only chip, so just drive the output as desired. */ - guard.gc->set(guard.gc, gpio_chip_hwgpio(desc), val); + ret = gpiochip_set(guard.gc, gpio_chip_hwgpio(desc), val); + if (ret) + return ret; } if (!ret) @@ -3557,9 +3570,7 @@ static int gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) return -ENODEV; trace_gpio_value(desc_to_gpio(desc), 0, value); - guard.gc->set(guard.gc, gpio_chip_hwgpio(desc), value); - - return 0; + return gpiochip_set(guard.gc, gpio_chip_hwgpio(desc), value); } /* @@ -3584,7 +3595,7 @@ static void gpio_chip_set_multiple(struct gpio_chip *gc, /* set outputs if the corresponding mask bit is set */ for_each_set_bit(i, mask, gc->ngpio) - gc->set(gc, i, test_bit(i, bits)); + gpiochip_set(gc, i, test_bit(i, bits)); } } From 9b407312755fd5db012413ca005f0f3a661db8dd Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 20 Feb 2025 10:57:01 +0100 Subject: [PATCH 051/119] gpiolib: rework the wrapper around gpio_chip::set_multiple() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the existing wrapper around gpio_chip::set_multiple() consistent with the one for gpio_chip::set(): make it return int, add a lockdep assertion, warn on missing set callback and move the code a bit for better readability. Add return value checks in all call places. Reviewed-by: Linus Walleij Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250220-gpio-set-retval-v2-4-bc4cfd38dae3@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index a83494dd3e12..d26cad6442bf 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3582,21 +3582,33 @@ static int gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) * defines which outputs are to be changed * @bits: bit value array; one bit per output; BITS_PER_LONG bits per word * defines the values the outputs specified by mask are to be set to + * + * Returns: 0 on success, negative error number on failure. */ -static void gpio_chip_set_multiple(struct gpio_chip *gc, - unsigned long *mask, unsigned long *bits) +static int gpiochip_set_multiple(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits) { + unsigned int i; + int ret; + lockdep_assert_held(&gc->gpiodev->srcu); + if (WARN_ON(unlikely(!gc->set_multiple && !gc->set))) + return -EOPNOTSUPP; + if (gc->set_multiple) { gc->set_multiple(gc, mask, bits); - } else { - unsigned int i; - - /* set outputs if the corresponding mask bit is set */ - for_each_set_bit(i, mask, gc->ngpio) - gpiochip_set(gc, i, test_bit(i, bits)); + return 0; } + + /* set outputs if the corresponding mask bit is set */ + for_each_set_bit(i, mask, gc->ngpio) { + ret = gpiochip_set(gc, i, test_bit(i, bits)); + if (ret) + break; + } + + return ret; } int gpiod_set_array_value_complex(bool raw, bool can_sleep, @@ -3606,7 +3618,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, unsigned long *value_bitmap) { struct gpio_chip *gc; - int i = 0; + int i = 0, ret; /* * Validate array_info against desc_array and its size. @@ -3629,7 +3641,10 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, bitmap_xor(value_bitmap, value_bitmap, array_info->invert_mask, array_size); - gpio_chip_set_multiple(gc, array_info->set_mask, value_bitmap); + ret = gpiochip_set_multiple(gc, array_info->set_mask, + value_bitmap); + if (ret) + return ret; i = find_first_zero_bit(array_info->set_mask, array_size); if (i == array_size) @@ -3706,8 +3721,11 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, } while ((i < array_size) && gpio_device_chip_cmp(desc_array[i]->gdev, guard.gc)); /* push collected bits to outputs */ - if (count != 0) - gpio_chip_set_multiple(guard.gc, mask, bits); + if (count != 0) { + ret = gpiochip_set_multiple(guard.gc, mask, bits); + if (ret) + return ret; + } if (mask != fastpath_mask) bitmap_free(mask); From 98ce1eb1fd87ea1b016e0913ef6836ab0139b5c4 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 20 Feb 2025 10:57:02 +0100 Subject: [PATCH 052/119] gpiolib: introduce gpio_chip setters that return values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new variants of the set() and set_multiple() callbacks that have integer return values allowing to indicate failures to users of the GPIO consumer API. Until we convert all GPIO providers treewide to using them, they will live in parallel to the existing ones. Make sure that providers cannot define both. Prefer the new ones and only use the old ones as fallback. Reviewed-by: Linus Walleij Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250220-gpio-set-retval-v2-5-bc4cfd38dae3@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 27 +++++++++++++++++++++++++-- include/linux/gpio/driver.h | 10 ++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index d26cad6442bf..1b4af0f97e5a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -958,6 +958,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, int base = 0; int ret = 0; + /* Only allow one set() and one set_multiple(). */ + if ((gc->set && gc->set_rv) || + (gc->set_multiple && gc->set_multiple_rv)) + return -EINVAL; + /* * First: allocate and populate the internal stat container, and * set up the struct device. @@ -2827,11 +2832,21 @@ int gpiod_direction_input_nonotify(struct gpio_desc *desc) static int gpiochip_set(struct gpio_chip *gc, unsigned int offset, int value) { + int ret; + lockdep_assert_held(&gc->gpiodev->srcu); - if (WARN_ON(unlikely(!gc->set))) + if (WARN_ON(unlikely(!gc->set && !gc->set_rv))) return -EOPNOTSUPP; + if (gc->set_rv) { + ret = gc->set_rv(gc, offset, value); + if (ret > 0) + ret = -EBADE; + + return ret; + } + gc->set(gc, offset, value); return 0; } @@ -3593,9 +3608,17 @@ static int gpiochip_set_multiple(struct gpio_chip *gc, lockdep_assert_held(&gc->gpiodev->srcu); - if (WARN_ON(unlikely(!gc->set_multiple && !gc->set))) + if (WARN_ON(unlikely(!gc->set_multiple && !gc->set_multiple_rv))) return -EOPNOTSUPP; + if (gc->set_multiple_rv) { + ret = gc->set_multiple_rv(gc, mask, bits); + if (ret > 0) + ret = -EBADE; + + return ret; + } + if (gc->set_multiple) { gc->set_multiple(gc, mask, bits); return 0; diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index ae96a2f260fb..a2a1b6434321 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -348,6 +348,10 @@ struct gpio_irq_chip { * stores them in "bits", returns 0 on success or negative error * @set: assigns output value for signal "offset" * @set_multiple: assigns output values for multiple signals defined by "mask" + * @set_rv: assigns output value for signal "offset", returns 0 on success or + * negative error value + * @set_multiple_rv: assigns output values for multiple signals defined by + * "mask", returns 0 on success or negative error value * @set_config: optional hook for all kinds of settings. Uses the same * packed config format as generic pinconf. Must return 0 on success and * a negative error number on failure. @@ -445,6 +449,12 @@ struct gpio_chip { void (*set_multiple)(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits); + int (*set_rv)(struct gpio_chip *gc, + unsigned int offset, + int value); + int (*set_multiple_rv)(struct gpio_chip *gc, + unsigned long *mask, + unsigned long *bits); int (*set_config)(struct gpio_chip *gc, unsigned int offset, unsigned long config); From fe69bedc77c119ffd4e27778eec03c89acb8e87b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 20 Feb 2025 10:57:03 +0100 Subject: [PATCH 053/119] gpio: sim: use value returning setters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct gpio_chip now has additional variants of the set(_multiple) driver callbacks that return an integer to indicate success or failure. Convert the driver to using them. Reviewed-by: Linus Walleij Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250220-gpio-set-retval-v2-6-bc4cfd38dae3@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sim.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index 758933fbd85b..f638219a7c4f 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -120,12 +120,14 @@ static int gpio_sim_get(struct gpio_chip *gc, unsigned int offset) return !!test_bit(offset, chip->value_map); } -static void gpio_sim_set(struct gpio_chip *gc, unsigned int offset, int value) +static int gpio_sim_set(struct gpio_chip *gc, unsigned int offset, int value) { struct gpio_sim_chip *chip = gpiochip_get_data(gc); scoped_guard(mutex, &chip->lock) __assign_bit(offset, chip->value_map, value); + + return 0; } static int gpio_sim_get_multiple(struct gpio_chip *gc, @@ -139,14 +141,16 @@ static int gpio_sim_get_multiple(struct gpio_chip *gc, return 0; } -static void gpio_sim_set_multiple(struct gpio_chip *gc, - unsigned long *mask, unsigned long *bits) +static int gpio_sim_set_multiple(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits) { struct gpio_sim_chip *chip = gpiochip_get_data(gc); scoped_guard(mutex, &chip->lock) bitmap_replace(chip->value_map, chip->value_map, bits, mask, gc->ngpio); + + return 0; } static int gpio_sim_direction_output(struct gpio_chip *gc, @@ -482,9 +486,9 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) gc->parent = dev; gc->fwnode = swnode; gc->get = gpio_sim_get; - gc->set = gpio_sim_set; + gc->set_rv = gpio_sim_set; gc->get_multiple = gpio_sim_get_multiple; - gc->set_multiple = gpio_sim_set_multiple; + gc->set_multiple_rv = gpio_sim_set_multiple; gc->direction_output = gpio_sim_direction_output; gc->direction_input = gpio_sim_direction_input; gc->get_direction = gpio_sim_get_direction; From a458d2309c81902dc6ca19b5037b9d25eb60a4a8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 20 Feb 2025 10:57:04 +0100 Subject: [PATCH 054/119] gpio: regmap: use value returning setters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct gpio_chip now has additional variants of the set(_multiple) driver callbacks that return an integer to indicate success or failure. Convert the driver to using them. Reviewed-by: Linus Walleij Reviewed-by: Michael Walle Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250220-gpio-set-retval-v2-7-bc4cfd38dae3@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-regmap.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index 856f8569566e..87c4225784cf 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -83,33 +83,43 @@ static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset) return !!(val & mask); } -static void gpio_regmap_set(struct gpio_chip *chip, unsigned int offset, - int val) +static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset, + int val) { struct gpio_regmap *gpio = gpiochip_get_data(chip); unsigned int base = gpio_regmap_addr(gpio->reg_set_base); unsigned int reg, mask; + int ret; + + ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); + if (ret) + return ret; - gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); if (val) - regmap_update_bits(gpio->regmap, reg, mask, mask); + ret = regmap_update_bits(gpio->regmap, reg, mask, mask); else - regmap_update_bits(gpio->regmap, reg, mask, 0); + ret = regmap_update_bits(gpio->regmap, reg, mask, 0); + + return ret; } -static void gpio_regmap_set_with_clear(struct gpio_chip *chip, - unsigned int offset, int val) +static int gpio_regmap_set_with_clear(struct gpio_chip *chip, + unsigned int offset, int val) { struct gpio_regmap *gpio = gpiochip_get_data(chip); unsigned int base, reg, mask; + int ret; if (val) base = gpio_regmap_addr(gpio->reg_set_base); else base = gpio_regmap_addr(gpio->reg_clr_base); - gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); - regmap_write(gpio->regmap, reg, mask); + ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); + if (ret) + return ret; + + return regmap_write(gpio->regmap, reg, mask); } static int gpio_regmap_get_direction(struct gpio_chip *chip, @@ -250,9 +260,9 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config chip->free = gpiochip_generic_free; chip->get = gpio_regmap_get; if (gpio->reg_set_base && gpio->reg_clr_base) - chip->set = gpio_regmap_set_with_clear; + chip->set_rv = gpio_regmap_set_with_clear; else if (gpio->reg_set_base) - chip->set = gpio_regmap_set; + chip->set_rv = gpio_regmap_set; chip->get_direction = gpio_regmap_get_direction; if (gpio->reg_dir_in_base || gpio->reg_dir_out_base) { From e32ce8f62dd9c0ec923cfb9c783fc04070edb24e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 20 Feb 2025 10:57:05 +0100 Subject: [PATCH 055/119] gpio: pca953x: use value returning setters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct gpio_chip now has additional variants of the set(_multiple) driver callbacks that return an integer to indicate success or failure. Convert the driver to using them. Reviewed-by: Linus Walleij Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250220-gpio-set-retval-v2-8-bc4cfd38dae3@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca953x.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index d63c1030e6ac..442435ded020 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -570,7 +570,8 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) return !!(reg_val & bit); } -static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) +static int pca953x_gpio_set_value(struct gpio_chip *gc, unsigned int off, + int val) { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 outreg = chip->recalc_addr(chip, chip->regs->output, off); @@ -578,7 +579,7 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) guard(mutex)(&chip->i2c_lock); - regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0); + return regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0); } static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off) @@ -616,8 +617,8 @@ static int pca953x_gpio_get_multiple(struct gpio_chip *gc, return 0; } -static void pca953x_gpio_set_multiple(struct gpio_chip *gc, - unsigned long *mask, unsigned long *bits) +static int pca953x_gpio_set_multiple(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits) { struct pca953x_chip *chip = gpiochip_get_data(gc); DECLARE_BITMAP(reg_val, MAX_LINE); @@ -627,11 +628,11 @@ static void pca953x_gpio_set_multiple(struct gpio_chip *gc, ret = pca953x_read_regs(chip, chip->regs->output, reg_val); if (ret) - return; + return ret; bitmap_replace(reg_val, reg_val, bits, mask, gc->ngpio); - pca953x_write_regs(chip, chip->regs->output, reg_val); + return pca953x_write_regs(chip, chip->regs->output, reg_val); } static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip, @@ -693,10 +694,10 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) gc->direction_input = pca953x_gpio_direction_input; gc->direction_output = pca953x_gpio_direction_output; gc->get = pca953x_gpio_get_value; - gc->set = pca953x_gpio_set_value; + gc->set_rv = pca953x_gpio_set_value; gc->get_direction = pca953x_gpio_get_direction; gc->get_multiple = pca953x_gpio_get_multiple; - gc->set_multiple = pca953x_gpio_set_multiple; + gc->set_multiple_rv = pca953x_gpio_set_multiple; gc->set_config = pca953x_gpio_set_config; gc->can_sleep = true; From 66d231b12eb8d39c21835a9bf553299a278ae363 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 20 Feb 2025 10:57:06 +0100 Subject: [PATCH 056/119] gpio: mockup: use value returning setters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct gpio_chip now has additional variants of the set(_multiple) driver callbacks that return an integer to indicate success or failure. Convert the driver to using them. Reviewed-by: Linus Walleij Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250220-gpio-set-retval-v2-9-bc4cfd38dae3@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mockup.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index d39c6618bade..266c0953d914 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -122,7 +122,7 @@ static void __gpio_mockup_set(struct gpio_mockup_chip *chip, chip->lines[offset].value = !!value; } -static void gpio_mockup_set(struct gpio_chip *gc, +static int gpio_mockup_set(struct gpio_chip *gc, unsigned int offset, int value) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); @@ -130,10 +130,12 @@ static void gpio_mockup_set(struct gpio_chip *gc, guard(mutex)(&chip->lock); __gpio_mockup_set(chip, offset, value); + + return 0; } -static void gpio_mockup_set_multiple(struct gpio_chip *gc, - unsigned long *mask, unsigned long *bits) +static int gpio_mockup_set_multiple(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); unsigned int bit; @@ -142,6 +144,8 @@ static void gpio_mockup_set_multiple(struct gpio_chip *gc, for_each_set_bit(bit, mask, gc->ngpio) __gpio_mockup_set(chip, bit, test_bit(bit, bits)); + + return 0; } static int gpio_mockup_apply_pull(struct gpio_mockup_chip *chip, @@ -445,9 +449,9 @@ static int gpio_mockup_probe(struct platform_device *pdev) gc->owner = THIS_MODULE; gc->parent = dev; gc->get = gpio_mockup_get; - gc->set = gpio_mockup_set; + gc->set_rv = gpio_mockup_set; gc->get_multiple = gpio_mockup_get_multiple; - gc->set_multiple = gpio_mockup_set_multiple; + gc->set_multiple_rv = gpio_mockup_set_multiple; gc->direction_output = gpio_mockup_dirout; gc->direction_input = gpio_mockup_dirin; gc->get_direction = gpio_mockup_get_direction; From 468eae4166ab796cd2f9ad2bbb141d914e19c0b1 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 20 Feb 2025 10:57:07 +0100 Subject: [PATCH 057/119] gpio: aggregator: use value returning setters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct gpio_chip now has additional variants of the set(_multiple) driver callbacks that return an integer to indicate success or failure. Convert the driver to using them. Reviewed-by: Linus Walleij Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250220-gpio-set-retval-v2-10-bc4cfd38dae3@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-aggregator.c | 38 +++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 65f41cc3eafc..e799599a06a1 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -358,25 +358,30 @@ static void gpio_fwd_delay(struct gpio_chip *chip, unsigned int offset, int valu udelay(delay_us); } -static void gpio_fwd_set(struct gpio_chip *chip, unsigned int offset, int value) +static int gpio_fwd_set(struct gpio_chip *chip, unsigned int offset, int value) { struct gpiochip_fwd *fwd = gpiochip_get_data(chip); + int ret; if (chip->can_sleep) - gpiod_set_value_cansleep(fwd->descs[offset], value); + ret = gpiod_set_value_cansleep(fwd->descs[offset], value); else - gpiod_set_value(fwd->descs[offset], value); + ret = gpiod_set_value(fwd->descs[offset], value); + if (ret) + return ret; if (fwd->delay_timings) gpio_fwd_delay(chip, offset, value); + + return ret; } -static void gpio_fwd_set_multiple(struct gpiochip_fwd *fwd, unsigned long *mask, - unsigned long *bits) +static int gpio_fwd_set_multiple(struct gpiochip_fwd *fwd, unsigned long *mask, + unsigned long *bits) { struct gpio_desc **descs = fwd_tmp_descs(fwd); unsigned long *values = fwd_tmp_values(fwd); - unsigned int i, j = 0; + unsigned int i, j = 0, ret; for_each_set_bit(i, mask, fwd->chip.ngpio) { __assign_bit(j, values, test_bit(i, bits)); @@ -384,26 +389,31 @@ static void gpio_fwd_set_multiple(struct gpiochip_fwd *fwd, unsigned long *mask, } if (fwd->chip.can_sleep) - gpiod_set_array_value_cansleep(j, descs, NULL, values); + ret = gpiod_set_array_value_cansleep(j, descs, NULL, values); else - gpiod_set_array_value(j, descs, NULL, values); + ret = gpiod_set_array_value(j, descs, NULL, values); + + return ret; } -static void gpio_fwd_set_multiple_locked(struct gpio_chip *chip, - unsigned long *mask, unsigned long *bits) +static int gpio_fwd_set_multiple_locked(struct gpio_chip *chip, + unsigned long *mask, unsigned long *bits) { struct gpiochip_fwd *fwd = gpiochip_get_data(chip); unsigned long flags; + int ret; if (chip->can_sleep) { mutex_lock(&fwd->mlock); - gpio_fwd_set_multiple(fwd, mask, bits); + ret = gpio_fwd_set_multiple(fwd, mask, bits); mutex_unlock(&fwd->mlock); } else { spin_lock_irqsave(&fwd->slock, flags); - gpio_fwd_set_multiple(fwd, mask, bits); + ret = gpio_fwd_set_multiple(fwd, mask, bits); spin_unlock_irqrestore(&fwd->slock, flags); } + + return ret; } static int gpio_fwd_set_config(struct gpio_chip *chip, unsigned int offset, @@ -533,8 +543,8 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev, chip->direction_output = gpio_fwd_direction_output; chip->get = gpio_fwd_get; chip->get_multiple = gpio_fwd_get_multiple_locked; - chip->set = gpio_fwd_set; - chip->set_multiple = gpio_fwd_set_multiple_locked; + chip->set_rv = gpio_fwd_set; + chip->set_multiple_rv = gpio_fwd_set_multiple_locked; chip->to_irq = gpio_fwd_to_irq; chip->base = -1; chip->ngpio = ngpios; From 97c9b59f6658671f3f13f57de1352ec9d16ad13d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 20 Feb 2025 10:57:08 +0100 Subject: [PATCH 058/119] gpio: max77650: use value returning setters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct gpio_chip now has additional variants of the set(_multiple) driver callbacks that return an integer to indicate success or failure. Convert the driver to using them. Reviewed-by: Linus Walleij Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250220-gpio-set-retval-v2-11-bc4cfd38dae3@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-max77650.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-max77650.c b/drivers/gpio/gpio-max77650.c index 3075f2513c6f..a553e141059f 100644 --- a/drivers/gpio/gpio-max77650.c +++ b/drivers/gpio/gpio-max77650.c @@ -62,18 +62,16 @@ static int max77650_gpio_direction_output(struct gpio_chip *gc, MAX77650_REG_CNFG_GPIO, mask, regval); } -static void max77650_gpio_set_value(struct gpio_chip *gc, - unsigned int offset, int value) +static int max77650_gpio_set_value(struct gpio_chip *gc, + unsigned int offset, int value) { struct max77650_gpio_chip *chip = gpiochip_get_data(gc); - int rv, regval; + int regval; regval = value ? MAX77650_GPIO_OUT_HIGH : MAX77650_GPIO_OUT_LOW; - rv = regmap_update_bits(chip->map, MAX77650_REG_CNFG_GPIO, - MAX77650_GPIO_OUTVAL_MASK, regval); - if (rv) - dev_err(gc->parent, "cannot set GPIO value: %d\n", rv); + return regmap_update_bits(chip->map, MAX77650_REG_CNFG_GPIO, + MAX77650_GPIO_OUTVAL_MASK, regval); } static int max77650_gpio_get_value(struct gpio_chip *gc, @@ -168,7 +166,7 @@ static int max77650_gpio_probe(struct platform_device *pdev) chip->gc.direction_input = max77650_gpio_direction_input; chip->gc.direction_output = max77650_gpio_direction_output; - chip->gc.set = max77650_gpio_set_value; + chip->gc.set_rv = max77650_gpio_set_value; chip->gc.get = max77650_gpio_get_value; chip->gc.get_direction = max77650_gpio_get_direction; chip->gc.set_config = max77650_gpio_set_config; From 14628b692707fa8e61d0a068ef012156d23dc776 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 20 Feb 2025 10:57:09 +0100 Subject: [PATCH 059/119] gpio: latch: use lock guards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use lock guards from linux/cleanup.h. This will make the subsequent commit that switches to using value returning GPIO line setters much simpler as we'll be able to return values without caring about releasing the locks. Reviewed-by: Linus Walleij Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250220-gpio-set-retval-v2-12-bc4cfd38dae3@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-latch.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c index 64174ea7d008..f227966c50d5 100644 --- a/drivers/gpio/gpio-latch.c +++ b/drivers/gpio/gpio-latch.c @@ -38,6 +38,7 @@ * in the corresponding device tree properties. */ +#include #include #include #include @@ -94,24 +95,19 @@ static void gpio_latch_set_unlocked(struct gpio_latch_priv *priv, static void gpio_latch_set(struct gpio_chip *gc, unsigned int offset, int val) { struct gpio_latch_priv *priv = gpiochip_get_data(gc); - unsigned long flags; - spin_lock_irqsave(&priv->spinlock, flags); + guard(spinlock_irqsave)(&priv->spinlock); gpio_latch_set_unlocked(priv, gpiod_set_value, offset, val); - - spin_unlock_irqrestore(&priv->spinlock, flags); } static void gpio_latch_set_can_sleep(struct gpio_chip *gc, unsigned int offset, int val) { struct gpio_latch_priv *priv = gpiochip_get_data(gc); - mutex_lock(&priv->mutex); + guard(mutex)(&priv->mutex); gpio_latch_set_unlocked(priv, gpiod_set_value_cansleep, offset, val); - - mutex_unlock(&priv->mutex); } static bool gpio_latch_can_sleep(struct gpio_latch_priv *priv, unsigned int n_latches) From 4b28762caa7b85609ee1a9a5e1038ae7bbd24892 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 20 Feb 2025 10:57:10 +0100 Subject: [PATCH 060/119] gpio: latch: use value returning setters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct gpio_chip now has additional variants of the set(_multiple) driver callbacks that return an integer to indicate success or failure. Convert the driver to using them. Reviewed-by: Linus Walleij Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250220-gpio-set-retval-v2-13-bc4cfd38dae3@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-latch.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c index f227966c50d5..3d0ff09284fb 100644 --- a/drivers/gpio/gpio-latch.c +++ b/drivers/gpio/gpio-latch.c @@ -73,41 +73,46 @@ static int gpio_latch_get_direction(struct gpio_chip *gc, unsigned int offset) return GPIO_LINE_DIRECTION_OUT; } -static void gpio_latch_set_unlocked(struct gpio_latch_priv *priv, - int (*set)(struct gpio_desc *desc, int value), - unsigned int offset, bool val) +static int gpio_latch_set_unlocked(struct gpio_latch_priv *priv, + int (*set)(struct gpio_desc *desc, int value), + unsigned int offset, bool val) { - int latch = offset / priv->n_latched_gpios; - int i; + int latch = offset / priv->n_latched_gpios, i, ret; assign_bit(offset, priv->shadow, val); - for (i = 0; i < priv->n_latched_gpios; i++) - set(priv->latched_gpios->desc[i], - test_bit(latch * priv->n_latched_gpios + i, priv->shadow)); + for (i = 0; i < priv->n_latched_gpios; i++) { + ret = set(priv->latched_gpios->desc[i], + test_bit(latch * priv->n_latched_gpios + i, + priv->shadow)); + if (ret) + return ret; + } ndelay(priv->setup_duration_ns); set(priv->clk_gpios->desc[latch], 1); ndelay(priv->clock_duration_ns); set(priv->clk_gpios->desc[latch], 0); + + return 0; } -static void gpio_latch_set(struct gpio_chip *gc, unsigned int offset, int val) +static int gpio_latch_set(struct gpio_chip *gc, unsigned int offset, int val) { struct gpio_latch_priv *priv = gpiochip_get_data(gc); guard(spinlock_irqsave)(&priv->spinlock); - gpio_latch_set_unlocked(priv, gpiod_set_value, offset, val); + return gpio_latch_set_unlocked(priv, gpiod_set_value, offset, val); } -static void gpio_latch_set_can_sleep(struct gpio_chip *gc, unsigned int offset, int val) +static int gpio_latch_set_can_sleep(struct gpio_chip *gc, unsigned int offset, int val) { struct gpio_latch_priv *priv = gpiochip_get_data(gc); guard(mutex)(&priv->mutex); - gpio_latch_set_unlocked(priv, gpiod_set_value_cansleep, offset, val); + return gpio_latch_set_unlocked(priv, gpiod_set_value_cansleep, offset, val); } static bool gpio_latch_can_sleep(struct gpio_latch_priv *priv, unsigned int n_latches) @@ -161,11 +166,11 @@ static int gpio_latch_probe(struct platform_device *pdev) if (gpio_latch_can_sleep(priv, n_latches)) { priv->gc.can_sleep = true; - priv->gc.set = gpio_latch_set_can_sleep; + priv->gc.set_rv = gpio_latch_set_can_sleep; mutex_init(&priv->mutex); } else { priv->gc.can_sleep = false; - priv->gc.set = gpio_latch_set; + priv->gc.set_rv = gpio_latch_set; spin_lock_init(&priv->spinlock); } From f01436c2a038fe8d7b69a5fe701ab98028ce5cc4 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 20 Feb 2025 10:57:11 +0100 Subject: [PATCH 061/119] gpio: davinci: use value returning setters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct gpio_chip now has additional variants of the set(_multiple) driver callbacks that return an integer to indicate success or failure. Convert the driver to using them. Reviewed-by: Linus Walleij Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250220-gpio-set-retval-v2-14-bc4cfd38dae3@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-davinci.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index 8c033e8cf3c9..63fc7888c1d4 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -139,7 +139,7 @@ static int davinci_gpio_get(struct gpio_chip *chip, unsigned offset) /* * Assuming the pin is muxed as a gpio output, set its output value. */ -static void +static int davinci_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct davinci_gpio_controller *d = gpiochip_get_data(chip); @@ -150,6 +150,8 @@ davinci_gpio_set(struct gpio_chip *chip, unsigned offset, int value) writel_relaxed(__gpio_mask(offset), value ? &g->set_data : &g->clr_data); + + return 0; } static int davinci_gpio_probe(struct platform_device *pdev) @@ -209,7 +211,7 @@ static int davinci_gpio_probe(struct platform_device *pdev) chips->chip.direction_input = davinci_direction_in; chips->chip.get = davinci_gpio_get; chips->chip.direction_output = davinci_direction_out; - chips->chip.set = davinci_gpio_set; + chips->chip.set_rv = davinci_gpio_set; chips->chip.ngpio = ngpio; chips->chip.base = -1; From 9080b5d1b9c259645cd0e3694ffba85ccdd25352 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 20 Feb 2025 10:57:12 +0100 Subject: [PATCH 062/119] gpio: mvebu: use value returning setters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct gpio_chip now has additional variants of the set(_multiple) driver callbacks that return an integer to indicate success or failure. Convert the driver to using them. Reviewed-by: Linus Walleij Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250220-gpio-set-retval-v2-15-bc4cfd38dae3@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mvebu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 363bad286c32..3604abcb6fec 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -298,12 +298,12 @@ static unsigned int mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpwm) /* * Functions implementing the gpio_chip methods */ -static void mvebu_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) +static int mvebu_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); - regmap_update_bits(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, - BIT(pin), value ? BIT(pin) : 0); + return regmap_update_bits(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, + BIT(pin), value ? BIT(pin) : 0); } static int mvebu_gpio_get(struct gpio_chip *chip, unsigned int pin) @@ -1173,7 +1173,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev) mvchip->chip.direction_input = mvebu_gpio_direction_input; mvchip->chip.get = mvebu_gpio_get; mvchip->chip.direction_output = mvebu_gpio_direction_output; - mvchip->chip.set = mvebu_gpio_set; + mvchip->chip.set_rv = mvebu_gpio_set; if (have_irqs) mvchip->chip.to_irq = mvebu_gpio_to_irq; mvchip->chip.base = id * MVEBU_MAX_GPIO_PER_BANK; From b28037d4f375ed36ce8abbbd31107b991792db72 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Mon, 24 Feb 2025 11:03:21 +0100 Subject: [PATCH 063/119] dt-bindings: gpio: nxp,pcf8575: add reset GPIO A few of the I2C GPIO expander chips supported by this binding have a RESETN pin to be able to reset the chip. The chip is held in reset while the pin is low, therefore the polarity of reset-gpios is expected to reflect that, i.e. a GPIO_ACTIVE_HIGH means the GPIO will be driven high for reset and then driven low, GPIO_ACTIVE_LOW means the GPIO will be driven low for reset and then driven high. If a GPIO is directly routed to RESETN pin on the IC without any inverter, GPIO_ACTIVE_LOW is thus expected. Out of the supported chips, only PCA9670, PCA9671, PCA9672 and PCA9673 show a RESETN pin in their datasheets. They all share the same reset timings, that is 4+us reset pulse[0] and 100+us reset time[0]. When performing a reset, "The PCA9670 registers and I2C-bus state machine will be held in their default state until the RESET input is once again HIGH."[1] meaning we now know the state of each line controlled by the GPIO expander. Therefore, setting lines-initial-states and reset-gpios both does not make sense and their presence is XOR'ed. [0] https://www.nxp.com/docs/en/data-sheet/PCA9670.pdf Fig 22. [1] https://www.nxp.com/docs/en/data-sheet/PCA9670.pdf 8.5 Tested-by: Heiko Stuebner # exclusion logic Acked-by: Conor Dooley Reviewed-by: Laurent Pinchart Signed-off-by: Quentin Schulz Link: https://lore.kernel.org/r/20250224-pca976x-reset-driver-v3-1-58370ef405be@cherry.de Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/nxp,pcf8575.yaml | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/nxp,pcf8575.yaml b/Documentation/devicetree/bindings/gpio/nxp,pcf8575.yaml index 3718103e966a..8bca574bb66d 100644 --- a/Documentation/devicetree/bindings/gpio/nxp,pcf8575.yaml +++ b/Documentation/devicetree/bindings/gpio/nxp,pcf8575.yaml @@ -73,6 +73,43 @@ properties: wakeup-source: true + reset-gpios: + maxItems: 1 + description: + GPIO controlling the (reset active LOW) RESET# pin. + + The active polarity of the GPIO must translate to the low state of the + RESET# pin on the IC, i.e. if a GPIO is directly routed to the RESET# pin + without any inverter, GPIO_ACTIVE_LOW is expected. + + Performing a reset makes all lines initialized to their input (pulled-up) + state. + +allOf: + - if: + properties: + compatible: + not: + contains: + enum: + - nxp,pca9670 + - nxp,pca9671 + - nxp,pca9672 + - nxp,pca9673 + then: + properties: + reset-gpios: false + + # lines-initial-states XOR reset-gpios + # Performing a reset reinitializes all lines to a known state which + # may not match passed lines-initial-states + - if: + required: + - lines-initial-states + then: + properties: + reset-gpios: false + patternProperties: "^(.+-hog(-[0-9]+)?)$": type: object From 087f8a6b8ce93d582e0b84af538da13d735e2444 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Mon, 24 Feb 2025 11:03:22 +0100 Subject: [PATCH 064/119] gpio: pcf857x: add support for reset-gpios on (most) PCA967x The PCA9670, PCA9671, PCA9672 and PCA9673 all have a RESETN input pin that is used to reset the I2C GPIO expander. One needs to hold this pin low for at least 4us and the reset should be finished after about 100us according to the datasheet[1]. Once the reset is done, the "registers and I2C-bus state machine will be held in their default state until the RESET input is once again HIGH.". Because the logic is reset, the latch values eventually provided in the Device Tree via lines-initial-states property are inapplicable so they are simply ignored if a reset GPIO is provided. [1] https://www.nxp.com/docs/en/data-sheet/PCA9670.pdf 8.5 and fig 22. Tested-by: Heiko Stuebner # RK3588 Tiger Haikou Video Demo Reviewed-by: Heiko Stuebner Signed-off-by: Quentin Schulz Link: https://lore.kernel.org/r/20250224-pca976x-reset-driver-v3-2-58370ef405be@cherry.de Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pcf857x.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 7c57eaeb0afe..2e5f5d7f8865 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -5,6 +5,8 @@ * Copyright (C) 2007 David Brownell */ +#include +#include #include #include #include @@ -272,12 +274,11 @@ static const struct irq_chip pcf857x_irq_chip = { static int pcf857x_probe(struct i2c_client *client) { + struct gpio_desc *reset_gpio; struct pcf857x *gpio; unsigned int n_latch = 0; int status; - device_property_read_u32(&client->dev, "lines-initial-states", &n_latch); - /* Allocate, initialize, and register this gpio_chip. */ gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL); if (!gpio) @@ -297,6 +298,30 @@ static int pcf857x_probe(struct i2c_client *client) gpio->chip.direction_output = pcf857x_output; gpio->chip.ngpio = (uintptr_t)i2c_get_match_data(client); + reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpio)) + return dev_err_probe(&client->dev, PTR_ERR(reset_gpio), + "failed to get reset GPIO\n"); + + if (reset_gpio) { + /* Reset already held with devm_gpiod_get_optional with GPIOD_OUT_HIGH */ + fsleep(4); /* tw(rst) > 4us */ + gpiod_set_value_cansleep(reset_gpio, 0); + fsleep(100); /* trst > 100uS */ + + /* + * Performing a reset means "The PCA9670 registers and I2C-bus + * state machine will be held in their default state until the + * RESET input is once again HIGH". + * + * This is the same as writing 1 for all pins, which is the same + * as n_latch=0, the default value of the variable. + */ + } else { + device_property_read_u32(&client->dev, "lines-initial-states", + &n_latch); + } + /* NOTE: the OnSemi jlc1562b is also largely compatible with * these parts, notably for output. It has a low-resolution * DAC instead of pin change IRQs; and its inputs can be the From 9becde08f1bc2857cd66f847eca2aaec3fc05c21 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 26 Feb 2025 11:12:29 +0100 Subject: [PATCH 065/119] gpiolib: don't use gpiochip_get_direction() when registering a chip During chip registration we should neither check the return value of gc->get_direction() nor hold the SRCU lock when calling it. The former is because pin controllers may have pins set to alternate functions and return errors from their get_direction() callbacks. That's alright - we should default to the safe INPUT state and not bail-out. The latter is not needed because we haven't registered the chip yet so there's nothing to protect against dynamic removal. In fact: we currently hit a lockdep splat. Revert to calling the gc->get_direction() callback directly and *not* checking its value. Fixes: 9d846b1aebbe ("gpiolib: check the return value of gpio_chip::get_direction()") Fixes: e623c4303ed1 ("gpiolib: sanitize the return value of gpio_chip::get_direction()") Reported-by: Marek Szyprowski Closes: https://lore.kernel.org/all/81f890fc-6688-42f0-9756-567efc8bb97a@samsung.com/ Reviewed-by: Andy Shevchenko Tested-by: Marek Szyprowski Link: https://lore.kernel.org/r/20250226-retval-fixes-v2-1-c8dc57182441@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1b4af0f97e5a..9de28bc20b20 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1087,24 +1087,19 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, desc->gdev = gdev; - if (gc->get_direction && gpiochip_line_is_valid(gc, desc_index)) { - ret = gpiochip_get_direction(gc, desc_index); - if (ret < 0) - /* - * FIXME: Bail-out here once all GPIO drivers - * are updated to not return errors in - * situations that can be considered normal - * operation. - */ - dev_warn(&gdev->dev, - "%s: get_direction failed: %d\n", - __func__, ret); - - assign_bit(FLAG_IS_OUT, &desc->flags, !ret); - } else { + /* + * We would typically want to use gpiochip_get_direction() here + * but we must not check the return value and bail-out as pin + * controllers can have pins configured to alternate functions + * and return -EINVAL. Also: there's no need to take the SRCU + * lock here. + */ + if (gc->get_direction && gpiochip_line_is_valid(gc, desc_index)) + assign_bit(FLAG_IS_OUT, &desc->flags, + !gc->get_direction(gc, desc_index)); + else assign_bit(FLAG_IS_OUT, &desc->flags, !gc->direction_input); - } } ret = of_gpiochip_add(gc); From cc78604fd4799a8e8e9d23ea4898acd6b605982d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 26 Feb 2025 11:12:30 +0100 Subject: [PATCH 066/119] gpiolib: use a more explicit retval logic in gpiochip_get_direction() We have existing macros for direction settings so we don't need to rely on the magic value of 1 in the retval check. Use readable logic that explicitly says we expect INPUT, OUTPUT or a negative errno and nothing else in gpiochip_get_direction(). Fixes: e623c4303ed1 ("gpiolib: sanitize the return value of gpio_chip::get_direction()") Suggested-by: Andy Shevchenko Closes: https://lore.kernel.org/all/Z7yfTggRrk3K6srs@black.fi.intel.com/ Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250226-retval-fixes-v2-2-c8dc57182441@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 9de28bc20b20..49bdae208744 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -352,7 +352,10 @@ static int gpiochip_get_direction(struct gpio_chip *gc, unsigned int offset) return -EOPNOTSUPP; ret = gc->get_direction(gc, offset); - if (ret > 1) + if (ret < 0) + return ret; + + if (ret != GPIO_LINE_DIRECTION_OUT && ret != GPIO_LINE_DIRECTION_IN) ret = -EBADE; return ret; From 8a5680bffb2f681688b5788751c767fc380ff9b7 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 26 Feb 2025 11:12:31 +0100 Subject: [PATCH 067/119] gpiolib: don't double-check the gc->get callback's existence gpiochip_get() is called only in two places: in gpio_chip_get_value() and in gpiochip_get_multiple() where the existence of the gc->get() callback is already checked. It makes sense to unduplicate the check by moving it one level up the stack. Fixes: 86ef402d805d ("gpiolib: sanitize the return value of gpio_chip::get()") Suggested-by: Andy Shevchenko Closes: https://lore.kernel.org/all/Z7yekJ8uRh8dphKn@black.fi.intel.com/ Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250226-retval-fixes-v2-3-c8dc57182441@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 49bdae208744..d0108cf2ee0b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3195,9 +3195,7 @@ static int gpiochip_get(struct gpio_chip *gc, unsigned int offset) lockdep_assert_held(&gc->gpiodev->srcu); - if (!gc->get) - return -EIO; - + /* Make sure this is called after checking for gc->get(). */ ret = gc->get(gc, offset); if (ret > 1) ret = -EBADE; @@ -3207,7 +3205,7 @@ static int gpiochip_get(struct gpio_chip *gc, unsigned int offset) static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *desc) { - return gpiochip_get(gc, gpio_chip_hwgpio(desc)); + return gc->get ? gpiochip_get(gc, gpio_chip_hwgpio(desc)) : -EIO; } /* I/O calls are only valid after configuration completed; the relevant From 8014097f1466f7e034844770c537b8dc7d98811f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 27 Feb 2025 16:28:31 +0100 Subject: [PATCH 068/119] gpiolib: remove unneeded WARN_ON() from gpiochip_set_multiple() GPIO drivers are not required to support set_multiple() - the core will fallback to calling set() for each line if it's missing. Remove the offending check from gpiochip_set_multiple(). Fixes: 98ce1eb1fd87 ("gpiolib: introduce gpio_chip setters that return values") Reported-by: Marek Szyprowski Closes: https://lore.kernel.org/all/ab3e42c0-70fa-48e0-ac93-ecbffef63507@samsung.com/ Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250227152831.59784-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 212269f2d9a2..ec2217a30db3 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3604,9 +3604,6 @@ static int gpiochip_set_multiple(struct gpio_chip *gc, lockdep_assert_held(&gc->gpiodev->srcu); - if (WARN_ON(unlikely(!gc->set_multiple && !gc->set_multiple_rv))) - return -EOPNOTSUPP; - if (gc->set_multiple_rv) { ret = gc->set_multiple_rv(gc, mask, bits); if (ret > 0) From 6224e7fc1ce75edcd03b56a2e0fd4c1765d5888e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 27 Feb 2025 09:37:47 +0100 Subject: [PATCH 069/119] gpiolib: deprecate gpio_chip::set and gpio_chip::set_multiple We now have setter callbacks that allow us to indicate success or failure using the integer return value. Deprecate the older callbacks so that no new code is tempted to use them. Link: https://lore.kernel.org/r/20250227083748.22400-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/driver.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index a2a1b6434321..783897d94be8 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -346,8 +346,8 @@ struct gpio_irq_chip { * @get: returns value for signal "offset", 0=low, 1=high, or negative error * @get_multiple: reads values for multiple signals defined by "mask" and * stores them in "bits", returns 0 on success or negative error - * @set: assigns output value for signal "offset" - * @set_multiple: assigns output values for multiple signals defined by "mask" + * @set: **DEPRECATED** - please use set_rv() instead + * @set_multiple: **DEPRECATED** - please use set_multiple_rv() instead * @set_rv: assigns output value for signal "offset", returns 0 on success or * negative error value * @set_multiple_rv: assigns output values for multiple signals defined by From 9778568dede2166c7bd124d473f9ec365f782935 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 27 Feb 2025 09:37:48 +0100 Subject: [PATCH 070/119] gpiolib: update kerneldocs for value setters Value setters now return int and can indicate failure. Update the kerneldocs. Link: https://lore.kernel.org/r/20250227083748.22400-2-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index ec2217a30db3..8724c7d8459e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3761,6 +3761,9 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, * * This function can be called from contexts where we cannot sleep, and will * complain if the GPIO chip functions potentially sleep. + * + * Returns: + * 0 on success, negative error number on failure. */ int gpiod_set_raw_value(struct gpio_desc *desc, int value) { @@ -3779,6 +3782,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value); * This sets the value of a GPIO line backing a descriptor, applying * different semantic quirks like active low and open drain/source * handling. + * + * Returns: + * 0 on success, negative error number on failure. */ static int gpiod_set_value_nocheck(struct gpio_desc *desc, int value) { @@ -3803,6 +3809,9 @@ static int gpiod_set_value_nocheck(struct gpio_desc *desc, int value) * * This function can be called from contexts where we cannot sleep, and will * complain if the GPIO chip functions potentially sleep. + * + * Returns: + * 0 on success, negative error number on failure. */ int gpiod_set_value(struct gpio_desc *desc, int value) { @@ -4227,6 +4236,9 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); * regard for its ACTIVE_LOW status. * * This function is to be called from contexts that can sleep. + * + * Returns: + * 0 on success, negative error number on failure. */ int gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) { @@ -4245,6 +4257,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); * account * * This function is to be called from contexts that can sleep. + * + * Returns: + * 0 on success, negative error number on failure. */ int gpiod_set_value_cansleep(struct gpio_desc *desc, int value) { From 732457dc46d62f1df4b2b48c6dbf22b4332da14b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 25 Feb 2025 20:40:33 +0100 Subject: [PATCH 071/119] gpiolib: of: Use local variables Instead of modifying the contents of the array of values read in from a phandle, use local variables to store the values. This makes the code easier to read and the array immutable. Reviewed-by: Alex Elder Tested-by: Yixun Lan Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20250225-gpio-ranges-fourcell-v3-1-860382ba4713@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-of.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 2e537ee979f3..86405218f4e2 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -1057,6 +1057,9 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) const char *name; static const char group_names_propname[] = "gpio-ranges-group-names"; bool has_group_names; + int offset; /* Offset of the first GPIO line on the chip */ + int pin; /* Pin base number in the range */ + int count; /* Number of pins/GPIO lines to map */ np = dev_of_node(&chip->gpiodev->dev); if (!np) @@ -1075,13 +1078,17 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) if (!pctldev) return -EPROBE_DEFER; + offset = pinspec.args[0]; + pin = pinspec.args[1]; + count = pinspec.args[2]; + /* Ignore ranges outside of this GPIO chip */ - if (pinspec.args[0] >= (chip->offset + chip->ngpio)) + if (offset >= (chip->offset + chip->ngpio)) continue; - if (pinspec.args[0] + pinspec.args[2] <= chip->offset) + if (offset + count <= chip->offset) continue; - if (pinspec.args[2]) { + if (count) { /* npins != 0: linear range */ if (has_group_names) { of_property_read_string_index(np, @@ -1095,27 +1102,27 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) } /* Trim the range to fit this GPIO chip */ - if (chip->offset > pinspec.args[0]) { - trim = chip->offset - pinspec.args[0]; - pinspec.args[2] -= trim; - pinspec.args[1] += trim; - pinspec.args[0] = 0; + if (chip->offset > offset) { + trim = chip->offset - offset; + count -= trim; + pin += trim; + offset = 0; } else { - pinspec.args[0] -= chip->offset; + offset -= chip->offset; } - if ((pinspec.args[0] + pinspec.args[2]) > chip->ngpio) - pinspec.args[2] = chip->ngpio - pinspec.args[0]; + if ((offset + count) > chip->ngpio) + count = chip->ngpio - offset; ret = gpiochip_add_pin_range(chip, pinctrl_dev_get_devname(pctldev), - pinspec.args[0], - pinspec.args[1], - pinspec.args[2]); + offset, + pin, + count); if (ret) return ret; } else { /* npins == 0: special range */ - if (pinspec.args[1]) { + if (pin) { pr_err("%pOF: Illegal gpio-range format.\n", np); break; @@ -1140,7 +1147,7 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) } ret = gpiochip_add_pingroup_range(chip, pctldev, - pinspec.args[0], name); + offset, name); if (ret) return ret; } From bd3ce71078bde4ecbfc60d49c96d1c55de0635cc Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 25 Feb 2025 20:40:34 +0100 Subject: [PATCH 072/119] gpiolib: of: Handle threecell GPIO chips When describing GPIO controllers in the device tree, the ambition of device tree to describe the hardware may require a three-cell scheme: gpios = <&gpio instance offset flags>; This implements support for this scheme in the gpiolib OF core. Drivers that want to handle multiple gpiochip instances from one OF node need to implement a callback similar to this to determine if a certain gpio chip is a pointer to the right instance (pseudo-code): struct my_gpio { struct gpio_chip gcs[MAX_CHIPS]; }; static bool my_of_node_instance_match(struct gpio_chip *gc unsigned int instance) { struct my_gpio *mg = gpiochip_get_data(gc); if (instance >= MAX_CHIPS) return false; return (gc == &mg->gcs[instance]); } probe() { struct my_gpio *mg; struct gpio_chip *gc; int i, ret; for (i = 0; i++; i < MAX_CHIPS) { gc = &mg->gcs[i]; /* This tells gpiolib we have several instances per node */ gc->of_gpio_n_cells = 3; gc->of_node_instance_match = my_of_node_instance_match; gc->base = -1; ... ret = devm_gpiochip_add_data(dev, gc, mg); if (ret) return ret; } } Rename the "simple" of_xlate function to "twocell" which is closer to what it actually does. In the device tree bindings, the provide node needs to specify #gpio-cells = <3>; where the first cell is the instance number: gpios = <&gpio instance offset flags>; Conversely ranges need to have four cells: gpio-ranges = <&pinctrl instance gpio_offset pin_offset count>; Reviewed-by: Alex Elder Tested-by: Yixun Lan Signed-off-by: Linus Walleij Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250225-gpio-ranges-fourcell-v3-2-860382ba4713@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-of.c | 95 ++++++++++++++++++++++++++++++++----- include/linux/gpio/driver.h | 24 +++++++++- 2 files changed, 107 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 86405218f4e2..6e0eb67dcbf0 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -929,7 +929,7 @@ struct notifier_block gpio_of_notifier = { #endif /* CONFIG_OF_DYNAMIC */ /** - * of_gpio_simple_xlate - translate gpiospec to the GPIO number and flags + * of_gpio_twocell_xlate - translate twocell gpiospec to the GPIO number and flags * @gc: pointer to the gpio_chip structure * @gpiospec: GPIO specifier as found in the device tree * @flags: a flags pointer to fill in @@ -941,9 +941,9 @@ struct notifier_block gpio_of_notifier = { * Returns: * GPIO number (>= 0) on success, negative errno on failure. */ -static int of_gpio_simple_xlate(struct gpio_chip *gc, - const struct of_phandle_args *gpiospec, - u32 *flags) +static int of_gpio_twocell_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, + u32 *flags) { /* * We're discouraging gpio_cells < 2, since that way you'll have to @@ -951,7 +951,7 @@ static int of_gpio_simple_xlate(struct gpio_chip *gc, * number and the flags from a single gpio cell -- this is possible, * but not recommended). */ - if (gc->of_gpio_n_cells < 2) { + if (gc->of_gpio_n_cells != 2) { WARN_ON(1); return -EINVAL; } @@ -968,6 +968,49 @@ static int of_gpio_simple_xlate(struct gpio_chip *gc, return gpiospec->args[0]; } +/** + * of_gpio_threecell_xlate - translate threecell gpiospec to the GPIO number and flags + * @gc: pointer to the gpio_chip structure + * @gpiospec: GPIO specifier as found in the device tree + * @flags: a flags pointer to fill in + * + * This is simple translation function, suitable for the most 1:n mapped + * GPIO chips, i.e. several GPIO chip instances from one device tree node. + * In this case the following binding is implied: + * + * foo-gpios = <&gpio instance offset flags>; + * + * Returns: + * GPIO number (>= 0) on success, negative errno on failure. + */ +static int of_gpio_threecell_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, + u32 *flags) +{ + if (gc->of_gpio_n_cells != 3) { + WARN_ON(1); + return -EINVAL; + } + + if (WARN_ON(gpiospec->args_count != 3)) + return -EINVAL; + + /* + * Check chip instance number, the driver responds with true if + * this is the chip we are looking for. + */ + if (!gc->of_node_instance_match(gc, gpiospec->args[0])) + return -EINVAL; + + if (gpiospec->args[1] >= gc->ngpio) + return -EINVAL; + + if (flags) + *flags = gpiospec->args[2]; + + return gpiospec->args[1]; +} + #if IS_ENABLED(CONFIG_OF_GPIO_MM_GPIOCHIP) #include /** @@ -1068,7 +1111,15 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) has_group_names = of_property_present(np, group_names_propname); for (;; index++) { - ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, + /* + * Ordinary phandles contain 2-3 cells: + * gpios = <&gpio [instance] offset flags>; + * Ranges always contain one more cell: + * gpio-ranges <&pinctrl [gpio_instance] gpio_offet pin_offet count>; + * This is why we parse chip->of_gpio_n_cells + 1 cells + */ + ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", + chip->of_gpio_n_cells + 1, index, &pinspec); if (ret) break; @@ -1078,9 +1129,25 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) if (!pctldev) return -EPROBE_DEFER; - offset = pinspec.args[0]; - pin = pinspec.args[1]; - count = pinspec.args[2]; + if (chip->of_gpio_n_cells == 3) { + /* First cell is the gpiochip instance number */ + offset = pinspec.args[1]; + pin = pinspec.args[2]; + count = pinspec.args[3]; + } else { + offset = pinspec.args[0]; + pin = pinspec.args[1]; + count = pinspec.args[2]; + } + + /* + * With multiple GPIO chips per node, check that this chip is the + * right instance. + */ + if (chip->of_node_instance_match && + (chip->of_gpio_n_cells == 3) && + !chip->of_node_instance_match(chip, pinspec.args[0])) + continue; /* Ignore ranges outside of this GPIO chip */ if (offset >= (chip->offset + chip->ngpio)) @@ -1170,8 +1237,14 @@ int of_gpiochip_add(struct gpio_chip *chip) return 0; if (!chip->of_xlate) { - chip->of_gpio_n_cells = 2; - chip->of_xlate = of_gpio_simple_xlate; + if (chip->of_gpio_n_cells == 3) { + if (!chip->of_node_instance_match) + return -EINVAL; + chip->of_xlate = of_gpio_threecell_xlate; + } else { + chip->of_gpio_n_cells = 2; + chip->of_xlate = of_gpio_twocell_xlate; + } } if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 783897d94be8..83e0a7e86962 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -531,10 +531,32 @@ struct gpio_chip { /** * @of_gpio_n_cells: * - * Number of cells used to form the GPIO specifier. + * Number of cells used to form the GPIO specifier. The standard is 2 + * cells: + * + * gpios = <&gpio offset flags>; + * + * some complex GPIO controllers instantiate more than one chip per + * device tree node and have 3 cells: + * + * gpios = <&gpio instance offset flags>; + * + * Legacy GPIO controllers may even have 1 cell: + * + * gpios = <&gpio offset>; */ unsigned int of_gpio_n_cells; + /** + * of_node_instance_match: + * + * Determine if a chip is the right instance. Must be implemented by + * any driver using more than one gpio_chip per device tree node. + * Returns true if gc is the instance indicated by i (which is the + * first cell in the phandles for GPIO lines and gpio-ranges). + */ + bool (*of_node_instance_match)(struct gpio_chip *gc, unsigned int i); + /** * @of_xlate: * From f2f3d5d62f6fbdaef46d1991086265a497b3e24f Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Thu, 27 Feb 2025 11:51:32 -0600 Subject: [PATCH 073/119] dt-bindings: gpio: mvebu: Add missing 'gpio-ranges' property and hog nodes The Marvell mvebu binding users already use 'gpio-ranges' and have hog nodes, so add them to the binding. Signed-off-by: Rob Herring (Arm) Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250227175132.3734524-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml b/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml index 33d4e4716516..7ed5f9c4dde9 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml @@ -72,6 +72,9 @@ properties: "#gpio-cells": const: 2 + gpio-ranges: + maxItems: 1 + marvell,pwm-offset: $ref: /schemas/types.yaml#/definitions/uint32 description: Offset in the register map for the pwm registers (in bytes) @@ -96,6 +99,13 @@ properties: - const: axi minItems: 1 +patternProperties: + "^(.+-hog(-[0-9]+)?)$": + type: object + + required: + - gpio-hog + required: - compatible - gpio-controller From a45faa2aba2cb2b12ad4c732c9f5692db1f7f12f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 3 Mar 2025 18:00:32 +0200 Subject: [PATCH 074/119] gpiolib: Align FLAG_* definitions in the struct gpio_desc Align FLAG_* definitions in the struct gpio_desc for better readability. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250303160341.1322640-2-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.h | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 147156ec502b..58af0491e60e 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -183,24 +183,24 @@ struct gpio_desc { struct gpio_device *gdev; unsigned long flags; /* flag symbols are bit numbers */ -#define FLAG_REQUESTED 0 -#define FLAG_IS_OUT 1 -#define FLAG_EXPORT 2 /* protected by sysfs_lock */ -#define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */ -#define FLAG_ACTIVE_LOW 6 /* value has active low */ -#define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */ -#define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ -#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ -#define FLAG_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */ -#define FLAG_IS_HOGGED 11 /* GPIO is hogged */ -#define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */ -#define FLAG_PULL_UP 13 /* GPIO has pull up enabled */ -#define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */ -#define FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */ -#define FLAG_EDGE_RISING 16 /* GPIO CDEV detects rising edge events */ -#define FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */ -#define FLAG_EVENT_CLOCK_REALTIME 18 /* GPIO CDEV reports REALTIME timestamps in events */ -#define FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */ +#define FLAG_REQUESTED 0 +#define FLAG_IS_OUT 1 +#define FLAG_EXPORT 2 /* protected by sysfs_lock */ +#define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */ +#define FLAG_ACTIVE_LOW 6 /* value has active low */ +#define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */ +#define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ +#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ +#define FLAG_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */ +#define FLAG_IS_HOGGED 11 /* GPIO is hogged */ +#define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */ +#define FLAG_PULL_UP 13 /* GPIO has pull up enabled */ +#define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */ +#define FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */ +#define FLAG_EDGE_RISING 16 /* GPIO CDEV detects rising edge events */ +#define FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */ +#define FLAG_EVENT_CLOCK_REALTIME 18 /* GPIO CDEV reports REALTIME timestamps in events */ +#define FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */ /* Connection label */ struct gpio_desc_label __rcu *label; From e646f0dae7b0f099b36d12d9cb5ca733b8273f10 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 4 Mar 2025 16:31:19 +0200 Subject: [PATCH 075/119] gpiolib-acpi: Drop unneeded ERR_CAST() in __acpi_find_gpio() The checked type by PTR_ERR() is the same as returned by __acpi_find_gpio(). Hence there is no need to cast, drop it. Acked-by: Mika Westerberg Signed-off-by: Andy Shevchenko --- 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 1f9fe50bba00..f0dd1aa89583 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -994,7 +994,7 @@ __acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id, unsigned int desc = acpi_get_gpiod_from_data(fwnode, propname, idx, info); if (PTR_ERR(desc) == -EPROBE_DEFER) - return ERR_CAST(desc); + return desc; if (!IS_ERR(desc)) return desc; From e4a345c55e1b9b5ab5212a93b081f666f71b303b Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Mon, 3 Mar 2025 15:45:51 +0800 Subject: [PATCH 076/119] dt-bindings: gpio: loongson: Add new loongson gpio chip compatible Add the devicetree compatibles for Loongson-7A2000 and Loongson-3A6000 gpio chip. Signed-off-by: Binbin Zhou Acked-by: Rob Herring (Arm) Reviewed-by: Linus Walleij Acked-by: Huacai Chen Link: https://lore.kernel.org/r/20250303074552.3335186-1-zhoubinbin@loongson.cn Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/loongson,ls-gpio.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/loongson,ls-gpio.yaml b/Documentation/devicetree/bindings/gpio/loongson,ls-gpio.yaml index cf3b1b270aa8..b68159600e2b 100644 --- a/Documentation/devicetree/bindings/gpio/loongson,ls-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/loongson,ls-gpio.yaml @@ -20,7 +20,10 @@ properties: - loongson,ls2k2000-gpio1 - loongson,ls2k2000-gpio2 - loongson,ls3a5000-gpio + - loongson,ls3a6000-gpio # Loongson-3A6000 node GPIO - loongson,ls7a-gpio + - loongson,ls7a2000-gpio1 # LS7A2000 chipset GPIO + - loongson,ls7a2000-gpio2 # LS7A2000 ACPI GPIO - items: - const: loongson,ls2k1000-gpio - const: loongson,ls2k-gpio From 44fe79020b91a1a8620e44d4f361b389e8fc552f Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Mon, 3 Mar 2025 15:45:52 +0800 Subject: [PATCH 077/119] gpio: loongson-64bit: Add more gpio chip support The Loongson-7A2000 and Loongson-3A6000 share the same gpio chip model. Just add them through driver_data. Signed-off-by: Binbin Zhou Acked-by: Huacai Chen Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250303074552.3335186-2-zhoubinbin@loongson.cn Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-loongson-64bit.c | 51 ++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/gpio/gpio-loongson-64bit.c b/drivers/gpio/gpio-loongson-64bit.c index f000cc0356c7..a9a93036f08f 100644 --- a/drivers/gpio/gpio-loongson-64bit.c +++ b/drivers/gpio/gpio-loongson-64bit.c @@ -254,6 +254,33 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls7a_data = { .out_offset = 0x900, }; +/* LS7A2000 chipset GPIO */ +static const struct loongson_gpio_chip_data loongson_gpio_ls7a2000_data0 = { + .label = "ls7a2000_gpio", + .mode = BYTE_CTRL_MODE, + .conf_offset = 0x800, + .in_offset = 0xa00, + .out_offset = 0x900, +}; + +/* LS7A2000 ACPI GPIO */ +static const struct loongson_gpio_chip_data loongson_gpio_ls7a2000_data1 = { + .label = "ls7a2000_gpio", + .mode = BYTE_CTRL_MODE, + .conf_offset = 0x4, + .in_offset = 0x8, + .out_offset = 0x0, +}; + +/* Loongson-3A6000 node GPIO */ +static const struct loongson_gpio_chip_data loongson_gpio_ls3a6000_data = { + .label = "ls3a6000_gpio", + .mode = BIT_CTRL_MODE, + .conf_offset = 0x0, + .in_offset = 0xc, + .out_offset = 0x8, +}; + static const struct of_device_id loongson_gpio_of_match[] = { { .compatible = "loongson,ls2k-gpio", @@ -287,6 +314,18 @@ static const struct of_device_id loongson_gpio_of_match[] = { .compatible = "loongson,ls7a-gpio", .data = &loongson_gpio_ls7a_data, }, + { + .compatible = "loongson,ls7a2000-gpio1", + .data = &loongson_gpio_ls7a2000_data0, + }, + { + .compatible = "loongson,ls7a2000-gpio2", + .data = &loongson_gpio_ls7a2000_data1, + }, + { + .compatible = "loongson,ls3a6000-gpio", + .data = &loongson_gpio_ls3a6000_data, + }, {} }; MODULE_DEVICE_TABLE(of, loongson_gpio_of_match); @@ -312,6 +351,18 @@ static const struct acpi_device_id loongson_gpio_acpi_match[] = { .id = "LOON000C", .driver_data = (kernel_ulong_t)&loongson_gpio_ls2k2000_data2, }, + { + .id = "LOON000D", + .driver_data = (kernel_ulong_t)&loongson_gpio_ls7a2000_data0, + }, + { + .id = "LOON000E", + .driver_data = (kernel_ulong_t)&loongson_gpio_ls7a2000_data1, + }, + { + .id = "LOON000F", + .driver_data = (kernel_ulong_t)&loongson_gpio_ls3a6000_data, + }, {} }; MODULE_DEVICE_TABLE(acpi, loongson_gpio_acpi_match); From a501624864f3fd9ab785f1f674f48dca535b198c Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Wed, 5 Mar 2025 15:12:45 +0200 Subject: [PATCH 078/119] gpio: Respect valid_mask when requesting GPIOs When GPIOs were requested the validity of GPIOs were checked only when the GPIO-chip had the request -callback populated. This made using masked GPIOs possible. The GPIO chip driver authors may find it difficult to understand the relation of enforsing the GPIO validity and the 'request' -callback because the current documentation for the 'request' callback does not mention this. It only states: * @request: optional hook for chip-specific activation, such as * enabling module power and clock; may sleep The validity of the GPIO line should be checked whether the driver provides the 'request' callback or not. Unconditionally check the GPIO validity when GPIO is being requested. Signed-off-by: Matti Vaittinen Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/cd5e067b80e1bb590027bc3bfa817e7f794f21c3.1741180097.git.mazziesaccount@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 8724c7d8459e..b5f472beb3bd 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2358,16 +2358,16 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) if (test_and_set_bit(FLAG_REQUESTED, &desc->flags)) return -EBUSY; + offset = gpio_chip_hwgpio(desc); + if (!gpiochip_line_is_valid(guard.gc, offset)) + return -EINVAL; + /* NOTE: gpio_request() can be called in early boot, * before IRQs are enabled, for non-sleeping (SOC) GPIOs. */ if (guard.gc->request) { - offset = gpio_chip_hwgpio(desc); - if (gpiochip_line_is_valid(guard.gc, offset)) - ret = guard.gc->request(guard.gc, offset); - else - ret = -EINVAL; + ret = guard.gc->request(guard.gc, offset); if (ret > 0) ret = -EBADE; if (ret) From f636d4f60ac477187a466a573f947731fa762059 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Wed, 5 Mar 2025 15:13:00 +0200 Subject: [PATCH 079/119] gpio: Add a valid_mask getter The valid_mask member of the struct gpio_chip is unconditionally written by the GPIO core at driver registration. It shouldn't be directly populated by drivers. This can be prevented by moving it from the struct gpio_chip to struct gpio_device, which is internal to the GPIO core. As a preparatory step, provide a getter function which can be used by those drivers which need the valid_mask information. Signed-off-by: Matti Vaittinen Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/026f9d78502eca883bfe3faeb684e23d5d6c5e84.1741180097.git.mazziesaccount@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 16 ++++++++++++++++ include/linux/gpio/driver.h | 1 + 2 files changed, 17 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index b5f472beb3bd..4c15a70d4d80 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -723,6 +723,22 @@ static int gpiochip_add_pin_ranges(struct gpio_chip *gc) return 0; } +/** + * gpiochip_query_valid_mask - return the GPIO validity information + * @gc: gpio chip which validity information is queried + * + * Returns: bitmap representing valid GPIOs or NULL if all GPIOs are valid + * + * Some GPIO chips may support configurations where some of the pins aren't + * available. These chips can have valid_mask set to represent the valid + * GPIOs. This function can be used to retrieve this information. + */ +const unsigned long *gpiochip_query_valid_mask(const struct gpio_chip *gc) +{ + return gc->valid_mask; +} +EXPORT_SYMBOL_GPL(gpiochip_query_valid_mask); + bool gpiochip_line_is_valid(const struct gpio_chip *gc, unsigned int offset) { diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 83e0a7e86962..e3b59fda62e0 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -720,6 +720,7 @@ bool gpiochip_line_is_open_source(struct gpio_chip *gc, unsigned int offset); /* Sleep persistence inquiry for drivers */ bool gpiochip_line_is_persistent(struct gpio_chip *gc, unsigned int offset); bool gpiochip_line_is_valid(const struct gpio_chip *gc, unsigned int offset); +const unsigned long *gpiochip_query_valid_mask(const struct gpio_chip *gc); /* get driver data */ void *gpiochip_get_data(struct gpio_chip *gc); From 43b665c961a6468fa8416805ef71daa5e7a152e7 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Wed, 5 Mar 2025 15:13:12 +0200 Subject: [PATCH 080/119] gpio: gpio-rcar: Drop direct use of valid_mask The valid_mask member of the struct gpio_chip is unconditionally written by the GPIO core at driver registration. It should not be directly populated by the drivers. Hiding the valid_mask in struct gpio_device makes it clear it is not meant to be directly populated by drivers. This means drivers should not access it directly from the struct gpio_chip. The gpio-rcar checks the valid mask in set/get_multiple() operations. This is no longer needed [1]. Drop these checks. Additionally, the valid_mask is needed for enabling the GPIO inputs at probe time. Use the new valid_mask -getter function instead of accessing it directly from the struct gpio_chip. Signed-off-by: Matti Vaittinen Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/e46441ba8a2840e6b48ec8d2ecd5919995a5675f.1741180097.git.mazziesaccount@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rcar.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 2ecee3269a0c..e32d731d0473 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -336,9 +336,6 @@ static int gpio_rcar_get_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long flags; bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); - if (chip->valid_mask) - bankmask &= chip->valid_mask[0]; - if (!bankmask) return 0; @@ -380,9 +377,6 @@ static void gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask, u32 val, bankmask; bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); - if (chip->valid_mask) - bankmask &= chip->valid_mask[0]; - if (!bankmask) return; @@ -482,10 +476,13 @@ static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins) static void gpio_rcar_enable_inputs(struct gpio_rcar_priv *p) { u32 mask = GENMASK(p->gpio_chip.ngpio - 1, 0); + const unsigned long *valid_mask; + + valid_mask = gpiochip_query_valid_mask(&p->gpio_chip); /* Select "Input Enable" in INEN */ - if (p->gpio_chip.valid_mask) - mask &= p->gpio_chip.valid_mask[0]; + if (valid_mask) + mask &= valid_mask[0]; if (mask) gpio_rcar_write(p, INEN, gpio_rcar_read(p, INEN) | mask); } From 8015443e24e76fac97268603e91c4793970ce657 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Wed, 5 Mar 2025 15:13:25 +0200 Subject: [PATCH 081/119] gpio: Hide valid_mask from direct assignments The valid_mask member of the struct gpio_chip is unconditionally written by the GPIO core at driver registration. Current documentation does not mention this but just says the valid_mask is used if it's not NULL. This lured me to try populating it directly in the GPIO driver probe instead of using the init_valid_mask() callback. It took some retries with different bitmaps and eventually a bit of code-reading to understand why the valid_mask was not obeyed. I could've avoided this trial and error if the valid_mask was hidden in the struct gpio_device instead of being a visible member of the struct gpio_chip. Help the next developer who decides to directly populate the valid_mask in struct gpio_chip by hiding the valid_mask in struct gpio_device and keep it internal to the GPIO core. Suggested-by: Linus Walleij Signed-off-by: Matti Vaittinen Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/4547ca90d910d60cab3d56d864d59ddde47a5e93.1741180097.git.mazziesaccount@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 16 ++++++++-------- drivers/gpio/gpiolib.h | 3 +++ include/linux/gpio/driver.h | 8 -------- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4c15a70d4d80..e5eb3f0ee071 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -672,7 +672,7 @@ static int gpiochip_apply_reserved_ranges(struct gpio_chip *gc) if (start >= gc->ngpio || start + count > gc->ngpio) continue; - bitmap_clear(gc->valid_mask, start, count); + bitmap_clear(gc->gpiodev->valid_mask, start, count); } kfree(ranges); @@ -686,8 +686,8 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gc) if (!(gpiochip_count_reserved_ranges(gc) || gc->init_valid_mask)) return 0; - gc->valid_mask = gpiochip_allocate_mask(gc); - if (!gc->valid_mask) + gc->gpiodev->valid_mask = gpiochip_allocate_mask(gc); + if (!gc->gpiodev->valid_mask) return -ENOMEM; ret = gpiochip_apply_reserved_ranges(gc); @@ -696,7 +696,7 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gc) if (gc->init_valid_mask) return gc->init_valid_mask(gc, - gc->valid_mask, + gc->gpiodev->valid_mask, gc->ngpio); return 0; @@ -704,7 +704,7 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gc) static void gpiochip_free_valid_mask(struct gpio_chip *gc) { - gpiochip_free_mask(&gc->valid_mask); + gpiochip_free_mask(&gc->gpiodev->valid_mask); } static int gpiochip_add_pin_ranges(struct gpio_chip *gc) @@ -735,7 +735,7 @@ static int gpiochip_add_pin_ranges(struct gpio_chip *gc) */ const unsigned long *gpiochip_query_valid_mask(const struct gpio_chip *gc) { - return gc->valid_mask; + return gc->gpiodev->valid_mask; } EXPORT_SYMBOL_GPL(gpiochip_query_valid_mask); @@ -743,9 +743,9 @@ bool gpiochip_line_is_valid(const struct gpio_chip *gc, unsigned int offset) { /* No mask means all valid */ - if (likely(!gc->valid_mask)) + if (likely(!gc->gpiodev->valid_mask)) return true; - return test_bit(offset, gc->valid_mask); + return test_bit(offset, gc->gpiodev->valid_mask); } EXPORT_SYMBOL_GPL(gpiochip_line_is_valid); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 58af0491e60e..a738e6c647d8 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -32,6 +32,8 @@ * @chip: pointer to the corresponding gpiochip, holding static * data for this device * @descs: array of ngpio descriptors. + * @valid_mask: If not %NULL, holds bitmask of GPIOs which are valid to be + * used from the chip. * @desc_srcu: ensures consistent state of GPIO descriptors exposed to users * @ngpio: the number of GPIO lines on this GPIO device, equal to the size * of the @descs array. @@ -65,6 +67,7 @@ struct gpio_device { struct module *owner; struct gpio_chip __rcu *chip; struct gpio_desc *descs; + unsigned long *valid_mask; struct srcu_struct desc_srcu; unsigned int base; u16 ngpio; diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index e3b59fda62e0..e6e5304c99ca 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -514,14 +514,6 @@ struct gpio_chip { struct gpio_irq_chip irq; #endif /* CONFIG_GPIOLIB_IRQCHIP */ - /** - * @valid_mask: - * - * If not %NULL, holds bitmask of GPIOs which are valid to be used - * from the chip. - */ - unsigned long *valid_mask; - #if defined(CONFIG_OF_GPIO) /* * If CONFIG_OF_GPIO is enabled, then all GPIO controllers described in From 9b443b68d97983dfb9a92009a5c951364fa35985 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 5 Mar 2025 10:49:39 +0100 Subject: [PATCH 082/119] gpiolib: fix kerneldoc Add missing '@' to the kernel doc for the new of_node_instance_match field of struct gpio_chip. Fixes: bd3ce71078bd ("gpiolib: of: Handle threecell GPIO chips") Reported-by: Stephen Rothwell Closes: https://lore.kernel.org/all/20250305203929.70283b9b@canb.auug.org.au/ Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250305094939.40011-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index e6e5304c99ca..4c0294a9104d 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -540,7 +540,7 @@ struct gpio_chip { unsigned int of_gpio_n_cells; /** - * of_node_instance_match: + * @of_node_instance_match: * * Determine if a chip is the right instance. Must be implemented by * any driver using more than one gpio_chip per device tree node. From e93160942585832a1836381018daf9729eb9ca64 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Thu, 6 Mar 2025 12:09:21 -0500 Subject: [PATCH 083/119] dt-bindings: gpio: vf610: Add i.MX94 support Add compatible string "fsl,imx94-gpio" for the i.MX94 chip, which is backward compatible with i.MX8ULP. Set it to fall back to "fsl,imx8ulp-gpio". Signed-off-by: Frank Li Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20250306170921.241690-1-Frank.Li@nxp.com Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/gpio-vf610.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio-vf610.yaml b/Documentation/devicetree/bindings/gpio/gpio-vf610.yaml index cabda2eab4a2..4fb32e9aec0a 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-vf610.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-vf610.yaml @@ -28,6 +28,7 @@ properties: - items: - enum: - fsl,imx93-gpio + - fsl,imx94-gpio - fsl,imx95-gpio - const: fsl,imx8ulp-gpio From 56f16c9f26ef2b36b09268a87f54c9f33eef3179 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 3 Mar 2025 14:18:26 +0100 Subject: [PATCH 084/119] gpio: 74x164: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250303-gpiochip-set-conversion-v1-1-1d5cceeebf8b@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-74x164.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index 640ac24b72a2..4dd5c2c330bb 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -50,8 +50,8 @@ static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset) return !!(chip->buffer[bank] & BIT(pin)); } -static void gen_74x164_set_value(struct gpio_chip *gc, - unsigned offset, int val) +static int gen_74x164_set_value(struct gpio_chip *gc, + unsigned int offset, int val) { struct gen_74x164_chip *chip = gpiochip_get_data(gc); u8 bank = chip->registers - 1 - offset / 8; @@ -64,11 +64,11 @@ static void gen_74x164_set_value(struct gpio_chip *gc, else chip->buffer[bank] &= ~BIT(pin); - __gen_74x164_write_config(chip); + return __gen_74x164_write_config(chip); } -static void gen_74x164_set_multiple(struct gpio_chip *gc, unsigned long *mask, - unsigned long *bits) +static int gen_74x164_set_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) { struct gen_74x164_chip *chip = gpiochip_get_data(gc); unsigned long offset; @@ -85,7 +85,7 @@ static void gen_74x164_set_multiple(struct gpio_chip *gc, unsigned long *mask, chip->buffer[bank] &= ~bankmask; chip->buffer[bank] |= bitmask; } - __gen_74x164_write_config(chip); + return __gen_74x164_write_config(chip); } static int gen_74x164_direction_output(struct gpio_chip *gc, @@ -141,8 +141,8 @@ static int gen_74x164_probe(struct spi_device *spi) chip->gpio_chip.label = spi->modalias; chip->gpio_chip.direction_output = gen_74x164_direction_output; chip->gpio_chip.get = gen_74x164_get_value; - chip->gpio_chip.set = gen_74x164_set_value; - chip->gpio_chip.set_multiple = gen_74x164_set_multiple; + chip->gpio_chip.set_rv = gen_74x164_set_value; + chip->gpio_chip.set_multiple_rv = gen_74x164_set_multiple; chip->gpio_chip.base = -1; chip->gpio_chip.ngpio = GEN_74X164_NUMBER_GPIOS * chip->registers; chip->gpio_chip.can_sleep = true; From 0dfce460fe2e2a4744bfe881793a9f48a46c6095 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 3 Mar 2025 14:18:28 +0100 Subject: [PATCH 085/119] gpio: adnp: use devm_mutex_init() The mutex initialized in probe() is never cleaned up. Use devm_mutex_init() to do it automatically. Link: https://lore.kernel.org/r/20250303-gpiochip-set-conversion-v1-3-1d5cceeebf8b@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-adnp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index 6dafab0cf964..9e752e98db9d 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -506,7 +507,10 @@ static int adnp_i2c_probe(struct i2c_client *client) if (!adnp) return -ENOMEM; - mutex_init(&adnp->i2c_lock); + err = devm_mutex_init(&client->dev, &adnp->i2c_lock); + if (err) + return err; + adnp->client = client; err = adnp_gpio_setup(adnp, num_gpios, device_property_read_bool(dev, "interrupt-controller")); From 8a9bc5a56f5335be56791e8404c6cdb7169bbba9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 3 Mar 2025 14:18:30 +0100 Subject: [PATCH 086/119] gpio: adp5520: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Acked-by: Michael Hennerich Link: https://lore.kernel.org/r/20250303-gpiochip-set-conversion-v1-5-1d5cceeebf8b@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-adp5520.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-adp5520.c b/drivers/gpio/gpio-adp5520.c index c55e821c63b6..57d12c10cbda 100644 --- a/drivers/gpio/gpio-adp5520.c +++ b/drivers/gpio/gpio-adp5520.c @@ -40,16 +40,18 @@ static int adp5520_gpio_get_value(struct gpio_chip *chip, unsigned off) return !!(reg_val & dev->lut[off]); } -static void adp5520_gpio_set_value(struct gpio_chip *chip, - unsigned off, int val) +static int adp5520_gpio_set_value(struct gpio_chip *chip, + unsigned int off, int val) { struct adp5520_gpio *dev; dev = gpiochip_get_data(chip); if (val) - adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]); + return adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, + dev->lut[off]); else - adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]); + return adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, + dev->lut[off]); } static int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off) @@ -120,7 +122,7 @@ static int adp5520_gpio_probe(struct platform_device *pdev) gc->direction_input = adp5520_gpio_direction_input; gc->direction_output = adp5520_gpio_direction_output; gc->get = adp5520_gpio_get_value; - gc->set = adp5520_gpio_set_value; + gc->set_rv = adp5520_gpio_set_value; gc->can_sleep = true; gc->base = pdata->gpio_start; From 3fccfa561b5504b11dd438fd29f1a37f3bf2f617 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 3 Mar 2025 14:18:31 +0100 Subject: [PATCH 087/119] gpio: adp5585: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Acked-by: Michael Hennerich Link: https://lore.kernel.org/r/20250303-gpiochip-set-conversion-v1-6-1d5cceeebf8b@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-adp5585.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-adp5585.c b/drivers/gpio/gpio-adp5585.c index 000d31f09671..d5c0f1b267c8 100644 --- a/drivers/gpio/gpio-adp5585.c +++ b/drivers/gpio/gpio-adp5585.c @@ -86,14 +86,16 @@ static int adp5585_gpio_get_value(struct gpio_chip *chip, unsigned int off) return !!(val & bit); } -static void adp5585_gpio_set_value(struct gpio_chip *chip, unsigned int off, int val) +static int adp5585_gpio_set_value(struct gpio_chip *chip, unsigned int off, + int val) { struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip); unsigned int bank = ADP5585_BANK(off); unsigned int bit = ADP5585_BIT(off); - regmap_update_bits(adp5585_gpio->regmap, ADP5585_GPO_DATA_OUT_A + bank, - bit, val ? bit : 0); + return regmap_update_bits(adp5585_gpio->regmap, + ADP5585_GPO_DATA_OUT_A + bank, + bit, val ? bit : 0); } static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio, @@ -192,7 +194,7 @@ static int adp5585_gpio_probe(struct platform_device *pdev) gc->direction_input = adp5585_gpio_direction_input; gc->direction_output = adp5585_gpio_direction_output; gc->get = adp5585_gpio_get_value; - gc->set = adp5585_gpio_set_value; + gc->set_rv = adp5585_gpio_set_value; gc->set_config = adp5585_gpio_set_config; gc->can_sleep = true; From 65a0b13d92a044fc86a6842a72572fbcdc8fc4e8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 3 Mar 2025 14:18:32 +0100 Subject: [PATCH 088/119] gpio: altera-a10sr: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250303-gpiochip-set-conversion-v1-7-1d5cceeebf8b@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-altera-a10sr.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-altera-a10sr.c b/drivers/gpio/gpio-altera-a10sr.c index 11edf1fe6c90..77a674cf99e4 100644 --- a/drivers/gpio/gpio-altera-a10sr.c +++ b/drivers/gpio/gpio-altera-a10sr.c @@ -35,15 +35,15 @@ static int altr_a10sr_gpio_get(struct gpio_chip *chip, unsigned int offset) return !!(val & BIT(offset - ALTR_A10SR_LED_VALID_SHIFT)); } -static void altr_a10sr_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int altr_a10sr_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct altr_a10sr_gpio *gpio = gpiochip_get_data(chip); - regmap_update_bits(gpio->regmap, ALTR_A10SR_LED_REG, - BIT(ALTR_A10SR_LED_VALID_SHIFT + offset), - value ? BIT(ALTR_A10SR_LED_VALID_SHIFT + offset) - : 0); + return regmap_update_bits(gpio->regmap, ALTR_A10SR_LED_REG, + BIT(ALTR_A10SR_LED_VALID_SHIFT + offset), + value ? + BIT(ALTR_A10SR_LED_VALID_SHIFT + offset) : 0); } static int altr_a10sr_gpio_direction_input(struct gpio_chip *gc, @@ -69,7 +69,7 @@ static const struct gpio_chip altr_a10sr_gc = { .label = "altr_a10sr_gpio", .owner = THIS_MODULE, .get = altr_a10sr_gpio_get, - .set = altr_a10sr_gpio_set, + .set_rv = altr_a10sr_gpio_set, .direction_input = altr_a10sr_gpio_direction_input, .direction_output = altr_a10sr_gpio_direction_output, .can_sleep = true, From adf5412d66e848c74178ab28a0278ad90237ee8d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 3 Mar 2025 14:18:33 +0100 Subject: [PATCH 089/119] gpio: altera: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250303-gpiochip-set-conversion-v1-8-1d5cceeebf8b@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-altera.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 17ab039c7413..1b28525726d7 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -113,7 +113,7 @@ static int altera_gpio_get(struct gpio_chip *gc, unsigned offset) return !!(readl(altera_gc->regs + ALTERA_GPIO_DATA) & BIT(offset)); } -static void altera_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +static int altera_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) { struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); unsigned long flags; @@ -127,6 +127,8 @@ static void altera_gpio_set(struct gpio_chip *gc, unsigned offset, int value) data_reg &= ~BIT(offset); writel(data_reg, altera_gc->regs + ALTERA_GPIO_DATA); raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); + + return 0; } static int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset) @@ -257,7 +259,7 @@ static int altera_gpio_probe(struct platform_device *pdev) altera_gc->gc.direction_input = altera_gpio_direction_input; altera_gc->gc.direction_output = altera_gpio_direction_output; altera_gc->gc.get = altera_gpio_get; - altera_gc->gc.set = altera_gpio_set; + altera_gc->gc.set_rv = altera_gpio_set; altera_gc->gc.owner = THIS_MODULE; altera_gc->gc.parent = &pdev->dev; altera_gc->gc.base = -1; From 53f2a240401bf945ca8b6bc9a1f1ef9429284584 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 3 Mar 2025 14:18:34 +0100 Subject: [PATCH 090/119] gpio: amd8111: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250303-gpiochip-set-conversion-v1-9-1d5cceeebf8b@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-amd8111.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-amd8111.c b/drivers/gpio/gpio-amd8111.c index 3377667a28de..425d8472f744 100644 --- a/drivers/gpio/gpio-amd8111.c +++ b/drivers/gpio/gpio-amd8111.c @@ -94,7 +94,7 @@ static void amd_gpio_free(struct gpio_chip *chip, unsigned offset) iowrite8(agp->orig[offset], agp->pm + AMD_REG_GPIO(offset)); } -static void amd_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int amd_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct amd_gpio *agp = gpiochip_get_data(chip); u8 temp; @@ -107,6 +107,8 @@ static void amd_gpio_set(struct gpio_chip *chip, unsigned offset, int value) spin_unlock_irqrestore(&agp->lock, flags); dev_dbg(&agp->pdev->dev, "Setting gpio %d, value %d, reg=%02x\n", offset, !!value, temp); + + return 0; } static int amd_gpio_get(struct gpio_chip *chip, unsigned offset) @@ -163,7 +165,7 @@ static struct amd_gpio gp = { .ngpio = 32, .request = amd_gpio_request, .free = amd_gpio_free, - .set = amd_gpio_set, + .set_rv = amd_gpio_set, .get = amd_gpio_get, .direction_output = amd_gpio_dirout, .direction_input = amd_gpio_dirin, From 33dbb118e89d109ad522f461d058766a4aa07414 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 3 Mar 2025 14:18:35 +0100 Subject: [PATCH 091/119] gpio: amd-fch: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250303-gpiochip-set-conversion-v1-10-1d5cceeebf8b@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-amd-fch.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-amd-fch.c b/drivers/gpio/gpio-amd-fch.c index 2a21354ed6a0..f8d0cea46049 100644 --- a/drivers/gpio/gpio-amd-fch.c +++ b/drivers/gpio/gpio-amd-fch.c @@ -95,8 +95,7 @@ static int amd_fch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; } -static void amd_fch_gpio_set(struct gpio_chip *gc, - unsigned int gpio, int value) +static int amd_fch_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) { unsigned long flags; struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); @@ -113,6 +112,8 @@ static void amd_fch_gpio_set(struct gpio_chip *gc, writel_relaxed(mask, ptr); spin_unlock_irqrestore(&priv->lock, flags); + + return 0; } static int amd_fch_gpio_get(struct gpio_chip *gc, @@ -164,7 +165,7 @@ static int amd_fch_gpio_probe(struct platform_device *pdev) priv->gc.direction_output = amd_fch_gpio_direction_output; priv->gc.get_direction = amd_fch_gpio_get_direction; priv->gc.get = amd_fch_gpio_get; - priv->gc.set = amd_fch_gpio_set; + priv->gc.set_rv = amd_fch_gpio_set; spin_lock_init(&priv->lock); From 74ab452321414ba90dc7b2c614bbce1222a6c62b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 3 Mar 2025 14:18:36 +0100 Subject: [PATCH 092/119] gpio: arizona: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20250303-gpiochip-set-conversion-v1-11-1d5cceeebf8b@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-arizona.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c index c15fda99120a..e530c94dcce8 100644 --- a/drivers/gpio/gpio-arizona.c +++ b/drivers/gpio/gpio-arizona.c @@ -121,7 +121,8 @@ static int arizona_gpio_direction_out(struct gpio_chip *chip, ARIZONA_GPN_DIR | ARIZONA_GPN_LVL, value); } -static void arizona_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int arizona_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip); struct arizona *arizona = arizona_gpio->arizona; @@ -129,8 +130,8 @@ static void arizona_gpio_set(struct gpio_chip *chip, unsigned offset, int value) if (value) value = ARIZONA_GPN_LVL; - regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset, - ARIZONA_GPN_LVL, value); + return regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset, + ARIZONA_GPN_LVL, value); } static const struct gpio_chip template_chip = { @@ -139,7 +140,7 @@ static const struct gpio_chip template_chip = { .direction_input = arizona_gpio_direction_in, .get = arizona_gpio_get, .direction_output = arizona_gpio_direction_out, - .set = arizona_gpio_set, + .set_rv = arizona_gpio_set, .can_sleep = true, }; From 4cdc191279cbb64d050ad3618126fc535d1bdda4 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 3 Mar 2025 14:18:37 +0100 Subject: [PATCH 093/119] gpio: aspeed: use lock guards Reduce the code complexity by using automatic lock guards with the raw spinlock. Link: https://lore.kernel.org/r/20250303-gpiochip-set-conversion-v1-12-1d5cceeebf8b@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-aspeed.c | 101 ++++++++++++++----------------------- 1 file changed, 38 insertions(+), 63 deletions(-) diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 40c1bd80f8b0..e2535aad1026 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -5,6 +5,7 @@ * Joel Stanley */ +#include #include #include #include @@ -427,37 +428,33 @@ static void aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) { struct aspeed_gpio *gpio = gpiochip_get_data(gc); - unsigned long flags; bool copro = false; - raw_spin_lock_irqsave(&gpio->lock, flags); + guard(raw_spinlock_irqsave)(&gpio->lock); + copro = aspeed_gpio_copro_request(gpio, offset); __aspeed_gpio_set(gc, offset, val); if (copro) aspeed_gpio_copro_release(gpio, offset); - raw_spin_unlock_irqrestore(&gpio->lock, flags); } static int aspeed_gpio_dir_in(struct gpio_chip *gc, unsigned int offset) { struct aspeed_gpio *gpio = gpiochip_get_data(gc); - unsigned long flags; bool copro = false; if (!have_input(gpio, offset)) return -ENOTSUPP; - raw_spin_lock_irqsave(&gpio->lock, flags); + guard(raw_spinlock_irqsave)(&gpio->lock); copro = aspeed_gpio_copro_request(gpio, offset); gpio->config->llops->reg_bit_set(gpio, offset, reg_dir, 0); if (copro) aspeed_gpio_copro_release(gpio, offset); - raw_spin_unlock_irqrestore(&gpio->lock, flags); - return 0; } @@ -465,13 +462,12 @@ static int aspeed_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val) { struct aspeed_gpio *gpio = gpiochip_get_data(gc); - unsigned long flags; bool copro = false; if (!have_output(gpio, offset)) return -ENOTSUPP; - raw_spin_lock_irqsave(&gpio->lock, flags); + guard(raw_spinlock_irqsave)(&gpio->lock); copro = aspeed_gpio_copro_request(gpio, offset); __aspeed_gpio_set(gc, offset, val); @@ -479,7 +475,6 @@ static int aspeed_gpio_dir_out(struct gpio_chip *gc, if (copro) aspeed_gpio_copro_release(gpio, offset); - raw_spin_unlock_irqrestore(&gpio->lock, flags); return 0; } @@ -487,7 +482,6 @@ static int aspeed_gpio_dir_out(struct gpio_chip *gc, static int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) { struct aspeed_gpio *gpio = gpiochip_get_data(gc); - unsigned long flags; u32 val; if (!have_input(gpio, offset)) @@ -496,12 +490,10 @@ static int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) if (!have_output(gpio, offset)) return GPIO_LINE_DIRECTION_IN; - raw_spin_lock_irqsave(&gpio->lock, flags); + guard(raw_spinlock_irqsave)(&gpio->lock); val = gpio->config->llops->reg_bit_get(gpio, offset, reg_dir); - raw_spin_unlock_irqrestore(&gpio->lock, flags); - return val ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; } @@ -527,7 +519,6 @@ static inline int irqd_to_aspeed_gpio_data(struct irq_data *d, static void aspeed_gpio_irq_ack(struct irq_data *d) { struct aspeed_gpio *gpio; - unsigned long flags; int rc, offset; bool copro = false; @@ -535,20 +526,19 @@ static void aspeed_gpio_irq_ack(struct irq_data *d) if (rc) return; - raw_spin_lock_irqsave(&gpio->lock, flags); + guard(raw_spinlock_irqsave)(&gpio->lock); + copro = aspeed_gpio_copro_request(gpio, offset); gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_status, 1); if (copro) aspeed_gpio_copro_release(gpio, offset); - raw_spin_unlock_irqrestore(&gpio->lock, flags); } static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set) { struct aspeed_gpio *gpio; - unsigned long flags; int rc, offset; bool copro = false; @@ -560,14 +550,14 @@ static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set) if (set) gpiochip_enable_irq(&gpio->chip, irqd_to_hwirq(d)); - raw_spin_lock_irqsave(&gpio->lock, flags); + guard(raw_spinlock_irqsave)(&gpio->lock); + copro = aspeed_gpio_copro_request(gpio, offset); gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_enable, set); if (copro) aspeed_gpio_copro_release(gpio, offset); - raw_spin_unlock_irqrestore(&gpio->lock, flags); /* Masking the IRQ */ if (!set) @@ -591,7 +581,6 @@ static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type) u32 type2 = 0; irq_flow_handler_t handler; struct aspeed_gpio *gpio; - unsigned long flags; int rc, offset; bool copro = false; @@ -620,16 +609,19 @@ static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type) return -EINVAL; } - raw_spin_lock_irqsave(&gpio->lock, flags); - copro = aspeed_gpio_copro_request(gpio, offset); + scoped_guard(raw_spinlock_irqsave, &gpio->lock) { + copro = aspeed_gpio_copro_request(gpio, offset); - gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_type0, type0); - gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_type1, type1); - gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_type2, type2); + gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_type0, + type0); + gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_type1, + type1); + gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_type2, + type2); - if (copro) - aspeed_gpio_copro_release(gpio, offset); - raw_spin_unlock_irqrestore(&gpio->lock, flags); + if (copro) + aspeed_gpio_copro_release(gpio, offset); + } irq_set_handler_locked(d, handler); @@ -686,17 +678,16 @@ static int aspeed_gpio_reset_tolerance(struct gpio_chip *chip, unsigned int offset, bool enable) { struct aspeed_gpio *gpio = gpiochip_get_data(chip); - unsigned long flags; bool copro = false; - raw_spin_lock_irqsave(&gpio->lock, flags); + guard(raw_spinlock_irqsave)(&gpio->lock); + copro = aspeed_gpio_copro_request(gpio, offset); gpio->config->llops->reg_bit_set(gpio, offset, reg_tolerance, enable); if (copro) aspeed_gpio_copro_release(gpio, offset); - raw_spin_unlock_irqrestore(&gpio->lock, flags); return 0; } @@ -798,7 +789,6 @@ static int enable_debounce(struct gpio_chip *chip, unsigned int offset, { struct aspeed_gpio *gpio = gpiochip_get_data(chip); u32 requested_cycles; - unsigned long flags; int rc; int i; @@ -812,12 +802,12 @@ static int enable_debounce(struct gpio_chip *chip, unsigned int offset, return rc; } - raw_spin_lock_irqsave(&gpio->lock, flags); + guard(raw_spinlock_irqsave)(&gpio->lock); if (timer_allocation_registered(gpio, offset)) { rc = unregister_allocated_timer(gpio, offset); if (rc < 0) - goto out; + return rc; } /* Try to find a timer already configured for the debounce period */ @@ -855,7 +845,7 @@ static int enable_debounce(struct gpio_chip *chip, unsigned int offset, * consistency. */ configure_timer(gpio, offset, 0); - goto out; + return rc; } i = j; @@ -863,34 +853,26 @@ static int enable_debounce(struct gpio_chip *chip, unsigned int offset, iowrite32(requested_cycles, gpio->base + gpio->config->debounce_timers_array[i]); } - if (WARN(i == 0, "Cannot register index of disabled timer\n")) { - rc = -EINVAL; - goto out; - } + if (WARN(i == 0, "Cannot register index of disabled timer\n")) + return -EINVAL; register_allocated_timer(gpio, offset, i); configure_timer(gpio, offset, i); -out: - raw_spin_unlock_irqrestore(&gpio->lock, flags); - return rc; } static int disable_debounce(struct gpio_chip *chip, unsigned int offset) { struct aspeed_gpio *gpio = gpiochip_get_data(chip); - unsigned long flags; int rc; - raw_spin_lock_irqsave(&gpio->lock, flags); + guard(raw_spinlock_irqsave)(&gpio->lock); rc = unregister_allocated_timer(gpio, offset); if (!rc) configure_timer(gpio, offset, 0); - raw_spin_unlock_irqrestore(&gpio->lock, flags); - return rc; } @@ -961,7 +943,6 @@ int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc, struct aspeed_gpio *gpio = gpiochip_get_data(chip); int rc = 0, bindex, offset = gpio_chip_hwgpio(desc); const struct aspeed_gpio_bank *bank = to_bank(offset); - unsigned long flags; if (!aspeed_gpio_support_copro(gpio)) return -EOPNOTSUPP; @@ -974,13 +955,12 @@ int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc, return -EINVAL; bindex = offset >> 3; - raw_spin_lock_irqsave(&gpio->lock, flags); + guard(raw_spinlock_irqsave)(&gpio->lock); /* Sanity check, this shouldn't happen */ - if (gpio->cf_copro_bankmap[bindex] == 0xff) { - rc = -EIO; - goto bail; - } + if (gpio->cf_copro_bankmap[bindex] == 0xff) + return -EIO; + gpio->cf_copro_bankmap[bindex]++; /* Switch command source */ @@ -994,8 +974,6 @@ int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc, *dreg_offset = bank->rdata_reg; if (bit) *bit = GPIO_OFFSET(offset); - bail: - raw_spin_unlock_irqrestore(&gpio->lock, flags); return rc; } EXPORT_SYMBOL_GPL(aspeed_gpio_copro_grab_gpio); @@ -1009,7 +987,6 @@ int aspeed_gpio_copro_release_gpio(struct gpio_desc *desc) struct gpio_chip *chip = gpiod_to_chip(desc); struct aspeed_gpio *gpio = gpiochip_get_data(chip); int rc = 0, bindex, offset = gpio_chip_hwgpio(desc); - unsigned long flags; if (!aspeed_gpio_support_copro(gpio)) return -EOPNOTSUPP; @@ -1021,21 +998,19 @@ int aspeed_gpio_copro_release_gpio(struct gpio_desc *desc) return -EINVAL; bindex = offset >> 3; - raw_spin_lock_irqsave(&gpio->lock, flags); + guard(raw_spinlock_irqsave)(&gpio->lock); /* Sanity check, this shouldn't happen */ - if (gpio->cf_copro_bankmap[bindex] == 0) { - rc = -EIO; - goto bail; - } + if (gpio->cf_copro_bankmap[bindex] == 0) + return -EIO; + gpio->cf_copro_bankmap[bindex]--; /* Switch command source */ if (gpio->cf_copro_bankmap[bindex] == 0) aspeed_gpio_change_cmd_source(gpio, offset, GPIO_CMDSRC_ARM); - bail: - raw_spin_unlock_irqrestore(&gpio->lock, flags); + return rc; } EXPORT_SYMBOL_GPL(aspeed_gpio_copro_release_gpio); From c72e61b51207f7a82e4efebf0cd21efe2352f363 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 3 Mar 2025 14:18:38 +0100 Subject: [PATCH 094/119] gpio: aspeed: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250303-gpiochip-set-conversion-v1-13-1d5cceeebf8b@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-aspeed.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index e2535aad1026..2d340a343a17 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -424,8 +424,7 @@ static void __aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset, gpio->config->llops->reg_bit_get(gpio, offset, reg_val); } -static void aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset, - int val) +static int aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) { struct aspeed_gpio *gpio = gpiochip_get_data(gc); bool copro = false; @@ -438,6 +437,8 @@ static void aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset, if (copro) aspeed_gpio_copro_release(gpio, offset); + + return 0; } static int aspeed_gpio_dir_in(struct gpio_chip *gc, unsigned int offset) @@ -1351,7 +1352,7 @@ static int aspeed_gpio_probe(struct platform_device *pdev) gpio->chip.request = aspeed_gpio_request; gpio->chip.free = aspeed_gpio_free; gpio->chip.get = aspeed_gpio_get; - gpio->chip.set = aspeed_gpio_set; + gpio->chip.set_rv = aspeed_gpio_set; gpio->chip.set_config = aspeed_gpio_set_config; gpio->chip.label = dev_name(&pdev->dev); gpio->chip.base = -1; From 952cf0938b38153909bb50f4ed37fe06a363f3b8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 3 Mar 2025 14:18:39 +0100 Subject: [PATCH 095/119] gpio: aspeed-sgpio: use lock guards Reduce the code complexity by using automatic lock guards with the raw spinlock. Link: https://lore.kernel.org/r/20250303-gpiochip-set-conversion-v1-14-1d5cceeebf8b@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-aspeed-sgpio.c | 76 ++++++++++++-------------------- 1 file changed, 29 insertions(+), 47 deletions(-) diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c index 34eb26298e32..5ce86de22563 100644 --- a/drivers/gpio/gpio-aspeed-sgpio.c +++ b/drivers/gpio/gpio-aspeed-sgpio.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -170,17 +171,14 @@ static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset) { struct aspeed_sgpio *gpio = gpiochip_get_data(gc); const struct aspeed_sgpio_bank *bank = to_bank(offset); - unsigned long flags; enum aspeed_sgpio_reg reg; int rc = 0; - raw_spin_lock_irqsave(&gpio->lock, flags); + guard(raw_spinlock_irqsave)(&gpio->lock); reg = aspeed_sgpio_is_input(offset) ? reg_val : reg_rdata; rc = !!(ioread32(bank_reg(gpio, bank, reg)) & GPIO_BIT(offset)); - raw_spin_unlock_irqrestore(&gpio->lock, flags); - return rc; } @@ -214,13 +212,10 @@ static int sgpio_set_value(struct gpio_chip *gc, unsigned int offset, int val) static void aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val) { struct aspeed_sgpio *gpio = gpiochip_get_data(gc); - unsigned long flags; - raw_spin_lock_irqsave(&gpio->lock, flags); + guard(raw_spinlock_irqsave)(&gpio->lock); sgpio_set_value(gc, offset, val); - - raw_spin_unlock_irqrestore(&gpio->lock, flags); } static int aspeed_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset) @@ -231,15 +226,14 @@ static int aspeed_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset) static int aspeed_sgpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val) { struct aspeed_sgpio *gpio = gpiochip_get_data(gc); - unsigned long flags; int rc; /* No special action is required for setting the direction; we'll * error-out in sgpio_set_value if this isn't an output GPIO */ - raw_spin_lock_irqsave(&gpio->lock, flags); + guard(raw_spinlock_irqsave)(&gpio->lock); + rc = sgpio_set_value(gc, offset, val); - raw_spin_unlock_irqrestore(&gpio->lock, flags); return rc; } @@ -269,7 +263,6 @@ static void aspeed_sgpio_irq_ack(struct irq_data *d) { const struct aspeed_sgpio_bank *bank; struct aspeed_sgpio *gpio; - unsigned long flags; void __iomem *status_addr; int offset; u32 bit; @@ -278,18 +271,15 @@ static void aspeed_sgpio_irq_ack(struct irq_data *d) status_addr = bank_reg(gpio, bank, reg_irq_status); - raw_spin_lock_irqsave(&gpio->lock, flags); + guard(raw_spinlock_irqsave)(&gpio->lock); iowrite32(bit, status_addr); - - raw_spin_unlock_irqrestore(&gpio->lock, flags); } static void aspeed_sgpio_irq_set_mask(struct irq_data *d, bool set) { const struct aspeed_sgpio_bank *bank; struct aspeed_sgpio *gpio; - unsigned long flags; u32 reg, bit; void __iomem *addr; int offset; @@ -301,17 +291,15 @@ static void aspeed_sgpio_irq_set_mask(struct irq_data *d, bool set) if (set) gpiochip_enable_irq(&gpio->chip, irqd_to_hwirq(d)); - raw_spin_lock_irqsave(&gpio->lock, flags); + scoped_guard(raw_spinlock_irqsave, &gpio->lock) { + reg = ioread32(addr); + if (set) + reg |= bit; + else + reg &= ~bit; - reg = ioread32(addr); - if (set) - reg |= bit; - else - reg &= ~bit; - - iowrite32(reg, addr); - - raw_spin_unlock_irqrestore(&gpio->lock, flags); + iowrite32(reg, addr); + } /* Masking the IRQ */ if (!set) @@ -339,7 +327,6 @@ static int aspeed_sgpio_set_type(struct irq_data *d, unsigned int type) const struct aspeed_sgpio_bank *bank; irq_flow_handler_t handler; struct aspeed_sgpio *gpio; - unsigned long flags; void __iomem *addr; int offset; @@ -366,24 +353,22 @@ static int aspeed_sgpio_set_type(struct irq_data *d, unsigned int type) return -EINVAL; } - raw_spin_lock_irqsave(&gpio->lock, flags); + scoped_guard(raw_spinlock_irqsave, &gpio->lock) { + addr = bank_reg(gpio, bank, reg_irq_type0); + reg = ioread32(addr); + reg = (reg & ~bit) | type0; + iowrite32(reg, addr); - addr = bank_reg(gpio, bank, reg_irq_type0); - reg = ioread32(addr); - reg = (reg & ~bit) | type0; - iowrite32(reg, addr); + addr = bank_reg(gpio, bank, reg_irq_type1); + reg = ioread32(addr); + reg = (reg & ~bit) | type1; + iowrite32(reg, addr); - addr = bank_reg(gpio, bank, reg_irq_type1); - reg = ioread32(addr); - reg = (reg & ~bit) | type1; - iowrite32(reg, addr); - - addr = bank_reg(gpio, bank, reg_irq_type2); - reg = ioread32(addr); - reg = (reg & ~bit) | type2; - iowrite32(reg, addr); - - raw_spin_unlock_irqrestore(&gpio->lock, flags); + addr = bank_reg(gpio, bank, reg_irq_type2); + reg = ioread32(addr); + reg = (reg & ~bit) | type2; + iowrite32(reg, addr); + } irq_set_handler_locked(d, handler); @@ -487,13 +472,12 @@ static int aspeed_sgpio_reset_tolerance(struct gpio_chip *chip, unsigned int offset, bool enable) { struct aspeed_sgpio *gpio = gpiochip_get_data(chip); - unsigned long flags; void __iomem *reg; u32 val; reg = bank_reg(gpio, to_bank(offset), reg_tolerance); - raw_spin_lock_irqsave(&gpio->lock, flags); + guard(raw_spinlock_irqsave)(&gpio->lock); val = readl(reg); @@ -504,8 +488,6 @@ static int aspeed_sgpio_reset_tolerance(struct gpio_chip *chip, writel(val, reg); - raw_spin_unlock_irqrestore(&gpio->lock, flags); - return 0; } From 460560100a2c993b46c84b659a9b09ba2238bb08 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 3 Mar 2025 14:18:40 +0100 Subject: [PATCH 096/119] gpio: aspeed-sgpio: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250303-gpiochip-set-conversion-v1-15-1d5cceeebf8b@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-aspeed-sgpio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c index 5ce86de22563..00b31497ecff 100644 --- a/drivers/gpio/gpio-aspeed-sgpio.c +++ b/drivers/gpio/gpio-aspeed-sgpio.c @@ -209,13 +209,13 @@ static int sgpio_set_value(struct gpio_chip *gc, unsigned int offset, int val) return 0; } -static void aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val) +static int aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val) { struct aspeed_sgpio *gpio = gpiochip_get_data(gc); guard(raw_spinlock_irqsave)(&gpio->lock); - sgpio_set_value(gc, offset, val); + return sgpio_set_value(gc, offset, val); } static int aspeed_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset) @@ -596,7 +596,7 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev) gpio->chip.request = NULL; gpio->chip.free = NULL; gpio->chip.get = aspeed_sgpio_get; - gpio->chip.set = aspeed_sgpio_set; + gpio->chip.set_rv = aspeed_sgpio_set; gpio->chip.set_config = aspeed_sgpio_set_config; gpio->chip.label = dev_name(&pdev->dev); gpio->chip.base = -1; From c7fe19ed39730c121449bdae11e030f02c7071a8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 6 Mar 2025 18:19:26 +0100 Subject: [PATCH 097/119] gpio: adnp: use lock guards for the I2C lock Reduce the code complexity by using automatic lock guards with the I2C mutex. Link: https://lore.kernel.org/r/20250306-gpiochip-set-conversion-v2-1-a76e72e21425@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-adnp.c | 122 ++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 73 deletions(-) diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index 9e752e98db9d..b911d60bf49b 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -3,6 +3,7 @@ * Copyright (C) 2011-2012 Avionic Design GmbH */ +#include #include #include #include @@ -102,9 +103,9 @@ static void adnp_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct adnp *adnp = gpiochip_get_data(chip); - mutex_lock(&adnp->i2c_lock); + guard(mutex)(&adnp->i2c_lock); + __adnp_gpio_set(adnp, offset, value); - mutex_unlock(&adnp->i2c_lock); } static int adnp_gpio_direction_input(struct gpio_chip *chip, unsigned offset) @@ -115,32 +116,26 @@ static int adnp_gpio_direction_input(struct gpio_chip *chip, unsigned offset) u8 value; int err; - mutex_lock(&adnp->i2c_lock); + guard(mutex)(&adnp->i2c_lock); err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &value); if (err < 0) - goto out; + return err; value &= ~BIT(pos); err = adnp_write(adnp, GPIO_DDR(adnp) + reg, value); if (err < 0) - goto out; + return err; err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &value); if (err < 0) - goto out; + return err; - if (value & BIT(pos)) { - err = -EPERM; - goto out; - } + if (value & BIT(pos)) + return -EPERM; - err = 0; - -out: - mutex_unlock(&adnp->i2c_lock); - return err; + return 0; } static int adnp_gpio_direction_output(struct gpio_chip *chip, unsigned offset, @@ -152,33 +147,28 @@ static int adnp_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int err; u8 val; - mutex_lock(&adnp->i2c_lock); + guard(mutex)(&adnp->i2c_lock); err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &val); if (err < 0) - goto out; + return err; val |= BIT(pos); err = adnp_write(adnp, GPIO_DDR(adnp) + reg, val); if (err < 0) - goto out; + return err; err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &val); if (err < 0) - goto out; + return err; - if (!(val & BIT(pos))) { - err = -EPERM; - goto out; - } + if (!(val & BIT(pos))) + return -EPERM; __adnp_gpio_set(adnp, offset, value); - err = 0; -out: - mutex_unlock(&adnp->i2c_lock); - return err; + return 0; } static void adnp_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) @@ -188,27 +178,26 @@ static void adnp_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) int err; for (i = 0; i < num_regs; i++) { - u8 ddr, plr, ier, isr; + u8 ddr = 0, plr = 0, ier = 0, isr = 0; - mutex_lock(&adnp->i2c_lock); + scoped_guard(mutex, &adnp->i2c_lock) { + err = adnp_read(adnp, GPIO_DDR(adnp) + i, &ddr); + if (err < 0) + return; - err = adnp_read(adnp, GPIO_DDR(adnp) + i, &ddr); - if (err < 0) - goto unlock; + err = adnp_read(adnp, GPIO_PLR(adnp) + i, &plr); + if (err < 0) + return; - err = adnp_read(adnp, GPIO_PLR(adnp) + i, &plr); - if (err < 0) - goto unlock; + err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier); + if (err < 0) + return; - err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier); - if (err < 0) - goto unlock; + err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr); + if (err < 0) + return; - err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr); - if (err < 0) - goto unlock; - - mutex_unlock(&adnp->i2c_lock); + } for (j = 0; j < 8; j++) { unsigned int bit = (i << adnp->reg_shift) + j; @@ -233,11 +222,6 @@ static void adnp_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) direction, level, interrupt, pending); } } - - return; - -unlock: - mutex_unlock(&adnp->i2c_lock); } static irqreturn_t adnp_irq(int irq, void *data) @@ -249,32 +233,24 @@ static irqreturn_t adnp_irq(int irq, void *data) for (i = 0; i < num_regs; i++) { unsigned int base = i << adnp->reg_shift, bit; - u8 changed, level, isr, ier; + u8 changed, level = 0, isr = 0, ier = 0; unsigned long pending; int err; - mutex_lock(&adnp->i2c_lock); + scoped_guard(mutex, &adnp->i2c_lock) { + err = adnp_read(adnp, GPIO_PLR(adnp) + i, &level); + if (err < 0) + continue; - err = adnp_read(adnp, GPIO_PLR(adnp) + i, &level); - if (err < 0) { - mutex_unlock(&adnp->i2c_lock); - continue; + err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr); + if (err < 0) + continue; + + err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier); + if (err < 0) + continue; } - err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr); - if (err < 0) { - mutex_unlock(&adnp->i2c_lock); - continue; - } - - err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier); - if (err < 0) { - mutex_unlock(&adnp->i2c_lock); - continue; - } - - mutex_unlock(&adnp->i2c_lock); - /* determine pins that changed levels */ changed = level ^ adnp->irq_level[i]; @@ -366,12 +342,12 @@ static void adnp_irq_bus_unlock(struct irq_data *d) struct adnp *adnp = gpiochip_get_data(gc); unsigned int num_regs = 1 << adnp->reg_shift, i; - mutex_lock(&adnp->i2c_lock); + scoped_guard(mutex, &adnp->i2c_lock) { + for (i = 0; i < num_regs; i++) + adnp_write(adnp, GPIO_IER(adnp) + i, + adnp->irq_enable[i]); + } - for (i = 0; i < num_regs; i++) - adnp_write(adnp, GPIO_IER(adnp) + i, adnp->irq_enable[i]); - - mutex_unlock(&adnp->i2c_lock); mutex_unlock(&adnp->irq_lock); } From 21c853ad93097619c7966542e838c54c37f57c90 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 6 Mar 2025 18:19:27 +0100 Subject: [PATCH 098/119] gpio: adnp: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250306-gpiochip-set-conversion-v2-2-a76e72e21425@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-adnp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index b911d60bf49b..dc2b941c3726 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -80,7 +80,7 @@ static int adnp_gpio_get(struct gpio_chip *chip, unsigned offset) return (value & BIT(pos)) ? 1 : 0; } -static void __adnp_gpio_set(struct adnp *adnp, unsigned offset, int value) +static int __adnp_gpio_set(struct adnp *adnp, unsigned int offset, int value) { unsigned int reg = offset >> adnp->reg_shift; unsigned int pos = offset & 7; @@ -89,23 +89,23 @@ static void __adnp_gpio_set(struct adnp *adnp, unsigned offset, int value) err = adnp_read(adnp, GPIO_PLR(adnp) + reg, &val); if (err < 0) - return; + return err; if (value) val |= BIT(pos); else val &= ~BIT(pos); - adnp_write(adnp, GPIO_PLR(adnp) + reg, val); + return adnp_write(adnp, GPIO_PLR(adnp) + reg, val); } -static void adnp_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int adnp_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct adnp *adnp = gpiochip_get_data(chip); guard(mutex)(&adnp->i2c_lock); - __adnp_gpio_set(adnp, offset, value); + return __adnp_gpio_set(adnp, offset, value); } static int adnp_gpio_direction_input(struct gpio_chip *chip, unsigned offset) @@ -430,7 +430,7 @@ static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios, chip->direction_input = adnp_gpio_direction_input; chip->direction_output = adnp_gpio_direction_output; chip->get = adnp_gpio_get; - chip->set = adnp_gpio_set; + chip->set_rv = adnp_gpio_set; chip->can_sleep = true; if (IS_ENABLED(CONFIG_DEBUG_FS)) From cd7d117a297149b61871d441fa1a8146c55c435d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Mar 2025 13:40:15 +0100 Subject: [PATCH 099/119] gpio: bcm-kona: use lock guards Reduce the code complexity by using automatic lock guards with the raw spinlock. Link: https://lore.kernel.org/r/20250310-gpiochip-set-conversion-v1-1-03798bb833eb@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-bcm-kona.c | 64 ++++++++++-------------------------- 1 file changed, 18 insertions(+), 46 deletions(-) diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index 64908f1a5e7f..a7390b1f1173 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -100,7 +101,6 @@ static void bcm_kona_gpio_lock_gpio(struct bcm_kona_gpio *kona_gpio, unsigned gpio) { u32 val; - unsigned long flags; int bank_id = GPIO_BANK(gpio); int bit = GPIO_BIT(gpio); struct bcm_kona_gpio_bank *bank = &kona_gpio->banks[bank_id]; @@ -112,13 +112,11 @@ static void bcm_kona_gpio_lock_gpio(struct bcm_kona_gpio *kona_gpio, } if (--bank->gpio_unlock_count[bit] == 0) { - raw_spin_lock_irqsave(&kona_gpio->lock, flags); + guard(raw_spinlock_irqsave)(&kona_gpio->lock); val = readl(kona_gpio->reg_base + GPIO_PWD_STATUS(bank_id)); val |= BIT(bit); bcm_kona_gpio_write_lock_regs(kona_gpio->reg_base, bank_id, val); - - raw_spin_unlock_irqrestore(&kona_gpio->lock, flags); } } @@ -126,19 +124,16 @@ static void bcm_kona_gpio_unlock_gpio(struct bcm_kona_gpio *kona_gpio, unsigned gpio) { u32 val; - unsigned long flags; int bank_id = GPIO_BANK(gpio); int bit = GPIO_BIT(gpio); struct bcm_kona_gpio_bank *bank = &kona_gpio->banks[bank_id]; if (bank->gpio_unlock_count[bit] == 0) { - raw_spin_lock_irqsave(&kona_gpio->lock, flags); + guard(raw_spinlock_irqsave)(&kona_gpio->lock); val = readl(kona_gpio->reg_base + GPIO_PWD_STATUS(bank_id)); val &= ~BIT(bit); bcm_kona_gpio_write_lock_regs(kona_gpio->reg_base, bank_id, val); - - raw_spin_unlock_irqrestore(&kona_gpio->lock, flags); } ++bank->gpio_unlock_count[bit]; @@ -161,24 +156,21 @@ static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value) int bank_id = GPIO_BANK(gpio); int bit = GPIO_BIT(gpio); u32 val, reg_offset; - unsigned long flags; kona_gpio = gpiochip_get_data(chip); reg_base = kona_gpio->reg_base; - raw_spin_lock_irqsave(&kona_gpio->lock, flags); + + guard(raw_spinlock_irqsave)(&kona_gpio->lock); /* this function only applies to output pin */ if (bcm_kona_gpio_get_dir(chip, gpio) == GPIO_LINE_DIRECTION_IN) - goto out; + return; reg_offset = value ? GPIO_OUT_SET(bank_id) : GPIO_OUT_CLEAR(bank_id); val = readl(reg_base + reg_offset); val |= BIT(bit); writel(val, reg_base + reg_offset); - -out: - raw_spin_unlock_irqrestore(&kona_gpio->lock, flags); } static int bcm_kona_gpio_get(struct gpio_chip *chip, unsigned gpio) @@ -188,11 +180,11 @@ static int bcm_kona_gpio_get(struct gpio_chip *chip, unsigned gpio) int bank_id = GPIO_BANK(gpio); int bit = GPIO_BIT(gpio); u32 val, reg_offset; - unsigned long flags; kona_gpio = gpiochip_get_data(chip); reg_base = kona_gpio->reg_base; - raw_spin_lock_irqsave(&kona_gpio->lock, flags); + + guard(raw_spinlock_irqsave)(&kona_gpio->lock); if (bcm_kona_gpio_get_dir(chip, gpio) == GPIO_LINE_DIRECTION_IN) reg_offset = GPIO_IN_STATUS(bank_id); @@ -202,8 +194,6 @@ static int bcm_kona_gpio_get(struct gpio_chip *chip, unsigned gpio) /* read the GPIO bank status */ val = readl(reg_base + reg_offset); - raw_spin_unlock_irqrestore(&kona_gpio->lock, flags); - /* return the specified bit status */ return !!(val & BIT(bit)); } @@ -228,19 +218,17 @@ static int bcm_kona_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) struct bcm_kona_gpio *kona_gpio; void __iomem *reg_base; u32 val; - unsigned long flags; kona_gpio = gpiochip_get_data(chip); reg_base = kona_gpio->reg_base; - raw_spin_lock_irqsave(&kona_gpio->lock, flags); + + guard(raw_spinlock_irqsave)(&kona_gpio->lock); val = readl(reg_base + GPIO_CONTROL(gpio)); val &= ~GPIO_GPCTR0_IOTR_MASK; val |= GPIO_GPCTR0_IOTR_CMD_INPUT; writel(val, reg_base + GPIO_CONTROL(gpio)); - raw_spin_unlock_irqrestore(&kona_gpio->lock, flags); - return 0; } @@ -252,11 +240,11 @@ static int bcm_kona_gpio_direction_output(struct gpio_chip *chip, int bank_id = GPIO_BANK(gpio); int bit = GPIO_BIT(gpio); u32 val, reg_offset; - unsigned long flags; kona_gpio = gpiochip_get_data(chip); reg_base = kona_gpio->reg_base; - raw_spin_lock_irqsave(&kona_gpio->lock, flags); + + guard(raw_spinlock_irqsave)(&kona_gpio->lock); val = readl(reg_base + GPIO_CONTROL(gpio)); val &= ~GPIO_GPCTR0_IOTR_MASK; @@ -268,8 +256,6 @@ static int bcm_kona_gpio_direction_output(struct gpio_chip *chip, val |= BIT(bit); writel(val, reg_base + reg_offset); - raw_spin_unlock_irqrestore(&kona_gpio->lock, flags); - return 0; } @@ -289,7 +275,6 @@ static int bcm_kona_gpio_set_debounce(struct gpio_chip *chip, unsigned gpio, struct bcm_kona_gpio *kona_gpio; void __iomem *reg_base; u32 val, res; - unsigned long flags; kona_gpio = gpiochip_get_data(chip); reg_base = kona_gpio->reg_base; @@ -312,7 +297,7 @@ static int bcm_kona_gpio_set_debounce(struct gpio_chip *chip, unsigned gpio, } /* spin lock for read-modify-write of the GPIO register */ - raw_spin_lock_irqsave(&kona_gpio->lock, flags); + guard(raw_spinlock_irqsave)(&kona_gpio->lock); val = readl(reg_base + GPIO_CONTROL(gpio)); val &= ~GPIO_GPCTR0_DBR_MASK; @@ -327,8 +312,6 @@ static int bcm_kona_gpio_set_debounce(struct gpio_chip *chip, unsigned gpio, writel(val, reg_base + GPIO_CONTROL(gpio)); - raw_spin_unlock_irqrestore(&kona_gpio->lock, flags); - return 0; } @@ -367,17 +350,15 @@ static void bcm_kona_gpio_irq_ack(struct irq_data *d) int bank_id = GPIO_BANK(gpio); int bit = GPIO_BIT(gpio); u32 val; - unsigned long flags; kona_gpio = irq_data_get_irq_chip_data(d); reg_base = kona_gpio->reg_base; - raw_spin_lock_irqsave(&kona_gpio->lock, flags); + + guard(raw_spinlock_irqsave)(&kona_gpio->lock); val = readl(reg_base + GPIO_INT_STATUS(bank_id)); val |= BIT(bit); writel(val, reg_base + GPIO_INT_STATUS(bank_id)); - - raw_spin_unlock_irqrestore(&kona_gpio->lock, flags); } static void bcm_kona_gpio_irq_mask(struct irq_data *d) @@ -388,19 +369,16 @@ static void bcm_kona_gpio_irq_mask(struct irq_data *d) int bank_id = GPIO_BANK(gpio); int bit = GPIO_BIT(gpio); u32 val; - unsigned long flags; kona_gpio = irq_data_get_irq_chip_data(d); reg_base = kona_gpio->reg_base; - raw_spin_lock_irqsave(&kona_gpio->lock, flags); + guard(raw_spinlock_irqsave)(&kona_gpio->lock); val = readl(reg_base + GPIO_INT_MASK(bank_id)); val |= BIT(bit); writel(val, reg_base + GPIO_INT_MASK(bank_id)); gpiochip_disable_irq(&kona_gpio->gpio_chip, gpio); - - raw_spin_unlock_irqrestore(&kona_gpio->lock, flags); } static void bcm_kona_gpio_irq_unmask(struct irq_data *d) @@ -411,19 +389,16 @@ static void bcm_kona_gpio_irq_unmask(struct irq_data *d) int bank_id = GPIO_BANK(gpio); int bit = GPIO_BIT(gpio); u32 val; - unsigned long flags; kona_gpio = irq_data_get_irq_chip_data(d); reg_base = kona_gpio->reg_base; - raw_spin_lock_irqsave(&kona_gpio->lock, flags); + guard(raw_spinlock_irqsave)(&kona_gpio->lock); val = readl(reg_base + GPIO_INT_MSKCLR(bank_id)); val |= BIT(bit); writel(val, reg_base + GPIO_INT_MSKCLR(bank_id)); gpiochip_enable_irq(&kona_gpio->gpio_chip, gpio); - - raw_spin_unlock_irqrestore(&kona_gpio->lock, flags); } static int bcm_kona_gpio_irq_set_type(struct irq_data *d, unsigned int type) @@ -433,7 +408,6 @@ static int bcm_kona_gpio_irq_set_type(struct irq_data *d, unsigned int type) unsigned gpio = d->hwirq; u32 lvl_type; u32 val; - unsigned long flags; kona_gpio = irq_data_get_irq_chip_data(d); reg_base = kona_gpio->reg_base; @@ -459,15 +433,13 @@ static int bcm_kona_gpio_irq_set_type(struct irq_data *d, unsigned int type) return -EINVAL; } - raw_spin_lock_irqsave(&kona_gpio->lock, flags); + guard(raw_spinlock_irqsave)(&kona_gpio->lock); val = readl(reg_base + GPIO_CONTROL(gpio)); val &= ~GPIO_GPCTR0_ITR_MASK; val |= lvl_type << GPIO_GPCTR0_ITR_SHIFT; writel(val, reg_base + GPIO_CONTROL(gpio)); - raw_spin_unlock_irqrestore(&kona_gpio->lock, flags); - return 0; } From d5cc72803b146c811b01f9a5b91e97337adf5784 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Mar 2025 13:40:16 +0100 Subject: [PATCH 100/119] gpio: bcm-kona: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250310-gpiochip-set-conversion-v1-2-03798bb833eb@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-bcm-kona.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index a7390b1f1173..17c287dc7471 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -149,7 +149,8 @@ static int bcm_kona_gpio_get_dir(struct gpio_chip *chip, unsigned gpio) return val ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; } -static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value) +static int bcm_kona_gpio_set(struct gpio_chip *chip, unsigned int gpio, + int value) { struct bcm_kona_gpio *kona_gpio; void __iomem *reg_base; @@ -164,13 +165,15 @@ static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value) /* this function only applies to output pin */ if (bcm_kona_gpio_get_dir(chip, gpio) == GPIO_LINE_DIRECTION_IN) - return; + return 0; reg_offset = value ? GPIO_OUT_SET(bank_id) : GPIO_OUT_CLEAR(bank_id); val = readl(reg_base + reg_offset); val |= BIT(bit); writel(val, reg_base + reg_offset); + + return 0; } static int bcm_kona_gpio_get(struct gpio_chip *chip, unsigned gpio) @@ -336,7 +339,7 @@ static const struct gpio_chip template_chip = { .direction_input = bcm_kona_gpio_direction_input, .get = bcm_kona_gpio_get, .direction_output = bcm_kona_gpio_direction_output, - .set = bcm_kona_gpio_set, + .set_rv = bcm_kona_gpio_set, .set_config = bcm_kona_gpio_set_config, .to_irq = bcm_kona_gpio_to_irq, .base = 0, From 7bd2bb7901a67f6b14c913596acb471351f500a5 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Mar 2025 13:40:17 +0100 Subject: [PATCH 101/119] gpio: bd71815: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Matti Vaittinen Link: https://lore.kernel.org/r/20250310-gpiochip-set-conversion-v1-3-03798bb833eb@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-bd71815.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/gpio/gpio-bd71815.c b/drivers/gpio/gpio-bd71815.c index 08ff2857256f..36701500925e 100644 --- a/drivers/gpio/gpio-bd71815.c +++ b/drivers/gpio/gpio-bd71815.c @@ -37,21 +37,18 @@ static int bd71815gpo_get(struct gpio_chip *chip, unsigned int offset) return (val >> offset) & 1; } -static void bd71815gpo_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int bd71815gpo_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct bd71815_gpio *bd71815 = gpiochip_get_data(chip); - int ret, bit; + int bit; bit = BIT(offset); if (value) - ret = regmap_set_bits(bd71815->regmap, BD71815_REG_GPO, bit); - else - ret = regmap_clear_bits(bd71815->regmap, BD71815_REG_GPO, bit); + return regmap_set_bits(bd71815->regmap, BD71815_REG_GPO, bit); - if (ret) - dev_warn(bd71815->dev, "failed to toggle GPO\n"); + return regmap_clear_bits(bd71815->regmap, BD71815_REG_GPO, bit); } static int bd71815_gpio_set_config(struct gpio_chip *chip, unsigned int offset, @@ -88,7 +85,7 @@ static const struct gpio_chip bd71815gpo_chip = { .owner = THIS_MODULE, .get = bd71815gpo_get, .get_direction = bd71815gpo_direction_get, - .set = bd71815gpo_set, + .set_rv = bd71815gpo_set, .set_config = bd71815_gpio_set_config, .can_sleep = true, }; From 8a050f738d41d337fb73ef582916328dc5bd73e1 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Mar 2025 13:40:18 +0100 Subject: [PATCH 102/119] gpio: bd71828: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Matti Vaittinen Link: https://lore.kernel.org/r/20250310-gpiochip-set-conversion-v1-4-03798bb833eb@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-bd71828.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/gpio/gpio-bd71828.c b/drivers/gpio/gpio-bd71828.c index b2ccc320c7b5..4ba151e5cf25 100644 --- a/drivers/gpio/gpio-bd71828.c +++ b/drivers/gpio/gpio-bd71828.c @@ -16,10 +16,9 @@ struct bd71828_gpio { struct gpio_chip gpio; }; -static void bd71828_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int bd71828_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { - int ret; struct bd71828_gpio *bdgpio = gpiochip_get_data(chip); u8 val = (value) ? BD71828_GPIO_OUT_HI : BD71828_GPIO_OUT_LO; @@ -28,12 +27,10 @@ static void bd71828_gpio_set(struct gpio_chip *chip, unsigned int offset, * we are dealing with - then we are done */ if (offset == HALL_GPIO_OFFSET) - return; + return 0; - ret = regmap_update_bits(bdgpio->regmap, GPIO_OUT_REG(offset), - BD71828_GPIO_OUT_MASK, val); - if (ret) - dev_err(bdgpio->dev, "Could not set gpio to %d\n", value); + return regmap_update_bits(bdgpio->regmap, GPIO_OUT_REG(offset), + BD71828_GPIO_OUT_MASK, val); } static int bd71828_gpio_get(struct gpio_chip *chip, unsigned int offset) @@ -112,7 +109,7 @@ static int bd71828_probe(struct platform_device *pdev) bdgpio->gpio.set_config = bd71828_gpio_set_config; bdgpio->gpio.can_sleep = true; bdgpio->gpio.get = bd71828_gpio_get; - bdgpio->gpio.set = bd71828_gpio_set; + bdgpio->gpio.set_rv = bd71828_gpio_set; bdgpio->gpio.base = -1; /* From fe7667f2085ee7d66a6a34e6bf6830be7e641b52 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Mar 2025 13:40:19 +0100 Subject: [PATCH 103/119] gpio: bd9571mwv: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Matti Vaittinen Link: https://lore.kernel.org/r/20250310-gpiochip-set-conversion-v1-5-03798bb833eb@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-bd9571mwv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-bd9571mwv.c b/drivers/gpio/gpio-bd9571mwv.c index 9a4d55f703bb..8df1361e3e84 100644 --- a/drivers/gpio/gpio-bd9571mwv.c +++ b/drivers/gpio/gpio-bd9571mwv.c @@ -72,13 +72,13 @@ static int bd9571mwv_gpio_get(struct gpio_chip *chip, unsigned int offset) return val & BIT(offset); } -static void bd9571mwv_gpio_set(struct gpio_chip *chip, unsigned int offset, +static int bd9571mwv_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct bd9571mwv_gpio *gpio = gpiochip_get_data(chip); - regmap_update_bits(gpio->regmap, BD9571MWV_GPIO_OUT, - BIT(offset), value ? BIT(offset) : 0); + return regmap_update_bits(gpio->regmap, BD9571MWV_GPIO_OUT, + BIT(offset), value ? BIT(offset) : 0); } static const struct gpio_chip template_chip = { @@ -88,7 +88,7 @@ static const struct gpio_chip template_chip = { .direction_input = bd9571mwv_gpio_direction_input, .direction_output = bd9571mwv_gpio_direction_output, .get = bd9571mwv_gpio_get, - .set = bd9571mwv_gpio_set, + .set_rv = bd9571mwv_gpio_set, .base = -1, .ngpio = 2, .can_sleep = true, From c948feeadba290e989b049913576b3d30ba02235 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Mar 2025 13:40:20 +0100 Subject: [PATCH 104/119] gpio: bt8xx: allow to build the module with COMPILE_TEST=y Link: https://lore.kernel.org/r/20250310-gpiochip-set-conversion-v1-6-03798bb833eb@linaro.org 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 3e9b174fee84..f2c39bbff83a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1671,7 +1671,7 @@ config GPIO_AMD8111 config GPIO_BT8XX tristate "BT8XX GPIO abuser" - depends on VIDEO_BT848=n + depends on VIDEO_BT848=n || COMPILE_TEST help The BT8xx frame grabber chip has 24 GPIO pins that can be abused as a cheap PCI GPIO card. From b9a557d05a7dde42b1e3652751eea6c06091402e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Mar 2025 13:40:21 +0100 Subject: [PATCH 105/119] gpio: bt8xx: use lock guards Reduce the code complexity by using automatic lock guards with the spinlock. Link: https://lore.kernel.org/r/20250310-gpiochip-set-conversion-v1-7-03798bb833eb@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-bt8xx.c | 43 +++++++++++++-------------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c index 7920cf256798..173da7bbfc98 100644 --- a/drivers/gpio/gpio-bt8xx.c +++ b/drivers/gpio/gpio-bt8xx.c @@ -31,6 +31,7 @@ */ +#include #include #include #include @@ -69,10 +70,9 @@ MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the static int bt8xxgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) { struct bt8xxgpio *bg = gpiochip_get_data(gpio); - unsigned long flags; u32 outen, data; - spin_lock_irqsave(&bg->lock, flags); + guard(spinlock_irqsave)(&bg->lock); data = bgread(BT848_GPIO_DATA); data &= ~(1 << nr); @@ -82,20 +82,17 @@ static int bt8xxgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) outen &= ~(1 << nr); bgwrite(outen, BT848_GPIO_OUT_EN); - spin_unlock_irqrestore(&bg->lock, flags); - return 0; } static int bt8xxgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) { struct bt8xxgpio *bg = gpiochip_get_data(gpio); - unsigned long flags; u32 val; - spin_lock_irqsave(&bg->lock, flags); + guard(spinlock_irqsave)(&bg->lock); + val = bgread(BT848_GPIO_DATA); - spin_unlock_irqrestore(&bg->lock, flags); return !!(val & (1 << nr)); } @@ -104,10 +101,9 @@ static int bt8xxgpio_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, int val) { struct bt8xxgpio *bg = gpiochip_get_data(gpio); - unsigned long flags; u32 outen, data; - spin_lock_irqsave(&bg->lock, flags); + guard(spinlock_irqsave)(&bg->lock); outen = bgread(BT848_GPIO_OUT_EN); outen |= (1 << nr); @@ -120,8 +116,6 @@ static int bt8xxgpio_gpio_direction_output(struct gpio_chip *gpio, data &= ~(1 << nr); bgwrite(data, BT848_GPIO_DATA); - spin_unlock_irqrestore(&bg->lock, flags); - return 0; } @@ -129,10 +123,9 @@ static void bt8xxgpio_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) { struct bt8xxgpio *bg = gpiochip_get_data(gpio); - unsigned long flags; u32 data; - spin_lock_irqsave(&bg->lock, flags); + guard(spinlock_irqsave)(&bg->lock); data = bgread(BT848_GPIO_DATA); if (val) @@ -140,8 +133,6 @@ static void bt8xxgpio_gpio_set(struct gpio_chip *gpio, else data &= ~(1 << nr); bgwrite(data, BT848_GPIO_DATA); - - spin_unlock_irqrestore(&bg->lock, flags); } static void bt8xxgpio_gpio_setup(struct bt8xxgpio *bg) @@ -236,18 +227,15 @@ static void bt8xxgpio_remove(struct pci_dev *pdev) static int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state) { struct bt8xxgpio *bg = pci_get_drvdata(pdev); - unsigned long flags; - spin_lock_irqsave(&bg->lock, flags); + scoped_guard(spinlock_irqsave, &bg->lock) { + bg->saved_outen = bgread(BT848_GPIO_OUT_EN); + bg->saved_data = bgread(BT848_GPIO_DATA); - bg->saved_outen = bgread(BT848_GPIO_OUT_EN); - bg->saved_data = bgread(BT848_GPIO_DATA); - - bgwrite(0, BT848_INT_MASK); - bgwrite(~0x0, BT848_INT_STAT); - bgwrite(0x0, BT848_GPIO_OUT_EN); - - spin_unlock_irqrestore(&bg->lock, flags); + bgwrite(0, BT848_INT_MASK); + bgwrite(~0x0, BT848_INT_STAT); + bgwrite(0x0, BT848_GPIO_OUT_EN); + } pci_save_state(pdev); pci_disable_device(pdev); @@ -259,7 +247,6 @@ static int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state) static int bt8xxgpio_resume(struct pci_dev *pdev) { struct bt8xxgpio *bg = pci_get_drvdata(pdev); - unsigned long flags; int err; pci_set_power_state(pdev, PCI_D0); @@ -268,7 +255,7 @@ static int bt8xxgpio_resume(struct pci_dev *pdev) return err; pci_restore_state(pdev); - spin_lock_irqsave(&bg->lock, flags); + guard(spinlock_irqsave)(&bg->lock); bgwrite(0, BT848_INT_MASK); bgwrite(0, BT848_GPIO_DMA_CTL); @@ -277,8 +264,6 @@ static int bt8xxgpio_resume(struct pci_dev *pdev) bgwrite(bg->saved_data & bg->saved_outen, BT848_GPIO_DATA); - spin_unlock_irqrestore(&bg->lock, flags); - return 0; } #else From 19c39c53752ae0b5cbf7577bcdf7c13d1c146e65 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Mar 2025 13:40:22 +0100 Subject: [PATCH 106/119] gpio: bt8xx: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250310-gpiochip-set-conversion-v1-8-03798bb833eb@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-bt8xx.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c index 173da7bbfc98..7c9e81fea37a 100644 --- a/drivers/gpio/gpio-bt8xx.c +++ b/drivers/gpio/gpio-bt8xx.c @@ -119,8 +119,7 @@ static int bt8xxgpio_gpio_direction_output(struct gpio_chip *gpio, return 0; } -static void bt8xxgpio_gpio_set(struct gpio_chip *gpio, - unsigned nr, int val) +static int bt8xxgpio_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) { struct bt8xxgpio *bg = gpiochip_get_data(gpio); u32 data; @@ -133,6 +132,8 @@ static void bt8xxgpio_gpio_set(struct gpio_chip *gpio, else data &= ~(1 << nr); bgwrite(data, BT848_GPIO_DATA); + + return 0; } static void bt8xxgpio_gpio_setup(struct bt8xxgpio *bg) @@ -144,7 +145,7 @@ static void bt8xxgpio_gpio_setup(struct bt8xxgpio *bg) c->direction_input = bt8xxgpio_gpio_direction_input; c->get = bt8xxgpio_gpio_get; c->direction_output = bt8xxgpio_gpio_direction_output; - c->set = bt8xxgpio_gpio_set; + c->set_rv = bt8xxgpio_gpio_set; c->dbg_show = NULL; c->base = modparam_gpiobase; c->ngpio = BT8XXGPIO_NR_GPIOS; From 1e69c7532a188a84b4cb535944fd7d60393a1fc8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Mar 2025 13:40:23 +0100 Subject: [PATCH 107/119] gpio: cgbc: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250310-gpiochip-set-conversion-v1-9-03798bb833eb@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-cgbc.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/gpio/gpio-cgbc.c b/drivers/gpio/gpio-cgbc.c index 9213faa11522..1495bec62456 100644 --- a/drivers/gpio/gpio-cgbc.c +++ b/drivers/gpio/gpio-cgbc.c @@ -51,8 +51,8 @@ static int cgbc_gpio_get(struct gpio_chip *chip, unsigned int offset) return (int)(val & (u8)BIT(offset)); } -static void __cgbc_gpio_set(struct gpio_chip *chip, - unsigned int offset, int value) +static int __cgbc_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); struct cgbc_device_data *cgbc = gpio->cgbc; @@ -61,23 +61,23 @@ static void __cgbc_gpio_set(struct gpio_chip *chip, ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_GET, (offset > 7) ? 1 : 0, 0, &val); if (ret) - return; + return ret; if (value) val |= BIT(offset % 8); else val &= ~(BIT(offset % 8)); - cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_SET, (offset > 7) ? 1 : 0, val, &val); + return cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_SET, (offset > 7) ? 1 : 0, val, &val); } -static void cgbc_gpio_set(struct gpio_chip *chip, - unsigned int offset, int value) +static int cgbc_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); - scoped_guard(mutex, &gpio->lock) - __cgbc_gpio_set(chip, offset, value); + guard(mutex)(&gpio->lock); + + return __cgbc_gpio_set(chip, offset, value); } static int cgbc_gpio_direction_set(struct gpio_chip *chip, @@ -116,10 +116,14 @@ static int cgbc_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); + int ret; guard(mutex)(&gpio->lock); - __cgbc_gpio_set(chip, offset, value); + ret = __cgbc_gpio_set(chip, offset, value); + if (ret) + return ret; + return cgbc_gpio_direction_set(chip, offset, GPIO_LINE_DIRECTION_OUT); } @@ -167,7 +171,7 @@ static int cgbc_gpio_probe(struct platform_device *pdev) chip->direction_output = cgbc_gpio_direction_output; chip->get_direction = cgbc_gpio_get_direction; chip->get = cgbc_gpio_get; - chip->set = cgbc_gpio_set; + chip->set_rv = cgbc_gpio_set; chip->ngpio = CGBC_GPIO_NGPIO; ret = devm_mutex_init(dev, &gpio->lock); From 68f5b74e0db7ab57885ad4ec05f7418cac8f4063 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Mar 2025 13:40:24 +0100 Subject: [PATCH 108/119] gpio: creg-snps: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250310-gpiochip-set-conversion-v1-10-03798bb833eb@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-creg-snps.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-creg-snps.c b/drivers/gpio/gpio-creg-snps.c index 4968232f70f2..8b49f02c7896 100644 --- a/drivers/gpio/gpio-creg-snps.c +++ b/drivers/gpio/gpio-creg-snps.c @@ -27,7 +27,7 @@ struct creg_gpio { const struct creg_layout *layout; }; -static void creg_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) +static int creg_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) { struct creg_gpio *hcg = gpiochip_get_data(gc); const struct creg_layout *layout = hcg->layout; @@ -47,13 +47,13 @@ static void creg_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) reg |= (value << reg_shift); writel(reg, hcg->regs); spin_unlock_irqrestore(&hcg->lock, flags); + + return 0; } static int creg_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val) { - creg_gpio_set(gc, offset, val); - - return 0; + return creg_gpio_set(gc, offset, val); } static int creg_gpio_validate_pg(struct device *dev, struct creg_gpio *hcg, @@ -167,7 +167,7 @@ static int creg_gpio_probe(struct platform_device *pdev) hcg->gc.label = dev_name(dev); hcg->gc.base = -1; hcg->gc.ngpio = ngpios; - hcg->gc.set = creg_gpio_set; + hcg->gc.set_rv = creg_gpio_set; hcg->gc.direction_output = creg_gpio_dir_out; ret = devm_gpiochip_add_data(dev, &hcg->gc, hcg); From 2661dc2de18617ac827aa9b50cb145bf5a185896 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Mar 2025 13:40:25 +0100 Subject: [PATCH 109/119] gpio: cros-ec: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20250310-gpiochip-set-conversion-v1-11-03798bb833eb@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-cros-ec.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-cros-ec.c b/drivers/gpio/gpio-cros-ec.c index 0c09bb54dc0c..53cd5ff6247b 100644 --- a/drivers/gpio/gpio-cros-ec.c +++ b/drivers/gpio/gpio-cros-ec.c @@ -24,24 +24,21 @@ static const char cros_ec_gpio_prefix[] = "EC:"; /* Setting gpios is only supported when the system is unlocked */ -static void cros_ec_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +static int cros_ec_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) { const char *name = gc->names[gpio] + strlen(cros_ec_gpio_prefix); struct cros_ec_device *cros_ec = gpiochip_get_data(gc); struct ec_params_gpio_set params = { .val = val, }; - int ret; ssize_t copied; copied = strscpy(params.name, name, sizeof(params.name)); if (copied < 0) - return; + return copied; - ret = cros_ec_cmd(cros_ec, 0, EC_CMD_GPIO_SET, ¶ms, - sizeof(params), NULL, 0); - if (ret < 0) - dev_err(gc->parent, "error setting gpio%d (%s) on EC: %d\n", gpio, name, ret); + return cros_ec_cmd(cros_ec, 0, EC_CMD_GPIO_SET, ¶ms, + sizeof(params), NULL, 0); } static int cros_ec_gpio_get(struct gpio_chip *gc, unsigned int gpio) @@ -191,7 +188,7 @@ static int cros_ec_gpio_probe(struct platform_device *pdev) gc->can_sleep = true; gc->label = dev_name(dev); gc->base = -1; - gc->set = cros_ec_gpio_set; + gc->set_rv = cros_ec_gpio_set; gc->get = cros_ec_gpio_get; gc->get_direction = cros_ec_gpio_get_direction; From 96498b83b3ded5f01207775d681374d62111d548 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Mar 2025 13:40:26 +0100 Subject: [PATCH 110/119] gpio: crystalcove: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250310-gpiochip-set-conversion-v1-12-03798bb833eb@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-crystalcove.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 56effd0f50c7..8db7cca3a060 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -168,18 +168,18 @@ static int crystalcove_gpio_get(struct gpio_chip *chip, unsigned int gpio) return val & 0x1; } -static void crystalcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) +static int crystalcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) { struct crystalcove_gpio *cg = gpiochip_get_data(chip); int reg = to_reg(gpio, CTRL_OUT); if (reg < 0) - return; + return 0; if (value) - regmap_update_bits(cg->regmap, reg, 1, 1); - else - regmap_update_bits(cg->regmap, reg, 1, 0); + return regmap_update_bits(cg->regmap, reg, 1, 1); + + return regmap_update_bits(cg->regmap, reg, 1, 0); } static int crystalcove_irq_type(struct irq_data *data, unsigned int type) @@ -349,7 +349,7 @@ static int crystalcove_gpio_probe(struct platform_device *pdev) cg->chip.direction_input = crystalcove_gpio_dir_in; cg->chip.direction_output = crystalcove_gpio_dir_out; cg->chip.get = crystalcove_gpio_get; - cg->chip.set = crystalcove_gpio_set; + cg->chip.set_rv = crystalcove_gpio_set; cg->chip.base = -1; cg->chip.ngpio = CRYSTALCOVE_VGPIO_NUM; cg->chip.can_sleep = true; From 588dfcdb162855b954f92fce73a12e3fa86ded01 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Mar 2025 13:40:27 +0100 Subject: [PATCH 111/119] gpio: cs5535: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250310-gpiochip-set-conversion-v1-13-03798bb833eb@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-cs5535.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-cs5535.c b/drivers/gpio/gpio-cs5535.c index 6da3a247614a..143d1f4173a6 100644 --- a/drivers/gpio/gpio-cs5535.c +++ b/drivers/gpio/gpio-cs5535.c @@ -232,12 +232,14 @@ static int chip_gpio_get(struct gpio_chip *chip, unsigned offset) return cs5535_gpio_isset(offset, GPIO_READ_BACK); } -static void chip_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +static int chip_gpio_set(struct gpio_chip *chip, unsigned int offset, int val) { if (val) cs5535_gpio_set(offset, GPIO_OUTPUT_VAL); else cs5535_gpio_clear(offset, GPIO_OUTPUT_VAL); + + return 0; } static int chip_direction_input(struct gpio_chip *c, unsigned offset) @@ -294,7 +296,7 @@ static struct cs5535_gpio_chip cs5535_gpio_chip = { .request = chip_gpio_request, .get = chip_gpio_get, - .set = chip_gpio_set, + .set_rv = chip_gpio_set, .direction_input = chip_direction_input, .direction_output = chip_direction_output, From 489c19cee3b9fd58e7967dbc4e54cdf212b073a0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Mar 2025 13:40:28 +0100 Subject: [PATCH 112/119] gpio: da9052: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250310-gpiochip-set-conversion-v1-14-03798bb833eb@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-da9052.c | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/drivers/gpio/gpio-da9052.c b/drivers/gpio/gpio-da9052.c index 6f3905f1b8f5..6482c5b267db 100644 --- a/drivers/gpio/gpio-da9052.c +++ b/drivers/gpio/gpio-da9052.c @@ -89,30 +89,20 @@ static int da9052_gpio_get(struct gpio_chip *gc, unsigned offset) } } -static void da9052_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +static int da9052_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) { struct da9052_gpio *gpio = gpiochip_get_data(gc); - int ret; - if (da9052_gpio_port_odd(offset)) { - ret = da9052_reg_update(gpio->da9052, (offset >> 1) + - DA9052_GPIO_0_1_REG, - DA9052_GPIO_ODD_PORT_MODE, - value << DA9052_GPIO_ODD_SHIFT); - if (ret != 0) - dev_err(gpio->da9052->dev, - "Failed to updated gpio odd reg,%d", - ret); - } else { - ret = da9052_reg_update(gpio->da9052, (offset >> 1) + - DA9052_GPIO_0_1_REG, - DA9052_GPIO_EVEN_PORT_MODE, - value << DA9052_GPIO_EVEN_SHIFT); - if (ret != 0) - dev_err(gpio->da9052->dev, - "Failed to updated gpio even reg,%d", - ret); - } + if (da9052_gpio_port_odd(offset)) + return da9052_reg_update(gpio->da9052, (offset >> 1) + + DA9052_GPIO_0_1_REG, + DA9052_GPIO_ODD_PORT_MODE, + value << DA9052_GPIO_ODD_SHIFT); + + return da9052_reg_update(gpio->da9052, + (offset >> 1) + DA9052_GPIO_0_1_REG, + DA9052_GPIO_EVEN_PORT_MODE, + value << DA9052_GPIO_EVEN_SHIFT); } static int da9052_gpio_direction_input(struct gpio_chip *gc, unsigned offset) @@ -182,7 +172,7 @@ static const struct gpio_chip reference_gp = { .label = "da9052-gpio", .owner = THIS_MODULE, .get = da9052_gpio_get, - .set = da9052_gpio_set, + .set_rv = da9052_gpio_set, .direction_input = da9052_gpio_direction_input, .direction_output = da9052_gpio_direction_output, .to_irq = da9052_gpio_to_irq, From 2eb5dc9a4b0d193b27289281faa05aadab978b41 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 10 Mar 2025 13:40:29 +0100 Subject: [PATCH 113/119] gpio: da9055: use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250310-gpiochip-set-conversion-v1-15-03798bb833eb@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-da9055.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/gpio/gpio-da9055.c b/drivers/gpio/gpio-da9055.c index 49446a030f10..3d9d0c700100 100644 --- a/drivers/gpio/gpio-da9055.c +++ b/drivers/gpio/gpio-da9055.c @@ -59,14 +59,12 @@ static int da9055_gpio_get(struct gpio_chip *gc, unsigned offset) } -static void da9055_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +static int da9055_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) { struct da9055_gpio *gpio = gpiochip_get_data(gc); - da9055_reg_update(gpio->da9055, - DA9055_REG_GPIO_MODE0_2, - 1 << offset, - value << offset); + return da9055_reg_update(gpio->da9055, DA9055_REG_GPIO_MODE0_2, + 1 << offset, value << offset); } static int da9055_gpio_direction_input(struct gpio_chip *gc, unsigned offset) @@ -102,9 +100,7 @@ static int da9055_gpio_direction_output(struct gpio_chip *gc, if (ret < 0) return ret; - da9055_gpio_set(gc, offset, value); - - return 0; + return da9055_gpio_set(gc, offset, value); } static int da9055_gpio_to_irq(struct gpio_chip *gc, u32 offset) @@ -120,7 +116,7 @@ static const struct gpio_chip reference_gp = { .label = "da9055-gpio", .owner = THIS_MODULE, .get = da9055_gpio_get, - .set = da9055_gpio_set, + .set_rv = da9055_gpio_set, .direction_input = da9055_gpio_direction_input, .direction_output = da9055_gpio_direction_output, .to_irq = da9055_gpio_to_irq, From 0c6dcc49487608e460b89ff7a6a50c084b01f3d8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Mar 2025 16:49:33 +0100 Subject: [PATCH 114/119] gpio: TODO: remove the item about the new debugfs interface The consensus among core GPIO stakeholders seems to be that a new debugfs interface will only increase maintenance burden and will fail to attract users that care about long-term stability of the ABI[1]. Let's not go this way and not add a fourth user-facing interface to the GPIO subsystem. [1] https://lore.kernel.org/all/9d3f1ca4-d865-45af-9032-c38cacc7fe93@pengutronix.de/ Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250321-gpio-todo-updates-v1-1-7b38f07110ee@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/TODO | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index 942d1cd2bd3c..9cf7b84cdb86 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -156,42 +156,6 @@ multiplexing, pin configuration, GPIO, etc selectable options in one and the same pin control and GPIO subsystem. -Debugfs in place of sysfs - -The old sysfs code that enables simple uses of GPIOs from the -command line is still popular despite the existance of the proper -character device. The reason is that it is simple to use on -root filesystems where you only have a minimal set of tools such -as "cat", "echo" etc. - -The old sysfs still need to be strongly deprecated and removed -as it relies on the global GPIO numberspace that assume a strict -order of global GPIO numbers that do not change between boots -and is independent of probe order. - -To solve this and provide an ABI that people can use for hacks -and development, implement a debugfs interface to manipulate -GPIO lines that can do everything that sysfs can do today: one -directory per gpiochip and one file entry per line: - -/sys/kernel/debug/gpiochip/gpiochip0 -/sys/kernel/debug/gpiochip/gpiochip0/gpio0 -/sys/kernel/debug/gpiochip/gpiochip0/gpio1 -/sys/kernel/debug/gpiochip/gpiochip0/gpio2 -/sys/kernel/debug/gpiochip/gpiochip0/gpio3 -... -/sys/kernel/debug/gpiochip/gpiochip1 -/sys/kernel/debug/gpiochip/gpiochip1/gpio0 -/sys/kernel/debug/gpiochip/gpiochip1/gpio1 -... - -The exact files and design of the debugfs interface can be -discussed but the idea is to provide a low-level access point -for debugging and hacking and to expose all lines without the -need of any exporting. Also provide ample ammunition to shoot -oneself in the foot, because this is debugfs after all. - - Moving over to immutable irq_chip structures Most of the gpio chips implementing interrupt support rely on gpiolib From 01cbfc45b48beafdab84524df14b0b4a863ea495 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Mar 2025 16:49:34 +0100 Subject: [PATCH 115/119] gpio: TODO: remove task duplication The removal of linux/gpio.h is already tracked by the item about converting drivers to using the descriptor-based API. Remove the duplicate. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250321-gpio-todo-updates-v1-2-7b38f07110ee@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/TODO | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index 9cf7b84cdb86..ff955befd0cc 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -86,15 +86,6 @@ Work items: CONFIG_OF_GPIO_MM_GPIOCHIP from the kernel. -Get rid of - -This legacy header is a one stop shop for anything GPIO is closely tied -to the global GPIO numberspace. The endgame of the above refactorings will -be the removal of and from that point only the specialized -headers under will be used. This requires all the above to -be completed and is expected to take a long time. - - Collect drivers Collect GPIO drivers from arch/* and other places that should be placed From c36420dc4f9e11d4e494a6182586008d7969c841 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Mar 2025 16:49:35 +0100 Subject: [PATCH 116/119] gpio: TODO: remove the pinctrl integration task While there are surely some arguments in favor of integrating the GPIO and pinctrl subsystems into one, I believe this is not the right approach. The GPIO subsystem uses intricate locking with SRCU to handle the fact that both consumers and providers may run in different contexts. Pin-controller drivers are always meant to run in process context. This alone is a huge obstacle to any attempt at integration as evident by many problems we already encountered during the hotplug rework. The current glue code is pretty minimal and for most part already allows GPIO controllers to query pinctrl about the information they need. I suggest to drop this task and keep the subsystems separate even if many pin-controllers implement GPIO functionality in addition to pin functions. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250321-gpio-todo-updates-v1-3-7b38f07110ee@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/TODO | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index ff955befd0cc..08ff60c65abb 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -136,17 +136,6 @@ try to cover any generic kind of irqchip cascaded from a GPIO. dry-code conversions to gpiolib irqchip for maintainers to test -Increase integration with pin control - -There are already ways to use pin control as back-end for GPIO and -it may make sense to bring these subsystems closer. One reason for -creating pin control as its own subsystem was that we could avoid any -use of the global GPIO numbers. Once the above is complete, it may -make sense to simply join the subsystems into one and make pin -multiplexing, pin configuration, GPIO, etc selectable options in one -and the same pin control and GPIO subsystem. - - Moving over to immutable irq_chip structures Most of the gpio chips implementing interrupt support rely on gpiolib From 5ceb3536f2f9ec4fcbe5f83cde6766c6cb673dce Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Mar 2025 16:49:36 +0100 Subject: [PATCH 117/119] gpio: TODO: add delimiters between tasks for better readability For better readability of the TODO, let's add some graphical delimiters between tasks. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250321-gpio-todo-updates-v1-4-7b38f07110ee@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/TODO | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index 08ff60c65abb..052ba7007003 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -1,6 +1,7 @@ This is a place for planning the ongoing long-term work in the GPIO subsystem. +=============================================================================== GPIO descriptors @@ -48,6 +49,7 @@ Work items: numberspace accessors from and eventually delete altogether. +------------------------------------------------------------------------------- Get rid of @@ -75,6 +77,7 @@ Work items: - Delete when all the above is complete and everything uses or instead. +------------------------------------------------------------------------------- Get rid of @@ -85,6 +88,7 @@ Work items: to_of_mm_gpio_chip(), of_mm_gpiochip_add_data(), of_mm_gpiochip_remove(), CONFIG_OF_GPIO_MM_GPIOCHIP from the kernel. +------------------------------------------------------------------------------- Collect drivers @@ -99,6 +103,7 @@ At the same time it makes sense to get rid of code duplication in existing or new coming drivers. For example, gpio-ml-ioh should be incorporated into gpio-pch. +------------------------------------------------------------------------------- Generic MMIO GPIO @@ -119,6 +124,7 @@ Work items: helpers (x86 inb()/outb()) and convert port-mapped I/O drivers to use this with dry-coding and sending to maintainers to test +------------------------------------------------------------------------------- Generic regmap GPIO @@ -126,6 +132,7 @@ In the very similar way to Generic MMIO GPIO convert the users which can take advantage of using regmap over direct IO accessors. Note, even in MMIO case the regmap MMIO with gpio-regmap.c is preferable over gpio-mmio.c. +------------------------------------------------------------------------------- GPIOLIB irqchip @@ -135,6 +142,7 @@ try to cover any generic kind of irqchip cascaded from a GPIO. - Look over and identify any remaining easily converted drivers and dry-code conversions to gpiolib irqchip for maintainers to test +------------------------------------------------------------------------------- Moving over to immutable irq_chip structures From 9ff2443b37d8db5b4712afb1cf44a1e75803407a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Mar 2025 16:49:37 +0100 Subject: [PATCH 118/119] gpio: TODO: add an item to track the conversion to the new value setters Add an item tracking the treewide conversion of GPIO drivers to using the new line value setter callbacks in struct gpio_chip instead of the old ones that don't allow drivers to signal failures to callers. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250321-gpio-todo-updates-v1-5-7b38f07110ee@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/TODO | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index 052ba7007003..3abf4805335f 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -161,3 +161,15 @@ A small number of drivers have been converted (pl061, tegra186, msm, amd, apple), and can be used as examples of how to proceed with this conversion. Note that drivers using the generic irqchip framework cannot be converted yet, but watch this space! + +------------------------------------------------------------------------------- + +Convert all GPIO chips to using the new, value returning line setters + +struct gpio_chip's set() and set_multiple() callbacks are now deprecated. They +return void and thus do not allow drivers to indicate failure to set the line +value back to the caller. + +We've now added new variants - set_rv() and set_multiple_rv() that return an +integer. Let's convert all GPIO drivers treewide to use the new callbacks, +remove the old ones and finally rename the new ones back to the old names. From af54a2fbdf45b1fd32cdcab916f422e6d097f430 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Mar 2025 16:49:38 +0100 Subject: [PATCH 119/119] gpio: TODO: add an item to track reworking the sysfs interface It seems there really exists the need for a simple sysfs interface that can be easily used from minimal initramfs images that don't contain much more than busybox. However the current interface poses a challenge to the removal of global GPIO numberspace. Add an item that tracks extending the existing ABI with a per-chip export/unexport attribute pair. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250321-gpio-todo-updates-v1-6-7b38f07110ee@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/TODO | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index 3abf4805335f..b5f0a7a2e1bf 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -173,3 +173,16 @@ value back to the caller. We've now added new variants - set_rv() and set_multiple_rv() that return an integer. Let's convert all GPIO drivers treewide to use the new callbacks, remove the old ones and finally rename the new ones back to the old names. + +------------------------------------------------------------------------------- + +Extend the sysfs ABI to allow exporting lines by their HW offsets + +The need to support the sysfs GPIO class is one of the main obstacles to +removing the global GPIO numberspace from the kernel. In order to wean users +off using global numbers from user-space, extend the existing interface with +new per-gpiochip export/unexport attributes that allow to refer to GPIOs using +their hardware offsets within the chip. + +Encourage users to switch to using them and eventually remove the existing +global export/unexport attribues.