mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-20 14:13:16 +00:00
af6cd37f42
Patches to support the SoC's GPIO controller for RTL930x and RTL931x devices have been accepted upstream. Replace the current preliminary patch with the upstream ones, excluding devictree binding changes. The updated patches add GPIO IRQ balancing support on RTL930x, but this cannot be used until these devices also support SMP. Signed-off-by: Sander Vanheule <sander@svanheule.net>
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);
|
|
}
|
|
|