1 /* $NetBSD: arm_fdt.c,v 1.11 2020/06/21 17:25:03 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Jared D. McNeill <jmcneill@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_modular.h" 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: arm_fdt.c,v 1.11 2020/06/21 17:25:03 jmcneill Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/cpu.h> 38 #include <sys/device.h> 39 #include <sys/kmem.h> 40 #include <sys/bus.h> 41 #include <sys/module.h> 42 43 #include <uvm/uvm_extern.h> 44 45 #include <dev/fdt/fdtvar.h> 46 #include <dev/ofw/openfirm.h> 47 48 #include <arm/fdt/arm_fdtvar.h> 49 50 static int arm_fdt_match(device_t, cfdata_t, void *); 51 static void arm_fdt_attach(device_t, device_t, void *); 52 53 CFATTACH_DECL_NEW(arm_fdt, 0, 54 arm_fdt_match, arm_fdt_attach, NULL, NULL); 55 56 struct arm_fdt_cpu_hatch_cb { 57 TAILQ_ENTRY(arm_fdt_cpu_hatch_cb) next; 58 void (*cb)(void *, struct cpu_info *); 59 void *priv; 60 }; 61 62 static TAILQ_HEAD(, arm_fdt_cpu_hatch_cb) arm_fdt_cpu_hatch_cbs = 63 TAILQ_HEAD_INITIALIZER(arm_fdt_cpu_hatch_cbs); 64 65 static void (*_arm_fdt_irq_handler)(void *) = NULL; 66 static void (*_arm_fdt_timer_init)(void) = NULL; 67 68 int 69 arm_fdt_match(device_t parent, cfdata_t cf, void *aux) 70 { 71 return 1; 72 } 73 74 void 75 arm_fdt_attach(device_t parent, device_t self, void *aux) 76 { 77 const struct arm_platform *plat = arm_fdt_platform(); 78 struct fdt_attach_args faa; 79 80 aprint_naive("\n"); 81 aprint_normal("\n"); 82 83 plat->ap_init_attach_args(&faa); 84 faa.faa_name = ""; 85 faa.faa_phandle = OF_peer(0); 86 87 config_found(self, &faa, NULL); 88 } 89 90 const struct arm_platform * 91 arm_fdt_platform(void) 92 { 93 static const struct arm_platform_info *booted_platform = NULL; 94 __link_set_decl(arm_platforms, struct arm_platform_info); 95 struct arm_platform_info * const *info; 96 97 if (booted_platform == NULL) { 98 const struct arm_platform_info *best_info = NULL; 99 const int phandle = OF_peer(0); 100 int match, best_match = 0; 101 102 __link_set_foreach(info, arm_platforms) { 103 const char * const compat[] = { (*info)->api_compat, NULL }; 104 match = of_match_compatible(phandle, compat); 105 if (match > best_match) { 106 best_match = match; 107 best_info = *info; 108 } 109 } 110 111 booted_platform = best_info; 112 } 113 114 /* 115 * No SoC specific platform was found. Try to find a generic 116 * platform definition and use that if available. 117 */ 118 if (booted_platform == NULL) { 119 __link_set_foreach(info, arm_platforms) { 120 if (strcmp((*info)->api_compat, ARM_PLATFORM_DEFAULT) == 0) { 121 booted_platform = *info; 122 break; 123 } 124 } 125 } 126 127 return booted_platform == NULL ? NULL : booted_platform->api_ops; 128 } 129 130 void 131 arm_fdt_cpu_hatch_register(void *priv, void (*cb)(void *, struct cpu_info *)) 132 { 133 struct arm_fdt_cpu_hatch_cb *c; 134 135 c = kmem_alloc(sizeof(*c), KM_SLEEP); 136 c->priv = priv; 137 c->cb = cb; 138 TAILQ_INSERT_TAIL(&arm_fdt_cpu_hatch_cbs, c, next); 139 } 140 141 void 142 arm_fdt_cpu_hatch(struct cpu_info *ci) 143 { 144 struct arm_fdt_cpu_hatch_cb *c; 145 146 TAILQ_FOREACH(c, &arm_fdt_cpu_hatch_cbs, next) 147 c->cb(c->priv, ci); 148 } 149 150 void 151 arm_fdt_irq_set_handler(void (*irq_handler)(void *)) 152 { 153 KASSERT(_arm_fdt_irq_handler == NULL); 154 _arm_fdt_irq_handler = irq_handler; 155 } 156 157 void 158 arm_fdt_irq_handler(void *tf) 159 { 160 _arm_fdt_irq_handler(tf); 161 } 162 163 void 164 arm_fdt_timer_register(void (*timerfn)(void)) 165 { 166 if (_arm_fdt_timer_init != NULL) { 167 #ifdef DIAGNOSTIC 168 aprint_verbose("%s: timer already registered\n", __func__); 169 #endif 170 return; 171 } 172 _arm_fdt_timer_init = timerfn; 173 } 174 175 void 176 arm_fdt_memory_dump(paddr_t pa) 177 { 178 const struct arm_platform *plat = arm_fdt_platform(); 179 struct fdt_attach_args faa; 180 bus_space_tag_t bst; 181 bus_space_handle_t bsh; 182 183 plat->ap_init_attach_args(&faa); 184 185 bst = faa.faa_bst; 186 bus_space_map(bst, pa, 0x100, 0, &bsh); 187 188 for (int i = 0; i < 0x100; i += 0x10) { 189 printf("%" PRIxPTR ": %08x %08x %08x %08x\n", 190 (uintptr_t)(pa + i), 191 bus_space_read_4(bst, bsh, i + 0), 192 bus_space_read_4(bst, bsh, i + 4), 193 bus_space_read_4(bst, bsh, i + 8), 194 bus_space_read_4(bst, bsh, i + 12)); 195 } 196 } 197 198 #ifdef __HAVE_GENERIC_CPU_INITCLOCKS 199 void 200 cpu_initclocks(void) 201 { 202 if (_arm_fdt_timer_init == NULL) 203 panic("cpu_initclocks: no timer registered"); 204 _arm_fdt_timer_init(); 205 } 206 #endif 207 208 void 209 arm_fdt_module_init(void) 210 { 211 #ifdef MODULAR 212 const int chosen = OF_finddevice("/chosen"); 213 const char *module_name; 214 const uint64_t *data; 215 u_int index; 216 paddr_t pa; 217 vaddr_t va; 218 int len; 219 220 if (chosen == -1) 221 return; 222 223 data = fdtbus_get_prop(chosen, "netbsd,modules", &len); 224 if (data == NULL) 225 return; 226 227 for (index = 0; index < len / 16; index++, data += 2) { 228 module_name = fdtbus_get_string_index(chosen, 229 "netbsd,module-names", index); 230 if (module_name == NULL) 231 break; 232 233 const paddr_t startpa = (paddr_t)be64dec(data + 0); 234 const size_t size = (size_t)be64dec(data + 1); 235 const paddr_t endpa = round_page(startpa + size); 236 237 const vaddr_t startva = uvm_km_alloc(kernel_map, endpa - startpa, 238 0, UVM_KMF_VAONLY | UVM_KMF_NOWAIT); 239 if (startva == 0) { 240 printf("ERROR: Cannot allocate VA for module %s\n", 241 module_name); 242 continue; 243 } 244 245 for (pa = startpa, va = startva; 246 pa < endpa; 247 pa += PAGE_SIZE, va += PAGE_SIZE) { 248 pmap_kenter_pa(va, pa, VM_PROT_ALL, PMAP_WRITE_BACK); 249 } 250 pmap_update(pmap_kernel()); 251 252 module_prime(module_name, (void *)(uintptr_t)startva, size); 253 } 254 #endif /* !MODULAR */ 255 } 256