pinctrl: renesas: Updates for v6.20

- Add support for GPIO IRQs on RZ/T2H and RZ/N2H.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQQ9qaHoIs/1I4cXmEiKwlD9ZEnxcAUCaWoT+gAKCRCKwlD9ZEnx
 cEo+AP4qLfOTmmOU3TD2z5OHCUcoOczfGVtYTZWT2XD/w5Z67QEA0c8I5JbSaI0S
 zr6RbyfNx0QTyqEuuENnPzOVo5OrTQY=
 =2Zkc
 -----END PGP SIGNATURE-----

Merge tag 'renesas-pinctrl-for-v6.20-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers into devel

pinctrl: renesas: Updates for v6.20

  - Add support for GPIO IRQs on RZ/T2H and RZ/N2H.

Signed-off-by: Linus Walleij <linusw@kernel.org>
This commit is contained in:
Linus Walleij 2026-01-19 00:27:18 +01:00
commit 43519f5457
3 changed files with 254 additions and 9 deletions

View File

@ -49,6 +49,17 @@ properties:
gpio-ranges:
maxItems: 1
interrupt-controller: true
'#interrupt-cells':
const: 2
description:
The first cell contains the global GPIO port index, constructed using the
RZT2H_GPIO() helper macro from <dt-bindings/pinctrl/renesas,r9a09g077-pinctrl.h>
and the second cell is used to specify the flag.
E.g. "interrupts = <RZT2H_GPIO(8, 6) IRQ_TYPE_EDGE_FALLING>;" if P08_6 is
being used as an interrupt.
clocks:
maxItems: 1
@ -139,6 +150,8 @@ examples:
gpio-controller;
#gpio-cells = <2>;
gpio-ranges = <&pinctrl 0 0 288>;
interrupt-controller;
#interrupt-cells = <2>;
power-domains = <&cpg>;
serial0-pins {

View File

@ -308,9 +308,11 @@ config PINCTRL_RZT2H
bool "pin control support for RZ/N2H and RZ/T2H" if COMPILE_TEST
depends on 64BIT && OF
select GPIOLIB
select GPIOLIB_IRQCHIP
select GENERIC_PINCTRL_GROUPS
select GENERIC_PINMUX_FUNCTIONS
select GENERIC_PINCONF
select IRQ_DOMAIN_HIERARCHY
help
This selects GPIO and pinctrl driver for Renesas RZ/T2H
platforms.

View File

@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spinlock.h>
@ -51,6 +52,7 @@
#define PFC_MASK GENMASK_ULL(5, 0)
#define PFC_PIN_MASK(pin) (PFC_MASK << ((pin) * 8))
#define PFC_FUNC_INTERRUPT 0
/*
* Use 16 lower bits [15:0] for pin identifier
@ -64,6 +66,9 @@
#define RZT2H_MAX_SAFETY_PORTS 12
#define RZT2H_INTERRUPTS_START 16
#define RZT2H_INTERRUPTS_NUM 17
struct rzt2h_pinctrl_data {
unsigned int n_port_pins;
const u8 *port_pin_configs;
@ -79,9 +84,11 @@ struct rzt2h_pinctrl {
struct device *dev;
struct gpio_chip gpio_chip;
struct pinctrl_gpio_range gpio_range;
DECLARE_BITMAP(used_irqs, RZT2H_INTERRUPTS_NUM);
spinlock_t lock; /* lock read/write registers */
struct mutex mutex; /* serialize adding groups and functions */
bool safety_port_enabled;
atomic_t wakeup_path;
};
#define RZT2H_GET_BASE(pctrl, port) \
@ -119,6 +126,19 @@ static int rzt2h_validate_pin(struct rzt2h_pinctrl *pctrl, unsigned int offset)
return (pincfg & BIT(pin)) ? 0 : -EINVAL;
}
static void rzt2h_pinctrl_set_gpio_en(struct rzt2h_pinctrl *pctrl,
u8 port, u8 pin, bool en)
{
u8 reg = rzt2h_pinctrl_readb(pctrl, port, PMC(port));
if (en)
reg &= ~BIT(pin);
else
reg |= BIT(pin);
rzt2h_pinctrl_writeb(pctrl, port, reg, PMC(port));
}
static void rzt2h_pinctrl_set_pfc_mode(struct rzt2h_pinctrl *pctrl,
u8 port, u8 pin, u8 func)
{
@ -133,8 +153,7 @@ static void rzt2h_pinctrl_set_pfc_mode(struct rzt2h_pinctrl *pctrl,
rzt2h_pinctrl_writew(pctrl, port, reg16, PM(port));
/* Temporarily switch to GPIO mode with PMC register */
reg16 = rzt2h_pinctrl_readb(pctrl, port, PMC(port));
rzt2h_pinctrl_writeb(pctrl, port, reg16 & ~BIT(pin), PMC(port));
rzt2h_pinctrl_set_gpio_en(pctrl, port, pin, true);
/* Select Pin function mode with PFC register */
reg64 = rzt2h_pinctrl_readq(pctrl, port, PFC(port));
@ -142,8 +161,7 @@ static void rzt2h_pinctrl_set_pfc_mode(struct rzt2h_pinctrl *pctrl,
rzt2h_pinctrl_writeq(pctrl, port, reg64 | ((u64)func << (pin * 8)), PFC(port));
/* Switch to Peripheral pin function with PMC register */
reg16 = rzt2h_pinctrl_readb(pctrl, port, PMC(port));
rzt2h_pinctrl_writeb(pctrl, port, reg16 | BIT(pin), PMC(port));
rzt2h_pinctrl_set_gpio_en(pctrl, port, pin, false);
}
static int rzt2h_pinctrl_set_mux(struct pinctrl_dev *pctldev,
@ -447,7 +465,6 @@ static int rzt2h_gpio_request(struct gpio_chip *chip, unsigned int offset)
u8 port = RZT2H_PIN_ID_TO_PORT(offset);
u8 bit = RZT2H_PIN_ID_TO_PIN(offset);
int ret;
u8 reg;
ret = rzt2h_validate_pin(pctrl, offset);
if (ret)
@ -460,9 +477,7 @@ static int rzt2h_gpio_request(struct gpio_chip *chip, unsigned int offset)
guard(spinlock_irqsave)(&pctrl->lock);
/* Select GPIO mode in PMC Register */
reg = rzt2h_pinctrl_readb(pctrl, port, PMC(port));
reg &= ~BIT(bit);
rzt2h_pinctrl_writeb(pctrl, port, reg, PMC(port));
rzt2h_pinctrl_set_gpio_en(pctrl, port, bit, true);
return 0;
}
@ -486,6 +501,7 @@ static int rzt2h_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
struct rzt2h_pinctrl *pctrl = gpiochip_get_data(chip);
u8 port = RZT2H_PIN_ID_TO_PORT(offset);
u8 bit = RZT2H_PIN_ID_TO_PIN(offset);
u64 reg64;
u16 reg;
int ret;
@ -493,8 +509,25 @@ static int rzt2h_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
if (ret)
return ret;
if (rzt2h_pinctrl_readb(pctrl, port, PMC(port)) & BIT(bit))
guard(spinlock_irqsave)(&pctrl->lock);
if (rzt2h_pinctrl_readb(pctrl, port, PMC(port)) & BIT(bit)) {
/*
* When a GPIO is being requested as an IRQ, the pinctrl
* framework expects to be able to read the GPIO's direction.
* IRQ function is separate from GPIO, and enabling it takes the
* pin out of GPIO mode.
* At this point, .child_to_parent_hwirq() has already been
* called to enable the IRQ function.
* Default to input direction for IRQ function.
*/
reg64 = rzt2h_pinctrl_readq(pctrl, port, PFC(port));
reg64 = (reg64 >> (bit * 8)) & PFC_MASK;
if (reg64 == PFC_FUNC_INTERRUPT)
return GPIO_LINE_DIRECTION_IN;
return -EINVAL;
}
reg = rzt2h_pinctrl_readw(pctrl, port, PM(port));
reg = (reg >> (bit * 2)) & PM_MASK;
@ -617,14 +650,185 @@ static const char * const rzt2h_gpio_names[] = {
"P35_0", "P35_1", "P35_2", "P35_3", "P35_4", "P35_5", "P35_6", "P35_7",
};
/*
* Interrupts 0-15 are for INTCPUn, which are not exposed externally.
* Interrupts 16-31 are for IRQn. SEI is 32.
* This table matches the information found in User Manual's Section
* 17.5, Multiplexed Pin Configurations, Tables 17.5 to 17.40, on the
* Interrupt rows.
* RZ/N2H has the same GPIO to IRQ mapping, except for the pins which
* are not present.
*/
static const u8 rzt2h_gpio_irq_map[] = {
32, 16, 17, 18, 19, 0, 20, 21,
22, 0, 0, 0, 0, 0, 0, 0,
23, 24, 25, 26, 27, 0, 0, 0,
0, 0, 28, 29, 30, 31, 0, 0,
0, 0, 0, 0, 0, 32, 16, 17,
18, 19, 20, 21, 22, 0, 0, 0,
0, 0, 24, 25, 26, 27, 0, 28,
29, 30, 31, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 24, 32, 16,
0, 0, 0, 0, 0, 0, 0, 0,
20, 23, 17, 18, 19, 0, 16, 25,
29, 20, 21, 22, 23, 0, 0, 0,
0, 0, 0, 0, 17, 0, 0, 18,
0, 0, 19, 0, 0, 20, 0, 30,
21, 0, 0, 22, 0, 0, 24, 25,
0, 0, 0, 0, 0, 16, 17, 0,
18, 0, 0, 26, 27, 0, 0, 0,
28, 29, 30, 31, 0, 0, 0, 0,
23, 31, 32, 16, 17, 18, 19, 20,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
27, 0, 0, 21, 22, 23, 24, 25,
26, 0, 0, 0, 0, 0, 0, 0,
27, 28, 29, 30, 31, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 28, 32, 16,
17, 18, 19, 0, 0, 0, 0, 20,
21, 22, 23, 0, 0, 0, 0, 0,
0, 0, 0, 0, 24, 25, 0, 0,
0, 0, 26, 27, 0, 0, 0, 30,
0, 29, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 28, 29, 30, 31, 0,
0, 0, 0, 0, 0, 0, 0, 30,
0, 0, 0, 0, 0, 0, 0, 0,
};
static void rzt2h_gpio_irq_disable(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
unsigned int hwirq = irqd_to_hwirq(d);
irq_chip_disable_parent(d);
gpiochip_disable_irq(gc, hwirq);
}
static void rzt2h_gpio_irq_enable(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
unsigned int hwirq = irqd_to_hwirq(d);
gpiochip_enable_irq(gc, hwirq);
irq_chip_enable_parent(d);
}
static int rzt2h_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct rzt2h_pinctrl *pctrl = container_of(gc, struct rzt2h_pinctrl, gpio_chip);
int ret;
ret = irq_chip_set_wake_parent(d, on);
if (ret)
return ret;
/*
* If any of the IRQs are in use, put the entire pin controller on the
* device wakeup path.
*/
if (on)
atomic_inc(&pctrl->wakeup_path);
else
atomic_dec(&pctrl->wakeup_path);
return 0;
}
static const struct irq_chip rzt2h_gpio_irqchip = {
.name = "rzt2h-gpio",
.irq_disable = rzt2h_gpio_irq_disable,
.irq_enable = rzt2h_gpio_irq_enable,
.irq_mask = irq_chip_mask_parent,
.irq_unmask = irq_chip_unmask_parent,
.irq_set_type = irq_chip_set_type_parent,
.irq_set_wake = rzt2h_gpio_irq_set_wake,
.irq_eoi = irq_chip_eoi_parent,
.irq_set_affinity = irq_chip_set_affinity_parent,
.flags = IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static int rzt2h_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
unsigned int child,
unsigned int child_type,
unsigned int *parent,
unsigned int *parent_type)
{
struct rzt2h_pinctrl *pctrl = gpiochip_get_data(gc);
u8 port = RZT2H_PIN_ID_TO_PORT(child);
u8 pin = RZT2H_PIN_ID_TO_PIN(child);
u8 parent_irq;
parent_irq = rzt2h_gpio_irq_map[child];
if (parent_irq < RZT2H_INTERRUPTS_START)
return -EINVAL;
if (test_and_set_bit(parent_irq - RZT2H_INTERRUPTS_START,
pctrl->used_irqs))
return -EBUSY;
rzt2h_pinctrl_set_pfc_mode(pctrl, port, pin, PFC_FUNC_INTERRUPT);
*parent = parent_irq;
*parent_type = child_type;
return 0;
}
static void rzt2h_gpio_irq_domain_free(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs)
{
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct rzt2h_pinctrl *pctrl = container_of(gc, struct rzt2h_pinctrl, gpio_chip);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
u8 port = RZT2H_PIN_ID_TO_PORT(hwirq);
u8 pin = RZT2H_PIN_ID_TO_PIN(hwirq);
if (test_and_clear_bit(hwirq - RZT2H_INTERRUPTS_START, pctrl->used_irqs))
rzt2h_pinctrl_set_gpio_en(pctrl, port, pin, false);
irq_domain_free_irqs_common(domain, virq, nr_irqs);
}
static void rzt2h_gpio_init_irq_valid_mask(struct gpio_chip *gc,
unsigned long *valid_mask,
unsigned int ngpios)
{
struct rzt2h_pinctrl *pctrl = gpiochip_get_data(gc);
unsigned int offset;
for (offset = 0; offset < ngpios; offset++) {
if (!rzt2h_gpio_irq_map[offset] || rzt2h_validate_pin(pctrl, offset))
clear_bit(offset, valid_mask);
}
}
static int rzt2h_gpio_register(struct rzt2h_pinctrl *pctrl)
{
struct pinctrl_gpio_range *range = &pctrl->gpio_range;
struct gpio_chip *chip = &pctrl->gpio_chip;
struct device_node *np = pctrl->dev->of_node;
struct irq_domain *parent_domain;
struct device *dev = pctrl->dev;
struct of_phandle_args of_args;
struct device_node *parent_np;
struct gpio_irq_chip *girq;
int ret;
parent_np = of_irq_find_parent(np);
if (!parent_np)
return -ENXIO;
parent_domain = irq_find_host(parent_np);
of_node_put(parent_np);
if (!parent_domain)
return -EPROBE_DEFER;
ret = of_parse_phandle_with_fixed_args(dev->of_node, "gpio-ranges", 3, 0, &of_args);
if (ret)
return dev_err_probe(dev, ret, "Unable to parse gpio-ranges\n");
@ -648,6 +852,17 @@ static int rzt2h_gpio_register(struct rzt2h_pinctrl *pctrl)
chip->set = rzt2h_gpio_set;
chip->label = dev_name(dev);
if (of_property_present(np, "interrupt-controller")) {
girq = &chip->irq;
gpio_irq_chip_set_chip(girq, &rzt2h_gpio_irqchip);
girq->fwnode = dev_fwnode(pctrl->dev);
girq->parent_domain = parent_domain;
girq->child_to_parent_hwirq = rzt2h_gpio_child_to_parent_hwirq;
girq->populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_twocell;
girq->child_irq_domain_ops.free = rzt2h_gpio_irq_domain_free;
girq->init_valid_mask = rzt2h_gpio_init_irq_valid_mask;
}
range->id = 0;
range->pin_base = 0;
range->base = 0;
@ -792,10 +1007,25 @@ static const struct of_device_id rzt2h_pinctrl_of_table[] = {
{ /* sentinel */ }
};
static int rzt2h_pinctrl_suspend_noirq(struct device *dev)
{
struct rzt2h_pinctrl *pctrl = dev_get_drvdata(dev);
if (atomic_read(&pctrl->wakeup_path))
device_set_wakeup_path(dev);
return 0;
}
static const struct dev_pm_ops rzt2h_pinctrl_pm_ops = {
NOIRQ_SYSTEM_SLEEP_PM_OPS(rzt2h_pinctrl_suspend_noirq, NULL)
};
static struct platform_driver rzt2h_pinctrl_driver = {
.driver = {
.name = DRV_NAME,
.of_match_table = of_match_ptr(rzt2h_pinctrl_of_table),
.pm = pm_sleep_ptr(&rzt2h_pinctrl_pm_ops),
.suppress_bind_attrs = true,
},
.probe = rzt2h_pinctrl_probe,