1/* 2 * 3 * Copyright (C) 2005 Embedded Alley Solutions, Inc 4 * Ported to 2.6. 5 * 6 * Per Hallsmark, per.hallsmark@mvista.com 7 * Copyright (C) 2000, 2001 MIPS Technologies, Inc. 8 * Copyright (C) 2001 Ralf Baechle 9 * 10 * Cleaned up and bug fixing: Pete Popov, ppopov@embeddedalley.com 11 * 12 * This program is free software; you can distribute it and/or modify it 13 * under the terms of the GNU General Public License (Version 2) as 14 * published by the Free Software Foundation. 15 * 16 * This program is distributed in the hope it will be useful, but WITHOUT 17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 19 * for more details. 20 * 21 * You should have received a copy of the GNU General Public License along 22 * with this program; if not, write to the Free Software Foundation, Inc., 23 * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. 24 * 25 */ 26#include <linux/compiler.h> 27#include <linux/init.h> 28#include <linux/irq.h> 29#include <linux/sched.h> 30#include <linux/interrupt.h> 31#include <linux/kernel_stat.h> 32#include <linux/random.h> 33#include <linux/module.h> 34 35#include <asm/io.h> 36#include <int.h> 37#include <uart.h> 38 39/* default prio for interrupts */ 40/* first one is a no-no so therefore always prio 0 (disabled) */ 41static char gic_prio[PNX8550_INT_GIC_TOTINT] = { 42 0, 1, 1, 1, 1, 15, 1, 1, 1, 1, // 0 - 9 43 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 10 - 19 44 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 20 - 29 45 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 30 - 39 46 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40 - 49 47 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 50 - 59 48 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60 - 69 49 1 // 70 50}; 51 52static void hw0_irqdispatch(int irq) 53{ 54 /* find out which interrupt */ 55 irq = PNX8550_GIC_VECTOR_0 >> 3; 56 57 if (irq == 0) { 58 printk("hw0_irqdispatch: irq 0, spurious interrupt?\n"); 59 return; 60 } 61 do_IRQ(PNX8550_INT_GIC_MIN + irq); 62} 63 64 65static void timer_irqdispatch(int irq) 66{ 67 irq = (0x01c0 & read_c0_config7()) >> 6; 68 69 if (unlikely(irq == 0)) { 70 printk("timer_irqdispatch: irq 0, spurious interrupt?\n"); 71 return; 72 } 73 74 if (irq & 0x1) 75 do_IRQ(PNX8550_INT_TIMER1); 76 if (irq & 0x2) 77 do_IRQ(PNX8550_INT_TIMER2); 78 if (irq & 0x4) 79 do_IRQ(PNX8550_INT_TIMER3); 80} 81 82asmlinkage void plat_irq_dispatch(void) 83{ 84 unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM; 85 86 if (pending & STATUSF_IP2) 87 hw0_irqdispatch(2); 88 else if (pending & STATUSF_IP7) { 89 if (read_c0_config7() & 0x01c0) 90 timer_irqdispatch(7); 91 } else 92 spurious_interrupt(); 93} 94 95static inline void modify_cp0_intmask(unsigned clr_mask, unsigned set_mask) 96{ 97 unsigned long status = read_c0_status(); 98 99 status &= ~((clr_mask & 0xFF) << 8); 100 status |= (set_mask & 0xFF) << 8; 101 102 write_c0_status(status); 103} 104 105static inline void mask_gic_int(unsigned int irq_nr) 106{ 107 /* interrupt disabled, bit 26(WE_ENABLE)=1 and bit 16(enable)=0 */ 108 PNX8550_GIC_REQ(irq_nr) = 1<<28; /* set priority to 0 */ 109} 110 111static inline void unmask_gic_int(unsigned int irq_nr) 112{ 113 /* set prio mask to lower four bits and enable interrupt */ 114 PNX8550_GIC_REQ(irq_nr) = (1<<26 | 1<<16) | (1<<28) | gic_prio[irq_nr]; 115} 116 117static inline void mask_irq(unsigned int irq_nr) 118{ 119 if ((PNX8550_INT_CP0_MIN <= irq_nr) && (irq_nr <= PNX8550_INT_CP0_MAX)) { 120 modify_cp0_intmask(1 << irq_nr, 0); 121 } else if ((PNX8550_INT_GIC_MIN <= irq_nr) && 122 (irq_nr <= PNX8550_INT_GIC_MAX)) { 123 mask_gic_int(irq_nr - PNX8550_INT_GIC_MIN); 124 } else if ((PNX8550_INT_TIMER_MIN <= irq_nr) && 125 (irq_nr <= PNX8550_INT_TIMER_MAX)) { 126 modify_cp0_intmask(1 << 7, 0); 127 } else { 128 printk("mask_irq: irq %d doesn't exist!\n", irq_nr); 129 } 130} 131 132static inline void unmask_irq(unsigned int irq_nr) 133{ 134 if ((PNX8550_INT_CP0_MIN <= irq_nr) && (irq_nr <= PNX8550_INT_CP0_MAX)) { 135 modify_cp0_intmask(0, 1 << irq_nr); 136 } else if ((PNX8550_INT_GIC_MIN <= irq_nr) && 137 (irq_nr <= PNX8550_INT_GIC_MAX)) { 138 unmask_gic_int(irq_nr - PNX8550_INT_GIC_MIN); 139 } else if ((PNX8550_INT_TIMER_MIN <= irq_nr) && 140 (irq_nr <= PNX8550_INT_TIMER_MAX)) { 141 modify_cp0_intmask(0, 1 << 7); 142 } else { 143 printk("mask_irq: irq %d doesn't exist!\n", irq_nr); 144 } 145} 146 147int pnx8550_set_gic_priority(int irq, int priority) 148{ 149 int gic_irq = irq-PNX8550_INT_GIC_MIN; 150 int prev_priority = PNX8550_GIC_REQ(gic_irq) & 0xf; 151 152 gic_prio[gic_irq] = priority; 153 PNX8550_GIC_REQ(gic_irq) |= (0x10000000 | gic_prio[gic_irq]); 154 155 return prev_priority; 156} 157 158static struct irq_chip level_irq_type = { 159 .name = "PNX Level IRQ", 160 .ack = mask_irq, 161 .mask = mask_irq, 162 .mask_ack = mask_irq, 163 .unmask = unmask_irq, 164}; 165 166static struct irqaction gic_action = { 167 .handler = no_action, 168 .flags = IRQF_DISABLED, 169 .name = "GIC", 170}; 171 172static struct irqaction timer_action = { 173 .handler = no_action, 174 .flags = IRQF_DISABLED | IRQF_TIMER, 175 .name = "Timer", 176}; 177 178void __init arch_init_irq(void) 179{ 180 int i; 181 int configPR; 182 183 for (i = 0; i < PNX8550_INT_CP0_TOTINT; i++) { 184 set_irq_chip_and_handler(i, &level_irq_type, handle_level_irq); 185 mask_irq(i); /* mask the irq just in case */ 186 } 187 188 /* init of GIC/IPC interrupts */ 189 /* should be done before cp0 since cp0 init enables the GIC int */ 190 for (i = PNX8550_INT_GIC_MIN; i <= PNX8550_INT_GIC_MAX; i++) { 191 int gic_int_line = i - PNX8550_INT_GIC_MIN; 192 if (gic_int_line == 0 ) 193 continue; // don't fiddle with int 0 194 /* 195 * enable change of TARGET, ENABLE and ACTIVE_LOW bits 196 * set TARGET 0 to route through hw0 interrupt 197 * set ACTIVE_LOW 0 active high (correct?) 198 * 199 * We really should setup an interrupt description table 200 * to do this nicely. 201 * Note, PCI INTA is active low on the bus, but inverted 202 * in the GIC, so to us it's active high. 203 */ 204 PNX8550_GIC_REQ(i - PNX8550_INT_GIC_MIN) = 0x1E000000; 205 206 /* mask/priority is still 0 so we will not get any 207 * interrupts until it is unmasked */ 208 209 set_irq_chip_and_handler(i, &level_irq_type, handle_level_irq); 210 } 211 212 /* Priority level 0 */ 213 PNX8550_GIC_PRIMASK_0 = PNX8550_GIC_PRIMASK_1 = 0; 214 215 /* Set int vector table address */ 216 PNX8550_GIC_VECTOR_0 = PNX8550_GIC_VECTOR_1 = 0; 217 218 set_irq_chip_and_handler(MIPS_CPU_GIC_IRQ, &level_irq_type, 219 handle_level_irq); 220 setup_irq(MIPS_CPU_GIC_IRQ, &gic_action); 221 222 /* init of Timer interrupts */ 223 for (i = PNX8550_INT_TIMER_MIN; i <= PNX8550_INT_TIMER_MAX; i++) 224 set_irq_chip_and_handler(i, &level_irq_type, handle_level_irq); 225 226 /* Stop Timer 1-3 */ 227 configPR = read_c0_config7(); 228 configPR |= 0x00000038; 229 write_c0_config7(configPR); 230 231 set_irq_chip_and_handler(MIPS_CPU_TIMER_IRQ, &level_irq_type, 232 handle_level_irq); 233 setup_irq(MIPS_CPU_TIMER_IRQ, &timer_action); 234} 235 236EXPORT_SYMBOL(pnx8550_set_gic_priority); 237