realtek: replace RTL93xx GPIO patches
[openwrt/staging/noltari.git] / target / linux / realtek / patches-5.10 / 021-v5.19-03-gpio-realtek-otto-Support-per-cpu-interrupts.patch
diff --git a/target/linux/realtek/patches-5.10/021-v5.19-03-gpio-realtek-otto-Support-per-cpu-interrupts.patch b/target/linux/realtek/patches-5.10/021-v5.19-03-gpio-realtek-otto-Support-per-cpu-interrupts.patch
new file mode 100644 (file)
index 0000000..8f5d571
--- /dev/null
@@ -0,0 +1,153 @@
+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);
+ }