diff options
Diffstat (limited to 'arch/sparc/kernel/mdesc.c')
-rw-r--r-- | arch/sparc/kernel/mdesc.c | 916 |
1 files changed, 916 insertions, 0 deletions
diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c new file mode 100644 index 000000000000..dde52bcf5c64 --- /dev/null +++ b/arch/sparc/kernel/mdesc.c | |||
@@ -0,0 +1,916 @@ | |||
1 | /* mdesc.c: Sun4V machine description handling. | ||
2 | * | ||
3 | * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> | ||
4 | */ | ||
5 | #include <linux/kernel.h> | ||
6 | #include <linux/types.h> | ||
7 | #include <linux/lmb.h> | ||
8 | #include <linux/log2.h> | ||
9 | #include <linux/list.h> | ||
10 | #include <linux/slab.h> | ||
11 | #include <linux/mm.h> | ||
12 | #include <linux/miscdevice.h> | ||
13 | |||
14 | #include <asm/hypervisor.h> | ||
15 | #include <asm/mdesc.h> | ||
16 | #include <asm/prom.h> | ||
17 | #include <asm/oplib.h> | ||
18 | #include <asm/smp.h> | ||
19 | |||
20 | /* Unlike the OBP device tree, the machine description is a full-on | ||
21 | * DAG. An arbitrary number of ARCs are possible from one | ||
22 | * node to other nodes and thus we can't use the OBP device_node | ||
23 | * data structure to represent these nodes inside of the kernel. | ||
24 | * | ||
25 | * Actually, it isn't even a DAG, because there are back pointers | ||
26 | * which create cycles in the graph. | ||
27 | * | ||
28 | * mdesc_hdr and mdesc_elem describe the layout of the data structure | ||
29 | * we get from the Hypervisor. | ||
30 | */ | ||
31 | struct mdesc_hdr { | ||
32 | u32 version; /* Transport version */ | ||
33 | u32 node_sz; /* node block size */ | ||
34 | u32 name_sz; /* name block size */ | ||
35 | u32 data_sz; /* data block size */ | ||
36 | } __attribute__((aligned(16))); | ||
37 | |||
38 | struct mdesc_elem { | ||
39 | u8 tag; | ||
40 | #define MD_LIST_END 0x00 | ||
41 | #define MD_NODE 0x4e | ||
42 | #define MD_NODE_END 0x45 | ||
43 | #define MD_NOOP 0x20 | ||
44 | #define MD_PROP_ARC 0x61 | ||
45 | #define MD_PROP_VAL 0x76 | ||
46 | #define MD_PROP_STR 0x73 | ||
47 | #define MD_PROP_DATA 0x64 | ||
48 | u8 name_len; | ||
49 | u16 resv; | ||
50 | u32 name_offset; | ||
51 | union { | ||
52 | struct { | ||
53 | u32 data_len; | ||
54 | u32 data_offset; | ||
55 | } data; | ||
56 | u64 val; | ||
57 | } d; | ||
58 | }; | ||
59 | |||
60 | struct mdesc_mem_ops { | ||
61 | struct mdesc_handle *(*alloc)(unsigned int mdesc_size); | ||
62 | void (*free)(struct mdesc_handle *handle); | ||
63 | }; | ||
64 | |||
65 | struct mdesc_handle { | ||
66 | struct list_head list; | ||
67 | struct mdesc_mem_ops *mops; | ||
68 | void *self_base; | ||
69 | atomic_t refcnt; | ||
70 | unsigned int handle_size; | ||
71 | struct mdesc_hdr mdesc; | ||
72 | }; | ||
73 | |||
74 | static void mdesc_handle_init(struct mdesc_handle *hp, | ||
75 | unsigned int handle_size, | ||
76 | void *base) | ||
77 | { | ||
78 | BUG_ON(((unsigned long)&hp->mdesc) & (16UL - 1)); | ||
79 | |||
80 | memset(hp, 0, handle_size); | ||
81 | INIT_LIST_HEAD(&hp->list); | ||
82 | hp->self_base = base; | ||
83 | atomic_set(&hp->refcnt, 1); | ||
84 | hp->handle_size = handle_size; | ||
85 | } | ||
86 | |||
87 | static struct mdesc_handle * __init mdesc_lmb_alloc(unsigned int mdesc_size) | ||
88 | { | ||
89 | unsigned int handle_size, alloc_size; | ||
90 | struct mdesc_handle *hp; | ||
91 | unsigned long paddr; | ||
92 | |||
93 | handle_size = (sizeof(struct mdesc_handle) - | ||
94 | sizeof(struct mdesc_hdr) + | ||
95 | mdesc_size); | ||
96 | alloc_size = PAGE_ALIGN(handle_size); | ||
97 | |||
98 | paddr = lmb_alloc(alloc_size, PAGE_SIZE); | ||
99 | |||
100 | hp = NULL; | ||
101 | if (paddr) { | ||
102 | hp = __va(paddr); | ||
103 | mdesc_handle_init(hp, handle_size, hp); | ||
104 | } | ||
105 | return hp; | ||
106 | } | ||
107 | |||
108 | static void mdesc_lmb_free(struct mdesc_handle *hp) | ||
109 | { | ||
110 | unsigned int alloc_size, handle_size = hp->handle_size; | ||
111 | unsigned long start, end; | ||
112 | |||
113 | BUG_ON(atomic_read(&hp->refcnt) != 0); | ||
114 | BUG_ON(!list_empty(&hp->list)); | ||
115 | |||
116 | alloc_size = PAGE_ALIGN(handle_size); | ||
117 | |||
118 | start = (unsigned long) hp; | ||
119 | end = start + alloc_size; | ||
120 | |||
121 | while (start < end) { | ||
122 | struct page *p; | ||
123 | |||
124 | p = virt_to_page(start); | ||
125 | ClearPageReserved(p); | ||
126 | __free_page(p); | ||
127 | start += PAGE_SIZE; | ||
128 | } | ||
129 | } | ||
130 | |||
131 | static struct mdesc_mem_ops lmb_mdesc_ops = { | ||
132 | .alloc = mdesc_lmb_alloc, | ||
133 | .free = mdesc_lmb_free, | ||
134 | }; | ||
135 | |||
136 | static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size) | ||
137 | { | ||
138 | unsigned int handle_size; | ||
139 | void *base; | ||
140 | |||
141 | handle_size = (sizeof(struct mdesc_handle) - | ||
142 | sizeof(struct mdesc_hdr) + | ||
143 | mdesc_size); | ||
144 | |||
145 | base = kmalloc(handle_size + 15, GFP_KERNEL | __GFP_NOFAIL); | ||
146 | if (base) { | ||
147 | struct mdesc_handle *hp; | ||
148 | unsigned long addr; | ||
149 | |||
150 | addr = (unsigned long)base; | ||
151 | addr = (addr + 15UL) & ~15UL; | ||
152 | hp = (struct mdesc_handle *) addr; | ||
153 | |||
154 | mdesc_handle_init(hp, handle_size, base); | ||
155 | return hp; | ||
156 | } | ||
157 | |||
158 | return NULL; | ||
159 | } | ||
160 | |||
161 | static void mdesc_kfree(struct mdesc_handle *hp) | ||
162 | { | ||
163 | BUG_ON(atomic_read(&hp->refcnt) != 0); | ||
164 | BUG_ON(!list_empty(&hp->list)); | ||
165 | |||
166 | kfree(hp->self_base); | ||
167 | } | ||
168 | |||
169 | static struct mdesc_mem_ops kmalloc_mdesc_memops = { | ||
170 | .alloc = mdesc_kmalloc, | ||
171 | .free = mdesc_kfree, | ||
172 | }; | ||
173 | |||
174 | static struct mdesc_handle *mdesc_alloc(unsigned int mdesc_size, | ||
175 | struct mdesc_mem_ops *mops) | ||
176 | { | ||
177 | struct mdesc_handle *hp = mops->alloc(mdesc_size); | ||
178 | |||
179 | if (hp) | ||
180 | hp->mops = mops; | ||
181 | |||
182 | return hp; | ||
183 | } | ||
184 | |||
185 | static void mdesc_free(struct mdesc_handle *hp) | ||
186 | { | ||
187 | hp->mops->free(hp); | ||
188 | } | ||
189 | |||
190 | static struct mdesc_handle *cur_mdesc; | ||
191 | static LIST_HEAD(mdesc_zombie_list); | ||
192 | static DEFINE_SPINLOCK(mdesc_lock); | ||
193 | |||
194 | struct mdesc_handle *mdesc_grab(void) | ||
195 | { | ||
196 | struct mdesc_handle *hp; | ||
197 | unsigned long flags; | ||
198 | |||
199 | spin_lock_irqsave(&mdesc_lock, flags); | ||
200 | hp = cur_mdesc; | ||
201 | if (hp) | ||
202 | atomic_inc(&hp->refcnt); | ||
203 | spin_unlock_irqrestore(&mdesc_lock, flags); | ||
204 | |||
205 | return hp; | ||
206 | } | ||
207 | EXPORT_SYMBOL(mdesc_grab); | ||
208 | |||
209 | void mdesc_release(struct mdesc_handle *hp) | ||
210 | { | ||
211 | unsigned long flags; | ||
212 | |||
213 | spin_lock_irqsave(&mdesc_lock, flags); | ||
214 | if (atomic_dec_and_test(&hp->refcnt)) { | ||
215 | list_del_init(&hp->list); | ||
216 | hp->mops->free(hp); | ||
217 | } | ||
218 | spin_unlock_irqrestore(&mdesc_lock, flags); | ||
219 | } | ||
220 | EXPORT_SYMBOL(mdesc_release); | ||
221 | |||
222 | static DEFINE_MUTEX(mdesc_mutex); | ||
223 | static struct mdesc_notifier_client *client_list; | ||
224 | |||
225 | void mdesc_register_notifier(struct mdesc_notifier_client *client) | ||
226 | { | ||
227 | u64 node; | ||
228 | |||
229 | mutex_lock(&mdesc_mutex); | ||
230 | client->next = client_list; | ||
231 | client_list = client; | ||
232 | |||
233 | mdesc_for_each_node_by_name(cur_mdesc, node, client->node_name) | ||
234 | client->add(cur_mdesc, node); | ||
235 | |||
236 | mutex_unlock(&mdesc_mutex); | ||
237 | } | ||
238 | |||
239 | static const u64 *parent_cfg_handle(struct mdesc_handle *hp, u64 node) | ||
240 | { | ||
241 | const u64 *id; | ||
242 | u64 a; | ||
243 | |||
244 | id = NULL; | ||
245 | mdesc_for_each_arc(a, hp, node, MDESC_ARC_TYPE_BACK) { | ||
246 | u64 target; | ||
247 | |||
248 | target = mdesc_arc_target(hp, a); | ||
249 | id = mdesc_get_property(hp, target, | ||
250 | "cfg-handle", NULL); | ||
251 | if (id) | ||
252 | break; | ||
253 | } | ||
254 | |||
255 | return id; | ||
256 | } | ||
257 | |||
258 | /* Run 'func' on nodes which are in A but not in B. */ | ||
259 | static void invoke_on_missing(const char *name, | ||
260 | struct mdesc_handle *a, | ||
261 | struct mdesc_handle *b, | ||
262 | void (*func)(struct mdesc_handle *, u64)) | ||
263 | { | ||
264 | u64 node; | ||
265 | |||
266 | mdesc_for_each_node_by_name(a, node, name) { | ||
267 | int found = 0, is_vdc_port = 0; | ||
268 | const char *name_prop; | ||
269 | const u64 *id; | ||
270 | u64 fnode; | ||
271 | |||
272 | name_prop = mdesc_get_property(a, node, "name", NULL); | ||
273 | if (name_prop && !strcmp(name_prop, "vdc-port")) { | ||
274 | is_vdc_port = 1; | ||
275 | id = parent_cfg_handle(a, node); | ||
276 | } else | ||
277 | id = mdesc_get_property(a, node, "id", NULL); | ||
278 | |||
279 | if (!id) { | ||
280 | printk(KERN_ERR "MD: Cannot find ID for %s node.\n", | ||
281 | (name_prop ? name_prop : name)); | ||
282 | continue; | ||
283 | } | ||
284 | |||
285 | mdesc_for_each_node_by_name(b, fnode, name) { | ||
286 | const u64 *fid; | ||
287 | |||
288 | if (is_vdc_port) { | ||
289 | name_prop = mdesc_get_property(b, fnode, | ||
290 | "name", NULL); | ||
291 | if (!name_prop || | ||
292 | strcmp(name_prop, "vdc-port")) | ||
293 | continue; | ||
294 | fid = parent_cfg_handle(b, fnode); | ||
295 | if (!fid) { | ||
296 | printk(KERN_ERR "MD: Cannot find ID " | ||
297 | "for vdc-port node.\n"); | ||
298 | continue; | ||
299 | } | ||
300 | } else | ||
301 | fid = mdesc_get_property(b, fnode, | ||
302 | "id", NULL); | ||
303 | |||
304 | if (*id == *fid) { | ||
305 | found = 1; | ||
306 | break; | ||
307 | } | ||
308 | } | ||
309 | if (!found) | ||
310 | func(a, node); | ||
311 | } | ||
312 | } | ||
313 | |||
314 | static void notify_one(struct mdesc_notifier_client *p, | ||
315 | struct mdesc_handle *old_hp, | ||
316 | struct mdesc_handle *new_hp) | ||
317 | { | ||
318 | invoke_on_missing(p->node_name, old_hp, new_hp, p->remove); | ||
319 | invoke_on_missing(p->node_name, new_hp, old_hp, p->add); | ||
320 | } | ||
321 | |||
322 | static void mdesc_notify_clients(struct mdesc_handle *old_hp, | ||
323 | struct mdesc_handle *new_hp) | ||
324 | { | ||
325 | struct mdesc_notifier_client *p = client_list; | ||
326 | |||
327 | while (p) { | ||
328 | notify_one(p, old_hp, new_hp); | ||
329 | p = p->next; | ||
330 | } | ||
331 | } | ||
332 | |||
333 | void mdesc_update(void) | ||
334 | { | ||
335 | unsigned long len, real_len, status; | ||
336 | struct mdesc_handle *hp, *orig_hp; | ||
337 | unsigned long flags; | ||
338 | |||
339 | mutex_lock(&mdesc_mutex); | ||
340 | |||
341 | (void) sun4v_mach_desc(0UL, 0UL, &len); | ||
342 | |||
343 | hp = mdesc_alloc(len, &kmalloc_mdesc_memops); | ||
344 | if (!hp) { | ||
345 | printk(KERN_ERR "MD: mdesc alloc fails\n"); | ||
346 | goto out; | ||
347 | } | ||
348 | |||
349 | status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len); | ||
350 | if (status != HV_EOK || real_len > len) { | ||
351 | printk(KERN_ERR "MD: mdesc reread fails with %lu\n", | ||
352 | status); | ||
353 | atomic_dec(&hp->refcnt); | ||
354 | mdesc_free(hp); | ||
355 | goto out; | ||
356 | } | ||
357 | |||
358 | spin_lock_irqsave(&mdesc_lock, flags); | ||
359 | orig_hp = cur_mdesc; | ||
360 | cur_mdesc = hp; | ||
361 | spin_unlock_irqrestore(&mdesc_lock, flags); | ||
362 | |||
363 | mdesc_notify_clients(orig_hp, hp); | ||
364 | |||
365 | spin_lock_irqsave(&mdesc_lock, flags); | ||
366 | if (atomic_dec_and_test(&orig_hp->refcnt)) | ||
367 | mdesc_free(orig_hp); | ||
368 | else | ||
369 | list_add(&orig_hp->list, &mdesc_zombie_list); | ||
370 | spin_unlock_irqrestore(&mdesc_lock, flags); | ||
371 | |||
372 | out: | ||
373 | mutex_unlock(&mdesc_mutex); | ||
374 | } | ||
375 | |||
376 | static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc) | ||
377 | { | ||
378 | return (struct mdesc_elem *) (mdesc + 1); | ||
379 | } | ||
380 | |||
381 | static void *name_block(struct mdesc_hdr *mdesc) | ||
382 | { | ||
383 | return ((void *) node_block(mdesc)) + mdesc->node_sz; | ||
384 | } | ||
385 | |||
386 | static void *data_block(struct mdesc_hdr *mdesc) | ||
387 | { | ||
388 | return ((void *) name_block(mdesc)) + mdesc->name_sz; | ||
389 | } | ||
390 | |||
391 | u64 mdesc_node_by_name(struct mdesc_handle *hp, | ||
392 | u64 from_node, const char *name) | ||
393 | { | ||
394 | struct mdesc_elem *ep = node_block(&hp->mdesc); | ||
395 | const char *names = name_block(&hp->mdesc); | ||
396 | u64 last_node = hp->mdesc.node_sz / 16; | ||
397 | u64 ret; | ||
398 | |||
399 | if (from_node == MDESC_NODE_NULL) { | ||
400 | ret = from_node = 0; | ||
401 | } else if (from_node >= last_node) { | ||
402 | return MDESC_NODE_NULL; | ||
403 | } else { | ||
404 | ret = ep[from_node].d.val; | ||
405 | } | ||
406 | |||
407 | while (ret < last_node) { | ||
408 | if (ep[ret].tag != MD_NODE) | ||
409 | return MDESC_NODE_NULL; | ||
410 | if (!strcmp(names + ep[ret].name_offset, name)) | ||
411 | break; | ||
412 | ret = ep[ret].d.val; | ||
413 | } | ||
414 | if (ret >= last_node) | ||
415 | ret = MDESC_NODE_NULL; | ||
416 | return ret; | ||
417 | } | ||
418 | EXPORT_SYMBOL(mdesc_node_by_name); | ||
419 | |||
420 | const void *mdesc_get_property(struct mdesc_handle *hp, u64 node, | ||
421 | const char *name, int *lenp) | ||
422 | { | ||
423 | const char *names = name_block(&hp->mdesc); | ||
424 | u64 last_node = hp->mdesc.node_sz / 16; | ||
425 | void *data = data_block(&hp->mdesc); | ||
426 | struct mdesc_elem *ep; | ||
427 | |||
428 | if (node == MDESC_NODE_NULL || node >= last_node) | ||
429 | return NULL; | ||
430 | |||
431 | ep = node_block(&hp->mdesc) + node; | ||
432 | ep++; | ||
433 | for (; ep->tag != MD_NODE_END; ep++) { | ||
434 | void *val = NULL; | ||
435 | int len = 0; | ||
436 | |||
437 | switch (ep->tag) { | ||
438 | case MD_PROP_VAL: | ||
439 | val = &ep->d.val; | ||
440 | len = 8; | ||
441 | break; | ||
442 | |||
443 | case MD_PROP_STR: | ||
444 | case MD_PROP_DATA: | ||
445 | val = data + ep->d.data.data_offset; | ||
446 | len = ep->d.data.data_len; | ||
447 | break; | ||
448 | |||
449 | default: | ||
450 | break; | ||
451 | } | ||
452 | if (!val) | ||
453 | continue; | ||
454 | |||
455 | if (!strcmp(names + ep->name_offset, name)) { | ||
456 | if (lenp) | ||
457 | *lenp = len; | ||
458 | return val; | ||
459 | } | ||
460 | } | ||
461 | |||
462 | return NULL; | ||
463 | } | ||
464 | EXPORT_SYMBOL(mdesc_get_property); | ||
465 | |||
466 | u64 mdesc_next_arc(struct mdesc_handle *hp, u64 from, const char *arc_type) | ||
467 | { | ||
468 | struct mdesc_elem *ep, *base = node_block(&hp->mdesc); | ||
469 | const char *names = name_block(&hp->mdesc); | ||
470 | u64 last_node = hp->mdesc.node_sz / 16; | ||
471 | |||
472 | if (from == MDESC_NODE_NULL || from >= last_node) | ||
473 | return MDESC_NODE_NULL; | ||
474 | |||
475 | ep = base + from; | ||
476 | |||
477 | ep++; | ||
478 | for (; ep->tag != MD_NODE_END; ep++) { | ||
479 | if (ep->tag != MD_PROP_ARC) | ||
480 | continue; | ||
481 | |||
482 | if (strcmp(names + ep->name_offset, arc_type)) | ||
483 | continue; | ||
484 | |||
485 | return ep - base; | ||
486 | } | ||
487 | |||
488 | return MDESC_NODE_NULL; | ||
489 | } | ||
490 | EXPORT_SYMBOL(mdesc_next_arc); | ||
491 | |||
492 | u64 mdesc_arc_target(struct mdesc_handle *hp, u64 arc) | ||
493 | { | ||
494 | struct mdesc_elem *ep, *base = node_block(&hp->mdesc); | ||
495 | |||
496 | ep = base + arc; | ||
497 | |||
498 | return ep->d.val; | ||
499 | } | ||
500 | EXPORT_SYMBOL(mdesc_arc_target); | ||
501 | |||
502 | const char *mdesc_node_name(struct mdesc_handle *hp, u64 node) | ||
503 | { | ||
504 | struct mdesc_elem *ep, *base = node_block(&hp->mdesc); | ||
505 | const char *names = name_block(&hp->mdesc); | ||
506 | u64 last_node = hp->mdesc.node_sz / 16; | ||
507 | |||
508 | if (node == MDESC_NODE_NULL || node >= last_node) | ||
509 | return NULL; | ||
510 | |||
511 | ep = base + node; | ||
512 | if (ep->tag != MD_NODE) | ||
513 | return NULL; | ||
514 | |||
515 | return names + ep->name_offset; | ||
516 | } | ||
517 | EXPORT_SYMBOL(mdesc_node_name); | ||
518 | |||
519 | static void __init report_platform_properties(void) | ||
520 | { | ||
521 | struct mdesc_handle *hp = mdesc_grab(); | ||
522 | u64 pn = mdesc_node_by_name(hp, MDESC_NODE_NULL, "platform"); | ||
523 | const char *s; | ||
524 | const u64 *v; | ||
525 | |||
526 | if (pn == MDESC_NODE_NULL) { | ||
527 | prom_printf("No platform node in machine-description.\n"); | ||
528 | prom_halt(); | ||
529 | } | ||
530 | |||
531 | s = mdesc_get_property(hp, pn, "banner-name", NULL); | ||
532 | printk("PLATFORM: banner-name [%s]\n", s); | ||
533 | s = mdesc_get_property(hp, pn, "name", NULL); | ||
534 | printk("PLATFORM: name [%s]\n", s); | ||
535 | |||
536 | v = mdesc_get_property(hp, pn, "hostid", NULL); | ||
537 | if (v) | ||
538 | printk("PLATFORM: hostid [%08lx]\n", *v); | ||
539 | v = mdesc_get_property(hp, pn, "serial#", NULL); | ||
540 | if (v) | ||
541 | printk("PLATFORM: serial# [%08lx]\n", *v); | ||
542 | v = mdesc_get_property(hp, pn, "stick-frequency", NULL); | ||
543 | printk("PLATFORM: stick-frequency [%08lx]\n", *v); | ||
544 | v = mdesc_get_property(hp, pn, "mac-address", NULL); | ||
545 | if (v) | ||
546 | printk("PLATFORM: mac-address [%lx]\n", *v); | ||
547 | v = mdesc_get_property(hp, pn, "watchdog-resolution", NULL); | ||
548 | if (v) | ||
549 | printk("PLATFORM: watchdog-resolution [%lu ms]\n", *v); | ||
550 | v = mdesc_get_property(hp, pn, "watchdog-max-timeout", NULL); | ||
551 | if (v) | ||
552 | printk("PLATFORM: watchdog-max-timeout [%lu ms]\n", *v); | ||
553 | v = mdesc_get_property(hp, pn, "max-cpus", NULL); | ||
554 | if (v) | ||
555 | printk("PLATFORM: max-cpus [%lu]\n", *v); | ||
556 | |||
557 | #ifdef CONFIG_SMP | ||
558 | { | ||
559 | int max_cpu, i; | ||
560 | |||
561 | if (v) { | ||
562 | max_cpu = *v; | ||
563 | if (max_cpu > NR_CPUS) | ||
564 | max_cpu = NR_CPUS; | ||
565 | } else { | ||
566 | max_cpu = NR_CPUS; | ||
567 | } | ||
568 | for (i = 0; i < max_cpu; i++) | ||
569 | cpu_set(i, cpu_possible_map); | ||
570 | } | ||
571 | #endif | ||
572 | |||
573 | mdesc_release(hp); | ||
574 | } | ||
575 | |||
576 | static void __devinit fill_in_one_cache(cpuinfo_sparc *c, | ||
577 | struct mdesc_handle *hp, | ||
578 | u64 mp) | ||
579 | { | ||
580 | const u64 *level = mdesc_get_property(hp, mp, "level", NULL); | ||
581 | const u64 *size = mdesc_get_property(hp, mp, "size", NULL); | ||
582 | const u64 *line_size = mdesc_get_property(hp, mp, "line-size", NULL); | ||
583 | const char *type; | ||
584 | int type_len; | ||
585 | |||
586 | type = mdesc_get_property(hp, mp, "type", &type_len); | ||
587 | |||
588 | switch (*level) { | ||
589 | case 1: | ||
590 | if (of_find_in_proplist(type, "instn", type_len)) { | ||
591 | c->icache_size = *size; | ||
592 | c->icache_line_size = *line_size; | ||
593 | } else if (of_find_in_proplist(type, "data", type_len)) { | ||
594 | c->dcache_size = *size; | ||
595 | c->dcache_line_size = *line_size; | ||
596 | } | ||
597 | break; | ||
598 | |||
599 | case 2: | ||
600 | c->ecache_size = *size; | ||
601 | c->ecache_line_size = *line_size; | ||
602 | break; | ||
603 | |||
604 | default: | ||
605 | break; | ||
606 | } | ||
607 | |||
608 | if (*level == 1) { | ||
609 | u64 a; | ||
610 | |||
611 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) { | ||
612 | u64 target = mdesc_arc_target(hp, a); | ||
613 | const char *name = mdesc_node_name(hp, target); | ||
614 | |||
615 | if (!strcmp(name, "cache")) | ||
616 | fill_in_one_cache(c, hp, target); | ||
617 | } | ||
618 | } | ||
619 | } | ||
620 | |||
621 | static void __devinit mark_core_ids(struct mdesc_handle *hp, u64 mp, | ||
622 | int core_id) | ||
623 | { | ||
624 | u64 a; | ||
625 | |||
626 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) { | ||
627 | u64 t = mdesc_arc_target(hp, a); | ||
628 | const char *name; | ||
629 | const u64 *id; | ||
630 | |||
631 | name = mdesc_node_name(hp, t); | ||
632 | if (!strcmp(name, "cpu")) { | ||
633 | id = mdesc_get_property(hp, t, "id", NULL); | ||
634 | if (*id < NR_CPUS) | ||
635 | cpu_data(*id).core_id = core_id; | ||
636 | } else { | ||
637 | u64 j; | ||
638 | |||
639 | mdesc_for_each_arc(j, hp, t, MDESC_ARC_TYPE_BACK) { | ||
640 | u64 n = mdesc_arc_target(hp, j); | ||
641 | const char *n_name; | ||
642 | |||
643 | n_name = mdesc_node_name(hp, n); | ||
644 | if (strcmp(n_name, "cpu")) | ||
645 | continue; | ||
646 | |||
647 | id = mdesc_get_property(hp, n, "id", NULL); | ||
648 | if (*id < NR_CPUS) | ||
649 | cpu_data(*id).core_id = core_id; | ||
650 | } | ||
651 | } | ||
652 | } | ||
653 | } | ||
654 | |||
655 | static void __devinit set_core_ids(struct mdesc_handle *hp) | ||
656 | { | ||
657 | int idx; | ||
658 | u64 mp; | ||
659 | |||
660 | idx = 1; | ||
661 | mdesc_for_each_node_by_name(hp, mp, "cache") { | ||
662 | const u64 *level; | ||
663 | const char *type; | ||
664 | int len; | ||
665 | |||
666 | level = mdesc_get_property(hp, mp, "level", NULL); | ||
667 | if (*level != 1) | ||
668 | continue; | ||
669 | |||
670 | type = mdesc_get_property(hp, mp, "type", &len); | ||
671 | if (!of_find_in_proplist(type, "instn", len)) | ||
672 | continue; | ||
673 | |||
674 | mark_core_ids(hp, mp, idx); | ||
675 | |||
676 | idx++; | ||
677 | } | ||
678 | } | ||
679 | |||
680 | static void __devinit mark_proc_ids(struct mdesc_handle *hp, u64 mp, | ||
681 | int proc_id) | ||
682 | { | ||
683 | u64 a; | ||
684 | |||
685 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) { | ||
686 | u64 t = mdesc_arc_target(hp, a); | ||
687 | const char *name; | ||
688 | const u64 *id; | ||
689 | |||
690 | name = mdesc_node_name(hp, t); | ||
691 | if (strcmp(name, "cpu")) | ||
692 | continue; | ||
693 | |||
694 | id = mdesc_get_property(hp, t, "id", NULL); | ||
695 | if (*id < NR_CPUS) | ||
696 | cpu_data(*id).proc_id = proc_id; | ||
697 | } | ||
698 | } | ||
699 | |||
700 | static void __devinit __set_proc_ids(struct mdesc_handle *hp, | ||
701 | const char *exec_unit_name) | ||
702 | { | ||
703 | int idx; | ||
704 | u64 mp; | ||
705 | |||
706 | idx = 0; | ||
707 | mdesc_for_each_node_by_name(hp, mp, exec_unit_name) { | ||
708 | const char *type; | ||
709 | int len; | ||
710 | |||
711 | type = mdesc_get_property(hp, mp, "type", &len); | ||
712 | if (!of_find_in_proplist(type, "int", len) && | ||
713 | !of_find_in_proplist(type, "integer", len)) | ||
714 | continue; | ||
715 | |||
716 | mark_proc_ids(hp, mp, idx); | ||
717 | |||
718 | idx++; | ||
719 | } | ||
720 | } | ||
721 | |||
722 | static void __devinit set_proc_ids(struct mdesc_handle *hp) | ||
723 | { | ||
724 | __set_proc_ids(hp, "exec_unit"); | ||
725 | __set_proc_ids(hp, "exec-unit"); | ||
726 | } | ||
727 | |||
728 | static void __devinit get_one_mondo_bits(const u64 *p, unsigned int *mask, | ||
729 | unsigned char def) | ||
730 | { | ||
731 | u64 val; | ||
732 | |||
733 | if (!p) | ||
734 | goto use_default; | ||
735 | val = *p; | ||
736 | |||
737 | if (!val || val >= 64) | ||
738 | goto use_default; | ||
739 | |||
740 | *mask = ((1U << val) * 64U) - 1U; | ||
741 | return; | ||
742 | |||
743 | use_default: | ||
744 | *mask = ((1U << def) * 64U) - 1U; | ||
745 | } | ||
746 | |||
747 | static void __devinit get_mondo_data(struct mdesc_handle *hp, u64 mp, | ||
748 | struct trap_per_cpu *tb) | ||
749 | { | ||
750 | const u64 *val; | ||
751 | |||
752 | val = mdesc_get_property(hp, mp, "q-cpu-mondo-#bits", NULL); | ||
753 | get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7); | ||
754 | |||
755 | val = mdesc_get_property(hp, mp, "q-dev-mondo-#bits", NULL); | ||
756 | get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7); | ||
757 | |||
758 | val = mdesc_get_property(hp, mp, "q-resumable-#bits", NULL); | ||
759 | get_one_mondo_bits(val, &tb->resum_qmask, 6); | ||
760 | |||
761 | val = mdesc_get_property(hp, mp, "q-nonresumable-#bits", NULL); | ||
762 | get_one_mondo_bits(val, &tb->nonresum_qmask, 2); | ||
763 | } | ||
764 | |||
765 | void __cpuinit mdesc_fill_in_cpu_data(cpumask_t mask) | ||
766 | { | ||
767 | struct mdesc_handle *hp = mdesc_grab(); | ||
768 | u64 mp; | ||
769 | |||
770 | ncpus_probed = 0; | ||
771 | mdesc_for_each_node_by_name(hp, mp, "cpu") { | ||
772 | const u64 *id = mdesc_get_property(hp, mp, "id", NULL); | ||
773 | const u64 *cfreq = mdesc_get_property(hp, mp, "clock-frequency", NULL); | ||
774 | struct trap_per_cpu *tb; | ||
775 | cpuinfo_sparc *c; | ||
776 | int cpuid; | ||
777 | u64 a; | ||
778 | |||
779 | ncpus_probed++; | ||
780 | |||
781 | cpuid = *id; | ||
782 | |||
783 | #ifdef CONFIG_SMP | ||
784 | if (cpuid >= NR_CPUS) { | ||
785 | printk(KERN_WARNING "Ignoring CPU %d which is " | ||
786 | ">= NR_CPUS (%d)\n", | ||
787 | cpuid, NR_CPUS); | ||
788 | continue; | ||
789 | } | ||
790 | if (!cpu_isset(cpuid, mask)) | ||
791 | continue; | ||
792 | #else | ||
793 | /* On uniprocessor we only want the values for the | ||
794 | * real physical cpu the kernel booted onto, however | ||
795 | * cpu_data() only has one entry at index 0. | ||
796 | */ | ||
797 | if (cpuid != real_hard_smp_processor_id()) | ||
798 | continue; | ||
799 | cpuid = 0; | ||
800 | #endif | ||
801 | |||
802 | c = &cpu_data(cpuid); | ||
803 | c->clock_tick = *cfreq; | ||
804 | |||
805 | tb = &trap_block[cpuid]; | ||
806 | get_mondo_data(hp, mp, tb); | ||
807 | |||
808 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) { | ||
809 | u64 j, t = mdesc_arc_target(hp, a); | ||
810 | const char *t_name; | ||
811 | |||
812 | t_name = mdesc_node_name(hp, t); | ||
813 | if (!strcmp(t_name, "cache")) { | ||
814 | fill_in_one_cache(c, hp, t); | ||
815 | continue; | ||
816 | } | ||
817 | |||
818 | mdesc_for_each_arc(j, hp, t, MDESC_ARC_TYPE_FWD) { | ||
819 | u64 n = mdesc_arc_target(hp, j); | ||
820 | const char *n_name; | ||
821 | |||
822 | n_name = mdesc_node_name(hp, n); | ||
823 | if (!strcmp(n_name, "cache")) | ||
824 | fill_in_one_cache(c, hp, n); | ||
825 | } | ||
826 | } | ||
827 | |||
828 | #ifdef CONFIG_SMP | ||
829 | cpu_set(cpuid, cpu_present_map); | ||
830 | #endif | ||
831 | |||
832 | c->core_id = 0; | ||
833 | c->proc_id = -1; | ||
834 | } | ||
835 | |||
836 | #ifdef CONFIG_SMP | ||
837 | sparc64_multi_core = 1; | ||
838 | #endif | ||
839 | |||
840 | set_core_ids(hp); | ||
841 | set_proc_ids(hp); | ||
842 | |||
843 | smp_fill_in_sib_core_maps(); | ||
844 | |||
845 | mdesc_release(hp); | ||
846 | } | ||
847 | |||
848 | static ssize_t mdesc_read(struct file *file, char __user *buf, | ||
849 | size_t len, loff_t *offp) | ||
850 | { | ||
851 | struct mdesc_handle *hp = mdesc_grab(); | ||
852 | int err; | ||
853 | |||
854 | if (!hp) | ||
855 | return -ENODEV; | ||
856 | |||
857 | err = hp->handle_size; | ||
858 | if (len < hp->handle_size) | ||
859 | err = -EMSGSIZE; | ||
860 | else if (copy_to_user(buf, &hp->mdesc, hp->handle_size)) | ||
861 | err = -EFAULT; | ||
862 | mdesc_release(hp); | ||
863 | |||
864 | return err; | ||
865 | } | ||
866 | |||
867 | static const struct file_operations mdesc_fops = { | ||
868 | .read = mdesc_read, | ||
869 | .owner = THIS_MODULE, | ||
870 | }; | ||
871 | |||
872 | static struct miscdevice mdesc_misc = { | ||
873 | .minor = MISC_DYNAMIC_MINOR, | ||
874 | .name = "mdesc", | ||
875 | .fops = &mdesc_fops, | ||
876 | }; | ||
877 | |||
878 | static int __init mdesc_misc_init(void) | ||
879 | { | ||
880 | return misc_register(&mdesc_misc); | ||
881 | } | ||
882 | |||
883 | __initcall(mdesc_misc_init); | ||
884 | |||
885 | void __init sun4v_mdesc_init(void) | ||
886 | { | ||
887 | struct mdesc_handle *hp; | ||
888 | unsigned long len, real_len, status; | ||
889 | cpumask_t mask; | ||
890 | |||
891 | (void) sun4v_mach_desc(0UL, 0UL, &len); | ||
892 | |||
893 | printk("MDESC: Size is %lu bytes.\n", len); | ||
894 | |||
895 | hp = mdesc_alloc(len, &lmb_mdesc_ops); | ||
896 | if (hp == NULL) { | ||
897 | prom_printf("MDESC: alloc of %lu bytes failed.\n", len); | ||
898 | prom_halt(); | ||
899 | } | ||
900 | |||
901 | status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len); | ||
902 | if (status != HV_EOK || real_len > len) { | ||
903 | prom_printf("sun4v_mach_desc fails, err(%lu), " | ||
904 | "len(%lu), real_len(%lu)\n", | ||
905 | status, len, real_len); | ||
906 | mdesc_free(hp); | ||
907 | prom_halt(); | ||
908 | } | ||
909 | |||
910 | cur_mdesc = hp; | ||
911 | |||
912 | report_platform_properties(); | ||
913 | |||
914 | cpus_setall(mask); | ||
915 | mdesc_fill_in_cpu_data(mask); | ||
916 | } | ||