1 /*-
2  * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
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: stable/12/sys/mips/broadcom/bcm_bmips.c 326079 2017-11-21 23:15:20Z landonf $");
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 
107 static int
bcm_bmips_attach(device_t dev)108 bcm_bmips_attach(device_t dev)
109 {
110 	struct bcm_bmips_softc	*sc;
111 	int			 error;
112 
113 	sc = device_get_softc(dev);
114 	sc->dev = dev;
115 
116 	/* Allocate our core's register block */
117 	sc->mem_rid = 0;
118 	sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
119 	    RF_ACTIVE);
120 	if (sc->mem == NULL) {
121 		device_printf(dev, "failed to allocate cpu register block\n");
122 		error = ENXIO;
123 		goto failed;
124 	}
125 
126 	/* Determine the resource ID for our siba CFG0 registers */
127 	sc->cfg_rid = bhnd_get_port_rid(dev, BHND_PORT_AGENT, 0, 0);
128 	if (sc->cfg_rid == -1) {
129 		device_printf(dev, "missing required cfg0 register block\n");
130 		error = ENXIO;
131 		goto failed;
132 	}
133 
134 	/* Allocate our CFG0 register block */
135 	sc->cfg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->cfg_rid,
136 	    RF_ACTIVE|RF_SHAREABLE);
137 	if (sc->cfg == NULL) {
138 		device_printf(dev, "failed to allocate cfg0 register block\n");
139 		error = ENXIO;
140 		goto failed;
141 	}
142 
143 	/* Clear interrupt map */
144 	bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, 0x0);	/* MIPS IRQ0 */
145 	bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, 0x0);	/* MIPS IRQ1-4 */
146 
147 	/* Initialize the generic BHND MIPS driver state */
148 	error = bcm_mips_attach(dev, BCM_BMIPS_NCPU_IRQS, BCM_BMIPS_TIMER_IRQ,
149 	    bcm_bmips_pic_intr);
150 	if (error)
151 		goto failed;
152 
153 	return (0);
154 
155 failed:
156 	if (sc->mem != NULL)
157 		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
158 
159 	if (sc->cfg != NULL)
160 		bus_release_resource(dev, SYS_RES_MEMORY, sc->cfg_rid, sc->cfg);
161 
162 	return (error);
163 }
164 
165 static int
bcm_bmips_detach(device_t dev)166 bcm_bmips_detach(device_t dev)
167 {
168 	struct bcm_bmips_softc	*sc;
169 	int			 error;
170 
171 	sc = device_get_softc(dev);
172 
173 	if ((error = bcm_mips_detach(dev)))
174 		return (error);
175 
176 	bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
177 	bus_release_resource(dev, SYS_RES_MEMORY, sc->cfg_rid, sc->cfg);
178 
179 	return (0);
180 }
181 
182 /* PIC_DISABLE_INTR() */
183 static void
bcm_bmips_pic_disable_intr(device_t dev,struct intr_irqsrc * irqsrc)184 bcm_bmips_pic_disable_intr(device_t dev, struct intr_irqsrc *irqsrc)
185 {
186 	struct bcm_bmips_softc	*sc;
187 	struct bcm_mips_irqsrc	*isrc;
188 
189 	sc = device_get_softc(dev);
190 	isrc = (struct bcm_mips_irqsrc *)irqsrc;
191 
192 	KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
193 
194 	bcm_bmips_mask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
195 }
196 
197 /* PIC_ENABLE_INTR() */
198 static void
bcm_bmips_pic_enable_intr(device_t dev,struct intr_irqsrc * irqsrc)199 bcm_bmips_pic_enable_intr(device_t dev, struct intr_irqsrc *irqsrc)
200 {
201 	struct bcm_bmips_softc	*sc;
202 	struct bcm_mips_irqsrc	*isrc;
203 
204 	sc = device_get_softc(dev);
205 	isrc = (struct bcm_mips_irqsrc *)irqsrc;
206 
207 	KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
208 
209 	bcm_bmips_unmask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
210 }
211 
212 /* PIC_PRE_ITHREAD() */
213 static void
bcm_bmips_pic_pre_ithread(device_t dev,struct intr_irqsrc * isrc)214 bcm_bmips_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
215 {
216 	bcm_bmips_pic_disable_intr(dev, isrc);
217 }
218 
219 /* PIC_POST_ITHREAD() */
220 static void
bcm_bmips_pic_post_ithread(device_t dev,struct intr_irqsrc * isrc)221 bcm_bmips_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
222 {
223 	bcm_bmips_pic_enable_intr(dev, isrc);
224 }
225 
226 /* PIC_POST_FILTER() */
227 static void
bcm_bmips_pic_post_filter(device_t dev,struct intr_irqsrc * isrc)228 bcm_bmips_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
229 {
230 }
231 
232 /**
233  * Disable routing of backplane interrupt vector @p ivec to MIPS IRQ
234  * @p mips_irq.
235  */
236 static void
bcm_bmips_mask_irq(struct bcm_bmips_softc * sc,u_int mips_irq,u_int ivec)237 bcm_bmips_mask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, u_int ivec)
238 {
239 	KASSERT(ivec < SIBA_MAX_INTR, ("invalid sbflag# ivec"));
240 	KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
241 	    mips_irq));
242 
243 	if (mips_irq == 0) {
244 		uint32_t sbintvec;
245 
246 		sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
247 		sbintvec &= ~(1 << ivec);
248 		bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, sbintvec);
249 	} else {
250 		uint32_t ipsflag;
251 
252 		/* Can we route this via ipsflag? */
253 		KASSERT(((1 << ivec) & SIBA_IPS_INT1_MASK) != 0,
254 		    ("cannot route high sbflag# ivec %u", ivec));
255 
256 		ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
257 		ipsflag &= ~(
258 		    ((1 << ivec) << SIBA_IPS_INT_SHIFT(mips_irq)) &
259 		    SIBA_IPS_INT_MASK(mips_irq));
260 		bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, ipsflag);
261 	}
262 
263 }
264 
265 /**
266  * Enable routing of an interrupt.
267  */
268 static void
bcm_bmips_unmask_irq(struct bcm_bmips_softc * sc,u_int mips_irq,u_int ivec)269 bcm_bmips_unmask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, u_int ivec)
270 {
271 	KASSERT(ivec < SIBA_MAX_INTR, ("invalid sbflag# ivec"));
272 	KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
273 	    mips_irq));
274 
275 	if (mips_irq == 0) {
276 		uint32_t sbintvec;
277 
278 		sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
279 		sbintvec |= (1 << ivec);
280 		bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, sbintvec);
281 	} else {
282 		uint32_t ipsflag;
283 
284 		/* Can we route this via ipsflag? */
285 		KASSERT(((1 << ivec) & SIBA_IPS_INT1_MASK) != 0,
286 		    ("cannot route high sbflag# ivec %u", ivec));
287 
288 		ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
289 		ipsflag |= (ivec << SIBA_IPS_INT_SHIFT(mips_irq)) &
290 		    SIBA_IPS_INT_MASK(mips_irq);
291 		bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, ipsflag);
292 	}
293 }
294 
295 /* our MIPS CPU interrupt filter */
296 static int
bcm_bmips_pic_intr(void * arg)297 bcm_bmips_pic_intr(void *arg)
298 {
299 	struct bcm_bmips_softc	*sc;
300 	struct bcm_mips_cpuirq	*cpuirq;
301 	struct bcm_mips_irqsrc	*isrc_solo;
302 	uint32_t		 sbintvec, sbstatus;
303 	u_int			 mips_irq, i;
304 	int			 error;
305 
306 	cpuirq = arg;
307 	sc = (struct bcm_bmips_softc*)cpuirq->sc;
308 
309 	/* Fetch current interrupt state */
310 	sbstatus = bus_read_4(sc->cfg, SIBA_CFG0_FLAGST);
311 
312 	/* Fetch mask of interrupt vectors routed to this MIPS IRQ */
313 	mips_irq = cpuirq->mips_irq;
314 	if (mips_irq == 0) {
315 		sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
316 	} else {
317 		uint32_t ipsflag;
318 
319 		ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
320 
321 		/* Map to an intvec-compatible representation */
322 		switch (mips_irq) {
323 		case 1:
324 			sbintvec = (ipsflag & SIBA_IPS_INT1_MASK) >>
325 			    SIBA_IPS_INT1_SHIFT;
326 			break;
327 		case 2:
328 			sbintvec = (ipsflag & SIBA_IPS_INT2_MASK) >>
329 			    SIBA_IPS_INT2_SHIFT;
330 			break;
331 		case 3:
332 			sbintvec = (ipsflag & SIBA_IPS_INT3_MASK) >>
333 			    SIBA_IPS_INT3_SHIFT;
334 			break;
335 		case 4:
336 			sbintvec = (ipsflag & SIBA_IPS_INT4_MASK) >>
337 			    SIBA_IPS_INT4_SHIFT;
338 			break;
339 		default:
340 			panic("invalid irq %u", mips_irq);
341 		}
342 	}
343 
344 	/* Ignore interrupts not routed to this MIPS IRQ */
345 	sbstatus &= sbintvec;
346 
347 	/* Handle isrc_solo direct dispatch path */
348 	isrc_solo = cpuirq->isrc_solo;
349 	if (isrc_solo != NULL) {
350 		if (sbstatus & BCM_MIPS_IVEC_MASK(isrc_solo)) {
351 			error = intr_isrc_dispatch(&isrc_solo->isrc,
352 			    curthread->td_intr_frame);
353 			if (error) {
354 				device_printf(sc->dev, "Stray interrupt %u "
355 				    "detected\n", isrc_solo->ivec);
356 				bcm_bmips_pic_disable_intr(sc->dev,
357 				    &isrc_solo->isrc);
358 			}
359 		}
360 
361 		sbstatus &= ~(BCM_MIPS_IVEC_MASK(isrc_solo));
362 		if (sbstatus == 0)
363 			return (FILTER_HANDLED);
364 
365 		/* Report and mask additional stray interrupts */
366 		while ((i = fls(sbstatus)) != 0) {
367 			i--; /* Get a 0-offset interrupt. */
368 			sbstatus &= ~(1 << i);
369 
370 			device_printf(sc->dev, "Stray interrupt %u "
371 				"detected\n", i);
372 			bcm_bmips_mask_irq(sc, mips_irq, i);
373 		}
374 
375 		return (FILTER_HANDLED);
376 	}
377 
378 	/* Standard dispatch path  */
379 	while ((i = fls(sbstatus)) != 0) {
380 		i--; /* Get a 0-offset interrupt. */
381 		sbstatus &= ~(1 << i);
382 
383 		KASSERT(i < nitems(sc->bcm_mips.isrcs), ("invalid ivec %u", i));
384 
385 		error = intr_isrc_dispatch(&sc->bcm_mips.isrcs[i].isrc,
386 		    curthread->td_intr_frame);
387 		if (error) {
388 			device_printf(sc->dev, "Stray interrupt %u detected\n",
389 			    i);
390 			bcm_bmips_mask_irq(sc, mips_irq, i);
391 			continue;
392 		}
393 	}
394 
395 	return (FILTER_HANDLED);
396 }
397 
398 static device_method_t bcm_bmips_methods[] = {
399 	/* Device interface */
400 	DEVMETHOD(device_probe,		bcm_bmips_probe),
401 	DEVMETHOD(device_attach,	bcm_bmips_attach),
402 	DEVMETHOD(device_detach,	bcm_bmips_detach),
403 
404 	/* Interrupt controller interface */
405 	DEVMETHOD(pic_disable_intr,	bcm_bmips_pic_disable_intr),
406 	DEVMETHOD(pic_enable_intr,	bcm_bmips_pic_enable_intr),
407 	DEVMETHOD(pic_pre_ithread,	bcm_bmips_pic_pre_ithread),
408 	DEVMETHOD(pic_post_ithread,	bcm_bmips_pic_post_ithread),
409 	DEVMETHOD(pic_post_filter,	bcm_bmips_pic_post_filter),
410 
411 	DEVMETHOD_END
412 };
413 
414 static devclass_t bcm_mips_devclass;
415 
416 DEFINE_CLASS_1(bcm_mips, bcm_bmips_driver, bcm_bmips_methods, sizeof(struct bcm_bmips_softc), bcm_mips_driver);
417 EARLY_DRIVER_MODULE(bcm_bmips, bhnd, bcm_bmips_driver, bcm_mips_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
418 
419 MODULE_VERSION(bcm_bmips, 1);
420 MODULE_DEPEND(bcm_bmips, bhnd, 1, 1, 1);
421