Commit 469820f4 authored by Jason Baron's avatar Jason Baron Committed by Kamal Mostafa

EDAC, ie31200_edac: Add Skylake support

BugLink: http://bugs.launchpad.net/bugs/1619766

Skylake adjusts some register locations, but otherwise follows the
existing model quite closely. I was able to verify that the 'ce_count'
increments when 'bad dimms' are used. The accounting of 'ce_count' and
'ue_count' is the primary functionality of interest for us. Tested on
Intel(R) Xeon(R) CPU E3-1260L v5 @ 2.90GHz.
Signed-off-by: default avatarJason Baron <jbaron@akamai.com>
Acked-by: default avatarTony Luck <tony.luck@intel.com>
Cc: linux-edac <linux-edac@vger.kernel.org>
Link: http://lkml.kernel.org/r/1462547927-22679-1-git-send-email-jbaron@akamai.comSigned-off-by: default avatarBorislav Petkov <bp@suse.de>
(cherry picked from commit 953dee9b)
Signed-off-by: default avatarJoseph Salisbury <joseph.salisbury@canonical.com>
Acked-by: default avatarTim Gardner <tim.gardner@canonical.com>
Signed-off-by: default avatarKamal Mostafa <kamal@canonical.com>
parent d2b59eef
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
* 015c: Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller * 015c: Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller
* 0c04: Xeon E3-1200 v3/4th Gen Core Processor DRAM Controller * 0c04: Xeon E3-1200 v3/4th Gen Core Processor DRAM Controller
* 0c08: Xeon E3-1200 v3 Processor DRAM Controller * 0c08: Xeon E3-1200 v3 Processor DRAM Controller
* 1918: Xeon E3-1200 v5 Skylake Host Bridge/DRAM Registers
* *
* Based on Intel specification: * Based on Intel specification:
* http://www.intel.com/content/dam/www/public/us/en/documents/datasheets/xeon-e3-1200v3-vol-2-datasheet.pdf * http://www.intel.com/content/dam/www/public/us/en/documents/datasheets/xeon-e3-1200v3-vol-2-datasheet.pdf
...@@ -55,6 +56,7 @@ ...@@ -55,6 +56,7 @@
#define PCI_DEVICE_ID_INTEL_IE31200_HB_5 0x015c #define PCI_DEVICE_ID_INTEL_IE31200_HB_5 0x015c
#define PCI_DEVICE_ID_INTEL_IE31200_HB_6 0x0c04 #define PCI_DEVICE_ID_INTEL_IE31200_HB_6 0x0c04
#define PCI_DEVICE_ID_INTEL_IE31200_HB_7 0x0c08 #define PCI_DEVICE_ID_INTEL_IE31200_HB_7 0x0c08
#define PCI_DEVICE_ID_INTEL_IE31200_HB_8 0x1918
#define IE31200_DIMMS 4 #define IE31200_DIMMS 4
#define IE31200_RANKS 8 #define IE31200_RANKS 8
...@@ -105,8 +107,11 @@ ...@@ -105,8 +107,11 @@
* 1 Multiple Bit Error Status (MERRSTS) * 1 Multiple Bit Error Status (MERRSTS)
* 0 Correctable Error Status (CERRSTS) * 0 Correctable Error Status (CERRSTS)
*/ */
#define IE31200_C0ECCERRLOG 0x40c8 #define IE31200_C0ECCERRLOG 0x40c8
#define IE31200_C1ECCERRLOG 0x44c8 #define IE31200_C1ECCERRLOG 0x44c8
#define IE31200_C0ECCERRLOG_SKL 0x4048
#define IE31200_C1ECCERRLOG_SKL 0x4448
#define IE31200_ECCERRLOG_CE BIT(0) #define IE31200_ECCERRLOG_CE BIT(0)
#define IE31200_ECCERRLOG_UE BIT(1) #define IE31200_ECCERRLOG_UE BIT(1)
#define IE31200_ECCERRLOG_RANK_BITS GENMASK_ULL(28, 27) #define IE31200_ECCERRLOG_RANK_BITS GENMASK_ULL(28, 27)
...@@ -123,17 +128,28 @@ ...@@ -123,17 +128,28 @@
#define IE31200_CAPID0_DDPCD BIT(6) #define IE31200_CAPID0_DDPCD BIT(6)
#define IE31200_CAPID0_ECC BIT(1) #define IE31200_CAPID0_ECC BIT(1)
#define IE31200_MAD_DIMM_0_OFFSET 0x5004 #define IE31200_MAD_DIMM_0_OFFSET 0x5004
#define IE31200_MAD_DIMM_SIZE GENMASK_ULL(7, 0) #define IE31200_MAD_DIMM_0_OFFSET_SKL 0x500C
#define IE31200_MAD_DIMM_A_RANK BIT(17) #define IE31200_MAD_DIMM_SIZE GENMASK_ULL(7, 0)
#define IE31200_MAD_DIMM_A_WIDTH BIT(19) #define IE31200_MAD_DIMM_A_RANK BIT(17)
#define IE31200_MAD_DIMM_A_RANK_SHIFT 17
#define IE31200_PAGES(n) (n << (28 - PAGE_SHIFT)) #define IE31200_MAD_DIMM_A_RANK_SKL BIT(10)
#define IE31200_MAD_DIMM_A_RANK_SKL_SHIFT 10
#define IE31200_MAD_DIMM_A_WIDTH BIT(19)
#define IE31200_MAD_DIMM_A_WIDTH_SHIFT 19
#define IE31200_MAD_DIMM_A_WIDTH_SKL GENMASK_ULL(9, 8)
#define IE31200_MAD_DIMM_A_WIDTH_SKL_SHIFT 8
/* Skylake reports 1GB increments, everything else is 256MB */
#define IE31200_PAGES(n, skl) \
(n << (28 + (2 * skl) - PAGE_SHIFT))
static int nr_channels; static int nr_channels;
struct ie31200_priv { struct ie31200_priv {
void __iomem *window; void __iomem *window;
void __iomem *c0errlog;
void __iomem *c1errlog;
}; };
enum ie31200_chips { enum ie31200_chips {
...@@ -157,9 +173,9 @@ static const struct ie31200_dev_info ie31200_devs[] = { ...@@ -157,9 +173,9 @@ static const struct ie31200_dev_info ie31200_devs[] = {
}; };
struct dimm_data { struct dimm_data {
u8 size; /* in 256MB multiples */ u8 size; /* in multiples of 256MB, except Skylake is 1GB */
u8 dual_rank : 1, u8 dual_rank : 1,
x16_width : 1; /* 0 means x8 width */ x16_width : 2; /* 0 means x8 width */
}; };
static int how_many_channels(struct pci_dev *pdev) static int how_many_channels(struct pci_dev *pdev)
...@@ -197,11 +213,10 @@ static bool ecc_capable(struct pci_dev *pdev) ...@@ -197,11 +213,10 @@ static bool ecc_capable(struct pci_dev *pdev)
return true; return true;
} }
static int eccerrlog_row(int channel, u64 log) static int eccerrlog_row(u64 log)
{ {
int rank = ((log & IE31200_ECCERRLOG_RANK_BITS) >> return ((log & IE31200_ECCERRLOG_RANK_BITS) >>
IE31200_ECCERRLOG_RANK_SHIFT); IE31200_ECCERRLOG_RANK_SHIFT);
return rank | (channel * IE31200_RANKS_PER_CHANNEL);
} }
static void ie31200_clear_error_info(struct mem_ctl_info *mci) static void ie31200_clear_error_info(struct mem_ctl_info *mci)
...@@ -219,7 +234,6 @@ static void ie31200_get_and_clear_error_info(struct mem_ctl_info *mci, ...@@ -219,7 +234,6 @@ static void ie31200_get_and_clear_error_info(struct mem_ctl_info *mci,
{ {
struct pci_dev *pdev; struct pci_dev *pdev;
struct ie31200_priv *priv = mci->pvt_info; struct ie31200_priv *priv = mci->pvt_info;
void __iomem *window = priv->window;
pdev = to_pci_dev(mci->pdev); pdev = to_pci_dev(mci->pdev);
...@@ -232,9 +246,9 @@ static void ie31200_get_and_clear_error_info(struct mem_ctl_info *mci, ...@@ -232,9 +246,9 @@ static void ie31200_get_and_clear_error_info(struct mem_ctl_info *mci,
if (!(info->errsts & IE31200_ERRSTS_BITS)) if (!(info->errsts & IE31200_ERRSTS_BITS))
return; return;
info->eccerrlog[0] = lo_hi_readq(window + IE31200_C0ECCERRLOG); info->eccerrlog[0] = lo_hi_readq(priv->c0errlog);
if (nr_channels == 2) if (nr_channels == 2)
info->eccerrlog[1] = lo_hi_readq(window + IE31200_C1ECCERRLOG); info->eccerrlog[1] = lo_hi_readq(priv->c1errlog);
pci_read_config_word(pdev, IE31200_ERRSTS, &info->errsts2); pci_read_config_word(pdev, IE31200_ERRSTS, &info->errsts2);
...@@ -245,10 +259,10 @@ static void ie31200_get_and_clear_error_info(struct mem_ctl_info *mci, ...@@ -245,10 +259,10 @@ static void ie31200_get_and_clear_error_info(struct mem_ctl_info *mci,
* should be UE info. * should be UE info.
*/ */
if ((info->errsts ^ info->errsts2) & IE31200_ERRSTS_BITS) { if ((info->errsts ^ info->errsts2) & IE31200_ERRSTS_BITS) {
info->eccerrlog[0] = lo_hi_readq(window + IE31200_C0ECCERRLOG); info->eccerrlog[0] = lo_hi_readq(priv->c0errlog);
if (nr_channels == 2) if (nr_channels == 2)
info->eccerrlog[1] = info->eccerrlog[1] =
lo_hi_readq(window + IE31200_C1ECCERRLOG); lo_hi_readq(priv->c1errlog);
} }
ie31200_clear_error_info(mci); ie31200_clear_error_info(mci);
...@@ -274,14 +288,14 @@ static void ie31200_process_error_info(struct mem_ctl_info *mci, ...@@ -274,14 +288,14 @@ static void ie31200_process_error_info(struct mem_ctl_info *mci,
if (log & IE31200_ECCERRLOG_UE) { if (log & IE31200_ECCERRLOG_UE) {
edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
0, 0, 0, 0, 0, 0,
eccerrlog_row(channel, log), eccerrlog_row(log),
channel, -1, channel, -1,
"ie31200 UE", ""); "ie31200 UE", "");
} else if (log & IE31200_ECCERRLOG_CE) { } else if (log & IE31200_ECCERRLOG_CE) {
edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
0, 0, 0, 0,
IE31200_ECCERRLOG_SYNDROME(log), IE31200_ECCERRLOG_SYNDROME(log),
eccerrlog_row(channel, log), eccerrlog_row(log),
channel, -1, channel, -1,
"ie31200 CE", ""); "ie31200 CE", "");
} }
...@@ -326,6 +340,33 @@ static void __iomem *ie31200_map_mchbar(struct pci_dev *pdev) ...@@ -326,6 +340,33 @@ static void __iomem *ie31200_map_mchbar(struct pci_dev *pdev)
return window; return window;
} }
static void __skl_populate_dimm_info(struct dimm_data *dd, u32 addr_decode,
int chan)
{
dd->size = (addr_decode >> (chan << 4)) & IE31200_MAD_DIMM_SIZE;
dd->dual_rank = (addr_decode & (IE31200_MAD_DIMM_A_RANK_SKL << (chan << 4))) ? 1 : 0;
dd->x16_width = ((addr_decode & (IE31200_MAD_DIMM_A_WIDTH_SKL << (chan << 4))) >>
(IE31200_MAD_DIMM_A_WIDTH_SKL_SHIFT + (chan << 4)));
}
static void __populate_dimm_info(struct dimm_data *dd, u32 addr_decode,
int chan)
{
dd->size = (addr_decode >> (chan << 3)) & IE31200_MAD_DIMM_SIZE;
dd->dual_rank = (addr_decode & (IE31200_MAD_DIMM_A_RANK << chan)) ? 1 : 0;
dd->x16_width = (addr_decode & (IE31200_MAD_DIMM_A_WIDTH << chan)) ? 1 : 0;
}
static void populate_dimm_info(struct dimm_data *dd, u32 addr_decode, int chan,
bool skl)
{
if (skl)
__skl_populate_dimm_info(dd, addr_decode, chan);
else
__populate_dimm_info(dd, addr_decode, chan);
}
static int ie31200_probe1(struct pci_dev *pdev, int dev_idx) static int ie31200_probe1(struct pci_dev *pdev, int dev_idx)
{ {
int i, j, ret; int i, j, ret;
...@@ -334,7 +375,8 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx) ...@@ -334,7 +375,8 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx)
struct dimm_data dimm_info[IE31200_CHANNELS][IE31200_DIMMS_PER_CHANNEL]; struct dimm_data dimm_info[IE31200_CHANNELS][IE31200_DIMMS_PER_CHANNEL];
void __iomem *window; void __iomem *window;
struct ie31200_priv *priv; struct ie31200_priv *priv;
u32 addr_decode; u32 addr_decode, mad_offset;
bool skl = (pdev->device == PCI_DEVICE_ID_INTEL_IE31200_HB_8);
edac_dbg(0, "MC:\n"); edac_dbg(0, "MC:\n");
...@@ -363,7 +405,10 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx) ...@@ -363,7 +405,10 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx)
edac_dbg(3, "MC: init mci\n"); edac_dbg(3, "MC: init mci\n");
mci->pdev = &pdev->dev; mci->pdev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_DDR3; if (skl)
mci->mtype_cap = MEM_FLAG_DDR4;
else
mci->mtype_cap = MEM_FLAG_DDR3;
mci->edac_ctl_cap = EDAC_FLAG_SECDED; mci->edac_ctl_cap = EDAC_FLAG_SECDED;
mci->edac_cap = EDAC_FLAG_SECDED; mci->edac_cap = EDAC_FLAG_SECDED;
mci->mod_name = EDAC_MOD_STR; mci->mod_name = EDAC_MOD_STR;
...@@ -374,19 +419,24 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx) ...@@ -374,19 +419,24 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx)
mci->ctl_page_to_phys = NULL; mci->ctl_page_to_phys = NULL;
priv = mci->pvt_info; priv = mci->pvt_info;
priv->window = window; priv->window = window;
if (skl) {
priv->c0errlog = window + IE31200_C0ECCERRLOG_SKL;
priv->c1errlog = window + IE31200_C1ECCERRLOG_SKL;
mad_offset = IE31200_MAD_DIMM_0_OFFSET_SKL;
} else {
priv->c0errlog = window + IE31200_C0ECCERRLOG;
priv->c1errlog = window + IE31200_C1ECCERRLOG;
mad_offset = IE31200_MAD_DIMM_0_OFFSET;
}
/* populate DIMM info */ /* populate DIMM info */
for (i = 0; i < IE31200_CHANNELS; i++) { for (i = 0; i < IE31200_CHANNELS; i++) {
addr_decode = readl(window + IE31200_MAD_DIMM_0_OFFSET + addr_decode = readl(window + mad_offset +
(i * 4)); (i * 4));
edac_dbg(0, "addr_decode: 0x%x\n", addr_decode); edac_dbg(0, "addr_decode: 0x%x\n", addr_decode);
for (j = 0; j < IE31200_DIMMS_PER_CHANNEL; j++) { for (j = 0; j < IE31200_DIMMS_PER_CHANNEL; j++) {
dimm_info[i][j].size = (addr_decode >> (j * 8)) & populate_dimm_info(&dimm_info[i][j], addr_decode, j,
IE31200_MAD_DIMM_SIZE; skl);
dimm_info[i][j].dual_rank = (addr_decode &
(IE31200_MAD_DIMM_A_RANK << j)) ? 1 : 0;
dimm_info[i][j].x16_width = (addr_decode &
(IE31200_MAD_DIMM_A_WIDTH << j)) ? 1 : 0;
edac_dbg(0, "size: 0x%x, rank: %d, width: %d\n", edac_dbg(0, "size: 0x%x, rank: %d, width: %d\n",
dimm_info[i][j].size, dimm_info[i][j].size,
dimm_info[i][j].dual_rank, dimm_info[i][j].dual_rank,
...@@ -405,7 +455,7 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx) ...@@ -405,7 +455,7 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx)
struct dimm_info *dimm; struct dimm_info *dimm;
unsigned long nr_pages; unsigned long nr_pages;
nr_pages = IE31200_PAGES(dimm_info[j][i].size); nr_pages = IE31200_PAGES(dimm_info[j][i].size, skl);
if (nr_pages == 0) if (nr_pages == 0)
continue; continue;
...@@ -417,7 +467,10 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx) ...@@ -417,7 +467,10 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx)
dimm->nr_pages = nr_pages; dimm->nr_pages = nr_pages;
edac_dbg(0, "set nr pages: 0x%lx\n", nr_pages); edac_dbg(0, "set nr pages: 0x%lx\n", nr_pages);
dimm->grain = 8; /* just a guess */ dimm->grain = 8; /* just a guess */
dimm->mtype = MEM_DDR3; if (skl)
dimm->mtype = MEM_DDR4;
else
dimm->mtype = MEM_DDR3;
dimm->dtype = DEV_UNKNOWN; dimm->dtype = DEV_UNKNOWN;
dimm->edac_mode = EDAC_UNKNOWN; dimm->edac_mode = EDAC_UNKNOWN;
} }
...@@ -426,7 +479,10 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx) ...@@ -426,7 +479,10 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx)
dimm->nr_pages = nr_pages; dimm->nr_pages = nr_pages;
edac_dbg(0, "set nr pages: 0x%lx\n", nr_pages); edac_dbg(0, "set nr pages: 0x%lx\n", nr_pages);
dimm->grain = 8; /* same guess */ dimm->grain = 8; /* same guess */
dimm->mtype = MEM_DDR3; if (skl)
dimm->mtype = MEM_DDR4;
else
dimm->mtype = MEM_DDR3;
dimm->dtype = DEV_UNKNOWN; dimm->dtype = DEV_UNKNOWN;
dimm->edac_mode = EDAC_UNKNOWN; dimm->edac_mode = EDAC_UNKNOWN;
} }
...@@ -500,6 +556,9 @@ static const struct pci_device_id ie31200_pci_tbl[] = { ...@@ -500,6 +556,9 @@ static const struct pci_device_id ie31200_pci_tbl[] = {
{ {
PCI_VEND_DEV(INTEL, IE31200_HB_7), PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_VEND_DEV(INTEL, IE31200_HB_7), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
IE31200}, IE31200},
{
PCI_VEND_DEV(INTEL, IE31200_HB_8), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
IE31200},
{ {
0, 0,
} /* 0 terminated list. */ } /* 0 terminated list. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment