1 /* $OpenBSD: mainbus.c,v 1.13 2016/08/06 00:04:39 jsg Exp $ */ 2 /* 3 * Copyright (c) 2016 Patrick Wildt <patrick@blueri.se> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/kernel.h> 21 #include <sys/device.h> 22 #include <sys/malloc.h> 23 24 #include <dev/ofw/openfirm.h> 25 #include <dev/ofw/fdt.h> 26 27 #include <arm/mainbus/mainbus.h> 28 29 int mainbus_match(struct device *, void *, void *); 30 void mainbus_attach(struct device *, struct device *, void *); 31 32 void mainbus_attach_node(struct device *, int); 33 34 int mainbus_legacy_search(struct device *, void *, void *); 35 36 struct mainbus_softc { 37 struct device sc_dev; 38 bus_space_tag_t sc_iot; 39 bus_dma_tag_t sc_dmat; 40 int sc_acells; 41 int sc_scells; 42 int *sc_ranges; 43 int sc_rangeslen; 44 }; 45 46 struct cfattach mainbus_ca = { 47 sizeof(struct mainbus_softc), mainbus_match, mainbus_attach, NULL, 48 config_activate_children 49 }; 50 51 struct cfdriver mainbus_cd = { 52 NULL, "mainbus", DV_DULL 53 }; 54 55 struct arm32_bus_dma_tag mainbus_dma_tag = { 56 0, 57 0, 58 NULL, 59 _bus_dmamap_create, 60 _bus_dmamap_destroy, 61 _bus_dmamap_load, 62 _bus_dmamap_load_mbuf, 63 _bus_dmamap_load_uio, 64 _bus_dmamap_load_raw, 65 _bus_dmamap_unload, 66 _bus_dmamap_sync, 67 _bus_dmamem_alloc, 68 _bus_dmamem_free, 69 _bus_dmamem_map, 70 _bus_dmamem_unmap, 71 _bus_dmamem_mmap, 72 }; 73 74 /* 75 * Mainbus takes care of FDT and non-FDT machines, so we 76 * always attach. 77 */ 78 int 79 mainbus_match(struct device *parent, void *cfdata, void *aux) 80 { 81 return (1); 82 } 83 84 extern char *hw_prod; 85 86 void 87 mainbus_attach(struct device *parent, struct device *self, void *aux) 88 { 89 struct mainbus_softc *sc = (struct mainbus_softc *)self; 90 char buffer[128]; 91 int node, len; 92 93 if ((node = OF_peer(0)) == 0) { 94 printf(": no device tree\n"); 95 config_search(mainbus_legacy_search, self, aux); 96 return; 97 } 98 99 #ifdef CPU_ARMv7 100 arm_intr_init_fdt(); 101 #endif 102 103 #ifdef CPU_ARMv7 104 extern struct bus_space armv7_bs_tag; 105 sc->sc_iot = &armv7_bs_tag; 106 #endif 107 sc->sc_dmat = &mainbus_dma_tag; 108 sc->sc_acells = OF_getpropint(OF_peer(0), "#address-cells", 1); 109 sc->sc_scells = OF_getpropint(OF_peer(0), "#size-cells", 1); 110 111 if ((len = OF_getprop(node, "model", buffer, sizeof(buffer))) > 0) { 112 printf(": %s\n", buffer); 113 hw_prod = malloc(len, M_DEVBUF, M_NOWAIT); 114 if (hw_prod) 115 strlcpy(hw_prod, buffer, len); 116 } else 117 printf(": unknown model\n"); 118 119 /* Attach CPU first. */ 120 mainbus_legacy_found(self, "cpu"); 121 #ifdef CPU_ARMv7 122 extern void platform_init_mainbus(struct device *); 123 platform_init_mainbus(self); 124 #endif 125 126 /* TODO: Scan for interrupt controllers and attach them first? */ 127 128 sc->sc_rangeslen = OF_getproplen(OF_peer(0), "ranges"); 129 if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) { 130 sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK); 131 OF_getpropintarray(OF_peer(0), "ranges", sc->sc_ranges, 132 sc->sc_rangeslen); 133 } 134 135 /* Scan the whole tree. */ 136 for (node = OF_child(node); 137 node != 0; 138 node = OF_peer(node)) 139 { 140 mainbus_attach_node(self, node); 141 } 142 } 143 144 /* 145 * Look for a driver that wants to be attached to this node. 146 */ 147 void 148 mainbus_attach_node(struct device *self, int node) 149 { 150 struct mainbus_softc *sc = (struct mainbus_softc *)self; 151 struct fdt_attach_args fa; 152 char buffer[128]; 153 int i, len, line; 154 uint32_t *cell, *reg; 155 156 if (!OF_getprop(node, "compatible", buffer, sizeof(buffer))) 157 return; 158 159 if (OF_getprop(node, "status", buffer, sizeof(buffer))) 160 if (!strcmp(buffer, "disabled")) 161 return; 162 163 memset(&fa, 0, sizeof(fa)); 164 fa.fa_name = ""; 165 fa.fa_node = node; 166 fa.fa_iot = sc->sc_iot; 167 fa.fa_dmat = sc->sc_dmat; 168 fa.fa_acells = sc->sc_acells; 169 fa.fa_scells = sc->sc_scells; 170 171 len = OF_getproplen(node, "reg"); 172 line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t); 173 if (len > 0 && (len % line) == 0) { 174 reg = malloc(len, M_TEMP, M_WAITOK); 175 OF_getpropintarray(node, "reg", reg, len); 176 177 fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg), 178 M_DEVBUF, M_WAITOK); 179 fa.fa_nreg = (len / line); 180 181 for (i = 0, cell = reg; i < len / line; i++) { 182 if (sc->sc_acells >= 1) 183 fa.fa_reg[i].addr = cell[0]; 184 if (sc->sc_acells == 2) { 185 fa.fa_reg[i].addr <<= 32; 186 fa.fa_reg[i].addr |= cell[1]; 187 } 188 cell += sc->sc_acells; 189 if (sc->sc_scells >= 1) 190 fa.fa_reg[i].size = cell[0]; 191 if (sc->sc_scells == 2) { 192 fa.fa_reg[i].size <<= 32; 193 fa.fa_reg[i].size |= cell[1]; 194 } 195 cell += sc->sc_scells; 196 } 197 198 free(reg, M_TEMP, len); 199 } 200 201 len = OF_getproplen(node, "interrupts"); 202 if (len > 0 && (len % sizeof(uint32_t)) == 0) { 203 fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK); 204 fa.fa_nintr = len / sizeof(uint32_t); 205 206 OF_getpropintarray(node, "interrupts", fa.fa_intr, len); 207 } 208 209 /* TODO: attach the device's clocks first? */ 210 211 config_found(self, &fa, NULL); 212 213 free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg)); 214 free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t)); 215 } 216 217 /* 218 * Legacy support for SoCs that do not use FDT. 219 */ 220 int 221 mainbus_legacy_search(struct device *parent, void *match, void *aux) 222 { 223 union mainbus_attach_args ma; 224 struct cfdata *cf = match; 225 226 memset(&ma, 0, sizeof(ma)); 227 ma.ma_name = cf->cf_driver->cd_name; 228 229 /* allow for devices to be disabled in UKC */ 230 if ((*cf->cf_attach->ca_match)(parent, cf, &ma) == 0) 231 return 0; 232 233 config_attach(parent, cf, &ma, NULL); 234 return 1; 235 } 236 237 void 238 mainbus_legacy_found(struct device *self, char *name) 239 { 240 union mainbus_attach_args ma; 241 242 memset(&ma, 0, sizeof(ma)); 243 ma.ma_name = name; 244 245 config_found(self, &ma, NULL); 246 } 247