1/* 2 * Copyright (C) 2007 Lemote Inc. 3 * Author: Fuxin Zhang, zhangfx@lemote.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 11#include <linux/interrupt.h> 12#include <linux/module.h> 13 14#include <asm/irq_cpu.h> 15#include <asm/i8259.h> 16#include <asm/mipsregs.h> 17 18#include <loongson.h> 19#include <machine.h> 20 21#define LOONGSON_TIMER_IRQ (MIPS_CPU_IRQ_BASE + 7) /* cpu timer */ 22#define LOONGSON_NORTH_BRIDGE_IRQ (MIPS_CPU_IRQ_BASE + 6) /* bonito */ 23#define LOONGSON_UART_IRQ (MIPS_CPU_IRQ_BASE + 3) /* cpu serial port */ 24#define LOONGSON_SOUTH_BRIDGE_IRQ (MIPS_CPU_IRQ_BASE + 2) /* i8259 */ 25 26#define LOONGSON_INT_BIT_INT0 (1 << 11) 27#define LOONGSON_INT_BIT_INT1 (1 << 12) 28 29/* 30 * The generic i8259_irq() make the kernel hang on booting. Since we cannot 31 * get the irq via the IRR directly, we access the ISR instead. 32 */ 33int mach_i8259_irq(void) 34{ 35 int irq, isr; 36 37 irq = -1; 38 39 if ((LOONGSON_INTISR & LOONGSON_INTEN) & LOONGSON_INT_BIT_INT0) { 40 raw_spin_lock(&i8259A_lock); 41 isr = inb(PIC_MASTER_CMD) & 42 ~inb(PIC_MASTER_IMR) & ~(1 << PIC_CASCADE_IR); 43 if (!isr) 44 isr = (inb(PIC_SLAVE_CMD) & ~inb(PIC_SLAVE_IMR)) << 8; 45 irq = ffs(isr) - 1; 46 if (unlikely(irq == 7)) { 47 /* 48 * This may be a spurious interrupt. 49 * 50 * Read the interrupt status register (ISR). If the most 51 * significant bit is not set then there is no valid 52 * interrupt. 53 */ 54 outb(0x0B, PIC_MASTER_ISR); /* ISR register */ 55 if (~inb(PIC_MASTER_ISR) & 0x80) 56 irq = -1; 57 } 58 raw_spin_unlock(&i8259A_lock); 59 } 60 61 return irq; 62} 63EXPORT_SYMBOL(mach_i8259_irq); 64 65static void i8259_irqdispatch(void) 66{ 67 int irq; 68 69 irq = mach_i8259_irq(); 70 if (irq >= 0) 71 do_IRQ(irq); 72 else 73 spurious_interrupt(); 74} 75 76void mach_irq_dispatch(unsigned int pending) 77{ 78 if (pending & CAUSEF_IP7) 79 do_IRQ(LOONGSON_TIMER_IRQ); 80 else if (pending & CAUSEF_IP6) { /* North Bridge, Perf counter */ 81 do_perfcnt_IRQ(); 82 bonito_irqdispatch(); 83 } else if (pending & CAUSEF_IP3) /* CPU UART */ 84 do_IRQ(LOONGSON_UART_IRQ); 85 else if (pending & CAUSEF_IP2) /* South Bridge */ 86 i8259_irqdispatch(); 87 else 88 spurious_interrupt(); 89} 90 91static irqreturn_t ip6_action(int cpl, void *dev_id) 92{ 93 return IRQ_HANDLED; 94} 95 96static struct irqaction ip6_irqaction = { 97 .handler = ip6_action, 98 .name = "cascade", 99 .flags = IRQF_SHARED | IRQF_NO_THREAD, 100}; 101 102static struct irqaction cascade_irqaction = { 103 .handler = no_action, 104 .name = "cascade", 105 .flags = IRQF_NO_THREAD, 106}; 107 108void __init mach_init_irq(void) 109{ 110 /* init all controller 111 * 0-15 ------> i8259 interrupt 112 * 16-23 ------> mips cpu interrupt 113 * 32-63 ------> bonito irq 114 */ 115 116 /* setup cs5536 as high level trigger */ 117 LOONGSON_INTPOL = LOONGSON_INT_BIT_INT0 | LOONGSON_INT_BIT_INT1; 118 LOONGSON_INTEDGE &= ~(LOONGSON_INT_BIT_INT0 | LOONGSON_INT_BIT_INT1); 119 120 /* Sets the first-level interrupt dispatcher. */ 121 mips_cpu_irq_init(); 122 init_i8259_irqs(); 123 bonito_irq_init(); 124 125 /* setup north bridge irq (bonito) */ 126 setup_irq(LOONGSON_NORTH_BRIDGE_IRQ, &ip6_irqaction); 127 /* setup source bridge irq (i8259) */ 128 setup_irq(LOONGSON_SOUTH_BRIDGE_IRQ, &cascade_irqaction); 129} 130