xref: /openbmc/linux/drivers/edac/amd64_edac.c (revision 2a28ceef)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22bc65418SDoug Thompson #include "amd64_edac.h"
323ac4ae8SAndreas Herrmann #include <asm/amd_nb.h>
42bc65418SDoug Thompson 
5d1ea71cdSBorislav Petkov static struct edac_pci_ctl_info *pci_ctl;
62bc65418SDoug Thompson 
72bc65418SDoug Thompson /*
82bc65418SDoug Thompson  * Set by command line parameter. If BIOS has enabled the ECC, this override is
92bc65418SDoug Thompson  * cleared to prevent re-enabling the hardware by this driver.
102bc65418SDoug Thompson  */
112bc65418SDoug Thompson static int ecc_enable_override;
122bc65418SDoug Thompson module_param(ecc_enable_override, int, 0644);
132bc65418SDoug Thompson 
14a29d8b8eSTejun Heo static struct msr __percpu *msrs;
1550542251SBorislav Petkov 
1638ddd4d1SYazen Ghannam static struct amd64_family_type *fam_type;
1738ddd4d1SYazen Ghannam 
182ec591acSBorislav Petkov /* Per-node stuff */
19ae7bb7c6SBorislav Petkov static struct ecc_settings **ecc_stngs;
202bc65418SDoug Thompson 
21706657b1SBorislav Petkov /* Device for the PCI component */
22706657b1SBorislav Petkov static struct device *pci_ctl_dev;
23706657b1SBorislav Petkov 
242bc65418SDoug Thompson /*
25b70ef010SBorislav Petkov  * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing
26b70ef010SBorislav Petkov  * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching-
27b70ef010SBorislav Petkov  * or higher value'.
28b70ef010SBorislav Petkov  *
29b70ef010SBorislav Petkov  *FIXME: Produce a better mapping/linearisation.
30b70ef010SBorislav Petkov  */
31c7e5301aSDaniel J Blueman static const struct scrubrate {
3239094443SBorislav Petkov        u32 scrubval;           /* bit pattern for scrub rate */
3339094443SBorislav Petkov        u32 bandwidth;          /* bandwidth consumed (bytes/sec) */
3439094443SBorislav Petkov } scrubrates[] = {
35b70ef010SBorislav Petkov 	{ 0x01, 1600000000UL},
36b70ef010SBorislav Petkov 	{ 0x02, 800000000UL},
37b70ef010SBorislav Petkov 	{ 0x03, 400000000UL},
38b70ef010SBorislav Petkov 	{ 0x04, 200000000UL},
39b70ef010SBorislav Petkov 	{ 0x05, 100000000UL},
40b70ef010SBorislav Petkov 	{ 0x06, 50000000UL},
41b70ef010SBorislav Petkov 	{ 0x07, 25000000UL},
42b70ef010SBorislav Petkov 	{ 0x08, 12284069UL},
43b70ef010SBorislav Petkov 	{ 0x09, 6274509UL},
44b70ef010SBorislav Petkov 	{ 0x0A, 3121951UL},
45b70ef010SBorislav Petkov 	{ 0x0B, 1560975UL},
46b70ef010SBorislav Petkov 	{ 0x0C, 781440UL},
47b70ef010SBorislav Petkov 	{ 0x0D, 390720UL},
48b70ef010SBorislav Petkov 	{ 0x0E, 195300UL},
49b70ef010SBorislav Petkov 	{ 0x0F, 97650UL},
50b70ef010SBorislav Petkov 	{ 0x10, 48854UL},
51b70ef010SBorislav Petkov 	{ 0x11, 24427UL},
52b70ef010SBorislav Petkov 	{ 0x12, 12213UL},
53b70ef010SBorislav Petkov 	{ 0x13, 6101UL},
54b70ef010SBorislav Petkov 	{ 0x14, 3051UL},
55b70ef010SBorislav Petkov 	{ 0x15, 1523UL},
56b70ef010SBorislav Petkov 	{ 0x16, 761UL},
57b70ef010SBorislav Petkov 	{ 0x00, 0UL},        /* scrubbing off */
58b70ef010SBorislav Petkov };
59b70ef010SBorislav Petkov 
6066fed2d4SBorislav Petkov int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset,
61b2b0c605SBorislav Petkov 			       u32 *val, const char *func)
62b2b0c605SBorislav Petkov {
63b2b0c605SBorislav Petkov 	int err = 0;
64b2b0c605SBorislav Petkov 
65b2b0c605SBorislav Petkov 	err = pci_read_config_dword(pdev, offset, val);
66b2b0c605SBorislav Petkov 	if (err)
67b2b0c605SBorislav Petkov 		amd64_warn("%s: error reading F%dx%03x.\n",
68b2b0c605SBorislav Petkov 			   func, PCI_FUNC(pdev->devfn), offset);
69b2b0c605SBorislav Petkov 
70b2b0c605SBorislav Petkov 	return err;
71b2b0c605SBorislav Petkov }
72b2b0c605SBorislav Petkov 
73b2b0c605SBorislav Petkov int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset,
74b2b0c605SBorislav Petkov 				u32 val, const char *func)
75b2b0c605SBorislav Petkov {
76b2b0c605SBorislav Petkov 	int err = 0;
77b2b0c605SBorislav Petkov 
78b2b0c605SBorislav Petkov 	err = pci_write_config_dword(pdev, offset, val);
79b2b0c605SBorislav Petkov 	if (err)
80b2b0c605SBorislav Petkov 		amd64_warn("%s: error writing to F%dx%03x.\n",
81b2b0c605SBorislav Petkov 			   func, PCI_FUNC(pdev->devfn), offset);
82b2b0c605SBorislav Petkov 
83b2b0c605SBorislav Petkov 	return err;
84b2b0c605SBorislav Petkov }
85b2b0c605SBorislav Petkov 
86b2b0c605SBorislav Petkov /*
8773ba8593SBorislav Petkov  * Select DCT to which PCI cfg accesses are routed
8873ba8593SBorislav Petkov  */
8973ba8593SBorislav Petkov static void f15h_select_dct(struct amd64_pvt *pvt, u8 dct)
9073ba8593SBorislav Petkov {
9173ba8593SBorislav Petkov 	u32 reg = 0;
9273ba8593SBorislav Petkov 
9373ba8593SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, &reg);
947981a28fSAravind Gopalakrishnan 	reg &= (pvt->model == 0x30) ? ~3 : ~1;
9573ba8593SBorislav Petkov 	reg |= dct;
9673ba8593SBorislav Petkov 	amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg);
9773ba8593SBorislav Petkov }
9873ba8593SBorislav Petkov 
997981a28fSAravind Gopalakrishnan /*
1007981a28fSAravind Gopalakrishnan  *
1017981a28fSAravind Gopalakrishnan  * Depending on the family, F2 DCT reads need special handling:
1027981a28fSAravind Gopalakrishnan  *
1037981a28fSAravind Gopalakrishnan  * K8: has a single DCT only and no address offsets >= 0x100
1047981a28fSAravind Gopalakrishnan  *
1057981a28fSAravind Gopalakrishnan  * F10h: each DCT has its own set of regs
1067981a28fSAravind Gopalakrishnan  *	DCT0 -> F2x040..
1077981a28fSAravind Gopalakrishnan  *	DCT1 -> F2x140..
1087981a28fSAravind Gopalakrishnan  *
1097981a28fSAravind Gopalakrishnan  * F16h: has only 1 DCT
1107981a28fSAravind Gopalakrishnan  *
1117981a28fSAravind Gopalakrishnan  * F15h: we select which DCT we access using F1x10C[DctCfgSel]
1127981a28fSAravind Gopalakrishnan  */
1137981a28fSAravind Gopalakrishnan static inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct,
1147981a28fSAravind Gopalakrishnan 					 int offset, u32 *val)
115b2b0c605SBorislav Petkov {
1167981a28fSAravind Gopalakrishnan 	switch (pvt->fam) {
1177981a28fSAravind Gopalakrishnan 	case 0xf:
1187981a28fSAravind Gopalakrishnan 		if (dct || offset >= 0x100)
1197981a28fSAravind Gopalakrishnan 			return -EINVAL;
1207981a28fSAravind Gopalakrishnan 		break;
121b2b0c605SBorislav Petkov 
1227981a28fSAravind Gopalakrishnan 	case 0x10:
1237981a28fSAravind Gopalakrishnan 		if (dct) {
1247981a28fSAravind Gopalakrishnan 			/*
1257981a28fSAravind Gopalakrishnan 			 * Note: If ganging is enabled, barring the regs
1267981a28fSAravind Gopalakrishnan 			 * F2x[1,0]98 and F2x[1,0]9C; reads reads to F2x1xx
1277981a28fSAravind Gopalakrishnan 			 * return 0. (cf. Section 2.8.1 F10h BKDG)
1287981a28fSAravind Gopalakrishnan 			 */
1297981a28fSAravind Gopalakrishnan 			if (dct_ganging_enabled(pvt))
1307981a28fSAravind Gopalakrishnan 				return 0;
1317981a28fSAravind Gopalakrishnan 
1327981a28fSAravind Gopalakrishnan 			offset += 0x100;
133b2b0c605SBorislav Petkov 		}
1347981a28fSAravind Gopalakrishnan 		break;
135b2b0c605SBorislav Petkov 
1367981a28fSAravind Gopalakrishnan 	case 0x15:
1377981a28fSAravind Gopalakrishnan 		/*
1387981a28fSAravind Gopalakrishnan 		 * F15h: F2x1xx addresses do not map explicitly to DCT1.
1397981a28fSAravind Gopalakrishnan 		 * We should select which DCT we access using F1x10C[DctCfgSel]
1407981a28fSAravind Gopalakrishnan 		 */
1417981a28fSAravind Gopalakrishnan 		dct = (dct && pvt->model == 0x30) ? 3 : dct;
14273ba8593SBorislav Petkov 		f15h_select_dct(pvt, dct);
1437981a28fSAravind Gopalakrishnan 		break;
144b2b0c605SBorislav Petkov 
1457981a28fSAravind Gopalakrishnan 	case 0x16:
1467981a28fSAravind Gopalakrishnan 		if (dct)
1477981a28fSAravind Gopalakrishnan 			return -EINVAL;
1487981a28fSAravind Gopalakrishnan 		break;
1497981a28fSAravind Gopalakrishnan 
1507981a28fSAravind Gopalakrishnan 	default:
1517981a28fSAravind Gopalakrishnan 		break;
1527981a28fSAravind Gopalakrishnan 	}
1537981a28fSAravind Gopalakrishnan 	return amd64_read_pci_cfg(pvt->F2, offset, val);
154b2b0c605SBorislav Petkov }
155b2b0c605SBorislav Petkov 
156b70ef010SBorislav Petkov /*
1572bc65418SDoug Thompson  * Memory scrubber control interface. For K8, memory scrubbing is handled by
1582bc65418SDoug Thompson  * hardware and can involve L2 cache, dcache as well as the main memory. With
1592bc65418SDoug Thompson  * F10, this is extended to L3 cache scrubbing on CPU models sporting that
1602bc65418SDoug Thompson  * functionality.
1612bc65418SDoug Thompson  *
1622bc65418SDoug Thompson  * This causes the "units" for the scrubbing speed to vary from 64 byte blocks
1632bc65418SDoug Thompson  * (dram) over to cache lines. This is nasty, so we will use bandwidth in
1642bc65418SDoug Thompson  * bytes/sec for the setting.
1652bc65418SDoug Thompson  *
1662bc65418SDoug Thompson  * Currently, we only do dram scrubbing. If the scrubbing is done in software on
1672bc65418SDoug Thompson  * other archs, we might not have access to the caches directly.
1682bc65418SDoug Thompson  */
1692bc65418SDoug Thompson 
1708051c0afSYazen Ghannam static inline void __f17h_set_scrubval(struct amd64_pvt *pvt, u32 scrubval)
1718051c0afSYazen Ghannam {
1722bc65418SDoug Thompson 	/*
1738051c0afSYazen Ghannam 	 * Fam17h supports scrub values between 0x5 and 0x14. Also, the values
1748051c0afSYazen Ghannam 	 * are shifted down by 0x5, so scrubval 0x5 is written to the register
1758051c0afSYazen Ghannam 	 * as 0x0, scrubval 0x6 as 0x1, etc.
1768051c0afSYazen Ghannam 	 */
1778051c0afSYazen Ghannam 	if (scrubval >= 0x5 && scrubval <= 0x14) {
1788051c0afSYazen Ghannam 		scrubval -= 0x5;
1798051c0afSYazen Ghannam 		pci_write_bits32(pvt->F6, F17H_SCR_LIMIT_ADDR, scrubval, 0xF);
1808051c0afSYazen Ghannam 		pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 1, 0x1);
1818051c0afSYazen Ghannam 	} else {
1828051c0afSYazen Ghannam 		pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 0, 0x1);
1838051c0afSYazen Ghannam 	}
1848051c0afSYazen Ghannam }
1858051c0afSYazen Ghannam /*
1868051c0afSYazen Ghannam  * Scan the scrub rate mapping table for a close or matching bandwidth value to
1872bc65418SDoug Thompson  * issue. If requested is too big, then use last maximum value found.
1882bc65418SDoug Thompson  */
189da92110dSAravind Gopalakrishnan static int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate)
1902bc65418SDoug Thompson {
1912bc65418SDoug Thompson 	u32 scrubval;
1922bc65418SDoug Thompson 	int i;
1932bc65418SDoug Thompson 
1942bc65418SDoug Thompson 	/*
1952bc65418SDoug Thompson 	 * map the configured rate (new_bw) to a value specific to the AMD64
1962bc65418SDoug Thompson 	 * memory controller and apply to register. Search for the first
1972bc65418SDoug Thompson 	 * bandwidth entry that is greater or equal than the setting requested
1982bc65418SDoug Thompson 	 * and program that. If at last entry, turn off DRAM scrubbing.
199168bfeefSAndrew Morton 	 *
200168bfeefSAndrew Morton 	 * If no suitable bandwidth is found, turn off DRAM scrubbing entirely
201168bfeefSAndrew Morton 	 * by falling back to the last element in scrubrates[].
2022bc65418SDoug Thompson 	 */
203168bfeefSAndrew Morton 	for (i = 0; i < ARRAY_SIZE(scrubrates) - 1; i++) {
2042bc65418SDoug Thompson 		/*
2052bc65418SDoug Thompson 		 * skip scrub rates which aren't recommended
2062bc65418SDoug Thompson 		 * (see F10 BKDG, F3x58)
2072bc65418SDoug Thompson 		 */
208395ae783SBorislav Petkov 		if (scrubrates[i].scrubval < min_rate)
2092bc65418SDoug Thompson 			continue;
2102bc65418SDoug Thompson 
2112bc65418SDoug Thompson 		if (scrubrates[i].bandwidth <= new_bw)
2122bc65418SDoug Thompson 			break;
2132bc65418SDoug Thompson 	}
2142bc65418SDoug Thompson 
2152bc65418SDoug Thompson 	scrubval = scrubrates[i].scrubval;
2162bc65418SDoug Thompson 
217dcd01394SYazen Ghannam 	if (pvt->umc) {
2188051c0afSYazen Ghannam 		__f17h_set_scrubval(pvt, scrubval);
2198051c0afSYazen Ghannam 	} else if (pvt->fam == 0x15 && pvt->model == 0x60) {
220da92110dSAravind Gopalakrishnan 		f15h_select_dct(pvt, 0);
221da92110dSAravind Gopalakrishnan 		pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
222da92110dSAravind Gopalakrishnan 		f15h_select_dct(pvt, 1);
223da92110dSAravind Gopalakrishnan 		pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
224da92110dSAravind Gopalakrishnan 	} else {
225da92110dSAravind Gopalakrishnan 		pci_write_bits32(pvt->F3, SCRCTRL, scrubval, 0x001F);
226da92110dSAravind Gopalakrishnan 	}
2272bc65418SDoug Thompson 
22839094443SBorislav Petkov 	if (scrubval)
22939094443SBorislav Petkov 		return scrubrates[i].bandwidth;
23039094443SBorislav Petkov 
2312bc65418SDoug Thompson 	return 0;
2322bc65418SDoug Thompson }
2332bc65418SDoug Thompson 
234d1ea71cdSBorislav Petkov static int set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
2352bc65418SDoug Thompson {
2362bc65418SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
23787b3e0e6SBorislav Petkov 	u32 min_scrubrate = 0x5;
2382bc65418SDoug Thompson 
239a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
24087b3e0e6SBorislav Petkov 		min_scrubrate = 0x0;
24187b3e0e6SBorislav Petkov 
242da92110dSAravind Gopalakrishnan 	if (pvt->fam == 0x15) {
2433f0aba4fSBorislav Petkov 		/* Erratum #505 */
244da92110dSAravind Gopalakrishnan 		if (pvt->model < 0x10)
24573ba8593SBorislav Petkov 			f15h_select_dct(pvt, 0);
24673ba8593SBorislav Petkov 
247da92110dSAravind Gopalakrishnan 		if (pvt->model == 0x60)
248da92110dSAravind Gopalakrishnan 			min_scrubrate = 0x6;
249da92110dSAravind Gopalakrishnan 	}
250da92110dSAravind Gopalakrishnan 	return __set_scrub_rate(pvt, bw, min_scrubrate);
2512bc65418SDoug Thompson }
2522bc65418SDoug Thompson 
253d1ea71cdSBorislav Petkov static int get_scrub_rate(struct mem_ctl_info *mci)
2542bc65418SDoug Thompson {
2552bc65418SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
25639094443SBorislav Petkov 	int i, retval = -EINVAL;
2578051c0afSYazen Ghannam 	u32 scrubval = 0;
2582bc65418SDoug Thompson 
259dcd01394SYazen Ghannam 	if (pvt->umc) {
2608051c0afSYazen Ghannam 		amd64_read_pci_cfg(pvt->F6, F17H_SCR_BASE_ADDR, &scrubval);
2618051c0afSYazen Ghannam 		if (scrubval & BIT(0)) {
2628051c0afSYazen Ghannam 			amd64_read_pci_cfg(pvt->F6, F17H_SCR_LIMIT_ADDR, &scrubval);
2638051c0afSYazen Ghannam 			scrubval &= 0xF;
2648051c0afSYazen Ghannam 			scrubval += 0x5;
2658051c0afSYazen Ghannam 		} else {
2668051c0afSYazen Ghannam 			scrubval = 0;
2678051c0afSYazen Ghannam 		}
268dcd01394SYazen Ghannam 	} else if (pvt->fam == 0x15) {
269dcd01394SYazen Ghannam 		/* Erratum #505 */
270dcd01394SYazen Ghannam 		if (pvt->model < 0x10)
271dcd01394SYazen Ghannam 			f15h_select_dct(pvt, 0);
2728051c0afSYazen Ghannam 
273dcd01394SYazen Ghannam 		if (pvt->model == 0x60)
274dcd01394SYazen Ghannam 			amd64_read_pci_cfg(pvt->F2, F15H_M60H_SCRCTRL, &scrubval);
275ee470bb2SBorislav Petkov 		else
276ee470bb2SBorislav Petkov 			amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
277dcd01394SYazen Ghannam 	} else {
2785980bb9cSBorislav Petkov 		amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
2798051c0afSYazen Ghannam 	}
2802bc65418SDoug Thompson 
2812bc65418SDoug Thompson 	scrubval = scrubval & 0x001F;
2822bc65418SDoug Thompson 
283926311fdSRoel Kluin 	for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
2842bc65418SDoug Thompson 		if (scrubrates[i].scrubval == scrubval) {
28539094443SBorislav Petkov 			retval = scrubrates[i].bandwidth;
2862bc65418SDoug Thompson 			break;
2872bc65418SDoug Thompson 		}
2882bc65418SDoug Thompson 	}
28939094443SBorislav Petkov 	return retval;
2902bc65418SDoug Thompson }
2912bc65418SDoug Thompson 
2926775763aSDoug Thompson /*
2937f19bf75SBorislav Petkov  * returns true if the SysAddr given by sys_addr matches the
2947f19bf75SBorislav Petkov  * DRAM base/limit associated with node_id
2956775763aSDoug Thompson  */
296d1ea71cdSBorislav Petkov static bool base_limit_match(struct amd64_pvt *pvt, u64 sys_addr, u8 nid)
2976775763aSDoug Thompson {
2987f19bf75SBorislav Petkov 	u64 addr;
2996775763aSDoug Thompson 
3006775763aSDoug Thompson 	/* The K8 treats this as a 40-bit value.  However, bits 63-40 will be
3016775763aSDoug Thompson 	 * all ones if the most significant implemented address bit is 1.
3026775763aSDoug Thompson 	 * Here we discard bits 63-40.  See section 3.4.2 of AMD publication
3036775763aSDoug Thompson 	 * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1
3046775763aSDoug Thompson 	 * Application Programming.
3056775763aSDoug Thompson 	 */
3066775763aSDoug Thompson 	addr = sys_addr & 0x000000ffffffffffull;
3076775763aSDoug Thompson 
3087f19bf75SBorislav Petkov 	return ((addr >= get_dram_base(pvt, nid)) &&
3097f19bf75SBorislav Petkov 		(addr <= get_dram_limit(pvt, nid)));
3106775763aSDoug Thompson }
3116775763aSDoug Thompson 
3126775763aSDoug Thompson /*
3136775763aSDoug Thompson  * Attempt to map a SysAddr to a node. On success, return a pointer to the
3146775763aSDoug Thompson  * mem_ctl_info structure for the node that the SysAddr maps to.
3156775763aSDoug Thompson  *
3166775763aSDoug Thompson  * On failure, return NULL.
3176775763aSDoug Thompson  */
3186775763aSDoug Thompson static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci,
3196775763aSDoug Thompson 						u64 sys_addr)
3206775763aSDoug Thompson {
3216775763aSDoug Thompson 	struct amd64_pvt *pvt;
322c7e5301aSDaniel J Blueman 	u8 node_id;
3236775763aSDoug Thompson 	u32 intlv_en, bits;
3246775763aSDoug Thompson 
3256775763aSDoug Thompson 	/*
3266775763aSDoug Thompson 	 * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section
3276775763aSDoug Thompson 	 * 3.4.4.2) registers to map the SysAddr to a node ID.
3286775763aSDoug Thompson 	 */
3296775763aSDoug Thompson 	pvt = mci->pvt_info;
3306775763aSDoug Thompson 
3316775763aSDoug Thompson 	/*
3326775763aSDoug Thompson 	 * The value of this field should be the same for all DRAM Base
3336775763aSDoug Thompson 	 * registers.  Therefore we arbitrarily choose to read it from the
3346775763aSDoug Thompson 	 * register for node 0.
3356775763aSDoug Thompson 	 */
3367f19bf75SBorislav Petkov 	intlv_en = dram_intlv_en(pvt, 0);
3376775763aSDoug Thompson 
3386775763aSDoug Thompson 	if (intlv_en == 0) {
3397f19bf75SBorislav Petkov 		for (node_id = 0; node_id < DRAM_RANGES; node_id++) {
340d1ea71cdSBorislav Petkov 			if (base_limit_match(pvt, sys_addr, node_id))
3416775763aSDoug Thompson 				goto found;
3426775763aSDoug Thompson 		}
3438edc5445SBorislav Petkov 		goto err_no_match;
3448edc5445SBorislav Petkov 	}
3456775763aSDoug Thompson 
34672f158feSBorislav Petkov 	if (unlikely((intlv_en != 0x01) &&
34772f158feSBorislav Petkov 		     (intlv_en != 0x03) &&
34872f158feSBorislav Petkov 		     (intlv_en != 0x07))) {
34924f9a7feSBorislav Petkov 		amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en);
3506775763aSDoug Thompson 		return NULL;
3516775763aSDoug Thompson 	}
3526775763aSDoug Thompson 
3536775763aSDoug Thompson 	bits = (((u32) sys_addr) >> 12) & intlv_en;
3546775763aSDoug Thompson 
3556775763aSDoug Thompson 	for (node_id = 0; ; ) {
3567f19bf75SBorislav Petkov 		if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits)
3576775763aSDoug Thompson 			break;	/* intlv_sel field matches */
3586775763aSDoug Thompson 
3597f19bf75SBorislav Petkov 		if (++node_id >= DRAM_RANGES)
3606775763aSDoug Thompson 			goto err_no_match;
3616775763aSDoug Thompson 	}
3626775763aSDoug Thompson 
3636775763aSDoug Thompson 	/* sanity test for sys_addr */
364d1ea71cdSBorislav Petkov 	if (unlikely(!base_limit_match(pvt, sys_addr, node_id))) {
36524f9a7feSBorislav Petkov 		amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address"
36624f9a7feSBorislav Petkov 			   "range for node %d with node interleaving enabled.\n",
3678edc5445SBorislav Petkov 			   __func__, sys_addr, node_id);
3686775763aSDoug Thompson 		return NULL;
3696775763aSDoug Thompson 	}
3706775763aSDoug Thompson 
3716775763aSDoug Thompson found:
372b487c33eSBorislav Petkov 	return edac_mc_find((int)node_id);
3736775763aSDoug Thompson 
3746775763aSDoug Thompson err_no_match:
375956b9ba1SJoe Perches 	edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n",
3766775763aSDoug Thompson 		 (unsigned long)sys_addr);
3776775763aSDoug Thompson 
3786775763aSDoug Thompson 	return NULL;
3796775763aSDoug Thompson }
380e2ce7255SDoug Thompson 
381e2ce7255SDoug Thompson /*
38211c75eadSBorislav Petkov  * compute the CS base address of the @csrow on the DRAM controller @dct.
38311c75eadSBorislav Petkov  * For details see F2x[5C:40] in the processor's BKDG
384e2ce7255SDoug Thompson  */
38511c75eadSBorislav Petkov static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct,
38611c75eadSBorislav Petkov 				 u64 *base, u64 *mask)
387e2ce7255SDoug Thompson {
38811c75eadSBorislav Petkov 	u64 csbase, csmask, base_bits, mask_bits;
38911c75eadSBorislav Petkov 	u8 addr_shift;
39011c75eadSBorislav Petkov 
39118b94f66SAravind Gopalakrishnan 	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
39211c75eadSBorislav Petkov 		csbase		= pvt->csels[dct].csbases[csrow];
39311c75eadSBorislav Petkov 		csmask		= pvt->csels[dct].csmasks[csrow];
39410ef6b0dSChen, Gong 		base_bits	= GENMASK_ULL(31, 21) | GENMASK_ULL(15, 9);
39510ef6b0dSChen, Gong 		mask_bits	= GENMASK_ULL(29, 21) | GENMASK_ULL(15, 9);
39611c75eadSBorislav Petkov 		addr_shift	= 4;
39794c1acf2SAravind Gopalakrishnan 
39894c1acf2SAravind Gopalakrishnan 	/*
39918b94f66SAravind Gopalakrishnan 	 * F16h and F15h, models 30h and later need two addr_shift values:
40018b94f66SAravind Gopalakrishnan 	 * 8 for high and 6 for low (cf. F16h BKDG).
40194c1acf2SAravind Gopalakrishnan 	 */
40218b94f66SAravind Gopalakrishnan 	} else if (pvt->fam == 0x16 ||
40318b94f66SAravind Gopalakrishnan 		  (pvt->fam == 0x15 && pvt->model >= 0x30)) {
40494c1acf2SAravind Gopalakrishnan 		csbase          = pvt->csels[dct].csbases[csrow];
40594c1acf2SAravind Gopalakrishnan 		csmask          = pvt->csels[dct].csmasks[csrow >> 1];
40694c1acf2SAravind Gopalakrishnan 
40710ef6b0dSChen, Gong 		*base  = (csbase & GENMASK_ULL(15,  5)) << 6;
40810ef6b0dSChen, Gong 		*base |= (csbase & GENMASK_ULL(30, 19)) << 8;
40994c1acf2SAravind Gopalakrishnan 
41094c1acf2SAravind Gopalakrishnan 		*mask = ~0ULL;
41194c1acf2SAravind Gopalakrishnan 		/* poke holes for the csmask */
41210ef6b0dSChen, Gong 		*mask &= ~((GENMASK_ULL(15, 5)  << 6) |
41310ef6b0dSChen, Gong 			   (GENMASK_ULL(30, 19) << 8));
41494c1acf2SAravind Gopalakrishnan 
41510ef6b0dSChen, Gong 		*mask |= (csmask & GENMASK_ULL(15, 5))  << 6;
41610ef6b0dSChen, Gong 		*mask |= (csmask & GENMASK_ULL(30, 19)) << 8;
41794c1acf2SAravind Gopalakrishnan 
41894c1acf2SAravind Gopalakrishnan 		return;
41911c75eadSBorislav Petkov 	} else {
42011c75eadSBorislav Petkov 		csbase		= pvt->csels[dct].csbases[csrow];
42111c75eadSBorislav Petkov 		csmask		= pvt->csels[dct].csmasks[csrow >> 1];
42211c75eadSBorislav Petkov 		addr_shift	= 8;
42311c75eadSBorislav Petkov 
424a4b4bedcSBorislav Petkov 		if (pvt->fam == 0x15)
42510ef6b0dSChen, Gong 			base_bits = mask_bits =
42610ef6b0dSChen, Gong 				GENMASK_ULL(30,19) | GENMASK_ULL(13,5);
42711c75eadSBorislav Petkov 		else
42810ef6b0dSChen, Gong 			base_bits = mask_bits =
42910ef6b0dSChen, Gong 				GENMASK_ULL(28,19) | GENMASK_ULL(13,5);
430e2ce7255SDoug Thompson 	}
431e2ce7255SDoug Thompson 
43211c75eadSBorislav Petkov 	*base  = (csbase & base_bits) << addr_shift;
433e2ce7255SDoug Thompson 
43411c75eadSBorislav Petkov 	*mask  = ~0ULL;
43511c75eadSBorislav Petkov 	/* poke holes for the csmask */
43611c75eadSBorislav Petkov 	*mask &= ~(mask_bits << addr_shift);
43711c75eadSBorislav Petkov 	/* OR them in */
43811c75eadSBorislav Petkov 	*mask |= (csmask & mask_bits) << addr_shift;
439e2ce7255SDoug Thompson }
440e2ce7255SDoug Thompson 
44111c75eadSBorislav Petkov #define for_each_chip_select(i, dct, pvt) \
44211c75eadSBorislav Petkov 	for (i = 0; i < pvt->csels[dct].b_cnt; i++)
44311c75eadSBorislav Petkov 
444614ec9d8SBorislav Petkov #define chip_select_base(i, dct, pvt) \
445614ec9d8SBorislav Petkov 	pvt->csels[dct].csbases[i]
446614ec9d8SBorislav Petkov 
44711c75eadSBorislav Petkov #define for_each_chip_select_mask(i, dct, pvt) \
44811c75eadSBorislav Petkov 	for (i = 0; i < pvt->csels[dct].m_cnt; i++)
44911c75eadSBorislav Petkov 
4504d30d2bcSYazen Ghannam #define for_each_umc(i) \
4515e4c5527SYazen Ghannam 	for (i = 0; i < fam_type->max_mcs; i++)
4524d30d2bcSYazen Ghannam 
453e2ce7255SDoug Thompson /*
454e2ce7255SDoug Thompson  * @input_addr is an InputAddr associated with the node given by mci. Return the
455e2ce7255SDoug Thompson  * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr).
456e2ce7255SDoug Thompson  */
457e2ce7255SDoug Thompson static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr)
458e2ce7255SDoug Thompson {
459e2ce7255SDoug Thompson 	struct amd64_pvt *pvt;
460e2ce7255SDoug Thompson 	int csrow;
461e2ce7255SDoug Thompson 	u64 base, mask;
462e2ce7255SDoug Thompson 
463e2ce7255SDoug Thompson 	pvt = mci->pvt_info;
464e2ce7255SDoug Thompson 
46511c75eadSBorislav Petkov 	for_each_chip_select(csrow, 0, pvt) {
46611c75eadSBorislav Petkov 		if (!csrow_enabled(csrow, 0, pvt))
467e2ce7255SDoug Thompson 			continue;
468e2ce7255SDoug Thompson 
46911c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, csrow, 0, &base, &mask);
47011c75eadSBorislav Petkov 
47111c75eadSBorislav Petkov 		mask = ~mask;
472e2ce7255SDoug Thompson 
473e2ce7255SDoug Thompson 		if ((input_addr & mask) == (base & mask)) {
474956b9ba1SJoe Perches 			edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n",
475e2ce7255SDoug Thompson 				 (unsigned long)input_addr, csrow,
476e2ce7255SDoug Thompson 				 pvt->mc_node_id);
477e2ce7255SDoug Thompson 
478e2ce7255SDoug Thompson 			return csrow;
479e2ce7255SDoug Thompson 		}
480e2ce7255SDoug Thompson 	}
481956b9ba1SJoe Perches 	edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n",
482e2ce7255SDoug Thompson 		 (unsigned long)input_addr, pvt->mc_node_id);
483e2ce7255SDoug Thompson 
484e2ce7255SDoug Thompson 	return -1;
485e2ce7255SDoug Thompson }
486e2ce7255SDoug Thompson 
487e2ce7255SDoug Thompson /*
488e2ce7255SDoug Thompson  * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094)
489e2ce7255SDoug Thompson  * for the node represented by mci. Info is passed back in *hole_base,
490e2ce7255SDoug Thompson  * *hole_offset, and *hole_size.  Function returns 0 if info is valid or 1 if
491e2ce7255SDoug Thompson  * info is invalid. Info may be invalid for either of the following reasons:
492e2ce7255SDoug Thompson  *
493e2ce7255SDoug Thompson  * - The revision of the node is not E or greater.  In this case, the DRAM Hole
494e2ce7255SDoug Thompson  *   Address Register does not exist.
495e2ce7255SDoug Thompson  *
496e2ce7255SDoug Thompson  * - The DramHoleValid bit is cleared in the DRAM Hole Address Register,
497e2ce7255SDoug Thompson  *   indicating that its contents are not valid.
498e2ce7255SDoug Thompson  *
499e2ce7255SDoug Thompson  * The values passed back in *hole_base, *hole_offset, and *hole_size are
500e2ce7255SDoug Thompson  * complete 32-bit values despite the fact that the bitfields in the DHAR
501e2ce7255SDoug Thompson  * only represent bits 31-24 of the base and offset values.
502e2ce7255SDoug Thompson  */
503*2a28ceefSBorislav Petkov static int get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
504e2ce7255SDoug Thompson 			      u64 *hole_offset, u64 *hole_size)
505e2ce7255SDoug Thompson {
506e2ce7255SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
507e2ce7255SDoug Thompson 
508e2ce7255SDoug Thompson 	/* only revE and later have the DRAM Hole Address Register */
509a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_E) {
510956b9ba1SJoe Perches 		edac_dbg(1, "  revision %d for node %d does not support DHAR\n",
511e2ce7255SDoug Thompson 			 pvt->ext_model, pvt->mc_node_id);
512e2ce7255SDoug Thompson 		return 1;
513e2ce7255SDoug Thompson 	}
514e2ce7255SDoug Thompson 
515bc21fa57SBorislav Petkov 	/* valid for Fam10h and above */
516a4b4bedcSBorislav Petkov 	if (pvt->fam >= 0x10 && !dhar_mem_hoist_valid(pvt)) {
517956b9ba1SJoe Perches 		edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this system\n");
518e2ce7255SDoug Thompson 		return 1;
519e2ce7255SDoug Thompson 	}
520e2ce7255SDoug Thompson 
521c8e518d5SBorislav Petkov 	if (!dhar_valid(pvt)) {
522956b9ba1SJoe Perches 		edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this node %d\n",
523e2ce7255SDoug Thompson 			 pvt->mc_node_id);
524e2ce7255SDoug Thompson 		return 1;
525e2ce7255SDoug Thompson 	}
526e2ce7255SDoug Thompson 
527e2ce7255SDoug Thompson 	/* This node has Memory Hoisting */
528e2ce7255SDoug Thompson 
529e2ce7255SDoug Thompson 	/* +------------------+--------------------+--------------------+-----
530e2ce7255SDoug Thompson 	 * | memory           | DRAM hole          | relocated          |
531e2ce7255SDoug Thompson 	 * | [0, (x - 1)]     | [x, 0xffffffff]    | addresses from     |
532e2ce7255SDoug Thompson 	 * |                  |                    | DRAM hole          |
533e2ce7255SDoug Thompson 	 * |                  |                    | [0x100000000,      |
534e2ce7255SDoug Thompson 	 * |                  |                    |  (0x100000000+     |
535e2ce7255SDoug Thompson 	 * |                  |                    |   (0xffffffff-x))] |
536e2ce7255SDoug Thompson 	 * +------------------+--------------------+--------------------+-----
537e2ce7255SDoug Thompson 	 *
538e2ce7255SDoug Thompson 	 * Above is a diagram of physical memory showing the DRAM hole and the
539e2ce7255SDoug Thompson 	 * relocated addresses from the DRAM hole.  As shown, the DRAM hole
540e2ce7255SDoug Thompson 	 * starts at address x (the base address) and extends through address
541e2ce7255SDoug Thompson 	 * 0xffffffff.  The DRAM Hole Address Register (DHAR) relocates the
542e2ce7255SDoug Thompson 	 * addresses in the hole so that they start at 0x100000000.
543e2ce7255SDoug Thompson 	 */
544e2ce7255SDoug Thompson 
5451f31677eSBorislav Petkov 	*hole_base = dhar_base(pvt);
5461f31677eSBorislav Petkov 	*hole_size = (1ULL << 32) - *hole_base;
547e2ce7255SDoug Thompson 
548a4b4bedcSBorislav Petkov 	*hole_offset = (pvt->fam > 0xf) ? f10_dhar_offset(pvt)
549a4b4bedcSBorislav Petkov 					: k8_dhar_offset(pvt);
550e2ce7255SDoug Thompson 
551956b9ba1SJoe Perches 	edac_dbg(1, "  DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
552e2ce7255SDoug Thompson 		 pvt->mc_node_id, (unsigned long)*hole_base,
553e2ce7255SDoug Thompson 		 (unsigned long)*hole_offset, (unsigned long)*hole_size);
554e2ce7255SDoug Thompson 
555e2ce7255SDoug Thompson 	return 0;
556e2ce7255SDoug Thompson }
557*2a28ceefSBorislav Petkov 
558*2a28ceefSBorislav Petkov #ifdef CONFIG_EDAC_DEBUG
559*2a28ceefSBorislav Petkov #define EDAC_DCT_ATTR_SHOW(reg)						\
560*2a28ceefSBorislav Petkov static ssize_t reg##_show(struct device *dev,				\
561*2a28ceefSBorislav Petkov 			 struct device_attribute *mattr, char *data)	\
562*2a28ceefSBorislav Petkov {									\
563*2a28ceefSBorislav Petkov 	struct mem_ctl_info *mci = to_mci(dev);				\
564*2a28ceefSBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;				\
565*2a28ceefSBorislav Petkov 									\
566*2a28ceefSBorislav Petkov 	return sprintf(data, "0x%016llx\n", (u64)pvt->reg);		\
567*2a28ceefSBorislav Petkov }
568*2a28ceefSBorislav Petkov 
569*2a28ceefSBorislav Petkov EDAC_DCT_ATTR_SHOW(dhar);
570*2a28ceefSBorislav Petkov EDAC_DCT_ATTR_SHOW(dbam0);
571*2a28ceefSBorislav Petkov EDAC_DCT_ATTR_SHOW(top_mem);
572*2a28ceefSBorislav Petkov EDAC_DCT_ATTR_SHOW(top_mem2);
573*2a28ceefSBorislav Petkov 
574*2a28ceefSBorislav Petkov static ssize_t hole_show(struct device *dev, struct device_attribute *mattr,
575*2a28ceefSBorislav Petkov 			 char *data)
576*2a28ceefSBorislav Petkov {
577*2a28ceefSBorislav Petkov 	struct mem_ctl_info *mci = to_mci(dev);
578*2a28ceefSBorislav Petkov 
579*2a28ceefSBorislav Petkov 	u64 hole_base = 0;
580*2a28ceefSBorislav Petkov 	u64 hole_offset = 0;
581*2a28ceefSBorislav Petkov 	u64 hole_size = 0;
582*2a28ceefSBorislav Petkov 
583*2a28ceefSBorislav Petkov 	get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size);
584*2a28ceefSBorislav Petkov 
585*2a28ceefSBorislav Petkov 	return sprintf(data, "%llx %llx %llx\n", hole_base, hole_offset,
586*2a28ceefSBorislav Petkov 						 hole_size);
587*2a28ceefSBorislav Petkov }
588*2a28ceefSBorislav Petkov 
589*2a28ceefSBorislav Petkov /*
590*2a28ceefSBorislav Petkov  * update NUM_DBG_ATTRS in case you add new members
591*2a28ceefSBorislav Petkov  */
592*2a28ceefSBorislav Petkov static DEVICE_ATTR(dhar, S_IRUGO, dhar_show, NULL);
593*2a28ceefSBorislav Petkov static DEVICE_ATTR(dbam, S_IRUGO, dbam0_show, NULL);
594*2a28ceefSBorislav Petkov static DEVICE_ATTR(topmem, S_IRUGO, top_mem_show, NULL);
595*2a28ceefSBorislav Petkov static DEVICE_ATTR(topmem2, S_IRUGO, top_mem2_show, NULL);
596*2a28ceefSBorislav Petkov static DEVICE_ATTR(dram_hole, S_IRUGO, hole_show, NULL);
597*2a28ceefSBorislav Petkov 
598*2a28ceefSBorislav Petkov static struct attribute *dbg_attrs[] = {
599*2a28ceefSBorislav Petkov 	&dev_attr_dhar.attr,
600*2a28ceefSBorislav Petkov 	&dev_attr_dbam.attr,
601*2a28ceefSBorislav Petkov 	&dev_attr_topmem.attr,
602*2a28ceefSBorislav Petkov 	&dev_attr_topmem2.attr,
603*2a28ceefSBorislav Petkov 	&dev_attr_dram_hole.attr,
604*2a28ceefSBorislav Petkov 	NULL
605*2a28ceefSBorislav Petkov };
606*2a28ceefSBorislav Petkov 
607*2a28ceefSBorislav Petkov static const struct attribute_group dbg_group = {
608*2a28ceefSBorislav Petkov 	.attrs = dbg_attrs,
609*2a28ceefSBorislav Petkov };
610*2a28ceefSBorislav Petkov #endif /* CONFIG_EDAC_DEBUG */
611*2a28ceefSBorislav Petkov 
612e2ce7255SDoug Thompson 
61393c2df58SDoug Thompson /*
61493c2df58SDoug Thompson  * Return the DramAddr that the SysAddr given by @sys_addr maps to.  It is
61593c2df58SDoug Thompson  * assumed that sys_addr maps to the node given by mci.
61693c2df58SDoug Thompson  *
61793c2df58SDoug Thompson  * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section
61893c2df58SDoug Thompson  * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a
61993c2df58SDoug Thompson  * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled,
62093c2df58SDoug Thompson  * then it is also involved in translating a SysAddr to a DramAddr. Sections
62193c2df58SDoug Thompson  * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting.
62293c2df58SDoug Thompson  * These parts of the documentation are unclear. I interpret them as follows:
62393c2df58SDoug Thompson  *
62493c2df58SDoug Thompson  * When node n receives a SysAddr, it processes the SysAddr as follows:
62593c2df58SDoug Thompson  *
62693c2df58SDoug Thompson  * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM
62793c2df58SDoug Thompson  *    Limit registers for node n. If the SysAddr is not within the range
62893c2df58SDoug Thompson  *    specified by the base and limit values, then node n ignores the Sysaddr
62993c2df58SDoug Thompson  *    (since it does not map to node n). Otherwise continue to step 2 below.
63093c2df58SDoug Thompson  *
63193c2df58SDoug Thompson  * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is
63293c2df58SDoug Thompson  *    disabled so skip to step 3 below. Otherwise see if the SysAddr is within
63393c2df58SDoug Thompson  *    the range of relocated addresses (starting at 0x100000000) from the DRAM
63493c2df58SDoug Thompson  *    hole. If not, skip to step 3 below. Else get the value of the
63593c2df58SDoug Thompson  *    DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the
63693c2df58SDoug Thompson  *    offset defined by this value from the SysAddr.
63793c2df58SDoug Thompson  *
63893c2df58SDoug Thompson  * 3. Obtain the base address for node n from the DRAMBase field of the DRAM
63993c2df58SDoug Thompson  *    Base register for node n. To obtain the DramAddr, subtract the base
64093c2df58SDoug Thompson  *    address from the SysAddr, as shown near the start of section 3.4.4 (p.70).
64193c2df58SDoug Thompson  */
64293c2df58SDoug Thompson static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
64393c2df58SDoug Thompson {
6447f19bf75SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
64593c2df58SDoug Thompson 	u64 dram_base, hole_base, hole_offset, hole_size, dram_addr;
6461f31677eSBorislav Petkov 	int ret;
64793c2df58SDoug Thompson 
6487f19bf75SBorislav Petkov 	dram_base = get_dram_base(pvt, pvt->mc_node_id);
64993c2df58SDoug Thompson 
650*2a28ceefSBorislav Petkov 	ret = get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size);
65193c2df58SDoug Thompson 	if (!ret) {
6521f31677eSBorislav Petkov 		if ((sys_addr >= (1ULL << 32)) &&
6531f31677eSBorislav Petkov 		    (sys_addr < ((1ULL << 32) + hole_size))) {
65493c2df58SDoug Thompson 			/* use DHAR to translate SysAddr to DramAddr */
65593c2df58SDoug Thompson 			dram_addr = sys_addr - hole_offset;
65693c2df58SDoug Thompson 
657956b9ba1SJoe Perches 			edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
65893c2df58SDoug Thompson 				 (unsigned long)sys_addr,
65993c2df58SDoug Thompson 				 (unsigned long)dram_addr);
66093c2df58SDoug Thompson 
66193c2df58SDoug Thompson 			return dram_addr;
66293c2df58SDoug Thompson 		}
66393c2df58SDoug Thompson 	}
66493c2df58SDoug Thompson 
66593c2df58SDoug Thompson 	/*
66693c2df58SDoug Thompson 	 * Translate the SysAddr to a DramAddr as shown near the start of
66793c2df58SDoug Thompson 	 * section 3.4.4 (p. 70).  Although sys_addr is a 64-bit value, the k8
66893c2df58SDoug Thompson 	 * only deals with 40-bit values.  Therefore we discard bits 63-40 of
66993c2df58SDoug Thompson 	 * sys_addr below.  If bit 39 of sys_addr is 1 then the bits we
67093c2df58SDoug Thompson 	 * discard are all 1s.  Otherwise the bits we discard are all 0s.  See
67193c2df58SDoug Thompson 	 * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture
67293c2df58SDoug Thompson 	 * Programmer's Manual Volume 1 Application Programming.
67393c2df58SDoug Thompson 	 */
67410ef6b0dSChen, Gong 	dram_addr = (sys_addr & GENMASK_ULL(39, 0)) - dram_base;
67593c2df58SDoug Thompson 
676956b9ba1SJoe Perches 	edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
677956b9ba1SJoe Perches 		 (unsigned long)sys_addr, (unsigned long)dram_addr);
67893c2df58SDoug Thompson 	return dram_addr;
67993c2df58SDoug Thompson }
68093c2df58SDoug Thompson 
68193c2df58SDoug Thompson /*
68293c2df58SDoug Thompson  * @intlv_en is the value of the IntlvEn field from a DRAM Base register
68393c2df58SDoug Thompson  * (section 3.4.4.1).  Return the number of bits from a SysAddr that are used
68493c2df58SDoug Thompson  * for node interleaving.
68593c2df58SDoug Thompson  */
68693c2df58SDoug Thompson static int num_node_interleave_bits(unsigned intlv_en)
68793c2df58SDoug Thompson {
68893c2df58SDoug Thompson 	static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 };
68993c2df58SDoug Thompson 	int n;
69093c2df58SDoug Thompson 
69193c2df58SDoug Thompson 	BUG_ON(intlv_en > 7);
69293c2df58SDoug Thompson 	n = intlv_shift_table[intlv_en];
69393c2df58SDoug Thompson 	return n;
69493c2df58SDoug Thompson }
69593c2df58SDoug Thompson 
69693c2df58SDoug Thompson /* Translate the DramAddr given by @dram_addr to an InputAddr. */
69793c2df58SDoug Thompson static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
69893c2df58SDoug Thompson {
69993c2df58SDoug Thompson 	struct amd64_pvt *pvt;
70093c2df58SDoug Thompson 	int intlv_shift;
70193c2df58SDoug Thompson 	u64 input_addr;
70293c2df58SDoug Thompson 
70393c2df58SDoug Thompson 	pvt = mci->pvt_info;
70493c2df58SDoug Thompson 
70593c2df58SDoug Thompson 	/*
70693c2df58SDoug Thompson 	 * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
70793c2df58SDoug Thompson 	 * concerning translating a DramAddr to an InputAddr.
70893c2df58SDoug Thompson 	 */
7097f19bf75SBorislav Petkov 	intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
71010ef6b0dSChen, Gong 	input_addr = ((dram_addr >> intlv_shift) & GENMASK_ULL(35, 12)) +
71193c2df58SDoug Thompson 		      (dram_addr & 0xfff);
71293c2df58SDoug Thompson 
713956b9ba1SJoe Perches 	edac_dbg(2, "  Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
71493c2df58SDoug Thompson 		 intlv_shift, (unsigned long)dram_addr,
71593c2df58SDoug Thompson 		 (unsigned long)input_addr);
71693c2df58SDoug Thompson 
71793c2df58SDoug Thompson 	return input_addr;
71893c2df58SDoug Thompson }
71993c2df58SDoug Thompson 
72093c2df58SDoug Thompson /*
72193c2df58SDoug Thompson  * Translate the SysAddr represented by @sys_addr to an InputAddr.  It is
72293c2df58SDoug Thompson  * assumed that @sys_addr maps to the node given by mci.
72393c2df58SDoug Thompson  */
72493c2df58SDoug Thompson static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
72593c2df58SDoug Thompson {
72693c2df58SDoug Thompson 	u64 input_addr;
72793c2df58SDoug Thompson 
72893c2df58SDoug Thompson 	input_addr =
72993c2df58SDoug Thompson 	    dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
73093c2df58SDoug Thompson 
731c19ca6cbSMasanari Iida 	edac_dbg(2, "SysAddr 0x%lx translates to InputAddr 0x%lx\n",
73293c2df58SDoug Thompson 		 (unsigned long)sys_addr, (unsigned long)input_addr);
73393c2df58SDoug Thompson 
73493c2df58SDoug Thompson 	return input_addr;
73593c2df58SDoug Thompson }
73693c2df58SDoug Thompson 
73793c2df58SDoug Thompson /* Map the Error address to a PAGE and PAGE OFFSET. */
73893c2df58SDoug Thompson static inline void error_address_to_page_and_offset(u64 error_address,
73933ca0643SBorislav Petkov 						    struct err_info *err)
74093c2df58SDoug Thompson {
74133ca0643SBorislav Petkov 	err->page = (u32) (error_address >> PAGE_SHIFT);
74233ca0643SBorislav Petkov 	err->offset = ((u32) error_address) & ~PAGE_MASK;
74393c2df58SDoug Thompson }
74493c2df58SDoug Thompson 
74593c2df58SDoug Thompson /*
74693c2df58SDoug Thompson  * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address
74793c2df58SDoug Thompson  * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers
74893c2df58SDoug Thompson  * of a node that detected an ECC memory error.  mci represents the node that
74993c2df58SDoug Thompson  * the error address maps to (possibly different from the node that detected
75093c2df58SDoug Thompson  * the error).  Return the number of the csrow that sys_addr maps to, or -1 on
75193c2df58SDoug Thompson  * error.
75293c2df58SDoug Thompson  */
75393c2df58SDoug Thompson static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
75493c2df58SDoug Thompson {
75593c2df58SDoug Thompson 	int csrow;
75693c2df58SDoug Thompson 
75793c2df58SDoug Thompson 	csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr));
75893c2df58SDoug Thompson 
75993c2df58SDoug Thompson 	if (csrow == -1)
76024f9a7feSBorislav Petkov 		amd64_mc_err(mci, "Failed to translate InputAddr to csrow for "
76193c2df58SDoug Thompson 				  "address 0x%lx\n", (unsigned long)sys_addr);
76293c2df58SDoug Thompson 	return csrow;
76393c2df58SDoug Thompson }
764e2ce7255SDoug Thompson 
765bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
7662da11654SDoug Thompson 
7672da11654SDoug Thompson /*
7682da11654SDoug Thompson  * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
7692da11654SDoug Thompson  * are ECC capable.
7702da11654SDoug Thompson  */
771d1ea71cdSBorislav Petkov static unsigned long determine_edac_cap(struct amd64_pvt *pvt)
7722da11654SDoug Thompson {
7731f6189edSDan Carpenter 	unsigned long edac_cap = EDAC_FLAG_NONE;
774d27f3a34SYazen Ghannam 	u8 bit;
7752da11654SDoug Thompson 
776d27f3a34SYazen Ghannam 	if (pvt->umc) {
777d27f3a34SYazen Ghannam 		u8 i, umc_en_mask = 0, dimm_ecc_en_mask = 0;
778d27f3a34SYazen Ghannam 
7794d30d2bcSYazen Ghannam 		for_each_umc(i) {
780d27f3a34SYazen Ghannam 			if (!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT))
781d27f3a34SYazen Ghannam 				continue;
782d27f3a34SYazen Ghannam 
783d27f3a34SYazen Ghannam 			umc_en_mask |= BIT(i);
784d27f3a34SYazen Ghannam 
785d27f3a34SYazen Ghannam 			/* UMC Configuration bit 12 (DimmEccEn) */
786d27f3a34SYazen Ghannam 			if (pvt->umc[i].umc_cfg & BIT(12))
787d27f3a34SYazen Ghannam 				dimm_ecc_en_mask |= BIT(i);
788d27f3a34SYazen Ghannam 		}
789d27f3a34SYazen Ghannam 
790d27f3a34SYazen Ghannam 		if (umc_en_mask == dimm_ecc_en_mask)
791d27f3a34SYazen Ghannam 			edac_cap = EDAC_FLAG_SECDED;
792d27f3a34SYazen Ghannam 	} else {
793a4b4bedcSBorislav Petkov 		bit = (pvt->fam > 0xf || pvt->ext_model >= K8_REV_F)
7942da11654SDoug Thompson 			? 19
7952da11654SDoug Thompson 			: 17;
7962da11654SDoug Thompson 
797584fcff4SBorislav Petkov 		if (pvt->dclr0 & BIT(bit))
7982da11654SDoug Thompson 			edac_cap = EDAC_FLAG_SECDED;
799d27f3a34SYazen Ghannam 	}
8002da11654SDoug Thompson 
8012da11654SDoug Thompson 	return edac_cap;
8022da11654SDoug Thompson }
8032da11654SDoug Thompson 
804d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *, u8);
8052da11654SDoug Thompson 
806d1ea71cdSBorislav Petkov static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan)
80768798e17SBorislav Petkov {
808956b9ba1SJoe Perches 	edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
80968798e17SBorislav Petkov 
810a597d2a5SAravind Gopalakrishnan 	if (pvt->dram_type == MEM_LRDDR3) {
811a597d2a5SAravind Gopalakrishnan 		u32 dcsm = pvt->csels[chan].csmasks[0];
812a597d2a5SAravind Gopalakrishnan 		/*
813a597d2a5SAravind Gopalakrishnan 		 * It's assumed all LRDIMMs in a DCT are going to be of
814a597d2a5SAravind Gopalakrishnan 		 * same 'type' until proven otherwise. So, use a cs
815a597d2a5SAravind Gopalakrishnan 		 * value of '0' here to get dcsm value.
816a597d2a5SAravind Gopalakrishnan 		 */
817a597d2a5SAravind Gopalakrishnan 		edac_dbg(1, " LRDIMM %dx rank multiply\n", (dcsm & 0x3));
818a597d2a5SAravind Gopalakrishnan 	}
819a597d2a5SAravind Gopalakrishnan 
820a597d2a5SAravind Gopalakrishnan 	edac_dbg(1, "All DIMMs support ECC:%s\n",
82168798e17SBorislav Petkov 		    (dclr & BIT(19)) ? "yes" : "no");
82268798e17SBorislav Petkov 
823a597d2a5SAravind Gopalakrishnan 
824956b9ba1SJoe Perches 	edac_dbg(1, "  PAR/ERR parity: %s\n",
82568798e17SBorislav Petkov 		 (dclr & BIT(8)) ?  "enabled" : "disabled");
82668798e17SBorislav Petkov 
827a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10)
828956b9ba1SJoe Perches 		edac_dbg(1, "  DCT 128bit mode width: %s\n",
82968798e17SBorislav Petkov 			 (dclr & BIT(11)) ?  "128b" : "64b");
83068798e17SBorislav Petkov 
831956b9ba1SJoe Perches 	edac_dbg(1, "  x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
83268798e17SBorislav Petkov 		 (dclr & BIT(12)) ?  "yes" : "no",
83368798e17SBorislav Petkov 		 (dclr & BIT(13)) ?  "yes" : "no",
83468798e17SBorislav Petkov 		 (dclr & BIT(14)) ?  "yes" : "no",
83568798e17SBorislav Petkov 		 (dclr & BIT(15)) ?  "yes" : "no");
83668798e17SBorislav Petkov }
83768798e17SBorislav Petkov 
838e53a3b26SYazen Ghannam #define CS_EVEN_PRIMARY		BIT(0)
839e53a3b26SYazen Ghannam #define CS_ODD_PRIMARY		BIT(1)
84081f5090dSYazen Ghannam #define CS_EVEN_SECONDARY	BIT(2)
84181f5090dSYazen Ghannam #define CS_ODD_SECONDARY	BIT(3)
842e53a3b26SYazen Ghannam 
84381f5090dSYazen Ghannam #define CS_EVEN			(CS_EVEN_PRIMARY | CS_EVEN_SECONDARY)
84481f5090dSYazen Ghannam #define CS_ODD			(CS_ODD_PRIMARY | CS_ODD_SECONDARY)
845e53a3b26SYazen Ghannam 
846e53a3b26SYazen Ghannam static int f17_get_cs_mode(int dimm, u8 ctrl, struct amd64_pvt *pvt)
847fc00c6a4SYazen Ghannam {
848e53a3b26SYazen Ghannam 	int cs_mode = 0;
849fc00c6a4SYazen Ghannam 
850e53a3b26SYazen Ghannam 	if (csrow_enabled(2 * dimm, ctrl, pvt))
851e53a3b26SYazen Ghannam 		cs_mode |= CS_EVEN_PRIMARY;
852fc00c6a4SYazen Ghannam 
853e53a3b26SYazen Ghannam 	if (csrow_enabled(2 * dimm + 1, ctrl, pvt))
854e53a3b26SYazen Ghannam 		cs_mode |= CS_ODD_PRIMARY;
855e53a3b26SYazen Ghannam 
85681f5090dSYazen Ghannam 	/* Asymmetric dual-rank DIMM support. */
85781f5090dSYazen Ghannam 	if (csrow_sec_enabled(2 * dimm + 1, ctrl, pvt))
85881f5090dSYazen Ghannam 		cs_mode |= CS_ODD_SECONDARY;
85981f5090dSYazen Ghannam 
860e53a3b26SYazen Ghannam 	return cs_mode;
861fc00c6a4SYazen Ghannam }
862fc00c6a4SYazen Ghannam 
86307ed82efSYazen Ghannam static void debug_display_dimm_sizes_df(struct amd64_pvt *pvt, u8 ctrl)
86407ed82efSYazen Ghannam {
865e53a3b26SYazen Ghannam 	int dimm, size0, size1, cs0, cs1, cs_mode;
86607ed82efSYazen Ghannam 
86707ed82efSYazen Ghannam 	edac_printk(KERN_DEBUG, EDAC_MC, "UMC%d chip selects:\n", ctrl);
86807ed82efSYazen Ghannam 
869d971e28eSYazen Ghannam 	for (dimm = 0; dimm < 2; dimm++) {
870eb77e6b8SYazen Ghannam 		cs0 = dimm * 2;
871eb77e6b8SYazen Ghannam 		cs1 = dimm * 2 + 1;
872eb77e6b8SYazen Ghannam 
873e53a3b26SYazen Ghannam 		cs_mode = f17_get_cs_mode(dimm, ctrl, pvt);
874e53a3b26SYazen Ghannam 
875e53a3b26SYazen Ghannam 		size0 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs0);
876e53a3b26SYazen Ghannam 		size1 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs1);
87707ed82efSYazen Ghannam 
87807ed82efSYazen Ghannam 		amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
879eb77e6b8SYazen Ghannam 				cs0,	size0,
880eb77e6b8SYazen Ghannam 				cs1,	size1);
88107ed82efSYazen Ghannam 	}
88207ed82efSYazen Ghannam }
88307ed82efSYazen Ghannam 
88407ed82efSYazen Ghannam static void __dump_misc_regs_df(struct amd64_pvt *pvt)
88507ed82efSYazen Ghannam {
88607ed82efSYazen Ghannam 	struct amd64_umc *umc;
88707ed82efSYazen Ghannam 	u32 i, tmp, umc_base;
88807ed82efSYazen Ghannam 
8894d30d2bcSYazen Ghannam 	for_each_umc(i) {
89007ed82efSYazen Ghannam 		umc_base = get_umc_base(i);
89107ed82efSYazen Ghannam 		umc = &pvt->umc[i];
89207ed82efSYazen Ghannam 
89307ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d DIMM cfg: 0x%x\n", i, umc->dimm_cfg);
89407ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d UMC cfg: 0x%x\n", i, umc->umc_cfg);
89507ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d SDP ctrl: 0x%x\n", i, umc->sdp_ctrl);
89607ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d ECC ctrl: 0x%x\n", i, umc->ecc_ctrl);
89707ed82efSYazen Ghannam 
89807ed82efSYazen Ghannam 		amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ECC_BAD_SYMBOL, &tmp);
89907ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d ECC bad symbol: 0x%x\n", i, tmp);
90007ed82efSYazen Ghannam 
90107ed82efSYazen Ghannam 		amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_UMC_CAP, &tmp);
90207ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d UMC cap: 0x%x\n", i, tmp);
90307ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d UMC cap high: 0x%x\n", i, umc->umc_cap_hi);
90407ed82efSYazen Ghannam 
90507ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d ECC capable: %s, ChipKill ECC capable: %s\n",
90607ed82efSYazen Ghannam 				i, (umc->umc_cap_hi & BIT(30)) ? "yes" : "no",
90707ed82efSYazen Ghannam 				    (umc->umc_cap_hi & BIT(31)) ? "yes" : "no");
90807ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d All DIMMs support ECC: %s\n",
90907ed82efSYazen Ghannam 				i, (umc->umc_cfg & BIT(12)) ? "yes" : "no");
91007ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d x4 DIMMs present: %s\n",
91107ed82efSYazen Ghannam 				i, (umc->dimm_cfg & BIT(6)) ? "yes" : "no");
91207ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d x16 DIMMs present: %s\n",
91307ed82efSYazen Ghannam 				i, (umc->dimm_cfg & BIT(7)) ? "yes" : "no");
91407ed82efSYazen Ghannam 
91507ed82efSYazen Ghannam 		if (pvt->dram_type == MEM_LRDDR4) {
91607ed82efSYazen Ghannam 			amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ADDR_CFG, &tmp);
91707ed82efSYazen Ghannam 			edac_dbg(1, "UMC%d LRDIMM %dx rank multiply\n",
91807ed82efSYazen Ghannam 					i, 1 << ((tmp >> 4) & 0x3));
91907ed82efSYazen Ghannam 		}
92007ed82efSYazen Ghannam 
92107ed82efSYazen Ghannam 		debug_display_dimm_sizes_df(pvt, i);
92207ed82efSYazen Ghannam 	}
92307ed82efSYazen Ghannam 
92407ed82efSYazen Ghannam 	edac_dbg(1, "F0x104 (DRAM Hole Address): 0x%08x, base: 0x%08x\n",
92507ed82efSYazen Ghannam 		 pvt->dhar, dhar_base(pvt));
92607ed82efSYazen Ghannam }
92707ed82efSYazen Ghannam 
9282da11654SDoug Thompson /* Display and decode various NB registers for debug purposes. */
92907ed82efSYazen Ghannam static void __dump_misc_regs(struct amd64_pvt *pvt)
9302da11654SDoug Thompson {
931956b9ba1SJoe Perches 	edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
9322da11654SDoug Thompson 
933956b9ba1SJoe Perches 	edac_dbg(1, "  NB two channel DRAM capable: %s\n",
9345980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
93568798e17SBorislav Petkov 
936956b9ba1SJoe Perches 	edac_dbg(1, "  ECC capable: %s, ChipKill ECC capable: %s\n",
9375980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
9385980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
93968798e17SBorislav Petkov 
940d1ea71cdSBorislav Petkov 	debug_dump_dramcfg_low(pvt, pvt->dclr0, 0);
9412da11654SDoug Thompson 
942956b9ba1SJoe Perches 	edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
9432da11654SDoug Thompson 
944956b9ba1SJoe Perches 	edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n",
945bc21fa57SBorislav Petkov 		 pvt->dhar, dhar_base(pvt),
946a4b4bedcSBorislav Petkov 		 (pvt->fam == 0xf) ? k8_dhar_offset(pvt)
947bc21fa57SBorislav Petkov 				   : f10_dhar_offset(pvt));
9482da11654SDoug Thompson 
949d1ea71cdSBorislav Petkov 	debug_display_dimm_sizes(pvt, 0);
9504d796364SBorislav Petkov 
9514d796364SBorislav Petkov 	/* everything below this point is Fam10h and above */
952a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
9532da11654SDoug Thompson 		return;
9544d796364SBorislav Petkov 
955d1ea71cdSBorislav Petkov 	debug_display_dimm_sizes(pvt, 1);
9562da11654SDoug Thompson 
9578de1d91eSBorislav Petkov 	/* Only if NOT ganged does dclr1 have valid info */
95868798e17SBorislav Petkov 	if (!dct_ganging_enabled(pvt))
959d1ea71cdSBorislav Petkov 		debug_dump_dramcfg_low(pvt, pvt->dclr1, 1);
9602da11654SDoug Thompson }
9612da11654SDoug Thompson 
96207ed82efSYazen Ghannam /* Display and decode various NB registers for debug purposes. */
96307ed82efSYazen Ghannam static void dump_misc_regs(struct amd64_pvt *pvt)
96407ed82efSYazen Ghannam {
96507ed82efSYazen Ghannam 	if (pvt->umc)
96607ed82efSYazen Ghannam 		__dump_misc_regs_df(pvt);
96707ed82efSYazen Ghannam 	else
96807ed82efSYazen Ghannam 		__dump_misc_regs(pvt);
96907ed82efSYazen Ghannam 
97007ed82efSYazen Ghannam 	edac_dbg(1, "  DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
97107ed82efSYazen Ghannam 
9727835961dSYazen Ghannam 	amd64_info("using x%u syndromes.\n", pvt->ecc_sym_sz);
97307ed82efSYazen Ghannam }
97407ed82efSYazen Ghannam 
97594be4bffSDoug Thompson /*
97618b94f66SAravind Gopalakrishnan  * See BKDG, F2x[1,0][5C:40], F2[1,0][6C:60]
97794be4bffSDoug Thompson  */
97811c75eadSBorislav Petkov static void prep_chip_selects(struct amd64_pvt *pvt)
97994be4bffSDoug Thompson {
98018b94f66SAravind Gopalakrishnan 	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
98111c75eadSBorislav Petkov 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
98211c75eadSBorislav Petkov 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8;
983a597d2a5SAravind Gopalakrishnan 	} else if (pvt->fam == 0x15 && pvt->model == 0x30) {
98418b94f66SAravind Gopalakrishnan 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4;
98518b94f66SAravind Gopalakrishnan 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2;
986d971e28eSYazen Ghannam 	} else if (pvt->fam >= 0x17) {
987d971e28eSYazen Ghannam 		int umc;
988d971e28eSYazen Ghannam 
989d971e28eSYazen Ghannam 		for_each_umc(umc) {
990d971e28eSYazen Ghannam 			pvt->csels[umc].b_cnt = 4;
991d971e28eSYazen Ghannam 			pvt->csels[umc].m_cnt = 2;
992d971e28eSYazen Ghannam 		}
993d971e28eSYazen Ghannam 
9949d858bb1SBorislav Petkov 	} else {
99511c75eadSBorislav Petkov 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
99611c75eadSBorislav Petkov 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4;
9979d858bb1SBorislav Petkov 	}
99894be4bffSDoug Thompson }
99994be4bffSDoug Thompson 
1000d971e28eSYazen Ghannam static void read_umc_base_mask(struct amd64_pvt *pvt)
1001d971e28eSYazen Ghannam {
10027574729eSYazen Ghannam 	u32 umc_base_reg, umc_base_reg_sec;
10037574729eSYazen Ghannam 	u32 umc_mask_reg, umc_mask_reg_sec;
10047574729eSYazen Ghannam 	u32 base_reg, base_reg_sec;
10057574729eSYazen Ghannam 	u32 mask_reg, mask_reg_sec;
10067574729eSYazen Ghannam 	u32 *base, *base_sec;
10077574729eSYazen Ghannam 	u32 *mask, *mask_sec;
1008d971e28eSYazen Ghannam 	int cs, umc;
1009d971e28eSYazen Ghannam 
1010d971e28eSYazen Ghannam 	for_each_umc(umc) {
1011d971e28eSYazen Ghannam 		umc_base_reg = get_umc_base(umc) + UMCCH_BASE_ADDR;
10127574729eSYazen Ghannam 		umc_base_reg_sec = get_umc_base(umc) + UMCCH_BASE_ADDR_SEC;
1013d971e28eSYazen Ghannam 
1014d971e28eSYazen Ghannam 		for_each_chip_select(cs, umc, pvt) {
1015d971e28eSYazen Ghannam 			base = &pvt->csels[umc].csbases[cs];
10167574729eSYazen Ghannam 			base_sec = &pvt->csels[umc].csbases_sec[cs];
1017d971e28eSYazen Ghannam 
1018d971e28eSYazen Ghannam 			base_reg = umc_base_reg + (cs * 4);
10197574729eSYazen Ghannam 			base_reg_sec = umc_base_reg_sec + (cs * 4);
1020d971e28eSYazen Ghannam 
1021d971e28eSYazen Ghannam 			if (!amd_smn_read(pvt->mc_node_id, base_reg, base))
1022d971e28eSYazen Ghannam 				edac_dbg(0, "  DCSB%d[%d]=0x%08x reg: 0x%x\n",
1023d971e28eSYazen Ghannam 					 umc, cs, *base, base_reg);
10247574729eSYazen Ghannam 
10257574729eSYazen Ghannam 			if (!amd_smn_read(pvt->mc_node_id, base_reg_sec, base_sec))
10267574729eSYazen Ghannam 				edac_dbg(0, "    DCSB_SEC%d[%d]=0x%08x reg: 0x%x\n",
10277574729eSYazen Ghannam 					 umc, cs, *base_sec, base_reg_sec);
1028d971e28eSYazen Ghannam 		}
1029d971e28eSYazen Ghannam 
1030d971e28eSYazen Ghannam 		umc_mask_reg = get_umc_base(umc) + UMCCH_ADDR_MASK;
10317574729eSYazen Ghannam 		umc_mask_reg_sec = get_umc_base(umc) + UMCCH_ADDR_MASK_SEC;
1032d971e28eSYazen Ghannam 
1033d971e28eSYazen Ghannam 		for_each_chip_select_mask(cs, umc, pvt) {
1034d971e28eSYazen Ghannam 			mask = &pvt->csels[umc].csmasks[cs];
10357574729eSYazen Ghannam 			mask_sec = &pvt->csels[umc].csmasks_sec[cs];
1036d971e28eSYazen Ghannam 
1037d971e28eSYazen Ghannam 			mask_reg = umc_mask_reg + (cs * 4);
10387574729eSYazen Ghannam 			mask_reg_sec = umc_mask_reg_sec + (cs * 4);
1039d971e28eSYazen Ghannam 
1040d971e28eSYazen Ghannam 			if (!amd_smn_read(pvt->mc_node_id, mask_reg, mask))
1041d971e28eSYazen Ghannam 				edac_dbg(0, "  DCSM%d[%d]=0x%08x reg: 0x%x\n",
1042d971e28eSYazen Ghannam 					 umc, cs, *mask, mask_reg);
10437574729eSYazen Ghannam 
10447574729eSYazen Ghannam 			if (!amd_smn_read(pvt->mc_node_id, mask_reg_sec, mask_sec))
10457574729eSYazen Ghannam 				edac_dbg(0, "    DCSM_SEC%d[%d]=0x%08x reg: 0x%x\n",
10467574729eSYazen Ghannam 					 umc, cs, *mask_sec, mask_reg_sec);
1047d971e28eSYazen Ghannam 		}
1048d971e28eSYazen Ghannam 	}
1049d971e28eSYazen Ghannam }
1050d971e28eSYazen Ghannam 
105194be4bffSDoug Thompson /*
105211c75eadSBorislav Petkov  * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers
105394be4bffSDoug Thompson  */
1054b2b0c605SBorislav Petkov static void read_dct_base_mask(struct amd64_pvt *pvt)
105594be4bffSDoug Thompson {
1056d971e28eSYazen Ghannam 	int cs;
105794be4bffSDoug Thompson 
105811c75eadSBorislav Petkov 	prep_chip_selects(pvt);
105994be4bffSDoug Thompson 
1060d971e28eSYazen Ghannam 	if (pvt->umc)
1061d971e28eSYazen Ghannam 		return read_umc_base_mask(pvt);
1062b64ce7cdSYazen Ghannam 
106311c75eadSBorislav Petkov 	for_each_chip_select(cs, 0, pvt) {
1064d971e28eSYazen Ghannam 		int reg0   = DCSB0 + (cs * 4);
1065d971e28eSYazen Ghannam 		int reg1   = DCSB1 + (cs * 4);
106611c75eadSBorislav Petkov 		u32 *base0 = &pvt->csels[0].csbases[cs];
106711c75eadSBorislav Petkov 		u32 *base1 = &pvt->csels[1].csbases[cs];
1068b2b0c605SBorislav Petkov 
10697981a28fSAravind Gopalakrishnan 		if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0))
1070956b9ba1SJoe Perches 			edac_dbg(0, "  DCSB0[%d]=0x%08x reg: F2x%x\n",
107111c75eadSBorislav Petkov 				 cs, *base0, reg0);
107294be4bffSDoug Thompson 
10737981a28fSAravind Gopalakrishnan 		if (pvt->fam == 0xf)
107411c75eadSBorislav Petkov 			continue;
1075b2b0c605SBorislav Petkov 
10767981a28fSAravind Gopalakrishnan 		if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1))
1077956b9ba1SJoe Perches 			edac_dbg(0, "  DCSB1[%d]=0x%08x reg: F2x%x\n",
10787981a28fSAravind Gopalakrishnan 				 cs, *base1, (pvt->fam == 0x10) ? reg1
10797981a28fSAravind Gopalakrishnan 							: reg0);
108094be4bffSDoug Thompson 	}
108194be4bffSDoug Thompson 
108211c75eadSBorislav Petkov 	for_each_chip_select_mask(cs, 0, pvt) {
1083d971e28eSYazen Ghannam 		int reg0   = DCSM0 + (cs * 4);
1084d971e28eSYazen Ghannam 		int reg1   = DCSM1 + (cs * 4);
108511c75eadSBorislav Petkov 		u32 *mask0 = &pvt->csels[0].csmasks[cs];
108611c75eadSBorislav Petkov 		u32 *mask1 = &pvt->csels[1].csmasks[cs];
1087b2b0c605SBorislav Petkov 
10887981a28fSAravind Gopalakrishnan 		if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0))
1089956b9ba1SJoe Perches 			edac_dbg(0, "    DCSM0[%d]=0x%08x reg: F2x%x\n",
109011c75eadSBorislav Petkov 				 cs, *mask0, reg0);
109194be4bffSDoug Thompson 
10927981a28fSAravind Gopalakrishnan 		if (pvt->fam == 0xf)
109311c75eadSBorislav Petkov 			continue;
1094b2b0c605SBorislav Petkov 
10957981a28fSAravind Gopalakrishnan 		if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1))
1096956b9ba1SJoe Perches 			edac_dbg(0, "    DCSM1[%d]=0x%08x reg: F2x%x\n",
10977981a28fSAravind Gopalakrishnan 				 cs, *mask1, (pvt->fam == 0x10) ? reg1
10987981a28fSAravind Gopalakrishnan 							: reg0);
109994be4bffSDoug Thompson 	}
11006ba5dcdcSBorislav Petkov }
110194be4bffSDoug Thompson 
1102a597d2a5SAravind Gopalakrishnan static void determine_memory_type(struct amd64_pvt *pvt)
110394be4bffSDoug Thompson {
1104a597d2a5SAravind Gopalakrishnan 	u32 dram_ctrl, dcsm;
110594be4bffSDoug Thompson 
1106dcd01394SYazen Ghannam 	if (pvt->umc) {
1107dcd01394SYazen Ghannam 		if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(5))
1108dcd01394SYazen Ghannam 			pvt->dram_type = MEM_LRDDR4;
1109dcd01394SYazen Ghannam 		else if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(4))
1110dcd01394SYazen Ghannam 			pvt->dram_type = MEM_RDDR4;
1111dcd01394SYazen Ghannam 		else
1112dcd01394SYazen Ghannam 			pvt->dram_type = MEM_DDR4;
1113dcd01394SYazen Ghannam 		return;
1114dcd01394SYazen Ghannam 	}
1115dcd01394SYazen Ghannam 
1116a597d2a5SAravind Gopalakrishnan 	switch (pvt->fam) {
1117a597d2a5SAravind Gopalakrishnan 	case 0xf:
1118a597d2a5SAravind Gopalakrishnan 		if (pvt->ext_model >= K8_REV_F)
1119a597d2a5SAravind Gopalakrishnan 			goto ddr3;
1120a597d2a5SAravind Gopalakrishnan 
1121a597d2a5SAravind Gopalakrishnan 		pvt->dram_type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
1122a597d2a5SAravind Gopalakrishnan 		return;
1123a597d2a5SAravind Gopalakrishnan 
1124a597d2a5SAravind Gopalakrishnan 	case 0x10:
11256b4c0bdeSBorislav Petkov 		if (pvt->dchr0 & DDR3_MODE)
1126a597d2a5SAravind Gopalakrishnan 			goto ddr3;
1127a597d2a5SAravind Gopalakrishnan 
1128a597d2a5SAravind Gopalakrishnan 		pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
1129a597d2a5SAravind Gopalakrishnan 		return;
1130a597d2a5SAravind Gopalakrishnan 
1131a597d2a5SAravind Gopalakrishnan 	case 0x15:
1132a597d2a5SAravind Gopalakrishnan 		if (pvt->model < 0x60)
1133a597d2a5SAravind Gopalakrishnan 			goto ddr3;
1134a597d2a5SAravind Gopalakrishnan 
1135a597d2a5SAravind Gopalakrishnan 		/*
1136a597d2a5SAravind Gopalakrishnan 		 * Model 0x60h needs special handling:
1137a597d2a5SAravind Gopalakrishnan 		 *
1138a597d2a5SAravind Gopalakrishnan 		 * We use a Chip Select value of '0' to obtain dcsm.
1139a597d2a5SAravind Gopalakrishnan 		 * Theoretically, it is possible to populate LRDIMMs of different
1140a597d2a5SAravind Gopalakrishnan 		 * 'Rank' value on a DCT. But this is not the common case. So,
1141a597d2a5SAravind Gopalakrishnan 		 * it's reasonable to assume all DIMMs are going to be of same
1142a597d2a5SAravind Gopalakrishnan 		 * 'type' until proven otherwise.
1143a597d2a5SAravind Gopalakrishnan 		 */
1144a597d2a5SAravind Gopalakrishnan 		amd64_read_dct_pci_cfg(pvt, 0, DRAM_CONTROL, &dram_ctrl);
1145a597d2a5SAravind Gopalakrishnan 		dcsm = pvt->csels[0].csmasks[0];
1146a597d2a5SAravind Gopalakrishnan 
1147a597d2a5SAravind Gopalakrishnan 		if (((dram_ctrl >> 8) & 0x7) == 0x2)
1148a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_DDR4;
1149a597d2a5SAravind Gopalakrishnan 		else if (pvt->dclr0 & BIT(16))
1150a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_DDR3;
1151a597d2a5SAravind Gopalakrishnan 		else if (dcsm & 0x3)
1152a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_LRDDR3;
11536b4c0bdeSBorislav Petkov 		else
1154a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_RDDR3;
1155a597d2a5SAravind Gopalakrishnan 
1156a597d2a5SAravind Gopalakrishnan 		return;
1157a597d2a5SAravind Gopalakrishnan 
1158a597d2a5SAravind Gopalakrishnan 	case 0x16:
1159a597d2a5SAravind Gopalakrishnan 		goto ddr3;
1160a597d2a5SAravind Gopalakrishnan 
1161a597d2a5SAravind Gopalakrishnan 	default:
1162a597d2a5SAravind Gopalakrishnan 		WARN(1, KERN_ERR "%s: Family??? 0x%x\n", __func__, pvt->fam);
1163a597d2a5SAravind Gopalakrishnan 		pvt->dram_type = MEM_EMPTY;
116494be4bffSDoug Thompson 	}
1165a597d2a5SAravind Gopalakrishnan 	return;
116694be4bffSDoug Thompson 
1167a597d2a5SAravind Gopalakrishnan ddr3:
1168a597d2a5SAravind Gopalakrishnan 	pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
116994be4bffSDoug Thompson }
117094be4bffSDoug Thompson 
1171cb328507SBorislav Petkov /* Get the number of DCT channels the memory controller is using. */
1172ddff876dSDoug Thompson static int k8_early_channel_count(struct amd64_pvt *pvt)
1173ddff876dSDoug Thompson {
1174cb328507SBorislav Petkov 	int flag;
1175ddff876dSDoug Thompson 
11769f56da0eSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F)
1177ddff876dSDoug Thompson 		/* RevF (NPT) and later */
117841d8bfabSBorislav Petkov 		flag = pvt->dclr0 & WIDTH_128;
11799f56da0eSBorislav Petkov 	else
1180ddff876dSDoug Thompson 		/* RevE and earlier */
1181ddff876dSDoug Thompson 		flag = pvt->dclr0 & REVE_WIDTH_128;
1182ddff876dSDoug Thompson 
1183ddff876dSDoug Thompson 	/* not used */
1184ddff876dSDoug Thompson 	pvt->dclr1 = 0;
1185ddff876dSDoug Thompson 
1186ddff876dSDoug Thompson 	return (flag) ? 2 : 1;
1187ddff876dSDoug Thompson }
1188ddff876dSDoug Thompson 
118970046624SBorislav Petkov /* On F10h and later ErrAddr is MC4_ADDR[47:1] */
1190a4b4bedcSBorislav Petkov static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m)
1191ddff876dSDoug Thompson {
1192db970bd2SYazen Ghannam 	u16 mce_nid = topology_die_id(m->extcpu);
11932ec591acSBorislav Petkov 	struct mem_ctl_info *mci;
119470046624SBorislav Petkov 	u8 start_bit = 1;
119570046624SBorislav Petkov 	u8 end_bit   = 47;
11962ec591acSBorislav Petkov 	u64 addr;
11972ec591acSBorislav Petkov 
11982ec591acSBorislav Petkov 	mci = edac_mc_find(mce_nid);
11992ec591acSBorislav Petkov 	if (!mci)
12002ec591acSBorislav Petkov 		return 0;
12012ec591acSBorislav Petkov 
12022ec591acSBorislav Petkov 	pvt = mci->pvt_info;
120370046624SBorislav Petkov 
1204a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf) {
120570046624SBorislav Petkov 		start_bit = 3;
120670046624SBorislav Petkov 		end_bit   = 39;
120770046624SBorislav Petkov 	}
120870046624SBorislav Petkov 
120910ef6b0dSChen, Gong 	addr = m->addr & GENMASK_ULL(end_bit, start_bit);
1210c1ae6830SBorislav Petkov 
1211c1ae6830SBorislav Petkov 	/*
1212c1ae6830SBorislav Petkov 	 * Erratum 637 workaround
1213c1ae6830SBorislav Petkov 	 */
1214a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x15) {
1215c1ae6830SBorislav Petkov 		u64 cc6_base, tmp_addr;
1216c1ae6830SBorislav Petkov 		u32 tmp;
12178b84c8dfSDaniel J Blueman 		u8 intlv_en;
1218c1ae6830SBorislav Petkov 
121910ef6b0dSChen, Gong 		if ((addr & GENMASK_ULL(47, 24)) >> 24 != 0x00fdf7)
1220c1ae6830SBorislav Petkov 			return addr;
1221c1ae6830SBorislav Petkov 
1222c1ae6830SBorislav Petkov 
1223c1ae6830SBorislav Petkov 		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp);
1224c1ae6830SBorislav Petkov 		intlv_en = tmp >> 21 & 0x7;
1225c1ae6830SBorislav Petkov 
1226c1ae6830SBorislav Petkov 		/* add [47:27] + 3 trailing bits */
122710ef6b0dSChen, Gong 		cc6_base  = (tmp & GENMASK_ULL(20, 0)) << 3;
1228c1ae6830SBorislav Petkov 
1229c1ae6830SBorislav Petkov 		/* reverse and add DramIntlvEn */
1230c1ae6830SBorislav Petkov 		cc6_base |= intlv_en ^ 0x7;
1231c1ae6830SBorislav Petkov 
1232c1ae6830SBorislav Petkov 		/* pin at [47:24] */
1233c1ae6830SBorislav Petkov 		cc6_base <<= 24;
1234c1ae6830SBorislav Petkov 
1235c1ae6830SBorislav Petkov 		if (!intlv_en)
123610ef6b0dSChen, Gong 			return cc6_base | (addr & GENMASK_ULL(23, 0));
1237c1ae6830SBorislav Petkov 
1238c1ae6830SBorislav Petkov 		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp);
1239c1ae6830SBorislav Petkov 
1240c1ae6830SBorislav Petkov 							/* faster log2 */
124110ef6b0dSChen, Gong 		tmp_addr  = (addr & GENMASK_ULL(23, 12)) << __fls(intlv_en + 1);
1242c1ae6830SBorislav Petkov 
1243c1ae6830SBorislav Petkov 		/* OR DramIntlvSel into bits [14:12] */
124410ef6b0dSChen, Gong 		tmp_addr |= (tmp & GENMASK_ULL(23, 21)) >> 9;
1245c1ae6830SBorislav Petkov 
1246c1ae6830SBorislav Petkov 		/* add remaining [11:0] bits from original MC4_ADDR */
124710ef6b0dSChen, Gong 		tmp_addr |= addr & GENMASK_ULL(11, 0);
1248c1ae6830SBorislav Petkov 
1249c1ae6830SBorislav Petkov 		return cc6_base | tmp_addr;
1250c1ae6830SBorislav Petkov 	}
1251c1ae6830SBorislav Petkov 
1252c1ae6830SBorislav Petkov 	return addr;
1253ddff876dSDoug Thompson }
1254ddff876dSDoug Thompson 
1255e2c0bffeSDaniel J Blueman static struct pci_dev *pci_get_related_function(unsigned int vendor,
1256e2c0bffeSDaniel J Blueman 						unsigned int device,
1257e2c0bffeSDaniel J Blueman 						struct pci_dev *related)
1258e2c0bffeSDaniel J Blueman {
1259e2c0bffeSDaniel J Blueman 	struct pci_dev *dev = NULL;
1260e2c0bffeSDaniel J Blueman 
1261e2c0bffeSDaniel J Blueman 	while ((dev = pci_get_device(vendor, device, dev))) {
1262e2c0bffeSDaniel J Blueman 		if (pci_domain_nr(dev->bus) == pci_domain_nr(related->bus) &&
1263e2c0bffeSDaniel J Blueman 		    (dev->bus->number == related->bus->number) &&
1264e2c0bffeSDaniel J Blueman 		    (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn)))
1265e2c0bffeSDaniel J Blueman 			break;
1266e2c0bffeSDaniel J Blueman 	}
1267e2c0bffeSDaniel J Blueman 
1268e2c0bffeSDaniel J Blueman 	return dev;
1269e2c0bffeSDaniel J Blueman }
1270e2c0bffeSDaniel J Blueman 
12717f19bf75SBorislav Petkov static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
1272ddff876dSDoug Thompson {
1273e2c0bffeSDaniel J Blueman 	struct amd_northbridge *nb;
127418b94f66SAravind Gopalakrishnan 	struct pci_dev *f1 = NULL;
127518b94f66SAravind Gopalakrishnan 	unsigned int pci_func;
127671d2a32eSBorislav Petkov 	int off = range << 3;
1277e2c0bffeSDaniel J Blueman 	u32 llim;
1278ddff876dSDoug Thompson 
12797f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off,  &pvt->ranges[range].base.lo);
12807f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo);
1281ddff876dSDoug Thompson 
128218b94f66SAravind Gopalakrishnan 	if (pvt->fam == 0xf)
12837f19bf75SBorislav Petkov 		return;
1284ddff876dSDoug Thompson 
12857f19bf75SBorislav Petkov 	if (!dram_rw(pvt, range))
12867f19bf75SBorislav Petkov 		return;
1287ddff876dSDoug Thompson 
12887f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off,  &pvt->ranges[range].base.hi);
12897f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi);
1290f08e457cSBorislav Petkov 
1291e2c0bffeSDaniel J Blueman 	/* F15h: factor in CC6 save area by reading dst node's limit reg */
129218b94f66SAravind Gopalakrishnan 	if (pvt->fam != 0x15)
1293e2c0bffeSDaniel J Blueman 		return;
1294f08e457cSBorislav Petkov 
1295e2c0bffeSDaniel J Blueman 	nb = node_to_amd_nb(dram_dst_node(pvt, range));
1296e2c0bffeSDaniel J Blueman 	if (WARN_ON(!nb))
1297e2c0bffeSDaniel J Blueman 		return;
1298e2c0bffeSDaniel J Blueman 
1299a597d2a5SAravind Gopalakrishnan 	if (pvt->model == 0x60)
1300a597d2a5SAravind Gopalakrishnan 		pci_func = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1;
1301a597d2a5SAravind Gopalakrishnan 	else if (pvt->model == 0x30)
1302a597d2a5SAravind Gopalakrishnan 		pci_func = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1;
1303a597d2a5SAravind Gopalakrishnan 	else
1304a597d2a5SAravind Gopalakrishnan 		pci_func = PCI_DEVICE_ID_AMD_15H_NB_F1;
130518b94f66SAravind Gopalakrishnan 
130618b94f66SAravind Gopalakrishnan 	f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc);
1307f08e457cSBorislav Petkov 	if (WARN_ON(!f1))
1308f08e457cSBorislav Petkov 		return;
1309f08e457cSBorislav Petkov 
1310f08e457cSBorislav Petkov 	amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim);
1311f08e457cSBorislav Petkov 
131210ef6b0dSChen, Gong 	pvt->ranges[range].lim.lo &= GENMASK_ULL(15, 0);
1313f08e457cSBorislav Petkov 
1314f08e457cSBorislav Petkov 				    /* {[39:27],111b} */
1315f08e457cSBorislav Petkov 	pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16;
1316f08e457cSBorislav Petkov 
131710ef6b0dSChen, Gong 	pvt->ranges[range].lim.hi &= GENMASK_ULL(7, 0);
1318f08e457cSBorislav Petkov 
1319f08e457cSBorislav Petkov 				    /* [47:40] */
1320f08e457cSBorislav Petkov 	pvt->ranges[range].lim.hi |= llim >> 13;
1321f08e457cSBorislav Petkov 
1322f08e457cSBorislav Petkov 	pci_dev_put(f1);
1323f08e457cSBorislav Petkov }
1324ddff876dSDoug Thompson 
1325f192c7b1SBorislav Petkov static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
132633ca0643SBorislav Petkov 				    struct err_info *err)
1327ddff876dSDoug Thompson {
1328f192c7b1SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
1329ddff876dSDoug Thompson 
133033ca0643SBorislav Petkov 	error_address_to_page_and_offset(sys_addr, err);
1331ab5a503cSMauro Carvalho Chehab 
1332ab5a503cSMauro Carvalho Chehab 	/*
1333ab5a503cSMauro Carvalho Chehab 	 * Find out which node the error address belongs to. This may be
1334ab5a503cSMauro Carvalho Chehab 	 * different from the node that detected the error.
1335ab5a503cSMauro Carvalho Chehab 	 */
133633ca0643SBorislav Petkov 	err->src_mci = find_mc_by_sys_addr(mci, sys_addr);
133733ca0643SBorislav Petkov 	if (!err->src_mci) {
1338ab5a503cSMauro Carvalho Chehab 		amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
1339ab5a503cSMauro Carvalho Chehab 			     (unsigned long)sys_addr);
134033ca0643SBorislav Petkov 		err->err_code = ERR_NODE;
1341ab5a503cSMauro Carvalho Chehab 		return;
1342ab5a503cSMauro Carvalho Chehab 	}
1343ab5a503cSMauro Carvalho Chehab 
1344ab5a503cSMauro Carvalho Chehab 	/* Now map the sys_addr to a CSROW */
134533ca0643SBorislav Petkov 	err->csrow = sys_addr_to_csrow(err->src_mci, sys_addr);
134633ca0643SBorislav Petkov 	if (err->csrow < 0) {
134733ca0643SBorislav Petkov 		err->err_code = ERR_CSROW;
1348ab5a503cSMauro Carvalho Chehab 		return;
1349ab5a503cSMauro Carvalho Chehab 	}
1350ab5a503cSMauro Carvalho Chehab 
1351ddff876dSDoug Thompson 	/* CHIPKILL enabled */
1352f192c7b1SBorislav Petkov 	if (pvt->nbcfg & NBCFG_CHIPKILL) {
135333ca0643SBorislav Petkov 		err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
135433ca0643SBorislav Petkov 		if (err->channel < 0) {
1355ddff876dSDoug Thompson 			/*
1356ddff876dSDoug Thompson 			 * Syndrome didn't map, so we don't know which of the
1357ddff876dSDoug Thompson 			 * 2 DIMMs is in error. So we need to ID 'both' of them
1358ddff876dSDoug Thompson 			 * as suspect.
1359ddff876dSDoug Thompson 			 */
136033ca0643SBorislav Petkov 			amd64_mc_warn(err->src_mci, "unknown syndrome 0x%04x - "
1361ab5a503cSMauro Carvalho Chehab 				      "possible error reporting race\n",
136233ca0643SBorislav Petkov 				      err->syndrome);
136333ca0643SBorislav Petkov 			err->err_code = ERR_CHANNEL;
1364ddff876dSDoug Thompson 			return;
1365ddff876dSDoug Thompson 		}
1366ddff876dSDoug Thompson 	} else {
1367ddff876dSDoug Thompson 		/*
1368ddff876dSDoug Thompson 		 * non-chipkill ecc mode
1369ddff876dSDoug Thompson 		 *
1370ddff876dSDoug Thompson 		 * The k8 documentation is unclear about how to determine the
1371ddff876dSDoug Thompson 		 * channel number when using non-chipkill memory.  This method
1372ddff876dSDoug Thompson 		 * was obtained from email communication with someone at AMD.
1373ddff876dSDoug Thompson 		 * (Wish the email was placed in this comment - norsk)
1374ddff876dSDoug Thompson 		 */
137533ca0643SBorislav Petkov 		err->channel = ((sys_addr & BIT(3)) != 0);
1376ddff876dSDoug Thompson 	}
1377ddff876dSDoug Thompson }
1378ddff876dSDoug Thompson 
137941d8bfabSBorislav Petkov static int ddr2_cs_size(unsigned i, bool dct_width)
1380ddff876dSDoug Thompson {
138141d8bfabSBorislav Petkov 	unsigned shift = 0;
1382ddff876dSDoug Thompson 
138341d8bfabSBorislav Petkov 	if (i <= 2)
138441d8bfabSBorislav Petkov 		shift = i;
138541d8bfabSBorislav Petkov 	else if (!(i & 0x1))
138641d8bfabSBorislav Petkov 		shift = i >> 1;
13871433eb99SBorislav Petkov 	else
138841d8bfabSBorislav Petkov 		shift = (i + 1) >> 1;
1389ddff876dSDoug Thompson 
139041d8bfabSBorislav Petkov 	return 128 << (shift + !!dct_width);
139141d8bfabSBorislav Petkov }
139241d8bfabSBorislav Petkov 
139341d8bfabSBorislav Petkov static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1394a597d2a5SAravind Gopalakrishnan 				  unsigned cs_mode, int cs_mask_nr)
139541d8bfabSBorislav Petkov {
139641d8bfabSBorislav Petkov 	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
139741d8bfabSBorislav Petkov 
139841d8bfabSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F) {
139941d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 11);
140041d8bfabSBorislav Petkov 		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
140141d8bfabSBorislav Petkov 	}
140241d8bfabSBorislav Petkov 	else if (pvt->ext_model >= K8_REV_D) {
140311b0a314SBorislav Petkov 		unsigned diff;
140441d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 10);
140541d8bfabSBorislav Petkov 
140611b0a314SBorislav Petkov 		/*
140711b0a314SBorislav Petkov 		 * the below calculation, besides trying to win an obfuscated C
140811b0a314SBorislav Petkov 		 * contest, maps cs_mode values to DIMM chip select sizes. The
140911b0a314SBorislav Petkov 		 * mappings are:
141011b0a314SBorislav Petkov 		 *
141111b0a314SBorislav Petkov 		 * cs_mode	CS size (mb)
141211b0a314SBorislav Petkov 		 * =======	============
141311b0a314SBorislav Petkov 		 * 0		32
141411b0a314SBorislav Petkov 		 * 1		64
141511b0a314SBorislav Petkov 		 * 2		128
141611b0a314SBorislav Petkov 		 * 3		128
141711b0a314SBorislav Petkov 		 * 4		256
141811b0a314SBorislav Petkov 		 * 5		512
141911b0a314SBorislav Petkov 		 * 6		256
142011b0a314SBorislav Petkov 		 * 7		512
142111b0a314SBorislav Petkov 		 * 8		1024
142211b0a314SBorislav Petkov 		 * 9		1024
142311b0a314SBorislav Petkov 		 * 10		2048
142411b0a314SBorislav Petkov 		 *
142511b0a314SBorislav Petkov 		 * Basically, it calculates a value with which to shift the
142611b0a314SBorislav Petkov 		 * smallest CS size of 32MB.
142711b0a314SBorislav Petkov 		 *
142811b0a314SBorislav Petkov 		 * ddr[23]_cs_size have a similar purpose.
142911b0a314SBorislav Petkov 		 */
143011b0a314SBorislav Petkov 		diff = cs_mode/3 + (unsigned)(cs_mode > 5);
143111b0a314SBorislav Petkov 
143211b0a314SBorislav Petkov 		return 32 << (cs_mode - diff);
143341d8bfabSBorislav Petkov 	}
143441d8bfabSBorislav Petkov 	else {
143541d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 6);
143641d8bfabSBorislav Petkov 		return 32 << cs_mode;
143741d8bfabSBorislav Petkov 	}
1438ddff876dSDoug Thompson }
1439ddff876dSDoug Thompson 
14401afd3c98SDoug Thompson /*
14411afd3c98SDoug Thompson  * Get the number of DCT channels in use.
14421afd3c98SDoug Thompson  *
14431afd3c98SDoug Thompson  * Return:
14441afd3c98SDoug Thompson  *	number of Memory Channels in operation
14451afd3c98SDoug Thompson  * Pass back:
14461afd3c98SDoug Thompson  *	contents of the DCL0_LOW register
14471afd3c98SDoug Thompson  */
14487d20d14dSBorislav Petkov static int f1x_early_channel_count(struct amd64_pvt *pvt)
14491afd3c98SDoug Thompson {
14506ba5dcdcSBorislav Petkov 	int i, j, channels = 0;
1451ddff876dSDoug Thompson 
14527d20d14dSBorislav Petkov 	/* On F10h, if we are in 128 bit mode, then we are using 2 channels */
1453a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10 && (pvt->dclr0 & WIDTH_128))
14547d20d14dSBorislav Petkov 		return 2;
14551afd3c98SDoug Thompson 
14561afd3c98SDoug Thompson 	/*
1457d16149e8SBorislav Petkov 	 * Need to check if in unganged mode: In such, there are 2 channels,
1458d16149e8SBorislav Petkov 	 * but they are not in 128 bit mode and thus the above 'dclr0' status
1459d16149e8SBorislav Petkov 	 * bit will be OFF.
14601afd3c98SDoug Thompson 	 *
14611afd3c98SDoug Thompson 	 * Need to check DCT0[0] and DCT1[0] to see if only one of them has
14621afd3c98SDoug Thompson 	 * their CSEnable bit on. If so, then SINGLE DIMM case.
14631afd3c98SDoug Thompson 	 */
1464956b9ba1SJoe Perches 	edac_dbg(0, "Data width is not 128 bits - need more decoding\n");
14651afd3c98SDoug Thompson 
14661afd3c98SDoug Thompson 	/*
14671afd3c98SDoug Thompson 	 * Check DRAM Bank Address Mapping values for each DIMM to see if there
14681afd3c98SDoug Thompson 	 * is more than just one DIMM present in unganged mode. Need to check
14691afd3c98SDoug Thompson 	 * both controllers since DIMMs can be placed in either one.
14701afd3c98SDoug Thompson 	 */
1471525a1b20SBorislav Petkov 	for (i = 0; i < 2; i++) {
1472525a1b20SBorislav Petkov 		u32 dbam = (i ? pvt->dbam1 : pvt->dbam0);
14731afd3c98SDoug Thompson 
147457a30854SWan Wei 		for (j = 0; j < 4; j++) {
147557a30854SWan Wei 			if (DBAM_DIMM(j, dbam) > 0) {
14761afd3c98SDoug Thompson 				channels++;
147757a30854SWan Wei 				break;
14781afd3c98SDoug Thompson 			}
147957a30854SWan Wei 		}
148057a30854SWan Wei 	}
14811afd3c98SDoug Thompson 
1482d16149e8SBorislav Petkov 	if (channels > 2)
1483d16149e8SBorislav Petkov 		channels = 2;
1484d16149e8SBorislav Petkov 
148524f9a7feSBorislav Petkov 	amd64_info("MCT channel count: %d\n", channels);
14861afd3c98SDoug Thompson 
14871afd3c98SDoug Thompson 	return channels;
14881afd3c98SDoug Thompson }
14891afd3c98SDoug Thompson 
1490f1cbbec9SYazen Ghannam static int f17_early_channel_count(struct amd64_pvt *pvt)
1491f1cbbec9SYazen Ghannam {
1492f1cbbec9SYazen Ghannam 	int i, channels = 0;
1493f1cbbec9SYazen Ghannam 
1494f1cbbec9SYazen Ghannam 	/* SDP Control bit 31 (SdpInit) is clear for unused UMC channels */
14954d30d2bcSYazen Ghannam 	for_each_umc(i)
1496f1cbbec9SYazen Ghannam 		channels += !!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT);
1497f1cbbec9SYazen Ghannam 
1498f1cbbec9SYazen Ghannam 	amd64_info("MCT channel count: %d\n", channels);
1499f1cbbec9SYazen Ghannam 
1500f1cbbec9SYazen Ghannam 	return channels;
1501f1cbbec9SYazen Ghannam }
1502f1cbbec9SYazen Ghannam 
150341d8bfabSBorislav Petkov static int ddr3_cs_size(unsigned i, bool dct_width)
15041afd3c98SDoug Thompson {
150541d8bfabSBorislav Petkov 	unsigned shift = 0;
150641d8bfabSBorislav Petkov 	int cs_size = 0;
150741d8bfabSBorislav Petkov 
150841d8bfabSBorislav Petkov 	if (i == 0 || i == 3 || i == 4)
150941d8bfabSBorislav Petkov 		cs_size = -1;
151041d8bfabSBorislav Petkov 	else if (i <= 2)
151141d8bfabSBorislav Petkov 		shift = i;
151241d8bfabSBorislav Petkov 	else if (i == 12)
151341d8bfabSBorislav Petkov 		shift = 7;
151441d8bfabSBorislav Petkov 	else if (!(i & 0x1))
151541d8bfabSBorislav Petkov 		shift = i >> 1;
151641d8bfabSBorislav Petkov 	else
151741d8bfabSBorislav Petkov 		shift = (i + 1) >> 1;
151841d8bfabSBorislav Petkov 
151941d8bfabSBorislav Petkov 	if (cs_size != -1)
152041d8bfabSBorislav Petkov 		cs_size = (128 * (1 << !!dct_width)) << shift;
152141d8bfabSBorislav Petkov 
152241d8bfabSBorislav Petkov 	return cs_size;
152341d8bfabSBorislav Petkov }
152441d8bfabSBorislav Petkov 
1525a597d2a5SAravind Gopalakrishnan static int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply)
1526a597d2a5SAravind Gopalakrishnan {
1527a597d2a5SAravind Gopalakrishnan 	unsigned shift = 0;
1528a597d2a5SAravind Gopalakrishnan 	int cs_size = 0;
1529a597d2a5SAravind Gopalakrishnan 
1530a597d2a5SAravind Gopalakrishnan 	if (i < 4 || i == 6)
1531a597d2a5SAravind Gopalakrishnan 		cs_size = -1;
1532a597d2a5SAravind Gopalakrishnan 	else if (i == 12)
1533a597d2a5SAravind Gopalakrishnan 		shift = 7;
1534a597d2a5SAravind Gopalakrishnan 	else if (!(i & 0x1))
1535a597d2a5SAravind Gopalakrishnan 		shift = i >> 1;
1536a597d2a5SAravind Gopalakrishnan 	else
1537a597d2a5SAravind Gopalakrishnan 		shift = (i + 1) >> 1;
1538a597d2a5SAravind Gopalakrishnan 
1539a597d2a5SAravind Gopalakrishnan 	if (cs_size != -1)
1540a597d2a5SAravind Gopalakrishnan 		cs_size = rank_multiply * (128 << shift);
1541a597d2a5SAravind Gopalakrishnan 
1542a597d2a5SAravind Gopalakrishnan 	return cs_size;
1543a597d2a5SAravind Gopalakrishnan }
1544a597d2a5SAravind Gopalakrishnan 
1545a597d2a5SAravind Gopalakrishnan static int ddr4_cs_size(unsigned i)
1546a597d2a5SAravind Gopalakrishnan {
1547a597d2a5SAravind Gopalakrishnan 	int cs_size = 0;
1548a597d2a5SAravind Gopalakrishnan 
1549a597d2a5SAravind Gopalakrishnan 	if (i == 0)
1550a597d2a5SAravind Gopalakrishnan 		cs_size = -1;
1551a597d2a5SAravind Gopalakrishnan 	else if (i == 1)
1552a597d2a5SAravind Gopalakrishnan 		cs_size = 1024;
1553a597d2a5SAravind Gopalakrishnan 	else
1554a597d2a5SAravind Gopalakrishnan 		/* Min cs_size = 1G */
1555a597d2a5SAravind Gopalakrishnan 		cs_size = 1024 * (1 << (i >> 1));
1556a597d2a5SAravind Gopalakrishnan 
1557a597d2a5SAravind Gopalakrishnan 	return cs_size;
1558a597d2a5SAravind Gopalakrishnan }
1559a597d2a5SAravind Gopalakrishnan 
156041d8bfabSBorislav Petkov static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1561a597d2a5SAravind Gopalakrishnan 				   unsigned cs_mode, int cs_mask_nr)
156241d8bfabSBorislav Petkov {
156341d8bfabSBorislav Petkov 	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
156441d8bfabSBorislav Petkov 
156541d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 11);
15661433eb99SBorislav Petkov 
15671433eb99SBorislav Petkov 	if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
156841d8bfabSBorislav Petkov 		return ddr3_cs_size(cs_mode, dclr & WIDTH_128);
15691433eb99SBorislav Petkov 	else
157041d8bfabSBorislav Petkov 		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
157141d8bfabSBorislav Petkov }
15721433eb99SBorislav Petkov 
157341d8bfabSBorislav Petkov /*
157441d8bfabSBorislav Petkov  * F15h supports only 64bit DCT interfaces
157541d8bfabSBorislav Petkov  */
157641d8bfabSBorislav Petkov static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1577a597d2a5SAravind Gopalakrishnan 				   unsigned cs_mode, int cs_mask_nr)
157841d8bfabSBorislav Petkov {
157941d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 12);
158041d8bfabSBorislav Petkov 
158141d8bfabSBorislav Petkov 	return ddr3_cs_size(cs_mode, false);
15821afd3c98SDoug Thompson }
15831afd3c98SDoug Thompson 
1584a597d2a5SAravind Gopalakrishnan /* F15h M60h supports DDR4 mapping as well.. */
1585a597d2a5SAravind Gopalakrishnan static int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1586a597d2a5SAravind Gopalakrishnan 					unsigned cs_mode, int cs_mask_nr)
1587a597d2a5SAravind Gopalakrishnan {
1588a597d2a5SAravind Gopalakrishnan 	int cs_size;
1589a597d2a5SAravind Gopalakrishnan 	u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr];
1590a597d2a5SAravind Gopalakrishnan 
1591a597d2a5SAravind Gopalakrishnan 	WARN_ON(cs_mode > 12);
1592a597d2a5SAravind Gopalakrishnan 
1593a597d2a5SAravind Gopalakrishnan 	if (pvt->dram_type == MEM_DDR4) {
1594a597d2a5SAravind Gopalakrishnan 		if (cs_mode > 9)
1595a597d2a5SAravind Gopalakrishnan 			return -1;
1596a597d2a5SAravind Gopalakrishnan 
1597a597d2a5SAravind Gopalakrishnan 		cs_size = ddr4_cs_size(cs_mode);
1598a597d2a5SAravind Gopalakrishnan 	} else if (pvt->dram_type == MEM_LRDDR3) {
1599a597d2a5SAravind Gopalakrishnan 		unsigned rank_multiply = dcsm & 0xf;
1600a597d2a5SAravind Gopalakrishnan 
1601a597d2a5SAravind Gopalakrishnan 		if (rank_multiply == 3)
1602a597d2a5SAravind Gopalakrishnan 			rank_multiply = 4;
1603a597d2a5SAravind Gopalakrishnan 		cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply);
1604a597d2a5SAravind Gopalakrishnan 	} else {
1605a597d2a5SAravind Gopalakrishnan 		/* Minimum cs size is 512mb for F15hM60h*/
1606a597d2a5SAravind Gopalakrishnan 		if (cs_mode == 0x1)
1607a597d2a5SAravind Gopalakrishnan 			return -1;
1608a597d2a5SAravind Gopalakrishnan 
1609a597d2a5SAravind Gopalakrishnan 		cs_size = ddr3_cs_size(cs_mode, false);
1610a597d2a5SAravind Gopalakrishnan 	}
1611a597d2a5SAravind Gopalakrishnan 
1612a597d2a5SAravind Gopalakrishnan 	return cs_size;
1613a597d2a5SAravind Gopalakrishnan }
1614a597d2a5SAravind Gopalakrishnan 
161594c1acf2SAravind Gopalakrishnan /*
161618b94f66SAravind Gopalakrishnan  * F16h and F15h model 30h have only limited cs_modes.
161794c1acf2SAravind Gopalakrishnan  */
161894c1acf2SAravind Gopalakrishnan static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1619a597d2a5SAravind Gopalakrishnan 				unsigned cs_mode, int cs_mask_nr)
162094c1acf2SAravind Gopalakrishnan {
162194c1acf2SAravind Gopalakrishnan 	WARN_ON(cs_mode > 12);
162294c1acf2SAravind Gopalakrishnan 
162394c1acf2SAravind Gopalakrishnan 	if (cs_mode == 6 || cs_mode == 8 ||
162494c1acf2SAravind Gopalakrishnan 	    cs_mode == 9 || cs_mode == 12)
162594c1acf2SAravind Gopalakrishnan 		return -1;
162694c1acf2SAravind Gopalakrishnan 	else
162794c1acf2SAravind Gopalakrishnan 		return ddr3_cs_size(cs_mode, false);
162894c1acf2SAravind Gopalakrishnan }
162994c1acf2SAravind Gopalakrishnan 
1630e53a3b26SYazen Ghannam static int f17_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc,
1631f1cbbec9SYazen Ghannam 				    unsigned int cs_mode, int csrow_nr)
1632f1cbbec9SYazen Ghannam {
1633e53a3b26SYazen Ghannam 	u32 addr_mask_orig, addr_mask_deinterleaved;
1634e53a3b26SYazen Ghannam 	u32 msb, weight, num_zero_bits;
1635e53a3b26SYazen Ghannam 	int dimm, size = 0;
1636f1cbbec9SYazen Ghannam 
1637e53a3b26SYazen Ghannam 	/* No Chip Selects are enabled. */
1638e53a3b26SYazen Ghannam 	if (!cs_mode)
1639e53a3b26SYazen Ghannam 		return size;
1640e53a3b26SYazen Ghannam 
1641e53a3b26SYazen Ghannam 	/* Requested size of an even CS but none are enabled. */
1642e53a3b26SYazen Ghannam 	if (!(cs_mode & CS_EVEN) && !(csrow_nr & 1))
1643e53a3b26SYazen Ghannam 		return size;
1644e53a3b26SYazen Ghannam 
1645e53a3b26SYazen Ghannam 	/* Requested size of an odd CS but none are enabled. */
1646e53a3b26SYazen Ghannam 	if (!(cs_mode & CS_ODD) && (csrow_nr & 1))
1647e53a3b26SYazen Ghannam 		return size;
1648e53a3b26SYazen Ghannam 
1649e53a3b26SYazen Ghannam 	/*
1650e53a3b26SYazen Ghannam 	 * There is one mask per DIMM, and two Chip Selects per DIMM.
1651e53a3b26SYazen Ghannam 	 *	CS0 and CS1 -> DIMM0
1652e53a3b26SYazen Ghannam 	 *	CS2 and CS3 -> DIMM1
1653e53a3b26SYazen Ghannam 	 */
1654e53a3b26SYazen Ghannam 	dimm = csrow_nr >> 1;
1655e53a3b26SYazen Ghannam 
165681f5090dSYazen Ghannam 	/* Asymmetric dual-rank DIMM support. */
165781f5090dSYazen Ghannam 	if ((csrow_nr & 1) && (cs_mode & CS_ODD_SECONDARY))
165881f5090dSYazen Ghannam 		addr_mask_orig = pvt->csels[umc].csmasks_sec[dimm];
165981f5090dSYazen Ghannam 	else
1660e53a3b26SYazen Ghannam 		addr_mask_orig = pvt->csels[umc].csmasks[dimm];
1661e53a3b26SYazen Ghannam 
1662e53a3b26SYazen Ghannam 	/*
1663e53a3b26SYazen Ghannam 	 * The number of zero bits in the mask is equal to the number of bits
1664e53a3b26SYazen Ghannam 	 * in a full mask minus the number of bits in the current mask.
1665e53a3b26SYazen Ghannam 	 *
1666e53a3b26SYazen Ghannam 	 * The MSB is the number of bits in the full mask because BIT[0] is
1667e53a3b26SYazen Ghannam 	 * always 0.
1668e53a3b26SYazen Ghannam 	 */
1669e53a3b26SYazen Ghannam 	msb = fls(addr_mask_orig) - 1;
1670e53a3b26SYazen Ghannam 	weight = hweight_long(addr_mask_orig);
1671e53a3b26SYazen Ghannam 	num_zero_bits = msb - weight;
1672e53a3b26SYazen Ghannam 
1673e53a3b26SYazen Ghannam 	/* Take the number of zero bits off from the top of the mask. */
1674e53a3b26SYazen Ghannam 	addr_mask_deinterleaved = GENMASK_ULL(msb - num_zero_bits, 1);
1675e53a3b26SYazen Ghannam 
1676e53a3b26SYazen Ghannam 	edac_dbg(1, "CS%d DIMM%d AddrMasks:\n", csrow_nr, dimm);
1677e53a3b26SYazen Ghannam 	edac_dbg(1, "  Original AddrMask: 0x%x\n", addr_mask_orig);
1678e53a3b26SYazen Ghannam 	edac_dbg(1, "  Deinterleaved AddrMask: 0x%x\n", addr_mask_deinterleaved);
1679f1cbbec9SYazen Ghannam 
1680f1cbbec9SYazen Ghannam 	/* Register [31:1] = Address [39:9]. Size is in kBs here. */
1681e53a3b26SYazen Ghannam 	size = (addr_mask_deinterleaved >> 2) + 1;
1682f1cbbec9SYazen Ghannam 
1683f1cbbec9SYazen Ghannam 	/* Return size in MBs. */
1684f1cbbec9SYazen Ghannam 	return size >> 10;
1685f1cbbec9SYazen Ghannam }
1686f1cbbec9SYazen Ghannam 
16875a5d2371SBorislav Petkov static void read_dram_ctl_register(struct amd64_pvt *pvt)
16886163b5d4SDoug Thompson {
16896163b5d4SDoug Thompson 
1690a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
16915a5d2371SBorislav Petkov 		return;
16925a5d2371SBorislav Petkov 
16937981a28fSAravind Gopalakrishnan 	if (!amd64_read_pci_cfg(pvt->F2, DCT_SEL_LO, &pvt->dct_sel_lo)) {
1694956b9ba1SJoe Perches 		edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
169578da121eSBorislav Petkov 			 pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
16966163b5d4SDoug Thompson 
1697956b9ba1SJoe Perches 		edac_dbg(0, "  DCTs operate in %s mode\n",
16985a5d2371SBorislav Petkov 			 (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
16996163b5d4SDoug Thompson 
170072381bd5SBorislav Petkov 		if (!dct_ganging_enabled(pvt))
1701956b9ba1SJoe Perches 			edac_dbg(0, "  Address range split per DCT: %s\n",
170272381bd5SBorislav Petkov 				 (dct_high_range_enabled(pvt) ? "yes" : "no"));
170372381bd5SBorislav Petkov 
1704956b9ba1SJoe Perches 		edac_dbg(0, "  data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n",
170572381bd5SBorislav Petkov 			 (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
170672381bd5SBorislav Petkov 			 (dct_memory_cleared(pvt) ? "yes" : "no"));
170772381bd5SBorislav Petkov 
1708956b9ba1SJoe Perches 		edac_dbg(0, "  channel interleave: %s, "
170978da121eSBorislav Petkov 			 "interleave bits selector: 0x%x\n",
171072381bd5SBorislav Petkov 			 (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
17116163b5d4SDoug Thompson 			 dct_sel_interleave_addr(pvt));
17126163b5d4SDoug Thompson 	}
17136163b5d4SDoug Thompson 
17147981a28fSAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F2, DCT_SEL_HI, &pvt->dct_sel_hi);
17156163b5d4SDoug Thompson }
17166163b5d4SDoug Thompson 
1717f71d0a05SDoug Thompson /*
171818b94f66SAravind Gopalakrishnan  * Determine channel (DCT) based on the interleaving mode (see F15h M30h BKDG,
171918b94f66SAravind Gopalakrishnan  * 2.10.12 Memory Interleaving Modes).
172018b94f66SAravind Gopalakrishnan  */
172118b94f66SAravind Gopalakrishnan static u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
172218b94f66SAravind Gopalakrishnan 				     u8 intlv_en, int num_dcts_intlv,
172318b94f66SAravind Gopalakrishnan 				     u32 dct_sel)
172418b94f66SAravind Gopalakrishnan {
172518b94f66SAravind Gopalakrishnan 	u8 channel = 0;
172618b94f66SAravind Gopalakrishnan 	u8 select;
172718b94f66SAravind Gopalakrishnan 
172818b94f66SAravind Gopalakrishnan 	if (!(intlv_en))
172918b94f66SAravind Gopalakrishnan 		return (u8)(dct_sel);
173018b94f66SAravind Gopalakrishnan 
173118b94f66SAravind Gopalakrishnan 	if (num_dcts_intlv == 2) {
173218b94f66SAravind Gopalakrishnan 		select = (sys_addr >> 8) & 0x3;
173318b94f66SAravind Gopalakrishnan 		channel = select ? 0x3 : 0;
17349d0e8d83SAravind Gopalakrishnan 	} else if (num_dcts_intlv == 4) {
17359d0e8d83SAravind Gopalakrishnan 		u8 intlv_addr = dct_sel_interleave_addr(pvt);
17369d0e8d83SAravind Gopalakrishnan 		switch (intlv_addr) {
17379d0e8d83SAravind Gopalakrishnan 		case 0x4:
17389d0e8d83SAravind Gopalakrishnan 			channel = (sys_addr >> 8) & 0x3;
17399d0e8d83SAravind Gopalakrishnan 			break;
17409d0e8d83SAravind Gopalakrishnan 		case 0x5:
17419d0e8d83SAravind Gopalakrishnan 			channel = (sys_addr >> 9) & 0x3;
17429d0e8d83SAravind Gopalakrishnan 			break;
17439d0e8d83SAravind Gopalakrishnan 		}
17449d0e8d83SAravind Gopalakrishnan 	}
174518b94f66SAravind Gopalakrishnan 	return channel;
174618b94f66SAravind Gopalakrishnan }
174718b94f66SAravind Gopalakrishnan 
174818b94f66SAravind Gopalakrishnan /*
1749229a7a11SBorislav Petkov  * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory
1750f71d0a05SDoug Thompson  * Interleaving Modes.
1751f71d0a05SDoug Thompson  */
1752b15f0fcaSBorislav Petkov static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
1753229a7a11SBorislav Petkov 				bool hi_range_sel, u8 intlv_en)
17546163b5d4SDoug Thompson {
1755151fa71cSBorislav Petkov 	u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1;
17566163b5d4SDoug Thompson 
17576163b5d4SDoug Thompson 	if (dct_ganging_enabled(pvt))
1758229a7a11SBorislav Petkov 		return 0;
1759229a7a11SBorislav Petkov 
1760229a7a11SBorislav Petkov 	if (hi_range_sel)
1761229a7a11SBorislav Petkov 		return dct_sel_high;
1762229a7a11SBorislav Petkov 
1763f71d0a05SDoug Thompson 	/*
1764f71d0a05SDoug Thompson 	 * see F2x110[DctSelIntLvAddr] - channel interleave mode
1765f71d0a05SDoug Thompson 	 */
1766229a7a11SBorislav Petkov 	if (dct_interleave_enabled(pvt)) {
1767229a7a11SBorislav Petkov 		u8 intlv_addr = dct_sel_interleave_addr(pvt);
17686163b5d4SDoug Thompson 
1769229a7a11SBorislav Petkov 		/* return DCT select function: 0=DCT0, 1=DCT1 */
1770229a7a11SBorislav Petkov 		if (!intlv_addr)
1771229a7a11SBorislav Petkov 			return sys_addr >> 6 & 1;
17726163b5d4SDoug Thompson 
1773229a7a11SBorislav Petkov 		if (intlv_addr & 0x2) {
1774229a7a11SBorislav Petkov 			u8 shift = intlv_addr & 0x1 ? 9 : 6;
1775dc0a50a8SYazen Ghannam 			u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) & 1;
1776229a7a11SBorislav Petkov 
1777229a7a11SBorislav Petkov 			return ((sys_addr >> shift) & 1) ^ temp;
17786163b5d4SDoug Thompson 		}
17796163b5d4SDoug Thompson 
1780dc0a50a8SYazen Ghannam 		if (intlv_addr & 0x4) {
1781dc0a50a8SYazen Ghannam 			u8 shift = intlv_addr & 0x1 ? 9 : 8;
1782dc0a50a8SYazen Ghannam 
1783dc0a50a8SYazen Ghannam 			return (sys_addr >> shift) & 1;
1784dc0a50a8SYazen Ghannam 		}
1785dc0a50a8SYazen Ghannam 
1786229a7a11SBorislav Petkov 		return (sys_addr >> (12 + hweight8(intlv_en))) & 1;
1787229a7a11SBorislav Petkov 	}
1788229a7a11SBorislav Petkov 
1789229a7a11SBorislav Petkov 	if (dct_high_range_enabled(pvt))
1790229a7a11SBorislav Petkov 		return ~dct_sel_high & 1;
17916163b5d4SDoug Thompson 
17926163b5d4SDoug Thompson 	return 0;
17936163b5d4SDoug Thompson }
17946163b5d4SDoug Thompson 
1795c8e518d5SBorislav Petkov /* Convert the sys_addr to the normalized DCT address */
1796c7e5301aSDaniel J Blueman static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range,
1797c8e518d5SBorislav Petkov 				 u64 sys_addr, bool hi_rng,
1798c8e518d5SBorislav Petkov 				 u32 dct_sel_base_addr)
17996163b5d4SDoug Thompson {
18006163b5d4SDoug Thompson 	u64 chan_off;
1801c8e518d5SBorislav Petkov 	u64 dram_base		= get_dram_base(pvt, range);
1802c8e518d5SBorislav Petkov 	u64 hole_off		= f10_dhar_offset(pvt);
18036f3508f6SDan Carpenter 	u64 dct_sel_base_off	= (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16;
18046163b5d4SDoug Thompson 
1805c8e518d5SBorislav Petkov 	if (hi_rng) {
1806c8e518d5SBorislav Petkov 		/*
1807c8e518d5SBorislav Petkov 		 * if
1808c8e518d5SBorislav Petkov 		 * base address of high range is below 4Gb
1809c8e518d5SBorislav Petkov 		 * (bits [47:27] at [31:11])
1810c8e518d5SBorislav Petkov 		 * DRAM address space on this DCT is hoisted above 4Gb	&&
1811c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1812c8e518d5SBorislav Petkov 		 *
1813c8e518d5SBorislav Petkov 		 *	remove hole offset from sys_addr
1814c8e518d5SBorislav Petkov 		 * else
1815c8e518d5SBorislav Petkov 		 *	remove high range offset from sys_addr
1816c8e518d5SBorislav Petkov 		 */
1817c8e518d5SBorislav Petkov 		if ((!(dct_sel_base_addr >> 16) ||
1818c8e518d5SBorislav Petkov 		     dct_sel_base_addr < dhar_base(pvt)) &&
1819972ea17aSBorislav Petkov 		    dhar_valid(pvt) &&
1820c8e518d5SBorislav Petkov 		    (sys_addr >= BIT_64(32)))
1821bc21fa57SBorislav Petkov 			chan_off = hole_off;
18226163b5d4SDoug Thompson 		else
18236163b5d4SDoug Thompson 			chan_off = dct_sel_base_off;
18246163b5d4SDoug Thompson 	} else {
1825c8e518d5SBorislav Petkov 		/*
1826c8e518d5SBorislav Petkov 		 * if
1827c8e518d5SBorislav Petkov 		 * we have a valid hole		&&
1828c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1829c8e518d5SBorislav Petkov 		 *
1830c8e518d5SBorislav Petkov 		 *	remove hole
1831c8e518d5SBorislav Petkov 		 * else
1832c8e518d5SBorislav Petkov 		 *	remove dram base to normalize to DCT address
1833c8e518d5SBorislav Petkov 		 */
1834972ea17aSBorislav Petkov 		if (dhar_valid(pvt) && (sys_addr >= BIT_64(32)))
1835bc21fa57SBorislav Petkov 			chan_off = hole_off;
18366163b5d4SDoug Thompson 		else
1837c8e518d5SBorislav Petkov 			chan_off = dram_base;
18386163b5d4SDoug Thompson 	}
18396163b5d4SDoug Thompson 
184010ef6b0dSChen, Gong 	return (sys_addr & GENMASK_ULL(47,6)) - (chan_off & GENMASK_ULL(47,23));
18416163b5d4SDoug Thompson }
18426163b5d4SDoug Thompson 
18436163b5d4SDoug Thompson /*
18446163b5d4SDoug Thompson  * checks if the csrow passed in is marked as SPARED, if so returns the new
18456163b5d4SDoug Thompson  * spare row
18466163b5d4SDoug Thompson  */
184711c75eadSBorislav Petkov static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow)
18486163b5d4SDoug Thompson {
1849614ec9d8SBorislav Petkov 	int tmp_cs;
18506163b5d4SDoug Thompson 
1851614ec9d8SBorislav Petkov 	if (online_spare_swap_done(pvt, dct) &&
1852614ec9d8SBorislav Petkov 	    csrow == online_spare_bad_dramcs(pvt, dct)) {
1853614ec9d8SBorislav Petkov 
1854614ec9d8SBorislav Petkov 		for_each_chip_select(tmp_cs, dct, pvt) {
1855614ec9d8SBorislav Petkov 			if (chip_select_base(tmp_cs, dct, pvt) & 0x2) {
1856614ec9d8SBorislav Petkov 				csrow = tmp_cs;
1857614ec9d8SBorislav Petkov 				break;
1858614ec9d8SBorislav Petkov 			}
1859614ec9d8SBorislav Petkov 		}
18606163b5d4SDoug Thompson 	}
18616163b5d4SDoug Thompson 	return csrow;
18626163b5d4SDoug Thompson }
18636163b5d4SDoug Thompson 
18646163b5d4SDoug Thompson /*
18656163b5d4SDoug Thompson  * Iterate over the DRAM DCT "base" and "mask" registers looking for a
18666163b5d4SDoug Thompson  * SystemAddr match on the specified 'ChannelSelect' and 'NodeID'
18676163b5d4SDoug Thompson  *
18686163b5d4SDoug Thompson  * Return:
18696163b5d4SDoug Thompson  *	-EINVAL:  NOT FOUND
18706163b5d4SDoug Thompson  *	0..csrow = Chip-Select Row
18716163b5d4SDoug Thompson  */
1872c7e5301aSDaniel J Blueman static int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct)
18736163b5d4SDoug Thompson {
18746163b5d4SDoug Thompson 	struct mem_ctl_info *mci;
18756163b5d4SDoug Thompson 	struct amd64_pvt *pvt;
187611c75eadSBorislav Petkov 	u64 cs_base, cs_mask;
18776163b5d4SDoug Thompson 	int cs_found = -EINVAL;
18786163b5d4SDoug Thompson 	int csrow;
18796163b5d4SDoug Thompson 
18802ec591acSBorislav Petkov 	mci = edac_mc_find(nid);
18816163b5d4SDoug Thompson 	if (!mci)
18826163b5d4SDoug Thompson 		return cs_found;
18836163b5d4SDoug Thompson 
18846163b5d4SDoug Thompson 	pvt = mci->pvt_info;
18856163b5d4SDoug Thompson 
1886956b9ba1SJoe Perches 	edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct);
18876163b5d4SDoug Thompson 
188811c75eadSBorislav Petkov 	for_each_chip_select(csrow, dct, pvt) {
188911c75eadSBorislav Petkov 		if (!csrow_enabled(csrow, dct, pvt))
18906163b5d4SDoug Thompson 			continue;
18916163b5d4SDoug Thompson 
189211c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask);
18936163b5d4SDoug Thompson 
1894956b9ba1SJoe Perches 		edac_dbg(1, "    CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
18956163b5d4SDoug Thompson 			 csrow, cs_base, cs_mask);
18966163b5d4SDoug Thompson 
189711c75eadSBorislav Petkov 		cs_mask = ~cs_mask;
18986163b5d4SDoug Thompson 
1899956b9ba1SJoe Perches 		edac_dbg(1, "    (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n",
190011c75eadSBorislav Petkov 			 (in_addr & cs_mask), (cs_base & cs_mask));
19016163b5d4SDoug Thompson 
190211c75eadSBorislav Petkov 		if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
190318b94f66SAravind Gopalakrishnan 			if (pvt->fam == 0x15 && pvt->model >= 0x30) {
190418b94f66SAravind Gopalakrishnan 				cs_found =  csrow;
190518b94f66SAravind Gopalakrishnan 				break;
190618b94f66SAravind Gopalakrishnan 			}
190711c75eadSBorislav Petkov 			cs_found = f10_process_possible_spare(pvt, dct, csrow);
19086163b5d4SDoug Thompson 
1909956b9ba1SJoe Perches 			edac_dbg(1, " MATCH csrow=%d\n", cs_found);
19106163b5d4SDoug Thompson 			break;
19116163b5d4SDoug Thompson 		}
19126163b5d4SDoug Thompson 	}
19136163b5d4SDoug Thompson 	return cs_found;
19146163b5d4SDoug Thompson }
19156163b5d4SDoug Thompson 
191695b0ef55SBorislav Petkov /*
191795b0ef55SBorislav Petkov  * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is
191895b0ef55SBorislav Petkov  * swapped with a region located at the bottom of memory so that the GPU can use
191995b0ef55SBorislav Petkov  * the interleaved region and thus two channels.
192095b0ef55SBorislav Petkov  */
1921b15f0fcaSBorislav Petkov static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr)
192295b0ef55SBorislav Petkov {
192395b0ef55SBorislav Petkov 	u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr;
192495b0ef55SBorislav Petkov 
1925a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10) {
192695b0ef55SBorislav Petkov 		/* only revC3 and revE have that feature */
1927a4b4bedcSBorislav Petkov 		if (pvt->model < 4 || (pvt->model < 0xa && pvt->stepping < 3))
192895b0ef55SBorislav Petkov 			return sys_addr;
192995b0ef55SBorislav Petkov 	}
193095b0ef55SBorislav Petkov 
19317981a28fSAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F2, SWAP_INTLV_REG, &swap_reg);
193295b0ef55SBorislav Petkov 
193395b0ef55SBorislav Petkov 	if (!(swap_reg & 0x1))
193495b0ef55SBorislav Petkov 		return sys_addr;
193595b0ef55SBorislav Petkov 
193695b0ef55SBorislav Petkov 	swap_base	= (swap_reg >> 3) & 0x7f;
193795b0ef55SBorislav Petkov 	swap_limit	= (swap_reg >> 11) & 0x7f;
193895b0ef55SBorislav Petkov 	rgn_size	= (swap_reg >> 20) & 0x7f;
193995b0ef55SBorislav Petkov 	tmp_addr	= sys_addr >> 27;
194095b0ef55SBorislav Petkov 
194195b0ef55SBorislav Petkov 	if (!(sys_addr >> 34) &&
194295b0ef55SBorislav Petkov 	    (((tmp_addr >= swap_base) &&
194395b0ef55SBorislav Petkov 	     (tmp_addr <= swap_limit)) ||
194495b0ef55SBorislav Petkov 	     (tmp_addr < rgn_size)))
194595b0ef55SBorislav Petkov 		return sys_addr ^ (u64)swap_base << 27;
194695b0ef55SBorislav Petkov 
194795b0ef55SBorislav Petkov 	return sys_addr;
194895b0ef55SBorislav Petkov }
194995b0ef55SBorislav Petkov 
1950f71d0a05SDoug Thompson /* For a given @dram_range, check if @sys_addr falls within it. */
1951e761359aSBorislav Petkov static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
195233ca0643SBorislav Petkov 				  u64 sys_addr, int *chan_sel)
1953f71d0a05SDoug Thompson {
1954229a7a11SBorislav Petkov 	int cs_found = -EINVAL;
1955c8e518d5SBorislav Petkov 	u64 chan_addr;
19565d4b58e8SBorislav Petkov 	u32 dct_sel_base;
195711c75eadSBorislav Petkov 	u8 channel;
1958229a7a11SBorislav Petkov 	bool high_range = false;
1959f71d0a05SDoug Thompson 
19607f19bf75SBorislav Petkov 	u8 node_id    = dram_dst_node(pvt, range);
1961229a7a11SBorislav Petkov 	u8 intlv_en   = dram_intlv_en(pvt, range);
19627f19bf75SBorislav Petkov 	u32 intlv_sel = dram_intlv_sel(pvt, range);
1963f71d0a05SDoug Thompson 
1964956b9ba1SJoe Perches 	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
1965c8e518d5SBorislav Petkov 		 range, sys_addr, get_dram_limit(pvt, range));
1966f71d0a05SDoug Thompson 
1967355fba60SBorislav Petkov 	if (dhar_valid(pvt) &&
1968355fba60SBorislav Petkov 	    dhar_base(pvt) <= sys_addr &&
1969355fba60SBorislav Petkov 	    sys_addr < BIT_64(32)) {
1970355fba60SBorislav Petkov 		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
1971355fba60SBorislav Petkov 			    sys_addr);
1972f71d0a05SDoug Thompson 		return -EINVAL;
1973355fba60SBorislav Petkov 	}
1974355fba60SBorislav Petkov 
1975f030ddfbSBorislav Petkov 	if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en)))
1976355fba60SBorislav Petkov 		return -EINVAL;
1977f71d0a05SDoug Thompson 
1978b15f0fcaSBorislav Petkov 	sys_addr = f1x_swap_interleaved_region(pvt, sys_addr);
197995b0ef55SBorislav Petkov 
1980f71d0a05SDoug Thompson 	dct_sel_base = dct_sel_baseaddr(pvt);
1981f71d0a05SDoug Thompson 
1982f71d0a05SDoug Thompson 	/*
1983f71d0a05SDoug Thompson 	 * check whether addresses >= DctSelBaseAddr[47:27] are to be used to
1984f71d0a05SDoug Thompson 	 * select between DCT0 and DCT1.
1985f71d0a05SDoug Thompson 	 */
1986f71d0a05SDoug Thompson 	if (dct_high_range_enabled(pvt) &&
1987f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt) &&
1988f71d0a05SDoug Thompson 	   ((sys_addr >> 27) >= (dct_sel_base >> 11)))
1989229a7a11SBorislav Petkov 		high_range = true;
1990f71d0a05SDoug Thompson 
1991b15f0fcaSBorislav Petkov 	channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en);
1992f71d0a05SDoug Thompson 
1993b15f0fcaSBorislav Petkov 	chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr,
1994c8e518d5SBorislav Petkov 					  high_range, dct_sel_base);
1995f71d0a05SDoug Thompson 
1996e2f79dbdSBorislav Petkov 	/* Remove node interleaving, see F1x120 */
1997e2f79dbdSBorislav Petkov 	if (intlv_en)
1998e2f79dbdSBorislav Petkov 		chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) |
1999e2f79dbdSBorislav Petkov 			    (chan_addr & 0xfff);
2000f71d0a05SDoug Thompson 
20015d4b58e8SBorislav Petkov 	/* remove channel interleave */
2002f71d0a05SDoug Thompson 	if (dct_interleave_enabled(pvt) &&
2003f71d0a05SDoug Thompson 	   !dct_high_range_enabled(pvt) &&
2004f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt)) {
20055d4b58e8SBorislav Petkov 
20065d4b58e8SBorislav Petkov 		if (dct_sel_interleave_addr(pvt) != 1) {
20075d4b58e8SBorislav Petkov 			if (dct_sel_interleave_addr(pvt) == 0x3)
20085d4b58e8SBorislav Petkov 				/* hash 9 */
20095d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 10) << 9) |
20105d4b58e8SBorislav Petkov 					     (chan_addr & 0x1ff);
20115d4b58e8SBorislav Petkov 			else
20125d4b58e8SBorislav Petkov 				/* A[6] or hash 6 */
20135d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 7) << 6) |
20145d4b58e8SBorislav Petkov 					     (chan_addr & 0x3f);
20155d4b58e8SBorislav Petkov 		} else
20165d4b58e8SBorislav Petkov 			/* A[12] */
20175d4b58e8SBorislav Petkov 			chan_addr = ((chan_addr >> 13) << 12) |
20185d4b58e8SBorislav Petkov 				     (chan_addr & 0xfff);
2019f71d0a05SDoug Thompson 	}
2020f71d0a05SDoug Thompson 
2021956b9ba1SJoe Perches 	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
2022f71d0a05SDoug Thompson 
2023b15f0fcaSBorislav Petkov 	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel);
2024f71d0a05SDoug Thompson 
202533ca0643SBorislav Petkov 	if (cs_found >= 0)
2026f71d0a05SDoug Thompson 		*chan_sel = channel;
202733ca0643SBorislav Petkov 
2028f71d0a05SDoug Thompson 	return cs_found;
2029f71d0a05SDoug Thompson }
2030f71d0a05SDoug Thompson 
203118b94f66SAravind Gopalakrishnan static int f15_m30h_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
203218b94f66SAravind Gopalakrishnan 					u64 sys_addr, int *chan_sel)
203318b94f66SAravind Gopalakrishnan {
203418b94f66SAravind Gopalakrishnan 	int cs_found = -EINVAL;
203518b94f66SAravind Gopalakrishnan 	int num_dcts_intlv = 0;
203618b94f66SAravind Gopalakrishnan 	u64 chan_addr, chan_offset;
203718b94f66SAravind Gopalakrishnan 	u64 dct_base, dct_limit;
203818b94f66SAravind Gopalakrishnan 	u32 dct_cont_base_reg, dct_cont_limit_reg, tmp;
203918b94f66SAravind Gopalakrishnan 	u8 channel, alias_channel, leg_mmio_hole, dct_sel, dct_offset_en;
204018b94f66SAravind Gopalakrishnan 
204118b94f66SAravind Gopalakrishnan 	u64 dhar_offset		= f10_dhar_offset(pvt);
204218b94f66SAravind Gopalakrishnan 	u8 intlv_addr		= dct_sel_interleave_addr(pvt);
204318b94f66SAravind Gopalakrishnan 	u8 node_id		= dram_dst_node(pvt, range);
204418b94f66SAravind Gopalakrishnan 	u8 intlv_en		= dram_intlv_en(pvt, range);
204518b94f66SAravind Gopalakrishnan 
204618b94f66SAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F1, DRAM_CONT_BASE, &dct_cont_base_reg);
204718b94f66SAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F1, DRAM_CONT_LIMIT, &dct_cont_limit_reg);
204818b94f66SAravind Gopalakrishnan 
204918b94f66SAravind Gopalakrishnan 	dct_offset_en		= (u8) ((dct_cont_base_reg >> 3) & BIT(0));
205018b94f66SAravind Gopalakrishnan 	dct_sel			= (u8) ((dct_cont_base_reg >> 4) & 0x7);
205118b94f66SAravind Gopalakrishnan 
205218b94f66SAravind Gopalakrishnan 	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
205318b94f66SAravind Gopalakrishnan 		 range, sys_addr, get_dram_limit(pvt, range));
205418b94f66SAravind Gopalakrishnan 
205518b94f66SAravind Gopalakrishnan 	if (!(get_dram_base(pvt, range)  <= sys_addr) &&
205618b94f66SAravind Gopalakrishnan 	    !(get_dram_limit(pvt, range) >= sys_addr))
205718b94f66SAravind Gopalakrishnan 		return -EINVAL;
205818b94f66SAravind Gopalakrishnan 
205918b94f66SAravind Gopalakrishnan 	if (dhar_valid(pvt) &&
206018b94f66SAravind Gopalakrishnan 	    dhar_base(pvt) <= sys_addr &&
206118b94f66SAravind Gopalakrishnan 	    sys_addr < BIT_64(32)) {
206218b94f66SAravind Gopalakrishnan 		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
206318b94f66SAravind Gopalakrishnan 			    sys_addr);
206418b94f66SAravind Gopalakrishnan 		return -EINVAL;
206518b94f66SAravind Gopalakrishnan 	}
206618b94f66SAravind Gopalakrishnan 
206718b94f66SAravind Gopalakrishnan 	/* Verify sys_addr is within DCT Range. */
20684fc06b31SAravind Gopalakrishnan 	dct_base = (u64) dct_sel_baseaddr(pvt);
20694fc06b31SAravind Gopalakrishnan 	dct_limit = (dct_cont_limit_reg >> 11) & 0x1FFF;
207018b94f66SAravind Gopalakrishnan 
207118b94f66SAravind Gopalakrishnan 	if (!(dct_cont_base_reg & BIT(0)) &&
20724fc06b31SAravind Gopalakrishnan 	    !(dct_base <= (sys_addr >> 27) &&
20734fc06b31SAravind Gopalakrishnan 	      dct_limit >= (sys_addr >> 27)))
207418b94f66SAravind Gopalakrishnan 		return -EINVAL;
207518b94f66SAravind Gopalakrishnan 
207618b94f66SAravind Gopalakrishnan 	/* Verify number of dct's that participate in channel interleaving. */
207718b94f66SAravind Gopalakrishnan 	num_dcts_intlv = (int) hweight8(intlv_en);
207818b94f66SAravind Gopalakrishnan 
207918b94f66SAravind Gopalakrishnan 	if (!(num_dcts_intlv % 2 == 0) || (num_dcts_intlv > 4))
208018b94f66SAravind Gopalakrishnan 		return -EINVAL;
208118b94f66SAravind Gopalakrishnan 
2082dc0a50a8SYazen Ghannam 	if (pvt->model >= 0x60)
2083dc0a50a8SYazen Ghannam 		channel = f1x_determine_channel(pvt, sys_addr, false, intlv_en);
2084dc0a50a8SYazen Ghannam 	else
208518b94f66SAravind Gopalakrishnan 		channel = f15_m30h_determine_channel(pvt, sys_addr, intlv_en,
208618b94f66SAravind Gopalakrishnan 						     num_dcts_intlv, dct_sel);
208718b94f66SAravind Gopalakrishnan 
208818b94f66SAravind Gopalakrishnan 	/* Verify we stay within the MAX number of channels allowed */
20897f3f5240SAravind Gopalakrishnan 	if (channel > 3)
209018b94f66SAravind Gopalakrishnan 		return -EINVAL;
209118b94f66SAravind Gopalakrishnan 
209218b94f66SAravind Gopalakrishnan 	leg_mmio_hole = (u8) (dct_cont_base_reg >> 1 & BIT(0));
209318b94f66SAravind Gopalakrishnan 
209418b94f66SAravind Gopalakrishnan 	/* Get normalized DCT addr */
209518b94f66SAravind Gopalakrishnan 	if (leg_mmio_hole && (sys_addr >= BIT_64(32)))
209618b94f66SAravind Gopalakrishnan 		chan_offset = dhar_offset;
209718b94f66SAravind Gopalakrishnan 	else
20984fc06b31SAravind Gopalakrishnan 		chan_offset = dct_base << 27;
209918b94f66SAravind Gopalakrishnan 
210018b94f66SAravind Gopalakrishnan 	chan_addr = sys_addr - chan_offset;
210118b94f66SAravind Gopalakrishnan 
210218b94f66SAravind Gopalakrishnan 	/* remove channel interleave */
210318b94f66SAravind Gopalakrishnan 	if (num_dcts_intlv == 2) {
210418b94f66SAravind Gopalakrishnan 		if (intlv_addr == 0x4)
210518b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 9) << 8) |
210618b94f66SAravind Gopalakrishnan 						(chan_addr & 0xff);
210718b94f66SAravind Gopalakrishnan 		else if (intlv_addr == 0x5)
210818b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 10) << 9) |
210918b94f66SAravind Gopalakrishnan 						(chan_addr & 0x1ff);
211018b94f66SAravind Gopalakrishnan 		else
211118b94f66SAravind Gopalakrishnan 			return -EINVAL;
211218b94f66SAravind Gopalakrishnan 
211318b94f66SAravind Gopalakrishnan 	} else if (num_dcts_intlv == 4) {
211418b94f66SAravind Gopalakrishnan 		if (intlv_addr == 0x4)
211518b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 10) << 8) |
211618b94f66SAravind Gopalakrishnan 							(chan_addr & 0xff);
211718b94f66SAravind Gopalakrishnan 		else if (intlv_addr == 0x5)
211818b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 11) << 9) |
211918b94f66SAravind Gopalakrishnan 							(chan_addr & 0x1ff);
212018b94f66SAravind Gopalakrishnan 		else
212118b94f66SAravind Gopalakrishnan 			return -EINVAL;
212218b94f66SAravind Gopalakrishnan 	}
212318b94f66SAravind Gopalakrishnan 
212418b94f66SAravind Gopalakrishnan 	if (dct_offset_en) {
212518b94f66SAravind Gopalakrishnan 		amd64_read_pci_cfg(pvt->F1,
212618b94f66SAravind Gopalakrishnan 				   DRAM_CONT_HIGH_OFF + (int) channel * 4,
212718b94f66SAravind Gopalakrishnan 				   &tmp);
21284fc06b31SAravind Gopalakrishnan 		chan_addr +=  (u64) ((tmp >> 11) & 0xfff) << 27;
212918b94f66SAravind Gopalakrishnan 	}
213018b94f66SAravind Gopalakrishnan 
213118b94f66SAravind Gopalakrishnan 	f15h_select_dct(pvt, channel);
213218b94f66SAravind Gopalakrishnan 
213318b94f66SAravind Gopalakrishnan 	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
213418b94f66SAravind Gopalakrishnan 
213518b94f66SAravind Gopalakrishnan 	/*
213618b94f66SAravind Gopalakrishnan 	 * Find Chip select:
213718b94f66SAravind Gopalakrishnan 	 * if channel = 3, then alias it to 1. This is because, in F15 M30h,
213818b94f66SAravind Gopalakrishnan 	 * there is support for 4 DCT's, but only 2 are currently functional.
213918b94f66SAravind Gopalakrishnan 	 * They are DCT0 and DCT3. But we have read all registers of DCT3 into
214018b94f66SAravind Gopalakrishnan 	 * pvt->csels[1]. So we need to use '1' here to get correct info.
214118b94f66SAravind Gopalakrishnan 	 * Refer F15 M30h BKDG Section 2.10 and 2.10.3 for clarifications.
214218b94f66SAravind Gopalakrishnan 	 */
214318b94f66SAravind Gopalakrishnan 	alias_channel =  (channel == 3) ? 1 : channel;
214418b94f66SAravind Gopalakrishnan 
214518b94f66SAravind Gopalakrishnan 	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, alias_channel);
214618b94f66SAravind Gopalakrishnan 
214718b94f66SAravind Gopalakrishnan 	if (cs_found >= 0)
214818b94f66SAravind Gopalakrishnan 		*chan_sel = alias_channel;
214918b94f66SAravind Gopalakrishnan 
215018b94f66SAravind Gopalakrishnan 	return cs_found;
215118b94f66SAravind Gopalakrishnan }
215218b94f66SAravind Gopalakrishnan 
215318b94f66SAravind Gopalakrishnan static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt,
215418b94f66SAravind Gopalakrishnan 					u64 sys_addr,
215533ca0643SBorislav Petkov 					int *chan_sel)
2156f71d0a05SDoug Thompson {
2157e761359aSBorislav Petkov 	int cs_found = -EINVAL;
2158e761359aSBorislav Petkov 	unsigned range;
2159f71d0a05SDoug Thompson 
21607f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
21617f19bf75SBorislav Petkov 		if (!dram_rw(pvt, range))
2162f71d0a05SDoug Thompson 			continue;
2163f71d0a05SDoug Thompson 
216418b94f66SAravind Gopalakrishnan 		if (pvt->fam == 0x15 && pvt->model >= 0x30)
216518b94f66SAravind Gopalakrishnan 			cs_found = f15_m30h_match_to_this_node(pvt, range,
216618b94f66SAravind Gopalakrishnan 							       sys_addr,
216718b94f66SAravind Gopalakrishnan 							       chan_sel);
2168f71d0a05SDoug Thompson 
216918b94f66SAravind Gopalakrishnan 		else if ((get_dram_base(pvt, range)  <= sys_addr) &&
217018b94f66SAravind Gopalakrishnan 			 (get_dram_limit(pvt, range) >= sys_addr)) {
2171b15f0fcaSBorislav Petkov 			cs_found = f1x_match_to_this_node(pvt, range,
217233ca0643SBorislav Petkov 							  sys_addr, chan_sel);
2173f71d0a05SDoug Thompson 			if (cs_found >= 0)
2174f71d0a05SDoug Thompson 				break;
2175f71d0a05SDoug Thompson 		}
2176f71d0a05SDoug Thompson 	}
2177f71d0a05SDoug Thompson 	return cs_found;
2178f71d0a05SDoug Thompson }
2179f71d0a05SDoug Thompson 
2180f71d0a05SDoug Thompson /*
2181bdc30a0cSBorislav Petkov  * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
2182bdc30a0cSBorislav Petkov  * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
2183f71d0a05SDoug Thompson  *
2184bdc30a0cSBorislav Petkov  * The @sys_addr is usually an error address received from the hardware
2185bdc30a0cSBorislav Petkov  * (MCX_ADDR).
2186f71d0a05SDoug Thompson  */
2187b15f0fcaSBorislav Petkov static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
218833ca0643SBorislav Petkov 				     struct err_info *err)
2189f71d0a05SDoug Thompson {
2190f71d0a05SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
2191f71d0a05SDoug Thompson 
219233ca0643SBorislav Petkov 	error_address_to_page_and_offset(sys_addr, err);
2193ab5a503cSMauro Carvalho Chehab 
219433ca0643SBorislav Petkov 	err->csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &err->channel);
219533ca0643SBorislav Petkov 	if (err->csrow < 0) {
219633ca0643SBorislav Petkov 		err->err_code = ERR_CSROW;
2197bdc30a0cSBorislav Petkov 		return;
2198bdc30a0cSBorislav Petkov 	}
2199bdc30a0cSBorislav Petkov 
2200f71d0a05SDoug Thompson 	/*
2201bdc30a0cSBorislav Petkov 	 * We need the syndromes for channel detection only when we're
2202bdc30a0cSBorislav Petkov 	 * ganged. Otherwise @chan should already contain the channel at
2203bdc30a0cSBorislav Petkov 	 * this point.
2204f71d0a05SDoug Thompson 	 */
2205a97fa68eSBorislav Petkov 	if (dct_ganging_enabled(pvt))
220633ca0643SBorislav Petkov 		err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
2207f71d0a05SDoug Thompson }
2208f71d0a05SDoug Thompson 
2209f71d0a05SDoug Thompson /*
22108566c4dfSBorislav Petkov  * debug routine to display the memory sizes of all logical DIMMs and its
2211cb328507SBorislav Petkov  * CSROWs
2212f71d0a05SDoug Thompson  */
2213d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
2214f71d0a05SDoug Thompson {
2215bb89f5a0SBorislav Petkov 	int dimm, size0, size1;
2216525a1b20SBorislav Petkov 	u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases;
2217525a1b20SBorislav Petkov 	u32 dbam  = ctrl ? pvt->dbam1 : pvt->dbam0;
2218f71d0a05SDoug Thompson 
2219a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf) {
22208566c4dfSBorislav Petkov 		/* K8 families < revF not supported yet */
22211433eb99SBorislav Petkov 	       if (pvt->ext_model < K8_REV_F)
22228566c4dfSBorislav Petkov 			return;
22238566c4dfSBorislav Petkov 	       else
22248566c4dfSBorislav Petkov 		       WARN_ON(ctrl != 0);
22258566c4dfSBorislav Petkov 	}
22268566c4dfSBorislav Petkov 
22277981a28fSAravind Gopalakrishnan 	if (pvt->fam == 0x10) {
22287981a28fSAravind Gopalakrishnan 		dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1
22297981a28fSAravind Gopalakrishnan 							   : pvt->dbam0;
22307981a28fSAravind Gopalakrishnan 		dcsb = (ctrl && !dct_ganging_enabled(pvt)) ?
22317981a28fSAravind Gopalakrishnan 				 pvt->csels[1].csbases :
22327981a28fSAravind Gopalakrishnan 				 pvt->csels[0].csbases;
22337981a28fSAravind Gopalakrishnan 	} else if (ctrl) {
22347981a28fSAravind Gopalakrishnan 		dbam = pvt->dbam0;
22357981a28fSAravind Gopalakrishnan 		dcsb = pvt->csels[1].csbases;
22367981a28fSAravind Gopalakrishnan 	}
2237956b9ba1SJoe Perches 	edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
2238956b9ba1SJoe Perches 		 ctrl, dbam);
2239f71d0a05SDoug Thompson 
22408566c4dfSBorislav Petkov 	edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
22418566c4dfSBorislav Petkov 
2242f71d0a05SDoug Thompson 	/* Dump memory sizes for DIMM and its CSROWs */
2243f71d0a05SDoug Thompson 	for (dimm = 0; dimm < 4; dimm++) {
2244f71d0a05SDoug Thompson 
2245f71d0a05SDoug Thompson 		size0 = 0;
224611c75eadSBorislav Petkov 		if (dcsb[dimm*2] & DCSB_CS_ENABLE)
224707ed82efSYazen Ghannam 			/*
224807ed82efSYazen Ghannam 			 * For F15m60h, we need multiplier for LRDIMM cs_size
224907ed82efSYazen Ghannam 			 * calculation. We pass dimm value to the dbam_to_cs
2250a597d2a5SAravind Gopalakrishnan 			 * mapper so we can find the multiplier from the
2251a597d2a5SAravind Gopalakrishnan 			 * corresponding DCSM.
2252a597d2a5SAravind Gopalakrishnan 			 */
225341d8bfabSBorislav Petkov 			size0 = pvt->ops->dbam_to_cs(pvt, ctrl,
2254a597d2a5SAravind Gopalakrishnan 						     DBAM_DIMM(dimm, dbam),
2255a597d2a5SAravind Gopalakrishnan 						     dimm);
2256f71d0a05SDoug Thompson 
2257f71d0a05SDoug Thompson 		size1 = 0;
225811c75eadSBorislav Petkov 		if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE)
225941d8bfabSBorislav Petkov 			size1 = pvt->ops->dbam_to_cs(pvt, ctrl,
2260a597d2a5SAravind Gopalakrishnan 						     DBAM_DIMM(dimm, dbam),
2261a597d2a5SAravind Gopalakrishnan 						     dimm);
2262f71d0a05SDoug Thompson 
226324f9a7feSBorislav Petkov 		amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
2264bb89f5a0SBorislav Petkov 				dimm * 2,     size0,
2265bb89f5a0SBorislav Petkov 				dimm * 2 + 1, size1);
2266f71d0a05SDoug Thompson 	}
2267f71d0a05SDoug Thompson }
2268f71d0a05SDoug Thompson 
2269d1ea71cdSBorislav Petkov static struct amd64_family_type family_types[] = {
22704d37607aSDoug Thompson 	[K8_CPUS] = {
22710092b20dSBorislav Petkov 		.ctl_name = "K8",
22728d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
22733f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
22745e4c5527SYazen Ghannam 		.max_mcs = 2,
22754d37607aSDoug Thompson 		.ops = {
22764d37607aSDoug Thompson 			.early_channel_count	= k8_early_channel_count,
22774d37607aSDoug Thompson 			.map_sysaddr_to_csrow	= k8_map_sysaddr_to_csrow,
22781433eb99SBorislav Petkov 			.dbam_to_cs		= k8_dbam_to_chip_select,
22794d37607aSDoug Thompson 		}
22804d37607aSDoug Thompson 	},
22814d37607aSDoug Thompson 	[F10_CPUS] = {
22820092b20dSBorislav Petkov 		.ctl_name = "F10h",
22838d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP,
22843f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM,
22855e4c5527SYazen Ghannam 		.max_mcs = 2,
22864d37607aSDoug Thompson 		.ops = {
22877d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
2288b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
22891433eb99SBorislav Petkov 			.dbam_to_cs		= f10_dbam_to_chip_select,
2290b2b0c605SBorislav Petkov 		}
2291b2b0c605SBorislav Petkov 	},
2292b2b0c605SBorislav Petkov 	[F15_CPUS] = {
2293b2b0c605SBorislav Petkov 		.ctl_name = "F15h",
2294df71a053SBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1,
22953f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2,
22965e4c5527SYazen Ghannam 		.max_mcs = 2,
2297b2b0c605SBorislav Petkov 		.ops = {
22987d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
2299b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
230041d8bfabSBorislav Petkov 			.dbam_to_cs		= f15_dbam_to_chip_select,
23014d37607aSDoug Thompson 		}
23024d37607aSDoug Thompson 	},
230318b94f66SAravind Gopalakrishnan 	[F15_M30H_CPUS] = {
230418b94f66SAravind Gopalakrishnan 		.ctl_name = "F15h_M30h",
230518b94f66SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1,
23063f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2,
23075e4c5527SYazen Ghannam 		.max_mcs = 2,
230818b94f66SAravind Gopalakrishnan 		.ops = {
230918b94f66SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
231018b94f66SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
231118b94f66SAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
231218b94f66SAravind Gopalakrishnan 		}
231318b94f66SAravind Gopalakrishnan 	},
2314a597d2a5SAravind Gopalakrishnan 	[F15_M60H_CPUS] = {
2315a597d2a5SAravind Gopalakrishnan 		.ctl_name = "F15h_M60h",
2316a597d2a5SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1,
23173f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2,
23185e4c5527SYazen Ghannam 		.max_mcs = 2,
2319a597d2a5SAravind Gopalakrishnan 		.ops = {
2320a597d2a5SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
2321a597d2a5SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
2322a597d2a5SAravind Gopalakrishnan 			.dbam_to_cs		= f15_m60h_dbam_to_chip_select,
2323a597d2a5SAravind Gopalakrishnan 		}
2324a597d2a5SAravind Gopalakrishnan 	},
232594c1acf2SAravind Gopalakrishnan 	[F16_CPUS] = {
232694c1acf2SAravind Gopalakrishnan 		.ctl_name = "F16h",
232794c1acf2SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1,
23283f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2,
23295e4c5527SYazen Ghannam 		.max_mcs = 2,
233094c1acf2SAravind Gopalakrishnan 		.ops = {
233194c1acf2SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
233294c1acf2SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
233394c1acf2SAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
233494c1acf2SAravind Gopalakrishnan 		}
233594c1acf2SAravind Gopalakrishnan 	},
233685a8885bSAravind Gopalakrishnan 	[F16_M30H_CPUS] = {
233785a8885bSAravind Gopalakrishnan 		.ctl_name = "F16h_M30h",
233885a8885bSAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F1,
23393f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2,
23405e4c5527SYazen Ghannam 		.max_mcs = 2,
234185a8885bSAravind Gopalakrishnan 		.ops = {
234285a8885bSAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
234385a8885bSAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
234485a8885bSAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
234585a8885bSAravind Gopalakrishnan 		}
234685a8885bSAravind Gopalakrishnan 	},
2347f1cbbec9SYazen Ghannam 	[F17_CPUS] = {
2348f1cbbec9SYazen Ghannam 		.ctl_name = "F17h",
2349f1cbbec9SYazen Ghannam 		.f0_id = PCI_DEVICE_ID_AMD_17H_DF_F0,
2350f1cbbec9SYazen Ghannam 		.f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6,
23515e4c5527SYazen Ghannam 		.max_mcs = 2,
2352f1cbbec9SYazen Ghannam 		.ops = {
2353f1cbbec9SYazen Ghannam 			.early_channel_count	= f17_early_channel_count,
2354e53a3b26SYazen Ghannam 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
2355f1cbbec9SYazen Ghannam 		}
2356f1cbbec9SYazen Ghannam 	},
23578960de4aSMichael Jin 	[F17_M10H_CPUS] = {
23588960de4aSMichael Jin 		.ctl_name = "F17h_M10h",
23598960de4aSMichael Jin 		.f0_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F0,
23608960de4aSMichael Jin 		.f6_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F6,
23615e4c5527SYazen Ghannam 		.max_mcs = 2,
23628960de4aSMichael Jin 		.ops = {
23638960de4aSMichael Jin 			.early_channel_count	= f17_early_channel_count,
2364e53a3b26SYazen Ghannam 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
23658960de4aSMichael Jin 		}
23668960de4aSMichael Jin 	},
23676e846239SYazen Ghannam 	[F17_M30H_CPUS] = {
23686e846239SYazen Ghannam 		.ctl_name = "F17h_M30h",
23696e846239SYazen Ghannam 		.f0_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F0,
23706e846239SYazen Ghannam 		.f6_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F6,
23715e4c5527SYazen Ghannam 		.max_mcs = 8,
23726e846239SYazen Ghannam 		.ops = {
23736e846239SYazen Ghannam 			.early_channel_count	= f17_early_channel_count,
2374e53a3b26SYazen Ghannam 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
23756e846239SYazen Ghannam 		}
23766e846239SYazen Ghannam 	},
2377b6bea24dSAlexander Monakov 	[F17_M60H_CPUS] = {
2378b6bea24dSAlexander Monakov 		.ctl_name = "F17h_M60h",
2379b6bea24dSAlexander Monakov 		.f0_id = PCI_DEVICE_ID_AMD_17H_M60H_DF_F0,
2380b6bea24dSAlexander Monakov 		.f6_id = PCI_DEVICE_ID_AMD_17H_M60H_DF_F6,
2381b6bea24dSAlexander Monakov 		.max_mcs = 2,
2382b6bea24dSAlexander Monakov 		.ops = {
2383b6bea24dSAlexander Monakov 			.early_channel_count	= f17_early_channel_count,
2384b6bea24dSAlexander Monakov 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
2385b6bea24dSAlexander Monakov 		}
2386b6bea24dSAlexander Monakov 	},
23873e443eb3SIsaac Vaughn 	[F17_M70H_CPUS] = {
23883e443eb3SIsaac Vaughn 		.ctl_name = "F17h_M70h",
23893e443eb3SIsaac Vaughn 		.f0_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F0,
23903e443eb3SIsaac Vaughn 		.f6_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F6,
23915e4c5527SYazen Ghannam 		.max_mcs = 2,
23923e443eb3SIsaac Vaughn 		.ops = {
23933e443eb3SIsaac Vaughn 			.early_channel_count	= f17_early_channel_count,
23943e443eb3SIsaac Vaughn 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
23953e443eb3SIsaac Vaughn 		}
23963e443eb3SIsaac Vaughn 	},
23972eb61c91SYazen Ghannam 	[F19_CPUS] = {
23982eb61c91SYazen Ghannam 		.ctl_name = "F19h",
23992eb61c91SYazen Ghannam 		.f0_id = PCI_DEVICE_ID_AMD_19H_DF_F0,
24002eb61c91SYazen Ghannam 		.f6_id = PCI_DEVICE_ID_AMD_19H_DF_F6,
24012eb61c91SYazen Ghannam 		.max_mcs = 8,
24022eb61c91SYazen Ghannam 		.ops = {
24032eb61c91SYazen Ghannam 			.early_channel_count	= f17_early_channel_count,
24042eb61c91SYazen Ghannam 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
24052eb61c91SYazen Ghannam 		}
24062eb61c91SYazen Ghannam 	},
24074d37607aSDoug Thompson };
24084d37607aSDoug Thompson 
2409b1289d6fSDoug Thompson /*
2410bfc04aecSBorislav Petkov  * These are tables of eigenvectors (one per line) which can be used for the
2411bfc04aecSBorislav Petkov  * construction of the syndrome tables. The modified syndrome search algorithm
2412bfc04aecSBorislav Petkov  * uses those to find the symbol in error and thus the DIMM.
2413b1289d6fSDoug Thompson  *
2414bfc04aecSBorislav Petkov  * Algorithm courtesy of Ross LaFetra from AMD.
2415b1289d6fSDoug Thompson  */
2416c7e5301aSDaniel J Blueman static const u16 x4_vectors[] = {
2417bfc04aecSBorislav Petkov 	0x2f57, 0x1afe, 0x66cc, 0xdd88,
2418bfc04aecSBorislav Petkov 	0x11eb, 0x3396, 0x7f4c, 0xeac8,
2419bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008,
2420bfc04aecSBorislav Petkov 	0x1013, 0x3032, 0x4044, 0x8088,
2421bfc04aecSBorislav Petkov 	0x106b, 0x30d6, 0x70fc, 0xe0a8,
2422bfc04aecSBorislav Petkov 	0x4857, 0xc4fe, 0x13cc, 0x3288,
2423bfc04aecSBorislav Petkov 	0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
2424bfc04aecSBorislav Petkov 	0x1f39, 0x251e, 0xbd6c, 0x6bd8,
2425bfc04aecSBorislav Petkov 	0x15c1, 0x2a42, 0x89ac, 0x4758,
2426bfc04aecSBorislav Petkov 	0x2b03, 0x1602, 0x4f0c, 0xca08,
2427bfc04aecSBorislav Petkov 	0x1f07, 0x3a0e, 0x6b04, 0xbd08,
2428bfc04aecSBorislav Petkov 	0x8ba7, 0x465e, 0x244c, 0x1cc8,
2429bfc04aecSBorislav Petkov 	0x2b87, 0x164e, 0x642c, 0xdc18,
2430bfc04aecSBorislav Petkov 	0x40b9, 0x80de, 0x1094, 0x20e8,
2431bfc04aecSBorislav Petkov 	0x27db, 0x1eb6, 0x9dac, 0x7b58,
2432bfc04aecSBorislav Petkov 	0x11c1, 0x2242, 0x84ac, 0x4c58,
2433bfc04aecSBorislav Petkov 	0x1be5, 0x2d7a, 0x5e34, 0xa718,
2434bfc04aecSBorislav Petkov 	0x4b39, 0x8d1e, 0x14b4, 0x28d8,
2435bfc04aecSBorislav Petkov 	0x4c97, 0xc87e, 0x11fc, 0x33a8,
2436bfc04aecSBorislav Petkov 	0x8e97, 0x497e, 0x2ffc, 0x1aa8,
2437bfc04aecSBorislav Petkov 	0x16b3, 0x3d62, 0x4f34, 0x8518,
2438bfc04aecSBorislav Petkov 	0x1e2f, 0x391a, 0x5cac, 0xf858,
2439bfc04aecSBorislav Petkov 	0x1d9f, 0x3b7a, 0x572c, 0xfe18,
2440bfc04aecSBorislav Petkov 	0x15f5, 0x2a5a, 0x5264, 0xa3b8,
2441bfc04aecSBorislav Petkov 	0x1dbb, 0x3b66, 0x715c, 0xe3f8,
2442bfc04aecSBorislav Petkov 	0x4397, 0xc27e, 0x17fc, 0x3ea8,
2443bfc04aecSBorislav Petkov 	0x1617, 0x3d3e, 0x6464, 0xb8b8,
2444bfc04aecSBorislav Petkov 	0x23ff, 0x12aa, 0xab6c, 0x56d8,
2445bfc04aecSBorislav Petkov 	0x2dfb, 0x1ba6, 0x913c, 0x7328,
2446bfc04aecSBorislav Petkov 	0x185d, 0x2ca6, 0x7914, 0x9e28,
2447bfc04aecSBorislav Petkov 	0x171b, 0x3e36, 0x7d7c, 0xebe8,
2448bfc04aecSBorislav Petkov 	0x4199, 0x82ee, 0x19f4, 0x2e58,
2449bfc04aecSBorislav Petkov 	0x4807, 0xc40e, 0x130c, 0x3208,
2450bfc04aecSBorislav Petkov 	0x1905, 0x2e0a, 0x5804, 0xac08,
2451bfc04aecSBorislav Petkov 	0x213f, 0x132a, 0xadfc, 0x5ba8,
2452bfc04aecSBorislav Petkov 	0x19a9, 0x2efe, 0xb5cc, 0x6f88,
2453b1289d6fSDoug Thompson };
2454b1289d6fSDoug Thompson 
2455c7e5301aSDaniel J Blueman static const u16 x8_vectors[] = {
2456bfc04aecSBorislav Petkov 	0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
2457bfc04aecSBorislav Petkov 	0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
2458bfc04aecSBorislav Petkov 	0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
2459bfc04aecSBorislav Petkov 	0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
2460bfc04aecSBorislav Petkov 	0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
2461bfc04aecSBorislav Petkov 	0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
2462bfc04aecSBorislav Petkov 	0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
2463bfc04aecSBorislav Petkov 	0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
2464bfc04aecSBorislav Petkov 	0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
2465bfc04aecSBorislav Petkov 	0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
2466bfc04aecSBorislav Petkov 	0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
2467bfc04aecSBorislav Petkov 	0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
2468bfc04aecSBorislav Petkov 	0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
2469bfc04aecSBorislav Petkov 	0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
2470bfc04aecSBorislav Petkov 	0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
2471bfc04aecSBorislav Petkov 	0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
2472bfc04aecSBorislav Petkov 	0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
2473bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
2474bfc04aecSBorislav Petkov 	0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
2475bfc04aecSBorislav Petkov };
2476bfc04aecSBorislav Petkov 
2477c7e5301aSDaniel J Blueman static int decode_syndrome(u16 syndrome, const u16 *vectors, unsigned num_vecs,
2478d34a6ecdSBorislav Petkov 			   unsigned v_dim)
2479b1289d6fSDoug Thompson {
2480bfc04aecSBorislav Petkov 	unsigned int i, err_sym;
2481b1289d6fSDoug Thompson 
2482bfc04aecSBorislav Petkov 	for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
2483bfc04aecSBorislav Petkov 		u16 s = syndrome;
2484d34a6ecdSBorislav Petkov 		unsigned v_idx =  err_sym * v_dim;
2485d34a6ecdSBorislav Petkov 		unsigned v_end = (err_sym + 1) * v_dim;
2486b1289d6fSDoug Thompson 
2487bfc04aecSBorislav Petkov 		/* walk over all 16 bits of the syndrome */
2488bfc04aecSBorislav Petkov 		for (i = 1; i < (1U << 16); i <<= 1) {
2489bfc04aecSBorislav Petkov 
2490bfc04aecSBorislav Petkov 			/* if bit is set in that eigenvector... */
2491bfc04aecSBorislav Petkov 			if (v_idx < v_end && vectors[v_idx] & i) {
2492bfc04aecSBorislav Petkov 				u16 ev_comp = vectors[v_idx++];
2493bfc04aecSBorislav Petkov 
2494bfc04aecSBorislav Petkov 				/* ... and bit set in the modified syndrome, */
2495bfc04aecSBorislav Petkov 				if (s & i) {
2496bfc04aecSBorislav Petkov 					/* remove it. */
2497bfc04aecSBorislav Petkov 					s ^= ev_comp;
2498bfc04aecSBorislav Petkov 
2499bfc04aecSBorislav Petkov 					if (!s)
2500bfc04aecSBorislav Petkov 						return err_sym;
2501bfc04aecSBorislav Petkov 				}
2502bfc04aecSBorislav Petkov 
2503bfc04aecSBorislav Petkov 			} else if (s & i)
2504bfc04aecSBorislav Petkov 				/* can't get to zero, move to next symbol */
2505bfc04aecSBorislav Petkov 				break;
2506bfc04aecSBorislav Petkov 		}
2507b1289d6fSDoug Thompson 	}
2508b1289d6fSDoug Thompson 
2509956b9ba1SJoe Perches 	edac_dbg(0, "syndrome(%x) not found\n", syndrome);
2510b1289d6fSDoug Thompson 	return -1;
2511b1289d6fSDoug Thompson }
2512d27bf6faSDoug Thompson 
2513bfc04aecSBorislav Petkov static int map_err_sym_to_channel(int err_sym, int sym_size)
2514bfc04aecSBorislav Petkov {
2515bfc04aecSBorislav Petkov 	if (sym_size == 4)
2516bfc04aecSBorislav Petkov 		switch (err_sym) {
2517bfc04aecSBorislav Petkov 		case 0x20:
2518bfc04aecSBorislav Petkov 		case 0x21:
2519bfc04aecSBorislav Petkov 			return 0;
2520bfc04aecSBorislav Petkov 		case 0x22:
2521bfc04aecSBorislav Petkov 		case 0x23:
2522bfc04aecSBorislav Petkov 			return 1;
2523bfc04aecSBorislav Petkov 		default:
2524bfc04aecSBorislav Petkov 			return err_sym >> 4;
2525bfc04aecSBorislav Petkov 		}
2526bfc04aecSBorislav Petkov 	/* x8 symbols */
2527bfc04aecSBorislav Petkov 	else
2528bfc04aecSBorislav Petkov 		switch (err_sym) {
2529bfc04aecSBorislav Petkov 		/* imaginary bits not in a DIMM */
2530bfc04aecSBorislav Petkov 		case 0x10:
2531bfc04aecSBorislav Petkov 			WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
2532bfc04aecSBorislav Petkov 					  err_sym);
2533bfc04aecSBorislav Petkov 			return -1;
2534bfc04aecSBorislav Petkov 		case 0x11:
2535bfc04aecSBorislav Petkov 			return 0;
2536bfc04aecSBorislav Petkov 		case 0x12:
2537bfc04aecSBorislav Petkov 			return 1;
2538bfc04aecSBorislav Petkov 		default:
2539bfc04aecSBorislav Petkov 			return err_sym >> 3;
2540bfc04aecSBorislav Petkov 		}
2541bfc04aecSBorislav Petkov 	return -1;
2542bfc04aecSBorislav Petkov }
2543bfc04aecSBorislav Petkov 
2544bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
2545bfc04aecSBorislav Petkov {
2546bfc04aecSBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
2547ad6a32e9SBorislav Petkov 	int err_sym = -1;
2548bfc04aecSBorislav Petkov 
2549a3b7db09SBorislav Petkov 	if (pvt->ecc_sym_sz == 8)
2550bfc04aecSBorislav Petkov 		err_sym = decode_syndrome(syndrome, x8_vectors,
2551ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x8_vectors),
2552a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
2553a3b7db09SBorislav Petkov 	else if (pvt->ecc_sym_sz == 4)
2554ad6a32e9SBorislav Petkov 		err_sym = decode_syndrome(syndrome, x4_vectors,
2555ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x4_vectors),
2556a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
2557ad6a32e9SBorislav Petkov 	else {
2558a3b7db09SBorislav Petkov 		amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz);
2559ad6a32e9SBorislav Petkov 		return err_sym;
2560bfc04aecSBorislav Petkov 	}
2561ad6a32e9SBorislav Petkov 
2562a3b7db09SBorislav Petkov 	return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz);
256341c31044SBorislav Petkov }
2564bfc04aecSBorislav Petkov 
2565e70984d9SYazen Ghannam static void __log_ecc_error(struct mem_ctl_info *mci, struct err_info *err,
256633ca0643SBorislav Petkov 			    u8 ecc_type)
2567d27bf6faSDoug Thompson {
256833ca0643SBorislav Petkov 	enum hw_event_mc_err_type err_type;
256933ca0643SBorislav Petkov 	const char *string;
2570d27bf6faSDoug Thompson 
257133ca0643SBorislav Petkov 	if (ecc_type == 2)
257233ca0643SBorislav Petkov 		err_type = HW_EVENT_ERR_CORRECTED;
257333ca0643SBorislav Petkov 	else if (ecc_type == 1)
257433ca0643SBorislav Petkov 		err_type = HW_EVENT_ERR_UNCORRECTED;
2575d12a969eSYazen Ghannam 	else if (ecc_type == 3)
2576d12a969eSYazen Ghannam 		err_type = HW_EVENT_ERR_DEFERRED;
257733ca0643SBorislav Petkov 	else {
257833ca0643SBorislav Petkov 		WARN(1, "Something is rotten in the state of Denmark.\n");
2579d27bf6faSDoug Thompson 		return;
2580d27bf6faSDoug Thompson 	}
2581d27bf6faSDoug Thompson 
258233ca0643SBorislav Petkov 	switch (err->err_code) {
258333ca0643SBorislav Petkov 	case DECODE_OK:
258433ca0643SBorislav Petkov 		string = "";
258533ca0643SBorislav Petkov 		break;
258633ca0643SBorislav Petkov 	case ERR_NODE:
258733ca0643SBorislav Petkov 		string = "Failed to map error addr to a node";
258833ca0643SBorislav Petkov 		break;
258933ca0643SBorislav Petkov 	case ERR_CSROW:
259033ca0643SBorislav Petkov 		string = "Failed to map error addr to a csrow";
259133ca0643SBorislav Petkov 		break;
259233ca0643SBorislav Petkov 	case ERR_CHANNEL:
2593713ad546SYazen Ghannam 		string = "Unknown syndrome - possible error reporting race";
2594713ad546SYazen Ghannam 		break;
2595713ad546SYazen Ghannam 	case ERR_SYND:
2596713ad546SYazen Ghannam 		string = "MCA_SYND not valid - unknown syndrome and csrow";
2597713ad546SYazen Ghannam 		break;
2598713ad546SYazen Ghannam 	case ERR_NORM_ADDR:
2599713ad546SYazen Ghannam 		string = "Cannot decode normalized address";
260033ca0643SBorislav Petkov 		break;
260133ca0643SBorislav Petkov 	default:
260233ca0643SBorislav Petkov 		string = "WTF error";
260333ca0643SBorislav Petkov 		break;
2604d27bf6faSDoug Thompson 	}
260533ca0643SBorislav Petkov 
260633ca0643SBorislav Petkov 	edac_mc_handle_error(err_type, mci, 1,
260733ca0643SBorislav Petkov 			     err->page, err->offset, err->syndrome,
260833ca0643SBorislav Petkov 			     err->csrow, err->channel, -1,
260933ca0643SBorislav Petkov 			     string, "");
2610d27bf6faSDoug Thompson }
2611d27bf6faSDoug Thompson 
2612df781d03SBorislav Petkov static inline void decode_bus_error(int node_id, struct mce *m)
2613d27bf6faSDoug Thompson {
26140c510cc8SDaniel J Blueman 	struct mem_ctl_info *mci;
26150c510cc8SDaniel J Blueman 	struct amd64_pvt *pvt;
2616f192c7b1SBorislav Petkov 	u8 ecc_type = (m->status >> 45) & 0x3;
261766fed2d4SBorislav Petkov 	u8 xec = XEC(m->status, 0x1f);
261866fed2d4SBorislav Petkov 	u16 ec = EC(m->status);
261933ca0643SBorislav Petkov 	u64 sys_addr;
262033ca0643SBorislav Petkov 	struct err_info err;
2621d27bf6faSDoug Thompson 
26220c510cc8SDaniel J Blueman 	mci = edac_mc_find(node_id);
26230c510cc8SDaniel J Blueman 	if (!mci)
26240c510cc8SDaniel J Blueman 		return;
26250c510cc8SDaniel J Blueman 
26260c510cc8SDaniel J Blueman 	pvt = mci->pvt_info;
26270c510cc8SDaniel J Blueman 
262866fed2d4SBorislav Petkov 	/* Bail out early if this was an 'observed' error */
26295980bb9cSBorislav Petkov 	if (PP(ec) == NBSL_PP_OBS)
2630b70ef010SBorislav Petkov 		return;
2631d27bf6faSDoug Thompson 
2632ecaf5606SBorislav Petkov 	/* Do only ECC errors */
2633ecaf5606SBorislav Petkov 	if (xec && xec != F10_NBSL_EXT_ERR_ECC)
2634d27bf6faSDoug Thompson 		return;
2635d27bf6faSDoug Thompson 
263633ca0643SBorislav Petkov 	memset(&err, 0, sizeof(err));
263733ca0643SBorislav Petkov 
2638a4b4bedcSBorislav Petkov 	sys_addr = get_error_address(pvt, m);
263933ca0643SBorislav Petkov 
2640ecaf5606SBorislav Petkov 	if (ecc_type == 2)
264133ca0643SBorislav Petkov 		err.syndrome = extract_syndrome(m->status);
264233ca0643SBorislav Petkov 
264333ca0643SBorislav Petkov 	pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err);
264433ca0643SBorislav Petkov 
2645e70984d9SYazen Ghannam 	__log_ecc_error(mci, &err, ecc_type);
2646d27bf6faSDoug Thompson }
2647d27bf6faSDoug Thompson 
26480ec449eeSDoug Thompson /*
2649713ad546SYazen Ghannam  * To find the UMC channel represented by this bank we need to match on its
2650713ad546SYazen Ghannam  * instance_id. The instance_id of a bank is held in the lower 32 bits of its
2651713ad546SYazen Ghannam  * IPID.
2652bdcee774SYazen Ghannam  *
2653bdcee774SYazen Ghannam  * Currently, we can derive the channel number by looking at the 6th nibble in
2654bdcee774SYazen Ghannam  * the instance_id. For example, instance_id=0xYXXXXX where Y is the channel
2655bdcee774SYazen Ghannam  * number.
2656713ad546SYazen Ghannam  */
2657bdcee774SYazen Ghannam static int find_umc_channel(struct mce *m)
2658713ad546SYazen Ghannam {
2659bdcee774SYazen Ghannam 	return (m->ipid & GENMASK(31, 0)) >> 20;
2660713ad546SYazen Ghannam }
2661713ad546SYazen Ghannam 
2662713ad546SYazen Ghannam static void decode_umc_error(int node_id, struct mce *m)
2663713ad546SYazen Ghannam {
2664713ad546SYazen Ghannam 	u8 ecc_type = (m->status >> 45) & 0x3;
2665713ad546SYazen Ghannam 	struct mem_ctl_info *mci;
2666713ad546SYazen Ghannam 	struct amd64_pvt *pvt;
2667713ad546SYazen Ghannam 	struct err_info err;
2668713ad546SYazen Ghannam 	u64 sys_addr;
2669713ad546SYazen Ghannam 
2670713ad546SYazen Ghannam 	mci = edac_mc_find(node_id);
2671713ad546SYazen Ghannam 	if (!mci)
2672713ad546SYazen Ghannam 		return;
2673713ad546SYazen Ghannam 
2674713ad546SYazen Ghannam 	pvt = mci->pvt_info;
2675713ad546SYazen Ghannam 
2676713ad546SYazen Ghannam 	memset(&err, 0, sizeof(err));
2677713ad546SYazen Ghannam 
2678713ad546SYazen Ghannam 	if (m->status & MCI_STATUS_DEFERRED)
2679713ad546SYazen Ghannam 		ecc_type = 3;
2680713ad546SYazen Ghannam 
2681bdcee774SYazen Ghannam 	err.channel = find_umc_channel(m);
2682713ad546SYazen Ghannam 
2683713ad546SYazen Ghannam 	if (!(m->status & MCI_STATUS_SYNDV)) {
2684713ad546SYazen Ghannam 		err.err_code = ERR_SYND;
2685713ad546SYazen Ghannam 		goto log_error;
2686713ad546SYazen Ghannam 	}
2687713ad546SYazen Ghannam 
2688713ad546SYazen Ghannam 	if (ecc_type == 2) {
2689713ad546SYazen Ghannam 		u8 length = (m->synd >> 18) & 0x3f;
2690713ad546SYazen Ghannam 
2691713ad546SYazen Ghannam 		if (length)
2692713ad546SYazen Ghannam 			err.syndrome = (m->synd >> 32) & GENMASK(length - 1, 0);
2693713ad546SYazen Ghannam 		else
2694713ad546SYazen Ghannam 			err.err_code = ERR_CHANNEL;
2695713ad546SYazen Ghannam 	}
2696713ad546SYazen Ghannam 
2697713ad546SYazen Ghannam 	err.csrow = m->synd & 0x7;
2698713ad546SYazen Ghannam 
26998a2eaab7SYazen Ghannam 	if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) {
27008a2eaab7SYazen Ghannam 		err.err_code = ERR_NORM_ADDR;
27018a2eaab7SYazen Ghannam 		goto log_error;
27028a2eaab7SYazen Ghannam 	}
27038a2eaab7SYazen Ghannam 
27048a2eaab7SYazen Ghannam 	error_address_to_page_and_offset(sys_addr, &err);
27058a2eaab7SYazen Ghannam 
2706713ad546SYazen Ghannam log_error:
2707713ad546SYazen Ghannam 	__log_ecc_error(mci, &err, ecc_type);
2708713ad546SYazen Ghannam }
2709713ad546SYazen Ghannam 
2710713ad546SYazen Ghannam /*
27113f37a36bSBorislav Petkov  * Use pvt->F3 which contains the F3 CPU PCI device to get the related
27123f37a36bSBorislav Petkov  * F1 (AddrMap) and F2 (Dct) devices. Return negative value on error.
2713936fc3afSYazen Ghannam  * Reserve F0 and F6 on systems with a UMC.
27140ec449eeSDoug Thompson  */
2715936fc3afSYazen Ghannam static int
2716936fc3afSYazen Ghannam reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2)
27170ec449eeSDoug Thompson {
2718936fc3afSYazen Ghannam 	if (pvt->umc) {
2719936fc3afSYazen Ghannam 		pvt->F0 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
2720936fc3afSYazen Ghannam 		if (!pvt->F0) {
27216a4afe38SYazen Ghannam 			edac_dbg(1, "F0 not found, device 0x%x\n", pci_id1);
2722936fc3afSYazen Ghannam 			return -ENODEV;
2723936fc3afSYazen Ghannam 		}
2724936fc3afSYazen Ghannam 
2725936fc3afSYazen Ghannam 		pvt->F6 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3);
2726936fc3afSYazen Ghannam 		if (!pvt->F6) {
2727936fc3afSYazen Ghannam 			pci_dev_put(pvt->F0);
2728936fc3afSYazen Ghannam 			pvt->F0 = NULL;
2729936fc3afSYazen Ghannam 
27306a4afe38SYazen Ghannam 			edac_dbg(1, "F6 not found: device 0x%x\n", pci_id2);
2731936fc3afSYazen Ghannam 			return -ENODEV;
2732936fc3afSYazen Ghannam 		}
27335246c540SBorislav Petkov 
2734706657b1SBorislav Petkov 		if (!pci_ctl_dev)
2735706657b1SBorislav Petkov 			pci_ctl_dev = &pvt->F0->dev;
2736706657b1SBorislav Petkov 
2737936fc3afSYazen Ghannam 		edac_dbg(1, "F0: %s\n", pci_name(pvt->F0));
2738936fc3afSYazen Ghannam 		edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
2739936fc3afSYazen Ghannam 		edac_dbg(1, "F6: %s\n", pci_name(pvt->F6));
2740936fc3afSYazen Ghannam 
2741936fc3afSYazen Ghannam 		return 0;
2742936fc3afSYazen Ghannam 	}
2743936fc3afSYazen Ghannam 
27440ec449eeSDoug Thompson 	/* Reserve the ADDRESS MAP Device */
2745936fc3afSYazen Ghannam 	pvt->F1 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
27468d5b5d9cSBorislav Petkov 	if (!pvt->F1) {
27476a4afe38SYazen Ghannam 		edac_dbg(1, "F1 not found: device 0x%x\n", pci_id1);
2748bbd0c1f6SBorislav Petkov 		return -ENODEV;
27490ec449eeSDoug Thompson 	}
27500ec449eeSDoug Thompson 
27513f37a36bSBorislav Petkov 	/* Reserve the DCT Device */
2752936fc3afSYazen Ghannam 	pvt->F2 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3);
27533f37a36bSBorislav Petkov 	if (!pvt->F2) {
27548d5b5d9cSBorislav Petkov 		pci_dev_put(pvt->F1);
27558d5b5d9cSBorislav Petkov 		pvt->F1 = NULL;
27560ec449eeSDoug Thompson 
27576a4afe38SYazen Ghannam 		edac_dbg(1, "F2 not found: device 0x%x\n", pci_id2);
2758bbd0c1f6SBorislav Petkov 		return -ENODEV;
27590ec449eeSDoug Thompson 	}
2760936fc3afSYazen Ghannam 
2761706657b1SBorislav Petkov 	if (!pci_ctl_dev)
2762706657b1SBorislav Petkov 		pci_ctl_dev = &pvt->F2->dev;
2763706657b1SBorislav Petkov 
2764956b9ba1SJoe Perches 	edac_dbg(1, "F1: %s\n", pci_name(pvt->F1));
2765956b9ba1SJoe Perches 	edac_dbg(1, "F2: %s\n", pci_name(pvt->F2));
2766956b9ba1SJoe Perches 	edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
27670ec449eeSDoug Thompson 
27680ec449eeSDoug Thompson 	return 0;
27690ec449eeSDoug Thompson }
27700ec449eeSDoug Thompson 
2771360b7f3cSBorislav Petkov static void free_mc_sibling_devs(struct amd64_pvt *pvt)
27720ec449eeSDoug Thompson {
2773936fc3afSYazen Ghannam 	if (pvt->umc) {
2774936fc3afSYazen Ghannam 		pci_dev_put(pvt->F0);
2775936fc3afSYazen Ghannam 		pci_dev_put(pvt->F6);
2776936fc3afSYazen Ghannam 	} else {
27778d5b5d9cSBorislav Petkov 		pci_dev_put(pvt->F1);
27783f37a36bSBorislav Petkov 		pci_dev_put(pvt->F2);
27790ec449eeSDoug Thompson 	}
2780936fc3afSYazen Ghannam }
27810ec449eeSDoug Thompson 
2782b64ce7cdSYazen Ghannam static void determine_ecc_sym_sz(struct amd64_pvt *pvt)
2783b64ce7cdSYazen Ghannam {
2784b64ce7cdSYazen Ghannam 	pvt->ecc_sym_sz = 4;
2785b64ce7cdSYazen Ghannam 
2786b64ce7cdSYazen Ghannam 	if (pvt->umc) {
2787b64ce7cdSYazen Ghannam 		u8 i;
2788b64ce7cdSYazen Ghannam 
27894d30d2bcSYazen Ghannam 		for_each_umc(i) {
2790b64ce7cdSYazen Ghannam 			/* Check enabled channels only: */
27917835961dSYazen Ghannam 			if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) {
27927835961dSYazen Ghannam 				if (pvt->umc[i].ecc_ctrl & BIT(9)) {
27937835961dSYazen Ghannam 					pvt->ecc_sym_sz = 16;
27947835961dSYazen Ghannam 					return;
27957835961dSYazen Ghannam 				} else if (pvt->umc[i].ecc_ctrl & BIT(7)) {
2796b64ce7cdSYazen Ghannam 					pvt->ecc_sym_sz = 8;
2797b64ce7cdSYazen Ghannam 					return;
2798b64ce7cdSYazen Ghannam 				}
27997835961dSYazen Ghannam 			}
28007835961dSYazen Ghannam 		}
28017835961dSYazen Ghannam 	} else if (pvt->fam >= 0x10) {
2802b64ce7cdSYazen Ghannam 		u32 tmp;
2803b64ce7cdSYazen Ghannam 
2804b64ce7cdSYazen Ghannam 		amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp);
2805b64ce7cdSYazen Ghannam 		/* F16h has only DCT0, so no need to read dbam1. */
2806b64ce7cdSYazen Ghannam 		if (pvt->fam != 0x16)
2807b64ce7cdSYazen Ghannam 			amd64_read_dct_pci_cfg(pvt, 1, DBAM0, &pvt->dbam1);
2808b64ce7cdSYazen Ghannam 
2809b64ce7cdSYazen Ghannam 		/* F10h, revD and later can do x8 ECC too. */
2810b64ce7cdSYazen Ghannam 		if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25))
2811b64ce7cdSYazen Ghannam 			pvt->ecc_sym_sz = 8;
2812b64ce7cdSYazen Ghannam 	}
2813b64ce7cdSYazen Ghannam }
2814b64ce7cdSYazen Ghannam 
2815b64ce7cdSYazen Ghannam /*
2816b64ce7cdSYazen Ghannam  * Retrieve the hardware registers of the memory controller.
2817b64ce7cdSYazen Ghannam  */
2818b64ce7cdSYazen Ghannam static void __read_mc_regs_df(struct amd64_pvt *pvt)
2819b64ce7cdSYazen Ghannam {
2820b64ce7cdSYazen Ghannam 	u8 nid = pvt->mc_node_id;
2821b64ce7cdSYazen Ghannam 	struct amd64_umc *umc;
2822b64ce7cdSYazen Ghannam 	u32 i, umc_base;
2823b64ce7cdSYazen Ghannam 
2824b64ce7cdSYazen Ghannam 	/* Read registers from each UMC */
28254d30d2bcSYazen Ghannam 	for_each_umc(i) {
2826b64ce7cdSYazen Ghannam 
2827b64ce7cdSYazen Ghannam 		umc_base = get_umc_base(i);
2828b64ce7cdSYazen Ghannam 		umc = &pvt->umc[i];
2829b64ce7cdSYazen Ghannam 
283007ed82efSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_DIMM_CFG, &umc->dimm_cfg);
283107ed82efSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_UMC_CFG, &umc->umc_cfg);
2832b64ce7cdSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_SDP_CTRL, &umc->sdp_ctrl);
2833b64ce7cdSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_ECC_CTRL, &umc->ecc_ctrl);
283407ed82efSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_UMC_CAP_HI, &umc->umc_cap_hi);
2835b64ce7cdSYazen Ghannam 	}
2836b64ce7cdSYazen Ghannam }
2837b64ce7cdSYazen Ghannam 
28380ec449eeSDoug Thompson /*
28390ec449eeSDoug Thompson  * Retrieve the hardware registers of the memory controller (this includes the
28400ec449eeSDoug Thompson  * 'Address Map' and 'Misc' device regs)
28410ec449eeSDoug Thompson  */
2842360b7f3cSBorislav Petkov static void read_mc_regs(struct amd64_pvt *pvt)
28430ec449eeSDoug Thompson {
2844b64ce7cdSYazen Ghannam 	unsigned int range;
28450ec449eeSDoug Thompson 	u64 msr_val;
28460ec449eeSDoug Thompson 
28470ec449eeSDoug Thompson 	/*
28480ec449eeSDoug Thompson 	 * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
2849b64ce7cdSYazen Ghannam 	 * those are Read-As-Zero.
28500ec449eeSDoug Thompson 	 */
2851e97f8bb8SBorislav Petkov 	rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
2852956b9ba1SJoe Perches 	edac_dbg(0, "  TOP_MEM:  0x%016llx\n", pvt->top_mem);
28530ec449eeSDoug Thompson 
2854b64ce7cdSYazen Ghannam 	/* Check first whether TOP_MEM2 is enabled: */
28550ec449eeSDoug Thompson 	rdmsrl(MSR_K8_SYSCFG, msr_val);
2856b64ce7cdSYazen Ghannam 	if (msr_val & BIT(21)) {
2857e97f8bb8SBorislav Petkov 		rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
2858956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
2859b64ce7cdSYazen Ghannam 	} else {
2860956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2 disabled\n");
2861b64ce7cdSYazen Ghannam 	}
2862b64ce7cdSYazen Ghannam 
2863b64ce7cdSYazen Ghannam 	if (pvt->umc) {
2864b64ce7cdSYazen Ghannam 		__read_mc_regs_df(pvt);
2865b64ce7cdSYazen Ghannam 		amd64_read_pci_cfg(pvt->F0, DF_DHAR, &pvt->dhar);
2866b64ce7cdSYazen Ghannam 
2867b64ce7cdSYazen Ghannam 		goto skip;
2868b64ce7cdSYazen Ghannam 	}
28690ec449eeSDoug Thompson 
28705980bb9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap);
28710ec449eeSDoug Thompson 
28725a5d2371SBorislav Petkov 	read_dram_ctl_register(pvt);
28730ec449eeSDoug Thompson 
28747f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
28757f19bf75SBorislav Petkov 		u8 rw;
28760ec449eeSDoug Thompson 
28777f19bf75SBorislav Petkov 		/* read settings for this DRAM range */
28787f19bf75SBorislav Petkov 		read_dram_base_limit_regs(pvt, range);
2879e97f8bb8SBorislav Petkov 
28807f19bf75SBorislav Petkov 		rw = dram_rw(pvt, range);
28817f19bf75SBorislav Petkov 		if (!rw)
28827f19bf75SBorislav Petkov 			continue;
28837f19bf75SBorislav Petkov 
2884956b9ba1SJoe Perches 		edac_dbg(1, "  DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
28857f19bf75SBorislav Petkov 			 range,
28867f19bf75SBorislav Petkov 			 get_dram_base(pvt, range),
28877f19bf75SBorislav Petkov 			 get_dram_limit(pvt, range));
28887f19bf75SBorislav Petkov 
2889956b9ba1SJoe Perches 		edac_dbg(1, "   IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
28907f19bf75SBorislav Petkov 			 dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
28917f19bf75SBorislav Petkov 			 (rw & 0x1) ? "R" : "-",
28927f19bf75SBorislav Petkov 			 (rw & 0x2) ? "W" : "-",
28937f19bf75SBorislav Petkov 			 dram_intlv_sel(pvt, range),
28947f19bf75SBorislav Petkov 			 dram_dst_node(pvt, range));
28950ec449eeSDoug Thompson 	}
28960ec449eeSDoug Thompson 
2897bc21fa57SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar);
28987981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DBAM0, &pvt->dbam0);
28990ec449eeSDoug Thompson 
29008d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare);
29010ec449eeSDoug Thompson 
29027981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DCLR0, &pvt->dclr0);
29037981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DCHR0, &pvt->dchr0);
29040ec449eeSDoug Thompson 
290578da121eSBorislav Petkov 	if (!dct_ganging_enabled(pvt)) {
29067981a28fSAravind Gopalakrishnan 		amd64_read_dct_pci_cfg(pvt, 1, DCLR0, &pvt->dclr1);
29077981a28fSAravind Gopalakrishnan 		amd64_read_dct_pci_cfg(pvt, 1, DCHR0, &pvt->dchr1);
29080ec449eeSDoug Thompson 	}
2909b2b0c605SBorislav Petkov 
2910b64ce7cdSYazen Ghannam skip:
2911b64ce7cdSYazen Ghannam 	read_dct_base_mask(pvt);
2912b64ce7cdSYazen Ghannam 
2913a597d2a5SAravind Gopalakrishnan 	determine_memory_type(pvt);
2914a597d2a5SAravind Gopalakrishnan 	edac_dbg(1, "  DIMM type: %s\n", edac_mem_types[pvt->dram_type]);
2915a3b7db09SBorislav Petkov 
2916b64ce7cdSYazen Ghannam 	determine_ecc_sym_sz(pvt);
29170ec449eeSDoug Thompson }
29180ec449eeSDoug Thompson 
29190ec449eeSDoug Thompson /*
29200ec449eeSDoug Thompson  * NOTE: CPU Revision Dependent code
29210ec449eeSDoug Thompson  *
29220ec449eeSDoug Thompson  * Input:
292311c75eadSBorislav Petkov  *	@csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1)
29240ec449eeSDoug Thompson  *	k8 private pointer to -->
29250ec449eeSDoug Thompson  *			DRAM Bank Address mapping register
29260ec449eeSDoug Thompson  *			node_id
29270ec449eeSDoug Thompson  *			DCL register where dual_channel_active is
29280ec449eeSDoug Thompson  *
29290ec449eeSDoug Thompson  * The DBAM register consists of 4 sets of 4 bits each definitions:
29300ec449eeSDoug Thompson  *
29310ec449eeSDoug Thompson  * Bits:	CSROWs
29320ec449eeSDoug Thompson  * 0-3		CSROWs 0 and 1
29330ec449eeSDoug Thompson  * 4-7		CSROWs 2 and 3
29340ec449eeSDoug Thompson  * 8-11		CSROWs 4 and 5
29350ec449eeSDoug Thompson  * 12-15	CSROWs 6 and 7
29360ec449eeSDoug Thompson  *
29370ec449eeSDoug Thompson  * Values range from: 0 to 15
29380ec449eeSDoug Thompson  * The meaning of the values depends on CPU revision and dual-channel state,
29390ec449eeSDoug Thompson  * see relevant BKDG more info.
29400ec449eeSDoug Thompson  *
29410ec449eeSDoug Thompson  * The memory controller provides for total of only 8 CSROWs in its current
29420ec449eeSDoug Thompson  * architecture. Each "pair" of CSROWs normally represents just one DIMM in
29430ec449eeSDoug Thompson  * single channel or two (2) DIMMs in dual channel mode.
29440ec449eeSDoug Thompson  *
29450ec449eeSDoug Thompson  * The following code logic collapses the various tables for CSROW based on CPU
29460ec449eeSDoug Thompson  * revision.
29470ec449eeSDoug Thompson  *
29480ec449eeSDoug Thompson  * Returns:
29490ec449eeSDoug Thompson  *	The number of PAGE_SIZE pages on the specified CSROW number it
29500ec449eeSDoug Thompson  *	encompasses
29510ec449eeSDoug Thompson  *
29520ec449eeSDoug Thompson  */
2953eb77e6b8SYazen Ghannam static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr_orig)
29540ec449eeSDoug Thompson {
2955f92cae45SAshish Shenoy 	u32 dbam = dct ? pvt->dbam1 : pvt->dbam0;
2956eb77e6b8SYazen Ghannam 	int csrow_nr = csrow_nr_orig;
2957eb77e6b8SYazen Ghannam 	u32 cs_mode, nr_pages;
29580ec449eeSDoug Thompson 
2959e53a3b26SYazen Ghannam 	if (!pvt->umc) {
2960eb77e6b8SYazen Ghannam 		csrow_nr >>= 1;
2961eb77e6b8SYazen Ghannam 		cs_mode = DBAM_DIMM(csrow_nr, dbam);
2962e53a3b26SYazen Ghannam 	} else {
2963e53a3b26SYazen Ghannam 		cs_mode = f17_get_cs_mode(csrow_nr >> 1, dct, pvt);
2964e53a3b26SYazen Ghannam 	}
29650ec449eeSDoug Thompson 
2966eb77e6b8SYazen Ghannam 	nr_pages   = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, csrow_nr);
2967eb77e6b8SYazen Ghannam 	nr_pages <<= 20 - PAGE_SHIFT;
29680ec449eeSDoug Thompson 
296910de6497SBorislav Petkov 	edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n",
2970eb77e6b8SYazen Ghannam 		    csrow_nr_orig, dct,  cs_mode);
297110de6497SBorislav Petkov 	edac_dbg(0, "nr_pages/channel: %u\n", nr_pages);
29720ec449eeSDoug Thompson 
29730ec449eeSDoug Thompson 	return nr_pages;
29740ec449eeSDoug Thompson }
29750ec449eeSDoug Thompson 
2976353a1fcbSYazen Ghannam static int init_csrows_df(struct mem_ctl_info *mci)
2977353a1fcbSYazen Ghannam {
2978353a1fcbSYazen Ghannam 	struct amd64_pvt *pvt = mci->pvt_info;
2979353a1fcbSYazen Ghannam 	enum edac_type edac_mode = EDAC_NONE;
2980353a1fcbSYazen Ghannam 	enum dev_type dev_type = DEV_UNKNOWN;
2981353a1fcbSYazen Ghannam 	struct dimm_info *dimm;
2982353a1fcbSYazen Ghannam 	int empty = 1;
2983353a1fcbSYazen Ghannam 	u8 umc, cs;
2984353a1fcbSYazen Ghannam 
2985353a1fcbSYazen Ghannam 	if (mci->edac_ctl_cap & EDAC_FLAG_S16ECD16ED) {
2986353a1fcbSYazen Ghannam 		edac_mode = EDAC_S16ECD16ED;
2987353a1fcbSYazen Ghannam 		dev_type = DEV_X16;
2988353a1fcbSYazen Ghannam 	} else if (mci->edac_ctl_cap & EDAC_FLAG_S8ECD8ED) {
2989353a1fcbSYazen Ghannam 		edac_mode = EDAC_S8ECD8ED;
2990353a1fcbSYazen Ghannam 		dev_type = DEV_X8;
2991353a1fcbSYazen Ghannam 	} else if (mci->edac_ctl_cap & EDAC_FLAG_S4ECD4ED) {
2992353a1fcbSYazen Ghannam 		edac_mode = EDAC_S4ECD4ED;
2993353a1fcbSYazen Ghannam 		dev_type = DEV_X4;
2994353a1fcbSYazen Ghannam 	} else if (mci->edac_ctl_cap & EDAC_FLAG_SECDED) {
2995353a1fcbSYazen Ghannam 		edac_mode = EDAC_SECDED;
2996353a1fcbSYazen Ghannam 	}
2997353a1fcbSYazen Ghannam 
2998353a1fcbSYazen Ghannam 	for_each_umc(umc) {
2999353a1fcbSYazen Ghannam 		for_each_chip_select(cs, umc, pvt) {
3000353a1fcbSYazen Ghannam 			if (!csrow_enabled(cs, umc, pvt))
3001353a1fcbSYazen Ghannam 				continue;
3002353a1fcbSYazen Ghannam 
3003353a1fcbSYazen Ghannam 			empty = 0;
3004353a1fcbSYazen Ghannam 			dimm = mci->csrows[cs]->channels[umc]->dimm;
3005353a1fcbSYazen Ghannam 
3006353a1fcbSYazen Ghannam 			edac_dbg(1, "MC node: %d, csrow: %d\n",
3007353a1fcbSYazen Ghannam 					pvt->mc_node_id, cs);
3008353a1fcbSYazen Ghannam 
3009353a1fcbSYazen Ghannam 			dimm->nr_pages = get_csrow_nr_pages(pvt, umc, cs);
3010353a1fcbSYazen Ghannam 			dimm->mtype = pvt->dram_type;
3011353a1fcbSYazen Ghannam 			dimm->edac_mode = edac_mode;
3012353a1fcbSYazen Ghannam 			dimm->dtype = dev_type;
3013466503d6SYazen Ghannam 			dimm->grain = 64;
3014353a1fcbSYazen Ghannam 		}
3015353a1fcbSYazen Ghannam 	}
3016353a1fcbSYazen Ghannam 
3017353a1fcbSYazen Ghannam 	return empty;
3018353a1fcbSYazen Ghannam }
3019353a1fcbSYazen Ghannam 
30200ec449eeSDoug Thompson /*
30210ec449eeSDoug Thompson  * Initialize the array of csrow attribute instances, based on the values
30220ec449eeSDoug Thompson  * from pci config hardware registers.
30230ec449eeSDoug Thompson  */
3024360b7f3cSBorislav Petkov static int init_csrows(struct mem_ctl_info *mci)
30250ec449eeSDoug Thompson {
302610de6497SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
30272d09d8f3SYazen Ghannam 	enum edac_type edac_mode = EDAC_NONE;
30280ec449eeSDoug Thompson 	struct csrow_info *csrow;
3029de3910ebSMauro Carvalho Chehab 	struct dimm_info *dimm;
303010de6497SBorislav Petkov 	int i, j, empty = 1;
3031a895bf8bSMauro Carvalho Chehab 	int nr_pages = 0;
303210de6497SBorislav Petkov 	u32 val;
30330ec449eeSDoug Thompson 
3034353a1fcbSYazen Ghannam 	if (pvt->umc)
3035353a1fcbSYazen Ghannam 		return init_csrows_df(mci);
3036353a1fcbSYazen Ghannam 
3037a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
30380ec449eeSDoug Thompson 
30392299ef71SBorislav Petkov 	pvt->nbcfg = val;
30400ec449eeSDoug Thompson 
3041956b9ba1SJoe Perches 	edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
30422299ef71SBorislav Petkov 		 pvt->mc_node_id, val,
3043a97fa68eSBorislav Petkov 		 !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
30440ec449eeSDoug Thompson 
304510de6497SBorislav Petkov 	/*
304610de6497SBorislav Petkov 	 * We iterate over DCT0 here but we look at DCT1 in parallel, if needed.
304710de6497SBorislav Petkov 	 */
304811c75eadSBorislav Petkov 	for_each_chip_select(i, 0, pvt) {
304910de6497SBorislav Petkov 		bool row_dct0 = !!csrow_enabled(i, 0, pvt);
305010de6497SBorislav Petkov 		bool row_dct1 = false;
30510ec449eeSDoug Thompson 
3052a4b4bedcSBorislav Petkov 		if (pvt->fam != 0xf)
305310de6497SBorislav Petkov 			row_dct1 = !!csrow_enabled(i, 1, pvt);
305410de6497SBorislav Petkov 
305510de6497SBorislav Petkov 		if (!row_dct0 && !row_dct1)
30560ec449eeSDoug Thompson 			continue;
30570ec449eeSDoug Thompson 
305810de6497SBorislav Petkov 		csrow = mci->csrows[i];
30590ec449eeSDoug Thompson 		empty = 0;
306011c75eadSBorislav Petkov 
306110de6497SBorislav Petkov 		edac_dbg(1, "MC node: %d, csrow: %d\n",
306210de6497SBorislav Petkov 			    pvt->mc_node_id, i);
306310de6497SBorislav Petkov 
30641eef1282SMauro Carvalho Chehab 		if (row_dct0) {
3065d1ea71cdSBorislav Petkov 			nr_pages = get_csrow_nr_pages(pvt, 0, i);
30661eef1282SMauro Carvalho Chehab 			csrow->channels[0]->dimm->nr_pages = nr_pages;
30671eef1282SMauro Carvalho Chehab 		}
306810de6497SBorislav Petkov 
306910de6497SBorislav Petkov 		/* K8 has only one DCT */
3070a4b4bedcSBorislav Petkov 		if (pvt->fam != 0xf && row_dct1) {
3071d1ea71cdSBorislav Petkov 			int row_dct1_pages = get_csrow_nr_pages(pvt, 1, i);
30721eef1282SMauro Carvalho Chehab 
30731eef1282SMauro Carvalho Chehab 			csrow->channels[1]->dimm->nr_pages = row_dct1_pages;
30741eef1282SMauro Carvalho Chehab 			nr_pages += row_dct1_pages;
30751eef1282SMauro Carvalho Chehab 		}
30760ec449eeSDoug Thompson 
307710de6497SBorislav Petkov 		edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages);
30780ec449eeSDoug Thompson 
30792d09d8f3SYazen Ghannam 		/* Determine DIMM ECC mode: */
3080353a1fcbSYazen Ghannam 		if (pvt->nbcfg & NBCFG_ECC_ENABLE) {
30812d09d8f3SYazen Ghannam 			edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL)
30822d09d8f3SYazen Ghannam 					? EDAC_S4ECD4ED
30832d09d8f3SYazen Ghannam 					: EDAC_SECDED;
30842d09d8f3SYazen Ghannam 		}
3085084a4fccSMauro Carvalho Chehab 
3086084a4fccSMauro Carvalho Chehab 		for (j = 0; j < pvt->channel_count; j++) {
3087de3910ebSMauro Carvalho Chehab 			dimm = csrow->channels[j]->dimm;
3088a597d2a5SAravind Gopalakrishnan 			dimm->mtype = pvt->dram_type;
3089de3910ebSMauro Carvalho Chehab 			dimm->edac_mode = edac_mode;
3090466503d6SYazen Ghannam 			dimm->grain = 64;
3091084a4fccSMauro Carvalho Chehab 		}
30920ec449eeSDoug Thompson 	}
30930ec449eeSDoug Thompson 
30940ec449eeSDoug Thompson 	return empty;
30950ec449eeSDoug Thompson }
3096d27bf6faSDoug Thompson 
309706724535SBorislav Petkov /* get all cores on this DCT */
30988b84c8dfSDaniel J Blueman static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, u16 nid)
3099f9431992SDoug Thompson {
310006724535SBorislav Petkov 	int cpu;
3101f9431992SDoug Thompson 
310206724535SBorislav Petkov 	for_each_online_cpu(cpu)
3103db970bd2SYazen Ghannam 		if (topology_die_id(cpu) == nid)
310406724535SBorislav Petkov 			cpumask_set_cpu(cpu, mask);
3105f9431992SDoug Thompson }
3106f9431992SDoug Thompson 
3107f9431992SDoug Thompson /* check MCG_CTL on all the cpus on this node */
3108d1ea71cdSBorislav Petkov static bool nb_mce_bank_enabled_on_node(u16 nid)
3109f9431992SDoug Thompson {
3110ba578cb3SRusty Russell 	cpumask_var_t mask;
311150542251SBorislav Petkov 	int cpu, nbe;
311206724535SBorislav Petkov 	bool ret = false;
3113f9431992SDoug Thompson 
3114ba578cb3SRusty Russell 	if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
311524f9a7feSBorislav Petkov 		amd64_warn("%s: Error allocating mask\n", __func__);
311606724535SBorislav Petkov 		return false;
311706724535SBorislav Petkov 	}
311806724535SBorislav Petkov 
3119ba578cb3SRusty Russell 	get_cpus_on_this_dct_cpumask(mask, nid);
312006724535SBorislav Petkov 
3121ba578cb3SRusty Russell 	rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
3122ba578cb3SRusty Russell 
3123ba578cb3SRusty Russell 	for_each_cpu(cpu, mask) {
312450542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
31255980bb9cSBorislav Petkov 		nbe = reg->l & MSR_MCGCTL_NBE;
312606724535SBorislav Petkov 
3127956b9ba1SJoe Perches 		edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
312850542251SBorislav Petkov 			 cpu, reg->q,
312906724535SBorislav Petkov 			 (nbe ? "enabled" : "disabled"));
313006724535SBorislav Petkov 
313106724535SBorislav Petkov 		if (!nbe)
313206724535SBorislav Petkov 			goto out;
313306724535SBorislav Petkov 	}
313406724535SBorislav Petkov 	ret = true;
313506724535SBorislav Petkov 
313606724535SBorislav Petkov out:
3137ba578cb3SRusty Russell 	free_cpumask_var(mask);
3138f9431992SDoug Thompson 	return ret;
3139f9431992SDoug Thompson }
3140f9431992SDoug Thompson 
3141c7e5301aSDaniel J Blueman static int toggle_ecc_err_reporting(struct ecc_settings *s, u16 nid, bool on)
3142f6d6ae96SBorislav Petkov {
3143f6d6ae96SBorislav Petkov 	cpumask_var_t cmask;
314450542251SBorislav Petkov 	int cpu;
3145f6d6ae96SBorislav Petkov 
3146f6d6ae96SBorislav Petkov 	if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
314724f9a7feSBorislav Petkov 		amd64_warn("%s: error allocating mask\n", __func__);
31480de27884SPan Bian 		return -ENOMEM;
3149f6d6ae96SBorislav Petkov 	}
3150f6d6ae96SBorislav Petkov 
3151ae7bb7c6SBorislav Petkov 	get_cpus_on_this_dct_cpumask(cmask, nid);
3152f6d6ae96SBorislav Petkov 
3153f6d6ae96SBorislav Petkov 	rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
3154f6d6ae96SBorislav Petkov 
3155f6d6ae96SBorislav Petkov 	for_each_cpu(cpu, cmask) {
3156f6d6ae96SBorislav Petkov 
315750542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
315850542251SBorislav Petkov 
3159f6d6ae96SBorislav Petkov 		if (on) {
31605980bb9cSBorislav Petkov 			if (reg->l & MSR_MCGCTL_NBE)
3161ae7bb7c6SBorislav Petkov 				s->flags.nb_mce_enable = 1;
3162f6d6ae96SBorislav Petkov 
31635980bb9cSBorislav Petkov 			reg->l |= MSR_MCGCTL_NBE;
3164f6d6ae96SBorislav Petkov 		} else {
3165f6d6ae96SBorislav Petkov 			/*
3166d95cf4deSBorislav Petkov 			 * Turn off NB MCE reporting only when it was off before
3167f6d6ae96SBorislav Petkov 			 */
3168ae7bb7c6SBorislav Petkov 			if (!s->flags.nb_mce_enable)
31695980bb9cSBorislav Petkov 				reg->l &= ~MSR_MCGCTL_NBE;
3170f6d6ae96SBorislav Petkov 		}
3171f6d6ae96SBorislav Petkov 	}
3172f6d6ae96SBorislav Petkov 	wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
3173f6d6ae96SBorislav Petkov 
3174f6d6ae96SBorislav Petkov 	free_cpumask_var(cmask);
3175f6d6ae96SBorislav Petkov 
3176f6d6ae96SBorislav Petkov 	return 0;
3177f6d6ae96SBorislav Petkov }
3178f6d6ae96SBorislav Petkov 
3179c7e5301aSDaniel J Blueman static bool enable_ecc_error_reporting(struct ecc_settings *s, u16 nid,
31802299ef71SBorislav Petkov 				       struct pci_dev *F3)
3181f6d6ae96SBorislav Petkov {
31822299ef71SBorislav Petkov 	bool ret = true;
3183c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
3184f6d6ae96SBorislav Petkov 
31852299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, ON)) {
31862299ef71SBorislav Petkov 		amd64_warn("Error enabling ECC reporting over MCGCTL!\n");
31872299ef71SBorislav Petkov 		return false;
31882299ef71SBorislav Petkov 	}
31892299ef71SBorislav Petkov 
3190c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
3191f6d6ae96SBorislav Petkov 
3192ae7bb7c6SBorislav Petkov 	s->old_nbctl   = value & mask;
3193ae7bb7c6SBorislav Petkov 	s->nbctl_valid = true;
3194f6d6ae96SBorislav Petkov 
3195f6d6ae96SBorislav Petkov 	value |= mask;
3196c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
3197f6d6ae96SBorislav Petkov 
3198a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCFG, &value);
3199f6d6ae96SBorislav Petkov 
3200956b9ba1SJoe Perches 	edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
3201a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
3202f6d6ae96SBorislav Petkov 
3203a97fa68eSBorislav Petkov 	if (!(value & NBCFG_ECC_ENABLE)) {
320424f9a7feSBorislav Petkov 		amd64_warn("DRAM ECC disabled on this node, enabling...\n");
3205f6d6ae96SBorislav Petkov 
3206ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 0;
3207d95cf4deSBorislav Petkov 
3208f6d6ae96SBorislav Petkov 		/* Attempt to turn on DRAM ECC Enable */
3209a97fa68eSBorislav Petkov 		value |= NBCFG_ECC_ENABLE;
3210a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
3211f6d6ae96SBorislav Petkov 
3212a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
3213f6d6ae96SBorislav Petkov 
3214a97fa68eSBorislav Petkov 		if (!(value & NBCFG_ECC_ENABLE)) {
321524f9a7feSBorislav Petkov 			amd64_warn("Hardware rejected DRAM ECC enable,"
321624f9a7feSBorislav Petkov 				   "check memory DIMM configuration.\n");
32172299ef71SBorislav Petkov 			ret = false;
3218f6d6ae96SBorislav Petkov 		} else {
321924f9a7feSBorislav Petkov 			amd64_info("Hardware accepted DRAM ECC Enable\n");
3220f6d6ae96SBorislav Petkov 		}
3221d95cf4deSBorislav Petkov 	} else {
3222ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 1;
3223f6d6ae96SBorislav Petkov 	}
3224d95cf4deSBorislav Petkov 
3225956b9ba1SJoe Perches 	edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
3226a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
3227f6d6ae96SBorislav Petkov 
32282299ef71SBorislav Petkov 	return ret;
3229f6d6ae96SBorislav Petkov }
3230f6d6ae96SBorislav Petkov 
3231c7e5301aSDaniel J Blueman static void restore_ecc_error_reporting(struct ecc_settings *s, u16 nid,
3232360b7f3cSBorislav Petkov 					struct pci_dev *F3)
3233f6d6ae96SBorislav Petkov {
3234c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
3235c9f4f26eSBorislav Petkov 
3236ae7bb7c6SBorislav Petkov 	if (!s->nbctl_valid)
3237f6d6ae96SBorislav Petkov 		return;
3238f6d6ae96SBorislav Petkov 
3239c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
3240f6d6ae96SBorislav Petkov 	value &= ~mask;
3241ae7bb7c6SBorislav Petkov 	value |= s->old_nbctl;
3242f6d6ae96SBorislav Petkov 
3243c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
3244f6d6ae96SBorislav Petkov 
3245ae7bb7c6SBorislav Petkov 	/* restore previous BIOS DRAM ECC "off" setting we force-enabled */
3246ae7bb7c6SBorislav Petkov 	if (!s->flags.nb_ecc_prev) {
3247a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
3248a97fa68eSBorislav Petkov 		value &= ~NBCFG_ECC_ENABLE;
3249a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
3250d95cf4deSBorislav Petkov 	}
3251d95cf4deSBorislav Petkov 
3252d95cf4deSBorislav Petkov 	/* restore the NB Enable MCGCTL bit */
32532299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, OFF))
325424f9a7feSBorislav Petkov 		amd64_warn("Error restoring NB MCGCTL settings!\n");
3255f6d6ae96SBorislav Petkov }
3256f6d6ae96SBorislav Petkov 
32571c9b08baSYazen Ghannam static bool ecc_enabled(struct amd64_pvt *pvt)
3258f9431992SDoug Thompson {
32591c9b08baSYazen Ghannam 	u16 nid = pvt->mc_node_id;
326006724535SBorislav Petkov 	bool nb_mce_en = false;
3261196b79fcSYazen Ghannam 	u8 ecc_en = 0, i;
3262196b79fcSYazen Ghannam 	u32 value;
3263f9431992SDoug Thompson 
3264196b79fcSYazen Ghannam 	if (boot_cpu_data.x86 >= 0x17) {
3265196b79fcSYazen Ghannam 		u8 umc_en_mask = 0, ecc_en_mask = 0;
32661c9b08baSYazen Ghannam 		struct amd64_umc *umc;
3267196b79fcSYazen Ghannam 
32684d30d2bcSYazen Ghannam 		for_each_umc(i) {
32691c9b08baSYazen Ghannam 			umc = &pvt->umc[i];
3270196b79fcSYazen Ghannam 
3271196b79fcSYazen Ghannam 			/* Only check enabled UMCs. */
32721c9b08baSYazen Ghannam 			if (!(umc->sdp_ctrl & UMC_SDP_INIT))
3273196b79fcSYazen Ghannam 				continue;
3274196b79fcSYazen Ghannam 
3275196b79fcSYazen Ghannam 			umc_en_mask |= BIT(i);
3276196b79fcSYazen Ghannam 
32771c9b08baSYazen Ghannam 			if (umc->umc_cap_hi & UMC_ECC_ENABLED)
3278196b79fcSYazen Ghannam 				ecc_en_mask |= BIT(i);
3279196b79fcSYazen Ghannam 		}
3280196b79fcSYazen Ghannam 
3281196b79fcSYazen Ghannam 		/* Check whether at least one UMC is enabled: */
3282196b79fcSYazen Ghannam 		if (umc_en_mask)
3283196b79fcSYazen Ghannam 			ecc_en = umc_en_mask == ecc_en_mask;
328411ab1caeSYazen Ghannam 		else
328511ab1caeSYazen Ghannam 			edac_dbg(0, "Node %d: No enabled UMCs.\n", nid);
3286196b79fcSYazen Ghannam 
3287196b79fcSYazen Ghannam 		/* Assume UMC MCA banks are enabled. */
3288196b79fcSYazen Ghannam 		nb_mce_en = true;
3289196b79fcSYazen Ghannam 	} else {
32901c9b08baSYazen Ghannam 		amd64_read_pci_cfg(pvt->F3, NBCFG, &value);
3291f9431992SDoug Thompson 
3292a97fa68eSBorislav Petkov 		ecc_en = !!(value & NBCFG_ECC_ENABLE);
3293be3468e8SBorislav Petkov 
3294d1ea71cdSBorislav Petkov 		nb_mce_en = nb_mce_bank_enabled_on_node(nid);
329506724535SBorislav Petkov 		if (!nb_mce_en)
329611ab1caeSYazen Ghannam 			edac_dbg(0, "NB MCE bank disabled, set MSR 0x%08x[4] on node %d to enable.\n",
32972299ef71SBorislav Petkov 				     MSR_IA32_MCG_CTL, nid);
3298196b79fcSYazen Ghannam 	}
3299196b79fcSYazen Ghannam 
330011ab1caeSYazen Ghannam 	amd64_info("Node %d: DRAM ECC %s.\n",
330111ab1caeSYazen Ghannam 		   nid, (ecc_en ? "enabled" : "disabled"));
3302be3468e8SBorislav Petkov 
33037fdfee92SBorislav Petkov 	if (!ecc_en || !nb_mce_en)
33042299ef71SBorislav Petkov 		return false;
33057fdfee92SBorislav Petkov 	else
33062299ef71SBorislav Petkov 		return true;
3307f9431992SDoug Thompson }
3308f9431992SDoug Thompson 
33092d09d8f3SYazen Ghannam static inline void
33102d09d8f3SYazen Ghannam f17h_determine_edac_ctl_cap(struct mem_ctl_info *mci, struct amd64_pvt *pvt)
33112d09d8f3SYazen Ghannam {
3312f8be8e56SYazen Ghannam 	u8 i, ecc_en = 1, cpk_en = 1, dev_x4 = 1, dev_x16 = 1;
33132d09d8f3SYazen Ghannam 
33144d30d2bcSYazen Ghannam 	for_each_umc(i) {
33152d09d8f3SYazen Ghannam 		if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) {
33162d09d8f3SYazen Ghannam 			ecc_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_ENABLED);
33172d09d8f3SYazen Ghannam 			cpk_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_CHIPKILL_CAP);
3318f8be8e56SYazen Ghannam 
3319f8be8e56SYazen Ghannam 			dev_x4  &= !!(pvt->umc[i].dimm_cfg & BIT(6));
3320f8be8e56SYazen Ghannam 			dev_x16 &= !!(pvt->umc[i].dimm_cfg & BIT(7));
33212d09d8f3SYazen Ghannam 		}
33222d09d8f3SYazen Ghannam 	}
33232d09d8f3SYazen Ghannam 
33242d09d8f3SYazen Ghannam 	/* Set chipkill only if ECC is enabled: */
33252d09d8f3SYazen Ghannam 	if (ecc_en) {
33262d09d8f3SYazen Ghannam 		mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
33272d09d8f3SYazen Ghannam 
3328f8be8e56SYazen Ghannam 		if (!cpk_en)
3329f8be8e56SYazen Ghannam 			return;
3330f8be8e56SYazen Ghannam 
3331f8be8e56SYazen Ghannam 		if (dev_x4)
33322d09d8f3SYazen Ghannam 			mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
3333f8be8e56SYazen Ghannam 		else if (dev_x16)
3334f8be8e56SYazen Ghannam 			mci->edac_ctl_cap |= EDAC_FLAG_S16ECD16ED;
3335f8be8e56SYazen Ghannam 		else
3336f8be8e56SYazen Ghannam 			mci->edac_ctl_cap |= EDAC_FLAG_S8ECD8ED;
33372d09d8f3SYazen Ghannam 	}
33382d09d8f3SYazen Ghannam }
33392d09d8f3SYazen Ghannam 
334038ddd4d1SYazen Ghannam static void setup_mci_misc_attrs(struct mem_ctl_info *mci)
33417d6034d3SDoug Thompson {
33427d6034d3SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
33437d6034d3SDoug Thompson 
33447d6034d3SDoug Thompson 	mci->mtype_cap		= MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
33457d6034d3SDoug Thompson 	mci->edac_ctl_cap	= EDAC_FLAG_NONE;
33467d6034d3SDoug Thompson 
33472d09d8f3SYazen Ghannam 	if (pvt->umc) {
33482d09d8f3SYazen Ghannam 		f17h_determine_edac_ctl_cap(mci, pvt);
33492d09d8f3SYazen Ghannam 	} else {
33505980bb9cSBorislav Petkov 		if (pvt->nbcap & NBCAP_SECDED)
33517d6034d3SDoug Thompson 			mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
33527d6034d3SDoug Thompson 
33535980bb9cSBorislav Petkov 		if (pvt->nbcap & NBCAP_CHIPKILL)
33547d6034d3SDoug Thompson 			mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
33552d09d8f3SYazen Ghannam 	}
33567d6034d3SDoug Thompson 
3357d1ea71cdSBorislav Petkov 	mci->edac_cap		= determine_edac_cap(pvt);
33587d6034d3SDoug Thompson 	mci->mod_name		= EDAC_MOD_STR;
335938ddd4d1SYazen Ghannam 	mci->ctl_name		= fam_type->ctl_name;
3360e7934b70SYazen Ghannam 	mci->dev_name		= pci_name(pvt->F3);
33617d6034d3SDoug Thompson 	mci->ctl_page_to_phys	= NULL;
33627d6034d3SDoug Thompson 
33637d6034d3SDoug Thompson 	/* memory scrubber interface */
3364d1ea71cdSBorislav Petkov 	mci->set_sdram_scrub_rate = set_scrub_rate;
3365d1ea71cdSBorislav Petkov 	mci->get_sdram_scrub_rate = get_scrub_rate;
33667d6034d3SDoug Thompson }
33677d6034d3SDoug Thompson 
33680092b20dSBorislav Petkov /*
33690092b20dSBorislav Petkov  * returns a pointer to the family descriptor on success, NULL otherwise.
33700092b20dSBorislav Petkov  */
3371d1ea71cdSBorislav Petkov static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt)
3372395ae783SBorislav Petkov {
337318b94f66SAravind Gopalakrishnan 	pvt->ext_model  = boot_cpu_data.x86_model >> 4;
3374b399151cSJia Zhang 	pvt->stepping	= boot_cpu_data.x86_stepping;
337518b94f66SAravind Gopalakrishnan 	pvt->model	= boot_cpu_data.x86_model;
337618b94f66SAravind Gopalakrishnan 	pvt->fam	= boot_cpu_data.x86;
337718b94f66SAravind Gopalakrishnan 
337818b94f66SAravind Gopalakrishnan 	switch (pvt->fam) {
3379395ae783SBorislav Petkov 	case 0xf:
3380d1ea71cdSBorislav Petkov 		fam_type	= &family_types[K8_CPUS];
3381d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[K8_CPUS].ops;
3382395ae783SBorislav Petkov 		break;
3383df71a053SBorislav Petkov 
3384395ae783SBorislav Petkov 	case 0x10:
3385d1ea71cdSBorislav Petkov 		fam_type	= &family_types[F10_CPUS];
3386d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[F10_CPUS].ops;
3387df71a053SBorislav Petkov 		break;
3388df71a053SBorislav Petkov 
3389df71a053SBorislav Petkov 	case 0x15:
339018b94f66SAravind Gopalakrishnan 		if (pvt->model == 0x30) {
3391d1ea71cdSBorislav Petkov 			fam_type = &family_types[F15_M30H_CPUS];
3392d1ea71cdSBorislav Petkov 			pvt->ops = &family_types[F15_M30H_CPUS].ops;
339318b94f66SAravind Gopalakrishnan 			break;
3394a597d2a5SAravind Gopalakrishnan 		} else if (pvt->model == 0x60) {
3395a597d2a5SAravind Gopalakrishnan 			fam_type = &family_types[F15_M60H_CPUS];
3396a597d2a5SAravind Gopalakrishnan 			pvt->ops = &family_types[F15_M60H_CPUS].ops;
3397a597d2a5SAravind Gopalakrishnan 			break;
33986c13d7ffSBorislav Petkov 		/* Richland is only client */
33996c13d7ffSBorislav Petkov 		} else if (pvt->model == 0x13) {
34006c13d7ffSBorislav Petkov 			return NULL;
34016c13d7ffSBorislav Petkov 		} else {
3402d1ea71cdSBorislav Petkov 			fam_type	= &family_types[F15_CPUS];
3403d1ea71cdSBorislav Petkov 			pvt->ops	= &family_types[F15_CPUS].ops;
34046c13d7ffSBorislav Petkov 		}
3405395ae783SBorislav Petkov 		break;
3406395ae783SBorislav Petkov 
340794c1acf2SAravind Gopalakrishnan 	case 0x16:
340885a8885bSAravind Gopalakrishnan 		if (pvt->model == 0x30) {
340985a8885bSAravind Gopalakrishnan 			fam_type = &family_types[F16_M30H_CPUS];
341085a8885bSAravind Gopalakrishnan 			pvt->ops = &family_types[F16_M30H_CPUS].ops;
341185a8885bSAravind Gopalakrishnan 			break;
341285a8885bSAravind Gopalakrishnan 		}
3413d1ea71cdSBorislav Petkov 		fam_type	= &family_types[F16_CPUS];
3414d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[F16_CPUS].ops;
341594c1acf2SAravind Gopalakrishnan 		break;
341694c1acf2SAravind Gopalakrishnan 
3417f1cbbec9SYazen Ghannam 	case 0x17:
34188960de4aSMichael Jin 		if (pvt->model >= 0x10 && pvt->model <= 0x2f) {
34198960de4aSMichael Jin 			fam_type = &family_types[F17_M10H_CPUS];
34208960de4aSMichael Jin 			pvt->ops = &family_types[F17_M10H_CPUS].ops;
34218960de4aSMichael Jin 			break;
34226e846239SYazen Ghannam 		} else if (pvt->model >= 0x30 && pvt->model <= 0x3f) {
34236e846239SYazen Ghannam 			fam_type = &family_types[F17_M30H_CPUS];
34246e846239SYazen Ghannam 			pvt->ops = &family_types[F17_M30H_CPUS].ops;
34256e846239SYazen Ghannam 			break;
3426b6bea24dSAlexander Monakov 		} else if (pvt->model >= 0x60 && pvt->model <= 0x6f) {
3427b6bea24dSAlexander Monakov 			fam_type = &family_types[F17_M60H_CPUS];
3428b6bea24dSAlexander Monakov 			pvt->ops = &family_types[F17_M60H_CPUS].ops;
3429b6bea24dSAlexander Monakov 			break;
34303e443eb3SIsaac Vaughn 		} else if (pvt->model >= 0x70 && pvt->model <= 0x7f) {
34313e443eb3SIsaac Vaughn 			fam_type = &family_types[F17_M70H_CPUS];
34323e443eb3SIsaac Vaughn 			pvt->ops = &family_types[F17_M70H_CPUS].ops;
34333e443eb3SIsaac Vaughn 			break;
34348960de4aSMichael Jin 		}
3435df561f66SGustavo A. R. Silva 		fallthrough;
3436c4a3e946SPu Wen 	case 0x18:
3437f1cbbec9SYazen Ghannam 		fam_type	= &family_types[F17_CPUS];
3438f1cbbec9SYazen Ghannam 		pvt->ops	= &family_types[F17_CPUS].ops;
3439c4a3e946SPu Wen 
3440c4a3e946SPu Wen 		if (pvt->fam == 0x18)
3441c4a3e946SPu Wen 			family_types[F17_CPUS].ctl_name = "F18h";
3442f1cbbec9SYazen Ghannam 		break;
3443f1cbbec9SYazen Ghannam 
34442eb61c91SYazen Ghannam 	case 0x19:
3445b4210eabSYazen Ghannam 		if (pvt->model >= 0x20 && pvt->model <= 0x2f) {
3446b4210eabSYazen Ghannam 			fam_type = &family_types[F17_M70H_CPUS];
3447b4210eabSYazen Ghannam 			pvt->ops = &family_types[F17_M70H_CPUS].ops;
3448b4210eabSYazen Ghannam 			fam_type->ctl_name = "F19h_M20h";
3449b4210eabSYazen Ghannam 			break;
3450b4210eabSYazen Ghannam 		}
34512eb61c91SYazen Ghannam 		fam_type	= &family_types[F19_CPUS];
34522eb61c91SYazen Ghannam 		pvt->ops	= &family_types[F19_CPUS].ops;
34532eb61c91SYazen Ghannam 		family_types[F19_CPUS].ctl_name = "F19h";
34542eb61c91SYazen Ghannam 		break;
34552eb61c91SYazen Ghannam 
3456395ae783SBorislav Petkov 	default:
345724f9a7feSBorislav Petkov 		amd64_err("Unsupported family!\n");
34580092b20dSBorislav Petkov 		return NULL;
3459395ae783SBorislav Petkov 	}
34600092b20dSBorislav Petkov 
3461df71a053SBorislav Petkov 	amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name,
346218b94f66SAravind Gopalakrishnan 		     (pvt->fam == 0xf ?
34630092b20dSBorislav Petkov 				(pvt->ext_model >= K8_REV_F  ? "revF or later "
34640092b20dSBorislav Petkov 							     : "revE or earlier ")
346524f9a7feSBorislav Petkov 				 : ""), pvt->mc_node_id);
34660092b20dSBorislav Petkov 	return fam_type;
3467395ae783SBorislav Petkov }
3468395ae783SBorislav Petkov 
3469e339f1ecSTakashi Iwai static const struct attribute_group *amd64_edac_attr_groups[] = {
3470e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_DEBUG
3471*2a28ceefSBorislav Petkov 	&dbg_group,
3472e339f1ecSTakashi Iwai #endif
3473e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION
3474e339f1ecSTakashi Iwai 	&amd64_edac_inj_group,
3475e339f1ecSTakashi Iwai #endif
3476e339f1ecSTakashi Iwai 	NULL
3477e339f1ecSTakashi Iwai };
3478e339f1ecSTakashi Iwai 
347980355a3bSYazen Ghannam static int hw_info_get(struct amd64_pvt *pvt)
34807d6034d3SDoug Thompson {
3481936fc3afSYazen Ghannam 	u16 pci_id1, pci_id2;
3482f00eb5ffSColin Ian King 	int ret;
3483395ae783SBorislav Petkov 
3484936fc3afSYazen Ghannam 	if (pvt->fam >= 0x17) {
34855e4c5527SYazen Ghannam 		pvt->umc = kcalloc(fam_type->max_mcs, sizeof(struct amd64_umc), GFP_KERNEL);
348680355a3bSYazen Ghannam 		if (!pvt->umc)
348780355a3bSYazen Ghannam 			return -ENOMEM;
3488936fc3afSYazen Ghannam 
3489936fc3afSYazen Ghannam 		pci_id1 = fam_type->f0_id;
3490936fc3afSYazen Ghannam 		pci_id2 = fam_type->f6_id;
3491936fc3afSYazen Ghannam 	} else {
3492936fc3afSYazen Ghannam 		pci_id1 = fam_type->f1_id;
3493936fc3afSYazen Ghannam 		pci_id2 = fam_type->f2_id;
3494936fc3afSYazen Ghannam 	}
3495936fc3afSYazen Ghannam 
349680355a3bSYazen Ghannam 	ret = reserve_mc_sibling_devs(pvt, pci_id1, pci_id2);
349780355a3bSYazen Ghannam 	if (ret)
349880355a3bSYazen Ghannam 		return ret;
34997d6034d3SDoug Thompson 
3500360b7f3cSBorislav Petkov 	read_mc_regs(pvt);
35017d6034d3SDoug Thompson 
350280355a3bSYazen Ghannam 	return 0;
350380355a3bSYazen Ghannam }
350480355a3bSYazen Ghannam 
350580355a3bSYazen Ghannam static void hw_info_put(struct amd64_pvt *pvt)
350680355a3bSYazen Ghannam {
350780355a3bSYazen Ghannam 	if (pvt->F0 || pvt->F1)
350880355a3bSYazen Ghannam 		free_mc_sibling_devs(pvt);
350980355a3bSYazen Ghannam 
351080355a3bSYazen Ghannam 	kfree(pvt->umc);
351180355a3bSYazen Ghannam }
351280355a3bSYazen Ghannam 
351380355a3bSYazen Ghannam static int init_one_instance(struct amd64_pvt *pvt)
351480355a3bSYazen Ghannam {
351580355a3bSYazen Ghannam 	struct mem_ctl_info *mci = NULL;
351680355a3bSYazen Ghannam 	struct edac_mc_layer layers[2];
351780355a3bSYazen Ghannam 	int ret = -EINVAL;
351880355a3bSYazen Ghannam 
35197d6034d3SDoug Thompson 	/*
35207d6034d3SDoug Thompson 	 * We need to determine how many memory channels there are. Then use
35217d6034d3SDoug Thompson 	 * that information for calculating the size of the dynamic instance
3522360b7f3cSBorislav Petkov 	 * tables in the 'mci' structure.
35237d6034d3SDoug Thompson 	 */
35247d6034d3SDoug Thompson 	pvt->channel_count = pvt->ops->early_channel_count(pvt);
35257d6034d3SDoug Thompson 	if (pvt->channel_count < 0)
352680355a3bSYazen Ghannam 		return ret;
35277d6034d3SDoug Thompson 
35287d6034d3SDoug Thompson 	ret = -ENOMEM;
3529ab5a503cSMauro Carvalho Chehab 	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
3530ab5a503cSMauro Carvalho Chehab 	layers[0].size = pvt->csels[0].b_cnt;
3531ab5a503cSMauro Carvalho Chehab 	layers[0].is_virt_csrow = true;
3532ab5a503cSMauro Carvalho Chehab 	layers[1].type = EDAC_MC_LAYER_CHANNEL;
3533f0a56c48SBorislav Petkov 
3534f0a56c48SBorislav Petkov 	/*
3535f0a56c48SBorislav Petkov 	 * Always allocate two channels since we can have setups with DIMMs on
3536f0a56c48SBorislav Petkov 	 * only one channel. Also, this simplifies handling later for the price
3537f0a56c48SBorislav Petkov 	 * of a couple of KBs tops.
3538f0a56c48SBorislav Petkov 	 */
35395e4c5527SYazen Ghannam 	layers[1].size = fam_type->max_mcs;
3540ab5a503cSMauro Carvalho Chehab 	layers[1].is_virt_csrow = false;
3541f0a56c48SBorislav Petkov 
354280355a3bSYazen Ghannam 	mci = edac_mc_alloc(pvt->mc_node_id, ARRAY_SIZE(layers), layers, 0);
35437d6034d3SDoug Thompson 	if (!mci)
354480355a3bSYazen Ghannam 		return ret;
35457d6034d3SDoug Thompson 
35467d6034d3SDoug Thompson 	mci->pvt_info = pvt;
35473f37a36bSBorislav Petkov 	mci->pdev = &pvt->F3->dev;
35487d6034d3SDoug Thompson 
354938ddd4d1SYazen Ghannam 	setup_mci_misc_attrs(mci);
3550360b7f3cSBorislav Petkov 
3551360b7f3cSBorislav Petkov 	if (init_csrows(mci))
35527d6034d3SDoug Thompson 		mci->edac_cap = EDAC_FLAG_NONE;
35537d6034d3SDoug Thompson 
35547d6034d3SDoug Thompson 	ret = -ENODEV;
3555e339f1ecSTakashi Iwai 	if (edac_mc_add_mc_with_groups(mci, amd64_edac_attr_groups)) {
3556956b9ba1SJoe Perches 		edac_dbg(1, "failed edac_mc_add_mc()\n");
355780355a3bSYazen Ghannam 		edac_mc_free(mci);
355880355a3bSYazen Ghannam 		return ret;
35597d6034d3SDoug Thompson 	}
35607d6034d3SDoug Thompson 
35617d6034d3SDoug Thompson 	return 0;
35627d6034d3SDoug Thompson }
35637d6034d3SDoug Thompson 
3564582f94b5SYazen Ghannam static bool instance_has_memory(struct amd64_pvt *pvt)
3565582f94b5SYazen Ghannam {
3566582f94b5SYazen Ghannam 	bool cs_enabled = false;
3567582f94b5SYazen Ghannam 	int cs = 0, dct = 0;
3568582f94b5SYazen Ghannam 
3569582f94b5SYazen Ghannam 	for (dct = 0; dct < fam_type->max_mcs; dct++) {
3570582f94b5SYazen Ghannam 		for_each_chip_select(cs, dct, pvt)
3571582f94b5SYazen Ghannam 			cs_enabled |= csrow_enabled(cs, dct, pvt);
3572582f94b5SYazen Ghannam 	}
3573582f94b5SYazen Ghannam 
3574582f94b5SYazen Ghannam 	return cs_enabled;
3575582f94b5SYazen Ghannam }
3576582f94b5SYazen Ghannam 
35773f37a36bSBorislav Petkov static int probe_one_instance(unsigned int nid)
35787d6034d3SDoug Thompson {
35792299ef71SBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
358080355a3bSYazen Ghannam 	struct amd64_pvt *pvt = NULL;
3581ae7bb7c6SBorislav Petkov 	struct ecc_settings *s;
35823f37a36bSBorislav Petkov 	int ret;
3583b8cfa02fSBorislav Petkov 
3584ae7bb7c6SBorislav Petkov 	ret = -ENOMEM;
3585ae7bb7c6SBorislav Petkov 	s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL);
3586ae7bb7c6SBorislav Petkov 	if (!s)
35872299ef71SBorislav Petkov 		goto err_out;
3588ae7bb7c6SBorislav Petkov 
3589ae7bb7c6SBorislav Petkov 	ecc_stngs[nid] = s;
3590ae7bb7c6SBorislav Petkov 
359180355a3bSYazen Ghannam 	pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
359280355a3bSYazen Ghannam 	if (!pvt)
359380355a3bSYazen Ghannam 		goto err_settings;
359480355a3bSYazen Ghannam 
359580355a3bSYazen Ghannam 	pvt->mc_node_id	= nid;
359680355a3bSYazen Ghannam 	pvt->F3 = F3;
359780355a3bSYazen Ghannam 
35986c13d7ffSBorislav Petkov 	ret = -ENODEV;
359980355a3bSYazen Ghannam 	fam_type = per_family_init(pvt);
360080355a3bSYazen Ghannam 	if (!fam_type)
360180355a3bSYazen Ghannam 		goto err_enable;
360280355a3bSYazen Ghannam 
360380355a3bSYazen Ghannam 	ret = hw_info_get(pvt);
360480355a3bSYazen Ghannam 	if (ret < 0)
360580355a3bSYazen Ghannam 		goto err_enable;
360680355a3bSYazen Ghannam 
36074688c9b4SYazen Ghannam 	ret = 0;
3608582f94b5SYazen Ghannam 	if (!instance_has_memory(pvt)) {
3609582f94b5SYazen Ghannam 		amd64_info("Node %d: No DIMMs detected.\n", nid);
3610582f94b5SYazen Ghannam 		goto err_enable;
3611582f94b5SYazen Ghannam 	}
3612582f94b5SYazen Ghannam 
3613582f94b5SYazen Ghannam 	if (!ecc_enabled(pvt)) {
3614582f94b5SYazen Ghannam 		ret = -ENODEV;
36152299ef71SBorislav Petkov 
36162299ef71SBorislav Petkov 		if (!ecc_enable_override)
36172299ef71SBorislav Petkov 			goto err_enable;
36182299ef71SBorislav Petkov 
3619044e7a41SYazen Ghannam 		if (boot_cpu_data.x86 >= 0x17) {
3620044e7a41SYazen Ghannam 			amd64_warn("Forcing ECC on is not recommended on newer systems. Please enable ECC in BIOS.");
3621044e7a41SYazen Ghannam 			goto err_enable;
3622044e7a41SYazen Ghannam 		} else
36232299ef71SBorislav Petkov 			amd64_warn("Forcing ECC on!\n");
36242299ef71SBorislav Petkov 
36252299ef71SBorislav Petkov 		if (!enable_ecc_error_reporting(s, nid, F3))
36262299ef71SBorislav Petkov 			goto err_enable;
36272299ef71SBorislav Petkov 	}
36282299ef71SBorislav Petkov 
362980355a3bSYazen Ghannam 	ret = init_one_instance(pvt);
3630360b7f3cSBorislav Petkov 	if (ret < 0) {
3631ae7bb7c6SBorislav Petkov 		amd64_err("Error probing instance: %d\n", nid);
3632044e7a41SYazen Ghannam 
3633044e7a41SYazen Ghannam 		if (boot_cpu_data.x86 < 0x17)
3634360b7f3cSBorislav Petkov 			restore_ecc_error_reporting(s, nid, F3);
36352b9b2c46SYazen Ghannam 
36362b9b2c46SYazen Ghannam 		goto err_enable;
3637360b7f3cSBorislav Petkov 	}
36387d6034d3SDoug Thompson 
3639582f94b5SYazen Ghannam 	dump_misc_regs(pvt);
3640582f94b5SYazen Ghannam 
36417d6034d3SDoug Thompson 	return ret;
36422299ef71SBorislav Petkov 
36432299ef71SBorislav Petkov err_enable:
364480355a3bSYazen Ghannam 	hw_info_put(pvt);
364580355a3bSYazen Ghannam 	kfree(pvt);
364680355a3bSYazen Ghannam 
364780355a3bSYazen Ghannam err_settings:
36482299ef71SBorislav Petkov 	kfree(s);
36492299ef71SBorislav Petkov 	ecc_stngs[nid] = NULL;
36502299ef71SBorislav Petkov 
36512299ef71SBorislav Petkov err_out:
36522299ef71SBorislav Petkov 	return ret;
36537d6034d3SDoug Thompson }
36547d6034d3SDoug Thompson 
36553f37a36bSBorislav Petkov static void remove_one_instance(unsigned int nid)
36567d6034d3SDoug Thompson {
3657360b7f3cSBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
3658360b7f3cSBorislav Petkov 	struct ecc_settings *s = ecc_stngs[nid];
36593f37a36bSBorislav Petkov 	struct mem_ctl_info *mci;
36603f37a36bSBorislav Petkov 	struct amd64_pvt *pvt;
36617d6034d3SDoug Thompson 
36627d6034d3SDoug Thompson 	/* Remove from EDAC CORE tracking list */
36633f37a36bSBorislav Petkov 	mci = edac_mc_del_mc(&F3->dev);
36647d6034d3SDoug Thompson 	if (!mci)
36657d6034d3SDoug Thompson 		return;
36667d6034d3SDoug Thompson 
36677d6034d3SDoug Thompson 	pvt = mci->pvt_info;
36687d6034d3SDoug Thompson 
3669360b7f3cSBorislav Petkov 	restore_ecc_error_reporting(s, nid, F3);
36707d6034d3SDoug Thompson 
3671360b7f3cSBorislav Petkov 	kfree(ecc_stngs[nid]);
3672360b7f3cSBorislav Petkov 	ecc_stngs[nid] = NULL;
3673ae7bb7c6SBorislav Petkov 
36747d6034d3SDoug Thompson 	/* Free the EDAC CORE resources */
36758f68ed97SBorislav Petkov 	mci->pvt_info = NULL;
36768f68ed97SBorislav Petkov 
367780355a3bSYazen Ghannam 	hw_info_put(pvt);
36788f68ed97SBorislav Petkov 	kfree(pvt);
36797d6034d3SDoug Thompson 	edac_mc_free(mci);
36807d6034d3SDoug Thompson }
36817d6034d3SDoug Thompson 
3682360b7f3cSBorislav Petkov static void setup_pci_device(void)
36837d6034d3SDoug Thompson {
3684d1ea71cdSBorislav Petkov 	if (pci_ctl)
36857d6034d3SDoug Thompson 		return;
36867d6034d3SDoug Thompson 
3687706657b1SBorislav Petkov 	pci_ctl = edac_pci_create_generic_ctl(pci_ctl_dev, EDAC_MOD_STR);
3688d1ea71cdSBorislav Petkov 	if (!pci_ctl) {
3689d1ea71cdSBorislav Petkov 		pr_warn("%s(): Unable to create PCI control\n", __func__);
3690d1ea71cdSBorislav Petkov 		pr_warn("%s(): PCI error report via EDAC not set\n", __func__);
36917d6034d3SDoug Thompson 	}
36927d6034d3SDoug Thompson }
36937d6034d3SDoug Thompson 
3694d6efab74SYazen Ghannam static const struct x86_cpu_id amd64_cpuids[] = {
369529842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x0F, NULL),
369629842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x10, NULL),
369729842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x15, NULL),
369829842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x16, NULL),
369929842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x17, NULL),
370029842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(HYGON,	0x18, NULL),
370129842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x19, NULL),
3702d6efab74SYazen Ghannam 	{ }
3703d6efab74SYazen Ghannam };
3704d6efab74SYazen Ghannam MODULE_DEVICE_TABLE(x86cpu, amd64_cpuids);
3705d6efab74SYazen Ghannam 
37067d6034d3SDoug Thompson static int __init amd64_edac_init(void)
37077d6034d3SDoug Thompson {
3708301375e7SToshi Kani 	const char *owner;
3709360b7f3cSBorislav Petkov 	int err = -ENODEV;
37103f37a36bSBorislav Petkov 	int i;
37117d6034d3SDoug Thompson 
3712301375e7SToshi Kani 	owner = edac_get_owner();
3713301375e7SToshi Kani 	if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
3714301375e7SToshi Kani 		return -EBUSY;
3715301375e7SToshi Kani 
37161bd9900bSYazen Ghannam 	if (!x86_match_cpu(amd64_cpuids))
37171bd9900bSYazen Ghannam 		return -ENODEV;
37181bd9900bSYazen Ghannam 
37199653a5c7SHans Rosenfeld 	if (amd_cache_northbridges() < 0)
37201bd9900bSYazen Ghannam 		return -ENODEV;
37217d6034d3SDoug Thompson 
37226ba92feaSBorislav Petkov 	opstate_init();
37236ba92feaSBorislav Petkov 
3724cc4d8860SBorislav Petkov 	err = -ENOMEM;
37256396bb22SKees Cook 	ecc_stngs = kcalloc(amd_nb_num(), sizeof(ecc_stngs[0]), GFP_KERNEL);
37262ec591acSBorislav Petkov 	if (!ecc_stngs)
3727a9f0fbe2SBorislav Petkov 		goto err_free;
3728cc4d8860SBorislav Petkov 
372950542251SBorislav Petkov 	msrs = msrs_alloc();
373056b34b91SBorislav Petkov 	if (!msrs)
3731360b7f3cSBorislav Petkov 		goto err_free;
373250542251SBorislav Petkov 
37332287c636SYazen Ghannam 	for (i = 0; i < amd_nb_num(); i++) {
37342287c636SYazen Ghannam 		err = probe_one_instance(i);
37352287c636SYazen Ghannam 		if (err) {
37363f37a36bSBorislav Petkov 			/* unwind properly */
37373f37a36bSBorislav Petkov 			while (--i >= 0)
37383f37a36bSBorislav Petkov 				remove_one_instance(i);
37397d6034d3SDoug Thompson 
37403f37a36bSBorislav Petkov 			goto err_pci;
37413f37a36bSBorislav Petkov 		}
37422287c636SYazen Ghannam 	}
37437d6034d3SDoug Thompson 
37444688c9b4SYazen Ghannam 	if (!edac_has_mcs()) {
37454688c9b4SYazen Ghannam 		err = -ENODEV;
37464688c9b4SYazen Ghannam 		goto err_pci;
37474688c9b4SYazen Ghannam 	}
37484688c9b4SYazen Ghannam 
3749234365f5SYazen Ghannam 	/* register stuff with EDAC MCE */
3750234365f5SYazen Ghannam 	if (boot_cpu_data.x86 >= 0x17)
3751234365f5SYazen Ghannam 		amd_register_ecc_decoder(decode_umc_error);
3752234365f5SYazen Ghannam 	else
3753234365f5SYazen Ghannam 		amd_register_ecc_decoder(decode_bus_error);
3754234365f5SYazen Ghannam 
3755360b7f3cSBorislav Petkov 	setup_pci_device();
3756f5b10c45STomasz Pala 
3757f5b10c45STomasz Pala #ifdef CONFIG_X86_32
3758f5b10c45STomasz Pala 	amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR);
3759f5b10c45STomasz Pala #endif
3760f5b10c45STomasz Pala 
3761de0336b3SBorislav Petkov 	printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION);
3762de0336b3SBorislav Petkov 
37637d6034d3SDoug Thompson 	return 0;
37647d6034d3SDoug Thompson 
376556b34b91SBorislav Petkov err_pci:
3766706657b1SBorislav Petkov 	pci_ctl_dev = NULL;
3767706657b1SBorislav Petkov 
376856b34b91SBorislav Petkov 	msrs_free(msrs);
376956b34b91SBorislav Petkov 	msrs = NULL;
3770cc4d8860SBorislav Petkov 
3771360b7f3cSBorislav Petkov err_free:
3772360b7f3cSBorislav Petkov 	kfree(ecc_stngs);
3773360b7f3cSBorislav Petkov 	ecc_stngs = NULL;
3774360b7f3cSBorislav Petkov 
37757d6034d3SDoug Thompson 	return err;
37767d6034d3SDoug Thompson }
37777d6034d3SDoug Thompson 
37787d6034d3SDoug Thompson static void __exit amd64_edac_exit(void)
37797d6034d3SDoug Thompson {
37803f37a36bSBorislav Petkov 	int i;
37813f37a36bSBorislav Petkov 
3782d1ea71cdSBorislav Petkov 	if (pci_ctl)
3783d1ea71cdSBorislav Petkov 		edac_pci_release_generic_ctl(pci_ctl);
37847d6034d3SDoug Thompson 
3785234365f5SYazen Ghannam 	/* unregister from EDAC MCE */
3786234365f5SYazen Ghannam 	if (boot_cpu_data.x86 >= 0x17)
3787234365f5SYazen Ghannam 		amd_unregister_ecc_decoder(decode_umc_error);
3788234365f5SYazen Ghannam 	else
3789234365f5SYazen Ghannam 		amd_unregister_ecc_decoder(decode_bus_error);
3790234365f5SYazen Ghannam 
37913f37a36bSBorislav Petkov 	for (i = 0; i < amd_nb_num(); i++)
37923f37a36bSBorislav Petkov 		remove_one_instance(i);
379350542251SBorislav Petkov 
3794ae7bb7c6SBorislav Petkov 	kfree(ecc_stngs);
3795ae7bb7c6SBorislav Petkov 	ecc_stngs = NULL;
3796ae7bb7c6SBorislav Petkov 
3797706657b1SBorislav Petkov 	pci_ctl_dev = NULL;
3798706657b1SBorislav Petkov 
379950542251SBorislav Petkov 	msrs_free(msrs);
380050542251SBorislav Petkov 	msrs = NULL;
38017d6034d3SDoug Thompson }
38027d6034d3SDoug Thompson 
38037d6034d3SDoug Thompson module_init(amd64_edac_init);
38047d6034d3SDoug Thompson module_exit(amd64_edac_exit);
38057d6034d3SDoug Thompson 
38067d6034d3SDoug Thompson MODULE_LICENSE("GPL");
38077d6034d3SDoug Thompson MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, "
38087d6034d3SDoug Thompson 		"Dave Peterson, Thayne Harbaugh");
38097d6034d3SDoug Thompson MODULE_DESCRIPTION("MC support for AMD64 memory controllers - "
38107d6034d3SDoug Thompson 		EDAC_AMD64_VERSION);
38117d6034d3SDoug Thompson 
38127d6034d3SDoug Thompson module_param(edac_op_state, int, 0444);
38137d6034d3SDoug Thompson MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
3814