1/* 2 * This file define the irq handler for MSP SLM subsystem interrupts. 3 * 4 * Copyright 2005-2007 PMC-Sierra, Inc, derived from irq_cpu.c 5 * Author: Andrew Hughes, Andrew_Hughes@pmc-sierra.com 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the 9 * Free Software Foundation; either version 2 of the License, or (at your 10 * option) any later version. 11 */ 12 13#include <linux/init.h> 14#include <linux/interrupt.h> 15#include <linux/kernel.h> 16#include <linux/bitops.h> 17 18#include <asm/system.h> 19 20#include <msp_cic_int.h> 21#include <msp_regs.h> 22 23/* 24 * NOTE: We are only enabling support for VPE0 right now. 25 */ 26 27static inline void unmask_msp_cic_irq(unsigned int irq) 28{ 29 30 /* check for PER interrupt range */ 31 if (irq < MSP_PER_INTBASE) 32 *CIC_VPE0_MSK_REG |= (1 << (irq - MSP_CIC_INTBASE)); 33 else 34 *PER_INT_MSK_REG |= (1 << (irq - MSP_PER_INTBASE)); 35} 36 37static inline void mask_msp_cic_irq(unsigned int irq) 38{ 39 /* check for PER interrupt range */ 40 if (irq < MSP_PER_INTBASE) 41 *CIC_VPE0_MSK_REG &= ~(1 << (irq - MSP_CIC_INTBASE)); 42 else 43 *PER_INT_MSK_REG &= ~(1 << (irq - MSP_PER_INTBASE)); 44} 45 46/* 47 * While we ack the interrupt interrupts are disabled and thus we don't need 48 * to deal with concurrency issues. Same for msp_cic_irq_end. 49 */ 50static inline void ack_msp_cic_irq(unsigned int irq) 51{ 52 mask_msp_cic_irq(irq); 53 54 /* 55 * only really necessary for 18, 16-14 and sometimes 3:0 (since 56 * these can be edge sensitive) but it doesn't hurt for the others. 57 */ 58 59 /* check for PER interrupt range */ 60 if (irq < MSP_PER_INTBASE) 61 *CIC_STS_REG = (1 << (irq - MSP_CIC_INTBASE)); 62 else 63 *PER_INT_STS_REG = (1 << (irq - MSP_PER_INTBASE)); 64} 65 66static struct irq_chip msp_cic_irq_controller = { 67 .name = "MSP_CIC", 68 .ack = ack_msp_cic_irq, 69 .mask = ack_msp_cic_irq, 70 .mask_ack = ack_msp_cic_irq, 71 .unmask = unmask_msp_cic_irq, 72}; 73 74 75void __init msp_cic_irq_init(void) 76{ 77 int i; 78 79 /* Mask/clear interrupts. */ 80 *CIC_VPE0_MSK_REG = 0x00000000; 81 *PER_INT_MSK_REG = 0x00000000; 82 *CIC_STS_REG = 0xFFFFFFFF; 83 *PER_INT_STS_REG = 0xFFFFFFFF; 84 85#if defined(CONFIG_PMC_MSP7120_GW) || defined(CONFIG_PMC_MSP7120_EVAL) 86 /* 87 * The MSP7120 RG and EVBD boards use IRQ[6:4] for PCI. 88 * These inputs map to EXT_INT_POL[6:4] inside the CIC. 89 * They are to be active low, level sensitive. 90 */ 91 *CIC_EXT_CFG_REG &= 0xFFFF8F8F; 92#endif 93 94 /* initialize all the IRQ descriptors */ 95 for (i = MSP_CIC_INTBASE; i < MSP_PER_INTBASE + 32; i++) 96 set_irq_chip_and_handler(i, &msp_cic_irq_controller, 97 handle_level_irq); 98} 99 100void msp_cic_irq_dispatch(void) 101{ 102 u32 pending; 103 int intbase; 104 105 intbase = MSP_CIC_INTBASE; 106 pending = *CIC_STS_REG & *CIC_VPE0_MSK_REG; 107 108 /* check for PER interrupt */ 109 if (pending == (1 << (MSP_INT_PER - MSP_CIC_INTBASE))) { 110 intbase = MSP_PER_INTBASE; 111 pending = *PER_INT_STS_REG & *PER_INT_MSK_REG; 112 } 113 114 /* check for spurious interrupt */ 115 if (pending == 0x00000000) { 116 printk(KERN_ERR 117 "Spurious %s interrupt? status %08x, mask %08x\n", 118 (intbase == MSP_CIC_INTBASE) ? "CIC" : "PER", 119 (intbase == MSP_CIC_INTBASE) ? 120 *CIC_STS_REG : *PER_INT_STS_REG, 121 (intbase == MSP_CIC_INTBASE) ? 122 *CIC_VPE0_MSK_REG : *PER_INT_MSK_REG); 123 return; 124 } 125 126 /* check for the timer and dispatch it first */ 127 if ((intbase == MSP_CIC_INTBASE) && 128 (pending & (1 << (MSP_INT_VPE0_TIMER - MSP_CIC_INTBASE)))) 129 do_IRQ(MSP_INT_VPE0_TIMER); 130 else 131 do_IRQ(ffs(pending) + intbase - 1); 132} 133