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