diff options
Diffstat (limited to 'arch/powerpc/kernel/sysfs.c')
-rw-r--r-- | arch/powerpc/kernel/sysfs.c | 300 |
1 files changed, 4 insertions, 296 deletions
diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index 0c64f10087b9..4a2ee08af6a7 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c | |||
@@ -18,6 +18,8 @@ | |||
18 | #include <asm/machdep.h> | 18 | #include <asm/machdep.h> |
19 | #include <asm/smp.h> | 19 | #include <asm/smp.h> |
20 | 20 | ||
21 | #include "cacheinfo.h" | ||
22 | |||
21 | #ifdef CONFIG_PPC64 | 23 | #ifdef CONFIG_PPC64 |
22 | #include <asm/paca.h> | 24 | #include <asm/paca.h> |
23 | #include <asm/lppaca.h> | 25 | #include <asm/lppaca.h> |
@@ -25,8 +27,6 @@ | |||
25 | 27 | ||
26 | static DEFINE_PER_CPU(struct cpu, cpu_devices); | 28 | static DEFINE_PER_CPU(struct cpu, cpu_devices); |
27 | 29 | ||
28 | static DEFINE_PER_CPU(struct kobject *, cache_toplevel); | ||
29 | |||
30 | /* | 30 | /* |
31 | * SMT snooze delay stuff, 64-bit only for now | 31 | * SMT snooze delay stuff, 64-bit only for now |
32 | */ | 32 | */ |
@@ -343,283 +343,6 @@ static struct sysdev_attribute pa6t_attrs[] = { | |||
343 | #endif /* HAS_PPC_PMC_PA6T */ | 343 | #endif /* HAS_PPC_PMC_PA6T */ |
344 | #endif /* HAS_PPC_PMC_CLASSIC */ | 344 | #endif /* HAS_PPC_PMC_CLASSIC */ |
345 | 345 | ||
346 | struct cache_desc { | ||
347 | struct kobject kobj; | ||
348 | struct cache_desc *next; | ||
349 | const char *type; /* Instruction, Data, or Unified */ | ||
350 | u32 size; /* total cache size in KB */ | ||
351 | u32 line_size; /* in bytes */ | ||
352 | u32 nr_sets; /* number of sets */ | ||
353 | u32 level; /* e.g. 1, 2, 3... */ | ||
354 | u32 associativity; /* e.g. 8-way... 0 is fully associative */ | ||
355 | }; | ||
356 | |||
357 | DEFINE_PER_CPU(struct cache_desc *, cache_desc); | ||
358 | |||
359 | static struct cache_desc *kobj_to_cache_desc(struct kobject *k) | ||
360 | { | ||
361 | return container_of(k, struct cache_desc, kobj); | ||
362 | } | ||
363 | |||
364 | static void cache_desc_release(struct kobject *k) | ||
365 | { | ||
366 | struct cache_desc *desc = kobj_to_cache_desc(k); | ||
367 | |||
368 | pr_debug("%s: releasing %s\n", __func__, kobject_name(k)); | ||
369 | |||
370 | if (desc->next) | ||
371 | kobject_put(&desc->next->kobj); | ||
372 | |||
373 | kfree(kobj_to_cache_desc(k)); | ||
374 | } | ||
375 | |||
376 | static ssize_t cache_desc_show(struct kobject *k, struct attribute *attr, char *buf) | ||
377 | { | ||
378 | struct kobj_attribute *kobj_attr; | ||
379 | |||
380 | kobj_attr = container_of(attr, struct kobj_attribute, attr); | ||
381 | |||
382 | return kobj_attr->show(k, kobj_attr, buf); | ||
383 | } | ||
384 | |||
385 | static struct sysfs_ops cache_desc_sysfs_ops = { | ||
386 | .show = cache_desc_show, | ||
387 | }; | ||
388 | |||
389 | static struct kobj_type cache_desc_type = { | ||
390 | .release = cache_desc_release, | ||
391 | .sysfs_ops = &cache_desc_sysfs_ops, | ||
392 | }; | ||
393 | |||
394 | static ssize_t cache_size_show(struct kobject *k, struct kobj_attribute *attr, char *buf) | ||
395 | { | ||
396 | struct cache_desc *cache = kobj_to_cache_desc(k); | ||
397 | |||
398 | return sprintf(buf, "%uK\n", cache->size); | ||
399 | } | ||
400 | |||
401 | static struct kobj_attribute cache_size_attr = | ||
402 | __ATTR(size, 0444, cache_size_show, NULL); | ||
403 | |||
404 | static ssize_t cache_line_size_show(struct kobject *k, struct kobj_attribute *attr, char *buf) | ||
405 | { | ||
406 | struct cache_desc *cache = kobj_to_cache_desc(k); | ||
407 | |||
408 | return sprintf(buf, "%u\n", cache->line_size); | ||
409 | } | ||
410 | |||
411 | static struct kobj_attribute cache_line_size_attr = | ||
412 | __ATTR(coherency_line_size, 0444, cache_line_size_show, NULL); | ||
413 | |||
414 | static ssize_t cache_nr_sets_show(struct kobject *k, struct kobj_attribute *attr, char *buf) | ||
415 | { | ||
416 | struct cache_desc *cache = kobj_to_cache_desc(k); | ||
417 | |||
418 | return sprintf(buf, "%u\n", cache->nr_sets); | ||
419 | } | ||
420 | |||
421 | static struct kobj_attribute cache_nr_sets_attr = | ||
422 | __ATTR(number_of_sets, 0444, cache_nr_sets_show, NULL); | ||
423 | |||
424 | static ssize_t cache_type_show(struct kobject *k, struct kobj_attribute *attr, char *buf) | ||
425 | { | ||
426 | struct cache_desc *cache = kobj_to_cache_desc(k); | ||
427 | |||
428 | return sprintf(buf, "%s\n", cache->type); | ||
429 | } | ||
430 | |||
431 | static struct kobj_attribute cache_type_attr = | ||
432 | __ATTR(type, 0444, cache_type_show, NULL); | ||
433 | |||
434 | static ssize_t cache_level_show(struct kobject *k, struct kobj_attribute *attr, char *buf) | ||
435 | { | ||
436 | struct cache_desc *cache = kobj_to_cache_desc(k); | ||
437 | |||
438 | return sprintf(buf, "%u\n", cache->level); | ||
439 | } | ||
440 | |||
441 | static struct kobj_attribute cache_level_attr = | ||
442 | __ATTR(level, 0444, cache_level_show, NULL); | ||
443 | |||
444 | static ssize_t cache_assoc_show(struct kobject *k, struct kobj_attribute *attr, char *buf) | ||
445 | { | ||
446 | struct cache_desc *cache = kobj_to_cache_desc(k); | ||
447 | |||
448 | return sprintf(buf, "%u\n", cache->associativity); | ||
449 | } | ||
450 | |||
451 | static struct kobj_attribute cache_assoc_attr = | ||
452 | __ATTR(ways_of_associativity, 0444, cache_assoc_show, NULL); | ||
453 | |||
454 | struct cache_desc_info { | ||
455 | const char *type; | ||
456 | const char *size_prop; | ||
457 | const char *line_size_prop; | ||
458 | const char *nr_sets_prop; | ||
459 | }; | ||
460 | |||
461 | /* PowerPC Processor binding says the [di]-cache-* must be equal on | ||
462 | * unified caches, so just use d-cache properties. */ | ||
463 | static struct cache_desc_info ucache_info = { | ||
464 | .type = "Unified", | ||
465 | .size_prop = "d-cache-size", | ||
466 | .line_size_prop = "d-cache-line-size", | ||
467 | .nr_sets_prop = "d-cache-sets", | ||
468 | }; | ||
469 | |||
470 | static struct cache_desc_info dcache_info = { | ||
471 | .type = "Data", | ||
472 | .size_prop = "d-cache-size", | ||
473 | .line_size_prop = "d-cache-line-size", | ||
474 | .nr_sets_prop = "d-cache-sets", | ||
475 | }; | ||
476 | |||
477 | static struct cache_desc_info icache_info = { | ||
478 | .type = "Instruction", | ||
479 | .size_prop = "i-cache-size", | ||
480 | .line_size_prop = "i-cache-line-size", | ||
481 | .nr_sets_prop = "i-cache-sets", | ||
482 | }; | ||
483 | |||
484 | static struct cache_desc * __cpuinit create_cache_desc(struct device_node *np, struct kobject *parent, int index, int level, struct cache_desc_info *info) | ||
485 | { | ||
486 | const u32 *cache_line_size; | ||
487 | struct cache_desc *new; | ||
488 | const u32 *cache_size; | ||
489 | const u32 *nr_sets; | ||
490 | int rc; | ||
491 | |||
492 | new = kzalloc(sizeof(*new), GFP_KERNEL); | ||
493 | if (!new) | ||
494 | return NULL; | ||
495 | |||
496 | rc = kobject_init_and_add(&new->kobj, &cache_desc_type, parent, | ||
497 | "index%d", index); | ||
498 | if (rc) | ||
499 | goto err; | ||
500 | |||
501 | /* type */ | ||
502 | new->type = info->type; | ||
503 | rc = sysfs_create_file(&new->kobj, &cache_type_attr.attr); | ||
504 | WARN_ON(rc); | ||
505 | |||
506 | /* level */ | ||
507 | new->level = level; | ||
508 | rc = sysfs_create_file(&new->kobj, &cache_level_attr.attr); | ||
509 | WARN_ON(rc); | ||
510 | |||
511 | /* size */ | ||
512 | cache_size = of_get_property(np, info->size_prop, NULL); | ||
513 | if (cache_size) { | ||
514 | new->size = *cache_size / 1024; | ||
515 | rc = sysfs_create_file(&new->kobj, | ||
516 | &cache_size_attr.attr); | ||
517 | WARN_ON(rc); | ||
518 | } | ||
519 | |||
520 | /* coherency_line_size */ | ||
521 | cache_line_size = of_get_property(np, info->line_size_prop, NULL); | ||
522 | if (cache_line_size) { | ||
523 | new->line_size = *cache_line_size; | ||
524 | rc = sysfs_create_file(&new->kobj, | ||
525 | &cache_line_size_attr.attr); | ||
526 | WARN_ON(rc); | ||
527 | } | ||
528 | |||
529 | /* number_of_sets */ | ||
530 | nr_sets = of_get_property(np, info->nr_sets_prop, NULL); | ||
531 | if (nr_sets) { | ||
532 | new->nr_sets = *nr_sets; | ||
533 | rc = sysfs_create_file(&new->kobj, | ||
534 | &cache_nr_sets_attr.attr); | ||
535 | WARN_ON(rc); | ||
536 | } | ||
537 | |||
538 | /* ways_of_associativity */ | ||
539 | if (new->nr_sets == 1) { | ||
540 | /* fully associative */ | ||
541 | new->associativity = 0; | ||
542 | goto create_assoc; | ||
543 | } | ||
544 | |||
545 | if (new->nr_sets && new->size && new->line_size) { | ||
546 | /* If we have values for all of these we can derive | ||
547 | * the associativity. */ | ||
548 | new->associativity = | ||
549 | ((new->size * 1024) / new->nr_sets) / new->line_size; | ||
550 | create_assoc: | ||
551 | rc = sysfs_create_file(&new->kobj, | ||
552 | &cache_assoc_attr.attr); | ||
553 | WARN_ON(rc); | ||
554 | } | ||
555 | |||
556 | return new; | ||
557 | err: | ||
558 | kfree(new); | ||
559 | return NULL; | ||
560 | } | ||
561 | |||
562 | static bool cache_is_unified(struct device_node *np) | ||
563 | { | ||
564 | return of_get_property(np, "cache-unified", NULL); | ||
565 | } | ||
566 | |||
567 | static struct cache_desc * __cpuinit create_cache_index_info(struct device_node *np, struct kobject *parent, int index, int level) | ||
568 | { | ||
569 | struct device_node *next_cache; | ||
570 | struct cache_desc *new, **end; | ||
571 | |||
572 | pr_debug("%s(node = %s, index = %d)\n", __func__, np->full_name, index); | ||
573 | |||
574 | if (cache_is_unified(np)) { | ||
575 | new = create_cache_desc(np, parent, index, level, | ||
576 | &ucache_info); | ||
577 | } else { | ||
578 | new = create_cache_desc(np, parent, index, level, | ||
579 | &dcache_info); | ||
580 | if (new) { | ||
581 | index++; | ||
582 | new->next = create_cache_desc(np, parent, index, level, | ||
583 | &icache_info); | ||
584 | } | ||
585 | } | ||
586 | if (!new) | ||
587 | return NULL; | ||
588 | |||
589 | end = &new->next; | ||
590 | while (*end) | ||
591 | end = &(*end)->next; | ||
592 | |||
593 | next_cache = of_find_next_cache_node(np); | ||
594 | if (!next_cache) | ||
595 | goto out; | ||
596 | |||
597 | *end = create_cache_index_info(next_cache, parent, ++index, ++level); | ||
598 | |||
599 | of_node_put(next_cache); | ||
600 | out: | ||
601 | return new; | ||
602 | } | ||
603 | |||
604 | static void __cpuinit create_cache_info(struct sys_device *sysdev) | ||
605 | { | ||
606 | struct kobject *cache_toplevel; | ||
607 | struct device_node *np = NULL; | ||
608 | int cpu = sysdev->id; | ||
609 | |||
610 | cache_toplevel = kobject_create_and_add("cache", &sysdev->kobj); | ||
611 | if (!cache_toplevel) | ||
612 | return; | ||
613 | per_cpu(cache_toplevel, cpu) = cache_toplevel; | ||
614 | np = of_get_cpu_node(cpu, NULL); | ||
615 | if (np != NULL) { | ||
616 | per_cpu(cache_desc, cpu) = | ||
617 | create_cache_index_info(np, cache_toplevel, 0, 1); | ||
618 | of_node_put(np); | ||
619 | } | ||
620 | return; | ||
621 | } | ||
622 | |||
623 | static void __cpuinit register_cpu_online(unsigned int cpu) | 346 | static void __cpuinit register_cpu_online(unsigned int cpu) |
624 | { | 347 | { |
625 | struct cpu *c = &per_cpu(cpu_devices, cpu); | 348 | struct cpu *c = &per_cpu(cpu_devices, cpu); |
@@ -684,25 +407,10 @@ static void __cpuinit register_cpu_online(unsigned int cpu) | |||
684 | sysdev_create_file(s, &attr_dscr); | 407 | sysdev_create_file(s, &attr_dscr); |
685 | #endif /* CONFIG_PPC64 */ | 408 | #endif /* CONFIG_PPC64 */ |
686 | 409 | ||
687 | create_cache_info(s); | 410 | cacheinfo_cpu_online(cpu); |
688 | } | 411 | } |
689 | 412 | ||
690 | #ifdef CONFIG_HOTPLUG_CPU | 413 | #ifdef CONFIG_HOTPLUG_CPU |
691 | static void remove_cache_info(struct sys_device *sysdev) | ||
692 | { | ||
693 | struct kobject *cache_toplevel; | ||
694 | struct cache_desc *cache_desc; | ||
695 | int cpu = sysdev->id; | ||
696 | |||
697 | cache_desc = per_cpu(cache_desc, cpu); | ||
698 | if (cache_desc != NULL) | ||
699 | kobject_put(&cache_desc->kobj); | ||
700 | |||
701 | cache_toplevel = per_cpu(cache_toplevel, cpu); | ||
702 | if (cache_toplevel != NULL) | ||
703 | kobject_put(cache_toplevel); | ||
704 | } | ||
705 | |||
706 | static void unregister_cpu_online(unsigned int cpu) | 414 | static void unregister_cpu_online(unsigned int cpu) |
707 | { | 415 | { |
708 | struct cpu *c = &per_cpu(cpu_devices, cpu); | 416 | struct cpu *c = &per_cpu(cpu_devices, cpu); |
@@ -769,7 +477,7 @@ static void unregister_cpu_online(unsigned int cpu) | |||
769 | sysdev_remove_file(s, &attr_dscr); | 477 | sysdev_remove_file(s, &attr_dscr); |
770 | #endif /* CONFIG_PPC64 */ | 478 | #endif /* CONFIG_PPC64 */ |
771 | 479 | ||
772 | remove_cache_info(s); | 480 | cacheinfo_cpu_offline(cpu); |
773 | } | 481 | } |
774 | #endif /* CONFIG_HOTPLUG_CPU */ | 482 | #endif /* CONFIG_HOTPLUG_CPU */ |
775 | 483 | ||