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 18*2151c84eSYazen Ghannam static inline u32 get_umc_reg(u32 reg) 19*2151c84eSYazen Ghannam { 20*2151c84eSYazen Ghannam if (!fam_type->flags.zn_regs_v2) 21*2151c84eSYazen Ghannam return reg; 22*2151c84eSYazen Ghannam 23*2151c84eSYazen Ghannam switch (reg) { 24*2151c84eSYazen Ghannam case UMCCH_ADDR_CFG: return UMCCH_ADDR_CFG_DDR5; 25*2151c84eSYazen Ghannam case UMCCH_ADDR_MASK_SEC: return UMCCH_ADDR_MASK_SEC_DDR5; 26*2151c84eSYazen Ghannam case UMCCH_DIMM_CFG: return UMCCH_DIMM_CFG_DDR5; 27*2151c84eSYazen Ghannam } 28*2151c84eSYazen Ghannam 29*2151c84eSYazen Ghannam WARN_ONCE(1, "%s: unknown register 0x%x", __func__, reg); 30*2151c84eSYazen Ghannam return 0; 31*2151c84eSYazen Ghannam } 32*2151c84eSYazen Ghannam 332ec591acSBorislav Petkov /* Per-node stuff */ 34ae7bb7c6SBorislav Petkov static struct ecc_settings **ecc_stngs; 352bc65418SDoug Thompson 36706657b1SBorislav Petkov /* Device for the PCI component */ 37706657b1SBorislav Petkov static struct device *pci_ctl_dev; 38706657b1SBorislav Petkov 392bc65418SDoug Thompson /* 40b70ef010SBorislav Petkov * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing 41b70ef010SBorislav Petkov * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching- 42b70ef010SBorislav Petkov * or higher value'. 43b70ef010SBorislav Petkov * 44b70ef010SBorislav Petkov *FIXME: Produce a better mapping/linearisation. 45b70ef010SBorislav Petkov */ 46c7e5301aSDaniel J Blueman static const struct scrubrate { 4739094443SBorislav Petkov u32 scrubval; /* bit pattern for scrub rate */ 4839094443SBorislav Petkov u32 bandwidth; /* bandwidth consumed (bytes/sec) */ 4939094443SBorislav Petkov } scrubrates[] = { 50b70ef010SBorislav Petkov { 0x01, 1600000000UL}, 51b70ef010SBorislav Petkov { 0x02, 800000000UL}, 52b70ef010SBorislav Petkov { 0x03, 400000000UL}, 53b70ef010SBorislav Petkov { 0x04, 200000000UL}, 54b70ef010SBorislav Petkov { 0x05, 100000000UL}, 55b70ef010SBorislav Petkov { 0x06, 50000000UL}, 56b70ef010SBorislav Petkov { 0x07, 25000000UL}, 57b70ef010SBorislav Petkov { 0x08, 12284069UL}, 58b70ef010SBorislav Petkov { 0x09, 6274509UL}, 59b70ef010SBorislav Petkov { 0x0A, 3121951UL}, 60b70ef010SBorislav Petkov { 0x0B, 1560975UL}, 61b70ef010SBorislav Petkov { 0x0C, 781440UL}, 62b70ef010SBorislav Petkov { 0x0D, 390720UL}, 63b70ef010SBorislav Petkov { 0x0E, 195300UL}, 64b70ef010SBorislav Petkov { 0x0F, 97650UL}, 65b70ef010SBorislav Petkov { 0x10, 48854UL}, 66b70ef010SBorislav Petkov { 0x11, 24427UL}, 67b70ef010SBorislav Petkov { 0x12, 12213UL}, 68b70ef010SBorislav Petkov { 0x13, 6101UL}, 69b70ef010SBorislav Petkov { 0x14, 3051UL}, 70b70ef010SBorislav Petkov { 0x15, 1523UL}, 71b70ef010SBorislav Petkov { 0x16, 761UL}, 72b70ef010SBorislav Petkov { 0x00, 0UL}, /* scrubbing off */ 73b70ef010SBorislav Petkov }; 74b70ef010SBorislav Petkov 7566fed2d4SBorislav Petkov int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset, 76b2b0c605SBorislav Petkov u32 *val, const char *func) 77b2b0c605SBorislav Petkov { 78b2b0c605SBorislav Petkov int err = 0; 79b2b0c605SBorislav Petkov 80b2b0c605SBorislav Petkov err = pci_read_config_dword(pdev, offset, val); 81b2b0c605SBorislav Petkov if (err) 82b2b0c605SBorislav Petkov amd64_warn("%s: error reading F%dx%03x.\n", 83b2b0c605SBorislav Petkov func, PCI_FUNC(pdev->devfn), offset); 84b2b0c605SBorislav Petkov 85b2b0c605SBorislav Petkov return err; 86b2b0c605SBorislav Petkov } 87b2b0c605SBorislav Petkov 88b2b0c605SBorislav Petkov int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset, 89b2b0c605SBorislav Petkov u32 val, const char *func) 90b2b0c605SBorislav Petkov { 91b2b0c605SBorislav Petkov int err = 0; 92b2b0c605SBorislav Petkov 93b2b0c605SBorislav Petkov err = pci_write_config_dword(pdev, offset, val); 94b2b0c605SBorislav Petkov if (err) 95b2b0c605SBorislav Petkov amd64_warn("%s: error writing to F%dx%03x.\n", 96b2b0c605SBorislav Petkov func, PCI_FUNC(pdev->devfn), offset); 97b2b0c605SBorislav Petkov 98b2b0c605SBorislav Petkov return err; 99b2b0c605SBorislav Petkov } 100b2b0c605SBorislav Petkov 101b2b0c605SBorislav Petkov /* 10273ba8593SBorislav Petkov * Select DCT to which PCI cfg accesses are routed 10373ba8593SBorislav Petkov */ 10473ba8593SBorislav Petkov static void f15h_select_dct(struct amd64_pvt *pvt, u8 dct) 10573ba8593SBorislav Petkov { 10673ba8593SBorislav Petkov u32 reg = 0; 10773ba8593SBorislav Petkov 10873ba8593SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, ®); 1097981a28fSAravind Gopalakrishnan reg &= (pvt->model == 0x30) ? ~3 : ~1; 11073ba8593SBorislav Petkov reg |= dct; 11173ba8593SBorislav Petkov amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg); 11273ba8593SBorislav Petkov } 11373ba8593SBorislav Petkov 1147981a28fSAravind Gopalakrishnan /* 1157981a28fSAravind Gopalakrishnan * 1167981a28fSAravind Gopalakrishnan * Depending on the family, F2 DCT reads need special handling: 1177981a28fSAravind Gopalakrishnan * 1187981a28fSAravind Gopalakrishnan * K8: has a single DCT only and no address offsets >= 0x100 1197981a28fSAravind Gopalakrishnan * 1207981a28fSAravind Gopalakrishnan * F10h: each DCT has its own set of regs 1217981a28fSAravind Gopalakrishnan * DCT0 -> F2x040.. 1227981a28fSAravind Gopalakrishnan * DCT1 -> F2x140.. 1237981a28fSAravind Gopalakrishnan * 1247981a28fSAravind Gopalakrishnan * F16h: has only 1 DCT 1257981a28fSAravind Gopalakrishnan * 1267981a28fSAravind Gopalakrishnan * F15h: we select which DCT we access using F1x10C[DctCfgSel] 1277981a28fSAravind Gopalakrishnan */ 1287981a28fSAravind Gopalakrishnan static inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct, 1297981a28fSAravind Gopalakrishnan int offset, u32 *val) 130b2b0c605SBorislav Petkov { 1317981a28fSAravind Gopalakrishnan switch (pvt->fam) { 1327981a28fSAravind Gopalakrishnan case 0xf: 1337981a28fSAravind Gopalakrishnan if (dct || offset >= 0x100) 1347981a28fSAravind Gopalakrishnan return -EINVAL; 1357981a28fSAravind Gopalakrishnan break; 136b2b0c605SBorislav Petkov 1377981a28fSAravind Gopalakrishnan case 0x10: 1387981a28fSAravind Gopalakrishnan if (dct) { 1397981a28fSAravind Gopalakrishnan /* 1407981a28fSAravind Gopalakrishnan * Note: If ganging is enabled, barring the regs 1417981a28fSAravind Gopalakrishnan * F2x[1,0]98 and F2x[1,0]9C; reads reads to F2x1xx 1427981a28fSAravind Gopalakrishnan * return 0. (cf. Section 2.8.1 F10h BKDG) 1437981a28fSAravind Gopalakrishnan */ 1447981a28fSAravind Gopalakrishnan if (dct_ganging_enabled(pvt)) 1457981a28fSAravind Gopalakrishnan return 0; 1467981a28fSAravind Gopalakrishnan 1477981a28fSAravind Gopalakrishnan offset += 0x100; 148b2b0c605SBorislav Petkov } 1497981a28fSAravind Gopalakrishnan break; 150b2b0c605SBorislav Petkov 1517981a28fSAravind Gopalakrishnan case 0x15: 1527981a28fSAravind Gopalakrishnan /* 1537981a28fSAravind Gopalakrishnan * F15h: F2x1xx addresses do not map explicitly to DCT1. 1547981a28fSAravind Gopalakrishnan * We should select which DCT we access using F1x10C[DctCfgSel] 1557981a28fSAravind Gopalakrishnan */ 1567981a28fSAravind Gopalakrishnan dct = (dct && pvt->model == 0x30) ? 3 : dct; 15773ba8593SBorislav Petkov f15h_select_dct(pvt, dct); 1587981a28fSAravind Gopalakrishnan break; 159b2b0c605SBorislav Petkov 1607981a28fSAravind Gopalakrishnan case 0x16: 1617981a28fSAravind Gopalakrishnan if (dct) 1627981a28fSAravind Gopalakrishnan return -EINVAL; 1637981a28fSAravind Gopalakrishnan break; 1647981a28fSAravind Gopalakrishnan 1657981a28fSAravind Gopalakrishnan default: 1667981a28fSAravind Gopalakrishnan break; 1677981a28fSAravind Gopalakrishnan } 1687981a28fSAravind Gopalakrishnan return amd64_read_pci_cfg(pvt->F2, offset, val); 169b2b0c605SBorislav Petkov } 170b2b0c605SBorislav Petkov 171b70ef010SBorislav Petkov /* 1722bc65418SDoug Thompson * Memory scrubber control interface. For K8, memory scrubbing is handled by 1732bc65418SDoug Thompson * hardware and can involve L2 cache, dcache as well as the main memory. With 1742bc65418SDoug Thompson * F10, this is extended to L3 cache scrubbing on CPU models sporting that 1752bc65418SDoug Thompson * functionality. 1762bc65418SDoug Thompson * 1772bc65418SDoug Thompson * This causes the "units" for the scrubbing speed to vary from 64 byte blocks 1782bc65418SDoug Thompson * (dram) over to cache lines. This is nasty, so we will use bandwidth in 1792bc65418SDoug Thompson * bytes/sec for the setting. 1802bc65418SDoug Thompson * 1812bc65418SDoug Thompson * Currently, we only do dram scrubbing. If the scrubbing is done in software on 1822bc65418SDoug Thompson * other archs, we might not have access to the caches directly. 1832bc65418SDoug Thompson */ 1842bc65418SDoug Thompson 1858051c0afSYazen Ghannam static inline void __f17h_set_scrubval(struct amd64_pvt *pvt, u32 scrubval) 1868051c0afSYazen Ghannam { 1872bc65418SDoug Thompson /* 1888051c0afSYazen Ghannam * Fam17h supports scrub values between 0x5 and 0x14. Also, the values 1898051c0afSYazen Ghannam * are shifted down by 0x5, so scrubval 0x5 is written to the register 1908051c0afSYazen Ghannam * as 0x0, scrubval 0x6 as 0x1, etc. 1918051c0afSYazen Ghannam */ 1928051c0afSYazen Ghannam if (scrubval >= 0x5 && scrubval <= 0x14) { 1938051c0afSYazen Ghannam scrubval -= 0x5; 1948051c0afSYazen Ghannam pci_write_bits32(pvt->F6, F17H_SCR_LIMIT_ADDR, scrubval, 0xF); 1958051c0afSYazen Ghannam pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 1, 0x1); 1968051c0afSYazen Ghannam } else { 1978051c0afSYazen Ghannam pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 0, 0x1); 1988051c0afSYazen Ghannam } 1998051c0afSYazen Ghannam } 2008051c0afSYazen Ghannam /* 2018051c0afSYazen Ghannam * Scan the scrub rate mapping table for a close or matching bandwidth value to 2022bc65418SDoug Thompson * issue. If requested is too big, then use last maximum value found. 2032bc65418SDoug Thompson */ 204da92110dSAravind Gopalakrishnan static int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate) 2052bc65418SDoug Thompson { 2062bc65418SDoug Thompson u32 scrubval; 2072bc65418SDoug Thompson int i; 2082bc65418SDoug Thompson 2092bc65418SDoug Thompson /* 2102bc65418SDoug Thompson * map the configured rate (new_bw) to a value specific to the AMD64 2112bc65418SDoug Thompson * memory controller and apply to register. Search for the first 2122bc65418SDoug Thompson * bandwidth entry that is greater or equal than the setting requested 2132bc65418SDoug Thompson * and program that. If at last entry, turn off DRAM scrubbing. 214168bfeefSAndrew Morton * 215168bfeefSAndrew Morton * If no suitable bandwidth is found, turn off DRAM scrubbing entirely 216168bfeefSAndrew Morton * by falling back to the last element in scrubrates[]. 2172bc65418SDoug Thompson */ 218168bfeefSAndrew Morton for (i = 0; i < ARRAY_SIZE(scrubrates) - 1; i++) { 2192bc65418SDoug Thompson /* 2202bc65418SDoug Thompson * skip scrub rates which aren't recommended 2212bc65418SDoug Thompson * (see F10 BKDG, F3x58) 2222bc65418SDoug Thompson */ 223395ae783SBorislav Petkov if (scrubrates[i].scrubval < min_rate) 2242bc65418SDoug Thompson continue; 2252bc65418SDoug Thompson 2262bc65418SDoug Thompson if (scrubrates[i].bandwidth <= new_bw) 2272bc65418SDoug Thompson break; 2282bc65418SDoug Thompson } 2292bc65418SDoug Thompson 2302bc65418SDoug Thompson scrubval = scrubrates[i].scrubval; 2312bc65418SDoug Thompson 232dcd01394SYazen Ghannam if (pvt->umc) { 2338051c0afSYazen Ghannam __f17h_set_scrubval(pvt, scrubval); 2348051c0afSYazen Ghannam } else if (pvt->fam == 0x15 && pvt->model == 0x60) { 235da92110dSAravind Gopalakrishnan f15h_select_dct(pvt, 0); 236da92110dSAravind Gopalakrishnan pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F); 237da92110dSAravind Gopalakrishnan f15h_select_dct(pvt, 1); 238da92110dSAravind Gopalakrishnan pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F); 239da92110dSAravind Gopalakrishnan } else { 240da92110dSAravind Gopalakrishnan pci_write_bits32(pvt->F3, SCRCTRL, scrubval, 0x001F); 241da92110dSAravind Gopalakrishnan } 2422bc65418SDoug Thompson 24339094443SBorislav Petkov if (scrubval) 24439094443SBorislav Petkov return scrubrates[i].bandwidth; 24539094443SBorislav Petkov 2462bc65418SDoug Thompson return 0; 2472bc65418SDoug Thompson } 2482bc65418SDoug Thompson 249d1ea71cdSBorislav Petkov static int set_scrub_rate(struct mem_ctl_info *mci, u32 bw) 2502bc65418SDoug Thompson { 2512bc65418SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 25287b3e0e6SBorislav Petkov u32 min_scrubrate = 0x5; 2532bc65418SDoug Thompson 254a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) 25587b3e0e6SBorislav Petkov min_scrubrate = 0x0; 25687b3e0e6SBorislav Petkov 257da92110dSAravind Gopalakrishnan if (pvt->fam == 0x15) { 2583f0aba4fSBorislav Petkov /* Erratum #505 */ 259da92110dSAravind Gopalakrishnan if (pvt->model < 0x10) 26073ba8593SBorislav Petkov f15h_select_dct(pvt, 0); 26173ba8593SBorislav Petkov 262da92110dSAravind Gopalakrishnan if (pvt->model == 0x60) 263da92110dSAravind Gopalakrishnan min_scrubrate = 0x6; 264da92110dSAravind Gopalakrishnan } 265da92110dSAravind Gopalakrishnan return __set_scrub_rate(pvt, bw, min_scrubrate); 2662bc65418SDoug Thompson } 2672bc65418SDoug Thompson 268d1ea71cdSBorislav Petkov static int get_scrub_rate(struct mem_ctl_info *mci) 2692bc65418SDoug Thompson { 2702bc65418SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 27139094443SBorislav Petkov int i, retval = -EINVAL; 2728051c0afSYazen Ghannam u32 scrubval = 0; 2732bc65418SDoug Thompson 274dcd01394SYazen Ghannam if (pvt->umc) { 2758051c0afSYazen Ghannam amd64_read_pci_cfg(pvt->F6, F17H_SCR_BASE_ADDR, &scrubval); 2768051c0afSYazen Ghannam if (scrubval & BIT(0)) { 2778051c0afSYazen Ghannam amd64_read_pci_cfg(pvt->F6, F17H_SCR_LIMIT_ADDR, &scrubval); 2788051c0afSYazen Ghannam scrubval &= 0xF; 2798051c0afSYazen Ghannam scrubval += 0x5; 2808051c0afSYazen Ghannam } else { 2818051c0afSYazen Ghannam scrubval = 0; 2828051c0afSYazen Ghannam } 283dcd01394SYazen Ghannam } else if (pvt->fam == 0x15) { 284dcd01394SYazen Ghannam /* Erratum #505 */ 285dcd01394SYazen Ghannam if (pvt->model < 0x10) 286dcd01394SYazen Ghannam f15h_select_dct(pvt, 0); 2878051c0afSYazen Ghannam 288dcd01394SYazen Ghannam if (pvt->model == 0x60) 289dcd01394SYazen Ghannam amd64_read_pci_cfg(pvt->F2, F15H_M60H_SCRCTRL, &scrubval); 290ee470bb2SBorislav Petkov else 291ee470bb2SBorislav Petkov amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval); 292dcd01394SYazen Ghannam } else { 2935980bb9cSBorislav Petkov amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval); 2948051c0afSYazen Ghannam } 2952bc65418SDoug Thompson 2962bc65418SDoug Thompson scrubval = scrubval & 0x001F; 2972bc65418SDoug Thompson 298926311fdSRoel Kluin for (i = 0; i < ARRAY_SIZE(scrubrates); i++) { 2992bc65418SDoug Thompson if (scrubrates[i].scrubval == scrubval) { 30039094443SBorislav Petkov retval = scrubrates[i].bandwidth; 3012bc65418SDoug Thompson break; 3022bc65418SDoug Thompson } 3032bc65418SDoug Thompson } 30439094443SBorislav Petkov return retval; 3052bc65418SDoug Thompson } 3062bc65418SDoug Thompson 3076775763aSDoug Thompson /* 3087f19bf75SBorislav Petkov * returns true if the SysAddr given by sys_addr matches the 3097f19bf75SBorislav Petkov * DRAM base/limit associated with node_id 3106775763aSDoug Thompson */ 311d1ea71cdSBorislav Petkov static bool base_limit_match(struct amd64_pvt *pvt, u64 sys_addr, u8 nid) 3126775763aSDoug Thompson { 3137f19bf75SBorislav Petkov u64 addr; 3146775763aSDoug Thompson 3156775763aSDoug Thompson /* The K8 treats this as a 40-bit value. However, bits 63-40 will be 3166775763aSDoug Thompson * all ones if the most significant implemented address bit is 1. 3176775763aSDoug Thompson * Here we discard bits 63-40. See section 3.4.2 of AMD publication 3186775763aSDoug Thompson * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1 3196775763aSDoug Thompson * Application Programming. 3206775763aSDoug Thompson */ 3216775763aSDoug Thompson addr = sys_addr & 0x000000ffffffffffull; 3226775763aSDoug Thompson 3237f19bf75SBorislav Petkov return ((addr >= get_dram_base(pvt, nid)) && 3247f19bf75SBorislav Petkov (addr <= get_dram_limit(pvt, nid))); 3256775763aSDoug Thompson } 3266775763aSDoug Thompson 3276775763aSDoug Thompson /* 3286775763aSDoug Thompson * Attempt to map a SysAddr to a node. On success, return a pointer to the 3296775763aSDoug Thompson * mem_ctl_info structure for the node that the SysAddr maps to. 3306775763aSDoug Thompson * 3316775763aSDoug Thompson * On failure, return NULL. 3326775763aSDoug Thompson */ 3336775763aSDoug Thompson static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci, 3346775763aSDoug Thompson u64 sys_addr) 3356775763aSDoug Thompson { 3366775763aSDoug Thompson struct amd64_pvt *pvt; 337c7e5301aSDaniel J Blueman u8 node_id; 3386775763aSDoug Thompson u32 intlv_en, bits; 3396775763aSDoug Thompson 3406775763aSDoug Thompson /* 3416775763aSDoug Thompson * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section 3426775763aSDoug Thompson * 3.4.4.2) registers to map the SysAddr to a node ID. 3436775763aSDoug Thompson */ 3446775763aSDoug Thompson pvt = mci->pvt_info; 3456775763aSDoug Thompson 3466775763aSDoug Thompson /* 3476775763aSDoug Thompson * The value of this field should be the same for all DRAM Base 3486775763aSDoug Thompson * registers. Therefore we arbitrarily choose to read it from the 3496775763aSDoug Thompson * register for node 0. 3506775763aSDoug Thompson */ 3517f19bf75SBorislav Petkov intlv_en = dram_intlv_en(pvt, 0); 3526775763aSDoug Thompson 3536775763aSDoug Thompson if (intlv_en == 0) { 3547f19bf75SBorislav Petkov for (node_id = 0; node_id < DRAM_RANGES; node_id++) { 355d1ea71cdSBorislav Petkov if (base_limit_match(pvt, sys_addr, node_id)) 3566775763aSDoug Thompson goto found; 3576775763aSDoug Thompson } 3588edc5445SBorislav Petkov goto err_no_match; 3598edc5445SBorislav Petkov } 3606775763aSDoug Thompson 36172f158feSBorislav Petkov if (unlikely((intlv_en != 0x01) && 36272f158feSBorislav Petkov (intlv_en != 0x03) && 36372f158feSBorislav Petkov (intlv_en != 0x07))) { 36424f9a7feSBorislav Petkov amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en); 3656775763aSDoug Thompson return NULL; 3666775763aSDoug Thompson } 3676775763aSDoug Thompson 3686775763aSDoug Thompson bits = (((u32) sys_addr) >> 12) & intlv_en; 3696775763aSDoug Thompson 3706775763aSDoug Thompson for (node_id = 0; ; ) { 3717f19bf75SBorislav Petkov if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits) 3726775763aSDoug Thompson break; /* intlv_sel field matches */ 3736775763aSDoug Thompson 3747f19bf75SBorislav Petkov if (++node_id >= DRAM_RANGES) 3756775763aSDoug Thompson goto err_no_match; 3766775763aSDoug Thompson } 3776775763aSDoug Thompson 3786775763aSDoug Thompson /* sanity test for sys_addr */ 379d1ea71cdSBorislav Petkov if (unlikely(!base_limit_match(pvt, sys_addr, node_id))) { 38024f9a7feSBorislav Petkov amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address" 38124f9a7feSBorislav Petkov "range for node %d with node interleaving enabled.\n", 3828edc5445SBorislav Petkov __func__, sys_addr, node_id); 3836775763aSDoug Thompson return NULL; 3846775763aSDoug Thompson } 3856775763aSDoug Thompson 3866775763aSDoug Thompson found: 387b487c33eSBorislav Petkov return edac_mc_find((int)node_id); 3886775763aSDoug Thompson 3896775763aSDoug Thompson err_no_match: 390956b9ba1SJoe Perches edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n", 3916775763aSDoug Thompson (unsigned long)sys_addr); 3926775763aSDoug Thompson 3936775763aSDoug Thompson return NULL; 3946775763aSDoug Thompson } 395e2ce7255SDoug Thompson 396e2ce7255SDoug Thompson /* 39711c75eadSBorislav Petkov * compute the CS base address of the @csrow on the DRAM controller @dct. 39811c75eadSBorislav Petkov * For details see F2x[5C:40] in the processor's BKDG 399e2ce7255SDoug Thompson */ 40011c75eadSBorislav Petkov static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct, 40111c75eadSBorislav Petkov u64 *base, u64 *mask) 402e2ce7255SDoug Thompson { 40311c75eadSBorislav Petkov u64 csbase, csmask, base_bits, mask_bits; 40411c75eadSBorislav Petkov u8 addr_shift; 40511c75eadSBorislav Petkov 40618b94f66SAravind Gopalakrishnan if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) { 40711c75eadSBorislav Petkov csbase = pvt->csels[dct].csbases[csrow]; 40811c75eadSBorislav Petkov csmask = pvt->csels[dct].csmasks[csrow]; 40910ef6b0dSChen, Gong base_bits = GENMASK_ULL(31, 21) | GENMASK_ULL(15, 9); 41010ef6b0dSChen, Gong mask_bits = GENMASK_ULL(29, 21) | GENMASK_ULL(15, 9); 41111c75eadSBorislav Petkov addr_shift = 4; 41294c1acf2SAravind Gopalakrishnan 41394c1acf2SAravind Gopalakrishnan /* 41418b94f66SAravind Gopalakrishnan * F16h and F15h, models 30h and later need two addr_shift values: 41518b94f66SAravind Gopalakrishnan * 8 for high and 6 for low (cf. F16h BKDG). 41694c1acf2SAravind Gopalakrishnan */ 41718b94f66SAravind Gopalakrishnan } else if (pvt->fam == 0x16 || 41818b94f66SAravind Gopalakrishnan (pvt->fam == 0x15 && pvt->model >= 0x30)) { 41994c1acf2SAravind Gopalakrishnan csbase = pvt->csels[dct].csbases[csrow]; 42094c1acf2SAravind Gopalakrishnan csmask = pvt->csels[dct].csmasks[csrow >> 1]; 42194c1acf2SAravind Gopalakrishnan 42210ef6b0dSChen, Gong *base = (csbase & GENMASK_ULL(15, 5)) << 6; 42310ef6b0dSChen, Gong *base |= (csbase & GENMASK_ULL(30, 19)) << 8; 42494c1acf2SAravind Gopalakrishnan 42594c1acf2SAravind Gopalakrishnan *mask = ~0ULL; 42694c1acf2SAravind Gopalakrishnan /* poke holes for the csmask */ 42710ef6b0dSChen, Gong *mask &= ~((GENMASK_ULL(15, 5) << 6) | 42810ef6b0dSChen, Gong (GENMASK_ULL(30, 19) << 8)); 42994c1acf2SAravind Gopalakrishnan 43010ef6b0dSChen, Gong *mask |= (csmask & GENMASK_ULL(15, 5)) << 6; 43110ef6b0dSChen, Gong *mask |= (csmask & GENMASK_ULL(30, 19)) << 8; 43294c1acf2SAravind Gopalakrishnan 43394c1acf2SAravind Gopalakrishnan return; 43411c75eadSBorislav Petkov } else { 43511c75eadSBorislav Petkov csbase = pvt->csels[dct].csbases[csrow]; 43611c75eadSBorislav Petkov csmask = pvt->csels[dct].csmasks[csrow >> 1]; 43711c75eadSBorislav Petkov addr_shift = 8; 43811c75eadSBorislav Petkov 439a4b4bedcSBorislav Petkov if (pvt->fam == 0x15) 44010ef6b0dSChen, Gong base_bits = mask_bits = 44110ef6b0dSChen, Gong GENMASK_ULL(30,19) | GENMASK_ULL(13,5); 44211c75eadSBorislav Petkov else 44310ef6b0dSChen, Gong base_bits = mask_bits = 44410ef6b0dSChen, Gong GENMASK_ULL(28,19) | GENMASK_ULL(13,5); 445e2ce7255SDoug Thompson } 446e2ce7255SDoug Thompson 44711c75eadSBorislav Petkov *base = (csbase & base_bits) << addr_shift; 448e2ce7255SDoug Thompson 44911c75eadSBorislav Petkov *mask = ~0ULL; 45011c75eadSBorislav Petkov /* poke holes for the csmask */ 45111c75eadSBorislav Petkov *mask &= ~(mask_bits << addr_shift); 45211c75eadSBorislav Petkov /* OR them in */ 45311c75eadSBorislav Petkov *mask |= (csmask & mask_bits) << addr_shift; 454e2ce7255SDoug Thompson } 455e2ce7255SDoug Thompson 45611c75eadSBorislav Petkov #define for_each_chip_select(i, dct, pvt) \ 45711c75eadSBorislav Petkov for (i = 0; i < pvt->csels[dct].b_cnt; i++) 45811c75eadSBorislav Petkov 459614ec9d8SBorislav Petkov #define chip_select_base(i, dct, pvt) \ 460614ec9d8SBorislav Petkov pvt->csels[dct].csbases[i] 461614ec9d8SBorislav Petkov 46211c75eadSBorislav Petkov #define for_each_chip_select_mask(i, dct, pvt) \ 46311c75eadSBorislav Petkov for (i = 0; i < pvt->csels[dct].m_cnt; i++) 46411c75eadSBorislav Petkov 4654d30d2bcSYazen Ghannam #define for_each_umc(i) \ 4665e4c5527SYazen Ghannam for (i = 0; i < fam_type->max_mcs; i++) 4674d30d2bcSYazen Ghannam 468e2ce7255SDoug Thompson /* 469e2ce7255SDoug Thompson * @input_addr is an InputAddr associated with the node given by mci. Return the 470e2ce7255SDoug Thompson * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr). 471e2ce7255SDoug Thompson */ 472e2ce7255SDoug Thompson static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr) 473e2ce7255SDoug Thompson { 474e2ce7255SDoug Thompson struct amd64_pvt *pvt; 475e2ce7255SDoug Thompson int csrow; 476e2ce7255SDoug Thompson u64 base, mask; 477e2ce7255SDoug Thompson 478e2ce7255SDoug Thompson pvt = mci->pvt_info; 479e2ce7255SDoug Thompson 48011c75eadSBorislav Petkov for_each_chip_select(csrow, 0, pvt) { 48111c75eadSBorislav Petkov if (!csrow_enabled(csrow, 0, pvt)) 482e2ce7255SDoug Thompson continue; 483e2ce7255SDoug Thompson 48411c75eadSBorislav Petkov get_cs_base_and_mask(pvt, csrow, 0, &base, &mask); 48511c75eadSBorislav Petkov 48611c75eadSBorislav Petkov mask = ~mask; 487e2ce7255SDoug Thompson 488e2ce7255SDoug Thompson if ((input_addr & mask) == (base & mask)) { 489956b9ba1SJoe Perches edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n", 490e2ce7255SDoug Thompson (unsigned long)input_addr, csrow, 491e2ce7255SDoug Thompson pvt->mc_node_id); 492e2ce7255SDoug Thompson 493e2ce7255SDoug Thompson return csrow; 494e2ce7255SDoug Thompson } 495e2ce7255SDoug Thompson } 496956b9ba1SJoe Perches edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n", 497e2ce7255SDoug Thompson (unsigned long)input_addr, pvt->mc_node_id); 498e2ce7255SDoug Thompson 499e2ce7255SDoug Thompson return -1; 500e2ce7255SDoug Thompson } 501e2ce7255SDoug Thompson 502e2ce7255SDoug Thompson /* 503e2ce7255SDoug Thompson * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094) 504e2ce7255SDoug Thompson * for the node represented by mci. Info is passed back in *hole_base, 505e2ce7255SDoug Thompson * *hole_offset, and *hole_size. Function returns 0 if info is valid or 1 if 506e2ce7255SDoug Thompson * info is invalid. Info may be invalid for either of the following reasons: 507e2ce7255SDoug Thompson * 508e2ce7255SDoug Thompson * - The revision of the node is not E or greater. In this case, the DRAM Hole 509e2ce7255SDoug Thompson * Address Register does not exist. 510e2ce7255SDoug Thompson * 511e2ce7255SDoug Thompson * - The DramHoleValid bit is cleared in the DRAM Hole Address Register, 512e2ce7255SDoug Thompson * indicating that its contents are not valid. 513e2ce7255SDoug Thompson * 514e2ce7255SDoug Thompson * The values passed back in *hole_base, *hole_offset, and *hole_size are 515e2ce7255SDoug Thompson * complete 32-bit values despite the fact that the bitfields in the DHAR 516e2ce7255SDoug Thompson * only represent bits 31-24 of the base and offset values. 517e2ce7255SDoug Thompson */ 5182a28ceefSBorislav Petkov static int get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base, 519e2ce7255SDoug Thompson u64 *hole_offset, u64 *hole_size) 520e2ce7255SDoug Thompson { 521e2ce7255SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 522e2ce7255SDoug Thompson 523e2ce7255SDoug Thompson /* only revE and later have the DRAM Hole Address Register */ 524a4b4bedcSBorislav Petkov if (pvt->fam == 0xf && pvt->ext_model < K8_REV_E) { 525956b9ba1SJoe Perches edac_dbg(1, " revision %d for node %d does not support DHAR\n", 526e2ce7255SDoug Thompson pvt->ext_model, pvt->mc_node_id); 527e2ce7255SDoug Thompson return 1; 528e2ce7255SDoug Thompson } 529e2ce7255SDoug Thompson 530bc21fa57SBorislav Petkov /* valid for Fam10h and above */ 531a4b4bedcSBorislav Petkov if (pvt->fam >= 0x10 && !dhar_mem_hoist_valid(pvt)) { 532956b9ba1SJoe Perches edac_dbg(1, " Dram Memory Hoisting is DISABLED on this system\n"); 533e2ce7255SDoug Thompson return 1; 534e2ce7255SDoug Thompson } 535e2ce7255SDoug Thompson 536c8e518d5SBorislav Petkov if (!dhar_valid(pvt)) { 537956b9ba1SJoe Perches edac_dbg(1, " Dram Memory Hoisting is DISABLED on this node %d\n", 538e2ce7255SDoug Thompson pvt->mc_node_id); 539e2ce7255SDoug Thompson return 1; 540e2ce7255SDoug Thompson } 541e2ce7255SDoug Thompson 542e2ce7255SDoug Thompson /* This node has Memory Hoisting */ 543e2ce7255SDoug Thompson 544e2ce7255SDoug Thompson /* +------------------+--------------------+--------------------+----- 545e2ce7255SDoug Thompson * | memory | DRAM hole | relocated | 546e2ce7255SDoug Thompson * | [0, (x - 1)] | [x, 0xffffffff] | addresses from | 547e2ce7255SDoug Thompson * | | | DRAM hole | 548e2ce7255SDoug Thompson * | | | [0x100000000, | 549e2ce7255SDoug Thompson * | | | (0x100000000+ | 550e2ce7255SDoug Thompson * | | | (0xffffffff-x))] | 551e2ce7255SDoug Thompson * +------------------+--------------------+--------------------+----- 552e2ce7255SDoug Thompson * 553e2ce7255SDoug Thompson * Above is a diagram of physical memory showing the DRAM hole and the 554e2ce7255SDoug Thompson * relocated addresses from the DRAM hole. As shown, the DRAM hole 555e2ce7255SDoug Thompson * starts at address x (the base address) and extends through address 556e2ce7255SDoug Thompson * 0xffffffff. The DRAM Hole Address Register (DHAR) relocates the 557e2ce7255SDoug Thompson * addresses in the hole so that they start at 0x100000000. 558e2ce7255SDoug Thompson */ 559e2ce7255SDoug Thompson 5601f31677eSBorislav Petkov *hole_base = dhar_base(pvt); 5611f31677eSBorislav Petkov *hole_size = (1ULL << 32) - *hole_base; 562e2ce7255SDoug Thompson 563a4b4bedcSBorislav Petkov *hole_offset = (pvt->fam > 0xf) ? f10_dhar_offset(pvt) 564a4b4bedcSBorislav Petkov : k8_dhar_offset(pvt); 565e2ce7255SDoug Thompson 566956b9ba1SJoe Perches edac_dbg(1, " DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n", 567e2ce7255SDoug Thompson pvt->mc_node_id, (unsigned long)*hole_base, 568e2ce7255SDoug Thompson (unsigned long)*hole_offset, (unsigned long)*hole_size); 569e2ce7255SDoug Thompson 570e2ce7255SDoug Thompson return 0; 571e2ce7255SDoug Thompson } 5722a28ceefSBorislav Petkov 5732a28ceefSBorislav Petkov #ifdef CONFIG_EDAC_DEBUG 5742a28ceefSBorislav Petkov #define EDAC_DCT_ATTR_SHOW(reg) \ 5752a28ceefSBorislav Petkov static ssize_t reg##_show(struct device *dev, \ 5762a28ceefSBorislav Petkov struct device_attribute *mattr, char *data) \ 5772a28ceefSBorislav Petkov { \ 5782a28ceefSBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); \ 5792a28ceefSBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; \ 5802a28ceefSBorislav Petkov \ 5812a28ceefSBorislav Petkov return sprintf(data, "0x%016llx\n", (u64)pvt->reg); \ 5822a28ceefSBorislav Petkov } 5832a28ceefSBorislav Petkov 5842a28ceefSBorislav Petkov EDAC_DCT_ATTR_SHOW(dhar); 5852a28ceefSBorislav Petkov EDAC_DCT_ATTR_SHOW(dbam0); 5862a28ceefSBorislav Petkov EDAC_DCT_ATTR_SHOW(top_mem); 5872a28ceefSBorislav Petkov EDAC_DCT_ATTR_SHOW(top_mem2); 5882a28ceefSBorislav Petkov 589d19faf0eSDwaipayan Ray static ssize_t dram_hole_show(struct device *dev, struct device_attribute *mattr, 5902a28ceefSBorislav Petkov char *data) 5912a28ceefSBorislav Petkov { 5922a28ceefSBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 5932a28ceefSBorislav Petkov 5942a28ceefSBorislav Petkov u64 hole_base = 0; 5952a28ceefSBorislav Petkov u64 hole_offset = 0; 5962a28ceefSBorislav Petkov u64 hole_size = 0; 5972a28ceefSBorislav Petkov 5982a28ceefSBorislav Petkov get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size); 5992a28ceefSBorislav Petkov 6002a28ceefSBorislav Petkov return sprintf(data, "%llx %llx %llx\n", hole_base, hole_offset, 6012a28ceefSBorislav Petkov hole_size); 6022a28ceefSBorislav Petkov } 6032a28ceefSBorislav Petkov 6042a28ceefSBorislav Petkov /* 6052a28ceefSBorislav Petkov * update NUM_DBG_ATTRS in case you add new members 6062a28ceefSBorislav Petkov */ 6072a28ceefSBorislav Petkov static DEVICE_ATTR(dhar, S_IRUGO, dhar_show, NULL); 6082a28ceefSBorislav Petkov static DEVICE_ATTR(dbam, S_IRUGO, dbam0_show, NULL); 6092a28ceefSBorislav Petkov static DEVICE_ATTR(topmem, S_IRUGO, top_mem_show, NULL); 6102a28ceefSBorislav Petkov static DEVICE_ATTR(topmem2, S_IRUGO, top_mem2_show, NULL); 611d19faf0eSDwaipayan Ray static DEVICE_ATTR_RO(dram_hole); 6122a28ceefSBorislav Petkov 6132a28ceefSBorislav Petkov static struct attribute *dbg_attrs[] = { 6142a28ceefSBorislav Petkov &dev_attr_dhar.attr, 6152a28ceefSBorislav Petkov &dev_attr_dbam.attr, 6162a28ceefSBorislav Petkov &dev_attr_topmem.attr, 6172a28ceefSBorislav Petkov &dev_attr_topmem2.attr, 6182a28ceefSBorislav Petkov &dev_attr_dram_hole.attr, 6192a28ceefSBorislav Petkov NULL 6202a28ceefSBorislav Petkov }; 6212a28ceefSBorislav Petkov 6222a28ceefSBorislav Petkov static const struct attribute_group dbg_group = { 6232a28ceefSBorislav Petkov .attrs = dbg_attrs, 6242a28ceefSBorislav Petkov }; 6252a28ceefSBorislav Petkov 62661810096SBorislav Petkov static ssize_t inject_section_show(struct device *dev, 62761810096SBorislav Petkov struct device_attribute *mattr, char *buf) 62861810096SBorislav Petkov { 62961810096SBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 63061810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 63161810096SBorislav Petkov return sprintf(buf, "0x%x\n", pvt->injection.section); 63261810096SBorislav Petkov } 63361810096SBorislav Petkov 63461810096SBorislav Petkov /* 63561810096SBorislav Petkov * store error injection section value which refers to one of 4 16-byte sections 63661810096SBorislav Petkov * within a 64-byte cacheline 63761810096SBorislav Petkov * 63861810096SBorislav Petkov * range: 0..3 63961810096SBorislav Petkov */ 64061810096SBorislav Petkov static ssize_t inject_section_store(struct device *dev, 64161810096SBorislav Petkov struct device_attribute *mattr, 64261810096SBorislav Petkov const char *data, size_t count) 64361810096SBorislav Petkov { 64461810096SBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 64561810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 64661810096SBorislav Petkov unsigned long value; 64761810096SBorislav Petkov int ret; 64861810096SBorislav Petkov 64961810096SBorislav Petkov ret = kstrtoul(data, 10, &value); 65061810096SBorislav Petkov if (ret < 0) 65161810096SBorislav Petkov return ret; 65261810096SBorislav Petkov 65361810096SBorislav Petkov if (value > 3) { 65461810096SBorislav Petkov amd64_warn("%s: invalid section 0x%lx\n", __func__, value); 65561810096SBorislav Petkov return -EINVAL; 65661810096SBorislav Petkov } 65761810096SBorislav Petkov 65861810096SBorislav Petkov pvt->injection.section = (u32) value; 65961810096SBorislav Petkov return count; 66061810096SBorislav Petkov } 66161810096SBorislav Petkov 66261810096SBorislav Petkov static ssize_t inject_word_show(struct device *dev, 66361810096SBorislav Petkov struct device_attribute *mattr, char *buf) 66461810096SBorislav Petkov { 66561810096SBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 66661810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 66761810096SBorislav Petkov return sprintf(buf, "0x%x\n", pvt->injection.word); 66861810096SBorislav Petkov } 66961810096SBorislav Petkov 67061810096SBorislav Petkov /* 67161810096SBorislav Petkov * store error injection word value which refers to one of 9 16-bit word of the 67261810096SBorislav Petkov * 16-byte (128-bit + ECC bits) section 67361810096SBorislav Petkov * 67461810096SBorislav Petkov * range: 0..8 67561810096SBorislav Petkov */ 67661810096SBorislav Petkov static ssize_t inject_word_store(struct device *dev, 67761810096SBorislav Petkov struct device_attribute *mattr, 67861810096SBorislav Petkov const char *data, size_t count) 67961810096SBorislav Petkov { 68061810096SBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 68161810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 68261810096SBorislav Petkov unsigned long value; 68361810096SBorislav Petkov int ret; 68461810096SBorislav Petkov 68561810096SBorislav Petkov ret = kstrtoul(data, 10, &value); 68661810096SBorislav Petkov if (ret < 0) 68761810096SBorislav Petkov return ret; 68861810096SBorislav Petkov 68961810096SBorislav Petkov if (value > 8) { 69061810096SBorislav Petkov amd64_warn("%s: invalid word 0x%lx\n", __func__, value); 69161810096SBorislav Petkov return -EINVAL; 69261810096SBorislav Petkov } 69361810096SBorislav Petkov 69461810096SBorislav Petkov pvt->injection.word = (u32) value; 69561810096SBorislav Petkov return count; 69661810096SBorislav Petkov } 69761810096SBorislav Petkov 69861810096SBorislav Petkov static ssize_t inject_ecc_vector_show(struct device *dev, 69961810096SBorislav Petkov struct device_attribute *mattr, 70061810096SBorislav Petkov char *buf) 70161810096SBorislav Petkov { 70261810096SBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 70361810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 70461810096SBorislav Petkov return sprintf(buf, "0x%x\n", pvt->injection.bit_map); 70561810096SBorislav Petkov } 70661810096SBorislav Petkov 70761810096SBorislav Petkov /* 70861810096SBorislav Petkov * store 16 bit error injection vector which enables injecting errors to the 70961810096SBorislav Petkov * corresponding bit within the error injection word above. When used during a 71061810096SBorislav Petkov * DRAM ECC read, it holds the contents of the of the DRAM ECC bits. 71161810096SBorislav Petkov */ 71261810096SBorislav Petkov static ssize_t inject_ecc_vector_store(struct device *dev, 71361810096SBorislav Petkov struct device_attribute *mattr, 71461810096SBorislav Petkov const char *data, size_t count) 71561810096SBorislav Petkov { 71661810096SBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 71761810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 71861810096SBorislav Petkov unsigned long value; 71961810096SBorislav Petkov int ret; 72061810096SBorislav Petkov 72161810096SBorislav Petkov ret = kstrtoul(data, 16, &value); 72261810096SBorislav Petkov if (ret < 0) 72361810096SBorislav Petkov return ret; 72461810096SBorislav Petkov 72561810096SBorislav Petkov if (value & 0xFFFF0000) { 72661810096SBorislav Petkov amd64_warn("%s: invalid EccVector: 0x%lx\n", __func__, value); 72761810096SBorislav Petkov return -EINVAL; 72861810096SBorislav Petkov } 72961810096SBorislav Petkov 73061810096SBorislav Petkov pvt->injection.bit_map = (u32) value; 73161810096SBorislav Petkov return count; 73261810096SBorislav Petkov } 73361810096SBorislav Petkov 73461810096SBorislav Petkov /* 73561810096SBorislav Petkov * Do a DRAM ECC read. Assemble staged values in the pvt area, format into 73661810096SBorislav Petkov * fields needed by the injection registers and read the NB Array Data Port. 73761810096SBorislav Petkov */ 73861810096SBorislav Petkov static ssize_t inject_read_store(struct device *dev, 73961810096SBorislav Petkov struct device_attribute *mattr, 74061810096SBorislav Petkov const char *data, size_t count) 74161810096SBorislav Petkov { 74261810096SBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 74361810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 74461810096SBorislav Petkov unsigned long value; 74561810096SBorislav Petkov u32 section, word_bits; 74661810096SBorislav Petkov int ret; 74761810096SBorislav Petkov 74861810096SBorislav Petkov ret = kstrtoul(data, 10, &value); 74961810096SBorislav Petkov if (ret < 0) 75061810096SBorislav Petkov return ret; 75161810096SBorislav Petkov 75261810096SBorislav Petkov /* Form value to choose 16-byte section of cacheline */ 75361810096SBorislav Petkov section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section); 75461810096SBorislav Petkov 75561810096SBorislav Petkov amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section); 75661810096SBorislav Petkov 75761810096SBorislav Petkov word_bits = SET_NB_DRAM_INJECTION_READ(pvt->injection); 75861810096SBorislav Petkov 75961810096SBorislav Petkov /* Issue 'word' and 'bit' along with the READ request */ 76061810096SBorislav Petkov amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits); 76161810096SBorislav Petkov 76261810096SBorislav Petkov edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits); 76361810096SBorislav Petkov 76461810096SBorislav Petkov return count; 76561810096SBorislav Petkov } 76661810096SBorislav Petkov 76761810096SBorislav Petkov /* 76861810096SBorislav Petkov * Do a DRAM ECC write. Assemble staged values in the pvt area and format into 76961810096SBorislav Petkov * fields needed by the injection registers. 77061810096SBorislav Petkov */ 77161810096SBorislav Petkov static ssize_t inject_write_store(struct device *dev, 77261810096SBorislav Petkov struct device_attribute *mattr, 77361810096SBorislav Petkov const char *data, size_t count) 77461810096SBorislav Petkov { 77561810096SBorislav Petkov struct mem_ctl_info *mci = to_mci(dev); 77661810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 77761810096SBorislav Petkov u32 section, word_bits, tmp; 77861810096SBorislav Petkov unsigned long value; 77961810096SBorislav Petkov int ret; 78061810096SBorislav Petkov 78161810096SBorislav Petkov ret = kstrtoul(data, 10, &value); 78261810096SBorislav Petkov if (ret < 0) 78361810096SBorislav Petkov return ret; 78461810096SBorislav Petkov 78561810096SBorislav Petkov /* Form value to choose 16-byte section of cacheline */ 78661810096SBorislav Petkov section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section); 78761810096SBorislav Petkov 78861810096SBorislav Petkov amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section); 78961810096SBorislav Petkov 79061810096SBorislav Petkov word_bits = SET_NB_DRAM_INJECTION_WRITE(pvt->injection); 79161810096SBorislav Petkov 79261810096SBorislav Petkov pr_notice_once("Don't forget to decrease MCE polling interval in\n" 79361810096SBorislav Petkov "/sys/bus/machinecheck/devices/machinecheck<CPUNUM>/check_interval\n" 79461810096SBorislav Petkov "so that you can get the error report faster.\n"); 79561810096SBorislav Petkov 79661810096SBorislav Petkov on_each_cpu(disable_caches, NULL, 1); 79761810096SBorislav Petkov 79861810096SBorislav Petkov /* Issue 'word' and 'bit' along with the READ request */ 79961810096SBorislav Petkov amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits); 80061810096SBorislav Petkov 80161810096SBorislav Petkov retry: 80261810096SBorislav Petkov /* wait until injection happens */ 80361810096SBorislav Petkov amd64_read_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, &tmp); 80461810096SBorislav Petkov if (tmp & F10_NB_ARR_ECC_WR_REQ) { 80561810096SBorislav Petkov cpu_relax(); 80661810096SBorislav Petkov goto retry; 80761810096SBorislav Petkov } 80861810096SBorislav Petkov 80961810096SBorislav Petkov on_each_cpu(enable_caches, NULL, 1); 81061810096SBorislav Petkov 81161810096SBorislav Petkov edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits); 81261810096SBorislav Petkov 81361810096SBorislav Petkov return count; 81461810096SBorislav Petkov } 81561810096SBorislav Petkov 81661810096SBorislav Petkov /* 81761810096SBorislav Petkov * update NUM_INJ_ATTRS in case you add new members 81861810096SBorislav Petkov */ 81961810096SBorislav Petkov 820d19faf0eSDwaipayan Ray static DEVICE_ATTR_RW(inject_section); 821d19faf0eSDwaipayan Ray static DEVICE_ATTR_RW(inject_word); 822d19faf0eSDwaipayan Ray static DEVICE_ATTR_RW(inject_ecc_vector); 823d19faf0eSDwaipayan Ray static DEVICE_ATTR_WO(inject_write); 824d19faf0eSDwaipayan Ray static DEVICE_ATTR_WO(inject_read); 82561810096SBorislav Petkov 82661810096SBorislav Petkov static struct attribute *inj_attrs[] = { 82761810096SBorislav Petkov &dev_attr_inject_section.attr, 82861810096SBorislav Petkov &dev_attr_inject_word.attr, 82961810096SBorislav Petkov &dev_attr_inject_ecc_vector.attr, 83061810096SBorislav Petkov &dev_attr_inject_write.attr, 83161810096SBorislav Petkov &dev_attr_inject_read.attr, 83261810096SBorislav Petkov NULL 83361810096SBorislav Petkov }; 83461810096SBorislav Petkov 83561810096SBorislav Petkov static umode_t inj_is_visible(struct kobject *kobj, struct attribute *attr, int idx) 83661810096SBorislav Petkov { 83761810096SBorislav Petkov struct device *dev = kobj_to_dev(kobj); 83861810096SBorislav Petkov struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev); 83961810096SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 84061810096SBorislav Petkov 8411865bc71SBorislav Petkov /* Families which have that injection hw */ 8421865bc71SBorislav Petkov if (pvt->fam >= 0x10 && pvt->fam <= 0x16) 84361810096SBorislav Petkov return attr->mode; 8441865bc71SBorislav Petkov 8451865bc71SBorislav Petkov return 0; 84661810096SBorislav Petkov } 84761810096SBorislav Petkov 84861810096SBorislav Petkov static const struct attribute_group inj_group = { 84961810096SBorislav Petkov .attrs = inj_attrs, 85061810096SBorislav Petkov .is_visible = inj_is_visible, 85161810096SBorislav Petkov }; 85261810096SBorislav Petkov #endif /* CONFIG_EDAC_DEBUG */ 853e2ce7255SDoug Thompson 85493c2df58SDoug Thompson /* 85593c2df58SDoug Thompson * Return the DramAddr that the SysAddr given by @sys_addr maps to. It is 85693c2df58SDoug Thompson * assumed that sys_addr maps to the node given by mci. 85793c2df58SDoug Thompson * 85893c2df58SDoug Thompson * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section 85993c2df58SDoug Thompson * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a 86093c2df58SDoug Thompson * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled, 86193c2df58SDoug Thompson * then it is also involved in translating a SysAddr to a DramAddr. Sections 86293c2df58SDoug Thompson * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting. 86393c2df58SDoug Thompson * These parts of the documentation are unclear. I interpret them as follows: 86493c2df58SDoug Thompson * 86593c2df58SDoug Thompson * When node n receives a SysAddr, it processes the SysAddr as follows: 86693c2df58SDoug Thompson * 86793c2df58SDoug Thompson * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM 86893c2df58SDoug Thompson * Limit registers for node n. If the SysAddr is not within the range 86993c2df58SDoug Thompson * specified by the base and limit values, then node n ignores the Sysaddr 87093c2df58SDoug Thompson * (since it does not map to node n). Otherwise continue to step 2 below. 87193c2df58SDoug Thompson * 87293c2df58SDoug Thompson * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is 87393c2df58SDoug Thompson * disabled so skip to step 3 below. Otherwise see if the SysAddr is within 87493c2df58SDoug Thompson * the range of relocated addresses (starting at 0x100000000) from the DRAM 87593c2df58SDoug Thompson * hole. If not, skip to step 3 below. Else get the value of the 87693c2df58SDoug Thompson * DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the 87793c2df58SDoug Thompson * offset defined by this value from the SysAddr. 87893c2df58SDoug Thompson * 87993c2df58SDoug Thompson * 3. Obtain the base address for node n from the DRAMBase field of the DRAM 88093c2df58SDoug Thompson * Base register for node n. To obtain the DramAddr, subtract the base 88193c2df58SDoug Thompson * address from the SysAddr, as shown near the start of section 3.4.4 (p.70). 88293c2df58SDoug Thompson */ 88393c2df58SDoug Thompson static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr) 88493c2df58SDoug Thompson { 8857f19bf75SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 88693c2df58SDoug Thompson u64 dram_base, hole_base, hole_offset, hole_size, dram_addr; 8871f31677eSBorislav Petkov int ret; 88893c2df58SDoug Thompson 8897f19bf75SBorislav Petkov dram_base = get_dram_base(pvt, pvt->mc_node_id); 89093c2df58SDoug Thompson 8912a28ceefSBorislav Petkov ret = get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size); 89293c2df58SDoug Thompson if (!ret) { 8931f31677eSBorislav Petkov if ((sys_addr >= (1ULL << 32)) && 8941f31677eSBorislav Petkov (sys_addr < ((1ULL << 32) + hole_size))) { 89593c2df58SDoug Thompson /* use DHAR to translate SysAddr to DramAddr */ 89693c2df58SDoug Thompson dram_addr = sys_addr - hole_offset; 89793c2df58SDoug Thompson 898956b9ba1SJoe Perches edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n", 89993c2df58SDoug Thompson (unsigned long)sys_addr, 90093c2df58SDoug Thompson (unsigned long)dram_addr); 90193c2df58SDoug Thompson 90293c2df58SDoug Thompson return dram_addr; 90393c2df58SDoug Thompson } 90493c2df58SDoug Thompson } 90593c2df58SDoug Thompson 90693c2df58SDoug Thompson /* 90793c2df58SDoug Thompson * Translate the SysAddr to a DramAddr as shown near the start of 90893c2df58SDoug Thompson * section 3.4.4 (p. 70). Although sys_addr is a 64-bit value, the k8 90993c2df58SDoug Thompson * only deals with 40-bit values. Therefore we discard bits 63-40 of 91093c2df58SDoug Thompson * sys_addr below. If bit 39 of sys_addr is 1 then the bits we 91193c2df58SDoug Thompson * discard are all 1s. Otherwise the bits we discard are all 0s. See 91293c2df58SDoug Thompson * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture 91393c2df58SDoug Thompson * Programmer's Manual Volume 1 Application Programming. 91493c2df58SDoug Thompson */ 91510ef6b0dSChen, Gong dram_addr = (sys_addr & GENMASK_ULL(39, 0)) - dram_base; 91693c2df58SDoug Thompson 917956b9ba1SJoe Perches edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n", 918956b9ba1SJoe Perches (unsigned long)sys_addr, (unsigned long)dram_addr); 91993c2df58SDoug Thompson return dram_addr; 92093c2df58SDoug Thompson } 92193c2df58SDoug Thompson 92293c2df58SDoug Thompson /* 92393c2df58SDoug Thompson * @intlv_en is the value of the IntlvEn field from a DRAM Base register 92493c2df58SDoug Thompson * (section 3.4.4.1). Return the number of bits from a SysAddr that are used 92593c2df58SDoug Thompson * for node interleaving. 92693c2df58SDoug Thompson */ 92793c2df58SDoug Thompson static int num_node_interleave_bits(unsigned intlv_en) 92893c2df58SDoug Thompson { 92993c2df58SDoug Thompson static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 }; 93093c2df58SDoug Thompson int n; 93193c2df58SDoug Thompson 93293c2df58SDoug Thompson BUG_ON(intlv_en > 7); 93393c2df58SDoug Thompson n = intlv_shift_table[intlv_en]; 93493c2df58SDoug Thompson return n; 93593c2df58SDoug Thompson } 93693c2df58SDoug Thompson 93793c2df58SDoug Thompson /* Translate the DramAddr given by @dram_addr to an InputAddr. */ 93893c2df58SDoug Thompson static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr) 93993c2df58SDoug Thompson { 94093c2df58SDoug Thompson struct amd64_pvt *pvt; 94193c2df58SDoug Thompson int intlv_shift; 94293c2df58SDoug Thompson u64 input_addr; 94393c2df58SDoug Thompson 94493c2df58SDoug Thompson pvt = mci->pvt_info; 94593c2df58SDoug Thompson 94693c2df58SDoug Thompson /* 94793c2df58SDoug Thompson * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E) 94893c2df58SDoug Thompson * concerning translating a DramAddr to an InputAddr. 94993c2df58SDoug Thompson */ 9507f19bf75SBorislav Petkov intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0)); 95110ef6b0dSChen, Gong input_addr = ((dram_addr >> intlv_shift) & GENMASK_ULL(35, 12)) + 95293c2df58SDoug Thompson (dram_addr & 0xfff); 95393c2df58SDoug Thompson 954956b9ba1SJoe Perches edac_dbg(2, " Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n", 95593c2df58SDoug Thompson intlv_shift, (unsigned long)dram_addr, 95693c2df58SDoug Thompson (unsigned long)input_addr); 95793c2df58SDoug Thompson 95893c2df58SDoug Thompson return input_addr; 95993c2df58SDoug Thompson } 96093c2df58SDoug Thompson 96193c2df58SDoug Thompson /* 96293c2df58SDoug Thompson * Translate the SysAddr represented by @sys_addr to an InputAddr. It is 96393c2df58SDoug Thompson * assumed that @sys_addr maps to the node given by mci. 96493c2df58SDoug Thompson */ 96593c2df58SDoug Thompson static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr) 96693c2df58SDoug Thompson { 96793c2df58SDoug Thompson u64 input_addr; 96893c2df58SDoug Thompson 96993c2df58SDoug Thompson input_addr = 97093c2df58SDoug Thompson dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr)); 97193c2df58SDoug Thompson 972c19ca6cbSMasanari Iida edac_dbg(2, "SysAddr 0x%lx translates to InputAddr 0x%lx\n", 97393c2df58SDoug Thompson (unsigned long)sys_addr, (unsigned long)input_addr); 97493c2df58SDoug Thompson 97593c2df58SDoug Thompson return input_addr; 97693c2df58SDoug Thompson } 97793c2df58SDoug Thompson 97893c2df58SDoug Thompson /* Map the Error address to a PAGE and PAGE OFFSET. */ 97993c2df58SDoug Thompson static inline void error_address_to_page_and_offset(u64 error_address, 98033ca0643SBorislav Petkov struct err_info *err) 98193c2df58SDoug Thompson { 98233ca0643SBorislav Petkov err->page = (u32) (error_address >> PAGE_SHIFT); 98333ca0643SBorislav Petkov err->offset = ((u32) error_address) & ~PAGE_MASK; 98493c2df58SDoug Thompson } 98593c2df58SDoug Thompson 98693c2df58SDoug Thompson /* 98793c2df58SDoug Thompson * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address 98893c2df58SDoug Thompson * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers 98993c2df58SDoug Thompson * of a node that detected an ECC memory error. mci represents the node that 99093c2df58SDoug Thompson * the error address maps to (possibly different from the node that detected 99193c2df58SDoug Thompson * the error). Return the number of the csrow that sys_addr maps to, or -1 on 99293c2df58SDoug Thompson * error. 99393c2df58SDoug Thompson */ 99493c2df58SDoug Thompson static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr) 99593c2df58SDoug Thompson { 99693c2df58SDoug Thompson int csrow; 99793c2df58SDoug Thompson 99893c2df58SDoug Thompson csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr)); 99993c2df58SDoug Thompson 100093c2df58SDoug Thompson if (csrow == -1) 100124f9a7feSBorislav Petkov amd64_mc_err(mci, "Failed to translate InputAddr to csrow for " 100293c2df58SDoug Thompson "address 0x%lx\n", (unsigned long)sys_addr); 100393c2df58SDoug Thompson return csrow; 100493c2df58SDoug Thompson } 1005e2ce7255SDoug Thompson 1006b3218ae4SYazen Ghannam /* Protect the PCI config register pairs used for DF indirect access. */ 1007b3218ae4SYazen Ghannam static DEFINE_MUTEX(df_indirect_mutex); 1008b3218ae4SYazen Ghannam 1009b3218ae4SYazen Ghannam /* 1010b3218ae4SYazen Ghannam * Data Fabric Indirect Access uses FICAA/FICAD. 1011b3218ae4SYazen Ghannam * 1012b3218ae4SYazen Ghannam * Fabric Indirect Configuration Access Address (FICAA): Constructed based 1013b3218ae4SYazen Ghannam * on the device's Instance Id and the PCI function and register offset of 1014b3218ae4SYazen Ghannam * the desired register. 1015b3218ae4SYazen Ghannam * 1016b3218ae4SYazen Ghannam * Fabric Indirect Configuration Access Data (FICAD): There are FICAD LO 1017b3218ae4SYazen Ghannam * and FICAD HI registers but so far we only need the LO register. 1018448c3d60SYazen Ghannam * 1019448c3d60SYazen Ghannam * Use Instance Id 0xFF to indicate a broadcast read. 1020b3218ae4SYazen Ghannam */ 1021448c3d60SYazen Ghannam #define DF_BROADCAST 0xFF 1022448c3d60SYazen Ghannam static int __df_indirect_read(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo) 1023b3218ae4SYazen Ghannam { 1024b3218ae4SYazen Ghannam struct pci_dev *F4; 1025b3218ae4SYazen Ghannam u32 ficaa; 1026b3218ae4SYazen Ghannam int err = -ENODEV; 1027b3218ae4SYazen Ghannam 1028b3218ae4SYazen Ghannam if (node >= amd_nb_num()) 1029b3218ae4SYazen Ghannam goto out; 1030b3218ae4SYazen Ghannam 1031b3218ae4SYazen Ghannam F4 = node_to_amd_nb(node)->link; 1032b3218ae4SYazen Ghannam if (!F4) 1033b3218ae4SYazen Ghannam goto out; 1034b3218ae4SYazen Ghannam 1035448c3d60SYazen Ghannam ficaa = (instance_id == DF_BROADCAST) ? 0 : 1; 1036b3218ae4SYazen Ghannam ficaa |= reg & 0x3FC; 1037b3218ae4SYazen Ghannam ficaa |= (func & 0x7) << 11; 1038b3218ae4SYazen Ghannam ficaa |= instance_id << 16; 1039b3218ae4SYazen Ghannam 1040b3218ae4SYazen Ghannam mutex_lock(&df_indirect_mutex); 1041b3218ae4SYazen Ghannam 1042b3218ae4SYazen Ghannam err = pci_write_config_dword(F4, 0x5C, ficaa); 1043b3218ae4SYazen Ghannam if (err) { 1044b3218ae4SYazen Ghannam pr_warn("Error writing DF Indirect FICAA, FICAA=0x%x\n", ficaa); 1045b3218ae4SYazen Ghannam goto out_unlock; 1046b3218ae4SYazen Ghannam } 1047b3218ae4SYazen Ghannam 1048b3218ae4SYazen Ghannam err = pci_read_config_dword(F4, 0x98, lo); 1049b3218ae4SYazen Ghannam if (err) 1050b3218ae4SYazen Ghannam pr_warn("Error reading DF Indirect FICAD LO, FICAA=0x%x.\n", ficaa); 1051b3218ae4SYazen Ghannam 1052b3218ae4SYazen Ghannam out_unlock: 1053b3218ae4SYazen Ghannam mutex_unlock(&df_indirect_mutex); 1054b3218ae4SYazen Ghannam 1055b3218ae4SYazen Ghannam out: 1056b3218ae4SYazen Ghannam return err; 1057b3218ae4SYazen Ghannam } 1058b3218ae4SYazen Ghannam 1059448c3d60SYazen Ghannam static int df_indirect_read_instance(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo) 1060448c3d60SYazen Ghannam { 1061448c3d60SYazen Ghannam return __df_indirect_read(node, func, reg, instance_id, lo); 1062448c3d60SYazen Ghannam } 1063448c3d60SYazen Ghannam 1064448c3d60SYazen Ghannam static int df_indirect_read_broadcast(u16 node, u8 func, u16 reg, u32 *lo) 1065448c3d60SYazen Ghannam { 1066448c3d60SYazen Ghannam return __df_indirect_read(node, func, reg, DF_BROADCAST, lo); 1067448c3d60SYazen Ghannam } 1068448c3d60SYazen Ghannam 106970aeb807SYazen Ghannam struct addr_ctx { 107070aeb807SYazen Ghannam u64 ret_addr; 107170aeb807SYazen Ghannam u32 tmp; 107270aeb807SYazen Ghannam u16 nid; 107370aeb807SYazen Ghannam u8 inst_id; 107470aeb807SYazen Ghannam }; 107570aeb807SYazen Ghannam 10760b746e8cSYazen Ghannam static int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) 10770b746e8cSYazen Ghannam { 10780b746e8cSYazen Ghannam u64 dram_base_addr, dram_limit_addr, dram_hole_base; 10790b746e8cSYazen Ghannam 10800b746e8cSYazen Ghannam u8 die_id_shift, die_id_mask, socket_id_shift, socket_id_mask; 10810b746e8cSYazen Ghannam u8 intlv_num_dies, intlv_num_chan, intlv_num_sockets; 10820b746e8cSYazen Ghannam u8 intlv_addr_sel, intlv_addr_bit; 10830b746e8cSYazen Ghannam u8 num_intlv_bits, hashed_bit; 10840b746e8cSYazen Ghannam u8 lgcy_mmio_hole_en, base = 0; 10850b746e8cSYazen Ghannam u8 cs_mask, cs_id = 0; 10860b746e8cSYazen Ghannam bool hash_enabled = false; 10870b746e8cSYazen Ghannam 108870aeb807SYazen Ghannam struct addr_ctx ctx; 108970aeb807SYazen Ghannam 109070aeb807SYazen Ghannam memset(&ctx, 0, sizeof(ctx)); 109170aeb807SYazen Ghannam 109270aeb807SYazen Ghannam /* Start from the normalized address */ 109370aeb807SYazen Ghannam ctx.ret_addr = norm_addr; 109470aeb807SYazen Ghannam 109570aeb807SYazen Ghannam ctx.nid = nid; 109670aeb807SYazen Ghannam ctx.inst_id = umc; 109770aeb807SYazen Ghannam 10980b746e8cSYazen Ghannam /* Read D18F0x1B4 (DramOffset), check if base 1 is used. */ 109970aeb807SYazen Ghannam if (df_indirect_read_instance(nid, 0, 0x1B4, umc, &ctx.tmp)) 11000b746e8cSYazen Ghannam goto out_err; 11010b746e8cSYazen Ghannam 11020b746e8cSYazen Ghannam /* Remove HiAddrOffset from normalized address, if enabled: */ 110370aeb807SYazen Ghannam if (ctx.tmp & BIT(0)) { 110470aeb807SYazen Ghannam u64 hi_addr_offset = (ctx.tmp & GENMASK_ULL(31, 20)) << 8; 11050b746e8cSYazen Ghannam 11060b746e8cSYazen Ghannam if (norm_addr >= hi_addr_offset) { 110770aeb807SYazen Ghannam ctx.ret_addr -= hi_addr_offset; 11080b746e8cSYazen Ghannam base = 1; 11090b746e8cSYazen Ghannam } 11100b746e8cSYazen Ghannam } 11110b746e8cSYazen Ghannam 11120b746e8cSYazen Ghannam /* Read D18F0x110 (DramBaseAddress). */ 111370aeb807SYazen Ghannam if (df_indirect_read_instance(nid, 0, 0x110 + (8 * base), umc, &ctx.tmp)) 11140b746e8cSYazen Ghannam goto out_err; 11150b746e8cSYazen Ghannam 11160b746e8cSYazen Ghannam /* Check if address range is valid. */ 111770aeb807SYazen Ghannam if (!(ctx.tmp & BIT(0))) { 11180b746e8cSYazen Ghannam pr_err("%s: Invalid DramBaseAddress range: 0x%x.\n", 111970aeb807SYazen Ghannam __func__, ctx.tmp); 11200b746e8cSYazen Ghannam goto out_err; 11210b746e8cSYazen Ghannam } 11220b746e8cSYazen Ghannam 112370aeb807SYazen Ghannam lgcy_mmio_hole_en = ctx.tmp & BIT(1); 112470aeb807SYazen Ghannam intlv_num_chan = (ctx.tmp >> 4) & 0xF; 112570aeb807SYazen Ghannam intlv_addr_sel = (ctx.tmp >> 8) & 0x7; 112670aeb807SYazen Ghannam dram_base_addr = (ctx.tmp & GENMASK_ULL(31, 12)) << 16; 11270b746e8cSYazen Ghannam 11280b746e8cSYazen Ghannam /* {0, 1, 2, 3} map to address bits {8, 9, 10, 11} respectively */ 11290b746e8cSYazen Ghannam if (intlv_addr_sel > 3) { 11300b746e8cSYazen Ghannam pr_err("%s: Invalid interleave address select %d.\n", 11310b746e8cSYazen Ghannam __func__, intlv_addr_sel); 11320b746e8cSYazen Ghannam goto out_err; 11330b746e8cSYazen Ghannam } 11340b746e8cSYazen Ghannam 11350b746e8cSYazen Ghannam /* Read D18F0x114 (DramLimitAddress). */ 113670aeb807SYazen Ghannam if (df_indirect_read_instance(nid, 0, 0x114 + (8 * base), umc, &ctx.tmp)) 11370b746e8cSYazen Ghannam goto out_err; 11380b746e8cSYazen Ghannam 113970aeb807SYazen Ghannam intlv_num_sockets = (ctx.tmp >> 8) & 0x1; 114070aeb807SYazen Ghannam intlv_num_dies = (ctx.tmp >> 10) & 0x3; 114170aeb807SYazen Ghannam dram_limit_addr = ((ctx.tmp & GENMASK_ULL(31, 12)) << 16) | GENMASK_ULL(27, 0); 11420b746e8cSYazen Ghannam 11430b746e8cSYazen Ghannam intlv_addr_bit = intlv_addr_sel + 8; 11440b746e8cSYazen Ghannam 11450b746e8cSYazen Ghannam /* Re-use intlv_num_chan by setting it equal to log2(#channels) */ 11460b746e8cSYazen Ghannam switch (intlv_num_chan) { 11470b746e8cSYazen Ghannam case 0: intlv_num_chan = 0; break; 11480b746e8cSYazen Ghannam case 1: intlv_num_chan = 1; break; 11490b746e8cSYazen Ghannam case 3: intlv_num_chan = 2; break; 11500b746e8cSYazen Ghannam case 5: intlv_num_chan = 3; break; 11510b746e8cSYazen Ghannam case 7: intlv_num_chan = 4; break; 11520b746e8cSYazen Ghannam 11530b746e8cSYazen Ghannam case 8: intlv_num_chan = 1; 11540b746e8cSYazen Ghannam hash_enabled = true; 11550b746e8cSYazen Ghannam break; 11560b746e8cSYazen Ghannam default: 11570b746e8cSYazen Ghannam pr_err("%s: Invalid number of interleaved channels %d.\n", 11580b746e8cSYazen Ghannam __func__, intlv_num_chan); 11590b746e8cSYazen Ghannam goto out_err; 11600b746e8cSYazen Ghannam } 11610b746e8cSYazen Ghannam 11620b746e8cSYazen Ghannam num_intlv_bits = intlv_num_chan; 11630b746e8cSYazen Ghannam 11640b746e8cSYazen Ghannam if (intlv_num_dies > 2) { 11650b746e8cSYazen Ghannam pr_err("%s: Invalid number of interleaved nodes/dies %d.\n", 11660b746e8cSYazen Ghannam __func__, intlv_num_dies); 11670b746e8cSYazen Ghannam goto out_err; 11680b746e8cSYazen Ghannam } 11690b746e8cSYazen Ghannam 11700b746e8cSYazen Ghannam num_intlv_bits += intlv_num_dies; 11710b746e8cSYazen Ghannam 11720b746e8cSYazen Ghannam /* Add a bit if sockets are interleaved. */ 11730b746e8cSYazen Ghannam num_intlv_bits += intlv_num_sockets; 11740b746e8cSYazen Ghannam 11750b746e8cSYazen Ghannam /* Assert num_intlv_bits <= 4 */ 11760b746e8cSYazen Ghannam if (num_intlv_bits > 4) { 11770b746e8cSYazen Ghannam pr_err("%s: Invalid interleave bits %d.\n", 11780b746e8cSYazen Ghannam __func__, num_intlv_bits); 11790b746e8cSYazen Ghannam goto out_err; 11800b746e8cSYazen Ghannam } 11810b746e8cSYazen Ghannam 11820b746e8cSYazen Ghannam if (num_intlv_bits > 0) { 11830b746e8cSYazen Ghannam u64 temp_addr_x, temp_addr_i, temp_addr_y; 11840b746e8cSYazen Ghannam u8 die_id_bit, sock_id_bit, cs_fabric_id; 11850b746e8cSYazen Ghannam 11860b746e8cSYazen Ghannam /* 11870b746e8cSYazen Ghannam * Read FabricBlockInstanceInformation3_CS[BlockFabricID]. 11880b746e8cSYazen Ghannam * This is the fabric id for this coherent slave. Use 11890b746e8cSYazen Ghannam * umc/channel# as instance id of the coherent slave 11900b746e8cSYazen Ghannam * for FICAA. 11910b746e8cSYazen Ghannam */ 119270aeb807SYazen Ghannam if (df_indirect_read_instance(nid, 0, 0x50, umc, &ctx.tmp)) 11930b746e8cSYazen Ghannam goto out_err; 11940b746e8cSYazen Ghannam 119570aeb807SYazen Ghannam cs_fabric_id = (ctx.tmp >> 8) & 0xFF; 11960b746e8cSYazen Ghannam die_id_bit = 0; 11970b746e8cSYazen Ghannam 11980b746e8cSYazen Ghannam /* If interleaved over more than 1 channel: */ 11990b746e8cSYazen Ghannam if (intlv_num_chan) { 12000b746e8cSYazen Ghannam die_id_bit = intlv_num_chan; 12010b746e8cSYazen Ghannam cs_mask = (1 << die_id_bit) - 1; 12020b746e8cSYazen Ghannam cs_id = cs_fabric_id & cs_mask; 12030b746e8cSYazen Ghannam } 12040b746e8cSYazen Ghannam 12050b746e8cSYazen Ghannam sock_id_bit = die_id_bit; 12060b746e8cSYazen Ghannam 12070b746e8cSYazen Ghannam /* Read D18F1x208 (SystemFabricIdMask). */ 12080b746e8cSYazen Ghannam if (intlv_num_dies || intlv_num_sockets) 120970aeb807SYazen Ghannam if (df_indirect_read_broadcast(nid, 1, 0x208, &ctx.tmp)) 12100b746e8cSYazen Ghannam goto out_err; 12110b746e8cSYazen Ghannam 12120b746e8cSYazen Ghannam /* If interleaved over more than 1 die. */ 12130b746e8cSYazen Ghannam if (intlv_num_dies) { 12140b746e8cSYazen Ghannam sock_id_bit = die_id_bit + intlv_num_dies; 121570aeb807SYazen Ghannam die_id_shift = (ctx.tmp >> 24) & 0xF; 121670aeb807SYazen Ghannam die_id_mask = (ctx.tmp >> 8) & 0xFF; 12170b746e8cSYazen Ghannam 12180b746e8cSYazen Ghannam cs_id |= ((cs_fabric_id & die_id_mask) >> die_id_shift) << die_id_bit; 12190b746e8cSYazen Ghannam } 12200b746e8cSYazen Ghannam 12210b746e8cSYazen Ghannam /* If interleaved over more than 1 socket. */ 12220b746e8cSYazen Ghannam if (intlv_num_sockets) { 122370aeb807SYazen Ghannam socket_id_shift = (ctx.tmp >> 28) & 0xF; 122470aeb807SYazen Ghannam socket_id_mask = (ctx.tmp >> 16) & 0xFF; 12250b746e8cSYazen Ghannam 12260b746e8cSYazen Ghannam cs_id |= ((cs_fabric_id & socket_id_mask) >> socket_id_shift) << sock_id_bit; 12270b746e8cSYazen Ghannam } 12280b746e8cSYazen Ghannam 12290b746e8cSYazen Ghannam /* 12300b746e8cSYazen Ghannam * The pre-interleaved address consists of XXXXXXIIIYYYYY 12310b746e8cSYazen Ghannam * where III is the ID for this CS, and XXXXXXYYYYY are the 12320b746e8cSYazen Ghannam * address bits from the post-interleaved address. 12330b746e8cSYazen Ghannam * "num_intlv_bits" has been calculated to tell us how many "I" 12340b746e8cSYazen Ghannam * bits there are. "intlv_addr_bit" tells us how many "Y" bits 12350b746e8cSYazen Ghannam * there are (where "I" starts). 12360b746e8cSYazen Ghannam */ 123770aeb807SYazen Ghannam temp_addr_y = ctx.ret_addr & GENMASK_ULL(intlv_addr_bit - 1, 0); 12380b746e8cSYazen Ghannam temp_addr_i = (cs_id << intlv_addr_bit); 123970aeb807SYazen Ghannam temp_addr_x = (ctx.ret_addr & GENMASK_ULL(63, intlv_addr_bit)) << num_intlv_bits; 124070aeb807SYazen Ghannam ctx.ret_addr = temp_addr_x | temp_addr_i | temp_addr_y; 12410b746e8cSYazen Ghannam } 12420b746e8cSYazen Ghannam 12430b746e8cSYazen Ghannam /* Add dram base address */ 124470aeb807SYazen Ghannam ctx.ret_addr += dram_base_addr; 12450b746e8cSYazen Ghannam 12460b746e8cSYazen Ghannam /* If legacy MMIO hole enabled */ 12470b746e8cSYazen Ghannam if (lgcy_mmio_hole_en) { 124870aeb807SYazen Ghannam if (df_indirect_read_broadcast(nid, 0, 0x104, &ctx.tmp)) 12490b746e8cSYazen Ghannam goto out_err; 12500b746e8cSYazen Ghannam 125170aeb807SYazen Ghannam dram_hole_base = ctx.tmp & GENMASK(31, 24); 125270aeb807SYazen Ghannam if (ctx.ret_addr >= dram_hole_base) 125370aeb807SYazen Ghannam ctx.ret_addr += (BIT_ULL(32) - dram_hole_base); 12540b746e8cSYazen Ghannam } 12550b746e8cSYazen Ghannam 12560b746e8cSYazen Ghannam if (hash_enabled) { 12570b746e8cSYazen Ghannam /* Save some parentheses and grab ls-bit at the end. */ 125870aeb807SYazen Ghannam hashed_bit = (ctx.ret_addr >> 12) ^ 125970aeb807SYazen Ghannam (ctx.ret_addr >> 18) ^ 126070aeb807SYazen Ghannam (ctx.ret_addr >> 21) ^ 126170aeb807SYazen Ghannam (ctx.ret_addr >> 30) ^ 12620b746e8cSYazen Ghannam cs_id; 12630b746e8cSYazen Ghannam 12640b746e8cSYazen Ghannam hashed_bit &= BIT(0); 12650b746e8cSYazen Ghannam 126670aeb807SYazen Ghannam if (hashed_bit != ((ctx.ret_addr >> intlv_addr_bit) & BIT(0))) 126770aeb807SYazen Ghannam ctx.ret_addr ^= BIT(intlv_addr_bit); 12680b746e8cSYazen Ghannam } 12690b746e8cSYazen Ghannam 12700b746e8cSYazen Ghannam /* Is calculated system address is above DRAM limit address? */ 127170aeb807SYazen Ghannam if (ctx.ret_addr > dram_limit_addr) 12720b746e8cSYazen Ghannam goto out_err; 12730b746e8cSYazen Ghannam 127470aeb807SYazen Ghannam *sys_addr = ctx.ret_addr; 12750b746e8cSYazen Ghannam return 0; 12760b746e8cSYazen Ghannam 12770b746e8cSYazen Ghannam out_err: 12780b746e8cSYazen Ghannam return -EINVAL; 12790b746e8cSYazen Ghannam } 12800b746e8cSYazen Ghannam 1281bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16); 12822da11654SDoug Thompson 12832da11654SDoug Thompson /* 12842da11654SDoug Thompson * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs 12852da11654SDoug Thompson * are ECC capable. 12862da11654SDoug Thompson */ 1287d1ea71cdSBorislav Petkov static unsigned long determine_edac_cap(struct amd64_pvt *pvt) 12882da11654SDoug Thompson { 12891f6189edSDan Carpenter unsigned long edac_cap = EDAC_FLAG_NONE; 1290d27f3a34SYazen Ghannam u8 bit; 12912da11654SDoug Thompson 1292d27f3a34SYazen Ghannam if (pvt->umc) { 1293d27f3a34SYazen Ghannam u8 i, umc_en_mask = 0, dimm_ecc_en_mask = 0; 1294d27f3a34SYazen Ghannam 12954d30d2bcSYazen Ghannam for_each_umc(i) { 1296d27f3a34SYazen Ghannam if (!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT)) 1297d27f3a34SYazen Ghannam continue; 1298d27f3a34SYazen Ghannam 1299d27f3a34SYazen Ghannam umc_en_mask |= BIT(i); 1300d27f3a34SYazen Ghannam 1301d27f3a34SYazen Ghannam /* UMC Configuration bit 12 (DimmEccEn) */ 1302d27f3a34SYazen Ghannam if (pvt->umc[i].umc_cfg & BIT(12)) 1303d27f3a34SYazen Ghannam dimm_ecc_en_mask |= BIT(i); 1304d27f3a34SYazen Ghannam } 1305d27f3a34SYazen Ghannam 1306d27f3a34SYazen Ghannam if (umc_en_mask == dimm_ecc_en_mask) 1307d27f3a34SYazen Ghannam edac_cap = EDAC_FLAG_SECDED; 1308d27f3a34SYazen Ghannam } else { 1309a4b4bedcSBorislav Petkov bit = (pvt->fam > 0xf || pvt->ext_model >= K8_REV_F) 13102da11654SDoug Thompson ? 19 13112da11654SDoug Thompson : 17; 13122da11654SDoug Thompson 1313584fcff4SBorislav Petkov if (pvt->dclr0 & BIT(bit)) 13142da11654SDoug Thompson edac_cap = EDAC_FLAG_SECDED; 1315d27f3a34SYazen Ghannam } 13162da11654SDoug Thompson 13172da11654SDoug Thompson return edac_cap; 13182da11654SDoug Thompson } 13192da11654SDoug Thompson 1320d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *, u8); 13212da11654SDoug Thompson 1322d1ea71cdSBorislav Petkov static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan) 132368798e17SBorislav Petkov { 1324956b9ba1SJoe Perches edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr); 132568798e17SBorislav Petkov 1326a597d2a5SAravind Gopalakrishnan if (pvt->dram_type == MEM_LRDDR3) { 1327a597d2a5SAravind Gopalakrishnan u32 dcsm = pvt->csels[chan].csmasks[0]; 1328a597d2a5SAravind Gopalakrishnan /* 1329a597d2a5SAravind Gopalakrishnan * It's assumed all LRDIMMs in a DCT are going to be of 1330a597d2a5SAravind Gopalakrishnan * same 'type' until proven otherwise. So, use a cs 1331a597d2a5SAravind Gopalakrishnan * value of '0' here to get dcsm value. 1332a597d2a5SAravind Gopalakrishnan */ 1333a597d2a5SAravind Gopalakrishnan edac_dbg(1, " LRDIMM %dx rank multiply\n", (dcsm & 0x3)); 1334a597d2a5SAravind Gopalakrishnan } 1335a597d2a5SAravind Gopalakrishnan 1336a597d2a5SAravind Gopalakrishnan edac_dbg(1, "All DIMMs support ECC:%s\n", 133768798e17SBorislav Petkov (dclr & BIT(19)) ? "yes" : "no"); 133868798e17SBorislav Petkov 1339a597d2a5SAravind Gopalakrishnan 1340956b9ba1SJoe Perches edac_dbg(1, " PAR/ERR parity: %s\n", 134168798e17SBorislav Petkov (dclr & BIT(8)) ? "enabled" : "disabled"); 134268798e17SBorislav Petkov 1343a4b4bedcSBorislav Petkov if (pvt->fam == 0x10) 1344956b9ba1SJoe Perches edac_dbg(1, " DCT 128bit mode width: %s\n", 134568798e17SBorislav Petkov (dclr & BIT(11)) ? "128b" : "64b"); 134668798e17SBorislav Petkov 1347956b9ba1SJoe Perches edac_dbg(1, " x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n", 134868798e17SBorislav Petkov (dclr & BIT(12)) ? "yes" : "no", 134968798e17SBorislav Petkov (dclr & BIT(13)) ? "yes" : "no", 135068798e17SBorislav Petkov (dclr & BIT(14)) ? "yes" : "no", 135168798e17SBorislav Petkov (dclr & BIT(15)) ? "yes" : "no"); 135268798e17SBorislav Petkov } 135368798e17SBorislav Petkov 1354e53a3b26SYazen Ghannam #define CS_EVEN_PRIMARY BIT(0) 1355e53a3b26SYazen Ghannam #define CS_ODD_PRIMARY BIT(1) 135681f5090dSYazen Ghannam #define CS_EVEN_SECONDARY BIT(2) 135781f5090dSYazen Ghannam #define CS_ODD_SECONDARY BIT(3) 13589f4873fbSYazen Ghannam #define CS_3R_INTERLEAVE BIT(4) 1359e53a3b26SYazen Ghannam 136081f5090dSYazen Ghannam #define CS_EVEN (CS_EVEN_PRIMARY | CS_EVEN_SECONDARY) 136181f5090dSYazen Ghannam #define CS_ODD (CS_ODD_PRIMARY | CS_ODD_SECONDARY) 1362e53a3b26SYazen Ghannam 1363e53a3b26SYazen Ghannam static int f17_get_cs_mode(int dimm, u8 ctrl, struct amd64_pvt *pvt) 1364fc00c6a4SYazen Ghannam { 13659f4873fbSYazen Ghannam u8 base, count = 0; 1366e53a3b26SYazen Ghannam int cs_mode = 0; 1367fc00c6a4SYazen Ghannam 1368e53a3b26SYazen Ghannam if (csrow_enabled(2 * dimm, ctrl, pvt)) 1369e53a3b26SYazen Ghannam cs_mode |= CS_EVEN_PRIMARY; 1370fc00c6a4SYazen Ghannam 1371e53a3b26SYazen Ghannam if (csrow_enabled(2 * dimm + 1, ctrl, pvt)) 1372e53a3b26SYazen Ghannam cs_mode |= CS_ODD_PRIMARY; 1373e53a3b26SYazen Ghannam 137481f5090dSYazen Ghannam /* Asymmetric dual-rank DIMM support. */ 137581f5090dSYazen Ghannam if (csrow_sec_enabled(2 * dimm + 1, ctrl, pvt)) 137681f5090dSYazen Ghannam cs_mode |= CS_ODD_SECONDARY; 137781f5090dSYazen Ghannam 13789f4873fbSYazen Ghannam /* 13799f4873fbSYazen Ghannam * 3 Rank inteleaving support. 13809f4873fbSYazen Ghannam * There should be only three bases enabled and their two masks should 13819f4873fbSYazen Ghannam * be equal. 13829f4873fbSYazen Ghannam */ 13839f4873fbSYazen Ghannam for_each_chip_select(base, ctrl, pvt) 13849f4873fbSYazen Ghannam count += csrow_enabled(base, ctrl, pvt); 13859f4873fbSYazen Ghannam 13869f4873fbSYazen Ghannam if (count == 3 && 13879f4873fbSYazen Ghannam pvt->csels[ctrl].csmasks[0] == pvt->csels[ctrl].csmasks[1]) { 13889f4873fbSYazen Ghannam edac_dbg(1, "3R interleaving in use.\n"); 13899f4873fbSYazen Ghannam cs_mode |= CS_3R_INTERLEAVE; 13909f4873fbSYazen Ghannam } 13919f4873fbSYazen Ghannam 1392e53a3b26SYazen Ghannam return cs_mode; 1393fc00c6a4SYazen Ghannam } 1394fc00c6a4SYazen Ghannam 139507ed82efSYazen Ghannam static void debug_display_dimm_sizes_df(struct amd64_pvt *pvt, u8 ctrl) 139607ed82efSYazen Ghannam { 1397e53a3b26SYazen Ghannam int dimm, size0, size1, cs0, cs1, cs_mode; 139807ed82efSYazen Ghannam 139907ed82efSYazen Ghannam edac_printk(KERN_DEBUG, EDAC_MC, "UMC%d chip selects:\n", ctrl); 140007ed82efSYazen Ghannam 1401d971e28eSYazen Ghannam for (dimm = 0; dimm < 2; dimm++) { 1402eb77e6b8SYazen Ghannam cs0 = dimm * 2; 1403eb77e6b8SYazen Ghannam cs1 = dimm * 2 + 1; 1404eb77e6b8SYazen Ghannam 1405e53a3b26SYazen Ghannam cs_mode = f17_get_cs_mode(dimm, ctrl, pvt); 1406e53a3b26SYazen Ghannam 1407e53a3b26SYazen Ghannam size0 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs0); 1408e53a3b26SYazen Ghannam size1 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs1); 140907ed82efSYazen Ghannam 141007ed82efSYazen Ghannam amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n", 1411eb77e6b8SYazen Ghannam cs0, size0, 1412eb77e6b8SYazen Ghannam cs1, size1); 141307ed82efSYazen Ghannam } 141407ed82efSYazen Ghannam } 141507ed82efSYazen Ghannam 141607ed82efSYazen Ghannam static void __dump_misc_regs_df(struct amd64_pvt *pvt) 141707ed82efSYazen Ghannam { 141807ed82efSYazen Ghannam struct amd64_umc *umc; 141907ed82efSYazen Ghannam u32 i, tmp, umc_base; 142007ed82efSYazen Ghannam 14214d30d2bcSYazen Ghannam for_each_umc(i) { 142207ed82efSYazen Ghannam umc_base = get_umc_base(i); 142307ed82efSYazen Ghannam umc = &pvt->umc[i]; 142407ed82efSYazen Ghannam 142507ed82efSYazen Ghannam edac_dbg(1, "UMC%d DIMM cfg: 0x%x\n", i, umc->dimm_cfg); 142607ed82efSYazen Ghannam edac_dbg(1, "UMC%d UMC cfg: 0x%x\n", i, umc->umc_cfg); 142707ed82efSYazen Ghannam edac_dbg(1, "UMC%d SDP ctrl: 0x%x\n", i, umc->sdp_ctrl); 142807ed82efSYazen Ghannam edac_dbg(1, "UMC%d ECC ctrl: 0x%x\n", i, umc->ecc_ctrl); 142907ed82efSYazen Ghannam 143007ed82efSYazen Ghannam amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ECC_BAD_SYMBOL, &tmp); 143107ed82efSYazen Ghannam edac_dbg(1, "UMC%d ECC bad symbol: 0x%x\n", i, tmp); 143207ed82efSYazen Ghannam 143307ed82efSYazen Ghannam amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_UMC_CAP, &tmp); 143407ed82efSYazen Ghannam edac_dbg(1, "UMC%d UMC cap: 0x%x\n", i, tmp); 143507ed82efSYazen Ghannam edac_dbg(1, "UMC%d UMC cap high: 0x%x\n", i, umc->umc_cap_hi); 143607ed82efSYazen Ghannam 143707ed82efSYazen Ghannam edac_dbg(1, "UMC%d ECC capable: %s, ChipKill ECC capable: %s\n", 143807ed82efSYazen Ghannam i, (umc->umc_cap_hi & BIT(30)) ? "yes" : "no", 143907ed82efSYazen Ghannam (umc->umc_cap_hi & BIT(31)) ? "yes" : "no"); 144007ed82efSYazen Ghannam edac_dbg(1, "UMC%d All DIMMs support ECC: %s\n", 144107ed82efSYazen Ghannam i, (umc->umc_cfg & BIT(12)) ? "yes" : "no"); 144207ed82efSYazen Ghannam edac_dbg(1, "UMC%d x4 DIMMs present: %s\n", 144307ed82efSYazen Ghannam i, (umc->dimm_cfg & BIT(6)) ? "yes" : "no"); 144407ed82efSYazen Ghannam edac_dbg(1, "UMC%d x16 DIMMs present: %s\n", 144507ed82efSYazen Ghannam i, (umc->dimm_cfg & BIT(7)) ? "yes" : "no"); 144607ed82efSYazen Ghannam 1447*2151c84eSYazen Ghannam if (umc->dram_type == MEM_LRDDR4 || umc->dram_type == MEM_LRDDR5) { 1448*2151c84eSYazen Ghannam amd_smn_read(pvt->mc_node_id, 1449*2151c84eSYazen Ghannam umc_base + get_umc_reg(UMCCH_ADDR_CFG), 1450*2151c84eSYazen Ghannam &tmp); 145107ed82efSYazen Ghannam edac_dbg(1, "UMC%d LRDIMM %dx rank multiply\n", 145207ed82efSYazen Ghannam i, 1 << ((tmp >> 4) & 0x3)); 145307ed82efSYazen Ghannam } 145407ed82efSYazen Ghannam 145507ed82efSYazen Ghannam debug_display_dimm_sizes_df(pvt, i); 145607ed82efSYazen Ghannam } 145707ed82efSYazen Ghannam 145807ed82efSYazen Ghannam edac_dbg(1, "F0x104 (DRAM Hole Address): 0x%08x, base: 0x%08x\n", 145907ed82efSYazen Ghannam pvt->dhar, dhar_base(pvt)); 146007ed82efSYazen Ghannam } 146107ed82efSYazen Ghannam 14622da11654SDoug Thompson /* Display and decode various NB registers for debug purposes. */ 146307ed82efSYazen Ghannam static void __dump_misc_regs(struct amd64_pvt *pvt) 14642da11654SDoug Thompson { 1465956b9ba1SJoe Perches edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap); 14662da11654SDoug Thompson 1467956b9ba1SJoe Perches edac_dbg(1, " NB two channel DRAM capable: %s\n", 14685980bb9cSBorislav Petkov (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no"); 146968798e17SBorislav Petkov 1470956b9ba1SJoe Perches edac_dbg(1, " ECC capable: %s, ChipKill ECC capable: %s\n", 14715980bb9cSBorislav Petkov (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no", 14725980bb9cSBorislav Petkov (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no"); 147368798e17SBorislav Petkov 1474d1ea71cdSBorislav Petkov debug_dump_dramcfg_low(pvt, pvt->dclr0, 0); 14752da11654SDoug Thompson 1476956b9ba1SJoe Perches edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare); 14772da11654SDoug Thompson 1478956b9ba1SJoe Perches edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n", 1479bc21fa57SBorislav Petkov pvt->dhar, dhar_base(pvt), 1480a4b4bedcSBorislav Petkov (pvt->fam == 0xf) ? k8_dhar_offset(pvt) 1481bc21fa57SBorislav Petkov : f10_dhar_offset(pvt)); 14822da11654SDoug Thompson 1483d1ea71cdSBorislav Petkov debug_display_dimm_sizes(pvt, 0); 14844d796364SBorislav Petkov 14854d796364SBorislav Petkov /* everything below this point is Fam10h and above */ 1486a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) 14872da11654SDoug Thompson return; 14884d796364SBorislav Petkov 1489d1ea71cdSBorislav Petkov debug_display_dimm_sizes(pvt, 1); 14902da11654SDoug Thompson 14918de1d91eSBorislav Petkov /* Only if NOT ganged does dclr1 have valid info */ 149268798e17SBorislav Petkov if (!dct_ganging_enabled(pvt)) 1493d1ea71cdSBorislav Petkov debug_dump_dramcfg_low(pvt, pvt->dclr1, 1); 14942da11654SDoug Thompson } 14952da11654SDoug Thompson 149607ed82efSYazen Ghannam /* Display and decode various NB registers for debug purposes. */ 149707ed82efSYazen Ghannam static void dump_misc_regs(struct amd64_pvt *pvt) 149807ed82efSYazen Ghannam { 149907ed82efSYazen Ghannam if (pvt->umc) 150007ed82efSYazen Ghannam __dump_misc_regs_df(pvt); 150107ed82efSYazen Ghannam else 150207ed82efSYazen Ghannam __dump_misc_regs(pvt); 150307ed82efSYazen Ghannam 150407ed82efSYazen Ghannam edac_dbg(1, " DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no"); 150507ed82efSYazen Ghannam 15067835961dSYazen Ghannam amd64_info("using x%u syndromes.\n", pvt->ecc_sym_sz); 150707ed82efSYazen Ghannam } 150807ed82efSYazen Ghannam 150994be4bffSDoug Thompson /* 151018b94f66SAravind Gopalakrishnan * See BKDG, F2x[1,0][5C:40], F2[1,0][6C:60] 151194be4bffSDoug Thompson */ 151211c75eadSBorislav Petkov static void prep_chip_selects(struct amd64_pvt *pvt) 151394be4bffSDoug Thompson { 151418b94f66SAravind Gopalakrishnan if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) { 151511c75eadSBorislav Petkov pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8; 151611c75eadSBorislav Petkov pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8; 1517a597d2a5SAravind Gopalakrishnan } else if (pvt->fam == 0x15 && pvt->model == 0x30) { 151818b94f66SAravind Gopalakrishnan pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4; 151918b94f66SAravind Gopalakrishnan pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2; 1520d971e28eSYazen Ghannam } else if (pvt->fam >= 0x17) { 1521d971e28eSYazen Ghannam int umc; 1522d971e28eSYazen Ghannam 1523d971e28eSYazen Ghannam for_each_umc(umc) { 1524d971e28eSYazen Ghannam pvt->csels[umc].b_cnt = 4; 1525*2151c84eSYazen Ghannam pvt->csels[umc].m_cnt = fam_type->flags.zn_regs_v2 ? 4 : 2; 1526d971e28eSYazen Ghannam } 1527d971e28eSYazen Ghannam 15289d858bb1SBorislav Petkov } else { 152911c75eadSBorislav Petkov pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8; 153011c75eadSBorislav Petkov pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4; 15319d858bb1SBorislav Petkov } 153294be4bffSDoug Thompson } 153394be4bffSDoug Thompson 1534d971e28eSYazen Ghannam static void read_umc_base_mask(struct amd64_pvt *pvt) 1535d971e28eSYazen Ghannam { 15367574729eSYazen Ghannam u32 umc_base_reg, umc_base_reg_sec; 15377574729eSYazen Ghannam u32 umc_mask_reg, umc_mask_reg_sec; 15387574729eSYazen Ghannam u32 base_reg, base_reg_sec; 15397574729eSYazen Ghannam u32 mask_reg, mask_reg_sec; 15407574729eSYazen Ghannam u32 *base, *base_sec; 15417574729eSYazen Ghannam u32 *mask, *mask_sec; 1542d971e28eSYazen Ghannam int cs, umc; 1543d971e28eSYazen Ghannam 1544d971e28eSYazen Ghannam for_each_umc(umc) { 1545d971e28eSYazen Ghannam umc_base_reg = get_umc_base(umc) + UMCCH_BASE_ADDR; 15467574729eSYazen Ghannam umc_base_reg_sec = get_umc_base(umc) + UMCCH_BASE_ADDR_SEC; 1547d971e28eSYazen Ghannam 1548d971e28eSYazen Ghannam for_each_chip_select(cs, umc, pvt) { 1549d971e28eSYazen Ghannam base = &pvt->csels[umc].csbases[cs]; 15507574729eSYazen Ghannam base_sec = &pvt->csels[umc].csbases_sec[cs]; 1551d971e28eSYazen Ghannam 1552d971e28eSYazen Ghannam base_reg = umc_base_reg + (cs * 4); 15537574729eSYazen Ghannam base_reg_sec = umc_base_reg_sec + (cs * 4); 1554d971e28eSYazen Ghannam 1555d971e28eSYazen Ghannam if (!amd_smn_read(pvt->mc_node_id, base_reg, base)) 1556d971e28eSYazen Ghannam edac_dbg(0, " DCSB%d[%d]=0x%08x reg: 0x%x\n", 1557d971e28eSYazen Ghannam umc, cs, *base, base_reg); 15587574729eSYazen Ghannam 15597574729eSYazen Ghannam if (!amd_smn_read(pvt->mc_node_id, base_reg_sec, base_sec)) 15607574729eSYazen Ghannam edac_dbg(0, " DCSB_SEC%d[%d]=0x%08x reg: 0x%x\n", 15617574729eSYazen Ghannam umc, cs, *base_sec, base_reg_sec); 1562d971e28eSYazen Ghannam } 1563d971e28eSYazen Ghannam 1564d971e28eSYazen Ghannam umc_mask_reg = get_umc_base(umc) + UMCCH_ADDR_MASK; 1565*2151c84eSYazen Ghannam umc_mask_reg_sec = get_umc_base(umc) + get_umc_reg(UMCCH_ADDR_MASK_SEC); 1566d971e28eSYazen Ghannam 1567d971e28eSYazen Ghannam for_each_chip_select_mask(cs, umc, pvt) { 1568d971e28eSYazen Ghannam mask = &pvt->csels[umc].csmasks[cs]; 15697574729eSYazen Ghannam mask_sec = &pvt->csels[umc].csmasks_sec[cs]; 1570d971e28eSYazen Ghannam 1571d971e28eSYazen Ghannam mask_reg = umc_mask_reg + (cs * 4); 15727574729eSYazen Ghannam mask_reg_sec = umc_mask_reg_sec + (cs * 4); 1573d971e28eSYazen Ghannam 1574d971e28eSYazen Ghannam if (!amd_smn_read(pvt->mc_node_id, mask_reg, mask)) 1575d971e28eSYazen Ghannam edac_dbg(0, " DCSM%d[%d]=0x%08x reg: 0x%x\n", 1576d971e28eSYazen Ghannam umc, cs, *mask, mask_reg); 15777574729eSYazen Ghannam 15787574729eSYazen Ghannam if (!amd_smn_read(pvt->mc_node_id, mask_reg_sec, mask_sec)) 15797574729eSYazen Ghannam edac_dbg(0, " DCSM_SEC%d[%d]=0x%08x reg: 0x%x\n", 15807574729eSYazen Ghannam umc, cs, *mask_sec, mask_reg_sec); 1581d971e28eSYazen Ghannam } 1582d971e28eSYazen Ghannam } 1583d971e28eSYazen Ghannam } 1584d971e28eSYazen Ghannam 158594be4bffSDoug Thompson /* 158611c75eadSBorislav Petkov * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers 158794be4bffSDoug Thompson */ 1588b2b0c605SBorislav Petkov static void read_dct_base_mask(struct amd64_pvt *pvt) 158994be4bffSDoug Thompson { 1590d971e28eSYazen Ghannam int cs; 159194be4bffSDoug Thompson 159211c75eadSBorislav Petkov prep_chip_selects(pvt); 159394be4bffSDoug Thompson 1594d971e28eSYazen Ghannam if (pvt->umc) 1595d971e28eSYazen Ghannam return read_umc_base_mask(pvt); 1596b64ce7cdSYazen Ghannam 159711c75eadSBorislav Petkov for_each_chip_select(cs, 0, pvt) { 1598d971e28eSYazen Ghannam int reg0 = DCSB0 + (cs * 4); 1599d971e28eSYazen Ghannam int reg1 = DCSB1 + (cs * 4); 160011c75eadSBorislav Petkov u32 *base0 = &pvt->csels[0].csbases[cs]; 160111c75eadSBorislav Petkov u32 *base1 = &pvt->csels[1].csbases[cs]; 1602b2b0c605SBorislav Petkov 16037981a28fSAravind Gopalakrishnan if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0)) 1604956b9ba1SJoe Perches edac_dbg(0, " DCSB0[%d]=0x%08x reg: F2x%x\n", 160511c75eadSBorislav Petkov cs, *base0, reg0); 160694be4bffSDoug Thompson 16077981a28fSAravind Gopalakrishnan if (pvt->fam == 0xf) 160811c75eadSBorislav Petkov continue; 1609b2b0c605SBorislav Petkov 16107981a28fSAravind Gopalakrishnan if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1)) 1611956b9ba1SJoe Perches edac_dbg(0, " DCSB1[%d]=0x%08x reg: F2x%x\n", 16127981a28fSAravind Gopalakrishnan cs, *base1, (pvt->fam == 0x10) ? reg1 16137981a28fSAravind Gopalakrishnan : reg0); 161494be4bffSDoug Thompson } 161594be4bffSDoug Thompson 161611c75eadSBorislav Petkov for_each_chip_select_mask(cs, 0, pvt) { 1617d971e28eSYazen Ghannam int reg0 = DCSM0 + (cs * 4); 1618d971e28eSYazen Ghannam int reg1 = DCSM1 + (cs * 4); 161911c75eadSBorislav Petkov u32 *mask0 = &pvt->csels[0].csmasks[cs]; 162011c75eadSBorislav Petkov u32 *mask1 = &pvt->csels[1].csmasks[cs]; 1621b2b0c605SBorislav Petkov 16227981a28fSAravind Gopalakrishnan if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0)) 1623956b9ba1SJoe Perches edac_dbg(0, " DCSM0[%d]=0x%08x reg: F2x%x\n", 162411c75eadSBorislav Petkov cs, *mask0, reg0); 162594be4bffSDoug Thompson 16267981a28fSAravind Gopalakrishnan if (pvt->fam == 0xf) 162711c75eadSBorislav Petkov continue; 1628b2b0c605SBorislav Petkov 16297981a28fSAravind Gopalakrishnan if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1)) 1630956b9ba1SJoe Perches edac_dbg(0, " DCSM1[%d]=0x%08x reg: F2x%x\n", 16317981a28fSAravind Gopalakrishnan cs, *mask1, (pvt->fam == 0x10) ? reg1 16327981a28fSAravind Gopalakrishnan : reg0); 163394be4bffSDoug Thompson } 16346ba5dcdcSBorislav Petkov } 163594be4bffSDoug Thompson 163675aeaaf2SYazen Ghannam static void determine_memory_type_df(struct amd64_pvt *pvt) 163775aeaaf2SYazen Ghannam { 163875aeaaf2SYazen Ghannam struct amd64_umc *umc; 163975aeaaf2SYazen Ghannam u32 i; 164075aeaaf2SYazen Ghannam 164175aeaaf2SYazen Ghannam for_each_umc(i) { 164275aeaaf2SYazen Ghannam umc = &pvt->umc[i]; 164375aeaaf2SYazen Ghannam 164475aeaaf2SYazen Ghannam if (!(umc->sdp_ctrl & UMC_SDP_INIT)) { 164575aeaaf2SYazen Ghannam umc->dram_type = MEM_EMPTY; 164675aeaaf2SYazen Ghannam continue; 164775aeaaf2SYazen Ghannam } 164875aeaaf2SYazen Ghannam 1649*2151c84eSYazen Ghannam /* 1650*2151c84eSYazen Ghannam * Check if the system supports the "DDR Type" field in UMC Config 1651*2151c84eSYazen Ghannam * and has DDR5 DIMMs in use. 1652*2151c84eSYazen Ghannam */ 1653*2151c84eSYazen Ghannam if (fam_type->flags.zn_regs_v2 && ((umc->umc_cfg & GENMASK(2, 0)) == 0x1)) { 1654*2151c84eSYazen Ghannam if (umc->dimm_cfg & BIT(5)) 1655*2151c84eSYazen Ghannam umc->dram_type = MEM_LRDDR5; 1656*2151c84eSYazen Ghannam else if (umc->dimm_cfg & BIT(4)) 1657*2151c84eSYazen Ghannam umc->dram_type = MEM_RDDR5; 1658*2151c84eSYazen Ghannam else 1659*2151c84eSYazen Ghannam umc->dram_type = MEM_DDR5; 1660*2151c84eSYazen Ghannam } else { 166175aeaaf2SYazen Ghannam if (umc->dimm_cfg & BIT(5)) 166275aeaaf2SYazen Ghannam umc->dram_type = MEM_LRDDR4; 166375aeaaf2SYazen Ghannam else if (umc->dimm_cfg & BIT(4)) 166475aeaaf2SYazen Ghannam umc->dram_type = MEM_RDDR4; 166575aeaaf2SYazen Ghannam else 166675aeaaf2SYazen Ghannam umc->dram_type = MEM_DDR4; 1667*2151c84eSYazen Ghannam } 166875aeaaf2SYazen Ghannam 166975aeaaf2SYazen Ghannam edac_dbg(1, " UMC%d DIMM type: %s\n", i, edac_mem_types[umc->dram_type]); 167075aeaaf2SYazen Ghannam } 167175aeaaf2SYazen Ghannam } 167275aeaaf2SYazen Ghannam 1673a597d2a5SAravind Gopalakrishnan static void determine_memory_type(struct amd64_pvt *pvt) 167494be4bffSDoug Thompson { 1675a597d2a5SAravind Gopalakrishnan u32 dram_ctrl, dcsm; 167694be4bffSDoug Thompson 167775aeaaf2SYazen Ghannam if (pvt->umc) 167875aeaaf2SYazen Ghannam return determine_memory_type_df(pvt); 1679dcd01394SYazen Ghannam 1680a597d2a5SAravind Gopalakrishnan switch (pvt->fam) { 1681a597d2a5SAravind Gopalakrishnan case 0xf: 1682a597d2a5SAravind Gopalakrishnan if (pvt->ext_model >= K8_REV_F) 1683a597d2a5SAravind Gopalakrishnan goto ddr3; 1684a597d2a5SAravind Gopalakrishnan 1685a597d2a5SAravind Gopalakrishnan pvt->dram_type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR; 1686a597d2a5SAravind Gopalakrishnan return; 1687a597d2a5SAravind Gopalakrishnan 1688a597d2a5SAravind Gopalakrishnan case 0x10: 16896b4c0bdeSBorislav Petkov if (pvt->dchr0 & DDR3_MODE) 1690a597d2a5SAravind Gopalakrishnan goto ddr3; 1691a597d2a5SAravind Gopalakrishnan 1692a597d2a5SAravind Gopalakrishnan pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2; 1693a597d2a5SAravind Gopalakrishnan return; 1694a597d2a5SAravind Gopalakrishnan 1695a597d2a5SAravind Gopalakrishnan case 0x15: 1696a597d2a5SAravind Gopalakrishnan if (pvt->model < 0x60) 1697a597d2a5SAravind Gopalakrishnan goto ddr3; 1698a597d2a5SAravind Gopalakrishnan 1699a597d2a5SAravind Gopalakrishnan /* 1700a597d2a5SAravind Gopalakrishnan * Model 0x60h needs special handling: 1701a597d2a5SAravind Gopalakrishnan * 1702a597d2a5SAravind Gopalakrishnan * We use a Chip Select value of '0' to obtain dcsm. 1703a597d2a5SAravind Gopalakrishnan * Theoretically, it is possible to populate LRDIMMs of different 1704a597d2a5SAravind Gopalakrishnan * 'Rank' value on a DCT. But this is not the common case. So, 1705a597d2a5SAravind Gopalakrishnan * it's reasonable to assume all DIMMs are going to be of same 1706a597d2a5SAravind Gopalakrishnan * 'type' until proven otherwise. 1707a597d2a5SAravind Gopalakrishnan */ 1708a597d2a5SAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DRAM_CONTROL, &dram_ctrl); 1709a597d2a5SAravind Gopalakrishnan dcsm = pvt->csels[0].csmasks[0]; 1710a597d2a5SAravind Gopalakrishnan 1711a597d2a5SAravind Gopalakrishnan if (((dram_ctrl >> 8) & 0x7) == 0x2) 1712a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_DDR4; 1713a597d2a5SAravind Gopalakrishnan else if (pvt->dclr0 & BIT(16)) 1714a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_DDR3; 1715a597d2a5SAravind Gopalakrishnan else if (dcsm & 0x3) 1716a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_LRDDR3; 17176b4c0bdeSBorislav Petkov else 1718a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_RDDR3; 1719a597d2a5SAravind Gopalakrishnan 1720a597d2a5SAravind Gopalakrishnan return; 1721a597d2a5SAravind Gopalakrishnan 1722a597d2a5SAravind Gopalakrishnan case 0x16: 1723a597d2a5SAravind Gopalakrishnan goto ddr3; 1724a597d2a5SAravind Gopalakrishnan 1725a597d2a5SAravind Gopalakrishnan default: 1726a597d2a5SAravind Gopalakrishnan WARN(1, KERN_ERR "%s: Family??? 0x%x\n", __func__, pvt->fam); 1727a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_EMPTY; 172894be4bffSDoug Thompson } 1729a597d2a5SAravind Gopalakrishnan return; 173094be4bffSDoug Thompson 1731a597d2a5SAravind Gopalakrishnan ddr3: 1732a597d2a5SAravind Gopalakrishnan pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3; 173394be4bffSDoug Thompson } 173494be4bffSDoug Thompson 1735cb328507SBorislav Petkov /* Get the number of DCT channels the memory controller is using. */ 1736ddff876dSDoug Thompson static int k8_early_channel_count(struct amd64_pvt *pvt) 1737ddff876dSDoug Thompson { 1738cb328507SBorislav Petkov int flag; 1739ddff876dSDoug Thompson 17409f56da0eSBorislav Petkov if (pvt->ext_model >= K8_REV_F) 1741ddff876dSDoug Thompson /* RevF (NPT) and later */ 174241d8bfabSBorislav Petkov flag = pvt->dclr0 & WIDTH_128; 17439f56da0eSBorislav Petkov else 1744ddff876dSDoug Thompson /* RevE and earlier */ 1745ddff876dSDoug Thompson flag = pvt->dclr0 & REVE_WIDTH_128; 1746ddff876dSDoug Thompson 1747ddff876dSDoug Thompson /* not used */ 1748ddff876dSDoug Thompson pvt->dclr1 = 0; 1749ddff876dSDoug Thompson 1750ddff876dSDoug Thompson return (flag) ? 2 : 1; 1751ddff876dSDoug Thompson } 1752ddff876dSDoug Thompson 175370046624SBorislav Petkov /* On F10h and later ErrAddr is MC4_ADDR[47:1] */ 1754a4b4bedcSBorislav Petkov static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m) 1755ddff876dSDoug Thompson { 1756db970bd2SYazen Ghannam u16 mce_nid = topology_die_id(m->extcpu); 17572ec591acSBorislav Petkov struct mem_ctl_info *mci; 175870046624SBorislav Petkov u8 start_bit = 1; 175970046624SBorislav Petkov u8 end_bit = 47; 17602ec591acSBorislav Petkov u64 addr; 17612ec591acSBorislav Petkov 17622ec591acSBorislav Petkov mci = edac_mc_find(mce_nid); 17632ec591acSBorislav Petkov if (!mci) 17642ec591acSBorislav Petkov return 0; 17652ec591acSBorislav Petkov 17662ec591acSBorislav Petkov pvt = mci->pvt_info; 176770046624SBorislav Petkov 1768a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) { 176970046624SBorislav Petkov start_bit = 3; 177070046624SBorislav Petkov end_bit = 39; 177170046624SBorislav Petkov } 177270046624SBorislav Petkov 177310ef6b0dSChen, Gong addr = m->addr & GENMASK_ULL(end_bit, start_bit); 1774c1ae6830SBorislav Petkov 1775c1ae6830SBorislav Petkov /* 1776c1ae6830SBorislav Petkov * Erratum 637 workaround 1777c1ae6830SBorislav Petkov */ 1778a4b4bedcSBorislav Petkov if (pvt->fam == 0x15) { 1779c1ae6830SBorislav Petkov u64 cc6_base, tmp_addr; 1780c1ae6830SBorislav Petkov u32 tmp; 17818b84c8dfSDaniel J Blueman u8 intlv_en; 1782c1ae6830SBorislav Petkov 178310ef6b0dSChen, Gong if ((addr & GENMASK_ULL(47, 24)) >> 24 != 0x00fdf7) 1784c1ae6830SBorislav Petkov return addr; 1785c1ae6830SBorislav Petkov 1786c1ae6830SBorislav Petkov 1787c1ae6830SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp); 1788c1ae6830SBorislav Petkov intlv_en = tmp >> 21 & 0x7; 1789c1ae6830SBorislav Petkov 1790c1ae6830SBorislav Petkov /* add [47:27] + 3 trailing bits */ 179110ef6b0dSChen, Gong cc6_base = (tmp & GENMASK_ULL(20, 0)) << 3; 1792c1ae6830SBorislav Petkov 1793c1ae6830SBorislav Petkov /* reverse and add DramIntlvEn */ 1794c1ae6830SBorislav Petkov cc6_base |= intlv_en ^ 0x7; 1795c1ae6830SBorislav Petkov 1796c1ae6830SBorislav Petkov /* pin at [47:24] */ 1797c1ae6830SBorislav Petkov cc6_base <<= 24; 1798c1ae6830SBorislav Petkov 1799c1ae6830SBorislav Petkov if (!intlv_en) 180010ef6b0dSChen, Gong return cc6_base | (addr & GENMASK_ULL(23, 0)); 1801c1ae6830SBorislav Petkov 1802c1ae6830SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp); 1803c1ae6830SBorislav Petkov 1804c1ae6830SBorislav Petkov /* faster log2 */ 180510ef6b0dSChen, Gong tmp_addr = (addr & GENMASK_ULL(23, 12)) << __fls(intlv_en + 1); 1806c1ae6830SBorislav Petkov 1807c1ae6830SBorislav Petkov /* OR DramIntlvSel into bits [14:12] */ 180810ef6b0dSChen, Gong tmp_addr |= (tmp & GENMASK_ULL(23, 21)) >> 9; 1809c1ae6830SBorislav Petkov 1810c1ae6830SBorislav Petkov /* add remaining [11:0] bits from original MC4_ADDR */ 181110ef6b0dSChen, Gong tmp_addr |= addr & GENMASK_ULL(11, 0); 1812c1ae6830SBorislav Petkov 1813c1ae6830SBorislav Petkov return cc6_base | tmp_addr; 1814c1ae6830SBorislav Petkov } 1815c1ae6830SBorislav Petkov 1816c1ae6830SBorislav Petkov return addr; 1817ddff876dSDoug Thompson } 1818ddff876dSDoug Thompson 1819e2c0bffeSDaniel J Blueman static struct pci_dev *pci_get_related_function(unsigned int vendor, 1820e2c0bffeSDaniel J Blueman unsigned int device, 1821e2c0bffeSDaniel J Blueman struct pci_dev *related) 1822e2c0bffeSDaniel J Blueman { 1823e2c0bffeSDaniel J Blueman struct pci_dev *dev = NULL; 1824e2c0bffeSDaniel J Blueman 1825e2c0bffeSDaniel J Blueman while ((dev = pci_get_device(vendor, device, dev))) { 1826e2c0bffeSDaniel J Blueman if (pci_domain_nr(dev->bus) == pci_domain_nr(related->bus) && 1827e2c0bffeSDaniel J Blueman (dev->bus->number == related->bus->number) && 1828e2c0bffeSDaniel J Blueman (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn))) 1829e2c0bffeSDaniel J Blueman break; 1830e2c0bffeSDaniel J Blueman } 1831e2c0bffeSDaniel J Blueman 1832e2c0bffeSDaniel J Blueman return dev; 1833e2c0bffeSDaniel J Blueman } 1834e2c0bffeSDaniel J Blueman 18357f19bf75SBorislav Petkov static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range) 1836ddff876dSDoug Thompson { 1837e2c0bffeSDaniel J Blueman struct amd_northbridge *nb; 183818b94f66SAravind Gopalakrishnan struct pci_dev *f1 = NULL; 183918b94f66SAravind Gopalakrishnan unsigned int pci_func; 184071d2a32eSBorislav Petkov int off = range << 3; 1841e2c0bffeSDaniel J Blueman u32 llim; 1842ddff876dSDoug Thompson 18437f19bf75SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off, &pvt->ranges[range].base.lo); 18447f19bf75SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo); 1845ddff876dSDoug Thompson 184618b94f66SAravind Gopalakrishnan if (pvt->fam == 0xf) 18477f19bf75SBorislav Petkov return; 1848ddff876dSDoug Thompson 18497f19bf75SBorislav Petkov if (!dram_rw(pvt, range)) 18507f19bf75SBorislav Petkov return; 1851ddff876dSDoug Thompson 18527f19bf75SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off, &pvt->ranges[range].base.hi); 18537f19bf75SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi); 1854f08e457cSBorislav Petkov 1855e2c0bffeSDaniel J Blueman /* F15h: factor in CC6 save area by reading dst node's limit reg */ 185618b94f66SAravind Gopalakrishnan if (pvt->fam != 0x15) 1857e2c0bffeSDaniel J Blueman return; 1858f08e457cSBorislav Petkov 1859e2c0bffeSDaniel J Blueman nb = node_to_amd_nb(dram_dst_node(pvt, range)); 1860e2c0bffeSDaniel J Blueman if (WARN_ON(!nb)) 1861e2c0bffeSDaniel J Blueman return; 1862e2c0bffeSDaniel J Blueman 1863a597d2a5SAravind Gopalakrishnan if (pvt->model == 0x60) 1864a597d2a5SAravind Gopalakrishnan pci_func = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1; 1865a597d2a5SAravind Gopalakrishnan else if (pvt->model == 0x30) 1866a597d2a5SAravind Gopalakrishnan pci_func = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1; 1867a597d2a5SAravind Gopalakrishnan else 1868a597d2a5SAravind Gopalakrishnan pci_func = PCI_DEVICE_ID_AMD_15H_NB_F1; 186918b94f66SAravind Gopalakrishnan 187018b94f66SAravind Gopalakrishnan f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc); 1871f08e457cSBorislav Petkov if (WARN_ON(!f1)) 1872f08e457cSBorislav Petkov return; 1873f08e457cSBorislav Petkov 1874f08e457cSBorislav Petkov amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim); 1875f08e457cSBorislav Petkov 187610ef6b0dSChen, Gong pvt->ranges[range].lim.lo &= GENMASK_ULL(15, 0); 1877f08e457cSBorislav Petkov 1878f08e457cSBorislav Petkov /* {[39:27],111b} */ 1879f08e457cSBorislav Petkov pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16; 1880f08e457cSBorislav Petkov 188110ef6b0dSChen, Gong pvt->ranges[range].lim.hi &= GENMASK_ULL(7, 0); 1882f08e457cSBorislav Petkov 1883f08e457cSBorislav Petkov /* [47:40] */ 1884f08e457cSBorislav Petkov pvt->ranges[range].lim.hi |= llim >> 13; 1885f08e457cSBorislav Petkov 1886f08e457cSBorislav Petkov pci_dev_put(f1); 1887f08e457cSBorislav Petkov } 1888ddff876dSDoug Thompson 1889f192c7b1SBorislav Petkov static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr, 189033ca0643SBorislav Petkov struct err_info *err) 1891ddff876dSDoug Thompson { 1892f192c7b1SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 1893ddff876dSDoug Thompson 189433ca0643SBorislav Petkov error_address_to_page_and_offset(sys_addr, err); 1895ab5a503cSMauro Carvalho Chehab 1896ab5a503cSMauro Carvalho Chehab /* 1897ab5a503cSMauro Carvalho Chehab * Find out which node the error address belongs to. This may be 1898ab5a503cSMauro Carvalho Chehab * different from the node that detected the error. 1899ab5a503cSMauro Carvalho Chehab */ 190033ca0643SBorislav Petkov err->src_mci = find_mc_by_sys_addr(mci, sys_addr); 190133ca0643SBorislav Petkov if (!err->src_mci) { 1902ab5a503cSMauro Carvalho Chehab amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n", 1903ab5a503cSMauro Carvalho Chehab (unsigned long)sys_addr); 190433ca0643SBorislav Petkov err->err_code = ERR_NODE; 1905ab5a503cSMauro Carvalho Chehab return; 1906ab5a503cSMauro Carvalho Chehab } 1907ab5a503cSMauro Carvalho Chehab 1908ab5a503cSMauro Carvalho Chehab /* Now map the sys_addr to a CSROW */ 190933ca0643SBorislav Petkov err->csrow = sys_addr_to_csrow(err->src_mci, sys_addr); 191033ca0643SBorislav Petkov if (err->csrow < 0) { 191133ca0643SBorislav Petkov err->err_code = ERR_CSROW; 1912ab5a503cSMauro Carvalho Chehab return; 1913ab5a503cSMauro Carvalho Chehab } 1914ab5a503cSMauro Carvalho Chehab 1915ddff876dSDoug Thompson /* CHIPKILL enabled */ 1916f192c7b1SBorislav Petkov if (pvt->nbcfg & NBCFG_CHIPKILL) { 191733ca0643SBorislav Petkov err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome); 191833ca0643SBorislav Petkov if (err->channel < 0) { 1919ddff876dSDoug Thompson /* 1920ddff876dSDoug Thompson * Syndrome didn't map, so we don't know which of the 1921ddff876dSDoug Thompson * 2 DIMMs is in error. So we need to ID 'both' of them 1922ddff876dSDoug Thompson * as suspect. 1923ddff876dSDoug Thompson */ 192433ca0643SBorislav Petkov amd64_mc_warn(err->src_mci, "unknown syndrome 0x%04x - " 1925ab5a503cSMauro Carvalho Chehab "possible error reporting race\n", 192633ca0643SBorislav Petkov err->syndrome); 192733ca0643SBorislav Petkov err->err_code = ERR_CHANNEL; 1928ddff876dSDoug Thompson return; 1929ddff876dSDoug Thompson } 1930ddff876dSDoug Thompson } else { 1931ddff876dSDoug Thompson /* 1932ddff876dSDoug Thompson * non-chipkill ecc mode 1933ddff876dSDoug Thompson * 1934ddff876dSDoug Thompson * The k8 documentation is unclear about how to determine the 1935ddff876dSDoug Thompson * channel number when using non-chipkill memory. This method 1936ddff876dSDoug Thompson * was obtained from email communication with someone at AMD. 1937ddff876dSDoug Thompson * (Wish the email was placed in this comment - norsk) 1938ddff876dSDoug Thompson */ 193933ca0643SBorislav Petkov err->channel = ((sys_addr & BIT(3)) != 0); 1940ddff876dSDoug Thompson } 1941ddff876dSDoug Thompson } 1942ddff876dSDoug Thompson 194341d8bfabSBorislav Petkov static int ddr2_cs_size(unsigned i, bool dct_width) 1944ddff876dSDoug Thompson { 194541d8bfabSBorislav Petkov unsigned shift = 0; 1946ddff876dSDoug Thompson 194741d8bfabSBorislav Petkov if (i <= 2) 194841d8bfabSBorislav Petkov shift = i; 194941d8bfabSBorislav Petkov else if (!(i & 0x1)) 195041d8bfabSBorislav Petkov shift = i >> 1; 19511433eb99SBorislav Petkov else 195241d8bfabSBorislav Petkov shift = (i + 1) >> 1; 1953ddff876dSDoug Thompson 195441d8bfabSBorislav Petkov return 128 << (shift + !!dct_width); 195541d8bfabSBorislav Petkov } 195641d8bfabSBorislav Petkov 195741d8bfabSBorislav Petkov static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 1958a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 195941d8bfabSBorislav Petkov { 196041d8bfabSBorislav Petkov u32 dclr = dct ? pvt->dclr1 : pvt->dclr0; 196141d8bfabSBorislav Petkov 196241d8bfabSBorislav Petkov if (pvt->ext_model >= K8_REV_F) { 196341d8bfabSBorislav Petkov WARN_ON(cs_mode > 11); 196441d8bfabSBorislav Petkov return ddr2_cs_size(cs_mode, dclr & WIDTH_128); 196541d8bfabSBorislav Petkov } 196641d8bfabSBorislav Petkov else if (pvt->ext_model >= K8_REV_D) { 196711b0a314SBorislav Petkov unsigned diff; 196841d8bfabSBorislav Petkov WARN_ON(cs_mode > 10); 196941d8bfabSBorislav Petkov 197011b0a314SBorislav Petkov /* 197111b0a314SBorislav Petkov * the below calculation, besides trying to win an obfuscated C 197211b0a314SBorislav Petkov * contest, maps cs_mode values to DIMM chip select sizes. The 197311b0a314SBorislav Petkov * mappings are: 197411b0a314SBorislav Petkov * 197511b0a314SBorislav Petkov * cs_mode CS size (mb) 197611b0a314SBorislav Petkov * ======= ============ 197711b0a314SBorislav Petkov * 0 32 197811b0a314SBorislav Petkov * 1 64 197911b0a314SBorislav Petkov * 2 128 198011b0a314SBorislav Petkov * 3 128 198111b0a314SBorislav Petkov * 4 256 198211b0a314SBorislav Petkov * 5 512 198311b0a314SBorislav Petkov * 6 256 198411b0a314SBorislav Petkov * 7 512 198511b0a314SBorislav Petkov * 8 1024 198611b0a314SBorislav Petkov * 9 1024 198711b0a314SBorislav Petkov * 10 2048 198811b0a314SBorislav Petkov * 198911b0a314SBorislav Petkov * Basically, it calculates a value with which to shift the 199011b0a314SBorislav Petkov * smallest CS size of 32MB. 199111b0a314SBorislav Petkov * 199211b0a314SBorislav Petkov * ddr[23]_cs_size have a similar purpose. 199311b0a314SBorislav Petkov */ 199411b0a314SBorislav Petkov diff = cs_mode/3 + (unsigned)(cs_mode > 5); 199511b0a314SBorislav Petkov 199611b0a314SBorislav Petkov return 32 << (cs_mode - diff); 199741d8bfabSBorislav Petkov } 199841d8bfabSBorislav Petkov else { 199941d8bfabSBorislav Petkov WARN_ON(cs_mode > 6); 200041d8bfabSBorislav Petkov return 32 << cs_mode; 200141d8bfabSBorislav Petkov } 2002ddff876dSDoug Thompson } 2003ddff876dSDoug Thompson 20041afd3c98SDoug Thompson /* 20051afd3c98SDoug Thompson * Get the number of DCT channels in use. 20061afd3c98SDoug Thompson * 20071afd3c98SDoug Thompson * Return: 20081afd3c98SDoug Thompson * number of Memory Channels in operation 20091afd3c98SDoug Thompson * Pass back: 20101afd3c98SDoug Thompson * contents of the DCL0_LOW register 20111afd3c98SDoug Thompson */ 20127d20d14dSBorislav Petkov static int f1x_early_channel_count(struct amd64_pvt *pvt) 20131afd3c98SDoug Thompson { 20146ba5dcdcSBorislav Petkov int i, j, channels = 0; 2015ddff876dSDoug Thompson 20167d20d14dSBorislav Petkov /* On F10h, if we are in 128 bit mode, then we are using 2 channels */ 2017a4b4bedcSBorislav Petkov if (pvt->fam == 0x10 && (pvt->dclr0 & WIDTH_128)) 20187d20d14dSBorislav Petkov return 2; 20191afd3c98SDoug Thompson 20201afd3c98SDoug Thompson /* 2021d16149e8SBorislav Petkov * Need to check if in unganged mode: In such, there are 2 channels, 2022d16149e8SBorislav Petkov * but they are not in 128 bit mode and thus the above 'dclr0' status 2023d16149e8SBorislav Petkov * bit will be OFF. 20241afd3c98SDoug Thompson * 20251afd3c98SDoug Thompson * Need to check DCT0[0] and DCT1[0] to see if only one of them has 20261afd3c98SDoug Thompson * their CSEnable bit on. If so, then SINGLE DIMM case. 20271afd3c98SDoug Thompson */ 2028956b9ba1SJoe Perches edac_dbg(0, "Data width is not 128 bits - need more decoding\n"); 20291afd3c98SDoug Thompson 20301afd3c98SDoug Thompson /* 20311afd3c98SDoug Thompson * Check DRAM Bank Address Mapping values for each DIMM to see if there 20321afd3c98SDoug Thompson * is more than just one DIMM present in unganged mode. Need to check 20331afd3c98SDoug Thompson * both controllers since DIMMs can be placed in either one. 20341afd3c98SDoug Thompson */ 2035525a1b20SBorislav Petkov for (i = 0; i < 2; i++) { 2036525a1b20SBorislav Petkov u32 dbam = (i ? pvt->dbam1 : pvt->dbam0); 20371afd3c98SDoug Thompson 203857a30854SWan Wei for (j = 0; j < 4; j++) { 203957a30854SWan Wei if (DBAM_DIMM(j, dbam) > 0) { 20401afd3c98SDoug Thompson channels++; 204157a30854SWan Wei break; 20421afd3c98SDoug Thompson } 204357a30854SWan Wei } 204457a30854SWan Wei } 20451afd3c98SDoug Thompson 2046d16149e8SBorislav Petkov if (channels > 2) 2047d16149e8SBorislav Petkov channels = 2; 2048d16149e8SBorislav Petkov 204924f9a7feSBorislav Petkov amd64_info("MCT channel count: %d\n", channels); 20501afd3c98SDoug Thompson 20511afd3c98SDoug Thompson return channels; 20521afd3c98SDoug Thompson } 20531afd3c98SDoug Thompson 2054f1cbbec9SYazen Ghannam static int f17_early_channel_count(struct amd64_pvt *pvt) 2055f1cbbec9SYazen Ghannam { 2056f1cbbec9SYazen Ghannam int i, channels = 0; 2057f1cbbec9SYazen Ghannam 2058f1cbbec9SYazen Ghannam /* SDP Control bit 31 (SdpInit) is clear for unused UMC channels */ 20594d30d2bcSYazen Ghannam for_each_umc(i) 2060f1cbbec9SYazen Ghannam channels += !!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT); 2061f1cbbec9SYazen Ghannam 2062f1cbbec9SYazen Ghannam amd64_info("MCT channel count: %d\n", channels); 2063f1cbbec9SYazen Ghannam 2064f1cbbec9SYazen Ghannam return channels; 2065f1cbbec9SYazen Ghannam } 2066f1cbbec9SYazen Ghannam 206741d8bfabSBorislav Petkov static int ddr3_cs_size(unsigned i, bool dct_width) 20681afd3c98SDoug Thompson { 206941d8bfabSBorislav Petkov unsigned shift = 0; 207041d8bfabSBorislav Petkov int cs_size = 0; 207141d8bfabSBorislav Petkov 207241d8bfabSBorislav Petkov if (i == 0 || i == 3 || i == 4) 207341d8bfabSBorislav Petkov cs_size = -1; 207441d8bfabSBorislav Petkov else if (i <= 2) 207541d8bfabSBorislav Petkov shift = i; 207641d8bfabSBorislav Petkov else if (i == 12) 207741d8bfabSBorislav Petkov shift = 7; 207841d8bfabSBorislav Petkov else if (!(i & 0x1)) 207941d8bfabSBorislav Petkov shift = i >> 1; 208041d8bfabSBorislav Petkov else 208141d8bfabSBorislav Petkov shift = (i + 1) >> 1; 208241d8bfabSBorislav Petkov 208341d8bfabSBorislav Petkov if (cs_size != -1) 208441d8bfabSBorislav Petkov cs_size = (128 * (1 << !!dct_width)) << shift; 208541d8bfabSBorislav Petkov 208641d8bfabSBorislav Petkov return cs_size; 208741d8bfabSBorislav Petkov } 208841d8bfabSBorislav Petkov 2089a597d2a5SAravind Gopalakrishnan static int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply) 2090a597d2a5SAravind Gopalakrishnan { 2091a597d2a5SAravind Gopalakrishnan unsigned shift = 0; 2092a597d2a5SAravind Gopalakrishnan int cs_size = 0; 2093a597d2a5SAravind Gopalakrishnan 2094a597d2a5SAravind Gopalakrishnan if (i < 4 || i == 6) 2095a597d2a5SAravind Gopalakrishnan cs_size = -1; 2096a597d2a5SAravind Gopalakrishnan else if (i == 12) 2097a597d2a5SAravind Gopalakrishnan shift = 7; 2098a597d2a5SAravind Gopalakrishnan else if (!(i & 0x1)) 2099a597d2a5SAravind Gopalakrishnan shift = i >> 1; 2100a597d2a5SAravind Gopalakrishnan else 2101a597d2a5SAravind Gopalakrishnan shift = (i + 1) >> 1; 2102a597d2a5SAravind Gopalakrishnan 2103a597d2a5SAravind Gopalakrishnan if (cs_size != -1) 2104a597d2a5SAravind Gopalakrishnan cs_size = rank_multiply * (128 << shift); 2105a597d2a5SAravind Gopalakrishnan 2106a597d2a5SAravind Gopalakrishnan return cs_size; 2107a597d2a5SAravind Gopalakrishnan } 2108a597d2a5SAravind Gopalakrishnan 2109a597d2a5SAravind Gopalakrishnan static int ddr4_cs_size(unsigned i) 2110a597d2a5SAravind Gopalakrishnan { 2111a597d2a5SAravind Gopalakrishnan int cs_size = 0; 2112a597d2a5SAravind Gopalakrishnan 2113a597d2a5SAravind Gopalakrishnan if (i == 0) 2114a597d2a5SAravind Gopalakrishnan cs_size = -1; 2115a597d2a5SAravind Gopalakrishnan else if (i == 1) 2116a597d2a5SAravind Gopalakrishnan cs_size = 1024; 2117a597d2a5SAravind Gopalakrishnan else 2118a597d2a5SAravind Gopalakrishnan /* Min cs_size = 1G */ 2119a597d2a5SAravind Gopalakrishnan cs_size = 1024 * (1 << (i >> 1)); 2120a597d2a5SAravind Gopalakrishnan 2121a597d2a5SAravind Gopalakrishnan return cs_size; 2122a597d2a5SAravind Gopalakrishnan } 2123a597d2a5SAravind Gopalakrishnan 212441d8bfabSBorislav Petkov static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 2125a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 212641d8bfabSBorislav Petkov { 212741d8bfabSBorislav Petkov u32 dclr = dct ? pvt->dclr1 : pvt->dclr0; 212841d8bfabSBorislav Petkov 212941d8bfabSBorislav Petkov WARN_ON(cs_mode > 11); 21301433eb99SBorislav Petkov 21311433eb99SBorislav Petkov if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE) 213241d8bfabSBorislav Petkov return ddr3_cs_size(cs_mode, dclr & WIDTH_128); 21331433eb99SBorislav Petkov else 213441d8bfabSBorislav Petkov return ddr2_cs_size(cs_mode, dclr & WIDTH_128); 213541d8bfabSBorislav Petkov } 21361433eb99SBorislav Petkov 213741d8bfabSBorislav Petkov /* 213841d8bfabSBorislav Petkov * F15h supports only 64bit DCT interfaces 213941d8bfabSBorislav Petkov */ 214041d8bfabSBorislav Petkov static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 2141a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 214241d8bfabSBorislav Petkov { 214341d8bfabSBorislav Petkov WARN_ON(cs_mode > 12); 214441d8bfabSBorislav Petkov 214541d8bfabSBorislav Petkov return ddr3_cs_size(cs_mode, false); 21461afd3c98SDoug Thompson } 21471afd3c98SDoug Thompson 2148a597d2a5SAravind Gopalakrishnan /* F15h M60h supports DDR4 mapping as well.. */ 2149a597d2a5SAravind Gopalakrishnan static int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 2150a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 2151a597d2a5SAravind Gopalakrishnan { 2152a597d2a5SAravind Gopalakrishnan int cs_size; 2153a597d2a5SAravind Gopalakrishnan u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr]; 2154a597d2a5SAravind Gopalakrishnan 2155a597d2a5SAravind Gopalakrishnan WARN_ON(cs_mode > 12); 2156a597d2a5SAravind Gopalakrishnan 2157a597d2a5SAravind Gopalakrishnan if (pvt->dram_type == MEM_DDR4) { 2158a597d2a5SAravind Gopalakrishnan if (cs_mode > 9) 2159a597d2a5SAravind Gopalakrishnan return -1; 2160a597d2a5SAravind Gopalakrishnan 2161a597d2a5SAravind Gopalakrishnan cs_size = ddr4_cs_size(cs_mode); 2162a597d2a5SAravind Gopalakrishnan } else if (pvt->dram_type == MEM_LRDDR3) { 2163a597d2a5SAravind Gopalakrishnan unsigned rank_multiply = dcsm & 0xf; 2164a597d2a5SAravind Gopalakrishnan 2165a597d2a5SAravind Gopalakrishnan if (rank_multiply == 3) 2166a597d2a5SAravind Gopalakrishnan rank_multiply = 4; 2167a597d2a5SAravind Gopalakrishnan cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply); 2168a597d2a5SAravind Gopalakrishnan } else { 2169a597d2a5SAravind Gopalakrishnan /* Minimum cs size is 512mb for F15hM60h*/ 2170a597d2a5SAravind Gopalakrishnan if (cs_mode == 0x1) 2171a597d2a5SAravind Gopalakrishnan return -1; 2172a597d2a5SAravind Gopalakrishnan 2173a597d2a5SAravind Gopalakrishnan cs_size = ddr3_cs_size(cs_mode, false); 2174a597d2a5SAravind Gopalakrishnan } 2175a597d2a5SAravind Gopalakrishnan 2176a597d2a5SAravind Gopalakrishnan return cs_size; 2177a597d2a5SAravind Gopalakrishnan } 2178a597d2a5SAravind Gopalakrishnan 217994c1acf2SAravind Gopalakrishnan /* 218018b94f66SAravind Gopalakrishnan * F16h and F15h model 30h have only limited cs_modes. 218194c1acf2SAravind Gopalakrishnan */ 218294c1acf2SAravind Gopalakrishnan static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 2183a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 218494c1acf2SAravind Gopalakrishnan { 218594c1acf2SAravind Gopalakrishnan WARN_ON(cs_mode > 12); 218694c1acf2SAravind Gopalakrishnan 218794c1acf2SAravind Gopalakrishnan if (cs_mode == 6 || cs_mode == 8 || 218894c1acf2SAravind Gopalakrishnan cs_mode == 9 || cs_mode == 12) 218994c1acf2SAravind Gopalakrishnan return -1; 219094c1acf2SAravind Gopalakrishnan else 219194c1acf2SAravind Gopalakrishnan return ddr3_cs_size(cs_mode, false); 219294c1acf2SAravind Gopalakrishnan } 219394c1acf2SAravind Gopalakrishnan 2194e53a3b26SYazen Ghannam static int f17_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc, 2195f1cbbec9SYazen Ghannam unsigned int cs_mode, int csrow_nr) 2196f1cbbec9SYazen Ghannam { 2197e53a3b26SYazen Ghannam u32 addr_mask_orig, addr_mask_deinterleaved; 2198e53a3b26SYazen Ghannam u32 msb, weight, num_zero_bits; 2199*2151c84eSYazen Ghannam int cs_mask_nr = csrow_nr; 2200e53a3b26SYazen Ghannam int dimm, size = 0; 2201f1cbbec9SYazen Ghannam 2202e53a3b26SYazen Ghannam /* No Chip Selects are enabled. */ 2203e53a3b26SYazen Ghannam if (!cs_mode) 2204e53a3b26SYazen Ghannam return size; 2205e53a3b26SYazen Ghannam 2206e53a3b26SYazen Ghannam /* Requested size of an even CS but none are enabled. */ 2207e53a3b26SYazen Ghannam if (!(cs_mode & CS_EVEN) && !(csrow_nr & 1)) 2208e53a3b26SYazen Ghannam return size; 2209e53a3b26SYazen Ghannam 2210e53a3b26SYazen Ghannam /* Requested size of an odd CS but none are enabled. */ 2211e53a3b26SYazen Ghannam if (!(cs_mode & CS_ODD) && (csrow_nr & 1)) 2212e53a3b26SYazen Ghannam return size; 2213e53a3b26SYazen Ghannam 2214e53a3b26SYazen Ghannam /* 2215*2151c84eSYazen Ghannam * Family 17h introduced systems with one mask per DIMM, 2216*2151c84eSYazen Ghannam * and two Chip Selects per DIMM. 2217*2151c84eSYazen Ghannam * 2218*2151c84eSYazen Ghannam * CS0 and CS1 -> MASK0 / DIMM0 2219*2151c84eSYazen Ghannam * CS2 and CS3 -> MASK1 / DIMM1 2220*2151c84eSYazen Ghannam * 2221*2151c84eSYazen Ghannam * Family 19h Model 10h introduced systems with one mask per Chip Select, 2222*2151c84eSYazen Ghannam * and two Chip Selects per DIMM. 2223*2151c84eSYazen Ghannam * 2224*2151c84eSYazen Ghannam * CS0 -> MASK0 -> DIMM0 2225*2151c84eSYazen Ghannam * CS1 -> MASK1 -> DIMM0 2226*2151c84eSYazen Ghannam * CS2 -> MASK2 -> DIMM1 2227*2151c84eSYazen Ghannam * CS3 -> MASK3 -> DIMM1 2228*2151c84eSYazen Ghannam * 2229*2151c84eSYazen Ghannam * Keep the mask number equal to the Chip Select number for newer systems, 2230*2151c84eSYazen Ghannam * and shift the mask number for older systems. 2231e53a3b26SYazen Ghannam */ 2232e53a3b26SYazen Ghannam dimm = csrow_nr >> 1; 2233e53a3b26SYazen Ghannam 2234*2151c84eSYazen Ghannam if (!fam_type->flags.zn_regs_v2) 2235*2151c84eSYazen Ghannam cs_mask_nr >>= 1; 2236*2151c84eSYazen Ghannam 223781f5090dSYazen Ghannam /* Asymmetric dual-rank DIMM support. */ 223881f5090dSYazen Ghannam if ((csrow_nr & 1) && (cs_mode & CS_ODD_SECONDARY)) 2239*2151c84eSYazen Ghannam addr_mask_orig = pvt->csels[umc].csmasks_sec[cs_mask_nr]; 224081f5090dSYazen Ghannam else 2241*2151c84eSYazen Ghannam addr_mask_orig = pvt->csels[umc].csmasks[cs_mask_nr]; 2242e53a3b26SYazen Ghannam 2243e53a3b26SYazen Ghannam /* 2244e53a3b26SYazen Ghannam * The number of zero bits in the mask is equal to the number of bits 2245e53a3b26SYazen Ghannam * in a full mask minus the number of bits in the current mask. 2246e53a3b26SYazen Ghannam * 2247e53a3b26SYazen Ghannam * The MSB is the number of bits in the full mask because BIT[0] is 2248e53a3b26SYazen Ghannam * always 0. 22499f4873fbSYazen Ghannam * 22509f4873fbSYazen Ghannam * In the special 3 Rank interleaving case, a single bit is flipped 22519f4873fbSYazen Ghannam * without swapping with the most significant bit. This can be handled 22529f4873fbSYazen Ghannam * by keeping the MSB where it is and ignoring the single zero bit. 2253e53a3b26SYazen Ghannam */ 2254e53a3b26SYazen Ghannam msb = fls(addr_mask_orig) - 1; 2255e53a3b26SYazen Ghannam weight = hweight_long(addr_mask_orig); 22569f4873fbSYazen Ghannam num_zero_bits = msb - weight - !!(cs_mode & CS_3R_INTERLEAVE); 2257e53a3b26SYazen Ghannam 2258e53a3b26SYazen Ghannam /* Take the number of zero bits off from the top of the mask. */ 2259e53a3b26SYazen Ghannam addr_mask_deinterleaved = GENMASK_ULL(msb - num_zero_bits, 1); 2260e53a3b26SYazen Ghannam 2261e53a3b26SYazen Ghannam edac_dbg(1, "CS%d DIMM%d AddrMasks:\n", csrow_nr, dimm); 2262e53a3b26SYazen Ghannam edac_dbg(1, " Original AddrMask: 0x%x\n", addr_mask_orig); 2263e53a3b26SYazen Ghannam edac_dbg(1, " Deinterleaved AddrMask: 0x%x\n", addr_mask_deinterleaved); 2264f1cbbec9SYazen Ghannam 2265f1cbbec9SYazen Ghannam /* Register [31:1] = Address [39:9]. Size is in kBs here. */ 2266e53a3b26SYazen Ghannam size = (addr_mask_deinterleaved >> 2) + 1; 2267f1cbbec9SYazen Ghannam 2268f1cbbec9SYazen Ghannam /* Return size in MBs. */ 2269f1cbbec9SYazen Ghannam return size >> 10; 2270f1cbbec9SYazen Ghannam } 2271f1cbbec9SYazen Ghannam 22725a5d2371SBorislav Petkov static void read_dram_ctl_register(struct amd64_pvt *pvt) 22736163b5d4SDoug Thompson { 22746163b5d4SDoug Thompson 2275a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) 22765a5d2371SBorislav Petkov return; 22775a5d2371SBorislav Petkov 22787981a28fSAravind Gopalakrishnan if (!amd64_read_pci_cfg(pvt->F2, DCT_SEL_LO, &pvt->dct_sel_lo)) { 2279956b9ba1SJoe Perches edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n", 228078da121eSBorislav Petkov pvt->dct_sel_lo, dct_sel_baseaddr(pvt)); 22816163b5d4SDoug Thompson 2282956b9ba1SJoe Perches edac_dbg(0, " DCTs operate in %s mode\n", 22835a5d2371SBorislav Petkov (dct_ganging_enabled(pvt) ? "ganged" : "unganged")); 22846163b5d4SDoug Thompson 228572381bd5SBorislav Petkov if (!dct_ganging_enabled(pvt)) 2286956b9ba1SJoe Perches edac_dbg(0, " Address range split per DCT: %s\n", 228772381bd5SBorislav Petkov (dct_high_range_enabled(pvt) ? "yes" : "no")); 228872381bd5SBorislav Petkov 2289956b9ba1SJoe Perches edac_dbg(0, " data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n", 229072381bd5SBorislav Petkov (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"), 229172381bd5SBorislav Petkov (dct_memory_cleared(pvt) ? "yes" : "no")); 229272381bd5SBorislav Petkov 2293956b9ba1SJoe Perches edac_dbg(0, " channel interleave: %s, " 229478da121eSBorislav Petkov "interleave bits selector: 0x%x\n", 229572381bd5SBorislav Petkov (dct_interleave_enabled(pvt) ? "enabled" : "disabled"), 22966163b5d4SDoug Thompson dct_sel_interleave_addr(pvt)); 22976163b5d4SDoug Thompson } 22986163b5d4SDoug Thompson 22997981a28fSAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F2, DCT_SEL_HI, &pvt->dct_sel_hi); 23006163b5d4SDoug Thompson } 23016163b5d4SDoug Thompson 2302f71d0a05SDoug Thompson /* 230318b94f66SAravind Gopalakrishnan * Determine channel (DCT) based on the interleaving mode (see F15h M30h BKDG, 230418b94f66SAravind Gopalakrishnan * 2.10.12 Memory Interleaving Modes). 230518b94f66SAravind Gopalakrishnan */ 230618b94f66SAravind Gopalakrishnan static u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, 230718b94f66SAravind Gopalakrishnan u8 intlv_en, int num_dcts_intlv, 230818b94f66SAravind Gopalakrishnan u32 dct_sel) 230918b94f66SAravind Gopalakrishnan { 231018b94f66SAravind Gopalakrishnan u8 channel = 0; 231118b94f66SAravind Gopalakrishnan u8 select; 231218b94f66SAravind Gopalakrishnan 231318b94f66SAravind Gopalakrishnan if (!(intlv_en)) 231418b94f66SAravind Gopalakrishnan return (u8)(dct_sel); 231518b94f66SAravind Gopalakrishnan 231618b94f66SAravind Gopalakrishnan if (num_dcts_intlv == 2) { 231718b94f66SAravind Gopalakrishnan select = (sys_addr >> 8) & 0x3; 231818b94f66SAravind Gopalakrishnan channel = select ? 0x3 : 0; 23199d0e8d83SAravind Gopalakrishnan } else if (num_dcts_intlv == 4) { 23209d0e8d83SAravind Gopalakrishnan u8 intlv_addr = dct_sel_interleave_addr(pvt); 23219d0e8d83SAravind Gopalakrishnan switch (intlv_addr) { 23229d0e8d83SAravind Gopalakrishnan case 0x4: 23239d0e8d83SAravind Gopalakrishnan channel = (sys_addr >> 8) & 0x3; 23249d0e8d83SAravind Gopalakrishnan break; 23259d0e8d83SAravind Gopalakrishnan case 0x5: 23269d0e8d83SAravind Gopalakrishnan channel = (sys_addr >> 9) & 0x3; 23279d0e8d83SAravind Gopalakrishnan break; 23289d0e8d83SAravind Gopalakrishnan } 23299d0e8d83SAravind Gopalakrishnan } 233018b94f66SAravind Gopalakrishnan return channel; 233118b94f66SAravind Gopalakrishnan } 233218b94f66SAravind Gopalakrishnan 233318b94f66SAravind Gopalakrishnan /* 2334229a7a11SBorislav Petkov * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory 2335f71d0a05SDoug Thompson * Interleaving Modes. 2336f71d0a05SDoug Thompson */ 2337b15f0fcaSBorislav Petkov static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, 2338229a7a11SBorislav Petkov bool hi_range_sel, u8 intlv_en) 23396163b5d4SDoug Thompson { 2340151fa71cSBorislav Petkov u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1; 23416163b5d4SDoug Thompson 23426163b5d4SDoug Thompson if (dct_ganging_enabled(pvt)) 2343229a7a11SBorislav Petkov return 0; 2344229a7a11SBorislav Petkov 2345229a7a11SBorislav Petkov if (hi_range_sel) 2346229a7a11SBorislav Petkov return dct_sel_high; 2347229a7a11SBorislav Petkov 2348f71d0a05SDoug Thompson /* 2349f71d0a05SDoug Thompson * see F2x110[DctSelIntLvAddr] - channel interleave mode 2350f71d0a05SDoug Thompson */ 2351229a7a11SBorislav Petkov if (dct_interleave_enabled(pvt)) { 2352229a7a11SBorislav Petkov u8 intlv_addr = dct_sel_interleave_addr(pvt); 23536163b5d4SDoug Thompson 2354229a7a11SBorislav Petkov /* return DCT select function: 0=DCT0, 1=DCT1 */ 2355229a7a11SBorislav Petkov if (!intlv_addr) 2356229a7a11SBorislav Petkov return sys_addr >> 6 & 1; 23576163b5d4SDoug Thompson 2358229a7a11SBorislav Petkov if (intlv_addr & 0x2) { 2359229a7a11SBorislav Petkov u8 shift = intlv_addr & 0x1 ? 9 : 6; 2360dc0a50a8SYazen Ghannam u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) & 1; 2361229a7a11SBorislav Petkov 2362229a7a11SBorislav Petkov return ((sys_addr >> shift) & 1) ^ temp; 23636163b5d4SDoug Thompson } 23646163b5d4SDoug Thompson 2365dc0a50a8SYazen Ghannam if (intlv_addr & 0x4) { 2366dc0a50a8SYazen Ghannam u8 shift = intlv_addr & 0x1 ? 9 : 8; 2367dc0a50a8SYazen Ghannam 2368dc0a50a8SYazen Ghannam return (sys_addr >> shift) & 1; 2369dc0a50a8SYazen Ghannam } 2370dc0a50a8SYazen Ghannam 2371229a7a11SBorislav Petkov return (sys_addr >> (12 + hweight8(intlv_en))) & 1; 2372229a7a11SBorislav Petkov } 2373229a7a11SBorislav Petkov 2374229a7a11SBorislav Petkov if (dct_high_range_enabled(pvt)) 2375229a7a11SBorislav Petkov return ~dct_sel_high & 1; 23766163b5d4SDoug Thompson 23776163b5d4SDoug Thompson return 0; 23786163b5d4SDoug Thompson } 23796163b5d4SDoug Thompson 2380c8e518d5SBorislav Petkov /* Convert the sys_addr to the normalized DCT address */ 2381c7e5301aSDaniel J Blueman static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range, 2382c8e518d5SBorislav Petkov u64 sys_addr, bool hi_rng, 2383c8e518d5SBorislav Petkov u32 dct_sel_base_addr) 23846163b5d4SDoug Thompson { 23856163b5d4SDoug Thompson u64 chan_off; 2386c8e518d5SBorislav Petkov u64 dram_base = get_dram_base(pvt, range); 2387c8e518d5SBorislav Petkov u64 hole_off = f10_dhar_offset(pvt); 23886f3508f6SDan Carpenter u64 dct_sel_base_off = (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16; 23896163b5d4SDoug Thompson 2390c8e518d5SBorislav Petkov if (hi_rng) { 2391c8e518d5SBorislav Petkov /* 2392c8e518d5SBorislav Petkov * if 2393c8e518d5SBorislav Petkov * base address of high range is below 4Gb 2394c8e518d5SBorislav Petkov * (bits [47:27] at [31:11]) 2395c8e518d5SBorislav Petkov * DRAM address space on this DCT is hoisted above 4Gb && 2396c8e518d5SBorislav Petkov * sys_addr > 4Gb 2397c8e518d5SBorislav Petkov * 2398c8e518d5SBorislav Petkov * remove hole offset from sys_addr 2399c8e518d5SBorislav Petkov * else 2400c8e518d5SBorislav Petkov * remove high range offset from sys_addr 2401c8e518d5SBorislav Petkov */ 2402c8e518d5SBorislav Petkov if ((!(dct_sel_base_addr >> 16) || 2403c8e518d5SBorislav Petkov dct_sel_base_addr < dhar_base(pvt)) && 2404972ea17aSBorislav Petkov dhar_valid(pvt) && 2405c8e518d5SBorislav Petkov (sys_addr >= BIT_64(32))) 2406bc21fa57SBorislav Petkov chan_off = hole_off; 24076163b5d4SDoug Thompson else 24086163b5d4SDoug Thompson chan_off = dct_sel_base_off; 24096163b5d4SDoug Thompson } else { 2410c8e518d5SBorislav Petkov /* 2411c8e518d5SBorislav Petkov * if 2412c8e518d5SBorislav Petkov * we have a valid hole && 2413c8e518d5SBorislav Petkov * sys_addr > 4Gb 2414c8e518d5SBorislav Petkov * 2415c8e518d5SBorislav Petkov * remove hole 2416c8e518d5SBorislav Petkov * else 2417c8e518d5SBorislav Petkov * remove dram base to normalize to DCT address 2418c8e518d5SBorislav Petkov */ 2419972ea17aSBorislav Petkov if (dhar_valid(pvt) && (sys_addr >= BIT_64(32))) 2420bc21fa57SBorislav Petkov chan_off = hole_off; 24216163b5d4SDoug Thompson else 2422c8e518d5SBorislav Petkov chan_off = dram_base; 24236163b5d4SDoug Thompson } 24246163b5d4SDoug Thompson 242510ef6b0dSChen, Gong return (sys_addr & GENMASK_ULL(47,6)) - (chan_off & GENMASK_ULL(47,23)); 24266163b5d4SDoug Thompson } 24276163b5d4SDoug Thompson 24286163b5d4SDoug Thompson /* 24296163b5d4SDoug Thompson * checks if the csrow passed in is marked as SPARED, if so returns the new 24306163b5d4SDoug Thompson * spare row 24316163b5d4SDoug Thompson */ 243211c75eadSBorislav Petkov static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow) 24336163b5d4SDoug Thompson { 2434614ec9d8SBorislav Petkov int tmp_cs; 24356163b5d4SDoug Thompson 2436614ec9d8SBorislav Petkov if (online_spare_swap_done(pvt, dct) && 2437614ec9d8SBorislav Petkov csrow == online_spare_bad_dramcs(pvt, dct)) { 2438614ec9d8SBorislav Petkov 2439614ec9d8SBorislav Petkov for_each_chip_select(tmp_cs, dct, pvt) { 2440614ec9d8SBorislav Petkov if (chip_select_base(tmp_cs, dct, pvt) & 0x2) { 2441614ec9d8SBorislav Petkov csrow = tmp_cs; 2442614ec9d8SBorislav Petkov break; 2443614ec9d8SBorislav Petkov } 2444614ec9d8SBorislav Petkov } 24456163b5d4SDoug Thompson } 24466163b5d4SDoug Thompson return csrow; 24476163b5d4SDoug Thompson } 24486163b5d4SDoug Thompson 24496163b5d4SDoug Thompson /* 24506163b5d4SDoug Thompson * Iterate over the DRAM DCT "base" and "mask" registers looking for a 24516163b5d4SDoug Thompson * SystemAddr match on the specified 'ChannelSelect' and 'NodeID' 24526163b5d4SDoug Thompson * 24536163b5d4SDoug Thompson * Return: 24546163b5d4SDoug Thompson * -EINVAL: NOT FOUND 24556163b5d4SDoug Thompson * 0..csrow = Chip-Select Row 24566163b5d4SDoug Thompson */ 2457c7e5301aSDaniel J Blueman static int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct) 24586163b5d4SDoug Thompson { 24596163b5d4SDoug Thompson struct mem_ctl_info *mci; 24606163b5d4SDoug Thompson struct amd64_pvt *pvt; 246111c75eadSBorislav Petkov u64 cs_base, cs_mask; 24626163b5d4SDoug Thompson int cs_found = -EINVAL; 24636163b5d4SDoug Thompson int csrow; 24646163b5d4SDoug Thompson 24652ec591acSBorislav Petkov mci = edac_mc_find(nid); 24666163b5d4SDoug Thompson if (!mci) 24676163b5d4SDoug Thompson return cs_found; 24686163b5d4SDoug Thompson 24696163b5d4SDoug Thompson pvt = mci->pvt_info; 24706163b5d4SDoug Thompson 2471956b9ba1SJoe Perches edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct); 24726163b5d4SDoug Thompson 247311c75eadSBorislav Petkov for_each_chip_select(csrow, dct, pvt) { 247411c75eadSBorislav Petkov if (!csrow_enabled(csrow, dct, pvt)) 24756163b5d4SDoug Thompson continue; 24766163b5d4SDoug Thompson 247711c75eadSBorislav Petkov get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask); 24786163b5d4SDoug Thompson 2479956b9ba1SJoe Perches edac_dbg(1, " CSROW=%d CSBase=0x%llx CSMask=0x%llx\n", 24806163b5d4SDoug Thompson csrow, cs_base, cs_mask); 24816163b5d4SDoug Thompson 248211c75eadSBorislav Petkov cs_mask = ~cs_mask; 24836163b5d4SDoug Thompson 2484956b9ba1SJoe Perches edac_dbg(1, " (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n", 248511c75eadSBorislav Petkov (in_addr & cs_mask), (cs_base & cs_mask)); 24866163b5d4SDoug Thompson 248711c75eadSBorislav Petkov if ((in_addr & cs_mask) == (cs_base & cs_mask)) { 248818b94f66SAravind Gopalakrishnan if (pvt->fam == 0x15 && pvt->model >= 0x30) { 248918b94f66SAravind Gopalakrishnan cs_found = csrow; 249018b94f66SAravind Gopalakrishnan break; 249118b94f66SAravind Gopalakrishnan } 249211c75eadSBorislav Petkov cs_found = f10_process_possible_spare(pvt, dct, csrow); 24936163b5d4SDoug Thompson 2494956b9ba1SJoe Perches edac_dbg(1, " MATCH csrow=%d\n", cs_found); 24956163b5d4SDoug Thompson break; 24966163b5d4SDoug Thompson } 24976163b5d4SDoug Thompson } 24986163b5d4SDoug Thompson return cs_found; 24996163b5d4SDoug Thompson } 25006163b5d4SDoug Thompson 250195b0ef55SBorislav Petkov /* 250295b0ef55SBorislav Petkov * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is 250395b0ef55SBorislav Petkov * swapped with a region located at the bottom of memory so that the GPU can use 250495b0ef55SBorislav Petkov * the interleaved region and thus two channels. 250595b0ef55SBorislav Petkov */ 2506b15f0fcaSBorislav Petkov static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr) 250795b0ef55SBorislav Petkov { 250895b0ef55SBorislav Petkov u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr; 250995b0ef55SBorislav Petkov 2510a4b4bedcSBorislav Petkov if (pvt->fam == 0x10) { 251195b0ef55SBorislav Petkov /* only revC3 and revE have that feature */ 2512a4b4bedcSBorislav Petkov if (pvt->model < 4 || (pvt->model < 0xa && pvt->stepping < 3)) 251395b0ef55SBorislav Petkov return sys_addr; 251495b0ef55SBorislav Petkov } 251595b0ef55SBorislav Petkov 25167981a28fSAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F2, SWAP_INTLV_REG, &swap_reg); 251795b0ef55SBorislav Petkov 251895b0ef55SBorislav Petkov if (!(swap_reg & 0x1)) 251995b0ef55SBorislav Petkov return sys_addr; 252095b0ef55SBorislav Petkov 252195b0ef55SBorislav Petkov swap_base = (swap_reg >> 3) & 0x7f; 252295b0ef55SBorislav Petkov swap_limit = (swap_reg >> 11) & 0x7f; 252395b0ef55SBorislav Petkov rgn_size = (swap_reg >> 20) & 0x7f; 252495b0ef55SBorislav Petkov tmp_addr = sys_addr >> 27; 252595b0ef55SBorislav Petkov 252695b0ef55SBorislav Petkov if (!(sys_addr >> 34) && 252795b0ef55SBorislav Petkov (((tmp_addr >= swap_base) && 252895b0ef55SBorislav Petkov (tmp_addr <= swap_limit)) || 252995b0ef55SBorislav Petkov (tmp_addr < rgn_size))) 253095b0ef55SBorislav Petkov return sys_addr ^ (u64)swap_base << 27; 253195b0ef55SBorislav Petkov 253295b0ef55SBorislav Petkov return sys_addr; 253395b0ef55SBorislav Petkov } 253495b0ef55SBorislav Petkov 2535f71d0a05SDoug Thompson /* For a given @dram_range, check if @sys_addr falls within it. */ 2536e761359aSBorislav Petkov static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range, 253733ca0643SBorislav Petkov u64 sys_addr, int *chan_sel) 2538f71d0a05SDoug Thompson { 2539229a7a11SBorislav Petkov int cs_found = -EINVAL; 2540c8e518d5SBorislav Petkov u64 chan_addr; 25415d4b58e8SBorislav Petkov u32 dct_sel_base; 254211c75eadSBorislav Petkov u8 channel; 2543229a7a11SBorislav Petkov bool high_range = false; 2544f71d0a05SDoug Thompson 25457f19bf75SBorislav Petkov u8 node_id = dram_dst_node(pvt, range); 2546229a7a11SBorislav Petkov u8 intlv_en = dram_intlv_en(pvt, range); 25477f19bf75SBorislav Petkov u32 intlv_sel = dram_intlv_sel(pvt, range); 2548f71d0a05SDoug Thompson 2549956b9ba1SJoe Perches edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n", 2550c8e518d5SBorislav Petkov range, sys_addr, get_dram_limit(pvt, range)); 2551f71d0a05SDoug Thompson 2552355fba60SBorislav Petkov if (dhar_valid(pvt) && 2553355fba60SBorislav Petkov dhar_base(pvt) <= sys_addr && 2554355fba60SBorislav Petkov sys_addr < BIT_64(32)) { 2555355fba60SBorislav Petkov amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n", 2556355fba60SBorislav Petkov sys_addr); 2557f71d0a05SDoug Thompson return -EINVAL; 2558355fba60SBorislav Petkov } 2559355fba60SBorislav Petkov 2560f030ddfbSBorislav Petkov if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en))) 2561355fba60SBorislav Petkov return -EINVAL; 2562f71d0a05SDoug Thompson 2563b15f0fcaSBorislav Petkov sys_addr = f1x_swap_interleaved_region(pvt, sys_addr); 256495b0ef55SBorislav Petkov 2565f71d0a05SDoug Thompson dct_sel_base = dct_sel_baseaddr(pvt); 2566f71d0a05SDoug Thompson 2567f71d0a05SDoug Thompson /* 2568f71d0a05SDoug Thompson * check whether addresses >= DctSelBaseAddr[47:27] are to be used to 2569f71d0a05SDoug Thompson * select between DCT0 and DCT1. 2570f71d0a05SDoug Thompson */ 2571f71d0a05SDoug Thompson if (dct_high_range_enabled(pvt) && 2572f71d0a05SDoug Thompson !dct_ganging_enabled(pvt) && 2573f71d0a05SDoug Thompson ((sys_addr >> 27) >= (dct_sel_base >> 11))) 2574229a7a11SBorislav Petkov high_range = true; 2575f71d0a05SDoug Thompson 2576b15f0fcaSBorislav Petkov channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en); 2577f71d0a05SDoug Thompson 2578b15f0fcaSBorislav Petkov chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr, 2579c8e518d5SBorislav Petkov high_range, dct_sel_base); 2580f71d0a05SDoug Thompson 2581e2f79dbdSBorislav Petkov /* Remove node interleaving, see F1x120 */ 2582e2f79dbdSBorislav Petkov if (intlv_en) 2583e2f79dbdSBorislav Petkov chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) | 2584e2f79dbdSBorislav Petkov (chan_addr & 0xfff); 2585f71d0a05SDoug Thompson 25865d4b58e8SBorislav Petkov /* remove channel interleave */ 2587f71d0a05SDoug Thompson if (dct_interleave_enabled(pvt) && 2588f71d0a05SDoug Thompson !dct_high_range_enabled(pvt) && 2589f71d0a05SDoug Thompson !dct_ganging_enabled(pvt)) { 25905d4b58e8SBorislav Petkov 25915d4b58e8SBorislav Petkov if (dct_sel_interleave_addr(pvt) != 1) { 25925d4b58e8SBorislav Petkov if (dct_sel_interleave_addr(pvt) == 0x3) 25935d4b58e8SBorislav Petkov /* hash 9 */ 25945d4b58e8SBorislav Petkov chan_addr = ((chan_addr >> 10) << 9) | 25955d4b58e8SBorislav Petkov (chan_addr & 0x1ff); 25965d4b58e8SBorislav Petkov else 25975d4b58e8SBorislav Petkov /* A[6] or hash 6 */ 25985d4b58e8SBorislav Petkov chan_addr = ((chan_addr >> 7) << 6) | 25995d4b58e8SBorislav Petkov (chan_addr & 0x3f); 26005d4b58e8SBorislav Petkov } else 26015d4b58e8SBorislav Petkov /* A[12] */ 26025d4b58e8SBorislav Petkov chan_addr = ((chan_addr >> 13) << 12) | 26035d4b58e8SBorislav Petkov (chan_addr & 0xfff); 2604f71d0a05SDoug Thompson } 2605f71d0a05SDoug Thompson 2606956b9ba1SJoe Perches edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr); 2607f71d0a05SDoug Thompson 2608b15f0fcaSBorislav Petkov cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel); 2609f71d0a05SDoug Thompson 261033ca0643SBorislav Petkov if (cs_found >= 0) 2611f71d0a05SDoug Thompson *chan_sel = channel; 261233ca0643SBorislav Petkov 2613f71d0a05SDoug Thompson return cs_found; 2614f71d0a05SDoug Thompson } 2615f71d0a05SDoug Thompson 261618b94f66SAravind Gopalakrishnan static int f15_m30h_match_to_this_node(struct amd64_pvt *pvt, unsigned range, 261718b94f66SAravind Gopalakrishnan u64 sys_addr, int *chan_sel) 261818b94f66SAravind Gopalakrishnan { 261918b94f66SAravind Gopalakrishnan int cs_found = -EINVAL; 262018b94f66SAravind Gopalakrishnan int num_dcts_intlv = 0; 262118b94f66SAravind Gopalakrishnan u64 chan_addr, chan_offset; 262218b94f66SAravind Gopalakrishnan u64 dct_base, dct_limit; 262318b94f66SAravind Gopalakrishnan u32 dct_cont_base_reg, dct_cont_limit_reg, tmp; 262418b94f66SAravind Gopalakrishnan u8 channel, alias_channel, leg_mmio_hole, dct_sel, dct_offset_en; 262518b94f66SAravind Gopalakrishnan 262618b94f66SAravind Gopalakrishnan u64 dhar_offset = f10_dhar_offset(pvt); 262718b94f66SAravind Gopalakrishnan u8 intlv_addr = dct_sel_interleave_addr(pvt); 262818b94f66SAravind Gopalakrishnan u8 node_id = dram_dst_node(pvt, range); 262918b94f66SAravind Gopalakrishnan u8 intlv_en = dram_intlv_en(pvt, range); 263018b94f66SAravind Gopalakrishnan 263118b94f66SAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F1, DRAM_CONT_BASE, &dct_cont_base_reg); 263218b94f66SAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F1, DRAM_CONT_LIMIT, &dct_cont_limit_reg); 263318b94f66SAravind Gopalakrishnan 263418b94f66SAravind Gopalakrishnan dct_offset_en = (u8) ((dct_cont_base_reg >> 3) & BIT(0)); 263518b94f66SAravind Gopalakrishnan dct_sel = (u8) ((dct_cont_base_reg >> 4) & 0x7); 263618b94f66SAravind Gopalakrishnan 263718b94f66SAravind Gopalakrishnan edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n", 263818b94f66SAravind Gopalakrishnan range, sys_addr, get_dram_limit(pvt, range)); 263918b94f66SAravind Gopalakrishnan 264018b94f66SAravind Gopalakrishnan if (!(get_dram_base(pvt, range) <= sys_addr) && 264118b94f66SAravind Gopalakrishnan !(get_dram_limit(pvt, range) >= sys_addr)) 264218b94f66SAravind Gopalakrishnan return -EINVAL; 264318b94f66SAravind Gopalakrishnan 264418b94f66SAravind Gopalakrishnan if (dhar_valid(pvt) && 264518b94f66SAravind Gopalakrishnan dhar_base(pvt) <= sys_addr && 264618b94f66SAravind Gopalakrishnan sys_addr < BIT_64(32)) { 264718b94f66SAravind Gopalakrishnan amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n", 264818b94f66SAravind Gopalakrishnan sys_addr); 264918b94f66SAravind Gopalakrishnan return -EINVAL; 265018b94f66SAravind Gopalakrishnan } 265118b94f66SAravind Gopalakrishnan 265218b94f66SAravind Gopalakrishnan /* Verify sys_addr is within DCT Range. */ 26534fc06b31SAravind Gopalakrishnan dct_base = (u64) dct_sel_baseaddr(pvt); 26544fc06b31SAravind Gopalakrishnan dct_limit = (dct_cont_limit_reg >> 11) & 0x1FFF; 265518b94f66SAravind Gopalakrishnan 265618b94f66SAravind Gopalakrishnan if (!(dct_cont_base_reg & BIT(0)) && 26574fc06b31SAravind Gopalakrishnan !(dct_base <= (sys_addr >> 27) && 26584fc06b31SAravind Gopalakrishnan dct_limit >= (sys_addr >> 27))) 265918b94f66SAravind Gopalakrishnan return -EINVAL; 266018b94f66SAravind Gopalakrishnan 266118b94f66SAravind Gopalakrishnan /* Verify number of dct's that participate in channel interleaving. */ 266218b94f66SAravind Gopalakrishnan num_dcts_intlv = (int) hweight8(intlv_en); 266318b94f66SAravind Gopalakrishnan 266418b94f66SAravind Gopalakrishnan if (!(num_dcts_intlv % 2 == 0) || (num_dcts_intlv > 4)) 266518b94f66SAravind Gopalakrishnan return -EINVAL; 266618b94f66SAravind Gopalakrishnan 2667dc0a50a8SYazen Ghannam if (pvt->model >= 0x60) 2668dc0a50a8SYazen Ghannam channel = f1x_determine_channel(pvt, sys_addr, false, intlv_en); 2669dc0a50a8SYazen Ghannam else 267018b94f66SAravind Gopalakrishnan channel = f15_m30h_determine_channel(pvt, sys_addr, intlv_en, 267118b94f66SAravind Gopalakrishnan num_dcts_intlv, dct_sel); 267218b94f66SAravind Gopalakrishnan 267318b94f66SAravind Gopalakrishnan /* Verify we stay within the MAX number of channels allowed */ 26747f3f5240SAravind Gopalakrishnan if (channel > 3) 267518b94f66SAravind Gopalakrishnan return -EINVAL; 267618b94f66SAravind Gopalakrishnan 267718b94f66SAravind Gopalakrishnan leg_mmio_hole = (u8) (dct_cont_base_reg >> 1 & BIT(0)); 267818b94f66SAravind Gopalakrishnan 267918b94f66SAravind Gopalakrishnan /* Get normalized DCT addr */ 268018b94f66SAravind Gopalakrishnan if (leg_mmio_hole && (sys_addr >= BIT_64(32))) 268118b94f66SAravind Gopalakrishnan chan_offset = dhar_offset; 268218b94f66SAravind Gopalakrishnan else 26834fc06b31SAravind Gopalakrishnan chan_offset = dct_base << 27; 268418b94f66SAravind Gopalakrishnan 268518b94f66SAravind Gopalakrishnan chan_addr = sys_addr - chan_offset; 268618b94f66SAravind Gopalakrishnan 268718b94f66SAravind Gopalakrishnan /* remove channel interleave */ 268818b94f66SAravind Gopalakrishnan if (num_dcts_intlv == 2) { 268918b94f66SAravind Gopalakrishnan if (intlv_addr == 0x4) 269018b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 9) << 8) | 269118b94f66SAravind Gopalakrishnan (chan_addr & 0xff); 269218b94f66SAravind Gopalakrishnan else if (intlv_addr == 0x5) 269318b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 10) << 9) | 269418b94f66SAravind Gopalakrishnan (chan_addr & 0x1ff); 269518b94f66SAravind Gopalakrishnan else 269618b94f66SAravind Gopalakrishnan return -EINVAL; 269718b94f66SAravind Gopalakrishnan 269818b94f66SAravind Gopalakrishnan } else if (num_dcts_intlv == 4) { 269918b94f66SAravind Gopalakrishnan if (intlv_addr == 0x4) 270018b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 10) << 8) | 270118b94f66SAravind Gopalakrishnan (chan_addr & 0xff); 270218b94f66SAravind Gopalakrishnan else if (intlv_addr == 0x5) 270318b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 11) << 9) | 270418b94f66SAravind Gopalakrishnan (chan_addr & 0x1ff); 270518b94f66SAravind Gopalakrishnan else 270618b94f66SAravind Gopalakrishnan return -EINVAL; 270718b94f66SAravind Gopalakrishnan } 270818b94f66SAravind Gopalakrishnan 270918b94f66SAravind Gopalakrishnan if (dct_offset_en) { 271018b94f66SAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F1, 271118b94f66SAravind Gopalakrishnan DRAM_CONT_HIGH_OFF + (int) channel * 4, 271218b94f66SAravind Gopalakrishnan &tmp); 27134fc06b31SAravind Gopalakrishnan chan_addr += (u64) ((tmp >> 11) & 0xfff) << 27; 271418b94f66SAravind Gopalakrishnan } 271518b94f66SAravind Gopalakrishnan 271618b94f66SAravind Gopalakrishnan f15h_select_dct(pvt, channel); 271718b94f66SAravind Gopalakrishnan 271818b94f66SAravind Gopalakrishnan edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr); 271918b94f66SAravind Gopalakrishnan 272018b94f66SAravind Gopalakrishnan /* 272118b94f66SAravind Gopalakrishnan * Find Chip select: 272218b94f66SAravind Gopalakrishnan * if channel = 3, then alias it to 1. This is because, in F15 M30h, 272318b94f66SAravind Gopalakrishnan * there is support for 4 DCT's, but only 2 are currently functional. 272418b94f66SAravind Gopalakrishnan * They are DCT0 and DCT3. But we have read all registers of DCT3 into 272518b94f66SAravind Gopalakrishnan * pvt->csels[1]. So we need to use '1' here to get correct info. 272618b94f66SAravind Gopalakrishnan * Refer F15 M30h BKDG Section 2.10 and 2.10.3 for clarifications. 272718b94f66SAravind Gopalakrishnan */ 272818b94f66SAravind Gopalakrishnan alias_channel = (channel == 3) ? 1 : channel; 272918b94f66SAravind Gopalakrishnan 273018b94f66SAravind Gopalakrishnan cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, alias_channel); 273118b94f66SAravind Gopalakrishnan 273218b94f66SAravind Gopalakrishnan if (cs_found >= 0) 273318b94f66SAravind Gopalakrishnan *chan_sel = alias_channel; 273418b94f66SAravind Gopalakrishnan 273518b94f66SAravind Gopalakrishnan return cs_found; 273618b94f66SAravind Gopalakrishnan } 273718b94f66SAravind Gopalakrishnan 273818b94f66SAravind Gopalakrishnan static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt, 273918b94f66SAravind Gopalakrishnan u64 sys_addr, 274033ca0643SBorislav Petkov int *chan_sel) 2741f71d0a05SDoug Thompson { 2742e761359aSBorislav Petkov int cs_found = -EINVAL; 2743e761359aSBorislav Petkov unsigned range; 2744f71d0a05SDoug Thompson 27457f19bf75SBorislav Petkov for (range = 0; range < DRAM_RANGES; range++) { 27467f19bf75SBorislav Petkov if (!dram_rw(pvt, range)) 2747f71d0a05SDoug Thompson continue; 2748f71d0a05SDoug Thompson 274918b94f66SAravind Gopalakrishnan if (pvt->fam == 0x15 && pvt->model >= 0x30) 275018b94f66SAravind Gopalakrishnan cs_found = f15_m30h_match_to_this_node(pvt, range, 275118b94f66SAravind Gopalakrishnan sys_addr, 275218b94f66SAravind Gopalakrishnan chan_sel); 2753f71d0a05SDoug Thompson 275418b94f66SAravind Gopalakrishnan else if ((get_dram_base(pvt, range) <= sys_addr) && 275518b94f66SAravind Gopalakrishnan (get_dram_limit(pvt, range) >= sys_addr)) { 2756b15f0fcaSBorislav Petkov cs_found = f1x_match_to_this_node(pvt, range, 275733ca0643SBorislav Petkov sys_addr, chan_sel); 2758f71d0a05SDoug Thompson if (cs_found >= 0) 2759f71d0a05SDoug Thompson break; 2760f71d0a05SDoug Thompson } 2761f71d0a05SDoug Thompson } 2762f71d0a05SDoug Thompson return cs_found; 2763f71d0a05SDoug Thompson } 2764f71d0a05SDoug Thompson 2765f71d0a05SDoug Thompson /* 2766bdc30a0cSBorislav Petkov * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps 2767bdc30a0cSBorislav Petkov * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW). 2768f71d0a05SDoug Thompson * 2769bdc30a0cSBorislav Petkov * The @sys_addr is usually an error address received from the hardware 2770bdc30a0cSBorislav Petkov * (MCX_ADDR). 2771f71d0a05SDoug Thompson */ 2772b15f0fcaSBorislav Petkov static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr, 277333ca0643SBorislav Petkov struct err_info *err) 2774f71d0a05SDoug Thompson { 2775f71d0a05SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 2776f71d0a05SDoug Thompson 277733ca0643SBorislav Petkov error_address_to_page_and_offset(sys_addr, err); 2778ab5a503cSMauro Carvalho Chehab 277933ca0643SBorislav Petkov err->csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &err->channel); 278033ca0643SBorislav Petkov if (err->csrow < 0) { 278133ca0643SBorislav Petkov err->err_code = ERR_CSROW; 2782bdc30a0cSBorislav Petkov return; 2783bdc30a0cSBorislav Petkov } 2784bdc30a0cSBorislav Petkov 2785f71d0a05SDoug Thompson /* 2786bdc30a0cSBorislav Petkov * We need the syndromes for channel detection only when we're 2787bdc30a0cSBorislav Petkov * ganged. Otherwise @chan should already contain the channel at 2788bdc30a0cSBorislav Petkov * this point. 2789f71d0a05SDoug Thompson */ 2790a97fa68eSBorislav Petkov if (dct_ganging_enabled(pvt)) 279133ca0643SBorislav Petkov err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome); 2792f71d0a05SDoug Thompson } 2793f71d0a05SDoug Thompson 2794f71d0a05SDoug Thompson /* 27958566c4dfSBorislav Petkov * debug routine to display the memory sizes of all logical DIMMs and its 2796cb328507SBorislav Petkov * CSROWs 2797f71d0a05SDoug Thompson */ 2798d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl) 2799f71d0a05SDoug Thompson { 2800bb89f5a0SBorislav Petkov int dimm, size0, size1; 2801525a1b20SBorislav Petkov u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases; 2802525a1b20SBorislav Petkov u32 dbam = ctrl ? pvt->dbam1 : pvt->dbam0; 2803f71d0a05SDoug Thompson 2804a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) { 28058566c4dfSBorislav Petkov /* K8 families < revF not supported yet */ 28061433eb99SBorislav Petkov if (pvt->ext_model < K8_REV_F) 28078566c4dfSBorislav Petkov return; 28088566c4dfSBorislav Petkov else 28098566c4dfSBorislav Petkov WARN_ON(ctrl != 0); 28108566c4dfSBorislav Petkov } 28118566c4dfSBorislav Petkov 28127981a28fSAravind Gopalakrishnan if (pvt->fam == 0x10) { 28137981a28fSAravind Gopalakrishnan dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1 28147981a28fSAravind Gopalakrishnan : pvt->dbam0; 28157981a28fSAravind Gopalakrishnan dcsb = (ctrl && !dct_ganging_enabled(pvt)) ? 28167981a28fSAravind Gopalakrishnan pvt->csels[1].csbases : 28177981a28fSAravind Gopalakrishnan pvt->csels[0].csbases; 28187981a28fSAravind Gopalakrishnan } else if (ctrl) { 28197981a28fSAravind Gopalakrishnan dbam = pvt->dbam0; 28207981a28fSAravind Gopalakrishnan dcsb = pvt->csels[1].csbases; 28217981a28fSAravind Gopalakrishnan } 2822956b9ba1SJoe Perches edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n", 2823956b9ba1SJoe Perches ctrl, dbam); 2824f71d0a05SDoug Thompson 28258566c4dfSBorislav Petkov edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl); 28268566c4dfSBorislav Petkov 2827f71d0a05SDoug Thompson /* Dump memory sizes for DIMM and its CSROWs */ 2828f71d0a05SDoug Thompson for (dimm = 0; dimm < 4; dimm++) { 2829f71d0a05SDoug Thompson 2830f71d0a05SDoug Thompson size0 = 0; 283111c75eadSBorislav Petkov if (dcsb[dimm*2] & DCSB_CS_ENABLE) 283207ed82efSYazen Ghannam /* 283307ed82efSYazen Ghannam * For F15m60h, we need multiplier for LRDIMM cs_size 283407ed82efSYazen Ghannam * calculation. We pass dimm value to the dbam_to_cs 2835a597d2a5SAravind Gopalakrishnan * mapper so we can find the multiplier from the 2836a597d2a5SAravind Gopalakrishnan * corresponding DCSM. 2837a597d2a5SAravind Gopalakrishnan */ 283841d8bfabSBorislav Petkov size0 = pvt->ops->dbam_to_cs(pvt, ctrl, 2839a597d2a5SAravind Gopalakrishnan DBAM_DIMM(dimm, dbam), 2840a597d2a5SAravind Gopalakrishnan dimm); 2841f71d0a05SDoug Thompson 2842f71d0a05SDoug Thompson size1 = 0; 284311c75eadSBorislav Petkov if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE) 284441d8bfabSBorislav Petkov size1 = pvt->ops->dbam_to_cs(pvt, ctrl, 2845a597d2a5SAravind Gopalakrishnan DBAM_DIMM(dimm, dbam), 2846a597d2a5SAravind Gopalakrishnan dimm); 2847f71d0a05SDoug Thompson 284824f9a7feSBorislav Petkov amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n", 2849bb89f5a0SBorislav Petkov dimm * 2, size0, 2850bb89f5a0SBorislav Petkov dimm * 2 + 1, size1); 2851f71d0a05SDoug Thompson } 2852f71d0a05SDoug Thompson } 2853f71d0a05SDoug Thompson 2854d1ea71cdSBorislav Petkov static struct amd64_family_type family_types[] = { 28554d37607aSDoug Thompson [K8_CPUS] = { 28560092b20dSBorislav Petkov .ctl_name = "K8", 28578d5b5d9cSBorislav Petkov .f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP, 28583f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL, 28595e4c5527SYazen Ghannam .max_mcs = 2, 28604d37607aSDoug Thompson .ops = { 28614d37607aSDoug Thompson .early_channel_count = k8_early_channel_count, 28624d37607aSDoug Thompson .map_sysaddr_to_csrow = k8_map_sysaddr_to_csrow, 28631433eb99SBorislav Petkov .dbam_to_cs = k8_dbam_to_chip_select, 28644d37607aSDoug Thompson } 28654d37607aSDoug Thompson }, 28664d37607aSDoug Thompson [F10_CPUS] = { 28670092b20dSBorislav Petkov .ctl_name = "F10h", 28688d5b5d9cSBorislav Petkov .f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP, 28693f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM, 28705e4c5527SYazen Ghannam .max_mcs = 2, 28714d37607aSDoug Thompson .ops = { 28727d20d14dSBorislav Petkov .early_channel_count = f1x_early_channel_count, 2873b15f0fcaSBorislav Petkov .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 28741433eb99SBorislav Petkov .dbam_to_cs = f10_dbam_to_chip_select, 2875b2b0c605SBorislav Petkov } 2876b2b0c605SBorislav Petkov }, 2877b2b0c605SBorislav Petkov [F15_CPUS] = { 2878b2b0c605SBorislav Petkov .ctl_name = "F15h", 2879df71a053SBorislav Petkov .f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1, 28803f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2, 28815e4c5527SYazen Ghannam .max_mcs = 2, 2882b2b0c605SBorislav Petkov .ops = { 28837d20d14dSBorislav Petkov .early_channel_count = f1x_early_channel_count, 2884b15f0fcaSBorislav Petkov .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 288541d8bfabSBorislav Petkov .dbam_to_cs = f15_dbam_to_chip_select, 28864d37607aSDoug Thompson } 28874d37607aSDoug Thompson }, 288818b94f66SAravind Gopalakrishnan [F15_M30H_CPUS] = { 288918b94f66SAravind Gopalakrishnan .ctl_name = "F15h_M30h", 289018b94f66SAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1, 28913f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2, 28925e4c5527SYazen Ghannam .max_mcs = 2, 289318b94f66SAravind Gopalakrishnan .ops = { 289418b94f66SAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 289518b94f66SAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 289618b94f66SAravind Gopalakrishnan .dbam_to_cs = f16_dbam_to_chip_select, 289718b94f66SAravind Gopalakrishnan } 289818b94f66SAravind Gopalakrishnan }, 2899a597d2a5SAravind Gopalakrishnan [F15_M60H_CPUS] = { 2900a597d2a5SAravind Gopalakrishnan .ctl_name = "F15h_M60h", 2901a597d2a5SAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1, 29023f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2, 29035e4c5527SYazen Ghannam .max_mcs = 2, 2904a597d2a5SAravind Gopalakrishnan .ops = { 2905a597d2a5SAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 2906a597d2a5SAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 2907a597d2a5SAravind Gopalakrishnan .dbam_to_cs = f15_m60h_dbam_to_chip_select, 2908a597d2a5SAravind Gopalakrishnan } 2909a597d2a5SAravind Gopalakrishnan }, 291094c1acf2SAravind Gopalakrishnan [F16_CPUS] = { 291194c1acf2SAravind Gopalakrishnan .ctl_name = "F16h", 291294c1acf2SAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1, 29133f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2, 29145e4c5527SYazen Ghannam .max_mcs = 2, 291594c1acf2SAravind Gopalakrishnan .ops = { 291694c1acf2SAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 291794c1acf2SAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 291894c1acf2SAravind Gopalakrishnan .dbam_to_cs = f16_dbam_to_chip_select, 291994c1acf2SAravind Gopalakrishnan } 292094c1acf2SAravind Gopalakrishnan }, 292185a8885bSAravind Gopalakrishnan [F16_M30H_CPUS] = { 292285a8885bSAravind Gopalakrishnan .ctl_name = "F16h_M30h", 292385a8885bSAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F1, 29243f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2, 29255e4c5527SYazen Ghannam .max_mcs = 2, 292685a8885bSAravind Gopalakrishnan .ops = { 292785a8885bSAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 292885a8885bSAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 292985a8885bSAravind Gopalakrishnan .dbam_to_cs = f16_dbam_to_chip_select, 293085a8885bSAravind Gopalakrishnan } 293185a8885bSAravind Gopalakrishnan }, 2932f1cbbec9SYazen Ghannam [F17_CPUS] = { 2933f1cbbec9SYazen Ghannam .ctl_name = "F17h", 2934f1cbbec9SYazen Ghannam .f0_id = PCI_DEVICE_ID_AMD_17H_DF_F0, 2935f1cbbec9SYazen Ghannam .f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6, 29365e4c5527SYazen Ghannam .max_mcs = 2, 2937f1cbbec9SYazen Ghannam .ops = { 2938f1cbbec9SYazen Ghannam .early_channel_count = f17_early_channel_count, 2939e53a3b26SYazen Ghannam .dbam_to_cs = f17_addr_mask_to_cs_size, 2940f1cbbec9SYazen Ghannam } 2941f1cbbec9SYazen Ghannam }, 29428960de4aSMichael Jin [F17_M10H_CPUS] = { 29438960de4aSMichael Jin .ctl_name = "F17h_M10h", 29448960de4aSMichael Jin .f0_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F0, 29458960de4aSMichael Jin .f6_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F6, 29465e4c5527SYazen Ghannam .max_mcs = 2, 29478960de4aSMichael Jin .ops = { 29488960de4aSMichael Jin .early_channel_count = f17_early_channel_count, 2949e53a3b26SYazen Ghannam .dbam_to_cs = f17_addr_mask_to_cs_size, 29508960de4aSMichael Jin } 29518960de4aSMichael Jin }, 29526e846239SYazen Ghannam [F17_M30H_CPUS] = { 29536e846239SYazen Ghannam .ctl_name = "F17h_M30h", 29546e846239SYazen Ghannam .f0_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F0, 29556e846239SYazen Ghannam .f6_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F6, 29565e4c5527SYazen Ghannam .max_mcs = 8, 29576e846239SYazen Ghannam .ops = { 29586e846239SYazen Ghannam .early_channel_count = f17_early_channel_count, 2959e53a3b26SYazen Ghannam .dbam_to_cs = f17_addr_mask_to_cs_size, 29606e846239SYazen Ghannam } 29616e846239SYazen Ghannam }, 2962b6bea24dSAlexander Monakov [F17_M60H_CPUS] = { 2963b6bea24dSAlexander Monakov .ctl_name = "F17h_M60h", 2964b6bea24dSAlexander Monakov .f0_id = PCI_DEVICE_ID_AMD_17H_M60H_DF_F0, 2965b6bea24dSAlexander Monakov .f6_id = PCI_DEVICE_ID_AMD_17H_M60H_DF_F6, 2966b6bea24dSAlexander Monakov .max_mcs = 2, 2967b6bea24dSAlexander Monakov .ops = { 2968b6bea24dSAlexander Monakov .early_channel_count = f17_early_channel_count, 2969b6bea24dSAlexander Monakov .dbam_to_cs = f17_addr_mask_to_cs_size, 2970b6bea24dSAlexander Monakov } 2971b6bea24dSAlexander Monakov }, 29723e443eb3SIsaac Vaughn [F17_M70H_CPUS] = { 29733e443eb3SIsaac Vaughn .ctl_name = "F17h_M70h", 29743e443eb3SIsaac Vaughn .f0_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F0, 29753e443eb3SIsaac Vaughn .f6_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F6, 29765e4c5527SYazen Ghannam .max_mcs = 2, 29773e443eb3SIsaac Vaughn .ops = { 29783e443eb3SIsaac Vaughn .early_channel_count = f17_early_channel_count, 29793e443eb3SIsaac Vaughn .dbam_to_cs = f17_addr_mask_to_cs_size, 29803e443eb3SIsaac Vaughn } 29813e443eb3SIsaac Vaughn }, 29822eb61c91SYazen Ghannam [F19_CPUS] = { 29832eb61c91SYazen Ghannam .ctl_name = "F19h", 29842eb61c91SYazen Ghannam .f0_id = PCI_DEVICE_ID_AMD_19H_DF_F0, 29852eb61c91SYazen Ghannam .f6_id = PCI_DEVICE_ID_AMD_19H_DF_F6, 29862eb61c91SYazen Ghannam .max_mcs = 8, 29872eb61c91SYazen Ghannam .ops = { 29882eb61c91SYazen Ghannam .early_channel_count = f17_early_channel_count, 29892eb61c91SYazen Ghannam .dbam_to_cs = f17_addr_mask_to_cs_size, 29902eb61c91SYazen Ghannam } 29912eb61c91SYazen Ghannam }, 2992e2be5955SYazen Ghannam [F19_M10H_CPUS] = { 2993e2be5955SYazen Ghannam .ctl_name = "F19h_M10h", 2994e2be5955SYazen Ghannam .f0_id = PCI_DEVICE_ID_AMD_19H_M10H_DF_F0, 2995e2be5955SYazen Ghannam .f6_id = PCI_DEVICE_ID_AMD_19H_M10H_DF_F6, 2996e2be5955SYazen Ghannam .max_mcs = 12, 2997*2151c84eSYazen Ghannam .flags.zn_regs_v2 = 1, 2998e2be5955SYazen Ghannam .ops = { 2999e2be5955SYazen Ghannam .early_channel_count = f17_early_channel_count, 3000e2be5955SYazen Ghannam .dbam_to_cs = f17_addr_mask_to_cs_size, 3001e2be5955SYazen Ghannam } 3002e2be5955SYazen Ghannam }, 30030b8bf9cbSMarc Bevand [F19_M50H_CPUS] = { 30040b8bf9cbSMarc Bevand .ctl_name = "F19h_M50h", 30050b8bf9cbSMarc Bevand .f0_id = PCI_DEVICE_ID_AMD_19H_M50H_DF_F0, 30060b8bf9cbSMarc Bevand .f6_id = PCI_DEVICE_ID_AMD_19H_M50H_DF_F6, 30070b8bf9cbSMarc Bevand .max_mcs = 2, 30080b8bf9cbSMarc Bevand .ops = { 30090b8bf9cbSMarc Bevand .early_channel_count = f17_early_channel_count, 30100b8bf9cbSMarc Bevand .dbam_to_cs = f17_addr_mask_to_cs_size, 30110b8bf9cbSMarc Bevand } 30120b8bf9cbSMarc Bevand }, 30134d37607aSDoug Thompson }; 30144d37607aSDoug Thompson 3015b1289d6fSDoug Thompson /* 3016bfc04aecSBorislav Petkov * These are tables of eigenvectors (one per line) which can be used for the 3017bfc04aecSBorislav Petkov * construction of the syndrome tables. The modified syndrome search algorithm 3018bfc04aecSBorislav Petkov * uses those to find the symbol in error and thus the DIMM. 3019b1289d6fSDoug Thompson * 3020bfc04aecSBorislav Petkov * Algorithm courtesy of Ross LaFetra from AMD. 3021b1289d6fSDoug Thompson */ 3022c7e5301aSDaniel J Blueman static const u16 x4_vectors[] = { 3023bfc04aecSBorislav Petkov 0x2f57, 0x1afe, 0x66cc, 0xdd88, 3024bfc04aecSBorislav Petkov 0x11eb, 0x3396, 0x7f4c, 0xeac8, 3025bfc04aecSBorislav Petkov 0x0001, 0x0002, 0x0004, 0x0008, 3026bfc04aecSBorislav Petkov 0x1013, 0x3032, 0x4044, 0x8088, 3027bfc04aecSBorislav Petkov 0x106b, 0x30d6, 0x70fc, 0xe0a8, 3028bfc04aecSBorislav Petkov 0x4857, 0xc4fe, 0x13cc, 0x3288, 3029bfc04aecSBorislav Petkov 0x1ac5, 0x2f4a, 0x5394, 0xa1e8, 3030bfc04aecSBorislav Petkov 0x1f39, 0x251e, 0xbd6c, 0x6bd8, 3031bfc04aecSBorislav Petkov 0x15c1, 0x2a42, 0x89ac, 0x4758, 3032bfc04aecSBorislav Petkov 0x2b03, 0x1602, 0x4f0c, 0xca08, 3033bfc04aecSBorislav Petkov 0x1f07, 0x3a0e, 0x6b04, 0xbd08, 3034bfc04aecSBorislav Petkov 0x8ba7, 0x465e, 0x244c, 0x1cc8, 3035bfc04aecSBorislav Petkov 0x2b87, 0x164e, 0x642c, 0xdc18, 3036bfc04aecSBorislav Petkov 0x40b9, 0x80de, 0x1094, 0x20e8, 3037bfc04aecSBorislav Petkov 0x27db, 0x1eb6, 0x9dac, 0x7b58, 3038bfc04aecSBorislav Petkov 0x11c1, 0x2242, 0x84ac, 0x4c58, 3039bfc04aecSBorislav Petkov 0x1be5, 0x2d7a, 0x5e34, 0xa718, 3040bfc04aecSBorislav Petkov 0x4b39, 0x8d1e, 0x14b4, 0x28d8, 3041bfc04aecSBorislav Petkov 0x4c97, 0xc87e, 0x11fc, 0x33a8, 3042bfc04aecSBorislav Petkov 0x8e97, 0x497e, 0x2ffc, 0x1aa8, 3043bfc04aecSBorislav Petkov 0x16b3, 0x3d62, 0x4f34, 0x8518, 3044bfc04aecSBorislav Petkov 0x1e2f, 0x391a, 0x5cac, 0xf858, 3045bfc04aecSBorislav Petkov 0x1d9f, 0x3b7a, 0x572c, 0xfe18, 3046bfc04aecSBorislav Petkov 0x15f5, 0x2a5a, 0x5264, 0xa3b8, 3047bfc04aecSBorislav Petkov 0x1dbb, 0x3b66, 0x715c, 0xe3f8, 3048bfc04aecSBorislav Petkov 0x4397, 0xc27e, 0x17fc, 0x3ea8, 3049bfc04aecSBorislav Petkov 0x1617, 0x3d3e, 0x6464, 0xb8b8, 3050bfc04aecSBorislav Petkov 0x23ff, 0x12aa, 0xab6c, 0x56d8, 3051bfc04aecSBorislav Petkov 0x2dfb, 0x1ba6, 0x913c, 0x7328, 3052bfc04aecSBorislav Petkov 0x185d, 0x2ca6, 0x7914, 0x9e28, 3053bfc04aecSBorislav Petkov 0x171b, 0x3e36, 0x7d7c, 0xebe8, 3054bfc04aecSBorislav Petkov 0x4199, 0x82ee, 0x19f4, 0x2e58, 3055bfc04aecSBorislav Petkov 0x4807, 0xc40e, 0x130c, 0x3208, 3056bfc04aecSBorislav Petkov 0x1905, 0x2e0a, 0x5804, 0xac08, 3057bfc04aecSBorislav Petkov 0x213f, 0x132a, 0xadfc, 0x5ba8, 3058bfc04aecSBorislav Petkov 0x19a9, 0x2efe, 0xb5cc, 0x6f88, 3059b1289d6fSDoug Thompson }; 3060b1289d6fSDoug Thompson 3061c7e5301aSDaniel J Blueman static const u16 x8_vectors[] = { 3062bfc04aecSBorislav Petkov 0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480, 3063bfc04aecSBorislav Petkov 0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80, 3064bfc04aecSBorislav Petkov 0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80, 3065bfc04aecSBorislav Petkov 0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80, 3066bfc04aecSBorislav Petkov 0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780, 3067bfc04aecSBorislav Petkov 0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080, 3068bfc04aecSBorislav Petkov 0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080, 3069bfc04aecSBorislav Petkov 0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080, 3070bfc04aecSBorislav Petkov 0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80, 3071bfc04aecSBorislav Petkov 0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580, 3072bfc04aecSBorislav Petkov 0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880, 3073bfc04aecSBorislav Petkov 0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280, 3074bfc04aecSBorislav Petkov 0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180, 3075bfc04aecSBorislav Petkov 0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580, 3076bfc04aecSBorislav Petkov 0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280, 3077bfc04aecSBorislav Petkov 0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180, 3078bfc04aecSBorislav Petkov 0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080, 3079bfc04aecSBorislav Petkov 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 3080bfc04aecSBorislav Petkov 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000, 3081bfc04aecSBorislav Petkov }; 3082bfc04aecSBorislav Petkov 3083c7e5301aSDaniel J Blueman static int decode_syndrome(u16 syndrome, const u16 *vectors, unsigned num_vecs, 3084d34a6ecdSBorislav Petkov unsigned v_dim) 3085b1289d6fSDoug Thompson { 3086bfc04aecSBorislav Petkov unsigned int i, err_sym; 3087b1289d6fSDoug Thompson 3088bfc04aecSBorislav Petkov for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) { 3089bfc04aecSBorislav Petkov u16 s = syndrome; 3090d34a6ecdSBorislav Petkov unsigned v_idx = err_sym * v_dim; 3091d34a6ecdSBorislav Petkov unsigned v_end = (err_sym + 1) * v_dim; 3092b1289d6fSDoug Thompson 3093bfc04aecSBorislav Petkov /* walk over all 16 bits of the syndrome */ 3094bfc04aecSBorislav Petkov for (i = 1; i < (1U << 16); i <<= 1) { 3095bfc04aecSBorislav Petkov 3096bfc04aecSBorislav Petkov /* if bit is set in that eigenvector... */ 3097bfc04aecSBorislav Petkov if (v_idx < v_end && vectors[v_idx] & i) { 3098bfc04aecSBorislav Petkov u16 ev_comp = vectors[v_idx++]; 3099bfc04aecSBorislav Petkov 3100bfc04aecSBorislav Petkov /* ... and bit set in the modified syndrome, */ 3101bfc04aecSBorislav Petkov if (s & i) { 3102bfc04aecSBorislav Petkov /* remove it. */ 3103bfc04aecSBorislav Petkov s ^= ev_comp; 3104bfc04aecSBorislav Petkov 3105bfc04aecSBorislav Petkov if (!s) 3106bfc04aecSBorislav Petkov return err_sym; 3107bfc04aecSBorislav Petkov } 3108bfc04aecSBorislav Petkov 3109bfc04aecSBorislav Petkov } else if (s & i) 3110bfc04aecSBorislav Petkov /* can't get to zero, move to next symbol */ 3111bfc04aecSBorislav Petkov break; 3112bfc04aecSBorislav Petkov } 3113b1289d6fSDoug Thompson } 3114b1289d6fSDoug Thompson 3115956b9ba1SJoe Perches edac_dbg(0, "syndrome(%x) not found\n", syndrome); 3116b1289d6fSDoug Thompson return -1; 3117b1289d6fSDoug Thompson } 3118d27bf6faSDoug Thompson 3119bfc04aecSBorislav Petkov static int map_err_sym_to_channel(int err_sym, int sym_size) 3120bfc04aecSBorislav Petkov { 3121bfc04aecSBorislav Petkov if (sym_size == 4) 3122bfc04aecSBorislav Petkov switch (err_sym) { 3123bfc04aecSBorislav Petkov case 0x20: 3124bfc04aecSBorislav Petkov case 0x21: 3125bfc04aecSBorislav Petkov return 0; 3126bfc04aecSBorislav Petkov case 0x22: 3127bfc04aecSBorislav Petkov case 0x23: 3128bfc04aecSBorislav Petkov return 1; 3129bfc04aecSBorislav Petkov default: 3130bfc04aecSBorislav Petkov return err_sym >> 4; 3131bfc04aecSBorislav Petkov } 3132bfc04aecSBorislav Petkov /* x8 symbols */ 3133bfc04aecSBorislav Petkov else 3134bfc04aecSBorislav Petkov switch (err_sym) { 3135bfc04aecSBorislav Petkov /* imaginary bits not in a DIMM */ 3136bfc04aecSBorislav Petkov case 0x10: 3137bfc04aecSBorislav Petkov WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n", 3138bfc04aecSBorislav Petkov err_sym); 3139bfc04aecSBorislav Petkov return -1; 3140bfc04aecSBorislav Petkov case 0x11: 3141bfc04aecSBorislav Petkov return 0; 3142bfc04aecSBorislav Petkov case 0x12: 3143bfc04aecSBorislav Petkov return 1; 3144bfc04aecSBorislav Petkov default: 3145bfc04aecSBorislav Petkov return err_sym >> 3; 3146bfc04aecSBorislav Petkov } 3147bfc04aecSBorislav Petkov return -1; 3148bfc04aecSBorislav Petkov } 3149bfc04aecSBorislav Petkov 3150bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome) 3151bfc04aecSBorislav Petkov { 3152bfc04aecSBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 3153ad6a32e9SBorislav Petkov int err_sym = -1; 3154bfc04aecSBorislav Petkov 3155a3b7db09SBorislav Petkov if (pvt->ecc_sym_sz == 8) 3156bfc04aecSBorislav Petkov err_sym = decode_syndrome(syndrome, x8_vectors, 3157ad6a32e9SBorislav Petkov ARRAY_SIZE(x8_vectors), 3158a3b7db09SBorislav Petkov pvt->ecc_sym_sz); 3159a3b7db09SBorislav Petkov else if (pvt->ecc_sym_sz == 4) 3160ad6a32e9SBorislav Petkov err_sym = decode_syndrome(syndrome, x4_vectors, 3161ad6a32e9SBorislav Petkov ARRAY_SIZE(x4_vectors), 3162a3b7db09SBorislav Petkov pvt->ecc_sym_sz); 3163ad6a32e9SBorislav Petkov else { 3164a3b7db09SBorislav Petkov amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz); 3165ad6a32e9SBorislav Petkov return err_sym; 3166bfc04aecSBorislav Petkov } 3167ad6a32e9SBorislav Petkov 3168a3b7db09SBorislav Petkov return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz); 316941c31044SBorislav Petkov } 3170bfc04aecSBorislav Petkov 3171e70984d9SYazen Ghannam static void __log_ecc_error(struct mem_ctl_info *mci, struct err_info *err, 317233ca0643SBorislav Petkov u8 ecc_type) 3173d27bf6faSDoug Thompson { 317433ca0643SBorislav Petkov enum hw_event_mc_err_type err_type; 317533ca0643SBorislav Petkov const char *string; 3176d27bf6faSDoug Thompson 317733ca0643SBorislav Petkov if (ecc_type == 2) 317833ca0643SBorislav Petkov err_type = HW_EVENT_ERR_CORRECTED; 317933ca0643SBorislav Petkov else if (ecc_type == 1) 318033ca0643SBorislav Petkov err_type = HW_EVENT_ERR_UNCORRECTED; 3181d12a969eSYazen Ghannam else if (ecc_type == 3) 3182d12a969eSYazen Ghannam err_type = HW_EVENT_ERR_DEFERRED; 318333ca0643SBorislav Petkov else { 318433ca0643SBorislav Petkov WARN(1, "Something is rotten in the state of Denmark.\n"); 3185d27bf6faSDoug Thompson return; 3186d27bf6faSDoug Thompson } 3187d27bf6faSDoug Thompson 318833ca0643SBorislav Petkov switch (err->err_code) { 318933ca0643SBorislav Petkov case DECODE_OK: 319033ca0643SBorislav Petkov string = ""; 319133ca0643SBorislav Petkov break; 319233ca0643SBorislav Petkov case ERR_NODE: 319333ca0643SBorislav Petkov string = "Failed to map error addr to a node"; 319433ca0643SBorislav Petkov break; 319533ca0643SBorislav Petkov case ERR_CSROW: 319633ca0643SBorislav Petkov string = "Failed to map error addr to a csrow"; 319733ca0643SBorislav Petkov break; 319833ca0643SBorislav Petkov case ERR_CHANNEL: 3199713ad546SYazen Ghannam string = "Unknown syndrome - possible error reporting race"; 3200713ad546SYazen Ghannam break; 3201713ad546SYazen Ghannam case ERR_SYND: 3202713ad546SYazen Ghannam string = "MCA_SYND not valid - unknown syndrome and csrow"; 3203713ad546SYazen Ghannam break; 3204713ad546SYazen Ghannam case ERR_NORM_ADDR: 3205713ad546SYazen Ghannam string = "Cannot decode normalized address"; 320633ca0643SBorislav Petkov break; 320733ca0643SBorislav Petkov default: 320833ca0643SBorislav Petkov string = "WTF error"; 320933ca0643SBorislav Petkov break; 3210d27bf6faSDoug Thompson } 321133ca0643SBorislav Petkov 321233ca0643SBorislav Petkov edac_mc_handle_error(err_type, mci, 1, 321333ca0643SBorislav Petkov err->page, err->offset, err->syndrome, 321433ca0643SBorislav Petkov err->csrow, err->channel, -1, 321533ca0643SBorislav Petkov string, ""); 3216d27bf6faSDoug Thompson } 3217d27bf6faSDoug Thompson 3218df781d03SBorislav Petkov static inline void decode_bus_error(int node_id, struct mce *m) 3219d27bf6faSDoug Thompson { 32200c510cc8SDaniel J Blueman struct mem_ctl_info *mci; 32210c510cc8SDaniel J Blueman struct amd64_pvt *pvt; 3222f192c7b1SBorislav Petkov u8 ecc_type = (m->status >> 45) & 0x3; 322366fed2d4SBorislav Petkov u8 xec = XEC(m->status, 0x1f); 322466fed2d4SBorislav Petkov u16 ec = EC(m->status); 322533ca0643SBorislav Petkov u64 sys_addr; 322633ca0643SBorislav Petkov struct err_info err; 3227d27bf6faSDoug Thompson 32280c510cc8SDaniel J Blueman mci = edac_mc_find(node_id); 32290c510cc8SDaniel J Blueman if (!mci) 32300c510cc8SDaniel J Blueman return; 32310c510cc8SDaniel J Blueman 32320c510cc8SDaniel J Blueman pvt = mci->pvt_info; 32330c510cc8SDaniel J Blueman 323466fed2d4SBorislav Petkov /* Bail out early if this was an 'observed' error */ 32355980bb9cSBorislav Petkov if (PP(ec) == NBSL_PP_OBS) 3236b70ef010SBorislav Petkov return; 3237d27bf6faSDoug Thompson 3238ecaf5606SBorislav Petkov /* Do only ECC errors */ 3239ecaf5606SBorislav Petkov if (xec && xec != F10_NBSL_EXT_ERR_ECC) 3240d27bf6faSDoug Thompson return; 3241d27bf6faSDoug Thompson 324233ca0643SBorislav Petkov memset(&err, 0, sizeof(err)); 324333ca0643SBorislav Petkov 3244a4b4bedcSBorislav Petkov sys_addr = get_error_address(pvt, m); 324533ca0643SBorislav Petkov 3246ecaf5606SBorislav Petkov if (ecc_type == 2) 324733ca0643SBorislav Petkov err.syndrome = extract_syndrome(m->status); 324833ca0643SBorislav Petkov 324933ca0643SBorislav Petkov pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err); 325033ca0643SBorislav Petkov 3251e70984d9SYazen Ghannam __log_ecc_error(mci, &err, ecc_type); 3252d27bf6faSDoug Thompson } 3253d27bf6faSDoug Thompson 32540ec449eeSDoug Thompson /* 3255713ad546SYazen Ghannam * To find the UMC channel represented by this bank we need to match on its 3256713ad546SYazen Ghannam * instance_id. The instance_id of a bank is held in the lower 32 bits of its 3257713ad546SYazen Ghannam * IPID. 3258bdcee774SYazen Ghannam * 3259bdcee774SYazen Ghannam * Currently, we can derive the channel number by looking at the 6th nibble in 3260bdcee774SYazen Ghannam * the instance_id. For example, instance_id=0xYXXXXX where Y is the channel 3261bdcee774SYazen Ghannam * number. 3262713ad546SYazen Ghannam */ 3263bdcee774SYazen Ghannam static int find_umc_channel(struct mce *m) 3264713ad546SYazen Ghannam { 3265bdcee774SYazen Ghannam return (m->ipid & GENMASK(31, 0)) >> 20; 3266713ad546SYazen Ghannam } 3267713ad546SYazen Ghannam 3268713ad546SYazen Ghannam static void decode_umc_error(int node_id, struct mce *m) 3269713ad546SYazen Ghannam { 3270713ad546SYazen Ghannam u8 ecc_type = (m->status >> 45) & 0x3; 3271713ad546SYazen Ghannam struct mem_ctl_info *mci; 3272713ad546SYazen Ghannam struct amd64_pvt *pvt; 3273713ad546SYazen Ghannam struct err_info err; 3274713ad546SYazen Ghannam u64 sys_addr; 3275713ad546SYazen Ghannam 3276713ad546SYazen Ghannam mci = edac_mc_find(node_id); 3277713ad546SYazen Ghannam if (!mci) 3278713ad546SYazen Ghannam return; 3279713ad546SYazen Ghannam 3280713ad546SYazen Ghannam pvt = mci->pvt_info; 3281713ad546SYazen Ghannam 3282713ad546SYazen Ghannam memset(&err, 0, sizeof(err)); 3283713ad546SYazen Ghannam 3284713ad546SYazen Ghannam if (m->status & MCI_STATUS_DEFERRED) 3285713ad546SYazen Ghannam ecc_type = 3; 3286713ad546SYazen Ghannam 3287bdcee774SYazen Ghannam err.channel = find_umc_channel(m); 3288713ad546SYazen Ghannam 3289713ad546SYazen Ghannam if (!(m->status & MCI_STATUS_SYNDV)) { 3290713ad546SYazen Ghannam err.err_code = ERR_SYND; 3291713ad546SYazen Ghannam goto log_error; 3292713ad546SYazen Ghannam } 3293713ad546SYazen Ghannam 3294713ad546SYazen Ghannam if (ecc_type == 2) { 3295713ad546SYazen Ghannam u8 length = (m->synd >> 18) & 0x3f; 3296713ad546SYazen Ghannam 3297713ad546SYazen Ghannam if (length) 3298713ad546SYazen Ghannam err.syndrome = (m->synd >> 32) & GENMASK(length - 1, 0); 3299713ad546SYazen Ghannam else 3300713ad546SYazen Ghannam err.err_code = ERR_CHANNEL; 3301713ad546SYazen Ghannam } 3302713ad546SYazen Ghannam 3303713ad546SYazen Ghannam err.csrow = m->synd & 0x7; 3304713ad546SYazen Ghannam 33058a2eaab7SYazen Ghannam if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) { 33068a2eaab7SYazen Ghannam err.err_code = ERR_NORM_ADDR; 33078a2eaab7SYazen Ghannam goto log_error; 33088a2eaab7SYazen Ghannam } 33098a2eaab7SYazen Ghannam 33108a2eaab7SYazen Ghannam error_address_to_page_and_offset(sys_addr, &err); 33118a2eaab7SYazen Ghannam 3312713ad546SYazen Ghannam log_error: 3313713ad546SYazen Ghannam __log_ecc_error(mci, &err, ecc_type); 3314713ad546SYazen Ghannam } 3315713ad546SYazen Ghannam 3316713ad546SYazen Ghannam /* 33173f37a36bSBorislav Petkov * Use pvt->F3 which contains the F3 CPU PCI device to get the related 33183f37a36bSBorislav Petkov * F1 (AddrMap) and F2 (Dct) devices. Return negative value on error. 3319936fc3afSYazen Ghannam * Reserve F0 and F6 on systems with a UMC. 33200ec449eeSDoug Thompson */ 3321936fc3afSYazen Ghannam static int 3322936fc3afSYazen Ghannam reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2) 33230ec449eeSDoug Thompson { 3324936fc3afSYazen Ghannam if (pvt->umc) { 3325936fc3afSYazen Ghannam pvt->F0 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3); 3326936fc3afSYazen Ghannam if (!pvt->F0) { 33276a4afe38SYazen Ghannam edac_dbg(1, "F0 not found, device 0x%x\n", pci_id1); 3328936fc3afSYazen Ghannam return -ENODEV; 3329936fc3afSYazen Ghannam } 3330936fc3afSYazen Ghannam 3331936fc3afSYazen Ghannam pvt->F6 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3); 3332936fc3afSYazen Ghannam if (!pvt->F6) { 3333936fc3afSYazen Ghannam pci_dev_put(pvt->F0); 3334936fc3afSYazen Ghannam pvt->F0 = NULL; 3335936fc3afSYazen Ghannam 33366a4afe38SYazen Ghannam edac_dbg(1, "F6 not found: device 0x%x\n", pci_id2); 3337936fc3afSYazen Ghannam return -ENODEV; 3338936fc3afSYazen Ghannam } 33395246c540SBorislav Petkov 3340706657b1SBorislav Petkov if (!pci_ctl_dev) 3341706657b1SBorislav Petkov pci_ctl_dev = &pvt->F0->dev; 3342706657b1SBorislav Petkov 3343936fc3afSYazen Ghannam edac_dbg(1, "F0: %s\n", pci_name(pvt->F0)); 3344936fc3afSYazen Ghannam edac_dbg(1, "F3: %s\n", pci_name(pvt->F3)); 3345936fc3afSYazen Ghannam edac_dbg(1, "F6: %s\n", pci_name(pvt->F6)); 3346936fc3afSYazen Ghannam 3347936fc3afSYazen Ghannam return 0; 3348936fc3afSYazen Ghannam } 3349936fc3afSYazen Ghannam 33500ec449eeSDoug Thompson /* Reserve the ADDRESS MAP Device */ 3351936fc3afSYazen Ghannam pvt->F1 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3); 33528d5b5d9cSBorislav Petkov if (!pvt->F1) { 33536a4afe38SYazen Ghannam edac_dbg(1, "F1 not found: device 0x%x\n", pci_id1); 3354bbd0c1f6SBorislav Petkov return -ENODEV; 33550ec449eeSDoug Thompson } 33560ec449eeSDoug Thompson 33573f37a36bSBorislav Petkov /* Reserve the DCT Device */ 3358936fc3afSYazen Ghannam pvt->F2 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3); 33593f37a36bSBorislav Petkov if (!pvt->F2) { 33608d5b5d9cSBorislav Petkov pci_dev_put(pvt->F1); 33618d5b5d9cSBorislav Petkov pvt->F1 = NULL; 33620ec449eeSDoug Thompson 33636a4afe38SYazen Ghannam edac_dbg(1, "F2 not found: device 0x%x\n", pci_id2); 3364bbd0c1f6SBorislav Petkov return -ENODEV; 33650ec449eeSDoug Thompson } 3366936fc3afSYazen Ghannam 3367706657b1SBorislav Petkov if (!pci_ctl_dev) 3368706657b1SBorislav Petkov pci_ctl_dev = &pvt->F2->dev; 3369706657b1SBorislav Petkov 3370956b9ba1SJoe Perches edac_dbg(1, "F1: %s\n", pci_name(pvt->F1)); 3371956b9ba1SJoe Perches edac_dbg(1, "F2: %s\n", pci_name(pvt->F2)); 3372956b9ba1SJoe Perches edac_dbg(1, "F3: %s\n", pci_name(pvt->F3)); 33730ec449eeSDoug Thompson 33740ec449eeSDoug Thompson return 0; 33750ec449eeSDoug Thompson } 33760ec449eeSDoug Thompson 3377360b7f3cSBorislav Petkov static void free_mc_sibling_devs(struct amd64_pvt *pvt) 33780ec449eeSDoug Thompson { 3379936fc3afSYazen Ghannam if (pvt->umc) { 3380936fc3afSYazen Ghannam pci_dev_put(pvt->F0); 3381936fc3afSYazen Ghannam pci_dev_put(pvt->F6); 3382936fc3afSYazen Ghannam } else { 33838d5b5d9cSBorislav Petkov pci_dev_put(pvt->F1); 33843f37a36bSBorislav Petkov pci_dev_put(pvt->F2); 33850ec449eeSDoug Thompson } 3386936fc3afSYazen Ghannam } 33870ec449eeSDoug Thompson 3388b64ce7cdSYazen Ghannam static void determine_ecc_sym_sz(struct amd64_pvt *pvt) 3389b64ce7cdSYazen Ghannam { 3390b64ce7cdSYazen Ghannam pvt->ecc_sym_sz = 4; 3391b64ce7cdSYazen Ghannam 3392b64ce7cdSYazen Ghannam if (pvt->umc) { 3393b64ce7cdSYazen Ghannam u8 i; 3394b64ce7cdSYazen Ghannam 33954d30d2bcSYazen Ghannam for_each_umc(i) { 3396b64ce7cdSYazen Ghannam /* Check enabled channels only: */ 33977835961dSYazen Ghannam if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) { 33987835961dSYazen Ghannam if (pvt->umc[i].ecc_ctrl & BIT(9)) { 33997835961dSYazen Ghannam pvt->ecc_sym_sz = 16; 34007835961dSYazen Ghannam return; 34017835961dSYazen Ghannam } else if (pvt->umc[i].ecc_ctrl & BIT(7)) { 3402b64ce7cdSYazen Ghannam pvt->ecc_sym_sz = 8; 3403b64ce7cdSYazen Ghannam return; 3404b64ce7cdSYazen Ghannam } 34057835961dSYazen Ghannam } 34067835961dSYazen Ghannam } 34077835961dSYazen Ghannam } else if (pvt->fam >= 0x10) { 3408b64ce7cdSYazen Ghannam u32 tmp; 3409b64ce7cdSYazen Ghannam 3410b64ce7cdSYazen Ghannam amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp); 3411b64ce7cdSYazen Ghannam /* F16h has only DCT0, so no need to read dbam1. */ 3412b64ce7cdSYazen Ghannam if (pvt->fam != 0x16) 3413b64ce7cdSYazen Ghannam amd64_read_dct_pci_cfg(pvt, 1, DBAM0, &pvt->dbam1); 3414b64ce7cdSYazen Ghannam 3415b64ce7cdSYazen Ghannam /* F10h, revD and later can do x8 ECC too. */ 3416b64ce7cdSYazen Ghannam if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25)) 3417b64ce7cdSYazen Ghannam pvt->ecc_sym_sz = 8; 3418b64ce7cdSYazen Ghannam } 3419b64ce7cdSYazen Ghannam } 3420b64ce7cdSYazen Ghannam 3421b64ce7cdSYazen Ghannam /* 3422b64ce7cdSYazen Ghannam * Retrieve the hardware registers of the memory controller. 3423b64ce7cdSYazen Ghannam */ 3424b64ce7cdSYazen Ghannam static void __read_mc_regs_df(struct amd64_pvt *pvt) 3425b64ce7cdSYazen Ghannam { 3426b64ce7cdSYazen Ghannam u8 nid = pvt->mc_node_id; 3427b64ce7cdSYazen Ghannam struct amd64_umc *umc; 3428b64ce7cdSYazen Ghannam u32 i, umc_base; 3429b64ce7cdSYazen Ghannam 3430b64ce7cdSYazen Ghannam /* Read registers from each UMC */ 34314d30d2bcSYazen Ghannam for_each_umc(i) { 3432b64ce7cdSYazen Ghannam 3433b64ce7cdSYazen Ghannam umc_base = get_umc_base(i); 3434b64ce7cdSYazen Ghannam umc = &pvt->umc[i]; 3435b64ce7cdSYazen Ghannam 3436*2151c84eSYazen Ghannam amd_smn_read(nid, umc_base + get_umc_reg(UMCCH_DIMM_CFG), &umc->dimm_cfg); 343707ed82efSYazen Ghannam amd_smn_read(nid, umc_base + UMCCH_UMC_CFG, &umc->umc_cfg); 3438b64ce7cdSYazen Ghannam amd_smn_read(nid, umc_base + UMCCH_SDP_CTRL, &umc->sdp_ctrl); 3439b64ce7cdSYazen Ghannam amd_smn_read(nid, umc_base + UMCCH_ECC_CTRL, &umc->ecc_ctrl); 344007ed82efSYazen Ghannam amd_smn_read(nid, umc_base + UMCCH_UMC_CAP_HI, &umc->umc_cap_hi); 3441b64ce7cdSYazen Ghannam } 3442b64ce7cdSYazen Ghannam } 3443b64ce7cdSYazen Ghannam 34440ec449eeSDoug Thompson /* 34450ec449eeSDoug Thompson * Retrieve the hardware registers of the memory controller (this includes the 34460ec449eeSDoug Thompson * 'Address Map' and 'Misc' device regs) 34470ec449eeSDoug Thompson */ 3448360b7f3cSBorislav Petkov static void read_mc_regs(struct amd64_pvt *pvt) 34490ec449eeSDoug Thompson { 3450b64ce7cdSYazen Ghannam unsigned int range; 34510ec449eeSDoug Thompson u64 msr_val; 34520ec449eeSDoug Thompson 34530ec449eeSDoug Thompson /* 34540ec449eeSDoug Thompson * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since 3455b64ce7cdSYazen Ghannam * those are Read-As-Zero. 34560ec449eeSDoug Thompson */ 3457e97f8bb8SBorislav Petkov rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem); 3458956b9ba1SJoe Perches edac_dbg(0, " TOP_MEM: 0x%016llx\n", pvt->top_mem); 34590ec449eeSDoug Thompson 3460b64ce7cdSYazen Ghannam /* Check first whether TOP_MEM2 is enabled: */ 3461059e5c32SBrijesh Singh rdmsrl(MSR_AMD64_SYSCFG, msr_val); 3462b64ce7cdSYazen Ghannam if (msr_val & BIT(21)) { 3463e97f8bb8SBorislav Petkov rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2); 3464956b9ba1SJoe Perches edac_dbg(0, " TOP_MEM2: 0x%016llx\n", pvt->top_mem2); 3465b64ce7cdSYazen Ghannam } else { 3466956b9ba1SJoe Perches edac_dbg(0, " TOP_MEM2 disabled\n"); 3467b64ce7cdSYazen Ghannam } 3468b64ce7cdSYazen Ghannam 3469b64ce7cdSYazen Ghannam if (pvt->umc) { 3470b64ce7cdSYazen Ghannam __read_mc_regs_df(pvt); 3471b64ce7cdSYazen Ghannam amd64_read_pci_cfg(pvt->F0, DF_DHAR, &pvt->dhar); 3472b64ce7cdSYazen Ghannam 3473b64ce7cdSYazen Ghannam goto skip; 3474b64ce7cdSYazen Ghannam } 34750ec449eeSDoug Thompson 34765980bb9cSBorislav Petkov amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap); 34770ec449eeSDoug Thompson 34785a5d2371SBorislav Petkov read_dram_ctl_register(pvt); 34790ec449eeSDoug Thompson 34807f19bf75SBorislav Petkov for (range = 0; range < DRAM_RANGES; range++) { 34817f19bf75SBorislav Petkov u8 rw; 34820ec449eeSDoug Thompson 34837f19bf75SBorislav Petkov /* read settings for this DRAM range */ 34847f19bf75SBorislav Petkov read_dram_base_limit_regs(pvt, range); 3485e97f8bb8SBorislav Petkov 34867f19bf75SBorislav Petkov rw = dram_rw(pvt, range); 34877f19bf75SBorislav Petkov if (!rw) 34887f19bf75SBorislav Petkov continue; 34897f19bf75SBorislav Petkov 3490956b9ba1SJoe Perches edac_dbg(1, " DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n", 34917f19bf75SBorislav Petkov range, 34927f19bf75SBorislav Petkov get_dram_base(pvt, range), 34937f19bf75SBorislav Petkov get_dram_limit(pvt, range)); 34947f19bf75SBorislav Petkov 3495956b9ba1SJoe Perches edac_dbg(1, " IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n", 34967f19bf75SBorislav Petkov dram_intlv_en(pvt, range) ? "Enabled" : "Disabled", 34977f19bf75SBorislav Petkov (rw & 0x1) ? "R" : "-", 34987f19bf75SBorislav Petkov (rw & 0x2) ? "W" : "-", 34997f19bf75SBorislav Petkov dram_intlv_sel(pvt, range), 35007f19bf75SBorislav Petkov dram_dst_node(pvt, range)); 35010ec449eeSDoug Thompson } 35020ec449eeSDoug Thompson 3503bc21fa57SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar); 35047981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DBAM0, &pvt->dbam0); 35050ec449eeSDoug Thompson 35068d5b5d9cSBorislav Petkov amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare); 35070ec449eeSDoug Thompson 35087981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DCLR0, &pvt->dclr0); 35097981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DCHR0, &pvt->dchr0); 35100ec449eeSDoug Thompson 351178da121eSBorislav Petkov if (!dct_ganging_enabled(pvt)) { 35127981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 1, DCLR0, &pvt->dclr1); 35137981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 1, DCHR0, &pvt->dchr1); 35140ec449eeSDoug Thompson } 3515b2b0c605SBorislav Petkov 3516b64ce7cdSYazen Ghannam skip: 3517b64ce7cdSYazen Ghannam read_dct_base_mask(pvt); 3518b64ce7cdSYazen Ghannam 3519a597d2a5SAravind Gopalakrishnan determine_memory_type(pvt); 352075aeaaf2SYazen Ghannam 352175aeaaf2SYazen Ghannam if (!pvt->umc) 3522a597d2a5SAravind Gopalakrishnan edac_dbg(1, " DIMM type: %s\n", edac_mem_types[pvt->dram_type]); 3523a3b7db09SBorislav Petkov 3524b64ce7cdSYazen Ghannam determine_ecc_sym_sz(pvt); 35250ec449eeSDoug Thompson } 35260ec449eeSDoug Thompson 35270ec449eeSDoug Thompson /* 35280ec449eeSDoug Thompson * NOTE: CPU Revision Dependent code 35290ec449eeSDoug Thompson * 35300ec449eeSDoug Thompson * Input: 353111c75eadSBorislav Petkov * @csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1) 35320ec449eeSDoug Thompson * k8 private pointer to --> 35330ec449eeSDoug Thompson * DRAM Bank Address mapping register 35340ec449eeSDoug Thompson * node_id 35350ec449eeSDoug Thompson * DCL register where dual_channel_active is 35360ec449eeSDoug Thompson * 35370ec449eeSDoug Thompson * The DBAM register consists of 4 sets of 4 bits each definitions: 35380ec449eeSDoug Thompson * 35390ec449eeSDoug Thompson * Bits: CSROWs 35400ec449eeSDoug Thompson * 0-3 CSROWs 0 and 1 35410ec449eeSDoug Thompson * 4-7 CSROWs 2 and 3 35420ec449eeSDoug Thompson * 8-11 CSROWs 4 and 5 35430ec449eeSDoug Thompson * 12-15 CSROWs 6 and 7 35440ec449eeSDoug Thompson * 35450ec449eeSDoug Thompson * Values range from: 0 to 15 35460ec449eeSDoug Thompson * The meaning of the values depends on CPU revision and dual-channel state, 35470ec449eeSDoug Thompson * see relevant BKDG more info. 35480ec449eeSDoug Thompson * 35490ec449eeSDoug Thompson * The memory controller provides for total of only 8 CSROWs in its current 35500ec449eeSDoug Thompson * architecture. Each "pair" of CSROWs normally represents just one DIMM in 35510ec449eeSDoug Thompson * single channel or two (2) DIMMs in dual channel mode. 35520ec449eeSDoug Thompson * 35530ec449eeSDoug Thompson * The following code logic collapses the various tables for CSROW based on CPU 35540ec449eeSDoug Thompson * revision. 35550ec449eeSDoug Thompson * 35560ec449eeSDoug Thompson * Returns: 35570ec449eeSDoug Thompson * The number of PAGE_SIZE pages on the specified CSROW number it 35580ec449eeSDoug Thompson * encompasses 35590ec449eeSDoug Thompson * 35600ec449eeSDoug Thompson */ 3561eb77e6b8SYazen Ghannam static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr_orig) 35620ec449eeSDoug Thompson { 3563f92cae45SAshish Shenoy u32 dbam = dct ? pvt->dbam1 : pvt->dbam0; 3564eb77e6b8SYazen Ghannam int csrow_nr = csrow_nr_orig; 3565eb77e6b8SYazen Ghannam u32 cs_mode, nr_pages; 35660ec449eeSDoug Thompson 3567e53a3b26SYazen Ghannam if (!pvt->umc) { 3568eb77e6b8SYazen Ghannam csrow_nr >>= 1; 3569eb77e6b8SYazen Ghannam cs_mode = DBAM_DIMM(csrow_nr, dbam); 3570e53a3b26SYazen Ghannam } else { 3571e53a3b26SYazen Ghannam cs_mode = f17_get_cs_mode(csrow_nr >> 1, dct, pvt); 3572e53a3b26SYazen Ghannam } 35730ec449eeSDoug Thompson 3574eb77e6b8SYazen Ghannam nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, csrow_nr); 3575eb77e6b8SYazen Ghannam nr_pages <<= 20 - PAGE_SHIFT; 35760ec449eeSDoug Thompson 357710de6497SBorislav Petkov edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n", 3578eb77e6b8SYazen Ghannam csrow_nr_orig, dct, cs_mode); 357910de6497SBorislav Petkov edac_dbg(0, "nr_pages/channel: %u\n", nr_pages); 35800ec449eeSDoug Thompson 35810ec449eeSDoug Thompson return nr_pages; 35820ec449eeSDoug Thompson } 35830ec449eeSDoug Thompson 3584353a1fcbSYazen Ghannam static int init_csrows_df(struct mem_ctl_info *mci) 3585353a1fcbSYazen Ghannam { 3586353a1fcbSYazen Ghannam struct amd64_pvt *pvt = mci->pvt_info; 3587353a1fcbSYazen Ghannam enum edac_type edac_mode = EDAC_NONE; 3588353a1fcbSYazen Ghannam enum dev_type dev_type = DEV_UNKNOWN; 3589353a1fcbSYazen Ghannam struct dimm_info *dimm; 3590353a1fcbSYazen Ghannam int empty = 1; 3591353a1fcbSYazen Ghannam u8 umc, cs; 3592353a1fcbSYazen Ghannam 3593353a1fcbSYazen Ghannam if (mci->edac_ctl_cap & EDAC_FLAG_S16ECD16ED) { 3594353a1fcbSYazen Ghannam edac_mode = EDAC_S16ECD16ED; 3595353a1fcbSYazen Ghannam dev_type = DEV_X16; 3596353a1fcbSYazen Ghannam } else if (mci->edac_ctl_cap & EDAC_FLAG_S8ECD8ED) { 3597353a1fcbSYazen Ghannam edac_mode = EDAC_S8ECD8ED; 3598353a1fcbSYazen Ghannam dev_type = DEV_X8; 3599353a1fcbSYazen Ghannam } else if (mci->edac_ctl_cap & EDAC_FLAG_S4ECD4ED) { 3600353a1fcbSYazen Ghannam edac_mode = EDAC_S4ECD4ED; 3601353a1fcbSYazen Ghannam dev_type = DEV_X4; 3602353a1fcbSYazen Ghannam } else if (mci->edac_ctl_cap & EDAC_FLAG_SECDED) { 3603353a1fcbSYazen Ghannam edac_mode = EDAC_SECDED; 3604353a1fcbSYazen Ghannam } 3605353a1fcbSYazen Ghannam 3606353a1fcbSYazen Ghannam for_each_umc(umc) { 3607353a1fcbSYazen Ghannam for_each_chip_select(cs, umc, pvt) { 3608353a1fcbSYazen Ghannam if (!csrow_enabled(cs, umc, pvt)) 3609353a1fcbSYazen Ghannam continue; 3610353a1fcbSYazen Ghannam 3611353a1fcbSYazen Ghannam empty = 0; 3612353a1fcbSYazen Ghannam dimm = mci->csrows[cs]->channels[umc]->dimm; 3613353a1fcbSYazen Ghannam 3614353a1fcbSYazen Ghannam edac_dbg(1, "MC node: %d, csrow: %d\n", 3615353a1fcbSYazen Ghannam pvt->mc_node_id, cs); 3616353a1fcbSYazen Ghannam 3617353a1fcbSYazen Ghannam dimm->nr_pages = get_csrow_nr_pages(pvt, umc, cs); 361875aeaaf2SYazen Ghannam dimm->mtype = pvt->umc[umc].dram_type; 3619353a1fcbSYazen Ghannam dimm->edac_mode = edac_mode; 3620353a1fcbSYazen Ghannam dimm->dtype = dev_type; 3621466503d6SYazen Ghannam dimm->grain = 64; 3622353a1fcbSYazen Ghannam } 3623353a1fcbSYazen Ghannam } 3624353a1fcbSYazen Ghannam 3625353a1fcbSYazen Ghannam return empty; 3626353a1fcbSYazen Ghannam } 3627353a1fcbSYazen Ghannam 36280ec449eeSDoug Thompson /* 36290ec449eeSDoug Thompson * Initialize the array of csrow attribute instances, based on the values 36300ec449eeSDoug Thompson * from pci config hardware registers. 36310ec449eeSDoug Thompson */ 3632360b7f3cSBorislav Petkov static int init_csrows(struct mem_ctl_info *mci) 36330ec449eeSDoug Thompson { 363410de6497SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 36352d09d8f3SYazen Ghannam enum edac_type edac_mode = EDAC_NONE; 36360ec449eeSDoug Thompson struct csrow_info *csrow; 3637de3910ebSMauro Carvalho Chehab struct dimm_info *dimm; 363810de6497SBorislav Petkov int i, j, empty = 1; 3639a895bf8bSMauro Carvalho Chehab int nr_pages = 0; 364010de6497SBorislav Petkov u32 val; 36410ec449eeSDoug Thompson 3642353a1fcbSYazen Ghannam if (pvt->umc) 3643353a1fcbSYazen Ghannam return init_csrows_df(mci); 3644353a1fcbSYazen Ghannam 3645a97fa68eSBorislav Petkov amd64_read_pci_cfg(pvt->F3, NBCFG, &val); 36460ec449eeSDoug Thompson 36472299ef71SBorislav Petkov pvt->nbcfg = val; 36480ec449eeSDoug Thompson 3649956b9ba1SJoe Perches edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n", 36502299ef71SBorislav Petkov pvt->mc_node_id, val, 3651a97fa68eSBorislav Petkov !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE)); 36520ec449eeSDoug Thompson 365310de6497SBorislav Petkov /* 365410de6497SBorislav Petkov * We iterate over DCT0 here but we look at DCT1 in parallel, if needed. 365510de6497SBorislav Petkov */ 365611c75eadSBorislav Petkov for_each_chip_select(i, 0, pvt) { 365710de6497SBorislav Petkov bool row_dct0 = !!csrow_enabled(i, 0, pvt); 365810de6497SBorislav Petkov bool row_dct1 = false; 36590ec449eeSDoug Thompson 3660a4b4bedcSBorislav Petkov if (pvt->fam != 0xf) 366110de6497SBorislav Petkov row_dct1 = !!csrow_enabled(i, 1, pvt); 366210de6497SBorislav Petkov 366310de6497SBorislav Petkov if (!row_dct0 && !row_dct1) 36640ec449eeSDoug Thompson continue; 36650ec449eeSDoug Thompson 366610de6497SBorislav Petkov csrow = mci->csrows[i]; 36670ec449eeSDoug Thompson empty = 0; 366811c75eadSBorislav Petkov 366910de6497SBorislav Petkov edac_dbg(1, "MC node: %d, csrow: %d\n", 367010de6497SBorislav Petkov pvt->mc_node_id, i); 367110de6497SBorislav Petkov 36721eef1282SMauro Carvalho Chehab if (row_dct0) { 3673d1ea71cdSBorislav Petkov nr_pages = get_csrow_nr_pages(pvt, 0, i); 36741eef1282SMauro Carvalho Chehab csrow->channels[0]->dimm->nr_pages = nr_pages; 36751eef1282SMauro Carvalho Chehab } 367610de6497SBorislav Petkov 367710de6497SBorislav Petkov /* K8 has only one DCT */ 3678a4b4bedcSBorislav Petkov if (pvt->fam != 0xf && row_dct1) { 3679d1ea71cdSBorislav Petkov int row_dct1_pages = get_csrow_nr_pages(pvt, 1, i); 36801eef1282SMauro Carvalho Chehab 36811eef1282SMauro Carvalho Chehab csrow->channels[1]->dimm->nr_pages = row_dct1_pages; 36821eef1282SMauro Carvalho Chehab nr_pages += row_dct1_pages; 36831eef1282SMauro Carvalho Chehab } 36840ec449eeSDoug Thompson 368510de6497SBorislav Petkov edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages); 36860ec449eeSDoug Thompson 36872d09d8f3SYazen Ghannam /* Determine DIMM ECC mode: */ 3688353a1fcbSYazen Ghannam if (pvt->nbcfg & NBCFG_ECC_ENABLE) { 36892d09d8f3SYazen Ghannam edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) 36902d09d8f3SYazen Ghannam ? EDAC_S4ECD4ED 36912d09d8f3SYazen Ghannam : EDAC_SECDED; 36922d09d8f3SYazen Ghannam } 3693084a4fccSMauro Carvalho Chehab 3694084a4fccSMauro Carvalho Chehab for (j = 0; j < pvt->channel_count; j++) { 3695de3910ebSMauro Carvalho Chehab dimm = csrow->channels[j]->dimm; 3696a597d2a5SAravind Gopalakrishnan dimm->mtype = pvt->dram_type; 3697de3910ebSMauro Carvalho Chehab dimm->edac_mode = edac_mode; 3698466503d6SYazen Ghannam dimm->grain = 64; 3699084a4fccSMauro Carvalho Chehab } 37000ec449eeSDoug Thompson } 37010ec449eeSDoug Thompson 37020ec449eeSDoug Thompson return empty; 37030ec449eeSDoug Thompson } 3704d27bf6faSDoug Thompson 370506724535SBorislav Petkov /* get all cores on this DCT */ 37068b84c8dfSDaniel J Blueman static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, u16 nid) 3707f9431992SDoug Thompson { 370806724535SBorislav Petkov int cpu; 3709f9431992SDoug Thompson 371006724535SBorislav Petkov for_each_online_cpu(cpu) 3711db970bd2SYazen Ghannam if (topology_die_id(cpu) == nid) 371206724535SBorislav Petkov cpumask_set_cpu(cpu, mask); 3713f9431992SDoug Thompson } 3714f9431992SDoug Thompson 3715f9431992SDoug Thompson /* check MCG_CTL on all the cpus on this node */ 3716d1ea71cdSBorislav Petkov static bool nb_mce_bank_enabled_on_node(u16 nid) 3717f9431992SDoug Thompson { 3718ba578cb3SRusty Russell cpumask_var_t mask; 371950542251SBorislav Petkov int cpu, nbe; 372006724535SBorislav Petkov bool ret = false; 3721f9431992SDoug Thompson 3722ba578cb3SRusty Russell if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) { 372324f9a7feSBorislav Petkov amd64_warn("%s: Error allocating mask\n", __func__); 372406724535SBorislav Petkov return false; 372506724535SBorislav Petkov } 372606724535SBorislav Petkov 3727ba578cb3SRusty Russell get_cpus_on_this_dct_cpumask(mask, nid); 372806724535SBorislav Petkov 3729ba578cb3SRusty Russell rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs); 3730ba578cb3SRusty Russell 3731ba578cb3SRusty Russell for_each_cpu(cpu, mask) { 373250542251SBorislav Petkov struct msr *reg = per_cpu_ptr(msrs, cpu); 37335980bb9cSBorislav Petkov nbe = reg->l & MSR_MCGCTL_NBE; 373406724535SBorislav Petkov 3735956b9ba1SJoe Perches edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n", 373650542251SBorislav Petkov cpu, reg->q, 373706724535SBorislav Petkov (nbe ? "enabled" : "disabled")); 373806724535SBorislav Petkov 373906724535SBorislav Petkov if (!nbe) 374006724535SBorislav Petkov goto out; 374106724535SBorislav Petkov } 374206724535SBorislav Petkov ret = true; 374306724535SBorislav Petkov 374406724535SBorislav Petkov out: 3745ba578cb3SRusty Russell free_cpumask_var(mask); 3746f9431992SDoug Thompson return ret; 3747f9431992SDoug Thompson } 3748f9431992SDoug Thompson 3749c7e5301aSDaniel J Blueman static int toggle_ecc_err_reporting(struct ecc_settings *s, u16 nid, bool on) 3750f6d6ae96SBorislav Petkov { 3751f6d6ae96SBorislav Petkov cpumask_var_t cmask; 375250542251SBorislav Petkov int cpu; 3753f6d6ae96SBorislav Petkov 3754f6d6ae96SBorislav Petkov if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) { 375524f9a7feSBorislav Petkov amd64_warn("%s: error allocating mask\n", __func__); 37560de27884SPan Bian return -ENOMEM; 3757f6d6ae96SBorislav Petkov } 3758f6d6ae96SBorislav Petkov 3759ae7bb7c6SBorislav Petkov get_cpus_on_this_dct_cpumask(cmask, nid); 3760f6d6ae96SBorislav Petkov 3761f6d6ae96SBorislav Petkov rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); 3762f6d6ae96SBorislav Petkov 3763f6d6ae96SBorislav Petkov for_each_cpu(cpu, cmask) { 3764f6d6ae96SBorislav Petkov 376550542251SBorislav Petkov struct msr *reg = per_cpu_ptr(msrs, cpu); 376650542251SBorislav Petkov 3767f6d6ae96SBorislav Petkov if (on) { 37685980bb9cSBorislav Petkov if (reg->l & MSR_MCGCTL_NBE) 3769ae7bb7c6SBorislav Petkov s->flags.nb_mce_enable = 1; 3770f6d6ae96SBorislav Petkov 37715980bb9cSBorislav Petkov reg->l |= MSR_MCGCTL_NBE; 3772f6d6ae96SBorislav Petkov } else { 3773f6d6ae96SBorislav Petkov /* 3774d95cf4deSBorislav Petkov * Turn off NB MCE reporting only when it was off before 3775f6d6ae96SBorislav Petkov */ 3776ae7bb7c6SBorislav Petkov if (!s->flags.nb_mce_enable) 37775980bb9cSBorislav Petkov reg->l &= ~MSR_MCGCTL_NBE; 3778f6d6ae96SBorislav Petkov } 3779f6d6ae96SBorislav Petkov } 3780f6d6ae96SBorislav Petkov wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); 3781f6d6ae96SBorislav Petkov 3782f6d6ae96SBorislav Petkov free_cpumask_var(cmask); 3783f6d6ae96SBorislav Petkov 3784f6d6ae96SBorislav Petkov return 0; 3785f6d6ae96SBorislav Petkov } 3786f6d6ae96SBorislav Petkov 3787c7e5301aSDaniel J Blueman static bool enable_ecc_error_reporting(struct ecc_settings *s, u16 nid, 37882299ef71SBorislav Petkov struct pci_dev *F3) 3789f6d6ae96SBorislav Petkov { 37902299ef71SBorislav Petkov bool ret = true; 3791c9f4f26eSBorislav Petkov u32 value, mask = 0x3; /* UECC/CECC enable */ 3792f6d6ae96SBorislav Petkov 37932299ef71SBorislav Petkov if (toggle_ecc_err_reporting(s, nid, ON)) { 37942299ef71SBorislav Petkov amd64_warn("Error enabling ECC reporting over MCGCTL!\n"); 37952299ef71SBorislav Petkov return false; 37962299ef71SBorislav Petkov } 37972299ef71SBorislav Petkov 3798c9f4f26eSBorislav Petkov amd64_read_pci_cfg(F3, NBCTL, &value); 3799f6d6ae96SBorislav Petkov 3800ae7bb7c6SBorislav Petkov s->old_nbctl = value & mask; 3801ae7bb7c6SBorislav Petkov s->nbctl_valid = true; 3802f6d6ae96SBorislav Petkov 3803f6d6ae96SBorislav Petkov value |= mask; 3804c9f4f26eSBorislav Petkov amd64_write_pci_cfg(F3, NBCTL, value); 3805f6d6ae96SBorislav Petkov 3806a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 3807f6d6ae96SBorislav Petkov 3808956b9ba1SJoe Perches edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n", 3809a97fa68eSBorislav Petkov nid, value, !!(value & NBCFG_ECC_ENABLE)); 3810f6d6ae96SBorislav Petkov 3811a97fa68eSBorislav Petkov if (!(value & NBCFG_ECC_ENABLE)) { 381224f9a7feSBorislav Petkov amd64_warn("DRAM ECC disabled on this node, enabling...\n"); 3813f6d6ae96SBorislav Petkov 3814ae7bb7c6SBorislav Petkov s->flags.nb_ecc_prev = 0; 3815d95cf4deSBorislav Petkov 3816f6d6ae96SBorislav Petkov /* Attempt to turn on DRAM ECC Enable */ 3817a97fa68eSBorislav Petkov value |= NBCFG_ECC_ENABLE; 3818a97fa68eSBorislav Petkov amd64_write_pci_cfg(F3, NBCFG, value); 3819f6d6ae96SBorislav Petkov 3820a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 3821f6d6ae96SBorislav Petkov 3822a97fa68eSBorislav Petkov if (!(value & NBCFG_ECC_ENABLE)) { 382324f9a7feSBorislav Petkov amd64_warn("Hardware rejected DRAM ECC enable," 382424f9a7feSBorislav Petkov "check memory DIMM configuration.\n"); 38252299ef71SBorislav Petkov ret = false; 3826f6d6ae96SBorislav Petkov } else { 382724f9a7feSBorislav Petkov amd64_info("Hardware accepted DRAM ECC Enable\n"); 3828f6d6ae96SBorislav Petkov } 3829d95cf4deSBorislav Petkov } else { 3830ae7bb7c6SBorislav Petkov s->flags.nb_ecc_prev = 1; 3831f6d6ae96SBorislav Petkov } 3832d95cf4deSBorislav Petkov 3833956b9ba1SJoe Perches edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n", 3834a97fa68eSBorislav Petkov nid, value, !!(value & NBCFG_ECC_ENABLE)); 3835f6d6ae96SBorislav Petkov 38362299ef71SBorislav Petkov return ret; 3837f6d6ae96SBorislav Petkov } 3838f6d6ae96SBorislav Petkov 3839c7e5301aSDaniel J Blueman static void restore_ecc_error_reporting(struct ecc_settings *s, u16 nid, 3840360b7f3cSBorislav Petkov struct pci_dev *F3) 3841f6d6ae96SBorislav Petkov { 3842c9f4f26eSBorislav Petkov u32 value, mask = 0x3; /* UECC/CECC enable */ 3843c9f4f26eSBorislav Petkov 3844ae7bb7c6SBorislav Petkov if (!s->nbctl_valid) 3845f6d6ae96SBorislav Petkov return; 3846f6d6ae96SBorislav Petkov 3847c9f4f26eSBorislav Petkov amd64_read_pci_cfg(F3, NBCTL, &value); 3848f6d6ae96SBorislav Petkov value &= ~mask; 3849ae7bb7c6SBorislav Petkov value |= s->old_nbctl; 3850f6d6ae96SBorislav Petkov 3851c9f4f26eSBorislav Petkov amd64_write_pci_cfg(F3, NBCTL, value); 3852f6d6ae96SBorislav Petkov 3853ae7bb7c6SBorislav Petkov /* restore previous BIOS DRAM ECC "off" setting we force-enabled */ 3854ae7bb7c6SBorislav Petkov if (!s->flags.nb_ecc_prev) { 3855a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 3856a97fa68eSBorislav Petkov value &= ~NBCFG_ECC_ENABLE; 3857a97fa68eSBorislav Petkov amd64_write_pci_cfg(F3, NBCFG, value); 3858d95cf4deSBorislav Petkov } 3859d95cf4deSBorislav Petkov 3860d95cf4deSBorislav Petkov /* restore the NB Enable MCGCTL bit */ 38612299ef71SBorislav Petkov if (toggle_ecc_err_reporting(s, nid, OFF)) 386224f9a7feSBorislav Petkov amd64_warn("Error restoring NB MCGCTL settings!\n"); 3863f6d6ae96SBorislav Petkov } 3864f6d6ae96SBorislav Petkov 38651c9b08baSYazen Ghannam static bool ecc_enabled(struct amd64_pvt *pvt) 3866f9431992SDoug Thompson { 38671c9b08baSYazen Ghannam u16 nid = pvt->mc_node_id; 386806724535SBorislav Petkov bool nb_mce_en = false; 3869196b79fcSYazen Ghannam u8 ecc_en = 0, i; 3870196b79fcSYazen Ghannam u32 value; 3871f9431992SDoug Thompson 3872196b79fcSYazen Ghannam if (boot_cpu_data.x86 >= 0x17) { 3873196b79fcSYazen Ghannam u8 umc_en_mask = 0, ecc_en_mask = 0; 38741c9b08baSYazen Ghannam struct amd64_umc *umc; 3875196b79fcSYazen Ghannam 38764d30d2bcSYazen Ghannam for_each_umc(i) { 38771c9b08baSYazen Ghannam umc = &pvt->umc[i]; 3878196b79fcSYazen Ghannam 3879196b79fcSYazen Ghannam /* Only check enabled UMCs. */ 38801c9b08baSYazen Ghannam if (!(umc->sdp_ctrl & UMC_SDP_INIT)) 3881196b79fcSYazen Ghannam continue; 3882196b79fcSYazen Ghannam 3883196b79fcSYazen Ghannam umc_en_mask |= BIT(i); 3884196b79fcSYazen Ghannam 38851c9b08baSYazen Ghannam if (umc->umc_cap_hi & UMC_ECC_ENABLED) 3886196b79fcSYazen Ghannam ecc_en_mask |= BIT(i); 3887196b79fcSYazen Ghannam } 3888196b79fcSYazen Ghannam 3889196b79fcSYazen Ghannam /* Check whether at least one UMC is enabled: */ 3890196b79fcSYazen Ghannam if (umc_en_mask) 3891196b79fcSYazen Ghannam ecc_en = umc_en_mask == ecc_en_mask; 389211ab1caeSYazen Ghannam else 389311ab1caeSYazen Ghannam edac_dbg(0, "Node %d: No enabled UMCs.\n", nid); 3894196b79fcSYazen Ghannam 3895196b79fcSYazen Ghannam /* Assume UMC MCA banks are enabled. */ 3896196b79fcSYazen Ghannam nb_mce_en = true; 3897196b79fcSYazen Ghannam } else { 38981c9b08baSYazen Ghannam amd64_read_pci_cfg(pvt->F3, NBCFG, &value); 3899f9431992SDoug Thompson 3900a97fa68eSBorislav Petkov ecc_en = !!(value & NBCFG_ECC_ENABLE); 3901be3468e8SBorislav Petkov 3902d1ea71cdSBorislav Petkov nb_mce_en = nb_mce_bank_enabled_on_node(nid); 390306724535SBorislav Petkov if (!nb_mce_en) 390411ab1caeSYazen Ghannam edac_dbg(0, "NB MCE bank disabled, set MSR 0x%08x[4] on node %d to enable.\n", 39052299ef71SBorislav Petkov MSR_IA32_MCG_CTL, nid); 3906196b79fcSYazen Ghannam } 3907196b79fcSYazen Ghannam 39084cbcb73bSBorislav Petkov edac_dbg(3, "Node %d: DRAM ECC %s.\n", nid, (ecc_en ? "enabled" : "disabled")); 3909be3468e8SBorislav Petkov 39107fdfee92SBorislav Petkov if (!ecc_en || !nb_mce_en) 39112299ef71SBorislav Petkov return false; 39127fdfee92SBorislav Petkov else 39132299ef71SBorislav Petkov return true; 3914f9431992SDoug Thompson } 3915f9431992SDoug Thompson 39162d09d8f3SYazen Ghannam static inline void 39172d09d8f3SYazen Ghannam f17h_determine_edac_ctl_cap(struct mem_ctl_info *mci, struct amd64_pvt *pvt) 39182d09d8f3SYazen Ghannam { 3919f8be8e56SYazen Ghannam u8 i, ecc_en = 1, cpk_en = 1, dev_x4 = 1, dev_x16 = 1; 39202d09d8f3SYazen Ghannam 39214d30d2bcSYazen Ghannam for_each_umc(i) { 39222d09d8f3SYazen Ghannam if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) { 39232d09d8f3SYazen Ghannam ecc_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_ENABLED); 39242d09d8f3SYazen Ghannam cpk_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_CHIPKILL_CAP); 3925f8be8e56SYazen Ghannam 3926f8be8e56SYazen Ghannam dev_x4 &= !!(pvt->umc[i].dimm_cfg & BIT(6)); 3927f8be8e56SYazen Ghannam dev_x16 &= !!(pvt->umc[i].dimm_cfg & BIT(7)); 39282d09d8f3SYazen Ghannam } 39292d09d8f3SYazen Ghannam } 39302d09d8f3SYazen Ghannam 39312d09d8f3SYazen Ghannam /* Set chipkill only if ECC is enabled: */ 39322d09d8f3SYazen Ghannam if (ecc_en) { 39332d09d8f3SYazen Ghannam mci->edac_ctl_cap |= EDAC_FLAG_SECDED; 39342d09d8f3SYazen Ghannam 3935f8be8e56SYazen Ghannam if (!cpk_en) 3936f8be8e56SYazen Ghannam return; 3937f8be8e56SYazen Ghannam 3938f8be8e56SYazen Ghannam if (dev_x4) 39392d09d8f3SYazen Ghannam mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED; 3940f8be8e56SYazen Ghannam else if (dev_x16) 3941f8be8e56SYazen Ghannam mci->edac_ctl_cap |= EDAC_FLAG_S16ECD16ED; 3942f8be8e56SYazen Ghannam else 3943f8be8e56SYazen Ghannam mci->edac_ctl_cap |= EDAC_FLAG_S8ECD8ED; 39442d09d8f3SYazen Ghannam } 39452d09d8f3SYazen Ghannam } 39462d09d8f3SYazen Ghannam 394738ddd4d1SYazen Ghannam static void setup_mci_misc_attrs(struct mem_ctl_info *mci) 39487d6034d3SDoug Thompson { 39497d6034d3SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 39507d6034d3SDoug Thompson 39517d6034d3SDoug Thompson mci->mtype_cap = MEM_FLAG_DDR2 | MEM_FLAG_RDDR2; 39527d6034d3SDoug Thompson mci->edac_ctl_cap = EDAC_FLAG_NONE; 39537d6034d3SDoug Thompson 39542d09d8f3SYazen Ghannam if (pvt->umc) { 39552d09d8f3SYazen Ghannam f17h_determine_edac_ctl_cap(mci, pvt); 39562d09d8f3SYazen Ghannam } else { 39575980bb9cSBorislav Petkov if (pvt->nbcap & NBCAP_SECDED) 39587d6034d3SDoug Thompson mci->edac_ctl_cap |= EDAC_FLAG_SECDED; 39597d6034d3SDoug Thompson 39605980bb9cSBorislav Petkov if (pvt->nbcap & NBCAP_CHIPKILL) 39617d6034d3SDoug Thompson mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED; 39622d09d8f3SYazen Ghannam } 39637d6034d3SDoug Thompson 3964d1ea71cdSBorislav Petkov mci->edac_cap = determine_edac_cap(pvt); 39657d6034d3SDoug Thompson mci->mod_name = EDAC_MOD_STR; 396638ddd4d1SYazen Ghannam mci->ctl_name = fam_type->ctl_name; 3967e7934b70SYazen Ghannam mci->dev_name = pci_name(pvt->F3); 39687d6034d3SDoug Thompson mci->ctl_page_to_phys = NULL; 39697d6034d3SDoug Thompson 39707d6034d3SDoug Thompson /* memory scrubber interface */ 3971d1ea71cdSBorislav Petkov mci->set_sdram_scrub_rate = set_scrub_rate; 3972d1ea71cdSBorislav Petkov mci->get_sdram_scrub_rate = get_scrub_rate; 39737d6034d3SDoug Thompson } 39747d6034d3SDoug Thompson 39750092b20dSBorislav Petkov /* 39760092b20dSBorislav Petkov * returns a pointer to the family descriptor on success, NULL otherwise. 39770092b20dSBorislav Petkov */ 3978d1ea71cdSBorislav Petkov static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt) 3979395ae783SBorislav Petkov { 398018b94f66SAravind Gopalakrishnan pvt->ext_model = boot_cpu_data.x86_model >> 4; 3981b399151cSJia Zhang pvt->stepping = boot_cpu_data.x86_stepping; 398218b94f66SAravind Gopalakrishnan pvt->model = boot_cpu_data.x86_model; 398318b94f66SAravind Gopalakrishnan pvt->fam = boot_cpu_data.x86; 398418b94f66SAravind Gopalakrishnan 398518b94f66SAravind Gopalakrishnan switch (pvt->fam) { 3986395ae783SBorislav Petkov case 0xf: 3987d1ea71cdSBorislav Petkov fam_type = &family_types[K8_CPUS]; 3988d1ea71cdSBorislav Petkov pvt->ops = &family_types[K8_CPUS].ops; 3989395ae783SBorislav Petkov break; 3990df71a053SBorislav Petkov 3991395ae783SBorislav Petkov case 0x10: 3992d1ea71cdSBorislav Petkov fam_type = &family_types[F10_CPUS]; 3993d1ea71cdSBorislav Petkov pvt->ops = &family_types[F10_CPUS].ops; 3994df71a053SBorislav Petkov break; 3995df71a053SBorislav Petkov 3996df71a053SBorislav Petkov case 0x15: 399718b94f66SAravind Gopalakrishnan if (pvt->model == 0x30) { 3998d1ea71cdSBorislav Petkov fam_type = &family_types[F15_M30H_CPUS]; 3999d1ea71cdSBorislav Petkov pvt->ops = &family_types[F15_M30H_CPUS].ops; 400018b94f66SAravind Gopalakrishnan break; 4001a597d2a5SAravind Gopalakrishnan } else if (pvt->model == 0x60) { 4002a597d2a5SAravind Gopalakrishnan fam_type = &family_types[F15_M60H_CPUS]; 4003a597d2a5SAravind Gopalakrishnan pvt->ops = &family_types[F15_M60H_CPUS].ops; 4004a597d2a5SAravind Gopalakrishnan break; 40056c13d7ffSBorislav Petkov /* Richland is only client */ 40066c13d7ffSBorislav Petkov } else if (pvt->model == 0x13) { 40076c13d7ffSBorislav Petkov return NULL; 40086c13d7ffSBorislav Petkov } else { 4009d1ea71cdSBorislav Petkov fam_type = &family_types[F15_CPUS]; 4010d1ea71cdSBorislav Petkov pvt->ops = &family_types[F15_CPUS].ops; 40116c13d7ffSBorislav Petkov } 4012395ae783SBorislav Petkov break; 4013395ae783SBorislav Petkov 401494c1acf2SAravind Gopalakrishnan case 0x16: 401585a8885bSAravind Gopalakrishnan if (pvt->model == 0x30) { 401685a8885bSAravind Gopalakrishnan fam_type = &family_types[F16_M30H_CPUS]; 401785a8885bSAravind Gopalakrishnan pvt->ops = &family_types[F16_M30H_CPUS].ops; 401885a8885bSAravind Gopalakrishnan break; 401985a8885bSAravind Gopalakrishnan } 4020d1ea71cdSBorislav Petkov fam_type = &family_types[F16_CPUS]; 4021d1ea71cdSBorislav Petkov pvt->ops = &family_types[F16_CPUS].ops; 402294c1acf2SAravind Gopalakrishnan break; 402394c1acf2SAravind Gopalakrishnan 4024f1cbbec9SYazen Ghannam case 0x17: 40258960de4aSMichael Jin if (pvt->model >= 0x10 && pvt->model <= 0x2f) { 40268960de4aSMichael Jin fam_type = &family_types[F17_M10H_CPUS]; 40278960de4aSMichael Jin pvt->ops = &family_types[F17_M10H_CPUS].ops; 40288960de4aSMichael Jin break; 40296e846239SYazen Ghannam } else if (pvt->model >= 0x30 && pvt->model <= 0x3f) { 40306e846239SYazen Ghannam fam_type = &family_types[F17_M30H_CPUS]; 40316e846239SYazen Ghannam pvt->ops = &family_types[F17_M30H_CPUS].ops; 40326e846239SYazen Ghannam break; 4033b6bea24dSAlexander Monakov } else if (pvt->model >= 0x60 && pvt->model <= 0x6f) { 4034b6bea24dSAlexander Monakov fam_type = &family_types[F17_M60H_CPUS]; 4035b6bea24dSAlexander Monakov pvt->ops = &family_types[F17_M60H_CPUS].ops; 4036b6bea24dSAlexander Monakov break; 40373e443eb3SIsaac Vaughn } else if (pvt->model >= 0x70 && pvt->model <= 0x7f) { 40383e443eb3SIsaac Vaughn fam_type = &family_types[F17_M70H_CPUS]; 40393e443eb3SIsaac Vaughn pvt->ops = &family_types[F17_M70H_CPUS].ops; 40403e443eb3SIsaac Vaughn break; 40418960de4aSMichael Jin } 4042df561f66SGustavo A. R. Silva fallthrough; 4043c4a3e946SPu Wen case 0x18: 4044f1cbbec9SYazen Ghannam fam_type = &family_types[F17_CPUS]; 4045f1cbbec9SYazen Ghannam pvt->ops = &family_types[F17_CPUS].ops; 4046c4a3e946SPu Wen 4047c4a3e946SPu Wen if (pvt->fam == 0x18) 4048c4a3e946SPu Wen family_types[F17_CPUS].ctl_name = "F18h"; 4049f1cbbec9SYazen Ghannam break; 4050f1cbbec9SYazen Ghannam 40512eb61c91SYazen Ghannam case 0x19: 4052e2be5955SYazen Ghannam if (pvt->model >= 0x10 && pvt->model <= 0x1f) { 4053e2be5955SYazen Ghannam fam_type = &family_types[F19_M10H_CPUS]; 4054e2be5955SYazen Ghannam pvt->ops = &family_types[F19_M10H_CPUS].ops; 4055e2be5955SYazen Ghannam break; 4056e2be5955SYazen Ghannam } else if (pvt->model >= 0x20 && pvt->model <= 0x2f) { 4057b4210eabSYazen Ghannam fam_type = &family_types[F17_M70H_CPUS]; 4058b4210eabSYazen Ghannam pvt->ops = &family_types[F17_M70H_CPUS].ops; 4059b4210eabSYazen Ghannam fam_type->ctl_name = "F19h_M20h"; 4060b4210eabSYazen Ghannam break; 40610b8bf9cbSMarc Bevand } else if (pvt->model >= 0x50 && pvt->model <= 0x5f) { 40620b8bf9cbSMarc Bevand fam_type = &family_types[F19_M50H_CPUS]; 40630b8bf9cbSMarc Bevand pvt->ops = &family_types[F19_M50H_CPUS].ops; 40640b8bf9cbSMarc Bevand fam_type->ctl_name = "F19h_M50h"; 40650b8bf9cbSMarc Bevand break; 4066e2be5955SYazen Ghannam } else if (pvt->model >= 0xa0 && pvt->model <= 0xaf) { 4067e2be5955SYazen Ghannam fam_type = &family_types[F19_M10H_CPUS]; 4068e2be5955SYazen Ghannam pvt->ops = &family_types[F19_M10H_CPUS].ops; 4069e2be5955SYazen Ghannam fam_type->ctl_name = "F19h_MA0h"; 4070e2be5955SYazen Ghannam break; 4071b4210eabSYazen Ghannam } 40722eb61c91SYazen Ghannam fam_type = &family_types[F19_CPUS]; 40732eb61c91SYazen Ghannam pvt->ops = &family_types[F19_CPUS].ops; 40742eb61c91SYazen Ghannam family_types[F19_CPUS].ctl_name = "F19h"; 40752eb61c91SYazen Ghannam break; 40762eb61c91SYazen Ghannam 4077395ae783SBorislav Petkov default: 407824f9a7feSBorislav Petkov amd64_err("Unsupported family!\n"); 40790092b20dSBorislav Petkov return NULL; 4080395ae783SBorislav Petkov } 40810092b20dSBorislav Petkov 40820092b20dSBorislav Petkov return fam_type; 4083395ae783SBorislav Petkov } 4084395ae783SBorislav Petkov 4085e339f1ecSTakashi Iwai static const struct attribute_group *amd64_edac_attr_groups[] = { 4086e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_DEBUG 40872a28ceefSBorislav Petkov &dbg_group, 408861810096SBorislav Petkov &inj_group, 4089e339f1ecSTakashi Iwai #endif 4090e339f1ecSTakashi Iwai NULL 4091e339f1ecSTakashi Iwai }; 4092e339f1ecSTakashi Iwai 409380355a3bSYazen Ghannam static int hw_info_get(struct amd64_pvt *pvt) 40947d6034d3SDoug Thompson { 4095936fc3afSYazen Ghannam u16 pci_id1, pci_id2; 4096f00eb5ffSColin Ian King int ret; 4097395ae783SBorislav Petkov 4098936fc3afSYazen Ghannam if (pvt->fam >= 0x17) { 40995e4c5527SYazen Ghannam pvt->umc = kcalloc(fam_type->max_mcs, sizeof(struct amd64_umc), GFP_KERNEL); 410080355a3bSYazen Ghannam if (!pvt->umc) 410180355a3bSYazen Ghannam return -ENOMEM; 4102936fc3afSYazen Ghannam 4103936fc3afSYazen Ghannam pci_id1 = fam_type->f0_id; 4104936fc3afSYazen Ghannam pci_id2 = fam_type->f6_id; 4105936fc3afSYazen Ghannam } else { 4106936fc3afSYazen Ghannam pci_id1 = fam_type->f1_id; 4107936fc3afSYazen Ghannam pci_id2 = fam_type->f2_id; 4108936fc3afSYazen Ghannam } 4109936fc3afSYazen Ghannam 411080355a3bSYazen Ghannam ret = reserve_mc_sibling_devs(pvt, pci_id1, pci_id2); 411180355a3bSYazen Ghannam if (ret) 411280355a3bSYazen Ghannam return ret; 41137d6034d3SDoug Thompson 4114360b7f3cSBorislav Petkov read_mc_regs(pvt); 41157d6034d3SDoug Thompson 411680355a3bSYazen Ghannam return 0; 411780355a3bSYazen Ghannam } 411880355a3bSYazen Ghannam 411980355a3bSYazen Ghannam static void hw_info_put(struct amd64_pvt *pvt) 412080355a3bSYazen Ghannam { 412180355a3bSYazen Ghannam if (pvt->F0 || pvt->F1) 412280355a3bSYazen Ghannam free_mc_sibling_devs(pvt); 412380355a3bSYazen Ghannam 412480355a3bSYazen Ghannam kfree(pvt->umc); 412580355a3bSYazen Ghannam } 412680355a3bSYazen Ghannam 412780355a3bSYazen Ghannam static int init_one_instance(struct amd64_pvt *pvt) 412880355a3bSYazen Ghannam { 412980355a3bSYazen Ghannam struct mem_ctl_info *mci = NULL; 413080355a3bSYazen Ghannam struct edac_mc_layer layers[2]; 413180355a3bSYazen Ghannam int ret = -EINVAL; 413280355a3bSYazen Ghannam 41337d6034d3SDoug Thompson /* 41347d6034d3SDoug Thompson * We need to determine how many memory channels there are. Then use 41357d6034d3SDoug Thompson * that information for calculating the size of the dynamic instance 4136360b7f3cSBorislav Petkov * tables in the 'mci' structure. 41377d6034d3SDoug Thompson */ 41387d6034d3SDoug Thompson pvt->channel_count = pvt->ops->early_channel_count(pvt); 41397d6034d3SDoug Thompson if (pvt->channel_count < 0) 414080355a3bSYazen Ghannam return ret; 41417d6034d3SDoug Thompson 41427d6034d3SDoug Thompson ret = -ENOMEM; 4143ab5a503cSMauro Carvalho Chehab layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 4144ab5a503cSMauro Carvalho Chehab layers[0].size = pvt->csels[0].b_cnt; 4145ab5a503cSMauro Carvalho Chehab layers[0].is_virt_csrow = true; 4146ab5a503cSMauro Carvalho Chehab layers[1].type = EDAC_MC_LAYER_CHANNEL; 4147f0a56c48SBorislav Petkov 4148f0a56c48SBorislav Petkov /* 4149f0a56c48SBorislav Petkov * Always allocate two channels since we can have setups with DIMMs on 4150f0a56c48SBorislav Petkov * only one channel. Also, this simplifies handling later for the price 4151f0a56c48SBorislav Petkov * of a couple of KBs tops. 4152f0a56c48SBorislav Petkov */ 41535e4c5527SYazen Ghannam layers[1].size = fam_type->max_mcs; 4154ab5a503cSMauro Carvalho Chehab layers[1].is_virt_csrow = false; 4155f0a56c48SBorislav Petkov 415680355a3bSYazen Ghannam mci = edac_mc_alloc(pvt->mc_node_id, ARRAY_SIZE(layers), layers, 0); 41577d6034d3SDoug Thompson if (!mci) 415880355a3bSYazen Ghannam return ret; 41597d6034d3SDoug Thompson 41607d6034d3SDoug Thompson mci->pvt_info = pvt; 41613f37a36bSBorislav Petkov mci->pdev = &pvt->F3->dev; 41627d6034d3SDoug Thompson 416338ddd4d1SYazen Ghannam setup_mci_misc_attrs(mci); 4164360b7f3cSBorislav Petkov 4165360b7f3cSBorislav Petkov if (init_csrows(mci)) 41667d6034d3SDoug Thompson mci->edac_cap = EDAC_FLAG_NONE; 41677d6034d3SDoug Thompson 41687d6034d3SDoug Thompson ret = -ENODEV; 4169e339f1ecSTakashi Iwai if (edac_mc_add_mc_with_groups(mci, amd64_edac_attr_groups)) { 4170956b9ba1SJoe Perches edac_dbg(1, "failed edac_mc_add_mc()\n"); 417180355a3bSYazen Ghannam edac_mc_free(mci); 417280355a3bSYazen Ghannam return ret; 41737d6034d3SDoug Thompson } 41747d6034d3SDoug Thompson 41757d6034d3SDoug Thompson return 0; 41767d6034d3SDoug Thompson } 41777d6034d3SDoug Thompson 4178582f94b5SYazen Ghannam static bool instance_has_memory(struct amd64_pvt *pvt) 4179582f94b5SYazen Ghannam { 4180582f94b5SYazen Ghannam bool cs_enabled = false; 4181582f94b5SYazen Ghannam int cs = 0, dct = 0; 4182582f94b5SYazen Ghannam 4183582f94b5SYazen Ghannam for (dct = 0; dct < fam_type->max_mcs; dct++) { 4184582f94b5SYazen Ghannam for_each_chip_select(cs, dct, pvt) 4185582f94b5SYazen Ghannam cs_enabled |= csrow_enabled(cs, dct, pvt); 4186582f94b5SYazen Ghannam } 4187582f94b5SYazen Ghannam 4188582f94b5SYazen Ghannam return cs_enabled; 4189582f94b5SYazen Ghannam } 4190582f94b5SYazen Ghannam 41913f37a36bSBorislav Petkov static int probe_one_instance(unsigned int nid) 41927d6034d3SDoug Thompson { 41932299ef71SBorislav Petkov struct pci_dev *F3 = node_to_amd_nb(nid)->misc; 419480355a3bSYazen Ghannam struct amd64_pvt *pvt = NULL; 4195ae7bb7c6SBorislav Petkov struct ecc_settings *s; 41963f37a36bSBorislav Petkov int ret; 4197b8cfa02fSBorislav Petkov 4198ae7bb7c6SBorislav Petkov ret = -ENOMEM; 4199ae7bb7c6SBorislav Petkov s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL); 4200ae7bb7c6SBorislav Petkov if (!s) 42012299ef71SBorislav Petkov goto err_out; 4202ae7bb7c6SBorislav Petkov 4203ae7bb7c6SBorislav Petkov ecc_stngs[nid] = s; 4204ae7bb7c6SBorislav Petkov 420580355a3bSYazen Ghannam pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL); 420680355a3bSYazen Ghannam if (!pvt) 420780355a3bSYazen Ghannam goto err_settings; 420880355a3bSYazen Ghannam 420980355a3bSYazen Ghannam pvt->mc_node_id = nid; 421080355a3bSYazen Ghannam pvt->F3 = F3; 421180355a3bSYazen Ghannam 42126c13d7ffSBorislav Petkov ret = -ENODEV; 421380355a3bSYazen Ghannam fam_type = per_family_init(pvt); 421480355a3bSYazen Ghannam if (!fam_type) 421580355a3bSYazen Ghannam goto err_enable; 421680355a3bSYazen Ghannam 421780355a3bSYazen Ghannam ret = hw_info_get(pvt); 421880355a3bSYazen Ghannam if (ret < 0) 421980355a3bSYazen Ghannam goto err_enable; 422080355a3bSYazen Ghannam 42214688c9b4SYazen Ghannam ret = 0; 4222582f94b5SYazen Ghannam if (!instance_has_memory(pvt)) { 4223582f94b5SYazen Ghannam amd64_info("Node %d: No DIMMs detected.\n", nid); 4224582f94b5SYazen Ghannam goto err_enable; 4225582f94b5SYazen Ghannam } 4226582f94b5SYazen Ghannam 4227582f94b5SYazen Ghannam if (!ecc_enabled(pvt)) { 4228582f94b5SYazen Ghannam ret = -ENODEV; 42292299ef71SBorislav Petkov 42302299ef71SBorislav Petkov if (!ecc_enable_override) 42312299ef71SBorislav Petkov goto err_enable; 42322299ef71SBorislav Petkov 4233044e7a41SYazen Ghannam if (boot_cpu_data.x86 >= 0x17) { 4234044e7a41SYazen Ghannam amd64_warn("Forcing ECC on is not recommended on newer systems. Please enable ECC in BIOS."); 4235044e7a41SYazen Ghannam goto err_enable; 4236044e7a41SYazen Ghannam } else 42372299ef71SBorislav Petkov amd64_warn("Forcing ECC on!\n"); 42382299ef71SBorislav Petkov 42392299ef71SBorislav Petkov if (!enable_ecc_error_reporting(s, nid, F3)) 42402299ef71SBorislav Petkov goto err_enable; 42412299ef71SBorislav Petkov } 42422299ef71SBorislav Petkov 424380355a3bSYazen Ghannam ret = init_one_instance(pvt); 4244360b7f3cSBorislav Petkov if (ret < 0) { 4245ae7bb7c6SBorislav Petkov amd64_err("Error probing instance: %d\n", nid); 4246044e7a41SYazen Ghannam 4247044e7a41SYazen Ghannam if (boot_cpu_data.x86 < 0x17) 4248360b7f3cSBorislav Petkov restore_ecc_error_reporting(s, nid, F3); 42492b9b2c46SYazen Ghannam 42502b9b2c46SYazen Ghannam goto err_enable; 4251360b7f3cSBorislav Petkov } 42527d6034d3SDoug Thompson 42534cbcb73bSBorislav Petkov amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name, 42544cbcb73bSBorislav Petkov (pvt->fam == 0xf ? 42554cbcb73bSBorislav Petkov (pvt->ext_model >= K8_REV_F ? "revF or later " 42564cbcb73bSBorislav Petkov : "revE or earlier ") 42574cbcb73bSBorislav Petkov : ""), pvt->mc_node_id); 42584cbcb73bSBorislav Petkov 4259582f94b5SYazen Ghannam dump_misc_regs(pvt); 4260582f94b5SYazen Ghannam 42617d6034d3SDoug Thompson return ret; 42622299ef71SBorislav Petkov 42632299ef71SBorislav Petkov err_enable: 426480355a3bSYazen Ghannam hw_info_put(pvt); 426580355a3bSYazen Ghannam kfree(pvt); 426680355a3bSYazen Ghannam 426780355a3bSYazen Ghannam err_settings: 42682299ef71SBorislav Petkov kfree(s); 42692299ef71SBorislav Petkov ecc_stngs[nid] = NULL; 42702299ef71SBorislav Petkov 42712299ef71SBorislav Petkov err_out: 42722299ef71SBorislav Petkov return ret; 42737d6034d3SDoug Thompson } 42747d6034d3SDoug Thompson 42753f37a36bSBorislav Petkov static void remove_one_instance(unsigned int nid) 42767d6034d3SDoug Thompson { 4277360b7f3cSBorislav Petkov struct pci_dev *F3 = node_to_amd_nb(nid)->misc; 4278360b7f3cSBorislav Petkov struct ecc_settings *s = ecc_stngs[nid]; 42793f37a36bSBorislav Petkov struct mem_ctl_info *mci; 42803f37a36bSBorislav Petkov struct amd64_pvt *pvt; 42817d6034d3SDoug Thompson 42827d6034d3SDoug Thompson /* Remove from EDAC CORE tracking list */ 42833f37a36bSBorislav Petkov mci = edac_mc_del_mc(&F3->dev); 42847d6034d3SDoug Thompson if (!mci) 42857d6034d3SDoug Thompson return; 42867d6034d3SDoug Thompson 42877d6034d3SDoug Thompson pvt = mci->pvt_info; 42887d6034d3SDoug Thompson 4289360b7f3cSBorislav Petkov restore_ecc_error_reporting(s, nid, F3); 42907d6034d3SDoug Thompson 4291360b7f3cSBorislav Petkov kfree(ecc_stngs[nid]); 4292360b7f3cSBorislav Petkov ecc_stngs[nid] = NULL; 4293ae7bb7c6SBorislav Petkov 42947d6034d3SDoug Thompson /* Free the EDAC CORE resources */ 42958f68ed97SBorislav Petkov mci->pvt_info = NULL; 42968f68ed97SBorislav Petkov 429780355a3bSYazen Ghannam hw_info_put(pvt); 42988f68ed97SBorislav Petkov kfree(pvt); 42997d6034d3SDoug Thompson edac_mc_free(mci); 43007d6034d3SDoug Thompson } 43017d6034d3SDoug Thompson 4302360b7f3cSBorislav Petkov static void setup_pci_device(void) 43037d6034d3SDoug Thompson { 4304d1ea71cdSBorislav Petkov if (pci_ctl) 43057d6034d3SDoug Thompson return; 43067d6034d3SDoug Thompson 4307706657b1SBorislav Petkov pci_ctl = edac_pci_create_generic_ctl(pci_ctl_dev, EDAC_MOD_STR); 4308d1ea71cdSBorislav Petkov if (!pci_ctl) { 4309d1ea71cdSBorislav Petkov pr_warn("%s(): Unable to create PCI control\n", __func__); 4310d1ea71cdSBorislav Petkov pr_warn("%s(): PCI error report via EDAC not set\n", __func__); 43117d6034d3SDoug Thompson } 43127d6034d3SDoug Thompson } 43137d6034d3SDoug Thompson 4314d6efab74SYazen Ghannam static const struct x86_cpu_id amd64_cpuids[] = { 431529842621SThomas Gleixner X86_MATCH_VENDOR_FAM(AMD, 0x0F, NULL), 431629842621SThomas Gleixner X86_MATCH_VENDOR_FAM(AMD, 0x10, NULL), 431729842621SThomas Gleixner X86_MATCH_VENDOR_FAM(AMD, 0x15, NULL), 431829842621SThomas Gleixner X86_MATCH_VENDOR_FAM(AMD, 0x16, NULL), 431929842621SThomas Gleixner X86_MATCH_VENDOR_FAM(AMD, 0x17, NULL), 432029842621SThomas Gleixner X86_MATCH_VENDOR_FAM(HYGON, 0x18, NULL), 432129842621SThomas Gleixner X86_MATCH_VENDOR_FAM(AMD, 0x19, NULL), 4322d6efab74SYazen Ghannam { } 4323d6efab74SYazen Ghannam }; 4324d6efab74SYazen Ghannam MODULE_DEVICE_TABLE(x86cpu, amd64_cpuids); 4325d6efab74SYazen Ghannam 43267d6034d3SDoug Thompson static int __init amd64_edac_init(void) 43277d6034d3SDoug Thompson { 4328301375e7SToshi Kani const char *owner; 4329360b7f3cSBorislav Petkov int err = -ENODEV; 43303f37a36bSBorislav Petkov int i; 43317d6034d3SDoug Thompson 4332301375e7SToshi Kani owner = edac_get_owner(); 4333301375e7SToshi Kani if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR))) 4334301375e7SToshi Kani return -EBUSY; 4335301375e7SToshi Kani 43361bd9900bSYazen Ghannam if (!x86_match_cpu(amd64_cpuids)) 43371bd9900bSYazen Ghannam return -ENODEV; 43381bd9900bSYazen Ghannam 43399653a5c7SHans Rosenfeld if (amd_cache_northbridges() < 0) 43401bd9900bSYazen Ghannam return -ENODEV; 43417d6034d3SDoug Thompson 43426ba92feaSBorislav Petkov opstate_init(); 43436ba92feaSBorislav Petkov 4344cc4d8860SBorislav Petkov err = -ENOMEM; 43456396bb22SKees Cook ecc_stngs = kcalloc(amd_nb_num(), sizeof(ecc_stngs[0]), GFP_KERNEL); 43462ec591acSBorislav Petkov if (!ecc_stngs) 4347a9f0fbe2SBorislav Petkov goto err_free; 4348cc4d8860SBorislav Petkov 434950542251SBorislav Petkov msrs = msrs_alloc(); 435056b34b91SBorislav Petkov if (!msrs) 4351360b7f3cSBorislav Petkov goto err_free; 435250542251SBorislav Petkov 43532287c636SYazen Ghannam for (i = 0; i < amd_nb_num(); i++) { 43542287c636SYazen Ghannam err = probe_one_instance(i); 43552287c636SYazen Ghannam if (err) { 43563f37a36bSBorislav Petkov /* unwind properly */ 43573f37a36bSBorislav Petkov while (--i >= 0) 43583f37a36bSBorislav Petkov remove_one_instance(i); 43597d6034d3SDoug Thompson 43603f37a36bSBorislav Petkov goto err_pci; 43613f37a36bSBorislav Petkov } 43622287c636SYazen Ghannam } 43637d6034d3SDoug Thompson 43644688c9b4SYazen Ghannam if (!edac_has_mcs()) { 43654688c9b4SYazen Ghannam err = -ENODEV; 43664688c9b4SYazen Ghannam goto err_pci; 43674688c9b4SYazen Ghannam } 43684688c9b4SYazen Ghannam 4369234365f5SYazen Ghannam /* register stuff with EDAC MCE */ 4370234365f5SYazen Ghannam if (boot_cpu_data.x86 >= 0x17) 4371234365f5SYazen Ghannam amd_register_ecc_decoder(decode_umc_error); 4372234365f5SYazen Ghannam else 4373234365f5SYazen Ghannam amd_register_ecc_decoder(decode_bus_error); 4374234365f5SYazen Ghannam 4375360b7f3cSBorislav Petkov setup_pci_device(); 4376f5b10c45STomasz Pala 4377f5b10c45STomasz Pala #ifdef CONFIG_X86_32 4378f5b10c45STomasz Pala amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR); 4379f5b10c45STomasz Pala #endif 4380f5b10c45STomasz Pala 4381de0336b3SBorislav Petkov printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION); 4382de0336b3SBorislav Petkov 43837d6034d3SDoug Thompson return 0; 43847d6034d3SDoug Thompson 438556b34b91SBorislav Petkov err_pci: 4386706657b1SBorislav Petkov pci_ctl_dev = NULL; 4387706657b1SBorislav Petkov 438856b34b91SBorislav Petkov msrs_free(msrs); 438956b34b91SBorislav Petkov msrs = NULL; 4390cc4d8860SBorislav Petkov 4391360b7f3cSBorislav Petkov err_free: 4392360b7f3cSBorislav Petkov kfree(ecc_stngs); 4393360b7f3cSBorislav Petkov ecc_stngs = NULL; 4394360b7f3cSBorislav Petkov 43957d6034d3SDoug Thompson return err; 43967d6034d3SDoug Thompson } 43977d6034d3SDoug Thompson 43987d6034d3SDoug Thompson static void __exit amd64_edac_exit(void) 43997d6034d3SDoug Thompson { 44003f37a36bSBorislav Petkov int i; 44013f37a36bSBorislav Petkov 4402d1ea71cdSBorislav Petkov if (pci_ctl) 4403d1ea71cdSBorislav Petkov edac_pci_release_generic_ctl(pci_ctl); 44047d6034d3SDoug Thompson 4405234365f5SYazen Ghannam /* unregister from EDAC MCE */ 4406234365f5SYazen Ghannam if (boot_cpu_data.x86 >= 0x17) 4407234365f5SYazen Ghannam amd_unregister_ecc_decoder(decode_umc_error); 4408234365f5SYazen Ghannam else 4409234365f5SYazen Ghannam amd_unregister_ecc_decoder(decode_bus_error); 4410234365f5SYazen Ghannam 44113f37a36bSBorislav Petkov for (i = 0; i < amd_nb_num(); i++) 44123f37a36bSBorislav Petkov remove_one_instance(i); 441350542251SBorislav Petkov 4414ae7bb7c6SBorislav Petkov kfree(ecc_stngs); 4415ae7bb7c6SBorislav Petkov ecc_stngs = NULL; 4416ae7bb7c6SBorislav Petkov 4417706657b1SBorislav Petkov pci_ctl_dev = NULL; 4418706657b1SBorislav Petkov 441950542251SBorislav Petkov msrs_free(msrs); 442050542251SBorislav Petkov msrs = NULL; 44217d6034d3SDoug Thompson } 44227d6034d3SDoug Thompson 44237d6034d3SDoug Thompson module_init(amd64_edac_init); 44247d6034d3SDoug Thompson module_exit(amd64_edac_exit); 44257d6034d3SDoug Thompson 44267d6034d3SDoug Thompson MODULE_LICENSE("GPL"); 44277d6034d3SDoug Thompson MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, " 44287d6034d3SDoug Thompson "Dave Peterson, Thayne Harbaugh"); 44297d6034d3SDoug Thompson MODULE_DESCRIPTION("MC support for AMD64 memory controllers - " 44307d6034d3SDoug Thompson EDAC_AMD64_VERSION); 44317d6034d3SDoug Thompson 44327d6034d3SDoug Thompson module_param(edac_op_state, int, 0444); 44337d6034d3SDoug Thompson MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); 4434