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