Blame view

arch/powerpc/boot/devtree.c 8.13 KB
27fbaa970   David Gibson   [POWERPC] Add dev...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  /*
   * devtree.c - convenience functions for device tree manipulation
   * Copyright 2007 David Gibson, IBM Corporation.
   * Copyright (c) 2007 Freescale Semiconductor, Inc.
   *
   * Authors: David Gibson <david@gibson.dropbear.id.au>
   *	    Scott Wood <scottwood@freescale.com>
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version
   * 2 of the License, or (at your option) any later version.
   */
  #include <stdarg.h>
  #include <stddef.h>
  #include "types.h"
  #include "string.h"
  #include "stdio.h"
  #include "ops.h"
  
  void dt_fixup_memory(u64 start, u64 size)
  {
  	void *root, *memory;
  	int naddr, nsize, i;
  	u32 memreg[4];
  
  	root = finddevice("/");
  	if (getprop(root, "#address-cells", &naddr, sizeof(naddr)) < 0)
  		naddr = 2;
  	if (naddr < 1 || naddr > 2)
  		fatal("Can't cope with #address-cells == %d in /
  \r", naddr);
  
  	if (getprop(root, "#size-cells", &nsize, sizeof(nsize)) < 0)
  		nsize = 1;
  	if (nsize < 1 || nsize > 2)
  		fatal("Can't cope with #size-cells == %d in /
  \r", nsize);
  
  	i = 0;
  	if (naddr == 2)
  		memreg[i++] = start >> 32;
  	memreg[i++] = start & 0xffffffff;
  	if (nsize == 2)
  		memreg[i++] = size >> 32;
  	memreg[i++] = size & 0xffffffff;
  
  	memory = finddevice("/memory");
  	if (! memory) {
  		memory = create_node(NULL, "memory");
  		setprop_str(memory, "device_type", "memory");
  	}
  
  	printf("Memory <- <0x%x", memreg[0]);
  	for (i = 1; i < (naddr + nsize); i++)
  		printf(" 0x%x", memreg[i]);
  	printf("> (%ldMB)
  \r", (unsigned long)(size >> 20));
  
  	setprop(memory, "reg", memreg, (naddr + nsize)*sizeof(u32));
  }
  
  #define MHZ(x)	((x + 500000) / 1000000)
  
  void dt_fixup_cpu_clocks(u32 cpu, u32 tb, u32 bus)
  {
  	void *devp = NULL;
  
  	printf("CPU clock-frequency <- 0x%x (%dMHz)
  \r", cpu, MHZ(cpu));
  	printf("CPU timebase-frequency <- 0x%x (%dMHz)
  \r", tb, MHZ(tb));
  	if (bus > 0)
  		printf("CPU bus-frequency <- 0x%x (%dMHz)
  \r", bus, MHZ(bus));
  
  	while ((devp = find_node_by_devtype(devp, "cpu"))) {
  		setprop_val(devp, "clock-frequency", cpu);
  		setprop_val(devp, "timebase-frequency", tb);
  		if (bus > 0)
  			setprop_val(devp, "bus-frequency", bus);
  	}
643d3c139   Scott Wood   [POWERPC] bootwra...
83
84
  
  	timebase_period_ns = 1000000000 / tb;
27fbaa970   David Gibson   [POWERPC] Add dev...
85
86
87
88
89
90
91
92
93
94
95
96
  }
  
  void dt_fixup_clock(const char *path, u32 freq)
  {
  	void *devp = finddevice(path);
  
  	if (devp) {
  		printf("%s: clock-frequency <- %x (%dMHz)
  \r", path, freq, MHZ(freq));
  		setprop_val(devp, "clock-frequency", freq);
  	}
  }
ad160681c   Kumar Gala   [POWERPC] bootwra...
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  void dt_fixup_mac_address_by_alias(const char *alias, const u8 *addr)
  {
  	void *devp = find_node_by_alias(alias);
  
  	if (devp) {
  		printf("%s: local-mac-address <-"
  		       " %02x:%02x:%02x:%02x:%02x:%02x
  \r", alias,
  		       addr[0], addr[1], addr[2],
  		       addr[3], addr[4], addr[5]);
  
  		setprop(devp, "local-mac-address", addr, 6);
  	}
  }
27ff35d90   Scott Wood   [POWERPC] bootwra...
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
  void dt_fixup_mac_address(u32 index, const u8 *addr)
  {
  	void *devp = find_node_by_prop_value(NULL, "linux,network-index",
  	                                     (void*)&index, sizeof(index));
  
  	if (devp) {
  		printf("ENET%d: local-mac-address <-"
  		       " %02x:%02x:%02x:%02x:%02x:%02x
  \r", index,
  		       addr[0], addr[1], addr[2],
  		       addr[3], addr[4], addr[5]);
  
  		setprop(devp, "local-mac-address", addr, 6);
  	}
  }
27fbaa970   David Gibson   [POWERPC] Add dev...
126
127
128
129
  void __dt_fixup_mac_addresses(u32 startindex, ...)
  {
  	va_list ap;
  	u32 index = startindex;
27fbaa970   David Gibson   [POWERPC] Add dev...
130
131
132
  	const u8 *addr;
  
  	va_start(ap, startindex);
27fbaa970   David Gibson   [POWERPC] Add dev...
133

27ff35d90   Scott Wood   [POWERPC] bootwra...
134
135
  	while ((addr = va_arg(ap, const u8 *)))
  		dt_fixup_mac_address(index++, addr);
27fbaa970   David Gibson   [POWERPC] Add dev...
136

27fbaa970   David Gibson   [POWERPC] Add dev...
137
138
  	va_end(ap);
  }
6e1af384f   Scott Wood   [POWERPC] bootwra...
139
140
  
  #define MAX_ADDR_CELLS 4
6e1af384f   Scott Wood   [POWERPC] bootwra...
141

e5d8d54db   Scott Wood   [POWERPC] bootwra...
142
  void dt_get_reg_format(void *node, u32 *naddr, u32 *nsize)
6e1af384f   Scott Wood   [POWERPC] bootwra...
143
144
145
146
147
148
149
150
151
  {
  	if (getprop(node, "#address-cells", naddr, 4) != 4)
  		*naddr = 2;
  	if (getprop(node, "#size-cells", nsize, 4) != 4)
  		*nsize = 1;
  }
  
  static void copy_val(u32 *dest, u32 *src, int naddr)
  {
e4bb688d9   Scott Wood   [POWERPC] bootwra...
152
153
154
155
  	int pad = MAX_ADDR_CELLS - naddr;
  
  	memset(dest, 0, pad * 4);
  	memcpy(dest + pad, src, naddr * 4);
6e1af384f   Scott Wood   [POWERPC] bootwra...
156
157
158
159
160
  }
  
  static int sub_reg(u32 *reg, u32 *sub)
  {
  	int i, borrow = 0;
e4bb688d9   Scott Wood   [POWERPC] bootwra...
161
  	for (i = MAX_ADDR_CELLS - 1; i >= 0; i--) {
6e1af384f   Scott Wood   [POWERPC] bootwra...
162
163
164
165
166
167
168
  		int prev_borrow = borrow;
  		borrow = reg[i] < sub[i] + prev_borrow;
  		reg[i] -= sub[i] + prev_borrow;
  	}
  
  	return !borrow;
  }
e4bb688d9   Scott Wood   [POWERPC] bootwra...
169
  static int add_reg(u32 *reg, u32 *add, int naddr)
6e1af384f   Scott Wood   [POWERPC] bootwra...
170
171
  {
  	int i, carry = 0;
e4bb688d9   Scott Wood   [POWERPC] bootwra...
172
  	for (i = MAX_ADDR_CELLS - 1; i >= MAX_ADDR_CELLS - naddr; i--) {
6e1af384f   Scott Wood   [POWERPC] bootwra...
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
  		u64 tmp = (u64)reg[i] + add[i] + carry;
  		carry = tmp >> 32;
  		reg[i] = (u32)tmp;
  	}
  
  	return !carry;
  }
  
  /* It is assumed that if the first byte of reg fits in a
   * range, then the whole reg block fits.
   */
  static int compare_reg(u32 *reg, u32 *range, u32 *rangesize)
  {
  	int i;
  	u32 end;
  
  	for (i = 0; i < MAX_ADDR_CELLS; i++) {
  		if (reg[i] < range[i])
  			return 0;
  		if (reg[i] > range[i])
  			break;
  	}
  
  	for (i = 0; i < MAX_ADDR_CELLS; i++) {
  		end = range[i] + rangesize[i];
  
  		if (reg[i] < end)
  			break;
  		if (reg[i] > end)
  			return 0;
  	}
  
  	return reg[i] != end;
  }
  
  /* reg must be MAX_ADDR_CELLS */
  static int find_range(u32 *reg, u32 *ranges, int nregaddr,
                        int naddr, int nsize, int buflen)
  {
  	int nrange = nregaddr + naddr + nsize;
  	int i;
  
  	for (i = 0; i + nrange <= buflen; i += nrange) {
  		u32 range_addr[MAX_ADDR_CELLS];
  		u32 range_size[MAX_ADDR_CELLS];
49e6e3f1a   Scott Wood   powerpc/bootwrapp...
218
  		copy_val(range_addr, ranges + i, nregaddr);
6e1af384f   Scott Wood   [POWERPC] bootwra...
219
220
221
222
223
224
225
226
227
228
229
230
231
  		copy_val(range_size, ranges + i + nregaddr + naddr, nsize);
  
  		if (compare_reg(reg, range_addr, range_size))
  			return i;
  	}
  
  	return -1;
  }
  
  /* Currently only generic buses without special encodings are supported.
   * In particular, PCI is not supported.  Also, only the beginning of the
   * reg block is tracked; size is ignored except in ranges.
   */
a73ac50c4   Scott Wood   [POWERPC] bootwra...
232
  static u32 prop_buf[MAX_PROP_LEN / 4];
8895ea483   Mark A. Greer   [POWERPC] Add dt_...
233
234
235
  
  static int dt_xlate(void *node, int res, int reglen, unsigned long *addr,
  		unsigned long *size)
6e1af384f   Scott Wood   [POWERPC] bootwra...
236
237
238
  {
  	u32 last_addr[MAX_ADDR_CELLS];
  	u32 this_addr[MAX_ADDR_CELLS];
6e1af384f   Scott Wood   [POWERPC] bootwra...
239
240
  	void *parent;
  	u64 ret_addr, ret_size;
0602801c2   Scott Wood   [POWERPC] bootwra...
241
  	u32 naddr, nsize, prev_naddr, prev_nsize;
6e1af384f   Scott Wood   [POWERPC] bootwra...
242
243
244
245
246
  	int buflen, offset;
  
  	parent = get_parent(node);
  	if (!parent)
  		return 0;
e5d8d54db   Scott Wood   [POWERPC] bootwra...
247
  	dt_get_reg_format(parent, &naddr, &nsize);
6e1af384f   Scott Wood   [POWERPC] bootwra...
248
249
250
  
  	if (nsize > 2)
  		return 0;
6e1af384f   Scott Wood   [POWERPC] bootwra...
251
  	offset = (naddr + nsize) * res;
8895ea483   Mark A. Greer   [POWERPC] Add dt_...
252
  	if (reglen < offset + naddr + nsize ||
a73ac50c4   Scott Wood   [POWERPC] bootwra...
253
  	    MAX_PROP_LEN < (offset + naddr + nsize) * 4)
6e1af384f   Scott Wood   [POWERPC] bootwra...
254
  		return 0;
a73ac50c4   Scott Wood   [POWERPC] bootwra...
255
  	copy_val(last_addr, prop_buf + offset, naddr);
6e1af384f   Scott Wood   [POWERPC] bootwra...
256

a73ac50c4   Scott Wood   [POWERPC] bootwra...
257
  	ret_size = prop_buf[offset + naddr];
6e1af384f   Scott Wood   [POWERPC] bootwra...
258
259
  	if (nsize == 2) {
  		ret_size <<= 32;
a73ac50c4   Scott Wood   [POWERPC] bootwra...
260
  		ret_size |= prop_buf[offset + naddr + 1];
6e1af384f   Scott Wood   [POWERPC] bootwra...
261
  	}
0602801c2   Scott Wood   [POWERPC] bootwra...
262
  	for (;;) {
6e1af384f   Scott Wood   [POWERPC] bootwra...
263
  		prev_naddr = naddr;
0602801c2   Scott Wood   [POWERPC] bootwra...
264
265
  		prev_nsize = nsize;
  		node = parent;
6e1af384f   Scott Wood   [POWERPC] bootwra...
266

0602801c2   Scott Wood   [POWERPC] bootwra...
267
268
269
  		parent = get_parent(node);
  		if (!parent)
  			break;
e5d8d54db   Scott Wood   [POWERPC] bootwra...
270
  		dt_get_reg_format(parent, &naddr, &nsize);
6e1af384f   Scott Wood   [POWERPC] bootwra...
271

a73ac50c4   Scott Wood   [POWERPC] bootwra...
272
273
  		buflen = getprop(node, "ranges", prop_buf,
  				sizeof(prop_buf));
0602801c2   Scott Wood   [POWERPC] bootwra...
274
  		if (buflen == 0)
6e1af384f   Scott Wood   [POWERPC] bootwra...
275
  			continue;
a73ac50c4   Scott Wood   [POWERPC] bootwra...
276
  		if (buflen < 0 || buflen > sizeof(prop_buf))
6e1af384f   Scott Wood   [POWERPC] bootwra...
277
  			return 0;
a73ac50c4   Scott Wood   [POWERPC] bootwra...
278
  		offset = find_range(last_addr, prop_buf, prev_naddr,
0602801c2   Scott Wood   [POWERPC] bootwra...
279
  		                    naddr, prev_nsize, buflen / 4);
6e1af384f   Scott Wood   [POWERPC] bootwra...
280
281
282
  
  		if (offset < 0)
  			return 0;
a73ac50c4   Scott Wood   [POWERPC] bootwra...
283
  		copy_val(this_addr, prop_buf + offset, prev_naddr);
6e1af384f   Scott Wood   [POWERPC] bootwra...
284
285
286
  
  		if (!sub_reg(last_addr, this_addr))
  			return 0;
a73ac50c4   Scott Wood   [POWERPC] bootwra...
287
  		copy_val(this_addr, prop_buf + offset + prev_naddr, naddr);
6e1af384f   Scott Wood   [POWERPC] bootwra...
288

e4bb688d9   Scott Wood   [POWERPC] bootwra...
289
  		if (!add_reg(last_addr, this_addr, naddr))
6e1af384f   Scott Wood   [POWERPC] bootwra...
290
291
292
293
294
  			return 0;
  	}
  
  	if (naddr > 2)
  		return 0;
e4bb688d9   Scott Wood   [POWERPC] bootwra...
295
  	ret_addr = ((u64)last_addr[2] << 32) | last_addr[3];
6e1af384f   Scott Wood   [POWERPC] bootwra...
296
297
298
299
300
301
302
303
304
305
306
307
  
  	if (sizeof(void *) == 4 &&
  	    (ret_addr >= 0x100000000ULL || ret_size > 0x100000000ULL ||
  	     ret_addr + ret_size > 0x100000000ULL))
  		return 0;
  
  	*addr = ret_addr;
  	if (size)
  		*size = ret_size;
  
  	return 1;
  }
8895ea483   Mark A. Greer   [POWERPC] Add dt_...
308
309
310
311
  
  int dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long *size)
  {
  	int reglen;
a73ac50c4   Scott Wood   [POWERPC] bootwra...
312
  	reglen = getprop(node, "reg", prop_buf, sizeof(prop_buf)) / 4;
8895ea483   Mark A. Greer   [POWERPC] Add dt_...
313
314
315
316
317
  	return dt_xlate(node, res, reglen, addr, size);
  }
  
  int dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long *xlated_addr)
  {
a73ac50c4   Scott Wood   [POWERPC] bootwra...
318
  	if (buflen > sizeof(prop_buf))
8895ea483   Mark A. Greer   [POWERPC] Add dt_...
319
  		return 0;
a73ac50c4   Scott Wood   [POWERPC] bootwra...
320
  	memcpy(prop_buf, buf, buflen);
8895ea483   Mark A. Greer   [POWERPC] Add dt_...
321
322
  	return dt_xlate(node, 0, buflen / 4, xlated_addr, NULL);
  }
a73ac50c4   Scott Wood   [POWERPC] bootwra...
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
  
  int dt_is_compatible(void *node, const char *compat)
  {
  	char *buf = (char *)prop_buf;
  	int len, pos;
  
  	len = getprop(node, "compatible", buf, MAX_PROP_LEN);
  	if (len < 0)
  		return 0;
  
  	for (pos = 0; pos < len; pos++) {
  		if (!strcmp(buf + pos, compat))
  			return 1;
  
  		pos += strnlen(&buf[pos], len - pos);
  	}
  
  	return 0;
  }
da0a5f0c6   Laurent Pinchart   [POWERPC] Add boo...
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
  
  int dt_get_virtual_reg(void *node, void **addr, int nres)
  {
  	unsigned long xaddr;
  	int n;
  
  	n = getprop(node, "virtual-reg", addr, nres * 4);
  	if (n > 0)
  		return n / 4;
  
  	for (n = 0; n < nres; n++) {
  		if (!dt_xlate_reg(node, n, &xaddr, NULL))
  			break;
  
  		addr[n] = (void *)xaddr;
  	}
  
  	return n;
  }