]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/mips/broadcom/bcm_bmips.c
zfs: merge openzfs/zfs@c4c162c1e (master) into main
[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 static int
107 bcm_bmips_attach(device_t dev)
108 {
109         struct bcm_bmips_softc  *sc;
110         int                      error;
111
112         sc = device_get_softc(dev);
113         sc->dev = dev;
114
115         /* Allocate our core's register block */
116         sc->mem_rid = 0;
117         sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
118             RF_ACTIVE);
119         if (sc->mem == NULL) {
120                 device_printf(dev, "failed to allocate cpu register block\n");
121                 error = ENXIO;
122                 goto failed;
123         }
124
125         /* Determine the resource ID for our siba CFG0 registers */
126         sc->cfg_rid = bhnd_get_port_rid(dev, BHND_PORT_AGENT, 0, 0);
127         if (sc->cfg_rid == -1) {
128                 device_printf(dev, "missing required cfg0 register block\n");
129                 error = ENXIO;
130                 goto failed;
131         }
132
133         /* Allocate our CFG0 register block */
134         sc->cfg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->cfg_rid,
135             RF_ACTIVE|RF_SHAREABLE);
136         if (sc->cfg == NULL) {
137                 device_printf(dev, "failed to allocate cfg0 register block\n");
138                 error = ENXIO;
139                 goto failed;
140         }
141
142         /* Clear interrupt map */
143         bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, 0x0);    /* MIPS IRQ0 */
144         bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, 0x0);   /* MIPS IRQ1-4 */
145
146         /* Initialize the generic BHND MIPS driver state */
147         error = bcm_mips_attach(dev, BCM_BMIPS_NCPU_IRQS, BCM_BMIPS_TIMER_IRQ,
148             bcm_bmips_pic_intr);
149         if (error)
150                 goto failed;
151
152         return (0);
153
154 failed:
155         if (sc->mem != NULL)
156                 bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
157
158         if (sc->cfg != NULL)
159                 bus_release_resource(dev, SYS_RES_MEMORY, sc->cfg_rid, sc->cfg);
160
161         return (error);
162 }
163
164 static int
165 bcm_bmips_detach(device_t dev)
166 {
167         struct bcm_bmips_softc  *sc;
168         int                      error;
169
170         sc = device_get_softc(dev);
171
172         if ((error = bcm_mips_detach(dev)))
173                 return (error);
174
175         bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
176         bus_release_resource(dev, SYS_RES_MEMORY, sc->cfg_rid, sc->cfg);
177
178         return (0);
179 }
180
181 /* PIC_DISABLE_INTR() */
182 static void
183 bcm_bmips_pic_disable_intr(device_t dev, struct intr_irqsrc *irqsrc)
184 {
185         struct bcm_bmips_softc  *sc;
186         struct bcm_mips_irqsrc  *isrc;
187
188         sc = device_get_softc(dev);
189         isrc = (struct bcm_mips_irqsrc *)irqsrc;
190
191         KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
192
193         bcm_bmips_mask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
194 }
195
196 /* PIC_ENABLE_INTR() */
197 static void
198 bcm_bmips_pic_enable_intr(device_t dev, struct intr_irqsrc *irqsrc)
199 {
200         struct bcm_bmips_softc  *sc;
201         struct bcm_mips_irqsrc  *isrc;
202
203         sc = device_get_softc(dev);
204         isrc = (struct bcm_mips_irqsrc *)irqsrc;
205
206         KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
207
208         bcm_bmips_unmask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
209 }
210
211 /* PIC_PRE_ITHREAD() */
212 static void
213 bcm_bmips_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
214 {
215         bcm_bmips_pic_disable_intr(dev, isrc);
216 }
217
218 /* PIC_POST_ITHREAD() */
219 static void
220 bcm_bmips_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
221 {
222         bcm_bmips_pic_enable_intr(dev, isrc);
223 }
224
225 /* PIC_POST_FILTER() */
226 static void
227 bcm_bmips_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
228 {
229 }
230
231 /**
232  * Disable routing of backplane interrupt vector @p ivec to MIPS IRQ
233  * @p mips_irq.
234  */
235 static void
236 bcm_bmips_mask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, u_int ivec)
237 {
238         KASSERT(ivec < SIBA_MAX_INTR, ("invalid sbflag# ivec"));
239         KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
240             mips_irq));
241
242         if (mips_irq == 0) {
243                 uint32_t sbintvec;
244
245                 sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
246                 sbintvec &= ~(1 << ivec);
247                 bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, sbintvec);
248         } else {
249                 uint32_t ipsflag;
250
251                 /* Can we route this via ipsflag? */
252                 KASSERT(((1 << ivec) & SIBA_IPS_INT1_MASK) != 0,
253                     ("cannot route high sbflag# ivec %u", ivec));
254
255                 ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
256                 ipsflag &= ~(
257                     ((1 << ivec) << SIBA_IPS_INT_SHIFT(mips_irq)) &
258                     SIBA_IPS_INT_MASK(mips_irq));
259                 bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, ipsflag);
260         }
261
262 }
263
264 /**
265  * Enable routing of an interrupt.
266  */
267 static void
268 bcm_bmips_unmask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, u_int ivec)
269 {
270         KASSERT(ivec < SIBA_MAX_INTR, ("invalid sbflag# ivec"));
271         KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
272             mips_irq));
273
274         if (mips_irq == 0) {
275                 uint32_t sbintvec;
276
277                 sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
278                 sbintvec |= (1 << ivec);
279                 bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, sbintvec);
280         } else {
281                 uint32_t ipsflag;
282
283                 /* Can we route this via ipsflag? */
284                 KASSERT(((1 << ivec) & SIBA_IPS_INT1_MASK) != 0,
285                     ("cannot route high sbflag# ivec %u", ivec));
286
287                 ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
288                 ipsflag |= (ivec << SIBA_IPS_INT_SHIFT(mips_irq)) &
289                     SIBA_IPS_INT_MASK(mips_irq);
290                 bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, ipsflag);
291         }
292 }
293
294 /* our MIPS CPU interrupt filter */
295 static int
296 bcm_bmips_pic_intr(void *arg)
297 {
298         struct bcm_bmips_softc  *sc;
299         struct bcm_mips_cpuirq  *cpuirq;
300         struct bcm_mips_irqsrc  *isrc_solo;
301         uint32_t                 sbintvec, sbstatus;
302         u_int                    mips_irq, i;
303         int                      error;
304
305         cpuirq = arg;
306         sc = (struct bcm_bmips_softc*)cpuirq->sc;
307
308         /* Fetch current interrupt state */
309         sbstatus = bus_read_4(sc->cfg, SIBA_CFG0_FLAGST);
310
311         /* Fetch mask of interrupt vectors routed to this MIPS IRQ */
312         mips_irq = cpuirq->mips_irq;
313         if (mips_irq == 0) {
314                 sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
315         } else {
316                 uint32_t ipsflag;
317
318                 ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
319
320                 /* Map to an intvec-compatible representation */
321                 switch (mips_irq) {
322                 case 1:
323                         sbintvec = (ipsflag & SIBA_IPS_INT1_MASK) >>
324                             SIBA_IPS_INT1_SHIFT;
325                         break;
326                 case 2:
327                         sbintvec = (ipsflag & SIBA_IPS_INT2_MASK) >>
328                             SIBA_IPS_INT2_SHIFT;
329                         break;
330                 case 3:
331                         sbintvec = (ipsflag & SIBA_IPS_INT3_MASK) >>
332                             SIBA_IPS_INT3_SHIFT;
333                         break;
334                 case 4: 
335                         sbintvec = (ipsflag & SIBA_IPS_INT4_MASK) >>
336                             SIBA_IPS_INT4_SHIFT;
337                         break;
338                 default:
339                         panic("invalid irq %u", mips_irq);
340                 }
341         }
342
343         /* Ignore interrupts not routed to this MIPS IRQ */
344         sbstatus &= sbintvec;
345
346         /* Handle isrc_solo direct dispatch path */
347         isrc_solo = cpuirq->isrc_solo;
348         if (isrc_solo != NULL) {
349                 if (sbstatus & BCM_MIPS_IVEC_MASK(isrc_solo)) {
350                         error = intr_isrc_dispatch(&isrc_solo->isrc,
351                             curthread->td_intr_frame);
352                         if (error) {
353                                 device_printf(sc->dev, "Stray interrupt %u "
354                                     "detected\n", isrc_solo->ivec);
355                                 bcm_bmips_pic_disable_intr(sc->dev,
356                                     &isrc_solo->isrc);
357                         }
358                 }
359
360                 sbstatus &= ~(BCM_MIPS_IVEC_MASK(isrc_solo));
361                 if (sbstatus == 0)
362                         return (FILTER_HANDLED);
363
364                 /* Report and mask additional stray interrupts */
365                 while ((i = fls(sbstatus)) != 0) {
366                         i--; /* Get a 0-offset interrupt. */
367                         sbstatus &= ~(1 << i);
368
369                         device_printf(sc->dev, "Stray interrupt %u "
370                                 "detected\n", i);
371                         bcm_bmips_mask_irq(sc, mips_irq, i);
372                 }
373
374                 return (FILTER_HANDLED);
375         }
376
377         /* Standard dispatch path  */
378         while ((i = fls(sbstatus)) != 0) {
379                 i--; /* Get a 0-offset interrupt. */
380                 sbstatus &= ~(1 << i);
381
382                 KASSERT(i < nitems(sc->bcm_mips.isrcs), ("invalid ivec %u", i));
383
384                 error = intr_isrc_dispatch(&sc->bcm_mips.isrcs[i].isrc,
385                     curthread->td_intr_frame);
386                 if (error) {
387                         device_printf(sc->dev, "Stray interrupt %u detected\n",
388                             i);
389                         bcm_bmips_mask_irq(sc, mips_irq, i);
390                         continue;
391                 }
392         }
393
394         return (FILTER_HANDLED);
395 }
396
397 static device_method_t bcm_bmips_methods[] = {
398         /* Device interface */
399         DEVMETHOD(device_probe,         bcm_bmips_probe),
400         DEVMETHOD(device_attach,        bcm_bmips_attach),
401         DEVMETHOD(device_detach,        bcm_bmips_detach),
402
403         /* Interrupt controller interface */
404         DEVMETHOD(pic_disable_intr,     bcm_bmips_pic_disable_intr),
405         DEVMETHOD(pic_enable_intr,      bcm_bmips_pic_enable_intr),
406         DEVMETHOD(pic_pre_ithread,      bcm_bmips_pic_pre_ithread),
407         DEVMETHOD(pic_post_ithread,     bcm_bmips_pic_post_ithread),
408         DEVMETHOD(pic_post_filter,      bcm_bmips_pic_post_filter),
409
410         DEVMETHOD_END
411 };
412
413 static devclass_t bcm_mips_devclass;
414
415 DEFINE_CLASS_1(bcm_mips, bcm_bmips_driver, bcm_bmips_methods, sizeof(struct bcm_bmips_softc), bcm_mips_driver);
416 EARLY_DRIVER_MODULE(bcm_bmips, bhnd, bcm_bmips_driver, bcm_mips_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
417
418 MODULE_VERSION(bcm_bmips, 1);
419 MODULE_DEPEND(bcm_bmips, bhnd, 1, 1, 1);