1 /* 2 * Copyright (c) 2004 MIPS Inc 3 * Author: chris@mips.com 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License as published by the 7 * Free Software Foundation; either version 2 of the License, or (at your 8 * option) any later version. 9 */ 10 #include <linux/module.h> 11 #include <linux/interrupt.h> 12 #include <linux/kernel.h> 13 #include <asm/ptrace.h> 14 #include <linux/sched.h> 15 #include <linux/kernel_stat.h> 16 #include <asm/io.h> 17 #include <asm/irq.h> 18 #include <asm/msc01_ic.h> 19 20 static unsigned long _icctrl_msc; 21 #define MSC01_IC_REG_BASE _icctrl_msc 22 23 #define MSCIC_WRITE(reg, data) do { *(volatile u32 *)(reg) = data; } while (0) 24 #define MSCIC_READ(reg, data) do { data = *(volatile u32 *)(reg); } while (0) 25 26 static unsigned int irq_base; 27 28 /* mask off an interrupt */ 29 static inline void mask_msc_irq(unsigned int irq) 30 { 31 if (irq < (irq_base + 32)) 32 MSCIC_WRITE(MSC01_IC_DISL, 1<<(irq - irq_base)); 33 else 34 MSCIC_WRITE(MSC01_IC_DISH, 1<<(irq - irq_base - 32)); 35 } 36 37 /* unmask an interrupt */ 38 static inline void unmask_msc_irq(unsigned int irq) 39 { 40 if (irq < (irq_base + 32)) 41 MSCIC_WRITE(MSC01_IC_ENAL, 1<<(irq - irq_base)); 42 else 43 MSCIC_WRITE(MSC01_IC_ENAH, 1<<(irq - irq_base - 32)); 44 } 45 46 /* 47 * Enables the IRQ on SOC-it 48 */ 49 static void enable_msc_irq(unsigned int irq) 50 { 51 unmask_msc_irq(irq); 52 } 53 54 /* 55 * Initialize the IRQ on SOC-it 56 */ 57 static unsigned int startup_msc_irq(unsigned int irq) 58 { 59 unmask_msc_irq(irq); 60 return 0; 61 } 62 63 /* 64 * Disables the IRQ on SOC-it 65 */ 66 static void disable_msc_irq(unsigned int irq) 67 { 68 mask_msc_irq(irq); 69 } 70 71 /* 72 * Masks and ACKs an IRQ 73 */ 74 static void level_mask_and_ack_msc_irq(unsigned int irq) 75 { 76 mask_msc_irq(irq); 77 if (!cpu_has_veic) 78 MSCIC_WRITE(MSC01_IC_EOI, 0); 79 } 80 81 /* 82 * Masks and ACKs an IRQ 83 */ 84 static void edge_mask_and_ack_msc_irq(unsigned int irq) 85 { 86 mask_msc_irq(irq); 87 if (!cpu_has_veic) 88 MSCIC_WRITE(MSC01_IC_EOI, 0); 89 else { 90 u32 r; 91 MSCIC_READ(MSC01_IC_SUP+irq*8, r); 92 MSCIC_WRITE(MSC01_IC_SUP+irq*8, r | ~MSC01_IC_SUP_EDGE_BIT); 93 MSCIC_WRITE(MSC01_IC_SUP+irq*8, r); 94 } 95 } 96 97 /* 98 * End IRQ processing 99 */ 100 static void end_msc_irq(unsigned int irq) 101 { 102 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) 103 unmask_msc_irq(irq); 104 } 105 106 /* 107 * Interrupt handler for interrupts coming from SOC-it. 108 */ 109 void ll_msc_irq(struct pt_regs *regs) 110 { 111 unsigned int irq; 112 113 /* read the interrupt vector register */ 114 MSCIC_READ(MSC01_IC_VEC, irq); 115 if (irq < 64) 116 do_IRQ(irq + irq_base, regs); 117 else { 118 /* Ignore spurious interrupt */ 119 } 120 } 121 122 void 123 msc_bind_eic_interrupt (unsigned int irq, unsigned int set) 124 { 125 MSCIC_WRITE(MSC01_IC_RAMW, 126 (irq<<MSC01_IC_RAMW_ADDR_SHF) | (set<<MSC01_IC_RAMW_DATA_SHF)); 127 } 128 129 #define shutdown_msc_irq disable_msc_irq 130 131 struct hw_interrupt_type msc_levelirq_type = { 132 .typename = "SOC-it-Level", 133 .startup = startup_msc_irq, 134 .shutdown = shutdown_msc_irq, 135 .enable = enable_msc_irq, 136 .disable = disable_msc_irq, 137 .ack = level_mask_and_ack_msc_irq, 138 .end = end_msc_irq, 139 }; 140 141 struct hw_interrupt_type msc_edgeirq_type = { 142 .typename = "SOC-it-Edge", 143 .startup =startup_msc_irq, 144 .shutdown = shutdown_msc_irq, 145 .enable = enable_msc_irq, 146 .disable = disable_msc_irq, 147 .ack = edge_mask_and_ack_msc_irq, 148 .end = end_msc_irq, 149 }; 150 151 152 void __init init_msc_irqs(unsigned int base, msc_irqmap_t *imp, int nirq) 153 { 154 extern void (*board_bind_eic_interrupt)(unsigned int irq, unsigned int regset); 155 156 _icctrl_msc = (unsigned long) ioremap (MIPS_MSC01_IC_REG_BASE, 0x40000); 157 158 /* Reset interrupt controller - initialises all registers to 0 */ 159 MSCIC_WRITE(MSC01_IC_RST, MSC01_IC_RST_RST_BIT); 160 161 board_bind_eic_interrupt = &msc_bind_eic_interrupt; 162 163 for (; nirq >= 0; nirq--, imp++) { 164 int n = imp->im_irq; 165 166 switch (imp->im_type) { 167 case MSC01_IRQ_EDGE: 168 irq_desc[base+n].handler = &msc_edgeirq_type; 169 if (cpu_has_veic) 170 MSCIC_WRITE(MSC01_IC_SUP+n*8, MSC01_IC_SUP_EDGE_BIT); 171 else 172 MSCIC_WRITE(MSC01_IC_SUP+n*8, MSC01_IC_SUP_EDGE_BIT | imp->im_lvl); 173 break; 174 case MSC01_IRQ_LEVEL: 175 irq_desc[base+n].handler = &msc_levelirq_type; 176 if (cpu_has_veic) 177 MSCIC_WRITE(MSC01_IC_SUP+n*8, 0); 178 else 179 MSCIC_WRITE(MSC01_IC_SUP+n*8, imp->im_lvl); 180 } 181 } 182 183 irq_base = base; 184 185 MSCIC_WRITE(MSC01_IC_GENA, MSC01_IC_GENA_GENA_BIT); /* Enable interrupt generation */ 186 187 } 188