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 BUG_ON(!res); 145 prom_early_allocated += chunk_size; 146 memset(res, 0, chunk_size); 147 free_mem = chunk_size; 148 mem = res; 149 } 150 151 /* allocate from the local cache */ 152 free_mem -= size; 153 res = mem; 154 mem += size; 155 return res; 156 } 157 158 static struct of_pdt_ops prom_olpc_ops __initdata = { 159 .nextprop = olpc_dt_nextprop, 160 .getproplen = olpc_dt_getproplen, 161 .getproperty = olpc_dt_getproperty, 162 .getchild = olpc_dt_getchild, 163 .getsibling = olpc_dt_getsibling, 164 .pkg2path = olpc_dt_pkg2path, 165 }; 166 167 static phandle __init olpc_dt_finddevice(const char *path) 168 { 169 phandle node; 170 const void *args[] = { path }; 171 void *res[] = { &node }; 172 173 if (olpc_ofw("finddevice", args, res)) { 174 pr_err("olpc_dt: finddevice failed!\n"); 175 return 0; 176 } 177 178 if ((s32) node == -1) 179 return 0; 180 181 return node; 182 } 183 184 static int __init olpc_dt_interpret(const char *words) 185 { 186 int result; 187 const void *args[] = { words }; 188 void *res[] = { &result }; 189 190 if (olpc_ofw("interpret", args, res)) { 191 pr_err("olpc_dt: interpret failed!\n"); 192 return -1; 193 } 194 195 return result; 196 } 197 198 /* 199 * Extract board revision directly from OFW device tree. 200 * We can't use olpc_platform_info because that hasn't been set up yet. 201 */ 202 static u32 __init olpc_dt_get_board_revision(void) 203 { 204 phandle node; 205 __be32 rev; 206 int r; 207 208 node = olpc_dt_finddevice("/"); 209 if (!node) 210 return 0; 211 212 r = olpc_dt_getproperty(node, "board-revision-int", 213 (char *) &rev, sizeof(rev)); 214 if (r < 0) 215 return 0; 216 217 return be32_to_cpu(rev); 218 } 219 220 void __init olpc_dt_fixup(void) 221 { 222 int r; 223 char buf[64]; 224 phandle node; 225 u32 board_rev; 226 227 node = olpc_dt_finddevice("/battery@0"); 228 if (!node) 229 return; 230 231 /* 232 * If the battery node has a compatible property, we are running a new 233 * enough firmware and don't have fixups to make. 234 */ 235 r = olpc_dt_getproperty(node, "compatible", buf, sizeof(buf)); 236 if (r > 0) 237 return; 238 239 pr_info("PROM DT: Old firmware detected, applying fixes\n"); 240 241 /* Add olpc,xo1-battery compatible marker to battery node */ 242 olpc_dt_interpret("\" /battery@0\" find-device" 243 " \" olpc,xo1-battery\" +compatible" 244 " device-end"); 245 246 board_rev = olpc_dt_get_board_revision(); 247 if (!board_rev) 248 return; 249 250 if (board_rev >= olpc_board_pre(0xd0)) { 251 /* XO-1.5: add dcon device */ 252 olpc_dt_interpret("\" /pci/display@1\" find-device" 253 " new-device" 254 " \" dcon\" device-name \" olpc,xo1-dcon\" +compatible" 255 " finish-device device-end"); 256 } else { 257 /* XO-1: add dcon device, mark RTC as olpc,xo1-rtc */ 258 olpc_dt_interpret("\" /pci/display@1,1\" find-device" 259 " new-device" 260 " \" dcon\" device-name \" olpc,xo1-dcon\" +compatible" 261 " finish-device device-end" 262 " \" /rtc\" find-device" 263 " \" olpc,xo1-rtc\" +compatible" 264 " device-end"); 265 } 266 } 267 268 void __init olpc_dt_build_devicetree(void) 269 { 270 phandle root; 271 272 if (!olpc_ofw_is_installed()) 273 return; 274 275 olpc_dt_fixup(); 276 277 root = olpc_dt_getsibling(0); 278 if (!root) { 279 pr_err("PROM: unable to get root node from OFW!\n"); 280 return; 281 } 282 of_pdt_build_devicetree(root, &prom_olpc_ops); 283 284 pr_info("PROM DT: Built device tree with %u bytes of memory.\n", 285 prom_early_allocated); 286 } 287