xref: /freebsd/sys/riscv/riscv/plic.c (revision 6ec8bf9f)
12d53a67cSRuslan Bukin /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
32d53a67cSRuslan Bukin  *
42d53a67cSRuslan Bukin  * Copyright (c) 2018 Ruslan Bukin <br@bsdpad.com>
52d53a67cSRuslan Bukin  * All rights reserved.
6bc5f705aSMitchell Horne  * Copyright (c) 2019 Mitchell Horne <mhorne@FreeBSD.org>
72d53a67cSRuslan Bukin  *
8bc5f705aSMitchell Horne  * Portions of this software were developed by SRI International and the
9bc5f705aSMitchell Horne  * University of Cambridge Computer Laboratory (Department of Computer Science
10bc5f705aSMitchell Horne  * and Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of
11bc5f705aSMitchell Horne  * the DARPA SSITH research programme.
122d53a67cSRuslan Bukin  *
132d53a67cSRuslan Bukin  * Redistribution and use in source and binary forms, with or without
142d53a67cSRuslan Bukin  * modification, are permitted provided that the following conditions
152d53a67cSRuslan Bukin  * are met:
162d53a67cSRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
172d53a67cSRuslan Bukin  *    notice, this list of conditions and the following disclaimer.
182d53a67cSRuslan Bukin  * 2. Redistributions in binary form must reproduce the above copyright
192d53a67cSRuslan Bukin  *    notice, this list of conditions and the following disclaimer in the
202d53a67cSRuslan Bukin  *    documentation and/or other materials provided with the distribution.
212d53a67cSRuslan Bukin  *
222d53a67cSRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
232d53a67cSRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
242d53a67cSRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
252d53a67cSRuslan Bukin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
262d53a67cSRuslan Bukin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
272d53a67cSRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
282d53a67cSRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
292d53a67cSRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
302d53a67cSRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
312d53a67cSRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
322d53a67cSRuslan Bukin  * SUCH DAMAGE.
332d53a67cSRuslan Bukin  */
342d53a67cSRuslan Bukin 
352d53a67cSRuslan Bukin #include <sys/param.h>
362d53a67cSRuslan Bukin #include <sys/systm.h>
372d53a67cSRuslan Bukin #include <sys/bus.h>
382d53a67cSRuslan Bukin #include <sys/kernel.h>
392d53a67cSRuslan Bukin #include <sys/ktr.h>
402d53a67cSRuslan Bukin #include <sys/module.h>
412d53a67cSRuslan Bukin #include <sys/proc.h>
422d53a67cSRuslan Bukin #include <sys/rman.h>
43a45a86c8SMitchell Horne #include <sys/smp.h>
442d53a67cSRuslan Bukin 
452d53a67cSRuslan Bukin #include <machine/bus.h>
462d53a67cSRuslan Bukin #include <machine/intr.h>
472d53a67cSRuslan Bukin 
482d53a67cSRuslan Bukin #include <dev/ofw/openfirm.h>
492d53a67cSRuslan Bukin #include <dev/ofw/ofw_bus.h>
502d53a67cSRuslan Bukin #include <dev/ofw/ofw_bus_subr.h>
512d53a67cSRuslan Bukin 
522d53a67cSRuslan Bukin #include "pic_if.h"
532d53a67cSRuslan Bukin 
549a640058SMitchell Horne #define	PLIC_MAX_IRQS		1024
55a45a86c8SMitchell Horne 
56a45a86c8SMitchell Horne #define	PLIC_PRIORITY_BASE	0x000000U
57a45a86c8SMitchell Horne 
58a45a86c8SMitchell Horne #define	PLIC_ENABLE_BASE	0x002000U
59a45a86c8SMitchell Horne #define	PLIC_ENABLE_STRIDE	0x80U
60a45a86c8SMitchell Horne 
61a45a86c8SMitchell Horne #define	PLIC_CONTEXT_BASE	0x200000U
62a45a86c8SMitchell Horne #define	PLIC_CONTEXT_STRIDE	0x1000U
63a45a86c8SMitchell Horne #define	PLIC_CONTEXT_THRESHOLD	0x0U
64a45a86c8SMitchell Horne #define	PLIC_CONTEXT_CLAIM	0x4U
65a45a86c8SMitchell Horne 
66a45a86c8SMitchell Horne #define	PLIC_PRIORITY(n)	(PLIC_PRIORITY_BASE + (n) * sizeof(uint32_t))
67a45a86c8SMitchell Horne #define	PLIC_ENABLE(sc, n, h)						\
68a45a86c8SMitchell Horne     (sc->contexts[h].enable_offset + ((n) / 32) * sizeof(uint32_t))
69a45a86c8SMitchell Horne #define	PLIC_THRESHOLD(sc, h)						\
70a45a86c8SMitchell Horne     (sc->contexts[h].context_offset + PLIC_CONTEXT_THRESHOLD)
71a45a86c8SMitchell Horne #define	PLIC_CLAIM(sc, h)						\
72a45a86c8SMitchell Horne     (sc->contexts[h].context_offset + PLIC_CONTEXT_CLAIM)
732d53a67cSRuslan Bukin 
74bc5f705aSMitchell Horne static pic_disable_intr_t	plic_disable_intr;
75bc5f705aSMitchell Horne static pic_enable_intr_t	plic_enable_intr;
76bc5f705aSMitchell Horne static pic_map_intr_t		plic_map_intr;
77bc5f705aSMitchell Horne static pic_setup_intr_t		plic_setup_intr;
78bc5f705aSMitchell Horne static pic_post_ithread_t	plic_post_ithread;
79bc5f705aSMitchell Horne static pic_pre_ithread_t	plic_pre_ithread;
80bc5f705aSMitchell Horne static pic_bind_intr_t		plic_bind_intr;
81bc5f705aSMitchell Horne 
822d53a67cSRuslan Bukin struct plic_irqsrc {
832d53a67cSRuslan Bukin 	struct intr_irqsrc	isrc;
842d53a67cSRuslan Bukin 	u_int			irq;
852d53a67cSRuslan Bukin };
862d53a67cSRuslan Bukin 
87a45a86c8SMitchell Horne struct plic_context {
88a45a86c8SMitchell Horne 	bus_size_t enable_offset;
89a45a86c8SMitchell Horne 	bus_size_t context_offset;
90a45a86c8SMitchell Horne };
91a45a86c8SMitchell Horne 
922d53a67cSRuslan Bukin struct plic_softc {
932d53a67cSRuslan Bukin 	device_t		dev;
94*6ec8bf9fSJessica Clarke 	struct resource		*mem_res;
95*6ec8bf9fSJessica Clarke 	struct resource		*irq_res;
96*6ec8bf9fSJessica Clarke 	void			*ih;
97a8cb655dSRuslan Bukin 	struct plic_irqsrc	isrcs[PLIC_MAX_IRQS];
98a45a86c8SMitchell Horne 	struct plic_context	contexts[MAXCPU];
99a8cb655dSRuslan Bukin 	int			ndev;
1002d53a67cSRuslan Bukin };
1012d53a67cSRuslan Bukin 
1022d53a67cSRuslan Bukin #define	RD4(sc, reg)				\
103*6ec8bf9fSJessica Clarke     bus_read_4(sc->mem_res, (reg))
1042d53a67cSRuslan Bukin #define	WR4(sc, reg, val)			\
105*6ec8bf9fSJessica Clarke     bus_write_4(sc->mem_res, (reg), (val))
1062d53a67cSRuslan Bukin 
107bc5f705aSMitchell Horne static u_int plic_irq_cpu;
108bc5f705aSMitchell Horne 
109a45a86c8SMitchell Horne static int
riscv_hartid_to_cpu(int hartid)110a45a86c8SMitchell Horne riscv_hartid_to_cpu(int hartid)
111a45a86c8SMitchell Horne {
112a45a86c8SMitchell Horne 	int i;
113a45a86c8SMitchell Horne 
114a45a86c8SMitchell Horne 	CPU_FOREACH(i) {
115a45a86c8SMitchell Horne 		if (pcpu_find(i)->pc_hart == hartid)
116a45a86c8SMitchell Horne 			return (i);
117a45a86c8SMitchell Horne 	}
118a45a86c8SMitchell Horne 
119a45a86c8SMitchell Horne 	return (-1);
120a45a86c8SMitchell Horne }
121a45a86c8SMitchell Horne 
122a45a86c8SMitchell Horne static int
plic_get_hartid(device_t dev,phandle_t intc)123a45a86c8SMitchell Horne plic_get_hartid(device_t dev, phandle_t intc)
124a45a86c8SMitchell Horne {
125a45a86c8SMitchell Horne 	int hart;
126a45a86c8SMitchell Horne 
127a45a86c8SMitchell Horne 	/* Check the interrupt controller layout. */
128a45a86c8SMitchell Horne 	if (OF_searchencprop(intc, "#interrupt-cells", &hart,
129a45a86c8SMitchell Horne 	    sizeof(hart)) == -1) {
130a45a86c8SMitchell Horne 		device_printf(dev,
131a45a86c8SMitchell Horne 		    "Could not find #interrupt-cells for phandle %u\n", intc);
132a45a86c8SMitchell Horne 		return (-1);
133a45a86c8SMitchell Horne 	}
134a45a86c8SMitchell Horne 
135a45a86c8SMitchell Horne 	/*
136a45a86c8SMitchell Horne 	 * The parent of the interrupt-controller is the CPU we are
137a45a86c8SMitchell Horne 	 * interested in, so search for its hart ID.
138a45a86c8SMitchell Horne 	 */
139a45a86c8SMitchell Horne 	if (OF_searchencprop(OF_parent(intc), "reg", (pcell_t *)&hart,
140a45a86c8SMitchell Horne 	    sizeof(hart)) == -1) {
141a45a86c8SMitchell Horne 		device_printf(dev, "Could not find hartid\n");
142a45a86c8SMitchell Horne 		return (-1);
143a45a86c8SMitchell Horne 	}
144a45a86c8SMitchell Horne 
145a45a86c8SMitchell Horne 	return (hart);
146a45a86c8SMitchell Horne }
147a45a86c8SMitchell Horne 
1482d53a67cSRuslan Bukin static inline void
plic_irq_dispatch(struct plic_softc * sc,u_int irq,struct trapframe * tf)1492d53a67cSRuslan Bukin plic_irq_dispatch(struct plic_softc *sc, u_int irq,
1502d53a67cSRuslan Bukin     struct trapframe *tf)
1512d53a67cSRuslan Bukin {
1522d53a67cSRuslan Bukin 	struct plic_irqsrc *src;
1532d53a67cSRuslan Bukin 
1542d53a67cSRuslan Bukin 	src = &sc->isrcs[irq];
1552d53a67cSRuslan Bukin 
1562d53a67cSRuslan Bukin 	if (intr_isrc_dispatch(&src->isrc, tf) != 0)
1572d53a67cSRuslan Bukin 		device_printf(sc->dev, "Stray irq %u detected\n", irq);
1582d53a67cSRuslan Bukin }
1592d53a67cSRuslan Bukin 
1602d53a67cSRuslan Bukin static int
plic_intr(void * arg)1612d53a67cSRuslan Bukin plic_intr(void *arg)
1622d53a67cSRuslan Bukin {
1632d53a67cSRuslan Bukin 	struct plic_softc *sc;
1642d53a67cSRuslan Bukin 	struct trapframe *tf;
1652d53a67cSRuslan Bukin 	uint32_t pending;
1662d53a67cSRuslan Bukin 	uint32_t cpu;
1672d53a67cSRuslan Bukin 
1682d53a67cSRuslan Bukin 	sc = arg;
1692d53a67cSRuslan Bukin 	cpu = PCPU_GET(cpuid);
1702d53a67cSRuslan Bukin 
17138d715f7SKristof Provost 	/* Claim any pending interrupt. */
172a45a86c8SMitchell Horne 	pending = RD4(sc, PLIC_CLAIM(sc, cpu));
1732d53a67cSRuslan Bukin 	if (pending) {
1742d53a67cSRuslan Bukin 		tf = curthread->td_intr_frame;
1752d53a67cSRuslan Bukin 		plic_irq_dispatch(sc, pending, tf);
1762d53a67cSRuslan Bukin 	}
1772d53a67cSRuslan Bukin 
1782d53a67cSRuslan Bukin 	return (FILTER_HANDLED);
1792d53a67cSRuslan Bukin }
1802d53a67cSRuslan Bukin 
1812d53a67cSRuslan Bukin static void
plic_disable_intr(device_t dev,struct intr_irqsrc * isrc)1822d53a67cSRuslan Bukin plic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
1832d53a67cSRuslan Bukin {
1842d53a67cSRuslan Bukin 	struct plic_softc *sc;
1852d53a67cSRuslan Bukin 	struct plic_irqsrc *src;
1862d53a67cSRuslan Bukin 
1872d53a67cSRuslan Bukin 	sc = device_get_softc(dev);
1882d53a67cSRuslan Bukin 	src = (struct plic_irqsrc *)isrc;
1892d53a67cSRuslan Bukin 
190bc5f705aSMitchell Horne 	WR4(sc, PLIC_PRIORITY(src->irq), 0);
1912d53a67cSRuslan Bukin }
1922d53a67cSRuslan Bukin 
1932d53a67cSRuslan Bukin static void
plic_enable_intr(device_t dev,struct intr_irqsrc * isrc)1942d53a67cSRuslan Bukin plic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
1952d53a67cSRuslan Bukin {
1962d53a67cSRuslan Bukin 	struct plic_softc *sc;
1972d53a67cSRuslan Bukin 	struct plic_irqsrc *src;
1982d53a67cSRuslan Bukin 
1992d53a67cSRuslan Bukin 	sc = device_get_softc(dev);
2002d53a67cSRuslan Bukin 	src = (struct plic_irqsrc *)isrc;
2012d53a67cSRuslan Bukin 
2022d53a67cSRuslan Bukin 	WR4(sc, PLIC_PRIORITY(src->irq), 1);
2032d53a67cSRuslan Bukin }
2042d53a67cSRuslan Bukin 
2052d53a67cSRuslan Bukin static int
plic_map_intr(device_t dev,struct intr_map_data * data,struct intr_irqsrc ** isrcp)2062d53a67cSRuslan Bukin plic_map_intr(device_t dev, struct intr_map_data *data,
2072d53a67cSRuslan Bukin     struct intr_irqsrc **isrcp)
2082d53a67cSRuslan Bukin {
2092d53a67cSRuslan Bukin 	struct intr_map_data_fdt *daf;
2102d53a67cSRuslan Bukin 	struct plic_softc *sc;
2112d53a67cSRuslan Bukin 
2122d53a67cSRuslan Bukin 	sc = device_get_softc(dev);
2132d53a67cSRuslan Bukin 
2142d53a67cSRuslan Bukin 	if (data->type != INTR_MAP_DATA_FDT)
2152d53a67cSRuslan Bukin 		return (ENOTSUP);
2162d53a67cSRuslan Bukin 
2172d53a67cSRuslan Bukin 	daf = (struct intr_map_data_fdt *)data;
218a8cb655dSRuslan Bukin 	if (daf->ncells != 1 || daf->cells[0] > sc->ndev)
2192d53a67cSRuslan Bukin 		return (EINVAL);
2202d53a67cSRuslan Bukin 
2212d53a67cSRuslan Bukin 	*isrcp = &sc->isrcs[daf->cells[0]].isrc;
2222d53a67cSRuslan Bukin 
2232d53a67cSRuslan Bukin 	return (0);
2242d53a67cSRuslan Bukin }
2252d53a67cSRuslan Bukin 
2262d53a67cSRuslan Bukin static int
plic_probe(device_t dev)2272d53a67cSRuslan Bukin plic_probe(device_t dev)
2282d53a67cSRuslan Bukin {
2292d53a67cSRuslan Bukin 
2302d53a67cSRuslan Bukin 	if (!ofw_bus_status_okay(dev))
2312d53a67cSRuslan Bukin 		return (ENXIO);
2322d53a67cSRuslan Bukin 
2336f1f29f6SMitchell Horne 	if (!ofw_bus_is_compatible(dev, "riscv,plic0") &&
2346f1f29f6SMitchell Horne 	    !ofw_bus_is_compatible(dev, "sifive,plic-1.0.0"))
2352d53a67cSRuslan Bukin 		return (ENXIO);
2362d53a67cSRuslan Bukin 
2372d53a67cSRuslan Bukin 	device_set_desc(dev, "RISC-V PLIC");
2382d53a67cSRuslan Bukin 
2392d53a67cSRuslan Bukin 	return (BUS_PROBE_DEFAULT);
2402d53a67cSRuslan Bukin }
2412d53a67cSRuslan Bukin 
2422d53a67cSRuslan Bukin static int
plic_attach(device_t dev)2432d53a67cSRuslan Bukin plic_attach(device_t dev)
2442d53a67cSRuslan Bukin {
2452d53a67cSRuslan Bukin 	struct plic_irqsrc *isrcs;
2462d53a67cSRuslan Bukin 	struct plic_softc *sc;
2472d53a67cSRuslan Bukin 	struct intr_pic *pic;
248a45a86c8SMitchell Horne 	pcell_t *cells;
2492d53a67cSRuslan Bukin 	uint32_t irq;
2502d53a67cSRuslan Bukin 	const char *name;
251a8cb655dSRuslan Bukin 	phandle_t node;
2522d53a67cSRuslan Bukin 	phandle_t xref;
2532d53a67cSRuslan Bukin 	uint32_t cpu;
2542d53a67cSRuslan Bukin 	int error;
2552d53a67cSRuslan Bukin 	int rid;
256a45a86c8SMitchell Horne 	int nintr;
257a45a86c8SMitchell Horne 	int context;
258a45a86c8SMitchell Horne 	int i;
259a45a86c8SMitchell Horne 	int hart;
2602d53a67cSRuslan Bukin 
2612d53a67cSRuslan Bukin 	sc = device_get_softc(dev);
2622d53a67cSRuslan Bukin 
2632d53a67cSRuslan Bukin 	sc->dev = dev;
2642d53a67cSRuslan Bukin 
265a8cb655dSRuslan Bukin 	node = ofw_bus_get_node(dev);
266a8cb655dSRuslan Bukin 	if ((OF_getencprop(node, "riscv,ndev", &sc->ndev,
267a8cb655dSRuslan Bukin 	    sizeof(sc->ndev))) < 0) {
268a8cb655dSRuslan Bukin 		device_printf(dev,
269a8cb655dSRuslan Bukin 		    "Error: could not get number of devices\n");
270a8cb655dSRuslan Bukin 		return (ENXIO);
271a8cb655dSRuslan Bukin 	}
272a8cb655dSRuslan Bukin 
273a8cb655dSRuslan Bukin 	if (sc->ndev >= PLIC_MAX_IRQS) {
274a8cb655dSRuslan Bukin 		device_printf(dev,
275a8cb655dSRuslan Bukin 		    "Error: invalid ndev (%d)\n", sc->ndev);
276a8cb655dSRuslan Bukin 		return (ENXIO);
277a8cb655dSRuslan Bukin 	}
278a8cb655dSRuslan Bukin 
2792d53a67cSRuslan Bukin 	/* Request memory resources */
2802d53a67cSRuslan Bukin 	rid = 0;
281*6ec8bf9fSJessica Clarke 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
2822d53a67cSRuslan Bukin 	    RF_ACTIVE);
283*6ec8bf9fSJessica Clarke 	if (sc->mem_res == NULL) {
2842d53a67cSRuslan Bukin 		device_printf(dev,
2852d53a67cSRuslan Bukin 		    "Error: could not allocate memory resources\n");
2862d53a67cSRuslan Bukin 		return (ENXIO);
2872d53a67cSRuslan Bukin 	}
2882d53a67cSRuslan Bukin 
289a45a86c8SMitchell Horne 	/* Register the interrupt sources */
2902d53a67cSRuslan Bukin 	isrcs = sc->isrcs;
2912d53a67cSRuslan Bukin 	name = device_get_nameunit(sc->dev);
292a8cb655dSRuslan Bukin 	for (irq = 1; irq <= sc->ndev; irq++) {
2932d53a67cSRuslan Bukin 		isrcs[irq].irq = irq;
2942d53a67cSRuslan Bukin 		error = intr_isrc_register(&isrcs[irq].isrc, sc->dev,
2952d53a67cSRuslan Bukin 		    0, "%s,%u", name, irq);
2962d53a67cSRuslan Bukin 		if (error != 0)
2972d53a67cSRuslan Bukin 			return (error);
2982d53a67cSRuslan Bukin 
29905efeb84SRuslan Bukin 		WR4(sc, PLIC_PRIORITY(irq), 0);
3002d53a67cSRuslan Bukin 	}
301a45a86c8SMitchell Horne 
302a45a86c8SMitchell Horne 	/*
303a45a86c8SMitchell Horne 	 * Calculate the per-cpu enable and context register offsets.
304a45a86c8SMitchell Horne 	 *
305a45a86c8SMitchell Horne 	 * This is tricky for a few reasons. The PLIC divides the interrupt
306a45a86c8SMitchell Horne 	 * enable, threshold, and claim bits by "context", where each context
307*6ec8bf9fSJessica Clarke 	 * routes to a core's local interrupt controller.
308a45a86c8SMitchell Horne 	 *
309a45a86c8SMitchell Horne 	 * The tricky part is that the PLIC spec imposes no restrictions on how
310a45a86c8SMitchell Horne 	 * these contexts are laid out. So for example, there is no guarantee
311a45a86c8SMitchell Horne 	 * that each CPU will have both a machine mode and supervisor context,
312a45a86c8SMitchell Horne 	 * or that different PLIC implementations will organize the context
313a45a86c8SMitchell Horne 	 * registers in the same way. On top of this, we must handle the fact
314a45a86c8SMitchell Horne 	 * that cpuid != hartid, as they may have been renumbered during boot.
315a45a86c8SMitchell Horne 	 * We perform the following steps:
316a45a86c8SMitchell Horne 	 *
317a45a86c8SMitchell Horne 	 * 1. Examine the PLIC's "interrupts-extended" property and skip any
318a45a86c8SMitchell Horne 	 *    entries that are not for supervisor external interrupts.
319a45a86c8SMitchell Horne 	 *
320a45a86c8SMitchell Horne 	 * 2. Walk up the device tree to find the corresponding CPU, and grab
321*6ec8bf9fSJessica Clarke 	 *    its hart ID.
322a45a86c8SMitchell Horne 	 *
323a45a86c8SMitchell Horne 	 * 3. Convert the hart to a cpuid, and calculate the register offsets
324a45a86c8SMitchell Horne 	 *    based on the context number.
325*6ec8bf9fSJessica Clarke 	 *
326*6ec8bf9fSJessica Clarke 	 * 4. Save the index for the boot hart's S-mode external interrupt in
327*6ec8bf9fSJessica Clarke 	 *    order to allocate and setup the corresponding resource, since the
328*6ec8bf9fSJessica Clarke 	 *    local interrupt controller newbus device is associated with that
329*6ec8bf9fSJessica Clarke 	 *    specific node.
330a45a86c8SMitchell Horne 	 */
331a45a86c8SMitchell Horne 	nintr = OF_getencprop_alloc_multi(node, "interrupts-extended",
332a45a86c8SMitchell Horne 	    sizeof(uint32_t), (void **)&cells);
333a45a86c8SMitchell Horne 	if (nintr <= 0) {
334a45a86c8SMitchell Horne 		device_printf(dev, "Could not read interrupts-extended\n");
335a45a86c8SMitchell Horne 		return (ENXIO);
336a45a86c8SMitchell Horne 	}
337a45a86c8SMitchell Horne 
338a45a86c8SMitchell Horne 	/* interrupts-extended is a list of phandles and interrupt types. */
339*6ec8bf9fSJessica Clarke 	rid = -1;
340a45a86c8SMitchell Horne 	for (i = 0, context = 0; i < nintr; i += 2, context++) {
341a45a86c8SMitchell Horne 		/* Skip M-mode external interrupts */
342a45a86c8SMitchell Horne 		if (cells[i + 1] != IRQ_EXTERNAL_SUPERVISOR)
343a45a86c8SMitchell Horne 			continue;
344a45a86c8SMitchell Horne 
345*6ec8bf9fSJessica Clarke 		/*
346*6ec8bf9fSJessica Clarke 		 * Get the hart ID from the core's interrupt controller
347*6ec8bf9fSJessica Clarke 		 * phandle.
348*6ec8bf9fSJessica Clarke 		 */
349a45a86c8SMitchell Horne 		hart = plic_get_hartid(dev, OF_node_from_xref(cells[i]));
350a45a86c8SMitchell Horne 		if (hart < 0) {
351a45a86c8SMitchell Horne 			OF_prop_free(cells);
352a45a86c8SMitchell Horne 			return (ENXIO);
353a45a86c8SMitchell Horne 		}
354a45a86c8SMitchell Horne 
355a45a86c8SMitchell Horne 		/* Get the corresponding cpuid. */
356a45a86c8SMitchell Horne 		cpu = riscv_hartid_to_cpu(hart);
357a45a86c8SMitchell Horne 		if (cpu < 0) {
358a45a86c8SMitchell Horne 			device_printf(dev, "Invalid hart!\n");
359a45a86c8SMitchell Horne 			OF_prop_free(cells);
360a45a86c8SMitchell Horne 			return (ENXIO);
361a45a86c8SMitchell Horne 		}
362a45a86c8SMitchell Horne 
363*6ec8bf9fSJessica Clarke 		if (cpu == 0)
364*6ec8bf9fSJessica Clarke 			rid = i / 2;
365*6ec8bf9fSJessica Clarke 
366a45a86c8SMitchell Horne 		/* Set the enable and context register offsets for the CPU. */
367a45a86c8SMitchell Horne 		sc->contexts[cpu].enable_offset = PLIC_ENABLE_BASE +
368a45a86c8SMitchell Horne 		    context * PLIC_ENABLE_STRIDE;
369a45a86c8SMitchell Horne 		sc->contexts[cpu].context_offset = PLIC_CONTEXT_BASE +
370a45a86c8SMitchell Horne 		    context * PLIC_CONTEXT_STRIDE;
371a45a86c8SMitchell Horne 	}
372a45a86c8SMitchell Horne 	OF_prop_free(cells);
373a45a86c8SMitchell Horne 
374*6ec8bf9fSJessica Clarke 	if (rid == -1) {
375*6ec8bf9fSJessica Clarke 		device_printf(dev,
376*6ec8bf9fSJessica Clarke 		    "Could not find local interrupt controller\n");
377*6ec8bf9fSJessica Clarke 		return (ENXIO);
378*6ec8bf9fSJessica Clarke 	}
379*6ec8bf9fSJessica Clarke 
380*6ec8bf9fSJessica Clarke 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
381*6ec8bf9fSJessica Clarke 	    RF_ACTIVE);
382*6ec8bf9fSJessica Clarke 	if (sc->irq_res == NULL) {
383*6ec8bf9fSJessica Clarke 		device_printf(dev,
384*6ec8bf9fSJessica Clarke 		    "Error: could not allocate IRQ resources\n");
385*6ec8bf9fSJessica Clarke 		return (ENXIO);
386*6ec8bf9fSJessica Clarke 	}
387*6ec8bf9fSJessica Clarke 
388bc5f705aSMitchell Horne 	/* Set the threshold for each CPU to accept all priorities. */
389bc5f705aSMitchell Horne 	CPU_FOREACH(cpu)
390a45a86c8SMitchell Horne 		WR4(sc, PLIC_THRESHOLD(sc, cpu), 0);
3912d53a67cSRuslan Bukin 
392a8cb655dSRuslan Bukin 	xref = OF_xref_from_node(node);
3932d53a67cSRuslan Bukin 	pic = intr_pic_register(sc->dev, xref);
3942d53a67cSRuslan Bukin 	if (pic == NULL)
3952d53a67cSRuslan Bukin 		return (ENXIO);
3962d53a67cSRuslan Bukin 
397*6ec8bf9fSJessica Clarke 	return (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_CLK | INTR_MPSAFE,
398*6ec8bf9fSJessica Clarke 	    plic_intr, NULL, sc, &sc->ih));
3992d53a67cSRuslan Bukin }
4002d53a67cSRuslan Bukin 
4017bad03a8SRuslan Bukin static void
plic_pre_ithread(device_t dev,struct intr_irqsrc * isrc)4027bad03a8SRuslan Bukin plic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
4037bad03a8SRuslan Bukin {
4047bad03a8SRuslan Bukin 
405bc5f705aSMitchell Horne 	plic_disable_intr(dev, isrc);
4067bad03a8SRuslan Bukin }
4077bad03a8SRuslan Bukin 
4087bad03a8SRuslan Bukin static void
plic_post_ithread(device_t dev,struct intr_irqsrc * isrc)4097bad03a8SRuslan Bukin plic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
4107bad03a8SRuslan Bukin {
41138d715f7SKristof Provost 	struct plic_softc *sc;
41238d715f7SKristof Provost 	struct plic_irqsrc *src;
41338d715f7SKristof Provost 	uint32_t cpu;
414bc5f705aSMitchell Horne 
41538d715f7SKristof Provost 	sc = device_get_softc(dev);
41638d715f7SKristof Provost 	src = (struct plic_irqsrc *)isrc;
41738d715f7SKristof Provost 
41838d715f7SKristof Provost 	cpu = CPU_FFS(&isrc->isrc_cpu) - 1;
41938d715f7SKristof Provost 
42038d715f7SKristof Provost 	/* Complete the interrupt. */
42138d715f7SKristof Provost 	WR4(sc, PLIC_CLAIM(sc, cpu), src->irq);
422bc5f705aSMitchell Horne 	plic_enable_intr(dev, isrc);
423bc5f705aSMitchell Horne }
424bc5f705aSMitchell Horne 
425bc5f705aSMitchell Horne static int
plic_setup_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)426bc5f705aSMitchell Horne plic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
427bc5f705aSMitchell Horne     struct resource *res, struct intr_map_data *data)
428bc5f705aSMitchell Horne {
429dc425090SMitchell Horne 	CPU_ZERO(&isrc->isrc_cpu);
430bc5f705aSMitchell Horne 	plic_bind_intr(dev, isrc);
431bc5f705aSMitchell Horne 
432bc5f705aSMitchell Horne 	return (0);
433bc5f705aSMitchell Horne }
434bc5f705aSMitchell Horne 
435bc5f705aSMitchell Horne static int
plic_bind_intr(device_t dev,struct intr_irqsrc * isrc)436bc5f705aSMitchell Horne plic_bind_intr(device_t dev, struct intr_irqsrc *isrc)
437bc5f705aSMitchell Horne {
438bc5f705aSMitchell Horne 	struct plic_softc *sc;
439bc5f705aSMitchell Horne 	struct plic_irqsrc *src;
440bc5f705aSMitchell Horne 	uint32_t reg;
441bc5f705aSMitchell Horne 	u_int cpu;
442bc5f705aSMitchell Horne 
443bc5f705aSMitchell Horne 	sc = device_get_softc(dev);
444bc5f705aSMitchell Horne 	src = (struct plic_irqsrc *)isrc;
445bc5f705aSMitchell Horne 
446bc5f705aSMitchell Horne 	/* Disable the interrupt source on all CPUs. */
447bc5f705aSMitchell Horne 	CPU_FOREACH(cpu) {
448bc5f705aSMitchell Horne 		reg = RD4(sc, PLIC_ENABLE(sc, src->irq, cpu));
449bc5f705aSMitchell Horne 		reg &= ~(1 << (src->irq % 32));
450bc5f705aSMitchell Horne 		WR4(sc, PLIC_ENABLE(sc, src->irq, cpu), reg);
451bc5f705aSMitchell Horne 	}
452bc5f705aSMitchell Horne 
453bc5f705aSMitchell Horne 	if (CPU_EMPTY(&isrc->isrc_cpu)) {
454bc5f705aSMitchell Horne 		cpu = plic_irq_cpu = intr_irq_next_cpu(plic_irq_cpu, &all_cpus);
455bc5f705aSMitchell Horne 		CPU_SETOF(cpu, &isrc->isrc_cpu);
456bc5f705aSMitchell Horne 	} else {
457bc5f705aSMitchell Horne 		/*
458bc5f705aSMitchell Horne 		 * We will only bind to a single CPU so select the first
459bc5f705aSMitchell Horne 		 * CPU found.
460bc5f705aSMitchell Horne 		 */
461bc5f705aSMitchell Horne 		cpu = CPU_FFS(&isrc->isrc_cpu) - 1;
462bc5f705aSMitchell Horne 	}
463bc5f705aSMitchell Horne 
464bc5f705aSMitchell Horne 	/* Enable the interrupt on the selected CPU only. */
465bc5f705aSMitchell Horne 	reg = RD4(sc, PLIC_ENABLE(sc, src->irq, cpu));
466bc5f705aSMitchell Horne 	reg |= (1 << (src->irq % 32));
467bc5f705aSMitchell Horne 	WR4(sc, PLIC_ENABLE(sc, src->irq, cpu), reg);
468bc5f705aSMitchell Horne 
469bc5f705aSMitchell Horne 	return (0);
4707bad03a8SRuslan Bukin }
4717bad03a8SRuslan Bukin 
4722d53a67cSRuslan Bukin static device_method_t plic_methods[] = {
4732d53a67cSRuslan Bukin 	DEVMETHOD(device_probe,		plic_probe),
4742d53a67cSRuslan Bukin 	DEVMETHOD(device_attach,	plic_attach),
4752d53a67cSRuslan Bukin 
4762d53a67cSRuslan Bukin 	DEVMETHOD(pic_disable_intr,	plic_disable_intr),
4772d53a67cSRuslan Bukin 	DEVMETHOD(pic_enable_intr,	plic_enable_intr),
4782d53a67cSRuslan Bukin 	DEVMETHOD(pic_map_intr,		plic_map_intr),
4797bad03a8SRuslan Bukin 	DEVMETHOD(pic_pre_ithread,	plic_pre_ithread),
4807bad03a8SRuslan Bukin 	DEVMETHOD(pic_post_ithread,	plic_post_ithread),
48138d715f7SKristof Provost 	DEVMETHOD(pic_post_filter,	plic_post_ithread),
482bc5f705aSMitchell Horne 	DEVMETHOD(pic_setup_intr,	plic_setup_intr),
483bc5f705aSMitchell Horne 	DEVMETHOD(pic_bind_intr,	plic_bind_intr),
4842d53a67cSRuslan Bukin 
4852d53a67cSRuslan Bukin 	DEVMETHOD_END
4862d53a67cSRuslan Bukin };
4872d53a67cSRuslan Bukin 
4882d53a67cSRuslan Bukin static driver_t plic_driver = {
4892d53a67cSRuslan Bukin 	"plic",
4902d53a67cSRuslan Bukin 	plic_methods,
4912d53a67cSRuslan Bukin 	sizeof(struct plic_softc),
4922d53a67cSRuslan Bukin };
4932d53a67cSRuslan Bukin 
4942663ef1bSJohn Baldwin EARLY_DRIVER_MODULE(plic, simplebus, plic_driver, 0, 0,
4952663ef1bSJohn Baldwin     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
496