intc-2.c (5369B)
1/* 2 * intc-2.c 3 * 4 * General interrupt controller code for the many ColdFire cores that use 5 * interrupt controllers with 63 interrupt sources, organized as 56 fully- 6 * programmable + 7 fixed-level interrupt sources. This includes the 523x 7 * family, the 5270, 5271, 5274, 5275, and the 528x family which have two such 8 * controllers, and the 547x and 548x families which have only one of them. 9 * 10 * The external 7 fixed interrupts are part the the Edge Port unit of these 11 * ColdFire parts. They can be configured as level or edge triggered. 12 * 13 * (C) Copyright 2009-2011, Greg Ungerer <gerg@snapgear.com> 14 * 15 * This file is subject to the terms and conditions of the GNU General Public 16 * License. See the file COPYING in the main directory of this archive 17 * for more details. 18 */ 19 20#include <linux/types.h> 21#include <linux/init.h> 22#include <linux/kernel.h> 23#include <linux/interrupt.h> 24#include <linux/irq.h> 25#include <linux/io.h> 26#include <asm/coldfire.h> 27#include <asm/mcfsim.h> 28#include <asm/traps.h> 29 30/* 31 * Bit definitions for the ICR family of registers. 32 */ 33#define MCFSIM_ICR_LEVEL(l) ((l)<<3) /* Level l intr */ 34#define MCFSIM_ICR_PRI(p) (p) /* Priority p intr */ 35 36/* 37 * The EDGE Port interrupts are the fixed 7 external interrupts. 38 * They need some special treatment, for example they need to be acked. 39 */ 40#define EINT0 64 /* Is not actually used, but spot reserved for it */ 41#define EINT1 65 /* EDGE Port interrupt 1 */ 42#define EINT7 71 /* EDGE Port interrupt 7 */ 43 44#ifdef MCFICM_INTC1 45#define NR_VECS 128 46#else 47#define NR_VECS 64 48#endif 49 50static void intc_irq_mask(struct irq_data *d) 51{ 52 unsigned int irq = d->irq - MCFINT_VECBASE; 53 unsigned long imraddr; 54 u32 val, imrbit; 55 56#ifdef MCFICM_INTC1 57 imraddr = (irq & 0x40) ? MCFICM_INTC1 : MCFICM_INTC0; 58#else 59 imraddr = MCFICM_INTC0; 60#endif 61 imraddr += (irq & 0x20) ? MCFINTC_IMRH : MCFINTC_IMRL; 62 imrbit = 0x1 << (irq & 0x1f); 63 64 val = __raw_readl(imraddr); 65 __raw_writel(val | imrbit, imraddr); 66} 67 68static void intc_irq_unmask(struct irq_data *d) 69{ 70 unsigned int irq = d->irq - MCFINT_VECBASE; 71 unsigned long imraddr; 72 u32 val, imrbit; 73 74#ifdef MCFICM_INTC1 75 imraddr = (irq & 0x40) ? MCFICM_INTC1 : MCFICM_INTC0; 76#else 77 imraddr = MCFICM_INTC0; 78#endif 79 imraddr += ((irq & 0x20) ? MCFINTC_IMRH : MCFINTC_IMRL); 80 imrbit = 0x1 << (irq & 0x1f); 81 82 /* Don't set the "maskall" bit! */ 83 if ((irq & 0x20) == 0) 84 imrbit |= 0x1; 85 86 val = __raw_readl(imraddr); 87 __raw_writel(val & ~imrbit, imraddr); 88} 89 90/* 91 * Only the external (or EDGE Port) interrupts need to be acknowledged 92 * here, as part of the IRQ handler. They only really need to be ack'ed 93 * if they are in edge triggered mode, but there is no harm in doing it 94 * for all types. 95 */ 96static void intc_irq_ack(struct irq_data *d) 97{ 98 unsigned int irq = d->irq; 99 100 __raw_writeb(0x1 << (irq - EINT0), MCFEPORT_EPFR); 101} 102 103/* 104 * Each vector needs a unique priority and level associated with it. 105 * We don't really care so much what they are, we don't rely on the 106 * traditional priority interrupt scheme of the m68k/ColdFire. This 107 * only needs to be set once for an interrupt, and we will never change 108 * these values once we have set them. 109 */ 110static u8 intc_intpri = MCFSIM_ICR_LEVEL(6) | MCFSIM_ICR_PRI(6); 111 112static unsigned int intc_irq_startup(struct irq_data *d) 113{ 114 unsigned int irq = d->irq - MCFINT_VECBASE; 115 unsigned long icraddr; 116 117#ifdef MCFICM_INTC1 118 icraddr = (irq & 0x40) ? MCFICM_INTC1 : MCFICM_INTC0; 119#else 120 icraddr = MCFICM_INTC0; 121#endif 122 icraddr += MCFINTC_ICR0 + (irq & 0x3f); 123 if (__raw_readb(icraddr) == 0) 124 __raw_writeb(intc_intpri--, icraddr); 125 126 irq = d->irq; 127 if ((irq >= EINT1) && (irq <= EINT7)) { 128 u8 v; 129 130 irq -= EINT0; 131 132 /* Set EPORT line as input */ 133 v = __raw_readb(MCFEPORT_EPDDR); 134 __raw_writeb(v & ~(0x1 << irq), MCFEPORT_EPDDR); 135 136 /* Set EPORT line as interrupt source */ 137 v = __raw_readb(MCFEPORT_EPIER); 138 __raw_writeb(v | (0x1 << irq), MCFEPORT_EPIER); 139 } 140 141 intc_irq_unmask(d); 142 return 0; 143} 144 145static int intc_irq_set_type(struct irq_data *d, unsigned int type) 146{ 147 unsigned int irq = d->irq; 148 u16 pa, tb; 149 150 switch (type) { 151 case IRQ_TYPE_EDGE_RISING: 152 tb = 0x1; 153 break; 154 case IRQ_TYPE_EDGE_FALLING: 155 tb = 0x2; 156 break; 157 case IRQ_TYPE_EDGE_BOTH: 158 tb = 0x3; 159 break; 160 default: 161 /* Level triggered */ 162 tb = 0; 163 break; 164 } 165 166 if (tb) 167 irq_set_handler(irq, handle_edge_irq); 168 169 irq -= EINT0; 170 pa = __raw_readw(MCFEPORT_EPPAR); 171 pa = (pa & ~(0x3 << (irq * 2))) | (tb << (irq * 2)); 172 __raw_writew(pa, MCFEPORT_EPPAR); 173 174 return 0; 175} 176 177static struct irq_chip intc_irq_chip = { 178 .name = "CF-INTC", 179 .irq_startup = intc_irq_startup, 180 .irq_mask = intc_irq_mask, 181 .irq_unmask = intc_irq_unmask, 182}; 183 184static struct irq_chip intc_irq_chip_edge_port = { 185 .name = "CF-INTC-EP", 186 .irq_startup = intc_irq_startup, 187 .irq_mask = intc_irq_mask, 188 .irq_unmask = intc_irq_unmask, 189 .irq_ack = intc_irq_ack, 190 .irq_set_type = intc_irq_set_type, 191}; 192 193void __init init_IRQ(void) 194{ 195 int irq; 196 197 /* Mask all interrupt sources */ 198 __raw_writel(0x1, MCFICM_INTC0 + MCFINTC_IMRL); 199#ifdef MCFICM_INTC1 200 __raw_writel(0x1, MCFICM_INTC1 + MCFINTC_IMRL); 201#endif 202 203 for (irq = MCFINT_VECBASE; (irq < MCFINT_VECBASE + NR_VECS); irq++) { 204 if ((irq >= EINT1) && (irq <=EINT7)) 205 irq_set_chip(irq, &intc_irq_chip_edge_port); 206 else 207 irq_set_chip(irq, &intc_irq_chip); 208 irq_set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH); 209 irq_set_handler(irq, handle_level_irq); 210 } 211} 212