1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * devtree.c - convenience functions for device tree manipulation 4 * Copyright 2007 David Gibson, IBM Corporation. 5 * Copyright (c) 2007 Freescale Semiconductor, Inc. 6 * 7 * Authors: David Gibson <david@gibson.dropbear.id.au> 8 * Scott Wood <scottwood@freescale.com> 9 */ 10 #include <stdarg.h> 11 #include <stddef.h> 12 #include "types.h" 13 #include "string.h" 14 #include "stdio.h" 15 #include "ops.h" 16 #include "of.h" 17 18 void dt_fixup_memory(u64 start, u64 size) 19 { 20 void *root, *memory; 21 int naddr, nsize, i; 22 u32 memreg[4]; 23 24 root = finddevice("/"); 25 if (getprop(root, "#address-cells", &naddr, sizeof(naddr)) < 0) 26 naddr = 2; 27 else 28 naddr = be32_to_cpu(naddr); 29 if (naddr < 1 || naddr > 2) 30 fatal("Can't cope with #address-cells == %d in /\n\r", naddr); 31 32 if (getprop(root, "#size-cells", &nsize, sizeof(nsize)) < 0) 33 nsize = 1; 34 else 35 nsize = be32_to_cpu(nsize); 36 if (nsize < 1 || nsize > 2) 37 fatal("Can't cope with #size-cells == %d in /\n\r", nsize); 38 39 i = 0; 40 if (naddr == 2) 41 memreg[i++] = cpu_to_be32(start >> 32); 42 memreg[i++] = cpu_to_be32(start & 0xffffffff); 43 if (nsize == 2) 44 memreg[i++] = cpu_to_be32(size >> 32); 45 memreg[i++] = cpu_to_be32(size & 0xffffffff); 46 47 memory = finddevice("/memory"); 48 if (! memory) { 49 memory = create_node(NULL, "memory"); 50 setprop_str(memory, "device_type", "memory"); 51 } 52 53 printf("Memory <- <0x%x", be32_to_cpu(memreg[0])); 54 for (i = 1; i < (naddr + nsize); i++) 55 printf(" 0x%x", be32_to_cpu(memreg[i])); 56 printf("> (%ldMB)\n\r", (unsigned long)(size >> 20)); 57 58 setprop(memory, "reg", memreg, (naddr + nsize)*sizeof(u32)); 59 } 60 61 #define MHZ(x) ((x + 500000) / 1000000) 62 63 void dt_fixup_cpu_clocks(u32 cpu, u32 tb, u32 bus) 64 { 65 void *devp = NULL; 66 67 printf("CPU clock-frequency <- 0x%x (%dMHz)\n\r", cpu, MHZ(cpu)); 68 printf("CPU timebase-frequency <- 0x%x (%dMHz)\n\r", tb, MHZ(tb)); 69 if (bus > 0) 70 printf("CPU bus-frequency <- 0x%x (%dMHz)\n\r", bus, MHZ(bus)); 71 72 while ((devp = find_node_by_devtype(devp, "cpu"))) { 73 setprop_val(devp, "clock-frequency", cpu_to_be32(cpu)); 74 setprop_val(devp, "timebase-frequency", cpu_to_be32(tb)); 75 if (bus > 0) 76 setprop_val(devp, "bus-frequency", cpu_to_be32(bus)); 77 } 78 79 timebase_period_ns = 1000000000 / tb; 80 } 81 82 void dt_fixup_clock(const char *path, u32 freq) 83 { 84 void *devp = finddevice(path); 85 86 if (devp) { 87 printf("%s: clock-frequency <- %x (%dMHz)\n\r", path, freq, MHZ(freq)); 88 setprop_val(devp, "clock-frequency", cpu_to_be32(freq)); 89 } 90 } 91 92 void dt_fixup_mac_address_by_alias(const char *alias, const u8 *addr) 93 { 94 void *devp = find_node_by_alias(alias); 95 96 if (devp) { 97 printf("%s: local-mac-address <-" 98 " %02x:%02x:%02x:%02x:%02x:%02x\n\r", alias, 99 addr[0], addr[1], addr[2], 100 addr[3], addr[4], addr[5]); 101 102 setprop(devp, "local-mac-address", addr, 6); 103 } 104 } 105 106 void dt_fixup_mac_address(u32 index, const u8 *addr) 107 { 108 void *devp = find_node_by_prop_value(NULL, "linux,network-index", 109 (void*)&index, sizeof(index)); 110 111 if (devp) { 112 printf("ENET%d: local-mac-address <-" 113 " %02x:%02x:%02x:%02x:%02x:%02x\n\r", index, 114 addr[0], addr[1], addr[2], 115 addr[3], addr[4], addr[5]); 116 117 setprop(devp, "local-mac-address", addr, 6); 118 } 119 } 120 121 void __dt_fixup_mac_addresses(u32 startindex, ...) 122 { 123 va_list ap; 124 u32 index = startindex; 125 const u8 *addr; 126 127 va_start(ap, startindex); 128 129 while ((addr = va_arg(ap, const u8 *))) 130 dt_fixup_mac_address(index++, addr); 131 132 va_end(ap); 133 } 134 135 #define MAX_ADDR_CELLS 4 136 137 void dt_get_reg_format(void *node, u32 *naddr, u32 *nsize) 138 { 139 if (getprop(node, "#address-cells", naddr, 4) != 4) 140 *naddr = 2; 141 else 142 *naddr = be32_to_cpu(*naddr); 143 if (getprop(node, "#size-cells", nsize, 4) != 4) 144 *nsize = 1; 145 else 146 *nsize = be32_to_cpu(*nsize); 147 } 148 149 static void copy_val(u32 *dest, u32 *src, int naddr) 150 { 151 int pad = MAX_ADDR_CELLS - naddr; 152 153 memset(dest, 0, pad * 4); 154 memcpy(dest + pad, src, naddr * 4); 155 } 156 157 static int sub_reg(u32 *reg, u32 *sub) 158 { 159 int i, borrow = 0; 160 161 for (i = MAX_ADDR_CELLS - 1; i >= 0; i--) { 162 int prev_borrow = borrow; 163 borrow = reg[i] < sub[i] + prev_borrow; 164 reg[i] -= sub[i] + prev_borrow; 165 } 166 167 return !borrow; 168 } 169 170 static int add_reg(u32 *reg, u32 *add, int naddr) 171 { 172 int i, carry = 0; 173 174 for (i = MAX_ADDR_CELLS - 1; i >= MAX_ADDR_CELLS - naddr; i--) { 175 u64 tmp = (u64)be32_to_cpu(reg[i]) + be32_to_cpu(add[i]) + carry; 176 carry = tmp >> 32; 177 reg[i] = cpu_to_be32((u32)tmp); 178 } 179 180 return !carry; 181 } 182 183 /* It is assumed that if the first byte of reg fits in a 184 * range, then the whole reg block fits. 185 */ 186 static int compare_reg(u32 *reg, u32 *range, u32 *rangesize) 187 { 188 int i; 189 u32 end; 190 191 for (i = 0; i < MAX_ADDR_CELLS; i++) { 192 if (be32_to_cpu(reg[i]) < be32_to_cpu(range[i])) 193 return 0; 194 if (be32_to_cpu(reg[i]) > be32_to_cpu(range[i])) 195 break; 196 } 197 198 for (i = 0; i < MAX_ADDR_CELLS; i++) { 199 end = be32_to_cpu(range[i]) + be32_to_cpu(rangesize[i]); 200 201 if (be32_to_cpu(reg[i]) < end) 202 break; 203 if (be32_to_cpu(reg[i]) > end) 204 return 0; 205 } 206 207 return reg[i] != end; 208 } 209 210 /* reg must be MAX_ADDR_CELLS */ 211 static int find_range(u32 *reg, u32 *ranges, int nregaddr, 212 int naddr, int nsize, int buflen) 213 { 214 int nrange = nregaddr + naddr + nsize; 215 int i; 216 217 for (i = 0; i + nrange <= buflen; i += nrange) { 218 u32 range_addr[MAX_ADDR_CELLS]; 219 u32 range_size[MAX_ADDR_CELLS]; 220 221 copy_val(range_addr, ranges + i, nregaddr); 222 copy_val(range_size, ranges + i + nregaddr + naddr, nsize); 223 224 if (compare_reg(reg, range_addr, range_size)) 225 return i; 226 } 227 228 return -1; 229 } 230 231 /* Currently only generic buses without special encodings are supported. 232 * In particular, PCI is not supported. Also, only the beginning of the 233 * reg block is tracked; size is ignored except in ranges. 234 */ 235 static u32 prop_buf[MAX_PROP_LEN / 4]; 236 237 static int dt_xlate(void *node, int res, int reglen, unsigned long *addr, 238 unsigned long *size) 239 { 240 u32 last_addr[MAX_ADDR_CELLS]; 241 u32 this_addr[MAX_ADDR_CELLS]; 242 void *parent; 243 u64 ret_addr, ret_size; 244 u32 naddr, nsize, prev_naddr, prev_nsize; 245 int buflen, offset; 246 247 parent = get_parent(node); 248 if (!parent) 249 return 0; 250 251 dt_get_reg_format(parent, &naddr, &nsize); 252 if (nsize > 2) 253 return 0; 254 255 offset = (naddr + nsize) * res; 256 257 if (reglen < offset + naddr + nsize || 258 MAX_PROP_LEN < (offset + naddr + nsize) * 4) 259 return 0; 260 261 copy_val(last_addr, prop_buf + offset, naddr); 262 263 ret_size = be32_to_cpu(prop_buf[offset + naddr]); 264 if (nsize == 2) { 265 ret_size <<= 32; 266 ret_size |= be32_to_cpu(prop_buf[offset + naddr + 1]); 267 } 268 269 for (;;) { 270 prev_naddr = naddr; 271 prev_nsize = nsize; 272 node = parent; 273 274 parent = get_parent(node); 275 if (!parent) 276 break; 277 278 dt_get_reg_format(parent, &naddr, &nsize); 279 280 buflen = getprop(node, "ranges", prop_buf, 281 sizeof(prop_buf)); 282 if (buflen == 0) 283 continue; 284 if (buflen < 0 || buflen > sizeof(prop_buf)) 285 return 0; 286 287 offset = find_range(last_addr, prop_buf, prev_naddr, 288 naddr, prev_nsize, buflen / 4); 289 if (offset < 0) 290 return 0; 291 292 copy_val(this_addr, prop_buf + offset, prev_naddr); 293 294 if (!sub_reg(last_addr, this_addr)) 295 return 0; 296 297 copy_val(this_addr, prop_buf + offset + prev_naddr, naddr); 298 299 if (!add_reg(last_addr, this_addr, naddr)) 300 return 0; 301 } 302 303 if (naddr > 2) 304 return 0; 305 306 ret_addr = ((u64)be32_to_cpu(last_addr[2]) << 32) | be32_to_cpu(last_addr[3]); 307 if (sizeof(void *) == 4 && 308 (ret_addr >= 0x100000000ULL || ret_size > 0x100000000ULL || 309 ret_addr + ret_size > 0x100000000ULL)) 310 return 0; 311 312 *addr = ret_addr; 313 if (size) 314 *size = ret_size; 315 316 return 1; 317 } 318 319 int dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long *size) 320 { 321 int reglen; 322 323 reglen = getprop(node, "reg", prop_buf, sizeof(prop_buf)) / 4; 324 return dt_xlate(node, res, reglen, addr, size); 325 } 326 327 int dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long *xlated_addr) 328 { 329 330 if (buflen > sizeof(prop_buf)) 331 return 0; 332 333 memcpy(prop_buf, buf, buflen); 334 return dt_xlate(node, 0, buflen / 4, xlated_addr, NULL); 335 } 336 337 int dt_is_compatible(void *node, const char *compat) 338 { 339 char *buf = (char *)prop_buf; 340 int len, pos; 341 342 len = getprop(node, "compatible", buf, MAX_PROP_LEN); 343 if (len < 0) 344 return 0; 345 346 for (pos = 0; pos < len; pos++) { 347 if (!strcmp(buf + pos, compat)) 348 return 1; 349 350 pos += strnlen(&buf[pos], len - pos); 351 } 352 353 return 0; 354 } 355 356 int dt_get_virtual_reg(void *node, void **addr, int nres) 357 { 358 unsigned long xaddr; 359 int n, i; 360 361 n = getprop(node, "virtual-reg", addr, nres * 4); 362 if (n > 0) { 363 for (i = 0; i < n/4; i ++) 364 ((u32 *)addr)[i] = be32_to_cpu(((u32 *)addr)[i]); 365 return n / 4; 366 } 367 368 for (n = 0; n < nres; n++) { 369 if (!dt_xlate_reg(node, n, &xaddr, NULL)) 370 break; 371 372 addr[n] = (void *)xaddr; 373 } 374 375 return n; 376 } 377 378
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.