diff options
Diffstat (limited to 'target/linux/realtek/patches-5.15/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch')
-rw-r--r-- | target/linux/realtek/patches-5.15/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/target/linux/realtek/patches-5.15/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch b/target/linux/realtek/patches-5.15/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch new file mode 100644 index 0000000000..bb95e7103a --- /dev/null +++ b/target/linux/realtek/patches-5.15/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch @@ -0,0 +1,159 @@ +From 2cd00b51470a30198b048a5fca48a04db77e29cc Mon Sep 17 00:00:00 2001 +From: INAGAKI Hiroshi <musashino.open@gmail.com> +Date: Fri, 21 May 2021 23:16:37 +0900 +Subject: [PATCH] realtek: backport irq-realtek-rtl driver from 5.12 to 5.10 + +This patch backports "irq-realtek-rtl" driver to Kernel 5.10 from 5.12. +"MACH_REALTEK_RTL" is used as a platform name in upstream, but "RTL838X" +is used in OpenWrt, so update the dependency by the additional patch. + +Submitted-by: INAGAKI Hiroshi <musashino.open@gmail.com> +--- + drivers/irqchip/irq-realtek-rtl.c | 38 +++++++++++------ + 1 files changed, 58 insertions(+), 20 deletions(-) + +--- 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 __iome + 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(stru + 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_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 + + 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(st + 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); ++ } + } + } + |