forked from Openwrt/openwrt
aa528eec73
Adjust patches for kernel 5.15. Signed-off-by: INAGAKI Hiroshi <musashino.open@gmail.com>
160 lines
5.3 KiB
Diff
160 lines
5.3 KiB
Diff
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);
|
|
|
|
@@ -285,9 +321,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);
|
|
+ }
|
|
}
|
|
}
|
|
|