1 | /* |
2 | * intc.c -- support for the old ColdFire interrupt controller |
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/traps.h> |
18 | #include <asm/coldfire.h> |
19 | #include <asm/mcfsim.h> |
20 | |
21 | /* |
22 | * The mapping of irq number to a mask register bit is not one-to-one. |
23 | * The irq numbers are either based on "level" of interrupt or fixed |
24 | * for an autovector-able interrupt. So we keep a local data structure |
25 | * that maps from irq to mask register. Not all interrupts will have |
26 | * an IMR bit. |
27 | */ |
28 | unsigned char mcf_irq2imr[NR_IRQS]; |
29 | |
30 | /* |
31 | * Define the minimum and maximum external interrupt numbers. |
32 | * This is also used as the "level" interrupt numbers. |
33 | */ |
34 | #define EIRQ1 25 |
35 | #define EIRQ7 31 |
36 | |
37 | /* |
38 | * In the early version 2 core ColdFire parts the IMR register was 16 bits |
39 | * in size. Version 3 (and later version 2) core parts have a 32 bit |
40 | * sized IMR register. Provide some size independent methods to access the |
41 | * IMR register. |
42 | */ |
43 | #ifdef MCFSIM_IMR_IS_16BITS |
44 | |
45 | void mcf_setimr(int index) |
46 | { |
47 | u16 imr; |
48 | imr = __raw_readw(MCFSIM_IMR); |
49 | __raw_writew(imr | (0x1 << index), MCFSIM_IMR); |
50 | } |
51 | |
52 | void mcf_clrimr(int index) |
53 | { |
54 | u16 imr; |
55 | imr = __raw_readw(MCFSIM_IMR); |
56 | __raw_writew(imr & ~(0x1 << index), MCFSIM_IMR); |
57 | } |
58 | |
59 | static void mcf_maskimr(unsigned int mask) |
60 | { |
61 | u16 imr; |
62 | imr = __raw_readw(MCFSIM_IMR); |
63 | imr |= mask; |
64 | __raw_writew(imr, MCFSIM_IMR); |
65 | } |
66 | |
67 | #else |
68 | |
69 | void mcf_setimr(int index) |
70 | { |
71 | u32 imr; |
72 | imr = __raw_readl(addr: MCFSIM_IMR); |
73 | __raw_writel(val: imr | (0x1 << index), addr: MCFSIM_IMR); |
74 | } |
75 | |
76 | void mcf_clrimr(int index) |
77 | { |
78 | u32 imr; |
79 | imr = __raw_readl(addr: MCFSIM_IMR); |
80 | __raw_writel(val: imr & ~(0x1 << index), addr: MCFSIM_IMR); |
81 | } |
82 | |
83 | static void mcf_maskimr(unsigned int mask) |
84 | { |
85 | u32 imr; |
86 | imr = __raw_readl(addr: MCFSIM_IMR); |
87 | imr |= mask; |
88 | __raw_writel(val: imr, addr: MCFSIM_IMR); |
89 | } |
90 | |
91 | #endif |
92 | |
93 | /* |
94 | * Interrupts can be "vectored" on the ColdFire cores that support this old |
95 | * interrupt controller. That is, the device raising the interrupt can also |
96 | * supply the vector number to interrupt through. The AVR register of the |
97 | * interrupt controller enables or disables this for each external interrupt, |
98 | * so provide generic support for this. Setting this up is out-of-band for |
99 | * the interrupt system API's, and needs to be done by the driver that |
100 | * supports this device. Very few devices actually use this. |
101 | */ |
102 | void mcf_autovector(int irq) |
103 | { |
104 | #ifdef MCFSIM_AVR |
105 | if ((irq >= EIRQ1) && (irq <= EIRQ7)) { |
106 | u8 avec; |
107 | avec = __raw_readb(MCFSIM_AVR); |
108 | avec |= (0x1 << (irq - EIRQ1 + 1)); |
109 | __raw_writeb(avec, MCFSIM_AVR); |
110 | } |
111 | #endif |
112 | } |
113 | |
114 | static void intc_irq_mask(struct irq_data *d) |
115 | { |
116 | if (mcf_irq2imr[d->irq]) |
117 | mcf_setimr(index: mcf_irq2imr[d->irq]); |
118 | } |
119 | |
120 | static void intc_irq_unmask(struct irq_data *d) |
121 | { |
122 | if (mcf_irq2imr[d->irq]) |
123 | mcf_clrimr(index: mcf_irq2imr[d->irq]); |
124 | } |
125 | |
126 | static int intc_irq_set_type(struct irq_data *d, unsigned int type) |
127 | { |
128 | return 0; |
129 | } |
130 | |
131 | static struct irq_chip intc_irq_chip = { |
132 | .name = "CF-INTC" , |
133 | .irq_mask = intc_irq_mask, |
134 | .irq_unmask = intc_irq_unmask, |
135 | .irq_set_type = intc_irq_set_type, |
136 | }; |
137 | |
138 | void __init init_IRQ(void) |
139 | { |
140 | int irq; |
141 | |
142 | mcf_maskimr(mask: 0xffffffff); |
143 | |
144 | for (irq = 0; (irq < NR_IRQS); irq++) { |
145 | irq_set_chip(irq, chip: &intc_irq_chip); |
146 | irq_set_irq_type(irq, type: IRQ_TYPE_LEVEL_HIGH); |
147 | irq_set_handler(irq, handle: handle_level_irq); |
148 | } |
149 | } |
150 | |
151 | |