Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5cbc3073 authored by David S. Miller's avatar David S. Miller
Browse files

[SPARC64]: Use machine description and OBP properly for cpu probing.

parent e01c0d6d
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -8,11 +8,11 @@ EXTRA_CFLAGS := -Werror
extra-y		:= head.o init_task.o vmlinux.lds

obj-y		:= process.o setup.o cpu.o idprom.o \
		   traps.o devices.o auxio.o una_asm.o \
		   traps.o auxio.o una_asm.o \
		   irq.o ptrace.o time.o sys_sparc.o signal.o \
		   unaligned.o central.o pci.o starfire.o semaphore.o \
		   power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o \
		   visemul.o prom.o of_device.o hvapi.o sstate.o
		   visemul.o prom.o of_device.o hvapi.o sstate.o mdesc.o

obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_PCI)	 += ebus.o isa.o pci_common.o pci_iommu.o \

arch/sparc64/kernel/devices.c

deleted100644 → 0
+0 −196
Original line number Diff line number Diff line
/* devices.c: Initial scan of the prom device tree for important
 *            Sparc device nodes which we need to find.
 *
 * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
 */

#include <linux/kernel.h>
#include <linux/threads.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/string.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/bootmem.h>

#include <asm/page.h>
#include <asm/oplib.h>
#include <asm/system.h>
#include <asm/smp.h>
#include <asm/spitfire.h>
#include <asm/timer.h>
#include <asm/cpudata.h>

/* Used to synchronize accesses to NatSemi SUPER I/O chip configure
 * operations in asm/ns87303.h
 */
DEFINE_SPINLOCK(ns87303_lock);

extern void cpu_probe(void);
extern void central_probe(void);

static const char *cpu_mid_prop(void)
{
	if (tlb_type == spitfire)
		return "upa-portid";
	return "portid";
}

static int get_cpu_mid(struct device_node *dp)
{
	struct property *prop;

	if (tlb_type == hypervisor) {
		struct linux_prom64_registers *reg;
		int len;

		prop = of_find_property(dp, "cpuid", &len);
		if (prop && len == 4)
			return *(int *) prop->value;

		prop = of_find_property(dp, "reg", NULL);
		reg = prop->value;
		return (reg[0].phys_addr >> 32) & 0x0fffffffUL;
	} else {
		const char *prop_name = cpu_mid_prop();

		prop = of_find_property(dp, prop_name, NULL);
		if (prop)
			return *(int *) prop->value;
		return 0;
	}
}

static int check_cpu_node(struct device_node *dp, int *cur_inst,
			  int (*compare)(struct device_node *, int, void *),
			  void *compare_arg,
			  struct device_node **dev_node, int *mid)
{
	if (!compare(dp, *cur_inst, compare_arg)) {
		if (dev_node)
			*dev_node = dp;
		if (mid)
			*mid = get_cpu_mid(dp);
		return 0;
	}

	(*cur_inst)++;

	return -ENODEV;
}

static int __cpu_find_by(int (*compare)(struct device_node *, int, void *),
			 void *compare_arg,
			 struct device_node **dev_node, int *mid)
{
	struct device_node *dp;
	int cur_inst;

	cur_inst = 0;
	for_each_node_by_type(dp, "cpu") {
		int err = check_cpu_node(dp, &cur_inst,
					 compare, compare_arg,
					 dev_node, mid);
		if (err == 0)
			return 0;
	}

	return -ENODEV;
}

static int cpu_instance_compare(struct device_node *dp, int instance, void *_arg)
{
	int desired_instance = (int) (long) _arg;

	if (instance == desired_instance)
		return 0;
	return -ENODEV;
}

int cpu_find_by_instance(int instance, struct device_node **dev_node, int *mid)
{
	return __cpu_find_by(cpu_instance_compare, (void *)(long)instance,
			     dev_node, mid);
}

static int cpu_mid_compare(struct device_node *dp, int instance, void *_arg)
{
	int desired_mid = (int) (long) _arg;
	int this_mid;

	this_mid = get_cpu_mid(dp);
	if (this_mid == desired_mid)
		return 0;
	return -ENODEV;
}

int cpu_find_by_mid(int mid, struct device_node **dev_node)
{
	return __cpu_find_by(cpu_mid_compare, (void *)(long)mid,
			     dev_node, NULL);
}

