ofw_machdep.c revision 166096
11553Srgrimes/*- 21553Srgrimes * Copyright (c) 2001 by Thomas Moestl <tmm@FreeBSD.org>. 31553Srgrimes * Copyright (c) 2005 by Marius Strobl <marius@FreeBSD.org>. 41553Srgrimes * All rights reserved. 51553Srgrimes * 61553Srgrimes * Redistribution and use in source and binary forms, with or without 71553Srgrimes * modification, are permitted provided that the following conditions 81553Srgrimes * are met: 91553Srgrimes * 1. Redistributions of source code must retain the above copyright 101553Srgrimes * notice, this list of conditions and the following disclaimer. 111553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 121553Srgrimes * notice, this list of conditions and the following disclaimer in the 131553Srgrimes * documentation and/or other materials provided with the distribution. 141553Srgrimes * 151553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 161553Srgrimes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 171553Srgrimes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 181553Srgrimes * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 191553Srgrimes * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 201553Srgrimes * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 211553Srgrimes * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 221553Srgrimes * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 231553Srgrimes * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 241553Srgrimes * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 251553Srgrimes */ 261553Srgrimes 271553Srgrimes#include <sys/cdefs.h> 281553Srgrimes__FBSDID("$FreeBSD: head/sys/sparc64/sparc64/ofw_machdep.c 166096 2007-01-18 18:32:26Z marius $"); 291553Srgrimes 301553Srgrimes/* 311553Srgrimes * Some Open Firmware helper functions that are likely machine dependent. 321553Srgrimes */ 331553Srgrimes 341553Srgrimes#include <sys/param.h> 351553Srgrimes#include <sys/bus.h> 361553Srgrimes#include <sys/systm.h> 371553Srgrimes 381553Srgrimes#include <net/ethernet.h> 391553Srgrimes 401553Srgrimes#include <dev/ofw/ofw_bus.h> 411553Srgrimes#include <dev/ofw/ofw_pci.h> 421553Srgrimes#include <dev/ofw/openfirm.h> 431553Srgrimes 441553Srgrimes#include <machine/bus.h> 451553Srgrimes#include <machine/idprom.h> 461553Srgrimes#include <machine/ofw_bus.h> 471553Srgrimes#include <machine/ofw_machdep.h> 481553Srgrimes 491553Srgrimesvoid 501553SrgrimesOF_getetheraddr(device_t dev, u_char *addr) 511553Srgrimes{ 521553Srgrimes char buf[sizeof("true")]; 531553Srgrimes phandle_t node; 54 struct idprom idp; 55 56 if ((node = OF_finddevice("/options")) > 0 && 57 OF_getprop(node, "local-mac-address?", buf, sizeof(buf)) > 0) { 58 buf[sizeof(buf) - 1] = '\0'; 59 if (strcmp(buf, "true") == 0 && 60 (node = ofw_bus_get_node(dev)) > 0 && 61 OF_getprop(node, "local-mac-address", addr, 62 ETHER_ADDR_LEN) == ETHER_ADDR_LEN) 63 return; 64 } 65 66 node = OF_peer(0); 67 if (node <= 0 || OF_getprop(node, "idprom", &idp, sizeof(idp)) == -1) 68 panic("Could not determine the machine ethernet address"); 69 bcopy(&idp.id_ether, addr, ETHER_ADDR_LEN); 70} 71 72static __inline uint32_t 73phys_hi_mask_space(const char *bus, uint32_t phys_hi) 74{ 75 uint32_t space; 76 77 space = phys_hi; 78 if (strcmp(bus, "ebus") == 0 || strcmp(bus, "isa") == 0) 79 space &= 0x1; 80 else if (strcmp(bus, "pci") == 0) 81 space &= OFW_PCI_PHYS_HI_SPACEMASK; 82 /* The phys.hi cells of the other busses only contain space bits. */ 83 return (space); 84} 85 86/* 87 * Return the physical address and the bus space to use for a node 88 * referenced by its package handle and the index of the register bank 89 * to decode. Intended to be used to together with sparc64_fake_bustag() 90 * by console drivers in early boot only. 91 * Works by mapping the address of the node's bank given in the address 92 * space of its parent upward in the device tree at each bridge along the 93 * path. 94 * Currently only really deals with max. 64-bit addresses, i.e. addresses 95 * consisting of max. 2 phys cells (phys.hi and phys.lo). If we encounter 96 * a 3 phys cells address (as with PCI addresses) we assume phys.hi can 97 * be ignored except for the space bits (generally contained in phys.hi) 98 * and treat phys.mid as phys.hi. 99 */ 100int 101OF_decode_addr(phandle_t node, int bank, int *space, bus_addr_t *addr) 102{ 103 char name[32]; 104 uint64_t cend, cstart, end, phys, sz, start; 105 pcell_t addrc, szc, paddrc; 106 phandle_t bus, lbus, pbus; 107 uint32_t banks[10 * 5]; /* 10 PCI banks */ 108 uint32_t cspace, spc; 109 int i, j, nbank; 110 111 /* 112 * In general the addresses are contained in the "reg" property 113 * of a node. The first address in the "reg" property of a PCI 114 * node however is the address of its configuration registers in 115 * the configuration space of the host bridge. Additional entries 116 * denote the memory and I/O addresses. For relocatable addresses 117 * the "reg" property contains the BAR, for non-relocatable 118 * addresses it contains the absolute PCI address. The PCI-only 119 * "assigned-addresses" property however always contains the 120 * absolute PCI addresses. 121 * The "assigned-addresses" and "reg" properties are arrays of 122 * address structures consisting of #address-cells 32-bit phys 123 * cells and #size-cells 32-bit size cells. If a parent lacks 124 * the "#address-cells" or "#size-cells" property the default 125 * for #address-cells to use is 2 and for #size-cells 1. 126 */ 127 bus = OF_parent(node); 128 if (bus == 0) 129 return (ENXIO); 130 if (OF_getprop(bus, "name", name, sizeof(name)) == -1) 131 return (ENXIO); 132 name[sizeof(name) - 1] = '\0'; 133 if (OF_getprop(bus, "#address-cells", &addrc, sizeof(addrc)) == -1) 134 addrc = 2; 135 if (OF_getprop(bus, "#size-cells", &szc, sizeof(szc)) == -1) 136 szc = 1; 137 if (addrc < 2 || addrc > 3 || szc < 1 || szc > 2) 138 return (ENXIO); 139 if (strcmp(name, "pci") == 0) { 140 if (addrc > 3) 141 return (ENXIO); 142 nbank = OF_getprop(node, "assigned-addresses", &banks, 143 sizeof(banks)); 144 } else { 145 if (addrc > 2) 146 return (ENXIO); 147 nbank = OF_getprop(node, "reg", &banks, sizeof(banks)); 148 } 149 if (nbank == -1) 150 return (ENXIO); 151 nbank /= sizeof(banks[0]) * (addrc + szc); 152 if (bank < 0 || bank > nbank - 1) 153 return (ENXIO); 154 phys = 0; 155 for (i = 0; i < MIN(2, addrc); i++) 156 phys |= (uint64_t)banks[(addrc + szc) * bank + addrc - 2 + i] << 157 32 * (MIN(2, addrc) - i - 1); 158 sz = 0; 159 for (i = 0; i < szc; i++) 160 sz |= (uint64_t)banks[(addrc + szc) * bank + addrc + i] << 161 32 * (szc - i - 1); 162 start = phys; 163 end = phys + sz - 1; 164 spc = phys_hi_mask_space(name, banks[(addrc + szc) * bank]); 165 166 /* 167 * Map upward in the device tree at every bridge we encounter 168 * using their "ranges" properties. 169 * The "ranges" property of a bridge is an array of a structure 170 * consisting of that bridge's #address-cells 32-bit child-phys 171 * cells, its parent bridge #address-cells 32-bit parent-phys 172 * cells and that bridge's #size-cells 32-bit size cells. 173 * If a bridge doesn't have a "ranges" property no mapping is 174 * necessary at that bridge. 175 */ 176 cspace = 0; 177 lbus = bus; 178 while ((pbus = OF_parent(bus)) != 0) { 179 if (OF_getprop(pbus, "#address-cells", &paddrc, 180 sizeof(paddrc)) == -1) 181 paddrc = 2; 182 if (paddrc < 2 || paddrc > 3) 183 return (ENXIO); 184 nbank = OF_getprop(bus, "ranges", &banks, sizeof(banks)); 185 if (nbank == -1) { 186 if (OF_getprop(pbus, "name", name, sizeof(name)) == -1) 187 return (ENXIO); 188 name[sizeof(name) - 1] = '\0'; 189 goto skip; 190 } 191 if (lbus != bus) { 192 if (OF_getprop(bus, "#size-cells", &szc, 193 sizeof(szc)) == -1) 194 szc = 1; 195 if (szc < 1 || szc > 2) 196 return (ENXIO); 197 } 198 nbank /= sizeof(banks[0]) * (addrc + paddrc + szc); 199 for (i = 0; i < nbank; i++) { 200 cspace = phys_hi_mask_space(name, 201 banks[(addrc + paddrc + szc) * i]); 202 if (cspace != spc) 203 continue; 204 phys = 0; 205 for (j = 0; j < MIN(2, addrc); j++) 206 phys |= (uint64_t)banks[ 207 (addrc + paddrc + szc) * i + 208 addrc - 2 + j] << 209 32 * (MIN(2, addrc) - j - 1); 210 sz = 0; 211 for (j = 0; j < szc; j++) 212 sz |= (uint64_t)banks[ 213 (addrc + paddrc + szc) * i + addrc + 214 paddrc + j] << 215 32 * (szc - j - 1); 216 cstart = phys; 217 cend = phys + sz - 1; 218 if (start < cstart || start > cend) 219 continue; 220 if (end < cstart || end > cend) 221 return (ENXIO); 222 phys = 0; 223 for (j = 0; j < MIN(2, paddrc); j++) 224 phys |= (uint64_t)banks[ 225 (addrc + paddrc + szc) * i + addrc + 226 paddrc - 2 + j] << 227 32 * (MIN(2, paddrc) - j - 1); 228 start += phys - cstart; 229 end += phys - cstart; 230 if (OF_getprop(pbus, "name", name, sizeof(name)) == -1) 231 return (ENXIO); 232 name[sizeof(name) - 1] = '\0'; 233 spc = phys_hi_mask_space(name, 234 banks[(addrc + paddrc + szc) * i + addrc]); 235 break; 236 } 237 if (i == nbank) 238 return (ENXIO); 239 skip: 240 addrc = paddrc; 241 lbus = bus; 242 bus = pbus; 243 } 244 245 /* Done with mapping. Return the bus space as used by FreeBSD. */ 246 *addr = start; 247 if (OF_parent(lbus) == 0) { 248 *space = NEXUS_BUS_SPACE; 249 return (0); 250 } 251 if (OF_getprop(lbus, "name", name, sizeof(name)) == -1) 252 return (ENXIO); 253 name[sizeof(name) - 1] = '\0'; 254 if (strcmp(name, "central") == 0 || strcmp(name, "upa") == 0) { 255 *space = NEXUS_BUS_SPACE; 256 return (0); 257 } else if (strcmp(name, "pci") == 0) { 258 switch (cspace) { 259 case OFW_PCI_PHYS_HI_SPACE_IO: 260 *space = PCI_IO_BUS_SPACE; 261 return (0); 262 case OFW_PCI_PHYS_HI_SPACE_MEM32: 263 *space = PCI_MEMORY_BUS_SPACE; 264 return (0); 265 } 266 } else if (strcmp(name, "sbus") == 0) { 267 *space = SBUS_BUS_SPACE; 268 return (0); 269 } 270 return (ENXIO); 271} 272