Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

irqchip: mips-cpu: Introduce IPI IRQ domain support

Introduce support for registering an IPI IRQ domain suitable for use by
systems using the MIPS MT (multithreading) ASE within a single core.
This will allow for such systems to be supported generically, without
the current kludge of IPI code split between the MIPS arch & the malta
board support code.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Acked-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/15836/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

authored by

Paul Burton and committed by
Ralf Baechle
3838a547 131735af

+119 -8
+2
drivers/irqchip/Kconfig
··· 128 128 config IRQ_MIPS_CPU 129 129 bool 130 130 select GENERIC_IRQ_CHIP 131 + select GENERIC_IRQ_IPI if SYS_SUPPORTS_MULTITHREADING 131 132 select IRQ_DOMAIN 133 + select IRQ_DOMAIN_HIERARCHY if GENERIC_IRQ_IPI 132 134 133 135 config CLPS711X_IRQCHIP 134 136 bool
+117 -8
drivers/irqchip/irq-mips-cpu.c
··· 17 17 /* 18 18 * Almost all MIPS CPUs define 8 interrupt sources. They are typically 19 19 * level triggered (i.e., cannot be cleared from CPU; must be cleared from 20 - * device). The first two are software interrupts which we don't really 21 - * use or support. The last one is usually the CPU timer interrupt if 22 - * counter register is present or, for CPUs with an external FPU, by 23 - * convention it's the FPU exception interrupt. 20 + * device). 24 21 * 25 - * Don't even think about using this on SMP. You have been warned. 22 + * The first two are software interrupts (i.e. not exposed as pins) which 23 + * may be used for IPIs in multi-threaded single-core systems. 26 24 * 27 - * This file exports one global function: 28 - * void mips_cpu_irq_init(void); 25 + * The last one is usually the CPU timer interrupt if the counter register 26 + * is present, or for old CPUs with an external FPU by convention it's the 27 + * FPU exception interrupt. 29 28 */ 30 29 #include <linux/init.h> 31 30 #include <linux/interrupt.h> ··· 39 40 #include <asm/setup.h> 40 41 41 42 static struct irq_domain *irq_domain; 43 + static struct irq_domain *ipi_domain; 42 44 43 45 static inline void unmask_mips_irq(struct irq_data *d) 44 46 { ··· 90 90 mask_mips_irq(d); 91 91 } 92 92 93 + #ifdef CONFIG_GENERIC_IRQ_IPI 94 + 95 + static void mips_mt_send_ipi(struct irq_data *d, unsigned int cpu) 96 + { 97 + irq_hw_number_t hwirq = irqd_to_hwirq(d); 98 + unsigned long flags; 99 + int vpflags; 100 + 101 + local_irq_save(flags); 102 + 103 + /* We can only send IPIs to VPEs within the local core */ 104 + WARN_ON(cpu_data[cpu].core != current_cpu_data.core); 105 + 106 + vpflags = dvpe(); 107 + settc(cpu_vpe_id(&cpu_data[cpu])); 108 + write_vpe_c0_cause(read_vpe_c0_cause() | (C_SW0 << hwirq)); 109 + evpe(vpflags); 110 + 111 + local_irq_restore(flags); 112 + } 113 + 114 + #endif /* CONFIG_GENERIC_IRQ_IPI */ 115 + 93 116 static struct irq_chip mips_mt_cpu_irq_controller = { 94 117 .name = "MIPS", 95 118 .irq_startup = mips_mt_cpu_irq_startup, ··· 123 100 .irq_eoi = unmask_mips_irq, 124 101 .irq_disable = mask_mips_irq, 125 102 .irq_enable = unmask_mips_irq, 103 + #ifdef CONFIG_GENERIC_IRQ_IPI 104 + .ipi_send_single = mips_mt_send_ipi, 105 + #endif 126 106 }; 127 107 128 108 asmlinkage void __weak plat_irq_dispatch(void) ··· 142 116 pending >>= CAUSEB_IP; 143 117 while (pending) { 144 118 irq = fls(pending) - 1; 145 - virq = irq_linear_revmap(irq_domain, irq); 119 + if (IS_ENABLED(CONFIG_GENERIC_IRQ_IPI) && irq < 2) 120 + virq = irq_linear_revmap(ipi_domain, irq); 121 + else 122 + virq = irq_linear_revmap(irq_domain, irq); 146 123 do_IRQ(virq); 147 124 pending &= ~BIT(irq); 148 125 } ··· 176 147 .xlate = irq_domain_xlate_onecell, 177 148 }; 178 149 150 + #ifdef CONFIG_GENERIC_IRQ_IPI 151 + 152 + struct cpu_ipi_domain_state { 153 + DECLARE_BITMAP(allocated, 2); 154 + }; 155 + 156 + static int mips_cpu_ipi_alloc(struct irq_domain *domain, unsigned int virq, 157 + unsigned int nr_irqs, void *arg) 158 + { 159 + struct cpu_ipi_domain_state *state = domain->host_data; 160 + unsigned int i, hwirq; 161 + int ret; 162 + 163 + for (i = 0; i < nr_irqs; i++) { 164 + hwirq = find_first_zero_bit(state->allocated, 2); 165 + if (hwirq == 2) 166 + return -EBUSY; 167 + bitmap_set(state->allocated, hwirq, 1); 168 + 169 + ret = irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq, 170 + &mips_mt_cpu_irq_controller, 171 + NULL); 172 + if (ret) 173 + return ret; 174 + 175 + ret = irq_set_irq_type(virq + i, IRQ_TYPE_LEVEL_HIGH); 176 + if (ret) 177 + return ret; 178 + } 179 + 180 + return 0; 181 + } 182 + 183 + static int mips_cpu_ipi_match(struct irq_domain *d, struct device_node *node, 184 + enum irq_domain_bus_token bus_token) 185 + { 186 + bool is_ipi; 187 + 188 + switch (bus_token) { 189 + case DOMAIN_BUS_IPI: 190 + is_ipi = d->bus_token == bus_token; 191 + return (!node || (to_of_node(d->fwnode) == node)) && is_ipi; 192 + default: 193 + return 0; 194 + } 195 + } 196 + 197 + static const struct irq_domain_ops mips_cpu_ipi_chip_ops = { 198 + .alloc = mips_cpu_ipi_alloc, 199 + .match = mips_cpu_ipi_match, 200 + }; 201 + 202 + static void mips_cpu_register_ipi_domain(struct device_node *of_node) 203 + { 204 + struct cpu_ipi_domain_state *ipi_domain_state; 205 + 206 + ipi_domain_state = kzalloc(sizeof(*ipi_domain_state), GFP_KERNEL); 207 + ipi_domain = irq_domain_add_hierarchy(irq_domain, 208 + IRQ_DOMAIN_FLAG_IPI_SINGLE, 209 + 2, of_node, 210 + &mips_cpu_ipi_chip_ops, 211 + ipi_domain_state); 212 + if (!ipi_domain) 213 + panic("Failed to add MIPS CPU IPI domain"); 214 + ipi_domain->bus_token = DOMAIN_BUS_IPI; 215 + } 216 + 217 + #else /* !CONFIG_GENERIC_IRQ_IPI */ 218 + 219 + static inline void mips_cpu_register_ipi_domain(struct device_node *of_node) {} 220 + 221 + #endif /* !CONFIG_GENERIC_IRQ_IPI */ 222 + 179 223 static void __init __mips_cpu_irq_init(struct device_node *of_node) 180 224 { 181 225 /* Mask interrupts. */ ··· 260 158 NULL); 261 159 if (!irq_domain) 262 160 panic("Failed to add irqdomain for MIPS CPU"); 161 + 162 + /* 163 + * Only proceed to register the software interrupt IPI implementation 164 + * for CPUs which implement the MIPS MT (multi-threading) ASE. 165 + */ 166 + if (cpu_has_mipsmt) 167 + mips_cpu_register_ipi_domain(of_node); 263 168 } 264 169 265 170 void __init mips_cpu_irq_init(void)