void __init device_scan(void)
{
	/* FIX ME FAST... -DaveM */
	ioport_resource.end = 0xffffffffffffffffUL;

	prom_printf("Booting Linux...\n");

#ifndef CONFIG_SMP
	{
		struct device_node *dp;
		int err, def;

		err = cpu_find_by_instance(0, &dp, NULL);
		if (err) {
			prom_printf("No cpu nodes, cannot continue\n");
			prom_halt();
		}
		cpu_data(0).clock_tick =
			of_getintprop_default(dp, "clock-frequency", 0);

		def = ((tlb_type == hypervisor) ?
		       (8 * 1024) :
		       (16 * 1024));
		cpu_data(0).dcache_size = of_getintprop_default(dp,
								"dcache-size",
								def);

		def = 32;
		cpu_data(0).dcache_line_size =
			of_getintprop_default(dp, "dcache-line-size", def);

		def = 16 * 1024;
		cpu_data(0).icache_size = of_getintprop_default(dp,
								"icache-size",
								def);

		def = 32;
		cpu_data(0).icache_line_size =
			of_getintprop_default(dp, "icache-line-size", def);

		def = ((tlb_type == hypervisor) ?
		       (3 * 1024 * 1024) :
		       (4 * 1024 * 1024));
		cpu_data(0).ecache_size = of_getintprop_default(dp,
								"ecache-size",
								def);

		def = 64;
		cpu_data(0).ecache_line_size =
			of_getintprop_default(dp, "ecache-line-size", def);
		printk("CPU[0]: Caches "
		       "D[sz(%d):line_sz(%d)] "
		       "I[sz(%d):line_sz(%d)] "
		       "E[sz(%d):line_sz(%d)]\n",
		       cpu_data(0).dcache_size, cpu_data(0).dcache_line_size,
		       cpu_data(0).icache_size, cpu_data(0).icache_line_size,
		       cpu_data(0).ecache_size, cpu_data(0).ecache_line_size);
	}
#endif

	central_probe();

	cpu_probe();
}
+9 −0
Original line number Diff line number Diff line
@@ -1949,3 +1949,12 @@ sun4v_mach_set_soft_state:
	ta	HV_FAST_TRAP
	retl
	 nop

	.globl	sun4v_mach_desc
sun4v_mach_desc:
	mov	%o2, %o4
	mov	HV_FAST_MACH_DESC, %o5
	ta	HV_FAST_TRAP
	stx	%o1, [%o4]
	retl
	 nop
+53 −30
Original line number Diff line number Diff line
@@ -171,8 +171,6 @@ int show_interrupts(struct seq_file *p, void *v)
	return 0;
}

extern unsigned long real_hard_smp_processor_id(void);

static unsigned int sun4u_compute_tid(unsigned long imap, unsigned long cpuid)
{
	unsigned int tid;
@@ -694,9 +692,20 @@ void init_irqwork_curcpu(void)
	trap_block[cpu].irq_worklist = 0;
}

static void __cpuinit register_one_mondo(unsigned long paddr, unsigned long type)
/* Please be very careful with register_one_mondo() and
 * sun4v_register_mondo_queues().
 *
 * On SMP this gets invoked from the CPU trampoline before
 * the cpu has fully taken over the trap table from OBP,
 * and it's kernel stack + %g6 thread register state is
 * not fully cooked yet.
 *
 * Therefore you cannot make any OBP calls, not even prom_printf,
 * from these two routines.
 */
