]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/mips/broadcom/bcm_mips74k.c
Fix multiple vulnerabilities in sqlite3.
[FreeBSD/FreeBSD.git] / sys / mips / broadcom / bcm_mips74k.c
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);