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