Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / drivers / edac / amd64_edac.c
index 873437be86d9c2d571953ef7624c06a0bb868f78..c1d4536ae466e7b3d683ecb0741baf7e7f653407 100644 (file)
@@ -788,51 +788,45 @@ static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan)
                 (dclr & BIT(15)) ?  "yes" : "no");
 }
 
-/*
- * The Address Mask should be a contiguous set of bits in the non-interleaved
- * case. So to check for CS interleaving, find the most- and least-significant
- * bits of the mask, generate a contiguous bitmask, and compare the two.
- */
-static bool f17_cs_interleaved(struct amd64_pvt *pvt, u8 ctrl, int cs)
+#define CS_EVEN_PRIMARY                BIT(0)
+#define CS_ODD_PRIMARY         BIT(1)
+#define CS_EVEN_SECONDARY      BIT(2)
+#define CS_ODD_SECONDARY       BIT(3)
+
+#define CS_EVEN                        (CS_EVEN_PRIMARY | CS_EVEN_SECONDARY)
+#define CS_ODD                 (CS_ODD_PRIMARY | CS_ODD_SECONDARY)
+
+static int f17_get_cs_mode(int dimm, u8 ctrl, struct amd64_pvt *pvt)
 {
-       u32 mask = pvt->csels[ctrl].csmasks[cs >> 1];
-       u32 msb = fls(mask) - 1, lsb = ffs(mask) - 1;
-       u32 test_mask = GENMASK(msb, lsb);
+       int cs_mode = 0;
 
-       edac_dbg(1, "mask=0x%08x test_mask=0x%08x\n", mask, test_mask);
+       if (csrow_enabled(2 * dimm, ctrl, pvt))
+               cs_mode |= CS_EVEN_PRIMARY;
 
-       return mask ^ test_mask;
+       if (csrow_enabled(2 * dimm + 1, ctrl, pvt))
+               cs_mode |= CS_ODD_PRIMARY;
+
+       /* Asymmetric dual-rank DIMM support. */
+       if (csrow_sec_enabled(2 * dimm + 1, ctrl, pvt))
+               cs_mode |= CS_ODD_SECONDARY;
+
+       return cs_mode;
 }
 
 static void debug_display_dimm_sizes_df(struct amd64_pvt *pvt, u8 ctrl)
 {
-       int dimm, size0, size1, cs0, cs1;
+       int dimm, size0, size1, cs0, cs1, cs_mode;
 
        edac_printk(KERN_DEBUG, EDAC_MC, "UMC%d chip selects:\n", ctrl);
 
-       for (dimm = 0; dimm < 4; dimm++) {
-               size0 = 0;
+       for (dimm = 0; dimm < 2; dimm++) {
                cs0 = dimm * 2;
-
-               if (csrow_enabled(cs0, ctrl, pvt))
-                       size0 = pvt->ops->dbam_to_cs(pvt, ctrl, 0, cs0);
-
-               size1 = 0;
                cs1 = dimm * 2 + 1;
 
-               if (csrow_enabled(cs1, ctrl, pvt)) {
-                       /*
-                        * CS interleaving is only supported if both CSes have
-                        * the same amount of memory. Because they are
-                        * interleaved, it will look like both CSes have the
-                        * full amount of memory. Save the size for both as
-                        * half the amount we found on CS0, if interleaved.
-                        */
-                       if (f17_cs_interleaved(pvt, ctrl, cs1))
-                               size1 = size0 = (size0 >> 1);
-                       else
-                               size1 = pvt->ops->dbam_to_cs(pvt, ctrl, 0, cs1);
-               }
+               cs_mode = f17_get_cs_mode(dimm, ctrl, pvt);
+
+               size0 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs0);
+               size1 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs1);
 
                amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
                                cs0,    size0,
@@ -942,89 +936,119 @@ static void prep_chip_selects(struct amd64_pvt *pvt)
        } else if (pvt->fam == 0x15 && pvt->model == 0x30) {
                pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4;
                pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2;
+       } else if (pvt->fam >= 0x17) {
+               int umc;
+
+               for_each_umc(umc) {
+                       pvt->csels[umc].b_cnt = 4;
+                       pvt->csels[umc].m_cnt = 2;
+               }
+
        } else {
                pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
                pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4;
        }
 }
 
