1 /* 2 * devtree.c - convenience functions for device tree manipulation 3 * Copyright 2007 David Gibson, IBM Corporation. 4 * Copyright (c) 2007 Freescale Semiconductor, Inc. 5 * 6 * Authors: David Gibson <[email protected]> 7 * Scott Wood <[email protected]> 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License 11 * as published by the Free Software Foundation; either version 12 * 2 of the License, or (at your option) any later version. 13 */ 14 #include <stdarg.h> 15 #include <stddef.h> 16 #include "types.h" 17 #include "string.h" 18 #include "stdio.h" 19 #include "ops.h" 20 21 void dt_fixup_memory(u64 start, u64 size) 22 { 23 void *root, *memory; 24 int naddr, nsize, i; 25 u32 memreg[4]; 26 27 root = finddevice("/"); 28 if (getprop(root, "#address-cells", &naddr, sizeof(naddr)) < 0) 29 naddr = 2; 30 if (naddr < 1 || naddr > 2) 31 fatal("Can't cope with #address-cells == %d in /\n\r", naddr); 32 33 if (getprop(root, "#size-cells", &nsize, sizeof(nsize)) < 0) 34 nsize = 1; 35 if (nsize < 1 || nsize > 2) 36 fatal("Can't cope with #size-cells == %d in /\n\r", nsize); 37 38 i = 0; 39 if (naddr == 2) 40 memreg[i++] = start >> 32; 41 memreg[i++] = start & 0xffffffff; 42 if (nsize == 2) 43 memreg[i++] = size >> 32; 44 memreg[i++] = size & 0xffffffff; 45 46 memory = finddevice("/memory"); 47 if (! memory) { 48 memory = create_node(NULL, "memory"); 49 setprop_str(memory, "device_type", "memory"); 50 } 51 52 printf("Memory <- <0x%x", memreg[0]); 53 for (i = 1; i < (naddr + nsize); i++) 54 printf(" 0x%x", memreg[i]); 55 printf("> (%ldMB)\n\r", (unsigned long)(size >> 20)); 56 57 setprop(memory, "reg", memreg, (naddr + nsize)*sizeof(u32)); 58 } 59 60 #define MHZ(x) ((x + 500000) / 1000000) 61 62 void dt_fixup_cpu_clocks(u32 cpu, u32 tb, u32 bus) 63 { 64 void *devp = NULL; 65 66 printf("CPU clock-frequency <- 0x%x (%dMHz)\n\r", cpu, MHZ(cpu)); 67 printf("CPU timebase-frequency <- 0x%x (%dMHz)\n\r", tb, MHZ(tb)); 68 if (bus > 0) 69 printf("CPU bus-frequency <- 0x%x (%dMHz)\n\r", bus, MHZ(bus)); 70 71 while ((devp = find_node_by_devtype(devp, "cpu"))) { 72 setprop_val(devp, "clock-frequency", cpu); 73 setprop_val(devp, "timebase-frequency", tb); 74 if (bus > 0) 75 setprop_val(devp, "bus-frequency", bus); 76 } 77 } 78 79 void dt_fixup_clock(const char *path, u32 freq) 80 { 81 void *devp = finddevice(path); 82 83 if (devp) { 84 printf("%s: clock-frequency <- %x (%dMHz)\n\r", path, freq, MHZ(freq)); 85 setprop_val(devp, "clock-frequency", freq); 86 } 87 } 88 89 void __dt_fixup_mac_addresses(u32 startindex, ...) 90 { 91 va_list ap; 92 u32 index = startindex; 93 void *devp; 94 const u8 *addr; 95 96 va_start(ap, startindex); 97 while ((addr = va_arg(ap, const u8 *))) { 98 devp = find_node_by_prop_value(NULL, "linux,network-index", 99 (void*)&index, sizeof(index)); 100 101 printf("ENET%d: local-mac-address <-" 102 " %02x:%02x:%02x:%02x:%02x:%02x\n\r", index, 103 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); 104 105 if (devp) 106 setprop(devp, "local-mac-address", addr, 6); 107 108 index++; 109 } 110 va_end(ap); 111 } 112 113 #define MAX_ADDR_CELLS 4 114 #define MAX_RANGES 8 115 116 static void get_reg_format(void *node, u32 *naddr, u32 *nsize) 117 { 118 if (getprop(node, "#address-cells", naddr, 4) != 4) 119 *naddr = 2; 120 if (getprop(node, "#size-cells", nsize, 4) != 4) 121 *nsize = 1; 122 } 123 124 static void copy_val(u32 *dest, u32 *src, int naddr) 125 { 126 int pad = MAX_ADDR_CELLS - naddr; 127 128 memset(dest, 0, pad * 4); 129 memcpy(dest + pad, src, naddr * 4); 130 } 131 132 static int sub_reg(u32 *reg, u32 *sub) 133 { 134 int i, borrow = 0; 135 136 for (i = MAX_ADDR_CELLS - 1; i >= 0; i--) { 137 int prev_borrow = borrow; 138 borrow = reg[i] < sub[i] + prev_borrow; 139 reg[i] -= sub[i] + prev_borrow; 140 } 141 142 return !borrow; 143 } 144 145 static int add_reg(u32 *reg, u32 *add, int naddr) 146 { 147 int i, carry = 0; 148 149 for (i = MAX_ADDR_CELLS - 1; i >= MAX_ADDR_CELLS - naddr; i--) { 150 u64 tmp = (u64)reg[i] + add[i] + carry; 151 carry = tmp >> 32; 152 reg[i] = (u32)tmp; 153 } 154 155 return !carry; 156 } 157 158 /* It is assumed that if the first byte of reg fits in a 159 * range, then the whole reg block fits. 160 */ 161 static int compare_reg(u32 *reg, u32 *range, u32 *rangesize) 162 { 163 int i; 164 u32 end; 165 166 for (i = 0; i < MAX_ADDR_CELLS; i++) { 167 if (reg[i] < range[i]) 168 return 0; 169 if (reg[i] > range[i]) 170 break; 171 } 172 173 for (i = 0; i < MAX_ADDR_CELLS; i++) { 174 end = range[i] + rangesize[i]; 175 176 if (reg[i] < end) 177 break; 178 if (reg[i] > end) 179 return 0; 180 } 181 182 return reg[i] != end; 183 } 184 185 /* reg must be MAX_ADDR_CELLS */ 186 static int find_range(u32 *reg, u32 *ranges, int nregaddr, 187 int naddr, int nsize, int buflen) 188 { 189 int nrange = nregaddr + naddr + nsize; 190 int i; 191 192 for (i = 0; i + nrange <= buflen; i += nrange) { 193 u32 range_addr[MAX_ADDR_CELLS]; 194 u32 range_size[MAX_ADDR_CELLS]; 195 196 copy_val(range_addr, ranges + i, naddr); 197 copy_val(range_size, ranges + i + nregaddr + naddr, nsize); 198 199 if (compare_reg(reg, range_addr, range_size)) 200 return i; 201 } 202 203 return -1; 204 } 205 206 /* Currently only generic buses without special encodings are supported. 207 * In particular, PCI is not supported. Also, only the beginning of the 208 * reg block is tracked; size is ignored except in ranges. 209 */ 210 int dt_xlate_reg(void *node, int res, unsigned long *addr, 211 unsigned long *size) 212 { 213 u32 last_addr[MAX_ADDR_CELLS]; 214 u32 this_addr[MAX_ADDR_CELLS]; 215 u32 buf[MAX_ADDR_CELLS * MAX_RANGES * 3]; 216 void *parent; 217 u64 ret_addr, ret_size; 218 u32 naddr, nsize, prev_naddr; 219 int buflen, offset; 220 221 parent = get_parent(node); 222 if (!parent) 223 return 0; 224 225 get_reg_format(parent, &naddr, &nsize); 226 227 if (nsize > 2) 228 return 0; 229 230 buflen = getprop(node, "reg", buf, sizeof(buf)) / 4; 231 offset = (naddr + nsize) * res; 232 233 if (buflen < offset + naddr + nsize || 234 sizeof(buf) < offset + naddr + nsize) 235 return 0; 236 237 copy_val(last_addr, buf + offset, naddr); 238 239 ret_size = buf[offset + naddr]; 240 if (nsize == 2) { 241 ret_size <<= 32; 242 ret_size |= buf[offset + naddr + 1]; 243 } 244 245 while ((node = get_parent(node))) { 246 prev_naddr = naddr; 247 248 get_reg_format(node, &naddr, &nsize); 249 250 buflen = getprop(node, "ranges", buf, sizeof(buf)); 251 if (buflen < 0) 252 continue; 253 if (buflen > sizeof(buf)) 254 return 0; 255 256 offset = find_range(last_addr, buf, prev_naddr, 257 naddr, nsize, buflen / 4); 258 259 if (offset < 0) 260 return 0; 261 262 copy_val(this_addr, buf + offset, prev_naddr); 263 264 if (!sub_reg(last_addr, this_addr)) 265 return 0; 266 267 copy_val(this_addr, buf + offset + prev_naddr, naddr); 268 269 if (!add_reg(last_addr, this_addr, naddr)) 270 return 0; 271 } 272 273 if (naddr > 2) 274 return 0; 275 276 ret_addr = ((u64)last_addr[2] << 32) | last_addr[3]; 277 278 if (sizeof(void *) == 4 && 279 (ret_addr >= 0x100000000ULL || ret_size > 0x100000000ULL || 280 ret_addr + ret_size > 0x100000000ULL)) 281 return 0; 282 283 *addr = ret_addr; 284 if (size) 285 *size = ret_size; 286 287 return 1; 288 } 289