123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- From 95fa6dbe58f286a8f87cb37b7516232eb678de2d Mon Sep 17 00:00:00 2001
- From: Sander Vanheule <[email protected]>
- Date: Sat, 9 Apr 2022 21:55:48 +0200
- Subject: [PATCH 3/6] gpio: realtek-otto: Support per-cpu interrupts
- On SoCs with multiple cores, it is possible that the GPIO interrupt
- controller supports assigning specific pins to one or more cores.
- IRQ balancing can be performed on a line-by-line basis if the parent
- interrupt is routed to all available cores, which is the default upon
- initialisation.
- Signed-off-by: Sander Vanheule <[email protected]>
- Signed-off-by: Bartosz Golaszewski <[email protected]>
- ---
- drivers/gpio/gpio-realtek-otto.c | 75 +++++++++++++++++++++++++++++++-
- 1 file changed, 74 insertions(+), 1 deletion(-)
- --- a/drivers/gpio/gpio-realtek-otto.c
- +++ b/drivers/gpio/gpio-realtek-otto.c
- @@ -1,6 +1,7 @@
- // SPDX-License-Identifier: GPL-2.0-only
-
- #include <linux/gpio/driver.h>
- +#include <linux/cpumask.h>
- #include <linux/irq.h>
- #include <linux/minmax.h>
- #include <linux/mod_devicetable.h>
- @@ -55,6 +56,8 @@
- struct realtek_gpio_ctrl {
- struct gpio_chip gc;
- void __iomem *base;
- + void __iomem *cpumask_base;
- + struct cpumask cpu_irq_maskable;
- raw_spinlock_t lock;
- u16 intr_mask[REALTEK_GPIO_PORTS_PER_BANK];
- u16 intr_type[REALTEK_GPIO_PORTS_PER_BANK];
- @@ -76,6 +79,11 @@ enum realtek_gpio_flags {
- * fields, and [BA, DC] for 2-bit fields.
- */
- GPIO_PORTS_REVERSED = BIT(1),
- + /*
- + * Interrupts can be enabled per cpu. This requires a secondary IO
- + * range, where the per-cpu enable masks are located.
- + */
- + GPIO_INTERRUPTS_PER_CPU = BIT(2),
- };
-
- static struct realtek_gpio_ctrl *irq_data_to_ctrl(struct irq_data *data)
- @@ -247,14 +255,61 @@ static void realtek_gpio_irq_handler(str
- chained_irq_exit(irq_chip, desc);
- }
-
- +static inline void __iomem *realtek_gpio_irq_cpu_mask(struct realtek_gpio_ctrl *ctrl,
- + unsigned int port, int cpu)
- +{
- + return ctrl->cpumask_base + ctrl->port_offset_u8(port) +
- + REALTEK_GPIO_PORTS_PER_BANK * cpu;
- +}
- +
- +static int realtek_gpio_irq_set_affinity(struct irq_data *data,
- + const struct cpumask *dest, bool force)
- +{
- + struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
- + unsigned int line = irqd_to_hwirq(data);
- + unsigned int port = line / 8;
- + unsigned int port_pin = line % 8;
- + void __iomem *irq_cpu_mask;
- + unsigned long flags;
- + int cpu;
- + u8 v;
- +
- + if (!ctrl->cpumask_base)
- + return -ENXIO;
- +
- + raw_spin_lock_irqsave(&ctrl->lock, flags);
- +
- + for_each_cpu(cpu, &ctrl->cpu_irq_maskable) {
- + irq_cpu_mask = realtek_gpio_irq_cpu_mask(ctrl, port, cpu);
- + v = ioread8(irq_cpu_mask);
- +
- + if (cpumask_test_cpu(cpu, dest))
- + v |= BIT(port_pin);
- + else
- + v &= ~BIT(port_pin);
- +
- + iowrite8(v, irq_cpu_mask);
- + }
- +
- + raw_spin_unlock_irqrestore(&ctrl->lock, flags);
- +
- + irq_data_update_effective_affinity(data, dest);
- +
- + return 0;
- +}
- +
- static int realtek_gpio_irq_init(struct gpio_chip *gc)
- {
- struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc);
- unsigned int port;
- + int cpu;
-
- for (port = 0; (port * 8) < gc->ngpio; port++) {
- realtek_gpio_write_imr(ctrl, port, 0, 0);
- realtek_gpio_clear_isr(ctrl, port, GENMASK(7, 0));
- +
- + for_each_cpu(cpu, &ctrl->cpu_irq_maskable)
- + iowrite8(GENMASK(7, 0), realtek_gpio_irq_cpu_mask(ctrl, port, cpu));
- }
-
- return 0;
- @@ -266,6 +321,7 @@ static struct irq_chip realtek_gpio_irq_
- .irq_mask = realtek_gpio_irq_mask,
- .irq_unmask = realtek_gpio_irq_unmask,
- .irq_set_type = realtek_gpio_irq_set_type,
- + .irq_set_affinity = realtek_gpio_irq_set_affinity,
- };
-
- static const struct of_device_id realtek_gpio_of_match[] = {
- @@ -290,8 +346,10 @@ static int realtek_gpio_probe(struct pla
- unsigned int dev_flags;
- struct gpio_irq_chip *girq;
- struct realtek_gpio_ctrl *ctrl;
- + struct resource *res;
- u32 ngpios;
- - int err, irq;
- + unsigned int nr_cpus;
- + int cpu, err, irq;
-
- ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
- if (!ctrl)
- @@ -352,6 +410,21 @@ static int realtek_gpio_probe(struct pla
- girq->init_hw = realtek_gpio_irq_init;
- }
-
- + cpumask_clear(&ctrl->cpu_irq_maskable);
- +
- + if ((dev_flags & GPIO_INTERRUPTS_PER_CPU) && irq > 0) {
- + ctrl->cpumask_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res);
- + if (IS_ERR(ctrl->cpumask_base))
- + return dev_err_probe(dev, PTR_ERR(ctrl->cpumask_base),
- + "missing CPU IRQ mask registers");
- +
- + nr_cpus = resource_size(res) / REALTEK_GPIO_PORTS_PER_BANK;
- + nr_cpus = min(nr_cpus, num_present_cpus());
- +
- + for (cpu = 0; cpu < nr_cpus; cpu++)
- + cpumask_set_cpu(cpu, &ctrl->cpu_irq_maskable);
- + }
- +
- return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl);
- }
-
|