|  | /* | 
|  | * Procedures for creating, accessing and interpreting the device tree. | 
|  | * | 
|  | * Paul Mackerras	August 1996. | 
|  | * Copyright (C) 1996-2005 Paul Mackerras. | 
|  | * | 
|  | *  Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. | 
|  | *    {engebret|bergner}@us.ibm.com | 
|  | * | 
|  | *  Adapted for sparc64 by David S. Miller davem@davemloft.net | 
|  | * | 
|  | *      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 <linux/kernel.h> | 
|  | #include <linux/types.h> | 
|  | #include <linux/string.h> | 
|  | #include <linux/mm.h> | 
|  | #include <linux/memblock.h> | 
|  | #include <linux/of.h> | 
|  |  | 
|  | #include <asm/prom.h> | 
|  | #include <asm/oplib.h> | 
|  | #include <asm/irq.h> | 
|  | #include <asm/asi.h> | 
|  | #include <asm/upa.h> | 
|  | #include <asm/smp.h> | 
|  |  | 
|  | #include "prom.h" | 
|  |  | 
|  | void * __init prom_early_alloc(unsigned long size) | 
|  | { | 
|  | unsigned long paddr = memblock_alloc(size, SMP_CACHE_BYTES); | 
|  | void *ret; | 
|  |  | 
|  | if (!paddr) { | 
|  | prom_printf("prom_early_alloc(%lu) failed\n"); | 
|  | prom_halt(); | 
|  | } | 
|  |  | 
|  | ret = __va(paddr); | 
|  | memset(ret, 0, size); | 
|  | prom_early_allocated += size; | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* The following routines deal with the black magic of fully naming a | 
|  | * node. | 
|  | * | 
|  | * Certain well known named nodes are just the simple name string. | 
|  | * | 
|  | * Actual devices have an address specifier appended to the base name | 
|  | * string, like this "foo@addr".  The "addr" can be in any number of | 
|  | * formats, and the platform plus the type of the node determine the | 
|  | * format and how it is constructed. | 
|  | * | 
|  | * For children of the ROOT node, the naming convention is fixed and | 
|  | * determined by whether this is a sun4u or sun4v system. | 
|  | * | 
|  | * For children of other nodes, it is bus type specific.  So | 
|  | * we walk up the tree until we discover a "device_type" property | 
|  | * we recognize and we go from there. | 
|  | * | 
|  | * As an example, the boot device on my workstation has a full path: | 
|  | * | 
|  | *	/pci@1e,600000/ide@d/disk@0,0:c | 
|  | */ | 
|  | static void __init sun4v_path_component(struct device_node *dp, char *tmp_buf) | 
|  | { | 
|  | struct linux_prom64_registers *regs; | 
|  | struct property *rprop; | 
|  | u32 high_bits, low_bits, type; | 
|  |  | 
|  | rprop = of_find_property(dp, "reg", NULL); | 
|  | if (!rprop) | 
|  | return; | 
|  |  | 
|  | regs = rprop->value; | 
|  | if (!of_node_is_root(dp->parent)) { | 
|  | sprintf(tmp_buf, "%s@%x,%x", | 
|  | dp->name, | 
|  | (unsigned int) (regs->phys_addr >> 32UL), | 
|  | (unsigned int) (regs->phys_addr & 0xffffffffUL)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | type = regs->phys_addr >> 60UL; | 
|  | high_bits = (regs->phys_addr >> 32UL) & 0x0fffffffUL; | 
|  | low_bits = (regs->phys_addr & 0xffffffffUL); | 
|  |  | 
|  | if (type == 0 || type == 8) { | 
|  | const char *prefix = (type == 0) ? "m" : "i"; | 
|  |  | 
|  | if (low_bits) | 
|  | sprintf(tmp_buf, "%s@%s%x,%x", | 
|  | dp->name, prefix, | 
|  | high_bits, low_bits); | 
|  | else | 
|  | sprintf(tmp_buf, "%s@%s%x", | 
|  | dp->name, | 
|  | prefix, | 
|  | high_bits); | 
|  | } else if (type == 12) { | 
|  | sprintf(tmp_buf, "%s@%x", | 
|  | dp->name, high_bits); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void __init sun4u_path_component(struct device_node *dp, char *tmp_buf) | 
|  | { | 
|  | struct linux_prom64_registers *regs; | 
|  | struct property *prop; | 
|  |  | 
|  | prop = of_find_property(dp, "reg", NULL); | 
|  | if (!prop) | 
|  | return; | 
|  |  | 
|  | regs = prop->value; | 
|  | if (!of_node_is_root(dp->parent)) { | 
|  | sprintf(tmp_buf, "%s@%x,%x", | 
|  | dp->name, | 
|  | (unsigned int) (regs->phys_addr >> 32UL), | 
|  | (unsigned int) (regs->phys_addr & 0xffffffffUL)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | prop = of_find_property(dp, "upa-portid", NULL); | 
|  | if (!prop) | 
|  | prop = of_find_property(dp, "portid", NULL); | 
|  | if (prop) { | 
|  | unsigned long mask = 0xffffffffUL; | 
|  |  | 
|  | if (tlb_type >= cheetah) | 
|  | mask = 0x7fffff; | 
|  |  | 
|  | sprintf(tmp_buf, "%s@%x,%x", | 
|  | dp->name, | 
|  | *(u32 *)prop->value, | 
|  | (unsigned int) (regs->phys_addr & mask)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* "name@slot,offset"  */ | 
|  | static void __init sbus_path_component(struct device_node *dp, char *tmp_buf) | 
|  | { | 
|  | struct linux_prom_registers *regs; | 
|  | struct property *prop; | 
|  |  | 
|  | prop = of_find_property(dp, "reg", NULL); | 
|  | if (!prop) | 
|  | return; | 
|  |  | 
|  | regs = prop->value; | 
|  | sprintf(tmp_buf, "%s@%x,%x", | 
|  | dp->name, | 
|  | regs->which_io, | 
|  | regs->phys_addr); | 
|  | } | 
|  |  | 
|  | /* "name@devnum[,func]" */ | 
|  | static void __init pci_path_component(struct device_node *dp, char *tmp_buf) | 
|  | { | 
|  | struct linux_prom_pci_registers *regs; | 
|  | struct property *prop; | 
|  | unsigned int devfn; | 
|  |  | 
|  | prop = of_find_property(dp, "reg", NULL); | 
|  | if (!prop) | 
|  | return; | 
|  |  | 
|  | regs = prop->value; | 
|  | devfn = (regs->phys_hi >> 8) & 0xff; | 
|  | if (devfn & 0x07) { | 
|  | sprintf(tmp_buf, "%s@%x,%x", | 
|  | dp->name, | 
|  | devfn >> 3, | 
|  | devfn & 0x07); | 
|  | } else { | 
|  | sprintf(tmp_buf, "%s@%x", | 
|  | dp->name, | 
|  | devfn >> 3); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* "name@UPA_PORTID,offset" */ | 
|  | static void __init upa_path_component(struct device_node *dp, char *tmp_buf) | 
|  | { | 
|  | struct linux_prom64_registers *regs; | 
|  | struct property *prop; | 
|  |  | 
|  | prop = of_find_property(dp, "reg", NULL); | 
|  | if (!prop) | 
|  | return; | 
|  |  | 
|  | regs = prop->value; | 
|  |  | 
|  | prop = of_find_property(dp, "upa-portid", NULL); | 
|  | if (!prop) | 
|  | return; | 
|  |  | 
|  | sprintf(tmp_buf, "%s@%x,%x", | 
|  | dp->name, | 
|  | *(u32 *) prop->value, | 
|  | (unsigned int) (regs->phys_addr & 0xffffffffUL)); | 
|  | } | 
|  |  | 
|  | /* "name@reg" */ | 
|  | static void __init vdev_path_component(struct device_node *dp, char *tmp_buf) | 
|  | { | 
|  | struct property *prop; | 
|  | u32 *regs; | 
|  |  | 
|  | prop = of_find_property(dp, "reg", NULL); | 
|  | if (!prop) | 
|  | return; | 
|  |  | 
|  | regs = prop->value; | 
|  |  | 
|  | sprintf(tmp_buf, "%s@%x", dp->name, *regs); | 
|  | } | 
|  |  | 
|  | /* "name@addrhi,addrlo" */ | 
|  | static void __init ebus_path_component(struct device_node *dp, char *tmp_buf) | 
|  | { | 
|  | struct linux_prom64_registers *regs; | 
|  | struct property *prop; | 
|  |  | 
|  | prop = of_find_property(dp, "reg", NULL); | 
|  | if (!prop) | 
|  | return; | 
|  |  | 
|  | regs = prop->value; | 
|  |  | 
|  | sprintf(tmp_buf, "%s@%x,%x", | 
|  | dp->name, | 
|  | (unsigned int) (regs->phys_addr >> 32UL), | 
|  | (unsigned int) (regs->phys_addr & 0xffffffffUL)); | 
|  | } | 
|  |  | 
|  | /* "name@bus,addr" */ | 
|  | static void __init i2c_path_component(struct device_node *dp, char *tmp_buf) | 
|  | { | 
|  | struct property *prop; | 
|  | u32 *regs; | 
|  |  | 
|  | prop = of_find_property(dp, "reg", NULL); | 
|  | if (!prop) | 
|  | return; | 
|  |  | 
|  | regs = prop->value; | 
|  |  | 
|  | /* This actually isn't right... should look at the #address-cells | 
|  | * property of the i2c bus node etc. etc. | 
|  | */ | 
|  | sprintf(tmp_buf, "%s@%x,%x", | 
|  | dp->name, regs[0], regs[1]); | 
|  | } | 
|  |  | 
|  | /* "name@reg0[,reg1]" */ | 
|  | static void __init usb_path_component(struct device_node *dp, char *tmp_buf) | 
|  | { | 
|  | struct property *prop; | 
|  | u32 *regs; | 
|  |  | 
|  | prop = of_find_property(dp, "reg", NULL); | 
|  | if (!prop) | 
|  | return; | 
|  |  | 
|  | regs = prop->value; | 
|  |  | 
|  | if (prop->length == sizeof(u32) || regs[1] == 1) { | 
|  | sprintf(tmp_buf, "%s@%x", | 
|  | dp->name, regs[0]); | 
|  | } else { | 
|  | sprintf(tmp_buf, "%s@%x,%x", | 
|  | dp->name, regs[0], regs[1]); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* "name@reg0reg1[,reg2reg3]" */ | 
|  | static void __init ieee1394_path_component(struct device_node *dp, char *tmp_buf) | 
|  | { | 
|  | struct property *prop; | 
|  | u32 *regs; | 
|  |  | 
|  | prop = of_find_property(dp, "reg", NULL); | 
|  | if (!prop) | 
|  | return; | 
|  |  | 
|  | regs = prop->value; | 
|  |  | 
|  | if (regs[2] || regs[3]) { | 
|  | sprintf(tmp_buf, "%s@%08x%08x,%04x%08x", | 
|  | dp->name, regs[0], regs[1], regs[2], regs[3]); | 
|  | } else { | 
|  | sprintf(tmp_buf, "%s@%08x%08x", | 
|  | dp->name, regs[0], regs[1]); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void __init __build_path_component(struct device_node *dp, char *tmp_buf) | 
|  | { | 
|  | struct device_node *parent = dp->parent; | 
|  |  | 
|  | if (parent != NULL) { | 
|  | if (!strcmp(parent->type, "pci") || | 
|  | !strcmp(parent->type, "pciex")) { | 
|  | pci_path_component(dp, tmp_buf); | 
|  | return; | 
|  | } | 
|  | if (!strcmp(parent->type, "sbus")) { | 
|  | sbus_path_component(dp, tmp_buf); | 
|  | return; | 
|  | } | 
|  | if (!strcmp(parent->type, "upa")) { | 
|  | upa_path_component(dp, tmp_buf); | 
|  | return; | 
|  | } | 
|  | if (!strcmp(parent->type, "ebus")) { | 
|  | ebus_path_component(dp, tmp_buf); | 
|  | return; | 
|  | } | 
|  | if (!strcmp(parent->name, "usb") || | 
|  | !strcmp(parent->name, "hub")) { | 
|  | usb_path_component(dp, tmp_buf); | 
|  | return; | 
|  | } | 
|  | if (!strcmp(parent->type, "i2c")) { | 
|  | i2c_path_component(dp, tmp_buf); | 
|  | return; | 
|  | } | 
|  | if (!strcmp(parent->type, "firewire")) { | 
|  | ieee1394_path_component(dp, tmp_buf); | 
|  | return; | 
|  | } | 
|  | if (!strcmp(parent->type, "virtual-devices")) { | 
|  | vdev_path_component(dp, tmp_buf); | 
|  | return; | 
|  | } | 
|  | /* "isa" is handled with platform naming */ | 
|  | } | 
|  |  | 
|  | /* Use platform naming convention.  */ | 
|  | if (tlb_type == hypervisor) { | 
|  | sun4v_path_component(dp, tmp_buf); | 
|  | return; | 
|  | } else { | 
|  | sun4u_path_component(dp, tmp_buf); | 
|  | } | 
|  | } | 
|  |  | 
|  | char * __init build_path_component(struct device_node *dp) | 
|  | { | 
|  | char tmp_buf[64], *n; | 
|  |  | 
|  | tmp_buf[0] = '\0'; | 
|  | __build_path_component(dp, tmp_buf); | 
|  | if (tmp_buf[0] == '\0') | 
|  | strcpy(tmp_buf, dp->name); | 
|  |  | 
|  | n = prom_early_alloc(strlen(tmp_buf) + 1); | 
|  | strcpy(n, tmp_buf); | 
|  |  | 
|  | return n; | 
|  | } | 
|  |  | 
|  | static const char *get_mid_prop(void) | 
|  | { | 
|  | return (tlb_type == spitfire ? "upa-portid" : "portid"); | 
|  | } | 
|  |  | 
|  | static void *of_iterate_over_cpus(void *(*func)(struct device_node *, int, int), int arg) | 
|  | { | 
|  | struct device_node *dp; | 
|  | const char *mid_prop; | 
|  |  | 
|  | mid_prop = get_mid_prop(); | 
|  | for_each_node_by_type(dp, "cpu") { | 
|  | int cpuid = of_getintprop_default(dp, mid_prop, -1); | 
|  | const char *this_mid_prop = mid_prop; | 
|  | void *ret; | 
|  |  | 
|  | if (cpuid < 0) { | 
|  | this_mid_prop = "cpuid"; | 
|  | cpuid = of_getintprop_default(dp, this_mid_prop, -1); | 
|  | } | 
|  | if (cpuid < 0) { | 
|  | prom_printf("OF: Serious problem, cpu lacks " | 
|  | "%s property", this_mid_prop); | 
|  | prom_halt(); | 
|  | } | 
|  | #ifdef CONFIG_SMP | 
|  | if (cpuid >= NR_CPUS) { | 
|  | printk(KERN_WARNING "Ignoring CPU %d which is " | 
|  | ">= NR_CPUS (%d)\n", | 
|  | cpuid, NR_CPUS); | 
|  | continue; | 
|  | } | 
|  | #endif | 
|  | ret = func(dp, cpuid, arg); | 
|  | if (ret) | 
|  | return ret; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static void *check_cpu_node(struct device_node *dp, int cpuid, int id) | 
|  | { | 
|  | if (id == cpuid) | 
|  | return dp; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | struct device_node *of_find_node_by_cpuid(int cpuid) | 
|  | { | 
|  | return of_iterate_over_cpus(check_cpu_node, cpuid); | 
|  | } | 
|  |  | 
|  | static void *record_one_cpu(struct device_node *dp, int cpuid, int arg) | 
|  | { | 
|  | ncpus_probed++; | 
|  | #ifdef CONFIG_SMP | 
|  | set_cpu_present(cpuid, true); | 
|  | set_cpu_possible(cpuid, true); | 
|  | #endif | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | void __init of_populate_present_mask(void) | 
|  | { | 
|  | if (tlb_type == hypervisor) | 
|  | return; | 
|  |  | 
|  | ncpus_probed = 0; | 
|  | of_iterate_over_cpus(record_one_cpu, 0); | 
|  | } | 
|  |  | 
|  | static void *fill_in_one_cpu(struct device_node *dp, int cpuid, int arg) | 
|  | { | 
|  | struct device_node *portid_parent = NULL; | 
|  | int portid = -1; | 
|  |  | 
|  | if (of_find_property(dp, "cpuid", NULL)) { | 
|  | int limit = 2; | 
|  |  | 
|  | portid_parent = dp; | 
|  | while (limit--) { | 
|  | portid_parent = portid_parent->parent; | 
|  | if (!portid_parent) | 
|  | break; | 
|  | portid = of_getintprop_default(portid_parent, | 
|  | "portid", -1); | 
|  | if (portid >= 0) | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | #ifndef CONFIG_SMP | 
|  | /* On uniprocessor we only want the values for the | 
|  | * real physical cpu the kernel booted onto, however | 
|  | * cpu_data() only has one entry at index 0. | 
|  | */ | 
|  | if (cpuid != real_hard_smp_processor_id()) | 
|  | return NULL; | 
|  | cpuid = 0; | 
|  | #endif | 
|  |  | 
|  | cpu_data(cpuid).clock_tick = | 
|  | of_getintprop_default(dp, "clock-frequency", 0); | 
|  |  | 
|  | if (portid_parent) { | 
|  | cpu_data(cpuid).dcache_size = | 
|  | of_getintprop_default(dp, "l1-dcache-size", | 
|  | 16 * 1024); | 
|  | cpu_data(cpuid).dcache_line_size = | 
|  | of_getintprop_default(dp, "l1-dcache-line-size", | 
|  | 32); | 
|  | cpu_data(cpuid).icache_size = | 
|  | of_getintprop_default(dp, "l1-icache-size", | 
|  | 8 * 1024); | 
|  | cpu_data(cpuid).icache_line_size = | 
|  | of_getintprop_default(dp, "l1-icache-line-size", | 
|  | 32); | 
|  | cpu_data(cpuid).ecache_size = | 
|  | of_getintprop_default(dp, "l2-cache-size", 0); | 
|  | cpu_data(cpuid).ecache_line_size = | 
|  | of_getintprop_default(dp, "l2-cache-line-size", 0); | 
|  | if (!cpu_data(cpuid).ecache_size || | 
|  | !cpu_data(cpuid).ecache_line_size) { | 
|  | cpu_data(cpuid).ecache_size = | 
|  | of_getintprop_default(portid_parent, | 
|  | "l2-cache-size", | 
|  | (4 * 1024 * 1024)); | 
|  | cpu_data(cpuid).ecache_line_size = | 
|  | of_getintprop_default(portid_parent, | 
|  | "l2-cache-line-size", 64); | 
|  | } | 
|  |  | 
|  | cpu_data(cpuid).core_id = portid + 1; | 
|  | cpu_data(cpuid).proc_id = portid; | 
|  | #ifdef CONFIG_SMP | 
|  | sparc64_multi_core = 1; | 
|  | #endif | 
|  | } else { | 
|  | cpu_data(cpuid).dcache_size = | 
|  | of_getintprop_default(dp, "dcache-size", 16 * 1024); | 
|  | cpu_data(cpuid).dcache_line_size = | 
|  | of_getintprop_default(dp, "dcache-line-size", 32); | 
|  |  | 
|  | cpu_data(cpuid).icache_size = | 
|  | of_getintprop_default(dp, "icache-size", 16 * 1024); | 
|  | cpu_data(cpuid).icache_line_size = | 
|  | of_getintprop_default(dp, "icache-line-size", 32); | 
|  |  | 
|  | cpu_data(cpuid).ecache_size = | 
|  | of_getintprop_default(dp, "ecache-size", | 
|  | (4 * 1024 * 1024)); | 
|  | cpu_data(cpuid).ecache_line_size = | 
|  | of_getintprop_default(dp, "ecache-line-size", 64); | 
|  |  | 
|  | cpu_data(cpuid).core_id = 0; | 
|  | cpu_data(cpuid).proc_id = -1; | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | void __init of_fill_in_cpu_data(void) | 
|  | { | 
|  | if (tlb_type == hypervisor) | 
|  | return; | 
|  |  | 
|  | of_iterate_over_cpus(fill_in_one_cpu, 0); | 
|  |  | 
|  | smp_fill_in_sib_core_maps(); | 
|  | } | 
|  |  | 
|  | void __init of_console_init(void) | 
|  | { | 
|  | char *msg = "OF stdout device is: %s\n"; | 
|  | struct device_node *dp; | 
|  | const char *type; | 
|  | phandle node; | 
|  |  | 
|  | of_console_path = prom_early_alloc(256); | 
|  | if (prom_ihandle2path(prom_stdout, of_console_path, 256) < 0) { | 
|  | prom_printf("Cannot obtain path of stdout.\n"); | 
|  | prom_halt(); | 
|  | } | 
|  | of_console_options = strrchr(of_console_path, ':'); | 
|  | if (of_console_options) { | 
|  | of_console_options++; | 
|  | if (*of_console_options == '\0') | 
|  | of_console_options = NULL; | 
|  | } | 
|  |  | 
|  | node = prom_inst2pkg(prom_stdout); | 
|  | if (!node) { | 
|  | prom_printf("Cannot resolve stdout node from " | 
|  | "instance %08x.\n", prom_stdout); | 
|  | prom_halt(); | 
|  | } | 
|  |  | 
|  | dp = of_find_node_by_phandle(node); | 
|  | type = of_get_property(dp, "device_type", NULL); | 
|  | if (!type) { | 
|  | prom_printf("Console stdout lacks device_type property.\n"); | 
|  | prom_halt(); | 
|  | } | 
|  |  | 
|  | if (strcmp(type, "display") && strcmp(type, "serial")) { | 
|  | prom_printf("Console device_type is neither display " | 
|  | "nor serial.\n"); | 
|  | prom_halt(); | 
|  | } | 
|  |  | 
|  | of_console_device = dp; | 
|  |  | 
|  | printk(msg, of_console_path); | 
|  | } |