xref: /freebsd/sys/arm/mv/mpic.c (revision eb69d1f144a6fcc765d1b9d44a5ae8082353e70b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2006 Benno Rice.
5  * Copyright (C) 2007-2011 MARVELL INTERNATIONAL LTD.
6  * Copyright (c) 2012 Semihalf.
7  * All rights reserved.
8  *
9  * Developed by Semihalf.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_icu.c, rev 1
32  * from: FreeBSD: src/sys/arm/mv/ic.c,v 1.5 2011/02/08 01:49:30
33  */
34 
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37 
38 #include "opt_platform.h"
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/bus.h>
43 #include <sys/kernel.h>
44 #include <sys/cpuset.h>
45 #include <sys/ktr.h>
46 #include <sys/kdb.h>
47 #include <sys/module.h>
48 #include <sys/lock.h>
49 #include <sys/mutex.h>
50 #include <sys/rman.h>
51 #include <sys/proc.h>
52 #include <sys/smp.h>
53 
54 #include <machine/bus.h>
55 #include <machine/intr.h>
56 #include <machine/smp.h>
57 
58 #include <arm/mv/mvvar.h>
59 #include <arm/mv/mvreg.h>
60 
61 #include <dev/ofw/ofw_bus.h>
62 #include <dev/ofw/ofw_bus_subr.h>
63 #include <dev/fdt/fdt_common.h>
64 
65 #ifdef INTRNG
66 #include "pic_if.h"
67 #endif
68 
69 #ifdef DEBUG
70 #define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
71     printf(fmt,##args); } while (0)
72 #else
73 #define debugf(fmt, args...)
74 #endif
75 
76 #define	MPIC_INT_LOCAL			3
77 #define	MPIC_INT_ERR			4
78 #define	MPIC_INT_MSI			96
79 
80 #define	MPIC_IRQ_MASK		0x3ff
81 
82 #define	MPIC_CTRL		0x0
83 #define	MPIC_SOFT_INT		0x4
84 #define	MPIC_SOFT_INT_DRBL1	(1 << 5)
85 #define	MPIC_ERR_CAUSE		0x20
86 #define	MPIC_ISE		0x30
87 #define	MPIC_ICE		0x34
88 #define	MPIC_INT_CTL(irq)	(0x100 + (irq)*4)
89 
90 #define	MPIC_INT_IRQ_FIQ_MASK(cpuid)	(0x101 << (cpuid))
91 #define	MPIC_CTRL_NIRQS(ctrl)	(((ctrl) >> 2) & 0x3ff)
92 
93 #define	MPIC_IN_DRBL		0x08
94 #define	MPIC_IN_DRBL_MASK	0x0c
95 #define	MPIC_PPI_CAUSE		0x10
96 #define	MPIC_CTP		0x40
97 #define	MPIC_IIACK		0x44
98 #define	MPIC_ISM		0x48
99 #define	MPIC_ICM		0x4c
100 #define	MPIC_ERR_MASK		0x50
101 #define	MPIC_LOCAL_MASK		0x54
102 #define	MPIC_CPU(n)		(n) * 0x100
103 
104 #define	MPIC_PPI	32
105 
106 #ifdef INTRNG
107 struct mv_mpic_irqsrc {
108 	struct intr_irqsrc	mmi_isrc;
109 	u_int			mmi_irq;
110 };
111 #endif
112 
113 struct mv_mpic_softc {
114 	device_t		sc_dev;
115 	struct resource	*	mpic_res[4];
116 	bus_space_tag_t		mpic_bst;
117 	bus_space_handle_t	mpic_bsh;
118 	bus_space_tag_t		cpu_bst;
119 	bus_space_handle_t	cpu_bsh;
120 	bus_space_tag_t		drbl_bst;
121 	bus_space_handle_t	drbl_bsh;
122 	struct mtx		mtx;
123 #ifdef INTRNG
124 	struct mv_mpic_irqsrc *	mpic_isrcs;
125 #endif
126 	int			nirqs;
127 	void *			intr_hand;
128 };
129 
130 static struct resource_spec mv_mpic_spec[] = {
131 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
132 	{ SYS_RES_MEMORY,	1,	RF_ACTIVE },
133 	{ SYS_RES_MEMORY,	2,	RF_ACTIVE | RF_OPTIONAL },
134 	{ SYS_RES_IRQ,		0,	RF_ACTIVE | RF_OPTIONAL },
135 	{ -1, 0 }
136 };
137 
138 static struct ofw_compat_data compat_data[] = {
139 	{"mrvl,mpic",		true},
140 	{"marvell,mpic",	true},
141 	{NULL,			false}
142 };
143 
144 static struct mv_mpic_softc *mv_mpic_sc = NULL;
145 
146 void mpic_send_ipi(int cpus, u_int ipi);
147 
148 static int	mv_mpic_probe(device_t);
149 static int	mv_mpic_attach(device_t);
150 uint32_t	mv_mpic_get_cause(void);
151 uint32_t	mv_mpic_get_cause_err(void);
152 uint32_t	mv_mpic_get_msi(void);
153 static void	mpic_unmask_irq(uintptr_t nb);
154 static void	mpic_mask_irq(uintptr_t nb);
155 static void	mpic_mask_irq_err(uintptr_t nb);
156 static void	mpic_unmask_irq_err(uintptr_t nb);
157 static boolean_t mpic_irq_is_percpu(uintptr_t);
158 #ifdef INTRNG
159 static int	mpic_intr(void *arg);
160 #endif
161 static void	mpic_unmask_msi(void);
162 
163 #define	MPIC_WRITE(softc, reg, val) \
164     bus_space_write_4((softc)->mpic_bst, (softc)->mpic_bsh, (reg), (val))
165 #define	MPIC_READ(softc, reg) \
166     bus_space_read_4((softc)->mpic_bst, (softc)->mpic_bsh, (reg))
167 
168 #define MPIC_CPU_WRITE(softc, reg, val) \
169     bus_space_write_4((softc)->cpu_bst, (softc)->cpu_bsh, (reg), (val))
170 #define MPIC_CPU_READ(softc, reg) \
171     bus_space_read_4((softc)->cpu_bst, (softc)->cpu_bsh, (reg))
172 
173 #define MPIC_DRBL_WRITE(softc, reg, val) \
174     bus_space_write_4((softc)->drbl_bst, (softc)->drbl_bsh, (reg), (val))
175 #define MPIC_DRBL_READ(softc, reg) \
176     bus_space_read_4((softc)->drbl_bst, (softc)->drbl_bsh, (reg))
177 
178 static int
179 mv_mpic_probe(device_t dev)
180 {
181 
182 	if (!ofw_bus_status_okay(dev))
183 		return (ENXIO);
184 
185 	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
186 		return (ENXIO);
187 
188 	device_set_desc(dev, "Marvell Integrated Interrupt Controller");
189 	return (0);
190 }
191 
192 #ifdef INTRNG
193 static int
194 mv_mpic_register_isrcs(struct mv_mpic_softc *sc)
195 {
196 	int error;
197 	uint32_t irq;
198 	struct intr_irqsrc *isrc;
199 	const char *name;
200 
201 	sc->mpic_isrcs = malloc(sc->nirqs * sizeof (*sc->mpic_isrcs), M_DEVBUF,
202 	    M_WAITOK | M_ZERO);
203 
204 	name = device_get_nameunit(sc->sc_dev);
205 	for (irq = 0; irq < sc->nirqs; irq++) {
206 		sc->mpic_isrcs[irq].mmi_irq = irq;
207 
208 		isrc = &sc->mpic_isrcs[irq].mmi_isrc;
209 		if (irq < MPIC_PPI) {
210 			error = intr_isrc_register(isrc, sc->sc_dev,
211 			    INTR_ISRCF_PPI, "%s", name);
212 		} else {
213 			error = intr_isrc_register(isrc, sc->sc_dev, 0, "%s",
214 			    name);
215 		}
216 		if (error != 0) {
217 			/* XXX call intr_isrc_deregister() */
218 			device_printf(sc->sc_dev, "%s failed", __func__);
219 			return (error);
220 		}
221 	}
222 	return (0);
223 }
224 #endif
225 
226 static int
227 mv_mpic_attach(device_t dev)
228 {
229 	struct mv_mpic_softc *sc;
230 	int error;
231 	uint32_t val;
232 	int cpu;
233 
234 	sc = (struct mv_mpic_softc *)device_get_softc(dev);
235 
236 	if (mv_mpic_sc != NULL)
237 		return (ENXIO);
238 	mv_mpic_sc = sc;
239 
240 	sc->sc_dev = dev;
241 
242 	mtx_init(&sc->mtx, "MPIC lock", NULL, MTX_SPIN);
243 
244 	error = bus_alloc_resources(dev, mv_mpic_spec, sc->mpic_res);
245 	if (error) {
246 		device_printf(dev, "could not allocate resources\n");
247 		return (ENXIO);
248 	}
249 #ifdef INTRNG
250 	if (sc->mpic_res[3] == NULL)
251 		device_printf(dev, "No interrupt to use.\n");
252 	else
253 		bus_setup_intr(dev, sc->mpic_res[3], INTR_TYPE_CLK,
254 		    mpic_intr, NULL, sc, &sc->intr_hand);
255 #endif
256 
257 	sc->mpic_bst = rman_get_bustag(sc->mpic_res[0]);
258 	sc->mpic_bsh = rman_get_bushandle(sc->mpic_res[0]);
259 
260 	sc->cpu_bst = rman_get_bustag(sc->mpic_res[1]);
261 	sc->cpu_bsh = rman_get_bushandle(sc->mpic_res[1]);
262 
263 	if (sc->mpic_res[2] != NULL) {
264 		/* This is required only if MSIs are used. */
265 		sc->drbl_bst = rman_get_bustag(sc->mpic_res[2]);
266 		sc->drbl_bsh = rman_get_bushandle(sc->mpic_res[2]);
267 	}
268 
269 	MPIC_WRITE(mv_mpic_sc, MPIC_CTRL, 1);
270 	MPIC_CPU_WRITE(mv_mpic_sc, MPIC_CTP, 0);
271 
272 	val = MPIC_READ(mv_mpic_sc, MPIC_CTRL);
273 	sc->nirqs = MPIC_CTRL_NIRQS(val);
274 
275 #ifdef INTRNG
276 	if (mv_mpic_register_isrcs(sc) != 0) {
277 		device_printf(dev, "could not register PIC ISRCs\n");
278 		bus_release_resources(dev, mv_mpic_spec, sc->mpic_res);
279 		return (ENXIO);
280 	}
281 
282 	OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev);
283 
284 	if (intr_pic_register(dev, OF_xref_from_device(dev)) == NULL) {
285 		device_printf(dev, "could not register PIC\n");
286 		bus_release_resources(dev, mv_mpic_spec, sc->mpic_res);
287 		return (ENXIO);
288 	}
289 #endif
290 
291 	mpic_unmask_msi();
292 
293 	/* Unmask CPU performance counters overflow irq */
294 	for (cpu = 0; cpu < mp_ncpus; cpu++)
295 		MPIC_CPU_WRITE(mv_mpic_sc, MPIC_CPU(cpu) + MPIC_LOCAL_MASK,
296 		    (1 << cpu) | MPIC_CPU_READ(mv_mpic_sc,
297 		    MPIC_CPU(cpu) + MPIC_LOCAL_MASK));
298 
299 	return (0);
300 }
301 
302 #ifdef INTRNG
303 static int
304 mpic_intr(void *arg)
305 {
306 	struct mv_mpic_softc *sc;
307 	uint32_t cause, irqsrc;
308 	unsigned int irq;
309 	u_int cpuid;
310 
311 	sc = arg;
312 	cpuid = PCPU_GET(cpuid);
313 	irq = 0;
314 
315 	for (cause = MPIC_CPU_READ(sc, MPIC_PPI_CAUSE); cause > 0;
316 	    cause >>= 1, irq++) {
317 		if (cause & 1) {
318 			irqsrc = MPIC_READ(sc, MPIC_INT_CTL(irq));
319 			if ((irqsrc & MPIC_INT_IRQ_FIQ_MASK(cpuid)) == 0)
320 				continue;
321 			if (intr_isrc_dispatch(&sc->mpic_isrcs[irq].mmi_isrc,
322 			    curthread->td_intr_frame) != 0) {
323 				mpic_mask_irq(irq);
324 				device_printf(sc->sc_dev, "Stray irq %u "
325 				    "disabled\n", irq);
326 			}
327 		}
328 	}
329 
330 	return (FILTER_HANDLED);
331 }
332 
333 static void
334 mpic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
335 {
336 	u_int irq;
337 
338 	irq = ((struct mv_mpic_irqsrc *)isrc)->mmi_irq;
339 	mpic_mask_irq(irq);
340 }
341 
342 static void
343 mpic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
344 {
345 	u_int irq;
346 
347 	irq = ((struct mv_mpic_irqsrc *)isrc)->mmi_irq;
348 	mpic_unmask_irq(irq);
349 }
350 
351 static int
352 mpic_map_intr(device_t dev, struct intr_map_data *data,
353     struct intr_irqsrc **isrcp)
354 {
355 	struct intr_map_data_fdt *daf;
356 	struct mv_mpic_softc *sc;
357 
358 	if (data->type != INTR_MAP_DATA_FDT)
359 		return (ENOTSUP);
360 
361 	sc = device_get_softc(dev);
362 	daf = (struct intr_map_data_fdt *)data;
363 
364 	if (daf->ncells !=1 || daf->cells[0] >= sc->nirqs)
365 		return (EINVAL);
366 
367 	*isrcp = &sc->mpic_isrcs[daf->cells[0]].mmi_isrc;
368 	return (0);
369 }
370 
371 static void
372 mpic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
373 {
374 
375 	mpic_disable_intr(dev, isrc);
376 }
377 
378 static void
379 mpic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
380 {
381 
382 	mpic_enable_intr(dev, isrc);
383 }
384 
385 static void
386 mpic_post_filter(device_t dev, struct intr_irqsrc *isrc)
387 {
388 }
389 #endif
390 
391 static device_method_t mv_mpic_methods[] = {
392 	DEVMETHOD(device_probe,		mv_mpic_probe),
393 	DEVMETHOD(device_attach,	mv_mpic_attach),
394 
395 #ifdef INTRNG
396 	DEVMETHOD(pic_disable_intr,	mpic_disable_intr),
397 	DEVMETHOD(pic_enable_intr,	mpic_enable_intr),
398 	DEVMETHOD(pic_map_intr,		mpic_map_intr),
399 	DEVMETHOD(pic_post_filter,	mpic_post_filter),
400 	DEVMETHOD(pic_post_ithread,	mpic_post_ithread),
401 	DEVMETHOD(pic_pre_ithread,	mpic_pre_ithread),
402 #endif
403 	{ 0, 0 }
404 };
405 
406 static driver_t mv_mpic_driver = {
407 	"mpic",
408 	mv_mpic_methods,
409 	sizeof(struct mv_mpic_softc),
410 };
411 
412 static devclass_t mv_mpic_devclass;
413 
414 EARLY_DRIVER_MODULE(mpic, simplebus, mv_mpic_driver, mv_mpic_devclass, 0, 0,
415     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
416 
417 #ifndef INTRNG
418 int
419 arm_get_next_irq(int last)
420 {
421 	u_int irq, next = -1;
422 
423 	irq = mv_mpic_get_cause() & MPIC_IRQ_MASK;
424 	CTR2(KTR_INTR, "%s: irq:%#x", __func__, irq);
425 
426 	if (irq != MPIC_IRQ_MASK) {
427 		if (irq == MPIC_INT_ERR)
428 			irq = mv_mpic_get_cause_err();
429 		if (irq == MPIC_INT_MSI)
430 			irq = mv_mpic_get_msi();
431 		next = irq;
432 	}
433 
434 	CTR3(KTR_INTR, "%s: last=%d, next=%d", __func__, last, next);
435 	return (next);
436 }
437 
438 /*
439  * XXX We can make arm_enable_irq to operate on ICE and then mask/unmask only
440  * by ISM/ICM and remove access to ICE in masking operation
441  */
442 void
443 arm_mask_irq(uintptr_t nb)
444 {
445 
446 	mpic_mask_irq(nb);
447 }
448 
449 void
450 arm_unmask_irq(uintptr_t nb)
451 {
452 
453 	mpic_unmask_irq(nb);
454 }
455 #endif
456 
457 static void
458 mpic_unmask_msi(void)
459 {
460 
461 	mpic_unmask_irq(MPIC_INT_MSI);
462 }
463 
464 static void
465 mpic_unmask_irq_err(uintptr_t nb)
466 {
467 	uint32_t mask;
468 	uint8_t bit_off;
469 
470 	MPIC_WRITE(mv_mpic_sc, MPIC_ISE, MPIC_INT_ERR);
471 	MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ICM, MPIC_INT_ERR);
472 
473 	bit_off = nb - ERR_IRQ;
474 	mask = MPIC_CPU_READ(mv_mpic_sc, MPIC_ERR_MASK);
475 	mask |= (1 << bit_off);
476 	MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ERR_MASK, mask);
477 }
478 
479 static void
480 mpic_mask_irq_err(uintptr_t nb)
481 {
482 	uint32_t mask;
483 	uint8_t bit_off;
484 
485 	bit_off = nb - ERR_IRQ;
486 	mask = MPIC_CPU_READ(mv_mpic_sc, MPIC_ERR_MASK);
487 	mask &= ~(1 << bit_off);
488 	MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ERR_MASK, mask);
489 }
490 
491 static boolean_t
492 mpic_irq_is_percpu(uintptr_t nb)
493 {
494 	if (nb < MPIC_PPI)
495 		return TRUE;
496 
497 	return FALSE;
498 }
499 
500 static void
501 mpic_unmask_irq(uintptr_t nb)
502 {
503 
504 #ifdef SMP
505 	int cpu;
506 
507 	if (nb == MPIC_INT_LOCAL) {
508 		for (cpu = 0; cpu < mp_ncpus; cpu++)
509 			MPIC_CPU_WRITE(mv_mpic_sc,
510 			    MPIC_CPU(cpu) + MPIC_ICM, nb);
511 		return;
512 	}
513 #endif
514 	if (mpic_irq_is_percpu(nb))
515 		MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ICM, nb);
516 	else if (nb < ERR_IRQ)
517 		MPIC_WRITE(mv_mpic_sc, MPIC_ISE, nb);
518 	else if (nb < MSI_IRQ)
519 		mpic_unmask_irq_err(nb);
520 
521 	if (nb == 0)
522 		MPIC_CPU_WRITE(mv_mpic_sc, MPIC_IN_DRBL_MASK, 0xffffffff);
523 }
524 
525 static void
526 mpic_mask_irq(uintptr_t nb)
527 {
528 
529 #ifdef SMP
530 	int cpu;
531 
532 	if (nb == MPIC_INT_LOCAL) {
533 		for (cpu = 0; cpu < mp_ncpus; cpu++)
534 			MPIC_CPU_WRITE(mv_mpic_sc,
535 			    MPIC_CPU(cpu) + MPIC_ISM, nb);
536 		return;
537 	}
538 #endif
539 	if (mpic_irq_is_percpu(nb))
540 		MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ISM, nb);
541 	else if (nb < ERR_IRQ)
542 		MPIC_WRITE(mv_mpic_sc, MPIC_ICE, nb);
543 	else if (nb < MSI_IRQ)
544 		mpic_mask_irq_err(nb);
545 }
546 
547 uint32_t
548 mv_mpic_get_cause(void)
549 {
550 
551 	return (MPIC_CPU_READ(mv_mpic_sc, MPIC_IIACK));
552 }
553 
554 uint32_t
555 mv_mpic_get_cause_err(void)
556 {
557 	uint32_t err_cause;
558 	uint8_t bit_off;
559 
560 	err_cause = MPIC_READ(mv_mpic_sc, MPIC_ERR_CAUSE);
561 
562 	if (err_cause)
563 		bit_off = ffs(err_cause) - 1;
564 	else
565 		return (-1);
566 
567 	debugf("%s: irq:%x cause:%x\n", __func__, bit_off, err_cause);
568 	return (ERR_IRQ + bit_off);
569 }
570 
571 uint32_t
572 mv_mpic_get_msi(void)
573 {
574 	uint32_t cause;
575 	uint8_t bit_off;
576 
577 	KASSERT(mv_mpic_sc->drbl_bst != NULL, ("No doorbell in mv_mpic_get_msi"));
578 	cause = MPIC_DRBL_READ(mv_mpic_sc, 0);
579 
580 	if (cause)
581 		bit_off = ffs(cause) - 1;
582 	else
583 		return (-1);
584 
585 	debugf("%s: irq:%x cause:%x\n", __func__, bit_off, cause);
586 
587 	cause &= ~(1 << bit_off);
588 	MPIC_DRBL_WRITE(mv_mpic_sc, 0, cause);
589 
590 	return (MSI_IRQ + bit_off);
591 }
592 
593 int
594 mv_msi_data(int irq, uint64_t *addr, uint32_t *data)
595 {
596 	u_long phys, base, size;
597 	phandle_t node;
598 	int error;
599 
600 	node = ofw_bus_get_node(mv_mpic_sc->sc_dev);
601 
602 	/* Get physical address of register space */
603 	error = fdt_get_range(OF_parent(node), 0, &phys, &size);
604 	if (error) {
605 		printf("%s: Cannot get register physical address, err:%d",
606 		    __func__, error);
607 		return (error);
608 	}
609 
610 	/* Get offset of MPIC register space */
611 	error = fdt_regsize(node, &base, &size);
612 	if (error) {
613 		printf("%s: Cannot get MPIC register offset, err:%d",
614 		    __func__, error);
615 		return (error);
616 	}
617 
618 	*addr = phys + base + MPIC_SOFT_INT;
619 	*data = MPIC_SOFT_INT_DRBL1 | irq;
620 
621 	return (0);
622 }
623 
624 
625 #if defined(SMP) && defined(SOC_MV_ARMADAXP)
626 void
627 intr_pic_init_secondary(void)
628 {
629 }
630 
631 void
632 pic_ipi_send(cpuset_t cpus, u_int ipi)
633 {
634 	uint32_t val, i;
635 
636 	val = 0x00000000;
637 	for (i = 0; i < MAXCPU; i++)
638 		if (CPU_ISSET(i, &cpus))
639 			val |= (1 << (8 + i));
640 	val |= ipi;
641 	MPIC_WRITE(mv_mpic_sc, MPIC_SOFT_INT, val);
642 }
643 
644 int
645 pic_ipi_read(int i __unused)
646 {
647 	uint32_t val;
648 	int ipi;
649 
650 	val = MPIC_CPU_READ(mv_mpic_sc, MPIC_IN_DRBL);
651 	if (val) {
652 		ipi = ffs(val) - 1;
653 		MPIC_CPU_WRITE(mv_mpic_sc, MPIC_IN_DRBL, ~(1 << ipi));
654 		return (ipi);
655 	}
656 
657 	return (0x3ff);
658 }
659 
660 void
661 pic_ipi_clear(int ipi)
662 {
663 }
664 
665 #endif
666