diff options
author | Adeel Raza <araza@nvidia.com> | 2015-12-11 19:16:21 -0500 |
---|---|---|
committer | Deepak Nibade <dnibade@nvidia.com> | 2016-12-27 04:52:10 -0500 |
commit | e9b03e903c10e1fce9daf5fa7e51b8c4a0b65c95 (patch) | |
tree | 7d55abac9face13a5753fc26a9817743606e8870 /drivers/gpu/nvgpu/gp10b/platform_gp10b_tegra.c | |
parent | f7d327985fca67266ea409e24c0ef6505d98f338 (diff) |
gpu: nvgpu: gp10b: add ECC stats sysfs nodes
Add sysfs nodes for querying ECC single/double bit error counts.
Bug 1699676
Change-Id: I6d5219facadaa17207ac759b88fe19077207d8f1
Signed-off-by: Adeel Raza <araza@nvidia.com>
Reviewed-on: http://git-master/r/935363
Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>
Diffstat (limited to 'drivers/gpu/nvgpu/gp10b/platform_gp10b_tegra.c')
-rw-r--r-- | drivers/gpu/nvgpu/gp10b/platform_gp10b_tegra.c | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/gp10b/platform_gp10b_tegra.c b/drivers/gpu/nvgpu/gp10b/platform_gp10b_tegra.c index 8bddff3d..0cfb1d91 100644 --- a/drivers/gpu/nvgpu/gp10b/platform_gp10b_tegra.c +++ b/drivers/gpu/nvgpu/gp10b/platform_gp10b_tegra.c | |||
@@ -26,9 +26,14 @@ | |||
26 | #include <linux/tegra_pm_domains.h> | 26 | #include <linux/tegra_pm_domains.h> |
27 | #include <linux/reset.h> | 27 | #include <linux/reset.h> |
28 | #include <soc/tegra/tegra_bpmp.h> | 28 | #include <soc/tegra/tegra_bpmp.h> |
29 | #include <linux/hashtable.h> | ||
29 | #include "gk20a/platform_gk20a.h" | 30 | #include "gk20a/platform_gk20a.h" |
30 | #include "gk20a/gk20a.h" | 31 | #include "gk20a/gk20a.h" |
31 | #include "platform_tegra.h" | 32 | #include "platform_tegra.h" |
33 | #include "gr_gp10b.h" | ||
34 | #include "ltc_gp10b.h" | ||
35 | #include "hw_gr_gp10b.h" | ||
36 | #include "hw_ltc_gp10b.h" | ||
32 | 37 | ||
33 | #define GP10B_MAX_SUPPORTED_FREQS 11 | 38 | #define GP10B_MAX_SUPPORTED_FREQS 11 |
34 | static unsigned long gp10b_freq_table[GP10B_MAX_SUPPORTED_FREQS]; | 39 | static unsigned long gp10b_freq_table[GP10B_MAX_SUPPORTED_FREQS]; |
@@ -40,6 +45,8 @@ static struct { | |||
40 | {"gpu", 1000000000}, | 45 | {"gpu", 1000000000}, |
41 | {"gpu_sys", 204000000} }; | 46 | {"gpu_sys", 204000000} }; |
42 | 47 | ||
48 | static void gr_gp10b_remove_sysfs(struct device *dev); | ||
49 | |||
43 | /* | 50 | /* |
44 | * gp10b_tegra_get_clocks() | 51 | * gp10b_tegra_get_clocks() |
45 | * | 52 | * |
@@ -144,6 +151,8 @@ static int gp10b_tegra_remove(struct platform_device *pdev) | |||
144 | /* remove gk20a power subdomain from host1x */ | 151 | /* remove gk20a power subdomain from host1x */ |
145 | nvhost_unregister_client_domain(dev_to_genpd(&pdev->dev)); | 152 | nvhost_unregister_client_domain(dev_to_genpd(&pdev->dev)); |
146 | 153 | ||
154 | gr_gp10b_remove_sysfs(&pdev->dev); | ||
155 | |||
147 | return 0; | 156 | return 0; |
148 | 157 | ||
149 | } | 158 | } |
@@ -345,3 +354,322 @@ struct gk20a_platform t18x_gpu_tegra_platform = { | |||
345 | 354 | ||
346 | .force_reset_in_do_idle = true, | 355 | .force_reset_in_do_idle = true, |
347 | }; | 356 | }; |
357 | |||
358 | |||
359 | #define ECC_STAT_NAME_MAX_SIZE 100 | ||
360 | |||
361 | |||
362 | DEFINE_HASHTABLE(ecc_hash_table, 5); | ||
363 | |||
364 | static struct device_attribute *dev_attr_sm_lrf_ecc_single_err_count_array; | ||
365 | static struct device_attribute *dev_attr_sm_lrf_ecc_double_err_count_array; | ||
366 | |||
367 | static struct device_attribute *dev_attr_sm_shm_ecc_sec_count_array; | ||
368 | static struct device_attribute *dev_attr_sm_shm_ecc_sed_count_array; | ||
369 | static struct device_attribute *dev_attr_sm_shm_ecc_ded_count_array; | ||
370 | |||
371 | static struct device_attribute *dev_attr_tex_ecc_total_sec_pipe0_count_array; | ||
372 | static struct device_attribute *dev_attr_tex_ecc_total_ded_pipe0_count_array; | ||
373 | static struct device_attribute *dev_attr_tex_ecc_unique_sec_pipe0_count_array; | ||
374 | static struct device_attribute *dev_attr_tex_ecc_unique_ded_pipe0_count_array; | ||
375 | static struct device_attribute *dev_attr_tex_ecc_total_sec_pipe1_count_array; | ||
376 | static struct device_attribute *dev_attr_tex_ecc_total_ded_pipe1_count_array; | ||
377 | static struct device_attribute *dev_attr_tex_ecc_unique_sec_pipe1_count_array; | ||
378 | static struct device_attribute *dev_attr_tex_ecc_unique_ded_pipe1_count_array; | ||
379 | |||
380 | static struct device_attribute *dev_attr_l2_ecc_sec_count_array; | ||
381 | static struct device_attribute *dev_attr_l2_ecc_ded_count_array; | ||
382 | |||
383 | |||
384 | static u32 gen_ecc_hash_key(char *str) | ||
385 | { | ||
386 | int i = 0; | ||
387 | u32 hash_key = 0; | ||
388 | |||
389 | while (str[i]) { | ||
390 | hash_key += (u32)(str[i]); | ||
391 | i++; | ||
392 | }; | ||
393 | |||
394 | return hash_key; | ||
395 | } | ||
396 | |||
397 | static ssize_t ecc_stat_show(struct device *dev, | ||
398 | struct device_attribute *attr, | ||
399 | char *buf) | ||
400 | { | ||
401 | const char *ecc_stat_full_name = attr->attr.name; | ||
402 | const char *ecc_stat_base_name; | ||
403 | unsigned int hw_unit; | ||
404 | struct ecc_stat *ecc_stat; | ||
405 | u32 hash_key; | ||
406 | |||
407 | if (sscanf(ecc_stat_full_name, "ltc%u", &hw_unit) == 1) { | ||
408 | ecc_stat_base_name = &(ecc_stat_full_name[strlen("ltc0_")]); | ||
409 | } else if (sscanf(ecc_stat_full_name, "gpc0_tpc%u", &hw_unit) == 1) { | ||
410 | ecc_stat_base_name = &(ecc_stat_full_name[strlen("gpc0_tpc0_")]); | ||
411 | } else { | ||
412 | return snprintf(buf, | ||
413 | PAGE_SIZE, | ||
414 | "Error: Invalid ECC stat name!\n"); | ||
415 | } | ||
416 | |||
417 | hash_key = gen_ecc_hash_key((char *)ecc_stat_base_name); | ||
418 | hash_for_each_possible(ecc_hash_table, | ||
419 | ecc_stat, | ||
420 | hash_node, | ||
421 | hash_key) { | ||
422 | if (!strcmp(ecc_stat_full_name, ecc_stat->names[hw_unit])) | ||
423 | return snprintf(buf, PAGE_SIZE, "%u\n", ecc_stat->counters[hw_unit]); | ||
424 | } | ||
425 | |||
426 | return snprintf(buf, PAGE_SIZE, "Error: No ECC stat found!\n"); | ||
427 | } | ||
428 | |||
429 | static int ecc_stat_create(struct platform_device *dev, | ||
430 | int is_l2, | ||
431 | char *ecc_stat_name, | ||
432 | struct ecc_stat *ecc_stat, | ||
433 | struct device_attribute *dev_attr_array) | ||
434 | { | ||
435 | int error = 0; | ||
436 | struct gk20a *g = get_gk20a(dev); | ||
437 | int num_hw_units = 0; | ||
438 | int hw_unit = 0; | ||
439 | u32 hash_key = 0; | ||
440 | |||
441 | if (is_l2) | ||
442 | num_hw_units = g->ltc_count; | ||
443 | else | ||
444 | num_hw_units = g->gr.tpc_count; | ||
445 | |||
446 | /* Allocate arrays */ | ||
447 | dev_attr_array = kzalloc(sizeof(struct device_attribute) * num_hw_units, GFP_KERNEL); | ||
448 | ecc_stat->counters = kzalloc(sizeof(u32) * num_hw_units, GFP_KERNEL); | ||
449 | ecc_stat->names = kzalloc(sizeof(char *) * num_hw_units, GFP_KERNEL); | ||
450 | for (hw_unit = 0; hw_unit < num_hw_units; hw_unit++) { | ||
451 | ecc_stat->names[hw_unit] = kzalloc(sizeof(char) * ECC_STAT_NAME_MAX_SIZE, GFP_KERNEL); | ||
452 | } | ||
453 | |||
454 | for (hw_unit = 0; hw_unit < num_hw_units; hw_unit++) { | ||
455 | /* Fill in struct device_attribute members */ | ||
456 | if (is_l2) | ||
457 | snprintf(ecc_stat->names[hw_unit], | ||
458 | ECC_STAT_NAME_MAX_SIZE, | ||
459 | "ltc%d_%s", | ||
460 | hw_unit, | ||
461 | ecc_stat_name); | ||
462 | else | ||
463 | snprintf(ecc_stat->names[hw_unit], | ||
464 | ECC_STAT_NAME_MAX_SIZE, | ||
465 | "gpc0_tpc%d_%s", | ||
466 | hw_unit, | ||
467 | ecc_stat_name); | ||
468 | dev_attr_array[hw_unit].attr.name = ecc_stat->names[hw_unit]; | ||
469 | dev_attr_array[hw_unit].attr.mode = VERIFY_OCTAL_PERMISSIONS(S_IRUGO); | ||
470 | dev_attr_array[hw_unit].show = ecc_stat_show; | ||
471 | dev_attr_array[hw_unit].store = NULL; | ||
472 | |||
473 | /* Create sysfs file */ | ||
474 | error |= device_create_file(&dev->dev, | ||
475 | &dev_attr_array[hw_unit]); | ||
476 | } | ||
477 | |||
478 | /* Add hash table entry */ | ||
479 | hash_key = gen_ecc_hash_key(ecc_stat_name); | ||
480 | hash_add(ecc_hash_table, | ||
481 | &ecc_stat->hash_node, | ||
482 | hash_key); | ||
483 | |||
484 | return error; | ||
485 | } | ||
486 | |||
487 | static void ecc_stat_remove(struct device *dev, | ||
488 | int is_l2, | ||
489 | struct ecc_stat *ecc_stat, | ||
490 | struct device_attribute *dev_attr_array) | ||
491 | { | ||
492 | struct platform_device *ndev = to_platform_device(dev); | ||
493 | struct gk20a *g = get_gk20a(ndev); | ||
494 | int num_hw_units = 0; | ||
495 | int hw_unit = 0; | ||
496 | |||
497 | if (is_l2) | ||
498 | num_hw_units = g->ltc_count; | ||
499 | else | ||
500 | num_hw_units = g->gr.tpc_count; | ||
501 | |||
502 | /* Remove sysfs files */ | ||
503 | for (hw_unit = 0; hw_unit < num_hw_units; hw_unit++) { | ||
504 | device_remove_file(dev, &dev_attr_array[hw_unit]); | ||
505 | } | ||
506 | |||
507 | /* Remove hash table entry */ | ||
508 | hash_del(&ecc_stat->hash_node); | ||
509 | |||
510 | /* Free arrays */ | ||
511 | kfree(ecc_stat->counters); | ||
512 | for (hw_unit = 0; hw_unit < num_hw_units; hw_unit++) { | ||
513 | kfree(ecc_stat->names[hw_unit]); | ||
514 | } | ||
515 | kfree(ecc_stat->names); | ||
516 | kfree(dev_attr_array); | ||
517 | } | ||
518 | |||
519 | void gr_gp10b_create_sysfs(struct platform_device *dev) | ||
520 | { | ||
521 | int error = 0; | ||
522 | struct gk20a *g = get_gk20a(dev); | ||
523 | |||
524 | error |= ecc_stat_create(dev, | ||
525 | 0, | ||
526 | "sm_lrf_ecc_single_err_count", | ||
527 | &g->gr.t18x.ecc_stats.sm_lrf_single_err_count, | ||
528 | dev_attr_sm_lrf_ecc_single_err_count_array); | ||
529 | error |= ecc_stat_create(dev, | ||
530 | 0, | ||
531 | "sm_lrf_ecc_double_err_count", | ||
532 | &g->gr.t18x.ecc_stats.sm_lrf_double_err_count, | ||
533 | dev_attr_sm_lrf_ecc_double_err_count_array); | ||
534 | |||
535 | error |= ecc_stat_create(dev, | ||
536 | 0, | ||
537 | "sm_shm_ecc_sec_count", | ||
538 | &g->gr.t18x.ecc_stats.sm_shm_sec_count, | ||
539 | dev_attr_sm_shm_ecc_sec_count_array); | ||
540 | error |= ecc_stat_create(dev, | ||
541 | 0, | ||
542 | "sm_shm_ecc_sed_count", | ||
543 | &g->gr.t18x.ecc_stats.sm_shm_sed_count, | ||
544 | dev_attr_sm_shm_ecc_sed_count_array); | ||
545 | error |= ecc_stat_create(dev, | ||
546 | 0, | ||
547 | "sm_shm_ecc_ded_count", | ||
548 | &g->gr.t18x.ecc_stats.sm_shm_ded_count, | ||
549 | dev_attr_sm_shm_ecc_ded_count_array); | ||
550 | |||
551 | error |= ecc_stat_create(dev, | ||
552 | 0, | ||
553 | "tex_ecc_total_sec_pipe0_count", | ||
554 | &g->gr.t18x.ecc_stats.tex_total_sec_pipe0_count, | ||
555 | dev_attr_tex_ecc_total_sec_pipe0_count_array); | ||
556 | error |= ecc_stat_create(dev, | ||
557 | 0, | ||
558 | "tex_ecc_total_ded_pipe0_count", | ||
559 | &g->gr.t18x.ecc_stats.tex_total_ded_pipe0_count, | ||
560 | dev_attr_tex_ecc_total_ded_pipe0_count_array); | ||
561 | error |= ecc_stat_create(dev, | ||
562 | 0, | ||
563 | "tex_ecc_unique_sec_pipe0_count", | ||
564 | &g->gr.t18x.ecc_stats.tex_unique_sec_pipe0_count, | ||
565 | dev_attr_tex_ecc_unique_sec_pipe0_count_array); | ||
566 | error |= ecc_stat_create(dev, | ||
567 | 0, | ||
568 | "tex_ecc_unique_ded_pipe0_count", | ||
569 | &g->gr.t18x.ecc_stats.tex_unique_ded_pipe0_count, | ||
570 | dev_attr_tex_ecc_unique_ded_pipe0_count_array); | ||
571 | error |= ecc_stat_create(dev, | ||
572 | 0, | ||
573 | "tex_ecc_total_sec_pipe1_count", | ||
574 | &g->gr.t18x.ecc_stats.tex_total_sec_pipe1_count, | ||
575 | dev_attr_tex_ecc_total_sec_pipe1_count_array); | ||
576 | error |= ecc_stat_create(dev, | ||
577 | 0, | ||
578 | "tex_ecc_total_ded_pipe1_count", | ||
579 | &g->gr.t18x.ecc_stats.tex_total_ded_pipe1_count, | ||
580 | dev_attr_tex_ecc_total_ded_pipe1_count_array); | ||
581 | error |= ecc_stat_create(dev, | ||
582 | 0, | ||
583 | "tex_ecc_unique_sec_pipe1_count", | ||
584 | &g->gr.t18x.ecc_stats.tex_unique_sec_pipe1_count, | ||
585 | dev_attr_tex_ecc_unique_sec_pipe1_count_array); | ||
586 | error |= ecc_stat_create(dev, | ||
587 | 0, | ||
588 | "tex_ecc_unique_ded_pipe1_count", | ||
589 | &g->gr.t18x.ecc_stats.tex_unique_ded_pipe1_count, | ||
590 | dev_attr_tex_ecc_unique_ded_pipe1_count_array); | ||
591 | |||
592 | error |= ecc_stat_create(dev, | ||
593 | 1, | ||
594 | "lts0_ecc_sec_count", | ||
595 | &g->gr.t18x.ecc_stats.l2_sec_count, | ||
596 | dev_attr_l2_ecc_sec_count_array); | ||
597 | error |= ecc_stat_create(dev, | ||
598 | 1, | ||
599 | "lts0_ecc_ded_count", | ||
600 | &g->gr.t18x.ecc_stats.l2_ded_count, | ||
601 | dev_attr_l2_ecc_ded_count_array); | ||
602 | |||
603 | if (error) | ||
604 | dev_err(&dev->dev, "Failed to create sysfs attributes!\n"); | ||
605 | } | ||
606 | |||
607 | static void gr_gp10b_remove_sysfs(struct device *dev) | ||
608 | { | ||
609 | struct platform_device *ndev = to_platform_device(dev); | ||
610 | struct gk20a *g = get_gk20a(ndev); | ||
611 | |||
612 | ecc_stat_remove(dev, | ||
613 | 0, | ||
614 | &g->gr.t18x.ecc_stats.sm_lrf_single_err_count, | ||
615 | dev_attr_sm_lrf_ecc_single_err_count_array); | ||
616 | ecc_stat_remove(dev, | ||
617 | 0, | ||
618 | &g->gr.t18x.ecc_stats.sm_lrf_double_err_count, | ||
619 | dev_attr_sm_lrf_ecc_double_err_count_array); | ||
620 | |||
621 | ecc_stat_remove(dev, | ||
622 | 0, | ||
623 | &g->gr.t18x.ecc_stats.sm_shm_sec_count, | ||
624 | dev_attr_sm_shm_ecc_sec_count_array); | ||
625 | ecc_stat_remove(dev, | ||
626 | 0, | ||
627 | &g->gr.t18x.ecc_stats.sm_shm_sed_count, | ||
628 | dev_attr_sm_shm_ecc_sed_count_array); | ||
629 | ecc_stat_remove(dev, | ||
630 | 0, | ||
631 | &g->gr.t18x.ecc_stats.sm_shm_ded_count, | ||
632 | dev_attr_sm_shm_ecc_ded_count_array); | ||
633 | |||
634 | ecc_stat_remove(dev, | ||
635 | 0, | ||
636 | &g->gr.t18x.ecc_stats.tex_total_sec_pipe0_count, | ||
637 | dev_attr_tex_ecc_total_sec_pipe0_count_array); | ||
638 | ecc_stat_remove(dev, | ||
639 | 0, | ||
640 | &g->gr.t18x.ecc_stats.tex_total_ded_pipe0_count, | ||
641 | dev_attr_tex_ecc_total_ded_pipe0_count_array); | ||
642 | ecc_stat_remove(dev, | ||
643 | 0, | ||
644 | &g->gr.t18x.ecc_stats.tex_unique_sec_pipe0_count, | ||
645 | dev_attr_tex_ecc_unique_sec_pipe0_count_array); | ||
646 | ecc_stat_remove(dev, | ||
647 | 0, | ||
648 | &g->gr.t18x.ecc_stats.tex_unique_ded_pipe0_count, | ||
649 | dev_attr_tex_ecc_unique_ded_pipe0_count_array); | ||
650 | ecc_stat_remove(dev, | ||
651 | 0, | ||
652 | &g->gr.t18x.ecc_stats.tex_total_sec_pipe1_count, | ||
653 | dev_attr_tex_ecc_total_sec_pipe1_count_array); | ||
654 | ecc_stat_remove(dev, | ||
655 | 0, | ||
656 | &g->gr.t18x.ecc_stats.tex_total_ded_pipe1_count, | ||
657 | dev_attr_tex_ecc_total_ded_pipe1_count_array); | ||
658 | ecc_stat_remove(dev, | ||
659 | 0, | ||
660 | &g->gr.t18x.ecc_stats.tex_unique_sec_pipe1_count, | ||
661 | dev_attr_tex_ecc_unique_sec_pipe1_count_array); | ||
662 | ecc_stat_remove(dev, | ||
663 | 0, | ||
664 | &g->gr.t18x.ecc_stats.tex_unique_ded_pipe1_count, | ||
665 | dev_attr_tex_ecc_unique_ded_pipe1_count_array); | ||
666 | |||
667 | ecc_stat_remove(dev, | ||
668 | 1, | ||
669 | &g->gr.t18x.ecc_stats.l2_sec_count, | ||
670 | dev_attr_l2_ecc_sec_count_array); | ||
671 | ecc_stat_remove(dev, | ||
672 | 1, | ||
673 | &g->gr.t18x.ecc_stats.l2_ded_count, | ||
674 | dev_attr_l2_ecc_ded_count_array); | ||
675 | } | ||