diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/edac/amd64_edac.c | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index b4ef2c70500e..744a49ac9f5c 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c | |||
@@ -1367,3 +1367,199 @@ static void f10_read_dram_base_limit(struct amd64_pvt *pvt, int dram) | |||
1367 | pvt->dram_limit[dram] = | 1367 | pvt->dram_limit[dram] = |
1368 | ((((u64) high_limit << 32) + (u64) low_limit) << 8) | (0xFF); | 1368 | ((((u64) high_limit << 32) + (u64) low_limit) << 8) | (0xFF); |
1369 | } | 1369 | } |
1370 | |||
1371 | static void f10_read_dram_ctl_register(struct amd64_pvt *pvt) | ||
1372 | { | ||
1373 | int err = 0; | ||
1374 | |||
1375 | err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCTL_SEL_LOW, | ||
1376 | &pvt->dram_ctl_select_low); | ||
1377 | if (err) { | ||
1378 | debugf0("Reading F10_DCTL_SEL_LOW failed\n"); | ||
1379 | } else { | ||
1380 | debugf0("DRAM_DCTL_SEL_LOW=0x%x DctSelBaseAddr=0x%x\n", | ||
1381 | pvt->dram_ctl_select_low, dct_sel_baseaddr(pvt)); | ||
1382 | |||
1383 | debugf0(" DRAM DCTs are=%s DRAM Is=%s DRAM-Ctl-" | ||
1384 | "sel-hi-range=%s\n", | ||
1385 | (dct_ganging_enabled(pvt) ? "GANGED" : "NOT GANGED"), | ||
1386 | (dct_dram_enabled(pvt) ? "Enabled" : "Disabled"), | ||
1387 | (dct_high_range_enabled(pvt) ? "Enabled" : "Disabled")); | ||
1388 | |||
1389 | debugf0(" DctDatIntLv=%s MemCleared=%s DctSelIntLvAddr=0x%x\n", | ||
1390 | (dct_data_intlv_enabled(pvt) ? "Enabled" : "Disabled"), | ||
1391 | (dct_memory_cleared(pvt) ? "True " : "False "), | ||
1392 | dct_sel_interleave_addr(pvt)); | ||
1393 | } | ||
1394 | |||
1395 | err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCTL_SEL_HIGH, | ||
1396 | &pvt->dram_ctl_select_high); | ||
1397 | if (err) | ||
1398 | debugf0("Reading F10_DCTL_SEL_HIGH failed\n"); | ||
1399 | } | ||
1400 | |||
1401 | static u32 f10_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, | ||
1402 | int hi_range_sel, u32 intlv_en) | ||
1403 | { | ||
1404 | u32 cs, temp, dct_sel_high = (pvt->dram_ctl_select_low >> 1) & 1; | ||
1405 | |||
1406 | if (dct_ganging_enabled(pvt)) | ||
1407 | cs = 0; | ||
1408 | else if (hi_range_sel) | ||
1409 | cs = dct_sel_high; | ||
1410 | else if (dct_interleave_enabled(pvt)) { | ||
1411 | if (dct_sel_interleave_addr(pvt) == 0) | ||
1412 | cs = sys_addr >> 6 & 1; | ||
1413 | else if ((dct_sel_interleave_addr(pvt) >> 1) & 1) { | ||
1414 | temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) % 2; | ||
1415 | |||
1416 | if (dct_sel_interleave_addr(pvt) & 1) | ||
1417 | cs = (sys_addr >> 9 & 1) ^ temp; | ||
1418 | else | ||
1419 | cs = (sys_addr >> 6 & 1) ^ temp; | ||
1420 | } else if (intlv_en & 4) | ||
1421 | cs = sys_addr >> 15 & 1; | ||
1422 | else if (intlv_en & 2) | ||
1423 | cs = sys_addr >> 14 & 1; | ||
1424 | else if (intlv_en & 1) | ||
1425 | cs = sys_addr >> 13 & 1; | ||
1426 | else | ||
1427 | cs = sys_addr >> 12 & 1; | ||
1428 | } else if (dct_high_range_enabled(pvt) && !dct_ganging_enabled(pvt)) | ||
1429 | cs = ~dct_sel_high & 1; | ||
1430 | else | ||
1431 | cs = 0; | ||
1432 | |||
1433 | return cs; | ||
1434 | } | ||
1435 | |||
1436 | static inline u32 f10_map_intlv_en_to_shift(u32 intlv_en) | ||
1437 | { | ||
1438 | if (intlv_en == 1) | ||
1439 | return 1; | ||
1440 | else if (intlv_en == 3) | ||
1441 | return 2; | ||
1442 | else if (intlv_en == 7) | ||
1443 | return 3; | ||
1444 | |||
1445 | return 0; | ||
1446 | } | ||
1447 | |||
1448 | static inline u64 f10_determine_base_addr_offset(u64 sys_addr, int hi_range_sel, | ||
1449 | u32 dct_sel_base_addr, | ||
1450 | u64 dct_sel_base_off, | ||
1451 | u32 hole_en, u32 hole_off, | ||
1452 | u64 dram_base) | ||
1453 | { | ||
1454 | u64 chan_off; | ||
1455 | |||
1456 | if (hi_range_sel) { | ||
1457 | if (!(dct_sel_base_addr & 0xFFFFF800) && | ||
1458 | (hole_en & 1) && (sys_addr >= 0x100000000ULL)) | ||
1459 | chan_off = hole_off << 16; | ||
1460 | else | ||
1461 | chan_off = dct_sel_base_off; | ||
1462 | } else { | ||
1463 | if ((hole_en & 1) && (sys_addr >= 0x100000000ULL)) | ||
1464 | chan_off = hole_off << 16; | ||
1465 | else | ||
1466 | chan_off = dram_base & 0xFFFFF8000000ULL; | ||
1467 | } | ||
1468 | |||
1469 | return (sys_addr & 0x0000FFFFFFFFFFC0ULL) - | ||
1470 | (chan_off & 0x0000FFFFFF800000ULL); | ||
1471 | } | ||
1472 | |||
1473 | /* Hack for the time being - Can we get this from BIOS?? */ | ||
1474 | #define CH0SPARE_RANK 0 | ||
1475 | #define CH1SPARE_RANK 1 | ||
1476 | |||
1477 | /* | ||
1478 | * checks if the csrow passed in is marked as SPARED, if so returns the new | ||
1479 | * spare row | ||
1480 | */ | ||
1481 | static inline int f10_process_possible_spare(int csrow, | ||
1482 | u32 cs, struct amd64_pvt *pvt) | ||
1483 | { | ||
1484 | u32 swap_done; | ||
1485 | u32 bad_dram_cs; | ||
1486 | |||
1487 | /* Depending on channel, isolate respective SPARING info */ | ||
1488 | if (cs) { | ||
1489 | swap_done = F10_ONLINE_SPARE_SWAPDONE1(pvt->online_spare); | ||
1490 | bad_dram_cs = F10_ONLINE_SPARE_BADDRAM_CS1(pvt->online_spare); | ||
1491 | if (swap_done && (csrow == bad_dram_cs)) | ||
1492 | csrow = CH1SPARE_RANK; | ||
1493 | } else { | ||
1494 | swap_done = F10_ONLINE_SPARE_SWAPDONE0(pvt->online_spare); | ||
1495 | bad_dram_cs = F10_ONLINE_SPARE_BADDRAM_CS0(pvt->online_spare); | ||
1496 | if (swap_done && (csrow == bad_dram_cs)) | ||
1497 | csrow = CH0SPARE_RANK; | ||
1498 | } | ||
1499 | return csrow; | ||
1500 | } | ||
1501 | |||
1502 | /* | ||
1503 | * Iterate over the DRAM DCT "base" and "mask" registers looking for a | ||
1504 | * SystemAddr match on the specified 'ChannelSelect' and 'NodeID' | ||
1505 | * | ||
1506 | * Return: | ||
1507 | * -EINVAL: NOT FOUND | ||
1508 | * 0..csrow = Chip-Select Row | ||
1509 | */ | ||
1510 | static int f10_lookup_addr_in_dct(u32 in_addr, u32 nid, u32 cs) | ||
1511 | { | ||
1512 | struct mem_ctl_info *mci; | ||
1513 | struct amd64_pvt *pvt; | ||
1514 | u32 cs_base, cs_mask; | ||
1515 | int cs_found = -EINVAL; | ||
1516 | int csrow; | ||
1517 | |||
1518 | mci = mci_lookup[nid]; | ||
1519 | if (!mci) | ||
1520 | return cs_found; | ||
1521 | |||
1522 | pvt = mci->pvt_info; | ||
1523 | |||
1524 | debugf1("InputAddr=0x%x channelselect=%d\n", in_addr, cs); | ||
1525 | |||
1526 | for (csrow = 0; csrow < CHIPSELECT_COUNT; csrow++) { | ||
1527 | |||
1528 | cs_base = amd64_get_dct_base(pvt, cs, csrow); | ||
1529 | if (!(cs_base & K8_DCSB_CS_ENABLE)) | ||
1530 | continue; | ||
1531 | |||
1532 | /* | ||
1533 | * We have an ENABLED CSROW, Isolate just the MASK bits of the | ||
1534 | * target: [28:19] and [13:5], which map to [36:27] and [21:13] | ||
1535 | * of the actual address. | ||
1536 | */ | ||
1537 | cs_base &= REV_F_F1Xh_DCSB_BASE_BITS; | ||
1538 | |||
1539 | /* | ||
1540 | * Get the DCT Mask, and ENABLE the reserved bits: [18:16] and | ||
1541 | * [4:0] to become ON. Then mask off bits [28:0] ([36:8]) | ||
1542 | */ | ||
1543 | cs_mask = amd64_get_dct_mask(pvt, cs, csrow); | ||
1544 | |||
1545 | debugf1(" CSROW=%d CSBase=0x%x RAW CSMask=0x%x\n", | ||
1546 | csrow, cs_base, cs_mask); | ||
1547 | |||
1548 | cs_mask = (cs_mask | 0x0007C01F) & 0x1FFFFFFF; | ||
1549 | |||
1550 | debugf1(" Final CSMask=0x%x\n", cs_mask); | ||
1551 | debugf1(" (InputAddr & ~CSMask)=0x%x " | ||
1552 | "(CSBase & ~CSMask)=0x%x\n", | ||
1553 | (in_addr & ~cs_mask), (cs_base & ~cs_mask)); | ||
1554 | |||
1555 | if ((in_addr & ~cs_mask) == (cs_base & ~cs_mask)) { | ||
1556 | cs_found = f10_process_possible_spare(csrow, cs, pvt); | ||
1557 | |||
1558 | debugf1(" MATCH csrow=%d\n", cs_found); | ||
1559 | break; | ||
1560 | } | ||
1561 | } | ||
1562 | return cs_found; | ||
1563 | } | ||
1564 | |||
1565 | |||