Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

EDAC, ie31200_edac: Add Skylake support

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: Jason Baron <jbaron@akamai.com>
Acked-by: Tony 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.com
Signed-off-by: Borislav Petkov <bp@suse.de>

authored by

Jason Baron and committed by
Borislav Petkov
953dee9b 2c1ea4c7

+89 -30
+89 -30
drivers/edac/ie31200_edac.c
··· 17 17 * 015c: Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller 18 18 * 0c04: Xeon E3-1200 v3/4th Gen Core Processor DRAM Controller 19 19 * 0c08: Xeon E3-1200 v3 Processor DRAM Controller 20 + * 1918: Xeon E3-1200 v5 Skylake Host Bridge/DRAM Registers 20 21 * 21 22 * Based on Intel specification: 22 23 * http://www.intel.com/content/dam/www/public/us/en/documents/datasheets/xeon-e3-1200v3-vol-2-datasheet.pdf ··· 56 55 #define PCI_DEVICE_ID_INTEL_IE31200_HB_5 0x015c 57 56 #define PCI_DEVICE_ID_INTEL_IE31200_HB_6 0x0c04 58 57 #define PCI_DEVICE_ID_INTEL_IE31200_HB_7 0x0c08 58 + #define PCI_DEVICE_ID_INTEL_IE31200_HB_8 0x1918 59 59 60 60 #define IE31200_DIMMS 4 61 61 #define IE31200_RANKS 8 ··· 107 105 * 1 Multiple Bit Error Status (MERRSTS) 108 106 * 0 Correctable Error Status (CERRSTS) 109 107 */ 108 + 110 109 #define IE31200_C0ECCERRLOG 0x40c8 111 110 #define IE31200_C1ECCERRLOG 0x44c8 111 + #define IE31200_C0ECCERRLOG_SKL 0x4048 112 + #define IE31200_C1ECCERRLOG_SKL 0x4448 112 113 #define IE31200_ECCERRLOG_CE BIT(0) 113 114 #define IE31200_ECCERRLOG_UE BIT(1) 114 115 #define IE31200_ECCERRLOG_RANK_BITS GENMASK_ULL(28, 27) ··· 128 123 #define IE31200_CAPID0_DDPCD BIT(6) 129 124 #define IE31200_CAPID0_ECC BIT(1) 130 125 131 - #define IE31200_MAD_DIMM_0_OFFSET 0x5004 132 - #define IE31200_MAD_DIMM_SIZE GENMASK_ULL(7, 0) 133 - #define IE31200_MAD_DIMM_A_RANK BIT(17) 134 - #define IE31200_MAD_DIMM_A_WIDTH BIT(19) 126 + #define IE31200_MAD_DIMM_0_OFFSET 0x5004 127 + #define IE31200_MAD_DIMM_0_OFFSET_SKL 0x500C 128 + #define IE31200_MAD_DIMM_SIZE GENMASK_ULL(7, 0) 129 + #define IE31200_MAD_DIMM_A_RANK BIT(17) 130 + #define IE31200_MAD_DIMM_A_RANK_SHIFT 17 131 + #define IE31200_MAD_DIMM_A_RANK_SKL BIT(10) 132 + #define IE31200_MAD_DIMM_A_RANK_SKL_SHIFT 10 133 + #define IE31200_MAD_DIMM_A_WIDTH BIT(19) 134 + #define IE31200_MAD_DIMM_A_WIDTH_SHIFT 19 135 + #define IE31200_MAD_DIMM_A_WIDTH_SKL GENMASK_ULL(9, 8) 136 + #define IE31200_MAD_DIMM_A_WIDTH_SKL_SHIFT 8 135 137 136 - #define IE31200_PAGES(n) (n << (28 - PAGE_SHIFT)) 138 + /* Skylake reports 1GB increments, everything else is 256MB */ 139 + #define IE31200_PAGES(n, skl) \ 140 + (n << (28 + (2 * skl) - PAGE_SHIFT)) 137 141 138 142 static int nr_channels; 139 143 140 144 struct ie31200_priv { 141 145 void __iomem *window; 146 + void __iomem *c0errlog; 147 + void __iomem *c1errlog; 142 148 }; 143 149 144 150 enum ie31200_chips { ··· 173 157 }; 174 158 175 159 struct dimm_data { 176 - u8 size; /* in 256MB multiples */ 160 + u8 size; /* in multiples of 256MB, except Skylake is 1GB */ 177 161 u8 dual_rank : 1, 178 - x16_width : 1; /* 0 means x8 width */ 162 + x16_width : 2; /* 0 means x8 width */ 179 163 }; 180 164 181 165 static int how_many_channels(struct pci_dev *pdev) ··· 213 197 return true; 214 198 } 215 199 216 - static int eccerrlog_row(int channel, u64 log) 200 + static int eccerrlog_row(u64 log) 217 201 { 218 - int rank = ((log & IE31200_ECCERRLOG_RANK_BITS) >> 219 - IE31200_ECCERRLOG_RANK_SHIFT); 220 - return rank | (channel * IE31200_RANKS_PER_CHANNEL); 202 + return ((log & IE31200_ECCERRLOG_RANK_BITS) >> 203 + IE31200_ECCERRLOG_RANK_SHIFT); 221 204 } 222 205 223 206 static void ie31200_clear_error_info(struct mem_ctl_info *mci) ··· 234 219 { 235 220 struct pci_dev *pdev; 236 221 struct ie31200_priv *priv = mci->pvt_info; 237 - void __iomem *window = priv->window; 238 222 239 223 pdev = to_pci_dev(mci->pdev); 240 224 ··· 246 232 if (!(info->errsts & IE31200_ERRSTS_BITS)) 247 233 return; 248 234 249 - info->eccerrlog[0] = lo_hi_readq(window + IE31200_C0ECCERRLOG); 235 + info->eccerrlog[0] = lo_hi_readq(priv->c0errlog); 250 236 if (nr_channels == 2) 251 - info->eccerrlog[1] = lo_hi_readq(window + IE31200_C1ECCERRLOG); 237 + info->eccerrlog[1] = lo_hi_readq(priv->c1errlog); 252 238 253 239 pci_read_config_word(pdev, IE31200_ERRSTS, &info->errsts2); 254 240 ··· 259 245 * should be UE info. 260 246 */ 261 247 if ((info->errsts ^ info->errsts2) & IE31200_ERRSTS_BITS) { 262 - info->eccerrlog[0] = lo_hi_readq(window + IE31200_C0ECCERRLOG); 248 + info->eccerrlog[0] = lo_hi_readq(priv->c0errlog); 263 249 if (nr_channels == 2) 264 250 info->eccerrlog[1] = 265 - lo_hi_readq(window + IE31200_C1ECCERRLOG); 251 + lo_hi_readq(priv->c1errlog); 266 252 } 267 253 268 254 ie31200_clear_error_info(mci); ··· 288 274 if (log & IE31200_ECCERRLOG_UE) { 289 275 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 290 276 0, 0, 0, 291 - eccerrlog_row(channel, log), 277 + eccerrlog_row(log), 292 278 channel, -1, 293 279 "ie31200 UE", ""); 294 280 } else if (log & IE31200_ECCERRLOG_CE) { 295 281 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 296 282 0, 0, 297 283 IE31200_ECCERRLOG_SYNDROME(log), 298 - eccerrlog_row(channel, log), 284 + eccerrlog_row(log), 299 285 channel, -1, 300 286 "ie31200 CE", ""); 301 287 } ··· 340 326 return window; 341 327 } 342 328 329 + static void __skl_populate_dimm_info(struct dimm_data *dd, u32 addr_decode, 330 + int chan) 331 + { 332 + dd->size = (addr_decode >> (chan << 4)) & IE31200_MAD_DIMM_SIZE; 333 + dd->dual_rank = (addr_decode & (IE31200_MAD_DIMM_A_RANK_SKL << (chan << 4))) ? 1 : 0; 334 + dd->x16_width = ((addr_decode & (IE31200_MAD_DIMM_A_WIDTH_SKL << (chan << 4))) >> 335 + (IE31200_MAD_DIMM_A_WIDTH_SKL_SHIFT + (chan << 4))); 336 + } 337 + 338 + static void __populate_dimm_info(struct dimm_data *dd, u32 addr_decode, 339 + int chan) 340 + { 341 + dd->size = (addr_decode >> (chan << 3)) & IE31200_MAD_DIMM_SIZE; 342 + dd->dual_rank = (addr_decode & (IE31200_MAD_DIMM_A_RANK << chan)) ? 1 : 0; 343 + dd->x16_width = (addr_decode & (IE31200_MAD_DIMM_A_WIDTH << chan)) ? 1 : 0; 344 + } 345 + 346 + static void populate_dimm_info(struct dimm_data *dd, u32 addr_decode, int chan, 347 + bool skl) 348 + { 349 + if (skl) 350 + __skl_populate_dimm_info(dd, addr_decode, chan); 351 + else 352 + __populate_dimm_info(dd, addr_decode, chan); 353 + } 354 + 355 + 343 356 static int ie31200_probe1(struct pci_dev *pdev, int dev_idx) 344 357 { 345 358 int i, j, ret; ··· 375 334 struct dimm_data dimm_info[IE31200_CHANNELS][IE31200_DIMMS_PER_CHANNEL]; 376 335 void __iomem *window; 377 336 struct ie31200_priv *priv; 378 - u32 addr_decode; 337 + u32 addr_decode, mad_offset; 338 + bool skl = (pdev->device == PCI_DEVICE_ID_INTEL_IE31200_HB_8); 379 339 380 340 edac_dbg(0, "MC:\n"); 381 341 ··· 405 363 406 364 edac_dbg(3, "MC: init mci\n"); 407 365 mci->pdev = &pdev->dev; 408 - mci->mtype_cap = MEM_FLAG_DDR3; 366 + if (skl) 367 + mci->mtype_cap = MEM_FLAG_DDR4; 368 + else 369 + mci->mtype_cap = MEM_FLAG_DDR3; 409 370 mci->edac_ctl_cap = EDAC_FLAG_SECDED; 410 371 mci->edac_cap = EDAC_FLAG_SECDED; 411 372 mci->mod_name = EDAC_MOD_STR; ··· 419 374 mci->ctl_page_to_phys = NULL; 420 375 priv = mci->pvt_info; 421 376 priv->window = window; 377 + if (skl) { 378 + priv->c0errlog = window + IE31200_C0ECCERRLOG_SKL; 379 + priv->c1errlog = window + IE31200_C1ECCERRLOG_SKL; 380 + mad_offset = IE31200_MAD_DIMM_0_OFFSET_SKL; 381 + } else { 382 + priv->c0errlog = window + IE31200_C0ECCERRLOG; 383 + priv->c1errlog = window + IE31200_C1ECCERRLOG; 384 + mad_offset = IE31200_MAD_DIMM_0_OFFSET; 385 + } 422 386 423 387 /* populate DIMM info */ 424 388 for (i = 0; i < IE31200_CHANNELS; i++) { 425 - addr_decode = readl(window + IE31200_MAD_DIMM_0_OFFSET + 389 + addr_decode = readl(window + mad_offset + 426 390 (i * 4)); 427 391 edac_dbg(0, "addr_decode: 0x%x\n", addr_decode); 428 392 for (j = 0; j < IE31200_DIMMS_PER_CHANNEL; j++) { 429 - dimm_info[i][j].size = (addr_decode >> (j * 8)) & 430 - IE31200_MAD_DIMM_SIZE; 431 - dimm_info[i][j].dual_rank = (addr_decode & 432 - (IE31200_MAD_DIMM_A_RANK << j)) ? 1 : 0; 433 - dimm_info[i][j].x16_width = (addr_decode & 434 - (IE31200_MAD_DIMM_A_WIDTH << j)) ? 1 : 0; 393 + populate_dimm_info(&dimm_info[i][j], addr_decode, j, 394 + skl); 435 395 edac_dbg(0, "size: 0x%x, rank: %d, width: %d\n", 436 396 dimm_info[i][j].size, 437 397 dimm_info[i][j].dual_rank, ··· 455 405 struct dimm_info *dimm; 456 406 unsigned long nr_pages; 457 407 458 - nr_pages = IE31200_PAGES(dimm_info[j][i].size); 408 + nr_pages = IE31200_PAGES(dimm_info[j][i].size, skl); 459 409 if (nr_pages == 0) 460 410 continue; 461 411 ··· 467 417 dimm->nr_pages = nr_pages; 468 418 edac_dbg(0, "set nr pages: 0x%lx\n", nr_pages); 469 419 dimm->grain = 8; /* just a guess */ 470 - dimm->mtype = MEM_DDR3; 420 + if (skl) 421 + dimm->mtype = MEM_DDR4; 422 + else 423 + dimm->mtype = MEM_DDR3; 471 424 dimm->dtype = DEV_UNKNOWN; 472 425 dimm->edac_mode = EDAC_UNKNOWN; 473 426 } ··· 479 426 dimm->nr_pages = nr_pages; 480 427 edac_dbg(0, "set nr pages: 0x%lx\n", nr_pages); 481 428 dimm->grain = 8; /* same guess */ 482 - dimm->mtype = MEM_DDR3; 429 + if (skl) 430 + dimm->mtype = MEM_DDR4; 431 + else 432 + dimm->mtype = MEM_DDR3; 483 433 dimm->dtype = DEV_UNKNOWN; 484 434 dimm->edac_mode = EDAC_UNKNOWN; 485 435 } ··· 555 499 IE31200}, 556 500 { 557 501 PCI_VEND_DEV(INTEL, IE31200_HB_7), PCI_ANY_ID, PCI_ANY_ID, 0, 0, 502 + IE31200}, 503 + { 504 + PCI_VEND_DEV(INTEL, IE31200_HB_8), PCI_ANY_ID, PCI_ANY_ID, 0, 0, 558 505 IE31200}, 559 506 { 560 507 0,