1 /* 2 * Cell Internal Interrupt Controller 3 * 4 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 5 * 6 * Author: Arnd Bergmann <arndb@de.ibm.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2, or (at your option) 11 * any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 */ 22 23 #include <linux/config.h> 24 #include <linux/interrupt.h> 25 #include <linux/irq.h> 26 #include <linux/module.h> 27 #include <linux/percpu.h> 28 #include <linux/types.h> 29 30 #include <asm/io.h> 31 #include <asm/pgtable.h> 32 #include <asm/prom.h> 33 #include <asm/ptrace.h> 34 35 #include "interrupt.h" 36 37 struct iic_pending_bits { 38 u32 data; 39 u8 flags; 40 u8 class; 41 u8 source; 42 u8 prio; 43 }; 44 45 enum iic_pending_flags { 46 IIC_VALID = 0x80, 47 IIC_IPI = 0x40, 48 }; 49 50 struct iic_regs { 51 struct iic_pending_bits pending; 52 struct iic_pending_bits pending_destr; 53 u64 generate; 54 u64 prio; 55 }; 56 57 struct iic { 58 struct iic_regs __iomem *regs; 59 u8 target_id; 60 }; 61 62 static DEFINE_PER_CPU(struct iic, iic); 63 64 void iic_local_enable(void) 65 { 66 out_be64(&__get_cpu_var(iic).regs->prio, 0xff); 67 } 68 69 void iic_local_disable(void) 70 { 71 out_be64(&__get_cpu_var(iic).regs->prio, 0x0); 72 } 73 74 static unsigned int iic_startup(unsigned int irq) 75 { 76 return 0; 77 } 78 79 static void iic_enable(unsigned int irq) 80 { 81 iic_local_enable(); 82 } 83 84 static void iic_disable(unsigned int irq) 85 { 86 } 87 88 static void iic_end(unsigned int irq) 89 { 90 iic_local_enable(); 91 } 92 93 static struct hw_interrupt_type iic_pic = { 94 .typename = " CELL-IIC ", 95 .startup = iic_startup, 96 .enable = iic_enable, 97 .disable = iic_disable, 98 .end = iic_end, 99 }; 100 101 static int iic_external_get_irq(struct iic_pending_bits pending) 102 { 103 int irq; 104 unsigned char node, unit; 105 106 node = pending.source >> 4; 107 unit = pending.source & 0xf; 108 irq = -1; 109 110 /* 111 * This mapping is specific to the Cell Broadband 112 * Engine. We might need to get the numbers 113 * from the device tree to support future CPUs. 114 */ 115 switch (unit) { 116 case 0x00: 117 case 0x0b: 118 /* 119 * One of these units can be connected 120 * to an external interrupt controller. 121 */ 122 if (pending.prio > 0x3f || 123 pending.class != 2) 124 break; 125 irq = IIC_EXT_OFFSET 126 + spider_get_irq(pending.prio + node * IIC_NODE_STRIDE) 127 + node * IIC_NODE_STRIDE; 128 break; 129 case 0x01 ... 0x04: 130 case 0x07 ... 0x0a: 131 /* 132 * These units are connected to the SPEs 133 */ 134 if (pending.class > 2) 135 break; 136 irq = IIC_SPE_OFFSET 137 + pending.class * IIC_CLASS_STRIDE 138 + node * IIC_NODE_STRIDE 139 + unit; 140 break; 141 } 142 if (irq == -1) 143 printk(KERN_WARNING "Unexpected interrupt class %02x, " 144 "source %02x, prio %02x, cpu %02x\n", pending.class, 145 pending.source, pending.prio, smp_processor_id()); 146 return irq; 147 } 148 149 /* Get an IRQ number from the pending state register of the IIC */ 150 int iic_get_irq(struct pt_regs *regs) 151 { 152 struct iic *iic; 153 int irq; 154 struct iic_pending_bits pending; 155 156 iic = &__get_cpu_var(iic); 157 *(unsigned long *) &pending = 158 in_be64((unsigned long __iomem *) &iic->regs->pending_destr); 159 160 irq = -1; 161 if (pending.flags & IIC_VALID) { 162 if (pending.flags & IIC_IPI) { 163 irq = IIC_IPI_OFFSET + (pending.prio >> 4); 164 /* 165 if (irq > 0x80) 166 printk(KERN_WARNING "Unexpected IPI prio %02x" 167 "on CPU %02x\n", pending.prio, 168 smp_processor_id()); 169 */ 170 } else { 171 irq = iic_external_get_irq(pending); 172 } 173 } 174 return irq; 175 } 176 177 static int setup_iic(int cpu, struct iic *iic) 178 { 179 struct device_node *np; 180 int nodeid = cpu / 2; 181 unsigned long regs; 182 183 for (np = of_find_node_by_type(NULL, "cpu"); 184 np; 185 np = of_find_node_by_type(np, "cpu")) { 186 if (nodeid == *(int *)get_property(np, "node-id", NULL)) 187 break; 188 } 189 190 if (!np) { 191 printk(KERN_WARNING "IIC: CPU %d not found\n", cpu); 192 iic->regs = NULL; 193 iic->target_id = 0xff; 194 return -ENODEV; 195 } 196 197 regs = *(long *)get_property(np, "iic", NULL); 198 199 /* hack until we have decided on the devtree info */ 200 regs += 0x400; 201 if (cpu & 1) 202 regs += 0x20; 203 204 printk(KERN_DEBUG "IIC for CPU %d at %lx\n", cpu, regs); 205 iic->regs = __ioremap(regs, sizeof(struct iic_regs), 206 _PAGE_NO_CACHE); 207 iic->target_id = (nodeid << 4) + ((cpu & 1) ? 0xf : 0xe); 208 return 0; 209 } 210 211 #ifdef CONFIG_SMP 212 213 /* Use the highest interrupt priorities for IPI */ 214 static inline int iic_ipi_to_irq(int ipi) 215 { 216 return IIC_IPI_OFFSET + IIC_NUM_IPIS - 1 - ipi; 217 } 218 219 static inline int iic_irq_to_ipi(int irq) 220 { 221 return IIC_NUM_IPIS - 1 - (irq - IIC_IPI_OFFSET); 222 } 223 224 void iic_setup_cpu(void) 225 { 226 out_be64(&__get_cpu_var(iic).regs->prio, 0xff); 227 } 228 229 void iic_cause_IPI(int cpu, int mesg) 230 { 231 out_be64(&per_cpu(iic, cpu).regs->generate, (IIC_NUM_IPIS - 1 - mesg) << 4); 232 } 233 234 u8 iic_get_target_id(int cpu) 235 { 236 return per_cpu(iic, cpu).target_id; 237 } 238 EXPORT_SYMBOL_GPL(iic_get_target_id); 239 240 static irqreturn_t iic_ipi_action(int irq, void *dev_id, struct pt_regs *regs) 241 { 242 smp_message_recv(iic_irq_to_ipi(irq), regs); 243 return IRQ_HANDLED; 244 } 245 246 static void iic_request_ipi(int ipi, const char *name) 247 { 248 int irq; 249 250 irq = iic_ipi_to_irq(ipi); 251 /* IPIs are marked SA_INTERRUPT as they must run with irqs 252 * disabled */ 253 get_irq_desc(irq)->handler = &iic_pic; 254 get_irq_desc(irq)->status |= IRQ_PER_CPU; 255 request_irq(irq, iic_ipi_action, SA_INTERRUPT, name, NULL); 256 } 257 258 void iic_request_IPIs(void) 259 { 260 iic_request_ipi(PPC_MSG_CALL_FUNCTION, "IPI-call"); 261 iic_request_ipi(PPC_MSG_RESCHEDULE, "IPI-resched"); 262 #ifdef CONFIG_DEBUGGER 263 iic_request_ipi(PPC_MSG_DEBUGGER_BREAK, "IPI-debug"); 264 #endif /* CONFIG_DEBUGGER */ 265 } 266 #endif /* CONFIG_SMP */ 267 268 static void iic_setup_spe_handlers(void) 269 { 270 int be, isrc; 271 272 /* Assume two threads per BE are present */ 273 for (be=0; be < num_present_cpus() / 2; be++) { 274 for (isrc = 0; isrc < IIC_CLASS_STRIDE * 3; isrc++) { 275 int irq = IIC_NODE_STRIDE * be + IIC_SPE_OFFSET + isrc; 276 get_irq_desc(irq)->handler = &iic_pic; 277 } 278 } 279 } 280 281 void iic_init_IRQ(void) 282 { 283 int cpu, irq_offset; 284 struct iic *iic; 285 286 irq_offset = 0; 287 for_each_cpu(cpu) { 288 iic = &per_cpu(iic, cpu); 289 setup_iic(cpu, iic); 290 if (iic->regs) 291 out_be64(&iic->regs->prio, 0xff); 292 } 293 iic_setup_spe_handlers(); 294 } 295