Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
intc-5272.c
Go to the documentation of this file.
1 /*
2  * intc.c -- interrupt controller or ColdFire 5272 SoC
3  *
4  * (C) Copyright 2009, Greg Ungerer <[email protected]>
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/kernel_stat.h>
16 #include <linux/irq.h>
17 #include <linux/io.h>
18 #include <asm/coldfire.h>
19 #include <asm/mcfsim.h>
20 #include <asm/traps.h>
21 
22 /*
23  * The 5272 ColdFire interrupt controller is nothing like any other
24  * ColdFire interrupt controller - it truly is completely different.
25  * Given its age it is unlikely to be used on any other ColdFire CPU.
26  */
27 
28 /*
29  * The masking and priproty setting of interrupts on the 5272 is done
30  * via a set of 4 "Interrupt Controller Registers" (ICR). There is a
31  * loose mapping of vector number to register and internal bits, but
32  * a table is the easiest and quickest way to map them.
33  *
34  * Note that the external interrupts are edge triggered (unlike the
35  * internal interrupt sources which are level triggered). Which means
36  * they also need acknowledging via acknowledge bits.
37  */
38 struct irqmap {
39  unsigned char icr;
40  unsigned char index;
41  unsigned char ack;
42 };
43 
44 static struct irqmap intc_irqmap[MCFINT_VECMAX - MCFINT_VECBASE] = {
45  /*MCF_IRQ_SPURIOUS*/ { .icr = 0, .index = 0, .ack = 0, },
46  /*MCF_IRQ_EINT1*/ { .icr = MCFSIM_ICR1, .index = 28, .ack = 1, },
47  /*MCF_IRQ_EINT2*/ { .icr = MCFSIM_ICR1, .index = 24, .ack = 1, },
48  /*MCF_IRQ_EINT3*/ { .icr = MCFSIM_ICR1, .index = 20, .ack = 1, },
49  /*MCF_IRQ_EINT4*/ { .icr = MCFSIM_ICR1, .index = 16, .ack = 1, },
50  /*MCF_IRQ_TIMER1*/ { .icr = MCFSIM_ICR1, .index = 12, .ack = 0, },
51  /*MCF_IRQ_TIMER2*/ { .icr = MCFSIM_ICR1, .index = 8, .ack = 0, },
52  /*MCF_IRQ_TIMER3*/ { .icr = MCFSIM_ICR1, .index = 4, .ack = 0, },
53  /*MCF_IRQ_TIMER4*/ { .icr = MCFSIM_ICR1, .index = 0, .ack = 0, },
54  /*MCF_IRQ_UART1*/ { .icr = MCFSIM_ICR2, .index = 28, .ack = 0, },
55  /*MCF_IRQ_UART2*/ { .icr = MCFSIM_ICR2, .index = 24, .ack = 0, },
56  /*MCF_IRQ_PLIP*/ { .icr = MCFSIM_ICR2, .index = 20, .ack = 0, },
57  /*MCF_IRQ_PLIA*/ { .icr = MCFSIM_ICR2, .index = 16, .ack = 0, },
58  /*MCF_IRQ_USB0*/ { .icr = MCFSIM_ICR2, .index = 12, .ack = 0, },
59  /*MCF_IRQ_USB1*/ { .icr = MCFSIM_ICR2, .index = 8, .ack = 0, },
60  /*MCF_IRQ_USB2*/ { .icr = MCFSIM_ICR2, .index = 4, .ack = 0, },
61  /*MCF_IRQ_USB3*/ { .icr = MCFSIM_ICR2, .index = 0, .ack = 0, },
62  /*MCF_IRQ_USB4*/ { .icr = MCFSIM_ICR3, .index = 28, .ack = 0, },
63  /*MCF_IRQ_USB5*/ { .icr = MCFSIM_ICR3, .index = 24, .ack = 0, },
64  /*MCF_IRQ_USB6*/ { .icr = MCFSIM_ICR3, .index = 20, .ack = 0, },
65  /*MCF_IRQ_USB7*/ { .icr = MCFSIM_ICR3, .index = 16, .ack = 0, },
66  /*MCF_IRQ_DMA*/ { .icr = MCFSIM_ICR3, .index = 12, .ack = 0, },
67  /*MCF_IRQ_ERX*/ { .icr = MCFSIM_ICR3, .index = 8, .ack = 0, },
68  /*MCF_IRQ_ETX*/ { .icr = MCFSIM_ICR3, .index = 4, .ack = 0, },
69  /*MCF_IRQ_ENTC*/ { .icr = MCFSIM_ICR3, .index = 0, .ack = 0, },
70  /*MCF_IRQ_QSPI*/ { .icr = MCFSIM_ICR4, .index = 28, .ack = 0, },
71  /*MCF_IRQ_EINT5*/ { .icr = MCFSIM_ICR4, .index = 24, .ack = 1, },
72  /*MCF_IRQ_EINT6*/ { .icr = MCFSIM_ICR4, .index = 20, .ack = 1, },
73  /*MCF_IRQ_SWTO*/ { .icr = MCFSIM_ICR4, .index = 16, .ack = 0, },
74 };
75 
76 /*
77  * The act of masking the interrupt also has a side effect of 'ack'ing
78  * an interrupt on this irq (for the external irqs). So this mask function
79  * is also an ack_mask function.
80  */
81 static void intc_irq_mask(struct irq_data *d)
82 {
83  unsigned int irq = d->irq;
84 
85  if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) {
86  u32 v;
87  irq -= MCFINT_VECBASE;
88  v = 0x8 << intc_irqmap[irq].index;
89  writel(v, intc_irqmap[irq].icr);
90  }
91 }
92 
93 static void intc_irq_unmask(struct irq_data *d)
94 {
95  unsigned int irq = d->irq;
96 
97  if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) {
98  u32 v;
99  irq -= MCFINT_VECBASE;
100  v = 0xd << intc_irqmap[irq].index;
101  writel(v, intc_irqmap[irq].icr);
102  }
103 }
104 
105 static void intc_irq_ack(struct irq_data *d)
106 {
107  unsigned int irq = d->irq;
108 
109  /* Only external interrupts are acked */
110  if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) {
111  irq -= MCFINT_VECBASE;
112  if (intc_irqmap[irq].ack) {
113  u32 v;
114  v = readl(intc_irqmap[irq].icr);
115  v &= (0x7 << intc_irqmap[irq].index);
116  v |= (0x8 << intc_irqmap[irq].index);
117  writel(v, intc_irqmap[irq].icr);
118  }
119  }
120 }
121 
122 static int intc_irq_set_type(struct irq_data *d, unsigned int type)
123 {
124  unsigned int irq = d->irq;
125 
126  if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) {
127  irq -= MCFINT_VECBASE;
128  if (intc_irqmap[irq].ack) {
129  u32 v;
130  v = readl(MCFSIM_PITR);
131  if (type == IRQ_TYPE_EDGE_FALLING)
132  v &= ~(0x1 << (32 - irq));
133  else
134  v |= (0x1 << (32 - irq));
135  writel(v, MCFSIM_PITR);
136  }
137  }
138  return 0;
139 }
140 
141 /*
142  * Simple flow handler to deal with the external edge triggered interrupts.
143  * We need to be careful with the masking/acking due to the side effects
144  * of masking an interrupt.
145  */
146 static void intc_external_irq(unsigned int irq, struct irq_desc *desc)
147 {
148  irq_desc_get_chip(desc)->irq_ack(&desc->irq_data);
149  handle_simple_irq(irq, desc);
150 }
151 
152 static struct irq_chip intc_irq_chip = {
153  .name = "CF-INTC",
154  .irq_mask = intc_irq_mask,
155  .irq_unmask = intc_irq_unmask,
156  .irq_mask_ack = intc_irq_mask,
157  .irq_ack = intc_irq_ack,
158  .irq_set_type = intc_irq_set_type,
159 };
160 
161 void __init init_IRQ(void)
162 {
163  int irq, edge;
164 
165  /* Mask all interrupt sources */
166  writel(0x88888888, MCFSIM_ICR1);
167  writel(0x88888888, MCFSIM_ICR2);
168  writel(0x88888888, MCFSIM_ICR3);
169  writel(0x88888888, MCFSIM_ICR4);
170 
171  for (irq = 0; (irq < NR_IRQS); irq++) {
172  irq_set_chip(irq, &intc_irq_chip);
173  edge = 0;
174  if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX))
175  edge = intc_irqmap[irq - MCFINT_VECBASE].ack;
176  if (edge) {
178  irq_set_handler(irq, intc_external_irq);
179  } else {
181  irq_set_handler(irq, handle_level_irq);
182  }
183  }
184 }
185