mirror of
https://github.com/torvalds/linux.git
synced 2026-05-23 06:31:58 +02:00
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:
commit
43519f5457
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user