EDAC, amd64: Determine EDAC MC capabilities on Fam17h
authorYazen Ghannam <Yazen.Ghannam@amd.com>
Tue, 29 Nov 2016 14:51:56 +0000 (08:51 -0600)
committerBorislav Petkov <bp@suse.de>
Tue, 29 Nov 2016 17:04:54 +0000 (18:04 +0100)
The UMCs on Fam17h are independent memory controllers so we need to
read the capabilities from all UMCs and make sure they agree. Once
we determine what capabilities are available we should save them for
convenience.

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/1480431116-94683-1-git-send-email-Yazen.Ghannam@amd.com
[ Simplify f17h_determine_edac_ctl_cap(), preinit edac_mode in init_csrows(). ]
Signed-off-by: Borislav Petkov <bp@suse.de>
drivers/edac/amd64_edac.c

index fdd9637..9f9d2bc 100644 (file)
@@ -2698,20 +2698,22 @@ static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr)
 static int init_csrows(struct mem_ctl_info *mci)
 {
        struct amd64_pvt *pvt = mci->pvt_info;
+       enum edac_type edac_mode = EDAC_NONE;
        struct csrow_info *csrow;
        struct dimm_info *dimm;
-       enum edac_type edac_mode;
        int i, j, empty = 1;
        int nr_pages = 0;
        u32 val;
 
-       amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
+       if (!pvt->umc) {
+               amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
 
-       pvt->nbcfg = val;
+               pvt->nbcfg = val;
 
-       edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
-                pvt->mc_node_id, val,
-                !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
+               edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
+                        pvt->mc_node_id, val,
+                        !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
+       }
 
        /*
         * We iterate over DCT0 here but we look at DCT1 in parallel, if needed.
@@ -2747,14 +2749,18 @@ static int init_csrows(struct mem_ctl_info *mci)
 
                edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages);
 
-               /*
-                * determine whether CHIPKILL or JUST ECC or NO ECC is operating
-                */
-               if (pvt->nbcfg & NBCFG_ECC_ENABLE)
-                       edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) ?
-                                   EDAC_S4ECD4ED : EDAC_SECDED;
-               else
-                       edac_mode = EDAC_NONE;
+               /* Determine DIMM ECC mode: */
+               if (pvt->umc) {
+                       if (mci->edac_ctl_cap & EDAC_FLAG_S4ECD4ED)
+                               edac_mode = EDAC_S4ECD4ED;
+                       else if (mci->edac_ctl_cap & EDAC_FLAG_SECDED)
+                               edac_mode = EDAC_SECDED;
+
+               } else if (pvt->nbcfg & NBCFG_ECC_ENABLE) {
+                       edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL)
+                                       ? EDAC_S4ECD4ED
+                                       : EDAC_SECDED;
+               }
 
                for (j = 0; j < pvt->channel_count; j++) {
                        dimm = csrow->channels[j]->dimm;
@@ -2992,6 +2998,27 @@ static bool ecc_enabled(struct pci_dev *F3, u16 nid)
        return true;
 }
 
+static inline void
+f17h_determine_edac_ctl_cap(struct mem_ctl_info *mci, struct amd64_pvt *pvt)
+{
+       u8 i, ecc_en = 1, cpk_en = 1;
+
+       for (i = 0; i < NUM_UMCS; i++) {
+               if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) {
+                       ecc_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_ENABLED);
+                       cpk_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_CHIPKILL_CAP);
+               }
+       }
+
+       /* Set chipkill only if ECC is enabled: */
+       if (ecc_en) {
+               mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
+
+               if (cpk_en)
+                       mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
+       }
+}
+
 static void setup_mci_misc_attrs(struct mem_ctl_info *mci,
                                 struct amd64_family_type *fam)
 {
@@ -3000,11 +3027,15 @@ static void setup_mci_misc_attrs(struct mem_ctl_info *mci,
        mci->mtype_cap          = MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
        mci->edac_ctl_cap       = EDAC_FLAG_NONE;
 
-       if (pvt->nbcap & NBCAP_SECDED)
-               mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
+       if (pvt->umc) {
+               f17h_determine_edac_ctl_cap(mci, pvt);
+       } else {
+               if (pvt->nbcap & NBCAP_SECDED)
+                       mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
 
-       if (pvt->nbcap & NBCAP_CHIPKILL)
-               mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
+               if (pvt->nbcap & NBCAP_CHIPKILL)
+                       mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
+       }
 
        mci->edac_cap           = determine_edac_cap(pvt);
        mci->mod_name           = EDAC_MOD_STR;