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.
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.
13 * (C) Copyright 2009-2011, Greg Ungerer <gerg@snapgear.com>
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
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>
26 #include <asm/coldfire.h>
27 #include <asm/mcfsim.h>
28 #include <asm/traps.h>
31 * Bit definitions for the ICR family of registers.
33 #define MCFSIM_ICR_LEVEL(l) ((l)<<3) /* Level l intr */
34 #define MCFSIM_ICR_PRI(p) (p) /* Priority p intr */
37 * The EDGE Port interrupts are the fixed 7 external interrupts.
38 * They need some special treatment, for example they need to be acked.
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 */
50 static void intc_irq_mask(struct irq_data
*d
)
52 unsigned int irq
= d
->irq
- MCFINT_VECBASE
;
53 unsigned long imraddr
;
57 imraddr
= (irq
& 0x40) ? MCFICM_INTC1
: MCFICM_INTC0
;
59 imraddr
= MCFICM_INTC0
;
61 imraddr
+= (irq
& 0x20) ? MCFINTC_IMRH
: MCFINTC_IMRL
;
62 imrbit
= 0x1 << (irq
& 0x1f);
64 val
= __raw_readl(imraddr
);
65 __raw_writel(val
| imrbit
, imraddr
);
68 static void intc_irq_unmask(struct irq_data
*d
)
70 unsigned int irq
= d
->irq
- MCFINT_VECBASE
;
71 unsigned long imraddr
;
75 imraddr
= (irq
& 0x40) ? MCFICM_INTC1
: MCFICM_INTC0
;
77 imraddr
= MCFICM_INTC0
;
79 imraddr
+= ((irq
& 0x20) ? MCFINTC_IMRH
: MCFINTC_IMRL
);
80 imrbit
= 0x1 << (irq
& 0x1f);
82 /* Don't set the "maskall" bit! */
83 if ((irq
& 0x20) == 0)
86 val
= __raw_readl(imraddr
);
87 __raw_writel(val
& ~imrbit
, imraddr
);
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
96 static void intc_irq_ack(struct irq_data
*d
)
98 unsigned int irq
= d
->irq
;
100 __raw_writeb(0x1 << (irq
- EINT0
), MCFEPORT_EPFR
);
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.
110 static u8 intc_intpri
= MCFSIM_ICR_LEVEL(6) | MCFSIM_ICR_PRI(6);
112 static unsigned int intc_irq_startup(struct irq_data
*d
)
114 unsigned int irq
= d
->irq
- MCFINT_VECBASE
;
115 unsigned long icraddr
;
118 icraddr
= (irq
& 0x40) ? MCFICM_INTC1
: MCFICM_INTC0
;
120 icraddr
= MCFICM_INTC0
;
122 icraddr
+= MCFINTC_ICR0
+ (irq
& 0x3f);
123 if (__raw_readb(icraddr
) == 0)
124 __raw_writeb(intc_intpri
--, icraddr
);
127 if ((irq
>= EINT1
) && (irq
<= EINT7
)) {
132 /* Set EPORT line as input */
133 v
= __raw_readb(MCFEPORT_EPDDR
);
134 __raw_writeb(v
& ~(0x1 << irq
), MCFEPORT_EPDDR
);
136 /* Set EPORT line as interrupt source */
137 v
= __raw_readb(MCFEPORT_EPIER
);
138 __raw_writeb(v
| (0x1 << irq
), MCFEPORT_EPIER
);
145 static int intc_irq_set_type(struct irq_data
*d
, unsigned int type
)
147 unsigned int irq
= d
->irq
;
151 case IRQ_TYPE_EDGE_RISING
:
154 case IRQ_TYPE_EDGE_FALLING
:
157 case IRQ_TYPE_EDGE_BOTH
:
161 /* Level triggered */
167 irq_set_handler(irq
, handle_edge_irq
);
170 pa
= __raw_readw(MCFEPORT_EPPAR
);
171 pa
= (pa
& ~(0x3 << (irq
* 2))) | (tb
<< (irq
* 2));
172 __raw_writew(pa
, MCFEPORT_EPPAR
);
177 static struct irq_chip intc_irq_chip
= {
179 .irq_startup
= intc_irq_startup
,
180 .irq_mask
= intc_irq_mask
,
181 .irq_unmask
= intc_irq_unmask
,
184 static 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
,
193 void __init
init_IRQ(void)
197 /* Mask all interrupt sources */
198 __raw_writel(0x1, MCFICM_INTC0
+ MCFINTC_IMRL
);
200 __raw_writel(0x1, MCFICM_INTC1
+ MCFINTC_IMRL
);
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
);
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
);