summaryrefslogtreecommitdiffstats
path: root/drivers/edac
diff options
context:
space:
mode:
authorYazen Ghannam <Yazen.Ghannam@amd.com>2016-11-29 08:51:56 -0600
committerBorislav Petkov <bp@suse.de>2016-11-29 18:04:54 +0100
commit2d09d8f301f53cb92e7ae7183d58a74fc55f85c0 (patch)
tree027fde1139a22f7bcd794ca7b6157ce84e1eed8a /drivers/edac
parent07ed82ef93d6c70dcd1f31429a8fd12fbdeb21fd (diff)
downloadlinux-2d09d8f301f53cb92e7ae7183d58a74fc55f85c0.tar.bz2
EDAC, amd64: Determine EDAC MC capabilities on Fam17h
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>
Diffstat (limited to 'drivers/edac')
-rw-r--r--drivers/edac/amd64_edac.c67
1 files changed, 49 insertions, 18 deletions
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index fdd963794cdb..9f9d2bc1868c 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -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;