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(name: "/" ); |
25 | if (getprop(devp: root, name: "#address-cells" , buf: &naddr, buflen: 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(devp: root, name: "#size-cells" , buf: &nsize, buflen: 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(name: "/memory" ); |
48 | if (! memory) { |
49 | memory = create_node(parent: NULL, name: "memory" ); |
50 | setprop_str(devp: memory, name: "device_type" , buf: "memory" ); |
51 | } |
52 | |
53 | printf(fmt: "Memory <- <0x%x" , be32_to_cpu(memreg[0])); |
54 | for (i = 1; i < (naddr + nsize); i++) |
55 | printf(fmt: " 0x%x" , be32_to_cpu(memreg[i])); |
56 | printf(fmt: "> (%ldMB)\n\r" , (unsigned long)(size >> 20)); |
57 | |
58 | setprop(devp: memory, name: "reg" , buf: memreg, buflen: (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(fmt: "CPU clock-frequency <- 0x%x (%dMHz)\n\r" , cpu, MHZ(cpu)); |
68 | printf(fmt: "CPU timebase-frequency <- 0x%x (%dMHz)\n\r" , tb, MHZ(tb)); |
69 | if (bus > 0) |
70 | printf(fmt: "CPU bus-frequency <- 0x%x (%dMHz)\n\r" , bus, MHZ(bus)); |
71 | |
72 | while ((devp = find_node_by_devtype(prev: devp, type: "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(name: path); |
85 | |
86 | if (devp) { |
87 | printf(fmt: "%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(fmt: "%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, name: "local-mac-address" , buf: addr, buflen: 6); |
103 | } |
104 | } |
105 | |
106 | void dt_fixup_mac_address(u32 index, const u8 *addr) |
107 | { |
108 | void *devp = find_node_by_prop_value(prev: NULL, propname: "linux,network-index" , |
109 | propval: (void*)&index, proplen: sizeof(index)); |
110 | |
111 | if (devp) { |
112 | printf(fmt: "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, name: "local-mac-address" , buf: addr, buflen: 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: 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(devp: node, name: "#address-cells" , buf: naddr, buflen: 4) != 4) |
140 | *naddr = 2; |
141 | else |
142 | *naddr = be32_to_cpu(*naddr); |
143 | if (getprop(devp: node, name: "#size-cells" , buf: nsize, buflen: 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: dest + pad, src, n: 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(dest: range_addr, src: ranges + i, naddr: nregaddr); |
222 | copy_val(dest: range_size, src: ranges + i + nregaddr + naddr, naddr: nsize); |
223 | |
224 | if (compare_reg(reg, range: range_addr, rangesize: 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(devp: node); |
248 | if (!parent) |
249 | return 0; |
250 | |
251 | dt_get_reg_format(node: parent, naddr: &naddr, nsize: &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(dest: last_addr, src: 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(devp: node); |
275 | if (!parent) |
276 | break; |
277 | |
278 | dt_get_reg_format(node: parent, naddr: &naddr, nsize: &nsize); |
279 | |
280 | buflen = getprop(devp: node, name: "ranges" , buf: prop_buf, |
281 | buflen: 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(reg: last_addr, ranges: prop_buf, nregaddr: prev_naddr, |
288 | naddr, nsize: prev_nsize, buflen: buflen / 4); |
289 | if (offset < 0) |
290 | return 0; |
291 | |
292 | copy_val(dest: this_addr, src: prop_buf + offset, naddr: prev_naddr); |
293 | |
294 | if (!sub_reg(reg: last_addr, sub: this_addr)) |
295 | return 0; |
296 | |
297 | copy_val(dest: this_addr, src: prop_buf + offset + prev_naddr, naddr); |
298 | |
299 | if (!add_reg(reg: last_addr, add: 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(devp: node, name: "reg" , buf: prop_buf, buflen: 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(dest: prop_buf, src: buf, n: buflen); |
334 | return dt_xlate(node, res: 0, reglen: buflen / 4, addr: xlated_addr, size: 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(devp: node, name: "compatible" , buf, MAX_PROP_LEN); |
343 | if (len < 0) |
344 | return 0; |
345 | |
346 | for (pos = 0; pos < len; pos++) { |
347 | if (!strcmp(s1: buf + pos, s2: 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(devp: node, name: "virtual-reg" , buf: addr, buflen: 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, res: n, addr: &xaddr, size: NULL)) |
370 | break; |
371 | |
372 | addr[n] = (void *)xaddr; |
373 | } |
374 | |
375 | return n; |
376 | } |
377 | |
378 | |