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