1 /* $NetBSD: clint_fdt.c,v 1.2 2023/07/26 06:36:34 skrll Exp $ */ 2 3 /*- 4 * Copyright (c) 2023 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by 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: clint_fdt.c,v 1.2 2023/07/26 06:36:34 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/evcnt.h> 41 #include <sys/intr.h> 42 43 #include <dev/fdt/fdtvar.h> 44 45 #include <riscv/fdt/riscv_fdtvar.h> 46 47 #include <machine/sysreg.h> 48 49 50 #define CLINT_MSIP_HARTN(n) 0x0000 + 4 * (n) 51 #define CLINT_MTIMECMP_HARTN(n) 0x4000 + 8 * (n) 52 #define CLINT_MTIME 0xbff8 53 #define CLINT_MTIME_LO CLINT_MTIME + 0x0 54 #define CLINT_MTIME_HI CLINT_MTIME + 0x4 55 56 static const struct device_compatible_entry compat_data[] = { 57 { .compat = "riscv,clint0" }, 58 DEVICE_COMPAT_EOL 59 }; 60 61 struct clint_fdt_softc { 62 device_t sc_dev; 63 bus_space_tag_t sc_bst; 64 bus_space_handle_t sc_bsh; 65 66 uint64_t sc_timebase_frequency; 67 uint64_t sc_ticks_per_hz; 68 }; 69 70 static struct clint_fdt_softc *clint_sc; 71 72 #define CLINT_READ(sc, reg) \ 73 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 74 #define CLINT_WRITE(sc, reg, val) \ 75 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 76 77 static int 78 clint_ipi_handler(void *arg) 79 { 80 cpu_Debugger(); 81 return 1; 82 } 83 84 85 static uint64_t 86 clint_time_read(struct clint_fdt_softc *sc) 87 { 88 #if _LP64 89 return bus_space_read_8(sc->sc_bst, sc->sc_bsh, CLINT_MTIME); 90 #else 91 uint32_t hi, lo; 92 do { 93 hi = CLINT_READ(sc, CLINT_MTIME_HI); 94 lo = CLINT_READ(sc, CLINT_MTIME_LO); 95 96 } while (hi != CLINT_READ(sc, CLINT_MTIME_HI)); 97 return 98 __SHIFTIN(hi, __BITS(63, 32)) | 99 __SHIFTIN(lo, __BITS(31, 0)); 100 #endif 101 } 102 103 104 static void 105 clint_timer_set(struct clint_fdt_softc *sc, struct cpu_info *ci) 106 { 107 const uint64_t now = clint_time_read(sc); 108 109 ci->ci_lastintr = now; 110 ci->ci_ev_timer.ev_count++; 111 112 ci->ci_lastintr_scheduled += sc->sc_ticks_per_hz; 113 114 CLINT_WRITE(sc, CLINT_MTIMECMP_HARTN(ci->ci_cpuid), 115 ci->ci_lastintr_scheduled); 116 } 117 118 static int 119 clint_timer_intr(void *arg) 120 { 121 struct cpu_info * const ci = curcpu(); 122 struct clockframe * const cf = arg; 123 struct clint_fdt_softc * const sc = clint_sc; 124 125 csr_sip_clear(SIP_STIP); /* clean pending interrupt status */ 126 127 clint_timer_set(sc, ci); 128 129 hardclock(cf); 130 131 return 1; 132 } 133 134 static void 135 clint_cpu_initclocks(void) 136 { 137 struct cpu_info * const ci = curcpu(); 138 struct clint_fdt_softc * const sc = clint_sc; 139 140 clint_timer_set(sc, ci); 141 142 csr_sie_set(SIE_STIE); /* enable supervisor timer intr */ 143 } 144 145 static int 146 clint_match(device_t parent, cfdata_t cf, void *aux) 147 { 148 struct fdt_attach_args * const faa = aux; 149 150 return of_compatible_match(faa->faa_phandle, compat_data); 151 } 152 153 static void 154 clint_attach(device_t parent, device_t self, void *aux) 155 { 156 struct clint_fdt_softc * const sc = device_private(self); 157 const struct fdt_attach_args * const faa = aux; 158 const int phandle = faa->faa_phandle; 159 bus_addr_t addr; 160 bus_size_t size; 161 162 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 163 aprint_error(": couldn't get registers\n"); 164 return; 165 } 166 167 const int cpus = OF_finddevice("/cpus"); 168 if (cpus == -1) { 169 aprint_error(": couldn't get 'cpus' node\n"); 170 return; 171 } 172 173 uint32_t tbfreq; 174 int ret = of_getprop_uint32(cpus, "timebase-frequency", &tbfreq); 175 if (ret < 0) { 176 aprint_error(": can't get timebase frequency\n"); 177 return; 178 } 179 180 sc->sc_dev = self; 181 sc->sc_bst = faa->faa_bst; 182 sc->sc_timebase_frequency = tbfreq; 183 184 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 185 aprint_error(": couldn't map registers\n"); 186 return; 187 } 188 189 printf("%s: addr %#" PRIxBUSADDR " size %#" PRIxBUSSIZE "\n", __func__, 190 addr, size); 191 192 aprint_naive("\n"); 193 aprint_normal(": local interrupt control. %" PRId64 " KHz timer.\n", 194 sc->sc_timebase_frequency / 1000); 195 196 sc->sc_ticks_per_hz = sc->sc_timebase_frequency / hz; 197 int len; 198 const uint32_t *data = 199 fdtbus_get_prop(phandle, "interrupts-extended", &len); 200 if (data == NULL) { 201 aprint_error_dev(self, "couldn't get context data\n"); 202 return; 203 } 204 205 clint_sc = sc; 206 // riscv_fdt_timer_register(clint_cpu_initclocks); 207 208 int context = 0; 209 while (len > 0) { 210 const int pphandle = be32toh(data[0]); 211 const int xref = fdtbus_get_phandle_from_native(pphandle); 212 const int intr_source = be32toh(data[1]); 213 uint32_t intr_cells; 214 215 int error = 216 of_getprop_uint32(xref, "#interrupt-cells", &intr_cells); 217 if (error) { 218 aprint_error_dev(self, "couldn't get cell length " 219 "for parent CPU for context %d", context); 220 return; 221 } 222 int (*handler)(void *) = NULL; 223 void *arg = NULL; 224 int ipl = IPL_HIGH; 225 switch (intr_source) { 226 case IRQ_MACHINE_SOFTWARE: 227 handler = clint_ipi_handler; 228 arg = sc; 229 break; 230 case IRQ_MACHINE_TIMER: 231 handler = clint_timer_intr; 232 arg = NULL; /* clock frame */ 233 ipl = IPL_SCHED; 234 break; 235 default: 236 aprint_error_dev(self, "unknown interrupt source %d", 237 intr_source); 238 } 239 240 if (handler) { 241 void *ih = fdtbus_intr_establish_xname(phandle, 242 context, ipl, FDT_INTR_MPSAFE, 243 handler, arg, device_xname(self)); 244 if (ih == NULL) { 245 aprint_error_dev(self, "couldn't install " 246 "interrupt handler\n"); 247 } else { 248 char intrstr[128]; 249 bool ok = fdtbus_intr_str(phandle, context, 250 intrstr, sizeof(intrstr)); 251 aprint_verbose_dev(self, "interrupt %s handler " 252 "installed\n", ok ? intrstr : "(unk)"); 253 } 254 } 255 256 len -= (intr_cells + 1) * 4; 257 data += (intr_cells + 1); 258 context++; 259 } 260 } 261 262 CFATTACH_DECL_NEW(clint_fdt, sizeof(struct clint_fdt_softc), 263 clint_match, clint_attach, NULL, NULL); 264