1 /* $NetBSD: plic_fdt.c,v 1.1 2023/05/07 12:41:48 skrll Exp $ */ 2 3 /*- 4 * Copyright (c) 2022 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Portions of this code is derived from software contributed to The NetBSD 8 * Foundation by Simon Burge and Nick Hudson. 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 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: plic_fdt.c,v 1.1 2023/05/07 12:41:48 skrll Exp $"); 34 35 #include <sys/param.h> 36 37 #include <sys/bus.h> 38 #include <sys/cpu.h> 39 #include <sys/device.h> 40 #include <sys/intr.h> 41 42 #include <dev/fdt/fdtvar.h> 43 44 #include <riscv/sysreg.h> 45 #include <riscv/dev/plicreg.h> 46 #include <riscv/dev/plicvar.h> 47 48 static const struct device_compatible_entry compat_data[] = { 49 { .compat = "riscv,plic0" }, 50 { .compat = "sifive,plic-1.0.0" }, 51 DEVICE_COMPAT_EOL 52 }; 53 54 static void * 55 plic_fdt_intr_establish(device_t dev, u_int *specifier, int ipl, int flags, 56 int (*func)(void *), void *arg, const char *xname) 57 { 58 struct plic_softc * const sc = device_private(dev); 59 struct plic_intrhand *ih; 60 61 /* 1st cell is the interrupt number */ 62 const u_int irq = be32toh(specifier[0]); 63 if (irq > sc->sc_ndev) { 64 aprint_error_dev(dev, "irq %d greater than max irq %d\n", 65 irq, sc->sc_ndev); 66 return NULL; 67 } 68 ih = plic_intr_establish_xname(irq, ipl, 69 (flags & FDT_INTR_MPSAFE) != 0 ? IST_MPSAFE : 0, func, arg, xname); 70 71 return ih; 72 } 73 static void 74 plic_fdt_intr_disestablish(device_t dev, void *cookie) 75 { 76 struct plic_softc * const sc = device_private(dev); 77 struct plic_intrhand * const ih = cookie; 78 const u_int cidx = ih->ih_cidx; 79 const u_int irq = ih->ih_irq; 80 81 plic_disable(sc, cidx, irq); 82 plic_set_priority(sc, irq, 0); 83 84 memset(&sc->sc_intr[irq], 0, sizeof(*sc->sc_intr)); 85 } 86 87 88 static bool 89 plic_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen) 90 { 91 /* 1st cell is the interrupt number */ 92 const int irq = be32toh(specifier[0]); 93 94 snprintf(buf, buflen, "%s irq %d", device_xname(dev), irq); 95 96 return true; 97 } 98 99 static struct fdtbus_interrupt_controller_func plic_funcs = { 100 .establish = plic_fdt_intr_establish, 101 .disestablish = plic_fdt_intr_disestablish, 102 .intrstr = plic_intrstr, 103 }; 104 105 static int 106 plic_fdt_match(device_t parent, cfdata_t cf, void *aux) 107 { 108 struct fdt_attach_args * const faa = aux; 109 110 return of_compatible_match(faa->faa_phandle, compat_data); 111 } 112 113 static void 114 plic_fdt_attach(device_t parent, device_t self, void *aux) 115 { 116 struct plic_softc * const sc = device_private(self); 117 struct fdt_attach_args * const faa = aux; 118 const int phandle = faa->faa_phandle; 119 bus_addr_t addr; 120 bus_size_t size; 121 const uint32_t *data; 122 int error, context, len; 123 124 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 125 aprint_error(": couldn't get registers\n"); 126 return; 127 } 128 129 sc->sc_dev = self; 130 sc->sc_bst = faa->faa_bst; 131 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 132 aprint_error(": couldn't get registers\n"); 133 return; 134 } 135 136 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 137 aprint_error(": couldn't map registers\n"); 138 return; 139 } 140 141 error = of_getprop_uint32(phandle, "riscv,ndev", &sc->sc_ndev); 142 if (error) { 143 aprint_error("couldn't get supported number of external " 144 "interrupts\n"); 145 return; 146 } 147 if (sc->sc_ndev > PLIC_MAX_IRQ) { 148 aprint_error(": invalid number of external interrupts (%u)\n", 149 sc->sc_ndev); 150 return; 151 } 152 aprint_verbose("\n"); 153 154 /* 155 * PLIC context device mappings is documented at 156 * https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/sifive%2Cplic-1.0.0.yaml 157 * We need to walk the "interrupts-extended" property of 158 * and register handlers for the defined contexts. 159 * 160 * XXX 161 * This is usually an abstraction violation to inspect 162 * the current node's properties directly. We do it 163 * in this case because the current binding spec defines 164 * this case. We do a bit of error checking to make 165 * sure all the documented assumptions are correct. 166 */ 167 168 data = fdtbus_get_prop(phandle, "interrupts-extended", &len); 169 if (data == NULL) { 170 aprint_error_dev(self, "couldn't get context data\n"); 171 return; 172 } 173 context = 0; 174 while (len > 0) { 175 const int pphandle = be32toh(data[0]); 176 const int xref = fdtbus_get_phandle_from_native(pphandle); 177 const int intr_source = be32toh(data[1]); 178 uint32_t intr_cells; 179 180 error = of_getprop_uint32(xref, "#interrupt-cells", &intr_cells); 181 if (error) { 182 aprint_error_dev(self, "couldn't get cell length " 183 "for parent CPU for context %d", context); 184 return; 185 } 186 187 if (intr_source != -1) { 188 /* What do we want to pass as arg to plic_intr */ 189 void *ih = fdtbus_intr_establish_xname(phandle, 190 context, IPL_VM, FDT_INTR_MPSAFE, 191 plic_intr, sc, device_xname(self)); 192 if (ih == NULL) { 193 aprint_error_dev(self, "couldn't install " 194 "interrupt handler\n"); 195 } else { 196 char intrstr[128]; 197 bool ok = fdtbus_intr_str(phandle, context, 198 intrstr, sizeof(intrstr)); 199 aprint_verbose_dev(self, "interrupt %s handler " 200 "installed\n", ok ? intrstr : "(unk)"); 201 } 202 203 if (intr_source == IRQ_SUPERVISOR_EXTERNAL) { 204 /* 205 * When finding context info, parent _must_ be a 206 * compatbile clint device. 207 */ 208 bus_addr_t cpuid; 209 int cpu_ref; 210 static const struct device_compatible_entry clint_compat_data[] = { 211 { .compat = "riscv,cpu-intc" }, 212 DEVICE_COMPAT_EOL 213 }; 214 215 if (of_compatible_match(xref, clint_compat_data)) { 216 /* get cpuid for the parent node */ 217 cpu_ref = OF_parent(xref); 218 fdtbus_get_reg(cpu_ref, 0, &cpuid, NULL); 219 220 KASSERT(context <= PLIC_MAX_CONTEXT); 221 sc->sc_context[cpuid] = context; 222 aprint_verbose_dev(self, 223 "cpu %"PRId64" context %d\n", 224 cpuid, context); 225 } else { 226 aprint_error_dev(self, "incompatiable CLINT " 227 " for PLIC for context %d\n", context); 228 } 229 230 } 231 } 232 len -= (intr_cells + 1) * 4; 233 data += (intr_cells + 1); 234 context++; 235 } 236 237 aprint_verbose_dev(self, ""); 238 error = plic_attach_common(sc, addr, size); 239 if (error != 0) { 240 return; 241 } 242 243 /* Setup complete, register this FDT bus. */ 244 error = fdtbus_register_interrupt_controller(self, phandle, 245 &plic_funcs); 246 if (error != 0) { 247 aprint_error(": couldn't register with fdtbus: %d\n", error); 248 } 249 } 250 251 CFATTACH_DECL_NEW(plic_fdt, sizeof(struct plic_softc), 252 plic_fdt_match, plic_fdt_attach, NULL, NULL); 253