1 /* 2 * OLPC-specific OFW device tree support code. 3 * 4 * Paul Mackerras August 1996. 5 * Copyright (C) 1996-2005 Paul Mackerras. 6 * 7 * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. 8 * {engebret|bergner}@us.ibm.com 9 * 10 * Adapted for sparc by David S. Miller davem@davemloft.net 11 * Adapted for x86/OLPC by Andres Salomon <dilinger@queued.net> 12 * 13 * This program is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU General Public License 15 * as published by the Free Software Foundation; either version 16 * 2 of the License, or (at your option) any later version. 17 */ 18 19 #include <linux/kernel.h> 20 #include <linux/memblock.h> 21 #include <linux/of.h> 22 #include <linux/of_pdt.h> 23 #include <asm/olpc.h> 24 #include <asm/olpc_ofw.h> 25 26 static phandle __init olpc_dt_getsibling(phandle node) 27 { 28 const void *args[] = { (void *)node }; 29 void *res[] = { &node }; 30 31 if ((s32)node == -1) 32 return 0; 33 34 if (olpc_ofw("peer", args, res) || (s32)node == -1) 35 return 0; 36 37 return node; 38 } 39 40 static phandle __init olpc_dt_getchild(phandle node) 41 { 42 const void *args[] = { (void *)node }; 43 void *res[] = { &node }; 44 45 if ((s32)node == -1) 46 return 0; 47 48 if (olpc_ofw("child", args, res) || (s32)node == -1) { 49 pr_err("PROM: %s: fetching child failed!\n", __func__); 50 return 0; 51 } 52 53 return node; 54 } 55 56 static int __init olpc_dt_getproplen(phandle node, const char *prop) 57 { 58 const void *args[] = { (void *)node, prop }; 59 int len; 60 void *res[] = { &len }; 61 62 if ((s32)node == -1) 63 return -1; 64 65 if (olpc_ofw("getproplen", args, res)) { 66 pr_err("PROM: %s: getproplen failed!\n", __func__); 67 return -1; 68 } 69 70 return len; 71 } 72 73 static int __init olpc_dt_getproperty(phandle node, const char *prop, 74 char *buf, int bufsize) 75 { 76 int plen; 77 78 plen = olpc_dt_getproplen(node, prop); 79 if (plen > bufsize || plen < 1) { 80 return -1; 81 } else { 82 const void *args[] = { (void *)node, prop, buf, (void *)plen }; 83 void *res[] = { &plen }; 84 85 if (olpc_ofw("getprop", args, res)) { 86 pr_err("PROM: %s: getprop failed!\n", __func__); 87 return -1; 88 } 89 } 90 91 return plen; 92 } 93 94 static int __init olpc_dt_nextprop(phandle node, char *prev, char *buf) 95 { 96 const void *args[] = { (void *)node, prev, buf }; 97 int success; 98 void *res[] = { &success }; 99 100 buf[0] = '\0'; 101 102 if ((s32)node == -1) 103 return -1; 104 105 if (olpc_ofw("nextprop", args, res) || success != 1) 106 return -1; 107 108 return 0; 109 } 110 111 static int __init olpc_dt_pkg2path(phandle node, char *buf, 112 const int buflen, int *len) 113 { 114 const void *args[] = { (void *)node, buf, (void *)buflen }; 115 void *res[] = { len }; 116 117 if ((s32)node == -1) 118 return -1; 119 120 if (olpc_ofw("package-to-path", args, res) || *len < 1) 121 return -1; 122 123 return 0; 124 } 125 126 static unsigned int prom_early_allocated __initdata; 127 128 void * __init prom_early_alloc(unsigned long size) 129 { 130 static u8 *mem; 131 static size_t free_mem; 132 void *res; 133 134 if (free_mem < size) { 135 const size_t chunk_size = max(PAGE_SIZE, size); 136 137 /* 138 * To mimimize the number of allocations, grab at least 139 * PAGE_SIZE of memory (that's an arbitrary choice that's 140 * fast enough on the platforms we care about while minimizing 141 * wasted bootmem) and hand off chunks of it to callers. 142 */ 143 res = memblock_alloc(chunk_size, SMP_CACHE_BYTES); 144 if (!res) 145 panic("%s: Failed to allocate %zu bytes\n", __func__, 146 chunk_size); 147 BUG_ON(!res); 148 prom_early_allocated += chunk_size; 149 memset(res, 0, chunk_size); 150 free_mem = chunk_size; 151 mem = res; 152 } 153 154 /* allocate from the local cache */ 155 free_mem -= size; 156 res = mem; 157 mem += size; 158 return res; 159 } 160 161 static struct of_pdt_ops prom_olpc_ops __initdata = { 162 .nextprop = olpc_dt_nextprop, 163 .getproplen = olpc_dt_getproplen, 164 .getproperty = olpc_dt_getproperty, 165 .getchild = olpc_dt_getchild, 166 .getsibling = olpc_dt_getsibling, 167 .pkg2path = olpc_dt_pkg2path, 168 }; 169 170 static phandle __init olpc_dt_finddevice(const char *path) 171 { 172 phandle node; 173 const void *args[] = { path }; 174 void *res[] = { &node }; 175 176 if (olpc_ofw("finddevice", args, res)) { 177 pr_err("olpc_dt: finddevice failed!\n"); 178 return 0; 179 } 180 181 if ((s32) node == -1) 182 return 0; 183 184 return node; 185 } 186 187 static int __init olpc_dt_interpret(const char *words) 188 { 189 int result; 190 const void *args[] = { words }; 191 void *res[] = { &result }; 192 193 if (olpc_ofw("interpret", args, res)) { 194 pr_err("olpc_dt: interpret failed!\n"); 195 return -1; 196 } 197 198 return result; 199 } 200 201 /* 202 * Extract board revision directly from OFW device tree. 203 * We can't use olpc_platform_info because that hasn't been set up yet. 204 */ 205 static u32 __init olpc_dt_get_board_revision(void) 206 { 207 phandle node; 208 __be32 rev; 209 int r; 210 211 node = olpc_dt_finddevice("/"); 212 if (!node) 213 return 0; 214 215 r = olpc_dt_getproperty(node, "board-revision-int", 216 (char *) &rev, sizeof(rev)); 217 if (r < 0) 218 return 0; 219 220 return be32_to_cpu(rev); 221 } 222 223 int olpc_dt_compatible_match(phandle node, const char *compat) 224 { 225 char buf[64], *p; 226 int plen, len; 227 228 plen = olpc_dt_getproperty(node, "compatible", buf, sizeof(buf)); 229 if (plen <= 0) 230 return 0; 231 232 len = strlen(compat); 233 for (p = buf; p < buf + plen; p += strlen(p) + 1) { 234 if (strcmp(p, compat) == 0) 235 return 1; 236 } 237 238 return 0; 239 } 240 241 void __init olpc_dt_fixup(void) 242 { 243 phandle node; 244 u32 board_rev; 245 246 node = olpc_dt_finddevice("/battery@0"); 247 if (!node) 248 return; 249 250 board_rev = olpc_dt_get_board_revision(); 251 if (!board_rev) 252 return; 253 254 if (board_rev >= olpc_board_pre(0xd0)) { 255 /* XO-1.5 */ 256 257 if (olpc_dt_compatible_match(node, "olpc,xo1.5-battery")) 258 return; 259 260 /* Add olpc,xo1.5-battery compatible marker to battery node */ 261 olpc_dt_interpret("\" /battery@0\" find-device"); 262 olpc_dt_interpret(" \" olpc,xo1.5-battery\" +compatible"); 263 olpc_dt_interpret("device-end"); 264 265 if (olpc_dt_compatible_match(node, "olpc,xo1-battery")) { 266 /* 267 * If we have a olpc,xo1-battery compatible, then we're 268 * running a new enough firmware that already has 269 * the dcon node. 270 */ 271 return; 272 } 273 274 /* Add dcon device */ 275 olpc_dt_interpret("\" /pci/display@1\" find-device"); 276 olpc_dt_interpret(" new-device"); 277 olpc_dt_interpret(" \" dcon\" device-name"); 278 olpc_dt_interpret(" \" olpc,xo1-dcon\" +compatible"); 279 olpc_dt_interpret(" finish-device"); 280 olpc_dt_interpret("device-end"); 281 } else { 282 /* XO-1 */ 283 284 if (olpc_dt_compatible_match(node, "olpc,xo1-battery")) { 285 /* 286 * If we have a olpc,xo1-battery compatible, then we're 287 * running a new enough firmware that already has 288 * the dcon and RTC nodes. 289 */ 290 return; 291 } 292 293 /* Add dcon device, mark RTC as olpc,xo1-rtc */ 294 olpc_dt_interpret("\" /pci/display@1,1\" find-device"); 295 olpc_dt_interpret(" new-device"); 296 olpc_dt_interpret(" \" dcon\" device-name"); 297 olpc_dt_interpret(" \" olpc,xo1-dcon\" +compatible"); 298 olpc_dt_interpret(" finish-device"); 299 olpc_dt_interpret("device-end"); 300 301 olpc_dt_interpret("\" /rtc\" find-device"); 302 olpc_dt_interpret(" \" olpc,xo1-rtc\" +compatible"); 303 olpc_dt_interpret("device-end"); 304 } 305 306 /* Add olpc,xo1-battery compatible marker to battery node */ 307 olpc_dt_interpret("\" /battery@0\" find-device"); 308 olpc_dt_interpret(" \" olpc,xo1-battery\" +compatible"); 309 olpc_dt_interpret("device-end"); 310 } 311 312 void __init olpc_dt_build_devicetree(void) 313 { 314 phandle root; 315 316 if (!olpc_ofw_is_installed()) 317 return; 318 319 olpc_dt_fixup(); 320 321 root = olpc_dt_getsibling(0); 322 if (!root) { 323 pr_err("PROM: unable to get root node from OFW!\n"); 324 return; 325 } 326 of_pdt_build_devicetree(root, &prom_olpc_ops); 327 328 pr_info("PROM DT: Built device tree with %u bytes of memory.\n", 329 prom_early_allocated); 330 } 331