+static void read_umc_base_mask(struct amd64_pvt *pvt)
+{
+       u32 umc_base_reg, umc_base_reg_sec;
+       u32 umc_mask_reg, umc_mask_reg_sec;
+       u32 base_reg, base_reg_sec;
+       u32 mask_reg, mask_reg_sec;
+       u32 *base, *base_sec;
+       u32 *mask, *mask_sec;
+       int cs, umc;
+
+       for_each_umc(umc) {
+               umc_base_reg = get_umc_base(umc) + UMCCH_BASE_ADDR;
+               umc_base_reg_sec = get_umc_base(umc) + UMCCH_BASE_ADDR_SEC;
+
+               for_each_chip_select(cs, umc, pvt) {
+                       base = &pvt->csels[umc].csbases[cs];
+                       base_sec = &pvt->csels[umc].csbases_sec[cs];
+
+                       base_reg = umc_base_reg + (cs * 4);
+                       base_reg_sec = umc_base_reg_sec + (cs * 4);
+
+                       if (!amd_smn_read(pvt->mc_node_id, base_reg, base))
+                               edac_dbg(0, "  DCSB%d[%d]=0x%08x reg: 0x%x\n",
+                                        umc, cs, *base, base_reg);
+
+                       if (!amd_smn_read(pvt->mc_node_id, base_reg_sec, base_sec))
+                               edac_dbg(0, "    DCSB_SEC%d[%d]=0x%08x reg: 0x%x\n",
+                                        umc, cs, *base_sec, base_reg_sec);
+               }
+
+               umc_mask_reg = get_umc_base(umc) + UMCCH_ADDR_MASK;
+               umc_mask_reg_sec = get_umc_base(umc) + UMCCH_ADDR_MASK_SEC;
+
+               for_each_chip_select_mask(cs, umc, pvt) {
+                       mask = &pvt->csels[umc].csmasks[cs];
+                       mask_sec = &pvt->csels[umc].csmasks_sec[cs];
+
+                       mask_reg = umc_mask_reg + (cs * 4);
+                       mask_reg_sec = umc_mask_reg_sec + (cs * 4);
+
+                       if (!amd_smn_read(pvt->mc_node_id, mask_reg, mask))
+                               edac_dbg(0, "  DCSM%d[%d]=0x%08x reg: 0x%x\n",
+                                        umc, cs, *mask, mask_reg);
+
+                       if (!amd_smn_read(pvt->mc_node_id, mask_reg_sec, mask_sec))
+                               edac_dbg(0, "    DCSM_SEC%d[%d]=0x%08x reg: 0x%x\n",
+                                        umc, cs, *mask_sec, mask_reg_sec);
+               }
+       }
+}
+
 /*
  * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers
  */
 static void read_dct_base_mask(struct amd64_pvt *pvt)
 {
-       int base_reg0, base_reg1, mask_reg0, mask_reg1, cs;
+       int cs;
 
        prep_chip_selects(pvt);
 
-       if (pvt->umc) {
-               base_reg0 = get_umc_base(0) + UMCCH_BASE_ADDR;
-               base_reg1 = get_umc_base(1) + UMCCH_BASE_ADDR;
-               mask_reg0 = get_umc_base(0) + UMCCH_ADDR_MASK;
-               mask_reg1 = get_umc_base(1) + UMCCH_ADDR_MASK;
-       } else {
-               base_reg0 = DCSB0;
-               base_reg1 = DCSB1;
-               mask_reg0 = DCSM0;
-               mask_reg1 = DCSM1;
-       }
+       if (pvt->umc)
+               return read_umc_base_mask(pvt);
 
        for_each_chip_select(cs, 0, pvt) {
-               int reg0   = base_reg0 + (cs * 4);
-               int reg1   = base_reg1 + (cs * 4);
+               int reg0   = DCSB0 + (cs * 4);
+               int reg1   = DCSB1 + (cs * 4);
                u32 *base0 = &pvt->csels[0].csbases[cs];
                u32 *base1 = &pvt->csels[1].csbases[cs];
 
-               if (pvt->umc) {
-                       if (!amd_smn_read(pvt->mc_node_id, reg0, base0))
-                               edac_dbg(0, "  DCSB0[%d]=0x%08x reg: 0x%x\n",
-                                        cs, *base0, reg0);
+               if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0))
+                       edac_dbg(0, "  DCSB0[%d]=0x%08x reg: F2x%x\n",
+                                cs, *base0, reg0);
 
-                       if (!amd_smn_read(pvt->mc_node_id, reg1, base1))
-                               edac_dbg(0, "  DCSB1[%d]=0x%08x reg: 0x%x\n",
-                                        cs, *base1, reg1);
-               } else {
-                       if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0))
-                               edac_dbg(0, "  DCSB0[%d]=0x%08x reg: F2x%x\n",
-                                        cs, *base0, reg0);
-
-                       if (pvt->fam == 0xf)
-                               continue;
+               if (pvt->fam == 0xf)
+                       continue;
 
