xref: /freebsd/sys/riscv/riscv/plic.c (revision a8cb655d)
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		2048
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 		return (ENXIO);
179 
180 	device_set_desc(dev, "RISC-V PLIC");
181 
182 	return (BUS_PROBE_DEFAULT);
183 }
184 
185 static int
186 plic_attach(device_t dev)
187 {
188 	struct plic_irqsrc *isrcs;
189 	struct plic_softc *sc;
190 	struct intr_pic *pic;
191 	uint32_t irq;
192 	const char *name;
193 	phandle_t node;
194 	phandle_t xref;
195 	uint32_t cpu;
196 	int error;
197 	int rid;
198 
199 	sc = device_get_softc(dev);
200 
201 	sc->dev = dev;
202 
203 	node = ofw_bus_get_node(dev);
204 	if ((OF_getencprop(node, "riscv,ndev", &sc->ndev,
205 	    sizeof(sc->ndev))) < 0) {
206 		device_printf(dev,
207 		    "Error: could not get number of devices\n");
208 		return (ENXIO);
209 	}
210 
211 	if (sc->ndev >= PLIC_MAX_IRQS) {
212 		device_printf(dev,
213 		    "Error: invalid ndev (%d)\n", sc->ndev);
214 		return (ENXIO);
215 	}
216 
217 	/* Request memory resources */
218 	rid = 0;
219 	sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
220 	    RF_ACTIVE);
221 	if (sc->intc_res == NULL) {
222 		device_printf(dev,
223 		    "Error: could not allocate memory resources\n");
224 		return (ENXIO);
225 	}
226 
227 	isrcs = sc->isrcs;
228 	name = device_get_nameunit(sc->dev);
229 	cpu = PCPU_GET(cpuid);
230 	for (irq = 1; irq <= sc->ndev; irq++) {
231 		isrcs[irq].irq = irq;
232 		error = intr_isrc_register(&isrcs[irq].isrc, sc->dev,
233 		    0, "%s,%u", name, irq);
234 		if (error != 0)
235 			return (error);
236 
237 		WR4(sc, PLIC_PRIORITY(irq), 0);
238 		WR4(sc, PLIC_ENABLE(irq, cpu), 0);
239 	}
240 	WR4(sc, PLIC_THRESHOLD(cpu), 0);
241 
242 	xref = OF_xref_from_node(node);
243 	pic = intr_pic_register(sc->dev, xref);
244 	if (pic == NULL)
245 		return (ENXIO);
246 
247 	csr_set(sie, SIE_SEIE);
248 
249 	return (intr_pic_claim_root(sc->dev, xref, plic_intr, sc, 0));
250 }
251 
252 static device_method_t plic_methods[] = {
253 	DEVMETHOD(device_probe,		plic_probe),
254 	DEVMETHOD(device_attach,	plic_attach),
255 
256 	DEVMETHOD(pic_disable_intr,	plic_disable_intr),
257 	DEVMETHOD(pic_enable_intr,	plic_enable_intr),
258 	DEVMETHOD(pic_map_intr,		plic_map_intr),
259 
260 	DEVMETHOD_END
261 };
262 
263 static driver_t plic_driver = {
264 	"plic",
265 	plic_methods,
266 	sizeof(struct plic_softc),
267 };
268 
269 static devclass_t plic_devclass;
270 
271 EARLY_DRIVER_MODULE(plic, simplebus, plic_driver, plic_devclass,
272     0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
273