From: Sander Vanheule Date: Thu, 14 Apr 2022 19:57:54 +0000 (+0200) Subject: realtek: replace RTL93xx GPIO patches X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=af6cd37f42f39fc240eef08930663cc2fdc7ac3d;p=openwrt%2Fstaging%2Fldir.git realtek: replace RTL93xx GPIO patches 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 --- diff --git a/target/linux/realtek/patches-5.10/021-v5.19-02-gpio-realtek-otto-Support-reversed-port-layouts.patch b/target/linux/realtek/patches-5.10/021-v5.19-02-gpio-realtek-otto-Support-reversed-port-layouts.patch new file mode 100644 index 0000000000..8f35351438 --- /dev/null +++ b/target/linux/realtek/patches-5.10/021-v5.19-02-gpio-realtek-otto-Support-reversed-port-layouts.patch @@ -0,0 +1,123 @@ +From 512c5be35223d9baa2629efa1084cf5210eaee80 Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +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 +Signed-off-by: Bartosz Golaszewski +--- + 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; 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 index 0000000000..8f5d571ea2 --- /dev/null +++ b/target/linux/realtek/patches-5.10/021-v5.19-03-gpio-realtek-otto-Support-per-cpu-interrupts.patch @@ -0,0 +1,153 @@ +From 95fa6dbe58f286a8f87cb37b7516232eb678de2d Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +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 +Signed-off-by: Bartosz Golaszewski +--- + 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 ++#include + #include + #include + #include +@@ -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); + } + diff --git a/target/linux/realtek/patches-5.10/021-v5.19-04-gpio-realtek-otto-Add-RTL930x-support.patch b/target/linux/realtek/patches-5.10/021-v5.19-04-gpio-realtek-otto-Add-RTL930x-support.patch new file mode 100644 index 0000000000..536c85740a --- /dev/null +++ b/target/linux/realtek/patches-5.10/021-v5.19-04-gpio-realtek-otto-Add-RTL930x-support.patch @@ -0,0 +1,29 @@ +From deaf1cecdeb052cdb5e92fd642016198724b44a4 Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +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 +Signed-off-by: Bartosz Golaszewski +--- + 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); diff --git a/target/linux/realtek/patches-5.10/021-v5.19-06-gpio-realtek-otto-Add-RTL931x-support.patch b/target/linux/realtek/patches-5.10/021-v5.19-06-gpio-realtek-otto-Add-RTL931x-support.patch new file mode 100644 index 0000000000..16990ce053 --- /dev/null +++ b/target/linux/realtek/patches-5.10/021-v5.19-06-gpio-realtek-otto-Add-RTL931x-support.patch @@ -0,0 +1,30 @@ +From d3bf3dc4bbbf6109bd9b4bd60089d36205ec4a37 Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +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 +Signed-off-by: Bartosz Golaszewski +--- + 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); diff --git a/target/linux/realtek/patches-5.10/320-gpio-add-support-for-RTL930X-and-RTL931X.patch b/target/linux/realtek/patches-5.10/320-gpio-add-support-for-RTL930X-and-RTL931X.patch deleted file mode 100644 index bd4639433d..0000000000 --- a/target/linux/realtek/patches-5.10/320-gpio-add-support-for-RTL930X-and-RTL931X.patch +++ /dev/null @@ -1,165 +0,0 @@ ---- 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); - } -