18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci#include "amd64_edac.h" 38c2ecf20Sopenharmony_ci#include <asm/amd_nb.h> 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_cistatic struct edac_pci_ctl_info *pci_ctl; 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci/* 88c2ecf20Sopenharmony_ci * Set by command line parameter. If BIOS has enabled the ECC, this override is 98c2ecf20Sopenharmony_ci * cleared to prevent re-enabling the hardware by this driver. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_cistatic int ecc_enable_override; 128c2ecf20Sopenharmony_cimodule_param(ecc_enable_override, int, 0644); 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic struct msr __percpu *msrs; 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic struct amd64_family_type *fam_type; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* Per-node stuff */ 198c2ecf20Sopenharmony_cistatic struct ecc_settings **ecc_stngs; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* Device for the PCI component */ 228c2ecf20Sopenharmony_cistatic struct device *pci_ctl_dev; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* 258c2ecf20Sopenharmony_ci * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing 268c2ecf20Sopenharmony_ci * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching- 278c2ecf20Sopenharmony_ci * or higher value'. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci *FIXME: Produce a better mapping/linearisation. 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_cistatic const struct scrubrate { 328c2ecf20Sopenharmony_ci u32 scrubval; /* bit pattern for scrub rate */ 338c2ecf20Sopenharmony_ci u32 bandwidth; /* bandwidth consumed (bytes/sec) */ 348c2ecf20Sopenharmony_ci} scrubrates[] = { 358c2ecf20Sopenharmony_ci { 0x01, 1600000000UL}, 368c2ecf20Sopenharmony_ci { 0x02, 800000000UL}, 378c2ecf20Sopenharmony_ci { 0x03, 400000000UL}, 388c2ecf20Sopenharmony_ci { 0x04, 200000000UL}, 398c2ecf20Sopenharmony_ci { 0x05, 100000000UL}, 408c2ecf20Sopenharmony_ci { 0x06, 50000000UL}, 418c2ecf20Sopenharmony_ci { 0x07, 25000000UL}, 428c2ecf20Sopenharmony_ci { 0x08, 12284069UL}, 438c2ecf20Sopenharmony_ci { 0x09, 6274509UL}, 448c2ecf20Sopenharmony_ci { 0x0A, 3121951UL}, 458c2ecf20Sopenharmony_ci { 0x0B, 1560975UL}, 468c2ecf20Sopenharmony_ci { 0x0C, 781440UL}, 478c2ecf20Sopenharmony_ci { 0x0D, 390720UL}, 488c2ecf20Sopenharmony_ci { 0x0E, 195300UL}, 498c2ecf20Sopenharmony_ci { 0x0F, 97650UL}, 508c2ecf20Sopenharmony_ci { 0x10, 48854UL}, 518c2ecf20Sopenharmony_ci { 0x11, 24427UL}, 528c2ecf20Sopenharmony_ci { 0x12, 12213UL}, 538c2ecf20Sopenharmony_ci { 0x13, 6101UL}, 548c2ecf20Sopenharmony_ci { 0x14, 3051UL}, 558c2ecf20Sopenharmony_ci { 0x15, 1523UL}, 568c2ecf20Sopenharmony_ci { 0x16, 761UL}, 578c2ecf20Sopenharmony_ci { 0x00, 0UL}, /* scrubbing off */ 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ciint __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset, 618c2ecf20Sopenharmony_ci u32 *val, const char *func) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci int err = 0; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci err = pci_read_config_dword(pdev, offset, val); 668c2ecf20Sopenharmony_ci if (err) 678c2ecf20Sopenharmony_ci amd64_warn("%s: error reading F%dx%03x.\n", 688c2ecf20Sopenharmony_ci func, PCI_FUNC(pdev->devfn), offset); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return err; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ciint __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset, 748c2ecf20Sopenharmony_ci u32 val, const char *func) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci int err = 0; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci err = pci_write_config_dword(pdev, offset, val); 798c2ecf20Sopenharmony_ci if (err) 808c2ecf20Sopenharmony_ci amd64_warn("%s: error writing to F%dx%03x.\n", 818c2ecf20Sopenharmony_ci func, PCI_FUNC(pdev->devfn), offset); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return err; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* 878c2ecf20Sopenharmony_ci * Select DCT to which PCI cfg accesses are routed 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_cistatic void f15h_select_dct(struct amd64_pvt *pvt, u8 dct) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci u32 reg = 0; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, ®); 948c2ecf20Sopenharmony_ci reg &= (pvt->model == 0x30) ? ~3 : ~1; 958c2ecf20Sopenharmony_ci reg |= dct; 968c2ecf20Sopenharmony_ci amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * Depending on the family, F2 DCT reads need special handling: 1028c2ecf20Sopenharmony_ci * 1038c2ecf20Sopenharmony_ci * K8: has a single DCT only and no address offsets >= 0x100 1048c2ecf20Sopenharmony_ci * 1058c2ecf20Sopenharmony_ci * F10h: each DCT has its own set of regs 1068c2ecf20Sopenharmony_ci * DCT0 -> F2x040.. 1078c2ecf20Sopenharmony_ci * DCT1 -> F2x140.. 1088c2ecf20Sopenharmony_ci * 1098c2ecf20Sopenharmony_ci * F16h: has only 1 DCT 1108c2ecf20Sopenharmony_ci * 1118c2ecf20Sopenharmony_ci * F15h: we select which DCT we access using F1x10C[DctCfgSel] 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_cistatic inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct, 1148c2ecf20Sopenharmony_ci int offset, u32 *val) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci switch (pvt->fam) { 1178c2ecf20Sopenharmony_ci case 0xf: 1188c2ecf20Sopenharmony_ci if (dct || offset >= 0x100) 1198c2ecf20Sopenharmony_ci return -EINVAL; 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci case 0x10: 1238c2ecf20Sopenharmony_ci if (dct) { 1248c2ecf20Sopenharmony_ci /* 1258c2ecf20Sopenharmony_ci * Note: If ganging is enabled, barring the regs 1268c2ecf20Sopenharmony_ci * F2x[1,0]98 and F2x[1,0]9C; reads reads to F2x1xx 1278c2ecf20Sopenharmony_ci * return 0. (cf. Section 2.8.1 F10h BKDG) 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ci if (dct_ganging_enabled(pvt)) 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci offset += 0x100; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci case 0x15: 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * F15h: F2x1xx addresses do not map explicitly to DCT1. 1398c2ecf20Sopenharmony_ci * We should select which DCT we access using F1x10C[DctCfgSel] 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci dct = (dct && pvt->model == 0x30) ? 3 : dct; 1428c2ecf20Sopenharmony_ci f15h_select_dct(pvt, dct); 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci case 0x16: 1468c2ecf20Sopenharmony_ci if (dct) 1478c2ecf20Sopenharmony_ci return -EINVAL; 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci default: 1518c2ecf20Sopenharmony_ci break; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci return amd64_read_pci_cfg(pvt->F2, offset, val); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* 1578c2ecf20Sopenharmony_ci * Memory scrubber control interface. For K8, memory scrubbing is handled by 1588c2ecf20Sopenharmony_ci * hardware and can involve L2 cache, dcache as well as the main memory. With 1598c2ecf20Sopenharmony_ci * F10, this is extended to L3 cache scrubbing on CPU models sporting that 1608c2ecf20Sopenharmony_ci * functionality. 1618c2ecf20Sopenharmony_ci * 1628c2ecf20Sopenharmony_ci * This causes the "units" for the scrubbing speed to vary from 64 byte blocks 1638c2ecf20Sopenharmony_ci * (dram) over to cache lines. This is nasty, so we will use bandwidth in 1648c2ecf20Sopenharmony_ci * bytes/sec for the setting. 1658c2ecf20Sopenharmony_ci * 1668c2ecf20Sopenharmony_ci * Currently, we only do dram scrubbing. If the scrubbing is done in software on 1678c2ecf20Sopenharmony_ci * other archs, we might not have access to the caches directly. 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic inline void __f17h_set_scrubval(struct amd64_pvt *pvt, u32 scrubval) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci /* 1738c2ecf20Sopenharmony_ci * Fam17h supports scrub values between 0x5 and 0x14. Also, the values 1748c2ecf20Sopenharmony_ci * are shifted down by 0x5, so scrubval 0x5 is written to the register 1758c2ecf20Sopenharmony_ci * as 0x0, scrubval 0x6 as 0x1, etc. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci if (scrubval >= 0x5 && scrubval <= 0x14) { 1788c2ecf20Sopenharmony_ci scrubval -= 0x5; 1798c2ecf20Sopenharmony_ci pci_write_bits32(pvt->F6, F17H_SCR_LIMIT_ADDR, scrubval, 0xF); 1808c2ecf20Sopenharmony_ci pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 1, 0x1); 1818c2ecf20Sopenharmony_ci } else { 1828c2ecf20Sopenharmony_ci pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 0, 0x1); 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci/* 1868c2ecf20Sopenharmony_ci * Scan the scrub rate mapping table for a close or matching bandwidth value to 1878c2ecf20Sopenharmony_ci * issue. If requested is too big, then use last maximum value found. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_cistatic int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci u32 scrubval; 1928c2ecf20Sopenharmony_ci int i; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* 1958c2ecf20Sopenharmony_ci * map the configured rate (new_bw) to a value specific to the AMD64 1968c2ecf20Sopenharmony_ci * memory controller and apply to register. Search for the first 1978c2ecf20Sopenharmony_ci * bandwidth entry that is greater or equal than the setting requested 1988c2ecf20Sopenharmony_ci * and program that. If at last entry, turn off DRAM scrubbing. 1998c2ecf20Sopenharmony_ci * 2008c2ecf20Sopenharmony_ci * If no suitable bandwidth is found, turn off DRAM scrubbing entirely 2018c2ecf20Sopenharmony_ci * by falling back to the last element in scrubrates[]. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(scrubrates) - 1; i++) { 2048c2ecf20Sopenharmony_ci /* 2058c2ecf20Sopenharmony_ci * skip scrub rates which aren't recommended 2068c2ecf20Sopenharmony_ci * (see F10 BKDG, F3x58) 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_ci if (scrubrates[i].scrubval < min_rate) 2098c2ecf20Sopenharmony_ci continue; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (scrubrates[i].bandwidth <= new_bw) 2128c2ecf20Sopenharmony_ci break; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci scrubval = scrubrates[i].scrubval; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (pvt->umc) { 2188c2ecf20Sopenharmony_ci __f17h_set_scrubval(pvt, scrubval); 2198c2ecf20Sopenharmony_ci } else if (pvt->fam == 0x15 && pvt->model == 0x60) { 2208c2ecf20Sopenharmony_ci f15h_select_dct(pvt, 0); 2218c2ecf20Sopenharmony_ci pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F); 2228c2ecf20Sopenharmony_ci f15h_select_dct(pvt, 1); 2238c2ecf20Sopenharmony_ci pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F); 2248c2ecf20Sopenharmony_ci } else { 2258c2ecf20Sopenharmony_ci pci_write_bits32(pvt->F3, SCRCTRL, scrubval, 0x001F); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (scrubval) 2298c2ecf20Sopenharmony_ci return scrubrates[i].bandwidth; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return 0; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int set_scrub_rate(struct mem_ctl_info *mci, u32 bw) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 2378c2ecf20Sopenharmony_ci u32 min_scrubrate = 0x5; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (pvt->fam == 0xf) 2408c2ecf20Sopenharmony_ci min_scrubrate = 0x0; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (pvt->fam == 0x15) { 2438c2ecf20Sopenharmony_ci /* Erratum #505 */ 2448c2ecf20Sopenharmony_ci if (pvt->model < 0x10) 2458c2ecf20Sopenharmony_ci f15h_select_dct(pvt, 0); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (pvt->model == 0x60) 2488c2ecf20Sopenharmony_ci min_scrubrate = 0x6; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci return __set_scrub_rate(pvt, bw, min_scrubrate); 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int get_scrub_rate(struct mem_ctl_info *mci) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 2568c2ecf20Sopenharmony_ci int i, retval = -EINVAL; 2578c2ecf20Sopenharmony_ci u32 scrubval = 0; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (pvt->umc) { 2608c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F6, F17H_SCR_BASE_ADDR, &scrubval); 2618c2ecf20Sopenharmony_ci if (scrubval & BIT(0)) { 2628c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F6, F17H_SCR_LIMIT_ADDR, &scrubval); 2638c2ecf20Sopenharmony_ci scrubval &= 0xF; 2648c2ecf20Sopenharmony_ci scrubval += 0x5; 2658c2ecf20Sopenharmony_ci } else { 2668c2ecf20Sopenharmony_ci scrubval = 0; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci } else if (pvt->fam == 0x15) { 2698c2ecf20Sopenharmony_ci /* Erratum #505 */ 2708c2ecf20Sopenharmony_ci if (pvt->model < 0x10) 2718c2ecf20Sopenharmony_ci f15h_select_dct(pvt, 0); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (pvt->model == 0x60) 2748c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F2, F15H_M60H_SCRCTRL, &scrubval); 2758c2ecf20Sopenharmony_ci else 2768c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval); 2778c2ecf20Sopenharmony_ci } else { 2788c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval); 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci scrubval = scrubval & 0x001F; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(scrubrates); i++) { 2848c2ecf20Sopenharmony_ci if (scrubrates[i].scrubval == scrubval) { 2858c2ecf20Sopenharmony_ci retval = scrubrates[i].bandwidth; 2868c2ecf20Sopenharmony_ci break; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci return retval; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/* 2938c2ecf20Sopenharmony_ci * returns true if the SysAddr given by sys_addr matches the 2948c2ecf20Sopenharmony_ci * DRAM base/limit associated with node_id 2958c2ecf20Sopenharmony_ci */ 2968c2ecf20Sopenharmony_cistatic bool base_limit_match(struct amd64_pvt *pvt, u64 sys_addr, u8 nid) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci u64 addr; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* The K8 treats this as a 40-bit value. However, bits 63-40 will be 3018c2ecf20Sopenharmony_ci * all ones if the most significant implemented address bit is 1. 3028c2ecf20Sopenharmony_ci * Here we discard bits 63-40. See section 3.4.2 of AMD publication 3038c2ecf20Sopenharmony_ci * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1 3048c2ecf20Sopenharmony_ci * Application Programming. 3058c2ecf20Sopenharmony_ci */ 3068c2ecf20Sopenharmony_ci addr = sys_addr & 0x000000ffffffffffull; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return ((addr >= get_dram_base(pvt, nid)) && 3098c2ecf20Sopenharmony_ci (addr <= get_dram_limit(pvt, nid))); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/* 3138c2ecf20Sopenharmony_ci * Attempt to map a SysAddr to a node. On success, return a pointer to the 3148c2ecf20Sopenharmony_ci * mem_ctl_info structure for the node that the SysAddr maps to. 3158c2ecf20Sopenharmony_ci * 3168c2ecf20Sopenharmony_ci * On failure, return NULL. 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_cistatic struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci, 3198c2ecf20Sopenharmony_ci u64 sys_addr) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct amd64_pvt *pvt; 3228c2ecf20Sopenharmony_ci u8 node_id; 3238c2ecf20Sopenharmony_ci u32 intlv_en, bits; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* 3268c2ecf20Sopenharmony_ci * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section 3278c2ecf20Sopenharmony_ci * 3.4.4.2) registers to map the SysAddr to a node ID. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* 3328c2ecf20Sopenharmony_ci * The value of this field should be the same for all DRAM Base 3338c2ecf20Sopenharmony_ci * registers. Therefore we arbitrarily choose to read it from the 3348c2ecf20Sopenharmony_ci * register for node 0. 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_ci intlv_en = dram_intlv_en(pvt, 0); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (intlv_en == 0) { 3398c2ecf20Sopenharmony_ci for (node_id = 0; node_id < DRAM_RANGES; node_id++) { 3408c2ecf20Sopenharmony_ci if (base_limit_match(pvt, sys_addr, node_id)) 3418c2ecf20Sopenharmony_ci goto found; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci goto err_no_match; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (unlikely((intlv_en != 0x01) && 3478c2ecf20Sopenharmony_ci (intlv_en != 0x03) && 3488c2ecf20Sopenharmony_ci (intlv_en != 0x07))) { 3498c2ecf20Sopenharmony_ci amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en); 3508c2ecf20Sopenharmony_ci return NULL; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci bits = (((u32) sys_addr) >> 12) & intlv_en; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci for (node_id = 0; ; ) { 3568c2ecf20Sopenharmony_ci if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits) 3578c2ecf20Sopenharmony_ci break; /* intlv_sel field matches */ 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (++node_id >= DRAM_RANGES) 3608c2ecf20Sopenharmony_ci goto err_no_match; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* sanity test for sys_addr */ 3648c2ecf20Sopenharmony_ci if (unlikely(!base_limit_match(pvt, sys_addr, node_id))) { 3658c2ecf20Sopenharmony_ci amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address" 3668c2ecf20Sopenharmony_ci "range for node %d with node interleaving enabled.\n", 3678c2ecf20Sopenharmony_ci __func__, sys_addr, node_id); 3688c2ecf20Sopenharmony_ci return NULL; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cifound: 3728c2ecf20Sopenharmony_ci return edac_mc_find((int)node_id); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cierr_no_match: 3758c2ecf20Sopenharmony_ci edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n", 3768c2ecf20Sopenharmony_ci (unsigned long)sys_addr); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return NULL; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci/* 3828c2ecf20Sopenharmony_ci * compute the CS base address of the @csrow on the DRAM controller @dct. 3838c2ecf20Sopenharmony_ci * For details see F2x[5C:40] in the processor's BKDG 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_cistatic void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct, 3868c2ecf20Sopenharmony_ci u64 *base, u64 *mask) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci u64 csbase, csmask, base_bits, mask_bits; 3898c2ecf20Sopenharmony_ci u8 addr_shift; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) { 3928c2ecf20Sopenharmony_ci csbase = pvt->csels[dct].csbases[csrow]; 3938c2ecf20Sopenharmony_ci csmask = pvt->csels[dct].csmasks[csrow]; 3948c2ecf20Sopenharmony_ci base_bits = GENMASK_ULL(31, 21) | GENMASK_ULL(15, 9); 3958c2ecf20Sopenharmony_ci mask_bits = GENMASK_ULL(29, 21) | GENMASK_ULL(15, 9); 3968c2ecf20Sopenharmony_ci addr_shift = 4; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* 3998c2ecf20Sopenharmony_ci * F16h and F15h, models 30h and later need two addr_shift values: 4008c2ecf20Sopenharmony_ci * 8 for high and 6 for low (cf. F16h BKDG). 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_ci } else if (pvt->fam == 0x16 || 4038c2ecf20Sopenharmony_ci (pvt->fam == 0x15 && pvt->model >= 0x30)) { 4048c2ecf20Sopenharmony_ci csbase = pvt->csels[dct].csbases[csrow]; 4058c2ecf20Sopenharmony_ci csmask = pvt->csels[dct].csmasks[csrow >> 1]; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci *base = (csbase & GENMASK_ULL(15, 5)) << 6; 4088c2ecf20Sopenharmony_ci *base |= (csbase & GENMASK_ULL(30, 19)) << 8; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci *mask = ~0ULL; 4118c2ecf20Sopenharmony_ci /* poke holes for the csmask */ 4128c2ecf20Sopenharmony_ci *mask &= ~((GENMASK_ULL(15, 5) << 6) | 4138c2ecf20Sopenharmony_ci (GENMASK_ULL(30, 19) << 8)); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci *mask |= (csmask & GENMASK_ULL(15, 5)) << 6; 4168c2ecf20Sopenharmony_ci *mask |= (csmask & GENMASK_ULL(30, 19)) << 8; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return; 4198c2ecf20Sopenharmony_ci } else { 4208c2ecf20Sopenharmony_ci csbase = pvt->csels[dct].csbases[csrow]; 4218c2ecf20Sopenharmony_ci csmask = pvt->csels[dct].csmasks[csrow >> 1]; 4228c2ecf20Sopenharmony_ci addr_shift = 8; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (pvt->fam == 0x15) 4258c2ecf20Sopenharmony_ci base_bits = mask_bits = 4268c2ecf20Sopenharmony_ci GENMASK_ULL(30,19) | GENMASK_ULL(13,5); 4278c2ecf20Sopenharmony_ci else 4288c2ecf20Sopenharmony_ci base_bits = mask_bits = 4298c2ecf20Sopenharmony_ci GENMASK_ULL(28,19) | GENMASK_ULL(13,5); 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci *base = (csbase & base_bits) << addr_shift; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci *mask = ~0ULL; 4358c2ecf20Sopenharmony_ci /* poke holes for the csmask */ 4368c2ecf20Sopenharmony_ci *mask &= ~(mask_bits << addr_shift); 4378c2ecf20Sopenharmony_ci /* OR them in */ 4388c2ecf20Sopenharmony_ci *mask |= (csmask & mask_bits) << addr_shift; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci#define for_each_chip_select(i, dct, pvt) \ 4428c2ecf20Sopenharmony_ci for (i = 0; i < pvt->csels[dct].b_cnt; i++) 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci#define chip_select_base(i, dct, pvt) \ 4458c2ecf20Sopenharmony_ci pvt->csels[dct].csbases[i] 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci#define for_each_chip_select_mask(i, dct, pvt) \ 4488c2ecf20Sopenharmony_ci for (i = 0; i < pvt->csels[dct].m_cnt; i++) 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci#define for_each_umc(i) \ 4518c2ecf20Sopenharmony_ci for (i = 0; i < fam_type->max_mcs; i++) 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci/* 4548c2ecf20Sopenharmony_ci * @input_addr is an InputAddr associated with the node given by mci. Return the 4558c2ecf20Sopenharmony_ci * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr). 4568c2ecf20Sopenharmony_ci */ 4578c2ecf20Sopenharmony_cistatic int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct amd64_pvt *pvt; 4608c2ecf20Sopenharmony_ci int csrow; 4618c2ecf20Sopenharmony_ci u64 base, mask; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci for_each_chip_select(csrow, 0, pvt) { 4668c2ecf20Sopenharmony_ci if (!csrow_enabled(csrow, 0, pvt)) 4678c2ecf20Sopenharmony_ci continue; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci get_cs_base_and_mask(pvt, csrow, 0, &base, &mask); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci mask = ~mask; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if ((input_addr & mask) == (base & mask)) { 4748c2ecf20Sopenharmony_ci edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n", 4758c2ecf20Sopenharmony_ci (unsigned long)input_addr, csrow, 4768c2ecf20Sopenharmony_ci pvt->mc_node_id); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci return csrow; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n", 4828c2ecf20Sopenharmony_ci (unsigned long)input_addr, pvt->mc_node_id); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci return -1; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci/* 4888c2ecf20Sopenharmony_ci * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094) 4898c2ecf20Sopenharmony_ci * for the node represented by mci. Info is passed back in *hole_base, 4908c2ecf20Sopenharmony_ci * *hole_offset, and *hole_size. Function returns 0 if info is valid or 1 if 4918c2ecf20Sopenharmony_ci * info is invalid. Info may be invalid for either of the following reasons: 4928c2ecf20Sopenharmony_ci * 4938c2ecf20Sopenharmony_ci * - The revision of the node is not E or greater. In this case, the DRAM Hole 4948c2ecf20Sopenharmony_ci * Address Register does not exist. 4958c2ecf20Sopenharmony_ci * 4968c2ecf20Sopenharmony_ci * - The DramHoleValid bit is cleared in the DRAM Hole Address Register, 4978c2ecf20Sopenharmony_ci * indicating that its contents are not valid. 4988c2ecf20Sopenharmony_ci * 4998c2ecf20Sopenharmony_ci * The values passed back in *hole_base, *hole_offset, and *hole_size are 5008c2ecf20Sopenharmony_ci * complete 32-bit values despite the fact that the bitfields in the DHAR 5018c2ecf20Sopenharmony_ci * only represent bits 31-24 of the base and offset values. 5028c2ecf20Sopenharmony_ci */ 5038c2ecf20Sopenharmony_ciint amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base, 5048c2ecf20Sopenharmony_ci u64 *hole_offset, u64 *hole_size) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* only revE and later have the DRAM Hole Address Register */ 5098c2ecf20Sopenharmony_ci if (pvt->fam == 0xf && pvt->ext_model < K8_REV_E) { 5108c2ecf20Sopenharmony_ci edac_dbg(1, " revision %d for node %d does not support DHAR\n", 5118c2ecf20Sopenharmony_ci pvt->ext_model, pvt->mc_node_id); 5128c2ecf20Sopenharmony_ci return 1; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* valid for Fam10h and above */ 5168c2ecf20Sopenharmony_ci if (pvt->fam >= 0x10 && !dhar_mem_hoist_valid(pvt)) { 5178c2ecf20Sopenharmony_ci edac_dbg(1, " Dram Memory Hoisting is DISABLED on this system\n"); 5188c2ecf20Sopenharmony_ci return 1; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (!dhar_valid(pvt)) { 5228c2ecf20Sopenharmony_ci edac_dbg(1, " Dram Memory Hoisting is DISABLED on this node %d\n", 5238c2ecf20Sopenharmony_ci pvt->mc_node_id); 5248c2ecf20Sopenharmony_ci return 1; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* This node has Memory Hoisting */ 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* +------------------+--------------------+--------------------+----- 5308c2ecf20Sopenharmony_ci * | memory | DRAM hole | relocated | 5318c2ecf20Sopenharmony_ci * | [0, (x - 1)] | [x, 0xffffffff] | addresses from | 5328c2ecf20Sopenharmony_ci * | | | DRAM hole | 5338c2ecf20Sopenharmony_ci * | | | [0x100000000, | 5348c2ecf20Sopenharmony_ci * | | | (0x100000000+ | 5358c2ecf20Sopenharmony_ci * | | | (0xffffffff-x))] | 5368c2ecf20Sopenharmony_ci * +------------------+--------------------+--------------------+----- 5378c2ecf20Sopenharmony_ci * 5388c2ecf20Sopenharmony_ci * Above is a diagram of physical memory showing the DRAM hole and the 5398c2ecf20Sopenharmony_ci * relocated addresses from the DRAM hole. As shown, the DRAM hole 5408c2ecf20Sopenharmony_ci * starts at address x (the base address) and extends through address 5418c2ecf20Sopenharmony_ci * 0xffffffff. The DRAM Hole Address Register (DHAR) relocates the 5428c2ecf20Sopenharmony_ci * addresses in the hole so that they start at 0x100000000. 5438c2ecf20Sopenharmony_ci */ 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci *hole_base = dhar_base(pvt); 5468c2ecf20Sopenharmony_ci *hole_size = (1ULL << 32) - *hole_base; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci *hole_offset = (pvt->fam > 0xf) ? f10_dhar_offset(pvt) 5498c2ecf20Sopenharmony_ci : k8_dhar_offset(pvt); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci edac_dbg(1, " DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n", 5528c2ecf20Sopenharmony_ci pvt->mc_node_id, (unsigned long)*hole_base, 5538c2ecf20Sopenharmony_ci (unsigned long)*hole_offset, (unsigned long)*hole_size); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci return 0; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(amd64_get_dram_hole_info); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci/* 5608c2ecf20Sopenharmony_ci * Return the DramAddr that the SysAddr given by @sys_addr maps to. It is 5618c2ecf20Sopenharmony_ci * assumed that sys_addr maps to the node given by mci. 5628c2ecf20Sopenharmony_ci * 5638c2ecf20Sopenharmony_ci * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section 5648c2ecf20Sopenharmony_ci * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a 5658c2ecf20Sopenharmony_ci * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled, 5668c2ecf20Sopenharmony_ci * then it is also involved in translating a SysAddr to a DramAddr. Sections 5678c2ecf20Sopenharmony_ci * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting. 5688c2ecf20Sopenharmony_ci * These parts of the documentation are unclear. I interpret them as follows: 5698c2ecf20Sopenharmony_ci * 5708c2ecf20Sopenharmony_ci * When node n receives a SysAddr, it processes the SysAddr as follows: 5718c2ecf20Sopenharmony_ci * 5728c2ecf20Sopenharmony_ci * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM 5738c2ecf20Sopenharmony_ci * Limit registers for node n. If the SysAddr is not within the range 5748c2ecf20Sopenharmony_ci * specified by the base and limit values, then node n ignores the Sysaddr 5758c2ecf20Sopenharmony_ci * (since it does not map to node n). Otherwise continue to step 2 below. 5768c2ecf20Sopenharmony_ci * 5778c2ecf20Sopenharmony_ci * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is 5788c2ecf20Sopenharmony_ci * disabled so skip to step 3 below. Otherwise see if the SysAddr is within 5798c2ecf20Sopenharmony_ci * the range of relocated addresses (starting at 0x100000000) from the DRAM 5808c2ecf20Sopenharmony_ci * hole. If not, skip to step 3 below. Else get the value of the 5818c2ecf20Sopenharmony_ci * DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the 5828c2ecf20Sopenharmony_ci * offset defined by this value from the SysAddr. 5838c2ecf20Sopenharmony_ci * 5848c2ecf20Sopenharmony_ci * 3. Obtain the base address for node n from the DRAMBase field of the DRAM 5858c2ecf20Sopenharmony_ci * Base register for node n. To obtain the DramAddr, subtract the base 5868c2ecf20Sopenharmony_ci * address from the SysAddr, as shown near the start of section 3.4.4 (p.70). 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_cistatic u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 5918c2ecf20Sopenharmony_ci u64 dram_base, hole_base, hole_offset, hole_size, dram_addr; 5928c2ecf20Sopenharmony_ci int ret; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci dram_base = get_dram_base(pvt, pvt->mc_node_id); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset, 5978c2ecf20Sopenharmony_ci &hole_size); 5988c2ecf20Sopenharmony_ci if (!ret) { 5998c2ecf20Sopenharmony_ci if ((sys_addr >= (1ULL << 32)) && 6008c2ecf20Sopenharmony_ci (sys_addr < ((1ULL << 32) + hole_size))) { 6018c2ecf20Sopenharmony_ci /* use DHAR to translate SysAddr to DramAddr */ 6028c2ecf20Sopenharmony_ci dram_addr = sys_addr - hole_offset; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n", 6058c2ecf20Sopenharmony_ci (unsigned long)sys_addr, 6068c2ecf20Sopenharmony_ci (unsigned long)dram_addr); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci return dram_addr; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci /* 6138c2ecf20Sopenharmony_ci * Translate the SysAddr to a DramAddr as shown near the start of 6148c2ecf20Sopenharmony_ci * section 3.4.4 (p. 70). Although sys_addr is a 64-bit value, the k8 6158c2ecf20Sopenharmony_ci * only deals with 40-bit values. Therefore we discard bits 63-40 of 6168c2ecf20Sopenharmony_ci * sys_addr below. If bit 39 of sys_addr is 1 then the bits we 6178c2ecf20Sopenharmony_ci * discard are all 1s. Otherwise the bits we discard are all 0s. See 6188c2ecf20Sopenharmony_ci * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture 6198c2ecf20Sopenharmony_ci * Programmer's Manual Volume 1 Application Programming. 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_ci dram_addr = (sys_addr & GENMASK_ULL(39, 0)) - dram_base; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n", 6248c2ecf20Sopenharmony_ci (unsigned long)sys_addr, (unsigned long)dram_addr); 6258c2ecf20Sopenharmony_ci return dram_addr; 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci/* 6298c2ecf20Sopenharmony_ci * @intlv_en is the value of the IntlvEn field from a DRAM Base register 6308c2ecf20Sopenharmony_ci * (section 3.4.4.1). Return the number of bits from a SysAddr that are used 6318c2ecf20Sopenharmony_ci * for node interleaving. 6328c2ecf20Sopenharmony_ci */ 6338c2ecf20Sopenharmony_cistatic int num_node_interleave_bits(unsigned intlv_en) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 }; 6368c2ecf20Sopenharmony_ci int n; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci BUG_ON(intlv_en > 7); 6398c2ecf20Sopenharmony_ci n = intlv_shift_table[intlv_en]; 6408c2ecf20Sopenharmony_ci return n; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci/* Translate the DramAddr given by @dram_addr to an InputAddr. */ 6448c2ecf20Sopenharmony_cistatic u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci struct amd64_pvt *pvt; 6478c2ecf20Sopenharmony_ci int intlv_shift; 6488c2ecf20Sopenharmony_ci u64 input_addr; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci /* 6538c2ecf20Sopenharmony_ci * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E) 6548c2ecf20Sopenharmony_ci * concerning translating a DramAddr to an InputAddr. 6558c2ecf20Sopenharmony_ci */ 6568c2ecf20Sopenharmony_ci intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0)); 6578c2ecf20Sopenharmony_ci input_addr = ((dram_addr >> intlv_shift) & GENMASK_ULL(35, 12)) + 6588c2ecf20Sopenharmony_ci (dram_addr & 0xfff); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci edac_dbg(2, " Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n", 6618c2ecf20Sopenharmony_ci intlv_shift, (unsigned long)dram_addr, 6628c2ecf20Sopenharmony_ci (unsigned long)input_addr); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci return input_addr; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci/* 6688c2ecf20Sopenharmony_ci * Translate the SysAddr represented by @sys_addr to an InputAddr. It is 6698c2ecf20Sopenharmony_ci * assumed that @sys_addr maps to the node given by mci. 6708c2ecf20Sopenharmony_ci */ 6718c2ecf20Sopenharmony_cistatic u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci u64 input_addr; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci input_addr = 6768c2ecf20Sopenharmony_ci dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr)); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci edac_dbg(2, "SysAddr 0x%lx translates to InputAddr 0x%lx\n", 6798c2ecf20Sopenharmony_ci (unsigned long)sys_addr, (unsigned long)input_addr); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci return input_addr; 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci/* Map the Error address to a PAGE and PAGE OFFSET. */ 6858c2ecf20Sopenharmony_cistatic inline void error_address_to_page_and_offset(u64 error_address, 6868c2ecf20Sopenharmony_ci struct err_info *err) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci err->page = (u32) (error_address >> PAGE_SHIFT); 6898c2ecf20Sopenharmony_ci err->offset = ((u32) error_address) & ~PAGE_MASK; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci/* 6938c2ecf20Sopenharmony_ci * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address 6948c2ecf20Sopenharmony_ci * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers 6958c2ecf20Sopenharmony_ci * of a node that detected an ECC memory error. mci represents the node that 6968c2ecf20Sopenharmony_ci * the error address maps to (possibly different from the node that detected 6978c2ecf20Sopenharmony_ci * the error). Return the number of the csrow that sys_addr maps to, or -1 on 6988c2ecf20Sopenharmony_ci * error. 6998c2ecf20Sopenharmony_ci */ 7008c2ecf20Sopenharmony_cistatic int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr) 7018c2ecf20Sopenharmony_ci{ 7028c2ecf20Sopenharmony_ci int csrow; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr)); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci if (csrow == -1) 7078c2ecf20Sopenharmony_ci amd64_mc_err(mci, "Failed to translate InputAddr to csrow for " 7088c2ecf20Sopenharmony_ci "address 0x%lx\n", (unsigned long)sys_addr); 7098c2ecf20Sopenharmony_ci return csrow; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci/* 7158c2ecf20Sopenharmony_ci * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs 7168c2ecf20Sopenharmony_ci * are ECC capable. 7178c2ecf20Sopenharmony_ci */ 7188c2ecf20Sopenharmony_cistatic unsigned long determine_edac_cap(struct amd64_pvt *pvt) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci unsigned long edac_cap = EDAC_FLAG_NONE; 7218c2ecf20Sopenharmony_ci u8 bit; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (pvt->umc) { 7248c2ecf20Sopenharmony_ci u8 i, umc_en_mask = 0, dimm_ecc_en_mask = 0; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci for_each_umc(i) { 7278c2ecf20Sopenharmony_ci if (!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT)) 7288c2ecf20Sopenharmony_ci continue; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci umc_en_mask |= BIT(i); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci /* UMC Configuration bit 12 (DimmEccEn) */ 7338c2ecf20Sopenharmony_ci if (pvt->umc[i].umc_cfg & BIT(12)) 7348c2ecf20Sopenharmony_ci dimm_ecc_en_mask |= BIT(i); 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci if (umc_en_mask == dimm_ecc_en_mask) 7388c2ecf20Sopenharmony_ci edac_cap = EDAC_FLAG_SECDED; 7398c2ecf20Sopenharmony_ci } else { 7408c2ecf20Sopenharmony_ci bit = (pvt->fam > 0xf || pvt->ext_model >= K8_REV_F) 7418c2ecf20Sopenharmony_ci ? 19 7428c2ecf20Sopenharmony_ci : 17; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (pvt->dclr0 & BIT(bit)) 7458c2ecf20Sopenharmony_ci edac_cap = EDAC_FLAG_SECDED; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci return edac_cap; 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic void debug_display_dimm_sizes(struct amd64_pvt *, u8); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cistatic void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if (pvt->dram_type == MEM_LRDDR3) { 7588c2ecf20Sopenharmony_ci u32 dcsm = pvt->csels[chan].csmasks[0]; 7598c2ecf20Sopenharmony_ci /* 7608c2ecf20Sopenharmony_ci * It's assumed all LRDIMMs in a DCT are going to be of 7618c2ecf20Sopenharmony_ci * same 'type' until proven otherwise. So, use a cs 7628c2ecf20Sopenharmony_ci * value of '0' here to get dcsm value. 7638c2ecf20Sopenharmony_ci */ 7648c2ecf20Sopenharmony_ci edac_dbg(1, " LRDIMM %dx rank multiply\n", (dcsm & 0x3)); 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci edac_dbg(1, "All DIMMs support ECC:%s\n", 7688c2ecf20Sopenharmony_ci (dclr & BIT(19)) ? "yes" : "no"); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci edac_dbg(1, " PAR/ERR parity: %s\n", 7728c2ecf20Sopenharmony_ci (dclr & BIT(8)) ? "enabled" : "disabled"); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci if (pvt->fam == 0x10) 7758c2ecf20Sopenharmony_ci edac_dbg(1, " DCT 128bit mode width: %s\n", 7768c2ecf20Sopenharmony_ci (dclr & BIT(11)) ? "128b" : "64b"); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci edac_dbg(1, " x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n", 7798c2ecf20Sopenharmony_ci (dclr & BIT(12)) ? "yes" : "no", 7808c2ecf20Sopenharmony_ci (dclr & BIT(13)) ? "yes" : "no", 7818c2ecf20Sopenharmony_ci (dclr & BIT(14)) ? "yes" : "no", 7828c2ecf20Sopenharmony_ci (dclr & BIT(15)) ? "yes" : "no"); 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci#define CS_EVEN_PRIMARY BIT(0) 7868c2ecf20Sopenharmony_ci#define CS_ODD_PRIMARY BIT(1) 7878c2ecf20Sopenharmony_ci#define CS_EVEN_SECONDARY BIT(2) 7888c2ecf20Sopenharmony_ci#define CS_ODD_SECONDARY BIT(3) 7898c2ecf20Sopenharmony_ci#define CS_3R_INTERLEAVE BIT(4) 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci#define CS_EVEN (CS_EVEN_PRIMARY | CS_EVEN_SECONDARY) 7928c2ecf20Sopenharmony_ci#define CS_ODD (CS_ODD_PRIMARY | CS_ODD_SECONDARY) 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cistatic int f17_get_cs_mode(int dimm, u8 ctrl, struct amd64_pvt *pvt) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci u8 base, count = 0; 7978c2ecf20Sopenharmony_ci int cs_mode = 0; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (csrow_enabled(2 * dimm, ctrl, pvt)) 8008c2ecf20Sopenharmony_ci cs_mode |= CS_EVEN_PRIMARY; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if (csrow_enabled(2 * dimm + 1, ctrl, pvt)) 8038c2ecf20Sopenharmony_ci cs_mode |= CS_ODD_PRIMARY; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci /* Asymmetric dual-rank DIMM support. */ 8068c2ecf20Sopenharmony_ci if (csrow_sec_enabled(2 * dimm + 1, ctrl, pvt)) 8078c2ecf20Sopenharmony_ci cs_mode |= CS_ODD_SECONDARY; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci /* 8108c2ecf20Sopenharmony_ci * 3 Rank inteleaving support. 8118c2ecf20Sopenharmony_ci * There should be only three bases enabled and their two masks should 8128c2ecf20Sopenharmony_ci * be equal. 8138c2ecf20Sopenharmony_ci */ 8148c2ecf20Sopenharmony_ci for_each_chip_select(base, ctrl, pvt) 8158c2ecf20Sopenharmony_ci count += csrow_enabled(base, ctrl, pvt); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (count == 3 && 8188c2ecf20Sopenharmony_ci pvt->csels[ctrl].csmasks[0] == pvt->csels[ctrl].csmasks[1]) { 8198c2ecf20Sopenharmony_ci edac_dbg(1, "3R interleaving in use.\n"); 8208c2ecf20Sopenharmony_ci cs_mode |= CS_3R_INTERLEAVE; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci return cs_mode; 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic void debug_display_dimm_sizes_df(struct amd64_pvt *pvt, u8 ctrl) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci int dimm, size0, size1, cs0, cs1, cs_mode; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci edac_printk(KERN_DEBUG, EDAC_MC, "UMC%d chip selects:\n", ctrl); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci for (dimm = 0; dimm < 2; dimm++) { 8338c2ecf20Sopenharmony_ci cs0 = dimm * 2; 8348c2ecf20Sopenharmony_ci cs1 = dimm * 2 + 1; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci cs_mode = f17_get_cs_mode(dimm, ctrl, pvt); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci size0 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs0); 8398c2ecf20Sopenharmony_ci size1 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs1); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n", 8428c2ecf20Sopenharmony_ci cs0, size0, 8438c2ecf20Sopenharmony_ci cs1, size1); 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic void __dump_misc_regs_df(struct amd64_pvt *pvt) 8488c2ecf20Sopenharmony_ci{ 8498c2ecf20Sopenharmony_ci struct amd64_umc *umc; 8508c2ecf20Sopenharmony_ci u32 i, tmp, umc_base; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci for_each_umc(i) { 8538c2ecf20Sopenharmony_ci umc_base = get_umc_base(i); 8548c2ecf20Sopenharmony_ci umc = &pvt->umc[i]; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci edac_dbg(1, "UMC%d DIMM cfg: 0x%x\n", i, umc->dimm_cfg); 8578c2ecf20Sopenharmony_ci edac_dbg(1, "UMC%d UMC cfg: 0x%x\n", i, umc->umc_cfg); 8588c2ecf20Sopenharmony_ci edac_dbg(1, "UMC%d SDP ctrl: 0x%x\n", i, umc->sdp_ctrl); 8598c2ecf20Sopenharmony_ci edac_dbg(1, "UMC%d ECC ctrl: 0x%x\n", i, umc->ecc_ctrl); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ECC_BAD_SYMBOL, &tmp); 8628c2ecf20Sopenharmony_ci edac_dbg(1, "UMC%d ECC bad symbol: 0x%x\n", i, tmp); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_UMC_CAP, &tmp); 8658c2ecf20Sopenharmony_ci edac_dbg(1, "UMC%d UMC cap: 0x%x\n", i, tmp); 8668c2ecf20Sopenharmony_ci edac_dbg(1, "UMC%d UMC cap high: 0x%x\n", i, umc->umc_cap_hi); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci edac_dbg(1, "UMC%d ECC capable: %s, ChipKill ECC capable: %s\n", 8698c2ecf20Sopenharmony_ci i, (umc->umc_cap_hi & BIT(30)) ? "yes" : "no", 8708c2ecf20Sopenharmony_ci (umc->umc_cap_hi & BIT(31)) ? "yes" : "no"); 8718c2ecf20Sopenharmony_ci edac_dbg(1, "UMC%d All DIMMs support ECC: %s\n", 8728c2ecf20Sopenharmony_ci i, (umc->umc_cfg & BIT(12)) ? "yes" : "no"); 8738c2ecf20Sopenharmony_ci edac_dbg(1, "UMC%d x4 DIMMs present: %s\n", 8748c2ecf20Sopenharmony_ci i, (umc->dimm_cfg & BIT(6)) ? "yes" : "no"); 8758c2ecf20Sopenharmony_ci edac_dbg(1, "UMC%d x16 DIMMs present: %s\n", 8768c2ecf20Sopenharmony_ci i, (umc->dimm_cfg & BIT(7)) ? "yes" : "no"); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci if (pvt->dram_type == MEM_LRDDR4) { 8798c2ecf20Sopenharmony_ci amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ADDR_CFG, &tmp); 8808c2ecf20Sopenharmony_ci edac_dbg(1, "UMC%d LRDIMM %dx rank multiply\n", 8818c2ecf20Sopenharmony_ci i, 1 << ((tmp >> 4) & 0x3)); 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci debug_display_dimm_sizes_df(pvt, i); 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci edac_dbg(1, "F0x104 (DRAM Hole Address): 0x%08x, base: 0x%08x\n", 8888c2ecf20Sopenharmony_ci pvt->dhar, dhar_base(pvt)); 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci/* Display and decode various NB registers for debug purposes. */ 8928c2ecf20Sopenharmony_cistatic void __dump_misc_regs(struct amd64_pvt *pvt) 8938c2ecf20Sopenharmony_ci{ 8948c2ecf20Sopenharmony_ci edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci edac_dbg(1, " NB two channel DRAM capable: %s\n", 8978c2ecf20Sopenharmony_ci (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no"); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci edac_dbg(1, " ECC capable: %s, ChipKill ECC capable: %s\n", 9008c2ecf20Sopenharmony_ci (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no", 9018c2ecf20Sopenharmony_ci (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no"); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci debug_dump_dramcfg_low(pvt, pvt->dclr0, 0); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n", 9088c2ecf20Sopenharmony_ci pvt->dhar, dhar_base(pvt), 9098c2ecf20Sopenharmony_ci (pvt->fam == 0xf) ? k8_dhar_offset(pvt) 9108c2ecf20Sopenharmony_ci : f10_dhar_offset(pvt)); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci debug_display_dimm_sizes(pvt, 0); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci /* everything below this point is Fam10h and above */ 9158c2ecf20Sopenharmony_ci if (pvt->fam == 0xf) 9168c2ecf20Sopenharmony_ci return; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci debug_display_dimm_sizes(pvt, 1); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci /* Only if NOT ganged does dclr1 have valid info */ 9218c2ecf20Sopenharmony_ci if (!dct_ganging_enabled(pvt)) 9228c2ecf20Sopenharmony_ci debug_dump_dramcfg_low(pvt, pvt->dclr1, 1); 9238c2ecf20Sopenharmony_ci} 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci/* Display and decode various NB registers for debug purposes. */ 9268c2ecf20Sopenharmony_cistatic void dump_misc_regs(struct amd64_pvt *pvt) 9278c2ecf20Sopenharmony_ci{ 9288c2ecf20Sopenharmony_ci if (pvt->umc) 9298c2ecf20Sopenharmony_ci __dump_misc_regs_df(pvt); 9308c2ecf20Sopenharmony_ci else 9318c2ecf20Sopenharmony_ci __dump_misc_regs(pvt); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci edac_dbg(1, " DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no"); 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci amd64_info("using x%u syndromes.\n", pvt->ecc_sym_sz); 9368c2ecf20Sopenharmony_ci} 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci/* 9398c2ecf20Sopenharmony_ci * See BKDG, F2x[1,0][5C:40], F2[1,0][6C:60] 9408c2ecf20Sopenharmony_ci */ 9418c2ecf20Sopenharmony_cistatic void prep_chip_selects(struct amd64_pvt *pvt) 9428c2ecf20Sopenharmony_ci{ 9438c2ecf20Sopenharmony_ci if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) { 9448c2ecf20Sopenharmony_ci pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8; 9458c2ecf20Sopenharmony_ci pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8; 9468c2ecf20Sopenharmony_ci } else if (pvt->fam == 0x15 && pvt->model == 0x30) { 9478c2ecf20Sopenharmony_ci pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4; 9488c2ecf20Sopenharmony_ci pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2; 9498c2ecf20Sopenharmony_ci } else if (pvt->fam >= 0x17) { 9508c2ecf20Sopenharmony_ci int umc; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci for_each_umc(umc) { 9538c2ecf20Sopenharmony_ci pvt->csels[umc].b_cnt = 4; 9548c2ecf20Sopenharmony_ci pvt->csels[umc].m_cnt = 2; 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci } else { 9588c2ecf20Sopenharmony_ci pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8; 9598c2ecf20Sopenharmony_ci pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_cistatic void read_umc_base_mask(struct amd64_pvt *pvt) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci u32 umc_base_reg, umc_base_reg_sec; 9668c2ecf20Sopenharmony_ci u32 umc_mask_reg, umc_mask_reg_sec; 9678c2ecf20Sopenharmony_ci u32 base_reg, base_reg_sec; 9688c2ecf20Sopenharmony_ci u32 mask_reg, mask_reg_sec; 9698c2ecf20Sopenharmony_ci u32 *base, *base_sec; 9708c2ecf20Sopenharmony_ci u32 *mask, *mask_sec; 9718c2ecf20Sopenharmony_ci int cs, umc; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci for_each_umc(umc) { 9748c2ecf20Sopenharmony_ci umc_base_reg = get_umc_base(umc) + UMCCH_BASE_ADDR; 9758c2ecf20Sopenharmony_ci umc_base_reg_sec = get_umc_base(umc) + UMCCH_BASE_ADDR_SEC; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci for_each_chip_select(cs, umc, pvt) { 9788c2ecf20Sopenharmony_ci base = &pvt->csels[umc].csbases[cs]; 9798c2ecf20Sopenharmony_ci base_sec = &pvt->csels[umc].csbases_sec[cs]; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci base_reg = umc_base_reg + (cs * 4); 9828c2ecf20Sopenharmony_ci base_reg_sec = umc_base_reg_sec + (cs * 4); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci if (!amd_smn_read(pvt->mc_node_id, base_reg, base)) 9858c2ecf20Sopenharmony_ci edac_dbg(0, " DCSB%d[%d]=0x%08x reg: 0x%x\n", 9868c2ecf20Sopenharmony_ci umc, cs, *base, base_reg); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci if (!amd_smn_read(pvt->mc_node_id, base_reg_sec, base_sec)) 9898c2ecf20Sopenharmony_ci edac_dbg(0, " DCSB_SEC%d[%d]=0x%08x reg: 0x%x\n", 9908c2ecf20Sopenharmony_ci umc, cs, *base_sec, base_reg_sec); 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci umc_mask_reg = get_umc_base(umc) + UMCCH_ADDR_MASK; 9948c2ecf20Sopenharmony_ci umc_mask_reg_sec = get_umc_base(umc) + UMCCH_ADDR_MASK_SEC; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci for_each_chip_select_mask(cs, umc, pvt) { 9978c2ecf20Sopenharmony_ci mask = &pvt->csels[umc].csmasks[cs]; 9988c2ecf20Sopenharmony_ci mask_sec = &pvt->csels[umc].csmasks_sec[cs]; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci mask_reg = umc_mask_reg + (cs * 4); 10018c2ecf20Sopenharmony_ci mask_reg_sec = umc_mask_reg_sec + (cs * 4); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci if (!amd_smn_read(pvt->mc_node_id, mask_reg, mask)) 10048c2ecf20Sopenharmony_ci edac_dbg(0, " DCSM%d[%d]=0x%08x reg: 0x%x\n", 10058c2ecf20Sopenharmony_ci umc, cs, *mask, mask_reg); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci if (!amd_smn_read(pvt->mc_node_id, mask_reg_sec, mask_sec)) 10088c2ecf20Sopenharmony_ci edac_dbg(0, " DCSM_SEC%d[%d]=0x%08x reg: 0x%x\n", 10098c2ecf20Sopenharmony_ci umc, cs, *mask_sec, mask_reg_sec); 10108c2ecf20Sopenharmony_ci } 10118c2ecf20Sopenharmony_ci } 10128c2ecf20Sopenharmony_ci} 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci/* 10158c2ecf20Sopenharmony_ci * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers 10168c2ecf20Sopenharmony_ci */ 10178c2ecf20Sopenharmony_cistatic void read_dct_base_mask(struct amd64_pvt *pvt) 10188c2ecf20Sopenharmony_ci{ 10198c2ecf20Sopenharmony_ci int cs; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci prep_chip_selects(pvt); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci if (pvt->umc) 10248c2ecf20Sopenharmony_ci return read_umc_base_mask(pvt); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci for_each_chip_select(cs, 0, pvt) { 10278c2ecf20Sopenharmony_ci int reg0 = DCSB0 + (cs * 4); 10288c2ecf20Sopenharmony_ci int reg1 = DCSB1 + (cs * 4); 10298c2ecf20Sopenharmony_ci u32 *base0 = &pvt->csels[0].csbases[cs]; 10308c2ecf20Sopenharmony_ci u32 *base1 = &pvt->csels[1].csbases[cs]; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0)) 10338c2ecf20Sopenharmony_ci edac_dbg(0, " DCSB0[%d]=0x%08x reg: F2x%x\n", 10348c2ecf20Sopenharmony_ci cs, *base0, reg0); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci if (pvt->fam == 0xf) 10378c2ecf20Sopenharmony_ci continue; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1)) 10408c2ecf20Sopenharmony_ci edac_dbg(0, " DCSB1[%d]=0x%08x reg: F2x%x\n", 10418c2ecf20Sopenharmony_ci cs, *base1, (pvt->fam == 0x10) ? reg1 10428c2ecf20Sopenharmony_ci : reg0); 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci for_each_chip_select_mask(cs, 0, pvt) { 10468c2ecf20Sopenharmony_ci int reg0 = DCSM0 + (cs * 4); 10478c2ecf20Sopenharmony_ci int reg1 = DCSM1 + (cs * 4); 10488c2ecf20Sopenharmony_ci u32 *mask0 = &pvt->csels[0].csmasks[cs]; 10498c2ecf20Sopenharmony_ci u32 *mask1 = &pvt->csels[1].csmasks[cs]; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0)) 10528c2ecf20Sopenharmony_ci edac_dbg(0, " DCSM0[%d]=0x%08x reg: F2x%x\n", 10538c2ecf20Sopenharmony_ci cs, *mask0, reg0); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci if (pvt->fam == 0xf) 10568c2ecf20Sopenharmony_ci continue; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1)) 10598c2ecf20Sopenharmony_ci edac_dbg(0, " DCSM1[%d]=0x%08x reg: F2x%x\n", 10608c2ecf20Sopenharmony_ci cs, *mask1, (pvt->fam == 0x10) ? reg1 10618c2ecf20Sopenharmony_ci : reg0); 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_cistatic void determine_memory_type(struct amd64_pvt *pvt) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci u32 dram_ctrl, dcsm; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci if (pvt->umc) { 10708c2ecf20Sopenharmony_ci if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(5)) 10718c2ecf20Sopenharmony_ci pvt->dram_type = MEM_LRDDR4; 10728c2ecf20Sopenharmony_ci else if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(4)) 10738c2ecf20Sopenharmony_ci pvt->dram_type = MEM_RDDR4; 10748c2ecf20Sopenharmony_ci else 10758c2ecf20Sopenharmony_ci pvt->dram_type = MEM_DDR4; 10768c2ecf20Sopenharmony_ci return; 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci switch (pvt->fam) { 10808c2ecf20Sopenharmony_ci case 0xf: 10818c2ecf20Sopenharmony_ci if (pvt->ext_model >= K8_REV_F) 10828c2ecf20Sopenharmony_ci goto ddr3; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci pvt->dram_type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR; 10858c2ecf20Sopenharmony_ci return; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci case 0x10: 10888c2ecf20Sopenharmony_ci if (pvt->dchr0 & DDR3_MODE) 10898c2ecf20Sopenharmony_ci goto ddr3; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2; 10928c2ecf20Sopenharmony_ci return; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci case 0x15: 10958c2ecf20Sopenharmony_ci if (pvt->model < 0x60) 10968c2ecf20Sopenharmony_ci goto ddr3; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci /* 10998c2ecf20Sopenharmony_ci * Model 0x60h needs special handling: 11008c2ecf20Sopenharmony_ci * 11018c2ecf20Sopenharmony_ci * We use a Chip Select value of '0' to obtain dcsm. 11028c2ecf20Sopenharmony_ci * Theoretically, it is possible to populate LRDIMMs of different 11038c2ecf20Sopenharmony_ci * 'Rank' value on a DCT. But this is not the common case. So, 11048c2ecf20Sopenharmony_ci * it's reasonable to assume all DIMMs are going to be of same 11058c2ecf20Sopenharmony_ci * 'type' until proven otherwise. 11068c2ecf20Sopenharmony_ci */ 11078c2ecf20Sopenharmony_ci amd64_read_dct_pci_cfg(pvt, 0, DRAM_CONTROL, &dram_ctrl); 11088c2ecf20Sopenharmony_ci dcsm = pvt->csels[0].csmasks[0]; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci if (((dram_ctrl >> 8) & 0x7) == 0x2) 11118c2ecf20Sopenharmony_ci pvt->dram_type = MEM_DDR4; 11128c2ecf20Sopenharmony_ci else if (pvt->dclr0 & BIT(16)) 11138c2ecf20Sopenharmony_ci pvt->dram_type = MEM_DDR3; 11148c2ecf20Sopenharmony_ci else if (dcsm & 0x3) 11158c2ecf20Sopenharmony_ci pvt->dram_type = MEM_LRDDR3; 11168c2ecf20Sopenharmony_ci else 11178c2ecf20Sopenharmony_ci pvt->dram_type = MEM_RDDR3; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci return; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci case 0x16: 11228c2ecf20Sopenharmony_ci goto ddr3; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci default: 11258c2ecf20Sopenharmony_ci WARN(1, KERN_ERR "%s: Family??? 0x%x\n", __func__, pvt->fam); 11268c2ecf20Sopenharmony_ci pvt->dram_type = MEM_EMPTY; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci return; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ciddr3: 11318c2ecf20Sopenharmony_ci pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3; 11328c2ecf20Sopenharmony_ci} 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci/* Get the number of DCT channels the memory controller is using. */ 11358c2ecf20Sopenharmony_cistatic int k8_early_channel_count(struct amd64_pvt *pvt) 11368c2ecf20Sopenharmony_ci{ 11378c2ecf20Sopenharmony_ci int flag; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if (pvt->ext_model >= K8_REV_F) 11408c2ecf20Sopenharmony_ci /* RevF (NPT) and later */ 11418c2ecf20Sopenharmony_ci flag = pvt->dclr0 & WIDTH_128; 11428c2ecf20Sopenharmony_ci else 11438c2ecf20Sopenharmony_ci /* RevE and earlier */ 11448c2ecf20Sopenharmony_ci flag = pvt->dclr0 & REVE_WIDTH_128; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci /* not used */ 11478c2ecf20Sopenharmony_ci pvt->dclr1 = 0; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci return (flag) ? 2 : 1; 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci/* On F10h and later ErrAddr is MC4_ADDR[47:1] */ 11538c2ecf20Sopenharmony_cistatic u64 get_error_address(struct amd64_pvt *pvt, struct mce *m) 11548c2ecf20Sopenharmony_ci{ 11558c2ecf20Sopenharmony_ci u16 mce_nid = amd_get_nb_id(m->extcpu); 11568c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 11578c2ecf20Sopenharmony_ci u8 start_bit = 1; 11588c2ecf20Sopenharmony_ci u8 end_bit = 47; 11598c2ecf20Sopenharmony_ci u64 addr; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci mci = edac_mc_find(mce_nid); 11628c2ecf20Sopenharmony_ci if (!mci) 11638c2ecf20Sopenharmony_ci return 0; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci if (pvt->fam == 0xf) { 11688c2ecf20Sopenharmony_ci start_bit = 3; 11698c2ecf20Sopenharmony_ci end_bit = 39; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci addr = m->addr & GENMASK_ULL(end_bit, start_bit); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci /* 11758c2ecf20Sopenharmony_ci * Erratum 637 workaround 11768c2ecf20Sopenharmony_ci */ 11778c2ecf20Sopenharmony_ci if (pvt->fam == 0x15) { 11788c2ecf20Sopenharmony_ci u64 cc6_base, tmp_addr; 11798c2ecf20Sopenharmony_ci u32 tmp; 11808c2ecf20Sopenharmony_ci u8 intlv_en; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci if ((addr & GENMASK_ULL(47, 24)) >> 24 != 0x00fdf7) 11838c2ecf20Sopenharmony_ci return addr; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp); 11878c2ecf20Sopenharmony_ci intlv_en = tmp >> 21 & 0x7; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci /* add [47:27] + 3 trailing bits */ 11908c2ecf20Sopenharmony_ci cc6_base = (tmp & GENMASK_ULL(20, 0)) << 3; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci /* reverse and add DramIntlvEn */ 11938c2ecf20Sopenharmony_ci cc6_base |= intlv_en ^ 0x7; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci /* pin at [47:24] */ 11968c2ecf20Sopenharmony_ci cc6_base <<= 24; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci if (!intlv_en) 11998c2ecf20Sopenharmony_ci return cc6_base | (addr & GENMASK_ULL(23, 0)); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci /* faster log2 */ 12048c2ecf20Sopenharmony_ci tmp_addr = (addr & GENMASK_ULL(23, 12)) << __fls(intlv_en + 1); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci /* OR DramIntlvSel into bits [14:12] */ 12078c2ecf20Sopenharmony_ci tmp_addr |= (tmp & GENMASK_ULL(23, 21)) >> 9; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci /* add remaining [11:0] bits from original MC4_ADDR */ 12108c2ecf20Sopenharmony_ci tmp_addr |= addr & GENMASK_ULL(11, 0); 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci return cc6_base | tmp_addr; 12138c2ecf20Sopenharmony_ci } 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci return addr; 12168c2ecf20Sopenharmony_ci} 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_cistatic struct pci_dev *pci_get_related_function(unsigned int vendor, 12198c2ecf20Sopenharmony_ci unsigned int device, 12208c2ecf20Sopenharmony_ci struct pci_dev *related) 12218c2ecf20Sopenharmony_ci{ 12228c2ecf20Sopenharmony_ci struct pci_dev *dev = NULL; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci while ((dev = pci_get_device(vendor, device, dev))) { 12258c2ecf20Sopenharmony_ci if (pci_domain_nr(dev->bus) == pci_domain_nr(related->bus) && 12268c2ecf20Sopenharmony_ci (dev->bus->number == related->bus->number) && 12278c2ecf20Sopenharmony_ci (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn))) 12288c2ecf20Sopenharmony_ci break; 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci return dev; 12328c2ecf20Sopenharmony_ci} 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_cistatic void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range) 12358c2ecf20Sopenharmony_ci{ 12368c2ecf20Sopenharmony_ci struct amd_northbridge *nb; 12378c2ecf20Sopenharmony_ci struct pci_dev *f1 = NULL; 12388c2ecf20Sopenharmony_ci unsigned int pci_func; 12398c2ecf20Sopenharmony_ci int off = range << 3; 12408c2ecf20Sopenharmony_ci u32 llim; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off, &pvt->ranges[range].base.lo); 12438c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci if (pvt->fam == 0xf) 12468c2ecf20Sopenharmony_ci return; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci if (!dram_rw(pvt, range)) 12498c2ecf20Sopenharmony_ci return; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off, &pvt->ranges[range].base.hi); 12528c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci /* F15h: factor in CC6 save area by reading dst node's limit reg */ 12558c2ecf20Sopenharmony_ci if (pvt->fam != 0x15) 12568c2ecf20Sopenharmony_ci return; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci nb = node_to_amd_nb(dram_dst_node(pvt, range)); 12598c2ecf20Sopenharmony_ci if (WARN_ON(!nb)) 12608c2ecf20Sopenharmony_ci return; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci if (pvt->model == 0x60) 12638c2ecf20Sopenharmony_ci pci_func = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1; 12648c2ecf20Sopenharmony_ci else if (pvt->model == 0x30) 12658c2ecf20Sopenharmony_ci pci_func = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1; 12668c2ecf20Sopenharmony_ci else 12678c2ecf20Sopenharmony_ci pci_func = PCI_DEVICE_ID_AMD_15H_NB_F1; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc); 12708c2ecf20Sopenharmony_ci if (WARN_ON(!f1)) 12718c2ecf20Sopenharmony_ci return; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci pvt->ranges[range].lim.lo &= GENMASK_ULL(15, 0); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci /* {[39:27],111b} */ 12788c2ecf20Sopenharmony_ci pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci pvt->ranges[range].lim.hi &= GENMASK_ULL(7, 0); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci /* [47:40] */ 12838c2ecf20Sopenharmony_ci pvt->ranges[range].lim.hi |= llim >> 13; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci pci_dev_put(f1); 12868c2ecf20Sopenharmony_ci} 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_cistatic void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr, 12898c2ecf20Sopenharmony_ci struct err_info *err) 12908c2ecf20Sopenharmony_ci{ 12918c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci error_address_to_page_and_offset(sys_addr, err); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci /* 12968c2ecf20Sopenharmony_ci * Find out which node the error address belongs to. This may be 12978c2ecf20Sopenharmony_ci * different from the node that detected the error. 12988c2ecf20Sopenharmony_ci */ 12998c2ecf20Sopenharmony_ci err->src_mci = find_mc_by_sys_addr(mci, sys_addr); 13008c2ecf20Sopenharmony_ci if (!err->src_mci) { 13018c2ecf20Sopenharmony_ci amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n", 13028c2ecf20Sopenharmony_ci (unsigned long)sys_addr); 13038c2ecf20Sopenharmony_ci err->err_code = ERR_NODE; 13048c2ecf20Sopenharmony_ci return; 13058c2ecf20Sopenharmony_ci } 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci /* Now map the sys_addr to a CSROW */ 13088c2ecf20Sopenharmony_ci err->csrow = sys_addr_to_csrow(err->src_mci, sys_addr); 13098c2ecf20Sopenharmony_ci if (err->csrow < 0) { 13108c2ecf20Sopenharmony_ci err->err_code = ERR_CSROW; 13118c2ecf20Sopenharmony_ci return; 13128c2ecf20Sopenharmony_ci } 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci /* CHIPKILL enabled */ 13158c2ecf20Sopenharmony_ci if (pvt->nbcfg & NBCFG_CHIPKILL) { 13168c2ecf20Sopenharmony_ci err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome); 13178c2ecf20Sopenharmony_ci if (err->channel < 0) { 13188c2ecf20Sopenharmony_ci /* 13198c2ecf20Sopenharmony_ci * Syndrome didn't map, so we don't know which of the 13208c2ecf20Sopenharmony_ci * 2 DIMMs is in error. So we need to ID 'both' of them 13218c2ecf20Sopenharmony_ci * as suspect. 13228c2ecf20Sopenharmony_ci */ 13238c2ecf20Sopenharmony_ci amd64_mc_warn(err->src_mci, "unknown syndrome 0x%04x - " 13248c2ecf20Sopenharmony_ci "possible error reporting race\n", 13258c2ecf20Sopenharmony_ci err->syndrome); 13268c2ecf20Sopenharmony_ci err->err_code = ERR_CHANNEL; 13278c2ecf20Sopenharmony_ci return; 13288c2ecf20Sopenharmony_ci } 13298c2ecf20Sopenharmony_ci } else { 13308c2ecf20Sopenharmony_ci /* 13318c2ecf20Sopenharmony_ci * non-chipkill ecc mode 13328c2ecf20Sopenharmony_ci * 13338c2ecf20Sopenharmony_ci * The k8 documentation is unclear about how to determine the 13348c2ecf20Sopenharmony_ci * channel number when using non-chipkill memory. This method 13358c2ecf20Sopenharmony_ci * was obtained from email communication with someone at AMD. 13368c2ecf20Sopenharmony_ci * (Wish the email was placed in this comment - norsk) 13378c2ecf20Sopenharmony_ci */ 13388c2ecf20Sopenharmony_ci err->channel = ((sys_addr & BIT(3)) != 0); 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci} 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_cistatic int ddr2_cs_size(unsigned i, bool dct_width) 13438c2ecf20Sopenharmony_ci{ 13448c2ecf20Sopenharmony_ci unsigned shift = 0; 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci if (i <= 2) 13478c2ecf20Sopenharmony_ci shift = i; 13488c2ecf20Sopenharmony_ci else if (!(i & 0x1)) 13498c2ecf20Sopenharmony_ci shift = i >> 1; 13508c2ecf20Sopenharmony_ci else 13518c2ecf20Sopenharmony_ci shift = (i + 1) >> 1; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci return 128 << (shift + !!dct_width); 13548c2ecf20Sopenharmony_ci} 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_cistatic int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 13578c2ecf20Sopenharmony_ci unsigned cs_mode, int cs_mask_nr) 13588c2ecf20Sopenharmony_ci{ 13598c2ecf20Sopenharmony_ci u32 dclr = dct ? pvt->dclr1 : pvt->dclr0; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci if (pvt->ext_model >= K8_REV_F) { 13628c2ecf20Sopenharmony_ci WARN_ON(cs_mode > 11); 13638c2ecf20Sopenharmony_ci return ddr2_cs_size(cs_mode, dclr & WIDTH_128); 13648c2ecf20Sopenharmony_ci } 13658c2ecf20Sopenharmony_ci else if (pvt->ext_model >= K8_REV_D) { 13668c2ecf20Sopenharmony_ci unsigned diff; 13678c2ecf20Sopenharmony_ci WARN_ON(cs_mode > 10); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci /* 13708c2ecf20Sopenharmony_ci * the below calculation, besides trying to win an obfuscated C 13718c2ecf20Sopenharmony_ci * contest, maps cs_mode values to DIMM chip select sizes. The 13728c2ecf20Sopenharmony_ci * mappings are: 13738c2ecf20Sopenharmony_ci * 13748c2ecf20Sopenharmony_ci * cs_mode CS size (mb) 13758c2ecf20Sopenharmony_ci * ======= ============ 13768c2ecf20Sopenharmony_ci * 0 32 13778c2ecf20Sopenharmony_ci * 1 64 13788c2ecf20Sopenharmony_ci * 2 128 13798c2ecf20Sopenharmony_ci * 3 128 13808c2ecf20Sopenharmony_ci * 4 256 13818c2ecf20Sopenharmony_ci * 5 512 13828c2ecf20Sopenharmony_ci * 6 256 13838c2ecf20Sopenharmony_ci * 7 512 13848c2ecf20Sopenharmony_ci * 8 1024 13858c2ecf20Sopenharmony_ci * 9 1024 13868c2ecf20Sopenharmony_ci * 10 2048 13878c2ecf20Sopenharmony_ci * 13888c2ecf20Sopenharmony_ci * Basically, it calculates a value with which to shift the 13898c2ecf20Sopenharmony_ci * smallest CS size of 32MB. 13908c2ecf20Sopenharmony_ci * 13918c2ecf20Sopenharmony_ci * ddr[23]_cs_size have a similar purpose. 13928c2ecf20Sopenharmony_ci */ 13938c2ecf20Sopenharmony_ci diff = cs_mode/3 + (unsigned)(cs_mode > 5); 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci return 32 << (cs_mode - diff); 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci else { 13988c2ecf20Sopenharmony_ci WARN_ON(cs_mode > 6); 13998c2ecf20Sopenharmony_ci return 32 << cs_mode; 14008c2ecf20Sopenharmony_ci } 14018c2ecf20Sopenharmony_ci} 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci/* 14048c2ecf20Sopenharmony_ci * Get the number of DCT channels in use. 14058c2ecf20Sopenharmony_ci * 14068c2ecf20Sopenharmony_ci * Return: 14078c2ecf20Sopenharmony_ci * number of Memory Channels in operation 14088c2ecf20Sopenharmony_ci * Pass back: 14098c2ecf20Sopenharmony_ci * contents of the DCL0_LOW register 14108c2ecf20Sopenharmony_ci */ 14118c2ecf20Sopenharmony_cistatic int f1x_early_channel_count(struct amd64_pvt *pvt) 14128c2ecf20Sopenharmony_ci{ 14138c2ecf20Sopenharmony_ci int i, j, channels = 0; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci /* On F10h, if we are in 128 bit mode, then we are using 2 channels */ 14168c2ecf20Sopenharmony_ci if (pvt->fam == 0x10 && (pvt->dclr0 & WIDTH_128)) 14178c2ecf20Sopenharmony_ci return 2; 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci /* 14208c2ecf20Sopenharmony_ci * Need to check if in unganged mode: In such, there are 2 channels, 14218c2ecf20Sopenharmony_ci * but they are not in 128 bit mode and thus the above 'dclr0' status 14228c2ecf20Sopenharmony_ci * bit will be OFF. 14238c2ecf20Sopenharmony_ci * 14248c2ecf20Sopenharmony_ci * Need to check DCT0[0] and DCT1[0] to see if only one of them has 14258c2ecf20Sopenharmony_ci * their CSEnable bit on. If so, then SINGLE DIMM case. 14268c2ecf20Sopenharmony_ci */ 14278c2ecf20Sopenharmony_ci edac_dbg(0, "Data width is not 128 bits - need more decoding\n"); 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci /* 14308c2ecf20Sopenharmony_ci * Check DRAM Bank Address Mapping values for each DIMM to see if there 14318c2ecf20Sopenharmony_ci * is more than just one DIMM present in unganged mode. Need to check 14328c2ecf20Sopenharmony_ci * both controllers since DIMMs can be placed in either one. 14338c2ecf20Sopenharmony_ci */ 14348c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 14358c2ecf20Sopenharmony_ci u32 dbam = (i ? pvt->dbam1 : pvt->dbam0); 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci for (j = 0; j < 4; j++) { 14388c2ecf20Sopenharmony_ci if (DBAM_DIMM(j, dbam) > 0) { 14398c2ecf20Sopenharmony_ci channels++; 14408c2ecf20Sopenharmony_ci break; 14418c2ecf20Sopenharmony_ci } 14428c2ecf20Sopenharmony_ci } 14438c2ecf20Sopenharmony_ci } 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci if (channels > 2) 14468c2ecf20Sopenharmony_ci channels = 2; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci amd64_info("MCT channel count: %d\n", channels); 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci return channels; 14518c2ecf20Sopenharmony_ci} 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_cistatic int f17_early_channel_count(struct amd64_pvt *pvt) 14548c2ecf20Sopenharmony_ci{ 14558c2ecf20Sopenharmony_ci int i, channels = 0; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci /* SDP Control bit 31 (SdpInit) is clear for unused UMC channels */ 14588c2ecf20Sopenharmony_ci for_each_umc(i) 14598c2ecf20Sopenharmony_ci channels += !!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT); 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci amd64_info("MCT channel count: %d\n", channels); 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci return channels; 14648c2ecf20Sopenharmony_ci} 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_cistatic int ddr3_cs_size(unsigned i, bool dct_width) 14678c2ecf20Sopenharmony_ci{ 14688c2ecf20Sopenharmony_ci unsigned shift = 0; 14698c2ecf20Sopenharmony_ci int cs_size = 0; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci if (i == 0 || i == 3 || i == 4) 14728c2ecf20Sopenharmony_ci cs_size = -1; 14738c2ecf20Sopenharmony_ci else if (i <= 2) 14748c2ecf20Sopenharmony_ci shift = i; 14758c2ecf20Sopenharmony_ci else if (i == 12) 14768c2ecf20Sopenharmony_ci shift = 7; 14778c2ecf20Sopenharmony_ci else if (!(i & 0x1)) 14788c2ecf20Sopenharmony_ci shift = i >> 1; 14798c2ecf20Sopenharmony_ci else 14808c2ecf20Sopenharmony_ci shift = (i + 1) >> 1; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci if (cs_size != -1) 14838c2ecf20Sopenharmony_ci cs_size = (128 * (1 << !!dct_width)) << shift; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci return cs_size; 14868c2ecf20Sopenharmony_ci} 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_cistatic int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply) 14898c2ecf20Sopenharmony_ci{ 14908c2ecf20Sopenharmony_ci unsigned shift = 0; 14918c2ecf20Sopenharmony_ci int cs_size = 0; 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci if (i < 4 || i == 6) 14948c2ecf20Sopenharmony_ci cs_size = -1; 14958c2ecf20Sopenharmony_ci else if (i == 12) 14968c2ecf20Sopenharmony_ci shift = 7; 14978c2ecf20Sopenharmony_ci else if (!(i & 0x1)) 14988c2ecf20Sopenharmony_ci shift = i >> 1; 14998c2ecf20Sopenharmony_ci else 15008c2ecf20Sopenharmony_ci shift = (i + 1) >> 1; 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci if (cs_size != -1) 15038c2ecf20Sopenharmony_ci cs_size = rank_multiply * (128 << shift); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci return cs_size; 15068c2ecf20Sopenharmony_ci} 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_cistatic int ddr4_cs_size(unsigned i) 15098c2ecf20Sopenharmony_ci{ 15108c2ecf20Sopenharmony_ci int cs_size = 0; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci if (i == 0) 15138c2ecf20Sopenharmony_ci cs_size = -1; 15148c2ecf20Sopenharmony_ci else if (i == 1) 15158c2ecf20Sopenharmony_ci cs_size = 1024; 15168c2ecf20Sopenharmony_ci else 15178c2ecf20Sopenharmony_ci /* Min cs_size = 1G */ 15188c2ecf20Sopenharmony_ci cs_size = 1024 * (1 << (i >> 1)); 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci return cs_size; 15218c2ecf20Sopenharmony_ci} 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_cistatic int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 15248c2ecf20Sopenharmony_ci unsigned cs_mode, int cs_mask_nr) 15258c2ecf20Sopenharmony_ci{ 15268c2ecf20Sopenharmony_ci u32 dclr = dct ? pvt->dclr1 : pvt->dclr0; 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci WARN_ON(cs_mode > 11); 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE) 15318c2ecf20Sopenharmony_ci return ddr3_cs_size(cs_mode, dclr & WIDTH_128); 15328c2ecf20Sopenharmony_ci else 15338c2ecf20Sopenharmony_ci return ddr2_cs_size(cs_mode, dclr & WIDTH_128); 15348c2ecf20Sopenharmony_ci} 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci/* 15378c2ecf20Sopenharmony_ci * F15h supports only 64bit DCT interfaces 15388c2ecf20Sopenharmony_ci */ 15398c2ecf20Sopenharmony_cistatic int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 15408c2ecf20Sopenharmony_ci unsigned cs_mode, int cs_mask_nr) 15418c2ecf20Sopenharmony_ci{ 15428c2ecf20Sopenharmony_ci WARN_ON(cs_mode > 12); 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci return ddr3_cs_size(cs_mode, false); 15458c2ecf20Sopenharmony_ci} 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci/* F15h M60h supports DDR4 mapping as well.. */ 15488c2ecf20Sopenharmony_cistatic int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 15498c2ecf20Sopenharmony_ci unsigned cs_mode, int cs_mask_nr) 15508c2ecf20Sopenharmony_ci{ 15518c2ecf20Sopenharmony_ci int cs_size; 15528c2ecf20Sopenharmony_ci u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr]; 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci WARN_ON(cs_mode > 12); 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci if (pvt->dram_type == MEM_DDR4) { 15578c2ecf20Sopenharmony_ci if (cs_mode > 9) 15588c2ecf20Sopenharmony_ci return -1; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci cs_size = ddr4_cs_size(cs_mode); 15618c2ecf20Sopenharmony_ci } else if (pvt->dram_type == MEM_LRDDR3) { 15628c2ecf20Sopenharmony_ci unsigned rank_multiply = dcsm & 0xf; 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci if (rank_multiply == 3) 15658c2ecf20Sopenharmony_ci rank_multiply = 4; 15668c2ecf20Sopenharmony_ci cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply); 15678c2ecf20Sopenharmony_ci } else { 15688c2ecf20Sopenharmony_ci /* Minimum cs size is 512mb for F15hM60h*/ 15698c2ecf20Sopenharmony_ci if (cs_mode == 0x1) 15708c2ecf20Sopenharmony_ci return -1; 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci cs_size = ddr3_cs_size(cs_mode, false); 15738c2ecf20Sopenharmony_ci } 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci return cs_size; 15768c2ecf20Sopenharmony_ci} 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci/* 15798c2ecf20Sopenharmony_ci * F16h and F15h model 30h have only limited cs_modes. 15808c2ecf20Sopenharmony_ci */ 15818c2ecf20Sopenharmony_cistatic int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 15828c2ecf20Sopenharmony_ci unsigned cs_mode, int cs_mask_nr) 15838c2ecf20Sopenharmony_ci{ 15848c2ecf20Sopenharmony_ci WARN_ON(cs_mode > 12); 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci if (cs_mode == 6 || cs_mode == 8 || 15878c2ecf20Sopenharmony_ci cs_mode == 9 || cs_mode == 12) 15888c2ecf20Sopenharmony_ci return -1; 15898c2ecf20Sopenharmony_ci else 15908c2ecf20Sopenharmony_ci return ddr3_cs_size(cs_mode, false); 15918c2ecf20Sopenharmony_ci} 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_cistatic int f17_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc, 15948c2ecf20Sopenharmony_ci unsigned int cs_mode, int csrow_nr) 15958c2ecf20Sopenharmony_ci{ 15968c2ecf20Sopenharmony_ci u32 addr_mask_orig, addr_mask_deinterleaved; 15978c2ecf20Sopenharmony_ci u32 msb, weight, num_zero_bits; 15988c2ecf20Sopenharmony_ci int dimm, size = 0; 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci /* No Chip Selects are enabled. */ 16018c2ecf20Sopenharmony_ci if (!cs_mode) 16028c2ecf20Sopenharmony_ci return size; 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci /* Requested size of an even CS but none are enabled. */ 16058c2ecf20Sopenharmony_ci if (!(cs_mode & CS_EVEN) && !(csrow_nr & 1)) 16068c2ecf20Sopenharmony_ci return size; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci /* Requested size of an odd CS but none are enabled. */ 16098c2ecf20Sopenharmony_ci if (!(cs_mode & CS_ODD) && (csrow_nr & 1)) 16108c2ecf20Sopenharmony_ci return size; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci /* 16138c2ecf20Sopenharmony_ci * There is one mask per DIMM, and two Chip Selects per DIMM. 16148c2ecf20Sopenharmony_ci * CS0 and CS1 -> DIMM0 16158c2ecf20Sopenharmony_ci * CS2 and CS3 -> DIMM1 16168c2ecf20Sopenharmony_ci */ 16178c2ecf20Sopenharmony_ci dimm = csrow_nr >> 1; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci /* Asymmetric dual-rank DIMM support. */ 16208c2ecf20Sopenharmony_ci if ((csrow_nr & 1) && (cs_mode & CS_ODD_SECONDARY)) 16218c2ecf20Sopenharmony_ci addr_mask_orig = pvt->csels[umc].csmasks_sec[dimm]; 16228c2ecf20Sopenharmony_ci else 16238c2ecf20Sopenharmony_ci addr_mask_orig = pvt->csels[umc].csmasks[dimm]; 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci /* 16268c2ecf20Sopenharmony_ci * The number of zero bits in the mask is equal to the number of bits 16278c2ecf20Sopenharmony_ci * in a full mask minus the number of bits in the current mask. 16288c2ecf20Sopenharmony_ci * 16298c2ecf20Sopenharmony_ci * The MSB is the number of bits in the full mask because BIT[0] is 16308c2ecf20Sopenharmony_ci * always 0. 16318c2ecf20Sopenharmony_ci * 16328c2ecf20Sopenharmony_ci * In the special 3 Rank interleaving case, a single bit is flipped 16338c2ecf20Sopenharmony_ci * without swapping with the most significant bit. This can be handled 16348c2ecf20Sopenharmony_ci * by keeping the MSB where it is and ignoring the single zero bit. 16358c2ecf20Sopenharmony_ci */ 16368c2ecf20Sopenharmony_ci msb = fls(addr_mask_orig) - 1; 16378c2ecf20Sopenharmony_ci weight = hweight_long(addr_mask_orig); 16388c2ecf20Sopenharmony_ci num_zero_bits = msb - weight - !!(cs_mode & CS_3R_INTERLEAVE); 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci /* Take the number of zero bits off from the top of the mask. */ 16418c2ecf20Sopenharmony_ci addr_mask_deinterleaved = GENMASK_ULL(msb - num_zero_bits, 1); 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci edac_dbg(1, "CS%d DIMM%d AddrMasks:\n", csrow_nr, dimm); 16448c2ecf20Sopenharmony_ci edac_dbg(1, " Original AddrMask: 0x%x\n", addr_mask_orig); 16458c2ecf20Sopenharmony_ci edac_dbg(1, " Deinterleaved AddrMask: 0x%x\n", addr_mask_deinterleaved); 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci /* Register [31:1] = Address [39:9]. Size is in kBs here. */ 16488c2ecf20Sopenharmony_ci size = (addr_mask_deinterleaved >> 2) + 1; 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci /* Return size in MBs. */ 16518c2ecf20Sopenharmony_ci return size >> 10; 16528c2ecf20Sopenharmony_ci} 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_cistatic void read_dram_ctl_register(struct amd64_pvt *pvt) 16558c2ecf20Sopenharmony_ci{ 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci if (pvt->fam == 0xf) 16588c2ecf20Sopenharmony_ci return; 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci if (!amd64_read_pci_cfg(pvt->F2, DCT_SEL_LO, &pvt->dct_sel_lo)) { 16618c2ecf20Sopenharmony_ci edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n", 16628c2ecf20Sopenharmony_ci pvt->dct_sel_lo, dct_sel_baseaddr(pvt)); 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci edac_dbg(0, " DCTs operate in %s mode\n", 16658c2ecf20Sopenharmony_ci (dct_ganging_enabled(pvt) ? "ganged" : "unganged")); 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci if (!dct_ganging_enabled(pvt)) 16688c2ecf20Sopenharmony_ci edac_dbg(0, " Address range split per DCT: %s\n", 16698c2ecf20Sopenharmony_ci (dct_high_range_enabled(pvt) ? "yes" : "no")); 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci edac_dbg(0, " data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n", 16728c2ecf20Sopenharmony_ci (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"), 16738c2ecf20Sopenharmony_ci (dct_memory_cleared(pvt) ? "yes" : "no")); 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci edac_dbg(0, " channel interleave: %s, " 16768c2ecf20Sopenharmony_ci "interleave bits selector: 0x%x\n", 16778c2ecf20Sopenharmony_ci (dct_interleave_enabled(pvt) ? "enabled" : "disabled"), 16788c2ecf20Sopenharmony_ci dct_sel_interleave_addr(pvt)); 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F2, DCT_SEL_HI, &pvt->dct_sel_hi); 16828c2ecf20Sopenharmony_ci} 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci/* 16858c2ecf20Sopenharmony_ci * Determine channel (DCT) based on the interleaving mode (see F15h M30h BKDG, 16868c2ecf20Sopenharmony_ci * 2.10.12 Memory Interleaving Modes). 16878c2ecf20Sopenharmony_ci */ 16888c2ecf20Sopenharmony_cistatic u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, 16898c2ecf20Sopenharmony_ci u8 intlv_en, int num_dcts_intlv, 16908c2ecf20Sopenharmony_ci u32 dct_sel) 16918c2ecf20Sopenharmony_ci{ 16928c2ecf20Sopenharmony_ci u8 channel = 0; 16938c2ecf20Sopenharmony_ci u8 select; 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci if (!(intlv_en)) 16968c2ecf20Sopenharmony_ci return (u8)(dct_sel); 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci if (num_dcts_intlv == 2) { 16998c2ecf20Sopenharmony_ci select = (sys_addr >> 8) & 0x3; 17008c2ecf20Sopenharmony_ci channel = select ? 0x3 : 0; 17018c2ecf20Sopenharmony_ci } else if (num_dcts_intlv == 4) { 17028c2ecf20Sopenharmony_ci u8 intlv_addr = dct_sel_interleave_addr(pvt); 17038c2ecf20Sopenharmony_ci switch (intlv_addr) { 17048c2ecf20Sopenharmony_ci case 0x4: 17058c2ecf20Sopenharmony_ci channel = (sys_addr >> 8) & 0x3; 17068c2ecf20Sopenharmony_ci break; 17078c2ecf20Sopenharmony_ci case 0x5: 17088c2ecf20Sopenharmony_ci channel = (sys_addr >> 9) & 0x3; 17098c2ecf20Sopenharmony_ci break; 17108c2ecf20Sopenharmony_ci } 17118c2ecf20Sopenharmony_ci } 17128c2ecf20Sopenharmony_ci return channel; 17138c2ecf20Sopenharmony_ci} 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci/* 17168c2ecf20Sopenharmony_ci * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory 17178c2ecf20Sopenharmony_ci * Interleaving Modes. 17188c2ecf20Sopenharmony_ci */ 17198c2ecf20Sopenharmony_cistatic u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, 17208c2ecf20Sopenharmony_ci bool hi_range_sel, u8 intlv_en) 17218c2ecf20Sopenharmony_ci{ 17228c2ecf20Sopenharmony_ci u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1; 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci if (dct_ganging_enabled(pvt)) 17258c2ecf20Sopenharmony_ci return 0; 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci if (hi_range_sel) 17288c2ecf20Sopenharmony_ci return dct_sel_high; 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci /* 17318c2ecf20Sopenharmony_ci * see F2x110[DctSelIntLvAddr] - channel interleave mode 17328c2ecf20Sopenharmony_ci */ 17338c2ecf20Sopenharmony_ci if (dct_interleave_enabled(pvt)) { 17348c2ecf20Sopenharmony_ci u8 intlv_addr = dct_sel_interleave_addr(pvt); 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci /* return DCT select function: 0=DCT0, 1=DCT1 */ 17378c2ecf20Sopenharmony_ci if (!intlv_addr) 17388c2ecf20Sopenharmony_ci return sys_addr >> 6 & 1; 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci if (intlv_addr & 0x2) { 17418c2ecf20Sopenharmony_ci u8 shift = intlv_addr & 0x1 ? 9 : 6; 17428c2ecf20Sopenharmony_ci u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) & 1; 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci return ((sys_addr >> shift) & 1) ^ temp; 17458c2ecf20Sopenharmony_ci } 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci if (intlv_addr & 0x4) { 17488c2ecf20Sopenharmony_ci u8 shift = intlv_addr & 0x1 ? 9 : 8; 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci return (sys_addr >> shift) & 1; 17518c2ecf20Sopenharmony_ci } 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci return (sys_addr >> (12 + hweight8(intlv_en))) & 1; 17548c2ecf20Sopenharmony_ci } 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci if (dct_high_range_enabled(pvt)) 17578c2ecf20Sopenharmony_ci return ~dct_sel_high & 1; 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci return 0; 17608c2ecf20Sopenharmony_ci} 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci/* Convert the sys_addr to the normalized DCT address */ 17638c2ecf20Sopenharmony_cistatic u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range, 17648c2ecf20Sopenharmony_ci u64 sys_addr, bool hi_rng, 17658c2ecf20Sopenharmony_ci u32 dct_sel_base_addr) 17668c2ecf20Sopenharmony_ci{ 17678c2ecf20Sopenharmony_ci u64 chan_off; 17688c2ecf20Sopenharmony_ci u64 dram_base = get_dram_base(pvt, range); 17698c2ecf20Sopenharmony_ci u64 hole_off = f10_dhar_offset(pvt); 17708c2ecf20Sopenharmony_ci u64 dct_sel_base_off = (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16; 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci if (hi_rng) { 17738c2ecf20Sopenharmony_ci /* 17748c2ecf20Sopenharmony_ci * if 17758c2ecf20Sopenharmony_ci * base address of high range is below 4Gb 17768c2ecf20Sopenharmony_ci * (bits [47:27] at [31:11]) 17778c2ecf20Sopenharmony_ci * DRAM address space on this DCT is hoisted above 4Gb && 17788c2ecf20Sopenharmony_ci * sys_addr > 4Gb 17798c2ecf20Sopenharmony_ci * 17808c2ecf20Sopenharmony_ci * remove hole offset from sys_addr 17818c2ecf20Sopenharmony_ci * else 17828c2ecf20Sopenharmony_ci * remove high range offset from sys_addr 17838c2ecf20Sopenharmony_ci */ 17848c2ecf20Sopenharmony_ci if ((!(dct_sel_base_addr >> 16) || 17858c2ecf20Sopenharmony_ci dct_sel_base_addr < dhar_base(pvt)) && 17868c2ecf20Sopenharmony_ci dhar_valid(pvt) && 17878c2ecf20Sopenharmony_ci (sys_addr >= BIT_64(32))) 17888c2ecf20Sopenharmony_ci chan_off = hole_off; 17898c2ecf20Sopenharmony_ci else 17908c2ecf20Sopenharmony_ci chan_off = dct_sel_base_off; 17918c2ecf20Sopenharmony_ci } else { 17928c2ecf20Sopenharmony_ci /* 17938c2ecf20Sopenharmony_ci * if 17948c2ecf20Sopenharmony_ci * we have a valid hole && 17958c2ecf20Sopenharmony_ci * sys_addr > 4Gb 17968c2ecf20Sopenharmony_ci * 17978c2ecf20Sopenharmony_ci * remove hole 17988c2ecf20Sopenharmony_ci * else 17998c2ecf20Sopenharmony_ci * remove dram base to normalize to DCT address 18008c2ecf20Sopenharmony_ci */ 18018c2ecf20Sopenharmony_ci if (dhar_valid(pvt) && (sys_addr >= BIT_64(32))) 18028c2ecf20Sopenharmony_ci chan_off = hole_off; 18038c2ecf20Sopenharmony_ci else 18048c2ecf20Sopenharmony_ci chan_off = dram_base; 18058c2ecf20Sopenharmony_ci } 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci return (sys_addr & GENMASK_ULL(47,6)) - (chan_off & GENMASK_ULL(47,23)); 18088c2ecf20Sopenharmony_ci} 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci/* 18118c2ecf20Sopenharmony_ci * checks if the csrow passed in is marked as SPARED, if so returns the new 18128c2ecf20Sopenharmony_ci * spare row 18138c2ecf20Sopenharmony_ci */ 18148c2ecf20Sopenharmony_cistatic int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow) 18158c2ecf20Sopenharmony_ci{ 18168c2ecf20Sopenharmony_ci int tmp_cs; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci if (online_spare_swap_done(pvt, dct) && 18198c2ecf20Sopenharmony_ci csrow == online_spare_bad_dramcs(pvt, dct)) { 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci for_each_chip_select(tmp_cs, dct, pvt) { 18228c2ecf20Sopenharmony_ci if (chip_select_base(tmp_cs, dct, pvt) & 0x2) { 18238c2ecf20Sopenharmony_ci csrow = tmp_cs; 18248c2ecf20Sopenharmony_ci break; 18258c2ecf20Sopenharmony_ci } 18268c2ecf20Sopenharmony_ci } 18278c2ecf20Sopenharmony_ci } 18288c2ecf20Sopenharmony_ci return csrow; 18298c2ecf20Sopenharmony_ci} 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci/* 18328c2ecf20Sopenharmony_ci * Iterate over the DRAM DCT "base" and "mask" registers looking for a 18338c2ecf20Sopenharmony_ci * SystemAddr match on the specified 'ChannelSelect' and 'NodeID' 18348c2ecf20Sopenharmony_ci * 18358c2ecf20Sopenharmony_ci * Return: 18368c2ecf20Sopenharmony_ci * -EINVAL: NOT FOUND 18378c2ecf20Sopenharmony_ci * 0..csrow = Chip-Select Row 18388c2ecf20Sopenharmony_ci */ 18398c2ecf20Sopenharmony_cistatic int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct) 18408c2ecf20Sopenharmony_ci{ 18418c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 18428c2ecf20Sopenharmony_ci struct amd64_pvt *pvt; 18438c2ecf20Sopenharmony_ci u64 cs_base, cs_mask; 18448c2ecf20Sopenharmony_ci int cs_found = -EINVAL; 18458c2ecf20Sopenharmony_ci int csrow; 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci mci = edac_mc_find(nid); 18488c2ecf20Sopenharmony_ci if (!mci) 18498c2ecf20Sopenharmony_ci return cs_found; 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct); 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci for_each_chip_select(csrow, dct, pvt) { 18568c2ecf20Sopenharmony_ci if (!csrow_enabled(csrow, dct, pvt)) 18578c2ecf20Sopenharmony_ci continue; 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask); 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci edac_dbg(1, " CSROW=%d CSBase=0x%llx CSMask=0x%llx\n", 18628c2ecf20Sopenharmony_ci csrow, cs_base, cs_mask); 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci cs_mask = ~cs_mask; 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci edac_dbg(1, " (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n", 18678c2ecf20Sopenharmony_ci (in_addr & cs_mask), (cs_base & cs_mask)); 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci if ((in_addr & cs_mask) == (cs_base & cs_mask)) { 18708c2ecf20Sopenharmony_ci if (pvt->fam == 0x15 && pvt->model >= 0x30) { 18718c2ecf20Sopenharmony_ci cs_found = csrow; 18728c2ecf20Sopenharmony_ci break; 18738c2ecf20Sopenharmony_ci } 18748c2ecf20Sopenharmony_ci cs_found = f10_process_possible_spare(pvt, dct, csrow); 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci edac_dbg(1, " MATCH csrow=%d\n", cs_found); 18778c2ecf20Sopenharmony_ci break; 18788c2ecf20Sopenharmony_ci } 18798c2ecf20Sopenharmony_ci } 18808c2ecf20Sopenharmony_ci return cs_found; 18818c2ecf20Sopenharmony_ci} 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci/* 18848c2ecf20Sopenharmony_ci * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is 18858c2ecf20Sopenharmony_ci * swapped with a region located at the bottom of memory so that the GPU can use 18868c2ecf20Sopenharmony_ci * the interleaved region and thus two channels. 18878c2ecf20Sopenharmony_ci */ 18888c2ecf20Sopenharmony_cistatic u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr) 18898c2ecf20Sopenharmony_ci{ 18908c2ecf20Sopenharmony_ci u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr; 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci if (pvt->fam == 0x10) { 18938c2ecf20Sopenharmony_ci /* only revC3 and revE have that feature */ 18948c2ecf20Sopenharmony_ci if (pvt->model < 4 || (pvt->model < 0xa && pvt->stepping < 3)) 18958c2ecf20Sopenharmony_ci return sys_addr; 18968c2ecf20Sopenharmony_ci } 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F2, SWAP_INTLV_REG, &swap_reg); 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci if (!(swap_reg & 0x1)) 19018c2ecf20Sopenharmony_ci return sys_addr; 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci swap_base = (swap_reg >> 3) & 0x7f; 19048c2ecf20Sopenharmony_ci swap_limit = (swap_reg >> 11) & 0x7f; 19058c2ecf20Sopenharmony_ci rgn_size = (swap_reg >> 20) & 0x7f; 19068c2ecf20Sopenharmony_ci tmp_addr = sys_addr >> 27; 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci if (!(sys_addr >> 34) && 19098c2ecf20Sopenharmony_ci (((tmp_addr >= swap_base) && 19108c2ecf20Sopenharmony_ci (tmp_addr <= swap_limit)) || 19118c2ecf20Sopenharmony_ci (tmp_addr < rgn_size))) 19128c2ecf20Sopenharmony_ci return sys_addr ^ (u64)swap_base << 27; 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci return sys_addr; 19158c2ecf20Sopenharmony_ci} 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci/* For a given @dram_range, check if @sys_addr falls within it. */ 19188c2ecf20Sopenharmony_cistatic int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range, 19198c2ecf20Sopenharmony_ci u64 sys_addr, int *chan_sel) 19208c2ecf20Sopenharmony_ci{ 19218c2ecf20Sopenharmony_ci int cs_found = -EINVAL; 19228c2ecf20Sopenharmony_ci u64 chan_addr; 19238c2ecf20Sopenharmony_ci u32 dct_sel_base; 19248c2ecf20Sopenharmony_ci u8 channel; 19258c2ecf20Sopenharmony_ci bool high_range = false; 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci u8 node_id = dram_dst_node(pvt, range); 19288c2ecf20Sopenharmony_ci u8 intlv_en = dram_intlv_en(pvt, range); 19298c2ecf20Sopenharmony_ci u32 intlv_sel = dram_intlv_sel(pvt, range); 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n", 19328c2ecf20Sopenharmony_ci range, sys_addr, get_dram_limit(pvt, range)); 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci if (dhar_valid(pvt) && 19358c2ecf20Sopenharmony_ci dhar_base(pvt) <= sys_addr && 19368c2ecf20Sopenharmony_ci sys_addr < BIT_64(32)) { 19378c2ecf20Sopenharmony_ci amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n", 19388c2ecf20Sopenharmony_ci sys_addr); 19398c2ecf20Sopenharmony_ci return -EINVAL; 19408c2ecf20Sopenharmony_ci } 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en))) 19438c2ecf20Sopenharmony_ci return -EINVAL; 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci sys_addr = f1x_swap_interleaved_region(pvt, sys_addr); 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci dct_sel_base = dct_sel_baseaddr(pvt); 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci /* 19508c2ecf20Sopenharmony_ci * check whether addresses >= DctSelBaseAddr[47:27] are to be used to 19518c2ecf20Sopenharmony_ci * select between DCT0 and DCT1. 19528c2ecf20Sopenharmony_ci */ 19538c2ecf20Sopenharmony_ci if (dct_high_range_enabled(pvt) && 19548c2ecf20Sopenharmony_ci !dct_ganging_enabled(pvt) && 19558c2ecf20Sopenharmony_ci ((sys_addr >> 27) >= (dct_sel_base >> 11))) 19568c2ecf20Sopenharmony_ci high_range = true; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en); 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr, 19618c2ecf20Sopenharmony_ci high_range, dct_sel_base); 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci /* Remove node interleaving, see F1x120 */ 19648c2ecf20Sopenharmony_ci if (intlv_en) 19658c2ecf20Sopenharmony_ci chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) | 19668c2ecf20Sopenharmony_ci (chan_addr & 0xfff); 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci /* remove channel interleave */ 19698c2ecf20Sopenharmony_ci if (dct_interleave_enabled(pvt) && 19708c2ecf20Sopenharmony_ci !dct_high_range_enabled(pvt) && 19718c2ecf20Sopenharmony_ci !dct_ganging_enabled(pvt)) { 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_ci if (dct_sel_interleave_addr(pvt) != 1) { 19748c2ecf20Sopenharmony_ci if (dct_sel_interleave_addr(pvt) == 0x3) 19758c2ecf20Sopenharmony_ci /* hash 9 */ 19768c2ecf20Sopenharmony_ci chan_addr = ((chan_addr >> 10) << 9) | 19778c2ecf20Sopenharmony_ci (chan_addr & 0x1ff); 19788c2ecf20Sopenharmony_ci else 19798c2ecf20Sopenharmony_ci /* A[6] or hash 6 */ 19808c2ecf20Sopenharmony_ci chan_addr = ((chan_addr >> 7) << 6) | 19818c2ecf20Sopenharmony_ci (chan_addr & 0x3f); 19828c2ecf20Sopenharmony_ci } else 19838c2ecf20Sopenharmony_ci /* A[12] */ 19848c2ecf20Sopenharmony_ci chan_addr = ((chan_addr >> 13) << 12) | 19858c2ecf20Sopenharmony_ci (chan_addr & 0xfff); 19868c2ecf20Sopenharmony_ci } 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr); 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel); 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci if (cs_found >= 0) 19938c2ecf20Sopenharmony_ci *chan_sel = channel; 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci return cs_found; 19968c2ecf20Sopenharmony_ci} 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_cistatic int f15_m30h_match_to_this_node(struct amd64_pvt *pvt, unsigned range, 19998c2ecf20Sopenharmony_ci u64 sys_addr, int *chan_sel) 20008c2ecf20Sopenharmony_ci{ 20018c2ecf20Sopenharmony_ci int cs_found = -EINVAL; 20028c2ecf20Sopenharmony_ci int num_dcts_intlv = 0; 20038c2ecf20Sopenharmony_ci u64 chan_addr, chan_offset; 20048c2ecf20Sopenharmony_ci u64 dct_base, dct_limit; 20058c2ecf20Sopenharmony_ci u32 dct_cont_base_reg, dct_cont_limit_reg, tmp; 20068c2ecf20Sopenharmony_ci u8 channel, alias_channel, leg_mmio_hole, dct_sel, dct_offset_en; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci u64 dhar_offset = f10_dhar_offset(pvt); 20098c2ecf20Sopenharmony_ci u8 intlv_addr = dct_sel_interleave_addr(pvt); 20108c2ecf20Sopenharmony_ci u8 node_id = dram_dst_node(pvt, range); 20118c2ecf20Sopenharmony_ci u8 intlv_en = dram_intlv_en(pvt, range); 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F1, DRAM_CONT_BASE, &dct_cont_base_reg); 20148c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F1, DRAM_CONT_LIMIT, &dct_cont_limit_reg); 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci dct_offset_en = (u8) ((dct_cont_base_reg >> 3) & BIT(0)); 20178c2ecf20Sopenharmony_ci dct_sel = (u8) ((dct_cont_base_reg >> 4) & 0x7); 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n", 20208c2ecf20Sopenharmony_ci range, sys_addr, get_dram_limit(pvt, range)); 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci if (!(get_dram_base(pvt, range) <= sys_addr) && 20238c2ecf20Sopenharmony_ci !(get_dram_limit(pvt, range) >= sys_addr)) 20248c2ecf20Sopenharmony_ci return -EINVAL; 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci if (dhar_valid(pvt) && 20278c2ecf20Sopenharmony_ci dhar_base(pvt) <= sys_addr && 20288c2ecf20Sopenharmony_ci sys_addr < BIT_64(32)) { 20298c2ecf20Sopenharmony_ci amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n", 20308c2ecf20Sopenharmony_ci sys_addr); 20318c2ecf20Sopenharmony_ci return -EINVAL; 20328c2ecf20Sopenharmony_ci } 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci /* Verify sys_addr is within DCT Range. */ 20358c2ecf20Sopenharmony_ci dct_base = (u64) dct_sel_baseaddr(pvt); 20368c2ecf20Sopenharmony_ci dct_limit = (dct_cont_limit_reg >> 11) & 0x1FFF; 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci if (!(dct_cont_base_reg & BIT(0)) && 20398c2ecf20Sopenharmony_ci !(dct_base <= (sys_addr >> 27) && 20408c2ecf20Sopenharmony_ci dct_limit >= (sys_addr >> 27))) 20418c2ecf20Sopenharmony_ci return -EINVAL; 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci /* Verify number of dct's that participate in channel interleaving. */ 20448c2ecf20Sopenharmony_ci num_dcts_intlv = (int) hweight8(intlv_en); 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci if (!(num_dcts_intlv % 2 == 0) || (num_dcts_intlv > 4)) 20478c2ecf20Sopenharmony_ci return -EINVAL; 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci if (pvt->model >= 0x60) 20508c2ecf20Sopenharmony_ci channel = f1x_determine_channel(pvt, sys_addr, false, intlv_en); 20518c2ecf20Sopenharmony_ci else 20528c2ecf20Sopenharmony_ci channel = f15_m30h_determine_channel(pvt, sys_addr, intlv_en, 20538c2ecf20Sopenharmony_ci num_dcts_intlv, dct_sel); 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci /* Verify we stay within the MAX number of channels allowed */ 20568c2ecf20Sopenharmony_ci if (channel > 3) 20578c2ecf20Sopenharmony_ci return -EINVAL; 20588c2ecf20Sopenharmony_ci 20598c2ecf20Sopenharmony_ci leg_mmio_hole = (u8) (dct_cont_base_reg >> 1 & BIT(0)); 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci /* Get normalized DCT addr */ 20628c2ecf20Sopenharmony_ci if (leg_mmio_hole && (sys_addr >= BIT_64(32))) 20638c2ecf20Sopenharmony_ci chan_offset = dhar_offset; 20648c2ecf20Sopenharmony_ci else 20658c2ecf20Sopenharmony_ci chan_offset = dct_base << 27; 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci chan_addr = sys_addr - chan_offset; 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci /* remove channel interleave */ 20708c2ecf20Sopenharmony_ci if (num_dcts_intlv == 2) { 20718c2ecf20Sopenharmony_ci if (intlv_addr == 0x4) 20728c2ecf20Sopenharmony_ci chan_addr = ((chan_addr >> 9) << 8) | 20738c2ecf20Sopenharmony_ci (chan_addr & 0xff); 20748c2ecf20Sopenharmony_ci else if (intlv_addr == 0x5) 20758c2ecf20Sopenharmony_ci chan_addr = ((chan_addr >> 10) << 9) | 20768c2ecf20Sopenharmony_ci (chan_addr & 0x1ff); 20778c2ecf20Sopenharmony_ci else 20788c2ecf20Sopenharmony_ci return -EINVAL; 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci } else if (num_dcts_intlv == 4) { 20818c2ecf20Sopenharmony_ci if (intlv_addr == 0x4) 20828c2ecf20Sopenharmony_ci chan_addr = ((chan_addr >> 10) << 8) | 20838c2ecf20Sopenharmony_ci (chan_addr & 0xff); 20848c2ecf20Sopenharmony_ci else if (intlv_addr == 0x5) 20858c2ecf20Sopenharmony_ci chan_addr = ((chan_addr >> 11) << 9) | 20868c2ecf20Sopenharmony_ci (chan_addr & 0x1ff); 20878c2ecf20Sopenharmony_ci else 20888c2ecf20Sopenharmony_ci return -EINVAL; 20898c2ecf20Sopenharmony_ci } 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_ci if (dct_offset_en) { 20928c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F1, 20938c2ecf20Sopenharmony_ci DRAM_CONT_HIGH_OFF + (int) channel * 4, 20948c2ecf20Sopenharmony_ci &tmp); 20958c2ecf20Sopenharmony_ci chan_addr += (u64) ((tmp >> 11) & 0xfff) << 27; 20968c2ecf20Sopenharmony_ci } 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci f15h_select_dct(pvt, channel); 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_ci edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr); 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci /* 21038c2ecf20Sopenharmony_ci * Find Chip select: 21048c2ecf20Sopenharmony_ci * if channel = 3, then alias it to 1. This is because, in F15 M30h, 21058c2ecf20Sopenharmony_ci * there is support for 4 DCT's, but only 2 are currently functional. 21068c2ecf20Sopenharmony_ci * They are DCT0 and DCT3. But we have read all registers of DCT3 into 21078c2ecf20Sopenharmony_ci * pvt->csels[1]. So we need to use '1' here to get correct info. 21088c2ecf20Sopenharmony_ci * Refer F15 M30h BKDG Section 2.10 and 2.10.3 for clarifications. 21098c2ecf20Sopenharmony_ci */ 21108c2ecf20Sopenharmony_ci alias_channel = (channel == 3) ? 1 : channel; 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, alias_channel); 21138c2ecf20Sopenharmony_ci 21148c2ecf20Sopenharmony_ci if (cs_found >= 0) 21158c2ecf20Sopenharmony_ci *chan_sel = alias_channel; 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci return cs_found; 21188c2ecf20Sopenharmony_ci} 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_cistatic int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt, 21218c2ecf20Sopenharmony_ci u64 sys_addr, 21228c2ecf20Sopenharmony_ci int *chan_sel) 21238c2ecf20Sopenharmony_ci{ 21248c2ecf20Sopenharmony_ci int cs_found = -EINVAL; 21258c2ecf20Sopenharmony_ci unsigned range; 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci for (range = 0; range < DRAM_RANGES; range++) { 21288c2ecf20Sopenharmony_ci if (!dram_rw(pvt, range)) 21298c2ecf20Sopenharmony_ci continue; 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci if (pvt->fam == 0x15 && pvt->model >= 0x30) 21328c2ecf20Sopenharmony_ci cs_found = f15_m30h_match_to_this_node(pvt, range, 21338c2ecf20Sopenharmony_ci sys_addr, 21348c2ecf20Sopenharmony_ci chan_sel); 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci else if ((get_dram_base(pvt, range) <= sys_addr) && 21378c2ecf20Sopenharmony_ci (get_dram_limit(pvt, range) >= sys_addr)) { 21388c2ecf20Sopenharmony_ci cs_found = f1x_match_to_this_node(pvt, range, 21398c2ecf20Sopenharmony_ci sys_addr, chan_sel); 21408c2ecf20Sopenharmony_ci if (cs_found >= 0) 21418c2ecf20Sopenharmony_ci break; 21428c2ecf20Sopenharmony_ci } 21438c2ecf20Sopenharmony_ci } 21448c2ecf20Sopenharmony_ci return cs_found; 21458c2ecf20Sopenharmony_ci} 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci/* 21488c2ecf20Sopenharmony_ci * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps 21498c2ecf20Sopenharmony_ci * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW). 21508c2ecf20Sopenharmony_ci * 21518c2ecf20Sopenharmony_ci * The @sys_addr is usually an error address received from the hardware 21528c2ecf20Sopenharmony_ci * (MCX_ADDR). 21538c2ecf20Sopenharmony_ci */ 21548c2ecf20Sopenharmony_cistatic void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr, 21558c2ecf20Sopenharmony_ci struct err_info *err) 21568c2ecf20Sopenharmony_ci{ 21578c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_ci error_address_to_page_and_offset(sys_addr, err); 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci err->csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &err->channel); 21628c2ecf20Sopenharmony_ci if (err->csrow < 0) { 21638c2ecf20Sopenharmony_ci err->err_code = ERR_CSROW; 21648c2ecf20Sopenharmony_ci return; 21658c2ecf20Sopenharmony_ci } 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci /* 21688c2ecf20Sopenharmony_ci * We need the syndromes for channel detection only when we're 21698c2ecf20Sopenharmony_ci * ganged. Otherwise @chan should already contain the channel at 21708c2ecf20Sopenharmony_ci * this point. 21718c2ecf20Sopenharmony_ci */ 21728c2ecf20Sopenharmony_ci if (dct_ganging_enabled(pvt)) 21738c2ecf20Sopenharmony_ci err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome); 21748c2ecf20Sopenharmony_ci} 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_ci/* 21778c2ecf20Sopenharmony_ci * debug routine to display the memory sizes of all logical DIMMs and its 21788c2ecf20Sopenharmony_ci * CSROWs 21798c2ecf20Sopenharmony_ci */ 21808c2ecf20Sopenharmony_cistatic void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl) 21818c2ecf20Sopenharmony_ci{ 21828c2ecf20Sopenharmony_ci int dimm, size0, size1; 21838c2ecf20Sopenharmony_ci u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases; 21848c2ecf20Sopenharmony_ci u32 dbam = ctrl ? pvt->dbam1 : pvt->dbam0; 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci if (pvt->fam == 0xf) { 21878c2ecf20Sopenharmony_ci /* K8 families < revF not supported yet */ 21888c2ecf20Sopenharmony_ci if (pvt->ext_model < K8_REV_F) 21898c2ecf20Sopenharmony_ci return; 21908c2ecf20Sopenharmony_ci else 21918c2ecf20Sopenharmony_ci WARN_ON(ctrl != 0); 21928c2ecf20Sopenharmony_ci } 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci if (pvt->fam == 0x10) { 21958c2ecf20Sopenharmony_ci dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1 21968c2ecf20Sopenharmony_ci : pvt->dbam0; 21978c2ecf20Sopenharmony_ci dcsb = (ctrl && !dct_ganging_enabled(pvt)) ? 21988c2ecf20Sopenharmony_ci pvt->csels[1].csbases : 21998c2ecf20Sopenharmony_ci pvt->csels[0].csbases; 22008c2ecf20Sopenharmony_ci } else if (ctrl) { 22018c2ecf20Sopenharmony_ci dbam = pvt->dbam0; 22028c2ecf20Sopenharmony_ci dcsb = pvt->csels[1].csbases; 22038c2ecf20Sopenharmony_ci } 22048c2ecf20Sopenharmony_ci edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n", 22058c2ecf20Sopenharmony_ci ctrl, dbam); 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_ci edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl); 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci /* Dump memory sizes for DIMM and its CSROWs */ 22108c2ecf20Sopenharmony_ci for (dimm = 0; dimm < 4; dimm++) { 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci size0 = 0; 22138c2ecf20Sopenharmony_ci if (dcsb[dimm*2] & DCSB_CS_ENABLE) 22148c2ecf20Sopenharmony_ci /* 22158c2ecf20Sopenharmony_ci * For F15m60h, we need multiplier for LRDIMM cs_size 22168c2ecf20Sopenharmony_ci * calculation. We pass dimm value to the dbam_to_cs 22178c2ecf20Sopenharmony_ci * mapper so we can find the multiplier from the 22188c2ecf20Sopenharmony_ci * corresponding DCSM. 22198c2ecf20Sopenharmony_ci */ 22208c2ecf20Sopenharmony_ci size0 = pvt->ops->dbam_to_cs(pvt, ctrl, 22218c2ecf20Sopenharmony_ci DBAM_DIMM(dimm, dbam), 22228c2ecf20Sopenharmony_ci dimm); 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci size1 = 0; 22258c2ecf20Sopenharmony_ci if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE) 22268c2ecf20Sopenharmony_ci size1 = pvt->ops->dbam_to_cs(pvt, ctrl, 22278c2ecf20Sopenharmony_ci DBAM_DIMM(dimm, dbam), 22288c2ecf20Sopenharmony_ci dimm); 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n", 22318c2ecf20Sopenharmony_ci dimm * 2, size0, 22328c2ecf20Sopenharmony_ci dimm * 2 + 1, size1); 22338c2ecf20Sopenharmony_ci } 22348c2ecf20Sopenharmony_ci} 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_cistatic struct amd64_family_type family_types[] = { 22378c2ecf20Sopenharmony_ci [K8_CPUS] = { 22388c2ecf20Sopenharmony_ci .ctl_name = "K8", 22398c2ecf20Sopenharmony_ci .f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP, 22408c2ecf20Sopenharmony_ci .f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL, 22418c2ecf20Sopenharmony_ci .max_mcs = 2, 22428c2ecf20Sopenharmony_ci .ops = { 22438c2ecf20Sopenharmony_ci .early_channel_count = k8_early_channel_count, 22448c2ecf20Sopenharmony_ci .map_sysaddr_to_csrow = k8_map_sysaddr_to_csrow, 22458c2ecf20Sopenharmony_ci .dbam_to_cs = k8_dbam_to_chip_select, 22468c2ecf20Sopenharmony_ci } 22478c2ecf20Sopenharmony_ci }, 22488c2ecf20Sopenharmony_ci [F10_CPUS] = { 22498c2ecf20Sopenharmony_ci .ctl_name = "F10h", 22508c2ecf20Sopenharmony_ci .f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP, 22518c2ecf20Sopenharmony_ci .f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM, 22528c2ecf20Sopenharmony_ci .max_mcs = 2, 22538c2ecf20Sopenharmony_ci .ops = { 22548c2ecf20Sopenharmony_ci .early_channel_count = f1x_early_channel_count, 22558c2ecf20Sopenharmony_ci .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 22568c2ecf20Sopenharmony_ci .dbam_to_cs = f10_dbam_to_chip_select, 22578c2ecf20Sopenharmony_ci } 22588c2ecf20Sopenharmony_ci }, 22598c2ecf20Sopenharmony_ci [F15_CPUS] = { 22608c2ecf20Sopenharmony_ci .ctl_name = "F15h", 22618c2ecf20Sopenharmony_ci .f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1, 22628c2ecf20Sopenharmony_ci .f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2, 22638c2ecf20Sopenharmony_ci .max_mcs = 2, 22648c2ecf20Sopenharmony_ci .ops = { 22658c2ecf20Sopenharmony_ci .early_channel_count = f1x_early_channel_count, 22668c2ecf20Sopenharmony_ci .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 22678c2ecf20Sopenharmony_ci .dbam_to_cs = f15_dbam_to_chip_select, 22688c2ecf20Sopenharmony_ci } 22698c2ecf20Sopenharmony_ci }, 22708c2ecf20Sopenharmony_ci [F15_M30H_CPUS] = { 22718c2ecf20Sopenharmony_ci .ctl_name = "F15h_M30h", 22728c2ecf20Sopenharmony_ci .f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1, 22738c2ecf20Sopenharmony_ci .f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2, 22748c2ecf20Sopenharmony_ci .max_mcs = 2, 22758c2ecf20Sopenharmony_ci .ops = { 22768c2ecf20Sopenharmony_ci .early_channel_count = f1x_early_channel_count, 22778c2ecf20Sopenharmony_ci .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 22788c2ecf20Sopenharmony_ci .dbam_to_cs = f16_dbam_to_chip_select, 22798c2ecf20Sopenharmony_ci } 22808c2ecf20Sopenharmony_ci }, 22818c2ecf20Sopenharmony_ci [F15_M60H_CPUS] = { 22828c2ecf20Sopenharmony_ci .ctl_name = "F15h_M60h", 22838c2ecf20Sopenharmony_ci .f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1, 22848c2ecf20Sopenharmony_ci .f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2, 22858c2ecf20Sopenharmony_ci .max_mcs = 2, 22868c2ecf20Sopenharmony_ci .ops = { 22878c2ecf20Sopenharmony_ci .early_channel_count = f1x_early_channel_count, 22888c2ecf20Sopenharmony_ci .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 22898c2ecf20Sopenharmony_ci .dbam_to_cs = f15_m60h_dbam_to_chip_select, 22908c2ecf20Sopenharmony_ci } 22918c2ecf20Sopenharmony_ci }, 22928c2ecf20Sopenharmony_ci [F16_CPUS] = { 22938c2ecf20Sopenharmony_ci .ctl_name = "F16h", 22948c2ecf20Sopenharmony_ci .f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1, 22958c2ecf20Sopenharmony_ci .f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2, 22968c2ecf20Sopenharmony_ci .max_mcs = 2, 22978c2ecf20Sopenharmony_ci .ops = { 22988c2ecf20Sopenharmony_ci .early_channel_count = f1x_early_channel_count, 22998c2ecf20Sopenharmony_ci .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 23008c2ecf20Sopenharmony_ci .dbam_to_cs = f16_dbam_to_chip_select, 23018c2ecf20Sopenharmony_ci } 23028c2ecf20Sopenharmony_ci }, 23038c2ecf20Sopenharmony_ci [F16_M30H_CPUS] = { 23048c2ecf20Sopenharmony_ci .ctl_name = "F16h_M30h", 23058c2ecf20Sopenharmony_ci .f1_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F1, 23068c2ecf20Sopenharmony_ci .f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2, 23078c2ecf20Sopenharmony_ci .max_mcs = 2, 23088c2ecf20Sopenharmony_ci .ops = { 23098c2ecf20Sopenharmony_ci .early_channel_count = f1x_early_channel_count, 23108c2ecf20Sopenharmony_ci .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 23118c2ecf20Sopenharmony_ci .dbam_to_cs = f16_dbam_to_chip_select, 23128c2ecf20Sopenharmony_ci } 23138c2ecf20Sopenharmony_ci }, 23148c2ecf20Sopenharmony_ci [F17_CPUS] = { 23158c2ecf20Sopenharmony_ci .ctl_name = "F17h", 23168c2ecf20Sopenharmony_ci .f0_id = PCI_DEVICE_ID_AMD_17H_DF_F0, 23178c2ecf20Sopenharmony_ci .f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6, 23188c2ecf20Sopenharmony_ci .max_mcs = 2, 23198c2ecf20Sopenharmony_ci .ops = { 23208c2ecf20Sopenharmony_ci .early_channel_count = f17_early_channel_count, 23218c2ecf20Sopenharmony_ci .dbam_to_cs = f17_addr_mask_to_cs_size, 23228c2ecf20Sopenharmony_ci } 23238c2ecf20Sopenharmony_ci }, 23248c2ecf20Sopenharmony_ci [F17_M10H_CPUS] = { 23258c2ecf20Sopenharmony_ci .ctl_name = "F17h_M10h", 23268c2ecf20Sopenharmony_ci .f0_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F0, 23278c2ecf20Sopenharmony_ci .f6_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F6, 23288c2ecf20Sopenharmony_ci .max_mcs = 2, 23298c2ecf20Sopenharmony_ci .ops = { 23308c2ecf20Sopenharmony_ci .early_channel_count = f17_early_channel_count, 23318c2ecf20Sopenharmony_ci .dbam_to_cs = f17_addr_mask_to_cs_size, 23328c2ecf20Sopenharmony_ci } 23338c2ecf20Sopenharmony_ci }, 23348c2ecf20Sopenharmony_ci [F17_M30H_CPUS] = { 23358c2ecf20Sopenharmony_ci .ctl_name = "F17h_M30h", 23368c2ecf20Sopenharmony_ci .f0_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F0, 23378c2ecf20Sopenharmony_ci .f6_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F6, 23388c2ecf20Sopenharmony_ci .max_mcs = 8, 23398c2ecf20Sopenharmony_ci .ops = { 23408c2ecf20Sopenharmony_ci .early_channel_count = f17_early_channel_count, 23418c2ecf20Sopenharmony_ci .dbam_to_cs = f17_addr_mask_to_cs_size, 23428c2ecf20Sopenharmony_ci } 23438c2ecf20Sopenharmony_ci }, 23448c2ecf20Sopenharmony_ci [F17_M60H_CPUS] = { 23458c2ecf20Sopenharmony_ci .ctl_name = "F17h_M60h", 23468c2ecf20Sopenharmony_ci .f0_id = PCI_DEVICE_ID_AMD_17H_M60H_DF_F0, 23478c2ecf20Sopenharmony_ci .f6_id = PCI_DEVICE_ID_AMD_17H_M60H_DF_F6, 23488c2ecf20Sopenharmony_ci .max_mcs = 2, 23498c2ecf20Sopenharmony_ci .ops = { 23508c2ecf20Sopenharmony_ci .early_channel_count = f17_early_channel_count, 23518c2ecf20Sopenharmony_ci .dbam_to_cs = f17_addr_mask_to_cs_size, 23528c2ecf20Sopenharmony_ci } 23538c2ecf20Sopenharmony_ci }, 23548c2ecf20Sopenharmony_ci [F17_M70H_CPUS] = { 23558c2ecf20Sopenharmony_ci .ctl_name = "F17h_M70h", 23568c2ecf20Sopenharmony_ci .f0_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F0, 23578c2ecf20Sopenharmony_ci .f6_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F6, 23588c2ecf20Sopenharmony_ci .max_mcs = 2, 23598c2ecf20Sopenharmony_ci .ops = { 23608c2ecf20Sopenharmony_ci .early_channel_count = f17_early_channel_count, 23618c2ecf20Sopenharmony_ci .dbam_to_cs = f17_addr_mask_to_cs_size, 23628c2ecf20Sopenharmony_ci } 23638c2ecf20Sopenharmony_ci }, 23648c2ecf20Sopenharmony_ci [F19_CPUS] = { 23658c2ecf20Sopenharmony_ci .ctl_name = "F19h", 23668c2ecf20Sopenharmony_ci .f0_id = PCI_DEVICE_ID_AMD_19H_DF_F0, 23678c2ecf20Sopenharmony_ci .f6_id = PCI_DEVICE_ID_AMD_19H_DF_F6, 23688c2ecf20Sopenharmony_ci .max_mcs = 8, 23698c2ecf20Sopenharmony_ci .ops = { 23708c2ecf20Sopenharmony_ci .early_channel_count = f17_early_channel_count, 23718c2ecf20Sopenharmony_ci .dbam_to_cs = f17_addr_mask_to_cs_size, 23728c2ecf20Sopenharmony_ci } 23738c2ecf20Sopenharmony_ci }, 23748c2ecf20Sopenharmony_ci}; 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_ci/* 23778c2ecf20Sopenharmony_ci * These are tables of eigenvectors (one per line) which can be used for the 23788c2ecf20Sopenharmony_ci * construction of the syndrome tables. The modified syndrome search algorithm 23798c2ecf20Sopenharmony_ci * uses those to find the symbol in error and thus the DIMM. 23808c2ecf20Sopenharmony_ci * 23818c2ecf20Sopenharmony_ci * Algorithm courtesy of Ross LaFetra from AMD. 23828c2ecf20Sopenharmony_ci */ 23838c2ecf20Sopenharmony_cistatic const u16 x4_vectors[] = { 23848c2ecf20Sopenharmony_ci 0x2f57, 0x1afe, 0x66cc, 0xdd88, 23858c2ecf20Sopenharmony_ci 0x11eb, 0x3396, 0x7f4c, 0xeac8, 23868c2ecf20Sopenharmony_ci 0x0001, 0x0002, 0x0004, 0x0008, 23878c2ecf20Sopenharmony_ci 0x1013, 0x3032, 0x4044, 0x8088, 23888c2ecf20Sopenharmony_ci 0x106b, 0x30d6, 0x70fc, 0xe0a8, 23898c2ecf20Sopenharmony_ci 0x4857, 0xc4fe, 0x13cc, 0x3288, 23908c2ecf20Sopenharmony_ci 0x1ac5, 0x2f4a, 0x5394, 0xa1e8, 23918c2ecf20Sopenharmony_ci 0x1f39, 0x251e, 0xbd6c, 0x6bd8, 23928c2ecf20Sopenharmony_ci 0x15c1, 0x2a42, 0x89ac, 0x4758, 23938c2ecf20Sopenharmony_ci 0x2b03, 0x1602, 0x4f0c, 0xca08, 23948c2ecf20Sopenharmony_ci 0x1f07, 0x3a0e, 0x6b04, 0xbd08, 23958c2ecf20Sopenharmony_ci 0x8ba7, 0x465e, 0x244c, 0x1cc8, 23968c2ecf20Sopenharmony_ci 0x2b87, 0x164e, 0x642c, 0xdc18, 23978c2ecf20Sopenharmony_ci 0x40b9, 0x80de, 0x1094, 0x20e8, 23988c2ecf20Sopenharmony_ci 0x27db, 0x1eb6, 0x9dac, 0x7b58, 23998c2ecf20Sopenharmony_ci 0x11c1, 0x2242, 0x84ac, 0x4c58, 24008c2ecf20Sopenharmony_ci 0x1be5, 0x2d7a, 0x5e34, 0xa718, 24018c2ecf20Sopenharmony_ci 0x4b39, 0x8d1e, 0x14b4, 0x28d8, 24028c2ecf20Sopenharmony_ci 0x4c97, 0xc87e, 0x11fc, 0x33a8, 24038c2ecf20Sopenharmony_ci 0x8e97, 0x497e, 0x2ffc, 0x1aa8, 24048c2ecf20Sopenharmony_ci 0x16b3, 0x3d62, 0x4f34, 0x8518, 24058c2ecf20Sopenharmony_ci 0x1e2f, 0x391a, 0x5cac, 0xf858, 24068c2ecf20Sopenharmony_ci 0x1d9f, 0x3b7a, 0x572c, 0xfe18, 24078c2ecf20Sopenharmony_ci 0x15f5, 0x2a5a, 0x5264, 0xa3b8, 24088c2ecf20Sopenharmony_ci 0x1dbb, 0x3b66, 0x715c, 0xe3f8, 24098c2ecf20Sopenharmony_ci 0x4397, 0xc27e, 0x17fc, 0x3ea8, 24108c2ecf20Sopenharmony_ci 0x1617, 0x3d3e, 0x6464, 0xb8b8, 24118c2ecf20Sopenharmony_ci 0x23ff, 0x12aa, 0xab6c, 0x56d8, 24128c2ecf20Sopenharmony_ci 0x2dfb, 0x1ba6, 0x913c, 0x7328, 24138c2ecf20Sopenharmony_ci 0x185d, 0x2ca6, 0x7914, 0x9e28, 24148c2ecf20Sopenharmony_ci 0x171b, 0x3e36, 0x7d7c, 0xebe8, 24158c2ecf20Sopenharmony_ci 0x4199, 0x82ee, 0x19f4, 0x2e58, 24168c2ecf20Sopenharmony_ci 0x4807, 0xc40e, 0x130c, 0x3208, 24178c2ecf20Sopenharmony_ci 0x1905, 0x2e0a, 0x5804, 0xac08, 24188c2ecf20Sopenharmony_ci 0x213f, 0x132a, 0xadfc, 0x5ba8, 24198c2ecf20Sopenharmony_ci 0x19a9, 0x2efe, 0xb5cc, 0x6f88, 24208c2ecf20Sopenharmony_ci}; 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_cistatic const u16 x8_vectors[] = { 24238c2ecf20Sopenharmony_ci 0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480, 24248c2ecf20Sopenharmony_ci 0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80, 24258c2ecf20Sopenharmony_ci 0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80, 24268c2ecf20Sopenharmony_ci 0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80, 24278c2ecf20Sopenharmony_ci 0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780, 24288c2ecf20Sopenharmony_ci 0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080, 24298c2ecf20Sopenharmony_ci 0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080, 24308c2ecf20Sopenharmony_ci 0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080, 24318c2ecf20Sopenharmony_ci 0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80, 24328c2ecf20Sopenharmony_ci 0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580, 24338c2ecf20Sopenharmony_ci 0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880, 24348c2ecf20Sopenharmony_ci 0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280, 24358c2ecf20Sopenharmony_ci 0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180, 24368c2ecf20Sopenharmony_ci 0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580, 24378c2ecf20Sopenharmony_ci 0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280, 24388c2ecf20Sopenharmony_ci 0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180, 24398c2ecf20Sopenharmony_ci 0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080, 24408c2ecf20Sopenharmony_ci 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 24418c2ecf20Sopenharmony_ci 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000, 24428c2ecf20Sopenharmony_ci}; 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_cistatic int decode_syndrome(u16 syndrome, const u16 *vectors, unsigned num_vecs, 24458c2ecf20Sopenharmony_ci unsigned v_dim) 24468c2ecf20Sopenharmony_ci{ 24478c2ecf20Sopenharmony_ci unsigned int i, err_sym; 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_ci for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) { 24508c2ecf20Sopenharmony_ci u16 s = syndrome; 24518c2ecf20Sopenharmony_ci unsigned v_idx = err_sym * v_dim; 24528c2ecf20Sopenharmony_ci unsigned v_end = (err_sym + 1) * v_dim; 24538c2ecf20Sopenharmony_ci 24548c2ecf20Sopenharmony_ci /* walk over all 16 bits of the syndrome */ 24558c2ecf20Sopenharmony_ci for (i = 1; i < (1U << 16); i <<= 1) { 24568c2ecf20Sopenharmony_ci 24578c2ecf20Sopenharmony_ci /* if bit is set in that eigenvector... */ 24588c2ecf20Sopenharmony_ci if (v_idx < v_end && vectors[v_idx] & i) { 24598c2ecf20Sopenharmony_ci u16 ev_comp = vectors[v_idx++]; 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ci /* ... and bit set in the modified syndrome, */ 24628c2ecf20Sopenharmony_ci if (s & i) { 24638c2ecf20Sopenharmony_ci /* remove it. */ 24648c2ecf20Sopenharmony_ci s ^= ev_comp; 24658c2ecf20Sopenharmony_ci 24668c2ecf20Sopenharmony_ci if (!s) 24678c2ecf20Sopenharmony_ci return err_sym; 24688c2ecf20Sopenharmony_ci } 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_ci } else if (s & i) 24718c2ecf20Sopenharmony_ci /* can't get to zero, move to next symbol */ 24728c2ecf20Sopenharmony_ci break; 24738c2ecf20Sopenharmony_ci } 24748c2ecf20Sopenharmony_ci } 24758c2ecf20Sopenharmony_ci 24768c2ecf20Sopenharmony_ci edac_dbg(0, "syndrome(%x) not found\n", syndrome); 24778c2ecf20Sopenharmony_ci return -1; 24788c2ecf20Sopenharmony_ci} 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_cistatic int map_err_sym_to_channel(int err_sym, int sym_size) 24818c2ecf20Sopenharmony_ci{ 24828c2ecf20Sopenharmony_ci if (sym_size == 4) 24838c2ecf20Sopenharmony_ci switch (err_sym) { 24848c2ecf20Sopenharmony_ci case 0x20: 24858c2ecf20Sopenharmony_ci case 0x21: 24868c2ecf20Sopenharmony_ci return 0; 24878c2ecf20Sopenharmony_ci break; 24888c2ecf20Sopenharmony_ci case 0x22: 24898c2ecf20Sopenharmony_ci case 0x23: 24908c2ecf20Sopenharmony_ci return 1; 24918c2ecf20Sopenharmony_ci break; 24928c2ecf20Sopenharmony_ci default: 24938c2ecf20Sopenharmony_ci return err_sym >> 4; 24948c2ecf20Sopenharmony_ci break; 24958c2ecf20Sopenharmony_ci } 24968c2ecf20Sopenharmony_ci /* x8 symbols */ 24978c2ecf20Sopenharmony_ci else 24988c2ecf20Sopenharmony_ci switch (err_sym) { 24998c2ecf20Sopenharmony_ci /* imaginary bits not in a DIMM */ 25008c2ecf20Sopenharmony_ci case 0x10: 25018c2ecf20Sopenharmony_ci WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n", 25028c2ecf20Sopenharmony_ci err_sym); 25038c2ecf20Sopenharmony_ci return -1; 25048c2ecf20Sopenharmony_ci break; 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_ci case 0x11: 25078c2ecf20Sopenharmony_ci return 0; 25088c2ecf20Sopenharmony_ci break; 25098c2ecf20Sopenharmony_ci case 0x12: 25108c2ecf20Sopenharmony_ci return 1; 25118c2ecf20Sopenharmony_ci break; 25128c2ecf20Sopenharmony_ci default: 25138c2ecf20Sopenharmony_ci return err_sym >> 3; 25148c2ecf20Sopenharmony_ci break; 25158c2ecf20Sopenharmony_ci } 25168c2ecf20Sopenharmony_ci return -1; 25178c2ecf20Sopenharmony_ci} 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_cistatic int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome) 25208c2ecf20Sopenharmony_ci{ 25218c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 25228c2ecf20Sopenharmony_ci int err_sym = -1; 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_ci if (pvt->ecc_sym_sz == 8) 25258c2ecf20Sopenharmony_ci err_sym = decode_syndrome(syndrome, x8_vectors, 25268c2ecf20Sopenharmony_ci ARRAY_SIZE(x8_vectors), 25278c2ecf20Sopenharmony_ci pvt->ecc_sym_sz); 25288c2ecf20Sopenharmony_ci else if (pvt->ecc_sym_sz == 4) 25298c2ecf20Sopenharmony_ci err_sym = decode_syndrome(syndrome, x4_vectors, 25308c2ecf20Sopenharmony_ci ARRAY_SIZE(x4_vectors), 25318c2ecf20Sopenharmony_ci pvt->ecc_sym_sz); 25328c2ecf20Sopenharmony_ci else { 25338c2ecf20Sopenharmony_ci amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz); 25348c2ecf20Sopenharmony_ci return err_sym; 25358c2ecf20Sopenharmony_ci } 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz); 25388c2ecf20Sopenharmony_ci} 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_cistatic void __log_ecc_error(struct mem_ctl_info *mci, struct err_info *err, 25418c2ecf20Sopenharmony_ci u8 ecc_type) 25428c2ecf20Sopenharmony_ci{ 25438c2ecf20Sopenharmony_ci enum hw_event_mc_err_type err_type; 25448c2ecf20Sopenharmony_ci const char *string; 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci if (ecc_type == 2) 25478c2ecf20Sopenharmony_ci err_type = HW_EVENT_ERR_CORRECTED; 25488c2ecf20Sopenharmony_ci else if (ecc_type == 1) 25498c2ecf20Sopenharmony_ci err_type = HW_EVENT_ERR_UNCORRECTED; 25508c2ecf20Sopenharmony_ci else if (ecc_type == 3) 25518c2ecf20Sopenharmony_ci err_type = HW_EVENT_ERR_DEFERRED; 25528c2ecf20Sopenharmony_ci else { 25538c2ecf20Sopenharmony_ci WARN(1, "Something is rotten in the state of Denmark.\n"); 25548c2ecf20Sopenharmony_ci return; 25558c2ecf20Sopenharmony_ci } 25568c2ecf20Sopenharmony_ci 25578c2ecf20Sopenharmony_ci switch (err->err_code) { 25588c2ecf20Sopenharmony_ci case DECODE_OK: 25598c2ecf20Sopenharmony_ci string = ""; 25608c2ecf20Sopenharmony_ci break; 25618c2ecf20Sopenharmony_ci case ERR_NODE: 25628c2ecf20Sopenharmony_ci string = "Failed to map error addr to a node"; 25638c2ecf20Sopenharmony_ci break; 25648c2ecf20Sopenharmony_ci case ERR_CSROW: 25658c2ecf20Sopenharmony_ci string = "Failed to map error addr to a csrow"; 25668c2ecf20Sopenharmony_ci break; 25678c2ecf20Sopenharmony_ci case ERR_CHANNEL: 25688c2ecf20Sopenharmony_ci string = "Unknown syndrome - possible error reporting race"; 25698c2ecf20Sopenharmony_ci break; 25708c2ecf20Sopenharmony_ci case ERR_SYND: 25718c2ecf20Sopenharmony_ci string = "MCA_SYND not valid - unknown syndrome and csrow"; 25728c2ecf20Sopenharmony_ci break; 25738c2ecf20Sopenharmony_ci case ERR_NORM_ADDR: 25748c2ecf20Sopenharmony_ci string = "Cannot decode normalized address"; 25758c2ecf20Sopenharmony_ci break; 25768c2ecf20Sopenharmony_ci default: 25778c2ecf20Sopenharmony_ci string = "WTF error"; 25788c2ecf20Sopenharmony_ci break; 25798c2ecf20Sopenharmony_ci } 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_ci edac_mc_handle_error(err_type, mci, 1, 25828c2ecf20Sopenharmony_ci err->page, err->offset, err->syndrome, 25838c2ecf20Sopenharmony_ci err->csrow, err->channel, -1, 25848c2ecf20Sopenharmony_ci string, ""); 25858c2ecf20Sopenharmony_ci} 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_cistatic inline void decode_bus_error(int node_id, struct mce *m) 25888c2ecf20Sopenharmony_ci{ 25898c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 25908c2ecf20Sopenharmony_ci struct amd64_pvt *pvt; 25918c2ecf20Sopenharmony_ci u8 ecc_type = (m->status >> 45) & 0x3; 25928c2ecf20Sopenharmony_ci u8 xec = XEC(m->status, 0x1f); 25938c2ecf20Sopenharmony_ci u16 ec = EC(m->status); 25948c2ecf20Sopenharmony_ci u64 sys_addr; 25958c2ecf20Sopenharmony_ci struct err_info err; 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_ci mci = edac_mc_find(node_id); 25988c2ecf20Sopenharmony_ci if (!mci) 25998c2ecf20Sopenharmony_ci return; 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_ci /* Bail out early if this was an 'observed' error */ 26048c2ecf20Sopenharmony_ci if (PP(ec) == NBSL_PP_OBS) 26058c2ecf20Sopenharmony_ci return; 26068c2ecf20Sopenharmony_ci 26078c2ecf20Sopenharmony_ci /* Do only ECC errors */ 26088c2ecf20Sopenharmony_ci if (xec && xec != F10_NBSL_EXT_ERR_ECC) 26098c2ecf20Sopenharmony_ci return; 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_ci memset(&err, 0, sizeof(err)); 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ci sys_addr = get_error_address(pvt, m); 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci if (ecc_type == 2) 26168c2ecf20Sopenharmony_ci err.syndrome = extract_syndrome(m->status); 26178c2ecf20Sopenharmony_ci 26188c2ecf20Sopenharmony_ci pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err); 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci __log_ecc_error(mci, &err, ecc_type); 26218c2ecf20Sopenharmony_ci} 26228c2ecf20Sopenharmony_ci 26238c2ecf20Sopenharmony_ci/* 26248c2ecf20Sopenharmony_ci * To find the UMC channel represented by this bank we need to match on its 26258c2ecf20Sopenharmony_ci * instance_id. The instance_id of a bank is held in the lower 32 bits of its 26268c2ecf20Sopenharmony_ci * IPID. 26278c2ecf20Sopenharmony_ci * 26288c2ecf20Sopenharmony_ci * Currently, we can derive the channel number by looking at the 6th nibble in 26298c2ecf20Sopenharmony_ci * the instance_id. For example, instance_id=0xYXXXXX where Y is the channel 26308c2ecf20Sopenharmony_ci * number. 26318c2ecf20Sopenharmony_ci */ 26328c2ecf20Sopenharmony_cistatic int find_umc_channel(struct mce *m) 26338c2ecf20Sopenharmony_ci{ 26348c2ecf20Sopenharmony_ci return (m->ipid & GENMASK(31, 0)) >> 20; 26358c2ecf20Sopenharmony_ci} 26368c2ecf20Sopenharmony_ci 26378c2ecf20Sopenharmony_cistatic void decode_umc_error(int node_id, struct mce *m) 26388c2ecf20Sopenharmony_ci{ 26398c2ecf20Sopenharmony_ci u8 ecc_type = (m->status >> 45) & 0x3; 26408c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 26418c2ecf20Sopenharmony_ci struct amd64_pvt *pvt; 26428c2ecf20Sopenharmony_ci struct err_info err; 26438c2ecf20Sopenharmony_ci u64 sys_addr; 26448c2ecf20Sopenharmony_ci 26458c2ecf20Sopenharmony_ci mci = edac_mc_find(node_id); 26468c2ecf20Sopenharmony_ci if (!mci) 26478c2ecf20Sopenharmony_ci return; 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_ci memset(&err, 0, sizeof(err)); 26528c2ecf20Sopenharmony_ci 26538c2ecf20Sopenharmony_ci if (m->status & MCI_STATUS_DEFERRED) 26548c2ecf20Sopenharmony_ci ecc_type = 3; 26558c2ecf20Sopenharmony_ci 26568c2ecf20Sopenharmony_ci err.channel = find_umc_channel(m); 26578c2ecf20Sopenharmony_ci 26588c2ecf20Sopenharmony_ci if (!(m->status & MCI_STATUS_SYNDV)) { 26598c2ecf20Sopenharmony_ci err.err_code = ERR_SYND; 26608c2ecf20Sopenharmony_ci goto log_error; 26618c2ecf20Sopenharmony_ci } 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci if (ecc_type == 2) { 26648c2ecf20Sopenharmony_ci u8 length = (m->synd >> 18) & 0x3f; 26658c2ecf20Sopenharmony_ci 26668c2ecf20Sopenharmony_ci if (length) 26678c2ecf20Sopenharmony_ci err.syndrome = (m->synd >> 32) & GENMASK(length - 1, 0); 26688c2ecf20Sopenharmony_ci else 26698c2ecf20Sopenharmony_ci err.err_code = ERR_CHANNEL; 26708c2ecf20Sopenharmony_ci } 26718c2ecf20Sopenharmony_ci 26728c2ecf20Sopenharmony_ci err.csrow = m->synd & 0x7; 26738c2ecf20Sopenharmony_ci 26748c2ecf20Sopenharmony_ci if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) { 26758c2ecf20Sopenharmony_ci err.err_code = ERR_NORM_ADDR; 26768c2ecf20Sopenharmony_ci goto log_error; 26778c2ecf20Sopenharmony_ci } 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci error_address_to_page_and_offset(sys_addr, &err); 26808c2ecf20Sopenharmony_ci 26818c2ecf20Sopenharmony_cilog_error: 26828c2ecf20Sopenharmony_ci __log_ecc_error(mci, &err, ecc_type); 26838c2ecf20Sopenharmony_ci} 26848c2ecf20Sopenharmony_ci 26858c2ecf20Sopenharmony_ci/* 26868c2ecf20Sopenharmony_ci * Use pvt->F3 which contains the F3 CPU PCI device to get the related 26878c2ecf20Sopenharmony_ci * F1 (AddrMap) and F2 (Dct) devices. Return negative value on error. 26888c2ecf20Sopenharmony_ci * Reserve F0 and F6 on systems with a UMC. 26898c2ecf20Sopenharmony_ci */ 26908c2ecf20Sopenharmony_cistatic int 26918c2ecf20Sopenharmony_cireserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2) 26928c2ecf20Sopenharmony_ci{ 26938c2ecf20Sopenharmony_ci if (pvt->umc) { 26948c2ecf20Sopenharmony_ci pvt->F0 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3); 26958c2ecf20Sopenharmony_ci if (!pvt->F0) { 26968c2ecf20Sopenharmony_ci amd64_err("F0 not found, device 0x%x (broken BIOS?)\n", pci_id1); 26978c2ecf20Sopenharmony_ci return -ENODEV; 26988c2ecf20Sopenharmony_ci } 26998c2ecf20Sopenharmony_ci 27008c2ecf20Sopenharmony_ci pvt->F6 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3); 27018c2ecf20Sopenharmony_ci if (!pvt->F6) { 27028c2ecf20Sopenharmony_ci pci_dev_put(pvt->F0); 27038c2ecf20Sopenharmony_ci pvt->F0 = NULL; 27048c2ecf20Sopenharmony_ci 27058c2ecf20Sopenharmony_ci amd64_err("F6 not found: device 0x%x (broken BIOS?)\n", pci_id2); 27068c2ecf20Sopenharmony_ci return -ENODEV; 27078c2ecf20Sopenharmony_ci } 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_ci if (!pci_ctl_dev) 27108c2ecf20Sopenharmony_ci pci_ctl_dev = &pvt->F0->dev; 27118c2ecf20Sopenharmony_ci 27128c2ecf20Sopenharmony_ci edac_dbg(1, "F0: %s\n", pci_name(pvt->F0)); 27138c2ecf20Sopenharmony_ci edac_dbg(1, "F3: %s\n", pci_name(pvt->F3)); 27148c2ecf20Sopenharmony_ci edac_dbg(1, "F6: %s\n", pci_name(pvt->F6)); 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_ci return 0; 27178c2ecf20Sopenharmony_ci } 27188c2ecf20Sopenharmony_ci 27198c2ecf20Sopenharmony_ci /* Reserve the ADDRESS MAP Device */ 27208c2ecf20Sopenharmony_ci pvt->F1 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3); 27218c2ecf20Sopenharmony_ci if (!pvt->F1) { 27228c2ecf20Sopenharmony_ci amd64_err("F1 not found: device 0x%x (broken BIOS?)\n", pci_id1); 27238c2ecf20Sopenharmony_ci return -ENODEV; 27248c2ecf20Sopenharmony_ci } 27258c2ecf20Sopenharmony_ci 27268c2ecf20Sopenharmony_ci /* Reserve the DCT Device */ 27278c2ecf20Sopenharmony_ci pvt->F2 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3); 27288c2ecf20Sopenharmony_ci if (!pvt->F2) { 27298c2ecf20Sopenharmony_ci pci_dev_put(pvt->F1); 27308c2ecf20Sopenharmony_ci pvt->F1 = NULL; 27318c2ecf20Sopenharmony_ci 27328c2ecf20Sopenharmony_ci amd64_err("F2 not found: device 0x%x (broken BIOS?)\n", pci_id2); 27338c2ecf20Sopenharmony_ci return -ENODEV; 27348c2ecf20Sopenharmony_ci } 27358c2ecf20Sopenharmony_ci 27368c2ecf20Sopenharmony_ci if (!pci_ctl_dev) 27378c2ecf20Sopenharmony_ci pci_ctl_dev = &pvt->F2->dev; 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_ci edac_dbg(1, "F1: %s\n", pci_name(pvt->F1)); 27408c2ecf20Sopenharmony_ci edac_dbg(1, "F2: %s\n", pci_name(pvt->F2)); 27418c2ecf20Sopenharmony_ci edac_dbg(1, "F3: %s\n", pci_name(pvt->F3)); 27428c2ecf20Sopenharmony_ci 27438c2ecf20Sopenharmony_ci return 0; 27448c2ecf20Sopenharmony_ci} 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_cistatic void free_mc_sibling_devs(struct amd64_pvt *pvt) 27478c2ecf20Sopenharmony_ci{ 27488c2ecf20Sopenharmony_ci if (pvt->umc) { 27498c2ecf20Sopenharmony_ci pci_dev_put(pvt->F0); 27508c2ecf20Sopenharmony_ci pci_dev_put(pvt->F6); 27518c2ecf20Sopenharmony_ci } else { 27528c2ecf20Sopenharmony_ci pci_dev_put(pvt->F1); 27538c2ecf20Sopenharmony_ci pci_dev_put(pvt->F2); 27548c2ecf20Sopenharmony_ci } 27558c2ecf20Sopenharmony_ci} 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_cistatic void determine_ecc_sym_sz(struct amd64_pvt *pvt) 27588c2ecf20Sopenharmony_ci{ 27598c2ecf20Sopenharmony_ci pvt->ecc_sym_sz = 4; 27608c2ecf20Sopenharmony_ci 27618c2ecf20Sopenharmony_ci if (pvt->umc) { 27628c2ecf20Sopenharmony_ci u8 i; 27638c2ecf20Sopenharmony_ci 27648c2ecf20Sopenharmony_ci for_each_umc(i) { 27658c2ecf20Sopenharmony_ci /* Check enabled channels only: */ 27668c2ecf20Sopenharmony_ci if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) { 27678c2ecf20Sopenharmony_ci if (pvt->umc[i].ecc_ctrl & BIT(9)) { 27688c2ecf20Sopenharmony_ci pvt->ecc_sym_sz = 16; 27698c2ecf20Sopenharmony_ci return; 27708c2ecf20Sopenharmony_ci } else if (pvt->umc[i].ecc_ctrl & BIT(7)) { 27718c2ecf20Sopenharmony_ci pvt->ecc_sym_sz = 8; 27728c2ecf20Sopenharmony_ci return; 27738c2ecf20Sopenharmony_ci } 27748c2ecf20Sopenharmony_ci } 27758c2ecf20Sopenharmony_ci } 27768c2ecf20Sopenharmony_ci } else if (pvt->fam >= 0x10) { 27778c2ecf20Sopenharmony_ci u32 tmp; 27788c2ecf20Sopenharmony_ci 27798c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp); 27808c2ecf20Sopenharmony_ci /* F16h has only DCT0, so no need to read dbam1. */ 27818c2ecf20Sopenharmony_ci if (pvt->fam != 0x16) 27828c2ecf20Sopenharmony_ci amd64_read_dct_pci_cfg(pvt, 1, DBAM0, &pvt->dbam1); 27838c2ecf20Sopenharmony_ci 27848c2ecf20Sopenharmony_ci /* F10h, revD and later can do x8 ECC too. */ 27858c2ecf20Sopenharmony_ci if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25)) 27868c2ecf20Sopenharmony_ci pvt->ecc_sym_sz = 8; 27878c2ecf20Sopenharmony_ci } 27888c2ecf20Sopenharmony_ci} 27898c2ecf20Sopenharmony_ci 27908c2ecf20Sopenharmony_ci/* 27918c2ecf20Sopenharmony_ci * Retrieve the hardware registers of the memory controller. 27928c2ecf20Sopenharmony_ci */ 27938c2ecf20Sopenharmony_cistatic void __read_mc_regs_df(struct amd64_pvt *pvt) 27948c2ecf20Sopenharmony_ci{ 27958c2ecf20Sopenharmony_ci u8 nid = pvt->mc_node_id; 27968c2ecf20Sopenharmony_ci struct amd64_umc *umc; 27978c2ecf20Sopenharmony_ci u32 i, umc_base; 27988c2ecf20Sopenharmony_ci 27998c2ecf20Sopenharmony_ci /* Read registers from each UMC */ 28008c2ecf20Sopenharmony_ci for_each_umc(i) { 28018c2ecf20Sopenharmony_ci 28028c2ecf20Sopenharmony_ci umc_base = get_umc_base(i); 28038c2ecf20Sopenharmony_ci umc = &pvt->umc[i]; 28048c2ecf20Sopenharmony_ci 28058c2ecf20Sopenharmony_ci amd_smn_read(nid, umc_base + UMCCH_DIMM_CFG, &umc->dimm_cfg); 28068c2ecf20Sopenharmony_ci amd_smn_read(nid, umc_base + UMCCH_UMC_CFG, &umc->umc_cfg); 28078c2ecf20Sopenharmony_ci amd_smn_read(nid, umc_base + UMCCH_SDP_CTRL, &umc->sdp_ctrl); 28088c2ecf20Sopenharmony_ci amd_smn_read(nid, umc_base + UMCCH_ECC_CTRL, &umc->ecc_ctrl); 28098c2ecf20Sopenharmony_ci amd_smn_read(nid, umc_base + UMCCH_UMC_CAP_HI, &umc->umc_cap_hi); 28108c2ecf20Sopenharmony_ci } 28118c2ecf20Sopenharmony_ci} 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_ci/* 28148c2ecf20Sopenharmony_ci * Retrieve the hardware registers of the memory controller (this includes the 28158c2ecf20Sopenharmony_ci * 'Address Map' and 'Misc' device regs) 28168c2ecf20Sopenharmony_ci */ 28178c2ecf20Sopenharmony_cistatic void read_mc_regs(struct amd64_pvt *pvt) 28188c2ecf20Sopenharmony_ci{ 28198c2ecf20Sopenharmony_ci unsigned int range; 28208c2ecf20Sopenharmony_ci u64 msr_val; 28218c2ecf20Sopenharmony_ci 28228c2ecf20Sopenharmony_ci /* 28238c2ecf20Sopenharmony_ci * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since 28248c2ecf20Sopenharmony_ci * those are Read-As-Zero. 28258c2ecf20Sopenharmony_ci */ 28268c2ecf20Sopenharmony_ci rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem); 28278c2ecf20Sopenharmony_ci edac_dbg(0, " TOP_MEM: 0x%016llx\n", pvt->top_mem); 28288c2ecf20Sopenharmony_ci 28298c2ecf20Sopenharmony_ci /* Check first whether TOP_MEM2 is enabled: */ 28308c2ecf20Sopenharmony_ci rdmsrl(MSR_K8_SYSCFG, msr_val); 28318c2ecf20Sopenharmony_ci if (msr_val & BIT(21)) { 28328c2ecf20Sopenharmony_ci rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2); 28338c2ecf20Sopenharmony_ci edac_dbg(0, " TOP_MEM2: 0x%016llx\n", pvt->top_mem2); 28348c2ecf20Sopenharmony_ci } else { 28358c2ecf20Sopenharmony_ci edac_dbg(0, " TOP_MEM2 disabled\n"); 28368c2ecf20Sopenharmony_ci } 28378c2ecf20Sopenharmony_ci 28388c2ecf20Sopenharmony_ci if (pvt->umc) { 28398c2ecf20Sopenharmony_ci __read_mc_regs_df(pvt); 28408c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F0, DF_DHAR, &pvt->dhar); 28418c2ecf20Sopenharmony_ci 28428c2ecf20Sopenharmony_ci goto skip; 28438c2ecf20Sopenharmony_ci } 28448c2ecf20Sopenharmony_ci 28458c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap); 28468c2ecf20Sopenharmony_ci 28478c2ecf20Sopenharmony_ci read_dram_ctl_register(pvt); 28488c2ecf20Sopenharmony_ci 28498c2ecf20Sopenharmony_ci for (range = 0; range < DRAM_RANGES; range++) { 28508c2ecf20Sopenharmony_ci u8 rw; 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_ci /* read settings for this DRAM range */ 28538c2ecf20Sopenharmony_ci read_dram_base_limit_regs(pvt, range); 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_ci rw = dram_rw(pvt, range); 28568c2ecf20Sopenharmony_ci if (!rw) 28578c2ecf20Sopenharmony_ci continue; 28588c2ecf20Sopenharmony_ci 28598c2ecf20Sopenharmony_ci edac_dbg(1, " DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n", 28608c2ecf20Sopenharmony_ci range, 28618c2ecf20Sopenharmony_ci get_dram_base(pvt, range), 28628c2ecf20Sopenharmony_ci get_dram_limit(pvt, range)); 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci edac_dbg(1, " IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n", 28658c2ecf20Sopenharmony_ci dram_intlv_en(pvt, range) ? "Enabled" : "Disabled", 28668c2ecf20Sopenharmony_ci (rw & 0x1) ? "R" : "-", 28678c2ecf20Sopenharmony_ci (rw & 0x2) ? "W" : "-", 28688c2ecf20Sopenharmony_ci dram_intlv_sel(pvt, range), 28698c2ecf20Sopenharmony_ci dram_dst_node(pvt, range)); 28708c2ecf20Sopenharmony_ci } 28718c2ecf20Sopenharmony_ci 28728c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar); 28738c2ecf20Sopenharmony_ci amd64_read_dct_pci_cfg(pvt, 0, DBAM0, &pvt->dbam0); 28748c2ecf20Sopenharmony_ci 28758c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare); 28768c2ecf20Sopenharmony_ci 28778c2ecf20Sopenharmony_ci amd64_read_dct_pci_cfg(pvt, 0, DCLR0, &pvt->dclr0); 28788c2ecf20Sopenharmony_ci amd64_read_dct_pci_cfg(pvt, 0, DCHR0, &pvt->dchr0); 28798c2ecf20Sopenharmony_ci 28808c2ecf20Sopenharmony_ci if (!dct_ganging_enabled(pvt)) { 28818c2ecf20Sopenharmony_ci amd64_read_dct_pci_cfg(pvt, 1, DCLR0, &pvt->dclr1); 28828c2ecf20Sopenharmony_ci amd64_read_dct_pci_cfg(pvt, 1, DCHR0, &pvt->dchr1); 28838c2ecf20Sopenharmony_ci } 28848c2ecf20Sopenharmony_ci 28858c2ecf20Sopenharmony_ciskip: 28868c2ecf20Sopenharmony_ci read_dct_base_mask(pvt); 28878c2ecf20Sopenharmony_ci 28888c2ecf20Sopenharmony_ci determine_memory_type(pvt); 28898c2ecf20Sopenharmony_ci edac_dbg(1, " DIMM type: %s\n", edac_mem_types[pvt->dram_type]); 28908c2ecf20Sopenharmony_ci 28918c2ecf20Sopenharmony_ci determine_ecc_sym_sz(pvt); 28928c2ecf20Sopenharmony_ci} 28938c2ecf20Sopenharmony_ci 28948c2ecf20Sopenharmony_ci/* 28958c2ecf20Sopenharmony_ci * NOTE: CPU Revision Dependent code 28968c2ecf20Sopenharmony_ci * 28978c2ecf20Sopenharmony_ci * Input: 28988c2ecf20Sopenharmony_ci * @csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1) 28998c2ecf20Sopenharmony_ci * k8 private pointer to --> 29008c2ecf20Sopenharmony_ci * DRAM Bank Address mapping register 29018c2ecf20Sopenharmony_ci * node_id 29028c2ecf20Sopenharmony_ci * DCL register where dual_channel_active is 29038c2ecf20Sopenharmony_ci * 29048c2ecf20Sopenharmony_ci * The DBAM register consists of 4 sets of 4 bits each definitions: 29058c2ecf20Sopenharmony_ci * 29068c2ecf20Sopenharmony_ci * Bits: CSROWs 29078c2ecf20Sopenharmony_ci * 0-3 CSROWs 0 and 1 29088c2ecf20Sopenharmony_ci * 4-7 CSROWs 2 and 3 29098c2ecf20Sopenharmony_ci * 8-11 CSROWs 4 and 5 29108c2ecf20Sopenharmony_ci * 12-15 CSROWs 6 and 7 29118c2ecf20Sopenharmony_ci * 29128c2ecf20Sopenharmony_ci * Values range from: 0 to 15 29138c2ecf20Sopenharmony_ci * The meaning of the values depends on CPU revision and dual-channel state, 29148c2ecf20Sopenharmony_ci * see relevant BKDG more info. 29158c2ecf20Sopenharmony_ci * 29168c2ecf20Sopenharmony_ci * The memory controller provides for total of only 8 CSROWs in its current 29178c2ecf20Sopenharmony_ci * architecture. Each "pair" of CSROWs normally represents just one DIMM in 29188c2ecf20Sopenharmony_ci * single channel or two (2) DIMMs in dual channel mode. 29198c2ecf20Sopenharmony_ci * 29208c2ecf20Sopenharmony_ci * The following code logic collapses the various tables for CSROW based on CPU 29218c2ecf20Sopenharmony_ci * revision. 29228c2ecf20Sopenharmony_ci * 29238c2ecf20Sopenharmony_ci * Returns: 29248c2ecf20Sopenharmony_ci * The number of PAGE_SIZE pages on the specified CSROW number it 29258c2ecf20Sopenharmony_ci * encompasses 29268c2ecf20Sopenharmony_ci * 29278c2ecf20Sopenharmony_ci */ 29288c2ecf20Sopenharmony_cistatic u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr_orig) 29298c2ecf20Sopenharmony_ci{ 29308c2ecf20Sopenharmony_ci u32 dbam = dct ? pvt->dbam1 : pvt->dbam0; 29318c2ecf20Sopenharmony_ci int csrow_nr = csrow_nr_orig; 29328c2ecf20Sopenharmony_ci u32 cs_mode, nr_pages; 29338c2ecf20Sopenharmony_ci 29348c2ecf20Sopenharmony_ci if (!pvt->umc) { 29358c2ecf20Sopenharmony_ci csrow_nr >>= 1; 29368c2ecf20Sopenharmony_ci cs_mode = DBAM_DIMM(csrow_nr, dbam); 29378c2ecf20Sopenharmony_ci } else { 29388c2ecf20Sopenharmony_ci cs_mode = f17_get_cs_mode(csrow_nr >> 1, dct, pvt); 29398c2ecf20Sopenharmony_ci } 29408c2ecf20Sopenharmony_ci 29418c2ecf20Sopenharmony_ci nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, csrow_nr); 29428c2ecf20Sopenharmony_ci nr_pages <<= 20 - PAGE_SHIFT; 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_ci edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n", 29458c2ecf20Sopenharmony_ci csrow_nr_orig, dct, cs_mode); 29468c2ecf20Sopenharmony_ci edac_dbg(0, "nr_pages/channel: %u\n", nr_pages); 29478c2ecf20Sopenharmony_ci 29488c2ecf20Sopenharmony_ci return nr_pages; 29498c2ecf20Sopenharmony_ci} 29508c2ecf20Sopenharmony_ci 29518c2ecf20Sopenharmony_cistatic int init_csrows_df(struct mem_ctl_info *mci) 29528c2ecf20Sopenharmony_ci{ 29538c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 29548c2ecf20Sopenharmony_ci enum edac_type edac_mode = EDAC_NONE; 29558c2ecf20Sopenharmony_ci enum dev_type dev_type = DEV_UNKNOWN; 29568c2ecf20Sopenharmony_ci struct dimm_info *dimm; 29578c2ecf20Sopenharmony_ci int empty = 1; 29588c2ecf20Sopenharmony_ci u8 umc, cs; 29598c2ecf20Sopenharmony_ci 29608c2ecf20Sopenharmony_ci if (mci->edac_ctl_cap & EDAC_FLAG_S16ECD16ED) { 29618c2ecf20Sopenharmony_ci edac_mode = EDAC_S16ECD16ED; 29628c2ecf20Sopenharmony_ci dev_type = DEV_X16; 29638c2ecf20Sopenharmony_ci } else if (mci->edac_ctl_cap & EDAC_FLAG_S8ECD8ED) { 29648c2ecf20Sopenharmony_ci edac_mode = EDAC_S8ECD8ED; 29658c2ecf20Sopenharmony_ci dev_type = DEV_X8; 29668c2ecf20Sopenharmony_ci } else if (mci->edac_ctl_cap & EDAC_FLAG_S4ECD4ED) { 29678c2ecf20Sopenharmony_ci edac_mode = EDAC_S4ECD4ED; 29688c2ecf20Sopenharmony_ci dev_type = DEV_X4; 29698c2ecf20Sopenharmony_ci } else if (mci->edac_ctl_cap & EDAC_FLAG_SECDED) { 29708c2ecf20Sopenharmony_ci edac_mode = EDAC_SECDED; 29718c2ecf20Sopenharmony_ci } 29728c2ecf20Sopenharmony_ci 29738c2ecf20Sopenharmony_ci for_each_umc(umc) { 29748c2ecf20Sopenharmony_ci for_each_chip_select(cs, umc, pvt) { 29758c2ecf20Sopenharmony_ci if (!csrow_enabled(cs, umc, pvt)) 29768c2ecf20Sopenharmony_ci continue; 29778c2ecf20Sopenharmony_ci 29788c2ecf20Sopenharmony_ci empty = 0; 29798c2ecf20Sopenharmony_ci dimm = mci->csrows[cs]->channels[umc]->dimm; 29808c2ecf20Sopenharmony_ci 29818c2ecf20Sopenharmony_ci edac_dbg(1, "MC node: %d, csrow: %d\n", 29828c2ecf20Sopenharmony_ci pvt->mc_node_id, cs); 29838c2ecf20Sopenharmony_ci 29848c2ecf20Sopenharmony_ci dimm->nr_pages = get_csrow_nr_pages(pvt, umc, cs); 29858c2ecf20Sopenharmony_ci dimm->mtype = pvt->dram_type; 29868c2ecf20Sopenharmony_ci dimm->edac_mode = edac_mode; 29878c2ecf20Sopenharmony_ci dimm->dtype = dev_type; 29888c2ecf20Sopenharmony_ci dimm->grain = 64; 29898c2ecf20Sopenharmony_ci } 29908c2ecf20Sopenharmony_ci } 29918c2ecf20Sopenharmony_ci 29928c2ecf20Sopenharmony_ci return empty; 29938c2ecf20Sopenharmony_ci} 29948c2ecf20Sopenharmony_ci 29958c2ecf20Sopenharmony_ci/* 29968c2ecf20Sopenharmony_ci * Initialize the array of csrow attribute instances, based on the values 29978c2ecf20Sopenharmony_ci * from pci config hardware registers. 29988c2ecf20Sopenharmony_ci */ 29998c2ecf20Sopenharmony_cistatic int init_csrows(struct mem_ctl_info *mci) 30008c2ecf20Sopenharmony_ci{ 30018c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 30028c2ecf20Sopenharmony_ci enum edac_type edac_mode = EDAC_NONE; 30038c2ecf20Sopenharmony_ci struct csrow_info *csrow; 30048c2ecf20Sopenharmony_ci struct dimm_info *dimm; 30058c2ecf20Sopenharmony_ci int i, j, empty = 1; 30068c2ecf20Sopenharmony_ci int nr_pages = 0; 30078c2ecf20Sopenharmony_ci u32 val; 30088c2ecf20Sopenharmony_ci 30098c2ecf20Sopenharmony_ci if (pvt->umc) 30108c2ecf20Sopenharmony_ci return init_csrows_df(mci); 30118c2ecf20Sopenharmony_ci 30128c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F3, NBCFG, &val); 30138c2ecf20Sopenharmony_ci 30148c2ecf20Sopenharmony_ci pvt->nbcfg = val; 30158c2ecf20Sopenharmony_ci 30168c2ecf20Sopenharmony_ci edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n", 30178c2ecf20Sopenharmony_ci pvt->mc_node_id, val, 30188c2ecf20Sopenharmony_ci !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE)); 30198c2ecf20Sopenharmony_ci 30208c2ecf20Sopenharmony_ci /* 30218c2ecf20Sopenharmony_ci * We iterate over DCT0 here but we look at DCT1 in parallel, if needed. 30228c2ecf20Sopenharmony_ci */ 30238c2ecf20Sopenharmony_ci for_each_chip_select(i, 0, pvt) { 30248c2ecf20Sopenharmony_ci bool row_dct0 = !!csrow_enabled(i, 0, pvt); 30258c2ecf20Sopenharmony_ci bool row_dct1 = false; 30268c2ecf20Sopenharmony_ci 30278c2ecf20Sopenharmony_ci if (pvt->fam != 0xf) 30288c2ecf20Sopenharmony_ci row_dct1 = !!csrow_enabled(i, 1, pvt); 30298c2ecf20Sopenharmony_ci 30308c2ecf20Sopenharmony_ci if (!row_dct0 && !row_dct1) 30318c2ecf20Sopenharmony_ci continue; 30328c2ecf20Sopenharmony_ci 30338c2ecf20Sopenharmony_ci csrow = mci->csrows[i]; 30348c2ecf20Sopenharmony_ci empty = 0; 30358c2ecf20Sopenharmony_ci 30368c2ecf20Sopenharmony_ci edac_dbg(1, "MC node: %d, csrow: %d\n", 30378c2ecf20Sopenharmony_ci pvt->mc_node_id, i); 30388c2ecf20Sopenharmony_ci 30398c2ecf20Sopenharmony_ci if (row_dct0) { 30408c2ecf20Sopenharmony_ci nr_pages = get_csrow_nr_pages(pvt, 0, i); 30418c2ecf20Sopenharmony_ci csrow->channels[0]->dimm->nr_pages = nr_pages; 30428c2ecf20Sopenharmony_ci } 30438c2ecf20Sopenharmony_ci 30448c2ecf20Sopenharmony_ci /* K8 has only one DCT */ 30458c2ecf20Sopenharmony_ci if (pvt->fam != 0xf && row_dct1) { 30468c2ecf20Sopenharmony_ci int row_dct1_pages = get_csrow_nr_pages(pvt, 1, i); 30478c2ecf20Sopenharmony_ci 30488c2ecf20Sopenharmony_ci csrow->channels[1]->dimm->nr_pages = row_dct1_pages; 30498c2ecf20Sopenharmony_ci nr_pages += row_dct1_pages; 30508c2ecf20Sopenharmony_ci } 30518c2ecf20Sopenharmony_ci 30528c2ecf20Sopenharmony_ci edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages); 30538c2ecf20Sopenharmony_ci 30548c2ecf20Sopenharmony_ci /* Determine DIMM ECC mode: */ 30558c2ecf20Sopenharmony_ci if (pvt->nbcfg & NBCFG_ECC_ENABLE) { 30568c2ecf20Sopenharmony_ci edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) 30578c2ecf20Sopenharmony_ci ? EDAC_S4ECD4ED 30588c2ecf20Sopenharmony_ci : EDAC_SECDED; 30598c2ecf20Sopenharmony_ci } 30608c2ecf20Sopenharmony_ci 30618c2ecf20Sopenharmony_ci for (j = 0; j < pvt->channel_count; j++) { 30628c2ecf20Sopenharmony_ci dimm = csrow->channels[j]->dimm; 30638c2ecf20Sopenharmony_ci dimm->mtype = pvt->dram_type; 30648c2ecf20Sopenharmony_ci dimm->edac_mode = edac_mode; 30658c2ecf20Sopenharmony_ci dimm->grain = 64; 30668c2ecf20Sopenharmony_ci } 30678c2ecf20Sopenharmony_ci } 30688c2ecf20Sopenharmony_ci 30698c2ecf20Sopenharmony_ci return empty; 30708c2ecf20Sopenharmony_ci} 30718c2ecf20Sopenharmony_ci 30728c2ecf20Sopenharmony_ci/* get all cores on this DCT */ 30738c2ecf20Sopenharmony_cistatic void get_cpus_on_this_dct_cpumask(struct cpumask *mask, u16 nid) 30748c2ecf20Sopenharmony_ci{ 30758c2ecf20Sopenharmony_ci int cpu; 30768c2ecf20Sopenharmony_ci 30778c2ecf20Sopenharmony_ci for_each_online_cpu(cpu) 30788c2ecf20Sopenharmony_ci if (amd_get_nb_id(cpu) == nid) 30798c2ecf20Sopenharmony_ci cpumask_set_cpu(cpu, mask); 30808c2ecf20Sopenharmony_ci} 30818c2ecf20Sopenharmony_ci 30828c2ecf20Sopenharmony_ci/* check MCG_CTL on all the cpus on this node */ 30838c2ecf20Sopenharmony_cistatic bool nb_mce_bank_enabled_on_node(u16 nid) 30848c2ecf20Sopenharmony_ci{ 30858c2ecf20Sopenharmony_ci cpumask_var_t mask; 30868c2ecf20Sopenharmony_ci int cpu, nbe; 30878c2ecf20Sopenharmony_ci bool ret = false; 30888c2ecf20Sopenharmony_ci 30898c2ecf20Sopenharmony_ci if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) { 30908c2ecf20Sopenharmony_ci amd64_warn("%s: Error allocating mask\n", __func__); 30918c2ecf20Sopenharmony_ci return false; 30928c2ecf20Sopenharmony_ci } 30938c2ecf20Sopenharmony_ci 30948c2ecf20Sopenharmony_ci get_cpus_on_this_dct_cpumask(mask, nid); 30958c2ecf20Sopenharmony_ci 30968c2ecf20Sopenharmony_ci rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs); 30978c2ecf20Sopenharmony_ci 30988c2ecf20Sopenharmony_ci for_each_cpu(cpu, mask) { 30998c2ecf20Sopenharmony_ci struct msr *reg = per_cpu_ptr(msrs, cpu); 31008c2ecf20Sopenharmony_ci nbe = reg->l & MSR_MCGCTL_NBE; 31018c2ecf20Sopenharmony_ci 31028c2ecf20Sopenharmony_ci edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n", 31038c2ecf20Sopenharmony_ci cpu, reg->q, 31048c2ecf20Sopenharmony_ci (nbe ? "enabled" : "disabled")); 31058c2ecf20Sopenharmony_ci 31068c2ecf20Sopenharmony_ci if (!nbe) 31078c2ecf20Sopenharmony_ci goto out; 31088c2ecf20Sopenharmony_ci } 31098c2ecf20Sopenharmony_ci ret = true; 31108c2ecf20Sopenharmony_ci 31118c2ecf20Sopenharmony_ciout: 31128c2ecf20Sopenharmony_ci free_cpumask_var(mask); 31138c2ecf20Sopenharmony_ci return ret; 31148c2ecf20Sopenharmony_ci} 31158c2ecf20Sopenharmony_ci 31168c2ecf20Sopenharmony_cistatic int toggle_ecc_err_reporting(struct ecc_settings *s, u16 nid, bool on) 31178c2ecf20Sopenharmony_ci{ 31188c2ecf20Sopenharmony_ci cpumask_var_t cmask; 31198c2ecf20Sopenharmony_ci int cpu; 31208c2ecf20Sopenharmony_ci 31218c2ecf20Sopenharmony_ci if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) { 31228c2ecf20Sopenharmony_ci amd64_warn("%s: error allocating mask\n", __func__); 31238c2ecf20Sopenharmony_ci return -ENOMEM; 31248c2ecf20Sopenharmony_ci } 31258c2ecf20Sopenharmony_ci 31268c2ecf20Sopenharmony_ci get_cpus_on_this_dct_cpumask(cmask, nid); 31278c2ecf20Sopenharmony_ci 31288c2ecf20Sopenharmony_ci rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); 31298c2ecf20Sopenharmony_ci 31308c2ecf20Sopenharmony_ci for_each_cpu(cpu, cmask) { 31318c2ecf20Sopenharmony_ci 31328c2ecf20Sopenharmony_ci struct msr *reg = per_cpu_ptr(msrs, cpu); 31338c2ecf20Sopenharmony_ci 31348c2ecf20Sopenharmony_ci if (on) { 31358c2ecf20Sopenharmony_ci if (reg->l & MSR_MCGCTL_NBE) 31368c2ecf20Sopenharmony_ci s->flags.nb_mce_enable = 1; 31378c2ecf20Sopenharmony_ci 31388c2ecf20Sopenharmony_ci reg->l |= MSR_MCGCTL_NBE; 31398c2ecf20Sopenharmony_ci } else { 31408c2ecf20Sopenharmony_ci /* 31418c2ecf20Sopenharmony_ci * Turn off NB MCE reporting only when it was off before 31428c2ecf20Sopenharmony_ci */ 31438c2ecf20Sopenharmony_ci if (!s->flags.nb_mce_enable) 31448c2ecf20Sopenharmony_ci reg->l &= ~MSR_MCGCTL_NBE; 31458c2ecf20Sopenharmony_ci } 31468c2ecf20Sopenharmony_ci } 31478c2ecf20Sopenharmony_ci wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); 31488c2ecf20Sopenharmony_ci 31498c2ecf20Sopenharmony_ci free_cpumask_var(cmask); 31508c2ecf20Sopenharmony_ci 31518c2ecf20Sopenharmony_ci return 0; 31528c2ecf20Sopenharmony_ci} 31538c2ecf20Sopenharmony_ci 31548c2ecf20Sopenharmony_cistatic bool enable_ecc_error_reporting(struct ecc_settings *s, u16 nid, 31558c2ecf20Sopenharmony_ci struct pci_dev *F3) 31568c2ecf20Sopenharmony_ci{ 31578c2ecf20Sopenharmony_ci bool ret = true; 31588c2ecf20Sopenharmony_ci u32 value, mask = 0x3; /* UECC/CECC enable */ 31598c2ecf20Sopenharmony_ci 31608c2ecf20Sopenharmony_ci if (toggle_ecc_err_reporting(s, nid, ON)) { 31618c2ecf20Sopenharmony_ci amd64_warn("Error enabling ECC reporting over MCGCTL!\n"); 31628c2ecf20Sopenharmony_ci return false; 31638c2ecf20Sopenharmony_ci } 31648c2ecf20Sopenharmony_ci 31658c2ecf20Sopenharmony_ci amd64_read_pci_cfg(F3, NBCTL, &value); 31668c2ecf20Sopenharmony_ci 31678c2ecf20Sopenharmony_ci s->old_nbctl = value & mask; 31688c2ecf20Sopenharmony_ci s->nbctl_valid = true; 31698c2ecf20Sopenharmony_ci 31708c2ecf20Sopenharmony_ci value |= mask; 31718c2ecf20Sopenharmony_ci amd64_write_pci_cfg(F3, NBCTL, value); 31728c2ecf20Sopenharmony_ci 31738c2ecf20Sopenharmony_ci amd64_read_pci_cfg(F3, NBCFG, &value); 31748c2ecf20Sopenharmony_ci 31758c2ecf20Sopenharmony_ci edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n", 31768c2ecf20Sopenharmony_ci nid, value, !!(value & NBCFG_ECC_ENABLE)); 31778c2ecf20Sopenharmony_ci 31788c2ecf20Sopenharmony_ci if (!(value & NBCFG_ECC_ENABLE)) { 31798c2ecf20Sopenharmony_ci amd64_warn("DRAM ECC disabled on this node, enabling...\n"); 31808c2ecf20Sopenharmony_ci 31818c2ecf20Sopenharmony_ci s->flags.nb_ecc_prev = 0; 31828c2ecf20Sopenharmony_ci 31838c2ecf20Sopenharmony_ci /* Attempt to turn on DRAM ECC Enable */ 31848c2ecf20Sopenharmony_ci value |= NBCFG_ECC_ENABLE; 31858c2ecf20Sopenharmony_ci amd64_write_pci_cfg(F3, NBCFG, value); 31868c2ecf20Sopenharmony_ci 31878c2ecf20Sopenharmony_ci amd64_read_pci_cfg(F3, NBCFG, &value); 31888c2ecf20Sopenharmony_ci 31898c2ecf20Sopenharmony_ci if (!(value & NBCFG_ECC_ENABLE)) { 31908c2ecf20Sopenharmony_ci amd64_warn("Hardware rejected DRAM ECC enable," 31918c2ecf20Sopenharmony_ci "check memory DIMM configuration.\n"); 31928c2ecf20Sopenharmony_ci ret = false; 31938c2ecf20Sopenharmony_ci } else { 31948c2ecf20Sopenharmony_ci amd64_info("Hardware accepted DRAM ECC Enable\n"); 31958c2ecf20Sopenharmony_ci } 31968c2ecf20Sopenharmony_ci } else { 31978c2ecf20Sopenharmony_ci s->flags.nb_ecc_prev = 1; 31988c2ecf20Sopenharmony_ci } 31998c2ecf20Sopenharmony_ci 32008c2ecf20Sopenharmony_ci edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n", 32018c2ecf20Sopenharmony_ci nid, value, !!(value & NBCFG_ECC_ENABLE)); 32028c2ecf20Sopenharmony_ci 32038c2ecf20Sopenharmony_ci return ret; 32048c2ecf20Sopenharmony_ci} 32058c2ecf20Sopenharmony_ci 32068c2ecf20Sopenharmony_cistatic void restore_ecc_error_reporting(struct ecc_settings *s, u16 nid, 32078c2ecf20Sopenharmony_ci struct pci_dev *F3) 32088c2ecf20Sopenharmony_ci{ 32098c2ecf20Sopenharmony_ci u32 value, mask = 0x3; /* UECC/CECC enable */ 32108c2ecf20Sopenharmony_ci 32118c2ecf20Sopenharmony_ci if (!s->nbctl_valid) 32128c2ecf20Sopenharmony_ci return; 32138c2ecf20Sopenharmony_ci 32148c2ecf20Sopenharmony_ci amd64_read_pci_cfg(F3, NBCTL, &value); 32158c2ecf20Sopenharmony_ci value &= ~mask; 32168c2ecf20Sopenharmony_ci value |= s->old_nbctl; 32178c2ecf20Sopenharmony_ci 32188c2ecf20Sopenharmony_ci amd64_write_pci_cfg(F3, NBCTL, value); 32198c2ecf20Sopenharmony_ci 32208c2ecf20Sopenharmony_ci /* restore previous BIOS DRAM ECC "off" setting we force-enabled */ 32218c2ecf20Sopenharmony_ci if (!s->flags.nb_ecc_prev) { 32228c2ecf20Sopenharmony_ci amd64_read_pci_cfg(F3, NBCFG, &value); 32238c2ecf20Sopenharmony_ci value &= ~NBCFG_ECC_ENABLE; 32248c2ecf20Sopenharmony_ci amd64_write_pci_cfg(F3, NBCFG, value); 32258c2ecf20Sopenharmony_ci } 32268c2ecf20Sopenharmony_ci 32278c2ecf20Sopenharmony_ci /* restore the NB Enable MCGCTL bit */ 32288c2ecf20Sopenharmony_ci if (toggle_ecc_err_reporting(s, nid, OFF)) 32298c2ecf20Sopenharmony_ci amd64_warn("Error restoring NB MCGCTL settings!\n"); 32308c2ecf20Sopenharmony_ci} 32318c2ecf20Sopenharmony_ci 32328c2ecf20Sopenharmony_cistatic bool ecc_enabled(struct amd64_pvt *pvt) 32338c2ecf20Sopenharmony_ci{ 32348c2ecf20Sopenharmony_ci u16 nid = pvt->mc_node_id; 32358c2ecf20Sopenharmony_ci bool nb_mce_en = false; 32368c2ecf20Sopenharmony_ci u8 ecc_en = 0, i; 32378c2ecf20Sopenharmony_ci u32 value; 32388c2ecf20Sopenharmony_ci 32398c2ecf20Sopenharmony_ci if (boot_cpu_data.x86 >= 0x17) { 32408c2ecf20Sopenharmony_ci u8 umc_en_mask = 0, ecc_en_mask = 0; 32418c2ecf20Sopenharmony_ci struct amd64_umc *umc; 32428c2ecf20Sopenharmony_ci 32438c2ecf20Sopenharmony_ci for_each_umc(i) { 32448c2ecf20Sopenharmony_ci umc = &pvt->umc[i]; 32458c2ecf20Sopenharmony_ci 32468c2ecf20Sopenharmony_ci /* Only check enabled UMCs. */ 32478c2ecf20Sopenharmony_ci if (!(umc->sdp_ctrl & UMC_SDP_INIT)) 32488c2ecf20Sopenharmony_ci continue; 32498c2ecf20Sopenharmony_ci 32508c2ecf20Sopenharmony_ci umc_en_mask |= BIT(i); 32518c2ecf20Sopenharmony_ci 32528c2ecf20Sopenharmony_ci if (umc->umc_cap_hi & UMC_ECC_ENABLED) 32538c2ecf20Sopenharmony_ci ecc_en_mask |= BIT(i); 32548c2ecf20Sopenharmony_ci } 32558c2ecf20Sopenharmony_ci 32568c2ecf20Sopenharmony_ci /* Check whether at least one UMC is enabled: */ 32578c2ecf20Sopenharmony_ci if (umc_en_mask) 32588c2ecf20Sopenharmony_ci ecc_en = umc_en_mask == ecc_en_mask; 32598c2ecf20Sopenharmony_ci else 32608c2ecf20Sopenharmony_ci edac_dbg(0, "Node %d: No enabled UMCs.\n", nid); 32618c2ecf20Sopenharmony_ci 32628c2ecf20Sopenharmony_ci /* Assume UMC MCA banks are enabled. */ 32638c2ecf20Sopenharmony_ci nb_mce_en = true; 32648c2ecf20Sopenharmony_ci } else { 32658c2ecf20Sopenharmony_ci amd64_read_pci_cfg(pvt->F3, NBCFG, &value); 32668c2ecf20Sopenharmony_ci 32678c2ecf20Sopenharmony_ci ecc_en = !!(value & NBCFG_ECC_ENABLE); 32688c2ecf20Sopenharmony_ci 32698c2ecf20Sopenharmony_ci nb_mce_en = nb_mce_bank_enabled_on_node(nid); 32708c2ecf20Sopenharmony_ci if (!nb_mce_en) 32718c2ecf20Sopenharmony_ci edac_dbg(0, "NB MCE bank disabled, set MSR 0x%08x[4] on node %d to enable.\n", 32728c2ecf20Sopenharmony_ci MSR_IA32_MCG_CTL, nid); 32738c2ecf20Sopenharmony_ci } 32748c2ecf20Sopenharmony_ci 32758c2ecf20Sopenharmony_ci amd64_info("Node %d: DRAM ECC %s.\n", 32768c2ecf20Sopenharmony_ci nid, (ecc_en ? "enabled" : "disabled")); 32778c2ecf20Sopenharmony_ci 32788c2ecf20Sopenharmony_ci if (!ecc_en || !nb_mce_en) 32798c2ecf20Sopenharmony_ci return false; 32808c2ecf20Sopenharmony_ci else 32818c2ecf20Sopenharmony_ci return true; 32828c2ecf20Sopenharmony_ci} 32838c2ecf20Sopenharmony_ci 32848c2ecf20Sopenharmony_cistatic inline void 32858c2ecf20Sopenharmony_cif17h_determine_edac_ctl_cap(struct mem_ctl_info *mci, struct amd64_pvt *pvt) 32868c2ecf20Sopenharmony_ci{ 32878c2ecf20Sopenharmony_ci u8 i, ecc_en = 1, cpk_en = 1, dev_x4 = 1, dev_x16 = 1; 32888c2ecf20Sopenharmony_ci 32898c2ecf20Sopenharmony_ci for_each_umc(i) { 32908c2ecf20Sopenharmony_ci if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) { 32918c2ecf20Sopenharmony_ci ecc_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_ENABLED); 32928c2ecf20Sopenharmony_ci cpk_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_CHIPKILL_CAP); 32938c2ecf20Sopenharmony_ci 32948c2ecf20Sopenharmony_ci dev_x4 &= !!(pvt->umc[i].dimm_cfg & BIT(6)); 32958c2ecf20Sopenharmony_ci dev_x16 &= !!(pvt->umc[i].dimm_cfg & BIT(7)); 32968c2ecf20Sopenharmony_ci } 32978c2ecf20Sopenharmony_ci } 32988c2ecf20Sopenharmony_ci 32998c2ecf20Sopenharmony_ci /* Set chipkill only if ECC is enabled: */ 33008c2ecf20Sopenharmony_ci if (ecc_en) { 33018c2ecf20Sopenharmony_ci mci->edac_ctl_cap |= EDAC_FLAG_SECDED; 33028c2ecf20Sopenharmony_ci 33038c2ecf20Sopenharmony_ci if (!cpk_en) 33048c2ecf20Sopenharmony_ci return; 33058c2ecf20Sopenharmony_ci 33068c2ecf20Sopenharmony_ci if (dev_x4) 33078c2ecf20Sopenharmony_ci mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED; 33088c2ecf20Sopenharmony_ci else if (dev_x16) 33098c2ecf20Sopenharmony_ci mci->edac_ctl_cap |= EDAC_FLAG_S16ECD16ED; 33108c2ecf20Sopenharmony_ci else 33118c2ecf20Sopenharmony_ci mci->edac_ctl_cap |= EDAC_FLAG_S8ECD8ED; 33128c2ecf20Sopenharmony_ci } 33138c2ecf20Sopenharmony_ci} 33148c2ecf20Sopenharmony_ci 33158c2ecf20Sopenharmony_cistatic void setup_mci_misc_attrs(struct mem_ctl_info *mci) 33168c2ecf20Sopenharmony_ci{ 33178c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = mci->pvt_info; 33188c2ecf20Sopenharmony_ci 33198c2ecf20Sopenharmony_ci mci->mtype_cap = MEM_FLAG_DDR2 | MEM_FLAG_RDDR2; 33208c2ecf20Sopenharmony_ci mci->edac_ctl_cap = EDAC_FLAG_NONE; 33218c2ecf20Sopenharmony_ci 33228c2ecf20Sopenharmony_ci if (pvt->umc) { 33238c2ecf20Sopenharmony_ci f17h_determine_edac_ctl_cap(mci, pvt); 33248c2ecf20Sopenharmony_ci } else { 33258c2ecf20Sopenharmony_ci if (pvt->nbcap & NBCAP_SECDED) 33268c2ecf20Sopenharmony_ci mci->edac_ctl_cap |= EDAC_FLAG_SECDED; 33278c2ecf20Sopenharmony_ci 33288c2ecf20Sopenharmony_ci if (pvt->nbcap & NBCAP_CHIPKILL) 33298c2ecf20Sopenharmony_ci mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED; 33308c2ecf20Sopenharmony_ci } 33318c2ecf20Sopenharmony_ci 33328c2ecf20Sopenharmony_ci mci->edac_cap = determine_edac_cap(pvt); 33338c2ecf20Sopenharmony_ci mci->mod_name = EDAC_MOD_STR; 33348c2ecf20Sopenharmony_ci mci->ctl_name = fam_type->ctl_name; 33358c2ecf20Sopenharmony_ci mci->dev_name = pci_name(pvt->F3); 33368c2ecf20Sopenharmony_ci mci->ctl_page_to_phys = NULL; 33378c2ecf20Sopenharmony_ci 33388c2ecf20Sopenharmony_ci /* memory scrubber interface */ 33398c2ecf20Sopenharmony_ci mci->set_sdram_scrub_rate = set_scrub_rate; 33408c2ecf20Sopenharmony_ci mci->get_sdram_scrub_rate = get_scrub_rate; 33418c2ecf20Sopenharmony_ci} 33428c2ecf20Sopenharmony_ci 33438c2ecf20Sopenharmony_ci/* 33448c2ecf20Sopenharmony_ci * returns a pointer to the family descriptor on success, NULL otherwise. 33458c2ecf20Sopenharmony_ci */ 33468c2ecf20Sopenharmony_cistatic struct amd64_family_type *per_family_init(struct amd64_pvt *pvt) 33478c2ecf20Sopenharmony_ci{ 33488c2ecf20Sopenharmony_ci pvt->ext_model = boot_cpu_data.x86_model >> 4; 33498c2ecf20Sopenharmony_ci pvt->stepping = boot_cpu_data.x86_stepping; 33508c2ecf20Sopenharmony_ci pvt->model = boot_cpu_data.x86_model; 33518c2ecf20Sopenharmony_ci pvt->fam = boot_cpu_data.x86; 33528c2ecf20Sopenharmony_ci 33538c2ecf20Sopenharmony_ci switch (pvt->fam) { 33548c2ecf20Sopenharmony_ci case 0xf: 33558c2ecf20Sopenharmony_ci fam_type = &family_types[K8_CPUS]; 33568c2ecf20Sopenharmony_ci pvt->ops = &family_types[K8_CPUS].ops; 33578c2ecf20Sopenharmony_ci break; 33588c2ecf20Sopenharmony_ci 33598c2ecf20Sopenharmony_ci case 0x10: 33608c2ecf20Sopenharmony_ci fam_type = &family_types[F10_CPUS]; 33618c2ecf20Sopenharmony_ci pvt->ops = &family_types[F10_CPUS].ops; 33628c2ecf20Sopenharmony_ci break; 33638c2ecf20Sopenharmony_ci 33648c2ecf20Sopenharmony_ci case 0x15: 33658c2ecf20Sopenharmony_ci if (pvt->model == 0x30) { 33668c2ecf20Sopenharmony_ci fam_type = &family_types[F15_M30H_CPUS]; 33678c2ecf20Sopenharmony_ci pvt->ops = &family_types[F15_M30H_CPUS].ops; 33688c2ecf20Sopenharmony_ci break; 33698c2ecf20Sopenharmony_ci } else if (pvt->model == 0x60) { 33708c2ecf20Sopenharmony_ci fam_type = &family_types[F15_M60H_CPUS]; 33718c2ecf20Sopenharmony_ci pvt->ops = &family_types[F15_M60H_CPUS].ops; 33728c2ecf20Sopenharmony_ci break; 33738c2ecf20Sopenharmony_ci /* Richland is only client */ 33748c2ecf20Sopenharmony_ci } else if (pvt->model == 0x13) { 33758c2ecf20Sopenharmony_ci return NULL; 33768c2ecf20Sopenharmony_ci } else { 33778c2ecf20Sopenharmony_ci fam_type = &family_types[F15_CPUS]; 33788c2ecf20Sopenharmony_ci pvt->ops = &family_types[F15_CPUS].ops; 33798c2ecf20Sopenharmony_ci } 33808c2ecf20Sopenharmony_ci break; 33818c2ecf20Sopenharmony_ci 33828c2ecf20Sopenharmony_ci case 0x16: 33838c2ecf20Sopenharmony_ci if (pvt->model == 0x30) { 33848c2ecf20Sopenharmony_ci fam_type = &family_types[F16_M30H_CPUS]; 33858c2ecf20Sopenharmony_ci pvt->ops = &family_types[F16_M30H_CPUS].ops; 33868c2ecf20Sopenharmony_ci break; 33878c2ecf20Sopenharmony_ci } 33888c2ecf20Sopenharmony_ci fam_type = &family_types[F16_CPUS]; 33898c2ecf20Sopenharmony_ci pvt->ops = &family_types[F16_CPUS].ops; 33908c2ecf20Sopenharmony_ci break; 33918c2ecf20Sopenharmony_ci 33928c2ecf20Sopenharmony_ci case 0x17: 33938c2ecf20Sopenharmony_ci if (pvt->model >= 0x10 && pvt->model <= 0x2f) { 33948c2ecf20Sopenharmony_ci fam_type = &family_types[F17_M10H_CPUS]; 33958c2ecf20Sopenharmony_ci pvt->ops = &family_types[F17_M10H_CPUS].ops; 33968c2ecf20Sopenharmony_ci break; 33978c2ecf20Sopenharmony_ci } else if (pvt->model >= 0x30 && pvt->model <= 0x3f) { 33988c2ecf20Sopenharmony_ci fam_type = &family_types[F17_M30H_CPUS]; 33998c2ecf20Sopenharmony_ci pvt->ops = &family_types[F17_M30H_CPUS].ops; 34008c2ecf20Sopenharmony_ci break; 34018c2ecf20Sopenharmony_ci } else if (pvt->model >= 0x60 && pvt->model <= 0x6f) { 34028c2ecf20Sopenharmony_ci fam_type = &family_types[F17_M60H_CPUS]; 34038c2ecf20Sopenharmony_ci pvt->ops = &family_types[F17_M60H_CPUS].ops; 34048c2ecf20Sopenharmony_ci break; 34058c2ecf20Sopenharmony_ci } else if (pvt->model >= 0x70 && pvt->model <= 0x7f) { 34068c2ecf20Sopenharmony_ci fam_type = &family_types[F17_M70H_CPUS]; 34078c2ecf20Sopenharmony_ci pvt->ops = &family_types[F17_M70H_CPUS].ops; 34088c2ecf20Sopenharmony_ci break; 34098c2ecf20Sopenharmony_ci } 34108c2ecf20Sopenharmony_ci fallthrough; 34118c2ecf20Sopenharmony_ci case 0x18: 34128c2ecf20Sopenharmony_ci fam_type = &family_types[F17_CPUS]; 34138c2ecf20Sopenharmony_ci pvt->ops = &family_types[F17_CPUS].ops; 34148c2ecf20Sopenharmony_ci 34158c2ecf20Sopenharmony_ci if (pvt->fam == 0x18) 34168c2ecf20Sopenharmony_ci family_types[F17_CPUS].ctl_name = "F18h"; 34178c2ecf20Sopenharmony_ci break; 34188c2ecf20Sopenharmony_ci 34198c2ecf20Sopenharmony_ci case 0x19: 34208c2ecf20Sopenharmony_ci if (pvt->model >= 0x20 && pvt->model <= 0x2f) { 34218c2ecf20Sopenharmony_ci fam_type = &family_types[F17_M70H_CPUS]; 34228c2ecf20Sopenharmony_ci pvt->ops = &family_types[F17_M70H_CPUS].ops; 34238c2ecf20Sopenharmony_ci fam_type->ctl_name = "F19h_M20h"; 34248c2ecf20Sopenharmony_ci break; 34258c2ecf20Sopenharmony_ci } 34268c2ecf20Sopenharmony_ci fam_type = &family_types[F19_CPUS]; 34278c2ecf20Sopenharmony_ci pvt->ops = &family_types[F19_CPUS].ops; 34288c2ecf20Sopenharmony_ci family_types[F19_CPUS].ctl_name = "F19h"; 34298c2ecf20Sopenharmony_ci break; 34308c2ecf20Sopenharmony_ci 34318c2ecf20Sopenharmony_ci default: 34328c2ecf20Sopenharmony_ci amd64_err("Unsupported family!\n"); 34338c2ecf20Sopenharmony_ci return NULL; 34348c2ecf20Sopenharmony_ci } 34358c2ecf20Sopenharmony_ci 34368c2ecf20Sopenharmony_ci amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name, 34378c2ecf20Sopenharmony_ci (pvt->fam == 0xf ? 34388c2ecf20Sopenharmony_ci (pvt->ext_model >= K8_REV_F ? "revF or later " 34398c2ecf20Sopenharmony_ci : "revE or earlier ") 34408c2ecf20Sopenharmony_ci : ""), pvt->mc_node_id); 34418c2ecf20Sopenharmony_ci return fam_type; 34428c2ecf20Sopenharmony_ci} 34438c2ecf20Sopenharmony_ci 34448c2ecf20Sopenharmony_cistatic const struct attribute_group *amd64_edac_attr_groups[] = { 34458c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG 34468c2ecf20Sopenharmony_ci &amd64_edac_dbg_group, 34478c2ecf20Sopenharmony_ci#endif 34488c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION 34498c2ecf20Sopenharmony_ci &amd64_edac_inj_group, 34508c2ecf20Sopenharmony_ci#endif 34518c2ecf20Sopenharmony_ci NULL 34528c2ecf20Sopenharmony_ci}; 34538c2ecf20Sopenharmony_ci 34548c2ecf20Sopenharmony_cistatic int hw_info_get(struct amd64_pvt *pvt) 34558c2ecf20Sopenharmony_ci{ 34568c2ecf20Sopenharmony_ci u16 pci_id1, pci_id2; 34578c2ecf20Sopenharmony_ci int ret; 34588c2ecf20Sopenharmony_ci 34598c2ecf20Sopenharmony_ci if (pvt->fam >= 0x17) { 34608c2ecf20Sopenharmony_ci pvt->umc = kcalloc(fam_type->max_mcs, sizeof(struct amd64_umc), GFP_KERNEL); 34618c2ecf20Sopenharmony_ci if (!pvt->umc) 34628c2ecf20Sopenharmony_ci return -ENOMEM; 34638c2ecf20Sopenharmony_ci 34648c2ecf20Sopenharmony_ci pci_id1 = fam_type->f0_id; 34658c2ecf20Sopenharmony_ci pci_id2 = fam_type->f6_id; 34668c2ecf20Sopenharmony_ci } else { 34678c2ecf20Sopenharmony_ci pci_id1 = fam_type->f1_id; 34688c2ecf20Sopenharmony_ci pci_id2 = fam_type->f2_id; 34698c2ecf20Sopenharmony_ci } 34708c2ecf20Sopenharmony_ci 34718c2ecf20Sopenharmony_ci ret = reserve_mc_sibling_devs(pvt, pci_id1, pci_id2); 34728c2ecf20Sopenharmony_ci if (ret) 34738c2ecf20Sopenharmony_ci return ret; 34748c2ecf20Sopenharmony_ci 34758c2ecf20Sopenharmony_ci read_mc_regs(pvt); 34768c2ecf20Sopenharmony_ci 34778c2ecf20Sopenharmony_ci return 0; 34788c2ecf20Sopenharmony_ci} 34798c2ecf20Sopenharmony_ci 34808c2ecf20Sopenharmony_cistatic void hw_info_put(struct amd64_pvt *pvt) 34818c2ecf20Sopenharmony_ci{ 34828c2ecf20Sopenharmony_ci if (pvt->F0 || pvt->F1) 34838c2ecf20Sopenharmony_ci free_mc_sibling_devs(pvt); 34848c2ecf20Sopenharmony_ci 34858c2ecf20Sopenharmony_ci kfree(pvt->umc); 34868c2ecf20Sopenharmony_ci} 34878c2ecf20Sopenharmony_ci 34888c2ecf20Sopenharmony_cistatic int init_one_instance(struct amd64_pvt *pvt) 34898c2ecf20Sopenharmony_ci{ 34908c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = NULL; 34918c2ecf20Sopenharmony_ci struct edac_mc_layer layers[2]; 34928c2ecf20Sopenharmony_ci int ret = -EINVAL; 34938c2ecf20Sopenharmony_ci 34948c2ecf20Sopenharmony_ci /* 34958c2ecf20Sopenharmony_ci * We need to determine how many memory channels there are. Then use 34968c2ecf20Sopenharmony_ci * that information for calculating the size of the dynamic instance 34978c2ecf20Sopenharmony_ci * tables in the 'mci' structure. 34988c2ecf20Sopenharmony_ci */ 34998c2ecf20Sopenharmony_ci pvt->channel_count = pvt->ops->early_channel_count(pvt); 35008c2ecf20Sopenharmony_ci if (pvt->channel_count < 0) 35018c2ecf20Sopenharmony_ci return ret; 35028c2ecf20Sopenharmony_ci 35038c2ecf20Sopenharmony_ci ret = -ENOMEM; 35048c2ecf20Sopenharmony_ci layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 35058c2ecf20Sopenharmony_ci layers[0].size = pvt->csels[0].b_cnt; 35068c2ecf20Sopenharmony_ci layers[0].is_virt_csrow = true; 35078c2ecf20Sopenharmony_ci layers[1].type = EDAC_MC_LAYER_CHANNEL; 35088c2ecf20Sopenharmony_ci 35098c2ecf20Sopenharmony_ci /* 35108c2ecf20Sopenharmony_ci * Always allocate two channels since we can have setups with DIMMs on 35118c2ecf20Sopenharmony_ci * only one channel. Also, this simplifies handling later for the price 35128c2ecf20Sopenharmony_ci * of a couple of KBs tops. 35138c2ecf20Sopenharmony_ci */ 35148c2ecf20Sopenharmony_ci layers[1].size = fam_type->max_mcs; 35158c2ecf20Sopenharmony_ci layers[1].is_virt_csrow = false; 35168c2ecf20Sopenharmony_ci 35178c2ecf20Sopenharmony_ci mci = edac_mc_alloc(pvt->mc_node_id, ARRAY_SIZE(layers), layers, 0); 35188c2ecf20Sopenharmony_ci if (!mci) 35198c2ecf20Sopenharmony_ci return ret; 35208c2ecf20Sopenharmony_ci 35218c2ecf20Sopenharmony_ci mci->pvt_info = pvt; 35228c2ecf20Sopenharmony_ci mci->pdev = &pvt->F3->dev; 35238c2ecf20Sopenharmony_ci 35248c2ecf20Sopenharmony_ci setup_mci_misc_attrs(mci); 35258c2ecf20Sopenharmony_ci 35268c2ecf20Sopenharmony_ci if (init_csrows(mci)) 35278c2ecf20Sopenharmony_ci mci->edac_cap = EDAC_FLAG_NONE; 35288c2ecf20Sopenharmony_ci 35298c2ecf20Sopenharmony_ci ret = -ENODEV; 35308c2ecf20Sopenharmony_ci if (edac_mc_add_mc_with_groups(mci, amd64_edac_attr_groups)) { 35318c2ecf20Sopenharmony_ci edac_dbg(1, "failed edac_mc_add_mc()\n"); 35328c2ecf20Sopenharmony_ci edac_mc_free(mci); 35338c2ecf20Sopenharmony_ci return ret; 35348c2ecf20Sopenharmony_ci } 35358c2ecf20Sopenharmony_ci 35368c2ecf20Sopenharmony_ci return 0; 35378c2ecf20Sopenharmony_ci} 35388c2ecf20Sopenharmony_ci 35398c2ecf20Sopenharmony_cistatic bool instance_has_memory(struct amd64_pvt *pvt) 35408c2ecf20Sopenharmony_ci{ 35418c2ecf20Sopenharmony_ci bool cs_enabled = false; 35428c2ecf20Sopenharmony_ci int cs = 0, dct = 0; 35438c2ecf20Sopenharmony_ci 35448c2ecf20Sopenharmony_ci for (dct = 0; dct < fam_type->max_mcs; dct++) { 35458c2ecf20Sopenharmony_ci for_each_chip_select(cs, dct, pvt) 35468c2ecf20Sopenharmony_ci cs_enabled |= csrow_enabled(cs, dct, pvt); 35478c2ecf20Sopenharmony_ci } 35488c2ecf20Sopenharmony_ci 35498c2ecf20Sopenharmony_ci return cs_enabled; 35508c2ecf20Sopenharmony_ci} 35518c2ecf20Sopenharmony_ci 35528c2ecf20Sopenharmony_cistatic int probe_one_instance(unsigned int nid) 35538c2ecf20Sopenharmony_ci{ 35548c2ecf20Sopenharmony_ci struct pci_dev *F3 = node_to_amd_nb(nid)->misc; 35558c2ecf20Sopenharmony_ci struct amd64_pvt *pvt = NULL; 35568c2ecf20Sopenharmony_ci struct ecc_settings *s; 35578c2ecf20Sopenharmony_ci int ret; 35588c2ecf20Sopenharmony_ci 35598c2ecf20Sopenharmony_ci ret = -ENOMEM; 35608c2ecf20Sopenharmony_ci s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL); 35618c2ecf20Sopenharmony_ci if (!s) 35628c2ecf20Sopenharmony_ci goto err_out; 35638c2ecf20Sopenharmony_ci 35648c2ecf20Sopenharmony_ci ecc_stngs[nid] = s; 35658c2ecf20Sopenharmony_ci 35668c2ecf20Sopenharmony_ci pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL); 35678c2ecf20Sopenharmony_ci if (!pvt) 35688c2ecf20Sopenharmony_ci goto err_settings; 35698c2ecf20Sopenharmony_ci 35708c2ecf20Sopenharmony_ci pvt->mc_node_id = nid; 35718c2ecf20Sopenharmony_ci pvt->F3 = F3; 35728c2ecf20Sopenharmony_ci 35738c2ecf20Sopenharmony_ci ret = -ENODEV; 35748c2ecf20Sopenharmony_ci fam_type = per_family_init(pvt); 35758c2ecf20Sopenharmony_ci if (!fam_type) 35768c2ecf20Sopenharmony_ci goto err_enable; 35778c2ecf20Sopenharmony_ci 35788c2ecf20Sopenharmony_ci ret = hw_info_get(pvt); 35798c2ecf20Sopenharmony_ci if (ret < 0) 35808c2ecf20Sopenharmony_ci goto err_enable; 35818c2ecf20Sopenharmony_ci 35828c2ecf20Sopenharmony_ci ret = 0; 35838c2ecf20Sopenharmony_ci if (!instance_has_memory(pvt)) { 35848c2ecf20Sopenharmony_ci amd64_info("Node %d: No DIMMs detected.\n", nid); 35858c2ecf20Sopenharmony_ci goto err_enable; 35868c2ecf20Sopenharmony_ci } 35878c2ecf20Sopenharmony_ci 35888c2ecf20Sopenharmony_ci if (!ecc_enabled(pvt)) { 35898c2ecf20Sopenharmony_ci ret = -ENODEV; 35908c2ecf20Sopenharmony_ci 35918c2ecf20Sopenharmony_ci if (!ecc_enable_override) 35928c2ecf20Sopenharmony_ci goto err_enable; 35938c2ecf20Sopenharmony_ci 35948c2ecf20Sopenharmony_ci if (boot_cpu_data.x86 >= 0x17) { 35958c2ecf20Sopenharmony_ci amd64_warn("Forcing ECC on is not recommended on newer systems. Please enable ECC in BIOS."); 35968c2ecf20Sopenharmony_ci goto err_enable; 35978c2ecf20Sopenharmony_ci } else 35988c2ecf20Sopenharmony_ci amd64_warn("Forcing ECC on!\n"); 35998c2ecf20Sopenharmony_ci 36008c2ecf20Sopenharmony_ci if (!enable_ecc_error_reporting(s, nid, F3)) 36018c2ecf20Sopenharmony_ci goto err_enable; 36028c2ecf20Sopenharmony_ci } 36038c2ecf20Sopenharmony_ci 36048c2ecf20Sopenharmony_ci ret = init_one_instance(pvt); 36058c2ecf20Sopenharmony_ci if (ret < 0) { 36068c2ecf20Sopenharmony_ci amd64_err("Error probing instance: %d\n", nid); 36078c2ecf20Sopenharmony_ci 36088c2ecf20Sopenharmony_ci if (boot_cpu_data.x86 < 0x17) 36098c2ecf20Sopenharmony_ci restore_ecc_error_reporting(s, nid, F3); 36108c2ecf20Sopenharmony_ci 36118c2ecf20Sopenharmony_ci goto err_enable; 36128c2ecf20Sopenharmony_ci } 36138c2ecf20Sopenharmony_ci 36148c2ecf20Sopenharmony_ci dump_misc_regs(pvt); 36158c2ecf20Sopenharmony_ci 36168c2ecf20Sopenharmony_ci return ret; 36178c2ecf20Sopenharmony_ci 36188c2ecf20Sopenharmony_cierr_enable: 36198c2ecf20Sopenharmony_ci hw_info_put(pvt); 36208c2ecf20Sopenharmony_ci kfree(pvt); 36218c2ecf20Sopenharmony_ci 36228c2ecf20Sopenharmony_cierr_settings: 36238c2ecf20Sopenharmony_ci kfree(s); 36248c2ecf20Sopenharmony_ci ecc_stngs[nid] = NULL; 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_cierr_out: 36278c2ecf20Sopenharmony_ci return ret; 36288c2ecf20Sopenharmony_ci} 36298c2ecf20Sopenharmony_ci 36308c2ecf20Sopenharmony_cistatic void remove_one_instance(unsigned int nid) 36318c2ecf20Sopenharmony_ci{ 36328c2ecf20Sopenharmony_ci struct pci_dev *F3 = node_to_amd_nb(nid)->misc; 36338c2ecf20Sopenharmony_ci struct ecc_settings *s = ecc_stngs[nid]; 36348c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 36358c2ecf20Sopenharmony_ci struct amd64_pvt *pvt; 36368c2ecf20Sopenharmony_ci 36378c2ecf20Sopenharmony_ci /* Remove from EDAC CORE tracking list */ 36388c2ecf20Sopenharmony_ci mci = edac_mc_del_mc(&F3->dev); 36398c2ecf20Sopenharmony_ci if (!mci) 36408c2ecf20Sopenharmony_ci return; 36418c2ecf20Sopenharmony_ci 36428c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 36438c2ecf20Sopenharmony_ci 36448c2ecf20Sopenharmony_ci restore_ecc_error_reporting(s, nid, F3); 36458c2ecf20Sopenharmony_ci 36468c2ecf20Sopenharmony_ci kfree(ecc_stngs[nid]); 36478c2ecf20Sopenharmony_ci ecc_stngs[nid] = NULL; 36488c2ecf20Sopenharmony_ci 36498c2ecf20Sopenharmony_ci /* Free the EDAC CORE resources */ 36508c2ecf20Sopenharmony_ci mci->pvt_info = NULL; 36518c2ecf20Sopenharmony_ci 36528c2ecf20Sopenharmony_ci hw_info_put(pvt); 36538c2ecf20Sopenharmony_ci kfree(pvt); 36548c2ecf20Sopenharmony_ci edac_mc_free(mci); 36558c2ecf20Sopenharmony_ci} 36568c2ecf20Sopenharmony_ci 36578c2ecf20Sopenharmony_cistatic void setup_pci_device(void) 36588c2ecf20Sopenharmony_ci{ 36598c2ecf20Sopenharmony_ci if (pci_ctl) 36608c2ecf20Sopenharmony_ci return; 36618c2ecf20Sopenharmony_ci 36628c2ecf20Sopenharmony_ci pci_ctl = edac_pci_create_generic_ctl(pci_ctl_dev, EDAC_MOD_STR); 36638c2ecf20Sopenharmony_ci if (!pci_ctl) { 36648c2ecf20Sopenharmony_ci pr_warn("%s(): Unable to create PCI control\n", __func__); 36658c2ecf20Sopenharmony_ci pr_warn("%s(): PCI error report via EDAC not set\n", __func__); 36668c2ecf20Sopenharmony_ci } 36678c2ecf20Sopenharmony_ci} 36688c2ecf20Sopenharmony_ci 36698c2ecf20Sopenharmony_cistatic const struct x86_cpu_id amd64_cpuids[] = { 36708c2ecf20Sopenharmony_ci X86_MATCH_VENDOR_FAM(AMD, 0x0F, NULL), 36718c2ecf20Sopenharmony_ci X86_MATCH_VENDOR_FAM(AMD, 0x10, NULL), 36728c2ecf20Sopenharmony_ci X86_MATCH_VENDOR_FAM(AMD, 0x15, NULL), 36738c2ecf20Sopenharmony_ci X86_MATCH_VENDOR_FAM(AMD, 0x16, NULL), 36748c2ecf20Sopenharmony_ci X86_MATCH_VENDOR_FAM(AMD, 0x17, NULL), 36758c2ecf20Sopenharmony_ci X86_MATCH_VENDOR_FAM(HYGON, 0x18, NULL), 36768c2ecf20Sopenharmony_ci X86_MATCH_VENDOR_FAM(AMD, 0x19, NULL), 36778c2ecf20Sopenharmony_ci { } 36788c2ecf20Sopenharmony_ci}; 36798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(x86cpu, amd64_cpuids); 36808c2ecf20Sopenharmony_ci 36818c2ecf20Sopenharmony_cistatic int __init amd64_edac_init(void) 36828c2ecf20Sopenharmony_ci{ 36838c2ecf20Sopenharmony_ci const char *owner; 36848c2ecf20Sopenharmony_ci int err = -ENODEV; 36858c2ecf20Sopenharmony_ci int i; 36868c2ecf20Sopenharmony_ci 36878c2ecf20Sopenharmony_ci owner = edac_get_owner(); 36888c2ecf20Sopenharmony_ci if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR))) 36898c2ecf20Sopenharmony_ci return -EBUSY; 36908c2ecf20Sopenharmony_ci 36918c2ecf20Sopenharmony_ci if (!x86_match_cpu(amd64_cpuids)) 36928c2ecf20Sopenharmony_ci return -ENODEV; 36938c2ecf20Sopenharmony_ci 36948c2ecf20Sopenharmony_ci if (amd_cache_northbridges() < 0) 36958c2ecf20Sopenharmony_ci return -ENODEV; 36968c2ecf20Sopenharmony_ci 36978c2ecf20Sopenharmony_ci opstate_init(); 36988c2ecf20Sopenharmony_ci 36998c2ecf20Sopenharmony_ci err = -ENOMEM; 37008c2ecf20Sopenharmony_ci ecc_stngs = kcalloc(amd_nb_num(), sizeof(ecc_stngs[0]), GFP_KERNEL); 37018c2ecf20Sopenharmony_ci if (!ecc_stngs) 37028c2ecf20Sopenharmony_ci goto err_free; 37038c2ecf20Sopenharmony_ci 37048c2ecf20Sopenharmony_ci msrs = msrs_alloc(); 37058c2ecf20Sopenharmony_ci if (!msrs) 37068c2ecf20Sopenharmony_ci goto err_free; 37078c2ecf20Sopenharmony_ci 37088c2ecf20Sopenharmony_ci for (i = 0; i < amd_nb_num(); i++) { 37098c2ecf20Sopenharmony_ci err = probe_one_instance(i); 37108c2ecf20Sopenharmony_ci if (err) { 37118c2ecf20Sopenharmony_ci /* unwind properly */ 37128c2ecf20Sopenharmony_ci while (--i >= 0) 37138c2ecf20Sopenharmony_ci remove_one_instance(i); 37148c2ecf20Sopenharmony_ci 37158c2ecf20Sopenharmony_ci goto err_pci; 37168c2ecf20Sopenharmony_ci } 37178c2ecf20Sopenharmony_ci } 37188c2ecf20Sopenharmony_ci 37198c2ecf20Sopenharmony_ci if (!edac_has_mcs()) { 37208c2ecf20Sopenharmony_ci err = -ENODEV; 37218c2ecf20Sopenharmony_ci goto err_pci; 37228c2ecf20Sopenharmony_ci } 37238c2ecf20Sopenharmony_ci 37248c2ecf20Sopenharmony_ci /* register stuff with EDAC MCE */ 37258c2ecf20Sopenharmony_ci if (boot_cpu_data.x86 >= 0x17) 37268c2ecf20Sopenharmony_ci amd_register_ecc_decoder(decode_umc_error); 37278c2ecf20Sopenharmony_ci else 37288c2ecf20Sopenharmony_ci amd_register_ecc_decoder(decode_bus_error); 37298c2ecf20Sopenharmony_ci 37308c2ecf20Sopenharmony_ci setup_pci_device(); 37318c2ecf20Sopenharmony_ci 37328c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_32 37338c2ecf20Sopenharmony_ci amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR); 37348c2ecf20Sopenharmony_ci#endif 37358c2ecf20Sopenharmony_ci 37368c2ecf20Sopenharmony_ci printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION); 37378c2ecf20Sopenharmony_ci 37388c2ecf20Sopenharmony_ci return 0; 37398c2ecf20Sopenharmony_ci 37408c2ecf20Sopenharmony_cierr_pci: 37418c2ecf20Sopenharmony_ci pci_ctl_dev = NULL; 37428c2ecf20Sopenharmony_ci 37438c2ecf20Sopenharmony_ci msrs_free(msrs); 37448c2ecf20Sopenharmony_ci msrs = NULL; 37458c2ecf20Sopenharmony_ci 37468c2ecf20Sopenharmony_cierr_free: 37478c2ecf20Sopenharmony_ci kfree(ecc_stngs); 37488c2ecf20Sopenharmony_ci ecc_stngs = NULL; 37498c2ecf20Sopenharmony_ci 37508c2ecf20Sopenharmony_ci return err; 37518c2ecf20Sopenharmony_ci} 37528c2ecf20Sopenharmony_ci 37538c2ecf20Sopenharmony_cistatic void __exit amd64_edac_exit(void) 37548c2ecf20Sopenharmony_ci{ 37558c2ecf20Sopenharmony_ci int i; 37568c2ecf20Sopenharmony_ci 37578c2ecf20Sopenharmony_ci if (pci_ctl) 37588c2ecf20Sopenharmony_ci edac_pci_release_generic_ctl(pci_ctl); 37598c2ecf20Sopenharmony_ci 37608c2ecf20Sopenharmony_ci /* unregister from EDAC MCE */ 37618c2ecf20Sopenharmony_ci if (boot_cpu_data.x86 >= 0x17) 37628c2ecf20Sopenharmony_ci amd_unregister_ecc_decoder(decode_umc_error); 37638c2ecf20Sopenharmony_ci else 37648c2ecf20Sopenharmony_ci amd_unregister_ecc_decoder(decode_bus_error); 37658c2ecf20Sopenharmony_ci 37668c2ecf20Sopenharmony_ci for (i = 0; i < amd_nb_num(); i++) 37678c2ecf20Sopenharmony_ci remove_one_instance(i); 37688c2ecf20Sopenharmony_ci 37698c2ecf20Sopenharmony_ci kfree(ecc_stngs); 37708c2ecf20Sopenharmony_ci ecc_stngs = NULL; 37718c2ecf20Sopenharmony_ci 37728c2ecf20Sopenharmony_ci pci_ctl_dev = NULL; 37738c2ecf20Sopenharmony_ci 37748c2ecf20Sopenharmony_ci msrs_free(msrs); 37758c2ecf20Sopenharmony_ci msrs = NULL; 37768c2ecf20Sopenharmony_ci} 37778c2ecf20Sopenharmony_ci 37788c2ecf20Sopenharmony_cimodule_init(amd64_edac_init); 37798c2ecf20Sopenharmony_cimodule_exit(amd64_edac_exit); 37808c2ecf20Sopenharmony_ci 37818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 37828c2ecf20Sopenharmony_ciMODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, " 37838c2ecf20Sopenharmony_ci "Dave Peterson, Thayne Harbaugh"); 37848c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MC support for AMD64 memory controllers - " 37858c2ecf20Sopenharmony_ci EDAC_AMD64_VERSION); 37868c2ecf20Sopenharmony_ci 37878c2ecf20Sopenharmony_cimodule_param(edac_op_state, int, 0444); 37888c2ecf20Sopenharmony_ciMODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); 3789