| /* |
| * |
| * Copyright 2002 Momentum Computer |
| * Author: mdharm@momenco.com |
| * |
| * arch/mips/momentum/ocelot_g/gt_irq.c |
| * Interrupt routines for gt64240. Currently it only handles timer irq. |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2 of the License, or (at your |
| * option) any later version. |
| */ |
| #include <linux/module.h> |
| #include <linux/interrupt.h> |
| #include <linux/kernel.h> |
| #include <asm/ptrace.h> |
| #include <linux/sched.h> |
| #include <linux/kernel_stat.h> |
| #include <asm/gt64240.h> |
| #include <asm/io.h> |
| |
| unsigned long bus_clock; |
| |
| /* |
| * These are interrupt handlers for the GT on-chip interrupts. They |
| * all come in to the MIPS on a single interrupt line, and have to |
| * be handled and ack'ed differently than other MIPS interrupts. |
| */ |
| |
| #if CURRENTLY_UNUSED |
| |
| struct tq_struct irq_handlers[MAX_CAUSE_REGS][MAX_CAUSE_REG_WIDTH]; |
| void hook_irq_handler(int int_cause, int bit_num, void *isr_ptr); |
| |
| /* |
| * Hooks IRQ handler to the system. When the system is interrupted |
| * the interrupt service routine is called. |
| * |
| * Inputs : |
| * int_cause - The interrupt cause number. In EVB64120 two parameters |
| * are declared, INT_CAUSE_MAIN and INT_CAUSE_HIGH. |
| * bit_num - Indicates which bit number in the cause register |
| * isr_ptr - Pointer to the interrupt service routine |
| */ |
| void hook_irq_handler(int int_cause, int bit_num, void *isr_ptr) |
| { |
| irq_handlers[int_cause][bit_num].routine = isr_ptr; |
| } |
| |
| |
| /* |
| * Enables the IRQ on Galileo Chip |
| * |
| * Inputs : |
| * int_cause - The interrupt cause number. In EVB64120 two parameters |
| * are declared, INT_CAUSE_MAIN and INT_CAUSE_HIGH. |
| * bit_num - Indicates which bit number in the cause register |
| * |
| * Outputs : |
| * 1 if successful, 0 if failure |
| */ |
| int enable_galileo_irq(int int_cause, int bit_num) |
| { |
| if (int_cause == INT_CAUSE_MAIN) |
| SET_REG_BITS(CPU_INTERRUPT_MASK_REGISTER, (1 << bit_num)); |
| else if (int_cause == INT_CAUSE_HIGH) |
| SET_REG_BITS(CPU_HIGH_INTERRUPT_MASK_REGISTER, |
| (1 << bit_num)); |
| else |
| return 0; |
| |
| return 1; |
| } |
| |
| /* |
| * Disables the IRQ on Galileo Chip |
| * |
| * Inputs : |
| * int_cause - The interrupt cause number. In EVB64120 two parameters |
| * are declared, INT_CAUSE_MAIN and INT_CAUSE_HIGH. |
| * bit_num - Indicates which bit number in the cause register |
| * |
| * Outputs : |
| * 1 if successful, 0 if failure |
| */ |
| int disable_galileo_irq(int int_cause, int bit_num) |
| { |
| if (int_cause == INT_CAUSE_MAIN) |
| RESET_REG_BITS(CPU_INTERRUPT_MASK_REGISTER, |
| (1 << bit_num)); |
| else if (int_cause == INT_CAUSE_HIGH) |
| RESET_REG_BITS(CPU_HIGH_INTERRUPT_MASK_REGISTER, |
| (1 << bit_num)); |
| else |
| return 0; |
| return 1; |
| } |
| #endif /* UNUSED */ |
| |
| /* |
| * Interrupt handler for interrupts coming from the Galileo chip via P0_INT#. |
| * |
| * We route the timer interrupt to P0_INT# (IRQ 6), and that's all this |
| * routine can handle, for now. |
| * |
| * In the future, we'll route more interrupts to this pin, and that's why |
| * we keep this particular structure in the function. |
| */ |
| |
| static irqreturn_t gt64240_p0int_irq(int irq, void *dev, struct pt_regs *regs) |
| { |
| uint32_t irq_src, irq_src_mask; |
| int handled; |
| |
| /* get the low interrupt cause register */ |
| irq_src = MV_READ(LOW_INTERRUPT_CAUSE_REGISTER); |
| |
| /* get the mask register for this pin */ |
| irq_src_mask = MV_READ(PCI_0INTERRUPT_CAUSE_MASK_REGISTER_LOW); |
| |
| /* mask off only the interrupts we're interested in */ |
| irq_src = irq_src & irq_src_mask; |
| |
| handled = IRQ_NONE; |
| |
| /* Check for timer interrupt */ |
| if (irq_src & 0x00000100) { |
| handled = IRQ_HANDLED; |
| irq_src &= ~0x00000100; |
| |
| /* Clear any pending cause bits */ |
| MV_WRITE(TIMER_COUNTER_0_3_INTERRUPT_CAUSE, 0x0); |
| |
| /* handle the timer call */ |
| do_timer(regs); |
| #ifndef CONFIG_SMP |
| update_process_times(user_mode(regs)); |
| #endif |
| } |
| |
| if (irq_src) { |
| printk(KERN_INFO |
| "UNKNOWN P0_INT# interrupt received, irq_src=0x%x\n", |
| irq_src); |
| } |
| |
| return handled; |
| } |
| |
| /* |
| * Initializes timer using galileo's built in timer. |
| */ |
| |
| /* |
| * This will ignore the standard MIPS timer interrupt handler |
| * that is passed in as *irq (=irq0 in ../kernel/time.c). |
| * We will do our own timer interrupt handling. |
| */ |
| void gt64240_time_init(void) |
| { |
| static struct irqaction timer; |
| |
| /* Stop the timer -- we'll use timer #0 */ |
| MV_WRITE(TIMER_COUNTER_0_3_CONTROL, 0x0); |
| |
| /* Load timer value for 100 Hz */ |
| MV_WRITE(TIMER_COUNTER0, bus_clock / 100); |
| |
| /* |
| * Create the IRQ structure entry for the timer. Since we're too early |
| * in the boot process to use the "request_irq()" call, we'll hard-code |
| * the values to the correct interrupt line. |
| */ |
| timer.handler = >64240_p0int_irq; |
| timer.flags = SA_SHIRQ | SA_INTERRUPT; |
| timer.name = "timer"; |
| timer.dev_id = NULL; |
| timer.next = NULL; |
| timer.mask = CPU_MASK_NONE; |
| irq_desc[6].action = &timer; |
| |
| enable_irq(6); |
| |
| /* Clear any pending cause bits */ |
| MV_WRITE(TIMER_COUNTER_0_3_INTERRUPT_CAUSE, 0x0); |
| |
| /* Enable the interrupt for timer 0 */ |
| MV_WRITE(TIMER_COUNTER_0_3_INTERRUPT_MASK, 0x1); |
| |
| /* Enable the timer interrupt for GT-64240 pin P0_INT# */ |
| MV_WRITE (PCI_0INTERRUPT_CAUSE_MASK_REGISTER_LOW, 0x100); |
| |
| /* Configure and start the timer */ |
| MV_WRITE(TIMER_COUNTER_0_3_CONTROL, 0x3); |
| } |
| |
| void gt64240_irq_init(void) |
| { |
| #if CURRENTLY_UNUSED |
| int i, j; |
| |
| /* Reset irq handlers pointers to NULL */ |
| for (i = 0; i < MAX_CAUSE_REGS; i++) { |
| for (j = 0; j < MAX_CAUSE_REG_WIDTH; j++) { |
| irq_handlers[i][j].next = NULL; |
| irq_handlers[i][j].sync = 0; |
| irq_handlers[i][j].routine = NULL; |
| irq_handlers[i][j].data = NULL; |
| } |
| } |
| #endif |
| } |