1
2
3
4
5
6
7
8
9
10
11#include <linux/interrupt.h>
12#include <linux/io.h>
13#include <linux/irqdomain.h>
14
15#include <asm/irq.h>
16#include <asm/hwthread.h>
17
18#define PERF0VECINT 0x04820580
19#define PERF1VECINT 0x04820588
20#define PERF0TRIG_OFFSET 16
21#define PERF1TRIG_OFFSET 17
22
23
24
25
26
27
28struct metag_internal_irq_priv {
29 struct irq_domain *domain;
30
31 unsigned long unmasked;
32};
33
34
35static struct metag_internal_irq_priv metag_internal_irq_priv;
36
37static unsigned int metag_internal_irq_startup(struct irq_data *data);
38static void metag_internal_irq_shutdown(struct irq_data *data);
39static void metag_internal_irq_ack(struct irq_data *data);
40static void metag_internal_irq_mask(struct irq_data *data);
41static void metag_internal_irq_unmask(struct irq_data *data);
42#ifdef CONFIG_SMP
43static int metag_internal_irq_set_affinity(struct irq_data *data,
44 const struct cpumask *cpumask, bool force);
45#endif
46
47static struct irq_chip internal_irq_edge_chip = {
48 .name = "HWSTATMETA-IRQ",
49 .irq_startup = metag_internal_irq_startup,
50 .irq_shutdown = metag_internal_irq_shutdown,
51 .irq_ack = metag_internal_irq_ack,
52 .irq_mask = metag_internal_irq_mask,
53 .irq_unmask = metag_internal_irq_unmask,
54#ifdef CONFIG_SMP
55 .irq_set_affinity = metag_internal_irq_set_affinity,
56#endif
57};
58
59
60
61
62
63
64
65
66static inline void __iomem *metag_hwvec_addr(irq_hw_number_t hw)
67{
68 void __iomem *addr;
69
70 switch (hw) {
71 case PERF0TRIG_OFFSET:
72 addr = (void __iomem *)PERF0VECINT;
73 break;
74 case PERF1TRIG_OFFSET:
75 addr = (void __iomem *)PERF1VECINT;
76 break;
77 default:
78 addr = NULL;
79 break;
80 }
81 return addr;
82}
83
84
85
86
87
88
89
90
91static unsigned int metag_internal_irq_startup(struct irq_data *data)
92{
93
94 metag_internal_irq_ack(data);
95
96
97 metag_internal_irq_unmask(data);
98
99 return 0;
100}
101
102
103
104
105
106
107
108
109static void metag_internal_irq_shutdown(struct irq_data *data)
110{
111
112 metag_internal_irq_mask(data);
113
114
115 metag_internal_irq_ack(data);
116}
117
118
119
120
121
122static void metag_internal_irq_ack(struct irq_data *data)
123{
124 irq_hw_number_t hw = data->hwirq;
125 unsigned int bit = 1 << hw;
126
127 if (metag_in32(HWSTATMETA) & bit)
128 metag_out32(bit, HWSTATMETA);
129}
130
131
132
133
134
135
136
137
138static void metag_internal_irq_mask(struct irq_data *data)
139{
140 struct metag_internal_irq_priv *priv = &metag_internal_irq_priv;
141 irq_hw_number_t hw = data->hwirq;
142 void __iomem *vec_addr = metag_hwvec_addr(hw);
143
144 clear_bit(hw, &priv->unmasked);
145
146
147 metag_out32(0, vec_addr);
148}
149
150
151
152
153
154
155
156
157static void metag_internal_irq_unmask(struct irq_data *data)
158{
159 struct metag_internal_irq_priv *priv = &metag_internal_irq_priv;
160 irq_hw_number_t hw = data->hwirq;
161 unsigned int bit = 1 << hw;
162 void __iomem *vec_addr = metag_hwvec_addr(hw);
163 unsigned int thread = hard_processor_id();
164
165 set_bit(hw, &priv->unmasked);
166
167
168 metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR1(thread)), vec_addr);
169
170
171
172
173
174
175
176
177
178
179
180
181 if (metag_in32(HWSTATMETA) & bit) {
182 metag_out32(bit, HWSTATMETA);
183 while (!(metag_in32(HWSTATMETA) & bit))
184 metag_out32(bit, HWSTATMETA);
185 }
186}
187
188#ifdef CONFIG_SMP
189
190
191
192static int metag_internal_irq_set_affinity(struct irq_data *data,
193 const struct cpumask *cpumask, bool force)
194{
195 unsigned int cpu, thread;
196 irq_hw_number_t hw = data->hwirq;
197
198
199
200
201
202
203
204 cpu = cpumask_any_and(cpumask, cpu_online_mask);
205 thread = cpu_2_hwthread_id[cpu];
206
207 metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR1(thread)),
208 metag_hwvec_addr(hw));
209
210 return 0;
211}
212#endif
213
214
215
216
217
218
219
220
221
222
223static void metag_internal_irq_demux(struct irq_desc *desc)
224{
225 struct metag_internal_irq_priv *priv = irq_desc_get_handler_data(desc);
226 irq_hw_number_t hw;
227 unsigned int irq_no;
228 u32 status;
229
230recalculate:
231 status = metag_in32(HWSTATMETA) & priv->unmasked;
232
233 for (hw = 0; status != 0; status >>= 1, ++hw) {
234 if (status & 0x1) {
235
236
237
238
239 irq_no = irq_linear_revmap(priv->domain, hw);
240
241
242
243
244
245
246
247 generic_handle_irq(irq_no);
248
249
250
251
252
253
254
255 goto recalculate;
256 }
257 }
258}
259
260
261
262
263
264
265
266
267int internal_irq_map(unsigned int hw)
268{
269 struct metag_internal_irq_priv *priv = &metag_internal_irq_priv;
270 if (!priv->domain)
271 return -ENODEV;
272 return irq_create_mapping(priv->domain, hw);
273}
274
275
276
277
278
279
280
281static void metag_internal_irq_init_cpu(struct metag_internal_irq_priv *priv,
282 int cpu)
283{
284 unsigned int thread = cpu_2_hwthread_id[cpu];
285 unsigned int signum = TBID_SIGNUM_TR1(thread);
286 int irq = tbisig_map(signum);
287
288
289 irq_set_chained_handler_and_data(irq, metag_internal_irq_demux, priv);
290 irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
291}
292
293
294
295
296
297
298
299
300
301
302static int metag_internal_intc_map(struct irq_domain *d, unsigned int irq,
303 irq_hw_number_t hw)
304{
305
306 if (!metag_hwvec_addr(hw))
307 return -EINVAL;
308
309 irq_set_chip_and_handler(irq, &internal_irq_edge_chip,
310 handle_edge_irq);
311 return 0;
312}
313
314static const struct irq_domain_ops metag_internal_intc_domain_ops = {
315 .map = metag_internal_intc_map,
316};
317
318
319
320
321
322
323int __init init_internal_IRQ(void)
324{
325 struct metag_internal_irq_priv *priv = &metag_internal_irq_priv;
326 unsigned int cpu;
327
328
329 priv->domain = irq_domain_add_linear(NULL, 32,
330 &metag_internal_intc_domain_ops,
331 priv);
332 if (unlikely(!priv->domain)) {
333 pr_err("meta-internal-intc: cannot add IRQ domain\n");
334 return -ENOMEM;
335 }
336
337
338 for_each_possible_cpu(cpu)
339 metag_internal_irq_init_cpu(priv, cpu);
340
341 return 0;
342};
343