12bc65418SDoug Thompson #include "amd64_edac.h" 223ac4ae8SAndreas Herrmann #include <asm/amd_nb.h> 32bc65418SDoug Thompson 4d1ea71cdSBorislav Petkov static struct edac_pci_ctl_info *pci_ctl; 52bc65418SDoug Thompson 62bc65418SDoug Thompson static int report_gart_errors; 72bc65418SDoug Thompson module_param(report_gart_errors, int, 0644); 82bc65418SDoug Thompson 92bc65418SDoug Thompson /* 102bc65418SDoug Thompson * Set by command line parameter. If BIOS has enabled the ECC, this override is 112bc65418SDoug Thompson * cleared to prevent re-enabling the hardware by this driver. 122bc65418SDoug Thompson */ 132bc65418SDoug Thompson static int ecc_enable_override; 142bc65418SDoug Thompson module_param(ecc_enable_override, int, 0644); 152bc65418SDoug Thompson 16a29d8b8eSTejun Heo static struct msr __percpu *msrs; 1750542251SBorislav Petkov 182ec591acSBorislav Petkov /* Per-node stuff */ 19ae7bb7c6SBorislav Petkov static struct ecc_settings **ecc_stngs; 202bc65418SDoug Thompson 212bc65418SDoug Thompson /* 22b70ef010SBorislav Petkov * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing 23b70ef010SBorislav Petkov * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching- 24b70ef010SBorislav Petkov * or higher value'. 25b70ef010SBorislav Petkov * 26b70ef010SBorislav Petkov *FIXME: Produce a better mapping/linearisation. 27b70ef010SBorislav Petkov */ 28c7e5301aSDaniel J Blueman static const struct scrubrate { 2939094443SBorislav Petkov u32 scrubval; /* bit pattern for scrub rate */ 3039094443SBorislav Petkov u32 bandwidth; /* bandwidth consumed (bytes/sec) */ 3139094443SBorislav Petkov } scrubrates[] = { 32b70ef010SBorislav Petkov { 0x01, 1600000000UL}, 33b70ef010SBorislav Petkov { 0x02, 800000000UL}, 34b70ef010SBorislav Petkov { 0x03, 400000000UL}, 35b70ef010SBorislav Petkov { 0x04, 200000000UL}, 36b70ef010SBorislav Petkov { 0x05, 100000000UL}, 37b70ef010SBorislav Petkov { 0x06, 50000000UL}, 38b70ef010SBorislav Petkov { 0x07, 25000000UL}, 39b70ef010SBorislav Petkov { 0x08, 12284069UL}, 40b70ef010SBorislav Petkov { 0x09, 6274509UL}, 41b70ef010SBorislav Petkov { 0x0A, 3121951UL}, 42b70ef010SBorislav Petkov { 0x0B, 1560975UL}, 43b70ef010SBorislav Petkov { 0x0C, 781440UL}, 44b70ef010SBorislav Petkov { 0x0D, 390720UL}, 45b70ef010SBorislav Petkov { 0x0E, 195300UL}, 46b70ef010SBorislav Petkov { 0x0F, 97650UL}, 47b70ef010SBorislav Petkov { 0x10, 48854UL}, 48b70ef010SBorislav Petkov { 0x11, 24427UL}, 49b70ef010SBorislav Petkov { 0x12, 12213UL}, 50b70ef010SBorislav Petkov { 0x13, 6101UL}, 51b70ef010SBorislav Petkov { 0x14, 3051UL}, 52b70ef010SBorislav Petkov { 0x15, 1523UL}, 53b70ef010SBorislav Petkov { 0x16, 761UL}, 54b70ef010SBorislav Petkov { 0x00, 0UL}, /* scrubbing off */ 55b70ef010SBorislav Petkov }; 56b70ef010SBorislav Petkov 5766fed2d4SBorislav Petkov int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset, 58b2b0c605SBorislav Petkov u32 *val, const char *func) 59b2b0c605SBorislav Petkov { 60b2b0c605SBorislav Petkov int err = 0; 61b2b0c605SBorislav Petkov 62b2b0c605SBorislav Petkov err = pci_read_config_dword(pdev, offset, val); 63b2b0c605SBorislav Petkov if (err) 64b2b0c605SBorislav Petkov amd64_warn("%s: error reading F%dx%03x.\n", 65b2b0c605SBorislav Petkov func, PCI_FUNC(pdev->devfn), offset); 66b2b0c605SBorislav Petkov 67b2b0c605SBorislav Petkov return err; 68b2b0c605SBorislav Petkov } 69b2b0c605SBorislav Petkov 70b2b0c605SBorislav Petkov int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset, 71b2b0c605SBorislav Petkov u32 val, const char *func) 72b2b0c605SBorislav Petkov { 73b2b0c605SBorislav Petkov int err = 0; 74b2b0c605SBorislav Petkov 75b2b0c605SBorislav Petkov err = pci_write_config_dword(pdev, offset, val); 76b2b0c605SBorislav Petkov if (err) 77b2b0c605SBorislav Petkov amd64_warn("%s: error writing to F%dx%03x.\n", 78b2b0c605SBorislav Petkov func, PCI_FUNC(pdev->devfn), offset); 79b2b0c605SBorislav Petkov 80b2b0c605SBorislav Petkov return err; 81b2b0c605SBorislav Petkov } 82b2b0c605SBorislav Petkov 83b2b0c605SBorislav Petkov /* 8473ba8593SBorislav Petkov * Select DCT to which PCI cfg accesses are routed 8573ba8593SBorislav Petkov */ 8673ba8593SBorislav Petkov static void f15h_select_dct(struct amd64_pvt *pvt, u8 dct) 8773ba8593SBorislav Petkov { 8873ba8593SBorislav Petkov u32 reg = 0; 8973ba8593SBorislav Petkov 9073ba8593SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, ®); 917981a28fSAravind Gopalakrishnan reg &= (pvt->model == 0x30) ? ~3 : ~1; 9273ba8593SBorislav Petkov reg |= dct; 9373ba8593SBorislav Petkov amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg); 9473ba8593SBorislav Petkov } 9573ba8593SBorislav Petkov 967981a28fSAravind Gopalakrishnan /* 977981a28fSAravind Gopalakrishnan * 987981a28fSAravind Gopalakrishnan * Depending on the family, F2 DCT reads need special handling: 997981a28fSAravind Gopalakrishnan * 1007981a28fSAravind Gopalakrishnan * K8: has a single DCT only and no address offsets >= 0x100 1017981a28fSAravind Gopalakrishnan * 1027981a28fSAravind Gopalakrishnan * F10h: each DCT has its own set of regs 1037981a28fSAravind Gopalakrishnan * DCT0 -> F2x040.. 1047981a28fSAravind Gopalakrishnan * DCT1 -> F2x140.. 1057981a28fSAravind Gopalakrishnan * 1067981a28fSAravind Gopalakrishnan * F16h: has only 1 DCT 1077981a28fSAravind Gopalakrishnan * 1087981a28fSAravind Gopalakrishnan * F15h: we select which DCT we access using F1x10C[DctCfgSel] 1097981a28fSAravind Gopalakrishnan */ 1107981a28fSAravind Gopalakrishnan static inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct, 1117981a28fSAravind Gopalakrishnan int offset, u32 *val) 112b2b0c605SBorislav Petkov { 1137981a28fSAravind Gopalakrishnan switch (pvt->fam) { 1147981a28fSAravind Gopalakrishnan case 0xf: 1157981a28fSAravind Gopalakrishnan if (dct || offset >= 0x100) 1167981a28fSAravind Gopalakrishnan return -EINVAL; 1177981a28fSAravind Gopalakrishnan break; 118b2b0c605SBorislav Petkov 1197981a28fSAravind Gopalakrishnan case 0x10: 1207981a28fSAravind Gopalakrishnan if (dct) { 1217981a28fSAravind Gopalakrishnan /* 1227981a28fSAravind Gopalakrishnan * Note: If ganging is enabled, barring the regs 1237981a28fSAravind Gopalakrishnan * F2x[1,0]98 and F2x[1,0]9C; reads reads to F2x1xx 1247981a28fSAravind Gopalakrishnan * return 0. (cf. Section 2.8.1 F10h BKDG) 1257981a28fSAravind Gopalakrishnan */ 1267981a28fSAravind Gopalakrishnan if (dct_ganging_enabled(pvt)) 1277981a28fSAravind Gopalakrishnan return 0; 1287981a28fSAravind Gopalakrishnan 1297981a28fSAravind Gopalakrishnan offset += 0x100; 130b2b0c605SBorislav Petkov } 1317981a28fSAravind Gopalakrishnan break; 132b2b0c605SBorislav Petkov 1337981a28fSAravind Gopalakrishnan case 0x15: 1347981a28fSAravind Gopalakrishnan /* 1357981a28fSAravind Gopalakrishnan * F15h: F2x1xx addresses do not map explicitly to DCT1. 1367981a28fSAravind Gopalakrishnan * We should select which DCT we access using F1x10C[DctCfgSel] 1377981a28fSAravind Gopalakrishnan */ 1387981a28fSAravind Gopalakrishnan dct = (dct && pvt->model == 0x30) ? 3 : dct; 13973ba8593SBorislav Petkov f15h_select_dct(pvt, dct); 1407981a28fSAravind Gopalakrishnan break; 141b2b0c605SBorislav Petkov 1427981a28fSAravind Gopalakrishnan case 0x16: 1437981a28fSAravind Gopalakrishnan if (dct) 1447981a28fSAravind Gopalakrishnan return -EINVAL; 1457981a28fSAravind Gopalakrishnan break; 1467981a28fSAravind Gopalakrishnan 1477981a28fSAravind Gopalakrishnan default: 1487981a28fSAravind Gopalakrishnan break; 1497981a28fSAravind Gopalakrishnan } 1507981a28fSAravind Gopalakrishnan return amd64_read_pci_cfg(pvt->F2, offset, val); 151b2b0c605SBorislav Petkov } 152b2b0c605SBorislav Petkov 153b70ef010SBorislav Petkov /* 1542bc65418SDoug Thompson * Memory scrubber control interface. For K8, memory scrubbing is handled by 1552bc65418SDoug Thompson * hardware and can involve L2 cache, dcache as well as the main memory. With 1562bc65418SDoug Thompson * F10, this is extended to L3 cache scrubbing on CPU models sporting that 1572bc65418SDoug Thompson * functionality. 1582bc65418SDoug Thompson * 1592bc65418SDoug Thompson * This causes the "units" for the scrubbing speed to vary from 64 byte blocks 1602bc65418SDoug Thompson * (dram) over to cache lines. This is nasty, so we will use bandwidth in 1612bc65418SDoug Thompson * bytes/sec for the setting. 1622bc65418SDoug Thompson * 1632bc65418SDoug Thompson * Currently, we only do dram scrubbing. If the scrubbing is done in software on 1642bc65418SDoug Thompson * other archs, we might not have access to the caches directly. 1652bc65418SDoug Thompson */ 1662bc65418SDoug Thompson 1672bc65418SDoug Thompson /* 1682bc65418SDoug Thompson * scan the scrub rate mapping table for a close or matching bandwidth value to 1692bc65418SDoug Thompson * issue. If requested is too big, then use last maximum value found. 1702bc65418SDoug Thompson */ 171da92110dSAravind Gopalakrishnan static int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate) 1722bc65418SDoug Thompson { 1732bc65418SDoug Thompson u32 scrubval; 1742bc65418SDoug Thompson int i; 1752bc65418SDoug Thompson 1762bc65418SDoug Thompson /* 1772bc65418SDoug Thompson * map the configured rate (new_bw) to a value specific to the AMD64 1782bc65418SDoug Thompson * memory controller and apply to register. Search for the first 1792bc65418SDoug Thompson * bandwidth entry that is greater or equal than the setting requested 1802bc65418SDoug Thompson * and program that. If at last entry, turn off DRAM scrubbing. 181168bfeefSAndrew Morton * 182168bfeefSAndrew Morton * If no suitable bandwidth is found, turn off DRAM scrubbing entirely 183168bfeefSAndrew Morton * by falling back to the last element in scrubrates[]. 1842bc65418SDoug Thompson */ 185168bfeefSAndrew Morton for (i = 0; i < ARRAY_SIZE(scrubrates) - 1; i++) { 1862bc65418SDoug Thompson /* 1872bc65418SDoug Thompson * skip scrub rates which aren't recommended 1882bc65418SDoug Thompson * (see F10 BKDG, F3x58) 1892bc65418SDoug Thompson */ 190395ae783SBorislav Petkov if (scrubrates[i].scrubval < min_rate) 1912bc65418SDoug Thompson continue; 1922bc65418SDoug Thompson 1932bc65418SDoug Thompson if (scrubrates[i].bandwidth <= new_bw) 1942bc65418SDoug Thompson break; 1952bc65418SDoug Thompson } 1962bc65418SDoug Thompson 1972bc65418SDoug Thompson scrubval = scrubrates[i].scrubval; 1982bc65418SDoug Thompson 199da92110dSAravind Gopalakrishnan if (pvt->fam == 0x15 && pvt->model == 0x60) { 200da92110dSAravind Gopalakrishnan f15h_select_dct(pvt, 0); 201da92110dSAravind Gopalakrishnan pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F); 202da92110dSAravind Gopalakrishnan f15h_select_dct(pvt, 1); 203da92110dSAravind Gopalakrishnan pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F); 204da92110dSAravind Gopalakrishnan } else { 205da92110dSAravind Gopalakrishnan pci_write_bits32(pvt->F3, SCRCTRL, scrubval, 0x001F); 206da92110dSAravind Gopalakrishnan } 2072bc65418SDoug Thompson 20839094443SBorislav Petkov if (scrubval) 20939094443SBorislav Petkov return scrubrates[i].bandwidth; 21039094443SBorislav Petkov 2112bc65418SDoug Thompson return 0; 2122bc65418SDoug Thompson } 2132bc65418SDoug Thompson 214d1ea71cdSBorislav Petkov static int set_scrub_rate(struct mem_ctl_info *mci, u32 bw) 2152bc65418SDoug Thompson { 2162bc65418SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 21787b3e0e6SBorislav Petkov u32 min_scrubrate = 0x5; 2182bc65418SDoug Thompson 219a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) 22087b3e0e6SBorislav Petkov min_scrubrate = 0x0; 22187b3e0e6SBorislav Petkov 222da92110dSAravind Gopalakrishnan if (pvt->fam == 0x15) { 2233f0aba4fSBorislav Petkov /* Erratum #505 */ 224da92110dSAravind Gopalakrishnan if (pvt->model < 0x10) 22573ba8593SBorislav Petkov f15h_select_dct(pvt, 0); 22673ba8593SBorislav Petkov 227da92110dSAravind Gopalakrishnan if (pvt->model == 0x60) 228da92110dSAravind Gopalakrishnan min_scrubrate = 0x6; 229da92110dSAravind Gopalakrishnan } 230da92110dSAravind Gopalakrishnan return __set_scrub_rate(pvt, bw, min_scrubrate); 2312bc65418SDoug Thompson } 2322bc65418SDoug Thompson 233d1ea71cdSBorislav Petkov static int get_scrub_rate(struct mem_ctl_info *mci) 2342bc65418SDoug Thompson { 2352bc65418SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 2362bc65418SDoug Thompson u32 scrubval = 0; 23739094443SBorislav Petkov int i, retval = -EINVAL; 2382bc65418SDoug Thompson 239da92110dSAravind Gopalakrishnan if (pvt->fam == 0x15) { 2403f0aba4fSBorislav Petkov /* Erratum #505 */ 241da92110dSAravind Gopalakrishnan if (pvt->model < 0x10) 24273ba8593SBorislav Petkov f15h_select_dct(pvt, 0); 24373ba8593SBorislav Petkov 244da92110dSAravind Gopalakrishnan if (pvt->model == 0x60) 245da92110dSAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F2, F15H_M60H_SCRCTRL, &scrubval); 246da92110dSAravind Gopalakrishnan } else 2475980bb9cSBorislav Petkov amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval); 2482bc65418SDoug Thompson 2492bc65418SDoug Thompson scrubval = scrubval & 0x001F; 2502bc65418SDoug Thompson 251926311fdSRoel Kluin for (i = 0; i < ARRAY_SIZE(scrubrates); i++) { 2522bc65418SDoug Thompson if (scrubrates[i].scrubval == scrubval) { 25339094443SBorislav Petkov retval = scrubrates[i].bandwidth; 2542bc65418SDoug Thompson break; 2552bc65418SDoug Thompson } 2562bc65418SDoug Thompson } 25739094443SBorislav Petkov return retval; 2582bc65418SDoug Thompson } 2592bc65418SDoug Thompson 2606775763aSDoug Thompson /* 2617f19bf75SBorislav Petkov * returns true if the SysAddr given by sys_addr matches the 2627f19bf75SBorislav Petkov * DRAM base/limit associated with node_id 2636775763aSDoug Thompson */ 264d1ea71cdSBorislav Petkov static bool base_limit_match(struct amd64_pvt *pvt, u64 sys_addr, u8 nid) 2656775763aSDoug Thompson { 2667f19bf75SBorislav Petkov u64 addr; 2676775763aSDoug Thompson 2686775763aSDoug Thompson /* The K8 treats this as a 40-bit value. However, bits 63-40 will be 2696775763aSDoug Thompson * all ones if the most significant implemented address bit is 1. 2706775763aSDoug Thompson * Here we discard bits 63-40. See section 3.4.2 of AMD publication 2716775763aSDoug Thompson * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1 2726775763aSDoug Thompson * Application Programming. 2736775763aSDoug Thompson */ 2746775763aSDoug Thompson addr = sys_addr & 0x000000ffffffffffull; 2756775763aSDoug Thompson 2767f19bf75SBorislav Petkov return ((addr >= get_dram_base(pvt, nid)) && 2777f19bf75SBorislav Petkov (addr <= get_dram_limit(pvt, nid))); 2786775763aSDoug Thompson } 2796775763aSDoug Thompson 2806775763aSDoug Thompson /* 2816775763aSDoug Thompson * Attempt to map a SysAddr to a node. On success, return a pointer to the 2826775763aSDoug Thompson * mem_ctl_info structure for the node that the SysAddr maps to. 2836775763aSDoug Thompson * 2846775763aSDoug Thompson * On failure, return NULL. 2856775763aSDoug Thompson */ 2866775763aSDoug Thompson static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci, 2876775763aSDoug Thompson u64 sys_addr) 2886775763aSDoug Thompson { 2896775763aSDoug Thompson struct amd64_pvt *pvt; 290c7e5301aSDaniel J Blueman u8 node_id; 2916775763aSDoug Thompson u32 intlv_en, bits; 2926775763aSDoug Thompson 2936775763aSDoug Thompson /* 2946775763aSDoug Thompson * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section 2956775763aSDoug Thompson * 3.4.4.2) registers to map the SysAddr to a node ID. 2966775763aSDoug Thompson */ 2976775763aSDoug Thompson pvt = mci->pvt_info; 2986775763aSDoug Thompson 2996775763aSDoug Thompson /* 3006775763aSDoug Thompson * The value of this field should be the same for all DRAM Base 3016775763aSDoug Thompson * registers. Therefore we arbitrarily choose to read it from the 3026775763aSDoug Thompson * register for node 0. 3036775763aSDoug Thompson */ 3047f19bf75SBorislav Petkov intlv_en = dram_intlv_en(pvt, 0); 3056775763aSDoug Thompson 3066775763aSDoug Thompson if (intlv_en == 0) { 3077f19bf75SBorislav Petkov for (node_id = 0; node_id < DRAM_RANGES; node_id++) { 308d1ea71cdSBorislav Petkov if (base_limit_match(pvt, sys_addr, node_id)) 3096775763aSDoug Thompson goto found; 3106775763aSDoug Thompson } 3118edc5445SBorislav Petkov goto err_no_match; 3128edc5445SBorislav Petkov } 3136775763aSDoug Thompson 31472f158feSBorislav Petkov if (unlikely((intlv_en != 0x01) && 31572f158feSBorislav Petkov (intlv_en != 0x03) && 31672f158feSBorislav Petkov (intlv_en != 0x07))) { 31724f9a7feSBorislav Petkov amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en); 3186775763aSDoug Thompson return NULL; 3196775763aSDoug Thompson } 3206775763aSDoug Thompson 3216775763aSDoug Thompson bits = (((u32) sys_addr) >> 12) & intlv_en; 3226775763aSDoug Thompson 3236775763aSDoug Thompson for (node_id = 0; ; ) { 3247f19bf75SBorislav Petkov if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits) 3256775763aSDoug Thompson break; /* intlv_sel field matches */ 3266775763aSDoug Thompson 3277f19bf75SBorislav Petkov if (++node_id >= DRAM_RANGES) 3286775763aSDoug Thompson goto err_no_match; 3296775763aSDoug Thompson } 3306775763aSDoug Thompson 3316775763aSDoug Thompson /* sanity test for sys_addr */ 332d1ea71cdSBorislav Petkov if (unlikely(!base_limit_match(pvt, sys_addr, node_id))) { 33324f9a7feSBorislav Petkov amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address" 33424f9a7feSBorislav Petkov "range for node %d with node interleaving enabled.\n", 3358edc5445SBorislav Petkov __func__, sys_addr, node_id); 3366775763aSDoug Thompson return NULL; 3376775763aSDoug Thompson } 3386775763aSDoug Thompson 3396775763aSDoug Thompson found: 340b487c33eSBorislav Petkov return edac_mc_find((int)node_id); 3416775763aSDoug Thompson 3426775763aSDoug Thompson err_no_match: 343956b9ba1SJoe Perches edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n", 3446775763aSDoug Thompson (unsigned long)sys_addr); 3456775763aSDoug Thompson 3466775763aSDoug Thompson return NULL; 3476775763aSDoug Thompson } 348e2ce7255SDoug Thompson 349e2ce7255SDoug Thompson /* 35011c75eadSBorislav Petkov * compute the CS base address of the @csrow on the DRAM controller @dct. 35111c75eadSBorislav Petkov * For details see F2x[5C:40] in the processor's BKDG 352e2ce7255SDoug Thompson */ 35311c75eadSBorislav Petkov static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct, 35411c75eadSBorislav Petkov u64 *base, u64 *mask) 355e2ce7255SDoug Thompson { 35611c75eadSBorislav Petkov u64 csbase, csmask, base_bits, mask_bits; 35711c75eadSBorislav Petkov u8 addr_shift; 35811c75eadSBorislav Petkov 35918b94f66SAravind Gopalakrishnan if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) { 36011c75eadSBorislav Petkov csbase = pvt->csels[dct].csbases[csrow]; 36111c75eadSBorislav Petkov csmask = pvt->csels[dct].csmasks[csrow]; 36210ef6b0dSChen, Gong base_bits = GENMASK_ULL(31, 21) | GENMASK_ULL(15, 9); 36310ef6b0dSChen, Gong mask_bits = GENMASK_ULL(29, 21) | GENMASK_ULL(15, 9); 36411c75eadSBorislav Petkov addr_shift = 4; 36594c1acf2SAravind Gopalakrishnan 36694c1acf2SAravind Gopalakrishnan /* 36718b94f66SAravind Gopalakrishnan * F16h and F15h, models 30h and later need two addr_shift values: 36818b94f66SAravind Gopalakrishnan * 8 for high and 6 for low (cf. F16h BKDG). 36994c1acf2SAravind Gopalakrishnan */ 37018b94f66SAravind Gopalakrishnan } else if (pvt->fam == 0x16 || 37118b94f66SAravind Gopalakrishnan (pvt->fam == 0x15 && pvt->model >= 0x30)) { 37294c1acf2SAravind Gopalakrishnan csbase = pvt->csels[dct].csbases[csrow]; 37394c1acf2SAravind Gopalakrishnan csmask = pvt->csels[dct].csmasks[csrow >> 1]; 37494c1acf2SAravind Gopalakrishnan 37510ef6b0dSChen, Gong *base = (csbase & GENMASK_ULL(15, 5)) << 6; 37610ef6b0dSChen, Gong *base |= (csbase & GENMASK_ULL(30, 19)) << 8; 37794c1acf2SAravind Gopalakrishnan 37894c1acf2SAravind Gopalakrishnan *mask = ~0ULL; 37994c1acf2SAravind Gopalakrishnan /* poke holes for the csmask */ 38010ef6b0dSChen, Gong *mask &= ~((GENMASK_ULL(15, 5) << 6) | 38110ef6b0dSChen, Gong (GENMASK_ULL(30, 19) << 8)); 38294c1acf2SAravind Gopalakrishnan 38310ef6b0dSChen, Gong *mask |= (csmask & GENMASK_ULL(15, 5)) << 6; 38410ef6b0dSChen, Gong *mask |= (csmask & GENMASK_ULL(30, 19)) << 8; 38594c1acf2SAravind Gopalakrishnan 38694c1acf2SAravind Gopalakrishnan return; 38711c75eadSBorislav Petkov } else { 38811c75eadSBorislav Petkov csbase = pvt->csels[dct].csbases[csrow]; 38911c75eadSBorislav Petkov csmask = pvt->csels[dct].csmasks[csrow >> 1]; 39011c75eadSBorislav Petkov addr_shift = 8; 39111c75eadSBorislav Petkov 392a4b4bedcSBorislav Petkov if (pvt->fam == 0x15) 39310ef6b0dSChen, Gong base_bits = mask_bits = 39410ef6b0dSChen, Gong GENMASK_ULL(30,19) | GENMASK_ULL(13,5); 39511c75eadSBorislav Petkov else 39610ef6b0dSChen, Gong base_bits = mask_bits = 39710ef6b0dSChen, Gong GENMASK_ULL(28,19) | GENMASK_ULL(13,5); 398e2ce7255SDoug Thompson } 399e2ce7255SDoug Thompson 40011c75eadSBorislav Petkov *base = (csbase & base_bits) << addr_shift; 401e2ce7255SDoug Thompson 40211c75eadSBorislav Petkov *mask = ~0ULL; 40311c75eadSBorislav Petkov /* poke holes for the csmask */ 40411c75eadSBorislav Petkov *mask &= ~(mask_bits << addr_shift); 40511c75eadSBorislav Petkov /* OR them in */ 40611c75eadSBorislav Petkov *mask |= (csmask & mask_bits) << addr_shift; 407e2ce7255SDoug Thompson } 408e2ce7255SDoug Thompson 40911c75eadSBorislav Petkov #define for_each_chip_select(i, dct, pvt) \ 41011c75eadSBorislav Petkov for (i = 0; i < pvt->csels[dct].b_cnt; i++) 41111c75eadSBorislav Petkov 412614ec9d8SBorislav Petkov #define chip_select_base(i, dct, pvt) \ 413614ec9d8SBorislav Petkov pvt->csels[dct].csbases[i] 414614ec9d8SBorislav Petkov 41511c75eadSBorislav Petkov #define for_each_chip_select_mask(i, dct, pvt) \ 41611c75eadSBorislav Petkov for (i = 0; i < pvt->csels[dct].m_cnt; i++) 41711c75eadSBorislav Petkov 418e2ce7255SDoug Thompson /* 419e2ce7255SDoug Thompson * @input_addr is an InputAddr associated with the node given by mci. Return the 420e2ce7255SDoug Thompson * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr). 421e2ce7255SDoug Thompson */ 422e2ce7255SDoug Thompson static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr) 423e2ce7255SDoug Thompson { 424e2ce7255SDoug Thompson struct amd64_pvt *pvt; 425e2ce7255SDoug Thompson int csrow; 426e2ce7255SDoug Thompson u64 base, mask; 427e2ce7255SDoug Thompson 428e2ce7255SDoug Thompson pvt = mci->pvt_info; 429e2ce7255SDoug Thompson 43011c75eadSBorislav Petkov for_each_chip_select(csrow, 0, pvt) { 43111c75eadSBorislav Petkov if (!csrow_enabled(csrow, 0, pvt)) 432e2ce7255SDoug Thompson continue; 433e2ce7255SDoug Thompson 43411c75eadSBorislav Petkov get_cs_base_and_mask(pvt, csrow, 0, &base, &mask); 43511c75eadSBorislav Petkov 43611c75eadSBorislav Petkov mask = ~mask; 437e2ce7255SDoug Thompson 438e2ce7255SDoug Thompson if ((input_addr & mask) == (base & mask)) { 439956b9ba1SJoe Perches edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n", 440e2ce7255SDoug Thompson (unsigned long)input_addr, csrow, 441e2ce7255SDoug Thompson pvt->mc_node_id); 442e2ce7255SDoug Thompson 443e2ce7255SDoug Thompson return csrow; 444e2ce7255SDoug Thompson } 445e2ce7255SDoug Thompson } 446956b9ba1SJoe Perches edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n", 447e2ce7255SDoug Thompson (unsigned long)input_addr, pvt->mc_node_id); 448e2ce7255SDoug Thompson 449e2ce7255SDoug Thompson return -1; 450e2ce7255SDoug Thompson } 451e2ce7255SDoug Thompson 452e2ce7255SDoug Thompson /* 453e2ce7255SDoug Thompson * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094) 454e2ce7255SDoug Thompson * for the node represented by mci. Info is passed back in *hole_base, 455e2ce7255SDoug Thompson * *hole_offset, and *hole_size. Function returns 0 if info is valid or 1 if 456e2ce7255SDoug Thompson * info is invalid. Info may be invalid for either of the following reasons: 457e2ce7255SDoug Thompson * 458e2ce7255SDoug Thompson * - The revision of the node is not E or greater. In this case, the DRAM Hole 459e2ce7255SDoug Thompson * Address Register does not exist. 460e2ce7255SDoug Thompson * 461e2ce7255SDoug Thompson * - The DramHoleValid bit is cleared in the DRAM Hole Address Register, 462e2ce7255SDoug Thompson * indicating that its contents are not valid. 463e2ce7255SDoug Thompson * 464e2ce7255SDoug Thompson * The values passed back in *hole_base, *hole_offset, and *hole_size are 465e2ce7255SDoug Thompson * complete 32-bit values despite the fact that the bitfields in the DHAR 466e2ce7255SDoug Thompson * only represent bits 31-24 of the base and offset values. 467e2ce7255SDoug Thompson */ 468e2ce7255SDoug Thompson int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base, 469e2ce7255SDoug Thompson u64 *hole_offset, u64 *hole_size) 470e2ce7255SDoug Thompson { 471e2ce7255SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 472e2ce7255SDoug Thompson 473e2ce7255SDoug Thompson /* only revE and later have the DRAM Hole Address Register */ 474a4b4bedcSBorislav Petkov if (pvt->fam == 0xf && pvt->ext_model < K8_REV_E) { 475956b9ba1SJoe Perches edac_dbg(1, " revision %d for node %d does not support DHAR\n", 476e2ce7255SDoug Thompson pvt->ext_model, pvt->mc_node_id); 477e2ce7255SDoug Thompson return 1; 478e2ce7255SDoug Thompson } 479e2ce7255SDoug Thompson 480bc21fa57SBorislav Petkov /* valid for Fam10h and above */ 481a4b4bedcSBorislav Petkov if (pvt->fam >= 0x10 && !dhar_mem_hoist_valid(pvt)) { 482956b9ba1SJoe Perches edac_dbg(1, " Dram Memory Hoisting is DISABLED on this system\n"); 483e2ce7255SDoug Thompson return 1; 484e2ce7255SDoug Thompson } 485e2ce7255SDoug Thompson 486c8e518d5SBorislav Petkov if (!dhar_valid(pvt)) { 487956b9ba1SJoe Perches edac_dbg(1, " Dram Memory Hoisting is DISABLED on this node %d\n", 488e2ce7255SDoug Thompson pvt->mc_node_id); 489e2ce7255SDoug Thompson return 1; 490e2ce7255SDoug Thompson } 491e2ce7255SDoug Thompson 492e2ce7255SDoug Thompson /* This node has Memory Hoisting */ 493e2ce7255SDoug Thompson 494e2ce7255SDoug Thompson /* +------------------+--------------------+--------------------+----- 495e2ce7255SDoug Thompson * | memory | DRAM hole | relocated | 496e2ce7255SDoug Thompson * | [0, (x - 1)] | [x, 0xffffffff] | addresses from | 497e2ce7255SDoug Thompson * | | | DRAM hole | 498e2ce7255SDoug Thompson * | | | [0x100000000, | 499e2ce7255SDoug Thompson * | | | (0x100000000+ | 500e2ce7255SDoug Thompson * | | | (0xffffffff-x))] | 501e2ce7255SDoug Thompson * +------------------+--------------------+--------------------+----- 502e2ce7255SDoug Thompson * 503e2ce7255SDoug Thompson * Above is a diagram of physical memory showing the DRAM hole and the 504e2ce7255SDoug Thompson * relocated addresses from the DRAM hole. As shown, the DRAM hole 505e2ce7255SDoug Thompson * starts at address x (the base address) and extends through address 506e2ce7255SDoug Thompson * 0xffffffff. The DRAM Hole Address Register (DHAR) relocates the 507e2ce7255SDoug Thompson * addresses in the hole so that they start at 0x100000000. 508e2ce7255SDoug Thompson */ 509e2ce7255SDoug Thompson 5101f31677eSBorislav Petkov *hole_base = dhar_base(pvt); 5111f31677eSBorislav Petkov *hole_size = (1ULL << 32) - *hole_base; 512e2ce7255SDoug Thompson 513a4b4bedcSBorislav Petkov *hole_offset = (pvt->fam > 0xf) ? f10_dhar_offset(pvt) 514a4b4bedcSBorislav Petkov : k8_dhar_offset(pvt); 515e2ce7255SDoug Thompson 516956b9ba1SJoe Perches edac_dbg(1, " DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n", 517e2ce7255SDoug Thompson pvt->mc_node_id, (unsigned long)*hole_base, 518e2ce7255SDoug Thompson (unsigned long)*hole_offset, (unsigned long)*hole_size); 519e2ce7255SDoug Thompson 520e2ce7255SDoug Thompson return 0; 521e2ce7255SDoug Thompson } 522e2ce7255SDoug Thompson EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info); 523e2ce7255SDoug Thompson 52493c2df58SDoug Thompson /* 52593c2df58SDoug Thompson * Return the DramAddr that the SysAddr given by @sys_addr maps to. It is 52693c2df58SDoug Thompson * assumed that sys_addr maps to the node given by mci. 52793c2df58SDoug Thompson * 52893c2df58SDoug Thompson * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section 52993c2df58SDoug Thompson * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a 53093c2df58SDoug Thompson * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled, 53193c2df58SDoug Thompson * then it is also involved in translating a SysAddr to a DramAddr. Sections 53293c2df58SDoug Thompson * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting. 53393c2df58SDoug Thompson * These parts of the documentation are unclear. I interpret them as follows: 53493c2df58SDoug Thompson * 53593c2df58SDoug Thompson * When node n receives a SysAddr, it processes the SysAddr as follows: 53693c2df58SDoug Thompson * 53793c2df58SDoug Thompson * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM 53893c2df58SDoug Thompson * Limit registers for node n. If the SysAddr is not within the range 53993c2df58SDoug Thompson * specified by the base and limit values, then node n ignores the Sysaddr 54093c2df58SDoug Thompson * (since it does not map to node n). Otherwise continue to step 2 below. 54193c2df58SDoug Thompson * 54293c2df58SDoug Thompson * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is 54393c2df58SDoug Thompson * disabled so skip to step 3 below. Otherwise see if the SysAddr is within 54493c2df58SDoug Thompson * the range of relocated addresses (starting at 0x100000000) from the DRAM 54593c2df58SDoug Thompson * hole. If not, skip to step 3 below. Else get the value of the 54693c2df58SDoug Thompson * DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the 54793c2df58SDoug Thompson * offset defined by this value from the SysAddr. 54893c2df58SDoug Thompson * 54993c2df58SDoug Thompson * 3. Obtain the base address for node n from the DRAMBase field of the DRAM 55093c2df58SDoug Thompson * Base register for node n. To obtain the DramAddr, subtract the base 55193c2df58SDoug Thompson * address from the SysAddr, as shown near the start of section 3.4.4 (p.70). 55293c2df58SDoug Thompson */ 55393c2df58SDoug Thompson static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr) 55493c2df58SDoug Thompson { 5557f19bf75SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 55693c2df58SDoug Thompson u64 dram_base, hole_base, hole_offset, hole_size, dram_addr; 5571f31677eSBorislav Petkov int ret; 55893c2df58SDoug Thompson 5597f19bf75SBorislav Petkov dram_base = get_dram_base(pvt, pvt->mc_node_id); 56093c2df58SDoug Thompson 56193c2df58SDoug Thompson ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset, 56293c2df58SDoug Thompson &hole_size); 56393c2df58SDoug Thompson if (!ret) { 5641f31677eSBorislav Petkov if ((sys_addr >= (1ULL << 32)) && 5651f31677eSBorislav Petkov (sys_addr < ((1ULL << 32) + hole_size))) { 56693c2df58SDoug Thompson /* use DHAR to translate SysAddr to DramAddr */ 56793c2df58SDoug Thompson dram_addr = sys_addr - hole_offset; 56893c2df58SDoug Thompson 569956b9ba1SJoe Perches edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n", 57093c2df58SDoug Thompson (unsigned long)sys_addr, 57193c2df58SDoug Thompson (unsigned long)dram_addr); 57293c2df58SDoug Thompson 57393c2df58SDoug Thompson return dram_addr; 57493c2df58SDoug Thompson } 57593c2df58SDoug Thompson } 57693c2df58SDoug Thompson 57793c2df58SDoug Thompson /* 57893c2df58SDoug Thompson * Translate the SysAddr to a DramAddr as shown near the start of 57993c2df58SDoug Thompson * section 3.4.4 (p. 70). Although sys_addr is a 64-bit value, the k8 58093c2df58SDoug Thompson * only deals with 40-bit values. Therefore we discard bits 63-40 of 58193c2df58SDoug Thompson * sys_addr below. If bit 39 of sys_addr is 1 then the bits we 58293c2df58SDoug Thompson * discard are all 1s. Otherwise the bits we discard are all 0s. See 58393c2df58SDoug Thompson * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture 58493c2df58SDoug Thompson * Programmer's Manual Volume 1 Application Programming. 58593c2df58SDoug Thompson */ 58610ef6b0dSChen, Gong dram_addr = (sys_addr & GENMASK_ULL(39, 0)) - dram_base; 58793c2df58SDoug Thompson 588956b9ba1SJoe Perches edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n", 589956b9ba1SJoe Perches (unsigned long)sys_addr, (unsigned long)dram_addr); 59093c2df58SDoug Thompson return dram_addr; 59193c2df58SDoug Thompson } 59293c2df58SDoug Thompson 59393c2df58SDoug Thompson /* 59493c2df58SDoug Thompson * @intlv_en is the value of the IntlvEn field from a DRAM Base register 59593c2df58SDoug Thompson * (section 3.4.4.1). Return the number of bits from a SysAddr that are used 59693c2df58SDoug Thompson * for node interleaving. 59793c2df58SDoug Thompson */ 59893c2df58SDoug Thompson static int num_node_interleave_bits(unsigned intlv_en) 59993c2df58SDoug Thompson { 60093c2df58SDoug Thompson static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 }; 60193c2df58SDoug Thompson int n; 60293c2df58SDoug Thompson 60393c2df58SDoug Thompson BUG_ON(intlv_en > 7); 60493c2df58SDoug Thompson n = intlv_shift_table[intlv_en]; 60593c2df58SDoug Thompson return n; 60693c2df58SDoug Thompson } 60793c2df58SDoug Thompson 60893c2df58SDoug Thompson /* Translate the DramAddr given by @dram_addr to an InputAddr. */ 60993c2df58SDoug Thompson static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr) 61093c2df58SDoug Thompson { 61193c2df58SDoug Thompson struct amd64_pvt *pvt; 61293c2df58SDoug Thompson int intlv_shift; 61393c2df58SDoug Thompson u64 input_addr; 61493c2df58SDoug Thompson 61593c2df58SDoug Thompson pvt = mci->pvt_info; 61693c2df58SDoug Thompson 61793c2df58SDoug Thompson /* 61893c2df58SDoug Thompson * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E) 61993c2df58SDoug Thompson * concerning translating a DramAddr to an InputAddr. 62093c2df58SDoug Thompson */ 6217f19bf75SBorislav Petkov intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0)); 62210ef6b0dSChen, Gong input_addr = ((dram_addr >> intlv_shift) & GENMASK_ULL(35, 12)) + 62393c2df58SDoug Thompson (dram_addr & 0xfff); 62493c2df58SDoug Thompson 625956b9ba1SJoe Perches edac_dbg(2, " Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n", 62693c2df58SDoug Thompson intlv_shift, (unsigned long)dram_addr, 62793c2df58SDoug Thompson (unsigned long)input_addr); 62893c2df58SDoug Thompson 62993c2df58SDoug Thompson return input_addr; 63093c2df58SDoug Thompson } 63193c2df58SDoug Thompson 63293c2df58SDoug Thompson /* 63393c2df58SDoug Thompson * Translate the SysAddr represented by @sys_addr to an InputAddr. It is 63493c2df58SDoug Thompson * assumed that @sys_addr maps to the node given by mci. 63593c2df58SDoug Thompson */ 63693c2df58SDoug Thompson static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr) 63793c2df58SDoug Thompson { 63893c2df58SDoug Thompson u64 input_addr; 63993c2df58SDoug Thompson 64093c2df58SDoug Thompson input_addr = 64193c2df58SDoug Thompson dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr)); 64293c2df58SDoug Thompson 643c19ca6cbSMasanari Iida edac_dbg(2, "SysAddr 0x%lx translates to InputAddr 0x%lx\n", 64493c2df58SDoug Thompson (unsigned long)sys_addr, (unsigned long)input_addr); 64593c2df58SDoug Thompson 64693c2df58SDoug Thompson return input_addr; 64793c2df58SDoug Thompson } 64893c2df58SDoug Thompson 64993c2df58SDoug Thompson /* Map the Error address to a PAGE and PAGE OFFSET. */ 65093c2df58SDoug Thompson static inline void error_address_to_page_and_offset(u64 error_address, 65133ca0643SBorislav Petkov struct err_info *err) 65293c2df58SDoug Thompson { 65333ca0643SBorislav Petkov err->page = (u32) (error_address >> PAGE_SHIFT); 65433ca0643SBorislav Petkov err->offset = ((u32) error_address) & ~PAGE_MASK; 65593c2df58SDoug Thompson } 65693c2df58SDoug Thompson 65793c2df58SDoug Thompson /* 65893c2df58SDoug Thompson * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address 65993c2df58SDoug Thompson * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers 66093c2df58SDoug Thompson * of a node that detected an ECC memory error. mci represents the node that 66193c2df58SDoug Thompson * the error address maps to (possibly different from the node that detected 66293c2df58SDoug Thompson * the error). Return the number of the csrow that sys_addr maps to, or -1 on 66393c2df58SDoug Thompson * error. 66493c2df58SDoug Thompson */ 66593c2df58SDoug Thompson static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr) 66693c2df58SDoug Thompson { 66793c2df58SDoug Thompson int csrow; 66893c2df58SDoug Thompson 66993c2df58SDoug Thompson csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr)); 67093c2df58SDoug Thompson 67193c2df58SDoug Thompson if (csrow == -1) 67224f9a7feSBorislav Petkov amd64_mc_err(mci, "Failed to translate InputAddr to csrow for " 67393c2df58SDoug Thompson "address 0x%lx\n", (unsigned long)sys_addr); 67493c2df58SDoug Thompson return csrow; 67593c2df58SDoug Thompson } 676e2ce7255SDoug Thompson 677bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16); 6782da11654SDoug Thompson 6792da11654SDoug Thompson /* 6802da11654SDoug Thompson * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs 6812da11654SDoug Thompson * are ECC capable. 6822da11654SDoug Thompson */ 683d1ea71cdSBorislav Petkov static unsigned long determine_edac_cap(struct amd64_pvt *pvt) 6842da11654SDoug Thompson { 685cb328507SBorislav Petkov u8 bit; 6861f6189edSDan Carpenter unsigned long edac_cap = EDAC_FLAG_NONE; 6872da11654SDoug Thompson 688a4b4bedcSBorislav Petkov bit = (pvt->fam > 0xf || pvt->ext_model >= K8_REV_F) 6892da11654SDoug Thompson ? 19 6902da11654SDoug Thompson : 17; 6912da11654SDoug Thompson 692584fcff4SBorislav Petkov if (pvt->dclr0 & BIT(bit)) 6932da11654SDoug Thompson edac_cap = EDAC_FLAG_SECDED; 6942da11654SDoug Thompson 6952da11654SDoug Thompson return edac_cap; 6962da11654SDoug Thompson } 6972da11654SDoug Thompson 698d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *, u8); 6992da11654SDoug Thompson 700d1ea71cdSBorislav Petkov static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan) 70168798e17SBorislav Petkov { 702956b9ba1SJoe Perches edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr); 70368798e17SBorislav Petkov 704a597d2a5SAravind Gopalakrishnan if (pvt->dram_type == MEM_LRDDR3) { 705a597d2a5SAravind Gopalakrishnan u32 dcsm = pvt->csels[chan].csmasks[0]; 706a597d2a5SAravind Gopalakrishnan /* 707a597d2a5SAravind Gopalakrishnan * It's assumed all LRDIMMs in a DCT are going to be of 708a597d2a5SAravind Gopalakrishnan * same 'type' until proven otherwise. So, use a cs 709a597d2a5SAravind Gopalakrishnan * value of '0' here to get dcsm value. 710a597d2a5SAravind Gopalakrishnan */ 711a597d2a5SAravind Gopalakrishnan edac_dbg(1, " LRDIMM %dx rank multiply\n", (dcsm & 0x3)); 712a597d2a5SAravind Gopalakrishnan } 713a597d2a5SAravind Gopalakrishnan 714a597d2a5SAravind Gopalakrishnan edac_dbg(1, "All DIMMs support ECC:%s\n", 71568798e17SBorislav Petkov (dclr & BIT(19)) ? "yes" : "no"); 71668798e17SBorislav Petkov 717a597d2a5SAravind Gopalakrishnan 718956b9ba1SJoe Perches edac_dbg(1, " PAR/ERR parity: %s\n", 71968798e17SBorislav Petkov (dclr & BIT(8)) ? "enabled" : "disabled"); 72068798e17SBorislav Petkov 721a4b4bedcSBorislav Petkov if (pvt->fam == 0x10) 722956b9ba1SJoe Perches edac_dbg(1, " DCT 128bit mode width: %s\n", 72368798e17SBorislav Petkov (dclr & BIT(11)) ? "128b" : "64b"); 72468798e17SBorislav Petkov 725956b9ba1SJoe Perches edac_dbg(1, " x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n", 72668798e17SBorislav Petkov (dclr & BIT(12)) ? "yes" : "no", 72768798e17SBorislav Petkov (dclr & BIT(13)) ? "yes" : "no", 72868798e17SBorislav Petkov (dclr & BIT(14)) ? "yes" : "no", 72968798e17SBorislav Petkov (dclr & BIT(15)) ? "yes" : "no"); 73068798e17SBorislav Petkov } 73168798e17SBorislav Petkov 7322da11654SDoug Thompson /* Display and decode various NB registers for debug purposes. */ 733b2b0c605SBorislav Petkov static void dump_misc_regs(struct amd64_pvt *pvt) 7342da11654SDoug Thompson { 735956b9ba1SJoe Perches edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap); 7362da11654SDoug Thompson 737956b9ba1SJoe Perches edac_dbg(1, " NB two channel DRAM capable: %s\n", 7385980bb9cSBorislav Petkov (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no"); 73968798e17SBorislav Petkov 740956b9ba1SJoe Perches edac_dbg(1, " ECC capable: %s, ChipKill ECC capable: %s\n", 7415980bb9cSBorislav Petkov (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no", 7425980bb9cSBorislav Petkov (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no"); 74368798e17SBorislav Petkov 744d1ea71cdSBorislav Petkov debug_dump_dramcfg_low(pvt, pvt->dclr0, 0); 7452da11654SDoug Thompson 746956b9ba1SJoe Perches edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare); 7472da11654SDoug Thompson 748956b9ba1SJoe Perches edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n", 749bc21fa57SBorislav Petkov pvt->dhar, dhar_base(pvt), 750a4b4bedcSBorislav Petkov (pvt->fam == 0xf) ? k8_dhar_offset(pvt) 751bc21fa57SBorislav Petkov : f10_dhar_offset(pvt)); 7522da11654SDoug Thompson 753956b9ba1SJoe Perches edac_dbg(1, " DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no"); 7542da11654SDoug Thompson 755d1ea71cdSBorislav Petkov debug_display_dimm_sizes(pvt, 0); 7564d796364SBorislav Petkov 7574d796364SBorislav Petkov /* everything below this point is Fam10h and above */ 758a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) 7592da11654SDoug Thompson return; 7604d796364SBorislav Petkov 761d1ea71cdSBorislav Petkov debug_display_dimm_sizes(pvt, 1); 7622da11654SDoug Thompson 763a3b7db09SBorislav Petkov amd64_info("using %s syndromes.\n", ((pvt->ecc_sym_sz == 8) ? "x8" : "x4")); 764ad6a32e9SBorislav Petkov 7658de1d91eSBorislav Petkov /* Only if NOT ganged does dclr1 have valid info */ 76668798e17SBorislav Petkov if (!dct_ganging_enabled(pvt)) 767d1ea71cdSBorislav Petkov debug_dump_dramcfg_low(pvt, pvt->dclr1, 1); 7682da11654SDoug Thompson } 7692da11654SDoug Thompson 77094be4bffSDoug Thompson /* 77118b94f66SAravind Gopalakrishnan * See BKDG, F2x[1,0][5C:40], F2[1,0][6C:60] 77294be4bffSDoug Thompson */ 77311c75eadSBorislav Petkov static void prep_chip_selects(struct amd64_pvt *pvt) 77494be4bffSDoug Thompson { 77518b94f66SAravind Gopalakrishnan if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) { 77611c75eadSBorislav Petkov pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8; 77711c75eadSBorislav Petkov pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8; 778a597d2a5SAravind Gopalakrishnan } else if (pvt->fam == 0x15 && pvt->model == 0x30) { 77918b94f66SAravind Gopalakrishnan pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4; 78018b94f66SAravind Gopalakrishnan pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2; 7819d858bb1SBorislav Petkov } else { 78211c75eadSBorislav Petkov pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8; 78311c75eadSBorislav Petkov pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4; 7849d858bb1SBorislav Petkov } 78594be4bffSDoug Thompson } 78694be4bffSDoug Thompson 78794be4bffSDoug Thompson /* 78811c75eadSBorislav Petkov * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers 78994be4bffSDoug Thompson */ 790b2b0c605SBorislav Petkov static void read_dct_base_mask(struct amd64_pvt *pvt) 79194be4bffSDoug Thompson { 79211c75eadSBorislav Petkov int cs; 79394be4bffSDoug Thompson 79411c75eadSBorislav Petkov prep_chip_selects(pvt); 79594be4bffSDoug Thompson 79611c75eadSBorislav Petkov for_each_chip_select(cs, 0, pvt) { 79771d2a32eSBorislav Petkov int reg0 = DCSB0 + (cs * 4); 79871d2a32eSBorislav Petkov int reg1 = DCSB1 + (cs * 4); 79911c75eadSBorislav Petkov u32 *base0 = &pvt->csels[0].csbases[cs]; 80011c75eadSBorislav Petkov u32 *base1 = &pvt->csels[1].csbases[cs]; 801b2b0c605SBorislav Petkov 8027981a28fSAravind Gopalakrishnan if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0)) 803956b9ba1SJoe Perches edac_dbg(0, " DCSB0[%d]=0x%08x reg: F2x%x\n", 80411c75eadSBorislav Petkov cs, *base0, reg0); 80594be4bffSDoug Thompson 8067981a28fSAravind Gopalakrishnan if (pvt->fam == 0xf) 80711c75eadSBorislav Petkov continue; 808b2b0c605SBorislav Petkov 8097981a28fSAravind Gopalakrishnan if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1)) 810956b9ba1SJoe Perches edac_dbg(0, " DCSB1[%d]=0x%08x reg: F2x%x\n", 8117981a28fSAravind Gopalakrishnan cs, *base1, (pvt->fam == 0x10) ? reg1 8127981a28fSAravind Gopalakrishnan : reg0); 81394be4bffSDoug Thompson } 81494be4bffSDoug Thompson 81511c75eadSBorislav Petkov for_each_chip_select_mask(cs, 0, pvt) { 81671d2a32eSBorislav Petkov int reg0 = DCSM0 + (cs * 4); 81771d2a32eSBorislav Petkov int reg1 = DCSM1 + (cs * 4); 81811c75eadSBorislav Petkov u32 *mask0 = &pvt->csels[0].csmasks[cs]; 81911c75eadSBorislav Petkov u32 *mask1 = &pvt->csels[1].csmasks[cs]; 820b2b0c605SBorislav Petkov 8217981a28fSAravind Gopalakrishnan if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0)) 822956b9ba1SJoe Perches edac_dbg(0, " DCSM0[%d]=0x%08x reg: F2x%x\n", 82311c75eadSBorislav Petkov cs, *mask0, reg0); 82494be4bffSDoug Thompson 8257981a28fSAravind Gopalakrishnan if (pvt->fam == 0xf) 82611c75eadSBorislav Petkov continue; 827b2b0c605SBorislav Petkov 8287981a28fSAravind Gopalakrishnan if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1)) 829956b9ba1SJoe Perches edac_dbg(0, " DCSM1[%d]=0x%08x reg: F2x%x\n", 8307981a28fSAravind Gopalakrishnan cs, *mask1, (pvt->fam == 0x10) ? reg1 8317981a28fSAravind Gopalakrishnan : reg0); 83294be4bffSDoug Thompson } 8336ba5dcdcSBorislav Petkov } 83494be4bffSDoug Thompson 835a597d2a5SAravind Gopalakrishnan static void determine_memory_type(struct amd64_pvt *pvt) 83694be4bffSDoug Thompson { 837a597d2a5SAravind Gopalakrishnan u32 dram_ctrl, dcsm; 83894be4bffSDoug Thompson 839a597d2a5SAravind Gopalakrishnan switch (pvt->fam) { 840a597d2a5SAravind Gopalakrishnan case 0xf: 841a597d2a5SAravind Gopalakrishnan if (pvt->ext_model >= K8_REV_F) 842a597d2a5SAravind Gopalakrishnan goto ddr3; 843a597d2a5SAravind Gopalakrishnan 844a597d2a5SAravind Gopalakrishnan pvt->dram_type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR; 845a597d2a5SAravind Gopalakrishnan return; 846a597d2a5SAravind Gopalakrishnan 847a597d2a5SAravind Gopalakrishnan case 0x10: 8486b4c0bdeSBorislav Petkov if (pvt->dchr0 & DDR3_MODE) 849a597d2a5SAravind Gopalakrishnan goto ddr3; 850a597d2a5SAravind Gopalakrishnan 851a597d2a5SAravind Gopalakrishnan pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2; 852a597d2a5SAravind Gopalakrishnan return; 853a597d2a5SAravind Gopalakrishnan 854a597d2a5SAravind Gopalakrishnan case 0x15: 855a597d2a5SAravind Gopalakrishnan if (pvt->model < 0x60) 856a597d2a5SAravind Gopalakrishnan goto ddr3; 857a597d2a5SAravind Gopalakrishnan 858a597d2a5SAravind Gopalakrishnan /* 859a597d2a5SAravind Gopalakrishnan * Model 0x60h needs special handling: 860a597d2a5SAravind Gopalakrishnan * 861a597d2a5SAravind Gopalakrishnan * We use a Chip Select value of '0' to obtain dcsm. 862a597d2a5SAravind Gopalakrishnan * Theoretically, it is possible to populate LRDIMMs of different 863a597d2a5SAravind Gopalakrishnan * 'Rank' value on a DCT. But this is not the common case. So, 864a597d2a5SAravind Gopalakrishnan * it's reasonable to assume all DIMMs are going to be of same 865a597d2a5SAravind Gopalakrishnan * 'type' until proven otherwise. 866a597d2a5SAravind Gopalakrishnan */ 867a597d2a5SAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DRAM_CONTROL, &dram_ctrl); 868a597d2a5SAravind Gopalakrishnan dcsm = pvt->csels[0].csmasks[0]; 869a597d2a5SAravind Gopalakrishnan 870a597d2a5SAravind Gopalakrishnan if (((dram_ctrl >> 8) & 0x7) == 0x2) 871a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_DDR4; 872a597d2a5SAravind Gopalakrishnan else if (pvt->dclr0 & BIT(16)) 873a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_DDR3; 874a597d2a5SAravind Gopalakrishnan else if (dcsm & 0x3) 875a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_LRDDR3; 8766b4c0bdeSBorislav Petkov else 877a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_RDDR3; 878a597d2a5SAravind Gopalakrishnan 879a597d2a5SAravind Gopalakrishnan return; 880a597d2a5SAravind Gopalakrishnan 881a597d2a5SAravind Gopalakrishnan case 0x16: 882a597d2a5SAravind Gopalakrishnan goto ddr3; 883a597d2a5SAravind Gopalakrishnan 884a597d2a5SAravind Gopalakrishnan default: 885a597d2a5SAravind Gopalakrishnan WARN(1, KERN_ERR "%s: Family??? 0x%x\n", __func__, pvt->fam); 886a597d2a5SAravind Gopalakrishnan pvt->dram_type = MEM_EMPTY; 88794be4bffSDoug Thompson } 888a597d2a5SAravind Gopalakrishnan return; 88994be4bffSDoug Thompson 890a597d2a5SAravind Gopalakrishnan ddr3: 891a597d2a5SAravind Gopalakrishnan pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3; 89294be4bffSDoug Thompson } 89394be4bffSDoug Thompson 894cb328507SBorislav Petkov /* Get the number of DCT channels the memory controller is using. */ 895ddff876dSDoug Thompson static int k8_early_channel_count(struct amd64_pvt *pvt) 896ddff876dSDoug Thompson { 897cb328507SBorislav Petkov int flag; 898ddff876dSDoug Thompson 8999f56da0eSBorislav Petkov if (pvt->ext_model >= K8_REV_F) 900ddff876dSDoug Thompson /* RevF (NPT) and later */ 90141d8bfabSBorislav Petkov flag = pvt->dclr0 & WIDTH_128; 9029f56da0eSBorislav Petkov else 903ddff876dSDoug Thompson /* RevE and earlier */ 904ddff876dSDoug Thompson flag = pvt->dclr0 & REVE_WIDTH_128; 905ddff876dSDoug Thompson 906ddff876dSDoug Thompson /* not used */ 907ddff876dSDoug Thompson pvt->dclr1 = 0; 908ddff876dSDoug Thompson 909ddff876dSDoug Thompson return (flag) ? 2 : 1; 910ddff876dSDoug Thompson } 911ddff876dSDoug Thompson 91270046624SBorislav Petkov /* On F10h and later ErrAddr is MC4_ADDR[47:1] */ 913a4b4bedcSBorislav Petkov static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m) 914ddff876dSDoug Thompson { 9152ec591acSBorislav Petkov u16 mce_nid = amd_get_nb_id(m->extcpu); 9162ec591acSBorislav Petkov struct mem_ctl_info *mci; 91770046624SBorislav Petkov u8 start_bit = 1; 91870046624SBorislav Petkov u8 end_bit = 47; 9192ec591acSBorislav Petkov u64 addr; 9202ec591acSBorislav Petkov 9212ec591acSBorislav Petkov mci = edac_mc_find(mce_nid); 9222ec591acSBorislav Petkov if (!mci) 9232ec591acSBorislav Petkov return 0; 9242ec591acSBorislav Petkov 9252ec591acSBorislav Petkov pvt = mci->pvt_info; 92670046624SBorislav Petkov 927a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) { 92870046624SBorislav Petkov start_bit = 3; 92970046624SBorislav Petkov end_bit = 39; 93070046624SBorislav Petkov } 93170046624SBorislav Petkov 93210ef6b0dSChen, Gong addr = m->addr & GENMASK_ULL(end_bit, start_bit); 933c1ae6830SBorislav Petkov 934c1ae6830SBorislav Petkov /* 935c1ae6830SBorislav Petkov * Erratum 637 workaround 936c1ae6830SBorislav Petkov */ 937a4b4bedcSBorislav Petkov if (pvt->fam == 0x15) { 938c1ae6830SBorislav Petkov u64 cc6_base, tmp_addr; 939c1ae6830SBorislav Petkov u32 tmp; 9408b84c8dfSDaniel J Blueman u8 intlv_en; 941c1ae6830SBorislav Petkov 94210ef6b0dSChen, Gong if ((addr & GENMASK_ULL(47, 24)) >> 24 != 0x00fdf7) 943c1ae6830SBorislav Petkov return addr; 944c1ae6830SBorislav Petkov 945c1ae6830SBorislav Petkov 946c1ae6830SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp); 947c1ae6830SBorislav Petkov intlv_en = tmp >> 21 & 0x7; 948c1ae6830SBorislav Petkov 949c1ae6830SBorislav Petkov /* add [47:27] + 3 trailing bits */ 95010ef6b0dSChen, Gong cc6_base = (tmp & GENMASK_ULL(20, 0)) << 3; 951c1ae6830SBorislav Petkov 952c1ae6830SBorislav Petkov /* reverse and add DramIntlvEn */ 953c1ae6830SBorislav Petkov cc6_base |= intlv_en ^ 0x7; 954c1ae6830SBorislav Petkov 955c1ae6830SBorislav Petkov /* pin at [47:24] */ 956c1ae6830SBorislav Petkov cc6_base <<= 24; 957c1ae6830SBorislav Petkov 958c1ae6830SBorislav Petkov if (!intlv_en) 95910ef6b0dSChen, Gong return cc6_base | (addr & GENMASK_ULL(23, 0)); 960c1ae6830SBorislav Petkov 961c1ae6830SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp); 962c1ae6830SBorislav Petkov 963c1ae6830SBorislav Petkov /* faster log2 */ 96410ef6b0dSChen, Gong tmp_addr = (addr & GENMASK_ULL(23, 12)) << __fls(intlv_en + 1); 965c1ae6830SBorislav Petkov 966c1ae6830SBorislav Petkov /* OR DramIntlvSel into bits [14:12] */ 96710ef6b0dSChen, Gong tmp_addr |= (tmp & GENMASK_ULL(23, 21)) >> 9; 968c1ae6830SBorislav Petkov 969c1ae6830SBorislav Petkov /* add remaining [11:0] bits from original MC4_ADDR */ 97010ef6b0dSChen, Gong tmp_addr |= addr & GENMASK_ULL(11, 0); 971c1ae6830SBorislav Petkov 972c1ae6830SBorislav Petkov return cc6_base | tmp_addr; 973c1ae6830SBorislav Petkov } 974c1ae6830SBorislav Petkov 975c1ae6830SBorislav Petkov return addr; 976ddff876dSDoug Thompson } 977ddff876dSDoug Thompson 978e2c0bffeSDaniel J Blueman static struct pci_dev *pci_get_related_function(unsigned int vendor, 979e2c0bffeSDaniel J Blueman unsigned int device, 980e2c0bffeSDaniel J Blueman struct pci_dev *related) 981e2c0bffeSDaniel J Blueman { 982e2c0bffeSDaniel J Blueman struct pci_dev *dev = NULL; 983e2c0bffeSDaniel J Blueman 984e2c0bffeSDaniel J Blueman while ((dev = pci_get_device(vendor, device, dev))) { 985e2c0bffeSDaniel J Blueman if (pci_domain_nr(dev->bus) == pci_domain_nr(related->bus) && 986e2c0bffeSDaniel J Blueman (dev->bus->number == related->bus->number) && 987e2c0bffeSDaniel J Blueman (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn))) 988e2c0bffeSDaniel J Blueman break; 989e2c0bffeSDaniel J Blueman } 990e2c0bffeSDaniel J Blueman 991e2c0bffeSDaniel J Blueman return dev; 992e2c0bffeSDaniel J Blueman } 993e2c0bffeSDaniel J Blueman 9947f19bf75SBorislav Petkov static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range) 995ddff876dSDoug Thompson { 996e2c0bffeSDaniel J Blueman struct amd_northbridge *nb; 99718b94f66SAravind Gopalakrishnan struct pci_dev *f1 = NULL; 99818b94f66SAravind Gopalakrishnan unsigned int pci_func; 99971d2a32eSBorislav Petkov int off = range << 3; 1000e2c0bffeSDaniel J Blueman u32 llim; 1001ddff876dSDoug Thompson 10027f19bf75SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off, &pvt->ranges[range].base.lo); 10037f19bf75SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo); 1004ddff876dSDoug Thompson 100518b94f66SAravind Gopalakrishnan if (pvt->fam == 0xf) 10067f19bf75SBorislav Petkov return; 1007ddff876dSDoug Thompson 10087f19bf75SBorislav Petkov if (!dram_rw(pvt, range)) 10097f19bf75SBorislav Petkov return; 1010ddff876dSDoug Thompson 10117f19bf75SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off, &pvt->ranges[range].base.hi); 10127f19bf75SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi); 1013f08e457cSBorislav Petkov 1014e2c0bffeSDaniel J Blueman /* F15h: factor in CC6 save area by reading dst node's limit reg */ 101518b94f66SAravind Gopalakrishnan if (pvt->fam != 0x15) 1016e2c0bffeSDaniel J Blueman return; 1017f08e457cSBorislav Petkov 1018e2c0bffeSDaniel J Blueman nb = node_to_amd_nb(dram_dst_node(pvt, range)); 1019e2c0bffeSDaniel J Blueman if (WARN_ON(!nb)) 1020e2c0bffeSDaniel J Blueman return; 1021e2c0bffeSDaniel J Blueman 1022a597d2a5SAravind Gopalakrishnan if (pvt->model == 0x60) 1023a597d2a5SAravind Gopalakrishnan pci_func = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1; 1024a597d2a5SAravind Gopalakrishnan else if (pvt->model == 0x30) 1025a597d2a5SAravind Gopalakrishnan pci_func = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1; 1026a597d2a5SAravind Gopalakrishnan else 1027a597d2a5SAravind Gopalakrishnan pci_func = PCI_DEVICE_ID_AMD_15H_NB_F1; 102818b94f66SAravind Gopalakrishnan 102918b94f66SAravind Gopalakrishnan f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc); 1030f08e457cSBorislav Petkov if (WARN_ON(!f1)) 1031f08e457cSBorislav Petkov return; 1032f08e457cSBorislav Petkov 1033f08e457cSBorislav Petkov amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim); 1034f08e457cSBorislav Petkov 103510ef6b0dSChen, Gong pvt->ranges[range].lim.lo &= GENMASK_ULL(15, 0); 1036f08e457cSBorislav Petkov 1037f08e457cSBorislav Petkov /* {[39:27],111b} */ 1038f08e457cSBorislav Petkov pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16; 1039f08e457cSBorislav Petkov 104010ef6b0dSChen, Gong pvt->ranges[range].lim.hi &= GENMASK_ULL(7, 0); 1041f08e457cSBorislav Petkov 1042f08e457cSBorislav Petkov /* [47:40] */ 1043f08e457cSBorislav Petkov pvt->ranges[range].lim.hi |= llim >> 13; 1044f08e457cSBorislav Petkov 1045f08e457cSBorislav Petkov pci_dev_put(f1); 1046f08e457cSBorislav Petkov } 1047ddff876dSDoug Thompson 1048f192c7b1SBorislav Petkov static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr, 104933ca0643SBorislav Petkov struct err_info *err) 1050ddff876dSDoug Thompson { 1051f192c7b1SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 1052ddff876dSDoug Thompson 105333ca0643SBorislav Petkov error_address_to_page_and_offset(sys_addr, err); 1054ab5a503cSMauro Carvalho Chehab 1055ab5a503cSMauro Carvalho Chehab /* 1056ab5a503cSMauro Carvalho Chehab * Find out which node the error address belongs to. This may be 1057ab5a503cSMauro Carvalho Chehab * different from the node that detected the error. 1058ab5a503cSMauro Carvalho Chehab */ 105933ca0643SBorislav Petkov err->src_mci = find_mc_by_sys_addr(mci, sys_addr); 106033ca0643SBorislav Petkov if (!err->src_mci) { 1061ab5a503cSMauro Carvalho Chehab amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n", 1062ab5a503cSMauro Carvalho Chehab (unsigned long)sys_addr); 106333ca0643SBorislav Petkov err->err_code = ERR_NODE; 1064ab5a503cSMauro Carvalho Chehab return; 1065ab5a503cSMauro Carvalho Chehab } 1066ab5a503cSMauro Carvalho Chehab 1067ab5a503cSMauro Carvalho Chehab /* Now map the sys_addr to a CSROW */ 106833ca0643SBorislav Petkov err->csrow = sys_addr_to_csrow(err->src_mci, sys_addr); 106933ca0643SBorislav Petkov if (err->csrow < 0) { 107033ca0643SBorislav Petkov err->err_code = ERR_CSROW; 1071ab5a503cSMauro Carvalho Chehab return; 1072ab5a503cSMauro Carvalho Chehab } 1073ab5a503cSMauro Carvalho Chehab 1074ddff876dSDoug Thompson /* CHIPKILL enabled */ 1075f192c7b1SBorislav Petkov if (pvt->nbcfg & NBCFG_CHIPKILL) { 107633ca0643SBorislav Petkov err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome); 107733ca0643SBorislav Petkov if (err->channel < 0) { 1078ddff876dSDoug Thompson /* 1079ddff876dSDoug Thompson * Syndrome didn't map, so we don't know which of the 1080ddff876dSDoug Thompson * 2 DIMMs is in error. So we need to ID 'both' of them 1081ddff876dSDoug Thompson * as suspect. 1082ddff876dSDoug Thompson */ 108333ca0643SBorislav Petkov amd64_mc_warn(err->src_mci, "unknown syndrome 0x%04x - " 1084ab5a503cSMauro Carvalho Chehab "possible error reporting race\n", 108533ca0643SBorislav Petkov err->syndrome); 108633ca0643SBorislav Petkov err->err_code = ERR_CHANNEL; 1087ddff876dSDoug Thompson return; 1088ddff876dSDoug Thompson } 1089ddff876dSDoug Thompson } else { 1090ddff876dSDoug Thompson /* 1091ddff876dSDoug Thompson * non-chipkill ecc mode 1092ddff876dSDoug Thompson * 1093ddff876dSDoug Thompson * The k8 documentation is unclear about how to determine the 1094ddff876dSDoug Thompson * channel number when using non-chipkill memory. This method 1095ddff876dSDoug Thompson * was obtained from email communication with someone at AMD. 1096ddff876dSDoug Thompson * (Wish the email was placed in this comment - norsk) 1097ddff876dSDoug Thompson */ 109833ca0643SBorislav Petkov err->channel = ((sys_addr & BIT(3)) != 0); 1099ddff876dSDoug Thompson } 1100ddff876dSDoug Thompson } 1101ddff876dSDoug Thompson 110241d8bfabSBorislav Petkov static int ddr2_cs_size(unsigned i, bool dct_width) 1103ddff876dSDoug Thompson { 110441d8bfabSBorislav Petkov unsigned shift = 0; 1105ddff876dSDoug Thompson 110641d8bfabSBorislav Petkov if (i <= 2) 110741d8bfabSBorislav Petkov shift = i; 110841d8bfabSBorislav Petkov else if (!(i & 0x1)) 110941d8bfabSBorislav Petkov shift = i >> 1; 11101433eb99SBorislav Petkov else 111141d8bfabSBorislav Petkov shift = (i + 1) >> 1; 1112ddff876dSDoug Thompson 111341d8bfabSBorislav Petkov return 128 << (shift + !!dct_width); 111441d8bfabSBorislav Petkov } 111541d8bfabSBorislav Petkov 111641d8bfabSBorislav Petkov static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 1117a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 111841d8bfabSBorislav Petkov { 111941d8bfabSBorislav Petkov u32 dclr = dct ? pvt->dclr1 : pvt->dclr0; 112041d8bfabSBorislav Petkov 112141d8bfabSBorislav Petkov if (pvt->ext_model >= K8_REV_F) { 112241d8bfabSBorislav Petkov WARN_ON(cs_mode > 11); 112341d8bfabSBorislav Petkov return ddr2_cs_size(cs_mode, dclr & WIDTH_128); 112441d8bfabSBorislav Petkov } 112541d8bfabSBorislav Petkov else if (pvt->ext_model >= K8_REV_D) { 112611b0a314SBorislav Petkov unsigned diff; 112741d8bfabSBorislav Petkov WARN_ON(cs_mode > 10); 112841d8bfabSBorislav Petkov 112911b0a314SBorislav Petkov /* 113011b0a314SBorislav Petkov * the below calculation, besides trying to win an obfuscated C 113111b0a314SBorislav Petkov * contest, maps cs_mode values to DIMM chip select sizes. The 113211b0a314SBorislav Petkov * mappings are: 113311b0a314SBorislav Petkov * 113411b0a314SBorislav Petkov * cs_mode CS size (mb) 113511b0a314SBorislav Petkov * ======= ============ 113611b0a314SBorislav Petkov * 0 32 113711b0a314SBorislav Petkov * 1 64 113811b0a314SBorislav Petkov * 2 128 113911b0a314SBorislav Petkov * 3 128 114011b0a314SBorislav Petkov * 4 256 114111b0a314SBorislav Petkov * 5 512 114211b0a314SBorislav Petkov * 6 256 114311b0a314SBorislav Petkov * 7 512 114411b0a314SBorislav Petkov * 8 1024 114511b0a314SBorislav Petkov * 9 1024 114611b0a314SBorislav Petkov * 10 2048 114711b0a314SBorislav Petkov * 114811b0a314SBorislav Petkov * Basically, it calculates a value with which to shift the 114911b0a314SBorislav Petkov * smallest CS size of 32MB. 115011b0a314SBorislav Petkov * 115111b0a314SBorislav Petkov * ddr[23]_cs_size have a similar purpose. 115211b0a314SBorislav Petkov */ 115311b0a314SBorislav Petkov diff = cs_mode/3 + (unsigned)(cs_mode > 5); 115411b0a314SBorislav Petkov 115511b0a314SBorislav Petkov return 32 << (cs_mode - diff); 115641d8bfabSBorislav Petkov } 115741d8bfabSBorislav Petkov else { 115841d8bfabSBorislav Petkov WARN_ON(cs_mode > 6); 115941d8bfabSBorislav Petkov return 32 << cs_mode; 116041d8bfabSBorislav Petkov } 1161ddff876dSDoug Thompson } 1162ddff876dSDoug Thompson 11631afd3c98SDoug Thompson /* 11641afd3c98SDoug Thompson * Get the number of DCT channels in use. 11651afd3c98SDoug Thompson * 11661afd3c98SDoug Thompson * Return: 11671afd3c98SDoug Thompson * number of Memory Channels in operation 11681afd3c98SDoug Thompson * Pass back: 11691afd3c98SDoug Thompson * contents of the DCL0_LOW register 11701afd3c98SDoug Thompson */ 11717d20d14dSBorislav Petkov static int f1x_early_channel_count(struct amd64_pvt *pvt) 11721afd3c98SDoug Thompson { 11736ba5dcdcSBorislav Petkov int i, j, channels = 0; 1174ddff876dSDoug Thompson 11757d20d14dSBorislav Petkov /* On F10h, if we are in 128 bit mode, then we are using 2 channels */ 1176a4b4bedcSBorislav Petkov if (pvt->fam == 0x10 && (pvt->dclr0 & WIDTH_128)) 11777d20d14dSBorislav Petkov return 2; 11781afd3c98SDoug Thompson 11791afd3c98SDoug Thompson /* 1180d16149e8SBorislav Petkov * Need to check if in unganged mode: In such, there are 2 channels, 1181d16149e8SBorislav Petkov * but they are not in 128 bit mode and thus the above 'dclr0' status 1182d16149e8SBorislav Petkov * bit will be OFF. 11831afd3c98SDoug Thompson * 11841afd3c98SDoug Thompson * Need to check DCT0[0] and DCT1[0] to see if only one of them has 11851afd3c98SDoug Thompson * their CSEnable bit on. If so, then SINGLE DIMM case. 11861afd3c98SDoug Thompson */ 1187956b9ba1SJoe Perches edac_dbg(0, "Data width is not 128 bits - need more decoding\n"); 11881afd3c98SDoug Thompson 11891afd3c98SDoug Thompson /* 11901afd3c98SDoug Thompson * Check DRAM Bank Address Mapping values for each DIMM to see if there 11911afd3c98SDoug Thompson * is more than just one DIMM present in unganged mode. Need to check 11921afd3c98SDoug Thompson * both controllers since DIMMs can be placed in either one. 11931afd3c98SDoug Thompson */ 1194525a1b20SBorislav Petkov for (i = 0; i < 2; i++) { 1195525a1b20SBorislav Petkov u32 dbam = (i ? pvt->dbam1 : pvt->dbam0); 11961afd3c98SDoug Thompson 119757a30854SWan Wei for (j = 0; j < 4; j++) { 119857a30854SWan Wei if (DBAM_DIMM(j, dbam) > 0) { 11991afd3c98SDoug Thompson channels++; 120057a30854SWan Wei break; 12011afd3c98SDoug Thompson } 120257a30854SWan Wei } 120357a30854SWan Wei } 12041afd3c98SDoug Thompson 1205d16149e8SBorislav Petkov if (channels > 2) 1206d16149e8SBorislav Petkov channels = 2; 1207d16149e8SBorislav Petkov 120824f9a7feSBorislav Petkov amd64_info("MCT channel count: %d\n", channels); 12091afd3c98SDoug Thompson 12101afd3c98SDoug Thompson return channels; 12111afd3c98SDoug Thompson } 12121afd3c98SDoug Thompson 1213f1cbbec9SYazen Ghannam static int f17_early_channel_count(struct amd64_pvt *pvt) 1214f1cbbec9SYazen Ghannam { 1215f1cbbec9SYazen Ghannam int i, channels = 0; 1216f1cbbec9SYazen Ghannam 1217f1cbbec9SYazen Ghannam /* SDP Control bit 31 (SdpInit) is clear for unused UMC channels */ 1218f1cbbec9SYazen Ghannam for (i = 0; i < NUM_UMCS; i++) 1219f1cbbec9SYazen Ghannam channels += !!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT); 1220f1cbbec9SYazen Ghannam 1221f1cbbec9SYazen Ghannam amd64_info("MCT channel count: %d\n", channels); 1222f1cbbec9SYazen Ghannam 1223f1cbbec9SYazen Ghannam return channels; 1224f1cbbec9SYazen Ghannam } 1225f1cbbec9SYazen Ghannam 122641d8bfabSBorislav Petkov static int ddr3_cs_size(unsigned i, bool dct_width) 12271afd3c98SDoug Thompson { 122841d8bfabSBorislav Petkov unsigned shift = 0; 122941d8bfabSBorislav Petkov int cs_size = 0; 123041d8bfabSBorislav Petkov 123141d8bfabSBorislav Petkov if (i == 0 || i == 3 || i == 4) 123241d8bfabSBorislav Petkov cs_size = -1; 123341d8bfabSBorislav Petkov else if (i <= 2) 123441d8bfabSBorislav Petkov shift = i; 123541d8bfabSBorislav Petkov else if (i == 12) 123641d8bfabSBorislav Petkov shift = 7; 123741d8bfabSBorislav Petkov else if (!(i & 0x1)) 123841d8bfabSBorislav Petkov shift = i >> 1; 123941d8bfabSBorislav Petkov else 124041d8bfabSBorislav Petkov shift = (i + 1) >> 1; 124141d8bfabSBorislav Petkov 124241d8bfabSBorislav Petkov if (cs_size != -1) 124341d8bfabSBorislav Petkov cs_size = (128 * (1 << !!dct_width)) << shift; 124441d8bfabSBorislav Petkov 124541d8bfabSBorislav Petkov return cs_size; 124641d8bfabSBorislav Petkov } 124741d8bfabSBorislav Petkov 1248a597d2a5SAravind Gopalakrishnan static int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply) 1249a597d2a5SAravind Gopalakrishnan { 1250a597d2a5SAravind Gopalakrishnan unsigned shift = 0; 1251a597d2a5SAravind Gopalakrishnan int cs_size = 0; 1252a597d2a5SAravind Gopalakrishnan 1253a597d2a5SAravind Gopalakrishnan if (i < 4 || i == 6) 1254a597d2a5SAravind Gopalakrishnan cs_size = -1; 1255a597d2a5SAravind Gopalakrishnan else if (i == 12) 1256a597d2a5SAravind Gopalakrishnan shift = 7; 1257a597d2a5SAravind Gopalakrishnan else if (!(i & 0x1)) 1258a597d2a5SAravind Gopalakrishnan shift = i >> 1; 1259a597d2a5SAravind Gopalakrishnan else 1260a597d2a5SAravind Gopalakrishnan shift = (i + 1) >> 1; 1261a597d2a5SAravind Gopalakrishnan 1262a597d2a5SAravind Gopalakrishnan if (cs_size != -1) 1263a597d2a5SAravind Gopalakrishnan cs_size = rank_multiply * (128 << shift); 1264a597d2a5SAravind Gopalakrishnan 1265a597d2a5SAravind Gopalakrishnan return cs_size; 1266a597d2a5SAravind Gopalakrishnan } 1267a597d2a5SAravind Gopalakrishnan 1268a597d2a5SAravind Gopalakrishnan static int ddr4_cs_size(unsigned i) 1269a597d2a5SAravind Gopalakrishnan { 1270a597d2a5SAravind Gopalakrishnan int cs_size = 0; 1271a597d2a5SAravind Gopalakrishnan 1272a597d2a5SAravind Gopalakrishnan if (i == 0) 1273a597d2a5SAravind Gopalakrishnan cs_size = -1; 1274a597d2a5SAravind Gopalakrishnan else if (i == 1) 1275a597d2a5SAravind Gopalakrishnan cs_size = 1024; 1276a597d2a5SAravind Gopalakrishnan else 1277a597d2a5SAravind Gopalakrishnan /* Min cs_size = 1G */ 1278a597d2a5SAravind Gopalakrishnan cs_size = 1024 * (1 << (i >> 1)); 1279a597d2a5SAravind Gopalakrishnan 1280a597d2a5SAravind Gopalakrishnan return cs_size; 1281a597d2a5SAravind Gopalakrishnan } 1282a597d2a5SAravind Gopalakrishnan 128341d8bfabSBorislav Petkov static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 1284a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 128541d8bfabSBorislav Petkov { 128641d8bfabSBorislav Petkov u32 dclr = dct ? pvt->dclr1 : pvt->dclr0; 128741d8bfabSBorislav Petkov 128841d8bfabSBorislav Petkov WARN_ON(cs_mode > 11); 12891433eb99SBorislav Petkov 12901433eb99SBorislav Petkov if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE) 129141d8bfabSBorislav Petkov return ddr3_cs_size(cs_mode, dclr & WIDTH_128); 12921433eb99SBorislav Petkov else 129341d8bfabSBorislav Petkov return ddr2_cs_size(cs_mode, dclr & WIDTH_128); 129441d8bfabSBorislav Petkov } 12951433eb99SBorislav Petkov 129641d8bfabSBorislav Petkov /* 129741d8bfabSBorislav Petkov * F15h supports only 64bit DCT interfaces 129841d8bfabSBorislav Petkov */ 129941d8bfabSBorislav Petkov static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 1300a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 130141d8bfabSBorislav Petkov { 130241d8bfabSBorislav Petkov WARN_ON(cs_mode > 12); 130341d8bfabSBorislav Petkov 130441d8bfabSBorislav Petkov return ddr3_cs_size(cs_mode, false); 13051afd3c98SDoug Thompson } 13061afd3c98SDoug Thompson 1307a597d2a5SAravind Gopalakrishnan /* F15h M60h supports DDR4 mapping as well.. */ 1308a597d2a5SAravind Gopalakrishnan static int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 1309a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 1310a597d2a5SAravind Gopalakrishnan { 1311a597d2a5SAravind Gopalakrishnan int cs_size; 1312a597d2a5SAravind Gopalakrishnan u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr]; 1313a597d2a5SAravind Gopalakrishnan 1314a597d2a5SAravind Gopalakrishnan WARN_ON(cs_mode > 12); 1315a597d2a5SAravind Gopalakrishnan 1316a597d2a5SAravind Gopalakrishnan if (pvt->dram_type == MEM_DDR4) { 1317a597d2a5SAravind Gopalakrishnan if (cs_mode > 9) 1318a597d2a5SAravind Gopalakrishnan return -1; 1319a597d2a5SAravind Gopalakrishnan 1320a597d2a5SAravind Gopalakrishnan cs_size = ddr4_cs_size(cs_mode); 1321a597d2a5SAravind Gopalakrishnan } else if (pvt->dram_type == MEM_LRDDR3) { 1322a597d2a5SAravind Gopalakrishnan unsigned rank_multiply = dcsm & 0xf; 1323a597d2a5SAravind Gopalakrishnan 1324a597d2a5SAravind Gopalakrishnan if (rank_multiply == 3) 1325a597d2a5SAravind Gopalakrishnan rank_multiply = 4; 1326a597d2a5SAravind Gopalakrishnan cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply); 1327a597d2a5SAravind Gopalakrishnan } else { 1328a597d2a5SAravind Gopalakrishnan /* Minimum cs size is 512mb for F15hM60h*/ 1329a597d2a5SAravind Gopalakrishnan if (cs_mode == 0x1) 1330a597d2a5SAravind Gopalakrishnan return -1; 1331a597d2a5SAravind Gopalakrishnan 1332a597d2a5SAravind Gopalakrishnan cs_size = ddr3_cs_size(cs_mode, false); 1333a597d2a5SAravind Gopalakrishnan } 1334a597d2a5SAravind Gopalakrishnan 1335a597d2a5SAravind Gopalakrishnan return cs_size; 1336a597d2a5SAravind Gopalakrishnan } 1337a597d2a5SAravind Gopalakrishnan 133894c1acf2SAravind Gopalakrishnan /* 133918b94f66SAravind Gopalakrishnan * F16h and F15h model 30h have only limited cs_modes. 134094c1acf2SAravind Gopalakrishnan */ 134194c1acf2SAravind Gopalakrishnan static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 1342a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 134394c1acf2SAravind Gopalakrishnan { 134494c1acf2SAravind Gopalakrishnan WARN_ON(cs_mode > 12); 134594c1acf2SAravind Gopalakrishnan 134694c1acf2SAravind Gopalakrishnan if (cs_mode == 6 || cs_mode == 8 || 134794c1acf2SAravind Gopalakrishnan cs_mode == 9 || cs_mode == 12) 134894c1acf2SAravind Gopalakrishnan return -1; 134994c1acf2SAravind Gopalakrishnan else 135094c1acf2SAravind Gopalakrishnan return ddr3_cs_size(cs_mode, false); 135194c1acf2SAravind Gopalakrishnan } 135294c1acf2SAravind Gopalakrishnan 1353f1cbbec9SYazen Ghannam static int f17_base_addr_to_cs_size(struct amd64_pvt *pvt, u8 umc, 1354f1cbbec9SYazen Ghannam unsigned int cs_mode, int csrow_nr) 1355f1cbbec9SYazen Ghannam { 1356f1cbbec9SYazen Ghannam u32 base_addr = pvt->csels[umc].csbases[csrow_nr]; 1357f1cbbec9SYazen Ghannam 1358f1cbbec9SYazen Ghannam /* Each mask is used for every two base addresses. */ 1359f1cbbec9SYazen Ghannam u32 addr_mask = pvt->csels[umc].csmasks[csrow_nr >> 1]; 1360f1cbbec9SYazen Ghannam 1361f1cbbec9SYazen Ghannam /* Register [31:1] = Address [39:9]. Size is in kBs here. */ 1362f1cbbec9SYazen Ghannam u32 size = ((addr_mask >> 1) - (base_addr >> 1) + 1) >> 1; 1363f1cbbec9SYazen Ghannam 1364f1cbbec9SYazen Ghannam edac_dbg(1, "BaseAddr: 0x%x, AddrMask: 0x%x\n", base_addr, addr_mask); 1365f1cbbec9SYazen Ghannam 1366f1cbbec9SYazen Ghannam /* Return size in MBs. */ 1367f1cbbec9SYazen Ghannam return size >> 10; 1368f1cbbec9SYazen Ghannam } 1369f1cbbec9SYazen Ghannam 13705a5d2371SBorislav Petkov static void read_dram_ctl_register(struct amd64_pvt *pvt) 13716163b5d4SDoug Thompson { 13726163b5d4SDoug Thompson 1373a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) 13745a5d2371SBorislav Petkov return; 13755a5d2371SBorislav Petkov 13767981a28fSAravind Gopalakrishnan if (!amd64_read_pci_cfg(pvt->F2, DCT_SEL_LO, &pvt->dct_sel_lo)) { 1377956b9ba1SJoe Perches edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n", 137878da121eSBorislav Petkov pvt->dct_sel_lo, dct_sel_baseaddr(pvt)); 13796163b5d4SDoug Thompson 1380956b9ba1SJoe Perches edac_dbg(0, " DCTs operate in %s mode\n", 13815a5d2371SBorislav Petkov (dct_ganging_enabled(pvt) ? "ganged" : "unganged")); 13826163b5d4SDoug Thompson 138372381bd5SBorislav Petkov if (!dct_ganging_enabled(pvt)) 1384956b9ba1SJoe Perches edac_dbg(0, " Address range split per DCT: %s\n", 138572381bd5SBorislav Petkov (dct_high_range_enabled(pvt) ? "yes" : "no")); 138672381bd5SBorislav Petkov 1387956b9ba1SJoe Perches edac_dbg(0, " data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n", 138872381bd5SBorislav Petkov (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"), 138972381bd5SBorislav Petkov (dct_memory_cleared(pvt) ? "yes" : "no")); 139072381bd5SBorislav Petkov 1391956b9ba1SJoe Perches edac_dbg(0, " channel interleave: %s, " 139278da121eSBorislav Petkov "interleave bits selector: 0x%x\n", 139372381bd5SBorislav Petkov (dct_interleave_enabled(pvt) ? "enabled" : "disabled"), 13946163b5d4SDoug Thompson dct_sel_interleave_addr(pvt)); 13956163b5d4SDoug Thompson } 13966163b5d4SDoug Thompson 13977981a28fSAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F2, DCT_SEL_HI, &pvt->dct_sel_hi); 13986163b5d4SDoug Thompson } 13996163b5d4SDoug Thompson 1400f71d0a05SDoug Thompson /* 140118b94f66SAravind Gopalakrishnan * Determine channel (DCT) based on the interleaving mode (see F15h M30h BKDG, 140218b94f66SAravind Gopalakrishnan * 2.10.12 Memory Interleaving Modes). 140318b94f66SAravind Gopalakrishnan */ 140418b94f66SAravind Gopalakrishnan static u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, 140518b94f66SAravind Gopalakrishnan u8 intlv_en, int num_dcts_intlv, 140618b94f66SAravind Gopalakrishnan u32 dct_sel) 140718b94f66SAravind Gopalakrishnan { 140818b94f66SAravind Gopalakrishnan u8 channel = 0; 140918b94f66SAravind Gopalakrishnan u8 select; 141018b94f66SAravind Gopalakrishnan 141118b94f66SAravind Gopalakrishnan if (!(intlv_en)) 141218b94f66SAravind Gopalakrishnan return (u8)(dct_sel); 141318b94f66SAravind Gopalakrishnan 141418b94f66SAravind Gopalakrishnan if (num_dcts_intlv == 2) { 141518b94f66SAravind Gopalakrishnan select = (sys_addr >> 8) & 0x3; 141618b94f66SAravind Gopalakrishnan channel = select ? 0x3 : 0; 14179d0e8d83SAravind Gopalakrishnan } else if (num_dcts_intlv == 4) { 14189d0e8d83SAravind Gopalakrishnan u8 intlv_addr = dct_sel_interleave_addr(pvt); 14199d0e8d83SAravind Gopalakrishnan switch (intlv_addr) { 14209d0e8d83SAravind Gopalakrishnan case 0x4: 14219d0e8d83SAravind Gopalakrishnan channel = (sys_addr >> 8) & 0x3; 14229d0e8d83SAravind Gopalakrishnan break; 14239d0e8d83SAravind Gopalakrishnan case 0x5: 14249d0e8d83SAravind Gopalakrishnan channel = (sys_addr >> 9) & 0x3; 14259d0e8d83SAravind Gopalakrishnan break; 14269d0e8d83SAravind Gopalakrishnan } 14279d0e8d83SAravind Gopalakrishnan } 142818b94f66SAravind Gopalakrishnan return channel; 142918b94f66SAravind Gopalakrishnan } 143018b94f66SAravind Gopalakrishnan 143118b94f66SAravind Gopalakrishnan /* 1432229a7a11SBorislav Petkov * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory 1433f71d0a05SDoug Thompson * Interleaving Modes. 1434f71d0a05SDoug Thompson */ 1435b15f0fcaSBorislav Petkov static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, 1436229a7a11SBorislav Petkov bool hi_range_sel, u8 intlv_en) 14376163b5d4SDoug Thompson { 1438151fa71cSBorislav Petkov u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1; 14396163b5d4SDoug Thompson 14406163b5d4SDoug Thompson if (dct_ganging_enabled(pvt)) 1441229a7a11SBorislav Petkov return 0; 1442229a7a11SBorislav Petkov 1443229a7a11SBorislav Petkov if (hi_range_sel) 1444229a7a11SBorislav Petkov return dct_sel_high; 1445229a7a11SBorislav Petkov 1446f71d0a05SDoug Thompson /* 1447f71d0a05SDoug Thompson * see F2x110[DctSelIntLvAddr] - channel interleave mode 1448f71d0a05SDoug Thompson */ 1449229a7a11SBorislav Petkov if (dct_interleave_enabled(pvt)) { 1450229a7a11SBorislav Petkov u8 intlv_addr = dct_sel_interleave_addr(pvt); 14516163b5d4SDoug Thompson 1452229a7a11SBorislav Petkov /* return DCT select function: 0=DCT0, 1=DCT1 */ 1453229a7a11SBorislav Petkov if (!intlv_addr) 1454229a7a11SBorislav Petkov return sys_addr >> 6 & 1; 14556163b5d4SDoug Thompson 1456229a7a11SBorislav Petkov if (intlv_addr & 0x2) { 1457229a7a11SBorislav Petkov u8 shift = intlv_addr & 0x1 ? 9 : 6; 1458dc0a50a8SYazen Ghannam u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) & 1; 1459229a7a11SBorislav Petkov 1460229a7a11SBorislav Petkov return ((sys_addr >> shift) & 1) ^ temp; 14616163b5d4SDoug Thompson } 14626163b5d4SDoug Thompson 1463dc0a50a8SYazen Ghannam if (intlv_addr & 0x4) { 1464dc0a50a8SYazen Ghannam u8 shift = intlv_addr & 0x1 ? 9 : 8; 1465dc0a50a8SYazen Ghannam 1466dc0a50a8SYazen Ghannam return (sys_addr >> shift) & 1; 1467dc0a50a8SYazen Ghannam } 1468dc0a50a8SYazen Ghannam 1469229a7a11SBorislav Petkov return (sys_addr >> (12 + hweight8(intlv_en))) & 1; 1470229a7a11SBorislav Petkov } 1471229a7a11SBorislav Petkov 1472229a7a11SBorislav Petkov if (dct_high_range_enabled(pvt)) 1473229a7a11SBorislav Petkov return ~dct_sel_high & 1; 14746163b5d4SDoug Thompson 14756163b5d4SDoug Thompson return 0; 14766163b5d4SDoug Thompson } 14776163b5d4SDoug Thompson 1478c8e518d5SBorislav Petkov /* Convert the sys_addr to the normalized DCT address */ 1479c7e5301aSDaniel J Blueman static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range, 1480c8e518d5SBorislav Petkov u64 sys_addr, bool hi_rng, 1481c8e518d5SBorislav Petkov u32 dct_sel_base_addr) 14826163b5d4SDoug Thompson { 14836163b5d4SDoug Thompson u64 chan_off; 1484c8e518d5SBorislav Petkov u64 dram_base = get_dram_base(pvt, range); 1485c8e518d5SBorislav Petkov u64 hole_off = f10_dhar_offset(pvt); 14866f3508f6SDan Carpenter u64 dct_sel_base_off = (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16; 14876163b5d4SDoug Thompson 1488c8e518d5SBorislav Petkov if (hi_rng) { 1489c8e518d5SBorislav Petkov /* 1490c8e518d5SBorislav Petkov * if 1491c8e518d5SBorislav Petkov * base address of high range is below 4Gb 1492c8e518d5SBorislav Petkov * (bits [47:27] at [31:11]) 1493c8e518d5SBorislav Petkov * DRAM address space on this DCT is hoisted above 4Gb && 1494c8e518d5SBorislav Petkov * sys_addr > 4Gb 1495c8e518d5SBorislav Petkov * 1496c8e518d5SBorislav Petkov * remove hole offset from sys_addr 1497c8e518d5SBorislav Petkov * else 1498c8e518d5SBorislav Petkov * remove high range offset from sys_addr 1499c8e518d5SBorislav Petkov */ 1500c8e518d5SBorislav Petkov if ((!(dct_sel_base_addr >> 16) || 1501c8e518d5SBorislav Petkov dct_sel_base_addr < dhar_base(pvt)) && 1502972ea17aSBorislav Petkov dhar_valid(pvt) && 1503c8e518d5SBorislav Petkov (sys_addr >= BIT_64(32))) 1504bc21fa57SBorislav Petkov chan_off = hole_off; 15056163b5d4SDoug Thompson else 15066163b5d4SDoug Thompson chan_off = dct_sel_base_off; 15076163b5d4SDoug Thompson } else { 1508c8e518d5SBorislav Petkov /* 1509c8e518d5SBorislav Petkov * if 1510c8e518d5SBorislav Petkov * we have a valid hole && 1511c8e518d5SBorislav Petkov * sys_addr > 4Gb 1512c8e518d5SBorislav Petkov * 1513c8e518d5SBorislav Petkov * remove hole 1514c8e518d5SBorislav Petkov * else 1515c8e518d5SBorislav Petkov * remove dram base to normalize to DCT address 1516c8e518d5SBorislav Petkov */ 1517972ea17aSBorislav Petkov if (dhar_valid(pvt) && (sys_addr >= BIT_64(32))) 1518bc21fa57SBorislav Petkov chan_off = hole_off; 15196163b5d4SDoug Thompson else 1520c8e518d5SBorislav Petkov chan_off = dram_base; 15216163b5d4SDoug Thompson } 15226163b5d4SDoug Thompson 152310ef6b0dSChen, Gong return (sys_addr & GENMASK_ULL(47,6)) - (chan_off & GENMASK_ULL(47,23)); 15246163b5d4SDoug Thompson } 15256163b5d4SDoug Thompson 15266163b5d4SDoug Thompson /* 15276163b5d4SDoug Thompson * checks if the csrow passed in is marked as SPARED, if so returns the new 15286163b5d4SDoug Thompson * spare row 15296163b5d4SDoug Thompson */ 153011c75eadSBorislav Petkov static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow) 15316163b5d4SDoug Thompson { 1532614ec9d8SBorislav Petkov int tmp_cs; 15336163b5d4SDoug Thompson 1534614ec9d8SBorislav Petkov if (online_spare_swap_done(pvt, dct) && 1535614ec9d8SBorislav Petkov csrow == online_spare_bad_dramcs(pvt, dct)) { 1536614ec9d8SBorislav Petkov 1537614ec9d8SBorislav Petkov for_each_chip_select(tmp_cs, dct, pvt) { 1538614ec9d8SBorislav Petkov if (chip_select_base(tmp_cs, dct, pvt) & 0x2) { 1539614ec9d8SBorislav Petkov csrow = tmp_cs; 1540614ec9d8SBorislav Petkov break; 1541614ec9d8SBorislav Petkov } 1542614ec9d8SBorislav Petkov } 15436163b5d4SDoug Thompson } 15446163b5d4SDoug Thompson return csrow; 15456163b5d4SDoug Thompson } 15466163b5d4SDoug Thompson 15476163b5d4SDoug Thompson /* 15486163b5d4SDoug Thompson * Iterate over the DRAM DCT "base" and "mask" registers looking for a 15496163b5d4SDoug Thompson * SystemAddr match on the specified 'ChannelSelect' and 'NodeID' 15506163b5d4SDoug Thompson * 15516163b5d4SDoug Thompson * Return: 15526163b5d4SDoug Thompson * -EINVAL: NOT FOUND 15536163b5d4SDoug Thompson * 0..csrow = Chip-Select Row 15546163b5d4SDoug Thompson */ 1555c7e5301aSDaniel J Blueman static int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct) 15566163b5d4SDoug Thompson { 15576163b5d4SDoug Thompson struct mem_ctl_info *mci; 15586163b5d4SDoug Thompson struct amd64_pvt *pvt; 155911c75eadSBorislav Petkov u64 cs_base, cs_mask; 15606163b5d4SDoug Thompson int cs_found = -EINVAL; 15616163b5d4SDoug Thompson int csrow; 15626163b5d4SDoug Thompson 15632ec591acSBorislav Petkov mci = edac_mc_find(nid); 15646163b5d4SDoug Thompson if (!mci) 15656163b5d4SDoug Thompson return cs_found; 15666163b5d4SDoug Thompson 15676163b5d4SDoug Thompson pvt = mci->pvt_info; 15686163b5d4SDoug Thompson 1569956b9ba1SJoe Perches edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct); 15706163b5d4SDoug Thompson 157111c75eadSBorislav Petkov for_each_chip_select(csrow, dct, pvt) { 157211c75eadSBorislav Petkov if (!csrow_enabled(csrow, dct, pvt)) 15736163b5d4SDoug Thompson continue; 15746163b5d4SDoug Thompson 157511c75eadSBorislav Petkov get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask); 15766163b5d4SDoug Thompson 1577956b9ba1SJoe Perches edac_dbg(1, " CSROW=%d CSBase=0x%llx CSMask=0x%llx\n", 15786163b5d4SDoug Thompson csrow, cs_base, cs_mask); 15796163b5d4SDoug Thompson 158011c75eadSBorislav Petkov cs_mask = ~cs_mask; 15816163b5d4SDoug Thompson 1582956b9ba1SJoe Perches edac_dbg(1, " (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n", 158311c75eadSBorislav Petkov (in_addr & cs_mask), (cs_base & cs_mask)); 15846163b5d4SDoug Thompson 158511c75eadSBorislav Petkov if ((in_addr & cs_mask) == (cs_base & cs_mask)) { 158618b94f66SAravind Gopalakrishnan if (pvt->fam == 0x15 && pvt->model >= 0x30) { 158718b94f66SAravind Gopalakrishnan cs_found = csrow; 158818b94f66SAravind Gopalakrishnan break; 158918b94f66SAravind Gopalakrishnan } 159011c75eadSBorislav Petkov cs_found = f10_process_possible_spare(pvt, dct, csrow); 15916163b5d4SDoug Thompson 1592956b9ba1SJoe Perches edac_dbg(1, " MATCH csrow=%d\n", cs_found); 15936163b5d4SDoug Thompson break; 15946163b5d4SDoug Thompson } 15956163b5d4SDoug Thompson } 15966163b5d4SDoug Thompson return cs_found; 15976163b5d4SDoug Thompson } 15986163b5d4SDoug Thompson 159995b0ef55SBorislav Petkov /* 160095b0ef55SBorislav Petkov * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is 160195b0ef55SBorislav Petkov * swapped with a region located at the bottom of memory so that the GPU can use 160295b0ef55SBorislav Petkov * the interleaved region and thus two channels. 160395b0ef55SBorislav Petkov */ 1604b15f0fcaSBorislav Petkov static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr) 160595b0ef55SBorislav Petkov { 160695b0ef55SBorislav Petkov u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr; 160795b0ef55SBorislav Petkov 1608a4b4bedcSBorislav Petkov if (pvt->fam == 0x10) { 160995b0ef55SBorislav Petkov /* only revC3 and revE have that feature */ 1610a4b4bedcSBorislav Petkov if (pvt->model < 4 || (pvt->model < 0xa && pvt->stepping < 3)) 161195b0ef55SBorislav Petkov return sys_addr; 161295b0ef55SBorislav Petkov } 161395b0ef55SBorislav Petkov 16147981a28fSAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F2, SWAP_INTLV_REG, &swap_reg); 161595b0ef55SBorislav Petkov 161695b0ef55SBorislav Petkov if (!(swap_reg & 0x1)) 161795b0ef55SBorislav Petkov return sys_addr; 161895b0ef55SBorislav Petkov 161995b0ef55SBorislav Petkov swap_base = (swap_reg >> 3) & 0x7f; 162095b0ef55SBorislav Petkov swap_limit = (swap_reg >> 11) & 0x7f; 162195b0ef55SBorislav Petkov rgn_size = (swap_reg >> 20) & 0x7f; 162295b0ef55SBorislav Petkov tmp_addr = sys_addr >> 27; 162395b0ef55SBorislav Petkov 162495b0ef55SBorislav Petkov if (!(sys_addr >> 34) && 162595b0ef55SBorislav Petkov (((tmp_addr >= swap_base) && 162695b0ef55SBorislav Petkov (tmp_addr <= swap_limit)) || 162795b0ef55SBorislav Petkov (tmp_addr < rgn_size))) 162895b0ef55SBorislav Petkov return sys_addr ^ (u64)swap_base << 27; 162995b0ef55SBorislav Petkov 163095b0ef55SBorislav Petkov return sys_addr; 163195b0ef55SBorislav Petkov } 163295b0ef55SBorislav Petkov 1633f71d0a05SDoug Thompson /* For a given @dram_range, check if @sys_addr falls within it. */ 1634e761359aSBorislav Petkov static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range, 163533ca0643SBorislav Petkov u64 sys_addr, int *chan_sel) 1636f71d0a05SDoug Thompson { 1637229a7a11SBorislav Petkov int cs_found = -EINVAL; 1638c8e518d5SBorislav Petkov u64 chan_addr; 16395d4b58e8SBorislav Petkov u32 dct_sel_base; 164011c75eadSBorislav Petkov u8 channel; 1641229a7a11SBorislav Petkov bool high_range = false; 1642f71d0a05SDoug Thompson 16437f19bf75SBorislav Petkov u8 node_id = dram_dst_node(pvt, range); 1644229a7a11SBorislav Petkov u8 intlv_en = dram_intlv_en(pvt, range); 16457f19bf75SBorislav Petkov u32 intlv_sel = dram_intlv_sel(pvt, range); 1646f71d0a05SDoug Thompson 1647956b9ba1SJoe Perches edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n", 1648c8e518d5SBorislav Petkov range, sys_addr, get_dram_limit(pvt, range)); 1649f71d0a05SDoug Thompson 1650355fba60SBorislav Petkov if (dhar_valid(pvt) && 1651355fba60SBorislav Petkov dhar_base(pvt) <= sys_addr && 1652355fba60SBorislav Petkov sys_addr < BIT_64(32)) { 1653355fba60SBorislav Petkov amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n", 1654355fba60SBorislav Petkov sys_addr); 1655f71d0a05SDoug Thompson return -EINVAL; 1656355fba60SBorislav Petkov } 1657355fba60SBorislav Petkov 1658f030ddfbSBorislav Petkov if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en))) 1659355fba60SBorislav Petkov return -EINVAL; 1660f71d0a05SDoug Thompson 1661b15f0fcaSBorislav Petkov sys_addr = f1x_swap_interleaved_region(pvt, sys_addr); 166295b0ef55SBorislav Petkov 1663f71d0a05SDoug Thompson dct_sel_base = dct_sel_baseaddr(pvt); 1664f71d0a05SDoug Thompson 1665f71d0a05SDoug Thompson /* 1666f71d0a05SDoug Thompson * check whether addresses >= DctSelBaseAddr[47:27] are to be used to 1667f71d0a05SDoug Thompson * select between DCT0 and DCT1. 1668f71d0a05SDoug Thompson */ 1669f71d0a05SDoug Thompson if (dct_high_range_enabled(pvt) && 1670f71d0a05SDoug Thompson !dct_ganging_enabled(pvt) && 1671f71d0a05SDoug Thompson ((sys_addr >> 27) >= (dct_sel_base >> 11))) 1672229a7a11SBorislav Petkov high_range = true; 1673f71d0a05SDoug Thompson 1674b15f0fcaSBorislav Petkov channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en); 1675f71d0a05SDoug Thompson 1676b15f0fcaSBorislav Petkov chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr, 1677c8e518d5SBorislav Petkov high_range, dct_sel_base); 1678f71d0a05SDoug Thompson 1679e2f79dbdSBorislav Petkov /* Remove node interleaving, see F1x120 */ 1680e2f79dbdSBorislav Petkov if (intlv_en) 1681e2f79dbdSBorislav Petkov chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) | 1682e2f79dbdSBorislav Petkov (chan_addr & 0xfff); 1683f71d0a05SDoug Thompson 16845d4b58e8SBorislav Petkov /* remove channel interleave */ 1685f71d0a05SDoug Thompson if (dct_interleave_enabled(pvt) && 1686f71d0a05SDoug Thompson !dct_high_range_enabled(pvt) && 1687f71d0a05SDoug Thompson !dct_ganging_enabled(pvt)) { 16885d4b58e8SBorislav Petkov 16895d4b58e8SBorislav Petkov if (dct_sel_interleave_addr(pvt) != 1) { 16905d4b58e8SBorislav Petkov if (dct_sel_interleave_addr(pvt) == 0x3) 16915d4b58e8SBorislav Petkov /* hash 9 */ 16925d4b58e8SBorislav Petkov chan_addr = ((chan_addr >> 10) << 9) | 16935d4b58e8SBorislav Petkov (chan_addr & 0x1ff); 16945d4b58e8SBorislav Petkov else 16955d4b58e8SBorislav Petkov /* A[6] or hash 6 */ 16965d4b58e8SBorislav Petkov chan_addr = ((chan_addr >> 7) << 6) | 16975d4b58e8SBorislav Petkov (chan_addr & 0x3f); 16985d4b58e8SBorislav Petkov } else 16995d4b58e8SBorislav Petkov /* A[12] */ 17005d4b58e8SBorislav Petkov chan_addr = ((chan_addr >> 13) << 12) | 17015d4b58e8SBorislav Petkov (chan_addr & 0xfff); 1702f71d0a05SDoug Thompson } 1703f71d0a05SDoug Thompson 1704956b9ba1SJoe Perches edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr); 1705f71d0a05SDoug Thompson 1706b15f0fcaSBorislav Petkov cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel); 1707f71d0a05SDoug Thompson 170833ca0643SBorislav Petkov if (cs_found >= 0) 1709f71d0a05SDoug Thompson *chan_sel = channel; 171033ca0643SBorislav Petkov 1711f71d0a05SDoug Thompson return cs_found; 1712f71d0a05SDoug Thompson } 1713f71d0a05SDoug Thompson 171418b94f66SAravind Gopalakrishnan static int f15_m30h_match_to_this_node(struct amd64_pvt *pvt, unsigned range, 171518b94f66SAravind Gopalakrishnan u64 sys_addr, int *chan_sel) 171618b94f66SAravind Gopalakrishnan { 171718b94f66SAravind Gopalakrishnan int cs_found = -EINVAL; 171818b94f66SAravind Gopalakrishnan int num_dcts_intlv = 0; 171918b94f66SAravind Gopalakrishnan u64 chan_addr, chan_offset; 172018b94f66SAravind Gopalakrishnan u64 dct_base, dct_limit; 172118b94f66SAravind Gopalakrishnan u32 dct_cont_base_reg, dct_cont_limit_reg, tmp; 172218b94f66SAravind Gopalakrishnan u8 channel, alias_channel, leg_mmio_hole, dct_sel, dct_offset_en; 172318b94f66SAravind Gopalakrishnan 172418b94f66SAravind Gopalakrishnan u64 dhar_offset = f10_dhar_offset(pvt); 172518b94f66SAravind Gopalakrishnan u8 intlv_addr = dct_sel_interleave_addr(pvt); 172618b94f66SAravind Gopalakrishnan u8 node_id = dram_dst_node(pvt, range); 172718b94f66SAravind Gopalakrishnan u8 intlv_en = dram_intlv_en(pvt, range); 172818b94f66SAravind Gopalakrishnan 172918b94f66SAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F1, DRAM_CONT_BASE, &dct_cont_base_reg); 173018b94f66SAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F1, DRAM_CONT_LIMIT, &dct_cont_limit_reg); 173118b94f66SAravind Gopalakrishnan 173218b94f66SAravind Gopalakrishnan dct_offset_en = (u8) ((dct_cont_base_reg >> 3) & BIT(0)); 173318b94f66SAravind Gopalakrishnan dct_sel = (u8) ((dct_cont_base_reg >> 4) & 0x7); 173418b94f66SAravind Gopalakrishnan 173518b94f66SAravind Gopalakrishnan edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n", 173618b94f66SAravind Gopalakrishnan range, sys_addr, get_dram_limit(pvt, range)); 173718b94f66SAravind Gopalakrishnan 173818b94f66SAravind Gopalakrishnan if (!(get_dram_base(pvt, range) <= sys_addr) && 173918b94f66SAravind Gopalakrishnan !(get_dram_limit(pvt, range) >= sys_addr)) 174018b94f66SAravind Gopalakrishnan return -EINVAL; 174118b94f66SAravind Gopalakrishnan 174218b94f66SAravind Gopalakrishnan if (dhar_valid(pvt) && 174318b94f66SAravind Gopalakrishnan dhar_base(pvt) <= sys_addr && 174418b94f66SAravind Gopalakrishnan sys_addr < BIT_64(32)) { 174518b94f66SAravind Gopalakrishnan amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n", 174618b94f66SAravind Gopalakrishnan sys_addr); 174718b94f66SAravind Gopalakrishnan return -EINVAL; 174818b94f66SAravind Gopalakrishnan } 174918b94f66SAravind Gopalakrishnan 175018b94f66SAravind Gopalakrishnan /* Verify sys_addr is within DCT Range. */ 17514fc06b31SAravind Gopalakrishnan dct_base = (u64) dct_sel_baseaddr(pvt); 17524fc06b31SAravind Gopalakrishnan dct_limit = (dct_cont_limit_reg >> 11) & 0x1FFF; 175318b94f66SAravind Gopalakrishnan 175418b94f66SAravind Gopalakrishnan if (!(dct_cont_base_reg & BIT(0)) && 17554fc06b31SAravind Gopalakrishnan !(dct_base <= (sys_addr >> 27) && 17564fc06b31SAravind Gopalakrishnan dct_limit >= (sys_addr >> 27))) 175718b94f66SAravind Gopalakrishnan return -EINVAL; 175818b94f66SAravind Gopalakrishnan 175918b94f66SAravind Gopalakrishnan /* Verify number of dct's that participate in channel interleaving. */ 176018b94f66SAravind Gopalakrishnan num_dcts_intlv = (int) hweight8(intlv_en); 176118b94f66SAravind Gopalakrishnan 176218b94f66SAravind Gopalakrishnan if (!(num_dcts_intlv % 2 == 0) || (num_dcts_intlv > 4)) 176318b94f66SAravind Gopalakrishnan return -EINVAL; 176418b94f66SAravind Gopalakrishnan 1765dc0a50a8SYazen Ghannam if (pvt->model >= 0x60) 1766dc0a50a8SYazen Ghannam channel = f1x_determine_channel(pvt, sys_addr, false, intlv_en); 1767dc0a50a8SYazen Ghannam else 176818b94f66SAravind Gopalakrishnan channel = f15_m30h_determine_channel(pvt, sys_addr, intlv_en, 176918b94f66SAravind Gopalakrishnan num_dcts_intlv, dct_sel); 177018b94f66SAravind Gopalakrishnan 177118b94f66SAravind Gopalakrishnan /* Verify we stay within the MAX number of channels allowed */ 17727f3f5240SAravind Gopalakrishnan if (channel > 3) 177318b94f66SAravind Gopalakrishnan return -EINVAL; 177418b94f66SAravind Gopalakrishnan 177518b94f66SAravind Gopalakrishnan leg_mmio_hole = (u8) (dct_cont_base_reg >> 1 & BIT(0)); 177618b94f66SAravind Gopalakrishnan 177718b94f66SAravind Gopalakrishnan /* Get normalized DCT addr */ 177818b94f66SAravind Gopalakrishnan if (leg_mmio_hole && (sys_addr >= BIT_64(32))) 177918b94f66SAravind Gopalakrishnan chan_offset = dhar_offset; 178018b94f66SAravind Gopalakrishnan else 17814fc06b31SAravind Gopalakrishnan chan_offset = dct_base << 27; 178218b94f66SAravind Gopalakrishnan 178318b94f66SAravind Gopalakrishnan chan_addr = sys_addr - chan_offset; 178418b94f66SAravind Gopalakrishnan 178518b94f66SAravind Gopalakrishnan /* remove channel interleave */ 178618b94f66SAravind Gopalakrishnan if (num_dcts_intlv == 2) { 178718b94f66SAravind Gopalakrishnan if (intlv_addr == 0x4) 178818b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 9) << 8) | 178918b94f66SAravind Gopalakrishnan (chan_addr & 0xff); 179018b94f66SAravind Gopalakrishnan else if (intlv_addr == 0x5) 179118b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 10) << 9) | 179218b94f66SAravind Gopalakrishnan (chan_addr & 0x1ff); 179318b94f66SAravind Gopalakrishnan else 179418b94f66SAravind Gopalakrishnan return -EINVAL; 179518b94f66SAravind Gopalakrishnan 179618b94f66SAravind Gopalakrishnan } else if (num_dcts_intlv == 4) { 179718b94f66SAravind Gopalakrishnan if (intlv_addr == 0x4) 179818b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 10) << 8) | 179918b94f66SAravind Gopalakrishnan (chan_addr & 0xff); 180018b94f66SAravind Gopalakrishnan else if (intlv_addr == 0x5) 180118b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 11) << 9) | 180218b94f66SAravind Gopalakrishnan (chan_addr & 0x1ff); 180318b94f66SAravind Gopalakrishnan else 180418b94f66SAravind Gopalakrishnan return -EINVAL; 180518b94f66SAravind Gopalakrishnan } 180618b94f66SAravind Gopalakrishnan 180718b94f66SAravind Gopalakrishnan if (dct_offset_en) { 180818b94f66SAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F1, 180918b94f66SAravind Gopalakrishnan DRAM_CONT_HIGH_OFF + (int) channel * 4, 181018b94f66SAravind Gopalakrishnan &tmp); 18114fc06b31SAravind Gopalakrishnan chan_addr += (u64) ((tmp >> 11) & 0xfff) << 27; 181218b94f66SAravind Gopalakrishnan } 181318b94f66SAravind Gopalakrishnan 181418b94f66SAravind Gopalakrishnan f15h_select_dct(pvt, channel); 181518b94f66SAravind Gopalakrishnan 181618b94f66SAravind Gopalakrishnan edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr); 181718b94f66SAravind Gopalakrishnan 181818b94f66SAravind Gopalakrishnan /* 181918b94f66SAravind Gopalakrishnan * Find Chip select: 182018b94f66SAravind Gopalakrishnan * if channel = 3, then alias it to 1. This is because, in F15 M30h, 182118b94f66SAravind Gopalakrishnan * there is support for 4 DCT's, but only 2 are currently functional. 182218b94f66SAravind Gopalakrishnan * They are DCT0 and DCT3. But we have read all registers of DCT3 into 182318b94f66SAravind Gopalakrishnan * pvt->csels[1]. So we need to use '1' here to get correct info. 182418b94f66SAravind Gopalakrishnan * Refer F15 M30h BKDG Section 2.10 and 2.10.3 for clarifications. 182518b94f66SAravind Gopalakrishnan */ 182618b94f66SAravind Gopalakrishnan alias_channel = (channel == 3) ? 1 : channel; 182718b94f66SAravind Gopalakrishnan 182818b94f66SAravind Gopalakrishnan cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, alias_channel); 182918b94f66SAravind Gopalakrishnan 183018b94f66SAravind Gopalakrishnan if (cs_found >= 0) 183118b94f66SAravind Gopalakrishnan *chan_sel = alias_channel; 183218b94f66SAravind Gopalakrishnan 183318b94f66SAravind Gopalakrishnan return cs_found; 183418b94f66SAravind Gopalakrishnan } 183518b94f66SAravind Gopalakrishnan 183618b94f66SAravind Gopalakrishnan static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt, 183718b94f66SAravind Gopalakrishnan u64 sys_addr, 183833ca0643SBorislav Petkov int *chan_sel) 1839f71d0a05SDoug Thompson { 1840e761359aSBorislav Petkov int cs_found = -EINVAL; 1841e761359aSBorislav Petkov unsigned range; 1842f71d0a05SDoug Thompson 18437f19bf75SBorislav Petkov for (range = 0; range < DRAM_RANGES; range++) { 18447f19bf75SBorislav Petkov if (!dram_rw(pvt, range)) 1845f71d0a05SDoug Thompson continue; 1846f71d0a05SDoug Thompson 184718b94f66SAravind Gopalakrishnan if (pvt->fam == 0x15 && pvt->model >= 0x30) 184818b94f66SAravind Gopalakrishnan cs_found = f15_m30h_match_to_this_node(pvt, range, 184918b94f66SAravind Gopalakrishnan sys_addr, 185018b94f66SAravind Gopalakrishnan chan_sel); 1851f71d0a05SDoug Thompson 185218b94f66SAravind Gopalakrishnan else if ((get_dram_base(pvt, range) <= sys_addr) && 185318b94f66SAravind Gopalakrishnan (get_dram_limit(pvt, range) >= sys_addr)) { 1854b15f0fcaSBorislav Petkov cs_found = f1x_match_to_this_node(pvt, range, 185533ca0643SBorislav Petkov sys_addr, chan_sel); 1856f71d0a05SDoug Thompson if (cs_found >= 0) 1857f71d0a05SDoug Thompson break; 1858f71d0a05SDoug Thompson } 1859f71d0a05SDoug Thompson } 1860f71d0a05SDoug Thompson return cs_found; 1861f71d0a05SDoug Thompson } 1862f71d0a05SDoug Thompson 1863f71d0a05SDoug Thompson /* 1864bdc30a0cSBorislav Petkov * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps 1865bdc30a0cSBorislav Petkov * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW). 1866f71d0a05SDoug Thompson * 1867bdc30a0cSBorislav Petkov * The @sys_addr is usually an error address received from the hardware 1868bdc30a0cSBorislav Petkov * (MCX_ADDR). 1869f71d0a05SDoug Thompson */ 1870b15f0fcaSBorislav Petkov static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr, 187133ca0643SBorislav Petkov struct err_info *err) 1872f71d0a05SDoug Thompson { 1873f71d0a05SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 1874f71d0a05SDoug Thompson 187533ca0643SBorislav Petkov error_address_to_page_and_offset(sys_addr, err); 1876ab5a503cSMauro Carvalho Chehab 187733ca0643SBorislav Petkov err->csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &err->channel); 187833ca0643SBorislav Petkov if (err->csrow < 0) { 187933ca0643SBorislav Petkov err->err_code = ERR_CSROW; 1880bdc30a0cSBorislav Petkov return; 1881bdc30a0cSBorislav Petkov } 1882bdc30a0cSBorislav Petkov 1883f71d0a05SDoug Thompson /* 1884bdc30a0cSBorislav Petkov * We need the syndromes for channel detection only when we're 1885bdc30a0cSBorislav Petkov * ganged. Otherwise @chan should already contain the channel at 1886bdc30a0cSBorislav Petkov * this point. 1887f71d0a05SDoug Thompson */ 1888a97fa68eSBorislav Petkov if (dct_ganging_enabled(pvt)) 188933ca0643SBorislav Petkov err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome); 1890f71d0a05SDoug Thompson } 1891f71d0a05SDoug Thompson 1892f71d0a05SDoug Thompson /* 18938566c4dfSBorislav Petkov * debug routine to display the memory sizes of all logical DIMMs and its 1894cb328507SBorislav Petkov * CSROWs 1895f71d0a05SDoug Thompson */ 1896d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl) 1897f71d0a05SDoug Thompson { 1898bb89f5a0SBorislav Petkov int dimm, size0, size1; 1899525a1b20SBorislav Petkov u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases; 1900525a1b20SBorislav Petkov u32 dbam = ctrl ? pvt->dbam1 : pvt->dbam0; 1901f71d0a05SDoug Thompson 1902a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) { 19038566c4dfSBorislav Petkov /* K8 families < revF not supported yet */ 19041433eb99SBorislav Petkov if (pvt->ext_model < K8_REV_F) 19058566c4dfSBorislav Petkov return; 19068566c4dfSBorislav Petkov else 19078566c4dfSBorislav Petkov WARN_ON(ctrl != 0); 19088566c4dfSBorislav Petkov } 19098566c4dfSBorislav Petkov 19107981a28fSAravind Gopalakrishnan if (pvt->fam == 0x10) { 19117981a28fSAravind Gopalakrishnan dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1 19127981a28fSAravind Gopalakrishnan : pvt->dbam0; 19137981a28fSAravind Gopalakrishnan dcsb = (ctrl && !dct_ganging_enabled(pvt)) ? 19147981a28fSAravind Gopalakrishnan pvt->csels[1].csbases : 19157981a28fSAravind Gopalakrishnan pvt->csels[0].csbases; 19167981a28fSAravind Gopalakrishnan } else if (ctrl) { 19177981a28fSAravind Gopalakrishnan dbam = pvt->dbam0; 19187981a28fSAravind Gopalakrishnan dcsb = pvt->csels[1].csbases; 19197981a28fSAravind Gopalakrishnan } 1920956b9ba1SJoe Perches edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n", 1921956b9ba1SJoe Perches ctrl, dbam); 1922f71d0a05SDoug Thompson 19238566c4dfSBorislav Petkov edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl); 19248566c4dfSBorislav Petkov 1925f71d0a05SDoug Thompson /* Dump memory sizes for DIMM and its CSROWs */ 1926f71d0a05SDoug Thompson for (dimm = 0; dimm < 4; dimm++) { 1927f71d0a05SDoug Thompson 1928f71d0a05SDoug Thompson size0 = 0; 192911c75eadSBorislav Petkov if (dcsb[dimm*2] & DCSB_CS_ENABLE) 1930a597d2a5SAravind Gopalakrishnan /* For f15m60h, need multiplier for LRDIMM cs_size 1931a597d2a5SAravind Gopalakrishnan * calculation. We pass 'dimm' value to the dbam_to_cs 1932a597d2a5SAravind Gopalakrishnan * mapper so we can find the multiplier from the 1933a597d2a5SAravind Gopalakrishnan * corresponding DCSM. 1934a597d2a5SAravind Gopalakrishnan */ 193541d8bfabSBorislav Petkov size0 = pvt->ops->dbam_to_cs(pvt, ctrl, 1936a597d2a5SAravind Gopalakrishnan DBAM_DIMM(dimm, dbam), 1937a597d2a5SAravind Gopalakrishnan dimm); 1938f71d0a05SDoug Thompson 1939f71d0a05SDoug Thompson size1 = 0; 194011c75eadSBorislav Petkov if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE) 194141d8bfabSBorislav Petkov size1 = pvt->ops->dbam_to_cs(pvt, ctrl, 1942a597d2a5SAravind Gopalakrishnan DBAM_DIMM(dimm, dbam), 1943a597d2a5SAravind Gopalakrishnan dimm); 1944f71d0a05SDoug Thompson 194524f9a7feSBorislav Petkov amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n", 1946bb89f5a0SBorislav Petkov dimm * 2, size0, 1947bb89f5a0SBorislav Petkov dimm * 2 + 1, size1); 1948f71d0a05SDoug Thompson } 1949f71d0a05SDoug Thompson } 1950f71d0a05SDoug Thompson 1951d1ea71cdSBorislav Petkov static struct amd64_family_type family_types[] = { 19524d37607aSDoug Thompson [K8_CPUS] = { 19530092b20dSBorislav Petkov .ctl_name = "K8", 19548d5b5d9cSBorislav Petkov .f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP, 19553f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL, 19564d37607aSDoug Thompson .ops = { 19574d37607aSDoug Thompson .early_channel_count = k8_early_channel_count, 19584d37607aSDoug Thompson .map_sysaddr_to_csrow = k8_map_sysaddr_to_csrow, 19591433eb99SBorislav Petkov .dbam_to_cs = k8_dbam_to_chip_select, 19604d37607aSDoug Thompson } 19614d37607aSDoug Thompson }, 19624d37607aSDoug Thompson [F10_CPUS] = { 19630092b20dSBorislav Petkov .ctl_name = "F10h", 19648d5b5d9cSBorislav Petkov .f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP, 19653f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM, 19664d37607aSDoug Thompson .ops = { 19677d20d14dSBorislav Petkov .early_channel_count = f1x_early_channel_count, 1968b15f0fcaSBorislav Petkov .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 19691433eb99SBorislav Petkov .dbam_to_cs = f10_dbam_to_chip_select, 1970b2b0c605SBorislav Petkov } 1971b2b0c605SBorislav Petkov }, 1972b2b0c605SBorislav Petkov [F15_CPUS] = { 1973b2b0c605SBorislav Petkov .ctl_name = "F15h", 1974df71a053SBorislav Petkov .f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1, 19753f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2, 1976b2b0c605SBorislav Petkov .ops = { 19777d20d14dSBorislav Petkov .early_channel_count = f1x_early_channel_count, 1978b15f0fcaSBorislav Petkov .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 197941d8bfabSBorislav Petkov .dbam_to_cs = f15_dbam_to_chip_select, 19804d37607aSDoug Thompson } 19814d37607aSDoug Thompson }, 198218b94f66SAravind Gopalakrishnan [F15_M30H_CPUS] = { 198318b94f66SAravind Gopalakrishnan .ctl_name = "F15h_M30h", 198418b94f66SAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1, 19853f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2, 198618b94f66SAravind Gopalakrishnan .ops = { 198718b94f66SAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 198818b94f66SAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 198918b94f66SAravind Gopalakrishnan .dbam_to_cs = f16_dbam_to_chip_select, 199018b94f66SAravind Gopalakrishnan } 199118b94f66SAravind Gopalakrishnan }, 1992a597d2a5SAravind Gopalakrishnan [F15_M60H_CPUS] = { 1993a597d2a5SAravind Gopalakrishnan .ctl_name = "F15h_M60h", 1994a597d2a5SAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1, 19953f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2, 1996a597d2a5SAravind Gopalakrishnan .ops = { 1997a597d2a5SAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 1998a597d2a5SAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 1999a597d2a5SAravind Gopalakrishnan .dbam_to_cs = f15_m60h_dbam_to_chip_select, 2000a597d2a5SAravind Gopalakrishnan } 2001a597d2a5SAravind Gopalakrishnan }, 200294c1acf2SAravind Gopalakrishnan [F16_CPUS] = { 200394c1acf2SAravind Gopalakrishnan .ctl_name = "F16h", 200494c1acf2SAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1, 20053f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2, 200694c1acf2SAravind Gopalakrishnan .ops = { 200794c1acf2SAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 200894c1acf2SAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 200994c1acf2SAravind Gopalakrishnan .dbam_to_cs = f16_dbam_to_chip_select, 201094c1acf2SAravind Gopalakrishnan } 201194c1acf2SAravind Gopalakrishnan }, 201285a8885bSAravind Gopalakrishnan [F16_M30H_CPUS] = { 201385a8885bSAravind Gopalakrishnan .ctl_name = "F16h_M30h", 201485a8885bSAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F1, 20153f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2, 201685a8885bSAravind Gopalakrishnan .ops = { 201785a8885bSAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 201885a8885bSAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 201985a8885bSAravind Gopalakrishnan .dbam_to_cs = f16_dbam_to_chip_select, 202085a8885bSAravind Gopalakrishnan } 202185a8885bSAravind Gopalakrishnan }, 2022f1cbbec9SYazen Ghannam [F17_CPUS] = { 2023f1cbbec9SYazen Ghannam .ctl_name = "F17h", 2024f1cbbec9SYazen Ghannam .f0_id = PCI_DEVICE_ID_AMD_17H_DF_F0, 2025f1cbbec9SYazen Ghannam .f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6, 2026f1cbbec9SYazen Ghannam .ops = { 2027f1cbbec9SYazen Ghannam .early_channel_count = f17_early_channel_count, 2028f1cbbec9SYazen Ghannam .dbam_to_cs = f17_base_addr_to_cs_size, 2029f1cbbec9SYazen Ghannam } 2030f1cbbec9SYazen Ghannam }, 20314d37607aSDoug Thompson }; 20324d37607aSDoug Thompson 2033b1289d6fSDoug Thompson /* 2034bfc04aecSBorislav Petkov * These are tables of eigenvectors (one per line) which can be used for the 2035bfc04aecSBorislav Petkov * construction of the syndrome tables. The modified syndrome search algorithm 2036bfc04aecSBorislav Petkov * uses those to find the symbol in error and thus the DIMM. 2037b1289d6fSDoug Thompson * 2038bfc04aecSBorislav Petkov * Algorithm courtesy of Ross LaFetra from AMD. 2039b1289d6fSDoug Thompson */ 2040c7e5301aSDaniel J Blueman static const u16 x4_vectors[] = { 2041bfc04aecSBorislav Petkov 0x2f57, 0x1afe, 0x66cc, 0xdd88, 2042bfc04aecSBorislav Petkov 0x11eb, 0x3396, 0x7f4c, 0xeac8, 2043bfc04aecSBorislav Petkov 0x0001, 0x0002, 0x0004, 0x0008, 2044bfc04aecSBorislav Petkov 0x1013, 0x3032, 0x4044, 0x8088, 2045bfc04aecSBorislav Petkov 0x106b, 0x30d6, 0x70fc, 0xe0a8, 2046bfc04aecSBorislav Petkov 0x4857, 0xc4fe, 0x13cc, 0x3288, 2047bfc04aecSBorislav Petkov 0x1ac5, 0x2f4a, 0x5394, 0xa1e8, 2048bfc04aecSBorislav Petkov 0x1f39, 0x251e, 0xbd6c, 0x6bd8, 2049bfc04aecSBorislav Petkov 0x15c1, 0x2a42, 0x89ac, 0x4758, 2050bfc04aecSBorislav Petkov 0x2b03, 0x1602, 0x4f0c, 0xca08, 2051bfc04aecSBorislav Petkov 0x1f07, 0x3a0e, 0x6b04, 0xbd08, 2052bfc04aecSBorislav Petkov 0x8ba7, 0x465e, 0x244c, 0x1cc8, 2053bfc04aecSBorislav Petkov 0x2b87, 0x164e, 0x642c, 0xdc18, 2054bfc04aecSBorislav Petkov 0x40b9, 0x80de, 0x1094, 0x20e8, 2055bfc04aecSBorislav Petkov 0x27db, 0x1eb6, 0x9dac, 0x7b58, 2056bfc04aecSBorislav Petkov 0x11c1, 0x2242, 0x84ac, 0x4c58, 2057bfc04aecSBorislav Petkov 0x1be5, 0x2d7a, 0x5e34, 0xa718, 2058bfc04aecSBorislav Petkov 0x4b39, 0x8d1e, 0x14b4, 0x28d8, 2059bfc04aecSBorislav Petkov 0x4c97, 0xc87e, 0x11fc, 0x33a8, 2060bfc04aecSBorislav Petkov 0x8e97, 0x497e, 0x2ffc, 0x1aa8, 2061bfc04aecSBorislav Petkov 0x16b3, 0x3d62, 0x4f34, 0x8518, 2062bfc04aecSBorislav Petkov 0x1e2f, 0x391a, 0x5cac, 0xf858, 2063bfc04aecSBorislav Petkov 0x1d9f, 0x3b7a, 0x572c, 0xfe18, 2064bfc04aecSBorislav Petkov 0x15f5, 0x2a5a, 0x5264, 0xa3b8, 2065bfc04aecSBorislav Petkov 0x1dbb, 0x3b66, 0x715c, 0xe3f8, 2066bfc04aecSBorislav Petkov 0x4397, 0xc27e, 0x17fc, 0x3ea8, 2067bfc04aecSBorislav Petkov 0x1617, 0x3d3e, 0x6464, 0xb8b8, 2068bfc04aecSBorislav Petkov 0x23ff, 0x12aa, 0xab6c, 0x56d8, 2069bfc04aecSBorislav Petkov 0x2dfb, 0x1ba6, 0x913c, 0x7328, 2070bfc04aecSBorislav Petkov 0x185d, 0x2ca6, 0x7914, 0x9e28, 2071bfc04aecSBorislav Petkov 0x171b, 0x3e36, 0x7d7c, 0xebe8, 2072bfc04aecSBorislav Petkov 0x4199, 0x82ee, 0x19f4, 0x2e58, 2073bfc04aecSBorislav Petkov 0x4807, 0xc40e, 0x130c, 0x3208, 2074bfc04aecSBorislav Petkov 0x1905, 0x2e0a, 0x5804, 0xac08, 2075bfc04aecSBorislav Petkov 0x213f, 0x132a, 0xadfc, 0x5ba8, 2076bfc04aecSBorislav Petkov 0x19a9, 0x2efe, 0xb5cc, 0x6f88, 2077b1289d6fSDoug Thompson }; 2078b1289d6fSDoug Thompson 2079c7e5301aSDaniel J Blueman static const u16 x8_vectors[] = { 2080bfc04aecSBorislav Petkov 0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480, 2081bfc04aecSBorislav Petkov 0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80, 2082bfc04aecSBorislav Petkov 0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80, 2083bfc04aecSBorislav Petkov 0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80, 2084bfc04aecSBorislav Petkov 0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780, 2085bfc04aecSBorislav Petkov 0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080, 2086bfc04aecSBorislav Petkov 0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080, 2087bfc04aecSBorislav Petkov 0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080, 2088bfc04aecSBorislav Petkov 0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80, 2089bfc04aecSBorislav Petkov 0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580, 2090bfc04aecSBorislav Petkov 0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880, 2091bfc04aecSBorislav Petkov 0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280, 2092bfc04aecSBorislav Petkov 0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180, 2093bfc04aecSBorislav Petkov 0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580, 2094bfc04aecSBorislav Petkov 0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280, 2095bfc04aecSBorislav Petkov 0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180, 2096bfc04aecSBorislav Petkov 0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080, 2097bfc04aecSBorislav Petkov 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 2098bfc04aecSBorislav Petkov 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000, 2099bfc04aecSBorislav Petkov }; 2100bfc04aecSBorislav Petkov 2101c7e5301aSDaniel J Blueman static int decode_syndrome(u16 syndrome, const u16 *vectors, unsigned num_vecs, 2102d34a6ecdSBorislav Petkov unsigned v_dim) 2103b1289d6fSDoug Thompson { 2104bfc04aecSBorislav Petkov unsigned int i, err_sym; 2105b1289d6fSDoug Thompson 2106bfc04aecSBorislav Petkov for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) { 2107bfc04aecSBorislav Petkov u16 s = syndrome; 2108d34a6ecdSBorislav Petkov unsigned v_idx = err_sym * v_dim; 2109d34a6ecdSBorislav Petkov unsigned v_end = (err_sym + 1) * v_dim; 2110b1289d6fSDoug Thompson 2111bfc04aecSBorislav Petkov /* walk over all 16 bits of the syndrome */ 2112bfc04aecSBorislav Petkov for (i = 1; i < (1U << 16); i <<= 1) { 2113bfc04aecSBorislav Petkov 2114bfc04aecSBorislav Petkov /* if bit is set in that eigenvector... */ 2115bfc04aecSBorislav Petkov if (v_idx < v_end && vectors[v_idx] & i) { 2116bfc04aecSBorislav Petkov u16 ev_comp = vectors[v_idx++]; 2117bfc04aecSBorislav Petkov 2118bfc04aecSBorislav Petkov /* ... and bit set in the modified syndrome, */ 2119bfc04aecSBorislav Petkov if (s & i) { 2120bfc04aecSBorislav Petkov /* remove it. */ 2121bfc04aecSBorislav Petkov s ^= ev_comp; 2122bfc04aecSBorislav Petkov 2123bfc04aecSBorislav Petkov if (!s) 2124bfc04aecSBorislav Petkov return err_sym; 2125bfc04aecSBorislav Petkov } 2126bfc04aecSBorislav Petkov 2127bfc04aecSBorislav Petkov } else if (s & i) 2128bfc04aecSBorislav Petkov /* can't get to zero, move to next symbol */ 2129bfc04aecSBorislav Petkov break; 2130bfc04aecSBorislav Petkov } 2131b1289d6fSDoug Thompson } 2132b1289d6fSDoug Thompson 2133956b9ba1SJoe Perches edac_dbg(0, "syndrome(%x) not found\n", syndrome); 2134b1289d6fSDoug Thompson return -1; 2135b1289d6fSDoug Thompson } 2136d27bf6faSDoug Thompson 2137bfc04aecSBorislav Petkov static int map_err_sym_to_channel(int err_sym, int sym_size) 2138bfc04aecSBorislav Petkov { 2139bfc04aecSBorislav Petkov if (sym_size == 4) 2140bfc04aecSBorislav Petkov switch (err_sym) { 2141bfc04aecSBorislav Petkov case 0x20: 2142bfc04aecSBorislav Petkov case 0x21: 2143bfc04aecSBorislav Petkov return 0; 2144bfc04aecSBorislav Petkov break; 2145bfc04aecSBorislav Petkov case 0x22: 2146bfc04aecSBorislav Petkov case 0x23: 2147bfc04aecSBorislav Petkov return 1; 2148bfc04aecSBorislav Petkov break; 2149bfc04aecSBorislav Petkov default: 2150bfc04aecSBorislav Petkov return err_sym >> 4; 2151bfc04aecSBorislav Petkov break; 2152bfc04aecSBorislav Petkov } 2153bfc04aecSBorislav Petkov /* x8 symbols */ 2154bfc04aecSBorislav Petkov else 2155bfc04aecSBorislav Petkov switch (err_sym) { 2156bfc04aecSBorislav Petkov /* imaginary bits not in a DIMM */ 2157bfc04aecSBorislav Petkov case 0x10: 2158bfc04aecSBorislav Petkov WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n", 2159bfc04aecSBorislav Petkov err_sym); 2160bfc04aecSBorislav Petkov return -1; 2161bfc04aecSBorislav Petkov break; 2162bfc04aecSBorislav Petkov 2163bfc04aecSBorislav Petkov case 0x11: 2164bfc04aecSBorislav Petkov return 0; 2165bfc04aecSBorislav Petkov break; 2166bfc04aecSBorislav Petkov case 0x12: 2167bfc04aecSBorislav Petkov return 1; 2168bfc04aecSBorislav Petkov break; 2169bfc04aecSBorislav Petkov default: 2170bfc04aecSBorislav Petkov return err_sym >> 3; 2171bfc04aecSBorislav Petkov break; 2172bfc04aecSBorislav Petkov } 2173bfc04aecSBorislav Petkov return -1; 2174bfc04aecSBorislav Petkov } 2175bfc04aecSBorislav Petkov 2176bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome) 2177bfc04aecSBorislav Petkov { 2178bfc04aecSBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 2179ad6a32e9SBorislav Petkov int err_sym = -1; 2180bfc04aecSBorislav Petkov 2181a3b7db09SBorislav Petkov if (pvt->ecc_sym_sz == 8) 2182bfc04aecSBorislav Petkov err_sym = decode_syndrome(syndrome, x8_vectors, 2183ad6a32e9SBorislav Petkov ARRAY_SIZE(x8_vectors), 2184a3b7db09SBorislav Petkov pvt->ecc_sym_sz); 2185a3b7db09SBorislav Petkov else if (pvt->ecc_sym_sz == 4) 2186ad6a32e9SBorislav Petkov err_sym = decode_syndrome(syndrome, x4_vectors, 2187ad6a32e9SBorislav Petkov ARRAY_SIZE(x4_vectors), 2188a3b7db09SBorislav Petkov pvt->ecc_sym_sz); 2189ad6a32e9SBorislav Petkov else { 2190a3b7db09SBorislav Petkov amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz); 2191ad6a32e9SBorislav Petkov return err_sym; 2192bfc04aecSBorislav Petkov } 2193ad6a32e9SBorislav Petkov 2194a3b7db09SBorislav Petkov return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz); 219541c31044SBorislav Petkov } 2196bfc04aecSBorislav Petkov 2197e70984d9SYazen Ghannam static void __log_ecc_error(struct mem_ctl_info *mci, struct err_info *err, 219833ca0643SBorislav Petkov u8 ecc_type) 2199d27bf6faSDoug Thompson { 220033ca0643SBorislav Petkov enum hw_event_mc_err_type err_type; 220133ca0643SBorislav Petkov const char *string; 2202d27bf6faSDoug Thompson 220333ca0643SBorislav Petkov if (ecc_type == 2) 220433ca0643SBorislav Petkov err_type = HW_EVENT_ERR_CORRECTED; 220533ca0643SBorislav Petkov else if (ecc_type == 1) 220633ca0643SBorislav Petkov err_type = HW_EVENT_ERR_UNCORRECTED; 2207d12a969eSYazen Ghannam else if (ecc_type == 3) 2208d12a969eSYazen Ghannam err_type = HW_EVENT_ERR_DEFERRED; 220933ca0643SBorislav Petkov else { 221033ca0643SBorislav Petkov WARN(1, "Something is rotten in the state of Denmark.\n"); 2211d27bf6faSDoug Thompson return; 2212d27bf6faSDoug Thompson } 2213d27bf6faSDoug Thompson 221433ca0643SBorislav Petkov switch (err->err_code) { 221533ca0643SBorislav Petkov case DECODE_OK: 221633ca0643SBorislav Petkov string = ""; 221733ca0643SBorislav Petkov break; 221833ca0643SBorislav Petkov case ERR_NODE: 221933ca0643SBorislav Petkov string = "Failed to map error addr to a node"; 222033ca0643SBorislav Petkov break; 222133ca0643SBorislav Petkov case ERR_CSROW: 222233ca0643SBorislav Petkov string = "Failed to map error addr to a csrow"; 222333ca0643SBorislav Petkov break; 222433ca0643SBorislav Petkov case ERR_CHANNEL: 222533ca0643SBorislav Petkov string = "unknown syndrome - possible error reporting race"; 222633ca0643SBorislav Petkov break; 222733ca0643SBorislav Petkov default: 222833ca0643SBorislav Petkov string = "WTF error"; 222933ca0643SBorislav Petkov break; 2230d27bf6faSDoug Thompson } 223133ca0643SBorislav Petkov 223233ca0643SBorislav Petkov edac_mc_handle_error(err_type, mci, 1, 223333ca0643SBorislav Petkov err->page, err->offset, err->syndrome, 223433ca0643SBorislav Petkov err->csrow, err->channel, -1, 223533ca0643SBorislav Petkov string, ""); 2236d27bf6faSDoug Thompson } 2237d27bf6faSDoug Thompson 2238df781d03SBorislav Petkov static inline void decode_bus_error(int node_id, struct mce *m) 2239d27bf6faSDoug Thompson { 22400c510cc8SDaniel J Blueman struct mem_ctl_info *mci; 22410c510cc8SDaniel J Blueman struct amd64_pvt *pvt; 2242f192c7b1SBorislav Petkov u8 ecc_type = (m->status >> 45) & 0x3; 224366fed2d4SBorislav Petkov u8 xec = XEC(m->status, 0x1f); 224466fed2d4SBorislav Petkov u16 ec = EC(m->status); 224533ca0643SBorislav Petkov u64 sys_addr; 224633ca0643SBorislav Petkov struct err_info err; 2247d27bf6faSDoug Thompson 22480c510cc8SDaniel J Blueman mci = edac_mc_find(node_id); 22490c510cc8SDaniel J Blueman if (!mci) 22500c510cc8SDaniel J Blueman return; 22510c510cc8SDaniel J Blueman 22520c510cc8SDaniel J Blueman pvt = mci->pvt_info; 22530c510cc8SDaniel J Blueman 225466fed2d4SBorislav Petkov /* Bail out early if this was an 'observed' error */ 22555980bb9cSBorislav Petkov if (PP(ec) == NBSL_PP_OBS) 2256b70ef010SBorislav Petkov return; 2257d27bf6faSDoug Thompson 2258ecaf5606SBorislav Petkov /* Do only ECC errors */ 2259ecaf5606SBorislav Petkov if (xec && xec != F10_NBSL_EXT_ERR_ECC) 2260d27bf6faSDoug Thompson return; 2261d27bf6faSDoug Thompson 226233ca0643SBorislav Petkov memset(&err, 0, sizeof(err)); 226333ca0643SBorislav Petkov 2264a4b4bedcSBorislav Petkov sys_addr = get_error_address(pvt, m); 226533ca0643SBorislav Petkov 2266ecaf5606SBorislav Petkov if (ecc_type == 2) 226733ca0643SBorislav Petkov err.syndrome = extract_syndrome(m->status); 226833ca0643SBorislav Petkov 226933ca0643SBorislav Petkov pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err); 227033ca0643SBorislav Petkov 2271e70984d9SYazen Ghannam __log_ecc_error(mci, &err, ecc_type); 2272d27bf6faSDoug Thompson } 2273d27bf6faSDoug Thompson 22740ec449eeSDoug Thompson /* 22753f37a36bSBorislav Petkov * Use pvt->F3 which contains the F3 CPU PCI device to get the related 22763f37a36bSBorislav Petkov * F1 (AddrMap) and F2 (Dct) devices. Return negative value on error. 22770ec449eeSDoug Thompson */ 22783f37a36bSBorislav Petkov static int reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 f1_id, u16 f2_id) 22790ec449eeSDoug Thompson { 22800ec449eeSDoug Thompson /* Reserve the ADDRESS MAP Device */ 22813f37a36bSBorislav Petkov pvt->F1 = pci_get_related_function(pvt->F3->vendor, f1_id, pvt->F3); 22828d5b5d9cSBorislav Petkov if (!pvt->F1) { 228324f9a7feSBorislav Petkov amd64_err("error address map device not found: " 22840ec449eeSDoug Thompson "vendor %x device 0x%x (broken BIOS?)\n", 2285bbd0c1f6SBorislav Petkov PCI_VENDOR_ID_AMD, f1_id); 2286bbd0c1f6SBorislav Petkov return -ENODEV; 22870ec449eeSDoug Thompson } 22880ec449eeSDoug Thompson 22893f37a36bSBorislav Petkov /* Reserve the DCT Device */ 22903f37a36bSBorislav Petkov pvt->F2 = pci_get_related_function(pvt->F3->vendor, f2_id, pvt->F3); 22913f37a36bSBorislav Petkov if (!pvt->F2) { 22928d5b5d9cSBorislav Petkov pci_dev_put(pvt->F1); 22938d5b5d9cSBorislav Petkov pvt->F1 = NULL; 22940ec449eeSDoug Thompson 22953f37a36bSBorislav Petkov amd64_err("error F2 device not found: " 22960ec449eeSDoug Thompson "vendor %x device 0x%x (broken BIOS?)\n", 22973f37a36bSBorislav Petkov PCI_VENDOR_ID_AMD, f2_id); 22988d5b5d9cSBorislav Petkov 2299bbd0c1f6SBorislav Petkov return -ENODEV; 23000ec449eeSDoug Thompson } 2301956b9ba1SJoe Perches edac_dbg(1, "F1: %s\n", pci_name(pvt->F1)); 2302956b9ba1SJoe Perches edac_dbg(1, "F2: %s\n", pci_name(pvt->F2)); 2303956b9ba1SJoe Perches edac_dbg(1, "F3: %s\n", pci_name(pvt->F3)); 23040ec449eeSDoug Thompson 23050ec449eeSDoug Thompson return 0; 23060ec449eeSDoug Thompson } 23070ec449eeSDoug Thompson 2308360b7f3cSBorislav Petkov static void free_mc_sibling_devs(struct amd64_pvt *pvt) 23090ec449eeSDoug Thompson { 23108d5b5d9cSBorislav Petkov pci_dev_put(pvt->F1); 23113f37a36bSBorislav Petkov pci_dev_put(pvt->F2); 23120ec449eeSDoug Thompson } 23130ec449eeSDoug Thompson 23140ec449eeSDoug Thompson /* 23150ec449eeSDoug Thompson * Retrieve the hardware registers of the memory controller (this includes the 23160ec449eeSDoug Thompson * 'Address Map' and 'Misc' device regs) 23170ec449eeSDoug Thompson */ 2318360b7f3cSBorislav Petkov static void read_mc_regs(struct amd64_pvt *pvt) 23190ec449eeSDoug Thompson { 2320a4b4bedcSBorislav Petkov unsigned range; 23210ec449eeSDoug Thompson u64 msr_val; 2322ad6a32e9SBorislav Petkov u32 tmp; 23230ec449eeSDoug Thompson 23240ec449eeSDoug Thompson /* 23250ec449eeSDoug Thompson * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since 23260ec449eeSDoug Thompson * those are Read-As-Zero 23270ec449eeSDoug Thompson */ 2328e97f8bb8SBorislav Petkov rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem); 2329956b9ba1SJoe Perches edac_dbg(0, " TOP_MEM: 0x%016llx\n", pvt->top_mem); 23300ec449eeSDoug Thompson 23310ec449eeSDoug Thompson /* check first whether TOP_MEM2 is enabled */ 23320ec449eeSDoug Thompson rdmsrl(MSR_K8_SYSCFG, msr_val); 23330ec449eeSDoug Thompson if (msr_val & (1U << 21)) { 2334e97f8bb8SBorislav Petkov rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2); 2335956b9ba1SJoe Perches edac_dbg(0, " TOP_MEM2: 0x%016llx\n", pvt->top_mem2); 23360ec449eeSDoug Thompson } else 2337956b9ba1SJoe Perches edac_dbg(0, " TOP_MEM2 disabled\n"); 23380ec449eeSDoug Thompson 23395980bb9cSBorislav Petkov amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap); 23400ec449eeSDoug Thompson 23415a5d2371SBorislav Petkov read_dram_ctl_register(pvt); 23420ec449eeSDoug Thompson 23437f19bf75SBorislav Petkov for (range = 0; range < DRAM_RANGES; range++) { 23447f19bf75SBorislav Petkov u8 rw; 23450ec449eeSDoug Thompson 23467f19bf75SBorislav Petkov /* read settings for this DRAM range */ 23477f19bf75SBorislav Petkov read_dram_base_limit_regs(pvt, range); 2348e97f8bb8SBorislav Petkov 23497f19bf75SBorislav Petkov rw = dram_rw(pvt, range); 23507f19bf75SBorislav Petkov if (!rw) 23517f19bf75SBorislav Petkov continue; 23527f19bf75SBorislav Petkov 2353956b9ba1SJoe Perches edac_dbg(1, " DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n", 23547f19bf75SBorislav Petkov range, 23557f19bf75SBorislav Petkov get_dram_base(pvt, range), 23567f19bf75SBorislav Petkov get_dram_limit(pvt, range)); 23577f19bf75SBorislav Petkov 2358956b9ba1SJoe Perches edac_dbg(1, " IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n", 23597f19bf75SBorislav Petkov dram_intlv_en(pvt, range) ? "Enabled" : "Disabled", 23607f19bf75SBorislav Petkov (rw & 0x1) ? "R" : "-", 23617f19bf75SBorislav Petkov (rw & 0x2) ? "W" : "-", 23627f19bf75SBorislav Petkov dram_intlv_sel(pvt, range), 23637f19bf75SBorislav Petkov dram_dst_node(pvt, range)); 23640ec449eeSDoug Thompson } 23650ec449eeSDoug Thompson 2366b2b0c605SBorislav Petkov read_dct_base_mask(pvt); 23670ec449eeSDoug Thompson 2368bc21fa57SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar); 23697981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DBAM0, &pvt->dbam0); 23700ec449eeSDoug Thompson 23718d5b5d9cSBorislav Petkov amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare); 23720ec449eeSDoug Thompson 23737981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DCLR0, &pvt->dclr0); 23747981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DCHR0, &pvt->dchr0); 23750ec449eeSDoug Thompson 237678da121eSBorislav Petkov if (!dct_ganging_enabled(pvt)) { 23777981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 1, DCLR0, &pvt->dclr1); 23787981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 1, DCHR0, &pvt->dchr1); 23790ec449eeSDoug Thompson } 2380b2b0c605SBorislav Petkov 2381a3b7db09SBorislav Petkov pvt->ecc_sym_sz = 4; 2382a597d2a5SAravind Gopalakrishnan determine_memory_type(pvt); 2383a597d2a5SAravind Gopalakrishnan edac_dbg(1, " DIMM type: %s\n", edac_mem_types[pvt->dram_type]); 2384a3b7db09SBorislav Petkov 2385a4b4bedcSBorislav Petkov if (pvt->fam >= 0x10) { 23868d5b5d9cSBorislav Petkov amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp); 23877981a28fSAravind Gopalakrishnan /* F16h has only DCT0, so no need to read dbam1 */ 2388a4b4bedcSBorislav Petkov if (pvt->fam != 0x16) 23897981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 1, DBAM0, &pvt->dbam1); 2390a3b7db09SBorislav Petkov 2391a3b7db09SBorislav Petkov /* F10h, revD and later can do x8 ECC too */ 2392a4b4bedcSBorislav Petkov if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25)) 2393a3b7db09SBorislav Petkov pvt->ecc_sym_sz = 8; 2394525a1b20SBorislav Petkov } 2395b2b0c605SBorislav Petkov dump_misc_regs(pvt); 23960ec449eeSDoug Thompson } 23970ec449eeSDoug Thompson 23980ec449eeSDoug Thompson /* 23990ec449eeSDoug Thompson * NOTE: CPU Revision Dependent code 24000ec449eeSDoug Thompson * 24010ec449eeSDoug Thompson * Input: 240211c75eadSBorislav Petkov * @csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1) 24030ec449eeSDoug Thompson * k8 private pointer to --> 24040ec449eeSDoug Thompson * DRAM Bank Address mapping register 24050ec449eeSDoug Thompson * node_id 24060ec449eeSDoug Thompson * DCL register where dual_channel_active is 24070ec449eeSDoug Thompson * 24080ec449eeSDoug Thompson * The DBAM register consists of 4 sets of 4 bits each definitions: 24090ec449eeSDoug Thompson * 24100ec449eeSDoug Thompson * Bits: CSROWs 24110ec449eeSDoug Thompson * 0-3 CSROWs 0 and 1 24120ec449eeSDoug Thompson * 4-7 CSROWs 2 and 3 24130ec449eeSDoug Thompson * 8-11 CSROWs 4 and 5 24140ec449eeSDoug Thompson * 12-15 CSROWs 6 and 7 24150ec449eeSDoug Thompson * 24160ec449eeSDoug Thompson * Values range from: 0 to 15 24170ec449eeSDoug Thompson * The meaning of the values depends on CPU revision and dual-channel state, 24180ec449eeSDoug Thompson * see relevant BKDG more info. 24190ec449eeSDoug Thompson * 24200ec449eeSDoug Thompson * The memory controller provides for total of only 8 CSROWs in its current 24210ec449eeSDoug Thompson * architecture. Each "pair" of CSROWs normally represents just one DIMM in 24220ec449eeSDoug Thompson * single channel or two (2) DIMMs in dual channel mode. 24230ec449eeSDoug Thompson * 24240ec449eeSDoug Thompson * The following code logic collapses the various tables for CSROW based on CPU 24250ec449eeSDoug Thompson * revision. 24260ec449eeSDoug Thompson * 24270ec449eeSDoug Thompson * Returns: 24280ec449eeSDoug Thompson * The number of PAGE_SIZE pages on the specified CSROW number it 24290ec449eeSDoug Thompson * encompasses 24300ec449eeSDoug Thompson * 24310ec449eeSDoug Thompson */ 2432d1ea71cdSBorislav Petkov static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr) 24330ec449eeSDoug Thompson { 24341433eb99SBorislav Petkov u32 cs_mode, nr_pages; 2435f92cae45SAshish Shenoy u32 dbam = dct ? pvt->dbam1 : pvt->dbam0; 24360ec449eeSDoug Thompson 243710de6497SBorislav Petkov 24380ec449eeSDoug Thompson /* 24390ec449eeSDoug Thompson * The math on this doesn't look right on the surface because x/2*4 can 24400ec449eeSDoug Thompson * be simplified to x*2 but this expression makes use of the fact that 24410ec449eeSDoug Thompson * it is integral math where 1/2=0. This intermediate value becomes the 24420ec449eeSDoug Thompson * number of bits to shift the DBAM register to extract the proper CSROW 24430ec449eeSDoug Thompson * field. 24440ec449eeSDoug Thompson */ 24450a5dfc31SBorislav Petkov cs_mode = DBAM_DIMM(csrow_nr / 2, dbam); 24460ec449eeSDoug Thompson 2447a597d2a5SAravind Gopalakrishnan nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, (csrow_nr / 2)) 2448a597d2a5SAravind Gopalakrishnan << (20 - PAGE_SHIFT); 24490ec449eeSDoug Thompson 245010de6497SBorislav Petkov edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n", 245110de6497SBorislav Petkov csrow_nr, dct, cs_mode); 245210de6497SBorislav Petkov edac_dbg(0, "nr_pages/channel: %u\n", nr_pages); 24530ec449eeSDoug Thompson 24540ec449eeSDoug Thompson return nr_pages; 24550ec449eeSDoug Thompson } 24560ec449eeSDoug Thompson 24570ec449eeSDoug Thompson /* 24580ec449eeSDoug Thompson * Initialize the array of csrow attribute instances, based on the values 24590ec449eeSDoug Thompson * from pci config hardware registers. 24600ec449eeSDoug Thompson */ 2461360b7f3cSBorislav Petkov static int init_csrows(struct mem_ctl_info *mci) 24620ec449eeSDoug Thompson { 246310de6497SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 24640ec449eeSDoug Thompson struct csrow_info *csrow; 2465de3910ebSMauro Carvalho Chehab struct dimm_info *dimm; 2466084a4fccSMauro Carvalho Chehab enum edac_type edac_mode; 246710de6497SBorislav Petkov int i, j, empty = 1; 2468a895bf8bSMauro Carvalho Chehab int nr_pages = 0; 246910de6497SBorislav Petkov u32 val; 24700ec449eeSDoug Thompson 2471a97fa68eSBorislav Petkov amd64_read_pci_cfg(pvt->F3, NBCFG, &val); 24720ec449eeSDoug Thompson 24732299ef71SBorislav Petkov pvt->nbcfg = val; 24740ec449eeSDoug Thompson 2475956b9ba1SJoe Perches edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n", 24762299ef71SBorislav Petkov pvt->mc_node_id, val, 2477a97fa68eSBorislav Petkov !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE)); 24780ec449eeSDoug Thompson 247910de6497SBorislav Petkov /* 248010de6497SBorislav Petkov * We iterate over DCT0 here but we look at DCT1 in parallel, if needed. 248110de6497SBorislav Petkov */ 248211c75eadSBorislav Petkov for_each_chip_select(i, 0, pvt) { 248310de6497SBorislav Petkov bool row_dct0 = !!csrow_enabled(i, 0, pvt); 248410de6497SBorislav Petkov bool row_dct1 = false; 24850ec449eeSDoug Thompson 2486a4b4bedcSBorislav Petkov if (pvt->fam != 0xf) 248710de6497SBorislav Petkov row_dct1 = !!csrow_enabled(i, 1, pvt); 248810de6497SBorislav Petkov 248910de6497SBorislav Petkov if (!row_dct0 && !row_dct1) 24900ec449eeSDoug Thompson continue; 24910ec449eeSDoug Thompson 249210de6497SBorislav Petkov csrow = mci->csrows[i]; 24930ec449eeSDoug Thompson empty = 0; 249411c75eadSBorislav Petkov 249510de6497SBorislav Petkov edac_dbg(1, "MC node: %d, csrow: %d\n", 249610de6497SBorislav Petkov pvt->mc_node_id, i); 249710de6497SBorislav Petkov 24981eef1282SMauro Carvalho Chehab if (row_dct0) { 2499d1ea71cdSBorislav Petkov nr_pages = get_csrow_nr_pages(pvt, 0, i); 25001eef1282SMauro Carvalho Chehab csrow->channels[0]->dimm->nr_pages = nr_pages; 25011eef1282SMauro Carvalho Chehab } 250210de6497SBorislav Petkov 250310de6497SBorislav Petkov /* K8 has only one DCT */ 2504a4b4bedcSBorislav Petkov if (pvt->fam != 0xf && row_dct1) { 2505d1ea71cdSBorislav Petkov int row_dct1_pages = get_csrow_nr_pages(pvt, 1, i); 25061eef1282SMauro Carvalho Chehab 25071eef1282SMauro Carvalho Chehab csrow->channels[1]->dimm->nr_pages = row_dct1_pages; 25081eef1282SMauro Carvalho Chehab nr_pages += row_dct1_pages; 25091eef1282SMauro Carvalho Chehab } 25100ec449eeSDoug Thompson 251110de6497SBorislav Petkov edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages); 25120ec449eeSDoug Thompson 25130ec449eeSDoug Thompson /* 25140ec449eeSDoug Thompson * determine whether CHIPKILL or JUST ECC or NO ECC is operating 25150ec449eeSDoug Thompson */ 2516a97fa68eSBorislav Petkov if (pvt->nbcfg & NBCFG_ECC_ENABLE) 2517084a4fccSMauro Carvalho Chehab edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) ? 25180ec449eeSDoug Thompson EDAC_S4ECD4ED : EDAC_SECDED; 25190ec449eeSDoug Thompson else 2520084a4fccSMauro Carvalho Chehab edac_mode = EDAC_NONE; 2521084a4fccSMauro Carvalho Chehab 2522084a4fccSMauro Carvalho Chehab for (j = 0; j < pvt->channel_count; j++) { 2523de3910ebSMauro Carvalho Chehab dimm = csrow->channels[j]->dimm; 2524a597d2a5SAravind Gopalakrishnan dimm->mtype = pvt->dram_type; 2525de3910ebSMauro Carvalho Chehab dimm->edac_mode = edac_mode; 2526084a4fccSMauro Carvalho Chehab } 25270ec449eeSDoug Thompson } 25280ec449eeSDoug Thompson 25290ec449eeSDoug Thompson return empty; 25300ec449eeSDoug Thompson } 2531d27bf6faSDoug Thompson 253206724535SBorislav Petkov /* get all cores on this DCT */ 25338b84c8dfSDaniel J Blueman static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, u16 nid) 2534f9431992SDoug Thompson { 253506724535SBorislav Petkov int cpu; 2536f9431992SDoug Thompson 253706724535SBorislav Petkov for_each_online_cpu(cpu) 253806724535SBorislav Petkov if (amd_get_nb_id(cpu) == nid) 253906724535SBorislav Petkov cpumask_set_cpu(cpu, mask); 2540f9431992SDoug Thompson } 2541f9431992SDoug Thompson 2542f9431992SDoug Thompson /* check MCG_CTL on all the cpus on this node */ 2543d1ea71cdSBorislav Petkov static bool nb_mce_bank_enabled_on_node(u16 nid) 2544f9431992SDoug Thompson { 2545ba578cb3SRusty Russell cpumask_var_t mask; 254650542251SBorislav Petkov int cpu, nbe; 254706724535SBorislav Petkov bool ret = false; 2548f9431992SDoug Thompson 2549ba578cb3SRusty Russell if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) { 255024f9a7feSBorislav Petkov amd64_warn("%s: Error allocating mask\n", __func__); 255106724535SBorislav Petkov return false; 255206724535SBorislav Petkov } 255306724535SBorislav Petkov 2554ba578cb3SRusty Russell get_cpus_on_this_dct_cpumask(mask, nid); 255506724535SBorislav Petkov 2556ba578cb3SRusty Russell rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs); 2557ba578cb3SRusty Russell 2558ba578cb3SRusty Russell for_each_cpu(cpu, mask) { 255950542251SBorislav Petkov struct msr *reg = per_cpu_ptr(msrs, cpu); 25605980bb9cSBorislav Petkov nbe = reg->l & MSR_MCGCTL_NBE; 256106724535SBorislav Petkov 2562956b9ba1SJoe Perches edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n", 256350542251SBorislav Petkov cpu, reg->q, 256406724535SBorislav Petkov (nbe ? "enabled" : "disabled")); 256506724535SBorislav Petkov 256606724535SBorislav Petkov if (!nbe) 256706724535SBorislav Petkov goto out; 256806724535SBorislav Petkov } 256906724535SBorislav Petkov ret = true; 257006724535SBorislav Petkov 257106724535SBorislav Petkov out: 2572ba578cb3SRusty Russell free_cpumask_var(mask); 2573f9431992SDoug Thompson return ret; 2574f9431992SDoug Thompson } 2575f9431992SDoug Thompson 2576c7e5301aSDaniel J Blueman static int toggle_ecc_err_reporting(struct ecc_settings *s, u16 nid, bool on) 2577f6d6ae96SBorislav Petkov { 2578f6d6ae96SBorislav Petkov cpumask_var_t cmask; 257950542251SBorislav Petkov int cpu; 2580f6d6ae96SBorislav Petkov 2581f6d6ae96SBorislav Petkov if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) { 258224f9a7feSBorislav Petkov amd64_warn("%s: error allocating mask\n", __func__); 2583f6d6ae96SBorislav Petkov return false; 2584f6d6ae96SBorislav Petkov } 2585f6d6ae96SBorislav Petkov 2586ae7bb7c6SBorislav Petkov get_cpus_on_this_dct_cpumask(cmask, nid); 2587f6d6ae96SBorislav Petkov 2588f6d6ae96SBorislav Petkov rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); 2589f6d6ae96SBorislav Petkov 2590f6d6ae96SBorislav Petkov for_each_cpu(cpu, cmask) { 2591f6d6ae96SBorislav Petkov 259250542251SBorislav Petkov struct msr *reg = per_cpu_ptr(msrs, cpu); 259350542251SBorislav Petkov 2594f6d6ae96SBorislav Petkov if (on) { 25955980bb9cSBorislav Petkov if (reg->l & MSR_MCGCTL_NBE) 2596ae7bb7c6SBorislav Petkov s->flags.nb_mce_enable = 1; 2597f6d6ae96SBorislav Petkov 25985980bb9cSBorislav Petkov reg->l |= MSR_MCGCTL_NBE; 2599f6d6ae96SBorislav Petkov } else { 2600f6d6ae96SBorislav Petkov /* 2601d95cf4deSBorislav Petkov * Turn off NB MCE reporting only when it was off before 2602f6d6ae96SBorislav Petkov */ 2603ae7bb7c6SBorislav Petkov if (!s->flags.nb_mce_enable) 26045980bb9cSBorislav Petkov reg->l &= ~MSR_MCGCTL_NBE; 2605f6d6ae96SBorislav Petkov } 2606f6d6ae96SBorislav Petkov } 2607f6d6ae96SBorislav Petkov wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); 2608f6d6ae96SBorislav Petkov 2609f6d6ae96SBorislav Petkov free_cpumask_var(cmask); 2610f6d6ae96SBorislav Petkov 2611f6d6ae96SBorislav Petkov return 0; 2612f6d6ae96SBorislav Petkov } 2613f6d6ae96SBorislav Petkov 2614c7e5301aSDaniel J Blueman static bool enable_ecc_error_reporting(struct ecc_settings *s, u16 nid, 26152299ef71SBorislav Petkov struct pci_dev *F3) 2616f6d6ae96SBorislav Petkov { 26172299ef71SBorislav Petkov bool ret = true; 2618c9f4f26eSBorislav Petkov u32 value, mask = 0x3; /* UECC/CECC enable */ 2619f6d6ae96SBorislav Petkov 26202299ef71SBorislav Petkov if (toggle_ecc_err_reporting(s, nid, ON)) { 26212299ef71SBorislav Petkov amd64_warn("Error enabling ECC reporting over MCGCTL!\n"); 26222299ef71SBorislav Petkov return false; 26232299ef71SBorislav Petkov } 26242299ef71SBorislav Petkov 2625c9f4f26eSBorislav Petkov amd64_read_pci_cfg(F3, NBCTL, &value); 2626f6d6ae96SBorislav Petkov 2627ae7bb7c6SBorislav Petkov s->old_nbctl = value & mask; 2628ae7bb7c6SBorislav Petkov s->nbctl_valid = true; 2629f6d6ae96SBorislav Petkov 2630f6d6ae96SBorislav Petkov value |= mask; 2631c9f4f26eSBorislav Petkov amd64_write_pci_cfg(F3, NBCTL, value); 2632f6d6ae96SBorislav Petkov 2633a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 2634f6d6ae96SBorislav Petkov 2635956b9ba1SJoe Perches edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n", 2636a97fa68eSBorislav Petkov nid, value, !!(value & NBCFG_ECC_ENABLE)); 2637f6d6ae96SBorislav Petkov 2638a97fa68eSBorislav Petkov if (!(value & NBCFG_ECC_ENABLE)) { 263924f9a7feSBorislav Petkov amd64_warn("DRAM ECC disabled on this node, enabling...\n"); 2640f6d6ae96SBorislav Petkov 2641ae7bb7c6SBorislav Petkov s->flags.nb_ecc_prev = 0; 2642d95cf4deSBorislav Petkov 2643f6d6ae96SBorislav Petkov /* Attempt to turn on DRAM ECC Enable */ 2644a97fa68eSBorislav Petkov value |= NBCFG_ECC_ENABLE; 2645a97fa68eSBorislav Petkov amd64_write_pci_cfg(F3, NBCFG, value); 2646f6d6ae96SBorislav Petkov 2647a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 2648f6d6ae96SBorislav Petkov 2649a97fa68eSBorislav Petkov if (!(value & NBCFG_ECC_ENABLE)) { 265024f9a7feSBorislav Petkov amd64_warn("Hardware rejected DRAM ECC enable," 265124f9a7feSBorislav Petkov "check memory DIMM configuration.\n"); 26522299ef71SBorislav Petkov ret = false; 2653f6d6ae96SBorislav Petkov } else { 265424f9a7feSBorislav Petkov amd64_info("Hardware accepted DRAM ECC Enable\n"); 2655f6d6ae96SBorislav Petkov } 2656d95cf4deSBorislav Petkov } else { 2657ae7bb7c6SBorislav Petkov s->flags.nb_ecc_prev = 1; 2658f6d6ae96SBorislav Petkov } 2659d95cf4deSBorislav Petkov 2660956b9ba1SJoe Perches edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n", 2661a97fa68eSBorislav Petkov nid, value, !!(value & NBCFG_ECC_ENABLE)); 2662f6d6ae96SBorislav Petkov 26632299ef71SBorislav Petkov return ret; 2664f6d6ae96SBorislav Petkov } 2665f6d6ae96SBorislav Petkov 2666c7e5301aSDaniel J Blueman static void restore_ecc_error_reporting(struct ecc_settings *s, u16 nid, 2667360b7f3cSBorislav Petkov struct pci_dev *F3) 2668f6d6ae96SBorislav Petkov { 2669c9f4f26eSBorislav Petkov u32 value, mask = 0x3; /* UECC/CECC enable */ 2670c9f4f26eSBorislav Petkov 2671ae7bb7c6SBorislav Petkov if (!s->nbctl_valid) 2672f6d6ae96SBorislav Petkov return; 2673f6d6ae96SBorislav Petkov 2674c9f4f26eSBorislav Petkov amd64_read_pci_cfg(F3, NBCTL, &value); 2675f6d6ae96SBorislav Petkov value &= ~mask; 2676ae7bb7c6SBorislav Petkov value |= s->old_nbctl; 2677f6d6ae96SBorislav Petkov 2678c9f4f26eSBorislav Petkov amd64_write_pci_cfg(F3, NBCTL, value); 2679f6d6ae96SBorislav Petkov 2680ae7bb7c6SBorislav Petkov /* restore previous BIOS DRAM ECC "off" setting we force-enabled */ 2681ae7bb7c6SBorislav Petkov if (!s->flags.nb_ecc_prev) { 2682a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 2683a97fa68eSBorislav Petkov value &= ~NBCFG_ECC_ENABLE; 2684a97fa68eSBorislav Petkov amd64_write_pci_cfg(F3, NBCFG, value); 2685d95cf4deSBorislav Petkov } 2686d95cf4deSBorislav Petkov 2687d95cf4deSBorislav Petkov /* restore the NB Enable MCGCTL bit */ 26882299ef71SBorislav Petkov if (toggle_ecc_err_reporting(s, nid, OFF)) 268924f9a7feSBorislav Petkov amd64_warn("Error restoring NB MCGCTL settings!\n"); 2690f6d6ae96SBorislav Petkov } 2691f6d6ae96SBorislav Petkov 2692f9431992SDoug Thompson /* 26932299ef71SBorislav Petkov * EDAC requires that the BIOS have ECC enabled before 26942299ef71SBorislav Petkov * taking over the processing of ECC errors. A command line 26952299ef71SBorislav Petkov * option allows to force-enable hardware ECC later in 26962299ef71SBorislav Petkov * enable_ecc_error_reporting(). 2697f9431992SDoug Thompson */ 2698cab4d277SBorislav Petkov static const char *ecc_msg = 2699cab4d277SBorislav Petkov "ECC disabled in the BIOS or no ECC capability, module will not load.\n" 2700cab4d277SBorislav Petkov " Either enable ECC checking or force module loading by setting " 2701cab4d277SBorislav Petkov "'ecc_enable_override'.\n" 2702cab4d277SBorislav Petkov " (Note that use of the override may cause unknown side effects.)\n"; 2703be3468e8SBorislav Petkov 2704c7e5301aSDaniel J Blueman static bool ecc_enabled(struct pci_dev *F3, u16 nid) 2705f9431992SDoug Thompson { 270606724535SBorislav Petkov bool nb_mce_en = false; 2707196b79fcSYazen Ghannam u8 ecc_en = 0, i; 2708196b79fcSYazen Ghannam u32 value; 2709f9431992SDoug Thompson 2710196b79fcSYazen Ghannam if (boot_cpu_data.x86 >= 0x17) { 2711196b79fcSYazen Ghannam u8 umc_en_mask = 0, ecc_en_mask = 0; 2712196b79fcSYazen Ghannam 2713196b79fcSYazen Ghannam for (i = 0; i < NUM_UMCS; i++) { 2714196b79fcSYazen Ghannam u32 base = get_umc_base(i); 2715196b79fcSYazen Ghannam 2716196b79fcSYazen Ghannam /* Only check enabled UMCs. */ 2717196b79fcSYazen Ghannam if (amd_smn_read(nid, base + UMCCH_SDP_CTRL, &value)) 2718196b79fcSYazen Ghannam continue; 2719196b79fcSYazen Ghannam 2720196b79fcSYazen Ghannam if (!(value & UMC_SDP_INIT)) 2721196b79fcSYazen Ghannam continue; 2722196b79fcSYazen Ghannam 2723196b79fcSYazen Ghannam umc_en_mask |= BIT(i); 2724196b79fcSYazen Ghannam 2725196b79fcSYazen Ghannam if (amd_smn_read(nid, base + UMCCH_UMC_CAP_HI, &value)) 2726196b79fcSYazen Ghannam continue; 2727196b79fcSYazen Ghannam 2728196b79fcSYazen Ghannam if (value & UMC_ECC_ENABLED) 2729196b79fcSYazen Ghannam ecc_en_mask |= BIT(i); 2730196b79fcSYazen Ghannam } 2731196b79fcSYazen Ghannam 2732196b79fcSYazen Ghannam /* Check whether at least one UMC is enabled: */ 2733196b79fcSYazen Ghannam if (umc_en_mask) 2734196b79fcSYazen Ghannam ecc_en = umc_en_mask == ecc_en_mask; 2735196b79fcSYazen Ghannam 2736196b79fcSYazen Ghannam /* Assume UMC MCA banks are enabled. */ 2737196b79fcSYazen Ghannam nb_mce_en = true; 2738196b79fcSYazen Ghannam } else { 2739a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 2740f9431992SDoug Thompson 2741a97fa68eSBorislav Petkov ecc_en = !!(value & NBCFG_ECC_ENABLE); 2742be3468e8SBorislav Petkov 2743d1ea71cdSBorislav Petkov nb_mce_en = nb_mce_bank_enabled_on_node(nid); 274406724535SBorislav Petkov if (!nb_mce_en) 2745196b79fcSYazen Ghannam amd64_notice("NB MCE bank disabled, set MSR 0x%08x[4] on node %d to enable.\n", 27462299ef71SBorislav Petkov MSR_IA32_MCG_CTL, nid); 2747196b79fcSYazen Ghannam } 2748196b79fcSYazen Ghannam 2749196b79fcSYazen Ghannam amd64_info("DRAM ECC %s.\n", (ecc_en ? "enabled" : "disabled")); 2750be3468e8SBorislav Petkov 27512299ef71SBorislav Petkov if (!ecc_en || !nb_mce_en) { 275224f9a7feSBorislav Petkov amd64_notice("%s", ecc_msg); 27532299ef71SBorislav Petkov return false; 2754be3468e8SBorislav Petkov } 27552299ef71SBorislav Petkov return true; 2756f9431992SDoug Thompson } 2757f9431992SDoug Thompson 2758df71a053SBorislav Petkov static void setup_mci_misc_attrs(struct mem_ctl_info *mci, 2759df71a053SBorislav Petkov struct amd64_family_type *fam) 27607d6034d3SDoug Thompson { 27617d6034d3SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 27627d6034d3SDoug Thompson 27637d6034d3SDoug Thompson mci->mtype_cap = MEM_FLAG_DDR2 | MEM_FLAG_RDDR2; 27647d6034d3SDoug Thompson mci->edac_ctl_cap = EDAC_FLAG_NONE; 27657d6034d3SDoug Thompson 27665980bb9cSBorislav Petkov if (pvt->nbcap & NBCAP_SECDED) 27677d6034d3SDoug Thompson mci->edac_ctl_cap |= EDAC_FLAG_SECDED; 27687d6034d3SDoug Thompson 27695980bb9cSBorislav Petkov if (pvt->nbcap & NBCAP_CHIPKILL) 27707d6034d3SDoug Thompson mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED; 27717d6034d3SDoug Thompson 2772d1ea71cdSBorislav Petkov mci->edac_cap = determine_edac_cap(pvt); 27737d6034d3SDoug Thompson mci->mod_name = EDAC_MOD_STR; 27747d6034d3SDoug Thompson mci->mod_ver = EDAC_AMD64_VERSION; 2775df71a053SBorislav Petkov mci->ctl_name = fam->ctl_name; 2776e7934b70SYazen Ghannam mci->dev_name = pci_name(pvt->F3); 27777d6034d3SDoug Thompson mci->ctl_page_to_phys = NULL; 27787d6034d3SDoug Thompson 27797d6034d3SDoug Thompson /* memory scrubber interface */ 2780d1ea71cdSBorislav Petkov mci->set_sdram_scrub_rate = set_scrub_rate; 2781d1ea71cdSBorislav Petkov mci->get_sdram_scrub_rate = get_scrub_rate; 27827d6034d3SDoug Thompson } 27837d6034d3SDoug Thompson 27840092b20dSBorislav Petkov /* 27850092b20dSBorislav Petkov * returns a pointer to the family descriptor on success, NULL otherwise. 27860092b20dSBorislav Petkov */ 2787d1ea71cdSBorislav Petkov static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt) 2788395ae783SBorislav Petkov { 27890092b20dSBorislav Petkov struct amd64_family_type *fam_type = NULL; 27900092b20dSBorislav Petkov 279118b94f66SAravind Gopalakrishnan pvt->ext_model = boot_cpu_data.x86_model >> 4; 2792a4b4bedcSBorislav Petkov pvt->stepping = boot_cpu_data.x86_mask; 279318b94f66SAravind Gopalakrishnan pvt->model = boot_cpu_data.x86_model; 279418b94f66SAravind Gopalakrishnan pvt->fam = boot_cpu_data.x86; 279518b94f66SAravind Gopalakrishnan 279618b94f66SAravind Gopalakrishnan switch (pvt->fam) { 2797395ae783SBorislav Petkov case 0xf: 2798d1ea71cdSBorislav Petkov fam_type = &family_types[K8_CPUS]; 2799d1ea71cdSBorislav Petkov pvt->ops = &family_types[K8_CPUS].ops; 2800395ae783SBorislav Petkov break; 2801df71a053SBorislav Petkov 2802395ae783SBorislav Petkov case 0x10: 2803d1ea71cdSBorislav Petkov fam_type = &family_types[F10_CPUS]; 2804d1ea71cdSBorislav Petkov pvt->ops = &family_types[F10_CPUS].ops; 2805df71a053SBorislav Petkov break; 2806df71a053SBorislav Petkov 2807df71a053SBorislav Petkov case 0x15: 280818b94f66SAravind Gopalakrishnan if (pvt->model == 0x30) { 2809d1ea71cdSBorislav Petkov fam_type = &family_types[F15_M30H_CPUS]; 2810d1ea71cdSBorislav Petkov pvt->ops = &family_types[F15_M30H_CPUS].ops; 281118b94f66SAravind Gopalakrishnan break; 2812a597d2a5SAravind Gopalakrishnan } else if (pvt->model == 0x60) { 2813a597d2a5SAravind Gopalakrishnan fam_type = &family_types[F15_M60H_CPUS]; 2814a597d2a5SAravind Gopalakrishnan pvt->ops = &family_types[F15_M60H_CPUS].ops; 2815a597d2a5SAravind Gopalakrishnan break; 281618b94f66SAravind Gopalakrishnan } 281718b94f66SAravind Gopalakrishnan 2818d1ea71cdSBorislav Petkov fam_type = &family_types[F15_CPUS]; 2819d1ea71cdSBorislav Petkov pvt->ops = &family_types[F15_CPUS].ops; 2820395ae783SBorislav Petkov break; 2821395ae783SBorislav Petkov 282294c1acf2SAravind Gopalakrishnan case 0x16: 282385a8885bSAravind Gopalakrishnan if (pvt->model == 0x30) { 282485a8885bSAravind Gopalakrishnan fam_type = &family_types[F16_M30H_CPUS]; 282585a8885bSAravind Gopalakrishnan pvt->ops = &family_types[F16_M30H_CPUS].ops; 282685a8885bSAravind Gopalakrishnan break; 282785a8885bSAravind Gopalakrishnan } 2828d1ea71cdSBorislav Petkov fam_type = &family_types[F16_CPUS]; 2829d1ea71cdSBorislav Petkov pvt->ops = &family_types[F16_CPUS].ops; 283094c1acf2SAravind Gopalakrishnan break; 283194c1acf2SAravind Gopalakrishnan 2832f1cbbec9SYazen Ghannam case 0x17: 2833f1cbbec9SYazen Ghannam fam_type = &family_types[F17_CPUS]; 2834f1cbbec9SYazen Ghannam pvt->ops = &family_types[F17_CPUS].ops; 2835f1cbbec9SYazen Ghannam break; 2836f1cbbec9SYazen Ghannam 2837395ae783SBorislav Petkov default: 283824f9a7feSBorislav Petkov amd64_err("Unsupported family!\n"); 28390092b20dSBorislav Petkov return NULL; 2840395ae783SBorislav Petkov } 28410092b20dSBorislav Petkov 2842df71a053SBorislav Petkov amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name, 284318b94f66SAravind Gopalakrishnan (pvt->fam == 0xf ? 28440092b20dSBorislav Petkov (pvt->ext_model >= K8_REV_F ? "revF or later " 28450092b20dSBorislav Petkov : "revE or earlier ") 284624f9a7feSBorislav Petkov : ""), pvt->mc_node_id); 28470092b20dSBorislav Petkov return fam_type; 2848395ae783SBorislav Petkov } 2849395ae783SBorislav Petkov 2850e339f1ecSTakashi Iwai static const struct attribute_group *amd64_edac_attr_groups[] = { 2851e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_DEBUG 2852e339f1ecSTakashi Iwai &amd64_edac_dbg_group, 2853e339f1ecSTakashi Iwai #endif 2854e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION 2855e339f1ecSTakashi Iwai &amd64_edac_inj_group, 2856e339f1ecSTakashi Iwai #endif 2857e339f1ecSTakashi Iwai NULL 2858e339f1ecSTakashi Iwai }; 2859e339f1ecSTakashi Iwai 28603f37a36bSBorislav Petkov static int init_one_instance(unsigned int nid) 28617d6034d3SDoug Thompson { 28623f37a36bSBorislav Petkov struct pci_dev *F3 = node_to_amd_nb(nid)->misc; 28630092b20dSBorislav Petkov struct amd64_family_type *fam_type = NULL; 2864360b7f3cSBorislav Petkov struct mem_ctl_info *mci = NULL; 2865ab5a503cSMauro Carvalho Chehab struct edac_mc_layer layers[2]; 28663f37a36bSBorislav Petkov struct amd64_pvt *pvt = NULL; 28677d6034d3SDoug Thompson int err = 0, ret; 28687d6034d3SDoug Thompson 28697d6034d3SDoug Thompson ret = -ENOMEM; 28707d6034d3SDoug Thompson pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL); 28717d6034d3SDoug Thompson if (!pvt) 2872360b7f3cSBorislav Petkov goto err_ret; 28737d6034d3SDoug Thompson 2874360b7f3cSBorislav Petkov pvt->mc_node_id = nid; 28753f37a36bSBorislav Petkov pvt->F3 = F3; 28767d6034d3SDoug Thompson 2877395ae783SBorislav Petkov ret = -EINVAL; 2878d1ea71cdSBorislav Petkov fam_type = per_family_init(pvt); 28790092b20dSBorislav Petkov if (!fam_type) 2880395ae783SBorislav Petkov goto err_free; 2881395ae783SBorislav Petkov 28827d6034d3SDoug Thompson ret = -ENODEV; 28833f37a36bSBorislav Petkov err = reserve_mc_sibling_devs(pvt, fam_type->f1_id, fam_type->f2_id); 28847d6034d3SDoug Thompson if (err) 28857d6034d3SDoug Thompson goto err_free; 28867d6034d3SDoug Thompson 2887360b7f3cSBorislav Petkov read_mc_regs(pvt); 28887d6034d3SDoug Thompson 28897d6034d3SDoug Thompson /* 28907d6034d3SDoug Thompson * We need to determine how many memory channels there are. Then use 28917d6034d3SDoug Thompson * that information for calculating the size of the dynamic instance 2892360b7f3cSBorislav Petkov * tables in the 'mci' structure. 28937d6034d3SDoug Thompson */ 2894360b7f3cSBorislav Petkov ret = -EINVAL; 28957d6034d3SDoug Thompson pvt->channel_count = pvt->ops->early_channel_count(pvt); 28967d6034d3SDoug Thompson if (pvt->channel_count < 0) 2897360b7f3cSBorislav Petkov goto err_siblings; 28987d6034d3SDoug Thompson 28997d6034d3SDoug Thompson ret = -ENOMEM; 2900ab5a503cSMauro Carvalho Chehab layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 2901ab5a503cSMauro Carvalho Chehab layers[0].size = pvt->csels[0].b_cnt; 2902ab5a503cSMauro Carvalho Chehab layers[0].is_virt_csrow = true; 2903ab5a503cSMauro Carvalho Chehab layers[1].type = EDAC_MC_LAYER_CHANNEL; 2904f0a56c48SBorislav Petkov 2905f0a56c48SBorislav Petkov /* 2906f0a56c48SBorislav Petkov * Always allocate two channels since we can have setups with DIMMs on 2907f0a56c48SBorislav Petkov * only one channel. Also, this simplifies handling later for the price 2908f0a56c48SBorislav Petkov * of a couple of KBs tops. 2909f0a56c48SBorislav Petkov */ 2910f0a56c48SBorislav Petkov layers[1].size = 2; 2911ab5a503cSMauro Carvalho Chehab layers[1].is_virt_csrow = false; 2912f0a56c48SBorislav Petkov 2913ca0907b9SMauro Carvalho Chehab mci = edac_mc_alloc(nid, ARRAY_SIZE(layers), layers, 0); 29147d6034d3SDoug Thompson if (!mci) 2915360b7f3cSBorislav Petkov goto err_siblings; 29167d6034d3SDoug Thompson 29177d6034d3SDoug Thompson mci->pvt_info = pvt; 29183f37a36bSBorislav Petkov mci->pdev = &pvt->F3->dev; 29197d6034d3SDoug Thompson 2920df71a053SBorislav Petkov setup_mci_misc_attrs(mci, fam_type); 2921360b7f3cSBorislav Petkov 2922360b7f3cSBorislav Petkov if (init_csrows(mci)) 29237d6034d3SDoug Thompson mci->edac_cap = EDAC_FLAG_NONE; 29247d6034d3SDoug Thompson 29257d6034d3SDoug Thompson ret = -ENODEV; 2926e339f1ecSTakashi Iwai if (edac_mc_add_mc_with_groups(mci, amd64_edac_attr_groups)) { 2927956b9ba1SJoe Perches edac_dbg(1, "failed edac_mc_add_mc()\n"); 29287d6034d3SDoug Thompson goto err_add_mc; 29297d6034d3SDoug Thompson } 29307d6034d3SDoug Thompson 2931549d042dSBorislav Petkov /* register stuff with EDAC MCE */ 2932549d042dSBorislav Petkov if (report_gart_errors) 2933549d042dSBorislav Petkov amd_report_gart_errors(true); 2934549d042dSBorislav Petkov 2935df781d03SBorislav Petkov amd_register_ecc_decoder(decode_bus_error); 2936549d042dSBorislav Petkov 29377d6034d3SDoug Thompson return 0; 29387d6034d3SDoug Thompson 29397d6034d3SDoug Thompson err_add_mc: 29407d6034d3SDoug Thompson edac_mc_free(mci); 29417d6034d3SDoug Thompson 2942360b7f3cSBorislav Petkov err_siblings: 2943360b7f3cSBorislav Petkov free_mc_sibling_devs(pvt); 29447d6034d3SDoug Thompson 2945360b7f3cSBorislav Petkov err_free: 2946360b7f3cSBorislav Petkov kfree(pvt); 29477d6034d3SDoug Thompson 2948360b7f3cSBorislav Petkov err_ret: 29497d6034d3SDoug Thompson return ret; 29507d6034d3SDoug Thompson } 29517d6034d3SDoug Thompson 29523f37a36bSBorislav Petkov static int probe_one_instance(unsigned int nid) 29537d6034d3SDoug Thompson { 29542299ef71SBorislav Petkov struct pci_dev *F3 = node_to_amd_nb(nid)->misc; 2955ae7bb7c6SBorislav Petkov struct ecc_settings *s; 29563f37a36bSBorislav Petkov int ret; 2957b8cfa02fSBorislav Petkov 2958ae7bb7c6SBorislav Petkov ret = -ENOMEM; 2959ae7bb7c6SBorislav Petkov s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL); 2960ae7bb7c6SBorislav Petkov if (!s) 29612299ef71SBorislav Petkov goto err_out; 2962ae7bb7c6SBorislav Petkov 2963ae7bb7c6SBorislav Petkov ecc_stngs[nid] = s; 2964ae7bb7c6SBorislav Petkov 29652299ef71SBorislav Petkov if (!ecc_enabled(F3, nid)) { 29662299ef71SBorislav Petkov ret = -ENODEV; 29672299ef71SBorislav Petkov 29682299ef71SBorislav Petkov if (!ecc_enable_override) 29692299ef71SBorislav Petkov goto err_enable; 29702299ef71SBorislav Petkov 2971044e7a41SYazen Ghannam if (boot_cpu_data.x86 >= 0x17) { 2972044e7a41SYazen Ghannam amd64_warn("Forcing ECC on is not recommended on newer systems. Please enable ECC in BIOS."); 2973044e7a41SYazen Ghannam goto err_enable; 2974044e7a41SYazen Ghannam } else 29752299ef71SBorislav Petkov amd64_warn("Forcing ECC on!\n"); 29762299ef71SBorislav Petkov 29772299ef71SBorislav Petkov if (!enable_ecc_error_reporting(s, nid, F3)) 29782299ef71SBorislav Petkov goto err_enable; 29792299ef71SBorislav Petkov } 29802299ef71SBorislav Petkov 29813f37a36bSBorislav Petkov ret = init_one_instance(nid); 2982360b7f3cSBorislav Petkov if (ret < 0) { 2983ae7bb7c6SBorislav Petkov amd64_err("Error probing instance: %d\n", nid); 2984044e7a41SYazen Ghannam 2985044e7a41SYazen Ghannam if (boot_cpu_data.x86 < 0x17) 2986360b7f3cSBorislav Petkov restore_ecc_error_reporting(s, nid, F3); 2987360b7f3cSBorislav Petkov } 29887d6034d3SDoug Thompson 29897d6034d3SDoug Thompson return ret; 29902299ef71SBorislav Petkov 29912299ef71SBorislav Petkov err_enable: 29922299ef71SBorislav Petkov kfree(s); 29932299ef71SBorislav Petkov ecc_stngs[nid] = NULL; 29942299ef71SBorislav Petkov 29952299ef71SBorislav Petkov err_out: 29962299ef71SBorislav Petkov return ret; 29977d6034d3SDoug Thompson } 29987d6034d3SDoug Thompson 29993f37a36bSBorislav Petkov static void remove_one_instance(unsigned int nid) 30007d6034d3SDoug Thompson { 3001360b7f3cSBorislav Petkov struct pci_dev *F3 = node_to_amd_nb(nid)->misc; 3002360b7f3cSBorislav Petkov struct ecc_settings *s = ecc_stngs[nid]; 30033f37a36bSBorislav Petkov struct mem_ctl_info *mci; 30043f37a36bSBorislav Petkov struct amd64_pvt *pvt; 30057d6034d3SDoug Thompson 30063f37a36bSBorislav Petkov mci = find_mci_by_dev(&F3->dev); 3007a4b4bedcSBorislav Petkov WARN_ON(!mci); 3008a4b4bedcSBorislav Petkov 30097d6034d3SDoug Thompson /* Remove from EDAC CORE tracking list */ 30103f37a36bSBorislav Petkov mci = edac_mc_del_mc(&F3->dev); 30117d6034d3SDoug Thompson if (!mci) 30127d6034d3SDoug Thompson return; 30137d6034d3SDoug Thompson 30147d6034d3SDoug Thompson pvt = mci->pvt_info; 30157d6034d3SDoug Thompson 3016360b7f3cSBorislav Petkov restore_ecc_error_reporting(s, nid, F3); 30177d6034d3SDoug Thompson 3018360b7f3cSBorislav Petkov free_mc_sibling_devs(pvt); 30197d6034d3SDoug Thompson 3020549d042dSBorislav Petkov /* unregister from EDAC MCE */ 3021549d042dSBorislav Petkov amd_report_gart_errors(false); 3022df781d03SBorislav Petkov amd_unregister_ecc_decoder(decode_bus_error); 3023549d042dSBorislav Petkov 3024360b7f3cSBorislav Petkov kfree(ecc_stngs[nid]); 3025360b7f3cSBorislav Petkov ecc_stngs[nid] = NULL; 3026ae7bb7c6SBorislav Petkov 30277d6034d3SDoug Thompson /* Free the EDAC CORE resources */ 30288f68ed97SBorislav Petkov mci->pvt_info = NULL; 30298f68ed97SBorislav Petkov 30308f68ed97SBorislav Petkov kfree(pvt); 30317d6034d3SDoug Thompson edac_mc_free(mci); 30327d6034d3SDoug Thompson } 30337d6034d3SDoug Thompson 3034360b7f3cSBorislav Petkov static void setup_pci_device(void) 30357d6034d3SDoug Thompson { 30367d6034d3SDoug Thompson struct mem_ctl_info *mci; 30377d6034d3SDoug Thompson struct amd64_pvt *pvt; 30387d6034d3SDoug Thompson 3039d1ea71cdSBorislav Petkov if (pci_ctl) 30407d6034d3SDoug Thompson return; 30417d6034d3SDoug Thompson 30422ec591acSBorislav Petkov mci = edac_mc_find(0); 3043d1ea71cdSBorislav Petkov if (!mci) 3044d1ea71cdSBorislav Petkov return; 30457d6034d3SDoug Thompson 30467d6034d3SDoug Thompson pvt = mci->pvt_info; 3047d1ea71cdSBorislav Petkov pci_ctl = edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR); 3048d1ea71cdSBorislav Petkov if (!pci_ctl) { 3049d1ea71cdSBorislav Petkov pr_warn("%s(): Unable to create PCI control\n", __func__); 3050d1ea71cdSBorislav Petkov pr_warn("%s(): PCI error report via EDAC not set\n", __func__); 30517d6034d3SDoug Thompson } 30527d6034d3SDoug Thompson } 30537d6034d3SDoug Thompson 3054d6efab74SYazen Ghannam static const struct x86_cpu_id amd64_cpuids[] = { 3055d6efab74SYazen Ghannam { X86_VENDOR_AMD, 0xF, X86_MODEL_ANY, X86_FEATURE_ANY, 0 }, 3056d6efab74SYazen Ghannam { X86_VENDOR_AMD, 0x10, X86_MODEL_ANY, X86_FEATURE_ANY, 0 }, 3057d6efab74SYazen Ghannam { X86_VENDOR_AMD, 0x15, X86_MODEL_ANY, X86_FEATURE_ANY, 0 }, 3058d6efab74SYazen Ghannam { X86_VENDOR_AMD, 0x16, X86_MODEL_ANY, X86_FEATURE_ANY, 0 }, 3059d6efab74SYazen Ghannam { } 3060d6efab74SYazen Ghannam }; 3061d6efab74SYazen Ghannam MODULE_DEVICE_TABLE(x86cpu, amd64_cpuids); 3062d6efab74SYazen Ghannam 30637d6034d3SDoug Thompson static int __init amd64_edac_init(void) 30647d6034d3SDoug Thompson { 3065360b7f3cSBorislav Petkov int err = -ENODEV; 30663f37a36bSBorislav Petkov int i; 30677d6034d3SDoug Thompson 30689653a5c7SHans Rosenfeld if (amd_cache_northbridges() < 0) 306956b34b91SBorislav Petkov goto err_ret; 30707d6034d3SDoug Thompson 30716ba92feaSBorislav Petkov opstate_init(); 30726ba92feaSBorislav Petkov 3073cc4d8860SBorislav Petkov err = -ENOMEM; 3074ae7bb7c6SBorislav Petkov ecc_stngs = kzalloc(amd_nb_num() * sizeof(ecc_stngs[0]), GFP_KERNEL); 30752ec591acSBorislav Petkov if (!ecc_stngs) 3076a9f0fbe2SBorislav Petkov goto err_free; 3077cc4d8860SBorislav Petkov 307850542251SBorislav Petkov msrs = msrs_alloc(); 307956b34b91SBorislav Petkov if (!msrs) 3080360b7f3cSBorislav Petkov goto err_free; 308150542251SBorislav Petkov 30823f37a36bSBorislav Petkov for (i = 0; i < amd_nb_num(); i++) 30833f37a36bSBorislav Petkov if (probe_one_instance(i)) { 30843f37a36bSBorislav Petkov /* unwind properly */ 30853f37a36bSBorislav Petkov while (--i >= 0) 30863f37a36bSBorislav Petkov remove_one_instance(i); 30877d6034d3SDoug Thompson 30883f37a36bSBorislav Petkov goto err_pci; 30893f37a36bSBorislav Petkov } 30907d6034d3SDoug Thompson 3091360b7f3cSBorislav Petkov setup_pci_device(); 3092f5b10c45STomasz Pala 3093f5b10c45STomasz Pala #ifdef CONFIG_X86_32 3094f5b10c45STomasz Pala amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR); 3095f5b10c45STomasz Pala #endif 3096f5b10c45STomasz Pala 3097de0336b3SBorislav Petkov printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION); 3098de0336b3SBorislav Petkov 30997d6034d3SDoug Thompson return 0; 31007d6034d3SDoug Thompson 310156b34b91SBorislav Petkov err_pci: 310256b34b91SBorislav Petkov msrs_free(msrs); 310356b34b91SBorislav Petkov msrs = NULL; 3104cc4d8860SBorislav Petkov 3105360b7f3cSBorislav Petkov err_free: 3106360b7f3cSBorislav Petkov kfree(ecc_stngs); 3107360b7f3cSBorislav Petkov ecc_stngs = NULL; 3108360b7f3cSBorislav Petkov 310956b34b91SBorislav Petkov err_ret: 31107d6034d3SDoug Thompson return err; 31117d6034d3SDoug Thompson } 31127d6034d3SDoug Thompson 31137d6034d3SDoug Thompson static void __exit amd64_edac_exit(void) 31147d6034d3SDoug Thompson { 31153f37a36bSBorislav Petkov int i; 31163f37a36bSBorislav Petkov 3117d1ea71cdSBorislav Petkov if (pci_ctl) 3118d1ea71cdSBorislav Petkov edac_pci_release_generic_ctl(pci_ctl); 31197d6034d3SDoug Thompson 31203f37a36bSBorislav Petkov for (i = 0; i < amd_nb_num(); i++) 31213f37a36bSBorislav Petkov remove_one_instance(i); 312250542251SBorislav Petkov 3123ae7bb7c6SBorislav Petkov kfree(ecc_stngs); 3124ae7bb7c6SBorislav Petkov ecc_stngs = NULL; 3125ae7bb7c6SBorislav Petkov 312650542251SBorislav Petkov msrs_free(msrs); 312750542251SBorislav Petkov msrs = NULL; 31287d6034d3SDoug Thompson } 31297d6034d3SDoug Thompson 31307d6034d3SDoug Thompson module_init(amd64_edac_init); 31317d6034d3SDoug Thompson module_exit(amd64_edac_exit); 31327d6034d3SDoug Thompson 31337d6034d3SDoug Thompson MODULE_LICENSE("GPL"); 31347d6034d3SDoug Thompson MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, " 31357d6034d3SDoug Thompson "Dave Peterson, Thayne Harbaugh"); 31367d6034d3SDoug Thompson MODULE_DESCRIPTION("MC support for AMD64 memory controllers - " 31377d6034d3SDoug Thompson EDAC_AMD64_VERSION); 31387d6034d3SDoug Thompson 31397d6034d3SDoug Thompson module_param(edac_op_state, int, 0444); 31407d6034d3SDoug Thompson MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); 3141