-                       if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1))
-                               edac_dbg(0, "  DCSB1[%d]=0x%08x reg: F2x%x\n",
-                                        cs, *base1, (pvt->fam == 0x10) ? reg1
-                                                               : reg0);
-               }
+               if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1))
+                       edac_dbg(0, "  DCSB1[%d]=0x%08x reg: F2x%x\n",
+                                cs, *base1, (pvt->fam == 0x10) ? reg1
+                                                       : reg0);
        }
 
        for_each_chip_select_mask(cs, 0, pvt) {
-               int reg0   = mask_reg0 + (cs * 4);
-               int reg1   = mask_reg1 + (cs * 4);
+               int reg0   = DCSM0 + (cs * 4);
+               int reg1   = DCSM1 + (cs * 4);
                u32 *mask0 = &pvt->csels[0].csmasks[cs];
                u32 *mask1 = &pvt->csels[1].csmasks[cs];
 
-               if (pvt->umc) {
-                       if (!amd_smn_read(pvt->mc_node_id, reg0, mask0))
-                               edac_dbg(0, "    DCSM0[%d]=0x%08x reg: 0x%x\n",
-                                        cs, *mask0, reg0);
-
-                       if (!amd_smn_read(pvt->mc_node_id, reg1, mask1))
-                               edac_dbg(0, "    DCSM1[%d]=0x%08x reg: 0x%x\n",
-                                        cs, *mask1, reg1);
-               } else {
-                       if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0))
-                               edac_dbg(0, "    DCSM0[%d]=0x%08x reg: F2x%x\n",
-                                        cs, *mask0, reg0);
+               if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0))
+                       edac_dbg(0, "    DCSM0[%d]=0x%08x reg: F2x%x\n",
+                                cs, *mask0, reg0);
 
-                       if (pvt->fam == 0xf)
-                               continue;
+               if (pvt->fam == 0xf)
+                       continue;
 
-                       if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1))
-                               edac_dbg(0, "    DCSM1[%d]=0x%08x reg: F2x%x\n",
-                                        cs, *mask1, (pvt->fam == 0x10) ? reg1
-                                                               : reg0);
-               }
+               if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1))
+                       edac_dbg(0, "    DCSM1[%d]=0x%08x reg: F2x%x\n",
+                                cs, *mask1, (pvt->fam == 0x10) ? reg1
+                                                       : reg0);
        }
 }
 
@@ -1556,18 +1580,58 @@ static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
                return ddr3_cs_size(cs_mode, false);
 }
 
