12bc65418SDoug Thompson #include "amd64_edac.h" 223ac4ae8SAndreas Herrmann #include <asm/amd_nb.h> 32bc65418SDoug Thompson 4d1ea71cdSBorislav Petkov static struct edac_pci_ctl_info *pci_ctl; 52bc65418SDoug Thompson 62bc65418SDoug Thompson static int report_gart_errors; 72bc65418SDoug Thompson module_param(report_gart_errors, int, 0644); 82bc65418SDoug Thompson 92bc65418SDoug Thompson /* 102bc65418SDoug Thompson * Set by command line parameter. If BIOS has enabled the ECC, this override is 112bc65418SDoug Thompson * cleared to prevent re-enabling the hardware by this driver. 122bc65418SDoug Thompson */ 132bc65418SDoug Thompson static int ecc_enable_override; 142bc65418SDoug Thompson module_param(ecc_enable_override, int, 0644); 152bc65418SDoug Thompson 16a29d8b8eSTejun Heo static struct msr __percpu *msrs; 1750542251SBorislav Petkov 182ec591acSBorislav Petkov /* Per-node stuff */ 19ae7bb7c6SBorislav Petkov static struct ecc_settings **ecc_stngs; 202bc65418SDoug Thompson 212bc65418SDoug Thompson /* 22b70ef010SBorislav Petkov * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing 23b70ef010SBorislav Petkov * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching- 24b70ef010SBorislav Petkov * or higher value'. 25b70ef010SBorislav Petkov * 26b70ef010SBorislav Petkov *FIXME: Produce a better mapping/linearisation. 27b70ef010SBorislav Petkov */ 28c7e5301aSDaniel J Blueman static const struct scrubrate { 2939094443SBorislav Petkov u32 scrubval; /* bit pattern for scrub rate */ 3039094443SBorislav Petkov u32 bandwidth; /* bandwidth consumed (bytes/sec) */ 3139094443SBorislav Petkov } scrubrates[] = { 32b70ef010SBorislav Petkov { 0x01, 1600000000UL}, 33b70ef010SBorislav Petkov { 0x02, 800000000UL}, 34b70ef010SBorislav Petkov { 0x03, 400000000UL}, 35b70ef010SBorislav Petkov { 0x04, 200000000UL}, 36b70ef010SBorislav Petkov { 0x05, 100000000UL}, 37b70ef010SBorislav Petkov { 0x06, 50000000UL}, 38b70ef010SBorislav Petkov { 0x07, 25000000UL}, 39b70ef010SBorislav Petkov { 0x08, 12284069UL}, 40b70ef010SBorislav Petkov { 0x09, 6274509UL}, 41b70ef010SBorislav Petkov { 0x0A, 3121951UL}, 42b70ef010SBorislav Petkov { 0x0B, 1560975UL}, 43b70ef010SBorislav Petkov { 0x0C, 781440UL}, 44b70ef010SBorislav Petkov { 0x0D, 390720UL}, 45b70ef010SBorislav Petkov { 0x0E, 195300UL}, 46b70ef010SBorislav Petkov { 0x0F, 97650UL}, 47b70ef010SBorislav Petkov { 0x10, 48854UL}, 48b70ef010SBorislav Petkov { 0x11, 24427UL}, 49b70ef010SBorislav Petkov { 0x12, 12213UL}, 50b70ef010SBorislav Petkov { 0x13, 6101UL}, 51b70ef010SBorislav Petkov { 0x14, 3051UL}, 52b70ef010SBorislav Petkov { 0x15, 1523UL}, 53b70ef010SBorislav Petkov { 0x16, 761UL}, 54b70ef010SBorislav Petkov { 0x00, 0UL}, /* scrubbing off */ 55b70ef010SBorislav Petkov }; 56b70ef010SBorislav Petkov 5766fed2d4SBorislav Petkov int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset, 58b2b0c605SBorislav Petkov u32 *val, const char *func) 59b2b0c605SBorislav Petkov { 60b2b0c605SBorislav Petkov int err = 0; 61b2b0c605SBorislav Petkov 62b2b0c605SBorislav Petkov err = pci_read_config_dword(pdev, offset, val); 63b2b0c605SBorislav Petkov if (err) 64b2b0c605SBorislav Petkov amd64_warn("%s: error reading F%dx%03x.\n", 65b2b0c605SBorislav Petkov func, PCI_FUNC(pdev->devfn), offset); 66b2b0c605SBorislav Petkov 67b2b0c605SBorislav Petkov return err; 68b2b0c605SBorislav Petkov } 69b2b0c605SBorislav Petkov 70b2b0c605SBorislav Petkov int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset, 71b2b0c605SBorislav Petkov u32 val, const char *func) 72b2b0c605SBorislav Petkov { 73b2b0c605SBorislav Petkov int err = 0; 74b2b0c605SBorislav Petkov 75b2b0c605SBorislav Petkov err = pci_write_config_dword(pdev, offset, val); 76b2b0c605SBorislav Petkov if (err) 77b2b0c605SBorislav Petkov amd64_warn("%s: error writing to F%dx%03x.\n", 78b2b0c605SBorislav Petkov func, PCI_FUNC(pdev->devfn), offset); 79b2b0c605SBorislav Petkov 80b2b0c605SBorislav Petkov return err; 81b2b0c605SBorislav Petkov } 82b2b0c605SBorislav Petkov 83b2b0c605SBorislav Petkov /* 8473ba8593SBorislav Petkov * Select DCT to which PCI cfg accesses are routed 8573ba8593SBorislav Petkov */ 8673ba8593SBorislav Petkov static void f15h_select_dct(struct amd64_pvt *pvt, u8 dct) 8773ba8593SBorislav Petkov { 8873ba8593SBorislav Petkov u32 reg = 0; 8973ba8593SBorislav Petkov 9073ba8593SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, ®); 917981a28fSAravind Gopalakrishnan reg &= (pvt->model == 0x30) ? ~3 : ~1; 9273ba8593SBorislav Petkov reg |= dct; 9373ba8593SBorislav Petkov amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg); 9473ba8593SBorislav Petkov } 9573ba8593SBorislav Petkov 967981a28fSAravind Gopalakrishnan /* 977981a28fSAravind Gopalakrishnan * 987981a28fSAravind Gopalakrishnan * Depending on the family, F2 DCT reads need special handling: 997981a28fSAravind Gopalakrishnan * 1007981a28fSAravind Gopalakrishnan * K8: has a single DCT only and no address offsets >= 0x100 1017981a28fSAravind Gopalakrishnan * 1027981a28fSAravind Gopalakrishnan * F10h: each DCT has its own set of regs 1037981a28fSAravind Gopalakrishnan * DCT0 -> F2x040.. 1047981a28fSAravind Gopalakrishnan * DCT1 -> F2x140.. 1057981a28fSAravind Gopalakrishnan * 1067981a28fSAravind Gopalakrishnan * F16h: has only 1 DCT 1077981a28fSAravind Gopalakrishnan * 1087981a28fSAravind Gopalakrishnan * F15h: we select which DCT we access using F1x10C[DctCfgSel] 1097981a28fSAravind Gopalakrishnan */ 1107981a28fSAravind Gopalakrishnan static inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct, 1117981a28fSAravind Gopalakrishnan int offset, u32 *val) 112b2b0c605SBorislav Petkov { 1137981a28fSAravind Gopalakrishnan switch (pvt->fam) { 1147981a28fSAravind Gopalakrishnan case 0xf: 1157981a28fSAravind Gopalakrishnan if (dct || offset >= 0x100) 1167981a28fSAravind Gopalakrishnan return -EINVAL; 1177981a28fSAravind Gopalakrishnan break; 118b2b0c605SBorislav Petkov 1197981a28fSAravind Gopalakrishnan case 0x10: 1207981a28fSAravind Gopalakrishnan if (dct) { 1217981a28fSAravind Gopalakrishnan /* 1227981a28fSAravind Gopalakrishnan * Note: If ganging is enabled, barring the regs 1237981a28fSAravind Gopalakrishnan * F2x[1,0]98 and F2x[1,0]9C; reads reads to F2x1xx 1247981a28fSAravind Gopalakrishnan * return 0. (cf. Section 2.8.1 F10h BKDG) 1257981a28fSAravind Gopalakrishnan */ 1267981a28fSAravind Gopalakrishnan if (dct_ganging_enabled(pvt)) 1277981a28fSAravind Gopalakrishnan return 0; 1287981a28fSAravind Gopalakrishnan 1297981a28fSAravind Gopalakrishnan offset += 0x100; 130b2b0c605SBorislav Petkov } 1317981a28fSAravind Gopalakrishnan break; 132b2b0c605SBorislav Petkov 1337981a28fSAravind Gopalakrishnan case 0x15: 1347981a28fSAravind Gopalakrishnan /* 1357981a28fSAravind Gopalakrishnan * F15h: F2x1xx addresses do not map explicitly to DCT1. 1367981a28fSAravind Gopalakrishnan * We should select which DCT we access using F1x10C[DctCfgSel] 1377981a28fSAravind Gopalakrishnan */ 1387981a28fSAravind Gopalakrishnan dct = (dct && pvt->model == 0x30) ? 3 : dct; 13973ba8593SBorislav Petkov f15h_select_dct(pvt, dct); 1407981a28fSAravind Gopalakrishnan break; 141b2b0c605SBorislav Petkov 1427981a28fSAravind Gopalakrishnan case 0x16: 1437981a28fSAravind Gopalakrishnan if (dct) 1447981a28fSAravind Gopalakrishnan return -EINVAL; 1457981a28fSAravind Gopalakrishnan break; 1467981a28fSAravind Gopalakrishnan 1477981a28fSAravind Gopalakrishnan default: 1487981a28fSAravind Gopalakrishnan break; 1497981a28fSAravind Gopalakrishnan } 1507981a28fSAravind Gopalakrishnan return amd64_read_pci_cfg(pvt->F2, offset, val); 151b2b0c605SBorislav Petkov } 152b2b0c605SBorislav Petkov 153b70ef010SBorislav Petkov /* 1542bc65418SDoug Thompson * Memory scrubber control interface. For K8, memory scrubbing is handled by 1552bc65418SDoug Thompson * hardware and can involve L2 cache, dcache as well as the main memory. With 1562bc65418SDoug Thompson * F10, this is extended to L3 cache scrubbing on CPU models sporting that 1572bc65418SDoug Thompson * functionality. 1582bc65418SDoug Thompson * 1592bc65418SDoug Thompson * This causes the "units" for the scrubbing speed to vary from 64 byte blocks 1602bc65418SDoug Thompson * (dram) over to cache lines. This is nasty, so we will use bandwidth in 1612bc65418SDoug Thompson * bytes/sec for the setting. 1622bc65418SDoug Thompson * 1632bc65418SDoug Thompson * Currently, we only do dram scrubbing. If the scrubbing is done in software on 1642bc65418SDoug Thompson * other archs, we might not have access to the caches directly. 1652bc65418SDoug Thompson */ 1662bc65418SDoug Thompson 1678051c0afSYazen Ghannam static inline void __f17h_set_scrubval(struct amd64_pvt *pvt, u32 scrubval) 1688051c0afSYazen Ghannam { 1692bc65418SDoug Thompson /* 1708051c0afSYazen Ghannam * Fam17h supports scrub values between 0x5 and 0x14. Also, the values 1718051c0afSYazen Ghannam * are shifted down by 0x5, so scrubval 0x5 is written to the register 1728051c0afSYazen Ghannam * as 0x0, scrubval 0x6 as 0x1, etc. 1738051c0afSYazen Ghannam */ 1748051c0afSYazen Ghannam if (scrubval >= 0x5 && scrubval <= 0x14) { 1758051c0afSYazen Ghannam scrubval -= 0x5; 1768051c0afSYazen Ghannam pci_write_bits32(pvt->F6, F17H_SCR_LIMIT_ADDR, scrubval, 0xF); 1778051c0afSYazen Ghannam pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 1, 0x1); 1788051c0afSYazen Ghannam } else { 1798051c0afSYazen Ghannam pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 0, 0x1); 1808051c0afSYazen Ghannam } 1818051c0afSYazen Ghannam } 1828051c0afSYazen Ghannam /* 1838051c0afSYazen Ghannam * Scan the scrub rate mapping table for a close or matching bandwidth value to 1842bc65418SDoug Thompson * issue. If requested is too big, then use last maximum value found. 1852bc65418SDoug Thompson */ 186da92110dSAravind Gopalakrishnan static int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate) 1872bc65418SDoug Thompson { 1882bc65418SDoug Thompson u32 scrubval; 1892bc65418SDoug Thompson int i; 1902bc65418SDoug Thompson 1912bc65418SDoug Thompson /* 1922bc65418SDoug Thompson * map the configured rate (new_bw) to a value specific to the AMD64 1932bc65418SDoug Thompson * memory controller and apply to register. Search for the first 1942bc65418SDoug Thompson * bandwidth entry that is greater or equal than the setting requested 1952bc65418SDoug Thompson * and program that. If at last entry, turn off DRAM scrubbing. 196168bfeefSAndrew Morton * 197168bfeefSAndrew Morton * If no suitable bandwidth is found, turn off DRAM scrubbing entirely 198168bfeefSAndrew Morton * by falling back to the last element in scrubrates[]. 1992bc65418SDoug Thompson */ 200168bfeefSAndrew Morton for (i = 0; i < ARRAY_SIZE(scrubrates) - 1; i++) { 2012bc65418SDoug Thompson /* 2022bc65418SDoug Thompson * skip scrub rates which aren't recommended 2032bc65418SDoug Thompson * (see F10 BKDG, F3x58) 2042bc65418SDoug Thompson */ 205395ae783SBorislav Petkov if (scrubrates[i].scrubval < min_rate) 2062bc65418SDoug Thompson continue; 2072bc65418SDoug Thompson 2082bc65418SDoug Thompson if (scrubrates[i].bandwidth <= new_bw) 2092bc65418SDoug Thompson break; 2102bc65418SDoug Thompson } 2112bc65418SDoug Thompson 2122bc65418SDoug Thompson scrubval = scrubrates[i].scrubval; 2132bc65418SDoug Thompson 2148051c0afSYazen Ghannam if (pvt->fam == 0x17) { 2158051c0afSYazen Ghannam __f17h_set_scrubval(pvt, scrubval); 2168051c0afSYazen Ghannam } else if (pvt->fam == 0x15 && pvt->model == 0x60) { 217da92110dSAravind Gopalakrishnan f15h_select_dct(pvt, 0); 218da92110dSAravind Gopalakrishnan pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F); 219da92110dSAravind Gopalakrishnan f15h_select_dct(pvt, 1); 220da92110dSAravind Gopalakrishnan pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F); 221da92110dSAravind Gopalakrishnan } else { 222da92110dSAravind Gopalakrishnan pci_write_bits32(pvt->F3, SCRCTRL, scrubval, 0x001F); 223da92110dSAravind Gopalakrishnan } 2242bc65418SDoug Thompson 22539094443SBorislav Petkov if (scrubval) 22639094443SBorislav Petkov return scrubrates[i].bandwidth; 22739094443SBorislav Petkov 2282bc65418SDoug Thompson return 0; 2292bc65418SDoug Thompson } 2302bc65418SDoug Thompson 231d1ea71cdSBorislav Petkov static int set_scrub_rate(struct mem_ctl_info *mci, u32 bw) 2322bc65418SDoug Thompson { 2332bc65418SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 23487b3e0e6SBorislav Petkov u32 min_scrubrate = 0x5; 2352bc65418SDoug Thompson 236a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) 23787b3e0e6SBorislav Petkov min_scrubrate = 0x0; 23887b3e0e6SBorislav Petkov 239da92110dSAravind Gopalakrishnan if (pvt->fam == 0x15) { 2403f0aba4fSBorislav Petkov /* Erratum #505 */ 241da92110dSAravind Gopalakrishnan if (pvt->model < 0x10) 24273ba8593SBorislav Petkov f15h_select_dct(pvt, 0); 24373ba8593SBorislav Petkov 244da92110dSAravind Gopalakrishnan if (pvt->model == 0x60) 245da92110dSAravind Gopalakrishnan min_scrubrate = 0x6; 246da92110dSAravind Gopalakrishnan } 247da92110dSAravind Gopalakrishnan return __set_scrub_rate(pvt, bw, min_scrubrate); 2482bc65418SDoug Thompson } 2492bc65418SDoug Thompson 250d1ea71cdSBorislav Petkov static int get_scrub_rate(struct mem_ctl_info *mci) 2512bc65418SDoug Thompson { 2522bc65418SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 25339094443SBorislav Petkov int i, retval = -EINVAL; 2548051c0afSYazen Ghannam u32 scrubval = 0; 2552bc65418SDoug Thompson 2568051c0afSYazen Ghannam switch (pvt->fam) { 2578051c0afSYazen Ghannam case 0x15: 2583f0aba4fSBorislav Petkov /* Erratum #505 */ 259da92110dSAravind Gopalakrishnan if (pvt->model < 0x10) 26073ba8593SBorislav Petkov f15h_select_dct(pvt, 0); 26173ba8593SBorislav Petkov 262da92110dSAravind Gopalakrishnan if (pvt->model == 0x60) 263da92110dSAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F2, F15H_M60H_SCRCTRL, &scrubval); 2648051c0afSYazen Ghannam break; 2658051c0afSYazen Ghannam 2668051c0afSYazen Ghannam case 0x17: 2678051c0afSYazen Ghannam amd64_read_pci_cfg(pvt->F6, F17H_SCR_BASE_ADDR, &scrubval); 2688051c0afSYazen Ghannam if (scrubval & BIT(0)) { 2698051c0afSYazen Ghannam amd64_read_pci_cfg(pvt->F6, F17H_SCR_LIMIT_ADDR, &scrubval); 2708051c0afSYazen Ghannam scrubval &= 0xF; 2718051c0afSYazen Ghannam scrubval += 0x5; 2728051c0afSYazen Ghannam } else { 2738051c0afSYazen Ghannam scrubval = 0; 2748051c0afSYazen Ghannam } 2758051c0afSYazen Ghannam break; 2768051c0afSYazen Ghannam 2778051c0afSYazen Ghannam default: 2785980bb9cSBorislav Petkov amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval); 2798051c0afSYazen Ghannam break; 2808051c0afSYazen Ghannam } 2812bc65418SDoug Thompson 2822bc65418SDoug Thompson scrubval = scrubval & 0x001F; 2832bc65418SDoug Thompson 284926311fdSRoel Kluin for (i = 0; i < ARRAY_SIZE(scrubrates); i++) { 2852bc65418SDoug Thompson if (scrubrates[i].scrubval == scrubval) { 28639094443SBorislav Petkov retval = scrubrates[i].bandwidth; 2872bc65418SDoug Thompson break; 2882bc65418SDoug Thompson } 2892bc65418SDoug Thompson } 29039094443SBorislav Petkov return retval; 2912bc65418SDoug Thompson } 2922bc65418SDoug Thompson 2936775763aSDoug Thompson /* 2947f19bf75SBorislav Petkov * returns true if the SysAddr given by sys_addr matches the 2957f19bf75SBorislav Petkov * DRAM base/limit associated with node_id 2966775763aSDoug Thompson */ 297d1ea71cdSBorislav Petkov static bool base_limit_match(struct amd64_pvt *pvt, u64 sys_addr, u8 nid) 2986775763aSDoug Thompson { 2997f19bf75SBorislav Petkov u64 addr; 3006775763aSDoug Thompson 3016775763aSDoug Thompson /* The K8 treats this as a 40-bit value. However, bits 63-40 will be 3026775763aSDoug Thompson * all ones if the most significant implemented address bit is 1. 3036775763aSDoug Thompson * Here we discard bits 63-40. See section 3.4.2 of AMD publication 3046775763aSDoug Thompson * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1 3056775763aSDoug Thompson * Application Programming. 3066775763aSDoug Thompson */ 3076775763aSDoug Thompson addr = sys_addr & 0x000000ffffffffffull; 3086775763aSDoug Thompson 3097f19bf75SBorislav Petkov return ((addr >= get_dram_base(pvt, nid)) && 3107f19bf75SBorislav Petkov (addr <= get_dram_limit(pvt, nid))); 3116775763aSDoug Thompson } 3126775763aSDoug Thompson 3136775763aSDoug Thompson /* 3146775763aSDoug Thompson * Attempt to map a SysAddr to a node. On success, return a pointer to the 3156775763aSDoug Thompson * mem_ctl_info structure for the node that the SysAddr maps to. 3166775763aSDoug Thompson * 3176775763aSDoug Thompson * On failure, return NULL. 3186775763aSDoug Thompson */ 3196775763aSDoug Thompson static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci, 3206775763aSDoug Thompson u64 sys_addr) 3216775763aSDoug Thompson { 3226775763aSDoug Thompson struct amd64_pvt *pvt; 323c7e5301aSDaniel J Blueman u8 node_id; 3246775763aSDoug Thompson u32 intlv_en, bits; 3256775763aSDoug Thompson 3266775763aSDoug Thompson /* 3276775763aSDoug Thompson * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section 3286775763aSDoug Thompson * 3.4.4.2) registers to map the SysAddr to a node ID. 3296775763aSDoug Thompson */ 3306775763aSDoug Thompson pvt = mci->pvt_info; 3316775763aSDoug Thompson 3326775763aSDoug Thompson /* 3336775763aSDoug Thompson * The value of this field should be the same for all DRAM Base 3346775763aSDoug Thompson * registers. Therefore we arbitrarily choose to read it from the 3356775763aSDoug Thompson * register for node 0. 3366775763aSDoug Thompson */ 3377f19bf75SBorislav Petkov intlv_en = dram_intlv_en(pvt, 0); 3386775763aSDoug Thompson 3396775763aSDoug Thompson if (intlv_en == 0) { 3407f19bf75SBorislav Petkov for (node_id = 0; node_id < DRAM_RANGES; node_id++) { 341d1ea71cdSBorislav Petkov if (base_limit_match(pvt, sys_addr, node_id)) 3426775763aSDoug Thompson goto found; 3436775763aSDoug Thompson } 3448edc5445SBorislav Petkov goto err_no_match; 3458edc5445SBorislav Petkov } 3466775763aSDoug Thompson 34772f158feSBorislav Petkov if (unlikely((intlv_en != 0x01) && 34872f158feSBorislav Petkov (intlv_en != 0x03) && 34972f158feSBorislav Petkov (intlv_en != 0x07))) { 35024f9a7feSBorislav Petkov amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en); 3516775763aSDoug Thompson return NULL; 3526775763aSDoug Thompson } 3536775763aSDoug Thompson 3546775763aSDoug Thompson bits = (((u32) sys_addr) >> 12) & intlv_en; 3556775763aSDoug Thompson 3566775763aSDoug Thompson for (node_id = 0; ; ) { 3577f19bf75SBorislav Petkov if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits) 3586775763aSDoug Thompson break; /* intlv_sel field matches */ 3596775763aSDoug Thompson 3607f19bf75SBorislav Petkov if (++node_id >= DRAM_RANGES) 3616775763aSDoug Thompson goto err_no_match; 3626775763aSDoug Thompson } 3636775763aSDoug Thompson 3646775763aSDoug Thompson /* sanity test for sys_addr */ 365d1ea71cdSBorislav Petkov if (unlikely(!base_limit_match(pvt, sys_addr, node_id))) { 36624f9a7feSBorislav Petkov amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address" 36724f9a7feSBorislav Petkov "range for node %d with node interleaving enabled.\n", 3688edc5445SBorislav Petkov __func__, sys_addr, node_id); 3696775763aSDoug Thompson return NULL; 3706775763aSDoug Thompson } 3716775763aSDoug Thompson 3726775763aSDoug Thompson found: 373b487c33eSBorislav Petkov return edac_mc_find((int)node_id); 3746775763aSDoug Thompson 3756775763aSDoug Thompson err_no_match: 376956b9ba1SJoe Perches edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n", 3776775763aSDoug Thompson (unsigned long)sys_addr); 3786775763aSDoug Thompson 3796775763aSDoug Thompson return NULL; 3806775763aSDoug Thompson } 381e2ce7255SDoug Thompson 382e2ce7255SDoug Thompson /* 38311c75eadSBorislav Petkov * compute the CS base address of the @csrow on the DRAM controller @dct. 38411c75eadSBorislav Petkov * For details see F2x[5C:40] in the processor's BKDG 385e2ce7255SDoug Thompson */ 38611c75eadSBorislav Petkov static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct, 38711c75eadSBorislav Petkov u64 *base, u64 *mask) 388e2ce7255SDoug Thompson { 38911c75eadSBorislav Petkov u64 csbase, csmask, base_bits, mask_bits; 39011c75eadSBorislav Petkov u8 addr_shift; 39111c75eadSBorislav Petkov 39218b94f66SAravind Gopalakrishnan if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) { 39311c75eadSBorislav Petkov csbase = pvt->csels[dct].csbases[csrow]; 39411c75eadSBorislav Petkov csmask = pvt->csels[dct].csmasks[csrow]; 39510ef6b0dSChen, Gong base_bits = GENMASK_ULL(31, 21) | GENMASK_ULL(15, 9); 39610ef6b0dSChen, Gong mask_bits = GENMASK_ULL(29, 21) | GENMASK_ULL(15, 9); 39711c75eadSBorislav Petkov addr_shift = 4; 39894c1acf2SAravind Gopalakrishnan 39994c1acf2SAravind Gopalakrishnan /* 40018b94f66SAravind Gopalakrishnan * F16h and F15h, models 30h and later need two addr_shift values: 40118b94f66SAravind Gopalakrishnan * 8 for high and 6 for low (cf. F16h BKDG). 40294c1acf2SAravind Gopalakrishnan */ 40318b94f66SAravind Gopalakrishnan } else if (pvt->fam == 0x16 || 40418b94f66SAravind Gopalakrishnan (pvt->fam == 0x15 && pvt->model >= 0x30)) { 40594c1acf2SAravind Gopalakrishnan csbase = pvt->csels[dct].csbases[csrow]; 40694c1acf2SAravind Gopalakrishnan csmask = pvt->csels[dct].csmasks[csrow >> 1]; 40794c1acf2SAravind Gopalakrishnan 40810ef6b0dSChen, Gong *base = (csbase & GENMASK_ULL(15, 5)) << 6; 40910ef6b0dSChen, Gong *base |= (csbase & GENMASK_ULL(30, 19)) << 8; 41094c1acf2SAravind Gopalakrishnan 41194c1acf2SAravind Gopalakrishnan *mask = ~0ULL; 41294c1acf2SAravind Gopalakrishnan /* poke holes for the csmask */ 41310ef6b0dSChen, Gong *mask &= ~((GENMASK_ULL(15, 5) << 6) | 41410ef6b0dSChen, Gong (GENMASK_ULL(30, 19) << 8)); 41594c1acf2SAravind Gopalakrishnan 41610ef6b0dSChen, Gong *mask |= (csmask & GENMASK_ULL(15, 5)) << 6; 41710ef6b0dSChen, Gong *mask |= (csmask & GENMASK_ULL(30, 19)) << 8; 41894c1acf2SAravind Gopalakrishnan 41994c1acf2SAravind Gopalakrishnan return; 42011c75eadSBorislav Petkov } else { 42111c75eadSBorislav Petkov csbase = pvt->csels[dct].csbases[csrow]; 42211c75eadSBorislav Petkov csmask = pvt->csels[dct].csmasks[csrow >> 1]; 42311c75eadSBorislav Petkov addr_shift = 8; 42411c75eadSBorislav Petkov 425a4b4bedcSBorislav Petkov if (pvt->fam == 0x15) 42610ef6b0dSChen, Gong base_bits = mask_bits = 42710ef6b0dSChen, Gong GENMASK_ULL(30,19) | GENMASK_ULL(13,5); 42811c75eadSBorislav Petkov else 42910ef6b0dSChen, Gong base_bits = mask_bits = 43010ef6b0dSChen, Gong GENMASK_ULL(28,19) | GENMASK_ULL(13,5); 431e2ce7255SDoug Thompson } 432e2ce7255SDoug Thompson 43311c75eadSBorislav Petkov *base = (csbase & base_bits) << addr_shift; 434e2ce7255SDoug Thompson 43511c75eadSBorislav Petkov *mask = ~0ULL; 43611c75eadSBorislav Petkov /* poke holes for the csmask */ 43711c75eadSBorislav Petkov *mask &= ~(mask_bits << addr_shift); 43811c75eadSBorislav Petkov /* OR them in */ 43911c75eadSBorislav Petkov *mask |= (csmask & mask_bits) << addr_shift; 440e2ce7255SDoug Thompson } 441e2ce7255SDoug Thompson 44211c75eadSBorislav Petkov #define for_each_chip_select(i, dct, pvt) \ 44311c75eadSBorislav Petkov for (i = 0; i < pvt->csels[dct].b_cnt; i++) 44411c75eadSBorislav Petkov 445614ec9d8SBorislav Petkov #define chip_select_base(i, dct, pvt) \ 446614ec9d8SBorislav Petkov pvt->csels[dct].csbases[i] 447614ec9d8SBorislav Petkov 44811c75eadSBorislav Petkov #define for_each_chip_select_mask(i, dct, pvt) \ 44911c75eadSBorislav Petkov for (i = 0; i < pvt->csels[dct].m_cnt; i++) 45011c75eadSBorislav Petkov 451e2ce7255SDoug Thompson /* 452e2ce7255SDoug Thompson * @input_addr is an InputAddr associated with the node given by mci. Return the 453e2ce7255SDoug Thompson * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr). 454e2ce7255SDoug Thompson */ 455e2ce7255SDoug Thompson static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr) 456e2ce7255SDoug Thompson { 457e2ce7255SDoug Thompson struct amd64_pvt *pvt; 458e2ce7255SDoug Thompson int csrow; 459e2ce7255SDoug Thompson u64 base, mask; 460e2ce7255SDoug Thompson 461e2ce7255SDoug Thompson pvt = mci->pvt_info; 462e2ce7255SDoug Thompson 46311c75eadSBorislav Petkov for_each_chip_select(csrow, 0, pvt) { 46411c75eadSBorislav Petkov if (!csrow_enabled(csrow, 0, pvt)) 465e2ce7255SDoug Thompson continue; 466e2ce7255SDoug Thompson 46711c75eadSBorislav Petkov get_cs_base_and_mask(pvt, csrow, 0, &base, &mask); 46811c75eadSBorislav Petkov 46911c75eadSBorislav Petkov mask = ~mask; 470e2ce7255SDoug Thompson 471e2ce7255SDoug Thompson if ((input_addr & mask) == (base & mask)) { 472956b9ba1SJoe Perches edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n", 473e2ce7255SDoug Thompson (unsigned long)input_addr, csrow, 474e2ce7255SDoug Thompson pvt->mc_node_id); 475e2ce7255SDoug Thompson 476e2ce7255SDoug Thompson return csrow; 477e2ce7255SDoug Thompson } 478e2ce7255SDoug Thompson } 479956b9ba1SJoe Perches edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n", 480e2ce7255SDoug Thompson (unsigned long)input_addr, pvt->mc_node_id); 481e2ce7255SDoug Thompson 482e2ce7255SDoug Thompson return -1; 483e2ce7255SDoug Thompson } 484e2ce7255SDoug Thompson 485e2ce7255SDoug Thompson /* 486e2ce7255SDoug Thompson * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094) 487e2ce7255SDoug Thompson * for the node represented by mci. Info is passed back in *hole_base, 488e2ce7255SDoug Thompson * *hole_offset, and *hole_size. Function returns 0 if info is valid or 1 if 489e2ce7255SDoug Thompson * info is invalid. Info may be invalid for either of the following reasons: 490e2ce7255SDoug Thompson * 491e2ce7255SDoug Thompson * - The revision of the node is not E or greater. In this case, the DRAM Hole 492e2ce7255SDoug Thompson * Address Register does not exist. 493e2ce7255SDoug Thompson * 494e2ce7255SDoug Thompson * - The DramHoleValid bit is cleared in the DRAM Hole Address Register, 495e2ce7255SDoug Thompson * indicating that its contents are not valid. 496e2ce7255SDoug Thompson * 497e2ce7255SDoug Thompson * The values passed back in *hole_base, *hole_offset, and *hole_size are 498e2ce7255SDoug Thompson * complete 32-bit values despite the fact that the bitfields in the DHAR 499e2ce7255SDoug Thompson * only represent bits 31-24 of the base and offset values. 500e2ce7255SDoug Thompson */ 501e2ce7255SDoug Thompson int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base, 502e2ce7255SDoug Thompson u64 *hole_offset, u64 *hole_size) 503e2ce7255SDoug Thompson { 504e2ce7255SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 505e2ce7255SDoug Thompson 506e2ce7255SDoug Thompson /* only revE and later have the DRAM Hole Address Register */ 507a4b4bedcSBorislav Petkov if (pvt->fam == 0xf && pvt->ext_model < K8_REV_E) { 508956b9ba1SJoe Perches edac_dbg(1, " revision %d for node %d does not support DHAR\n", 509e2ce7255SDoug Thompson pvt->ext_model, pvt->mc_node_id); 510e2ce7255SDoug Thompson return 1; 511e2ce7255SDoug Thompson } 512e2ce7255SDoug Thompson 513bc21fa57SBorislav Petkov /* valid for Fam10h and above */ 514a4b4bedcSBorislav Petkov if (pvt->fam >= 0x10 && !dhar_mem_hoist_valid(pvt)) { 515956b9ba1SJoe Perches edac_dbg(1, " Dram Memory Hoisting is DISABLED on this system\n"); 516e2ce7255SDoug Thompson return 1; 517e2ce7255SDoug Thompson } 518e2ce7255SDoug Thompson 519c8e518d5SBorislav Petkov if (!dhar_valid(pvt)) { 520956b9ba1SJoe Perches edac_dbg(1, " Dram Memory Hoisting is DISABLED on this node %d\n", 521e2ce7255SDoug Thompson pvt->mc_node_id); 522e2ce7255SDoug Thompson return 1; 523e2ce7255SDoug Thompson } 524e2ce7255SDoug Thompson 525e2ce7255SDoug Thompson /* This node has Memory Hoisting */ 526e2ce7255SDoug Thompson 527e2ce7255SDoug Thompson /* +------------------+--------------------+--------------------+----- 528e2ce7255SDoug Thompson * | memory | DRAM hole | relocated | 529e2ce7255SDoug Thompson * | [0, (x - 1)] | [x, 0xffffffff] | addresses from | 530e2ce7255SDoug Thompson * | | | DRAM hole | 531e2ce7255SDoug Thompson * | | | [0x100000000, | 532e2ce7255SDoug Thompson * | | | (0x100000000+ | 533e2ce7255SDoug Thompson * | | | (0xffffffff-x))] | 534e2ce7255SDoug Thompson * +------------------+--------------------+--------------------+----- 535e2ce7255SDoug Thompson * 536e2ce7255SDoug Thompson * Above is a diagram of physical memory showing the DRAM hole and the 537e2ce7255SDoug Thompson * relocated addresses from the DRAM hole. As shown, the DRAM hole 538e2ce7255SDoug Thompson * starts at address x (the base address) and extends through address 539e2ce7255SDoug Thompson * 0xffffffff. The DRAM Hole Address Register (DHAR) relocates the 540e2ce7255SDoug Thompson * addresses in the hole so that they start at 0x100000000. 541e2ce7255SDoug Thompson */ 542e2ce7255SDoug Thompson 5431f31677eSBorislav Petkov *hole_base = dhar_base(pvt); 5441f31677eSBorislav Petkov *hole_size = (1ULL << 32) - *hole_base; 545e2ce7255SDoug Thompson 546a4b4bedcSBorislav Petkov *hole_offset = (pvt->fam > 0xf) ? f10_dhar_offset(pvt) 547a4b4bedcSBorislav Petkov : k8_dhar_offset(pvt); 548e2ce7255SDoug Thompson 549956b9ba1SJoe Perches edac_dbg(1, " DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n", 550e2ce7255SDoug Thompson pvt->mc_node_id, (unsigned long)*hole_base, 551e2ce7255SDoug Thompson (unsigned long)*hole_offset, (unsigned long)*hole_size); 552e2ce7255SDoug Thompson 553e2ce7255SDoug Thompson return 0; 554e2ce7255SDoug Thompson } 555e2ce7255SDoug Thompson EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info); 556e2ce7255SDoug Thompson 55793c2df58SDoug Thompson /* 55893c2df58SDoug Thompson * Return the DramAddr that the SysAddr given by @sys_addr maps to. It is 55993c2df58SDoug Thompson * assumed that sys_addr maps to the node given by mci. 56093c2df58SDoug Thompson * 56193c2df58SDoug Thompson * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section 56293c2df58SDoug Thompson * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a 56393c2df58SDoug Thompson * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled, 56493c2df58SDoug Thompson * then it is also involved in translating a SysAddr to a DramAddr. Sections 56593c2df58SDoug Thompson * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting. 56693c2df58SDoug Thompson * These parts of the documentation are unclear. I interpret them as follows: 56793c2df58SDoug Thompson * 56893c2df58SDoug Thompson * When node n receives a SysAddr, it processes the SysAddr as follows: 56993c2df58SDoug Thompson * 57093c2df58SDoug Thompson * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM 57193c2df58SDoug Thompson * Limit registers for node n. If the SysAddr is not within the range 57293c2df58SDoug Thompson * specified by the base and limit values, then node n ignores the Sysaddr 57393c2df58SDoug Thompson * (since it does not map to node n). Otherwise continue to step 2 below. 57493c2df58SDoug Thompson * 57593c2df58SDoug Thompson * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is 57693c2df58SDoug Thompson * disabled so skip to step 3 below. Otherwise see if the SysAddr is within 57793c2df58SDoug Thompson * the range of relocated addresses (starting at 0x100000000) from the DRAM 57893c2df58SDoug Thompson * hole. If not, skip to step 3 below. Else get the value of the 57993c2df58SDoug Thompson * DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the 58093c2df58SDoug Thompson * offset defined by this value from the SysAddr. 58193c2df58SDoug Thompson * 58293c2df58SDoug Thompson * 3. Obtain the base address for node n from the DRAMBase field of the DRAM 58393c2df58SDoug Thompson * Base register for node n. To obtain the DramAddr, subtract the base 58493c2df58SDoug Thompson * address from the SysAddr, as shown near the start of section 3.4.4 (p.70). 58593c2df58SDoug Thompson */ 58693c2df58SDoug Thompson static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr) 58793c2df58SDoug Thompson { 5887f19bf75SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 58993c2df58SDoug Thompson u64 dram_base, hole_base, hole_offset, hole_size, dram_addr; 5901f31677eSBorislav Petkov int ret; 59193c2df58SDoug Thompson 5927f19bf75SBorislav Petkov dram_base = get_dram_base(pvt, pvt->mc_node_id); 59393c2df58SDoug Thompson 59493c2df58SDoug Thompson ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset, 59593c2df58SDoug Thompson &hole_size); 59693c2df58SDoug Thompson if (!ret) { 5971f31677eSBorislav Petkov if ((sys_addr >= (1ULL << 32)) && 5981f31677eSBorislav Petkov (sys_addr < ((1ULL << 32) + hole_size))) { 59993c2df58SDoug Thompson /* use DHAR to translate SysAddr to DramAddr */ 60093c2df58SDoug Thompson dram_addr = sys_addr - hole_offset; 60193c2df58SDoug Thompson 602956b9ba1SJoe Perches edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n", 60393c2df58SDoug Thompson (unsigned long)sys_addr, 60493c2df58SDoug Thompson (unsigned long)dram_addr); 60593c2df58SDoug Thompson 60693c2df58SDoug Thompson return dram_addr; 60793c2df58SDoug Thompson } 60893c2df58SDoug Thompson } 60993c2df58SDoug Thompson 61093c2df58SDoug Thompson /* 61193c2df58SDoug Thompson * Translate the SysAddr to a DramAddr as shown near the start of 61293c2df58SDoug Thompson * section 3.4.4 (p. 70). Although sys_addr is a 64-bit value, the k8 61393c2df58SDoug Thompson * only deals with 40-bit values. Therefore we discard bits 63-40 of 61493c2df58SDoug Thompson * sys_addr below. If bit 39 of sys_addr is 1 then the bits we 61593c2df58SDoug Thompson * discard are all 1s. Otherwise the bits we discard are all 0s. See 61693c2df58SDoug Thompson * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture 61793c2df58SDoug Thompson * Programmer's Manual Volume 1 Application Programming. 61893c2df58SDoug Thompson */ 61910ef6b0dSChen, Gong dram_addr = (sys_addr & GENMASK_ULL(39, 0)) - dram_base; 62093c2df58SDoug Thompson 621956b9ba1SJoe Perches edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n", 622956b9ba1SJoe Perches (unsigned long)sys_addr, (unsigned long)dram_addr); 62393c2df58SDoug Thompson return dram_addr; 62493c2df58SDoug Thompson } 62593c2df58SDoug Thompson 62693c2df58SDoug Thompson /* 62793c2df58SDoug Thompson * @intlv_en is the value of the IntlvEn field from a DRAM Base register 62893c2df58SDoug Thompson * (section 3.4.4.1). Return the number of bits from a SysAddr that are used 62993c2df58SDoug Thompson * for node interleaving. 63093c2df58SDoug Thompson */ 63193c2df58SDoug Thompson static int num_node_interleave_bits(unsigned intlv_en) 63293c2df58SDoug Thompson { 63393c2df58SDoug Thompson static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 }; 63493c2df58SDoug Thompson int n; 63593c2df58SDoug Thompson 63693c2df58SDoug Thompson BUG_ON(intlv_en > 7); 63793c2df58SDoug Thompson n = intlv_shift_table[intlv_en]; 63893c2df58SDoug Thompson return n; 63993c2df58SDoug Thompson } 64093c2df58SDoug Thompson 64193c2df58SDoug Thompson /* Translate the DramAddr given by @dram_addr to an InputAddr. */ 64293c2df58SDoug Thompson static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr) 64393c2df58SDoug Thompson { 64493c2df58SDoug Thompson struct amd64_pvt *pvt; 64593c2df58SDoug Thompson int intlv_shift; 64693c2df58SDoug Thompson u64 input_addr; 64793c2df58SDoug Thompson 64893c2df58SDoug Thompson pvt = mci->pvt_info; 64993c2df58SDoug Thompson 65093c2df58SDoug Thompson /* 65193c2df58SDoug Thompson * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E) 65293c2df58SDoug Thompson * concerning translating a DramAddr to an InputAddr. 65393c2df58SDoug Thompson */ 6547f19bf75SBorislav Petkov intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0)); 65510ef6b0dSChen, Gong input_addr = ((dram_addr >> intlv_shift) & GENMASK_ULL(35, 12)) + 65693c2df58SDoug Thompson (dram_addr & 0xfff); 65793c2df58SDoug Thompson 658956b9ba1SJoe Perches edac_dbg(2, " Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n", 65993c2df58SDoug Thompson intlv_shift, (unsigned long)dram_addr, 66093c2df58SDoug Thompson (unsigned long)input_addr); 66193c2df58SDoug Thompson 66293c2df58SDoug Thompson return input_addr; 66393c2df58SDoug Thompson } 66493c2df58SDoug Thompson 66593c2df58SDoug Thompson /* 66693c2df58SDoug Thompson * Translate the SysAddr represented by @sys_addr to an InputAddr. It is 66793c2df58SDoug Thompson * assumed that @sys_addr maps to the node given by mci. 66893c2df58SDoug Thompson */ 66993c2df58SDoug Thompson static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr) 67093c2df58SDoug Thompson { 67193c2df58SDoug Thompson u64 input_addr; 67293c2df58SDoug Thompson 67393c2df58SDoug Thompson input_addr = 67493c2df58SDoug Thompson dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr)); 67593c2df58SDoug Thompson 676c19ca6cbSMasanari Iida edac_dbg(2, "SysAddr 0x%lx translates to InputAddr 0x%lx\n", 67793c2df58SDoug Thompson (unsigned long)sys_addr, (unsigned long)input_addr); 67893c2df58SDoug Thompson 67993c2df58SDoug Thompson return input_addr; 68093c2df58SDoug Thompson } 68193c2df58SDoug Thompson 68293c2df58SDoug Thompson /* Map the Error address to a PAGE and PAGE OFFSET. */ 68393c2df58SDoug Thompson static inline void error_address_to_page_and_offset(u64 error_address, 68433ca0643SBorislav Petkov struct err_info *err) 68593c2df58SDoug Thompson { 68633ca0643SBorislav Petkov err->page = (u32) (error_address >> PAGE_SHIFT); 68733ca0643SBorislav Petkov err->offset = ((u32) error_address) & ~PAGE_MASK; 68893c2df58SDoug Thompson } 68993c2df58SDoug Thompson 69093c2df58SDoug Thompson /* 69193c2df58SDoug Thompson * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address 69293c2df58SDoug Thompson * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers 69393c2df58SDoug Thompson * of a node that detected an ECC memory error. mci represents the node that 69493c2df58SDoug Thompson * the error address maps to (possibly different from the node that detected 69593c2df58SDoug Thompson * the error). Return the number of the csrow that sys_addr maps to, or -1 on 69693c2df58SDoug Thompson * error. 69793c2df58SDoug Thompson */ 69893c2df58SDoug Thompson static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr) 69993c2df58SDoug Thompson { 70093c2df58SDoug Thompson int csrow; 70193c2df58SDoug Thompson 70293c2df58SDoug Thompson csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr)); 70393c2df58SDoug Thompson 70493c2df58SDoug Thompson if (csrow == -1) 70524f9a7feSBorislav Petkov amd64_mc_err(mci, "Failed to translate InputAddr to csrow for " 70693c2df58SDoug Thompson "address 0x%lx\n", (unsigned long)sys_addr); 70793c2df58SDoug Thompson return csrow; 70893c2df58SDoug Thompson } 709e2ce7255SDoug Thompson 710bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16); 7112da11654SDoug Thompson 7122da11654SDoug Thompson /* 7132da11654SDoug Thompson * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs 7142da11654SDoug Thompson * are ECC capable. 7152da11654SDoug Thompson */ 716d1ea71cdSBorislav Petkov static unsigned long determine_edac_cap(struct amd64_pvt *pvt) 7172da11654SDoug Thompson { 7181f6189edSDan Carpenter unsigned long edac_cap = EDAC_FLAG_NONE; 719d27f3a34SYazen Ghannam u8 bit; 7202da11654SDoug Thompson 721d27f3a34SYazen Ghannam if (pvt->umc) { 722d27f3a34SYazen Ghannam u8 i, umc_en_mask = 0, dimm_ecc_en_mask = 0; 723d27f3a34SYazen Ghannam 724d27f3a34SYazen Ghannam for (i = 0; i < NUM_UMCS; i++) { 725d27f3a34SYazen Ghannam if (!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT)) 726d27f3a34SYazen Ghannam continue; 727d27f3a34SYazen Ghannam 728d27f3a34SYazen Ghannam umc_en_mask |= BIT(i); 729d27f3a34SYazen Ghannam 730d27f3a34SYazen Ghannam /* UMC Configuration bit 12 (DimmEccEn) */ 731d27f3a34SYazen Ghannam if (pvt->umc[i].umc_cfg & BIT(12)) 732d27f3a34SYazen Ghannam dimm_ecc_en_mask |= BIT(i); 733d27f3a34SYazen Ghannam } 734d27f3a34SYazen Ghannam 735d27f3a34SYazen Ghannam if (umc_en_mask == dimm_ecc_en_mask) 736d27f3a34SYazen Ghannam edac_cap = EDAC_FLAG_SECDED; 737d27f3a34SYazen Ghannam } else { 738a4b4bedcSBorislav Petkov bit = (pvt->fam > 0xf || pvt->ext_model >= K8_REV_F) 7392da11654SDoug Thompson ? 19 7402da11654SDoug Thompson : 17; 7412da11654SDoug Thompson 742584fcff4SBorislav Petkov if (pvt->dclr0 & BIT(bit)) 7432da11654SDoug Thompson edac_cap = EDAC_FLAG_SECDED; 744d27f3a34SYazen Ghannam } 7452da11654SDoug Thompson 7462da11654SDoug Thompson return edac_cap; 7472da11654SDoug Thompson } 7482da11654SDoug Thompson 749d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *, u8); 7502da11654SDoug Thompson 751d1ea71cdSBorislav Petkov static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan) 75268798e17SBorislav Petkov { 753956b9ba1SJoe Perches edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr); 75468798e17SBorislav Petkov 755a597d2a5SAravind Gopalakrishnan if (pvt->dram_type == MEM_LRDDR3) { 756a597d2a5SAravind Gopalakrishnan u32 dcsm = pvt->csels[chan].csmasks[0]; 757a597d2a5SAravind Gopalakrishnan /* 758a597d2a5SAravind Gopalakrishnan * It's assumed all LRDIMMs in a DCT are going to be of 759a597d2a5SAravind Gopalakrishnan * same 'type' until proven otherwise. So, use a cs 760a597d2a5SAravind Gopalakrishnan * value of '0' here to get dcsm value. 761a597d2a5SAravind Gopalakrishnan */ 762a597d2a5SAravind Gopalakrishnan edac_dbg(1, " LRDIMM %dx rank multiply\n", (dcsm & 0x3)); 763a597d2a5SAravind Gopalakrishnan } 764a597d2a5SAravind Gopalakrishnan 765a597d2a5SAravind Gopalakrishnan edac_dbg(1, "All DIMMs support ECC:%s\n", 76668798e17SBorislav Petkov (dclr & BIT(19)) ? "yes" : "no"); 76768798e17SBorislav Petkov 768a597d2a5SAravind Gopalakrishnan 769956b9ba1SJoe Perches edac_dbg(1, " PAR/ERR parity: %s\n", 77068798e17SBorislav Petkov (dclr & BIT(8)) ? "enabled" : "disabled"); 77168798e17SBorislav Petkov 772a4b4bedcSBorislav Petkov if (pvt->fam == 0x10) 773956b9ba1SJoe Perches edac_dbg(1, " DCT 128bit mode width: %s\n", 77468798e17SBorislav Petkov (dclr & BIT(11)) ? "128b" : "64b"); 77568798e17SBorislav Petkov 776956b9ba1SJoe Perches edac_dbg(1, " x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n", 77768798e17SBorislav Petkov (dclr & BIT(12)) ? "yes" : "no", 77868798e17SBorislav Petkov (dclr & BIT(13)) ? "yes" : "no", 77968798e17SBorislav Petkov (dclr & BIT(14)) ? "yes" : "no", 78068798e17SBorislav Petkov (dclr & BIT(15)) ? "yes" : "no"); 78168798e17SBorislav Petkov } 78268798e17SBorislav Petkov 78307ed82efSYazen Ghannam static void debug_display_dimm_sizes_df(struct amd64_pvt *pvt, u8 ctrl) 78407ed82efSYazen Ghannam { 78507ed82efSYazen Ghannam u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases; 78607ed82efSYazen Ghannam int dimm, size0, size1; 78707ed82efSYazen Ghannam 78807ed82efSYazen Ghannam edac_printk(KERN_DEBUG, EDAC_MC, "UMC%d chip selects:\n", ctrl); 78907ed82efSYazen Ghannam 79007ed82efSYazen Ghannam for (dimm = 0; dimm < 4; dimm++) { 79107ed82efSYazen Ghannam size0 = 0; 79207ed82efSYazen Ghannam 79307ed82efSYazen Ghannam if (dcsb[dimm*2] & DCSB_CS_ENABLE) 79407ed82efSYazen Ghannam size0 = pvt->ops->dbam_to_cs(pvt, ctrl, 0, dimm); 79507ed82efSYazen Ghannam 79607ed82efSYazen Ghannam size1 = 0; 79707ed82efSYazen Ghannam if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE) 79807ed82efSYazen Ghannam size1 = pvt->ops->dbam_to_cs(pvt, ctrl, 0, dimm); 79907ed82efSYazen Ghannam 80007ed82efSYazen Ghannam amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n", 80107ed82efSYazen Ghannam dimm * 2, size0, 80207ed82efSYazen Ghannam dimm * 2 + 1, size1); 80307ed82efSYazen Ghannam } 80407ed82efSYazen Ghannam } 80507ed82efSYazen Ghannam 80607ed82efSYazen Ghannam static void __dump_misc_regs_df(struct amd64_pvt *pvt) 80707ed82efSYazen Ghannam { 80807ed82efSYazen Ghannam struct amd64_umc *umc; 80907ed82efSYazen Ghannam u32 i, tmp, umc_base; 81007ed82efSYazen Ghannam 81107ed82efSYazen Ghannam for (i = 0; i < NUM_UMCS; i++) { 81207ed82efSYazen Ghannam umc_base = get_umc_base(i); 81307ed82efSYazen Ghannam umc = &pvt->umc[i]; 81407ed82efSYazen Ghannam 81507ed82efSYazen Ghannam edac_dbg(1, "UMC%d DIMM cfg: 0x%x\n", i, umc->dimm_cfg); 81607ed82efSYazen Ghannam edac_dbg(1, "UMC%d UMC cfg: 0x%x\n", i, umc->umc_cfg); 81707ed82efSYazen Ghannam edac_dbg(1, "UMC%d SDP ctrl: 0x%x\n", i, umc->sdp_ctrl); 81807ed82efSYazen Ghannam edac_dbg(1, "UMC%d ECC ctrl: 0x%x\n", i, umc->ecc_ctrl); 81907ed82efSYazen Ghannam 82007ed82efSYazen Ghannam amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ECC_BAD_SYMBOL, &tmp); 82107ed82efSYazen Ghannam edac_dbg(1, "UMC%d ECC bad symbol: 0x%x\n", i, tmp); 82207ed82efSYazen Ghannam 82307ed82efSYazen Ghannam amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_UMC_CAP, &tmp); 82407ed82efSYazen Ghannam edac_dbg(1, "UMC%d UMC cap: 0x%x\n", i, tmp); 82507ed82efSYazen Ghannam edac_dbg(1, "UMC%d UMC cap high: 0x%x\n", i, umc->umc_cap_hi); 82607ed82efSYazen Ghannam 82707ed82efSYazen Ghannam edac_dbg(1, "UMC%d ECC capable: %s, ChipKill ECC capable: %s\n", 82807ed82efSYazen Ghannam i, (umc->umc_cap_hi & BIT(30)) ? "yes" : "no", 82907ed82efSYazen Ghannam (umc->umc_cap_hi & BIT(31)) ? "yes" : "no"); 83007ed82efSYazen Ghannam edac_dbg(1, "UMC%d All DIMMs support ECC: %s\n", 83107ed82efSYazen Ghannam i, (umc->umc_cfg & BIT(12)) ? "yes" : "no"); 83207ed82efSYazen Ghannam edac_dbg(1, "UMC%d x4 DIMMs present: %s\n", 83307ed82efSYazen Ghannam i, (umc->dimm_cfg & BIT(6)) ? "yes" : "no"); 83407ed82efSYazen Ghannam edac_dbg(1, "UMC%d x16 DIMMs present: %s\n", 83507ed82efSYazen Ghannam i, (umc->dimm_cfg & BIT(7)) ? "yes" : "no"); 83607ed82efSYazen Ghannam 83707ed82efSYazen Ghannam if (pvt->dram_type == MEM_LRDDR4) { 83807ed82efSYazen Ghannam amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ADDR_CFG, &tmp); 83907ed82efSYazen Ghannam edac_dbg(1, "UMC%d LRDIMM %dx rank multiply\n", 84007ed82efSYazen Ghannam i, 1 << ((tmp >> 4) & 0x3)); 84107ed82efSYazen Ghannam } 84207ed82efSYazen Ghannam 84307ed82efSYazen Ghannam debug_display_dimm_sizes_df(pvt, i); 84407ed82efSYazen Ghannam } 84507ed82efSYazen Ghannam 84607ed82efSYazen Ghannam edac_dbg(1, "F0x104 (DRAM Hole Address): 0x%08x, base: 0x%08x\n", 84707ed82efSYazen Ghannam pvt->dhar, dhar_base(pvt)); 84807ed82efSYazen Ghannam } 84907ed82efSYazen Ghannam 8502da11654SDoug Thompson /* Display and decode various NB registers for debug purposes. */ 85107ed82efSYazen Ghannam static void __dump_misc_regs(struct amd64_pvt *pvt) 8522da11654SDoug Thompson { 853956b9ba1SJoe Perches edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap); 8542da11654SDoug Thompson 855956b9ba1SJoe Perches edac_dbg(1, " NB two channel DRAM capable: %s\n", 8565980bb9cSBorislav Petkov (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no"); 85768798e17SBorislav Petkov 858956b9ba1SJoe Perches edac_dbg(1, " ECC capable: %s, ChipKill ECC capable: %s\n", 8595980bb9cSBorislav Petkov (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no", 8605980bb9cSBorislav Petkov (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no"); 86168798e17SBorislav Petkov 862d1ea71cdSBorislav Petkov debug_dump_dramcfg_low(pvt, pvt->dclr0, 0); 8632da11654SDoug Thompson 864956b9ba1SJoe Perches edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare); 8652da11654SDoug Thompson 866956b9ba1SJoe Perches edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n", 867bc21fa57SBorislav Petkov pvt->dhar, dhar_base(pvt), 868a4b4bedcSBorislav Petkov (pvt->fam == 0xf) ? k8_dhar_offset(pvt) 869bc21fa57SBorislav Petkov : f10_dhar_offset(pvt)); 8702da11654SDoug Thompson 871d1ea71cdSBorislav Petkov debug_display_dimm_sizes(pvt, 0); 8724d796364SBorislav Petkov 8734d796364SBorislav Petkov /* everything below this point is Fam10h and above */ 874a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) 8752da11654SDoug Thompson return; 8764d796364SBorislav Petkov 877d1ea71cdSBorislav Petkov debug_display_dimm_sizes(pvt, 1); 8782da11654SDoug Thompson 8798de1d91eSBorislav Petkov /* Only if NOT ganged does dclr1 have valid info */ 88068798e17SBorislav Petkov if (!dct_ganging_enabled(pvt)) 881d1ea71cdSBorislav Petkov debug_dump_dramcfg_low(pvt, pvt->dclr1, 1); 8822da11654SDoug Thompson } 8832da11654SDoug Thompson 88407ed82efSYazen Ghannam /* Display and decode various NB registers for debug purposes. */ 88507ed82efSYazen Ghannam static void dump_misc_regs(struct amd64_pvt *pvt) 88607ed82efSYazen Ghannam { 88707ed82efSYazen Ghannam if (pvt->umc) 88807ed82efSYazen Ghannam __dump_misc_regs_df(pvt); 88907ed82efSYazen Ghannam else 89007ed82efSYazen Ghannam __dump_misc_regs(pvt); 89107ed82efSYazen Ghannam 89207ed82efSYazen Ghannam edac_dbg(1, " DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no"); 89307ed82efSYazen Ghannam 89407ed82efSYazen Ghannam amd64_info("using %s syndromes.\n", 89507ed82efSYazen Ghannam ((pvt->ecc_sym_sz == 8) ? "x8" : "x4")); 89607ed82efSYazen Ghannam } 89707ed82efSYazen Ghannam 89894be4bffSDoug Thompson /* 89918b94f66SAravind Gopalakrishnan * See BKDG, F2x[1,0][5C:40], F2[1,0][6C:60] 90094be4bffSDoug Thompson */ 90111c75eadSBorislav Petkov static void prep_chip_selects(struct amd64_pvt *pvt) 90294be4bffSDoug Thompson { 90318b94f66SAravind Gopalakrishnan if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) { 90411c75eadSBorislav Petkov pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8; 90511c75eadSBorislav Petkov pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8; 906a597d2a5SAravind Gopalakrishnan } else if (pvt->fam == 0x15 && pvt->model == 0x30) { 90718b94f66SAravind Gopalakrishnan pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4; 90818b94f66SAravind Gopalakrishnan pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2; 9099d858bb1SBorislav Petkov } else { 91011c75eadSBorislav Petkov pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8; 91111c75eadSBorislav Petkov pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4; 9129d858bb1SBorislav Petkov } 91394be4bffSDoug Thompson } 91494be4bffSDoug Thompson 91594be4bffSDoug Thompson /* 91611c75eadSBorislav Petkov * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers 91794be4bffSDoug Thompson */ 918b2b0c605SBorislav Petkov static void read_dct_base_mask(struct amd64_pvt *pvt) 91994be4bffSDoug Thompson { 920b64ce7cdSYazen Ghannam int base_reg0, base_reg1, mask_reg0, mask_reg1, cs; 92194be4bffSDoug Thompson 92211c75eadSBorislav Petkov prep_chip_selects(pvt); 92394be4bffSDoug Thompson 924b64ce7cdSYazen Ghannam if (pvt->umc) { 925b64ce7cdSYazen Ghannam base_reg0 = get_umc_base(0) + UMCCH_BASE_ADDR; 926b64ce7cdSYazen Ghannam base_reg1 = get_umc_base(1) + UMCCH_BASE_ADDR; 927b64ce7cdSYazen Ghannam mask_reg0 = get_umc_base(0) + UMCCH_ADDR_MASK; 928b64ce7cdSYazen Ghannam mask_reg1 = get_umc_base(1) + UMCCH_ADDR_MASK; 929b64ce7cdSYazen Ghannam } else { 930b64ce7cdSYazen Ghannam base_reg0 = DCSB0; 931b64ce7cdSYazen Ghannam base_reg1 = DCSB1; 932b64ce7cdSYazen Ghannam mask_reg0 = DCSM0; 933b64ce7cdSYazen Ghannam mask_reg1 = DCSM1; 934b64ce7cdSYazen Ghannam } 935b64ce7cdSYazen Ghannam 93611c75eadSBorislav Petkov for_each_chip_select(cs, 0, pvt) { 937b64ce7cdSYazen Ghannam int reg0 = base_reg0 + (cs * 4); 938b64ce7cdSYazen Ghannam int reg1 = base_reg1 + (cs * 4); 93911c75eadSBorislav Petkov u32 *base0 = &pvt->csels[0].csbases[cs]; 94011c75eadSBorislav Petkov u32 *base1 = &pvt->csels[1].csbases[cs]; 941b2b0c605SBorislav Petkov 942b64ce7cdSYazen Ghannam if (pvt->umc) { 943b64ce7cdSYazen Ghannam if (!amd_smn_read(pvt->mc_node_id, reg0, base0)) 944b64ce7cdSYazen Ghannam edac_dbg(0, " DCSB0[%d]=0x%08x reg: 0x%x\n", 945b64ce7cdSYazen Ghannam cs, *base0, reg0); 946b64ce7cdSYazen Ghannam 947b64ce7cdSYazen Ghannam if (!amd_smn_read(pvt->mc_node_id, reg1, base1)) 948b64ce7cdSYazen Ghannam edac_dbg(0, " DCSB1[%d]=0x%08x reg: 0x%x\n", 949b64ce7cdSYazen Ghannam cs, *base1, reg1); 950b64ce7cdSYazen Ghannam } else { 9517981a28fSAravind Gopalakrishnan if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0)) 952956b9ba1SJoe Perches edac_dbg(0, " DCSB0[%d]=0x%08x reg: F2x%x\n", 95311c75eadSBorislav Petkov cs, *base0, reg0); 95494be4bffSDoug Thompson 9557981a28fSAravind Gopalakrishnan if (pvt->fam == 0xf) 95611c75eadSBorislav Petkov continue; 957b2b0c605SBorislav Petkov 9587981a28fSAravind Gopalakrishnan if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1)) 959956b9ba1SJoe Perches edac_dbg(0, " DCSB1[%d]=0x%08x reg: F2x%x\n", 9607981a28fSAravind Gopalakrishnan cs, *base1, (pvt->fam == 0x10) ? reg1 9617981a28fSAravind Gopalakrishnan : reg0); 96294be4bffSDoug Thompson } 963b64ce7cdSYazen Ghannam } 96494be4bffSDoug Thompson 96511c75eadSBorislav Petkov for_each_chip_select_mask(cs, 0, pvt) { 966b64ce7cdSYazen Ghannam int reg0 = mask_reg0 + (cs * 4); 967b64ce7cdSYazen Ghannam int reg1 = mask_reg1 + (cs * 4); 96811c75eadSBorislav Petkov u32 *mask0 = &pvt->csels[0].csmasks[cs]; 96911c75eadSBorislav Petkov u32 *mask1 = &pvt->csels[1].csmasks[cs]; 970b2b0c605SBorislav Petkov 971b64ce7cdSYazen Ghannam if (pvt->umc) { 972b64ce7cdSYazen Ghannam if (!amd_smn_read(pvt->mc_node_id, reg0, mask0)) 973b64ce7cdSYazen Ghannam edac_dbg(0, " DCSM0[%d]=0x%08x reg: 0x%x\n", 974b64ce7cdSYazen Ghannam cs, *mask0, reg0); 975b64ce7cdSYazen Ghannam 976b64ce7cdSYazen Ghannam if (!amd_smn_read(pvt->mc_node_id, reg1, mask1)) 977b64ce7cdSYazen Ghannam edac_dbg(0, " DCSM1[%d]=0x%08x reg: 0x%x\n", 978b64ce7cdSYazen Ghannam cs, *mask1, reg1); 979b64ce7cdSYazen Ghannam } else { 9807981a28fSAravind Gopalakrishnan if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0)) 981956b9ba1SJoe Perches edac_dbg(0, " DCSM0[%d]=0x%08x reg: F2x%x\n", 98211c75eadSBorislav Petkov cs, *mask0, reg0); 98394be4bffSDoug Thompson 9847981a28fSAravind Gopalakrishnan if (pvt->fam == 0xf) 98511c75eadSBorislav Petkov continue; 986b2b0c605SBorislav Petkov 9877981a28fSAravind Gopalakrishnan if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1)) 988956b9ba1SJoe Perches edac_dbg(0, " DCSM1[%d]=0x%08x reg: F2x%x\n", 9897981a28fSAravind Gopalakrishnan cs, *mask1, (pvt->fam == 0x10) ? reg1 9907981a28fSAravind Gopalakrishnan : reg0); 99194be4bffSDoug Thompson } 9926ba5dcdcSBorislav Petkov } 993b64ce7cdSYazen Ghannam } 99494be4bffSDoug Thompson 995a597d2a5SAravind Gopalakrishnan static void determine_memory_type(struct amd64_pvt *pvt) 99694be4bffSDoug Thompson { 997a597d2a5SAravind Gopalakrishnan u32 dram_ctrl, dcsm; 99894be4bffSDoug Thompson 999a597d2a5SAravind Gopalakrishnan switch (pvt->fam) { 1000a597d2a5SAravind Gopalakrishnan case 0xf: 1001a597d2a5SAravind Gopalakrishnan if (pvt->ext_model >= K8_REV_F) 1002a597d2a5SAravind Gopalakrishnan goto ddr3; 1003a597d2a5SAravind Gopalakrishnan 1004a597d2a5SAravind Gopalakrishnan pvt->dram_type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR; 1005a597d2a5SAravind Gopalakrishnan return; 1006a597d2a5SAravind Gopalakrishnan 1007a597d2a5SAravind Gopalakrishnan case 0x10: 10086b4c0bdeSBorislav Petkov if (pvt->dchr0 & DDR3_MODE) 1009a597d2a5SAravind Gopalakrishnan goto ddr3; 1010a597d2a5SAravind Gopalakrishnan 1011a597d2a5SAravind Gopalakrishnan pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2; 1012a597d2a5SAravind Gopalakrishnan return; 1013a597d2a5SAravind Gopalakrishnan 1014a597d2a5SAravind Gopalakrishnan case 0x15: 1015a597d2a5SAravind Gopalakrishnan if (pvt->model < 0x60) 1016a597d2a5SAravind Gopalakrishnan goto ddr3; 1017a597d2a5SAravind Gopalakrishnan 1018a597d2a5SAravind Gopalakrishnan /* 1019a597d2a5SAravind Gopalakrishnan * Model 0x60h needs special handling: 1020a597d2a5SAravind Gopalakrishnan * 1021a597d2a5SAravind Gopalakrishnan * We use a Chip Select value of '0' to obtain dcsm. 1022a597d2a5SAravind Gopalakrishnan * Theoretically, it is possible to populate LRDIMMs of different 1023a597d2a5SAravind Gopalakrishnan * 'Rank' value on a DCT. But this is not the common case. So, 1024a597d2a5SAravind Gopalakrishnan * it's reasonable to assume all DIMMs are going to be of same 1025a597d2a5SAravind Gopalakrishnan * 'type' until proven otherwise. 1026a597d2a5SAravind Gopalakrishnan */ 1027a597d2a5SAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DRAM_CONTROL, &dram_ctrl); 1028a597d2a5SAravind Gopalakrishnan dcsm = pvt->csels[0].csmasks[0]; 1029a597d2a5SAravind Gopalakrishnan 1030a597d2a5SAravind Gopalakrishnan if (((dram_ctrl >> 8) & 0x7) == 0x2) 1031a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_DDR4; 1032a597d2a5SAravind Gopalakrishnan else if (pvt->dclr0 & BIT(16)) 1033a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_DDR3; 1034a597d2a5SAravind Gopalakrishnan else if (dcsm & 0x3) 1035a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_LRDDR3; 10366b4c0bdeSBorislav Petkov else 1037a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_RDDR3; 1038a597d2a5SAravind Gopalakrishnan 1039a597d2a5SAravind Gopalakrishnan return; 1040a597d2a5SAravind Gopalakrishnan 1041a597d2a5SAravind Gopalakrishnan case 0x16: 1042a597d2a5SAravind Gopalakrishnan goto ddr3; 1043a597d2a5SAravind Gopalakrishnan 1044b64ce7cdSYazen Ghannam case 0x17: 1045b64ce7cdSYazen Ghannam if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(5)) 1046b64ce7cdSYazen Ghannam pvt->dram_type = MEM_LRDDR4; 1047b64ce7cdSYazen Ghannam else if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(4)) 1048b64ce7cdSYazen Ghannam pvt->dram_type = MEM_RDDR4; 1049b64ce7cdSYazen Ghannam else 1050b64ce7cdSYazen Ghannam pvt->dram_type = MEM_DDR4; 1051b64ce7cdSYazen Ghannam return; 1052b64ce7cdSYazen Ghannam 1053a597d2a5SAravind Gopalakrishnan default: 1054a597d2a5SAravind Gopalakrishnan WARN(1, KERN_ERR "%s: Family??? 0x%x\n", __func__, pvt->fam); 1055a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_EMPTY; 105694be4bffSDoug Thompson } 1057a597d2a5SAravind Gopalakrishnan return; 105894be4bffSDoug Thompson 1059a597d2a5SAravind Gopalakrishnan ddr3: 1060a597d2a5SAravind Gopalakrishnan pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3; 106194be4bffSDoug Thompson } 106294be4bffSDoug Thompson 1063cb328507SBorislav Petkov /* Get the number of DCT channels the memory controller is using. */ 1064ddff876dSDoug Thompson static int k8_early_channel_count(struct amd64_pvt *pvt) 1065ddff876dSDoug Thompson { 1066cb328507SBorislav Petkov int flag; 1067ddff876dSDoug Thompson 10689f56da0eSBorislav Petkov if (pvt->ext_model >= K8_REV_F) 1069ddff876dSDoug Thompson /* RevF (NPT) and later */ 107041d8bfabSBorislav Petkov flag = pvt->dclr0 & WIDTH_128; 10719f56da0eSBorislav Petkov else 1072ddff876dSDoug Thompson /* RevE and earlier */ 1073ddff876dSDoug Thompson flag = pvt->dclr0 & REVE_WIDTH_128; 1074ddff876dSDoug Thompson 1075ddff876dSDoug Thompson /* not used */ 1076ddff876dSDoug Thompson pvt->dclr1 = 0; 1077ddff876dSDoug Thompson 1078ddff876dSDoug Thompson return (flag) ? 2 : 1; 1079ddff876dSDoug Thompson } 1080ddff876dSDoug Thompson 108170046624SBorislav Petkov /* On F10h and later ErrAddr is MC4_ADDR[47:1] */ 1082a4b4bedcSBorislav Petkov static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m) 1083ddff876dSDoug Thompson { 10842ec591acSBorislav Petkov u16 mce_nid = amd_get_nb_id(m->extcpu); 10852ec591acSBorislav Petkov struct mem_ctl_info *mci; 108670046624SBorislav Petkov u8 start_bit = 1; 108770046624SBorislav Petkov u8 end_bit = 47; 10882ec591acSBorislav Petkov u64 addr; 10892ec591acSBorislav Petkov 10902ec591acSBorislav Petkov mci = edac_mc_find(mce_nid); 10912ec591acSBorislav Petkov if (!mci) 10922ec591acSBorislav Petkov return 0; 10932ec591acSBorislav Petkov 10942ec591acSBorislav Petkov pvt = mci->pvt_info; 109570046624SBorislav Petkov 1096a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) { 109770046624SBorislav Petkov start_bit = 3; 109870046624SBorislav Petkov end_bit = 39; 109970046624SBorislav Petkov } 110070046624SBorislav Petkov 110110ef6b0dSChen, Gong addr = m->addr & GENMASK_ULL(end_bit, start_bit); 1102c1ae6830SBorislav Petkov 1103c1ae6830SBorislav Petkov /* 1104c1ae6830SBorislav Petkov * Erratum 637 workaround 1105c1ae6830SBorislav Petkov */ 1106a4b4bedcSBorislav Petkov if (pvt->fam == 0x15) { 1107c1ae6830SBorislav Petkov u64 cc6_base, tmp_addr; 1108c1ae6830SBorislav Petkov u32 tmp; 11098b84c8dfSDaniel J Blueman u8 intlv_en; 1110c1ae6830SBorislav Petkov 111110ef6b0dSChen, Gong if ((addr & GENMASK_ULL(47, 24)) >> 24 != 0x00fdf7) 1112c1ae6830SBorislav Petkov return addr; 1113c1ae6830SBorislav Petkov 1114c1ae6830SBorislav Petkov 1115c1ae6830SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp); 1116c1ae6830SBorislav Petkov intlv_en = tmp >> 21 & 0x7; 1117c1ae6830SBorislav Petkov 1118c1ae6830SBorislav Petkov /* add [47:27] + 3 trailing bits */ 111910ef6b0dSChen, Gong cc6_base = (tmp & GENMASK_ULL(20, 0)) << 3; 1120c1ae6830SBorislav Petkov 1121c1ae6830SBorislav Petkov /* reverse and add DramIntlvEn */ 1122c1ae6830SBorislav Petkov cc6_base |= intlv_en ^ 0x7; 1123c1ae6830SBorislav Petkov 1124c1ae6830SBorislav Petkov /* pin at [47:24] */ 1125c1ae6830SBorislav Petkov cc6_base <<= 24; 1126c1ae6830SBorislav Petkov 1127c1ae6830SBorislav Petkov if (!intlv_en) 112810ef6b0dSChen, Gong return cc6_base | (addr & GENMASK_ULL(23, 0)); 1129c1ae6830SBorislav Petkov 1130c1ae6830SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp); 1131c1ae6830SBorislav Petkov 1132c1ae6830SBorislav Petkov /* faster log2 */ 113310ef6b0dSChen, Gong tmp_addr = (addr & GENMASK_ULL(23, 12)) << __fls(intlv_en + 1); 1134c1ae6830SBorislav Petkov 1135c1ae6830SBorislav Petkov /* OR DramIntlvSel into bits [14:12] */ 113610ef6b0dSChen, Gong tmp_addr |= (tmp & GENMASK_ULL(23, 21)) >> 9; 1137c1ae6830SBorislav Petkov 1138c1ae6830SBorislav Petkov /* add remaining [11:0] bits from original MC4_ADDR */ 113910ef6b0dSChen, Gong tmp_addr |= addr & GENMASK_ULL(11, 0); 1140c1ae6830SBorislav Petkov 1141c1ae6830SBorislav Petkov return cc6_base | tmp_addr; 1142c1ae6830SBorislav Petkov } 1143c1ae6830SBorislav Petkov 1144c1ae6830SBorislav Petkov return addr; 1145ddff876dSDoug Thompson } 1146ddff876dSDoug Thompson 1147e2c0bffeSDaniel J Blueman static struct pci_dev *pci_get_related_function(unsigned int vendor, 1148e2c0bffeSDaniel J Blueman unsigned int device, 1149e2c0bffeSDaniel J Blueman struct pci_dev *related) 1150e2c0bffeSDaniel J Blueman { 1151e2c0bffeSDaniel J Blueman struct pci_dev *dev = NULL; 1152e2c0bffeSDaniel J Blueman 1153e2c0bffeSDaniel J Blueman while ((dev = pci_get_device(vendor, device, dev))) { 1154e2c0bffeSDaniel J Blueman if (pci_domain_nr(dev->bus) == pci_domain_nr(related->bus) && 1155e2c0bffeSDaniel J Blueman (dev->bus->number == related->bus->number) && 1156e2c0bffeSDaniel J Blueman (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn))) 1157e2c0bffeSDaniel J Blueman break; 1158e2c0bffeSDaniel J Blueman } 1159e2c0bffeSDaniel J Blueman 1160e2c0bffeSDaniel J Blueman return dev; 1161e2c0bffeSDaniel J Blueman } 1162e2c0bffeSDaniel J Blueman 11637f19bf75SBorislav Petkov static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range) 1164ddff876dSDoug Thompson { 1165e2c0bffeSDaniel J Blueman struct amd_northbridge *nb; 116618b94f66SAravind Gopalakrishnan struct pci_dev *f1 = NULL; 116718b94f66SAravind Gopalakrishnan unsigned int pci_func; 116871d2a32eSBorislav Petkov int off = range << 3; 1169e2c0bffeSDaniel J Blueman u32 llim; 1170ddff876dSDoug Thompson 11717f19bf75SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off, &pvt->ranges[range].base.lo); 11727f19bf75SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo); 1173ddff876dSDoug Thompson 117418b94f66SAravind Gopalakrishnan if (pvt->fam == 0xf) 11757f19bf75SBorislav Petkov return; 1176ddff876dSDoug Thompson 11777f19bf75SBorislav Petkov if (!dram_rw(pvt, range)) 11787f19bf75SBorislav Petkov return; 1179ddff876dSDoug Thompson 11807f19bf75SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off, &pvt->ranges[range].base.hi); 11817f19bf75SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi); 1182f08e457cSBorislav Petkov 1183e2c0bffeSDaniel J Blueman /* F15h: factor in CC6 save area by reading dst node's limit reg */ 118418b94f66SAravind Gopalakrishnan if (pvt->fam != 0x15) 1185e2c0bffeSDaniel J Blueman return; 1186f08e457cSBorislav Petkov 1187e2c0bffeSDaniel J Blueman nb = node_to_amd_nb(dram_dst_node(pvt, range)); 1188e2c0bffeSDaniel J Blueman if (WARN_ON(!nb)) 1189e2c0bffeSDaniel J Blueman return; 1190e2c0bffeSDaniel J Blueman 1191a597d2a5SAravind Gopalakrishnan if (pvt->model == 0x60) 1192a597d2a5SAravind Gopalakrishnan pci_func = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1; 1193a597d2a5SAravind Gopalakrishnan else if (pvt->model == 0x30) 1194a597d2a5SAravind Gopalakrishnan pci_func = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1; 1195a597d2a5SAravind Gopalakrishnan else 1196a597d2a5SAravind Gopalakrishnan pci_func = PCI_DEVICE_ID_AMD_15H_NB_F1; 119718b94f66SAravind Gopalakrishnan 119818b94f66SAravind Gopalakrishnan f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc); 1199f08e457cSBorislav Petkov if (WARN_ON(!f1)) 1200f08e457cSBorislav Petkov return; 1201f08e457cSBorislav Petkov 1202f08e457cSBorislav Petkov amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim); 1203f08e457cSBorislav Petkov 120410ef6b0dSChen, Gong pvt->ranges[range].lim.lo &= GENMASK_ULL(15, 0); 1205f08e457cSBorislav Petkov 1206f08e457cSBorislav Petkov /* {[39:27],111b} */ 1207f08e457cSBorislav Petkov pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16; 1208f08e457cSBorislav Petkov 120910ef6b0dSChen, Gong pvt->ranges[range].lim.hi &= GENMASK_ULL(7, 0); 1210f08e457cSBorislav Petkov 1211f08e457cSBorislav Petkov /* [47:40] */ 1212f08e457cSBorislav Petkov pvt->ranges[range].lim.hi |= llim >> 13; 1213f08e457cSBorislav Petkov 1214f08e457cSBorislav Petkov pci_dev_put(f1); 1215f08e457cSBorislav Petkov } 1216ddff876dSDoug Thompson 1217f192c7b1SBorislav Petkov static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr, 121833ca0643SBorislav Petkov struct err_info *err) 1219ddff876dSDoug Thompson { 1220f192c7b1SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 1221ddff876dSDoug Thompson 122233ca0643SBorislav Petkov error_address_to_page_and_offset(sys_addr, err); 1223ab5a503cSMauro Carvalho Chehab 1224ab5a503cSMauro Carvalho Chehab /* 1225ab5a503cSMauro Carvalho Chehab * Find out which node the error address belongs to. This may be 1226ab5a503cSMauro Carvalho Chehab * different from the node that detected the error. 1227ab5a503cSMauro Carvalho Chehab */ 122833ca0643SBorislav Petkov err->src_mci = find_mc_by_sys_addr(mci, sys_addr); 122933ca0643SBorislav Petkov if (!err->src_mci) { 1230ab5a503cSMauro Carvalho Chehab amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n", 1231ab5a503cSMauro Carvalho Chehab (unsigned long)sys_addr); 123233ca0643SBorislav Petkov err->err_code = ERR_NODE; 1233ab5a503cSMauro Carvalho Chehab return; 1234ab5a503cSMauro Carvalho Chehab } 1235ab5a503cSMauro Carvalho Chehab 1236ab5a503cSMauro Carvalho Chehab /* Now map the sys_addr to a CSROW */ 123733ca0643SBorislav Petkov err->csrow = sys_addr_to_csrow(err->src_mci, sys_addr); 123833ca0643SBorislav Petkov if (err->csrow < 0) { 123933ca0643SBorislav Petkov err->err_code = ERR_CSROW; 1240ab5a503cSMauro Carvalho Chehab return; 1241ab5a503cSMauro Carvalho Chehab } 1242ab5a503cSMauro Carvalho Chehab 1243ddff876dSDoug Thompson /* CHIPKILL enabled */ 1244f192c7b1SBorislav Petkov if (pvt->nbcfg & NBCFG_CHIPKILL) { 124533ca0643SBorislav Petkov err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome); 124633ca0643SBorislav Petkov if (err->channel < 0) { 1247ddff876dSDoug Thompson /* 1248ddff876dSDoug Thompson * Syndrome didn't map, so we don't know which of the 1249ddff876dSDoug Thompson * 2 DIMMs is in error. So we need to ID 'both' of them 1250ddff876dSDoug Thompson * as suspect. 1251ddff876dSDoug Thompson */ 125233ca0643SBorislav Petkov amd64_mc_warn(err->src_mci, "unknown syndrome 0x%04x - " 1253ab5a503cSMauro Carvalho Chehab "possible error reporting race\n", 125433ca0643SBorislav Petkov err->syndrome); 125533ca0643SBorislav Petkov err->err_code = ERR_CHANNEL; 1256ddff876dSDoug Thompson return; 1257ddff876dSDoug Thompson } 1258ddff876dSDoug Thompson } else { 1259ddff876dSDoug Thompson /* 1260ddff876dSDoug Thompson * non-chipkill ecc mode 1261ddff876dSDoug Thompson * 1262ddff876dSDoug Thompson * The k8 documentation is unclear about how to determine the 1263ddff876dSDoug Thompson * channel number when using non-chipkill memory. This method 1264ddff876dSDoug Thompson * was obtained from email communication with someone at AMD. 1265ddff876dSDoug Thompson * (Wish the email was placed in this comment - norsk) 1266ddff876dSDoug Thompson */ 126733ca0643SBorislav Petkov err->channel = ((sys_addr & BIT(3)) != 0); 1268ddff876dSDoug Thompson } 1269ddff876dSDoug Thompson } 1270ddff876dSDoug Thompson 127141d8bfabSBorislav Petkov static int ddr2_cs_size(unsigned i, bool dct_width) 1272ddff876dSDoug Thompson { 127341d8bfabSBorislav Petkov unsigned shift = 0; 1274ddff876dSDoug Thompson 127541d8bfabSBorislav Petkov if (i <= 2) 127641d8bfabSBorislav Petkov shift = i; 127741d8bfabSBorislav Petkov else if (!(i & 0x1)) 127841d8bfabSBorislav Petkov shift = i >> 1; 12791433eb99SBorislav Petkov else 128041d8bfabSBorislav Petkov shift = (i + 1) >> 1; 1281ddff876dSDoug Thompson 128241d8bfabSBorislav Petkov return 128 << (shift + !!dct_width); 128341d8bfabSBorislav Petkov } 128441d8bfabSBorislav Petkov 128541d8bfabSBorislav Petkov static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 1286a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 128741d8bfabSBorislav Petkov { 128841d8bfabSBorislav Petkov u32 dclr = dct ? pvt->dclr1 : pvt->dclr0; 128941d8bfabSBorislav Petkov 129041d8bfabSBorislav Petkov if (pvt->ext_model >= K8_REV_F) { 129141d8bfabSBorislav Petkov WARN_ON(cs_mode > 11); 129241d8bfabSBorislav Petkov return ddr2_cs_size(cs_mode, dclr & WIDTH_128); 129341d8bfabSBorislav Petkov } 129441d8bfabSBorislav Petkov else if (pvt->ext_model >= K8_REV_D) { 129511b0a314SBorislav Petkov unsigned diff; 129641d8bfabSBorislav Petkov WARN_ON(cs_mode > 10); 129741d8bfabSBorislav Petkov 129811b0a314SBorislav Petkov /* 129911b0a314SBorislav Petkov * the below calculation, besides trying to win an obfuscated C 130011b0a314SBorislav Petkov * contest, maps cs_mode values to DIMM chip select sizes. The 130111b0a314SBorislav Petkov * mappings are: 130211b0a314SBorislav Petkov * 130311b0a314SBorislav Petkov * cs_mode CS size (mb) 130411b0a314SBorislav Petkov * ======= ============ 130511b0a314SBorislav Petkov * 0 32 130611b0a314SBorislav Petkov * 1 64 130711b0a314SBorislav Petkov * 2 128 130811b0a314SBorislav Petkov * 3 128 130911b0a314SBorislav Petkov * 4 256 131011b0a314SBorislav Petkov * 5 512 131111b0a314SBorislav Petkov * 6 256 131211b0a314SBorislav Petkov * 7 512 131311b0a314SBorislav Petkov * 8 1024 131411b0a314SBorislav Petkov * 9 1024 131511b0a314SBorislav Petkov * 10 2048 131611b0a314SBorislav Petkov * 131711b0a314SBorislav Petkov * Basically, it calculates a value with which to shift the 131811b0a314SBorislav Petkov * smallest CS size of 32MB. 131911b0a314SBorislav Petkov * 132011b0a314SBorislav Petkov * ddr[23]_cs_size have a similar purpose. 132111b0a314SBorislav Petkov */ 132211b0a314SBorislav Petkov diff = cs_mode/3 + (unsigned)(cs_mode > 5); 132311b0a314SBorislav Petkov 132411b0a314SBorislav Petkov return 32 << (cs_mode - diff); 132541d8bfabSBorislav Petkov } 132641d8bfabSBorislav Petkov else { 132741d8bfabSBorislav Petkov WARN_ON(cs_mode > 6); 132841d8bfabSBorislav Petkov return 32 << cs_mode; 132941d8bfabSBorislav Petkov } 1330ddff876dSDoug Thompson } 1331ddff876dSDoug Thompson 13321afd3c98SDoug Thompson /* 13331afd3c98SDoug Thompson * Get the number of DCT channels in use. 13341afd3c98SDoug Thompson * 13351afd3c98SDoug Thompson * Return: 13361afd3c98SDoug Thompson * number of Memory Channels in operation 13371afd3c98SDoug Thompson * Pass back: 13381afd3c98SDoug Thompson * contents of the DCL0_LOW register 13391afd3c98SDoug Thompson */ 13407d20d14dSBorislav Petkov static int f1x_early_channel_count(struct amd64_pvt *pvt) 13411afd3c98SDoug Thompson { 13426ba5dcdcSBorislav Petkov int i, j, channels = 0; 1343ddff876dSDoug Thompson 13447d20d14dSBorislav Petkov /* On F10h, if we are in 128 bit mode, then we are using 2 channels */ 1345a4b4bedcSBorislav Petkov if (pvt->fam == 0x10 && (pvt->dclr0 & WIDTH_128)) 13467d20d14dSBorislav Petkov return 2; 13471afd3c98SDoug Thompson 13481afd3c98SDoug Thompson /* 1349d16149e8SBorislav Petkov * Need to check if in unganged mode: In such, there are 2 channels, 1350d16149e8SBorislav Petkov * but they are not in 128 bit mode and thus the above 'dclr0' status 1351d16149e8SBorislav Petkov * bit will be OFF. 13521afd3c98SDoug Thompson * 13531afd3c98SDoug Thompson * Need to check DCT0[0] and DCT1[0] to see if only one of them has 13541afd3c98SDoug Thompson * their CSEnable bit on. If so, then SINGLE DIMM case. 13551afd3c98SDoug Thompson */ 1356956b9ba1SJoe Perches edac_dbg(0, "Data width is not 128 bits - need more decoding\n"); 13571afd3c98SDoug Thompson 13581afd3c98SDoug Thompson /* 13591afd3c98SDoug Thompson * Check DRAM Bank Address Mapping values for each DIMM to see if there 13601afd3c98SDoug Thompson * is more than just one DIMM present in unganged mode. Need to check 13611afd3c98SDoug Thompson * both controllers since DIMMs can be placed in either one. 13621afd3c98SDoug Thompson */ 1363525a1b20SBorislav Petkov for (i = 0; i < 2; i++) { 1364525a1b20SBorislav Petkov u32 dbam = (i ? pvt->dbam1 : pvt->dbam0); 13651afd3c98SDoug Thompson 136657a30854SWan Wei for (j = 0; j < 4; j++) { 136757a30854SWan Wei if (DBAM_DIMM(j, dbam) > 0) { 13681afd3c98SDoug Thompson channels++; 136957a30854SWan Wei break; 13701afd3c98SDoug Thompson } 137157a30854SWan Wei } 137257a30854SWan Wei } 13731afd3c98SDoug Thompson 1374d16149e8SBorislav Petkov if (channels > 2) 1375d16149e8SBorislav Petkov channels = 2; 1376d16149e8SBorislav Petkov 137724f9a7feSBorislav Petkov amd64_info("MCT channel count: %d\n", channels); 13781afd3c98SDoug Thompson 13791afd3c98SDoug Thompson return channels; 13801afd3c98SDoug Thompson } 13811afd3c98SDoug Thompson 1382f1cbbec9SYazen Ghannam static int f17_early_channel_count(struct amd64_pvt *pvt) 1383f1cbbec9SYazen Ghannam { 1384f1cbbec9SYazen Ghannam int i, channels = 0; 1385f1cbbec9SYazen Ghannam 1386f1cbbec9SYazen Ghannam /* SDP Control bit 31 (SdpInit) is clear for unused UMC channels */ 1387f1cbbec9SYazen Ghannam for (i = 0; i < NUM_UMCS; i++) 1388f1cbbec9SYazen Ghannam channels += !!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT); 1389f1cbbec9SYazen Ghannam 1390f1cbbec9SYazen Ghannam amd64_info("MCT channel count: %d\n", channels); 1391f1cbbec9SYazen Ghannam 1392f1cbbec9SYazen Ghannam return channels; 1393f1cbbec9SYazen Ghannam } 1394f1cbbec9SYazen Ghannam 139541d8bfabSBorislav Petkov static int ddr3_cs_size(unsigned i, bool dct_width) 13961afd3c98SDoug Thompson { 139741d8bfabSBorislav Petkov unsigned shift = 0; 139841d8bfabSBorislav Petkov int cs_size = 0; 139941d8bfabSBorislav Petkov 140041d8bfabSBorislav Petkov if (i == 0 || i == 3 || i == 4) 140141d8bfabSBorislav Petkov cs_size = -1; 140241d8bfabSBorislav Petkov else if (i <= 2) 140341d8bfabSBorislav Petkov shift = i; 140441d8bfabSBorislav Petkov else if (i == 12) 140541d8bfabSBorislav Petkov shift = 7; 140641d8bfabSBorislav Petkov else if (!(i & 0x1)) 140741d8bfabSBorislav Petkov shift = i >> 1; 140841d8bfabSBorislav Petkov else 140941d8bfabSBorislav Petkov shift = (i + 1) >> 1; 141041d8bfabSBorislav Petkov 141141d8bfabSBorislav Petkov if (cs_size != -1) 141241d8bfabSBorislav Petkov cs_size = (128 * (1 << !!dct_width)) << shift; 141341d8bfabSBorislav Petkov 141441d8bfabSBorislav Petkov return cs_size; 141541d8bfabSBorislav Petkov } 141641d8bfabSBorislav Petkov 1417a597d2a5SAravind Gopalakrishnan static int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply) 1418a597d2a5SAravind Gopalakrishnan { 1419a597d2a5SAravind Gopalakrishnan unsigned shift = 0; 1420a597d2a5SAravind Gopalakrishnan int cs_size = 0; 1421a597d2a5SAravind Gopalakrishnan 1422a597d2a5SAravind Gopalakrishnan if (i < 4 || i == 6) 1423a597d2a5SAravind Gopalakrishnan cs_size = -1; 1424a597d2a5SAravind Gopalakrishnan else if (i == 12) 1425a597d2a5SAravind Gopalakrishnan shift = 7; 1426a597d2a5SAravind Gopalakrishnan else if (!(i & 0x1)) 1427a597d2a5SAravind Gopalakrishnan shift = i >> 1; 1428a597d2a5SAravind Gopalakrishnan else 1429a597d2a5SAravind Gopalakrishnan shift = (i + 1) >> 1; 1430a597d2a5SAravind Gopalakrishnan 1431a597d2a5SAravind Gopalakrishnan if (cs_size != -1) 1432a597d2a5SAravind Gopalakrishnan cs_size = rank_multiply * (128 << shift); 1433a597d2a5SAravind Gopalakrishnan 1434a597d2a5SAravind Gopalakrishnan return cs_size; 1435a597d2a5SAravind Gopalakrishnan } 1436a597d2a5SAravind Gopalakrishnan 1437a597d2a5SAravind Gopalakrishnan static int ddr4_cs_size(unsigned i) 1438a597d2a5SAravind Gopalakrishnan { 1439a597d2a5SAravind Gopalakrishnan int cs_size = 0; 1440a597d2a5SAravind Gopalakrishnan 1441a597d2a5SAravind Gopalakrishnan if (i == 0) 1442a597d2a5SAravind Gopalakrishnan cs_size = -1; 1443a597d2a5SAravind Gopalakrishnan else if (i == 1) 1444a597d2a5SAravind Gopalakrishnan cs_size = 1024; 1445a597d2a5SAravind Gopalakrishnan else 1446a597d2a5SAravind Gopalakrishnan /* Min cs_size = 1G */ 1447a597d2a5SAravind Gopalakrishnan cs_size = 1024 * (1 << (i >> 1)); 1448a597d2a5SAravind Gopalakrishnan 1449a597d2a5SAravind Gopalakrishnan return cs_size; 1450a597d2a5SAravind Gopalakrishnan } 1451a597d2a5SAravind Gopalakrishnan 145241d8bfabSBorislav Petkov static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 1453a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 145441d8bfabSBorislav Petkov { 145541d8bfabSBorislav Petkov u32 dclr = dct ? pvt->dclr1 : pvt->dclr0; 145641d8bfabSBorislav Petkov 145741d8bfabSBorislav Petkov WARN_ON(cs_mode > 11); 14581433eb99SBorislav Petkov 14591433eb99SBorislav Petkov if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE) 146041d8bfabSBorislav Petkov return ddr3_cs_size(cs_mode, dclr & WIDTH_128); 14611433eb99SBorislav Petkov else 146241d8bfabSBorislav Petkov return ddr2_cs_size(cs_mode, dclr & WIDTH_128); 146341d8bfabSBorislav Petkov } 14641433eb99SBorislav Petkov 146541d8bfabSBorislav Petkov /* 146641d8bfabSBorislav Petkov * F15h supports only 64bit DCT interfaces 146741d8bfabSBorislav Petkov */ 146841d8bfabSBorislav Petkov static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 1469a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 147041d8bfabSBorislav Petkov { 147141d8bfabSBorislav Petkov WARN_ON(cs_mode > 12); 147241d8bfabSBorislav Petkov 147341d8bfabSBorislav Petkov return ddr3_cs_size(cs_mode, false); 14741afd3c98SDoug Thompson } 14751afd3c98SDoug Thompson 1476a597d2a5SAravind Gopalakrishnan /* F15h M60h supports DDR4 mapping as well.. */ 1477a597d2a5SAravind Gopalakrishnan static int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 1478a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 1479a597d2a5SAravind Gopalakrishnan { 1480a597d2a5SAravind Gopalakrishnan int cs_size; 1481a597d2a5SAravind Gopalakrishnan u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr]; 1482a597d2a5SAravind Gopalakrishnan 1483a597d2a5SAravind Gopalakrishnan WARN_ON(cs_mode > 12); 1484a597d2a5SAravind Gopalakrishnan 1485a597d2a5SAravind Gopalakrishnan if (pvt->dram_type == MEM_DDR4) { 1486a597d2a5SAravind Gopalakrishnan if (cs_mode > 9) 1487a597d2a5SAravind Gopalakrishnan return -1; 1488a597d2a5SAravind Gopalakrishnan 1489a597d2a5SAravind Gopalakrishnan cs_size = ddr4_cs_size(cs_mode); 1490a597d2a5SAravind Gopalakrishnan } else if (pvt->dram_type == MEM_LRDDR3) { 1491a597d2a5SAravind Gopalakrishnan unsigned rank_multiply = dcsm & 0xf; 1492a597d2a5SAravind Gopalakrishnan 1493a597d2a5SAravind Gopalakrishnan if (rank_multiply == 3) 1494a597d2a5SAravind Gopalakrishnan rank_multiply = 4; 1495a597d2a5SAravind Gopalakrishnan cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply); 1496a597d2a5SAravind Gopalakrishnan } else { 1497a597d2a5SAravind Gopalakrishnan /* Minimum cs size is 512mb for F15hM60h*/ 1498a597d2a5SAravind Gopalakrishnan if (cs_mode == 0x1) 1499a597d2a5SAravind Gopalakrishnan return -1; 1500a597d2a5SAravind Gopalakrishnan 1501a597d2a5SAravind Gopalakrishnan cs_size = ddr3_cs_size(cs_mode, false); 1502a597d2a5SAravind Gopalakrishnan } 1503a597d2a5SAravind Gopalakrishnan 1504a597d2a5SAravind Gopalakrishnan return cs_size; 1505a597d2a5SAravind Gopalakrishnan } 1506a597d2a5SAravind Gopalakrishnan 150794c1acf2SAravind Gopalakrishnan /* 150818b94f66SAravind Gopalakrishnan * F16h and F15h model 30h have only limited cs_modes. 150994c1acf2SAravind Gopalakrishnan */ 151094c1acf2SAravind Gopalakrishnan static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 1511a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 151294c1acf2SAravind Gopalakrishnan { 151394c1acf2SAravind Gopalakrishnan WARN_ON(cs_mode > 12); 151494c1acf2SAravind Gopalakrishnan 151594c1acf2SAravind Gopalakrishnan if (cs_mode == 6 || cs_mode == 8 || 151694c1acf2SAravind Gopalakrishnan cs_mode == 9 || cs_mode == 12) 151794c1acf2SAravind Gopalakrishnan return -1; 151894c1acf2SAravind Gopalakrishnan else 151994c1acf2SAravind Gopalakrishnan return ddr3_cs_size(cs_mode, false); 152094c1acf2SAravind Gopalakrishnan } 152194c1acf2SAravind Gopalakrishnan 1522f1cbbec9SYazen Ghannam static int f17_base_addr_to_cs_size(struct amd64_pvt *pvt, u8 umc, 1523f1cbbec9SYazen Ghannam unsigned int cs_mode, int csrow_nr) 1524f1cbbec9SYazen Ghannam { 1525f1cbbec9SYazen Ghannam u32 base_addr = pvt->csels[umc].csbases[csrow_nr]; 1526f1cbbec9SYazen Ghannam 1527f1cbbec9SYazen Ghannam /* Each mask is used for every two base addresses. */ 1528f1cbbec9SYazen Ghannam u32 addr_mask = pvt->csels[umc].csmasks[csrow_nr >> 1]; 1529f1cbbec9SYazen Ghannam 1530f1cbbec9SYazen Ghannam /* Register [31:1] = Address [39:9]. Size is in kBs here. */ 1531f1cbbec9SYazen Ghannam u32 size = ((addr_mask >> 1) - (base_addr >> 1) + 1) >> 1; 1532f1cbbec9SYazen Ghannam 1533f1cbbec9SYazen Ghannam edac_dbg(1, "BaseAddr: 0x%x, AddrMask: 0x%x\n", base_addr, addr_mask); 1534f1cbbec9SYazen Ghannam 1535f1cbbec9SYazen Ghannam /* Return size in MBs. */ 1536f1cbbec9SYazen Ghannam return size >> 10; 1537f1cbbec9SYazen Ghannam } 1538f1cbbec9SYazen Ghannam 15395a5d2371SBorislav Petkov static void read_dram_ctl_register(struct amd64_pvt *pvt) 15406163b5d4SDoug Thompson { 15416163b5d4SDoug Thompson 1542a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) 15435a5d2371SBorislav Petkov return; 15445a5d2371SBorislav Petkov 15457981a28fSAravind Gopalakrishnan if (!amd64_read_pci_cfg(pvt->F2, DCT_SEL_LO, &pvt->dct_sel_lo)) { 1546956b9ba1SJoe Perches edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n", 154778da121eSBorislav Petkov pvt->dct_sel_lo, dct_sel_baseaddr(pvt)); 15486163b5d4SDoug Thompson 1549956b9ba1SJoe Perches edac_dbg(0, " DCTs operate in %s mode\n", 15505a5d2371SBorislav Petkov (dct_ganging_enabled(pvt) ? "ganged" : "unganged")); 15516163b5d4SDoug Thompson 155272381bd5SBorislav Petkov if (!dct_ganging_enabled(pvt)) 1553956b9ba1SJoe Perches edac_dbg(0, " Address range split per DCT: %s\n", 155472381bd5SBorislav Petkov (dct_high_range_enabled(pvt) ? "yes" : "no")); 155572381bd5SBorislav Petkov 1556956b9ba1SJoe Perches edac_dbg(0, " data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n", 155772381bd5SBorislav Petkov (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"), 155872381bd5SBorislav Petkov (dct_memory_cleared(pvt) ? "yes" : "no")); 155972381bd5SBorislav Petkov 1560956b9ba1SJoe Perches edac_dbg(0, " channel interleave: %s, " 156178da121eSBorislav Petkov "interleave bits selector: 0x%x\n", 156272381bd5SBorislav Petkov (dct_interleave_enabled(pvt) ? "enabled" : "disabled"), 15636163b5d4SDoug Thompson dct_sel_interleave_addr(pvt)); 15646163b5d4SDoug Thompson } 15656163b5d4SDoug Thompson 15667981a28fSAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F2, DCT_SEL_HI, &pvt->dct_sel_hi); 15676163b5d4SDoug Thompson } 15686163b5d4SDoug Thompson 1569f71d0a05SDoug Thompson /* 157018b94f66SAravind Gopalakrishnan * Determine channel (DCT) based on the interleaving mode (see F15h M30h BKDG, 157118b94f66SAravind Gopalakrishnan * 2.10.12 Memory Interleaving Modes). 157218b94f66SAravind Gopalakrishnan */ 157318b94f66SAravind Gopalakrishnan static u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, 157418b94f66SAravind Gopalakrishnan u8 intlv_en, int num_dcts_intlv, 157518b94f66SAravind Gopalakrishnan u32 dct_sel) 157618b94f66SAravind Gopalakrishnan { 157718b94f66SAravind Gopalakrishnan u8 channel = 0; 157818b94f66SAravind Gopalakrishnan u8 select; 157918b94f66SAravind Gopalakrishnan 158018b94f66SAravind Gopalakrishnan if (!(intlv_en)) 158118b94f66SAravind Gopalakrishnan return (u8)(dct_sel); 158218b94f66SAravind Gopalakrishnan 158318b94f66SAravind Gopalakrishnan if (num_dcts_intlv == 2) { 158418b94f66SAravind Gopalakrishnan select = (sys_addr >> 8) & 0x3; 158518b94f66SAravind Gopalakrishnan channel = select ? 0x3 : 0; 15869d0e8d83SAravind Gopalakrishnan } else if (num_dcts_intlv == 4) { 15879d0e8d83SAravind Gopalakrishnan u8 intlv_addr = dct_sel_interleave_addr(pvt); 15889d0e8d83SAravind Gopalakrishnan switch (intlv_addr) { 15899d0e8d83SAravind Gopalakrishnan case 0x4: 15909d0e8d83SAravind Gopalakrishnan channel = (sys_addr >> 8) & 0x3; 15919d0e8d83SAravind Gopalakrishnan break; 15929d0e8d83SAravind Gopalakrishnan case 0x5: 15939d0e8d83SAravind Gopalakrishnan channel = (sys_addr >> 9) & 0x3; 15949d0e8d83SAravind Gopalakrishnan break; 15959d0e8d83SAravind Gopalakrishnan } 15969d0e8d83SAravind Gopalakrishnan } 159718b94f66SAravind Gopalakrishnan return channel; 159818b94f66SAravind Gopalakrishnan } 159918b94f66SAravind Gopalakrishnan 160018b94f66SAravind Gopalakrishnan /* 1601229a7a11SBorislav Petkov * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory 1602f71d0a05SDoug Thompson * Interleaving Modes. 1603f71d0a05SDoug Thompson */ 1604b15f0fcaSBorislav Petkov static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, 1605229a7a11SBorislav Petkov bool hi_range_sel, u8 intlv_en) 16066163b5d4SDoug Thompson { 1607151fa71cSBorislav Petkov u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1; 16086163b5d4SDoug Thompson 16096163b5d4SDoug Thompson if (dct_ganging_enabled(pvt)) 1610229a7a11SBorislav Petkov return 0; 1611229a7a11SBorislav Petkov 1612229a7a11SBorislav Petkov if (hi_range_sel) 1613229a7a11SBorislav Petkov return dct_sel_high; 1614229a7a11SBorislav Petkov 1615f71d0a05SDoug Thompson /* 1616f71d0a05SDoug Thompson * see F2x110[DctSelIntLvAddr] - channel interleave mode 1617f71d0a05SDoug Thompson */ 1618229a7a11SBorislav Petkov if (dct_interleave_enabled(pvt)) { 1619229a7a11SBorislav Petkov u8 intlv_addr = dct_sel_interleave_addr(pvt); 16206163b5d4SDoug Thompson 1621229a7a11SBorislav Petkov /* return DCT select function: 0=DCT0, 1=DCT1 */ 1622229a7a11SBorislav Petkov if (!intlv_addr) 1623229a7a11SBorislav Petkov return sys_addr >> 6 & 1; 16246163b5d4SDoug Thompson 1625229a7a11SBorislav Petkov if (intlv_addr & 0x2) { 1626229a7a11SBorislav Petkov u8 shift = intlv_addr & 0x1 ? 9 : 6; 1627dc0a50a8SYazen Ghannam u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) & 1; 1628229a7a11SBorislav Petkov 1629229a7a11SBorislav Petkov return ((sys_addr >> shift) & 1) ^ temp; 16306163b5d4SDoug Thompson } 16316163b5d4SDoug Thompson 1632dc0a50a8SYazen Ghannam if (intlv_addr & 0x4) { 1633dc0a50a8SYazen Ghannam u8 shift = intlv_addr & 0x1 ? 9 : 8; 1634dc0a50a8SYazen Ghannam 1635dc0a50a8SYazen Ghannam return (sys_addr >> shift) & 1; 1636dc0a50a8SYazen Ghannam } 1637dc0a50a8SYazen Ghannam 1638229a7a11SBorislav Petkov return (sys_addr >> (12 + hweight8(intlv_en))) & 1; 1639229a7a11SBorislav Petkov } 1640229a7a11SBorislav Petkov 1641229a7a11SBorislav Petkov if (dct_high_range_enabled(pvt)) 1642229a7a11SBorislav Petkov return ~dct_sel_high & 1; 16436163b5d4SDoug Thompson 16446163b5d4SDoug Thompson return 0; 16456163b5d4SDoug Thompson } 16466163b5d4SDoug Thompson 1647c8e518d5SBorislav Petkov /* Convert the sys_addr to the normalized DCT address */ 1648c7e5301aSDaniel J Blueman static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range, 1649c8e518d5SBorislav Petkov u64 sys_addr, bool hi_rng, 1650c8e518d5SBorislav Petkov u32 dct_sel_base_addr) 16516163b5d4SDoug Thompson { 16526163b5d4SDoug Thompson u64 chan_off; 1653c8e518d5SBorislav Petkov u64 dram_base = get_dram_base(pvt, range); 1654c8e518d5SBorislav Petkov u64 hole_off = f10_dhar_offset(pvt); 16556f3508f6SDan Carpenter u64 dct_sel_base_off = (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16; 16566163b5d4SDoug Thompson 1657c8e518d5SBorislav Petkov if (hi_rng) { 1658c8e518d5SBorislav Petkov /* 1659c8e518d5SBorislav Petkov * if 1660c8e518d5SBorislav Petkov * base address of high range is below 4Gb 1661c8e518d5SBorislav Petkov * (bits [47:27] at [31:11]) 1662c8e518d5SBorislav Petkov * DRAM address space on this DCT is hoisted above 4Gb && 1663c8e518d5SBorislav Petkov * sys_addr > 4Gb 1664c8e518d5SBorislav Petkov * 1665c8e518d5SBorislav Petkov * remove hole offset from sys_addr 1666c8e518d5SBorislav Petkov * else 1667c8e518d5SBorislav Petkov * remove high range offset from sys_addr 1668c8e518d5SBorislav Petkov */ 1669c8e518d5SBorislav Petkov if ((!(dct_sel_base_addr >> 16) || 1670c8e518d5SBorislav Petkov dct_sel_base_addr < dhar_base(pvt)) && 1671972ea17aSBorislav Petkov dhar_valid(pvt) && 1672c8e518d5SBorislav Petkov (sys_addr >= BIT_64(32))) 1673bc21fa57SBorislav Petkov chan_off = hole_off; 16746163b5d4SDoug Thompson else 16756163b5d4SDoug Thompson chan_off = dct_sel_base_off; 16766163b5d4SDoug Thompson } else { 1677c8e518d5SBorislav Petkov /* 1678c8e518d5SBorislav Petkov * if 1679c8e518d5SBorislav Petkov * we have a valid hole && 1680c8e518d5SBorislav Petkov * sys_addr > 4Gb 1681c8e518d5SBorislav Petkov * 1682c8e518d5SBorislav Petkov * remove hole 1683c8e518d5SBorislav Petkov * else 1684c8e518d5SBorislav Petkov * remove dram base to normalize to DCT address 1685c8e518d5SBorislav Petkov */ 1686972ea17aSBorislav Petkov if (dhar_valid(pvt) && (sys_addr >= BIT_64(32))) 1687bc21fa57SBorislav Petkov chan_off = hole_off; 16886163b5d4SDoug Thompson else 1689c8e518d5SBorislav Petkov chan_off = dram_base; 16906163b5d4SDoug Thompson } 16916163b5d4SDoug Thompson 169210ef6b0dSChen, Gong return (sys_addr & GENMASK_ULL(47,6)) - (chan_off & GENMASK_ULL(47,23)); 16936163b5d4SDoug Thompson } 16946163b5d4SDoug Thompson 16956163b5d4SDoug Thompson /* 16966163b5d4SDoug Thompson * checks if the csrow passed in is marked as SPARED, if so returns the new 16976163b5d4SDoug Thompson * spare row 16986163b5d4SDoug Thompson */ 169911c75eadSBorislav Petkov static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow) 17006163b5d4SDoug Thompson { 1701614ec9d8SBorislav Petkov int tmp_cs; 17026163b5d4SDoug Thompson 1703614ec9d8SBorislav Petkov if (online_spare_swap_done(pvt, dct) && 1704614ec9d8SBorislav Petkov csrow == online_spare_bad_dramcs(pvt, dct)) { 1705614ec9d8SBorislav Petkov 1706614ec9d8SBorislav Petkov for_each_chip_select(tmp_cs, dct, pvt) { 1707614ec9d8SBorislav Petkov if (chip_select_base(tmp_cs, dct, pvt) & 0x2) { 1708614ec9d8SBorislav Petkov csrow = tmp_cs; 1709614ec9d8SBorislav Petkov break; 1710614ec9d8SBorislav Petkov } 1711614ec9d8SBorislav Petkov } 17126163b5d4SDoug Thompson } 17136163b5d4SDoug Thompson return csrow; 17146163b5d4SDoug Thompson } 17156163b5d4SDoug Thompson 17166163b5d4SDoug Thompson /* 17176163b5d4SDoug Thompson * Iterate over the DRAM DCT "base" and "mask" registers looking for a 17186163b5d4SDoug Thompson * SystemAddr match on the specified 'ChannelSelect' and 'NodeID' 17196163b5d4SDoug Thompson * 17206163b5d4SDoug Thompson * Return: 17216163b5d4SDoug Thompson * -EINVAL: NOT FOUND 17226163b5d4SDoug Thompson * 0..csrow = Chip-Select Row 17236163b5d4SDoug Thompson */ 1724c7e5301aSDaniel J Blueman static int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct) 17256163b5d4SDoug Thompson { 17266163b5d4SDoug Thompson struct mem_ctl_info *mci; 17276163b5d4SDoug Thompson struct amd64_pvt *pvt; 172811c75eadSBorislav Petkov u64 cs_base, cs_mask; 17296163b5d4SDoug Thompson int cs_found = -EINVAL; 17306163b5d4SDoug Thompson int csrow; 17316163b5d4SDoug Thompson 17322ec591acSBorislav Petkov mci = edac_mc_find(nid); 17336163b5d4SDoug Thompson if (!mci) 17346163b5d4SDoug Thompson return cs_found; 17356163b5d4SDoug Thompson 17366163b5d4SDoug Thompson pvt = mci->pvt_info; 17376163b5d4SDoug Thompson 1738956b9ba1SJoe Perches edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct); 17396163b5d4SDoug Thompson 174011c75eadSBorislav Petkov for_each_chip_select(csrow, dct, pvt) { 174111c75eadSBorislav Petkov if (!csrow_enabled(csrow, dct, pvt)) 17426163b5d4SDoug Thompson continue; 17436163b5d4SDoug Thompson 174411c75eadSBorislav Petkov get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask); 17456163b5d4SDoug Thompson 1746956b9ba1SJoe Perches edac_dbg(1, " CSROW=%d CSBase=0x%llx CSMask=0x%llx\n", 17476163b5d4SDoug Thompson csrow, cs_base, cs_mask); 17486163b5d4SDoug Thompson 174911c75eadSBorislav Petkov cs_mask = ~cs_mask; 17506163b5d4SDoug Thompson 1751956b9ba1SJoe Perches edac_dbg(1, " (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n", 175211c75eadSBorislav Petkov (in_addr & cs_mask), (cs_base & cs_mask)); 17536163b5d4SDoug Thompson 175411c75eadSBorislav Petkov if ((in_addr & cs_mask) == (cs_base & cs_mask)) { 175518b94f66SAravind Gopalakrishnan if (pvt->fam == 0x15 && pvt->model >= 0x30) { 175618b94f66SAravind Gopalakrishnan cs_found = csrow; 175718b94f66SAravind Gopalakrishnan break; 175818b94f66SAravind Gopalakrishnan } 175911c75eadSBorislav Petkov cs_found = f10_process_possible_spare(pvt, dct, csrow); 17606163b5d4SDoug Thompson 1761956b9ba1SJoe Perches edac_dbg(1, " MATCH csrow=%d\n", cs_found); 17626163b5d4SDoug Thompson break; 17636163b5d4SDoug Thompson } 17646163b5d4SDoug Thompson } 17656163b5d4SDoug Thompson return cs_found; 17666163b5d4SDoug Thompson } 17676163b5d4SDoug Thompson 176895b0ef55SBorislav Petkov /* 176995b0ef55SBorislav Petkov * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is 177095b0ef55SBorislav Petkov * swapped with a region located at the bottom of memory so that the GPU can use 177195b0ef55SBorislav Petkov * the interleaved region and thus two channels. 177295b0ef55SBorislav Petkov */ 1773b15f0fcaSBorislav Petkov static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr) 177495b0ef55SBorislav Petkov { 177595b0ef55SBorislav Petkov u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr; 177695b0ef55SBorislav Petkov 1777a4b4bedcSBorislav Petkov if (pvt->fam == 0x10) { 177895b0ef55SBorislav Petkov /* only revC3 and revE have that feature */ 1779a4b4bedcSBorislav Petkov if (pvt->model < 4 || (pvt->model < 0xa && pvt->stepping < 3)) 178095b0ef55SBorislav Petkov return sys_addr; 178195b0ef55SBorislav Petkov } 178295b0ef55SBorislav Petkov 17837981a28fSAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F2, SWAP_INTLV_REG, &swap_reg); 178495b0ef55SBorislav Petkov 178595b0ef55SBorislav Petkov if (!(swap_reg & 0x1)) 178695b0ef55SBorislav Petkov return sys_addr; 178795b0ef55SBorislav Petkov 178895b0ef55SBorislav Petkov swap_base = (swap_reg >> 3) & 0x7f; 178995b0ef55SBorislav Petkov swap_limit = (swap_reg >> 11) & 0x7f; 179095b0ef55SBorislav Petkov rgn_size = (swap_reg >> 20) & 0x7f; 179195b0ef55SBorislav Petkov tmp_addr = sys_addr >> 27; 179295b0ef55SBorislav Petkov 179395b0ef55SBorislav Petkov if (!(sys_addr >> 34) && 179495b0ef55SBorislav Petkov (((tmp_addr >= swap_base) && 179595b0ef55SBorislav Petkov (tmp_addr <= swap_limit)) || 179695b0ef55SBorislav Petkov (tmp_addr < rgn_size))) 179795b0ef55SBorislav Petkov return sys_addr ^ (u64)swap_base << 27; 179895b0ef55SBorislav Petkov 179995b0ef55SBorislav Petkov return sys_addr; 180095b0ef55SBorislav Petkov } 180195b0ef55SBorislav Petkov 1802f71d0a05SDoug Thompson /* For a given @dram_range, check if @sys_addr falls within it. */ 1803e761359aSBorislav Petkov static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range, 180433ca0643SBorislav Petkov u64 sys_addr, int *chan_sel) 1805f71d0a05SDoug Thompson { 1806229a7a11SBorislav Petkov int cs_found = -EINVAL; 1807c8e518d5SBorislav Petkov u64 chan_addr; 18085d4b58e8SBorislav Petkov u32 dct_sel_base; 180911c75eadSBorislav Petkov u8 channel; 1810229a7a11SBorislav Petkov bool high_range = false; 1811f71d0a05SDoug Thompson 18127f19bf75SBorislav Petkov u8 node_id = dram_dst_node(pvt, range); 1813229a7a11SBorislav Petkov u8 intlv_en = dram_intlv_en(pvt, range); 18147f19bf75SBorislav Petkov u32 intlv_sel = dram_intlv_sel(pvt, range); 1815f71d0a05SDoug Thompson 1816956b9ba1SJoe Perches edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n", 1817c8e518d5SBorislav Petkov range, sys_addr, get_dram_limit(pvt, range)); 1818f71d0a05SDoug Thompson 1819355fba60SBorislav Petkov if (dhar_valid(pvt) && 1820355fba60SBorislav Petkov dhar_base(pvt) <= sys_addr && 1821355fba60SBorislav Petkov sys_addr < BIT_64(32)) { 1822355fba60SBorislav Petkov amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n", 1823355fba60SBorislav Petkov sys_addr); 1824f71d0a05SDoug Thompson return -EINVAL; 1825355fba60SBorislav Petkov } 1826355fba60SBorislav Petkov 1827f030ddfbSBorislav Petkov if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en))) 1828355fba60SBorislav Petkov return -EINVAL; 1829f71d0a05SDoug Thompson 1830b15f0fcaSBorislav Petkov sys_addr = f1x_swap_interleaved_region(pvt, sys_addr); 183195b0ef55SBorislav Petkov 1832f71d0a05SDoug Thompson dct_sel_base = dct_sel_baseaddr(pvt); 1833f71d0a05SDoug Thompson 1834f71d0a05SDoug Thompson /* 1835f71d0a05SDoug Thompson * check whether addresses >= DctSelBaseAddr[47:27] are to be used to 1836f71d0a05SDoug Thompson * select between DCT0 and DCT1. 1837f71d0a05SDoug Thompson */ 1838f71d0a05SDoug Thompson if (dct_high_range_enabled(pvt) && 1839f71d0a05SDoug Thompson !dct_ganging_enabled(pvt) && 1840f71d0a05SDoug Thompson ((sys_addr >> 27) >= (dct_sel_base >> 11))) 1841229a7a11SBorislav Petkov high_range = true; 1842f71d0a05SDoug Thompson 1843b15f0fcaSBorislav Petkov channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en); 1844f71d0a05SDoug Thompson 1845b15f0fcaSBorislav Petkov chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr, 1846c8e518d5SBorislav Petkov high_range, dct_sel_base); 1847f71d0a05SDoug Thompson 1848e2f79dbdSBorislav Petkov /* Remove node interleaving, see F1x120 */ 1849e2f79dbdSBorislav Petkov if (intlv_en) 1850e2f79dbdSBorislav Petkov chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) | 1851e2f79dbdSBorislav Petkov (chan_addr & 0xfff); 1852f71d0a05SDoug Thompson 18535d4b58e8SBorislav Petkov /* remove channel interleave */ 1854f71d0a05SDoug Thompson if (dct_interleave_enabled(pvt) && 1855f71d0a05SDoug Thompson !dct_high_range_enabled(pvt) && 1856f71d0a05SDoug Thompson !dct_ganging_enabled(pvt)) { 18575d4b58e8SBorislav Petkov 18585d4b58e8SBorislav Petkov if (dct_sel_interleave_addr(pvt) != 1) { 18595d4b58e8SBorislav Petkov if (dct_sel_interleave_addr(pvt) == 0x3) 18605d4b58e8SBorislav Petkov /* hash 9 */ 18615d4b58e8SBorislav Petkov chan_addr = ((chan_addr >> 10) << 9) | 18625d4b58e8SBorislav Petkov (chan_addr & 0x1ff); 18635d4b58e8SBorislav Petkov else 18645d4b58e8SBorislav Petkov /* A[6] or hash 6 */ 18655d4b58e8SBorislav Petkov chan_addr = ((chan_addr >> 7) << 6) | 18665d4b58e8SBorislav Petkov (chan_addr & 0x3f); 18675d4b58e8SBorislav Petkov } else 18685d4b58e8SBorislav Petkov /* A[12] */ 18695d4b58e8SBorislav Petkov chan_addr = ((chan_addr >> 13) << 12) | 18705d4b58e8SBorislav Petkov (chan_addr & 0xfff); 1871f71d0a05SDoug Thompson } 1872f71d0a05SDoug Thompson 1873956b9ba1SJoe Perches edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr); 1874f71d0a05SDoug Thompson 1875b15f0fcaSBorislav Petkov cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel); 1876f71d0a05SDoug Thompson 187733ca0643SBorislav Petkov if (cs_found >= 0) 1878f71d0a05SDoug Thompson *chan_sel = channel; 187933ca0643SBorislav Petkov 1880f71d0a05SDoug Thompson return cs_found; 1881f71d0a05SDoug Thompson } 1882f71d0a05SDoug Thompson 188318b94f66SAravind Gopalakrishnan static int f15_m30h_match_to_this_node(struct amd64_pvt *pvt, unsigned range, 188418b94f66SAravind Gopalakrishnan u64 sys_addr, int *chan_sel) 188518b94f66SAravind Gopalakrishnan { 188618b94f66SAravind Gopalakrishnan int cs_found = -EINVAL; 188718b94f66SAravind Gopalakrishnan int num_dcts_intlv = 0; 188818b94f66SAravind Gopalakrishnan u64 chan_addr, chan_offset; 188918b94f66SAravind Gopalakrishnan u64 dct_base, dct_limit; 189018b94f66SAravind Gopalakrishnan u32 dct_cont_base_reg, dct_cont_limit_reg, tmp; 189118b94f66SAravind Gopalakrishnan u8 channel, alias_channel, leg_mmio_hole, dct_sel, dct_offset_en; 189218b94f66SAravind Gopalakrishnan 189318b94f66SAravind Gopalakrishnan u64 dhar_offset = f10_dhar_offset(pvt); 189418b94f66SAravind Gopalakrishnan u8 intlv_addr = dct_sel_interleave_addr(pvt); 189518b94f66SAravind Gopalakrishnan u8 node_id = dram_dst_node(pvt, range); 189618b94f66SAravind Gopalakrishnan u8 intlv_en = dram_intlv_en(pvt, range); 189718b94f66SAravind Gopalakrishnan 189818b94f66SAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F1, DRAM_CONT_BASE, &dct_cont_base_reg); 189918b94f66SAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F1, DRAM_CONT_LIMIT, &dct_cont_limit_reg); 190018b94f66SAravind Gopalakrishnan 190118b94f66SAravind Gopalakrishnan dct_offset_en = (u8) ((dct_cont_base_reg >> 3) & BIT(0)); 190218b94f66SAravind Gopalakrishnan dct_sel = (u8) ((dct_cont_base_reg >> 4) & 0x7); 190318b94f66SAravind Gopalakrishnan 190418b94f66SAravind Gopalakrishnan edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n", 190518b94f66SAravind Gopalakrishnan range, sys_addr, get_dram_limit(pvt, range)); 190618b94f66SAravind Gopalakrishnan 190718b94f66SAravind Gopalakrishnan if (!(get_dram_base(pvt, range) <= sys_addr) && 190818b94f66SAravind Gopalakrishnan !(get_dram_limit(pvt, range) >= sys_addr)) 190918b94f66SAravind Gopalakrishnan return -EINVAL; 191018b94f66SAravind Gopalakrishnan 191118b94f66SAravind Gopalakrishnan if (dhar_valid(pvt) && 191218b94f66SAravind Gopalakrishnan dhar_base(pvt) <= sys_addr && 191318b94f66SAravind Gopalakrishnan sys_addr < BIT_64(32)) { 191418b94f66SAravind Gopalakrishnan amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n", 191518b94f66SAravind Gopalakrishnan sys_addr); 191618b94f66SAravind Gopalakrishnan return -EINVAL; 191718b94f66SAravind Gopalakrishnan } 191818b94f66SAravind Gopalakrishnan 191918b94f66SAravind Gopalakrishnan /* Verify sys_addr is within DCT Range. */ 19204fc06b31SAravind Gopalakrishnan dct_base = (u64) dct_sel_baseaddr(pvt); 19214fc06b31SAravind Gopalakrishnan dct_limit = (dct_cont_limit_reg >> 11) & 0x1FFF; 192218b94f66SAravind Gopalakrishnan 192318b94f66SAravind Gopalakrishnan if (!(dct_cont_base_reg & BIT(0)) && 19244fc06b31SAravind Gopalakrishnan !(dct_base <= (sys_addr >> 27) && 19254fc06b31SAravind Gopalakrishnan dct_limit >= (sys_addr >> 27))) 192618b94f66SAravind Gopalakrishnan return -EINVAL; 192718b94f66SAravind Gopalakrishnan 192818b94f66SAravind Gopalakrishnan /* Verify number of dct's that participate in channel interleaving. */ 192918b94f66SAravind Gopalakrishnan num_dcts_intlv = (int) hweight8(intlv_en); 193018b94f66SAravind Gopalakrishnan 193118b94f66SAravind Gopalakrishnan if (!(num_dcts_intlv % 2 == 0) || (num_dcts_intlv > 4)) 193218b94f66SAravind Gopalakrishnan return -EINVAL; 193318b94f66SAravind Gopalakrishnan 1934dc0a50a8SYazen Ghannam if (pvt->model >= 0x60) 1935dc0a50a8SYazen Ghannam channel = f1x_determine_channel(pvt, sys_addr, false, intlv_en); 1936dc0a50a8SYazen Ghannam else 193718b94f66SAravind Gopalakrishnan channel = f15_m30h_determine_channel(pvt, sys_addr, intlv_en, 193818b94f66SAravind Gopalakrishnan num_dcts_intlv, dct_sel); 193918b94f66SAravind Gopalakrishnan 194018b94f66SAravind Gopalakrishnan /* Verify we stay within the MAX number of channels allowed */ 19417f3f5240SAravind Gopalakrishnan if (channel > 3) 194218b94f66SAravind Gopalakrishnan return -EINVAL; 194318b94f66SAravind Gopalakrishnan 194418b94f66SAravind Gopalakrishnan leg_mmio_hole = (u8) (dct_cont_base_reg >> 1 & BIT(0)); 194518b94f66SAravind Gopalakrishnan 194618b94f66SAravind Gopalakrishnan /* Get normalized DCT addr */ 194718b94f66SAravind Gopalakrishnan if (leg_mmio_hole && (sys_addr >= BIT_64(32))) 194818b94f66SAravind Gopalakrishnan chan_offset = dhar_offset; 194918b94f66SAravind Gopalakrishnan else 19504fc06b31SAravind Gopalakrishnan chan_offset = dct_base << 27; 195118b94f66SAravind Gopalakrishnan 195218b94f66SAravind Gopalakrishnan chan_addr = sys_addr - chan_offset; 195318b94f66SAravind Gopalakrishnan 195418b94f66SAravind Gopalakrishnan /* remove channel interleave */ 195518b94f66SAravind Gopalakrishnan if (num_dcts_intlv == 2) { 195618b94f66SAravind Gopalakrishnan if (intlv_addr == 0x4) 195718b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 9) << 8) | 195818b94f66SAravind Gopalakrishnan (chan_addr & 0xff); 195918b94f66SAravind Gopalakrishnan else if (intlv_addr == 0x5) 196018b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 10) << 9) | 196118b94f66SAravind Gopalakrishnan (chan_addr & 0x1ff); 196218b94f66SAravind Gopalakrishnan else 196318b94f66SAravind Gopalakrishnan return -EINVAL; 196418b94f66SAravind Gopalakrishnan 196518b94f66SAravind Gopalakrishnan } else if (num_dcts_intlv == 4) { 196618b94f66SAravind Gopalakrishnan if (intlv_addr == 0x4) 196718b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 10) << 8) | 196818b94f66SAravind Gopalakrishnan (chan_addr & 0xff); 196918b94f66SAravind Gopalakrishnan else if (intlv_addr == 0x5) 197018b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 11) << 9) | 197118b94f66SAravind Gopalakrishnan (chan_addr & 0x1ff); 197218b94f66SAravind Gopalakrishnan else 197318b94f66SAravind Gopalakrishnan return -EINVAL; 197418b94f66SAravind Gopalakrishnan } 197518b94f66SAravind Gopalakrishnan 197618b94f66SAravind Gopalakrishnan if (dct_offset_en) { 197718b94f66SAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F1, 197818b94f66SAravind Gopalakrishnan DRAM_CONT_HIGH_OFF + (int) channel * 4, 197918b94f66SAravind Gopalakrishnan &tmp); 19804fc06b31SAravind Gopalakrishnan chan_addr += (u64) ((tmp >> 11) & 0xfff) << 27; 198118b94f66SAravind Gopalakrishnan } 198218b94f66SAravind Gopalakrishnan 198318b94f66SAravind Gopalakrishnan f15h_select_dct(pvt, channel); 198418b94f66SAravind Gopalakrishnan 198518b94f66SAravind Gopalakrishnan edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr); 198618b94f66SAravind Gopalakrishnan 198718b94f66SAravind Gopalakrishnan /* 198818b94f66SAravind Gopalakrishnan * Find Chip select: 198918b94f66SAravind Gopalakrishnan * if channel = 3, then alias it to 1. This is because, in F15 M30h, 199018b94f66SAravind Gopalakrishnan * there is support for 4 DCT's, but only 2 are currently functional. 199118b94f66SAravind Gopalakrishnan * They are DCT0 and DCT3. But we have read all registers of DCT3 into 199218b94f66SAravind Gopalakrishnan * pvt->csels[1]. So we need to use '1' here to get correct info. 199318b94f66SAravind Gopalakrishnan * Refer F15 M30h BKDG Section 2.10 and 2.10.3 for clarifications. 199418b94f66SAravind Gopalakrishnan */ 199518b94f66SAravind Gopalakrishnan alias_channel = (channel == 3) ? 1 : channel; 199618b94f66SAravind Gopalakrishnan 199718b94f66SAravind Gopalakrishnan cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, alias_channel); 199818b94f66SAravind Gopalakrishnan 199918b94f66SAravind Gopalakrishnan if (cs_found >= 0) 200018b94f66SAravind Gopalakrishnan *chan_sel = alias_channel; 200118b94f66SAravind Gopalakrishnan 200218b94f66SAravind Gopalakrishnan return cs_found; 200318b94f66SAravind Gopalakrishnan } 200418b94f66SAravind Gopalakrishnan 200518b94f66SAravind Gopalakrishnan static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt, 200618b94f66SAravind Gopalakrishnan u64 sys_addr, 200733ca0643SBorislav Petkov int *chan_sel) 2008f71d0a05SDoug Thompson { 2009e761359aSBorislav Petkov int cs_found = -EINVAL; 2010e761359aSBorislav Petkov unsigned range; 2011f71d0a05SDoug Thompson 20127f19bf75SBorislav Petkov for (range = 0; range < DRAM_RANGES; range++) { 20137f19bf75SBorislav Petkov if (!dram_rw(pvt, range)) 2014f71d0a05SDoug Thompson continue; 2015f71d0a05SDoug Thompson 201618b94f66SAravind Gopalakrishnan if (pvt->fam == 0x15 && pvt->model >= 0x30) 201718b94f66SAravind Gopalakrishnan cs_found = f15_m30h_match_to_this_node(pvt, range, 201818b94f66SAravind Gopalakrishnan sys_addr, 201918b94f66SAravind Gopalakrishnan chan_sel); 2020f71d0a05SDoug Thompson 202118b94f66SAravind Gopalakrishnan else if ((get_dram_base(pvt, range) <= sys_addr) && 202218b94f66SAravind Gopalakrishnan (get_dram_limit(pvt, range) >= sys_addr)) { 2023b15f0fcaSBorislav Petkov cs_found = f1x_match_to_this_node(pvt, range, 202433ca0643SBorislav Petkov sys_addr, chan_sel); 2025f71d0a05SDoug Thompson if (cs_found >= 0) 2026f71d0a05SDoug Thompson break; 2027f71d0a05SDoug Thompson } 2028f71d0a05SDoug Thompson } 2029f71d0a05SDoug Thompson return cs_found; 2030f71d0a05SDoug Thompson } 2031f71d0a05SDoug Thompson 2032f71d0a05SDoug Thompson /* 2033bdc30a0cSBorislav Petkov * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps 2034bdc30a0cSBorislav Petkov * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW). 2035f71d0a05SDoug Thompson * 2036bdc30a0cSBorislav Petkov * The @sys_addr is usually an error address received from the hardware 2037bdc30a0cSBorislav Petkov * (MCX_ADDR). 2038f71d0a05SDoug Thompson */ 2039b15f0fcaSBorislav Petkov static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr, 204033ca0643SBorislav Petkov struct err_info *err) 2041f71d0a05SDoug Thompson { 2042f71d0a05SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 2043f71d0a05SDoug Thompson 204433ca0643SBorislav Petkov error_address_to_page_and_offset(sys_addr, err); 2045ab5a503cSMauro Carvalho Chehab 204633ca0643SBorislav Petkov err->csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &err->channel); 204733ca0643SBorislav Petkov if (err->csrow < 0) { 204833ca0643SBorislav Petkov err->err_code = ERR_CSROW; 2049bdc30a0cSBorislav Petkov return; 2050bdc30a0cSBorislav Petkov } 2051bdc30a0cSBorislav Petkov 2052f71d0a05SDoug Thompson /* 2053bdc30a0cSBorislav Petkov * We need the syndromes for channel detection only when we're 2054bdc30a0cSBorislav Petkov * ganged. Otherwise @chan should already contain the channel at 2055bdc30a0cSBorislav Petkov * this point. 2056f71d0a05SDoug Thompson */ 2057a97fa68eSBorislav Petkov if (dct_ganging_enabled(pvt)) 205833ca0643SBorislav Petkov err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome); 2059f71d0a05SDoug Thompson } 2060f71d0a05SDoug Thompson 2061f71d0a05SDoug Thompson /* 20628566c4dfSBorislav Petkov * debug routine to display the memory sizes of all logical DIMMs and its 2063cb328507SBorislav Petkov * CSROWs 2064f71d0a05SDoug Thompson */ 2065d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl) 2066f71d0a05SDoug Thompson { 2067bb89f5a0SBorislav Petkov int dimm, size0, size1; 2068525a1b20SBorislav Petkov u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases; 2069525a1b20SBorislav Petkov u32 dbam = ctrl ? pvt->dbam1 : pvt->dbam0; 2070f71d0a05SDoug Thompson 2071a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) { 20728566c4dfSBorislav Petkov /* K8 families < revF not supported yet */ 20731433eb99SBorislav Petkov if (pvt->ext_model < K8_REV_F) 20748566c4dfSBorislav Petkov return; 20758566c4dfSBorislav Petkov else 20768566c4dfSBorislav Petkov WARN_ON(ctrl != 0); 20778566c4dfSBorislav Petkov } 20788566c4dfSBorislav Petkov 20797981a28fSAravind Gopalakrishnan if (pvt->fam == 0x10) { 20807981a28fSAravind Gopalakrishnan dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1 20817981a28fSAravind Gopalakrishnan : pvt->dbam0; 20827981a28fSAravind Gopalakrishnan dcsb = (ctrl && !dct_ganging_enabled(pvt)) ? 20837981a28fSAravind Gopalakrishnan pvt->csels[1].csbases : 20847981a28fSAravind Gopalakrishnan pvt->csels[0].csbases; 20857981a28fSAravind Gopalakrishnan } else if (ctrl) { 20867981a28fSAravind Gopalakrishnan dbam = pvt->dbam0; 20877981a28fSAravind Gopalakrishnan dcsb = pvt->csels[1].csbases; 20887981a28fSAravind Gopalakrishnan } 2089956b9ba1SJoe Perches edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n", 2090956b9ba1SJoe Perches ctrl, dbam); 2091f71d0a05SDoug Thompson 20928566c4dfSBorislav Petkov edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl); 20938566c4dfSBorislav Petkov 2094f71d0a05SDoug Thompson /* Dump memory sizes for DIMM and its CSROWs */ 2095f71d0a05SDoug Thompson for (dimm = 0; dimm < 4; dimm++) { 2096f71d0a05SDoug Thompson 2097f71d0a05SDoug Thompson size0 = 0; 209811c75eadSBorislav Petkov if (dcsb[dimm*2] & DCSB_CS_ENABLE) 209907ed82efSYazen Ghannam /* 210007ed82efSYazen Ghannam * For F15m60h, we need multiplier for LRDIMM cs_size 210107ed82efSYazen Ghannam * calculation. We pass dimm value to the dbam_to_cs 2102a597d2a5SAravind Gopalakrishnan * mapper so we can find the multiplier from the 2103a597d2a5SAravind Gopalakrishnan * corresponding DCSM. 2104a597d2a5SAravind Gopalakrishnan */ 210541d8bfabSBorislav Petkov size0 = pvt->ops->dbam_to_cs(pvt, ctrl, 2106a597d2a5SAravind Gopalakrishnan DBAM_DIMM(dimm, dbam), 2107a597d2a5SAravind Gopalakrishnan dimm); 2108f71d0a05SDoug Thompson 2109f71d0a05SDoug Thompson size1 = 0; 211011c75eadSBorislav Petkov if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE) 211141d8bfabSBorislav Petkov size1 = pvt->ops->dbam_to_cs(pvt, ctrl, 2112a597d2a5SAravind Gopalakrishnan DBAM_DIMM(dimm, dbam), 2113a597d2a5SAravind Gopalakrishnan dimm); 2114f71d0a05SDoug Thompson 211524f9a7feSBorislav Petkov amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n", 2116bb89f5a0SBorislav Petkov dimm * 2, size0, 2117bb89f5a0SBorislav Petkov dimm * 2 + 1, size1); 2118f71d0a05SDoug Thompson } 2119f71d0a05SDoug Thompson } 2120f71d0a05SDoug Thompson 2121d1ea71cdSBorislav Petkov static struct amd64_family_type family_types[] = { 21224d37607aSDoug Thompson [K8_CPUS] = { 21230092b20dSBorislav Petkov .ctl_name = "K8", 21248d5b5d9cSBorislav Petkov .f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP, 21253f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL, 21264d37607aSDoug Thompson .ops = { 21274d37607aSDoug Thompson .early_channel_count = k8_early_channel_count, 21284d37607aSDoug Thompson .map_sysaddr_to_csrow = k8_map_sysaddr_to_csrow, 21291433eb99SBorislav Petkov .dbam_to_cs = k8_dbam_to_chip_select, 21304d37607aSDoug Thompson } 21314d37607aSDoug Thompson }, 21324d37607aSDoug Thompson [F10_CPUS] = { 21330092b20dSBorislav Petkov .ctl_name = "F10h", 21348d5b5d9cSBorislav Petkov .f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP, 21353f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM, 21364d37607aSDoug Thompson .ops = { 21377d20d14dSBorislav Petkov .early_channel_count = f1x_early_channel_count, 2138b15f0fcaSBorislav Petkov .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 21391433eb99SBorislav Petkov .dbam_to_cs = f10_dbam_to_chip_select, 2140b2b0c605SBorislav Petkov } 2141b2b0c605SBorislav Petkov }, 2142b2b0c605SBorislav Petkov [F15_CPUS] = { 2143b2b0c605SBorislav Petkov .ctl_name = "F15h", 2144df71a053SBorislav Petkov .f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1, 21453f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2, 2146b2b0c605SBorislav Petkov .ops = { 21477d20d14dSBorislav Petkov .early_channel_count = f1x_early_channel_count, 2148b15f0fcaSBorislav Petkov .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 214941d8bfabSBorislav Petkov .dbam_to_cs = f15_dbam_to_chip_select, 21504d37607aSDoug Thompson } 21514d37607aSDoug Thompson }, 215218b94f66SAravind Gopalakrishnan [F15_M30H_CPUS] = { 215318b94f66SAravind Gopalakrishnan .ctl_name = "F15h_M30h", 215418b94f66SAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1, 21553f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2, 215618b94f66SAravind Gopalakrishnan .ops = { 215718b94f66SAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 215818b94f66SAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 215918b94f66SAravind Gopalakrishnan .dbam_to_cs = f16_dbam_to_chip_select, 216018b94f66SAravind Gopalakrishnan } 216118b94f66SAravind Gopalakrishnan }, 2162a597d2a5SAravind Gopalakrishnan [F15_M60H_CPUS] = { 2163a597d2a5SAravind Gopalakrishnan .ctl_name = "F15h_M60h", 2164a597d2a5SAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1, 21653f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2, 2166a597d2a5SAravind Gopalakrishnan .ops = { 2167a597d2a5SAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 2168a597d2a5SAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 2169a597d2a5SAravind Gopalakrishnan .dbam_to_cs = f15_m60h_dbam_to_chip_select, 2170a597d2a5SAravind Gopalakrishnan } 2171a597d2a5SAravind Gopalakrishnan }, 217294c1acf2SAravind Gopalakrishnan [F16_CPUS] = { 217394c1acf2SAravind Gopalakrishnan .ctl_name = "F16h", 217494c1acf2SAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1, 21753f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2, 217694c1acf2SAravind Gopalakrishnan .ops = { 217794c1acf2SAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 217894c1acf2SAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 217994c1acf2SAravind Gopalakrishnan .dbam_to_cs = f16_dbam_to_chip_select, 218094c1acf2SAravind Gopalakrishnan } 218194c1acf2SAravind Gopalakrishnan }, 218285a8885bSAravind Gopalakrishnan [F16_M30H_CPUS] = { 218385a8885bSAravind Gopalakrishnan .ctl_name = "F16h_M30h", 218485a8885bSAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F1, 21853f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2, 218685a8885bSAravind Gopalakrishnan .ops = { 218785a8885bSAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 218885a8885bSAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 218985a8885bSAravind Gopalakrishnan .dbam_to_cs = f16_dbam_to_chip_select, 219085a8885bSAravind Gopalakrishnan } 219185a8885bSAravind Gopalakrishnan }, 2192f1cbbec9SYazen Ghannam [F17_CPUS] = { 2193f1cbbec9SYazen Ghannam .ctl_name = "F17h", 2194f1cbbec9SYazen Ghannam .f0_id = PCI_DEVICE_ID_AMD_17H_DF_F0, 2195f1cbbec9SYazen Ghannam .f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6, 2196f1cbbec9SYazen Ghannam .ops = { 2197f1cbbec9SYazen Ghannam .early_channel_count = f17_early_channel_count, 2198f1cbbec9SYazen Ghannam .dbam_to_cs = f17_base_addr_to_cs_size, 2199f1cbbec9SYazen Ghannam } 2200f1cbbec9SYazen Ghannam }, 22014d37607aSDoug Thompson }; 22024d37607aSDoug Thompson 2203b1289d6fSDoug Thompson /* 2204bfc04aecSBorislav Petkov * These are tables of eigenvectors (one per line) which can be used for the 2205bfc04aecSBorislav Petkov * construction of the syndrome tables. The modified syndrome search algorithm 2206bfc04aecSBorislav Petkov * uses those to find the symbol in error and thus the DIMM. 2207b1289d6fSDoug Thompson * 2208bfc04aecSBorislav Petkov * Algorithm courtesy of Ross LaFetra from AMD. 2209b1289d6fSDoug Thompson */ 2210c7e5301aSDaniel J Blueman static const u16 x4_vectors[] = { 2211bfc04aecSBorislav Petkov 0x2f57, 0x1afe, 0x66cc, 0xdd88, 2212bfc04aecSBorislav Petkov 0x11eb, 0x3396, 0x7f4c, 0xeac8, 2213bfc04aecSBorislav Petkov 0x0001, 0x0002, 0x0004, 0x0008, 2214bfc04aecSBorislav Petkov 0x1013, 0x3032, 0x4044, 0x8088, 2215bfc04aecSBorislav Petkov 0x106b, 0x30d6, 0x70fc, 0xe0a8, 2216bfc04aecSBorislav Petkov 0x4857, 0xc4fe, 0x13cc, 0x3288, 2217bfc04aecSBorislav Petkov 0x1ac5, 0x2f4a, 0x5394, 0xa1e8, 2218bfc04aecSBorislav Petkov 0x1f39, 0x251e, 0xbd6c, 0x6bd8, 2219bfc04aecSBorislav Petkov 0x15c1, 0x2a42, 0x89ac, 0x4758, 2220bfc04aecSBorislav Petkov 0x2b03, 0x1602, 0x4f0c, 0xca08, 2221bfc04aecSBorislav Petkov 0x1f07, 0x3a0e, 0x6b04, 0xbd08, 2222bfc04aecSBorislav Petkov 0x8ba7, 0x465e, 0x244c, 0x1cc8, 2223bfc04aecSBorislav Petkov 0x2b87, 0x164e, 0x642c, 0xdc18, 2224bfc04aecSBorislav Petkov 0x40b9, 0x80de, 0x1094, 0x20e8, 2225bfc04aecSBorislav Petkov 0x27db, 0x1eb6, 0x9dac, 0x7b58, 2226bfc04aecSBorislav Petkov 0x11c1, 0x2242, 0x84ac, 0x4c58, 2227bfc04aecSBorislav Petkov 0x1be5, 0x2d7a, 0x5e34, 0xa718, 2228bfc04aecSBorislav Petkov 0x4b39, 0x8d1e, 0x14b4, 0x28d8, 2229bfc04aecSBorislav Petkov 0x4c97, 0xc87e, 0x11fc, 0x33a8, 2230bfc04aecSBorislav Petkov 0x8e97, 0x497e, 0x2ffc, 0x1aa8, 2231bfc04aecSBorislav Petkov 0x16b3, 0x3d62, 0x4f34, 0x8518, 2232bfc04aecSBorislav Petkov 0x1e2f, 0x391a, 0x5cac, 0xf858, 2233bfc04aecSBorislav Petkov 0x1d9f, 0x3b7a, 0x572c, 0xfe18, 2234bfc04aecSBorislav Petkov 0x15f5, 0x2a5a, 0x5264, 0xa3b8, 2235bfc04aecSBorislav Petkov 0x1dbb, 0x3b66, 0x715c, 0xe3f8, 2236bfc04aecSBorislav Petkov 0x4397, 0xc27e, 0x17fc, 0x3ea8, 2237bfc04aecSBorislav Petkov 0x1617, 0x3d3e, 0x6464, 0xb8b8, 2238bfc04aecSBorislav Petkov 0x23ff, 0x12aa, 0xab6c, 0x56d8, 2239bfc04aecSBorislav Petkov 0x2dfb, 0x1ba6, 0x913c, 0x7328, 2240bfc04aecSBorislav Petkov 0x185d, 0x2ca6, 0x7914, 0x9e28, 2241bfc04aecSBorislav Petkov 0x171b, 0x3e36, 0x7d7c, 0xebe8, 2242bfc04aecSBorislav Petkov 0x4199, 0x82ee, 0x19f4, 0x2e58, 2243bfc04aecSBorislav Petkov 0x4807, 0xc40e, 0x130c, 0x3208, 2244bfc04aecSBorislav Petkov 0x1905, 0x2e0a, 0x5804, 0xac08, 2245bfc04aecSBorislav Petkov 0x213f, 0x132a, 0xadfc, 0x5ba8, 2246bfc04aecSBorislav Petkov 0x19a9, 0x2efe, 0xb5cc, 0x6f88, 2247b1289d6fSDoug Thompson }; 2248b1289d6fSDoug Thompson 2249c7e5301aSDaniel J Blueman static const u16 x8_vectors[] = { 2250bfc04aecSBorislav Petkov 0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480, 2251bfc04aecSBorislav Petkov 0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80, 2252bfc04aecSBorislav Petkov 0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80, 2253bfc04aecSBorislav Petkov 0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80, 2254bfc04aecSBorislav Petkov 0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780, 2255bfc04aecSBorislav Petkov 0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080, 2256bfc04aecSBorislav Petkov 0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080, 2257bfc04aecSBorislav Petkov 0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080, 2258bfc04aecSBorislav Petkov 0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80, 2259bfc04aecSBorislav Petkov 0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580, 2260bfc04aecSBorislav Petkov 0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880, 2261bfc04aecSBorislav Petkov 0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280, 2262bfc04aecSBorislav Petkov 0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180, 2263bfc04aecSBorislav Petkov 0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580, 2264bfc04aecSBorislav Petkov 0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280, 2265bfc04aecSBorislav Petkov 0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180, 2266bfc04aecSBorislav Petkov 0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080, 2267bfc04aecSBorislav Petkov 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 2268bfc04aecSBorislav Petkov 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000, 2269bfc04aecSBorislav Petkov }; 2270bfc04aecSBorislav Petkov 2271c7e5301aSDaniel J Blueman static int decode_syndrome(u16 syndrome, const u16 *vectors, unsigned num_vecs, 2272d34a6ecdSBorislav Petkov unsigned v_dim) 2273b1289d6fSDoug Thompson { 2274bfc04aecSBorislav Petkov unsigned int i, err_sym; 2275b1289d6fSDoug Thompson 2276bfc04aecSBorislav Petkov for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) { 2277bfc04aecSBorislav Petkov u16 s = syndrome; 2278d34a6ecdSBorislav Petkov unsigned v_idx = err_sym * v_dim; 2279d34a6ecdSBorislav Petkov unsigned v_end = (err_sym + 1) * v_dim; 2280b1289d6fSDoug Thompson 2281bfc04aecSBorislav Petkov /* walk over all 16 bits of the syndrome */ 2282bfc04aecSBorislav Petkov for (i = 1; i < (1U << 16); i <<= 1) { 2283bfc04aecSBorislav Petkov 2284bfc04aecSBorislav Petkov /* if bit is set in that eigenvector... */ 2285bfc04aecSBorislav Petkov if (v_idx < v_end && vectors[v_idx] & i) { 2286bfc04aecSBorislav Petkov u16 ev_comp = vectors[v_idx++]; 2287bfc04aecSBorislav Petkov 2288bfc04aecSBorislav Petkov /* ... and bit set in the modified syndrome, */ 2289bfc04aecSBorislav Petkov if (s & i) { 2290bfc04aecSBorislav Petkov /* remove it. */ 2291bfc04aecSBorislav Petkov s ^= ev_comp; 2292bfc04aecSBorislav Petkov 2293bfc04aecSBorislav Petkov if (!s) 2294bfc04aecSBorislav Petkov return err_sym; 2295bfc04aecSBorislav Petkov } 2296bfc04aecSBorislav Petkov 2297bfc04aecSBorislav Petkov } else if (s & i) 2298bfc04aecSBorislav Petkov /* can't get to zero, move to next symbol */ 2299bfc04aecSBorislav Petkov break; 2300bfc04aecSBorislav Petkov } 2301b1289d6fSDoug Thompson } 2302b1289d6fSDoug Thompson 2303956b9ba1SJoe Perches edac_dbg(0, "syndrome(%x) not found\n", syndrome); 2304b1289d6fSDoug Thompson return -1; 2305b1289d6fSDoug Thompson } 2306d27bf6faSDoug Thompson 2307bfc04aecSBorislav Petkov static int map_err_sym_to_channel(int err_sym, int sym_size) 2308bfc04aecSBorislav Petkov { 2309bfc04aecSBorislav Petkov if (sym_size == 4) 2310bfc04aecSBorislav Petkov switch (err_sym) { 2311bfc04aecSBorislav Petkov case 0x20: 2312bfc04aecSBorislav Petkov case 0x21: 2313bfc04aecSBorislav Petkov return 0; 2314bfc04aecSBorislav Petkov break; 2315bfc04aecSBorislav Petkov case 0x22: 2316bfc04aecSBorislav Petkov case 0x23: 2317bfc04aecSBorislav Petkov return 1; 2318bfc04aecSBorislav Petkov break; 2319bfc04aecSBorislav Petkov default: 2320bfc04aecSBorislav Petkov return err_sym >> 4; 2321bfc04aecSBorislav Petkov break; 2322bfc04aecSBorislav Petkov } 2323bfc04aecSBorislav Petkov /* x8 symbols */ 2324bfc04aecSBorislav Petkov else 2325bfc04aecSBorislav Petkov switch (err_sym) { 2326bfc04aecSBorislav Petkov /* imaginary bits not in a DIMM */ 2327bfc04aecSBorislav Petkov case 0x10: 2328bfc04aecSBorislav Petkov WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n", 2329bfc04aecSBorislav Petkov err_sym); 2330bfc04aecSBorislav Petkov return -1; 2331bfc04aecSBorislav Petkov break; 2332bfc04aecSBorislav Petkov 2333bfc04aecSBorislav Petkov case 0x11: 2334bfc04aecSBorislav Petkov return 0; 2335bfc04aecSBorislav Petkov break; 2336bfc04aecSBorislav Petkov case 0x12: 2337bfc04aecSBorislav Petkov return 1; 2338bfc04aecSBorislav Petkov break; 2339bfc04aecSBorislav Petkov default: 2340bfc04aecSBorislav Petkov return err_sym >> 3; 2341bfc04aecSBorislav Petkov break; 2342bfc04aecSBorislav Petkov } 2343bfc04aecSBorislav Petkov return -1; 2344bfc04aecSBorislav Petkov } 2345bfc04aecSBorislav Petkov 2346bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome) 2347bfc04aecSBorislav Petkov { 2348bfc04aecSBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 2349ad6a32e9SBorislav Petkov int err_sym = -1; 2350bfc04aecSBorislav Petkov 2351a3b7db09SBorislav Petkov if (pvt->ecc_sym_sz == 8) 2352bfc04aecSBorislav Petkov err_sym = decode_syndrome(syndrome, x8_vectors, 2353ad6a32e9SBorislav Petkov ARRAY_SIZE(x8_vectors), 2354a3b7db09SBorislav Petkov pvt->ecc_sym_sz); 2355a3b7db09SBorislav Petkov else if (pvt->ecc_sym_sz == 4) 2356ad6a32e9SBorislav Petkov err_sym = decode_syndrome(syndrome, x4_vectors, 2357ad6a32e9SBorislav Petkov ARRAY_SIZE(x4_vectors), 2358a3b7db09SBorislav Petkov pvt->ecc_sym_sz); 2359ad6a32e9SBorislav Petkov else { 2360a3b7db09SBorislav Petkov amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz); 2361ad6a32e9SBorislav Petkov return err_sym; 2362bfc04aecSBorislav Petkov } 2363ad6a32e9SBorislav Petkov 2364a3b7db09SBorislav Petkov return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz); 236541c31044SBorislav Petkov } 2366bfc04aecSBorislav Petkov 2367e70984d9SYazen Ghannam static void __log_ecc_error(struct mem_ctl_info *mci, struct err_info *err, 236833ca0643SBorislav Petkov u8 ecc_type) 2369d27bf6faSDoug Thompson { 237033ca0643SBorislav Petkov enum hw_event_mc_err_type err_type; 237133ca0643SBorislav Petkov const char *string; 2372d27bf6faSDoug Thompson 237333ca0643SBorislav Petkov if (ecc_type == 2) 237433ca0643SBorislav Petkov err_type = HW_EVENT_ERR_CORRECTED; 237533ca0643SBorislav Petkov else if (ecc_type == 1) 237633ca0643SBorislav Petkov err_type = HW_EVENT_ERR_UNCORRECTED; 2377d12a969eSYazen Ghannam else if (ecc_type == 3) 2378d12a969eSYazen Ghannam err_type = HW_EVENT_ERR_DEFERRED; 237933ca0643SBorislav Petkov else { 238033ca0643SBorislav Petkov WARN(1, "Something is rotten in the state of Denmark.\n"); 2381d27bf6faSDoug Thompson return; 2382d27bf6faSDoug Thompson } 2383d27bf6faSDoug Thompson 238433ca0643SBorislav Petkov switch (err->err_code) { 238533ca0643SBorislav Petkov case DECODE_OK: 238633ca0643SBorislav Petkov string = ""; 238733ca0643SBorislav Petkov break; 238833ca0643SBorislav Petkov case ERR_NODE: 238933ca0643SBorislav Petkov string = "Failed to map error addr to a node"; 239033ca0643SBorislav Petkov break; 239133ca0643SBorislav Petkov case ERR_CSROW: 239233ca0643SBorislav Petkov string = "Failed to map error addr to a csrow"; 239333ca0643SBorislav Petkov break; 239433ca0643SBorislav Petkov case ERR_CHANNEL: 2395713ad546SYazen Ghannam string = "Unknown syndrome - possible error reporting race"; 2396713ad546SYazen Ghannam break; 2397713ad546SYazen Ghannam case ERR_SYND: 2398713ad546SYazen Ghannam string = "MCA_SYND not valid - unknown syndrome and csrow"; 2399713ad546SYazen Ghannam break; 2400713ad546SYazen Ghannam case ERR_NORM_ADDR: 2401713ad546SYazen Ghannam string = "Cannot decode normalized address"; 240233ca0643SBorislav Petkov break; 240333ca0643SBorislav Petkov default: 240433ca0643SBorislav Petkov string = "WTF error"; 240533ca0643SBorislav Petkov break; 2406d27bf6faSDoug Thompson } 240733ca0643SBorislav Petkov 240833ca0643SBorislav Petkov edac_mc_handle_error(err_type, mci, 1, 240933ca0643SBorislav Petkov err->page, err->offset, err->syndrome, 241033ca0643SBorislav Petkov err->csrow, err->channel, -1, 241133ca0643SBorislav Petkov string, ""); 2412d27bf6faSDoug Thompson } 2413d27bf6faSDoug Thompson 2414df781d03SBorislav Petkov static inline void decode_bus_error(int node_id, struct mce *m) 2415d27bf6faSDoug Thompson { 24160c510cc8SDaniel J Blueman struct mem_ctl_info *mci; 24170c510cc8SDaniel J Blueman struct amd64_pvt *pvt; 2418f192c7b1SBorislav Petkov u8 ecc_type = (m->status >> 45) & 0x3; 241966fed2d4SBorislav Petkov u8 xec = XEC(m->status, 0x1f); 242066fed2d4SBorislav Petkov u16 ec = EC(m->status); 242133ca0643SBorislav Petkov u64 sys_addr; 242233ca0643SBorislav Petkov struct err_info err; 2423d27bf6faSDoug Thompson 24240c510cc8SDaniel J Blueman mci = edac_mc_find(node_id); 24250c510cc8SDaniel J Blueman if (!mci) 24260c510cc8SDaniel J Blueman return; 24270c510cc8SDaniel J Blueman 24280c510cc8SDaniel J Blueman pvt = mci->pvt_info; 24290c510cc8SDaniel J Blueman 243066fed2d4SBorislav Petkov /* Bail out early if this was an 'observed' error */ 24315980bb9cSBorislav Petkov if (PP(ec) == NBSL_PP_OBS) 2432b70ef010SBorislav Petkov return; 2433d27bf6faSDoug Thompson 2434ecaf5606SBorislav Petkov /* Do only ECC errors */ 2435ecaf5606SBorislav Petkov if (xec && xec != F10_NBSL_EXT_ERR_ECC) 2436d27bf6faSDoug Thompson return; 2437d27bf6faSDoug Thompson 243833ca0643SBorislav Petkov memset(&err, 0, sizeof(err)); 243933ca0643SBorislav Petkov 2440a4b4bedcSBorislav Petkov sys_addr = get_error_address(pvt, m); 244133ca0643SBorislav Petkov 2442ecaf5606SBorislav Petkov if (ecc_type == 2) 244333ca0643SBorislav Petkov err.syndrome = extract_syndrome(m->status); 244433ca0643SBorislav Petkov 244533ca0643SBorislav Petkov pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err); 244633ca0643SBorislav Petkov 2447e70984d9SYazen Ghannam __log_ecc_error(mci, &err, ecc_type); 2448d27bf6faSDoug Thompson } 2449d27bf6faSDoug Thompson 24500ec449eeSDoug Thompson /* 2451713ad546SYazen Ghannam * To find the UMC channel represented by this bank we need to match on its 2452713ad546SYazen Ghannam * instance_id. The instance_id of a bank is held in the lower 32 bits of its 2453713ad546SYazen Ghannam * IPID. 2454713ad546SYazen Ghannam */ 2455713ad546SYazen Ghannam static int find_umc_channel(struct amd64_pvt *pvt, struct mce *m) 2456713ad546SYazen Ghannam { 2457713ad546SYazen Ghannam u32 umc_instance_id[] = {0x50f00, 0x150f00}; 2458713ad546SYazen Ghannam u32 instance_id = m->ipid & GENMASK(31, 0); 2459713ad546SYazen Ghannam int i, channel = -1; 2460713ad546SYazen Ghannam 2461713ad546SYazen Ghannam for (i = 0; i < ARRAY_SIZE(umc_instance_id); i++) 2462713ad546SYazen Ghannam if (umc_instance_id[i] == instance_id) 2463713ad546SYazen Ghannam channel = i; 2464713ad546SYazen Ghannam 2465713ad546SYazen Ghannam return channel; 2466713ad546SYazen Ghannam } 2467713ad546SYazen Ghannam 2468713ad546SYazen Ghannam static void decode_umc_error(int node_id, struct mce *m) 2469713ad546SYazen Ghannam { 2470713ad546SYazen Ghannam u8 ecc_type = (m->status >> 45) & 0x3; 2471713ad546SYazen Ghannam struct mem_ctl_info *mci; 2472713ad546SYazen Ghannam struct amd64_pvt *pvt; 2473713ad546SYazen Ghannam struct err_info err; 2474713ad546SYazen Ghannam u64 sys_addr; 2475713ad546SYazen Ghannam 2476713ad546SYazen Ghannam mci = edac_mc_find(node_id); 2477713ad546SYazen Ghannam if (!mci) 2478713ad546SYazen Ghannam return; 2479713ad546SYazen Ghannam 2480713ad546SYazen Ghannam pvt = mci->pvt_info; 2481713ad546SYazen Ghannam 2482713ad546SYazen Ghannam memset(&err, 0, sizeof(err)); 2483713ad546SYazen Ghannam 2484713ad546SYazen Ghannam if (m->status & MCI_STATUS_DEFERRED) 2485713ad546SYazen Ghannam ecc_type = 3; 2486713ad546SYazen Ghannam 2487713ad546SYazen Ghannam err.channel = find_umc_channel(pvt, m); 2488713ad546SYazen Ghannam if (err.channel < 0) { 2489713ad546SYazen Ghannam err.err_code = ERR_CHANNEL; 2490713ad546SYazen Ghannam goto log_error; 2491713ad546SYazen Ghannam } 2492713ad546SYazen Ghannam 2493713ad546SYazen Ghannam if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) { 2494713ad546SYazen Ghannam err.err_code = ERR_NORM_ADDR; 2495713ad546SYazen Ghannam goto log_error; 2496713ad546SYazen Ghannam } 2497713ad546SYazen Ghannam 2498713ad546SYazen Ghannam error_address_to_page_and_offset(sys_addr, &err); 2499713ad546SYazen Ghannam 2500713ad546SYazen Ghannam if (!(m->status & MCI_STATUS_SYNDV)) { 2501713ad546SYazen Ghannam err.err_code = ERR_SYND; 2502713ad546SYazen Ghannam goto log_error; 2503713ad546SYazen Ghannam } 2504713ad546SYazen Ghannam 2505713ad546SYazen Ghannam if (ecc_type == 2) { 2506713ad546SYazen Ghannam u8 length = (m->synd >> 18) & 0x3f; 2507713ad546SYazen Ghannam 2508713ad546SYazen Ghannam if (length) 2509713ad546SYazen Ghannam err.syndrome = (m->synd >> 32) & GENMASK(length - 1, 0); 2510713ad546SYazen Ghannam else 2511713ad546SYazen Ghannam err.err_code = ERR_CHANNEL; 2512713ad546SYazen Ghannam } 2513713ad546SYazen Ghannam 2514713ad546SYazen Ghannam err.csrow = m->synd & 0x7; 2515713ad546SYazen Ghannam 2516713ad546SYazen Ghannam log_error: 2517713ad546SYazen Ghannam __log_ecc_error(mci, &err, ecc_type); 2518713ad546SYazen Ghannam } 2519713ad546SYazen Ghannam 2520713ad546SYazen Ghannam /* 25213f37a36bSBorislav Petkov * Use pvt->F3 which contains the F3 CPU PCI device to get the related 25223f37a36bSBorislav Petkov * F1 (AddrMap) and F2 (Dct) devices. Return negative value on error. 2523936fc3afSYazen Ghannam * Reserve F0 and F6 on systems with a UMC. 25240ec449eeSDoug Thompson */ 2525936fc3afSYazen Ghannam static int 2526936fc3afSYazen Ghannam reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2) 25270ec449eeSDoug Thompson { 2528936fc3afSYazen Ghannam if (pvt->umc) { 2529936fc3afSYazen Ghannam pvt->F0 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3); 2530936fc3afSYazen Ghannam if (!pvt->F0) { 25315246c540SBorislav Petkov amd64_err("F0 not found, device 0x%x (broken BIOS?)\n", pci_id1); 2532936fc3afSYazen Ghannam return -ENODEV; 2533936fc3afSYazen Ghannam } 2534936fc3afSYazen Ghannam 2535936fc3afSYazen Ghannam pvt->F6 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3); 2536936fc3afSYazen Ghannam if (!pvt->F6) { 2537936fc3afSYazen Ghannam pci_dev_put(pvt->F0); 2538936fc3afSYazen Ghannam pvt->F0 = NULL; 2539936fc3afSYazen Ghannam 25405246c540SBorislav Petkov amd64_err("F6 not found: device 0x%x (broken BIOS?)\n", pci_id2); 2541936fc3afSYazen Ghannam return -ENODEV; 2542936fc3afSYazen Ghannam } 25435246c540SBorislav Petkov 2544936fc3afSYazen Ghannam edac_dbg(1, "F0: %s\n", pci_name(pvt->F0)); 2545936fc3afSYazen Ghannam edac_dbg(1, "F3: %s\n", pci_name(pvt->F3)); 2546936fc3afSYazen Ghannam edac_dbg(1, "F6: %s\n", pci_name(pvt->F6)); 2547936fc3afSYazen Ghannam 2548936fc3afSYazen Ghannam return 0; 2549936fc3afSYazen Ghannam } 2550936fc3afSYazen Ghannam 25510ec449eeSDoug Thompson /* Reserve the ADDRESS MAP Device */ 2552936fc3afSYazen Ghannam pvt->F1 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3); 25538d5b5d9cSBorislav Petkov if (!pvt->F1) { 25545246c540SBorislav Petkov amd64_err("F1 not found: device 0x%x (broken BIOS?)\n", pci_id1); 2555bbd0c1f6SBorislav Petkov return -ENODEV; 25560ec449eeSDoug Thompson } 25570ec449eeSDoug Thompson 25583f37a36bSBorislav Petkov /* Reserve the DCT Device */ 2559936fc3afSYazen Ghannam pvt->F2 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3); 25603f37a36bSBorislav Petkov if (!pvt->F2) { 25618d5b5d9cSBorislav Petkov pci_dev_put(pvt->F1); 25628d5b5d9cSBorislav Petkov pvt->F1 = NULL; 25630ec449eeSDoug Thompson 25645246c540SBorislav Petkov amd64_err("F2 not found: device 0x%x (broken BIOS?)\n", pci_id2); 2565bbd0c1f6SBorislav Petkov return -ENODEV; 25660ec449eeSDoug Thompson } 2567936fc3afSYazen Ghannam 2568956b9ba1SJoe Perches edac_dbg(1, "F1: %s\n", pci_name(pvt->F1)); 2569956b9ba1SJoe Perches edac_dbg(1, "F2: %s\n", pci_name(pvt->F2)); 2570956b9ba1SJoe Perches edac_dbg(1, "F3: %s\n", pci_name(pvt->F3)); 25710ec449eeSDoug Thompson 25720ec449eeSDoug Thompson return 0; 25730ec449eeSDoug Thompson } 25740ec449eeSDoug Thompson 2575360b7f3cSBorislav Petkov static void free_mc_sibling_devs(struct amd64_pvt *pvt) 25760ec449eeSDoug Thompson { 2577936fc3afSYazen Ghannam if (pvt->umc) { 2578936fc3afSYazen Ghannam pci_dev_put(pvt->F0); 2579936fc3afSYazen Ghannam pci_dev_put(pvt->F6); 2580936fc3afSYazen Ghannam } else { 25818d5b5d9cSBorislav Petkov pci_dev_put(pvt->F1); 25823f37a36bSBorislav Petkov pci_dev_put(pvt->F2); 25830ec449eeSDoug Thompson } 2584936fc3afSYazen Ghannam } 25850ec449eeSDoug Thompson 2586b64ce7cdSYazen Ghannam static void determine_ecc_sym_sz(struct amd64_pvt *pvt) 2587b64ce7cdSYazen Ghannam { 2588b64ce7cdSYazen Ghannam pvt->ecc_sym_sz = 4; 2589b64ce7cdSYazen Ghannam 2590b64ce7cdSYazen Ghannam if (pvt->umc) { 2591b64ce7cdSYazen Ghannam u8 i; 2592b64ce7cdSYazen Ghannam 2593b64ce7cdSYazen Ghannam for (i = 0; i < NUM_UMCS; i++) { 2594b64ce7cdSYazen Ghannam /* Check enabled channels only: */ 2595b64ce7cdSYazen Ghannam if ((pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) && 2596b64ce7cdSYazen Ghannam (pvt->umc[i].ecc_ctrl & BIT(7))) { 2597b64ce7cdSYazen Ghannam pvt->ecc_sym_sz = 8; 2598b64ce7cdSYazen Ghannam break; 2599b64ce7cdSYazen Ghannam } 2600b64ce7cdSYazen Ghannam } 2601b64ce7cdSYazen Ghannam 2602b64ce7cdSYazen Ghannam return; 2603b64ce7cdSYazen Ghannam } 2604b64ce7cdSYazen Ghannam 2605b64ce7cdSYazen Ghannam if (pvt->fam >= 0x10) { 2606b64ce7cdSYazen Ghannam u32 tmp; 2607b64ce7cdSYazen Ghannam 2608b64ce7cdSYazen Ghannam amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp); 2609b64ce7cdSYazen Ghannam /* F16h has only DCT0, so no need to read dbam1. */ 2610b64ce7cdSYazen Ghannam if (pvt->fam != 0x16) 2611b64ce7cdSYazen Ghannam amd64_read_dct_pci_cfg(pvt, 1, DBAM0, &pvt->dbam1); 2612b64ce7cdSYazen Ghannam 2613b64ce7cdSYazen Ghannam /* F10h, revD and later can do x8 ECC too. */ 2614b64ce7cdSYazen Ghannam if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25)) 2615b64ce7cdSYazen Ghannam pvt->ecc_sym_sz = 8; 2616b64ce7cdSYazen Ghannam } 2617b64ce7cdSYazen Ghannam } 2618b64ce7cdSYazen Ghannam 2619b64ce7cdSYazen Ghannam /* 2620b64ce7cdSYazen Ghannam * Retrieve the hardware registers of the memory controller. 2621b64ce7cdSYazen Ghannam */ 2622b64ce7cdSYazen Ghannam static void __read_mc_regs_df(struct amd64_pvt *pvt) 2623b64ce7cdSYazen Ghannam { 2624b64ce7cdSYazen Ghannam u8 nid = pvt->mc_node_id; 2625b64ce7cdSYazen Ghannam struct amd64_umc *umc; 2626b64ce7cdSYazen Ghannam u32 i, umc_base; 2627b64ce7cdSYazen Ghannam 2628b64ce7cdSYazen Ghannam /* Read registers from each UMC */ 2629b64ce7cdSYazen Ghannam for (i = 0; i < NUM_UMCS; i++) { 2630b64ce7cdSYazen Ghannam 2631b64ce7cdSYazen Ghannam umc_base = get_umc_base(i); 2632b64ce7cdSYazen Ghannam umc = &pvt->umc[i]; 2633b64ce7cdSYazen Ghannam 263407ed82efSYazen Ghannam amd_smn_read(nid, umc_base + UMCCH_DIMM_CFG, &umc->dimm_cfg); 263507ed82efSYazen Ghannam amd_smn_read(nid, umc_base + UMCCH_UMC_CFG, &umc->umc_cfg); 2636b64ce7cdSYazen Ghannam amd_smn_read(nid, umc_base + UMCCH_SDP_CTRL, &umc->sdp_ctrl); 2637b64ce7cdSYazen Ghannam amd_smn_read(nid, umc_base + UMCCH_ECC_CTRL, &umc->ecc_ctrl); 263807ed82efSYazen Ghannam amd_smn_read(nid, umc_base + UMCCH_UMC_CAP_HI, &umc->umc_cap_hi); 2639b64ce7cdSYazen Ghannam } 2640b64ce7cdSYazen Ghannam } 2641b64ce7cdSYazen Ghannam 26420ec449eeSDoug Thompson /* 26430ec449eeSDoug Thompson * Retrieve the hardware registers of the memory controller (this includes the 26440ec449eeSDoug Thompson * 'Address Map' and 'Misc' device regs) 26450ec449eeSDoug Thompson */ 2646360b7f3cSBorislav Petkov static void read_mc_regs(struct amd64_pvt *pvt) 26470ec449eeSDoug Thompson { 2648b64ce7cdSYazen Ghannam unsigned int range; 26490ec449eeSDoug Thompson u64 msr_val; 26500ec449eeSDoug Thompson 26510ec449eeSDoug Thompson /* 26520ec449eeSDoug Thompson * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since 2653b64ce7cdSYazen Ghannam * those are Read-As-Zero. 26540ec449eeSDoug Thompson */ 2655e97f8bb8SBorislav Petkov rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem); 2656956b9ba1SJoe Perches edac_dbg(0, " TOP_MEM: 0x%016llx\n", pvt->top_mem); 26570ec449eeSDoug Thompson 2658b64ce7cdSYazen Ghannam /* Check first whether TOP_MEM2 is enabled: */ 26590ec449eeSDoug Thompson rdmsrl(MSR_K8_SYSCFG, msr_val); 2660b64ce7cdSYazen Ghannam if (msr_val & BIT(21)) { 2661e97f8bb8SBorislav Petkov rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2); 2662956b9ba1SJoe Perches edac_dbg(0, " TOP_MEM2: 0x%016llx\n", pvt->top_mem2); 2663b64ce7cdSYazen Ghannam } else { 2664956b9ba1SJoe Perches edac_dbg(0, " TOP_MEM2 disabled\n"); 2665b64ce7cdSYazen Ghannam } 2666b64ce7cdSYazen Ghannam 2667b64ce7cdSYazen Ghannam if (pvt->umc) { 2668b64ce7cdSYazen Ghannam __read_mc_regs_df(pvt); 2669b64ce7cdSYazen Ghannam amd64_read_pci_cfg(pvt->F0, DF_DHAR, &pvt->dhar); 2670b64ce7cdSYazen Ghannam 2671b64ce7cdSYazen Ghannam goto skip; 2672b64ce7cdSYazen Ghannam } 26730ec449eeSDoug Thompson 26745980bb9cSBorislav Petkov amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap); 26750ec449eeSDoug Thompson 26765a5d2371SBorislav Petkov read_dram_ctl_register(pvt); 26770ec449eeSDoug Thompson 26787f19bf75SBorislav Petkov for (range = 0; range < DRAM_RANGES; range++) { 26797f19bf75SBorislav Petkov u8 rw; 26800ec449eeSDoug Thompson 26817f19bf75SBorislav Petkov /* read settings for this DRAM range */ 26827f19bf75SBorislav Petkov read_dram_base_limit_regs(pvt, range); 2683e97f8bb8SBorislav Petkov 26847f19bf75SBorislav Petkov rw = dram_rw(pvt, range); 26857f19bf75SBorislav Petkov if (!rw) 26867f19bf75SBorislav Petkov continue; 26877f19bf75SBorislav Petkov 2688956b9ba1SJoe Perches edac_dbg(1, " DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n", 26897f19bf75SBorislav Petkov range, 26907f19bf75SBorislav Petkov get_dram_base(pvt, range), 26917f19bf75SBorislav Petkov get_dram_limit(pvt, range)); 26927f19bf75SBorislav Petkov 2693956b9ba1SJoe Perches edac_dbg(1, " IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n", 26947f19bf75SBorislav Petkov dram_intlv_en(pvt, range) ? "Enabled" : "Disabled", 26957f19bf75SBorislav Petkov (rw & 0x1) ? "R" : "-", 26967f19bf75SBorislav Petkov (rw & 0x2) ? "W" : "-", 26977f19bf75SBorislav Petkov dram_intlv_sel(pvt, range), 26987f19bf75SBorislav Petkov dram_dst_node(pvt, range)); 26990ec449eeSDoug Thompson } 27000ec449eeSDoug Thompson 2701bc21fa57SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar); 27027981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DBAM0, &pvt->dbam0); 27030ec449eeSDoug Thompson 27048d5b5d9cSBorislav Petkov amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare); 27050ec449eeSDoug Thompson 27067981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DCLR0, &pvt->dclr0); 27077981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DCHR0, &pvt->dchr0); 27080ec449eeSDoug Thompson 270978da121eSBorislav Petkov if (!dct_ganging_enabled(pvt)) { 27107981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 1, DCLR0, &pvt->dclr1); 27117981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 1, DCHR0, &pvt->dchr1); 27120ec449eeSDoug Thompson } 2713b2b0c605SBorislav Petkov 2714b64ce7cdSYazen Ghannam skip: 2715b64ce7cdSYazen Ghannam read_dct_base_mask(pvt); 2716b64ce7cdSYazen Ghannam 2717a597d2a5SAravind Gopalakrishnan determine_memory_type(pvt); 2718a597d2a5SAravind Gopalakrishnan edac_dbg(1, " DIMM type: %s\n", edac_mem_types[pvt->dram_type]); 2719a3b7db09SBorislav Petkov 2720b64ce7cdSYazen Ghannam determine_ecc_sym_sz(pvt); 2721a3b7db09SBorislav Petkov 2722b2b0c605SBorislav Petkov dump_misc_regs(pvt); 27230ec449eeSDoug Thompson } 27240ec449eeSDoug Thompson 27250ec449eeSDoug Thompson /* 27260ec449eeSDoug Thompson * NOTE: CPU Revision Dependent code 27270ec449eeSDoug Thompson * 27280ec449eeSDoug Thompson * Input: 272911c75eadSBorislav Petkov * @csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1) 27300ec449eeSDoug Thompson * k8 private pointer to --> 27310ec449eeSDoug Thompson * DRAM Bank Address mapping register 27320ec449eeSDoug Thompson * node_id 27330ec449eeSDoug Thompson * DCL register where dual_channel_active is 27340ec449eeSDoug Thompson * 27350ec449eeSDoug Thompson * The DBAM register consists of 4 sets of 4 bits each definitions: 27360ec449eeSDoug Thompson * 27370ec449eeSDoug Thompson * Bits: CSROWs 27380ec449eeSDoug Thompson * 0-3 CSROWs 0 and 1 27390ec449eeSDoug Thompson * 4-7 CSROWs 2 and 3 27400ec449eeSDoug Thompson * 8-11 CSROWs 4 and 5 27410ec449eeSDoug Thompson * 12-15 CSROWs 6 and 7 27420ec449eeSDoug Thompson * 27430ec449eeSDoug Thompson * Values range from: 0 to 15 27440ec449eeSDoug Thompson * The meaning of the values depends on CPU revision and dual-channel state, 27450ec449eeSDoug Thompson * see relevant BKDG more info. 27460ec449eeSDoug Thompson * 27470ec449eeSDoug Thompson * The memory controller provides for total of only 8 CSROWs in its current 27480ec449eeSDoug Thompson * architecture. Each "pair" of CSROWs normally represents just one DIMM in 27490ec449eeSDoug Thompson * single channel or two (2) DIMMs in dual channel mode. 27500ec449eeSDoug Thompson * 27510ec449eeSDoug Thompson * The following code logic collapses the various tables for CSROW based on CPU 27520ec449eeSDoug Thompson * revision. 27530ec449eeSDoug Thompson * 27540ec449eeSDoug Thompson * Returns: 27550ec449eeSDoug Thompson * The number of PAGE_SIZE pages on the specified CSROW number it 27560ec449eeSDoug Thompson * encompasses 27570ec449eeSDoug Thompson * 27580ec449eeSDoug Thompson */ 2759d1ea71cdSBorislav Petkov static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr) 27600ec449eeSDoug Thompson { 27611433eb99SBorislav Petkov u32 cs_mode, nr_pages; 2762f92cae45SAshish Shenoy u32 dbam = dct ? pvt->dbam1 : pvt->dbam0; 27630ec449eeSDoug Thompson 276410de6497SBorislav Petkov 27650ec449eeSDoug Thompson /* 27660ec449eeSDoug Thompson * The math on this doesn't look right on the surface because x/2*4 can 27670ec449eeSDoug Thompson * be simplified to x*2 but this expression makes use of the fact that 27680ec449eeSDoug Thompson * it is integral math where 1/2=0. This intermediate value becomes the 27690ec449eeSDoug Thompson * number of bits to shift the DBAM register to extract the proper CSROW 27700ec449eeSDoug Thompson * field. 27710ec449eeSDoug Thompson */ 27720a5dfc31SBorislav Petkov cs_mode = DBAM_DIMM(csrow_nr / 2, dbam); 27730ec449eeSDoug Thompson 2774a597d2a5SAravind Gopalakrishnan nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, (csrow_nr / 2)) 2775a597d2a5SAravind Gopalakrishnan << (20 - PAGE_SHIFT); 27760ec449eeSDoug Thompson 277710de6497SBorislav Petkov edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n", 277810de6497SBorislav Petkov csrow_nr, dct, cs_mode); 277910de6497SBorislav Petkov edac_dbg(0, "nr_pages/channel: %u\n", nr_pages); 27800ec449eeSDoug Thompson 27810ec449eeSDoug Thompson return nr_pages; 27820ec449eeSDoug Thompson } 27830ec449eeSDoug Thompson 27840ec449eeSDoug Thompson /* 27850ec449eeSDoug Thompson * Initialize the array of csrow attribute instances, based on the values 27860ec449eeSDoug Thompson * from pci config hardware registers. 27870ec449eeSDoug Thompson */ 2788360b7f3cSBorislav Petkov static int init_csrows(struct mem_ctl_info *mci) 27890ec449eeSDoug Thompson { 279010de6497SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 27912d09d8f3SYazen Ghannam enum edac_type edac_mode = EDAC_NONE; 27920ec449eeSDoug Thompson struct csrow_info *csrow; 2793de3910ebSMauro Carvalho Chehab struct dimm_info *dimm; 279410de6497SBorislav Petkov int i, j, empty = 1; 2795a895bf8bSMauro Carvalho Chehab int nr_pages = 0; 279610de6497SBorislav Petkov u32 val; 27970ec449eeSDoug Thompson 27982d09d8f3SYazen Ghannam if (!pvt->umc) { 2799a97fa68eSBorislav Petkov amd64_read_pci_cfg(pvt->F3, NBCFG, &val); 28000ec449eeSDoug Thompson 28012299ef71SBorislav Petkov pvt->nbcfg = val; 28020ec449eeSDoug Thompson 2803956b9ba1SJoe Perches edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n", 28042299ef71SBorislav Petkov pvt->mc_node_id, val, 2805a97fa68eSBorislav Petkov !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE)); 28062d09d8f3SYazen Ghannam } 28070ec449eeSDoug Thompson 280810de6497SBorislav Petkov /* 280910de6497SBorislav Petkov * We iterate over DCT0 here but we look at DCT1 in parallel, if needed. 281010de6497SBorislav Petkov */ 281111c75eadSBorislav Petkov for_each_chip_select(i, 0, pvt) { 281210de6497SBorislav Petkov bool row_dct0 = !!csrow_enabled(i, 0, pvt); 281310de6497SBorislav Petkov bool row_dct1 = false; 28140ec449eeSDoug Thompson 2815a4b4bedcSBorislav Petkov if (pvt->fam != 0xf) 281610de6497SBorislav Petkov row_dct1 = !!csrow_enabled(i, 1, pvt); 281710de6497SBorislav Petkov 281810de6497SBorislav Petkov if (!row_dct0 && !row_dct1) 28190ec449eeSDoug Thompson continue; 28200ec449eeSDoug Thompson 282110de6497SBorislav Petkov csrow = mci->csrows[i]; 28220ec449eeSDoug Thompson empty = 0; 282311c75eadSBorislav Petkov 282410de6497SBorislav Petkov edac_dbg(1, "MC node: %d, csrow: %d\n", 282510de6497SBorislav Petkov pvt->mc_node_id, i); 282610de6497SBorislav Petkov 28271eef1282SMauro Carvalho Chehab if (row_dct0) { 2828d1ea71cdSBorislav Petkov nr_pages = get_csrow_nr_pages(pvt, 0, i); 28291eef1282SMauro Carvalho Chehab csrow->channels[0]->dimm->nr_pages = nr_pages; 28301eef1282SMauro Carvalho Chehab } 283110de6497SBorislav Petkov 283210de6497SBorislav Petkov /* K8 has only one DCT */ 2833a4b4bedcSBorislav Petkov if (pvt->fam != 0xf && row_dct1) { 2834d1ea71cdSBorislav Petkov int row_dct1_pages = get_csrow_nr_pages(pvt, 1, i); 28351eef1282SMauro Carvalho Chehab 28361eef1282SMauro Carvalho Chehab csrow->channels[1]->dimm->nr_pages = row_dct1_pages; 28371eef1282SMauro Carvalho Chehab nr_pages += row_dct1_pages; 28381eef1282SMauro Carvalho Chehab } 28390ec449eeSDoug Thompson 284010de6497SBorislav Petkov edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages); 28410ec449eeSDoug Thompson 28422d09d8f3SYazen Ghannam /* Determine DIMM ECC mode: */ 28432d09d8f3SYazen Ghannam if (pvt->umc) { 28442d09d8f3SYazen Ghannam if (mci->edac_ctl_cap & EDAC_FLAG_S4ECD4ED) 28452d09d8f3SYazen Ghannam edac_mode = EDAC_S4ECD4ED; 28462d09d8f3SYazen Ghannam else if (mci->edac_ctl_cap & EDAC_FLAG_SECDED) 28472d09d8f3SYazen Ghannam edac_mode = EDAC_SECDED; 28482d09d8f3SYazen Ghannam 28492d09d8f3SYazen Ghannam } else if (pvt->nbcfg & NBCFG_ECC_ENABLE) { 28502d09d8f3SYazen Ghannam edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) 28512d09d8f3SYazen Ghannam ? EDAC_S4ECD4ED 28522d09d8f3SYazen Ghannam : EDAC_SECDED; 28532d09d8f3SYazen Ghannam } 2854084a4fccSMauro Carvalho Chehab 2855084a4fccSMauro Carvalho Chehab for (j = 0; j < pvt->channel_count; j++) { 2856de3910ebSMauro Carvalho Chehab dimm = csrow->channels[j]->dimm; 2857a597d2a5SAravind Gopalakrishnan dimm->mtype = pvt->dram_type; 2858de3910ebSMauro Carvalho Chehab dimm->edac_mode = edac_mode; 2859084a4fccSMauro Carvalho Chehab } 28600ec449eeSDoug Thompson } 28610ec449eeSDoug Thompson 28620ec449eeSDoug Thompson return empty; 28630ec449eeSDoug Thompson } 2864d27bf6faSDoug Thompson 286506724535SBorislav Petkov /* get all cores on this DCT */ 28668b84c8dfSDaniel J Blueman static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, u16 nid) 2867f9431992SDoug Thompson { 286806724535SBorislav Petkov int cpu; 2869f9431992SDoug Thompson 287006724535SBorislav Petkov for_each_online_cpu(cpu) 287106724535SBorislav Petkov if (amd_get_nb_id(cpu) == nid) 287206724535SBorislav Petkov cpumask_set_cpu(cpu, mask); 2873f9431992SDoug Thompson } 2874f9431992SDoug Thompson 2875f9431992SDoug Thompson /* check MCG_CTL on all the cpus on this node */ 2876d1ea71cdSBorislav Petkov static bool nb_mce_bank_enabled_on_node(u16 nid) 2877f9431992SDoug Thompson { 2878ba578cb3SRusty Russell cpumask_var_t mask; 287950542251SBorislav Petkov int cpu, nbe; 288006724535SBorislav Petkov bool ret = false; 2881f9431992SDoug Thompson 2882ba578cb3SRusty Russell if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) { 288324f9a7feSBorislav Petkov amd64_warn("%s: Error allocating mask\n", __func__); 288406724535SBorislav Petkov return false; 288506724535SBorislav Petkov } 288606724535SBorislav Petkov 2887ba578cb3SRusty Russell get_cpus_on_this_dct_cpumask(mask, nid); 288806724535SBorislav Petkov 2889ba578cb3SRusty Russell rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs); 2890ba578cb3SRusty Russell 2891ba578cb3SRusty Russell for_each_cpu(cpu, mask) { 289250542251SBorislav Petkov struct msr *reg = per_cpu_ptr(msrs, cpu); 28935980bb9cSBorislav Petkov nbe = reg->l & MSR_MCGCTL_NBE; 289406724535SBorislav Petkov 2895956b9ba1SJoe Perches edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n", 289650542251SBorislav Petkov cpu, reg->q, 289706724535SBorislav Petkov (nbe ? "enabled" : "disabled")); 289806724535SBorislav Petkov 289906724535SBorislav Petkov if (!nbe) 290006724535SBorislav Petkov goto out; 290106724535SBorislav Petkov } 290206724535SBorislav Petkov ret = true; 290306724535SBorislav Petkov 290406724535SBorislav Petkov out: 2905ba578cb3SRusty Russell free_cpumask_var(mask); 2906f9431992SDoug Thompson return ret; 2907f9431992SDoug Thompson } 2908f9431992SDoug Thompson 2909c7e5301aSDaniel J Blueman static int toggle_ecc_err_reporting(struct ecc_settings *s, u16 nid, bool on) 2910f6d6ae96SBorislav Petkov { 2911f6d6ae96SBorislav Petkov cpumask_var_t cmask; 291250542251SBorislav Petkov int cpu; 2913f6d6ae96SBorislav Petkov 2914f6d6ae96SBorislav Petkov if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) { 291524f9a7feSBorislav Petkov amd64_warn("%s: error allocating mask\n", __func__); 29160de27884SPan Bian return -ENOMEM; 2917f6d6ae96SBorislav Petkov } 2918f6d6ae96SBorislav Petkov 2919ae7bb7c6SBorislav Petkov get_cpus_on_this_dct_cpumask(cmask, nid); 2920f6d6ae96SBorislav Petkov 2921f6d6ae96SBorislav Petkov rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); 2922f6d6ae96SBorislav Petkov 2923f6d6ae96SBorislav Petkov for_each_cpu(cpu, cmask) { 2924f6d6ae96SBorislav Petkov 292550542251SBorislav Petkov struct msr *reg = per_cpu_ptr(msrs, cpu); 292650542251SBorislav Petkov 2927f6d6ae96SBorislav Petkov if (on) { 29285980bb9cSBorislav Petkov if (reg->l & MSR_MCGCTL_NBE) 2929ae7bb7c6SBorislav Petkov s->flags.nb_mce_enable = 1; 2930f6d6ae96SBorislav Petkov 29315980bb9cSBorislav Petkov reg->l |= MSR_MCGCTL_NBE; 2932f6d6ae96SBorislav Petkov } else { 2933f6d6ae96SBorislav Petkov /* 2934d95cf4deSBorislav Petkov * Turn off NB MCE reporting only when it was off before 2935f6d6ae96SBorislav Petkov */ 2936ae7bb7c6SBorislav Petkov if (!s->flags.nb_mce_enable) 29375980bb9cSBorislav Petkov reg->l &= ~MSR_MCGCTL_NBE; 2938f6d6ae96SBorislav Petkov } 2939f6d6ae96SBorislav Petkov } 2940f6d6ae96SBorislav Petkov wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); 2941f6d6ae96SBorislav Petkov 2942f6d6ae96SBorislav Petkov free_cpumask_var(cmask); 2943f6d6ae96SBorislav Petkov 2944f6d6ae96SBorislav Petkov return 0; 2945f6d6ae96SBorislav Petkov } 2946f6d6ae96SBorislav Petkov 2947c7e5301aSDaniel J Blueman static bool enable_ecc_error_reporting(struct ecc_settings *s, u16 nid, 29482299ef71SBorislav Petkov struct pci_dev *F3) 2949f6d6ae96SBorislav Petkov { 29502299ef71SBorislav Petkov bool ret = true; 2951c9f4f26eSBorislav Petkov u32 value, mask = 0x3; /* UECC/CECC enable */ 2952f6d6ae96SBorislav Petkov 29532299ef71SBorislav Petkov if (toggle_ecc_err_reporting(s, nid, ON)) { 29542299ef71SBorislav Petkov amd64_warn("Error enabling ECC reporting over MCGCTL!\n"); 29552299ef71SBorislav Petkov return false; 29562299ef71SBorislav Petkov } 29572299ef71SBorislav Petkov 2958c9f4f26eSBorislav Petkov amd64_read_pci_cfg(F3, NBCTL, &value); 2959f6d6ae96SBorislav Petkov 2960ae7bb7c6SBorislav Petkov s->old_nbctl = value & mask; 2961ae7bb7c6SBorislav Petkov s->nbctl_valid = true; 2962f6d6ae96SBorislav Petkov 2963f6d6ae96SBorislav Petkov value |= mask; 2964c9f4f26eSBorislav Petkov amd64_write_pci_cfg(F3, NBCTL, value); 2965f6d6ae96SBorislav Petkov 2966a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 2967f6d6ae96SBorislav Petkov 2968956b9ba1SJoe Perches edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n", 2969a97fa68eSBorislav Petkov nid, value, !!(value & NBCFG_ECC_ENABLE)); 2970f6d6ae96SBorislav Petkov 2971a97fa68eSBorislav Petkov if (!(value & NBCFG_ECC_ENABLE)) { 297224f9a7feSBorislav Petkov amd64_warn("DRAM ECC disabled on this node, enabling...\n"); 2973f6d6ae96SBorislav Petkov 2974ae7bb7c6SBorislav Petkov s->flags.nb_ecc_prev = 0; 2975d95cf4deSBorislav Petkov 2976f6d6ae96SBorislav Petkov /* Attempt to turn on DRAM ECC Enable */ 2977a97fa68eSBorislav Petkov value |= NBCFG_ECC_ENABLE; 2978a97fa68eSBorislav Petkov amd64_write_pci_cfg(F3, NBCFG, value); 2979f6d6ae96SBorislav Petkov 2980a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 2981f6d6ae96SBorislav Petkov 2982a97fa68eSBorislav Petkov if (!(value & NBCFG_ECC_ENABLE)) { 298324f9a7feSBorislav Petkov amd64_warn("Hardware rejected DRAM ECC enable," 298424f9a7feSBorislav Petkov "check memory DIMM configuration.\n"); 29852299ef71SBorislav Petkov ret = false; 2986f6d6ae96SBorislav Petkov } else { 298724f9a7feSBorislav Petkov amd64_info("Hardware accepted DRAM ECC Enable\n"); 2988f6d6ae96SBorislav Petkov } 2989d95cf4deSBorislav Petkov } else { 2990ae7bb7c6SBorislav Petkov s->flags.nb_ecc_prev = 1; 2991f6d6ae96SBorislav Petkov } 2992d95cf4deSBorislav Petkov 2993956b9ba1SJoe Perches edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n", 2994a97fa68eSBorislav Petkov nid, value, !!(value & NBCFG_ECC_ENABLE)); 2995f6d6ae96SBorislav Petkov 29962299ef71SBorislav Petkov return ret; 2997f6d6ae96SBorislav Petkov } 2998f6d6ae96SBorislav Petkov 2999c7e5301aSDaniel J Blueman static void restore_ecc_error_reporting(struct ecc_settings *s, u16 nid, 3000360b7f3cSBorislav Petkov struct pci_dev *F3) 3001f6d6ae96SBorislav Petkov { 3002c9f4f26eSBorislav Petkov u32 value, mask = 0x3; /* UECC/CECC enable */ 3003c9f4f26eSBorislav Petkov 3004ae7bb7c6SBorislav Petkov if (!s->nbctl_valid) 3005f6d6ae96SBorislav Petkov return; 3006f6d6ae96SBorislav Petkov 3007c9f4f26eSBorislav Petkov amd64_read_pci_cfg(F3, NBCTL, &value); 3008f6d6ae96SBorislav Petkov value &= ~mask; 3009ae7bb7c6SBorislav Petkov value |= s->old_nbctl; 3010f6d6ae96SBorislav Petkov 3011c9f4f26eSBorislav Petkov amd64_write_pci_cfg(F3, NBCTL, value); 3012f6d6ae96SBorislav Petkov 3013ae7bb7c6SBorislav Petkov /* restore previous BIOS DRAM ECC "off" setting we force-enabled */ 3014ae7bb7c6SBorislav Petkov if (!s->flags.nb_ecc_prev) { 3015a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 3016a97fa68eSBorislav Petkov value &= ~NBCFG_ECC_ENABLE; 3017a97fa68eSBorislav Petkov amd64_write_pci_cfg(F3, NBCFG, value); 3018d95cf4deSBorislav Petkov } 3019d95cf4deSBorislav Petkov 3020d95cf4deSBorislav Petkov /* restore the NB Enable MCGCTL bit */ 30212299ef71SBorislav Petkov if (toggle_ecc_err_reporting(s, nid, OFF)) 302224f9a7feSBorislav Petkov amd64_warn("Error restoring NB MCGCTL settings!\n"); 3023f6d6ae96SBorislav Petkov } 3024f6d6ae96SBorislav Petkov 3025f9431992SDoug Thompson /* 30262299ef71SBorislav Petkov * EDAC requires that the BIOS have ECC enabled before 30272299ef71SBorislav Petkov * taking over the processing of ECC errors. A command line 30282299ef71SBorislav Petkov * option allows to force-enable hardware ECC later in 30292299ef71SBorislav Petkov * enable_ecc_error_reporting(). 3030f9431992SDoug Thompson */ 3031cab4d277SBorislav Petkov static const char *ecc_msg = 3032cab4d277SBorislav Petkov "ECC disabled in the BIOS or no ECC capability, module will not load.\n" 3033cab4d277SBorislav Petkov " Either enable ECC checking or force module loading by setting " 3034cab4d277SBorislav Petkov "'ecc_enable_override'.\n" 3035cab4d277SBorislav Petkov " (Note that use of the override may cause unknown side effects.)\n"; 3036be3468e8SBorislav Petkov 3037c7e5301aSDaniel J Blueman static bool ecc_enabled(struct pci_dev *F3, u16 nid) 3038f9431992SDoug Thompson { 303906724535SBorislav Petkov bool nb_mce_en = false; 3040196b79fcSYazen Ghannam u8 ecc_en = 0, i; 3041196b79fcSYazen Ghannam u32 value; 3042f9431992SDoug Thompson 3043196b79fcSYazen Ghannam if (boot_cpu_data.x86 >= 0x17) { 3044196b79fcSYazen Ghannam u8 umc_en_mask = 0, ecc_en_mask = 0; 3045196b79fcSYazen Ghannam 3046196b79fcSYazen Ghannam for (i = 0; i < NUM_UMCS; i++) { 3047196b79fcSYazen Ghannam u32 base = get_umc_base(i); 3048196b79fcSYazen Ghannam 3049196b79fcSYazen Ghannam /* Only check enabled UMCs. */ 3050196b79fcSYazen Ghannam if (amd_smn_read(nid, base + UMCCH_SDP_CTRL, &value)) 3051196b79fcSYazen Ghannam continue; 3052196b79fcSYazen Ghannam 3053196b79fcSYazen Ghannam if (!(value & UMC_SDP_INIT)) 3054196b79fcSYazen Ghannam continue; 3055196b79fcSYazen Ghannam 3056196b79fcSYazen Ghannam umc_en_mask |= BIT(i); 3057196b79fcSYazen Ghannam 3058196b79fcSYazen Ghannam if (amd_smn_read(nid, base + UMCCH_UMC_CAP_HI, &value)) 3059196b79fcSYazen Ghannam continue; 3060196b79fcSYazen Ghannam 3061196b79fcSYazen Ghannam if (value & UMC_ECC_ENABLED) 3062196b79fcSYazen Ghannam ecc_en_mask |= BIT(i); 3063196b79fcSYazen Ghannam } 3064196b79fcSYazen Ghannam 3065196b79fcSYazen Ghannam /* Check whether at least one UMC is enabled: */ 3066196b79fcSYazen Ghannam if (umc_en_mask) 3067196b79fcSYazen Ghannam ecc_en = umc_en_mask == ecc_en_mask; 3068196b79fcSYazen Ghannam 3069196b79fcSYazen Ghannam /* Assume UMC MCA banks are enabled. */ 3070196b79fcSYazen Ghannam nb_mce_en = true; 3071196b79fcSYazen Ghannam } else { 3072a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 3073f9431992SDoug Thompson 3074a97fa68eSBorislav Petkov ecc_en = !!(value & NBCFG_ECC_ENABLE); 3075be3468e8SBorislav Petkov 3076d1ea71cdSBorislav Petkov nb_mce_en = nb_mce_bank_enabled_on_node(nid); 307706724535SBorislav Petkov if (!nb_mce_en) 3078196b79fcSYazen Ghannam amd64_notice("NB MCE bank disabled, set MSR 0x%08x[4] on node %d to enable.\n", 30792299ef71SBorislav Petkov MSR_IA32_MCG_CTL, nid); 3080196b79fcSYazen Ghannam } 3081196b79fcSYazen Ghannam 3082196b79fcSYazen Ghannam amd64_info("DRAM ECC %s.\n", (ecc_en ? "enabled" : "disabled")); 3083be3468e8SBorislav Petkov 30842299ef71SBorislav Petkov if (!ecc_en || !nb_mce_en) { 308524f9a7feSBorislav Petkov amd64_notice("%s", ecc_msg); 30862299ef71SBorislav Petkov return false; 3087be3468e8SBorislav Petkov } 30882299ef71SBorislav Petkov return true; 3089f9431992SDoug Thompson } 3090f9431992SDoug Thompson 30912d09d8f3SYazen Ghannam static inline void 30922d09d8f3SYazen Ghannam f17h_determine_edac_ctl_cap(struct mem_ctl_info *mci, struct amd64_pvt *pvt) 30932d09d8f3SYazen Ghannam { 30942d09d8f3SYazen Ghannam u8 i, ecc_en = 1, cpk_en = 1; 30952d09d8f3SYazen Ghannam 30962d09d8f3SYazen Ghannam for (i = 0; i < NUM_UMCS; i++) { 30972d09d8f3SYazen Ghannam if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) { 30982d09d8f3SYazen Ghannam ecc_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_ENABLED); 30992d09d8f3SYazen Ghannam cpk_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_CHIPKILL_CAP); 31002d09d8f3SYazen Ghannam } 31012d09d8f3SYazen Ghannam } 31022d09d8f3SYazen Ghannam 31032d09d8f3SYazen Ghannam /* Set chipkill only if ECC is enabled: */ 31042d09d8f3SYazen Ghannam if (ecc_en) { 31052d09d8f3SYazen Ghannam mci->edac_ctl_cap |= EDAC_FLAG_SECDED; 31062d09d8f3SYazen Ghannam 31072d09d8f3SYazen Ghannam if (cpk_en) 31082d09d8f3SYazen Ghannam mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED; 31092d09d8f3SYazen Ghannam } 31102d09d8f3SYazen Ghannam } 31112d09d8f3SYazen Ghannam 3112df71a053SBorislav Petkov static void setup_mci_misc_attrs(struct mem_ctl_info *mci, 3113df71a053SBorislav Petkov struct amd64_family_type *fam) 31147d6034d3SDoug Thompson { 31157d6034d3SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 31167d6034d3SDoug Thompson 31177d6034d3SDoug Thompson mci->mtype_cap = MEM_FLAG_DDR2 | MEM_FLAG_RDDR2; 31187d6034d3SDoug Thompson mci->edac_ctl_cap = EDAC_FLAG_NONE; 31197d6034d3SDoug Thompson 31202d09d8f3SYazen Ghannam if (pvt->umc) { 31212d09d8f3SYazen Ghannam f17h_determine_edac_ctl_cap(mci, pvt); 31222d09d8f3SYazen Ghannam } else { 31235980bb9cSBorislav Petkov if (pvt->nbcap & NBCAP_SECDED) 31247d6034d3SDoug Thompson mci->edac_ctl_cap |= EDAC_FLAG_SECDED; 31257d6034d3SDoug Thompson 31265980bb9cSBorislav Petkov if (pvt->nbcap & NBCAP_CHIPKILL) 31277d6034d3SDoug Thompson mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED; 31282d09d8f3SYazen Ghannam } 31297d6034d3SDoug Thompson 3130d1ea71cdSBorislav Petkov mci->edac_cap = determine_edac_cap(pvt); 31317d6034d3SDoug Thompson mci->mod_name = EDAC_MOD_STR; 31327d6034d3SDoug Thompson mci->mod_ver = EDAC_AMD64_VERSION; 3133df71a053SBorislav Petkov mci->ctl_name = fam->ctl_name; 3134e7934b70SYazen Ghannam mci->dev_name = pci_name(pvt->F3); 31357d6034d3SDoug Thompson mci->ctl_page_to_phys = NULL; 31367d6034d3SDoug Thompson 31377d6034d3SDoug Thompson /* memory scrubber interface */ 3138d1ea71cdSBorislav Petkov mci->set_sdram_scrub_rate = set_scrub_rate; 3139d1ea71cdSBorislav Petkov mci->get_sdram_scrub_rate = get_scrub_rate; 31407d6034d3SDoug Thompson } 31417d6034d3SDoug Thompson 31420092b20dSBorislav Petkov /* 31430092b20dSBorislav Petkov * returns a pointer to the family descriptor on success, NULL otherwise. 31440092b20dSBorislav Petkov */ 3145d1ea71cdSBorislav Petkov static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt) 3146395ae783SBorislav Petkov { 31470092b20dSBorislav Petkov struct amd64_family_type *fam_type = NULL; 31480092b20dSBorislav Petkov 314918b94f66SAravind Gopalakrishnan pvt->ext_model = boot_cpu_data.x86_model >> 4; 3150a4b4bedcSBorislav Petkov pvt->stepping = boot_cpu_data.x86_mask; 315118b94f66SAravind Gopalakrishnan pvt->model = boot_cpu_data.x86_model; 315218b94f66SAravind Gopalakrishnan pvt->fam = boot_cpu_data.x86; 315318b94f66SAravind Gopalakrishnan 315418b94f66SAravind Gopalakrishnan switch (pvt->fam) { 3155395ae783SBorislav Petkov case 0xf: 3156d1ea71cdSBorislav Petkov fam_type = &family_types[K8_CPUS]; 3157d1ea71cdSBorislav Petkov pvt->ops = &family_types[K8_CPUS].ops; 3158395ae783SBorislav Petkov break; 3159df71a053SBorislav Petkov 3160395ae783SBorislav Petkov case 0x10: 3161d1ea71cdSBorislav Petkov fam_type = &family_types[F10_CPUS]; 3162d1ea71cdSBorislav Petkov pvt->ops = &family_types[F10_CPUS].ops; 3163df71a053SBorislav Petkov break; 3164df71a053SBorislav Petkov 3165df71a053SBorislav Petkov case 0x15: 316618b94f66SAravind Gopalakrishnan if (pvt->model == 0x30) { 3167d1ea71cdSBorislav Petkov fam_type = &family_types[F15_M30H_CPUS]; 3168d1ea71cdSBorislav Petkov pvt->ops = &family_types[F15_M30H_CPUS].ops; 316918b94f66SAravind Gopalakrishnan break; 3170a597d2a5SAravind Gopalakrishnan } else if (pvt->model == 0x60) { 3171a597d2a5SAravind Gopalakrishnan fam_type = &family_types[F15_M60H_CPUS]; 3172a597d2a5SAravind Gopalakrishnan pvt->ops = &family_types[F15_M60H_CPUS].ops; 3173a597d2a5SAravind Gopalakrishnan break; 317418b94f66SAravind Gopalakrishnan } 317518b94f66SAravind Gopalakrishnan 3176d1ea71cdSBorislav Petkov fam_type = &family_types[F15_CPUS]; 3177d1ea71cdSBorislav Petkov pvt->ops = &family_types[F15_CPUS].ops; 3178395ae783SBorislav Petkov break; 3179395ae783SBorislav Petkov 318094c1acf2SAravind Gopalakrishnan case 0x16: 318185a8885bSAravind Gopalakrishnan if (pvt->model == 0x30) { 318285a8885bSAravind Gopalakrishnan fam_type = &family_types[F16_M30H_CPUS]; 318385a8885bSAravind Gopalakrishnan pvt->ops = &family_types[F16_M30H_CPUS].ops; 318485a8885bSAravind Gopalakrishnan break; 318585a8885bSAravind Gopalakrishnan } 3186d1ea71cdSBorislav Petkov fam_type = &family_types[F16_CPUS]; 3187d1ea71cdSBorislav Petkov pvt->ops = &family_types[F16_CPUS].ops; 318894c1acf2SAravind Gopalakrishnan break; 318994c1acf2SAravind Gopalakrishnan 3190f1cbbec9SYazen Ghannam case 0x17: 3191f1cbbec9SYazen Ghannam fam_type = &family_types[F17_CPUS]; 3192f1cbbec9SYazen Ghannam pvt->ops = &family_types[F17_CPUS].ops; 3193f1cbbec9SYazen Ghannam break; 3194f1cbbec9SYazen Ghannam 3195395ae783SBorislav Petkov default: 319624f9a7feSBorislav Petkov amd64_err("Unsupported family!\n"); 31970092b20dSBorislav Petkov return NULL; 3198395ae783SBorislav Petkov } 31990092b20dSBorislav Petkov 3200df71a053SBorislav Petkov amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name, 320118b94f66SAravind Gopalakrishnan (pvt->fam == 0xf ? 32020092b20dSBorislav Petkov (pvt->ext_model >= K8_REV_F ? "revF or later " 32030092b20dSBorislav Petkov : "revE or earlier ") 320424f9a7feSBorislav Petkov : ""), pvt->mc_node_id); 32050092b20dSBorislav Petkov return fam_type; 3206395ae783SBorislav Petkov } 3207395ae783SBorislav Petkov 3208e339f1ecSTakashi Iwai static const struct attribute_group *amd64_edac_attr_groups[] = { 3209e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_DEBUG 3210e339f1ecSTakashi Iwai &amd64_edac_dbg_group, 3211e339f1ecSTakashi Iwai #endif 3212e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION 3213e339f1ecSTakashi Iwai &amd64_edac_inj_group, 3214e339f1ecSTakashi Iwai #endif 3215e339f1ecSTakashi Iwai NULL 3216e339f1ecSTakashi Iwai }; 3217e339f1ecSTakashi Iwai 32183f37a36bSBorislav Petkov static int init_one_instance(unsigned int nid) 32197d6034d3SDoug Thompson { 32203f37a36bSBorislav Petkov struct pci_dev *F3 = node_to_amd_nb(nid)->misc; 32210092b20dSBorislav Petkov struct amd64_family_type *fam_type = NULL; 3222360b7f3cSBorislav Petkov struct mem_ctl_info *mci = NULL; 3223ab5a503cSMauro Carvalho Chehab struct edac_mc_layer layers[2]; 32243f37a36bSBorislav Petkov struct amd64_pvt *pvt = NULL; 3225936fc3afSYazen Ghannam u16 pci_id1, pci_id2; 32267d6034d3SDoug Thompson int err = 0, ret; 32277d6034d3SDoug Thompson 32287d6034d3SDoug Thompson ret = -ENOMEM; 32297d6034d3SDoug Thompson pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL); 32307d6034d3SDoug Thompson if (!pvt) 3231360b7f3cSBorislav Petkov goto err_ret; 32327d6034d3SDoug Thompson 3233360b7f3cSBorislav Petkov pvt->mc_node_id = nid; 32343f37a36bSBorislav Petkov pvt->F3 = F3; 32357d6034d3SDoug Thompson 3236395ae783SBorislav Petkov ret = -EINVAL; 3237d1ea71cdSBorislav Petkov fam_type = per_family_init(pvt); 32380092b20dSBorislav Petkov if (!fam_type) 3239395ae783SBorislav Petkov goto err_free; 3240395ae783SBorislav Petkov 3241936fc3afSYazen Ghannam if (pvt->fam >= 0x17) { 3242936fc3afSYazen Ghannam pvt->umc = kcalloc(NUM_UMCS, sizeof(struct amd64_umc), GFP_KERNEL); 3243936fc3afSYazen Ghannam if (!pvt->umc) { 3244936fc3afSYazen Ghannam ret = -ENOMEM; 32457d6034d3SDoug Thompson goto err_free; 3246936fc3afSYazen Ghannam } 3247936fc3afSYazen Ghannam 3248936fc3afSYazen Ghannam pci_id1 = fam_type->f0_id; 3249936fc3afSYazen Ghannam pci_id2 = fam_type->f6_id; 3250936fc3afSYazen Ghannam } else { 3251936fc3afSYazen Ghannam pci_id1 = fam_type->f1_id; 3252936fc3afSYazen Ghannam pci_id2 = fam_type->f2_id; 3253936fc3afSYazen Ghannam } 3254936fc3afSYazen Ghannam 3255936fc3afSYazen Ghannam err = reserve_mc_sibling_devs(pvt, pci_id1, pci_id2); 3256936fc3afSYazen Ghannam if (err) 3257936fc3afSYazen Ghannam goto err_post_init; 32587d6034d3SDoug Thompson 3259360b7f3cSBorislav Petkov read_mc_regs(pvt); 32607d6034d3SDoug Thompson 32617d6034d3SDoug Thompson /* 32627d6034d3SDoug Thompson * We need to determine how many memory channels there are. Then use 32637d6034d3SDoug Thompson * that information for calculating the size of the dynamic instance 3264360b7f3cSBorislav Petkov * tables in the 'mci' structure. 32657d6034d3SDoug Thompson */ 3266360b7f3cSBorislav Petkov ret = -EINVAL; 32677d6034d3SDoug Thompson pvt->channel_count = pvt->ops->early_channel_count(pvt); 32687d6034d3SDoug Thompson if (pvt->channel_count < 0) 3269360b7f3cSBorislav Petkov goto err_siblings; 32707d6034d3SDoug Thompson 32717d6034d3SDoug Thompson ret = -ENOMEM; 3272ab5a503cSMauro Carvalho Chehab layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 3273ab5a503cSMauro Carvalho Chehab layers[0].size = pvt->csels[0].b_cnt; 3274ab5a503cSMauro Carvalho Chehab layers[0].is_virt_csrow = true; 3275ab5a503cSMauro Carvalho Chehab layers[1].type = EDAC_MC_LAYER_CHANNEL; 3276f0a56c48SBorislav Petkov 3277f0a56c48SBorislav Petkov /* 3278f0a56c48SBorislav Petkov * Always allocate two channels since we can have setups with DIMMs on 3279f0a56c48SBorislav Petkov * only one channel. Also, this simplifies handling later for the price 3280f0a56c48SBorislav Petkov * of a couple of KBs tops. 3281f0a56c48SBorislav Petkov */ 3282f0a56c48SBorislav Petkov layers[1].size = 2; 3283ab5a503cSMauro Carvalho Chehab layers[1].is_virt_csrow = false; 3284f0a56c48SBorislav Petkov 3285ca0907b9SMauro Carvalho Chehab mci = edac_mc_alloc(nid, ARRAY_SIZE(layers), layers, 0); 32867d6034d3SDoug Thompson if (!mci) 3287360b7f3cSBorislav Petkov goto err_siblings; 32887d6034d3SDoug Thompson 32897d6034d3SDoug Thompson mci->pvt_info = pvt; 32903f37a36bSBorislav Petkov mci->pdev = &pvt->F3->dev; 32917d6034d3SDoug Thompson 3292df71a053SBorislav Petkov setup_mci_misc_attrs(mci, fam_type); 3293360b7f3cSBorislav Petkov 3294360b7f3cSBorislav Petkov if (init_csrows(mci)) 32957d6034d3SDoug Thompson mci->edac_cap = EDAC_FLAG_NONE; 32967d6034d3SDoug Thompson 32977d6034d3SDoug Thompson ret = -ENODEV; 3298e339f1ecSTakashi Iwai if (edac_mc_add_mc_with_groups(mci, amd64_edac_attr_groups)) { 3299956b9ba1SJoe Perches edac_dbg(1, "failed edac_mc_add_mc()\n"); 33007d6034d3SDoug Thompson goto err_add_mc; 33017d6034d3SDoug Thompson } 33027d6034d3SDoug Thompson 33037d6034d3SDoug Thompson return 0; 33047d6034d3SDoug Thompson 33057d6034d3SDoug Thompson err_add_mc: 33067d6034d3SDoug Thompson edac_mc_free(mci); 33077d6034d3SDoug Thompson 3308360b7f3cSBorislav Petkov err_siblings: 3309360b7f3cSBorislav Petkov free_mc_sibling_devs(pvt); 33107d6034d3SDoug Thompson 3311936fc3afSYazen Ghannam err_post_init: 3312936fc3afSYazen Ghannam if (pvt->fam >= 0x17) 3313936fc3afSYazen Ghannam kfree(pvt->umc); 3314936fc3afSYazen Ghannam 3315360b7f3cSBorislav Petkov err_free: 3316360b7f3cSBorislav Petkov kfree(pvt); 33177d6034d3SDoug Thompson 3318360b7f3cSBorislav Petkov err_ret: 33197d6034d3SDoug Thompson return ret; 33207d6034d3SDoug Thompson } 33217d6034d3SDoug Thompson 33223f37a36bSBorislav Petkov static int probe_one_instance(unsigned int nid) 33237d6034d3SDoug Thompson { 33242299ef71SBorislav Petkov struct pci_dev *F3 = node_to_amd_nb(nid)->misc; 3325ae7bb7c6SBorislav Petkov struct ecc_settings *s; 33263f37a36bSBorislav Petkov int ret; 3327b8cfa02fSBorislav Petkov 3328ae7bb7c6SBorislav Petkov ret = -ENOMEM; 3329ae7bb7c6SBorislav Petkov s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL); 3330ae7bb7c6SBorislav Petkov if (!s) 33312299ef71SBorislav Petkov goto err_out; 3332ae7bb7c6SBorislav Petkov 3333ae7bb7c6SBorislav Petkov ecc_stngs[nid] = s; 3334ae7bb7c6SBorislav Petkov 33352299ef71SBorislav Petkov if (!ecc_enabled(F3, nid)) { 33362299ef71SBorislav Petkov ret = -ENODEV; 33372299ef71SBorislav Petkov 33382299ef71SBorislav Petkov if (!ecc_enable_override) 33392299ef71SBorislav Petkov goto err_enable; 33402299ef71SBorislav Petkov 3341044e7a41SYazen Ghannam if (boot_cpu_data.x86 >= 0x17) { 3342044e7a41SYazen Ghannam amd64_warn("Forcing ECC on is not recommended on newer systems. Please enable ECC in BIOS."); 3343044e7a41SYazen Ghannam goto err_enable; 3344044e7a41SYazen Ghannam } else 33452299ef71SBorislav Petkov amd64_warn("Forcing ECC on!\n"); 33462299ef71SBorislav Petkov 33472299ef71SBorislav Petkov if (!enable_ecc_error_reporting(s, nid, F3)) 33482299ef71SBorislav Petkov goto err_enable; 33492299ef71SBorislav Petkov } 33502299ef71SBorislav Petkov 33513f37a36bSBorislav Petkov ret = init_one_instance(nid); 3352360b7f3cSBorislav Petkov if (ret < 0) { 3353ae7bb7c6SBorislav Petkov amd64_err("Error probing instance: %d\n", nid); 3354044e7a41SYazen Ghannam 3355044e7a41SYazen Ghannam if (boot_cpu_data.x86 < 0x17) 3356360b7f3cSBorislav Petkov restore_ecc_error_reporting(s, nid, F3); 33572b9b2c46SYazen Ghannam 33582b9b2c46SYazen Ghannam goto err_enable; 3359360b7f3cSBorislav Petkov } 33607d6034d3SDoug Thompson 33617d6034d3SDoug Thompson return ret; 33622299ef71SBorislav Petkov 33632299ef71SBorislav Petkov err_enable: 33642299ef71SBorislav Petkov kfree(s); 33652299ef71SBorislav Petkov ecc_stngs[nid] = NULL; 33662299ef71SBorislav Petkov 33672299ef71SBorislav Petkov err_out: 33682299ef71SBorislav Petkov return ret; 33697d6034d3SDoug Thompson } 33707d6034d3SDoug Thompson 33713f37a36bSBorislav Petkov static void remove_one_instance(unsigned int nid) 33727d6034d3SDoug Thompson { 3373360b7f3cSBorislav Petkov struct pci_dev *F3 = node_to_amd_nb(nid)->misc; 3374360b7f3cSBorislav Petkov struct ecc_settings *s = ecc_stngs[nid]; 33753f37a36bSBorislav Petkov struct mem_ctl_info *mci; 33763f37a36bSBorislav Petkov struct amd64_pvt *pvt; 33777d6034d3SDoug Thompson 33783f37a36bSBorislav Petkov mci = find_mci_by_dev(&F3->dev); 3379a4b4bedcSBorislav Petkov WARN_ON(!mci); 3380a4b4bedcSBorislav Petkov 33817d6034d3SDoug Thompson /* Remove from EDAC CORE tracking list */ 33823f37a36bSBorislav Petkov mci = edac_mc_del_mc(&F3->dev); 33837d6034d3SDoug Thompson if (!mci) 33847d6034d3SDoug Thompson return; 33857d6034d3SDoug Thompson 33867d6034d3SDoug Thompson pvt = mci->pvt_info; 33877d6034d3SDoug Thompson 3388360b7f3cSBorislav Petkov restore_ecc_error_reporting(s, nid, F3); 33897d6034d3SDoug Thompson 3390360b7f3cSBorislav Petkov free_mc_sibling_devs(pvt); 33917d6034d3SDoug Thompson 3392360b7f3cSBorislav Petkov kfree(ecc_stngs[nid]); 3393360b7f3cSBorislav Petkov ecc_stngs[nid] = NULL; 3394ae7bb7c6SBorislav Petkov 33957d6034d3SDoug Thompson /* Free the EDAC CORE resources */ 33968f68ed97SBorislav Petkov mci->pvt_info = NULL; 33978f68ed97SBorislav Petkov 33988f68ed97SBorislav Petkov kfree(pvt); 33997d6034d3SDoug Thompson edac_mc_free(mci); 34007d6034d3SDoug Thompson } 34017d6034d3SDoug Thompson 3402360b7f3cSBorislav Petkov static void setup_pci_device(void) 34037d6034d3SDoug Thompson { 34047d6034d3SDoug Thompson struct mem_ctl_info *mci; 34057d6034d3SDoug Thompson struct amd64_pvt *pvt; 34067d6034d3SDoug Thompson 3407d1ea71cdSBorislav Petkov if (pci_ctl) 34087d6034d3SDoug Thompson return; 34097d6034d3SDoug Thompson 34102ec591acSBorislav Petkov mci = edac_mc_find(0); 3411d1ea71cdSBorislav Petkov if (!mci) 3412d1ea71cdSBorislav Petkov return; 34137d6034d3SDoug Thompson 34147d6034d3SDoug Thompson pvt = mci->pvt_info; 3415936fc3afSYazen Ghannam if (pvt->umc) 3416936fc3afSYazen Ghannam pci_ctl = edac_pci_create_generic_ctl(&pvt->F0->dev, EDAC_MOD_STR); 3417936fc3afSYazen Ghannam else 3418d1ea71cdSBorislav Petkov pci_ctl = edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR); 3419d1ea71cdSBorislav Petkov if (!pci_ctl) { 3420d1ea71cdSBorislav Petkov pr_warn("%s(): Unable to create PCI control\n", __func__); 3421d1ea71cdSBorislav Petkov pr_warn("%s(): PCI error report via EDAC not set\n", __func__); 34227d6034d3SDoug Thompson } 34237d6034d3SDoug Thompson } 34247d6034d3SDoug Thompson 3425d6efab74SYazen Ghannam static const struct x86_cpu_id amd64_cpuids[] = { 3426d6efab74SYazen Ghannam { X86_VENDOR_AMD, 0xF, X86_MODEL_ANY, X86_FEATURE_ANY, 0 }, 3427d6efab74SYazen Ghannam { X86_VENDOR_AMD, 0x10, X86_MODEL_ANY, X86_FEATURE_ANY, 0 }, 3428d6efab74SYazen Ghannam { X86_VENDOR_AMD, 0x15, X86_MODEL_ANY, X86_FEATURE_ANY, 0 }, 3429d6efab74SYazen Ghannam { X86_VENDOR_AMD, 0x16, X86_MODEL_ANY, X86_FEATURE_ANY, 0 }, 343095d3af6bSYazen Ghannam { X86_VENDOR_AMD, 0x17, X86_MODEL_ANY, X86_FEATURE_ANY, 0 }, 3431d6efab74SYazen Ghannam { } 3432d6efab74SYazen Ghannam }; 3433d6efab74SYazen Ghannam MODULE_DEVICE_TABLE(x86cpu, amd64_cpuids); 3434d6efab74SYazen Ghannam 34357d6034d3SDoug Thompson static int __init amd64_edac_init(void) 34367d6034d3SDoug Thompson { 3437360b7f3cSBorislav Petkov int err = -ENODEV; 34383f37a36bSBorislav Petkov int i; 34397d6034d3SDoug Thompson 34409653a5c7SHans Rosenfeld if (amd_cache_northbridges() < 0) 344156b34b91SBorislav Petkov goto err_ret; 34427d6034d3SDoug Thompson 34436ba92feaSBorislav Petkov opstate_init(); 34446ba92feaSBorislav Petkov 3445cc4d8860SBorislav Petkov err = -ENOMEM; 3446ae7bb7c6SBorislav Petkov ecc_stngs = kzalloc(amd_nb_num() * sizeof(ecc_stngs[0]), GFP_KERNEL); 34472ec591acSBorislav Petkov if (!ecc_stngs) 3448a9f0fbe2SBorislav Petkov goto err_free; 3449cc4d8860SBorislav Petkov 345050542251SBorislav Petkov msrs = msrs_alloc(); 345156b34b91SBorislav Petkov if (!msrs) 3452360b7f3cSBorislav Petkov goto err_free; 345350542251SBorislav Petkov 34542287c636SYazen Ghannam for (i = 0; i < amd_nb_num(); i++) { 34552287c636SYazen Ghannam err = probe_one_instance(i); 34562287c636SYazen Ghannam if (err) { 34573f37a36bSBorislav Petkov /* unwind properly */ 34583f37a36bSBorislav Petkov while (--i >= 0) 34593f37a36bSBorislav Petkov remove_one_instance(i); 34607d6034d3SDoug Thompson 34613f37a36bSBorislav Petkov goto err_pci; 34623f37a36bSBorislav Petkov } 34632287c636SYazen Ghannam } 34647d6034d3SDoug Thompson 3465234365f5SYazen Ghannam /* register stuff with EDAC MCE */ 3466234365f5SYazen Ghannam if (report_gart_errors) 3467234365f5SYazen Ghannam amd_report_gart_errors(true); 3468234365f5SYazen Ghannam 3469234365f5SYazen Ghannam if (boot_cpu_data.x86 >= 0x17) 3470234365f5SYazen Ghannam amd_register_ecc_decoder(decode_umc_error); 3471234365f5SYazen Ghannam else 3472234365f5SYazen Ghannam amd_register_ecc_decoder(decode_bus_error); 3473234365f5SYazen Ghannam 3474360b7f3cSBorislav Petkov setup_pci_device(); 3475f5b10c45STomasz Pala 3476f5b10c45STomasz Pala #ifdef CONFIG_X86_32 3477f5b10c45STomasz Pala amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR); 3478f5b10c45STomasz Pala #endif 3479f5b10c45STomasz Pala 3480de0336b3SBorislav Petkov printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION); 3481de0336b3SBorislav Petkov 34827d6034d3SDoug Thompson return 0; 34837d6034d3SDoug Thompson 348456b34b91SBorislav Petkov err_pci: 348556b34b91SBorislav Petkov msrs_free(msrs); 348656b34b91SBorislav Petkov msrs = NULL; 3487cc4d8860SBorislav Petkov 3488360b7f3cSBorislav Petkov err_free: 3489360b7f3cSBorislav Petkov kfree(ecc_stngs); 3490360b7f3cSBorislav Petkov ecc_stngs = NULL; 3491360b7f3cSBorislav Petkov 349256b34b91SBorislav Petkov err_ret: 34937d6034d3SDoug Thompson return err; 34947d6034d3SDoug Thompson } 34957d6034d3SDoug Thompson 34967d6034d3SDoug Thompson static void __exit amd64_edac_exit(void) 34977d6034d3SDoug Thompson { 34983f37a36bSBorislav Petkov int i; 34993f37a36bSBorislav Petkov 3500d1ea71cdSBorislav Petkov if (pci_ctl) 3501d1ea71cdSBorislav Petkov edac_pci_release_generic_ctl(pci_ctl); 35027d6034d3SDoug Thompson 3503234365f5SYazen Ghannam /* unregister from EDAC MCE */ 3504234365f5SYazen Ghannam amd_report_gart_errors(false); 3505234365f5SYazen Ghannam 3506234365f5SYazen Ghannam if (boot_cpu_data.x86 >= 0x17) 3507234365f5SYazen Ghannam amd_unregister_ecc_decoder(decode_umc_error); 3508234365f5SYazen Ghannam else 3509234365f5SYazen Ghannam amd_unregister_ecc_decoder(decode_bus_error); 3510234365f5SYazen Ghannam 35113f37a36bSBorislav Petkov for (i = 0; i < amd_nb_num(); i++) 35123f37a36bSBorislav Petkov remove_one_instance(i); 351350542251SBorislav Petkov 3514ae7bb7c6SBorislav Petkov kfree(ecc_stngs); 3515ae7bb7c6SBorislav Petkov ecc_stngs = NULL; 3516ae7bb7c6SBorislav Petkov 351750542251SBorislav Petkov msrs_free(msrs); 351850542251SBorislav Petkov msrs = NULL; 35197d6034d3SDoug Thompson } 35207d6034d3SDoug Thompson 35217d6034d3SDoug Thompson module_init(amd64_edac_init); 35227d6034d3SDoug Thompson module_exit(amd64_edac_exit); 35237d6034d3SDoug Thompson 35247d6034d3SDoug Thompson MODULE_LICENSE("GPL"); 35257d6034d3SDoug Thompson MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, " 35267d6034d3SDoug Thompson "Dave Peterson, Thayne Harbaugh"); 35277d6034d3SDoug Thompson MODULE_DESCRIPTION("MC support for AMD64 memory controllers - " 35287d6034d3SDoug Thompson EDAC_AMD64_VERSION); 35297d6034d3SDoug Thompson 35307d6034d3SDoug Thompson module_param(edac_op_state, int, 0444); 35317d6034d3SDoug Thompson MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); 3532