aboutsummaryrefslogtreecommitdiffstats
path: root/target
diff options
context:
space:
mode:
Diffstat (limited to 'target')
-rw-r--r--target/linux/realtek/patches-5.10/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch145
1 files changed, 145 insertions, 0 deletions
diff --git a/target/linux/realtek/patches-5.10/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch b/target/linux/realtek/patches-5.10/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch
new file mode 100644
index 0000000000..0f4ab38d4d
--- /dev/null
+++ b/target/linux/realtek/patches-5.10/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch
@@ -0,0 +1,145 @@
+--- a/drivers/irqchip/irq-realtek-rtl.c
++++ b/drivers/irqchip/irq-realtek-rtl.c
+@@ -28,6 +28,7 @@ static DEFINE_RAW_SPINLOCK(irq_lock);
+
+ #define REG(offset, cpu) (realtek_ictl_base[cpu] + offset)
+
++static u32 realtek_ictl_unmask[NR_CPUS];
+ static void __iomem *realtek_ictl_base[NR_CPUS];
+ static cpumask_t realtek_ictl_cpu_configurable;
+
+@@ -41,11 +42,29 @@ struct realtek_ictl_output {
+ };
+
+ /*
+- * IRR0-IRR3 store 4 bits per interrupt, but Realtek uses inverted numbering,
+- * placing IRQ 31 in the first four bits. A routing value of '0' means the
+- * interrupt is left disconnected. Routing values {1..15} connect to output
+- * lines {0..14}.
++ * Per CPU we have a set of 5 registers that determine interrupt handling for
++ * 32 external interrupts. GIMR (enable/disable interrupt) plus IRR0-IRR3 that
++ * contain "routing" or "priority" values. GIMR uses one bit for each interrupt
++ * and IRRx store 4 bits per interrupt. Realtek uses inverted numbering,
++ * placing IRQ 31 in the first four bits. The register combinations give the
++ * following results for a single interrupt in the wild:
++ *
++ * a) GIMR = 0 / IRRx > 0 -> no interrupts
++ * b) GIMR = 0 / IRRx = 0 -> no interrupts
++ * c) GIMR = 1 / IRRx > 0 -> interrupts
++ * d) GIMR = 1 / IRRx = 0 -> rare interrupts in SMP environment
++ *
++ * Combination d) seems to trigger interrupts only on a VPE if the other VPE
++ * has GIMR = 0 and IRRx > 0. E.g. busy without interrupts allowed. To provide
++ * IRQ balancing features in SMP this driver will handle the registers as
++ * follows:
++ *
++ * 1) set IRRx > 0 for VPE where the interrupt is desired
++ * 2) set IRRx = 0 for VPE where the interrupt is not desired
++ * 3) set both GIMR = 0 to mask (disabled) interrupt
++ * 4) set GIMR = 1 to unmask (enable) interrupt but only for VPE where IRRx > 0
+ */
++
+ #define IRR_OFFSET(idx) (4 * (3 - (idx * 4) / 32))
+ #define IRR_SHIFT(idx) ((idx * 4) % 32)
+
+@@ -65,19 +84,33 @@ static inline void write_irr(void __iomem *irr0, int idx, u32 value)
+ writel(irr, irr0 + offset);
+ }
+
++static inline void enable_gimr(int hwirq, int cpu)
++{
++ u32 value;
++
++ value = readl(REG(RTL_ICTL_GIMR, cpu));
++ value |= (BIT(hwirq) & realtek_ictl_unmask[cpu]);
++ writel(value, REG(RTL_ICTL_GIMR, cpu));
++}
++
++static inline void disable_gimr(int hwirq, int cpu)
++{
++ u32 value;
++
++ value = readl(REG(RTL_ICTL_GIMR, cpu));
++ value &= ~BIT(hwirq);
++ writel(value, REG(RTL_ICTL_GIMR, cpu));
++}
++
+ static void realtek_ictl_unmask_irq(struct irq_data *i)
+ {
+ unsigned long flags;
+- u32 value;
+ int cpu;
+
+ raw_spin_lock_irqsave(&irq_lock, flags);
+
+- for_each_cpu(cpu, &realtek_ictl_cpu_configurable) {
+- value = readl(REG(RTL_ICTL_GIMR, cpu));
+- value |= BIT(i->hwirq);
+- writel(value, REG(RTL_ICTL_GIMR, cpu));
+- }
++ for_each_cpu(cpu, &realtek_ictl_cpu_configurable)
++ enable_gimr(i->hwirq, cpu);
+
+ raw_spin_unlock_irqrestore(&irq_lock, flags);
+ }
+@@ -85,16 +118,12 @@ static void realtek_ictl_unmask_irq(struct irq_data *i)
+ static void realtek_ictl_mask_irq(struct irq_data *i)
+ {
+ unsigned long flags;
+- u32 value;
+ int cpu;
+
+ raw_spin_lock_irqsave(&irq_lock, flags);
+
+- for_each_cpu(cpu, &realtek_ictl_cpu_configurable) {
+- value = readl(REG(RTL_ICTL_GIMR, cpu));
+- value &= ~BIT(i->hwirq);
+- writel(value, REG(RTL_ICTL_GIMR, cpu));
+- }
++ for_each_cpu(cpu, &realtek_ictl_cpu_configurable)
++ disable_gimr(i->hwirq, cpu);
+
+ raw_spin_unlock_irqrestore(&irq_lock, flags);
+ }
+@@ -116,11 +145,17 @@ static int __maybe_unused realtek_ictl_irq_affinity(struct irq_data *i,
+ cpumask_and(&cpu_enable, &cpu_configure, dest);
+ cpumask_andnot(&cpu_disable, &cpu_configure, dest);
+
+- for_each_cpu(cpu, &cpu_disable)
++ for_each_cpu(cpu, &cpu_disable) {
+ write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, 0);
++ realtek_ictl_unmask[cpu] &= ~BIT(i->hwirq);
++ disable_gimr(i->hwirq, cpu);
++ }
+
+- for_each_cpu(cpu, &cpu_enable)
++ for_each_cpu(cpu, &cpu_enable) {
+ write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, output->output_index + 1);
++ realtek_ictl_unmask[cpu] |= BIT(i->hwirq);
++ enable_gimr(i->hwirq, cpu);
++ }
+
+ irq_data_update_effective_affinity(i, &cpu_enable);
+
+@@ -149,6 +184,7 @@ static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
+
+ output->child_mask |= BIT(hw);
+ write_irr(REG(RTL_ICTL_IRR0, 0), hw, output->output_index + 1);
++ realtek_ictl_unmask[0] |= BIT(hw);
+
+ raw_spin_unlock_irqrestore(&irq_lock, flags);
+
+@@ -279,9 +315,11 @@ static int __init realtek_rtl_of_init(struct device_node *node, struct device_no
+ cpumask_set_cpu(cpu, &realtek_ictl_cpu_configurable);
+
+ /* Disable all cascaded interrupts and clear routing */
+- writel(0, REG(RTL_ICTL_GIMR, cpu));
+- for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++)
++ for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++) {
+ write_irr(REG(RTL_ICTL_IRR0, cpu), soc_irq, 0);
++ realtek_ictl_unmask[cpu] &= ~BIT(soc_irq);
++ disable_gimr(soc_irq, cpu);
++ }
+ }
+ }
+