1 /*-
2 * Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com>
3 * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
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
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
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
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
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
204 /* PIC_DISABLE_INTR() */
205 static void
206 bcm_mips74k_pic_disable_intr(device_t dev, struct intr_irqsrc *irqsrc)
207 {
208 struct bcm_mips74k_softc *sc;
209 struct bcm_mips_irqsrc *isrc;
210
211 sc = device_get_softc(dev);
212 isrc = (struct bcm_mips_irqsrc *)irqsrc;
213
214 KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
215
216 bcm_mips74k_mask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
217 }
218
219 /* PIC_ENABLE_INTR() */
220 static void
221 bcm_mips74k_pic_enable_intr(device_t dev, struct intr_irqsrc *irqsrc)
222 {
223 struct bcm_mips74k_softc *sc;
224 struct bcm_mips_irqsrc *isrc;
225
226 sc = device_get_softc(dev);
227 isrc = (struct bcm_mips_irqsrc *)irqsrc;
228
229 KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
230
231 bcm_mips74k_unmask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
232 }
233
234 /* PIC_PRE_ITHREAD() */
235 static void
236 bcm_mips74k_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
237 {
238 bcm_mips74k_pic_disable_intr(dev, isrc);
239 }
240
241 /* PIC_POST_ITHREAD() */
242 static void
243 bcm_mips74k_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
244 {
245 bcm_mips74k_pic_enable_intr(dev, isrc);
246 }
247
248 /* PIC_POST_FILTER() */
249 static void
250 bcm_mips74k_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
251 {
252 }
253
254 /**
255 * Disable routing of backplane interrupt vector @p ivec to MIPS IRQ
256 * @p mips_irq.
257 */
258 static void
259 bcm_mips74k_mask_irq(struct bcm_mips74k_softc *sc, u_int mips_irq, u_int ivec)
260 {
261 uint32_t oobsel;
262
263 KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
264 mips_irq));
265 KASSERT(mips_irq < BCM_MIPS74K_NUM_INTR, ("unsupported MIPS IRQ %u",
266 mips_irq));
267 KASSERT(ivec < BCMA_OOB_NUM_BUSLINES, ("invalid backplane ivec"));
268
269 oobsel = bus_read_4(sc->mem, BCM_MIPS74K_INTR_SEL(mips_irq));
270 oobsel &= ~(BCM_MIPS74K_INTR_SEL_FLAG(ivec));
271 bus_write_4(sc->mem, BCM_MIPS74K_INTR_SEL(mips_irq), oobsel);
272 }
273
274 /**
275 * Enable routing of an interrupt.
276 */
277 static void
278 bcm_mips74k_unmask_irq(struct bcm_mips74k_softc *sc, u_int mips_irq, u_int ivec)
279 {
280 uint32_t oobsel;
281
282 KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
283 mips_irq));
284 KASSERT(mips_irq < BCM_MIPS74K_NUM_INTR, ("unsupported MIPS IRQ %u",
285 mips_irq));
286 KASSERT(ivec < BCMA_OOB_NUM_BUSLINES, ("invalid backplane ivec"));
287
288 oobsel = bus_read_4(sc->mem, BCM_MIPS74K_INTR_SEL(mips_irq));
289 oobsel |= BCM_MIPS74K_INTR_SEL_FLAG(ivec);
290 bus_write_4(sc->mem, BCM_MIPS74K_INTR_SEL(mips_irq), oobsel);
291 }
292
293 /* our MIPS CPU interrupt filter */
294 static int
295 bcm_mips74k_pic_intr(void *arg)
296 {
297 struct bcm_mips74k_softc *sc;
298 struct bcm_mips_cpuirq *cpuirq;
299 struct bcm_mips_irqsrc *isrc_solo;
300 uint32_t oobsel, intr;
301 u_int i;
302 int error;
303
304 cpuirq = arg;
305 sc = (struct bcm_mips74k_softc*)cpuirq->sc;
306
307 /* Fetch current interrupt state */
308 intr = bus_read_4(sc->mem, BCM_MIPS74K_INTR_STATUS);
309
310 /* Fetch mask of interrupt vectors routed to this MIPS IRQ */
311 KASSERT(cpuirq->mips_irq < BCM_MIPS74K_NUM_INTR,
312 ("invalid irq %u", cpuirq->mips_irq));
313
314 oobsel = bus_read_4(sc->mem, BCM_MIPS74K_INTR_SEL(cpuirq->mips_irq));
315
316 /* Ignore interrupts not routed to this MIPS IRQ */
317 intr &= oobsel;
318
319 /* Handle isrc_solo direct dispatch path */
320 isrc_solo = cpuirq->isrc_solo;
321 if (isrc_solo != NULL) {
322 if (intr & BCM_MIPS_IVEC_MASK(isrc_solo)) {
323 error = intr_isrc_dispatch(&isrc_solo->isrc,
324 curthread->td_intr_frame);
325 if (error) {
326 device_printf(sc->dev, "Stray interrupt %u "
327 "detected\n", isrc_solo->ivec);
328 bcm_mips74k_pic_disable_intr(sc->dev,
329 &isrc_solo->isrc);
330 }
331 }
332
333 intr &= ~(BCM_MIPS_IVEC_MASK(isrc_solo));
334 if (intr == 0)
335 return (FILTER_HANDLED);
336
337 /* Report and mask additional stray interrupts */
338 while ((i = fls(intr)) != 0) {
339 i--; /* Get a 0-offset interrupt. */
340 intr &= ~(1 << i);
341
342 device_printf(sc->dev, "Stray interrupt %u "
343 "detected\n", i);
344 bcm_mips74k_mask_irq(sc, cpuirq->mips_irq, i);
345 }
346
347 return (FILTER_HANDLED);
348 }
349
350 /* Standard dispatch path */
351 while ((i = fls(intr)) != 0) {
352 i--; /* Get a 0-offset interrupt. */
353 intr &= ~(1 << i);
354
355 KASSERT(i < nitems(sc->bcm_mips.isrcs), ("invalid ivec %u", i));
356
357 error = intr_isrc_dispatch(&sc->bcm_mips.isrcs[i].isrc,
358 curthread->td_intr_frame);
359 if (error) {
360 device_printf(sc->dev, "Stray interrupt %u detected\n",
361 i);
362 bcm_mips74k_mask_irq(sc, cpuirq->mips_irq, i);
363 continue;
364 }
365 }
366
367 return (FILTER_HANDLED);
368 }
369
370 static device_method_t bcm_mips74k_methods[] = {
371 /* Device interface */
372 DEVMETHOD(device_probe, bcm_mips74k_probe),
373 DEVMETHOD(device_attach, bcm_mips74k_attach),
374 DEVMETHOD(device_detach, bcm_mips74k_detach),
375
376 /* Interrupt controller interface */
377 DEVMETHOD(pic_disable_intr, bcm_mips74k_pic_disable_intr),
378 DEVMETHOD(pic_enable_intr, bcm_mips74k_pic_enable_intr),
379 DEVMETHOD(pic_pre_ithread, bcm_mips74k_pic_pre_ithread),
380 DEVMETHOD(pic_post_ithread, bcm_mips74k_pic_post_ithread),
381 DEVMETHOD(pic_post_filter, bcm_mips74k_pic_post_filter),
382
383 DEVMETHOD_END
384 };
385
386 static devclass_t bcm_mips_devclass;
387
388 DEFINE_CLASS_1(bcm_mips, bcm_mips74k_driver, bcm_mips74k_methods, sizeof(struct bcm_mips_softc), bcm_mips_driver);
389 EARLY_DRIVER_MODULE(bcm_mips74k, bhnd, bcm_mips74k_driver, bcm_mips_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
390 SYSINIT(cpu_init, SI_SUB_CPU, SI_ORDER_FIRST, bcm_mips74k_timer_init, NULL);
391 MODULE_VERSION(bcm_mips74k, 1);
392 MODULE_DEPEND(bcm_mips74k, bhnd, 1, 1, 1);
Cache object: 4ba818f928f3d52df00eed28d9159f89
|