Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/powerpc/boot/devtree.c
10817 views
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
timebase_period_ns = 1000000000 / tb;
79
}
80
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
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
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
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
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
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
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
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
*/
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 */
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
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
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
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
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
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
374