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 121341d8bfabSBorislav Petkov static int ddr3_cs_size(unsigned i, bool dct_width) 12141afd3c98SDoug Thompson { 121541d8bfabSBorislav Petkov unsigned shift = 0; 121641d8bfabSBorislav Petkov int cs_size = 0; 121741d8bfabSBorislav Petkov 121841d8bfabSBorislav Petkov if (i == 0 || i == 3 || i == 4) 121941d8bfabSBorislav Petkov cs_size = -1; 122041d8bfabSBorislav Petkov else if (i <= 2) 122141d8bfabSBorislav Petkov shift = i; 122241d8bfabSBorislav Petkov else if (i == 12) 122341d8bfabSBorislav Petkov shift = 7; 122441d8bfabSBorislav Petkov else if (!(i & 0x1)) 122541d8bfabSBorislav Petkov shift = i >> 1; 122641d8bfabSBorislav Petkov else 122741d8bfabSBorislav Petkov shift = (i + 1) >> 1; 122841d8bfabSBorislav Petkov 122941d8bfabSBorislav Petkov if (cs_size != -1) 123041d8bfabSBorislav Petkov cs_size = (128 * (1 << !!dct_width)) << shift; 123141d8bfabSBorislav Petkov 123241d8bfabSBorislav Petkov return cs_size; 123341d8bfabSBorislav Petkov } 123441d8bfabSBorislav Petkov 1235a597d2a5SAravind Gopalakrishnan static int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply) 1236a597d2a5SAravind Gopalakrishnan { 1237a597d2a5SAravind Gopalakrishnan unsigned shift = 0; 1238a597d2a5SAravind Gopalakrishnan int cs_size = 0; 1239a597d2a5SAravind Gopalakrishnan 1240a597d2a5SAravind Gopalakrishnan if (i < 4 || i == 6) 1241a597d2a5SAravind Gopalakrishnan cs_size = -1; 1242a597d2a5SAravind Gopalakrishnan else if (i == 12) 1243a597d2a5SAravind Gopalakrishnan shift = 7; 1244a597d2a5SAravind Gopalakrishnan else if (!(i & 0x1)) 1245a597d2a5SAravind Gopalakrishnan shift = i >> 1; 1246a597d2a5SAravind Gopalakrishnan else 1247a597d2a5SAravind Gopalakrishnan shift = (i + 1) >> 1; 1248a597d2a5SAravind Gopalakrishnan 1249a597d2a5SAravind Gopalakrishnan if (cs_size != -1) 1250a597d2a5SAravind Gopalakrishnan cs_size = rank_multiply * (128 << shift); 1251a597d2a5SAravind Gopalakrishnan 1252a597d2a5SAravind Gopalakrishnan return cs_size; 1253a597d2a5SAravind Gopalakrishnan } 1254a597d2a5SAravind Gopalakrishnan 1255a597d2a5SAravind Gopalakrishnan static int ddr4_cs_size(unsigned i) 1256a597d2a5SAravind Gopalakrishnan { 1257a597d2a5SAravind Gopalakrishnan int cs_size = 0; 1258a597d2a5SAravind Gopalakrishnan 1259a597d2a5SAravind Gopalakrishnan if (i == 0) 1260a597d2a5SAravind Gopalakrishnan cs_size = -1; 1261a597d2a5SAravind Gopalakrishnan else if (i == 1) 1262a597d2a5SAravind Gopalakrishnan cs_size = 1024; 1263a597d2a5SAravind Gopalakrishnan else 1264a597d2a5SAravind Gopalakrishnan /* Min cs_size = 1G */ 1265a597d2a5SAravind Gopalakrishnan cs_size = 1024 * (1 << (i >> 1)); 1266a597d2a5SAravind Gopalakrishnan 1267a597d2a5SAravind Gopalakrishnan return cs_size; 1268a597d2a5SAravind Gopalakrishnan } 1269a597d2a5SAravind Gopalakrishnan 127041d8bfabSBorislav Petkov static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 1271a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 127241d8bfabSBorislav Petkov { 127341d8bfabSBorislav Petkov u32 dclr = dct ? pvt->dclr1 : pvt->dclr0; 127441d8bfabSBorislav Petkov 127541d8bfabSBorislav Petkov WARN_ON(cs_mode > 11); 12761433eb99SBorislav Petkov 12771433eb99SBorislav Petkov if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE) 127841d8bfabSBorislav Petkov return ddr3_cs_size(cs_mode, dclr & WIDTH_128); 12791433eb99SBorislav Petkov else 128041d8bfabSBorislav Petkov return ddr2_cs_size(cs_mode, dclr & WIDTH_128); 128141d8bfabSBorislav Petkov } 12821433eb99SBorislav Petkov 128341d8bfabSBorislav Petkov /* 128441d8bfabSBorislav Petkov * F15h supports only 64bit DCT interfaces 128541d8bfabSBorislav Petkov */ 128641d8bfabSBorislav Petkov static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 1287a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 128841d8bfabSBorislav Petkov { 128941d8bfabSBorislav Petkov WARN_ON(cs_mode > 12); 129041d8bfabSBorislav Petkov 129141d8bfabSBorislav Petkov return ddr3_cs_size(cs_mode, false); 12921afd3c98SDoug Thompson } 12931afd3c98SDoug Thompson 1294a597d2a5SAravind Gopalakrishnan /* F15h M60h supports DDR4 mapping as well.. */ 1295a597d2a5SAravind Gopalakrishnan static int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 1296a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 1297a597d2a5SAravind Gopalakrishnan { 1298a597d2a5SAravind Gopalakrishnan int cs_size; 1299a597d2a5SAravind Gopalakrishnan u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr]; 1300a597d2a5SAravind Gopalakrishnan 1301a597d2a5SAravind Gopalakrishnan WARN_ON(cs_mode > 12); 1302a597d2a5SAravind Gopalakrishnan 1303a597d2a5SAravind Gopalakrishnan if (pvt->dram_type == MEM_DDR4) { 1304a597d2a5SAravind Gopalakrishnan if (cs_mode > 9) 1305a597d2a5SAravind Gopalakrishnan return -1; 1306a597d2a5SAravind Gopalakrishnan 1307a597d2a5SAravind Gopalakrishnan cs_size = ddr4_cs_size(cs_mode); 1308a597d2a5SAravind Gopalakrishnan } else if (pvt->dram_type == MEM_LRDDR3) { 1309a597d2a5SAravind Gopalakrishnan unsigned rank_multiply = dcsm & 0xf; 1310a597d2a5SAravind Gopalakrishnan 1311a597d2a5SAravind Gopalakrishnan if (rank_multiply == 3) 1312a597d2a5SAravind Gopalakrishnan rank_multiply = 4; 1313a597d2a5SAravind Gopalakrishnan cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply); 1314a597d2a5SAravind Gopalakrishnan } else { 1315a597d2a5SAravind Gopalakrishnan /* Minimum cs size is 512mb for F15hM60h*/ 1316a597d2a5SAravind Gopalakrishnan if (cs_mode == 0x1) 1317a597d2a5SAravind Gopalakrishnan return -1; 1318a597d2a5SAravind Gopalakrishnan 1319a597d2a5SAravind Gopalakrishnan cs_size = ddr3_cs_size(cs_mode, false); 1320a597d2a5SAravind Gopalakrishnan } 1321a597d2a5SAravind Gopalakrishnan 1322a597d2a5SAravind Gopalakrishnan return cs_size; 1323a597d2a5SAravind Gopalakrishnan } 1324a597d2a5SAravind Gopalakrishnan 132594c1acf2SAravind Gopalakrishnan /* 132618b94f66SAravind Gopalakrishnan * F16h and F15h model 30h have only limited cs_modes. 132794c1acf2SAravind Gopalakrishnan */ 132894c1acf2SAravind Gopalakrishnan static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, 1329a597d2a5SAravind Gopalakrishnan unsigned cs_mode, int cs_mask_nr) 133094c1acf2SAravind Gopalakrishnan { 133194c1acf2SAravind Gopalakrishnan WARN_ON(cs_mode > 12); 133294c1acf2SAravind Gopalakrishnan 133394c1acf2SAravind Gopalakrishnan if (cs_mode == 6 || cs_mode == 8 || 133494c1acf2SAravind Gopalakrishnan cs_mode == 9 || cs_mode == 12) 133594c1acf2SAravind Gopalakrishnan return -1; 133694c1acf2SAravind Gopalakrishnan else 133794c1acf2SAravind Gopalakrishnan return ddr3_cs_size(cs_mode, false); 133894c1acf2SAravind Gopalakrishnan } 133994c1acf2SAravind Gopalakrishnan 13405a5d2371SBorislav Petkov static void read_dram_ctl_register(struct amd64_pvt *pvt) 13416163b5d4SDoug Thompson { 13426163b5d4SDoug Thompson 1343a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) 13445a5d2371SBorislav Petkov return; 13455a5d2371SBorislav Petkov 13467981a28fSAravind Gopalakrishnan if (!amd64_read_pci_cfg(pvt->F2, DCT_SEL_LO, &pvt->dct_sel_lo)) { 1347956b9ba1SJoe Perches edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n", 134878da121eSBorislav Petkov pvt->dct_sel_lo, dct_sel_baseaddr(pvt)); 13496163b5d4SDoug Thompson 1350956b9ba1SJoe Perches edac_dbg(0, " DCTs operate in %s mode\n", 13515a5d2371SBorislav Petkov (dct_ganging_enabled(pvt) ? "ganged" : "unganged")); 13526163b5d4SDoug Thompson 135372381bd5SBorislav Petkov if (!dct_ganging_enabled(pvt)) 1354956b9ba1SJoe Perches edac_dbg(0, " Address range split per DCT: %s\n", 135572381bd5SBorislav Petkov (dct_high_range_enabled(pvt) ? "yes" : "no")); 135672381bd5SBorislav Petkov 1357956b9ba1SJoe Perches edac_dbg(0, " data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n", 135872381bd5SBorislav Petkov (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"), 135972381bd5SBorislav Petkov (dct_memory_cleared(pvt) ? "yes" : "no")); 136072381bd5SBorislav Petkov 1361956b9ba1SJoe Perches edac_dbg(0, " channel interleave: %s, " 136278da121eSBorislav Petkov "interleave bits selector: 0x%x\n", 136372381bd5SBorislav Petkov (dct_interleave_enabled(pvt) ? "enabled" : "disabled"), 13646163b5d4SDoug Thompson dct_sel_interleave_addr(pvt)); 13656163b5d4SDoug Thompson } 13666163b5d4SDoug Thompson 13677981a28fSAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F2, DCT_SEL_HI, &pvt->dct_sel_hi); 13686163b5d4SDoug Thompson } 13696163b5d4SDoug Thompson 1370f71d0a05SDoug Thompson /* 137118b94f66SAravind Gopalakrishnan * Determine channel (DCT) based on the interleaving mode (see F15h M30h BKDG, 137218b94f66SAravind Gopalakrishnan * 2.10.12 Memory Interleaving Modes). 137318b94f66SAravind Gopalakrishnan */ 137418b94f66SAravind Gopalakrishnan static u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, 137518b94f66SAravind Gopalakrishnan u8 intlv_en, int num_dcts_intlv, 137618b94f66SAravind Gopalakrishnan u32 dct_sel) 137718b94f66SAravind Gopalakrishnan { 137818b94f66SAravind Gopalakrishnan u8 channel = 0; 137918b94f66SAravind Gopalakrishnan u8 select; 138018b94f66SAravind Gopalakrishnan 138118b94f66SAravind Gopalakrishnan if (!(intlv_en)) 138218b94f66SAravind Gopalakrishnan return (u8)(dct_sel); 138318b94f66SAravind Gopalakrishnan 138418b94f66SAravind Gopalakrishnan if (num_dcts_intlv == 2) { 138518b94f66SAravind Gopalakrishnan select = (sys_addr >> 8) & 0x3; 138618b94f66SAravind Gopalakrishnan channel = select ? 0x3 : 0; 13879d0e8d83SAravind Gopalakrishnan } else if (num_dcts_intlv == 4) { 13889d0e8d83SAravind Gopalakrishnan u8 intlv_addr = dct_sel_interleave_addr(pvt); 13899d0e8d83SAravind Gopalakrishnan switch (intlv_addr) { 13909d0e8d83SAravind Gopalakrishnan case 0x4: 13919d0e8d83SAravind Gopalakrishnan channel = (sys_addr >> 8) & 0x3; 13929d0e8d83SAravind Gopalakrishnan break; 13939d0e8d83SAravind Gopalakrishnan case 0x5: 13949d0e8d83SAravind Gopalakrishnan channel = (sys_addr >> 9) & 0x3; 13959d0e8d83SAravind Gopalakrishnan break; 13969d0e8d83SAravind Gopalakrishnan } 13979d0e8d83SAravind Gopalakrishnan } 139818b94f66SAravind Gopalakrishnan return channel; 139918b94f66SAravind Gopalakrishnan } 140018b94f66SAravind Gopalakrishnan 140118b94f66SAravind Gopalakrishnan /* 1402229a7a11SBorislav Petkov * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory 1403f71d0a05SDoug Thompson * Interleaving Modes. 1404f71d0a05SDoug Thompson */ 1405b15f0fcaSBorislav Petkov static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, 1406229a7a11SBorislav Petkov bool hi_range_sel, u8 intlv_en) 14076163b5d4SDoug Thompson { 1408151fa71cSBorislav Petkov u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1; 14096163b5d4SDoug Thompson 14106163b5d4SDoug Thompson if (dct_ganging_enabled(pvt)) 1411229a7a11SBorislav Petkov return 0; 1412229a7a11SBorislav Petkov 1413229a7a11SBorislav Petkov if (hi_range_sel) 1414229a7a11SBorislav Petkov return dct_sel_high; 1415229a7a11SBorislav Petkov 1416f71d0a05SDoug Thompson /* 1417f71d0a05SDoug Thompson * see F2x110[DctSelIntLvAddr] - channel interleave mode 1418f71d0a05SDoug Thompson */ 1419229a7a11SBorislav Petkov if (dct_interleave_enabled(pvt)) { 1420229a7a11SBorislav Petkov u8 intlv_addr = dct_sel_interleave_addr(pvt); 14216163b5d4SDoug Thompson 1422229a7a11SBorislav Petkov /* return DCT select function: 0=DCT0, 1=DCT1 */ 1423229a7a11SBorislav Petkov if (!intlv_addr) 1424229a7a11SBorislav Petkov return sys_addr >> 6 & 1; 14256163b5d4SDoug Thompson 1426229a7a11SBorislav Petkov if (intlv_addr & 0x2) { 1427229a7a11SBorislav Petkov u8 shift = intlv_addr & 0x1 ? 9 : 6; 1428229a7a11SBorislav Petkov u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) % 2; 1429229a7a11SBorislav Petkov 1430229a7a11SBorislav Petkov return ((sys_addr >> shift) & 1) ^ temp; 14316163b5d4SDoug Thompson } 14326163b5d4SDoug Thompson 1433229a7a11SBorislav Petkov return (sys_addr >> (12 + hweight8(intlv_en))) & 1; 1434229a7a11SBorislav Petkov } 1435229a7a11SBorislav Petkov 1436229a7a11SBorislav Petkov if (dct_high_range_enabled(pvt)) 1437229a7a11SBorislav Petkov return ~dct_sel_high & 1; 14386163b5d4SDoug Thompson 14396163b5d4SDoug Thompson return 0; 14406163b5d4SDoug Thompson } 14416163b5d4SDoug Thompson 1442c8e518d5SBorislav Petkov /* Convert the sys_addr to the normalized DCT address */ 1443c7e5301aSDaniel J Blueman static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range, 1444c8e518d5SBorislav Petkov u64 sys_addr, bool hi_rng, 1445c8e518d5SBorislav Petkov u32 dct_sel_base_addr) 14466163b5d4SDoug Thompson { 14476163b5d4SDoug Thompson u64 chan_off; 1448c8e518d5SBorislav Petkov u64 dram_base = get_dram_base(pvt, range); 1449c8e518d5SBorislav Petkov u64 hole_off = f10_dhar_offset(pvt); 14506f3508f6SDan Carpenter u64 dct_sel_base_off = (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16; 14516163b5d4SDoug Thompson 1452c8e518d5SBorislav Petkov if (hi_rng) { 1453c8e518d5SBorislav Petkov /* 1454c8e518d5SBorislav Petkov * if 1455c8e518d5SBorislav Petkov * base address of high range is below 4Gb 1456c8e518d5SBorislav Petkov * (bits [47:27] at [31:11]) 1457c8e518d5SBorislav Petkov * DRAM address space on this DCT is hoisted above 4Gb && 1458c8e518d5SBorislav Petkov * sys_addr > 4Gb 1459c8e518d5SBorislav Petkov * 1460c8e518d5SBorislav Petkov * remove hole offset from sys_addr 1461c8e518d5SBorislav Petkov * else 1462c8e518d5SBorislav Petkov * remove high range offset from sys_addr 1463c8e518d5SBorislav Petkov */ 1464c8e518d5SBorislav Petkov if ((!(dct_sel_base_addr >> 16) || 1465c8e518d5SBorislav Petkov dct_sel_base_addr < dhar_base(pvt)) && 1466972ea17aSBorislav Petkov dhar_valid(pvt) && 1467c8e518d5SBorislav Petkov (sys_addr >= BIT_64(32))) 1468bc21fa57SBorislav Petkov chan_off = hole_off; 14696163b5d4SDoug Thompson else 14706163b5d4SDoug Thompson chan_off = dct_sel_base_off; 14716163b5d4SDoug Thompson } else { 1472c8e518d5SBorislav Petkov /* 1473c8e518d5SBorislav Petkov * if 1474c8e518d5SBorislav Petkov * we have a valid hole && 1475c8e518d5SBorislav Petkov * sys_addr > 4Gb 1476c8e518d5SBorislav Petkov * 1477c8e518d5SBorislav Petkov * remove hole 1478c8e518d5SBorislav Petkov * else 1479c8e518d5SBorislav Petkov * remove dram base to normalize to DCT address 1480c8e518d5SBorislav Petkov */ 1481972ea17aSBorislav Petkov if (dhar_valid(pvt) && (sys_addr >= BIT_64(32))) 1482bc21fa57SBorislav Petkov chan_off = hole_off; 14836163b5d4SDoug Thompson else 1484c8e518d5SBorislav Petkov chan_off = dram_base; 14856163b5d4SDoug Thompson } 14866163b5d4SDoug Thompson 148710ef6b0dSChen, Gong return (sys_addr & GENMASK_ULL(47,6)) - (chan_off & GENMASK_ULL(47,23)); 14886163b5d4SDoug Thompson } 14896163b5d4SDoug Thompson 14906163b5d4SDoug Thompson /* 14916163b5d4SDoug Thompson * checks if the csrow passed in is marked as SPARED, if so returns the new 14926163b5d4SDoug Thompson * spare row 14936163b5d4SDoug Thompson */ 149411c75eadSBorislav Petkov static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow) 14956163b5d4SDoug Thompson { 1496614ec9d8SBorislav Petkov int tmp_cs; 14976163b5d4SDoug Thompson 1498614ec9d8SBorislav Petkov if (online_spare_swap_done(pvt, dct) && 1499614ec9d8SBorislav Petkov csrow == online_spare_bad_dramcs(pvt, dct)) { 1500614ec9d8SBorislav Petkov 1501614ec9d8SBorislav Petkov for_each_chip_select(tmp_cs, dct, pvt) { 1502614ec9d8SBorislav Petkov if (chip_select_base(tmp_cs, dct, pvt) & 0x2) { 1503614ec9d8SBorislav Petkov csrow = tmp_cs; 1504614ec9d8SBorislav Petkov break; 1505614ec9d8SBorislav Petkov } 1506614ec9d8SBorislav Petkov } 15076163b5d4SDoug Thompson } 15086163b5d4SDoug Thompson return csrow; 15096163b5d4SDoug Thompson } 15106163b5d4SDoug Thompson 15116163b5d4SDoug Thompson /* 15126163b5d4SDoug Thompson * Iterate over the DRAM DCT "base" and "mask" registers looking for a 15136163b5d4SDoug Thompson * SystemAddr match on the specified 'ChannelSelect' and 'NodeID' 15146163b5d4SDoug Thompson * 15156163b5d4SDoug Thompson * Return: 15166163b5d4SDoug Thompson * -EINVAL: NOT FOUND 15176163b5d4SDoug Thompson * 0..csrow = Chip-Select Row 15186163b5d4SDoug Thompson */ 1519c7e5301aSDaniel J Blueman static int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct) 15206163b5d4SDoug Thompson { 15216163b5d4SDoug Thompson struct mem_ctl_info *mci; 15226163b5d4SDoug Thompson struct amd64_pvt *pvt; 152311c75eadSBorislav Petkov u64 cs_base, cs_mask; 15246163b5d4SDoug Thompson int cs_found = -EINVAL; 15256163b5d4SDoug Thompson int csrow; 15266163b5d4SDoug Thompson 15272ec591acSBorislav Petkov mci = edac_mc_find(nid); 15286163b5d4SDoug Thompson if (!mci) 15296163b5d4SDoug Thompson return cs_found; 15306163b5d4SDoug Thompson 15316163b5d4SDoug Thompson pvt = mci->pvt_info; 15326163b5d4SDoug Thompson 1533956b9ba1SJoe Perches edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct); 15346163b5d4SDoug Thompson 153511c75eadSBorislav Petkov for_each_chip_select(csrow, dct, pvt) { 153611c75eadSBorislav Petkov if (!csrow_enabled(csrow, dct, pvt)) 15376163b5d4SDoug Thompson continue; 15386163b5d4SDoug Thompson 153911c75eadSBorislav Petkov get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask); 15406163b5d4SDoug Thompson 1541956b9ba1SJoe Perches edac_dbg(1, " CSROW=%d CSBase=0x%llx CSMask=0x%llx\n", 15426163b5d4SDoug Thompson csrow, cs_base, cs_mask); 15436163b5d4SDoug Thompson 154411c75eadSBorislav Petkov cs_mask = ~cs_mask; 15456163b5d4SDoug Thompson 1546956b9ba1SJoe Perches edac_dbg(1, " (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n", 154711c75eadSBorislav Petkov (in_addr & cs_mask), (cs_base & cs_mask)); 15486163b5d4SDoug Thompson 154911c75eadSBorislav Petkov if ((in_addr & cs_mask) == (cs_base & cs_mask)) { 155018b94f66SAravind Gopalakrishnan if (pvt->fam == 0x15 && pvt->model >= 0x30) { 155118b94f66SAravind Gopalakrishnan cs_found = csrow; 155218b94f66SAravind Gopalakrishnan break; 155318b94f66SAravind Gopalakrishnan } 155411c75eadSBorislav Petkov cs_found = f10_process_possible_spare(pvt, dct, csrow); 15556163b5d4SDoug Thompson 1556956b9ba1SJoe Perches edac_dbg(1, " MATCH csrow=%d\n", cs_found); 15576163b5d4SDoug Thompson break; 15586163b5d4SDoug Thompson } 15596163b5d4SDoug Thompson } 15606163b5d4SDoug Thompson return cs_found; 15616163b5d4SDoug Thompson } 15626163b5d4SDoug Thompson 156395b0ef55SBorislav Petkov /* 156495b0ef55SBorislav Petkov * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is 156595b0ef55SBorislav Petkov * swapped with a region located at the bottom of memory so that the GPU can use 156695b0ef55SBorislav Petkov * the interleaved region and thus two channels. 156795b0ef55SBorislav Petkov */ 1568b15f0fcaSBorislav Petkov static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr) 156995b0ef55SBorislav Petkov { 157095b0ef55SBorislav Petkov u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr; 157195b0ef55SBorislav Petkov 1572a4b4bedcSBorislav Petkov if (pvt->fam == 0x10) { 157395b0ef55SBorislav Petkov /* only revC3 and revE have that feature */ 1574a4b4bedcSBorislav Petkov if (pvt->model < 4 || (pvt->model < 0xa && pvt->stepping < 3)) 157595b0ef55SBorislav Petkov return sys_addr; 157695b0ef55SBorislav Petkov } 157795b0ef55SBorislav Petkov 15787981a28fSAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F2, SWAP_INTLV_REG, &swap_reg); 157995b0ef55SBorislav Petkov 158095b0ef55SBorislav Petkov if (!(swap_reg & 0x1)) 158195b0ef55SBorislav Petkov return sys_addr; 158295b0ef55SBorislav Petkov 158395b0ef55SBorislav Petkov swap_base = (swap_reg >> 3) & 0x7f; 158495b0ef55SBorislav Petkov swap_limit = (swap_reg >> 11) & 0x7f; 158595b0ef55SBorislav Petkov rgn_size = (swap_reg >> 20) & 0x7f; 158695b0ef55SBorislav Petkov tmp_addr = sys_addr >> 27; 158795b0ef55SBorislav Petkov 158895b0ef55SBorislav Petkov if (!(sys_addr >> 34) && 158995b0ef55SBorislav Petkov (((tmp_addr >= swap_base) && 159095b0ef55SBorislav Petkov (tmp_addr <= swap_limit)) || 159195b0ef55SBorislav Petkov (tmp_addr < rgn_size))) 159295b0ef55SBorislav Petkov return sys_addr ^ (u64)swap_base << 27; 159395b0ef55SBorislav Petkov 159495b0ef55SBorislav Petkov return sys_addr; 159595b0ef55SBorislav Petkov } 159695b0ef55SBorislav Petkov 1597f71d0a05SDoug Thompson /* For a given @dram_range, check if @sys_addr falls within it. */ 1598e761359aSBorislav Petkov static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range, 159933ca0643SBorislav Petkov u64 sys_addr, int *chan_sel) 1600f71d0a05SDoug Thompson { 1601229a7a11SBorislav Petkov int cs_found = -EINVAL; 1602c8e518d5SBorislav Petkov u64 chan_addr; 16035d4b58e8SBorislav Petkov u32 dct_sel_base; 160411c75eadSBorislav Petkov u8 channel; 1605229a7a11SBorislav Petkov bool high_range = false; 1606f71d0a05SDoug Thompson 16077f19bf75SBorislav Petkov u8 node_id = dram_dst_node(pvt, range); 1608229a7a11SBorislav Petkov u8 intlv_en = dram_intlv_en(pvt, range); 16097f19bf75SBorislav Petkov u32 intlv_sel = dram_intlv_sel(pvt, range); 1610f71d0a05SDoug Thompson 1611956b9ba1SJoe Perches edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n", 1612c8e518d5SBorislav Petkov range, sys_addr, get_dram_limit(pvt, range)); 1613f71d0a05SDoug Thompson 1614355fba60SBorislav Petkov if (dhar_valid(pvt) && 1615355fba60SBorislav Petkov dhar_base(pvt) <= sys_addr && 1616355fba60SBorislav Petkov sys_addr < BIT_64(32)) { 1617355fba60SBorislav Petkov amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n", 1618355fba60SBorislav Petkov sys_addr); 1619f71d0a05SDoug Thompson return -EINVAL; 1620355fba60SBorislav Petkov } 1621355fba60SBorislav Petkov 1622f030ddfbSBorislav Petkov if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en))) 1623355fba60SBorislav Petkov return -EINVAL; 1624f71d0a05SDoug Thompson 1625b15f0fcaSBorislav Petkov sys_addr = f1x_swap_interleaved_region(pvt, sys_addr); 162695b0ef55SBorislav Petkov 1627f71d0a05SDoug Thompson dct_sel_base = dct_sel_baseaddr(pvt); 1628f71d0a05SDoug Thompson 1629f71d0a05SDoug Thompson /* 1630f71d0a05SDoug Thompson * check whether addresses >= DctSelBaseAddr[47:27] are to be used to 1631f71d0a05SDoug Thompson * select between DCT0 and DCT1. 1632f71d0a05SDoug Thompson */ 1633f71d0a05SDoug Thompson if (dct_high_range_enabled(pvt) && 1634f71d0a05SDoug Thompson !dct_ganging_enabled(pvt) && 1635f71d0a05SDoug Thompson ((sys_addr >> 27) >= (dct_sel_base >> 11))) 1636229a7a11SBorislav Petkov high_range = true; 1637f71d0a05SDoug Thompson 1638b15f0fcaSBorislav Petkov channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en); 1639f71d0a05SDoug Thompson 1640b15f0fcaSBorislav Petkov chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr, 1641c8e518d5SBorislav Petkov high_range, dct_sel_base); 1642f71d0a05SDoug Thompson 1643e2f79dbdSBorislav Petkov /* Remove node interleaving, see F1x120 */ 1644e2f79dbdSBorislav Petkov if (intlv_en) 1645e2f79dbdSBorislav Petkov chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) | 1646e2f79dbdSBorislav Petkov (chan_addr & 0xfff); 1647f71d0a05SDoug Thompson 16485d4b58e8SBorislav Petkov /* remove channel interleave */ 1649f71d0a05SDoug Thompson if (dct_interleave_enabled(pvt) && 1650f71d0a05SDoug Thompson !dct_high_range_enabled(pvt) && 1651f71d0a05SDoug Thompson !dct_ganging_enabled(pvt)) { 16525d4b58e8SBorislav Petkov 16535d4b58e8SBorislav Petkov if (dct_sel_interleave_addr(pvt) != 1) { 16545d4b58e8SBorislav Petkov if (dct_sel_interleave_addr(pvt) == 0x3) 16555d4b58e8SBorislav Petkov /* hash 9 */ 16565d4b58e8SBorislav Petkov chan_addr = ((chan_addr >> 10) << 9) | 16575d4b58e8SBorislav Petkov (chan_addr & 0x1ff); 16585d4b58e8SBorislav Petkov else 16595d4b58e8SBorislav Petkov /* A[6] or hash 6 */ 16605d4b58e8SBorislav Petkov chan_addr = ((chan_addr >> 7) << 6) | 16615d4b58e8SBorislav Petkov (chan_addr & 0x3f); 16625d4b58e8SBorislav Petkov } else 16635d4b58e8SBorislav Petkov /* A[12] */ 16645d4b58e8SBorislav Petkov chan_addr = ((chan_addr >> 13) << 12) | 16655d4b58e8SBorislav Petkov (chan_addr & 0xfff); 1666f71d0a05SDoug Thompson } 1667f71d0a05SDoug Thompson 1668956b9ba1SJoe Perches edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr); 1669f71d0a05SDoug Thompson 1670b15f0fcaSBorislav Petkov cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel); 1671f71d0a05SDoug Thompson 167233ca0643SBorislav Petkov if (cs_found >= 0) 1673f71d0a05SDoug Thompson *chan_sel = channel; 167433ca0643SBorislav Petkov 1675f71d0a05SDoug Thompson return cs_found; 1676f71d0a05SDoug Thompson } 1677f71d0a05SDoug Thompson 167818b94f66SAravind Gopalakrishnan static int f15_m30h_match_to_this_node(struct amd64_pvt *pvt, unsigned range, 167918b94f66SAravind Gopalakrishnan u64 sys_addr, int *chan_sel) 168018b94f66SAravind Gopalakrishnan { 168118b94f66SAravind Gopalakrishnan int cs_found = -EINVAL; 168218b94f66SAravind Gopalakrishnan int num_dcts_intlv = 0; 168318b94f66SAravind Gopalakrishnan u64 chan_addr, chan_offset; 168418b94f66SAravind Gopalakrishnan u64 dct_base, dct_limit; 168518b94f66SAravind Gopalakrishnan u32 dct_cont_base_reg, dct_cont_limit_reg, tmp; 168618b94f66SAravind Gopalakrishnan u8 channel, alias_channel, leg_mmio_hole, dct_sel, dct_offset_en; 168718b94f66SAravind Gopalakrishnan 168818b94f66SAravind Gopalakrishnan u64 dhar_offset = f10_dhar_offset(pvt); 168918b94f66SAravind Gopalakrishnan u8 intlv_addr = dct_sel_interleave_addr(pvt); 169018b94f66SAravind Gopalakrishnan u8 node_id = dram_dst_node(pvt, range); 169118b94f66SAravind Gopalakrishnan u8 intlv_en = dram_intlv_en(pvt, range); 169218b94f66SAravind Gopalakrishnan 169318b94f66SAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F1, DRAM_CONT_BASE, &dct_cont_base_reg); 169418b94f66SAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F1, DRAM_CONT_LIMIT, &dct_cont_limit_reg); 169518b94f66SAravind Gopalakrishnan 169618b94f66SAravind Gopalakrishnan dct_offset_en = (u8) ((dct_cont_base_reg >> 3) & BIT(0)); 169718b94f66SAravind Gopalakrishnan dct_sel = (u8) ((dct_cont_base_reg >> 4) & 0x7); 169818b94f66SAravind Gopalakrishnan 169918b94f66SAravind Gopalakrishnan edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n", 170018b94f66SAravind Gopalakrishnan range, sys_addr, get_dram_limit(pvt, range)); 170118b94f66SAravind Gopalakrishnan 170218b94f66SAravind Gopalakrishnan if (!(get_dram_base(pvt, range) <= sys_addr) && 170318b94f66SAravind Gopalakrishnan !(get_dram_limit(pvt, range) >= sys_addr)) 170418b94f66SAravind Gopalakrishnan return -EINVAL; 170518b94f66SAravind Gopalakrishnan 170618b94f66SAravind Gopalakrishnan if (dhar_valid(pvt) && 170718b94f66SAravind Gopalakrishnan dhar_base(pvt) <= sys_addr && 170818b94f66SAravind Gopalakrishnan sys_addr < BIT_64(32)) { 170918b94f66SAravind Gopalakrishnan amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n", 171018b94f66SAravind Gopalakrishnan sys_addr); 171118b94f66SAravind Gopalakrishnan return -EINVAL; 171218b94f66SAravind Gopalakrishnan } 171318b94f66SAravind Gopalakrishnan 171418b94f66SAravind Gopalakrishnan /* Verify sys_addr is within DCT Range. */ 17154fc06b31SAravind Gopalakrishnan dct_base = (u64) dct_sel_baseaddr(pvt); 17164fc06b31SAravind Gopalakrishnan dct_limit = (dct_cont_limit_reg >> 11) & 0x1FFF; 171718b94f66SAravind Gopalakrishnan 171818b94f66SAravind Gopalakrishnan if (!(dct_cont_base_reg & BIT(0)) && 17194fc06b31SAravind Gopalakrishnan !(dct_base <= (sys_addr >> 27) && 17204fc06b31SAravind Gopalakrishnan dct_limit >= (sys_addr >> 27))) 172118b94f66SAravind Gopalakrishnan return -EINVAL; 172218b94f66SAravind Gopalakrishnan 172318b94f66SAravind Gopalakrishnan /* Verify number of dct's that participate in channel interleaving. */ 172418b94f66SAravind Gopalakrishnan num_dcts_intlv = (int) hweight8(intlv_en); 172518b94f66SAravind Gopalakrishnan 172618b94f66SAravind Gopalakrishnan if (!(num_dcts_intlv % 2 == 0) || (num_dcts_intlv > 4)) 172718b94f66SAravind Gopalakrishnan return -EINVAL; 172818b94f66SAravind Gopalakrishnan 172918b94f66SAravind Gopalakrishnan channel = f15_m30h_determine_channel(pvt, sys_addr, intlv_en, 173018b94f66SAravind Gopalakrishnan num_dcts_intlv, dct_sel); 173118b94f66SAravind Gopalakrishnan 173218b94f66SAravind Gopalakrishnan /* Verify we stay within the MAX number of channels allowed */ 17337f3f5240SAravind Gopalakrishnan if (channel > 3) 173418b94f66SAravind Gopalakrishnan return -EINVAL; 173518b94f66SAravind Gopalakrishnan 173618b94f66SAravind Gopalakrishnan leg_mmio_hole = (u8) (dct_cont_base_reg >> 1 & BIT(0)); 173718b94f66SAravind Gopalakrishnan 173818b94f66SAravind Gopalakrishnan /* Get normalized DCT addr */ 173918b94f66SAravind Gopalakrishnan if (leg_mmio_hole && (sys_addr >= BIT_64(32))) 174018b94f66SAravind Gopalakrishnan chan_offset = dhar_offset; 174118b94f66SAravind Gopalakrishnan else 17424fc06b31SAravind Gopalakrishnan chan_offset = dct_base << 27; 174318b94f66SAravind Gopalakrishnan 174418b94f66SAravind Gopalakrishnan chan_addr = sys_addr - chan_offset; 174518b94f66SAravind Gopalakrishnan 174618b94f66SAravind Gopalakrishnan /* remove channel interleave */ 174718b94f66SAravind Gopalakrishnan if (num_dcts_intlv == 2) { 174818b94f66SAravind Gopalakrishnan if (intlv_addr == 0x4) 174918b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 9) << 8) | 175018b94f66SAravind Gopalakrishnan (chan_addr & 0xff); 175118b94f66SAravind Gopalakrishnan else if (intlv_addr == 0x5) 175218b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 10) << 9) | 175318b94f66SAravind Gopalakrishnan (chan_addr & 0x1ff); 175418b94f66SAravind Gopalakrishnan else 175518b94f66SAravind Gopalakrishnan return -EINVAL; 175618b94f66SAravind Gopalakrishnan 175718b94f66SAravind Gopalakrishnan } else if (num_dcts_intlv == 4) { 175818b94f66SAravind Gopalakrishnan if (intlv_addr == 0x4) 175918b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 10) << 8) | 176018b94f66SAravind Gopalakrishnan (chan_addr & 0xff); 176118b94f66SAravind Gopalakrishnan else if (intlv_addr == 0x5) 176218b94f66SAravind Gopalakrishnan chan_addr = ((chan_addr >> 11) << 9) | 176318b94f66SAravind Gopalakrishnan (chan_addr & 0x1ff); 176418b94f66SAravind Gopalakrishnan else 176518b94f66SAravind Gopalakrishnan return -EINVAL; 176618b94f66SAravind Gopalakrishnan } 176718b94f66SAravind Gopalakrishnan 176818b94f66SAravind Gopalakrishnan if (dct_offset_en) { 176918b94f66SAravind Gopalakrishnan amd64_read_pci_cfg(pvt->F1, 177018b94f66SAravind Gopalakrishnan DRAM_CONT_HIGH_OFF + (int) channel * 4, 177118b94f66SAravind Gopalakrishnan &tmp); 17724fc06b31SAravind Gopalakrishnan chan_addr += (u64) ((tmp >> 11) & 0xfff) << 27; 177318b94f66SAravind Gopalakrishnan } 177418b94f66SAravind Gopalakrishnan 177518b94f66SAravind Gopalakrishnan f15h_select_dct(pvt, channel); 177618b94f66SAravind Gopalakrishnan 177718b94f66SAravind Gopalakrishnan edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr); 177818b94f66SAravind Gopalakrishnan 177918b94f66SAravind Gopalakrishnan /* 178018b94f66SAravind Gopalakrishnan * Find Chip select: 178118b94f66SAravind Gopalakrishnan * if channel = 3, then alias it to 1. This is because, in F15 M30h, 178218b94f66SAravind Gopalakrishnan * there is support for 4 DCT's, but only 2 are currently functional. 178318b94f66SAravind Gopalakrishnan * They are DCT0 and DCT3. But we have read all registers of DCT3 into 178418b94f66SAravind Gopalakrishnan * pvt->csels[1]. So we need to use '1' here to get correct info. 178518b94f66SAravind Gopalakrishnan * Refer F15 M30h BKDG Section 2.10 and 2.10.3 for clarifications. 178618b94f66SAravind Gopalakrishnan */ 178718b94f66SAravind Gopalakrishnan alias_channel = (channel == 3) ? 1 : channel; 178818b94f66SAravind Gopalakrishnan 178918b94f66SAravind Gopalakrishnan cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, alias_channel); 179018b94f66SAravind Gopalakrishnan 179118b94f66SAravind Gopalakrishnan if (cs_found >= 0) 179218b94f66SAravind Gopalakrishnan *chan_sel = alias_channel; 179318b94f66SAravind Gopalakrishnan 179418b94f66SAravind Gopalakrishnan return cs_found; 179518b94f66SAravind Gopalakrishnan } 179618b94f66SAravind Gopalakrishnan 179718b94f66SAravind Gopalakrishnan static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt, 179818b94f66SAravind Gopalakrishnan u64 sys_addr, 179933ca0643SBorislav Petkov int *chan_sel) 1800f71d0a05SDoug Thompson { 1801e761359aSBorislav Petkov int cs_found = -EINVAL; 1802e761359aSBorislav Petkov unsigned range; 1803f71d0a05SDoug Thompson 18047f19bf75SBorislav Petkov for (range = 0; range < DRAM_RANGES; range++) { 18057f19bf75SBorislav Petkov if (!dram_rw(pvt, range)) 1806f71d0a05SDoug Thompson continue; 1807f71d0a05SDoug Thompson 180818b94f66SAravind Gopalakrishnan if (pvt->fam == 0x15 && pvt->model >= 0x30) 180918b94f66SAravind Gopalakrishnan cs_found = f15_m30h_match_to_this_node(pvt, range, 181018b94f66SAravind Gopalakrishnan sys_addr, 181118b94f66SAravind Gopalakrishnan chan_sel); 1812f71d0a05SDoug Thompson 181318b94f66SAravind Gopalakrishnan else if ((get_dram_base(pvt, range) <= sys_addr) && 181418b94f66SAravind Gopalakrishnan (get_dram_limit(pvt, range) >= sys_addr)) { 1815b15f0fcaSBorislav Petkov cs_found = f1x_match_to_this_node(pvt, range, 181633ca0643SBorislav Petkov sys_addr, chan_sel); 1817f71d0a05SDoug Thompson if (cs_found >= 0) 1818f71d0a05SDoug Thompson break; 1819f71d0a05SDoug Thompson } 1820f71d0a05SDoug Thompson } 1821f71d0a05SDoug Thompson return cs_found; 1822f71d0a05SDoug Thompson } 1823f71d0a05SDoug Thompson 1824f71d0a05SDoug Thompson /* 1825bdc30a0cSBorislav Petkov * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps 1826bdc30a0cSBorislav Petkov * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW). 1827f71d0a05SDoug Thompson * 1828bdc30a0cSBorislav Petkov * The @sys_addr is usually an error address received from the hardware 1829bdc30a0cSBorislav Petkov * (MCX_ADDR). 1830f71d0a05SDoug Thompson */ 1831b15f0fcaSBorislav Petkov static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr, 183233ca0643SBorislav Petkov struct err_info *err) 1833f71d0a05SDoug Thompson { 1834f71d0a05SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 1835f71d0a05SDoug Thompson 183633ca0643SBorislav Petkov error_address_to_page_and_offset(sys_addr, err); 1837ab5a503cSMauro Carvalho Chehab 183833ca0643SBorislav Petkov err->csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &err->channel); 183933ca0643SBorislav Petkov if (err->csrow < 0) { 184033ca0643SBorislav Petkov err->err_code = ERR_CSROW; 1841bdc30a0cSBorislav Petkov return; 1842bdc30a0cSBorislav Petkov } 1843bdc30a0cSBorislav Petkov 1844f71d0a05SDoug Thompson /* 1845bdc30a0cSBorislav Petkov * We need the syndromes for channel detection only when we're 1846bdc30a0cSBorislav Petkov * ganged. Otherwise @chan should already contain the channel at 1847bdc30a0cSBorislav Petkov * this point. 1848f71d0a05SDoug Thompson */ 1849a97fa68eSBorislav Petkov if (dct_ganging_enabled(pvt)) 185033ca0643SBorislav Petkov err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome); 1851f71d0a05SDoug Thompson } 1852f71d0a05SDoug Thompson 1853f71d0a05SDoug Thompson /* 18548566c4dfSBorislav Petkov * debug routine to display the memory sizes of all logical DIMMs and its 1855cb328507SBorislav Petkov * CSROWs 1856f71d0a05SDoug Thompson */ 1857d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl) 1858f71d0a05SDoug Thompson { 1859bb89f5a0SBorislav Petkov int dimm, size0, size1; 1860525a1b20SBorislav Petkov u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases; 1861525a1b20SBorislav Petkov u32 dbam = ctrl ? pvt->dbam1 : pvt->dbam0; 1862f71d0a05SDoug Thompson 1863a4b4bedcSBorislav Petkov if (pvt->fam == 0xf) { 18648566c4dfSBorislav Petkov /* K8 families < revF not supported yet */ 18651433eb99SBorislav Petkov if (pvt->ext_model < K8_REV_F) 18668566c4dfSBorislav Petkov return; 18678566c4dfSBorislav Petkov else 18688566c4dfSBorislav Petkov WARN_ON(ctrl != 0); 18698566c4dfSBorislav Petkov } 18708566c4dfSBorislav Petkov 18717981a28fSAravind Gopalakrishnan if (pvt->fam == 0x10) { 18727981a28fSAravind Gopalakrishnan dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1 18737981a28fSAravind Gopalakrishnan : pvt->dbam0; 18747981a28fSAravind Gopalakrishnan dcsb = (ctrl && !dct_ganging_enabled(pvt)) ? 18757981a28fSAravind Gopalakrishnan pvt->csels[1].csbases : 18767981a28fSAravind Gopalakrishnan pvt->csels[0].csbases; 18777981a28fSAravind Gopalakrishnan } else if (ctrl) { 18787981a28fSAravind Gopalakrishnan dbam = pvt->dbam0; 18797981a28fSAravind Gopalakrishnan dcsb = pvt->csels[1].csbases; 18807981a28fSAravind Gopalakrishnan } 1881956b9ba1SJoe Perches edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n", 1882956b9ba1SJoe Perches ctrl, dbam); 1883f71d0a05SDoug Thompson 18848566c4dfSBorislav Petkov edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl); 18858566c4dfSBorislav Petkov 1886f71d0a05SDoug Thompson /* Dump memory sizes for DIMM and its CSROWs */ 1887f71d0a05SDoug Thompson for (dimm = 0; dimm < 4; dimm++) { 1888f71d0a05SDoug Thompson 1889f71d0a05SDoug Thompson size0 = 0; 189011c75eadSBorislav Petkov if (dcsb[dimm*2] & DCSB_CS_ENABLE) 1891a597d2a5SAravind Gopalakrishnan /* For f15m60h, need multiplier for LRDIMM cs_size 1892a597d2a5SAravind Gopalakrishnan * calculation. We pass 'dimm' value to the dbam_to_cs 1893a597d2a5SAravind Gopalakrishnan * mapper so we can find the multiplier from the 1894a597d2a5SAravind Gopalakrishnan * corresponding DCSM. 1895a597d2a5SAravind Gopalakrishnan */ 189641d8bfabSBorislav Petkov size0 = pvt->ops->dbam_to_cs(pvt, ctrl, 1897a597d2a5SAravind Gopalakrishnan DBAM_DIMM(dimm, dbam), 1898a597d2a5SAravind Gopalakrishnan dimm); 1899f71d0a05SDoug Thompson 1900f71d0a05SDoug Thompson size1 = 0; 190111c75eadSBorislav Petkov if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE) 190241d8bfabSBorislav Petkov size1 = pvt->ops->dbam_to_cs(pvt, ctrl, 1903a597d2a5SAravind Gopalakrishnan DBAM_DIMM(dimm, dbam), 1904a597d2a5SAravind Gopalakrishnan dimm); 1905f71d0a05SDoug Thompson 190624f9a7feSBorislav Petkov amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n", 1907bb89f5a0SBorislav Petkov dimm * 2, size0, 1908bb89f5a0SBorislav Petkov dimm * 2 + 1, size1); 1909f71d0a05SDoug Thompson } 1910f71d0a05SDoug Thompson } 1911f71d0a05SDoug Thompson 1912d1ea71cdSBorislav Petkov static struct amd64_family_type family_types[] = { 19134d37607aSDoug Thompson [K8_CPUS] = { 19140092b20dSBorislav Petkov .ctl_name = "K8", 19158d5b5d9cSBorislav Petkov .f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP, 19163f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL, 19174d37607aSDoug Thompson .ops = { 19184d37607aSDoug Thompson .early_channel_count = k8_early_channel_count, 19194d37607aSDoug Thompson .map_sysaddr_to_csrow = k8_map_sysaddr_to_csrow, 19201433eb99SBorislav Petkov .dbam_to_cs = k8_dbam_to_chip_select, 19214d37607aSDoug Thompson } 19224d37607aSDoug Thompson }, 19234d37607aSDoug Thompson [F10_CPUS] = { 19240092b20dSBorislav Petkov .ctl_name = "F10h", 19258d5b5d9cSBorislav Petkov .f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP, 19263f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM, 19274d37607aSDoug Thompson .ops = { 19287d20d14dSBorislav Petkov .early_channel_count = f1x_early_channel_count, 1929b15f0fcaSBorislav Petkov .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 19301433eb99SBorislav Petkov .dbam_to_cs = f10_dbam_to_chip_select, 1931b2b0c605SBorislav Petkov } 1932b2b0c605SBorislav Petkov }, 1933b2b0c605SBorislav Petkov [F15_CPUS] = { 1934b2b0c605SBorislav Petkov .ctl_name = "F15h", 1935df71a053SBorislav Petkov .f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1, 19363f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2, 1937b2b0c605SBorislav Petkov .ops = { 19387d20d14dSBorislav Petkov .early_channel_count = f1x_early_channel_count, 1939b15f0fcaSBorislav Petkov .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 194041d8bfabSBorislav Petkov .dbam_to_cs = f15_dbam_to_chip_select, 19414d37607aSDoug Thompson } 19424d37607aSDoug Thompson }, 194318b94f66SAravind Gopalakrishnan [F15_M30H_CPUS] = { 194418b94f66SAravind Gopalakrishnan .ctl_name = "F15h_M30h", 194518b94f66SAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1, 19463f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2, 194718b94f66SAravind Gopalakrishnan .ops = { 194818b94f66SAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 194918b94f66SAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 195018b94f66SAravind Gopalakrishnan .dbam_to_cs = f16_dbam_to_chip_select, 195118b94f66SAravind Gopalakrishnan } 195218b94f66SAravind Gopalakrishnan }, 1953a597d2a5SAravind Gopalakrishnan [F15_M60H_CPUS] = { 1954a597d2a5SAravind Gopalakrishnan .ctl_name = "F15h_M60h", 1955a597d2a5SAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1, 19563f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2, 1957a597d2a5SAravind Gopalakrishnan .ops = { 1958a597d2a5SAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 1959a597d2a5SAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 1960a597d2a5SAravind Gopalakrishnan .dbam_to_cs = f15_m60h_dbam_to_chip_select, 1961a597d2a5SAravind Gopalakrishnan } 1962a597d2a5SAravind Gopalakrishnan }, 196394c1acf2SAravind Gopalakrishnan [F16_CPUS] = { 196494c1acf2SAravind Gopalakrishnan .ctl_name = "F16h", 196594c1acf2SAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1, 19663f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2, 196794c1acf2SAravind Gopalakrishnan .ops = { 196894c1acf2SAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 196994c1acf2SAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 197094c1acf2SAravind Gopalakrishnan .dbam_to_cs = f16_dbam_to_chip_select, 197194c1acf2SAravind Gopalakrishnan } 197294c1acf2SAravind Gopalakrishnan }, 197385a8885bSAravind Gopalakrishnan [F16_M30H_CPUS] = { 197485a8885bSAravind Gopalakrishnan .ctl_name = "F16h_M30h", 197585a8885bSAravind Gopalakrishnan .f1_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F1, 19763f37a36bSBorislav Petkov .f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2, 197785a8885bSAravind Gopalakrishnan .ops = { 197885a8885bSAravind Gopalakrishnan .early_channel_count = f1x_early_channel_count, 197985a8885bSAravind Gopalakrishnan .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, 198085a8885bSAravind Gopalakrishnan .dbam_to_cs = f16_dbam_to_chip_select, 198185a8885bSAravind Gopalakrishnan } 198285a8885bSAravind Gopalakrishnan }, 19834d37607aSDoug Thompson }; 19844d37607aSDoug Thompson 1985b1289d6fSDoug Thompson /* 1986bfc04aecSBorislav Petkov * These are tables of eigenvectors (one per line) which can be used for the 1987bfc04aecSBorislav Petkov * construction of the syndrome tables. The modified syndrome search algorithm 1988bfc04aecSBorislav Petkov * uses those to find the symbol in error and thus the DIMM. 1989b1289d6fSDoug Thompson * 1990bfc04aecSBorislav Petkov * Algorithm courtesy of Ross LaFetra from AMD. 1991b1289d6fSDoug Thompson */ 1992c7e5301aSDaniel J Blueman static const u16 x4_vectors[] = { 1993bfc04aecSBorislav Petkov 0x2f57, 0x1afe, 0x66cc, 0xdd88, 1994bfc04aecSBorislav Petkov 0x11eb, 0x3396, 0x7f4c, 0xeac8, 1995bfc04aecSBorislav Petkov 0x0001, 0x0002, 0x0004, 0x0008, 1996bfc04aecSBorislav Petkov 0x1013, 0x3032, 0x4044, 0x8088, 1997bfc04aecSBorislav Petkov 0x106b, 0x30d6, 0x70fc, 0xe0a8, 1998bfc04aecSBorislav Petkov 0x4857, 0xc4fe, 0x13cc, 0x3288, 1999bfc04aecSBorislav Petkov 0x1ac5, 0x2f4a, 0x5394, 0xa1e8, 2000bfc04aecSBorislav Petkov 0x1f39, 0x251e, 0xbd6c, 0x6bd8, 2001bfc04aecSBorislav Petkov 0x15c1, 0x2a42, 0x89ac, 0x4758, 2002bfc04aecSBorislav Petkov 0x2b03, 0x1602, 0x4f0c, 0xca08, 2003bfc04aecSBorislav Petkov 0x1f07, 0x3a0e, 0x6b04, 0xbd08, 2004bfc04aecSBorislav Petkov 0x8ba7, 0x465e, 0x244c, 0x1cc8, 2005bfc04aecSBorislav Petkov 0x2b87, 0x164e, 0x642c, 0xdc18, 2006bfc04aecSBorislav Petkov 0x40b9, 0x80de, 0x1094, 0x20e8, 2007bfc04aecSBorislav Petkov 0x27db, 0x1eb6, 0x9dac, 0x7b58, 2008bfc04aecSBorislav Petkov 0x11c1, 0x2242, 0x84ac, 0x4c58, 2009bfc04aecSBorislav Petkov 0x1be5, 0x2d7a, 0x5e34, 0xa718, 2010bfc04aecSBorislav Petkov 0x4b39, 0x8d1e, 0x14b4, 0x28d8, 2011bfc04aecSBorislav Petkov 0x4c97, 0xc87e, 0x11fc, 0x33a8, 2012bfc04aecSBorislav Petkov 0x8e97, 0x497e, 0x2ffc, 0x1aa8, 2013bfc04aecSBorislav Petkov 0x16b3, 0x3d62, 0x4f34, 0x8518, 2014bfc04aecSBorislav Petkov 0x1e2f, 0x391a, 0x5cac, 0xf858, 2015bfc04aecSBorislav Petkov 0x1d9f, 0x3b7a, 0x572c, 0xfe18, 2016bfc04aecSBorislav Petkov 0x15f5, 0x2a5a, 0x5264, 0xa3b8, 2017bfc04aecSBorislav Petkov 0x1dbb, 0x3b66, 0x715c, 0xe3f8, 2018bfc04aecSBorislav Petkov 0x4397, 0xc27e, 0x17fc, 0x3ea8, 2019bfc04aecSBorislav Petkov 0x1617, 0x3d3e, 0x6464, 0xb8b8, 2020bfc04aecSBorislav Petkov 0x23ff, 0x12aa, 0xab6c, 0x56d8, 2021bfc04aecSBorislav Petkov 0x2dfb, 0x1ba6, 0x913c, 0x7328, 2022bfc04aecSBorislav Petkov 0x185d, 0x2ca6, 0x7914, 0x9e28, 2023bfc04aecSBorislav Petkov 0x171b, 0x3e36, 0x7d7c, 0xebe8, 2024bfc04aecSBorislav Petkov 0x4199, 0x82ee, 0x19f4, 0x2e58, 2025bfc04aecSBorislav Petkov 0x4807, 0xc40e, 0x130c, 0x3208, 2026bfc04aecSBorislav Petkov 0x1905, 0x2e0a, 0x5804, 0xac08, 2027bfc04aecSBorislav Petkov 0x213f, 0x132a, 0xadfc, 0x5ba8, 2028bfc04aecSBorislav Petkov 0x19a9, 0x2efe, 0xb5cc, 0x6f88, 2029b1289d6fSDoug Thompson }; 2030b1289d6fSDoug Thompson 2031c7e5301aSDaniel J Blueman static const u16 x8_vectors[] = { 2032bfc04aecSBorislav Petkov 0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480, 2033bfc04aecSBorislav Petkov 0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80, 2034bfc04aecSBorislav Petkov 0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80, 2035bfc04aecSBorislav Petkov 0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80, 2036bfc04aecSBorislav Petkov 0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780, 2037bfc04aecSBorislav Petkov 0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080, 2038bfc04aecSBorislav Petkov 0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080, 2039bfc04aecSBorislav Petkov 0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080, 2040bfc04aecSBorislav Petkov 0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80, 2041bfc04aecSBorislav Petkov 0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580, 2042bfc04aecSBorislav Petkov 0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880, 2043bfc04aecSBorislav Petkov 0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280, 2044bfc04aecSBorislav Petkov 0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180, 2045bfc04aecSBorislav Petkov 0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580, 2046bfc04aecSBorislav Petkov 0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280, 2047bfc04aecSBorislav Petkov 0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180, 2048bfc04aecSBorislav Petkov 0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080, 2049bfc04aecSBorislav Petkov 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 2050bfc04aecSBorislav Petkov 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000, 2051bfc04aecSBorislav Petkov }; 2052bfc04aecSBorislav Petkov 2053c7e5301aSDaniel J Blueman static int decode_syndrome(u16 syndrome, const u16 *vectors, unsigned num_vecs, 2054d34a6ecdSBorislav Petkov unsigned v_dim) 2055b1289d6fSDoug Thompson { 2056bfc04aecSBorislav Petkov unsigned int i, err_sym; 2057b1289d6fSDoug Thompson 2058bfc04aecSBorislav Petkov for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) { 2059bfc04aecSBorislav Petkov u16 s = syndrome; 2060d34a6ecdSBorislav Petkov unsigned v_idx = err_sym * v_dim; 2061d34a6ecdSBorislav Petkov unsigned v_end = (err_sym + 1) * v_dim; 2062b1289d6fSDoug Thompson 2063bfc04aecSBorislav Petkov /* walk over all 16 bits of the syndrome */ 2064bfc04aecSBorislav Petkov for (i = 1; i < (1U << 16); i <<= 1) { 2065bfc04aecSBorislav Petkov 2066bfc04aecSBorislav Petkov /* if bit is set in that eigenvector... */ 2067bfc04aecSBorislav Petkov if (v_idx < v_end && vectors[v_idx] & i) { 2068bfc04aecSBorislav Petkov u16 ev_comp = vectors[v_idx++]; 2069bfc04aecSBorislav Petkov 2070bfc04aecSBorislav Petkov /* ... and bit set in the modified syndrome, */ 2071bfc04aecSBorislav Petkov if (s & i) { 2072bfc04aecSBorislav Petkov /* remove it. */ 2073bfc04aecSBorislav Petkov s ^= ev_comp; 2074bfc04aecSBorislav Petkov 2075bfc04aecSBorislav Petkov if (!s) 2076bfc04aecSBorislav Petkov return err_sym; 2077bfc04aecSBorislav Petkov } 2078bfc04aecSBorislav Petkov 2079bfc04aecSBorislav Petkov } else if (s & i) 2080bfc04aecSBorislav Petkov /* can't get to zero, move to next symbol */ 2081bfc04aecSBorislav Petkov break; 2082bfc04aecSBorislav Petkov } 2083b1289d6fSDoug Thompson } 2084b1289d6fSDoug Thompson 2085956b9ba1SJoe Perches edac_dbg(0, "syndrome(%x) not found\n", syndrome); 2086b1289d6fSDoug Thompson return -1; 2087b1289d6fSDoug Thompson } 2088d27bf6faSDoug Thompson 2089bfc04aecSBorislav Petkov static int map_err_sym_to_channel(int err_sym, int sym_size) 2090bfc04aecSBorislav Petkov { 2091bfc04aecSBorislav Petkov if (sym_size == 4) 2092bfc04aecSBorislav Petkov switch (err_sym) { 2093bfc04aecSBorislav Petkov case 0x20: 2094bfc04aecSBorislav Petkov case 0x21: 2095bfc04aecSBorislav Petkov return 0; 2096bfc04aecSBorislav Petkov break; 2097bfc04aecSBorislav Petkov case 0x22: 2098bfc04aecSBorislav Petkov case 0x23: 2099bfc04aecSBorislav Petkov return 1; 2100bfc04aecSBorislav Petkov break; 2101bfc04aecSBorislav Petkov default: 2102bfc04aecSBorislav Petkov return err_sym >> 4; 2103bfc04aecSBorislav Petkov break; 2104bfc04aecSBorislav Petkov } 2105bfc04aecSBorislav Petkov /* x8 symbols */ 2106bfc04aecSBorislav Petkov else 2107bfc04aecSBorislav Petkov switch (err_sym) { 2108bfc04aecSBorislav Petkov /* imaginary bits not in a DIMM */ 2109bfc04aecSBorislav Petkov case 0x10: 2110bfc04aecSBorislav Petkov WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n", 2111bfc04aecSBorislav Petkov err_sym); 2112bfc04aecSBorislav Petkov return -1; 2113bfc04aecSBorislav Petkov break; 2114bfc04aecSBorislav Petkov 2115bfc04aecSBorislav Petkov case 0x11: 2116bfc04aecSBorislav Petkov return 0; 2117bfc04aecSBorislav Petkov break; 2118bfc04aecSBorislav Petkov case 0x12: 2119bfc04aecSBorislav Petkov return 1; 2120bfc04aecSBorislav Petkov break; 2121bfc04aecSBorislav Petkov default: 2122bfc04aecSBorislav Petkov return err_sym >> 3; 2123bfc04aecSBorislav Petkov break; 2124bfc04aecSBorislav Petkov } 2125bfc04aecSBorislav Petkov return -1; 2126bfc04aecSBorislav Petkov } 2127bfc04aecSBorislav Petkov 2128bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome) 2129bfc04aecSBorislav Petkov { 2130bfc04aecSBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 2131ad6a32e9SBorislav Petkov int err_sym = -1; 2132bfc04aecSBorislav Petkov 2133a3b7db09SBorislav Petkov if (pvt->ecc_sym_sz == 8) 2134bfc04aecSBorislav Petkov err_sym = decode_syndrome(syndrome, x8_vectors, 2135ad6a32e9SBorislav Petkov ARRAY_SIZE(x8_vectors), 2136a3b7db09SBorislav Petkov pvt->ecc_sym_sz); 2137a3b7db09SBorislav Petkov else if (pvt->ecc_sym_sz == 4) 2138ad6a32e9SBorislav Petkov err_sym = decode_syndrome(syndrome, x4_vectors, 2139ad6a32e9SBorislav Petkov ARRAY_SIZE(x4_vectors), 2140a3b7db09SBorislav Petkov pvt->ecc_sym_sz); 2141ad6a32e9SBorislav Petkov else { 2142a3b7db09SBorislav Petkov amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz); 2143ad6a32e9SBorislav Petkov return err_sym; 2144bfc04aecSBorislav Petkov } 2145ad6a32e9SBorislav Petkov 2146a3b7db09SBorislav Petkov return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz); 214741c31044SBorislav Petkov } 2148bfc04aecSBorislav Petkov 214933ca0643SBorislav Petkov static void __log_bus_error(struct mem_ctl_info *mci, struct err_info *err, 215033ca0643SBorislav Petkov u8 ecc_type) 2151d27bf6faSDoug Thompson { 215233ca0643SBorislav Petkov enum hw_event_mc_err_type err_type; 215333ca0643SBorislav Petkov const char *string; 2154d27bf6faSDoug Thompson 215533ca0643SBorislav Petkov if (ecc_type == 2) 215633ca0643SBorislav Petkov err_type = HW_EVENT_ERR_CORRECTED; 215733ca0643SBorislav Petkov else if (ecc_type == 1) 215833ca0643SBorislav Petkov err_type = HW_EVENT_ERR_UNCORRECTED; 215933ca0643SBorislav Petkov else { 216033ca0643SBorislav Petkov WARN(1, "Something is rotten in the state of Denmark.\n"); 2161d27bf6faSDoug Thompson return; 2162d27bf6faSDoug Thompson } 2163d27bf6faSDoug Thompson 216433ca0643SBorislav Petkov switch (err->err_code) { 216533ca0643SBorislav Petkov case DECODE_OK: 216633ca0643SBorislav Petkov string = ""; 216733ca0643SBorislav Petkov break; 216833ca0643SBorislav Petkov case ERR_NODE: 216933ca0643SBorislav Petkov string = "Failed to map error addr to a node"; 217033ca0643SBorislav Petkov break; 217133ca0643SBorislav Petkov case ERR_CSROW: 217233ca0643SBorislav Petkov string = "Failed to map error addr to a csrow"; 217333ca0643SBorislav Petkov break; 217433ca0643SBorislav Petkov case ERR_CHANNEL: 217533ca0643SBorislav Petkov string = "unknown syndrome - possible error reporting race"; 217633ca0643SBorislav Petkov break; 217733ca0643SBorislav Petkov default: 217833ca0643SBorislav Petkov string = "WTF error"; 217933ca0643SBorislav Petkov break; 2180d27bf6faSDoug Thompson } 218133ca0643SBorislav Petkov 218233ca0643SBorislav Petkov edac_mc_handle_error(err_type, mci, 1, 218333ca0643SBorislav Petkov err->page, err->offset, err->syndrome, 218433ca0643SBorislav Petkov err->csrow, err->channel, -1, 218533ca0643SBorislav Petkov string, ""); 2186d27bf6faSDoug Thompson } 2187d27bf6faSDoug Thompson 2188df781d03SBorislav Petkov static inline void decode_bus_error(int node_id, struct mce *m) 2189d27bf6faSDoug Thompson { 21900c510cc8SDaniel J Blueman struct mem_ctl_info *mci; 21910c510cc8SDaniel J Blueman struct amd64_pvt *pvt; 2192f192c7b1SBorislav Petkov u8 ecc_type = (m->status >> 45) & 0x3; 219366fed2d4SBorislav Petkov u8 xec = XEC(m->status, 0x1f); 219466fed2d4SBorislav Petkov u16 ec = EC(m->status); 219533ca0643SBorislav Petkov u64 sys_addr; 219633ca0643SBorislav Petkov struct err_info err; 2197d27bf6faSDoug Thompson 21980c510cc8SDaniel J Blueman mci = edac_mc_find(node_id); 21990c510cc8SDaniel J Blueman if (!mci) 22000c510cc8SDaniel J Blueman return; 22010c510cc8SDaniel J Blueman 22020c510cc8SDaniel J Blueman pvt = mci->pvt_info; 22030c510cc8SDaniel J Blueman 220466fed2d4SBorislav Petkov /* Bail out early if this was an 'observed' error */ 22055980bb9cSBorislav Petkov if (PP(ec) == NBSL_PP_OBS) 2206b70ef010SBorislav Petkov return; 2207d27bf6faSDoug Thompson 2208ecaf5606SBorislav Petkov /* Do only ECC errors */ 2209ecaf5606SBorislav Petkov if (xec && xec != F10_NBSL_EXT_ERR_ECC) 2210d27bf6faSDoug Thompson return; 2211d27bf6faSDoug Thompson 221233ca0643SBorislav Petkov memset(&err, 0, sizeof(err)); 221333ca0643SBorislav Petkov 2214a4b4bedcSBorislav Petkov sys_addr = get_error_address(pvt, m); 221533ca0643SBorislav Petkov 2216ecaf5606SBorislav Petkov if (ecc_type == 2) 221733ca0643SBorislav Petkov err.syndrome = extract_syndrome(m->status); 221833ca0643SBorislav Petkov 221933ca0643SBorislav Petkov pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err); 222033ca0643SBorislav Petkov 222133ca0643SBorislav Petkov __log_bus_error(mci, &err, ecc_type); 2222d27bf6faSDoug Thompson } 2223d27bf6faSDoug Thompson 22240ec449eeSDoug Thompson /* 22253f37a36bSBorislav Petkov * Use pvt->F3 which contains the F3 CPU PCI device to get the related 22263f37a36bSBorislav Petkov * F1 (AddrMap) and F2 (Dct) devices. Return negative value on error. 22270ec449eeSDoug Thompson */ 22283f37a36bSBorislav Petkov static int reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 f1_id, u16 f2_id) 22290ec449eeSDoug Thompson { 22300ec449eeSDoug Thompson /* Reserve the ADDRESS MAP Device */ 22313f37a36bSBorislav Petkov pvt->F1 = pci_get_related_function(pvt->F3->vendor, f1_id, pvt->F3); 22328d5b5d9cSBorislav Petkov if (!pvt->F1) { 223324f9a7feSBorislav Petkov amd64_err("error address map device not found: " 22340ec449eeSDoug Thompson "vendor %x device 0x%x (broken BIOS?)\n", 2235bbd0c1f6SBorislav Petkov PCI_VENDOR_ID_AMD, f1_id); 2236bbd0c1f6SBorislav Petkov return -ENODEV; 22370ec449eeSDoug Thompson } 22380ec449eeSDoug Thompson 22393f37a36bSBorislav Petkov /* Reserve the DCT Device */ 22403f37a36bSBorislav Petkov pvt->F2 = pci_get_related_function(pvt->F3->vendor, f2_id, pvt->F3); 22413f37a36bSBorislav Petkov if (!pvt->F2) { 22428d5b5d9cSBorislav Petkov pci_dev_put(pvt->F1); 22438d5b5d9cSBorislav Petkov pvt->F1 = NULL; 22440ec449eeSDoug Thompson 22453f37a36bSBorislav Petkov amd64_err("error F2 device not found: " 22460ec449eeSDoug Thompson "vendor %x device 0x%x (broken BIOS?)\n", 22473f37a36bSBorislav Petkov PCI_VENDOR_ID_AMD, f2_id); 22488d5b5d9cSBorislav Petkov 2249bbd0c1f6SBorislav Petkov return -ENODEV; 22500ec449eeSDoug Thompson } 2251956b9ba1SJoe Perches edac_dbg(1, "F1: %s\n", pci_name(pvt->F1)); 2252956b9ba1SJoe Perches edac_dbg(1, "F2: %s\n", pci_name(pvt->F2)); 2253956b9ba1SJoe Perches edac_dbg(1, "F3: %s\n", pci_name(pvt->F3)); 22540ec449eeSDoug Thompson 22550ec449eeSDoug Thompson return 0; 22560ec449eeSDoug Thompson } 22570ec449eeSDoug Thompson 2258360b7f3cSBorislav Petkov static void free_mc_sibling_devs(struct amd64_pvt *pvt) 22590ec449eeSDoug Thompson { 22608d5b5d9cSBorislav Petkov pci_dev_put(pvt->F1); 22613f37a36bSBorislav Petkov pci_dev_put(pvt->F2); 22620ec449eeSDoug Thompson } 22630ec449eeSDoug Thompson 22640ec449eeSDoug Thompson /* 22650ec449eeSDoug Thompson * Retrieve the hardware registers of the memory controller (this includes the 22660ec449eeSDoug Thompson * 'Address Map' and 'Misc' device regs) 22670ec449eeSDoug Thompson */ 2268360b7f3cSBorislav Petkov static void read_mc_regs(struct amd64_pvt *pvt) 22690ec449eeSDoug Thompson { 2270a4b4bedcSBorislav Petkov unsigned range; 22710ec449eeSDoug Thompson u64 msr_val; 2272ad6a32e9SBorislav Petkov u32 tmp; 22730ec449eeSDoug Thompson 22740ec449eeSDoug Thompson /* 22750ec449eeSDoug Thompson * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since 22760ec449eeSDoug Thompson * those are Read-As-Zero 22770ec449eeSDoug Thompson */ 2278e97f8bb8SBorislav Petkov rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem); 2279956b9ba1SJoe Perches edac_dbg(0, " TOP_MEM: 0x%016llx\n", pvt->top_mem); 22800ec449eeSDoug Thompson 22810ec449eeSDoug Thompson /* check first whether TOP_MEM2 is enabled */ 22820ec449eeSDoug Thompson rdmsrl(MSR_K8_SYSCFG, msr_val); 22830ec449eeSDoug Thompson if (msr_val & (1U << 21)) { 2284e97f8bb8SBorislav Petkov rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2); 2285956b9ba1SJoe Perches edac_dbg(0, " TOP_MEM2: 0x%016llx\n", pvt->top_mem2); 22860ec449eeSDoug Thompson } else 2287956b9ba1SJoe Perches edac_dbg(0, " TOP_MEM2 disabled\n"); 22880ec449eeSDoug Thompson 22895980bb9cSBorislav Petkov amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap); 22900ec449eeSDoug Thompson 22915a5d2371SBorislav Petkov read_dram_ctl_register(pvt); 22920ec449eeSDoug Thompson 22937f19bf75SBorislav Petkov for (range = 0; range < DRAM_RANGES; range++) { 22947f19bf75SBorislav Petkov u8 rw; 22950ec449eeSDoug Thompson 22967f19bf75SBorislav Petkov /* read settings for this DRAM range */ 22977f19bf75SBorislav Petkov read_dram_base_limit_regs(pvt, range); 2298e97f8bb8SBorislav Petkov 22997f19bf75SBorislav Petkov rw = dram_rw(pvt, range); 23007f19bf75SBorislav Petkov if (!rw) 23017f19bf75SBorislav Petkov continue; 23027f19bf75SBorislav Petkov 2303956b9ba1SJoe Perches edac_dbg(1, " DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n", 23047f19bf75SBorislav Petkov range, 23057f19bf75SBorislav Petkov get_dram_base(pvt, range), 23067f19bf75SBorislav Petkov get_dram_limit(pvt, range)); 23077f19bf75SBorislav Petkov 2308956b9ba1SJoe Perches edac_dbg(1, " IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n", 23097f19bf75SBorislav Petkov dram_intlv_en(pvt, range) ? "Enabled" : "Disabled", 23107f19bf75SBorislav Petkov (rw & 0x1) ? "R" : "-", 23117f19bf75SBorislav Petkov (rw & 0x2) ? "W" : "-", 23127f19bf75SBorislav Petkov dram_intlv_sel(pvt, range), 23137f19bf75SBorislav Petkov dram_dst_node(pvt, range)); 23140ec449eeSDoug Thompson } 23150ec449eeSDoug Thompson 2316b2b0c605SBorislav Petkov read_dct_base_mask(pvt); 23170ec449eeSDoug Thompson 2318bc21fa57SBorislav Petkov amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar); 23197981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DBAM0, &pvt->dbam0); 23200ec449eeSDoug Thompson 23218d5b5d9cSBorislav Petkov amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare); 23220ec449eeSDoug Thompson 23237981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DCLR0, &pvt->dclr0); 23247981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 0, DCHR0, &pvt->dchr0); 23250ec449eeSDoug Thompson 232678da121eSBorislav Petkov if (!dct_ganging_enabled(pvt)) { 23277981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 1, DCLR0, &pvt->dclr1); 23287981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 1, DCHR0, &pvt->dchr1); 23290ec449eeSDoug Thompson } 2330b2b0c605SBorislav Petkov 2331a3b7db09SBorislav Petkov pvt->ecc_sym_sz = 4; 2332a597d2a5SAravind Gopalakrishnan determine_memory_type(pvt); 2333a597d2a5SAravind Gopalakrishnan edac_dbg(1, " DIMM type: %s\n", edac_mem_types[pvt->dram_type]); 2334a3b7db09SBorislav Petkov 2335a4b4bedcSBorislav Petkov if (pvt->fam >= 0x10) { 23368d5b5d9cSBorislav Petkov amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp); 23377981a28fSAravind Gopalakrishnan /* F16h has only DCT0, so no need to read dbam1 */ 2338a4b4bedcSBorislav Petkov if (pvt->fam != 0x16) 23397981a28fSAravind Gopalakrishnan amd64_read_dct_pci_cfg(pvt, 1, DBAM0, &pvt->dbam1); 2340a3b7db09SBorislav Petkov 2341a3b7db09SBorislav Petkov /* F10h, revD and later can do x8 ECC too */ 2342a4b4bedcSBorislav Petkov if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25)) 2343a3b7db09SBorislav Petkov pvt->ecc_sym_sz = 8; 2344525a1b20SBorislav Petkov } 2345b2b0c605SBorislav Petkov dump_misc_regs(pvt); 23460ec449eeSDoug Thompson } 23470ec449eeSDoug Thompson 23480ec449eeSDoug Thompson /* 23490ec449eeSDoug Thompson * NOTE: CPU Revision Dependent code 23500ec449eeSDoug Thompson * 23510ec449eeSDoug Thompson * Input: 235211c75eadSBorislav Petkov * @csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1) 23530ec449eeSDoug Thompson * k8 private pointer to --> 23540ec449eeSDoug Thompson * DRAM Bank Address mapping register 23550ec449eeSDoug Thompson * node_id 23560ec449eeSDoug Thompson * DCL register where dual_channel_active is 23570ec449eeSDoug Thompson * 23580ec449eeSDoug Thompson * The DBAM register consists of 4 sets of 4 bits each definitions: 23590ec449eeSDoug Thompson * 23600ec449eeSDoug Thompson * Bits: CSROWs 23610ec449eeSDoug Thompson * 0-3 CSROWs 0 and 1 23620ec449eeSDoug Thompson * 4-7 CSROWs 2 and 3 23630ec449eeSDoug Thompson * 8-11 CSROWs 4 and 5 23640ec449eeSDoug Thompson * 12-15 CSROWs 6 and 7 23650ec449eeSDoug Thompson * 23660ec449eeSDoug Thompson * Values range from: 0 to 15 23670ec449eeSDoug Thompson * The meaning of the values depends on CPU revision and dual-channel state, 23680ec449eeSDoug Thompson * see relevant BKDG more info. 23690ec449eeSDoug Thompson * 23700ec449eeSDoug Thompson * The memory controller provides for total of only 8 CSROWs in its current 23710ec449eeSDoug Thompson * architecture. Each "pair" of CSROWs normally represents just one DIMM in 23720ec449eeSDoug Thompson * single channel or two (2) DIMMs in dual channel mode. 23730ec449eeSDoug Thompson * 23740ec449eeSDoug Thompson * The following code logic collapses the various tables for CSROW based on CPU 23750ec449eeSDoug Thompson * revision. 23760ec449eeSDoug Thompson * 23770ec449eeSDoug Thompson * Returns: 23780ec449eeSDoug Thompson * The number of PAGE_SIZE pages on the specified CSROW number it 23790ec449eeSDoug Thompson * encompasses 23800ec449eeSDoug Thompson * 23810ec449eeSDoug Thompson */ 2382d1ea71cdSBorislav Petkov static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr) 23830ec449eeSDoug Thompson { 23841433eb99SBorislav Petkov u32 cs_mode, nr_pages; 2385f92cae45SAshish Shenoy u32 dbam = dct ? pvt->dbam1 : pvt->dbam0; 23860ec449eeSDoug Thompson 238710de6497SBorislav Petkov 23880ec449eeSDoug Thompson /* 23890ec449eeSDoug Thompson * The math on this doesn't look right on the surface because x/2*4 can 23900ec449eeSDoug Thompson * be simplified to x*2 but this expression makes use of the fact that 23910ec449eeSDoug Thompson * it is integral math where 1/2=0. This intermediate value becomes the 23920ec449eeSDoug Thompson * number of bits to shift the DBAM register to extract the proper CSROW 23930ec449eeSDoug Thompson * field. 23940ec449eeSDoug Thompson */ 23950a5dfc31SBorislav Petkov cs_mode = DBAM_DIMM(csrow_nr / 2, dbam); 23960ec449eeSDoug Thompson 2397a597d2a5SAravind Gopalakrishnan nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, (csrow_nr / 2)) 2398a597d2a5SAravind Gopalakrishnan << (20 - PAGE_SHIFT); 23990ec449eeSDoug Thompson 240010de6497SBorislav Petkov edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n", 240110de6497SBorislav Petkov csrow_nr, dct, cs_mode); 240210de6497SBorislav Petkov edac_dbg(0, "nr_pages/channel: %u\n", nr_pages); 24030ec449eeSDoug Thompson 24040ec449eeSDoug Thompson return nr_pages; 24050ec449eeSDoug Thompson } 24060ec449eeSDoug Thompson 24070ec449eeSDoug Thompson /* 24080ec449eeSDoug Thompson * Initialize the array of csrow attribute instances, based on the values 24090ec449eeSDoug Thompson * from pci config hardware registers. 24100ec449eeSDoug Thompson */ 2411360b7f3cSBorislav Petkov static int init_csrows(struct mem_ctl_info *mci) 24120ec449eeSDoug Thompson { 241310de6497SBorislav Petkov struct amd64_pvt *pvt = mci->pvt_info; 24140ec449eeSDoug Thompson struct csrow_info *csrow; 2415de3910ebSMauro Carvalho Chehab struct dimm_info *dimm; 2416084a4fccSMauro Carvalho Chehab enum edac_type edac_mode; 241710de6497SBorislav Petkov int i, j, empty = 1; 2418a895bf8bSMauro Carvalho Chehab int nr_pages = 0; 241910de6497SBorislav Petkov u32 val; 24200ec449eeSDoug Thompson 2421a97fa68eSBorislav Petkov amd64_read_pci_cfg(pvt->F3, NBCFG, &val); 24220ec449eeSDoug Thompson 24232299ef71SBorislav Petkov pvt->nbcfg = val; 24240ec449eeSDoug Thompson 2425956b9ba1SJoe Perches edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n", 24262299ef71SBorislav Petkov pvt->mc_node_id, val, 2427a97fa68eSBorislav Petkov !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE)); 24280ec449eeSDoug Thompson 242910de6497SBorislav Petkov /* 243010de6497SBorislav Petkov * We iterate over DCT0 here but we look at DCT1 in parallel, if needed. 243110de6497SBorislav Petkov */ 243211c75eadSBorislav Petkov for_each_chip_select(i, 0, pvt) { 243310de6497SBorislav Petkov bool row_dct0 = !!csrow_enabled(i, 0, pvt); 243410de6497SBorislav Petkov bool row_dct1 = false; 24350ec449eeSDoug Thompson 2436a4b4bedcSBorislav Petkov if (pvt->fam != 0xf) 243710de6497SBorislav Petkov row_dct1 = !!csrow_enabled(i, 1, pvt); 243810de6497SBorislav Petkov 243910de6497SBorislav Petkov if (!row_dct0 && !row_dct1) 24400ec449eeSDoug Thompson continue; 24410ec449eeSDoug Thompson 244210de6497SBorislav Petkov csrow = mci->csrows[i]; 24430ec449eeSDoug Thompson empty = 0; 244411c75eadSBorislav Petkov 244510de6497SBorislav Petkov edac_dbg(1, "MC node: %d, csrow: %d\n", 244610de6497SBorislav Petkov pvt->mc_node_id, i); 244710de6497SBorislav Petkov 24481eef1282SMauro Carvalho Chehab if (row_dct0) { 2449d1ea71cdSBorislav Petkov nr_pages = get_csrow_nr_pages(pvt, 0, i); 24501eef1282SMauro Carvalho Chehab csrow->channels[0]->dimm->nr_pages = nr_pages; 24511eef1282SMauro Carvalho Chehab } 245210de6497SBorislav Petkov 245310de6497SBorislav Petkov /* K8 has only one DCT */ 2454a4b4bedcSBorislav Petkov if (pvt->fam != 0xf && row_dct1) { 2455d1ea71cdSBorislav Petkov int row_dct1_pages = get_csrow_nr_pages(pvt, 1, i); 24561eef1282SMauro Carvalho Chehab 24571eef1282SMauro Carvalho Chehab csrow->channels[1]->dimm->nr_pages = row_dct1_pages; 24581eef1282SMauro Carvalho Chehab nr_pages += row_dct1_pages; 24591eef1282SMauro Carvalho Chehab } 24600ec449eeSDoug Thompson 246110de6497SBorislav Petkov edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages); 24620ec449eeSDoug Thompson 24630ec449eeSDoug Thompson /* 24640ec449eeSDoug Thompson * determine whether CHIPKILL or JUST ECC or NO ECC is operating 24650ec449eeSDoug Thompson */ 2466a97fa68eSBorislav Petkov if (pvt->nbcfg & NBCFG_ECC_ENABLE) 2467084a4fccSMauro Carvalho Chehab edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) ? 24680ec449eeSDoug Thompson EDAC_S4ECD4ED : EDAC_SECDED; 24690ec449eeSDoug Thompson else 2470084a4fccSMauro Carvalho Chehab edac_mode = EDAC_NONE; 2471084a4fccSMauro Carvalho Chehab 2472084a4fccSMauro Carvalho Chehab for (j = 0; j < pvt->channel_count; j++) { 2473de3910ebSMauro Carvalho Chehab dimm = csrow->channels[j]->dimm; 2474a597d2a5SAravind Gopalakrishnan dimm->mtype = pvt->dram_type; 2475de3910ebSMauro Carvalho Chehab dimm->edac_mode = edac_mode; 2476084a4fccSMauro Carvalho Chehab } 24770ec449eeSDoug Thompson } 24780ec449eeSDoug Thompson 24790ec449eeSDoug Thompson return empty; 24800ec449eeSDoug Thompson } 2481d27bf6faSDoug Thompson 248206724535SBorislav Petkov /* get all cores on this DCT */ 24838b84c8dfSDaniel J Blueman static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, u16 nid) 2484f9431992SDoug Thompson { 248506724535SBorislav Petkov int cpu; 2486f9431992SDoug Thompson 248706724535SBorislav Petkov for_each_online_cpu(cpu) 248806724535SBorislav Petkov if (amd_get_nb_id(cpu) == nid) 248906724535SBorislav Petkov cpumask_set_cpu(cpu, mask); 2490f9431992SDoug Thompson } 2491f9431992SDoug Thompson 2492f9431992SDoug Thompson /* check MCG_CTL on all the cpus on this node */ 2493d1ea71cdSBorislav Petkov static bool nb_mce_bank_enabled_on_node(u16 nid) 2494f9431992SDoug Thompson { 2495ba578cb3SRusty Russell cpumask_var_t mask; 249650542251SBorislav Petkov int cpu, nbe; 249706724535SBorislav Petkov bool ret = false; 2498f9431992SDoug Thompson 2499ba578cb3SRusty Russell if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) { 250024f9a7feSBorislav Petkov amd64_warn("%s: Error allocating mask\n", __func__); 250106724535SBorislav Petkov return false; 250206724535SBorislav Petkov } 250306724535SBorislav Petkov 2504ba578cb3SRusty Russell get_cpus_on_this_dct_cpumask(mask, nid); 250506724535SBorislav Petkov 2506ba578cb3SRusty Russell rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs); 2507ba578cb3SRusty Russell 2508ba578cb3SRusty Russell for_each_cpu(cpu, mask) { 250950542251SBorislav Petkov struct msr *reg = per_cpu_ptr(msrs, cpu); 25105980bb9cSBorislav Petkov nbe = reg->l & MSR_MCGCTL_NBE; 251106724535SBorislav Petkov 2512956b9ba1SJoe Perches edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n", 251350542251SBorislav Petkov cpu, reg->q, 251406724535SBorislav Petkov (nbe ? "enabled" : "disabled")); 251506724535SBorislav Petkov 251606724535SBorislav Petkov if (!nbe) 251706724535SBorislav Petkov goto out; 251806724535SBorislav Petkov } 251906724535SBorislav Petkov ret = true; 252006724535SBorislav Petkov 252106724535SBorislav Petkov out: 2522ba578cb3SRusty Russell free_cpumask_var(mask); 2523f9431992SDoug Thompson return ret; 2524f9431992SDoug Thompson } 2525f9431992SDoug Thompson 2526c7e5301aSDaniel J Blueman static int toggle_ecc_err_reporting(struct ecc_settings *s, u16 nid, bool on) 2527f6d6ae96SBorislav Petkov { 2528f6d6ae96SBorislav Petkov cpumask_var_t cmask; 252950542251SBorislav Petkov int cpu; 2530f6d6ae96SBorislav Petkov 2531f6d6ae96SBorislav Petkov if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) { 253224f9a7feSBorislav Petkov amd64_warn("%s: error allocating mask\n", __func__); 2533f6d6ae96SBorislav Petkov return false; 2534f6d6ae96SBorislav Petkov } 2535f6d6ae96SBorislav Petkov 2536ae7bb7c6SBorislav Petkov get_cpus_on_this_dct_cpumask(cmask, nid); 2537f6d6ae96SBorislav Petkov 2538f6d6ae96SBorislav Petkov rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); 2539f6d6ae96SBorislav Petkov 2540f6d6ae96SBorislav Petkov for_each_cpu(cpu, cmask) { 2541f6d6ae96SBorislav Petkov 254250542251SBorislav Petkov struct msr *reg = per_cpu_ptr(msrs, cpu); 254350542251SBorislav Petkov 2544f6d6ae96SBorislav Petkov if (on) { 25455980bb9cSBorislav Petkov if (reg->l & MSR_MCGCTL_NBE) 2546ae7bb7c6SBorislav Petkov s->flags.nb_mce_enable = 1; 2547f6d6ae96SBorislav Petkov 25485980bb9cSBorislav Petkov reg->l |= MSR_MCGCTL_NBE; 2549f6d6ae96SBorislav Petkov } else { 2550f6d6ae96SBorislav Petkov /* 2551d95cf4deSBorislav Petkov * Turn off NB MCE reporting only when it was off before 2552f6d6ae96SBorislav Petkov */ 2553ae7bb7c6SBorislav Petkov if (!s->flags.nb_mce_enable) 25545980bb9cSBorislav Petkov reg->l &= ~MSR_MCGCTL_NBE; 2555f6d6ae96SBorislav Petkov } 2556f6d6ae96SBorislav Petkov } 2557f6d6ae96SBorislav Petkov wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); 2558f6d6ae96SBorislav Petkov 2559f6d6ae96SBorislav Petkov free_cpumask_var(cmask); 2560f6d6ae96SBorislav Petkov 2561f6d6ae96SBorislav Petkov return 0; 2562f6d6ae96SBorislav Petkov } 2563f6d6ae96SBorislav Petkov 2564c7e5301aSDaniel J Blueman static bool enable_ecc_error_reporting(struct ecc_settings *s, u16 nid, 25652299ef71SBorislav Petkov struct pci_dev *F3) 2566f6d6ae96SBorislav Petkov { 25672299ef71SBorislav Petkov bool ret = true; 2568c9f4f26eSBorislav Petkov u32 value, mask = 0x3; /* UECC/CECC enable */ 2569f6d6ae96SBorislav Petkov 25702299ef71SBorislav Petkov if (toggle_ecc_err_reporting(s, nid, ON)) { 25712299ef71SBorislav Petkov amd64_warn("Error enabling ECC reporting over MCGCTL!\n"); 25722299ef71SBorislav Petkov return false; 25732299ef71SBorislav Petkov } 25742299ef71SBorislav Petkov 2575c9f4f26eSBorislav Petkov amd64_read_pci_cfg(F3, NBCTL, &value); 2576f6d6ae96SBorislav Petkov 2577ae7bb7c6SBorislav Petkov s->old_nbctl = value & mask; 2578ae7bb7c6SBorislav Petkov s->nbctl_valid = true; 2579f6d6ae96SBorislav Petkov 2580f6d6ae96SBorislav Petkov value |= mask; 2581c9f4f26eSBorislav Petkov amd64_write_pci_cfg(F3, NBCTL, value); 2582f6d6ae96SBorislav Petkov 2583a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 2584f6d6ae96SBorislav Petkov 2585956b9ba1SJoe Perches edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n", 2586a97fa68eSBorislav Petkov nid, value, !!(value & NBCFG_ECC_ENABLE)); 2587f6d6ae96SBorislav Petkov 2588a97fa68eSBorislav Petkov if (!(value & NBCFG_ECC_ENABLE)) { 258924f9a7feSBorislav Petkov amd64_warn("DRAM ECC disabled on this node, enabling...\n"); 2590f6d6ae96SBorislav Petkov 2591ae7bb7c6SBorislav Petkov s->flags.nb_ecc_prev = 0; 2592d95cf4deSBorislav Petkov 2593f6d6ae96SBorislav Petkov /* Attempt to turn on DRAM ECC Enable */ 2594a97fa68eSBorislav Petkov value |= NBCFG_ECC_ENABLE; 2595a97fa68eSBorislav Petkov amd64_write_pci_cfg(F3, NBCFG, value); 2596f6d6ae96SBorislav Petkov 2597a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 2598f6d6ae96SBorislav Petkov 2599a97fa68eSBorislav Petkov if (!(value & NBCFG_ECC_ENABLE)) { 260024f9a7feSBorislav Petkov amd64_warn("Hardware rejected DRAM ECC enable," 260124f9a7feSBorislav Petkov "check memory DIMM configuration.\n"); 26022299ef71SBorislav Petkov ret = false; 2603f6d6ae96SBorislav Petkov } else { 260424f9a7feSBorislav Petkov amd64_info("Hardware accepted DRAM ECC Enable\n"); 2605f6d6ae96SBorislav Petkov } 2606d95cf4deSBorislav Petkov } else { 2607ae7bb7c6SBorislav Petkov s->flags.nb_ecc_prev = 1; 2608f6d6ae96SBorislav Petkov } 2609d95cf4deSBorislav Petkov 2610956b9ba1SJoe Perches edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n", 2611a97fa68eSBorislav Petkov nid, value, !!(value & NBCFG_ECC_ENABLE)); 2612f6d6ae96SBorislav Petkov 26132299ef71SBorislav Petkov return ret; 2614f6d6ae96SBorislav Petkov } 2615f6d6ae96SBorislav Petkov 2616c7e5301aSDaniel J Blueman static void restore_ecc_error_reporting(struct ecc_settings *s, u16 nid, 2617360b7f3cSBorislav Petkov struct pci_dev *F3) 2618f6d6ae96SBorislav Petkov { 2619c9f4f26eSBorislav Petkov u32 value, mask = 0x3; /* UECC/CECC enable */ 2620c9f4f26eSBorislav Petkov 2621f6d6ae96SBorislav Petkov 2622ae7bb7c6SBorislav Petkov if (!s->nbctl_valid) 2623f6d6ae96SBorislav Petkov return; 2624f6d6ae96SBorislav Petkov 2625c9f4f26eSBorislav Petkov amd64_read_pci_cfg(F3, NBCTL, &value); 2626f6d6ae96SBorislav Petkov value &= ~mask; 2627ae7bb7c6SBorislav Petkov value |= s->old_nbctl; 2628f6d6ae96SBorislav Petkov 2629c9f4f26eSBorislav Petkov amd64_write_pci_cfg(F3, NBCTL, value); 2630f6d6ae96SBorislav Petkov 2631ae7bb7c6SBorislav Petkov /* restore previous BIOS DRAM ECC "off" setting we force-enabled */ 2632ae7bb7c6SBorislav Petkov if (!s->flags.nb_ecc_prev) { 2633a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 2634a97fa68eSBorislav Petkov value &= ~NBCFG_ECC_ENABLE; 2635a97fa68eSBorislav Petkov amd64_write_pci_cfg(F3, NBCFG, value); 2636d95cf4deSBorislav Petkov } 2637d95cf4deSBorislav Petkov 2638d95cf4deSBorislav Petkov /* restore the NB Enable MCGCTL bit */ 26392299ef71SBorislav Petkov if (toggle_ecc_err_reporting(s, nid, OFF)) 264024f9a7feSBorislav Petkov amd64_warn("Error restoring NB MCGCTL settings!\n"); 2641f6d6ae96SBorislav Petkov } 2642f6d6ae96SBorislav Petkov 2643f9431992SDoug Thompson /* 26442299ef71SBorislav Petkov * EDAC requires that the BIOS have ECC enabled before 26452299ef71SBorislav Petkov * taking over the processing of ECC errors. A command line 26462299ef71SBorislav Petkov * option allows to force-enable hardware ECC later in 26472299ef71SBorislav Petkov * enable_ecc_error_reporting(). 2648f9431992SDoug Thompson */ 2649cab4d277SBorislav Petkov static const char *ecc_msg = 2650cab4d277SBorislav Petkov "ECC disabled in the BIOS or no ECC capability, module will not load.\n" 2651cab4d277SBorislav Petkov " Either enable ECC checking or force module loading by setting " 2652cab4d277SBorislav Petkov "'ecc_enable_override'.\n" 2653cab4d277SBorislav Petkov " (Note that use of the override may cause unknown side effects.)\n"; 2654be3468e8SBorislav Petkov 2655c7e5301aSDaniel J Blueman static bool ecc_enabled(struct pci_dev *F3, u16 nid) 2656f9431992SDoug Thompson { 2657f9431992SDoug Thompson u32 value; 26582299ef71SBorislav Petkov u8 ecc_en = 0; 265906724535SBorislav Petkov bool nb_mce_en = false; 2660f9431992SDoug Thompson 2661a97fa68eSBorislav Petkov amd64_read_pci_cfg(F3, NBCFG, &value); 2662f9431992SDoug Thompson 2663a97fa68eSBorislav Petkov ecc_en = !!(value & NBCFG_ECC_ENABLE); 26642299ef71SBorislav Petkov amd64_info("DRAM ECC %s.\n", (ecc_en ? "enabled" : "disabled")); 2665be3468e8SBorislav Petkov 2666d1ea71cdSBorislav Petkov nb_mce_en = nb_mce_bank_enabled_on_node(nid); 266706724535SBorislav Petkov if (!nb_mce_en) 26682299ef71SBorislav Petkov amd64_notice("NB MCE bank disabled, set MSR " 26692299ef71SBorislav Petkov "0x%08x[4] on node %d to enable.\n", 26702299ef71SBorislav Petkov MSR_IA32_MCG_CTL, nid); 2671be3468e8SBorislav Petkov 26722299ef71SBorislav Petkov if (!ecc_en || !nb_mce_en) { 267324f9a7feSBorislav Petkov amd64_notice("%s", ecc_msg); 26742299ef71SBorislav Petkov return false; 2675be3468e8SBorislav Petkov } 26762299ef71SBorislav Petkov return true; 2677f9431992SDoug Thompson } 2678f9431992SDoug Thompson 2679df71a053SBorislav Petkov static void setup_mci_misc_attrs(struct mem_ctl_info *mci, 2680df71a053SBorislav Petkov struct amd64_family_type *fam) 26817d6034d3SDoug Thompson { 26827d6034d3SDoug Thompson struct amd64_pvt *pvt = mci->pvt_info; 26837d6034d3SDoug Thompson 26847d6034d3SDoug Thompson mci->mtype_cap = MEM_FLAG_DDR2 | MEM_FLAG_RDDR2; 26857d6034d3SDoug Thompson mci->edac_ctl_cap = EDAC_FLAG_NONE; 26867d6034d3SDoug Thompson 26875980bb9cSBorislav Petkov if (pvt->nbcap & NBCAP_SECDED) 26887d6034d3SDoug Thompson mci->edac_ctl_cap |= EDAC_FLAG_SECDED; 26897d6034d3SDoug Thompson 26905980bb9cSBorislav Petkov if (pvt->nbcap & NBCAP_CHIPKILL) 26917d6034d3SDoug Thompson mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED; 26927d6034d3SDoug Thompson 2693d1ea71cdSBorislav Petkov mci->edac_cap = determine_edac_cap(pvt); 26947d6034d3SDoug Thompson mci->mod_name = EDAC_MOD_STR; 26957d6034d3SDoug Thompson mci->mod_ver = EDAC_AMD64_VERSION; 2696df71a053SBorislav Petkov mci->ctl_name = fam->ctl_name; 26978d5b5d9cSBorislav Petkov mci->dev_name = pci_name(pvt->F2); 26987d6034d3SDoug Thompson mci->ctl_page_to_phys = NULL; 26997d6034d3SDoug Thompson 27007d6034d3SDoug Thompson /* memory scrubber interface */ 2701d1ea71cdSBorislav Petkov mci->set_sdram_scrub_rate = set_scrub_rate; 2702d1ea71cdSBorislav Petkov mci->get_sdram_scrub_rate = get_scrub_rate; 27037d6034d3SDoug Thompson } 27047d6034d3SDoug Thompson 27050092b20dSBorislav Petkov /* 27060092b20dSBorislav Petkov * returns a pointer to the family descriptor on success, NULL otherwise. 27070092b20dSBorislav Petkov */ 2708d1ea71cdSBorislav Petkov static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt) 2709395ae783SBorislav Petkov { 27100092b20dSBorislav Petkov struct amd64_family_type *fam_type = NULL; 27110092b20dSBorislav Petkov 271218b94f66SAravind Gopalakrishnan pvt->ext_model = boot_cpu_data.x86_model >> 4; 2713a4b4bedcSBorislav Petkov pvt->stepping = boot_cpu_data.x86_mask; 271418b94f66SAravind Gopalakrishnan pvt->model = boot_cpu_data.x86_model; 271518b94f66SAravind Gopalakrishnan pvt->fam = boot_cpu_data.x86; 271618b94f66SAravind Gopalakrishnan 271718b94f66SAravind Gopalakrishnan switch (pvt->fam) { 2718395ae783SBorislav Petkov case 0xf: 2719d1ea71cdSBorislav Petkov fam_type = &family_types[K8_CPUS]; 2720d1ea71cdSBorislav Petkov pvt->ops = &family_types[K8_CPUS].ops; 2721395ae783SBorislav Petkov break; 2722df71a053SBorislav Petkov 2723395ae783SBorislav Petkov case 0x10: 2724d1ea71cdSBorislav Petkov fam_type = &family_types[F10_CPUS]; 2725d1ea71cdSBorislav Petkov pvt->ops = &family_types[F10_CPUS].ops; 2726df71a053SBorislav Petkov break; 2727df71a053SBorislav Petkov 2728df71a053SBorislav Petkov case 0x15: 272918b94f66SAravind Gopalakrishnan if (pvt->model == 0x30) { 2730d1ea71cdSBorislav Petkov fam_type = &family_types[F15_M30H_CPUS]; 2731d1ea71cdSBorislav Petkov pvt->ops = &family_types[F15_M30H_CPUS].ops; 273218b94f66SAravind Gopalakrishnan break; 2733a597d2a5SAravind Gopalakrishnan } else if (pvt->model == 0x60) { 2734a597d2a5SAravind Gopalakrishnan fam_type = &family_types[F15_M60H_CPUS]; 2735a597d2a5SAravind Gopalakrishnan pvt->ops = &family_types[F15_M60H_CPUS].ops; 2736a597d2a5SAravind Gopalakrishnan break; 273718b94f66SAravind Gopalakrishnan } 273818b94f66SAravind Gopalakrishnan 2739d1ea71cdSBorislav Petkov fam_type = &family_types[F15_CPUS]; 2740d1ea71cdSBorislav Petkov pvt->ops = &family_types[F15_CPUS].ops; 2741395ae783SBorislav Petkov break; 2742395ae783SBorislav Petkov 274394c1acf2SAravind Gopalakrishnan case 0x16: 274485a8885bSAravind Gopalakrishnan if (pvt->model == 0x30) { 274585a8885bSAravind Gopalakrishnan fam_type = &family_types[F16_M30H_CPUS]; 274685a8885bSAravind Gopalakrishnan pvt->ops = &family_types[F16_M30H_CPUS].ops; 274785a8885bSAravind Gopalakrishnan break; 274885a8885bSAravind Gopalakrishnan } 2749d1ea71cdSBorislav Petkov fam_type = &family_types[F16_CPUS]; 2750d1ea71cdSBorislav Petkov pvt->ops = &family_types[F16_CPUS].ops; 275194c1acf2SAravind Gopalakrishnan break; 275294c1acf2SAravind Gopalakrishnan 2753395ae783SBorislav Petkov default: 275424f9a7feSBorislav Petkov amd64_err("Unsupported family!\n"); 27550092b20dSBorislav Petkov return NULL; 2756395ae783SBorislav Petkov } 27570092b20dSBorislav Petkov 2758df71a053SBorislav Petkov amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name, 275918b94f66SAravind Gopalakrishnan (pvt->fam == 0xf ? 27600092b20dSBorislav Petkov (pvt->ext_model >= K8_REV_F ? "revF or later " 27610092b20dSBorislav Petkov : "revE or earlier ") 276224f9a7feSBorislav Petkov : ""), pvt->mc_node_id); 27630092b20dSBorislav Petkov return fam_type; 2764395ae783SBorislav Petkov } 2765395ae783SBorislav Petkov 2766e339f1ecSTakashi Iwai static const struct attribute_group *amd64_edac_attr_groups[] = { 2767e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_DEBUG 2768e339f1ecSTakashi Iwai &amd64_edac_dbg_group, 2769e339f1ecSTakashi Iwai #endif 2770e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION 2771e339f1ecSTakashi Iwai &amd64_edac_inj_group, 2772e339f1ecSTakashi Iwai #endif 2773e339f1ecSTakashi Iwai NULL 2774e339f1ecSTakashi Iwai }; 2775e339f1ecSTakashi Iwai 27763f37a36bSBorislav Petkov static int init_one_instance(unsigned int nid) 27777d6034d3SDoug Thompson { 27783f37a36bSBorislav Petkov struct pci_dev *F3 = node_to_amd_nb(nid)->misc; 27790092b20dSBorislav Petkov struct amd64_family_type *fam_type = NULL; 2780360b7f3cSBorislav Petkov struct mem_ctl_info *mci = NULL; 2781ab5a503cSMauro Carvalho Chehab struct edac_mc_layer layers[2]; 27823f37a36bSBorislav Petkov struct amd64_pvt *pvt = NULL; 27837d6034d3SDoug Thompson int err = 0, ret; 27847d6034d3SDoug Thompson 27857d6034d3SDoug Thompson ret = -ENOMEM; 27867d6034d3SDoug Thompson pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL); 27877d6034d3SDoug Thompson if (!pvt) 2788360b7f3cSBorislav Petkov goto err_ret; 27897d6034d3SDoug Thompson 2790360b7f3cSBorislav Petkov pvt->mc_node_id = nid; 27913f37a36bSBorislav Petkov pvt->F3 = F3; 27927d6034d3SDoug Thompson 2793395ae783SBorislav Petkov ret = -EINVAL; 2794d1ea71cdSBorislav Petkov fam_type = per_family_init(pvt); 27950092b20dSBorislav Petkov if (!fam_type) 2796395ae783SBorislav Petkov goto err_free; 2797395ae783SBorislav Petkov 27987d6034d3SDoug Thompson ret = -ENODEV; 27993f37a36bSBorislav Petkov err = reserve_mc_sibling_devs(pvt, fam_type->f1_id, fam_type->f2_id); 28007d6034d3SDoug Thompson if (err) 28017d6034d3SDoug Thompson goto err_free; 28027d6034d3SDoug Thompson 2803360b7f3cSBorislav Petkov read_mc_regs(pvt); 28047d6034d3SDoug Thompson 28057d6034d3SDoug Thompson /* 28067d6034d3SDoug Thompson * We need to determine how many memory channels there are. Then use 28077d6034d3SDoug Thompson * that information for calculating the size of the dynamic instance 2808360b7f3cSBorislav Petkov * tables in the 'mci' structure. 28097d6034d3SDoug Thompson */ 2810360b7f3cSBorislav Petkov ret = -EINVAL; 28117d6034d3SDoug Thompson pvt->channel_count = pvt->ops->early_channel_count(pvt); 28127d6034d3SDoug Thompson if (pvt->channel_count < 0) 2813360b7f3cSBorislav Petkov goto err_siblings; 28147d6034d3SDoug Thompson 28157d6034d3SDoug Thompson ret = -ENOMEM; 2816ab5a503cSMauro Carvalho Chehab layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 2817ab5a503cSMauro Carvalho Chehab layers[0].size = pvt->csels[0].b_cnt; 2818ab5a503cSMauro Carvalho Chehab layers[0].is_virt_csrow = true; 2819ab5a503cSMauro Carvalho Chehab layers[1].type = EDAC_MC_LAYER_CHANNEL; 2820f0a56c48SBorislav Petkov 2821f0a56c48SBorislav Petkov /* 2822f0a56c48SBorislav Petkov * Always allocate two channels since we can have setups with DIMMs on 2823f0a56c48SBorislav Petkov * only one channel. Also, this simplifies handling later for the price 2824f0a56c48SBorislav Petkov * of a couple of KBs tops. 2825f0a56c48SBorislav Petkov */ 2826f0a56c48SBorislav Petkov layers[1].size = 2; 2827ab5a503cSMauro Carvalho Chehab layers[1].is_virt_csrow = false; 2828f0a56c48SBorislav Petkov 2829ca0907b9SMauro Carvalho Chehab mci = edac_mc_alloc(nid, ARRAY_SIZE(layers), layers, 0); 28307d6034d3SDoug Thompson if (!mci) 2831360b7f3cSBorislav Petkov goto err_siblings; 28327d6034d3SDoug Thompson 28337d6034d3SDoug Thompson mci->pvt_info = pvt; 28343f37a36bSBorislav Petkov mci->pdev = &pvt->F3->dev; 28357d6034d3SDoug Thompson 2836df71a053SBorislav Petkov setup_mci_misc_attrs(mci, fam_type); 2837360b7f3cSBorislav Petkov 2838360b7f3cSBorislav Petkov if (init_csrows(mci)) 28397d6034d3SDoug Thompson mci->edac_cap = EDAC_FLAG_NONE; 28407d6034d3SDoug Thompson 28417d6034d3SDoug Thompson ret = -ENODEV; 2842e339f1ecSTakashi Iwai if (edac_mc_add_mc_with_groups(mci, amd64_edac_attr_groups)) { 2843956b9ba1SJoe Perches edac_dbg(1, "failed edac_mc_add_mc()\n"); 28447d6034d3SDoug Thompson goto err_add_mc; 28457d6034d3SDoug Thompson } 28467d6034d3SDoug Thompson 2847549d042dSBorislav Petkov /* register stuff with EDAC MCE */ 2848549d042dSBorislav Petkov if (report_gart_errors) 2849549d042dSBorislav Petkov amd_report_gart_errors(true); 2850549d042dSBorislav Petkov 2851df781d03SBorislav Petkov amd_register_ecc_decoder(decode_bus_error); 2852549d042dSBorislav Petkov 28537d6034d3SDoug Thompson return 0; 28547d6034d3SDoug Thompson 28557d6034d3SDoug Thompson err_add_mc: 28567d6034d3SDoug Thompson edac_mc_free(mci); 28577d6034d3SDoug Thompson 2858360b7f3cSBorislav Petkov err_siblings: 2859360b7f3cSBorislav Petkov free_mc_sibling_devs(pvt); 28607d6034d3SDoug Thompson 2861360b7f3cSBorislav Petkov err_free: 2862360b7f3cSBorislav Petkov kfree(pvt); 28637d6034d3SDoug Thompson 2864360b7f3cSBorislav Petkov err_ret: 28657d6034d3SDoug Thompson return ret; 28667d6034d3SDoug Thompson } 28677d6034d3SDoug Thompson 28683f37a36bSBorislav Petkov static int probe_one_instance(unsigned int nid) 28697d6034d3SDoug Thompson { 28702299ef71SBorislav Petkov struct pci_dev *F3 = node_to_amd_nb(nid)->misc; 2871ae7bb7c6SBorislav Petkov struct ecc_settings *s; 28723f37a36bSBorislav Petkov int ret; 2873b8cfa02fSBorislav Petkov 2874ae7bb7c6SBorislav Petkov ret = -ENOMEM; 2875ae7bb7c6SBorislav Petkov s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL); 2876ae7bb7c6SBorislav Petkov if (!s) 28772299ef71SBorislav Petkov goto err_out; 2878ae7bb7c6SBorislav Petkov 2879ae7bb7c6SBorislav Petkov ecc_stngs[nid] = s; 2880ae7bb7c6SBorislav Petkov 28812299ef71SBorislav Petkov if (!ecc_enabled(F3, nid)) { 28822299ef71SBorislav Petkov ret = -ENODEV; 28832299ef71SBorislav Petkov 28842299ef71SBorislav Petkov if (!ecc_enable_override) 28852299ef71SBorislav Petkov goto err_enable; 28862299ef71SBorislav Petkov 28872299ef71SBorislav Petkov amd64_warn("Forcing ECC on!\n"); 28882299ef71SBorislav Petkov 28892299ef71SBorislav Petkov if (!enable_ecc_error_reporting(s, nid, F3)) 28902299ef71SBorislav Petkov goto err_enable; 28912299ef71SBorislav Petkov } 28922299ef71SBorislav Petkov 28933f37a36bSBorislav Petkov ret = init_one_instance(nid); 2894360b7f3cSBorislav Petkov if (ret < 0) { 2895ae7bb7c6SBorislav Petkov amd64_err("Error probing instance: %d\n", nid); 2896360b7f3cSBorislav Petkov restore_ecc_error_reporting(s, nid, F3); 2897360b7f3cSBorislav Petkov } 28987d6034d3SDoug Thompson 28997d6034d3SDoug Thompson return ret; 29002299ef71SBorislav Petkov 29012299ef71SBorislav Petkov err_enable: 29022299ef71SBorislav Petkov kfree(s); 29032299ef71SBorislav Petkov ecc_stngs[nid] = NULL; 29042299ef71SBorislav Petkov 29052299ef71SBorislav Petkov err_out: 29062299ef71SBorislav Petkov return ret; 29077d6034d3SDoug Thompson } 29087d6034d3SDoug Thompson 29093f37a36bSBorislav Petkov static void remove_one_instance(unsigned int nid) 29107d6034d3SDoug Thompson { 2911360b7f3cSBorislav Petkov struct pci_dev *F3 = node_to_amd_nb(nid)->misc; 2912360b7f3cSBorislav Petkov struct ecc_settings *s = ecc_stngs[nid]; 29133f37a36bSBorislav Petkov struct mem_ctl_info *mci; 29143f37a36bSBorislav Petkov struct amd64_pvt *pvt; 29157d6034d3SDoug Thompson 29163f37a36bSBorislav Petkov mci = find_mci_by_dev(&F3->dev); 2917a4b4bedcSBorislav Petkov WARN_ON(!mci); 2918a4b4bedcSBorislav Petkov 29197d6034d3SDoug Thompson /* Remove from EDAC CORE tracking list */ 29203f37a36bSBorislav Petkov mci = edac_mc_del_mc(&F3->dev); 29217d6034d3SDoug Thompson if (!mci) 29227d6034d3SDoug Thompson return; 29237d6034d3SDoug Thompson 29247d6034d3SDoug Thompson pvt = mci->pvt_info; 29257d6034d3SDoug Thompson 2926360b7f3cSBorislav Petkov restore_ecc_error_reporting(s, nid, F3); 29277d6034d3SDoug Thompson 2928360b7f3cSBorislav Petkov free_mc_sibling_devs(pvt); 29297d6034d3SDoug Thompson 2930549d042dSBorislav Petkov /* unregister from EDAC MCE */ 2931549d042dSBorislav Petkov amd_report_gart_errors(false); 2932df781d03SBorislav Petkov amd_unregister_ecc_decoder(decode_bus_error); 2933549d042dSBorislav Petkov 2934360b7f3cSBorislav Petkov kfree(ecc_stngs[nid]); 2935360b7f3cSBorislav Petkov ecc_stngs[nid] = NULL; 2936ae7bb7c6SBorislav Petkov 29377d6034d3SDoug Thompson /* Free the EDAC CORE resources */ 29388f68ed97SBorislav Petkov mci->pvt_info = NULL; 29398f68ed97SBorislav Petkov 29408f68ed97SBorislav Petkov kfree(pvt); 29417d6034d3SDoug Thompson edac_mc_free(mci); 29427d6034d3SDoug Thompson } 29437d6034d3SDoug Thompson 2944360b7f3cSBorislav Petkov static void setup_pci_device(void) 29457d6034d3SDoug Thompson { 29467d6034d3SDoug Thompson struct mem_ctl_info *mci; 29477d6034d3SDoug Thompson struct amd64_pvt *pvt; 29487d6034d3SDoug Thompson 2949d1ea71cdSBorislav Petkov if (pci_ctl) 29507d6034d3SDoug Thompson return; 29517d6034d3SDoug Thompson 29522ec591acSBorislav Petkov mci = edac_mc_find(0); 2953d1ea71cdSBorislav Petkov if (!mci) 2954d1ea71cdSBorislav Petkov return; 29557d6034d3SDoug Thompson 29567d6034d3SDoug Thompson pvt = mci->pvt_info; 2957d1ea71cdSBorislav Petkov pci_ctl = edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR); 2958d1ea71cdSBorislav Petkov if (!pci_ctl) { 2959d1ea71cdSBorislav Petkov pr_warn("%s(): Unable to create PCI control\n", __func__); 2960d1ea71cdSBorislav Petkov pr_warn("%s(): PCI error report via EDAC not set\n", __func__); 29617d6034d3SDoug Thompson } 29627d6034d3SDoug Thompson } 29637d6034d3SDoug Thompson 29647d6034d3SDoug Thompson static int __init amd64_edac_init(void) 29657d6034d3SDoug Thompson { 2966360b7f3cSBorislav Petkov int err = -ENODEV; 29673f37a36bSBorislav Petkov int i; 29687d6034d3SDoug Thompson 29699653a5c7SHans Rosenfeld if (amd_cache_northbridges() < 0) 297056b34b91SBorislav Petkov goto err_ret; 29717d6034d3SDoug Thompson 29726ba92feaSBorislav Petkov opstate_init(); 29736ba92feaSBorislav Petkov 2974cc4d8860SBorislav Petkov err = -ENOMEM; 2975ae7bb7c6SBorislav Petkov ecc_stngs = kzalloc(amd_nb_num() * sizeof(ecc_stngs[0]), GFP_KERNEL); 29762ec591acSBorislav Petkov if (!ecc_stngs) 2977a9f0fbe2SBorislav Petkov goto err_free; 2978cc4d8860SBorislav Petkov 297950542251SBorislav Petkov msrs = msrs_alloc(); 298056b34b91SBorislav Petkov if (!msrs) 2981360b7f3cSBorislav Petkov goto err_free; 298250542251SBorislav Petkov 29833f37a36bSBorislav Petkov for (i = 0; i < amd_nb_num(); i++) 29843f37a36bSBorislav Petkov if (probe_one_instance(i)) { 29853f37a36bSBorislav Petkov /* unwind properly */ 29863f37a36bSBorislav Petkov while (--i >= 0) 29873f37a36bSBorislav Petkov remove_one_instance(i); 29887d6034d3SDoug Thompson 29893f37a36bSBorislav Petkov goto err_pci; 29903f37a36bSBorislav Petkov } 29917d6034d3SDoug Thompson 2992360b7f3cSBorislav Petkov setup_pci_device(); 2993f5b10c45STomasz Pala 2994f5b10c45STomasz Pala #ifdef CONFIG_X86_32 2995f5b10c45STomasz Pala amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR); 2996f5b10c45STomasz Pala #endif 2997f5b10c45STomasz Pala 2998de0336b3SBorislav Petkov printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION); 2999de0336b3SBorislav Petkov 30007d6034d3SDoug Thompson return 0; 30017d6034d3SDoug Thompson 300256b34b91SBorislav Petkov err_pci: 300356b34b91SBorislav Petkov msrs_free(msrs); 300456b34b91SBorislav Petkov msrs = NULL; 3005cc4d8860SBorislav Petkov 3006360b7f3cSBorislav Petkov err_free: 3007360b7f3cSBorislav Petkov kfree(ecc_stngs); 3008360b7f3cSBorislav Petkov ecc_stngs = NULL; 3009360b7f3cSBorislav Petkov 301056b34b91SBorislav Petkov err_ret: 30117d6034d3SDoug Thompson return err; 30127d6034d3SDoug Thompson } 30137d6034d3SDoug Thompson 30147d6034d3SDoug Thompson static void __exit amd64_edac_exit(void) 30157d6034d3SDoug Thompson { 30163f37a36bSBorislav Petkov int i; 30173f37a36bSBorislav Petkov 3018d1ea71cdSBorislav Petkov if (pci_ctl) 3019d1ea71cdSBorislav Petkov edac_pci_release_generic_ctl(pci_ctl); 30207d6034d3SDoug Thompson 30213f37a36bSBorislav Petkov for (i = 0; i < amd_nb_num(); i++) 30223f37a36bSBorislav Petkov remove_one_instance(i); 302350542251SBorislav Petkov 3024ae7bb7c6SBorislav Petkov kfree(ecc_stngs); 3025ae7bb7c6SBorislav Petkov ecc_stngs = NULL; 3026ae7bb7c6SBorislav Petkov 302750542251SBorislav Petkov msrs_free(msrs); 302850542251SBorislav Petkov msrs = NULL; 30297d6034d3SDoug Thompson } 30307d6034d3SDoug Thompson 30317d6034d3SDoug Thompson module_init(amd64_edac_init); 30327d6034d3SDoug Thompson module_exit(amd64_edac_exit); 30337d6034d3SDoug Thompson 30347d6034d3SDoug Thompson MODULE_LICENSE("GPL"); 30357d6034d3SDoug Thompson MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, " 30367d6034d3SDoug Thompson "Dave Peterson, Thayne Harbaugh"); 30377d6034d3SDoug Thompson MODULE_DESCRIPTION("MC support for AMD64 memory controllers - " 30387d6034d3SDoug Thompson EDAC_AMD64_VERSION); 30397d6034d3SDoug Thompson 30407d6034d3SDoug Thompson module_param(edac_op_state, int, 0444); 30417d6034d3SDoug Thompson MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); 3042