1 /* $OpenBSD: mainbus.c,v 1.23 2020/08/26 03:29:05 visa Exp $ */ 2 /* 3 * Copyright (c) 2016 Patrick Wildt <patrick@blueri.se> 4 * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/kernel.h> 22 #include <sys/device.h> 23 #include <sys/malloc.h> 24 25 #include <dev/ofw/openfirm.h> 26 #include <dev/ofw/fdt.h> 27 #include <dev/ofw/ofw_thermal.h> 28 29 #include <arm/mainbus/mainbus.h> 30 31 int mainbus_match(struct device *, void *, void *); 32 void mainbus_attach(struct device *, struct device *, void *); 33 34 void mainbus_attach_node(struct device *, int, cfmatch_t); 35 int mainbus_match_status(struct device *, void *, void *); 36 void mainbus_attach_cpus(struct device *, cfmatch_t); 37 int mainbus_match_primary(struct device *, void *, void *); 38 int mainbus_match_secondary(struct device *, void *, void *); 39 void mainbus_attach_framebuffer(struct device *); 40 41 struct mainbus_softc { 42 struct device sc_dev; 43 int sc_node; 44 bus_space_tag_t sc_iot; 45 bus_dma_tag_t sc_dmat; 46 int sc_acells; 47 int sc_scells; 48 int *sc_ranges; 49 int sc_rangeslen; 50 int sc_early; 51 }; 52 53 struct cfattach mainbus_ca = { 54 sizeof(struct mainbus_softc), mainbus_match, mainbus_attach, NULL, 55 config_activate_children 56 }; 57 58 struct cfdriver mainbus_cd = { 59 NULL, "mainbus", DV_DULL 60 }; 61 62 struct arm32_bus_dma_tag mainbus_dma_tag = { 63 NULL, 64 _bus_dmamap_create, 65 _bus_dmamap_destroy, 66 _bus_dmamap_load, 67 _bus_dmamap_load_mbuf, 68 _bus_dmamap_load_uio, 69 _bus_dmamap_load_raw, 70 _bus_dmamap_load_buffer, 71 _bus_dmamap_unload, 72 _bus_dmamap_sync, 73 _bus_dmamem_alloc, 74 _bus_dmamem_free, 75 _bus_dmamem_map, 76 _bus_dmamem_unmap, 77 _bus_dmamem_mmap, 78 }; 79 80 /* 81 * Mainbus takes care of FDT and non-FDT machines, so we 82 * always attach. 83 */ 84 int 85 mainbus_match(struct device *parent, void *cfdata, void *aux) 86 { 87 return (1); 88 } 89 90 extern struct bus_space armv7_bs_tag; 91 void platform_init_mainbus(struct device *); 92 93 void 94 mainbus_attach(struct device *parent, struct device *self, void *aux) 95 { 96 struct mainbus_softc *sc = (struct mainbus_softc *)self; 97 char prop[128]; 98 int node, len; 99 100 arm_intr_init_fdt(); 101 102 sc->sc_node = OF_peer(0); 103 sc->sc_iot = &armv7_bs_tag; 104 sc->sc_dmat = &mainbus_dma_tag; 105 sc->sc_acells = OF_getpropint(OF_peer(0), "#address-cells", 1); 106 sc->sc_scells = OF_getpropint(OF_peer(0), "#size-cells", 1); 107 108 len = OF_getprop(sc->sc_node, "model", prop, sizeof(prop)); 109 if (len > 0) { 110 printf(": %s\n", prop); 111 hw_prod = malloc(len, M_DEVBUF, M_NOWAIT); 112 if (hw_prod) 113 strlcpy(hw_prod, prop, len); 114 } else 115 printf(": unknown model\n"); 116 117 len = OF_getprop(sc->sc_node, "serial-number", prop, sizeof(prop)); 118 if (len > 0) { 119 hw_serial = malloc(len, M_DEVBUF, M_NOWAIT); 120 if (hw_serial) 121 strlcpy(hw_serial, prop, len); 122 } 123 124 /* Attach primary CPU first. */ 125 mainbus_attach_cpus(self, mainbus_match_primary); 126 127 platform_init_mainbus(self); 128 129 sc->sc_rangeslen = OF_getproplen(OF_peer(0), "ranges"); 130 if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) { 131 sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK); 132 OF_getpropintarray(OF_peer(0), "ranges", sc->sc_ranges, 133 sc->sc_rangeslen); 134 } 135 136 /* Scan the whole tree. */ 137 sc->sc_early = 1; 138 for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node)) 139 mainbus_attach_node(self, node, NULL); 140 141 sc->sc_early = 0; 142 for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node)) 143 mainbus_attach_node(self, node, NULL); 144 145 mainbus_attach_framebuffer(self); 146 147 /* Attach secondary CPUs. */ 148 mainbus_attach_cpus(self, mainbus_match_secondary); 149 150 thermal_init(); 151 } 152 153 /* 154 * Look for a driver that wants to be attached to this node. 155 */ 156 void 157 mainbus_attach_node(struct device *self, int node, cfmatch_t submatch) 158 { 159 struct mainbus_softc *sc = (struct mainbus_softc *)self; 160 struct fdt_attach_args fa; 161 int i, len, line; 162 uint32_t *cell, *reg; 163 164 memset(&fa, 0, sizeof(fa)); 165 fa.fa_name = ""; 166 fa.fa_node = node; 167 fa.fa_iot = sc->sc_iot; 168 fa.fa_dmat = sc->sc_dmat; 169 fa.fa_acells = sc->sc_acells; 170 fa.fa_scells = sc->sc_scells; 171 172 len = OF_getproplen(node, "reg"); 173 line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t); 174 if (len > 0 && (len % line) == 0) { 175 reg = malloc(len, M_TEMP, M_WAITOK); 176 OF_getpropintarray(node, "reg", reg, len); 177 178 fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg), 179 M_DEVBUF, M_WAITOK); 180 fa.fa_nreg = (len / line); 181 182 for (i = 0, cell = reg; i < len / line; i++) { 183 if (sc->sc_acells >= 1) 184 fa.fa_reg[i].addr = cell[0]; 185 if (sc->sc_acells == 2) { 186 fa.fa_reg[i].addr <<= 32; 187 fa.fa_reg[i].addr |= cell[1]; 188 } 189 cell += sc->sc_acells; 190 if (sc->sc_scells >= 1) 191 fa.fa_reg[i].size = cell[0]; 192 if (sc->sc_scells == 2) { 193 fa.fa_reg[i].size <<= 32; 194 fa.fa_reg[i].size |= cell[1]; 195 } 196 cell += sc->sc_scells; 197 } 198 199 free(reg, M_TEMP, len); 200 } 201 202 len = OF_getproplen(node, "interrupts"); 203 if (len > 0 && (len % sizeof(uint32_t)) == 0) { 204 fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK); 205 fa.fa_nintr = len / sizeof(uint32_t); 206 207 OF_getpropintarray(node, "interrupts", fa.fa_intr, len); 208 } 209 210 if (submatch == NULL) 211 submatch = mainbus_match_status; 212 config_found_sm(self, &fa, NULL, submatch); 213 214 free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg)); 215 free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t)); 216 } 217 218 int 219 mainbus_match_status(struct device *parent, void *match, void *aux) 220 { 221 struct mainbus_softc *sc = (struct mainbus_softc *)parent; 222 struct fdt_attach_args *fa = aux; 223 struct cfdata *cf = match; 224 char buf[32]; 225 226 if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 && 227 strcmp(buf, "disabled") == 0) 228 return 0; 229 230 if (cf->cf_loc[0] == sc->sc_early) 231 return (*cf->cf_attach->ca_match)(parent, match, aux); 232 return 0; 233 } 234 235 void 236 mainbus_attach_cpus(struct device *self, cfmatch_t match) 237 { 238 struct mainbus_softc *sc = (struct mainbus_softc *)self; 239 int node = OF_finddevice("/cpus"); 240 int acells, scells; 241 242 if (node == 0) 243 return; 244 245 acells = sc->sc_acells; 246 scells = sc->sc_scells; 247 sc->sc_acells = OF_getpropint(node, "#address-cells", 1); 248 sc->sc_scells = OF_getpropint(node, "#size-cells", 0); 249 250 for (node = OF_child(node); node != 0; node = OF_peer(node)) 251 mainbus_attach_node(self, node, match); 252 253 sc->sc_acells = acells; 254 sc->sc_scells = scells; 255 } 256 257 int 258 mainbus_match_primary(struct device *parent, void *match, void *aux) 259 { 260 struct fdt_attach_args *fa = aux; 261 struct cfdata *cf = match; 262 uint32_t mpidr; 263 264 __asm volatile("mrc p15, 0, %0, c0, c0, 5" : "=r" (mpidr)); 265 266 if (fa->fa_nreg < 1 || fa->fa_reg[0].addr != (mpidr & MPIDR_AFF)) 267 return 0; 268 269 return (*cf->cf_attach->ca_match)(parent, match, aux); 270 } 271 272 int 273 mainbus_match_secondary(struct device *parent, void *match, void *aux) 274 { 275 struct fdt_attach_args *fa = aux; 276 struct cfdata *cf = match; 277 uint32_t mpidr; 278 279 __asm volatile("mrc p15, 0, %0, c0, c0, 5" : "=r" (mpidr)); 280 281 if (fa->fa_nreg < 1 || fa->fa_reg[0].addr == (mpidr & MPIDR_AFF)) 282 return 0; 283 284 return (*cf->cf_attach->ca_match)(parent, match, aux); 285 } 286 287 void 288 mainbus_attach_framebuffer(struct device *self) 289 { 290 int node = OF_finddevice("/chosen"); 291 292 if (node == 0) 293 return; 294 295 for (node = OF_child(node); node != 0; node = OF_peer(node)) 296 mainbus_attach_node(self, node, NULL); 297 } 298 299 /* 300 * Legacy support for SoCs that do not fully use FDT. 301 */ 302 void 303 mainbus_legacy_found(struct device *self, char *name) 304 { 305 union mainbus_attach_args ma; 306 307 memset(&ma, 0, sizeof(ma)); 308 ma.ma_name = name; 309 310 config_found(self, &ma, NULL); 311 } 312