-static int f17_base_addr_to_cs_size(struct amd64_pvt *pvt, u8 umc,
+static int f17_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc,
                                    unsigned int cs_mode, int csrow_nr)
 {
-       u32 base_addr = pvt->csels[umc].csbases[csrow_nr];
+       u32 addr_mask_orig, addr_mask_deinterleaved;
+       u32 msb, weight, num_zero_bits;
+       int dimm, size = 0;
 
-       /*  Each mask is used for every two base addresses. */
-       u32 addr_mask = pvt->csels[umc].csmasks[csrow_nr >> 1];
+       /* No Chip Selects are enabled. */
+       if (!cs_mode)
+               return size;
 
-       /*  Register [31:1] = Address [39:9]. Size is in kBs here. */
-       u32 size = ((addr_mask >> 1) - (base_addr >> 1) + 1) >> 1;
+       /* Requested size of an even CS but none are enabled. */
+       if (!(cs_mode & CS_EVEN) && !(csrow_nr & 1))
+               return size;
 
-       edac_dbg(1, "BaseAddr: 0x%x, AddrMask: 0x%x\n", base_addr, addr_mask);
+       /* Requested size of an odd CS but none are enabled. */
+       if (!(cs_mode & CS_ODD) && (csrow_nr & 1))
+               return size;
+
+       /*
+        * There is one mask per DIMM, and two Chip Selects per DIMM.
+        *      CS0 and CS1 -> DIMM0
+        *      CS2 and CS3 -> DIMM1
+        */
+       dimm = csrow_nr >> 1;
+
+       /* Asymmetric dual-rank DIMM support. */
+       if ((csrow_nr & 1) && (cs_mode & CS_ODD_SECONDARY))
+               addr_mask_orig = pvt->csels[umc].csmasks_sec[dimm];
+       else
+               addr_mask_orig = pvt->csels[umc].csmasks[dimm];
+
+       /*
+        * The number of zero bits in the mask is equal to the number of bits
+        * in a full mask minus the number of bits in the current mask.
+        *
+        * The MSB is the number of bits in the full mask because BIT[0] is
+        * always 0.
+        */
+       msb = fls(addr_mask_orig) - 1;
+       weight = hweight_long(addr_mask_orig);
+       num_zero_bits = msb - weight;
+
+       /* Take the number of zero bits off from the top of the mask. */
+       addr_mask_deinterleaved = GENMASK_ULL(msb - num_zero_bits, 1);
+
+       edac_dbg(1, "CS%d DIMM%d AddrMasks:\n", csrow_nr, dimm);
+       edac_dbg(1, "  Original AddrMask: 0x%x\n", addr_mask_orig);
+       edac_dbg(1, "  Deinterleaved AddrMask: 0x%x\n", addr_mask_deinterleaved);
+
+       /* Register [31:1] = Address [39:9]. Size is in kBs here. */
+       size = (addr_mask_deinterleaved >> 2) + 1;
 
        /* Return size in MBs. */
        return size >> 10;
@@ -2232,7 +2296,7 @@ static struct amd64_family_type family_types[] = {
                .f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6,
                .ops = {
                        .early_channel_count    = f17_early_channel_count,
-                       .dbam_to_cs             = f17_base_addr_to_cs_size,
+                       .dbam_to_cs             = f17_addr_mask_to_cs_size,
                }
        },
        [F17_M10H_CPUS] = {
@@ -2241,7 +2305,7 @@ static struct amd64_family_type family_types[] = {
                .f6_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F6,
                .ops = {
                        .early_channel_count    = f17_early_channel_count,
-                       .dbam_to_cs             = f17_base_addr_to_cs_size,
+                       .dbam_to_cs             = f17_addr_mask_to_cs_size,
                }
        },
        [F17_M30H_CPUS] = {
@@ -2250,7 +2314,16 @@ static struct amd64_family_type family_types[] = {
                .f6_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F6,
                .ops = {
                        .early_channel_count    = f17_early_channel_count,
-                       .dbam_to_cs             = f17_base_addr_to_cs_size,
+                       .dbam_to_cs             = f17_addr_mask_to_cs_size,
+               }
+       },
+       [F17_M70H_CPUS] = {
+               .ctl_name = "F17h_M70h",
+               .f0_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F0,
+               .f6_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F6,
+               .ops = {
+                       .early_channel_count    = f17_early_channel_count,
+                       .dbam_to_cs             = f17_addr_mask_to_cs_size,
                }
        },
 };
@@ -2537,13 +2610,6 @@ static void decode_umc_error(int node_id, struct mce *m)
 
        err.channel = find_umc_channel(m);
 
-       if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) {
-               err.err_code = ERR_NORM_ADDR;
-               goto log_error;
-       }
-
-       error_address_to_page_and_offset(sys_addr, &err);
-
        if (!(m->status & MCI_STATUS_SYNDV)) {
                err.err_code = ERR_SYND;
                goto log_error;
@@ -2560,6 +2626,13 @@ static void decode_umc_error(int node_id, struct mce *m)
 
        err.csrow = m->synd & 0x7;
 
+       if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) {
+               err.err_code = ERR_NORM_ADDR;
+               goto log_error;
+       }
+
+       error_address_to_page_and_offset(sys_addr, &err);
+
 log_error:
        __log_ecc_error(mci, &err, ecc_type);
 }
@@ -2809,10 +2882,12 @@ static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr_orig)
        int csrow_nr = csrow_nr_orig;
        u32 cs_mode, nr_pages;
 
-       if (!pvt->umc)
+       if (!pvt->umc) {
                csrow_nr >>= 1;
-
-       cs_mode = DBAM_DIMM(csrow_nr, dbam);
+               cs_mode = DBAM_DIMM(csrow_nr, dbam);
+       } else {
+               cs_mode = f17_get_cs_mode(csrow_nr >> 1, dct, pvt);
+       }
 
        nr_pages   = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, csrow_nr);
        nr_pages <<= 20 - PAGE_SHIFT;
