EDAC, amd64: Add Fam17h debug output
authorYazen Ghannam <Yazen.Ghannam@amd.com>
Mon, 28 Nov 2016 14:50:21 +0000 (08:50 -0600)
committerBorislav Petkov <bp@suse.de>
Tue, 29 Nov 2016 16:16:09 +0000 (17:16 +0100)
Read a few more UMC registers and provide debug output in order to be as
similar as possible to older AMD systems.

Signed-off-by: Yazen Ghannam <Yazen.Ghannam@amd.com>
Cc: Aravind Gopalakrishnan <aravindksg.lkml@gmail.com>
Cc: linux-edac <linux-edac@vger.kernel.org>
Cc: x86-ml <x86@kernel.org>
Link: http://lkml.kernel.org/r/1480344621-14966-1-git-send-email-Yazen.Ghannam@amd.com
[ Remove unneeded K8 check and comments, fixup others. ]
Signed-off-by: Borislav Petkov <bp@suse.de>
drivers/edac/amd64_edac.c
drivers/edac/amd64_edac.h

index 52ae415aa69977def44ea46b130c3897ff87c91c..fdd963794cdb529f9dcfe9a17a1abfc9d3ecce38 100644 (file)
@@ -762,8 +762,75 @@ static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan)
                 (dclr & BIT(15)) ?  "yes" : "no");
 }
 
+static void debug_display_dimm_sizes_df(struct amd64_pvt *pvt, u8 ctrl)
+{
+       u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases;
+       int dimm, size0, size1;
+
+       edac_printk(KERN_DEBUG, EDAC_MC, "UMC%d chip selects:\n", ctrl);
+
+       for (dimm = 0; dimm < 4; dimm++) {
+               size0 = 0;
+
+               if (dcsb[dimm*2] & DCSB_CS_ENABLE)
+                       size0 = pvt->ops->dbam_to_cs(pvt, ctrl, 0, dimm);
+
+               size1 = 0;
+               if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE)
+                       size1 = pvt->ops->dbam_to_cs(pvt, ctrl, 0, dimm);
+
+               amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
+                               dimm * 2,     size0,
+                               dimm * 2 + 1, size1);
+       }
+}
+
+static void __dump_misc_regs_df(struct amd64_pvt *pvt)
+{
+       struct amd64_umc *umc;
+       u32 i, tmp, umc_base;
+
+       for (i = 0; i < NUM_UMCS; i++) {
+               umc_base = get_umc_base(i);
+               umc = &pvt->umc[i];
+
+               edac_dbg(1, "UMC%d DIMM cfg: 0x%x\n", i, umc->dimm_cfg);
+               edac_dbg(1, "UMC%d UMC cfg: 0x%x\n", i, umc->umc_cfg);
+               edac_dbg(1, "UMC%d SDP ctrl: 0x%x\n", i, umc->sdp_ctrl);
+               edac_dbg(1, "UMC%d ECC ctrl: 0x%x\n", i, umc->ecc_ctrl);
+
+               amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ECC_BAD_SYMBOL, &tmp);
+               edac_dbg(1, "UMC%d ECC bad symbol: 0x%x\n", i, tmp);
+
+               amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_UMC_CAP, &tmp);
+               edac_dbg(1, "UMC%d UMC cap: 0x%x\n", i, tmp);
+               edac_dbg(1, "UMC%d UMC cap high: 0x%x\n", i, umc->umc_cap_hi);
+
+               edac_dbg(1, "UMC%d ECC capable: %s, ChipKill ECC capable: %s\n",
+                               i, (umc->umc_cap_hi & BIT(30)) ? "yes" : "no",
+                                   (umc->umc_cap_hi & BIT(31)) ? "yes" : "no");
+               edac_dbg(1, "UMC%d All DIMMs support ECC: %s\n",
+                               i, (umc->umc_cfg & BIT(12)) ? "yes" : "no");
+               edac_dbg(1, "UMC%d x4 DIMMs present: %s\n",
+                               i, (umc->dimm_cfg & BIT(6)) ? "yes" : "no");
+               edac_dbg(1, "UMC%d x16 DIMMs present: %s\n",
+                               i, (umc->dimm_cfg & BIT(7)) ? "yes" : "no");
+
+               if (pvt->dram_type == MEM_LRDDR4) {
+                       amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ADDR_CFG, &tmp);
+                       edac_dbg(1, "UMC%d LRDIMM %dx rank multiply\n",
+                                       i, 1 << ((tmp >> 4) & 0x3));
+               }
+
+               debug_display_dimm_sizes_df(pvt, i);
+       }
+
+       edac_dbg(1, "F0x104 (DRAM Hole Address): 0x%08x, base: 0x%08x\n",
+                pvt->dhar, dhar_base(pvt));
+}
+
 /* Display and decode various NB registers for debug purposes. */
