1 /* $OpenBSD: mainbus.c,v 1.20 2019/09/21 15:57:03 kettenis 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 0, 64 0, 65 NULL, 66 _bus_dmamap_create, 67 _bus_dmamap_destroy, 68 _bus_dmamap_load, 69 _bus_dmamap_load_mbuf, 70 _bus_dmamap_load_uio, 71 _bus_dmamap_load_raw, 72 _bus_dmamap_unload, 73 _bus_dmamap_sync, 74 _bus_dmamem_alloc, 75 _bus_dmamem_free, 76 _bus_dmamem_map, 77 _bus_dmamem_unmap, 78 _bus_dmamem_mmap, 79 }; 80 81 /* 82 * Mainbus takes care of FDT and non-FDT machines, so we 83 * always attach. 84 */ 85 int 86 mainbus_match(struct device *parent, void *cfdata, void *aux) 87 { 88 return (1); 89 } 90 91 extern char *hw_prod; 92 extern struct bus_space armv7_bs_tag; 93 void platform_init_mainbus(struct device *); 94 95 void 96 mainbus_attach(struct device *parent, struct device *self, void *aux) 97 { 98 struct mainbus_softc *sc = (struct mainbus_softc *)self; 99 char model[128]; 100 int node, len; 101 102 arm_intr_init_fdt(); 103 104 sc->sc_node = OF_peer(0); 105 sc->sc_iot = &armv7_bs_tag; 106 sc->sc_dmat = &mainbus_dma_tag; 107 sc->sc_acells = OF_getpropint(OF_peer(0), "#address-cells", 1); 108 sc->sc_scells = OF_getpropint(OF_peer(0), "#size-cells", 1); 109 110 len = OF_getprop(sc->sc_node, "model", model, sizeof(model)); 111 if (len > 0) { 112 printf(": %s\n", model); 113 hw_prod = malloc(len, M_DEVBUF, M_NOWAIT); 114 if (hw_prod) 115 strlcpy(hw_prod, model, len); 116 } else 117 printf(": unknown model\n"); 118 119 /* Attach primary CPU first. */ 120 mainbus_attach_cpus(self, mainbus_match_primary); 121 122 platform_init_mainbus(self); 123 124 sc->sc_rangeslen = OF_getproplen(OF_peer(0), "ranges"); 125 if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) { 126 sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK); 127 OF_getpropintarray(OF_peer(0), "ranges", sc->sc_ranges, 128 sc->sc_rangeslen); 129 } 130 131 /* Scan the whole tree. */ 132 sc->sc_early = 1; 133 for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node)) 134 mainbus_attach_node(self, node, NULL); 135 136 sc->sc_early = 0; 137 for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node)) 138 mainbus_attach_node(self, node, NULL); 139 140 mainbus_attach_framebuffer(self); 141 142 /* Attach secondary CPUs. */ 143 mainbus_attach_cpus(self, mainbus_match_secondary); 144 145 thermal_init(); 146 } 147 148 /* 149 * Look for a driver that wants to be attached to this node. 150 */ 151 void 152 mainbus_attach_node(struct device *self, int node, cfmatch_t submatch) 153 { 154 struct mainbus_softc *sc = (struct mainbus_softc *)self; 155 struct fdt_attach_args fa; 156 int i, len, line; 157 uint32_t *cell, *reg; 158 159 memset(&fa, 0, sizeof(fa)); 160 fa.fa_name = ""; 161 fa.fa_node = node; 162 fa.fa_iot = sc->sc_iot; 163 fa.fa_dmat = sc->sc_dmat; 164 fa.fa_acells = sc->sc_acells; 165 fa.fa_scells = sc->sc_scells; 166 167 len = OF_getproplen(node, "reg"); 168 line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t); 169 if (len > 0 && (len % line) == 0) { 170 reg = malloc(len, M_TEMP, M_WAITOK); 171 OF_getpropintarray(node, "reg", reg, len); 172 173 fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg), 174 M_DEVBUF, M_WAITOK); 175 fa.fa_nreg = (len / line); 176 177 for (i = 0, cell = reg; i < len / line; i++) { 178 if (sc->sc_acells >= 1) 179 fa.fa_reg[i].addr = cell[0]; 180 if (sc->sc_acells == 2) { 181 fa.fa_reg[i].addr <<= 32; 182 fa.fa_reg[i].addr |= cell[1]; 183 } 184 cell += sc->sc_acells; 185 if (sc->sc_scells >= 1) 186 fa.fa_reg[i].size = cell[0]; 187 if (sc->sc_scells == 2) { 188 fa.fa_reg[i].size <<= 32; 189 fa.fa_reg[i].size |= cell[1]; 190 } 191 cell += sc->sc_scells; 192 } 193 194 free(reg, M_TEMP, len); 195 } 196 197 len = OF_getproplen(node, "interrupts"); 198 if (len > 0 && (len % sizeof(uint32_t)) == 0) { 199 fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK); 200 fa.fa_nintr = len / sizeof(uint32_t); 201 202 OF_getpropintarray(node, "interrupts", fa.fa_intr, len); 203 } 204 205 if (submatch == NULL) 206 submatch = mainbus_match_status; 207 config_found_sm(self, &fa, NULL, submatch); 208 209 free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg)); 210 free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t)); 211 } 212 213 int 214 mainbus_match_status(struct device *parent, void *match, void *aux) 215 { 216 struct mainbus_softc *sc = (struct mainbus_softc *)parent; 217 struct fdt_attach_args *fa = aux; 218 struct cfdata *cf = match; 219 char buf[32]; 220 221 if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 && 222 strcmp(buf, "disabled") == 0) 223 return 0; 224 225 if (cf->cf_loc[0] == sc->sc_early) 226 return (*cf->cf_attach->ca_match)(parent, match, aux); 227 return 0; 228 } 229 230 void 231 mainbus_attach_cpus(struct device *self, cfmatch_t match) 232 { 233 struct mainbus_softc *sc = (struct mainbus_softc *)self; 234 int node = OF_finddevice("/cpus"); 235 int acells, scells; 236 237 if (node == 0) 238 return; 239 240 acells = sc->sc_acells; 241 scells = sc->sc_scells; 242 sc->sc_acells = OF_getpropint(node, "#address-cells", 1); 243 sc->sc_scells = OF_getpropint(node, "#size-cells", 0); 244 245 for (node = OF_child(node); node != 0; node = OF_peer(node)) 246 mainbus_attach_node(self, node, match); 247 248 sc->sc_acells = acells; 249 sc->sc_scells = scells; 250 } 251 252 int 253 mainbus_match_primary(struct device *parent, void *match, void *aux) 254 { 255 struct fdt_attach_args *fa = aux; 256 struct cfdata *cf = match; 257 uint32_t mpidr; 258 259 __asm volatile("mrc p15, 0, %0, c0, c0, 5" : "=r" (mpidr)); 260 261 if (fa->fa_nreg < 1 || fa->fa_reg[0].addr != (mpidr & MPIDR_AFF)) 262 return 0; 263 264 return (*cf->cf_attach->ca_match)(parent, match, aux); 265 } 266 267 int 268 mainbus_match_secondary(struct device *parent, void *match, void *aux) 269 { 270 struct fdt_attach_args *fa = aux; 271 struct cfdata *cf = match; 272 uint32_t mpidr; 273 274 __asm volatile("mrc p15, 0, %0, c0, c0, 5" : "=r" (mpidr)); 275 276 if (fa->fa_nreg < 1 || fa->fa_reg[0].addr == (mpidr & MPIDR_AFF)) 277 return 0; 278 279 return (*cf->cf_attach->ca_match)(parent, match, aux); 280 } 281 282 void 283 mainbus_attach_framebuffer(struct device *self) 284 { 285 int node = OF_finddevice("/chosen"); 286 287 if (node == 0) 288 return; 289 290 for (node = OF_child(node); node != 0; node = OF_peer(node)) 291 mainbus_attach_node(self, node, NULL); 292 } 293 294 /* 295 * Legacy support for SoCs that do not fully use FDT. 296 */ 297 void 298 mainbus_legacy_found(struct device *self, char *name) 299 { 300 union mainbus_attach_args ma; 301 302 memset(&ma, 0, sizeof(ma)); 303 ma.ma_name = name; 304 305 config_found(self, &ma, NULL); 306 } 307