FreeBSD/Linux Kernel Cross Reference
sys/mips/cavium/ciu.c
1 /*-
2 * Copyright (c) 2010 Juli Mallett <jmallett@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: releng/11.0/sys/mips/cavium/ciu.c 294883 2016-01-27 02:23:54Z jhibbits $
27 */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD: releng/11.0/sys/mips/cavium/ciu.c 294883 2016-01-27 02:23:54Z jhibbits $");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/interrupt.h>
36 #include <sys/kernel.h>
37 #include <sys/module.h>
38 #include <sys/rman.h>
39 #include <sys/malloc.h>
40 #include <sys/smp.h>
41
42 #include <machine/bus.h>
43 #include <machine/intr_machdep.h>
44
45 #include <contrib/octeon-sdk/cvmx.h>
46 #include <mips/cavium/octeon_irq.h>
47
48 /*
49 * This bus sits between devices/buses and nexus and handles CIU interrupts
50 * and passes everything else through. It should really be a nexus subclass
51 * or something, but for now this will be sufficient.
52 */
53
54 #define CIU_IRQ_HARD (0)
55
56 #define CIU_IRQ_EN0_BEGIN OCTEON_IRQ_WORKQ0
57 #define CIU_IRQ_EN0_END OCTEON_IRQ_BOOTDMA
58 #define CIU_IRQ_EN0_COUNT ((CIU_IRQ_EN0_END - CIU_IRQ_EN0_BEGIN) + 1)
59
60 #define CIU_IRQ_EN1_BEGIN OCTEON_IRQ_WDOG0
61 #define CIU_IRQ_EN1_END OCTEON_IRQ_DFM
62 #define CIU_IRQ_EN1_COUNT ((CIU_IRQ_EN1_END - CIU_IRQ_EN1_BEGIN) + 1)
63
64 struct ciu_softc {
65 struct rman irq_rman;
66 struct resource *ciu_irq;
67 };
68
69 static mips_intrcnt_t ciu_en0_intrcnt[CIU_IRQ_EN0_COUNT];
70 static mips_intrcnt_t ciu_en1_intrcnt[CIU_IRQ_EN1_COUNT];
71
72 static struct intr_event *ciu_en0_intr_events[CIU_IRQ_EN0_COUNT];
73 static struct intr_event *ciu_en1_intr_events[CIU_IRQ_EN1_COUNT];
74
75 static int ciu_probe(device_t);
76 static int ciu_attach(device_t);
77 static struct resource *ciu_alloc_resource(device_t, device_t, int, int *,
78 rman_res_t, rman_res_t, rman_res_t,
79 u_int);
80 static int ciu_setup_intr(device_t, device_t, struct resource *,
81 int, driver_filter_t *, driver_intr_t *,
82 void *, void **);
83 static int ciu_teardown_intr(device_t, device_t,
84 struct resource *, void *);
85 static int ciu_bind_intr(device_t, device_t, struct resource *,
86 int);
87 static int ciu_describe_intr(device_t, device_t,
88 struct resource *, void *,
89 const char *);
90 static void ciu_hinted_child(device_t, const char *, int);
91
92 static void ciu_en0_intr_mask(void *);
93 static void ciu_en0_intr_unmask(void *);
94 #ifdef SMP
95 static int ciu_en0_intr_bind(void *, int);
96 #endif
97
98 static void ciu_en1_intr_mask(void *);
99 static void ciu_en1_intr_unmask(void *);
100 #ifdef SMP
101 static int ciu_en1_intr_bind(void *, int);
102 #endif
103
104 static int ciu_intr(void *);
105
106 static int
107 ciu_probe(device_t dev)
108 {
109 if (device_get_unit(dev) != 0)
110 return (ENXIO);
111
112 device_set_desc(dev, "Cavium Octeon Central Interrupt Unit");
113 return (BUS_PROBE_NOWILDCARD);
114 }
115
116 static int
117 ciu_attach(device_t dev)
118 {
119 char name[MAXCOMLEN + 1];
120 struct ciu_softc *sc;
121 unsigned i;
122 int error;
123 int rid;
124
125 sc = device_get_softc(dev);
126
127 rid = 0;
128 sc->ciu_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, CIU_IRQ_HARD,
129 CIU_IRQ_HARD, 1, RF_ACTIVE);
130 if (sc->ciu_irq == NULL) {
131 device_printf(dev, "could not allocate irq%d\n", CIU_IRQ_HARD);
132 return (ENXIO);
133 }
134
135 error = bus_setup_intr(dev, sc->ciu_irq, INTR_TYPE_MISC, ciu_intr,
136 NULL, sc, NULL);
137 if (error != 0) {
138 device_printf(dev, "bus_setup_intr failed: %d\n", error);
139 return (error);
140 }
141
142 sc->irq_rman.rm_type = RMAN_ARRAY;
143 sc->irq_rman.rm_descr = "CIU IRQ";
144
145 error = rman_init(&sc->irq_rman);
146 if (error != 0)
147 return (error);
148
149 /*
150 * We have two contiguous IRQ regions, use a single rman.
151 */
152 error = rman_manage_region(&sc->irq_rman, CIU_IRQ_EN0_BEGIN,
153 CIU_IRQ_EN1_END);
154 if (error != 0)
155 return (error);
156
157 for (i = 0; i < CIU_IRQ_EN0_COUNT; i++) {
158 snprintf(name, sizeof name, "int%d:", i + CIU_IRQ_EN0_BEGIN);
159 ciu_en0_intrcnt[i] = mips_intrcnt_create(name);
160 }
161
162 for (i = 0; i < CIU_IRQ_EN1_COUNT; i++) {
163 snprintf(name, sizeof name, "int%d:", i + CIU_IRQ_EN1_BEGIN);
164 ciu_en1_intrcnt[i] = mips_intrcnt_create(name);
165 }
166
167 bus_generic_probe(dev);
168 bus_generic_attach(dev);
169
170 return (0);
171 }
172
173 static struct resource *
174 ciu_alloc_resource(device_t bus, device_t child, int type, int *rid,
175 rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
176 {
177 struct resource *res;
178 struct ciu_softc *sc;
179
180 sc = device_get_softc(bus);
181
182 switch (type) {
183 case SYS_RES_IRQ:
184 break;
185 default:
186 return (bus_alloc_resource(device_get_parent(bus), type, rid,
187 start, end, count, flags));
188 }
189
190 /*
191 * One interrupt at a time for now.
192 */
193 if (start != end)
194 return (NULL);
195
196 res = rman_reserve_resource(&sc->irq_rman, start, end, count, flags,
197 child);
198 if (res != NULL)
199 return (res);
200
201 return (NULL);
202 }
203
204 static int
205 ciu_setup_intr(device_t bus, device_t child, struct resource *res, int flags,
206 driver_filter_t *filter, driver_intr_t *intr, void *arg,
207 void **cookiep)
208 {
209 struct intr_event *event, **eventp;
210 void (*mask_func)(void *);
211 void (*unmask_func)(void *);
212 int (*bind_func)(void *, int);
213 mips_intrcnt_t intrcnt;
214 int error;
215 int irq;
216
217 irq = rman_get_start(res);
218 if (irq <= CIU_IRQ_EN0_END) {
219 eventp = &ciu_en0_intr_events[irq - CIU_IRQ_EN0_BEGIN];
220 intrcnt = ciu_en0_intrcnt[irq - CIU_IRQ_EN0_BEGIN];
221 mask_func = ciu_en0_intr_mask;
222 unmask_func = ciu_en0_intr_unmask;
223 #ifdef SMP
224 bind_func = ciu_en0_intr_bind;
225 #endif
226 } else {
227 eventp = &ciu_en1_intr_events[irq - CIU_IRQ_EN1_BEGIN];
228 intrcnt = ciu_en1_intrcnt[irq - CIU_IRQ_EN1_BEGIN];
229 mask_func = ciu_en1_intr_mask;
230 unmask_func = ciu_en1_intr_unmask;
231 #ifdef SMP
232 bind_func = ciu_en1_intr_bind;
233 #endif
234 }
235 #if !defined(SMP)
236 bind_func = NULL;
237 #endif
238
239 if ((event = *eventp) == NULL) {
240 error = intr_event_create(eventp, (void *)(uintptr_t)irq, 0,
241 irq, mask_func, unmask_func, NULL, bind_func, "int%d", irq);
242 if (error != 0)
243 return (error);
244
245 event = *eventp;
246
247 unmask_func((void *)(uintptr_t)irq);
248 }
249
250 intr_event_add_handler(event, device_get_nameunit(child),
251 filter, intr, arg, intr_priority(flags), flags, cookiep);
252
253 mips_intrcnt_setname(intrcnt, event->ie_fullname);
254
255 return (0);
256 }
257
258 static int
259 ciu_teardown_intr(device_t bus, device_t child, struct resource *res,
260 void *cookie)
261 {
262 int error;
263
264 error = intr_event_remove_handler(cookie);
265 if (error != 0)
266 return (error);
267
268 return (0);
269 }
270
271 #ifdef SMP
272 static int
273 ciu_bind_intr(device_t bus, device_t child, struct resource *res, int cpu)
274 {
275 struct intr_event *event;
276 int irq;
277
278 irq = rman_get_start(res);
279 if (irq <= CIU_IRQ_EN0_END)
280 event = ciu_en0_intr_events[irq - CIU_IRQ_EN0_BEGIN];
281 else
282 event = ciu_en1_intr_events[irq - CIU_IRQ_EN1_BEGIN];
283
284 return (intr_event_bind(event, cpu));
285 }
286 #endif
287
288 static int
289 ciu_describe_intr(device_t bus, device_t child, struct resource *res,
290 void *cookie, const char *descr)
291 {
292 struct intr_event *event;
293 mips_intrcnt_t intrcnt;
294 int error;
295 int irq;
296
297 irq = rman_get_start(res);
298 if (irq <= CIU_IRQ_EN0_END) {
299 event = ciu_en0_intr_events[irq - CIU_IRQ_EN0_BEGIN];
300 intrcnt = ciu_en0_intrcnt[irq - CIU_IRQ_EN0_BEGIN];
301 } else {
302 event = ciu_en1_intr_events[irq - CIU_IRQ_EN1_BEGIN];
303 intrcnt = ciu_en1_intrcnt[irq - CIU_IRQ_EN1_BEGIN];
304 }
305
306 error = intr_event_describe_handler(event, cookie, descr);
307 if (error != 0)
308 return (error);
309
310 mips_intrcnt_setname(intrcnt, event->ie_fullname);
311
312 return (0);
313 }
314
315 static void
316 ciu_hinted_child(device_t bus, const char *dname, int dunit)
317 {
318 BUS_ADD_CHILD(bus, 0, dname, dunit);
319 }
320
321 static void
322 ciu_en0_intr_mask(void *arg)
323 {
324 uint64_t mask;
325 int irq;
326
327 irq = (uintptr_t)arg;
328 mask = cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2));
329 mask &= ~(1ull << (irq - CIU_IRQ_EN0_BEGIN));
330 cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2), mask);
331 }
332
333 static void
334 ciu_en0_intr_unmask(void *arg)
335 {
336 uint64_t mask;
337 int irq;
338
339 irq = (uintptr_t)arg;
340 mask = cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2));
341 mask |= 1ull << (irq - CIU_IRQ_EN0_BEGIN);
342 cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2), mask);
343 }
344
345 #ifdef SMP
346 static int
347 ciu_en0_intr_bind(void *arg, int target)
348 {
349 uint64_t mask;
350 int core;
351 int irq;
352
353 irq = (uintptr_t)arg;
354 CPU_FOREACH(core) {
355 mask = cvmx_read_csr(CVMX_CIU_INTX_EN0(core*2));
356 if (core == target)
357 mask |= 1ull << (irq - CIU_IRQ_EN0_BEGIN);
358 else
359 mask &= ~(1ull << (irq - CIU_IRQ_EN0_BEGIN));
360 cvmx_write_csr(CVMX_CIU_INTX_EN0(core*2), mask);
361 }
362
363 return (0);
364 }
365 #endif
366
367 static void
368 ciu_en1_intr_mask(void *arg)
369 {
370 uint64_t mask;
371 int irq;
372
373 irq = (uintptr_t)arg;
374 mask = cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2));
375 mask &= ~(1ull << (irq - CIU_IRQ_EN1_BEGIN));
376 cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2), mask);
377 }
378
379 static void
380 ciu_en1_intr_unmask(void *arg)
381 {
382 uint64_t mask;
383 int irq;
384
385 irq = (uintptr_t)arg;
386 mask = cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2));
387 mask |= 1ull << (irq - CIU_IRQ_EN1_BEGIN);
388 cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2), mask);
389 }
390
391 #ifdef SMP
392 static int
393 ciu_en1_intr_bind(void *arg, int target)
394 {
395 uint64_t mask;
396 int core;
397 int irq;
398
399 irq = (uintptr_t)arg;
400 CPU_FOREACH(core) {
401 mask = cvmx_read_csr(CVMX_CIU_INTX_EN1(core*2));
402 if (core == target)
403 mask |= 1ull << (irq - CIU_IRQ_EN1_BEGIN);
404 else
405 mask &= ~(1ull << (irq - CIU_IRQ_EN1_BEGIN));
406 cvmx_write_csr(CVMX_CIU_INTX_EN1(core*2), mask);
407 }
408
409 return (0);
410 }
411 #endif
412
413 static int
414 ciu_intr(void *arg)
415 {
416 struct ciu_softc *sc;
417 uint64_t en0_sum, en1_sum;
418 uint64_t en0_mask, en1_mask;
419 int irq_index;
420 int error;
421
422 sc = arg;
423 (void)sc;
424
425 en0_sum = cvmx_read_csr(CVMX_CIU_INTX_SUM0(cvmx_get_core_num()*2));
426 en1_sum = cvmx_read_csr(CVMX_CIU_INT_SUM1);
427
428 en0_mask = cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2));
429 en1_mask = cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2));
430
431 en0_sum &= en0_mask;
432 en1_sum &= en1_mask;
433
434 if (en0_sum == 0 && en1_sum == 0)
435 return (FILTER_STRAY);
436
437 for (irq_index = 0; en0_sum != 0; irq_index++, en0_sum >>= 1) {
438 if ((en0_sum & 1) == 0)
439 continue;
440
441 mips_intrcnt_inc(ciu_en0_intrcnt[irq_index]);
442
443 error = intr_event_handle(ciu_en0_intr_events[irq_index], NULL);
444 if (error != 0)
445 printf("%s: stray en0 irq%d\n", __func__, irq_index);
446 }
447
448 for (irq_index = 0; en1_sum != 0; irq_index++, en1_sum >>= 1) {
449 if ((en1_sum & 1) == 0)
450 continue;
451
452 mips_intrcnt_inc(ciu_en1_intrcnt[irq_index]);
453
454 error = intr_event_handle(ciu_en1_intr_events[irq_index], NULL);
455 if (error != 0)
456 printf("%s: stray en1 irq%d\n", __func__, irq_index);
457 }
458
459 return (FILTER_HANDLED);
460 }
461
462 static device_method_t ciu_methods[] = {
463 DEVMETHOD(device_probe, ciu_probe),
464 DEVMETHOD(device_attach, ciu_attach),
465
466 DEVMETHOD(bus_alloc_resource, ciu_alloc_resource),
467 DEVMETHOD(bus_activate_resource,bus_generic_activate_resource),
468 DEVMETHOD(bus_setup_intr, ciu_setup_intr),
469 DEVMETHOD(bus_teardown_intr, ciu_teardown_intr),
470 #ifdef SMP
471 DEVMETHOD(bus_bind_intr, ciu_bind_intr),
472 #endif
473 DEVMETHOD(bus_describe_intr, ciu_describe_intr),
474
475 DEVMETHOD(bus_add_child, bus_generic_add_child),
476 DEVMETHOD(bus_hinted_child, ciu_hinted_child),
477
478 { 0, 0 }
479 };
480
481 static driver_t ciu_driver = {
482 "ciu",
483 ciu_methods,
484 sizeof(struct ciu_softc),
485 };
486 static devclass_t ciu_devclass;
487 DRIVER_MODULE(ciu, nexus, ciu_driver, ciu_devclass, 0, 0);
Cache object: ec2ef9146a0f8936a684358f9833f236
|