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 67struct bcm_mips74k_softc; 68 69static int bcm_mips74k_pic_intr(void *arg); 70static void bcm_mips74k_mask_irq(struct bcm_mips74k_softc *sc, 71 u_int mips_irq, u_int ivec); 72static void bcm_mips74k_unmask_irq(struct bcm_mips74k_softc *sc, 73 u_int mips_irq, u_int ivec); 74 75static const struct bhnd_device bcm_mips74k_devs[] = { 76 BHND_DEVICE(MIPS, MIPS74K, NULL, NULL, BHND_DF_SOC), 77 BHND_DEVICE_END 78}; 79 80struct 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 */ 88static void 89bcm_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 124static int 125bcm_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 146static int 147bcm_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 187static int 188bcm_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() */ 205static void 206bcm_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() */ 220static void 221bcm_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() */ 235static void 236bcm_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() */ 242static void 243bcm_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() */ 249static void 250bcm_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 */ 258static void 259bcm_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 */ 277static void 278bcm_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 */ 294static int 295bcm_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 370static 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 386static devclass_t bcm_mips_devclass; 387 388DEFINE_CLASS_1(bcm_mips, bcm_mips74k_driver, bcm_mips74k_methods, sizeof(struct bcm_mips_softc), bcm_mips_driver); 389EARLY_DRIVER_MODULE(bcm_mips74k, bhnd, bcm_mips74k_driver, bcm_mips_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 390SYSINIT(cpu_init, SI_SUB_CPU, SI_ORDER_FIRST, bcm_mips74k_timer_init, NULL); 391MODULE_VERSION(bcm_mips74k, 1); 392MODULE_DEPEND(bcm_mips74k, bhnd, 1, 1, 1); 393