mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-20 14:13:16 +00:00
154 lines
4.5 KiB
Diff
154 lines
4.5 KiB
Diff
|
From 95fa6dbe58f286a8f87cb37b7516232eb678de2d Mon Sep 17 00:00:00 2001
|
||
|
From: Sander Vanheule <sander@svanheule.net>
|
||
|
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 <sander@svanheule.net>
|
||
|
Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
|
||
|
---
|
||
|
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)
|
||
|
@@ -250,14 +258,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;
|
||
|
@@ -269,6 +324,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[] = {
|
||
|
@@ -293,8 +349,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)
|
||
|
@@ -355,6 +413,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);
|
||
|
}
|
||
|
|