1 /* $NetBSD: arm_fdt.c,v 1.25 2026/05/11 19:36:06 yurix Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Jared D. McNeill <jmcneill (at) invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include "opt_arm_timer.h" 30 #include "opt_efi.h" 31 #include "opt_modular.h" 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: arm_fdt.c,v 1.25 2026/05/11 19:36:06 yurix Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/cpu.h> 39 #include <sys/device.h> 40 #include <sys/kmem.h> 41 #include <sys/bus.h> 42 #include <sys/module.h> 43 44 #include <uvm/uvm_extern.h> 45 46 #include <dev/fdt/fdtvar.h> 47 #include <dev/fdt/fdt_platform.h> 48 49 #include <dev/ofw/openfirm.h> 50 51 #include <arm/fdt/arm_fdtvar.h> 52 53 #include <arm/locore.h> 54 55 #ifdef EFI_RUNTIME 56 #include <arm/arm/efi_runtime.h> 57 #include <dev/clock_subr.h> 58 #endif 59 60 static int arm_fdt_match(device_t, cfdata_t, void *); 61 static void arm_fdt_attach(device_t, device_t, void *); 62 63 static void arm_fdt_irq_default_handler(void *); 64 static void arm_fdt_fiq_default_handler(void *); 65 66 #ifdef EFI_RUNTIME 67 static void arm_fdt_efi_init(device_t); 68 static int arm_fdt_efi_rtc_gettime(todr_chip_handle_t, struct clock_ymdhms *); 69 static int arm_fdt_efi_rtc_settime(todr_chip_handle_t, struct clock_ymdhms *); 70 71 static struct todr_chip_handle efi_todr; 72 73 static const char * const ignore_efi_runtime_models[] = { 74 /* RTC calls do not work with current firmware. */ 75 "Radxa Computer (Shenzhen) Co., Ltd. Radxa Orion O6", 76 }; 77 #endif 78 79 CFATTACH_DECL_NEW(arm_fdt, 0, 80 arm_fdt_match, arm_fdt_attach, NULL, NULL); 81 82 struct arm_fdt_cpu_hatch_cb { 83 TAILQ_ENTRY(arm_fdt_cpu_hatch_cb) next; 84 void (*cb)(void *, struct cpu_info *); 85 void *priv; 86 }; 87 88 static TAILQ_HEAD(, arm_fdt_cpu_hatch_cb) arm_fdt_cpu_hatch_cbs = 89 TAILQ_HEAD_INITIALIZER(arm_fdt_cpu_hatch_cbs); 90 91 static void (*_arm_fdt_irq_handler)(void *) = arm_fdt_irq_default_handler; 92 static void (*_arm_fdt_fiq_handler)(void *) = arm_fdt_fiq_default_handler; 93 static void (*_arm_fdt_timer_init)(void) = NULL; 94 static void (*_arm_fdt_timer_setstatclockrate)(int) = NULL; 95 96 int 97 arm_fdt_match(device_t parent, cfdata_t cf, void *aux) 98 { 99 return 1; 100 } 101 102 void 103 arm_fdt_attach(device_t parent, device_t self, void *aux) 104 { 105 const struct fdt_platform *plat = fdt_platform_find(); 106 struct fdt_attach_args faa; 107 108 aprint_naive("\n"); 109 aprint_normal("\n"); 110 111 DISABLE_INTERRUPT(); 112 113 #ifdef EFI_RUNTIME 114 arm_fdt_efi_init(self); 115 #endif 116 117 plat->fp_init_attach_args(&faa); 118 faa.faa_name = ""; 119 faa.faa_phandle = OF_peer(0); 120 121 config_found(self, &faa, NULL, CFARGS_NONE); 122 } 123 void 124 arm_fdt_cpu_hatch_register(void *priv, void (*cb)(void *, struct cpu_info *)) 125 { 126 struct arm_fdt_cpu_hatch_cb *c; 127 128 c = kmem_alloc(sizeof(*c), KM_SLEEP); 129 c->priv = priv; 130 c->cb = cb; 131 TAILQ_INSERT_TAIL(&arm_fdt_cpu_hatch_cbs, c, next); 132 } 133 134 void 135 arm_fdt_cpu_hatch(struct cpu_info *ci) 136 { 137 struct arm_fdt_cpu_hatch_cb *c; 138 139 TAILQ_FOREACH(c, &arm_fdt_cpu_hatch_cbs, next) 140 c->cb(c->priv, ci); 141 } 142 143 static void 144 arm_fdt_irq_default_handler(void *frame) 145 { 146 panic("No IRQ handler installed"); 147 } 148 149 static void 150 arm_fdt_fiq_default_handler(void *frame) 151 { 152 panic("No FIQ handler installed"); 153 } 154 155 void 156 arm_fdt_irq_set_handler(void (*irq_handler)(void *)) 157 { 158 KASSERT(_arm_fdt_irq_handler == arm_fdt_irq_default_handler); 159 _arm_fdt_irq_handler = irq_handler; 160 } 161 162 void 163 arm_fdt_fiq_set_handler(void (*fiq_handler)(void *)) 164 { 165 KASSERT(_arm_fdt_fiq_handler == arm_fdt_fiq_default_handler); 166 _arm_fdt_fiq_handler = fiq_handler; 167 } 168 169 void 170 arm_fdt_irq_handler(void *tf) 171 { 172 _arm_fdt_irq_handler(tf); 173 } 174 175 void 176 arm_fdt_fiq_handler(void *tf) 177 { 178 _arm_fdt_fiq_handler(tf); 179 } 180 181 void 182 arm_fdt_timer_register(void (*timerfn)(void)) 183 { 184 if (_arm_fdt_timer_init != NULL) { 185 #ifdef DIAGNOSTIC 186 aprint_verbose("%s: timer already registered\n", __func__); 187 #endif 188 return; 189 } 190 _arm_fdt_timer_init = timerfn; 191 } 192 193 #ifdef __HAVE_GENERIC_CPU_INITCLOCKS 194 void 195 cpu_initclocks(void) 196 { 197 if (_arm_fdt_timer_init == NULL) 198 panic("cpu_initclocks: no timer registered"); 199 _arm_fdt_timer_init(); 200 ENABLE_INTERRUPT(); 201 } 202 #endif 203 204 void 205 arm_fdt_timer_register_setstatclockrate(void (*ratefn)(int)) 206 { 207 if (_arm_fdt_timer_setstatclockrate != NULL) { 208 #ifdef DIAGNOSTIC 209 aprint_verbose("%s: setstatclockrate already registered\n", 210 __func__); 211 #endif 212 return; 213 } 214 _arm_fdt_timer_setstatclockrate = ratefn; 215 } 216 217 #ifdef __HAVE_GENERIC_SETSTATCLOCKRATE 218 void 219 setstatclockrate(int newhz) 220 { 221 if (_arm_fdt_timer_setstatclockrate != NULL) { 222 _arm_fdt_timer_setstatclockrate(newhz); 223 } 224 } 225 #endif 226 227 void 228 arm_fdt_module_init(void) 229 { 230 #ifdef MODULAR 231 const int chosen = OF_finddevice("/chosen"); 232 const char *module_name; 233 const uint64_t *data; 234 u_int index; 235 paddr_t pa; 236 vaddr_t va; 237 int len; 238 239 if (chosen == -1) 240 return; 241 242 data = fdtbus_get_prop(chosen, "netbsd,modules", &len); 243 if (data == NULL) 244 return; 245 246 for (index = 0; index < len / 16; index++, data += 2) { 247 module_name = fdtbus_get_string_index(chosen, 248 "netbsd,module-names", index); 249 if (module_name == NULL) 250 break; 251 252 const paddr_t startpa = (paddr_t)be64dec(data + 0); 253 const size_t size = (size_t)be64dec(data + 1); 254 const paddr_t endpa = round_page(startpa + size); 255 256 const vaddr_t startva = uvm_km_alloc(kernel_map, endpa - startpa, 257 0, UVM_KMF_VAONLY | UVM_KMF_NOWAIT); 258 if (startva == 0) { 259 printf("ERROR: Cannot allocate VA for module %s\n", 260 module_name); 261 continue; 262 } 263 264 for (pa = startpa, va = startva; 265 pa < endpa; 266 pa += PAGE_SIZE, va += PAGE_SIZE) { 267 pmap_kenter_pa(va, pa, VM_PROT_ALL, 0); 268 } 269 pmap_update(pmap_kernel()); 270 271 module_prime(module_name, (void *)(uintptr_t)startva, size); 272 } 273 #endif /* !MODULAR */ 274 } 275 276 #ifdef EFI_RUNTIME 277 static bool 278 arm_fdi_efi_ignored(void) 279 { 280 const int phandle = OF_peer(0); 281 const char *descr; 282 u_int n; 283 284 descr = fdtbus_get_string(phandle, "model"); 285 if (descr == NULL) { 286 return false; 287 } 288 289 for (n = 0; n < __arraycount(ignore_efi_runtime_models); n++) { 290 if (strcmp(descr, ignore_efi_runtime_models[n]) == 0) { 291 return true; 292 } 293 } 294 295 return false; 296 } 297 298 static void 299 arm_fdt_efi_init(device_t dev) 300 { 301 uint64_t efi_system_table; 302 struct efi_tm tm; 303 int error; 304 305 const int chosen = OF_finddevice("/chosen"); 306 if (chosen < 0) 307 return; 308 309 if (arm_fdi_efi_ignored()) { 310 aprint_debug_dev(dev, "EFI runtime services ignored on this platform\n"); 311 return; 312 } 313 314 if (of_getprop_uint64(chosen, "netbsd,uefi-system-table", &efi_system_table) != 0) 315 return; 316 317 error = arm_efirt_init(efi_system_table); 318 if (error) 319 return; 320 321 aprint_debug_dev(dev, "EFI system table at %#" PRIx64 "\n", efi_system_table); 322 323 if (arm_efirt_gettime(&tm, NULL) == 0) { 324 aprint_normal_dev(dev, "using EFI runtime services for RTC\n"); 325 efi_todr.todr_gettime_ymdhms = arm_fdt_efi_rtc_gettime; 326 efi_todr.todr_settime_ymdhms = arm_fdt_efi_rtc_settime; 327 todr_attach(&efi_todr); 328 } 329 } 330 331 static int 332 arm_fdt_efi_rtc_gettime(todr_chip_handle_t tch, struct clock_ymdhms *dt) 333 { 334 struct efi_tm tm; 335 efi_status status; 336 337 status = arm_efirt_gettime(&tm, NULL); 338 if (status != 0) 339 return EIO; 340 341 dt->dt_year = tm.tm_year; 342 dt->dt_mon = tm.tm_mon; 343 dt->dt_day = tm.tm_mday; 344 dt->dt_wday = 0; 345 dt->dt_hour = tm.tm_hour; 346 dt->dt_min = tm.tm_min; 347 dt->dt_sec = tm.tm_sec; 348 349 return 0; 350 } 351 352 static int 353 arm_fdt_efi_rtc_settime(todr_chip_handle_t tch, struct clock_ymdhms *dt) 354 { 355 struct efi_tm tm; 356 efi_status status; 357 358 memset(&tm, 0, sizeof(tm)); 359 tm.tm_year = dt->dt_year; 360 tm.tm_mon = dt->dt_mon; 361 tm.tm_mday = dt->dt_day; 362 tm.tm_hour = dt->dt_hour; 363 tm.tm_min = dt->dt_min; 364 tm.tm_sec = dt->dt_sec; 365 366 status = arm_efirt_settime(&tm); 367 if (status != 0) 368 return EIO; 369 370 return 0; 371 } 372 #endif 373