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: releng/12.0/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
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
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
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
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
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
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
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
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
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
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
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);
Cache object: 107aabbbf878b99752d991a88b3ed8f4
|