1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2018 Ruslan Bukin <br@bsdpad.com> 5 * All rights reserved. 6 * Copyright (c) 2019 Mitchell Horne <mhorne@FreeBSD.org> 7 * 8 * Portions of this software were developed by SRI International and the 9 * University of Cambridge Computer Laboratory (Department of Computer Science 10 * and Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of 11 * the DARPA SSITH research programme. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/bus.h> 39 #include <sys/kernel.h> 40 #include <sys/ktr.h> 41 #include <sys/module.h> 42 #include <sys/proc.h> 43 #include <sys/rman.h> 44 #include <sys/smp.h> 45 46 #include <machine/bus.h> 47 #include <machine/intr.h> 48 49 #include <dev/ofw/openfirm.h> 50 #include <dev/ofw/ofw_bus.h> 51 #include <dev/ofw/ofw_bus_subr.h> 52 53 #include "pic_if.h" 54 55 #define PLIC_MAX_IRQS 1024 56 57 #define PLIC_PRIORITY_BASE 0x000000U 58 59 #define PLIC_ENABLE_BASE 0x002000U 60 #define PLIC_ENABLE_STRIDE 0x80U 61 62 #define PLIC_CONTEXT_BASE 0x200000U 63 #define PLIC_CONTEXT_STRIDE 0x1000U 64 #define PLIC_CONTEXT_THRESHOLD 0x0U 65 #define PLIC_CONTEXT_CLAIM 0x4U 66 67 #define PLIC_PRIORITY(n) (PLIC_PRIORITY_BASE + (n) * sizeof(uint32_t)) 68 #define PLIC_ENABLE(sc, n, h) \ 69 (sc->contexts[h].enable_offset + ((n) / 32) * sizeof(uint32_t)) 70 #define PLIC_THRESHOLD(sc, h) \ 71 (sc->contexts[h].context_offset + PLIC_CONTEXT_THRESHOLD) 72 #define PLIC_CLAIM(sc, h) \ 73 (sc->contexts[h].context_offset + PLIC_CONTEXT_CLAIM) 74 75 static pic_disable_intr_t plic_disable_intr; 76 static pic_enable_intr_t plic_enable_intr; 77 static pic_map_intr_t plic_map_intr; 78 static pic_setup_intr_t plic_setup_intr; 79 static pic_post_ithread_t plic_post_ithread; 80 static pic_pre_ithread_t plic_pre_ithread; 81 static pic_bind_intr_t plic_bind_intr; 82 83 struct plic_irqsrc { 84 struct intr_irqsrc isrc; 85 u_int irq; 86 }; 87 88 struct plic_context { 89 bus_size_t enable_offset; 90 bus_size_t context_offset; 91 }; 92 93 struct plic_softc { 94 device_t dev; 95 struct resource * intc_res; 96 struct plic_irqsrc isrcs[PLIC_MAX_IRQS]; 97 struct plic_context contexts[MAXCPU]; 98 int ndev; 99 }; 100 101 #define RD4(sc, reg) \ 102 bus_read_4(sc->intc_res, (reg)) 103 #define WR4(sc, reg, val) \ 104 bus_write_4(sc->intc_res, (reg), (val)) 105 106 static u_int plic_irq_cpu; 107 108 static int 109 riscv_hartid_to_cpu(int hartid) 110 { 111 int i; 112 113 CPU_FOREACH(i) { 114 if (pcpu_find(i)->pc_hart == hartid) 115 return (i); 116 } 117 118 return (-1); 119 } 120 121 static int 122 plic_get_hartid(device_t dev, phandle_t intc) 123 { 124 int hart; 125 126 /* Check the interrupt controller layout. */ 127 if (OF_searchencprop(intc, "#interrupt-cells", &hart, 128 sizeof(hart)) == -1) { 129 device_printf(dev, 130 "Could not find #interrupt-cells for phandle %u\n", intc); 131 return (-1); 132 } 133 134 /* 135 * The parent of the interrupt-controller is the CPU we are 136 * interested in, so search for its hart ID. 137 */ 138 if (OF_searchencprop(OF_parent(intc), "reg", (pcell_t *)&hart, 139 sizeof(hart)) == -1) { 140 device_printf(dev, "Could not find hartid\n"); 141 return (-1); 142 } 143 144 return (hart); 145 } 146 147 static inline void 148 plic_irq_dispatch(struct plic_softc *sc, u_int irq, 149 struct trapframe *tf) 150 { 151 struct plic_irqsrc *src; 152 153 src = &sc->isrcs[irq]; 154 155 if (intr_isrc_dispatch(&src->isrc, tf) != 0) 156 device_printf(sc->dev, "Stray irq %u detected\n", irq); 157 } 158 159 static int 160 plic_intr(void *arg) 161 { 162 struct plic_softc *sc; 163 struct trapframe *tf; 164 uint32_t pending; 165 uint32_t cpu; 166 167 sc = arg; 168 cpu = PCPU_GET(cpuid); 169 170 /* Claim any pending interrupt. */ 171 pending = RD4(sc, PLIC_CLAIM(sc, cpu)); 172 if (pending) { 173 tf = curthread->td_intr_frame; 174 plic_irq_dispatch(sc, pending, tf); 175 } 176 177 return (FILTER_HANDLED); 178 } 179 180 static void 181 plic_disable_intr(device_t dev, struct intr_irqsrc *isrc) 182 { 183 struct plic_softc *sc; 184 struct plic_irqsrc *src; 185 186 sc = device_get_softc(dev); 187 src = (struct plic_irqsrc *)isrc; 188 189 WR4(sc, PLIC_PRIORITY(src->irq), 0); 190 } 191 192 static void 193 plic_enable_intr(device_t dev, struct intr_irqsrc *isrc) 194 { 195 struct plic_softc *sc; 196 struct plic_irqsrc *src; 197 198 sc = device_get_softc(dev); 199 src = (struct plic_irqsrc *)isrc; 200 201 WR4(sc, PLIC_PRIORITY(src->irq), 1); 202 } 203 204 static int 205 plic_map_intr(device_t dev, struct intr_map_data *data, 206 struct intr_irqsrc **isrcp) 207 { 208 struct intr_map_data_fdt *daf; 209 struct plic_softc *sc; 210 211 sc = device_get_softc(dev); 212 213 if (data->type != INTR_MAP_DATA_FDT) 214 return (ENOTSUP); 215 216 daf = (struct intr_map_data_fdt *)data; 217 if (daf->ncells != 1 || daf->cells[0] > sc->ndev) 218 return (EINVAL); 219 220 *isrcp = &sc->isrcs[daf->cells[0]].isrc; 221 222 return (0); 223 } 224 225 static int 226 plic_probe(device_t dev) 227 { 228 229 if (!ofw_bus_status_okay(dev)) 230 return (ENXIO); 231 232 if (!ofw_bus_is_compatible(dev, "riscv,plic0") && 233 !ofw_bus_is_compatible(dev, "sifive,plic-1.0.0")) 234 return (ENXIO); 235 236 device_set_desc(dev, "RISC-V PLIC"); 237 238 return (BUS_PROBE_DEFAULT); 239 } 240 241 static int 242 plic_attach(device_t dev) 243 { 244 struct plic_irqsrc *isrcs; 245 struct plic_softc *sc; 246 struct intr_pic *pic; 247 pcell_t *cells; 248 uint32_t irq; 249 const char *name; 250 phandle_t node; 251 phandle_t xref; 252 uint32_t cpu; 253 int error; 254 int rid; 255 int nintr; 256 int context; 257 int i; 258 int hart; 259 260 sc = device_get_softc(dev); 261 262 sc->dev = dev; 263 264 node = ofw_bus_get_node(dev); 265 if ((OF_getencprop(node, "riscv,ndev", &sc->ndev, 266 sizeof(sc->ndev))) < 0) { 267 device_printf(dev, 268 "Error: could not get number of devices\n"); 269 return (ENXIO); 270 } 271 272 if (sc->ndev >= PLIC_MAX_IRQS) { 273 device_printf(dev, 274 "Error: invalid ndev (%d)\n", sc->ndev); 275 return (ENXIO); 276 } 277 278 /* Request memory resources */ 279 rid = 0; 280 sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 281 RF_ACTIVE); 282 if (sc->intc_res == NULL) { 283 device_printf(dev, 284 "Error: could not allocate memory resources\n"); 285 return (ENXIO); 286 } 287 288 /* Register the interrupt sources */ 289 isrcs = sc->isrcs; 290 name = device_get_nameunit(sc->dev); 291 for (irq = 1; irq <= sc->ndev; irq++) { 292 isrcs[irq].irq = irq; 293 error = intr_isrc_register(&isrcs[irq].isrc, sc->dev, 294 0, "%s,%u", name, irq); 295 if (error != 0) 296 return (error); 297 298 WR4(sc, PLIC_PRIORITY(irq), 0); 299 } 300 301 /* 302 * Calculate the per-cpu enable and context register offsets. 303 * 304 * This is tricky for a few reasons. The PLIC divides the interrupt 305 * enable, threshold, and claim bits by "context", where each context 306 * routes to a Core-Local Interrupt Controller (CLIC). 307 * 308 * The tricky part is that the PLIC spec imposes no restrictions on how 309 * these contexts are laid out. So for example, there is no guarantee 310 * that each CPU will have both a machine mode and supervisor context, 311 * or that different PLIC implementations will organize the context 312 * registers in the same way. On top of this, we must handle the fact 313 * that cpuid != hartid, as they may have been renumbered during boot. 314 * We perform the following steps: 315 * 316 * 1. Examine the PLIC's "interrupts-extended" property and skip any 317 * entries that are not for supervisor external interrupts. 318 * 319 * 2. Walk up the device tree to find the corresponding CPU, and grab 320 * it's hart ID. 321 * 322 * 3. Convert the hart to a cpuid, and calculate the register offsets 323 * based on the context number. 324 */ 325 nintr = OF_getencprop_alloc_multi(node, "interrupts-extended", 326 sizeof(uint32_t), (void **)&cells); 327 if (nintr <= 0) { 328 device_printf(dev, "Could not read interrupts-extended\n"); 329 return (ENXIO); 330 } 331 332 /* interrupts-extended is a list of phandles and interrupt types. */ 333 for (i = 0, context = 0; i < nintr; i += 2, context++) { 334 /* Skip M-mode external interrupts */ 335 if (cells[i + 1] != IRQ_EXTERNAL_SUPERVISOR) 336 continue; 337 338 /* Get the hart ID from the CLIC's phandle. */ 339 hart = plic_get_hartid(dev, OF_node_from_xref(cells[i])); 340 if (hart < 0) { 341 OF_prop_free(cells); 342 return (ENXIO); 343 } 344 345 /* Get the corresponding cpuid. */ 346 cpu = riscv_hartid_to_cpu(hart); 347 if (cpu < 0) { 348 device_printf(dev, "Invalid hart!\n"); 349 OF_prop_free(cells); 350 return (ENXIO); 351 } 352 353 /* Set the enable and context register offsets for the CPU. */ 354 sc->contexts[cpu].enable_offset = PLIC_ENABLE_BASE + 355 context * PLIC_ENABLE_STRIDE; 356 sc->contexts[cpu].context_offset = PLIC_CONTEXT_BASE + 357 context * PLIC_CONTEXT_STRIDE; 358 } 359 OF_prop_free(cells); 360 361 /* Set the threshold for each CPU to accept all priorities. */ 362 CPU_FOREACH(cpu) 363 WR4(sc, PLIC_THRESHOLD(sc, cpu), 0); 364 365 xref = OF_xref_from_node(node); 366 pic = intr_pic_register(sc->dev, xref); 367 if (pic == NULL) 368 return (ENXIO); 369 370 csr_set(sie, SIE_SEIE); 371 372 return (intr_pic_claim_root(sc->dev, xref, plic_intr, sc, 0)); 373 } 374 375 static void 376 plic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) 377 { 378 379 plic_disable_intr(dev, isrc); 380 } 381 382 static void 383 plic_post_ithread(device_t dev, struct intr_irqsrc *isrc) 384 { 385 struct plic_softc *sc; 386 struct plic_irqsrc *src; 387 uint32_t cpu; 388 389 sc = device_get_softc(dev); 390 src = (struct plic_irqsrc *)isrc; 391 392 cpu = CPU_FFS(&isrc->isrc_cpu) - 1; 393 394 /* Complete the interrupt. */ 395 WR4(sc, PLIC_CLAIM(sc, cpu), src->irq); 396 plic_enable_intr(dev, isrc); 397 } 398 399 static int 400 plic_setup_intr(device_t dev, struct intr_irqsrc *isrc, 401 struct resource *res, struct intr_map_data *data) 402 { 403 CPU_ZERO(&isrc->isrc_cpu); 404 plic_bind_intr(dev, isrc); 405 406 return (0); 407 } 408 409 static int 410 plic_bind_intr(device_t dev, struct intr_irqsrc *isrc) 411 { 412 struct plic_softc *sc; 413 struct plic_irqsrc *src; 414 uint32_t reg; 415 u_int cpu; 416 417 sc = device_get_softc(dev); 418 src = (struct plic_irqsrc *)isrc; 419 420 /* Disable the interrupt source on all CPUs. */ 421 CPU_FOREACH(cpu) { 422 reg = RD4(sc, PLIC_ENABLE(sc, src->irq, cpu)); 423 reg &= ~(1 << (src->irq % 32)); 424 WR4(sc, PLIC_ENABLE(sc, src->irq, cpu), reg); 425 } 426 427 if (CPU_EMPTY(&isrc->isrc_cpu)) { 428 cpu = plic_irq_cpu = intr_irq_next_cpu(plic_irq_cpu, &all_cpus); 429 CPU_SETOF(cpu, &isrc->isrc_cpu); 430 } else { 431 /* 432 * We will only bind to a single CPU so select the first 433 * CPU found. 434 */ 435 cpu = CPU_FFS(&isrc->isrc_cpu) - 1; 436 } 437 438 /* Enable the interrupt on the selected CPU only. */ 439 reg = RD4(sc, PLIC_ENABLE(sc, src->irq, cpu)); 440 reg |= (1 << (src->irq % 32)); 441 WR4(sc, PLIC_ENABLE(sc, src->irq, cpu), reg); 442 443 return (0); 444 } 445 446 static device_method_t plic_methods[] = { 447 DEVMETHOD(device_probe, plic_probe), 448 DEVMETHOD(device_attach, plic_attach), 449 450 DEVMETHOD(pic_disable_intr, plic_disable_intr), 451 DEVMETHOD(pic_enable_intr, plic_enable_intr), 452 DEVMETHOD(pic_map_intr, plic_map_intr), 453 DEVMETHOD(pic_pre_ithread, plic_pre_ithread), 454 DEVMETHOD(pic_post_ithread, plic_post_ithread), 455 DEVMETHOD(pic_post_filter, plic_post_ithread), 456 DEVMETHOD(pic_setup_intr, plic_setup_intr), 457 DEVMETHOD(pic_bind_intr, plic_bind_intr), 458 459 DEVMETHOD_END 460 }; 461 462 static driver_t plic_driver = { 463 "plic", 464 plic_methods, 465 sizeof(struct plic_softc), 466 }; 467 468 EARLY_DRIVER_MODULE(plic, simplebus, plic_driver, 0, 0, 469 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 470