-static void dump_misc_regs(struct amd64_pvt *pvt)
+static void __dump_misc_regs(struct amd64_pvt *pvt)
 {
        edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
 
@@ -783,8 +850,6 @@ static void dump_misc_regs(struct amd64_pvt *pvt)
                 (pvt->fam == 0xf) ? k8_dhar_offset(pvt)
                                   : f10_dhar_offset(pvt));
 
-       edac_dbg(1, "  DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
-
        debug_display_dimm_sizes(pvt, 0);
 
        /* everything below this point is Fam10h and above */
@@ -793,13 +858,25 @@ static void dump_misc_regs(struct amd64_pvt *pvt)
 
        debug_display_dimm_sizes(pvt, 1);
 
-       amd64_info("using %s syndromes.\n", ((pvt->ecc_sym_sz == 8) ? "x8" : "x4"));
-
        /* Only if NOT ganged does dclr1 have valid info */
        if (!dct_ganging_enabled(pvt))
                debug_dump_dramcfg_low(pvt, pvt->dclr1, 1);
 }
 
+/* Display and decode various NB registers for debug purposes. */
+static void dump_misc_regs(struct amd64_pvt *pvt)
+{
+       if (pvt->umc)
+               __dump_misc_regs_df(pvt);
+       else
+               __dump_misc_regs(pvt);
+
+       edac_dbg(1, "  DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
+
+       amd64_info("using %s syndromes.\n",
+                       ((pvt->ecc_sym_sz == 8) ? "x8" : "x4"));
+}
+
 /*
  * See BKDG, F2x[1,0][5C:40], F2[1,0][6C:60]
  */
@@ -2001,8 +2078,9 @@ static void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
 
                size0 = 0;
                if (dcsb[dimm*2] & DCSB_CS_ENABLE)
-                       /* For f15m60h, need multiplier for LRDIMM cs_size
-                        * calculation. We pass 'dimm' value to the dbam_to_cs
+                       /*
+                        * For F15m60h, we need multiplier for LRDIMM cs_size
+                        * calculation. We pass dimm value to the dbam_to_cs
                         * mapper so we can find the multiplier from the
                         * corresponding DCSM.
                         */
@@ -2463,9 +2541,11 @@ static void __read_mc_regs_df(struct amd64_pvt *pvt)
                umc_base = get_umc_base(i);
                umc = &pvt->umc[i];
 
+               amd_smn_read(nid, umc_base + UMCCH_DIMM_CFG, &umc->dimm_cfg);
+               amd_smn_read(nid, umc_base + UMCCH_UMC_CFG, &umc->umc_cfg);
                amd_smn_read(nid, umc_base + UMCCH_SDP_CTRL, &umc->sdp_ctrl);
                amd_smn_read(nid, umc_base + UMCCH_ECC_CTRL, &umc->ecc_ctrl);
-               amd_smn_read(nid, umc_base + UMCCH_DIMM_CFG, &umc->dimm_cfg);
+               amd_smn_read(nid, umc_base + UMCCH_UMC_CAP_HI, &umc->umc_cap_hi);
        }
 }
 
index 34d915782d4a586f3b1bca4cd6ce68bb432e009d..cb91d0b06d23ede2a63413e81394cbcf9922ca64 100644 (file)
 /* UMC CH register offsets */
 #define UMCCH_BASE_ADDR                        0x0
 #define UMCCH_ADDR_MASK                        0x20
+#define UMCCH_ADDR_CFG                 0x30
 #define UMCCH_DIMM_CFG                 0x80
+#define UMCCH_UMC_CFG                  0x100
 #define UMCCH_SDP_CTRL                 0x104
 #define UMCCH_ECC_CTRL                 0x14C
+#define UMCCH_ECC_BAD_SYMBOL           0xD90
+#define UMCCH_UMC_CAP                  0xDF0
 #define UMCCH_UMC_CAP_HI               0xDF4
 
 /* UMC CH bitfields */
@@ -316,8 +320,10 @@ struct chip_select {
 
 struct amd64_umc {
        u32 dimm_cfg;           /* DIMM Configuration reg */
+       u32 umc_cfg;            /* Configuration reg */
        u32 sdp_ctrl;           /* SDP Control reg */
        u32 ecc_ctrl;           /* DRAM ECC Control reg */
+       u32 umc_cap_hi;         /* Capabilities High reg */
 };
 
 struct amd64_pvt {