xref: /f-stack/freebsd/mips/broadcom/bcm_bmips.c (revision 22ce4aff)
1 /*-
2  * Copyright (c) 2016 Landon Fuller <[email protected]>
3  * Copyright (c) 2017 The FreeBSD Foundation
4  * All rights reserved.
5  *
6  * Portions of this software were developed by Landon Fuller
7  * under sponsorship from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer,
14  *    without modification.
15  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
16  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
17  *    redistribution must be conditioned upon including a substantially
18  *    similar Disclaimer requirement for further binary redistribution.
19  *
20  * NO WARRANTY
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
24  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
25  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
26  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31  * THE POSSIBILITY OF SUCH DAMAGES.
32  */
33 
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36 
37 #include <sys/param.h>
38 #include <sys/kernel.h>
39 #include <sys/bus.h>
40 #include <sys/module.h>
41 #include <sys/proc.h>
42 
43 #include <machine/bus.h>
44 #include <sys/rman.h>
45 
46 #include <machine/intr.h>
47 #include <machine/resource.h>
48 
49 #include <dev/bhnd/bhnd.h>
50 #include <dev/bhnd/siba/sibareg.h>
51 
52 #include "pic_if.h"
53 
54 #include "bcm_mipsvar.h"
55 #include "bcm_bmipsreg.h"
56 
57 /*
58  * BMIPS32 and BMIPS3300 core driver.
59  *
60  * These cores are only found on siba(4) chipsets, allowing
61  * us to assume the availability of siba interrupt registers.
62  */
63 
64 struct bcm_bmips_softc;
65 
66 static int	bcm_bmips_pic_intr(void *arg);
67 static void	bcm_bmips_mask_irq(struct bcm_bmips_softc *sc, u_int mips_irq,
68 		    u_int ivec);
69 static void	bcm_bmips_unmask_irq(struct bcm_bmips_softc *sc, u_int mips_irq,
70 		    u_int ivec);
71 
72 static const struct bhnd_device bcm_bmips_devs[] = {
73 	BHND_DEVICE(BCM, MIPS33, NULL, NULL, BHND_DF_SOC),
74 	BHND_DEVICE_END
75 };
76 
77 struct bcm_bmips_softc {
78 	struct bcm_mips_softc	 bcm_mips;	/**< parent softc */
79 	device_t		 dev;
80 	struct resource		*mem;		/**< cpu core registers */
81 	int			 mem_rid;
82 	struct resource		*cfg;		/**< cpu core's cfg0 register block */
83 	int			 cfg_rid;
84 };
85 
86 #define	BCM_BMIPS_NCPU_IRQS	5	/**< MIPS HW IRQs 0-4 are assignable */
87 #define	BCM_BMIPS_TIMER_IRQ	5	/**< MIPS HW IRQ5 is always assigned to the timer */
88 
89 static int
bcm_bmips_probe(device_t dev)90 bcm_bmips_probe(device_t dev)
91 {
92 	const struct bhnd_device *id;
93 
94 	id = bhnd_device_lookup(dev, bcm_bmips_devs, sizeof(bcm_bmips_devs[0]));
95 	if (id == NULL)
96 		return (ENXIO);
97 
98 	/* Check the chip type; should only be found on siba(4) chipsets */
99 	if (bhnd_get_chipid(dev)->chip_type != BHND_CHIPTYPE_SIBA)
100 		return (ENXIO);
101 
102 	bhnd_set_default_core_desc(dev);
103 	return (BUS_PROBE_DEFAULT);
104 }
105 
106 static int
bcm_bmips_attach(device_t dev)107 bcm_bmips_attach(device_t dev)
108 {
109 	struct bcm_bmips_softc	*sc;
110 	int			 error;
111 
112 	sc = device_get_softc(dev);
113 	sc->dev = dev;
114 
115 	/* Allocate our core's register block */
116 	sc->mem_rid = 0;
117 	sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
118 	    RF_ACTIVE);
119 	if (sc->mem == NULL) {
120 		device_printf(dev, "failed to allocate cpu register block\n");
121 		error = ENXIO;
122 		goto failed;
123 	}
124 
125 	/* Determine the resource ID for our siba CFG0 registers */
126 	sc->cfg_rid = bhnd_get_port_rid(dev, BHND_PORT_AGENT, 0, 0);
127 	if (sc->cfg_rid == -1) {
128 		device_printf(dev, "missing required cfg0 register block\n");
129 		error = ENXIO;
130 		goto failed;
131 	}
132 
133 	/* Allocate our CFG0 register block */
134 	sc->cfg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->cfg_rid,
135 	    RF_ACTIVE|RF_SHAREABLE);
136 	if (sc->cfg == NULL) {
137 		device_printf(dev, "failed to allocate cfg0 register block\n");
138 		error = ENXIO;
139 		goto failed;
140 	}
141 
142 	/* Clear interrupt map */
143 	bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, 0x0);	/* MIPS IRQ0 */
144 	bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, 0x0);	/* MIPS IRQ1-4 */
145 
146 	/* Initialize the generic BHND MIPS driver state */
147 	error = bcm_mips_attach(dev, BCM_BMIPS_NCPU_IRQS, BCM_BMIPS_TIMER_IRQ,
148 	    bcm_bmips_pic_intr);
149 	if (error)
150 		goto failed;
151 
152 	return (0);
153 
154 failed:
155 	if (sc->mem != NULL)
156 		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
157 
158 	if (sc->cfg != NULL)
159 		bus_release_resource(dev, SYS_RES_MEMORY, sc->cfg_rid, sc->cfg);
160 
161 	return (error);
162 }
163 
164 static int
bcm_bmips_detach(device_t dev)165 bcm_bmips_detach(device_t dev)
166 {
167 	struct bcm_bmips_softc	*sc;
168 	int			 error;
169 
170 	sc = device_get_softc(dev);
171 
172 	if ((error = bcm_mips_detach(dev)))
173 		return (error);
174 
175 	bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
176 	bus_release_resource(dev, SYS_RES_MEMORY, sc->cfg_rid, sc->cfg);
177 
178 	return (0);
179 }
180 
181 /* PIC_DISABLE_INTR() */
182 static void
bcm_bmips_pic_disable_intr(device_t dev,struct intr_irqsrc * irqsrc)183 bcm_bmips_pic_disable_intr(device_t dev, struct intr_irqsrc *irqsrc)
184 {
185 	struct bcm_bmips_softc	*sc;
186 	struct bcm_mips_irqsrc	*isrc;
187 
188 	sc = device_get_softc(dev);
189 	isrc = (struct bcm_mips_irqsrc *)irqsrc;
190 
191 	KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
192 
193 	bcm_bmips_mask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
194 }
195 
196 /* PIC_ENABLE_INTR() */
197 static void
bcm_bmips_pic_enable_intr(device_t dev,struct intr_irqsrc * irqsrc)198 bcm_bmips_pic_enable_intr(device_t dev, struct intr_irqsrc *irqsrc)
199 {
200 	struct bcm_bmips_softc	*sc;
201 	struct bcm_mips_irqsrc	*isrc;
202 
203 	sc = device_get_softc(dev);
204 	isrc = (struct bcm_mips_irqsrc *)irqsrc;
205 
206 	KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
207 
208 	bcm_bmips_unmask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
209 }
210 
211 /* PIC_PRE_ITHREAD() */
212 static void
bcm_bmips_pic_pre_ithread(device_t dev,struct intr_irqsrc * isrc)213 bcm_bmips_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
214 {
215 	bcm_bmips_pic_disable_intr(dev, isrc);
216 }
217 
218 /* PIC_POST_ITHREAD() */
219 static void
bcm_bmips_pic_post_ithread(device_t dev,struct intr_irqsrc * isrc)220 bcm_bmips_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
221 {
222 	bcm_bmips_pic_enable_intr(dev, isrc);
223 }
224 
225 /* PIC_POST_FILTER() */
226 static void
bcm_bmips_pic_post_filter(device_t dev,struct intr_irqsrc * isrc)227 bcm_bmips_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
228 {
229 }
230 
231 /**
232  * Disable routing of backplane interrupt vector @p ivec to MIPS IRQ
233  * @p mips_irq.
234  */
235 static void
bcm_bmips_mask_irq(struct bcm_bmips_softc * sc,u_int mips_irq,u_int ivec)236 bcm_bmips_mask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, u_int ivec)
237 {
238 	KASSERT(ivec < SIBA_MAX_INTR, ("invalid sbflag# ivec"));
239 	KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
240 	    mips_irq));
241 
242 	if (mips_irq == 0) {
243 		uint32_t sbintvec;
244 
245 		sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
246 		sbintvec &= ~(1 << ivec);
247 		bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, sbintvec);
248 	} else {
249 		uint32_t ipsflag;
250 
251 		/* Can we route this via ipsflag? */
252 		KASSERT(((1 << ivec) & SIBA_IPS_INT1_MASK) != 0,
253 		    ("cannot route high sbflag# ivec %u", ivec));
254 
255 		ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
256 		ipsflag &= ~(
257 		    ((1 << ivec) << SIBA_IPS_INT_SHIFT(mips_irq)) &
258 		    SIBA_IPS_INT_MASK(mips_irq));
259 		bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, ipsflag);
260 	}
261 
262 }
263 
264 /**
265  * Enable routing of an interrupt.
266  */
267 static void
bcm_bmips_unmask_irq(struct bcm_bmips_softc * sc,u_int mips_irq,u_int ivec)268 bcm_bmips_unmask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, u_int ivec)
269 {
270 	KASSERT(ivec < SIBA_MAX_INTR, ("invalid sbflag# ivec"));
271 	KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
272 	    mips_irq));
273 
274 	if (mips_irq == 0) {
275 		uint32_t sbintvec;
276 
277 		sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
278 		sbintvec |= (1 << ivec);
279 		bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, sbintvec);
280 	} else {
281 		uint32_t ipsflag;
282 
283 		/* Can we route this via ipsflag? */
284 		KASSERT(((1 << ivec) & SIBA_IPS_INT1_MASK) != 0,
285 		    ("cannot route high sbflag# ivec %u", ivec));
286 
287 		ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
288 		ipsflag |= (ivec << SIBA_IPS_INT_SHIFT(mips_irq)) &
289 		    SIBA_IPS_INT_MASK(mips_irq);
290 		bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, ipsflag);
291 	}
292 }
293 
294 /* our MIPS CPU interrupt filter */
295 static int
bcm_bmips_pic_intr(void * arg)296 bcm_bmips_pic_intr(void *arg)
297 {
298 	struct bcm_bmips_softc	*sc;
299 	struct bcm_mips_cpuirq	*cpuirq;
300 	struct bcm_mips_irqsrc	*isrc_solo;
301 	uint32_t		 sbintvec, sbstatus;
302 	u_int			 mips_irq, i;
303 	int			 error;
304 
305 	cpuirq = arg;
306 	sc = (struct bcm_bmips_softc*)cpuirq->sc;
307 
308 	/* Fetch current interrupt state */
309 	sbstatus = bus_read_4(sc->cfg, SIBA_CFG0_FLAGST);
310 
311 	/* Fetch mask of interrupt vectors routed to this MIPS IRQ */
312 	mips_irq = cpuirq->mips_irq;
313 	if (mips_irq == 0) {
314 		sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
315 	} else {
316 		uint32_t ipsflag;
317 
318 		ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
319 
320 		/* Map to an intvec-compatible representation */
321 		switch (mips_irq) {
322 		case 1:
323 			sbintvec = (ipsflag & SIBA_IPS_INT1_MASK) >>
324 			    SIBA_IPS_INT1_SHIFT;
325 			break;
326 		case 2:
327 			sbintvec = (ipsflag & SIBA_IPS_INT2_MASK) >>
328 			    SIBA_IPS_INT2_SHIFT;
329 			break;
330 		case 3:
331 			sbintvec = (ipsflag & SIBA_IPS_INT3_MASK) >>
332 			    SIBA_IPS_INT3_SHIFT;
333 			break;
334 		case 4:
335 			sbintvec = (ipsflag & SIBA_IPS_INT4_MASK) >>
336 			    SIBA_IPS_INT4_SHIFT;
337 			break;
338 		default:
339 			panic("invalid irq %u", mips_irq);
340 		}
341 	}
342 
343 	/* Ignore interrupts not routed to this MIPS IRQ */
344 	sbstatus &= sbintvec;
345 
346 	/* Handle isrc_solo direct dispatch path */
347 	isrc_solo = cpuirq->isrc_solo;
348 	if (isrc_solo != NULL) {
349 		if (sbstatus & BCM_MIPS_IVEC_MASK(isrc_solo)) {
350 			error = intr_isrc_dispatch(&isrc_solo->isrc,
351 			    curthread->td_intr_frame);
352 			if (error) {
353 				device_printf(sc->dev, "Stray interrupt %u "
354 				    "detected\n", isrc_solo->ivec);
355 				bcm_bmips_pic_disable_intr(sc->dev,
356 				    &isrc_solo->isrc);
357 			}
358 		}
359 
360 		sbstatus &= ~(BCM_MIPS_IVEC_MASK(isrc_solo));
361 		if (sbstatus == 0)
362 			return (FILTER_HANDLED);
363 
364 		/* Report and mask additional stray interrupts */
365 		while ((i = fls(sbstatus)) != 0) {
366 			i--; /* Get a 0-offset interrupt. */
367 			sbstatus &= ~(1 << i);
368 
369 			device_printf(sc->dev, "Stray interrupt %u "
370 				"detected\n", i);
371 			bcm_bmips_mask_irq(sc, mips_irq, i);
372 		}
373 
374 		return (FILTER_HANDLED);
375 	}
376 
377 	/* Standard dispatch path  */
378 	while ((i = fls(sbstatus)) != 0) {
379 		i--; /* Get a 0-offset interrupt. */
380 		sbstatus &= ~(1 << i);
381 
382 		KASSERT(i < nitems(sc->bcm_mips.isrcs), ("invalid ivec %u", i));
383 
384 		error = intr_isrc_dispatch(&sc->bcm_mips.isrcs[i].isrc,
385 		    curthread->td_intr_frame);
386 		if (error) {
387 			device_printf(sc->dev, "Stray interrupt %u detected\n",
388 			    i);
389 			bcm_bmips_mask_irq(sc, mips_irq, i);
390 			continue;
391 		}
392 	}
393 
394 	return (FILTER_HANDLED);
395 }
396 
397 static device_method_t bcm_bmips_methods[] = {
398 	/* Device interface */
399 	DEVMETHOD(device_probe,		bcm_bmips_probe),
400 	DEVMETHOD(device_attach,	bcm_bmips_attach),
401 	DEVMETHOD(device_detach,	bcm_bmips_detach),
402 
403 	/* Interrupt controller interface */
404 	DEVMETHOD(pic_disable_intr,	bcm_bmips_pic_disable_intr),
405 	DEVMETHOD(pic_enable_intr,	bcm_bmips_pic_enable_intr),
406 	DEVMETHOD(pic_pre_ithread,	bcm_bmips_pic_pre_ithread),
407 	DEVMETHOD(pic_post_ithread,	bcm_bmips_pic_post_ithread),
408 	DEVMETHOD(pic_post_filter,	bcm_bmips_pic_post_filter),
409 
410 	DEVMETHOD_END
411 };
412 
413 static devclass_t bcm_mips_devclass;
414 
415 DEFINE_CLASS_1(bcm_mips, bcm_bmips_driver, bcm_bmips_methods, sizeof(struct bcm_bmips_softc), bcm_mips_driver);
416 EARLY_DRIVER_MODULE(bcm_bmips, bhnd, bcm_bmips_driver, bcm_mips_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
417 
418 MODULE_VERSION(bcm_bmips, 1);
419 MODULE_DEPEND(bcm_bmips, bhnd, 1, 1, 1);
420