diff options
Diffstat (limited to 'arch/arm/mach-vt8500/irq.c')
-rw-r--r-- | arch/arm/mach-vt8500/irq.c | 253 |
1 files changed, 0 insertions, 253 deletions
diff --git a/arch/arm/mach-vt8500/irq.c b/arch/arm/mach-vt8500/irq.c deleted file mode 100644 index b9cf5ce9efbb..000000000000 --- a/arch/arm/mach-vt8500/irq.c +++ /dev/null | |||
@@ -1,253 +0,0 @@ | |||
1 | /* | ||
2 | * arch/arm/mach-vt8500/irq.c | ||
3 | * | ||
4 | * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz> | ||
5 | * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | /* | ||
23 | * This file is copied and modified from the original irq.c provided by | ||
24 | * Alexey Charkov. Minor changes have been made for Device Tree Support. | ||
25 | */ | ||
26 | |||
27 | #include <linux/slab.h> | ||
28 | #include <linux/io.h> | ||
29 | #include <linux/irq.h> | ||
30 | #include <linux/irqdomain.h> | ||
31 | #include <linux/interrupt.h> | ||
32 | #include <linux/bitops.h> | ||
33 | |||
34 | #include <linux/of.h> | ||
35 | #include <linux/of_irq.h> | ||
36 | #include <linux/of_address.h> | ||
37 | |||
38 | #include <asm/irq.h> | ||
39 | #include <asm/exception.h> | ||
40 | |||
41 | #define VT8500_ICPC_IRQ 0x20 | ||
42 | #define VT8500_ICPC_FIQ 0x24 | ||
43 | #define VT8500_ICDC 0x40 /* Destination Control 64*u32 */ | ||
44 | #define VT8500_ICIS 0x80 /* Interrupt status, 16*u32 */ | ||
45 | |||
46 | /* ICPC */ | ||
47 | #define ICPC_MASK 0x3F | ||
48 | #define ICPC_ROTATE BIT(6) | ||
49 | |||
50 | /* IC_DCTR */ | ||
51 | #define ICDC_IRQ 0x00 | ||
52 | #define ICDC_FIQ 0x01 | ||
53 | #define ICDC_DSS0 0x02 | ||
54 | #define ICDC_DSS1 0x03 | ||
55 | #define ICDC_DSS2 0x04 | ||
56 | #define ICDC_DSS3 0x05 | ||
57 | #define ICDC_DSS4 0x06 | ||
58 | #define ICDC_DSS5 0x07 | ||
59 | |||
60 | #define VT8500_INT_DISABLE 0 | ||
61 | #define VT8500_INT_ENABLE BIT(3) | ||
62 | |||
63 | #define VT8500_TRIGGER_HIGH 0 | ||
64 | #define VT8500_TRIGGER_RISING BIT(5) | ||
65 | #define VT8500_TRIGGER_FALLING BIT(6) | ||
66 | #define VT8500_EDGE ( VT8500_TRIGGER_RISING \ | ||
67 | | VT8500_TRIGGER_FALLING) | ||
68 | |||
69 | /* vt8500 has 1 intc, wm8505 and wm8650 have 2 */ | ||
70 | #define VT8500_INTC_MAX 2 | ||
71 | |||
72 | struct vt8500_irq_data { | ||
73 | void __iomem *base; /* IO Memory base address */ | ||
74 | struct irq_domain *domain; /* Domain for this controller */ | ||
75 | }; | ||
76 | |||
77 | /* Global variable for accessing io-mem addresses */ | ||
78 | static struct vt8500_irq_data intc[VT8500_INTC_MAX]; | ||
79 | static u32 active_cnt = 0; | ||
80 | |||
81 | static void vt8500_irq_mask(struct irq_data *d) | ||
82 | { | ||
83 | struct vt8500_irq_data *priv = d->domain->host_data; | ||
84 | void __iomem *base = priv->base; | ||
85 | void __iomem *stat_reg = base + VT8500_ICIS + (d->hwirq < 32 ? 0 : 4); | ||
86 | u8 edge, dctr; | ||
87 | u32 status; | ||
88 | |||
89 | edge = readb(base + VT8500_ICDC + d->hwirq) & VT8500_EDGE; | ||
90 | if (edge) { | ||
91 | status = readl(stat_reg); | ||
92 | |||
93 | status |= (1 << (d->hwirq & 0x1f)); | ||
94 | writel(status, stat_reg); | ||
95 | } else { | ||
96 | dctr = readb(base + VT8500_ICDC + d->hwirq); | ||
97 | dctr &= ~VT8500_INT_ENABLE; | ||
98 | writeb(dctr, base + VT8500_ICDC + d->hwirq); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | static void vt8500_irq_unmask(struct irq_data *d) | ||
103 | { | ||
104 | struct vt8500_irq_data *priv = d->domain->host_data; | ||
105 | void __iomem *base = priv->base; | ||
106 | u8 dctr; | ||
107 | |||
108 | dctr = readb(base + VT8500_ICDC + d->hwirq); | ||
109 | dctr |= VT8500_INT_ENABLE; | ||
110 | writeb(dctr, base + VT8500_ICDC + d->hwirq); | ||
111 | } | ||
112 | |||
113 | static int vt8500_irq_set_type(struct irq_data *d, unsigned int flow_type) | ||
114 | { | ||
115 | struct vt8500_irq_data *priv = d->domain->host_data; | ||
116 | void __iomem *base = priv->base; | ||
117 | u8 dctr; | ||
118 | |||
119 | dctr = readb(base + VT8500_ICDC + d->hwirq); | ||
120 | dctr &= ~VT8500_EDGE; | ||
121 | |||
122 | switch (flow_type) { | ||
123 | case IRQF_TRIGGER_LOW: | ||
124 | return -EINVAL; | ||
125 | case IRQF_TRIGGER_HIGH: | ||
126 | dctr |= VT8500_TRIGGER_HIGH; | ||
127 | __irq_set_handler_locked(d->irq, handle_level_irq); | ||
128 | break; | ||
129 | case IRQF_TRIGGER_FALLING: | ||
130 | dctr |= VT8500_TRIGGER_FALLING; | ||
131 | __irq_set_handler_locked(d->irq, handle_edge_irq); | ||
132 | break; | ||
133 | case IRQF_TRIGGER_RISING: | ||
134 | dctr |= VT8500_TRIGGER_RISING; | ||
135 | __irq_set_handler_locked(d->irq, handle_edge_irq); | ||
136 | break; | ||
137 | } | ||
138 | writeb(dctr, base + VT8500_ICDC + d->hwirq); | ||
139 | |||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | static struct irq_chip vt8500_irq_chip = { | ||
144 | .name = "vt8500", | ||
145 | .irq_ack = vt8500_irq_mask, | ||
146 | .irq_mask = vt8500_irq_mask, | ||
147 | .irq_unmask = vt8500_irq_unmask, | ||
148 | .irq_set_type = vt8500_irq_set_type, | ||
149 | }; | ||
150 | |||
151 | static void __init vt8500_init_irq_hw(void __iomem *base) | ||
152 | { | ||
153 | u32 i; | ||
154 | |||
155 | /* Enable rotating priority for IRQ */ | ||
156 | writel(ICPC_ROTATE, base + VT8500_ICPC_IRQ); | ||
157 | writel(0x00, base + VT8500_ICPC_FIQ); | ||
158 | |||
159 | /* Disable all interrupts and route them to IRQ */ | ||
160 | for (i = 0; i < 64; i++) | ||
161 | writeb(VT8500_INT_DISABLE | ICDC_IRQ, base + VT8500_ICDC + i); | ||
162 | } | ||
163 | |||
164 | static int vt8500_irq_map(struct irq_domain *h, unsigned int virq, | ||
165 | irq_hw_number_t hw) | ||
166 | { | ||
167 | irq_set_chip_and_handler(virq, &vt8500_irq_chip, handle_level_irq); | ||
168 | set_irq_flags(virq, IRQF_VALID); | ||
169 | |||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | static struct irq_domain_ops vt8500_irq_domain_ops = { | ||
174 | .map = vt8500_irq_map, | ||
175 | .xlate = irq_domain_xlate_onecell, | ||
176 | }; | ||
177 | |||
178 | asmlinkage void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs) | ||
179 | { | ||
180 | u32 stat, i; | ||
181 | int irqnr, virq; | ||
182 | void __iomem *base; | ||
183 | |||
184 | /* Loop through each active controller */ | ||
185 | for (i=0; i<active_cnt; i++) { | ||
186 | base = intc[i].base; | ||
187 | irqnr = readl_relaxed(base) & 0x3F; | ||
188 | /* | ||
189 | Highest Priority register default = 63, so check that this | ||
190 | is a real interrupt by checking the status register | ||
191 | */ | ||
192 | if (irqnr == 63) { | ||
193 | stat = readl_relaxed(base + VT8500_ICIS + 4); | ||
194 | if (!(stat & BIT(31))) | ||
195 | continue; | ||
196 | } | ||
197 | |||
198 | virq = irq_find_mapping(intc[i].domain, irqnr); | ||
199 | handle_IRQ(virq, regs); | ||
200 | } | ||
201 | } | ||
202 | |||
203 | int __init vt8500_irq_init(struct device_node *node, struct device_node *parent) | ||
204 | { | ||
205 | int irq, i; | ||
206 | struct device_node *np = node; | ||
207 | |||
208 | if (active_cnt == VT8500_INTC_MAX) { | ||
209 | pr_err("%s: Interrupt controllers > VT8500_INTC_MAX\n", | ||
210 | __func__); | ||
211 | goto out; | ||
212 | } | ||
213 | |||
214 | intc[active_cnt].base = of_iomap(np, 0); | ||
215 | intc[active_cnt].domain = irq_domain_add_linear(node, 64, | ||
216 | &vt8500_irq_domain_ops, &intc[active_cnt]); | ||
217 | |||
218 | if (!intc[active_cnt].base) { | ||
219 | pr_err("%s: Unable to map IO memory\n", __func__); | ||
220 | goto out; | ||
221 | } | ||
222 | |||
223 | if (!intc[active_cnt].domain) { | ||
224 | pr_err("%s: Unable to add irq domain!\n", __func__); | ||
225 | goto out; | ||
226 | } | ||
227 | |||
228 | vt8500_init_irq_hw(intc[active_cnt].base); | ||
229 | |||
230 | pr_info("vt8500-irq: Added interrupt controller\n"); | ||
231 | |||
232 | active_cnt++; | ||
233 | |||
234 | /* check if this is a slaved controller */ | ||
235 | if (of_irq_count(np) != 0) { | ||
236 | /* check that we have the correct number of interrupts */ | ||
237 | if (of_irq_count(np) != 8) { | ||
238 | pr_err("%s: Incorrect IRQ map for slaved controller\n", | ||
239 | __func__); | ||
240 | return -EINVAL; | ||
241 | } | ||
242 | |||
243 | for (i = 0; i < 8; i++) { | ||
244 | irq = irq_of_parse_and_map(np, i); | ||
245 | enable_irq(irq); | ||
246 | } | ||
247 | |||
248 | pr_info("vt8500-irq: Enabled slave->parent interrupts\n"); | ||
249 | } | ||
250 | out: | ||
251 | return 0; | ||
252 | } | ||
253 | |||