109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 22bc65418SDoug Thompson #include "amd64_edac.h" 323ac4ae8SAndreas Herrmann #include <asm/amd_nb.h> 42bc65418SDoug Thompson 5d1ea71cdSBorislav Petkov static struct edac_pci_ctl_info *pci_ctl; 62bc65418SDoug Thompson 72bc65418SDoug Thompson /* 82bc65418SDoug Thompson * Set by command line parameter. If BIOS has enabled the ECC, this override is 92bc65418SDoug Thompson * cleared to prevent re-enabling the hardware by this driver. 102bc65418SDoug Thompson */ 112bc65418SDoug Thompson static int ecc_enable_override; 122bc65418SDoug Thompson module_param(ecc_enable_override, int, 0644); 132bc65418SDoug Thompson 14a29d8b8eSTejun Heo static struct msr __percpu *msrs; 1550542251SBorislav Petkov 1638ddd4d1SYazen Ghannam static struct amd64_family_type *fam_type; 1738ddd4d1SYazen Ghannam 182ec591acSBorislav Petkov /* Per-node stuff */ 19ae7bb7c6SBorislav Petkov static struct ecc_settings **ecc_stngs; 202bc65418SDoug Thompson 21706657b1SBorislav Petkov /* Device for the PCI component */ 22706657b1SBorislav Petkov static struct device *pci_ctl_dev; 23706657b1SBorislav Petkov 242bc65418SDoug Thompson /* 25b70ef010SBorislav Petkov * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing 26b70ef010SBorislav Petkov * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching- 27b70ef010SBorislav Petkov * or higher value'. 28b70ef010SBorislav Petkov * 29b70ef010SBorislav Petkov *FIXME: Produce a better mapping/linearisation. 30b70ef010SBorislav Petkov */ 31c7e5301aSDaniel J Blueman static const struct scrubrate { 3239094443SBorislav Petkov u32 scrubval; /* bit pattern for scrub rate */ 3339094443SBorislav Petkov u32 bandwidth; /* bandwidth consumed (bytes/sec) */ 3439094443SBorislav Petkov } scrubrates[] = { 35b70ef010SBorislav Petkov { 0x01, 1600000000UL}, 36b70ef010SBorislav Petkov { 0x02, 800000000UL}, 37b70ef010SBorislav Petkov { 0x03, 400000000UL}, 38b70ef010SBorislav Petkov { 0x04, 200000000UL}, 39b70ef010SBorislav Petkov { 0x05, 100000000UL}, 40b70ef010SBorislav Petkov { 0x06, 50000000UL}, 41b70ef010SBorislav Petkov { 0x07, 25000000UL}, 42b70ef010SBorislav Petkov { 0x08, 12284069UL}, 43b70ef010SBorislav Petkov { 0x09, 6274509UL}, 44b70ef010SBorislav Petkov { 0x0A, 3121951UL}, 45b70ef010SBorislav Petkov { 0x0B, 1560975UL}, 46b70ef010SBorislav Petkov { 0x0C, 781440UL}, 47b70ef010SBorislav Petkov { 0x0D, 390720UL}, 48b70ef010SBorislav Petkov { 0x0E, 195300UL}, 49b70ef010SBorislav Petkov { 0x0F, 97650UL}, 50b70ef010SBorislav Petkov { 0x10, 48854UL}, 51b70ef010SBorislav Petkov { 0x11, 24427UL}, 52b70ef010SBorislav Petkov { 0x12, 12213UL}, 53b70ef010SBorislav Petkov { 0x13, 6101UL}, 54b70ef010SBorislav Petkov { 0x14, 3051UL}, 55b70ef010SBorislav Petkov { 0x15, 1523UL}, 56b70ef010SBorislav Petkov { 0x16, 761UL}, 57b70ef010SBorislav Petkov { 0x00, 0UL}, /* scrubbing off */ 58b70ef010SBorislav Petkov }; 59b70ef010SBorislav Petkov 6066fed2d4SBorislav Petkov int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset, 61b2b0c605SBorislav Petkov u32 *val, const char *func) 62b2b0c605SBorislav Petkov { 63b2b0c605SBorislav Petkov int err = 0; 64b2b0c605SBorislav Petkov 65b2b0c605SBorislav Petkov err = pci_read_config_dword(pdev, offset, val); 66b2b0c605SBorislav Petkov if (err) 67b2b0c605SBorislav Petkov amd64_warn("%s: error reading F%dx%03x.\n", 68b2b0c605SBorislav Petkov func, PCI_FUNC(pdev->devfn), offset); 69b2b0c605SBorislav Petkov 70b2b0c605SBorislav Petkov return err; 71b2b0c605SBorislav Petkov } 72b2b0c605SBorislav Petkov 73b2b0c605SBorislav Petkov int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset, 74b2b0c605SBorislav Petkov u32 val, const char *func) 75b2b0c605SBorislav Petkov { 76b2b0c605SBorislav Petkov int err = 0; 77b2b0c605SBorislav Petkov 78b2b0c605SBorislav Petkov err = pci_write_config_dword(pdev, offset, val); 79b2b0c605SBorislav Petkov if (err) 80b2b0c605SBorislav Petkov amd64_warn("%s: error writing to F%dx%03x.\n", 81b2b0c605SBorislav Petkov func, PCI_FUNC(pdev->devfn), offset); 82b2b0c605SBorislav Petkov 83b2b0c605SBorislav Petkov return err; 84b2b0c605SBorislav Petkov } 85b2b0c605SBorislav Petkov 86b2b0c605SBorislav Petkov /* 8773ba8593SBorislav Petkov * Select DCT to which PCI cfg accesses are routed 8873ba8593SBorislav Petkov */ 8973ba8593SBorislav Petkov static void f15h_select_dct(struct amd64_pvt *pvt, u8 dct) 9073ba8593SBorislav Petkov { 9173ba8593SBorislav Petkov u32 reg = 0; 9273ba8593SBorislav Petkov 9373ba8593SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, ®); 947981a28fSAravind Gopalakrishnan reg &= (pvt->model == 0x30) ? ~3 : ~1; 9573ba8593SBorislav Petkov reg |= dct; 9673ba8593SBorislav Petkov amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg); 9773ba8593SBorislav Petkov } 9873ba8593SBorislav Petkov 997981a28fSAravind Gopalakrishnan /* 1007981a28fSAravind Gopalakrishnan * 1017981a28fSAravind Gopalakrishnan * Depending on the family, F2 DCT reads need special handling: 1027981a28fSAravind Gopalakrishnan * 1037981a28fSAravind Gopalakrishnan * K8: has a single DCT only and no address offsets >= 0x100 1047981a28fSAravind Gopalakrishnan * 1057981a28fSAravind Gopalakrishnan * F10h: each DCT has its own set of regs 1067981a28fSAravind Gopalakrishnan * DCT0 -> F2x040.. 1077981a28fSAravind Gopalakrishnan * DCT1 -> F2x140.. 1087981a28fSAravind Gopalakrishnan * 1097981a28fSAravind Gopalakrishnan * F16h: has only 1 DCT 1107981a28fSAravind Gopalakrishnan * 1117981a28fSAravind Gopalakrishnan * F15h: we select which DCT we access using F1x10C[DctCfgSel] 1127981a28fSAravind Gopalakrishnan */ 1137981a28fSAravind Gopalakrishnan static inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct, 1147981a28fSAravind Gopalakrishnan int offset, u32 *val) 115b2b0c605SBorislav Petkov { 1167981a28fSAravind Gopalakrishnan switch (pvt->fam) { 1177981a28fSAravind Gopalakrishnan case 0xf: 1187981a28fSAravind Gopalakrishnan if (dct || offset >= 0x100) 1197981a28fSAravind Gopalakrishnan return -EINVAL; 1207981a28fSAravind Gopalakrishnan break; 121b2b0c605SBorislav Petkov 1227981a28fSAravind Gopalakrishnan case 0x10: 1237981a28fSAravind Gopalakrishnan if (dct) { 1247981a28fSAravind Gopalakrishnan /* 1257981a28fSAravind Gopalakrishnan * Note: If ganging is enabled, barring the regs 1267981a28fSAravind Gopalakrishnan * F2x[1,0]98 and F2x[1,0]9C; reads reads to F2x1xx 1277981a28fSAravind Gopalakrishnan * return 0. (cf. Section 2.8.1 F10h BKDG) 1287981a28fSAravind Gopalakrishnan */ 1297981a28fSAravind Gopalakrishnan if (dct_ganging_enabled(pvt)) 1307981a28fSAravind Gopalakrishnan return 0; 1317981a28fSAravind Gopalakrishnan 1327981a28fSAravind Gopalakrishnan offset += 0x100; 133b2b0c605SBorislav Petkov } 1347981a28fSAravind Gopalakrishnan break; 135b2b0c605SBorislav Petkov 1367981a28fSAravind Gopalakrishnan case 0x15: 1377981a28fSAravind Gopalakrishnan /* 1387981a28fSAravind Gopalakrishnan * F15h: F2x1xx addresses do not map explicitly to DCT1. 1397981a28fSAravind Gopalakrishnan * We should select which DCT we access using F1x10C[DctCfgSel] 1407981a28fSAravind Gopalakrishnan */ 1417981a28fSAravind Gopalakrishnan dct = (dct && pvt->model == 0x30) ? 3 : dct; 14273ba8593SBorislav Petkov f15h_select_dct(pvt, dct); 1437981a28fSAravind Gopalakrishnan break; 144b2b0c605SBorislav Petkov 1457981a28fSAravind Gopalakrishnan case 0x16: 1467981a28fSAravind Gopalakrishnan if (dct) 1477981a28fSAravind Gopalakrishnan return -EINVAL; 1487981a28fSAravind Gopalakrishnan break; 1497981a28fSAravind Gopalakrishnan 1507981a28fSAravind Gopalakrishnan default: 1517981a28fSAravind Gopalakrishnan break; 1527981a28fSAravind Gopalakrishnan } 1537981a28fSAravind Gopalakrishnan return amd64_read_pci_cfg(pvt->F2, offset, val); 154b2b0c605SBorislav Petkov } 155b2b0c605SBorislav Petkov 156b70ef010SBorislav Petkov /* 1572bc65418SDoug Thompson * Memory scrubber control interface. For K8, memory scrubbing is handled by 1582bc65418SDoug Thompson * hardware and can involve L2 cache, dcache as well as the main memory. With 1592bc65418SDoug Thompson * F10, this is extended to L3 cache scrubbing on CPU models sporting that 1602bc65418SDoug Thompson * functionality. 1612bc65418SDoug Thompson * 1622bc65418SDoug Thompson * This causes the "units" for the scrubbing speed to vary from 64 byte blocks 1632bc65418SDoug Thompson * (dram) over to cache lines. This is nasty, so we will use bandwidth in 1642bc65418SDoug Thompson * bytes/sec for the setting. 1652bc65418SDoug Thompson * 1662bc65418SDoug Thompson * Currently, we only do dram scrubbing. If the scrubbing is done in software on 1672bc65418SDoug Thompson * other archs, we might not have access to the caches directly. 1682bc65418SDoug Thompson */ 1692bc65418SDoug Thompson 1708051c0afSYazen Ghannam static inline void __f17h_set_scrubval(struct amd64_pvt *pvt, u32 scrubval) 1718051c0afSYazen Ghannam { 1722bc65418SDoug Thompson /* 1738051c0afSYazen Ghannam * Fam17h supports scrub values between 0x5 and 0x14. Also, the values 1748051c0afSYazen Ghannam * are shifted down by 0x5, so scrubval 0x5 is written to the register 1758051c0afSYazen Ghannam * as 0x0, scrubval 0x6 as 0x1, etc. 1768051c0afSYazen Ghannam */ 1778051c0afSYazen Ghannam if (scrubval >= 0x5 && scrubval <= 0x14) { 1788051c0afSYazen Ghannam scrubval -= 0x5; 1798051c0afSYazen Ghannam pci_write_bits32(pvt->F6, F17H_SCR_LIMIT_ADDR, scrubval, 0xF); 1808051c0afSYazen Ghannam pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 1, 0x1); 1818051c0afSYazen Ghannam } else { 1828051c0afSYazen Ghannam pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 0, 0x1); 1838051c0afSYazen Ghannam } 1848051c0afSYazen Ghannam } 1858051c0afSYazen Ghannam /* 1868051c0afSYazen Ghannam * Scan the scrub rate mapping table for a close or matching bandwidth value to 1872bc65418SDoug Thompson * issue. If requested is too big, then use last maximum value found. 1882bc65418SDoug Thompson */ 189da92110dSAravind Gopalakrishnan static int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate) 1902bc65418SDoug Thompson { 1912bc65418SDoug Thompson u32 scrubval; 1922bc65418SDoug Thompson int i; 1932bc65418SDoug Thompson 1942bc65418SDoug Thompson /* 1952bc65418SDoug Thompson * map the configured rate (new_bw) to a value specific to the AMD64 1962bc65418SDoug Thompson * memory controller and apply to register. Search for the first 1972bc65418SDoug Thompson * bandwidth entry that is greater or equal than the setting requested 1982bc65418SDoug Thompson * and program that. If at last entry, turn off DRAM scrubbing. 199168bfeefSAndrew Morton * 200168bfeefSAndrew Morton * If no suitable bandwidth is found, turn off DRAM scrubbing entirely 201168bfeefSAndrew Morton * by falling back to the last element in scrubrates[]. 2022bc65418SDoug Thompson */ 203168bfeefSAndrew Morton for (i = 0; i < ARRAY_SIZE(scrubrates) - 1; i++) { 2042bc65418SDoug Thompson /* 2052bc65418SDoug Thompson * skip scrub rates which aren't recommended 2062bc65418SDoug Thompson * (see F10 BKDG, F3x58) 2072bc65418SDoug Thompson */ 208395ae783SBorislav Petkov if (scrubrates[i].scrubval < min_rate) 2092bc65418SDoug Thompson continue; 2102bc65418SDoug Thompson 2112bc65418SDoug Thompson if (scrubrates[i].bandwidth <= new_bw) 2122bc65418SDoug Thompson break; 2132bc65418SDoug Thompson } 2142bc65418SDoug Thompson 2152bc65418SDoug Thompson scrubval = scrubrates[i].scrubval; 2162bc65418SDoug Thompson 217dcd01394SYazen Ghannam if (pvt->umc) { 2188051c0afSYazen Ghannam __f17h_set_scrubval(pvt, scrubval); 2198051c0afSYazen Ghannam } else if (pvt->fam == 0x15 && pvt->model == 0x60) { 220da92110dSAravind Gopalakrishnan f15h_select_dct(pvt, 0); 221da92110dSAravind Gopalakrishnan pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F); 222da92110dSAravind Gopalakrishnan f15h_select_dct(pvt, 1); 223da92110dSAravind Gopalakrishnan pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F); 224da92110dSAravind Gopalakrishnan } else { 225da92110dSAravind Gopalakrishnan pci_write_bits32(pvt->F3, SCRCTRL, scrubval, 0x001F); 226da92110dSAravind Gopalakrishnan } 2272bc65418SDoug Thompson 22839094443SBorislav Petkov if (scrubval) 22939094443SBorislav Petkov return scrubrates[i].bandwidth; 23039094443SBorislav Petkov 2312bc65418SDoug Thompson return 0; 2322bc65418SDoug Thompson } 2332bc65418SDoug Thompson 234d1ea71cdSBorislav Petkov static int set_scrub_rate(struct mem_ctl_info *mci, u32 bw) 2352bc65418SDoug Thompson { 2362bc65418SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 23787b3e0e6SBorislav Petkov u32 min_scrubrate = 0x5; 2382bc65418SDoug Thompson 239a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) 24087b3e0e6SBorislav Petkov min_scrubrate = 0x0; 24187b3e0e6SBorislav Petkov 242da92110dSAravind Gopalakrishnan if (pvt->fam == 0x15) { 2433f0aba4fSBorislav Petkov /* Erratum #505 */ 244da92110dSAravind Gopalakrishnan if (pvt->model < 0x10) 24573ba8593SBorislav Petkov f15h_select_dct(pvt, 0); 24673ba8593SBorislav Petkov 247da92110dSAravind Gopalakrishnan if (pvt->model == 0x60) 248da92110dSAravind Gopalakrishnan min_scrubrate = 0x6; 249da92110dSAravind Gopalakrishnan } 250da92110dSAravind Gopalakrishnan return __set_scrub_rate(pvt, bw, min_scrubrate); 2512bc65418SDoug Thompson } 2522bc65418SDoug Thompson 253d1ea71cdSBorislav Petkov static int get_scrub_rate(struct mem_ctl_info *mci) 2542bc65418SDoug Thompson { 2552bc65418SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 25639094443SBorislav Petkov int i, retval = -EINVAL; 2578051c0afSYazen Ghannam u32 scrubval = 0; 2582bc65418SDoug Thompson 259dcd01394SYazen Ghannam if (pvt->umc) { 2608051c0afSYazen Ghannam amd64_read_pci_cfg(pvt->F6, F17H_SCR_BASE_ADDR, &scrubval); 2618051c0afSYazen Ghannam if (scrubval & BIT(0)) { 2628051c0afSYazen Ghannam amd64_read_pci_cfg(pvt->F6, F17H_SCR_LIMIT_ADDR, &scrubval); 2638051c0afSYazen Ghannam scrubval &= 0xF; 2648051c0afSYazen Ghannam scrubval += 0x5; 2658051c0afSYazen Ghannam } else { 2668051c0afSYazen Ghannam scrubval = 0; 2678051c0afSYazen Ghannam } 268dcd01394SYazen Ghannam } else if (pvt->fam == 0x15) { 269dcd01394SYazen Ghannam /* Erratum #505 */ 270dcd01394SYazen Ghannam if (pvt->model < 0x10) 271dcd01394SYazen Ghannam f15h_select_dct(pvt, 0); 2728051c0afSYazen Ghannam 273dcd01394SYazen Ghannam if (pvt->model == 0x60) 274dcd01394SYazen Ghannam amd64_read_pci_cfg(pvt->F2, F15H_M60H_SCRCTRL, &scrubval); 275ee470bb2SBorislav Petkov else 276ee470bb2SBorislav Petkov amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval); 277dcd01394SYazen Ghannam } else { 2785980bb9cSBorislav Petkov amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval); 2798051c0afSYazen Ghannam } 2802bc65418SDoug Thompson 2812bc65418SDoug Thompson scrubval = scrubval & 0x001F; 2822bc65418SDoug Thompson 283926311fdSRoel Kluin for (i = 0; i < ARRAY_SIZE(scrubrates); i++) { 2842bc65418SDoug Thompson if (scrubrates[i].scrubval == scrubval) { 28539094443SBorislav Petkov retval = scrubrates[i].bandwidth; 2862bc65418SDoug Thompson break; 2872bc65418SDoug Thompson } 2882bc65418SDoug Thompson } 28939094443SBorislav Petkov return retval; 2902bc65418SDoug Thompson } 2912bc65418SDoug Thompson 2926775763aSDoug Thompson /* 2937f19bf75SBorislav Petkov * returns true if the SysAddr given by sys_addr matches the 2947f19bf75SBorislav Petkov * DRAM base/limit associated with node_id 2956775763aSDoug Thompson */ 296d1ea71cdSBorislav Petkov static bool base_limit_match(struct amd64_pvt *pvt, u64 sys_addr, u8 nid) 2976775763aSDoug Thompson { 2987f19bf75SBorislav Petkov u64 addr; 2996775763aSDoug Thompson 3006775763aSDoug Thompson /* The K8 treats this as a 40-bit value. However, bits 63-40 will be 3016775763aSDoug Thompson * all ones if the most significant implemented address bit is 1. 3026775763aSDoug Thompson * Here we discard bits 63-40. See section 3.4.2 of AMD publication 3036775763aSDoug Thompson * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1 3046775763aSDoug Thompson * Application Programming. 3056775763aSDoug Thompson */ 3066775763aSDoug Thompson addr = sys_addr & 0x000000ffffffffffull; 3076775763aSDoug Thompson 3087f19bf75SBorislav Petkov return ((addr >= get_dram_base(pvt, nid)) && 3097f19bf75SBorislav Petkov (addr <= get_dram_limit(pvt, nid))); 3106775763aSDoug Thompson } 3116775763aSDoug Thompson 3126775763aSDoug Thompson /* 3136775763aSDoug Thompson * Attempt to map a SysAddr to a node. On success, return a pointer to the 3146775763aSDoug Thompson * mem_ctl_info structure for the node that the SysAddr maps to. 3156775763aSDoug Thompson * 3166775763aSDoug Thompson * On failure, return NULL. 3176775763aSDoug Thompson */ 3186775763aSDoug Thompson static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci, 3196775763aSDoug Thompson u64 sys_addr) 3206775763aSDoug Thompson { 3216775763aSDoug Thompson struct amd64_pvt *pvt; 322c7e5301aSDaniel J Blueman u8 node_id; 3236775763aSDoug Thompson u32 intlv_en, bits; 3246775763aSDoug Thompson 3256775763aSDoug Thompson /* 3266775763aSDoug Thompson * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section 3276775763aSDoug Thompson * 3.4.4.2) registers to map the SysAddr to a node ID. 3286775763aSDoug Thompson */ 3296775763aSDoug Thompson pvt = mci->pvt_info; 3306775763aSDoug Thompson 3316775763aSDoug Thompson /* 3326775763aSDoug Thompson * The value of this field should be the same for all DRAM Base 3336775763aSDoug Thompson * registers. Therefore we arbitrarily choose to read it from the 3346775763aSDoug Thompson * register for node 0. 3356775763aSDoug Thompson */ 3367f19bf75SBorislav Petkov intlv_en = dram_intlv_en(pvt, 0); 3376775763aSDoug Thompson 3386775763aSDoug Thompson if (intlv_en == 0) { 3397f19bf75SBorislav Petkov for (node_id = 0; node_id < DRAM_RANGES; node_id++) { 340d1ea71cdSBorislav Petkov if (base_limit_match(pvt, sys_addr, node_id)) 3416775763aSDoug Thompson goto found; 3426775763aSDoug Thompson } 3438edc5445SBorislav Petkov goto err_no_match; 3448edc5445SBorislav Petkov } 3456775763aSDoug Thompson 34672f158feSBorislav Petkov if (unlikely((intlv_en != 0x01) && 34772f158feSBorislav Petkov (intlv_en != 0x03) && 34872f158feSBorislav Petkov (intlv_en != 0x07))) { 34924f9a7feSBorislav Petkov amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en); 3506775763aSDoug Thompson return NULL; 3516775763aSDoug Thompson } 3526775763aSDoug Thompson 3536775763aSDoug Thompson bits = (((u32) sys_addr) >> 12) & intlv_en; 3546775763aSDoug Thompson 3556775763aSDoug Thompson for (node_id = 0; ; ) { 3567f19bf75SBorislav Petkov if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits) 3576775763aSDoug Thompson break; /* intlv_sel field matches */ 3586775763aSDoug Thompson 3597f19bf75SBorislav Petkov if (++node_id >= DRAM_RANGES) 3606775763aSDoug Thompson goto err_no_match; 3616775763aSDoug Thompson } 3626775763aSDoug Thompson 3636775763aSDoug Thompson /* sanity test for sys_addr */ 364d1ea71cdSBorislav Petkov if (unlikely(!base_limit_match(pvt, sys_addr, node_id))) { 36524f9a7feSBorislav Petkov amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address" 36624f9a7feSBorislav Petkov "range for node %d with node interleaving enabled.\n", 3678edc5445SBorislav Petkov __func__, sys_addr, node_id); 3686775763aSDoug Thompson return NULL; 3696775763aSDoug Thompson } 3706775763aSDoug Thompson 3716775763aSDoug Thompson found: 372b487c33eSBorislav Petkov return edac_mc_find((int)node_id); 3736775763aSDoug Thompson 3746775763aSDoug Thompson err_no_match: 375956b9ba1SJoe Perches edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n", 3766775763aSDoug Thompson (unsigned long)sys_addr); 3776775763aSDoug Thompson 3786775763aSDoug Thompson return NULL; 3796775763aSDoug Thompson } 380e2ce7255SDoug Thompson 381e2ce7255SDoug Thompson /* 38211c75eadSBorislav Petkov * compute the CS base address of the @csrow on the DRAM controller @dct. 38311c75eadSBorislav Petkov * For details see F2x[5C:40] in the processor's BKDG 384e2ce7255SDoug Thompson */ 38511c75eadSBorislav Petkov static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct, 38611c75eadSBorislav Petkov u64 *base, u64 *mask) 387e2ce7255SDoug Thompson { 38811c75eadSBorislav Petkov u64 csbase, csmask, base_bits, mask_bits; 38911c75eadSBorislav Petkov u8 addr_shift; 39011c75eadSBorislav Petkov 39118b94f66SAravind Gopalakrishnan if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) { 39211c75eadSBorislav Petkov csbase = pvt->csels[dct].csbases[csrow]; 39311c75eadSBorislav Petkov csmask = pvt->csels[dct].csmasks[csrow]; 39410ef6b0dSChen, Gong base_bits = GENMASK_ULL(31, 21) | GENMASK_ULL(15, 9); 39510ef6b0dSChen, Gong mask_bits = GENMASK_ULL(29, 21) | GENMASK_ULL(15, 9); 39611c75eadSBorislav Petkov addr_shift = 4; 39794c1acf2SAravind Gopalakrishnan 39894c1acf2SAravind Gopalakrishnan /* 39918b94f66SAravind Gopalakrishnan * F16h and F15h, models 30h and later need two addr_shift values: 40018b94f66SAravind Gopalakrishnan * 8 for high and 6 for low (cf. F16h BKDG). 40194c1acf2SAravind Gopalakrishnan */ 40218b94f66SAravind Gopalakrishnan } else if (pvt->fam == 0x16 || 40318b94f66SAravind Gopalakrishnan (pvt->fam == 0x15 && pvt->model >= 0x30)) { 40494c1acf2SAravind Gopalakrishnan csbase = pvt->csels[dct].csbases[csrow]; 40594c1acf2SAravind Gopalakrishnan csmask = pvt->csels[dct].csmasks[csrow >> 1]; 40694c1acf2SAravind Gopalakrishnan 40710ef6b0dSChen, Gong *base = (csbase & GENMASK_ULL(15, 5)) << 6; 40810ef6b0dSChen, Gong *base |= (csbase & GENMASK_ULL(30, 19)) << 8; 40994c1acf2SAravind Gopalakrishnan 41094c1acf2SAravind Gopalakrishnan *mask = ~0ULL; 41194c1acf2SAravind Gopalakrishnan /* poke holes for the csmask */ 41210ef6b0dSChen, Gong *mask &= ~((GENMASK_ULL(15, 5) << 6) | 41310ef6b0dSChen, Gong (GENMASK_ULL(30, 19) << 8)); 41494c1acf2SAravind Gopalakrishnan 41510ef6b0dSChen, Gong *mask |= (csmask & GENMASK_ULL(15, 5)) << 6; 41610ef6b0dSChen, Gong *mask |= (csmask & GENMASK_ULL(30, 19)) << 8; 41794c1acf2SAravind Gopalakrishnan 41894c1acf2SAravind Gopalakrishnan return; 41911c75eadSBorislav Petkov } else { 42011c75eadSBorislav Petkov csbase = pvt->csels[dct].csbases[csrow]; 42111c75eadSBorislav Petkov csmask = pvt->csels[dct].csmasks[csrow >> 1]; 42211c75eadSBorislav Petkov addr_shift = 8; 42311c75eadSBorislav Petkov 424a4b4bedcSBorislav Petkov if (pvt->fam == 0x15) 42510ef6b0dSChen, Gong base_bits = mask_bits = 42610ef6b0dSChen, Gong GENMASK_ULL(30,19) | GENMASK_ULL(13,5); 42711c75eadSBorislav Petkov else 42810ef6b0dSChen, Gong base_bits = mask_bits = 42910ef6b0dSChen, Gong GENMASK_ULL(28,19) | GENMASK_ULL(13,5); 430e2ce7255SDoug Thompson } 431e2ce7255SDoug Thompson 43211c75eadSBorislav Petkov *base = (csbase & base_bits) << addr_shift; 433e2ce7255SDoug Thompson 43411c75eadSBorislav Petkov *mask = ~0ULL; 43511c75eadSBorislav Petkov /* poke holes for the csmask */ 43611c75eadSBorislav Petkov *mask &= ~(mask_bits << addr_shift); 43711c75eadSBorislav Petkov /* OR them in */ 43811c75eadSBorislav Petkov *mask |= (csmask & mask_bits) << addr_shift; 439e2ce7255SDoug Thompson } 440e2ce7255SDoug Thompson 44111c75eadSBorislav Petkov #define for_each_chip_select(i, dct, pvt) \ 44211c75eadSBorislav Petkov for (i = 0; i < pvt->csels[dct].b_cnt; i++) 44311c75eadSBorislav Petkov 444614ec9d8SBorislav Petkov #define chip_select_base(i, dct, pvt) \ 445614ec9d8SBorislav Petkov pvt->csels[dct].csbases[i] 446614ec9d8SBorislav Petkov 44711c75eadSBorislav Petkov #define for_each_chip_select_mask(i, dct, pvt) \ 44811c75eadSBorislav Petkov for (i = 0; i < pvt->csels[dct].m_cnt; i++) 44911c75eadSBorislav Petkov 4504d30d2bcSYazen Ghannam #define for_each_umc(i) \ 4515e4c5527SYazen Ghannam for (i = 0; i < fam_type->max_mcs; i++) 4524d30d2bcSYazen Ghannam 453e2ce7255SDoug Thompson /* 454e2ce7255SDoug Thompson * @input_addr is an InputAddr associated with the node given by mci. Return the 455e2ce7255SDoug Thompson * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr). 456e2ce7255SDoug Thompson */ 457e2ce7255SDoug Thompson static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr) 458e2ce7255SDoug Thompson { 459e2ce7255SDoug Thompson struct amd64_pvt *pvt; 460e2ce7255SDoug Thompson int csrow; 461e2ce7255SDoug Thompson u64 base, mask; 462e2ce7255SDoug Thompson 463e2ce7255SDoug Thompson pvt = mci->pvt_info; 464e2ce7255SDoug Thompson 46511c75eadSBorislav Petkov for_each_chip_select(csrow, 0, pvt) { 46611c75eadSBorislav Petkov if (!csrow_enabled(csrow, 0, pvt)) 467e2ce7255SDoug Thompson continue; 468e2ce7255SDoug Thompson 46911c75eadSBorislav Petkov get_cs_base_and_mask(pvt, csrow, 0, &base, &mask); 47011c75eadSBorislav Petkov 47111c75eadSBorislav Petkov mask = ~mask; 472e2ce7255SDoug Thompson 473e2ce7255SDoug Thompson if ((input_addr & mask) == (base & mask)) { 474956b9ba1SJoe Perches edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n", 475e2ce7255SDoug Thompson (unsigned long)input_addr, csrow, 476e2ce7255SDoug Thompson pvt->mc_node_id); 477e2ce7255SDoug Thompson 478e2ce7255SDoug Thompson return csrow; 479e2ce7255SDoug Thompson } 480e2ce7255SDoug Thompson } 481956b9ba1SJoe Perches edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n", 482e2ce7255SDoug Thompson (unsigned long)input_addr, pvt->mc_node_id); 483e2ce7255SDoug Thompson 484e2ce7255SDoug Thompson return -1; 485e2ce7255SDoug Thompson } 486e2ce7255SDoug Thompson 487e2ce7255SDoug Thompson /* 488e2ce7255SDoug Thompson * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094) 489e2ce7255SDoug Thompson * for the node represented by mci. Info is passed back in *hole_base, 490e2ce7255SDoug Thompson * *hole_offset, and *hole_size. Function returns 0 if info is valid or 1 if 491e2ce7255SDoug Thompson * info is invalid. Info may be invalid for either of the following reasons: 492e2ce7255SDoug Thompson * 493e2ce7255SDoug Thompson * - The revision of the node is not E or greater. In this case, the DRAM Hole 494e2ce7255SDoug Thompson * Address Register does not exist. 495e2ce7255SDoug Thompson * 496e2ce7255SDoug Thompson * - The DramHoleValid bit is cleared in the DRAM Hole Address Register, 497e2ce7255SDoug Thompson * indicating that its contents are not valid. 498e2ce7255SDoug Thompson * 499e2ce7255SDoug Thompson * The values passed back in *hole_base, *hole_offset, and *hole_size are 500e2ce7255SDoug Thompson * complete 32-bit values despite the fact that the bitfields in the DHAR 501e2ce7255SDoug Thompson * only represent bits 31-24 of the base and offset values. 502e2ce7255SDoug Thompson */ 5032a28ceefSBorislav Petkov static int get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base, 504e2ce7255SDoug Thompson u64 *hole_offset, u64 *hole_size) 505e2ce7255SDoug Thompson { 506e2ce7255SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 507e2ce7255SDoug Thompson 508e2ce7255SDoug Thompson /* only revE and later have the DRAM Hole Address Register */ 509a4b4bedcSBorislav Petkov if (pvt->fam == 0xf && pvt->ext_model < K8_REV_E) { 510956b9ba1SJoe Perches edac_dbg(1, " revision %d for node %d does not support DHAR\n", 511e2ce7255SDoug Thompson pvt->ext_model, pvt->mc_node_id); 512e2ce7255SDoug Thompson return 1; 513e2ce7255SDoug Thompson } 514e2ce7255SDoug Thompson 515bc21fa57SBorislav Petkov /* valid for Fam10h and above */ 516a4b4bedcSBorislav Petkov if (pvt->fam >= 0x10 && !dhar_mem_hoist_valid(pvt)) { 517956b9ba1SJoe Perches edac_dbg(1, " Dram Memory Hoisting is DISABLED on this system\n"); 518e2ce7255SDoug Thompson return 1; 519e2ce7255SDoug Thompson } 520e2ce7255SDoug Thompson 521c8e518d5SBorislav Petkov if (!dhar_valid(pvt)) { 522956b9ba1SJoe Perches edac_dbg(1, " Dram Memory Hoisting is DISABLED on this node %d\n", 523e2ce7255SDoug Thompson pvt->mc_node_id); 524e2ce7255SDoug Thompson return 1; 525e2ce7255SDoug Thompson } 526e2ce7255SDoug Thompson 527e2ce7255SDoug Thompson /* This node has Memory Hoisting */ 528e2ce7255SDoug Thompson 529e2ce7255SDoug Thompson /* +------------------+--------------------+--------------------+----- 530e2ce7255SDoug Thompson * | memory | DRAM hole | relocated | 531e2ce7255SDoug Thompson * | [0, (x - 1)] | [x, 0xffffffff] | addresses from | 532e2ce7255SDoug Thompson * | | | DRAM hole | 533e2ce7255SDoug Thompson * | | | [0x100000000, | 534e2ce7255SDoug Thompson * | | | (0x100000000+ | 535e2ce7255SDoug Thompson * | | | (0xffffffff-x))] | 536e2ce7255SDoug Thompson * +------------------+--------------------+--------------------+----- 537e2ce7255SDoug Thompson * 538e2ce7255SDoug Thompson * Above is a diagram of physical memory showing the DRAM hole and the 539e2ce7255SDoug Thompson * relocated addresses from the DRAM hole. As shown, the DRAM hole 540e2ce7255SDoug Thompson * starts at address x (the base address) and extends through address 541e2ce7255SDoug Thompson * 0xffffffff. The DRAM Hole Address Register (DHAR) relocates the 542e2ce7255SDoug Thompson * addresses in the hole so that they start at 0x100000000. 543e2ce7255SDoug Thompson */ 544e2ce7255SDoug Thompson 5451f31677eSBorislav Petkov *hole_base = dhar_base(pvt); 5461f31677eSBorislav Petkov *hole_size = (1ULL << 32) - *hole_base; 547e2ce7255SDoug Thompson 548a4b4bedcSBorislav Petkov *hole_offset = (pvt->fam > 0xf) ? f10_dhar_offset(pvt) 549a4b4bedcSBorislav Petkov : k8_dhar_offset(pvt); 550e2ce7255SDoug Thompson 551956b9ba1SJoe Perches edac_dbg(1, " DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n", 552e2ce7255SDoug Thompson pvt->mc_node_id, (unsigned long)*hole_base, 553e2ce7255SDoug Thompson (unsigned long)*hole_offset, (unsigned long)*hole_size); 554e2ce7255SDoug Thompson 555e2ce7255SDoug Thompson return 0; 556e2ce7255SDoug Thompson } 5572a28ceefSBorislav Petkov 5582a28ceefSBorislav Petkov #ifdef CONFIG_EDAC_DEBUG 5592a28ceefSBorislav Petkov #define EDAC_DCT_ATTR_SHOW(reg) \ 5602a28ceefSBorislav Petkov static ssize_t reg##_show(struct device *dev, \ 5612a28ceefSBorislav Petkov struct device_attribute *mattr, char *data) \ 5622a28ceefSBorislav Petkov { \ 5632a28ceefSBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); \ 5642a28ceefSBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; \ 5652a28ceefSBorislav Petkov \ 5662a28ceefSBorislav Petkov return sprintf(data, "0x%016llx\n", (u64)pvt->reg); \ 5672a28ceefSBorislav Petkov } 5682a28ceefSBorislav Petkov 5692a28ceefSBorislav Petkov EDAC_DCT_ATTR_SHOW(dhar); 5702a28ceefSBorislav Petkov EDAC_DCT_ATTR_SHOW(dbam0); 5712a28ceefSBorislav Petkov EDAC_DCT_ATTR_SHOW(top_mem); 5722a28ceefSBorislav Petkov EDAC_DCT_ATTR_SHOW(top_mem2); 5732a28ceefSBorislav Petkov 574d19faf0eSDwaipayan Ray static ssize_t dram_hole_show(struct device *dev, struct device_attribute *mattr, 5752a28ceefSBorislav Petkov char *data) 5762a28ceefSBorislav Petkov { 5772a28ceefSBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 5782a28ceefSBorislav Petkov 5792a28ceefSBorislav Petkov u64 hole_base = 0; 5802a28ceefSBorislav Petkov u64 hole_offset = 0; 5812a28ceefSBorislav Petkov u64 hole_size = 0; 5822a28ceefSBorislav Petkov 5832a28ceefSBorislav Petkov get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size); 5842a28ceefSBorislav Petkov 5852a28ceefSBorislav Petkov return sprintf(data, "%llx %llx %llx\n", hole_base, hole_offset, 5862a28ceefSBorislav Petkov hole_size); 5872a28ceefSBorislav Petkov } 5882a28ceefSBorislav Petkov 5892a28ceefSBorislav Petkov /* 5902a28ceefSBorislav Petkov * update NUM_DBG_ATTRS in case you add new members 5912a28ceefSBorislav Petkov */ 5922a28ceefSBorislav Petkov static DEVICE_ATTR(dhar, S_IRUGO, dhar_show, NULL); 5932a28ceefSBorislav Petkov static DEVICE_ATTR(dbam, S_IRUGO, dbam0_show, NULL); 5942a28ceefSBorislav Petkov static DEVICE_ATTR(topmem, S_IRUGO, top_mem_show, NULL); 5952a28ceefSBorislav Petkov static DEVICE_ATTR(topmem2, S_IRUGO, top_mem2_show, NULL); 596d19faf0eSDwaipayan Ray static DEVICE_ATTR_RO(dram_hole); 5972a28ceefSBorislav Petkov 5982a28ceefSBorislav Petkov static struct attribute *dbg_attrs[] = { 5992a28ceefSBorislav Petkov &dev_attr_dhar.attr, 6002a28ceefSBorislav Petkov &dev_attr_dbam.attr, 6012a28ceefSBorislav Petkov &dev_attr_topmem.attr, 6022a28ceefSBorislav Petkov &dev_attr_topmem2.attr, 6032a28ceefSBorislav Petkov &dev_attr_dram_hole.attr, 6042a28ceefSBorislav Petkov NULL 6052a28ceefSBorislav Petkov }; 6062a28ceefSBorislav Petkov 6072a28ceefSBorislav Petkov static const struct attribute_group dbg_group = { 6082a28ceefSBorislav Petkov .attrs = dbg_attrs, 6092a28ceefSBorislav Petkov }; 6102a28ceefSBorislav Petkov 61161810096SBorislav Petkov static ssize_t inject_section_show(struct device *dev, 61261810096SBorislav Petkov struct device_attribute *mattr, char *buf) 61361810096SBorislav Petkov { 61461810096SBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 61561810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 61661810096SBorislav Petkov return sprintf(buf, "0x%x\n", pvt->injection.section); 61761810096SBorislav Petkov } 61861810096SBorislav Petkov 61961810096SBorislav Petkov /* 62061810096SBorislav Petkov * store error injection section value which refers to one of 4 16-byte sections 62161810096SBorislav Petkov * within a 64-byte cacheline 62261810096SBorislav Petkov * 62361810096SBorislav Petkov * range: 0..3 62461810096SBorislav Petkov */ 62561810096SBorislav Petkov static ssize_t inject_section_store(struct device *dev, 62661810096SBorislav Petkov struct device_attribute *mattr, 62761810096SBorislav Petkov const char *data, size_t count) 62861810096SBorislav Petkov { 62961810096SBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 63061810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 63161810096SBorislav Petkov unsigned long value; 63261810096SBorislav Petkov int ret; 63361810096SBorislav Petkov 63461810096SBorislav Petkov ret = kstrtoul(data, 10, &value); 63561810096SBorislav Petkov if (ret < 0) 63661810096SBorislav Petkov return ret; 63761810096SBorislav Petkov 63861810096SBorislav Petkov if (value > 3) { 63961810096SBorislav Petkov amd64_warn("%s: invalid section 0x%lx\n", __func__, value); 64061810096SBorislav Petkov return -EINVAL; 64161810096SBorislav Petkov } 64261810096SBorislav Petkov 64361810096SBorislav Petkov pvt->injection.section = (u32) value; 64461810096SBorislav Petkov return count; 64561810096SBorislav Petkov } 64661810096SBorislav Petkov 64761810096SBorislav Petkov static ssize_t inject_word_show(struct device *dev, 64861810096SBorislav Petkov struct device_attribute *mattr, char *buf) 64961810096SBorislav Petkov { 65061810096SBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 65161810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 65261810096SBorislav Petkov return sprintf(buf, "0x%x\n", pvt->injection.word); 65361810096SBorislav Petkov } 65461810096SBorislav Petkov 65561810096SBorislav Petkov /* 65661810096SBorislav Petkov * store error injection word value which refers to one of 9 16-bit word of the 65761810096SBorislav Petkov * 16-byte (128-bit + ECC bits) section 65861810096SBorislav Petkov * 65961810096SBorislav Petkov * range: 0..8 66061810096SBorislav Petkov */ 66161810096SBorislav Petkov static ssize_t inject_word_store(struct device *dev, 66261810096SBorislav Petkov struct device_attribute *mattr, 66361810096SBorislav Petkov const char *data, size_t count) 66461810096SBorislav Petkov { 66561810096SBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 66661810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 66761810096SBorislav Petkov unsigned long value; 66861810096SBorislav Petkov int ret; 66961810096SBorislav Petkov 67061810096SBorislav Petkov ret = kstrtoul(data, 10, &value); 67161810096SBorislav Petkov if (ret < 0) 67261810096SBorislav Petkov return ret; 67361810096SBorislav Petkov 67461810096SBorislav Petkov if (value > 8) { 67561810096SBorislav Petkov amd64_warn("%s: invalid word 0x%lx\n", __func__, value); 67661810096SBorislav Petkov return -EINVAL; 67761810096SBorislav Petkov } 67861810096SBorislav Petkov 67961810096SBorislav Petkov pvt->injection.word = (u32) value; 68061810096SBorislav Petkov return count; 68161810096SBorislav Petkov } 68261810096SBorislav Petkov 68361810096SBorislav Petkov static ssize_t inject_ecc_vector_show(struct device *dev, 68461810096SBorislav Petkov struct device_attribute *mattr, 68561810096SBorislav Petkov char *buf) 68661810096SBorislav Petkov { 68761810096SBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 68861810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 68961810096SBorislav Petkov return sprintf(buf, "0x%x\n", pvt->injection.bit_map); 69061810096SBorislav Petkov } 69161810096SBorislav Petkov 69261810096SBorislav Petkov /* 69361810096SBorislav Petkov * store 16 bit error injection vector which enables injecting errors to the 69461810096SBorislav Petkov * corresponding bit within the error injection word above. When used during a 69561810096SBorislav Petkov * DRAM ECC read, it holds the contents of the of the DRAM ECC bits. 69661810096SBorislav Petkov */ 69761810096SBorislav Petkov static ssize_t inject_ecc_vector_store(struct device *dev, 69861810096SBorislav Petkov struct device_attribute *mattr, 69961810096SBorislav Petkov const char *data, size_t count) 70061810096SBorislav Petkov { 70161810096SBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 70261810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 70361810096SBorislav Petkov unsigned long value; 70461810096SBorislav Petkov int ret; 70561810096SBorislav Petkov 70661810096SBorislav Petkov ret = kstrtoul(data, 16, &value); 70761810096SBorislav Petkov if (ret < 0) 70861810096SBorislav Petkov return ret; 70961810096SBorislav Petkov 71061810096SBorislav Petkov if (value & 0xFFFF0000) { 71161810096SBorislav Petkov amd64_warn("%s: invalid EccVector: 0x%lx\n", __func__, value); 71261810096SBorislav Petkov return -EINVAL; 71361810096SBorislav Petkov } 71461810096SBorislav Petkov 71561810096SBorislav Petkov pvt->injection.bit_map = (u32) value; 71661810096SBorislav Petkov return count; 71761810096SBorislav Petkov } 71861810096SBorislav Petkov 71961810096SBorislav Petkov /* 72061810096SBorislav Petkov * Do a DRAM ECC read. Assemble staged values in the pvt area, format into 72161810096SBorislav Petkov * fields needed by the injection registers and read the NB Array Data Port. 72261810096SBorislav Petkov */ 72361810096SBorislav Petkov static ssize_t inject_read_store(struct device *dev, 72461810096SBorislav Petkov struct device_attribute *mattr, 72561810096SBorislav Petkov const char *data, size_t count) 72661810096SBorislav Petkov { 72761810096SBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 72861810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 72961810096SBorislav Petkov unsigned long value; 73061810096SBorislav Petkov u32 section, word_bits; 73161810096SBorislav Petkov int ret; 73261810096SBorislav Petkov 73361810096SBorislav Petkov ret = kstrtoul(data, 10, &value); 73461810096SBorislav Petkov if (ret < 0) 73561810096SBorislav Petkov return ret; 73661810096SBorislav Petkov 73761810096SBorislav Petkov /* Form value to choose 16-byte section of cacheline */ 73861810096SBorislav Petkov section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section); 73961810096SBorislav Petkov 74061810096SBorislav Petkov amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section); 74161810096SBorislav Petkov 74261810096SBorislav Petkov word_bits = SET_NB_DRAM_INJECTION_READ(pvt->injection); 74361810096SBorislav Petkov 74461810096SBorislav Petkov /* Issue 'word' and 'bit' along with the READ request */ 74561810096SBorislav Petkov amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits); 74661810096SBorislav Petkov 74761810096SBorislav Petkov edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits); 74861810096SBorislav Petkov 74961810096SBorislav Petkov return count; 75061810096SBorislav Petkov } 75161810096SBorislav Petkov 75261810096SBorislav Petkov /* 75361810096SBorislav Petkov * Do a DRAM ECC write. Assemble staged values in the pvt area and format into 75461810096SBorislav Petkov * fields needed by the injection registers. 75561810096SBorislav Petkov */ 75661810096SBorislav Petkov static ssize_t inject_write_store(struct device *dev, 75761810096SBorislav Petkov struct device_attribute *mattr, 75861810096SBorislav Petkov const char *data, size_t count) 75961810096SBorislav Petkov { 76061810096SBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 76161810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 76261810096SBorislav Petkov u32 section, word_bits, tmp; 76361810096SBorislav Petkov unsigned long value; 76461810096SBorislav Petkov int ret; 76561810096SBorislav Petkov 76661810096SBorislav Petkov ret = kstrtoul(data, 10, &value); 76761810096SBorislav Petkov if (ret < 0) 76861810096SBorislav Petkov return ret; 76961810096SBorislav Petkov 77061810096SBorislav Petkov /* Form value to choose 16-byte section of cacheline */ 77161810096SBorislav Petkov section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section); 77261810096SBorislav Petkov 77361810096SBorislav Petkov amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section); 77461810096SBorislav Petkov 77561810096SBorislav Petkov word_bits = SET_NB_DRAM_INJECTION_WRITE(pvt->injection); 77661810096SBorislav Petkov 77761810096SBorislav Petkov pr_notice_once("Don't forget to decrease MCE polling interval in\n" 77861810096SBorislav Petkov "/sys/bus/machinecheck/devices/machinecheck<CPUNUM>/check_interval\n" 77961810096SBorislav Petkov "so that you can get the error report faster.\n"); 78061810096SBorislav Petkov 78161810096SBorislav Petkov on_each_cpu(disable_caches, NULL, 1); 78261810096SBorislav Petkov 78361810096SBorislav Petkov /* Issue 'word' and 'bit' along with the READ request */ 78461810096SBorislav Petkov amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits); 78561810096SBorislav Petkov 78661810096SBorislav Petkov retry: 78761810096SBorislav Petkov /* wait until injection happens */ 78861810096SBorislav Petkov amd64_read_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, &tmp); 78961810096SBorislav Petkov if (tmp & F10_NB_ARR_ECC_WR_REQ) { 79061810096SBorislav Petkov cpu_relax(); 79161810096SBorislav Petkov goto retry; 79261810096SBorislav Petkov } 79361810096SBorislav Petkov 79461810096SBorislav Petkov on_each_cpu(enable_caches, NULL, 1); 79561810096SBorislav Petkov 79661810096SBorislav Petkov edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits); 79761810096SBorislav Petkov 79861810096SBorislav Petkov return count; 79961810096SBorislav Petkov } 80061810096SBorislav Petkov 80161810096SBorislav Petkov /* 80261810096SBorislav Petkov * update NUM_INJ_ATTRS in case you add new members 80361810096SBorislav Petkov */ 80461810096SBorislav Petkov 805d19faf0eSDwaipayan Ray static DEVICE_ATTR_RW(inject_section); 806d19faf0eSDwaipayan Ray static DEVICE_ATTR_RW(inject_word); 807d19faf0eSDwaipayan Ray static DEVICE_ATTR_RW(inject_ecc_vector); 808d19faf0eSDwaipayan Ray static DEVICE_ATTR_WO(inject_write); 809d19faf0eSDwaipayan Ray static DEVICE_ATTR_WO(inject_read); 81061810096SBorislav Petkov 81161810096SBorislav Petkov static struct attribute *inj_attrs[] = { 81261810096SBorislav Petkov &dev_attr_inject_section.attr, 81361810096SBorislav Petkov &dev_attr_inject_word.attr, 81461810096SBorislav Petkov &dev_attr_inject_ecc_vector.attr, 81561810096SBorislav Petkov &dev_attr_inject_write.attr, 81661810096SBorislav Petkov &dev_attr_inject_read.attr, 81761810096SBorislav Petkov NULL 81861810096SBorislav Petkov }; 81961810096SBorislav Petkov 82061810096SBorislav Petkov static umode_t inj_is_visible(struct kobject *kobj, struct attribute *attr, int idx) 82161810096SBorislav Petkov { 82261810096SBorislav Petkov struct device *dev = kobj_to_dev(kobj); 82361810096SBorislav Petkov struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev); 82461810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 82561810096SBorislav Petkov 8261865bc71SBorislav Petkov /* Families which have that injection hw */ 8271865bc71SBorislav Petkov if (pvt->fam >= 0x10 && pvt->fam <= 0x16) 82861810096SBorislav Petkov return attr->mode; 8291865bc71SBorislav Petkov 8301865bc71SBorislav Petkov return 0; 83161810096SBorislav Petkov } 83261810096SBorislav Petkov 83361810096SBorislav Petkov static const struct attribute_group inj_group = { 83461810096SBorislav Petkov .attrs = inj_attrs, 83561810096SBorislav Petkov .is_visible = inj_is_visible, 83661810096SBorislav Petkov }; 83761810096SBorislav Petkov #endif /* CONFIG_EDAC_DEBUG */ 838e2ce7255SDoug Thompson 83993c2df58SDoug Thompson /* 84093c2df58SDoug Thompson * Return the DramAddr that the SysAddr given by @sys_addr maps to. It is 84193c2df58SDoug Thompson * assumed that sys_addr maps to the node given by mci. 84293c2df58SDoug Thompson * 84393c2df58SDoug Thompson * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section 84493c2df58SDoug Thompson * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a 84593c2df58SDoug Thompson * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled, 84693c2df58SDoug Thompson * then it is also involved in translating a SysAddr to a DramAddr. Sections 84793c2df58SDoug Thompson * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting. 84893c2df58SDoug Thompson * These parts of the documentation are unclear. I interpret them as follows: 84993c2df58SDoug Thompson * 85093c2df58SDoug Thompson * When node n receives a SysAddr, it processes the SysAddr as follows: 85193c2df58SDoug Thompson * 85293c2df58SDoug Thompson * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM 85393c2df58SDoug Thompson * Limit registers for node n. If the SysAddr is not within the range 85493c2df58SDoug Thompson * specified by the base and limit values, then node n ignores the Sysaddr 85593c2df58SDoug Thompson * (since it does not map to node n). Otherwise continue to step 2 below. 85693c2df58SDoug Thompson * 85793c2df58SDoug Thompson * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is 85893c2df58SDoug Thompson * disabled so skip to step 3 below. Otherwise see if the SysAddr is within 85993c2df58SDoug Thompson * the range of relocated addresses (starting at 0x100000000) from the DRAM 86093c2df58SDoug Thompson * hole. If not, skip to step 3 below. Else get the value of the 86193c2df58SDoug Thompson * DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the 86293c2df58SDoug Thompson * offset defined by this value from the SysAddr. 86393c2df58SDoug Thompson * 86493c2df58SDoug Thompson * 3. Obtain the base address for node n from the DRAMBase field of the DRAM 86593c2df58SDoug Thompson * Base register for node n. To obtain the DramAddr, subtract the base 86693c2df58SDoug Thompson * address from the SysAddr, as shown near the start of section 3.4.4 (p.70). 86793c2df58SDoug Thompson */ 86893c2df58SDoug Thompson static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr) 86993c2df58SDoug Thompson { 8707f19bf75SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 87193c2df58SDoug Thompson u64 dram_base, hole_base, hole_offset, hole_size, dram_addr; 8721f31677eSBorislav Petkov int ret; 87393c2df58SDoug Thompson 8747f19bf75SBorislav Petkov dram_base = get_dram_base(pvt, pvt->mc_node_id); 87593c2df58SDoug Thompson 8762a28ceefSBorislav Petkov ret = get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size); 87793c2df58SDoug Thompson if (!ret) { 8781f31677eSBorislav Petkov if ((sys_addr >= (1ULL << 32)) && 8791f31677eSBorislav Petkov (sys_addr < ((1ULL << 32) + hole_size))) { 88093c2df58SDoug Thompson /* use DHAR to translate SysAddr to DramAddr */ 88193c2df58SDoug Thompson dram_addr = sys_addr - hole_offset; 88293c2df58SDoug Thompson 883956b9ba1SJoe Perches edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n", 88493c2df58SDoug Thompson (unsigned long)sys_addr, 88593c2df58SDoug Thompson (unsigned long)dram_addr); 88693c2df58SDoug Thompson 88793c2df58SDoug Thompson return dram_addr; 88893c2df58SDoug Thompson } 88993c2df58SDoug Thompson } 89093c2df58SDoug Thompson 89193c2df58SDoug Thompson /* 89293c2df58SDoug Thompson * Translate the SysAddr to a DramAddr as shown near the start of 89393c2df58SDoug Thompson * section 3.4.4 (p. 70). Although sys_addr is a 64-bit value, the k8 89493c2df58SDoug Thompson * only deals with 40-bit values. Therefore we discard bits 63-40 of 89593c2df58SDoug Thompson * sys_addr below. If bit 39 of sys_addr is 1 then the bits we 89693c2df58SDoug Thompson * discard are all 1s. Otherwise the bits we discard are all 0s. See 89793c2df58SDoug Thompson * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture 89893c2df58SDoug Thompson * Programmer's Manual Volume 1 Application Programming. 89993c2df58SDoug Thompson */ 90010ef6b0dSChen, Gong dram_addr = (sys_addr & GENMASK_ULL(39, 0)) - dram_base; 90193c2df58SDoug Thompson 902956b9ba1SJoe Perches edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n", 903956b9ba1SJoe Perches (unsigned long)sys_addr, (unsigned long)dram_addr); 90493c2df58SDoug Thompson return dram_addr; 90593c2df58SDoug Thompson } 90693c2df58SDoug Thompson 90793c2df58SDoug Thompson /* 90893c2df58SDoug Thompson * @intlv_en is the value of the IntlvEn field from a DRAM Base register 90993c2df58SDoug Thompson * (section 3.4.4.1). Return the number of bits from a SysAddr that are used 91093c2df58SDoug Thompson * for node interleaving. 91193c2df58SDoug Thompson */ 91293c2df58SDoug Thompson static int num_node_interleave_bits(unsigned intlv_en) 91393c2df58SDoug Thompson { 91493c2df58SDoug Thompson static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 }; 91593c2df58SDoug Thompson int n; 91693c2df58SDoug Thompson 91793c2df58SDoug Thompson BUG_ON(intlv_en > 7); 91893c2df58SDoug Thompson n = intlv_shift_table[intlv_en]; 91993c2df58SDoug Thompson return n; 92093c2df58SDoug Thompson } 92193c2df58SDoug Thompson 92293c2df58SDoug Thompson /* Translate the DramAddr given by @dram_addr to an InputAddr. */ 92393c2df58SDoug Thompson static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr) 92493c2df58SDoug Thompson { 92593c2df58SDoug Thompson struct amd64_pvt *pvt; 92693c2df58SDoug Thompson int intlv_shift; 92793c2df58SDoug Thompson u64 input_addr; 92893c2df58SDoug Thompson 92993c2df58SDoug Thompson pvt = mci->pvt_info; 93093c2df58SDoug Thompson 93193c2df58SDoug Thompson /* 93293c2df58SDoug Thompson * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E) 93393c2df58SDoug Thompson * concerning translating a DramAddr to an InputAddr. 93493c2df58SDoug Thompson */ 9357f19bf75SBorislav Petkov intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0)); 93610ef6b0dSChen, Gong input_addr = ((dram_addr >> intlv_shift) & GENMASK_ULL(35, 12)) + 93793c2df58SDoug Thompson (dram_addr & 0xfff); 93893c2df58SDoug Thompson 939956b9ba1SJoe Perches edac_dbg(2, " Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n", 94093c2df58SDoug Thompson intlv_shift, (unsigned long)dram_addr, 94193c2df58SDoug Thompson (unsigned long)input_addr); 94293c2df58SDoug Thompson 94393c2df58SDoug Thompson return input_addr; 94493c2df58SDoug Thompson } 94593c2df58SDoug Thompson 94693c2df58SDoug Thompson /* 94793c2df58SDoug Thompson * Translate the SysAddr represented by @sys_addr to an InputAddr. It is 94893c2df58SDoug Thompson * assumed that @sys_addr maps to the node given by mci. 94993c2df58SDoug Thompson */ 95093c2df58SDoug Thompson static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr) 95193c2df58SDoug Thompson { 95293c2df58SDoug Thompson u64 input_addr; 95393c2df58SDoug Thompson 95493c2df58SDoug Thompson input_addr = 95593c2df58SDoug Thompson dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr)); 95693c2df58SDoug Thompson 957c19ca6cbSMasanari Iida edac_dbg(2, "SysAddr 0x%lx translates to InputAddr 0x%lx\n", 95893c2df58SDoug Thompson (unsigned long)sys_addr, (unsigned long)input_addr); 95993c2df58SDoug Thompson 96093c2df58SDoug Thompson return input_addr; 96193c2df58SDoug Thompson } 96293c2df58SDoug Thompson 96393c2df58SDoug Thompson /* Map the Error address to a PAGE and PAGE OFFSET. */ 96493c2df58SDoug Thompson static inline void error_address_to_page_and_offset(u64 error_address, 96533ca0643SBorislav Petkov struct err_info *err) 96693c2df58SDoug Thompson { 96733ca0643SBorislav Petkov err->page = (u32) (error_address >> PAGE_SHIFT); 96833ca0643SBorislav Petkov err->offset = ((u32) error_address) & ~PAGE_MASK; 96993c2df58SDoug Thompson } 97093c2df58SDoug Thompson 97193c2df58SDoug Thompson /* 97293c2df58SDoug Thompson * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address 97393c2df58SDoug Thompson * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers 97493c2df58SDoug Thompson * of a node that detected an ECC memory error. mci represents the node that 97593c2df58SDoug Thompson * the error address maps to (possibly different from the node that detected 97693c2df58SDoug Thompson * the error). Return the number of the csrow that sys_addr maps to, or -1 on 97793c2df58SDoug Thompson * error. 97893c2df58SDoug Thompson */ 97993c2df58SDoug Thompson static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr) 98093c2df58SDoug Thompson { 98193c2df58SDoug Thompson int csrow; 98293c2df58SDoug Thompson 98393c2df58SDoug Thompson csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr)); 98493c2df58SDoug Thompson 98593c2df58SDoug Thompson if (csrow == -1) 98624f9a7feSBorislav Petkov amd64_mc_err(mci, "Failed to translate InputAddr to csrow for " 98793c2df58SDoug Thompson "address 0x%lx\n", (unsigned long)sys_addr); 98893c2df58SDoug Thompson return csrow; 98993c2df58SDoug Thompson } 990e2ce7255SDoug Thompson 991b3218ae4SYazen Ghannam /* Protect the PCI config register pairs used for DF indirect access. */ 992b3218ae4SYazen Ghannam static DEFINE_MUTEX(df_indirect_mutex); 993b3218ae4SYazen Ghannam 994b3218ae4SYazen Ghannam /* 995b3218ae4SYazen Ghannam * Data Fabric Indirect Access uses FICAA/FICAD. 996b3218ae4SYazen Ghannam * 997b3218ae4SYazen Ghannam * Fabric Indirect Configuration Access Address (FICAA): Constructed based 998b3218ae4SYazen Ghannam * on the device's Instance Id and the PCI function and register offset of 999b3218ae4SYazen Ghannam * the desired register. 1000b3218ae4SYazen Ghannam * 1001b3218ae4SYazen Ghannam * Fabric Indirect Configuration Access Data (FICAD): There are FICAD LO 1002b3218ae4SYazen Ghannam * and FICAD HI registers but so far we only need the LO register. 1003448c3d60SYazen Ghannam * 1004448c3d60SYazen Ghannam * Use Instance Id 0xFF to indicate a broadcast read. 1005b3218ae4SYazen Ghannam */ 1006448c3d60SYazen Ghannam #define DF_BROADCAST 0xFF 1007448c3d60SYazen Ghannam static int __df_indirect_read(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo) 1008b3218ae4SYazen Ghannam { 1009b3218ae4SYazen Ghannam struct pci_dev *F4; 1010b3218ae4SYazen Ghannam u32 ficaa; 1011b3218ae4SYazen Ghannam int err = -ENODEV; 1012b3218ae4SYazen Ghannam 1013b3218ae4SYazen Ghannam if (node >= amd_nb_num()) 1014b3218ae4SYazen Ghannam goto out; 1015b3218ae4SYazen Ghannam 1016b3218ae4SYazen Ghannam F4 = node_to_amd_nb(node)->link; 1017b3218ae4SYazen Ghannam if (!F4) 1018b3218ae4SYazen Ghannam goto out; 1019b3218ae4SYazen Ghannam 1020448c3d60SYazen Ghannam ficaa = (instance_id == DF_BROADCAST) ? 0 : 1; 1021b3218ae4SYazen Ghannam ficaa |= reg & 0x3FC; 1022b3218ae4SYazen Ghannam ficaa |= (func & 0x7) << 11; 1023b3218ae4SYazen Ghannam ficaa |= instance_id << 16; 1024b3218ae4SYazen Ghannam 1025b3218ae4SYazen Ghannam mutex_lock(&df_indirect_mutex); 1026b3218ae4SYazen Ghannam 1027b3218ae4SYazen Ghannam err = pci_write_config_dword(F4, 0x5C, ficaa); 1028b3218ae4SYazen Ghannam if (err) { 1029b3218ae4SYazen Ghannam pr_warn("Error writing DF Indirect FICAA, FICAA=0x%x\n", ficaa); 1030b3218ae4SYazen Ghannam goto out_unlock; 1031b3218ae4SYazen Ghannam } 1032b3218ae4SYazen Ghannam 1033b3218ae4SYazen Ghannam err = pci_read_config_dword(F4, 0x98, lo); 1034b3218ae4SYazen Ghannam if (err) 1035b3218ae4SYazen Ghannam pr_warn("Error reading DF Indirect FICAD LO, FICAA=0x%x.\n", ficaa); 1036b3218ae4SYazen Ghannam 1037b3218ae4SYazen Ghannam out_unlock: 1038b3218ae4SYazen Ghannam mutex_unlock(&df_indirect_mutex); 1039b3218ae4SYazen Ghannam 1040b3218ae4SYazen Ghannam out: 1041b3218ae4SYazen Ghannam return err; 1042b3218ae4SYazen Ghannam } 1043b3218ae4SYazen Ghannam 1044448c3d60SYazen Ghannam static int df_indirect_read_instance(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo) 1045448c3d60SYazen Ghannam { 1046448c3d60SYazen Ghannam return __df_indirect_read(node, func, reg, instance_id, lo); 1047448c3d60SYazen Ghannam } 1048448c3d60SYazen Ghannam 1049448c3d60SYazen Ghannam static int df_indirect_read_broadcast(u16 node, u8 func, u16 reg, u32 *lo) 1050448c3d60SYazen Ghannam { 1051448c3d60SYazen Ghannam return __df_indirect_read(node, func, reg, DF_BROADCAST, lo); 1052448c3d60SYazen Ghannam } 1053448c3d60SYazen Ghannam 1054*70aeb807SYazen Ghannam struct addr_ctx { 1055*70aeb807SYazen Ghannam u64 ret_addr; 1056*70aeb807SYazen Ghannam u32 tmp; 1057*70aeb807SYazen Ghannam u16 nid; 1058*70aeb807SYazen Ghannam u8 inst_id; 1059*70aeb807SYazen Ghannam }; 1060*70aeb807SYazen Ghannam 10610b746e8cSYazen Ghannam static int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) 10620b746e8cSYazen Ghannam { 10630b746e8cSYazen Ghannam u64 dram_base_addr, dram_limit_addr, dram_hole_base; 10640b746e8cSYazen Ghannam 10650b746e8cSYazen Ghannam u8 die_id_shift, die_id_mask, socket_id_shift, socket_id_mask; 10660b746e8cSYazen Ghannam u8 intlv_num_dies, intlv_num_chan, intlv_num_sockets; 10670b746e8cSYazen Ghannam u8 intlv_addr_sel, intlv_addr_bit; 10680b746e8cSYazen Ghannam u8 num_intlv_bits, hashed_bit; 10690b746e8cSYazen Ghannam u8 lgcy_mmio_hole_en, base = 0; 10700b746e8cSYazen Ghannam u8 cs_mask, cs_id = 0; 10710b746e8cSYazen Ghannam bool hash_enabled = false; 10720b746e8cSYazen Ghannam 1073*70aeb807SYazen Ghannam struct addr_ctx ctx; 1074*70aeb807SYazen Ghannam 1075*70aeb807SYazen Ghannam memset(&ctx, 0, sizeof(ctx)); 1076*70aeb807SYazen Ghannam 1077*70aeb807SYazen Ghannam /* Start from the normalized address */ 1078*70aeb807SYazen Ghannam ctx.ret_addr = norm_addr; 1079*70aeb807SYazen Ghannam 1080*70aeb807SYazen Ghannam ctx.nid = nid; 1081*70aeb807SYazen Ghannam ctx.inst_id = umc; 1082*70aeb807SYazen Ghannam 10830b746e8cSYazen Ghannam /* Read D18F0x1B4 (DramOffset), check if base 1 is used. */ 1084*70aeb807SYazen Ghannam if (df_indirect_read_instance(nid, 0, 0x1B4, umc, &ctx.tmp)) 10850b746e8cSYazen Ghannam goto out_err; 10860b746e8cSYazen Ghannam 10870b746e8cSYazen Ghannam /* Remove HiAddrOffset from normalized address, if enabled: */ 1088*70aeb807SYazen Ghannam if (ctx.tmp & BIT(0)) { 1089*70aeb807SYazen Ghannam u64 hi_addr_offset = (ctx.tmp & GENMASK_ULL(31, 20)) << 8; 10900b746e8cSYazen Ghannam 10910b746e8cSYazen Ghannam if (norm_addr >= hi_addr_offset) { 1092*70aeb807SYazen Ghannam ctx.ret_addr -= hi_addr_offset; 10930b746e8cSYazen Ghannam base = 1; 10940b746e8cSYazen Ghannam } 10950b746e8cSYazen Ghannam } 10960b746e8cSYazen Ghannam 10970b746e8cSYazen Ghannam /* Read D18F0x110 (DramBaseAddress). */ 1098*70aeb807SYazen Ghannam if (df_indirect_read_instance(nid, 0, 0x110 + (8 * base), umc, &ctx.tmp)) 10990b746e8cSYazen Ghannam goto out_err; 11000b746e8cSYazen Ghannam 11010b746e8cSYazen Ghannam /* Check if address range is valid. */ 1102*70aeb807SYazen Ghannam if (!(ctx.tmp & BIT(0))) { 11030b746e8cSYazen Ghannam pr_err("%s: Invalid DramBaseAddress range: 0x%x.\n", 1104*70aeb807SYazen Ghannam __func__, ctx.tmp); 11050b746e8cSYazen Ghannam goto out_err; 11060b746e8cSYazen Ghannam } 11070b746e8cSYazen Ghannam 1108*70aeb807SYazen Ghannam lgcy_mmio_hole_en = ctx.tmp & BIT(1); 1109*70aeb807SYazen Ghannam intlv_num_chan = (ctx.tmp >> 4) & 0xF; 1110*70aeb807SYazen Ghannam intlv_addr_sel = (ctx.tmp >> 8) & 0x7; 1111*70aeb807SYazen Ghannam dram_base_addr = (ctx.tmp & GENMASK_ULL(31, 12)) << 16; 11120b746e8cSYazen Ghannam 11130b746e8cSYazen Ghannam /* {0, 1, 2, 3} map to address bits {8, 9, 10, 11} respectively */ 11140b746e8cSYazen Ghannam if (intlv_addr_sel > 3) { 11150b746e8cSYazen Ghannam pr_err("%s: Invalid interleave address select %d.\n", 11160b746e8cSYazen Ghannam __func__, intlv_addr_sel); 11170b746e8cSYazen Ghannam goto out_err; 11180b746e8cSYazen Ghannam } 11190b746e8cSYazen Ghannam 11200b746e8cSYazen Ghannam /* Read D18F0x114 (DramLimitAddress). */ 1121*70aeb807SYazen Ghannam if (df_indirect_read_instance(nid, 0, 0x114 + (8 * base), umc, &ctx.tmp)) 11220b746e8cSYazen Ghannam goto out_err; 11230b746e8cSYazen Ghannam 1124*70aeb807SYazen Ghannam intlv_num_sockets = (ctx.tmp >> 8) & 0x1; 1125*70aeb807SYazen Ghannam intlv_num_dies = (ctx.tmp >> 10) & 0x3; 1126*70aeb807SYazen Ghannam dram_limit_addr = ((ctx.tmp & GENMASK_ULL(31, 12)) << 16) | GENMASK_ULL(27, 0); 11270b746e8cSYazen Ghannam 11280b746e8cSYazen Ghannam intlv_addr_bit = intlv_addr_sel + 8; 11290b746e8cSYazen Ghannam 11300b746e8cSYazen Ghannam /* Re-use intlv_num_chan by setting it equal to log2(#channels) */ 11310b746e8cSYazen Ghannam switch (intlv_num_chan) { 11320b746e8cSYazen Ghannam case 0: intlv_num_chan = 0; break; 11330b746e8cSYazen Ghannam case 1: intlv_num_chan = 1; break; 11340b746e8cSYazen Ghannam case 3: intlv_num_chan = 2; break; 11350b746e8cSYazen Ghannam case 5: intlv_num_chan = 3; break; 11360b746e8cSYazen Ghannam case 7: intlv_num_chan = 4; break; 11370b746e8cSYazen Ghannam 11380b746e8cSYazen Ghannam case 8: intlv_num_chan = 1; 11390b746e8cSYazen Ghannam hash_enabled = true; 11400b746e8cSYazen Ghannam break; 11410b746e8cSYazen Ghannam default: 11420b746e8cSYazen Ghannam pr_err("%s: Invalid number of interleaved channels %d.\n", 11430b746e8cSYazen Ghannam __func__, intlv_num_chan); 11440b746e8cSYazen Ghannam goto out_err; 11450b746e8cSYazen Ghannam } 11460b746e8cSYazen Ghannam 11470b746e8cSYazen Ghannam num_intlv_bits = intlv_num_chan; 11480b746e8cSYazen Ghannam 11490b746e8cSYazen Ghannam if (intlv_num_dies > 2) { 11500b746e8cSYazen Ghannam pr_err("%s: Invalid number of interleaved nodes/dies %d.\n", 11510b746e8cSYazen Ghannam __func__, intlv_num_dies); 11520b746e8cSYazen Ghannam goto out_err; 11530b746e8cSYazen Ghannam } 11540b746e8cSYazen Ghannam 11550b746e8cSYazen Ghannam num_intlv_bits += intlv_num_dies; 11560b746e8cSYazen Ghannam 11570b746e8cSYazen Ghannam /* Add a bit if sockets are interleaved. */ 11580b746e8cSYazen Ghannam num_intlv_bits += intlv_num_sockets; 11590b746e8cSYazen Ghannam 11600b746e8cSYazen Ghannam /* Assert num_intlv_bits <= 4 */ 11610b746e8cSYazen Ghannam if (num_intlv_bits > 4) { 11620b746e8cSYazen Ghannam pr_err("%s: Invalid interleave bits %d.\n", 11630b746e8cSYazen Ghannam __func__, num_intlv_bits); 11640b746e8cSYazen Ghannam goto out_err; 11650b746e8cSYazen Ghannam } 11660b746e8cSYazen Ghannam 11670b746e8cSYazen Ghannam if (num_intlv_bits > 0) { 11680b746e8cSYazen Ghannam u64 temp_addr_x, temp_addr_i, temp_addr_y; 11690b746e8cSYazen Ghannam u8 die_id_bit, sock_id_bit, cs_fabric_id; 11700b746e8cSYazen Ghannam 11710b746e8cSYazen Ghannam /* 11720b746e8cSYazen Ghannam * Read FabricBlockInstanceInformation3_CS[BlockFabricID]. 11730b746e8cSYazen Ghannam * This is the fabric id for this coherent slave. Use 11740b746e8cSYazen Ghannam * umc/channel# as instance id of the coherent slave 11750b746e8cSYazen Ghannam * for FICAA. 11760b746e8cSYazen Ghannam */ 1177*70aeb807SYazen Ghannam if (df_indirect_read_instance(nid, 0, 0x50, umc, &ctx.tmp)) 11780b746e8cSYazen Ghannam goto out_err; 11790b746e8cSYazen Ghannam 1180*70aeb807SYazen Ghannam cs_fabric_id = (ctx.tmp >> 8) & 0xFF; 11810b746e8cSYazen Ghannam die_id_bit = 0; 11820b746e8cSYazen Ghannam 11830b746e8cSYazen Ghannam /* If interleaved over more than 1 channel: */ 11840b746e8cSYazen Ghannam if (intlv_num_chan) { 11850b746e8cSYazen Ghannam die_id_bit = intlv_num_chan; 11860b746e8cSYazen Ghannam cs_mask = (1 << die_id_bit) - 1; 11870b746e8cSYazen Ghannam cs_id = cs_fabric_id & cs_mask; 11880b746e8cSYazen Ghannam } 11890b746e8cSYazen Ghannam 11900b746e8cSYazen Ghannam sock_id_bit = die_id_bit; 11910b746e8cSYazen Ghannam 11920b746e8cSYazen Ghannam /* Read D18F1x208 (SystemFabricIdMask). */ 11930b746e8cSYazen Ghannam if (intlv_num_dies || intlv_num_sockets) 1194*70aeb807SYazen Ghannam if (df_indirect_read_broadcast(nid, 1, 0x208, &ctx.tmp)) 11950b746e8cSYazen Ghannam goto out_err; 11960b746e8cSYazen Ghannam 11970b746e8cSYazen Ghannam /* If interleaved over more than 1 die. */ 11980b746e8cSYazen Ghannam if (intlv_num_dies) { 11990b746e8cSYazen Ghannam sock_id_bit = die_id_bit + intlv_num_dies; 1200*70aeb807SYazen Ghannam die_id_shift = (ctx.tmp >> 24) & 0xF; 1201*70aeb807SYazen Ghannam die_id_mask = (ctx.tmp >> 8) & 0xFF; 12020b746e8cSYazen Ghannam 12030b746e8cSYazen Ghannam cs_id |= ((cs_fabric_id & die_id_mask) >> die_id_shift) << die_id_bit; 12040b746e8cSYazen Ghannam } 12050b746e8cSYazen Ghannam 12060b746e8cSYazen Ghannam /* If interleaved over more than 1 socket. */ 12070b746e8cSYazen Ghannam if (intlv_num_sockets) { 1208*70aeb807SYazen Ghannam socket_id_shift = (ctx.tmp >> 28) & 0xF; 1209*70aeb807SYazen Ghannam socket_id_mask = (ctx.tmp >> 16) & 0xFF; 12100b746e8cSYazen Ghannam 12110b746e8cSYazen Ghannam cs_id |= ((cs_fabric_id & socket_id_mask) >> socket_id_shift) << sock_id_bit; 12120b746e8cSYazen Ghannam } 12130b746e8cSYazen Ghannam 12140b746e8cSYazen Ghannam /* 12150b746e8cSYazen Ghannam * The pre-interleaved address consists of XXXXXXIIIYYYYY 12160b746e8cSYazen Ghannam * where III is the ID for this CS, and XXXXXXYYYYY are the 12170b746e8cSYazen Ghannam * address bits from the post-interleaved address. 12180b746e8cSYazen Ghannam * "num_intlv_bits" has been calculated to tell us how many "I" 12190b746e8cSYazen Ghannam * bits there are. "intlv_addr_bit" tells us how many "Y" bits 12200b746e8cSYazen Ghannam * there are (where "I" starts). 12210b746e8cSYazen Ghannam */ 1222*70aeb807SYazen Ghannam temp_addr_y = ctx.ret_addr & GENMASK_ULL(intlv_addr_bit - 1, 0); 12230b746e8cSYazen Ghannam temp_addr_i = (cs_id << intlv_addr_bit); 1224*70aeb807SYazen Ghannam temp_addr_x = (ctx.ret_addr & GENMASK_ULL(63, intlv_addr_bit)) << num_intlv_bits; 1225*70aeb807SYazen Ghannam ctx.ret_addr = temp_addr_x | temp_addr_i | temp_addr_y; 12260b746e8cSYazen Ghannam } 12270b746e8cSYazen Ghannam 12280b746e8cSYazen Ghannam /* Add dram base address */ 1229*70aeb807SYazen Ghannam ctx.ret_addr += dram_base_addr; 12300b746e8cSYazen Ghannam 12310b746e8cSYazen Ghannam /* If legacy MMIO hole enabled */ 12320b746e8cSYazen Ghannam if (lgcy_mmio_hole_en) { 1233*70aeb807SYazen Ghannam if (df_indirect_read_broadcast(nid, 0, 0x104, &ctx.tmp)) 12340b746e8cSYazen Ghannam goto out_err; 12350b746e8cSYazen Ghannam 1236*70aeb807SYazen Ghannam dram_hole_base = ctx.tmp & GENMASK(31, 24); 1237*70aeb807SYazen Ghannam if (ctx.ret_addr >= dram_hole_base) 1238*70aeb807SYazen Ghannam ctx.ret_addr += (BIT_ULL(32) - dram_hole_base); 12390b746e8cSYazen Ghannam } 12400b746e8cSYazen Ghannam 12410b746e8cSYazen Ghannam if (hash_enabled) { 12420b746e8cSYazen Ghannam /* Save some parentheses and grab ls-bit at the end. */ 1243*70aeb807SYazen Ghannam hashed_bit = (ctx.ret_addr >> 12) ^ 1244*70aeb807SYazen Ghannam (ctx.ret_addr >> 18) ^ 1245*70aeb807SYazen Ghannam (ctx.ret_addr >> 21) ^ 1246*70aeb807SYazen Ghannam (ctx.ret_addr >> 30) ^ 12470b746e8cSYazen Ghannam cs_id; 12480b746e8cSYazen Ghannam 12490b746e8cSYazen Ghannam hashed_bit &= BIT(0); 12500b746e8cSYazen Ghannam 1251*70aeb807SYazen Ghannam if (hashed_bit != ((ctx.ret_addr >> intlv_addr_bit) & BIT(0))) 1252*70aeb807SYazen Ghannam ctx.ret_addr ^= BIT(intlv_addr_bit); 12530b746e8cSYazen Ghannam } 12540b746e8cSYazen Ghannam 12550b746e8cSYazen Ghannam /* Is calculated system address is above DRAM limit address? */ 1256*70aeb807SYazen Ghannam if (ctx.ret_addr > dram_limit_addr) 12570b746e8cSYazen Ghannam goto out_err; 12580b746e8cSYazen Ghannam 1259*70aeb807SYazen Ghannam *sys_addr = ctx.ret_addr; 12600b746e8cSYazen Ghannam return 0; 12610b746e8cSYazen Ghannam 12620b746e8cSYazen Ghannam out_err: 12630b746e8cSYazen Ghannam return -EINVAL; 12640b746e8cSYazen Ghannam } 12650b746e8cSYazen Ghannam 1266bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16); 12672da11654SDoug Thompson 12682da11654SDoug Thompson /* 12692da11654SDoug Thompson * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs 12702da11654SDoug Thompson * are ECC capable. 12712da11654SDoug Thompson */ 1272d1ea71cdSBorislav Petkov static unsigned long determine_edac_cap(struct amd64_pvt *pvt) 12732da11654SDoug Thompson { 12741f6189edSDan Carpenter unsigned long edac_cap = EDAC_FLAG_NONE; 1275d27f3a34SYazen Ghannam u8 bit; 12762da11654SDoug Thompson 1277d27f3a34SYazen Ghannam if (pvt->umc) { 1278d27f3a34SYazen Ghannam u8 i, umc_en_mask = 0, dimm_ecc_en_mask = 0; 1279d27f3a34SYazen Ghannam 12804d30d2bcSYazen Ghannam for_each_umc(i) { 1281d27f3a34SYazen Ghannam if (!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT)) 1282d27f3a34SYazen Ghannam continue; 1283d27f3a34SYazen Ghannam 1284d27f3a34SYazen Ghannam umc_en_mask |= BIT(i); 1285d27f3a34SYazen Ghannam 1286d27f3a34SYazen Ghannam /* UMC Configuration bit 12 (DimmEccEn) */ 1287d27f3a34SYazen Ghannam if (pvt->umc[i].umc_cfg & BIT(12)) 1288d27f3a34SYazen Ghannam dimm_ecc_en_mask |= BIT(i); 1289d27f3a34SYazen Ghannam } 1290d27f3a34SYazen Ghannam 1291d27f3a34SYazen Ghannam if (umc_en_mask == dimm_ecc_en_mask) 1292d27f3a34SYazen Ghannam edac_cap = EDAC_FLAG_SECDED; 1293d27f3a34SYazen Ghannam } else { 1294a4b4bedcSBorislav Petkov bit = (pvt->fam > 0xf || pvt->ext_model >= K8_REV_F) 12952da11654SDoug Thompson ? 19 12962da11654SDoug Thompson : 17; 12972da11654SDoug Thompson 1298584fcff4SBorislav Petkov if (pvt->dclr0 & BIT(bit)) 12992da11654SDoug Thompson edac_cap = EDAC_FLAG_SECDED; 1300d27f3a34SYazen Ghannam } 13012da11654SDoug Thompson 13022da11654SDoug Thompson return edac_cap; 13032da11654SDoug Thompson } 13042da11654SDoug Thompson 1305d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *, u8); 13062da11654SDoug Thompson 1307d1ea71cdSBorislav Petkov static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan) 130868798e17SBorislav Petkov { 1309956b9ba1SJoe Perches edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr); 131068798e17SBorislav Petkov 1311a597d2a5SAravind Gopalakrishnan if (pvt->dram_type == MEM_LRDDR3) { 1312a597d2a5SAravind Gopalakrishnan u32 dcsm = pvt->csels[chan].csmasks[0]; 1313a597d2a5SAravind Gopalakrishnan /* 1314a597d2a5SAravind Gopalakrishnan * It's assumed all LRDIMMs in a DCT are going to be of 1315a597d2a5SAravind Gopalakrishnan * same 'type' until proven otherwise. So, use a cs 1316a597d2a5SAravind Gopalakrishnan * value of '0' here to get dcsm value. 1317a597d2a5SAravind Gopalakrishnan */ 1318a597d2a5SAravind Gopalakrishnan edac_dbg(1, " LRDIMM %dx rank multiply\n", (dcsm & 0x3)); 1319a597d2a5SAravind Gopalakrishnan } 1320a597d2a5SAravind Gopalakrishnan 1321a597d2a5SAravind Gopalakrishnan edac_dbg(1, "All DIMMs support ECC:%s\n", 132268798e17SBorislav Petkov (dclr & BIT(19)) ? "yes" : "no"); 132368798e17SBorislav Petkov 1324a597d2a5SAravind Gopalakrishnan 1325956b9ba1SJoe Perches edac_dbg(1, " PAR/ERR parity: %s\n", 132668798e17SBorislav Petkov (dclr & BIT(8)) ? "enabled" : "disabled"); 132768798e17SBorislav Petkov 1328a4b4bedcSBorislav Petkov if (pvt->fam == 0x10) 1329956b9ba1SJoe Perches edac_dbg(1, " DCT 128bit mode width: %s\n", 133068798e17SBorislav Petkov (dclr & BIT(11)) ? "128b" : "64b"); 133168798e17SBorislav Petkov 1332956b9ba1SJoe Perches edac_dbg(1, " x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n", 133368798e17SBorislav Petkov (dclr & BIT(12)) ? "yes" : "no", 133468798e17SBorislav Petkov (dclr & BIT(13)) ? "yes" : "no", 133568798e17SBorislav Petkov (dclr & BIT(14)) ? "yes" : "no", 133668798e17SBorislav Petkov (dclr & BIT(15)) ? "yes" : "no"); 133768798e17SBorislav Petkov } 133868798e17SBorislav Petkov 1339e53a3b26SYazen Ghannam #define CS_EVEN_PRIMARY BIT(0) 1340e53a3b26SYazen Ghannam #define CS_ODD_PRIMARY BIT(1) 134181f5090dSYazen Ghannam #define CS_EVEN_SECONDARY BIT(2) 134281f5090dSYazen Ghannam #define CS_ODD_SECONDARY BIT(3) 13439f4873fbSYazen Ghannam #define CS_3R_INTERLEAVE BIT(4) 1344e53a3b26SYazen Ghannam 134581f5090dSYazen Ghannam #define CS_EVEN (CS_EVEN_PRIMARY | CS_EVEN_SECONDARY) 134681f5090dSYazen Ghannam #define CS_ODD (CS_ODD_PRIMARY | CS_ODD_SECONDARY) 1347e53a3b26SYazen Ghannam 1348e53a3b26SYazen Ghannam static int f17_get_cs_mode(int dimm, u8 ctrl, struct amd64_pvt *pvt) 1349fc00c6a4SYazen Ghannam { 13509f4873fbSYazen Ghannam u8 base, count = 0; 1351e53a3b26SYazen Ghannam int cs_mode = 0; 1352fc00c6a4SYazen Ghannam 1353e53a3b26SYazen Ghannam if (csrow_enabled(2 * dimm, ctrl, pvt)) 1354e53a3b26SYazen Ghannam cs_mode |= CS_EVEN_PRIMARY; 1355fc00c6a4SYazen Ghannam 1356e53a3b26SYazen Ghannam if (csrow_enabled(2 * dimm + 1, ctrl, pvt)) 1357e53a3b26SYazen Ghannam cs_mode |= CS_ODD_PRIMARY; 1358e53a3b26SYazen Ghannam 135981f5090dSYazen Ghannam /* Asymmetric dual-rank DIMM support. */ 136081f5090dSYazen Ghannam if (csrow_sec_enabled(2 * dimm + 1, ctrl, pvt)) 136181f5090dSYazen Ghannam cs_mode |= CS_ODD_SECONDARY; 136281f5090dSYazen Ghannam 13639f4873fbSYazen Ghannam /* 13649f4873fbSYazen Ghannam * 3 Rank inteleaving support. 13659f4873fbSYazen Ghannam * There should be only three bases enabled and their two masks should 13669f4873fbSYazen Ghannam * be equal. 13679f4873fbSYazen Ghannam */ 13689f4873fbSYazen Ghannam for_each_chip_select(base, ctrl, pvt) 13699f4873fbSYazen Ghannam count += csrow_enabled(base, ctrl, pvt); 13709f4873fbSYazen Ghannam 13719f4873fbSYazen Ghannam if (count == 3 && 13729f4873fbSYazen Ghannam pvt->csels[ctrl].csmasks[0] == pvt->csels[ctrl].csmasks[1]) { 13739f4873fbSYazen Ghannam edac_dbg(1, "3R interleaving in use.\n"); 13749f4873fbSYazen Ghannam cs_mode |= CS_3R_INTERLEAVE; 13759f4873fbSYazen Ghannam } 13769f4873fbSYazen Ghannam 1377e53a3b26SYazen Ghannam return cs_mode; 1378fc00c6a4SYazen Ghannam } 1379fc00c6a4SYazen Ghannam 138007ed82efSYazen Ghannam static void debug_display_dimm_sizes_df(struct amd64_pvt *pvt, u8 ctrl) 138107ed82efSYazen Ghannam { 1382e53a3b26SYazen Ghannam int dimm, size0, size1, cs0, cs1, cs_mode; 138307ed82efSYazen Ghannam 138407ed82efSYazen Ghannam edac_printk(KERN_DEBUG, EDAC_MC, "UMC%d chip selects:\n", ctrl); 138507ed82efSYazen Ghannam 1386d971e28eSYazen Ghannam for (dimm = 0; dimm < 2; dimm++) { 1387eb77e6b8SYazen Ghannam cs0 = dimm * 2; 1388eb77e6b8SYazen Ghannam cs1 = dimm * 2 + 1; 1389eb77e6b8SYazen Ghannam 1390e53a3b26SYazen Ghannam cs_mode = f17_get_cs_mode(dimm, ctrl, pvt); 1391e53a3b26SYazen Ghannam 1392e53a3b26SYazen Ghannam size0 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs0); 1393e53a3b26SYazen Ghannam size1 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs1); 139407ed82efSYazen Ghannam 139507ed82efSYazen Ghannam amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n", 1396eb77e6b8SYazen Ghannam cs0, size0, 1397eb77e6b8SYazen Ghannam cs1, size1); 139807ed82efSYazen Ghannam } 139907ed82efSYazen Ghannam } 140007ed82efSYazen Ghannam 140107ed82efSYazen Ghannam static void __dump_misc_regs_df(struct amd64_pvt *pvt) 140207ed82efSYazen Ghannam { 140307ed82efSYazen Ghannam struct amd64_umc *umc; 140407ed82efSYazen Ghannam u32 i, tmp, umc_base; 140507ed82efSYazen Ghannam 14064d30d2bcSYazen Ghannam for_each_umc(i) { 140707ed82efSYazen Ghannam umc_base = get_umc_base(i); 140807ed82efSYazen Ghannam umc = &pvt->umc[i]; 140907ed82efSYazen Ghannam 141007ed82efSYazen Ghannam edac_dbg(1, "UMC%d DIMM cfg: 0x%x\n", i, umc->dimm_cfg); 141107ed82efSYazen Ghannam edac_dbg(1, "UMC%d UMC cfg: 0x%x\n", i, umc->umc_cfg); 141207ed82efSYazen Ghannam edac_dbg(1, "UMC%d SDP ctrl: 0x%x\n", i, umc->sdp_ctrl); 141307ed82efSYazen Ghannam edac_dbg(1, "UMC%d ECC ctrl: 0x%x\n", i, umc->ecc_ctrl); 141407ed82efSYazen Ghannam 141507ed82efSYazen Ghannam amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ECC_BAD_SYMBOL, &tmp); 141607ed82efSYazen Ghannam edac_dbg(1, "UMC%d ECC bad symbol: 0x%x\n", i, tmp); 141707ed82efSYazen Ghannam 141807ed82efSYazen Ghannam amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_UMC_CAP, &tmp); 141907ed82efSYazen Ghannam edac_dbg(1, "UMC%d UMC cap: 0x%x\n", i, tmp); 142007ed82efSYazen Ghannam edac_dbg(1, "UMC%d UMC cap high: 0x%x\n", i, umc->umc_cap_hi); 142107ed82efSYazen Ghannam 142207ed82efSYazen Ghannam edac_dbg(1, "UMC%d ECC capable: %s, ChipKill ECC capable: %s\n", 142307ed82efSYazen Ghannam i, (umc->umc_cap_hi & BIT(30)) ? "yes" : "no", 142407ed82efSYazen Ghannam (umc->umc_cap_hi & BIT(31)) ? "yes" : "no"); 142507ed82efSYazen Ghannam edac_dbg(1, "UMC%d All DIMMs support ECC: %s\n", 142607ed82efSYazen Ghannam i, (umc->umc_cfg & BIT(12)) ? "yes" : "no"); 142707ed82efSYazen Ghannam edac_dbg(1, "UMC%d x4 DIMMs present: %s\n", 142807ed82efSYazen Ghannam i, (umc->dimm_cfg & BIT(6)) ? "yes" : "no"); 142907ed82efSYazen Ghannam edac_dbg(1, "UMC%d x16 DIMMs present: %s\n", 143007ed82efSYazen Ghannam i, (umc->dimm_cfg & BIT(7)) ? "yes" : "no"); 143107ed82efSYazen Ghannam 143207ed82efSYazen Ghannam if (pvt->dram_type == MEM_LRDDR4) { 143307ed82efSYazen Ghannam amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ADDR_CFG, &tmp); 143407ed82efSYazen Ghannam edac_dbg(1, "UMC%d LRDIMM %dx rank multiply\n", 143507ed82efSYazen Ghannam i, 1 << ((tmp >> 4) & 0x3)); 143607ed82efSYazen Ghannam } 143707ed82efSYazen Ghannam 143807ed82efSYazen Ghannam debug_display_dimm_sizes_df(pvt, i); 143907ed82efSYazen Ghannam } 144007ed82efSYazen Ghannam 144107ed82efSYazen Ghannam edac_dbg(1, "F0x104 (DRAM Hole Address): 0x%08x, base: 0x%08x\n", 144207ed82efSYazen Ghannam pvt->dhar, dhar_base(pvt)); 144307ed82efSYazen Ghannam } 144407ed82efSYazen Ghannam 14452da11654SDoug Thompson /* Display and decode various NB registers for debug purposes. */ 144607ed82efSYazen Ghannam static void __dump_misc_regs(struct amd64_pvt *pvt) 14472da11654SDoug Thompson { 1448956b9ba1SJoe Perches edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap); 14492da11654SDoug Thompson 1450956b9ba1SJoe Perches edac_dbg(1, " NB two channel DRAM capable: %s\n", 14515980bb9cSBorislav Petkov (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no"); 145268798e17SBorislav Petkov 1453956b9ba1SJoe Perches edac_dbg(1, " ECC capable: %s, ChipKill ECC capable: %s\n", 14545980bb9cSBorislav Petkov (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no", 14555980bb9cSBorislav Petkov (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no"); 145668798e17SBorislav Petkov 1457d1ea71cdSBorislav Petkov debug_dump_dramcfg_low(pvt, pvt->dclr0, 0); 14582da11654SDoug Thompson 1459956b9ba1SJoe Perches edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare); 14602da11654SDoug Thompson 1461956b9ba1SJoe Perches edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n", 1462bc21fa57SBorislav Petkov pvt->dhar, dhar_base(pvt), 1463a4b4bedcSBorislav Petkov (pvt->fam == 0xf) ? k8_dhar_offset(pvt) 1464bc21fa57SBorislav Petkov : f10_dhar_offset(pvt)); 14652da11654SDoug Thompson 1466d1ea71cdSBorislav Petkov debug_display_dimm_sizes(pvt, 0); 14674d796364SBorislav Petkov 14684d796364SBorislav Petkov /* everything below this point is Fam10h and above */ 1469a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) 14702da11654SDoug Thompson return; 14714d796364SBorislav Petkov 1472d1ea71cdSBorislav Petkov debug_display_dimm_sizes(pvt, 1); 14732da11654SDoug Thompson 14748de1d91eSBorislav Petkov /* Only if NOT ganged does dclr1 have valid info */ 147568798e17SBorislav Petkov if (!dct_ganging_enabled(pvt)) 1476d1ea71cdSBorislav Petkov debug_dump_dramcfg_low(pvt, pvt->dclr1, 1); 14772da11654SDoug Thompson } 14782da11654SDoug Thompson 147907ed82efSYazen Ghannam /* Display and decode various NB registers for debug purposes. */ 148007ed82efSYazen Ghannam static void dump_misc_regs(struct amd64_pvt *pvt) 148107ed82efSYazen Ghannam { 148207ed82efSYazen Ghannam if (pvt->umc) 148307ed82efSYazen Ghannam __dump_misc_regs_df(pvt); 148407ed82efSYazen Ghannam else 148507ed82efSYazen Ghannam __dump_misc_regs(pvt); 148607ed82efSYazen Ghannam 148707ed82efSYazen Ghannam edac_dbg(1, " DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no"); 148807ed82efSYazen Ghannam 14897835961dSYazen Ghannam amd64_info("using x%u syndromes.\n", pvt->ecc_sym_sz); 149007ed82efSYazen Ghannam } 149107ed82efSYazen Ghannam 149294be4bffSDoug Thompson /* 149318b94f66SAravind Gopalakrishnan * See BKDG, F2x[1,0][5C:40], F2[1,0][6C:60] 149494be4bffSDoug Thompson */ 149511c75eadSBorislav Petkov static void prep_chip_selects(struct amd64_pvt *pvt) 149694be4bffSDoug Thompson { 149718b94f66SAravind Gopalakrishnan if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) { 149811c75eadSBorislav Petkov pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8; 149911c75eadSBorislav Petkov pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8; 1500a597d2a5SAravind Gopalakrishnan } else if (pvt->fam == 0x15 && pvt->model == 0x30) { 150118b94f66SAravind Gopalakrishnan pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4; 150218b94f66SAravind Gopalakrishnan pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2; 1503d971e28eSYazen Ghannam } else if (pvt->fam >= 0x17) { 1504d971e28eSYazen Ghannam int umc; 1505d971e28eSYazen Ghannam 1506d971e28eSYazen Ghannam for_each_umc(umc) { 1507d971e28eSYazen Ghannam pvt->csels[umc].b_cnt = 4; 1508d971e28eSYazen Ghannam pvt->csels[umc].m_cnt = 2; 1509d971e28eSYazen Ghannam } 1510d971e28eSYazen Ghannam 15119d858bb1SBorislav Petkov } else { 151211c75eadSBorislav Petkov pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8; 151311c75eadSBorislav Petkov pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4; 15149d858bb1SBorislav Petkov } 151594be4bffSDoug Thompson } 151694be4bffSDoug Thompson 1517d971e28eSYazen Ghannam static void read_umc_base_mask(struct amd64_pvt *pvt) 1518d971e28eSYazen Ghannam { 15197574729eSYazen Ghannam u32 umc_base_reg, umc_base_reg_sec; 15207574729eSYazen Ghannam u32 umc_mask_reg, umc_mask_reg_sec; 15217574729eSYazen Ghannam u32 base_reg, base_reg_sec; 15227574729eSYazen Ghannam u32 mask_reg, mask_reg_sec; 15237574729eSYazen Ghannam u32 *base, *base_sec; 15247574729eSYazen Ghannam u32 *mask, *mask_sec; 1525d971e28eSYazen Ghannam int cs, umc; 1526d971e28eSYazen Ghannam 1527d971e28eSYazen Ghannam for_each_umc(umc) { 1528d971e28eSYazen Ghannam umc_base_reg = get_umc_base(umc) + UMCCH_BASE_ADDR; 15297574729eSYazen Ghannam umc_base_reg_sec = get_umc_base(umc) + UMCCH_BASE_ADDR_SEC; 1530d971e28eSYazen Ghannam 1531d971e28eSYazen Ghannam for_each_chip_select(cs, umc, pvt) { 1532d971e28eSYazen Ghannam base = &pvt->csels[umc].csbases[cs]; 15337574729eSYazen Ghannam base_sec = &pvt->csels[umc].csbases_sec[cs]; 1534d971e28eSYazen Ghannam 1535d971e28eSYazen Ghannam base_reg = umc_base_reg + (cs * 4); 15367574729eSYazen Ghannam base_reg_sec = umc_base_reg_sec + (cs * 4); 1537d971e28eSYazen Ghannam 1538d971e28eSYazen Ghannam if (!amd_smn_read(pvt->mc_node_id, base_reg, base)) 1539d971e28eSYazen Ghannam edac_dbg(0, " DCSB%d[%d]=0x%08x reg: 0x%x\n", 1540d971e28eSYazen Ghannam umc, cs, *base, base_reg); 15417574729eSYazen Ghannam 15427574729eSYazen Ghannam if (!amd_smn_read(pvt->mc_node_id, base_reg_sec, base_sec)) 15437574729eSYazen Ghannam edac_dbg(0, " DCSB_SEC%d[%d]=0x%08x reg: 0x%x\n", 15447574729eSYazen Ghannam umc, cs, *base_sec, base_reg_sec); 1545d971e28eSYazen Ghannam } 1546d971e28eSYazen Ghannam 1547d971e28eSYazen Ghannam umc_mask_reg = get_umc_base(umc) + UMCCH_ADDR_MASK; 15487574729eSYazen Ghannam umc_mask_reg_sec = get_umc_base(umc) + UMCCH_ADDR_MASK_SEC; 1549d971e28eSYazen Ghannam 1550d971e28eSYazen Ghannam for_each_chip_select_mask(cs, umc, pvt) { 1551d971e28eSYazen Ghannam mask = &pvt->csels[umc].csmasks[cs]; 15527574729eSYazen Ghannam mask_sec = &pvt->csels[umc].csmasks_sec[cs]; 1553d971e28eSYazen Ghannam 1554d971e28eSYazen Ghannam mask_reg = umc_mask_reg + (cs * 4); 15557574729eSYazen Ghannam mask_reg_sec = umc_mask_reg_sec + (cs * 4); 1556d971e28eSYazen Ghannam 1557d971e28eSYazen Ghannam if (!amd_smn_read(pvt->mc_node_id, mask_reg, mask)) 1558d971e28eSYazen Ghannam edac_dbg(0, " DCSM%d[%d]=0x%08x reg: 0x%x\n", 1559d971e28eSYazen Ghannam umc, cs, *mask, mask_reg); 15607574729eSYazen Ghannam 15617574729eSYazen Ghannam if (!amd_smn_read(pvt->mc_node_id, mask_reg_sec, mask_sec)) 15627574729eSYazen Ghannam edac_dbg(0, " DCSM_SEC%d[%d]=0x%08x reg: 0x%x\n", 15637574729eSYazen Ghannam umc, cs, *mask_sec, mask_reg_sec); 1564d971e28eSYazen Ghannam } 1565d971e28eSYazen Ghannam } 1566d971e28eSYazen Ghannam } 1567d971e28eSYazen Ghannam 156894be4bffSDoug Thompson /* 156911c75eadSBorislav Petkov * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers 157094be4bffSDoug Thompson */ 1571b2b0c605SBorislav Petkov static void read_dct_base_mask(struct amd64_pvt *pvt) 157294be4bffSDoug Thompson { 1573d971e28eSYazen Ghannam int cs; 157494be4bffSDoug Thompson 157511c75eadSBorislav Petkov prep_chip_selects(pvt); 157694be4bffSDoug Thompson 1577d971e28eSYazen Ghannam if (pvt->umc) 1578d971e28eSYazen Ghannam return read_umc_base_mask(pvt); 1579b64ce7cdSYazen Ghannam 158011c75eadSBorislav Petkov for_each_chip_select(cs, 0, pvt) { 1581d971e28eSYazen Ghannam int reg0 = DCSB0 + (cs * 4); 1582d971e28eSYazen Ghannam int reg1 = DCSB1 + (cs * 4); 158311c75eadSBorislav Petkov u32 *base0 = &pvt->csels[0].csbases[cs]; 158411c75eadSBorislav Petkov u32 *base1 = &pvt->csels[1].csbases[cs]; 1585b2b0c605SBorislav Petkov 15867981a28fSAravind Gopalakrishnan if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0)) 1587956b9ba1SJoe Perches edac_dbg(0, " DCSB0[%d]=0x%08x reg: F2x%x\n", 158811c75eadSBorislav Petkov cs, *base0, reg0); 158994be4bffSDoug Thompson 15907981a28fSAravind Gopalakrishnan if (pvt->fam == 0xf) 159111c75eadSBorislav Petkov continue; 1592b2b0c605SBorislav Petkov 15937981a28fSAravind Gopalakrishnan if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1)) 1594956b9ba1SJoe Perches edac_dbg(0, " DCSB1[%d]=0x%08x reg: F2x%x\n", 15957981a28fSAravind Gopalakrishnan cs, *base1, (pvt->fam == 0x10) ? reg1 15967981a28fSAravind Gopalakrishnan : reg0); 159794be4bffSDoug Thompson } 159894be4bffSDoug Thompson 159911c75eadSBorislav Petkov for_each_chip_select_mask(cs, 0, pvt) { 1600d971e28eSYazen Ghannam int reg0 = DCSM0 + (cs * 4); 1601d971e28eSYazen Ghannam int reg1 = DCSM1 + (cs * 4); 160211c75eadSBorislav Petkov u32 *mask0 = &pvt->csels[0].csmasks[cs]; 160311c75eadSBorislav Petkov u32 *mask1 = &pvt->csels[1].csmasks[cs]; 1604b2b0c605SBorislav Petkov 16057981a28fSAravind Gopalakrishnan if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0)) 1606956b9ba1SJoe Perches edac_dbg(0, " DCSM0[%d]=0x%08x reg: F2x%x\n", 160711c75eadSBorislav Petkov cs, *mask0, reg0); 160894be4bffSDoug Thompson 16097981a28fSAravind Gopalakrishnan if (pvt->fam == 0xf) 161011c75eadSBorislav Petkov continue; 1611b2b0c605SBorislav Petkov 16127981a28fSAravind Gopalakrishnan if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1)) 1613956b9ba1SJoe Perches edac_dbg(0, " DCSM1[%d]=0x%08x reg: F2x%x\n", 16147981a28fSAravind Gopalakrishnan cs, *mask1, (pvt->fam == 0x10) ? reg1 16157981a28fSAravind Gopalakrishnan : reg0); 161694be4bffSDoug Thompson } 16176ba5dcdcSBorislav Petkov } 161894be4bffSDoug Thompson 1619a597d2a5SAravind Gopalakrishnan static void determine_memory_type(struct amd64_pvt *pvt) 162094be4bffSDoug Thompson { 1621a597d2a5SAravind Gopalakrishnan u32 dram_ctrl, dcsm; 162294be4bffSDoug Thompson 1623dcd01394SYazen Ghannam if (pvt->umc) { 1624dcd01394SYazen Ghannam if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(5)) 1625dcd01394SYazen Ghannam pvt->dram_type = MEM_LRDDR4; 1626dcd01394SYazen Ghannam else if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(4)) 1627dcd01394SYazen Ghannam pvt->dram_type = MEM_RDDR4; 1628dcd01394SYazen Ghannam else 1629dcd01394SYazen Ghannam pvt->dram_type = MEM_DDR4; 1630dcd01394SYazen Ghannam return; 1631dcd01394SYazen Ghannam } 1632dcd01394SYazen Ghannam 1633a597d2a5SAravind Gopalakrishnan switch (pvt->fam) { 1634a597d2a5SAravind Gopalakrishnan case 0xf: 1635a597d2a5SAravind Gopalakrishnan if (pvt->ext_model >= K8_REV_F) 1636a597d2a5SAravind Gopalakrishnan goto ddr3; 1637a597d2a5SAravind Gopalakrishnan 1638a597d2a5SAravind Gopalakrishnan pvt->dram_type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR; 1639a597d2a5SAravind Gopalakrishnan return; 1640a597d2a5SAravind Gopalakrishnan 1641a597d2a5SAravind Gopalakrishnan case 0x10: 16426b4c0bdeSBorislav Petkov if (pvt->dchr0 & DDR3_MODE) 1643a597d2a5SAravind Gopalakrishnan goto ddr3; 1644a597d2a5SAravind Gopalakrishnan 1645a597d2a5SAravind Gopalakrishnan pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2; 1646a597d2a5SAravind Gopalakrishnan return; 1647a597d2a5SAravind Gopalakrishnan 1648a597d2a5SAravind Gopalakrishnan case 0x15: 1649a597d2a5SAravind Gopalakrishnan if (pvt->model < 0x60) 1650a597d2a5SAravind Gopalakrishnan goto ddr3; 1651a597d2a5SAravind Gopalakrishnan 1652a597d2a5SAravind Gopalakrishnan /* 1653a597d2a5SAravind Gopalakrishnan * Model 0x60h needs special handling: 1654a597d2a5SAravind Gopalakrishnan * 1655a597d2a5SAravind Gopalakrishnan * We use a Chip Select value of '0' to obtain dcsm. 1656a597d2a5SAravind Gopalakrishnan * Theoretically, it is possible to populate LRDIMMs of different 1657a597d2a5SAravind Gopalakrishnan * 'Rank' value on a DCT. But this is not the common case. So, 1658a597d2a5SAravind Gopalakrishnan * it's reasonable to assume all DIMMs are going to be of same 1659a597d2a5SAravind Gopalakrishnan * 'type' until proven otherwise. 1660a597d2a5SAravind Gopalakrishnan */ 1661a597d2a5SAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DRAM_CONTROL, &dram_ctrl); 1662a597d2a5SAravind Gopalakrishnan dcsm = pvt->csels[0].csmasks[0]; 1663a597d2a5SAravind Gopalakrishnan 1664a597d2a5SAravind Gopalakrishnan if (((dram_ctrl >> 8) & 0x7) == 0x2) 1665a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_DDR4; 1666a597d2a5SAravind Gopalakrishnan else if (pvt->dclr0 & BIT(16)) 1667a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_DDR3; 1668a597d2a5SAravind Gopalakrishnan else if (dcsm & 0x3) 1669a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_LRDDR3; 16706b4c0bdeSBorislav Petkov else 1671a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_RDDR3; 1672a597d2a5SAravind Gopalakrishnan 1673a597d2a5SAravind Gopalakrishnan return; 1674a597d2a5SAravind Gopalakrishnan 1675a597d2a5SAravind Gopalakrishnan case 0x16: 1676a597d2a5SAravind Gopalakrishnan goto ddr3; 1677a597d2a5SAravind Gopalakrishnan 1678a597d2a5SAravind Gopalakrishnan default: 1679a597d2a5SAravind Gopalakrishnan WARN(1, KERN_ERR "%s: Family??? 0x%x\n", __func__, pvt->fam); 1680a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_EMPTY; 168194be4bffSDoug Thompson } 1682a597d2a5SAravind Gopalakrishnan return; 168394be4bffSDoug Thompson 1684a597d2a5SAravind Gopalakrishnan ddr3: 1685a597d2a5SAravind Gopalakrishnan pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3; 168694be4bffSDoug Thompson } 168794be4bffSDoug Thompson 1688cb328507SBorislav Petkov /* Get the number of DCT channels the memory controller is using. */ 1689ddff876dSDoug Thompson static int k8_early_channel_count(struct amd64_pvt *pvt) 1690ddff876dSDoug Thompson { 1691cb328507SBorislav Petkov int flag; 1692ddff876dSDoug Thompson 16939f56da0eSBorislav Petkov if (pvt->ext_model >= K8_REV_F) 1694ddff876dSDoug Thompson /* RevF (NPT) and later */ 169541d8bfabSBorislav Petkov flag = pvt->dclr0 & WIDTH_128; 16969f56da0eSBorislav Petkov else 1697ddff876dSDoug Thompson /* RevE and earlier */ 1698ddff876dSDoug Thompson flag = pvt->dclr0 & REVE_WIDTH_128; 1699ddff876dSDoug Thompson 1700ddff876dSDoug Thompson /* not used */ 1701ddff876dSDoug Thompson pvt->dclr1 = 0; 1702ddff876dSDoug Thompson 1703ddff876dSDoug Thompson return (flag) ? 2 : 1; 1704ddff876dSDoug Thompson } 1705ddff876dSDoug Thompson 170670046624SBorislav Petkov /* On F10h and later ErrAddr is MC4_ADDR[47:1] */ 1707a4b4bedcSBorislav Petkov static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m) 1708ddff876dSDoug Thompson { 1709db970bd2SYazen Ghannam u16 mce_nid = topology_die_id(m->extcpu); 17102ec591acSBorislav Petkov struct mem_ctl_info *mci; 171170046624SBorislav Petkov u8 start_bit = 1; 171270046624SBorislav Petkov u8 end_bit = 47; 17132ec591acSBorislav Petkov u64 addr; 17142ec591acSBorislav Petkov 17152ec591acSBorislav Petkov mci = edac_mc_find(mce_nid); 17162ec591acSBorislav Petkov if (!mci) 17172ec591acSBorislav Petkov return 0; 17182ec591acSBorislav Petkov 17192ec591acSBorislav Petkov pvt = mci->pvt_info; 172070046624SBorislav Petkov 1721a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) { 172270046624SBorislav Petkov start_bit = 3; 172370046624SBorislav Petkov end_bit = 39; 172470046624SBorislav Petkov } 172570046624SBorislav Petkov 172610ef6b0dSChen, Gong addr = m->addr & GENMASK_ULL(end_bit, start_bit); 1727c1ae6830SBorislav Petkov 1728c1ae6830SBorislav Petkov /* 1729c1ae6830SBorislav Petkov * Erratum 637 workaround 1730c1ae6830SBorislav Petkov */ 1731a4b4bedcSBorislav Petkov if (pvt->fam == 0x15) { 1732c1ae6830SBorislav Petkov u64 cc6_base, tmp_addr; 1733c1ae6830SBorislav Petkov u32 tmp; 17348b84c8dfSDaniel J Blueman u8 intlv_en; 1735c1ae6830SBorislav Petkov 173610ef6b0dSChen, Gong if ((addr & GENMASK_ULL(47, 24)) >> 24 != 0x00fdf7) 1737c1ae6830SBorislav Petkov return addr; 1738c1ae6830SBorislav Petkov 1739c1ae6830SBorislav Petkov 1740c1ae6830SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp); 1741c1ae6830SBorislav Petkov intlv_en = tmp >> 21 & 0x7; 1742c1ae6830SBorislav Petkov 1743c1ae6830SBorislav Petkov /* add [47:27] + 3 trailing bits */ 174410ef6b0dSChen, Gong cc6_base = (tmp & GENMASK_ULL(20, 0)) << 3; 1745c1ae6830SBorislav Petkov 1746c1ae6830SBorislav Petkov /* reverse and add DramIntlvEn */ 1747c1ae6830SBorislav Petkov cc6_base |= intlv_en ^ 0x7; 1748c1ae6830SBorislav Petkov 1749c1ae6830SBorislav Petkov /* pin at [47:24] */ 1750c1ae6830SBorislav Petkov cc6_base <<= 24; 1751c1ae6830SBorislav Petkov 1752c1ae6830SBorislav Petkov if (!intlv_en) 175310ef6b0dSChen, Gong return cc6_base | (addr & GENMASK_ULL(23, 0)); 1754c1ae6830SBorislav Petkov 1755c1ae6830SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp); 1756c1ae6830SBorislav Petkov 1757c1ae6830SBorislav Petkov /* faster log2 */ 175810ef6b0dSChen, Gong tmp_addr = (addr & GENMASK_ULL(23, 12)) << __fls(intlv_en + 1); 1759c1ae6830SBorislav Petkov 1760c1ae6830SBorislav Petkov /* OR DramIntlvSel into bits [14:12] */ 176110ef6b0dSChen, Gong tmp_addr |= (tmp & GENMASK_ULL(23, 21)) >> 9; 1762c1ae6830SBorislav Petkov 1763c1ae6830SBorislav Petkov /* add remaining [11:0] bits from original MC4_ADDR */ 176410ef6b0dSChen, Gong tmp_addr |= addr & GENMASK_ULL(11, 0); 1765c1ae6830SBorislav Petkov 1766c1ae6830SBorislav Petkov return cc6_base | tmp_addr; 1767c1ae6830SBorislav Petkov } 1768c1ae6830SBorislav Petkov 1769c1ae6830SBorislav Petkov return addr; 1770ddff876dSDoug Thompson } 1771ddff876dSDoug Thompson 1772e2c0bffeSDaniel J Blueman static struct pci_dev *pci_get_related_function(unsigned int vendor, 1773e2c0bffeSDaniel J Blueman unsigned int device, 1774e2c0bffeSDaniel J Blueman struct pci_dev *related) 1775e2c0bffeSDaniel J Blueman { 1776e2c0bffeSDaniel J Blueman struct pci_dev *dev = NULL; 1777e2c0bffeSDaniel J Blueman 1778e2c0bffeSDaniel J Blueman while ((dev = pci_get_device(vendor, device, dev))) { 1779e2c0bffeSDaniel J Blueman if (pci_domain_nr(dev->bus) == pci_domain_nr(related->bus) && 1780e2c0bffeSDaniel J Blueman (dev->bus->number == related->bus->number) && 1781e2c0bffeSDaniel J Blueman (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn))) 1782e2c0bffeSDaniel J Blueman break; 1783e2c0bffeSDaniel J Blueman } 1784e2c0bffeSDaniel J Blueman 1785e2c0bffeSDaniel J Blueman return dev; 1786e2c0bffeSDaniel J Blueman } 1787e2c0bffeSDaniel J Blueman 17887f19bf75SBorislav Petkov static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range) 1789ddff876dSDoug Thompson { 1790e2c0bffeSDaniel J Blueman struct amd_northbridge *nb; 179118b94f66SAravind Gopalakrishnan struct pci_dev *f1 = NULL; 179218b94f66SAravind Gopalakrishnan unsigned int pci_func; 179371d2a32eSBorislav Petkov int off = range << 3; 1794e2c0bffeSDaniel J Blueman u32 llim; 1795ddff876dSDoug Thompson 17967f19bf75SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off, &pvt->ranges[range].base.lo); 17977f19bf75SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo); 1798ddff876dSDoug Thompson 179918b94f66SAravind Gopalakrishnan if (pvt->fam == 0xf) 18007f19bf75SBorislav Petkov return; 1801ddff876dSDoug Thompson 18027f19bf75SBorislav Petkov if (!dram_rw(pvt, range)) 18037f19bf75SBorislav Petkov return; 1804ddff876dSDoug Thompson 18057f19bf75SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off, &pvt->ranges[range].base.hi); 18067f19bf75SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi); 1807f08e457cSBorislav Petkov 1808e2c0bffeSDaniel J Blueman /* F15h: factor in CC6 save area by reading dst node's limit reg */ 180918b94f66SAravind Gopalakrishnan if (pvt->fam != 0x15) 1810e2c0bffeSDaniel J Blueman return; 1811f08e457cSBorislav Petkov 1812e2c0bffeSDaniel J Blueman nb = node_to_amd_nb(dram_dst_node(pvt, range)); 1813e2c0bffeSDaniel J Blueman if (WARN_ON(!nb)) 1814e2c0bffeSDaniel J Blueman return; 1815e2c0bffeSDaniel J Blueman 1816a597d2a5SAravind Gopalakrishnan if (pvt->model == 0x60) 1817a597d2a5SAravind Gopalakrishnan pci_func = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1; 1818a597d2a5SAravind Gopalakrishnan else if (pvt->model == 0x30) 1819a597d2a5SAravind Gopalakrishnan pci_func = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1; 1820a597d2a5SAravind Gopalakrishnan else 1821a597d2a5SAravind Gopalakrishnan pci_func = PCI_DEVICE_ID_AMD_15H_NB_F1; 182218b94f66SAravind Gopalakrishnan 182318b94f66SAravind Gopalakrishnan f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc); 1824f08e457cSBorislav Petkov if (WARN_ON(!f1)) 1825f08e457cSBorislav Petkov return; 1826f08e457cSBorislav Petkov 1827f08e457cSBorislav Petkov amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim); 1828f08e457cSBorislav Petkov 182910ef6b0dSChen, Gong pvt->ranges[range].lim.lo &= GENMASK_ULL(15, 0); 1830f08e457cSBorislav Petkov 1831f08e457cSBorislav Petkov /* {[39:27],111b} */ 1832f08e457cSBorislav Petkov pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16; 1833f08e457cSBorislav Petkov 183410ef6b0dSChen, Gong pvt->ranges[range].lim.hi &= GENMASK_ULL(7, 0); 1835f08e457cSBorislav Petkov 1836f08e457cSBorislav Petkov /* [47:40] */ 1837f08e457cSBorislav Petkov pvt->ranges[range].lim.hi |= llim >> 13; 1838f08e457cSBorislav Petkov 1839f08e457cSBorislav Petkov pci_dev_put(f1); 1840f08e457cSBorislav Petkov } 1841ddff876dSDoug Thompson 1842f192c7b1SBorislav Petkov static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr, 184333ca0643SBorislav Petkov struct err_info *err) 1844ddff876dSDoug Thompson { 1845f192c7b1SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 1846ddff876dSDoug Thompson 184733ca0643SBorislav Petkov error_address_to_page_and_offset(sys_addr, err); 1848ab5a503cSMauro Carvalho Chehab 1849ab5a503cSMauro Carvalho Chehab /* 1850ab5a503cSMauro Carvalho Chehab * Find out which node the error address belongs to. This may be 1851ab5a503cSMauro Carvalho Chehab * different from the node that detected the error. 1852ab5a503cSMauro Carvalho Chehab */ 185333ca0643SBorislav Petkov err->src_mci = find_mc_by_sys_addr(mci, sys_addr); 185433ca0643SBorislav Petkov if (!err->src_mci) { 1855ab5a503cSMauro Carvalho Chehab amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n", 1856ab5a503cSMauro Carvalho Chehab (unsigned long)sys_addr); 185733ca0643SBorislav Petkov err->err_code = ERR_NODE; 1858ab5a503cSMauro Carvalho Chehab return; 1859ab5a503cSMauro Carvalho Chehab } 1860ab5a503cSMauro Carvalho Chehab 1861ab5a503cSMauro Carvalho Chehab /* Now map the sys_addr to a CSROW */ 186233ca0643SBorislav Petkov err->csrow = sys_addr_to_csrow(err->src_mci, sys_addr); 186333ca0643SBorislav Petkov if (err->csrow < 0) { 186433ca0643SBorislav Petkov err->err_code = ERR_CSROW; 1865ab5a503cSMauro Carvalho Chehab return; 1866ab5a503cSMauro Carvalho Chehab } 1867ab5a503cSMauro Carvalho Chehab 1868ddff876dSDoug Thompson /* CHIPKILL enabled */ 1869f192c7b1SBorislav Petkov if (pvt->nbcfg & NBCFG_CHIPKILL) { 187033ca0643SBorislav Petkov err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome); 187133ca0643SBorislav Petkov if (err->channel < 0) { 1872ddff876dSDoug Thompson /* 1873ddff876dSDoug Thompson * Syndrome didn't map, so we don't know which of the 1874ddff876dSDoug Thompson * 2 DIMMs is in error. So we need to ID 'both' of them 1875ddff876dSDoug Thompson * as suspect. 1876ddff876dSDoug Thompson */ 187733ca0643SBorislav Petkov amd64_mc_warn(err->src_mci, "unknown syndrome 0x%04x - " 1878ab5a503cSMauro Carvalho Chehab "possible error reporting race\n", 187933ca0643SBorislav Petkov err->syndrome); 188033ca0643SBorislav Petkov err->err_code = ERR_CHANNEL; 1881ddff876dSDoug Thompson return; 1882ddff876dSDoug Thompson } 1883ddff876dSDoug Thompson } else { 1884ddff876dSDoug Thompson /* 1885ddff876dSDoug Thompson * non-chipkill ecc mode 1886ddff876dSDoug Thompson * 1887ddff876dSDoug Thompson * The k8 documentation is unclear about how to determine the 1888ddff876dSDoug Thompson * channel number when using non-chipkill memory. This method 1889ddff876dSDoug Thompson * was obtained from email communication with someone at AMD. 1890ddff876dSDoug Thompson * (Wish the email was placed in this comment - norsk) 1891ddff876dSDoug Thompson */ 189233ca0643SBorislav Petkov err->channel = ((sys_addr & BIT(3)) != 0); 1893ddff876dSDoug Thompson } 1894ddff876dSDoug Thompson } 1895ddff876dSDoug Thompson 189641d8bfabSBorislav Petkov static int ddr2_cs_size(unsigned i, bool dct_width) 1897ddff876dSDoug Thompson { 189841d8bfabSBorislav Petkov unsigned shift = 0; 1899ddff876dSDoug Thompson 190041d8bfabSBorislav Petkov if (i <= 2) 190141d8bfabSBorislav Petkov shift = i; 190241d8bfabSBorislav Petkov else if (!(i & 0x1)) 190341d8bfabSBorislav Petkov shift = i >> 1; 19041433eb99SBorislav Petkov else 190541d8bfabSBorislav Petkov shift = (i + 1) >> 1; 1906ddff876dSDoug Thompson 190741d8bfabSBorislav Petkov return 128 << (shift + !!dct_width); 190841d8bfabSBorislav Petkov } 190941d8bfabSBorislav Petkov 191041d8bfabSBorislav Petkov static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 1911a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 191241d8bfabSBorislav Petkov { 191341d8bfabSBorislav Petkov u32 dclr = dct ? pvt->dclr1 : pvt->dclr0; 191441d8bfabSBorislav Petkov 191541d8bfabSBorislav Petkov if (pvt->ext_model >= K8_REV_F) { 191641d8bfabSBorislav Petkov WARN_ON(cs_mode > 11); 191741d8bfabSBorislav Petkov return ddr2_cs_size(cs_mode, dclr & WIDTH_128); 191841d8bfabSBorislav Petkov } 191941d8bfabSBorislav Petkov else if (pvt->ext_model >= K8_REV_D) { 192011b0a314SBorislav Petkov unsigned diff; 192141d8bfabSBorislav Petkov WARN_ON(cs_mode > 10); 192241d8bfabSBorislav Petkov 192311b0a314SBorislav Petkov /* 192411b0a314SBorislav Petkov * the below calculation, besides trying to win an obfuscated C 192511b0a314SBorislav Petkov * contest, maps cs_mode values to DIMM chip select sizes. The 192611b0a314SBorislav Petkov * mappings are: 192711b0a314SBorislav Petkov * 192811b0a314SBorislav Petkov * cs_mode CS size (mb) 192911b0a314SBorislav Petkov * ======= ============ 193011b0a314SBorislav Petkov * 0 32 193111b0a314SBorislav Petkov * 1 64 193211b0a314SBorislav Petkov * 2 128 193311b0a314SBorislav Petkov * 3 128 193411b0a314SBorislav Petkov * 4 256 193511b0a314SBorislav Petkov * 5 512 193611b0a314SBorislav Petkov * 6 256 193711b0a314SBorislav Petkov * 7 512 193811b0a314SBorislav Petkov * 8 1024 193911b0a314SBorislav Petkov * 9 1024 194011b0a314SBorislav Petkov * 10 2048 194111b0a314SBorislav Petkov * 194211b0a314SBorislav Petkov * Basically, it calculates a value with which to shift the 194311b0a314SBorislav Petkov * smallest CS size of 32MB. 194411b0a314SBorislav Petkov * 194511b0a314SBorislav Petkov * ddr[23]_cs_size have a similar purpose. 194611b0a314SBorislav Petkov */ 194711b0a314SBorislav Petkov diff = cs_mode/3 + (unsigned)(cs_mode > 5); 194811b0a314SBorislav Petkov 194911b0a314SBorislav Petkov return 32 << (cs_mode - diff); 195041d8bfabSBorislav Petkov } 195141d8bfabSBorislav Petkov else { 195241d8bfabSBorislav Petkov WARN_ON(cs_mode > 6); 195341d8bfabSBorislav Petkov return 32 << cs_mode; 195441d8bfabSBorislav Petkov } 1955ddff876dSDoug Thompson } 1956ddff876dSDoug Thompson 19571afd3c98SDoug Thompson /* 19581afd3c98SDoug Thompson * Get the number of DCT channels in use. 19591afd3c98SDoug Thompson * 19601afd3c98SDoug Thompson * Return: 19611afd3c98SDoug Thompson * number of Memory Channels in operation 19621afd3c98SDoug Thompson * Pass back: 19631afd3c98SDoug Thompson * contents of the DCL0_LOW register 19641afd3c98SDoug Thompson */ 19657d20d14dSBorislav Petkov static int f1x_early_channel_count(struct amd64_pvt *pvt) 19661afd3c98SDoug Thompson { 19676ba5dcdcSBorislav Petkov int i, j, channels = 0; 1968ddff876dSDoug Thompson 19697d20d14dSBorislav Petkov /* On F10h, if we are in 128 bit mode, then we are using 2 channels */ 1970a4b4bedcSBorislav Petkov if (pvt->fam == 0x10 && (pvt->dclr0 & WIDTH_128)) 19717d20d14dSBorislav Petkov return 2; 19721afd3c98SDoug Thompson 19731afd3c98SDoug Thompson /* 1974d16149e8SBorislav Petkov * Need to check if in unganged mode: In such, there are 2 channels, 1975d16149e8SBorislav Petkov * but they are not in 128 bit mode and thus the above 'dclr0' status 1976d16149e8SBorislav Petkov * bit will be OFF. 19771afd3c98SDoug Thompson * 19781afd3c98SDoug Thompson * Need to check DCT0[0] and DCT1[0] to see if only one of them has 19791afd3c98SDoug Thompson * their CSEnable bit on. If so, then SINGLE DIMM case. 19801afd3c98SDoug Thompson */ 1981956b9ba1SJoe Perches edac_dbg(0, "Data width is not 128 bits - need more decoding\n"); 19821afd3c98SDoug Thompson 19831afd3c98SDoug Thompson /* 19841afd3c98SDoug Thompson * Check DRAM Bank Address Mapping values for each DIMM to see if there 19851afd3c98SDoug Thompson * is more than just one DIMM present in unganged mode. Need to check 19861afd3c98SDoug Thompson * both controllers since DIMMs can be placed in either one. 19871afd3c98SDoug Thompson */ 1988525a1b20SBorislav Petkov for (i = 0; i < 2; i++) { 1989525a1b20SBorislav Petkov u32 dbam = (i ? pvt->dbam1 : pvt->dbam0); 19901afd3c98SDoug Thompson 199157a30854SWan Wei for (j = 0; j < 4; j++) { 199257a30854SWan Wei if (DBAM_DIMM(j, dbam) > 0) { 19931afd3c98SDoug Thompson channels++; 199457a30854SWan Wei break; 19951afd3c98SDoug Thompson } 199657a30854SWan Wei } 199757a30854SWan Wei } 19981afd3c98SDoug Thompson 1999d16149e8SBorislav Petkov if (channels > 2) 2000d16149e8SBorislav Petkov channels = 2; 2001d16149e8SBorislav Petkov 200224f9a7feSBorislav Petkov amd64_info("MCT channel count: %d\n", channels); 20031afd3c98SDoug Thompson 20041afd3c98SDoug Thompson return channels; 20051afd3c98SDoug Thompson } 20061afd3c98SDoug Thompson 2007f1cbbec9SYazen Ghannam static int f17_early_channel_count(struct amd64_pvt *pvt) 2008f1cbbec9SYazen Ghannam { 2009f1cbbec9SYazen Ghannam int i, channels = 0; 2010f1cbbec9SYazen Ghannam 2011f1cbbec9SYazen Ghannam /* SDP Control bit 31 (SdpInit) is clear for unused UMC channels */ 20124d30d2bcSYazen Ghannam for_each_umc(i) 2013f1cbbec9SYazen Ghannam channels += !!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT); 2014f1cbbec9SYazen Ghannam 2015f1cbbec9SYazen Ghannam amd64_info("MCT channel count: %d\n", channels); 2016f1cbbec9SYazen Ghannam 2017f1cbbec9SYazen Ghannam return channels; 2018f1cbbec9SYazen Ghannam } 2019f1cbbec9SYazen Ghannam 202041d8bfabSBorislav Petkov static int ddr3_cs_size(unsigned i, bool dct_width) 20211afd3c98SDoug Thompson { 202241d8bfabSBorislav Petkov unsigned shift = 0; 202341d8bfabSBorislav Petkov int cs_size = 0; 202441d8bfabSBorislav Petkov 202541d8bfabSBorislav Petkov if (i == 0 || i == 3 || i == 4) 202641d8bfabSBorislav Petkov cs_size = -1; 202741d8bfabSBorislav Petkov else if (i <= 2) 202841d8bfabSBorislav Petkov shift = i; 202941d8bfabSBorislav Petkov else if (i == 12) 203041d8bfabSBorislav Petkov shift = 7; 203141d8bfabSBorislav Petkov else if (!(i & 0x1)) 203241d8bfabSBorislav Petkov shift = i >> 1; 203341d8bfabSBorislav Petkov else 203441d8bfabSBorislav Petkov shift = (i + 1) >> 1; 203541d8bfabSBorislav Petkov 203641d8bfabSBorislav Petkov if (cs_size != -1) 203741d8bfabSBorislav Petkov cs_size = (128 * (1 << !!dct_width)) << shift; 203841d8bfabSBorislav Petkov 203941d8bfabSBorislav Petkov return cs_size; 204041d8bfabSBorislav Petkov } 204141d8bfabSBorislav Petkov 2042a597d2a5SAravind Gopalakrishnan static int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply) 2043a597d2a5SAravind Gopalakrishnan { 2044a597d2a5SAravind Gopalakrishnan unsigned shift = 0; 2045a597d2a5SAravind Gopalakrishnan int cs_size = 0; 2046a597d2a5SAravind Gopalakrishnan 2047a597d2a5SAravind Gopalakrishnan if (i < 4 || i == 6) 2048a597d2a5SAravind Gopalakrishnan cs_size = -1; 2049a597d2a5SAravind Gopalakrishnan else if (i == 12) 2050a597d2a5SAravind Gopalakrishnan shift = 7; 2051a597d2a5SAravind Gopalakrishnan else if (!(i & 0x1)) 2052a597d2a5SAravind Gopalakrishnan shift = i >> 1; 2053a597d2a5SAravind Gopalakrishnan else 2054a597d2a5SAravind Gopalakrishnan shift = (i + 1) >> 1; 2055a597d2a5SAravind Gopalakrishnan 2056a597d2a5SAravind Gopalakrishnan if (cs_size != -1) 2057a597d2a5SAravind Gopalakrishnan cs_size = rank_multiply * (128 << shift); 2058a597d2a5SAravind Gopalakrishnan 2059a597d2a5SAravind Gopalakrishnan return cs_size; 2060a597d2a5SAravind Gopalakrishnan } 2061a597d2a5SAravind Gopalakrishnan 2062a597d2a5SAravind Gopalakrishnan static int ddr4_cs_size(unsigned i) 2063a597d2a5SAravind Gopalakrishnan { 2064a597d2a5SAravind Gopalakrishnan int cs_size = 0; 2065a597d2a5SAravind Gopalakrishnan 2066a597d2a5SAravind Gopalakrishnan if (i == 0) 2067a597d2a5SAravind Gopalakrishnan cs_size = -1; 2068a597d2a5SAravind Gopalakrishnan else if (i == 1) 2069a597d2a5SAravind Gopalakrishnan cs_size = 1024; 2070a597d2a5SAravind Gopalakrishnan else 2071a597d2a5SAravind Gopalakrishnan /* Min cs_size = 1G */ 2072a597d2a5SAravind Gopalakrishnan cs_size = 1024 * (1 << (i >> 1)); 2073a597d2a5SAravind Gopalakrishnan 2074a597d2a5SAravind Gopalakrishnan return cs_size; 2075a597d2a5SAravind Gopalakrishnan } 2076a597d2a5SAravind Gopalakrishnan 207741d8bfabSBorislav Petkov static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 2078a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 207941d8bfabSBorislav Petkov { 208041d8bfabSBorislav Petkov u32 dclr = dct ? pvt->dclr1 : pvt->dclr0; 208141d8bfabSBorislav Petkov 208241d8bfabSBorislav Petkov WARN_ON(cs_mode > 11); 20831433eb99SBorislav Petkov 20841433eb99SBorislav Petkov if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE) 208541d8bfabSBorislav Petkov return ddr3_cs_size(cs_mode, dclr & WIDTH_128); 20861433eb99SBorislav Petkov else 208741d8bfabSBorislav Petkov return ddr2_cs_size(cs_mode, dclr & WIDTH_128); 208841d8bfabSBorislav Petkov } 20891433eb99SBorislav Petkov 209041d8bfabSBorislav Petkov /* 209141d8bfabSBorislav Petkov * F15h supports only 64bit DCT interfaces 209241d8bfabSBorislav Petkov */ 209341d8bfabSBorislav Petkov static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 2094a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 209541d8bfabSBorislav Petkov { 209641d8bfabSBorislav Petkov WARN_ON(cs_mode > 12); 209741d8bfabSBorislav Petkov 209841d8bfabSBorislav Petkov return ddr3_cs_size(cs_mode, false); 20991afd3c98SDoug Thompson } 21001afd3c98SDoug Thompson 2101a597d2a5SAravind Gopalakrishnan /* F15h M60h supports DDR4 mapping as well.. */ 2102a597d2a5SAravind Gopalakrishnan static int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 2103a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 2104a597d2a5SAravind Gopalakrishnan { 2105a597d2a5SAravind Gopalakrishnan int cs_size; 2106a597d2a5SAravind Gopalakrishnan u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr]; 2107a597d2a5SAravind Gopalakrishnan 2108a597d2a5SAravind Gopalakrishnan WARN_ON(cs_mode > 12); 2109a597d2a5SAravind Gopalakrishnan 2110a597d2a5SAravind Gopalakrishnan if (pvt->dram_type == MEM_DDR4) { 2111a597d2a5SAravind Gopalakrishnan if (cs_mode > 9) 2112a597d2a5SAravind Gopalakrishnan return -1; 2113a597d2a5SAravind Gopalakrishnan 2114a597d2a5SAravind Gopalakrishnan cs_size = ddr4_cs_size(cs_mode); 2115a597d2a5SAravind Gopalakrishnan } else if (pvt->dram_type == MEM_LRDDR3) { 2116a597d2a5SAravind Gopalakrishnan unsigned rank_multiply = dcsm & 0xf; 2117a597d2a5SAravind Gopalakrishnan 2118a597d2a5SAravind Gopalakrishnan if (rank_multiply == 3) 2119a597d2a5SAravind Gopalakrishnan rank_multiply = 4; 2120a597d2a5SAravind Gopalakrishnan cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply); 2121a597d2a5SAravind Gopalakrishnan } else { 2122a597d2a5SAravind Gopalakrishnan /* Minimum cs size is 512mb for F15hM60h*/ 2123a597d2a5SAravind Gopalakrishnan if (cs_mode == 0x1) 2124a597d2a5SAravind Gopalakrishnan return -1; 2125a597d2a5SAravind Gopalakrishnan 2126a597d2a5SAravind Gopalakrishnan cs_size = ddr3_cs_size(cs_mode, false); 2127a597d2a5SAravind Gopalakrishnan } 2128a597d2a5SAravind Gopalakrishnan 2129a597d2a5SAravind Gopalakrishnan return cs_size; 2130a597d2a5SAravind Gopalakrishnan } 2131a597d2a5SAravind Gopalakrishnan 213294c1acf2SAravind Gopalakrishnan /* 213318b94f66SAravind Gopalakrishnan * F16h and F15h model 30h have only limited cs_modes. 213494c1acf2SAravind Gopalakrishnan */ 213594c1acf2SAravind Gopalakrishnan static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 2136a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 213794c1acf2SAravind Gopalakrishnan { 213894c1acf2SAravind Gopalakrishnan WARN_ON(cs_mode > 12); 213994c1acf2SAravind Gopalakrishnan 214094c1acf2SAravind Gopalakrishnan if (cs_mode == 6 || cs_mode == 8 || 214194c1acf2SAravind Gopalakrishnan cs_mode == 9 || cs_mode == 12) 214294c1acf2SAravind Gopalakrishnan return -1; 214394c1acf2SAravind Gopalakrishnan else 214494c1acf2SAravind Gopalakrishnan return ddr3_cs_size(cs_mode, false); 214594c1acf2SAravind Gopalakrishnan } 214694c1acf2SAravind Gopalakrishnan 2147e53a3b26SYazen Ghannam static int f17_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc, 2148f1cbbec9SYazen Ghannam unsigned int cs_mode, int csrow_nr) 2149f1cbbec9SYazen Ghannam { 2150e53a3b26SYazen Ghannam u32 addr_mask_orig, addr_mask_deinterleaved; 2151e53a3b26SYazen Ghannam u32 msb, weight, num_zero_bits; 2152e53a3b26SYazen Ghannam int dimm, size = 0; 2153f1cbbec9SYazen Ghannam 2154e53a3b26SYazen Ghannam /* No Chip Selects are enabled. */ 2155e53a3b26SYazen Ghannam if (!cs_mode) 2156e53a3b26SYazen Ghannam return size; 2157e53a3b26SYazen Ghannam 2158e53a3b26SYazen Ghannam /* Requested size of an even CS but none are enabled. */ 2159e53a3b26SYazen Ghannam if (!(cs_mode & CS_EVEN) && !(csrow_nr & 1)) 2160e53a3b26SYazen Ghannam return size; 2161e53a3b26SYazen Ghannam 2162e53a3b26SYazen Ghannam /* Requested size of an odd CS but none are enabled. */ 2163e53a3b26SYazen Ghannam if (!(cs_mode & CS_ODD) && (csrow_nr & 1)) 2164e53a3b26SYazen Ghannam return size; 2165e53a3b26SYazen Ghannam 2166e53a3b26SYazen Ghannam /* 2167e53a3b26SYazen Ghannam * There is one mask per DIMM, and two Chip Selects per DIMM. 2168e53a3b26SYazen Ghannam * CS0 and CS1 -> DIMM0 2169e53a3b26SYazen Ghannam * CS2 and CS3 -> DIMM1 2170e53a3b26SYazen Ghannam */ 2171e53a3b26SYazen Ghannam dimm = csrow_nr >> 1; 2172e53a3b26SYazen Ghannam 217381f5090dSYazen Ghannam /* Asymmetric dual-rank DIMM support. */ 217481f5090dSYazen Ghannam if ((csrow_nr & 1) && (cs_mode & CS_ODD_SECONDARY)) 217581f5090dSYazen Ghannam addr_mask_orig = pvt->csels[umc].csmasks_sec[dimm]; 217681f5090dSYazen Ghannam else 2177e53a3b26SYazen Ghannam addr_mask_orig = pvt->csels[umc].csmasks[dimm]; 2178e53a3b26SYazen Ghannam 2179e53a3b26SYazen Ghannam /* 2180e53a3b26SYazen Ghannam * The number of zero bits in the mask is equal to the number of bits 2181e53a3b26SYazen Ghannam * in a full mask minus the number of bits in the current mask. 2182e53a3b26SYazen Ghannam * 2183e53a3b26SYazen Ghannam * The MSB is the number of bits in the full mask because BIT[0] is 2184e53a3b26SYazen Ghannam * always 0. 21859f4873fbSYazen Ghannam * 21869f4873fbSYazen Ghannam * In the special 3 Rank interleaving case, a single bit is flipped 21879f4873fbSYazen Ghannam * without swapping with the most significant bit. This can be handled 21889f4873fbSYazen Ghannam * by keeping the MSB where it is and ignoring the single zero bit. 2189e53a3b26SYazen Ghannam */ 2190e53a3b26SYazen Ghannam msb = fls(addr_mask_orig) - 1; 2191e53a3b26SYazen Ghannam weight = hweight_long(addr_mask_orig); 21929f4873fbSYazen Ghannam num_zero_bits = msb - weight - !!(cs_mode & CS_3R_INTERLEAVE); 2193e53a3b26SYazen Ghannam 2194e53a3b26SYazen Ghannam /* Take the number of zero bits off from the top of the mask. */ 2195e53a3b26SYazen Ghannam addr_mask_deinterleaved = GENMASK_ULL(msb - num_zero_bits, 1); 2196e53a3b26SYazen Ghannam 2197e53a3b26SYazen Ghannam edac_dbg(1, "CS%d DIMM%d AddrMasks:\n", csrow_nr, dimm); 2198e53a3b26SYazen Ghannam edac_dbg(1, " Original AddrMask: 0x%x\n", addr_mask_orig); 2199e53a3b26SYazen Ghannam edac_dbg(1, " Deinterleaved AddrMask: 0x%x\n", addr_mask_deinterleaved); 2200f1cbbec9SYazen Ghannam 2201f1cbbec9SYazen Ghannam /* Register [31:1] = Address [39:9]. Size is in kBs here. */ 2202e53a3b26SYazen Ghannam size = (addr_mask_deinterleaved >> 2) + 1; 2203f1cbbec9SYazen Ghannam 2204f1cbbec9SYazen Ghannam /* Return size in MBs. */ 2205f1cbbec9SYazen Ghannam return size >> 10; 2206f1cbbec9SYazen Ghannam } 2207f1cbbec9SYazen Ghannam 22085a5d2371SBorislav Petkov static void read_dram_ctl_register(struct amd64_pvt *pvt) 22096163b5d4SDoug Thompson { 22106163b5d4SDoug Thompson 2211a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) 22125a5d2371SBorislav Petkov return; 22135a5d2371SBorislav Petkov 22147981a28fSAravind Gopalakrishnan if (!amd64_read_pci_cfg(pvt->F2, DCT_SEL_LO, &pvt->dct_sel_lo)) { 2215956b9ba1SJoe Perches edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n", 221678da121eSBorislav Petkov pvt->dct_sel_lo, dct_sel_baseaddr(pvt)); 22176163b5d4SDoug Thompson 2218956b9ba1SJoe Perches edac_dbg(0, " DCTs operate in %s mode\n", 22195a5d2371SBorislav Petkov (dct_ganging_enabled(pvt) ? "ganged" : "unganged")); 22206163b5d4SDoug Thompson 222172381bd5SBorislav Petkov if (!dct_ganging_enabled(pvt)) 2222956b9ba1SJoe Perches edac_dbg(0, " Address range split per DCT: %s\n", 222372381bd5SBorislav Petkov (dct_high_range_enabled(pvt) ? "yes" : "no")); 222472381bd5SBorislav Petkov 2225956b9ba1SJoe Perches edac_dbg(0, " data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n", 222672381bd5SBorislav Petkov (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"), 222772381bd5SBorislav Petkov (dct_memory_cleared(pvt) ? "yes" : "no")); 222872381bd5SBorislav Petkov 2229956b9ba1SJoe Perches edac_dbg(0, " channel interleave: %s, " 223078da121eSBorislav Petkov "interleave bits selector: 0x%x\n", 223172381bd5SBorislav Petkov (dct_interleave_enabled(pvt) ? "enabled" : "disabled"), 22326163b5d4SDoug Thompson dct_sel_interleave_addr(pvt)); 22336163b5d4SDoug Thompson } 22346163b5d4SDoug Thompson 22357981a28fSAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F2, DCT_SEL_HI, &pvt->dct_sel_hi); 22366163b5d4SDoug Thompson } 22376163b5d4SDoug Thompson 2238f71d0a05SDoug Thompson /* 223918b94f66SAravind Gopalakrishnan * Determine channel (DCT) based on the interleaving mode (see F15h M30h BKDG, 224018b94f66SAravind Gopalakrishnan * 2.10.12 Memory Interleaving Modes). 224118b94f66SAravind Gopalakrishnan */ 224218b94f66SAravind Gopalakrishnan static u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, 224318b94f66SAravind Gopalakrishnan u8 intlv_en, int num_dcts_intlv, 224418b94f66SAravind Gopalakrishnan u32 dct_sel) 224518b94f66SAravind Gopalakrishnan { 224618b94f66SAravind Gopalakrishnan u8 channel = 0; 224718b94f66SAravind Gopalakrishnan u8 select; 224818b94f66SAravind Gopalakrishnan 224918b94f66SAravind Gopalakrishnan if (!(intlv_en)) 225018b94f66SAravind Gopalakrishnan return (u8)(dct_sel); 225118b94f66SAravind Gopalakrishnan 225218b94f66SAravind Gopalakrishnan if (num_dcts_intlv == 2) { 225318b94f66SAravind Gopalakrishnan select = (sys_addr >> 8) & 0x3; 225418b94f66SAravind Gopalakrishnan channel = select ? 0x3 : 0; 22559d0e8d83SAravind Gopalakrishnan } else if (num_dcts_intlv == 4) { 22569d0e8d83SAravind Gopalakrishnan u8 intlv_addr = dct_sel_interleave_addr(pvt); 22579d0e8d83SAravind Gopalakrishnan switch (intlv_addr) { 22589d0e8d83SAravind Gopalakrishnan case 0x4: 22599d0e8d83SAravind Gopalakrishnan channel = (sys_addr >> 8) & 0x3; 22609d0e8d83SAravind Gopalakrishnan break; 22619d0e8d83SAravind Gopalakrishnan case 0x5: 22629d0e8d83SAravind Gopalakrishnan channel = (sys_addr >> 9) & 0x3; 22639d0e8d83SAravind Gopalakrishnan break; 22649d0e8d83SAravind Gopalakrishnan } 22659d0e8d83SAravind Gopalakrishnan } 226618b94f66SAravind Gopalakrishnan return channel; 226718b94f66SAravind Gopalakrishnan } 226818b94f66SAravind Gopalakrishnan 226918b94f66SAravind Gopalakrishnan /* 2270229a7a11SBorislav Petkov * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory 2271f71d0a05SDoug Thompson * Interleaving Modes. 2272f71d0a05SDoug Thompson */ 2273b15f0fcaSBorislav Petkov static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, 2274229a7a11SBorislav Petkov bool hi_range_sel, u8 intlv_en) 22756163b5d4SDoug Thompson { 2276151fa71cSBorislav Petkov u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1; 22776163b5d4SDoug Thompson 22786163b5d4SDoug Thompson if (dct_ganging_enabled(pvt)) 2279229a7a11SBorislav Petkov return 0; 2280229a7a11SBorislav Petkov 2281229a7a11SBorislav Petkov if (hi_range_sel) 2282229a7a11SBorislav Petkov return dct_sel_high; 2283229a7a11SBorislav Petkov 2284f71d0a05SDoug Thompson /* 2285f71d0a05SDoug Thompson * see F2x110[DctSelIntLvAddr] - channel interleave mode 2286f71d0a05SDoug Thompson */ 2287229a7a11SBorislav Petkov if (dct_interleave_enabled(pvt)) { 2288229a7a11SBorislav Petkov u8 intlv_addr = dct_sel_interleave_addr(pvt); 22896163b5d4SDoug Thompson 2290229a7a11SBorislav Petkov /* return DCT select function: 0=DCT0, 1=DCT1 */ 2291229a7a11SBorislav Petkov if (!intlv_addr) 2292229a7a11SBorislav Petkov return sys_addr >> 6 & 1; 22936163b5d4SDoug Thompson 2294229a7a11SBorislav Petkov if (intlv_addr & 0x2) { 2295229a7a11SBorislav Petkov u8 shift = intlv_addr & 0x1 ? 9 : 6; 2296dc0a50a8SYazen Ghannam u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) & 1; 2297229a7a11SBorislav Petkov 2298229a7a11SBorislav Petkov return ((sys_addr >> shift) & 1) ^ temp; 22996163b5d4SDoug Thompson } 23006163b5d4SDoug Thompson 2301dc0a50a8SYazen Ghannam if (intlv_addr & 0x4) { 2302dc0a50a8SYazen Ghannam u8 shift = intlv_addr & 0x1 ? 9 : 8; 2303dc0a50a8SYazen Ghannam 2304dc0a50a8SYazen Ghannam return (sys_addr >> shift) & 1; 2305dc0a50a8SYazen Ghannam } 2306dc0a50a8SYazen Ghannam 2307229a7a11SBorislav Petkov return (sys_addr >> (12 + hweight8(intlv_en))) & 1; 2308229a7a11SBorislav Petkov } 2309229a7a11SBorislav Petkov 2310229a7a11SBorislav Petkov if (dct_high_range_enabled(pvt)) 2311229a7a11SBorislav Petkov return ~dct_sel_high & 1; 23126163b5d4SDoug Thompson 23136163b5d4SDoug Thompson return 0; 23146163b5d4SDoug Thompson } 23156163b5d4SDoug Thompson 2316c8e518d5SBorislav Petkov /* Convert the sys_addr to the normalized DCT address */ 2317c7e5301aSDaniel J Blueman static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range, 2318c8e518d5SBorislav Petkov u64 sys_addr, bool hi_rng, 2319c8e518d5SBorislav Petkov u32 dct_sel_base_addr) 23206163b5d4SDoug Thompson { 23216163b5d4SDoug Thompson u64 chan_off; 2322c8e518d5SBorislav Petkov u64 dram_base = get_dram_base(pvt, range); 2323c8e518d5SBorislav Petkov u64 hole_off = f10_dhar_offset(pvt); 23246f3508f6SDan Carpenter u64 dct_sel_base_off = (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16; 23256163b5d4SDoug Thompson 2326c8e518d5SBorislav Petkov if (hi_rng) { 2327c8e518d5SBorislav Petkov /* 2328c8e518d5SBorislav Petkov * if 2329c8e518d5SBorislav Petkov * base address of high range is below 4Gb 2330c8e518d5SBorislav Petkov * (bits [47:27] at [31:11]) 2331c8e518d5SBorislav Petkov * DRAM address space on this DCT is hoisted above 4Gb && 2332c8e518d5SBorislav Petkov * sys_addr > 4Gb 2333c8e518d5SBorislav Petkov * 2334c8e518d5SBorislav Petkov * remove hole offset from sys_addr 2335c8e518d5SBorislav Petkov * else 2336c8e518d5SBorislav Petkov * remove high range offset from sys_addr 2337c8e518d5SBorislav Petkov */ 2338c8e518d5SBorislav Petkov if ((!(dct_sel_base_addr >> 16) || 2339c8e518d5SBorislav Petkov dct_sel_base_addr < dhar_base(pvt)) && 2340972ea17aSBorislav Petkov dhar_valid(pvt) && 2341c8e518d5SBorislav Petkov (sys_addr >= BIT_64(32))) 2342bc21fa57SBorislav Petkov chan_off = hole_off; 23436163b5d4SDoug Thompson else 23446163b5d4SDoug Thompson chan_off = dct_sel_base_off; 23456163b5d4SDoug Thompson } else { 2346c8e518d5SBorislav Petkov /* 2347c8e518d5SBorislav Petkov * if 2348c8e518d5SBorislav Petkov * we have a valid hole && 2349c8e518d5SBorislav Petkov * sys_addr > 4Gb 2350c8e518d5SBorislav Petkov * 2351c8e518d5SBorislav Petkov * remove hole 2352c8e518d5SBorislav Petkov * else 2353c8e518d5SBorislav Petkov * remove dram base to normalize to DCT address 2354c8e518d5SBorislav Petkov */ 2355972ea17aSBorislav Petkov if (dhar_valid(pvt) && (sys_addr >= BIT_64(32))) 2356bc21fa57SBorislav Petkov chan_off = hole_off; 23576163b5d4SDoug Thompson else 2358c8e518d5SBorislav Petkov chan_off = dram_base; 23596163b5d4SDoug Thompson } 23606163b5d4SDoug Thompson 236110ef6b0dSChen, Gong return (sys_addr & GENMASK_ULL(47,6)) - (chan_off & GENMASK_ULL(47,23)); 23626163b5d4SDoug Thompson } 23636163b5d4SDoug Thompson 23646163b5d4SDoug Thompson /* 23656163b5d4SDoug Thompson * checks if the csrow passed in is marked as SPARED, if so returns the new 23666163b5d4SDoug Thompson * spare row 23676163b5d4SDoug Thompson */ 236811c75eadSBorislav Petkov static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow) 23696163b5d4SDoug Thompson { 2370614ec9d8SBorislav Petkov int tmp_cs; 23716163b5d4SDoug Thompson 2372614ec9d8SBorislav Petkov if (online_spare_swap_done(pvt, dct) && 2373614ec9d8SBorislav Petkov csrow == online_spare_bad_dramcs(pvt, dct)) { 2374614ec9d8SBorislav Petkov 2375614ec9d8SBorislav Petkov for_each_chip_select(tmp_cs, dct, pvt) { 2376614ec9d8SBorislav Petkov if (chip_select_base(tmp_cs, dct, pvt) & 0x2) { 2377614ec9d8SBorislav Petkov csrow = tmp_cs; 2378614ec9d8SBorislav Petkov break; 2379614ec9d8SBorislav Petkov } 2380614ec9d8SBorislav Petkov } 23816163b5d4SDoug Thompson } 23826163b5d4SDoug Thompson return csrow; 23836163b5d4SDoug Thompson } 23846163b5d4SDoug Thompson 23856163b5d4SDoug Thompson /* 23866163b5d4SDoug Thompson * Iterate over the DRAM DCT "base" and "mask" registers looking for a 23876163b5d4SDoug Thompson * SystemAddr match on the specified 'ChannelSelect' and 'NodeID' 23886163b5d4SDoug Thompson * 23896163b5d4SDoug Thompson * Return: 23906163b5d4SDoug Thompson * -EINVAL: NOT FOUND 23916163b5d4SDoug Thompson * 0..csrow = Chip-Select Row 23926163b5d4SDoug Thompson */ 2393c7e5301aSDaniel J Blueman static int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct) 23946163b5d4SDoug Thompson { 23956163b5d4SDoug Thompson struct mem_ctl_info *mci; 23966163b5d4SDoug Thompson struct amd64_pvt *pvt; 239711c75eadSBorislav Petkov u64 cs_base, cs_mask; 23986163b5d4SDoug Thompson int cs_found = -EINVAL; 23996163b5d4SDoug Thompson int csrow; 24006163b5d4SDoug Thompson 24012ec591acSBorislav Petkov mci = edac_mc_find(nid); 24026163b5d4SDoug Thompson if (!mci) 24036163b5d4SDoug Thompson return cs_found; 24046163b5d4SDoug Thompson 24056163b5d4SDoug Thompson pvt = mci->pvt_info; 24066163b5d4SDoug Thompson 2407956b9ba1SJoe Perches edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct); 24086163b5d4SDoug Thompson 240911c75eadSBorislav Petkov for_each_chip_select(csrow, dct, pvt) { 241011c75eadSBorislav Petkov if (!csrow_enabled(csrow, dct, pvt)) 24116163b5d4SDoug Thompson continue; 24126163b5d4SDoug Thompson 241311c75eadSBorislav Petkov get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask); 24146163b5d4SDoug Thompson 2415956b9ba1SJoe Perches edac_dbg(1, " CSROW=%d CSBase=0x%llx CSMask=0x%llx\n", 24166163b5d4SDoug Thompson csrow, cs_base, cs_mask); 24176163b5d4SDoug Thompson 241811c75eadSBorislav Petkov cs_mask = ~cs_mask; 24196163b5d4SDoug Thompson 2420956b9ba1SJoe Perches edac_dbg(1, " (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n", 242111c75eadSBorislav Petkov (in_addr & cs_mask), (cs_base & cs_mask)); 24226163b5d4SDoug Thompson 242311c75eadSBorislav Petkov if ((in_addr & cs_mask) == (cs_base & cs_mask)) { 242418b94f66SAravind Gopalakrishnan if (pvt->fam == 0x15 && pvt->model >= 0x30) { 242518b94f66SAravind Gopalakrishnan cs_found = csrow; 242618b94f66SAravind Gopalakrishnan break; 242718b94f66SAravind Gopalakrishnan } 242811c75eadSBorislav Petkov cs_found = f10_process_possible_spare(pvt, dct, csrow); 24296163b5d4SDoug Thompson 2430956b9ba1SJoe Perches edac_dbg(1, " MATCH csrow=%d\n", cs_found); 24316163b5d4SDoug Thompson break; 24326163b5d4SDoug Thompson } 24336163b5d4SDoug Thompson } 24346163b5d4SDoug Thompson return cs_found; 24356163b5d4SDoug Thompson } 24366163b5d4SDoug Thompson 243795b0ef55SBorislav Petkov /* 243895b0ef55SBorislav Petkov * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is 243995b0ef55SBorislav Petkov * swapped with a region located at the bottom of memory so that the GPU can use 244095b0ef55SBorislav Petkov * the interleaved region and thus two channels. 244195b0ef55SBorislav Petkov */ 2442b15f0fcaSBorislav Petkov static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr) 244395b0ef55SBorislav Petkov { 244495b0ef55SBorislav Petkov u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr; 244595b0ef55SBorislav Petkov 2446a4b4bedcSBorislav Petkov if (pvt->fam == 0x10) { 244795b0ef55SBorislav Petkov /* only revC3 and revE have that feature */ 2448a4b4bedcSBorislav Petkov if (pvt->model < 4 || (pvt->model < 0xa && pvt->stepping < 3)) 244995b0ef55SBorislav Petkov return sys_addr; 245095b0ef55SBorislav Petkov } 245195b0ef55SBorislav Petkov 24527981a28fSAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F2, SWAP_INTLV_REG, &swap_reg); 245395b0ef55SBorislav Petkov 245495b0ef55SBorislav Petkov if (!(swap_reg & 0x1)) 245595b0ef55SBorislav Petkov return sys_addr; 245695b0ef55SBorislav Petkov 245795b0ef55SBorislav Petkov swap_base = (swap_reg >> 3) & 0x7f; 245895b0ef55SBorislav Petkov swap_limit = (swap_reg >> 11) & 0x7f; 245995b0ef55SBorislav Petkov rgn_size = (swap_reg >> 20) & 0x7f; 246095b0ef55SBorislav Petkov tmp_addr = sys_addr >> 27; 246195b0ef55SBorislav Petkov 246295b0ef55SBorislav Petkov if (!(sys_addr >> 34) && 246395b0ef55SBorislav Petkov (((tmp_addr >= swap_base) && 246495b0ef55SBorislav Petkov (tmp_addr <= swap_limit)) || 246595b0ef55SBorislav Petkov (tmp_addr < rgn_size))) 246695b0ef55SBorislav Petkov return sys_addr ^ (u64)swap_base << 27; 246795b0ef55SBorislav Petkov 246895b0ef55SBorislav Petkov return sys_addr; 246995b0ef55SBorislav Petkov } 247095b0ef55SBorislav Petkov 2471f71d0a05SDoug Thompson /* For a given @dram_range, check if @sys_addr falls within it. */ 2472e761359aSBorislav Petkov static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range, 247333ca0643SBorislav Petkov u64 sys_addr, int *chan_sel) 2474f71d0a05SDoug Thompson { 2475229a7a11SBorislav Petkov int cs_found = -EINVAL; 2476c8e518d5SBorislav Petkov u64 chan_addr; 24775d4b58e8SBorislav Petkov u32 dct_sel_base; 247811c75eadSBorislav Petkov u8 channel; 2479229a7a11SBorislav Petkov bool high_range = false; 2480f71d0a05SDoug Thompson 24817f19bf75SBorislav Petkov u8 node_id = dram_dst_node(pvt, range); 2482229a7a11SBorislav Petkov u8 intlv_en = dram_intlv_en(pvt, range); 24837f19bf75SBorislav Petkov u32 intlv_sel = dram_intlv_sel(pvt, range); 2484f71d0a05SDoug Thompson 2485956b9ba1SJoe Perches edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n", 2486c8e518d5SBorislav Petkov range, sys_addr, get_dram_limit(pvt, range)); 2487f71d0a05SDoug Thompson 2488355fba60SBorislav Petkov if (dhar_valid(pvt) && 2489355fba60SBorislav Petkov dhar_base(pvt) <= sys_addr && 2490355fba60SBorislav Petkov sys_addr < BIT_64(32)) { 2491355fba60SBorislav Petkov amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n", 2492355fba60SBorislav Petkov sys_addr); 2493f71d0a05SDoug Thompson return -EINVAL; 2494355fba60SBorislav Petkov } 2495355fba60SBorislav Petkov 2496f030ddfbSBorislav Petkov if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en))) 2497355fba60SBorislav Petkov return -EINVAL; 2498f71d0a05SDoug Thompson 2499b15f0fcaSBorislav Petkov sys_addr = f1x_swap_interleaved_region(pvt, sys_addr); 250095b0ef55SBorislav Petkov 2501f71d0a05SDoug Thompson dct_sel_base = dct_sel_baseaddr(pvt); 2502f71d0a05SDoug Thompson 2503f71d0a05SDoug Thompson /* 2504f71d0a05SDoug Thompson * check whether addresses >= DctSelBaseAddr[47:27] are to be used to 2505f71d0a05SDoug Thompson * select between DCT0 and DCT1. 2506f71d0a05SDoug Thompson */ 2507f71d0a05SDoug Thompson if (dct_high_range_enabled(pvt) && 2508f71d0a05SDoug Thompson !dct_ganging_enabled(pvt) && 2509f71d0a05SDoug Thompson ((sys_addr >> 27) >= (dct_sel_base >> 11))) 2510229a7a11SBorislav Petkov high_range = true; 2511f71d0a05SDoug Thompson 2512b15f0fcaSBorislav Petkov channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en); 2513f71d0a05SDoug Thompson 2514b15f0fcaSBorislav Petkov chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr, 2515c8e518d5SBorislav Petkov high_range, dct_sel_base); 2516f71d0a05SDoug Thompson 2517e2f79dbdSBorislav Petkov /* Remove node interleaving, see F1x120 */ 2518e2f79dbdSBorislav Petkov if (intlv_en) 2519e2f79dbdSBorislav Petkov chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) | 2520e2f79dbdSBorislav Petkov (chan_addr & 0xfff); 2521f71d0a05SDoug Thompson 25225d4b58e8SBorislav Petkov /* remove channel interleave */ 2523f71d0a05SDoug Thompson if (dct_interleave_enabled(pvt) && 2524f71d0a05SDoug Thompson !dct_high_range_enabled(pvt) && 2525f71d0a05SDoug Thompson !dct_ganging_enabled(pvt)) { 25265d4b58e8SBorislav Petkov 25275d4b58e8SBorislav Petkov if (dct_sel_interleave_addr(pvt) != 1) { 25285d4b58e8SBorislav Petkov if (dct_sel_interleave_addr(pvt) == 0x3) 25295d4b58e8SBorislav Petkov /* hash 9 */ 25305d4b58e8SBorislav Petkov chan_addr = ((chan_addr >> 10) << 9) | 25315d4b58e8SBorislav Petkov (chan_addr & 0x1ff); 25325d4b58e8SBorislav Petkov else 25335d4b58e8SBorislav Petkov /* A[6] or hash 6 */ 25345d4b58e8SBorislav Petkov chan_addr = ((chan_addr >> 7) << 6) | 25355d4b58e8SBorislav Petkov (chan_addr & 0x3f); 25365d4b58e8SBorislav Petkov } else 25375d4b58e8SBorislav Petkov /* A[12] */ 25385d4b58e8SBorislav Petkov chan_addr = ((chan_addr >> 13) << 12) | 25395d4b58e8SBorislav Petkov (chan_addr & 0xfff); 2540f71d0a05SDoug Thompson } 2541f71d0a05SDoug Thompson 2542956b9ba1SJoe Perches edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr); 2543f71d0a05SDoug Thompson 2544b15f0fcaSBorislav Petkov cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel); 2545f71d0a05SDoug Thompson 254633ca0643SBorislav Petkov if (cs_found >= 0) 2547f71d0a05SDoug Thompson *chan_sel = channel; 254833ca0643SBorislav Petkov 2549f71d0a05SDoug Thompson return cs_found; 2550f71d0a05SDoug Thompson } 2551f71d0a05SDoug Thompson 255218b94f66SAravind Gopalakrishnan static int f15_m30h_match_to_this_node(struct amd64_pvt *pvt, unsigned range, 255318b94f66SAravind Gopalakrishnan u64 sys_addr, int *chan_sel) 255418b94f66SAravind Gopalakrishnan { 255518b94f66SAravind Gopalakrishnan int cs_found = -EINVAL; 255618b94f66SAravind Gopalakrishnan int num_dcts_intlv = 0; 255718b94f66SAravind Gopalakrishnan u64 chan_addr, chan_offset; 255818b94f66SAravind Gopalakrishnan u64 dct_base, dct_limit; 255918b94f66SAravind Gopalakrishnan u32 dct_cont_base_reg, dct_cont_limit_reg, tmp; 256018b94f66SAravind Gopalakrishnan u8 channel, alias_channel, leg_mmio_hole, dct_sel, dct_offset_en; 256118b94f66SAravind Gopalakrishnan 256218b94f66SAravind Gopalakrishnan u64 dhar_offset = f10_dhar_offset(pvt); 256318b94f66SAravind Gopalakrishnan u8 intlv_addr = dct_sel_interleave_addr(pvt); 256418b94f66SAravind Gopalakrishnan u8 node_id = dram_dst_node(pvt, range); 256518b94f66SAravind Gopalakrishnan u8 intlv_en = dram_intlv_en(pvt, range); 256618b94f66SAravind Gopalakrishnan 256718b94f66SAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F1, DRAM_CONT_BASE, &dct_cont_base_reg); 256818b94f66SAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F1, DRAM_CONT_LIMIT, &dct_cont_limit_reg); 256918b94f66SAravind Gopalakrishnan 257018b94f66SAravind Gopalakrishnan dct_offset_en = (u8) ((dct_cont_base_reg >> 3) & BIT(0)); 257118b94f66SAravind Gopalakrishnan dct_sel = (u8) ((dct_cont_base_reg >> 4) & 0x7); 257218b94f66SAravind Gopalakrishnan 257318b94f66SAravind Gopalakrishnan edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n", 257418b94f66SAravind Gopalakrishnan range, sys_addr, get_dram_limit(pvt, range)); 257518b94f66SAravind Gopalakrishnan 257618b94f66SAravind Gopalakrishnan if (!(get_dram_base(pvt, range) <= sys_addr) && 257718b94f66SAravind Gopalakrishnan !(get_dram_limit(pvt, range) >= sys_addr)) 257818b94f66SAravind Gopalakrishnan return -EINVAL; 257918b94f66SAravind Gopalakrishnan 258018b94f66SAravind Gopalakrishnan if (dhar_valid(pvt) && 258118b94f66SAravind Gopalakrishnan dhar_base(pvt) <= sys_addr && 258218b94f66SAravind Gopalakrishnan sys_addr < BIT_64(32)) { 258318b94f66SAravind Gopalakrishnan amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n", 258418b94f66SAravind Gopalakrishnan sys_addr); 258518b94f66SAravind Gopalakrishnan return -EINVAL; 258618b94f66SAravind Gopalakrishnan } 258718b94f66SAravind Gopalakrishnan 258818b94f66SAravind Gopalakrishnan /* Verify sys_addr is within DCT Range. */ 25894fc06b31SAravind Gopalakrishnan dct_base = (u64) dct_sel_baseaddr(pvt); 25904fc06b31SAravind Gopalakrishnan dct_limit = (dct_cont_limit_reg >> 11) & 0x1FFF; 259118b94f66SAravind Gopalakrishnan 259218b94f66SAravind Gopalakrishnan if (!(dct_cont_base_reg & BIT(0)) && 25934fc06b31SAravind Gopalakrishnan !(dct_base <= (sys_addr >> 27) && 25944fc06b31SAravind Gopalakrishnan dct_limit >= (sys_addr >> 27))) 259518b94f66SAravind Gopalakrishnan return -EINVAL; 259618b94f66SAravind Gopalakrishnan 259718b94f66SAravind Gopalakrishnan /* Verify number of dct's that participate in channel interleaving. */ 259818b94f66SAravind Gopalakrishnan num_dcts_intlv = (int) hweight8(intlv_en); 259918b94f66SAravind Gopalakrishnan 260018b94f66SAravind Gopalakrishnan if (!(num_dcts_intlv % 2 == 0) || (num_dcts_intlv > 4)) 260118b94f66SAravind Gopalakrishnan return -EINVAL; 260218b94f66SAravind Gopalakrishnan 2603dc0a50a8SYazen Ghannam if (pvt->model >= 0x60) 2604dc0a50a8SYazen Ghannam channel = f1x_determine_channel(pvt, sys_addr, false, intlv_en); 2605dc0a50a8SYazen Ghannam else 260618b94f66SAravind Gopalakrishnan channel = f15_m30h_determine_channel(pvt, sys_addr, intlv_en, 260718b94f66SAravind Gopalakrishnan num_dcts_intlv, dct_sel); 260818b94f66SAravind Gopalakrishnan 260918b94f66SAravind Gopalakrishnan /* Verify we stay within the MAX number of channels allowed */ 26107f3f5240SAravind Gopalakrishnan if (channel > 3) 261118b94f66SAravind Gopalakrishnan return -EINVAL; 261218b94f66SAravind Gopalakrishnan 261318b94f66SAravind Gopalakrishnan leg_mmio_hole = (u8) (dct_cont_base_reg >> 1 & BIT(0)); 261418b94f66SAravind Gopalakrishnan 261518b94f66SAravind Gopalakrishnan /* Get normalized DCT addr */ 261618b94f66SAravind Gopalakrishnan if (leg_mmio_hole && (sys_addr >= BIT_64(32))) 261718b94f66SAravind Gopalakrishnan chan_offset = dhar_offset; 261818b94f66SAravind Gopalakrishnan else 26194fc06b31SAravind Gopalakrishnan chan_offset = dct_base << 27; 262018b94f66SAravind Gopalakrishnan 262118b94f66SAravind Gopalakrishnan chan_addr = sys_addr - chan_offset; 262218b94f66SAravind Gopalakrishnan 262318b94f66SAravind Gopalakrishnan /* remove channel interleave */ 262418b94f66SAravind Gopalakrishnan if (num_dcts_intlv == 2) { 262518b94f66SAravind Gopalakrishnan if (intlv_addr == 0x4) 262618b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 9) << 8) | 262718b94f66SAravind Gopalakrishnan (chan_addr & 0xff); 262818b94f66SAravind Gopalakrishnan else if (intlv_addr == 0x5) 262918b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 10) << 9) | 263018b94f66SAravind Gopalakrishnan (chan_addr & 0x1ff); 263118b94f66SAravind Gopalakrishnan else 263218b94f66SAravind Gopalakrishnan return -EINVAL; 263318b94f66SAravind Gopalakrishnan 263418b94f66SAravind Gopalakrishnan } else if (num_dcts_intlv == 4) { 263518b94f66SAravind Gopalakrishnan if (intlv_addr == 0x4) 263618b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 10) << 8) | 263718b94f66SAravind Gopalakrishnan (chan_addr & 0xff); 263818b94f66SAravind Gopalakrishnan else if (intlv_addr == 0x5) 263918b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 11) << 9) | 264018b94f66SAravind Gopalakrishnan (chan_addr & 0x1ff); 264118b94f66SAravind Gopalakrishnan else 264218b94f66SAravind Gopalakrishnan return -EINVAL; 264318b94f66SAravind Gopalakrishnan } 264418b94f66SAravind Gopalakrishnan 264518b94f66SAravind Gopalakrishnan if (dct_offset_en) { 264618b94f66SAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F1, 264718b94f66SAravind Gopalakrishnan DRAM_CONT_HIGH_OFF + (int) channel * 4, 264818b94f66SAravind Gopalakrishnan &tmp); 26494fc06b31SAravind Gopalakrishnan chan_addr += (u64) ((tmp >> 11) & 0xfff) << 27; 265018b94f66SAravind Gopalakrishnan } 265118b94f66SAravind Gopalakrishnan 265218b94f66SAravind Gopalakrishnan f15h_select_dct(pvt, channel); 265318b94f66SAravind Gopalakrishnan 265418b94f66SAravind Gopalakrishnan edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr); 265518b94f66SAravind Gopalakrishnan 265618b94f66SAravind Gopalakrishnan /* 265718b94f66SAravind Gopalakrishnan * Find Chip select: 265818b94f66SAravind Gopalakrishnan * if channel = 3, then alias it to 1. This is because, in F15 M30h, 265918b94f66SAravind Gopalakrishnan * there is support for 4 DCT's, but only 2 are currently functional. 266018b94f66SAravind Gopalakrishnan * They are DCT0 and DCT3. But we have read all registers of DCT3 into 266118b94f66SAravind Gopalakrishnan * pvt->csels[1]. So we need to use '1' here to get correct info. 266218b94f66SAravind Gopalakrishnan * Refer F15 M30h BKDG Section 2.10 and 2.10.3 for clarifications. 266318b94f66SAravind Gopalakrishnan */ 266418b94f66SAravind Gopalakrishnan alias_channel = (channel == 3) ? 1 : channel; 266518b94f66SAravind Gopalakrishnan 266618b94f66SAravind Gopalakrishnan cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, alias_channel); 266718b94f66SAravind Gopalakrishnan 266818b94f66SAravind Gopalakrishnan if (cs_found >= 0) 266918b94f66SAravind Gopalakrishnan *chan_sel = alias_channel; 267018b94f66SAravind Gopalakrishnan 267118b94f66SAravind Gopalakrishnan return cs_found; 267218b94f66SAravind Gopalakrishnan } 267318b94f66SAravind Gopalakrishnan 267418b94f66SAravind Gopalakrishnan static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt, 267518b94f66SAravind Gopalakrishnan u64 sys_addr, 267633ca0643SBorislav Petkov int *chan_sel) 2677f71d0a05SDoug Thompson { 2678e761359aSBorislav Petkov int cs_found = -EINVAL; 2679e761359aSBorislav Petkov unsigned range; 2680f71d0a05SDoug Thompson 26817f19bf75SBorislav Petkov for (range = 0; range < DRAM_RANGES; range++) { 26827f19bf75SBorislav Petkov if (!dram_rw(pvt, range)) 2683f71d0a05SDoug Thompson continue; 2684f71d0a05SDoug Thompson 268518b94f66SAravind Gopalakrishnan if (pvt->fam == 0x15 && pvt->model >= 0x30) 268618b94f66SAravind Gopalakrishnan cs_found = f15_m30h_match_to_this_node(pvt, range, 268718b94f66SAravind Gopalakrishnan sys_addr, 268818b94f66SAravind Gopalakrishnan chan_sel); 2689f71d0a05SDoug Thompson 269018b94f66SAravind Gopalakrishnan else if ((get_dram_base(pvt, range) <= sys_addr) && 269118b94f66SAravind Gopalakrishnan (get_dram_limit(pvt, range) >= sys_addr)) { 2692b15f0fcaSBorislav Petkov cs_found = f1x_match_to_this_node(pvt, range, 269333ca0643SBorislav Petkov sys_addr, chan_sel); 2694f71d0a05SDoug Thompson if (cs_found >= 0) 2695f71d0a05SDoug Thompson break; 2696f71d0a05SDoug Thompson } 2697f71d0a05SDoug Thompson } 2698f71d0a05SDoug Thompson return cs_found; 2699f71d0a05SDoug Thompson } 2700f71d0a05SDoug Thompson 2701f71d0a05SDoug Thompson /* 2702bdc30a0cSBorislav Petkov * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps 2703bdc30a0cSBorislav Petkov * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW). 2704f71d0a05SDoug Thompson * 2705bdc30a0cSBorislav Petkov * The @sys_addr is usually an error address received from the hardware 2706bdc30a0cSBorislav Petkov * (MCX_ADDR). 2707f71d0a05SDoug Thompson */ 2708b15f0fcaSBorislav Petkov static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr, 270933ca0643SBorislav Petkov struct err_info *err) 2710f71d0a05SDoug Thompson { 2711f71d0a05SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 2712f71d0a05SDoug Thompson 271333ca0643SBorislav Petkov error_address_to_page_and_offset(sys_addr, err); 2714ab5a503cSMauro Carvalho Chehab 271533ca0643SBorislav Petkov err->csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &err->channel); 271633ca0643SBorislav Petkov if (err->csrow < 0) { 271733ca0643SBorislav Petkov err->err_code = ERR_CSROW; 2718bdc30a0cSBorislav Petkov return; 2719bdc30a0cSBorislav Petkov } 2720bdc30a0cSBorislav Petkov 2721f71d0a05SDoug Thompson /* 2722bdc30a0cSBorislav Petkov * We need the syndromes for channel detection only when we're 2723bdc30a0cSBorislav Petkov * ganged. Otherwise @chan should already contain the channel at 2724bdc30a0cSBorislav Petkov * this point. 2725f71d0a05SDoug Thompson */ 2726a97fa68eSBorislav Petkov if (dct_ganging_enabled(pvt)) 272733ca0643SBorislav Petkov err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome); 2728f71d0a05SDoug Thompson } 2729f71d0a05SDoug Thompson 2730f71d0a05SDoug Thompson /* 27318566c4dfSBorislav Petkov * debug routine to display the memory sizes of all logical DIMMs and its 2732cb328507SBorislav Petkov * CSROWs 2733f71d0a05SDoug Thompson */ 2734d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl) 2735f71d0a05SDoug Thompson { 2736bb89f5a0SBorislav Petkov int dimm, size0, size1; 2737525a1b20SBorislav Petkov u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases; 2738525a1b20SBorislav Petkov u32 dbam = ctrl ? pvt->dbam1 : pvt->dbam0; 2739f71d0a05SDoug Thompson 2740a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) { 27418566c4dfSBorislav Petkov /* K8 families < revF not supported yet */ 27421433eb99SBorislav Petkov if (pvt->ext_model < K8_REV_F) 27438566c4dfSBorislav Petkov return; 27448566c4dfSBorislav Petkov else 27458566c4dfSBorislav Petkov WARN_ON(ctrl != 0); 27468566c4dfSBorislav Petkov } 27478566c4dfSBorislav Petkov 27487981a28fSAravind Gopalakrishnan if (pvt->fam == 0x10) { 27497981a28fSAravind Gopalakrishnan dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1 27507981a28fSAravind Gopalakrishnan : pvt->dbam0; 27517981a28fSAravind Gopalakrishnan dcsb = (ctrl && !dct_ganging_enabled(pvt)) ? 27527981a28fSAravind Gopalakrishnan pvt->csels[1].csbases : 27537981a28fSAravind Gopalakrishnan pvt->csels[0].csbases; 27547981a28fSAravind Gopalakrishnan } else if (ctrl) { 27557981a28fSAravind Gopalakrishnan dbam = pvt->dbam0; 27567981a28fSAravind Gopalakrishnan dcsb = pvt->csels[1].csbases; 27577981a28fSAravind Gopalakrishnan } 2758956b9ba1SJoe Perches edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n", 2759956b9ba1SJoe Perches ctrl, dbam); 2760f71d0a05SDoug Thompson 27618566c4dfSBorislav Petkov edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl); 27628566c4dfSBorislav Petkov 2763f71d0a05SDoug Thompson /* Dump memory sizes for DIMM and its CSROWs */ 2764f71d0a05SDoug Thompson for (dimm = 0; dimm < 4; dimm++) { 2765f71d0a05SDoug Thompson 2766f71d0a05SDoug Thompson size0 = 0; 276711c75eadSBorislav Petkov if (dcsb[dimm*2] & DCSB_CS_ENABLE) 276807ed82efSYazen Ghannam /* 276907ed82efSYazen Ghannam * For F15m60h, we need multiplier for LRDIMM cs_size 277007ed82efSYazen Ghannam * calculation. We pass dimm value to the dbam_to_cs 2771a597d2a5SAravind Gopalakrishnan * mapper so we can find the multiplier from the 2772a597d2a5SAravind Gopalakrishnan * corresponding DCSM. 2773a597d2a5SAravind Gopalakrishnan */ 277441d8bfabSBorislav Petkov size0 = pvt->ops->dbam_to_cs(pvt, ctrl, 2775a597d2a5SAravind Gopalakrishnan DBAM_DIMM(dimm, dbam), 2776a597d2a5SAravind Gopalakrishnan dimm); 2777f71d0a05SDoug Thompson 2778f71d0a05SDoug Thompson size1 = 0; 277911c75eadSBorislav Petkov if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE) 278041d8bfabSBorislav Petkov size1 = pvt->ops->dbam_to_cs(pvt, ctrl, 2781a597d2a5SAravind Gopalakrishnan DBAM_DIMM(dimm, dbam), 2782a597d2a5SAravind Gopalakrishnan dimm); 2783f71d0a05SDoug Thompson 278424f9a7feSBorislav Petkov amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n", 2785bb89f5a0SBorislav Petkov dimm * 2, size0, 2786bb89f5a0SBorislav Petkov dimm * 2 + 1, size1); 2787f71d0a05SDoug Thompson } 2788f71d0a05SDoug Thompson } 2789f71d0a05SDoug Thompson 2790d1ea71cdSBorislav Petkov static struct amd64_family_type family_types[] = { 27914d37607aSDoug Thompson [K8_CPUS] = { 27920092b20dSBorislav Petkov .ctl_name = "K8", 27938d5b5d9cSBorislav Petkov .f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP, 27943f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL, 27955e4c5527SYazen Ghannam .max_mcs = 2, 27964d37607aSDoug Thompson .ops = { 27974d37607aSDoug Thompson .early_channel_count = k8_early_channel_count, 27984d37607aSDoug Thompson .map_sysaddr_to_csrow = k8_map_sysaddr_to_csrow, 27991433eb99SBorislav Petkov .dbam_to_cs = k8_dbam_to_chip_select, 28004d37607aSDoug Thompson } 28014d37607aSDoug Thompson }, 28024d37607aSDoug Thompson [F10_CPUS] = { 28030092b20dSBorislav Petkov .ctl_name = "F10h", 28048d5b5d9cSBorislav Petkov .f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP, 28053f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM, 28065e4c5527SYazen Ghannam .max_mcs = 2, 28074d37607aSDoug Thompson .ops = { 28087d20d14dSBorislav Petkov .early_channel_count = f1x_early_channel_count, 2809b15f0fcaSBorislav Petkov .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 28101433eb99SBorislav Petkov .dbam_to_cs = f10_dbam_to_chip_select, 2811b2b0c605SBorislav Petkov } 2812b2b0c605SBorislav Petkov }, 2813b2b0c605SBorislav Petkov [F15_CPUS] = { 2814b2b0c605SBorislav Petkov .ctl_name = "F15h", 2815df71a053SBorislav Petkov .f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1, 28163f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2, 28175e4c5527SYazen Ghannam .max_mcs = 2, 2818b2b0c605SBorislav Petkov .ops = { 28197d20d14dSBorislav Petkov .early_channel_count = f1x_early_channel_count, 2820b15f0fcaSBorislav Petkov .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 282141d8bfabSBorislav Petkov .dbam_to_cs = f15_dbam_to_chip_select, 28224d37607aSDoug Thompson } 28234d37607aSDoug Thompson }, 282418b94f66SAravind Gopalakrishnan [F15_M30H_CPUS] = { 282518b94f66SAravind Gopalakrishnan .ctl_name = "F15h_M30h", 282618b94f66SAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1, 28273f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2, 28285e4c5527SYazen Ghannam .max_mcs = 2, 282918b94f66SAravind Gopalakrishnan .ops = { 283018b94f66SAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 283118b94f66SAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 283218b94f66SAravind Gopalakrishnan .dbam_to_cs = f16_dbam_to_chip_select, 283318b94f66SAravind Gopalakrishnan } 283418b94f66SAravind Gopalakrishnan }, 2835a597d2a5SAravind Gopalakrishnan [F15_M60H_CPUS] = { 2836a597d2a5SAravind Gopalakrishnan .ctl_name = "F15h_M60h", 2837a597d2a5SAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1, 28383f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2, 28395e4c5527SYazen Ghannam .max_mcs = 2, 2840a597d2a5SAravind Gopalakrishnan .ops = { 2841a597d2a5SAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 2842a597d2a5SAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 2843a597d2a5SAravind Gopalakrishnan .dbam_to_cs = f15_m60h_dbam_to_chip_select, 2844a597d2a5SAravind Gopalakrishnan } 2845a597d2a5SAravind Gopalakrishnan }, 284694c1acf2SAravind Gopalakrishnan [F16_CPUS] = { 284794c1acf2SAravind Gopalakrishnan .ctl_name = "F16h", 284894c1acf2SAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1, 28493f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2, 28505e4c5527SYazen Ghannam .max_mcs = 2, 285194c1acf2SAravind Gopalakrishnan .ops = { 285294c1acf2SAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 285394c1acf2SAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 285494c1acf2SAravind Gopalakrishnan .dbam_to_cs = f16_dbam_to_chip_select, 285594c1acf2SAravind Gopalakrishnan } 285694c1acf2SAravind Gopalakrishnan }, 285785a8885bSAravind Gopalakrishnan [F16_M30H_CPUS] = { 285885a8885bSAravind Gopalakrishnan .ctl_name = "F16h_M30h", 285985a8885bSAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F1, 28603f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2, 28615e4c5527SYazen Ghannam .max_mcs = 2, 286285a8885bSAravind Gopalakrishnan .ops = { 286385a8885bSAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 286485a8885bSAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 286585a8885bSAravind Gopalakrishnan .dbam_to_cs = f16_dbam_to_chip_select, 286685a8885bSAravind Gopalakrishnan } 286785a8885bSAravind Gopalakrishnan }, 2868f1cbbec9SYazen Ghannam [F17_CPUS] = { 2869f1cbbec9SYazen Ghannam .ctl_name = "F17h", 2870f1cbbec9SYazen Ghannam .f0_id = PCI_DEVICE_ID_AMD_17H_DF_F0, 2871f1cbbec9SYazen Ghannam .f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6, 28725e4c5527SYazen Ghannam .max_mcs = 2, 2873f1cbbec9SYazen Ghannam .ops = { 2874f1cbbec9SYazen Ghannam .early_channel_count = f17_early_channel_count, 2875e53a3b26SYazen Ghannam .dbam_to_cs = f17_addr_mask_to_cs_size, 2876f1cbbec9SYazen Ghannam } 2877f1cbbec9SYazen Ghannam }, 28788960de4aSMichael Jin [F17_M10H_CPUS] = { 28798960de4aSMichael Jin .ctl_name = "F17h_M10h", 28808960de4aSMichael Jin .f0_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F0, 28818960de4aSMichael Jin .f6_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F6, 28825e4c5527SYazen Ghannam .max_mcs = 2, 28838960de4aSMichael Jin .ops = { 28848960de4aSMichael Jin .early_channel_count = f17_early_channel_count, 2885e53a3b26SYazen Ghannam .dbam_to_cs = f17_addr_mask_to_cs_size, 28868960de4aSMichael Jin } 28878960de4aSMichael Jin }, 28886e846239SYazen Ghannam [F17_M30H_CPUS] = { 28896e846239SYazen Ghannam .ctl_name = "F17h_M30h", 28906e846239SYazen Ghannam .f0_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F0, 28916e846239SYazen Ghannam .f6_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F6, 28925e4c5527SYazen Ghannam .max_mcs = 8, 28936e846239SYazen Ghannam .ops = { 28946e846239SYazen Ghannam .early_channel_count = f17_early_channel_count, 2895e53a3b26SYazen Ghannam .dbam_to_cs = f17_addr_mask_to_cs_size, 28966e846239SYazen Ghannam } 28976e846239SYazen Ghannam }, 2898b6bea24dSAlexander Monakov [F17_M60H_CPUS] = { 2899b6bea24dSAlexander Monakov .ctl_name = "F17h_M60h", 2900b6bea24dSAlexander Monakov .f0_id = PCI_DEVICE_ID_AMD_17H_M60H_DF_F0, 2901b6bea24dSAlexander Monakov .f6_id = PCI_DEVICE_ID_AMD_17H_M60H_DF_F6, 2902b6bea24dSAlexander Monakov .max_mcs = 2, 2903b6bea24dSAlexander Monakov .ops = { 2904b6bea24dSAlexander Monakov .early_channel_count = f17_early_channel_count, 2905b6bea24dSAlexander Monakov .dbam_to_cs = f17_addr_mask_to_cs_size, 2906b6bea24dSAlexander Monakov } 2907b6bea24dSAlexander Monakov }, 29083e443eb3SIsaac Vaughn [F17_M70H_CPUS] = { 29093e443eb3SIsaac Vaughn .ctl_name = "F17h_M70h", 29103e443eb3SIsaac Vaughn .f0_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F0, 29113e443eb3SIsaac Vaughn .f6_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F6, 29125e4c5527SYazen Ghannam .max_mcs = 2, 29133e443eb3SIsaac Vaughn .ops = { 29143e443eb3SIsaac Vaughn .early_channel_count = f17_early_channel_count, 29153e443eb3SIsaac Vaughn .dbam_to_cs = f17_addr_mask_to_cs_size, 29163e443eb3SIsaac Vaughn } 29173e443eb3SIsaac Vaughn }, 29182eb61c91SYazen Ghannam [F19_CPUS] = { 29192eb61c91SYazen Ghannam .ctl_name = "F19h", 29202eb61c91SYazen Ghannam .f0_id = PCI_DEVICE_ID_AMD_19H_DF_F0, 29212eb61c91SYazen Ghannam .f6_id = PCI_DEVICE_ID_AMD_19H_DF_F6, 29222eb61c91SYazen Ghannam .max_mcs = 8, 29232eb61c91SYazen Ghannam .ops = { 29242eb61c91SYazen Ghannam .early_channel_count = f17_early_channel_count, 29252eb61c91SYazen Ghannam .dbam_to_cs = f17_addr_mask_to_cs_size, 29262eb61c91SYazen Ghannam } 29272eb61c91SYazen Ghannam }, 29284d37607aSDoug Thompson }; 29294d37607aSDoug Thompson 2930b1289d6fSDoug Thompson /* 2931bfc04aecSBorislav Petkov * These are tables of eigenvectors (one per line) which can be used for the 2932bfc04aecSBorislav Petkov * construction of the syndrome tables. The modified syndrome search algorithm 2933bfc04aecSBorislav Petkov * uses those to find the symbol in error and thus the DIMM. 2934b1289d6fSDoug Thompson * 2935bfc04aecSBorislav Petkov * Algorithm courtesy of Ross LaFetra from AMD. 2936b1289d6fSDoug Thompson */ 2937c7e5301aSDaniel J Blueman static const u16 x4_vectors[] = { 2938bfc04aecSBorislav Petkov 0x2f57, 0x1afe, 0x66cc, 0xdd88, 2939bfc04aecSBorislav Petkov 0x11eb, 0x3396, 0x7f4c, 0xeac8, 2940bfc04aecSBorislav Petkov 0x0001, 0x0002, 0x0004, 0x0008, 2941bfc04aecSBorislav Petkov 0x1013, 0x3032, 0x4044, 0x8088, 2942bfc04aecSBorislav Petkov 0x106b, 0x30d6, 0x70fc, 0xe0a8, 2943bfc04aecSBorislav Petkov 0x4857, 0xc4fe, 0x13cc, 0x3288, 2944bfc04aecSBorislav Petkov 0x1ac5, 0x2f4a, 0x5394, 0xa1e8, 2945bfc04aecSBorislav Petkov 0x1f39, 0x251e, 0xbd6c, 0x6bd8, 2946bfc04aecSBorislav Petkov 0x15c1, 0x2a42, 0x89ac, 0x4758, 2947bfc04aecSBorislav Petkov 0x2b03, 0x1602, 0x4f0c, 0xca08, 2948bfc04aecSBorislav Petkov 0x1f07, 0x3a0e, 0x6b04, 0xbd08, 2949bfc04aecSBorislav Petkov 0x8ba7, 0x465e, 0x244c, 0x1cc8, 2950bfc04aecSBorislav Petkov 0x2b87, 0x164e, 0x642c, 0xdc18, 2951bfc04aecSBorislav Petkov 0x40b9, 0x80de, 0x1094, 0x20e8, 2952bfc04aecSBorislav Petkov 0x27db, 0x1eb6, 0x9dac, 0x7b58, 2953bfc04aecSBorislav Petkov 0x11c1, 0x2242, 0x84ac, 0x4c58, 2954bfc04aecSBorislav Petkov 0x1be5, 0x2d7a, 0x5e34, 0xa718, 2955bfc04aecSBorislav Petkov 0x4b39, 0x8d1e, 0x14b4, 0x28d8, 2956bfc04aecSBorislav Petkov 0x4c97, 0xc87e, 0x11fc, 0x33a8, 2957bfc04aecSBorislav Petkov 0x8e97, 0x497e, 0x2ffc, 0x1aa8, 2958bfc04aecSBorislav Petkov 0x16b3, 0x3d62, 0x4f34, 0x8518, 2959bfc04aecSBorislav Petkov 0x1e2f, 0x391a, 0x5cac, 0xf858, 2960bfc04aecSBorislav Petkov 0x1d9f, 0x3b7a, 0x572c, 0xfe18, 2961bfc04aecSBorislav Petkov 0x15f5, 0x2a5a, 0x5264, 0xa3b8, 2962bfc04aecSBorislav Petkov 0x1dbb, 0x3b66, 0x715c, 0xe3f8, 2963bfc04aecSBorislav Petkov 0x4397, 0xc27e, 0x17fc, 0x3ea8, 2964bfc04aecSBorislav Petkov 0x1617, 0x3d3e, 0x6464, 0xb8b8, 2965bfc04aecSBorislav Petkov 0x23ff, 0x12aa, 0xab6c, 0x56d8, 2966bfc04aecSBorislav Petkov 0x2dfb, 0x1ba6, 0x913c, 0x7328, 2967bfc04aecSBorislav Petkov 0x185d, 0x2ca6, 0x7914, 0x9e28, 2968bfc04aecSBorislav Petkov 0x171b, 0x3e36, 0x7d7c, 0xebe8, 2969bfc04aecSBorislav Petkov 0x4199, 0x82ee, 0x19f4, 0x2e58, 2970bfc04aecSBorislav Petkov 0x4807, 0xc40e, 0x130c, 0x3208, 2971bfc04aecSBorislav Petkov 0x1905, 0x2e0a, 0x5804, 0xac08, 2972bfc04aecSBorislav Petkov 0x213f, 0x132a, 0xadfc, 0x5ba8, 2973bfc04aecSBorislav Petkov 0x19a9, 0x2efe, 0xb5cc, 0x6f88, 2974b1289d6fSDoug Thompson }; 2975b1289d6fSDoug Thompson 2976c7e5301aSDaniel J Blueman static const u16 x8_vectors[] = { 2977bfc04aecSBorislav Petkov 0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480, 2978bfc04aecSBorislav Petkov 0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80, 2979bfc04aecSBorislav Petkov 0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80, 2980bfc04aecSBorislav Petkov 0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80, 2981bfc04aecSBorislav Petkov 0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780, 2982bfc04aecSBorislav Petkov 0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080, 2983bfc04aecSBorislav Petkov 0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080, 2984bfc04aecSBorislav Petkov 0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080, 2985bfc04aecSBorislav Petkov 0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80, 2986bfc04aecSBorislav Petkov 0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580, 2987bfc04aecSBorislav Petkov 0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880, 2988bfc04aecSBorislav Petkov 0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280, 2989bfc04aecSBorislav Petkov 0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180, 2990bfc04aecSBorislav Petkov 0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580, 2991bfc04aecSBorislav Petkov 0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280, 2992bfc04aecSBorislav Petkov 0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180, 2993bfc04aecSBorislav Petkov 0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080, 2994bfc04aecSBorislav Petkov 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 2995bfc04aecSBorislav Petkov 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000, 2996bfc04aecSBorislav Petkov }; 2997bfc04aecSBorislav Petkov 2998c7e5301aSDaniel J Blueman static int decode_syndrome(u16 syndrome, const u16 *vectors, unsigned num_vecs, 2999d34a6ecdSBorislav Petkov unsigned v_dim) 3000b1289d6fSDoug Thompson { 3001bfc04aecSBorislav Petkov unsigned int i, err_sym; 3002b1289d6fSDoug Thompson 3003bfc04aecSBorislav Petkov for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) { 3004bfc04aecSBorislav Petkov u16 s = syndrome; 3005d34a6ecdSBorislav Petkov unsigned v_idx = err_sym * v_dim; 3006d34a6ecdSBorislav Petkov unsigned v_end = (err_sym + 1) * v_dim; 3007b1289d6fSDoug Thompson 3008bfc04aecSBorislav Petkov /* walk over all 16 bits of the syndrome */ 3009bfc04aecSBorislav Petkov for (i = 1; i < (1U << 16); i <<= 1) { 3010bfc04aecSBorislav Petkov 3011bfc04aecSBorislav Petkov /* if bit is set in that eigenvector... */ 3012bfc04aecSBorislav Petkov if (v_idx < v_end && vectors[v_idx] & i) { 3013bfc04aecSBorislav Petkov u16 ev_comp = vectors[v_idx++]; 3014bfc04aecSBorislav Petkov 3015bfc04aecSBorislav Petkov /* ... and bit set in the modified syndrome, */ 3016bfc04aecSBorislav Petkov if (s & i) { 3017bfc04aecSBorislav Petkov /* remove it. */ 3018bfc04aecSBorislav Petkov s ^= ev_comp; 3019bfc04aecSBorislav Petkov 3020bfc04aecSBorislav Petkov if (!s) 3021bfc04aecSBorislav Petkov return err_sym; 3022bfc04aecSBorislav Petkov } 3023bfc04aecSBorislav Petkov 3024bfc04aecSBorislav Petkov } else if (s & i) 3025bfc04aecSBorislav Petkov /* can't get to zero, move to next symbol */ 3026bfc04aecSBorislav Petkov break; 3027bfc04aecSBorislav Petkov } 3028b1289d6fSDoug Thompson } 3029b1289d6fSDoug Thompson 3030956b9ba1SJoe Perches edac_dbg(0, "syndrome(%x) not found\n", syndrome); 3031b1289d6fSDoug Thompson return -1; 3032b1289d6fSDoug Thompson } 3033d27bf6faSDoug Thompson 3034bfc04aecSBorislav Petkov static int map_err_sym_to_channel(int err_sym, int sym_size) 3035bfc04aecSBorislav Petkov { 3036bfc04aecSBorislav Petkov if (sym_size == 4) 3037bfc04aecSBorislav Petkov switch (err_sym) { 3038bfc04aecSBorislav Petkov case 0x20: 3039bfc04aecSBorislav Petkov case 0x21: 3040bfc04aecSBorislav Petkov return 0; 3041bfc04aecSBorislav Petkov case 0x22: 3042bfc04aecSBorislav Petkov case 0x23: 3043bfc04aecSBorislav Petkov return 1; 3044bfc04aecSBorislav Petkov default: 3045bfc04aecSBorislav Petkov return err_sym >> 4; 3046bfc04aecSBorislav Petkov } 3047bfc04aecSBorislav Petkov /* x8 symbols */ 3048bfc04aecSBorislav Petkov else 3049bfc04aecSBorislav Petkov switch (err_sym) { 3050bfc04aecSBorislav Petkov /* imaginary bits not in a DIMM */ 3051bfc04aecSBorislav Petkov case 0x10: 3052bfc04aecSBorislav Petkov WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n", 3053bfc04aecSBorislav Petkov err_sym); 3054bfc04aecSBorislav Petkov return -1; 3055bfc04aecSBorislav Petkov case 0x11: 3056bfc04aecSBorislav Petkov return 0; 3057bfc04aecSBorislav Petkov case 0x12: 3058bfc04aecSBorislav Petkov return 1; 3059bfc04aecSBorislav Petkov default: 3060bfc04aecSBorislav Petkov return err_sym >> 3; 3061bfc04aecSBorislav Petkov } 3062bfc04aecSBorislav Petkov return -1; 3063bfc04aecSBorislav Petkov } 3064bfc04aecSBorislav Petkov 3065bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome) 3066bfc04aecSBorislav Petkov { 3067bfc04aecSBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 3068ad6a32e9SBorislav Petkov int err_sym = -1; 3069bfc04aecSBorislav Petkov 3070a3b7db09SBorislav Petkov if (pvt->ecc_sym_sz == 8) 3071bfc04aecSBorislav Petkov err_sym = decode_syndrome(syndrome, x8_vectors, 3072ad6a32e9SBorislav Petkov ARRAY_SIZE(x8_vectors), 3073a3b7db09SBorislav Petkov pvt->ecc_sym_sz); 3074a3b7db09SBorislav Petkov else if (pvt->ecc_sym_sz == 4) 3075ad6a32e9SBorislav Petkov err_sym = decode_syndrome(syndrome, x4_vectors, 3076ad6a32e9SBorislav Petkov ARRAY_SIZE(x4_vectors), 3077a3b7db09SBorislav Petkov pvt->ecc_sym_sz); 3078ad6a32e9SBorislav Petkov else { 3079a3b7db09SBorislav Petkov amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz); 3080ad6a32e9SBorislav Petkov return err_sym; 3081bfc04aecSBorislav Petkov } 3082ad6a32e9SBorislav Petkov 3083a3b7db09SBorislav Petkov return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz); 308441c31044SBorislav Petkov } 3085bfc04aecSBorislav Petkov 3086e70984d9SYazen Ghannam static void __log_ecc_error(struct mem_ctl_info *mci, struct err_info *err, 308733ca0643SBorislav Petkov u8 ecc_type) 3088d27bf6faSDoug Thompson { 308933ca0643SBorislav Petkov enum hw_event_mc_err_type err_type; 309033ca0643SBorislav Petkov const char *string; 3091d27bf6faSDoug Thompson 309233ca0643SBorislav Petkov if (ecc_type == 2) 309333ca0643SBorislav Petkov err_type = HW_EVENT_ERR_CORRECTED; 309433ca0643SBorislav Petkov else if (ecc_type == 1) 309533ca0643SBorislav Petkov err_type = HW_EVENT_ERR_UNCORRECTED; 3096d12a969eSYazen Ghannam else if (ecc_type == 3) 3097d12a969eSYazen Ghannam err_type = HW_EVENT_ERR_DEFERRED; 309833ca0643SBorislav Petkov else { 309933ca0643SBorislav Petkov WARN(1, "Something is rotten in the state of Denmark.\n"); 3100d27bf6faSDoug Thompson return; 3101d27bf6faSDoug Thompson } 3102d27bf6faSDoug Thompson 310333ca0643SBorislav Petkov switch (err->err_code) { 310433ca0643SBorislav Petkov case DECODE_OK: 310533ca0643SBorislav Petkov string = ""; 310633ca0643SBorislav Petkov break; 310733ca0643SBorislav Petkov case ERR_NODE: 310833ca0643SBorislav Petkov string = "Failed to map error addr to a node"; 310933ca0643SBorislav Petkov break; 311033ca0643SBorislav Petkov case ERR_CSROW: 311133ca0643SBorislav Petkov string = "Failed to map error addr to a csrow"; 311233ca0643SBorislav Petkov break; 311333ca0643SBorislav Petkov case ERR_CHANNEL: 3114713ad546SYazen Ghannam string = "Unknown syndrome - possible error reporting race"; 3115713ad546SYazen Ghannam break; 3116713ad546SYazen Ghannam case ERR_SYND: 3117713ad546SYazen Ghannam string = "MCA_SYND not valid - unknown syndrome and csrow"; 3118713ad546SYazen Ghannam break; 3119713ad546SYazen Ghannam case ERR_NORM_ADDR: 3120713ad546SYazen Ghannam string = "Cannot decode normalized address"; 312133ca0643SBorislav Petkov break; 312233ca0643SBorislav Petkov default: 312333ca0643SBorislav Petkov string = "WTF error"; 312433ca0643SBorislav Petkov break; 3125d27bf6faSDoug Thompson } 312633ca0643SBorislav Petkov 312733ca0643SBorislav Petkov edac_mc_handle_error(err_type, mci, 1, 312833ca0643SBorislav Petkov err->page, err->offset, err->syndrome, 312933ca0643SBorislav Petkov err->csrow, err->channel, -1, 313033ca0643SBorislav Petkov string, ""); 3131d27bf6faSDoug Thompson } 3132d27bf6faSDoug Thompson 3133df781d03SBorislav Petkov static inline void decode_bus_error(int node_id, struct mce *m) 3134d27bf6faSDoug Thompson { 31350c510cc8SDaniel J Blueman struct mem_ctl_info *mci; 31360c510cc8SDaniel J Blueman struct amd64_pvt *pvt; 3137f192c7b1SBorislav Petkov u8 ecc_type = (m->status >> 45) & 0x3; 313866fed2d4SBorislav Petkov u8 xec = XEC(m->status, 0x1f); 313966fed2d4SBorislav Petkov u16 ec = EC(m->status); 314033ca0643SBorislav Petkov u64 sys_addr; 314133ca0643SBorislav Petkov struct err_info err; 3142d27bf6faSDoug Thompson 31430c510cc8SDaniel J Blueman mci = edac_mc_find(node_id); 31440c510cc8SDaniel J Blueman if (!mci) 31450c510cc8SDaniel J Blueman return; 31460c510cc8SDaniel J Blueman 31470c510cc8SDaniel J Blueman pvt = mci->pvt_info; 31480c510cc8SDaniel J Blueman 314966fed2d4SBorislav Petkov /* Bail out early if this was an 'observed' error */ 31505980bb9cSBorislav Petkov if (PP(ec) == NBSL_PP_OBS) 3151b70ef010SBorislav Petkov return; 3152d27bf6faSDoug Thompson 3153ecaf5606SBorislav Petkov /* Do only ECC errors */ 3154ecaf5606SBorislav Petkov if (xec && xec != F10_NBSL_EXT_ERR_ECC) 3155d27bf6faSDoug Thompson return; 3156d27bf6faSDoug Thompson 315733ca0643SBorislav Petkov memset(&err, 0, sizeof(err)); 315833ca0643SBorislav Petkov 3159a4b4bedcSBorislav Petkov sys_addr = get_error_address(pvt, m); 316033ca0643SBorislav Petkov 3161ecaf5606SBorislav Petkov if (ecc_type == 2) 316233ca0643SBorislav Petkov err.syndrome = extract_syndrome(m->status); 316333ca0643SBorislav Petkov 316433ca0643SBorislav Petkov pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err); 316533ca0643SBorislav Petkov 3166e70984d9SYazen Ghannam __log_ecc_error(mci, &err, ecc_type); 3167d27bf6faSDoug Thompson } 3168d27bf6faSDoug Thompson 31690ec449eeSDoug Thompson /* 3170713ad546SYazen Ghannam * To find the UMC channel represented by this bank we need to match on its 3171713ad546SYazen Ghannam * instance_id. The instance_id of a bank is held in the lower 32 bits of its 3172713ad546SYazen Ghannam * IPID. 3173bdcee774SYazen Ghannam * 3174bdcee774SYazen Ghannam * Currently, we can derive the channel number by looking at the 6th nibble in 3175bdcee774SYazen Ghannam * the instance_id. For example, instance_id=0xYXXXXX where Y is the channel 3176bdcee774SYazen Ghannam * number. 3177713ad546SYazen Ghannam */ 3178bdcee774SYazen Ghannam static int find_umc_channel(struct mce *m) 3179713ad546SYazen Ghannam { 3180bdcee774SYazen Ghannam return (m->ipid & GENMASK(31, 0)) >> 20; 3181713ad546SYazen Ghannam } 3182713ad546SYazen Ghannam 3183713ad546SYazen Ghannam static void decode_umc_error(int node_id, struct mce *m) 3184713ad546SYazen Ghannam { 3185713ad546SYazen Ghannam u8 ecc_type = (m->status >> 45) & 0x3; 3186713ad546SYazen Ghannam struct mem_ctl_info *mci; 3187713ad546SYazen Ghannam struct amd64_pvt *pvt; 3188713ad546SYazen Ghannam struct err_info err; 3189713ad546SYazen Ghannam u64 sys_addr; 3190713ad546SYazen Ghannam 3191713ad546SYazen Ghannam mci = edac_mc_find(node_id); 3192713ad546SYazen Ghannam if (!mci) 3193713ad546SYazen Ghannam return; 3194713ad546SYazen Ghannam 3195713ad546SYazen Ghannam pvt = mci->pvt_info; 3196713ad546SYazen Ghannam 3197713ad546SYazen Ghannam memset(&err, 0, sizeof(err)); 3198713ad546SYazen Ghannam 3199713ad546SYazen Ghannam if (m->status & MCI_STATUS_DEFERRED) 3200713ad546SYazen Ghannam ecc_type = 3; 3201713ad546SYazen Ghannam 3202bdcee774SYazen Ghannam err.channel = find_umc_channel(m); 3203713ad546SYazen Ghannam 3204713ad546SYazen Ghannam if (!(m->status & MCI_STATUS_SYNDV)) { 3205713ad546SYazen Ghannam err.err_code = ERR_SYND; 3206713ad546SYazen Ghannam goto log_error; 3207713ad546SYazen Ghannam } 3208713ad546SYazen Ghannam 3209713ad546SYazen Ghannam if (ecc_type == 2) { 3210713ad546SYazen Ghannam u8 length = (m->synd >> 18) & 0x3f; 3211713ad546SYazen Ghannam 3212713ad546SYazen Ghannam if (length) 3213713ad546SYazen Ghannam err.syndrome = (m->synd >> 32) & GENMASK(length - 1, 0); 3214713ad546SYazen Ghannam else 3215713ad546SYazen Ghannam err.err_code = ERR_CHANNEL; 3216713ad546SYazen Ghannam } 3217713ad546SYazen Ghannam 3218713ad546SYazen Ghannam err.csrow = m->synd & 0x7; 3219713ad546SYazen Ghannam 32208a2eaab7SYazen Ghannam if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) { 32218a2eaab7SYazen Ghannam err.err_code = ERR_NORM_ADDR; 32228a2eaab7SYazen Ghannam goto log_error; 32238a2eaab7SYazen Ghannam } 32248a2eaab7SYazen Ghannam 32258a2eaab7SYazen Ghannam error_address_to_page_and_offset(sys_addr, &err); 32268a2eaab7SYazen Ghannam 3227713ad546SYazen Ghannam log_error: 3228713ad546SYazen Ghannam __log_ecc_error(mci, &err, ecc_type); 3229713ad546SYazen Ghannam } 3230713ad546SYazen Ghannam 3231713ad546SYazen Ghannam /* 32323f37a36bSBorislav Petkov * Use pvt->F3 which contains the F3 CPU PCI device to get the related 32333f37a36bSBorislav Petkov * F1 (AddrMap) and F2 (Dct) devices. Return negative value on error. 3234936fc3afSYazen Ghannam * Reserve F0 and F6 on systems with a UMC. 32350ec449eeSDoug Thompson */ 3236936fc3afSYazen Ghannam static int 3237936fc3afSYazen Ghannam reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2) 32380ec449eeSDoug Thompson { 3239936fc3afSYazen Ghannam if (pvt->umc) { 3240936fc3afSYazen Ghannam pvt->F0 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3); 3241936fc3afSYazen Ghannam if (!pvt->F0) { 32426a4afe38SYazen Ghannam edac_dbg(1, "F0 not found, device 0x%x\n", pci_id1); 3243936fc3afSYazen Ghannam return -ENODEV; 3244936fc3afSYazen Ghannam } 3245936fc3afSYazen Ghannam 3246936fc3afSYazen Ghannam pvt->F6 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3); 3247936fc3afSYazen Ghannam if (!pvt->F6) { 3248936fc3afSYazen Ghannam pci_dev_put(pvt->F0); 3249936fc3afSYazen Ghannam pvt->F0 = NULL; 3250936fc3afSYazen Ghannam 32516a4afe38SYazen Ghannam edac_dbg(1, "F6 not found: device 0x%x\n", pci_id2); 3252936fc3afSYazen Ghannam return -ENODEV; 3253936fc3afSYazen Ghannam } 32545246c540SBorislav Petkov 3255706657b1SBorislav Petkov if (!pci_ctl_dev) 3256706657b1SBorislav Petkov pci_ctl_dev = &pvt->F0->dev; 3257706657b1SBorislav Petkov 3258936fc3afSYazen Ghannam edac_dbg(1, "F0: %s\n", pci_name(pvt->F0)); 3259936fc3afSYazen Ghannam edac_dbg(1, "F3: %s\n", pci_name(pvt->F3)); 3260936fc3afSYazen Ghannam edac_dbg(1, "F6: %s\n", pci_name(pvt->F6)); 3261936fc3afSYazen Ghannam 3262936fc3afSYazen Ghannam return 0; 3263936fc3afSYazen Ghannam } 3264936fc3afSYazen Ghannam 32650ec449eeSDoug Thompson /* Reserve the ADDRESS MAP Device */ 3266936fc3afSYazen Ghannam pvt->F1 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3); 32678d5b5d9cSBorislav Petkov if (!pvt->F1) { 32686a4afe38SYazen Ghannam edac_dbg(1, "F1 not found: device 0x%x\n", pci_id1); 3269bbd0c1f6SBorislav Petkov return -ENODEV; 32700ec449eeSDoug Thompson } 32710ec449eeSDoug Thompson 32723f37a36bSBorislav Petkov /* Reserve the DCT Device */ 3273936fc3afSYazen Ghannam pvt->F2 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3); 32743f37a36bSBorislav Petkov if (!pvt->F2) { 32758d5b5d9cSBorislav Petkov pci_dev_put(pvt->F1); 32768d5b5d9cSBorislav Petkov pvt->F1 = NULL; 32770ec449eeSDoug Thompson 32786a4afe38SYazen Ghannam edac_dbg(1, "F2 not found: device 0x%x\n", pci_id2); 3279bbd0c1f6SBorislav Petkov return -ENODEV; 32800ec449eeSDoug Thompson } 3281936fc3afSYazen Ghannam 3282706657b1SBorislav Petkov if (!pci_ctl_dev) 3283706657b1SBorislav Petkov pci_ctl_dev = &pvt->F2->dev; 3284706657b1SBorislav Petkov 3285956b9ba1SJoe Perches edac_dbg(1, "F1: %s\n", pci_name(pvt->F1)); 3286956b9ba1SJoe Perches edac_dbg(1, "F2: %s\n", pci_name(pvt->F2)); 3287956b9ba1SJoe Perches edac_dbg(1, "F3: %s\n", pci_name(pvt->F3)); 32880ec449eeSDoug Thompson 32890ec449eeSDoug Thompson return 0; 32900ec449eeSDoug Thompson } 32910ec449eeSDoug Thompson 3292360b7f3cSBorislav Petkov static void free_mc_sibling_devs(struct amd64_pvt *pvt) 32930ec449eeSDoug Thompson { 3294936fc3afSYazen Ghannam if (pvt->umc) { 3295936fc3afSYazen Ghannam pci_dev_put(pvt->F0); 3296936fc3afSYazen Ghannam pci_dev_put(pvt->F6); 3297936fc3afSYazen Ghannam } else { 32988d5b5d9cSBorislav Petkov pci_dev_put(pvt->F1); 32993f37a36bSBorislav Petkov pci_dev_put(pvt->F2); 33000ec449eeSDoug Thompson } 3301936fc3afSYazen Ghannam } 33020ec449eeSDoug Thompson 3303b64ce7cdSYazen Ghannam static void determine_ecc_sym_sz(struct amd64_pvt *pvt) 3304b64ce7cdSYazen Ghannam { 3305b64ce7cdSYazen Ghannam pvt->ecc_sym_sz = 4; 3306b64ce7cdSYazen Ghannam 3307b64ce7cdSYazen Ghannam if (pvt->umc) { 3308b64ce7cdSYazen Ghannam u8 i; 3309b64ce7cdSYazen Ghannam 33104d30d2bcSYazen Ghannam for_each_umc(i) { 3311b64ce7cdSYazen Ghannam /* Check enabled channels only: */ 33127835961dSYazen Ghannam if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) { 33137835961dSYazen Ghannam if (pvt->umc[i].ecc_ctrl & BIT(9)) { 33147835961dSYazen Ghannam pvt->ecc_sym_sz = 16; 33157835961dSYazen Ghannam return; 33167835961dSYazen Ghannam } else if (pvt->umc[i].ecc_ctrl & BIT(7)) { 3317b64ce7cdSYazen Ghannam pvt->ecc_sym_sz = 8; 3318b64ce7cdSYazen Ghannam return; 3319b64ce7cdSYazen Ghannam } 33207835961dSYazen Ghannam } 33217835961dSYazen Ghannam } 33227835961dSYazen Ghannam } else if (pvt->fam >= 0x10) { 3323b64ce7cdSYazen Ghannam u32 tmp; 3324b64ce7cdSYazen Ghannam 3325b64ce7cdSYazen Ghannam amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp); 3326b64ce7cdSYazen Ghannam /* F16h has only DCT0, so no need to read dbam1. */ 3327b64ce7cdSYazen Ghannam if (pvt->fam != 0x16) 3328b64ce7cdSYazen Ghannam amd64_read_dct_pci_cfg(pvt, 1, DBAM0, &pvt->dbam1); 3329b64ce7cdSYazen Ghannam 3330b64ce7cdSYazen Ghannam /* F10h, revD and later can do x8 ECC too. */ 3331b64ce7cdSYazen Ghannam if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25)) 3332b64ce7cdSYazen Ghannam pvt->ecc_sym_sz = 8; 3333b64ce7cdSYazen Ghannam } 3334b64ce7cdSYazen Ghannam } 3335b64ce7cdSYazen Ghannam 3336b64ce7cdSYazen Ghannam /* 3337b64ce7cdSYazen Ghannam * Retrieve the hardware registers of the memory controller. 3338b64ce7cdSYazen Ghannam */ 3339b64ce7cdSYazen Ghannam static void __read_mc_regs_df(struct amd64_pvt *pvt) 3340b64ce7cdSYazen Ghannam { 3341b64ce7cdSYazen Ghannam u8 nid = pvt->mc_node_id; 3342b64ce7cdSYazen Ghannam struct amd64_umc *umc; 3343b64ce7cdSYazen Ghannam u32 i, umc_base; 3344b64ce7cdSYazen Ghannam 3345b64ce7cdSYazen Ghannam /* Read registers from each UMC */ 33464d30d2bcSYazen Ghannam for_each_umc(i) { 3347b64ce7cdSYazen Ghannam 3348b64ce7cdSYazen Ghannam umc_base = get_umc_base(i); 3349b64ce7cdSYazen Ghannam umc = &pvt->umc[i]; 3350b64ce7cdSYazen Ghannam 335107ed82efSYazen Ghannam amd_smn_read(nid, umc_base + UMCCH_DIMM_CFG, &umc->dimm_cfg); 335207ed82efSYazen Ghannam amd_smn_read(nid, umc_base + UMCCH_UMC_CFG, &umc->umc_cfg); 3353b64ce7cdSYazen Ghannam amd_smn_read(nid, umc_base + UMCCH_SDP_CTRL, &umc->sdp_ctrl); 3354b64ce7cdSYazen Ghannam amd_smn_read(nid, umc_base + UMCCH_ECC_CTRL, &umc->ecc_ctrl); 335507ed82efSYazen Ghannam amd_smn_read(nid, umc_base + UMCCH_UMC_CAP_HI, &umc->umc_cap_hi); 3356b64ce7cdSYazen Ghannam } 3357b64ce7cdSYazen Ghannam } 3358b64ce7cdSYazen Ghannam 33590ec449eeSDoug Thompson /* 33600ec449eeSDoug Thompson * Retrieve the hardware registers of the memory controller (this includes the 33610ec449eeSDoug Thompson * 'Address Map' and 'Misc' device regs) 33620ec449eeSDoug Thompson */ 3363360b7f3cSBorislav Petkov static void read_mc_regs(struct amd64_pvt *pvt) 33640ec449eeSDoug Thompson { 3365b64ce7cdSYazen Ghannam unsigned int range; 33660ec449eeSDoug Thompson u64 msr_val; 33670ec449eeSDoug Thompson 33680ec449eeSDoug Thompson /* 33690ec449eeSDoug Thompson * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since 3370b64ce7cdSYazen Ghannam * those are Read-As-Zero. 33710ec449eeSDoug Thompson */ 3372e97f8bb8SBorislav Petkov rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem); 3373956b9ba1SJoe Perches edac_dbg(0, " TOP_MEM: 0x%016llx\n", pvt->top_mem); 33740ec449eeSDoug Thompson 3375b64ce7cdSYazen Ghannam /* Check first whether TOP_MEM2 is enabled: */ 3376059e5c32SBrijesh Singh rdmsrl(MSR_AMD64_SYSCFG, msr_val); 3377b64ce7cdSYazen Ghannam if (msr_val & BIT(21)) { 3378e97f8bb8SBorislav Petkov rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2); 3379956b9ba1SJoe Perches edac_dbg(0, " TOP_MEM2: 0x%016llx\n", pvt->top_mem2); 3380b64ce7cdSYazen Ghannam } else { 3381956b9ba1SJoe Perches edac_dbg(0, " TOP_MEM2 disabled\n"); 3382b64ce7cdSYazen Ghannam } 3383b64ce7cdSYazen Ghannam 3384b64ce7cdSYazen Ghannam if (pvt->umc) { 3385b64ce7cdSYazen Ghannam __read_mc_regs_df(pvt); 3386b64ce7cdSYazen Ghannam amd64_read_pci_cfg(pvt->F0, DF_DHAR, &pvt->dhar); 3387b64ce7cdSYazen Ghannam 3388b64ce7cdSYazen Ghannam goto skip; 3389b64ce7cdSYazen Ghannam } 33900ec449eeSDoug Thompson 33915980bb9cSBorislav Petkov amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap); 33920ec449eeSDoug Thompson 33935a5d2371SBorislav Petkov read_dram_ctl_register(pvt); 33940ec449eeSDoug Thompson 33957f19bf75SBorislav Petkov for (range = 0; range < DRAM_RANGES; range++) { 33967f19bf75SBorislav Petkov u8 rw; 33970ec449eeSDoug Thompson 33987f19bf75SBorislav Petkov /* read settings for this DRAM range */ 33997f19bf75SBorislav Petkov read_dram_base_limit_regs(pvt, range); 3400e97f8bb8SBorislav Petkov 34017f19bf75SBorislav Petkov rw = dram_rw(pvt, range); 34027f19bf75SBorislav Petkov if (!rw) 34037f19bf75SBorislav Petkov continue; 34047f19bf75SBorislav Petkov 3405956b9ba1SJoe Perches edac_dbg(1, " DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n", 34067f19bf75SBorislav Petkov range, 34077f19bf75SBorislav Petkov get_dram_base(pvt, range), 34087f19bf75SBorislav Petkov get_dram_limit(pvt, range)); 34097f19bf75SBorislav Petkov 3410956b9ba1SJoe Perches edac_dbg(1, " IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n", 34117f19bf75SBorislav Petkov dram_intlv_en(pvt, range) ? "Enabled" : "Disabled", 34127f19bf75SBorislav Petkov (rw & 0x1) ? "R" : "-", 34137f19bf75SBorislav Petkov (rw & 0x2) ? "W" : "-", 34147f19bf75SBorislav Petkov dram_intlv_sel(pvt, range), 34157f19bf75SBorislav Petkov dram_dst_node(pvt, range)); 34160ec449eeSDoug Thompson } 34170ec449eeSDoug Thompson 3418bc21fa57SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar); 34197981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DBAM0, &pvt->dbam0); 34200ec449eeSDoug Thompson 34218d5b5d9cSBorislav Petkov amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare); 34220ec449eeSDoug Thompson 34237981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DCLR0, &pvt->dclr0); 34247981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DCHR0, &pvt->dchr0); 34250ec449eeSDoug Thompson 342678da121eSBorislav Petkov if (!dct_ganging_enabled(pvt)) { 34277981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 1, DCLR0, &pvt->dclr1); 34287981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 1, DCHR0, &pvt->dchr1); 34290ec449eeSDoug Thompson } 3430b2b0c605SBorislav Petkov 3431b64ce7cdSYazen Ghannam skip: 3432b64ce7cdSYazen Ghannam read_dct_base_mask(pvt); 3433b64ce7cdSYazen Ghannam 3434a597d2a5SAravind Gopalakrishnan determine_memory_type(pvt); 3435a597d2a5SAravind Gopalakrishnan edac_dbg(1, " DIMM type: %s\n", edac_mem_types[pvt->dram_type]); 3436a3b7db09SBorislav Petkov 3437b64ce7cdSYazen Ghannam determine_ecc_sym_sz(pvt); 34380ec449eeSDoug Thompson } 34390ec449eeSDoug Thompson 34400ec449eeSDoug Thompson /* 34410ec449eeSDoug Thompson * NOTE: CPU Revision Dependent code 34420ec449eeSDoug Thompson * 34430ec449eeSDoug Thompson * Input: 344411c75eadSBorislav Petkov * @csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1) 34450ec449eeSDoug Thompson * k8 private pointer to --> 34460ec449eeSDoug Thompson * DRAM Bank Address mapping register 34470ec449eeSDoug Thompson * node_id 34480ec449eeSDoug Thompson * DCL register where dual_channel_active is 34490ec449eeSDoug Thompson * 34500ec449eeSDoug Thompson * The DBAM register consists of 4 sets of 4 bits each definitions: 34510ec449eeSDoug Thompson * 34520ec449eeSDoug Thompson * Bits: CSROWs 34530ec449eeSDoug Thompson * 0-3 CSROWs 0 and 1 34540ec449eeSDoug Thompson * 4-7 CSROWs 2 and 3 34550ec449eeSDoug Thompson * 8-11 CSROWs 4 and 5 34560ec449eeSDoug Thompson * 12-15 CSROWs 6 and 7 34570ec449eeSDoug Thompson * 34580ec449eeSDoug Thompson * Values range from: 0 to 15 34590ec449eeSDoug Thompson * The meaning of the values depends on CPU revision and dual-channel state, 34600ec449eeSDoug Thompson * see relevant BKDG more info. 34610ec449eeSDoug Thompson * 34620ec449eeSDoug Thompson * The memory controller provides for total of only 8 CSROWs in its current 34630ec449eeSDoug Thompson * architecture. Each "pair" of CSROWs normally represents just one DIMM in 34640ec449eeSDoug Thompson * single channel or two (2) DIMMs in dual channel mode. 34650ec449eeSDoug Thompson * 34660ec449eeSDoug Thompson * The following code logic collapses the various tables for CSROW based on CPU 34670ec449eeSDoug Thompson * revision. 34680ec449eeSDoug Thompson * 34690ec449eeSDoug Thompson * Returns: 34700ec449eeSDoug Thompson * The number of PAGE_SIZE pages on the specified CSROW number it 34710ec449eeSDoug Thompson * encompasses 34720ec449eeSDoug Thompson * 34730ec449eeSDoug Thompson */ 3474eb77e6b8SYazen Ghannam static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr_orig) 34750ec449eeSDoug Thompson { 3476f92cae45SAshish Shenoy u32 dbam = dct ? pvt->dbam1 : pvt->dbam0; 3477eb77e6b8SYazen Ghannam int csrow_nr = csrow_nr_orig; 3478eb77e6b8SYazen Ghannam u32 cs_mode, nr_pages; 34790ec449eeSDoug Thompson 3480e53a3b26SYazen Ghannam if (!pvt->umc) { 3481eb77e6b8SYazen Ghannam csrow_nr >>= 1; 3482eb77e6b8SYazen Ghannam cs_mode = DBAM_DIMM(csrow_nr, dbam); 3483e53a3b26SYazen Ghannam } else { 3484e53a3b26SYazen Ghannam cs_mode = f17_get_cs_mode(csrow_nr >> 1, dct, pvt); 3485e53a3b26SYazen Ghannam } 34860ec449eeSDoug Thompson 3487eb77e6b8SYazen Ghannam nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, csrow_nr); 3488eb77e6b8SYazen Ghannam nr_pages <<= 20 - PAGE_SHIFT; 34890ec449eeSDoug Thompson 349010de6497SBorislav Petkov edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n", 3491eb77e6b8SYazen Ghannam csrow_nr_orig, dct, cs_mode); 349210de6497SBorislav Petkov edac_dbg(0, "nr_pages/channel: %u\n", nr_pages); 34930ec449eeSDoug Thompson 34940ec449eeSDoug Thompson return nr_pages; 34950ec449eeSDoug Thompson } 34960ec449eeSDoug Thompson 3497353a1fcbSYazen Ghannam static int init_csrows_df(struct mem_ctl_info *mci) 3498353a1fcbSYazen Ghannam { 3499353a1fcbSYazen Ghannam struct amd64_pvt *pvt = mci->pvt_info; 3500353a1fcbSYazen Ghannam enum edac_type edac_mode = EDAC_NONE; 3501353a1fcbSYazen Ghannam enum dev_type dev_type = DEV_UNKNOWN; 3502353a1fcbSYazen Ghannam struct dimm_info *dimm; 3503353a1fcbSYazen Ghannam int empty = 1; 3504353a1fcbSYazen Ghannam u8 umc, cs; 3505353a1fcbSYazen Ghannam 3506353a1fcbSYazen Ghannam if (mci->edac_ctl_cap & EDAC_FLAG_S16ECD16ED) { 3507353a1fcbSYazen Ghannam edac_mode = EDAC_S16ECD16ED; 3508353a1fcbSYazen Ghannam dev_type = DEV_X16; 3509353a1fcbSYazen Ghannam } else if (mci->edac_ctl_cap & EDAC_FLAG_S8ECD8ED) { 3510353a1fcbSYazen Ghannam edac_mode = EDAC_S8ECD8ED; 3511353a1fcbSYazen Ghannam dev_type = DEV_X8; 3512353a1fcbSYazen Ghannam } else if (mci->edac_ctl_cap & EDAC_FLAG_S4ECD4ED) { 3513353a1fcbSYazen Ghannam edac_mode = EDAC_S4ECD4ED; 3514353a1fcbSYazen Ghannam dev_type = DEV_X4; 3515353a1fcbSYazen Ghannam } else if (mci->edac_ctl_cap & EDAC_FLAG_SECDED) { 3516353a1fcbSYazen Ghannam edac_mode = EDAC_SECDED; 3517353a1fcbSYazen Ghannam } 3518353a1fcbSYazen Ghannam 3519353a1fcbSYazen Ghannam for_each_umc(umc) { 3520353a1fcbSYazen Ghannam for_each_chip_select(cs, umc, pvt) { 3521353a1fcbSYazen Ghannam if (!csrow_enabled(cs, umc, pvt)) 3522353a1fcbSYazen Ghannam continue; 3523353a1fcbSYazen Ghannam 3524353a1fcbSYazen Ghannam empty = 0; 3525353a1fcbSYazen Ghannam dimm = mci->csrows[cs]->channels[umc]->dimm; 3526353a1fcbSYazen Ghannam 3527353a1fcbSYazen Ghannam edac_dbg(1, "MC node: %d, csrow: %d\n", 3528353a1fcbSYazen Ghannam pvt->mc_node_id, cs); 3529353a1fcbSYazen Ghannam 3530353a1fcbSYazen Ghannam dimm->nr_pages = get_csrow_nr_pages(pvt, umc, cs); 3531353a1fcbSYazen Ghannam dimm->mtype = pvt->dram_type; 3532353a1fcbSYazen Ghannam dimm->edac_mode = edac_mode; 3533353a1fcbSYazen Ghannam dimm->dtype = dev_type; 3534466503d6SYazen Ghannam dimm->grain = 64; 3535353a1fcbSYazen Ghannam } 3536353a1fcbSYazen Ghannam } 3537353a1fcbSYazen Ghannam 3538353a1fcbSYazen Ghannam return empty; 3539353a1fcbSYazen Ghannam } 3540353a1fcbSYazen Ghannam 35410ec449eeSDoug Thompson /* 35420ec449eeSDoug Thompson * Initialize the array of csrow attribute instances, based on the values 35430ec449eeSDoug Thompson * from pci config hardware registers. 35440ec449eeSDoug Thompson */ 3545360b7f3cSBorislav Petkov static int init_csrows(struct mem_ctl_info *mci) 35460ec449eeSDoug Thompson { 354710de6497SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 35482d09d8f3SYazen Ghannam enum edac_type edac_mode = EDAC_NONE; 35490ec449eeSDoug Thompson struct csrow_info *csrow; 3550de3910ebSMauro Carvalho Chehab struct dimm_info *dimm; 355110de6497SBorislav Petkov int i, j, empty = 1; 3552a895bf8bSMauro Carvalho Chehab int nr_pages = 0; 355310de6497SBorislav Petkov u32 val; 35540ec449eeSDoug Thompson 3555353a1fcbSYazen Ghannam if (pvt->umc) 3556353a1fcbSYazen Ghannam return init_csrows_df(mci); 3557353a1fcbSYazen Ghannam 3558a97fa68eSBorislav Petkov amd64_read_pci_cfg(pvt->F3, NBCFG, &val); 35590ec449eeSDoug Thompson 35602299ef71SBorislav Petkov pvt->nbcfg = val; 35610ec449eeSDoug Thompson 3562956b9ba1SJoe Perches edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n", 35632299ef71SBorislav Petkov pvt->mc_node_id, val, 3564a97fa68eSBorislav Petkov !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE)); 35650ec449eeSDoug Thompson 356610de6497SBorislav Petkov /* 356710de6497SBorislav Petkov * We iterate over DCT0 here but we look at DCT1 in parallel, if needed. 356810de6497SBorislav Petkov */ 356911c75eadSBorislav Petkov for_each_chip_select(i, 0, pvt) { 357010de6497SBorislav Petkov bool row_dct0 = !!csrow_enabled(i, 0, pvt); 357110de6497SBorislav Petkov bool row_dct1 = false; 35720ec449eeSDoug Thompson 3573a4b4bedcSBorislav Petkov if (pvt->fam != 0xf) 357410de6497SBorislav Petkov row_dct1 = !!csrow_enabled(i, 1, pvt); 357510de6497SBorislav Petkov 357610de6497SBorislav Petkov if (!row_dct0 && !row_dct1) 35770ec449eeSDoug Thompson continue; 35780ec449eeSDoug Thompson 357910de6497SBorislav Petkov csrow = mci->csrows[i]; 35800ec449eeSDoug Thompson empty = 0; 358111c75eadSBorislav Petkov 358210de6497SBorislav Petkov edac_dbg(1, "MC node: %d, csrow: %d\n", 358310de6497SBorislav Petkov pvt->mc_node_id, i); 358410de6497SBorislav Petkov 35851eef1282SMauro Carvalho Chehab if (row_dct0) { 3586d1ea71cdSBorislav Petkov nr_pages = get_csrow_nr_pages(pvt, 0, i); 35871eef1282SMauro Carvalho Chehab csrow->channels[0]->dimm->nr_pages = nr_pages; 35881eef1282SMauro Carvalho Chehab } 358910de6497SBorislav Petkov 359010de6497SBorislav Petkov /* K8 has only one DCT */ 3591a4b4bedcSBorislav Petkov if (pvt->fam != 0xf && row_dct1) { 3592d1ea71cdSBorislav Petkov int row_dct1_pages = get_csrow_nr_pages(pvt, 1, i); 35931eef1282SMauro Carvalho Chehab 35941eef1282SMauro Carvalho Chehab csrow->channels[1]->dimm->nr_pages = row_dct1_pages; 35951eef1282SMauro Carvalho Chehab nr_pages += row_dct1_pages; 35961eef1282SMauro Carvalho Chehab } 35970ec449eeSDoug Thompson 359810de6497SBorislav Petkov edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages); 35990ec449eeSDoug Thompson 36002d09d8f3SYazen Ghannam /* Determine DIMM ECC mode: */ 3601353a1fcbSYazen Ghannam if (pvt->nbcfg & NBCFG_ECC_ENABLE) { 36022d09d8f3SYazen Ghannam edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) 36032d09d8f3SYazen Ghannam ? EDAC_S4ECD4ED 36042d09d8f3SYazen Ghannam : EDAC_SECDED; 36052d09d8f3SYazen Ghannam } 3606084a4fccSMauro Carvalho Chehab 3607084a4fccSMauro Carvalho Chehab for (j = 0; j < pvt->channel_count; j++) { 3608de3910ebSMauro Carvalho Chehab dimm = csrow->channels[j]->dimm; 3609a597d2a5SAravind Gopalakrishnan dimm->mtype = pvt->dram_type; 3610de3910ebSMauro Carvalho Chehab dimm->edac_mode = edac_mode; 3611466503d6SYazen Ghannam dimm->grain = 64; 3612084a4fccSMauro Carvalho Chehab } 36130ec449eeSDoug Thompson } 36140ec449eeSDoug Thompson 36150ec449eeSDoug Thompson return empty; 36160ec449eeSDoug Thompson } 3617d27bf6faSDoug Thompson 361806724535SBorislav Petkov /* get all cores on this DCT */ 36198b84c8dfSDaniel J Blueman static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, u16 nid) 3620f9431992SDoug Thompson { 362106724535SBorislav Petkov int cpu; 3622f9431992SDoug Thompson 362306724535SBorislav Petkov for_each_online_cpu(cpu) 3624db970bd2SYazen Ghannam if (topology_die_id(cpu) == nid) 362506724535SBorislav Petkov cpumask_set_cpu(cpu, mask); 3626f9431992SDoug Thompson } 3627f9431992SDoug Thompson 3628f9431992SDoug Thompson /* check MCG_CTL on all the cpus on this node */ 3629d1ea71cdSBorislav Petkov static bool nb_mce_bank_enabled_on_node(u16 nid) 3630f9431992SDoug Thompson { 3631ba578cb3SRusty Russell cpumask_var_t mask; 363250542251SBorislav Petkov int cpu, nbe; 363306724535SBorislav Petkov bool ret = false; 3634f9431992SDoug Thompson 3635ba578cb3SRusty Russell if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) { 363624f9a7feSBorislav Petkov amd64_warn("%s: Error allocating mask\n", __func__); 363706724535SBorislav Petkov return false; 363806724535SBorislav Petkov } 363906724535SBorislav Petkov 3640ba578cb3SRusty Russell get_cpus_on_this_dct_cpumask(mask, nid); 364106724535SBorislav Petkov 3642ba578cb3SRusty Russell rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs); 3643ba578cb3SRusty Russell 3644ba578cb3SRusty Russell for_each_cpu(cpu, mask) { 364550542251SBorislav Petkov struct msr *reg = per_cpu_ptr(msrs, cpu); 36465980bb9cSBorislav Petkov nbe = reg->l & MSR_MCGCTL_NBE; 364706724535SBorislav Petkov 3648956b9ba1SJoe Perches edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n", 364950542251SBorislav Petkov cpu, reg->q, 365006724535SBorislav Petkov (nbe ? "enabled" : "disabled")); 365106724535SBorislav Petkov 365206724535SBorislav Petkov if (!nbe) 365306724535SBorislav Petkov goto out; 365406724535SBorislav Petkov } 365506724535SBorislav Petkov ret = true; 365606724535SBorislav Petkov 365706724535SBorislav Petkov out: 3658ba578cb3SRusty Russell free_cpumask_var(mask); 3659f9431992SDoug Thompson return ret; 3660f9431992SDoug Thompson } 3661f9431992SDoug Thompson 3662c7e5301aSDaniel J Blueman static int toggle_ecc_err_reporting(struct ecc_settings *s, u16 nid, bool on) 3663f6d6ae96SBorislav Petkov { 3664f6d6ae96SBorislav Petkov cpumask_var_t cmask; 366550542251SBorislav Petkov int cpu; 3666f6d6ae96SBorislav Petkov 3667f6d6ae96SBorislav Petkov if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) { 366824f9a7feSBorislav Petkov amd64_warn("%s: error allocating mask\n", __func__); 36690de27884SPan Bian return -ENOMEM; 3670f6d6ae96SBorislav Petkov } 3671f6d6ae96SBorislav Petkov 3672ae7bb7c6SBorislav Petkov get_cpus_on_this_dct_cpumask(cmask, nid); 3673f6d6ae96SBorislav Petkov 3674f6d6ae96SBorislav Petkov rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); 3675f6d6ae96SBorislav Petkov 3676f6d6ae96SBorislav Petkov for_each_cpu(cpu, cmask) { 3677f6d6ae96SBorislav Petkov 367850542251SBorislav Petkov struct msr *reg = per_cpu_ptr(msrs, cpu); 367950542251SBorislav Petkov 3680f6d6ae96SBorislav Petkov if (on) { 36815980bb9cSBorislav Petkov if (reg->l & MSR_MCGCTL_NBE) 3682ae7bb7c6SBorislav Petkov s->flags.nb_mce_enable = 1; 3683f6d6ae96SBorislav Petkov 36845980bb9cSBorislav Petkov reg->l |= MSR_MCGCTL_NBE; 3685f6d6ae96SBorislav Petkov } else { 3686f6d6ae96SBorislav Petkov /* 3687d95cf4deSBorislav Petkov * Turn off NB MCE reporting only when it was off before 3688f6d6ae96SBorislav Petkov */ 3689ae7bb7c6SBorislav Petkov if (!s->flags.nb_mce_enable) 36905980bb9cSBorislav Petkov reg->l &= ~MSR_MCGCTL_NBE; 3691f6d6ae96SBorislav Petkov } 3692f6d6ae96SBorislav Petkov } 3693f6d6ae96SBorislav Petkov wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); 3694f6d6ae96SBorislav Petkov 3695f6d6ae96SBorislav Petkov free_cpumask_var(cmask); 3696f6d6ae96SBorislav Petkov 3697f6d6ae96SBorislav Petkov return 0; 3698f6d6ae96SBorislav Petkov } 3699f6d6ae96SBorislav Petkov 3700c7e5301aSDaniel J Blueman static bool enable_ecc_error_reporting(struct ecc_settings *s, u16 nid, 37012299ef71SBorislav Petkov struct pci_dev *F3) 3702f6d6ae96SBorislav Petkov { 37032299ef71SBorislav Petkov bool ret = true; 3704c9f4f26eSBorislav Petkov u32 value, mask = 0x3; /* UECC/CECC enable */ 3705f6d6ae96SBorislav Petkov 37062299ef71SBorislav Petkov if (toggle_ecc_err_reporting(s, nid, ON)) { 37072299ef71SBorislav Petkov amd64_warn("Error enabling ECC reporting over MCGCTL!\n"); 37082299ef71SBorislav Petkov return false; 37092299ef71SBorislav Petkov } 37102299ef71SBorislav Petkov 3711c9f4f26eSBorislav Petkov amd64_read_pci_cfg(F3, NBCTL, &value); 3712f6d6ae96SBorislav Petkov 3713ae7bb7c6SBorislav Petkov s->old_nbctl = value & mask; 3714ae7bb7c6SBorislav Petkov s->nbctl_valid = true; 3715f6d6ae96SBorislav Petkov 3716f6d6ae96SBorislav Petkov value |= mask; 3717c9f4f26eSBorislav Petkov amd64_write_pci_cfg(F3, NBCTL, value); 3718f6d6ae96SBorislav Petkov 3719a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 3720f6d6ae96SBorislav Petkov 3721956b9ba1SJoe Perches edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n", 3722a97fa68eSBorislav Petkov nid, value, !!(value & NBCFG_ECC_ENABLE)); 3723f6d6ae96SBorislav Petkov 3724a97fa68eSBorislav Petkov if (!(value & NBCFG_ECC_ENABLE)) { 372524f9a7feSBorislav Petkov amd64_warn("DRAM ECC disabled on this node, enabling...\n"); 3726f6d6ae96SBorislav Petkov 3727ae7bb7c6SBorislav Petkov s->flags.nb_ecc_prev = 0; 3728d95cf4deSBorislav Petkov 3729f6d6ae96SBorislav Petkov /* Attempt to turn on DRAM ECC Enable */ 3730a97fa68eSBorislav Petkov value |= NBCFG_ECC_ENABLE; 3731a97fa68eSBorislav Petkov amd64_write_pci_cfg(F3, NBCFG, value); 3732f6d6ae96SBorislav Petkov 3733a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 3734f6d6ae96SBorislav Petkov 3735a97fa68eSBorislav Petkov if (!(value & NBCFG_ECC_ENABLE)) { 373624f9a7feSBorislav Petkov amd64_warn("Hardware rejected DRAM ECC enable," 373724f9a7feSBorislav Petkov "check memory DIMM configuration.\n"); 37382299ef71SBorislav Petkov ret = false; 3739f6d6ae96SBorislav Petkov } else { 374024f9a7feSBorislav Petkov amd64_info("Hardware accepted DRAM ECC Enable\n"); 3741f6d6ae96SBorislav Petkov } 3742d95cf4deSBorislav Petkov } else { 3743ae7bb7c6SBorislav Petkov s->flags.nb_ecc_prev = 1; 3744f6d6ae96SBorislav Petkov } 3745d95cf4deSBorislav Petkov 3746956b9ba1SJoe Perches edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n", 3747a97fa68eSBorislav Petkov nid, value, !!(value & NBCFG_ECC_ENABLE)); 3748f6d6ae96SBorislav Petkov 37492299ef71SBorislav Petkov return ret; 3750f6d6ae96SBorislav Petkov } 3751f6d6ae96SBorislav Petkov 3752c7e5301aSDaniel J Blueman static void restore_ecc_error_reporting(struct ecc_settings *s, u16 nid, 3753360b7f3cSBorislav Petkov struct pci_dev *F3) 3754f6d6ae96SBorislav Petkov { 3755c9f4f26eSBorislav Petkov u32 value, mask = 0x3; /* UECC/CECC enable */ 3756c9f4f26eSBorislav Petkov 3757ae7bb7c6SBorislav Petkov if (!s->nbctl_valid) 3758f6d6ae96SBorislav Petkov return; 3759f6d6ae96SBorislav Petkov 3760c9f4f26eSBorislav Petkov amd64_read_pci_cfg(F3, NBCTL, &value); 3761f6d6ae96SBorislav Petkov value &= ~mask; 3762ae7bb7c6SBorislav Petkov value |= s->old_nbctl; 3763f6d6ae96SBorislav Petkov 3764c9f4f26eSBorislav Petkov amd64_write_pci_cfg(F3, NBCTL, value); 3765f6d6ae96SBorislav Petkov 3766ae7bb7c6SBorislav Petkov /* restore previous BIOS DRAM ECC "off" setting we force-enabled */ 3767ae7bb7c6SBorislav Petkov if (!s->flags.nb_ecc_prev) { 3768a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 3769a97fa68eSBorislav Petkov value &= ~NBCFG_ECC_ENABLE; 3770a97fa68eSBorislav Petkov amd64_write_pci_cfg(F3, NBCFG, value); 3771d95cf4deSBorislav Petkov } 3772d95cf4deSBorislav Petkov 3773d95cf4deSBorislav Petkov /* restore the NB Enable MCGCTL bit */ 37742299ef71SBorislav Petkov if (toggle_ecc_err_reporting(s, nid, OFF)) 377524f9a7feSBorislav Petkov amd64_warn("Error restoring NB MCGCTL settings!\n"); 3776f6d6ae96SBorislav Petkov } 3777f6d6ae96SBorislav Petkov 37781c9b08baSYazen Ghannam static bool ecc_enabled(struct amd64_pvt *pvt) 3779f9431992SDoug Thompson { 37801c9b08baSYazen Ghannam u16 nid = pvt->mc_node_id; 378106724535SBorislav Petkov bool nb_mce_en = false; 3782196b79fcSYazen Ghannam u8 ecc_en = 0, i; 3783196b79fcSYazen Ghannam u32 value; 3784f9431992SDoug Thompson 3785196b79fcSYazen Ghannam if (boot_cpu_data.x86 >= 0x17) { 3786196b79fcSYazen Ghannam u8 umc_en_mask = 0, ecc_en_mask = 0; 37871c9b08baSYazen Ghannam struct amd64_umc *umc; 3788196b79fcSYazen Ghannam 37894d30d2bcSYazen Ghannam for_each_umc(i) { 37901c9b08baSYazen Ghannam umc = &pvt->umc[i]; 3791196b79fcSYazen Ghannam 3792196b79fcSYazen Ghannam /* Only check enabled UMCs. */ 37931c9b08baSYazen Ghannam if (!(umc->sdp_ctrl & UMC_SDP_INIT)) 3794196b79fcSYazen Ghannam continue; 3795196b79fcSYazen Ghannam 3796196b79fcSYazen Ghannam umc_en_mask |= BIT(i); 3797196b79fcSYazen Ghannam 37981c9b08baSYazen Ghannam if (umc->umc_cap_hi & UMC_ECC_ENABLED) 3799196b79fcSYazen Ghannam ecc_en_mask |= BIT(i); 3800196b79fcSYazen Ghannam } 3801196b79fcSYazen Ghannam 3802196b79fcSYazen Ghannam /* Check whether at least one UMC is enabled: */ 3803196b79fcSYazen Ghannam if (umc_en_mask) 3804196b79fcSYazen Ghannam ecc_en = umc_en_mask == ecc_en_mask; 380511ab1caeSYazen Ghannam else 380611ab1caeSYazen Ghannam edac_dbg(0, "Node %d: No enabled UMCs.\n", nid); 3807196b79fcSYazen Ghannam 3808196b79fcSYazen Ghannam /* Assume UMC MCA banks are enabled. */ 3809196b79fcSYazen Ghannam nb_mce_en = true; 3810196b79fcSYazen Ghannam } else { 38111c9b08baSYazen Ghannam amd64_read_pci_cfg(pvt->F3, NBCFG, &value); 3812f9431992SDoug Thompson 3813a97fa68eSBorislav Petkov ecc_en = !!(value & NBCFG_ECC_ENABLE); 3814be3468e8SBorislav Petkov 3815d1ea71cdSBorislav Petkov nb_mce_en = nb_mce_bank_enabled_on_node(nid); 381606724535SBorislav Petkov if (!nb_mce_en) 381711ab1caeSYazen Ghannam edac_dbg(0, "NB MCE bank disabled, set MSR 0x%08x[4] on node %d to enable.\n", 38182299ef71SBorislav Petkov MSR_IA32_MCG_CTL, nid); 3819196b79fcSYazen Ghannam } 3820196b79fcSYazen Ghannam 38214cbcb73bSBorislav Petkov edac_dbg(3, "Node %d: DRAM ECC %s.\n", nid, (ecc_en ? "enabled" : "disabled")); 3822be3468e8SBorislav Petkov 38237fdfee92SBorislav Petkov if (!ecc_en || !nb_mce_en) 38242299ef71SBorislav Petkov return false; 38257fdfee92SBorislav Petkov else 38262299ef71SBorislav Petkov return true; 3827f9431992SDoug Thompson } 3828f9431992SDoug Thompson 38292d09d8f3SYazen Ghannam static inline void 38302d09d8f3SYazen Ghannam f17h_determine_edac_ctl_cap(struct mem_ctl_info *mci, struct amd64_pvt *pvt) 38312d09d8f3SYazen Ghannam { 3832f8be8e56SYazen Ghannam u8 i, ecc_en = 1, cpk_en = 1, dev_x4 = 1, dev_x16 = 1; 38332d09d8f3SYazen Ghannam 38344d30d2bcSYazen Ghannam for_each_umc(i) { 38352d09d8f3SYazen Ghannam if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) { 38362d09d8f3SYazen Ghannam ecc_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_ENABLED); 38372d09d8f3SYazen Ghannam cpk_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_CHIPKILL_CAP); 3838f8be8e56SYazen Ghannam 3839f8be8e56SYazen Ghannam dev_x4 &= !!(pvt->umc[i].dimm_cfg & BIT(6)); 3840f8be8e56SYazen Ghannam dev_x16 &= !!(pvt->umc[i].dimm_cfg & BIT(7)); 38412d09d8f3SYazen Ghannam } 38422d09d8f3SYazen Ghannam } 38432d09d8f3SYazen Ghannam 38442d09d8f3SYazen Ghannam /* Set chipkill only if ECC is enabled: */ 38452d09d8f3SYazen Ghannam if (ecc_en) { 38462d09d8f3SYazen Ghannam mci->edac_ctl_cap |= EDAC_FLAG_SECDED; 38472d09d8f3SYazen Ghannam 3848f8be8e56SYazen Ghannam if (!cpk_en) 3849f8be8e56SYazen Ghannam return; 3850f8be8e56SYazen Ghannam 3851f8be8e56SYazen Ghannam if (dev_x4) 38522d09d8f3SYazen Ghannam mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED; 3853f8be8e56SYazen Ghannam else if (dev_x16) 3854f8be8e56SYazen Ghannam mci->edac_ctl_cap |= EDAC_FLAG_S16ECD16ED; 3855f8be8e56SYazen Ghannam else 3856f8be8e56SYazen Ghannam mci->edac_ctl_cap |= EDAC_FLAG_S8ECD8ED; 38572d09d8f3SYazen Ghannam } 38582d09d8f3SYazen Ghannam } 38592d09d8f3SYazen Ghannam 386038ddd4d1SYazen Ghannam static void setup_mci_misc_attrs(struct mem_ctl_info *mci) 38617d6034d3SDoug Thompson { 38627d6034d3SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 38637d6034d3SDoug Thompson 38647d6034d3SDoug Thompson mci->mtype_cap = MEM_FLAG_DDR2 | MEM_FLAG_RDDR2; 38657d6034d3SDoug Thompson mci->edac_ctl_cap = EDAC_FLAG_NONE; 38667d6034d3SDoug Thompson 38672d09d8f3SYazen Ghannam if (pvt->umc) { 38682d09d8f3SYazen Ghannam f17h_determine_edac_ctl_cap(mci, pvt); 38692d09d8f3SYazen Ghannam } else { 38705980bb9cSBorislav Petkov if (pvt->nbcap & NBCAP_SECDED) 38717d6034d3SDoug Thompson mci->edac_ctl_cap |= EDAC_FLAG_SECDED; 38727d6034d3SDoug Thompson 38735980bb9cSBorislav Petkov if (pvt->nbcap & NBCAP_CHIPKILL) 38747d6034d3SDoug Thompson mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED; 38752d09d8f3SYazen Ghannam } 38767d6034d3SDoug Thompson 3877d1ea71cdSBorislav Petkov mci->edac_cap = determine_edac_cap(pvt); 38787d6034d3SDoug Thompson mci->mod_name = EDAC_MOD_STR; 387938ddd4d1SYazen Ghannam mci->ctl_name = fam_type->ctl_name; 3880e7934b70SYazen Ghannam mci->dev_name = pci_name(pvt->F3); 38817d6034d3SDoug Thompson mci->ctl_page_to_phys = NULL; 38827d6034d3SDoug Thompson 38837d6034d3SDoug Thompson /* memory scrubber interface */ 3884d1ea71cdSBorislav Petkov mci->set_sdram_scrub_rate = set_scrub_rate; 3885d1ea71cdSBorislav Petkov mci->get_sdram_scrub_rate = get_scrub_rate; 38867d6034d3SDoug Thompson } 38877d6034d3SDoug Thompson 38880092b20dSBorislav Petkov /* 38890092b20dSBorislav Petkov * returns a pointer to the family descriptor on success, NULL otherwise. 38900092b20dSBorislav Petkov */ 3891d1ea71cdSBorislav Petkov static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt) 3892395ae783SBorislav Petkov { 389318b94f66SAravind Gopalakrishnan pvt->ext_model = boot_cpu_data.x86_model >> 4; 3894b399151cSJia Zhang pvt->stepping = boot_cpu_data.x86_stepping; 389518b94f66SAravind Gopalakrishnan pvt->model = boot_cpu_data.x86_model; 389618b94f66SAravind Gopalakrishnan pvt->fam = boot_cpu_data.x86; 389718b94f66SAravind Gopalakrishnan 389818b94f66SAravind Gopalakrishnan switch (pvt->fam) { 3899395ae783SBorislav Petkov case 0xf: 3900d1ea71cdSBorislav Petkov fam_type = &family_types[K8_CPUS]; 3901d1ea71cdSBorislav Petkov pvt->ops = &family_types[K8_CPUS].ops; 3902395ae783SBorislav Petkov break; 3903df71a053SBorislav Petkov 3904395ae783SBorislav Petkov case 0x10: 3905d1ea71cdSBorislav Petkov fam_type = &family_types[F10_CPUS]; 3906d1ea71cdSBorislav Petkov pvt->ops = &family_types[F10_CPUS].ops; 3907df71a053SBorislav Petkov break; 3908df71a053SBorislav Petkov 3909df71a053SBorislav Petkov case 0x15: 391018b94f66SAravind Gopalakrishnan if (pvt->model == 0x30) { 3911d1ea71cdSBorislav Petkov fam_type = &family_types[F15_M30H_CPUS]; 3912d1ea71cdSBorislav Petkov pvt->ops = &family_types[F15_M30H_CPUS].ops; 391318b94f66SAravind Gopalakrishnan break; 3914a597d2a5SAravind Gopalakrishnan } else if (pvt->model == 0x60) { 3915a597d2a5SAravind Gopalakrishnan fam_type = &family_types[F15_M60H_CPUS]; 3916a597d2a5SAravind Gopalakrishnan pvt->ops = &family_types[F15_M60H_CPUS].ops; 3917a597d2a5SAravind Gopalakrishnan break; 39186c13d7ffSBorislav Petkov /* Richland is only client */ 39196c13d7ffSBorislav Petkov } else if (pvt->model == 0x13) { 39206c13d7ffSBorislav Petkov return NULL; 39216c13d7ffSBorislav Petkov } else { 3922d1ea71cdSBorislav Petkov fam_type = &family_types[F15_CPUS]; 3923d1ea71cdSBorislav Petkov pvt->ops = &family_types[F15_CPUS].ops; 39246c13d7ffSBorislav Petkov } 3925395ae783SBorislav Petkov break; 3926395ae783SBorislav Petkov 392794c1acf2SAravind Gopalakrishnan case 0x16: 392885a8885bSAravind Gopalakrishnan if (pvt->model == 0x30) { 392985a8885bSAravind Gopalakrishnan fam_type = &family_types[F16_M30H_CPUS]; 393085a8885bSAravind Gopalakrishnan pvt->ops = &family_types[F16_M30H_CPUS].ops; 393185a8885bSAravind Gopalakrishnan break; 393285a8885bSAravind Gopalakrishnan } 3933d1ea71cdSBorislav Petkov fam_type = &family_types[F16_CPUS]; 3934d1ea71cdSBorislav Petkov pvt->ops = &family_types[F16_CPUS].ops; 393594c1acf2SAravind Gopalakrishnan break; 393694c1acf2SAravind Gopalakrishnan 3937f1cbbec9SYazen Ghannam case 0x17: 39388960de4aSMichael Jin if (pvt->model >= 0x10 && pvt->model <= 0x2f) { 39398960de4aSMichael Jin fam_type = &family_types[F17_M10H_CPUS]; 39408960de4aSMichael Jin pvt->ops = &family_types[F17_M10H_CPUS].ops; 39418960de4aSMichael Jin break; 39426e846239SYazen Ghannam } else if (pvt->model >= 0x30 && pvt->model <= 0x3f) { 39436e846239SYazen Ghannam fam_type = &family_types[F17_M30H_CPUS]; 39446e846239SYazen Ghannam pvt->ops = &family_types[F17_M30H_CPUS].ops; 39456e846239SYazen Ghannam break; 3946b6bea24dSAlexander Monakov } else if (pvt->model >= 0x60 && pvt->model <= 0x6f) { 3947b6bea24dSAlexander Monakov fam_type = &family_types[F17_M60H_CPUS]; 3948b6bea24dSAlexander Monakov pvt->ops = &family_types[F17_M60H_CPUS].ops; 3949b6bea24dSAlexander Monakov break; 39503e443eb3SIsaac Vaughn } else if (pvt->model >= 0x70 && pvt->model <= 0x7f) { 39513e443eb3SIsaac Vaughn fam_type = &family_types[F17_M70H_CPUS]; 39523e443eb3SIsaac Vaughn pvt->ops = &family_types[F17_M70H_CPUS].ops; 39533e443eb3SIsaac Vaughn break; 39548960de4aSMichael Jin } 3955df561f66SGustavo A. R. Silva fallthrough; 3956c4a3e946SPu Wen case 0x18: 3957f1cbbec9SYazen Ghannam fam_type = &family_types[F17_CPUS]; 3958f1cbbec9SYazen Ghannam pvt->ops = &family_types[F17_CPUS].ops; 3959c4a3e946SPu Wen 3960c4a3e946SPu Wen if (pvt->fam == 0x18) 3961c4a3e946SPu Wen family_types[F17_CPUS].ctl_name = "F18h"; 3962f1cbbec9SYazen Ghannam break; 3963f1cbbec9SYazen Ghannam 39642eb61c91SYazen Ghannam case 0x19: 3965b4210eabSYazen Ghannam if (pvt->model >= 0x20 && pvt->model <= 0x2f) { 3966b4210eabSYazen Ghannam fam_type = &family_types[F17_M70H_CPUS]; 3967b4210eabSYazen Ghannam pvt->ops = &family_types[F17_M70H_CPUS].ops; 3968b4210eabSYazen Ghannam fam_type->ctl_name = "F19h_M20h"; 3969b4210eabSYazen Ghannam break; 3970b4210eabSYazen Ghannam } 39712eb61c91SYazen Ghannam fam_type = &family_types[F19_CPUS]; 39722eb61c91SYazen Ghannam pvt->ops = &family_types[F19_CPUS].ops; 39732eb61c91SYazen Ghannam family_types[F19_CPUS].ctl_name = "F19h"; 39742eb61c91SYazen Ghannam break; 39752eb61c91SYazen Ghannam 3976395ae783SBorislav Petkov default: 397724f9a7feSBorislav Petkov amd64_err("Unsupported family!\n"); 39780092b20dSBorislav Petkov return NULL; 3979395ae783SBorislav Petkov } 39800092b20dSBorislav Petkov 39810092b20dSBorislav Petkov return fam_type; 3982395ae783SBorislav Petkov } 3983395ae783SBorislav Petkov 3984e339f1ecSTakashi Iwai static const struct attribute_group *amd64_edac_attr_groups[] = { 3985e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_DEBUG 39862a28ceefSBorislav Petkov &dbg_group, 398761810096SBorislav Petkov &inj_group, 3988e339f1ecSTakashi Iwai #endif 3989e339f1ecSTakashi Iwai NULL 3990e339f1ecSTakashi Iwai }; 3991e339f1ecSTakashi Iwai 399280355a3bSYazen Ghannam static int hw_info_get(struct amd64_pvt *pvt) 39937d6034d3SDoug Thompson { 3994936fc3afSYazen Ghannam u16 pci_id1, pci_id2; 3995f00eb5ffSColin Ian King int ret; 3996395ae783SBorislav Petkov 3997936fc3afSYazen Ghannam if (pvt->fam >= 0x17) { 39985e4c5527SYazen Ghannam pvt->umc = kcalloc(fam_type->max_mcs, sizeof(struct amd64_umc), GFP_KERNEL); 399980355a3bSYazen Ghannam if (!pvt->umc) 400080355a3bSYazen Ghannam return -ENOMEM; 4001936fc3afSYazen Ghannam 4002936fc3afSYazen Ghannam pci_id1 = fam_type->f0_id; 4003936fc3afSYazen Ghannam pci_id2 = fam_type->f6_id; 4004936fc3afSYazen Ghannam } else { 4005936fc3afSYazen Ghannam pci_id1 = fam_type->f1_id; 4006936fc3afSYazen Ghannam pci_id2 = fam_type->f2_id; 4007936fc3afSYazen Ghannam } 4008936fc3afSYazen Ghannam 400980355a3bSYazen Ghannam ret = reserve_mc_sibling_devs(pvt, pci_id1, pci_id2); 401080355a3bSYazen Ghannam if (ret) 401180355a3bSYazen Ghannam return ret; 40127d6034d3SDoug Thompson 4013360b7f3cSBorislav Petkov read_mc_regs(pvt); 40147d6034d3SDoug Thompson 401580355a3bSYazen Ghannam return 0; 401680355a3bSYazen Ghannam } 401780355a3bSYazen Ghannam 401880355a3bSYazen Ghannam static void hw_info_put(struct amd64_pvt *pvt) 401980355a3bSYazen Ghannam { 402080355a3bSYazen Ghannam if (pvt->F0 || pvt->F1) 402180355a3bSYazen Ghannam free_mc_sibling_devs(pvt); 402280355a3bSYazen Ghannam 402380355a3bSYazen Ghannam kfree(pvt->umc); 402480355a3bSYazen Ghannam } 402580355a3bSYazen Ghannam 402680355a3bSYazen Ghannam static int init_one_instance(struct amd64_pvt *pvt) 402780355a3bSYazen Ghannam { 402880355a3bSYazen Ghannam struct mem_ctl_info *mci = NULL; 402980355a3bSYazen Ghannam struct edac_mc_layer layers[2]; 403080355a3bSYazen Ghannam int ret = -EINVAL; 403180355a3bSYazen Ghannam 40327d6034d3SDoug Thompson /* 40337d6034d3SDoug Thompson * We need to determine how many memory channels there are. Then use 40347d6034d3SDoug Thompson * that information for calculating the size of the dynamic instance 4035360b7f3cSBorislav Petkov * tables in the 'mci' structure. 40367d6034d3SDoug Thompson */ 40377d6034d3SDoug Thompson pvt->channel_count = pvt->ops->early_channel_count(pvt); 40387d6034d3SDoug Thompson if (pvt->channel_count < 0) 403980355a3bSYazen Ghannam return ret; 40407d6034d3SDoug Thompson 40417d6034d3SDoug Thompson ret = -ENOMEM; 4042ab5a503cSMauro Carvalho Chehab layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 4043ab5a503cSMauro Carvalho Chehab layers[0].size = pvt->csels[0].b_cnt; 4044ab5a503cSMauro Carvalho Chehab layers[0].is_virt_csrow = true; 4045ab5a503cSMauro Carvalho Chehab layers[1].type = EDAC_MC_LAYER_CHANNEL; 4046f0a56c48SBorislav Petkov 4047f0a56c48SBorislav Petkov /* 4048f0a56c48SBorislav Petkov * Always allocate two channels since we can have setups with DIMMs on 4049f0a56c48SBorislav Petkov * only one channel. Also, this simplifies handling later for the price 4050f0a56c48SBorislav Petkov * of a couple of KBs tops. 4051f0a56c48SBorislav Petkov */ 40525e4c5527SYazen Ghannam layers[1].size = fam_type->max_mcs; 4053ab5a503cSMauro Carvalho Chehab layers[1].is_virt_csrow = false; 4054f0a56c48SBorislav Petkov 405580355a3bSYazen Ghannam mci = edac_mc_alloc(pvt->mc_node_id, ARRAY_SIZE(layers), layers, 0); 40567d6034d3SDoug Thompson if (!mci) 405780355a3bSYazen Ghannam return ret; 40587d6034d3SDoug Thompson 40597d6034d3SDoug Thompson mci->pvt_info = pvt; 40603f37a36bSBorislav Petkov mci->pdev = &pvt->F3->dev; 40617d6034d3SDoug Thompson 406238ddd4d1SYazen Ghannam setup_mci_misc_attrs(mci); 4063360b7f3cSBorislav Petkov 4064360b7f3cSBorislav Petkov if (init_csrows(mci)) 40657d6034d3SDoug Thompson mci->edac_cap = EDAC_FLAG_NONE; 40667d6034d3SDoug Thompson 40677d6034d3SDoug Thompson ret = -ENODEV; 4068e339f1ecSTakashi Iwai if (edac_mc_add_mc_with_groups(mci, amd64_edac_attr_groups)) { 4069956b9ba1SJoe Perches edac_dbg(1, "failed edac_mc_add_mc()\n"); 407080355a3bSYazen Ghannam edac_mc_free(mci); 407180355a3bSYazen Ghannam return ret; 40727d6034d3SDoug Thompson } 40737d6034d3SDoug Thompson 40747d6034d3SDoug Thompson return 0; 40757d6034d3SDoug Thompson } 40767d6034d3SDoug Thompson 4077582f94b5SYazen Ghannam static bool instance_has_memory(struct amd64_pvt *pvt) 4078582f94b5SYazen Ghannam { 4079582f94b5SYazen Ghannam bool cs_enabled = false; 4080582f94b5SYazen Ghannam int cs = 0, dct = 0; 4081582f94b5SYazen Ghannam 4082582f94b5SYazen Ghannam for (dct = 0; dct < fam_type->max_mcs; dct++) { 4083582f94b5SYazen Ghannam for_each_chip_select(cs, dct, pvt) 4084582f94b5SYazen Ghannam cs_enabled |= csrow_enabled(cs, dct, pvt); 4085582f94b5SYazen Ghannam } 4086582f94b5SYazen Ghannam 4087582f94b5SYazen Ghannam return cs_enabled; 4088582f94b5SYazen Ghannam } 4089582f94b5SYazen Ghannam 40903f37a36bSBorislav Petkov static int probe_one_instance(unsigned int nid) 40917d6034d3SDoug Thompson { 40922299ef71SBorislav Petkov struct pci_dev *F3 = node_to_amd_nb(nid)->misc; 409380355a3bSYazen Ghannam struct amd64_pvt *pvt = NULL; 4094ae7bb7c6SBorislav Petkov struct ecc_settings *s; 40953f37a36bSBorislav Petkov int ret; 4096b8cfa02fSBorislav Petkov 4097ae7bb7c6SBorislav Petkov ret = -ENOMEM; 4098ae7bb7c6SBorislav Petkov s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL); 4099ae7bb7c6SBorislav Petkov if (!s) 41002299ef71SBorislav Petkov goto err_out; 4101ae7bb7c6SBorislav Petkov 4102ae7bb7c6SBorislav Petkov ecc_stngs[nid] = s; 4103ae7bb7c6SBorislav Petkov 410480355a3bSYazen Ghannam pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL); 410580355a3bSYazen Ghannam if (!pvt) 410680355a3bSYazen Ghannam goto err_settings; 410780355a3bSYazen Ghannam 410880355a3bSYazen Ghannam pvt->mc_node_id = nid; 410980355a3bSYazen Ghannam pvt->F3 = F3; 411080355a3bSYazen Ghannam 41116c13d7ffSBorislav Petkov ret = -ENODEV; 411280355a3bSYazen Ghannam fam_type = per_family_init(pvt); 411380355a3bSYazen Ghannam if (!fam_type) 411480355a3bSYazen Ghannam goto err_enable; 411580355a3bSYazen Ghannam 411680355a3bSYazen Ghannam ret = hw_info_get(pvt); 411780355a3bSYazen Ghannam if (ret < 0) 411880355a3bSYazen Ghannam goto err_enable; 411980355a3bSYazen Ghannam 41204688c9b4SYazen Ghannam ret = 0; 4121582f94b5SYazen Ghannam if (!instance_has_memory(pvt)) { 4122582f94b5SYazen Ghannam amd64_info("Node %d: No DIMMs detected.\n", nid); 4123582f94b5SYazen Ghannam goto err_enable; 4124582f94b5SYazen Ghannam } 4125582f94b5SYazen Ghannam 4126582f94b5SYazen Ghannam if (!ecc_enabled(pvt)) { 4127582f94b5SYazen Ghannam ret = -ENODEV; 41282299ef71SBorislav Petkov 41292299ef71SBorislav Petkov if (!ecc_enable_override) 41302299ef71SBorislav Petkov goto err_enable; 41312299ef71SBorislav Petkov 4132044e7a41SYazen Ghannam if (boot_cpu_data.x86 >= 0x17) { 4133044e7a41SYazen Ghannam amd64_warn("Forcing ECC on is not recommended on newer systems. Please enable ECC in BIOS."); 4134044e7a41SYazen Ghannam goto err_enable; 4135044e7a41SYazen Ghannam } else 41362299ef71SBorislav Petkov amd64_warn("Forcing ECC on!\n"); 41372299ef71SBorislav Petkov 41382299ef71SBorislav Petkov if (!enable_ecc_error_reporting(s, nid, F3)) 41392299ef71SBorislav Petkov goto err_enable; 41402299ef71SBorislav Petkov } 41412299ef71SBorislav Petkov 414280355a3bSYazen Ghannam ret = init_one_instance(pvt); 4143360b7f3cSBorislav Petkov if (ret < 0) { 4144ae7bb7c6SBorislav Petkov amd64_err("Error probing instance: %d\n", nid); 4145044e7a41SYazen Ghannam 4146044e7a41SYazen Ghannam if (boot_cpu_data.x86 < 0x17) 4147360b7f3cSBorislav Petkov restore_ecc_error_reporting(s, nid, F3); 41482b9b2c46SYazen Ghannam 41492b9b2c46SYazen Ghannam goto err_enable; 4150360b7f3cSBorislav Petkov } 41517d6034d3SDoug Thompson 41524cbcb73bSBorislav Petkov amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name, 41534cbcb73bSBorislav Petkov (pvt->fam == 0xf ? 41544cbcb73bSBorislav Petkov (pvt->ext_model >= K8_REV_F ? "revF or later " 41554cbcb73bSBorislav Petkov : "revE or earlier ") 41564cbcb73bSBorislav Petkov : ""), pvt->mc_node_id); 41574cbcb73bSBorislav Petkov 4158582f94b5SYazen Ghannam dump_misc_regs(pvt); 4159582f94b5SYazen Ghannam 41607d6034d3SDoug Thompson return ret; 41612299ef71SBorislav Petkov 41622299ef71SBorislav Petkov err_enable: 416380355a3bSYazen Ghannam hw_info_put(pvt); 416480355a3bSYazen Ghannam kfree(pvt); 416580355a3bSYazen Ghannam 416680355a3bSYazen Ghannam err_settings: 41672299ef71SBorislav Petkov kfree(s); 41682299ef71SBorislav Petkov ecc_stngs[nid] = NULL; 41692299ef71SBorislav Petkov 41702299ef71SBorislav Petkov err_out: 41712299ef71SBorislav Petkov return ret; 41727d6034d3SDoug Thompson } 41737d6034d3SDoug Thompson 41743f37a36bSBorislav Petkov static void remove_one_instance(unsigned int nid) 41757d6034d3SDoug Thompson { 4176360b7f3cSBorislav Petkov struct pci_dev *F3 = node_to_amd_nb(nid)->misc; 4177360b7f3cSBorislav Petkov struct ecc_settings *s = ecc_stngs[nid]; 41783f37a36bSBorislav Petkov struct mem_ctl_info *mci; 41793f37a36bSBorislav Petkov struct amd64_pvt *pvt; 41807d6034d3SDoug Thompson 41817d6034d3SDoug Thompson /* Remove from EDAC CORE tracking list */ 41823f37a36bSBorislav Petkov mci = edac_mc_del_mc(&F3->dev); 41837d6034d3SDoug Thompson if (!mci) 41847d6034d3SDoug Thompson return; 41857d6034d3SDoug Thompson 41867d6034d3SDoug Thompson pvt = mci->pvt_info; 41877d6034d3SDoug Thompson 4188360b7f3cSBorislav Petkov restore_ecc_error_reporting(s, nid, F3); 41897d6034d3SDoug Thompson 4190360b7f3cSBorislav Petkov kfree(ecc_stngs[nid]); 4191360b7f3cSBorislav Petkov ecc_stngs[nid] = NULL; 4192ae7bb7c6SBorislav Petkov 41937d6034d3SDoug Thompson /* Free the EDAC CORE resources */ 41948f68ed97SBorislav Petkov mci->pvt_info = NULL; 41958f68ed97SBorislav Petkov 419680355a3bSYazen Ghannam hw_info_put(pvt); 41978f68ed97SBorislav Petkov kfree(pvt); 41987d6034d3SDoug Thompson edac_mc_free(mci); 41997d6034d3SDoug Thompson } 42007d6034d3SDoug Thompson 4201360b7f3cSBorislav Petkov static void setup_pci_device(void) 42027d6034d3SDoug Thompson { 4203d1ea71cdSBorislav Petkov if (pci_ctl) 42047d6034d3SDoug Thompson return; 42057d6034d3SDoug Thompson 4206706657b1SBorislav Petkov pci_ctl = edac_pci_create_generic_ctl(pci_ctl_dev, EDAC_MOD_STR); 4207d1ea71cdSBorislav Petkov if (!pci_ctl) { 4208d1ea71cdSBorislav Petkov pr_warn("%s(): Unable to create PCI control\n", __func__); 4209d1ea71cdSBorislav Petkov pr_warn("%s(): PCI error report via EDAC not set\n", __func__); 42107d6034d3SDoug Thompson } 42117d6034d3SDoug Thompson } 42127d6034d3SDoug Thompson 4213d6efab74SYazen Ghannam static const struct x86_cpu_id amd64_cpuids[] = { 421429842621SThomas Gleixner X86_MATCH_VENDOR_FAM(AMD, 0x0F, NULL), 421529842621SThomas Gleixner X86_MATCH_VENDOR_FAM(AMD, 0x10, NULL), 421629842621SThomas Gleixner X86_MATCH_VENDOR_FAM(AMD, 0x15, NULL), 421729842621SThomas Gleixner X86_MATCH_VENDOR_FAM(AMD, 0x16, NULL), 421829842621SThomas Gleixner X86_MATCH_VENDOR_FAM(AMD, 0x17, NULL), 421929842621SThomas Gleixner X86_MATCH_VENDOR_FAM(HYGON, 0x18, NULL), 422029842621SThomas Gleixner X86_MATCH_VENDOR_FAM(AMD, 0x19, NULL), 4221d6efab74SYazen Ghannam { } 4222d6efab74SYazen Ghannam }; 4223d6efab74SYazen Ghannam MODULE_DEVICE_TABLE(x86cpu, amd64_cpuids); 4224d6efab74SYazen Ghannam 42257d6034d3SDoug Thompson static int __init amd64_edac_init(void) 42267d6034d3SDoug Thompson { 4227301375e7SToshi Kani const char *owner; 4228360b7f3cSBorislav Petkov int err = -ENODEV; 42293f37a36bSBorislav Petkov int i; 42307d6034d3SDoug Thompson 4231301375e7SToshi Kani owner = edac_get_owner(); 4232301375e7SToshi Kani if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR))) 4233301375e7SToshi Kani return -EBUSY; 4234301375e7SToshi Kani 42351bd9900bSYazen Ghannam if (!x86_match_cpu(amd64_cpuids)) 42361bd9900bSYazen Ghannam return -ENODEV; 42371bd9900bSYazen Ghannam 42389653a5c7SHans Rosenfeld if (amd_cache_northbridges() < 0) 42391bd9900bSYazen Ghannam return -ENODEV; 42407d6034d3SDoug Thompson 42416ba92feaSBorislav Petkov opstate_init(); 42426ba92feaSBorislav Petkov 4243cc4d8860SBorislav Petkov err = -ENOMEM; 42446396bb22SKees Cook ecc_stngs = kcalloc(amd_nb_num(), sizeof(ecc_stngs[0]), GFP_KERNEL); 42452ec591acSBorislav Petkov if (!ecc_stngs) 4246a9f0fbe2SBorislav Petkov goto err_free; 4247cc4d8860SBorislav Petkov 424850542251SBorislav Petkov msrs = msrs_alloc(); 424956b34b91SBorislav Petkov if (!msrs) 4250360b7f3cSBorislav Petkov goto err_free; 425150542251SBorislav Petkov 42522287c636SYazen Ghannam for (i = 0; i < amd_nb_num(); i++) { 42532287c636SYazen Ghannam err = probe_one_instance(i); 42542287c636SYazen Ghannam if (err) { 42553f37a36bSBorislav Petkov /* unwind properly */ 42563f37a36bSBorislav Petkov while (--i >= 0) 42573f37a36bSBorislav Petkov remove_one_instance(i); 42587d6034d3SDoug Thompson 42593f37a36bSBorislav Petkov goto err_pci; 42603f37a36bSBorislav Petkov } 42612287c636SYazen Ghannam } 42627d6034d3SDoug Thompson 42634688c9b4SYazen Ghannam if (!edac_has_mcs()) { 42644688c9b4SYazen Ghannam err = -ENODEV; 42654688c9b4SYazen Ghannam goto err_pci; 42664688c9b4SYazen Ghannam } 42674688c9b4SYazen Ghannam 4268234365f5SYazen Ghannam /* register stuff with EDAC MCE */ 4269234365f5SYazen Ghannam if (boot_cpu_data.x86 >= 0x17) 4270234365f5SYazen Ghannam amd_register_ecc_decoder(decode_umc_error); 4271234365f5SYazen Ghannam else 4272234365f5SYazen Ghannam amd_register_ecc_decoder(decode_bus_error); 4273234365f5SYazen Ghannam 4274360b7f3cSBorislav Petkov setup_pci_device(); 4275f5b10c45STomasz Pala 4276f5b10c45STomasz Pala #ifdef CONFIG_X86_32 4277f5b10c45STomasz Pala amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR); 4278f5b10c45STomasz Pala #endif 4279f5b10c45STomasz Pala 4280de0336b3SBorislav Petkov printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION); 4281de0336b3SBorislav Petkov 42827d6034d3SDoug Thompson return 0; 42837d6034d3SDoug Thompson 428456b34b91SBorislav Petkov err_pci: 4285706657b1SBorislav Petkov pci_ctl_dev = NULL; 4286706657b1SBorislav Petkov 428756b34b91SBorislav Petkov msrs_free(msrs); 428856b34b91SBorislav Petkov msrs = NULL; 4289cc4d8860SBorislav Petkov 4290360b7f3cSBorislav Petkov err_free: 4291360b7f3cSBorislav Petkov kfree(ecc_stngs); 4292360b7f3cSBorislav Petkov ecc_stngs = NULL; 4293360b7f3cSBorislav Petkov 42947d6034d3SDoug Thompson return err; 42957d6034d3SDoug Thompson } 42967d6034d3SDoug Thompson 42977d6034d3SDoug Thompson static void __exit amd64_edac_exit(void) 42987d6034d3SDoug Thompson { 42993f37a36bSBorislav Petkov int i; 43003f37a36bSBorislav Petkov 4301d1ea71cdSBorislav Petkov if (pci_ctl) 4302d1ea71cdSBorislav Petkov edac_pci_release_generic_ctl(pci_ctl); 43037d6034d3SDoug Thompson 4304234365f5SYazen Ghannam /* unregister from EDAC MCE */ 4305234365f5SYazen Ghannam if (boot_cpu_data.x86 >= 0x17) 4306234365f5SYazen Ghannam amd_unregister_ecc_decoder(decode_umc_error); 4307234365f5SYazen Ghannam else 4308234365f5SYazen Ghannam amd_unregister_ecc_decoder(decode_bus_error); 4309234365f5SYazen Ghannam 43103f37a36bSBorislav Petkov for (i = 0; i < amd_nb_num(); i++) 43113f37a36bSBorislav Petkov remove_one_instance(i); 431250542251SBorislav Petkov 4313ae7bb7c6SBorislav Petkov kfree(ecc_stngs); 4314ae7bb7c6SBorislav Petkov ecc_stngs = NULL; 4315ae7bb7c6SBorislav Petkov 4316706657b1SBorislav Petkov pci_ctl_dev = NULL; 4317706657b1SBorislav Petkov 431850542251SBorislav Petkov msrs_free(msrs); 431950542251SBorislav Petkov msrs = NULL; 43207d6034d3SDoug Thompson } 43217d6034d3SDoug Thompson 43227d6034d3SDoug Thompson module_init(amd64_edac_init); 43237d6034d3SDoug Thompson module_exit(amd64_edac_exit); 43247d6034d3SDoug Thompson 43257d6034d3SDoug Thompson MODULE_LICENSE("GPL"); 43267d6034d3SDoug Thompson MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, " 43277d6034d3SDoug Thompson "Dave Peterson, Thayne Harbaugh"); 43287d6034d3SDoug Thompson MODULE_DESCRIPTION("MC support for AMD64 memory controllers - " 43297d6034d3SDoug Thompson EDAC_AMD64_VERSION); 43307d6034d3SDoug Thompson 43317d6034d3SDoug Thompson module_param(edac_op_state, int, 0444); 43327d6034d3SDoug Thompson MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); 4333