aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@sunset.davemloft.net>2006-06-21 18:35:28 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2006-06-24 02:15:02 -0400
commit372b07bb5a13f8a1b8a3ce49cd76d39a79dbd3bd (patch)
tree934af50f039921411e2ab6f4678b083f2b8c7eed
parent8fae097debdf8ac9b66d220ac258535ea09f3898 (diff)
[SPARC64]: Import OBP device tree into kernel data structures.
The basic framework is based on the PowerPC OF code. This code even tries to get the device addressing components correct in the full path names. Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--arch/sparc64/kernel/Makefile2
-rw-r--r--arch/sparc64/kernel/prom.c543
-rw-r--r--arch/sparc64/mm/init.c3
-rw-r--r--include/asm-sparc64/prom.h79
4 files changed, 626 insertions, 1 deletions
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile
index 6f6816488b04..7f00189ed294 100644
--- a/arch/sparc64/kernel/Makefile
+++ b/arch/sparc64/kernel/Makefile
@@ -12,7 +12,7 @@ obj-y := process.o setup.o cpu.o idprom.o \
12 irq.o ptrace.o time.o sys_sparc.o signal.o \ 12 irq.o ptrace.o time.o sys_sparc.o signal.o \
13 unaligned.o central.o pci.o starfire.o semaphore.o \ 13 unaligned.o central.o pci.o starfire.o semaphore.o \
14 power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o \ 14 power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o \
15 visemul.o 15 visemul.o prom.o
16 16
17obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \ 17obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \
18 pci_psycho.o pci_sabre.o pci_schizo.o \ 18 pci_psycho.o pci_sabre.o pci_schizo.o \
diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c
new file mode 100644
index 000000000000..c99b7aae19df
--- /dev/null
+++ b/arch/sparc64/kernel/prom.c
@@ -0,0 +1,543 @@
1/*
2 * Procedures for creating, accessing and interpreting the device tree.
3 *
4 * Paul Mackerras August 1996.
5 * Copyright (C) 1996-2005 Paul Mackerras.
6 *
7 * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
8 * {engebret|bergner}@us.ibm.com
9 *
10 * Adapted for sparc64 by David S. Miller davem@davemloft.net
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version
15 * 2 of the License, or (at your option) any later version.
16 */
17
18#include <linux/kernel.h>
19#include <linux/types.h>
20#include <linux/string.h>
21#include <linux/mm.h>
22#include <linux/bootmem.h>
23
24#include <asm/prom.h>
25#include <asm/oplib.h>
26
27static struct device_node *allnodes;
28
29struct device_node *of_get_parent(const struct device_node *node)
30{
31 struct device_node *np;
32
33 if (!node)
34 return NULL;
35
36 np = node->parent;
37
38 return np;
39}
40
41struct device_node *of_get_next_child(const struct device_node *node,
42 struct device_node *prev)
43{
44 struct device_node *next;
45
46 next = prev ? prev->sibling : node->child;
47 for (; next != 0; next = next->sibling) {
48 break;
49 }
50
51 return next;
52}
53
54struct device_node *of_find_node_by_path(const char *path)
55{
56 struct device_node *np = allnodes;
57
58 for (; np != 0; np = np->allnext) {
59 if (np->full_name != 0 && strcmp(np->full_name, path) == 0)
60 break;
61 }
62
63 return np;
64}
65
66struct property *of_find_property(struct device_node *np, const char *name,
67 int *lenp)
68{
69 struct property *pp;
70
71 for (pp = np->properties; pp != 0; pp = pp->next) {
72 if (strcmp(pp->name, name) == 0) {
73 if (lenp != 0)
74 *lenp = pp->length;
75 break;
76 }
77 }
78 return pp;
79}
80
81static unsigned int prom_early_allocated;
82
83static void * __init prom_early_alloc(unsigned long size)
84{
85 void *ret;
86
87 ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL);
88 if (ret != NULL)
89 memset(ret, 0, size);
90
91 prom_early_allocated += size;
92
93 return ret;
94}
95
96static int is_root_node(const struct device_node *dp)
97{
98 if (!dp)
99 return 0;
100
101 return (dp->parent == NULL);
102}
103
104/* The following routines deal with the black magic of fully naming a
105 * node.
106 *
107 * Certain well known named nodes are just the simple name string.
108 *
109 * Actual devices have an address specifier appended to the base name
110 * string, like this "foo@addr". The "addr" can be in any number of
111 * formats, and the platform plus the type of the node determine the
112 * format and how it is constructed.
113 *
114 * For children of the ROOT node, the naming convention is fixed and
115 * determined by whether this is a sun4u or sun4v system.
116 *
117 * For children of other nodes, it is bus type specific. So
118 * we walk up the tree until we discover a "device_type" property
119 * we recognize and we go from there.
120 *
121 * As an example, the boot device on my workstation has a full path:
122 *
123 * /pci@1e,600000/ide@d/disk@0,0:c
124 */
125static void __init sun4v_path_component(struct device_node *dp, char *tmp_buf)
126{
127 struct linux_prom64_registers *regs;
128 struct property *rprop;
129 u32 high_bits, low_bits, type;
130
131 rprop = of_find_property(dp, "reg", NULL);
132 if (!rprop)
133 return;
134
135 regs = rprop->value;
136 if (!is_root_node(dp->parent)) {
137 sprintf(tmp_buf, "%s@%x,%x",
138 dp->name,
139 (unsigned int) (regs->phys_addr >> 32UL),
140 (unsigned int) (regs->phys_addr & 0xffffffffUL));
141 return;
142 }
143
144 type = regs->phys_addr >> 60UL;
145 high_bits = (regs->phys_addr >> 32UL) & 0x0fffffffUL;
146 low_bits = (regs->phys_addr & 0xffffffffUL);
147
148 if (type == 0 || type == 8) {
149 const char *prefix = (type == 0) ? "m" : "i";
150
151 if (low_bits)
152 sprintf(tmp_buf, "%s@%s%x,%x",
153 dp->name, prefix,
154 high_bits, low_bits);
155 else
156 sprintf(tmp_buf, "%s@%s%x",
157 dp->name,
158 prefix,
159 high_bits);
160 } else if (type == 12) {
161 sprintf(tmp_buf, "%s@%x",
162 dp->name, high_bits);
163 }
164}
165
166static void __init sun4u_path_component(struct device_node *dp, char *tmp_buf)
167{
168 struct linux_prom64_registers *regs;
169 struct property *prop;
170
171 prop = of_find_property(dp, "reg", NULL);
172 if (!prop)
173 return;
174
175 regs = prop->value;
176 if (!is_root_node(dp->parent)) {
177 sprintf(tmp_buf, "%s@%x,%x",
178 dp->name,
179 (unsigned int) (regs->phys_addr >> 32UL),
180 (unsigned int) (regs->phys_addr & 0xffffffffUL));
181 return;
182 }
183
184 prop = of_find_property(dp, "upa-portid", NULL);
185 if (!prop)
186 prop = of_find_property(dp, "portid", NULL);
187 if (prop) {
188 unsigned long mask = 0xffffffffUL;
189
190 if (tlb_type >= cheetah)
191 mask = 0x7fffff;
192
193 sprintf(tmp_buf, "%s@%x,%x",
194 dp->name,
195 *(u32 *)prop->value,
196 (unsigned int) (regs->phys_addr & mask));
197 }
198}
199
200/* "name@slot,offset" */
201static void __init sbus_path_component(struct device_node *dp, char *tmp_buf)
202{
203 struct linux_prom_registers *regs;
204 struct property *prop;
205
206 prop = of_find_property(dp, "reg", NULL);
207 if (!prop)
208 return;
209
210 regs = prop->value;
211 sprintf(tmp_buf, "%s@%x,%x",
212 dp->name,
213 regs->which_io,
214 regs->phys_addr);
215}
216
217/* "name@devnum[,func]" */
218static void __init pci_path_component(struct device_node *dp, char *tmp_buf)
219{
220 struct linux_prom_pci_registers *regs;
221 struct property *prop;
222 unsigned int devfn;
223
224 prop = of_find_property(dp, "reg", NULL);
225 if (!prop)
226 return;
227
228 regs = prop->value;
229 devfn = (regs->phys_hi >> 8) & 0xff;
230 if (devfn & 0x07) {
231 sprintf(tmp_buf, "%s@%x,%x",
232 dp->name,
233 devfn >> 3,
234 devfn & 0x07);
235 } else {
236 sprintf(tmp_buf, "%s@%x",
237 dp->name,
238 devfn >> 3);
239 }
240}
241
242/* "name@UPA_PORTID,offset" */
243static void __init upa_path_component(struct device_node *dp, char *tmp_buf)
244{
245 struct linux_prom64_registers *regs;
246 struct property *prop;
247
248 prop = of_find_property(dp, "reg", NULL);
249 if (!prop)
250 return;
251
252 regs = prop->value;
253
254 prop = of_find_property(dp, "upa-portid", NULL);
255 if (!prop)
256 return;
257
258 sprintf(tmp_buf, "%s@%x,%x",
259 dp->name,
260 *(u32 *) prop->value,
261 (unsigned int) (regs->phys_addr & 0xffffffffUL));
262}
263
264/* "name@reg" */
265static void __init vdev_path_component(struct device_node *dp, char *tmp_buf)
266{
267 struct property *prop;
268 u32 *regs;
269
270 prop = of_find_property(dp, "reg", NULL);
271 if (!prop)
272 return;
273
274 regs = prop->value;
275
276 sprintf(tmp_buf, "%s@%x", dp->name, *regs);
277}
278
279/* "name@addrhi,addrlo" */
280static void __init ebus_path_component(struct device_node *dp, char *tmp_buf)
281{
282 struct linux_prom64_registers *regs;
283 struct property *prop;
284
285 prop = of_find_property(dp, "reg", NULL);
286 if (!prop)
287 return;
288
289 regs = prop->value;
290
291 sprintf(tmp_buf, "%s@%x,%x",
292 dp->name,
293 (unsigned int) (regs->phys_addr >> 32UL),
294 (unsigned int) (regs->phys_addr & 0xffffffffUL));
295}
296
297/* "name@bus,addr" */
298static void __init i2c_path_component(struct device_node *dp, char *tmp_buf)
299{
300 struct property *prop;
301 u32 *regs;
302
303 prop = of_find_property(dp, "reg", NULL);
304 if (!prop)
305 return;
306
307 regs = prop->value;
308
309 /* This actually isn't right... should look at the #address-cells
310 * property of the i2c bus node etc. etc.
311 */
312 sprintf(tmp_buf, "%s@%x,%x",
313 dp->name, regs[0], regs[1]);
314}
315
316/* "name@reg0[,reg1]" */
317static void __init usb_path_component(struct device_node *dp, char *tmp_buf)
318{
319 struct property *prop;
320 u32 *regs;
321
322 prop = of_find_property(dp, "reg", NULL);
323 if (!prop)
324 return;
325
326 regs = prop->value;
327
328 if (prop->length == sizeof(u32) || regs[1] == 1) {
329 sprintf(tmp_buf, "%s@%x",
330 dp->name, regs[0]);
331 } else {
332 sprintf(tmp_buf, "%s@%x,%x",
333 dp->name, regs[0], regs[1]);
334 }
335}
336
337/* "name@reg0reg1[,reg2reg3]" */
338static void __init ieee1394_path_component(struct device_node *dp, char *tmp_buf)
339{
340 struct property *prop;
341 u32 *regs;
342
343 prop = of_find_property(dp, "reg", NULL);
344 if (!prop)
345 return;
346
347 regs = prop->value;
348
349 if (regs[2] || regs[3]) {
350 sprintf(tmp_buf, "%s@%08x%08x,%04x%08x",
351 dp->name, regs[0], regs[1], regs[2], regs[3]);
352 } else {
353 sprintf(tmp_buf, "%s@%08x%08x",
354 dp->name, regs[0], regs[1]);
355 }
356}
357
358static void __init __build_path_component(struct device_node *dp, char *tmp_buf)
359{
360 struct device_node *parent = dp->parent;
361
362 if (parent != NULL) {
363 if (!strcmp(parent->type, "pci") ||
364 !strcmp(parent->type, "pciex"))
365 return pci_path_component(dp, tmp_buf);
366 if (!strcmp(parent->type, "sbus"))
367 return sbus_path_component(dp, tmp_buf);
368 if (!strcmp(parent->type, "upa"))
369 return upa_path_component(dp, tmp_buf);
370 if (!strcmp(parent->type, "ebus"))
371 return ebus_path_component(dp, tmp_buf);
372 if (!strcmp(parent->name, "usb") ||
373 !strcmp(parent->name, "hub"))
374 return usb_path_component(dp, tmp_buf);
375 if (!strcmp(parent->type, "i2c"))
376 return i2c_path_component(dp, tmp_buf);
377 if (!strcmp(parent->type, "firewire"))
378 return ieee1394_path_component(dp, tmp_buf);
379 if (!strcmp(parent->type, "virtual-devices"))
380 return vdev_path_component(dp, tmp_buf);
381
382 /* "isa" is handled with platform naming */
383 }
384
385 /* Use platform naming convention. */
386 if (tlb_type == hypervisor)
387 return sun4v_path_component(dp, tmp_buf);
388 else
389 return sun4u_path_component(dp, tmp_buf);
390}
391
392static char * __init build_path_component(struct device_node *dp)
393{
394 char tmp_buf[64], *n;
395
396 tmp_buf[0] = '\0';
397 __build_path_component(dp, tmp_buf);
398 if (tmp_buf[0] == '\0')
399 strcpy(tmp_buf, dp->name);
400
401 n = prom_early_alloc(strlen(tmp_buf) + 1);
402 strcpy(n, tmp_buf);
403
404 return n;
405}
406
407static char * __init build_full_name(struct device_node *dp)
408{
409 int len, ourlen, plen;
410 char *n;
411
412 plen = strlen(dp->parent->full_name);
413 ourlen = strlen(dp->path_component_name);
414 len = ourlen + plen + 2;
415
416 n = prom_early_alloc(len);
417 strcpy(n, dp->parent->full_name);
418 if (!is_root_node(dp->parent)) {
419 strcpy(n + plen, "/");
420 plen++;
421 }
422 strcpy(n + plen, dp->path_component_name);
423
424 return n;
425}
426
427static struct property * __init build_one_prop(phandle node, char *prev)
428{
429 static struct property *tmp = NULL;
430 struct property *p;
431
432 if (tmp) {
433 p = tmp;
434 memset(p, 0, sizeof(*p) + 32);
435 tmp = NULL;
436 } else
437 p = prom_early_alloc(sizeof(struct property) + 32);
438
439 p->name = (char *) (p + 1);
440 if (prev == NULL) {
441 prom_firstprop(node, p->name);
442 } else {
443 prom_nextprop(node, prev, p->name);
444 }
445 if (strlen(p->name) == 0) {
446 tmp = p;
447 return NULL;
448 }
449 p->length = prom_getproplen(node, p->name);
450 if (p->length <= 0) {
451 p->length = 0;
452 } else {
453 p->value = prom_early_alloc(p->length);
454 prom_getproperty(node, p->name, p->value, p->length);
455 }
456 return p;
457}
458
459static struct property * __init build_prop_list(phandle node)
460{
461 struct property *head, *tail;
462
463 head = tail = build_one_prop(node, NULL);
464 while(tail) {
465 tail->next = build_one_prop(node, tail->name);
466 tail = tail->next;
467 }
468
469 return head;
470}
471
472static char * __init get_one_property(phandle node, const char *name)
473{
474 char *buf = "<NULL>";
475 int len;
476
477 len = prom_getproplen(node, name);
478 if (len > 0) {
479 buf = prom_early_alloc(len);
480 prom_getproperty(node, name, buf, len);
481 }
482
483 return buf;
484}
485
486static struct device_node * __init create_node(phandle node)
487{
488 struct device_node *dp;
489
490 if (!node)
491 return NULL;
492
493 dp = prom_early_alloc(sizeof(*dp));
494
495 kref_init(&dp->kref);
496
497 dp->name = get_one_property(node, "name");
498 dp->type = get_one_property(node, "device_type");
499 dp->node = node;
500
501 /* Build interrupts later... */
502
503 dp->properties = build_prop_list(node);
504
505 return dp;
506}
507
508static struct device_node * __init build_tree(struct device_node *parent, phandle node, struct device_node ***nextp)
509{
510 struct device_node *dp;
511
512 dp = create_node(node);
513 if (dp) {
514 *(*nextp) = dp;
515 *nextp = &dp->allnext;
516
517 dp->parent = parent;
518 dp->path_component_name = build_path_component(dp);
519 dp->full_name = build_full_name(dp);
520
521 dp->child = build_tree(dp, prom_getchild(node), nextp);
522
523 dp->sibling = build_tree(parent, prom_getsibling(node), nextp);
524 }
525
526 return dp;
527}
528
529void __init prom_build_devicetree(void)
530{
531 struct device_node **nextp;
532
533 allnodes = create_node(prom_root_node);
534 allnodes->path_component_name = "";
535 allnodes->full_name = "/";
536
537 nextp = &allnodes->allnext;
538 allnodes->child = build_tree(allnodes,
539 prom_getchild(allnodes->node),
540 &nextp);
541 printk("PROM: Built device tree with %u bytes of memory.\n",
542 prom_early_allocated);
543}
diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c
index 1539a8362b6f..45a70d677d8b 100644
--- a/arch/sparc64/mm/init.c
+++ b/arch/sparc64/mm/init.c
@@ -42,6 +42,7 @@
42#include <asm/sections.h> 42#include <asm/sections.h>
43#include <asm/tsb.h> 43#include <asm/tsb.h>
44#include <asm/hypervisor.h> 44#include <asm/hypervisor.h>
45#include <asm/prom.h>
45 46
46extern void device_scan(void); 47extern void device_scan(void);
47 48
@@ -1339,6 +1340,8 @@ void __init paging_init(void)
1339 1340
1340 kernel_physical_mapping_init(); 1341 kernel_physical_mapping_init();
1341 1342
1343 prom_build_devicetree();
1344
1342 { 1345 {
1343 unsigned long zones_size[MAX_NR_ZONES]; 1346 unsigned long zones_size[MAX_NR_ZONES];
1344 unsigned long zholes_size[MAX_NR_ZONES]; 1347 unsigned long zholes_size[MAX_NR_ZONES];
diff --git a/include/asm-sparc64/prom.h b/include/asm-sparc64/prom.h
new file mode 100644
index 000000000000..44bcc83267f3
--- /dev/null
+++ b/include/asm-sparc64/prom.h
@@ -0,0 +1,79 @@
1#ifndef _SPARC64_PROM_H
2#define _SPARC64_PROM_H
3#ifdef __KERNEL__
4
5
6/*
7 * Definitions for talking to the Open Firmware PROM on
8 * Power Macintosh computers.
9 *
10 * Copyright (C) 1996-2005 Paul Mackerras.
11 *
12 * Updates for PPC64 by Peter Bergner & David Engebretsen, IBM Corp.
13 * Updates for SPARC64 by David S. Miller
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version
18 * 2 of the License, or (at your option) any later version.
19 */
20
21#include <linux/types.h>
22#include <linux/proc_fs.h>
23#include <asm/atomic.h>
24
25typedef u32 phandle;
26typedef u32 ihandle;
27
28struct interrupt_info {
29 int line;
30 int sense; /* +ve/-ve logic, edge or level, etc. */
31};
32
33struct property {
34 char *name;
35 int length;
36 void *value;
37 struct property *next;
38};
39
40struct device_node {
41 char *name;
42 char *type;
43 phandle node;
44 phandle linux_phandle;
45 int n_intrs;
46 struct interrupt_info *intrs;
47 char *path_component_name;
48 char *full_name;
49
50 struct property *properties;
51 struct property *deadprops; /* removed properties */
52 struct device_node *parent;
53 struct device_node *child;
54 struct device_node *sibling;
55 struct device_node *next; /* next device of same type */
56 struct device_node *allnext; /* next in list of all nodes */
57 struct proc_dir_entry *pde; /* this node's proc directory */
58 struct kref kref;
59 unsigned long _flags;
60 void *data;
61};
62
63static inline void set_node_proc_entry(struct device_node *dn, struct proc_dir_entry *de)
64{
65 dn->pde = de;
66}
67
68extern struct device_node *of_find_node_by_path(const char *path);
69extern struct device_node *of_get_parent(const struct device_node *node);
70extern struct device_node *of_get_next_child(const struct device_node *node,
71 struct device_node *prev);
72extern struct property *of_find_property(struct device_node *np,
73 const char *name,
74 int *lenp);
75
76extern void prom_build_devicetree(void);
77
78#endif /* __KERNEL__ */
79#endif /* _SPARC64_PROM_H */