static void __cpuinit register_one_mondo(unsigned long paddr, unsigned long type, unsigned long qmask)
{
	unsigned long num_entries = 128;
	unsigned long num_entries = (qmask + 1) / 64;
	unsigned long status;

	status = sun4v_cpu_qconf(type, paddr, num_entries);
@@ -711,44 +720,58 @@ static void __cpuinit sun4v_register_mondo_queues(int this_cpu)
{
	struct trap_per_cpu *tb = &trap_block[this_cpu];

	register_one_mondo(tb->cpu_mondo_pa, HV_CPU_QUEUE_CPU_MONDO);
	register_one_mondo(tb->dev_mondo_pa, HV_CPU_QUEUE_DEVICE_MONDO);
	register_one_mondo(tb->resum_mondo_pa, HV_CPU_QUEUE_RES_ERROR);
	register_one_mondo(tb->nonresum_mondo_pa, HV_CPU_QUEUE_NONRES_ERROR);
	register_one_mondo(tb->cpu_mondo_pa, HV_CPU_QUEUE_CPU_MONDO,
			   tb->cpu_mondo_qmask);
	register_one_mondo(tb->dev_mondo_pa, HV_CPU_QUEUE_DEVICE_MONDO,
			   tb->dev_mondo_qmask);
	register_one_mondo(tb->resum_mondo_pa, HV_CPU_QUEUE_RES_ERROR,
			   tb->resum_qmask);
	register_one_mondo(tb->nonresum_mondo_pa, HV_CPU_QUEUE_NONRES_ERROR,
			   tb->nonresum_qmask);
}

static void __cpuinit alloc_one_mondo(unsigned long *pa_ptr, int use_bootmem)
static void __cpuinit alloc_one_mondo(unsigned long *pa_ptr, unsigned long qmask, int use_bootmem)
{
	void *page;
	unsigned long size = PAGE_ALIGN(qmask + 1);
	unsigned long order = get_order(size);
	void *p = NULL;

	if (use_bootmem)
		page = alloc_bootmem_low_pages(PAGE_SIZE);
	else
		page = (void *) get_zeroed_page(GFP_ATOMIC);
	if (use_bootmem) {
		p = __alloc_bootmem_low(size, size, 0);
	} else {
		struct page *page = alloc_pages(GFP_ATOMIC | __GFP_ZERO, order);
		if (page)
			p = page_address(page);
	}

	if (!page) {
	if (!p) {
		prom_printf("SUN4V: Error, cannot allocate mondo queue.\n");
		prom_halt();
	}

	*pa_ptr = __pa(page);
	*pa_ptr = __pa(p);
}

static void __cpuinit alloc_one_kbuf(unsigned long *pa_ptr, int use_bootmem)
static void __cpuinit alloc_one_kbuf(unsigned long *pa_ptr, unsigned long qmask, int use_bootmem)
{
	void *page;
	unsigned long size = PAGE_ALIGN(qmask + 1);
	unsigned long order = get_order(size);
	void *p = NULL;

	if (use_bootmem)
		page = alloc_bootmem_low_pages(PAGE_SIZE);
	else
		page = (void *) get_zeroed_page(GFP_ATOMIC);
	if (use_bootmem) {
		p = __alloc_bootmem_low(size, size, 0);
	} else {
		struct page *page = alloc_pages(GFP_ATOMIC | __GFP_ZERO, order);
		if (page)
			p = page_address(page);
	}

	if (!page) {
	if (!p) {
		prom_printf("SUN4V: Error, cannot allocate kbuf page.\n");
		prom_halt();
	}

	*pa_ptr = __pa(page);
	*pa_ptr = __pa(p);
}

static void __cpuinit init_cpu_send_mondo_info(struct trap_per_cpu *tb, int use_bootmem)
@@ -779,12 +802,12 @@ void __cpuinit sun4v_init_mondo_queues(int use_bootmem, int cpu, int alloc, int
	struct trap_per_cpu *tb = &trap_block[cpu];

	if (alloc) {
		alloc_one_mondo(&tb->cpu_mondo_pa, use_bootmem);
		alloc_one_mondo(&tb->dev_mondo_pa, use_bootmem);
		alloc_one_mondo(&tb->resum_mondo_pa, use_bootmem);
		alloc_one_kbuf(&tb->resum_kernel_buf_pa, use_bootmem);
		alloc_one_mondo(&tb->nonresum_mondo_pa, use_bootmem);
		alloc_one_kbuf(&tb->nonresum_kernel_buf_pa, use_bootmem);
		alloc_one_mondo(&tb->cpu_mondo_pa, tb->cpu_mondo_qmask, use_bootmem);
		alloc_one_mondo(&tb->dev_mondo_pa, tb->dev_mondo_qmask, use_bootmem);
		alloc_one_mondo(&tb->resum_mondo_pa, tb->resum_qmask, use_bootmem);
		alloc_one_kbuf(&tb->resum_kernel_buf_pa, tb->resum_qmask, use_bootmem);
		alloc_one_mondo(&tb->nonresum_mondo_pa, tb->nonresum_qmask, use_bootmem);
		alloc_one_kbuf(&tb->nonresum_kernel_buf_pa, tb->nonresum_qmask, use_bootmem);

		init_cpu_send_mondo_info(tb, use_bootmem);
	}
+619 −0
Original line number Diff line number Diff line
/* mdesc.c: Sun4V machine description handling.
 *
 * Copyright (C) 2007 David S. Miller <davem@davemloft.net>
 */
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/bootmem.h>
#include <linux/log2.h>

#include <asm/hypervisor.h>
#include <asm/mdesc.h>
#include <asm/prom.h>
#include <asm/oplib.h>
#include <asm/smp.h>

/* Unlike the OBP device tree, the machine description is a full-on
 * DAG.  An arbitrary number of ARCs are possible from one
 * node to other nodes and thus we can't use the OBP device_node
 * data structure to represent these nodes inside of the kernel.
 *
 * Actually, it isn't even a DAG, because there are back pointers
 * which create cycles in the graph.
 *
 * mdesc_hdr and mdesc_elem describe the layout of the data structure
 * we get from the Hypervisor.
 */
struct mdesc_hdr {
	u32	version; /* Transport version */
	u32	node_sz; /* node block size */
	u32	name_sz; /* name block size */
	u32	data_sz; /* data block size */
};

struct mdesc_elem {
	u8	tag;
#define MD_LIST_END	0x00
#define MD_NODE		0x4e
#define MD_NODE_END	0x45
#define MD_NOOP		0x20
#define MD_PROP_ARC	0x61
#define MD_PROP_VAL	0x76
#define MD_PROP_STR	0x73
#define MD_PROP_DATA	0x64
	u8	name_len;
	u16	resv;
	u32	name_offset;
	union {
		struct {
			u32	data_len;
			u32	data_offset;
		} data;
		u64	val;
	} d;
};

static struct mdesc_hdr *main_mdesc;
static struct mdesc_node *allnodes;

static struct mdesc_node *allnodes_tail;
static unsigned int unique_id;

static struct mdesc_node **mdesc_hash;
static unsigned int mdesc_hash_size;

static inline unsigned int node_hashfn(u64 node)
{
	return ((unsigned int) (node ^ (node >> 8) ^ (node >> 16)))
		& (mdesc_hash_size - 1);
}

static inline void hash_node(struct mdesc_node *mp)
{
	struct mdesc_node **head = &mdesc_hash[node_hashfn(mp->node)];

	mp->hash_next = *head;
	*head = mp;

	if (allnodes_tail) {
		allnodes_tail->allnodes_next = mp;
		allnodes_tail = mp;
	} else {
		allnodes = allnodes_tail = mp;
	}
}

static struct mdesc_node *find_node(u64 node)
{
	struct mdesc_node *mp = mdesc_hash[node_hashfn(node)];

	while (mp) {
		if (mp->node == node)
			return mp;

		mp = mp->hash_next;
	}
	return NULL;
}

struct property *md_find_property(const struct mdesc_node *mp,
				  const char *name,
				  int *lenp)
{
	struct property *pp;

	for (pp = mp->properties; pp != 0; pp = pp->next) {
		if (strcasecmp(pp->name, name) == 0) {
			if (lenp)
				*lenp = pp->length;
			break;
		}
	}
	return pp;
}
EXPORT_SYMBOL(md_find_property);

/*
 * Find a property with a given name for a given node
 * and return the value.
 */
const void *md_get_property(const struct mdesc_node *mp, const char *name,
			    int *lenp)
{
	struct property *pp = md_find_property(mp, name, lenp);
	return pp ? pp->value : NULL;
}
EXPORT_SYMBOL(md_get_property);

struct mdesc_node *md_find_node_by_name(struct mdesc_node *from,
					const char *name)
{
	struct mdesc_node *mp;

	mp = from ? from->allnodes_next : allnodes;
	for (; mp != NULL; mp = mp->allnodes_next) {
		if (strcmp(mp->name, name) == 0)
			break;
	}
	return mp;
}
EXPORT_SYMBOL(md_find_node_by_name);

static unsigned int mdesc_early_allocated;

static void * __init mdesc_early_alloc(unsigned long size)
{
	void *ret;

	ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL);
	if (ret == NULL) {
		prom_printf("MDESC: alloc of %lu bytes failed.\n", size);
		prom_halt();
	}

	memset(ret, 0, size);

	mdesc_early_allocated += size;

	return ret;
}

static unsigned int __init count_arcs(struct mdesc_elem *ep)
{
	unsigned int ret = 0;

	ep++;
	while (ep->tag != MD_NODE_END) {
		if (ep->tag == MD_PROP_ARC)
			ret++;
		ep++;
	}
	return ret;
}

static void __init mdesc_node_alloc(u64 node, struct mdesc_elem *ep, const char *names)
{
	unsigned int num_arcs = count_arcs(ep);
	struct mdesc_node *mp;

	mp = mdesc_early_alloc(sizeof(*mp) +
			       (num_arcs * sizeof(struct mdesc_arc)));
	mp->name = names + ep->name_offset;
	mp->node = node;
	mp->unique_id = unique_id++;
	mp->num_arcs = num_arcs;

	hash_node(mp);
}

static inline struct mdesc_elem *node_block(struct mdesc_hdr *mdesc)
{
	return (struct mdesc_elem *) (mdesc + 1);
}

static inline void *name_block(struct mdesc_hdr *mdesc)
{
	return ((void *) node_block(mdesc)) + mdesc->node_sz;
}

static inline void *data_block(struct mdesc_hdr *mdesc)
{
	return ((void *) name_block(mdesc)) + mdesc->name_sz;
}

/* In order to avoid recursion (the graph can be very deep) we use a
 * two pass algorithm.  First we allocate all the nodes and hash them.
 * Then we iterate over each node, filling in the arcs and properties.
 */
static void __init build_all_nodes(struct mdesc_hdr *mdesc)
{
	struct mdesc_elem *start, *ep;
	struct mdesc_node *mp;
	const char *names;
	void *data;
	u64 last_node;

	start = ep = node_block(mdesc);
	last_node = mdesc->node_sz / 16;

	names = name_block(mdesc);

	while (1) {
		u64 node = ep - start;

		if (ep->tag == MD_LIST_END)
			break;

		if (ep->tag != MD_NODE) {
			prom_printf("MDESC: Inconsistent element list.\n");
			prom_halt();
		}

		mdesc_node_alloc(node, ep, names);

		if (ep->d.val >= last_node) {
			printk("MDESC: Warning, early break out of node scan.\n");
			printk("MDESC: Next node [%lu] last_node [%lu].\n",
			       node, last_node);
			break;
		}

		ep = start + ep->d.val;
	}

	data = data_block(mdesc);
	for (mp = allnodes; mp; mp = mp->allnodes_next) {
		struct mdesc_elem *ep = start + mp->node;
		struct property **link = &mp->properties;
		unsigned int this_arc = 0;

		ep++;
		while (ep->tag != MD_NODE_END) {
			switch (ep->tag) {
			case MD_PROP_ARC: {
				struct mdesc_node *target;

				if (this_arc >= mp->num_arcs) {
					prom_printf("MDESC: ARC overrun [%u:%u]\n",
						    this_arc, mp->num_arcs);
					prom_halt();
				}
				target = find_node(ep->d.val);
				if (!target) {
					printk("MDESC: Warning, arc points to "
					       "missing node, ignoring.\n");
					break;
				}
				mp->arcs[this_arc].name =
					(names + ep->name_offset);
				mp->arcs[this_arc].arc = target;
				this_arc++;
				break;
			}

			case MD_PROP_VAL:
			case MD_PROP_STR:
			case MD_PROP_DATA: {
				struct property *p = mdesc_early_alloc(sizeof(*p));

				p->unique_id = unique_id++;
				p->name = (char *) names + ep->name_offset;
				if (ep->tag == MD_PROP_VAL) {
					p->value = &ep->d.val;
					p->length = 8;
				} else {
					p->value = data + ep->d.data.data_offset;
					p->length = ep->d.data.data_len;
				}
				*link = p;
				link = &p->next;
				break;
			}

			case MD_NOOP:
				break;

			default:
				printk("MDESC: Warning, ignoring unknown tag type %02x\n",
				       ep->tag);
			}
			ep++;
		}
	}
}

static unsigned int __init count_nodes(struct mdesc_hdr *mdesc)
{
	struct mdesc_elem *ep = node_block(mdesc);
	struct mdesc_elem *end;
	unsigned int cnt = 0;

	end = ((void *)ep) + mdesc->node_sz;
	while (ep < end) {
		if (ep->tag == MD_NODE)
			cnt++;
		ep++;
	}
	return cnt;
}

static void __init report_platform_properties(void)
{
	struct mdesc_node *pn = md_find_node_by_name(NULL, "platform");
	const char *s;
	const u64 *v;

	if (!pn) {
		prom_printf("No platform node in machine-description.\n");
		prom_halt();
	}

	s = md_get_property(pn, "banner-name", NULL);
	printk("PLATFORM: banner-name [%s]\n", s);
	s = md_get_property(pn, "name", NULL);
	printk("PLATFORM: name [%s]\n", s);

	v = md_get_property(pn, "hostid", NULL);
	if (v)
		printk("PLATFORM: hostid [%08lx]\n", *v);
	v = md_get_property(pn, "serial#", NULL);
	if (v)
		printk("PLATFORM: serial# [%08lx]\n", *v);
	v = md_get_property(pn, "stick-frequency", NULL);
	printk("PLATFORM: stick-frequency [%08lx]\n", *v);
	v = md_get_property(pn, "mac-address", NULL);
	if (v)
		printk("PLATFORM: mac-address [%lx]\n", *v);
	v = md_get_property(pn, "watchdog-resolution", NULL);
	if (v)
		printk("PLATFORM: watchdog-resolution [%lu ms]\n", *v);
	v = md_get_property(pn, "watchdog-max-timeout", NULL);
	if (v)
		printk("PLATFORM: watchdog-max-timeout [%lu ms]\n", *v);
	v = md_get_property(pn, "max-cpus", NULL);
	if (v)
		printk("PLATFORM: max-cpus [%lu]\n", *v);
}

static int inline find_in_proplist(const char *list, const char *match, int len)
{
	while (len > 0) {
		int l;

		if (!strcmp(list, match))
			return 1;
		l = strlen(list) + 1;
		list += l;
		len -= l;
	}
	return 0;
}

static void __init fill_in_one_cache(cpuinfo_sparc *c, struct mdesc_node *mp)
{
	const u64 *level = md_get_property(mp, "level", NULL);
	const u64 *size = md_get_property(mp, "size", NULL);
	const u64 *line_size = md_get_property(mp, "line-size", NULL);
	const char *type;
	int type_len;

	type = md_get_property(mp, "type", &type_len);

	switch (*level) {
	case 1:
		if (find_in_proplist(type, "instn", type_len)) {
			c->icache_size = *size;
			c->icache_line_size = *line_size;
		} else if (find_in_proplist(type, "data", type_len)) {
			c->dcache_size = *size;
			c->dcache_line_size = *line_size;
		}
		break;

	case 2:
		c->ecache_size = *size;
		c->ecache_line_size = *line_size;
		break;

	default:
		break;
	}

	if (*level == 1) {
		unsigned int i;

		for (i = 0; i < mp->num_arcs; i++) {
			struct mdesc_node *t = mp->arcs[i].arc;

			if (strcmp(mp->arcs[i].name, "fwd"))
				continue;

			if (!strcmp(t->name, "cache"))
				fill_in_one_cache(c, t);
		}
	}
}

static void __init mark_core_ids(struct mdesc_node *mp, int core_id)
{
	unsigned int i;

	for (i = 0; i < mp->num_arcs; i++) {
		struct mdesc_node *t = mp->arcs[i].arc;
		const u64 *id;

		if (strcmp(mp->arcs[i].name, "back"))
			continue;

		if (!strcmp(t->name, "cpu")) {
			id = md_get_property(t, "id", NULL);
			if (*id < NR_CPUS)
				cpu_data(*id).core_id = core_id;
		} else {
			unsigned int j;

			for (j = 0; j < t->num_arcs; j++) {
				struct mdesc_node *n = t->arcs[j].arc;

				if (strcmp(t->arcs[j].name, "back"))
					continue;

				if (strcmp(n->name, "cpu"))
					continue;

				id = md_get_property(n, "id", NULL);
				if (*id < NR_CPUS)
					cpu_data(*id).core_id = core_id;
			}
		}
	}
}

static void __init set_core_ids(void)
{
	struct mdesc_node *mp;
	int idx;

	idx = 1;
	md_for_each_node_by_name(mp, "cache") {
		const u64 *level = md_get_property(mp, "level", NULL);
		const char *type;
		int len;

		if (*level != 1)
			continue;

		type = md_get_property(mp, "type", &len);
		if (!find_in_proplist(type, "instn", len))
			continue;

		mark_core_ids(mp, idx);

		idx++;
	}
}

static void __init get_one_mondo_bits(const u64 *p, unsigned int *mask, unsigned char def)
{
	u64 val;

	if (!p)
		goto use_default;
	val = *p;

	if (!val || val >= 64)
		goto use_default;

	*mask = ((1U << val) * 64U) - 1U;
	return;

use_default:
	*mask = ((1U << def) * 64U) - 1U;
}

static void __init get_mondo_data(struct mdesc_node *mp, struct trap_per_cpu *tb)
{
	const u64 *val;

	val = md_get_property(mp, "q-cpu-mondo-#bits", NULL);
	get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7);

	val = md_get_property(mp, "q-dev-mondo-#bits", NULL);
	get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7);

	val = md_get_property(mp, "q-resumable-#bits", NULL);
	get_one_mondo_bits(val, &tb->resum_qmask, 6);

	val = md_get_property(mp, "q-nonresumable-#bits", NULL);
	get_one_mondo_bits(val, &tb->nonresum_qmask, 2);
}

static void __init mdesc_fill_in_cpu_data(void)
{
	struct mdesc_node *mp;

	ncpus_probed = 0;
	md_for_each_node_by_name(mp, "cpu") {
		const u64 *id = md_get_property(mp, "id", NULL);
		const u64 *cfreq = md_get_property(mp, "clock-frequency", NULL);
		struct trap_per_cpu *tb;
		cpuinfo_sparc *c;
		unsigned int i;
		int cpuid;

		ncpus_probed++;

		cpuid = *id;

#ifdef CONFIG_SMP
		if (cpuid >= NR_CPUS)
			continue;
#else
		/* 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())
			continue;
		cpuid = 0;
#endif

		c = &cpu_data(cpuid);
		c->clock_tick = *cfreq;

		tb = &trap_block[cpuid];
		get_mondo_data(mp, tb);

		for (i = 0; i < mp->num_arcs; i++) {
			struct mdesc_node *t = mp->arcs[i].arc;
			unsigned int j;

			if (strcmp(mp->arcs[i].name, "fwd"))
				continue;

			if (!strcmp(t->name, "cache")) {
				fill_in_one_cache(c, t);
				continue;
			}

			for (j = 0; j < t->num_arcs; j++) {
				struct mdesc_node *n;

				n = t->arcs[j].arc;
				if (strcmp(t->arcs[j].name, "fwd"))
					continue;

				if (!strcmp(n->name, "cache"))
					fill_in_one_cache(c, n);
			}
		}

#ifdef CONFIG_SMP
		cpu_set(cpuid, cpu_present_map);
		cpu_set(cpuid, phys_cpu_present_map);
#endif

		c->core_id = 0;
	}

	set_core_ids();

	smp_fill_in_sib_core_maps();
}

void __init sun4v_mdesc_init(void)
{
	unsigned long len, real_len, status;

	(void) sun4v_mach_desc(0UL, 0UL, &len);

	printk("MDESC: Size is %lu bytes.\n", len);

	main_mdesc = mdesc_early_alloc(len);

	status = sun4v_mach_desc(__pa(main_mdesc), len, &real_len);
	if (status != HV_EOK || real_len > len) {
		prom_printf("sun4v_mach_desc fails, err(%lu), "
			    "len(%lu), real_len(%lu)\n",
			    status, len, real_len);
		prom_halt();
	}

	len = count_nodes(main_mdesc);
	printk("MDESC: %lu nodes.\n", len);

	len = roundup_pow_of_two(len);

	mdesc_hash = mdesc_early_alloc(len * sizeof(struct mdesc_node *));
	mdesc_hash_size = len;

	printk("MDESC: Hash size %lu entries.\n", len);

	build_all_nodes(main_mdesc);

	printk("MDESC: Built graph with %u bytes of memory.\n",
	       mdesc_early_allocated);

	report_platform_properties();
	mdesc_fill_in_cpu_data();
}
Loading