mips_pic.c revision 308333
1228072Sbapt/*-
2228072Sbapt * Copyright (c) 2015 Alexander Kabaev
3228072Sbapt * Copyright (c) 2006 Oleksandr Tymoshenko
4228072Sbapt * Copyright (c) 2002-2004 Juli Mallett <jmallett@FreeBSD.org>
5228072Sbapt * All rights reserved.
6228072Sbapt *
7228072Sbapt * Redistribution and use in source and binary forms, with or without
8228072Sbapt * modification, are permitted provided that the following conditions
9228072Sbapt * are met:
10228072Sbapt * 1. Redistributions of source code must retain the above copyright
11228072Sbapt *    notice, this list of conditions, and the following disclaimer,
12228072Sbapt *    without modification, immediately at the beginning of the file.
13228072Sbapt * 2. The name of the author may not be used to endorse or promote products
14228072Sbapt *    derived from this software without specific prior written permission.
15228072Sbapt *
16228072Sbapt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17228072Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18228072Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19228072Sbapt * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20228072Sbapt * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21228072Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22228072Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23228072Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24228072Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25228072Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26228072Sbapt * SUCH DAMAGE.
27228072Sbapt *
28228072Sbapt */
29228072Sbapt
30228072Sbapt#include <sys/cdefs.h>
31228072Sbapt__FBSDID("$FreeBSD: stable/11/sys/mips/mips/mips_pic.c 308333 2016-11-05 10:23:02Z mmel $");
32228072Sbapt
33228072Sbapt#include "opt_platform.h"
34228072Sbapt#include "opt_hwpmc_hooks.h"
35228072Sbapt
36228072Sbapt#include <sys/param.h>
37228072Sbapt#include <sys/systm.h>
38228072Sbapt#include <sys/bus.h>
39228072Sbapt#include <sys/kernel.h>
40228072Sbapt#include <sys/ktr.h>
41228072Sbapt#include <sys/module.h>
42228072Sbapt#include <sys/malloc.h>
43228072Sbapt#include <sys/rman.h>
44228072Sbapt#include <sys/pcpu.h>
45228072Sbapt#include <sys/proc.h>
46228072Sbapt#include <sys/cpuset.h>
47228072Sbapt#include <sys/lock.h>
48228072Sbapt#include <sys/mutex.h>
49228072Sbapt#include <sys/smp.h>
50228072Sbapt#include <sys/sched.h>
51228072Sbapt#include <sys/pmc.h>
52228072Sbapt#include <sys/pmckern.h>
53228072Sbapt
54228072Sbapt#include <machine/bus.h>
55228072Sbapt#include <machine/hwfunc.h>
56228072Sbapt#include <machine/intr.h>
57228072Sbapt#include <machine/smp.h>
58228072Sbapt
59228072Sbapt#ifdef FDT
60228072Sbapt#include <dev/fdt/fdt_common.h>
61228072Sbapt#include <dev/ofw/openfirm.h>
62228072Sbapt#include <dev/ofw/ofw_bus.h>
63228072Sbapt#include <dev/ofw/ofw_bus_subr.h>
64228072Sbapt#endif
65228072Sbapt
66228072Sbapt#include "pic_if.h"
67228072Sbapt
68228072Sbapt#define NHARD_IRQS	6
69228072Sbapt#define NSOFT_IRQS	2
70228072Sbapt#define NREAL_IRQS	(NHARD_IRQS + NSOFT_IRQS)
71228072Sbapt
72228072Sbaptstatic int mips_pic_intr(void *);
73228072Sbapt
74228072Sbaptstruct intr_map_data_mips_pic {
75228072Sbapt	struct intr_map_data	hdr;
76228072Sbapt	u_int			irq;
77250125Sjkim};
78250125Sjkim
79228072Sbaptstruct mips_pic_irqsrc {
80228072Sbapt	struct intr_irqsrc	isrc;
81228072Sbapt	struct resource		*res;
82228072Sbapt	u_int			irq;
83228072Sbapt};
84228072Sbapt
85228072Sbaptstruct mips_pic_softc {
86228072Sbapt	device_t			pic_dev;
87228072Sbapt	struct mips_pic_irqsrc		pic_irqs[NREAL_IRQS];
88228072Sbapt	struct rman			pic_irq_rman;
89228072Sbapt	struct mtx			mutex;
90228072Sbapt	uint32_t			nirqs;
91228072Sbapt};
92228072Sbapt
93250875Sjkimstatic struct mips_pic_softc *pic_sc;
94250875Sjkim
95250125Sjkim#define PIC_INTR_ISRC(sc, irq)		(&(sc)->pic_irqs[(irq)].isrc)
96250125Sjkim
97250125Sjkim#ifdef FDT
98250876Sjkimstatic struct ofw_compat_data compat_data[] = {
99250125Sjkim	{"mti,cpu-interrupt-controller",	true},
100250125Sjkim	{NULL,					false}
101250125Sjkim};
102250125Sjkim#endif
103250125Sjkim
104250125Sjkim#ifndef FDT
105250125Sjkimstatic void
106250125Sjkimmips_pic_identify(driver_t *drv, device_t parent)
107250125Sjkim{
108228072Sbapt
109228072Sbapt	BUS_ADD_CHILD(parent, 0, "cpupic", 0);
110228072Sbapt}
111228072Sbapt#endif
112228072Sbapt
113228072Sbaptstatic int
114228072Sbaptmips_pic_probe(device_t dev)
115228072Sbapt{
116228072Sbapt
117228072Sbapt#ifdef FDT
118228072Sbapt	if (!ofw_bus_status_okay(dev))
119228072Sbapt		return (ENXIO);
120228072Sbapt
121228072Sbapt	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
122228072Sbapt		return (ENXIO);
123228072Sbapt#endif
124228072Sbapt	device_set_desc(dev, "MIPS32 Interrupt Controller");
125228072Sbapt	return (BUS_PROBE_DEFAULT);
126228072Sbapt}
127228072Sbapt
128228072Sbaptstatic inline void
129228072Sbaptpic_irq_unmask(struct mips_pic_softc *sc, u_int irq)
130228072Sbapt{
131228072Sbapt
132228072Sbapt	mips_wr_status(mips_rd_status() | ((1 << irq) << 8));
133228072Sbapt}
134228072Sbapt
135228072Sbaptstatic inline void
136228072Sbaptpic_irq_mask(struct mips_pic_softc *sc, u_int irq)
137228072Sbapt{
138228072Sbapt
139228072Sbapt	mips_wr_status(mips_rd_status() & ~((1 << irq) << 8));
140228072Sbapt}
141228072Sbapt
142228072Sbaptstatic inline intptr_t
143228072Sbaptpic_xref(device_t dev)
144228072Sbapt{
145228072Sbapt#ifdef FDT
146228072Sbapt	return (OF_xref_from_node(ofw_bus_get_node(dev)));
147228072Sbapt#else
148228072Sbapt	return (0);
149228072Sbapt#endif
150228072Sbapt}
151228072Sbapt
152228072Sbaptstatic int
153228072Sbaptmips_pic_register_isrcs(struct mips_pic_softc *sc)
154228072Sbapt{
155228072Sbapt	int error;
156228072Sbapt	uint32_t irq, i, tmpirq;
157228072Sbapt	struct intr_irqsrc *isrc;
158228072Sbapt	char *name;
159228072Sbapt
160228072Sbapt	for (irq = 0; irq < sc->nirqs; irq++) {
161228072Sbapt		sc->pic_irqs[irq].irq = irq;
162228072Sbapt		sc->pic_irqs[irq].res = rman_reserve_resource(&sc->pic_irq_rman,
163228072Sbapt		    irq, irq, 1, RF_ACTIVE, sc->pic_dev);
164228072Sbapt		if (sc->pic_irqs[irq].res == NULL) {
165228072Sbapt			device_printf(sc->pic_dev,
166228072Sbapt			    "%s failed to alloc resource for irq %u",
167228072Sbapt			    __func__, irq);
168228072Sbapt			return (ENOMEM);
169228072Sbapt		}
170228072Sbapt		isrc = PIC_INTR_ISRC(sc, irq);
171228072Sbapt		if (irq < NSOFT_IRQS) {
172228072Sbapt			name = "sint";
173228072Sbapt			tmpirq = irq;
174228072Sbapt		} else {
175228072Sbapt			name = "int";
176228072Sbapt			tmpirq = irq - NSOFT_IRQS;
177250125Sjkim		}
178250125Sjkim		error = intr_isrc_register(isrc, sc->pic_dev, 0, "%s%u",
179228072Sbapt		    name, tmpirq);
180228072Sbapt		if (error != 0) {
181228072Sbapt			for (i = 0; i < irq; i++) {
182228072Sbapt				intr_isrc_deregister(PIC_INTR_ISRC(sc, i));
183228072Sbapt			}
184228072Sbapt			device_printf(sc->pic_dev, "%s failed", __func__);
185228072Sbapt			return (error);
186228072Sbapt		}
187228072Sbapt	}
188228072Sbapt
189228072Sbapt	return (0);
190228072Sbapt}
191228072Sbapt
192228072Sbaptstatic int
193228072Sbaptmips_pic_attach(device_t dev)
194228072Sbapt{
195228072Sbapt	struct		mips_pic_softc *sc;
196228072Sbapt	intptr_t	xref = pic_xref(dev);
197250125Sjkim
198250125Sjkim	if (pic_sc)
199228072Sbapt		return (ENXIO);
200228072Sbapt
201228072Sbapt	sc = device_get_softc(dev);
202228072Sbapt
203228072Sbapt	sc->pic_dev = dev;
204228072Sbapt	pic_sc = sc;
205228072Sbapt
206228072Sbapt	/* Initialize mutex */
207228072Sbapt	mtx_init(&sc->mutex, "PIC lock", "", MTX_SPIN);
208228072Sbapt
209228072Sbapt	/* Set the number of interrupts */
210228072Sbapt	sc->nirqs = nitems(sc->pic_irqs);
211228072Sbapt
212228072Sbapt	/* Init the IRQ rman */
213228072Sbapt	sc->pic_irq_rman.rm_type = RMAN_ARRAY;
214228072Sbapt	sc->pic_irq_rman.rm_descr = "MIPS PIC IRQs";
215228072Sbapt	if (rman_init(&sc->pic_irq_rman) != 0 ||
216228072Sbapt	    rman_manage_region(&sc->pic_irq_rman, 0, sc->nirqs - 1) != 0) {
217228072Sbapt		device_printf(dev, "failed to setup IRQ rman\n");
218228072Sbapt		goto cleanup;
219228072Sbapt	}
220228072Sbapt
221228072Sbapt	/* Register the interrupts */
222228072Sbapt	if (mips_pic_register_isrcs(sc) != 0) {
223228072Sbapt		device_printf(dev, "could not register PIC ISRCs\n");
224228072Sbapt		goto cleanup;
225228072Sbapt	}
226228072Sbapt
227228072Sbapt	/*
228228072Sbapt	 * Now, when everything is initialized, it's right time to
229228072Sbapt	 * register interrupt controller to interrupt framefork.
230228072Sbapt	 */
231228072Sbapt	if (intr_pic_register(dev, xref) == NULL) {
232228072Sbapt		device_printf(dev, "could not register PIC\n");
233228072Sbapt		goto cleanup;
234228072Sbapt	}
235228072Sbapt
236228072Sbapt	/* Claim our root controller role */
237228072Sbapt	if (intr_pic_claim_root(dev, xref, mips_pic_intr, sc, 0) != 0) {
238228072Sbapt		device_printf(dev, "could not set PIC as a root\n");
239228072Sbapt		intr_pic_deregister(dev, xref);
240228072Sbapt		goto cleanup;
241228072Sbapt	}
242228072Sbapt
243228072Sbapt	return (0);
244228072Sbapt
245228072Sbaptcleanup:
246228072Sbapt	return(ENXIO);
247228072Sbapt}
248228072Sbapt
249228072Sbaptint
250228072Sbaptmips_pic_intr(void *arg)
251228072Sbapt{
252228072Sbapt	struct mips_pic_softc *sc = arg;
253228072Sbapt	register_t cause, status;
254228072Sbapt	int i, intr;
255228072Sbapt
256228072Sbapt	cause = mips_rd_cause();
257228072Sbapt	status = mips_rd_status();
258228072Sbapt	intr = (cause & MIPS_INT_MASK) >> 8;
259228072Sbapt	/*
260228072Sbapt	 * Do not handle masked interrupts. They were masked by
261228072Sbapt	 * pre_ithread function (mips_mask_XXX_intr) and will be
262228072Sbapt	 * unmasked once ithread is through with handler
263228072Sbapt	 */
264228072Sbapt	intr &= (status & MIPS_INT_MASK) >> 8;
265228072Sbapt	while ((i = fls(intr)) != 0) {
266228072Sbapt		i--; /* Get a 0-offset interrupt. */
267228072Sbapt		intr &= ~(1 << i);
268228072Sbapt
269228072Sbapt		if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, i),
270228072Sbapt		    curthread->td_intr_frame) != 0) {
271228072Sbapt			device_printf(sc->pic_dev,
272228072Sbapt			    "Stray interrupt %u detected\n", i);
273228072Sbapt			pic_irq_mask(sc, i);
274228072Sbapt			continue;
275		}
276	}
277
278	KASSERT(i == 0, ("all interrupts handled"));
279
280#ifdef HWPMC_HOOKS
281	if (pmc_hook && (PCPU_GET(curthread)->td_pflags & TDP_CALLCHAIN)) {
282		struct trapframe *tf = PCPU_GET(curthread)->td_intr_frame;
283
284		pmc_hook(PCPU_GET(curthread), PMC_FN_USER_CALLCHAIN, tf);
285	}
286#endif
287	return (FILTER_HANDLED);
288}
289
290static void
291mips_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
292{
293	u_int irq;
294
295	irq = ((struct mips_pic_irqsrc *)isrc)->irq;
296	pic_irq_mask(device_get_softc(dev), irq);
297}
298
299static void
300mips_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
301{
302	u_int irq;
303
304	irq = ((struct mips_pic_irqsrc *)isrc)->irq;
305	pic_irq_unmask(device_get_softc(dev), irq);
306}
307
308static int
309mips_pic_map_intr(device_t dev, struct intr_map_data *data,
310    struct intr_irqsrc **isrcp)
311{
312	struct mips_pic_softc *sc;
313	int res;
314
315	sc = device_get_softc(dev);
316	res = 0;
317#ifdef FDT
318	if (data->type == INTR_MAP_DATA_FDT) {
319		struct intr_map_data_fdt *daf;
320
321		daf = (struct intr_map_data_fdt *)data;
322
323		if (daf->ncells != 1 || daf->cells[0] >= sc->nirqs)
324			return (EINVAL);
325
326		*isrcp = PIC_INTR_ISRC(sc, daf->cells[0]);
327	} else
328#endif
329	if (data->type == INTR_MAP_DATA_PLAT_1) {
330		struct intr_map_data_mips_pic *mpd;
331
332		mpd = (struct intr_map_data_mips_pic *)data;
333
334		if (mpd->irq < 0 || mpd->irq >= sc->nirqs)
335			return (EINVAL);
336
337		*isrcp = PIC_INTR_ISRC(sc, mpd->irq);
338	} else {
339		res = ENOTSUP;
340	}
341
342	return (res);
343}
344
345static void
346mips_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
347{
348
349	mips_pic_disable_intr(dev, isrc);
350}
351
352static void
353mips_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
354{
355
356	mips_pic_enable_intr(dev, isrc);
357}
358
359static void
360mips_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
361{
362}
363
364static device_method_t mips_pic_methods[] = {
365	/* Device interface */
366#ifndef FDT
367	DEVMETHOD(device_identify,	mips_pic_identify),
368#endif
369	DEVMETHOD(device_probe,		mips_pic_probe),
370	DEVMETHOD(device_attach,	mips_pic_attach),
371
372	/* Interrupt controller interface */
373	DEVMETHOD(pic_disable_intr,	mips_pic_disable_intr),
374	DEVMETHOD(pic_enable_intr,	mips_pic_enable_intr),
375	DEVMETHOD(pic_map_intr,		mips_pic_map_intr),
376	DEVMETHOD(pic_pre_ithread,	mips_pic_pre_ithread),
377	DEVMETHOD(pic_post_ithread,	mips_pic_post_ithread),
378	DEVMETHOD(pic_post_filter,	mips_pic_post_filter),
379
380	{ 0, 0 }
381};
382
383static driver_t mips_pic_driver = {
384	"cpupic",
385	mips_pic_methods,
386	sizeof(struct mips_pic_softc),
387};
388
389static devclass_t mips_pic_devclass;
390
391#ifdef FDT
392EARLY_DRIVER_MODULE(cpupic, ofwbus, mips_pic_driver, mips_pic_devclass, 0, 0,
393    BUS_PASS_INTERRUPT);
394#else
395EARLY_DRIVER_MODULE(cpupic, nexus, mips_pic_driver, mips_pic_devclass, 0, 0,
396    BUS_PASS_INTERRUPT);
397#endif
398
399void
400cpu_init_interrupts(void)
401{
402}
403
404int
405cpu_create_intr_map(int irq)
406{
407	struct intr_map_data_mips_pic *mips_pic_data;
408	intptr_t iparent;
409	size_t len;
410	u_int new_irq;
411
412	len = sizeof(*mips_pic_data);
413	iparent = pic_xref(pic_sc->pic_dev);
414
415	/* Allocate mips_pic data and fill it in */
416	mips_pic_data = (struct intr_map_data_mips_pic *)intr_alloc_map_data(
417	    INTR_MAP_DATA_PLAT_1, len, M_WAITOK | M_ZERO);
418	mips_pic_data->irq = irq;
419
420	/* Get the new irq number */
421	new_irq = intr_map_irq(pic_sc->pic_dev, iparent,
422	    (struct intr_map_data *)mips_pic_data);
423
424	/* Adjust the resource accordingly */
425	rman_set_start(pic_sc->pic_irqs[irq].res, new_irq);
426	rman_set_end(pic_sc->pic_irqs[irq].res, new_irq);
427
428	/* Activate the new irq */
429	return (intr_activate_irq(pic_sc->pic_dev, pic_sc->pic_irqs[irq].res));
430}
431
432struct resource *
433cpu_get_irq_resource(int irq)
434{
435
436	KASSERT(pic_sc != NULL, ("%s: no pic", __func__));
437
438	if (irq < 0 || irq >= pic_sc->nirqs)
439		panic("%s called for unknown irq %d", __func__, irq);
440
441	return pic_sc->pic_irqs[irq].res;
442}
443
444void
445cpu_establish_hardintr(const char *name, driver_filter_t *filt,
446    void (*handler)(void*), void *arg, int irq, int flags, void **cookiep)
447{
448	int res;
449
450	/*
451	 * We have 6 levels, but thats 0 - 5 (not including 6)
452	 */
453	if (irq < 0 || irq >= NHARD_IRQS)
454		panic("%s called for unknown hard intr %d", __func__, irq);
455
456	KASSERT(pic_sc != NULL, ("%s: no pic", __func__));
457
458	irq += NSOFT_IRQS;
459
460	res = cpu_create_intr_map(irq);
461	if (res != 0) panic("Unable to create map for hard IRQ %d", irq);
462
463	res = intr_setup_irq(pic_sc->pic_dev, pic_sc->pic_irqs[irq].res, filt,
464	    handler, arg, flags, cookiep);
465	if (res != 0) panic("Unable to add hard IRQ %d handler", irq);
466}
467
468void
469cpu_establish_softintr(const char *name, driver_filter_t *filt,
470    void (*handler)(void*), void *arg, int irq, int flags,
471    void **cookiep)
472{
473	int res;
474
475	if (irq < 0 || irq > NSOFT_IRQS)
476		panic("%s called for unknown soft intr %d", __func__, irq);
477
478	KASSERT(pic_sc != NULL, ("%s: no pic", __func__));
479
480	res = cpu_create_intr_map(irq);
481	if (res != 0) panic("Unable to create map for soft IRQ %d", irq);
482
483	res = intr_setup_irq(pic_sc->pic_dev, pic_sc->pic_irqs[irq].res, filt,
484	    handler, arg, flags, cookiep);
485	if (res != 0) panic("Unable to add soft IRQ %d handler", irq);
486}
487
488