xref: /openbmc/linux/drivers/edac/amd64_edac.c (revision 8960de4a)
12bc65418SDoug Thompson #include "amd64_edac.h"
223ac4ae8SAndreas Herrmann #include <asm/amd_nb.h>
32bc65418SDoug Thompson 
4d1ea71cdSBorislav Petkov static struct edac_pci_ctl_info *pci_ctl;
52bc65418SDoug Thompson 
62bc65418SDoug Thompson static int report_gart_errors;
72bc65418SDoug Thompson module_param(report_gart_errors, int, 0644);
82bc65418SDoug Thompson 
92bc65418SDoug Thompson /*
102bc65418SDoug Thompson  * Set by command line parameter. If BIOS has enabled the ECC, this override is
112bc65418SDoug Thompson  * cleared to prevent re-enabling the hardware by this driver.
122bc65418SDoug Thompson  */
132bc65418SDoug Thompson static int ecc_enable_override;
142bc65418SDoug Thompson module_param(ecc_enable_override, int, 0644);
152bc65418SDoug Thompson 
16a29d8b8eSTejun Heo static struct msr __percpu *msrs;
1750542251SBorislav Petkov 
182ec591acSBorislav Petkov /* Per-node stuff */
19ae7bb7c6SBorislav Petkov static struct ecc_settings **ecc_stngs;
202bc65418SDoug Thompson 
212bc65418SDoug Thompson /*
22b70ef010SBorislav Petkov  * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing
23b70ef010SBorislav Petkov  * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching-
24b70ef010SBorislav Petkov  * or higher value'.
25b70ef010SBorislav Petkov  *
26b70ef010SBorislav Petkov  *FIXME: Produce a better mapping/linearisation.
27b70ef010SBorislav Petkov  */
28c7e5301aSDaniel J Blueman static const struct scrubrate {
2939094443SBorislav Petkov        u32 scrubval;           /* bit pattern for scrub rate */
3039094443SBorislav Petkov        u32 bandwidth;          /* bandwidth consumed (bytes/sec) */
3139094443SBorislav Petkov } scrubrates[] = {
32b70ef010SBorislav Petkov 	{ 0x01, 1600000000UL},
33b70ef010SBorislav Petkov 	{ 0x02, 800000000UL},
34b70ef010SBorislav Petkov 	{ 0x03, 400000000UL},
35b70ef010SBorislav Petkov 	{ 0x04, 200000000UL},
36b70ef010SBorislav Petkov 	{ 0x05, 100000000UL},
37b70ef010SBorislav Petkov 	{ 0x06, 50000000UL},
38b70ef010SBorislav Petkov 	{ 0x07, 25000000UL},
39b70ef010SBorislav Petkov 	{ 0x08, 12284069UL},
40b70ef010SBorislav Petkov 	{ 0x09, 6274509UL},
41b70ef010SBorislav Petkov 	{ 0x0A, 3121951UL},
42b70ef010SBorislav Petkov 	{ 0x0B, 1560975UL},
43b70ef010SBorislav Petkov 	{ 0x0C, 781440UL},
44b70ef010SBorislav Petkov 	{ 0x0D, 390720UL},
45b70ef010SBorislav Petkov 	{ 0x0E, 195300UL},
46b70ef010SBorislav Petkov 	{ 0x0F, 97650UL},
47b70ef010SBorislav Petkov 	{ 0x10, 48854UL},
48b70ef010SBorislav Petkov 	{ 0x11, 24427UL},
49b70ef010SBorislav Petkov 	{ 0x12, 12213UL},
50b70ef010SBorislav Petkov 	{ 0x13, 6101UL},
51b70ef010SBorislav Petkov 	{ 0x14, 3051UL},
52b70ef010SBorislav Petkov 	{ 0x15, 1523UL},
53b70ef010SBorislav Petkov 	{ 0x16, 761UL},
54b70ef010SBorislav Petkov 	{ 0x00, 0UL},        /* scrubbing off */
55b70ef010SBorislav Petkov };
56b70ef010SBorislav Petkov 
5766fed2d4SBorislav Petkov int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset,
58b2b0c605SBorislav Petkov 			       u32 *val, const char *func)
59b2b0c605SBorislav Petkov {
60b2b0c605SBorislav Petkov 	int err = 0;
61b2b0c605SBorislav Petkov 
62b2b0c605SBorislav Petkov 	err = pci_read_config_dword(pdev, offset, val);
63b2b0c605SBorislav Petkov 	if (err)
64b2b0c605SBorislav Petkov 		amd64_warn("%s: error reading F%dx%03x.\n",
65b2b0c605SBorislav Petkov 			   func, PCI_FUNC(pdev->devfn), offset);
66b2b0c605SBorislav Petkov 
67b2b0c605SBorislav Petkov 	return err;
68b2b0c605SBorislav Petkov }
69b2b0c605SBorislav Petkov 
70b2b0c605SBorislav Petkov int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset,
71b2b0c605SBorislav Petkov 				u32 val, const char *func)
72b2b0c605SBorislav Petkov {
73b2b0c605SBorislav Petkov 	int err = 0;
74b2b0c605SBorislav Petkov 
75b2b0c605SBorislav Petkov 	err = pci_write_config_dword(pdev, offset, val);
76b2b0c605SBorislav Petkov 	if (err)
77b2b0c605SBorislav Petkov 		amd64_warn("%s: error writing to F%dx%03x.\n",
78b2b0c605SBorislav Petkov 			   func, PCI_FUNC(pdev->devfn), offset);
79b2b0c605SBorislav Petkov 
80b2b0c605SBorislav Petkov 	return err;
81b2b0c605SBorislav Petkov }
82b2b0c605SBorislav Petkov 
83b2b0c605SBorislav Petkov /*
8473ba8593SBorislav Petkov  * Select DCT to which PCI cfg accesses are routed
8573ba8593SBorislav Petkov  */
8673ba8593SBorislav Petkov static void f15h_select_dct(struct amd64_pvt *pvt, u8 dct)
8773ba8593SBorislav Petkov {
8873ba8593SBorislav Petkov 	u32 reg = 0;
8973ba8593SBorislav Petkov 
9073ba8593SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, &reg);
917981a28fSAravind Gopalakrishnan 	reg &= (pvt->model == 0x30) ? ~3 : ~1;
9273ba8593SBorislav Petkov 	reg |= dct;
9373ba8593SBorislav Petkov 	amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg);
9473ba8593SBorislav Petkov }
9573ba8593SBorislav Petkov 
967981a28fSAravind Gopalakrishnan /*
977981a28fSAravind Gopalakrishnan  *
987981a28fSAravind Gopalakrishnan  * Depending on the family, F2 DCT reads need special handling:
997981a28fSAravind Gopalakrishnan  *
1007981a28fSAravind Gopalakrishnan  * K8: has a single DCT only and no address offsets >= 0x100
1017981a28fSAravind Gopalakrishnan  *
1027981a28fSAravind Gopalakrishnan  * F10h: each DCT has its own set of regs
1037981a28fSAravind Gopalakrishnan  *	DCT0 -> F2x040..
1047981a28fSAravind Gopalakrishnan  *	DCT1 -> F2x140..
1057981a28fSAravind Gopalakrishnan  *
1067981a28fSAravind Gopalakrishnan  * F16h: has only 1 DCT
1077981a28fSAravind Gopalakrishnan  *
1087981a28fSAravind Gopalakrishnan  * F15h: we select which DCT we access using F1x10C[DctCfgSel]
1097981a28fSAravind Gopalakrishnan  */
1107981a28fSAravind Gopalakrishnan static inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct,
1117981a28fSAravind Gopalakrishnan 					 int offset, u32 *val)
112b2b0c605SBorislav Petkov {
1137981a28fSAravind Gopalakrishnan 	switch (pvt->fam) {
1147981a28fSAravind Gopalakrishnan 	case 0xf:
1157981a28fSAravind Gopalakrishnan 		if (dct || offset >= 0x100)
1167981a28fSAravind Gopalakrishnan 			return -EINVAL;
1177981a28fSAravind Gopalakrishnan 		break;
118b2b0c605SBorislav Petkov 
1197981a28fSAravind Gopalakrishnan 	case 0x10:
1207981a28fSAravind Gopalakrishnan 		if (dct) {
1217981a28fSAravind Gopalakrishnan 			/*
1227981a28fSAravind Gopalakrishnan 			 * Note: If ganging is enabled, barring the regs
1237981a28fSAravind Gopalakrishnan 			 * F2x[1,0]98 and F2x[1,0]9C; reads reads to F2x1xx
1247981a28fSAravind Gopalakrishnan 			 * return 0. (cf. Section 2.8.1 F10h BKDG)
1257981a28fSAravind Gopalakrishnan 			 */
1267981a28fSAravind Gopalakrishnan 			if (dct_ganging_enabled(pvt))
1277981a28fSAravind Gopalakrishnan 				return 0;
1287981a28fSAravind Gopalakrishnan 
1297981a28fSAravind Gopalakrishnan 			offset += 0x100;
130b2b0c605SBorislav Petkov 		}
1317981a28fSAravind Gopalakrishnan 		break;
132b2b0c605SBorislav Petkov 
1337981a28fSAravind Gopalakrishnan 	case 0x15:
1347981a28fSAravind Gopalakrishnan 		/*
1357981a28fSAravind Gopalakrishnan 		 * F15h: F2x1xx addresses do not map explicitly to DCT1.
1367981a28fSAravind Gopalakrishnan 		 * We should select which DCT we access using F1x10C[DctCfgSel]
1377981a28fSAravind Gopalakrishnan 		 */
1387981a28fSAravind Gopalakrishnan 		dct = (dct && pvt->model == 0x30) ? 3 : dct;
13973ba8593SBorislav Petkov 		f15h_select_dct(pvt, dct);
1407981a28fSAravind Gopalakrishnan 		break;
141b2b0c605SBorislav Petkov 
1427981a28fSAravind Gopalakrishnan 	case 0x16:
1437981a28fSAravind Gopalakrishnan 		if (dct)
1447981a28fSAravind Gopalakrishnan 			return -EINVAL;
1457981a28fSAravind Gopalakrishnan 		break;
1467981a28fSAravind Gopalakrishnan 
1477981a28fSAravind Gopalakrishnan 	default:
1487981a28fSAravind Gopalakrishnan 		break;
1497981a28fSAravind Gopalakrishnan 	}
1507981a28fSAravind Gopalakrishnan 	return amd64_read_pci_cfg(pvt->F2, offset, val);
151b2b0c605SBorislav Petkov }
152b2b0c605SBorislav Petkov 
153b70ef010SBorislav Petkov /*
1542bc65418SDoug Thompson  * Memory scrubber control interface. For K8, memory scrubbing is handled by
1552bc65418SDoug Thompson  * hardware and can involve L2 cache, dcache as well as the main memory. With
1562bc65418SDoug Thompson  * F10, this is extended to L3 cache scrubbing on CPU models sporting that
1572bc65418SDoug Thompson  * functionality.
1582bc65418SDoug Thompson  *
1592bc65418SDoug Thompson  * This causes the "units" for the scrubbing speed to vary from 64 byte blocks
1602bc65418SDoug Thompson  * (dram) over to cache lines. This is nasty, so we will use bandwidth in
1612bc65418SDoug Thompson  * bytes/sec for the setting.
1622bc65418SDoug Thompson  *
1632bc65418SDoug Thompson  * Currently, we only do dram scrubbing. If the scrubbing is done in software on
1642bc65418SDoug Thompson  * other archs, we might not have access to the caches directly.
1652bc65418SDoug Thompson  */
1662bc65418SDoug Thompson 
1678051c0afSYazen Ghannam static inline void __f17h_set_scrubval(struct amd64_pvt *pvt, u32 scrubval)
1688051c0afSYazen Ghannam {
1692bc65418SDoug Thompson 	/*
1708051c0afSYazen Ghannam 	 * Fam17h supports scrub values between 0x5 and 0x14. Also, the values
1718051c0afSYazen Ghannam 	 * are shifted down by 0x5, so scrubval 0x5 is written to the register
1728051c0afSYazen Ghannam 	 * as 0x0, scrubval 0x6 as 0x1, etc.
1738051c0afSYazen Ghannam 	 */
1748051c0afSYazen Ghannam 	if (scrubval >= 0x5 && scrubval <= 0x14) {
1758051c0afSYazen Ghannam 		scrubval -= 0x5;
1768051c0afSYazen Ghannam 		pci_write_bits32(pvt->F6, F17H_SCR_LIMIT_ADDR, scrubval, 0xF);
1778051c0afSYazen Ghannam 		pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 1, 0x1);
1788051c0afSYazen Ghannam 	} else {
1798051c0afSYazen Ghannam 		pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 0, 0x1);
1808051c0afSYazen Ghannam 	}
1818051c0afSYazen Ghannam }
1828051c0afSYazen Ghannam /*
1838051c0afSYazen Ghannam  * Scan the scrub rate mapping table for a close or matching bandwidth value to
1842bc65418SDoug Thompson  * issue. If requested is too big, then use last maximum value found.
1852bc65418SDoug Thompson  */
186da92110dSAravind Gopalakrishnan static int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate)
1872bc65418SDoug Thompson {
1882bc65418SDoug Thompson 	u32 scrubval;
1892bc65418SDoug Thompson 	int i;
1902bc65418SDoug Thompson 
1912bc65418SDoug Thompson 	/*
1922bc65418SDoug Thompson 	 * map the configured rate (new_bw) to a value specific to the AMD64
1932bc65418SDoug Thompson 	 * memory controller and apply to register. Search for the first
1942bc65418SDoug Thompson 	 * bandwidth entry that is greater or equal than the setting requested
1952bc65418SDoug Thompson 	 * and program that. If at last entry, turn off DRAM scrubbing.
196168bfeefSAndrew Morton 	 *
197168bfeefSAndrew Morton 	 * If no suitable bandwidth is found, turn off DRAM scrubbing entirely
198168bfeefSAndrew Morton 	 * by falling back to the last element in scrubrates[].
1992bc65418SDoug Thompson 	 */
200168bfeefSAndrew Morton 	for (i = 0; i < ARRAY_SIZE(scrubrates) - 1; i++) {
2012bc65418SDoug Thompson 		/*
2022bc65418SDoug Thompson 		 * skip scrub rates which aren't recommended
2032bc65418SDoug Thompson 		 * (see F10 BKDG, F3x58)
2042bc65418SDoug Thompson 		 */
205395ae783SBorislav Petkov 		if (scrubrates[i].scrubval < min_rate)
2062bc65418SDoug Thompson 			continue;
2072bc65418SDoug Thompson 
2082bc65418SDoug Thompson 		if (scrubrates[i].bandwidth <= new_bw)
2092bc65418SDoug Thompson 			break;
2102bc65418SDoug Thompson 	}
2112bc65418SDoug Thompson 
2122bc65418SDoug Thompson 	scrubval = scrubrates[i].scrubval;
2132bc65418SDoug Thompson 
2148051c0afSYazen Ghannam 	if (pvt->fam == 0x17) {
2158051c0afSYazen Ghannam 		__f17h_set_scrubval(pvt, scrubval);
2168051c0afSYazen Ghannam 	} else if (pvt->fam == 0x15 && pvt->model == 0x60) {
217da92110dSAravind Gopalakrishnan 		f15h_select_dct(pvt, 0);
218da92110dSAravind Gopalakrishnan 		pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
219da92110dSAravind Gopalakrishnan 		f15h_select_dct(pvt, 1);
220da92110dSAravind Gopalakrishnan 		pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
221da92110dSAravind Gopalakrishnan 	} else {
222da92110dSAravind Gopalakrishnan 		pci_write_bits32(pvt->F3, SCRCTRL, scrubval, 0x001F);
223da92110dSAravind Gopalakrishnan 	}
2242bc65418SDoug Thompson 
22539094443SBorislav Petkov 	if (scrubval)
22639094443SBorislav Petkov 		return scrubrates[i].bandwidth;
22739094443SBorislav Petkov 
2282bc65418SDoug Thompson 	return 0;
2292bc65418SDoug Thompson }
2302bc65418SDoug Thompson 
231d1ea71cdSBorislav Petkov static int set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
2322bc65418SDoug Thompson {
2332bc65418SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
23487b3e0e6SBorislav Petkov 	u32 min_scrubrate = 0x5;
2352bc65418SDoug Thompson 
236a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
23787b3e0e6SBorislav Petkov 		min_scrubrate = 0x0;
23887b3e0e6SBorislav Petkov 
239da92110dSAravind Gopalakrishnan 	if (pvt->fam == 0x15) {
2403f0aba4fSBorislav Petkov 		/* Erratum #505 */
241da92110dSAravind Gopalakrishnan 		if (pvt->model < 0x10)
24273ba8593SBorislav Petkov 			f15h_select_dct(pvt, 0);
24373ba8593SBorislav Petkov 
244da92110dSAravind Gopalakrishnan 		if (pvt->model == 0x60)
245da92110dSAravind Gopalakrishnan 			min_scrubrate = 0x6;
246da92110dSAravind Gopalakrishnan 	}
247da92110dSAravind Gopalakrishnan 	return __set_scrub_rate(pvt, bw, min_scrubrate);
2482bc65418SDoug Thompson }
2492bc65418SDoug Thompson 
250d1ea71cdSBorislav Petkov static int get_scrub_rate(struct mem_ctl_info *mci)
2512bc65418SDoug Thompson {
2522bc65418SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
25339094443SBorislav Petkov 	int i, retval = -EINVAL;
2548051c0afSYazen Ghannam 	u32 scrubval = 0;
2552bc65418SDoug Thompson 
2568051c0afSYazen Ghannam 	switch (pvt->fam) {
2578051c0afSYazen Ghannam 	case 0x15:
2583f0aba4fSBorislav Petkov 		/* Erratum #505 */
259da92110dSAravind Gopalakrishnan 		if (pvt->model < 0x10)
26073ba8593SBorislav Petkov 			f15h_select_dct(pvt, 0);
26173ba8593SBorislav Petkov 
262da92110dSAravind Gopalakrishnan 		if (pvt->model == 0x60)
263da92110dSAravind Gopalakrishnan 			amd64_read_pci_cfg(pvt->F2, F15H_M60H_SCRCTRL, &scrubval);
2648051c0afSYazen Ghannam 		break;
2658051c0afSYazen Ghannam 
2668051c0afSYazen Ghannam 	case 0x17:
2678051c0afSYazen Ghannam 		amd64_read_pci_cfg(pvt->F6, F17H_SCR_BASE_ADDR, &scrubval);
2688051c0afSYazen Ghannam 		if (scrubval & BIT(0)) {
2698051c0afSYazen Ghannam 			amd64_read_pci_cfg(pvt->F6, F17H_SCR_LIMIT_ADDR, &scrubval);
2708051c0afSYazen Ghannam 			scrubval &= 0xF;
2718051c0afSYazen Ghannam 			scrubval += 0x5;
2728051c0afSYazen Ghannam 		} else {
2738051c0afSYazen Ghannam 			scrubval = 0;
2748051c0afSYazen Ghannam 		}
2758051c0afSYazen Ghannam 		break;
2768051c0afSYazen Ghannam 
2778051c0afSYazen Ghannam 	default:
2785980bb9cSBorislav Petkov 		amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
2798051c0afSYazen Ghannam 		break;
2808051c0afSYazen Ghannam 	}
2812bc65418SDoug Thompson 
2822bc65418SDoug Thompson 	scrubval = scrubval & 0x001F;
2832bc65418SDoug Thompson 
284926311fdSRoel Kluin 	for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
2852bc65418SDoug Thompson 		if (scrubrates[i].scrubval == scrubval) {
28639094443SBorislav Petkov 			retval = scrubrates[i].bandwidth;
2872bc65418SDoug Thompson 			break;
2882bc65418SDoug Thompson 		}
2892bc65418SDoug Thompson 	}
29039094443SBorislav Petkov 	return retval;
2912bc65418SDoug Thompson }
2922bc65418SDoug Thompson 
2936775763aSDoug Thompson /*
2947f19bf75SBorislav Petkov  * returns true if the SysAddr given by sys_addr matches the
2957f19bf75SBorislav Petkov  * DRAM base/limit associated with node_id
2966775763aSDoug Thompson  */
297d1ea71cdSBorislav Petkov static bool base_limit_match(struct amd64_pvt *pvt, u64 sys_addr, u8 nid)
2986775763aSDoug Thompson {
2997f19bf75SBorislav Petkov 	u64 addr;
3006775763aSDoug Thompson 
3016775763aSDoug Thompson 	/* The K8 treats this as a 40-bit value.  However, bits 63-40 will be
3026775763aSDoug Thompson 	 * all ones if the most significant implemented address bit is 1.
3036775763aSDoug Thompson 	 * Here we discard bits 63-40.  See section 3.4.2 of AMD publication
3046775763aSDoug Thompson 	 * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1
3056775763aSDoug Thompson 	 * Application Programming.
3066775763aSDoug Thompson 	 */
3076775763aSDoug Thompson 	addr = sys_addr & 0x000000ffffffffffull;
3086775763aSDoug Thompson 
3097f19bf75SBorislav Petkov 	return ((addr >= get_dram_base(pvt, nid)) &&
3107f19bf75SBorislav Petkov 		(addr <= get_dram_limit(pvt, nid)));
3116775763aSDoug Thompson }
3126775763aSDoug Thompson 
3136775763aSDoug Thompson /*
3146775763aSDoug Thompson  * Attempt to map a SysAddr to a node. On success, return a pointer to the
3156775763aSDoug Thompson  * mem_ctl_info structure for the node that the SysAddr maps to.
3166775763aSDoug Thompson  *
3176775763aSDoug Thompson  * On failure, return NULL.
3186775763aSDoug Thompson  */
3196775763aSDoug Thompson static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci,
3206775763aSDoug Thompson 						u64 sys_addr)
3216775763aSDoug Thompson {
3226775763aSDoug Thompson 	struct amd64_pvt *pvt;
323c7e5301aSDaniel J Blueman 	u8 node_id;
3246775763aSDoug Thompson 	u32 intlv_en, bits;
3256775763aSDoug Thompson 
3266775763aSDoug Thompson 	/*
3276775763aSDoug Thompson 	 * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section
3286775763aSDoug Thompson 	 * 3.4.4.2) registers to map the SysAddr to a node ID.
3296775763aSDoug Thompson 	 */
3306775763aSDoug Thompson 	pvt = mci->pvt_info;
3316775763aSDoug Thompson 
3326775763aSDoug Thompson 	/*
3336775763aSDoug Thompson 	 * The value of this field should be the same for all DRAM Base
3346775763aSDoug Thompson 	 * registers.  Therefore we arbitrarily choose to read it from the
3356775763aSDoug Thompson 	 * register for node 0.
3366775763aSDoug Thompson 	 */
3377f19bf75SBorislav Petkov 	intlv_en = dram_intlv_en(pvt, 0);
3386775763aSDoug Thompson 
3396775763aSDoug Thompson 	if (intlv_en == 0) {
3407f19bf75SBorislav Petkov 		for (node_id = 0; node_id < DRAM_RANGES; node_id++) {
341d1ea71cdSBorislav Petkov 			if (base_limit_match(pvt, sys_addr, node_id))
3426775763aSDoug Thompson 				goto found;
3436775763aSDoug Thompson 		}
3448edc5445SBorislav Petkov 		goto err_no_match;
3458edc5445SBorislav Petkov 	}
3466775763aSDoug Thompson 
34772f158feSBorislav Petkov 	if (unlikely((intlv_en != 0x01) &&
34872f158feSBorislav Petkov 		     (intlv_en != 0x03) &&
34972f158feSBorislav Petkov 		     (intlv_en != 0x07))) {
35024f9a7feSBorislav Petkov 		amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en);
3516775763aSDoug Thompson 		return NULL;
3526775763aSDoug Thompson 	}
3536775763aSDoug Thompson 
3546775763aSDoug Thompson 	bits = (((u32) sys_addr) >> 12) & intlv_en;
3556775763aSDoug Thompson 
3566775763aSDoug Thompson 	for (node_id = 0; ; ) {
3577f19bf75SBorislav Petkov 		if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits)
3586775763aSDoug Thompson 			break;	/* intlv_sel field matches */
3596775763aSDoug Thompson 
3607f19bf75SBorislav Petkov 		if (++node_id >= DRAM_RANGES)
3616775763aSDoug Thompson 			goto err_no_match;
3626775763aSDoug Thompson 	}
3636775763aSDoug Thompson 
3646775763aSDoug Thompson 	/* sanity test for sys_addr */
365d1ea71cdSBorislav Petkov 	if (unlikely(!base_limit_match(pvt, sys_addr, node_id))) {
36624f9a7feSBorislav Petkov 		amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address"
36724f9a7feSBorislav Petkov 			   "range for node %d with node interleaving enabled.\n",
3688edc5445SBorislav Petkov 			   __func__, sys_addr, node_id);
3696775763aSDoug Thompson 		return NULL;
3706775763aSDoug Thompson 	}
3716775763aSDoug Thompson 
3726775763aSDoug Thompson found:
373b487c33eSBorislav Petkov 	return edac_mc_find((int)node_id);
3746775763aSDoug Thompson 
3756775763aSDoug Thompson err_no_match:
376956b9ba1SJoe Perches 	edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n",
3776775763aSDoug Thompson 		 (unsigned long)sys_addr);
3786775763aSDoug Thompson 
3796775763aSDoug Thompson 	return NULL;
3806775763aSDoug Thompson }
381e2ce7255SDoug Thompson 
382e2ce7255SDoug Thompson /*
38311c75eadSBorislav Petkov  * compute the CS base address of the @csrow on the DRAM controller @dct.
38411c75eadSBorislav Petkov  * For details see F2x[5C:40] in the processor's BKDG
385e2ce7255SDoug Thompson  */
38611c75eadSBorislav Petkov static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct,
38711c75eadSBorislav Petkov 				 u64 *base, u64 *mask)
388e2ce7255SDoug Thompson {
38911c75eadSBorislav Petkov 	u64 csbase, csmask, base_bits, mask_bits;
39011c75eadSBorislav Petkov 	u8 addr_shift;
39111c75eadSBorislav Petkov 
39218b94f66SAravind Gopalakrishnan 	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
39311c75eadSBorislav Petkov 		csbase		= pvt->csels[dct].csbases[csrow];
39411c75eadSBorislav Petkov 		csmask		= pvt->csels[dct].csmasks[csrow];
39510ef6b0dSChen, Gong 		base_bits	= GENMASK_ULL(31, 21) | GENMASK_ULL(15, 9);
39610ef6b0dSChen, Gong 		mask_bits	= GENMASK_ULL(29, 21) | GENMASK_ULL(15, 9);
39711c75eadSBorislav Petkov 		addr_shift	= 4;
39894c1acf2SAravind Gopalakrishnan 
39994c1acf2SAravind Gopalakrishnan 	/*
40018b94f66SAravind Gopalakrishnan 	 * F16h and F15h, models 30h and later need two addr_shift values:
40118b94f66SAravind Gopalakrishnan 	 * 8 for high and 6 for low (cf. F16h BKDG).
40294c1acf2SAravind Gopalakrishnan 	 */
40318b94f66SAravind Gopalakrishnan 	} else if (pvt->fam == 0x16 ||
40418b94f66SAravind Gopalakrishnan 		  (pvt->fam == 0x15 && pvt->model >= 0x30)) {
40594c1acf2SAravind Gopalakrishnan 		csbase          = pvt->csels[dct].csbases[csrow];
40694c1acf2SAravind Gopalakrishnan 		csmask          = pvt->csels[dct].csmasks[csrow >> 1];
40794c1acf2SAravind Gopalakrishnan 
40810ef6b0dSChen, Gong 		*base  = (csbase & GENMASK_ULL(15,  5)) << 6;
40910ef6b0dSChen, Gong 		*base |= (csbase & GENMASK_ULL(30, 19)) << 8;
41094c1acf2SAravind Gopalakrishnan 
41194c1acf2SAravind Gopalakrishnan 		*mask = ~0ULL;
41294c1acf2SAravind Gopalakrishnan 		/* poke holes for the csmask */
41310ef6b0dSChen, Gong 		*mask &= ~((GENMASK_ULL(15, 5)  << 6) |
41410ef6b0dSChen, Gong 			   (GENMASK_ULL(30, 19) << 8));
41594c1acf2SAravind Gopalakrishnan 
41610ef6b0dSChen, Gong 		*mask |= (csmask & GENMASK_ULL(15, 5))  << 6;
41710ef6b0dSChen, Gong 		*mask |= (csmask & GENMASK_ULL(30, 19)) << 8;
41894c1acf2SAravind Gopalakrishnan 
41994c1acf2SAravind Gopalakrishnan 		return;
42011c75eadSBorislav Petkov 	} else {
42111c75eadSBorislav Petkov 		csbase		= pvt->csels[dct].csbases[csrow];
42211c75eadSBorislav Petkov 		csmask		= pvt->csels[dct].csmasks[csrow >> 1];
42311c75eadSBorislav Petkov 		addr_shift	= 8;
42411c75eadSBorislav Petkov 
425a4b4bedcSBorislav Petkov 		if (pvt->fam == 0x15)
42610ef6b0dSChen, Gong 			base_bits = mask_bits =
42710ef6b0dSChen, Gong 				GENMASK_ULL(30,19) | GENMASK_ULL(13,5);
42811c75eadSBorislav Petkov 		else
42910ef6b0dSChen, Gong 			base_bits = mask_bits =
43010ef6b0dSChen, Gong 				GENMASK_ULL(28,19) | GENMASK_ULL(13,5);
431e2ce7255SDoug Thompson 	}
432e2ce7255SDoug Thompson 
43311c75eadSBorislav Petkov 	*base  = (csbase & base_bits) << addr_shift;
434e2ce7255SDoug Thompson 
43511c75eadSBorislav Petkov 	*mask  = ~0ULL;
43611c75eadSBorislav Petkov 	/* poke holes for the csmask */
43711c75eadSBorislav Petkov 	*mask &= ~(mask_bits << addr_shift);
43811c75eadSBorislav Petkov 	/* OR them in */
43911c75eadSBorislav Petkov 	*mask |= (csmask & mask_bits) << addr_shift;
440e2ce7255SDoug Thompson }
441e2ce7255SDoug Thompson 
44211c75eadSBorislav Petkov #define for_each_chip_select(i, dct, pvt) \
44311c75eadSBorislav Petkov 	for (i = 0; i < pvt->csels[dct].b_cnt; i++)
44411c75eadSBorislav Petkov 
445614ec9d8SBorislav Petkov #define chip_select_base(i, dct, pvt) \
446614ec9d8SBorislav Petkov 	pvt->csels[dct].csbases[i]
447614ec9d8SBorislav Petkov 
44811c75eadSBorislav Petkov #define for_each_chip_select_mask(i, dct, pvt) \
44911c75eadSBorislav Petkov 	for (i = 0; i < pvt->csels[dct].m_cnt; i++)
45011c75eadSBorislav Petkov 
451e2ce7255SDoug Thompson /*
452e2ce7255SDoug Thompson  * @input_addr is an InputAddr associated with the node given by mci. Return the
453e2ce7255SDoug Thompson  * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr).
454e2ce7255SDoug Thompson  */
455e2ce7255SDoug Thompson static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr)
456e2ce7255SDoug Thompson {
457e2ce7255SDoug Thompson 	struct amd64_pvt *pvt;
458e2ce7255SDoug Thompson 	int csrow;
459e2ce7255SDoug Thompson 	u64 base, mask;
460e2ce7255SDoug Thompson 
461e2ce7255SDoug Thompson 	pvt = mci->pvt_info;
462e2ce7255SDoug Thompson 
46311c75eadSBorislav Petkov 	for_each_chip_select(csrow, 0, pvt) {
46411c75eadSBorislav Petkov 		if (!csrow_enabled(csrow, 0, pvt))
465e2ce7255SDoug Thompson 			continue;
466e2ce7255SDoug Thompson 
46711c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, csrow, 0, &base, &mask);
46811c75eadSBorislav Petkov 
46911c75eadSBorislav Petkov 		mask = ~mask;
470e2ce7255SDoug Thompson 
471e2ce7255SDoug Thompson 		if ((input_addr & mask) == (base & mask)) {
472956b9ba1SJoe Perches 			edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n",
473e2ce7255SDoug Thompson 				 (unsigned long)input_addr, csrow,
474e2ce7255SDoug Thompson 				 pvt->mc_node_id);
475e2ce7255SDoug Thompson 
476e2ce7255SDoug Thompson 			return csrow;
477e2ce7255SDoug Thompson 		}
478e2ce7255SDoug Thompson 	}
479956b9ba1SJoe Perches 	edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n",
480e2ce7255SDoug Thompson 		 (unsigned long)input_addr, pvt->mc_node_id);
481e2ce7255SDoug Thompson 
482e2ce7255SDoug Thompson 	return -1;
483e2ce7255SDoug Thompson }
484e2ce7255SDoug Thompson 
485e2ce7255SDoug Thompson /*
486e2ce7255SDoug Thompson  * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094)
487e2ce7255SDoug Thompson  * for the node represented by mci. Info is passed back in *hole_base,
488e2ce7255SDoug Thompson  * *hole_offset, and *hole_size.  Function returns 0 if info is valid or 1 if
489e2ce7255SDoug Thompson  * info is invalid. Info may be invalid for either of the following reasons:
490e2ce7255SDoug Thompson  *
491e2ce7255SDoug Thompson  * - The revision of the node is not E or greater.  In this case, the DRAM Hole
492e2ce7255SDoug Thompson  *   Address Register does not exist.
493e2ce7255SDoug Thompson  *
494e2ce7255SDoug Thompson  * - The DramHoleValid bit is cleared in the DRAM Hole Address Register,
495e2ce7255SDoug Thompson  *   indicating that its contents are not valid.
496e2ce7255SDoug Thompson  *
497e2ce7255SDoug Thompson  * The values passed back in *hole_base, *hole_offset, and *hole_size are
498e2ce7255SDoug Thompson  * complete 32-bit values despite the fact that the bitfields in the DHAR
499e2ce7255SDoug Thompson  * only represent bits 31-24 of the base and offset values.
500e2ce7255SDoug Thompson  */
501e2ce7255SDoug Thompson int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
502e2ce7255SDoug Thompson 			     u64 *hole_offset, u64 *hole_size)
503e2ce7255SDoug Thompson {
504e2ce7255SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
505e2ce7255SDoug Thompson 
506e2ce7255SDoug Thompson 	/* only revE and later have the DRAM Hole Address Register */
507a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_E) {
508956b9ba1SJoe Perches 		edac_dbg(1, "  revision %d for node %d does not support DHAR\n",
509e2ce7255SDoug Thompson 			 pvt->ext_model, pvt->mc_node_id);
510e2ce7255SDoug Thompson 		return 1;
511e2ce7255SDoug Thompson 	}
512e2ce7255SDoug Thompson 
513bc21fa57SBorislav Petkov 	/* valid for Fam10h and above */
514a4b4bedcSBorislav Petkov 	if (pvt->fam >= 0x10 && !dhar_mem_hoist_valid(pvt)) {
515956b9ba1SJoe Perches 		edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this system\n");
516e2ce7255SDoug Thompson 		return 1;
517e2ce7255SDoug Thompson 	}
518e2ce7255SDoug Thompson 
519c8e518d5SBorislav Petkov 	if (!dhar_valid(pvt)) {
520956b9ba1SJoe Perches 		edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this node %d\n",
521e2ce7255SDoug Thompson 			 pvt->mc_node_id);
522e2ce7255SDoug Thompson 		return 1;
523e2ce7255SDoug Thompson 	}
524e2ce7255SDoug Thompson 
525e2ce7255SDoug Thompson 	/* This node has Memory Hoisting */
526e2ce7255SDoug Thompson 
527e2ce7255SDoug Thompson 	/* +------------------+--------------------+--------------------+-----
528e2ce7255SDoug Thompson 	 * | memory           | DRAM hole          | relocated          |
529e2ce7255SDoug Thompson 	 * | [0, (x - 1)]     | [x, 0xffffffff]    | addresses from     |
530e2ce7255SDoug Thompson 	 * |                  |                    | DRAM hole          |
531e2ce7255SDoug Thompson 	 * |                  |                    | [0x100000000,      |
532e2ce7255SDoug Thompson 	 * |                  |                    |  (0x100000000+     |
533e2ce7255SDoug Thompson 	 * |                  |                    |   (0xffffffff-x))] |
534e2ce7255SDoug Thompson 	 * +------------------+--------------------+--------------------+-----
535e2ce7255SDoug Thompson 	 *
536e2ce7255SDoug Thompson 	 * Above is a diagram of physical memory showing the DRAM hole and the
537e2ce7255SDoug Thompson 	 * relocated addresses from the DRAM hole.  As shown, the DRAM hole
538e2ce7255SDoug Thompson 	 * starts at address x (the base address) and extends through address
539e2ce7255SDoug Thompson 	 * 0xffffffff.  The DRAM Hole Address Register (DHAR) relocates the
540e2ce7255SDoug Thompson 	 * addresses in the hole so that they start at 0x100000000.
541e2ce7255SDoug Thompson 	 */
542e2ce7255SDoug Thompson 
5431f31677eSBorislav Petkov 	*hole_base = dhar_base(pvt);
5441f31677eSBorislav Petkov 	*hole_size = (1ULL << 32) - *hole_base;
545e2ce7255SDoug Thompson 
546a4b4bedcSBorislav Petkov 	*hole_offset = (pvt->fam > 0xf) ? f10_dhar_offset(pvt)
547a4b4bedcSBorislav Petkov 					: k8_dhar_offset(pvt);
548e2ce7255SDoug Thompson 
549956b9ba1SJoe Perches 	edac_dbg(1, "  DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
550e2ce7255SDoug Thompson 		 pvt->mc_node_id, (unsigned long)*hole_base,
551e2ce7255SDoug Thompson 		 (unsigned long)*hole_offset, (unsigned long)*hole_size);
552e2ce7255SDoug Thompson 
553e2ce7255SDoug Thompson 	return 0;
554e2ce7255SDoug Thompson }
555e2ce7255SDoug Thompson EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info);
556e2ce7255SDoug Thompson 
55793c2df58SDoug Thompson /*
55893c2df58SDoug Thompson  * Return the DramAddr that the SysAddr given by @sys_addr maps to.  It is
55993c2df58SDoug Thompson  * assumed that sys_addr maps to the node given by mci.
56093c2df58SDoug Thompson  *
56193c2df58SDoug Thompson  * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section
56293c2df58SDoug Thompson  * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a
56393c2df58SDoug Thompson  * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled,
56493c2df58SDoug Thompson  * then it is also involved in translating a SysAddr to a DramAddr. Sections
56593c2df58SDoug Thompson  * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting.
56693c2df58SDoug Thompson  * These parts of the documentation are unclear. I interpret them as follows:
56793c2df58SDoug Thompson  *
56893c2df58SDoug Thompson  * When node n receives a SysAddr, it processes the SysAddr as follows:
56993c2df58SDoug Thompson  *
57093c2df58SDoug Thompson  * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM
57193c2df58SDoug Thompson  *    Limit registers for node n. If the SysAddr is not within the range
57293c2df58SDoug Thompson  *    specified by the base and limit values, then node n ignores the Sysaddr
57393c2df58SDoug Thompson  *    (since it does not map to node n). Otherwise continue to step 2 below.
57493c2df58SDoug Thompson  *
57593c2df58SDoug Thompson  * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is
57693c2df58SDoug Thompson  *    disabled so skip to step 3 below. Otherwise see if the SysAddr is within
57793c2df58SDoug Thompson  *    the range of relocated addresses (starting at 0x100000000) from the DRAM
57893c2df58SDoug Thompson  *    hole. If not, skip to step 3 below. Else get the value of the
57993c2df58SDoug Thompson  *    DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the
58093c2df58SDoug Thompson  *    offset defined by this value from the SysAddr.
58193c2df58SDoug Thompson  *
58293c2df58SDoug Thompson  * 3. Obtain the base address for node n from the DRAMBase field of the DRAM
58393c2df58SDoug Thompson  *    Base register for node n. To obtain the DramAddr, subtract the base
58493c2df58SDoug Thompson  *    address from the SysAddr, as shown near the start of section 3.4.4 (p.70).
58593c2df58SDoug Thompson  */
58693c2df58SDoug Thompson static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
58793c2df58SDoug Thompson {
5887f19bf75SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
58993c2df58SDoug Thompson 	u64 dram_base, hole_base, hole_offset, hole_size, dram_addr;
5901f31677eSBorislav Petkov 	int ret;
59193c2df58SDoug Thompson 
5927f19bf75SBorislav Petkov 	dram_base = get_dram_base(pvt, pvt->mc_node_id);
59393c2df58SDoug Thompson 
59493c2df58SDoug Thompson 	ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
59593c2df58SDoug Thompson 				      &hole_size);
59693c2df58SDoug Thompson 	if (!ret) {
5971f31677eSBorislav Petkov 		if ((sys_addr >= (1ULL << 32)) &&
5981f31677eSBorislav Petkov 		    (sys_addr < ((1ULL << 32) + hole_size))) {
59993c2df58SDoug Thompson 			/* use DHAR to translate SysAddr to DramAddr */
60093c2df58SDoug Thompson 			dram_addr = sys_addr - hole_offset;
60193c2df58SDoug Thompson 
602956b9ba1SJoe Perches 			edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
60393c2df58SDoug Thompson 				 (unsigned long)sys_addr,
60493c2df58SDoug Thompson 				 (unsigned long)dram_addr);
60593c2df58SDoug Thompson 
60693c2df58SDoug Thompson 			return dram_addr;
60793c2df58SDoug Thompson 		}
60893c2df58SDoug Thompson 	}
60993c2df58SDoug Thompson 
61093c2df58SDoug Thompson 	/*
61193c2df58SDoug Thompson 	 * Translate the SysAddr to a DramAddr as shown near the start of
61293c2df58SDoug Thompson 	 * section 3.4.4 (p. 70).  Although sys_addr is a 64-bit value, the k8
61393c2df58SDoug Thompson 	 * only deals with 40-bit values.  Therefore we discard bits 63-40 of
61493c2df58SDoug Thompson 	 * sys_addr below.  If bit 39 of sys_addr is 1 then the bits we
61593c2df58SDoug Thompson 	 * discard are all 1s.  Otherwise the bits we discard are all 0s.  See
61693c2df58SDoug Thompson 	 * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture
61793c2df58SDoug Thompson 	 * Programmer's Manual Volume 1 Application Programming.
61893c2df58SDoug Thompson 	 */
61910ef6b0dSChen, Gong 	dram_addr = (sys_addr & GENMASK_ULL(39, 0)) - dram_base;
62093c2df58SDoug Thompson 
621956b9ba1SJoe Perches 	edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
622956b9ba1SJoe Perches 		 (unsigned long)sys_addr, (unsigned long)dram_addr);
62393c2df58SDoug Thompson 	return dram_addr;
62493c2df58SDoug Thompson }
62593c2df58SDoug Thompson 
62693c2df58SDoug Thompson /*
62793c2df58SDoug Thompson  * @intlv_en is the value of the IntlvEn field from a DRAM Base register
62893c2df58SDoug Thompson  * (section 3.4.4.1).  Return the number of bits from a SysAddr that are used
62993c2df58SDoug Thompson  * for node interleaving.
63093c2df58SDoug Thompson  */
63193c2df58SDoug Thompson static int num_node_interleave_bits(unsigned intlv_en)
63293c2df58SDoug Thompson {
63393c2df58SDoug Thompson 	static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 };
63493c2df58SDoug Thompson 	int n;
63593c2df58SDoug Thompson 
63693c2df58SDoug Thompson 	BUG_ON(intlv_en > 7);
63793c2df58SDoug Thompson 	n = intlv_shift_table[intlv_en];
63893c2df58SDoug Thompson 	return n;
63993c2df58SDoug Thompson }
64093c2df58SDoug Thompson 
64193c2df58SDoug Thompson /* Translate the DramAddr given by @dram_addr to an InputAddr. */
64293c2df58SDoug Thompson static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
64393c2df58SDoug Thompson {
64493c2df58SDoug Thompson 	struct amd64_pvt *pvt;
64593c2df58SDoug Thompson 	int intlv_shift;
64693c2df58SDoug Thompson 	u64 input_addr;
64793c2df58SDoug Thompson 
64893c2df58SDoug Thompson 	pvt = mci->pvt_info;
64993c2df58SDoug Thompson 
65093c2df58SDoug Thompson 	/*
65193c2df58SDoug Thompson 	 * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
65293c2df58SDoug Thompson 	 * concerning translating a DramAddr to an InputAddr.
65393c2df58SDoug Thompson 	 */
6547f19bf75SBorislav Petkov 	intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
65510ef6b0dSChen, Gong 	input_addr = ((dram_addr >> intlv_shift) & GENMASK_ULL(35, 12)) +
65693c2df58SDoug Thompson 		      (dram_addr & 0xfff);
65793c2df58SDoug Thompson 
658956b9ba1SJoe Perches 	edac_dbg(2, "  Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
65993c2df58SDoug Thompson 		 intlv_shift, (unsigned long)dram_addr,
66093c2df58SDoug Thompson 		 (unsigned long)input_addr);
66193c2df58SDoug Thompson 
66293c2df58SDoug Thompson 	return input_addr;
66393c2df58SDoug Thompson }
66493c2df58SDoug Thompson 
66593c2df58SDoug Thompson /*
66693c2df58SDoug Thompson  * Translate the SysAddr represented by @sys_addr to an InputAddr.  It is
66793c2df58SDoug Thompson  * assumed that @sys_addr maps to the node given by mci.
66893c2df58SDoug Thompson  */
66993c2df58SDoug Thompson static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
67093c2df58SDoug Thompson {
67193c2df58SDoug Thompson 	u64 input_addr;
67293c2df58SDoug Thompson 
67393c2df58SDoug Thompson 	input_addr =
67493c2df58SDoug Thompson 	    dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
67593c2df58SDoug Thompson 
676c19ca6cbSMasanari Iida 	edac_dbg(2, "SysAddr 0x%lx translates to InputAddr 0x%lx\n",
67793c2df58SDoug Thompson 		 (unsigned long)sys_addr, (unsigned long)input_addr);
67893c2df58SDoug Thompson 
67993c2df58SDoug Thompson 	return input_addr;
68093c2df58SDoug Thompson }
68193c2df58SDoug Thompson 
68293c2df58SDoug Thompson /* Map the Error address to a PAGE and PAGE OFFSET. */
68393c2df58SDoug Thompson static inline void error_address_to_page_and_offset(u64 error_address,
68433ca0643SBorislav Petkov 						    struct err_info *err)
68593c2df58SDoug Thompson {
68633ca0643SBorislav Petkov 	err->page = (u32) (error_address >> PAGE_SHIFT);
68733ca0643SBorislav Petkov 	err->offset = ((u32) error_address) & ~PAGE_MASK;
68893c2df58SDoug Thompson }
68993c2df58SDoug Thompson 
69093c2df58SDoug Thompson /*
69193c2df58SDoug Thompson  * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address
69293c2df58SDoug Thompson  * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers
69393c2df58SDoug Thompson  * of a node that detected an ECC memory error.  mci represents the node that
69493c2df58SDoug Thompson  * the error address maps to (possibly different from the node that detected
69593c2df58SDoug Thompson  * the error).  Return the number of the csrow that sys_addr maps to, or -1 on
69693c2df58SDoug Thompson  * error.
69793c2df58SDoug Thompson  */
69893c2df58SDoug Thompson static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
69993c2df58SDoug Thompson {
70093c2df58SDoug Thompson 	int csrow;
70193c2df58SDoug Thompson 
70293c2df58SDoug Thompson 	csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr));
70393c2df58SDoug Thompson 
70493c2df58SDoug Thompson 	if (csrow == -1)
70524f9a7feSBorislav Petkov 		amd64_mc_err(mci, "Failed to translate InputAddr to csrow for "
70693c2df58SDoug Thompson 				  "address 0x%lx\n", (unsigned long)sys_addr);
70793c2df58SDoug Thompson 	return csrow;
70893c2df58SDoug Thompson }
709e2ce7255SDoug Thompson 
710bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
7112da11654SDoug Thompson 
7122da11654SDoug Thompson /*
7132da11654SDoug Thompson  * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
7142da11654SDoug Thompson  * are ECC capable.
7152da11654SDoug Thompson  */
716d1ea71cdSBorislav Petkov static unsigned long determine_edac_cap(struct amd64_pvt *pvt)
7172da11654SDoug Thompson {
7181f6189edSDan Carpenter 	unsigned long edac_cap = EDAC_FLAG_NONE;
719d27f3a34SYazen Ghannam 	u8 bit;
7202da11654SDoug Thompson 
721d27f3a34SYazen Ghannam 	if (pvt->umc) {
722d27f3a34SYazen Ghannam 		u8 i, umc_en_mask = 0, dimm_ecc_en_mask = 0;
723d27f3a34SYazen Ghannam 
724d27f3a34SYazen Ghannam 		for (i = 0; i < NUM_UMCS; i++) {
725d27f3a34SYazen Ghannam 			if (!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT))
726d27f3a34SYazen Ghannam 				continue;
727d27f3a34SYazen Ghannam 
728d27f3a34SYazen Ghannam 			umc_en_mask |= BIT(i);
729d27f3a34SYazen Ghannam 
730d27f3a34SYazen Ghannam 			/* UMC Configuration bit 12 (DimmEccEn) */
731d27f3a34SYazen Ghannam 			if (pvt->umc[i].umc_cfg & BIT(12))
732d27f3a34SYazen Ghannam 				dimm_ecc_en_mask |= BIT(i);
733d27f3a34SYazen Ghannam 		}
734d27f3a34SYazen Ghannam 
735d27f3a34SYazen Ghannam 		if (umc_en_mask == dimm_ecc_en_mask)
736d27f3a34SYazen Ghannam 			edac_cap = EDAC_FLAG_SECDED;
737d27f3a34SYazen Ghannam 	} else {
738a4b4bedcSBorislav Petkov 		bit = (pvt->fam > 0xf || pvt->ext_model >= K8_REV_F)
7392da11654SDoug Thompson 			? 19
7402da11654SDoug Thompson 			: 17;
7412da11654SDoug Thompson 
742584fcff4SBorislav Petkov 		if (pvt->dclr0 & BIT(bit))
7432da11654SDoug Thompson 			edac_cap = EDAC_FLAG_SECDED;
744d27f3a34SYazen Ghannam 	}
7452da11654SDoug Thompson 
7462da11654SDoug Thompson 	return edac_cap;
7472da11654SDoug Thompson }
7482da11654SDoug Thompson 
749d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *, u8);
7502da11654SDoug Thompson 
751d1ea71cdSBorislav Petkov static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan)
75268798e17SBorislav Petkov {
753956b9ba1SJoe Perches 	edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
75468798e17SBorislav Petkov 
755a597d2a5SAravind Gopalakrishnan 	if (pvt->dram_type == MEM_LRDDR3) {
756a597d2a5SAravind Gopalakrishnan 		u32 dcsm = pvt->csels[chan].csmasks[0];
757a597d2a5SAravind Gopalakrishnan 		/*
758a597d2a5SAravind Gopalakrishnan 		 * It's assumed all LRDIMMs in a DCT are going to be of
759a597d2a5SAravind Gopalakrishnan 		 * same 'type' until proven otherwise. So, use a cs
760a597d2a5SAravind Gopalakrishnan 		 * value of '0' here to get dcsm value.
761a597d2a5SAravind Gopalakrishnan 		 */
762a597d2a5SAravind Gopalakrishnan 		edac_dbg(1, " LRDIMM %dx rank multiply\n", (dcsm & 0x3));
763a597d2a5SAravind Gopalakrishnan 	}
764a597d2a5SAravind Gopalakrishnan 
765a597d2a5SAravind Gopalakrishnan 	edac_dbg(1, "All DIMMs support ECC:%s\n",
76668798e17SBorislav Petkov 		    (dclr & BIT(19)) ? "yes" : "no");
76768798e17SBorislav Petkov 
768a597d2a5SAravind Gopalakrishnan 
769956b9ba1SJoe Perches 	edac_dbg(1, "  PAR/ERR parity: %s\n",
77068798e17SBorislav Petkov 		 (dclr & BIT(8)) ?  "enabled" : "disabled");
77168798e17SBorislav Petkov 
772a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10)
773956b9ba1SJoe Perches 		edac_dbg(1, "  DCT 128bit mode width: %s\n",
77468798e17SBorislav Petkov 			 (dclr & BIT(11)) ?  "128b" : "64b");
77568798e17SBorislav Petkov 
776956b9ba1SJoe Perches 	edac_dbg(1, "  x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
77768798e17SBorislav Petkov 		 (dclr & BIT(12)) ?  "yes" : "no",
77868798e17SBorislav Petkov 		 (dclr & BIT(13)) ?  "yes" : "no",
77968798e17SBorislav Petkov 		 (dclr & BIT(14)) ?  "yes" : "no",
78068798e17SBorislav Petkov 		 (dclr & BIT(15)) ?  "yes" : "no");
78168798e17SBorislav Petkov }
78268798e17SBorislav Petkov 
78307ed82efSYazen Ghannam static void debug_display_dimm_sizes_df(struct amd64_pvt *pvt, u8 ctrl)
78407ed82efSYazen Ghannam {
785eb77e6b8SYazen Ghannam 	int dimm, size0, size1, cs0, cs1;
78607ed82efSYazen Ghannam 
78707ed82efSYazen Ghannam 	edac_printk(KERN_DEBUG, EDAC_MC, "UMC%d chip selects:\n", ctrl);
78807ed82efSYazen Ghannam 
78907ed82efSYazen Ghannam 	for (dimm = 0; dimm < 4; dimm++) {
79007ed82efSYazen Ghannam 		size0 = 0;
791eb77e6b8SYazen Ghannam 		cs0 = dimm * 2;
79207ed82efSYazen Ghannam 
793eb77e6b8SYazen Ghannam 		if (csrow_enabled(cs0, ctrl, pvt))
794eb77e6b8SYazen Ghannam 			size0 = pvt->ops->dbam_to_cs(pvt, ctrl, 0, cs0);
79507ed82efSYazen Ghannam 
79607ed82efSYazen Ghannam 		size1 = 0;
797eb77e6b8SYazen Ghannam 		cs1 = dimm * 2 + 1;
798eb77e6b8SYazen Ghannam 
799eb77e6b8SYazen Ghannam 		if (csrow_enabled(cs1, ctrl, pvt))
800eb77e6b8SYazen Ghannam 			size1 = pvt->ops->dbam_to_cs(pvt, ctrl, 0, cs1);
80107ed82efSYazen Ghannam 
80207ed82efSYazen Ghannam 		amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
803eb77e6b8SYazen Ghannam 				cs0,	size0,
804eb77e6b8SYazen Ghannam 				cs1,	size1);
80507ed82efSYazen Ghannam 	}
80607ed82efSYazen Ghannam }
80707ed82efSYazen Ghannam 
80807ed82efSYazen Ghannam static void __dump_misc_regs_df(struct amd64_pvt *pvt)
80907ed82efSYazen Ghannam {
81007ed82efSYazen Ghannam 	struct amd64_umc *umc;
81107ed82efSYazen Ghannam 	u32 i, tmp, umc_base;
81207ed82efSYazen Ghannam 
81307ed82efSYazen Ghannam 	for (i = 0; i < NUM_UMCS; i++) {
81407ed82efSYazen Ghannam 		umc_base = get_umc_base(i);
81507ed82efSYazen Ghannam 		umc = &pvt->umc[i];
81607ed82efSYazen Ghannam 
81707ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d DIMM cfg: 0x%x\n", i, umc->dimm_cfg);
81807ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d UMC cfg: 0x%x\n", i, umc->umc_cfg);
81907ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d SDP ctrl: 0x%x\n", i, umc->sdp_ctrl);
82007ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d ECC ctrl: 0x%x\n", i, umc->ecc_ctrl);
82107ed82efSYazen Ghannam 
82207ed82efSYazen Ghannam 		amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ECC_BAD_SYMBOL, &tmp);
82307ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d ECC bad symbol: 0x%x\n", i, tmp);
82407ed82efSYazen Ghannam 
82507ed82efSYazen Ghannam 		amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_UMC_CAP, &tmp);
82607ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d UMC cap: 0x%x\n", i, tmp);
82707ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d UMC cap high: 0x%x\n", i, umc->umc_cap_hi);
82807ed82efSYazen Ghannam 
82907ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d ECC capable: %s, ChipKill ECC capable: %s\n",
83007ed82efSYazen Ghannam 				i, (umc->umc_cap_hi & BIT(30)) ? "yes" : "no",
83107ed82efSYazen Ghannam 				    (umc->umc_cap_hi & BIT(31)) ? "yes" : "no");
83207ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d All DIMMs support ECC: %s\n",
83307ed82efSYazen Ghannam 				i, (umc->umc_cfg & BIT(12)) ? "yes" : "no");
83407ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d x4 DIMMs present: %s\n",
83507ed82efSYazen Ghannam 				i, (umc->dimm_cfg & BIT(6)) ? "yes" : "no");
83607ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d x16 DIMMs present: %s\n",
83707ed82efSYazen Ghannam 				i, (umc->dimm_cfg & BIT(7)) ? "yes" : "no");
83807ed82efSYazen Ghannam 
83907ed82efSYazen Ghannam 		if (pvt->dram_type == MEM_LRDDR4) {
84007ed82efSYazen Ghannam 			amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ADDR_CFG, &tmp);
84107ed82efSYazen Ghannam 			edac_dbg(1, "UMC%d LRDIMM %dx rank multiply\n",
84207ed82efSYazen Ghannam 					i, 1 << ((tmp >> 4) & 0x3));
84307ed82efSYazen Ghannam 		}
84407ed82efSYazen Ghannam 
84507ed82efSYazen Ghannam 		debug_display_dimm_sizes_df(pvt, i);
84607ed82efSYazen Ghannam 	}
84707ed82efSYazen Ghannam 
84807ed82efSYazen Ghannam 	edac_dbg(1, "F0x104 (DRAM Hole Address): 0x%08x, base: 0x%08x\n",
84907ed82efSYazen Ghannam 		 pvt->dhar, dhar_base(pvt));
85007ed82efSYazen Ghannam }
85107ed82efSYazen Ghannam 
8522da11654SDoug Thompson /* Display and decode various NB registers for debug purposes. */
85307ed82efSYazen Ghannam static void __dump_misc_regs(struct amd64_pvt *pvt)
8542da11654SDoug Thompson {
855956b9ba1SJoe Perches 	edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
8562da11654SDoug Thompson 
857956b9ba1SJoe Perches 	edac_dbg(1, "  NB two channel DRAM capable: %s\n",
8585980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
85968798e17SBorislav Petkov 
860956b9ba1SJoe Perches 	edac_dbg(1, "  ECC capable: %s, ChipKill ECC capable: %s\n",
8615980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
8625980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
86368798e17SBorislav Petkov 
864d1ea71cdSBorislav Petkov 	debug_dump_dramcfg_low(pvt, pvt->dclr0, 0);
8652da11654SDoug Thompson 
866956b9ba1SJoe Perches 	edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
8672da11654SDoug Thompson 
868956b9ba1SJoe Perches 	edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n",
869bc21fa57SBorislav Petkov 		 pvt->dhar, dhar_base(pvt),
870a4b4bedcSBorislav Petkov 		 (pvt->fam == 0xf) ? k8_dhar_offset(pvt)
871bc21fa57SBorislav Petkov 				   : f10_dhar_offset(pvt));
8722da11654SDoug Thompson 
873d1ea71cdSBorislav Petkov 	debug_display_dimm_sizes(pvt, 0);
8744d796364SBorislav Petkov 
8754d796364SBorislav Petkov 	/* everything below this point is Fam10h and above */
876a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
8772da11654SDoug Thompson 		return;
8784d796364SBorislav Petkov 
879d1ea71cdSBorislav Petkov 	debug_display_dimm_sizes(pvt, 1);
8802da11654SDoug Thompson 
8818de1d91eSBorislav Petkov 	/* Only if NOT ganged does dclr1 have valid info */
88268798e17SBorislav Petkov 	if (!dct_ganging_enabled(pvt))
883d1ea71cdSBorislav Petkov 		debug_dump_dramcfg_low(pvt, pvt->dclr1, 1);
8842da11654SDoug Thompson }
8852da11654SDoug Thompson 
88607ed82efSYazen Ghannam /* Display and decode various NB registers for debug purposes. */
88707ed82efSYazen Ghannam static void dump_misc_regs(struct amd64_pvt *pvt)
88807ed82efSYazen Ghannam {
88907ed82efSYazen Ghannam 	if (pvt->umc)
89007ed82efSYazen Ghannam 		__dump_misc_regs_df(pvt);
89107ed82efSYazen Ghannam 	else
89207ed82efSYazen Ghannam 		__dump_misc_regs(pvt);
89307ed82efSYazen Ghannam 
89407ed82efSYazen Ghannam 	edac_dbg(1, "  DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
89507ed82efSYazen Ghannam 
89607ed82efSYazen Ghannam 	amd64_info("using %s syndromes.\n",
89707ed82efSYazen Ghannam 			((pvt->ecc_sym_sz == 8) ? "x8" : "x4"));
89807ed82efSYazen Ghannam }
89907ed82efSYazen Ghannam 
90094be4bffSDoug Thompson /*
90118b94f66SAravind Gopalakrishnan  * See BKDG, F2x[1,0][5C:40], F2[1,0][6C:60]
90294be4bffSDoug Thompson  */
90311c75eadSBorislav Petkov static void prep_chip_selects(struct amd64_pvt *pvt)
90494be4bffSDoug Thompson {
90518b94f66SAravind Gopalakrishnan 	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
90611c75eadSBorislav Petkov 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
90711c75eadSBorislav Petkov 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8;
908a597d2a5SAravind Gopalakrishnan 	} else if (pvt->fam == 0x15 && pvt->model == 0x30) {
90918b94f66SAravind Gopalakrishnan 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4;
91018b94f66SAravind Gopalakrishnan 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2;
9119d858bb1SBorislav Petkov 	} else {
91211c75eadSBorislav Petkov 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
91311c75eadSBorislav Petkov 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4;
9149d858bb1SBorislav Petkov 	}
91594be4bffSDoug Thompson }
91694be4bffSDoug Thompson 
91794be4bffSDoug Thompson /*
91811c75eadSBorislav Petkov  * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers
91994be4bffSDoug Thompson  */
920b2b0c605SBorislav Petkov static void read_dct_base_mask(struct amd64_pvt *pvt)
92194be4bffSDoug Thompson {
922b64ce7cdSYazen Ghannam 	int base_reg0, base_reg1, mask_reg0, mask_reg1, cs;
92394be4bffSDoug Thompson 
92411c75eadSBorislav Petkov 	prep_chip_selects(pvt);
92594be4bffSDoug Thompson 
926b64ce7cdSYazen Ghannam 	if (pvt->umc) {
927b64ce7cdSYazen Ghannam 		base_reg0 = get_umc_base(0) + UMCCH_BASE_ADDR;
928b64ce7cdSYazen Ghannam 		base_reg1 = get_umc_base(1) + UMCCH_BASE_ADDR;
929b64ce7cdSYazen Ghannam 		mask_reg0 = get_umc_base(0) + UMCCH_ADDR_MASK;
930b64ce7cdSYazen Ghannam 		mask_reg1 = get_umc_base(1) + UMCCH_ADDR_MASK;
931b64ce7cdSYazen Ghannam 	} else {
932b64ce7cdSYazen Ghannam 		base_reg0 = DCSB0;
933b64ce7cdSYazen Ghannam 		base_reg1 = DCSB1;
934b64ce7cdSYazen Ghannam 		mask_reg0 = DCSM0;
935b64ce7cdSYazen Ghannam 		mask_reg1 = DCSM1;
936b64ce7cdSYazen Ghannam 	}
937b64ce7cdSYazen Ghannam 
93811c75eadSBorislav Petkov 	for_each_chip_select(cs, 0, pvt) {
939b64ce7cdSYazen Ghannam 		int reg0   = base_reg0 + (cs * 4);
940b64ce7cdSYazen Ghannam 		int reg1   = base_reg1 + (cs * 4);
94111c75eadSBorislav Petkov 		u32 *base0 = &pvt->csels[0].csbases[cs];
94211c75eadSBorislav Petkov 		u32 *base1 = &pvt->csels[1].csbases[cs];
943b2b0c605SBorislav Petkov 
944b64ce7cdSYazen Ghannam 		if (pvt->umc) {
945b64ce7cdSYazen Ghannam 			if (!amd_smn_read(pvt->mc_node_id, reg0, base0))
946b64ce7cdSYazen Ghannam 				edac_dbg(0, "  DCSB0[%d]=0x%08x reg: 0x%x\n",
947b64ce7cdSYazen Ghannam 					 cs, *base0, reg0);
948b64ce7cdSYazen Ghannam 
949b64ce7cdSYazen Ghannam 			if (!amd_smn_read(pvt->mc_node_id, reg1, base1))
950b64ce7cdSYazen Ghannam 				edac_dbg(0, "  DCSB1[%d]=0x%08x reg: 0x%x\n",
951b64ce7cdSYazen Ghannam 					 cs, *base1, reg1);
952b64ce7cdSYazen Ghannam 		} else {
9537981a28fSAravind Gopalakrishnan 			if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0))
954956b9ba1SJoe Perches 				edac_dbg(0, "  DCSB0[%d]=0x%08x reg: F2x%x\n",
95511c75eadSBorislav Petkov 					 cs, *base0, reg0);
95694be4bffSDoug Thompson 
9577981a28fSAravind Gopalakrishnan 			if (pvt->fam == 0xf)
95811c75eadSBorislav Petkov 				continue;
959b2b0c605SBorislav Petkov 
9607981a28fSAravind Gopalakrishnan 			if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1))
961956b9ba1SJoe Perches 				edac_dbg(0, "  DCSB1[%d]=0x%08x reg: F2x%x\n",
9627981a28fSAravind Gopalakrishnan 					 cs, *base1, (pvt->fam == 0x10) ? reg1
9637981a28fSAravind Gopalakrishnan 								: reg0);
96494be4bffSDoug Thompson 		}
965b64ce7cdSYazen Ghannam 	}
96694be4bffSDoug Thompson 
96711c75eadSBorislav Petkov 	for_each_chip_select_mask(cs, 0, pvt) {
968b64ce7cdSYazen Ghannam 		int reg0   = mask_reg0 + (cs * 4);
969b64ce7cdSYazen Ghannam 		int reg1   = mask_reg1 + (cs * 4);
97011c75eadSBorislav Petkov 		u32 *mask0 = &pvt->csels[0].csmasks[cs];
97111c75eadSBorislav Petkov 		u32 *mask1 = &pvt->csels[1].csmasks[cs];
972b2b0c605SBorislav Petkov 
973b64ce7cdSYazen Ghannam 		if (pvt->umc) {
974b64ce7cdSYazen Ghannam 			if (!amd_smn_read(pvt->mc_node_id, reg0, mask0))
975b64ce7cdSYazen Ghannam 				edac_dbg(0, "    DCSM0[%d]=0x%08x reg: 0x%x\n",
976b64ce7cdSYazen Ghannam 					 cs, *mask0, reg0);
977b64ce7cdSYazen Ghannam 
978b64ce7cdSYazen Ghannam 			if (!amd_smn_read(pvt->mc_node_id, reg1, mask1))
979b64ce7cdSYazen Ghannam 				edac_dbg(0, "    DCSM1[%d]=0x%08x reg: 0x%x\n",
980b64ce7cdSYazen Ghannam 					 cs, *mask1, reg1);
981b64ce7cdSYazen Ghannam 		} else {
9827981a28fSAravind Gopalakrishnan 			if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0))
983956b9ba1SJoe Perches 				edac_dbg(0, "    DCSM0[%d]=0x%08x reg: F2x%x\n",
98411c75eadSBorislav Petkov 					 cs, *mask0, reg0);
98594be4bffSDoug Thompson 
9867981a28fSAravind Gopalakrishnan 			if (pvt->fam == 0xf)
98711c75eadSBorislav Petkov 				continue;
988b2b0c605SBorislav Petkov 
9897981a28fSAravind Gopalakrishnan 			if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1))
990956b9ba1SJoe Perches 				edac_dbg(0, "    DCSM1[%d]=0x%08x reg: F2x%x\n",
9917981a28fSAravind Gopalakrishnan 					 cs, *mask1, (pvt->fam == 0x10) ? reg1
9927981a28fSAravind Gopalakrishnan 								: reg0);
99394be4bffSDoug Thompson 		}
9946ba5dcdcSBorislav Petkov 	}
995b64ce7cdSYazen Ghannam }
99694be4bffSDoug Thompson 
997a597d2a5SAravind Gopalakrishnan static void determine_memory_type(struct amd64_pvt *pvt)
99894be4bffSDoug Thompson {
999a597d2a5SAravind Gopalakrishnan 	u32 dram_ctrl, dcsm;
100094be4bffSDoug Thompson 
1001a597d2a5SAravind Gopalakrishnan 	switch (pvt->fam) {
1002a597d2a5SAravind Gopalakrishnan 	case 0xf:
1003a597d2a5SAravind Gopalakrishnan 		if (pvt->ext_model >= K8_REV_F)
1004a597d2a5SAravind Gopalakrishnan 			goto ddr3;
1005a597d2a5SAravind Gopalakrishnan 
1006a597d2a5SAravind Gopalakrishnan 		pvt->dram_type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
1007a597d2a5SAravind Gopalakrishnan 		return;
1008a597d2a5SAravind Gopalakrishnan 
1009a597d2a5SAravind Gopalakrishnan 	case 0x10:
10106b4c0bdeSBorislav Petkov 		if (pvt->dchr0 & DDR3_MODE)
1011a597d2a5SAravind Gopalakrishnan 			goto ddr3;
1012a597d2a5SAravind Gopalakrishnan 
1013a597d2a5SAravind Gopalakrishnan 		pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
1014a597d2a5SAravind Gopalakrishnan 		return;
1015a597d2a5SAravind Gopalakrishnan 
1016a597d2a5SAravind Gopalakrishnan 	case 0x15:
1017a597d2a5SAravind Gopalakrishnan 		if (pvt->model < 0x60)
1018a597d2a5SAravind Gopalakrishnan 			goto ddr3;
1019a597d2a5SAravind Gopalakrishnan 
1020a597d2a5SAravind Gopalakrishnan 		/*
1021a597d2a5SAravind Gopalakrishnan 		 * Model 0x60h needs special handling:
1022a597d2a5SAravind Gopalakrishnan 		 *
1023a597d2a5SAravind Gopalakrishnan 		 * We use a Chip Select value of '0' to obtain dcsm.
1024a597d2a5SAravind Gopalakrishnan 		 * Theoretically, it is possible to populate LRDIMMs of different
1025a597d2a5SAravind Gopalakrishnan 		 * 'Rank' value on a DCT. But this is not the common case. So,
1026a597d2a5SAravind Gopalakrishnan 		 * it's reasonable to assume all DIMMs are going to be of same
1027a597d2a5SAravind Gopalakrishnan 		 * 'type' until proven otherwise.
1028a597d2a5SAravind Gopalakrishnan 		 */
1029a597d2a5SAravind Gopalakrishnan 		amd64_read_dct_pci_cfg(pvt, 0, DRAM_CONTROL, &dram_ctrl);
1030a597d2a5SAravind Gopalakrishnan 		dcsm = pvt->csels[0].csmasks[0];
1031a597d2a5SAravind Gopalakrishnan 
1032a597d2a5SAravind Gopalakrishnan 		if (((dram_ctrl >> 8) & 0x7) == 0x2)
1033a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_DDR4;
1034a597d2a5SAravind Gopalakrishnan 		else if (pvt->dclr0 & BIT(16))
1035a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_DDR3;
1036a597d2a5SAravind Gopalakrishnan 		else if (dcsm & 0x3)
1037a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_LRDDR3;
10386b4c0bdeSBorislav Petkov 		else
1039a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_RDDR3;
1040a597d2a5SAravind Gopalakrishnan 
1041a597d2a5SAravind Gopalakrishnan 		return;
1042a597d2a5SAravind Gopalakrishnan 
1043a597d2a5SAravind Gopalakrishnan 	case 0x16:
1044a597d2a5SAravind Gopalakrishnan 		goto ddr3;
1045a597d2a5SAravind Gopalakrishnan 
1046b64ce7cdSYazen Ghannam 	case 0x17:
1047b64ce7cdSYazen Ghannam 		if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(5))
1048b64ce7cdSYazen Ghannam 			pvt->dram_type = MEM_LRDDR4;
1049b64ce7cdSYazen Ghannam 		else if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(4))
1050b64ce7cdSYazen Ghannam 			pvt->dram_type = MEM_RDDR4;
1051b64ce7cdSYazen Ghannam 		else
1052b64ce7cdSYazen Ghannam 			pvt->dram_type = MEM_DDR4;
1053b64ce7cdSYazen Ghannam 		return;
1054b64ce7cdSYazen Ghannam 
1055a597d2a5SAravind Gopalakrishnan 	default:
1056a597d2a5SAravind Gopalakrishnan 		WARN(1, KERN_ERR "%s: Family??? 0x%x\n", __func__, pvt->fam);
1057a597d2a5SAravind Gopalakrishnan 		pvt->dram_type = MEM_EMPTY;
105894be4bffSDoug Thompson 	}
1059a597d2a5SAravind Gopalakrishnan 	return;
106094be4bffSDoug Thompson 
1061a597d2a5SAravind Gopalakrishnan ddr3:
1062a597d2a5SAravind Gopalakrishnan 	pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
106394be4bffSDoug Thompson }
106494be4bffSDoug Thompson 
1065cb328507SBorislav Petkov /* Get the number of DCT channels the memory controller is using. */
1066ddff876dSDoug Thompson static int k8_early_channel_count(struct amd64_pvt *pvt)
1067ddff876dSDoug Thompson {
1068cb328507SBorislav Petkov 	int flag;
1069ddff876dSDoug Thompson 
10709f56da0eSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F)
1071ddff876dSDoug Thompson 		/* RevF (NPT) and later */
107241d8bfabSBorislav Petkov 		flag = pvt->dclr0 & WIDTH_128;
10739f56da0eSBorislav Petkov 	else
1074ddff876dSDoug Thompson 		/* RevE and earlier */
1075ddff876dSDoug Thompson 		flag = pvt->dclr0 & REVE_WIDTH_128;
1076ddff876dSDoug Thompson 
1077ddff876dSDoug Thompson 	/* not used */
1078ddff876dSDoug Thompson 	pvt->dclr1 = 0;
1079ddff876dSDoug Thompson 
1080ddff876dSDoug Thompson 	return (flag) ? 2 : 1;
1081ddff876dSDoug Thompson }
1082ddff876dSDoug Thompson 
108370046624SBorislav Petkov /* On F10h and later ErrAddr is MC4_ADDR[47:1] */
1084a4b4bedcSBorislav Petkov static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m)
1085ddff876dSDoug Thompson {
10862ec591acSBorislav Petkov 	u16 mce_nid = amd_get_nb_id(m->extcpu);
10872ec591acSBorislav Petkov 	struct mem_ctl_info *mci;
108870046624SBorislav Petkov 	u8 start_bit = 1;
108970046624SBorislav Petkov 	u8 end_bit   = 47;
10902ec591acSBorislav Petkov 	u64 addr;
10912ec591acSBorislav Petkov 
10922ec591acSBorislav Petkov 	mci = edac_mc_find(mce_nid);
10932ec591acSBorislav Petkov 	if (!mci)
10942ec591acSBorislav Petkov 		return 0;
10952ec591acSBorislav Petkov 
10962ec591acSBorislav Petkov 	pvt = mci->pvt_info;
109770046624SBorislav Petkov 
1098a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf) {
109970046624SBorislav Petkov 		start_bit = 3;
110070046624SBorislav Petkov 		end_bit   = 39;
110170046624SBorislav Petkov 	}
110270046624SBorislav Petkov 
110310ef6b0dSChen, Gong 	addr = m->addr & GENMASK_ULL(end_bit, start_bit);
1104c1ae6830SBorislav Petkov 
1105c1ae6830SBorislav Petkov 	/*
1106c1ae6830SBorislav Petkov 	 * Erratum 637 workaround
1107c1ae6830SBorislav Petkov 	 */
1108a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x15) {
1109c1ae6830SBorislav Petkov 		u64 cc6_base, tmp_addr;
1110c1ae6830SBorislav Petkov 		u32 tmp;
11118b84c8dfSDaniel J Blueman 		u8 intlv_en;
1112c1ae6830SBorislav Petkov 
111310ef6b0dSChen, Gong 		if ((addr & GENMASK_ULL(47, 24)) >> 24 != 0x00fdf7)
1114c1ae6830SBorislav Petkov 			return addr;
1115c1ae6830SBorislav Petkov 
1116c1ae6830SBorislav Petkov 
1117c1ae6830SBorislav Petkov 		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp);
1118c1ae6830SBorislav Petkov 		intlv_en = tmp >> 21 & 0x7;
1119c1ae6830SBorislav Petkov 
1120c1ae6830SBorislav Petkov 		/* add [47:27] + 3 trailing bits */
112110ef6b0dSChen, Gong 		cc6_base  = (tmp & GENMASK_ULL(20, 0)) << 3;
1122c1ae6830SBorislav Petkov 
1123c1ae6830SBorislav Petkov 		/* reverse and add DramIntlvEn */
1124c1ae6830SBorislav Petkov 		cc6_base |= intlv_en ^ 0x7;
1125c1ae6830SBorislav Petkov 
1126c1ae6830SBorislav Petkov 		/* pin at [47:24] */
1127c1ae6830SBorislav Petkov 		cc6_base <<= 24;
1128c1ae6830SBorislav Petkov 
1129c1ae6830SBorislav Petkov 		if (!intlv_en)
113010ef6b0dSChen, Gong 			return cc6_base | (addr & GENMASK_ULL(23, 0));
1131c1ae6830SBorislav Petkov 
1132c1ae6830SBorislav Petkov 		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp);
1133c1ae6830SBorislav Petkov 
1134c1ae6830SBorislav Petkov 							/* faster log2 */
113510ef6b0dSChen, Gong 		tmp_addr  = (addr & GENMASK_ULL(23, 12)) << __fls(intlv_en + 1);
1136c1ae6830SBorislav Petkov 
1137c1ae6830SBorislav Petkov 		/* OR DramIntlvSel into bits [14:12] */
113810ef6b0dSChen, Gong 		tmp_addr |= (tmp & GENMASK_ULL(23, 21)) >> 9;
1139c1ae6830SBorislav Petkov 
1140c1ae6830SBorislav Petkov 		/* add remaining [11:0] bits from original MC4_ADDR */
114110ef6b0dSChen, Gong 		tmp_addr |= addr & GENMASK_ULL(11, 0);
1142c1ae6830SBorislav Petkov 
1143c1ae6830SBorislav Petkov 		return cc6_base | tmp_addr;
1144c1ae6830SBorislav Petkov 	}
1145c1ae6830SBorislav Petkov 
1146c1ae6830SBorislav Petkov 	return addr;
1147ddff876dSDoug Thompson }
1148ddff876dSDoug Thompson 
1149e2c0bffeSDaniel J Blueman static struct pci_dev *pci_get_related_function(unsigned int vendor,
1150e2c0bffeSDaniel J Blueman 						unsigned int device,
1151e2c0bffeSDaniel J Blueman 						struct pci_dev *related)
1152e2c0bffeSDaniel J Blueman {
1153e2c0bffeSDaniel J Blueman 	struct pci_dev *dev = NULL;
1154e2c0bffeSDaniel J Blueman 
1155e2c0bffeSDaniel J Blueman 	while ((dev = pci_get_device(vendor, device, dev))) {
1156e2c0bffeSDaniel J Blueman 		if (pci_domain_nr(dev->bus) == pci_domain_nr(related->bus) &&
1157e2c0bffeSDaniel J Blueman 		    (dev->bus->number == related->bus->number) &&
1158e2c0bffeSDaniel J Blueman 		    (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn)))
1159e2c0bffeSDaniel J Blueman 			break;
1160e2c0bffeSDaniel J Blueman 	}
1161e2c0bffeSDaniel J Blueman 
1162e2c0bffeSDaniel J Blueman 	return dev;
1163e2c0bffeSDaniel J Blueman }
1164e2c0bffeSDaniel J Blueman 
11657f19bf75SBorislav Petkov static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
1166ddff876dSDoug Thompson {
1167e2c0bffeSDaniel J Blueman 	struct amd_northbridge *nb;
116818b94f66SAravind Gopalakrishnan 	struct pci_dev *f1 = NULL;
116918b94f66SAravind Gopalakrishnan 	unsigned int pci_func;
117071d2a32eSBorislav Petkov 	int off = range << 3;
1171e2c0bffeSDaniel J Blueman 	u32 llim;
1172ddff876dSDoug Thompson 
11737f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off,  &pvt->ranges[range].base.lo);
11747f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo);
1175ddff876dSDoug Thompson 
117618b94f66SAravind Gopalakrishnan 	if (pvt->fam == 0xf)
11777f19bf75SBorislav Petkov 		return;
1178ddff876dSDoug Thompson 
11797f19bf75SBorislav Petkov 	if (!dram_rw(pvt, range))
11807f19bf75SBorislav Petkov 		return;
1181ddff876dSDoug Thompson 
11827f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off,  &pvt->ranges[range].base.hi);
11837f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi);
1184f08e457cSBorislav Petkov 
1185e2c0bffeSDaniel J Blueman 	/* F15h: factor in CC6 save area by reading dst node's limit reg */
118618b94f66SAravind Gopalakrishnan 	if (pvt->fam != 0x15)
1187e2c0bffeSDaniel J Blueman 		return;
1188f08e457cSBorislav Petkov 
1189e2c0bffeSDaniel J Blueman 	nb = node_to_amd_nb(dram_dst_node(pvt, range));
1190e2c0bffeSDaniel J Blueman 	if (WARN_ON(!nb))
1191e2c0bffeSDaniel J Blueman 		return;
1192e2c0bffeSDaniel J Blueman 
1193a597d2a5SAravind Gopalakrishnan 	if (pvt->model == 0x60)
1194a597d2a5SAravind Gopalakrishnan 		pci_func = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1;
1195a597d2a5SAravind Gopalakrishnan 	else if (pvt->model == 0x30)
1196a597d2a5SAravind Gopalakrishnan 		pci_func = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1;
1197a597d2a5SAravind Gopalakrishnan 	else
1198a597d2a5SAravind Gopalakrishnan 		pci_func = PCI_DEVICE_ID_AMD_15H_NB_F1;
119918b94f66SAravind Gopalakrishnan 
120018b94f66SAravind Gopalakrishnan 	f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc);
1201f08e457cSBorislav Petkov 	if (WARN_ON(!f1))
1202f08e457cSBorislav Petkov 		return;
1203f08e457cSBorislav Petkov 
1204f08e457cSBorislav Petkov 	amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim);
1205f08e457cSBorislav Petkov 
120610ef6b0dSChen, Gong 	pvt->ranges[range].lim.lo &= GENMASK_ULL(15, 0);
1207f08e457cSBorislav Petkov 
1208f08e457cSBorislav Petkov 				    /* {[39:27],111b} */
1209f08e457cSBorislav Petkov 	pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16;
1210f08e457cSBorislav Petkov 
121110ef6b0dSChen, Gong 	pvt->ranges[range].lim.hi &= GENMASK_ULL(7, 0);
1212f08e457cSBorislav Petkov 
1213f08e457cSBorislav Petkov 				    /* [47:40] */
1214f08e457cSBorislav Petkov 	pvt->ranges[range].lim.hi |= llim >> 13;
1215f08e457cSBorislav Petkov 
1216f08e457cSBorislav Petkov 	pci_dev_put(f1);
1217f08e457cSBorislav Petkov }
1218ddff876dSDoug Thompson 
1219f192c7b1SBorislav Petkov static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
122033ca0643SBorislav Petkov 				    struct err_info *err)
1221ddff876dSDoug Thompson {
1222f192c7b1SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
1223ddff876dSDoug Thompson 
122433ca0643SBorislav Petkov 	error_address_to_page_and_offset(sys_addr, err);
1225ab5a503cSMauro Carvalho Chehab 
1226ab5a503cSMauro Carvalho Chehab 	/*
1227ab5a503cSMauro Carvalho Chehab 	 * Find out which node the error address belongs to. This may be
1228ab5a503cSMauro Carvalho Chehab 	 * different from the node that detected the error.
1229ab5a503cSMauro Carvalho Chehab 	 */
123033ca0643SBorislav Petkov 	err->src_mci = find_mc_by_sys_addr(mci, sys_addr);
123133ca0643SBorislav Petkov 	if (!err->src_mci) {
1232ab5a503cSMauro Carvalho Chehab 		amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
1233ab5a503cSMauro Carvalho Chehab 			     (unsigned long)sys_addr);
123433ca0643SBorislav Petkov 		err->err_code = ERR_NODE;
1235ab5a503cSMauro Carvalho Chehab 		return;
1236ab5a503cSMauro Carvalho Chehab 	}
1237ab5a503cSMauro Carvalho Chehab 
1238ab5a503cSMauro Carvalho Chehab 	/* Now map the sys_addr to a CSROW */
123933ca0643SBorislav Petkov 	err->csrow = sys_addr_to_csrow(err->src_mci, sys_addr);
124033ca0643SBorislav Petkov 	if (err->csrow < 0) {
124133ca0643SBorislav Petkov 		err->err_code = ERR_CSROW;
1242ab5a503cSMauro Carvalho Chehab 		return;
1243ab5a503cSMauro Carvalho Chehab 	}
1244ab5a503cSMauro Carvalho Chehab 
1245ddff876dSDoug Thompson 	/* CHIPKILL enabled */
1246f192c7b1SBorislav Petkov 	if (pvt->nbcfg & NBCFG_CHIPKILL) {
124733ca0643SBorislav Petkov 		err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
124833ca0643SBorislav Petkov 		if (err->channel < 0) {
1249ddff876dSDoug Thompson 			/*
1250ddff876dSDoug Thompson 			 * Syndrome didn't map, so we don't know which of the
1251ddff876dSDoug Thompson 			 * 2 DIMMs is in error. So we need to ID 'both' of them
1252ddff876dSDoug Thompson 			 * as suspect.
1253ddff876dSDoug Thompson 			 */
125433ca0643SBorislav Petkov 			amd64_mc_warn(err->src_mci, "unknown syndrome 0x%04x - "
1255ab5a503cSMauro Carvalho Chehab 				      "possible error reporting race\n",
125633ca0643SBorislav Petkov 				      err->syndrome);
125733ca0643SBorislav Petkov 			err->err_code = ERR_CHANNEL;
1258ddff876dSDoug Thompson 			return;
1259ddff876dSDoug Thompson 		}
1260ddff876dSDoug Thompson 	} else {
1261ddff876dSDoug Thompson 		/*
1262ddff876dSDoug Thompson 		 * non-chipkill ecc mode
1263ddff876dSDoug Thompson 		 *
1264ddff876dSDoug Thompson 		 * The k8 documentation is unclear about how to determine the
1265ddff876dSDoug Thompson 		 * channel number when using non-chipkill memory.  This method
1266ddff876dSDoug Thompson 		 * was obtained from email communication with someone at AMD.
1267ddff876dSDoug Thompson 		 * (Wish the email was placed in this comment - norsk)
1268ddff876dSDoug Thompson 		 */
126933ca0643SBorislav Petkov 		err->channel = ((sys_addr & BIT(3)) != 0);
1270ddff876dSDoug Thompson 	}
1271ddff876dSDoug Thompson }
1272ddff876dSDoug Thompson 
127341d8bfabSBorislav Petkov static int ddr2_cs_size(unsigned i, bool dct_width)
1274ddff876dSDoug Thompson {
127541d8bfabSBorislav Petkov 	unsigned shift = 0;
1276ddff876dSDoug Thompson 
127741d8bfabSBorislav Petkov 	if (i <= 2)
127841d8bfabSBorislav Petkov 		shift = i;
127941d8bfabSBorislav Petkov 	else if (!(i & 0x1))
128041d8bfabSBorislav Petkov 		shift = i >> 1;
12811433eb99SBorislav Petkov 	else
128241d8bfabSBorislav Petkov 		shift = (i + 1) >> 1;
1283ddff876dSDoug Thompson 
128441d8bfabSBorislav Petkov 	return 128 << (shift + !!dct_width);
128541d8bfabSBorislav Petkov }
128641d8bfabSBorislav Petkov 
128741d8bfabSBorislav Petkov static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1288a597d2a5SAravind Gopalakrishnan 				  unsigned cs_mode, int cs_mask_nr)
128941d8bfabSBorislav Petkov {
129041d8bfabSBorislav Petkov 	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
129141d8bfabSBorislav Petkov 
129241d8bfabSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F) {
129341d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 11);
129441d8bfabSBorislav Petkov 		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
129541d8bfabSBorislav Petkov 	}
129641d8bfabSBorislav Petkov 	else if (pvt->ext_model >= K8_REV_D) {
129711b0a314SBorislav Petkov 		unsigned diff;
129841d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 10);
129941d8bfabSBorislav Petkov 
130011b0a314SBorislav Petkov 		/*
130111b0a314SBorislav Petkov 		 * the below calculation, besides trying to win an obfuscated C
130211b0a314SBorislav Petkov 		 * contest, maps cs_mode values to DIMM chip select sizes. The
130311b0a314SBorislav Petkov 		 * mappings are:
130411b0a314SBorislav Petkov 		 *
130511b0a314SBorislav Petkov 		 * cs_mode	CS size (mb)
130611b0a314SBorislav Petkov 		 * =======	============
130711b0a314SBorislav Petkov 		 * 0		32
130811b0a314SBorislav Petkov 		 * 1		64
130911b0a314SBorislav Petkov 		 * 2		128
131011b0a314SBorislav Petkov 		 * 3		128
131111b0a314SBorislav Petkov 		 * 4		256
131211b0a314SBorislav Petkov 		 * 5		512
131311b0a314SBorislav Petkov 		 * 6		256
131411b0a314SBorislav Petkov 		 * 7		512
131511b0a314SBorislav Petkov 		 * 8		1024
131611b0a314SBorislav Petkov 		 * 9		1024
131711b0a314SBorislav Petkov 		 * 10		2048
131811b0a314SBorislav Petkov 		 *
131911b0a314SBorislav Petkov 		 * Basically, it calculates a value with which to shift the
132011b0a314SBorislav Petkov 		 * smallest CS size of 32MB.
132111b0a314SBorislav Petkov 		 *
132211b0a314SBorislav Petkov 		 * ddr[23]_cs_size have a similar purpose.
132311b0a314SBorislav Petkov 		 */
132411b0a314SBorislav Petkov 		diff = cs_mode/3 + (unsigned)(cs_mode > 5);
132511b0a314SBorislav Petkov 
132611b0a314SBorislav Petkov 		return 32 << (cs_mode - diff);
132741d8bfabSBorislav Petkov 	}
132841d8bfabSBorislav Petkov 	else {
132941d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 6);
133041d8bfabSBorislav Petkov 		return 32 << cs_mode;
133141d8bfabSBorislav Petkov 	}
1332ddff876dSDoug Thompson }
1333ddff876dSDoug Thompson 
13341afd3c98SDoug Thompson /*
13351afd3c98SDoug Thompson  * Get the number of DCT channels in use.
13361afd3c98SDoug Thompson  *
13371afd3c98SDoug Thompson  * Return:
13381afd3c98SDoug Thompson  *	number of Memory Channels in operation
13391afd3c98SDoug Thompson  * Pass back:
13401afd3c98SDoug Thompson  *	contents of the DCL0_LOW register
13411afd3c98SDoug Thompson  */
13427d20d14dSBorislav Petkov static int f1x_early_channel_count(struct amd64_pvt *pvt)
13431afd3c98SDoug Thompson {
13446ba5dcdcSBorislav Petkov 	int i, j, channels = 0;
1345ddff876dSDoug Thompson 
13467d20d14dSBorislav Petkov 	/* On F10h, if we are in 128 bit mode, then we are using 2 channels */
1347a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10 && (pvt->dclr0 & WIDTH_128))
13487d20d14dSBorislav Petkov 		return 2;
13491afd3c98SDoug Thompson 
13501afd3c98SDoug Thompson 	/*
1351d16149e8SBorislav Petkov 	 * Need to check if in unganged mode: In such, there are 2 channels,
1352d16149e8SBorislav Petkov 	 * but they are not in 128 bit mode and thus the above 'dclr0' status
1353d16149e8SBorislav Petkov 	 * bit will be OFF.
13541afd3c98SDoug Thompson 	 *
13551afd3c98SDoug Thompson 	 * Need to check DCT0[0] and DCT1[0] to see if only one of them has
13561afd3c98SDoug Thompson 	 * their CSEnable bit on. If so, then SINGLE DIMM case.
13571afd3c98SDoug Thompson 	 */
1358956b9ba1SJoe Perches 	edac_dbg(0, "Data width is not 128 bits - need more decoding\n");
13591afd3c98SDoug Thompson 
13601afd3c98SDoug Thompson 	/*
13611afd3c98SDoug Thompson 	 * Check DRAM Bank Address Mapping values for each DIMM to see if there
13621afd3c98SDoug Thompson 	 * is more than just one DIMM present in unganged mode. Need to check
13631afd3c98SDoug Thompson 	 * both controllers since DIMMs can be placed in either one.
13641afd3c98SDoug Thompson 	 */
1365525a1b20SBorislav Petkov 	for (i = 0; i < 2; i++) {
1366525a1b20SBorislav Petkov 		u32 dbam = (i ? pvt->dbam1 : pvt->dbam0);
13671afd3c98SDoug Thompson 
136857a30854SWan Wei 		for (j = 0; j < 4; j++) {
136957a30854SWan Wei 			if (DBAM_DIMM(j, dbam) > 0) {
13701afd3c98SDoug Thompson 				channels++;
137157a30854SWan Wei 				break;
13721afd3c98SDoug Thompson 			}
137357a30854SWan Wei 		}
137457a30854SWan Wei 	}
13751afd3c98SDoug Thompson 
1376d16149e8SBorislav Petkov 	if (channels > 2)
1377d16149e8SBorislav Petkov 		channels = 2;
1378d16149e8SBorislav Petkov 
137924f9a7feSBorislav Petkov 	amd64_info("MCT channel count: %d\n", channels);
13801afd3c98SDoug Thompson 
13811afd3c98SDoug Thompson 	return channels;
13821afd3c98SDoug Thompson }
13831afd3c98SDoug Thompson 
1384f1cbbec9SYazen Ghannam static int f17_early_channel_count(struct amd64_pvt *pvt)
1385f1cbbec9SYazen Ghannam {
1386f1cbbec9SYazen Ghannam 	int i, channels = 0;
1387f1cbbec9SYazen Ghannam 
1388f1cbbec9SYazen Ghannam 	/* SDP Control bit 31 (SdpInit) is clear for unused UMC channels */
1389f1cbbec9SYazen Ghannam 	for (i = 0; i < NUM_UMCS; i++)
1390f1cbbec9SYazen Ghannam 		channels += !!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT);
1391f1cbbec9SYazen Ghannam 
1392f1cbbec9SYazen Ghannam 	amd64_info("MCT channel count: %d\n", channels);
1393f1cbbec9SYazen Ghannam 
1394f1cbbec9SYazen Ghannam 	return channels;
1395f1cbbec9SYazen Ghannam }
1396f1cbbec9SYazen Ghannam 
139741d8bfabSBorislav Petkov static int ddr3_cs_size(unsigned i, bool dct_width)
13981afd3c98SDoug Thompson {
139941d8bfabSBorislav Petkov 	unsigned shift = 0;
140041d8bfabSBorislav Petkov 	int cs_size = 0;
140141d8bfabSBorislav Petkov 
140241d8bfabSBorislav Petkov 	if (i == 0 || i == 3 || i == 4)
140341d8bfabSBorislav Petkov 		cs_size = -1;
140441d8bfabSBorislav Petkov 	else if (i <= 2)
140541d8bfabSBorislav Petkov 		shift = i;
140641d8bfabSBorislav Petkov 	else if (i == 12)
140741d8bfabSBorislav Petkov 		shift = 7;
140841d8bfabSBorislav Petkov 	else if (!(i & 0x1))
140941d8bfabSBorislav Petkov 		shift = i >> 1;
141041d8bfabSBorislav Petkov 	else
141141d8bfabSBorislav Petkov 		shift = (i + 1) >> 1;
141241d8bfabSBorislav Petkov 
141341d8bfabSBorislav Petkov 	if (cs_size != -1)
141441d8bfabSBorislav Petkov 		cs_size = (128 * (1 << !!dct_width)) << shift;
141541d8bfabSBorislav Petkov 
141641d8bfabSBorislav Petkov 	return cs_size;
141741d8bfabSBorislav Petkov }
141841d8bfabSBorislav Petkov 
1419a597d2a5SAravind Gopalakrishnan static int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply)
1420a597d2a5SAravind Gopalakrishnan {
1421a597d2a5SAravind Gopalakrishnan 	unsigned shift = 0;
1422a597d2a5SAravind Gopalakrishnan 	int cs_size = 0;
1423a597d2a5SAravind Gopalakrishnan 
1424a597d2a5SAravind Gopalakrishnan 	if (i < 4 || i == 6)
1425a597d2a5SAravind Gopalakrishnan 		cs_size = -1;
1426a597d2a5SAravind Gopalakrishnan 	else if (i == 12)
1427a597d2a5SAravind Gopalakrishnan 		shift = 7;
1428a597d2a5SAravind Gopalakrishnan 	else if (!(i & 0x1))
1429a597d2a5SAravind Gopalakrishnan 		shift = i >> 1;
1430a597d2a5SAravind Gopalakrishnan 	else
1431a597d2a5SAravind Gopalakrishnan 		shift = (i + 1) >> 1;
1432a597d2a5SAravind Gopalakrishnan 
1433a597d2a5SAravind Gopalakrishnan 	if (cs_size != -1)
1434a597d2a5SAravind Gopalakrishnan 		cs_size = rank_multiply * (128 << shift);
1435a597d2a5SAravind Gopalakrishnan 
1436a597d2a5SAravind Gopalakrishnan 	return cs_size;
1437a597d2a5SAravind Gopalakrishnan }
1438a597d2a5SAravind Gopalakrishnan 
1439a597d2a5SAravind Gopalakrishnan static int ddr4_cs_size(unsigned i)
1440a597d2a5SAravind Gopalakrishnan {
1441a597d2a5SAravind Gopalakrishnan 	int cs_size = 0;
1442a597d2a5SAravind Gopalakrishnan 
1443a597d2a5SAravind Gopalakrishnan 	if (i == 0)
1444a597d2a5SAravind Gopalakrishnan 		cs_size = -1;
1445a597d2a5SAravind Gopalakrishnan 	else if (i == 1)
1446a597d2a5SAravind Gopalakrishnan 		cs_size = 1024;
1447a597d2a5SAravind Gopalakrishnan 	else
1448a597d2a5SAravind Gopalakrishnan 		/* Min cs_size = 1G */
1449a597d2a5SAravind Gopalakrishnan 		cs_size = 1024 * (1 << (i >> 1));
1450a597d2a5SAravind Gopalakrishnan 
1451a597d2a5SAravind Gopalakrishnan 	return cs_size;
1452a597d2a5SAravind Gopalakrishnan }
1453a597d2a5SAravind Gopalakrishnan 
145441d8bfabSBorislav Petkov static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1455a597d2a5SAravind Gopalakrishnan 				   unsigned cs_mode, int cs_mask_nr)
145641d8bfabSBorislav Petkov {
145741d8bfabSBorislav Petkov 	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
145841d8bfabSBorislav Petkov 
145941d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 11);
14601433eb99SBorislav Petkov 
14611433eb99SBorislav Petkov 	if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
146241d8bfabSBorislav Petkov 		return ddr3_cs_size(cs_mode, dclr & WIDTH_128);
14631433eb99SBorislav Petkov 	else
146441d8bfabSBorislav Petkov 		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
146541d8bfabSBorislav Petkov }
14661433eb99SBorislav Petkov 
146741d8bfabSBorislav Petkov /*
146841d8bfabSBorislav Petkov  * F15h supports only 64bit DCT interfaces
146941d8bfabSBorislav Petkov  */
147041d8bfabSBorislav Petkov static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1471a597d2a5SAravind Gopalakrishnan 				   unsigned cs_mode, int cs_mask_nr)
147241d8bfabSBorislav Petkov {
147341d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 12);
147441d8bfabSBorislav Petkov 
147541d8bfabSBorislav Petkov 	return ddr3_cs_size(cs_mode, false);
14761afd3c98SDoug Thompson }
14771afd3c98SDoug Thompson 
1478a597d2a5SAravind Gopalakrishnan /* F15h M60h supports DDR4 mapping as well.. */
1479a597d2a5SAravind Gopalakrishnan static int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1480a597d2a5SAravind Gopalakrishnan 					unsigned cs_mode, int cs_mask_nr)
1481a597d2a5SAravind Gopalakrishnan {
1482a597d2a5SAravind Gopalakrishnan 	int cs_size;
1483a597d2a5SAravind Gopalakrishnan 	u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr];
1484a597d2a5SAravind Gopalakrishnan 
1485a597d2a5SAravind Gopalakrishnan 	WARN_ON(cs_mode > 12);
1486a597d2a5SAravind Gopalakrishnan 
1487a597d2a5SAravind Gopalakrishnan 	if (pvt->dram_type == MEM_DDR4) {
1488a597d2a5SAravind Gopalakrishnan 		if (cs_mode > 9)
1489a597d2a5SAravind Gopalakrishnan 			return -1;
1490a597d2a5SAravind Gopalakrishnan 
1491a597d2a5SAravind Gopalakrishnan 		cs_size = ddr4_cs_size(cs_mode);
1492a597d2a5SAravind Gopalakrishnan 	} else if (pvt->dram_type == MEM_LRDDR3) {
1493a597d2a5SAravind Gopalakrishnan 		unsigned rank_multiply = dcsm & 0xf;
1494a597d2a5SAravind Gopalakrishnan 
1495a597d2a5SAravind Gopalakrishnan 		if (rank_multiply == 3)
1496a597d2a5SAravind Gopalakrishnan 			rank_multiply = 4;
1497a597d2a5SAravind Gopalakrishnan 		cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply);
1498a597d2a5SAravind Gopalakrishnan 	} else {
1499a597d2a5SAravind Gopalakrishnan 		/* Minimum cs size is 512mb for F15hM60h*/
1500a597d2a5SAravind Gopalakrishnan 		if (cs_mode == 0x1)
1501a597d2a5SAravind Gopalakrishnan 			return -1;
1502a597d2a5SAravind Gopalakrishnan 
1503a597d2a5SAravind Gopalakrishnan 		cs_size = ddr3_cs_size(cs_mode, false);
1504a597d2a5SAravind Gopalakrishnan 	}
1505a597d2a5SAravind Gopalakrishnan 
1506a597d2a5SAravind Gopalakrishnan 	return cs_size;
1507a597d2a5SAravind Gopalakrishnan }
1508a597d2a5SAravind Gopalakrishnan 
150994c1acf2SAravind Gopalakrishnan /*
151018b94f66SAravind Gopalakrishnan  * F16h and F15h model 30h have only limited cs_modes.
151194c1acf2SAravind Gopalakrishnan  */
151294c1acf2SAravind Gopalakrishnan static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1513a597d2a5SAravind Gopalakrishnan 				unsigned cs_mode, int cs_mask_nr)
151494c1acf2SAravind Gopalakrishnan {
151594c1acf2SAravind Gopalakrishnan 	WARN_ON(cs_mode > 12);
151694c1acf2SAravind Gopalakrishnan 
151794c1acf2SAravind Gopalakrishnan 	if (cs_mode == 6 || cs_mode == 8 ||
151894c1acf2SAravind Gopalakrishnan 	    cs_mode == 9 || cs_mode == 12)
151994c1acf2SAravind Gopalakrishnan 		return -1;
152094c1acf2SAravind Gopalakrishnan 	else
152194c1acf2SAravind Gopalakrishnan 		return ddr3_cs_size(cs_mode, false);
152294c1acf2SAravind Gopalakrishnan }
152394c1acf2SAravind Gopalakrishnan 
1524f1cbbec9SYazen Ghannam static int f17_base_addr_to_cs_size(struct amd64_pvt *pvt, u8 umc,
1525f1cbbec9SYazen Ghannam 				    unsigned int cs_mode, int csrow_nr)
1526f1cbbec9SYazen Ghannam {
1527f1cbbec9SYazen Ghannam 	u32 base_addr = pvt->csels[umc].csbases[csrow_nr];
1528f1cbbec9SYazen Ghannam 
1529f1cbbec9SYazen Ghannam 	/*  Each mask is used for every two base addresses. */
1530f1cbbec9SYazen Ghannam 	u32 addr_mask = pvt->csels[umc].csmasks[csrow_nr >> 1];
1531f1cbbec9SYazen Ghannam 
1532f1cbbec9SYazen Ghannam 	/*  Register [31:1] = Address [39:9]. Size is in kBs here. */
1533f1cbbec9SYazen Ghannam 	u32 size = ((addr_mask >> 1) - (base_addr >> 1) + 1) >> 1;
1534f1cbbec9SYazen Ghannam 
1535f1cbbec9SYazen Ghannam 	edac_dbg(1, "BaseAddr: 0x%x, AddrMask: 0x%x\n", base_addr, addr_mask);
1536f1cbbec9SYazen Ghannam 
1537f1cbbec9SYazen Ghannam 	/* Return size in MBs. */
1538f1cbbec9SYazen Ghannam 	return size >> 10;
1539f1cbbec9SYazen Ghannam }
1540f1cbbec9SYazen Ghannam 
15415a5d2371SBorislav Petkov static void read_dram_ctl_register(struct amd64_pvt *pvt)
15426163b5d4SDoug Thompson {
15436163b5d4SDoug Thompson 
1544a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
15455a5d2371SBorislav Petkov 		return;
15465a5d2371SBorislav Petkov 
15477981a28fSAravind Gopalakrishnan 	if (!amd64_read_pci_cfg(pvt->F2, DCT_SEL_LO, &pvt->dct_sel_lo)) {
1548956b9ba1SJoe Perches 		edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
154978da121eSBorislav Petkov 			 pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
15506163b5d4SDoug Thompson 
1551956b9ba1SJoe Perches 		edac_dbg(0, "  DCTs operate in %s mode\n",
15525a5d2371SBorislav Petkov 			 (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
15536163b5d4SDoug Thompson 
155472381bd5SBorislav Petkov 		if (!dct_ganging_enabled(pvt))
1555956b9ba1SJoe Perches 			edac_dbg(0, "  Address range split per DCT: %s\n",
155672381bd5SBorislav Petkov 				 (dct_high_range_enabled(pvt) ? "yes" : "no"));
155772381bd5SBorislav Petkov 
1558956b9ba1SJoe Perches 		edac_dbg(0, "  data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n",
155972381bd5SBorislav Petkov 			 (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
156072381bd5SBorislav Petkov 			 (dct_memory_cleared(pvt) ? "yes" : "no"));
156172381bd5SBorislav Petkov 
1562956b9ba1SJoe Perches 		edac_dbg(0, "  channel interleave: %s, "
156378da121eSBorislav Petkov 			 "interleave bits selector: 0x%x\n",
156472381bd5SBorislav Petkov 			 (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
15656163b5d4SDoug Thompson 			 dct_sel_interleave_addr(pvt));
15666163b5d4SDoug Thompson 	}
15676163b5d4SDoug Thompson 
15687981a28fSAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F2, DCT_SEL_HI, &pvt->dct_sel_hi);
15696163b5d4SDoug Thompson }
15706163b5d4SDoug Thompson 
1571f71d0a05SDoug Thompson /*
157218b94f66SAravind Gopalakrishnan  * Determine channel (DCT) based on the interleaving mode (see F15h M30h BKDG,
157318b94f66SAravind Gopalakrishnan  * 2.10.12 Memory Interleaving Modes).
157418b94f66SAravind Gopalakrishnan  */
157518b94f66SAravind Gopalakrishnan static u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
157618b94f66SAravind Gopalakrishnan 				     u8 intlv_en, int num_dcts_intlv,
157718b94f66SAravind Gopalakrishnan 				     u32 dct_sel)
157818b94f66SAravind Gopalakrishnan {
157918b94f66SAravind Gopalakrishnan 	u8 channel = 0;
158018b94f66SAravind Gopalakrishnan 	u8 select;
158118b94f66SAravind Gopalakrishnan 
158218b94f66SAravind Gopalakrishnan 	if (!(intlv_en))
158318b94f66SAravind Gopalakrishnan 		return (u8)(dct_sel);
158418b94f66SAravind Gopalakrishnan 
158518b94f66SAravind Gopalakrishnan 	if (num_dcts_intlv == 2) {
158618b94f66SAravind Gopalakrishnan 		select = (sys_addr >> 8) & 0x3;
158718b94f66SAravind Gopalakrishnan 		channel = select ? 0x3 : 0;
15889d0e8d83SAravind Gopalakrishnan 	} else if (num_dcts_intlv == 4) {
15899d0e8d83SAravind Gopalakrishnan 		u8 intlv_addr = dct_sel_interleave_addr(pvt);
15909d0e8d83SAravind Gopalakrishnan 		switch (intlv_addr) {
15919d0e8d83SAravind Gopalakrishnan 		case 0x4:
15929d0e8d83SAravind Gopalakrishnan 			channel = (sys_addr >> 8) & 0x3;
15939d0e8d83SAravind Gopalakrishnan 			break;
15949d0e8d83SAravind Gopalakrishnan 		case 0x5:
15959d0e8d83SAravind Gopalakrishnan 			channel = (sys_addr >> 9) & 0x3;
15969d0e8d83SAravind Gopalakrishnan 			break;
15979d0e8d83SAravind Gopalakrishnan 		}
15989d0e8d83SAravind Gopalakrishnan 	}
159918b94f66SAravind Gopalakrishnan 	return channel;
160018b94f66SAravind Gopalakrishnan }
160118b94f66SAravind Gopalakrishnan 
160218b94f66SAravind Gopalakrishnan /*
1603229a7a11SBorislav Petkov  * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory
1604f71d0a05SDoug Thompson  * Interleaving Modes.
1605f71d0a05SDoug Thompson  */
1606b15f0fcaSBorislav Petkov static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
1607229a7a11SBorislav Petkov 				bool hi_range_sel, u8 intlv_en)
16086163b5d4SDoug Thompson {
1609151fa71cSBorislav Petkov 	u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1;
16106163b5d4SDoug Thompson 
16116163b5d4SDoug Thompson 	if (dct_ganging_enabled(pvt))
1612229a7a11SBorislav Petkov 		return 0;
1613229a7a11SBorislav Petkov 
1614229a7a11SBorislav Petkov 	if (hi_range_sel)
1615229a7a11SBorislav Petkov 		return dct_sel_high;
1616229a7a11SBorislav Petkov 
1617f71d0a05SDoug Thompson 	/*
1618f71d0a05SDoug Thompson 	 * see F2x110[DctSelIntLvAddr] - channel interleave mode
1619f71d0a05SDoug Thompson 	 */
1620229a7a11SBorislav Petkov 	if (dct_interleave_enabled(pvt)) {
1621229a7a11SBorislav Petkov 		u8 intlv_addr = dct_sel_interleave_addr(pvt);
16226163b5d4SDoug Thompson 
1623229a7a11SBorislav Petkov 		/* return DCT select function: 0=DCT0, 1=DCT1 */
1624229a7a11SBorislav Petkov 		if (!intlv_addr)
1625229a7a11SBorislav Petkov 			return sys_addr >> 6 & 1;
16266163b5d4SDoug Thompson 
1627229a7a11SBorislav Petkov 		if (intlv_addr & 0x2) {
1628229a7a11SBorislav Petkov 			u8 shift = intlv_addr & 0x1 ? 9 : 6;
1629dc0a50a8SYazen Ghannam 			u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) & 1;
1630229a7a11SBorislav Petkov 
1631229a7a11SBorislav Petkov 			return ((sys_addr >> shift) & 1) ^ temp;
16326163b5d4SDoug Thompson 		}
16336163b5d4SDoug Thompson 
1634dc0a50a8SYazen Ghannam 		if (intlv_addr & 0x4) {
1635dc0a50a8SYazen Ghannam 			u8 shift = intlv_addr & 0x1 ? 9 : 8;
1636dc0a50a8SYazen Ghannam 
1637dc0a50a8SYazen Ghannam 			return (sys_addr >> shift) & 1;
1638dc0a50a8SYazen Ghannam 		}
1639dc0a50a8SYazen Ghannam 
1640229a7a11SBorislav Petkov 		return (sys_addr >> (12 + hweight8(intlv_en))) & 1;
1641229a7a11SBorislav Petkov 	}
1642229a7a11SBorislav Petkov 
1643229a7a11SBorislav Petkov 	if (dct_high_range_enabled(pvt))
1644229a7a11SBorislav Petkov 		return ~dct_sel_high & 1;
16456163b5d4SDoug Thompson 
16466163b5d4SDoug Thompson 	return 0;
16476163b5d4SDoug Thompson }
16486163b5d4SDoug Thompson 
1649c8e518d5SBorislav Petkov /* Convert the sys_addr to the normalized DCT address */
1650c7e5301aSDaniel J Blueman static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range,
1651c8e518d5SBorislav Petkov 				 u64 sys_addr, bool hi_rng,
1652c8e518d5SBorislav Petkov 				 u32 dct_sel_base_addr)
16536163b5d4SDoug Thompson {
16546163b5d4SDoug Thompson 	u64 chan_off;
1655c8e518d5SBorislav Petkov 	u64 dram_base		= get_dram_base(pvt, range);
1656c8e518d5SBorislav Petkov 	u64 hole_off		= f10_dhar_offset(pvt);
16576f3508f6SDan Carpenter 	u64 dct_sel_base_off	= (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16;
16586163b5d4SDoug Thompson 
1659c8e518d5SBorislav Petkov 	if (hi_rng) {
1660c8e518d5SBorislav Petkov 		/*
1661c8e518d5SBorislav Petkov 		 * if
1662c8e518d5SBorislav Petkov 		 * base address of high range is below 4Gb
1663c8e518d5SBorislav Petkov 		 * (bits [47:27] at [31:11])
1664c8e518d5SBorislav Petkov 		 * DRAM address space on this DCT is hoisted above 4Gb	&&
1665c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1666c8e518d5SBorislav Petkov 		 *
1667c8e518d5SBorislav Petkov 		 *	remove hole offset from sys_addr
1668c8e518d5SBorislav Petkov 		 * else
1669c8e518d5SBorislav Petkov 		 *	remove high range offset from sys_addr
1670c8e518d5SBorislav Petkov 		 */
1671c8e518d5SBorislav Petkov 		if ((!(dct_sel_base_addr >> 16) ||
1672c8e518d5SBorislav Petkov 		     dct_sel_base_addr < dhar_base(pvt)) &&
1673972ea17aSBorislav Petkov 		    dhar_valid(pvt) &&
1674c8e518d5SBorislav Petkov 		    (sys_addr >= BIT_64(32)))
1675bc21fa57SBorislav Petkov 			chan_off = hole_off;
16766163b5d4SDoug Thompson 		else
16776163b5d4SDoug Thompson 			chan_off = dct_sel_base_off;
16786163b5d4SDoug Thompson 	} else {
1679c8e518d5SBorislav Petkov 		/*
1680c8e518d5SBorislav Petkov 		 * if
1681c8e518d5SBorislav Petkov 		 * we have a valid hole		&&
1682c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1683c8e518d5SBorislav Petkov 		 *
1684c8e518d5SBorislav Petkov 		 *	remove hole
1685c8e518d5SBorislav Petkov 		 * else
1686c8e518d5SBorislav Petkov 		 *	remove dram base to normalize to DCT address
1687c8e518d5SBorislav Petkov 		 */
1688972ea17aSBorislav Petkov 		if (dhar_valid(pvt) && (sys_addr >= BIT_64(32)))
1689bc21fa57SBorislav Petkov 			chan_off = hole_off;
16906163b5d4SDoug Thompson 		else
1691c8e518d5SBorislav Petkov 			chan_off = dram_base;
16926163b5d4SDoug Thompson 	}
16936163b5d4SDoug Thompson 
169410ef6b0dSChen, Gong 	return (sys_addr & GENMASK_ULL(47,6)) - (chan_off & GENMASK_ULL(47,23));
16956163b5d4SDoug Thompson }
16966163b5d4SDoug Thompson 
16976163b5d4SDoug Thompson /*
16986163b5d4SDoug Thompson  * checks if the csrow passed in is marked as SPARED, if so returns the new
16996163b5d4SDoug Thompson  * spare row
17006163b5d4SDoug Thompson  */
170111c75eadSBorislav Petkov static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow)
17026163b5d4SDoug Thompson {
1703614ec9d8SBorislav Petkov 	int tmp_cs;
17046163b5d4SDoug Thompson 
1705614ec9d8SBorislav Petkov 	if (online_spare_swap_done(pvt, dct) &&
1706614ec9d8SBorislav Petkov 	    csrow == online_spare_bad_dramcs(pvt, dct)) {
1707614ec9d8SBorislav Petkov 
1708614ec9d8SBorislav Petkov 		for_each_chip_select(tmp_cs, dct, pvt) {
1709614ec9d8SBorislav Petkov 			if (chip_select_base(tmp_cs, dct, pvt) & 0x2) {
1710614ec9d8SBorislav Petkov 				csrow = tmp_cs;
1711614ec9d8SBorislav Petkov 				break;
1712614ec9d8SBorislav Petkov 			}
1713614ec9d8SBorislav Petkov 		}
17146163b5d4SDoug Thompson 	}
17156163b5d4SDoug Thompson 	return csrow;
17166163b5d4SDoug Thompson }
17176163b5d4SDoug Thompson 
17186163b5d4SDoug Thompson /*
17196163b5d4SDoug Thompson  * Iterate over the DRAM DCT "base" and "mask" registers looking for a
17206163b5d4SDoug Thompson  * SystemAddr match on the specified 'ChannelSelect' and 'NodeID'
17216163b5d4SDoug Thompson  *
17226163b5d4SDoug Thompson  * Return:
17236163b5d4SDoug Thompson  *	-EINVAL:  NOT FOUND
17246163b5d4SDoug Thompson  *	0..csrow = Chip-Select Row
17256163b5d4SDoug Thompson  */
1726c7e5301aSDaniel J Blueman static int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct)
17276163b5d4SDoug Thompson {
17286163b5d4SDoug Thompson 	struct mem_ctl_info *mci;
17296163b5d4SDoug Thompson 	struct amd64_pvt *pvt;
173011c75eadSBorislav Petkov 	u64 cs_base, cs_mask;
17316163b5d4SDoug Thompson 	int cs_found = -EINVAL;
17326163b5d4SDoug Thompson 	int csrow;
17336163b5d4SDoug Thompson 
17342ec591acSBorislav Petkov 	mci = edac_mc_find(nid);
17356163b5d4SDoug Thompson 	if (!mci)
17366163b5d4SDoug Thompson 		return cs_found;
17376163b5d4SDoug Thompson 
17386163b5d4SDoug Thompson 	pvt = mci->pvt_info;
17396163b5d4SDoug Thompson 
1740956b9ba1SJoe Perches 	edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct);
17416163b5d4SDoug Thompson 
174211c75eadSBorislav Petkov 	for_each_chip_select(csrow, dct, pvt) {
174311c75eadSBorislav Petkov 		if (!csrow_enabled(csrow, dct, pvt))
17446163b5d4SDoug Thompson 			continue;
17456163b5d4SDoug Thompson 
174611c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask);
17476163b5d4SDoug Thompson 
1748956b9ba1SJoe Perches 		edac_dbg(1, "    CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
17496163b5d4SDoug Thompson 			 csrow, cs_base, cs_mask);
17506163b5d4SDoug Thompson 
175111c75eadSBorislav Petkov 		cs_mask = ~cs_mask;
17526163b5d4SDoug Thompson 
1753956b9ba1SJoe Perches 		edac_dbg(1, "    (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n",
175411c75eadSBorislav Petkov 			 (in_addr & cs_mask), (cs_base & cs_mask));
17556163b5d4SDoug Thompson 
175611c75eadSBorislav Petkov 		if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
175718b94f66SAravind Gopalakrishnan 			if (pvt->fam == 0x15 && pvt->model >= 0x30) {
175818b94f66SAravind Gopalakrishnan 				cs_found =  csrow;
175918b94f66SAravind Gopalakrishnan 				break;
176018b94f66SAravind Gopalakrishnan 			}
176111c75eadSBorislav Petkov 			cs_found = f10_process_possible_spare(pvt, dct, csrow);
17626163b5d4SDoug Thompson 
1763956b9ba1SJoe Perches 			edac_dbg(1, " MATCH csrow=%d\n", cs_found);
17646163b5d4SDoug Thompson 			break;
17656163b5d4SDoug Thompson 		}
17666163b5d4SDoug Thompson 	}
17676163b5d4SDoug Thompson 	return cs_found;
17686163b5d4SDoug Thompson }
17696163b5d4SDoug Thompson 
177095b0ef55SBorislav Petkov /*
177195b0ef55SBorislav Petkov  * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is
177295b0ef55SBorislav Petkov  * swapped with a region located at the bottom of memory so that the GPU can use
177395b0ef55SBorislav Petkov  * the interleaved region and thus two channels.
177495b0ef55SBorislav Petkov  */
1775b15f0fcaSBorislav Petkov static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr)
177695b0ef55SBorislav Petkov {
177795b0ef55SBorislav Petkov 	u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr;
177895b0ef55SBorislav Petkov 
1779a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10) {
178095b0ef55SBorislav Petkov 		/* only revC3 and revE have that feature */
1781a4b4bedcSBorislav Petkov 		if (pvt->model < 4 || (pvt->model < 0xa && pvt->stepping < 3))
178295b0ef55SBorislav Petkov 			return sys_addr;
178395b0ef55SBorislav Petkov 	}
178495b0ef55SBorislav Petkov 
17857981a28fSAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F2, SWAP_INTLV_REG, &swap_reg);
178695b0ef55SBorislav Petkov 
178795b0ef55SBorislav Petkov 	if (!(swap_reg & 0x1))
178895b0ef55SBorislav Petkov 		return sys_addr;
178995b0ef55SBorislav Petkov 
179095b0ef55SBorislav Petkov 	swap_base	= (swap_reg >> 3) & 0x7f;
179195b0ef55SBorislav Petkov 	swap_limit	= (swap_reg >> 11) & 0x7f;
179295b0ef55SBorislav Petkov 	rgn_size	= (swap_reg >> 20) & 0x7f;
179395b0ef55SBorislav Petkov 	tmp_addr	= sys_addr >> 27;
179495b0ef55SBorislav Petkov 
179595b0ef55SBorislav Petkov 	if (!(sys_addr >> 34) &&
179695b0ef55SBorislav Petkov 	    (((tmp_addr >= swap_base) &&
179795b0ef55SBorislav Petkov 	     (tmp_addr <= swap_limit)) ||
179895b0ef55SBorislav Petkov 	     (tmp_addr < rgn_size)))
179995b0ef55SBorislav Petkov 		return sys_addr ^ (u64)swap_base << 27;
180095b0ef55SBorislav Petkov 
180195b0ef55SBorislav Petkov 	return sys_addr;
180295b0ef55SBorislav Petkov }
180395b0ef55SBorislav Petkov 
1804f71d0a05SDoug Thompson /* For a given @dram_range, check if @sys_addr falls within it. */
1805e761359aSBorislav Petkov static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
180633ca0643SBorislav Petkov 				  u64 sys_addr, int *chan_sel)
1807f71d0a05SDoug Thompson {
1808229a7a11SBorislav Petkov 	int cs_found = -EINVAL;
1809c8e518d5SBorislav Petkov 	u64 chan_addr;
18105d4b58e8SBorislav Petkov 	u32 dct_sel_base;
181111c75eadSBorislav Petkov 	u8 channel;
1812229a7a11SBorislav Petkov 	bool high_range = false;
1813f71d0a05SDoug Thompson 
18147f19bf75SBorislav Petkov 	u8 node_id    = dram_dst_node(pvt, range);
1815229a7a11SBorislav Petkov 	u8 intlv_en   = dram_intlv_en(pvt, range);
18167f19bf75SBorislav Petkov 	u32 intlv_sel = dram_intlv_sel(pvt, range);
1817f71d0a05SDoug Thompson 
1818956b9ba1SJoe Perches 	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
1819c8e518d5SBorislav Petkov 		 range, sys_addr, get_dram_limit(pvt, range));
1820f71d0a05SDoug Thompson 
1821355fba60SBorislav Petkov 	if (dhar_valid(pvt) &&
1822355fba60SBorislav Petkov 	    dhar_base(pvt) <= sys_addr &&
1823355fba60SBorislav Petkov 	    sys_addr < BIT_64(32)) {
1824355fba60SBorislav Petkov 		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
1825355fba60SBorislav Petkov 			    sys_addr);
1826f71d0a05SDoug Thompson 		return -EINVAL;
1827355fba60SBorislav Petkov 	}
1828355fba60SBorislav Petkov 
1829f030ddfbSBorislav Petkov 	if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en)))
1830355fba60SBorislav Petkov 		return -EINVAL;
1831f71d0a05SDoug Thompson 
1832b15f0fcaSBorislav Petkov 	sys_addr = f1x_swap_interleaved_region(pvt, sys_addr);
183395b0ef55SBorislav Petkov 
1834f71d0a05SDoug Thompson 	dct_sel_base = dct_sel_baseaddr(pvt);
1835f71d0a05SDoug Thompson 
1836f71d0a05SDoug Thompson 	/*
1837f71d0a05SDoug Thompson 	 * check whether addresses >= DctSelBaseAddr[47:27] are to be used to
1838f71d0a05SDoug Thompson 	 * select between DCT0 and DCT1.
1839f71d0a05SDoug Thompson 	 */
1840f71d0a05SDoug Thompson 	if (dct_high_range_enabled(pvt) &&
1841f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt) &&
1842f71d0a05SDoug Thompson 	   ((sys_addr >> 27) >= (dct_sel_base >> 11)))
1843229a7a11SBorislav Petkov 		high_range = true;
1844f71d0a05SDoug Thompson 
1845b15f0fcaSBorislav Petkov 	channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en);
1846f71d0a05SDoug Thompson 
1847b15f0fcaSBorislav Petkov 	chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr,
1848c8e518d5SBorislav Petkov 					  high_range, dct_sel_base);
1849f71d0a05SDoug Thompson 
1850e2f79dbdSBorislav Petkov 	/* Remove node interleaving, see F1x120 */
1851e2f79dbdSBorislav Petkov 	if (intlv_en)
1852e2f79dbdSBorislav Petkov 		chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) |
1853e2f79dbdSBorislav Petkov 			    (chan_addr & 0xfff);
1854f71d0a05SDoug Thompson 
18555d4b58e8SBorislav Petkov 	/* remove channel interleave */
1856f71d0a05SDoug Thompson 	if (dct_interleave_enabled(pvt) &&
1857f71d0a05SDoug Thompson 	   !dct_high_range_enabled(pvt) &&
1858f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt)) {
18595d4b58e8SBorislav Petkov 
18605d4b58e8SBorislav Petkov 		if (dct_sel_interleave_addr(pvt) != 1) {
18615d4b58e8SBorislav Petkov 			if (dct_sel_interleave_addr(pvt) == 0x3)
18625d4b58e8SBorislav Petkov 				/* hash 9 */
18635d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 10) << 9) |
18645d4b58e8SBorislav Petkov 					     (chan_addr & 0x1ff);
18655d4b58e8SBorislav Petkov 			else
18665d4b58e8SBorislav Petkov 				/* A[6] or hash 6 */
18675d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 7) << 6) |
18685d4b58e8SBorislav Petkov 					     (chan_addr & 0x3f);
18695d4b58e8SBorislav Petkov 		} else
18705d4b58e8SBorislav Petkov 			/* A[12] */
18715d4b58e8SBorislav Petkov 			chan_addr = ((chan_addr >> 13) << 12) |
18725d4b58e8SBorislav Petkov 				     (chan_addr & 0xfff);
1873f71d0a05SDoug Thompson 	}
1874f71d0a05SDoug Thompson 
1875956b9ba1SJoe Perches 	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
1876f71d0a05SDoug Thompson 
1877b15f0fcaSBorislav Petkov 	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel);
1878f71d0a05SDoug Thompson 
187933ca0643SBorislav Petkov 	if (cs_found >= 0)
1880f71d0a05SDoug Thompson 		*chan_sel = channel;
188133ca0643SBorislav Petkov 
1882f71d0a05SDoug Thompson 	return cs_found;
1883f71d0a05SDoug Thompson }
1884f71d0a05SDoug Thompson 
188518b94f66SAravind Gopalakrishnan static int f15_m30h_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
188618b94f66SAravind Gopalakrishnan 					u64 sys_addr, int *chan_sel)
188718b94f66SAravind Gopalakrishnan {
188818b94f66SAravind Gopalakrishnan 	int cs_found = -EINVAL;
188918b94f66SAravind Gopalakrishnan 	int num_dcts_intlv = 0;
189018b94f66SAravind Gopalakrishnan 	u64 chan_addr, chan_offset;
189118b94f66SAravind Gopalakrishnan 	u64 dct_base, dct_limit;
189218b94f66SAravind Gopalakrishnan 	u32 dct_cont_base_reg, dct_cont_limit_reg, tmp;
189318b94f66SAravind Gopalakrishnan 	u8 channel, alias_channel, leg_mmio_hole, dct_sel, dct_offset_en;
189418b94f66SAravind Gopalakrishnan 
189518b94f66SAravind Gopalakrishnan 	u64 dhar_offset		= f10_dhar_offset(pvt);
189618b94f66SAravind Gopalakrishnan 	u8 intlv_addr		= dct_sel_interleave_addr(pvt);
189718b94f66SAravind Gopalakrishnan 	u8 node_id		= dram_dst_node(pvt, range);
189818b94f66SAravind Gopalakrishnan 	u8 intlv_en		= dram_intlv_en(pvt, range);
189918b94f66SAravind Gopalakrishnan 
190018b94f66SAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F1, DRAM_CONT_BASE, &dct_cont_base_reg);
190118b94f66SAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F1, DRAM_CONT_LIMIT, &dct_cont_limit_reg);
190218b94f66SAravind Gopalakrishnan 
190318b94f66SAravind Gopalakrishnan 	dct_offset_en		= (u8) ((dct_cont_base_reg >> 3) & BIT(0));
190418b94f66SAravind Gopalakrishnan 	dct_sel			= (u8) ((dct_cont_base_reg >> 4) & 0x7);
190518b94f66SAravind Gopalakrishnan 
190618b94f66SAravind Gopalakrishnan 	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
190718b94f66SAravind Gopalakrishnan 		 range, sys_addr, get_dram_limit(pvt, range));
190818b94f66SAravind Gopalakrishnan 
190918b94f66SAravind Gopalakrishnan 	if (!(get_dram_base(pvt, range)  <= sys_addr) &&
191018b94f66SAravind Gopalakrishnan 	    !(get_dram_limit(pvt, range) >= sys_addr))
191118b94f66SAravind Gopalakrishnan 		return -EINVAL;
191218b94f66SAravind Gopalakrishnan 
191318b94f66SAravind Gopalakrishnan 	if (dhar_valid(pvt) &&
191418b94f66SAravind Gopalakrishnan 	    dhar_base(pvt) <= sys_addr &&
191518b94f66SAravind Gopalakrishnan 	    sys_addr < BIT_64(32)) {
191618b94f66SAravind Gopalakrishnan 		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
191718b94f66SAravind Gopalakrishnan 			    sys_addr);
191818b94f66SAravind Gopalakrishnan 		return -EINVAL;
191918b94f66SAravind Gopalakrishnan 	}
192018b94f66SAravind Gopalakrishnan 
192118b94f66SAravind Gopalakrishnan 	/* Verify sys_addr is within DCT Range. */
19224fc06b31SAravind Gopalakrishnan 	dct_base = (u64) dct_sel_baseaddr(pvt);
19234fc06b31SAravind Gopalakrishnan 	dct_limit = (dct_cont_limit_reg >> 11) & 0x1FFF;
192418b94f66SAravind Gopalakrishnan 
192518b94f66SAravind Gopalakrishnan 	if (!(dct_cont_base_reg & BIT(0)) &&
19264fc06b31SAravind Gopalakrishnan 	    !(dct_base <= (sys_addr >> 27) &&
19274fc06b31SAravind Gopalakrishnan 	      dct_limit >= (sys_addr >> 27)))
192818b94f66SAravind Gopalakrishnan 		return -EINVAL;
192918b94f66SAravind Gopalakrishnan 
193018b94f66SAravind Gopalakrishnan 	/* Verify number of dct's that participate in channel interleaving. */
193118b94f66SAravind Gopalakrishnan 	num_dcts_intlv = (int) hweight8(intlv_en);
193218b94f66SAravind Gopalakrishnan 
193318b94f66SAravind Gopalakrishnan 	if (!(num_dcts_intlv % 2 == 0) || (num_dcts_intlv > 4))
193418b94f66SAravind Gopalakrishnan 		return -EINVAL;
193518b94f66SAravind Gopalakrishnan 
1936dc0a50a8SYazen Ghannam 	if (pvt->model >= 0x60)
1937dc0a50a8SYazen Ghannam 		channel = f1x_determine_channel(pvt, sys_addr, false, intlv_en);
1938dc0a50a8SYazen Ghannam 	else
193918b94f66SAravind Gopalakrishnan 		channel = f15_m30h_determine_channel(pvt, sys_addr, intlv_en,
194018b94f66SAravind Gopalakrishnan 						     num_dcts_intlv, dct_sel);
194118b94f66SAravind Gopalakrishnan 
194218b94f66SAravind Gopalakrishnan 	/* Verify we stay within the MAX number of channels allowed */
19437f3f5240SAravind Gopalakrishnan 	if (channel > 3)
194418b94f66SAravind Gopalakrishnan 		return -EINVAL;
194518b94f66SAravind Gopalakrishnan 
194618b94f66SAravind Gopalakrishnan 	leg_mmio_hole = (u8) (dct_cont_base_reg >> 1 & BIT(0));
194718b94f66SAravind Gopalakrishnan 
194818b94f66SAravind Gopalakrishnan 	/* Get normalized DCT addr */
194918b94f66SAravind Gopalakrishnan 	if (leg_mmio_hole && (sys_addr >= BIT_64(32)))
195018b94f66SAravind Gopalakrishnan 		chan_offset = dhar_offset;
195118b94f66SAravind Gopalakrishnan 	else
19524fc06b31SAravind Gopalakrishnan 		chan_offset = dct_base << 27;
195318b94f66SAravind Gopalakrishnan 
195418b94f66SAravind Gopalakrishnan 	chan_addr = sys_addr - chan_offset;
195518b94f66SAravind Gopalakrishnan 
195618b94f66SAravind Gopalakrishnan 	/* remove channel interleave */
195718b94f66SAravind Gopalakrishnan 	if (num_dcts_intlv == 2) {
195818b94f66SAravind Gopalakrishnan 		if (intlv_addr == 0x4)
195918b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 9) << 8) |
196018b94f66SAravind Gopalakrishnan 						(chan_addr & 0xff);
196118b94f66SAravind Gopalakrishnan 		else if (intlv_addr == 0x5)
196218b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 10) << 9) |
196318b94f66SAravind Gopalakrishnan 						(chan_addr & 0x1ff);
196418b94f66SAravind Gopalakrishnan 		else
196518b94f66SAravind Gopalakrishnan 			return -EINVAL;
196618b94f66SAravind Gopalakrishnan 
196718b94f66SAravind Gopalakrishnan 	} else if (num_dcts_intlv == 4) {
196818b94f66SAravind Gopalakrishnan 		if (intlv_addr == 0x4)
196918b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 10) << 8) |
197018b94f66SAravind Gopalakrishnan 							(chan_addr & 0xff);
197118b94f66SAravind Gopalakrishnan 		else if (intlv_addr == 0x5)
197218b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 11) << 9) |
197318b94f66SAravind Gopalakrishnan 							(chan_addr & 0x1ff);
197418b94f66SAravind Gopalakrishnan 		else
197518b94f66SAravind Gopalakrishnan 			return -EINVAL;
197618b94f66SAravind Gopalakrishnan 	}
197718b94f66SAravind Gopalakrishnan 
197818b94f66SAravind Gopalakrishnan 	if (dct_offset_en) {
197918b94f66SAravind Gopalakrishnan 		amd64_read_pci_cfg(pvt->F1,
198018b94f66SAravind Gopalakrishnan 				   DRAM_CONT_HIGH_OFF + (int) channel * 4,
198118b94f66SAravind Gopalakrishnan 				   &tmp);
19824fc06b31SAravind Gopalakrishnan 		chan_addr +=  (u64) ((tmp >> 11) & 0xfff) << 27;
198318b94f66SAravind Gopalakrishnan 	}
198418b94f66SAravind Gopalakrishnan 
198518b94f66SAravind Gopalakrishnan 	f15h_select_dct(pvt, channel);
198618b94f66SAravind Gopalakrishnan 
198718b94f66SAravind Gopalakrishnan 	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
198818b94f66SAravind Gopalakrishnan 
198918b94f66SAravind Gopalakrishnan 	/*
199018b94f66SAravind Gopalakrishnan 	 * Find Chip select:
199118b94f66SAravind Gopalakrishnan 	 * if channel = 3, then alias it to 1. This is because, in F15 M30h,
199218b94f66SAravind Gopalakrishnan 	 * there is support for 4 DCT's, but only 2 are currently functional.
199318b94f66SAravind Gopalakrishnan 	 * They are DCT0 and DCT3. But we have read all registers of DCT3 into
199418b94f66SAravind Gopalakrishnan 	 * pvt->csels[1]. So we need to use '1' here to get correct info.
199518b94f66SAravind Gopalakrishnan 	 * Refer F15 M30h BKDG Section 2.10 and 2.10.3 for clarifications.
199618b94f66SAravind Gopalakrishnan 	 */
199718b94f66SAravind Gopalakrishnan 	alias_channel =  (channel == 3) ? 1 : channel;
199818b94f66SAravind Gopalakrishnan 
199918b94f66SAravind Gopalakrishnan 	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, alias_channel);
200018b94f66SAravind Gopalakrishnan 
200118b94f66SAravind Gopalakrishnan 	if (cs_found >= 0)
200218b94f66SAravind Gopalakrishnan 		*chan_sel = alias_channel;
200318b94f66SAravind Gopalakrishnan 
200418b94f66SAravind Gopalakrishnan 	return cs_found;
200518b94f66SAravind Gopalakrishnan }
200618b94f66SAravind Gopalakrishnan 
200718b94f66SAravind Gopalakrishnan static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt,
200818b94f66SAravind Gopalakrishnan 					u64 sys_addr,
200933ca0643SBorislav Petkov 					int *chan_sel)
2010f71d0a05SDoug Thompson {
2011e761359aSBorislav Petkov 	int cs_found = -EINVAL;
2012e761359aSBorislav Petkov 	unsigned range;
2013f71d0a05SDoug Thompson 
20147f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
20157f19bf75SBorislav Petkov 		if (!dram_rw(pvt, range))
2016f71d0a05SDoug Thompson 			continue;
2017f71d0a05SDoug Thompson 
201818b94f66SAravind Gopalakrishnan 		if (pvt->fam == 0x15 && pvt->model >= 0x30)
201918b94f66SAravind Gopalakrishnan 			cs_found = f15_m30h_match_to_this_node(pvt, range,
202018b94f66SAravind Gopalakrishnan 							       sys_addr,
202118b94f66SAravind Gopalakrishnan 							       chan_sel);
2022f71d0a05SDoug Thompson 
202318b94f66SAravind Gopalakrishnan 		else if ((get_dram_base(pvt, range)  <= sys_addr) &&
202418b94f66SAravind Gopalakrishnan 			 (get_dram_limit(pvt, range) >= sys_addr)) {
2025b15f0fcaSBorislav Petkov 			cs_found = f1x_match_to_this_node(pvt, range,
202633ca0643SBorislav Petkov 							  sys_addr, chan_sel);
2027f71d0a05SDoug Thompson 			if (cs_found >= 0)
2028f71d0a05SDoug Thompson 				break;
2029f71d0a05SDoug Thompson 		}
2030f71d0a05SDoug Thompson 	}
2031f71d0a05SDoug Thompson 	return cs_found;
2032f71d0a05SDoug Thompson }
2033f71d0a05SDoug Thompson 
2034f71d0a05SDoug Thompson /*
2035bdc30a0cSBorislav Petkov  * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
2036bdc30a0cSBorislav Petkov  * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
2037f71d0a05SDoug Thompson  *
2038bdc30a0cSBorislav Petkov  * The @sys_addr is usually an error address received from the hardware
2039bdc30a0cSBorislav Petkov  * (MCX_ADDR).
2040f71d0a05SDoug Thompson  */
2041b15f0fcaSBorislav Petkov static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
204233ca0643SBorislav Petkov 				     struct err_info *err)
2043f71d0a05SDoug Thompson {
2044f71d0a05SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
2045f71d0a05SDoug Thompson 
204633ca0643SBorislav Petkov 	error_address_to_page_and_offset(sys_addr, err);
2047ab5a503cSMauro Carvalho Chehab 
204833ca0643SBorislav Petkov 	err->csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &err->channel);
204933ca0643SBorislav Petkov 	if (err->csrow < 0) {
205033ca0643SBorislav Petkov 		err->err_code = ERR_CSROW;
2051bdc30a0cSBorislav Petkov 		return;
2052bdc30a0cSBorislav Petkov 	}
2053bdc30a0cSBorislav Petkov 
2054f71d0a05SDoug Thompson 	/*
2055bdc30a0cSBorislav Petkov 	 * We need the syndromes for channel detection only when we're
2056bdc30a0cSBorislav Petkov 	 * ganged. Otherwise @chan should already contain the channel at
2057bdc30a0cSBorislav Petkov 	 * this point.
2058f71d0a05SDoug Thompson 	 */
2059a97fa68eSBorislav Petkov 	if (dct_ganging_enabled(pvt))
206033ca0643SBorislav Petkov 		err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
2061f71d0a05SDoug Thompson }
2062f71d0a05SDoug Thompson 
2063f71d0a05SDoug Thompson /*
20648566c4dfSBorislav Petkov  * debug routine to display the memory sizes of all logical DIMMs and its
2065cb328507SBorislav Petkov  * CSROWs
2066f71d0a05SDoug Thompson  */
2067d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
2068f71d0a05SDoug Thompson {
2069bb89f5a0SBorislav Petkov 	int dimm, size0, size1;
2070525a1b20SBorislav Petkov 	u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases;
2071525a1b20SBorislav Petkov 	u32 dbam  = ctrl ? pvt->dbam1 : pvt->dbam0;
2072f71d0a05SDoug Thompson 
2073a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf) {
20748566c4dfSBorislav Petkov 		/* K8 families < revF not supported yet */
20751433eb99SBorislav Petkov 	       if (pvt->ext_model < K8_REV_F)
20768566c4dfSBorislav Petkov 			return;
20778566c4dfSBorislav Petkov 	       else
20788566c4dfSBorislav Petkov 		       WARN_ON(ctrl != 0);
20798566c4dfSBorislav Petkov 	}
20808566c4dfSBorislav Petkov 
20817981a28fSAravind Gopalakrishnan 	if (pvt->fam == 0x10) {
20827981a28fSAravind Gopalakrishnan 		dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1
20837981a28fSAravind Gopalakrishnan 							   : pvt->dbam0;
20847981a28fSAravind Gopalakrishnan 		dcsb = (ctrl && !dct_ganging_enabled(pvt)) ?
20857981a28fSAravind Gopalakrishnan 				 pvt->csels[1].csbases :
20867981a28fSAravind Gopalakrishnan 				 pvt->csels[0].csbases;
20877981a28fSAravind Gopalakrishnan 	} else if (ctrl) {
20887981a28fSAravind Gopalakrishnan 		dbam = pvt->dbam0;
20897981a28fSAravind Gopalakrishnan 		dcsb = pvt->csels[1].csbases;
20907981a28fSAravind Gopalakrishnan 	}
2091956b9ba1SJoe Perches 	edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
2092956b9ba1SJoe Perches 		 ctrl, dbam);
2093f71d0a05SDoug Thompson 
20948566c4dfSBorislav Petkov 	edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
20958566c4dfSBorislav Petkov 
2096f71d0a05SDoug Thompson 	/* Dump memory sizes for DIMM and its CSROWs */
2097f71d0a05SDoug Thompson 	for (dimm = 0; dimm < 4; dimm++) {
2098f71d0a05SDoug Thompson 
2099f71d0a05SDoug Thompson 		size0 = 0;
210011c75eadSBorislav Petkov 		if (dcsb[dimm*2] & DCSB_CS_ENABLE)
210107ed82efSYazen Ghannam 			/*
210207ed82efSYazen Ghannam 			 * For F15m60h, we need multiplier for LRDIMM cs_size
210307ed82efSYazen Ghannam 			 * calculation. We pass dimm value to the dbam_to_cs
2104a597d2a5SAravind Gopalakrishnan 			 * mapper so we can find the multiplier from the
2105a597d2a5SAravind Gopalakrishnan 			 * corresponding DCSM.
2106a597d2a5SAravind Gopalakrishnan 			 */
210741d8bfabSBorislav Petkov 			size0 = pvt->ops->dbam_to_cs(pvt, ctrl,
2108a597d2a5SAravind Gopalakrishnan 						     DBAM_DIMM(dimm, dbam),
2109a597d2a5SAravind Gopalakrishnan 						     dimm);
2110f71d0a05SDoug Thompson 
2111f71d0a05SDoug Thompson 		size1 = 0;
211211c75eadSBorislav Petkov 		if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE)
211341d8bfabSBorislav Petkov 			size1 = pvt->ops->dbam_to_cs(pvt, ctrl,
2114a597d2a5SAravind Gopalakrishnan 						     DBAM_DIMM(dimm, dbam),
2115a597d2a5SAravind Gopalakrishnan 						     dimm);
2116f71d0a05SDoug Thompson 
211724f9a7feSBorislav Petkov 		amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
2118bb89f5a0SBorislav Petkov 				dimm * 2,     size0,
2119bb89f5a0SBorislav Petkov 				dimm * 2 + 1, size1);
2120f71d0a05SDoug Thompson 	}
2121f71d0a05SDoug Thompson }
2122f71d0a05SDoug Thompson 
2123d1ea71cdSBorislav Petkov static struct amd64_family_type family_types[] = {
21244d37607aSDoug Thompson 	[K8_CPUS] = {
21250092b20dSBorislav Petkov 		.ctl_name = "K8",
21268d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
21273f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
21284d37607aSDoug Thompson 		.ops = {
21294d37607aSDoug Thompson 			.early_channel_count	= k8_early_channel_count,
21304d37607aSDoug Thompson 			.map_sysaddr_to_csrow	= k8_map_sysaddr_to_csrow,
21311433eb99SBorislav Petkov 			.dbam_to_cs		= k8_dbam_to_chip_select,
21324d37607aSDoug Thompson 		}
21334d37607aSDoug Thompson 	},
21344d37607aSDoug Thompson 	[F10_CPUS] = {
21350092b20dSBorislav Petkov 		.ctl_name = "F10h",
21368d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP,
21373f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM,
21384d37607aSDoug Thompson 		.ops = {
21397d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
2140b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
21411433eb99SBorislav Petkov 			.dbam_to_cs		= f10_dbam_to_chip_select,
2142b2b0c605SBorislav Petkov 		}
2143b2b0c605SBorislav Petkov 	},
2144b2b0c605SBorislav Petkov 	[F15_CPUS] = {
2145b2b0c605SBorislav Petkov 		.ctl_name = "F15h",
2146df71a053SBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1,
21473f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2,
2148b2b0c605SBorislav Petkov 		.ops = {
21497d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
2150b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
215141d8bfabSBorislav Petkov 			.dbam_to_cs		= f15_dbam_to_chip_select,
21524d37607aSDoug Thompson 		}
21534d37607aSDoug Thompson 	},
215418b94f66SAravind Gopalakrishnan 	[F15_M30H_CPUS] = {
215518b94f66SAravind Gopalakrishnan 		.ctl_name = "F15h_M30h",
215618b94f66SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1,
21573f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2,
215818b94f66SAravind Gopalakrishnan 		.ops = {
215918b94f66SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
216018b94f66SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
216118b94f66SAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
216218b94f66SAravind Gopalakrishnan 		}
216318b94f66SAravind Gopalakrishnan 	},
2164a597d2a5SAravind Gopalakrishnan 	[F15_M60H_CPUS] = {
2165a597d2a5SAravind Gopalakrishnan 		.ctl_name = "F15h_M60h",
2166a597d2a5SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1,
21673f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2,
2168a597d2a5SAravind Gopalakrishnan 		.ops = {
2169a597d2a5SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
2170a597d2a5SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
2171a597d2a5SAravind Gopalakrishnan 			.dbam_to_cs		= f15_m60h_dbam_to_chip_select,
2172a597d2a5SAravind Gopalakrishnan 		}
2173a597d2a5SAravind Gopalakrishnan 	},
217494c1acf2SAravind Gopalakrishnan 	[F16_CPUS] = {
217594c1acf2SAravind Gopalakrishnan 		.ctl_name = "F16h",
217694c1acf2SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1,
21773f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2,
217894c1acf2SAravind Gopalakrishnan 		.ops = {
217994c1acf2SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
218094c1acf2SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
218194c1acf2SAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
218294c1acf2SAravind Gopalakrishnan 		}
218394c1acf2SAravind Gopalakrishnan 	},
218485a8885bSAravind Gopalakrishnan 	[F16_M30H_CPUS] = {
218585a8885bSAravind Gopalakrishnan 		.ctl_name = "F16h_M30h",
218685a8885bSAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F1,
21873f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2,
218885a8885bSAravind Gopalakrishnan 		.ops = {
218985a8885bSAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
219085a8885bSAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
219185a8885bSAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
219285a8885bSAravind Gopalakrishnan 		}
219385a8885bSAravind Gopalakrishnan 	},
2194f1cbbec9SYazen Ghannam 	[F17_CPUS] = {
2195f1cbbec9SYazen Ghannam 		.ctl_name = "F17h",
2196f1cbbec9SYazen Ghannam 		.f0_id = PCI_DEVICE_ID_AMD_17H_DF_F0,
2197f1cbbec9SYazen Ghannam 		.f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6,
2198f1cbbec9SYazen Ghannam 		.ops = {
2199f1cbbec9SYazen Ghannam 			.early_channel_count	= f17_early_channel_count,
2200f1cbbec9SYazen Ghannam 			.dbam_to_cs		= f17_base_addr_to_cs_size,
2201f1cbbec9SYazen Ghannam 		}
2202f1cbbec9SYazen Ghannam 	},
22038960de4aSMichael Jin 	[F17_M10H_CPUS] = {
22048960de4aSMichael Jin 		.ctl_name = "F17h_M10h",
22058960de4aSMichael Jin 		.f0_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F0,
22068960de4aSMichael Jin 		.f6_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F6,
22078960de4aSMichael Jin 		.ops = {
22088960de4aSMichael Jin 			.early_channel_count	= f17_early_channel_count,
22098960de4aSMichael Jin 			.dbam_to_cs		= f17_base_addr_to_cs_size,
22108960de4aSMichael Jin 		}
22118960de4aSMichael Jin 	},
22124d37607aSDoug Thompson };
22134d37607aSDoug Thompson 
2214b1289d6fSDoug Thompson /*
2215bfc04aecSBorislav Petkov  * These are tables of eigenvectors (one per line) which can be used for the
2216bfc04aecSBorislav Petkov  * construction of the syndrome tables. The modified syndrome search algorithm
2217bfc04aecSBorislav Petkov  * uses those to find the symbol in error and thus the DIMM.
2218b1289d6fSDoug Thompson  *
2219bfc04aecSBorislav Petkov  * Algorithm courtesy of Ross LaFetra from AMD.
2220b1289d6fSDoug Thompson  */
2221c7e5301aSDaniel J Blueman static const u16 x4_vectors[] = {
2222bfc04aecSBorislav Petkov 	0x2f57, 0x1afe, 0x66cc, 0xdd88,
2223bfc04aecSBorislav Petkov 	0x11eb, 0x3396, 0x7f4c, 0xeac8,
2224bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008,
2225bfc04aecSBorislav Petkov 	0x1013, 0x3032, 0x4044, 0x8088,
2226bfc04aecSBorislav Petkov 	0x106b, 0x30d6, 0x70fc, 0xe0a8,
2227bfc04aecSBorislav Petkov 	0x4857, 0xc4fe, 0x13cc, 0x3288,
2228bfc04aecSBorislav Petkov 	0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
2229bfc04aecSBorislav Petkov 	0x1f39, 0x251e, 0xbd6c, 0x6bd8,
2230bfc04aecSBorislav Petkov 	0x15c1, 0x2a42, 0x89ac, 0x4758,
2231bfc04aecSBorislav Petkov 	0x2b03, 0x1602, 0x4f0c, 0xca08,
2232bfc04aecSBorislav Petkov 	0x1f07, 0x3a0e, 0x6b04, 0xbd08,
2233bfc04aecSBorislav Petkov 	0x8ba7, 0x465e, 0x244c, 0x1cc8,
2234bfc04aecSBorislav Petkov 	0x2b87, 0x164e, 0x642c, 0xdc18,
2235bfc04aecSBorislav Petkov 	0x40b9, 0x80de, 0x1094, 0x20e8,
2236bfc04aecSBorislav Petkov 	0x27db, 0x1eb6, 0x9dac, 0x7b58,
2237bfc04aecSBorislav Petkov 	0x11c1, 0x2242, 0x84ac, 0x4c58,
2238bfc04aecSBorislav Petkov 	0x1be5, 0x2d7a, 0x5e34, 0xa718,
2239bfc04aecSBorislav Petkov 	0x4b39, 0x8d1e, 0x14b4, 0x28d8,
2240bfc04aecSBorislav Petkov 	0x4c97, 0xc87e, 0x11fc, 0x33a8,
2241bfc04aecSBorislav Petkov 	0x8e97, 0x497e, 0x2ffc, 0x1aa8,
2242bfc04aecSBorislav Petkov 	0x16b3, 0x3d62, 0x4f34, 0x8518,
2243bfc04aecSBorislav Petkov 	0x1e2f, 0x391a, 0x5cac, 0xf858,
2244bfc04aecSBorislav Petkov 	0x1d9f, 0x3b7a, 0x572c, 0xfe18,
2245bfc04aecSBorislav Petkov 	0x15f5, 0x2a5a, 0x5264, 0xa3b8,
2246bfc04aecSBorislav Petkov 	0x1dbb, 0x3b66, 0x715c, 0xe3f8,
2247bfc04aecSBorislav Petkov 	0x4397, 0xc27e, 0x17fc, 0x3ea8,
2248bfc04aecSBorislav Petkov 	0x1617, 0x3d3e, 0x6464, 0xb8b8,
2249bfc04aecSBorislav Petkov 	0x23ff, 0x12aa, 0xab6c, 0x56d8,
2250bfc04aecSBorislav Petkov 	0x2dfb, 0x1ba6, 0x913c, 0x7328,
2251bfc04aecSBorislav Petkov 	0x185d, 0x2ca6, 0x7914, 0x9e28,
2252bfc04aecSBorislav Petkov 	0x171b, 0x3e36, 0x7d7c, 0xebe8,
2253bfc04aecSBorislav Petkov 	0x4199, 0x82ee, 0x19f4, 0x2e58,
2254bfc04aecSBorislav Petkov 	0x4807, 0xc40e, 0x130c, 0x3208,
2255bfc04aecSBorislav Petkov 	0x1905, 0x2e0a, 0x5804, 0xac08,
2256bfc04aecSBorislav Petkov 	0x213f, 0x132a, 0xadfc, 0x5ba8,
2257bfc04aecSBorislav Petkov 	0x19a9, 0x2efe, 0xb5cc, 0x6f88,
2258b1289d6fSDoug Thompson };
2259b1289d6fSDoug Thompson 
2260c7e5301aSDaniel J Blueman static const u16 x8_vectors[] = {
2261bfc04aecSBorislav Petkov 	0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
2262bfc04aecSBorislav Petkov 	0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
2263bfc04aecSBorislav Petkov 	0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
2264bfc04aecSBorislav Petkov 	0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
2265bfc04aecSBorislav Petkov 	0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
2266bfc04aecSBorislav Petkov 	0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
2267bfc04aecSBorislav Petkov 	0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
2268bfc04aecSBorislav Petkov 	0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
2269bfc04aecSBorislav Petkov 	0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
2270bfc04aecSBorislav Petkov 	0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
2271bfc04aecSBorislav Petkov 	0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
2272bfc04aecSBorislav Petkov 	0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
2273bfc04aecSBorislav Petkov 	0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
2274bfc04aecSBorislav Petkov 	0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
2275bfc04aecSBorislav Petkov 	0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
2276bfc04aecSBorislav Petkov 	0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
2277bfc04aecSBorislav Petkov 	0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
2278bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
2279bfc04aecSBorislav Petkov 	0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
2280bfc04aecSBorislav Petkov };
2281bfc04aecSBorislav Petkov 
2282c7e5301aSDaniel J Blueman static int decode_syndrome(u16 syndrome, const u16 *vectors, unsigned num_vecs,
2283d34a6ecdSBorislav Petkov 			   unsigned v_dim)
2284b1289d6fSDoug Thompson {
2285bfc04aecSBorislav Petkov 	unsigned int i, err_sym;
2286b1289d6fSDoug Thompson 
2287bfc04aecSBorislav Petkov 	for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
2288bfc04aecSBorislav Petkov 		u16 s = syndrome;
2289d34a6ecdSBorislav Petkov 		unsigned v_idx =  err_sym * v_dim;
2290d34a6ecdSBorislav Petkov 		unsigned v_end = (err_sym + 1) * v_dim;
2291b1289d6fSDoug Thompson 
2292bfc04aecSBorislav Petkov 		/* walk over all 16 bits of the syndrome */
2293bfc04aecSBorislav Petkov 		for (i = 1; i < (1U << 16); i <<= 1) {
2294bfc04aecSBorislav Petkov 
2295bfc04aecSBorislav Petkov 			/* if bit is set in that eigenvector... */
2296bfc04aecSBorislav Petkov 			if (v_idx < v_end && vectors[v_idx] & i) {
2297bfc04aecSBorislav Petkov 				u16 ev_comp = vectors[v_idx++];
2298bfc04aecSBorislav Petkov 
2299bfc04aecSBorislav Petkov 				/* ... and bit set in the modified syndrome, */
2300bfc04aecSBorislav Petkov 				if (s & i) {
2301bfc04aecSBorislav Petkov 					/* remove it. */
2302bfc04aecSBorislav Petkov 					s ^= ev_comp;
2303bfc04aecSBorislav Petkov 
2304bfc04aecSBorislav Petkov 					if (!s)
2305bfc04aecSBorislav Petkov 						return err_sym;
2306bfc04aecSBorislav Petkov 				}
2307bfc04aecSBorislav Petkov 
2308bfc04aecSBorislav Petkov 			} else if (s & i)
2309bfc04aecSBorislav Petkov 				/* can't get to zero, move to next symbol */
2310bfc04aecSBorislav Petkov 				break;
2311bfc04aecSBorislav Petkov 		}
2312b1289d6fSDoug Thompson 	}
2313b1289d6fSDoug Thompson 
2314956b9ba1SJoe Perches 	edac_dbg(0, "syndrome(%x) not found\n", syndrome);
2315b1289d6fSDoug Thompson 	return -1;
2316b1289d6fSDoug Thompson }
2317d27bf6faSDoug Thompson 
2318bfc04aecSBorislav Petkov static int map_err_sym_to_channel(int err_sym, int sym_size)
2319bfc04aecSBorislav Petkov {
2320bfc04aecSBorislav Petkov 	if (sym_size == 4)
2321bfc04aecSBorislav Petkov 		switch (err_sym) {
2322bfc04aecSBorislav Petkov 		case 0x20:
2323bfc04aecSBorislav Petkov 		case 0x21:
2324bfc04aecSBorislav Petkov 			return 0;
2325bfc04aecSBorislav Petkov 			break;
2326bfc04aecSBorislav Petkov 		case 0x22:
2327bfc04aecSBorislav Petkov 		case 0x23:
2328bfc04aecSBorislav Petkov 			return 1;
2329bfc04aecSBorislav Petkov 			break;
2330bfc04aecSBorislav Petkov 		default:
2331bfc04aecSBorislav Petkov 			return err_sym >> 4;
2332bfc04aecSBorislav Petkov 			break;
2333bfc04aecSBorislav Petkov 		}
2334bfc04aecSBorislav Petkov 	/* x8 symbols */
2335bfc04aecSBorislav Petkov 	else
2336bfc04aecSBorislav Petkov 		switch (err_sym) {
2337bfc04aecSBorislav Petkov 		/* imaginary bits not in a DIMM */
2338bfc04aecSBorislav Petkov 		case 0x10:
2339bfc04aecSBorislav Petkov 			WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
2340bfc04aecSBorislav Petkov 					  err_sym);
2341bfc04aecSBorislav Petkov 			return -1;
2342bfc04aecSBorislav Petkov 			break;
2343bfc04aecSBorislav Petkov 
2344bfc04aecSBorislav Petkov 		case 0x11:
2345bfc04aecSBorislav Petkov 			return 0;
2346bfc04aecSBorislav Petkov 			break;
2347bfc04aecSBorislav Petkov 		case 0x12:
2348bfc04aecSBorislav Petkov 			return 1;
2349bfc04aecSBorislav Petkov 			break;
2350bfc04aecSBorislav Petkov 		default:
2351bfc04aecSBorislav Petkov 			return err_sym >> 3;
2352bfc04aecSBorislav Petkov 			break;
2353bfc04aecSBorislav Petkov 		}
2354bfc04aecSBorislav Petkov 	return -1;
2355bfc04aecSBorislav Petkov }
2356bfc04aecSBorislav Petkov 
2357bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
2358bfc04aecSBorislav Petkov {
2359bfc04aecSBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
2360ad6a32e9SBorislav Petkov 	int err_sym = -1;
2361bfc04aecSBorislav Petkov 
2362a3b7db09SBorislav Petkov 	if (pvt->ecc_sym_sz == 8)
2363bfc04aecSBorislav Petkov 		err_sym = decode_syndrome(syndrome, x8_vectors,
2364ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x8_vectors),
2365a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
2366a3b7db09SBorislav Petkov 	else if (pvt->ecc_sym_sz == 4)
2367ad6a32e9SBorislav Petkov 		err_sym = decode_syndrome(syndrome, x4_vectors,
2368ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x4_vectors),
2369a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
2370ad6a32e9SBorislav Petkov 	else {
2371a3b7db09SBorislav Petkov 		amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz);
2372ad6a32e9SBorislav Petkov 		return err_sym;
2373bfc04aecSBorislav Petkov 	}
2374ad6a32e9SBorislav Petkov 
2375a3b7db09SBorislav Petkov 	return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz);
237641c31044SBorislav Petkov }
2377bfc04aecSBorislav Petkov 
2378e70984d9SYazen Ghannam static void __log_ecc_error(struct mem_ctl_info *mci, struct err_info *err,
237933ca0643SBorislav Petkov 			    u8 ecc_type)
2380d27bf6faSDoug Thompson {
238133ca0643SBorislav Petkov 	enum hw_event_mc_err_type err_type;
238233ca0643SBorislav Petkov 	const char *string;
2383d27bf6faSDoug Thompson 
238433ca0643SBorislav Petkov 	if (ecc_type == 2)
238533ca0643SBorislav Petkov 		err_type = HW_EVENT_ERR_CORRECTED;
238633ca0643SBorislav Petkov 	else if (ecc_type == 1)
238733ca0643SBorislav Petkov 		err_type = HW_EVENT_ERR_UNCORRECTED;
2388d12a969eSYazen Ghannam 	else if (ecc_type == 3)
2389d12a969eSYazen Ghannam 		err_type = HW_EVENT_ERR_DEFERRED;
239033ca0643SBorislav Petkov 	else {
239133ca0643SBorislav Petkov 		WARN(1, "Something is rotten in the state of Denmark.\n");
2392d27bf6faSDoug Thompson 		return;
2393d27bf6faSDoug Thompson 	}
2394d27bf6faSDoug Thompson 
239533ca0643SBorislav Petkov 	switch (err->err_code) {
239633ca0643SBorislav Petkov 	case DECODE_OK:
239733ca0643SBorislav Petkov 		string = "";
239833ca0643SBorislav Petkov 		break;
239933ca0643SBorislav Petkov 	case ERR_NODE:
240033ca0643SBorislav Petkov 		string = "Failed to map error addr to a node";
240133ca0643SBorislav Petkov 		break;
240233ca0643SBorislav Petkov 	case ERR_CSROW:
240333ca0643SBorislav Petkov 		string = "Failed to map error addr to a csrow";
240433ca0643SBorislav Petkov 		break;
240533ca0643SBorislav Petkov 	case ERR_CHANNEL:
2406713ad546SYazen Ghannam 		string = "Unknown syndrome - possible error reporting race";
2407713ad546SYazen Ghannam 		break;
2408713ad546SYazen Ghannam 	case ERR_SYND:
2409713ad546SYazen Ghannam 		string = "MCA_SYND not valid - unknown syndrome and csrow";
2410713ad546SYazen Ghannam 		break;
2411713ad546SYazen Ghannam 	case ERR_NORM_ADDR:
2412713ad546SYazen Ghannam 		string = "Cannot decode normalized address";
241333ca0643SBorislav Petkov 		break;
241433ca0643SBorislav Petkov 	default:
241533ca0643SBorislav Petkov 		string = "WTF error";
241633ca0643SBorislav Petkov 		break;
2417d27bf6faSDoug Thompson 	}
241833ca0643SBorislav Petkov 
241933ca0643SBorislav Petkov 	edac_mc_handle_error(err_type, mci, 1,
242033ca0643SBorislav Petkov 			     err->page, err->offset, err->syndrome,
242133ca0643SBorislav Petkov 			     err->csrow, err->channel, -1,
242233ca0643SBorislav Petkov 			     string, "");
2423d27bf6faSDoug Thompson }
2424d27bf6faSDoug Thompson 
2425df781d03SBorislav Petkov static inline void decode_bus_error(int node_id, struct mce *m)
2426d27bf6faSDoug Thompson {
24270c510cc8SDaniel J Blueman 	struct mem_ctl_info *mci;
24280c510cc8SDaniel J Blueman 	struct amd64_pvt *pvt;
2429f192c7b1SBorislav Petkov 	u8 ecc_type = (m->status >> 45) & 0x3;
243066fed2d4SBorislav Petkov 	u8 xec = XEC(m->status, 0x1f);
243166fed2d4SBorislav Petkov 	u16 ec = EC(m->status);
243233ca0643SBorislav Petkov 	u64 sys_addr;
243333ca0643SBorislav Petkov 	struct err_info err;
2434d27bf6faSDoug Thompson 
24350c510cc8SDaniel J Blueman 	mci = edac_mc_find(node_id);
24360c510cc8SDaniel J Blueman 	if (!mci)
24370c510cc8SDaniel J Blueman 		return;
24380c510cc8SDaniel J Blueman 
24390c510cc8SDaniel J Blueman 	pvt = mci->pvt_info;
24400c510cc8SDaniel J Blueman 
244166fed2d4SBorislav Petkov 	/* Bail out early if this was an 'observed' error */
24425980bb9cSBorislav Petkov 	if (PP(ec) == NBSL_PP_OBS)
2443b70ef010SBorislav Petkov 		return;
2444d27bf6faSDoug Thompson 
2445ecaf5606SBorislav Petkov 	/* Do only ECC errors */
2446ecaf5606SBorislav Petkov 	if (xec && xec != F10_NBSL_EXT_ERR_ECC)
2447d27bf6faSDoug Thompson 		return;
2448d27bf6faSDoug Thompson 
244933ca0643SBorislav Petkov 	memset(&err, 0, sizeof(err));
245033ca0643SBorislav Petkov 
2451a4b4bedcSBorislav Petkov 	sys_addr = get_error_address(pvt, m);
245233ca0643SBorislav Petkov 
2453ecaf5606SBorislav Petkov 	if (ecc_type == 2)
245433ca0643SBorislav Petkov 		err.syndrome = extract_syndrome(m->status);
245533ca0643SBorislav Petkov 
245633ca0643SBorislav Petkov 	pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err);
245733ca0643SBorislav Petkov 
2458e70984d9SYazen Ghannam 	__log_ecc_error(mci, &err, ecc_type);
2459d27bf6faSDoug Thompson }
2460d27bf6faSDoug Thompson 
24610ec449eeSDoug Thompson /*
2462713ad546SYazen Ghannam  * To find the UMC channel represented by this bank we need to match on its
2463713ad546SYazen Ghannam  * instance_id. The instance_id of a bank is held in the lower 32 bits of its
2464713ad546SYazen Ghannam  * IPID.
2465713ad546SYazen Ghannam  */
2466713ad546SYazen Ghannam static int find_umc_channel(struct amd64_pvt *pvt, struct mce *m)
2467713ad546SYazen Ghannam {
2468713ad546SYazen Ghannam 	u32 umc_instance_id[] = {0x50f00, 0x150f00};
2469713ad546SYazen Ghannam 	u32 instance_id = m->ipid & GENMASK(31, 0);
2470713ad546SYazen Ghannam 	int i, channel = -1;
2471713ad546SYazen Ghannam 
2472713ad546SYazen Ghannam 	for (i = 0; i < ARRAY_SIZE(umc_instance_id); i++)
2473713ad546SYazen Ghannam 		if (umc_instance_id[i] == instance_id)
2474713ad546SYazen Ghannam 			channel = i;
2475713ad546SYazen Ghannam 
2476713ad546SYazen Ghannam 	return channel;
2477713ad546SYazen Ghannam }
2478713ad546SYazen Ghannam 
2479713ad546SYazen Ghannam static void decode_umc_error(int node_id, struct mce *m)
2480713ad546SYazen Ghannam {
2481713ad546SYazen Ghannam 	u8 ecc_type = (m->status >> 45) & 0x3;
2482713ad546SYazen Ghannam 	struct mem_ctl_info *mci;
2483713ad546SYazen Ghannam 	struct amd64_pvt *pvt;
2484713ad546SYazen Ghannam 	struct err_info err;
2485713ad546SYazen Ghannam 	u64 sys_addr;
2486713ad546SYazen Ghannam 
2487713ad546SYazen Ghannam 	mci = edac_mc_find(node_id);
2488713ad546SYazen Ghannam 	if (!mci)
2489713ad546SYazen Ghannam 		return;
2490713ad546SYazen Ghannam 
2491713ad546SYazen Ghannam 	pvt = mci->pvt_info;
2492713ad546SYazen Ghannam 
2493713ad546SYazen Ghannam 	memset(&err, 0, sizeof(err));
2494713ad546SYazen Ghannam 
2495713ad546SYazen Ghannam 	if (m->status & MCI_STATUS_DEFERRED)
2496713ad546SYazen Ghannam 		ecc_type = 3;
2497713ad546SYazen Ghannam 
2498713ad546SYazen Ghannam 	err.channel = find_umc_channel(pvt, m);
2499713ad546SYazen Ghannam 	if (err.channel < 0) {
2500713ad546SYazen Ghannam 		err.err_code = ERR_CHANNEL;
2501713ad546SYazen Ghannam 		goto log_error;
2502713ad546SYazen Ghannam 	}
2503713ad546SYazen Ghannam 
2504713ad546SYazen Ghannam 	if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) {
2505713ad546SYazen Ghannam 		err.err_code = ERR_NORM_ADDR;
2506713ad546SYazen Ghannam 		goto log_error;
2507713ad546SYazen Ghannam 	}
2508713ad546SYazen Ghannam 
2509713ad546SYazen Ghannam 	error_address_to_page_and_offset(sys_addr, &err);
2510713ad546SYazen Ghannam 
2511713ad546SYazen Ghannam 	if (!(m->status & MCI_STATUS_SYNDV)) {
2512713ad546SYazen Ghannam 		err.err_code = ERR_SYND;
2513713ad546SYazen Ghannam 		goto log_error;
2514713ad546SYazen Ghannam 	}
2515713ad546SYazen Ghannam 
2516713ad546SYazen Ghannam 	if (ecc_type == 2) {
2517713ad546SYazen Ghannam 		u8 length = (m->synd >> 18) & 0x3f;
2518713ad546SYazen Ghannam 
2519713ad546SYazen Ghannam 		if (length)
2520713ad546SYazen Ghannam 			err.syndrome = (m->synd >> 32) & GENMASK(length - 1, 0);
2521713ad546SYazen Ghannam 		else
2522713ad546SYazen Ghannam 			err.err_code = ERR_CHANNEL;
2523713ad546SYazen Ghannam 	}
2524713ad546SYazen Ghannam 
2525713ad546SYazen Ghannam 	err.csrow = m->synd & 0x7;
2526713ad546SYazen Ghannam 
2527713ad546SYazen Ghannam log_error:
2528713ad546SYazen Ghannam 	__log_ecc_error(mci, &err, ecc_type);
2529713ad546SYazen Ghannam }
2530713ad546SYazen Ghannam 
2531713ad546SYazen Ghannam /*
25323f37a36bSBorislav Petkov  * Use pvt->F3 which contains the F3 CPU PCI device to get the related
25333f37a36bSBorislav Petkov  * F1 (AddrMap) and F2 (Dct) devices. Return negative value on error.
2534936fc3afSYazen Ghannam  * Reserve F0 and F6 on systems with a UMC.
25350ec449eeSDoug Thompson  */
2536936fc3afSYazen Ghannam static int
2537936fc3afSYazen Ghannam reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2)
25380ec449eeSDoug Thompson {
2539936fc3afSYazen Ghannam 	if (pvt->umc) {
2540936fc3afSYazen Ghannam 		pvt->F0 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
2541936fc3afSYazen Ghannam 		if (!pvt->F0) {
25425246c540SBorislav Petkov 			amd64_err("F0 not found, device 0x%x (broken BIOS?)\n", pci_id1);
2543936fc3afSYazen Ghannam 			return -ENODEV;
2544936fc3afSYazen Ghannam 		}
2545936fc3afSYazen Ghannam 
2546936fc3afSYazen Ghannam 		pvt->F6 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3);
2547936fc3afSYazen Ghannam 		if (!pvt->F6) {
2548936fc3afSYazen Ghannam 			pci_dev_put(pvt->F0);
2549936fc3afSYazen Ghannam 			pvt->F0 = NULL;
2550936fc3afSYazen Ghannam 
25515246c540SBorislav Petkov 			amd64_err("F6 not found: device 0x%x (broken BIOS?)\n", pci_id2);
2552936fc3afSYazen Ghannam 			return -ENODEV;
2553936fc3afSYazen Ghannam 		}
25545246c540SBorislav Petkov 
2555936fc3afSYazen Ghannam 		edac_dbg(1, "F0: %s\n", pci_name(pvt->F0));
2556936fc3afSYazen Ghannam 		edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
2557936fc3afSYazen Ghannam 		edac_dbg(1, "F6: %s\n", pci_name(pvt->F6));
2558936fc3afSYazen Ghannam 
2559936fc3afSYazen Ghannam 		return 0;
2560936fc3afSYazen Ghannam 	}
2561936fc3afSYazen Ghannam 
25620ec449eeSDoug Thompson 	/* Reserve the ADDRESS MAP Device */
2563936fc3afSYazen Ghannam 	pvt->F1 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
25648d5b5d9cSBorislav Petkov 	if (!pvt->F1) {
25655246c540SBorislav Petkov 		amd64_err("F1 not found: device 0x%x (broken BIOS?)\n", pci_id1);
2566bbd0c1f6SBorislav Petkov 		return -ENODEV;
25670ec449eeSDoug Thompson 	}
25680ec449eeSDoug Thompson 
25693f37a36bSBorislav Petkov 	/* Reserve the DCT Device */
2570936fc3afSYazen Ghannam 	pvt->F2 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3);
25713f37a36bSBorislav Petkov 	if (!pvt->F2) {
25728d5b5d9cSBorislav Petkov 		pci_dev_put(pvt->F1);
25738d5b5d9cSBorislav Petkov 		pvt->F1 = NULL;
25740ec449eeSDoug Thompson 
25755246c540SBorislav Petkov 		amd64_err("F2 not found: device 0x%x (broken BIOS?)\n", pci_id2);
2576bbd0c1f6SBorislav Petkov 		return -ENODEV;
25770ec449eeSDoug Thompson 	}
2578936fc3afSYazen Ghannam 
2579956b9ba1SJoe Perches 	edac_dbg(1, "F1: %s\n", pci_name(pvt->F1));
2580956b9ba1SJoe Perches 	edac_dbg(1, "F2: %s\n", pci_name(pvt->F2));
2581956b9ba1SJoe Perches 	edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
25820ec449eeSDoug Thompson 
25830ec449eeSDoug Thompson 	return 0;
25840ec449eeSDoug Thompson }
25850ec449eeSDoug Thompson 
2586360b7f3cSBorislav Petkov static void free_mc_sibling_devs(struct amd64_pvt *pvt)
25870ec449eeSDoug Thompson {
2588936fc3afSYazen Ghannam 	if (pvt->umc) {
2589936fc3afSYazen Ghannam 		pci_dev_put(pvt->F0);
2590936fc3afSYazen Ghannam 		pci_dev_put(pvt->F6);
2591936fc3afSYazen Ghannam 	} else {
25928d5b5d9cSBorislav Petkov 		pci_dev_put(pvt->F1);
25933f37a36bSBorislav Petkov 		pci_dev_put(pvt->F2);
25940ec449eeSDoug Thompson 	}
2595936fc3afSYazen Ghannam }
25960ec449eeSDoug Thompson 
2597b64ce7cdSYazen Ghannam static void determine_ecc_sym_sz(struct amd64_pvt *pvt)
2598b64ce7cdSYazen Ghannam {
2599b64ce7cdSYazen Ghannam 	pvt->ecc_sym_sz = 4;
2600b64ce7cdSYazen Ghannam 
2601b64ce7cdSYazen Ghannam 	if (pvt->umc) {
2602b64ce7cdSYazen Ghannam 		u8 i;
2603b64ce7cdSYazen Ghannam 
2604b64ce7cdSYazen Ghannam 		for (i = 0; i < NUM_UMCS; i++) {
2605b64ce7cdSYazen Ghannam 			/* Check enabled channels only: */
2606b64ce7cdSYazen Ghannam 			if ((pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) &&
2607b64ce7cdSYazen Ghannam 			    (pvt->umc[i].ecc_ctrl & BIT(7))) {
2608b64ce7cdSYazen Ghannam 				pvt->ecc_sym_sz = 8;
2609b64ce7cdSYazen Ghannam 				break;
2610b64ce7cdSYazen Ghannam 			}
2611b64ce7cdSYazen Ghannam 		}
2612b64ce7cdSYazen Ghannam 
2613b64ce7cdSYazen Ghannam 		return;
2614b64ce7cdSYazen Ghannam 	}
2615b64ce7cdSYazen Ghannam 
2616b64ce7cdSYazen Ghannam 	if (pvt->fam >= 0x10) {
2617b64ce7cdSYazen Ghannam 		u32 tmp;
2618b64ce7cdSYazen Ghannam 
2619b64ce7cdSYazen Ghannam 		amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp);
2620b64ce7cdSYazen Ghannam 		/* F16h has only DCT0, so no need to read dbam1. */
2621b64ce7cdSYazen Ghannam 		if (pvt->fam != 0x16)
2622b64ce7cdSYazen Ghannam 			amd64_read_dct_pci_cfg(pvt, 1, DBAM0, &pvt->dbam1);
2623b64ce7cdSYazen Ghannam 
2624b64ce7cdSYazen Ghannam 		/* F10h, revD and later can do x8 ECC too. */
2625b64ce7cdSYazen Ghannam 		if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25))
2626b64ce7cdSYazen Ghannam 			pvt->ecc_sym_sz = 8;
2627b64ce7cdSYazen Ghannam 	}
2628b64ce7cdSYazen Ghannam }
2629b64ce7cdSYazen Ghannam 
2630b64ce7cdSYazen Ghannam /*
2631b64ce7cdSYazen Ghannam  * Retrieve the hardware registers of the memory controller.
2632b64ce7cdSYazen Ghannam  */
2633b64ce7cdSYazen Ghannam static void __read_mc_regs_df(struct amd64_pvt *pvt)
2634b64ce7cdSYazen Ghannam {
2635b64ce7cdSYazen Ghannam 	u8 nid = pvt->mc_node_id;
2636b64ce7cdSYazen Ghannam 	struct amd64_umc *umc;
2637b64ce7cdSYazen Ghannam 	u32 i, umc_base;
2638b64ce7cdSYazen Ghannam 
2639b64ce7cdSYazen Ghannam 	/* Read registers from each UMC */
2640b64ce7cdSYazen Ghannam 	for (i = 0; i < NUM_UMCS; i++) {
2641b64ce7cdSYazen Ghannam 
2642b64ce7cdSYazen Ghannam 		umc_base = get_umc_base(i);
2643b64ce7cdSYazen Ghannam 		umc = &pvt->umc[i];
2644b64ce7cdSYazen Ghannam 
264507ed82efSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_DIMM_CFG, &umc->dimm_cfg);
264607ed82efSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_UMC_CFG, &umc->umc_cfg);
2647b64ce7cdSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_SDP_CTRL, &umc->sdp_ctrl);
2648b64ce7cdSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_ECC_CTRL, &umc->ecc_ctrl);
264907ed82efSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_UMC_CAP_HI, &umc->umc_cap_hi);
2650b64ce7cdSYazen Ghannam 	}
2651b64ce7cdSYazen Ghannam }
2652b64ce7cdSYazen Ghannam 
26530ec449eeSDoug Thompson /*
26540ec449eeSDoug Thompson  * Retrieve the hardware registers of the memory controller (this includes the
26550ec449eeSDoug Thompson  * 'Address Map' and 'Misc' device regs)
26560ec449eeSDoug Thompson  */
2657360b7f3cSBorislav Petkov static void read_mc_regs(struct amd64_pvt *pvt)
26580ec449eeSDoug Thompson {
2659b64ce7cdSYazen Ghannam 	unsigned int range;
26600ec449eeSDoug Thompson 	u64 msr_val;
26610ec449eeSDoug Thompson 
26620ec449eeSDoug Thompson 	/*
26630ec449eeSDoug Thompson 	 * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
2664b64ce7cdSYazen Ghannam 	 * those are Read-As-Zero.
26650ec449eeSDoug Thompson 	 */
2666e97f8bb8SBorislav Petkov 	rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
2667956b9ba1SJoe Perches 	edac_dbg(0, "  TOP_MEM:  0x%016llx\n", pvt->top_mem);
26680ec449eeSDoug Thompson 
2669b64ce7cdSYazen Ghannam 	/* Check first whether TOP_MEM2 is enabled: */
26700ec449eeSDoug Thompson 	rdmsrl(MSR_K8_SYSCFG, msr_val);
2671b64ce7cdSYazen Ghannam 	if (msr_val & BIT(21)) {
2672e97f8bb8SBorislav Petkov 		rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
2673956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
2674b64ce7cdSYazen Ghannam 	} else {
2675956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2 disabled\n");
2676b64ce7cdSYazen Ghannam 	}
2677b64ce7cdSYazen Ghannam 
2678b64ce7cdSYazen Ghannam 	if (pvt->umc) {
2679b64ce7cdSYazen Ghannam 		__read_mc_regs_df(pvt);
2680b64ce7cdSYazen Ghannam 		amd64_read_pci_cfg(pvt->F0, DF_DHAR, &pvt->dhar);
2681b64ce7cdSYazen Ghannam 
2682b64ce7cdSYazen Ghannam 		goto skip;
2683b64ce7cdSYazen Ghannam 	}
26840ec449eeSDoug Thompson 
26855980bb9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap);
26860ec449eeSDoug Thompson 
26875a5d2371SBorislav Petkov 	read_dram_ctl_register(pvt);
26880ec449eeSDoug Thompson 
26897f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
26907f19bf75SBorislav Petkov 		u8 rw;
26910ec449eeSDoug Thompson 
26927f19bf75SBorislav Petkov 		/* read settings for this DRAM range */
26937f19bf75SBorislav Petkov 		read_dram_base_limit_regs(pvt, range);
2694e97f8bb8SBorislav Petkov 
26957f19bf75SBorislav Petkov 		rw = dram_rw(pvt, range);
26967f19bf75SBorislav Petkov 		if (!rw)
26977f19bf75SBorislav Petkov 			continue;
26987f19bf75SBorislav Petkov 
2699956b9ba1SJoe Perches 		edac_dbg(1, "  DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
27007f19bf75SBorislav Petkov 			 range,
27017f19bf75SBorislav Petkov 			 get_dram_base(pvt, range),
27027f19bf75SBorislav Petkov 			 get_dram_limit(pvt, range));
27037f19bf75SBorislav Petkov 
2704956b9ba1SJoe Perches 		edac_dbg(1, "   IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
27057f19bf75SBorislav Petkov 			 dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
27067f19bf75SBorislav Petkov 			 (rw & 0x1) ? "R" : "-",
27077f19bf75SBorislav Petkov 			 (rw & 0x2) ? "W" : "-",
27087f19bf75SBorislav Petkov 			 dram_intlv_sel(pvt, range),
27097f19bf75SBorislav Petkov 			 dram_dst_node(pvt, range));
27100ec449eeSDoug Thompson 	}
27110ec449eeSDoug Thompson 
2712bc21fa57SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar);
27137981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DBAM0, &pvt->dbam0);
27140ec449eeSDoug Thompson 
27158d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare);
27160ec449eeSDoug Thompson 
27177981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DCLR0, &pvt->dclr0);
27187981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DCHR0, &pvt->dchr0);
27190ec449eeSDoug Thompson 
272078da121eSBorislav Petkov 	if (!dct_ganging_enabled(pvt)) {
27217981a28fSAravind Gopalakrishnan 		amd64_read_dct_pci_cfg(pvt, 1, DCLR0, &pvt->dclr1);
27227981a28fSAravind Gopalakrishnan 		amd64_read_dct_pci_cfg(pvt, 1, DCHR0, &pvt->dchr1);
27230ec449eeSDoug Thompson 	}
2724b2b0c605SBorislav Petkov 
2725b64ce7cdSYazen Ghannam skip:
2726b64ce7cdSYazen Ghannam 	read_dct_base_mask(pvt);
2727b64ce7cdSYazen Ghannam 
2728a597d2a5SAravind Gopalakrishnan 	determine_memory_type(pvt);
2729a597d2a5SAravind Gopalakrishnan 	edac_dbg(1, "  DIMM type: %s\n", edac_mem_types[pvt->dram_type]);
2730a3b7db09SBorislav Petkov 
2731b64ce7cdSYazen Ghannam 	determine_ecc_sym_sz(pvt);
2732a3b7db09SBorislav Petkov 
2733b2b0c605SBorislav Petkov 	dump_misc_regs(pvt);
27340ec449eeSDoug Thompson }
27350ec449eeSDoug Thompson 
27360ec449eeSDoug Thompson /*
27370ec449eeSDoug Thompson  * NOTE: CPU Revision Dependent code
27380ec449eeSDoug Thompson  *
27390ec449eeSDoug Thompson  * Input:
274011c75eadSBorislav Petkov  *	@csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1)
27410ec449eeSDoug Thompson  *	k8 private pointer to -->
27420ec449eeSDoug Thompson  *			DRAM Bank Address mapping register
27430ec449eeSDoug Thompson  *			node_id
27440ec449eeSDoug Thompson  *			DCL register where dual_channel_active is
27450ec449eeSDoug Thompson  *
27460ec449eeSDoug Thompson  * The DBAM register consists of 4 sets of 4 bits each definitions:
27470ec449eeSDoug Thompson  *
27480ec449eeSDoug Thompson  * Bits:	CSROWs
27490ec449eeSDoug Thompson  * 0-3		CSROWs 0 and 1
27500ec449eeSDoug Thompson  * 4-7		CSROWs 2 and 3
27510ec449eeSDoug Thompson  * 8-11		CSROWs 4 and 5
27520ec449eeSDoug Thompson  * 12-15	CSROWs 6 and 7
27530ec449eeSDoug Thompson  *
27540ec449eeSDoug Thompson  * Values range from: 0 to 15
27550ec449eeSDoug Thompson  * The meaning of the values depends on CPU revision and dual-channel state,
27560ec449eeSDoug Thompson  * see relevant BKDG more info.
27570ec449eeSDoug Thompson  *
27580ec449eeSDoug Thompson  * The memory controller provides for total of only 8 CSROWs in its current
27590ec449eeSDoug Thompson  * architecture. Each "pair" of CSROWs normally represents just one DIMM in
27600ec449eeSDoug Thompson  * single channel or two (2) DIMMs in dual channel mode.
27610ec449eeSDoug Thompson  *
27620ec449eeSDoug Thompson  * The following code logic collapses the various tables for CSROW based on CPU
27630ec449eeSDoug Thompson  * revision.
27640ec449eeSDoug Thompson  *
27650ec449eeSDoug Thompson  * Returns:
27660ec449eeSDoug Thompson  *	The number of PAGE_SIZE pages on the specified CSROW number it
27670ec449eeSDoug Thompson  *	encompasses
27680ec449eeSDoug Thompson  *
27690ec449eeSDoug Thompson  */
2770eb77e6b8SYazen Ghannam static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr_orig)
27710ec449eeSDoug Thompson {
2772f92cae45SAshish Shenoy 	u32 dbam = dct ? pvt->dbam1 : pvt->dbam0;
2773eb77e6b8SYazen Ghannam 	int csrow_nr = csrow_nr_orig;
2774eb77e6b8SYazen Ghannam 	u32 cs_mode, nr_pages;
27750ec449eeSDoug Thompson 
2776eb77e6b8SYazen Ghannam 	if (!pvt->umc)
2777eb77e6b8SYazen Ghannam 		csrow_nr >>= 1;
277810de6497SBorislav Petkov 
2779eb77e6b8SYazen Ghannam 	cs_mode = DBAM_DIMM(csrow_nr, dbam);
27800ec449eeSDoug Thompson 
2781eb77e6b8SYazen Ghannam 	nr_pages   = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, csrow_nr);
2782eb77e6b8SYazen Ghannam 	nr_pages <<= 20 - PAGE_SHIFT;
27830ec449eeSDoug Thompson 
278410de6497SBorislav Petkov 	edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n",
2785eb77e6b8SYazen Ghannam 		    csrow_nr_orig, dct,  cs_mode);
278610de6497SBorislav Petkov 	edac_dbg(0, "nr_pages/channel: %u\n", nr_pages);
27870ec449eeSDoug Thompson 
27880ec449eeSDoug Thompson 	return nr_pages;
27890ec449eeSDoug Thompson }
27900ec449eeSDoug Thompson 
27910ec449eeSDoug Thompson /*
27920ec449eeSDoug Thompson  * Initialize the array of csrow attribute instances, based on the values
27930ec449eeSDoug Thompson  * from pci config hardware registers.
27940ec449eeSDoug Thompson  */
2795360b7f3cSBorislav Petkov static int init_csrows(struct mem_ctl_info *mci)
27960ec449eeSDoug Thompson {
279710de6497SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
27982d09d8f3SYazen Ghannam 	enum edac_type edac_mode = EDAC_NONE;
27990ec449eeSDoug Thompson 	struct csrow_info *csrow;
2800de3910ebSMauro Carvalho Chehab 	struct dimm_info *dimm;
280110de6497SBorislav Petkov 	int i, j, empty = 1;
2802a895bf8bSMauro Carvalho Chehab 	int nr_pages = 0;
280310de6497SBorislav Petkov 	u32 val;
28040ec449eeSDoug Thompson 
28052d09d8f3SYazen Ghannam 	if (!pvt->umc) {
2806a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
28070ec449eeSDoug Thompson 
28082299ef71SBorislav Petkov 		pvt->nbcfg = val;
28090ec449eeSDoug Thompson 
2810956b9ba1SJoe Perches 		edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
28112299ef71SBorislav Petkov 			 pvt->mc_node_id, val,
2812a97fa68eSBorislav Petkov 			 !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
28132d09d8f3SYazen Ghannam 	}
28140ec449eeSDoug Thompson 
281510de6497SBorislav Petkov 	/*
281610de6497SBorislav Petkov 	 * We iterate over DCT0 here but we look at DCT1 in parallel, if needed.
281710de6497SBorislav Petkov 	 */
281811c75eadSBorislav Petkov 	for_each_chip_select(i, 0, pvt) {
281910de6497SBorislav Petkov 		bool row_dct0 = !!csrow_enabled(i, 0, pvt);
282010de6497SBorislav Petkov 		bool row_dct1 = false;
28210ec449eeSDoug Thompson 
2822a4b4bedcSBorislav Petkov 		if (pvt->fam != 0xf)
282310de6497SBorislav Petkov 			row_dct1 = !!csrow_enabled(i, 1, pvt);
282410de6497SBorislav Petkov 
282510de6497SBorislav Petkov 		if (!row_dct0 && !row_dct1)
28260ec449eeSDoug Thompson 			continue;
28270ec449eeSDoug Thompson 
282810de6497SBorislav Petkov 		csrow = mci->csrows[i];
28290ec449eeSDoug Thompson 		empty = 0;
283011c75eadSBorislav Petkov 
283110de6497SBorislav Petkov 		edac_dbg(1, "MC node: %d, csrow: %d\n",
283210de6497SBorislav Petkov 			    pvt->mc_node_id, i);
283310de6497SBorislav Petkov 
28341eef1282SMauro Carvalho Chehab 		if (row_dct0) {
2835d1ea71cdSBorislav Petkov 			nr_pages = get_csrow_nr_pages(pvt, 0, i);
28361eef1282SMauro Carvalho Chehab 			csrow->channels[0]->dimm->nr_pages = nr_pages;
28371eef1282SMauro Carvalho Chehab 		}
283810de6497SBorislav Petkov 
283910de6497SBorislav Petkov 		/* K8 has only one DCT */
2840a4b4bedcSBorislav Petkov 		if (pvt->fam != 0xf && row_dct1) {
2841d1ea71cdSBorislav Petkov 			int row_dct1_pages = get_csrow_nr_pages(pvt, 1, i);
28421eef1282SMauro Carvalho Chehab 
28431eef1282SMauro Carvalho Chehab 			csrow->channels[1]->dimm->nr_pages = row_dct1_pages;
28441eef1282SMauro Carvalho Chehab 			nr_pages += row_dct1_pages;
28451eef1282SMauro Carvalho Chehab 		}
28460ec449eeSDoug Thompson 
284710de6497SBorislav Petkov 		edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages);
28480ec449eeSDoug Thompson 
28492d09d8f3SYazen Ghannam 		/* Determine DIMM ECC mode: */
28502d09d8f3SYazen Ghannam 		if (pvt->umc) {
28512d09d8f3SYazen Ghannam 			if (mci->edac_ctl_cap & EDAC_FLAG_S4ECD4ED)
28522d09d8f3SYazen Ghannam 				edac_mode = EDAC_S4ECD4ED;
28532d09d8f3SYazen Ghannam 			else if (mci->edac_ctl_cap & EDAC_FLAG_SECDED)
28542d09d8f3SYazen Ghannam 				edac_mode = EDAC_SECDED;
28552d09d8f3SYazen Ghannam 
28562d09d8f3SYazen Ghannam 		} else if (pvt->nbcfg & NBCFG_ECC_ENABLE) {
28572d09d8f3SYazen Ghannam 			edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL)
28582d09d8f3SYazen Ghannam 					? EDAC_S4ECD4ED
28592d09d8f3SYazen Ghannam 					: EDAC_SECDED;
28602d09d8f3SYazen Ghannam 		}
2861084a4fccSMauro Carvalho Chehab 
2862084a4fccSMauro Carvalho Chehab 		for (j = 0; j < pvt->channel_count; j++) {
2863de3910ebSMauro Carvalho Chehab 			dimm = csrow->channels[j]->dimm;
2864a597d2a5SAravind Gopalakrishnan 			dimm->mtype = pvt->dram_type;
2865de3910ebSMauro Carvalho Chehab 			dimm->edac_mode = edac_mode;
2866084a4fccSMauro Carvalho Chehab 		}
28670ec449eeSDoug Thompson 	}
28680ec449eeSDoug Thompson 
28690ec449eeSDoug Thompson 	return empty;
28700ec449eeSDoug Thompson }
2871d27bf6faSDoug Thompson 
287206724535SBorislav Petkov /* get all cores on this DCT */
28738b84c8dfSDaniel J Blueman static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, u16 nid)
2874f9431992SDoug Thompson {
287506724535SBorislav Petkov 	int cpu;
2876f9431992SDoug Thompson 
287706724535SBorislav Petkov 	for_each_online_cpu(cpu)
287806724535SBorislav Petkov 		if (amd_get_nb_id(cpu) == nid)
287906724535SBorislav Petkov 			cpumask_set_cpu(cpu, mask);
2880f9431992SDoug Thompson }
2881f9431992SDoug Thompson 
2882f9431992SDoug Thompson /* check MCG_CTL on all the cpus on this node */
2883d1ea71cdSBorislav Petkov static bool nb_mce_bank_enabled_on_node(u16 nid)
2884f9431992SDoug Thompson {
2885ba578cb3SRusty Russell 	cpumask_var_t mask;
288650542251SBorislav Petkov 	int cpu, nbe;
288706724535SBorislav Petkov 	bool ret = false;
2888f9431992SDoug Thompson 
2889ba578cb3SRusty Russell 	if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
289024f9a7feSBorislav Petkov 		amd64_warn("%s: Error allocating mask\n", __func__);
289106724535SBorislav Petkov 		return false;
289206724535SBorislav Petkov 	}
289306724535SBorislav Petkov 
2894ba578cb3SRusty Russell 	get_cpus_on_this_dct_cpumask(mask, nid);
289506724535SBorislav Petkov 
2896ba578cb3SRusty Russell 	rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
2897ba578cb3SRusty Russell 
2898ba578cb3SRusty Russell 	for_each_cpu(cpu, mask) {
289950542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
29005980bb9cSBorislav Petkov 		nbe = reg->l & MSR_MCGCTL_NBE;
290106724535SBorislav Petkov 
2902956b9ba1SJoe Perches 		edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
290350542251SBorislav Petkov 			 cpu, reg->q,
290406724535SBorislav Petkov 			 (nbe ? "enabled" : "disabled"));
290506724535SBorislav Petkov 
290606724535SBorislav Petkov 		if (!nbe)
290706724535SBorislav Petkov 			goto out;
290806724535SBorislav Petkov 	}
290906724535SBorislav Petkov 	ret = true;
291006724535SBorislav Petkov 
291106724535SBorislav Petkov out:
2912ba578cb3SRusty Russell 	free_cpumask_var(mask);
2913f9431992SDoug Thompson 	return ret;
2914f9431992SDoug Thompson }
2915f9431992SDoug Thompson 
2916c7e5301aSDaniel J Blueman static int toggle_ecc_err_reporting(struct ecc_settings *s, u16 nid, bool on)
2917f6d6ae96SBorislav Petkov {
2918f6d6ae96SBorislav Petkov 	cpumask_var_t cmask;
291950542251SBorislav Petkov 	int cpu;
2920f6d6ae96SBorislav Petkov 
2921f6d6ae96SBorislav Petkov 	if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
292224f9a7feSBorislav Petkov 		amd64_warn("%s: error allocating mask\n", __func__);
29230de27884SPan Bian 		return -ENOMEM;
2924f6d6ae96SBorislav Petkov 	}
2925f6d6ae96SBorislav Petkov 
2926ae7bb7c6SBorislav Petkov 	get_cpus_on_this_dct_cpumask(cmask, nid);
2927f6d6ae96SBorislav Petkov 
2928f6d6ae96SBorislav Petkov 	rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2929f6d6ae96SBorislav Petkov 
2930f6d6ae96SBorislav Petkov 	for_each_cpu(cpu, cmask) {
2931f6d6ae96SBorislav Petkov 
293250542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
293350542251SBorislav Petkov 
2934f6d6ae96SBorislav Petkov 		if (on) {
29355980bb9cSBorislav Petkov 			if (reg->l & MSR_MCGCTL_NBE)
2936ae7bb7c6SBorislav Petkov 				s->flags.nb_mce_enable = 1;
2937f6d6ae96SBorislav Petkov 
29385980bb9cSBorislav Petkov 			reg->l |= MSR_MCGCTL_NBE;
2939f6d6ae96SBorislav Petkov 		} else {
2940f6d6ae96SBorislav Petkov 			/*
2941d95cf4deSBorislav Petkov 			 * Turn off NB MCE reporting only when it was off before
2942f6d6ae96SBorislav Petkov 			 */
2943ae7bb7c6SBorislav Petkov 			if (!s->flags.nb_mce_enable)
29445980bb9cSBorislav Petkov 				reg->l &= ~MSR_MCGCTL_NBE;
2945f6d6ae96SBorislav Petkov 		}
2946f6d6ae96SBorislav Petkov 	}
2947f6d6ae96SBorislav Petkov 	wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2948f6d6ae96SBorislav Petkov 
2949f6d6ae96SBorislav Petkov 	free_cpumask_var(cmask);
2950f6d6ae96SBorislav Petkov 
2951f6d6ae96SBorislav Petkov 	return 0;
2952f6d6ae96SBorislav Petkov }
2953f6d6ae96SBorislav Petkov 
2954c7e5301aSDaniel J Blueman static bool enable_ecc_error_reporting(struct ecc_settings *s, u16 nid,
29552299ef71SBorislav Petkov 				       struct pci_dev *F3)
2956f6d6ae96SBorislav Petkov {
29572299ef71SBorislav Petkov 	bool ret = true;
2958c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
2959f6d6ae96SBorislav Petkov 
29602299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, ON)) {
29612299ef71SBorislav Petkov 		amd64_warn("Error enabling ECC reporting over MCGCTL!\n");
29622299ef71SBorislav Petkov 		return false;
29632299ef71SBorislav Petkov 	}
29642299ef71SBorislav Petkov 
2965c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
2966f6d6ae96SBorislav Petkov 
2967ae7bb7c6SBorislav Petkov 	s->old_nbctl   = value & mask;
2968ae7bb7c6SBorislav Petkov 	s->nbctl_valid = true;
2969f6d6ae96SBorislav Petkov 
2970f6d6ae96SBorislav Petkov 	value |= mask;
2971c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
2972f6d6ae96SBorislav Petkov 
2973a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCFG, &value);
2974f6d6ae96SBorislav Petkov 
2975956b9ba1SJoe Perches 	edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2976a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
2977f6d6ae96SBorislav Petkov 
2978a97fa68eSBorislav Petkov 	if (!(value & NBCFG_ECC_ENABLE)) {
297924f9a7feSBorislav Petkov 		amd64_warn("DRAM ECC disabled on this node, enabling...\n");
2980f6d6ae96SBorislav Petkov 
2981ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 0;
2982d95cf4deSBorislav Petkov 
2983f6d6ae96SBorislav Petkov 		/* Attempt to turn on DRAM ECC Enable */
2984a97fa68eSBorislav Petkov 		value |= NBCFG_ECC_ENABLE;
2985a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
2986f6d6ae96SBorislav Petkov 
2987a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
2988f6d6ae96SBorislav Petkov 
2989a97fa68eSBorislav Petkov 		if (!(value & NBCFG_ECC_ENABLE)) {
299024f9a7feSBorislav Petkov 			amd64_warn("Hardware rejected DRAM ECC enable,"
299124f9a7feSBorislav Petkov 				   "check memory DIMM configuration.\n");
29922299ef71SBorislav Petkov 			ret = false;
2993f6d6ae96SBorislav Petkov 		} else {
299424f9a7feSBorislav Petkov 			amd64_info("Hardware accepted DRAM ECC Enable\n");
2995f6d6ae96SBorislav Petkov 		}
2996d95cf4deSBorislav Petkov 	} else {
2997ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 1;
2998f6d6ae96SBorislav Petkov 	}
2999d95cf4deSBorislav Petkov 
3000956b9ba1SJoe Perches 	edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
3001a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
3002f6d6ae96SBorislav Petkov 
30032299ef71SBorislav Petkov 	return ret;
3004f6d6ae96SBorislav Petkov }
3005f6d6ae96SBorislav Petkov 
3006c7e5301aSDaniel J Blueman static void restore_ecc_error_reporting(struct ecc_settings *s, u16 nid,
3007360b7f3cSBorislav Petkov 					struct pci_dev *F3)
3008f6d6ae96SBorislav Petkov {
3009c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
3010c9f4f26eSBorislav Petkov 
3011ae7bb7c6SBorislav Petkov 	if (!s->nbctl_valid)
3012f6d6ae96SBorislav Petkov 		return;
3013f6d6ae96SBorislav Petkov 
3014c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
3015f6d6ae96SBorislav Petkov 	value &= ~mask;
3016ae7bb7c6SBorislav Petkov 	value |= s->old_nbctl;
3017f6d6ae96SBorislav Petkov 
3018c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
3019f6d6ae96SBorislav Petkov 
3020ae7bb7c6SBorislav Petkov 	/* restore previous BIOS DRAM ECC "off" setting we force-enabled */
3021ae7bb7c6SBorislav Petkov 	if (!s->flags.nb_ecc_prev) {
3022a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
3023a97fa68eSBorislav Petkov 		value &= ~NBCFG_ECC_ENABLE;
3024a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
3025d95cf4deSBorislav Petkov 	}
3026d95cf4deSBorislav Petkov 
3027d95cf4deSBorislav Petkov 	/* restore the NB Enable MCGCTL bit */
30282299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, OFF))
302924f9a7feSBorislav Petkov 		amd64_warn("Error restoring NB MCGCTL settings!\n");
3030f6d6ae96SBorislav Petkov }
3031f6d6ae96SBorislav Petkov 
3032f9431992SDoug Thompson /*
30332299ef71SBorislav Petkov  * EDAC requires that the BIOS have ECC enabled before
30342299ef71SBorislav Petkov  * taking over the processing of ECC errors. A command line
30352299ef71SBorislav Petkov  * option allows to force-enable hardware ECC later in
30362299ef71SBorislav Petkov  * enable_ecc_error_reporting().
3037f9431992SDoug Thompson  */
3038cab4d277SBorislav Petkov static const char *ecc_msg =
3039cab4d277SBorislav Petkov 	"ECC disabled in the BIOS or no ECC capability, module will not load.\n"
3040cab4d277SBorislav Petkov 	" Either enable ECC checking or force module loading by setting "
3041cab4d277SBorislav Petkov 	"'ecc_enable_override'.\n"
3042cab4d277SBorislav Petkov 	" (Note that use of the override may cause unknown side effects.)\n";
3043be3468e8SBorislav Petkov 
3044c7e5301aSDaniel J Blueman static bool ecc_enabled(struct pci_dev *F3, u16 nid)
3045f9431992SDoug Thompson {
304606724535SBorislav Petkov 	bool nb_mce_en = false;
3047196b79fcSYazen Ghannam 	u8 ecc_en = 0, i;
3048196b79fcSYazen Ghannam 	u32 value;
3049f9431992SDoug Thompson 
3050196b79fcSYazen Ghannam 	if (boot_cpu_data.x86 >= 0x17) {
3051196b79fcSYazen Ghannam 		u8 umc_en_mask = 0, ecc_en_mask = 0;
3052196b79fcSYazen Ghannam 
3053196b79fcSYazen Ghannam 		for (i = 0; i < NUM_UMCS; i++) {
3054196b79fcSYazen Ghannam 			u32 base = get_umc_base(i);
3055196b79fcSYazen Ghannam 
3056196b79fcSYazen Ghannam 			/* Only check enabled UMCs. */
3057196b79fcSYazen Ghannam 			if (amd_smn_read(nid, base + UMCCH_SDP_CTRL, &value))
3058196b79fcSYazen Ghannam 				continue;
3059196b79fcSYazen Ghannam 
3060196b79fcSYazen Ghannam 			if (!(value & UMC_SDP_INIT))
3061196b79fcSYazen Ghannam 				continue;
3062196b79fcSYazen Ghannam 
3063196b79fcSYazen Ghannam 			umc_en_mask |= BIT(i);
3064196b79fcSYazen Ghannam 
3065196b79fcSYazen Ghannam 			if (amd_smn_read(nid, base + UMCCH_UMC_CAP_HI, &value))
3066196b79fcSYazen Ghannam 				continue;
3067196b79fcSYazen Ghannam 
3068196b79fcSYazen Ghannam 			if (value & UMC_ECC_ENABLED)
3069196b79fcSYazen Ghannam 				ecc_en_mask |= BIT(i);
3070196b79fcSYazen Ghannam 		}
3071196b79fcSYazen Ghannam 
3072196b79fcSYazen Ghannam 		/* Check whether at least one UMC is enabled: */
3073196b79fcSYazen Ghannam 		if (umc_en_mask)
3074196b79fcSYazen Ghannam 			ecc_en = umc_en_mask == ecc_en_mask;
307511ab1caeSYazen Ghannam 		else
307611ab1caeSYazen Ghannam 			edac_dbg(0, "Node %d: No enabled UMCs.\n", nid);
3077196b79fcSYazen Ghannam 
3078196b79fcSYazen Ghannam 		/* Assume UMC MCA banks are enabled. */
3079196b79fcSYazen Ghannam 		nb_mce_en = true;
3080196b79fcSYazen Ghannam 	} else {
3081a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
3082f9431992SDoug Thompson 
3083a97fa68eSBorislav Petkov 		ecc_en = !!(value & NBCFG_ECC_ENABLE);
3084be3468e8SBorislav Petkov 
3085d1ea71cdSBorislav Petkov 		nb_mce_en = nb_mce_bank_enabled_on_node(nid);
308606724535SBorislav Petkov 		if (!nb_mce_en)
308711ab1caeSYazen Ghannam 			edac_dbg(0, "NB MCE bank disabled, set MSR 0x%08x[4] on node %d to enable.\n",
30882299ef71SBorislav Petkov 				     MSR_IA32_MCG_CTL, nid);
3089196b79fcSYazen Ghannam 	}
3090196b79fcSYazen Ghannam 
309111ab1caeSYazen Ghannam 	amd64_info("Node %d: DRAM ECC %s.\n",
309211ab1caeSYazen Ghannam 		   nid, (ecc_en ? "enabled" : "disabled"));
3093be3468e8SBorislav Petkov 
30942299ef71SBorislav Petkov 	if (!ecc_en || !nb_mce_en) {
309511ab1caeSYazen Ghannam 		amd64_info("%s", ecc_msg);
30962299ef71SBorislav Petkov 		return false;
3097be3468e8SBorislav Petkov 	}
30982299ef71SBorislav Petkov 	return true;
3099f9431992SDoug Thompson }
3100f9431992SDoug Thompson 
31012d09d8f3SYazen Ghannam static inline void
31022d09d8f3SYazen Ghannam f17h_determine_edac_ctl_cap(struct mem_ctl_info *mci, struct amd64_pvt *pvt)
31032d09d8f3SYazen Ghannam {
31042d09d8f3SYazen Ghannam 	u8 i, ecc_en = 1, cpk_en = 1;
31052d09d8f3SYazen Ghannam 
31062d09d8f3SYazen Ghannam 	for (i = 0; i < NUM_UMCS; i++) {
31072d09d8f3SYazen Ghannam 		if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) {
31082d09d8f3SYazen Ghannam 			ecc_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_ENABLED);
31092d09d8f3SYazen Ghannam 			cpk_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_CHIPKILL_CAP);
31102d09d8f3SYazen Ghannam 		}
31112d09d8f3SYazen Ghannam 	}
31122d09d8f3SYazen Ghannam 
31132d09d8f3SYazen Ghannam 	/* Set chipkill only if ECC is enabled: */
31142d09d8f3SYazen Ghannam 	if (ecc_en) {
31152d09d8f3SYazen Ghannam 		mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
31162d09d8f3SYazen Ghannam 
31172d09d8f3SYazen Ghannam 		if (cpk_en)
31182d09d8f3SYazen Ghannam 			mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
31192d09d8f3SYazen Ghannam 	}
31202d09d8f3SYazen Ghannam }
31212d09d8f3SYazen Ghannam 
3122df71a053SBorislav Petkov static void setup_mci_misc_attrs(struct mem_ctl_info *mci,
3123df71a053SBorislav Petkov 				 struct amd64_family_type *fam)
31247d6034d3SDoug Thompson {
31257d6034d3SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
31267d6034d3SDoug Thompson 
31277d6034d3SDoug Thompson 	mci->mtype_cap		= MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
31287d6034d3SDoug Thompson 	mci->edac_ctl_cap	= EDAC_FLAG_NONE;
31297d6034d3SDoug Thompson 
31302d09d8f3SYazen Ghannam 	if (pvt->umc) {
31312d09d8f3SYazen Ghannam 		f17h_determine_edac_ctl_cap(mci, pvt);
31322d09d8f3SYazen Ghannam 	} else {
31335980bb9cSBorislav Petkov 		if (pvt->nbcap & NBCAP_SECDED)
31347d6034d3SDoug Thompson 			mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
31357d6034d3SDoug Thompson 
31365980bb9cSBorislav Petkov 		if (pvt->nbcap & NBCAP_CHIPKILL)
31377d6034d3SDoug Thompson 			mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
31382d09d8f3SYazen Ghannam 	}
31397d6034d3SDoug Thompson 
3140d1ea71cdSBorislav Petkov 	mci->edac_cap		= determine_edac_cap(pvt);
31417d6034d3SDoug Thompson 	mci->mod_name		= EDAC_MOD_STR;
3142df71a053SBorislav Petkov 	mci->ctl_name		= fam->ctl_name;
3143e7934b70SYazen Ghannam 	mci->dev_name		= pci_name(pvt->F3);
31447d6034d3SDoug Thompson 	mci->ctl_page_to_phys	= NULL;
31457d6034d3SDoug Thompson 
31467d6034d3SDoug Thompson 	/* memory scrubber interface */
3147d1ea71cdSBorislav Petkov 	mci->set_sdram_scrub_rate = set_scrub_rate;
3148d1ea71cdSBorislav Petkov 	mci->get_sdram_scrub_rate = get_scrub_rate;
31497d6034d3SDoug Thompson }
31507d6034d3SDoug Thompson 
31510092b20dSBorislav Petkov /*
31520092b20dSBorislav Petkov  * returns a pointer to the family descriptor on success, NULL otherwise.
31530092b20dSBorislav Petkov  */
3154d1ea71cdSBorislav Petkov static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt)
3155395ae783SBorislav Petkov {
31560092b20dSBorislav Petkov 	struct amd64_family_type *fam_type = NULL;
31570092b20dSBorislav Petkov 
315818b94f66SAravind Gopalakrishnan 	pvt->ext_model  = boot_cpu_data.x86_model >> 4;
3159b399151cSJia Zhang 	pvt->stepping	= boot_cpu_data.x86_stepping;
316018b94f66SAravind Gopalakrishnan 	pvt->model	= boot_cpu_data.x86_model;
316118b94f66SAravind Gopalakrishnan 	pvt->fam	= boot_cpu_data.x86;
316218b94f66SAravind Gopalakrishnan 
316318b94f66SAravind Gopalakrishnan 	switch (pvt->fam) {
3164395ae783SBorislav Petkov 	case 0xf:
3165d1ea71cdSBorislav Petkov 		fam_type	= &family_types[K8_CPUS];
3166d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[K8_CPUS].ops;
3167395ae783SBorislav Petkov 		break;
3168df71a053SBorislav Petkov 
3169395ae783SBorislav Petkov 	case 0x10:
3170d1ea71cdSBorislav Petkov 		fam_type	= &family_types[F10_CPUS];
3171d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[F10_CPUS].ops;
3172df71a053SBorislav Petkov 		break;
3173df71a053SBorislav Petkov 
3174df71a053SBorislav Petkov 	case 0x15:
317518b94f66SAravind Gopalakrishnan 		if (pvt->model == 0x30) {
3176d1ea71cdSBorislav Petkov 			fam_type = &family_types[F15_M30H_CPUS];
3177d1ea71cdSBorislav Petkov 			pvt->ops = &family_types[F15_M30H_CPUS].ops;
317818b94f66SAravind Gopalakrishnan 			break;
3179a597d2a5SAravind Gopalakrishnan 		} else if (pvt->model == 0x60) {
3180a597d2a5SAravind Gopalakrishnan 			fam_type = &family_types[F15_M60H_CPUS];
3181a597d2a5SAravind Gopalakrishnan 			pvt->ops = &family_types[F15_M60H_CPUS].ops;
3182a597d2a5SAravind Gopalakrishnan 			break;
318318b94f66SAravind Gopalakrishnan 		}
318418b94f66SAravind Gopalakrishnan 
3185d1ea71cdSBorislav Petkov 		fam_type	= &family_types[F15_CPUS];
3186d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[F15_CPUS].ops;
3187395ae783SBorislav Petkov 		break;
3188395ae783SBorislav Petkov 
318994c1acf2SAravind Gopalakrishnan 	case 0x16:
319085a8885bSAravind Gopalakrishnan 		if (pvt->model == 0x30) {
319185a8885bSAravind Gopalakrishnan 			fam_type = &family_types[F16_M30H_CPUS];
319285a8885bSAravind Gopalakrishnan 			pvt->ops = &family_types[F16_M30H_CPUS].ops;
319385a8885bSAravind Gopalakrishnan 			break;
319485a8885bSAravind Gopalakrishnan 		}
3195d1ea71cdSBorislav Petkov 		fam_type	= &family_types[F16_CPUS];
3196d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[F16_CPUS].ops;
319794c1acf2SAravind Gopalakrishnan 		break;
319894c1acf2SAravind Gopalakrishnan 
3199f1cbbec9SYazen Ghannam 	case 0x17:
32008960de4aSMichael Jin 		if (pvt->model >= 0x10 && pvt->model <= 0x2f) {
32018960de4aSMichael Jin 			fam_type = &family_types[F17_M10H_CPUS];
32028960de4aSMichael Jin 			pvt->ops = &family_types[F17_M10H_CPUS].ops;
32038960de4aSMichael Jin 			break;
32048960de4aSMichael Jin 		}
3205f1cbbec9SYazen Ghannam 		fam_type	= &family_types[F17_CPUS];
3206f1cbbec9SYazen Ghannam 		pvt->ops	= &family_types[F17_CPUS].ops;
3207f1cbbec9SYazen Ghannam 		break;
3208f1cbbec9SYazen Ghannam 
3209395ae783SBorislav Petkov 	default:
321024f9a7feSBorislav Petkov 		amd64_err("Unsupported family!\n");
32110092b20dSBorislav Petkov 		return NULL;
3212395ae783SBorislav Petkov 	}
32130092b20dSBorislav Petkov 
3214df71a053SBorislav Petkov 	amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name,
321518b94f66SAravind Gopalakrishnan 		     (pvt->fam == 0xf ?
32160092b20dSBorislav Petkov 				(pvt->ext_model >= K8_REV_F  ? "revF or later "
32170092b20dSBorislav Petkov 							     : "revE or earlier ")
321824f9a7feSBorislav Petkov 				 : ""), pvt->mc_node_id);
32190092b20dSBorislav Petkov 	return fam_type;
3220395ae783SBorislav Petkov }
3221395ae783SBorislav Petkov 
3222e339f1ecSTakashi Iwai static const struct attribute_group *amd64_edac_attr_groups[] = {
3223e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_DEBUG
3224e339f1ecSTakashi Iwai 	&amd64_edac_dbg_group,
3225e339f1ecSTakashi Iwai #endif
3226e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION
3227e339f1ecSTakashi Iwai 	&amd64_edac_inj_group,
3228e339f1ecSTakashi Iwai #endif
3229e339f1ecSTakashi Iwai 	NULL
3230e339f1ecSTakashi Iwai };
3231e339f1ecSTakashi Iwai 
32323f37a36bSBorislav Petkov static int init_one_instance(unsigned int nid)
32337d6034d3SDoug Thompson {
32343f37a36bSBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
32350092b20dSBorislav Petkov 	struct amd64_family_type *fam_type = NULL;
3236360b7f3cSBorislav Petkov 	struct mem_ctl_info *mci = NULL;
3237ab5a503cSMauro Carvalho Chehab 	struct edac_mc_layer layers[2];
32383f37a36bSBorislav Petkov 	struct amd64_pvt *pvt = NULL;
3239936fc3afSYazen Ghannam 	u16 pci_id1, pci_id2;
32407d6034d3SDoug Thompson 	int err = 0, ret;
32417d6034d3SDoug Thompson 
32427d6034d3SDoug Thompson 	ret = -ENOMEM;
32437d6034d3SDoug Thompson 	pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
32447d6034d3SDoug Thompson 	if (!pvt)
3245360b7f3cSBorislav Petkov 		goto err_ret;
32467d6034d3SDoug Thompson 
3247360b7f3cSBorislav Petkov 	pvt->mc_node_id	= nid;
32483f37a36bSBorislav Petkov 	pvt->F3 = F3;
32497d6034d3SDoug Thompson 
3250395ae783SBorislav Petkov 	ret = -EINVAL;
3251d1ea71cdSBorislav Petkov 	fam_type = per_family_init(pvt);
32520092b20dSBorislav Petkov 	if (!fam_type)
3253395ae783SBorislav Petkov 		goto err_free;
3254395ae783SBorislav Petkov 
3255936fc3afSYazen Ghannam 	if (pvt->fam >= 0x17) {
3256936fc3afSYazen Ghannam 		pvt->umc = kcalloc(NUM_UMCS, sizeof(struct amd64_umc), GFP_KERNEL);
3257936fc3afSYazen Ghannam 		if (!pvt->umc) {
3258936fc3afSYazen Ghannam 			ret = -ENOMEM;
32597d6034d3SDoug Thompson 			goto err_free;
3260936fc3afSYazen Ghannam 		}
3261936fc3afSYazen Ghannam 
3262936fc3afSYazen Ghannam 		pci_id1 = fam_type->f0_id;
3263936fc3afSYazen Ghannam 		pci_id2 = fam_type->f6_id;
3264936fc3afSYazen Ghannam 	} else {
3265936fc3afSYazen Ghannam 		pci_id1 = fam_type->f1_id;
3266936fc3afSYazen Ghannam 		pci_id2 = fam_type->f2_id;
3267936fc3afSYazen Ghannam 	}
3268936fc3afSYazen Ghannam 
3269936fc3afSYazen Ghannam 	err = reserve_mc_sibling_devs(pvt, pci_id1, pci_id2);
3270936fc3afSYazen Ghannam 	if (err)
3271936fc3afSYazen Ghannam 		goto err_post_init;
32727d6034d3SDoug Thompson 
3273360b7f3cSBorislav Petkov 	read_mc_regs(pvt);
32747d6034d3SDoug Thompson 
32757d6034d3SDoug Thompson 	/*
32767d6034d3SDoug Thompson 	 * We need to determine how many memory channels there are. Then use
32777d6034d3SDoug Thompson 	 * that information for calculating the size of the dynamic instance
3278360b7f3cSBorislav Petkov 	 * tables in the 'mci' structure.
32797d6034d3SDoug Thompson 	 */
3280360b7f3cSBorislav Petkov 	ret = -EINVAL;
32817d6034d3SDoug Thompson 	pvt->channel_count = pvt->ops->early_channel_count(pvt);
32827d6034d3SDoug Thompson 	if (pvt->channel_count < 0)
3283360b7f3cSBorislav Petkov 		goto err_siblings;
32847d6034d3SDoug Thompson 
32857d6034d3SDoug Thompson 	ret = -ENOMEM;
3286ab5a503cSMauro Carvalho Chehab 	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
3287ab5a503cSMauro Carvalho Chehab 	layers[0].size = pvt->csels[0].b_cnt;
3288ab5a503cSMauro Carvalho Chehab 	layers[0].is_virt_csrow = true;
3289ab5a503cSMauro Carvalho Chehab 	layers[1].type = EDAC_MC_LAYER_CHANNEL;
3290f0a56c48SBorislav Petkov 
3291f0a56c48SBorislav Petkov 	/*
3292f0a56c48SBorislav Petkov 	 * Always allocate two channels since we can have setups with DIMMs on
3293f0a56c48SBorislav Petkov 	 * only one channel. Also, this simplifies handling later for the price
3294f0a56c48SBorislav Petkov 	 * of a couple of KBs tops.
3295f0a56c48SBorislav Petkov 	 */
3296f0a56c48SBorislav Petkov 	layers[1].size = 2;
3297ab5a503cSMauro Carvalho Chehab 	layers[1].is_virt_csrow = false;
3298f0a56c48SBorislav Petkov 
3299ca0907b9SMauro Carvalho Chehab 	mci = edac_mc_alloc(nid, ARRAY_SIZE(layers), layers, 0);
33007d6034d3SDoug Thompson 	if (!mci)
3301360b7f3cSBorislav Petkov 		goto err_siblings;
33027d6034d3SDoug Thompson 
33037d6034d3SDoug Thompson 	mci->pvt_info = pvt;
33043f37a36bSBorislav Petkov 	mci->pdev = &pvt->F3->dev;
33057d6034d3SDoug Thompson 
3306df71a053SBorislav Petkov 	setup_mci_misc_attrs(mci, fam_type);
3307360b7f3cSBorislav Petkov 
3308360b7f3cSBorislav Petkov 	if (init_csrows(mci))
33097d6034d3SDoug Thompson 		mci->edac_cap = EDAC_FLAG_NONE;
33107d6034d3SDoug Thompson 
33117d6034d3SDoug Thompson 	ret = -ENODEV;
3312e339f1ecSTakashi Iwai 	if (edac_mc_add_mc_with_groups(mci, amd64_edac_attr_groups)) {
3313956b9ba1SJoe Perches 		edac_dbg(1, "failed edac_mc_add_mc()\n");
33147d6034d3SDoug Thompson 		goto err_add_mc;
33157d6034d3SDoug Thompson 	}
33167d6034d3SDoug Thompson 
33177d6034d3SDoug Thompson 	return 0;
33187d6034d3SDoug Thompson 
33197d6034d3SDoug Thompson err_add_mc:
33207d6034d3SDoug Thompson 	edac_mc_free(mci);
33217d6034d3SDoug Thompson 
3322360b7f3cSBorislav Petkov err_siblings:
3323360b7f3cSBorislav Petkov 	free_mc_sibling_devs(pvt);
33247d6034d3SDoug Thompson 
3325936fc3afSYazen Ghannam err_post_init:
3326936fc3afSYazen Ghannam 	if (pvt->fam >= 0x17)
3327936fc3afSYazen Ghannam 		kfree(pvt->umc);
3328936fc3afSYazen Ghannam 
3329360b7f3cSBorislav Petkov err_free:
3330360b7f3cSBorislav Petkov 	kfree(pvt);
33317d6034d3SDoug Thompson 
3332360b7f3cSBorislav Petkov err_ret:
33337d6034d3SDoug Thompson 	return ret;
33347d6034d3SDoug Thompson }
33357d6034d3SDoug Thompson 
33363f37a36bSBorislav Petkov static int probe_one_instance(unsigned int nid)
33377d6034d3SDoug Thompson {
33382299ef71SBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
3339ae7bb7c6SBorislav Petkov 	struct ecc_settings *s;
33403f37a36bSBorislav Petkov 	int ret;
3341b8cfa02fSBorislav Petkov 
3342ae7bb7c6SBorislav Petkov 	ret = -ENOMEM;
3343ae7bb7c6SBorislav Petkov 	s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL);
3344ae7bb7c6SBorislav Petkov 	if (!s)
33452299ef71SBorislav Petkov 		goto err_out;
3346ae7bb7c6SBorislav Petkov 
3347ae7bb7c6SBorislav Petkov 	ecc_stngs[nid] = s;
3348ae7bb7c6SBorislav Petkov 
33492299ef71SBorislav Petkov 	if (!ecc_enabled(F3, nid)) {
33504688c9b4SYazen Ghannam 		ret = 0;
33512299ef71SBorislav Petkov 
33522299ef71SBorislav Petkov 		if (!ecc_enable_override)
33532299ef71SBorislav Petkov 			goto err_enable;
33542299ef71SBorislav Petkov 
3355044e7a41SYazen Ghannam 		if (boot_cpu_data.x86 >= 0x17) {
3356044e7a41SYazen Ghannam 			amd64_warn("Forcing ECC on is not recommended on newer systems. Please enable ECC in BIOS.");
3357044e7a41SYazen Ghannam 			goto err_enable;
3358044e7a41SYazen Ghannam 		} else
33592299ef71SBorislav Petkov 			amd64_warn("Forcing ECC on!\n");
33602299ef71SBorislav Petkov 
33612299ef71SBorislav Petkov 		if (!enable_ecc_error_reporting(s, nid, F3))
33622299ef71SBorislav Petkov 			goto err_enable;
33632299ef71SBorislav Petkov 	}
33642299ef71SBorislav Petkov 
33653f37a36bSBorislav Petkov 	ret = init_one_instance(nid);
3366360b7f3cSBorislav Petkov 	if (ret < 0) {
3367ae7bb7c6SBorislav Petkov 		amd64_err("Error probing instance: %d\n", nid);
3368044e7a41SYazen Ghannam 
3369044e7a41SYazen Ghannam 		if (boot_cpu_data.x86 < 0x17)
3370360b7f3cSBorislav Petkov 			restore_ecc_error_reporting(s, nid, F3);
33712b9b2c46SYazen Ghannam 
33722b9b2c46SYazen Ghannam 		goto err_enable;
3373360b7f3cSBorislav Petkov 	}
33747d6034d3SDoug Thompson 
33757d6034d3SDoug Thompson 	return ret;
33762299ef71SBorislav Petkov 
33772299ef71SBorislav Petkov err_enable:
33782299ef71SBorislav Petkov 	kfree(s);
33792299ef71SBorislav Petkov 	ecc_stngs[nid] = NULL;
33802299ef71SBorislav Petkov 
33812299ef71SBorislav Petkov err_out:
33822299ef71SBorislav Petkov 	return ret;
33837d6034d3SDoug Thompson }
33847d6034d3SDoug Thompson 
33853f37a36bSBorislav Petkov static void remove_one_instance(unsigned int nid)
33867d6034d3SDoug Thompson {
3387360b7f3cSBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
3388360b7f3cSBorislav Petkov 	struct ecc_settings *s = ecc_stngs[nid];
33893f37a36bSBorislav Petkov 	struct mem_ctl_info *mci;
33903f37a36bSBorislav Petkov 	struct amd64_pvt *pvt;
33917d6034d3SDoug Thompson 
33923f37a36bSBorislav Petkov 	mci = find_mci_by_dev(&F3->dev);
3393a4b4bedcSBorislav Petkov 	WARN_ON(!mci);
3394a4b4bedcSBorislav Petkov 
33957d6034d3SDoug Thompson 	/* Remove from EDAC CORE tracking list */
33963f37a36bSBorislav Petkov 	mci = edac_mc_del_mc(&F3->dev);
33977d6034d3SDoug Thompson 	if (!mci)
33987d6034d3SDoug Thompson 		return;
33997d6034d3SDoug Thompson 
34007d6034d3SDoug Thompson 	pvt = mci->pvt_info;
34017d6034d3SDoug Thompson 
3402360b7f3cSBorislav Petkov 	restore_ecc_error_reporting(s, nid, F3);
34037d6034d3SDoug Thompson 
3404360b7f3cSBorislav Petkov 	free_mc_sibling_devs(pvt);
34057d6034d3SDoug Thompson 
3406360b7f3cSBorislav Petkov 	kfree(ecc_stngs[nid]);
3407360b7f3cSBorislav Petkov 	ecc_stngs[nid] = NULL;
3408ae7bb7c6SBorislav Petkov 
34097d6034d3SDoug Thompson 	/* Free the EDAC CORE resources */
34108f68ed97SBorislav Petkov 	mci->pvt_info = NULL;
34118f68ed97SBorislav Petkov 
34128f68ed97SBorislav Petkov 	kfree(pvt);
34137d6034d3SDoug Thompson 	edac_mc_free(mci);
34147d6034d3SDoug Thompson }
34157d6034d3SDoug Thompson 
3416360b7f3cSBorislav Petkov static void setup_pci_device(void)
34177d6034d3SDoug Thompson {
34187d6034d3SDoug Thompson 	struct mem_ctl_info *mci;
34197d6034d3SDoug Thompson 	struct amd64_pvt *pvt;
34207d6034d3SDoug Thompson 
3421d1ea71cdSBorislav Petkov 	if (pci_ctl)
34227d6034d3SDoug Thompson 		return;
34237d6034d3SDoug Thompson 
34242ec591acSBorislav Petkov 	mci = edac_mc_find(0);
3425d1ea71cdSBorislav Petkov 	if (!mci)
3426d1ea71cdSBorislav Petkov 		return;
34277d6034d3SDoug Thompson 
34287d6034d3SDoug Thompson 	pvt = mci->pvt_info;
3429936fc3afSYazen Ghannam 	if (pvt->umc)
3430936fc3afSYazen Ghannam 		pci_ctl = edac_pci_create_generic_ctl(&pvt->F0->dev, EDAC_MOD_STR);
3431936fc3afSYazen Ghannam 	else
3432d1ea71cdSBorislav Petkov 		pci_ctl = edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR);
3433d1ea71cdSBorislav Petkov 	if (!pci_ctl) {
3434d1ea71cdSBorislav Petkov 		pr_warn("%s(): Unable to create PCI control\n", __func__);
3435d1ea71cdSBorislav Petkov 		pr_warn("%s(): PCI error report via EDAC not set\n", __func__);
34367d6034d3SDoug Thompson 	}
34377d6034d3SDoug Thompson }
34387d6034d3SDoug Thompson 
3439d6efab74SYazen Ghannam static const struct x86_cpu_id amd64_cpuids[] = {
3440d6efab74SYazen Ghannam 	{ X86_VENDOR_AMD, 0xF,	X86_MODEL_ANY,	X86_FEATURE_ANY, 0 },
3441d6efab74SYazen Ghannam 	{ X86_VENDOR_AMD, 0x10, X86_MODEL_ANY,	X86_FEATURE_ANY, 0 },
3442d6efab74SYazen Ghannam 	{ X86_VENDOR_AMD, 0x15, X86_MODEL_ANY,	X86_FEATURE_ANY, 0 },
3443d6efab74SYazen Ghannam 	{ X86_VENDOR_AMD, 0x16, X86_MODEL_ANY,	X86_FEATURE_ANY, 0 },
344495d3af6bSYazen Ghannam 	{ X86_VENDOR_AMD, 0x17, X86_MODEL_ANY,	X86_FEATURE_ANY, 0 },
3445d6efab74SYazen Ghannam 	{ }
3446d6efab74SYazen Ghannam };
3447d6efab74SYazen Ghannam MODULE_DEVICE_TABLE(x86cpu, amd64_cpuids);
3448d6efab74SYazen Ghannam 
34497d6034d3SDoug Thompson static int __init amd64_edac_init(void)
34507d6034d3SDoug Thompson {
3451301375e7SToshi Kani 	const char *owner;
3452360b7f3cSBorislav Petkov 	int err = -ENODEV;
34533f37a36bSBorislav Petkov 	int i;
34547d6034d3SDoug Thompson 
3455301375e7SToshi Kani 	owner = edac_get_owner();
3456301375e7SToshi Kani 	if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
3457301375e7SToshi Kani 		return -EBUSY;
3458301375e7SToshi Kani 
34591bd9900bSYazen Ghannam 	if (!x86_match_cpu(amd64_cpuids))
34601bd9900bSYazen Ghannam 		return -ENODEV;
34611bd9900bSYazen Ghannam 
34629653a5c7SHans Rosenfeld 	if (amd_cache_northbridges() < 0)
34631bd9900bSYazen Ghannam 		return -ENODEV;
34647d6034d3SDoug Thompson 
34656ba92feaSBorislav Petkov 	opstate_init();
34666ba92feaSBorislav Petkov 
3467cc4d8860SBorislav Petkov 	err = -ENOMEM;
34686396bb22SKees Cook 	ecc_stngs = kcalloc(amd_nb_num(), sizeof(ecc_stngs[0]), GFP_KERNEL);
34692ec591acSBorislav Petkov 	if (!ecc_stngs)
3470a9f0fbe2SBorislav Petkov 		goto err_free;
3471cc4d8860SBorislav Petkov 
347250542251SBorislav Petkov 	msrs = msrs_alloc();
347356b34b91SBorislav Petkov 	if (!msrs)
3474360b7f3cSBorislav Petkov 		goto err_free;
347550542251SBorislav Petkov 
34762287c636SYazen Ghannam 	for (i = 0; i < amd_nb_num(); i++) {
34772287c636SYazen Ghannam 		err = probe_one_instance(i);
34782287c636SYazen Ghannam 		if (err) {
34793f37a36bSBorislav Petkov 			/* unwind properly */
34803f37a36bSBorislav Petkov 			while (--i >= 0)
34813f37a36bSBorislav Petkov 				remove_one_instance(i);
34827d6034d3SDoug Thompson 
34833f37a36bSBorislav Petkov 			goto err_pci;
34843f37a36bSBorislav Petkov 		}
34852287c636SYazen Ghannam 	}
34867d6034d3SDoug Thompson 
34874688c9b4SYazen Ghannam 	if (!edac_has_mcs()) {
34884688c9b4SYazen Ghannam 		err = -ENODEV;
34894688c9b4SYazen Ghannam 		goto err_pci;
34904688c9b4SYazen Ghannam 	}
34914688c9b4SYazen Ghannam 
3492234365f5SYazen Ghannam 	/* register stuff with EDAC MCE */
3493234365f5SYazen Ghannam 	if (report_gart_errors)
3494234365f5SYazen Ghannam 		amd_report_gart_errors(true);
3495234365f5SYazen Ghannam 
3496234365f5SYazen Ghannam 	if (boot_cpu_data.x86 >= 0x17)
3497234365f5SYazen Ghannam 		amd_register_ecc_decoder(decode_umc_error);
3498234365f5SYazen Ghannam 	else
3499234365f5SYazen Ghannam 		amd_register_ecc_decoder(decode_bus_error);
3500234365f5SYazen Ghannam 
3501360b7f3cSBorislav Petkov 	setup_pci_device();
3502f5b10c45STomasz Pala 
3503f5b10c45STomasz Pala #ifdef CONFIG_X86_32
3504f5b10c45STomasz Pala 	amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR);
3505f5b10c45STomasz Pala #endif
3506f5b10c45STomasz Pala 
3507de0336b3SBorislav Petkov 	printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION);
3508de0336b3SBorislav Petkov 
35097d6034d3SDoug Thompson 	return 0;
35107d6034d3SDoug Thompson 
351156b34b91SBorislav Petkov err_pci:
351256b34b91SBorislav Petkov 	msrs_free(msrs);
351356b34b91SBorislav Petkov 	msrs = NULL;
3514cc4d8860SBorislav Petkov 
3515360b7f3cSBorislav Petkov err_free:
3516360b7f3cSBorislav Petkov 	kfree(ecc_stngs);
3517360b7f3cSBorislav Petkov 	ecc_stngs = NULL;
3518360b7f3cSBorislav Petkov 
35197d6034d3SDoug Thompson 	return err;
35207d6034d3SDoug Thompson }
35217d6034d3SDoug Thompson 
35227d6034d3SDoug Thompson static void __exit amd64_edac_exit(void)
35237d6034d3SDoug Thompson {
35243f37a36bSBorislav Petkov 	int i;
35253f37a36bSBorislav Petkov 
3526d1ea71cdSBorislav Petkov 	if (pci_ctl)
3527d1ea71cdSBorislav Petkov 		edac_pci_release_generic_ctl(pci_ctl);
35287d6034d3SDoug Thompson 
3529234365f5SYazen Ghannam 	/* unregister from EDAC MCE */
3530234365f5SYazen Ghannam 	amd_report_gart_errors(false);
3531234365f5SYazen Ghannam 
3532234365f5SYazen Ghannam 	if (boot_cpu_data.x86 >= 0x17)
3533234365f5SYazen Ghannam 		amd_unregister_ecc_decoder(decode_umc_error);
3534234365f5SYazen Ghannam 	else
3535234365f5SYazen Ghannam 		amd_unregister_ecc_decoder(decode_bus_error);
3536234365f5SYazen Ghannam 
35373f37a36bSBorislav Petkov 	for (i = 0; i < amd_nb_num(); i++)
35383f37a36bSBorislav Petkov 		remove_one_instance(i);
353950542251SBorislav Petkov 
3540ae7bb7c6SBorislav Petkov 	kfree(ecc_stngs);
3541ae7bb7c6SBorislav Petkov 	ecc_stngs = NULL;
3542ae7bb7c6SBorislav Petkov 
354350542251SBorislav Petkov 	msrs_free(msrs);
354450542251SBorislav Petkov 	msrs = NULL;
35457d6034d3SDoug Thompson }
35467d6034d3SDoug Thompson 
35477d6034d3SDoug Thompson module_init(amd64_edac_init);
35487d6034d3SDoug Thompson module_exit(amd64_edac_exit);
35497d6034d3SDoug Thompson 
35507d6034d3SDoug Thompson MODULE_LICENSE("GPL");
35517d6034d3SDoug Thompson MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, "
35527d6034d3SDoug Thompson 		"Dave Peterson, Thayne Harbaugh");
35537d6034d3SDoug Thompson MODULE_DESCRIPTION("MC support for AMD64 memory controllers - "
35547d6034d3SDoug Thompson 		EDAC_AMD64_VERSION);
35557d6034d3SDoug Thompson 
35567d6034d3SDoug Thompson module_param(edac_op_state, int, 0444);
35577d6034d3SDoug Thompson MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
3558