1 /*- 2 * Copyright (c) 2015-2017 Ruslan Bukin <br@bsdpad.com> 3 * All rights reserved. 4 * Copyright (c) 2021 Jessica Clarke <jrtc27@FreeBSD.org> 5 * 6 * Portions of this software were developed by SRI International and the 7 * University of Cambridge Computer Laboratory under DARPA/AFRL contract 8 * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme. 9 * 10 * Portions of this software were developed by the University of Cambridge 11 * Computer Laboratory as part of the CTSRD Project, with support from the 12 * UK Higher Education Innovation Fund (HEIF). 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/bus.h> 41 #include <sys/kernel.h> 42 #include <sys/module.h> 43 #include <sys/proc.h> 44 #include <sys/cpuset.h> 45 #include <sys/interrupt.h> 46 #include <sys/smp.h> 47 48 #include <machine/bus.h> 49 #include <machine/cpu.h> 50 #include <machine/cpufunc.h> 51 #include <machine/frame.h> 52 #include <machine/intr.h> 53 54 #include <dev/fdt/simplebus.h> 55 56 #include <dev/ofw/openfirm.h> 57 #include <dev/ofw/ofw_bus.h> 58 #include <dev/ofw/ofw_bus_subr.h> 59 60 #include "pic_if.h" 61 62 #define INTC_NIRQS 16 63 64 struct intc_irqsrc { 65 struct intr_irqsrc isrc; 66 u_int irq; 67 }; 68 69 struct intc_softc { 70 device_t dev; 71 struct intc_irqsrc isrcs[INTC_NIRQS]; 72 }; 73 74 static int intc_intr(void *arg); 75 76 static phandle_t 77 intc_ofw_find(device_t dev, uint32_t hartid) 78 { 79 phandle_t node; 80 pcell_t reg; 81 82 node = OF_finddevice("/cpus"); 83 if (node == -1) { 84 device_printf(dev, "Can't find cpus node\n"); 85 return ((phandle_t)-1); 86 } 87 88 for (node = OF_child(node); node != 0; node = OF_peer(node)) { 89 if (!ofw_bus_node_status_okay(node)) 90 continue; 91 92 if (!ofw_bus_node_is_compatible(node, "riscv")) 93 continue; 94 95 if (OF_searchencprop(node, "reg", ®, sizeof(reg)) == -1) 96 continue; 97 98 if (reg == hartid) 99 break; 100 } 101 102 if (node == 0) { 103 device_printf(dev, "Can't find boot cpu node\n"); 104 return ((phandle_t)-1); 105 } 106 107 for (node = OF_child(node); node != 0; node = OF_peer(node)) { 108 if (!ofw_bus_node_status_okay(node)) 109 continue; 110 111 if (ofw_bus_node_is_compatible(node, "riscv,cpu-intc")) 112 break; 113 } 114 115 if (node == 0) { 116 device_printf(dev, 117 "Can't find boot cpu local interrupt controller\n"); 118 return ((phandle_t)-1); 119 } 120 121 return (node); 122 } 123 124 static void 125 intc_identify(driver_t *driver, device_t parent) 126 { 127 device_t dev; 128 phandle_t node; 129 130 if (device_find_child(parent, "intc", -1) != NULL) 131 return; 132 133 node = intc_ofw_find(parent, PCPU_GET(hart)); 134 if (node == -1) 135 return; 136 137 dev = simplebus_add_device(parent, node, 0, "intc", -1, NULL); 138 if (dev == NULL) 139 device_printf(parent, "Can't add intc child\n"); 140 } 141 142 static int 143 intc_probe(device_t dev) 144 { 145 device_set_desc(dev, "RISC-V Local Interrupt Controller"); 146 147 return (BUS_PROBE_NOWILDCARD); 148 } 149 150 static int 151 intc_attach(device_t dev) 152 { 153 struct intc_irqsrc *isrcs; 154 struct intc_softc *sc; 155 struct intr_pic *pic; 156 const char *name; 157 phandle_t xref; 158 u_int flags; 159 int i, error; 160 161 sc = device_get_softc(dev); 162 sc->dev = dev; 163 164 name = device_get_nameunit(dev); 165 xref = OF_xref_from_node(ofw_bus_get_node(dev)); 166 167 isrcs = sc->isrcs; 168 for (i = 0; i < INTC_NIRQS; i++) { 169 isrcs[i].irq = i; 170 flags = i == IRQ_SOFTWARE_SUPERVISOR 171 ? INTR_ISRCF_IPI : INTR_ISRCF_PPI; 172 error = intr_isrc_register(&isrcs[i].isrc, sc->dev, flags, 173 "%s,%u", name, i); 174 if (error != 0) { 175 device_printf(dev, "Can't register interrupt %d\n", i); 176 return (error); 177 } 178 } 179 180 pic = intr_pic_register(sc->dev, xref); 181 if (pic == NULL) 182 return (ENXIO); 183 184 return (intr_pic_claim_root(sc->dev, xref, intc_intr, sc)); 185 } 186 187 static void 188 intc_disable_intr(device_t dev, struct intr_irqsrc *isrc) 189 { 190 u_int irq; 191 192 irq = ((struct intc_irqsrc *)isrc)->irq; 193 if (irq >= INTC_NIRQS) 194 panic("%s: Unsupported IRQ %u", __func__, irq); 195 196 csr_clear(sie, 1ul << irq); 197 } 198 199 static void 200 intc_enable_intr(device_t dev, struct intr_irqsrc *isrc) 201 { 202 u_int irq; 203 204 irq = ((struct intc_irqsrc *)isrc)->irq; 205 if (irq >= INTC_NIRQS) 206 panic("%s: Unsupported IRQ %u", __func__, irq); 207 208 csr_set(sie, 1ul << irq); 209 } 210 211 static int 212 intc_map_intr(device_t dev, struct intr_map_data *data, 213 struct intr_irqsrc **isrcp) 214 { 215 struct intr_map_data_fdt *daf; 216 struct intc_softc *sc; 217 218 sc = device_get_softc(dev); 219 220 if (data->type != INTR_MAP_DATA_FDT) 221 return (ENOTSUP); 222 223 daf = (struct intr_map_data_fdt *)data; 224 if (daf->ncells != 1 || daf->cells[0] >= INTC_NIRQS) 225 return (EINVAL); 226 227 *isrcp = &sc->isrcs[daf->cells[0]].isrc; 228 229 return (0); 230 } 231 232 static int 233 intc_setup_intr(device_t dev, struct intr_irqsrc *isrc, 234 struct resource *res, struct intr_map_data *data) 235 { 236 if (isrc->isrc_flags & INTR_ISRCF_PPI) 237 CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); 238 239 return (0); 240 } 241 242 #ifdef SMP 243 static void 244 intc_init_secondary(device_t dev) 245 { 246 struct intc_softc *sc; 247 struct intr_irqsrc *isrc; 248 u_int cpu, irq; 249 250 sc = device_get_softc(dev); 251 cpu = PCPU_GET(cpuid); 252 253 /* Unmask attached interrupts */ 254 for (irq = 0; irq < INTC_NIRQS; irq++) { 255 isrc = &sc->isrcs[irq].isrc; 256 if (intr_isrc_init_on_cpu(isrc, cpu)) 257 intc_enable_intr(dev, isrc); 258 } 259 } 260 #endif 261 262 static int 263 intc_intr(void *arg) 264 { 265 struct trapframe *frame; 266 struct intc_softc *sc; 267 uint64_t active_irq; 268 struct intc_irqsrc *src; 269 270 sc = arg; 271 frame = curthread->td_intr_frame; 272 273 KASSERT((frame->tf_scause & SCAUSE_INTR) != 0, 274 ("%s: not an interrupt frame", __func__)); 275 276 active_irq = frame->tf_scause & SCAUSE_CODE; 277 278 if (active_irq >= INTC_NIRQS) 279 return (FILTER_HANDLED); 280 281 src = &sc->isrcs[active_irq]; 282 if (intr_isrc_dispatch(&src->isrc, frame) != 0) { 283 intc_disable_intr(sc->dev, &src->isrc); 284 device_printf(sc->dev, "Stray irq %lu disabled\n", 285 active_irq); 286 } 287 288 return (FILTER_HANDLED); 289 } 290 291 static device_method_t intc_methods[] = { 292 /* Device interface */ 293 DEVMETHOD(device_identify, intc_identify), 294 DEVMETHOD(device_probe, intc_probe), 295 DEVMETHOD(device_attach, intc_attach), 296 297 /* Interrupt controller interface */ 298 DEVMETHOD(pic_disable_intr, intc_disable_intr), 299 DEVMETHOD(pic_enable_intr, intc_enable_intr), 300 DEVMETHOD(pic_map_intr, intc_map_intr), 301 DEVMETHOD(pic_setup_intr, intc_setup_intr), 302 #ifdef SMP 303 DEVMETHOD(pic_init_secondary, intc_init_secondary), 304 #endif 305 306 DEVMETHOD_END 307 }; 308 309 DEFINE_CLASS_0(intc, intc_driver, intc_methods, sizeof(struct intc_softc)); 310 EARLY_DRIVER_MODULE(intc, ofwbus, intc_driver, 0, 0, 311 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_FIRST); 312