Skip to content

Commit

Permalink
realtek: replace RTL93xx GPIO patches
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
svanheule committed May 22, 2022
1 parent 58b3b55 commit af6cd37
Show file tree
Hide file tree
Showing 4 changed files with 234 additions and 64 deletions.
@@ -1,10 +1,22 @@
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
@@ -55,9 +55,12 @@
struct realtek_gpio_ctrl {
struct gpio_chip gc;
void __iomem *base;
+ void __iomem *cpumap_base;
@@ -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];
Expand All @@ -13,7 +25,7 @@
};

/* Expand with more flags as devices with other quirks are added */
@@ -69,6 +72,16 @@ enum realtek_gpio_flags {
@@ -69,6 +71,11 @@ enum realtek_gpio_flags {
* line the IRQ handler was assigned to, causing uncaught interrupts.
*/
GPIO_INTERRUPTS_DISABLED = BIT(0),
Expand All @@ -22,15 +34,10 @@
+ * 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
@@ -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.
*/
Expand Down Expand Up @@ -84,58 +91,23 @@
}

/* 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_
@@ -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;
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
@@ -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 {
+ } 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;
Expand All @@ -149,17 +121,3 @@
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);
}

@@ -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);
}

@@ -0,0 +1,29 @@
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);
@@ -0,0 +1,30 @@
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);

0 comments on commit af6cd37

Please sign in to comment.