1/* 2 * Carsten Langgaard, carstenl@mips.com 3 * Copyright (C) 2000, 2001, 2004 MIPS Technologies, Inc. 4 * Copyright (C) 2001 Ralf Baechle 5 * 6 * This program is free software; you can distribute it and/or modify it 7 * under the terms of the GNU General Public License (Version 2) as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. 18 * 19 * Routines for generic manipulation of the interrupts found on the MIPS 20 * Malta board. 21 * The interrupt controller is located in the South Bridge a PIIX4 device 22 * with two internal 82C95 interrupt controllers. 23 */ 24#include <linux/init.h> 25#include <linux/irq.h> 26#include <linux/sched.h> 27#include <linux/slab.h> 28#include <linux/interrupt.h> 29#include <linux/kernel_stat.h> 30#include <linux/kernel.h> 31#include <linux/random.h> 32 33#include <asm/i8259.h> 34#include <asm/irq_cpu.h> 35#include <asm/io.h> 36#include <asm/irq_regs.h> 37#include <asm/mips-boards/malta.h> 38#include <asm/mips-boards/maltaint.h> 39#include <asm/mips-boards/piix4.h> 40#include <asm/gt64120.h> 41#include <asm/mips-boards/generic.h> 42#include <asm/mips-boards/msc01_pci.h> 43#include <asm/msc01_ic.h> 44 45static DEFINE_SPINLOCK(mips_irq_lock); 46 47static inline int mips_pcibios_iack(void) 48{ 49 int irq; 50 u32 dummy; 51 52 /* 53 * Determine highest priority pending interrupt by performing 54 * a PCI Interrupt Acknowledge cycle. 55 */ 56 switch (mips_revision_sconid) { 57 case MIPS_REVISION_SCON_SOCIT: 58 case MIPS_REVISION_SCON_ROCIT: 59 case MIPS_REVISION_SCON_SOCITSC: 60 case MIPS_REVISION_SCON_SOCITSCP: 61 MSC_READ(MSC01_PCI_IACK, irq); 62 irq &= 0xff; 63 break; 64 case MIPS_REVISION_SCON_GT64120: 65 irq = GT_READ(GT_PCI0_IACK_OFS); 66 irq &= 0xff; 67 break; 68 case MIPS_REVISION_SCON_BONITO: 69 /* The following will generate a PCI IACK cycle on the 70 * Bonito controller. It's a little bit kludgy, but it 71 * was the easiest way to implement it in hardware at 72 * the given time. 73 */ 74 BONITO_PCIMAP_CFG = 0x20000; 75 76 /* Flush Bonito register block */ 77 dummy = BONITO_PCIMAP_CFG; 78 iob(); /* sync */ 79 80 irq = readl((u32 *)_pcictrl_bonito_pcicfg); 81 iob(); /* sync */ 82 irq &= 0xff; 83 BONITO_PCIMAP_CFG = 0; 84 break; 85 default: 86 printk("Unknown system controller.\n"); 87 return -1; 88 } 89 return irq; 90} 91 92static inline int get_int(void) 93{ 94 unsigned long flags; 95 int irq; 96 spin_lock_irqsave(&mips_irq_lock, flags); 97 98 irq = mips_pcibios_iack(); 99 100 /* 101 * The only way we can decide if an interrupt is spurious 102 * is by checking the 8259 registers. This needs a spinlock 103 * on an SMP system, so leave it up to the generic code... 104 */ 105 106 spin_unlock_irqrestore(&mips_irq_lock, flags); 107 108 return irq; 109} 110 111static void malta_hw0_irqdispatch(void) 112{ 113 int irq; 114 115 irq = get_int(); 116 if (irq < 0) { 117 return; /* interrupt has already been cleared */ 118 } 119 120 do_IRQ(MALTA_INT_BASE + irq); 121} 122 123static void corehi_irqdispatch(void) 124{ 125 unsigned int intedge, intsteer, pcicmd, pcibadaddr; 126 unsigned int pcimstat, intisr, inten, intpol; 127 unsigned int intrcause,datalo,datahi; 128 struct pt_regs *regs = get_irq_regs(); 129 130 printk("CoreHI interrupt, shouldn't happen, so we die here!!!\n"); 131 printk("epc : %08lx\nStatus: %08lx\n" 132 "Cause : %08lx\nbadVaddr : %08lx\n", 133 regs->cp0_epc, regs->cp0_status, 134 regs->cp0_cause, regs->cp0_badvaddr); 135 136 /* Read all the registers and then print them as there is a 137 problem with interspersed printk's upsetting the Bonito controller. 138 Do it for the others too. 139 */ 140 141 switch (mips_revision_sconid) { 142 case MIPS_REVISION_SCON_SOCIT: 143 case MIPS_REVISION_SCON_ROCIT: 144 case MIPS_REVISION_SCON_SOCITSC: 145 case MIPS_REVISION_SCON_SOCITSCP: 146 ll_msc_irq(); 147 break; 148 case MIPS_REVISION_SCON_GT64120: 149 intrcause = GT_READ(GT_INTRCAUSE_OFS); 150 datalo = GT_READ(GT_CPUERR_ADDRLO_OFS); 151 datahi = GT_READ(GT_CPUERR_ADDRHI_OFS); 152 printk("GT_INTRCAUSE = %08x\n", intrcause); 153 printk("GT_CPUERR_ADDR = %02x%08x\n", datahi, datalo); 154 break; 155 case MIPS_REVISION_SCON_BONITO: 156 pcibadaddr = BONITO_PCIBADADDR; 157 pcimstat = BONITO_PCIMSTAT; 158 intisr = BONITO_INTISR; 159 inten = BONITO_INTEN; 160 intpol = BONITO_INTPOL; 161 intedge = BONITO_INTEDGE; 162 intsteer = BONITO_INTSTEER; 163 pcicmd = BONITO_PCICMD; 164 printk("BONITO_INTISR = %08x\n", intisr); 165 printk("BONITO_INTEN = %08x\n", inten); 166 printk("BONITO_INTPOL = %08x\n", intpol); 167 printk("BONITO_INTEDGE = %08x\n", intedge); 168 printk("BONITO_INTSTEER = %08x\n", intsteer); 169 printk("BONITO_PCICMD = %08x\n", pcicmd); 170 printk("BONITO_PCIBADADDR = %08x\n", pcibadaddr); 171 printk("BONITO_PCIMSTAT = %08x\n", pcimstat); 172 break; 173 } 174 175 /* We die here*/ 176 die("CoreHi interrupt", regs); 177} 178 179static inline int clz(unsigned long x) 180{ 181 __asm__ ( 182 " .set push \n" 183 " .set mips32 \n" 184 " clz %0, %1 \n" 185 " .set pop \n" 186 : "=r" (x) 187 : "r" (x)); 188 189 return x; 190} 191 192/* 193 * Version of ffs that only looks at bits 12..15. 194 */ 195static inline unsigned int irq_ffs(unsigned int pending) 196{ 197#if defined(CONFIG_CPU_MIPS32) || defined(CONFIG_CPU_MIPS64) 198 return -clz(pending) + 31 - CAUSEB_IP; 199#else 200 unsigned int a0 = 7; 201 unsigned int t0; 202 203 t0 = pending & 0xf000; 204 t0 = t0 < 1; 205 t0 = t0 << 2; 206 a0 = a0 - t0; 207 pending = pending << t0; 208 209 t0 = pending & 0xc000; 210 t0 = t0 < 1; 211 t0 = t0 << 1; 212 a0 = a0 - t0; 213 pending = pending << t0; 214 215 t0 = pending & 0x8000; 216 t0 = t0 < 1; 217 //t0 = t0 << 2; 218 a0 = a0 - t0; 219 //pending = pending << t0; 220 221 return a0; 222#endif 223} 224 225/* 226 * IRQs on the Malta board look basically (barring software IRQs which we 227 * don't use at all and all external interrupt sources are combined together 228 * on hardware interrupt 0 (MIPS IRQ 2)) like: 229 * 230 * MIPS IRQ Source 231 * -------- ------ 232 * 0 Software (ignored) 233 * 1 Software (ignored) 234 * 2 Combined hardware interrupt (hw0) 235 * 3 Hardware (ignored) 236 * 4 Hardware (ignored) 237 * 5 Hardware (ignored) 238 * 6 Hardware (ignored) 239 * 7 R4k timer (what we use) 240 * 241 * We handle the IRQ according to _our_ priority which is: 242 * 243 * Highest ---- R4k Timer 244 * Lowest ---- Combined hardware interrupt 245 * 246 * then we just return, if multiple IRQs are pending then we will just take 247 * another exception, big deal. 248 */ 249 250asmlinkage void plat_irq_dispatch(void) 251{ 252 unsigned int pending = read_c0_cause() & read_c0_status() & ST0_IM; 253 int irq; 254 255 irq = irq_ffs(pending); 256 257 if (irq == MIPSCPU_INT_I8259A) 258 malta_hw0_irqdispatch(); 259 else if (irq > 0) 260 do_IRQ(MIPS_CPU_IRQ_BASE + irq); 261 else 262 spurious_interrupt(); 263} 264 265static struct irqaction i8259irq = { 266 .handler = no_action, 267 .name = "XT-PIC cascade" 268}; 269 270static struct irqaction corehi_irqaction = { 271 .handler = no_action, 272 .name = "CoreHi" 273}; 274 275msc_irqmap_t __initdata msc_irqmap[] = { 276 {MSC01C_INT_TMR, MSC01_IRQ_EDGE, 0}, 277 {MSC01C_INT_PCI, MSC01_IRQ_LEVEL, 0}, 278}; 279int __initdata msc_nr_irqs = ARRAY_SIZE(msc_irqmap); 280 281msc_irqmap_t __initdata msc_eicirqmap[] = { 282 {MSC01E_INT_SW0, MSC01_IRQ_LEVEL, 0}, 283 {MSC01E_INT_SW1, MSC01_IRQ_LEVEL, 0}, 284 {MSC01E_INT_I8259A, MSC01_IRQ_LEVEL, 0}, 285 {MSC01E_INT_SMI, MSC01_IRQ_LEVEL, 0}, 286 {MSC01E_INT_COREHI, MSC01_IRQ_LEVEL, 0}, 287 {MSC01E_INT_CORELO, MSC01_IRQ_LEVEL, 0}, 288 {MSC01E_INT_TMR, MSC01_IRQ_EDGE, 0}, 289 {MSC01E_INT_PCI, MSC01_IRQ_LEVEL, 0}, 290 {MSC01E_INT_PERFCTR, MSC01_IRQ_LEVEL, 0}, 291 {MSC01E_INT_CPUCTR, MSC01_IRQ_LEVEL, 0} 292}; 293int __initdata msc_nr_eicirqs = ARRAY_SIZE(msc_eicirqmap); 294 295void __init arch_init_irq(void) 296{ 297 init_i8259_irqs(); 298 299 if (!cpu_has_veic) 300 mips_cpu_irq_init(); 301 302 switch(mips_revision_sconid) { 303 case MIPS_REVISION_SCON_SOCIT: 304 case MIPS_REVISION_SCON_ROCIT: 305 if (cpu_has_veic) 306 init_msc_irqs (MIPS_MSC01_IC_REG_BASE, MSC01E_INT_BASE, msc_eicirqmap, msc_nr_eicirqs); 307 else 308 init_msc_irqs (MIPS_MSC01_IC_REG_BASE, MSC01C_INT_BASE, msc_irqmap, msc_nr_irqs); 309 break; 310 311 case MIPS_REVISION_SCON_SOCITSC: 312 case MIPS_REVISION_SCON_SOCITSCP: 313 if (cpu_has_veic) 314 init_msc_irqs (MIPS_SOCITSC_IC_REG_BASE, MSC01E_INT_BASE, msc_eicirqmap, msc_nr_eicirqs); 315 else 316 init_msc_irqs (MIPS_SOCITSC_IC_REG_BASE, MSC01C_INT_BASE, msc_irqmap, msc_nr_irqs); 317 } 318 319 if (cpu_has_veic) { 320 set_vi_handler (MSC01E_INT_I8259A, malta_hw0_irqdispatch); 321 set_vi_handler (MSC01E_INT_COREHI, corehi_irqdispatch); 322 setup_irq (MSC01E_INT_BASE+MSC01E_INT_I8259A, &i8259irq); 323 setup_irq (MSC01E_INT_BASE+MSC01E_INT_COREHI, &corehi_irqaction); 324 } 325 else if (cpu_has_vint) { 326 set_vi_handler (MIPSCPU_INT_I8259A, malta_hw0_irqdispatch); 327 set_vi_handler (MIPSCPU_INT_COREHI, corehi_irqdispatch); 328#ifdef CONFIG_MIPS_MT_SMTC 329 setup_irq_smtc (MIPS_CPU_IRQ_BASE+MIPSCPU_INT_I8259A, &i8259irq, 330 (0x100 << MIPSCPU_INT_I8259A)); 331 setup_irq_smtc (MIPS_CPU_IRQ_BASE+MIPSCPU_INT_COREHI, 332 &corehi_irqaction, (0x100 << MIPSCPU_INT_COREHI)); 333#else /* Not SMTC */ 334 setup_irq (MIPS_CPU_IRQ_BASE+MIPSCPU_INT_I8259A, &i8259irq); 335 setup_irq (MIPS_CPU_IRQ_BASE+MIPSCPU_INT_COREHI, &corehi_irqaction); 336#endif /* CONFIG_MIPS_MT_SMTC */ 337 } 338 else { 339 setup_irq (MIPS_CPU_IRQ_BASE+MIPSCPU_INT_I8259A, &i8259irq); 340 setup_irq (MIPS_CPU_IRQ_BASE+MIPSCPU_INT_COREHI, &corehi_irqaction); 341 } 342} 343