diff options
Diffstat (limited to 'arch/powerpc/kernel/sysfs.c')
-rw-r--r-- | arch/powerpc/kernel/sysfs.c | 311 |
1 files changed, 310 insertions, 1 deletions
diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index 800e5e9a087..56d172d16e5 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c | |||
@@ -22,6 +22,8 @@ | |||
22 | 22 | ||
23 | static DEFINE_PER_CPU(struct cpu, cpu_devices); | 23 | static DEFINE_PER_CPU(struct cpu, cpu_devices); |
24 | 24 | ||
25 | static DEFINE_PER_CPU(struct kobject *, cache_toplevel); | ||
26 | |||
25 | /* SMT stuff */ | 27 | /* SMT stuff */ |
26 | 28 | ||
27 | #ifdef CONFIG_PPC_MULTIPLATFORM | 29 | #ifdef CONFIG_PPC_MULTIPLATFORM |
@@ -297,8 +299,289 @@ static struct sysdev_attribute pa6t_attrs[] = { | |||
297 | #endif /* CONFIG_DEBUG_KERNEL */ | 299 | #endif /* CONFIG_DEBUG_KERNEL */ |
298 | }; | 300 | }; |
299 | 301 | ||
302 | struct cache_desc { | ||
303 | struct kobject kobj; | ||
304 | struct cache_desc *next; | ||
305 | const char *type; /* Instruction, Data, or Unified */ | ||
306 | u32 size; /* total cache size in KB */ | ||
307 | u32 line_size; /* in bytes */ | ||
308 | u32 nr_sets; /* number of sets */ | ||
309 | u32 level; /* e.g. 1, 2, 3... */ | ||
310 | u32 associativity; /* e.g. 8-way... 0 is fully associative */ | ||
311 | }; | ||
312 | |||
313 | DEFINE_PER_CPU(struct cache_desc *, cache_desc); | ||
314 | |||
315 | static struct cache_desc *kobj_to_cache_desc(struct kobject *k) | ||
316 | { | ||
317 | return container_of(k, struct cache_desc, kobj); | ||
318 | } | ||
319 | |||
320 | static void cache_desc_release(struct kobject *k) | ||
321 | { | ||
322 | struct cache_desc *desc = kobj_to_cache_desc(k); | ||
323 | |||
324 | pr_debug("%s: releasing %s\n", __func__, kobject_name(k)); | ||
325 | |||
326 | if (desc->next) | ||
327 | kobject_put(&desc->next->kobj); | ||
328 | |||
329 | kfree(kobj_to_cache_desc(k)); | ||
330 | } | ||
331 | |||
332 | static ssize_t cache_desc_show(struct kobject *k, struct attribute *attr, char *buf) | ||
333 | { | ||
334 | struct kobj_attribute *kobj_attr; | ||
335 | |||
336 | kobj_attr = container_of(attr, struct kobj_attribute, attr); | ||
337 | |||
338 | return kobj_attr->show(k, kobj_attr, buf); | ||
339 | } | ||
340 | |||
341 | static struct sysfs_ops cache_desc_sysfs_ops = { | ||
342 | .show = cache_desc_show, | ||
343 | }; | ||
344 | |||
345 | static struct kobj_type cache_desc_type = { | ||
346 | .release = cache_desc_release, | ||
347 | .sysfs_ops = &cache_desc_sysfs_ops, | ||
348 | }; | ||
349 | |||
350 | static ssize_t cache_size_show(struct kobject *k, struct kobj_attribute *attr, char *buf) | ||
351 | { | ||
352 | struct cache_desc *cache = kobj_to_cache_desc(k); | ||
353 | |||
354 | return sprintf(buf, "%uK\n", cache->size); | ||
355 | } | ||
356 | |||
357 | static struct kobj_attribute cache_size_attr = | ||
358 | __ATTR(size, 0444, cache_size_show, NULL); | ||
359 | |||
360 | static ssize_t cache_line_size_show(struct kobject *k, struct kobj_attribute *attr, char *buf) | ||
361 | { | ||
362 | struct cache_desc *cache = kobj_to_cache_desc(k); | ||
363 | |||
364 | return sprintf(buf, "%u\n", cache->line_size); | ||
365 | } | ||
366 | |||
367 | static struct kobj_attribute cache_line_size_attr = | ||
368 | __ATTR(coherency_line_size, 0444, cache_line_size_show, NULL); | ||
369 | |||
370 | static ssize_t cache_nr_sets_show(struct kobject *k, struct kobj_attribute *attr, char *buf) | ||
371 | { | ||
372 | struct cache_desc *cache = kobj_to_cache_desc(k); | ||
373 | |||
374 | return sprintf(buf, "%u\n", cache->nr_sets); | ||
375 | } | ||
376 | |||
377 | static struct kobj_attribute cache_nr_sets_attr = | ||
378 | __ATTR(number_of_sets, 0444, cache_nr_sets_show, NULL); | ||
379 | |||
380 | static ssize_t cache_type_show(struct kobject *k, struct kobj_attribute *attr, char *buf) | ||
381 | { | ||
382 | struct cache_desc *cache = kobj_to_cache_desc(k); | ||
383 | |||
384 | return sprintf(buf, "%s\n", cache->type); | ||
385 | } | ||
386 | |||
387 | static struct kobj_attribute cache_type_attr = | ||
388 | __ATTR(type, 0444, cache_type_show, NULL); | ||
389 | |||
390 | static ssize_t cache_level_show(struct kobject *k, struct kobj_attribute *attr, char *buf) | ||
391 | { | ||
392 | struct cache_desc *cache = kobj_to_cache_desc(k); | ||
393 | |||
394 | return sprintf(buf, "%u\n", cache->level); | ||
395 | } | ||
396 | |||
397 | static struct kobj_attribute cache_level_attr = | ||
398 | __ATTR(level, 0444, cache_level_show, NULL); | ||
399 | |||
400 | static ssize_t cache_assoc_show(struct kobject *k, struct kobj_attribute *attr, char *buf) | ||
401 | { | ||
402 | struct cache_desc *cache = kobj_to_cache_desc(k); | ||
403 | |||
404 | return sprintf(buf, "%u\n", cache->associativity); | ||
405 | } | ||
406 | |||
407 | static struct kobj_attribute cache_assoc_attr = | ||
408 | __ATTR(ways_of_associativity, 0444, cache_assoc_show, NULL); | ||
409 | |||
410 | struct cache_desc_info { | ||
411 | const char *type; | ||
412 | const char *size_prop; | ||
413 | const char *line_size_prop; | ||
414 | const char *nr_sets_prop; | ||
415 | }; | ||
416 | |||
417 | /* PowerPC Processor binding says the [di]-cache-* must be equal on | ||
418 | * unified caches, so just use d-cache properties. */ | ||
419 | static struct cache_desc_info ucache_info = { | ||
420 | .type = "Unified", | ||
421 | .size_prop = "d-cache-size", | ||
422 | .line_size_prop = "d-cache-line-size", | ||
423 | .nr_sets_prop = "d-cache-sets", | ||
424 | }; | ||
300 | 425 | ||
301 | static void register_cpu_online(unsigned int cpu) | 426 | static struct cache_desc_info dcache_info = { |
427 | .type = "Data", | ||
428 | .size_prop = "d-cache-size", | ||
429 | .line_size_prop = "d-cache-line-size", | ||
430 | .nr_sets_prop = "d-cache-sets", | ||
431 | }; | ||
432 | |||
433 | static struct cache_desc_info icache_info = { | ||
434 | .type = "Instruction", | ||
435 | .size_prop = "i-cache-size", | ||
436 | .line_size_prop = "i-cache-line-size", | ||
437 | .nr_sets_prop = "i-cache-sets", | ||
438 | }; | ||
439 | |||
440 | static struct cache_desc * __cpuinit create_cache_desc(struct device_node *np, struct kobject *parent, int index, int level, struct cache_desc_info *info) | ||
441 | { | ||
442 | const u32 *cache_line_size; | ||
443 | struct cache_desc *new; | ||
444 | const u32 *cache_size; | ||
445 | const u32 *nr_sets; | ||
446 | int rc; | ||
447 | |||
448 | new = kzalloc(sizeof(*new), GFP_KERNEL); | ||
449 | if (!new) | ||
450 | return NULL; | ||
451 | |||
452 | rc = kobject_init_and_add(&new->kobj, &cache_desc_type, parent, | ||
453 | "index%d", index); | ||
454 | if (rc) | ||
455 | goto err; | ||
456 | |||
457 | /* type */ | ||
458 | new->type = info->type; | ||
459 | rc = sysfs_create_file(&new->kobj, &cache_type_attr.attr); | ||
460 | WARN_ON(rc); | ||
461 | |||
462 | /* level */ | ||
463 | new->level = level; | ||
464 | rc = sysfs_create_file(&new->kobj, &cache_level_attr.attr); | ||
465 | WARN_ON(rc); | ||
466 | |||
467 | /* size */ | ||
468 | cache_size = of_get_property(np, info->size_prop, NULL); | ||
469 | if (cache_size) { | ||
470 | new->size = *cache_size / 1024; | ||
471 | rc = sysfs_create_file(&new->kobj, | ||
472 | &cache_size_attr.attr); | ||
473 | WARN_ON(rc); | ||
474 | } | ||
475 | |||
476 | /* coherency_line_size */ | ||
477 | cache_line_size = of_get_property(np, info->line_size_prop, NULL); | ||
478 | if (cache_line_size) { | ||
479 | new->line_size = *cache_line_size; | ||
480 | rc = sysfs_create_file(&new->kobj, | ||
481 | &cache_line_size_attr.attr); | ||
482 | WARN_ON(rc); | ||
483 | } | ||
484 | |||
485 | /* number_of_sets */ | ||
486 | nr_sets = of_get_property(np, info->nr_sets_prop, NULL); | ||
487 | if (nr_sets) { | ||
488 | new->nr_sets = *nr_sets; | ||
489 | rc = sysfs_create_file(&new->kobj, | ||
490 | &cache_nr_sets_attr.attr); | ||
491 | WARN_ON(rc); | ||
492 | } | ||
493 | |||
494 | /* ways_of_associativity */ | ||
495 | if (new->nr_sets == 1) { | ||
496 | /* fully associative */ | ||
497 | new->associativity = 0; | ||
498 | goto create_assoc; | ||
499 | } | ||
500 | |||
501 | if (new->nr_sets && new->size && new->line_size) { | ||
502 | /* If we have values for all of these we can derive | ||
503 | * the associativity. */ | ||
504 | new->associativity = | ||
505 | ((new->size * 1024) / new->nr_sets) / new->line_size; | ||
506 | create_assoc: | ||
507 | rc = sysfs_create_file(&new->kobj, | ||
508 | &cache_assoc_attr.attr); | ||
509 | WARN_ON(rc); | ||
510 | } | ||
511 | |||
512 | return new; | ||
513 | err: | ||
514 | kfree(new); | ||
515 | return NULL; | ||
516 | } | ||
517 | |||
518 | static bool cache_is_unified(struct device_node *np) | ||
519 | { | ||
520 | return of_get_property(np, "cache-unified", NULL); | ||
521 | } | ||
522 | |||
523 | static struct cache_desc * __cpuinit create_cache_index_info(struct device_node *np, struct kobject *parent, int index, int level) | ||
524 | { | ||
525 | const phandle *next_cache_phandle; | ||
526 | struct device_node *next_cache; | ||
527 | struct cache_desc *new, **end; | ||
528 | |||
529 | pr_debug("%s(node = %s, index = %d)\n", __func__, np->full_name, index); | ||
530 | |||
531 | if (cache_is_unified(np)) { | ||
532 | new = create_cache_desc(np, parent, index, level, | ||
533 | &ucache_info); | ||
534 | } else { | ||
535 | new = create_cache_desc(np, parent, index, level, | ||
536 | &dcache_info); | ||
537 | if (new) { | ||
538 | index++; | ||
539 | new->next = create_cache_desc(np, parent, index, level, | ||
540 | &icache_info); | ||
541 | } | ||
542 | } | ||
543 | if (!new) | ||
544 | return NULL; | ||
545 | |||
546 | end = &new->next; | ||
547 | while (*end) | ||
548 | end = &(*end)->next; | ||
549 | |||
550 | next_cache_phandle = of_get_property(np, "l2-cache", NULL); | ||
551 | if (!next_cache_phandle) | ||
552 | goto out; | ||
553 | |||
554 | next_cache = of_find_node_by_phandle(*next_cache_phandle); | ||
555 | if (!next_cache) | ||
556 | goto out; | ||
557 | |||
558 | *end = create_cache_index_info(next_cache, parent, ++index, ++level); | ||
559 | |||
560 | of_node_put(next_cache); | ||
561 | out: | ||
562 | return new; | ||
563 | } | ||
564 | |||
565 | static void __cpuinit create_cache_info(struct sys_device *sysdev) | ||
566 | { | ||
567 | struct kobject *cache_toplevel; | ||
568 | struct device_node *np = NULL; | ||
569 | int cpu = sysdev->id; | ||
570 | |||
571 | cache_toplevel = kobject_create_and_add("cache", &sysdev->kobj); | ||
572 | if (!cache_toplevel) | ||
573 | return; | ||
574 | per_cpu(cache_toplevel, cpu) = cache_toplevel; | ||
575 | np = of_get_cpu_node(cpu, NULL); | ||
576 | if (np != NULL) { | ||
577 | per_cpu(cache_desc, cpu) = | ||
578 | create_cache_index_info(np, cache_toplevel, 0, 1); | ||
579 | of_node_put(np); | ||
580 | } | ||
581 | return; | ||
582 | } | ||
583 | |||
584 | static void __cpuinit register_cpu_online(unsigned int cpu) | ||
302 | { | 585 | { |
303 | struct cpu *c = &per_cpu(cpu_devices, cpu); | 586 | struct cpu *c = &per_cpu(cpu_devices, cpu); |
304 | struct sys_device *s = &c->sysdev; | 587 | struct sys_device *s = &c->sysdev; |
@@ -346,9 +629,33 @@ static void register_cpu_online(unsigned int cpu) | |||
346 | 629 | ||
347 | if (cpu_has_feature(CPU_FTR_DSCR)) | 630 | if (cpu_has_feature(CPU_FTR_DSCR)) |
348 | sysdev_create_file(s, &attr_dscr); | 631 | sysdev_create_file(s, &attr_dscr); |
632 | |||
633 | create_cache_info(s); | ||
349 | } | 634 | } |
350 | 635 | ||
351 | #ifdef CONFIG_HOTPLUG_CPU | 636 | #ifdef CONFIG_HOTPLUG_CPU |
637 | static void remove_cache_info(struct sys_device *sysdev) | ||
638 | { | ||
639 | struct kobject *cache_toplevel; | ||
640 | struct cache_desc *cache_desc; | ||
641 | int cpu = sysdev->id; | ||
642 | |||
643 | cache_desc = per_cpu(cache_desc, cpu); | ||
644 | if (cache_desc != NULL) { | ||
645 | sysfs_remove_file(&cache_desc->kobj, &cache_size_attr.attr); | ||
646 | sysfs_remove_file(&cache_desc->kobj, &cache_line_size_attr.attr); | ||
647 | sysfs_remove_file(&cache_desc->kobj, &cache_type_attr.attr); | ||
648 | sysfs_remove_file(&cache_desc->kobj, &cache_level_attr.attr); | ||
649 | sysfs_remove_file(&cache_desc->kobj, &cache_nr_sets_attr.attr); | ||
650 | sysfs_remove_file(&cache_desc->kobj, &cache_assoc_attr.attr); | ||
651 | |||
652 | kobject_put(&cache_desc->kobj); | ||
653 | } | ||
654 | cache_toplevel = per_cpu(cache_toplevel, cpu); | ||
655 | if (cache_toplevel != NULL) | ||
656 | kobject_put(cache_toplevel); | ||
657 | } | ||
658 | |||
352 | static void unregister_cpu_online(unsigned int cpu) | 659 | static void unregister_cpu_online(unsigned int cpu) |
353 | { | 660 | { |
354 | struct cpu *c = &per_cpu(cpu_devices, cpu); | 661 | struct cpu *c = &per_cpu(cpu_devices, cpu); |
@@ -399,6 +706,8 @@ static void unregister_cpu_online(unsigned int cpu) | |||
399 | 706 | ||
400 | if (cpu_has_feature(CPU_FTR_DSCR)) | 707 | if (cpu_has_feature(CPU_FTR_DSCR)) |
401 | sysdev_remove_file(s, &attr_dscr); | 708 | sysdev_remove_file(s, &attr_dscr); |
709 | |||
710 | remove_cache_info(s); | ||
402 | } | 711 | } |
403 | #endif /* CONFIG_HOTPLUG_CPU */ | 712 | #endif /* CONFIG_HOTPLUG_CPU */ |
404 | 713 | ||