From 26a4dedc5f3cdb6e2de79371f57d12e5119f03c1 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 3 Dec 2024 15:33:54 -0800 Subject: [PATCH 01/15] gpio: twl4030: use gpiochip_get_data We can pass the pointer in probe to gpiochip_add_data instead of using dev_get_drvdata. Signed-off-by: Rosen Penev Link: https://lore.kernel.org/r/20241203233354.184404-1-rosenp@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-twl6040.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c index 6c3fbf382dba8..b9171bf66168f 100644 --- a/drivers/gpio/gpio-twl6040.c +++ b/drivers/gpio/gpio-twl6040.c @@ -22,7 +22,7 @@ static int twl6040gpo_get(struct gpio_chip *chip, unsigned offset) { - struct twl6040 *twl6040 = dev_get_drvdata(chip->parent->parent); + struct twl6040 *twl6040 = gpiochip_get_data(chip); int ret = 0; ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL); @@ -46,7 +46,7 @@ static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset, static void twl6040gpo_set(struct gpio_chip *chip, unsigned offset, int value) { - struct twl6040 *twl6040 = dev_get_drvdata(chip->parent->parent); + struct twl6040 *twl6040 = gpiochip_get_data(chip); int ret; u8 gpoctl; @@ -91,7 +91,7 @@ static int gpo_twl6040_probe(struct platform_device *pdev) twl6040gpo_chip.parent = &pdev->dev; - ret = devm_gpiochip_add_data(&pdev->dev, &twl6040gpo_chip, NULL); + ret = devm_gpiochip_add_data(&pdev->dev, &twl6040gpo_chip, twl6040); if (ret < 0) { dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret); twl6040gpo_chip.ngpio = 0; From 934bacb6441afdb77093a46b3138f81cbcde7d81 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 5 Dec 2024 13:06:09 +0100 Subject: [PATCH 02/15] gpio: omap: allow building the module with COMPILE_TEST=y For better build coverage, allow building the gpio-omap driver with COMPILE_TEST Kconfig option enabled. While at it: simplify the dependencies and drop the dependency on ARM as it's already implied by ARCH_OMAP. Reviewed-by: Alexander Sverdlin Link: https://lore.kernel.org/r/20241205120610.40644-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 56fee58e281e7..d87b3d99f8f7a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -528,9 +528,9 @@ config GPIO_OCTEON family of SOCs. config GPIO_OMAP - tristate "TI OMAP GPIO support" if ARCH_OMAP2PLUS || COMPILE_TEST + tristate "TI OMAP GPIO support" + depends on ARCH_OMAP || COMPILE_TEST default y if ARCH_OMAP - depends on ARM select GENERIC_IRQ_CHIP select GPIOLIB_IRQCHIP help From 2a485c83787723671b7ad215e4e141315e46b311 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Thu, 12 Dec 2024 15:08:05 +0100 Subject: [PATCH 03/15] gpio: tqmx86: add macros for interrupt configuration Consistently use TQMX86_INT_* flags for irq_type values. The TQMX86_GPII_CONFIG macro is used to convert from TQMX86_INT_TRIG_* flags to GPII register values. Bit patterns for TQMX86_INT_* are chosen to make this conversion as simple as possible. No functional change intended. Signed-off-by: Matthias Schiffer Link: https://lore.kernel.org/r/26c01bce589aedb794c19ea7ccd85f6143532e48.1734001247.git.matthias.schiffer@ew.tq-group.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tqmx86.c | 44 +++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index 5e26eb3adabbf..dda57fc02214b 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -29,18 +29,22 @@ #define TQMX86_GPIIC 3 /* GPI Interrupt Configuration Register */ #define TQMX86_GPIIS 4 /* GPI Interrupt Status Register */ -#define TQMX86_GPII_NONE 0 -#define TQMX86_GPII_FALLING BIT(0) -#define TQMX86_GPII_RISING BIT(1) -/* Stored in irq_type as a trigger type, but not actually valid as a register - * value, so the name doesn't use "GPII" +/* + * NONE, FALLING and RISING use the same bit patterns that can be programmed to + * the GPII register (after passing them to the TQMX86_GPII_ macros to shift + * them to the right position) */ -#define TQMX86_INT_BOTH (BIT(0) | BIT(1)) -#define TQMX86_GPII_MASK (BIT(0) | BIT(1)) -#define TQMX86_GPII_BITS 2 +#define TQMX86_INT_TRIG_NONE 0 +#define TQMX86_INT_TRIG_FALLING BIT(0) +#define TQMX86_INT_TRIG_RISING BIT(1) +#define TQMX86_INT_TRIG_BOTH (BIT(0) | BIT(1)) +#define TQMX86_INT_TRIG_MASK (BIT(0) | BIT(1)) /* Stored in irq_type with GPII bits */ #define TQMX86_INT_UNMASKED BIT(2) +#define TQMX86_GPIIC_CONFIG(i, v) ((v) << (2 * (i))) +#define TQMX86_GPIIC_MASK(i) TQMX86_GPIIC_CONFIG(i, TQMX86_INT_TRIG_MASK) + struct tqmx86_gpio_data { struct gpio_chip chip; void __iomem *io_base; @@ -115,20 +119,20 @@ static int tqmx86_gpio_get_direction(struct gpio_chip *chip, static void tqmx86_gpio_irq_config(struct tqmx86_gpio_data *gpio, int offset) __must_hold(&gpio->spinlock) { - u8 type = TQMX86_GPII_NONE, gpiic; + u8 type = TQMX86_INT_TRIG_NONE, gpiic; if (gpio->irq_type[offset] & TQMX86_INT_UNMASKED) { - type = gpio->irq_type[offset] & TQMX86_GPII_MASK; + type = gpio->irq_type[offset] & TQMX86_INT_TRIG_MASK; - if (type == TQMX86_INT_BOTH) + if (type == TQMX86_INT_TRIG_BOTH) type = tqmx86_gpio_get(&gpio->chip, offset + TQMX86_NGPO) - ? TQMX86_GPII_FALLING - : TQMX86_GPII_RISING; + ? TQMX86_INT_TRIG_FALLING + : TQMX86_INT_TRIG_RISING; } gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); - gpiic &= ~(TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS)); - gpiic |= type << (offset * TQMX86_GPII_BITS); + gpiic &= ~TQMX86_GPIIC_MASK(offset); + gpiic |= TQMX86_GPIIC_CONFIG(offset, type); tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); } @@ -173,20 +177,20 @@ static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) switch (edge_type) { case IRQ_TYPE_EDGE_RISING: - new_type = TQMX86_GPII_RISING; + new_type = TQMX86_INT_TRIG_RISING; break; case IRQ_TYPE_EDGE_FALLING: - new_type = TQMX86_GPII_FALLING; + new_type = TQMX86_INT_TRIG_FALLING; break; case IRQ_TYPE_EDGE_BOTH: - new_type = TQMX86_INT_BOTH; + new_type = TQMX86_INT_TRIG_BOTH; break; default: return -EINVAL; /* not supported */ } raw_spin_lock_irqsave(&gpio->spinlock, flags); - gpio->irq_type[offset] &= ~TQMX86_GPII_MASK; + gpio->irq_type[offset] &= ~TQMX86_INT_TRIG_MASK; gpio->irq_type[offset] |= new_type; tqmx86_gpio_irq_config(gpio, offset); raw_spin_unlock_irqrestore(&gpio->spinlock, flags); @@ -232,7 +236,7 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) * reading the input and setting the trigger, we will have a new * interrupt pending. */ - if ((gpio->irq_type[i] & TQMX86_GPII_MASK) == TQMX86_INT_BOTH) + if ((gpio->irq_type[i] & TQMX86_INT_TRIG_MASK) == TQMX86_INT_TRIG_BOTH) tqmx86_gpio_irq_config(gpio, i); } raw_spin_unlock_irqrestore(&gpio->spinlock, flags); From 0ccf314304ed5b83df7470a8ed0fe1b6ed48fc03 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Thu, 12 Dec 2024 15:08:06 +0100 Subject: [PATCH 04/15] gpio: tqmx86: consistently refer to IRQs by hwirq numbers On currently supported variants of the TQMx86 GPIO controller, only GPIOs 4-7 have IRQ support; in the interrupt status and config registers, position 0 therefore corresponds to GPIO 4, position 1 to GPIO 5, etc. This was made even more confusing by sometimes using the term "offset" to refer to GPIO numbers (which are equavalent to hwirq numbers), and sometimes to bit positions in the hardware registers. With this change, the whole driver consistently uses hwirq numbers (== GPIO numbers) when referring to the IRQs, and only the two pieces of code that interact with the hardware registers (tqmx86_gpio_irq_config() and tqmx86_gpio_irq_handler()) deal with bit positions. Space for hwirq numbers 0-3 is reserved in the irq_type array, but remains unused for existing (COM Express) TQMx86 variants; support for TQMx86 variants that support IRQs on all GPIO lines will be added in the future. No functional change intended. Signed-off-by: Matthias Schiffer Link: https://lore.kernel.org/r/94b78f4a9500bb71e66c0f7d3b084fec5cfe42ca.1734001247.git.matthias.schiffer@ew.tq-group.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tqmx86.c | 40 +++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index dda57fc02214b..38208a7dc1e62 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -52,7 +52,7 @@ struct tqmx86_gpio_data { /* Lock must be held for accessing output and irq_type fields */ raw_spinlock_t spinlock; DECLARE_BITMAP(output, TQMX86_NGPIO); - u8 irq_type[TQMX86_NGPI]; + u8 irq_type[TQMX86_NGPIO]; }; static u8 tqmx86_gpio_read(struct tqmx86_gpio_data *gd, unsigned int reg) @@ -116,36 +116,36 @@ static int tqmx86_gpio_get_direction(struct gpio_chip *chip, return GPIO_LINE_DIRECTION_OUT; } -static void tqmx86_gpio_irq_config(struct tqmx86_gpio_data *gpio, int offset) +static void tqmx86_gpio_irq_config(struct tqmx86_gpio_data *gpio, int hwirq) __must_hold(&gpio->spinlock) { u8 type = TQMX86_INT_TRIG_NONE, gpiic; + int gpiic_irq = hwirq - TQMX86_NGPO; - if (gpio->irq_type[offset] & TQMX86_INT_UNMASKED) { - type = gpio->irq_type[offset] & TQMX86_INT_TRIG_MASK; + if (gpio->irq_type[hwirq] & TQMX86_INT_UNMASKED) { + type = gpio->irq_type[hwirq] & TQMX86_INT_TRIG_MASK; if (type == TQMX86_INT_TRIG_BOTH) - type = tqmx86_gpio_get(&gpio->chip, offset + TQMX86_NGPO) + type = tqmx86_gpio_get(&gpio->chip, hwirq) ? TQMX86_INT_TRIG_FALLING : TQMX86_INT_TRIG_RISING; } gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); - gpiic &= ~TQMX86_GPIIC_MASK(offset); - gpiic |= TQMX86_GPIIC_CONFIG(offset, type); + gpiic &= ~TQMX86_GPIIC_MASK(gpiic_irq); + gpiic |= TQMX86_GPIIC_CONFIG(gpiic_irq, type); tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); } static void tqmx86_gpio_irq_mask(struct irq_data *data) { - unsigned int offset = (data->hwirq - TQMX86_NGPO); struct tqmx86_gpio_data *gpio = gpiochip_get_data( irq_data_get_irq_chip_data(data)); unsigned long flags; raw_spin_lock_irqsave(&gpio->spinlock, flags); - gpio->irq_type[offset] &= ~TQMX86_INT_UNMASKED; - tqmx86_gpio_irq_config(gpio, offset); + gpio->irq_type[data->hwirq] &= ~TQMX86_INT_UNMASKED; + tqmx86_gpio_irq_config(gpio, data->hwirq); raw_spin_unlock_irqrestore(&gpio->spinlock, flags); gpiochip_disable_irq(&gpio->chip, irqd_to_hwirq(data)); @@ -153,7 +153,6 @@ static void tqmx86_gpio_irq_mask(struct irq_data *data) static void tqmx86_gpio_irq_unmask(struct irq_data *data) { - unsigned int offset = (data->hwirq - TQMX86_NGPO); struct tqmx86_gpio_data *gpio = gpiochip_get_data( irq_data_get_irq_chip_data(data)); unsigned long flags; @@ -161,8 +160,8 @@ static void tqmx86_gpio_irq_unmask(struct irq_data *data) gpiochip_enable_irq(&gpio->chip, irqd_to_hwirq(data)); raw_spin_lock_irqsave(&gpio->spinlock, flags); - gpio->irq_type[offset] |= TQMX86_INT_UNMASKED; - tqmx86_gpio_irq_config(gpio, offset); + gpio->irq_type[data->hwirq] |= TQMX86_INT_UNMASKED; + tqmx86_gpio_irq_config(gpio, data->hwirq); raw_spin_unlock_irqrestore(&gpio->spinlock, flags); } @@ -170,7 +169,6 @@ static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) { struct tqmx86_gpio_data *gpio = gpiochip_get_data( irq_data_get_irq_chip_data(data)); - unsigned int offset = (data->hwirq - TQMX86_NGPO); unsigned int edge_type = type & IRQF_TRIGGER_MASK; unsigned long flags; u8 new_type; @@ -190,9 +188,9 @@ static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) } raw_spin_lock_irqsave(&gpio->spinlock, flags); - gpio->irq_type[offset] &= ~TQMX86_INT_TRIG_MASK; - gpio->irq_type[offset] |= new_type; - tqmx86_gpio_irq_config(gpio, offset); + gpio->irq_type[data->hwirq] &= ~TQMX86_INT_TRIG_MASK; + gpio->irq_type[data->hwirq] |= new_type; + tqmx86_gpio_irq_config(gpio, data->hwirq); raw_spin_unlock_irqrestore(&gpio->spinlock, flags); return 0; @@ -204,7 +202,7 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); struct irq_chip *irq_chip = irq_desc_get_chip(desc); unsigned long irq_bits, flags; - int i; + int i, hwirq; u8 irq_status; chained_irq_enter(irq_chip, desc); @@ -216,6 +214,8 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) raw_spin_lock_irqsave(&gpio->spinlock, flags); for_each_set_bit(i, &irq_bits, TQMX86_NGPI) { + hwirq = i + TQMX86_NGPO; + /* * Edge-both triggers are implemented by flipping the edge * trigger after each interrupt, as the controller only supports @@ -236,8 +236,8 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) * reading the input and setting the trigger, we will have a new * interrupt pending. */ - if ((gpio->irq_type[i] & TQMX86_INT_TRIG_MASK) == TQMX86_INT_TRIG_BOTH) - tqmx86_gpio_irq_config(gpio, i); + if ((gpio->irq_type[hwirq] & TQMX86_INT_TRIG_MASK) == TQMX86_INT_TRIG_BOTH) + tqmx86_gpio_irq_config(gpio, hwirq); } raw_spin_unlock_irqrestore(&gpio->spinlock, flags); From 2abb6e53b5b08987265946b258ca29762091930c Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Thu, 12 Dec 2024 15:08:07 +0100 Subject: [PATCH 05/15] gpio: tqmx86: use cleanup guards for spinlock As we're touching this code anyways, go all the way and fully replace lock/unlock with guard and scoped_guard. No functional change intended. Signed-off-by: Matthias Schiffer Link: https://lore.kernel.org/r/c89e7814ce5705e516116d0b86146d8455aaeddc.1734001247.git.matthias.schiffer@ew.tq-group.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tqmx86.c | 84 +++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index 38208a7dc1e62..e55ce4503e70b 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -77,12 +77,11 @@ static void tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); - unsigned long flags; - raw_spin_lock_irqsave(&gpio->spinlock, flags); + guard(raw_spinlock_irqsave)(&gpio->spinlock); + __assign_bit(offset, gpio->output, value); tqmx86_gpio_write(gpio, bitmap_get_value8(gpio->output, 0), TQMX86_GPIOD); - raw_spin_unlock_irqrestore(&gpio->spinlock, flags); } static int tqmx86_gpio_direction_input(struct gpio_chip *chip, @@ -141,12 +140,11 @@ static void tqmx86_gpio_irq_mask(struct irq_data *data) { struct tqmx86_gpio_data *gpio = gpiochip_get_data( irq_data_get_irq_chip_data(data)); - unsigned long flags; - raw_spin_lock_irqsave(&gpio->spinlock, flags); - gpio->irq_type[data->hwirq] &= ~TQMX86_INT_UNMASKED; - tqmx86_gpio_irq_config(gpio, data->hwirq); - raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + scoped_guard(raw_spinlock_irqsave, &gpio->spinlock) { + gpio->irq_type[data->hwirq] &= ~TQMX86_INT_UNMASKED; + tqmx86_gpio_irq_config(gpio, data->hwirq); + } gpiochip_disable_irq(&gpio->chip, irqd_to_hwirq(data)); } @@ -155,14 +153,13 @@ static void tqmx86_gpio_irq_unmask(struct irq_data *data) { struct tqmx86_gpio_data *gpio = gpiochip_get_data( irq_data_get_irq_chip_data(data)); - unsigned long flags; gpiochip_enable_irq(&gpio->chip, irqd_to_hwirq(data)); - raw_spin_lock_irqsave(&gpio->spinlock, flags); + guard(raw_spinlock_irqsave)(&gpio->spinlock); + gpio->irq_type[data->hwirq] |= TQMX86_INT_UNMASKED; tqmx86_gpio_irq_config(gpio, data->hwirq); - raw_spin_unlock_irqrestore(&gpio->spinlock, flags); } static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) @@ -170,7 +167,6 @@ static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) struct tqmx86_gpio_data *gpio = gpiochip_get_data( irq_data_get_irq_chip_data(data)); unsigned int edge_type = type & IRQF_TRIGGER_MASK; - unsigned long flags; u8 new_type; switch (edge_type) { @@ -187,11 +183,11 @@ static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) return -EINVAL; /* not supported */ } - raw_spin_lock_irqsave(&gpio->spinlock, flags); + guard(raw_spinlock_irqsave)(&gpio->spinlock); + gpio->irq_type[data->hwirq] &= ~TQMX86_INT_TRIG_MASK; gpio->irq_type[data->hwirq] |= new_type; tqmx86_gpio_irq_config(gpio, data->hwirq); - raw_spin_unlock_irqrestore(&gpio->spinlock, flags); return 0; } @@ -201,7 +197,7 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) struct gpio_chip *chip = irq_desc_get_handler_data(desc); struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); struct irq_chip *irq_chip = irq_desc_get_chip(desc); - unsigned long irq_bits, flags; + unsigned long irq_bits; int i, hwirq; u8 irq_status; @@ -212,34 +208,38 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) irq_bits = irq_status; - raw_spin_lock_irqsave(&gpio->spinlock, flags); - for_each_set_bit(i, &irq_bits, TQMX86_NGPI) { - hwirq = i + TQMX86_NGPO; - - /* - * Edge-both triggers are implemented by flipping the edge - * trigger after each interrupt, as the controller only supports - * either rising or falling edge triggers, but not both. - * - * Internally, the TQMx86 GPIO controller has separate status - * registers for rising and falling edge interrupts. GPIIC - * configures which bits from which register are visible in the - * interrupt status register GPIIS and defines what triggers the - * parent IRQ line. Writing to GPIIS always clears both rising - * and falling interrupt flags internally, regardless of the - * currently configured trigger. - * - * In consequence, we can cleanly implement the edge-both - * trigger in software by first clearing the interrupt and then - * setting the new trigger based on the current GPIO input in - * tqmx86_gpio_irq_config() - even if an edge arrives between - * reading the input and setting the trigger, we will have a new - * interrupt pending. - */ - if ((gpio->irq_type[hwirq] & TQMX86_INT_TRIG_MASK) == TQMX86_INT_TRIG_BOTH) - tqmx86_gpio_irq_config(gpio, hwirq); + scoped_guard(raw_spinlock_irqsave, &gpio->spinlock) { + for_each_set_bit(i, &irq_bits, TQMX86_NGPI) { + hwirq = i + TQMX86_NGPO; + + /* + * Edge-both triggers are implemented by flipping the + * edge trigger after each interrupt, as the controller + * only supports either rising or falling edge triggers, + * but not both. + * + * Internally, the TQMx86 GPIO controller has separate + * status registers for rising and falling edge + * interrupts. GPIIC configures which bits from which + * register are visible in the interrupt status register + * GPIIS and defines what triggers the parent IRQ line. + * Writing to GPIIS always clears both rising and + * falling interrupt flags internally, regardless of the + * currently configured trigger. + * + * In consequence, we can cleanly implement the + * edge-both trigger in software by first clearing the + * interrupt and then setting the new trigger based on + * the current GPIO input in tqmx86_gpio_irq_config() - + * even if an edge arrives between reading the input and + * setting the trigger, we will have a new interrupt + * pending. + */ + if ((gpio->irq_type[hwirq] & TQMX86_INT_TRIG_MASK) == + TQMX86_INT_TRIG_BOTH) + tqmx86_gpio_irq_config(gpio, hwirq); + } } - raw_spin_unlock_irqrestore(&gpio->spinlock, flags); for_each_set_bit(i, &irq_bits, TQMX86_NGPI) generic_handle_domain_irq(gpio->chip.irq.domain, From a1389f5c128e80c8ad3132bbdc7b5061f3710b7f Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Thu, 12 Dec 2024 15:08:08 +0100 Subject: [PATCH 06/15] gpio: tqmx86: introduce tqmx86_gpio_clrsetbits() helper Add a helper for the common read-modify-write pattern (only used in tqmx86_gpio_irq_config() initially). No functional change intended. Signed-off-by: Matthias Schiffer Link: https://lore.kernel.org/r/f60ec3cc1386d4527ebb864d1b069683473b7a5a.1734001247.git.matthias.schiffer@ew.tq-group.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tqmx86.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index e55ce4503e70b..4bef13cad1807 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -66,6 +66,18 @@ static void tqmx86_gpio_write(struct tqmx86_gpio_data *gd, u8 val, iowrite8(val, gd->io_base + reg); } +static void tqmx86_gpio_clrsetbits(struct tqmx86_gpio_data *gpio, + u8 clr, u8 set, unsigned int reg) + __must_hold(&gpio->spinlock) +{ + u8 val = tqmx86_gpio_read(gpio, reg); + + val &= ~clr; + val |= set; + + tqmx86_gpio_write(gpio, val, reg); +} + static int tqmx86_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); @@ -118,7 +130,7 @@ static int tqmx86_gpio_get_direction(struct gpio_chip *chip, static void tqmx86_gpio_irq_config(struct tqmx86_gpio_data *gpio, int hwirq) __must_hold(&gpio->spinlock) { - u8 type = TQMX86_INT_TRIG_NONE, gpiic; + u8 type = TQMX86_INT_TRIG_NONE; int gpiic_irq = hwirq - TQMX86_NGPO; if (gpio->irq_type[hwirq] & TQMX86_INT_UNMASKED) { @@ -130,10 +142,10 @@ static void tqmx86_gpio_irq_config(struct tqmx86_gpio_data *gpio, int hwirq) : TQMX86_INT_TRIG_RISING; } - gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); - gpiic &= ~TQMX86_GPIIC_MASK(gpiic_irq); - gpiic |= TQMX86_GPIIC_CONFIG(gpiic_irq, type); - tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + tqmx86_gpio_clrsetbits(gpio, + TQMX86_GPIIC_MASK(gpiic_irq), + TQMX86_GPIIC_CONFIG(gpiic_irq, type), + TQMX86_GPIIC); } static void tqmx86_gpio_irq_mask(struct irq_data *data) From 2251fbd05f2357927fa5c5a8dd955f84da883008 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Thu, 12 Dec 2024 15:08:09 +0100 Subject: [PATCH 07/15] gpio: tqmx86: add support for changing GPIO directions Only GPIOs 4..7 have IRQ support on the TQMx86 variants currently handled by the driver, but apart from that, changing directions works fine. The default directions are left unchanged (0..3 output, 4..7 input) to match the COM Express specification. A tqmx86_gpio_set() variant without locking is introduced as a new helper. Signed-off-by: Matthias Schiffer Link: https://lore.kernel.org/r/d89da2f0e13fa6c8ec3f9076eed242133a1e3a63.1734001247.git.matthias.schiffer@ew.tq-group.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tqmx86.c | 44 ++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index 4bef13cad1807..18f523a15b3c0 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -85,6 +85,14 @@ static int tqmx86_gpio_get(struct gpio_chip *chip, unsigned int offset) return !!(tqmx86_gpio_read(gpio, TQMX86_GPIOD) & BIT(offset)); } +static void _tqmx86_gpio_set(struct tqmx86_gpio_data *gpio, unsigned int offset, + int value) + __must_hold(&gpio->spinlock) +{ + __assign_bit(offset, gpio->output, value); + tqmx86_gpio_write(gpio, bitmap_get_value8(gpio->output, 0), TQMX86_GPIOD); +} + static void tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { @@ -92,39 +100,47 @@ static void tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset, guard(raw_spinlock_irqsave)(&gpio->spinlock); - __assign_bit(offset, gpio->output, value); - tqmx86_gpio_write(gpio, bitmap_get_value8(gpio->output, 0), TQMX86_GPIOD); + _tqmx86_gpio_set(gpio, offset, value); } static int tqmx86_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { - /* Direction cannot be changed. Validate is an input. */ - if (BIT(offset) & TQMX86_DIR_INPUT_MASK) - return 0; - else - return -EINVAL; + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + + guard(raw_spinlock_irqsave)(&gpio->spinlock); + + tqmx86_gpio_clrsetbits(gpio, BIT(offset), 0, TQMX86_GPIODD); + + return 0; } static int tqmx86_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { - /* Direction cannot be changed, validate is an output */ - if (BIT(offset) & TQMX86_DIR_INPUT_MASK) - return -EINVAL; + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + + guard(raw_spinlock_irqsave)(&gpio->spinlock); + + _tqmx86_gpio_set(gpio, offset, value); + tqmx86_gpio_clrsetbits(gpio, 0, BIT(offset), TQMX86_GPIODD); - tqmx86_gpio_set(chip, offset, value); return 0; } static int tqmx86_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { - if (TQMX86_DIR_INPUT_MASK & BIT(offset)) - return GPIO_LINE_DIRECTION_IN; + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + u8 val; + + val = tqmx86_gpio_read(gpio, TQMX86_GPIODD); + + if (val & BIT(offset)) + return GPIO_LINE_DIRECTION_OUT; - return GPIO_LINE_DIRECTION_OUT; + return GPIO_LINE_DIRECTION_IN; } static void tqmx86_gpio_irq_config(struct tqmx86_gpio_data *gpio, int hwirq) From 83a9752729c455a6bd9b7cf62198506180691931 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 12 Dec 2024 18:36:30 +0000 Subject: [PATCH 08/15] dt-bindings: gpio: brcmstb: permit gpio-line-names property gpio-line-names is a generic property that can be supported by any GPIO controller, so permit it through the binding. It is permitted to have a variable number of GPIOs per node based on brcm,gpio-bank-widths, so define an arbitrary maximum number of items based on current users. Signed-off-by: Dave Stevenson Acked-by: Florian Fainelli Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20241212-dt-bcm2712-fixes-v3-3-44a7f3390331@raspberrypi.com Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.yaml b/Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.yaml index f096f286da19c..fbd69b4cecc70 100644 --- a/Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.yaml @@ -64,6 +64,10 @@ properties: gpio-ranges: true + gpio-line-names: + minItems: 1 + maxItems: 128 + wakeup-source: type: boolean description: > From 7cef813a91c468253c80633891393478b9f2c966 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Thu, 19 Dec 2024 10:39:46 +0100 Subject: [PATCH 09/15] gpio: pca953x: log an error when failing to get the reset GPIO When the dirver fails getting this GPIO, it fails silently. Log an error message to make debugging a lot easier by just reading dmesg. Signed-off-by: Luca Ceresoli Fixes: 054ccdef8b28 ("gpio: pca953x: Add optional reset gpio control") Link: https://lore.kernel.org/r/20241219-pca953x-log-no-reset-gpio-v1-1-9aa7bcc45ead@bootlin.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca953x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 272febc3230e9..be4c9981ebc40 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -1088,7 +1088,8 @@ static int pca953x_probe(struct i2c_client *client) */ reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(reset_gpio)) - return PTR_ERR(reset_gpio); + return dev_err_probe(dev, PTR_ERR(reset_gpio), + "Failed to get reset gpio\n"); } chip->client = client; From c9ec045fa8053820573efbaa70137d0857e444db Mon Sep 17 00:00:00 2001 From: Shree Ramamoorthy Date: Tue, 17 Dec 2024 14:47:55 -0600 Subject: [PATCH 10/15] gpio: tps65219: Use existing kernel gpio macros Replace the user-defined macros with the equivalent kernel macros provided. The user-defined macros serve the same purpose as the common kernel macros. The user-defined macros rely on the inference that _IN corresponds to true and _OUT is false. Signed-off-by: Shree Ramamoorthy Link: https://lore.kernel.org/r/20241217204755.1011731-2-s-ramamoorthy@ti.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tps65219.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-tps65219.c b/drivers/gpio/gpio-tps65219.c index cd1f17041f8c0..526640c39a11f 100644 --- a/drivers/gpio/gpio-tps65219.c +++ b/drivers/gpio/gpio-tps65219.c @@ -15,8 +15,6 @@ #define TPS65219_GPIO0_DIR_MASK BIT(3) #define TPS65219_GPIO0_OFFSET 2 #define TPS65219_GPIO0_IDX 0 -#define TPS65219_GPIO_DIR_IN 1 -#define TPS65219_GPIO_DIR_OUT 0 struct tps65219_gpio { struct gpio_chip gpio_chip; @@ -61,7 +59,7 @@ static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset) * status bit. */ - if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_OUT) + if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT) return -ENOTSUPP; return ret; @@ -124,10 +122,10 @@ static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offs return -ENOTSUPP; } - if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_IN) + if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_IN) return 0; - return tps65219_gpio_change_direction(gc, offset, TPS65219_GPIO_DIR_IN); + return tps65219_gpio_change_direction(gc, offset, GPIO_LINE_DIRECTION_IN); } static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value) @@ -136,10 +134,10 @@ static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int off if (offset != TPS65219_GPIO0_IDX) return 0; - if (tps65219_gpio_get_direction(gc, offset) == TPS65219_GPIO_DIR_OUT) + if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT) return 0; - return tps65219_gpio_change_direction(gc, offset, TPS65219_GPIO_DIR_OUT); + return tps65219_gpio_change_direction(gc, offset, GPIO_LINE_DIRECTION_OUT); } static const struct gpio_chip tps65219_template_chip = { From 267f2c5662e3fd8891bca01f71bc1c258e293206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Tue, 24 Dec 2024 09:02:12 +0100 Subject: [PATCH 11/15] dt-bindings: gpio: fairchild,74hc595: Document chip select vs. latch clock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From looking at the data sheets, it is not obvious that CS# and latch clock can be treated at the same, but doing so works fine and saves the hassle of (1) trying to specify a SPI device without CS, and (2) adding another property to drive the latch clock[1]. [1]: https://lore.kernel.org/lkml/20241213-gpio74-v1-2-fa2c089caf41@posteo.net/ Signed-off-by: J. Neuschäfer Reviewed-by: Linus Walleij Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20241224-gpio74-v2-3-bbcf14183191@posteo.net Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/fairchild,74hc595.yaml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml b/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml index e8bc9f018edb1..0e5c22929bdeb 100644 --- a/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml +++ b/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml @@ -6,6 +6,23 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Generic 8-bit shift register +description: | + NOTE: These chips nominally don't have a chip select pin. They do however + have a rising-edge triggered latch clock (or storage register clock) pin, + which behaves like an active-low chip select. + + After the bits are shifted into the shift register, CS# is driven high, which + the 74HC595 sees as a rising edge on the latch clock that results in a + transfer of the bits from the shift register to the storage register and thus + to the output pins. + _ _ _ _ + shift clock ____| |_| |_..._| |_| |_________ + + latch clock * trigger + ___ ________ + chip select# |___________________| + + maintainers: - Maxime Ripard From b7c1336e2ecaba2faccebaa98dbff64d57c31727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Thu, 2 Jan 2025 19:31:52 +0100 Subject: [PATCH 12/15] dt-bindings: gpio: fsl,qoriq-gpio: Add compatible string fsl,mpc8314-gpio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is for the MPC831{4,5}{,E} SoCs. Signed-off-by: J. Neuschäfer Reviewed-by: Linus Walleij Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20250102-mpc83xx-v1-11-86f78ba2a7af@posteo.net Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/fsl,qoriq-gpio.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/gpio/fsl,qoriq-gpio.yaml b/Documentation/devicetree/bindings/gpio/fsl,qoriq-gpio.yaml index 84fd82291ee40..f1b60ab3f356b 100644 --- a/Documentation/devicetree/bindings/gpio/fsl,qoriq-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/fsl,qoriq-gpio.yaml @@ -15,6 +15,7 @@ properties: - enum: - fsl,mpc5121-gpio - fsl,mpc5125-gpio + - fsl,mpc8314-gpio - fsl,mpc8349-gpio - fsl,mpc8572-gpio - fsl,mpc8610-gpio From 65b3aacff639e4670533042c587498196d036a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Thu, 2 Jan 2025 19:31:54 +0100 Subject: [PATCH 13/15] gpio: mpc8xxx: Add MPC8314 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GPIO input, output, and interrupts have been tested on a MPC8314E board. Signed-off-by: J. Neuschäfer Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250102-mpc83xx-v1-13-86f78ba2a7af@posteo.net Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mpc8xxx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 24417c3247b0a..0cd4c36ae8aaf 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -285,6 +285,7 @@ static const struct mpc8xxx_gpio_devtype mpc8xxx_gpio_devtype_default = { }; static const struct of_device_id mpc8xxx_gpio_ids[] = { + { .compatible = "fsl,mpc8314-gpio", }, { .compatible = "fsl,mpc8349-gpio", }, { .compatible = "fsl,mpc8572-gpio", .data = &mpc8572_gpio_devtype, }, { .compatible = "fsl,mpc8610-gpio", }, From 3c838184407f3e0b28cd86a7275ffc2f9e790efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 9 Jan 2025 10:08:03 +0100 Subject: [PATCH 14/15] gpio: altera: Drop .mapped_irq from driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct altera_gpio_chip::mapped_irq is only used in the driver's probe function. So it's enough if mapped_irq is a local variable, and can be dropped from driver data. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250109090802.3763275-2-u.kleine-koenig@baylibre.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-altera.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 73e660c5e38a1..17ab039c74131 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -32,14 +32,12 @@ * will be blocked until the current one completes. * @interrupt_trigger : specifies the hardware configured IRQ trigger type * (rising, falling, both, high) -* @mapped_irq : kernel mapped irq number. */ struct altera_gpio_chip { struct gpio_chip gc; void __iomem *regs; raw_spinlock_t gpio_lock; int interrupt_trigger; - int mapped_irq; }; static void altera_gpio_irq_unmask(struct irq_data *d) @@ -235,6 +233,7 @@ static int altera_gpio_probe(struct platform_device *pdev) int reg, ret; struct altera_gpio_chip *altera_gc; struct gpio_irq_chip *girq; + int mapped_irq; altera_gc = devm_kzalloc(&pdev->dev, sizeof(*altera_gc), GFP_KERNEL); if (!altera_gc) @@ -271,8 +270,8 @@ static int altera_gpio_probe(struct platform_device *pdev) if (IS_ERR(altera_gc->regs)) return dev_err_probe(dev, PTR_ERR(altera_gc->regs), "failed to ioremap memory resource\n"); - altera_gc->mapped_irq = platform_get_irq_optional(pdev, 0); - if (altera_gc->mapped_irq < 0) + mapped_irq = platform_get_irq_optional(pdev, 0); + if (mapped_irq < 0) goto skip_irq; if (device_property_read_u32(dev, "altr,interrupt-type", ®)) { @@ -296,7 +295,7 @@ static int altera_gpio_probe(struct platform_device *pdev) return -ENOMEM; girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_bad_irq; - girq->parents[0] = altera_gc->mapped_irq; + girq->parents[0] = mapped_irq; skip_irq: ret = devm_gpiochip_add_data(dev, &altera_gc->gc, altera_gc); From b0fa00fe38f673c986633c11087274deeb7ce7b0 Mon Sep 17 00:00:00 2001 From: Sander Vanheule Date: Tue, 7 Jan 2025 21:16:20 +0100 Subject: [PATCH 15/15] gpio: regmap: Use generic request/free ops Set the gpiochip request and free ops to the generic implementations. This way a user can provide a gpio-ranges property defined for a pinmux, easing muxing of gpio functions. Provided that the pin controller implementents the pinmux op .gpio_request_enable(), pins will automatically be muxed to their GPIO function when requested. Signed-off-by: Sander Vanheule Acked-by: Michael Walle Link: https://lore.kernel.org/r/20250107201621.12467-1-sander@svanheule.net Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-regmap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index 71684dee2ca5d..05f8781b52048 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -262,6 +262,8 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config chip->label = config->label ?: dev_name(config->parent); chip->can_sleep = regmap_might_sleep(config->regmap); + chip->request = gpiochip_generic_request; + 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;