xref: /linux/arch/sparc/kernel/mdesc.c (revision fc7c028d)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2a88b5ba8SSam Ravnborg /* mdesc.c: Sun4V machine description handling.
3a88b5ba8SSam Ravnborg  *
4a88b5ba8SSam Ravnborg  * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net>
5a88b5ba8SSam Ravnborg  */
6a88b5ba8SSam Ravnborg #include <linux/kernel.h>
7a88b5ba8SSam Ravnborg #include <linux/types.h>
8a88b5ba8SSam Ravnborg #include <linux/log2.h>
9a88b5ba8SSam Ravnborg #include <linux/list.h>
10a88b5ba8SSam Ravnborg #include <linux/slab.h>
11a88b5ba8SSam Ravnborg #include <linux/mm.h>
12a88b5ba8SSam Ravnborg #include <linux/miscdevice.h>
1353ab85ebSMike Rapoport #include <linux/memblock.h>
147b64db60SPaul Gortmaker #include <linux/export.h>
15cdf5976fSElena Reshetova #include <linux/refcount.h>
16a88b5ba8SSam Ravnborg 
176e6ab2e2SSam Ravnborg #include <asm/cpudata.h>
18a88b5ba8SSam Ravnborg #include <asm/hypervisor.h>
19a88b5ba8SSam Ravnborg #include <asm/mdesc.h>
20a88b5ba8SSam Ravnborg #include <asm/prom.h>
217c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
22a88b5ba8SSam Ravnborg #include <asm/oplib.h>
23a88b5ba8SSam Ravnborg #include <asm/smp.h>
24c6202ca7SKhalid Aziz #include <asm/adi.h>
25a88b5ba8SSam Ravnborg 
26a88b5ba8SSam Ravnborg /* Unlike the OBP device tree, the machine description is a full-on
27a88b5ba8SSam Ravnborg  * DAG.  An arbitrary number of ARCs are possible from one
28a88b5ba8SSam Ravnborg  * node to other nodes and thus we can't use the OBP device_node
29a88b5ba8SSam Ravnborg  * data structure to represent these nodes inside of the kernel.
30a88b5ba8SSam Ravnborg  *
31a88b5ba8SSam Ravnborg  * Actually, it isn't even a DAG, because there are back pointers
32a88b5ba8SSam Ravnborg  * which create cycles in the graph.
33a88b5ba8SSam Ravnborg  *
34a88b5ba8SSam Ravnborg  * mdesc_hdr and mdesc_elem describe the layout of the data structure
35a88b5ba8SSam Ravnborg  * we get from the Hypervisor.
36a88b5ba8SSam Ravnborg  */
37a88b5ba8SSam Ravnborg struct mdesc_hdr {
38a88b5ba8SSam Ravnborg 	u32	version; /* Transport version */
39a88b5ba8SSam Ravnborg 	u32	node_sz; /* node block size */
40a88b5ba8SSam Ravnborg 	u32	name_sz; /* name block size */
41a88b5ba8SSam Ravnborg 	u32	data_sz; /* data block size */
42*fc7c028dSLinus Torvalds 	char	data[];
43a88b5ba8SSam Ravnborg } __attribute__((aligned(16)));
44a88b5ba8SSam Ravnborg 
45a88b5ba8SSam Ravnborg struct mdesc_elem {
46a88b5ba8SSam Ravnborg 	u8	tag;
47a88b5ba8SSam Ravnborg #define MD_LIST_END	0x00
48a88b5ba8SSam Ravnborg #define MD_NODE		0x4e
49a88b5ba8SSam Ravnborg #define MD_NODE_END	0x45
50a88b5ba8SSam Ravnborg #define MD_NOOP		0x20
51a88b5ba8SSam Ravnborg #define MD_PROP_ARC	0x61
52a88b5ba8SSam Ravnborg #define MD_PROP_VAL	0x76
53a88b5ba8SSam Ravnborg #define MD_PROP_STR	0x73
54a88b5ba8SSam Ravnborg #define MD_PROP_DATA	0x64
55a88b5ba8SSam Ravnborg 	u8	name_len;
56a88b5ba8SSam Ravnborg 	u16	resv;
57a88b5ba8SSam Ravnborg 	u32	name_offset;
58a88b5ba8SSam Ravnborg 	union {
59a88b5ba8SSam Ravnborg 		struct {
60a88b5ba8SSam Ravnborg 			u32	data_len;
61a88b5ba8SSam Ravnborg 			u32	data_offset;
62a88b5ba8SSam Ravnborg 		} data;
63a88b5ba8SSam Ravnborg 		u64	val;
64a88b5ba8SSam Ravnborg 	} d;
65a88b5ba8SSam Ravnborg };
66a88b5ba8SSam Ravnborg 
67a88b5ba8SSam Ravnborg struct mdesc_mem_ops {
68a88b5ba8SSam Ravnborg 	struct mdesc_handle *(*alloc)(unsigned int mdesc_size);
69a88b5ba8SSam Ravnborg 	void (*free)(struct mdesc_handle *handle);
70a88b5ba8SSam Ravnborg };
71a88b5ba8SSam Ravnborg 
72a88b5ba8SSam Ravnborg struct mdesc_handle {
73a88b5ba8SSam Ravnborg 	struct list_head	list;
74a88b5ba8SSam Ravnborg 	struct mdesc_mem_ops	*mops;
75a88b5ba8SSam Ravnborg 	void			*self_base;
76cdf5976fSElena Reshetova 	refcount_t		refcnt;
77a88b5ba8SSam Ravnborg 	unsigned int		handle_size;
78a88b5ba8SSam Ravnborg 	struct mdesc_hdr	mdesc;
79a88b5ba8SSam Ravnborg };
80a88b5ba8SSam Ravnborg 
81411cb4a0SJag Raman typedef int (*mdesc_node_info_get_f)(struct mdesc_handle *, u64,
82411cb4a0SJag Raman 				     union md_node_info *);
83411cb4a0SJag Raman typedef void (*mdesc_node_info_rel_f)(union md_node_info *);
84411cb4a0SJag Raman typedef bool (*mdesc_node_match_f)(union md_node_info *, union md_node_info *);
85411cb4a0SJag Raman 
86411cb4a0SJag Raman struct md_node_ops {
87411cb4a0SJag Raman 	char			*name;
88411cb4a0SJag Raman 	mdesc_node_info_get_f	get_info;
89411cb4a0SJag Raman 	mdesc_node_info_rel_f	rel_info;
90411cb4a0SJag Raman 	mdesc_node_match_f	node_match;
91411cb4a0SJag Raman };
92411cb4a0SJag Raman 
93411cb4a0SJag Raman static int get_vdev_port_node_info(struct mdesc_handle *md, u64 node,
94411cb4a0SJag Raman 				   union md_node_info *node_info);
95411cb4a0SJag Raman static void rel_vdev_port_node_info(union md_node_info *node_info);
96411cb4a0SJag Raman static bool vdev_port_node_match(union md_node_info *a_node_info,
97411cb4a0SJag Raman 				 union md_node_info *b_node_info);
98411cb4a0SJag Raman 
99411cb4a0SJag Raman static int get_ds_port_node_info(struct mdesc_handle *md, u64 node,
100411cb4a0SJag Raman 				 union md_node_info *node_info);
101411cb4a0SJag Raman static void rel_ds_port_node_info(union md_node_info *node_info);
102411cb4a0SJag Raman static bool ds_port_node_match(union md_node_info *a_node_info,
103411cb4a0SJag Raman 			       union md_node_info *b_node_info);
104411cb4a0SJag Raman 
105411cb4a0SJag Raman /* supported node types which can be registered */
106411cb4a0SJag Raman static struct md_node_ops md_node_ops_table[] = {
107411cb4a0SJag Raman 	{"virtual-device-port", get_vdev_port_node_info,
108411cb4a0SJag Raman 	 rel_vdev_port_node_info, vdev_port_node_match},
109411cb4a0SJag Raman 	{"domain-services-port", get_ds_port_node_info,
110411cb4a0SJag Raman 	 rel_ds_port_node_info, ds_port_node_match},
111411cb4a0SJag Raman 	{NULL, NULL, NULL, NULL}
112411cb4a0SJag Raman };
113411cb4a0SJag Raman 
mdesc_get_node_ops(const char * node_name,mdesc_node_info_get_f * get_info_f,mdesc_node_info_rel_f * rel_info_f,mdesc_node_match_f * match_f)114411cb4a0SJag Raman static void mdesc_get_node_ops(const char *node_name,
115411cb4a0SJag Raman 			       mdesc_node_info_get_f *get_info_f,
116411cb4a0SJag Raman 			       mdesc_node_info_rel_f *rel_info_f,
117411cb4a0SJag Raman 			       mdesc_node_match_f *match_f)
118411cb4a0SJag Raman {
119411cb4a0SJag Raman 	int i;
120411cb4a0SJag Raman 
121411cb4a0SJag Raman 	if (get_info_f)
122411cb4a0SJag Raman 		*get_info_f = NULL;
123411cb4a0SJag Raman 
124411cb4a0SJag Raman 	if (rel_info_f)
125411cb4a0SJag Raman 		*rel_info_f = NULL;
126411cb4a0SJag Raman 
127411cb4a0SJag Raman 	if (match_f)
128411cb4a0SJag Raman 		*match_f = NULL;
129411cb4a0SJag Raman 
130411cb4a0SJag Raman 	if (!node_name)
131411cb4a0SJag Raman 		return;
132411cb4a0SJag Raman 
133411cb4a0SJag Raman 	for (i = 0; md_node_ops_table[i].name != NULL; i++) {
134411cb4a0SJag Raman 		if (strcmp(md_node_ops_table[i].name, node_name) == 0) {
135411cb4a0SJag Raman 			if (get_info_f)
136411cb4a0SJag Raman 				*get_info_f = md_node_ops_table[i].get_info;
137411cb4a0SJag Raman 
138411cb4a0SJag Raman 			if (rel_info_f)
139411cb4a0SJag Raman 				*rel_info_f = md_node_ops_table[i].rel_info;
140411cb4a0SJag Raman 
141411cb4a0SJag Raman 			if (match_f)
142411cb4a0SJag Raman 				*match_f = md_node_ops_table[i].node_match;
143411cb4a0SJag Raman 
144411cb4a0SJag Raman 			break;
145411cb4a0SJag Raman 		}
146411cb4a0SJag Raman 	}
147411cb4a0SJag Raman }
148411cb4a0SJag Raman 
mdesc_handle_init(struct mdesc_handle * hp,unsigned int handle_size,void * base)149a88b5ba8SSam Ravnborg static void mdesc_handle_init(struct mdesc_handle *hp,
150a88b5ba8SSam Ravnborg 			      unsigned int handle_size,
151a88b5ba8SSam Ravnborg 			      void *base)
152a88b5ba8SSam Ravnborg {
153a88b5ba8SSam Ravnborg 	BUG_ON(((unsigned long)&hp->mdesc) & (16UL - 1));
154a88b5ba8SSam Ravnborg 
155a88b5ba8SSam Ravnborg 	memset(hp, 0, handle_size);
156a88b5ba8SSam Ravnborg 	INIT_LIST_HEAD(&hp->list);
157a88b5ba8SSam Ravnborg 	hp->self_base = base;
158cdf5976fSElena Reshetova 	refcount_set(&hp->refcnt, 1);
159a88b5ba8SSam Ravnborg 	hp->handle_size = handle_size;
160a88b5ba8SSam Ravnborg }
161a88b5ba8SSam Ravnborg 
mdesc_memblock_alloc(unsigned int mdesc_size)16295f72d1eSYinghai Lu static struct mdesc_handle * __init mdesc_memblock_alloc(unsigned int mdesc_size)
163a88b5ba8SSam Ravnborg {
164a88b5ba8SSam Ravnborg 	unsigned int handle_size, alloc_size;
165a88b5ba8SSam Ravnborg 	struct mdesc_handle *hp;
166a88b5ba8SSam Ravnborg 	unsigned long paddr;
167a88b5ba8SSam Ravnborg 
168a88b5ba8SSam Ravnborg 	handle_size = (sizeof(struct mdesc_handle) -
169a88b5ba8SSam Ravnborg 		       sizeof(struct mdesc_hdr) +
170a88b5ba8SSam Ravnborg 		       mdesc_size);
171a88b5ba8SSam Ravnborg 	alloc_size = PAGE_ALIGN(handle_size);
172a88b5ba8SSam Ravnborg 
1739a8dd708SMike Rapoport 	paddr = memblock_phys_alloc(alloc_size, PAGE_SIZE);
174a88b5ba8SSam Ravnborg 
175a88b5ba8SSam Ravnborg 	hp = NULL;
176a88b5ba8SSam Ravnborg 	if (paddr) {
177a88b5ba8SSam Ravnborg 		hp = __va(paddr);
178a88b5ba8SSam Ravnborg 		mdesc_handle_init(hp, handle_size, hp);
179a88b5ba8SSam Ravnborg 	}
180a88b5ba8SSam Ravnborg 	return hp;
181a88b5ba8SSam Ravnborg }
182a88b5ba8SSam Ravnborg 
mdesc_memblock_free(struct mdesc_handle * hp)1833628aa06SDavid S. Miller static void __init mdesc_memblock_free(struct mdesc_handle *hp)
184a88b5ba8SSam Ravnborg {
185adfe67ddSDavid S. Miller 	unsigned int alloc_size;
186adfe67ddSDavid S. Miller 	unsigned long start;
187a88b5ba8SSam Ravnborg 
188cdf5976fSElena Reshetova 	BUG_ON(refcount_read(&hp->refcnt) != 0);
189a88b5ba8SSam Ravnborg 	BUG_ON(!list_empty(&hp->list));
190a88b5ba8SSam Ravnborg 
191adfe67ddSDavid S. Miller 	alloc_size = PAGE_ALIGN(hp->handle_size);
192adfe67ddSDavid S. Miller 	start = __pa(hp);
19353ab85ebSMike Rapoport 	memblock_free_late(start, alloc_size);
194a88b5ba8SSam Ravnborg }
195a88b5ba8SSam Ravnborg 
19695f72d1eSYinghai Lu static struct mdesc_mem_ops memblock_mdesc_ops = {
19795f72d1eSYinghai Lu 	.alloc = mdesc_memblock_alloc,
19895f72d1eSYinghai Lu 	.free  = mdesc_memblock_free,
199a88b5ba8SSam Ravnborg };
200a88b5ba8SSam Ravnborg 
mdesc_kmalloc(unsigned int mdesc_size)201a88b5ba8SSam Ravnborg static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size)
202a88b5ba8SSam Ravnborg {
203a88b5ba8SSam Ravnborg 	unsigned int handle_size;
204f91e8d6dSMichal Hocko 	struct mdesc_handle *hp;
205f91e8d6dSMichal Hocko 	unsigned long addr;
206a88b5ba8SSam Ravnborg 	void *base;
207a88b5ba8SSam Ravnborg 
208a88b5ba8SSam Ravnborg 	handle_size = (sizeof(struct mdesc_handle) -
209a88b5ba8SSam Ravnborg 		       sizeof(struct mdesc_hdr) +
210a88b5ba8SSam Ravnborg 		       mdesc_size);
211dcda9b04SMichal Hocko 	base = kmalloc(handle_size + 15, GFP_KERNEL | __GFP_RETRY_MAYFAIL);
2120ab2fcd6SJag Raman 	if (!base)
2130ab2fcd6SJag Raman 		return NULL;
214a88b5ba8SSam Ravnborg 
215a88b5ba8SSam Ravnborg 	addr = (unsigned long)base;
216a88b5ba8SSam Ravnborg 	addr = (addr + 15UL) & ~15UL;
217a88b5ba8SSam Ravnborg 	hp = (struct mdesc_handle *) addr;
218a88b5ba8SSam Ravnborg 
219a88b5ba8SSam Ravnborg 	mdesc_handle_init(hp, handle_size, base);
220a88b5ba8SSam Ravnborg 
221f91e8d6dSMichal Hocko 	return hp;
222a88b5ba8SSam Ravnborg }
223a88b5ba8SSam Ravnborg 
mdesc_kfree(struct mdesc_handle * hp)224a88b5ba8SSam Ravnborg static void mdesc_kfree(struct mdesc_handle *hp)
225a88b5ba8SSam Ravnborg {
226cdf5976fSElena Reshetova 	BUG_ON(refcount_read(&hp->refcnt) != 0);
227a88b5ba8SSam Ravnborg 	BUG_ON(!list_empty(&hp->list));
228a88b5ba8SSam Ravnborg 
229a88b5ba8SSam Ravnborg 	kfree(hp->self_base);
230a88b5ba8SSam Ravnborg }
231a88b5ba8SSam Ravnborg 
232a88b5ba8SSam Ravnborg static struct mdesc_mem_ops kmalloc_mdesc_memops = {
233a88b5ba8SSam Ravnborg 	.alloc = mdesc_kmalloc,
234a88b5ba8SSam Ravnborg 	.free  = mdesc_kfree,
235a88b5ba8SSam Ravnborg };
236a88b5ba8SSam Ravnborg 
mdesc_alloc(unsigned int mdesc_size,struct mdesc_mem_ops * mops)237a88b5ba8SSam Ravnborg static struct mdesc_handle *mdesc_alloc(unsigned int mdesc_size,
238a88b5ba8SSam Ravnborg 					struct mdesc_mem_ops *mops)
239a88b5ba8SSam Ravnborg {
240a88b5ba8SSam Ravnborg 	struct mdesc_handle *hp = mops->alloc(mdesc_size);
241a88b5ba8SSam Ravnborg 
242a88b5ba8SSam Ravnborg 	if (hp)
243a88b5ba8SSam Ravnborg 		hp->mops = mops;
244a88b5ba8SSam Ravnborg 
245a88b5ba8SSam Ravnborg 	return hp;
246a88b5ba8SSam Ravnborg }
247a88b5ba8SSam Ravnborg 
mdesc_free(struct mdesc_handle * hp)248a88b5ba8SSam Ravnborg static void mdesc_free(struct mdesc_handle *hp)
249a88b5ba8SSam Ravnborg {
250a88b5ba8SSam Ravnborg 	hp->mops->free(hp);
251a88b5ba8SSam Ravnborg }
252a88b5ba8SSam Ravnborg 
253a88b5ba8SSam Ravnborg static struct mdesc_handle *cur_mdesc;
254a88b5ba8SSam Ravnborg static LIST_HEAD(mdesc_zombie_list);
255a88b5ba8SSam Ravnborg static DEFINE_SPINLOCK(mdesc_lock);
256a88b5ba8SSam Ravnborg 
mdesc_grab(void)257a88b5ba8SSam Ravnborg struct mdesc_handle *mdesc_grab(void)
258a88b5ba8SSam Ravnborg {
259a88b5ba8SSam Ravnborg 	struct mdesc_handle *hp;
260a88b5ba8SSam Ravnborg 	unsigned long flags;
261a88b5ba8SSam Ravnborg 
262a88b5ba8SSam Ravnborg 	spin_lock_irqsave(&mdesc_lock, flags);
263a88b5ba8SSam Ravnborg 	hp = cur_mdesc;
264a88b5ba8SSam Ravnborg 	if (hp)
265cdf5976fSElena Reshetova 		refcount_inc(&hp->refcnt);
266a88b5ba8SSam Ravnborg 	spin_unlock_irqrestore(&mdesc_lock, flags);
267a88b5ba8SSam Ravnborg 
268a88b5ba8SSam Ravnborg 	return hp;
269a88b5ba8SSam Ravnborg }
270a88b5ba8SSam Ravnborg EXPORT_SYMBOL(mdesc_grab);
271a88b5ba8SSam Ravnborg 
mdesc_release(struct mdesc_handle * hp)272a88b5ba8SSam Ravnborg void mdesc_release(struct mdesc_handle *hp)
273a88b5ba8SSam Ravnborg {
274a88b5ba8SSam Ravnborg 	unsigned long flags;
275a88b5ba8SSam Ravnborg 
276a88b5ba8SSam Ravnborg 	spin_lock_irqsave(&mdesc_lock, flags);
277cdf5976fSElena Reshetova 	if (refcount_dec_and_test(&hp->refcnt)) {
278a88b5ba8SSam Ravnborg 		list_del_init(&hp->list);
279a88b5ba8SSam Ravnborg 		hp->mops->free(hp);
280a88b5ba8SSam Ravnborg 	}
281a88b5ba8SSam Ravnborg 	spin_unlock_irqrestore(&mdesc_lock, flags);
282a88b5ba8SSam Ravnborg }
283a88b5ba8SSam Ravnborg EXPORT_SYMBOL(mdesc_release);
284a88b5ba8SSam Ravnborg 
285a88b5ba8SSam Ravnborg static DEFINE_MUTEX(mdesc_mutex);
286a88b5ba8SSam Ravnborg static struct mdesc_notifier_client *client_list;
287a88b5ba8SSam Ravnborg 
mdesc_register_notifier(struct mdesc_notifier_client * client)288a88b5ba8SSam Ravnborg void mdesc_register_notifier(struct mdesc_notifier_client *client)
289a88b5ba8SSam Ravnborg {
290110f2264SJag Raman 	bool supported = false;
291a88b5ba8SSam Ravnborg 	u64 node;
292110f2264SJag Raman 	int i;
293a88b5ba8SSam Ravnborg 
294a88b5ba8SSam Ravnborg 	mutex_lock(&mdesc_mutex);
295110f2264SJag Raman 
296110f2264SJag Raman 	/* check to see if the node is supported for registration */
297110f2264SJag Raman 	for (i = 0; md_node_ops_table[i].name != NULL; i++) {
298110f2264SJag Raman 		if (strcmp(md_node_ops_table[i].name, client->node_name) == 0) {
299110f2264SJag Raman 			supported = true;
300110f2264SJag Raman 			break;
301110f2264SJag Raman 		}
302110f2264SJag Raman 	}
303110f2264SJag Raman 
304110f2264SJag Raman 	if (!supported) {
305110f2264SJag Raman 		pr_err("MD: %s node not supported\n", client->node_name);
306110f2264SJag Raman 		mutex_unlock(&mdesc_mutex);
307110f2264SJag Raman 		return;
308110f2264SJag Raman 	}
309110f2264SJag Raman 
310a88b5ba8SSam Ravnborg 	client->next = client_list;
311a88b5ba8SSam Ravnborg 	client_list = client;
312a88b5ba8SSam Ravnborg 
313a88b5ba8SSam Ravnborg 	mdesc_for_each_node_by_name(cur_mdesc, node, client->node_name)
31406f3c3acSJag Raman 		client->add(cur_mdesc, node, client->node_name);
315a88b5ba8SSam Ravnborg 
316a88b5ba8SSam Ravnborg 	mutex_unlock(&mdesc_mutex);
317a88b5ba8SSam Ravnborg }
318a88b5ba8SSam Ravnborg 
parent_cfg_handle(struct mdesc_handle * hp,u64 node)319a88b5ba8SSam Ravnborg static const u64 *parent_cfg_handle(struct mdesc_handle *hp, u64 node)
320a88b5ba8SSam Ravnborg {
321a88b5ba8SSam Ravnborg 	const u64 *id;
322a88b5ba8SSam Ravnborg 	u64 a;
323a88b5ba8SSam Ravnborg 
324a88b5ba8SSam Ravnborg 	id = NULL;
325a88b5ba8SSam Ravnborg 	mdesc_for_each_arc(a, hp, node, MDESC_ARC_TYPE_BACK) {
326a88b5ba8SSam Ravnborg 		u64 target;
327a88b5ba8SSam Ravnborg 
328a88b5ba8SSam Ravnborg 		target = mdesc_arc_target(hp, a);
329a88b5ba8SSam Ravnborg 		id = mdesc_get_property(hp, target,
330a88b5ba8SSam Ravnborg 					"cfg-handle", NULL);
331a88b5ba8SSam Ravnborg 		if (id)
332a88b5ba8SSam Ravnborg 			break;
333a88b5ba8SSam Ravnborg 	}
334a88b5ba8SSam Ravnborg 
335a88b5ba8SSam Ravnborg 	return id;
336a88b5ba8SSam Ravnborg }
337a88b5ba8SSam Ravnborg 
get_vdev_port_node_info(struct mdesc_handle * md,u64 node,union md_node_info * node_info)338411cb4a0SJag Raman static int get_vdev_port_node_info(struct mdesc_handle *md, u64 node,
339411cb4a0SJag Raman 				   union md_node_info *node_info)
340411cb4a0SJag Raman {
341411cb4a0SJag Raman 	const u64 *parent_cfg_hdlp;
342411cb4a0SJag Raman 	const char *name;
343411cb4a0SJag Raman 	const u64 *idp;
344411cb4a0SJag Raman 
345411cb4a0SJag Raman 	/*
346411cb4a0SJag Raman 	 * Virtual device nodes are distinguished by:
347411cb4a0SJag Raman 	 * 1. "id" property
348411cb4a0SJag Raman 	 * 2. "name" property
349411cb4a0SJag Raman 	 * 3. parent node "cfg-handle" property
350411cb4a0SJag Raman 	 */
351411cb4a0SJag Raman 	idp = mdesc_get_property(md, node, "id", NULL);
352411cb4a0SJag Raman 	name = mdesc_get_property(md, node, "name", NULL);
353411cb4a0SJag Raman 	parent_cfg_hdlp = parent_cfg_handle(md, node);
354411cb4a0SJag Raman 
355411cb4a0SJag Raman 	if (!idp || !name || !parent_cfg_hdlp)
356411cb4a0SJag Raman 		return -1;
357411cb4a0SJag Raman 
358411cb4a0SJag Raman 	node_info->vdev_port.id = *idp;
359411cb4a0SJag Raman 	node_info->vdev_port.name = kstrdup_const(name, GFP_KERNEL);
36080caf435SGen Zhang 	if (!node_info->vdev_port.name)
36180caf435SGen Zhang 		return -1;
362411cb4a0SJag Raman 	node_info->vdev_port.parent_cfg_hdl = *parent_cfg_hdlp;
363411cb4a0SJag Raman 
364411cb4a0SJag Raman 	return 0;
365411cb4a0SJag Raman }
366411cb4a0SJag Raman 
rel_vdev_port_node_info(union md_node_info * node_info)367411cb4a0SJag Raman static void rel_vdev_port_node_info(union md_node_info *node_info)
368411cb4a0SJag Raman {
369411cb4a0SJag Raman 	if (node_info && node_info->vdev_port.name) {
370411cb4a0SJag Raman 		kfree_const(node_info->vdev_port.name);
371411cb4a0SJag Raman 		node_info->vdev_port.name = NULL;
372411cb4a0SJag Raman 	}
373411cb4a0SJag Raman }
374411cb4a0SJag Raman 
vdev_port_node_match(union md_node_info * a_node_info,union md_node_info * b_node_info)375411cb4a0SJag Raman static bool vdev_port_node_match(union md_node_info *a_node_info,
376411cb4a0SJag Raman 				 union md_node_info *b_node_info)
377411cb4a0SJag Raman {
378411cb4a0SJag Raman 	if (a_node_info->vdev_port.id != b_node_info->vdev_port.id)
379411cb4a0SJag Raman 		return false;
380411cb4a0SJag Raman 
381411cb4a0SJag Raman 	if (a_node_info->vdev_port.parent_cfg_hdl !=
382411cb4a0SJag Raman 	    b_node_info->vdev_port.parent_cfg_hdl)
383411cb4a0SJag Raman 		return false;
384411cb4a0SJag Raman 
385411cb4a0SJag Raman 	if (strncmp(a_node_info->vdev_port.name,
386411cb4a0SJag Raman 		    b_node_info->vdev_port.name, MDESC_MAX_STR_LEN) != 0)
387411cb4a0SJag Raman 		return false;
388411cb4a0SJag Raman 
389411cb4a0SJag Raman 	return true;
390411cb4a0SJag Raman }
391411cb4a0SJag Raman 
get_ds_port_node_info(struct mdesc_handle * md,u64 node,union md_node_info * node_info)392411cb4a0SJag Raman static int get_ds_port_node_info(struct mdesc_handle *md, u64 node,
393411cb4a0SJag Raman 				 union md_node_info *node_info)
394411cb4a0SJag Raman {
395411cb4a0SJag Raman 	const u64 *idp;
396411cb4a0SJag Raman 
397411cb4a0SJag Raman 	/* DS port nodes use the "id" property to distinguish them */
398411cb4a0SJag Raman 	idp = mdesc_get_property(md, node, "id", NULL);
399411cb4a0SJag Raman 	if (!idp)
400411cb4a0SJag Raman 		return -1;
401411cb4a0SJag Raman 
402411cb4a0SJag Raman 	node_info->ds_port.id = *idp;
403411cb4a0SJag Raman 
404411cb4a0SJag Raman 	return 0;
405411cb4a0SJag Raman }
406411cb4a0SJag Raman 
rel_ds_port_node_info(union md_node_info * node_info)407411cb4a0SJag Raman static void rel_ds_port_node_info(union md_node_info *node_info)
408411cb4a0SJag Raman {
409411cb4a0SJag Raman }
410411cb4a0SJag Raman 
ds_port_node_match(union md_node_info * a_node_info,union md_node_info * b_node_info)411411cb4a0SJag Raman static bool ds_port_node_match(union md_node_info *a_node_info,
412411cb4a0SJag Raman 			       union md_node_info *b_node_info)
413411cb4a0SJag Raman {
414411cb4a0SJag Raman 	if (a_node_info->ds_port.id != b_node_info->ds_port.id)
415411cb4a0SJag Raman 		return false;
416411cb4a0SJag Raman 
417411cb4a0SJag Raman 	return true;
418411cb4a0SJag Raman }
419411cb4a0SJag Raman 
420a88b5ba8SSam Ravnborg /* Run 'func' on nodes which are in A but not in B.  */
invoke_on_missing(const char * name,struct mdesc_handle * a,struct mdesc_handle * b,void (* func)(struct mdesc_handle *,u64,const char * node_name))421a88b5ba8SSam Ravnborg static void invoke_on_missing(const char *name,
422a88b5ba8SSam Ravnborg 			      struct mdesc_handle *a,
423a88b5ba8SSam Ravnborg 			      struct mdesc_handle *b,
42406f3c3acSJag Raman 			      void (*func)(struct mdesc_handle *, u64,
42506f3c3acSJag Raman 					   const char *node_name))
426a88b5ba8SSam Ravnborg {
42706f3c3acSJag Raman 	mdesc_node_info_get_f get_info_func;
42806f3c3acSJag Raman 	mdesc_node_info_rel_f rel_info_func;
42906f3c3acSJag Raman 	mdesc_node_match_f node_match_func;
43006f3c3acSJag Raman 	union md_node_info a_node_info;
43106f3c3acSJag Raman 	union md_node_info b_node_info;
43206f3c3acSJag Raman 	bool found;
43306f3c3acSJag Raman 	u64 a_node;
43406f3c3acSJag Raman 	u64 b_node;
43506f3c3acSJag Raman 	int rv;
436a88b5ba8SSam Ravnborg 
43706f3c3acSJag Raman 	/*
43806f3c3acSJag Raman 	 * Find the get_info, rel_info and node_match ops for the given
43906f3c3acSJag Raman 	 * node name
44006f3c3acSJag Raman 	 */
44106f3c3acSJag Raman 	mdesc_get_node_ops(name, &get_info_func, &rel_info_func,
44206f3c3acSJag Raman 			   &node_match_func);
443a88b5ba8SSam Ravnborg 
44406f3c3acSJag Raman 	/* If we didn't find a match, the node type is not supported */
44506f3c3acSJag Raman 	if (!get_info_func || !rel_info_func || !node_match_func) {
44606f3c3acSJag Raman 		pr_err("MD: %s node type is not supported\n", name);
44706f3c3acSJag Raman 		return;
44806f3c3acSJag Raman 	}
449a88b5ba8SSam Ravnborg 
45006f3c3acSJag Raman 	mdesc_for_each_node_by_name(a, a_node, name) {
45106f3c3acSJag Raman 		found = false;
45206f3c3acSJag Raman 
45306f3c3acSJag Raman 		rv = get_info_func(a, a_node, &a_node_info);
45406f3c3acSJag Raman 		if (rv != 0) {
45506f3c3acSJag Raman 			pr_err("MD: Cannot find 1 or more required match properties for %s node.\n",
45606f3c3acSJag Raman 			       name);
457a88b5ba8SSam Ravnborg 			continue;
458a88b5ba8SSam Ravnborg 		}
459a88b5ba8SSam Ravnborg 
46006f3c3acSJag Raman 		/* Check each node in B for node matching a_node */
46106f3c3acSJag Raman 		mdesc_for_each_node_by_name(b, b_node, name) {
46206f3c3acSJag Raman 			rv = get_info_func(b, b_node, &b_node_info);
46306f3c3acSJag Raman 			if (rv != 0)
464a88b5ba8SSam Ravnborg 				continue;
465a88b5ba8SSam Ravnborg 
46606f3c3acSJag Raman 			if (node_match_func(&a_node_info, &b_node_info)) {
46706f3c3acSJag Raman 				found = true;
46806f3c3acSJag Raman 				rel_info_func(&b_node_info);
469a88b5ba8SSam Ravnborg 				break;
470a88b5ba8SSam Ravnborg 			}
47106f3c3acSJag Raman 
47206f3c3acSJag Raman 			rel_info_func(&b_node_info);
473a88b5ba8SSam Ravnborg 		}
47406f3c3acSJag Raman 
47506f3c3acSJag Raman 		rel_info_func(&a_node_info);
47606f3c3acSJag Raman 
477a88b5ba8SSam Ravnborg 		if (!found)
47806f3c3acSJag Raman 			func(a, a_node, name);
479a88b5ba8SSam Ravnborg 	}
480a88b5ba8SSam Ravnborg }
481a88b5ba8SSam Ravnborg 
notify_one(struct mdesc_notifier_client * p,struct mdesc_handle * old_hp,struct mdesc_handle * new_hp)482a88b5ba8SSam Ravnborg static void notify_one(struct mdesc_notifier_client *p,
483a88b5ba8SSam Ravnborg 		       struct mdesc_handle *old_hp,
484a88b5ba8SSam Ravnborg 		       struct mdesc_handle *new_hp)
485a88b5ba8SSam Ravnborg {
486a88b5ba8SSam Ravnborg 	invoke_on_missing(p->node_name, old_hp, new_hp, p->remove);
487a88b5ba8SSam Ravnborg 	invoke_on_missing(p->node_name, new_hp, old_hp, p->add);
488a88b5ba8SSam Ravnborg }
489a88b5ba8SSam Ravnborg 
mdesc_notify_clients(struct mdesc_handle * old_hp,struct mdesc_handle * new_hp)490a88b5ba8SSam Ravnborg static void mdesc_notify_clients(struct mdesc_handle *old_hp,
491a88b5ba8SSam Ravnborg 				 struct mdesc_handle *new_hp)
492a88b5ba8SSam Ravnborg {
493a88b5ba8SSam Ravnborg 	struct mdesc_notifier_client *p = client_list;
494a88b5ba8SSam Ravnborg 
495a88b5ba8SSam Ravnborg 	while (p) {
496a88b5ba8SSam Ravnborg 		notify_one(p, old_hp, new_hp);
497a88b5ba8SSam Ravnborg 		p = p->next;
498a88b5ba8SSam Ravnborg 	}
499a88b5ba8SSam Ravnborg }
500a88b5ba8SSam Ravnborg 
mdesc_update(void)501a88b5ba8SSam Ravnborg void mdesc_update(void)
502a88b5ba8SSam Ravnborg {
503a88b5ba8SSam Ravnborg 	unsigned long len, real_len, status;
504a88b5ba8SSam Ravnborg 	struct mdesc_handle *hp, *orig_hp;
505a88b5ba8SSam Ravnborg 	unsigned long flags;
506a88b5ba8SSam Ravnborg 
507a88b5ba8SSam Ravnborg 	mutex_lock(&mdesc_mutex);
508a88b5ba8SSam Ravnborg 
509a88b5ba8SSam Ravnborg 	(void) sun4v_mach_desc(0UL, 0UL, &len);
510a88b5ba8SSam Ravnborg 
511a88b5ba8SSam Ravnborg 	hp = mdesc_alloc(len, &kmalloc_mdesc_memops);
512a88b5ba8SSam Ravnborg 	if (!hp) {
513a88b5ba8SSam Ravnborg 		printk(KERN_ERR "MD: mdesc alloc fails\n");
514a88b5ba8SSam Ravnborg 		goto out;
515a88b5ba8SSam Ravnborg 	}
516a88b5ba8SSam Ravnborg 
517a88b5ba8SSam Ravnborg 	status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len);
518a88b5ba8SSam Ravnborg 	if (status != HV_EOK || real_len > len) {
519a88b5ba8SSam Ravnborg 		printk(KERN_ERR "MD: mdesc reread fails with %lu\n",
520a88b5ba8SSam Ravnborg 		       status);
521cdf5976fSElena Reshetova 		refcount_dec(&hp->refcnt);
522a88b5ba8SSam Ravnborg 		mdesc_free(hp);
523a88b5ba8SSam Ravnborg 		goto out;
524a88b5ba8SSam Ravnborg 	}
525a88b5ba8SSam Ravnborg 
526a88b5ba8SSam Ravnborg 	spin_lock_irqsave(&mdesc_lock, flags);
527a88b5ba8SSam Ravnborg 	orig_hp = cur_mdesc;
528a88b5ba8SSam Ravnborg 	cur_mdesc = hp;
529a88b5ba8SSam Ravnborg 	spin_unlock_irqrestore(&mdesc_lock, flags);
530a88b5ba8SSam Ravnborg 
531a88b5ba8SSam Ravnborg 	mdesc_notify_clients(orig_hp, hp);
532a88b5ba8SSam Ravnborg 
533a88b5ba8SSam Ravnborg 	spin_lock_irqsave(&mdesc_lock, flags);
534cdf5976fSElena Reshetova 	if (refcount_dec_and_test(&orig_hp->refcnt))
535a88b5ba8SSam Ravnborg 		mdesc_free(orig_hp);
536a88b5ba8SSam Ravnborg 	else
537a88b5ba8SSam Ravnborg 		list_add(&orig_hp->list, &mdesc_zombie_list);
538a88b5ba8SSam Ravnborg 	spin_unlock_irqrestore(&mdesc_lock, flags);
539a88b5ba8SSam Ravnborg 
540a88b5ba8SSam Ravnborg out:
541a88b5ba8SSam Ravnborg 	mutex_unlock(&mdesc_mutex);
542a88b5ba8SSam Ravnborg }
543a88b5ba8SSam Ravnborg 
mdesc_get_node(struct mdesc_handle * hp,const char * node_name,union md_node_info * node_info)544411cb4a0SJag Raman u64 mdesc_get_node(struct mdesc_handle *hp, const char *node_name,
545411cb4a0SJag Raman 		   union md_node_info *node_info)
546411cb4a0SJag Raman {
547411cb4a0SJag Raman 	mdesc_node_info_get_f get_info_func;
548411cb4a0SJag Raman 	mdesc_node_info_rel_f rel_info_func;
549411cb4a0SJag Raman 	mdesc_node_match_f node_match_func;
550411cb4a0SJag Raman 	union md_node_info hp_node_info;
551411cb4a0SJag Raman 	u64 hp_node;
552411cb4a0SJag Raman 	int rv;
553411cb4a0SJag Raman 
554411cb4a0SJag Raman 	if (hp == NULL || node_name == NULL || node_info == NULL)
555411cb4a0SJag Raman 		return MDESC_NODE_NULL;
556411cb4a0SJag Raman 
557411cb4a0SJag Raman 	/* Find the ops for the given node name */
558411cb4a0SJag Raman 	mdesc_get_node_ops(node_name, &get_info_func, &rel_info_func,
559411cb4a0SJag Raman 			   &node_match_func);
560411cb4a0SJag Raman 
561411cb4a0SJag Raman 	/* If we didn't find ops for the given node name, it is not supported */
562411cb4a0SJag Raman 	if (!get_info_func || !rel_info_func || !node_match_func) {
563411cb4a0SJag Raman 		pr_err("MD: %s node is not supported\n", node_name);
564411cb4a0SJag Raman 		return -EINVAL;
565411cb4a0SJag Raman 	}
566411cb4a0SJag Raman 
567411cb4a0SJag Raman 	mdesc_for_each_node_by_name(hp, hp_node, node_name) {
568411cb4a0SJag Raman 		rv = get_info_func(hp, hp_node, &hp_node_info);
569411cb4a0SJag Raman 		if (rv != 0)
570411cb4a0SJag Raman 			continue;
571411cb4a0SJag Raman 
572411cb4a0SJag Raman 		if (node_match_func(node_info, &hp_node_info))
573411cb4a0SJag Raman 			break;
574411cb4a0SJag Raman 
575411cb4a0SJag Raman 		rel_info_func(&hp_node_info);
576411cb4a0SJag Raman 	}
577411cb4a0SJag Raman 
578411cb4a0SJag Raman 	rel_info_func(&hp_node_info);
579411cb4a0SJag Raman 
580411cb4a0SJag Raman 	return hp_node;
581411cb4a0SJag Raman }
582f4d29ca7SJag Raman EXPORT_SYMBOL(mdesc_get_node);
583411cb4a0SJag Raman 
mdesc_get_node_info(struct mdesc_handle * hp,u64 node,const char * node_name,union md_node_info * node_info)584411cb4a0SJag Raman int mdesc_get_node_info(struct mdesc_handle *hp, u64 node,
585411cb4a0SJag Raman 			const char *node_name, union md_node_info *node_info)
586411cb4a0SJag Raman {
587411cb4a0SJag Raman 	mdesc_node_info_get_f get_info_func;
588411cb4a0SJag Raman 	int rv;
589411cb4a0SJag Raman 
590411cb4a0SJag Raman 	if (hp == NULL || node == MDESC_NODE_NULL ||
591411cb4a0SJag Raman 	    node_name == NULL || node_info == NULL)
592411cb4a0SJag Raman 		return -EINVAL;
593411cb4a0SJag Raman 
594411cb4a0SJag Raman 	/* Find the get_info op for the given node name */
595411cb4a0SJag Raman 	mdesc_get_node_ops(node_name, &get_info_func, NULL, NULL);
596411cb4a0SJag Raman 
597411cb4a0SJag Raman 	/* If we didn't find a get_info_func, the node name is not supported */
598411cb4a0SJag Raman 	if (get_info_func == NULL) {
599411cb4a0SJag Raman 		pr_err("MD: %s node is not supported\n", node_name);
600411cb4a0SJag Raman 		return -EINVAL;
601411cb4a0SJag Raman 	}
602411cb4a0SJag Raman 
603411cb4a0SJag Raman 	rv = get_info_func(hp, node, node_info);
604411cb4a0SJag Raman 	if (rv != 0) {
605411cb4a0SJag Raman 		pr_err("MD: Cannot find 1 or more required match properties for %s node.\n",
606411cb4a0SJag Raman 		       node_name);
607411cb4a0SJag Raman 		return -1;
608411cb4a0SJag Raman 	}
609411cb4a0SJag Raman 
610411cb4a0SJag Raman 	return 0;
611411cb4a0SJag Raman }
612f4d29ca7SJag Raman EXPORT_SYMBOL(mdesc_get_node_info);
613411cb4a0SJag Raman 
node_block(struct mdesc_hdr * mdesc)614a88b5ba8SSam Ravnborg static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc)
615a88b5ba8SSam Ravnborg {
616*fc7c028dSLinus Torvalds 	return (struct mdesc_elem *) mdesc->data;
617a88b5ba8SSam Ravnborg }
618a88b5ba8SSam Ravnborg 
name_block(struct mdesc_hdr * mdesc)619a88b5ba8SSam Ravnborg static void *name_block(struct mdesc_hdr *mdesc)
620a88b5ba8SSam Ravnborg {
621a88b5ba8SSam Ravnborg 	return ((void *) node_block(mdesc)) + mdesc->node_sz;
622a88b5ba8SSam Ravnborg }
623a88b5ba8SSam Ravnborg 
data_block(struct mdesc_hdr * mdesc)624a88b5ba8SSam Ravnborg static void *data_block(struct mdesc_hdr *mdesc)
625a88b5ba8SSam Ravnborg {
626a88b5ba8SSam Ravnborg 	return ((void *) name_block(mdesc)) + mdesc->name_sz;
627a88b5ba8SSam Ravnborg }
628a88b5ba8SSam Ravnborg 
mdesc_node_by_name(struct mdesc_handle * hp,u64 from_node,const char * name)629a88b5ba8SSam Ravnborg u64 mdesc_node_by_name(struct mdesc_handle *hp,
630a88b5ba8SSam Ravnborg 		       u64 from_node, const char *name)
631a88b5ba8SSam Ravnborg {
632a88b5ba8SSam Ravnborg 	struct mdesc_elem *ep = node_block(&hp->mdesc);
633a88b5ba8SSam Ravnborg 	const char *names = name_block(&hp->mdesc);
634a88b5ba8SSam Ravnborg 	u64 last_node = hp->mdesc.node_sz / 16;
635a88b5ba8SSam Ravnborg 	u64 ret;
636a88b5ba8SSam Ravnborg 
637a88b5ba8SSam Ravnborg 	if (from_node == MDESC_NODE_NULL) {
638a88b5ba8SSam Ravnborg 		ret = from_node = 0;
639a88b5ba8SSam Ravnborg 	} else if (from_node >= last_node) {
640a88b5ba8SSam Ravnborg 		return MDESC_NODE_NULL;
641a88b5ba8SSam Ravnborg 	} else {
642a88b5ba8SSam Ravnborg 		ret = ep[from_node].d.val;
643a88b5ba8SSam Ravnborg 	}
644a88b5ba8SSam Ravnborg 
645a88b5ba8SSam Ravnborg 	while (ret < last_node) {
646a88b5ba8SSam Ravnborg 		if (ep[ret].tag != MD_NODE)
647a88b5ba8SSam Ravnborg 			return MDESC_NODE_NULL;
648a88b5ba8SSam Ravnborg 		if (!strcmp(names + ep[ret].name_offset, name))
649a88b5ba8SSam Ravnborg 			break;
650a88b5ba8SSam Ravnborg 		ret = ep[ret].d.val;
651a88b5ba8SSam Ravnborg 	}
652a88b5ba8SSam Ravnborg 	if (ret >= last_node)
653a88b5ba8SSam Ravnborg 		ret = MDESC_NODE_NULL;
654a88b5ba8SSam Ravnborg 	return ret;
655a88b5ba8SSam Ravnborg }
656a88b5ba8SSam Ravnborg EXPORT_SYMBOL(mdesc_node_by_name);
657a88b5ba8SSam Ravnborg 
mdesc_get_property(struct mdesc_handle * hp,u64 node,const char * name,int * lenp)658a88b5ba8SSam Ravnborg const void *mdesc_get_property(struct mdesc_handle *hp, u64 node,
659a88b5ba8SSam Ravnborg 			       const char *name, int *lenp)
660a88b5ba8SSam Ravnborg {
661a88b5ba8SSam Ravnborg 	const char *names = name_block(&hp->mdesc);
662a88b5ba8SSam Ravnborg 	u64 last_node = hp->mdesc.node_sz / 16;
663a88b5ba8SSam Ravnborg 	void *data = data_block(&hp->mdesc);
664a88b5ba8SSam Ravnborg 	struct mdesc_elem *ep;
665a88b5ba8SSam Ravnborg 
666a88b5ba8SSam Ravnborg 	if (node == MDESC_NODE_NULL || node >= last_node)
667a88b5ba8SSam Ravnborg 		return NULL;
668a88b5ba8SSam Ravnborg 
669a88b5ba8SSam Ravnborg 	ep = node_block(&hp->mdesc) + node;
670a88b5ba8SSam Ravnborg 	ep++;
671a88b5ba8SSam Ravnborg 	for (; ep->tag != MD_NODE_END; ep++) {
672a88b5ba8SSam Ravnborg 		void *val = NULL;
673a88b5ba8SSam Ravnborg 		int len = 0;
674a88b5ba8SSam Ravnborg 
675a88b5ba8SSam Ravnborg 		switch (ep->tag) {
676a88b5ba8SSam Ravnborg 		case MD_PROP_VAL:
677a88b5ba8SSam Ravnborg 			val = &ep->d.val;
678a88b5ba8SSam Ravnborg 			len = 8;
679a88b5ba8SSam Ravnborg 			break;
680a88b5ba8SSam Ravnborg 
681a88b5ba8SSam Ravnborg 		case MD_PROP_STR:
682a88b5ba8SSam Ravnborg 		case MD_PROP_DATA:
683a88b5ba8SSam Ravnborg 			val = data + ep->d.data.data_offset;
684a88b5ba8SSam Ravnborg 			len = ep->d.data.data_len;
685a88b5ba8SSam Ravnborg 			break;
686a88b5ba8SSam Ravnborg 
687a88b5ba8SSam Ravnborg 		default:
688a88b5ba8SSam Ravnborg 			break;
689a88b5ba8SSam Ravnborg 		}
690a88b5ba8SSam Ravnborg 		if (!val)
691a88b5ba8SSam Ravnborg 			continue;
692a88b5ba8SSam Ravnborg 
693a88b5ba8SSam Ravnborg 		if (!strcmp(names + ep->name_offset, name)) {
694a88b5ba8SSam Ravnborg 			if (lenp)
695a88b5ba8SSam Ravnborg 				*lenp = len;
696a88b5ba8SSam Ravnborg 			return val;
697a88b5ba8SSam Ravnborg 		}
698a88b5ba8SSam Ravnborg 	}
699a88b5ba8SSam Ravnborg 
700a88b5ba8SSam Ravnborg 	return NULL;
701a88b5ba8SSam Ravnborg }
702a88b5ba8SSam Ravnborg EXPORT_SYMBOL(mdesc_get_property);
703a88b5ba8SSam Ravnborg 
mdesc_next_arc(struct mdesc_handle * hp,u64 from,const char * arc_type)704a88b5ba8SSam Ravnborg u64 mdesc_next_arc(struct mdesc_handle *hp, u64 from, const char *arc_type)
705a88b5ba8SSam Ravnborg {
706a88b5ba8SSam Ravnborg 	struct mdesc_elem *ep, *base = node_block(&hp->mdesc);
707a88b5ba8SSam Ravnborg 	const char *names = name_block(&hp->mdesc);
708a88b5ba8SSam Ravnborg 	u64 last_node = hp->mdesc.node_sz / 16;
709a88b5ba8SSam Ravnborg 
710a88b5ba8SSam Ravnborg 	if (from == MDESC_NODE_NULL || from >= last_node)
711a88b5ba8SSam Ravnborg 		return MDESC_NODE_NULL;
712a88b5ba8SSam Ravnborg 
713a88b5ba8SSam Ravnborg 	ep = base + from;
714a88b5ba8SSam Ravnborg 
715a88b5ba8SSam Ravnborg 	ep++;
716a88b5ba8SSam Ravnborg 	for (; ep->tag != MD_NODE_END; ep++) {
717a88b5ba8SSam Ravnborg 		if (ep->tag != MD_PROP_ARC)
718a88b5ba8SSam Ravnborg 			continue;
719a88b5ba8SSam Ravnborg 
720a88b5ba8SSam Ravnborg 		if (strcmp(names + ep->name_offset, arc_type))
721a88b5ba8SSam Ravnborg 			continue;
722a88b5ba8SSam Ravnborg 
723a88b5ba8SSam Ravnborg 		return ep - base;
724a88b5ba8SSam Ravnborg 	}
725a88b5ba8SSam Ravnborg 
726a88b5ba8SSam Ravnborg 	return MDESC_NODE_NULL;
727a88b5ba8SSam Ravnborg }
728a88b5ba8SSam Ravnborg EXPORT_SYMBOL(mdesc_next_arc);
729a88b5ba8SSam Ravnborg 
mdesc_arc_target(struct mdesc_handle * hp,u64 arc)730a88b5ba8SSam Ravnborg u64 mdesc_arc_target(struct mdesc_handle *hp, u64 arc)
731a88b5ba8SSam Ravnborg {
732a88b5ba8SSam Ravnborg 	struct mdesc_elem *ep, *base = node_block(&hp->mdesc);
733a88b5ba8SSam Ravnborg 
734a88b5ba8SSam Ravnborg 	ep = base + arc;
735a88b5ba8SSam Ravnborg 
736a88b5ba8SSam Ravnborg 	return ep->d.val;
737a88b5ba8SSam Ravnborg }
738a88b5ba8SSam Ravnborg EXPORT_SYMBOL(mdesc_arc_target);
739a88b5ba8SSam Ravnborg 
mdesc_node_name(struct mdesc_handle * hp,u64 node)740a88b5ba8SSam Ravnborg const char *mdesc_node_name(struct mdesc_handle *hp, u64 node)
741a88b5ba8SSam Ravnborg {
742a88b5ba8SSam Ravnborg 	struct mdesc_elem *ep, *base = node_block(&hp->mdesc);
743a88b5ba8SSam Ravnborg 	const char *names = name_block(&hp->mdesc);
744a88b5ba8SSam Ravnborg 	u64 last_node = hp->mdesc.node_sz / 16;
745a88b5ba8SSam Ravnborg 
746a88b5ba8SSam Ravnborg 	if (node == MDESC_NODE_NULL || node >= last_node)
747a88b5ba8SSam Ravnborg 		return NULL;
748a88b5ba8SSam Ravnborg 
749a88b5ba8SSam Ravnborg 	ep = base + node;
750a88b5ba8SSam Ravnborg 	if (ep->tag != MD_NODE)
751a88b5ba8SSam Ravnborg 		return NULL;
752a88b5ba8SSam Ravnborg 
753a88b5ba8SSam Ravnborg 	return names + ep->name_offset;
754a88b5ba8SSam Ravnborg }
755a88b5ba8SSam Ravnborg EXPORT_SYMBOL(mdesc_node_name);
756a88b5ba8SSam Ravnborg 
757961f65fcSDavid S. Miller static u64 max_cpus = 64;
758961f65fcSDavid S. Miller 
report_platform_properties(void)759a88b5ba8SSam Ravnborg static void __init report_platform_properties(void)
760a88b5ba8SSam Ravnborg {
761a88b5ba8SSam Ravnborg 	struct mdesc_handle *hp = mdesc_grab();
762a88b5ba8SSam Ravnborg 	u64 pn = mdesc_node_by_name(hp, MDESC_NODE_NULL, "platform");
763a88b5ba8SSam Ravnborg 	const char *s;
764a88b5ba8SSam Ravnborg 	const u64 *v;
765a88b5ba8SSam Ravnborg 
766a88b5ba8SSam Ravnborg 	if (pn == MDESC_NODE_NULL) {
767a88b5ba8SSam Ravnborg 		prom_printf("No platform node in machine-description.\n");
768a88b5ba8SSam Ravnborg 		prom_halt();
769a88b5ba8SSam Ravnborg 	}
770a88b5ba8SSam Ravnborg 
771a88b5ba8SSam Ravnborg 	s = mdesc_get_property(hp, pn, "banner-name", NULL);
772a88b5ba8SSam Ravnborg 	printk("PLATFORM: banner-name [%s]\n", s);
773a88b5ba8SSam Ravnborg 	s = mdesc_get_property(hp, pn, "name", NULL);
774a88b5ba8SSam Ravnborg 	printk("PLATFORM: name [%s]\n", s);
775a88b5ba8SSam Ravnborg 
776a88b5ba8SSam Ravnborg 	v = mdesc_get_property(hp, pn, "hostid", NULL);
777a88b5ba8SSam Ravnborg 	if (v)
77890181136SSam Ravnborg 		printk("PLATFORM: hostid [%08llx]\n", *v);
779a88b5ba8SSam Ravnborg 	v = mdesc_get_property(hp, pn, "serial#", NULL);
780a88b5ba8SSam Ravnborg 	if (v)
78190181136SSam Ravnborg 		printk("PLATFORM: serial# [%08llx]\n", *v);
782a88b5ba8SSam Ravnborg 	v = mdesc_get_property(hp, pn, "stick-frequency", NULL);
78390181136SSam Ravnborg 	printk("PLATFORM: stick-frequency [%08llx]\n", *v);
784a88b5ba8SSam Ravnborg 	v = mdesc_get_property(hp, pn, "mac-address", NULL);
785a88b5ba8SSam Ravnborg 	if (v)
78690181136SSam Ravnborg 		printk("PLATFORM: mac-address [%llx]\n", *v);
787a88b5ba8SSam Ravnborg 	v = mdesc_get_property(hp, pn, "watchdog-resolution", NULL);
788a88b5ba8SSam Ravnborg 	if (v)
78990181136SSam Ravnborg 		printk("PLATFORM: watchdog-resolution [%llu ms]\n", *v);
790a88b5ba8SSam Ravnborg 	v = mdesc_get_property(hp, pn, "watchdog-max-timeout", NULL);
791a88b5ba8SSam Ravnborg 	if (v)
79290181136SSam Ravnborg 		printk("PLATFORM: watchdog-max-timeout [%llu ms]\n", *v);
793a88b5ba8SSam Ravnborg 	v = mdesc_get_property(hp, pn, "max-cpus", NULL);
794961f65fcSDavid S. Miller 	if (v) {
795961f65fcSDavid S. Miller 		max_cpus = *v;
796961f65fcSDavid S. Miller 		printk("PLATFORM: max-cpus [%llu]\n", max_cpus);
797961f65fcSDavid S. Miller 	}
798a88b5ba8SSam Ravnborg 
799a88b5ba8SSam Ravnborg #ifdef CONFIG_SMP
800a88b5ba8SSam Ravnborg 	{
801a88b5ba8SSam Ravnborg 		int max_cpu, i;
802a88b5ba8SSam Ravnborg 
803a88b5ba8SSam Ravnborg 		if (v) {
804a88b5ba8SSam Ravnborg 			max_cpu = *v;
805a88b5ba8SSam Ravnborg 			if (max_cpu > NR_CPUS)
806a88b5ba8SSam Ravnborg 				max_cpu = NR_CPUS;
807a88b5ba8SSam Ravnborg 		} else {
808a88b5ba8SSam Ravnborg 			max_cpu = NR_CPUS;
809a88b5ba8SSam Ravnborg 		}
810a88b5ba8SSam Ravnborg 		for (i = 0; i < max_cpu; i++)
81189229071SRusty Russell 			set_cpu_possible(i, true);
812a88b5ba8SSam Ravnborg 	}
813a88b5ba8SSam Ravnborg #endif
814a88b5ba8SSam Ravnborg 
815a88b5ba8SSam Ravnborg 	mdesc_release(hp);
816a88b5ba8SSam Ravnborg }
817a88b5ba8SSam Ravnborg 
fill_in_one_cache(cpuinfo_sparc * c,struct mdesc_handle * hp,u64 mp)8182066aaddSPaul Gortmaker static void fill_in_one_cache(cpuinfo_sparc *c, struct mdesc_handle *hp, u64 mp)
819a88b5ba8SSam Ravnborg {
820a88b5ba8SSam Ravnborg 	const u64 *level = mdesc_get_property(hp, mp, "level", NULL);
821a88b5ba8SSam Ravnborg 	const u64 *size = mdesc_get_property(hp, mp, "size", NULL);
822a88b5ba8SSam Ravnborg 	const u64 *line_size = mdesc_get_property(hp, mp, "line-size", NULL);
823a88b5ba8SSam Ravnborg 	const char *type;
824a88b5ba8SSam Ravnborg 	int type_len;
825a88b5ba8SSam Ravnborg 
826a88b5ba8SSam Ravnborg 	type = mdesc_get_property(hp, mp, "type", &type_len);
827a88b5ba8SSam Ravnborg 
828a88b5ba8SSam Ravnborg 	switch (*level) {
829a88b5ba8SSam Ravnborg 	case 1:
830a88b5ba8SSam Ravnborg 		if (of_find_in_proplist(type, "instn", type_len)) {
831a88b5ba8SSam Ravnborg 			c->icache_size = *size;
832a88b5ba8SSam Ravnborg 			c->icache_line_size = *line_size;
833a88b5ba8SSam Ravnborg 		} else if (of_find_in_proplist(type, "data", type_len)) {
834a88b5ba8SSam Ravnborg 			c->dcache_size = *size;
835a88b5ba8SSam Ravnborg 			c->dcache_line_size = *line_size;
836a88b5ba8SSam Ravnborg 		}
837a88b5ba8SSam Ravnborg 		break;
838a88b5ba8SSam Ravnborg 
839a88b5ba8SSam Ravnborg 	case 2:
840a88b5ba8SSam Ravnborg 		c->ecache_size = *size;
841a88b5ba8SSam Ravnborg 		c->ecache_line_size = *line_size;
842a88b5ba8SSam Ravnborg 		break;
843a88b5ba8SSam Ravnborg 
844a88b5ba8SSam Ravnborg 	default:
845a88b5ba8SSam Ravnborg 		break;
846a88b5ba8SSam Ravnborg 	}
847a88b5ba8SSam Ravnborg 
848a88b5ba8SSam Ravnborg 	if (*level == 1) {
849a88b5ba8SSam Ravnborg 		u64 a;
850a88b5ba8SSam Ravnborg 
851a88b5ba8SSam Ravnborg 		mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) {
852a88b5ba8SSam Ravnborg 			u64 target = mdesc_arc_target(hp, a);
853a88b5ba8SSam Ravnborg 			const char *name = mdesc_node_name(hp, target);
854a88b5ba8SSam Ravnborg 
855a88b5ba8SSam Ravnborg 			if (!strcmp(name, "cache"))
856a88b5ba8SSam Ravnborg 				fill_in_one_cache(c, hp, target);
857a88b5ba8SSam Ravnborg 		}
858a88b5ba8SSam Ravnborg 	}
859a88b5ba8SSam Ravnborg }
860a88b5ba8SSam Ravnborg 
find_back_node_value(struct mdesc_handle * hp,u64 node,char * srch_val,void (* func)(struct mdesc_handle *,u64,int),u64 val,int depth)861acc455cfSchris hyser static void find_back_node_value(struct mdesc_handle *hp, u64 node,
862acc455cfSchris hyser 				 char *srch_val,
863acc455cfSchris hyser 				 void (*func)(struct mdesc_handle *, u64, int),
864acc455cfSchris hyser 				 u64 val, int depth)
865a88b5ba8SSam Ravnborg {
866acc455cfSchris hyser 	u64 arc;
867a88b5ba8SSam Ravnborg 
868acc455cfSchris hyser 	/* Since we have an estimate of recursion depth, do a sanity check. */
869acc455cfSchris hyser 	if (depth == 0)
870acc455cfSchris hyser 		return;
871a88b5ba8SSam Ravnborg 
872acc455cfSchris hyser 	mdesc_for_each_arc(arc, hp, node, MDESC_ARC_TYPE_BACK) {
873acc455cfSchris hyser 		u64 n = mdesc_arc_target(hp, arc);
874acc455cfSchris hyser 		const char *name = mdesc_node_name(hp, n);
875a88b5ba8SSam Ravnborg 
876acc455cfSchris hyser 		if (!strcmp(srch_val, name))
877acc455cfSchris hyser 			(*func)(hp, n, val);
878a88b5ba8SSam Ravnborg 
879acc455cfSchris hyser 		find_back_node_value(hp, n, srch_val, func, val, depth-1);
880acc455cfSchris hyser 	}
881acc455cfSchris hyser }
882a88b5ba8SSam Ravnborg 
__mark_core_id(struct mdesc_handle * hp,u64 node,int core_id)883acc455cfSchris hyser static void __mark_core_id(struct mdesc_handle *hp, u64 node,
884acc455cfSchris hyser 			   int core_id)
885acc455cfSchris hyser {
886acc455cfSchris hyser 	const u64 *id = mdesc_get_property(hp, node, "id", NULL);
887acc455cfSchris hyser 
888acc455cfSchris hyser 	if (*id < num_possible_cpus())
889a88b5ba8SSam Ravnborg 		cpu_data(*id).core_id = core_id;
890a88b5ba8SSam Ravnborg }
891acc455cfSchris hyser 
__mark_max_cache_id(struct mdesc_handle * hp,u64 node,int max_cache_id)892d624716bSAtish Patra static void __mark_max_cache_id(struct mdesc_handle *hp, u64 node,
893d624716bSAtish Patra 				int max_cache_id)
894acc455cfSchris hyser {
895acc455cfSchris hyser 	const u64 *id = mdesc_get_property(hp, node, "id", NULL);
896acc455cfSchris hyser 
897d624716bSAtish Patra 	if (*id < num_possible_cpus()) {
898d624716bSAtish Patra 		cpu_data(*id).max_cache_id = max_cache_id;
899d624716bSAtish Patra 
900d624716bSAtish Patra 		/**
901d624716bSAtish Patra 		 * On systems without explicit socket descriptions socket
902d624716bSAtish Patra 		 * is max_cache_id
903d624716bSAtish Patra 		 */
904d624716bSAtish Patra 		cpu_data(*id).sock_id = max_cache_id;
905d624716bSAtish Patra 	}
906a88b5ba8SSam Ravnborg }
907acc455cfSchris hyser 
mark_core_ids(struct mdesc_handle * hp,u64 mp,int core_id)908acc455cfSchris hyser static void mark_core_ids(struct mdesc_handle *hp, u64 mp,
909acc455cfSchris hyser 			  int core_id)
910acc455cfSchris hyser {
911acc455cfSchris hyser 	find_back_node_value(hp, mp, "cpu", __mark_core_id, core_id, 10);
912a88b5ba8SSam Ravnborg }
913acc455cfSchris hyser 
mark_max_cache_ids(struct mdesc_handle * hp,u64 mp,int max_cache_id)914d624716bSAtish Patra static void mark_max_cache_ids(struct mdesc_handle *hp, u64 mp,
915d624716bSAtish Patra 			       int max_cache_id)
916acc455cfSchris hyser {
917d624716bSAtish Patra 	find_back_node_value(hp, mp, "cpu", __mark_max_cache_id,
918d624716bSAtish Patra 			     max_cache_id, 10);
919a88b5ba8SSam Ravnborg }
920a88b5ba8SSam Ravnborg 
set_core_ids(struct mdesc_handle * hp)9212066aaddSPaul Gortmaker static void set_core_ids(struct mdesc_handle *hp)
922a88b5ba8SSam Ravnborg {
923a88b5ba8SSam Ravnborg 	int idx;
924a88b5ba8SSam Ravnborg 	u64 mp;
925a88b5ba8SSam Ravnborg 
926a88b5ba8SSam Ravnborg 	idx = 1;
927acc455cfSchris hyser 
928acc455cfSchris hyser 	/* Identify unique cores by looking for cpus backpointed to by
929acc455cfSchris hyser 	 * level 1 instruction caches.
930acc455cfSchris hyser 	 */
931a88b5ba8SSam Ravnborg 	mdesc_for_each_node_by_name(hp, mp, "cache") {
932a88b5ba8SSam Ravnborg 		const u64 *level;
933a88b5ba8SSam Ravnborg 		const char *type;
934a88b5ba8SSam Ravnborg 		int len;
935a88b5ba8SSam Ravnborg 
936a88b5ba8SSam Ravnborg 		level = mdesc_get_property(hp, mp, "level", NULL);
937a88b5ba8SSam Ravnborg 		if (*level != 1)
938a88b5ba8SSam Ravnborg 			continue;
939a88b5ba8SSam Ravnborg 
940a88b5ba8SSam Ravnborg 		type = mdesc_get_property(hp, mp, "type", &len);
941a88b5ba8SSam Ravnborg 		if (!of_find_in_proplist(type, "instn", len))
942a88b5ba8SSam Ravnborg 			continue;
943a88b5ba8SSam Ravnborg 
944a88b5ba8SSam Ravnborg 		mark_core_ids(hp, mp, idx);
945a88b5ba8SSam Ravnborg 		idx++;
946a88b5ba8SSam Ravnborg 	}
947a88b5ba8SSam Ravnborg }
948a88b5ba8SSam Ravnborg 
set_max_cache_ids_by_cache(struct mdesc_handle * hp,int level)949d624716bSAtish Patra static int set_max_cache_ids_by_cache(struct mdesc_handle *hp, int level)
950acc455cfSchris hyser {
951acc455cfSchris hyser 	u64 mp;
952acc455cfSchris hyser 	int idx = 1;
953acc455cfSchris hyser 	int fnd = 0;
954acc455cfSchris hyser 
955d624716bSAtish Patra 	/**
956d624716bSAtish Patra 	 * Identify unique highest level of shared cache by looking for cpus
957d624716bSAtish Patra 	 * backpointed to by shared level N caches.
958acc455cfSchris hyser 	 */
959acc455cfSchris hyser 	mdesc_for_each_node_by_name(hp, mp, "cache") {
960acc455cfSchris hyser 		const u64 *cur_lvl;
961acc455cfSchris hyser 
962acc455cfSchris hyser 		cur_lvl = mdesc_get_property(hp, mp, "level", NULL);
963acc455cfSchris hyser 		if (*cur_lvl != level)
964acc455cfSchris hyser 			continue;
965d624716bSAtish Patra 		mark_max_cache_ids(hp, mp, idx);
966acc455cfSchris hyser 		idx++;
967acc455cfSchris hyser 		fnd = 1;
968acc455cfSchris hyser 	}
969acc455cfSchris hyser 	return fnd;
970acc455cfSchris hyser }
971acc455cfSchris hyser 
set_sock_ids_by_socket(struct mdesc_handle * hp,u64 mp)972acc455cfSchris hyser static void set_sock_ids_by_socket(struct mdesc_handle *hp, u64 mp)
973acc455cfSchris hyser {
974acc455cfSchris hyser 	int idx = 1;
975acc455cfSchris hyser 
976acc455cfSchris hyser 	mdesc_for_each_node_by_name(hp, mp, "socket") {
977acc455cfSchris hyser 		u64 a;
978acc455cfSchris hyser 
979acc455cfSchris hyser 		mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) {
980acc455cfSchris hyser 			u64 t = mdesc_arc_target(hp, a);
981acc455cfSchris hyser 			const char *name;
982acc455cfSchris hyser 			const u64 *id;
983acc455cfSchris hyser 
984acc455cfSchris hyser 			name = mdesc_node_name(hp, t);
985acc455cfSchris hyser 			if (strcmp(name, "cpu"))
986acc455cfSchris hyser 				continue;
987acc455cfSchris hyser 
988acc455cfSchris hyser 			id = mdesc_get_property(hp, t, "id", NULL);
989acc455cfSchris hyser 			if (*id < num_possible_cpus())
990acc455cfSchris hyser 				cpu_data(*id).sock_id = idx;
991acc455cfSchris hyser 		}
992acc455cfSchris hyser 		idx++;
993acc455cfSchris hyser 	}
994acc455cfSchris hyser }
995acc455cfSchris hyser 
set_sock_ids(struct mdesc_handle * hp)996acc455cfSchris hyser static void set_sock_ids(struct mdesc_handle *hp)
997acc455cfSchris hyser {
998acc455cfSchris hyser 	u64 mp;
999acc455cfSchris hyser 
1000d624716bSAtish Patra 	/**
1001d624716bSAtish Patra 	 * Find the highest level of shared cache which pre-T7 is also
1002d624716bSAtish Patra 	 * the socket.
1003acc455cfSchris hyser 	 */
1004d624716bSAtish Patra 	if (!set_max_cache_ids_by_cache(hp, 3))
1005d624716bSAtish Patra 		set_max_cache_ids_by_cache(hp, 2);
1006d624716bSAtish Patra 
1007d624716bSAtish Patra 	/* If machine description exposes sockets data use it.*/
1008acc455cfSchris hyser 	mp = mdesc_node_by_name(hp, MDESC_NODE_NULL, "sockets");
1009acc455cfSchris hyser 	if (mp != MDESC_NODE_NULL)
1010d624716bSAtish Patra 		set_sock_ids_by_socket(hp, mp);
1011acc455cfSchris hyser }
1012acc455cfSchris hyser 
mark_proc_ids(struct mdesc_handle * hp,u64 mp,int proc_id)10132066aaddSPaul Gortmaker static void mark_proc_ids(struct mdesc_handle *hp, u64 mp, int proc_id)
1014a88b5ba8SSam Ravnborg {
1015a88b5ba8SSam Ravnborg 	u64 a;
1016a88b5ba8SSam Ravnborg 
1017a88b5ba8SSam Ravnborg 	mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) {
1018a88b5ba8SSam Ravnborg 		u64 t = mdesc_arc_target(hp, a);
1019a88b5ba8SSam Ravnborg 		const char *name;
1020a88b5ba8SSam Ravnborg 		const u64 *id;
1021a88b5ba8SSam Ravnborg 
1022a88b5ba8SSam Ravnborg 		name = mdesc_node_name(hp, t);
1023a88b5ba8SSam Ravnborg 		if (strcmp(name, "cpu"))
1024a88b5ba8SSam Ravnborg 			continue;
1025a88b5ba8SSam Ravnborg 
1026a88b5ba8SSam Ravnborg 		id = mdesc_get_property(hp, t, "id", NULL);
1027a88b5ba8SSam Ravnborg 		if (*id < NR_CPUS)
1028a88b5ba8SSam Ravnborg 			cpu_data(*id).proc_id = proc_id;
1029a88b5ba8SSam Ravnborg 	}
1030a88b5ba8SSam Ravnborg }
1031a88b5ba8SSam Ravnborg 
__set_proc_ids(struct mdesc_handle * hp,const char * exec_unit_name)10322066aaddSPaul Gortmaker static void __set_proc_ids(struct mdesc_handle *hp, const char *exec_unit_name)
1033a88b5ba8SSam Ravnborg {
1034a88b5ba8SSam Ravnborg 	int idx;
1035a88b5ba8SSam Ravnborg 	u64 mp;
1036a88b5ba8SSam Ravnborg 
1037a88b5ba8SSam Ravnborg 	idx = 0;
1038a88b5ba8SSam Ravnborg 	mdesc_for_each_node_by_name(hp, mp, exec_unit_name) {
1039a88b5ba8SSam Ravnborg 		const char *type;
1040a88b5ba8SSam Ravnborg 		int len;
1041a88b5ba8SSam Ravnborg 
1042a88b5ba8SSam Ravnborg 		type = mdesc_get_property(hp, mp, "type", &len);
1043a88b5ba8SSam Ravnborg 		if (!of_find_in_proplist(type, "int", len) &&
1044a88b5ba8SSam Ravnborg 		    !of_find_in_proplist(type, "integer", len))
1045a88b5ba8SSam Ravnborg 			continue;
1046a88b5ba8SSam Ravnborg 
1047a88b5ba8SSam Ravnborg 		mark_proc_ids(hp, mp, idx);
1048a88b5ba8SSam Ravnborg 		idx++;
1049a88b5ba8SSam Ravnborg 	}
1050a88b5ba8SSam Ravnborg }
1051a88b5ba8SSam Ravnborg 
set_proc_ids(struct mdesc_handle * hp)10522066aaddSPaul Gortmaker static void set_proc_ids(struct mdesc_handle *hp)
1053a88b5ba8SSam Ravnborg {
1054a88b5ba8SSam Ravnborg 	__set_proc_ids(hp, "exec_unit");
1055a88b5ba8SSam Ravnborg 	__set_proc_ids(hp, "exec-unit");
1056a88b5ba8SSam Ravnborg }
1057a88b5ba8SSam Ravnborg 
get_one_mondo_bits(const u64 * p,unsigned int * mask,unsigned long def,unsigned long max)10582066aaddSPaul Gortmaker static void get_one_mondo_bits(const u64 *p, unsigned int *mask,
1059961f65fcSDavid S. Miller 			       unsigned long def, unsigned long max)
1060a88b5ba8SSam Ravnborg {
1061a88b5ba8SSam Ravnborg 	u64 val;
1062a88b5ba8SSam Ravnborg 
1063a88b5ba8SSam Ravnborg 	if (!p)
1064a88b5ba8SSam Ravnborg 		goto use_default;
1065a88b5ba8SSam Ravnborg 	val = *p;
1066a88b5ba8SSam Ravnborg 
1067a88b5ba8SSam Ravnborg 	if (!val || val >= 64)
1068a88b5ba8SSam Ravnborg 		goto use_default;
1069a88b5ba8SSam Ravnborg 
1070961f65fcSDavid S. Miller 	if (val > max)
1071961f65fcSDavid S. Miller 		val = max;
1072961f65fcSDavid S. Miller 
1073a88b5ba8SSam Ravnborg 	*mask = ((1U << val) * 64U) - 1U;
1074a88b5ba8SSam Ravnborg 	return;
1075a88b5ba8SSam Ravnborg 
1076a88b5ba8SSam Ravnborg use_default:
1077a88b5ba8SSam Ravnborg 	*mask = ((1U << def) * 64U) - 1U;
1078a88b5ba8SSam Ravnborg }
1079a88b5ba8SSam Ravnborg 
get_mondo_data(struct mdesc_handle * hp,u64 mp,struct trap_per_cpu * tb)10802066aaddSPaul Gortmaker static void get_mondo_data(struct mdesc_handle *hp, u64 mp,
1081a88b5ba8SSam Ravnborg 			   struct trap_per_cpu *tb)
1082a88b5ba8SSam Ravnborg {
1083961f65fcSDavid S. Miller 	static int printed;
1084a88b5ba8SSam Ravnborg 	const u64 *val;
1085a88b5ba8SSam Ravnborg 
1086a88b5ba8SSam Ravnborg 	val = mdesc_get_property(hp, mp, "q-cpu-mondo-#bits", NULL);
1087961f65fcSDavid S. Miller 	get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7, ilog2(max_cpus * 2));
1088a88b5ba8SSam Ravnborg 
1089a88b5ba8SSam Ravnborg 	val = mdesc_get_property(hp, mp, "q-dev-mondo-#bits", NULL);
1090961f65fcSDavid S. Miller 	get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7, 8);
1091a88b5ba8SSam Ravnborg 
1092a88b5ba8SSam Ravnborg 	val = mdesc_get_property(hp, mp, "q-resumable-#bits", NULL);
1093961f65fcSDavid S. Miller 	get_one_mondo_bits(val, &tb->resum_qmask, 6, 7);
1094a88b5ba8SSam Ravnborg 
1095a88b5ba8SSam Ravnborg 	val = mdesc_get_property(hp, mp, "q-nonresumable-#bits", NULL);
1096961f65fcSDavid S. Miller 	get_one_mondo_bits(val, &tb->nonresum_qmask, 2, 2);
1097961f65fcSDavid S. Miller 	if (!printed++) {
1098961f65fcSDavid S. Miller 		pr_info("SUN4V: Mondo queue sizes "
1099961f65fcSDavid S. Miller 			"[cpu(%u) dev(%u) r(%u) nr(%u)]\n",
1100961f65fcSDavid S. Miller 			tb->cpu_mondo_qmask + 1,
1101961f65fcSDavid S. Miller 			tb->dev_mondo_qmask + 1,
1102961f65fcSDavid S. Miller 			tb->resum_qmask + 1,
1103961f65fcSDavid S. Miller 			tb->nonresum_qmask + 1);
1104961f65fcSDavid S. Miller 	}
1105a88b5ba8SSam Ravnborg }
1106a88b5ba8SSam Ravnborg 
mdesc_iterate_over_cpus(void * (* func)(struct mdesc_handle *,u64,int,void *),void * arg,cpumask_t * mask)11072066aaddSPaul Gortmaker static void *mdesc_iterate_over_cpus(void *(*func)(struct mdesc_handle *, u64, int, void *), void *arg, cpumask_t *mask)
1108a88b5ba8SSam Ravnborg {
1109a88b5ba8SSam Ravnborg 	struct mdesc_handle *hp = mdesc_grab();
11105052f525SDavid S. Miller 	void *ret = NULL;
1111a88b5ba8SSam Ravnborg 	u64 mp;
1112a88b5ba8SSam Ravnborg 
1113a88b5ba8SSam Ravnborg 	mdesc_for_each_node_by_name(hp, mp, "cpu") {
1114a88b5ba8SSam Ravnborg 		const u64 *id = mdesc_get_property(hp, mp, "id", NULL);
11155052f525SDavid S. Miller 		int cpuid = *id;
1116a88b5ba8SSam Ravnborg 
1117a88b5ba8SSam Ravnborg #ifdef CONFIG_SMP
1118a88b5ba8SSam Ravnborg 		if (cpuid >= NR_CPUS) {
1119a88b5ba8SSam Ravnborg 			printk(KERN_WARNING "Ignoring CPU %d which is "
1120a88b5ba8SSam Ravnborg 			       ">= NR_CPUS (%d)\n",
1121a88b5ba8SSam Ravnborg 			       cpuid, NR_CPUS);
1122a88b5ba8SSam Ravnborg 			continue;
1123a88b5ba8SSam Ravnborg 		}
1124fb1fece5SKOSAKI Motohiro 		if (!cpumask_test_cpu(cpuid, mask))
1125a88b5ba8SSam Ravnborg 			continue;
11265052f525SDavid S. Miller #endif
11275052f525SDavid S. Miller 
11285052f525SDavid S. Miller 		ret = func(hp, mp, cpuid, arg);
11295052f525SDavid S. Miller 		if (ret)
11305052f525SDavid S. Miller 			goto out;
11315052f525SDavid S. Miller 	}
11325052f525SDavid S. Miller out:
11335052f525SDavid S. Miller 	mdesc_release(hp);
11345052f525SDavid S. Miller 	return ret;
11355052f525SDavid S. Miller }
11365052f525SDavid S. Miller 
record_one_cpu(struct mdesc_handle * hp,u64 mp,int cpuid,void * arg)11372066aaddSPaul Gortmaker static void *record_one_cpu(struct mdesc_handle *hp, u64 mp, int cpuid,
11382066aaddSPaul Gortmaker 			    void *arg)
11395052f525SDavid S. Miller {
11405052f525SDavid S. Miller 	ncpus_probed++;
11415052f525SDavid S. Miller #ifdef CONFIG_SMP
11425052f525SDavid S. Miller 	set_cpu_present(cpuid, true);
11435052f525SDavid S. Miller #endif
11445052f525SDavid S. Miller 	return NULL;
11455052f525SDavid S. Miller }
11465052f525SDavid S. Miller 
mdesc_populate_present_mask(cpumask_t * mask)11472066aaddSPaul Gortmaker void mdesc_populate_present_mask(cpumask_t *mask)
11485052f525SDavid S. Miller {
11495052f525SDavid S. Miller 	if (tlb_type != hypervisor)
11505052f525SDavid S. Miller 		return;
11515052f525SDavid S. Miller 
11525052f525SDavid S. Miller 	ncpus_probed = 0;
11535052f525SDavid S. Miller 	mdesc_iterate_over_cpus(record_one_cpu, NULL, mask);
11545052f525SDavid S. Miller }
11555052f525SDavid S. Miller 
check_one_pgsz(struct mdesc_handle * hp,u64 mp,int cpuid,void * arg)1156ce33fdc5SDavid S. Miller static void * __init check_one_pgsz(struct mdesc_handle *hp, u64 mp, int cpuid, void *arg)
1157ce33fdc5SDavid S. Miller {
1158ce33fdc5SDavid S. Miller 	const u64 *pgsz_prop = mdesc_get_property(hp, mp, "mmu-page-size-list", NULL);
1159ce33fdc5SDavid S. Miller 	unsigned long *pgsz_mask = arg;
1160ce33fdc5SDavid S. Miller 	u64 val;
1161ce33fdc5SDavid S. Miller 
1162ce33fdc5SDavid S. Miller 	val = (HV_PGSZ_MASK_8K | HV_PGSZ_MASK_64K |
1163ce33fdc5SDavid S. Miller 	       HV_PGSZ_MASK_512K | HV_PGSZ_MASK_4MB);
1164ce33fdc5SDavid S. Miller 	if (pgsz_prop)
1165ce33fdc5SDavid S. Miller 		val = *pgsz_prop;
1166ce33fdc5SDavid S. Miller 
1167ce33fdc5SDavid S. Miller 	if (!*pgsz_mask)
1168ce33fdc5SDavid S. Miller 		*pgsz_mask = val;
1169ce33fdc5SDavid S. Miller 	else
1170ce33fdc5SDavid S. Miller 		*pgsz_mask &= val;
1171ce33fdc5SDavid S. Miller 	return NULL;
1172ce33fdc5SDavid S. Miller }
1173ce33fdc5SDavid S. Miller 
mdesc_get_page_sizes(cpumask_t * mask,unsigned long * pgsz_mask)1174ce33fdc5SDavid S. Miller void __init mdesc_get_page_sizes(cpumask_t *mask, unsigned long *pgsz_mask)
1175ce33fdc5SDavid S. Miller {
1176ce33fdc5SDavid S. Miller 	*pgsz_mask = 0;
1177ce33fdc5SDavid S. Miller 	mdesc_iterate_over_cpus(check_one_pgsz, pgsz_mask, mask);
1178ce33fdc5SDavid S. Miller }
1179ce33fdc5SDavid S. Miller 
fill_in_one_cpu(struct mdesc_handle * hp,u64 mp,int cpuid,void * arg)11802066aaddSPaul Gortmaker static void *fill_in_one_cpu(struct mdesc_handle *hp, u64 mp, int cpuid,
11812066aaddSPaul Gortmaker 			     void *arg)
11825052f525SDavid S. Miller {
11835052f525SDavid S. Miller 	const u64 *cfreq = mdesc_get_property(hp, mp, "clock-frequency", NULL);
11845052f525SDavid S. Miller 	struct trap_per_cpu *tb;
11855052f525SDavid S. Miller 	cpuinfo_sparc *c;
11865052f525SDavid S. Miller 	u64 a;
11875052f525SDavid S. Miller 
11885052f525SDavid S. Miller #ifndef CONFIG_SMP
1189a88b5ba8SSam Ravnborg 	/* On uniprocessor we only want the values for the
1190a88b5ba8SSam Ravnborg 	 * real physical cpu the kernel booted onto, however
1191a88b5ba8SSam Ravnborg 	 * cpu_data() only has one entry at index 0.
1192a88b5ba8SSam Ravnborg 	 */
1193a88b5ba8SSam Ravnborg 	if (cpuid != real_hard_smp_processor_id())
11945052f525SDavid S. Miller 		return NULL;
1195a88b5ba8SSam Ravnborg 	cpuid = 0;
1196a88b5ba8SSam Ravnborg #endif
1197a88b5ba8SSam Ravnborg 
1198a88b5ba8SSam Ravnborg 	c = &cpu_data(cpuid);
1199a88b5ba8SSam Ravnborg 	c->clock_tick = *cfreq;
1200a88b5ba8SSam Ravnborg 
1201a88b5ba8SSam Ravnborg 	tb = &trap_block[cpuid];
1202a88b5ba8SSam Ravnborg 	get_mondo_data(hp, mp, tb);
1203a88b5ba8SSam Ravnborg 
1204a88b5ba8SSam Ravnborg 	mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) {
1205a88b5ba8SSam Ravnborg 		u64 j, t = mdesc_arc_target(hp, a);
1206a88b5ba8SSam Ravnborg 		const char *t_name;
1207a88b5ba8SSam Ravnborg 
1208a88b5ba8SSam Ravnborg 		t_name = mdesc_node_name(hp, t);
1209a88b5ba8SSam Ravnborg 		if (!strcmp(t_name, "cache")) {
1210a88b5ba8SSam Ravnborg 			fill_in_one_cache(c, hp, t);
1211a88b5ba8SSam Ravnborg 			continue;
1212a88b5ba8SSam Ravnborg 		}
1213a88b5ba8SSam Ravnborg 
1214a88b5ba8SSam Ravnborg 		mdesc_for_each_arc(j, hp, t, MDESC_ARC_TYPE_FWD) {
1215a88b5ba8SSam Ravnborg 			u64 n = mdesc_arc_target(hp, j);
1216a88b5ba8SSam Ravnborg 			const char *n_name;
1217a88b5ba8SSam Ravnborg 
1218a88b5ba8SSam Ravnborg 			n_name = mdesc_node_name(hp, n);
1219a88b5ba8SSam Ravnborg 			if (!strcmp(n_name, "cache"))
1220a88b5ba8SSam Ravnborg 				fill_in_one_cache(c, hp, n);
1221a88b5ba8SSam Ravnborg 		}
1222a88b5ba8SSam Ravnborg 	}
1223a88b5ba8SSam Ravnborg 
1224a88b5ba8SSam Ravnborg 	c->core_id = 0;
1225a88b5ba8SSam Ravnborg 	c->proc_id = -1;
12265052f525SDavid S. Miller 
12275052f525SDavid S. Miller 	return NULL;
1228a88b5ba8SSam Ravnborg }
1229a88b5ba8SSam Ravnborg 
mdesc_fill_in_cpu_data(cpumask_t * mask)12302066aaddSPaul Gortmaker void mdesc_fill_in_cpu_data(cpumask_t *mask)
12315052f525SDavid S. Miller {
12325052f525SDavid S. Miller 	struct mdesc_handle *hp;
12335052f525SDavid S. Miller 
1234a2094502SDavid S. Miller 	mdesc_iterate_over_cpus(fill_in_one_cpu, NULL, mask);
12355052f525SDavid S. Miller 
12365052f525SDavid S. Miller 	hp = mdesc_grab();
12375052f525SDavid S. Miller 
1238a88b5ba8SSam Ravnborg 	set_core_ids(hp);
1239a88b5ba8SSam Ravnborg 	set_proc_ids(hp);
1240acc455cfSchris hyser 	set_sock_ids(hp);
1241a88b5ba8SSam Ravnborg 
1242a88b5ba8SSam Ravnborg 	mdesc_release(hp);
12435052f525SDavid S. Miller 
12445052f525SDavid S. Miller 	smp_fill_in_sib_core_maps();
1245a88b5ba8SSam Ravnborg }
1246a88b5ba8SSam Ravnborg 
124707d66921SKhalid Aziz /* mdesc_open() - Grab a reference to mdesc_handle when /dev/mdesc is
124807d66921SKhalid Aziz  * opened. Hold this reference until /dev/mdesc is closed to ensure
124907d66921SKhalid Aziz  * mdesc data structure is not released underneath us. Store the
125007d66921SKhalid Aziz  * pointer to mdesc structure in private_data for read and seek to use
125107d66921SKhalid Aziz  */
mdesc_open(struct inode * inode,struct file * file)125207d66921SKhalid Aziz static int mdesc_open(struct inode *inode, struct file *file)
1253a88b5ba8SSam Ravnborg {
1254a88b5ba8SSam Ravnborg 	struct mdesc_handle *hp = mdesc_grab();
1255a88b5ba8SSam Ravnborg 
1256a88b5ba8SSam Ravnborg 	if (!hp)
1257a88b5ba8SSam Ravnborg 		return -ENODEV;
1258a88b5ba8SSam Ravnborg 
125907d66921SKhalid Aziz 	file->private_data = hp;
1260a88b5ba8SSam Ravnborg 
126107d66921SKhalid Aziz 	return 0;
126207d66921SKhalid Aziz }
126307d66921SKhalid Aziz 
mdesc_read(struct file * file,char __user * buf,size_t len,loff_t * offp)126407d66921SKhalid Aziz static ssize_t mdesc_read(struct file *file, char __user *buf,
126507d66921SKhalid Aziz 			  size_t len, loff_t *offp)
126607d66921SKhalid Aziz {
126707d66921SKhalid Aziz 	struct mdesc_handle *hp = file->private_data;
126807d66921SKhalid Aziz 	unsigned char *mdesc;
126907d66921SKhalid Aziz 	int bytes_left, count = len;
127007d66921SKhalid Aziz 
127107d66921SKhalid Aziz 	if (*offp >= hp->handle_size)
127207d66921SKhalid Aziz 		return 0;
127307d66921SKhalid Aziz 
127407d66921SKhalid Aziz 	bytes_left = hp->handle_size - *offp;
127507d66921SKhalid Aziz 	if (count > bytes_left)
127607d66921SKhalid Aziz 		count = bytes_left;
127707d66921SKhalid Aziz 
127807d66921SKhalid Aziz 	mdesc = (unsigned char *)&hp->mdesc;
127907d66921SKhalid Aziz 	mdesc += *offp;
128007d66921SKhalid Aziz 	if (!copy_to_user(buf, mdesc, count)) {
128107d66921SKhalid Aziz 		*offp += count;
128207d66921SKhalid Aziz 		return count;
128307d66921SKhalid Aziz 	} else {
128407d66921SKhalid Aziz 		return -EFAULT;
128507d66921SKhalid Aziz 	}
128607d66921SKhalid Aziz }
128707d66921SKhalid Aziz 
mdesc_llseek(struct file * file,loff_t offset,int whence)128807d66921SKhalid Aziz static loff_t mdesc_llseek(struct file *file, loff_t offset, int whence)
128907d66921SKhalid Aziz {
1290b25472f9SAl Viro 	struct mdesc_handle *hp = file->private_data;
129107d66921SKhalid Aziz 
1292b25472f9SAl Viro 	return no_seek_end_llseek_size(file, offset, whence, hp->handle_size);
129307d66921SKhalid Aziz }
129407d66921SKhalid Aziz 
129507d66921SKhalid Aziz /* mdesc_close() - /dev/mdesc is being closed, release the reference to
129607d66921SKhalid Aziz  * mdesc structure.
129707d66921SKhalid Aziz  */
mdesc_close(struct inode * inode,struct file * file)129807d66921SKhalid Aziz static int mdesc_close(struct inode *inode, struct file *file)
129907d66921SKhalid Aziz {
130007d66921SKhalid Aziz 	mdesc_release(file->private_data);
130107d66921SKhalid Aziz 	return 0;
1302a88b5ba8SSam Ravnborg }
1303a88b5ba8SSam Ravnborg 
1304a88b5ba8SSam Ravnborg static const struct file_operations mdesc_fops = {
130507d66921SKhalid Aziz 	.open    = mdesc_open,
1306a88b5ba8SSam Ravnborg 	.read	 = mdesc_read,
130707d66921SKhalid Aziz 	.llseek  = mdesc_llseek,
130807d66921SKhalid Aziz 	.release = mdesc_close,
1309a88b5ba8SSam Ravnborg 	.owner	 = THIS_MODULE,
1310a88b5ba8SSam Ravnborg };
1311a88b5ba8SSam Ravnborg 
1312a88b5ba8SSam Ravnborg static struct miscdevice mdesc_misc = {
1313a88b5ba8SSam Ravnborg 	.minor	= MISC_DYNAMIC_MINOR,
1314a88b5ba8SSam Ravnborg 	.name	= "mdesc",
1315a88b5ba8SSam Ravnborg 	.fops	= &mdesc_fops,
1316a88b5ba8SSam Ravnborg };
1317a88b5ba8SSam Ravnborg 
mdesc_misc_init(void)1318a88b5ba8SSam Ravnborg static int __init mdesc_misc_init(void)
1319a88b5ba8SSam Ravnborg {
1320a88b5ba8SSam Ravnborg 	return misc_register(&mdesc_misc);
1321a88b5ba8SSam Ravnborg }
1322a88b5ba8SSam Ravnborg 
1323a88b5ba8SSam Ravnborg __initcall(mdesc_misc_init);
1324a88b5ba8SSam Ravnborg 
sun4v_mdesc_init(void)1325a88b5ba8SSam Ravnborg void __init sun4v_mdesc_init(void)
1326a88b5ba8SSam Ravnborg {
1327a88b5ba8SSam Ravnborg 	struct mdesc_handle *hp;
1328a88b5ba8SSam Ravnborg 	unsigned long len, real_len, status;
1329a88b5ba8SSam Ravnborg 
1330a88b5ba8SSam Ravnborg 	(void) sun4v_mach_desc(0UL, 0UL, &len);
1331a88b5ba8SSam Ravnborg 
1332a88b5ba8SSam Ravnborg 	printk("MDESC: Size is %lu bytes.\n", len);
1333a88b5ba8SSam Ravnborg 
133495f72d1eSYinghai Lu 	hp = mdesc_alloc(len, &memblock_mdesc_ops);
1335a88b5ba8SSam Ravnborg 	if (hp == NULL) {
1336a88b5ba8SSam Ravnborg 		prom_printf("MDESC: alloc of %lu bytes failed.\n", len);
1337a88b5ba8SSam Ravnborg 		prom_halt();
1338a88b5ba8SSam Ravnborg 	}
1339a88b5ba8SSam Ravnborg 
1340a88b5ba8SSam Ravnborg 	status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len);
1341a88b5ba8SSam Ravnborg 	if (status != HV_EOK || real_len > len) {
1342a88b5ba8SSam Ravnborg 		prom_printf("sun4v_mach_desc fails, err(%lu), "
1343a88b5ba8SSam Ravnborg 			    "len(%lu), real_len(%lu)\n",
1344a88b5ba8SSam Ravnborg 			    status, len, real_len);
1345a88b5ba8SSam Ravnborg 		mdesc_free(hp);
1346a88b5ba8SSam Ravnborg 		prom_halt();
1347a88b5ba8SSam Ravnborg 	}
1348a88b5ba8SSam Ravnborg 
1349a88b5ba8SSam Ravnborg 	cur_mdesc = hp;
1350a88b5ba8SSam Ravnborg 
1351c6202ca7SKhalid Aziz 	mdesc_adi_init();
1352a88b5ba8SSam Ravnborg 	report_platform_properties();
1353a88b5ba8SSam Ravnborg }
1354