xref: /freebsd/sys/riscv/riscv/plic.c (revision 6f1f29f6)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2018 Ruslan Bukin <br@bsdpad.com>
5  * All rights reserved.
6  *
7  * This software was developed by SRI International and the University of
8  * Cambridge Computer Laboratory (Department of Computer Science and
9  * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
10  * DARPA SSITH research programme.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/bus.h>
40 #include <sys/kernel.h>
41 #include <sys/ktr.h>
42 #include <sys/module.h>
43 #include <sys/proc.h>
44 #include <sys/rman.h>
45 
46 #include <machine/bus.h>
47 #include <machine/intr.h>
48 
49 #include <dev/ofw/openfirm.h>
50 #include <dev/ofw/ofw_bus.h>
51 #include <dev/ofw/ofw_bus_subr.h>
52 
53 #include "pic_if.h"
54 
55 #define	PLIC_MAX_IRQS		1024
56 #define	PLIC_PRIORITY(n)	(0x000000 + (n) * 0x4)
57 #define	PLIC_ENABLE(n, h)	(0x002000 + (h) * 0x80 + 4 * ((n) / 32))
58 #define	PLIC_THRESHOLD(h)	(0x200000 + (h) * 0x1000 + 0x0)
59 #define	PLIC_CLAIM(h)		(0x200000 + (h) * 0x1000 + 0x4)
60 
61 struct plic_irqsrc {
62 	struct intr_irqsrc	isrc;
63 	u_int			irq;
64 };
65 
66 struct plic_softc {
67 	device_t		dev;
68 	struct resource *	intc_res;
69 	struct plic_irqsrc	isrcs[PLIC_MAX_IRQS];
70 	int			ndev;
71 };
72 
73 #define	RD4(sc, reg)				\
74     bus_read_4(sc->intc_res, (reg))
75 #define	WR4(sc, reg, val)			\
76     bus_write_4(sc->intc_res, (reg), (val))
77 
78 static inline void
79 plic_irq_dispatch(struct plic_softc *sc, u_int irq,
80     struct trapframe *tf)
81 {
82 	struct plic_irqsrc *src;
83 
84 	src = &sc->isrcs[irq];
85 
86 	if (intr_isrc_dispatch(&src->isrc, tf) != 0)
87 		device_printf(sc->dev, "Stray irq %u detected\n", irq);
88 }
89 
90 static int
91 plic_intr(void *arg)
92 {
93 	struct plic_softc *sc;
94 	struct trapframe *tf;
95 	uint32_t pending;
96 	uint32_t cpu;
97 
98 	sc = arg;
99 	cpu = PCPU_GET(cpuid);
100 
101 	pending = RD4(sc, PLIC_CLAIM(cpu));
102 	if (pending) {
103 		tf = curthread->td_intr_frame;
104 		plic_irq_dispatch(sc, pending, tf);
105 		WR4(sc, PLIC_CLAIM(cpu), pending);
106 	}
107 
108 	return (FILTER_HANDLED);
109 }
110 
111 static void
112 plic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
113 {
114 	struct plic_softc *sc;
115 	struct plic_irqsrc *src;
116 	uint32_t reg;
117 	uint32_t cpu;
118 
119 	sc = device_get_softc(dev);
120 	src = (struct plic_irqsrc *)isrc;
121 
122 	cpu = PCPU_GET(cpuid);
123 
124 	reg = RD4(sc, PLIC_ENABLE(src->irq, cpu));
125 	reg &= ~(1 << (src->irq % 32));
126 	WR4(sc, PLIC_ENABLE(src->irq, cpu), reg);
127 }
128 
129 static void
130 plic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
131 {
132 	struct plic_softc *sc;
133 	struct plic_irqsrc *src;
134 	uint32_t reg;
135 	uint32_t cpu;
136 
137 	sc = device_get_softc(dev);
138 	src = (struct plic_irqsrc *)isrc;
139 
140 	WR4(sc, PLIC_PRIORITY(src->irq), 1);
141 
142 	cpu = PCPU_GET(cpuid);
143 
144 	reg = RD4(sc, PLIC_ENABLE(src->irq, cpu));
145 	reg |= (1 << (src->irq % 32));
146 	WR4(sc, PLIC_ENABLE(src->irq, cpu), reg);
147 }
148 
149 static int
150 plic_map_intr(device_t dev, struct intr_map_data *data,
151     struct intr_irqsrc **isrcp)
152 {
153 	struct intr_map_data_fdt *daf;
154 	struct plic_softc *sc;
155 
156 	sc = device_get_softc(dev);
157 
158 	if (data->type != INTR_MAP_DATA_FDT)
159 		return (ENOTSUP);
160 
161 	daf = (struct intr_map_data_fdt *)data;
162 	if (daf->ncells != 1 || daf->cells[0] > sc->ndev)
163 		return (EINVAL);
164 
165 	*isrcp = &sc->isrcs[daf->cells[0]].isrc;
166 
167 	return (0);
168 }
169 
170 static int
171 plic_probe(device_t dev)
172 {
173 
174 	if (!ofw_bus_status_okay(dev))
175 		return (ENXIO);
176 
177 	if (!ofw_bus_is_compatible(dev, "riscv,plic0") &&
178 	    !ofw_bus_is_compatible(dev, "sifive,plic-1.0.0"))
179 		return (ENXIO);
180 
181 	device_set_desc(dev, "RISC-V PLIC");
182 
183 	return (BUS_PROBE_DEFAULT);
184 }
185 
186 static int
187 plic_attach(device_t dev)
188 {
189 	struct plic_irqsrc *isrcs;
190 	struct plic_softc *sc;
191 	struct intr_pic *pic;
192 	uint32_t irq;
193 	const char *name;
194 	phandle_t node;
195 	phandle_t xref;
196 	uint32_t cpu;
197 	int error;
198 	int rid;
199 
200 	sc = device_get_softc(dev);
201 
202 	sc->dev = dev;
203 
204 	node = ofw_bus_get_node(dev);
205 	if ((OF_getencprop(node, "riscv,ndev", &sc->ndev,
206 	    sizeof(sc->ndev))) < 0) {
207 		device_printf(dev,
208 		    "Error: could not get number of devices\n");
209 		return (ENXIO);
210 	}
211 
212 	if (sc->ndev >= PLIC_MAX_IRQS) {
213 		device_printf(dev,
214 		    "Error: invalid ndev (%d)\n", sc->ndev);
215 		return (ENXIO);
216 	}
217 
218 	/* Request memory resources */
219 	rid = 0;
220 	sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
221 	    RF_ACTIVE);
222 	if (sc->intc_res == NULL) {
223 		device_printf(dev,
224 		    "Error: could not allocate memory resources\n");
225 		return (ENXIO);
226 	}
227 
228 	isrcs = sc->isrcs;
229 	name = device_get_nameunit(sc->dev);
230 	cpu = PCPU_GET(cpuid);
231 	for (irq = 1; irq <= sc->ndev; irq++) {
232 		isrcs[irq].irq = irq;
233 		error = intr_isrc_register(&isrcs[irq].isrc, sc->dev,
234 		    0, "%s,%u", name, irq);
235 		if (error != 0)
236 			return (error);
237 
238 		WR4(sc, PLIC_PRIORITY(irq), 0);
239 		WR4(sc, PLIC_ENABLE(irq, cpu), 0);
240 	}
241 	WR4(sc, PLIC_THRESHOLD(cpu), 0);
242 
243 	xref = OF_xref_from_node(node);
244 	pic = intr_pic_register(sc->dev, xref);
245 	if (pic == NULL)
246 		return (ENXIO);
247 
248 	csr_set(sie, SIE_SEIE);
249 
250 	return (intr_pic_claim_root(sc->dev, xref, plic_intr, sc, 0));
251 }
252 
253 static void
254 plic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
255 {
256 	struct plic_softc *sc;
257 	struct plic_irqsrc *src;
258 
259 	sc = device_get_softc(dev);
260 	src = (struct plic_irqsrc *)isrc;
261 
262 	WR4(sc, PLIC_PRIORITY(src->irq), 0);
263 }
264 
265 static void
266 plic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
267 {
268 	struct plic_softc *sc;
269 	struct plic_irqsrc *src;
270 
271 	sc = device_get_softc(dev);
272 	src = (struct plic_irqsrc *)isrc;
273 
274 	WR4(sc, PLIC_PRIORITY(src->irq), 1);
275 }
276 
277 static device_method_t plic_methods[] = {
278 	DEVMETHOD(device_probe,		plic_probe),
279 	DEVMETHOD(device_attach,	plic_attach),
280 
281 	DEVMETHOD(pic_disable_intr,	plic_disable_intr),
282 	DEVMETHOD(pic_enable_intr,	plic_enable_intr),
283 	DEVMETHOD(pic_map_intr,		plic_map_intr),
284 	DEVMETHOD(pic_pre_ithread,	plic_pre_ithread),
285 	DEVMETHOD(pic_post_ithread,	plic_post_ithread),
286 
287 	DEVMETHOD_END
288 };
289 
290 static driver_t plic_driver = {
291 	"plic",
292 	plic_methods,
293 	sizeof(struct plic_softc),
294 };
295 
296 static devclass_t plic_devclass;
297 
298 EARLY_DRIVER_MODULE(plic, simplebus, plic_driver, plic_devclass,
299     0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
300