--- /dev/null
+From 512c5be35223d9baa2629efa1084cf5210eaee80 Mon Sep 17 00:00:00 2001
+From: Sander Vanheule <sander@svanheule.net>
+Date: Sat, 9 Apr 2022 21:55:47 +0200
+Subject: [PATCH 2/6] gpio: realtek-otto: Support reversed port layouts
+
+The GPIO port layout on the RTL930x SoC series is reversed compared to
+the RTL838x and RTL839x SoC series. Add new port offset calculator
+functions to ensure the correct order is used when reading port IRQ
+data, and ensure bgpio uses the right byte ordering.
+
+Signed-off-by: Sander Vanheule <sander@svanheule.net>
+Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
+---
+ drivers/gpio/gpio-realtek-otto.c | 55 +++++++++++++++++++++++++++++---
+ 1 file changed, 51 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpio/gpio-realtek-otto.c
++++ b/drivers/gpio/gpio-realtek-otto.c
+@@ -58,6 +58,8 @@ struct realtek_gpio_ctrl {
+ raw_spinlock_t lock;
+ u16 intr_mask[REALTEK_GPIO_PORTS_PER_BANK];
+ u16 intr_type[REALTEK_GPIO_PORTS_PER_BANK];
++ unsigned int (*port_offset_u8)(unsigned int port);
++ unsigned int (*port_offset_u16)(unsigned int port);
+ };
+
+ /* Expand with more flags as devices with other quirks are added */
+@@ -69,6 +71,11 @@ enum realtek_gpio_flags {
+ * line the IRQ handler was assigned to, causing uncaught interrupts.
+ */
+ GPIO_INTERRUPTS_DISABLED = BIT(0),
++ /*
++ * Port order is reversed, meaning DCBA register layout for 1-bit
++ * fields, and [BA, DC] for 2-bit fields.
++ */
++ GPIO_PORTS_REVERSED = BIT(1),
+ };
+
+ static struct realtek_gpio_ctrl *irq_data_to_ctrl(struct irq_data *data)
+@@ -86,21 +93,50 @@ static struct realtek_gpio_ctrl *irq_dat
+ * port. The two interrupt mask registers store two bits per GPIO, so use u16
+ * values.
+ */
++static unsigned int realtek_gpio_port_offset_u8(unsigned int port)
++{
++ return port;
++}
++
++static unsigned int realtek_gpio_port_offset_u16(unsigned int port)
++{
++ return 2 * port;
++}
++
++/*
++ * Reversed port order register access
++ *
++ * For registers with one bit per GPIO, all ports are stored as u8-s in one
++ * register in reversed order. The two interrupt mask registers store two bits
++ * per GPIO, so use u16 values. The first register contains ports 1 and 0, the
++ * second ports 3 and 2.
++ */
++static unsigned int realtek_gpio_port_offset_u8_rev(unsigned int port)
++{
++ return 3 - port;
++}
++
++static unsigned int realtek_gpio_port_offset_u16_rev(unsigned int port)
++{
++ return 2 * (port ^ 1);
++}
++
+ static void realtek_gpio_write_imr(struct realtek_gpio_ctrl *ctrl,
+ unsigned int port, u16 irq_type, u16 irq_mask)
+ {
+- iowrite16(irq_type & irq_mask, ctrl->base + REALTEK_GPIO_REG_IMR + 2 * port);
++ iowrite16(irq_type & irq_mask,
++ ctrl->base + REALTEK_GPIO_REG_IMR + ctrl->port_offset_u16(port));
+ }
+
+ static void realtek_gpio_clear_isr(struct realtek_gpio_ctrl *ctrl,
+ unsigned int port, u8 mask)
+ {
+- iowrite8(mask, ctrl->base + REALTEK_GPIO_REG_ISR + port);
++ iowrite8(mask, ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port));
+ }
+
+ static u8 realtek_gpio_read_isr(struct realtek_gpio_ctrl *ctrl, unsigned int port)
+ {
+- return ioread8(ctrl->base + REALTEK_GPIO_REG_ISR + port);
++ return ioread8(ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port));
+ }
+
+ /* Set the rising and falling edge mask bits for a GPIO port pin */
+@@ -253,6 +289,7 @@ MODULE_DEVICE_TABLE(of, realtek_gpio_of_
+ static int realtek_gpio_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
++ unsigned long bgpio_flags;
+ unsigned int dev_flags;
+ struct gpio_irq_chip *girq;
+ struct realtek_gpio_ctrl *ctrl;
+@@ -280,10 +317,20 @@ static int realtek_gpio_probe(struct pla
+
+ raw_spin_lock_init(&ctrl->lock);
+
++ if (dev_flags & GPIO_PORTS_REVERSED) {
++ bgpio_flags = 0;
++ ctrl->port_offset_u8 = realtek_gpio_port_offset_u8_rev;
++ ctrl->port_offset_u16 = realtek_gpio_port_offset_u16_rev;
++ } else {
++ bgpio_flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER;
++ ctrl->port_offset_u8 = realtek_gpio_port_offset_u8;
++ ctrl->port_offset_u16 = realtek_gpio_port_offset_u16;
++ }
++
+ err = bgpio_init(&ctrl->gc, dev, 4,
+ ctrl->base + REALTEK_GPIO_REG_DATA, NULL, NULL,
+ ctrl->base + REALTEK_GPIO_REG_DIR, NULL,
+- BGPIOF_BIG_ENDIAN_BYTE_ORDER);
++ bgpio_flags);
+ if (err) {
+ dev_err(dev, "unable to init generic GPIO");
+ return err;
--- /dev/null
+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);
+ }
+
--- /dev/null
+From deaf1cecdeb052cdb5e92fd642016198724b44a4 Mon Sep 17 00:00:00 2001
+From: Sander Vanheule <sander@svanheule.net>
+Date: Sat, 9 Apr 2022 21:55:49 +0200
+Subject: [PATCH 4/6] gpio: realtek-otto: Add RTL930x support
+
+The RTL930x SoC series has support for 24 GPIOs, with the port order
+reversed compared to RTL838x and RTL839x. The RTL930x series also has
+two CPUs (VPEs) and can distribute individual GPIO interrupts between
+them.
+
+Signed-off-by: Sander Vanheule <sander@svanheule.net>
+Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
+---
+ drivers/gpio/gpio-realtek-otto.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/gpio/gpio-realtek-otto.c
++++ b/drivers/gpio/gpio-realtek-otto.c
+@@ -338,6 +338,10 @@ static const struct of_device_id realtek
+ {
+ .compatible = "realtek,rtl8390-gpio",
+ },
++ {
++ .compatible = "realtek,rtl9300-gpio",
++ .data = (void *)(GPIO_PORTS_REVERSED | GPIO_INTERRUPTS_PER_CPU)
++ },
+ {}
+ };
+ MODULE_DEVICE_TABLE(of, realtek_gpio_of_match);
--- /dev/null
+From d3bf3dc4bbbf6109bd9b4bd60089d36205ec4a37 Mon Sep 17 00:00:00 2001
+From: Sander Vanheule <sander@svanheule.net>
+Date: Sat, 9 Apr 2022 21:55:51 +0200
+Subject: [PATCH 6/6] gpio: realtek-otto: Add RTL931x support
+
+The RTL931x SoC series has support for 32 GPIOs, although not all lines
+may be broken out to a physical pad.
+
+The GPIO bank's parent interrupt can be routed to either or both of the
+SoC's CPU cores by the GIC. Line-by-line IRQ balancing is not possible
+on these SoCs.
+
+Signed-off-by: Sander Vanheule <sander@svanheule.net>
+Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
+---
+ drivers/gpio/gpio-realtek-otto.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/gpio/gpio-realtek-otto.c
++++ b/drivers/gpio/gpio-realtek-otto.c
+@@ -342,6 +342,9 @@ static const struct of_device_id realtek
+ .compatible = "realtek,rtl9300-gpio",
+ .data = (void *)(GPIO_PORTS_REVERSED | GPIO_INTERRUPTS_PER_CPU)
+ },
++ {
++ .compatible = "realtek,rtl9310-gpio",
++ },
+ {}
+ };
+ MODULE_DEVICE_TABLE(of, realtek_gpio_of_match);
+++ /dev/null
---- a/drivers/gpio/gpio-realtek-otto.c
-+++ b/drivers/gpio/gpio-realtek-otto.c
-@@ -55,9 +55,12 @@
- struct realtek_gpio_ctrl {
- struct gpio_chip gc;
- void __iomem *base;
-+ void __iomem *cpumap_base;
- raw_spinlock_t lock;
- u16 intr_mask[REALTEK_GPIO_PORTS_PER_BANK];
- u16 intr_type[REALTEK_GPIO_PORTS_PER_BANK];
-+ unsigned int (*port_offset_u8)(unsigned int port);
-+ unsigned int (*port_offset_u16)(unsigned int port);
- };
-
- /* Expand with more flags as devices with other quirks are added */
-@@ -69,6 +72,16 @@ enum realtek_gpio_flags {
- * line the IRQ handler was assigned to, causing uncaught interrupts.
- */
- GPIO_INTERRUPTS_DISABLED = BIT(0),
-+ /*
-+ * Port order is reversed, meaning DCBA register layout for 1-bit
-+ * 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)
-@@ -86,21 +99,50 @@ static struct realtek_gpio_ctrl *irq_dat
- * port. The two interrupt mask registers store two bits per GPIO, so use u16
- * values.
- */
-+static unsigned int realtek_gpio_port_offset_u8(unsigned int port)
-+{
-+ return port;
-+}
-+
-+static unsigned int realtek_gpio_port_offset_u16(unsigned int port)
-+{
-+ return 2 * port;
-+}
-+
-+/*
-+ * Reversed port order register access
-+ *
-+ * For registers with one bit per GPIO, all ports are stored as u8-s in one
-+ * register in reversed order. The two interrupt mask registers store two bits
-+ * per GPIO, so use u16 values. The first register contains ports 1 and 0, the
-+ * second ports 3 and 2.
-+ */
-+static unsigned int realtek_gpio_port_offset_u8_rev(unsigned int port)
-+{
-+ return 3 - port;
-+}
-+
-+static unsigned int realtek_gpio_port_offset_u16_rev(unsigned int port)
-+{
-+ return 2 * (port ^ 1);
-+}
-+
- static void realtek_gpio_write_imr(struct realtek_gpio_ctrl *ctrl,
- unsigned int port, u16 irq_type, u16 irq_mask)
- {
-- iowrite16(irq_type & irq_mask, ctrl->base + REALTEK_GPIO_REG_IMR + 2 * port);
-+ iowrite16(irq_type & irq_mask,
-+ ctrl->base + REALTEK_GPIO_REG_IMR + ctrl->port_offset_u16(port));
- }
-
- static void realtek_gpio_clear_isr(struct realtek_gpio_ctrl *ctrl,
- unsigned int port, u8 mask)
- {
-- iowrite8(mask, ctrl->base + REALTEK_GPIO_REG_ISR + port);
-+ iowrite8(mask, ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port));
- }
-
- static u8 realtek_gpio_read_isr(struct realtek_gpio_ctrl *ctrl, unsigned int port)
- {
-- return ioread8(ctrl->base + REALTEK_GPIO_REG_ISR + port);
-+ return ioread8(ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port));
- }
-
- /* Set the rising and falling edge mask bits for a GPIO port pin */
-@@ -222,6 +264,12 @@ static int realtek_gpio_irq_init(struct
- 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));
-+
-+ if (ctrl->cpumap_base) {
-+ /* Default CPU affinity to the first CPU */
-+ iowrite8(GENMASK(7, 0),
-+ ctrl->cpumap_base + ctrl->port_offset_u8(port));
-+ }
- }
-
- return 0;
-@@ -246,6 +294,13 @@ static const struct of_device_id realtek
- {
- .compatible = "realtek,rtl8390-gpio",
- },
-+ {
-+ .compatible = "realtek,rtl9300-gpio",
-+ .data = (void *)(GPIO_PORTS_REVERSED | GPIO_INTERRUPTS_PER_CPU)
-+ },
-+ {
-+ .compatible = "realtek,rtl9310-gpio",
-+ },
- {}
- };
- MODULE_DEVICE_TABLE(of, realtek_gpio_of_match);
-@@ -253,12 +308,14 @@ MODULE_DEVICE_TABLE(of, realtek_gpio_of_
- static int realtek_gpio_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
-+ unsigned long bgpio_flags;
- unsigned int dev_flags;
- struct gpio_irq_chip *girq;
- struct realtek_gpio_ctrl *ctrl;
- u32 ngpios;
- int err, irq;
-
-+ pr_info("%s probing RTL GPIO\n", __func__);
- ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
- if (!ctrl)
- return -ENOMEM;
-@@ -280,10 +337,21 @@ static int realtek_gpio_probe(struct pla
-
- raw_spin_lock_init(&ctrl->lock);
-
-+ if (dev_flags & GPIO_PORTS_REVERSED) {
-+ bgpio_flags = 0;
-+ ctrl->port_offset_u8 = realtek_gpio_port_offset_u8_rev;
-+ ctrl->port_offset_u16 = realtek_gpio_port_offset_u16_rev;
-+ }
-+ else {
-+ bgpio_flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER;
-+ ctrl->port_offset_u8 = realtek_gpio_port_offset_u8;
-+ ctrl->port_offset_u16 = realtek_gpio_port_offset_u16;
-+ }
-+
- err = bgpio_init(&ctrl->gc, dev, 4,
- ctrl->base + REALTEK_GPIO_REG_DATA, NULL, NULL,
- ctrl->base + REALTEK_GPIO_REG_DIR, NULL,
-- BGPIOF_BIG_ENDIAN_BYTE_ORDER);
-+ bgpio_flags);
- if (err) {
- dev_err(dev, "unable to init generic GPIO");
- return err;
-@@ -308,6 +376,13 @@ static int realtek_gpio_probe(struct pla
- girq->init_hw = realtek_gpio_irq_init;
- }
-
-+ if (dev_flags & GPIO_INTERRUPTS_PER_CPU) {
-+ ctrl->cpumap_base = devm_platform_ioremap_resource(pdev, 1);
-+ if (IS_ERR(ctrl->cpumap_base))
-+ return dev_err_probe(dev, PTR_ERR(ctrl->cpumap_base),
-+ "IRQ CPU map registers not defined");
-+ }
-+
- return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl);
- }
-