xref: /f-stack/freebsd/mips/broadcom/bcm_mips74k.c (revision 22ce4aff)
1 /*-
2  * Copyright (c) 2016 Michael Zhilin <[email protected]>
3  * Copyright (c) 2016 Landon Fuller <[email protected]>
4  * Copyright (c) 2017 The FreeBSD Foundation
5  * All rights reserved.
6  *
7  * Portions of this software were developed by Landon Fuller
8  * under sponsorship from the FreeBSD Foundation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer,
15  *    without modification.
16  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
17  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
18  *    redistribution must be conditioned upon including a substantially
19  *    similar Disclaimer requirement for further binary redistribution.
20  *
21  * NO WARRANTY
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
25  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
26  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
27  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
30  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32  * THE POSSIBILITY OF SUCH DAMAGES.
33  */
34 
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37 
38 #include <sys/param.h>
39 #include <sys/kernel.h>
40 #include <sys/bus.h>
41 #include <sys/module.h>
42 #include <sys/proc.h>
43 
44 #include <machine/bus.h>
45 #include <sys/rman.h>
46 
47 #include <machine/cpufunc.h>
48 #include <machine/intr.h>
49 #include <machine/resource.h>
50 
51 #include <dev/bhnd/bhnd.h>
52 #include <dev/bhnd/bcma/bcma_dmp.h>
53 
54 #include "pic_if.h"
55 
56 #include "bcm_machdep.h"
57 
58 #include "bcm_mipsvar.h"
59 #include "bcm_mips74kreg.h"
60 
61 /*
62  * Broadcom MIPS74K Core
63  *
64  * These cores are only found on bcma(4) chipsets.
65  */
66 
67 struct bcm_mips74k_softc;
68 
69 static int	bcm_mips74k_pic_intr(void *arg);
70 static void	bcm_mips74k_mask_irq(struct bcm_mips74k_softc *sc,
71 		    u_int mips_irq, u_int ivec);
72 static void	bcm_mips74k_unmask_irq(struct bcm_mips74k_softc *sc,
73 		    u_int mips_irq, u_int ivec);
74 
75 static const struct bhnd_device bcm_mips74k_devs[] = {
76 	BHND_DEVICE(MIPS, MIPS74K, NULL, NULL, BHND_DF_SOC),
77 	BHND_DEVICE_END
78 };
79 
80 struct bcm_mips74k_softc {
81 	struct bcm_mips_softc	 bcm_mips;	/**< parent softc */
82 	device_t		 dev;
83 	struct resource		*mem;		/**< cpu core registers */
84 	int			 mem_rid;
85 };
86 
87 /* Early routing of the CPU timer interrupt is required */
88 static void
bcm_mips74k_timer_init(void * unused)89 bcm_mips74k_timer_init(void *unused)
90 {
91 	struct bcm_platform	*bp;
92 	u_int			 irq;
93 	uint32_t		 mask;
94 
95 	bp = bcm_get_platform();
96 
97 	/* Must be a MIPS74K core attached to a BCMA interconnect */
98 	if (!bhnd_core_matches(&bp->cpu_id, &(struct bhnd_core_match) {
99 		BHND_MATCH_CORE(BHND_MFGID_MIPS, BHND_COREID_MIPS74K)
100 	})) {
101 		if (bootverbose) {
102 			BCM_ERR("not a MIPS74K core: %s %s\n",
103 			    bhnd_vendor_name(bp->cpu_id.vendor),
104 			    bhnd_core_name(&bp->cpu_id));
105 		}
106 
107 		return;
108 	}
109 
110 	if (!BHND_CHIPTYPE_IS_BCMA_COMPATIBLE(bp->cid.chip_type)) {
111 		if (bootverbose)
112 			BCM_ERR("not a BCMA device\n");
113 		return;
114 	}
115 
116 	/* Route the timer bus ivec to the CPU's timer IRQ, and disable any
117 	 * other vectors assigned to the IRQ. */
118 	irq = BCM_MIPS74K_GET_TIMER_IRQ();
119 	mask = BCM_MIPS74K_INTR_SEL_FLAG(BCM_MIPS74K_TIMER_IVEC);
120 
121 	BCM_CPU_WRITE_4(bp, BCM_MIPS74K_INTR_SEL(irq), mask);
122 }
123 
124 static int
bcm_mips74k_probe(device_t dev)125 bcm_mips74k_probe(device_t dev)
126 {
127 	const struct bhnd_device	*id;
128 	const struct bhnd_chipid	*cid;
129 
130 	id = bhnd_device_lookup(dev, bcm_mips74k_devs,
131 	    sizeof(bcm_mips74k_devs[0]));
132 	if (id == NULL)
133 		return (ENXIO);
134 
135 	/* Check the chip type; the MIPS74K core should only be found
136 	 * on bcma(4) chipsets (and we rely on bcma OOB interrupt
137 	 * routing). */
138 	cid = bhnd_get_chipid(dev);
139 	if (!BHND_CHIPTYPE_IS_BCMA_COMPATIBLE(cid->chip_type))
140 		return (ENXIO);
141 
142 	bhnd_set_default_core_desc(dev);
143 	return (BUS_PROBE_DEFAULT);
144 }
145 
146 static int
bcm_mips74k_attach(device_t dev)147 bcm_mips74k_attach(device_t dev)
148 {
149 	struct bcm_mips74k_softc	*sc;
150 	u_int				 timer_irq;
151 	int				 error;
152 
153 	sc = device_get_softc(dev);
154 	sc->dev = dev;
155 
156 	/* Allocate our core's register block */
157 	sc->mem_rid = 0;
158 	sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
159 	    RF_ACTIVE);
160 	if (sc->mem == NULL) {
161 		device_printf(dev, "failed to allocate cpu register block\n");
162 		return (ENXIO);
163 	}
164 
165 	/* Clear interrupt map */
166 	timer_irq = BCM_MIPS74K_GET_TIMER_IRQ();
167 	for (size_t i = 0; i < BCM_MIPS74K_NUM_INTR; i++) {
168 		/* We don't use the timer IRQ; leave it routed to the
169 		 * MIPS CPU */
170 		if (i == timer_irq)
171 			continue;
172 
173 		bus_write_4(sc->mem, BCM_MIPS74K_INTR_SEL(i), 0);
174 	}
175 
176 	/* Initialize the generic BHND MIPS driver state */
177 	error = bcm_mips_attach(dev, BCM_MIPS74K_NUM_INTR, timer_irq,
178 	    bcm_mips74k_pic_intr);
179 	if (error) {
180 		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
181 		return (error);
182 	}
183 
184 	return (0);
185 }
186 
187 static int
bcm_mips74k_detach(device_t dev)188 bcm_mips74k_detach(device_t dev)
189 {
190 	struct bcm_mips74k_softc	*sc;
191 	int				 error;
192 
193 	sc = device_get_softc(dev);
194 
195 	if ((error = bcm_mips_detach(dev)))
196 		return (error);
197 
198 	bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
199 
200 	return (0);
201 }
202 
203 /* PIC_DISABLE_INTR() */
204 static void
bcm_mips74k_pic_disable_intr(device_t dev,struct intr_irqsrc * irqsrc)205 bcm_mips74k_pic_disable_intr(device_t dev, struct intr_irqsrc *irqsrc)
206 {
207 	struct bcm_mips74k_softc	*sc;
208 	struct bcm_mips_irqsrc		*isrc;
209 
210 	sc = device_get_softc(dev);
211 	isrc = (struct bcm_mips_irqsrc *)irqsrc;
212 
213 	KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
214 
215 	bcm_mips74k_mask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
216 }
217 
218 /* PIC_ENABLE_INTR() */
219 static void
bcm_mips74k_pic_enable_intr(device_t dev,struct intr_irqsrc * irqsrc)220 bcm_mips74k_pic_enable_intr(device_t dev, struct intr_irqsrc *irqsrc)
221 {
222 	struct bcm_mips74k_softc	*sc;
223 	struct bcm_mips_irqsrc		*isrc;
224 
225 	sc = device_get_softc(dev);
226 	isrc = (struct bcm_mips_irqsrc *)irqsrc;
227 
228 	KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
229 
230 	bcm_mips74k_unmask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
231 }
232 
233 /* PIC_PRE_ITHREAD() */
234 static void
bcm_mips74k_pic_pre_ithread(device_t dev,struct intr_irqsrc * isrc)235 bcm_mips74k_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
236 {
237 	bcm_mips74k_pic_disable_intr(dev, isrc);
238 }
239 
240 /* PIC_POST_ITHREAD() */
241 static void
bcm_mips74k_pic_post_ithread(device_t dev,struct intr_irqsrc * isrc)242 bcm_mips74k_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
243 {
244 	bcm_mips74k_pic_enable_intr(dev, isrc);
245 }
246 
247 /* PIC_POST_FILTER() */
248 static void
bcm_mips74k_pic_post_filter(device_t dev,struct intr_irqsrc * isrc)249 bcm_mips74k_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
250 {
251 }
252 
253 /**
254  * Disable routing of backplane interrupt vector @p ivec to MIPS IRQ
255  * @p mips_irq.
256  */
257 static void
bcm_mips74k_mask_irq(struct bcm_mips74k_softc * sc,u_int mips_irq,u_int ivec)258 bcm_mips74k_mask_irq(struct bcm_mips74k_softc *sc, u_int mips_irq, u_int ivec)
259 {
260 	uint32_t oobsel;
261 
262 	KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
263 	    mips_irq));
264 	KASSERT(mips_irq < BCM_MIPS74K_NUM_INTR, ("unsupported MIPS IRQ %u",
265 	    mips_irq));
266 	KASSERT(ivec < BCMA_OOB_NUM_BUSLINES, ("invalid backplane ivec"));
267 
268 	oobsel = bus_read_4(sc->mem, BCM_MIPS74K_INTR_SEL(mips_irq));
269 	oobsel &= ~(BCM_MIPS74K_INTR_SEL_FLAG(ivec));
270 	bus_write_4(sc->mem, BCM_MIPS74K_INTR_SEL(mips_irq), oobsel);
271 }
272 
273 /**
274  * Enable routing of an interrupt.
275  */
276 static void
bcm_mips74k_unmask_irq(struct bcm_mips74k_softc * sc,u_int mips_irq,u_int ivec)277 bcm_mips74k_unmask_irq(struct bcm_mips74k_softc *sc, u_int mips_irq, u_int ivec)
278 {
279 	uint32_t oobsel;
280 
281 	KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
282 	    mips_irq));
283 	KASSERT(mips_irq < BCM_MIPS74K_NUM_INTR, ("unsupported MIPS IRQ %u",
284 	    mips_irq));
285 	KASSERT(ivec < BCMA_OOB_NUM_BUSLINES, ("invalid backplane ivec"));
286 
287 	oobsel = bus_read_4(sc->mem, BCM_MIPS74K_INTR_SEL(mips_irq));
288 	oobsel |= BCM_MIPS74K_INTR_SEL_FLAG(ivec);
289 	bus_write_4(sc->mem, BCM_MIPS74K_INTR_SEL(mips_irq), oobsel);
290 }
291 
292 /* our MIPS CPU interrupt filter */
293 static int
bcm_mips74k_pic_intr(void * arg)294 bcm_mips74k_pic_intr(void *arg)
295 {
296 	struct bcm_mips74k_softc	*sc;
297 	struct bcm_mips_cpuirq		*cpuirq;
298 	struct bcm_mips_irqsrc		*isrc_solo;
299 	uint32_t			 oobsel, intr;
300 	u_int				 i;
301 	int				 error;
302 
303 	cpuirq = arg;
304 	sc = (struct bcm_mips74k_softc*)cpuirq->sc;
305 
306 	/* Fetch current interrupt state */
307 	intr = bus_read_4(sc->mem, BCM_MIPS74K_INTR_STATUS);
308 
309 	/* Fetch mask of interrupt vectors routed to this MIPS IRQ */
310 	KASSERT(cpuirq->mips_irq < BCM_MIPS74K_NUM_INTR,
311 	    ("invalid irq %u", cpuirq->mips_irq));
312 
313 	oobsel = bus_read_4(sc->mem, BCM_MIPS74K_INTR_SEL(cpuirq->mips_irq));
314 
315 	/* Ignore interrupts not routed to this MIPS IRQ */
316 	intr &= oobsel;
317 
318 	/* Handle isrc_solo direct dispatch path */
319 	isrc_solo = cpuirq->isrc_solo;
320 	if (isrc_solo != NULL) {
321 		if (intr & BCM_MIPS_IVEC_MASK(isrc_solo)) {
322 			error = intr_isrc_dispatch(&isrc_solo->isrc,
323 			    curthread->td_intr_frame);
324 			if (error) {
325 				device_printf(sc->dev, "Stray interrupt %u "
326 				    "detected\n", isrc_solo->ivec);
327 				bcm_mips74k_pic_disable_intr(sc->dev,
328 				    &isrc_solo->isrc);
329 			}
330 		}
331 
332 		intr &= ~(BCM_MIPS_IVEC_MASK(isrc_solo));
333 		if (intr == 0)
334 			return (FILTER_HANDLED);
335 
336 		/* Report and mask additional stray interrupts */
337 		while ((i = fls(intr)) != 0) {
338 			i--; /* Get a 0-offset interrupt. */
339 			intr &= ~(1 << i);
340 
341 			device_printf(sc->dev, "Stray interrupt %u "
342 				"detected\n", i);
343 			bcm_mips74k_mask_irq(sc, cpuirq->mips_irq, i);
344 		}
345 
346 		return (FILTER_HANDLED);
347 	}
348 
349 	/* Standard dispatch path  */
350 	while ((i = fls(intr)) != 0) {
351 		i--; /* Get a 0-offset interrupt. */
352 		intr &= ~(1 << i);
353 
354 		KASSERT(i < nitems(sc->bcm_mips.isrcs), ("invalid ivec %u", i));
355 
356 		error = intr_isrc_dispatch(&sc->bcm_mips.isrcs[i].isrc,
357 		    curthread->td_intr_frame);
358 		if (error) {
359 			device_printf(sc->dev, "Stray interrupt %u detected\n",
360 			    i);
361 			bcm_mips74k_mask_irq(sc, cpuirq->mips_irq, i);
362 			continue;
363 		}
364 	}
365 
366 	return (FILTER_HANDLED);
367 }
368 
369 static device_method_t bcm_mips74k_methods[] = {
370 	/* Device interface */
371 	DEVMETHOD(device_probe,		bcm_mips74k_probe),
372 	DEVMETHOD(device_attach,	bcm_mips74k_attach),
373 	DEVMETHOD(device_detach,	bcm_mips74k_detach),
374 
375 	/* Interrupt controller interface */
376 	DEVMETHOD(pic_disable_intr,	bcm_mips74k_pic_disable_intr),
377 	DEVMETHOD(pic_enable_intr,	bcm_mips74k_pic_enable_intr),
378 	DEVMETHOD(pic_pre_ithread,	bcm_mips74k_pic_pre_ithread),
379 	DEVMETHOD(pic_post_ithread,	bcm_mips74k_pic_post_ithread),
380 	DEVMETHOD(pic_post_filter,	bcm_mips74k_pic_post_filter),
381 
382 	DEVMETHOD_END
383 };
384 
385 static devclass_t bcm_mips_devclass;
386 
387 DEFINE_CLASS_1(bcm_mips, bcm_mips74k_driver, bcm_mips74k_methods, sizeof(struct bcm_mips_softc), bcm_mips_driver);
388 EARLY_DRIVER_MODULE(bcm_mips74k, bhnd, bcm_mips74k_driver, bcm_mips_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
389 SYSINIT(cpu_init, SI_SUB_CPU, SI_ORDER_FIRST, bcm_mips74k_timer_init, NULL);
390 MODULE_VERSION(bcm_mips74k, 1);
391 MODULE_DEPEND(bcm_mips74k, bhnd, 1, 1, 1);
392