• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <david@gibson.dropbear.id.au>
7   *	    Scott Wood <scottwood@freescale.com>
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  
dt_fixup_memory(u64 start,u64 size)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  
dt_fixup_cpu_clocks(u32 cpu,u32 tb,u32 bus)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  	timebase_period_ns = 1000000000 / tb;
79  }
80  
dt_fixup_clock(const char * path,u32 freq)81  void dt_fixup_clock(const char *path, u32 freq)
82  {
83  	void *devp = finddevice(path);
84  
85  	if (devp) {
86  		printf("%s: clock-frequency <- %x (%dMHz)\n\r", path, freq, MHZ(freq));
87  		setprop_val(devp, "clock-frequency", freq);
88  	}
89  }
90  
dt_fixup_mac_address_by_alias(const char * alias,const u8 * addr)91  void dt_fixup_mac_address_by_alias(const char *alias, const u8 *addr)
92  {
93  	void *devp = find_node_by_alias(alias);
94  
95  	if (devp) {
96  		printf("%s: local-mac-address <-"
97  		       " %02x:%02x:%02x:%02x:%02x:%02x\n\r", alias,
98  		       addr[0], addr[1], addr[2],
99  		       addr[3], addr[4], addr[5]);
100  
101  		setprop(devp, "local-mac-address", addr, 6);
102  	}
103  }
104  
dt_fixup_mac_address(u32 index,const u8 * addr)105  void dt_fixup_mac_address(u32 index, const u8 *addr)
106  {
107  	void *devp = find_node_by_prop_value(NULL, "linux,network-index",
108  	                                     (void*)&index, sizeof(index));
109  
110  	if (devp) {
111  		printf("ENET%d: local-mac-address <-"
112  		       " %02x:%02x:%02x:%02x:%02x:%02x\n\r", index,
113  		       addr[0], addr[1], addr[2],
114  		       addr[3], addr[4], addr[5]);
115  
116  		setprop(devp, "local-mac-address", addr, 6);
117  	}
118  }
119  
__dt_fixup_mac_addresses(u32 startindex,...)120  void __dt_fixup_mac_addresses(u32 startindex, ...)
121  {
122  	va_list ap;
123  	u32 index = startindex;
124  	const u8 *addr;
125  
126  	va_start(ap, startindex);
127  
128  	while ((addr = va_arg(ap, const u8 *)))
129  		dt_fixup_mac_address(index++, addr);
130  
131  	va_end(ap);
132  }
133  
134  #define MAX_ADDR_CELLS 4
135  
dt_get_reg_format(void * node,u32 * naddr,u32 * nsize)136  void dt_get_reg_format(void *node, u32 *naddr, u32 *nsize)
137  {
138  	if (getprop(node, "#address-cells", naddr, 4) != 4)
139  		*naddr = 2;
140  	if (getprop(node, "#size-cells", nsize, 4) != 4)
141  		*nsize = 1;
142  }
143  
copy_val(u32 * dest,u32 * src,int naddr)144  static void copy_val(u32 *dest, u32 *src, int naddr)
145  {
146  	int pad = MAX_ADDR_CELLS - naddr;
147  
148  	memset(dest, 0, pad * 4);
149  	memcpy(dest + pad, src, naddr * 4);
150  }
151  
sub_reg(u32 * reg,u32 * sub)152  static int sub_reg(u32 *reg, u32 *sub)
153  {
154  	int i, borrow = 0;
155  
156  	for (i = MAX_ADDR_CELLS - 1; i >= 0; i--) {
157  		int prev_borrow = borrow;
158  		borrow = reg[i] < sub[i] + prev_borrow;
159  		reg[i] -= sub[i] + prev_borrow;
160  	}
161  
162  	return !borrow;
163  }
164  
add_reg(u32 * reg,u32 * add,int naddr)165  static int add_reg(u32 *reg, u32 *add, int naddr)
166  {
167  	int i, carry = 0;
168  
169  	for (i = MAX_ADDR_CELLS - 1; i >= MAX_ADDR_CELLS - naddr; i--) {
170  		u64 tmp = (u64)reg[i] + add[i] + carry;
171  		carry = tmp >> 32;
172  		reg[i] = (u32)tmp;
173  	}
174  
175  	return !carry;
176  }
177  
178  /* It is assumed that if the first byte of reg fits in a
179   * range, then the whole reg block fits.
180   */
compare_reg(u32 * reg,u32 * range,u32 * rangesize)181  static int compare_reg(u32 *reg, u32 *range, u32 *rangesize)
182  {
183  	int i;
184  	u32 end;
185  
186  	for (i = 0; i < MAX_ADDR_CELLS; i++) {
187  		if (reg[i] < range[i])
188  			return 0;
189  		if (reg[i] > range[i])
190  			break;
191  	}
192  
193  	for (i = 0; i < MAX_ADDR_CELLS; i++) {
194  		end = range[i] + rangesize[i];
195  
196  		if (reg[i] < end)
197  			break;
198  		if (reg[i] > end)
199  			return 0;
200  	}
201  
202  	return reg[i] != end;
203  }
204  
205  /* reg must be MAX_ADDR_CELLS */
find_range(u32 * reg,u32 * ranges,int nregaddr,int naddr,int nsize,int buflen)206  static int find_range(u32 *reg, u32 *ranges, int nregaddr,
207                        int naddr, int nsize, int buflen)
208  {
209  	int nrange = nregaddr + naddr + nsize;
210  	int i;
211  
212  	for (i = 0; i + nrange <= buflen; i += nrange) {
213  		u32 range_addr[MAX_ADDR_CELLS];
214  		u32 range_size[MAX_ADDR_CELLS];
215  
216  		copy_val(range_addr, ranges + i, nregaddr);
217  		copy_val(range_size, ranges + i + nregaddr + naddr, nsize);
218  
219  		if (compare_reg(reg, range_addr, range_size))
220  			return i;
221  	}
222  
223  	return -1;
224  }
225  
226  /* Currently only generic buses without special encodings are supported.
227   * In particular, PCI is not supported.  Also, only the beginning of the
228   * reg block is tracked; size is ignored except in ranges.
229   */
230  static u32 prop_buf[MAX_PROP_LEN / 4];
231  
dt_xlate(void * node,int res,int reglen,unsigned long * addr,unsigned long * size)232  static int dt_xlate(void *node, int res, int reglen, unsigned long *addr,
233  		unsigned long *size)
234  {
235  	u32 last_addr[MAX_ADDR_CELLS];
236  	u32 this_addr[MAX_ADDR_CELLS];
237  	void *parent;
238  	u64 ret_addr, ret_size;
239  	u32 naddr, nsize, prev_naddr, prev_nsize;
240  	int buflen, offset;
241  
242  	parent = get_parent(node);
243  	if (!parent)
244  		return 0;
245  
246  	dt_get_reg_format(parent, &naddr, &nsize);
247  
248  	if (nsize > 2)
249  		return 0;
250  
251  	offset = (naddr + nsize) * res;
252  
253  	if (reglen < offset + naddr + nsize ||
254  	    MAX_PROP_LEN < (offset + naddr + nsize) * 4)
255  		return 0;
256  
257  	copy_val(last_addr, prop_buf + offset, naddr);
258  
259  	ret_size = prop_buf[offset + naddr];
260  	if (nsize == 2) {
261  		ret_size <<= 32;
262  		ret_size |= prop_buf[offset + naddr + 1];
263  	}
264  
265  	for (;;) {
266  		prev_naddr = naddr;
267  		prev_nsize = nsize;
268  		node = parent;
269  
270  		parent = get_parent(node);
271  		if (!parent)
272  			break;
273  
274  		dt_get_reg_format(parent, &naddr, &nsize);
275  
276  		buflen = getprop(node, "ranges", prop_buf,
277  				sizeof(prop_buf));
278  		if (buflen == 0)
279  			continue;
280  		if (buflen < 0 || buflen > sizeof(prop_buf))
281  			return 0;
282  
283  		offset = find_range(last_addr, prop_buf, prev_naddr,
284  		                    naddr, prev_nsize, buflen / 4);
285  
286  		if (offset < 0)
287  			return 0;
288  
289  		copy_val(this_addr, prop_buf + offset, prev_naddr);
290  
291  		if (!sub_reg(last_addr, this_addr))
292  			return 0;
293  
294  		copy_val(this_addr, prop_buf + offset + prev_naddr, naddr);
295  
296  		if (!add_reg(last_addr, this_addr, naddr))
297  			return 0;
298  	}
299  
300  	if (naddr > 2)
301  		return 0;
302  
303  	ret_addr = ((u64)last_addr[2] << 32) | last_addr[3];
304  
305  	if (sizeof(void *) == 4 &&
306  	    (ret_addr >= 0x100000000ULL || ret_size > 0x100000000ULL ||
307  	     ret_addr + ret_size > 0x100000000ULL))
308  		return 0;
309  
310  	*addr = ret_addr;
311  	if (size)
312  		*size = ret_size;
313  
314  	return 1;
315  }
316  
dt_xlate_reg(void * node,int res,unsigned long * addr,unsigned long * size)317  int dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long *size)
318  {
319  	int reglen;
320  
321  	reglen = getprop(node, "reg", prop_buf, sizeof(prop_buf)) / 4;
322  	return dt_xlate(node, res, reglen, addr, size);
323  }
324  
dt_xlate_addr(void * node,u32 * buf,int buflen,unsigned long * xlated_addr)325  int dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long *xlated_addr)
326  {
327  
328  	if (buflen > sizeof(prop_buf))
329  		return 0;
330  
331  	memcpy(prop_buf, buf, buflen);
332  	return dt_xlate(node, 0, buflen / 4, xlated_addr, NULL);
333  }
334  
dt_is_compatible(void * node,const char * compat)335  int dt_is_compatible(void *node, const char *compat)
336  {
337  	char *buf = (char *)prop_buf;
338  	int len, pos;
339  
340  	len = getprop(node, "compatible", buf, MAX_PROP_LEN);
341  	if (len < 0)
342  		return 0;
343  
344  	for (pos = 0; pos < len; pos++) {
345  		if (!strcmp(buf + pos, compat))
346  			return 1;
347  
348  		pos += strnlen(&buf[pos], len - pos);
349  	}
350  
351  	return 0;
352  }
353  
dt_get_virtual_reg(void * node,void ** addr,int nres)354  int dt_get_virtual_reg(void *node, void **addr, int nres)
355  {
356  	unsigned long xaddr;
357  	int n;
358  
359  	n = getprop(node, "virtual-reg", addr, nres * 4);
360  	if (n > 0)
361  		return n / 4;
362  
363  	for (n = 0; n < nres; n++) {
364  		if (!dt_xlate_reg(node, n, &xaddr, NULL))
365  			break;
366  
367  		addr[n] = (void *)xaddr;
368  	}
369  
370  	return n;
371  }
372  
373