xref: /freebsd/sys/riscv/riscv/plic.c (revision 05efeb84)
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_NIRQS		32
56 #define	PLIC_PRIORITY(n)	(0x000000 + (n) * 0x4)
57 #define	PLIC_ENABLE(n, h)	(0x002000 + (h) * 0x80 + (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_NIRQS];
70 };
71 
72 #define	RD4(sc, reg)				\
73     bus_read_4(sc->intc_res, (reg))
74 #define	WR4(sc, reg, val)			\
75     bus_write_4(sc->intc_res, (reg), (val))
76 
77 static inline void
78 plic_irq_dispatch(struct plic_softc *sc, u_int irq,
79     struct trapframe *tf)
80 {
81 	struct plic_irqsrc *src;
82 
83 	src = &sc->isrcs[irq];
84 
85 	if (intr_isrc_dispatch(&src->isrc, tf) != 0)
86 		device_printf(sc->dev, "Stray irq %u detected\n", irq);
87 }
88 
89 static int
90 plic_intr(void *arg)
91 {
92 	struct plic_softc *sc;
93 	struct trapframe *tf;
94 	uint32_t pending;
95 	uint32_t cpu;
96 
97 	sc = arg;
98 	cpu = PCPU_GET(cpuid);
99 
100 	pending = RD4(sc, PLIC_CLAIM(cpu));
101 	if (pending) {
102 		tf = curthread->td_intr_frame;
103 		plic_irq_dispatch(sc, pending, tf);
104 		WR4(sc, PLIC_CLAIM(cpu), pending);
105 	}
106 
107 	return (FILTER_HANDLED);
108 }
109 
110 static void
111 plic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
112 {
113 	struct plic_softc *sc;
114 	struct plic_irqsrc *src;
115 	uint32_t reg;
116 	uint32_t cpu;
117 
118 	sc = device_get_softc(dev);
119 	src = (struct plic_irqsrc *)isrc;
120 
121 	cpu = PCPU_GET(cpuid);
122 
123 	reg = RD4(sc, PLIC_ENABLE(src->irq, cpu));
124 	reg &= ~(1 << (src->irq % 32));
125 	WR4(sc, PLIC_ENABLE(src->irq, cpu), reg);
126 }
127 
128 static void
129 plic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
130 {
131 	struct plic_softc *sc;
132 	struct plic_irqsrc *src;
133 	uint32_t reg;
134 	uint32_t cpu;
135 
136 	sc = device_get_softc(dev);
137 	src = (struct plic_irqsrc *)isrc;
138 
139 	WR4(sc, PLIC_PRIORITY(src->irq), 1);
140 
141 	cpu = PCPU_GET(cpuid);
142 
143 	reg = RD4(sc, PLIC_ENABLE(src->irq, cpu));
144 	reg |= (1 << (src->irq % 32));
145 	WR4(sc, PLIC_ENABLE(src->irq, cpu), reg);
146 }
147 
148 static int
149 plic_map_intr(device_t dev, struct intr_map_data *data,
150     struct intr_irqsrc **isrcp)
151 {
152 	struct intr_map_data_fdt *daf;
153 	struct plic_softc *sc;
154 
155 	sc = device_get_softc(dev);
156 
157 	if (data->type != INTR_MAP_DATA_FDT)
158 		return (ENOTSUP);
159 
160 	daf = (struct intr_map_data_fdt *)data;
161 	if (daf->ncells != 1 || daf->cells[0] >= PLIC_NIRQS)
162 		return (EINVAL);
163 
164 	*isrcp = &sc->isrcs[daf->cells[0]].isrc;
165 
166 	return (0);
167 }
168 
169 static int
170 plic_probe(device_t dev)
171 {
172 
173 	if (!ofw_bus_status_okay(dev))
174 		return (ENXIO);
175 
176 	if (!ofw_bus_is_compatible(dev, "riscv,plic0"))
177 		return (ENXIO);
178 
179 	device_set_desc(dev, "RISC-V PLIC");
180 
181 	return (BUS_PROBE_DEFAULT);
182 }
183 
184 static int
185 plic_attach(device_t dev)
186 {
187 	struct plic_irqsrc *isrcs;
188 	struct plic_softc *sc;
189 	struct intr_pic *pic;
190 	uint32_t irq;
191 	const char *name;
192 	phandle_t xref;
193 	uint32_t cpu;
194 	int error;
195 	int rid;
196 
197 	sc = device_get_softc(dev);
198 
199 	sc->dev = dev;
200 
201 	/* Request memory resources */
202 	rid = 0;
203 	sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
204 	    RF_ACTIVE);
205 	if (sc->intc_res == NULL) {
206 		device_printf(dev,
207 		    "Error: could not allocate memory resources\n");
208 		return (ENXIO);
209 	}
210 
211 	isrcs = sc->isrcs;
212 	name = device_get_nameunit(sc->dev);
213 	cpu = PCPU_GET(cpuid);
214 	for (irq = 0; irq < PLIC_NIRQS; irq++) {
215 		isrcs[irq].irq = irq;
216 		error = intr_isrc_register(&isrcs[irq].isrc, sc->dev,
217 		    0, "%s,%u", name, irq);
218 		if (error != 0)
219 			return (error);
220 
221 		WR4(sc, PLIC_PRIORITY(irq), 0);
222 		WR4(sc, PLIC_ENABLE(irq, cpu), 0);
223 	}
224 	WR4(sc, PLIC_THRESHOLD(cpu), 0);
225 
226 	xref = OF_xref_from_node(ofw_bus_get_node(sc->dev));
227 	pic = intr_pic_register(sc->dev, xref);
228 	if (pic == NULL)
229 		return (ENXIO);
230 
231 	csr_set(sie, SIE_SEIE);
232 
233 	return (intr_pic_claim_root(sc->dev, xref, plic_intr, sc, 0));
234 }
235 
236 static device_method_t plic_methods[] = {
237 	DEVMETHOD(device_probe,		plic_probe),
238 	DEVMETHOD(device_attach,	plic_attach),
239 
240 	DEVMETHOD(pic_disable_intr,	plic_disable_intr),
241 	DEVMETHOD(pic_enable_intr,	plic_enable_intr),
242 	DEVMETHOD(pic_map_intr,		plic_map_intr),
243 
244 	DEVMETHOD_END
245 };
246 
247 static driver_t plic_driver = {
248 	"plic",
249 	plic_methods,
250 	sizeof(struct plic_softc),
251 };
252 
253 static devclass_t plic_devclass;
254 
255 EARLY_DRIVER_MODULE(plic, simplebus, plic_driver, plic_devclass,
256     0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
257