diff options
author | Loc Ho <lho@apm.com> | 2015-09-23 20:40:59 -0400 |
---|---|---|
committer | Borislav Petkov <bp@suse.de> | 2015-09-25 09:36:31 -0400 |
commit | 9347473c7d8218c795b5a73d8d94aa53657d9e29 (patch) | |
tree | a7132b1f4141363208ac9ee63099b7fb1291d7a1 /drivers/edac | |
parent | 3498355eec32cc8307655890916741e8a3419182 (diff) |
EDAC, xgene: Add L3 support
Add EDAC support for the L3 component.
Signed-off-by: Loc Ho <lho@apm.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: devicetree@vger.kernel.org
Cc: ijc+devicetree@hellion.org.uk
Cc: jcm@redhat.com
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-edac <linux-edac@vger.kernel.org>
Cc: mark.rutland@arm.com
Cc: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
Cc: patches@apm.com
Cc: robh+dt@kernel.org
Link: http://lkml.kernel.org/r/1443055261-8613-3-git-send-email-lho@apm.com
Signed-off-by: Borislav Petkov <bp@suse.de>
Diffstat (limited to 'drivers/edac')
-rw-r--r-- | drivers/edac/xgene_edac.c | 669 |
1 files changed, 474 insertions, 195 deletions
diff --git a/drivers/edac/xgene_edac.c b/drivers/edac/xgene_edac.c index 5ff42d5b019d..aee03c21632a 100644 --- a/drivers/edac/xgene_edac.c +++ b/drivers/edac/xgene_edac.c | |||
@@ -63,10 +63,11 @@ struct xgene_edac { | |||
63 | struct regmap *efuse_map; | 63 | struct regmap *efuse_map; |
64 | void __iomem *pcp_csr; | 64 | void __iomem *pcp_csr; |
65 | spinlock_t lock; | 65 | spinlock_t lock; |
66 | struct dentry *dfs; | 66 | struct dentry *dfs; |
67 | 67 | ||
68 | struct list_head mcus; | 68 | struct list_head mcus; |
69 | struct list_head pmds; | 69 | struct list_head pmds; |
70 | struct list_head l3s; | ||
70 | 71 | ||
71 | struct mutex mc_lock; | 72 | struct mutex mc_lock; |
72 | int mc_active_mask; | 73 | int mc_active_mask; |
@@ -537,140 +538,134 @@ static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev, | |||
537 | pg_f = ctx->pmd_csr + cpu_idx * CPU_CSR_STRIDE + CPU_MEMERR_CPU_PAGE; | 538 | pg_f = ctx->pmd_csr + cpu_idx * CPU_CSR_STRIDE + CPU_MEMERR_CPU_PAGE; |
538 | 539 | ||
539 | val = readl(pg_f + MEMERR_CPU_ICFESR_PAGE_OFFSET); | 540 | val = readl(pg_f + MEMERR_CPU_ICFESR_PAGE_OFFSET); |
540 | if (val) { | 541 | if (!val) |
541 | dev_err(edac_dev->dev, | 542 | goto chk_lsu; |
542 | "CPU%d L1 memory error ICF 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n", | 543 | dev_err(edac_dev->dev, |
543 | ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val, | 544 | "CPU%d L1 memory error ICF 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n", |
544 | MEMERR_CPU_ICFESR_ERRWAY_RD(val), | 545 | ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val, |
545 | MEMERR_CPU_ICFESR_ERRINDEX_RD(val), | 546 | MEMERR_CPU_ICFESR_ERRWAY_RD(val), |
546 | MEMERR_CPU_ICFESR_ERRINFO_RD(val)); | 547 | MEMERR_CPU_ICFESR_ERRINDEX_RD(val), |
547 | if (val & MEMERR_CPU_ICFESR_CERR_MASK) | 548 | MEMERR_CPU_ICFESR_ERRINFO_RD(val)); |
548 | dev_err(edac_dev->dev, | 549 | if (val & MEMERR_CPU_ICFESR_CERR_MASK) |
549 | "One or more correctable error\n"); | 550 | dev_err(edac_dev->dev, "One or more correctable error\n"); |
550 | if (val & MEMERR_CPU_ICFESR_MULTCERR_MASK) | 551 | if (val & MEMERR_CPU_ICFESR_MULTCERR_MASK) |
551 | dev_err(edac_dev->dev, "Multiple correctable error\n"); | 552 | dev_err(edac_dev->dev, "Multiple correctable error\n"); |
552 | switch (MEMERR_CPU_ICFESR_ERRTYPE_RD(val)) { | 553 | switch (MEMERR_CPU_ICFESR_ERRTYPE_RD(val)) { |
553 | case 1: | 554 | case 1: |
554 | dev_err(edac_dev->dev, "L1 TLB multiple hit\n"); | 555 | dev_err(edac_dev->dev, "L1 TLB multiple hit\n"); |
555 | break; | 556 | break; |
556 | case 2: | 557 | case 2: |
557 | dev_err(edac_dev->dev, "Way select multiple hit\n"); | 558 | dev_err(edac_dev->dev, "Way select multiple hit\n"); |
558 | break; | 559 | break; |
559 | case 3: | 560 | case 3: |
560 | dev_err(edac_dev->dev, "Physical tag parity error\n"); | 561 | dev_err(edac_dev->dev, "Physical tag parity error\n"); |
561 | break; | 562 | break; |
562 | case 4: | 563 | case 4: |
563 | case 5: | 564 | case 5: |
564 | dev_err(edac_dev->dev, "L1 data parity error\n"); | 565 | dev_err(edac_dev->dev, "L1 data parity error\n"); |
565 | break; | 566 | break; |
566 | case 6: | 567 | case 6: |
567 | dev_err(edac_dev->dev, "L1 pre-decode parity error\n"); | 568 | dev_err(edac_dev->dev, "L1 pre-decode parity error\n"); |
568 | break; | 569 | break; |
569 | } | 570 | } |
570 | 571 | ||
571 | /* Clear any HW errors */ | 572 | /* Clear any HW errors */ |
572 | writel(val, pg_f + MEMERR_CPU_ICFESR_PAGE_OFFSET); | 573 | writel(val, pg_f + MEMERR_CPU_ICFESR_PAGE_OFFSET); |
573 | 574 | ||
574 | if (val & (MEMERR_CPU_ICFESR_CERR_MASK | | 575 | if (val & (MEMERR_CPU_ICFESR_CERR_MASK | |
575 | MEMERR_CPU_ICFESR_MULTCERR_MASK)) | 576 | MEMERR_CPU_ICFESR_MULTCERR_MASK)) |
576 | edac_device_handle_ce(edac_dev, 0, 0, | 577 | edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name); |
577 | edac_dev->ctl_name); | ||
578 | } | ||
579 | 578 | ||
579 | chk_lsu: | ||
580 | val = readl(pg_f + MEMERR_CPU_LSUESR_PAGE_OFFSET); | 580 | val = readl(pg_f + MEMERR_CPU_LSUESR_PAGE_OFFSET); |
581 | if (val) { | 581 | if (!val) |
582 | goto chk_mmu; | ||
583 | dev_err(edac_dev->dev, | ||
584 | "CPU%d memory error LSU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n", | ||
585 | ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val, | ||
586 | MEMERR_CPU_LSUESR_ERRWAY_RD(val), | ||
587 | MEMERR_CPU_LSUESR_ERRINDEX_RD(val), | ||
588 | MEMERR_CPU_LSUESR_ERRINFO_RD(val)); | ||
589 | if (val & MEMERR_CPU_LSUESR_CERR_MASK) | ||
590 | dev_err(edac_dev->dev, "One or more correctable error\n"); | ||
591 | if (val & MEMERR_CPU_LSUESR_MULTCERR_MASK) | ||
592 | dev_err(edac_dev->dev, "Multiple correctable error\n"); | ||
593 | switch (MEMERR_CPU_LSUESR_ERRTYPE_RD(val)) { | ||
594 | case 0: | ||
595 | dev_err(edac_dev->dev, "Load tag error\n"); | ||
596 | break; | ||
597 | case 1: | ||
598 | dev_err(edac_dev->dev, "Load data error\n"); | ||
599 | break; | ||
600 | case 2: | ||
601 | dev_err(edac_dev->dev, "WSL multihit error\n"); | ||
602 | break; | ||
603 | case 3: | ||
604 | dev_err(edac_dev->dev, "Store tag error\n"); | ||
605 | break; | ||
606 | case 4: | ||
582 | dev_err(edac_dev->dev, | 607 | dev_err(edac_dev->dev, |
583 | "CPU%d memory error LSU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n", | 608 | "DTB multihit from load pipeline error\n"); |
584 | ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val, | 609 | break; |
585 | MEMERR_CPU_LSUESR_ERRWAY_RD(val), | 610 | case 5: |
586 | MEMERR_CPU_LSUESR_ERRINDEX_RD(val), | 611 | dev_err(edac_dev->dev, |
587 | MEMERR_CPU_LSUESR_ERRINFO_RD(val)); | 612 | "DTB multihit from store pipeline error\n"); |
588 | if (val & MEMERR_CPU_LSUESR_CERR_MASK) | 613 | break; |
589 | dev_err(edac_dev->dev, | 614 | } |
590 | "One or more correctable error\n"); | ||
591 | if (val & MEMERR_CPU_LSUESR_MULTCERR_MASK) | ||
592 | dev_err(edac_dev->dev, "Multiple correctable error\n"); | ||
593 | switch (MEMERR_CPU_LSUESR_ERRTYPE_RD(val)) { | ||
594 | case 0: | ||
595 | dev_err(edac_dev->dev, "Load tag error\n"); | ||
596 | break; | ||
597 | case 1: | ||
598 | dev_err(edac_dev->dev, "Load data error\n"); | ||
599 | break; | ||
600 | case 2: | ||
601 | dev_err(edac_dev->dev, "WSL multihit error\n"); | ||
602 | break; | ||
603 | case 3: | ||
604 | dev_err(edac_dev->dev, "Store tag error\n"); | ||
605 | break; | ||
606 | case 4: | ||
607 | dev_err(edac_dev->dev, | ||
608 | "DTB multihit from load pipeline error\n"); | ||
609 | break; | ||
610 | case 5: | ||
611 | dev_err(edac_dev->dev, | ||
612 | "DTB multihit from store pipeline error\n"); | ||
613 | break; | ||
614 | } | ||
615 | 615 | ||
616 | /* Clear any HW errors */ | 616 | /* Clear any HW errors */ |
617 | writel(val, pg_f + MEMERR_CPU_LSUESR_PAGE_OFFSET); | 617 | writel(val, pg_f + MEMERR_CPU_LSUESR_PAGE_OFFSET); |
618 | 618 | ||
619 | if (val & (MEMERR_CPU_LSUESR_CERR_MASK | | 619 | if (val & (MEMERR_CPU_LSUESR_CERR_MASK | |
620 | MEMERR_CPU_LSUESR_MULTCERR_MASK)) | 620 | MEMERR_CPU_LSUESR_MULTCERR_MASK)) |
621 | edac_device_handle_ce(edac_dev, 0, 0, | 621 | edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name); |
622 | edac_dev->ctl_name); | ||
623 | } | ||
624 | 622 | ||
623 | chk_mmu: | ||
625 | val = readl(pg_f + MEMERR_CPU_MMUESR_PAGE_OFFSET); | 624 | val = readl(pg_f + MEMERR_CPU_MMUESR_PAGE_OFFSET); |
626 | if (val) { | 625 | if (!val) |
627 | dev_err(edac_dev->dev, | 626 | return; |
628 | "CPU%d memory error MMU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X %s\n", | 627 | dev_err(edac_dev->dev, |
629 | ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val, | 628 | "CPU%d memory error MMU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X %s\n", |
630 | MEMERR_CPU_MMUESR_ERRWAY_RD(val), | 629 | ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val, |
631 | MEMERR_CPU_MMUESR_ERRINDEX_RD(val), | 630 | MEMERR_CPU_MMUESR_ERRWAY_RD(val), |
632 | MEMERR_CPU_MMUESR_ERRINFO_RD(val), | 631 | MEMERR_CPU_MMUESR_ERRINDEX_RD(val), |
633 | val & MEMERR_CPU_MMUESR_ERRREQSTR_LSU_MASK ? "LSU" : | 632 | MEMERR_CPU_MMUESR_ERRINFO_RD(val), |
634 | "ICF"); | 633 | val & MEMERR_CPU_MMUESR_ERRREQSTR_LSU_MASK ? "LSU" : "ICF"); |
635 | if (val & MEMERR_CPU_MMUESR_CERR_MASK) | 634 | if (val & MEMERR_CPU_MMUESR_CERR_MASK) |
636 | dev_err(edac_dev->dev, | 635 | dev_err(edac_dev->dev, "One or more correctable error\n"); |
637 | "One or more correctable error\n"); | 636 | if (val & MEMERR_CPU_MMUESR_MULTCERR_MASK) |
638 | if (val & MEMERR_CPU_MMUESR_MULTCERR_MASK) | 637 | dev_err(edac_dev->dev, "Multiple correctable error\n"); |
639 | dev_err(edac_dev->dev, "Multiple correctable error\n"); | 638 | switch (MEMERR_CPU_MMUESR_ERRTYPE_RD(val)) { |
640 | switch (MEMERR_CPU_MMUESR_ERRTYPE_RD(val)) { | 639 | case 0: |
641 | case 0: | 640 | dev_err(edac_dev->dev, "Stage 1 UTB hit error\n"); |
642 | dev_err(edac_dev->dev, "Stage 1 UTB hit error\n"); | 641 | break; |
643 | break; | 642 | case 1: |
644 | case 1: | 643 | dev_err(edac_dev->dev, "Stage 1 UTB miss error\n"); |
645 | dev_err(edac_dev->dev, "Stage 1 UTB miss error\n"); | 644 | break; |
646 | break; | 645 | case 2: |
647 | case 2: | 646 | dev_err(edac_dev->dev, "Stage 1 UTB allocate error\n"); |
648 | dev_err(edac_dev->dev, "Stage 1 UTB allocate error\n"); | 647 | break; |
649 | break; | 648 | case 3: |
650 | case 3: | 649 | dev_err(edac_dev->dev, "TMO operation single bank error\n"); |
651 | dev_err(edac_dev->dev, | 650 | break; |
652 | "TMO operation single bank error\n"); | 651 | case 4: |
653 | break; | 652 | dev_err(edac_dev->dev, "Stage 2 UTB error\n"); |
654 | case 4: | 653 | break; |
655 | dev_err(edac_dev->dev, "Stage 2 UTB error\n"); | 654 | case 5: |
656 | break; | 655 | dev_err(edac_dev->dev, "Stage 2 UTB miss error\n"); |
657 | case 5: | 656 | break; |
658 | dev_err(edac_dev->dev, "Stage 2 UTB miss error\n"); | 657 | case 6: |
659 | break; | 658 | dev_err(edac_dev->dev, "Stage 2 UTB allocate error\n"); |
660 | case 6: | 659 | break; |
661 | dev_err(edac_dev->dev, "Stage 2 UTB allocate error\n"); | 660 | case 7: |
662 | break; | 661 | dev_err(edac_dev->dev, "TMO operation multiple bank error\n"); |
663 | case 7: | 662 | break; |
664 | dev_err(edac_dev->dev, | 663 | } |
665 | "TMO operation multiple bank error\n"); | ||
666 | break; | ||
667 | } | ||
668 | 664 | ||
669 | /* Clear any HW errors */ | 665 | /* Clear any HW errors */ |
670 | writel(val, pg_f + MEMERR_CPU_MMUESR_PAGE_OFFSET); | 666 | writel(val, pg_f + MEMERR_CPU_MMUESR_PAGE_OFFSET); |
671 | 667 | ||
672 | edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name); | 668 | edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name); |
673 | } | ||
674 | } | 669 | } |
675 | 670 | ||
676 | static void xgene_edac_pmd_l2_check(struct edac_device_ctl_info *edac_dev) | 671 | static void xgene_edac_pmd_l2_check(struct edac_device_ctl_info *edac_dev) |
@@ -685,60 +680,56 @@ static void xgene_edac_pmd_l2_check(struct edac_device_ctl_info *edac_dev) | |||
685 | /* Check L2 */ | 680 | /* Check L2 */ |
686 | pg_e = ctx->pmd_csr + CPU_MEMERR_L2C_PAGE; | 681 | pg_e = ctx->pmd_csr + CPU_MEMERR_L2C_PAGE; |
687 | val = readl(pg_e + MEMERR_L2C_L2ESR_PAGE_OFFSET); | 682 | val = readl(pg_e + MEMERR_L2C_L2ESR_PAGE_OFFSET); |
688 | if (val) { | 683 | if (!val) |
689 | val_lo = readl(pg_e + MEMERR_L2C_L2EALR_PAGE_OFFSET); | 684 | goto chk_l2c; |
690 | val_hi = readl(pg_e + MEMERR_L2C_L2EAHR_PAGE_OFFSET); | 685 | val_lo = readl(pg_e + MEMERR_L2C_L2EALR_PAGE_OFFSET); |
691 | dev_err(edac_dev->dev, | 686 | val_hi = readl(pg_e + MEMERR_L2C_L2EAHR_PAGE_OFFSET); |
692 | "PMD%d memory error L2C L2ESR 0x%08X @ 0x%08X.%08X\n", | 687 | dev_err(edac_dev->dev, |
693 | ctx->pmd, val, val_hi, val_lo); | 688 | "PMD%d memory error L2C L2ESR 0x%08X @ 0x%08X.%08X\n", |
694 | dev_err(edac_dev->dev, | 689 | ctx->pmd, val, val_hi, val_lo); |
695 | "ErrSyndrome 0x%02X ErrWay 0x%02X ErrCpu %d ErrGroup 0x%02X ErrAction 0x%02X\n", | 690 | dev_err(edac_dev->dev, |
696 | MEMERR_L2C_L2ESR_ERRSYN_RD(val), | 691 | "ErrSyndrome 0x%02X ErrWay 0x%02X ErrCpu %d ErrGroup 0x%02X ErrAction 0x%02X\n", |
697 | MEMERR_L2C_L2ESR_ERRWAY_RD(val), | 692 | MEMERR_L2C_L2ESR_ERRSYN_RD(val), |
698 | MEMERR_L2C_L2ESR_ERRCPU_RD(val), | 693 | MEMERR_L2C_L2ESR_ERRWAY_RD(val), |
699 | MEMERR_L2C_L2ESR_ERRGROUP_RD(val), | 694 | MEMERR_L2C_L2ESR_ERRCPU_RD(val), |
700 | MEMERR_L2C_L2ESR_ERRACTION_RD(val)); | 695 | MEMERR_L2C_L2ESR_ERRGROUP_RD(val), |
701 | 696 | MEMERR_L2C_L2ESR_ERRACTION_RD(val)); | |
702 | if (val & MEMERR_L2C_L2ESR_ERR_MASK) | 697 | |
703 | dev_err(edac_dev->dev, | 698 | if (val & MEMERR_L2C_L2ESR_ERR_MASK) |
704 | "One or more correctable error\n"); | 699 | dev_err(edac_dev->dev, "One or more correctable error\n"); |
705 | if (val & MEMERR_L2C_L2ESR_MULTICERR_MASK) | 700 | if (val & MEMERR_L2C_L2ESR_MULTICERR_MASK) |
706 | dev_err(edac_dev->dev, "Multiple correctable error\n"); | 701 | dev_err(edac_dev->dev, "Multiple correctable error\n"); |
707 | if (val & MEMERR_L2C_L2ESR_UCERR_MASK) | 702 | if (val & MEMERR_L2C_L2ESR_UCERR_MASK) |
708 | dev_err(edac_dev->dev, | 703 | dev_err(edac_dev->dev, "One or more uncorrectable error\n"); |
709 | "One or more uncorrectable error\n"); | 704 | if (val & MEMERR_L2C_L2ESR_MULTUCERR_MASK) |
710 | if (val & MEMERR_L2C_L2ESR_MULTUCERR_MASK) | 705 | dev_err(edac_dev->dev, "Multiple uncorrectable error\n"); |
711 | dev_err(edac_dev->dev, | 706 | |
712 | "Multiple uncorrectable error\n"); | 707 | switch (MEMERR_L2C_L2ESR_ERRTYPE_RD(val)) { |
713 | 708 | case 0: | |
714 | switch (MEMERR_L2C_L2ESR_ERRTYPE_RD(val)) { | 709 | dev_err(edac_dev->dev, "Outbound SDB parity error\n"); |
715 | case 0: | 710 | break; |
716 | dev_err(edac_dev->dev, "Outbound SDB parity error\n"); | 711 | case 1: |
717 | break; | 712 | dev_err(edac_dev->dev, "Inbound SDB parity error\n"); |
718 | case 1: | 713 | break; |
719 | dev_err(edac_dev->dev, "Inbound SDB parity error\n"); | 714 | case 2: |
720 | break; | 715 | dev_err(edac_dev->dev, "Tag ECC error\n"); |
721 | case 2: | 716 | break; |
722 | dev_err(edac_dev->dev, "Tag ECC error\n"); | 717 | case 3: |
723 | break; | 718 | dev_err(edac_dev->dev, "Data ECC error\n"); |
724 | case 3: | 719 | break; |
725 | dev_err(edac_dev->dev, "Data ECC error\n"); | ||
726 | break; | ||
727 | } | ||
728 | |||
729 | /* Clear any HW errors */ | ||
730 | writel(val, pg_e + MEMERR_L2C_L2ESR_PAGE_OFFSET); | ||
731 | |||
732 | if (val & (MEMERR_L2C_L2ESR_ERR_MASK | | ||
733 | MEMERR_L2C_L2ESR_MULTICERR_MASK)) | ||
734 | edac_device_handle_ce(edac_dev, 0, 0, | ||
735 | edac_dev->ctl_name); | ||
736 | if (val & (MEMERR_L2C_L2ESR_UCERR_MASK | | ||
737 | MEMERR_L2C_L2ESR_MULTUCERR_MASK)) | ||
738 | edac_device_handle_ue(edac_dev, 0, 0, | ||
739 | edac_dev->ctl_name); | ||
740 | } | 720 | } |
741 | 721 | ||
722 | /* Clear any HW errors */ | ||
723 | writel(val, pg_e + MEMERR_L2C_L2ESR_PAGE_OFFSET); | ||
724 | |||
725 | if (val & (MEMERR_L2C_L2ESR_ERR_MASK | | ||
726 | MEMERR_L2C_L2ESR_MULTICERR_MASK)) | ||
727 | edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name); | ||
728 | if (val & (MEMERR_L2C_L2ESR_UCERR_MASK | | ||
729 | MEMERR_L2C_L2ESR_MULTUCERR_MASK)) | ||
730 | edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name); | ||
731 | |||
732 | chk_l2c: | ||
742 | /* Check if any memory request timed out on L2 cache */ | 733 | /* Check if any memory request timed out on L2 cache */ |
743 | pg_d = ctx->pmd_csr + CPU_L2C_PAGE; | 734 | pg_d = ctx->pmd_csr + CPU_L2C_PAGE; |
744 | val = readl(pg_d + CPUX_L2C_L2RTOSR_PAGE_OFFSET); | 735 | val = readl(pg_d + CPUX_L2C_L2RTOSR_PAGE_OFFSET); |
@@ -878,25 +869,16 @@ static const struct file_operations xgene_edac_pmd_debug_inject_fops[] = { | |||
878 | { } | 869 | { } |
879 | }; | 870 | }; |
880 | 871 | ||
881 | static void xgene_edac_pmd_create_debugfs_nodes( | 872 | static void |
882 | struct edac_device_ctl_info *edac_dev) | 873 | xgene_edac_pmd_create_debugfs_nodes(struct edac_device_ctl_info *edac_dev) |
883 | { | 874 | { |
884 | struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info; | 875 | struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info; |
885 | struct dentry *dbgfs_dir; | 876 | struct dentry *dbgfs_dir; |
886 | char name[30]; | 877 | char name[10]; |
887 | 878 | ||
888 | if (!IS_ENABLED(CONFIG_EDAC_DEBUG)) | 879 | if (!IS_ENABLED(CONFIG_EDAC_DEBUG) || !ctx->edac->dfs) |
889 | return; | 880 | return; |
890 | 881 | ||
891 | /* | ||
892 | * Todo: Switch to common EDAC debug file system for edac device | ||
893 | * when available. | ||
894 | */ | ||
895 | if (!ctx->edac->dfs) { | ||
896 | ctx->edac->dfs = edac_debugfs_create_dir(edac_dev->dev->kobj.name); | ||
897 | if (!ctx->edac->dfs) | ||
898 | return; | ||
899 | } | ||
900 | sprintf(name, "PMD%d", ctx->pmd); | 882 | sprintf(name, "PMD%d", ctx->pmd); |
901 | dbgfs_dir = edac_debugfs_create_dir_at(name, ctx->edac->dfs); | 883 | dbgfs_dir = edac_debugfs_create_dir_at(name, ctx->edac->dfs); |
902 | if (!dbgfs_dir) | 884 | if (!dbgfs_dir) |
@@ -1016,10 +998,294 @@ static int xgene_edac_pmd_remove(struct xgene_edac_pmd_ctx *pmd) | |||
1016 | return 0; | 998 | return 0; |
1017 | } | 999 | } |
1018 | 1000 | ||
1001 | /* L3 Error device */ | ||
1002 | #define L3C_ESR (0x0A * 4) | ||
1003 | #define L3C_ESR_DATATAG_MASK BIT(9) | ||
1004 | #define L3C_ESR_MULTIHIT_MASK BIT(8) | ||
1005 | #define L3C_ESR_UCEVICT_MASK BIT(6) | ||
1006 | #define L3C_ESR_MULTIUCERR_MASK BIT(5) | ||
1007 | #define L3C_ESR_MULTICERR_MASK BIT(4) | ||
1008 | #define L3C_ESR_UCERR_MASK BIT(3) | ||
1009 | #define L3C_ESR_CERR_MASK BIT(2) | ||
1010 | #define L3C_ESR_UCERRINTR_MASK BIT(1) | ||
1011 | #define L3C_ESR_CERRINTR_MASK BIT(0) | ||
1012 | #define L3C_ECR (0x0B * 4) | ||
1013 | #define L3C_ECR_UCINTREN BIT(3) | ||
1014 | #define L3C_ECR_CINTREN BIT(2) | ||
1015 | #define L3C_UCERREN BIT(1) | ||
1016 | #define L3C_CERREN BIT(0) | ||
1017 | #define L3C_ELR (0x0C * 4) | ||
1018 | #define L3C_ELR_ERRSYN(src) ((src & 0xFF800000) >> 23) | ||
1019 | #define L3C_ELR_ERRWAY(src) ((src & 0x007E0000) >> 17) | ||
1020 | #define L3C_ELR_AGENTID(src) ((src & 0x0001E000) >> 13) | ||
1021 | #define L3C_ELR_ERRGRP(src) ((src & 0x00000F00) >> 8) | ||
1022 | #define L3C_ELR_OPTYPE(src) ((src & 0x000000F0) >> 4) | ||
1023 | #define L3C_ELR_PADDRHIGH(src) (src & 0x0000000F) | ||
1024 | #define L3C_AELR (0x0D * 4) | ||
1025 | #define L3C_BELR (0x0E * 4) | ||
1026 | #define L3C_BELR_BANK(src) (src & 0x0000000F) | ||
1027 | |||
1028 | struct xgene_edac_dev_ctx { | ||
1029 | struct list_head next; | ||
1030 | struct device ddev; | ||
1031 | char *name; | ||
1032 | struct xgene_edac *edac; | ||
1033 | struct edac_device_ctl_info *edac_dev; | ||
1034 | int edac_idx; | ||
1035 | void __iomem *dev_csr; | ||
1036 | int version; | ||
1037 | }; | ||
1038 | |||
1039 | /* | ||
1040 | * Version 1 of the L3 controller has broken single bit correctable logic for | ||
1041 | * certain error syndromes. Log them as uncorrectable in that case. | ||
1042 | */ | ||
1043 | static bool xgene_edac_l3_promote_to_uc_err(u32 l3cesr, u32 l3celr) | ||
1044 | { | ||
1045 | if (l3cesr & L3C_ESR_DATATAG_MASK) { | ||
1046 | switch (L3C_ELR_ERRSYN(l3celr)) { | ||
1047 | case 0x13C: | ||
1048 | case 0x0B4: | ||
1049 | case 0x007: | ||
1050 | case 0x00D: | ||
1051 | case 0x00E: | ||
1052 | case 0x019: | ||
1053 | case 0x01A: | ||
1054 | case 0x01C: | ||
1055 | case 0x04E: | ||
1056 | case 0x041: | ||
1057 | return true; | ||
1058 | } | ||
1059 | } else if (L3C_ELR_ERRSYN(l3celr) == 9) | ||
1060 | return true; | ||
1061 | |||
1062 | return false; | ||
1063 | } | ||
1064 | |||
1065 | static void xgene_edac_l3_check(struct edac_device_ctl_info *edac_dev) | ||
1066 | { | ||
1067 | struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info; | ||
1068 | u32 l3cesr; | ||
1069 | u32 l3celr; | ||
1070 | u32 l3caelr; | ||
1071 | u32 l3cbelr; | ||
1072 | |||
1073 | l3cesr = readl(ctx->dev_csr + L3C_ESR); | ||
1074 | if (!(l3cesr & (L3C_ESR_UCERR_MASK | L3C_ESR_CERR_MASK))) | ||
1075 | return; | ||
1076 | |||
1077 | if (l3cesr & L3C_ESR_UCERR_MASK) | ||
1078 | dev_err(edac_dev->dev, "L3C uncorrectable error\n"); | ||
1079 | if (l3cesr & L3C_ESR_CERR_MASK) | ||
1080 | dev_warn(edac_dev->dev, "L3C correctable error\n"); | ||
1081 | |||
1082 | l3celr = readl(ctx->dev_csr + L3C_ELR); | ||
1083 | l3caelr = readl(ctx->dev_csr + L3C_AELR); | ||
1084 | l3cbelr = readl(ctx->dev_csr + L3C_BELR); | ||
1085 | if (l3cesr & L3C_ESR_MULTIHIT_MASK) | ||
1086 | dev_err(edac_dev->dev, "L3C multiple hit error\n"); | ||
1087 | if (l3cesr & L3C_ESR_UCEVICT_MASK) | ||
1088 | dev_err(edac_dev->dev, | ||
1089 | "L3C dropped eviction of line with error\n"); | ||
1090 | if (l3cesr & L3C_ESR_MULTIUCERR_MASK) | ||
1091 | dev_err(edac_dev->dev, "L3C multiple uncorrectable error\n"); | ||
1092 | if (l3cesr & L3C_ESR_DATATAG_MASK) | ||
1093 | dev_err(edac_dev->dev, | ||
1094 | "L3C data error syndrome 0x%X group 0x%X\n", | ||
1095 | L3C_ELR_ERRSYN(l3celr), L3C_ELR_ERRGRP(l3celr)); | ||
1096 | else | ||
1097 | dev_err(edac_dev->dev, | ||
1098 | "L3C tag error syndrome 0x%X Way of Tag 0x%X Agent ID 0x%X Operation type 0x%X\n", | ||
1099 | L3C_ELR_ERRSYN(l3celr), L3C_ELR_ERRWAY(l3celr), | ||
1100 | L3C_ELR_AGENTID(l3celr), L3C_ELR_OPTYPE(l3celr)); | ||
1101 | /* | ||
1102 | * NOTE: Address [41:38] in L3C_ELR_PADDRHIGH(l3celr). | ||
1103 | * Address [37:6] in l3caelr. Lower 6 bits are zero. | ||
1104 | */ | ||
1105 | dev_err(edac_dev->dev, "L3C error address 0x%08X.%08X bank %d\n", | ||
1106 | L3C_ELR_PADDRHIGH(l3celr) << 6 | (l3caelr >> 26), | ||
1107 | (l3caelr & 0x3FFFFFFF) << 6, L3C_BELR_BANK(l3cbelr)); | ||
1108 | dev_err(edac_dev->dev, | ||
1109 | "L3C error status register value 0x%X\n", l3cesr); | ||
1110 | |||
1111 | /* Clear L3C error interrupt */ | ||
1112 | writel(0, ctx->dev_csr + L3C_ESR); | ||
1113 | |||
1114 | if (ctx->version <= 1 && | ||
1115 | xgene_edac_l3_promote_to_uc_err(l3cesr, l3celr)) { | ||
1116 | edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name); | ||
1117 | return; | ||
1118 | } | ||
1119 | if (l3cesr & L3C_ESR_CERR_MASK) | ||
1120 | edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name); | ||
1121 | if (l3cesr & L3C_ESR_UCERR_MASK) | ||
1122 | edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name); | ||
1123 | } | ||
1124 | |||
1125 | static void xgene_edac_l3_hw_init(struct edac_device_ctl_info *edac_dev, | ||
1126 | bool enable) | ||
1127 | { | ||
1128 | struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info; | ||
1129 | u32 val; | ||
1130 | |||
1131 | val = readl(ctx->dev_csr + L3C_ECR); | ||
1132 | val |= L3C_UCERREN | L3C_CERREN; | ||
1133 | /* On disable, we just disable interrupt but keep error enabled */ | ||
1134 | if (edac_dev->op_state == OP_RUNNING_INTERRUPT) { | ||
1135 | if (enable) | ||
1136 | val |= L3C_ECR_UCINTREN | L3C_ECR_CINTREN; | ||
1137 | else | ||
1138 | val &= ~(L3C_ECR_UCINTREN | L3C_ECR_CINTREN); | ||
1139 | } | ||
1140 | writel(val, ctx->dev_csr + L3C_ECR); | ||
1141 | |||
1142 | if (edac_dev->op_state == OP_RUNNING_INTERRUPT) { | ||
1143 | /* Enable/disable L3 error top level interrupt */ | ||
1144 | if (enable) { | ||
1145 | xgene_edac_pcp_clrbits(ctx->edac, PCPHPERRINTMSK, | ||
1146 | L3C_UNCORR_ERR_MASK); | ||
1147 | xgene_edac_pcp_clrbits(ctx->edac, PCPLPERRINTMSK, | ||
1148 | L3C_CORR_ERR_MASK); | ||
1149 | } else { | ||
1150 | xgene_edac_pcp_setbits(ctx->edac, PCPHPERRINTMSK, | ||
1151 | L3C_UNCORR_ERR_MASK); | ||
1152 | xgene_edac_pcp_setbits(ctx->edac, PCPLPERRINTMSK, | ||
1153 | L3C_CORR_ERR_MASK); | ||
1154 | } | ||
1155 | } | ||
1156 | } | ||
1157 | |||
1158 | static ssize_t xgene_edac_l3_inject_ctrl_write(struct file *file, | ||
1159 | const char __user *data, | ||
1160 | size_t count, loff_t *ppos) | ||
1161 | { | ||
1162 | struct edac_device_ctl_info *edac_dev = file->private_data; | ||
1163 | struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info; | ||
1164 | |||
1165 | /* Generate all errors */ | ||
1166 | writel(0xFFFFFFFF, ctx->dev_csr + L3C_ESR); | ||
1167 | return count; | ||
1168 | } | ||
1169 | |||
1170 | static const struct file_operations xgene_edac_l3_debug_inject_fops = { | ||
1171 | .open = simple_open, | ||
1172 | .write = xgene_edac_l3_inject_ctrl_write, | ||
1173 | .llseek = generic_file_llseek | ||
1174 | }; | ||
1175 | |||
1176 | static void | ||
1177 | xgene_edac_l3_create_debugfs_nodes(struct edac_device_ctl_info *edac_dev) | ||
1178 | { | ||
1179 | struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info; | ||
1180 | struct dentry *dbgfs_dir; | ||
1181 | char name[10]; | ||
1182 | |||
1183 | if (!IS_ENABLED(CONFIG_EDAC_DEBUG) || !ctx->edac->dfs) | ||
1184 | return; | ||
1185 | |||
1186 | snprintf(name, sizeof(name), "l3c%d", ctx->edac_idx); | ||
1187 | dbgfs_dir = edac_debugfs_create_dir_at(name, ctx->edac->dfs); | ||
1188 | if (!dbgfs_dir) | ||
1189 | return; | ||
1190 | |||
1191 | debugfs_create_file("l3_inject_ctrl", S_IWUSR, dbgfs_dir, edac_dev, | ||
1192 | &xgene_edac_l3_debug_inject_fops); | ||
1193 | } | ||
1194 | |||
1195 | static int xgene_edac_l3_add(struct xgene_edac *edac, struct device_node *np, | ||
1196 | int version) | ||
1197 | { | ||
1198 | struct edac_device_ctl_info *edac_dev; | ||
1199 | struct xgene_edac_dev_ctx *ctx; | ||
1200 | struct resource res; | ||
1201 | void __iomem *dev_csr; | ||
1202 | int edac_idx; | ||
1203 | int rc = 0; | ||
1204 | |||
1205 | if (!devres_open_group(edac->dev, xgene_edac_l3_add, GFP_KERNEL)) | ||
1206 | return -ENOMEM; | ||
1207 | |||
1208 | rc = of_address_to_resource(np, 0, &res); | ||
1209 | if (rc < 0) { | ||
1210 | dev_err(edac->dev, "no L3 resource address\n"); | ||
1211 | goto err_release_group; | ||
1212 | } | ||
1213 | dev_csr = devm_ioremap_resource(edac->dev, &res); | ||
1214 | if (IS_ERR(dev_csr)) { | ||
1215 | dev_err(edac->dev, | ||
1216 | "devm_ioremap_resource failed for L3 resource address\n"); | ||
1217 | rc = PTR_ERR(dev_csr); | ||
1218 | goto err_release_group; | ||
1219 | } | ||
1220 | |||
1221 | edac_idx = edac_device_alloc_index(); | ||
1222 | edac_dev = edac_device_alloc_ctl_info(sizeof(*ctx), | ||
1223 | "l3c", 1, "l3c", 1, 0, NULL, 0, | ||
1224 | edac_idx); | ||
1225 | if (!edac_dev) { | ||
1226 | rc = -ENOMEM; | ||
1227 | goto err_release_group; | ||
1228 | } | ||
1229 | |||
1230 | ctx = edac_dev->pvt_info; | ||
1231 | ctx->dev_csr = dev_csr; | ||
1232 | ctx->name = "xgene_l3_err"; | ||
1233 | ctx->edac_idx = edac_idx; | ||
1234 | ctx->edac = edac; | ||
1235 | ctx->edac_dev = edac_dev; | ||
1236 | ctx->ddev = *edac->dev; | ||
1237 | ctx->version = version; | ||
1238 | edac_dev->dev = &ctx->ddev; | ||
1239 | edac_dev->ctl_name = ctx->name; | ||
1240 | edac_dev->dev_name = ctx->name; | ||
1241 | edac_dev->mod_name = EDAC_MOD_STR; | ||
1242 | |||
1243 | if (edac_op_state == EDAC_OPSTATE_POLL) | ||
1244 | edac_dev->edac_check = xgene_edac_l3_check; | ||
1245 | |||
1246 | xgene_edac_l3_create_debugfs_nodes(edac_dev); | ||
1247 | |||
1248 | rc = edac_device_add_device(edac_dev); | ||
1249 | if (rc > 0) { | ||
1250 | dev_err(edac->dev, "failed edac_device_add_device()\n"); | ||
1251 | rc = -ENOMEM; | ||
1252 | goto err_ctl_free; | ||
1253 | } | ||
1254 | |||
1255 | if (edac_op_state == EDAC_OPSTATE_INT) | ||
1256 | edac_dev->op_state = OP_RUNNING_INTERRUPT; | ||
1257 | |||
1258 | list_add(&ctx->next, &edac->l3s); | ||
1259 | |||
1260 | xgene_edac_l3_hw_init(edac_dev, 1); | ||
1261 | |||
1262 | devres_remove_group(edac->dev, xgene_edac_l3_add); | ||
1263 | |||
1264 | dev_info(edac->dev, "X-Gene EDAC L3 registered\n"); | ||
1265 | return 0; | ||
1266 | |||
1267 | err_ctl_free: | ||
1268 | edac_device_free_ctl_info(edac_dev); | ||
1269 | err_release_group: | ||
1270 | devres_release_group(edac->dev, xgene_edac_l3_add); | ||
1271 | return rc; | ||
1272 | } | ||
1273 | |||
1274 | static int xgene_edac_l3_remove(struct xgene_edac_dev_ctx *l3) | ||
1275 | { | ||
1276 | struct edac_device_ctl_info *edac_dev = l3->edac_dev; | ||
1277 | |||
1278 | xgene_edac_l3_hw_init(edac_dev, 0); | ||
1279 | edac_device_del_device(l3->edac->dev); | ||
1280 | edac_device_free_ctl_info(edac_dev); | ||
1281 | return 0; | ||
1282 | } | ||
1283 | |||
1019 | static irqreturn_t xgene_edac_isr(int irq, void *dev_id) | 1284 | static irqreturn_t xgene_edac_isr(int irq, void *dev_id) |
1020 | { | 1285 | { |
1021 | struct xgene_edac *ctx = dev_id; | 1286 | struct xgene_edac *ctx = dev_id; |
1022 | struct xgene_edac_pmd_ctx *pmd; | 1287 | struct xgene_edac_pmd_ctx *pmd; |
1288 | struct xgene_edac_dev_ctx *node; | ||
1023 | unsigned int pcp_hp_stat; | 1289 | unsigned int pcp_hp_stat; |
1024 | unsigned int pcp_lp_stat; | 1290 | unsigned int pcp_lp_stat; |
1025 | 1291 | ||
@@ -1030,9 +1296,8 @@ static irqreturn_t xgene_edac_isr(int irq, void *dev_id) | |||
1030 | (MCU_CORR_ERR_MASK & pcp_lp_stat)) { | 1296 | (MCU_CORR_ERR_MASK & pcp_lp_stat)) { |
1031 | struct xgene_edac_mc_ctx *mcu; | 1297 | struct xgene_edac_mc_ctx *mcu; |
1032 | 1298 | ||
1033 | list_for_each_entry(mcu, &ctx->mcus, next) { | 1299 | list_for_each_entry(mcu, &ctx->mcus, next) |
1034 | xgene_edac_mc_check(mcu->mci); | 1300 | xgene_edac_mc_check(mcu->mci); |
1035 | } | ||
1036 | } | 1301 | } |
1037 | 1302 | ||
1038 | list_for_each_entry(pmd, &ctx->pmds, next) { | 1303 | list_for_each_entry(pmd, &ctx->pmds, next) { |
@@ -1040,6 +1305,9 @@ static irqreturn_t xgene_edac_isr(int irq, void *dev_id) | |||
1040 | xgene_edac_pmd_check(pmd->edac_dev); | 1305 | xgene_edac_pmd_check(pmd->edac_dev); |
1041 | } | 1306 | } |
1042 | 1307 | ||
1308 | list_for_each_entry(node, &ctx->l3s, next) | ||
1309 | xgene_edac_l3_check(node->edac_dev); | ||
1310 | |||
1043 | return IRQ_HANDLED; | 1311 | return IRQ_HANDLED; |
1044 | } | 1312 | } |
1045 | 1313 | ||
@@ -1058,6 +1326,7 @@ static int xgene_edac_probe(struct platform_device *pdev) | |||
1058 | platform_set_drvdata(pdev, edac); | 1326 | platform_set_drvdata(pdev, edac); |
1059 | INIT_LIST_HEAD(&edac->mcus); | 1327 | INIT_LIST_HEAD(&edac->mcus); |
1060 | INIT_LIST_HEAD(&edac->pmds); | 1328 | INIT_LIST_HEAD(&edac->pmds); |
1329 | INIT_LIST_HEAD(&edac->l3s); | ||
1061 | spin_lock_init(&edac->lock); | 1330 | spin_lock_init(&edac->lock); |
1062 | mutex_init(&edac->mc_lock); | 1331 | mutex_init(&edac->mc_lock); |
1063 | 1332 | ||
@@ -1122,6 +1391,8 @@ static int xgene_edac_probe(struct platform_device *pdev) | |||
1122 | } | 1391 | } |
1123 | } | 1392 | } |
1124 | 1393 | ||
1394 | edac->dfs = edac_debugfs_create_dir(pdev->dev.kobj.name); | ||
1395 | |||
1125 | for_each_child_of_node(pdev->dev.of_node, child) { | 1396 | for_each_child_of_node(pdev->dev.of_node, child) { |
1126 | if (!of_device_is_available(child)) | 1397 | if (!of_device_is_available(child)) |
1127 | continue; | 1398 | continue; |
@@ -1131,6 +1402,10 @@ static int xgene_edac_probe(struct platform_device *pdev) | |||
1131 | xgene_edac_pmd_add(edac, child, 1); | 1402 | xgene_edac_pmd_add(edac, child, 1); |
1132 | if (of_device_is_compatible(child, "apm,xgene-edac-pmd-v2")) | 1403 | if (of_device_is_compatible(child, "apm,xgene-edac-pmd-v2")) |
1133 | xgene_edac_pmd_add(edac, child, 2); | 1404 | xgene_edac_pmd_add(edac, child, 2); |
1405 | if (of_device_is_compatible(child, "apm,xgene-edac-l3")) | ||
1406 | xgene_edac_l3_add(edac, child, 1); | ||
1407 | if (of_device_is_compatible(child, "apm,xgene-edac-l3-v2")) | ||
1408 | xgene_edac_l3_add(edac, child, 2); | ||
1134 | } | 1409 | } |
1135 | 1410 | ||
1136 | return 0; | 1411 | return 0; |
@@ -1146,14 +1421,18 @@ static int xgene_edac_remove(struct platform_device *pdev) | |||
1146 | struct xgene_edac_mc_ctx *temp_mcu; | 1421 | struct xgene_edac_mc_ctx *temp_mcu; |
1147 | struct xgene_edac_pmd_ctx *pmd; | 1422 | struct xgene_edac_pmd_ctx *pmd; |
1148 | struct xgene_edac_pmd_ctx *temp_pmd; | 1423 | struct xgene_edac_pmd_ctx *temp_pmd; |
1424 | struct xgene_edac_dev_ctx *node; | ||
1425 | struct xgene_edac_dev_ctx *temp_node; | ||
1149 | 1426 | ||
1150 | list_for_each_entry_safe(mcu, temp_mcu, &edac->mcus, next) { | 1427 | list_for_each_entry_safe(mcu, temp_mcu, &edac->mcus, next) |
1151 | xgene_edac_mc_remove(mcu); | 1428 | xgene_edac_mc_remove(mcu); |
1152 | } | ||
1153 | 1429 | ||
1154 | list_for_each_entry_safe(pmd, temp_pmd, &edac->pmds, next) { | 1430 | list_for_each_entry_safe(pmd, temp_pmd, &edac->pmds, next) |
1155 | xgene_edac_pmd_remove(pmd); | 1431 | xgene_edac_pmd_remove(pmd); |
1156 | } | 1432 | |
1433 | list_for_each_entry_safe(node, temp_node, &edac->l3s, next) | ||
1434 | xgene_edac_l3_remove(node); | ||
1435 | |||
1157 | return 0; | 1436 | return 0; |
1158 | } | 1437 | } |
1159 | 1438 | ||