xref: /openbmc/linux/drivers/edac/amd64_edac.c (revision 6c13d7ff)
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  */
503e2ce7255SDoug Thompson int amd64_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 }
557e2ce7255SDoug Thompson EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info);
558e2ce7255SDoug Thompson 
55993c2df58SDoug Thompson /*
56093c2df58SDoug Thompson  * Return the DramAddr that the SysAddr given by @sys_addr maps to.  It is
56193c2df58SDoug Thompson  * assumed that sys_addr maps to the node given by mci.
56293c2df58SDoug Thompson  *
56393c2df58SDoug Thompson  * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section
56493c2df58SDoug Thompson  * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a
56593c2df58SDoug Thompson  * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled,
56693c2df58SDoug Thompson  * then it is also involved in translating a SysAddr to a DramAddr. Sections
56793c2df58SDoug Thompson  * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting.
56893c2df58SDoug Thompson  * These parts of the documentation are unclear. I interpret them as follows:
56993c2df58SDoug Thompson  *
57093c2df58SDoug Thompson  * When node n receives a SysAddr, it processes the SysAddr as follows:
57193c2df58SDoug Thompson  *
57293c2df58SDoug Thompson  * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM
57393c2df58SDoug Thompson  *    Limit registers for node n. If the SysAddr is not within the range
57493c2df58SDoug Thompson  *    specified by the base and limit values, then node n ignores the Sysaddr
57593c2df58SDoug Thompson  *    (since it does not map to node n). Otherwise continue to step 2 below.
57693c2df58SDoug Thompson  *
57793c2df58SDoug Thompson  * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is
57893c2df58SDoug Thompson  *    disabled so skip to step 3 below. Otherwise see if the SysAddr is within
57993c2df58SDoug Thompson  *    the range of relocated addresses (starting at 0x100000000) from the DRAM
58093c2df58SDoug Thompson  *    hole. If not, skip to step 3 below. Else get the value of the
58193c2df58SDoug Thompson  *    DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the
58293c2df58SDoug Thompson  *    offset defined by this value from the SysAddr.
58393c2df58SDoug Thompson  *
58493c2df58SDoug Thompson  * 3. Obtain the base address for node n from the DRAMBase field of the DRAM
58593c2df58SDoug Thompson  *    Base register for node n. To obtain the DramAddr, subtract the base
58693c2df58SDoug Thompson  *    address from the SysAddr, as shown near the start of section 3.4.4 (p.70).
58793c2df58SDoug Thompson  */
58893c2df58SDoug Thompson static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
58993c2df58SDoug Thompson {
5907f19bf75SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
59193c2df58SDoug Thompson 	u64 dram_base, hole_base, hole_offset, hole_size, dram_addr;
5921f31677eSBorislav Petkov 	int ret;
59393c2df58SDoug Thompson 
5947f19bf75SBorislav Petkov 	dram_base = get_dram_base(pvt, pvt->mc_node_id);
59593c2df58SDoug Thompson 
59693c2df58SDoug Thompson 	ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
59793c2df58SDoug Thompson 				      &hole_size);
59893c2df58SDoug Thompson 	if (!ret) {
5991f31677eSBorislav Petkov 		if ((sys_addr >= (1ULL << 32)) &&
6001f31677eSBorislav Petkov 		    (sys_addr < ((1ULL << 32) + hole_size))) {
60193c2df58SDoug Thompson 			/* use DHAR to translate SysAddr to DramAddr */
60293c2df58SDoug Thompson 			dram_addr = sys_addr - hole_offset;
60393c2df58SDoug Thompson 
604956b9ba1SJoe Perches 			edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
60593c2df58SDoug Thompson 				 (unsigned long)sys_addr,
60693c2df58SDoug Thompson 				 (unsigned long)dram_addr);
60793c2df58SDoug Thompson 
60893c2df58SDoug Thompson 			return dram_addr;
60993c2df58SDoug Thompson 		}
61093c2df58SDoug Thompson 	}
61193c2df58SDoug Thompson 
61293c2df58SDoug Thompson 	/*
61393c2df58SDoug Thompson 	 * Translate the SysAddr to a DramAddr as shown near the start of
61493c2df58SDoug Thompson 	 * section 3.4.4 (p. 70).  Although sys_addr is a 64-bit value, the k8
61593c2df58SDoug Thompson 	 * only deals with 40-bit values.  Therefore we discard bits 63-40 of
61693c2df58SDoug Thompson 	 * sys_addr below.  If bit 39 of sys_addr is 1 then the bits we
61793c2df58SDoug Thompson 	 * discard are all 1s.  Otherwise the bits we discard are all 0s.  See
61893c2df58SDoug Thompson 	 * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture
61993c2df58SDoug Thompson 	 * Programmer's Manual Volume 1 Application Programming.
62093c2df58SDoug Thompson 	 */
62110ef6b0dSChen, Gong 	dram_addr = (sys_addr & GENMASK_ULL(39, 0)) - dram_base;
62293c2df58SDoug Thompson 
623956b9ba1SJoe Perches 	edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
624956b9ba1SJoe Perches 		 (unsigned long)sys_addr, (unsigned long)dram_addr);
62593c2df58SDoug Thompson 	return dram_addr;
62693c2df58SDoug Thompson }
62793c2df58SDoug Thompson 
62893c2df58SDoug Thompson /*
62993c2df58SDoug Thompson  * @intlv_en is the value of the IntlvEn field from a DRAM Base register
63093c2df58SDoug Thompson  * (section 3.4.4.1).  Return the number of bits from a SysAddr that are used
63193c2df58SDoug Thompson  * for node interleaving.
63293c2df58SDoug Thompson  */
63393c2df58SDoug Thompson static int num_node_interleave_bits(unsigned intlv_en)
63493c2df58SDoug Thompson {
63593c2df58SDoug Thompson 	static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 };
63693c2df58SDoug Thompson 	int n;
63793c2df58SDoug Thompson 
63893c2df58SDoug Thompson 	BUG_ON(intlv_en > 7);
63993c2df58SDoug Thompson 	n = intlv_shift_table[intlv_en];
64093c2df58SDoug Thompson 	return n;
64193c2df58SDoug Thompson }
64293c2df58SDoug Thompson 
64393c2df58SDoug Thompson /* Translate the DramAddr given by @dram_addr to an InputAddr. */
64493c2df58SDoug Thompson static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
64593c2df58SDoug Thompson {
64693c2df58SDoug Thompson 	struct amd64_pvt *pvt;
64793c2df58SDoug Thompson 	int intlv_shift;
64893c2df58SDoug Thompson 	u64 input_addr;
64993c2df58SDoug Thompson 
65093c2df58SDoug Thompson 	pvt = mci->pvt_info;
65193c2df58SDoug Thompson 
65293c2df58SDoug Thompson 	/*
65393c2df58SDoug Thompson 	 * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
65493c2df58SDoug Thompson 	 * concerning translating a DramAddr to an InputAddr.
65593c2df58SDoug Thompson 	 */
6567f19bf75SBorislav Petkov 	intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
65710ef6b0dSChen, Gong 	input_addr = ((dram_addr >> intlv_shift) & GENMASK_ULL(35, 12)) +
65893c2df58SDoug Thompson 		      (dram_addr & 0xfff);
65993c2df58SDoug Thompson 
660956b9ba1SJoe Perches 	edac_dbg(2, "  Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
66193c2df58SDoug Thompson 		 intlv_shift, (unsigned long)dram_addr,
66293c2df58SDoug Thompson 		 (unsigned long)input_addr);
66393c2df58SDoug Thompson 
66493c2df58SDoug Thompson 	return input_addr;
66593c2df58SDoug Thompson }
66693c2df58SDoug Thompson 
66793c2df58SDoug Thompson /*
66893c2df58SDoug Thompson  * Translate the SysAddr represented by @sys_addr to an InputAddr.  It is
66993c2df58SDoug Thompson  * assumed that @sys_addr maps to the node given by mci.
67093c2df58SDoug Thompson  */
67193c2df58SDoug Thompson static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
67293c2df58SDoug Thompson {
67393c2df58SDoug Thompson 	u64 input_addr;
67493c2df58SDoug Thompson 
67593c2df58SDoug Thompson 	input_addr =
67693c2df58SDoug Thompson 	    dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
67793c2df58SDoug Thompson 
678c19ca6cbSMasanari Iida 	edac_dbg(2, "SysAddr 0x%lx translates to InputAddr 0x%lx\n",
67993c2df58SDoug Thompson 		 (unsigned long)sys_addr, (unsigned long)input_addr);
68093c2df58SDoug Thompson 
68193c2df58SDoug Thompson 	return input_addr;
68293c2df58SDoug Thompson }
68393c2df58SDoug Thompson 
68493c2df58SDoug Thompson /* Map the Error address to a PAGE and PAGE OFFSET. */
68593c2df58SDoug Thompson static inline void error_address_to_page_and_offset(u64 error_address,
68633ca0643SBorislav Petkov 						    struct err_info *err)
68793c2df58SDoug Thompson {
68833ca0643SBorislav Petkov 	err->page = (u32) (error_address >> PAGE_SHIFT);
68933ca0643SBorislav Petkov 	err->offset = ((u32) error_address) & ~PAGE_MASK;
69093c2df58SDoug Thompson }
69193c2df58SDoug Thompson 
69293c2df58SDoug Thompson /*
69393c2df58SDoug Thompson  * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address
69493c2df58SDoug Thompson  * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers
69593c2df58SDoug Thompson  * of a node that detected an ECC memory error.  mci represents the node that
69693c2df58SDoug Thompson  * the error address maps to (possibly different from the node that detected
69793c2df58SDoug Thompson  * the error).  Return the number of the csrow that sys_addr maps to, or -1 on
69893c2df58SDoug Thompson  * error.
69993c2df58SDoug Thompson  */
70093c2df58SDoug Thompson static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
70193c2df58SDoug Thompson {
70293c2df58SDoug Thompson 	int csrow;
70393c2df58SDoug Thompson 
70493c2df58SDoug Thompson 	csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr));
70593c2df58SDoug Thompson 
70693c2df58SDoug Thompson 	if (csrow == -1)
70724f9a7feSBorislav Petkov 		amd64_mc_err(mci, "Failed to translate InputAddr to csrow for "
70893c2df58SDoug Thompson 				  "address 0x%lx\n", (unsigned long)sys_addr);
70993c2df58SDoug Thompson 	return csrow;
71093c2df58SDoug Thompson }
711e2ce7255SDoug Thompson 
712bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
7132da11654SDoug Thompson 
7142da11654SDoug Thompson /*
7152da11654SDoug Thompson  * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
7162da11654SDoug Thompson  * are ECC capable.
7172da11654SDoug Thompson  */
718d1ea71cdSBorislav Petkov static unsigned long determine_edac_cap(struct amd64_pvt *pvt)
7192da11654SDoug Thompson {
7201f6189edSDan Carpenter 	unsigned long edac_cap = EDAC_FLAG_NONE;
721d27f3a34SYazen Ghannam 	u8 bit;
7222da11654SDoug Thompson 
723d27f3a34SYazen Ghannam 	if (pvt->umc) {
724d27f3a34SYazen Ghannam 		u8 i, umc_en_mask = 0, dimm_ecc_en_mask = 0;
725d27f3a34SYazen Ghannam 
7264d30d2bcSYazen Ghannam 		for_each_umc(i) {
727d27f3a34SYazen Ghannam 			if (!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT))
728d27f3a34SYazen Ghannam 				continue;
729d27f3a34SYazen Ghannam 
730d27f3a34SYazen Ghannam 			umc_en_mask |= BIT(i);
731d27f3a34SYazen Ghannam 
732d27f3a34SYazen Ghannam 			/* UMC Configuration bit 12 (DimmEccEn) */
733d27f3a34SYazen Ghannam 			if (pvt->umc[i].umc_cfg & BIT(12))
734d27f3a34SYazen Ghannam 				dimm_ecc_en_mask |= BIT(i);
735d27f3a34SYazen Ghannam 		}
736d27f3a34SYazen Ghannam 
737d27f3a34SYazen Ghannam 		if (umc_en_mask == dimm_ecc_en_mask)
738d27f3a34SYazen Ghannam 			edac_cap = EDAC_FLAG_SECDED;
739d27f3a34SYazen Ghannam 	} else {
740a4b4bedcSBorislav Petkov 		bit = (pvt->fam > 0xf || pvt->ext_model >= K8_REV_F)
7412da11654SDoug Thompson 			? 19
7422da11654SDoug Thompson 			: 17;
7432da11654SDoug Thompson 
744584fcff4SBorislav Petkov 		if (pvt->dclr0 & BIT(bit))
7452da11654SDoug Thompson 			edac_cap = EDAC_FLAG_SECDED;
746d27f3a34SYazen Ghannam 	}
7472da11654SDoug Thompson 
7482da11654SDoug Thompson 	return edac_cap;
7492da11654SDoug Thompson }
7502da11654SDoug Thompson 
751d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *, u8);
7522da11654SDoug Thompson 
753d1ea71cdSBorislav Petkov static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan)
75468798e17SBorislav Petkov {
755956b9ba1SJoe Perches 	edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
75668798e17SBorislav Petkov 
757a597d2a5SAravind Gopalakrishnan 	if (pvt->dram_type == MEM_LRDDR3) {
758a597d2a5SAravind Gopalakrishnan 		u32 dcsm = pvt->csels[chan].csmasks[0];
759a597d2a5SAravind Gopalakrishnan 		/*
760a597d2a5SAravind Gopalakrishnan 		 * It's assumed all LRDIMMs in a DCT are going to be of
761a597d2a5SAravind Gopalakrishnan 		 * same 'type' until proven otherwise. So, use a cs
762a597d2a5SAravind Gopalakrishnan 		 * value of '0' here to get dcsm value.
763a597d2a5SAravind Gopalakrishnan 		 */
764a597d2a5SAravind Gopalakrishnan 		edac_dbg(1, " LRDIMM %dx rank multiply\n", (dcsm & 0x3));
765a597d2a5SAravind Gopalakrishnan 	}
766a597d2a5SAravind Gopalakrishnan 
767a597d2a5SAravind Gopalakrishnan 	edac_dbg(1, "All DIMMs support ECC:%s\n",
76868798e17SBorislav Petkov 		    (dclr & BIT(19)) ? "yes" : "no");
76968798e17SBorislav Petkov 
770a597d2a5SAravind Gopalakrishnan 
771956b9ba1SJoe Perches 	edac_dbg(1, "  PAR/ERR parity: %s\n",
77268798e17SBorislav Petkov 		 (dclr & BIT(8)) ?  "enabled" : "disabled");
77368798e17SBorislav Petkov 
774a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10)
775956b9ba1SJoe Perches 		edac_dbg(1, "  DCT 128bit mode width: %s\n",
77668798e17SBorislav Petkov 			 (dclr & BIT(11)) ?  "128b" : "64b");
77768798e17SBorislav Petkov 
778956b9ba1SJoe Perches 	edac_dbg(1, "  x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
77968798e17SBorislav Petkov 		 (dclr & BIT(12)) ?  "yes" : "no",
78068798e17SBorislav Petkov 		 (dclr & BIT(13)) ?  "yes" : "no",
78168798e17SBorislav Petkov 		 (dclr & BIT(14)) ?  "yes" : "no",
78268798e17SBorislav Petkov 		 (dclr & BIT(15)) ?  "yes" : "no");
78368798e17SBorislav Petkov }
78468798e17SBorislav Petkov 
785e53a3b26SYazen Ghannam #define CS_EVEN_PRIMARY		BIT(0)
786e53a3b26SYazen Ghannam #define CS_ODD_PRIMARY		BIT(1)
78781f5090dSYazen Ghannam #define CS_EVEN_SECONDARY	BIT(2)
78881f5090dSYazen Ghannam #define CS_ODD_SECONDARY	BIT(3)
789e53a3b26SYazen Ghannam 
79081f5090dSYazen Ghannam #define CS_EVEN			(CS_EVEN_PRIMARY | CS_EVEN_SECONDARY)
79181f5090dSYazen Ghannam #define CS_ODD			(CS_ODD_PRIMARY | CS_ODD_SECONDARY)
792e53a3b26SYazen Ghannam 
793e53a3b26SYazen Ghannam static int f17_get_cs_mode(int dimm, u8 ctrl, struct amd64_pvt *pvt)
794fc00c6a4SYazen Ghannam {
795e53a3b26SYazen Ghannam 	int cs_mode = 0;
796fc00c6a4SYazen Ghannam 
797e53a3b26SYazen Ghannam 	if (csrow_enabled(2 * dimm, ctrl, pvt))
798e53a3b26SYazen Ghannam 		cs_mode |= CS_EVEN_PRIMARY;
799fc00c6a4SYazen Ghannam 
800e53a3b26SYazen Ghannam 	if (csrow_enabled(2 * dimm + 1, ctrl, pvt))
801e53a3b26SYazen Ghannam 		cs_mode |= CS_ODD_PRIMARY;
802e53a3b26SYazen Ghannam 
80381f5090dSYazen Ghannam 	/* Asymmetric dual-rank DIMM support. */
80481f5090dSYazen Ghannam 	if (csrow_sec_enabled(2 * dimm + 1, ctrl, pvt))
80581f5090dSYazen Ghannam 		cs_mode |= CS_ODD_SECONDARY;
80681f5090dSYazen Ghannam 
807e53a3b26SYazen Ghannam 	return cs_mode;
808fc00c6a4SYazen Ghannam }
809fc00c6a4SYazen Ghannam 
81007ed82efSYazen Ghannam static void debug_display_dimm_sizes_df(struct amd64_pvt *pvt, u8 ctrl)
81107ed82efSYazen Ghannam {
812e53a3b26SYazen Ghannam 	int dimm, size0, size1, cs0, cs1, cs_mode;
81307ed82efSYazen Ghannam 
81407ed82efSYazen Ghannam 	edac_printk(KERN_DEBUG, EDAC_MC, "UMC%d chip selects:\n", ctrl);
81507ed82efSYazen Ghannam 
816d971e28eSYazen Ghannam 	for (dimm = 0; dimm < 2; dimm++) {
817eb77e6b8SYazen Ghannam 		cs0 = dimm * 2;
818eb77e6b8SYazen Ghannam 		cs1 = dimm * 2 + 1;
819eb77e6b8SYazen Ghannam 
820e53a3b26SYazen Ghannam 		cs_mode = f17_get_cs_mode(dimm, ctrl, pvt);
821e53a3b26SYazen Ghannam 
822e53a3b26SYazen Ghannam 		size0 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs0);
823e53a3b26SYazen Ghannam 		size1 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs1);
82407ed82efSYazen Ghannam 
82507ed82efSYazen Ghannam 		amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
826eb77e6b8SYazen Ghannam 				cs0,	size0,
827eb77e6b8SYazen Ghannam 				cs1,	size1);
82807ed82efSYazen Ghannam 	}
82907ed82efSYazen Ghannam }
83007ed82efSYazen Ghannam 
83107ed82efSYazen Ghannam static void __dump_misc_regs_df(struct amd64_pvt *pvt)
83207ed82efSYazen Ghannam {
83307ed82efSYazen Ghannam 	struct amd64_umc *umc;
83407ed82efSYazen Ghannam 	u32 i, tmp, umc_base;
83507ed82efSYazen Ghannam 
8364d30d2bcSYazen Ghannam 	for_each_umc(i) {
83707ed82efSYazen Ghannam 		umc_base = get_umc_base(i);
83807ed82efSYazen Ghannam 		umc = &pvt->umc[i];
83907ed82efSYazen Ghannam 
84007ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d DIMM cfg: 0x%x\n", i, umc->dimm_cfg);
84107ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d UMC cfg: 0x%x\n", i, umc->umc_cfg);
84207ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d SDP ctrl: 0x%x\n", i, umc->sdp_ctrl);
84307ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d ECC ctrl: 0x%x\n", i, umc->ecc_ctrl);
84407ed82efSYazen Ghannam 
84507ed82efSYazen Ghannam 		amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ECC_BAD_SYMBOL, &tmp);
84607ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d ECC bad symbol: 0x%x\n", i, tmp);
84707ed82efSYazen Ghannam 
84807ed82efSYazen Ghannam 		amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_UMC_CAP, &tmp);
84907ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d UMC cap: 0x%x\n", i, tmp);
85007ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d UMC cap high: 0x%x\n", i, umc->umc_cap_hi);
85107ed82efSYazen Ghannam 
85207ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d ECC capable: %s, ChipKill ECC capable: %s\n",
85307ed82efSYazen Ghannam 				i, (umc->umc_cap_hi & BIT(30)) ? "yes" : "no",
85407ed82efSYazen Ghannam 				    (umc->umc_cap_hi & BIT(31)) ? "yes" : "no");
85507ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d All DIMMs support ECC: %s\n",
85607ed82efSYazen Ghannam 				i, (umc->umc_cfg & BIT(12)) ? "yes" : "no");
85707ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d x4 DIMMs present: %s\n",
85807ed82efSYazen Ghannam 				i, (umc->dimm_cfg & BIT(6)) ? "yes" : "no");
85907ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d x16 DIMMs present: %s\n",
86007ed82efSYazen Ghannam 				i, (umc->dimm_cfg & BIT(7)) ? "yes" : "no");
86107ed82efSYazen Ghannam 
86207ed82efSYazen Ghannam 		if (pvt->dram_type == MEM_LRDDR4) {
86307ed82efSYazen Ghannam 			amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ADDR_CFG, &tmp);
86407ed82efSYazen Ghannam 			edac_dbg(1, "UMC%d LRDIMM %dx rank multiply\n",
86507ed82efSYazen Ghannam 					i, 1 << ((tmp >> 4) & 0x3));
86607ed82efSYazen Ghannam 		}
86707ed82efSYazen Ghannam 
86807ed82efSYazen Ghannam 		debug_display_dimm_sizes_df(pvt, i);
86907ed82efSYazen Ghannam 	}
87007ed82efSYazen Ghannam 
87107ed82efSYazen Ghannam 	edac_dbg(1, "F0x104 (DRAM Hole Address): 0x%08x, base: 0x%08x\n",
87207ed82efSYazen Ghannam 		 pvt->dhar, dhar_base(pvt));
87307ed82efSYazen Ghannam }
87407ed82efSYazen Ghannam 
8752da11654SDoug Thompson /* Display and decode various NB registers for debug purposes. */
87607ed82efSYazen Ghannam static void __dump_misc_regs(struct amd64_pvt *pvt)
8772da11654SDoug Thompson {
878956b9ba1SJoe Perches 	edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
8792da11654SDoug Thompson 
880956b9ba1SJoe Perches 	edac_dbg(1, "  NB two channel DRAM capable: %s\n",
8815980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
88268798e17SBorislav Petkov 
883956b9ba1SJoe Perches 	edac_dbg(1, "  ECC capable: %s, ChipKill ECC capable: %s\n",
8845980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
8855980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
88668798e17SBorislav Petkov 
887d1ea71cdSBorislav Petkov 	debug_dump_dramcfg_low(pvt, pvt->dclr0, 0);
8882da11654SDoug Thompson 
889956b9ba1SJoe Perches 	edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
8902da11654SDoug Thompson 
891956b9ba1SJoe Perches 	edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n",
892bc21fa57SBorislav Petkov 		 pvt->dhar, dhar_base(pvt),
893a4b4bedcSBorislav Petkov 		 (pvt->fam == 0xf) ? k8_dhar_offset(pvt)
894bc21fa57SBorislav Petkov 				   : f10_dhar_offset(pvt));
8952da11654SDoug Thompson 
896d1ea71cdSBorislav Petkov 	debug_display_dimm_sizes(pvt, 0);
8974d796364SBorislav Petkov 
8984d796364SBorislav Petkov 	/* everything below this point is Fam10h and above */
899a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
9002da11654SDoug Thompson 		return;
9014d796364SBorislav Petkov 
902d1ea71cdSBorislav Petkov 	debug_display_dimm_sizes(pvt, 1);
9032da11654SDoug Thompson 
9048de1d91eSBorislav Petkov 	/* Only if NOT ganged does dclr1 have valid info */
90568798e17SBorislav Petkov 	if (!dct_ganging_enabled(pvt))
906d1ea71cdSBorislav Petkov 		debug_dump_dramcfg_low(pvt, pvt->dclr1, 1);
9072da11654SDoug Thompson }
9082da11654SDoug Thompson 
90907ed82efSYazen Ghannam /* Display and decode various NB registers for debug purposes. */
91007ed82efSYazen Ghannam static void dump_misc_regs(struct amd64_pvt *pvt)
91107ed82efSYazen Ghannam {
91207ed82efSYazen Ghannam 	if (pvt->umc)
91307ed82efSYazen Ghannam 		__dump_misc_regs_df(pvt);
91407ed82efSYazen Ghannam 	else
91507ed82efSYazen Ghannam 		__dump_misc_regs(pvt);
91607ed82efSYazen Ghannam 
91707ed82efSYazen Ghannam 	edac_dbg(1, "  DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
91807ed82efSYazen Ghannam 
9197835961dSYazen Ghannam 	amd64_info("using x%u syndromes.\n", pvt->ecc_sym_sz);
92007ed82efSYazen Ghannam }
92107ed82efSYazen Ghannam 
92294be4bffSDoug Thompson /*
92318b94f66SAravind Gopalakrishnan  * See BKDG, F2x[1,0][5C:40], F2[1,0][6C:60]
92494be4bffSDoug Thompson  */
92511c75eadSBorislav Petkov static void prep_chip_selects(struct amd64_pvt *pvt)
92694be4bffSDoug Thompson {
92718b94f66SAravind Gopalakrishnan 	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
92811c75eadSBorislav Petkov 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
92911c75eadSBorislav Petkov 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8;
930a597d2a5SAravind Gopalakrishnan 	} else if (pvt->fam == 0x15 && pvt->model == 0x30) {
93118b94f66SAravind Gopalakrishnan 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4;
93218b94f66SAravind Gopalakrishnan 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2;
933d971e28eSYazen Ghannam 	} else if (pvt->fam >= 0x17) {
934d971e28eSYazen Ghannam 		int umc;
935d971e28eSYazen Ghannam 
936d971e28eSYazen Ghannam 		for_each_umc(umc) {
937d971e28eSYazen Ghannam 			pvt->csels[umc].b_cnt = 4;
938d971e28eSYazen Ghannam 			pvt->csels[umc].m_cnt = 2;
939d971e28eSYazen Ghannam 		}
940d971e28eSYazen Ghannam 
9419d858bb1SBorislav Petkov 	} else {
94211c75eadSBorislav Petkov 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
94311c75eadSBorislav Petkov 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4;
9449d858bb1SBorislav Petkov 	}
94594be4bffSDoug Thompson }
94694be4bffSDoug Thompson 
947d971e28eSYazen Ghannam static void read_umc_base_mask(struct amd64_pvt *pvt)
948d971e28eSYazen Ghannam {
9497574729eSYazen Ghannam 	u32 umc_base_reg, umc_base_reg_sec;
9507574729eSYazen Ghannam 	u32 umc_mask_reg, umc_mask_reg_sec;
9517574729eSYazen Ghannam 	u32 base_reg, base_reg_sec;
9527574729eSYazen Ghannam 	u32 mask_reg, mask_reg_sec;
9537574729eSYazen Ghannam 	u32 *base, *base_sec;
9547574729eSYazen Ghannam 	u32 *mask, *mask_sec;
955d971e28eSYazen Ghannam 	int cs, umc;
956d971e28eSYazen Ghannam 
957d971e28eSYazen Ghannam 	for_each_umc(umc) {
958d971e28eSYazen Ghannam 		umc_base_reg = get_umc_base(umc) + UMCCH_BASE_ADDR;
9597574729eSYazen Ghannam 		umc_base_reg_sec = get_umc_base(umc) + UMCCH_BASE_ADDR_SEC;
960d971e28eSYazen Ghannam 
961d971e28eSYazen Ghannam 		for_each_chip_select(cs, umc, pvt) {
962d971e28eSYazen Ghannam 			base = &pvt->csels[umc].csbases[cs];
9637574729eSYazen Ghannam 			base_sec = &pvt->csels[umc].csbases_sec[cs];
964d971e28eSYazen Ghannam 
965d971e28eSYazen Ghannam 			base_reg = umc_base_reg + (cs * 4);
9667574729eSYazen Ghannam 			base_reg_sec = umc_base_reg_sec + (cs * 4);
967d971e28eSYazen Ghannam 
968d971e28eSYazen Ghannam 			if (!amd_smn_read(pvt->mc_node_id, base_reg, base))
969d971e28eSYazen Ghannam 				edac_dbg(0, "  DCSB%d[%d]=0x%08x reg: 0x%x\n",
970d971e28eSYazen Ghannam 					 umc, cs, *base, base_reg);
9717574729eSYazen Ghannam 
9727574729eSYazen Ghannam 			if (!amd_smn_read(pvt->mc_node_id, base_reg_sec, base_sec))
9737574729eSYazen Ghannam 				edac_dbg(0, "    DCSB_SEC%d[%d]=0x%08x reg: 0x%x\n",
9747574729eSYazen Ghannam 					 umc, cs, *base_sec, base_reg_sec);
975d971e28eSYazen Ghannam 		}
976d971e28eSYazen Ghannam 
977d971e28eSYazen Ghannam 		umc_mask_reg = get_umc_base(umc) + UMCCH_ADDR_MASK;
9787574729eSYazen Ghannam 		umc_mask_reg_sec = get_umc_base(umc) + UMCCH_ADDR_MASK_SEC;
979d971e28eSYazen Ghannam 
980d971e28eSYazen Ghannam 		for_each_chip_select_mask(cs, umc, pvt) {
981d971e28eSYazen Ghannam 			mask = &pvt->csels[umc].csmasks[cs];
9827574729eSYazen Ghannam 			mask_sec = &pvt->csels[umc].csmasks_sec[cs];
983d971e28eSYazen Ghannam 
984d971e28eSYazen Ghannam 			mask_reg = umc_mask_reg + (cs * 4);
9857574729eSYazen Ghannam 			mask_reg_sec = umc_mask_reg_sec + (cs * 4);
986d971e28eSYazen Ghannam 
987d971e28eSYazen Ghannam 			if (!amd_smn_read(pvt->mc_node_id, mask_reg, mask))
988d971e28eSYazen Ghannam 				edac_dbg(0, "  DCSM%d[%d]=0x%08x reg: 0x%x\n",
989d971e28eSYazen Ghannam 					 umc, cs, *mask, mask_reg);
9907574729eSYazen Ghannam 
9917574729eSYazen Ghannam 			if (!amd_smn_read(pvt->mc_node_id, mask_reg_sec, mask_sec))
9927574729eSYazen Ghannam 				edac_dbg(0, "    DCSM_SEC%d[%d]=0x%08x reg: 0x%x\n",
9937574729eSYazen Ghannam 					 umc, cs, *mask_sec, mask_reg_sec);
994d971e28eSYazen Ghannam 		}
995d971e28eSYazen Ghannam 	}
996d971e28eSYazen Ghannam }
997d971e28eSYazen Ghannam 
99894be4bffSDoug Thompson /*
99911c75eadSBorislav Petkov  * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers
100094be4bffSDoug Thompson  */
1001b2b0c605SBorislav Petkov static void read_dct_base_mask(struct amd64_pvt *pvt)
100294be4bffSDoug Thompson {
1003d971e28eSYazen Ghannam 	int cs;
100494be4bffSDoug Thompson 
100511c75eadSBorislav Petkov 	prep_chip_selects(pvt);
100694be4bffSDoug Thompson 
1007d971e28eSYazen Ghannam 	if (pvt->umc)
1008d971e28eSYazen Ghannam 		return read_umc_base_mask(pvt);
1009b64ce7cdSYazen Ghannam 
101011c75eadSBorislav Petkov 	for_each_chip_select(cs, 0, pvt) {
1011d971e28eSYazen Ghannam 		int reg0   = DCSB0 + (cs * 4);
1012d971e28eSYazen Ghannam 		int reg1   = DCSB1 + (cs * 4);
101311c75eadSBorislav Petkov 		u32 *base0 = &pvt->csels[0].csbases[cs];
101411c75eadSBorislav Petkov 		u32 *base1 = &pvt->csels[1].csbases[cs];
1015b2b0c605SBorislav Petkov 
10167981a28fSAravind Gopalakrishnan 		if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0))
1017956b9ba1SJoe Perches 			edac_dbg(0, "  DCSB0[%d]=0x%08x reg: F2x%x\n",
101811c75eadSBorislav Petkov 				 cs, *base0, reg0);
101994be4bffSDoug Thompson 
10207981a28fSAravind Gopalakrishnan 		if (pvt->fam == 0xf)
102111c75eadSBorislav Petkov 			continue;
1022b2b0c605SBorislav Petkov 
10237981a28fSAravind Gopalakrishnan 		if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1))
1024956b9ba1SJoe Perches 			edac_dbg(0, "  DCSB1[%d]=0x%08x reg: F2x%x\n",
10257981a28fSAravind Gopalakrishnan 				 cs, *base1, (pvt->fam == 0x10) ? reg1
10267981a28fSAravind Gopalakrishnan 							: reg0);
102794be4bffSDoug Thompson 	}
102894be4bffSDoug Thompson 
102911c75eadSBorislav Petkov 	for_each_chip_select_mask(cs, 0, pvt) {
1030d971e28eSYazen Ghannam 		int reg0   = DCSM0 + (cs * 4);
1031d971e28eSYazen Ghannam 		int reg1   = DCSM1 + (cs * 4);
103211c75eadSBorislav Petkov 		u32 *mask0 = &pvt->csels[0].csmasks[cs];
103311c75eadSBorislav Petkov 		u32 *mask1 = &pvt->csels[1].csmasks[cs];
1034b2b0c605SBorislav Petkov 
10357981a28fSAravind Gopalakrishnan 		if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0))
1036956b9ba1SJoe Perches 			edac_dbg(0, "    DCSM0[%d]=0x%08x reg: F2x%x\n",
103711c75eadSBorislav Petkov 				 cs, *mask0, reg0);
103894be4bffSDoug Thompson 
10397981a28fSAravind Gopalakrishnan 		if (pvt->fam == 0xf)
104011c75eadSBorislav Petkov 			continue;
1041b2b0c605SBorislav Petkov 
10427981a28fSAravind Gopalakrishnan 		if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1))
1043956b9ba1SJoe Perches 			edac_dbg(0, "    DCSM1[%d]=0x%08x reg: F2x%x\n",
10447981a28fSAravind Gopalakrishnan 				 cs, *mask1, (pvt->fam == 0x10) ? reg1
10457981a28fSAravind Gopalakrishnan 							: reg0);
104694be4bffSDoug Thompson 	}
10476ba5dcdcSBorislav Petkov }
104894be4bffSDoug Thompson 
1049a597d2a5SAravind Gopalakrishnan static void determine_memory_type(struct amd64_pvt *pvt)
105094be4bffSDoug Thompson {
1051a597d2a5SAravind Gopalakrishnan 	u32 dram_ctrl, dcsm;
105294be4bffSDoug Thompson 
1053dcd01394SYazen Ghannam 	if (pvt->umc) {
1054dcd01394SYazen Ghannam 		if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(5))
1055dcd01394SYazen Ghannam 			pvt->dram_type = MEM_LRDDR4;
1056dcd01394SYazen Ghannam 		else if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(4))
1057dcd01394SYazen Ghannam 			pvt->dram_type = MEM_RDDR4;
1058dcd01394SYazen Ghannam 		else
1059dcd01394SYazen Ghannam 			pvt->dram_type = MEM_DDR4;
1060dcd01394SYazen Ghannam 		return;
1061dcd01394SYazen Ghannam 	}
1062dcd01394SYazen Ghannam 
1063a597d2a5SAravind Gopalakrishnan 	switch (pvt->fam) {
1064a597d2a5SAravind Gopalakrishnan 	case 0xf:
1065a597d2a5SAravind Gopalakrishnan 		if (pvt->ext_model >= K8_REV_F)
1066a597d2a5SAravind Gopalakrishnan 			goto ddr3;
1067a597d2a5SAravind Gopalakrishnan 
1068a597d2a5SAravind Gopalakrishnan 		pvt->dram_type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
1069a597d2a5SAravind Gopalakrishnan 		return;
1070a597d2a5SAravind Gopalakrishnan 
1071a597d2a5SAravind Gopalakrishnan 	case 0x10:
10726b4c0bdeSBorislav Petkov 		if (pvt->dchr0 & DDR3_MODE)
1073a597d2a5SAravind Gopalakrishnan 			goto ddr3;
1074a597d2a5SAravind Gopalakrishnan 
1075a597d2a5SAravind Gopalakrishnan 		pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
1076a597d2a5SAravind Gopalakrishnan 		return;
1077a597d2a5SAravind Gopalakrishnan 
1078a597d2a5SAravind Gopalakrishnan 	case 0x15:
1079a597d2a5SAravind Gopalakrishnan 		if (pvt->model < 0x60)
1080a597d2a5SAravind Gopalakrishnan 			goto ddr3;
1081a597d2a5SAravind Gopalakrishnan 
1082a597d2a5SAravind Gopalakrishnan 		/*
1083a597d2a5SAravind Gopalakrishnan 		 * Model 0x60h needs special handling:
1084a597d2a5SAravind Gopalakrishnan 		 *
1085a597d2a5SAravind Gopalakrishnan 		 * We use a Chip Select value of '0' to obtain dcsm.
1086a597d2a5SAravind Gopalakrishnan 		 * Theoretically, it is possible to populate LRDIMMs of different
1087a597d2a5SAravind Gopalakrishnan 		 * 'Rank' value on a DCT. But this is not the common case. So,
1088a597d2a5SAravind Gopalakrishnan 		 * it's reasonable to assume all DIMMs are going to be of same
1089a597d2a5SAravind Gopalakrishnan 		 * 'type' until proven otherwise.
1090a597d2a5SAravind Gopalakrishnan 		 */
1091a597d2a5SAravind Gopalakrishnan 		amd64_read_dct_pci_cfg(pvt, 0, DRAM_CONTROL, &dram_ctrl);
1092a597d2a5SAravind Gopalakrishnan 		dcsm = pvt->csels[0].csmasks[0];
1093a597d2a5SAravind Gopalakrishnan 
1094a597d2a5SAravind Gopalakrishnan 		if (((dram_ctrl >> 8) & 0x7) == 0x2)
1095a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_DDR4;
1096a597d2a5SAravind Gopalakrishnan 		else if (pvt->dclr0 & BIT(16))
1097a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_DDR3;
1098a597d2a5SAravind Gopalakrishnan 		else if (dcsm & 0x3)
1099a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_LRDDR3;
11006b4c0bdeSBorislav Petkov 		else
1101a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_RDDR3;
1102a597d2a5SAravind Gopalakrishnan 
1103a597d2a5SAravind Gopalakrishnan 		return;
1104a597d2a5SAravind Gopalakrishnan 
1105a597d2a5SAravind Gopalakrishnan 	case 0x16:
1106a597d2a5SAravind Gopalakrishnan 		goto ddr3;
1107a597d2a5SAravind Gopalakrishnan 
1108a597d2a5SAravind Gopalakrishnan 	default:
1109a597d2a5SAravind Gopalakrishnan 		WARN(1, KERN_ERR "%s: Family??? 0x%x\n", __func__, pvt->fam);
1110a597d2a5SAravind Gopalakrishnan 		pvt->dram_type = MEM_EMPTY;
111194be4bffSDoug Thompson 	}
1112a597d2a5SAravind Gopalakrishnan 	return;
111394be4bffSDoug Thompson 
1114a597d2a5SAravind Gopalakrishnan ddr3:
1115a597d2a5SAravind Gopalakrishnan 	pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
111694be4bffSDoug Thompson }
111794be4bffSDoug Thompson 
1118cb328507SBorislav Petkov /* Get the number of DCT channels the memory controller is using. */
1119ddff876dSDoug Thompson static int k8_early_channel_count(struct amd64_pvt *pvt)
1120ddff876dSDoug Thompson {
1121cb328507SBorislav Petkov 	int flag;
1122ddff876dSDoug Thompson 
11239f56da0eSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F)
1124ddff876dSDoug Thompson 		/* RevF (NPT) and later */
112541d8bfabSBorislav Petkov 		flag = pvt->dclr0 & WIDTH_128;
11269f56da0eSBorislav Petkov 	else
1127ddff876dSDoug Thompson 		/* RevE and earlier */
1128ddff876dSDoug Thompson 		flag = pvt->dclr0 & REVE_WIDTH_128;
1129ddff876dSDoug Thompson 
1130ddff876dSDoug Thompson 	/* not used */
1131ddff876dSDoug Thompson 	pvt->dclr1 = 0;
1132ddff876dSDoug Thompson 
1133ddff876dSDoug Thompson 	return (flag) ? 2 : 1;
1134ddff876dSDoug Thompson }
1135ddff876dSDoug Thompson 
113670046624SBorislav Petkov /* On F10h and later ErrAddr is MC4_ADDR[47:1] */
1137a4b4bedcSBorislav Petkov static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m)
1138ddff876dSDoug Thompson {
1139db970bd2SYazen Ghannam 	u16 mce_nid = topology_die_id(m->extcpu);
11402ec591acSBorislav Petkov 	struct mem_ctl_info *mci;
114170046624SBorislav Petkov 	u8 start_bit = 1;
114270046624SBorislav Petkov 	u8 end_bit   = 47;
11432ec591acSBorislav Petkov 	u64 addr;
11442ec591acSBorislav Petkov 
11452ec591acSBorislav Petkov 	mci = edac_mc_find(mce_nid);
11462ec591acSBorislav Petkov 	if (!mci)
11472ec591acSBorislav Petkov 		return 0;
11482ec591acSBorislav Petkov 
11492ec591acSBorislav Petkov 	pvt = mci->pvt_info;
115070046624SBorislav Petkov 
1151a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf) {
115270046624SBorislav Petkov 		start_bit = 3;
115370046624SBorislav Petkov 		end_bit   = 39;
115470046624SBorislav Petkov 	}
115570046624SBorislav Petkov 
115610ef6b0dSChen, Gong 	addr = m->addr & GENMASK_ULL(end_bit, start_bit);
1157c1ae6830SBorislav Petkov 
1158c1ae6830SBorislav Petkov 	/*
1159c1ae6830SBorislav Petkov 	 * Erratum 637 workaround
1160c1ae6830SBorislav Petkov 	 */
1161a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x15) {
1162c1ae6830SBorislav Petkov 		u64 cc6_base, tmp_addr;
1163c1ae6830SBorislav Petkov 		u32 tmp;
11648b84c8dfSDaniel J Blueman 		u8 intlv_en;
1165c1ae6830SBorislav Petkov 
116610ef6b0dSChen, Gong 		if ((addr & GENMASK_ULL(47, 24)) >> 24 != 0x00fdf7)
1167c1ae6830SBorislav Petkov 			return addr;
1168c1ae6830SBorislav Petkov 
1169c1ae6830SBorislav Petkov 
1170c1ae6830SBorislav Petkov 		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp);
1171c1ae6830SBorislav Petkov 		intlv_en = tmp >> 21 & 0x7;
1172c1ae6830SBorislav Petkov 
1173c1ae6830SBorislav Petkov 		/* add [47:27] + 3 trailing bits */
117410ef6b0dSChen, Gong 		cc6_base  = (tmp & GENMASK_ULL(20, 0)) << 3;
1175c1ae6830SBorislav Petkov 
1176c1ae6830SBorislav Petkov 		/* reverse and add DramIntlvEn */
1177c1ae6830SBorislav Petkov 		cc6_base |= intlv_en ^ 0x7;
1178c1ae6830SBorislav Petkov 
1179c1ae6830SBorislav Petkov 		/* pin at [47:24] */
1180c1ae6830SBorislav Petkov 		cc6_base <<= 24;
1181c1ae6830SBorislav Petkov 
1182c1ae6830SBorislav Petkov 		if (!intlv_en)
118310ef6b0dSChen, Gong 			return cc6_base | (addr & GENMASK_ULL(23, 0));
1184c1ae6830SBorislav Petkov 
1185c1ae6830SBorislav Petkov 		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp);
1186c1ae6830SBorislav Petkov 
1187c1ae6830SBorislav Petkov 							/* faster log2 */
118810ef6b0dSChen, Gong 		tmp_addr  = (addr & GENMASK_ULL(23, 12)) << __fls(intlv_en + 1);
1189c1ae6830SBorislav Petkov 
1190c1ae6830SBorislav Petkov 		/* OR DramIntlvSel into bits [14:12] */
119110ef6b0dSChen, Gong 		tmp_addr |= (tmp & GENMASK_ULL(23, 21)) >> 9;
1192c1ae6830SBorislav Petkov 
1193c1ae6830SBorislav Petkov 		/* add remaining [11:0] bits from original MC4_ADDR */
119410ef6b0dSChen, Gong 		tmp_addr |= addr & GENMASK_ULL(11, 0);
1195c1ae6830SBorislav Petkov 
1196c1ae6830SBorislav Petkov 		return cc6_base | tmp_addr;
1197c1ae6830SBorislav Petkov 	}
1198c1ae6830SBorislav Petkov 
1199c1ae6830SBorislav Petkov 	return addr;
1200ddff876dSDoug Thompson }
1201ddff876dSDoug Thompson 
1202e2c0bffeSDaniel J Blueman static struct pci_dev *pci_get_related_function(unsigned int vendor,
1203e2c0bffeSDaniel J Blueman 						unsigned int device,
1204e2c0bffeSDaniel J Blueman 						struct pci_dev *related)
1205e2c0bffeSDaniel J Blueman {
1206e2c0bffeSDaniel J Blueman 	struct pci_dev *dev = NULL;
1207e2c0bffeSDaniel J Blueman 
1208e2c0bffeSDaniel J Blueman 	while ((dev = pci_get_device(vendor, device, dev))) {
1209e2c0bffeSDaniel J Blueman 		if (pci_domain_nr(dev->bus) == pci_domain_nr(related->bus) &&
1210e2c0bffeSDaniel J Blueman 		    (dev->bus->number == related->bus->number) &&
1211e2c0bffeSDaniel J Blueman 		    (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn)))
1212e2c0bffeSDaniel J Blueman 			break;
1213e2c0bffeSDaniel J Blueman 	}
1214e2c0bffeSDaniel J Blueman 
1215e2c0bffeSDaniel J Blueman 	return dev;
1216e2c0bffeSDaniel J Blueman }
1217e2c0bffeSDaniel J Blueman 
12187f19bf75SBorislav Petkov static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
1219ddff876dSDoug Thompson {
1220e2c0bffeSDaniel J Blueman 	struct amd_northbridge *nb;
122118b94f66SAravind Gopalakrishnan 	struct pci_dev *f1 = NULL;
122218b94f66SAravind Gopalakrishnan 	unsigned int pci_func;
122371d2a32eSBorislav Petkov 	int off = range << 3;
1224e2c0bffeSDaniel J Blueman 	u32 llim;
1225ddff876dSDoug Thompson 
12267f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off,  &pvt->ranges[range].base.lo);
12277f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo);
1228ddff876dSDoug Thompson 
122918b94f66SAravind Gopalakrishnan 	if (pvt->fam == 0xf)
12307f19bf75SBorislav Petkov 		return;
1231ddff876dSDoug Thompson 
12327f19bf75SBorislav Petkov 	if (!dram_rw(pvt, range))
12337f19bf75SBorislav Petkov 		return;
1234ddff876dSDoug Thompson 
12357f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off,  &pvt->ranges[range].base.hi);
12367f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi);
1237f08e457cSBorislav Petkov 
1238e2c0bffeSDaniel J Blueman 	/* F15h: factor in CC6 save area by reading dst node's limit reg */
123918b94f66SAravind Gopalakrishnan 	if (pvt->fam != 0x15)
1240e2c0bffeSDaniel J Blueman 		return;
1241f08e457cSBorislav Petkov 
1242e2c0bffeSDaniel J Blueman 	nb = node_to_amd_nb(dram_dst_node(pvt, range));
1243e2c0bffeSDaniel J Blueman 	if (WARN_ON(!nb))
1244e2c0bffeSDaniel J Blueman 		return;
1245e2c0bffeSDaniel J Blueman 
1246a597d2a5SAravind Gopalakrishnan 	if (pvt->model == 0x60)
1247a597d2a5SAravind Gopalakrishnan 		pci_func = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1;
1248a597d2a5SAravind Gopalakrishnan 	else if (pvt->model == 0x30)
1249a597d2a5SAravind Gopalakrishnan 		pci_func = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1;
1250a597d2a5SAravind Gopalakrishnan 	else
1251a597d2a5SAravind Gopalakrishnan 		pci_func = PCI_DEVICE_ID_AMD_15H_NB_F1;
125218b94f66SAravind Gopalakrishnan 
125318b94f66SAravind Gopalakrishnan 	f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc);
1254f08e457cSBorislav Petkov 	if (WARN_ON(!f1))
1255f08e457cSBorislav Petkov 		return;
1256f08e457cSBorislav Petkov 
1257f08e457cSBorislav Petkov 	amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim);
1258f08e457cSBorislav Petkov 
125910ef6b0dSChen, Gong 	pvt->ranges[range].lim.lo &= GENMASK_ULL(15, 0);
1260f08e457cSBorislav Petkov 
1261f08e457cSBorislav Petkov 				    /* {[39:27],111b} */
1262f08e457cSBorislav Petkov 	pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16;
1263f08e457cSBorislav Petkov 
126410ef6b0dSChen, Gong 	pvt->ranges[range].lim.hi &= GENMASK_ULL(7, 0);
1265f08e457cSBorislav Petkov 
1266f08e457cSBorislav Petkov 				    /* [47:40] */
1267f08e457cSBorislav Petkov 	pvt->ranges[range].lim.hi |= llim >> 13;
1268f08e457cSBorislav Petkov 
1269f08e457cSBorislav Petkov 	pci_dev_put(f1);
1270f08e457cSBorislav Petkov }
1271ddff876dSDoug Thompson 
1272f192c7b1SBorislav Petkov static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
127333ca0643SBorislav Petkov 				    struct err_info *err)
1274ddff876dSDoug Thompson {
1275f192c7b1SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
1276ddff876dSDoug Thompson 
127733ca0643SBorislav Petkov 	error_address_to_page_and_offset(sys_addr, err);
1278ab5a503cSMauro Carvalho Chehab 
1279ab5a503cSMauro Carvalho Chehab 	/*
1280ab5a503cSMauro Carvalho Chehab 	 * Find out which node the error address belongs to. This may be
1281ab5a503cSMauro Carvalho Chehab 	 * different from the node that detected the error.
1282ab5a503cSMauro Carvalho Chehab 	 */
128333ca0643SBorislav Petkov 	err->src_mci = find_mc_by_sys_addr(mci, sys_addr);
128433ca0643SBorislav Petkov 	if (!err->src_mci) {
1285ab5a503cSMauro Carvalho Chehab 		amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
1286ab5a503cSMauro Carvalho Chehab 			     (unsigned long)sys_addr);
128733ca0643SBorislav Petkov 		err->err_code = ERR_NODE;
1288ab5a503cSMauro Carvalho Chehab 		return;
1289ab5a503cSMauro Carvalho Chehab 	}
1290ab5a503cSMauro Carvalho Chehab 
1291ab5a503cSMauro Carvalho Chehab 	/* Now map the sys_addr to a CSROW */
129233ca0643SBorislav Petkov 	err->csrow = sys_addr_to_csrow(err->src_mci, sys_addr);
129333ca0643SBorislav Petkov 	if (err->csrow < 0) {
129433ca0643SBorislav Petkov 		err->err_code = ERR_CSROW;
1295ab5a503cSMauro Carvalho Chehab 		return;
1296ab5a503cSMauro Carvalho Chehab 	}
1297ab5a503cSMauro Carvalho Chehab 
1298ddff876dSDoug Thompson 	/* CHIPKILL enabled */
1299f192c7b1SBorislav Petkov 	if (pvt->nbcfg & NBCFG_CHIPKILL) {
130033ca0643SBorislav Petkov 		err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
130133ca0643SBorislav Petkov 		if (err->channel < 0) {
1302ddff876dSDoug Thompson 			/*
1303ddff876dSDoug Thompson 			 * Syndrome didn't map, so we don't know which of the
1304ddff876dSDoug Thompson 			 * 2 DIMMs is in error. So we need to ID 'both' of them
1305ddff876dSDoug Thompson 			 * as suspect.
1306ddff876dSDoug Thompson 			 */
130733ca0643SBorislav Petkov 			amd64_mc_warn(err->src_mci, "unknown syndrome 0x%04x - "
1308ab5a503cSMauro Carvalho Chehab 				      "possible error reporting race\n",
130933ca0643SBorislav Petkov 				      err->syndrome);
131033ca0643SBorislav Petkov 			err->err_code = ERR_CHANNEL;
1311ddff876dSDoug Thompson 			return;
1312ddff876dSDoug Thompson 		}
1313ddff876dSDoug Thompson 	} else {
1314ddff876dSDoug Thompson 		/*
1315ddff876dSDoug Thompson 		 * non-chipkill ecc mode
1316ddff876dSDoug Thompson 		 *
1317ddff876dSDoug Thompson 		 * The k8 documentation is unclear about how to determine the
1318ddff876dSDoug Thompson 		 * channel number when using non-chipkill memory.  This method
1319ddff876dSDoug Thompson 		 * was obtained from email communication with someone at AMD.
1320ddff876dSDoug Thompson 		 * (Wish the email was placed in this comment - norsk)
1321ddff876dSDoug Thompson 		 */
132233ca0643SBorislav Petkov 		err->channel = ((sys_addr & BIT(3)) != 0);
1323ddff876dSDoug Thompson 	}
1324ddff876dSDoug Thompson }
1325ddff876dSDoug Thompson 
132641d8bfabSBorislav Petkov static int ddr2_cs_size(unsigned i, bool dct_width)
1327ddff876dSDoug Thompson {
132841d8bfabSBorislav Petkov 	unsigned shift = 0;
1329ddff876dSDoug Thompson 
133041d8bfabSBorislav Petkov 	if (i <= 2)
133141d8bfabSBorislav Petkov 		shift = i;
133241d8bfabSBorislav Petkov 	else if (!(i & 0x1))
133341d8bfabSBorislav Petkov 		shift = i >> 1;
13341433eb99SBorislav Petkov 	else
133541d8bfabSBorislav Petkov 		shift = (i + 1) >> 1;
1336ddff876dSDoug Thompson 
133741d8bfabSBorislav Petkov 	return 128 << (shift + !!dct_width);
133841d8bfabSBorislav Petkov }
133941d8bfabSBorislav Petkov 
134041d8bfabSBorislav Petkov static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1341a597d2a5SAravind Gopalakrishnan 				  unsigned cs_mode, int cs_mask_nr)
134241d8bfabSBorislav Petkov {
134341d8bfabSBorislav Petkov 	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
134441d8bfabSBorislav Petkov 
134541d8bfabSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F) {
134641d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 11);
134741d8bfabSBorislav Petkov 		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
134841d8bfabSBorislav Petkov 	}
134941d8bfabSBorislav Petkov 	else if (pvt->ext_model >= K8_REV_D) {
135011b0a314SBorislav Petkov 		unsigned diff;
135141d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 10);
135241d8bfabSBorislav Petkov 
135311b0a314SBorislav Petkov 		/*
135411b0a314SBorislav Petkov 		 * the below calculation, besides trying to win an obfuscated C
135511b0a314SBorislav Petkov 		 * contest, maps cs_mode values to DIMM chip select sizes. The
135611b0a314SBorislav Petkov 		 * mappings are:
135711b0a314SBorislav Petkov 		 *
135811b0a314SBorislav Petkov 		 * cs_mode	CS size (mb)
135911b0a314SBorislav Petkov 		 * =======	============
136011b0a314SBorislav Petkov 		 * 0		32
136111b0a314SBorislav Petkov 		 * 1		64
136211b0a314SBorislav Petkov 		 * 2		128
136311b0a314SBorislav Petkov 		 * 3		128
136411b0a314SBorislav Petkov 		 * 4		256
136511b0a314SBorislav Petkov 		 * 5		512
136611b0a314SBorislav Petkov 		 * 6		256
136711b0a314SBorislav Petkov 		 * 7		512
136811b0a314SBorislav Petkov 		 * 8		1024
136911b0a314SBorislav Petkov 		 * 9		1024
137011b0a314SBorislav Petkov 		 * 10		2048
137111b0a314SBorislav Petkov 		 *
137211b0a314SBorislav Petkov 		 * Basically, it calculates a value with which to shift the
137311b0a314SBorislav Petkov 		 * smallest CS size of 32MB.
137411b0a314SBorislav Petkov 		 *
137511b0a314SBorislav Petkov 		 * ddr[23]_cs_size have a similar purpose.
137611b0a314SBorislav Petkov 		 */
137711b0a314SBorislav Petkov 		diff = cs_mode/3 + (unsigned)(cs_mode > 5);
137811b0a314SBorislav Petkov 
137911b0a314SBorislav Petkov 		return 32 << (cs_mode - diff);
138041d8bfabSBorislav Petkov 	}
138141d8bfabSBorislav Petkov 	else {
138241d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 6);
138341d8bfabSBorislav Petkov 		return 32 << cs_mode;
138441d8bfabSBorislav Petkov 	}
1385ddff876dSDoug Thompson }
1386ddff876dSDoug Thompson 
13871afd3c98SDoug Thompson /*
13881afd3c98SDoug Thompson  * Get the number of DCT channels in use.
13891afd3c98SDoug Thompson  *
13901afd3c98SDoug Thompson  * Return:
13911afd3c98SDoug Thompson  *	number of Memory Channels in operation
13921afd3c98SDoug Thompson  * Pass back:
13931afd3c98SDoug Thompson  *	contents of the DCL0_LOW register
13941afd3c98SDoug Thompson  */
13957d20d14dSBorislav Petkov static int f1x_early_channel_count(struct amd64_pvt *pvt)
13961afd3c98SDoug Thompson {
13976ba5dcdcSBorislav Petkov 	int i, j, channels = 0;
1398ddff876dSDoug Thompson 
13997d20d14dSBorislav Petkov 	/* On F10h, if we are in 128 bit mode, then we are using 2 channels */
1400a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10 && (pvt->dclr0 & WIDTH_128))
14017d20d14dSBorislav Petkov 		return 2;
14021afd3c98SDoug Thompson 
14031afd3c98SDoug Thompson 	/*
1404d16149e8SBorislav Petkov 	 * Need to check if in unganged mode: In such, there are 2 channels,
1405d16149e8SBorislav Petkov 	 * but they are not in 128 bit mode and thus the above 'dclr0' status
1406d16149e8SBorislav Petkov 	 * bit will be OFF.
14071afd3c98SDoug Thompson 	 *
14081afd3c98SDoug Thompson 	 * Need to check DCT0[0] and DCT1[0] to see if only one of them has
14091afd3c98SDoug Thompson 	 * their CSEnable bit on. If so, then SINGLE DIMM case.
14101afd3c98SDoug Thompson 	 */
1411956b9ba1SJoe Perches 	edac_dbg(0, "Data width is not 128 bits - need more decoding\n");
14121afd3c98SDoug Thompson 
14131afd3c98SDoug Thompson 	/*
14141afd3c98SDoug Thompson 	 * Check DRAM Bank Address Mapping values for each DIMM to see if there
14151afd3c98SDoug Thompson 	 * is more than just one DIMM present in unganged mode. Need to check
14161afd3c98SDoug Thompson 	 * both controllers since DIMMs can be placed in either one.
14171afd3c98SDoug Thompson 	 */
1418525a1b20SBorislav Petkov 	for (i = 0; i < 2; i++) {
1419525a1b20SBorislav Petkov 		u32 dbam = (i ? pvt->dbam1 : pvt->dbam0);
14201afd3c98SDoug Thompson 
142157a30854SWan Wei 		for (j = 0; j < 4; j++) {
142257a30854SWan Wei 			if (DBAM_DIMM(j, dbam) > 0) {
14231afd3c98SDoug Thompson 				channels++;
142457a30854SWan Wei 				break;
14251afd3c98SDoug Thompson 			}
142657a30854SWan Wei 		}
142757a30854SWan Wei 	}
14281afd3c98SDoug Thompson 
1429d16149e8SBorislav Petkov 	if (channels > 2)
1430d16149e8SBorislav Petkov 		channels = 2;
1431d16149e8SBorislav Petkov 
143224f9a7feSBorislav Petkov 	amd64_info("MCT channel count: %d\n", channels);
14331afd3c98SDoug Thompson 
14341afd3c98SDoug Thompson 	return channels;
14351afd3c98SDoug Thompson }
14361afd3c98SDoug Thompson 
1437f1cbbec9SYazen Ghannam static int f17_early_channel_count(struct amd64_pvt *pvt)
1438f1cbbec9SYazen Ghannam {
1439f1cbbec9SYazen Ghannam 	int i, channels = 0;
1440f1cbbec9SYazen Ghannam 
1441f1cbbec9SYazen Ghannam 	/* SDP Control bit 31 (SdpInit) is clear for unused UMC channels */
14424d30d2bcSYazen Ghannam 	for_each_umc(i)
1443f1cbbec9SYazen Ghannam 		channels += !!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT);
1444f1cbbec9SYazen Ghannam 
1445f1cbbec9SYazen Ghannam 	amd64_info("MCT channel count: %d\n", channels);
1446f1cbbec9SYazen Ghannam 
1447f1cbbec9SYazen Ghannam 	return channels;
1448f1cbbec9SYazen Ghannam }
1449f1cbbec9SYazen Ghannam 
145041d8bfabSBorislav Petkov static int ddr3_cs_size(unsigned i, bool dct_width)
14511afd3c98SDoug Thompson {
145241d8bfabSBorislav Petkov 	unsigned shift = 0;
145341d8bfabSBorislav Petkov 	int cs_size = 0;
145441d8bfabSBorislav Petkov 
145541d8bfabSBorislav Petkov 	if (i == 0 || i == 3 || i == 4)
145641d8bfabSBorislav Petkov 		cs_size = -1;
145741d8bfabSBorislav Petkov 	else if (i <= 2)
145841d8bfabSBorislav Petkov 		shift = i;
145941d8bfabSBorislav Petkov 	else if (i == 12)
146041d8bfabSBorislav Petkov 		shift = 7;
146141d8bfabSBorislav Petkov 	else if (!(i & 0x1))
146241d8bfabSBorislav Petkov 		shift = i >> 1;
146341d8bfabSBorislav Petkov 	else
146441d8bfabSBorislav Petkov 		shift = (i + 1) >> 1;
146541d8bfabSBorislav Petkov 
146641d8bfabSBorislav Petkov 	if (cs_size != -1)
146741d8bfabSBorislav Petkov 		cs_size = (128 * (1 << !!dct_width)) << shift;
146841d8bfabSBorislav Petkov 
146941d8bfabSBorislav Petkov 	return cs_size;
147041d8bfabSBorislav Petkov }
147141d8bfabSBorislav Petkov 
1472a597d2a5SAravind Gopalakrishnan static int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply)
1473a597d2a5SAravind Gopalakrishnan {
1474a597d2a5SAravind Gopalakrishnan 	unsigned shift = 0;
1475a597d2a5SAravind Gopalakrishnan 	int cs_size = 0;
1476a597d2a5SAravind Gopalakrishnan 
1477a597d2a5SAravind Gopalakrishnan 	if (i < 4 || i == 6)
1478a597d2a5SAravind Gopalakrishnan 		cs_size = -1;
1479a597d2a5SAravind Gopalakrishnan 	else if (i == 12)
1480a597d2a5SAravind Gopalakrishnan 		shift = 7;
1481a597d2a5SAravind Gopalakrishnan 	else if (!(i & 0x1))
1482a597d2a5SAravind Gopalakrishnan 		shift = i >> 1;
1483a597d2a5SAravind Gopalakrishnan 	else
1484a597d2a5SAravind Gopalakrishnan 		shift = (i + 1) >> 1;
1485a597d2a5SAravind Gopalakrishnan 
1486a597d2a5SAravind Gopalakrishnan 	if (cs_size != -1)
1487a597d2a5SAravind Gopalakrishnan 		cs_size = rank_multiply * (128 << shift);
1488a597d2a5SAravind Gopalakrishnan 
1489a597d2a5SAravind Gopalakrishnan 	return cs_size;
1490a597d2a5SAravind Gopalakrishnan }
1491a597d2a5SAravind Gopalakrishnan 
1492a597d2a5SAravind Gopalakrishnan static int ddr4_cs_size(unsigned i)
1493a597d2a5SAravind Gopalakrishnan {
1494a597d2a5SAravind Gopalakrishnan 	int cs_size = 0;
1495a597d2a5SAravind Gopalakrishnan 
1496a597d2a5SAravind Gopalakrishnan 	if (i == 0)
1497a597d2a5SAravind Gopalakrishnan 		cs_size = -1;
1498a597d2a5SAravind Gopalakrishnan 	else if (i == 1)
1499a597d2a5SAravind Gopalakrishnan 		cs_size = 1024;
1500a597d2a5SAravind Gopalakrishnan 	else
1501a597d2a5SAravind Gopalakrishnan 		/* Min cs_size = 1G */
1502a597d2a5SAravind Gopalakrishnan 		cs_size = 1024 * (1 << (i >> 1));
1503a597d2a5SAravind Gopalakrishnan 
1504a597d2a5SAravind Gopalakrishnan 	return cs_size;
1505a597d2a5SAravind Gopalakrishnan }
1506a597d2a5SAravind Gopalakrishnan 
150741d8bfabSBorislav Petkov static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1508a597d2a5SAravind Gopalakrishnan 				   unsigned cs_mode, int cs_mask_nr)
150941d8bfabSBorislav Petkov {
151041d8bfabSBorislav Petkov 	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
151141d8bfabSBorislav Petkov 
151241d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 11);
15131433eb99SBorislav Petkov 
15141433eb99SBorislav Petkov 	if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
151541d8bfabSBorislav Petkov 		return ddr3_cs_size(cs_mode, dclr & WIDTH_128);
15161433eb99SBorislav Petkov 	else
151741d8bfabSBorislav Petkov 		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
151841d8bfabSBorislav Petkov }
15191433eb99SBorislav Petkov 
152041d8bfabSBorislav Petkov /*
152141d8bfabSBorislav Petkov  * F15h supports only 64bit DCT interfaces
152241d8bfabSBorislav Petkov  */
152341d8bfabSBorislav Petkov static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1524a597d2a5SAravind Gopalakrishnan 				   unsigned cs_mode, int cs_mask_nr)
152541d8bfabSBorislav Petkov {
152641d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 12);
152741d8bfabSBorislav Petkov 
152841d8bfabSBorislav Petkov 	return ddr3_cs_size(cs_mode, false);
15291afd3c98SDoug Thompson }
15301afd3c98SDoug Thompson 
1531a597d2a5SAravind Gopalakrishnan /* F15h M60h supports DDR4 mapping as well.. */
1532a597d2a5SAravind Gopalakrishnan static int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1533a597d2a5SAravind Gopalakrishnan 					unsigned cs_mode, int cs_mask_nr)
1534a597d2a5SAravind Gopalakrishnan {
1535a597d2a5SAravind Gopalakrishnan 	int cs_size;
1536a597d2a5SAravind Gopalakrishnan 	u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr];
1537a597d2a5SAravind Gopalakrishnan 
1538a597d2a5SAravind Gopalakrishnan 	WARN_ON(cs_mode > 12);
1539a597d2a5SAravind Gopalakrishnan 
1540a597d2a5SAravind Gopalakrishnan 	if (pvt->dram_type == MEM_DDR4) {
1541a597d2a5SAravind Gopalakrishnan 		if (cs_mode > 9)
1542a597d2a5SAravind Gopalakrishnan 			return -1;
1543a597d2a5SAravind Gopalakrishnan 
1544a597d2a5SAravind Gopalakrishnan 		cs_size = ddr4_cs_size(cs_mode);
1545a597d2a5SAravind Gopalakrishnan 	} else if (pvt->dram_type == MEM_LRDDR3) {
1546a597d2a5SAravind Gopalakrishnan 		unsigned rank_multiply = dcsm & 0xf;
1547a597d2a5SAravind Gopalakrishnan 
1548a597d2a5SAravind Gopalakrishnan 		if (rank_multiply == 3)
1549a597d2a5SAravind Gopalakrishnan 			rank_multiply = 4;
1550a597d2a5SAravind Gopalakrishnan 		cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply);
1551a597d2a5SAravind Gopalakrishnan 	} else {
1552a597d2a5SAravind Gopalakrishnan 		/* Minimum cs size is 512mb for F15hM60h*/
1553a597d2a5SAravind Gopalakrishnan 		if (cs_mode == 0x1)
1554a597d2a5SAravind Gopalakrishnan 			return -1;
1555a597d2a5SAravind Gopalakrishnan 
1556a597d2a5SAravind Gopalakrishnan 		cs_size = ddr3_cs_size(cs_mode, false);
1557a597d2a5SAravind Gopalakrishnan 	}
1558a597d2a5SAravind Gopalakrishnan 
1559a597d2a5SAravind Gopalakrishnan 	return cs_size;
1560a597d2a5SAravind Gopalakrishnan }
1561a597d2a5SAravind Gopalakrishnan 
156294c1acf2SAravind Gopalakrishnan /*
156318b94f66SAravind Gopalakrishnan  * F16h and F15h model 30h have only limited cs_modes.
156494c1acf2SAravind Gopalakrishnan  */
156594c1acf2SAravind Gopalakrishnan static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1566a597d2a5SAravind Gopalakrishnan 				unsigned cs_mode, int cs_mask_nr)
156794c1acf2SAravind Gopalakrishnan {
156894c1acf2SAravind Gopalakrishnan 	WARN_ON(cs_mode > 12);
156994c1acf2SAravind Gopalakrishnan 
157094c1acf2SAravind Gopalakrishnan 	if (cs_mode == 6 || cs_mode == 8 ||
157194c1acf2SAravind Gopalakrishnan 	    cs_mode == 9 || cs_mode == 12)
157294c1acf2SAravind Gopalakrishnan 		return -1;
157394c1acf2SAravind Gopalakrishnan 	else
157494c1acf2SAravind Gopalakrishnan 		return ddr3_cs_size(cs_mode, false);
157594c1acf2SAravind Gopalakrishnan }
157694c1acf2SAravind Gopalakrishnan 
1577e53a3b26SYazen Ghannam static int f17_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc,
1578f1cbbec9SYazen Ghannam 				    unsigned int cs_mode, int csrow_nr)
1579f1cbbec9SYazen Ghannam {
1580e53a3b26SYazen Ghannam 	u32 addr_mask_orig, addr_mask_deinterleaved;
1581e53a3b26SYazen Ghannam 	u32 msb, weight, num_zero_bits;
1582e53a3b26SYazen Ghannam 	int dimm, size = 0;
1583f1cbbec9SYazen Ghannam 
1584e53a3b26SYazen Ghannam 	/* No Chip Selects are enabled. */
1585e53a3b26SYazen Ghannam 	if (!cs_mode)
1586e53a3b26SYazen Ghannam 		return size;
1587e53a3b26SYazen Ghannam 
1588e53a3b26SYazen Ghannam 	/* Requested size of an even CS but none are enabled. */
1589e53a3b26SYazen Ghannam 	if (!(cs_mode & CS_EVEN) && !(csrow_nr & 1))
1590e53a3b26SYazen Ghannam 		return size;
1591e53a3b26SYazen Ghannam 
1592e53a3b26SYazen Ghannam 	/* Requested size of an odd CS but none are enabled. */
1593e53a3b26SYazen Ghannam 	if (!(cs_mode & CS_ODD) && (csrow_nr & 1))
1594e53a3b26SYazen Ghannam 		return size;
1595e53a3b26SYazen Ghannam 
1596e53a3b26SYazen Ghannam 	/*
1597e53a3b26SYazen Ghannam 	 * There is one mask per DIMM, and two Chip Selects per DIMM.
1598e53a3b26SYazen Ghannam 	 *	CS0 and CS1 -> DIMM0
1599e53a3b26SYazen Ghannam 	 *	CS2 and CS3 -> DIMM1
1600e53a3b26SYazen Ghannam 	 */
1601e53a3b26SYazen Ghannam 	dimm = csrow_nr >> 1;
1602e53a3b26SYazen Ghannam 
160381f5090dSYazen Ghannam 	/* Asymmetric dual-rank DIMM support. */
160481f5090dSYazen Ghannam 	if ((csrow_nr & 1) && (cs_mode & CS_ODD_SECONDARY))
160581f5090dSYazen Ghannam 		addr_mask_orig = pvt->csels[umc].csmasks_sec[dimm];
160681f5090dSYazen Ghannam 	else
1607e53a3b26SYazen Ghannam 		addr_mask_orig = pvt->csels[umc].csmasks[dimm];
1608e53a3b26SYazen Ghannam 
1609e53a3b26SYazen Ghannam 	/*
1610e53a3b26SYazen Ghannam 	 * The number of zero bits in the mask is equal to the number of bits
1611e53a3b26SYazen Ghannam 	 * in a full mask minus the number of bits in the current mask.
1612e53a3b26SYazen Ghannam 	 *
1613e53a3b26SYazen Ghannam 	 * The MSB is the number of bits in the full mask because BIT[0] is
1614e53a3b26SYazen Ghannam 	 * always 0.
1615e53a3b26SYazen Ghannam 	 */
1616e53a3b26SYazen Ghannam 	msb = fls(addr_mask_orig) - 1;
1617e53a3b26SYazen Ghannam 	weight = hweight_long(addr_mask_orig);
1618e53a3b26SYazen Ghannam 	num_zero_bits = msb - weight;
1619e53a3b26SYazen Ghannam 
1620e53a3b26SYazen Ghannam 	/* Take the number of zero bits off from the top of the mask. */
1621e53a3b26SYazen Ghannam 	addr_mask_deinterleaved = GENMASK_ULL(msb - num_zero_bits, 1);
1622e53a3b26SYazen Ghannam 
1623e53a3b26SYazen Ghannam 	edac_dbg(1, "CS%d DIMM%d AddrMasks:\n", csrow_nr, dimm);
1624e53a3b26SYazen Ghannam 	edac_dbg(1, "  Original AddrMask: 0x%x\n", addr_mask_orig);
1625e53a3b26SYazen Ghannam 	edac_dbg(1, "  Deinterleaved AddrMask: 0x%x\n", addr_mask_deinterleaved);
1626f1cbbec9SYazen Ghannam 
1627f1cbbec9SYazen Ghannam 	/* Register [31:1] = Address [39:9]. Size is in kBs here. */
1628e53a3b26SYazen Ghannam 	size = (addr_mask_deinterleaved >> 2) + 1;
1629f1cbbec9SYazen Ghannam 
1630f1cbbec9SYazen Ghannam 	/* Return size in MBs. */
1631f1cbbec9SYazen Ghannam 	return size >> 10;
1632f1cbbec9SYazen Ghannam }
1633f1cbbec9SYazen Ghannam 
16345a5d2371SBorislav Petkov static void read_dram_ctl_register(struct amd64_pvt *pvt)
16356163b5d4SDoug Thompson {
16366163b5d4SDoug Thompson 
1637a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
16385a5d2371SBorislav Petkov 		return;
16395a5d2371SBorislav Petkov 
16407981a28fSAravind Gopalakrishnan 	if (!amd64_read_pci_cfg(pvt->F2, DCT_SEL_LO, &pvt->dct_sel_lo)) {
1641956b9ba1SJoe Perches 		edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
164278da121eSBorislav Petkov 			 pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
16436163b5d4SDoug Thompson 
1644956b9ba1SJoe Perches 		edac_dbg(0, "  DCTs operate in %s mode\n",
16455a5d2371SBorislav Petkov 			 (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
16466163b5d4SDoug Thompson 
164772381bd5SBorislav Petkov 		if (!dct_ganging_enabled(pvt))
1648956b9ba1SJoe Perches 			edac_dbg(0, "  Address range split per DCT: %s\n",
164972381bd5SBorislav Petkov 				 (dct_high_range_enabled(pvt) ? "yes" : "no"));
165072381bd5SBorislav Petkov 
1651956b9ba1SJoe Perches 		edac_dbg(0, "  data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n",
165272381bd5SBorislav Petkov 			 (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
165372381bd5SBorislav Petkov 			 (dct_memory_cleared(pvt) ? "yes" : "no"));
165472381bd5SBorislav Petkov 
1655956b9ba1SJoe Perches 		edac_dbg(0, "  channel interleave: %s, "
165678da121eSBorislav Petkov 			 "interleave bits selector: 0x%x\n",
165772381bd5SBorislav Petkov 			 (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
16586163b5d4SDoug Thompson 			 dct_sel_interleave_addr(pvt));
16596163b5d4SDoug Thompson 	}
16606163b5d4SDoug Thompson 
16617981a28fSAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F2, DCT_SEL_HI, &pvt->dct_sel_hi);
16626163b5d4SDoug Thompson }
16636163b5d4SDoug Thompson 
1664f71d0a05SDoug Thompson /*
166518b94f66SAravind Gopalakrishnan  * Determine channel (DCT) based on the interleaving mode (see F15h M30h BKDG,
166618b94f66SAravind Gopalakrishnan  * 2.10.12 Memory Interleaving Modes).
166718b94f66SAravind Gopalakrishnan  */
166818b94f66SAravind Gopalakrishnan static u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
166918b94f66SAravind Gopalakrishnan 				     u8 intlv_en, int num_dcts_intlv,
167018b94f66SAravind Gopalakrishnan 				     u32 dct_sel)
167118b94f66SAravind Gopalakrishnan {
167218b94f66SAravind Gopalakrishnan 	u8 channel = 0;
167318b94f66SAravind Gopalakrishnan 	u8 select;
167418b94f66SAravind Gopalakrishnan 
167518b94f66SAravind Gopalakrishnan 	if (!(intlv_en))
167618b94f66SAravind Gopalakrishnan 		return (u8)(dct_sel);
167718b94f66SAravind Gopalakrishnan 
167818b94f66SAravind Gopalakrishnan 	if (num_dcts_intlv == 2) {
167918b94f66SAravind Gopalakrishnan 		select = (sys_addr >> 8) & 0x3;
168018b94f66SAravind Gopalakrishnan 		channel = select ? 0x3 : 0;
16819d0e8d83SAravind Gopalakrishnan 	} else if (num_dcts_intlv == 4) {
16829d0e8d83SAravind Gopalakrishnan 		u8 intlv_addr = dct_sel_interleave_addr(pvt);
16839d0e8d83SAravind Gopalakrishnan 		switch (intlv_addr) {
16849d0e8d83SAravind Gopalakrishnan 		case 0x4:
16859d0e8d83SAravind Gopalakrishnan 			channel = (sys_addr >> 8) & 0x3;
16869d0e8d83SAravind Gopalakrishnan 			break;
16879d0e8d83SAravind Gopalakrishnan 		case 0x5:
16889d0e8d83SAravind Gopalakrishnan 			channel = (sys_addr >> 9) & 0x3;
16899d0e8d83SAravind Gopalakrishnan 			break;
16909d0e8d83SAravind Gopalakrishnan 		}
16919d0e8d83SAravind Gopalakrishnan 	}
169218b94f66SAravind Gopalakrishnan 	return channel;
169318b94f66SAravind Gopalakrishnan }
169418b94f66SAravind Gopalakrishnan 
169518b94f66SAravind Gopalakrishnan /*
1696229a7a11SBorislav Petkov  * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory
1697f71d0a05SDoug Thompson  * Interleaving Modes.
1698f71d0a05SDoug Thompson  */
1699b15f0fcaSBorislav Petkov static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
1700229a7a11SBorislav Petkov 				bool hi_range_sel, u8 intlv_en)
17016163b5d4SDoug Thompson {
1702151fa71cSBorislav Petkov 	u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1;
17036163b5d4SDoug Thompson 
17046163b5d4SDoug Thompson 	if (dct_ganging_enabled(pvt))
1705229a7a11SBorislav Petkov 		return 0;
1706229a7a11SBorislav Petkov 
1707229a7a11SBorislav Petkov 	if (hi_range_sel)
1708229a7a11SBorislav Petkov 		return dct_sel_high;
1709229a7a11SBorislav Petkov 
1710f71d0a05SDoug Thompson 	/*
1711f71d0a05SDoug Thompson 	 * see F2x110[DctSelIntLvAddr] - channel interleave mode
1712f71d0a05SDoug Thompson 	 */
1713229a7a11SBorislav Petkov 	if (dct_interleave_enabled(pvt)) {
1714229a7a11SBorislav Petkov 		u8 intlv_addr = dct_sel_interleave_addr(pvt);
17156163b5d4SDoug Thompson 
1716229a7a11SBorislav Petkov 		/* return DCT select function: 0=DCT0, 1=DCT1 */
1717229a7a11SBorislav Petkov 		if (!intlv_addr)
1718229a7a11SBorislav Petkov 			return sys_addr >> 6 & 1;
17196163b5d4SDoug Thompson 
1720229a7a11SBorislav Petkov 		if (intlv_addr & 0x2) {
1721229a7a11SBorislav Petkov 			u8 shift = intlv_addr & 0x1 ? 9 : 6;
1722dc0a50a8SYazen Ghannam 			u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) & 1;
1723229a7a11SBorislav Petkov 
1724229a7a11SBorislav Petkov 			return ((sys_addr >> shift) & 1) ^ temp;
17256163b5d4SDoug Thompson 		}
17266163b5d4SDoug Thompson 
1727dc0a50a8SYazen Ghannam 		if (intlv_addr & 0x4) {
1728dc0a50a8SYazen Ghannam 			u8 shift = intlv_addr & 0x1 ? 9 : 8;
1729dc0a50a8SYazen Ghannam 
1730dc0a50a8SYazen Ghannam 			return (sys_addr >> shift) & 1;
1731dc0a50a8SYazen Ghannam 		}
1732dc0a50a8SYazen Ghannam 
1733229a7a11SBorislav Petkov 		return (sys_addr >> (12 + hweight8(intlv_en))) & 1;
1734229a7a11SBorislav Petkov 	}
1735229a7a11SBorislav Petkov 
1736229a7a11SBorislav Petkov 	if (dct_high_range_enabled(pvt))
1737229a7a11SBorislav Petkov 		return ~dct_sel_high & 1;
17386163b5d4SDoug Thompson 
17396163b5d4SDoug Thompson 	return 0;
17406163b5d4SDoug Thompson }
17416163b5d4SDoug Thompson 
1742c8e518d5SBorislav Petkov /* Convert the sys_addr to the normalized DCT address */
1743c7e5301aSDaniel J Blueman static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range,
1744c8e518d5SBorislav Petkov 				 u64 sys_addr, bool hi_rng,
1745c8e518d5SBorislav Petkov 				 u32 dct_sel_base_addr)
17466163b5d4SDoug Thompson {
17476163b5d4SDoug Thompson 	u64 chan_off;
1748c8e518d5SBorislav Petkov 	u64 dram_base		= get_dram_base(pvt, range);
1749c8e518d5SBorislav Petkov 	u64 hole_off		= f10_dhar_offset(pvt);
17506f3508f6SDan Carpenter 	u64 dct_sel_base_off	= (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16;
17516163b5d4SDoug Thompson 
1752c8e518d5SBorislav Petkov 	if (hi_rng) {
1753c8e518d5SBorislav Petkov 		/*
1754c8e518d5SBorislav Petkov 		 * if
1755c8e518d5SBorislav Petkov 		 * base address of high range is below 4Gb
1756c8e518d5SBorislav Petkov 		 * (bits [47:27] at [31:11])
1757c8e518d5SBorislav Petkov 		 * DRAM address space on this DCT is hoisted above 4Gb	&&
1758c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1759c8e518d5SBorislav Petkov 		 *
1760c8e518d5SBorislav Petkov 		 *	remove hole offset from sys_addr
1761c8e518d5SBorislav Petkov 		 * else
1762c8e518d5SBorislav Petkov 		 *	remove high range offset from sys_addr
1763c8e518d5SBorislav Petkov 		 */
1764c8e518d5SBorislav Petkov 		if ((!(dct_sel_base_addr >> 16) ||
1765c8e518d5SBorislav Petkov 		     dct_sel_base_addr < dhar_base(pvt)) &&
1766972ea17aSBorislav Petkov 		    dhar_valid(pvt) &&
1767c8e518d5SBorislav Petkov 		    (sys_addr >= BIT_64(32)))
1768bc21fa57SBorislav Petkov 			chan_off = hole_off;
17696163b5d4SDoug Thompson 		else
17706163b5d4SDoug Thompson 			chan_off = dct_sel_base_off;
17716163b5d4SDoug Thompson 	} else {
1772c8e518d5SBorislav Petkov 		/*
1773c8e518d5SBorislav Petkov 		 * if
1774c8e518d5SBorislav Petkov 		 * we have a valid hole		&&
1775c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1776c8e518d5SBorislav Petkov 		 *
1777c8e518d5SBorislav Petkov 		 *	remove hole
1778c8e518d5SBorislav Petkov 		 * else
1779c8e518d5SBorislav Petkov 		 *	remove dram base to normalize to DCT address
1780c8e518d5SBorislav Petkov 		 */
1781972ea17aSBorislav Petkov 		if (dhar_valid(pvt) && (sys_addr >= BIT_64(32)))
1782bc21fa57SBorislav Petkov 			chan_off = hole_off;
17836163b5d4SDoug Thompson 		else
1784c8e518d5SBorislav Petkov 			chan_off = dram_base;
17856163b5d4SDoug Thompson 	}
17866163b5d4SDoug Thompson 
178710ef6b0dSChen, Gong 	return (sys_addr & GENMASK_ULL(47,6)) - (chan_off & GENMASK_ULL(47,23));
17886163b5d4SDoug Thompson }
17896163b5d4SDoug Thompson 
17906163b5d4SDoug Thompson /*
17916163b5d4SDoug Thompson  * checks if the csrow passed in is marked as SPARED, if so returns the new
17926163b5d4SDoug Thompson  * spare row
17936163b5d4SDoug Thompson  */
179411c75eadSBorislav Petkov static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow)
17956163b5d4SDoug Thompson {
1796614ec9d8SBorislav Petkov 	int tmp_cs;
17976163b5d4SDoug Thompson 
1798614ec9d8SBorislav Petkov 	if (online_spare_swap_done(pvt, dct) &&
1799614ec9d8SBorislav Petkov 	    csrow == online_spare_bad_dramcs(pvt, dct)) {
1800614ec9d8SBorislav Petkov 
1801614ec9d8SBorislav Petkov 		for_each_chip_select(tmp_cs, dct, pvt) {
1802614ec9d8SBorislav Petkov 			if (chip_select_base(tmp_cs, dct, pvt) & 0x2) {
1803614ec9d8SBorislav Petkov 				csrow = tmp_cs;
1804614ec9d8SBorislav Petkov 				break;
1805614ec9d8SBorislav Petkov 			}
1806614ec9d8SBorislav Petkov 		}
18076163b5d4SDoug Thompson 	}
18086163b5d4SDoug Thompson 	return csrow;
18096163b5d4SDoug Thompson }
18106163b5d4SDoug Thompson 
18116163b5d4SDoug Thompson /*
18126163b5d4SDoug Thompson  * Iterate over the DRAM DCT "base" and "mask" registers looking for a
18136163b5d4SDoug Thompson  * SystemAddr match on the specified 'ChannelSelect' and 'NodeID'
18146163b5d4SDoug Thompson  *
18156163b5d4SDoug Thompson  * Return:
18166163b5d4SDoug Thompson  *	-EINVAL:  NOT FOUND
18176163b5d4SDoug Thompson  *	0..csrow = Chip-Select Row
18186163b5d4SDoug Thompson  */
1819c7e5301aSDaniel J Blueman static int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct)
18206163b5d4SDoug Thompson {
18216163b5d4SDoug Thompson 	struct mem_ctl_info *mci;
18226163b5d4SDoug Thompson 	struct amd64_pvt *pvt;
182311c75eadSBorislav Petkov 	u64 cs_base, cs_mask;
18246163b5d4SDoug Thompson 	int cs_found = -EINVAL;
18256163b5d4SDoug Thompson 	int csrow;
18266163b5d4SDoug Thompson 
18272ec591acSBorislav Petkov 	mci = edac_mc_find(nid);
18286163b5d4SDoug Thompson 	if (!mci)
18296163b5d4SDoug Thompson 		return cs_found;
18306163b5d4SDoug Thompson 
18316163b5d4SDoug Thompson 	pvt = mci->pvt_info;
18326163b5d4SDoug Thompson 
1833956b9ba1SJoe Perches 	edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct);
18346163b5d4SDoug Thompson 
183511c75eadSBorislav Petkov 	for_each_chip_select(csrow, dct, pvt) {
183611c75eadSBorislav Petkov 		if (!csrow_enabled(csrow, dct, pvt))
18376163b5d4SDoug Thompson 			continue;
18386163b5d4SDoug Thompson 
183911c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask);
18406163b5d4SDoug Thompson 
1841956b9ba1SJoe Perches 		edac_dbg(1, "    CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
18426163b5d4SDoug Thompson 			 csrow, cs_base, cs_mask);
18436163b5d4SDoug Thompson 
184411c75eadSBorislav Petkov 		cs_mask = ~cs_mask;
18456163b5d4SDoug Thompson 
1846956b9ba1SJoe Perches 		edac_dbg(1, "    (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n",
184711c75eadSBorislav Petkov 			 (in_addr & cs_mask), (cs_base & cs_mask));
18486163b5d4SDoug Thompson 
184911c75eadSBorislav Petkov 		if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
185018b94f66SAravind Gopalakrishnan 			if (pvt->fam == 0x15 && pvt->model >= 0x30) {
185118b94f66SAravind Gopalakrishnan 				cs_found =  csrow;
185218b94f66SAravind Gopalakrishnan 				break;
185318b94f66SAravind Gopalakrishnan 			}
185411c75eadSBorislav Petkov 			cs_found = f10_process_possible_spare(pvt, dct, csrow);
18556163b5d4SDoug Thompson 
1856956b9ba1SJoe Perches 			edac_dbg(1, " MATCH csrow=%d\n", cs_found);
18576163b5d4SDoug Thompson 			break;
18586163b5d4SDoug Thompson 		}
18596163b5d4SDoug Thompson 	}
18606163b5d4SDoug Thompson 	return cs_found;
18616163b5d4SDoug Thompson }
18626163b5d4SDoug Thompson 
186395b0ef55SBorislav Petkov /*
186495b0ef55SBorislav Petkov  * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is
186595b0ef55SBorislav Petkov  * swapped with a region located at the bottom of memory so that the GPU can use
186695b0ef55SBorislav Petkov  * the interleaved region and thus two channels.
186795b0ef55SBorislav Petkov  */
1868b15f0fcaSBorislav Petkov static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr)
186995b0ef55SBorislav Petkov {
187095b0ef55SBorislav Petkov 	u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr;
187195b0ef55SBorislav Petkov 
1872a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10) {
187395b0ef55SBorislav Petkov 		/* only revC3 and revE have that feature */
1874a4b4bedcSBorislav Petkov 		if (pvt->model < 4 || (pvt->model < 0xa && pvt->stepping < 3))
187595b0ef55SBorislav Petkov 			return sys_addr;
187695b0ef55SBorislav Petkov 	}
187795b0ef55SBorislav Petkov 
18787981a28fSAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F2, SWAP_INTLV_REG, &swap_reg);
187995b0ef55SBorislav Petkov 
188095b0ef55SBorislav Petkov 	if (!(swap_reg & 0x1))
188195b0ef55SBorislav Petkov 		return sys_addr;
188295b0ef55SBorislav Petkov 
188395b0ef55SBorislav Petkov 	swap_base	= (swap_reg >> 3) & 0x7f;
188495b0ef55SBorislav Petkov 	swap_limit	= (swap_reg >> 11) & 0x7f;
188595b0ef55SBorislav Petkov 	rgn_size	= (swap_reg >> 20) & 0x7f;
188695b0ef55SBorislav Petkov 	tmp_addr	= sys_addr >> 27;
188795b0ef55SBorislav Petkov 
188895b0ef55SBorislav Petkov 	if (!(sys_addr >> 34) &&
188995b0ef55SBorislav Petkov 	    (((tmp_addr >= swap_base) &&
189095b0ef55SBorislav Petkov 	     (tmp_addr <= swap_limit)) ||
189195b0ef55SBorislav Petkov 	     (tmp_addr < rgn_size)))
189295b0ef55SBorislav Petkov 		return sys_addr ^ (u64)swap_base << 27;
189395b0ef55SBorislav Petkov 
189495b0ef55SBorislav Petkov 	return sys_addr;
189595b0ef55SBorislav Petkov }
189695b0ef55SBorislav Petkov 
1897f71d0a05SDoug Thompson /* For a given @dram_range, check if @sys_addr falls within it. */
1898e761359aSBorislav Petkov static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
189933ca0643SBorislav Petkov 				  u64 sys_addr, int *chan_sel)
1900f71d0a05SDoug Thompson {
1901229a7a11SBorislav Petkov 	int cs_found = -EINVAL;
1902c8e518d5SBorislav Petkov 	u64 chan_addr;
19035d4b58e8SBorislav Petkov 	u32 dct_sel_base;
190411c75eadSBorislav Petkov 	u8 channel;
1905229a7a11SBorislav Petkov 	bool high_range = false;
1906f71d0a05SDoug Thompson 
19077f19bf75SBorislav Petkov 	u8 node_id    = dram_dst_node(pvt, range);
1908229a7a11SBorislav Petkov 	u8 intlv_en   = dram_intlv_en(pvt, range);
19097f19bf75SBorislav Petkov 	u32 intlv_sel = dram_intlv_sel(pvt, range);
1910f71d0a05SDoug Thompson 
1911956b9ba1SJoe Perches 	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
1912c8e518d5SBorislav Petkov 		 range, sys_addr, get_dram_limit(pvt, range));
1913f71d0a05SDoug Thompson 
1914355fba60SBorislav Petkov 	if (dhar_valid(pvt) &&
1915355fba60SBorislav Petkov 	    dhar_base(pvt) <= sys_addr &&
1916355fba60SBorislav Petkov 	    sys_addr < BIT_64(32)) {
1917355fba60SBorislav Petkov 		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
1918355fba60SBorislav Petkov 			    sys_addr);
1919f71d0a05SDoug Thompson 		return -EINVAL;
1920355fba60SBorislav Petkov 	}
1921355fba60SBorislav Petkov 
1922f030ddfbSBorislav Petkov 	if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en)))
1923355fba60SBorislav Petkov 		return -EINVAL;
1924f71d0a05SDoug Thompson 
1925b15f0fcaSBorislav Petkov 	sys_addr = f1x_swap_interleaved_region(pvt, sys_addr);
192695b0ef55SBorislav Petkov 
1927f71d0a05SDoug Thompson 	dct_sel_base = dct_sel_baseaddr(pvt);
1928f71d0a05SDoug Thompson 
1929f71d0a05SDoug Thompson 	/*
1930f71d0a05SDoug Thompson 	 * check whether addresses >= DctSelBaseAddr[47:27] are to be used to
1931f71d0a05SDoug Thompson 	 * select between DCT0 and DCT1.
1932f71d0a05SDoug Thompson 	 */
1933f71d0a05SDoug Thompson 	if (dct_high_range_enabled(pvt) &&
1934f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt) &&
1935f71d0a05SDoug Thompson 	   ((sys_addr >> 27) >= (dct_sel_base >> 11)))
1936229a7a11SBorislav Petkov 		high_range = true;
1937f71d0a05SDoug Thompson 
1938b15f0fcaSBorislav Petkov 	channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en);
1939f71d0a05SDoug Thompson 
1940b15f0fcaSBorislav Petkov 	chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr,
1941c8e518d5SBorislav Petkov 					  high_range, dct_sel_base);
1942f71d0a05SDoug Thompson 
1943e2f79dbdSBorislav Petkov 	/* Remove node interleaving, see F1x120 */
1944e2f79dbdSBorislav Petkov 	if (intlv_en)
1945e2f79dbdSBorislav Petkov 		chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) |
1946e2f79dbdSBorislav Petkov 			    (chan_addr & 0xfff);
1947f71d0a05SDoug Thompson 
19485d4b58e8SBorislav Petkov 	/* remove channel interleave */
1949f71d0a05SDoug Thompson 	if (dct_interleave_enabled(pvt) &&
1950f71d0a05SDoug Thompson 	   !dct_high_range_enabled(pvt) &&
1951f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt)) {
19525d4b58e8SBorislav Petkov 
19535d4b58e8SBorislav Petkov 		if (dct_sel_interleave_addr(pvt) != 1) {
19545d4b58e8SBorislav Petkov 			if (dct_sel_interleave_addr(pvt) == 0x3)
19555d4b58e8SBorislav Petkov 				/* hash 9 */
19565d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 10) << 9) |
19575d4b58e8SBorislav Petkov 					     (chan_addr & 0x1ff);
19585d4b58e8SBorislav Petkov 			else
19595d4b58e8SBorislav Petkov 				/* A[6] or hash 6 */
19605d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 7) << 6) |
19615d4b58e8SBorislav Petkov 					     (chan_addr & 0x3f);
19625d4b58e8SBorislav Petkov 		} else
19635d4b58e8SBorislav Petkov 			/* A[12] */
19645d4b58e8SBorislav Petkov 			chan_addr = ((chan_addr >> 13) << 12) |
19655d4b58e8SBorislav Petkov 				     (chan_addr & 0xfff);
1966f71d0a05SDoug Thompson 	}
1967f71d0a05SDoug Thompson 
1968956b9ba1SJoe Perches 	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
1969f71d0a05SDoug Thompson 
1970b15f0fcaSBorislav Petkov 	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel);
1971f71d0a05SDoug Thompson 
197233ca0643SBorislav Petkov 	if (cs_found >= 0)
1973f71d0a05SDoug Thompson 		*chan_sel = channel;
197433ca0643SBorislav Petkov 
1975f71d0a05SDoug Thompson 	return cs_found;
1976f71d0a05SDoug Thompson }
1977f71d0a05SDoug Thompson 
197818b94f66SAravind Gopalakrishnan static int f15_m30h_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
197918b94f66SAravind Gopalakrishnan 					u64 sys_addr, int *chan_sel)
198018b94f66SAravind Gopalakrishnan {
198118b94f66SAravind Gopalakrishnan 	int cs_found = -EINVAL;
198218b94f66SAravind Gopalakrishnan 	int num_dcts_intlv = 0;
198318b94f66SAravind Gopalakrishnan 	u64 chan_addr, chan_offset;
198418b94f66SAravind Gopalakrishnan 	u64 dct_base, dct_limit;
198518b94f66SAravind Gopalakrishnan 	u32 dct_cont_base_reg, dct_cont_limit_reg, tmp;
198618b94f66SAravind Gopalakrishnan 	u8 channel, alias_channel, leg_mmio_hole, dct_sel, dct_offset_en;
198718b94f66SAravind Gopalakrishnan 
198818b94f66SAravind Gopalakrishnan 	u64 dhar_offset		= f10_dhar_offset(pvt);
198918b94f66SAravind Gopalakrishnan 	u8 intlv_addr		= dct_sel_interleave_addr(pvt);
199018b94f66SAravind Gopalakrishnan 	u8 node_id		= dram_dst_node(pvt, range);
199118b94f66SAravind Gopalakrishnan 	u8 intlv_en		= dram_intlv_en(pvt, range);
199218b94f66SAravind Gopalakrishnan 
199318b94f66SAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F1, DRAM_CONT_BASE, &dct_cont_base_reg);
199418b94f66SAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F1, DRAM_CONT_LIMIT, &dct_cont_limit_reg);
199518b94f66SAravind Gopalakrishnan 
199618b94f66SAravind Gopalakrishnan 	dct_offset_en		= (u8) ((dct_cont_base_reg >> 3) & BIT(0));
199718b94f66SAravind Gopalakrishnan 	dct_sel			= (u8) ((dct_cont_base_reg >> 4) & 0x7);
199818b94f66SAravind Gopalakrishnan 
199918b94f66SAravind Gopalakrishnan 	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
200018b94f66SAravind Gopalakrishnan 		 range, sys_addr, get_dram_limit(pvt, range));
200118b94f66SAravind Gopalakrishnan 
200218b94f66SAravind Gopalakrishnan 	if (!(get_dram_base(pvt, range)  <= sys_addr) &&
200318b94f66SAravind Gopalakrishnan 	    !(get_dram_limit(pvt, range) >= sys_addr))
200418b94f66SAravind Gopalakrishnan 		return -EINVAL;
200518b94f66SAravind Gopalakrishnan 
200618b94f66SAravind Gopalakrishnan 	if (dhar_valid(pvt) &&
200718b94f66SAravind Gopalakrishnan 	    dhar_base(pvt) <= sys_addr &&
200818b94f66SAravind Gopalakrishnan 	    sys_addr < BIT_64(32)) {
200918b94f66SAravind Gopalakrishnan 		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
201018b94f66SAravind Gopalakrishnan 			    sys_addr);
201118b94f66SAravind Gopalakrishnan 		return -EINVAL;
201218b94f66SAravind Gopalakrishnan 	}
201318b94f66SAravind Gopalakrishnan 
201418b94f66SAravind Gopalakrishnan 	/* Verify sys_addr is within DCT Range. */
20154fc06b31SAravind Gopalakrishnan 	dct_base = (u64) dct_sel_baseaddr(pvt);
20164fc06b31SAravind Gopalakrishnan 	dct_limit = (dct_cont_limit_reg >> 11) & 0x1FFF;
201718b94f66SAravind Gopalakrishnan 
201818b94f66SAravind Gopalakrishnan 	if (!(dct_cont_base_reg & BIT(0)) &&
20194fc06b31SAravind Gopalakrishnan 	    !(dct_base <= (sys_addr >> 27) &&
20204fc06b31SAravind Gopalakrishnan 	      dct_limit >= (sys_addr >> 27)))
202118b94f66SAravind Gopalakrishnan 		return -EINVAL;
202218b94f66SAravind Gopalakrishnan 
202318b94f66SAravind Gopalakrishnan 	/* Verify number of dct's that participate in channel interleaving. */
202418b94f66SAravind Gopalakrishnan 	num_dcts_intlv = (int) hweight8(intlv_en);
202518b94f66SAravind Gopalakrishnan 
202618b94f66SAravind Gopalakrishnan 	if (!(num_dcts_intlv % 2 == 0) || (num_dcts_intlv > 4))
202718b94f66SAravind Gopalakrishnan 		return -EINVAL;
202818b94f66SAravind Gopalakrishnan 
2029dc0a50a8SYazen Ghannam 	if (pvt->model >= 0x60)
2030dc0a50a8SYazen Ghannam 		channel = f1x_determine_channel(pvt, sys_addr, false, intlv_en);
2031dc0a50a8SYazen Ghannam 	else
203218b94f66SAravind Gopalakrishnan 		channel = f15_m30h_determine_channel(pvt, sys_addr, intlv_en,
203318b94f66SAravind Gopalakrishnan 						     num_dcts_intlv, dct_sel);
203418b94f66SAravind Gopalakrishnan 
203518b94f66SAravind Gopalakrishnan 	/* Verify we stay within the MAX number of channels allowed */
20367f3f5240SAravind Gopalakrishnan 	if (channel > 3)
203718b94f66SAravind Gopalakrishnan 		return -EINVAL;
203818b94f66SAravind Gopalakrishnan 
203918b94f66SAravind Gopalakrishnan 	leg_mmio_hole = (u8) (dct_cont_base_reg >> 1 & BIT(0));
204018b94f66SAravind Gopalakrishnan 
204118b94f66SAravind Gopalakrishnan 	/* Get normalized DCT addr */
204218b94f66SAravind Gopalakrishnan 	if (leg_mmio_hole && (sys_addr >= BIT_64(32)))
204318b94f66SAravind Gopalakrishnan 		chan_offset = dhar_offset;
204418b94f66SAravind Gopalakrishnan 	else
20454fc06b31SAravind Gopalakrishnan 		chan_offset = dct_base << 27;
204618b94f66SAravind Gopalakrishnan 
204718b94f66SAravind Gopalakrishnan 	chan_addr = sys_addr - chan_offset;
204818b94f66SAravind Gopalakrishnan 
204918b94f66SAravind Gopalakrishnan 	/* remove channel interleave */
205018b94f66SAravind Gopalakrishnan 	if (num_dcts_intlv == 2) {
205118b94f66SAravind Gopalakrishnan 		if (intlv_addr == 0x4)
205218b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 9) << 8) |
205318b94f66SAravind Gopalakrishnan 						(chan_addr & 0xff);
205418b94f66SAravind Gopalakrishnan 		else if (intlv_addr == 0x5)
205518b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 10) << 9) |
205618b94f66SAravind Gopalakrishnan 						(chan_addr & 0x1ff);
205718b94f66SAravind Gopalakrishnan 		else
205818b94f66SAravind Gopalakrishnan 			return -EINVAL;
205918b94f66SAravind Gopalakrishnan 
206018b94f66SAravind Gopalakrishnan 	} else if (num_dcts_intlv == 4) {
206118b94f66SAravind Gopalakrishnan 		if (intlv_addr == 0x4)
206218b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 10) << 8) |
206318b94f66SAravind Gopalakrishnan 							(chan_addr & 0xff);
206418b94f66SAravind Gopalakrishnan 		else if (intlv_addr == 0x5)
206518b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 11) << 9) |
206618b94f66SAravind Gopalakrishnan 							(chan_addr & 0x1ff);
206718b94f66SAravind Gopalakrishnan 		else
206818b94f66SAravind Gopalakrishnan 			return -EINVAL;
206918b94f66SAravind Gopalakrishnan 	}
207018b94f66SAravind Gopalakrishnan 
207118b94f66SAravind Gopalakrishnan 	if (dct_offset_en) {
207218b94f66SAravind Gopalakrishnan 		amd64_read_pci_cfg(pvt->F1,
207318b94f66SAravind Gopalakrishnan 				   DRAM_CONT_HIGH_OFF + (int) channel * 4,
207418b94f66SAravind Gopalakrishnan 				   &tmp);
20754fc06b31SAravind Gopalakrishnan 		chan_addr +=  (u64) ((tmp >> 11) & 0xfff) << 27;
207618b94f66SAravind Gopalakrishnan 	}
207718b94f66SAravind Gopalakrishnan 
207818b94f66SAravind Gopalakrishnan 	f15h_select_dct(pvt, channel);
207918b94f66SAravind Gopalakrishnan 
208018b94f66SAravind Gopalakrishnan 	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
208118b94f66SAravind Gopalakrishnan 
208218b94f66SAravind Gopalakrishnan 	/*
208318b94f66SAravind Gopalakrishnan 	 * Find Chip select:
208418b94f66SAravind Gopalakrishnan 	 * if channel = 3, then alias it to 1. This is because, in F15 M30h,
208518b94f66SAravind Gopalakrishnan 	 * there is support for 4 DCT's, but only 2 are currently functional.
208618b94f66SAravind Gopalakrishnan 	 * They are DCT0 and DCT3. But we have read all registers of DCT3 into
208718b94f66SAravind Gopalakrishnan 	 * pvt->csels[1]. So we need to use '1' here to get correct info.
208818b94f66SAravind Gopalakrishnan 	 * Refer F15 M30h BKDG Section 2.10 and 2.10.3 for clarifications.
208918b94f66SAravind Gopalakrishnan 	 */
209018b94f66SAravind Gopalakrishnan 	alias_channel =  (channel == 3) ? 1 : channel;
209118b94f66SAravind Gopalakrishnan 
209218b94f66SAravind Gopalakrishnan 	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, alias_channel);
209318b94f66SAravind Gopalakrishnan 
209418b94f66SAravind Gopalakrishnan 	if (cs_found >= 0)
209518b94f66SAravind Gopalakrishnan 		*chan_sel = alias_channel;
209618b94f66SAravind Gopalakrishnan 
209718b94f66SAravind Gopalakrishnan 	return cs_found;
209818b94f66SAravind Gopalakrishnan }
209918b94f66SAravind Gopalakrishnan 
210018b94f66SAravind Gopalakrishnan static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt,
210118b94f66SAravind Gopalakrishnan 					u64 sys_addr,
210233ca0643SBorislav Petkov 					int *chan_sel)
2103f71d0a05SDoug Thompson {
2104e761359aSBorislav Petkov 	int cs_found = -EINVAL;
2105e761359aSBorislav Petkov 	unsigned range;
2106f71d0a05SDoug Thompson 
21077f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
21087f19bf75SBorislav Petkov 		if (!dram_rw(pvt, range))
2109f71d0a05SDoug Thompson 			continue;
2110f71d0a05SDoug Thompson 
211118b94f66SAravind Gopalakrishnan 		if (pvt->fam == 0x15 && pvt->model >= 0x30)
211218b94f66SAravind Gopalakrishnan 			cs_found = f15_m30h_match_to_this_node(pvt, range,
211318b94f66SAravind Gopalakrishnan 							       sys_addr,
211418b94f66SAravind Gopalakrishnan 							       chan_sel);
2115f71d0a05SDoug Thompson 
211618b94f66SAravind Gopalakrishnan 		else if ((get_dram_base(pvt, range)  <= sys_addr) &&
211718b94f66SAravind Gopalakrishnan 			 (get_dram_limit(pvt, range) >= sys_addr)) {
2118b15f0fcaSBorislav Petkov 			cs_found = f1x_match_to_this_node(pvt, range,
211933ca0643SBorislav Petkov 							  sys_addr, chan_sel);
2120f71d0a05SDoug Thompson 			if (cs_found >= 0)
2121f71d0a05SDoug Thompson 				break;
2122f71d0a05SDoug Thompson 		}
2123f71d0a05SDoug Thompson 	}
2124f71d0a05SDoug Thompson 	return cs_found;
2125f71d0a05SDoug Thompson }
2126f71d0a05SDoug Thompson 
2127f71d0a05SDoug Thompson /*
2128bdc30a0cSBorislav Petkov  * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
2129bdc30a0cSBorislav Petkov  * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
2130f71d0a05SDoug Thompson  *
2131bdc30a0cSBorislav Petkov  * The @sys_addr is usually an error address received from the hardware
2132bdc30a0cSBorislav Petkov  * (MCX_ADDR).
2133f71d0a05SDoug Thompson  */
2134b15f0fcaSBorislav Petkov static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
213533ca0643SBorislav Petkov 				     struct err_info *err)
2136f71d0a05SDoug Thompson {
2137f71d0a05SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
2138f71d0a05SDoug Thompson 
213933ca0643SBorislav Petkov 	error_address_to_page_and_offset(sys_addr, err);
2140ab5a503cSMauro Carvalho Chehab 
214133ca0643SBorislav Petkov 	err->csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &err->channel);
214233ca0643SBorislav Petkov 	if (err->csrow < 0) {
214333ca0643SBorislav Petkov 		err->err_code = ERR_CSROW;
2144bdc30a0cSBorislav Petkov 		return;
2145bdc30a0cSBorislav Petkov 	}
2146bdc30a0cSBorislav Petkov 
2147f71d0a05SDoug Thompson 	/*
2148bdc30a0cSBorislav Petkov 	 * We need the syndromes for channel detection only when we're
2149bdc30a0cSBorislav Petkov 	 * ganged. Otherwise @chan should already contain the channel at
2150bdc30a0cSBorislav Petkov 	 * this point.
2151f71d0a05SDoug Thompson 	 */
2152a97fa68eSBorislav Petkov 	if (dct_ganging_enabled(pvt))
215333ca0643SBorislav Petkov 		err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
2154f71d0a05SDoug Thompson }
2155f71d0a05SDoug Thompson 
2156f71d0a05SDoug Thompson /*
21578566c4dfSBorislav Petkov  * debug routine to display the memory sizes of all logical DIMMs and its
2158cb328507SBorislav Petkov  * CSROWs
2159f71d0a05SDoug Thompson  */
2160d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
2161f71d0a05SDoug Thompson {
2162bb89f5a0SBorislav Petkov 	int dimm, size0, size1;
2163525a1b20SBorislav Petkov 	u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases;
2164525a1b20SBorislav Petkov 	u32 dbam  = ctrl ? pvt->dbam1 : pvt->dbam0;
2165f71d0a05SDoug Thompson 
2166a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf) {
21678566c4dfSBorislav Petkov 		/* K8 families < revF not supported yet */
21681433eb99SBorislav Petkov 	       if (pvt->ext_model < K8_REV_F)
21698566c4dfSBorislav Petkov 			return;
21708566c4dfSBorislav Petkov 	       else
21718566c4dfSBorislav Petkov 		       WARN_ON(ctrl != 0);
21728566c4dfSBorislav Petkov 	}
21738566c4dfSBorislav Petkov 
21747981a28fSAravind Gopalakrishnan 	if (pvt->fam == 0x10) {
21757981a28fSAravind Gopalakrishnan 		dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1
21767981a28fSAravind Gopalakrishnan 							   : pvt->dbam0;
21777981a28fSAravind Gopalakrishnan 		dcsb = (ctrl && !dct_ganging_enabled(pvt)) ?
21787981a28fSAravind Gopalakrishnan 				 pvt->csels[1].csbases :
21797981a28fSAravind Gopalakrishnan 				 pvt->csels[0].csbases;
21807981a28fSAravind Gopalakrishnan 	} else if (ctrl) {
21817981a28fSAravind Gopalakrishnan 		dbam = pvt->dbam0;
21827981a28fSAravind Gopalakrishnan 		dcsb = pvt->csels[1].csbases;
21837981a28fSAravind Gopalakrishnan 	}
2184956b9ba1SJoe Perches 	edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
2185956b9ba1SJoe Perches 		 ctrl, dbam);
2186f71d0a05SDoug Thompson 
21878566c4dfSBorislav Petkov 	edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
21888566c4dfSBorislav Petkov 
2189f71d0a05SDoug Thompson 	/* Dump memory sizes for DIMM and its CSROWs */
2190f71d0a05SDoug Thompson 	for (dimm = 0; dimm < 4; dimm++) {
2191f71d0a05SDoug Thompson 
2192f71d0a05SDoug Thompson 		size0 = 0;
219311c75eadSBorislav Petkov 		if (dcsb[dimm*2] & DCSB_CS_ENABLE)
219407ed82efSYazen Ghannam 			/*
219507ed82efSYazen Ghannam 			 * For F15m60h, we need multiplier for LRDIMM cs_size
219607ed82efSYazen Ghannam 			 * calculation. We pass dimm value to the dbam_to_cs
2197a597d2a5SAravind Gopalakrishnan 			 * mapper so we can find the multiplier from the
2198a597d2a5SAravind Gopalakrishnan 			 * corresponding DCSM.
2199a597d2a5SAravind Gopalakrishnan 			 */
220041d8bfabSBorislav Petkov 			size0 = pvt->ops->dbam_to_cs(pvt, ctrl,
2201a597d2a5SAravind Gopalakrishnan 						     DBAM_DIMM(dimm, dbam),
2202a597d2a5SAravind Gopalakrishnan 						     dimm);
2203f71d0a05SDoug Thompson 
2204f71d0a05SDoug Thompson 		size1 = 0;
220511c75eadSBorislav Petkov 		if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE)
220641d8bfabSBorislav Petkov 			size1 = pvt->ops->dbam_to_cs(pvt, ctrl,
2207a597d2a5SAravind Gopalakrishnan 						     DBAM_DIMM(dimm, dbam),
2208a597d2a5SAravind Gopalakrishnan 						     dimm);
2209f71d0a05SDoug Thompson 
221024f9a7feSBorislav Petkov 		amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
2211bb89f5a0SBorislav Petkov 				dimm * 2,     size0,
2212bb89f5a0SBorislav Petkov 				dimm * 2 + 1, size1);
2213f71d0a05SDoug Thompson 	}
2214f71d0a05SDoug Thompson }
2215f71d0a05SDoug Thompson 
2216d1ea71cdSBorislav Petkov static struct amd64_family_type family_types[] = {
22174d37607aSDoug Thompson 	[K8_CPUS] = {
22180092b20dSBorislav Petkov 		.ctl_name = "K8",
22198d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
22203f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
22215e4c5527SYazen Ghannam 		.max_mcs = 2,
22224d37607aSDoug Thompson 		.ops = {
22234d37607aSDoug Thompson 			.early_channel_count	= k8_early_channel_count,
22244d37607aSDoug Thompson 			.map_sysaddr_to_csrow	= k8_map_sysaddr_to_csrow,
22251433eb99SBorislav Petkov 			.dbam_to_cs		= k8_dbam_to_chip_select,
22264d37607aSDoug Thompson 		}
22274d37607aSDoug Thompson 	},
22284d37607aSDoug Thompson 	[F10_CPUS] = {
22290092b20dSBorislav Petkov 		.ctl_name = "F10h",
22308d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP,
22313f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM,
22325e4c5527SYazen Ghannam 		.max_mcs = 2,
22334d37607aSDoug Thompson 		.ops = {
22347d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
2235b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
22361433eb99SBorislav Petkov 			.dbam_to_cs		= f10_dbam_to_chip_select,
2237b2b0c605SBorislav Petkov 		}
2238b2b0c605SBorislav Petkov 	},
2239b2b0c605SBorislav Petkov 	[F15_CPUS] = {
2240b2b0c605SBorislav Petkov 		.ctl_name = "F15h",
2241df71a053SBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1,
22423f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2,
22435e4c5527SYazen Ghannam 		.max_mcs = 2,
2244b2b0c605SBorislav Petkov 		.ops = {
22457d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
2246b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
224741d8bfabSBorislav Petkov 			.dbam_to_cs		= f15_dbam_to_chip_select,
22484d37607aSDoug Thompson 		}
22494d37607aSDoug Thompson 	},
225018b94f66SAravind Gopalakrishnan 	[F15_M30H_CPUS] = {
225118b94f66SAravind Gopalakrishnan 		.ctl_name = "F15h_M30h",
225218b94f66SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1,
22533f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2,
22545e4c5527SYazen Ghannam 		.max_mcs = 2,
225518b94f66SAravind Gopalakrishnan 		.ops = {
225618b94f66SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
225718b94f66SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
225818b94f66SAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
225918b94f66SAravind Gopalakrishnan 		}
226018b94f66SAravind Gopalakrishnan 	},
2261a597d2a5SAravind Gopalakrishnan 	[F15_M60H_CPUS] = {
2262a597d2a5SAravind Gopalakrishnan 		.ctl_name = "F15h_M60h",
2263a597d2a5SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1,
22643f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2,
22655e4c5527SYazen Ghannam 		.max_mcs = 2,
2266a597d2a5SAravind Gopalakrishnan 		.ops = {
2267a597d2a5SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
2268a597d2a5SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
2269a597d2a5SAravind Gopalakrishnan 			.dbam_to_cs		= f15_m60h_dbam_to_chip_select,
2270a597d2a5SAravind Gopalakrishnan 		}
2271a597d2a5SAravind Gopalakrishnan 	},
227294c1acf2SAravind Gopalakrishnan 	[F16_CPUS] = {
227394c1acf2SAravind Gopalakrishnan 		.ctl_name = "F16h",
227494c1acf2SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1,
22753f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2,
22765e4c5527SYazen Ghannam 		.max_mcs = 2,
227794c1acf2SAravind Gopalakrishnan 		.ops = {
227894c1acf2SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
227994c1acf2SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
228094c1acf2SAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
228194c1acf2SAravind Gopalakrishnan 		}
228294c1acf2SAravind Gopalakrishnan 	},
228385a8885bSAravind Gopalakrishnan 	[F16_M30H_CPUS] = {
228485a8885bSAravind Gopalakrishnan 		.ctl_name = "F16h_M30h",
228585a8885bSAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F1,
22863f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2,
22875e4c5527SYazen Ghannam 		.max_mcs = 2,
228885a8885bSAravind Gopalakrishnan 		.ops = {
228985a8885bSAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
229085a8885bSAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
229185a8885bSAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
229285a8885bSAravind Gopalakrishnan 		}
229385a8885bSAravind Gopalakrishnan 	},
2294f1cbbec9SYazen Ghannam 	[F17_CPUS] = {
2295f1cbbec9SYazen Ghannam 		.ctl_name = "F17h",
2296f1cbbec9SYazen Ghannam 		.f0_id = PCI_DEVICE_ID_AMD_17H_DF_F0,
2297f1cbbec9SYazen Ghannam 		.f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6,
22985e4c5527SYazen Ghannam 		.max_mcs = 2,
2299f1cbbec9SYazen Ghannam 		.ops = {
2300f1cbbec9SYazen Ghannam 			.early_channel_count	= f17_early_channel_count,
2301e53a3b26SYazen Ghannam 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
2302f1cbbec9SYazen Ghannam 		}
2303f1cbbec9SYazen Ghannam 	},
23048960de4aSMichael Jin 	[F17_M10H_CPUS] = {
23058960de4aSMichael Jin 		.ctl_name = "F17h_M10h",
23068960de4aSMichael Jin 		.f0_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F0,
23078960de4aSMichael Jin 		.f6_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F6,
23085e4c5527SYazen Ghannam 		.max_mcs = 2,
23098960de4aSMichael Jin 		.ops = {
23108960de4aSMichael Jin 			.early_channel_count	= f17_early_channel_count,
2311e53a3b26SYazen Ghannam 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
23128960de4aSMichael Jin 		}
23138960de4aSMichael Jin 	},
23146e846239SYazen Ghannam 	[F17_M30H_CPUS] = {
23156e846239SYazen Ghannam 		.ctl_name = "F17h_M30h",
23166e846239SYazen Ghannam 		.f0_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F0,
23176e846239SYazen Ghannam 		.f6_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F6,
23185e4c5527SYazen Ghannam 		.max_mcs = 8,
23196e846239SYazen Ghannam 		.ops = {
23206e846239SYazen Ghannam 			.early_channel_count	= f17_early_channel_count,
2321e53a3b26SYazen Ghannam 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
23226e846239SYazen Ghannam 		}
23236e846239SYazen Ghannam 	},
2324b6bea24dSAlexander Monakov 	[F17_M60H_CPUS] = {
2325b6bea24dSAlexander Monakov 		.ctl_name = "F17h_M60h",
2326b6bea24dSAlexander Monakov 		.f0_id = PCI_DEVICE_ID_AMD_17H_M60H_DF_F0,
2327b6bea24dSAlexander Monakov 		.f6_id = PCI_DEVICE_ID_AMD_17H_M60H_DF_F6,
2328b6bea24dSAlexander Monakov 		.max_mcs = 2,
2329b6bea24dSAlexander Monakov 		.ops = {
2330b6bea24dSAlexander Monakov 			.early_channel_count	= f17_early_channel_count,
2331b6bea24dSAlexander Monakov 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
2332b6bea24dSAlexander Monakov 		}
2333b6bea24dSAlexander Monakov 	},
23343e443eb3SIsaac Vaughn 	[F17_M70H_CPUS] = {
23353e443eb3SIsaac Vaughn 		.ctl_name = "F17h_M70h",
23363e443eb3SIsaac Vaughn 		.f0_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F0,
23373e443eb3SIsaac Vaughn 		.f6_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F6,
23385e4c5527SYazen Ghannam 		.max_mcs = 2,
23393e443eb3SIsaac Vaughn 		.ops = {
23403e443eb3SIsaac Vaughn 			.early_channel_count	= f17_early_channel_count,
23413e443eb3SIsaac Vaughn 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
23423e443eb3SIsaac Vaughn 		}
23433e443eb3SIsaac Vaughn 	},
23442eb61c91SYazen Ghannam 	[F19_CPUS] = {
23452eb61c91SYazen Ghannam 		.ctl_name = "F19h",
23462eb61c91SYazen Ghannam 		.f0_id = PCI_DEVICE_ID_AMD_19H_DF_F0,
23472eb61c91SYazen Ghannam 		.f6_id = PCI_DEVICE_ID_AMD_19H_DF_F6,
23482eb61c91SYazen Ghannam 		.max_mcs = 8,
23492eb61c91SYazen Ghannam 		.ops = {
23502eb61c91SYazen Ghannam 			.early_channel_count	= f17_early_channel_count,
23512eb61c91SYazen Ghannam 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
23522eb61c91SYazen Ghannam 		}
23532eb61c91SYazen Ghannam 	},
23544d37607aSDoug Thompson };
23554d37607aSDoug Thompson 
2356b1289d6fSDoug Thompson /*
2357bfc04aecSBorislav Petkov  * These are tables of eigenvectors (one per line) which can be used for the
2358bfc04aecSBorislav Petkov  * construction of the syndrome tables. The modified syndrome search algorithm
2359bfc04aecSBorislav Petkov  * uses those to find the symbol in error and thus the DIMM.
2360b1289d6fSDoug Thompson  *
2361bfc04aecSBorislav Petkov  * Algorithm courtesy of Ross LaFetra from AMD.
2362b1289d6fSDoug Thompson  */
2363c7e5301aSDaniel J Blueman static const u16 x4_vectors[] = {
2364bfc04aecSBorislav Petkov 	0x2f57, 0x1afe, 0x66cc, 0xdd88,
2365bfc04aecSBorislav Petkov 	0x11eb, 0x3396, 0x7f4c, 0xeac8,
2366bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008,
2367bfc04aecSBorislav Petkov 	0x1013, 0x3032, 0x4044, 0x8088,
2368bfc04aecSBorislav Petkov 	0x106b, 0x30d6, 0x70fc, 0xe0a8,
2369bfc04aecSBorislav Petkov 	0x4857, 0xc4fe, 0x13cc, 0x3288,
2370bfc04aecSBorislav Petkov 	0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
2371bfc04aecSBorislav Petkov 	0x1f39, 0x251e, 0xbd6c, 0x6bd8,
2372bfc04aecSBorislav Petkov 	0x15c1, 0x2a42, 0x89ac, 0x4758,
2373bfc04aecSBorislav Petkov 	0x2b03, 0x1602, 0x4f0c, 0xca08,
2374bfc04aecSBorislav Petkov 	0x1f07, 0x3a0e, 0x6b04, 0xbd08,
2375bfc04aecSBorislav Petkov 	0x8ba7, 0x465e, 0x244c, 0x1cc8,
2376bfc04aecSBorislav Petkov 	0x2b87, 0x164e, 0x642c, 0xdc18,
2377bfc04aecSBorislav Petkov 	0x40b9, 0x80de, 0x1094, 0x20e8,
2378bfc04aecSBorislav Petkov 	0x27db, 0x1eb6, 0x9dac, 0x7b58,
2379bfc04aecSBorislav Petkov 	0x11c1, 0x2242, 0x84ac, 0x4c58,
2380bfc04aecSBorislav Petkov 	0x1be5, 0x2d7a, 0x5e34, 0xa718,
2381bfc04aecSBorislav Petkov 	0x4b39, 0x8d1e, 0x14b4, 0x28d8,
2382bfc04aecSBorislav Petkov 	0x4c97, 0xc87e, 0x11fc, 0x33a8,
2383bfc04aecSBorislav Petkov 	0x8e97, 0x497e, 0x2ffc, 0x1aa8,
2384bfc04aecSBorislav Petkov 	0x16b3, 0x3d62, 0x4f34, 0x8518,
2385bfc04aecSBorislav Petkov 	0x1e2f, 0x391a, 0x5cac, 0xf858,
2386bfc04aecSBorislav Petkov 	0x1d9f, 0x3b7a, 0x572c, 0xfe18,
2387bfc04aecSBorislav Petkov 	0x15f5, 0x2a5a, 0x5264, 0xa3b8,
2388bfc04aecSBorislav Petkov 	0x1dbb, 0x3b66, 0x715c, 0xe3f8,
2389bfc04aecSBorislav Petkov 	0x4397, 0xc27e, 0x17fc, 0x3ea8,
2390bfc04aecSBorislav Petkov 	0x1617, 0x3d3e, 0x6464, 0xb8b8,
2391bfc04aecSBorislav Petkov 	0x23ff, 0x12aa, 0xab6c, 0x56d8,
2392bfc04aecSBorislav Petkov 	0x2dfb, 0x1ba6, 0x913c, 0x7328,
2393bfc04aecSBorislav Petkov 	0x185d, 0x2ca6, 0x7914, 0x9e28,
2394bfc04aecSBorislav Petkov 	0x171b, 0x3e36, 0x7d7c, 0xebe8,
2395bfc04aecSBorislav Petkov 	0x4199, 0x82ee, 0x19f4, 0x2e58,
2396bfc04aecSBorislav Petkov 	0x4807, 0xc40e, 0x130c, 0x3208,
2397bfc04aecSBorislav Petkov 	0x1905, 0x2e0a, 0x5804, 0xac08,
2398bfc04aecSBorislav Petkov 	0x213f, 0x132a, 0xadfc, 0x5ba8,
2399bfc04aecSBorislav Petkov 	0x19a9, 0x2efe, 0xb5cc, 0x6f88,
2400b1289d6fSDoug Thompson };
2401b1289d6fSDoug Thompson 
2402c7e5301aSDaniel J Blueman static const u16 x8_vectors[] = {
2403bfc04aecSBorislav Petkov 	0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
2404bfc04aecSBorislav Petkov 	0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
2405bfc04aecSBorislav Petkov 	0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
2406bfc04aecSBorislav Petkov 	0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
2407bfc04aecSBorislav Petkov 	0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
2408bfc04aecSBorislav Petkov 	0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
2409bfc04aecSBorislav Petkov 	0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
2410bfc04aecSBorislav Petkov 	0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
2411bfc04aecSBorislav Petkov 	0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
2412bfc04aecSBorislav Petkov 	0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
2413bfc04aecSBorislav Petkov 	0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
2414bfc04aecSBorislav Petkov 	0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
2415bfc04aecSBorislav Petkov 	0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
2416bfc04aecSBorislav Petkov 	0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
2417bfc04aecSBorislav Petkov 	0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
2418bfc04aecSBorislav Petkov 	0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
2419bfc04aecSBorislav Petkov 	0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
2420bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
2421bfc04aecSBorislav Petkov 	0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
2422bfc04aecSBorislav Petkov };
2423bfc04aecSBorislav Petkov 
2424c7e5301aSDaniel J Blueman static int decode_syndrome(u16 syndrome, const u16 *vectors, unsigned num_vecs,
2425d34a6ecdSBorislav Petkov 			   unsigned v_dim)
2426b1289d6fSDoug Thompson {
2427bfc04aecSBorislav Petkov 	unsigned int i, err_sym;
2428b1289d6fSDoug Thompson 
2429bfc04aecSBorislav Petkov 	for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
2430bfc04aecSBorislav Petkov 		u16 s = syndrome;
2431d34a6ecdSBorislav Petkov 		unsigned v_idx =  err_sym * v_dim;
2432d34a6ecdSBorislav Petkov 		unsigned v_end = (err_sym + 1) * v_dim;
2433b1289d6fSDoug Thompson 
2434bfc04aecSBorislav Petkov 		/* walk over all 16 bits of the syndrome */
2435bfc04aecSBorislav Petkov 		for (i = 1; i < (1U << 16); i <<= 1) {
2436bfc04aecSBorislav Petkov 
2437bfc04aecSBorislav Petkov 			/* if bit is set in that eigenvector... */
2438bfc04aecSBorislav Petkov 			if (v_idx < v_end && vectors[v_idx] & i) {
2439bfc04aecSBorislav Petkov 				u16 ev_comp = vectors[v_idx++];
2440bfc04aecSBorislav Petkov 
2441bfc04aecSBorislav Petkov 				/* ... and bit set in the modified syndrome, */
2442bfc04aecSBorislav Petkov 				if (s & i) {
2443bfc04aecSBorislav Petkov 					/* remove it. */
2444bfc04aecSBorislav Petkov 					s ^= ev_comp;
2445bfc04aecSBorislav Petkov 
2446bfc04aecSBorislav Petkov 					if (!s)
2447bfc04aecSBorislav Petkov 						return err_sym;
2448bfc04aecSBorislav Petkov 				}
2449bfc04aecSBorislav Petkov 
2450bfc04aecSBorislav Petkov 			} else if (s & i)
2451bfc04aecSBorislav Petkov 				/* can't get to zero, move to next symbol */
2452bfc04aecSBorislav Petkov 				break;
2453bfc04aecSBorislav Petkov 		}
2454b1289d6fSDoug Thompson 	}
2455b1289d6fSDoug Thompson 
2456956b9ba1SJoe Perches 	edac_dbg(0, "syndrome(%x) not found\n", syndrome);
2457b1289d6fSDoug Thompson 	return -1;
2458b1289d6fSDoug Thompson }
2459d27bf6faSDoug Thompson 
2460bfc04aecSBorislav Petkov static int map_err_sym_to_channel(int err_sym, int sym_size)
2461bfc04aecSBorislav Petkov {
2462bfc04aecSBorislav Petkov 	if (sym_size == 4)
2463bfc04aecSBorislav Petkov 		switch (err_sym) {
2464bfc04aecSBorislav Petkov 		case 0x20:
2465bfc04aecSBorislav Petkov 		case 0x21:
2466bfc04aecSBorislav Petkov 			return 0;
2467bfc04aecSBorislav Petkov 		case 0x22:
2468bfc04aecSBorislav Petkov 		case 0x23:
2469bfc04aecSBorislav Petkov 			return 1;
2470bfc04aecSBorislav Petkov 		default:
2471bfc04aecSBorislav Petkov 			return err_sym >> 4;
2472bfc04aecSBorislav Petkov 		}
2473bfc04aecSBorislav Petkov 	/* x8 symbols */
2474bfc04aecSBorislav Petkov 	else
2475bfc04aecSBorislav Petkov 		switch (err_sym) {
2476bfc04aecSBorislav Petkov 		/* imaginary bits not in a DIMM */
2477bfc04aecSBorislav Petkov 		case 0x10:
2478bfc04aecSBorislav Petkov 			WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
2479bfc04aecSBorislav Petkov 					  err_sym);
2480bfc04aecSBorislav Petkov 			return -1;
2481bfc04aecSBorislav Petkov 		case 0x11:
2482bfc04aecSBorislav Petkov 			return 0;
2483bfc04aecSBorislav Petkov 		case 0x12:
2484bfc04aecSBorislav Petkov 			return 1;
2485bfc04aecSBorislav Petkov 		default:
2486bfc04aecSBorislav Petkov 			return err_sym >> 3;
2487bfc04aecSBorislav Petkov 		}
2488bfc04aecSBorislav Petkov 	return -1;
2489bfc04aecSBorislav Petkov }
2490bfc04aecSBorislav Petkov 
2491bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
2492bfc04aecSBorislav Petkov {
2493bfc04aecSBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
2494ad6a32e9SBorislav Petkov 	int err_sym = -1;
2495bfc04aecSBorislav Petkov 
2496a3b7db09SBorislav Petkov 	if (pvt->ecc_sym_sz == 8)
2497bfc04aecSBorislav Petkov 		err_sym = decode_syndrome(syndrome, x8_vectors,
2498ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x8_vectors),
2499a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
2500a3b7db09SBorislav Petkov 	else if (pvt->ecc_sym_sz == 4)
2501ad6a32e9SBorislav Petkov 		err_sym = decode_syndrome(syndrome, x4_vectors,
2502ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x4_vectors),
2503a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
2504ad6a32e9SBorislav Petkov 	else {
2505a3b7db09SBorislav Petkov 		amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz);
2506ad6a32e9SBorislav Petkov 		return err_sym;
2507bfc04aecSBorislav Petkov 	}
2508ad6a32e9SBorislav Petkov 
2509a3b7db09SBorislav Petkov 	return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz);
251041c31044SBorislav Petkov }
2511bfc04aecSBorislav Petkov 
2512e70984d9SYazen Ghannam static void __log_ecc_error(struct mem_ctl_info *mci, struct err_info *err,
251333ca0643SBorislav Petkov 			    u8 ecc_type)
2514d27bf6faSDoug Thompson {
251533ca0643SBorislav Petkov 	enum hw_event_mc_err_type err_type;
251633ca0643SBorislav Petkov 	const char *string;
2517d27bf6faSDoug Thompson 
251833ca0643SBorislav Petkov 	if (ecc_type == 2)
251933ca0643SBorislav Petkov 		err_type = HW_EVENT_ERR_CORRECTED;
252033ca0643SBorislav Petkov 	else if (ecc_type == 1)
252133ca0643SBorislav Petkov 		err_type = HW_EVENT_ERR_UNCORRECTED;
2522d12a969eSYazen Ghannam 	else if (ecc_type == 3)
2523d12a969eSYazen Ghannam 		err_type = HW_EVENT_ERR_DEFERRED;
252433ca0643SBorislav Petkov 	else {
252533ca0643SBorislav Petkov 		WARN(1, "Something is rotten in the state of Denmark.\n");
2526d27bf6faSDoug Thompson 		return;
2527d27bf6faSDoug Thompson 	}
2528d27bf6faSDoug Thompson 
252933ca0643SBorislav Petkov 	switch (err->err_code) {
253033ca0643SBorislav Petkov 	case DECODE_OK:
253133ca0643SBorislav Petkov 		string = "";
253233ca0643SBorislav Petkov 		break;
253333ca0643SBorislav Petkov 	case ERR_NODE:
253433ca0643SBorislav Petkov 		string = "Failed to map error addr to a node";
253533ca0643SBorislav Petkov 		break;
253633ca0643SBorislav Petkov 	case ERR_CSROW:
253733ca0643SBorislav Petkov 		string = "Failed to map error addr to a csrow";
253833ca0643SBorislav Petkov 		break;
253933ca0643SBorislav Petkov 	case ERR_CHANNEL:
2540713ad546SYazen Ghannam 		string = "Unknown syndrome - possible error reporting race";
2541713ad546SYazen Ghannam 		break;
2542713ad546SYazen Ghannam 	case ERR_SYND:
2543713ad546SYazen Ghannam 		string = "MCA_SYND not valid - unknown syndrome and csrow";
2544713ad546SYazen Ghannam 		break;
2545713ad546SYazen Ghannam 	case ERR_NORM_ADDR:
2546713ad546SYazen Ghannam 		string = "Cannot decode normalized address";
254733ca0643SBorislav Petkov 		break;
254833ca0643SBorislav Petkov 	default:
254933ca0643SBorislav Petkov 		string = "WTF error";
255033ca0643SBorislav Petkov 		break;
2551d27bf6faSDoug Thompson 	}
255233ca0643SBorislav Petkov 
255333ca0643SBorislav Petkov 	edac_mc_handle_error(err_type, mci, 1,
255433ca0643SBorislav Petkov 			     err->page, err->offset, err->syndrome,
255533ca0643SBorislav Petkov 			     err->csrow, err->channel, -1,
255633ca0643SBorislav Petkov 			     string, "");
2557d27bf6faSDoug Thompson }
2558d27bf6faSDoug Thompson 
2559df781d03SBorislav Petkov static inline void decode_bus_error(int node_id, struct mce *m)
2560d27bf6faSDoug Thompson {
25610c510cc8SDaniel J Blueman 	struct mem_ctl_info *mci;
25620c510cc8SDaniel J Blueman 	struct amd64_pvt *pvt;
2563f192c7b1SBorislav Petkov 	u8 ecc_type = (m->status >> 45) & 0x3;
256466fed2d4SBorislav Petkov 	u8 xec = XEC(m->status, 0x1f);
256566fed2d4SBorislav Petkov 	u16 ec = EC(m->status);
256633ca0643SBorislav Petkov 	u64 sys_addr;
256733ca0643SBorislav Petkov 	struct err_info err;
2568d27bf6faSDoug Thompson 
25690c510cc8SDaniel J Blueman 	mci = edac_mc_find(node_id);
25700c510cc8SDaniel J Blueman 	if (!mci)
25710c510cc8SDaniel J Blueman 		return;
25720c510cc8SDaniel J Blueman 
25730c510cc8SDaniel J Blueman 	pvt = mci->pvt_info;
25740c510cc8SDaniel J Blueman 
257566fed2d4SBorislav Petkov 	/* Bail out early if this was an 'observed' error */
25765980bb9cSBorislav Petkov 	if (PP(ec) == NBSL_PP_OBS)
2577b70ef010SBorislav Petkov 		return;
2578d27bf6faSDoug Thompson 
2579ecaf5606SBorislav Petkov 	/* Do only ECC errors */
2580ecaf5606SBorislav Petkov 	if (xec && xec != F10_NBSL_EXT_ERR_ECC)
2581d27bf6faSDoug Thompson 		return;
2582d27bf6faSDoug Thompson 
258333ca0643SBorislav Petkov 	memset(&err, 0, sizeof(err));
258433ca0643SBorislav Petkov 
2585a4b4bedcSBorislav Petkov 	sys_addr = get_error_address(pvt, m);
258633ca0643SBorislav Petkov 
2587ecaf5606SBorislav Petkov 	if (ecc_type == 2)
258833ca0643SBorislav Petkov 		err.syndrome = extract_syndrome(m->status);
258933ca0643SBorislav Petkov 
259033ca0643SBorislav Petkov 	pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err);
259133ca0643SBorislav Petkov 
2592e70984d9SYazen Ghannam 	__log_ecc_error(mci, &err, ecc_type);
2593d27bf6faSDoug Thompson }
2594d27bf6faSDoug Thompson 
25950ec449eeSDoug Thompson /*
2596713ad546SYazen Ghannam  * To find the UMC channel represented by this bank we need to match on its
2597713ad546SYazen Ghannam  * instance_id. The instance_id of a bank is held in the lower 32 bits of its
2598713ad546SYazen Ghannam  * IPID.
2599bdcee774SYazen Ghannam  *
2600bdcee774SYazen Ghannam  * Currently, we can derive the channel number by looking at the 6th nibble in
2601bdcee774SYazen Ghannam  * the instance_id. For example, instance_id=0xYXXXXX where Y is the channel
2602bdcee774SYazen Ghannam  * number.
2603713ad546SYazen Ghannam  */
2604bdcee774SYazen Ghannam static int find_umc_channel(struct mce *m)
2605713ad546SYazen Ghannam {
2606bdcee774SYazen Ghannam 	return (m->ipid & GENMASK(31, 0)) >> 20;
2607713ad546SYazen Ghannam }
2608713ad546SYazen Ghannam 
2609713ad546SYazen Ghannam static void decode_umc_error(int node_id, struct mce *m)
2610713ad546SYazen Ghannam {
2611713ad546SYazen Ghannam 	u8 ecc_type = (m->status >> 45) & 0x3;
2612713ad546SYazen Ghannam 	struct mem_ctl_info *mci;
2613713ad546SYazen Ghannam 	struct amd64_pvt *pvt;
2614713ad546SYazen Ghannam 	struct err_info err;
2615713ad546SYazen Ghannam 	u64 sys_addr;
2616713ad546SYazen Ghannam 
2617713ad546SYazen Ghannam 	mci = edac_mc_find(node_id);
2618713ad546SYazen Ghannam 	if (!mci)
2619713ad546SYazen Ghannam 		return;
2620713ad546SYazen Ghannam 
2621713ad546SYazen Ghannam 	pvt = mci->pvt_info;
2622713ad546SYazen Ghannam 
2623713ad546SYazen Ghannam 	memset(&err, 0, sizeof(err));
2624713ad546SYazen Ghannam 
2625713ad546SYazen Ghannam 	if (m->status & MCI_STATUS_DEFERRED)
2626713ad546SYazen Ghannam 		ecc_type = 3;
2627713ad546SYazen Ghannam 
2628bdcee774SYazen Ghannam 	err.channel = find_umc_channel(m);
2629713ad546SYazen Ghannam 
2630713ad546SYazen Ghannam 	if (!(m->status & MCI_STATUS_SYNDV)) {
2631713ad546SYazen Ghannam 		err.err_code = ERR_SYND;
2632713ad546SYazen Ghannam 		goto log_error;
2633713ad546SYazen Ghannam 	}
2634713ad546SYazen Ghannam 
2635713ad546SYazen Ghannam 	if (ecc_type == 2) {
2636713ad546SYazen Ghannam 		u8 length = (m->synd >> 18) & 0x3f;
2637713ad546SYazen Ghannam 
2638713ad546SYazen Ghannam 		if (length)
2639713ad546SYazen Ghannam 			err.syndrome = (m->synd >> 32) & GENMASK(length - 1, 0);
2640713ad546SYazen Ghannam 		else
2641713ad546SYazen Ghannam 			err.err_code = ERR_CHANNEL;
2642713ad546SYazen Ghannam 	}
2643713ad546SYazen Ghannam 
2644713ad546SYazen Ghannam 	err.csrow = m->synd & 0x7;
2645713ad546SYazen Ghannam 
26468a2eaab7SYazen Ghannam 	if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) {
26478a2eaab7SYazen Ghannam 		err.err_code = ERR_NORM_ADDR;
26488a2eaab7SYazen Ghannam 		goto log_error;
26498a2eaab7SYazen Ghannam 	}
26508a2eaab7SYazen Ghannam 
26518a2eaab7SYazen Ghannam 	error_address_to_page_and_offset(sys_addr, &err);
26528a2eaab7SYazen Ghannam 
2653713ad546SYazen Ghannam log_error:
2654713ad546SYazen Ghannam 	__log_ecc_error(mci, &err, ecc_type);
2655713ad546SYazen Ghannam }
2656713ad546SYazen Ghannam 
2657713ad546SYazen Ghannam /*
26583f37a36bSBorislav Petkov  * Use pvt->F3 which contains the F3 CPU PCI device to get the related
26593f37a36bSBorislav Petkov  * F1 (AddrMap) and F2 (Dct) devices. Return negative value on error.
2660936fc3afSYazen Ghannam  * Reserve F0 and F6 on systems with a UMC.
26610ec449eeSDoug Thompson  */
2662936fc3afSYazen Ghannam static int
2663936fc3afSYazen Ghannam reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2)
26640ec449eeSDoug Thompson {
2665936fc3afSYazen Ghannam 	if (pvt->umc) {
2666936fc3afSYazen Ghannam 		pvt->F0 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
2667936fc3afSYazen Ghannam 		if (!pvt->F0) {
26685246c540SBorislav Petkov 			amd64_err("F0 not found, device 0x%x (broken BIOS?)\n", pci_id1);
2669936fc3afSYazen Ghannam 			return -ENODEV;
2670936fc3afSYazen Ghannam 		}
2671936fc3afSYazen Ghannam 
2672936fc3afSYazen Ghannam 		pvt->F6 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3);
2673936fc3afSYazen Ghannam 		if (!pvt->F6) {
2674936fc3afSYazen Ghannam 			pci_dev_put(pvt->F0);
2675936fc3afSYazen Ghannam 			pvt->F0 = NULL;
2676936fc3afSYazen Ghannam 
26775246c540SBorislav Petkov 			amd64_err("F6 not found: device 0x%x (broken BIOS?)\n", pci_id2);
2678936fc3afSYazen Ghannam 			return -ENODEV;
2679936fc3afSYazen Ghannam 		}
26805246c540SBorislav Petkov 
2681706657b1SBorislav Petkov 		if (!pci_ctl_dev)
2682706657b1SBorislav Petkov 			pci_ctl_dev = &pvt->F0->dev;
2683706657b1SBorislav Petkov 
2684936fc3afSYazen Ghannam 		edac_dbg(1, "F0: %s\n", pci_name(pvt->F0));
2685936fc3afSYazen Ghannam 		edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
2686936fc3afSYazen Ghannam 		edac_dbg(1, "F6: %s\n", pci_name(pvt->F6));
2687936fc3afSYazen Ghannam 
2688936fc3afSYazen Ghannam 		return 0;
2689936fc3afSYazen Ghannam 	}
2690936fc3afSYazen Ghannam 
26910ec449eeSDoug Thompson 	/* Reserve the ADDRESS MAP Device */
2692936fc3afSYazen Ghannam 	pvt->F1 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
26938d5b5d9cSBorislav Petkov 	if (!pvt->F1) {
26945246c540SBorislav Petkov 		amd64_err("F1 not found: device 0x%x (broken BIOS?)\n", pci_id1);
2695bbd0c1f6SBorislav Petkov 		return -ENODEV;
26960ec449eeSDoug Thompson 	}
26970ec449eeSDoug Thompson 
26983f37a36bSBorislav Petkov 	/* Reserve the DCT Device */
2699936fc3afSYazen Ghannam 	pvt->F2 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3);
27003f37a36bSBorislav Petkov 	if (!pvt->F2) {
27018d5b5d9cSBorislav Petkov 		pci_dev_put(pvt->F1);
27028d5b5d9cSBorislav Petkov 		pvt->F1 = NULL;
27030ec449eeSDoug Thompson 
27045246c540SBorislav Petkov 		amd64_err("F2 not found: device 0x%x (broken BIOS?)\n", pci_id2);
2705bbd0c1f6SBorislav Petkov 		return -ENODEV;
27060ec449eeSDoug Thompson 	}
2707936fc3afSYazen Ghannam 
2708706657b1SBorislav Petkov 	if (!pci_ctl_dev)
2709706657b1SBorislav Petkov 		pci_ctl_dev = &pvt->F2->dev;
2710706657b1SBorislav Petkov 
2711956b9ba1SJoe Perches 	edac_dbg(1, "F1: %s\n", pci_name(pvt->F1));
2712956b9ba1SJoe Perches 	edac_dbg(1, "F2: %s\n", pci_name(pvt->F2));
2713956b9ba1SJoe Perches 	edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
27140ec449eeSDoug Thompson 
27150ec449eeSDoug Thompson 	return 0;
27160ec449eeSDoug Thompson }
27170ec449eeSDoug Thompson 
2718360b7f3cSBorislav Petkov static void free_mc_sibling_devs(struct amd64_pvt *pvt)
27190ec449eeSDoug Thompson {
2720936fc3afSYazen Ghannam 	if (pvt->umc) {
2721936fc3afSYazen Ghannam 		pci_dev_put(pvt->F0);
2722936fc3afSYazen Ghannam 		pci_dev_put(pvt->F6);
2723936fc3afSYazen Ghannam 	} else {
27248d5b5d9cSBorislav Petkov 		pci_dev_put(pvt->F1);
27253f37a36bSBorislav Petkov 		pci_dev_put(pvt->F2);
27260ec449eeSDoug Thompson 	}
2727936fc3afSYazen Ghannam }
27280ec449eeSDoug Thompson 
2729b64ce7cdSYazen Ghannam static void determine_ecc_sym_sz(struct amd64_pvt *pvt)
2730b64ce7cdSYazen Ghannam {
2731b64ce7cdSYazen Ghannam 	pvt->ecc_sym_sz = 4;
2732b64ce7cdSYazen Ghannam 
2733b64ce7cdSYazen Ghannam 	if (pvt->umc) {
2734b64ce7cdSYazen Ghannam 		u8 i;
2735b64ce7cdSYazen Ghannam 
27364d30d2bcSYazen Ghannam 		for_each_umc(i) {
2737b64ce7cdSYazen Ghannam 			/* Check enabled channels only: */
27387835961dSYazen Ghannam 			if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) {
27397835961dSYazen Ghannam 				if (pvt->umc[i].ecc_ctrl & BIT(9)) {
27407835961dSYazen Ghannam 					pvt->ecc_sym_sz = 16;
27417835961dSYazen Ghannam 					return;
27427835961dSYazen Ghannam 				} else if (pvt->umc[i].ecc_ctrl & BIT(7)) {
2743b64ce7cdSYazen Ghannam 					pvt->ecc_sym_sz = 8;
2744b64ce7cdSYazen Ghannam 					return;
2745b64ce7cdSYazen Ghannam 				}
27467835961dSYazen Ghannam 			}
27477835961dSYazen Ghannam 		}
27487835961dSYazen Ghannam 	} else if (pvt->fam >= 0x10) {
2749b64ce7cdSYazen Ghannam 		u32 tmp;
2750b64ce7cdSYazen Ghannam 
2751b64ce7cdSYazen Ghannam 		amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp);
2752b64ce7cdSYazen Ghannam 		/* F16h has only DCT0, so no need to read dbam1. */
2753b64ce7cdSYazen Ghannam 		if (pvt->fam != 0x16)
2754b64ce7cdSYazen Ghannam 			amd64_read_dct_pci_cfg(pvt, 1, DBAM0, &pvt->dbam1);
2755b64ce7cdSYazen Ghannam 
2756b64ce7cdSYazen Ghannam 		/* F10h, revD and later can do x8 ECC too. */
2757b64ce7cdSYazen Ghannam 		if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25))
2758b64ce7cdSYazen Ghannam 			pvt->ecc_sym_sz = 8;
2759b64ce7cdSYazen Ghannam 	}
2760b64ce7cdSYazen Ghannam }
2761b64ce7cdSYazen Ghannam 
2762b64ce7cdSYazen Ghannam /*
2763b64ce7cdSYazen Ghannam  * Retrieve the hardware registers of the memory controller.
2764b64ce7cdSYazen Ghannam  */
2765b64ce7cdSYazen Ghannam static void __read_mc_regs_df(struct amd64_pvt *pvt)
2766b64ce7cdSYazen Ghannam {
2767b64ce7cdSYazen Ghannam 	u8 nid = pvt->mc_node_id;
2768b64ce7cdSYazen Ghannam 	struct amd64_umc *umc;
2769b64ce7cdSYazen Ghannam 	u32 i, umc_base;
2770b64ce7cdSYazen Ghannam 
2771b64ce7cdSYazen Ghannam 	/* Read registers from each UMC */
27724d30d2bcSYazen Ghannam 	for_each_umc(i) {
2773b64ce7cdSYazen Ghannam 
2774b64ce7cdSYazen Ghannam 		umc_base = get_umc_base(i);
2775b64ce7cdSYazen Ghannam 		umc = &pvt->umc[i];
2776b64ce7cdSYazen Ghannam 
277707ed82efSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_DIMM_CFG, &umc->dimm_cfg);
277807ed82efSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_UMC_CFG, &umc->umc_cfg);
2779b64ce7cdSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_SDP_CTRL, &umc->sdp_ctrl);
2780b64ce7cdSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_ECC_CTRL, &umc->ecc_ctrl);
278107ed82efSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_UMC_CAP_HI, &umc->umc_cap_hi);
2782b64ce7cdSYazen Ghannam 	}
2783b64ce7cdSYazen Ghannam }
2784b64ce7cdSYazen Ghannam 
27850ec449eeSDoug Thompson /*
27860ec449eeSDoug Thompson  * Retrieve the hardware registers of the memory controller (this includes the
27870ec449eeSDoug Thompson  * 'Address Map' and 'Misc' device regs)
27880ec449eeSDoug Thompson  */
2789360b7f3cSBorislav Petkov static void read_mc_regs(struct amd64_pvt *pvt)
27900ec449eeSDoug Thompson {
2791b64ce7cdSYazen Ghannam 	unsigned int range;
27920ec449eeSDoug Thompson 	u64 msr_val;
27930ec449eeSDoug Thompson 
27940ec449eeSDoug Thompson 	/*
27950ec449eeSDoug Thompson 	 * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
2796b64ce7cdSYazen Ghannam 	 * those are Read-As-Zero.
27970ec449eeSDoug Thompson 	 */
2798e97f8bb8SBorislav Petkov 	rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
2799956b9ba1SJoe Perches 	edac_dbg(0, "  TOP_MEM:  0x%016llx\n", pvt->top_mem);
28000ec449eeSDoug Thompson 
2801b64ce7cdSYazen Ghannam 	/* Check first whether TOP_MEM2 is enabled: */
28020ec449eeSDoug Thompson 	rdmsrl(MSR_K8_SYSCFG, msr_val);
2803b64ce7cdSYazen Ghannam 	if (msr_val & BIT(21)) {
2804e97f8bb8SBorislav Petkov 		rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
2805956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
2806b64ce7cdSYazen Ghannam 	} else {
2807956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2 disabled\n");
2808b64ce7cdSYazen Ghannam 	}
2809b64ce7cdSYazen Ghannam 
2810b64ce7cdSYazen Ghannam 	if (pvt->umc) {
2811b64ce7cdSYazen Ghannam 		__read_mc_regs_df(pvt);
2812b64ce7cdSYazen Ghannam 		amd64_read_pci_cfg(pvt->F0, DF_DHAR, &pvt->dhar);
2813b64ce7cdSYazen Ghannam 
2814b64ce7cdSYazen Ghannam 		goto skip;
2815b64ce7cdSYazen Ghannam 	}
28160ec449eeSDoug Thompson 
28175980bb9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap);
28180ec449eeSDoug Thompson 
28195a5d2371SBorislav Petkov 	read_dram_ctl_register(pvt);
28200ec449eeSDoug Thompson 
28217f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
28227f19bf75SBorislav Petkov 		u8 rw;
28230ec449eeSDoug Thompson 
28247f19bf75SBorislav Petkov 		/* read settings for this DRAM range */
28257f19bf75SBorislav Petkov 		read_dram_base_limit_regs(pvt, range);
2826e97f8bb8SBorislav Petkov 
28277f19bf75SBorislav Petkov 		rw = dram_rw(pvt, range);
28287f19bf75SBorislav Petkov 		if (!rw)
28297f19bf75SBorislav Petkov 			continue;
28307f19bf75SBorislav Petkov 
2831956b9ba1SJoe Perches 		edac_dbg(1, "  DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
28327f19bf75SBorislav Petkov 			 range,
28337f19bf75SBorislav Petkov 			 get_dram_base(pvt, range),
28347f19bf75SBorislav Petkov 			 get_dram_limit(pvt, range));
28357f19bf75SBorislav Petkov 
2836956b9ba1SJoe Perches 		edac_dbg(1, "   IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
28377f19bf75SBorislav Petkov 			 dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
28387f19bf75SBorislav Petkov 			 (rw & 0x1) ? "R" : "-",
28397f19bf75SBorislav Petkov 			 (rw & 0x2) ? "W" : "-",
28407f19bf75SBorislav Petkov 			 dram_intlv_sel(pvt, range),
28417f19bf75SBorislav Petkov 			 dram_dst_node(pvt, range));
28420ec449eeSDoug Thompson 	}
28430ec449eeSDoug Thompson 
2844bc21fa57SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar);
28457981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DBAM0, &pvt->dbam0);
28460ec449eeSDoug Thompson 
28478d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare);
28480ec449eeSDoug Thompson 
28497981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DCLR0, &pvt->dclr0);
28507981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DCHR0, &pvt->dchr0);
28510ec449eeSDoug Thompson 
285278da121eSBorislav Petkov 	if (!dct_ganging_enabled(pvt)) {
28537981a28fSAravind Gopalakrishnan 		amd64_read_dct_pci_cfg(pvt, 1, DCLR0, &pvt->dclr1);
28547981a28fSAravind Gopalakrishnan 		amd64_read_dct_pci_cfg(pvt, 1, DCHR0, &pvt->dchr1);
28550ec449eeSDoug Thompson 	}
2856b2b0c605SBorislav Petkov 
2857b64ce7cdSYazen Ghannam skip:
2858b64ce7cdSYazen Ghannam 	read_dct_base_mask(pvt);
2859b64ce7cdSYazen Ghannam 
2860a597d2a5SAravind Gopalakrishnan 	determine_memory_type(pvt);
2861a597d2a5SAravind Gopalakrishnan 	edac_dbg(1, "  DIMM type: %s\n", edac_mem_types[pvt->dram_type]);
2862a3b7db09SBorislav Petkov 
2863b64ce7cdSYazen Ghannam 	determine_ecc_sym_sz(pvt);
28640ec449eeSDoug Thompson }
28650ec449eeSDoug Thompson 
28660ec449eeSDoug Thompson /*
28670ec449eeSDoug Thompson  * NOTE: CPU Revision Dependent code
28680ec449eeSDoug Thompson  *
28690ec449eeSDoug Thompson  * Input:
287011c75eadSBorislav Petkov  *	@csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1)
28710ec449eeSDoug Thompson  *	k8 private pointer to -->
28720ec449eeSDoug Thompson  *			DRAM Bank Address mapping register
28730ec449eeSDoug Thompson  *			node_id
28740ec449eeSDoug Thompson  *			DCL register where dual_channel_active is
28750ec449eeSDoug Thompson  *
28760ec449eeSDoug Thompson  * The DBAM register consists of 4 sets of 4 bits each definitions:
28770ec449eeSDoug Thompson  *
28780ec449eeSDoug Thompson  * Bits:	CSROWs
28790ec449eeSDoug Thompson  * 0-3		CSROWs 0 and 1
28800ec449eeSDoug Thompson  * 4-7		CSROWs 2 and 3
28810ec449eeSDoug Thompson  * 8-11		CSROWs 4 and 5
28820ec449eeSDoug Thompson  * 12-15	CSROWs 6 and 7
28830ec449eeSDoug Thompson  *
28840ec449eeSDoug Thompson  * Values range from: 0 to 15
28850ec449eeSDoug Thompson  * The meaning of the values depends on CPU revision and dual-channel state,
28860ec449eeSDoug Thompson  * see relevant BKDG more info.
28870ec449eeSDoug Thompson  *
28880ec449eeSDoug Thompson  * The memory controller provides for total of only 8 CSROWs in its current
28890ec449eeSDoug Thompson  * architecture. Each "pair" of CSROWs normally represents just one DIMM in
28900ec449eeSDoug Thompson  * single channel or two (2) DIMMs in dual channel mode.
28910ec449eeSDoug Thompson  *
28920ec449eeSDoug Thompson  * The following code logic collapses the various tables for CSROW based on CPU
28930ec449eeSDoug Thompson  * revision.
28940ec449eeSDoug Thompson  *
28950ec449eeSDoug Thompson  * Returns:
28960ec449eeSDoug Thompson  *	The number of PAGE_SIZE pages on the specified CSROW number it
28970ec449eeSDoug Thompson  *	encompasses
28980ec449eeSDoug Thompson  *
28990ec449eeSDoug Thompson  */
2900eb77e6b8SYazen Ghannam static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr_orig)
29010ec449eeSDoug Thompson {
2902f92cae45SAshish Shenoy 	u32 dbam = dct ? pvt->dbam1 : pvt->dbam0;
2903eb77e6b8SYazen Ghannam 	int csrow_nr = csrow_nr_orig;
2904eb77e6b8SYazen Ghannam 	u32 cs_mode, nr_pages;
29050ec449eeSDoug Thompson 
2906e53a3b26SYazen Ghannam 	if (!pvt->umc) {
2907eb77e6b8SYazen Ghannam 		csrow_nr >>= 1;
2908eb77e6b8SYazen Ghannam 		cs_mode = DBAM_DIMM(csrow_nr, dbam);
2909e53a3b26SYazen Ghannam 	} else {
2910e53a3b26SYazen Ghannam 		cs_mode = f17_get_cs_mode(csrow_nr >> 1, dct, pvt);
2911e53a3b26SYazen Ghannam 	}
29120ec449eeSDoug Thompson 
2913eb77e6b8SYazen Ghannam 	nr_pages   = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, csrow_nr);
2914eb77e6b8SYazen Ghannam 	nr_pages <<= 20 - PAGE_SHIFT;
29150ec449eeSDoug Thompson 
291610de6497SBorislav Petkov 	edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n",
2917eb77e6b8SYazen Ghannam 		    csrow_nr_orig, dct,  cs_mode);
291810de6497SBorislav Petkov 	edac_dbg(0, "nr_pages/channel: %u\n", nr_pages);
29190ec449eeSDoug Thompson 
29200ec449eeSDoug Thompson 	return nr_pages;
29210ec449eeSDoug Thompson }
29220ec449eeSDoug Thompson 
2923353a1fcbSYazen Ghannam static int init_csrows_df(struct mem_ctl_info *mci)
2924353a1fcbSYazen Ghannam {
2925353a1fcbSYazen Ghannam 	struct amd64_pvt *pvt = mci->pvt_info;
2926353a1fcbSYazen Ghannam 	enum edac_type edac_mode = EDAC_NONE;
2927353a1fcbSYazen Ghannam 	enum dev_type dev_type = DEV_UNKNOWN;
2928353a1fcbSYazen Ghannam 	struct dimm_info *dimm;
2929353a1fcbSYazen Ghannam 	int empty = 1;
2930353a1fcbSYazen Ghannam 	u8 umc, cs;
2931353a1fcbSYazen Ghannam 
2932353a1fcbSYazen Ghannam 	if (mci->edac_ctl_cap & EDAC_FLAG_S16ECD16ED) {
2933353a1fcbSYazen Ghannam 		edac_mode = EDAC_S16ECD16ED;
2934353a1fcbSYazen Ghannam 		dev_type = DEV_X16;
2935353a1fcbSYazen Ghannam 	} else if (mci->edac_ctl_cap & EDAC_FLAG_S8ECD8ED) {
2936353a1fcbSYazen Ghannam 		edac_mode = EDAC_S8ECD8ED;
2937353a1fcbSYazen Ghannam 		dev_type = DEV_X8;
2938353a1fcbSYazen Ghannam 	} else if (mci->edac_ctl_cap & EDAC_FLAG_S4ECD4ED) {
2939353a1fcbSYazen Ghannam 		edac_mode = EDAC_S4ECD4ED;
2940353a1fcbSYazen Ghannam 		dev_type = DEV_X4;
2941353a1fcbSYazen Ghannam 	} else if (mci->edac_ctl_cap & EDAC_FLAG_SECDED) {
2942353a1fcbSYazen Ghannam 		edac_mode = EDAC_SECDED;
2943353a1fcbSYazen Ghannam 	}
2944353a1fcbSYazen Ghannam 
2945353a1fcbSYazen Ghannam 	for_each_umc(umc) {
2946353a1fcbSYazen Ghannam 		for_each_chip_select(cs, umc, pvt) {
2947353a1fcbSYazen Ghannam 			if (!csrow_enabled(cs, umc, pvt))
2948353a1fcbSYazen Ghannam 				continue;
2949353a1fcbSYazen Ghannam 
2950353a1fcbSYazen Ghannam 			empty = 0;
2951353a1fcbSYazen Ghannam 			dimm = mci->csrows[cs]->channels[umc]->dimm;
2952353a1fcbSYazen Ghannam 
2953353a1fcbSYazen Ghannam 			edac_dbg(1, "MC node: %d, csrow: %d\n",
2954353a1fcbSYazen Ghannam 					pvt->mc_node_id, cs);
2955353a1fcbSYazen Ghannam 
2956353a1fcbSYazen Ghannam 			dimm->nr_pages = get_csrow_nr_pages(pvt, umc, cs);
2957353a1fcbSYazen Ghannam 			dimm->mtype = pvt->dram_type;
2958353a1fcbSYazen Ghannam 			dimm->edac_mode = edac_mode;
2959353a1fcbSYazen Ghannam 			dimm->dtype = dev_type;
2960466503d6SYazen Ghannam 			dimm->grain = 64;
2961353a1fcbSYazen Ghannam 		}
2962353a1fcbSYazen Ghannam 	}
2963353a1fcbSYazen Ghannam 
2964353a1fcbSYazen Ghannam 	return empty;
2965353a1fcbSYazen Ghannam }
2966353a1fcbSYazen Ghannam 
29670ec449eeSDoug Thompson /*
29680ec449eeSDoug Thompson  * Initialize the array of csrow attribute instances, based on the values
29690ec449eeSDoug Thompson  * from pci config hardware registers.
29700ec449eeSDoug Thompson  */
2971360b7f3cSBorislav Petkov static int init_csrows(struct mem_ctl_info *mci)
29720ec449eeSDoug Thompson {
297310de6497SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
29742d09d8f3SYazen Ghannam 	enum edac_type edac_mode = EDAC_NONE;
29750ec449eeSDoug Thompson 	struct csrow_info *csrow;
2976de3910ebSMauro Carvalho Chehab 	struct dimm_info *dimm;
297710de6497SBorislav Petkov 	int i, j, empty = 1;
2978a895bf8bSMauro Carvalho Chehab 	int nr_pages = 0;
297910de6497SBorislav Petkov 	u32 val;
29800ec449eeSDoug Thompson 
2981353a1fcbSYazen Ghannam 	if (pvt->umc)
2982353a1fcbSYazen Ghannam 		return init_csrows_df(mci);
2983353a1fcbSYazen Ghannam 
2984a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
29850ec449eeSDoug Thompson 
29862299ef71SBorislav Petkov 	pvt->nbcfg = val;
29870ec449eeSDoug Thompson 
2988956b9ba1SJoe Perches 	edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
29892299ef71SBorislav Petkov 		 pvt->mc_node_id, val,
2990a97fa68eSBorislav Petkov 		 !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
29910ec449eeSDoug Thompson 
299210de6497SBorislav Petkov 	/*
299310de6497SBorislav Petkov 	 * We iterate over DCT0 here but we look at DCT1 in parallel, if needed.
299410de6497SBorislav Petkov 	 */
299511c75eadSBorislav Petkov 	for_each_chip_select(i, 0, pvt) {
299610de6497SBorislav Petkov 		bool row_dct0 = !!csrow_enabled(i, 0, pvt);
299710de6497SBorislav Petkov 		bool row_dct1 = false;
29980ec449eeSDoug Thompson 
2999a4b4bedcSBorislav Petkov 		if (pvt->fam != 0xf)
300010de6497SBorislav Petkov 			row_dct1 = !!csrow_enabled(i, 1, pvt);
300110de6497SBorislav Petkov 
300210de6497SBorislav Petkov 		if (!row_dct0 && !row_dct1)
30030ec449eeSDoug Thompson 			continue;
30040ec449eeSDoug Thompson 
300510de6497SBorislav Petkov 		csrow = mci->csrows[i];
30060ec449eeSDoug Thompson 		empty = 0;
300711c75eadSBorislav Petkov 
300810de6497SBorislav Petkov 		edac_dbg(1, "MC node: %d, csrow: %d\n",
300910de6497SBorislav Petkov 			    pvt->mc_node_id, i);
301010de6497SBorislav Petkov 
30111eef1282SMauro Carvalho Chehab 		if (row_dct0) {
3012d1ea71cdSBorislav Petkov 			nr_pages = get_csrow_nr_pages(pvt, 0, i);
30131eef1282SMauro Carvalho Chehab 			csrow->channels[0]->dimm->nr_pages = nr_pages;
30141eef1282SMauro Carvalho Chehab 		}
301510de6497SBorislav Petkov 
301610de6497SBorislav Petkov 		/* K8 has only one DCT */
3017a4b4bedcSBorislav Petkov 		if (pvt->fam != 0xf && row_dct1) {
3018d1ea71cdSBorislav Petkov 			int row_dct1_pages = get_csrow_nr_pages(pvt, 1, i);
30191eef1282SMauro Carvalho Chehab 
30201eef1282SMauro Carvalho Chehab 			csrow->channels[1]->dimm->nr_pages = row_dct1_pages;
30211eef1282SMauro Carvalho Chehab 			nr_pages += row_dct1_pages;
30221eef1282SMauro Carvalho Chehab 		}
30230ec449eeSDoug Thompson 
302410de6497SBorislav Petkov 		edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages);
30250ec449eeSDoug Thompson 
30262d09d8f3SYazen Ghannam 		/* Determine DIMM ECC mode: */
3027353a1fcbSYazen Ghannam 		if (pvt->nbcfg & NBCFG_ECC_ENABLE) {
30282d09d8f3SYazen Ghannam 			edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL)
30292d09d8f3SYazen Ghannam 					? EDAC_S4ECD4ED
30302d09d8f3SYazen Ghannam 					: EDAC_SECDED;
30312d09d8f3SYazen Ghannam 		}
3032084a4fccSMauro Carvalho Chehab 
3033084a4fccSMauro Carvalho Chehab 		for (j = 0; j < pvt->channel_count; j++) {
3034de3910ebSMauro Carvalho Chehab 			dimm = csrow->channels[j]->dimm;
3035a597d2a5SAravind Gopalakrishnan 			dimm->mtype = pvt->dram_type;
3036de3910ebSMauro Carvalho Chehab 			dimm->edac_mode = edac_mode;
3037466503d6SYazen Ghannam 			dimm->grain = 64;
3038084a4fccSMauro Carvalho Chehab 		}
30390ec449eeSDoug Thompson 	}
30400ec449eeSDoug Thompson 
30410ec449eeSDoug Thompson 	return empty;
30420ec449eeSDoug Thompson }
3043d27bf6faSDoug Thompson 
304406724535SBorislav Petkov /* get all cores on this DCT */
30458b84c8dfSDaniel J Blueman static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, u16 nid)
3046f9431992SDoug Thompson {
304706724535SBorislav Petkov 	int cpu;
3048f9431992SDoug Thompson 
304906724535SBorislav Petkov 	for_each_online_cpu(cpu)
3050db970bd2SYazen Ghannam 		if (topology_die_id(cpu) == nid)
305106724535SBorislav Petkov 			cpumask_set_cpu(cpu, mask);
3052f9431992SDoug Thompson }
3053f9431992SDoug Thompson 
3054f9431992SDoug Thompson /* check MCG_CTL on all the cpus on this node */
3055d1ea71cdSBorislav Petkov static bool nb_mce_bank_enabled_on_node(u16 nid)
3056f9431992SDoug Thompson {
3057ba578cb3SRusty Russell 	cpumask_var_t mask;
305850542251SBorislav Petkov 	int cpu, nbe;
305906724535SBorislav Petkov 	bool ret = false;
3060f9431992SDoug Thompson 
3061ba578cb3SRusty Russell 	if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
306224f9a7feSBorislav Petkov 		amd64_warn("%s: Error allocating mask\n", __func__);
306306724535SBorislav Petkov 		return false;
306406724535SBorislav Petkov 	}
306506724535SBorislav Petkov 
3066ba578cb3SRusty Russell 	get_cpus_on_this_dct_cpumask(mask, nid);
306706724535SBorislav Petkov 
3068ba578cb3SRusty Russell 	rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
3069ba578cb3SRusty Russell 
3070ba578cb3SRusty Russell 	for_each_cpu(cpu, mask) {
307150542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
30725980bb9cSBorislav Petkov 		nbe = reg->l & MSR_MCGCTL_NBE;
307306724535SBorislav Petkov 
3074956b9ba1SJoe Perches 		edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
307550542251SBorislav Petkov 			 cpu, reg->q,
307606724535SBorislav Petkov 			 (nbe ? "enabled" : "disabled"));
307706724535SBorislav Petkov 
307806724535SBorislav Petkov 		if (!nbe)
307906724535SBorislav Petkov 			goto out;
308006724535SBorislav Petkov 	}
308106724535SBorislav Petkov 	ret = true;
308206724535SBorislav Petkov 
308306724535SBorislav Petkov out:
3084ba578cb3SRusty Russell 	free_cpumask_var(mask);
3085f9431992SDoug Thompson 	return ret;
3086f9431992SDoug Thompson }
3087f9431992SDoug Thompson 
3088c7e5301aSDaniel J Blueman static int toggle_ecc_err_reporting(struct ecc_settings *s, u16 nid, bool on)
3089f6d6ae96SBorislav Petkov {
3090f6d6ae96SBorislav Petkov 	cpumask_var_t cmask;
309150542251SBorislav Petkov 	int cpu;
3092f6d6ae96SBorislav Petkov 
3093f6d6ae96SBorislav Petkov 	if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
309424f9a7feSBorislav Petkov 		amd64_warn("%s: error allocating mask\n", __func__);
30950de27884SPan Bian 		return -ENOMEM;
3096f6d6ae96SBorislav Petkov 	}
3097f6d6ae96SBorislav Petkov 
3098ae7bb7c6SBorislav Petkov 	get_cpus_on_this_dct_cpumask(cmask, nid);
3099f6d6ae96SBorislav Petkov 
3100f6d6ae96SBorislav Petkov 	rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
3101f6d6ae96SBorislav Petkov 
3102f6d6ae96SBorislav Petkov 	for_each_cpu(cpu, cmask) {
3103f6d6ae96SBorislav Petkov 
310450542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
310550542251SBorislav Petkov 
3106f6d6ae96SBorislav Petkov 		if (on) {
31075980bb9cSBorislav Petkov 			if (reg->l & MSR_MCGCTL_NBE)
3108ae7bb7c6SBorislav Petkov 				s->flags.nb_mce_enable = 1;
3109f6d6ae96SBorislav Petkov 
31105980bb9cSBorislav Petkov 			reg->l |= MSR_MCGCTL_NBE;
3111f6d6ae96SBorislav Petkov 		} else {
3112f6d6ae96SBorislav Petkov 			/*
3113d95cf4deSBorislav Petkov 			 * Turn off NB MCE reporting only when it was off before
3114f6d6ae96SBorislav Petkov 			 */
3115ae7bb7c6SBorislav Petkov 			if (!s->flags.nb_mce_enable)
31165980bb9cSBorislav Petkov 				reg->l &= ~MSR_MCGCTL_NBE;
3117f6d6ae96SBorislav Petkov 		}
3118f6d6ae96SBorislav Petkov 	}
3119f6d6ae96SBorislav Petkov 	wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
3120f6d6ae96SBorislav Petkov 
3121f6d6ae96SBorislav Petkov 	free_cpumask_var(cmask);
3122f6d6ae96SBorislav Petkov 
3123f6d6ae96SBorislav Petkov 	return 0;
3124f6d6ae96SBorislav Petkov }
3125f6d6ae96SBorislav Petkov 
3126c7e5301aSDaniel J Blueman static bool enable_ecc_error_reporting(struct ecc_settings *s, u16 nid,
31272299ef71SBorislav Petkov 				       struct pci_dev *F3)
3128f6d6ae96SBorislav Petkov {
31292299ef71SBorislav Petkov 	bool ret = true;
3130c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
3131f6d6ae96SBorislav Petkov 
31322299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, ON)) {
31332299ef71SBorislav Petkov 		amd64_warn("Error enabling ECC reporting over MCGCTL!\n");
31342299ef71SBorislav Petkov 		return false;
31352299ef71SBorislav Petkov 	}
31362299ef71SBorislav Petkov 
3137c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
3138f6d6ae96SBorislav Petkov 
3139ae7bb7c6SBorislav Petkov 	s->old_nbctl   = value & mask;
3140ae7bb7c6SBorislav Petkov 	s->nbctl_valid = true;
3141f6d6ae96SBorislav Petkov 
3142f6d6ae96SBorislav Petkov 	value |= mask;
3143c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
3144f6d6ae96SBorislav Petkov 
3145a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCFG, &value);
3146f6d6ae96SBorislav Petkov 
3147956b9ba1SJoe Perches 	edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
3148a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
3149f6d6ae96SBorislav Petkov 
3150a97fa68eSBorislav Petkov 	if (!(value & NBCFG_ECC_ENABLE)) {
315124f9a7feSBorislav Petkov 		amd64_warn("DRAM ECC disabled on this node, enabling...\n");
3152f6d6ae96SBorislav Petkov 
3153ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 0;
3154d95cf4deSBorislav Petkov 
3155f6d6ae96SBorislav Petkov 		/* Attempt to turn on DRAM ECC Enable */
3156a97fa68eSBorislav Petkov 		value |= NBCFG_ECC_ENABLE;
3157a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
3158f6d6ae96SBorislav Petkov 
3159a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
3160f6d6ae96SBorislav Petkov 
3161a97fa68eSBorislav Petkov 		if (!(value & NBCFG_ECC_ENABLE)) {
316224f9a7feSBorislav Petkov 			amd64_warn("Hardware rejected DRAM ECC enable,"
316324f9a7feSBorislav Petkov 				   "check memory DIMM configuration.\n");
31642299ef71SBorislav Petkov 			ret = false;
3165f6d6ae96SBorislav Petkov 		} else {
316624f9a7feSBorislav Petkov 			amd64_info("Hardware accepted DRAM ECC Enable\n");
3167f6d6ae96SBorislav Petkov 		}
3168d95cf4deSBorislav Petkov 	} else {
3169ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 1;
3170f6d6ae96SBorislav Petkov 	}
3171d95cf4deSBorislav Petkov 
3172956b9ba1SJoe Perches 	edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
3173a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
3174f6d6ae96SBorislav Petkov 
31752299ef71SBorislav Petkov 	return ret;
3176f6d6ae96SBorislav Petkov }
3177f6d6ae96SBorislav Petkov 
3178c7e5301aSDaniel J Blueman static void restore_ecc_error_reporting(struct ecc_settings *s, u16 nid,
3179360b7f3cSBorislav Petkov 					struct pci_dev *F3)
3180f6d6ae96SBorislav Petkov {
3181c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
3182c9f4f26eSBorislav Petkov 
3183ae7bb7c6SBorislav Petkov 	if (!s->nbctl_valid)
3184f6d6ae96SBorislav Petkov 		return;
3185f6d6ae96SBorislav Petkov 
3186c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
3187f6d6ae96SBorislav Petkov 	value &= ~mask;
3188ae7bb7c6SBorislav Petkov 	value |= s->old_nbctl;
3189f6d6ae96SBorislav Petkov 
3190c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
3191f6d6ae96SBorislav Petkov 
3192ae7bb7c6SBorislav Petkov 	/* restore previous BIOS DRAM ECC "off" setting we force-enabled */
3193ae7bb7c6SBorislav Petkov 	if (!s->flags.nb_ecc_prev) {
3194a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
3195a97fa68eSBorislav Petkov 		value &= ~NBCFG_ECC_ENABLE;
3196a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
3197d95cf4deSBorislav Petkov 	}
3198d95cf4deSBorislav Petkov 
3199d95cf4deSBorislav Petkov 	/* restore the NB Enable MCGCTL bit */
32002299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, OFF))
320124f9a7feSBorislav Petkov 		amd64_warn("Error restoring NB MCGCTL settings!\n");
3202f6d6ae96SBorislav Petkov }
3203f6d6ae96SBorislav Petkov 
32041c9b08baSYazen Ghannam static bool ecc_enabled(struct amd64_pvt *pvt)
3205f9431992SDoug Thompson {
32061c9b08baSYazen Ghannam 	u16 nid = pvt->mc_node_id;
320706724535SBorislav Petkov 	bool nb_mce_en = false;
3208196b79fcSYazen Ghannam 	u8 ecc_en = 0, i;
3209196b79fcSYazen Ghannam 	u32 value;
3210f9431992SDoug Thompson 
3211196b79fcSYazen Ghannam 	if (boot_cpu_data.x86 >= 0x17) {
3212196b79fcSYazen Ghannam 		u8 umc_en_mask = 0, ecc_en_mask = 0;
32131c9b08baSYazen Ghannam 		struct amd64_umc *umc;
3214196b79fcSYazen Ghannam 
32154d30d2bcSYazen Ghannam 		for_each_umc(i) {
32161c9b08baSYazen Ghannam 			umc = &pvt->umc[i];
3217196b79fcSYazen Ghannam 
3218196b79fcSYazen Ghannam 			/* Only check enabled UMCs. */
32191c9b08baSYazen Ghannam 			if (!(umc->sdp_ctrl & UMC_SDP_INIT))
3220196b79fcSYazen Ghannam 				continue;
3221196b79fcSYazen Ghannam 
3222196b79fcSYazen Ghannam 			umc_en_mask |= BIT(i);
3223196b79fcSYazen Ghannam 
32241c9b08baSYazen Ghannam 			if (umc->umc_cap_hi & UMC_ECC_ENABLED)
3225196b79fcSYazen Ghannam 				ecc_en_mask |= BIT(i);
3226196b79fcSYazen Ghannam 		}
3227196b79fcSYazen Ghannam 
3228196b79fcSYazen Ghannam 		/* Check whether at least one UMC is enabled: */
3229196b79fcSYazen Ghannam 		if (umc_en_mask)
3230196b79fcSYazen Ghannam 			ecc_en = umc_en_mask == ecc_en_mask;
323111ab1caeSYazen Ghannam 		else
323211ab1caeSYazen Ghannam 			edac_dbg(0, "Node %d: No enabled UMCs.\n", nid);
3233196b79fcSYazen Ghannam 
3234196b79fcSYazen Ghannam 		/* Assume UMC MCA banks are enabled. */
3235196b79fcSYazen Ghannam 		nb_mce_en = true;
3236196b79fcSYazen Ghannam 	} else {
32371c9b08baSYazen Ghannam 		amd64_read_pci_cfg(pvt->F3, NBCFG, &value);
3238f9431992SDoug Thompson 
3239a97fa68eSBorislav Petkov 		ecc_en = !!(value & NBCFG_ECC_ENABLE);
3240be3468e8SBorislav Petkov 
3241d1ea71cdSBorislav Petkov 		nb_mce_en = nb_mce_bank_enabled_on_node(nid);
324206724535SBorislav Petkov 		if (!nb_mce_en)
324311ab1caeSYazen Ghannam 			edac_dbg(0, "NB MCE bank disabled, set MSR 0x%08x[4] on node %d to enable.\n",
32442299ef71SBorislav Petkov 				     MSR_IA32_MCG_CTL, nid);
3245196b79fcSYazen Ghannam 	}
3246196b79fcSYazen Ghannam 
324711ab1caeSYazen Ghannam 	amd64_info("Node %d: DRAM ECC %s.\n",
324811ab1caeSYazen Ghannam 		   nid, (ecc_en ? "enabled" : "disabled"));
3249be3468e8SBorislav Petkov 
32507fdfee92SBorislav Petkov 	if (!ecc_en || !nb_mce_en)
32512299ef71SBorislav Petkov 		return false;
32527fdfee92SBorislav Petkov 	else
32532299ef71SBorislav Petkov 		return true;
3254f9431992SDoug Thompson }
3255f9431992SDoug Thompson 
32562d09d8f3SYazen Ghannam static inline void
32572d09d8f3SYazen Ghannam f17h_determine_edac_ctl_cap(struct mem_ctl_info *mci, struct amd64_pvt *pvt)
32582d09d8f3SYazen Ghannam {
3259f8be8e56SYazen Ghannam 	u8 i, ecc_en = 1, cpk_en = 1, dev_x4 = 1, dev_x16 = 1;
32602d09d8f3SYazen Ghannam 
32614d30d2bcSYazen Ghannam 	for_each_umc(i) {
32622d09d8f3SYazen Ghannam 		if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) {
32632d09d8f3SYazen Ghannam 			ecc_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_ENABLED);
32642d09d8f3SYazen Ghannam 			cpk_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_CHIPKILL_CAP);
3265f8be8e56SYazen Ghannam 
3266f8be8e56SYazen Ghannam 			dev_x4  &= !!(pvt->umc[i].dimm_cfg & BIT(6));
3267f8be8e56SYazen Ghannam 			dev_x16 &= !!(pvt->umc[i].dimm_cfg & BIT(7));
32682d09d8f3SYazen Ghannam 		}
32692d09d8f3SYazen Ghannam 	}
32702d09d8f3SYazen Ghannam 
32712d09d8f3SYazen Ghannam 	/* Set chipkill only if ECC is enabled: */
32722d09d8f3SYazen Ghannam 	if (ecc_en) {
32732d09d8f3SYazen Ghannam 		mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
32742d09d8f3SYazen Ghannam 
3275f8be8e56SYazen Ghannam 		if (!cpk_en)
3276f8be8e56SYazen Ghannam 			return;
3277f8be8e56SYazen Ghannam 
3278f8be8e56SYazen Ghannam 		if (dev_x4)
32792d09d8f3SYazen Ghannam 			mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
3280f8be8e56SYazen Ghannam 		else if (dev_x16)
3281f8be8e56SYazen Ghannam 			mci->edac_ctl_cap |= EDAC_FLAG_S16ECD16ED;
3282f8be8e56SYazen Ghannam 		else
3283f8be8e56SYazen Ghannam 			mci->edac_ctl_cap |= EDAC_FLAG_S8ECD8ED;
32842d09d8f3SYazen Ghannam 	}
32852d09d8f3SYazen Ghannam }
32862d09d8f3SYazen Ghannam 
328738ddd4d1SYazen Ghannam static void setup_mci_misc_attrs(struct mem_ctl_info *mci)
32887d6034d3SDoug Thompson {
32897d6034d3SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
32907d6034d3SDoug Thompson 
32917d6034d3SDoug Thompson 	mci->mtype_cap		= MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
32927d6034d3SDoug Thompson 	mci->edac_ctl_cap	= EDAC_FLAG_NONE;
32937d6034d3SDoug Thompson 
32942d09d8f3SYazen Ghannam 	if (pvt->umc) {
32952d09d8f3SYazen Ghannam 		f17h_determine_edac_ctl_cap(mci, pvt);
32962d09d8f3SYazen Ghannam 	} else {
32975980bb9cSBorislav Petkov 		if (pvt->nbcap & NBCAP_SECDED)
32987d6034d3SDoug Thompson 			mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
32997d6034d3SDoug Thompson 
33005980bb9cSBorislav Petkov 		if (pvt->nbcap & NBCAP_CHIPKILL)
33017d6034d3SDoug Thompson 			mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
33022d09d8f3SYazen Ghannam 	}
33037d6034d3SDoug Thompson 
3304d1ea71cdSBorislav Petkov 	mci->edac_cap		= determine_edac_cap(pvt);
33057d6034d3SDoug Thompson 	mci->mod_name		= EDAC_MOD_STR;
330638ddd4d1SYazen Ghannam 	mci->ctl_name		= fam_type->ctl_name;
3307e7934b70SYazen Ghannam 	mci->dev_name		= pci_name(pvt->F3);
33087d6034d3SDoug Thompson 	mci->ctl_page_to_phys	= NULL;
33097d6034d3SDoug Thompson 
33107d6034d3SDoug Thompson 	/* memory scrubber interface */
3311d1ea71cdSBorislav Petkov 	mci->set_sdram_scrub_rate = set_scrub_rate;
3312d1ea71cdSBorislav Petkov 	mci->get_sdram_scrub_rate = get_scrub_rate;
33137d6034d3SDoug Thompson }
33147d6034d3SDoug Thompson 
33150092b20dSBorislav Petkov /*
33160092b20dSBorislav Petkov  * returns a pointer to the family descriptor on success, NULL otherwise.
33170092b20dSBorislav Petkov  */
3318d1ea71cdSBorislav Petkov static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt)
3319395ae783SBorislav Petkov {
332018b94f66SAravind Gopalakrishnan 	pvt->ext_model  = boot_cpu_data.x86_model >> 4;
3321b399151cSJia Zhang 	pvt->stepping	= boot_cpu_data.x86_stepping;
332218b94f66SAravind Gopalakrishnan 	pvt->model	= boot_cpu_data.x86_model;
332318b94f66SAravind Gopalakrishnan 	pvt->fam	= boot_cpu_data.x86;
332418b94f66SAravind Gopalakrishnan 
332518b94f66SAravind Gopalakrishnan 	switch (pvt->fam) {
3326395ae783SBorislav Petkov 	case 0xf:
3327d1ea71cdSBorislav Petkov 		fam_type	= &family_types[K8_CPUS];
3328d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[K8_CPUS].ops;
3329395ae783SBorislav Petkov 		break;
3330df71a053SBorislav Petkov 
3331395ae783SBorislav Petkov 	case 0x10:
3332d1ea71cdSBorislav Petkov 		fam_type	= &family_types[F10_CPUS];
3333d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[F10_CPUS].ops;
3334df71a053SBorislav Petkov 		break;
3335df71a053SBorislav Petkov 
3336df71a053SBorislav Petkov 	case 0x15:
333718b94f66SAravind Gopalakrishnan 		if (pvt->model == 0x30) {
3338d1ea71cdSBorislav Petkov 			fam_type = &family_types[F15_M30H_CPUS];
3339d1ea71cdSBorislav Petkov 			pvt->ops = &family_types[F15_M30H_CPUS].ops;
334018b94f66SAravind Gopalakrishnan 			break;
3341a597d2a5SAravind Gopalakrishnan 		} else if (pvt->model == 0x60) {
3342a597d2a5SAravind Gopalakrishnan 			fam_type = &family_types[F15_M60H_CPUS];
3343a597d2a5SAravind Gopalakrishnan 			pvt->ops = &family_types[F15_M60H_CPUS].ops;
3344a597d2a5SAravind Gopalakrishnan 			break;
3345*6c13d7ffSBorislav Petkov 		/* Richland is only client */
3346*6c13d7ffSBorislav Petkov 		} else if (pvt->model == 0x13) {
3347*6c13d7ffSBorislav Petkov 			return NULL;
3348*6c13d7ffSBorislav Petkov 		} else {
3349d1ea71cdSBorislav Petkov 			fam_type	= &family_types[F15_CPUS];
3350d1ea71cdSBorislav Petkov 			pvt->ops	= &family_types[F15_CPUS].ops;
3351*6c13d7ffSBorislav Petkov 		}
3352395ae783SBorislav Petkov 		break;
3353395ae783SBorislav Petkov 
335494c1acf2SAravind Gopalakrishnan 	case 0x16:
335585a8885bSAravind Gopalakrishnan 		if (pvt->model == 0x30) {
335685a8885bSAravind Gopalakrishnan 			fam_type = &family_types[F16_M30H_CPUS];
335785a8885bSAravind Gopalakrishnan 			pvt->ops = &family_types[F16_M30H_CPUS].ops;
335885a8885bSAravind Gopalakrishnan 			break;
335985a8885bSAravind Gopalakrishnan 		}
3360d1ea71cdSBorislav Petkov 		fam_type	= &family_types[F16_CPUS];
3361d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[F16_CPUS].ops;
336294c1acf2SAravind Gopalakrishnan 		break;
336394c1acf2SAravind Gopalakrishnan 
3364f1cbbec9SYazen Ghannam 	case 0x17:
33658960de4aSMichael Jin 		if (pvt->model >= 0x10 && pvt->model <= 0x2f) {
33668960de4aSMichael Jin 			fam_type = &family_types[F17_M10H_CPUS];
33678960de4aSMichael Jin 			pvt->ops = &family_types[F17_M10H_CPUS].ops;
33688960de4aSMichael Jin 			break;
33696e846239SYazen Ghannam 		} else if (pvt->model >= 0x30 && pvt->model <= 0x3f) {
33706e846239SYazen Ghannam 			fam_type = &family_types[F17_M30H_CPUS];
33716e846239SYazen Ghannam 			pvt->ops = &family_types[F17_M30H_CPUS].ops;
33726e846239SYazen Ghannam 			break;
3373b6bea24dSAlexander Monakov 		} else if (pvt->model >= 0x60 && pvt->model <= 0x6f) {
3374b6bea24dSAlexander Monakov 			fam_type = &family_types[F17_M60H_CPUS];
3375b6bea24dSAlexander Monakov 			pvt->ops = &family_types[F17_M60H_CPUS].ops;
3376b6bea24dSAlexander Monakov 			break;
33773e443eb3SIsaac Vaughn 		} else if (pvt->model >= 0x70 && pvt->model <= 0x7f) {
33783e443eb3SIsaac Vaughn 			fam_type = &family_types[F17_M70H_CPUS];
33793e443eb3SIsaac Vaughn 			pvt->ops = &family_types[F17_M70H_CPUS].ops;
33803e443eb3SIsaac Vaughn 			break;
33818960de4aSMichael Jin 		}
3382df561f66SGustavo A. R. Silva 		fallthrough;
3383c4a3e946SPu Wen 	case 0x18:
3384f1cbbec9SYazen Ghannam 		fam_type	= &family_types[F17_CPUS];
3385f1cbbec9SYazen Ghannam 		pvt->ops	= &family_types[F17_CPUS].ops;
3386c4a3e946SPu Wen 
3387c4a3e946SPu Wen 		if (pvt->fam == 0x18)
3388c4a3e946SPu Wen 			family_types[F17_CPUS].ctl_name = "F18h";
3389f1cbbec9SYazen Ghannam 		break;
3390f1cbbec9SYazen Ghannam 
33912eb61c91SYazen Ghannam 	case 0x19:
3392b4210eabSYazen Ghannam 		if (pvt->model >= 0x20 && pvt->model <= 0x2f) {
3393b4210eabSYazen Ghannam 			fam_type = &family_types[F17_M70H_CPUS];
3394b4210eabSYazen Ghannam 			pvt->ops = &family_types[F17_M70H_CPUS].ops;
3395b4210eabSYazen Ghannam 			fam_type->ctl_name = "F19h_M20h";
3396b4210eabSYazen Ghannam 			break;
3397b4210eabSYazen Ghannam 		}
33982eb61c91SYazen Ghannam 		fam_type	= &family_types[F19_CPUS];
33992eb61c91SYazen Ghannam 		pvt->ops	= &family_types[F19_CPUS].ops;
34002eb61c91SYazen Ghannam 		family_types[F19_CPUS].ctl_name = "F19h";
34012eb61c91SYazen Ghannam 		break;
34022eb61c91SYazen Ghannam 
3403395ae783SBorislav Petkov 	default:
340424f9a7feSBorislav Petkov 		amd64_err("Unsupported family!\n");
34050092b20dSBorislav Petkov 		return NULL;
3406395ae783SBorislav Petkov 	}
34070092b20dSBorislav Petkov 
3408df71a053SBorislav Petkov 	amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name,
340918b94f66SAravind Gopalakrishnan 		     (pvt->fam == 0xf ?
34100092b20dSBorislav Petkov 				(pvt->ext_model >= K8_REV_F  ? "revF or later "
34110092b20dSBorislav Petkov 							     : "revE or earlier ")
341224f9a7feSBorislav Petkov 				 : ""), pvt->mc_node_id);
34130092b20dSBorislav Petkov 	return fam_type;
3414395ae783SBorislav Petkov }
3415395ae783SBorislav Petkov 
3416e339f1ecSTakashi Iwai static const struct attribute_group *amd64_edac_attr_groups[] = {
3417e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_DEBUG
3418e339f1ecSTakashi Iwai 	&amd64_edac_dbg_group,
3419e339f1ecSTakashi Iwai #endif
3420e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION
3421e339f1ecSTakashi Iwai 	&amd64_edac_inj_group,
3422e339f1ecSTakashi Iwai #endif
3423e339f1ecSTakashi Iwai 	NULL
3424e339f1ecSTakashi Iwai };
3425e339f1ecSTakashi Iwai 
342680355a3bSYazen Ghannam static int hw_info_get(struct amd64_pvt *pvt)
34277d6034d3SDoug Thompson {
3428936fc3afSYazen Ghannam 	u16 pci_id1, pci_id2;
3429f00eb5ffSColin Ian King 	int ret;
3430395ae783SBorislav Petkov 
3431936fc3afSYazen Ghannam 	if (pvt->fam >= 0x17) {
34325e4c5527SYazen Ghannam 		pvt->umc = kcalloc(fam_type->max_mcs, sizeof(struct amd64_umc), GFP_KERNEL);
343380355a3bSYazen Ghannam 		if (!pvt->umc)
343480355a3bSYazen Ghannam 			return -ENOMEM;
3435936fc3afSYazen Ghannam 
3436936fc3afSYazen Ghannam 		pci_id1 = fam_type->f0_id;
3437936fc3afSYazen Ghannam 		pci_id2 = fam_type->f6_id;
3438936fc3afSYazen Ghannam 	} else {
3439936fc3afSYazen Ghannam 		pci_id1 = fam_type->f1_id;
3440936fc3afSYazen Ghannam 		pci_id2 = fam_type->f2_id;
3441936fc3afSYazen Ghannam 	}
3442936fc3afSYazen Ghannam 
344380355a3bSYazen Ghannam 	ret = reserve_mc_sibling_devs(pvt, pci_id1, pci_id2);
344480355a3bSYazen Ghannam 	if (ret)
344580355a3bSYazen Ghannam 		return ret;
34467d6034d3SDoug Thompson 
3447360b7f3cSBorislav Petkov 	read_mc_regs(pvt);
34487d6034d3SDoug Thompson 
344980355a3bSYazen Ghannam 	return 0;
345080355a3bSYazen Ghannam }
345180355a3bSYazen Ghannam 
345280355a3bSYazen Ghannam static void hw_info_put(struct amd64_pvt *pvt)
345380355a3bSYazen Ghannam {
345480355a3bSYazen Ghannam 	if (pvt->F0 || pvt->F1)
345580355a3bSYazen Ghannam 		free_mc_sibling_devs(pvt);
345680355a3bSYazen Ghannam 
345780355a3bSYazen Ghannam 	kfree(pvt->umc);
345880355a3bSYazen Ghannam }
345980355a3bSYazen Ghannam 
346080355a3bSYazen Ghannam static int init_one_instance(struct amd64_pvt *pvt)
346180355a3bSYazen Ghannam {
346280355a3bSYazen Ghannam 	struct mem_ctl_info *mci = NULL;
346380355a3bSYazen Ghannam 	struct edac_mc_layer layers[2];
346480355a3bSYazen Ghannam 	int ret = -EINVAL;
346580355a3bSYazen Ghannam 
34667d6034d3SDoug Thompson 	/*
34677d6034d3SDoug Thompson 	 * We need to determine how many memory channels there are. Then use
34687d6034d3SDoug Thompson 	 * that information for calculating the size of the dynamic instance
3469360b7f3cSBorislav Petkov 	 * tables in the 'mci' structure.
34707d6034d3SDoug Thompson 	 */
34717d6034d3SDoug Thompson 	pvt->channel_count = pvt->ops->early_channel_count(pvt);
34727d6034d3SDoug Thompson 	if (pvt->channel_count < 0)
347380355a3bSYazen Ghannam 		return ret;
34747d6034d3SDoug Thompson 
34757d6034d3SDoug Thompson 	ret = -ENOMEM;
3476ab5a503cSMauro Carvalho Chehab 	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
3477ab5a503cSMauro Carvalho Chehab 	layers[0].size = pvt->csels[0].b_cnt;
3478ab5a503cSMauro Carvalho Chehab 	layers[0].is_virt_csrow = true;
3479ab5a503cSMauro Carvalho Chehab 	layers[1].type = EDAC_MC_LAYER_CHANNEL;
3480f0a56c48SBorislav Petkov 
3481f0a56c48SBorislav Petkov 	/*
3482f0a56c48SBorislav Petkov 	 * Always allocate two channels since we can have setups with DIMMs on
3483f0a56c48SBorislav Petkov 	 * only one channel. Also, this simplifies handling later for the price
3484f0a56c48SBorislav Petkov 	 * of a couple of KBs tops.
3485f0a56c48SBorislav Petkov 	 */
34865e4c5527SYazen Ghannam 	layers[1].size = fam_type->max_mcs;
3487ab5a503cSMauro Carvalho Chehab 	layers[1].is_virt_csrow = false;
3488f0a56c48SBorislav Petkov 
348980355a3bSYazen Ghannam 	mci = edac_mc_alloc(pvt->mc_node_id, ARRAY_SIZE(layers), layers, 0);
34907d6034d3SDoug Thompson 	if (!mci)
349180355a3bSYazen Ghannam 		return ret;
34927d6034d3SDoug Thompson 
34937d6034d3SDoug Thompson 	mci->pvt_info = pvt;
34943f37a36bSBorislav Petkov 	mci->pdev = &pvt->F3->dev;
34957d6034d3SDoug Thompson 
349638ddd4d1SYazen Ghannam 	setup_mci_misc_attrs(mci);
3497360b7f3cSBorislav Petkov 
3498360b7f3cSBorislav Petkov 	if (init_csrows(mci))
34997d6034d3SDoug Thompson 		mci->edac_cap = EDAC_FLAG_NONE;
35007d6034d3SDoug Thompson 
35017d6034d3SDoug Thompson 	ret = -ENODEV;
3502e339f1ecSTakashi Iwai 	if (edac_mc_add_mc_with_groups(mci, amd64_edac_attr_groups)) {
3503956b9ba1SJoe Perches 		edac_dbg(1, "failed edac_mc_add_mc()\n");
350480355a3bSYazen Ghannam 		edac_mc_free(mci);
350580355a3bSYazen Ghannam 		return ret;
35067d6034d3SDoug Thompson 	}
35077d6034d3SDoug Thompson 
35087d6034d3SDoug Thompson 	return 0;
35097d6034d3SDoug Thompson }
35107d6034d3SDoug Thompson 
3511582f94b5SYazen Ghannam static bool instance_has_memory(struct amd64_pvt *pvt)
3512582f94b5SYazen Ghannam {
3513582f94b5SYazen Ghannam 	bool cs_enabled = false;
3514582f94b5SYazen Ghannam 	int cs = 0, dct = 0;
3515582f94b5SYazen Ghannam 
3516582f94b5SYazen Ghannam 	for (dct = 0; dct < fam_type->max_mcs; dct++) {
3517582f94b5SYazen Ghannam 		for_each_chip_select(cs, dct, pvt)
3518582f94b5SYazen Ghannam 			cs_enabled |= csrow_enabled(cs, dct, pvt);
3519582f94b5SYazen Ghannam 	}
3520582f94b5SYazen Ghannam 
3521582f94b5SYazen Ghannam 	return cs_enabled;
3522582f94b5SYazen Ghannam }
3523582f94b5SYazen Ghannam 
35243f37a36bSBorislav Petkov static int probe_one_instance(unsigned int nid)
35257d6034d3SDoug Thompson {
35262299ef71SBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
352780355a3bSYazen Ghannam 	struct amd64_pvt *pvt = NULL;
3528ae7bb7c6SBorislav Petkov 	struct ecc_settings *s;
35293f37a36bSBorislav Petkov 	int ret;
3530b8cfa02fSBorislav Petkov 
3531ae7bb7c6SBorislav Petkov 	ret = -ENOMEM;
3532ae7bb7c6SBorislav Petkov 	s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL);
3533ae7bb7c6SBorislav Petkov 	if (!s)
35342299ef71SBorislav Petkov 		goto err_out;
3535ae7bb7c6SBorislav Petkov 
3536ae7bb7c6SBorislav Petkov 	ecc_stngs[nid] = s;
3537ae7bb7c6SBorislav Petkov 
353880355a3bSYazen Ghannam 	pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
353980355a3bSYazen Ghannam 	if (!pvt)
354080355a3bSYazen Ghannam 		goto err_settings;
354180355a3bSYazen Ghannam 
354280355a3bSYazen Ghannam 	pvt->mc_node_id	= nid;
354380355a3bSYazen Ghannam 	pvt->F3 = F3;
354480355a3bSYazen Ghannam 
3545*6c13d7ffSBorislav Petkov 	ret = -ENODEV;
354680355a3bSYazen Ghannam 	fam_type = per_family_init(pvt);
354780355a3bSYazen Ghannam 	if (!fam_type)
354880355a3bSYazen Ghannam 		goto err_enable;
354980355a3bSYazen Ghannam 
355080355a3bSYazen Ghannam 	ret = hw_info_get(pvt);
355180355a3bSYazen Ghannam 	if (ret < 0)
355280355a3bSYazen Ghannam 		goto err_enable;
355380355a3bSYazen Ghannam 
35544688c9b4SYazen Ghannam 	ret = 0;
3555582f94b5SYazen Ghannam 	if (!instance_has_memory(pvt)) {
3556582f94b5SYazen Ghannam 		amd64_info("Node %d: No DIMMs detected.\n", nid);
3557582f94b5SYazen Ghannam 		goto err_enable;
3558582f94b5SYazen Ghannam 	}
3559582f94b5SYazen Ghannam 
3560582f94b5SYazen Ghannam 	if (!ecc_enabled(pvt)) {
3561582f94b5SYazen Ghannam 		ret = -ENODEV;
35622299ef71SBorislav Petkov 
35632299ef71SBorislav Petkov 		if (!ecc_enable_override)
35642299ef71SBorislav Petkov 			goto err_enable;
35652299ef71SBorislav Petkov 
3566044e7a41SYazen Ghannam 		if (boot_cpu_data.x86 >= 0x17) {
3567044e7a41SYazen Ghannam 			amd64_warn("Forcing ECC on is not recommended on newer systems. Please enable ECC in BIOS.");
3568044e7a41SYazen Ghannam 			goto err_enable;
3569044e7a41SYazen Ghannam 		} else
35702299ef71SBorislav Petkov 			amd64_warn("Forcing ECC on!\n");
35712299ef71SBorislav Petkov 
35722299ef71SBorislav Petkov 		if (!enable_ecc_error_reporting(s, nid, F3))
35732299ef71SBorislav Petkov 			goto err_enable;
35742299ef71SBorislav Petkov 	}
35752299ef71SBorislav Petkov 
357680355a3bSYazen Ghannam 	ret = init_one_instance(pvt);
3577360b7f3cSBorislav Petkov 	if (ret < 0) {
3578ae7bb7c6SBorislav Petkov 		amd64_err("Error probing instance: %d\n", nid);
3579044e7a41SYazen Ghannam 
3580044e7a41SYazen Ghannam 		if (boot_cpu_data.x86 < 0x17)
3581360b7f3cSBorislav Petkov 			restore_ecc_error_reporting(s, nid, F3);
35822b9b2c46SYazen Ghannam 
35832b9b2c46SYazen Ghannam 		goto err_enable;
3584360b7f3cSBorislav Petkov 	}
35857d6034d3SDoug Thompson 
3586582f94b5SYazen Ghannam 	dump_misc_regs(pvt);
3587582f94b5SYazen Ghannam 
35887d6034d3SDoug Thompson 	return ret;
35892299ef71SBorislav Petkov 
35902299ef71SBorislav Petkov err_enable:
359180355a3bSYazen Ghannam 	hw_info_put(pvt);
359280355a3bSYazen Ghannam 	kfree(pvt);
359380355a3bSYazen Ghannam 
359480355a3bSYazen Ghannam err_settings:
35952299ef71SBorislav Petkov 	kfree(s);
35962299ef71SBorislav Petkov 	ecc_stngs[nid] = NULL;
35972299ef71SBorislav Petkov 
35982299ef71SBorislav Petkov err_out:
35992299ef71SBorislav Petkov 	return ret;
36007d6034d3SDoug Thompson }
36017d6034d3SDoug Thompson 
36023f37a36bSBorislav Petkov static void remove_one_instance(unsigned int nid)
36037d6034d3SDoug Thompson {
3604360b7f3cSBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
3605360b7f3cSBorislav Petkov 	struct ecc_settings *s = ecc_stngs[nid];
36063f37a36bSBorislav Petkov 	struct mem_ctl_info *mci;
36073f37a36bSBorislav Petkov 	struct amd64_pvt *pvt;
36087d6034d3SDoug Thompson 
36097d6034d3SDoug Thompson 	/* Remove from EDAC CORE tracking list */
36103f37a36bSBorislav Petkov 	mci = edac_mc_del_mc(&F3->dev);
36117d6034d3SDoug Thompson 	if (!mci)
36127d6034d3SDoug Thompson 		return;
36137d6034d3SDoug Thompson 
36147d6034d3SDoug Thompson 	pvt = mci->pvt_info;
36157d6034d3SDoug Thompson 
3616360b7f3cSBorislav Petkov 	restore_ecc_error_reporting(s, nid, F3);
36177d6034d3SDoug Thompson 
3618360b7f3cSBorislav Petkov 	kfree(ecc_stngs[nid]);
3619360b7f3cSBorislav Petkov 	ecc_stngs[nid] = NULL;
3620ae7bb7c6SBorislav Petkov 
36217d6034d3SDoug Thompson 	/* Free the EDAC CORE resources */
36228f68ed97SBorislav Petkov 	mci->pvt_info = NULL;
36238f68ed97SBorislav Petkov 
362480355a3bSYazen Ghannam 	hw_info_put(pvt);
36258f68ed97SBorislav Petkov 	kfree(pvt);
36267d6034d3SDoug Thompson 	edac_mc_free(mci);
36277d6034d3SDoug Thompson }
36287d6034d3SDoug Thompson 
3629360b7f3cSBorislav Petkov static void setup_pci_device(void)
36307d6034d3SDoug Thompson {
3631d1ea71cdSBorislav Petkov 	if (pci_ctl)
36327d6034d3SDoug Thompson 		return;
36337d6034d3SDoug Thompson 
3634706657b1SBorislav Petkov 	pci_ctl = edac_pci_create_generic_ctl(pci_ctl_dev, EDAC_MOD_STR);
3635d1ea71cdSBorislav Petkov 	if (!pci_ctl) {
3636d1ea71cdSBorislav Petkov 		pr_warn("%s(): Unable to create PCI control\n", __func__);
3637d1ea71cdSBorislav Petkov 		pr_warn("%s(): PCI error report via EDAC not set\n", __func__);
36387d6034d3SDoug Thompson 	}
36397d6034d3SDoug Thompson }
36407d6034d3SDoug Thompson 
3641d6efab74SYazen Ghannam static const struct x86_cpu_id amd64_cpuids[] = {
364229842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x0F, NULL),
364329842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x10, NULL),
364429842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x15, NULL),
364529842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x16, NULL),
364629842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x17, NULL),
364729842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(HYGON,	0x18, NULL),
364829842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x19, NULL),
3649d6efab74SYazen Ghannam 	{ }
3650d6efab74SYazen Ghannam };
3651d6efab74SYazen Ghannam MODULE_DEVICE_TABLE(x86cpu, amd64_cpuids);
3652d6efab74SYazen Ghannam 
36537d6034d3SDoug Thompson static int __init amd64_edac_init(void)
36547d6034d3SDoug Thompson {
3655301375e7SToshi Kani 	const char *owner;
3656360b7f3cSBorislav Petkov 	int err = -ENODEV;
36573f37a36bSBorislav Petkov 	int i;
36587d6034d3SDoug Thompson 
3659301375e7SToshi Kani 	owner = edac_get_owner();
3660301375e7SToshi Kani 	if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
3661301375e7SToshi Kani 		return -EBUSY;
3662301375e7SToshi Kani 
36631bd9900bSYazen Ghannam 	if (!x86_match_cpu(amd64_cpuids))
36641bd9900bSYazen Ghannam 		return -ENODEV;
36651bd9900bSYazen Ghannam 
36669653a5c7SHans Rosenfeld 	if (amd_cache_northbridges() < 0)
36671bd9900bSYazen Ghannam 		return -ENODEV;
36687d6034d3SDoug Thompson 
36696ba92feaSBorislav Petkov 	opstate_init();
36706ba92feaSBorislav Petkov 
3671cc4d8860SBorislav Petkov 	err = -ENOMEM;
36726396bb22SKees Cook 	ecc_stngs = kcalloc(amd_nb_num(), sizeof(ecc_stngs[0]), GFP_KERNEL);
36732ec591acSBorislav Petkov 	if (!ecc_stngs)
3674a9f0fbe2SBorislav Petkov 		goto err_free;
3675cc4d8860SBorislav Petkov 
367650542251SBorislav Petkov 	msrs = msrs_alloc();
367756b34b91SBorislav Petkov 	if (!msrs)
3678360b7f3cSBorislav Petkov 		goto err_free;
367950542251SBorislav Petkov 
36802287c636SYazen Ghannam 	for (i = 0; i < amd_nb_num(); i++) {
36812287c636SYazen Ghannam 		err = probe_one_instance(i);
36822287c636SYazen Ghannam 		if (err) {
36833f37a36bSBorislav Petkov 			/* unwind properly */
36843f37a36bSBorislav Petkov 			while (--i >= 0)
36853f37a36bSBorislav Petkov 				remove_one_instance(i);
36867d6034d3SDoug Thompson 
36873f37a36bSBorislav Petkov 			goto err_pci;
36883f37a36bSBorislav Petkov 		}
36892287c636SYazen Ghannam 	}
36907d6034d3SDoug Thompson 
36914688c9b4SYazen Ghannam 	if (!edac_has_mcs()) {
36924688c9b4SYazen Ghannam 		err = -ENODEV;
36934688c9b4SYazen Ghannam 		goto err_pci;
36944688c9b4SYazen Ghannam 	}
36954688c9b4SYazen Ghannam 
3696234365f5SYazen Ghannam 	/* register stuff with EDAC MCE */
3697234365f5SYazen Ghannam 	if (boot_cpu_data.x86 >= 0x17)
3698234365f5SYazen Ghannam 		amd_register_ecc_decoder(decode_umc_error);
3699234365f5SYazen Ghannam 	else
3700234365f5SYazen Ghannam 		amd_register_ecc_decoder(decode_bus_error);
3701234365f5SYazen Ghannam 
3702360b7f3cSBorislav Petkov 	setup_pci_device();
3703f5b10c45STomasz Pala 
3704f5b10c45STomasz Pala #ifdef CONFIG_X86_32
3705f5b10c45STomasz Pala 	amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR);
3706f5b10c45STomasz Pala #endif
3707f5b10c45STomasz Pala 
3708de0336b3SBorislav Petkov 	printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION);
3709de0336b3SBorislav Petkov 
37107d6034d3SDoug Thompson 	return 0;
37117d6034d3SDoug Thompson 
371256b34b91SBorislav Petkov err_pci:
3713706657b1SBorislav Petkov 	pci_ctl_dev = NULL;
3714706657b1SBorislav Petkov 
371556b34b91SBorislav Petkov 	msrs_free(msrs);
371656b34b91SBorislav Petkov 	msrs = NULL;
3717cc4d8860SBorislav Petkov 
3718360b7f3cSBorislav Petkov err_free:
3719360b7f3cSBorislav Petkov 	kfree(ecc_stngs);
3720360b7f3cSBorislav Petkov 	ecc_stngs = NULL;
3721360b7f3cSBorislav Petkov 
37227d6034d3SDoug Thompson 	return err;
37237d6034d3SDoug Thompson }
37247d6034d3SDoug Thompson 
37257d6034d3SDoug Thompson static void __exit amd64_edac_exit(void)
37267d6034d3SDoug Thompson {
37273f37a36bSBorislav Petkov 	int i;
37283f37a36bSBorislav Petkov 
3729d1ea71cdSBorislav Petkov 	if (pci_ctl)
3730d1ea71cdSBorislav Petkov 		edac_pci_release_generic_ctl(pci_ctl);
37317d6034d3SDoug Thompson 
3732234365f5SYazen Ghannam 	/* unregister from EDAC MCE */
3733234365f5SYazen Ghannam 	if (boot_cpu_data.x86 >= 0x17)
3734234365f5SYazen Ghannam 		amd_unregister_ecc_decoder(decode_umc_error);
3735234365f5SYazen Ghannam 	else
3736234365f5SYazen Ghannam 		amd_unregister_ecc_decoder(decode_bus_error);
3737234365f5SYazen Ghannam 
37383f37a36bSBorislav Petkov 	for (i = 0; i < amd_nb_num(); i++)
37393f37a36bSBorislav Petkov 		remove_one_instance(i);
374050542251SBorislav Petkov 
3741ae7bb7c6SBorislav Petkov 	kfree(ecc_stngs);
3742ae7bb7c6SBorislav Petkov 	ecc_stngs = NULL;
3743ae7bb7c6SBorislav Petkov 
3744706657b1SBorislav Petkov 	pci_ctl_dev = NULL;
3745706657b1SBorislav Petkov 
374650542251SBorislav Petkov 	msrs_free(msrs);
374750542251SBorislav Petkov 	msrs = NULL;
37487d6034d3SDoug Thompson }
37497d6034d3SDoug Thompson 
37507d6034d3SDoug Thompson module_init(amd64_edac_init);
37517d6034d3SDoug Thompson module_exit(amd64_edac_exit);
37527d6034d3SDoug Thompson 
37537d6034d3SDoug Thompson MODULE_LICENSE("GPL");
37547d6034d3SDoug Thompson MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, "
37557d6034d3SDoug Thompson 		"Dave Peterson, Thayne Harbaugh");
37567d6034d3SDoug Thompson MODULE_DESCRIPTION("MC support for AMD64 memory controllers - "
37577d6034d3SDoug Thompson 		EDAC_AMD64_VERSION);
37587d6034d3SDoug Thompson 
37597d6034d3SDoug Thompson module_param(edac_op_state, int, 0444);
37607d6034d3SDoug Thompson MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
3761