xref: /f-stack/freebsd/mips/beri/beri_pic.c (revision 22ce4aff)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2017 Ruslan Bukin <[email protected]>
5  * Copyright (c) 2013 SRI International
6  * All rights reserved.
7  *
8  * This software was developed by SRI International and the University of
9  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
10  * ("CTSRD"), as part of the DARPA CRASH 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 "opt_platform.h"
35 
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38 
39 #include <sys/param.h>
40 #include <sys/kernel.h>
41 #include <sys/lock.h>
42 #include <sys/malloc.h>
43 #include <sys/module.h>
44 #include <sys/mutex.h>
45 #include <sys/proc.h>
46 #include <sys/systm.h>
47 #include <sys/bus.h>
48 
49 #include <machine/bus.h>
50 #include <machine/intr.h>
51 
52 #ifdef SMP
53 #include <mips/beri/beri_mp.h>
54 #endif
55 
56 #include <dev/fdt/fdt_common.h>
57 #include <dev/ofw/openfirm.h>
58 #include <dev/ofw/ofw_bus.h>
59 #include <dev/ofw/ofw_bus_subr.h>
60 
61 #include "pic_if.h"
62 
63 #define	BP_NUM_HARD_IRQS	5
64 #define	BP_NUM_IRQS		32
65 /* We use hard irqs 15-31 as soft */
66 #define	BP_FIRST_SOFT		16
67 
68 #define	BP_CFG_IRQ_S		0
69 #define	BP_CFG_IRQ_M		(0xf << BP_CFG_IRQ_S)
70 #define	BP_CFG_TID_S		8
71 #define	BP_CFG_TID_M		(0x7FFFFF << BP_CFG_TID_S)
72 #define	BP_CFG_ENABLE		(1 << 31)
73 
74 enum {
75 	BP_CFG,
76 	BP_IP_READ,
77 	BP_IP_SET,
78 	BP_IP_CLEAR
79 };
80 
81 struct beripic_softc;
82 
83 struct beri_pic_isrc {
84 	struct intr_irqsrc	isrc;
85 	u_int			irq;
86 	uint32_t		mips_hard_irq;
87 };
88 
89 struct hirq {
90 	uint32_t		irq;
91 	struct beripic_softc	*sc;
92 };
93 
94 struct beripic_softc {
95 	device_t		dev;
96 	uint32_t		nirqs;
97 	struct beri_pic_isrc	irqs[BP_NUM_IRQS];
98 	struct resource		*res[4 + BP_NUM_HARD_IRQS];
99 	void			*ih[BP_NUM_HARD_IRQS];
100 	struct hirq		hirq[BP_NUM_HARD_IRQS];
101 	uint8_t			mips_hard_irq_idx;
102 };
103 
104 static struct resource_spec beri_pic_spec[] = {
105 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
106 	{ SYS_RES_MEMORY,	1,	RF_ACTIVE },
107 	{ SYS_RES_MEMORY,	2,	RF_ACTIVE },
108 	{ SYS_RES_MEMORY,	3,	RF_ACTIVE },
109 	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
110 	{ SYS_RES_IRQ,		1,	RF_ACTIVE },
111 	{ SYS_RES_IRQ,		2,	RF_ACTIVE },
112 	{ SYS_RES_IRQ,		3,	RF_ACTIVE },
113 	{ SYS_RES_IRQ,		4,	RF_ACTIVE },
114 	{ -1, 0 }
115 };
116 
117 static int
beri_pic_intr(void * arg)118 beri_pic_intr(void *arg)
119 {
120 	struct beripic_softc *sc;
121 	struct intr_irqsrc *isrc;
122 	struct hirq *h;
123 	uint64_t intr;
124 	uint64_t reg;
125 	int i;
126 
127 	h = arg;
128 	sc = h->sc;
129 
130 	intr = bus_read_8(sc->res[BP_IP_READ], 0);
131 	while ((i = fls(intr)) != 0) {
132 		i--;
133 		intr &= ~(1u << i);
134 
135 		isrc = &sc->irqs[i].isrc;
136 
137 		reg = bus_read_8(sc->res[BP_CFG], i * 8);
138 		if ((reg & BP_CFG_IRQ_M) != h->irq) {
139 			continue;
140 		}
141 		if ((reg & (BP_CFG_ENABLE)) == 0) {
142 			continue;
143 		}
144 
145 		if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) {
146 			device_printf(sc->dev, "Stray interrupt %u detected\n", i);
147 		}
148 
149 		bus_write_8(sc->res[BP_IP_CLEAR], 0, (1 << i));
150 	}
151 
152 	return (FILTER_HANDLED);
153 }
154 
155 static int
beripic_probe(device_t dev)156 beripic_probe(device_t dev)
157 {
158 
159 	if (!ofw_bus_status_okay(dev))
160 		return (ENXIO);
161 
162 	if (!ofw_bus_is_compatible(dev, "sri-cambridge,beri-pic"))
163 		return (ENXIO);
164 
165 	device_set_desc(dev, "BERI Programmable Interrupt Controller");
166 
167 	return (BUS_PROBE_DEFAULT);
168 }
169 
170 static int
beripic_attach(device_t dev)171 beripic_attach(device_t dev)
172 {
173 	struct beripic_softc *sc;
174 	struct beri_pic_isrc *pic_isrc;
175 	const char *name;
176 	struct intr_irqsrc *isrc;
177 	intptr_t xref;
178 	uint32_t unit;
179 	int err;
180 	int i;
181 
182 	sc = device_get_softc(dev);
183 	sc->dev = dev;
184 
185 	if (bus_alloc_resources(dev, beri_pic_spec, sc->res)) {
186 		device_printf(dev, "could not allocate resources\n");
187 		return (ENXIO);
188 	}
189 
190 	xref = OF_xref_from_node(ofw_bus_get_node(dev));
191 	name = device_get_nameunit(dev);
192 	unit = device_get_unit(dev);
193 	sc->nirqs = BP_NUM_IRQS;
194 
195 	for (i = 0; i < sc->nirqs; i++) {
196 		sc->irqs[i].irq = i;
197 		isrc = &sc->irqs[i].isrc;
198 
199 		/* Assign mips hard irq number. */
200 		pic_isrc = (struct beri_pic_isrc *)isrc;
201 		pic_isrc->mips_hard_irq = sc->mips_hard_irq_idx++;
202 		/* Last IRQ is used for IPIs. */
203 		if (sc->mips_hard_irq_idx >= (BP_NUM_HARD_IRQS - 1)) {
204 			sc->mips_hard_irq_idx = 0;
205 		}
206 
207 		err = intr_isrc_register(isrc, sc->dev,
208 		    0, "pic%d,%d", unit, i);
209 		bus_write_8(sc->res[BP_CFG], i * 8, 0);
210 	}
211 
212 	/*
213 	 * Now, when everything is initialized, it's right time to
214 	 * register interrupt controller to interrupt framefork.
215 	 */
216 	if (intr_pic_register(dev, xref) == NULL) {
217 		device_printf(dev, "could not register PIC\n");
218 		return (ENXIO);
219 	}
220 
221 	/* Last IRQ is used for IPIs. */
222 	for (i = 0; i < (BP_NUM_HARD_IRQS - 1); i++) {
223 		sc->hirq[i].sc = sc;
224 		sc->hirq[i].irq = i;
225 		if (bus_setup_intr(dev, sc->res[4+i], INTR_TYPE_CLK,
226 		    beri_pic_intr, NULL, &sc->hirq[i], sc->ih[i])) {
227 			device_printf(dev, "could not setup irq handler\n");
228 			intr_pic_deregister(dev, xref);
229 			return (ENXIO);
230 		}
231 	}
232 
233 	return (0);
234 }
235 
236 static void
beri_pic_enable_intr(device_t dev,struct intr_irqsrc * isrc)237 beri_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
238 {
239 	struct beri_pic_isrc *pic_isrc;
240 	struct beripic_softc *sc;
241 	uint64_t reg;
242 
243 	sc = device_get_softc(dev);
244 	pic_isrc = (struct beri_pic_isrc *)isrc;
245 
246 	reg = BP_CFG_ENABLE;
247 	reg |= (pic_isrc->mips_hard_irq << BP_CFG_IRQ_S);
248 	bus_write_8(sc->res[BP_CFG], pic_isrc->irq * 8, reg);
249 }
250 
251 static void
beri_pic_disable_intr(device_t dev,struct intr_irqsrc * isrc)252 beri_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
253 {
254 	struct beri_pic_isrc *pic_isrc;
255 	struct beripic_softc *sc;
256 	uint64_t reg;
257 
258 	sc = device_get_softc(dev);
259 	pic_isrc = (struct beri_pic_isrc *)isrc;
260 
261 	reg = bus_read_8(sc->res[BP_CFG], pic_isrc->irq * 8);
262 	reg &= ~BP_CFG_ENABLE;
263 	bus_write_8(sc->res[BP_CFG], pic_isrc->irq * 8, reg);
264 }
265 
266 static int
beri_pic_map_intr(device_t dev,struct intr_map_data * data,struct intr_irqsrc ** isrcp)267 beri_pic_map_intr(device_t dev, struct intr_map_data *data,
268     struct intr_irqsrc **isrcp)
269 {
270 	struct beripic_softc *sc;
271 	struct intr_map_data_fdt *daf;
272 	uint32_t irq;
273 
274 	sc = device_get_softc(dev);
275 	daf = (struct intr_map_data_fdt *)data;
276 
277 	if (data == NULL || data->type != INTR_MAP_DATA_FDT ||
278 	    daf->ncells != 1 || daf->cells[0] >= sc->nirqs)
279 		return (EINVAL);
280 
281 	irq = daf->cells[0];
282 
283 	*isrcp = &sc->irqs[irq].isrc;
284 
285 	return (0);
286 }
287 
288 static void
beri_pic_post_ithread(device_t dev,struct intr_irqsrc * isrc)289 beri_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
290 {
291 
292 	beri_pic_enable_intr(dev, isrc);
293 }
294 
295 static void
beri_pic_pre_ithread(device_t dev,struct intr_irqsrc * isrc)296 beri_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
297 {
298 
299 	beri_pic_disable_intr(dev, isrc);
300 }
301 
302 #ifdef SMP
303 void
beripic_setup_ipi(device_t dev,u_int tid,u_int ipi_irq)304 beripic_setup_ipi(device_t dev, u_int tid, u_int ipi_irq)
305 {
306 	struct beripic_softc *sc;
307 	uint64_t reg;
308 
309 	sc = device_get_softc(dev);
310 
311 	reg = (BP_CFG_ENABLE);
312 	reg |= (ipi_irq << BP_CFG_IRQ_S);
313 	reg |= (tid << BP_CFG_TID_S);
314 	bus_write_8(sc->res[BP_CFG], ((BP_FIRST_SOFT + tid) * 8), reg);
315 }
316 
317 void
beripic_send_ipi(device_t dev,u_int tid)318 beripic_send_ipi(device_t dev, u_int tid)
319 {
320 	struct beripic_softc *sc;
321 	uint64_t bit;
322 
323 	sc = device_get_softc(dev);
324 
325 	bit = (BP_FIRST_SOFT + tid);
326 	KASSERT(bit < BP_NUM_IRQS, ("tid (%d) to large\n", tid));
327 
328 	bus_write_8(sc->res[BP_IP_SET], 0x0, (1 << bit));
329 }
330 
331 void
beripic_clear_ipi(device_t dev,u_int tid)332 beripic_clear_ipi(device_t dev, u_int tid)
333 {
334 	struct beripic_softc *sc;
335 	uint64_t bit;
336 
337 	sc = device_get_softc(dev);
338 
339 	bit = (BP_FIRST_SOFT + tid);
340 	KASSERT(bit < BP_NUM_IRQS, ("tid (%d) to large\n", tid));
341 
342 	bus_write_8(sc->res[BP_IP_CLEAR], 0x0, (1 << bit));
343 }
344 #endif
345 
346 static device_method_t beripic_fdt_methods[] = {
347 	/* Device interface */
348 	DEVMETHOD(device_probe,		beripic_probe),
349 	DEVMETHOD(device_attach,	beripic_attach),
350 
351 	/* Interrupt controller interface */
352 	DEVMETHOD(pic_enable_intr,	beri_pic_enable_intr),
353 	DEVMETHOD(pic_disable_intr,	beri_pic_disable_intr),
354 	DEVMETHOD(pic_map_intr,		beri_pic_map_intr),
355 	DEVMETHOD(pic_post_ithread,	beri_pic_post_ithread),
356 	DEVMETHOD(pic_pre_ithread,	beri_pic_pre_ithread),
357 
358 	DEVMETHOD_END
359 };
360 
361 devclass_t beripic_devclass;
362 
363 static driver_t beripic_driver = {
364 	"beripic",
365 	beripic_fdt_methods,
366 	sizeof(struct beripic_softc)
367 };
368 
369 EARLY_DRIVER_MODULE(beripic, ofwbus, beripic_driver, beripic_devclass, 0, 0,
370     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
371