]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/mips/broadcom/bcm_bmips.c
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / sys / mips / broadcom / bcm_bmips.c
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$");
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);