1/* 2 * 3 * Copyright 2002 Momentum Computer 4 * Author: mdharm@momenco.com 5 * 6 * arch/mips/momentum/ocelot_g/gt_irq.c 7 * Interrupt routines for gt64240. Currently it only handles timer irq. 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License as published by the 11 * Free Software Foundation; either version 2 of the License, or (at your 12 * option) any later version. 13 */ 14#include <linux/module.h> 15#include <linux/interrupt.h> 16#include <linux/kernel.h> 17#include <asm/ptrace.h> 18#include <linux/config.h> 19#include <linux/sched.h> 20#include <linux/kernel_stat.h> 21#include <asm/io.h> 22#include "gt64240.h" 23 24unsigned long bus_clock; 25 26/* 27 * These are interrupt handlers for the GT on-chip interrupts. They 28 * all come in to the MIPS on a single interrupt line, and have to 29 * be handled and ack'ed differently than other MIPS interrupts. 30 */ 31 32#if CURRENTLY_UNUSED 33 34struct tq_struct irq_handlers[MAX_CAUSE_REGS][MAX_CAUSE_REG_WIDTH]; 35void hook_irq_handler(int int_cause, int bit_num, void *isr_ptr); 36 37/* 38 * Hooks IRQ handler to the system. When the system is interrupted 39 * the interrupt service routine is called. 40 * 41 * Inputs : 42 * int_cause - The interrupt cause number. In EVB64120 two parameters 43 * are declared, INT_CAUSE_MAIN and INT_CAUSE_HIGH. 44 * bit_num - Indicates which bit number in the cause register 45 * isr_ptr - Pointer to the interrupt service routine 46 */ 47void hook_irq_handler(int int_cause, int bit_num, void *isr_ptr) 48{ 49 irq_handlers[int_cause][bit_num].routine = isr_ptr; 50} 51 52 53/* 54 * Enables the IRQ on Galileo Chip 55 * 56 * Inputs : 57 * int_cause - The interrupt cause number. In EVB64120 two parameters 58 * are declared, INT_CAUSE_MAIN and INT_CAUSE_HIGH. 59 * bit_num - Indicates which bit number in the cause register 60 * 61 * Outputs : 62 * 1 if succesful, 0 if failure 63 */ 64int enable_galileo_irq(int int_cause, int bit_num) 65{ 66 if (int_cause == INT_CAUSE_MAIN) 67 SET_REG_BITS(CPU_INTERRUPT_MASK_REGISTER, (1 << bit_num)); 68 else if (int_cause == INT_CAUSE_HIGH) 69 SET_REG_BITS(CPU_HIGH_INTERRUPT_MASK_REGISTER, 70 (1 << bit_num)); 71 else 72 return 0; 73 74 return 1; 75} 76 77/* 78 * Disables the IRQ on Galileo Chip 79 * 80 * Inputs : 81 * int_cause - The interrupt cause number. In EVB64120 two parameters 82 * are declared, INT_CAUSE_MAIN and INT_CAUSE_HIGH. 83 * bit_num - Indicates which bit number in the cause register 84 * 85 * Outputs : 86 * 1 if succesful, 0 if failure 87 */ 88int disable_galileo_irq(int int_cause, int bit_num) 89{ 90 if (int_cause == INT_CAUSE_MAIN) 91 RESET_REG_BITS(CPU_INTERRUPT_MASK_REGISTER, 92 (1 << bit_num)); 93 else if (int_cause == INT_CAUSE_HIGH) 94 RESET_REG_BITS(CPU_HIGH_INTERRUPT_MASK_REGISTER, 95 (1 << bit_num)); 96 else 97 return 0; 98 return 1; 99} 100#endif /* UNUSED */ 101 102/* 103 * Interrupt handler for interrupts coming from the Galileo chip via P0_INT#. 104 * 105 * We route the timer interrupt to P0_INT# (IRQ 6), and that's all this 106 * routine can handle, for now. 107 * 108 * In the future, we'll route more interrupts to this pin, and that's why 109 * we keep this particular structure in the function. 110 */ 111 112static void gt64240_p0int_irq(int irq, void *dev_id, struct pt_regs *regs) 113{ 114 uint32_t irq_src, irq_src_mask; 115 int handled; 116 117 /* get the low interrupt cause register */ 118 GT_READ(LOW_INTERRUPT_CAUSE_REGISTER, &irq_src); 119 120 /* get the mask register for this pin */ 121 GT_READ(PCI_0INTERRUPT_CAUSE_MASK_REGISTER_LOW, &irq_src_mask); 122 123 /* mask off only the interrupts we're interested in */ 124 irq_src = irq_src & irq_src_mask; 125 126 handled = 0; 127 128 /* Check for timer interrupt */ 129 if (irq_src & 0x00000100) { 130 handled = 1; 131 irq_src &= ~0x00000100; 132 133 /* Clear any pending cause bits */ 134 GT_WRITE(TIMER_COUNTER_0_3_INTERRUPT_CAUSE, 0x0); 135 136 /* handle the timer call */ 137 do_timer(regs); 138 } 139 140 if (irq_src) { 141 printk(KERN_INFO 142 "UNKNOWN P0_INT# interrupt received, irq_src=0x%x\n", 143 irq_src); 144 } 145} 146 147/* 148 * Interrupt handler for interrupts coming from the Galileo chip. 149 * It could be built in ethernet ports etc... 150 */ 151static void gt64240_irq(int irq, void *dev_id, struct pt_regs *regs) 152{ 153 unsigned int irq_src, int_high_src, irq_src_mask, 154 int_high_src_mask; 155 int handled; 156 157 irq_src = irq_src & irq_src_mask; 158 int_high_src = int_high_src & int_high_src_mask; 159 160 handled = 0; 161 162 /* Execute all interrupt handlers */ 163 /* Check for timer interrupt */ 164 if (irq_src & 0x00000800) { 165 handled = 1; 166 irq_src &= ~0x00000800; 167 // RESET_REG_BITS (INTERRUPT_CAUSE_REGISTER,BIT8); 168 do_timer(regs); 169 } 170 171 if (irq_src) { 172 printk(KERN_INFO 173 "Other Galileo interrupt received irq_src %x\n", 174 irq_src); 175#if CURRENTLY_UNUSED 176 for (count = 0; count < MAX_CAUSE_REG_WIDTH; count++) { 177 if (irq_src & (1 << count)) { 178 if (irq_handlers[INT_CAUSE_MAIN][count]. 179 routine) { 180 queue_task(&irq_handlers 181 [INT_CAUSE_MAIN][count], 182 &tq_immediate); 183 mark_bh(IMMEDIATE_BH); 184 handled = 1; 185 } 186 } 187 } 188#endif /* UNUSED */ 189 } 190 191#undef GALILEO_I2O 192#ifdef GALILEO_I2O 193 /* 194 * Future I2O support. We currently attach I2O interrupt handlers to 195 * the Galileo interrupt (int 4) and handle them in do_IRQ. 196 */ 197 if (isInBoundDoorBellInterruptSet()) { 198 printk(KERN_INFO "I2O doorbell interrupt received.\n"); 199 handled = 1; 200 } 201 202 if (isInBoundPostQueueInterruptSet()) { 203 printk(KERN_INFO "I2O Queue interrupt received.\n"); 204 handled = 1; 205 } 206 207 /* 208 * This normally would be outside of the ifdef, but since we're 209 * handling I2O outside of this handler, this printk shows up every 210 * time we get a valid I2O interrupt. So turn this off for now. 211 */ 212 if (handled == 0) { 213 if (counter < 50) { 214 printk("Spurious Galileo interrupt...\n"); 215 counter++; 216 } 217 } 218#endif 219} 220 221/* 222 * Initializes timer using galileo's built in timer. 223 */ 224 225/* 226 * This will ignore the standard MIPS timer interrupt handler 227 * that is passed in as *irq (=irq0 in ../kernel/time.c). 228 * We will do our own timer interrupt handling. 229 */ 230void gt64240_time_init(void) 231{ 232 extern irq_desc_t irq_desc[NR_IRQS]; 233 static struct irqaction timer; 234 235 /* Stop the timer -- we'll use timer #0 */ 236 GT_WRITE(TIMER_COUNTER_0_3_CONTROL, 0x0); 237 238 /* Load timer value for 100 Hz */ 239 GT_WRITE(TIMER_COUNTER0, bus_clock / 100); 240 241 /* 242 * Create the IRQ structure entry for the timer. Since we're too early 243 * in the boot process to use the "request_irq()" call, we'll hard-code 244 * the values to the correct interrupt line. 245 */ 246 timer.handler = >64240_p0int_irq; 247 timer.flags = SA_SHIRQ | SA_INTERRUPT; 248 timer.name = "timer"; 249 timer.dev_id = NULL; 250 timer.next = NULL; 251 timer.mask = 0; 252 irq_desc[6].action = &timer; 253 254 enable_irq(6); 255 256 /* Clear any pending cause bits */ 257 GT_WRITE(TIMER_COUNTER_0_3_INTERRUPT_CAUSE, 0x0); 258 259 /* Enable the interrupt for timer 0 */ 260 GT_WRITE(TIMER_COUNTER_0_3_INTERRUPT_MASK, 0x1); 261 262 /* Enable the timer interrupt for GT-64240 pin P0_INT# */ 263 GT_WRITE (PCI_0INTERRUPT_CAUSE_MASK_REGISTER_LOW, 0x100); 264 265 /* Configure and start the timer */ 266 GT_WRITE(TIMER_COUNTER_0_3_CONTROL, 0x3); 267} 268 269void gt64240_irq_init(void) 270{ 271#if CURRENTLY_UNUSED 272 int i, j; 273 274 /* Reset irq handlers pointers to NULL */ 275 for (i = 0; i < MAX_CAUSE_REGS; i++) { 276 for (j = 0; j < MAX_CAUSE_REG_WIDTH; j++) { 277 irq_handlers[i][j].next = NULL; 278 irq_handlers[i][j].sync = 0; 279 irq_handlers[i][j].routine = NULL; 280 irq_handlers[i][j].data = NULL; 281 } 282 } 283#endif 284} 285