1/* 2 * intc.c -- interrupt controller or ColdFire 5272 SoC 3 * 4 * (C) Copyright 2009, Greg Ungerer <gerg@snapgear.com> 5 * 6 * This file is subject to the terms and conditions of the GNU General Public 7 * License. See the file COPYING in the main directory of this archive 8 * for more details. 9 */ 10 11#include <linux/types.h> 12#include <linux/init.h> 13#include <linux/kernel.h> 14#include <linux/interrupt.h> 15#include <linux/irq.h> 16#include <linux/io.h> 17#include <asm/coldfire.h> 18#include <asm/mcfsim.h> 19#include <asm/traps.h> 20 21/* 22 * The 5272 ColdFire interrupt controller is nothing like any other 23 * ColdFire interrupt controller - it truly is completely different. 24 * Given its age it is unlikely to be used on any other ColdFire CPU. 25 */ 26 27/* 28 * The masking and priproty setting of interrupts on the 5272 is done 29 * via a set of 4 "Interrupt Controller Registers" (ICR). There is a 30 * loose mapping of vector number to register and internal bits, but 31 * a table is the easiest and quickest way to map them. 32 */ 33struct irqmap { 34 unsigned char icr; 35 unsigned char index; 36 unsigned char ack; 37}; 38 39static struct irqmap intc_irqmap[MCFINT_VECMAX - MCFINT_VECBASE] = { 40 /*MCF_IRQ_SPURIOUS*/ { .icr = 0, .index = 0, .ack = 0, }, 41 /*MCF_IRQ_EINT1*/ { .icr = MCFSIM_ICR1, .index = 28, .ack = 1, }, 42 /*MCF_IRQ_EINT2*/ { .icr = MCFSIM_ICR1, .index = 24, .ack = 1, }, 43 /*MCF_IRQ_EINT3*/ { .icr = MCFSIM_ICR1, .index = 20, .ack = 1, }, 44 /*MCF_IRQ_EINT4*/ { .icr = MCFSIM_ICR1, .index = 16, .ack = 1, }, 45 /*MCF_IRQ_TIMER1*/ { .icr = MCFSIM_ICR1, .index = 12, .ack = 0, }, 46 /*MCF_IRQ_TIMER2*/ { .icr = MCFSIM_ICR1, .index = 8, .ack = 0, }, 47 /*MCF_IRQ_TIMER3*/ { .icr = MCFSIM_ICR1, .index = 4, .ack = 0, }, 48 /*MCF_IRQ_TIMER4*/ { .icr = MCFSIM_ICR1, .index = 0, .ack = 0, }, 49 /*MCF_IRQ_UART1*/ { .icr = MCFSIM_ICR2, .index = 28, .ack = 0, }, 50 /*MCF_IRQ_UART2*/ { .icr = MCFSIM_ICR2, .index = 24, .ack = 0, }, 51 /*MCF_IRQ_PLIP*/ { .icr = MCFSIM_ICR2, .index = 20, .ack = 0, }, 52 /*MCF_IRQ_PLIA*/ { .icr = MCFSIM_ICR2, .index = 16, .ack = 0, }, 53 /*MCF_IRQ_USB0*/ { .icr = MCFSIM_ICR2, .index = 12, .ack = 0, }, 54 /*MCF_IRQ_USB1*/ { .icr = MCFSIM_ICR2, .index = 8, .ack = 0, }, 55 /*MCF_IRQ_USB2*/ { .icr = MCFSIM_ICR2, .index = 4, .ack = 0, }, 56 /*MCF_IRQ_USB3*/ { .icr = MCFSIM_ICR2, .index = 0, .ack = 0, }, 57 /*MCF_IRQ_USB4*/ { .icr = MCFSIM_ICR3, .index = 28, .ack = 0, }, 58 /*MCF_IRQ_USB5*/ { .icr = MCFSIM_ICR3, .index = 24, .ack = 0, }, 59 /*MCF_IRQ_USB6*/ { .icr = MCFSIM_ICR3, .index = 20, .ack = 0, }, 60 /*MCF_IRQ_USB7*/ { .icr = MCFSIM_ICR3, .index = 16, .ack = 0, }, 61 /*MCF_IRQ_DMA*/ { .icr = MCFSIM_ICR3, .index = 12, .ack = 0, }, 62 /*MCF_IRQ_ERX*/ { .icr = MCFSIM_ICR3, .index = 8, .ack = 0, }, 63 /*MCF_IRQ_ETX*/ { .icr = MCFSIM_ICR3, .index = 4, .ack = 0, }, 64 /*MCF_IRQ_ENTC*/ { .icr = MCFSIM_ICR3, .index = 0, .ack = 0, }, 65 /*MCF_IRQ_QSPI*/ { .icr = MCFSIM_ICR4, .index = 28, .ack = 0, }, 66 /*MCF_IRQ_EINT5*/ { .icr = MCFSIM_ICR4, .index = 24, .ack = 1, }, 67 /*MCF_IRQ_EINT6*/ { .icr = MCFSIM_ICR4, .index = 20, .ack = 1, }, 68 /*MCF_IRQ_SWTO*/ { .icr = MCFSIM_ICR4, .index = 16, .ack = 0, }, 69}; 70 71static void intc_irq_mask(unsigned int irq) 72{ 73 if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) { 74 u32 v; 75 irq -= MCFINT_VECBASE; 76 v = 0x8 << intc_irqmap[irq].index; 77 writel(v, MCF_MBAR + intc_irqmap[irq].icr); 78 } 79} 80 81static void intc_irq_unmask(unsigned int irq) 82{ 83 if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) { 84 u32 v; 85 irq -= MCFINT_VECBASE; 86 v = 0xd << intc_irqmap[irq].index; 87 writel(v, MCF_MBAR + intc_irqmap[irq].icr); 88 } 89} 90 91static void intc_irq_ack(unsigned int irq) 92{ 93 /* Only external interrupts are acked */ 94 if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) { 95 irq -= MCFINT_VECBASE; 96 if (intc_irqmap[irq].ack) { 97 u32 v; 98 v = 0xd << intc_irqmap[irq].index; 99 writel(v, MCF_MBAR + intc_irqmap[irq].icr); 100 } 101 } 102} 103 104static int intc_irq_set_type(unsigned int irq, unsigned int type) 105{ 106 /* We can set the edge type here for external interrupts */ 107 return 0; 108} 109 110static struct irq_chip intc_irq_chip = { 111 .name = "CF-INTC", 112 .mask = intc_irq_mask, 113 .unmask = intc_irq_unmask, 114 .ack = intc_irq_ack, 115 .set_type = intc_irq_set_type, 116}; 117 118void __init init_IRQ(void) 119{ 120 int irq; 121 122 init_vectors(); 123 124 /* Mask all interrupt sources */ 125 writel(0x88888888, MCF_MBAR + MCFSIM_ICR1); 126 writel(0x88888888, MCF_MBAR + MCFSIM_ICR2); 127 writel(0x88888888, MCF_MBAR + MCFSIM_ICR3); 128 writel(0x88888888, MCF_MBAR + MCFSIM_ICR4); 129 130 for (irq = 0; (irq < NR_IRQS); irq++) { 131 irq_desc[irq].status = IRQ_DISABLED; 132 irq_desc[irq].action = NULL; 133 irq_desc[irq].depth = 1; 134 irq_desc[irq].chip = &intc_irq_chip; 135 intc_irq_set_type(irq, 0); 136 } 137} 138