@@ -2824,6 +2899,49 @@ static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr_orig)
        return nr_pages;
 }
 
+static int init_csrows_df(struct mem_ctl_info *mci)
+{
+       struct amd64_pvt *pvt = mci->pvt_info;
+       enum edac_type edac_mode = EDAC_NONE;
+       enum dev_type dev_type = DEV_UNKNOWN;
+       struct dimm_info *dimm;
+       int empty = 1;
+       u8 umc, cs;
+
+       if (mci->edac_ctl_cap & EDAC_FLAG_S16ECD16ED) {
+               edac_mode = EDAC_S16ECD16ED;
+               dev_type = DEV_X16;
+       } else if (mci->edac_ctl_cap & EDAC_FLAG_S8ECD8ED) {
+               edac_mode = EDAC_S8ECD8ED;
+               dev_type = DEV_X8;
+       } else if (mci->edac_ctl_cap & EDAC_FLAG_S4ECD4ED) {
+               edac_mode = EDAC_S4ECD4ED;
+               dev_type = DEV_X4;
+       } else if (mci->edac_ctl_cap & EDAC_FLAG_SECDED) {
+               edac_mode = EDAC_SECDED;
+       }
+
+       for_each_umc(umc) {
+               for_each_chip_select(cs, umc, pvt) {
+                       if (!csrow_enabled(cs, umc, pvt))
+                               continue;
+
+                       empty = 0;
+                       dimm = mci->csrows[cs]->channels[umc]->dimm;
+
+                       edac_dbg(1, "MC node: %d, csrow: %d\n",
+                                       pvt->mc_node_id, cs);
+
+                       dimm->nr_pages = get_csrow_nr_pages(pvt, umc, cs);
+                       dimm->mtype = pvt->dram_type;
+                       dimm->edac_mode = edac_mode;
+                       dimm->dtype = dev_type;
+               }
+       }
+
+       return empty;
+}
+
 /*
  * Initialize the array of csrow attribute instances, based on the values
  * from pci config hardware registers.
@@ -2838,15 +2956,16 @@ static int init_csrows(struct mem_ctl_info *mci)
        int nr_pages = 0;
        u32 val;
 
-       if (!pvt->umc) {
-               amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
+       if (pvt->umc)
+               return init_csrows_df(mci);
 
-               pvt->nbcfg = val;
+       amd64_read_pci_cfg(pvt->F3, 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));
-       }
+       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));
 
        /*
         * We iterate over DCT0 here but we look at DCT1 in parallel, if needed.
@@ -2883,13 +3002,7 @@ static int init_csrows(struct mem_ctl_info *mci)
                edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages);
 
                /* 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) {
+               if (pvt->nbcfg & NBCFG_ECC_ENABLE) {
                        edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL)
                                        ? EDAC_S4ECD4ED
                                        : EDAC_SECDED;
@@ -3137,12 +3250,15 @@ static bool ecc_enabled(struct pci_dev *F3, u16 nid)
 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;
+       u8 i, ecc_en = 1, cpk_en = 1, dev_x4 = 1, dev_x16 = 1;
 
        for_each_umc(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);
+
+                       dev_x4  &= !!(pvt->umc[i].dimm_cfg & BIT(6));
+                       dev_x16 &= !!(pvt->umc[i].dimm_cfg & BIT(7));
                }
        }
 
@@ -3150,8 +3266,15 @@ f17h_determine_edac_ctl_cap(struct mem_ctl_info *mci, struct amd64_pvt *pvt)
        if (ecc_en) {
                mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
 
-               if (cpk_en)
+               if (!cpk_en)
+                       return;
+
+               if (dev_x4)
                        mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
+               else if (dev_x16)
+                       mci->edac_ctl_cap |= EDAC_FLAG_S16ECD16ED;
+               else
+                       mci->edac_ctl_cap |= EDAC_FLAG_S8ECD8ED;
        }
 }
 
@@ -3241,6 +3364,10 @@ static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt)
                        fam_type = &family_types[F17_M30H_CPUS];
                        pvt->ops = &family_types[F17_M30H_CPUS].ops;
                        break;
+               } else if (pvt->model >= 0x70 && pvt->model <= 0x7f) {
+                       fam_type = &family_types[F17_M70H_CPUS];
+                       pvt->ops = &family_types[F17_M70H_CPUS].ops;
+                       break;
                }
                /* fall through */
        case 0x18: