xref: /openbmc/linux/drivers/edac/amd64_edac.c (revision 2151c84e)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22bc65418SDoug Thompson #include "amd64_edac.h"
323ac4ae8SAndreas Herrmann #include <asm/amd_nb.h>
42bc65418SDoug Thompson 
5d1ea71cdSBorislav Petkov static struct edac_pci_ctl_info *pci_ctl;
62bc65418SDoug Thompson 
72bc65418SDoug Thompson /*
82bc65418SDoug Thompson  * Set by command line parameter. If BIOS has enabled the ECC, this override is
92bc65418SDoug Thompson  * cleared to prevent re-enabling the hardware by this driver.
102bc65418SDoug Thompson  */
112bc65418SDoug Thompson static int ecc_enable_override;
122bc65418SDoug Thompson module_param(ecc_enable_override, int, 0644);
132bc65418SDoug Thompson 
14a29d8b8eSTejun Heo static struct msr __percpu *msrs;
1550542251SBorislav Petkov 
1638ddd4d1SYazen Ghannam static struct amd64_family_type *fam_type;
1738ddd4d1SYazen Ghannam 
18*2151c84eSYazen Ghannam static inline u32 get_umc_reg(u32 reg)
19*2151c84eSYazen Ghannam {
20*2151c84eSYazen Ghannam 	if (!fam_type->flags.zn_regs_v2)
21*2151c84eSYazen Ghannam 		return reg;
22*2151c84eSYazen Ghannam 
23*2151c84eSYazen Ghannam 	switch (reg) {
24*2151c84eSYazen Ghannam 	case UMCCH_ADDR_CFG:		return UMCCH_ADDR_CFG_DDR5;
25*2151c84eSYazen Ghannam 	case UMCCH_ADDR_MASK_SEC:	return UMCCH_ADDR_MASK_SEC_DDR5;
26*2151c84eSYazen Ghannam 	case UMCCH_DIMM_CFG:		return UMCCH_DIMM_CFG_DDR5;
27*2151c84eSYazen Ghannam 	}
28*2151c84eSYazen Ghannam 
29*2151c84eSYazen Ghannam 	WARN_ONCE(1, "%s: unknown register 0x%x", __func__, reg);
30*2151c84eSYazen Ghannam 	return 0;
31*2151c84eSYazen Ghannam }
32*2151c84eSYazen Ghannam 
332ec591acSBorislav Petkov /* Per-node stuff */
34ae7bb7c6SBorislav Petkov static struct ecc_settings **ecc_stngs;
352bc65418SDoug Thompson 
36706657b1SBorislav Petkov /* Device for the PCI component */
37706657b1SBorislav Petkov static struct device *pci_ctl_dev;
38706657b1SBorislav Petkov 
392bc65418SDoug Thompson /*
40b70ef010SBorislav Petkov  * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing
41b70ef010SBorislav Petkov  * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching-
42b70ef010SBorislav Petkov  * or higher value'.
43b70ef010SBorislav Petkov  *
44b70ef010SBorislav Petkov  *FIXME: Produce a better mapping/linearisation.
45b70ef010SBorislav Petkov  */
46c7e5301aSDaniel J Blueman static const struct scrubrate {
4739094443SBorislav Petkov        u32 scrubval;           /* bit pattern for scrub rate */
4839094443SBorislav Petkov        u32 bandwidth;          /* bandwidth consumed (bytes/sec) */
4939094443SBorislav Petkov } scrubrates[] = {
50b70ef010SBorislav Petkov 	{ 0x01, 1600000000UL},
51b70ef010SBorislav Petkov 	{ 0x02, 800000000UL},
52b70ef010SBorislav Petkov 	{ 0x03, 400000000UL},
53b70ef010SBorislav Petkov 	{ 0x04, 200000000UL},
54b70ef010SBorislav Petkov 	{ 0x05, 100000000UL},
55b70ef010SBorislav Petkov 	{ 0x06, 50000000UL},
56b70ef010SBorislav Petkov 	{ 0x07, 25000000UL},
57b70ef010SBorislav Petkov 	{ 0x08, 12284069UL},
58b70ef010SBorislav Petkov 	{ 0x09, 6274509UL},
59b70ef010SBorislav Petkov 	{ 0x0A, 3121951UL},
60b70ef010SBorislav Petkov 	{ 0x0B, 1560975UL},
61b70ef010SBorislav Petkov 	{ 0x0C, 781440UL},
62b70ef010SBorislav Petkov 	{ 0x0D, 390720UL},
63b70ef010SBorislav Petkov 	{ 0x0E, 195300UL},
64b70ef010SBorislav Petkov 	{ 0x0F, 97650UL},
65b70ef010SBorislav Petkov 	{ 0x10, 48854UL},
66b70ef010SBorislav Petkov 	{ 0x11, 24427UL},
67b70ef010SBorislav Petkov 	{ 0x12, 12213UL},
68b70ef010SBorislav Petkov 	{ 0x13, 6101UL},
69b70ef010SBorislav Petkov 	{ 0x14, 3051UL},
70b70ef010SBorislav Petkov 	{ 0x15, 1523UL},
71b70ef010SBorislav Petkov 	{ 0x16, 761UL},
72b70ef010SBorislav Petkov 	{ 0x00, 0UL},        /* scrubbing off */
73b70ef010SBorislav Petkov };
74b70ef010SBorislav Petkov 
7566fed2d4SBorislav Petkov int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset,
76b2b0c605SBorislav Petkov 			       u32 *val, const char *func)
77b2b0c605SBorislav Petkov {
78b2b0c605SBorislav Petkov 	int err = 0;
79b2b0c605SBorislav Petkov 
80b2b0c605SBorislav Petkov 	err = pci_read_config_dword(pdev, offset, val);
81b2b0c605SBorislav Petkov 	if (err)
82b2b0c605SBorislav Petkov 		amd64_warn("%s: error reading F%dx%03x.\n",
83b2b0c605SBorislav Petkov 			   func, PCI_FUNC(pdev->devfn), offset);
84b2b0c605SBorislav Petkov 
85b2b0c605SBorislav Petkov 	return err;
86b2b0c605SBorislav Petkov }
87b2b0c605SBorislav Petkov 
88b2b0c605SBorislav Petkov int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset,
89b2b0c605SBorislav Petkov 				u32 val, const char *func)
90b2b0c605SBorislav Petkov {
91b2b0c605SBorislav Petkov 	int err = 0;
92b2b0c605SBorislav Petkov 
93b2b0c605SBorislav Petkov 	err = pci_write_config_dword(pdev, offset, val);
94b2b0c605SBorislav Petkov 	if (err)
95b2b0c605SBorislav Petkov 		amd64_warn("%s: error writing to F%dx%03x.\n",
96b2b0c605SBorislav Petkov 			   func, PCI_FUNC(pdev->devfn), offset);
97b2b0c605SBorislav Petkov 
98b2b0c605SBorislav Petkov 	return err;
99b2b0c605SBorislav Petkov }
100b2b0c605SBorislav Petkov 
101b2b0c605SBorislav Petkov /*
10273ba8593SBorislav Petkov  * Select DCT to which PCI cfg accesses are routed
10373ba8593SBorislav Petkov  */
10473ba8593SBorislav Petkov static void f15h_select_dct(struct amd64_pvt *pvt, u8 dct)
10573ba8593SBorislav Petkov {
10673ba8593SBorislav Petkov 	u32 reg = 0;
10773ba8593SBorislav Petkov 
10873ba8593SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, &reg);
1097981a28fSAravind Gopalakrishnan 	reg &= (pvt->model == 0x30) ? ~3 : ~1;
11073ba8593SBorislav Petkov 	reg |= dct;
11173ba8593SBorislav Petkov 	amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg);
11273ba8593SBorislav Petkov }
11373ba8593SBorislav Petkov 
1147981a28fSAravind Gopalakrishnan /*
1157981a28fSAravind Gopalakrishnan  *
1167981a28fSAravind Gopalakrishnan  * Depending on the family, F2 DCT reads need special handling:
1177981a28fSAravind Gopalakrishnan  *
1187981a28fSAravind Gopalakrishnan  * K8: has a single DCT only and no address offsets >= 0x100
1197981a28fSAravind Gopalakrishnan  *
1207981a28fSAravind Gopalakrishnan  * F10h: each DCT has its own set of regs
1217981a28fSAravind Gopalakrishnan  *	DCT0 -> F2x040..
1227981a28fSAravind Gopalakrishnan  *	DCT1 -> F2x140..
1237981a28fSAravind Gopalakrishnan  *
1247981a28fSAravind Gopalakrishnan  * F16h: has only 1 DCT
1257981a28fSAravind Gopalakrishnan  *
1267981a28fSAravind Gopalakrishnan  * F15h: we select which DCT we access using F1x10C[DctCfgSel]
1277981a28fSAravind Gopalakrishnan  */
1287981a28fSAravind Gopalakrishnan static inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct,
1297981a28fSAravind Gopalakrishnan 					 int offset, u32 *val)
130b2b0c605SBorislav Petkov {
1317981a28fSAravind Gopalakrishnan 	switch (pvt->fam) {
1327981a28fSAravind Gopalakrishnan 	case 0xf:
1337981a28fSAravind Gopalakrishnan 		if (dct || offset >= 0x100)
1347981a28fSAravind Gopalakrishnan 			return -EINVAL;
1357981a28fSAravind Gopalakrishnan 		break;
136b2b0c605SBorislav Petkov 
1377981a28fSAravind Gopalakrishnan 	case 0x10:
1387981a28fSAravind Gopalakrishnan 		if (dct) {
1397981a28fSAravind Gopalakrishnan 			/*
1407981a28fSAravind Gopalakrishnan 			 * Note: If ganging is enabled, barring the regs
1417981a28fSAravind Gopalakrishnan 			 * F2x[1,0]98 and F2x[1,0]9C; reads reads to F2x1xx
1427981a28fSAravind Gopalakrishnan 			 * return 0. (cf. Section 2.8.1 F10h BKDG)
1437981a28fSAravind Gopalakrishnan 			 */
1447981a28fSAravind Gopalakrishnan 			if (dct_ganging_enabled(pvt))
1457981a28fSAravind Gopalakrishnan 				return 0;
1467981a28fSAravind Gopalakrishnan 
1477981a28fSAravind Gopalakrishnan 			offset += 0x100;
148b2b0c605SBorislav Petkov 		}
1497981a28fSAravind Gopalakrishnan 		break;
150b2b0c605SBorislav Petkov 
1517981a28fSAravind Gopalakrishnan 	case 0x15:
1527981a28fSAravind Gopalakrishnan 		/*
1537981a28fSAravind Gopalakrishnan 		 * F15h: F2x1xx addresses do not map explicitly to DCT1.
1547981a28fSAravind Gopalakrishnan 		 * We should select which DCT we access using F1x10C[DctCfgSel]
1557981a28fSAravind Gopalakrishnan 		 */
1567981a28fSAravind Gopalakrishnan 		dct = (dct && pvt->model == 0x30) ? 3 : dct;
15773ba8593SBorislav Petkov 		f15h_select_dct(pvt, dct);
1587981a28fSAravind Gopalakrishnan 		break;
159b2b0c605SBorislav Petkov 
1607981a28fSAravind Gopalakrishnan 	case 0x16:
1617981a28fSAravind Gopalakrishnan 		if (dct)
1627981a28fSAravind Gopalakrishnan 			return -EINVAL;
1637981a28fSAravind Gopalakrishnan 		break;
1647981a28fSAravind Gopalakrishnan 
1657981a28fSAravind Gopalakrishnan 	default:
1667981a28fSAravind Gopalakrishnan 		break;
1677981a28fSAravind Gopalakrishnan 	}
1687981a28fSAravind Gopalakrishnan 	return amd64_read_pci_cfg(pvt->F2, offset, val);
169b2b0c605SBorislav Petkov }
170b2b0c605SBorislav Petkov 
171b70ef010SBorislav Petkov /*
1722bc65418SDoug Thompson  * Memory scrubber control interface. For K8, memory scrubbing is handled by
1732bc65418SDoug Thompson  * hardware and can involve L2 cache, dcache as well as the main memory. With
1742bc65418SDoug Thompson  * F10, this is extended to L3 cache scrubbing on CPU models sporting that
1752bc65418SDoug Thompson  * functionality.
1762bc65418SDoug Thompson  *
1772bc65418SDoug Thompson  * This causes the "units" for the scrubbing speed to vary from 64 byte blocks
1782bc65418SDoug Thompson  * (dram) over to cache lines. This is nasty, so we will use bandwidth in
1792bc65418SDoug Thompson  * bytes/sec for the setting.
1802bc65418SDoug Thompson  *
1812bc65418SDoug Thompson  * Currently, we only do dram scrubbing. If the scrubbing is done in software on
1822bc65418SDoug Thompson  * other archs, we might not have access to the caches directly.
1832bc65418SDoug Thompson  */
1842bc65418SDoug Thompson 
1858051c0afSYazen Ghannam static inline void __f17h_set_scrubval(struct amd64_pvt *pvt, u32 scrubval)
1868051c0afSYazen Ghannam {
1872bc65418SDoug Thompson 	/*
1888051c0afSYazen Ghannam 	 * Fam17h supports scrub values between 0x5 and 0x14. Also, the values
1898051c0afSYazen Ghannam 	 * are shifted down by 0x5, so scrubval 0x5 is written to the register
1908051c0afSYazen Ghannam 	 * as 0x0, scrubval 0x6 as 0x1, etc.
1918051c0afSYazen Ghannam 	 */
1928051c0afSYazen Ghannam 	if (scrubval >= 0x5 && scrubval <= 0x14) {
1938051c0afSYazen Ghannam 		scrubval -= 0x5;
1948051c0afSYazen Ghannam 		pci_write_bits32(pvt->F6, F17H_SCR_LIMIT_ADDR, scrubval, 0xF);
1958051c0afSYazen Ghannam 		pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 1, 0x1);
1968051c0afSYazen Ghannam 	} else {
1978051c0afSYazen Ghannam 		pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 0, 0x1);
1988051c0afSYazen Ghannam 	}
1998051c0afSYazen Ghannam }
2008051c0afSYazen Ghannam /*
2018051c0afSYazen Ghannam  * Scan the scrub rate mapping table for a close or matching bandwidth value to
2022bc65418SDoug Thompson  * issue. If requested is too big, then use last maximum value found.
2032bc65418SDoug Thompson  */
204da92110dSAravind Gopalakrishnan static int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate)
2052bc65418SDoug Thompson {
2062bc65418SDoug Thompson 	u32 scrubval;
2072bc65418SDoug Thompson 	int i;
2082bc65418SDoug Thompson 
2092bc65418SDoug Thompson 	/*
2102bc65418SDoug Thompson 	 * map the configured rate (new_bw) to a value specific to the AMD64
2112bc65418SDoug Thompson 	 * memory controller and apply to register. Search for the first
2122bc65418SDoug Thompson 	 * bandwidth entry that is greater or equal than the setting requested
2132bc65418SDoug Thompson 	 * and program that. If at last entry, turn off DRAM scrubbing.
214168bfeefSAndrew Morton 	 *
215168bfeefSAndrew Morton 	 * If no suitable bandwidth is found, turn off DRAM scrubbing entirely
216168bfeefSAndrew Morton 	 * by falling back to the last element in scrubrates[].
2172bc65418SDoug Thompson 	 */
218168bfeefSAndrew Morton 	for (i = 0; i < ARRAY_SIZE(scrubrates) - 1; i++) {
2192bc65418SDoug Thompson 		/*
2202bc65418SDoug Thompson 		 * skip scrub rates which aren't recommended
2212bc65418SDoug Thompson 		 * (see F10 BKDG, F3x58)
2222bc65418SDoug Thompson 		 */
223395ae783SBorislav Petkov 		if (scrubrates[i].scrubval < min_rate)
2242bc65418SDoug Thompson 			continue;
2252bc65418SDoug Thompson 
2262bc65418SDoug Thompson 		if (scrubrates[i].bandwidth <= new_bw)
2272bc65418SDoug Thompson 			break;
2282bc65418SDoug Thompson 	}
2292bc65418SDoug Thompson 
2302bc65418SDoug Thompson 	scrubval = scrubrates[i].scrubval;
2312bc65418SDoug Thompson 
232dcd01394SYazen Ghannam 	if (pvt->umc) {
2338051c0afSYazen Ghannam 		__f17h_set_scrubval(pvt, scrubval);
2348051c0afSYazen Ghannam 	} else if (pvt->fam == 0x15 && pvt->model == 0x60) {
235da92110dSAravind Gopalakrishnan 		f15h_select_dct(pvt, 0);
236da92110dSAravind Gopalakrishnan 		pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
237da92110dSAravind Gopalakrishnan 		f15h_select_dct(pvt, 1);
238da92110dSAravind Gopalakrishnan 		pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
239da92110dSAravind Gopalakrishnan 	} else {
240da92110dSAravind Gopalakrishnan 		pci_write_bits32(pvt->F3, SCRCTRL, scrubval, 0x001F);
241da92110dSAravind Gopalakrishnan 	}
2422bc65418SDoug Thompson 
24339094443SBorislav Petkov 	if (scrubval)
24439094443SBorislav Petkov 		return scrubrates[i].bandwidth;
24539094443SBorislav Petkov 
2462bc65418SDoug Thompson 	return 0;
2472bc65418SDoug Thompson }
2482bc65418SDoug Thompson 
249d1ea71cdSBorislav Petkov static int set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
2502bc65418SDoug Thompson {
2512bc65418SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
25287b3e0e6SBorislav Petkov 	u32 min_scrubrate = 0x5;
2532bc65418SDoug Thompson 
254a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
25587b3e0e6SBorislav Petkov 		min_scrubrate = 0x0;
25687b3e0e6SBorislav Petkov 
257da92110dSAravind Gopalakrishnan 	if (pvt->fam == 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 			min_scrubrate = 0x6;
264da92110dSAravind Gopalakrishnan 	}
265da92110dSAravind Gopalakrishnan 	return __set_scrub_rate(pvt, bw, min_scrubrate);
2662bc65418SDoug Thompson }
2672bc65418SDoug Thompson 
268d1ea71cdSBorislav Petkov static int get_scrub_rate(struct mem_ctl_info *mci)
2692bc65418SDoug Thompson {
2702bc65418SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
27139094443SBorislav Petkov 	int i, retval = -EINVAL;
2728051c0afSYazen Ghannam 	u32 scrubval = 0;
2732bc65418SDoug Thompson 
274dcd01394SYazen Ghannam 	if (pvt->umc) {
2758051c0afSYazen Ghannam 		amd64_read_pci_cfg(pvt->F6, F17H_SCR_BASE_ADDR, &scrubval);
2768051c0afSYazen Ghannam 		if (scrubval & BIT(0)) {
2778051c0afSYazen Ghannam 			amd64_read_pci_cfg(pvt->F6, F17H_SCR_LIMIT_ADDR, &scrubval);
2788051c0afSYazen Ghannam 			scrubval &= 0xF;
2798051c0afSYazen Ghannam 			scrubval += 0x5;
2808051c0afSYazen Ghannam 		} else {
2818051c0afSYazen Ghannam 			scrubval = 0;
2828051c0afSYazen Ghannam 		}
283dcd01394SYazen Ghannam 	} else if (pvt->fam == 0x15) {
284dcd01394SYazen Ghannam 		/* Erratum #505 */
285dcd01394SYazen Ghannam 		if (pvt->model < 0x10)
286dcd01394SYazen Ghannam 			f15h_select_dct(pvt, 0);
2878051c0afSYazen Ghannam 
288dcd01394SYazen Ghannam 		if (pvt->model == 0x60)
289dcd01394SYazen Ghannam 			amd64_read_pci_cfg(pvt->F2, F15H_M60H_SCRCTRL, &scrubval);
290ee470bb2SBorislav Petkov 		else
291ee470bb2SBorislav Petkov 			amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
292dcd01394SYazen Ghannam 	} else {
2935980bb9cSBorislav Petkov 		amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
2948051c0afSYazen Ghannam 	}
2952bc65418SDoug Thompson 
2962bc65418SDoug Thompson 	scrubval = scrubval & 0x001F;
2972bc65418SDoug Thompson 
298926311fdSRoel Kluin 	for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
2992bc65418SDoug Thompson 		if (scrubrates[i].scrubval == scrubval) {
30039094443SBorislav Petkov 			retval = scrubrates[i].bandwidth;
3012bc65418SDoug Thompson 			break;
3022bc65418SDoug Thompson 		}
3032bc65418SDoug Thompson 	}
30439094443SBorislav Petkov 	return retval;
3052bc65418SDoug Thompson }
3062bc65418SDoug Thompson 
3076775763aSDoug Thompson /*
3087f19bf75SBorislav Petkov  * returns true if the SysAddr given by sys_addr matches the
3097f19bf75SBorislav Petkov  * DRAM base/limit associated with node_id
3106775763aSDoug Thompson  */
311d1ea71cdSBorislav Petkov static bool base_limit_match(struct amd64_pvt *pvt, u64 sys_addr, u8 nid)
3126775763aSDoug Thompson {
3137f19bf75SBorislav Petkov 	u64 addr;
3146775763aSDoug Thompson 
3156775763aSDoug Thompson 	/* The K8 treats this as a 40-bit value.  However, bits 63-40 will be
3166775763aSDoug Thompson 	 * all ones if the most significant implemented address bit is 1.
3176775763aSDoug Thompson 	 * Here we discard bits 63-40.  See section 3.4.2 of AMD publication
3186775763aSDoug Thompson 	 * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1
3196775763aSDoug Thompson 	 * Application Programming.
3206775763aSDoug Thompson 	 */
3216775763aSDoug Thompson 	addr = sys_addr & 0x000000ffffffffffull;
3226775763aSDoug Thompson 
3237f19bf75SBorislav Petkov 	return ((addr >= get_dram_base(pvt, nid)) &&
3247f19bf75SBorislav Petkov 		(addr <= get_dram_limit(pvt, nid)));
3256775763aSDoug Thompson }
3266775763aSDoug Thompson 
3276775763aSDoug Thompson /*
3286775763aSDoug Thompson  * Attempt to map a SysAddr to a node. On success, return a pointer to the
3296775763aSDoug Thompson  * mem_ctl_info structure for the node that the SysAddr maps to.
3306775763aSDoug Thompson  *
3316775763aSDoug Thompson  * On failure, return NULL.
3326775763aSDoug Thompson  */
3336775763aSDoug Thompson static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci,
3346775763aSDoug Thompson 						u64 sys_addr)
3356775763aSDoug Thompson {
3366775763aSDoug Thompson 	struct amd64_pvt *pvt;
337c7e5301aSDaniel J Blueman 	u8 node_id;
3386775763aSDoug Thompson 	u32 intlv_en, bits;
3396775763aSDoug Thompson 
3406775763aSDoug Thompson 	/*
3416775763aSDoug Thompson 	 * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section
3426775763aSDoug Thompson 	 * 3.4.4.2) registers to map the SysAddr to a node ID.
3436775763aSDoug Thompson 	 */
3446775763aSDoug Thompson 	pvt = mci->pvt_info;
3456775763aSDoug Thompson 
3466775763aSDoug Thompson 	/*
3476775763aSDoug Thompson 	 * The value of this field should be the same for all DRAM Base
3486775763aSDoug Thompson 	 * registers.  Therefore we arbitrarily choose to read it from the
3496775763aSDoug Thompson 	 * register for node 0.
3506775763aSDoug Thompson 	 */
3517f19bf75SBorislav Petkov 	intlv_en = dram_intlv_en(pvt, 0);
3526775763aSDoug Thompson 
3536775763aSDoug Thompson 	if (intlv_en == 0) {
3547f19bf75SBorislav Petkov 		for (node_id = 0; node_id < DRAM_RANGES; node_id++) {
355d1ea71cdSBorislav Petkov 			if (base_limit_match(pvt, sys_addr, node_id))
3566775763aSDoug Thompson 				goto found;
3576775763aSDoug Thompson 		}
3588edc5445SBorislav Petkov 		goto err_no_match;
3598edc5445SBorislav Petkov 	}
3606775763aSDoug Thompson 
36172f158feSBorislav Petkov 	if (unlikely((intlv_en != 0x01) &&
36272f158feSBorislav Petkov 		     (intlv_en != 0x03) &&
36372f158feSBorislav Petkov 		     (intlv_en != 0x07))) {
36424f9a7feSBorislav Petkov 		amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en);
3656775763aSDoug Thompson 		return NULL;
3666775763aSDoug Thompson 	}
3676775763aSDoug Thompson 
3686775763aSDoug Thompson 	bits = (((u32) sys_addr) >> 12) & intlv_en;
3696775763aSDoug Thompson 
3706775763aSDoug Thompson 	for (node_id = 0; ; ) {
3717f19bf75SBorislav Petkov 		if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits)
3726775763aSDoug Thompson 			break;	/* intlv_sel field matches */
3736775763aSDoug Thompson 
3747f19bf75SBorislav Petkov 		if (++node_id >= DRAM_RANGES)
3756775763aSDoug Thompson 			goto err_no_match;
3766775763aSDoug Thompson 	}
3776775763aSDoug Thompson 
3786775763aSDoug Thompson 	/* sanity test for sys_addr */
379d1ea71cdSBorislav Petkov 	if (unlikely(!base_limit_match(pvt, sys_addr, node_id))) {
38024f9a7feSBorislav Petkov 		amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address"
38124f9a7feSBorislav Petkov 			   "range for node %d with node interleaving enabled.\n",
3828edc5445SBorislav Petkov 			   __func__, sys_addr, node_id);
3836775763aSDoug Thompson 		return NULL;
3846775763aSDoug Thompson 	}
3856775763aSDoug Thompson 
3866775763aSDoug Thompson found:
387b487c33eSBorislav Petkov 	return edac_mc_find((int)node_id);
3886775763aSDoug Thompson 
3896775763aSDoug Thompson err_no_match:
390956b9ba1SJoe Perches 	edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n",
3916775763aSDoug Thompson 		 (unsigned long)sys_addr);
3926775763aSDoug Thompson 
3936775763aSDoug Thompson 	return NULL;
3946775763aSDoug Thompson }
395e2ce7255SDoug Thompson 
396e2ce7255SDoug Thompson /*
39711c75eadSBorislav Petkov  * compute the CS base address of the @csrow on the DRAM controller @dct.
39811c75eadSBorislav Petkov  * For details see F2x[5C:40] in the processor's BKDG
399e2ce7255SDoug Thompson  */
40011c75eadSBorislav Petkov static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct,
40111c75eadSBorislav Petkov 				 u64 *base, u64 *mask)
402e2ce7255SDoug Thompson {
40311c75eadSBorislav Petkov 	u64 csbase, csmask, base_bits, mask_bits;
40411c75eadSBorislav Petkov 	u8 addr_shift;
40511c75eadSBorislav Petkov 
40618b94f66SAravind Gopalakrishnan 	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
40711c75eadSBorislav Petkov 		csbase		= pvt->csels[dct].csbases[csrow];
40811c75eadSBorislav Petkov 		csmask		= pvt->csels[dct].csmasks[csrow];
40910ef6b0dSChen, Gong 		base_bits	= GENMASK_ULL(31, 21) | GENMASK_ULL(15, 9);
41010ef6b0dSChen, Gong 		mask_bits	= GENMASK_ULL(29, 21) | GENMASK_ULL(15, 9);
41111c75eadSBorislav Petkov 		addr_shift	= 4;
41294c1acf2SAravind Gopalakrishnan 
41394c1acf2SAravind Gopalakrishnan 	/*
41418b94f66SAravind Gopalakrishnan 	 * F16h and F15h, models 30h and later need two addr_shift values:
41518b94f66SAravind Gopalakrishnan 	 * 8 for high and 6 for low (cf. F16h BKDG).
41694c1acf2SAravind Gopalakrishnan 	 */
41718b94f66SAravind Gopalakrishnan 	} else if (pvt->fam == 0x16 ||
41818b94f66SAravind Gopalakrishnan 		  (pvt->fam == 0x15 && pvt->model >= 0x30)) {
41994c1acf2SAravind Gopalakrishnan 		csbase          = pvt->csels[dct].csbases[csrow];
42094c1acf2SAravind Gopalakrishnan 		csmask          = pvt->csels[dct].csmasks[csrow >> 1];
42194c1acf2SAravind Gopalakrishnan 
42210ef6b0dSChen, Gong 		*base  = (csbase & GENMASK_ULL(15,  5)) << 6;
42310ef6b0dSChen, Gong 		*base |= (csbase & GENMASK_ULL(30, 19)) << 8;
42494c1acf2SAravind Gopalakrishnan 
42594c1acf2SAravind Gopalakrishnan 		*mask = ~0ULL;
42694c1acf2SAravind Gopalakrishnan 		/* poke holes for the csmask */
42710ef6b0dSChen, Gong 		*mask &= ~((GENMASK_ULL(15, 5)  << 6) |
42810ef6b0dSChen, Gong 			   (GENMASK_ULL(30, 19) << 8));
42994c1acf2SAravind Gopalakrishnan 
43010ef6b0dSChen, Gong 		*mask |= (csmask & GENMASK_ULL(15, 5))  << 6;
43110ef6b0dSChen, Gong 		*mask |= (csmask & GENMASK_ULL(30, 19)) << 8;
43294c1acf2SAravind Gopalakrishnan 
43394c1acf2SAravind Gopalakrishnan 		return;
43411c75eadSBorislav Petkov 	} else {
43511c75eadSBorislav Petkov 		csbase		= pvt->csels[dct].csbases[csrow];
43611c75eadSBorislav Petkov 		csmask		= pvt->csels[dct].csmasks[csrow >> 1];
43711c75eadSBorislav Petkov 		addr_shift	= 8;
43811c75eadSBorislav Petkov 
439a4b4bedcSBorislav Petkov 		if (pvt->fam == 0x15)
44010ef6b0dSChen, Gong 			base_bits = mask_bits =
44110ef6b0dSChen, Gong 				GENMASK_ULL(30,19) | GENMASK_ULL(13,5);
44211c75eadSBorislav Petkov 		else
44310ef6b0dSChen, Gong 			base_bits = mask_bits =
44410ef6b0dSChen, Gong 				GENMASK_ULL(28,19) | GENMASK_ULL(13,5);
445e2ce7255SDoug Thompson 	}
446e2ce7255SDoug Thompson 
44711c75eadSBorislav Petkov 	*base  = (csbase & base_bits) << addr_shift;
448e2ce7255SDoug Thompson 
44911c75eadSBorislav Petkov 	*mask  = ~0ULL;
45011c75eadSBorislav Petkov 	/* poke holes for the csmask */
45111c75eadSBorislav Petkov 	*mask &= ~(mask_bits << addr_shift);
45211c75eadSBorislav Petkov 	/* OR them in */
45311c75eadSBorislav Petkov 	*mask |= (csmask & mask_bits) << addr_shift;
454e2ce7255SDoug Thompson }
455e2ce7255SDoug Thompson 
45611c75eadSBorislav Petkov #define for_each_chip_select(i, dct, pvt) \
45711c75eadSBorislav Petkov 	for (i = 0; i < pvt->csels[dct].b_cnt; i++)
45811c75eadSBorislav Petkov 
459614ec9d8SBorislav Petkov #define chip_select_base(i, dct, pvt) \
460614ec9d8SBorislav Petkov 	pvt->csels[dct].csbases[i]
461614ec9d8SBorislav Petkov 
46211c75eadSBorislav Petkov #define for_each_chip_select_mask(i, dct, pvt) \
46311c75eadSBorislav Petkov 	for (i = 0; i < pvt->csels[dct].m_cnt; i++)
46411c75eadSBorislav Petkov 
4654d30d2bcSYazen Ghannam #define for_each_umc(i) \
4665e4c5527SYazen Ghannam 	for (i = 0; i < fam_type->max_mcs; i++)
4674d30d2bcSYazen Ghannam 
468e2ce7255SDoug Thompson /*
469e2ce7255SDoug Thompson  * @input_addr is an InputAddr associated with the node given by mci. Return the
470e2ce7255SDoug Thompson  * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr).
471e2ce7255SDoug Thompson  */
472e2ce7255SDoug Thompson static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr)
473e2ce7255SDoug Thompson {
474e2ce7255SDoug Thompson 	struct amd64_pvt *pvt;
475e2ce7255SDoug Thompson 	int csrow;
476e2ce7255SDoug Thompson 	u64 base, mask;
477e2ce7255SDoug Thompson 
478e2ce7255SDoug Thompson 	pvt = mci->pvt_info;
479e2ce7255SDoug Thompson 
48011c75eadSBorislav Petkov 	for_each_chip_select(csrow, 0, pvt) {
48111c75eadSBorislav Petkov 		if (!csrow_enabled(csrow, 0, pvt))
482e2ce7255SDoug Thompson 			continue;
483e2ce7255SDoug Thompson 
48411c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, csrow, 0, &base, &mask);
48511c75eadSBorislav Petkov 
48611c75eadSBorislav Petkov 		mask = ~mask;
487e2ce7255SDoug Thompson 
488e2ce7255SDoug Thompson 		if ((input_addr & mask) == (base & mask)) {
489956b9ba1SJoe Perches 			edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n",
490e2ce7255SDoug Thompson 				 (unsigned long)input_addr, csrow,
491e2ce7255SDoug Thompson 				 pvt->mc_node_id);
492e2ce7255SDoug Thompson 
493e2ce7255SDoug Thompson 			return csrow;
494e2ce7255SDoug Thompson 		}
495e2ce7255SDoug Thompson 	}
496956b9ba1SJoe Perches 	edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n",
497e2ce7255SDoug Thompson 		 (unsigned long)input_addr, pvt->mc_node_id);
498e2ce7255SDoug Thompson 
499e2ce7255SDoug Thompson 	return -1;
500e2ce7255SDoug Thompson }
501e2ce7255SDoug Thompson 
502e2ce7255SDoug Thompson /*
503e2ce7255SDoug Thompson  * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094)
504e2ce7255SDoug Thompson  * for the node represented by mci. Info is passed back in *hole_base,
505e2ce7255SDoug Thompson  * *hole_offset, and *hole_size.  Function returns 0 if info is valid or 1 if
506e2ce7255SDoug Thompson  * info is invalid. Info may be invalid for either of the following reasons:
507e2ce7255SDoug Thompson  *
508e2ce7255SDoug Thompson  * - The revision of the node is not E or greater.  In this case, the DRAM Hole
509e2ce7255SDoug Thompson  *   Address Register does not exist.
510e2ce7255SDoug Thompson  *
511e2ce7255SDoug Thompson  * - The DramHoleValid bit is cleared in the DRAM Hole Address Register,
512e2ce7255SDoug Thompson  *   indicating that its contents are not valid.
513e2ce7255SDoug Thompson  *
514e2ce7255SDoug Thompson  * The values passed back in *hole_base, *hole_offset, and *hole_size are
515e2ce7255SDoug Thompson  * complete 32-bit values despite the fact that the bitfields in the DHAR
516e2ce7255SDoug Thompson  * only represent bits 31-24 of the base and offset values.
517e2ce7255SDoug Thompson  */
5182a28ceefSBorislav Petkov static int get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
519e2ce7255SDoug Thompson 			      u64 *hole_offset, u64 *hole_size)
520e2ce7255SDoug Thompson {
521e2ce7255SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
522e2ce7255SDoug Thompson 
523e2ce7255SDoug Thompson 	/* only revE and later have the DRAM Hole Address Register */
524a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_E) {
525956b9ba1SJoe Perches 		edac_dbg(1, "  revision %d for node %d does not support DHAR\n",
526e2ce7255SDoug Thompson 			 pvt->ext_model, pvt->mc_node_id);
527e2ce7255SDoug Thompson 		return 1;
528e2ce7255SDoug Thompson 	}
529e2ce7255SDoug Thompson 
530bc21fa57SBorislav Petkov 	/* valid for Fam10h and above */
531a4b4bedcSBorislav Petkov 	if (pvt->fam >= 0x10 && !dhar_mem_hoist_valid(pvt)) {
532956b9ba1SJoe Perches 		edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this system\n");
533e2ce7255SDoug Thompson 		return 1;
534e2ce7255SDoug Thompson 	}
535e2ce7255SDoug Thompson 
536c8e518d5SBorislav Petkov 	if (!dhar_valid(pvt)) {
537956b9ba1SJoe Perches 		edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this node %d\n",
538e2ce7255SDoug Thompson 			 pvt->mc_node_id);
539e2ce7255SDoug Thompson 		return 1;
540e2ce7255SDoug Thompson 	}
541e2ce7255SDoug Thompson 
542e2ce7255SDoug Thompson 	/* This node has Memory Hoisting */
543e2ce7255SDoug Thompson 
544e2ce7255SDoug Thompson 	/* +------------------+--------------------+--------------------+-----
545e2ce7255SDoug Thompson 	 * | memory           | DRAM hole          | relocated          |
546e2ce7255SDoug Thompson 	 * | [0, (x - 1)]     | [x, 0xffffffff]    | addresses from     |
547e2ce7255SDoug Thompson 	 * |                  |                    | DRAM hole          |
548e2ce7255SDoug Thompson 	 * |                  |                    | [0x100000000,      |
549e2ce7255SDoug Thompson 	 * |                  |                    |  (0x100000000+     |
550e2ce7255SDoug Thompson 	 * |                  |                    |   (0xffffffff-x))] |
551e2ce7255SDoug Thompson 	 * +------------------+--------------------+--------------------+-----
552e2ce7255SDoug Thompson 	 *
553e2ce7255SDoug Thompson 	 * Above is a diagram of physical memory showing the DRAM hole and the
554e2ce7255SDoug Thompson 	 * relocated addresses from the DRAM hole.  As shown, the DRAM hole
555e2ce7255SDoug Thompson 	 * starts at address x (the base address) and extends through address
556e2ce7255SDoug Thompson 	 * 0xffffffff.  The DRAM Hole Address Register (DHAR) relocates the
557e2ce7255SDoug Thompson 	 * addresses in the hole so that they start at 0x100000000.
558e2ce7255SDoug Thompson 	 */
559e2ce7255SDoug Thompson 
5601f31677eSBorislav Petkov 	*hole_base = dhar_base(pvt);
5611f31677eSBorislav Petkov 	*hole_size = (1ULL << 32) - *hole_base;
562e2ce7255SDoug Thompson 
563a4b4bedcSBorislav Petkov 	*hole_offset = (pvt->fam > 0xf) ? f10_dhar_offset(pvt)
564a4b4bedcSBorislav Petkov 					: k8_dhar_offset(pvt);
565e2ce7255SDoug Thompson 
566956b9ba1SJoe Perches 	edac_dbg(1, "  DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
567e2ce7255SDoug Thompson 		 pvt->mc_node_id, (unsigned long)*hole_base,
568e2ce7255SDoug Thompson 		 (unsigned long)*hole_offset, (unsigned long)*hole_size);
569e2ce7255SDoug Thompson 
570e2ce7255SDoug Thompson 	return 0;
571e2ce7255SDoug Thompson }
5722a28ceefSBorislav Petkov 
5732a28ceefSBorislav Petkov #ifdef CONFIG_EDAC_DEBUG
5742a28ceefSBorislav Petkov #define EDAC_DCT_ATTR_SHOW(reg)						\
5752a28ceefSBorislav Petkov static ssize_t reg##_show(struct device *dev,				\
5762a28ceefSBorislav Petkov 			 struct device_attribute *mattr, char *data)	\
5772a28ceefSBorislav Petkov {									\
5782a28ceefSBorislav Petkov 	struct mem_ctl_info *mci = to_mci(dev);				\
5792a28ceefSBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;				\
5802a28ceefSBorislav Petkov 									\
5812a28ceefSBorislav Petkov 	return sprintf(data, "0x%016llx\n", (u64)pvt->reg);		\
5822a28ceefSBorislav Petkov }
5832a28ceefSBorislav Petkov 
5842a28ceefSBorislav Petkov EDAC_DCT_ATTR_SHOW(dhar);
5852a28ceefSBorislav Petkov EDAC_DCT_ATTR_SHOW(dbam0);
5862a28ceefSBorislav Petkov EDAC_DCT_ATTR_SHOW(top_mem);
5872a28ceefSBorislav Petkov EDAC_DCT_ATTR_SHOW(top_mem2);
5882a28ceefSBorislav Petkov 
589d19faf0eSDwaipayan Ray static ssize_t dram_hole_show(struct device *dev, struct device_attribute *mattr,
5902a28ceefSBorislav Petkov 			      char *data)
5912a28ceefSBorislav Petkov {
5922a28ceefSBorislav Petkov 	struct mem_ctl_info *mci = to_mci(dev);
5932a28ceefSBorislav Petkov 
5942a28ceefSBorislav Petkov 	u64 hole_base = 0;
5952a28ceefSBorislav Petkov 	u64 hole_offset = 0;
5962a28ceefSBorislav Petkov 	u64 hole_size = 0;
5972a28ceefSBorislav Petkov 
5982a28ceefSBorislav Petkov 	get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size);
5992a28ceefSBorislav Petkov 
6002a28ceefSBorislav Petkov 	return sprintf(data, "%llx %llx %llx\n", hole_base, hole_offset,
6012a28ceefSBorislav Petkov 						 hole_size);
6022a28ceefSBorislav Petkov }
6032a28ceefSBorislav Petkov 
6042a28ceefSBorislav Petkov /*
6052a28ceefSBorislav Petkov  * update NUM_DBG_ATTRS in case you add new members
6062a28ceefSBorislav Petkov  */
6072a28ceefSBorislav Petkov static DEVICE_ATTR(dhar, S_IRUGO, dhar_show, NULL);
6082a28ceefSBorislav Petkov static DEVICE_ATTR(dbam, S_IRUGO, dbam0_show, NULL);
6092a28ceefSBorislav Petkov static DEVICE_ATTR(topmem, S_IRUGO, top_mem_show, NULL);
6102a28ceefSBorislav Petkov static DEVICE_ATTR(topmem2, S_IRUGO, top_mem2_show, NULL);
611d19faf0eSDwaipayan Ray static DEVICE_ATTR_RO(dram_hole);
6122a28ceefSBorislav Petkov 
6132a28ceefSBorislav Petkov static struct attribute *dbg_attrs[] = {
6142a28ceefSBorislav Petkov 	&dev_attr_dhar.attr,
6152a28ceefSBorislav Petkov 	&dev_attr_dbam.attr,
6162a28ceefSBorislav Petkov 	&dev_attr_topmem.attr,
6172a28ceefSBorislav Petkov 	&dev_attr_topmem2.attr,
6182a28ceefSBorislav Petkov 	&dev_attr_dram_hole.attr,
6192a28ceefSBorislav Petkov 	NULL
6202a28ceefSBorislav Petkov };
6212a28ceefSBorislav Petkov 
6222a28ceefSBorislav Petkov static const struct attribute_group dbg_group = {
6232a28ceefSBorislav Petkov 	.attrs = dbg_attrs,
6242a28ceefSBorislav Petkov };
6252a28ceefSBorislav Petkov 
62661810096SBorislav Petkov static ssize_t inject_section_show(struct device *dev,
62761810096SBorislav Petkov 				   struct device_attribute *mattr, char *buf)
62861810096SBorislav Petkov {
62961810096SBorislav Petkov 	struct mem_ctl_info *mci = to_mci(dev);
63061810096SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
63161810096SBorislav Petkov 	return sprintf(buf, "0x%x\n", pvt->injection.section);
63261810096SBorislav Petkov }
63361810096SBorislav Petkov 
63461810096SBorislav Petkov /*
63561810096SBorislav Petkov  * store error injection section value which refers to one of 4 16-byte sections
63661810096SBorislav Petkov  * within a 64-byte cacheline
63761810096SBorislav Petkov  *
63861810096SBorislav Petkov  * range: 0..3
63961810096SBorislav Petkov  */
64061810096SBorislav Petkov static ssize_t inject_section_store(struct device *dev,
64161810096SBorislav Petkov 				    struct device_attribute *mattr,
64261810096SBorislav Petkov 				    const char *data, size_t count)
64361810096SBorislav Petkov {
64461810096SBorislav Petkov 	struct mem_ctl_info *mci = to_mci(dev);
64561810096SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
64661810096SBorislav Petkov 	unsigned long value;
64761810096SBorislav Petkov 	int ret;
64861810096SBorislav Petkov 
64961810096SBorislav Petkov 	ret = kstrtoul(data, 10, &value);
65061810096SBorislav Petkov 	if (ret < 0)
65161810096SBorislav Petkov 		return ret;
65261810096SBorislav Petkov 
65361810096SBorislav Petkov 	if (value > 3) {
65461810096SBorislav Petkov 		amd64_warn("%s: invalid section 0x%lx\n", __func__, value);
65561810096SBorislav Petkov 		return -EINVAL;
65661810096SBorislav Petkov 	}
65761810096SBorislav Petkov 
65861810096SBorislav Petkov 	pvt->injection.section = (u32) value;
65961810096SBorislav Petkov 	return count;
66061810096SBorislav Petkov }
66161810096SBorislav Petkov 
66261810096SBorislav Petkov static ssize_t inject_word_show(struct device *dev,
66361810096SBorislav Petkov 				struct device_attribute *mattr, char *buf)
66461810096SBorislav Petkov {
66561810096SBorislav Petkov 	struct mem_ctl_info *mci = to_mci(dev);
66661810096SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
66761810096SBorislav Petkov 	return sprintf(buf, "0x%x\n", pvt->injection.word);
66861810096SBorislav Petkov }
66961810096SBorislav Petkov 
67061810096SBorislav Petkov /*
67161810096SBorislav Petkov  * store error injection word value which refers to one of 9 16-bit word of the
67261810096SBorislav Petkov  * 16-byte (128-bit + ECC bits) section
67361810096SBorislav Petkov  *
67461810096SBorislav Petkov  * range: 0..8
67561810096SBorislav Petkov  */
67661810096SBorislav Petkov static ssize_t inject_word_store(struct device *dev,
67761810096SBorislav Petkov 				 struct device_attribute *mattr,
67861810096SBorislav Petkov 				 const char *data, size_t count)
67961810096SBorislav Petkov {
68061810096SBorislav Petkov 	struct mem_ctl_info *mci = to_mci(dev);
68161810096SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
68261810096SBorislav Petkov 	unsigned long value;
68361810096SBorislav Petkov 	int ret;
68461810096SBorislav Petkov 
68561810096SBorislav Petkov 	ret = kstrtoul(data, 10, &value);
68661810096SBorislav Petkov 	if (ret < 0)
68761810096SBorislav Petkov 		return ret;
68861810096SBorislav Petkov 
68961810096SBorislav Petkov 	if (value > 8) {
69061810096SBorislav Petkov 		amd64_warn("%s: invalid word 0x%lx\n", __func__, value);
69161810096SBorislav Petkov 		return -EINVAL;
69261810096SBorislav Petkov 	}
69361810096SBorislav Petkov 
69461810096SBorislav Petkov 	pvt->injection.word = (u32) value;
69561810096SBorislav Petkov 	return count;
69661810096SBorislav Petkov }
69761810096SBorislav Petkov 
69861810096SBorislav Petkov static ssize_t inject_ecc_vector_show(struct device *dev,
69961810096SBorislav Petkov 				      struct device_attribute *mattr,
70061810096SBorislav Petkov 				      char *buf)
70161810096SBorislav Petkov {
70261810096SBorislav Petkov 	struct mem_ctl_info *mci = to_mci(dev);
70361810096SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
70461810096SBorislav Petkov 	return sprintf(buf, "0x%x\n", pvt->injection.bit_map);
70561810096SBorislav Petkov }
70661810096SBorislav Petkov 
70761810096SBorislav Petkov /*
70861810096SBorislav Petkov  * store 16 bit error injection vector which enables injecting errors to the
70961810096SBorislav Petkov  * corresponding bit within the error injection word above. When used during a
71061810096SBorislav Petkov  * DRAM ECC read, it holds the contents of the of the DRAM ECC bits.
71161810096SBorislav Petkov  */
71261810096SBorislav Petkov static ssize_t inject_ecc_vector_store(struct device *dev,
71361810096SBorislav Petkov 				       struct device_attribute *mattr,
71461810096SBorislav Petkov 				       const char *data, size_t count)
71561810096SBorislav Petkov {
71661810096SBorislav Petkov 	struct mem_ctl_info *mci = to_mci(dev);
71761810096SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
71861810096SBorislav Petkov 	unsigned long value;
71961810096SBorislav Petkov 	int ret;
72061810096SBorislav Petkov 
72161810096SBorislav Petkov 	ret = kstrtoul(data, 16, &value);
72261810096SBorislav Petkov 	if (ret < 0)
72361810096SBorislav Petkov 		return ret;
72461810096SBorislav Petkov 
72561810096SBorislav Petkov 	if (value & 0xFFFF0000) {
72661810096SBorislav Petkov 		amd64_warn("%s: invalid EccVector: 0x%lx\n", __func__, value);
72761810096SBorislav Petkov 		return -EINVAL;
72861810096SBorislav Petkov 	}
72961810096SBorislav Petkov 
73061810096SBorislav Petkov 	pvt->injection.bit_map = (u32) value;
73161810096SBorislav Petkov 	return count;
73261810096SBorislav Petkov }
73361810096SBorislav Petkov 
73461810096SBorislav Petkov /*
73561810096SBorislav Petkov  * Do a DRAM ECC read. Assemble staged values in the pvt area, format into
73661810096SBorislav Petkov  * fields needed by the injection registers and read the NB Array Data Port.
73761810096SBorislav Petkov  */
73861810096SBorislav Petkov static ssize_t inject_read_store(struct device *dev,
73961810096SBorislav Petkov 				 struct device_attribute *mattr,
74061810096SBorislav Petkov 				 const char *data, size_t count)
74161810096SBorislav Petkov {
74261810096SBorislav Petkov 	struct mem_ctl_info *mci = to_mci(dev);
74361810096SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
74461810096SBorislav Petkov 	unsigned long value;
74561810096SBorislav Petkov 	u32 section, word_bits;
74661810096SBorislav Petkov 	int ret;
74761810096SBorislav Petkov 
74861810096SBorislav Petkov 	ret = kstrtoul(data, 10, &value);
74961810096SBorislav Petkov 	if (ret < 0)
75061810096SBorislav Petkov 		return ret;
75161810096SBorislav Petkov 
75261810096SBorislav Petkov 	/* Form value to choose 16-byte section of cacheline */
75361810096SBorislav Petkov 	section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section);
75461810096SBorislav Petkov 
75561810096SBorislav Petkov 	amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section);
75661810096SBorislav Petkov 
75761810096SBorislav Petkov 	word_bits = SET_NB_DRAM_INJECTION_READ(pvt->injection);
75861810096SBorislav Petkov 
75961810096SBorislav Petkov 	/* Issue 'word' and 'bit' along with the READ request */
76061810096SBorislav Petkov 	amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits);
76161810096SBorislav Petkov 
76261810096SBorislav Petkov 	edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits);
76361810096SBorislav Petkov 
76461810096SBorislav Petkov 	return count;
76561810096SBorislav Petkov }
76661810096SBorislav Petkov 
76761810096SBorislav Petkov /*
76861810096SBorislav Petkov  * Do a DRAM ECC write. Assemble staged values in the pvt area and format into
76961810096SBorislav Petkov  * fields needed by the injection registers.
77061810096SBorislav Petkov  */
77161810096SBorislav Petkov static ssize_t inject_write_store(struct device *dev,
77261810096SBorislav Petkov 				  struct device_attribute *mattr,
77361810096SBorislav Petkov 				  const char *data, size_t count)
77461810096SBorislav Petkov {
77561810096SBorislav Petkov 	struct mem_ctl_info *mci = to_mci(dev);
77661810096SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
77761810096SBorislav Petkov 	u32 section, word_bits, tmp;
77861810096SBorislav Petkov 	unsigned long value;
77961810096SBorislav Petkov 	int ret;
78061810096SBorislav Petkov 
78161810096SBorislav Petkov 	ret = kstrtoul(data, 10, &value);
78261810096SBorislav Petkov 	if (ret < 0)
78361810096SBorislav Petkov 		return ret;
78461810096SBorislav Petkov 
78561810096SBorislav Petkov 	/* Form value to choose 16-byte section of cacheline */
78661810096SBorislav Petkov 	section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section);
78761810096SBorislav Petkov 
78861810096SBorislav Petkov 	amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section);
78961810096SBorislav Petkov 
79061810096SBorislav Petkov 	word_bits = SET_NB_DRAM_INJECTION_WRITE(pvt->injection);
79161810096SBorislav Petkov 
79261810096SBorislav Petkov 	pr_notice_once("Don't forget to decrease MCE polling interval in\n"
79361810096SBorislav Petkov 			"/sys/bus/machinecheck/devices/machinecheck<CPUNUM>/check_interval\n"
79461810096SBorislav Petkov 			"so that you can get the error report faster.\n");
79561810096SBorislav Petkov 
79661810096SBorislav Petkov 	on_each_cpu(disable_caches, NULL, 1);
79761810096SBorislav Petkov 
79861810096SBorislav Petkov 	/* Issue 'word' and 'bit' along with the READ request */
79961810096SBorislav Petkov 	amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits);
80061810096SBorislav Petkov 
80161810096SBorislav Petkov  retry:
80261810096SBorislav Petkov 	/* wait until injection happens */
80361810096SBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, &tmp);
80461810096SBorislav Petkov 	if (tmp & F10_NB_ARR_ECC_WR_REQ) {
80561810096SBorislav Petkov 		cpu_relax();
80661810096SBorislav Petkov 		goto retry;
80761810096SBorislav Petkov 	}
80861810096SBorislav Petkov 
80961810096SBorislav Petkov 	on_each_cpu(enable_caches, NULL, 1);
81061810096SBorislav Petkov 
81161810096SBorislav Petkov 	edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits);
81261810096SBorislav Petkov 
81361810096SBorislav Petkov 	return count;
81461810096SBorislav Petkov }
81561810096SBorislav Petkov 
81661810096SBorislav Petkov /*
81761810096SBorislav Petkov  * update NUM_INJ_ATTRS in case you add new members
81861810096SBorislav Petkov  */
81961810096SBorislav Petkov 
820d19faf0eSDwaipayan Ray static DEVICE_ATTR_RW(inject_section);
821d19faf0eSDwaipayan Ray static DEVICE_ATTR_RW(inject_word);
822d19faf0eSDwaipayan Ray static DEVICE_ATTR_RW(inject_ecc_vector);
823d19faf0eSDwaipayan Ray static DEVICE_ATTR_WO(inject_write);
824d19faf0eSDwaipayan Ray static DEVICE_ATTR_WO(inject_read);
82561810096SBorislav Petkov 
82661810096SBorislav Petkov static struct attribute *inj_attrs[] = {
82761810096SBorislav Petkov 	&dev_attr_inject_section.attr,
82861810096SBorislav Petkov 	&dev_attr_inject_word.attr,
82961810096SBorislav Petkov 	&dev_attr_inject_ecc_vector.attr,
83061810096SBorislav Petkov 	&dev_attr_inject_write.attr,
83161810096SBorislav Petkov 	&dev_attr_inject_read.attr,
83261810096SBorislav Petkov 	NULL
83361810096SBorislav Petkov };
83461810096SBorislav Petkov 
83561810096SBorislav Petkov static umode_t inj_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
83661810096SBorislav Petkov {
83761810096SBorislav Petkov 	struct device *dev = kobj_to_dev(kobj);
83861810096SBorislav Petkov 	struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev);
83961810096SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
84061810096SBorislav Petkov 
8411865bc71SBorislav Petkov 	/* Families which have that injection hw */
8421865bc71SBorislav Petkov 	if (pvt->fam >= 0x10 && pvt->fam <= 0x16)
84361810096SBorislav Petkov 		return attr->mode;
8441865bc71SBorislav Petkov 
8451865bc71SBorislav Petkov 	return 0;
84661810096SBorislav Petkov }
84761810096SBorislav Petkov 
84861810096SBorislav Petkov static const struct attribute_group inj_group = {
84961810096SBorislav Petkov 	.attrs = inj_attrs,
85061810096SBorislav Petkov 	.is_visible = inj_is_visible,
85161810096SBorislav Petkov };
85261810096SBorislav Petkov #endif /* CONFIG_EDAC_DEBUG */
853e2ce7255SDoug Thompson 
85493c2df58SDoug Thompson /*
85593c2df58SDoug Thompson  * Return the DramAddr that the SysAddr given by @sys_addr maps to.  It is
85693c2df58SDoug Thompson  * assumed that sys_addr maps to the node given by mci.
85793c2df58SDoug Thompson  *
85893c2df58SDoug Thompson  * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section
85993c2df58SDoug Thompson  * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a
86093c2df58SDoug Thompson  * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled,
86193c2df58SDoug Thompson  * then it is also involved in translating a SysAddr to a DramAddr. Sections
86293c2df58SDoug Thompson  * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting.
86393c2df58SDoug Thompson  * These parts of the documentation are unclear. I interpret them as follows:
86493c2df58SDoug Thompson  *
86593c2df58SDoug Thompson  * When node n receives a SysAddr, it processes the SysAddr as follows:
86693c2df58SDoug Thompson  *
86793c2df58SDoug Thompson  * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM
86893c2df58SDoug Thompson  *    Limit registers for node n. If the SysAddr is not within the range
86993c2df58SDoug Thompson  *    specified by the base and limit values, then node n ignores the Sysaddr
87093c2df58SDoug Thompson  *    (since it does not map to node n). Otherwise continue to step 2 below.
87193c2df58SDoug Thompson  *
87293c2df58SDoug Thompson  * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is
87393c2df58SDoug Thompson  *    disabled so skip to step 3 below. Otherwise see if the SysAddr is within
87493c2df58SDoug Thompson  *    the range of relocated addresses (starting at 0x100000000) from the DRAM
87593c2df58SDoug Thompson  *    hole. If not, skip to step 3 below. Else get the value of the
87693c2df58SDoug Thompson  *    DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the
87793c2df58SDoug Thompson  *    offset defined by this value from the SysAddr.
87893c2df58SDoug Thompson  *
87993c2df58SDoug Thompson  * 3. Obtain the base address for node n from the DRAMBase field of the DRAM
88093c2df58SDoug Thompson  *    Base register for node n. To obtain the DramAddr, subtract the base
88193c2df58SDoug Thompson  *    address from the SysAddr, as shown near the start of section 3.4.4 (p.70).
88293c2df58SDoug Thompson  */
88393c2df58SDoug Thompson static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
88493c2df58SDoug Thompson {
8857f19bf75SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
88693c2df58SDoug Thompson 	u64 dram_base, hole_base, hole_offset, hole_size, dram_addr;
8871f31677eSBorislav Petkov 	int ret;
88893c2df58SDoug Thompson 
8897f19bf75SBorislav Petkov 	dram_base = get_dram_base(pvt, pvt->mc_node_id);
89093c2df58SDoug Thompson 
8912a28ceefSBorislav Petkov 	ret = get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size);
89293c2df58SDoug Thompson 	if (!ret) {
8931f31677eSBorislav Petkov 		if ((sys_addr >= (1ULL << 32)) &&
8941f31677eSBorislav Petkov 		    (sys_addr < ((1ULL << 32) + hole_size))) {
89593c2df58SDoug Thompson 			/* use DHAR to translate SysAddr to DramAddr */
89693c2df58SDoug Thompson 			dram_addr = sys_addr - hole_offset;
89793c2df58SDoug Thompson 
898956b9ba1SJoe Perches 			edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
89993c2df58SDoug Thompson 				 (unsigned long)sys_addr,
90093c2df58SDoug Thompson 				 (unsigned long)dram_addr);
90193c2df58SDoug Thompson 
90293c2df58SDoug Thompson 			return dram_addr;
90393c2df58SDoug Thompson 		}
90493c2df58SDoug Thompson 	}
90593c2df58SDoug Thompson 
90693c2df58SDoug Thompson 	/*
90793c2df58SDoug Thompson 	 * Translate the SysAddr to a DramAddr as shown near the start of
90893c2df58SDoug Thompson 	 * section 3.4.4 (p. 70).  Although sys_addr is a 64-bit value, the k8
90993c2df58SDoug Thompson 	 * only deals with 40-bit values.  Therefore we discard bits 63-40 of
91093c2df58SDoug Thompson 	 * sys_addr below.  If bit 39 of sys_addr is 1 then the bits we
91193c2df58SDoug Thompson 	 * discard are all 1s.  Otherwise the bits we discard are all 0s.  See
91293c2df58SDoug Thompson 	 * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture
91393c2df58SDoug Thompson 	 * Programmer's Manual Volume 1 Application Programming.
91493c2df58SDoug Thompson 	 */
91510ef6b0dSChen, Gong 	dram_addr = (sys_addr & GENMASK_ULL(39, 0)) - dram_base;
91693c2df58SDoug Thompson 
917956b9ba1SJoe Perches 	edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
918956b9ba1SJoe Perches 		 (unsigned long)sys_addr, (unsigned long)dram_addr);
91993c2df58SDoug Thompson 	return dram_addr;
92093c2df58SDoug Thompson }
92193c2df58SDoug Thompson 
92293c2df58SDoug Thompson /*
92393c2df58SDoug Thompson  * @intlv_en is the value of the IntlvEn field from a DRAM Base register
92493c2df58SDoug Thompson  * (section 3.4.4.1).  Return the number of bits from a SysAddr that are used
92593c2df58SDoug Thompson  * for node interleaving.
92693c2df58SDoug Thompson  */
92793c2df58SDoug Thompson static int num_node_interleave_bits(unsigned intlv_en)
92893c2df58SDoug Thompson {
92993c2df58SDoug Thompson 	static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 };
93093c2df58SDoug Thompson 	int n;
93193c2df58SDoug Thompson 
93293c2df58SDoug Thompson 	BUG_ON(intlv_en > 7);
93393c2df58SDoug Thompson 	n = intlv_shift_table[intlv_en];
93493c2df58SDoug Thompson 	return n;
93593c2df58SDoug Thompson }
93693c2df58SDoug Thompson 
93793c2df58SDoug Thompson /* Translate the DramAddr given by @dram_addr to an InputAddr. */
93893c2df58SDoug Thompson static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
93993c2df58SDoug Thompson {
94093c2df58SDoug Thompson 	struct amd64_pvt *pvt;
94193c2df58SDoug Thompson 	int intlv_shift;
94293c2df58SDoug Thompson 	u64 input_addr;
94393c2df58SDoug Thompson 
94493c2df58SDoug Thompson 	pvt = mci->pvt_info;
94593c2df58SDoug Thompson 
94693c2df58SDoug Thompson 	/*
94793c2df58SDoug Thompson 	 * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
94893c2df58SDoug Thompson 	 * concerning translating a DramAddr to an InputAddr.
94993c2df58SDoug Thompson 	 */
9507f19bf75SBorislav Petkov 	intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
95110ef6b0dSChen, Gong 	input_addr = ((dram_addr >> intlv_shift) & GENMASK_ULL(35, 12)) +
95293c2df58SDoug Thompson 		      (dram_addr & 0xfff);
95393c2df58SDoug Thompson 
954956b9ba1SJoe Perches 	edac_dbg(2, "  Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
95593c2df58SDoug Thompson 		 intlv_shift, (unsigned long)dram_addr,
95693c2df58SDoug Thompson 		 (unsigned long)input_addr);
95793c2df58SDoug Thompson 
95893c2df58SDoug Thompson 	return input_addr;
95993c2df58SDoug Thompson }
96093c2df58SDoug Thompson 
96193c2df58SDoug Thompson /*
96293c2df58SDoug Thompson  * Translate the SysAddr represented by @sys_addr to an InputAddr.  It is
96393c2df58SDoug Thompson  * assumed that @sys_addr maps to the node given by mci.
96493c2df58SDoug Thompson  */
96593c2df58SDoug Thompson static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
96693c2df58SDoug Thompson {
96793c2df58SDoug Thompson 	u64 input_addr;
96893c2df58SDoug Thompson 
96993c2df58SDoug Thompson 	input_addr =
97093c2df58SDoug Thompson 	    dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
97193c2df58SDoug Thompson 
972c19ca6cbSMasanari Iida 	edac_dbg(2, "SysAddr 0x%lx translates to InputAddr 0x%lx\n",
97393c2df58SDoug Thompson 		 (unsigned long)sys_addr, (unsigned long)input_addr);
97493c2df58SDoug Thompson 
97593c2df58SDoug Thompson 	return input_addr;
97693c2df58SDoug Thompson }
97793c2df58SDoug Thompson 
97893c2df58SDoug Thompson /* Map the Error address to a PAGE and PAGE OFFSET. */
97993c2df58SDoug Thompson static inline void error_address_to_page_and_offset(u64 error_address,
98033ca0643SBorislav Petkov 						    struct err_info *err)
98193c2df58SDoug Thompson {
98233ca0643SBorislav Petkov 	err->page = (u32) (error_address >> PAGE_SHIFT);
98333ca0643SBorislav Petkov 	err->offset = ((u32) error_address) & ~PAGE_MASK;
98493c2df58SDoug Thompson }
98593c2df58SDoug Thompson 
98693c2df58SDoug Thompson /*
98793c2df58SDoug Thompson  * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address
98893c2df58SDoug Thompson  * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers
98993c2df58SDoug Thompson  * of a node that detected an ECC memory error.  mci represents the node that
99093c2df58SDoug Thompson  * the error address maps to (possibly different from the node that detected
99193c2df58SDoug Thompson  * the error).  Return the number of the csrow that sys_addr maps to, or -1 on
99293c2df58SDoug Thompson  * error.
99393c2df58SDoug Thompson  */
99493c2df58SDoug Thompson static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
99593c2df58SDoug Thompson {
99693c2df58SDoug Thompson 	int csrow;
99793c2df58SDoug Thompson 
99893c2df58SDoug Thompson 	csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr));
99993c2df58SDoug Thompson 
100093c2df58SDoug Thompson 	if (csrow == -1)
100124f9a7feSBorislav Petkov 		amd64_mc_err(mci, "Failed to translate InputAddr to csrow for "
100293c2df58SDoug Thompson 				  "address 0x%lx\n", (unsigned long)sys_addr);
100393c2df58SDoug Thompson 	return csrow;
100493c2df58SDoug Thompson }
1005e2ce7255SDoug Thompson 
1006b3218ae4SYazen Ghannam /* Protect the PCI config register pairs used for DF indirect access. */
1007b3218ae4SYazen Ghannam static DEFINE_MUTEX(df_indirect_mutex);
1008b3218ae4SYazen Ghannam 
1009b3218ae4SYazen Ghannam /*
1010b3218ae4SYazen Ghannam  * Data Fabric Indirect Access uses FICAA/FICAD.
1011b3218ae4SYazen Ghannam  *
1012b3218ae4SYazen Ghannam  * Fabric Indirect Configuration Access Address (FICAA): Constructed based
1013b3218ae4SYazen Ghannam  * on the device's Instance Id and the PCI function and register offset of
1014b3218ae4SYazen Ghannam  * the desired register.
1015b3218ae4SYazen Ghannam  *
1016b3218ae4SYazen Ghannam  * Fabric Indirect Configuration Access Data (FICAD): There are FICAD LO
1017b3218ae4SYazen Ghannam  * and FICAD HI registers but so far we only need the LO register.
1018448c3d60SYazen Ghannam  *
1019448c3d60SYazen Ghannam  * Use Instance Id 0xFF to indicate a broadcast read.
1020b3218ae4SYazen Ghannam  */
1021448c3d60SYazen Ghannam #define DF_BROADCAST	0xFF
1022448c3d60SYazen Ghannam static int __df_indirect_read(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo)
1023b3218ae4SYazen Ghannam {
1024b3218ae4SYazen Ghannam 	struct pci_dev *F4;
1025b3218ae4SYazen Ghannam 	u32 ficaa;
1026b3218ae4SYazen Ghannam 	int err = -ENODEV;
1027b3218ae4SYazen Ghannam 
1028b3218ae4SYazen Ghannam 	if (node >= amd_nb_num())
1029b3218ae4SYazen Ghannam 		goto out;
1030b3218ae4SYazen Ghannam 
1031b3218ae4SYazen Ghannam 	F4 = node_to_amd_nb(node)->link;
1032b3218ae4SYazen Ghannam 	if (!F4)
1033b3218ae4SYazen Ghannam 		goto out;
1034b3218ae4SYazen Ghannam 
1035448c3d60SYazen Ghannam 	ficaa  = (instance_id == DF_BROADCAST) ? 0 : 1;
1036b3218ae4SYazen Ghannam 	ficaa |= reg & 0x3FC;
1037b3218ae4SYazen Ghannam 	ficaa |= (func & 0x7) << 11;
1038b3218ae4SYazen Ghannam 	ficaa |= instance_id << 16;
1039b3218ae4SYazen Ghannam 
1040b3218ae4SYazen Ghannam 	mutex_lock(&df_indirect_mutex);
1041b3218ae4SYazen Ghannam 
1042b3218ae4SYazen Ghannam 	err = pci_write_config_dword(F4, 0x5C, ficaa);
1043b3218ae4SYazen Ghannam 	if (err) {
1044b3218ae4SYazen Ghannam 		pr_warn("Error writing DF Indirect FICAA, FICAA=0x%x\n", ficaa);
1045b3218ae4SYazen Ghannam 		goto out_unlock;
1046b3218ae4SYazen Ghannam 	}
1047b3218ae4SYazen Ghannam 
1048b3218ae4SYazen Ghannam 	err = pci_read_config_dword(F4, 0x98, lo);
1049b3218ae4SYazen Ghannam 	if (err)
1050b3218ae4SYazen Ghannam 		pr_warn("Error reading DF Indirect FICAD LO, FICAA=0x%x.\n", ficaa);
1051b3218ae4SYazen Ghannam 
1052b3218ae4SYazen Ghannam out_unlock:
1053b3218ae4SYazen Ghannam 	mutex_unlock(&df_indirect_mutex);
1054b3218ae4SYazen Ghannam 
1055b3218ae4SYazen Ghannam out:
1056b3218ae4SYazen Ghannam 	return err;
1057b3218ae4SYazen Ghannam }
1058b3218ae4SYazen Ghannam 
1059448c3d60SYazen Ghannam static int df_indirect_read_instance(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo)
1060448c3d60SYazen Ghannam {
1061448c3d60SYazen Ghannam 	return __df_indirect_read(node, func, reg, instance_id, lo);
1062448c3d60SYazen Ghannam }
1063448c3d60SYazen Ghannam 
1064448c3d60SYazen Ghannam static int df_indirect_read_broadcast(u16 node, u8 func, u16 reg, u32 *lo)
1065448c3d60SYazen Ghannam {
1066448c3d60SYazen Ghannam 	return __df_indirect_read(node, func, reg, DF_BROADCAST, lo);
1067448c3d60SYazen Ghannam }
1068448c3d60SYazen Ghannam 
106970aeb807SYazen Ghannam struct addr_ctx {
107070aeb807SYazen Ghannam 	u64 ret_addr;
107170aeb807SYazen Ghannam 	u32 tmp;
107270aeb807SYazen Ghannam 	u16 nid;
107370aeb807SYazen Ghannam 	u8 inst_id;
107470aeb807SYazen Ghannam };
107570aeb807SYazen Ghannam 
10760b746e8cSYazen Ghannam static int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr)
10770b746e8cSYazen Ghannam {
10780b746e8cSYazen Ghannam 	u64 dram_base_addr, dram_limit_addr, dram_hole_base;
10790b746e8cSYazen Ghannam 
10800b746e8cSYazen Ghannam 	u8 die_id_shift, die_id_mask, socket_id_shift, socket_id_mask;
10810b746e8cSYazen Ghannam 	u8 intlv_num_dies, intlv_num_chan, intlv_num_sockets;
10820b746e8cSYazen Ghannam 	u8 intlv_addr_sel, intlv_addr_bit;
10830b746e8cSYazen Ghannam 	u8 num_intlv_bits, hashed_bit;
10840b746e8cSYazen Ghannam 	u8 lgcy_mmio_hole_en, base = 0;
10850b746e8cSYazen Ghannam 	u8 cs_mask, cs_id = 0;
10860b746e8cSYazen Ghannam 	bool hash_enabled = false;
10870b746e8cSYazen Ghannam 
108870aeb807SYazen Ghannam 	struct addr_ctx ctx;
108970aeb807SYazen Ghannam 
109070aeb807SYazen Ghannam 	memset(&ctx, 0, sizeof(ctx));
109170aeb807SYazen Ghannam 
109270aeb807SYazen Ghannam 	/* Start from the normalized address */
109370aeb807SYazen Ghannam 	ctx.ret_addr = norm_addr;
109470aeb807SYazen Ghannam 
109570aeb807SYazen Ghannam 	ctx.nid = nid;
109670aeb807SYazen Ghannam 	ctx.inst_id = umc;
109770aeb807SYazen Ghannam 
10980b746e8cSYazen Ghannam 	/* Read D18F0x1B4 (DramOffset), check if base 1 is used. */
109970aeb807SYazen Ghannam 	if (df_indirect_read_instance(nid, 0, 0x1B4, umc, &ctx.tmp))
11000b746e8cSYazen Ghannam 		goto out_err;
11010b746e8cSYazen Ghannam 
11020b746e8cSYazen Ghannam 	/* Remove HiAddrOffset from normalized address, if enabled: */
110370aeb807SYazen Ghannam 	if (ctx.tmp & BIT(0)) {
110470aeb807SYazen Ghannam 		u64 hi_addr_offset = (ctx.tmp & GENMASK_ULL(31, 20)) << 8;
11050b746e8cSYazen Ghannam 
11060b746e8cSYazen Ghannam 		if (norm_addr >= hi_addr_offset) {
110770aeb807SYazen Ghannam 			ctx.ret_addr -= hi_addr_offset;
11080b746e8cSYazen Ghannam 			base = 1;
11090b746e8cSYazen Ghannam 		}
11100b746e8cSYazen Ghannam 	}
11110b746e8cSYazen Ghannam 
11120b746e8cSYazen Ghannam 	/* Read D18F0x110 (DramBaseAddress). */
111370aeb807SYazen Ghannam 	if (df_indirect_read_instance(nid, 0, 0x110 + (8 * base), umc, &ctx.tmp))
11140b746e8cSYazen Ghannam 		goto out_err;
11150b746e8cSYazen Ghannam 
11160b746e8cSYazen Ghannam 	/* Check if address range is valid. */
111770aeb807SYazen Ghannam 	if (!(ctx.tmp & BIT(0))) {
11180b746e8cSYazen Ghannam 		pr_err("%s: Invalid DramBaseAddress range: 0x%x.\n",
111970aeb807SYazen Ghannam 			__func__, ctx.tmp);
11200b746e8cSYazen Ghannam 		goto out_err;
11210b746e8cSYazen Ghannam 	}
11220b746e8cSYazen Ghannam 
112370aeb807SYazen Ghannam 	lgcy_mmio_hole_en = ctx.tmp & BIT(1);
112470aeb807SYazen Ghannam 	intlv_num_chan	  = (ctx.tmp >> 4) & 0xF;
112570aeb807SYazen Ghannam 	intlv_addr_sel	  = (ctx.tmp >> 8) & 0x7;
112670aeb807SYazen Ghannam 	dram_base_addr	  = (ctx.tmp & GENMASK_ULL(31, 12)) << 16;
11270b746e8cSYazen Ghannam 
11280b746e8cSYazen Ghannam 	/* {0, 1, 2, 3} map to address bits {8, 9, 10, 11} respectively */
11290b746e8cSYazen Ghannam 	if (intlv_addr_sel > 3) {
11300b746e8cSYazen Ghannam 		pr_err("%s: Invalid interleave address select %d.\n",
11310b746e8cSYazen Ghannam 			__func__, intlv_addr_sel);
11320b746e8cSYazen Ghannam 		goto out_err;
11330b746e8cSYazen Ghannam 	}
11340b746e8cSYazen Ghannam 
11350b746e8cSYazen Ghannam 	/* Read D18F0x114 (DramLimitAddress). */
113670aeb807SYazen Ghannam 	if (df_indirect_read_instance(nid, 0, 0x114 + (8 * base), umc, &ctx.tmp))
11370b746e8cSYazen Ghannam 		goto out_err;
11380b746e8cSYazen Ghannam 
113970aeb807SYazen Ghannam 	intlv_num_sockets = (ctx.tmp >> 8) & 0x1;
114070aeb807SYazen Ghannam 	intlv_num_dies	  = (ctx.tmp >> 10) & 0x3;
114170aeb807SYazen Ghannam 	dram_limit_addr	  = ((ctx.tmp & GENMASK_ULL(31, 12)) << 16) | GENMASK_ULL(27, 0);
11420b746e8cSYazen Ghannam 
11430b746e8cSYazen Ghannam 	intlv_addr_bit = intlv_addr_sel + 8;
11440b746e8cSYazen Ghannam 
11450b746e8cSYazen Ghannam 	/* Re-use intlv_num_chan by setting it equal to log2(#channels) */
11460b746e8cSYazen Ghannam 	switch (intlv_num_chan) {
11470b746e8cSYazen Ghannam 	case 0:	intlv_num_chan = 0; break;
11480b746e8cSYazen Ghannam 	case 1: intlv_num_chan = 1; break;
11490b746e8cSYazen Ghannam 	case 3: intlv_num_chan = 2; break;
11500b746e8cSYazen Ghannam 	case 5:	intlv_num_chan = 3; break;
11510b746e8cSYazen Ghannam 	case 7:	intlv_num_chan = 4; break;
11520b746e8cSYazen Ghannam 
11530b746e8cSYazen Ghannam 	case 8: intlv_num_chan = 1;
11540b746e8cSYazen Ghannam 		hash_enabled = true;
11550b746e8cSYazen Ghannam 		break;
11560b746e8cSYazen Ghannam 	default:
11570b746e8cSYazen Ghannam 		pr_err("%s: Invalid number of interleaved channels %d.\n",
11580b746e8cSYazen Ghannam 			__func__, intlv_num_chan);
11590b746e8cSYazen Ghannam 		goto out_err;
11600b746e8cSYazen Ghannam 	}
11610b746e8cSYazen Ghannam 
11620b746e8cSYazen Ghannam 	num_intlv_bits = intlv_num_chan;
11630b746e8cSYazen Ghannam 
11640b746e8cSYazen Ghannam 	if (intlv_num_dies > 2) {
11650b746e8cSYazen Ghannam 		pr_err("%s: Invalid number of interleaved nodes/dies %d.\n",
11660b746e8cSYazen Ghannam 			__func__, intlv_num_dies);
11670b746e8cSYazen Ghannam 		goto out_err;
11680b746e8cSYazen Ghannam 	}
11690b746e8cSYazen Ghannam 
11700b746e8cSYazen Ghannam 	num_intlv_bits += intlv_num_dies;
11710b746e8cSYazen Ghannam 
11720b746e8cSYazen Ghannam 	/* Add a bit if sockets are interleaved. */
11730b746e8cSYazen Ghannam 	num_intlv_bits += intlv_num_sockets;
11740b746e8cSYazen Ghannam 
11750b746e8cSYazen Ghannam 	/* Assert num_intlv_bits <= 4 */
11760b746e8cSYazen Ghannam 	if (num_intlv_bits > 4) {
11770b746e8cSYazen Ghannam 		pr_err("%s: Invalid interleave bits %d.\n",
11780b746e8cSYazen Ghannam 			__func__, num_intlv_bits);
11790b746e8cSYazen Ghannam 		goto out_err;
11800b746e8cSYazen Ghannam 	}
11810b746e8cSYazen Ghannam 
11820b746e8cSYazen Ghannam 	if (num_intlv_bits > 0) {
11830b746e8cSYazen Ghannam 		u64 temp_addr_x, temp_addr_i, temp_addr_y;
11840b746e8cSYazen Ghannam 		u8 die_id_bit, sock_id_bit, cs_fabric_id;
11850b746e8cSYazen Ghannam 
11860b746e8cSYazen Ghannam 		/*
11870b746e8cSYazen Ghannam 		 * Read FabricBlockInstanceInformation3_CS[BlockFabricID].
11880b746e8cSYazen Ghannam 		 * This is the fabric id for this coherent slave. Use
11890b746e8cSYazen Ghannam 		 * umc/channel# as instance id of the coherent slave
11900b746e8cSYazen Ghannam 		 * for FICAA.
11910b746e8cSYazen Ghannam 		 */
119270aeb807SYazen Ghannam 		if (df_indirect_read_instance(nid, 0, 0x50, umc, &ctx.tmp))
11930b746e8cSYazen Ghannam 			goto out_err;
11940b746e8cSYazen Ghannam 
119570aeb807SYazen Ghannam 		cs_fabric_id = (ctx.tmp >> 8) & 0xFF;
11960b746e8cSYazen Ghannam 		die_id_bit   = 0;
11970b746e8cSYazen Ghannam 
11980b746e8cSYazen Ghannam 		/* If interleaved over more than 1 channel: */
11990b746e8cSYazen Ghannam 		if (intlv_num_chan) {
12000b746e8cSYazen Ghannam 			die_id_bit = intlv_num_chan;
12010b746e8cSYazen Ghannam 			cs_mask	   = (1 << die_id_bit) - 1;
12020b746e8cSYazen Ghannam 			cs_id	   = cs_fabric_id & cs_mask;
12030b746e8cSYazen Ghannam 		}
12040b746e8cSYazen Ghannam 
12050b746e8cSYazen Ghannam 		sock_id_bit = die_id_bit;
12060b746e8cSYazen Ghannam 
12070b746e8cSYazen Ghannam 		/* Read D18F1x208 (SystemFabricIdMask). */
12080b746e8cSYazen Ghannam 		if (intlv_num_dies || intlv_num_sockets)
120970aeb807SYazen Ghannam 			if (df_indirect_read_broadcast(nid, 1, 0x208, &ctx.tmp))
12100b746e8cSYazen Ghannam 				goto out_err;
12110b746e8cSYazen Ghannam 
12120b746e8cSYazen Ghannam 		/* If interleaved over more than 1 die. */
12130b746e8cSYazen Ghannam 		if (intlv_num_dies) {
12140b746e8cSYazen Ghannam 			sock_id_bit  = die_id_bit + intlv_num_dies;
121570aeb807SYazen Ghannam 			die_id_shift = (ctx.tmp >> 24) & 0xF;
121670aeb807SYazen Ghannam 			die_id_mask  = (ctx.tmp >> 8) & 0xFF;
12170b746e8cSYazen Ghannam 
12180b746e8cSYazen Ghannam 			cs_id |= ((cs_fabric_id & die_id_mask) >> die_id_shift) << die_id_bit;
12190b746e8cSYazen Ghannam 		}
12200b746e8cSYazen Ghannam 
12210b746e8cSYazen Ghannam 		/* If interleaved over more than 1 socket. */
12220b746e8cSYazen Ghannam 		if (intlv_num_sockets) {
122370aeb807SYazen Ghannam 			socket_id_shift	= (ctx.tmp >> 28) & 0xF;
122470aeb807SYazen Ghannam 			socket_id_mask	= (ctx.tmp >> 16) & 0xFF;
12250b746e8cSYazen Ghannam 
12260b746e8cSYazen Ghannam 			cs_id |= ((cs_fabric_id & socket_id_mask) >> socket_id_shift) << sock_id_bit;
12270b746e8cSYazen Ghannam 		}
12280b746e8cSYazen Ghannam 
12290b746e8cSYazen Ghannam 		/*
12300b746e8cSYazen Ghannam 		 * The pre-interleaved address consists of XXXXXXIIIYYYYY
12310b746e8cSYazen Ghannam 		 * where III is the ID for this CS, and XXXXXXYYYYY are the
12320b746e8cSYazen Ghannam 		 * address bits from the post-interleaved address.
12330b746e8cSYazen Ghannam 		 * "num_intlv_bits" has been calculated to tell us how many "I"
12340b746e8cSYazen Ghannam 		 * bits there are. "intlv_addr_bit" tells us how many "Y" bits
12350b746e8cSYazen Ghannam 		 * there are (where "I" starts).
12360b746e8cSYazen Ghannam 		 */
123770aeb807SYazen Ghannam 		temp_addr_y = ctx.ret_addr & GENMASK_ULL(intlv_addr_bit - 1, 0);
12380b746e8cSYazen Ghannam 		temp_addr_i = (cs_id << intlv_addr_bit);
123970aeb807SYazen Ghannam 		temp_addr_x = (ctx.ret_addr & GENMASK_ULL(63, intlv_addr_bit)) << num_intlv_bits;
124070aeb807SYazen Ghannam 		ctx.ret_addr    = temp_addr_x | temp_addr_i | temp_addr_y;
12410b746e8cSYazen Ghannam 	}
12420b746e8cSYazen Ghannam 
12430b746e8cSYazen Ghannam 	/* Add dram base address */
124470aeb807SYazen Ghannam 	ctx.ret_addr += dram_base_addr;
12450b746e8cSYazen Ghannam 
12460b746e8cSYazen Ghannam 	/* If legacy MMIO hole enabled */
12470b746e8cSYazen Ghannam 	if (lgcy_mmio_hole_en) {
124870aeb807SYazen Ghannam 		if (df_indirect_read_broadcast(nid, 0, 0x104, &ctx.tmp))
12490b746e8cSYazen Ghannam 			goto out_err;
12500b746e8cSYazen Ghannam 
125170aeb807SYazen Ghannam 		dram_hole_base = ctx.tmp & GENMASK(31, 24);
125270aeb807SYazen Ghannam 		if (ctx.ret_addr >= dram_hole_base)
125370aeb807SYazen Ghannam 			ctx.ret_addr += (BIT_ULL(32) - dram_hole_base);
12540b746e8cSYazen Ghannam 	}
12550b746e8cSYazen Ghannam 
12560b746e8cSYazen Ghannam 	if (hash_enabled) {
12570b746e8cSYazen Ghannam 		/* Save some parentheses and grab ls-bit at the end. */
125870aeb807SYazen Ghannam 		hashed_bit =	(ctx.ret_addr >> 12) ^
125970aeb807SYazen Ghannam 				(ctx.ret_addr >> 18) ^
126070aeb807SYazen Ghannam 				(ctx.ret_addr >> 21) ^
126170aeb807SYazen Ghannam 				(ctx.ret_addr >> 30) ^
12620b746e8cSYazen Ghannam 				cs_id;
12630b746e8cSYazen Ghannam 
12640b746e8cSYazen Ghannam 		hashed_bit &= BIT(0);
12650b746e8cSYazen Ghannam 
126670aeb807SYazen Ghannam 		if (hashed_bit != ((ctx.ret_addr >> intlv_addr_bit) & BIT(0)))
126770aeb807SYazen Ghannam 			ctx.ret_addr ^= BIT(intlv_addr_bit);
12680b746e8cSYazen Ghannam 	}
12690b746e8cSYazen Ghannam 
12700b746e8cSYazen Ghannam 	/* Is calculated system address is above DRAM limit address? */
127170aeb807SYazen Ghannam 	if (ctx.ret_addr > dram_limit_addr)
12720b746e8cSYazen Ghannam 		goto out_err;
12730b746e8cSYazen Ghannam 
127470aeb807SYazen Ghannam 	*sys_addr = ctx.ret_addr;
12750b746e8cSYazen Ghannam 	return 0;
12760b746e8cSYazen Ghannam 
12770b746e8cSYazen Ghannam out_err:
12780b746e8cSYazen Ghannam 	return -EINVAL;
12790b746e8cSYazen Ghannam }
12800b746e8cSYazen Ghannam 
1281bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
12822da11654SDoug Thompson 
12832da11654SDoug Thompson /*
12842da11654SDoug Thompson  * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
12852da11654SDoug Thompson  * are ECC capable.
12862da11654SDoug Thompson  */
1287d1ea71cdSBorislav Petkov static unsigned long determine_edac_cap(struct amd64_pvt *pvt)
12882da11654SDoug Thompson {
12891f6189edSDan Carpenter 	unsigned long edac_cap = EDAC_FLAG_NONE;
1290d27f3a34SYazen Ghannam 	u8 bit;
12912da11654SDoug Thompson 
1292d27f3a34SYazen Ghannam 	if (pvt->umc) {
1293d27f3a34SYazen Ghannam 		u8 i, umc_en_mask = 0, dimm_ecc_en_mask = 0;
1294d27f3a34SYazen Ghannam 
12954d30d2bcSYazen Ghannam 		for_each_umc(i) {
1296d27f3a34SYazen Ghannam 			if (!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT))
1297d27f3a34SYazen Ghannam 				continue;
1298d27f3a34SYazen Ghannam 
1299d27f3a34SYazen Ghannam 			umc_en_mask |= BIT(i);
1300d27f3a34SYazen Ghannam 
1301d27f3a34SYazen Ghannam 			/* UMC Configuration bit 12 (DimmEccEn) */
1302d27f3a34SYazen Ghannam 			if (pvt->umc[i].umc_cfg & BIT(12))
1303d27f3a34SYazen Ghannam 				dimm_ecc_en_mask |= BIT(i);
1304d27f3a34SYazen Ghannam 		}
1305d27f3a34SYazen Ghannam 
1306d27f3a34SYazen Ghannam 		if (umc_en_mask == dimm_ecc_en_mask)
1307d27f3a34SYazen Ghannam 			edac_cap = EDAC_FLAG_SECDED;
1308d27f3a34SYazen Ghannam 	} else {
1309a4b4bedcSBorislav Petkov 		bit = (pvt->fam > 0xf || pvt->ext_model >= K8_REV_F)
13102da11654SDoug Thompson 			? 19
13112da11654SDoug Thompson 			: 17;
13122da11654SDoug Thompson 
1313584fcff4SBorislav Petkov 		if (pvt->dclr0 & BIT(bit))
13142da11654SDoug Thompson 			edac_cap = EDAC_FLAG_SECDED;
1315d27f3a34SYazen Ghannam 	}
13162da11654SDoug Thompson 
13172da11654SDoug Thompson 	return edac_cap;
13182da11654SDoug Thompson }
13192da11654SDoug Thompson 
1320d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *, u8);
13212da11654SDoug Thompson 
1322d1ea71cdSBorislav Petkov static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan)
132368798e17SBorislav Petkov {
1324956b9ba1SJoe Perches 	edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
132568798e17SBorislav Petkov 
1326a597d2a5SAravind Gopalakrishnan 	if (pvt->dram_type == MEM_LRDDR3) {
1327a597d2a5SAravind Gopalakrishnan 		u32 dcsm = pvt->csels[chan].csmasks[0];
1328a597d2a5SAravind Gopalakrishnan 		/*
1329a597d2a5SAravind Gopalakrishnan 		 * It's assumed all LRDIMMs in a DCT are going to be of
1330a597d2a5SAravind Gopalakrishnan 		 * same 'type' until proven otherwise. So, use a cs
1331a597d2a5SAravind Gopalakrishnan 		 * value of '0' here to get dcsm value.
1332a597d2a5SAravind Gopalakrishnan 		 */
1333a597d2a5SAravind Gopalakrishnan 		edac_dbg(1, " LRDIMM %dx rank multiply\n", (dcsm & 0x3));
1334a597d2a5SAravind Gopalakrishnan 	}
1335a597d2a5SAravind Gopalakrishnan 
1336a597d2a5SAravind Gopalakrishnan 	edac_dbg(1, "All DIMMs support ECC:%s\n",
133768798e17SBorislav Petkov 		    (dclr & BIT(19)) ? "yes" : "no");
133868798e17SBorislav Petkov 
1339a597d2a5SAravind Gopalakrishnan 
1340956b9ba1SJoe Perches 	edac_dbg(1, "  PAR/ERR parity: %s\n",
134168798e17SBorislav Petkov 		 (dclr & BIT(8)) ?  "enabled" : "disabled");
134268798e17SBorislav Petkov 
1343a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10)
1344956b9ba1SJoe Perches 		edac_dbg(1, "  DCT 128bit mode width: %s\n",
134568798e17SBorislav Petkov 			 (dclr & BIT(11)) ?  "128b" : "64b");
134668798e17SBorislav Petkov 
1347956b9ba1SJoe Perches 	edac_dbg(1, "  x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
134868798e17SBorislav Petkov 		 (dclr & BIT(12)) ?  "yes" : "no",
134968798e17SBorislav Petkov 		 (dclr & BIT(13)) ?  "yes" : "no",
135068798e17SBorislav Petkov 		 (dclr & BIT(14)) ?  "yes" : "no",
135168798e17SBorislav Petkov 		 (dclr & BIT(15)) ?  "yes" : "no");
135268798e17SBorislav Petkov }
135368798e17SBorislav Petkov 
1354e53a3b26SYazen Ghannam #define CS_EVEN_PRIMARY		BIT(0)
1355e53a3b26SYazen Ghannam #define CS_ODD_PRIMARY		BIT(1)
135681f5090dSYazen Ghannam #define CS_EVEN_SECONDARY	BIT(2)
135781f5090dSYazen Ghannam #define CS_ODD_SECONDARY	BIT(3)
13589f4873fbSYazen Ghannam #define CS_3R_INTERLEAVE	BIT(4)
1359e53a3b26SYazen Ghannam 
136081f5090dSYazen Ghannam #define CS_EVEN			(CS_EVEN_PRIMARY | CS_EVEN_SECONDARY)
136181f5090dSYazen Ghannam #define CS_ODD			(CS_ODD_PRIMARY | CS_ODD_SECONDARY)
1362e53a3b26SYazen Ghannam 
1363e53a3b26SYazen Ghannam static int f17_get_cs_mode(int dimm, u8 ctrl, struct amd64_pvt *pvt)
1364fc00c6a4SYazen Ghannam {
13659f4873fbSYazen Ghannam 	u8 base, count = 0;
1366e53a3b26SYazen Ghannam 	int cs_mode = 0;
1367fc00c6a4SYazen Ghannam 
1368e53a3b26SYazen Ghannam 	if (csrow_enabled(2 * dimm, ctrl, pvt))
1369e53a3b26SYazen Ghannam 		cs_mode |= CS_EVEN_PRIMARY;
1370fc00c6a4SYazen Ghannam 
1371e53a3b26SYazen Ghannam 	if (csrow_enabled(2 * dimm + 1, ctrl, pvt))
1372e53a3b26SYazen Ghannam 		cs_mode |= CS_ODD_PRIMARY;
1373e53a3b26SYazen Ghannam 
137481f5090dSYazen Ghannam 	/* Asymmetric dual-rank DIMM support. */
137581f5090dSYazen Ghannam 	if (csrow_sec_enabled(2 * dimm + 1, ctrl, pvt))
137681f5090dSYazen Ghannam 		cs_mode |= CS_ODD_SECONDARY;
137781f5090dSYazen Ghannam 
13789f4873fbSYazen Ghannam 	/*
13799f4873fbSYazen Ghannam 	 * 3 Rank inteleaving support.
13809f4873fbSYazen Ghannam 	 * There should be only three bases enabled and their two masks should
13819f4873fbSYazen Ghannam 	 * be equal.
13829f4873fbSYazen Ghannam 	 */
13839f4873fbSYazen Ghannam 	for_each_chip_select(base, ctrl, pvt)
13849f4873fbSYazen Ghannam 		count += csrow_enabled(base, ctrl, pvt);
13859f4873fbSYazen Ghannam 
13869f4873fbSYazen Ghannam 	if (count == 3 &&
13879f4873fbSYazen Ghannam 	    pvt->csels[ctrl].csmasks[0] == pvt->csels[ctrl].csmasks[1]) {
13889f4873fbSYazen Ghannam 		edac_dbg(1, "3R interleaving in use.\n");
13899f4873fbSYazen Ghannam 		cs_mode |= CS_3R_INTERLEAVE;
13909f4873fbSYazen Ghannam 	}
13919f4873fbSYazen Ghannam 
1392e53a3b26SYazen Ghannam 	return cs_mode;
1393fc00c6a4SYazen Ghannam }
1394fc00c6a4SYazen Ghannam 
139507ed82efSYazen Ghannam static void debug_display_dimm_sizes_df(struct amd64_pvt *pvt, u8 ctrl)
139607ed82efSYazen Ghannam {
1397e53a3b26SYazen Ghannam 	int dimm, size0, size1, cs0, cs1, cs_mode;
139807ed82efSYazen Ghannam 
139907ed82efSYazen Ghannam 	edac_printk(KERN_DEBUG, EDAC_MC, "UMC%d chip selects:\n", ctrl);
140007ed82efSYazen Ghannam 
1401d971e28eSYazen Ghannam 	for (dimm = 0; dimm < 2; dimm++) {
1402eb77e6b8SYazen Ghannam 		cs0 = dimm * 2;
1403eb77e6b8SYazen Ghannam 		cs1 = dimm * 2 + 1;
1404eb77e6b8SYazen Ghannam 
1405e53a3b26SYazen Ghannam 		cs_mode = f17_get_cs_mode(dimm, ctrl, pvt);
1406e53a3b26SYazen Ghannam 
1407e53a3b26SYazen Ghannam 		size0 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs0);
1408e53a3b26SYazen Ghannam 		size1 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs1);
140907ed82efSYazen Ghannam 
141007ed82efSYazen Ghannam 		amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
1411eb77e6b8SYazen Ghannam 				cs0,	size0,
1412eb77e6b8SYazen Ghannam 				cs1,	size1);
141307ed82efSYazen Ghannam 	}
141407ed82efSYazen Ghannam }
141507ed82efSYazen Ghannam 
141607ed82efSYazen Ghannam static void __dump_misc_regs_df(struct amd64_pvt *pvt)
141707ed82efSYazen Ghannam {
141807ed82efSYazen Ghannam 	struct amd64_umc *umc;
141907ed82efSYazen Ghannam 	u32 i, tmp, umc_base;
142007ed82efSYazen Ghannam 
14214d30d2bcSYazen Ghannam 	for_each_umc(i) {
142207ed82efSYazen Ghannam 		umc_base = get_umc_base(i);
142307ed82efSYazen Ghannam 		umc = &pvt->umc[i];
142407ed82efSYazen Ghannam 
142507ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d DIMM cfg: 0x%x\n", i, umc->dimm_cfg);
142607ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d UMC cfg: 0x%x\n", i, umc->umc_cfg);
142707ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d SDP ctrl: 0x%x\n", i, umc->sdp_ctrl);
142807ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d ECC ctrl: 0x%x\n", i, umc->ecc_ctrl);
142907ed82efSYazen Ghannam 
143007ed82efSYazen Ghannam 		amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ECC_BAD_SYMBOL, &tmp);
143107ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d ECC bad symbol: 0x%x\n", i, tmp);
143207ed82efSYazen Ghannam 
143307ed82efSYazen Ghannam 		amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_UMC_CAP, &tmp);
143407ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d UMC cap: 0x%x\n", i, tmp);
143507ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d UMC cap high: 0x%x\n", i, umc->umc_cap_hi);
143607ed82efSYazen Ghannam 
143707ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d ECC capable: %s, ChipKill ECC capable: %s\n",
143807ed82efSYazen Ghannam 				i, (umc->umc_cap_hi & BIT(30)) ? "yes" : "no",
143907ed82efSYazen Ghannam 				    (umc->umc_cap_hi & BIT(31)) ? "yes" : "no");
144007ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d All DIMMs support ECC: %s\n",
144107ed82efSYazen Ghannam 				i, (umc->umc_cfg & BIT(12)) ? "yes" : "no");
144207ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d x4 DIMMs present: %s\n",
144307ed82efSYazen Ghannam 				i, (umc->dimm_cfg & BIT(6)) ? "yes" : "no");
144407ed82efSYazen Ghannam 		edac_dbg(1, "UMC%d x16 DIMMs present: %s\n",
144507ed82efSYazen Ghannam 				i, (umc->dimm_cfg & BIT(7)) ? "yes" : "no");
144607ed82efSYazen Ghannam 
1447*2151c84eSYazen Ghannam 		if (umc->dram_type == MEM_LRDDR4 || umc->dram_type == MEM_LRDDR5) {
1448*2151c84eSYazen Ghannam 			amd_smn_read(pvt->mc_node_id,
1449*2151c84eSYazen Ghannam 				     umc_base + get_umc_reg(UMCCH_ADDR_CFG),
1450*2151c84eSYazen Ghannam 				     &tmp);
145107ed82efSYazen Ghannam 			edac_dbg(1, "UMC%d LRDIMM %dx rank multiply\n",
145207ed82efSYazen Ghannam 					i, 1 << ((tmp >> 4) & 0x3));
145307ed82efSYazen Ghannam 		}
145407ed82efSYazen Ghannam 
145507ed82efSYazen Ghannam 		debug_display_dimm_sizes_df(pvt, i);
145607ed82efSYazen Ghannam 	}
145707ed82efSYazen Ghannam 
145807ed82efSYazen Ghannam 	edac_dbg(1, "F0x104 (DRAM Hole Address): 0x%08x, base: 0x%08x\n",
145907ed82efSYazen Ghannam 		 pvt->dhar, dhar_base(pvt));
146007ed82efSYazen Ghannam }
146107ed82efSYazen Ghannam 
14622da11654SDoug Thompson /* Display and decode various NB registers for debug purposes. */
146307ed82efSYazen Ghannam static void __dump_misc_regs(struct amd64_pvt *pvt)
14642da11654SDoug Thompson {
1465956b9ba1SJoe Perches 	edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
14662da11654SDoug Thompson 
1467956b9ba1SJoe Perches 	edac_dbg(1, "  NB two channel DRAM capable: %s\n",
14685980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
146968798e17SBorislav Petkov 
1470956b9ba1SJoe Perches 	edac_dbg(1, "  ECC capable: %s, ChipKill ECC capable: %s\n",
14715980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
14725980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
147368798e17SBorislav Petkov 
1474d1ea71cdSBorislav Petkov 	debug_dump_dramcfg_low(pvt, pvt->dclr0, 0);
14752da11654SDoug Thompson 
1476956b9ba1SJoe Perches 	edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
14772da11654SDoug Thompson 
1478956b9ba1SJoe Perches 	edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n",
1479bc21fa57SBorislav Petkov 		 pvt->dhar, dhar_base(pvt),
1480a4b4bedcSBorislav Petkov 		 (pvt->fam == 0xf) ? k8_dhar_offset(pvt)
1481bc21fa57SBorislav Petkov 				   : f10_dhar_offset(pvt));
14822da11654SDoug Thompson 
1483d1ea71cdSBorislav Petkov 	debug_display_dimm_sizes(pvt, 0);
14844d796364SBorislav Petkov 
14854d796364SBorislav Petkov 	/* everything below this point is Fam10h and above */
1486a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
14872da11654SDoug Thompson 		return;
14884d796364SBorislav Petkov 
1489d1ea71cdSBorislav Petkov 	debug_display_dimm_sizes(pvt, 1);
14902da11654SDoug Thompson 
14918de1d91eSBorislav Petkov 	/* Only if NOT ganged does dclr1 have valid info */
149268798e17SBorislav Petkov 	if (!dct_ganging_enabled(pvt))
1493d1ea71cdSBorislav Petkov 		debug_dump_dramcfg_low(pvt, pvt->dclr1, 1);
14942da11654SDoug Thompson }
14952da11654SDoug Thompson 
149607ed82efSYazen Ghannam /* Display and decode various NB registers for debug purposes. */
149707ed82efSYazen Ghannam static void dump_misc_regs(struct amd64_pvt *pvt)
149807ed82efSYazen Ghannam {
149907ed82efSYazen Ghannam 	if (pvt->umc)
150007ed82efSYazen Ghannam 		__dump_misc_regs_df(pvt);
150107ed82efSYazen Ghannam 	else
150207ed82efSYazen Ghannam 		__dump_misc_regs(pvt);
150307ed82efSYazen Ghannam 
150407ed82efSYazen Ghannam 	edac_dbg(1, "  DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
150507ed82efSYazen Ghannam 
15067835961dSYazen Ghannam 	amd64_info("using x%u syndromes.\n", pvt->ecc_sym_sz);
150707ed82efSYazen Ghannam }
150807ed82efSYazen Ghannam 
150994be4bffSDoug Thompson /*
151018b94f66SAravind Gopalakrishnan  * See BKDG, F2x[1,0][5C:40], F2[1,0][6C:60]
151194be4bffSDoug Thompson  */
151211c75eadSBorislav Petkov static void prep_chip_selects(struct amd64_pvt *pvt)
151394be4bffSDoug Thompson {
151418b94f66SAravind Gopalakrishnan 	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
151511c75eadSBorislav Petkov 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
151611c75eadSBorislav Petkov 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8;
1517a597d2a5SAravind Gopalakrishnan 	} else if (pvt->fam == 0x15 && pvt->model == 0x30) {
151818b94f66SAravind Gopalakrishnan 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4;
151918b94f66SAravind Gopalakrishnan 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2;
1520d971e28eSYazen Ghannam 	} else if (pvt->fam >= 0x17) {
1521d971e28eSYazen Ghannam 		int umc;
1522d971e28eSYazen Ghannam 
1523d971e28eSYazen Ghannam 		for_each_umc(umc) {
1524d971e28eSYazen Ghannam 			pvt->csels[umc].b_cnt = 4;
1525*2151c84eSYazen Ghannam 			pvt->csels[umc].m_cnt = fam_type->flags.zn_regs_v2 ? 4 : 2;
1526d971e28eSYazen Ghannam 		}
1527d971e28eSYazen Ghannam 
15289d858bb1SBorislav Petkov 	} else {
152911c75eadSBorislav Petkov 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
153011c75eadSBorislav Petkov 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4;
15319d858bb1SBorislav Petkov 	}
153294be4bffSDoug Thompson }
153394be4bffSDoug Thompson 
1534d971e28eSYazen Ghannam static void read_umc_base_mask(struct amd64_pvt *pvt)
1535d971e28eSYazen Ghannam {
15367574729eSYazen Ghannam 	u32 umc_base_reg, umc_base_reg_sec;
15377574729eSYazen Ghannam 	u32 umc_mask_reg, umc_mask_reg_sec;
15387574729eSYazen Ghannam 	u32 base_reg, base_reg_sec;
15397574729eSYazen Ghannam 	u32 mask_reg, mask_reg_sec;
15407574729eSYazen Ghannam 	u32 *base, *base_sec;
15417574729eSYazen Ghannam 	u32 *mask, *mask_sec;
1542d971e28eSYazen Ghannam 	int cs, umc;
1543d971e28eSYazen Ghannam 
1544d971e28eSYazen Ghannam 	for_each_umc(umc) {
1545d971e28eSYazen Ghannam 		umc_base_reg = get_umc_base(umc) + UMCCH_BASE_ADDR;
15467574729eSYazen Ghannam 		umc_base_reg_sec = get_umc_base(umc) + UMCCH_BASE_ADDR_SEC;
1547d971e28eSYazen Ghannam 
1548d971e28eSYazen Ghannam 		for_each_chip_select(cs, umc, pvt) {
1549d971e28eSYazen Ghannam 			base = &pvt->csels[umc].csbases[cs];
15507574729eSYazen Ghannam 			base_sec = &pvt->csels[umc].csbases_sec[cs];
1551d971e28eSYazen Ghannam 
1552d971e28eSYazen Ghannam 			base_reg = umc_base_reg + (cs * 4);
15537574729eSYazen Ghannam 			base_reg_sec = umc_base_reg_sec + (cs * 4);
1554d971e28eSYazen Ghannam 
1555d971e28eSYazen Ghannam 			if (!amd_smn_read(pvt->mc_node_id, base_reg, base))
1556d971e28eSYazen Ghannam 				edac_dbg(0, "  DCSB%d[%d]=0x%08x reg: 0x%x\n",
1557d971e28eSYazen Ghannam 					 umc, cs, *base, base_reg);
15587574729eSYazen Ghannam 
15597574729eSYazen Ghannam 			if (!amd_smn_read(pvt->mc_node_id, base_reg_sec, base_sec))
15607574729eSYazen Ghannam 				edac_dbg(0, "    DCSB_SEC%d[%d]=0x%08x reg: 0x%x\n",
15617574729eSYazen Ghannam 					 umc, cs, *base_sec, base_reg_sec);
1562d971e28eSYazen Ghannam 		}
1563d971e28eSYazen Ghannam 
1564d971e28eSYazen Ghannam 		umc_mask_reg = get_umc_base(umc) + UMCCH_ADDR_MASK;
1565*2151c84eSYazen Ghannam 		umc_mask_reg_sec = get_umc_base(umc) + get_umc_reg(UMCCH_ADDR_MASK_SEC);
1566d971e28eSYazen Ghannam 
1567d971e28eSYazen Ghannam 		for_each_chip_select_mask(cs, umc, pvt) {
1568d971e28eSYazen Ghannam 			mask = &pvt->csels[umc].csmasks[cs];
15697574729eSYazen Ghannam 			mask_sec = &pvt->csels[umc].csmasks_sec[cs];
1570d971e28eSYazen Ghannam 
1571d971e28eSYazen Ghannam 			mask_reg = umc_mask_reg + (cs * 4);
15727574729eSYazen Ghannam 			mask_reg_sec = umc_mask_reg_sec + (cs * 4);
1573d971e28eSYazen Ghannam 
1574d971e28eSYazen Ghannam 			if (!amd_smn_read(pvt->mc_node_id, mask_reg, mask))
1575d971e28eSYazen Ghannam 				edac_dbg(0, "  DCSM%d[%d]=0x%08x reg: 0x%x\n",
1576d971e28eSYazen Ghannam 					 umc, cs, *mask, mask_reg);
15777574729eSYazen Ghannam 
15787574729eSYazen Ghannam 			if (!amd_smn_read(pvt->mc_node_id, mask_reg_sec, mask_sec))
15797574729eSYazen Ghannam 				edac_dbg(0, "    DCSM_SEC%d[%d]=0x%08x reg: 0x%x\n",
15807574729eSYazen Ghannam 					 umc, cs, *mask_sec, mask_reg_sec);
1581d971e28eSYazen Ghannam 		}
1582d971e28eSYazen Ghannam 	}
1583d971e28eSYazen Ghannam }
1584d971e28eSYazen Ghannam 
158594be4bffSDoug Thompson /*
158611c75eadSBorislav Petkov  * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers
158794be4bffSDoug Thompson  */
1588b2b0c605SBorislav Petkov static void read_dct_base_mask(struct amd64_pvt *pvt)
158994be4bffSDoug Thompson {
1590d971e28eSYazen Ghannam 	int cs;
159194be4bffSDoug Thompson 
159211c75eadSBorislav Petkov 	prep_chip_selects(pvt);
159394be4bffSDoug Thompson 
1594d971e28eSYazen Ghannam 	if (pvt->umc)
1595d971e28eSYazen Ghannam 		return read_umc_base_mask(pvt);
1596b64ce7cdSYazen Ghannam 
159711c75eadSBorislav Petkov 	for_each_chip_select(cs, 0, pvt) {
1598d971e28eSYazen Ghannam 		int reg0   = DCSB0 + (cs * 4);
1599d971e28eSYazen Ghannam 		int reg1   = DCSB1 + (cs * 4);
160011c75eadSBorislav Petkov 		u32 *base0 = &pvt->csels[0].csbases[cs];
160111c75eadSBorislav Petkov 		u32 *base1 = &pvt->csels[1].csbases[cs];
1602b2b0c605SBorislav Petkov 
16037981a28fSAravind Gopalakrishnan 		if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0))
1604956b9ba1SJoe Perches 			edac_dbg(0, "  DCSB0[%d]=0x%08x reg: F2x%x\n",
160511c75eadSBorislav Petkov 				 cs, *base0, reg0);
160694be4bffSDoug Thompson 
16077981a28fSAravind Gopalakrishnan 		if (pvt->fam == 0xf)
160811c75eadSBorislav Petkov 			continue;
1609b2b0c605SBorislav Petkov 
16107981a28fSAravind Gopalakrishnan 		if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1))
1611956b9ba1SJoe Perches 			edac_dbg(0, "  DCSB1[%d]=0x%08x reg: F2x%x\n",
16127981a28fSAravind Gopalakrishnan 				 cs, *base1, (pvt->fam == 0x10) ? reg1
16137981a28fSAravind Gopalakrishnan 							: reg0);
161494be4bffSDoug Thompson 	}
161594be4bffSDoug Thompson 
161611c75eadSBorislav Petkov 	for_each_chip_select_mask(cs, 0, pvt) {
1617d971e28eSYazen Ghannam 		int reg0   = DCSM0 + (cs * 4);
1618d971e28eSYazen Ghannam 		int reg1   = DCSM1 + (cs * 4);
161911c75eadSBorislav Petkov 		u32 *mask0 = &pvt->csels[0].csmasks[cs];
162011c75eadSBorislav Petkov 		u32 *mask1 = &pvt->csels[1].csmasks[cs];
1621b2b0c605SBorislav Petkov 
16227981a28fSAravind Gopalakrishnan 		if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0))
1623956b9ba1SJoe Perches 			edac_dbg(0, "    DCSM0[%d]=0x%08x reg: F2x%x\n",
162411c75eadSBorislav Petkov 				 cs, *mask0, reg0);
162594be4bffSDoug Thompson 
16267981a28fSAravind Gopalakrishnan 		if (pvt->fam == 0xf)
162711c75eadSBorislav Petkov 			continue;
1628b2b0c605SBorislav Petkov 
16297981a28fSAravind Gopalakrishnan 		if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1))
1630956b9ba1SJoe Perches 			edac_dbg(0, "    DCSM1[%d]=0x%08x reg: F2x%x\n",
16317981a28fSAravind Gopalakrishnan 				 cs, *mask1, (pvt->fam == 0x10) ? reg1
16327981a28fSAravind Gopalakrishnan 							: reg0);
163394be4bffSDoug Thompson 	}
16346ba5dcdcSBorislav Petkov }
163594be4bffSDoug Thompson 
163675aeaaf2SYazen Ghannam static void determine_memory_type_df(struct amd64_pvt *pvt)
163775aeaaf2SYazen Ghannam {
163875aeaaf2SYazen Ghannam 	struct amd64_umc *umc;
163975aeaaf2SYazen Ghannam 	u32 i;
164075aeaaf2SYazen Ghannam 
164175aeaaf2SYazen Ghannam 	for_each_umc(i) {
164275aeaaf2SYazen Ghannam 		umc = &pvt->umc[i];
164375aeaaf2SYazen Ghannam 
164475aeaaf2SYazen Ghannam 		if (!(umc->sdp_ctrl & UMC_SDP_INIT)) {
164575aeaaf2SYazen Ghannam 			umc->dram_type = MEM_EMPTY;
164675aeaaf2SYazen Ghannam 			continue;
164775aeaaf2SYazen Ghannam 		}
164875aeaaf2SYazen Ghannam 
1649*2151c84eSYazen Ghannam 		/*
1650*2151c84eSYazen Ghannam 		 * Check if the system supports the "DDR Type" field in UMC Config
1651*2151c84eSYazen Ghannam 		 * and has DDR5 DIMMs in use.
1652*2151c84eSYazen Ghannam 		 */
1653*2151c84eSYazen Ghannam 		if (fam_type->flags.zn_regs_v2 && ((umc->umc_cfg & GENMASK(2, 0)) == 0x1)) {
1654*2151c84eSYazen Ghannam 			if (umc->dimm_cfg & BIT(5))
1655*2151c84eSYazen Ghannam 				umc->dram_type = MEM_LRDDR5;
1656*2151c84eSYazen Ghannam 			else if (umc->dimm_cfg & BIT(4))
1657*2151c84eSYazen Ghannam 				umc->dram_type = MEM_RDDR5;
1658*2151c84eSYazen Ghannam 			else
1659*2151c84eSYazen Ghannam 				umc->dram_type = MEM_DDR5;
1660*2151c84eSYazen Ghannam 		} else {
166175aeaaf2SYazen Ghannam 			if (umc->dimm_cfg & BIT(5))
166275aeaaf2SYazen Ghannam 				umc->dram_type = MEM_LRDDR4;
166375aeaaf2SYazen Ghannam 			else if (umc->dimm_cfg & BIT(4))
166475aeaaf2SYazen Ghannam 				umc->dram_type = MEM_RDDR4;
166575aeaaf2SYazen Ghannam 			else
166675aeaaf2SYazen Ghannam 				umc->dram_type = MEM_DDR4;
1667*2151c84eSYazen Ghannam 		}
166875aeaaf2SYazen Ghannam 
166975aeaaf2SYazen Ghannam 		edac_dbg(1, "  UMC%d DIMM type: %s\n", i, edac_mem_types[umc->dram_type]);
167075aeaaf2SYazen Ghannam 	}
167175aeaaf2SYazen Ghannam }
167275aeaaf2SYazen Ghannam 
1673a597d2a5SAravind Gopalakrishnan static void determine_memory_type(struct amd64_pvt *pvt)
167494be4bffSDoug Thompson {
1675a597d2a5SAravind Gopalakrishnan 	u32 dram_ctrl, dcsm;
167694be4bffSDoug Thompson 
167775aeaaf2SYazen Ghannam 	if (pvt->umc)
167875aeaaf2SYazen Ghannam 		return determine_memory_type_df(pvt);
1679dcd01394SYazen Ghannam 
1680a597d2a5SAravind Gopalakrishnan 	switch (pvt->fam) {
1681a597d2a5SAravind Gopalakrishnan 	case 0xf:
1682a597d2a5SAravind Gopalakrishnan 		if (pvt->ext_model >= K8_REV_F)
1683a597d2a5SAravind Gopalakrishnan 			goto ddr3;
1684a597d2a5SAravind Gopalakrishnan 
1685a597d2a5SAravind Gopalakrishnan 		pvt->dram_type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
1686a597d2a5SAravind Gopalakrishnan 		return;
1687a597d2a5SAravind Gopalakrishnan 
1688a597d2a5SAravind Gopalakrishnan 	case 0x10:
16896b4c0bdeSBorislav Petkov 		if (pvt->dchr0 & DDR3_MODE)
1690a597d2a5SAravind Gopalakrishnan 			goto ddr3;
1691a597d2a5SAravind Gopalakrishnan 
1692a597d2a5SAravind Gopalakrishnan 		pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
1693a597d2a5SAravind Gopalakrishnan 		return;
1694a597d2a5SAravind Gopalakrishnan 
1695a597d2a5SAravind Gopalakrishnan 	case 0x15:
1696a597d2a5SAravind Gopalakrishnan 		if (pvt->model < 0x60)
1697a597d2a5SAravind Gopalakrishnan 			goto ddr3;
1698a597d2a5SAravind Gopalakrishnan 
1699a597d2a5SAravind Gopalakrishnan 		/*
1700a597d2a5SAravind Gopalakrishnan 		 * Model 0x60h needs special handling:
1701a597d2a5SAravind Gopalakrishnan 		 *
1702a597d2a5SAravind Gopalakrishnan 		 * We use a Chip Select value of '0' to obtain dcsm.
1703a597d2a5SAravind Gopalakrishnan 		 * Theoretically, it is possible to populate LRDIMMs of different
1704a597d2a5SAravind Gopalakrishnan 		 * 'Rank' value on a DCT. But this is not the common case. So,
1705a597d2a5SAravind Gopalakrishnan 		 * it's reasonable to assume all DIMMs are going to be of same
1706a597d2a5SAravind Gopalakrishnan 		 * 'type' until proven otherwise.
1707a597d2a5SAravind Gopalakrishnan 		 */
1708a597d2a5SAravind Gopalakrishnan 		amd64_read_dct_pci_cfg(pvt, 0, DRAM_CONTROL, &dram_ctrl);
1709a597d2a5SAravind Gopalakrishnan 		dcsm = pvt->csels[0].csmasks[0];
1710a597d2a5SAravind Gopalakrishnan 
1711a597d2a5SAravind Gopalakrishnan 		if (((dram_ctrl >> 8) & 0x7) == 0x2)
1712a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_DDR4;
1713a597d2a5SAravind Gopalakrishnan 		else if (pvt->dclr0 & BIT(16))
1714a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_DDR3;
1715a597d2a5SAravind Gopalakrishnan 		else if (dcsm & 0x3)
1716a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_LRDDR3;
17176b4c0bdeSBorislav Petkov 		else
1718a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_RDDR3;
1719a597d2a5SAravind Gopalakrishnan 
1720a597d2a5SAravind Gopalakrishnan 		return;
1721a597d2a5SAravind Gopalakrishnan 
1722a597d2a5SAravind Gopalakrishnan 	case 0x16:
1723a597d2a5SAravind Gopalakrishnan 		goto ddr3;
1724a597d2a5SAravind Gopalakrishnan 
1725a597d2a5SAravind Gopalakrishnan 	default:
1726a597d2a5SAravind Gopalakrishnan 		WARN(1, KERN_ERR "%s: Family??? 0x%x\n", __func__, pvt->fam);
1727a597d2a5SAravind Gopalakrishnan 		pvt->dram_type = MEM_EMPTY;
172894be4bffSDoug Thompson 	}
1729a597d2a5SAravind Gopalakrishnan 	return;
173094be4bffSDoug Thompson 
1731a597d2a5SAravind Gopalakrishnan ddr3:
1732a597d2a5SAravind Gopalakrishnan 	pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
173394be4bffSDoug Thompson }
173494be4bffSDoug Thompson 
1735cb328507SBorislav Petkov /* Get the number of DCT channels the memory controller is using. */
1736ddff876dSDoug Thompson static int k8_early_channel_count(struct amd64_pvt *pvt)
1737ddff876dSDoug Thompson {
1738cb328507SBorislav Petkov 	int flag;
1739ddff876dSDoug Thompson 
17409f56da0eSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F)
1741ddff876dSDoug Thompson 		/* RevF (NPT) and later */
174241d8bfabSBorislav Petkov 		flag = pvt->dclr0 & WIDTH_128;
17439f56da0eSBorislav Petkov 	else
1744ddff876dSDoug Thompson 		/* RevE and earlier */
1745ddff876dSDoug Thompson 		flag = pvt->dclr0 & REVE_WIDTH_128;
1746ddff876dSDoug Thompson 
1747ddff876dSDoug Thompson 	/* not used */
1748ddff876dSDoug Thompson 	pvt->dclr1 = 0;
1749ddff876dSDoug Thompson 
1750ddff876dSDoug Thompson 	return (flag) ? 2 : 1;
1751ddff876dSDoug Thompson }
1752ddff876dSDoug Thompson 
175370046624SBorislav Petkov /* On F10h and later ErrAddr is MC4_ADDR[47:1] */
1754a4b4bedcSBorislav Petkov static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m)
1755ddff876dSDoug Thompson {
1756db970bd2SYazen Ghannam 	u16 mce_nid = topology_die_id(m->extcpu);
17572ec591acSBorislav Petkov 	struct mem_ctl_info *mci;
175870046624SBorislav Petkov 	u8 start_bit = 1;
175970046624SBorislav Petkov 	u8 end_bit   = 47;
17602ec591acSBorislav Petkov 	u64 addr;
17612ec591acSBorislav Petkov 
17622ec591acSBorislav Petkov 	mci = edac_mc_find(mce_nid);
17632ec591acSBorislav Petkov 	if (!mci)
17642ec591acSBorislav Petkov 		return 0;
17652ec591acSBorislav Petkov 
17662ec591acSBorislav Petkov 	pvt = mci->pvt_info;
176770046624SBorislav Petkov 
1768a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf) {
176970046624SBorislav Petkov 		start_bit = 3;
177070046624SBorislav Petkov 		end_bit   = 39;
177170046624SBorislav Petkov 	}
177270046624SBorislav Petkov 
177310ef6b0dSChen, Gong 	addr = m->addr & GENMASK_ULL(end_bit, start_bit);
1774c1ae6830SBorislav Petkov 
1775c1ae6830SBorislav Petkov 	/*
1776c1ae6830SBorislav Petkov 	 * Erratum 637 workaround
1777c1ae6830SBorislav Petkov 	 */
1778a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x15) {
1779c1ae6830SBorislav Petkov 		u64 cc6_base, tmp_addr;
1780c1ae6830SBorislav Petkov 		u32 tmp;
17818b84c8dfSDaniel J Blueman 		u8 intlv_en;
1782c1ae6830SBorislav Petkov 
178310ef6b0dSChen, Gong 		if ((addr & GENMASK_ULL(47, 24)) >> 24 != 0x00fdf7)
1784c1ae6830SBorislav Petkov 			return addr;
1785c1ae6830SBorislav Petkov 
1786c1ae6830SBorislav Petkov 
1787c1ae6830SBorislav Petkov 		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp);
1788c1ae6830SBorislav Petkov 		intlv_en = tmp >> 21 & 0x7;
1789c1ae6830SBorislav Petkov 
1790c1ae6830SBorislav Petkov 		/* add [47:27] + 3 trailing bits */
179110ef6b0dSChen, Gong 		cc6_base  = (tmp & GENMASK_ULL(20, 0)) << 3;
1792c1ae6830SBorislav Petkov 
1793c1ae6830SBorislav Petkov 		/* reverse and add DramIntlvEn */
1794c1ae6830SBorislav Petkov 		cc6_base |= intlv_en ^ 0x7;
1795c1ae6830SBorislav Petkov 
1796c1ae6830SBorislav Petkov 		/* pin at [47:24] */
1797c1ae6830SBorislav Petkov 		cc6_base <<= 24;
1798c1ae6830SBorislav Petkov 
1799c1ae6830SBorislav Petkov 		if (!intlv_en)
180010ef6b0dSChen, Gong 			return cc6_base | (addr & GENMASK_ULL(23, 0));
1801c1ae6830SBorislav Petkov 
1802c1ae6830SBorislav Petkov 		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp);
1803c1ae6830SBorislav Petkov 
1804c1ae6830SBorislav Petkov 							/* faster log2 */
180510ef6b0dSChen, Gong 		tmp_addr  = (addr & GENMASK_ULL(23, 12)) << __fls(intlv_en + 1);
1806c1ae6830SBorislav Petkov 
1807c1ae6830SBorislav Petkov 		/* OR DramIntlvSel into bits [14:12] */
180810ef6b0dSChen, Gong 		tmp_addr |= (tmp & GENMASK_ULL(23, 21)) >> 9;
1809c1ae6830SBorislav Petkov 
1810c1ae6830SBorislav Petkov 		/* add remaining [11:0] bits from original MC4_ADDR */
181110ef6b0dSChen, Gong 		tmp_addr |= addr & GENMASK_ULL(11, 0);
1812c1ae6830SBorislav Petkov 
1813c1ae6830SBorislav Petkov 		return cc6_base | tmp_addr;
1814c1ae6830SBorislav Petkov 	}
1815c1ae6830SBorislav Petkov 
1816c1ae6830SBorislav Petkov 	return addr;
1817ddff876dSDoug Thompson }
1818ddff876dSDoug Thompson 
1819e2c0bffeSDaniel J Blueman static struct pci_dev *pci_get_related_function(unsigned int vendor,
1820e2c0bffeSDaniel J Blueman 						unsigned int device,
1821e2c0bffeSDaniel J Blueman 						struct pci_dev *related)
1822e2c0bffeSDaniel J Blueman {
1823e2c0bffeSDaniel J Blueman 	struct pci_dev *dev = NULL;
1824e2c0bffeSDaniel J Blueman 
1825e2c0bffeSDaniel J Blueman 	while ((dev = pci_get_device(vendor, device, dev))) {
1826e2c0bffeSDaniel J Blueman 		if (pci_domain_nr(dev->bus) == pci_domain_nr(related->bus) &&
1827e2c0bffeSDaniel J Blueman 		    (dev->bus->number == related->bus->number) &&
1828e2c0bffeSDaniel J Blueman 		    (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn)))
1829e2c0bffeSDaniel J Blueman 			break;
1830e2c0bffeSDaniel J Blueman 	}
1831e2c0bffeSDaniel J Blueman 
1832e2c0bffeSDaniel J Blueman 	return dev;
1833e2c0bffeSDaniel J Blueman }
1834e2c0bffeSDaniel J Blueman 
18357f19bf75SBorislav Petkov static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
1836ddff876dSDoug Thompson {
1837e2c0bffeSDaniel J Blueman 	struct amd_northbridge *nb;
183818b94f66SAravind Gopalakrishnan 	struct pci_dev *f1 = NULL;
183918b94f66SAravind Gopalakrishnan 	unsigned int pci_func;
184071d2a32eSBorislav Petkov 	int off = range << 3;
1841e2c0bffeSDaniel J Blueman 	u32 llim;
1842ddff876dSDoug Thompson 
18437f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off,  &pvt->ranges[range].base.lo);
18447f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo);
1845ddff876dSDoug Thompson 
184618b94f66SAravind Gopalakrishnan 	if (pvt->fam == 0xf)
18477f19bf75SBorislav Petkov 		return;
1848ddff876dSDoug Thompson 
18497f19bf75SBorislav Petkov 	if (!dram_rw(pvt, range))
18507f19bf75SBorislav Petkov 		return;
1851ddff876dSDoug Thompson 
18527f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off,  &pvt->ranges[range].base.hi);
18537f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi);
1854f08e457cSBorislav Petkov 
1855e2c0bffeSDaniel J Blueman 	/* F15h: factor in CC6 save area by reading dst node's limit reg */
185618b94f66SAravind Gopalakrishnan 	if (pvt->fam != 0x15)
1857e2c0bffeSDaniel J Blueman 		return;
1858f08e457cSBorislav Petkov 
1859e2c0bffeSDaniel J Blueman 	nb = node_to_amd_nb(dram_dst_node(pvt, range));
1860e2c0bffeSDaniel J Blueman 	if (WARN_ON(!nb))
1861e2c0bffeSDaniel J Blueman 		return;
1862e2c0bffeSDaniel J Blueman 
1863a597d2a5SAravind Gopalakrishnan 	if (pvt->model == 0x60)
1864a597d2a5SAravind Gopalakrishnan 		pci_func = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1;
1865a597d2a5SAravind Gopalakrishnan 	else if (pvt->model == 0x30)
1866a597d2a5SAravind Gopalakrishnan 		pci_func = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1;
1867a597d2a5SAravind Gopalakrishnan 	else
1868a597d2a5SAravind Gopalakrishnan 		pci_func = PCI_DEVICE_ID_AMD_15H_NB_F1;
186918b94f66SAravind Gopalakrishnan 
187018b94f66SAravind Gopalakrishnan 	f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc);
1871f08e457cSBorislav Petkov 	if (WARN_ON(!f1))
1872f08e457cSBorislav Petkov 		return;
1873f08e457cSBorislav Petkov 
1874f08e457cSBorislav Petkov 	amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim);
1875f08e457cSBorislav Petkov 
187610ef6b0dSChen, Gong 	pvt->ranges[range].lim.lo &= GENMASK_ULL(15, 0);
1877f08e457cSBorislav Petkov 
1878f08e457cSBorislav Petkov 				    /* {[39:27],111b} */
1879f08e457cSBorislav Petkov 	pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16;
1880f08e457cSBorislav Petkov 
188110ef6b0dSChen, Gong 	pvt->ranges[range].lim.hi &= GENMASK_ULL(7, 0);
1882f08e457cSBorislav Petkov 
1883f08e457cSBorislav Petkov 				    /* [47:40] */
1884f08e457cSBorislav Petkov 	pvt->ranges[range].lim.hi |= llim >> 13;
1885f08e457cSBorislav Petkov 
1886f08e457cSBorislav Petkov 	pci_dev_put(f1);
1887f08e457cSBorislav Petkov }
1888ddff876dSDoug Thompson 
1889f192c7b1SBorislav Petkov static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
189033ca0643SBorislav Petkov 				    struct err_info *err)
1891ddff876dSDoug Thompson {
1892f192c7b1SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
1893ddff876dSDoug Thompson 
189433ca0643SBorislav Petkov 	error_address_to_page_and_offset(sys_addr, err);
1895ab5a503cSMauro Carvalho Chehab 
1896ab5a503cSMauro Carvalho Chehab 	/*
1897ab5a503cSMauro Carvalho Chehab 	 * Find out which node the error address belongs to. This may be
1898ab5a503cSMauro Carvalho Chehab 	 * different from the node that detected the error.
1899ab5a503cSMauro Carvalho Chehab 	 */
190033ca0643SBorislav Petkov 	err->src_mci = find_mc_by_sys_addr(mci, sys_addr);
190133ca0643SBorislav Petkov 	if (!err->src_mci) {
1902ab5a503cSMauro Carvalho Chehab 		amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
1903ab5a503cSMauro Carvalho Chehab 			     (unsigned long)sys_addr);
190433ca0643SBorislav Petkov 		err->err_code = ERR_NODE;
1905ab5a503cSMauro Carvalho Chehab 		return;
1906ab5a503cSMauro Carvalho Chehab 	}
1907ab5a503cSMauro Carvalho Chehab 
1908ab5a503cSMauro Carvalho Chehab 	/* Now map the sys_addr to a CSROW */
190933ca0643SBorislav Petkov 	err->csrow = sys_addr_to_csrow(err->src_mci, sys_addr);
191033ca0643SBorislav Petkov 	if (err->csrow < 0) {
191133ca0643SBorislav Petkov 		err->err_code = ERR_CSROW;
1912ab5a503cSMauro Carvalho Chehab 		return;
1913ab5a503cSMauro Carvalho Chehab 	}
1914ab5a503cSMauro Carvalho Chehab 
1915ddff876dSDoug Thompson 	/* CHIPKILL enabled */
1916f192c7b1SBorislav Petkov 	if (pvt->nbcfg & NBCFG_CHIPKILL) {
191733ca0643SBorislav Petkov 		err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
191833ca0643SBorislav Petkov 		if (err->channel < 0) {
1919ddff876dSDoug Thompson 			/*
1920ddff876dSDoug Thompson 			 * Syndrome didn't map, so we don't know which of the
1921ddff876dSDoug Thompson 			 * 2 DIMMs is in error. So we need to ID 'both' of them
1922ddff876dSDoug Thompson 			 * as suspect.
1923ddff876dSDoug Thompson 			 */
192433ca0643SBorislav Petkov 			amd64_mc_warn(err->src_mci, "unknown syndrome 0x%04x - "
1925ab5a503cSMauro Carvalho Chehab 				      "possible error reporting race\n",
192633ca0643SBorislav Petkov 				      err->syndrome);
192733ca0643SBorislav Petkov 			err->err_code = ERR_CHANNEL;
1928ddff876dSDoug Thompson 			return;
1929ddff876dSDoug Thompson 		}
1930ddff876dSDoug Thompson 	} else {
1931ddff876dSDoug Thompson 		/*
1932ddff876dSDoug Thompson 		 * non-chipkill ecc mode
1933ddff876dSDoug Thompson 		 *
1934ddff876dSDoug Thompson 		 * The k8 documentation is unclear about how to determine the
1935ddff876dSDoug Thompson 		 * channel number when using non-chipkill memory.  This method
1936ddff876dSDoug Thompson 		 * was obtained from email communication with someone at AMD.
1937ddff876dSDoug Thompson 		 * (Wish the email was placed in this comment - norsk)
1938ddff876dSDoug Thompson 		 */
193933ca0643SBorislav Petkov 		err->channel = ((sys_addr & BIT(3)) != 0);
1940ddff876dSDoug Thompson 	}
1941ddff876dSDoug Thompson }
1942ddff876dSDoug Thompson 
194341d8bfabSBorislav Petkov static int ddr2_cs_size(unsigned i, bool dct_width)
1944ddff876dSDoug Thompson {
194541d8bfabSBorislav Petkov 	unsigned shift = 0;
1946ddff876dSDoug Thompson 
194741d8bfabSBorislav Petkov 	if (i <= 2)
194841d8bfabSBorislav Petkov 		shift = i;
194941d8bfabSBorislav Petkov 	else if (!(i & 0x1))
195041d8bfabSBorislav Petkov 		shift = i >> 1;
19511433eb99SBorislav Petkov 	else
195241d8bfabSBorislav Petkov 		shift = (i + 1) >> 1;
1953ddff876dSDoug Thompson 
195441d8bfabSBorislav Petkov 	return 128 << (shift + !!dct_width);
195541d8bfabSBorislav Petkov }
195641d8bfabSBorislav Petkov 
195741d8bfabSBorislav Petkov static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1958a597d2a5SAravind Gopalakrishnan 				  unsigned cs_mode, int cs_mask_nr)
195941d8bfabSBorislav Petkov {
196041d8bfabSBorislav Petkov 	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
196141d8bfabSBorislav Petkov 
196241d8bfabSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F) {
196341d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 11);
196441d8bfabSBorislav Petkov 		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
196541d8bfabSBorislav Petkov 	}
196641d8bfabSBorislav Petkov 	else if (pvt->ext_model >= K8_REV_D) {
196711b0a314SBorislav Petkov 		unsigned diff;
196841d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 10);
196941d8bfabSBorislav Petkov 
197011b0a314SBorislav Petkov 		/*
197111b0a314SBorislav Petkov 		 * the below calculation, besides trying to win an obfuscated C
197211b0a314SBorislav Petkov 		 * contest, maps cs_mode values to DIMM chip select sizes. The
197311b0a314SBorislav Petkov 		 * mappings are:
197411b0a314SBorislav Petkov 		 *
197511b0a314SBorislav Petkov 		 * cs_mode	CS size (mb)
197611b0a314SBorislav Petkov 		 * =======	============
197711b0a314SBorislav Petkov 		 * 0		32
197811b0a314SBorislav Petkov 		 * 1		64
197911b0a314SBorislav Petkov 		 * 2		128
198011b0a314SBorislav Petkov 		 * 3		128
198111b0a314SBorislav Petkov 		 * 4		256
198211b0a314SBorislav Petkov 		 * 5		512
198311b0a314SBorislav Petkov 		 * 6		256
198411b0a314SBorislav Petkov 		 * 7		512
198511b0a314SBorislav Petkov 		 * 8		1024
198611b0a314SBorislav Petkov 		 * 9		1024
198711b0a314SBorislav Petkov 		 * 10		2048
198811b0a314SBorislav Petkov 		 *
198911b0a314SBorislav Petkov 		 * Basically, it calculates a value with which to shift the
199011b0a314SBorislav Petkov 		 * smallest CS size of 32MB.
199111b0a314SBorislav Petkov 		 *
199211b0a314SBorislav Petkov 		 * ddr[23]_cs_size have a similar purpose.
199311b0a314SBorislav Petkov 		 */
199411b0a314SBorislav Petkov 		diff = cs_mode/3 + (unsigned)(cs_mode > 5);
199511b0a314SBorislav Petkov 
199611b0a314SBorislav Petkov 		return 32 << (cs_mode - diff);
199741d8bfabSBorislav Petkov 	}
199841d8bfabSBorislav Petkov 	else {
199941d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 6);
200041d8bfabSBorislav Petkov 		return 32 << cs_mode;
200141d8bfabSBorislav Petkov 	}
2002ddff876dSDoug Thompson }
2003ddff876dSDoug Thompson 
20041afd3c98SDoug Thompson /*
20051afd3c98SDoug Thompson  * Get the number of DCT channels in use.
20061afd3c98SDoug Thompson  *
20071afd3c98SDoug Thompson  * Return:
20081afd3c98SDoug Thompson  *	number of Memory Channels in operation
20091afd3c98SDoug Thompson  * Pass back:
20101afd3c98SDoug Thompson  *	contents of the DCL0_LOW register
20111afd3c98SDoug Thompson  */
20127d20d14dSBorislav Petkov static int f1x_early_channel_count(struct amd64_pvt *pvt)
20131afd3c98SDoug Thompson {
20146ba5dcdcSBorislav Petkov 	int i, j, channels = 0;
2015ddff876dSDoug Thompson 
20167d20d14dSBorislav Petkov 	/* On F10h, if we are in 128 bit mode, then we are using 2 channels */
2017a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10 && (pvt->dclr0 & WIDTH_128))
20187d20d14dSBorislav Petkov 		return 2;
20191afd3c98SDoug Thompson 
20201afd3c98SDoug Thompson 	/*
2021d16149e8SBorislav Petkov 	 * Need to check if in unganged mode: In such, there are 2 channels,
2022d16149e8SBorislav Petkov 	 * but they are not in 128 bit mode and thus the above 'dclr0' status
2023d16149e8SBorislav Petkov 	 * bit will be OFF.
20241afd3c98SDoug Thompson 	 *
20251afd3c98SDoug Thompson 	 * Need to check DCT0[0] and DCT1[0] to see if only one of them has
20261afd3c98SDoug Thompson 	 * their CSEnable bit on. If so, then SINGLE DIMM case.
20271afd3c98SDoug Thompson 	 */
2028956b9ba1SJoe Perches 	edac_dbg(0, "Data width is not 128 bits - need more decoding\n");
20291afd3c98SDoug Thompson 
20301afd3c98SDoug Thompson 	/*
20311afd3c98SDoug Thompson 	 * Check DRAM Bank Address Mapping values for each DIMM to see if there
20321afd3c98SDoug Thompson 	 * is more than just one DIMM present in unganged mode. Need to check
20331afd3c98SDoug Thompson 	 * both controllers since DIMMs can be placed in either one.
20341afd3c98SDoug Thompson 	 */
2035525a1b20SBorislav Petkov 	for (i = 0; i < 2; i++) {
2036525a1b20SBorislav Petkov 		u32 dbam = (i ? pvt->dbam1 : pvt->dbam0);
20371afd3c98SDoug Thompson 
203857a30854SWan Wei 		for (j = 0; j < 4; j++) {
203957a30854SWan Wei 			if (DBAM_DIMM(j, dbam) > 0) {
20401afd3c98SDoug Thompson 				channels++;
204157a30854SWan Wei 				break;
20421afd3c98SDoug Thompson 			}
204357a30854SWan Wei 		}
204457a30854SWan Wei 	}
20451afd3c98SDoug Thompson 
2046d16149e8SBorislav Petkov 	if (channels > 2)
2047d16149e8SBorislav Petkov 		channels = 2;
2048d16149e8SBorislav Petkov 
204924f9a7feSBorislav Petkov 	amd64_info("MCT channel count: %d\n", channels);
20501afd3c98SDoug Thompson 
20511afd3c98SDoug Thompson 	return channels;
20521afd3c98SDoug Thompson }
20531afd3c98SDoug Thompson 
2054f1cbbec9SYazen Ghannam static int f17_early_channel_count(struct amd64_pvt *pvt)
2055f1cbbec9SYazen Ghannam {
2056f1cbbec9SYazen Ghannam 	int i, channels = 0;
2057f1cbbec9SYazen Ghannam 
2058f1cbbec9SYazen Ghannam 	/* SDP Control bit 31 (SdpInit) is clear for unused UMC channels */
20594d30d2bcSYazen Ghannam 	for_each_umc(i)
2060f1cbbec9SYazen Ghannam 		channels += !!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT);
2061f1cbbec9SYazen Ghannam 
2062f1cbbec9SYazen Ghannam 	amd64_info("MCT channel count: %d\n", channels);
2063f1cbbec9SYazen Ghannam 
2064f1cbbec9SYazen Ghannam 	return channels;
2065f1cbbec9SYazen Ghannam }
2066f1cbbec9SYazen Ghannam 
206741d8bfabSBorislav Petkov static int ddr3_cs_size(unsigned i, bool dct_width)
20681afd3c98SDoug Thompson {
206941d8bfabSBorislav Petkov 	unsigned shift = 0;
207041d8bfabSBorislav Petkov 	int cs_size = 0;
207141d8bfabSBorislav Petkov 
207241d8bfabSBorislav Petkov 	if (i == 0 || i == 3 || i == 4)
207341d8bfabSBorislav Petkov 		cs_size = -1;
207441d8bfabSBorislav Petkov 	else if (i <= 2)
207541d8bfabSBorislav Petkov 		shift = i;
207641d8bfabSBorislav Petkov 	else if (i == 12)
207741d8bfabSBorislav Petkov 		shift = 7;
207841d8bfabSBorislav Petkov 	else if (!(i & 0x1))
207941d8bfabSBorislav Petkov 		shift = i >> 1;
208041d8bfabSBorislav Petkov 	else
208141d8bfabSBorislav Petkov 		shift = (i + 1) >> 1;
208241d8bfabSBorislav Petkov 
208341d8bfabSBorislav Petkov 	if (cs_size != -1)
208441d8bfabSBorislav Petkov 		cs_size = (128 * (1 << !!dct_width)) << shift;
208541d8bfabSBorislav Petkov 
208641d8bfabSBorislav Petkov 	return cs_size;
208741d8bfabSBorislav Petkov }
208841d8bfabSBorislav Petkov 
2089a597d2a5SAravind Gopalakrishnan static int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply)
2090a597d2a5SAravind Gopalakrishnan {
2091a597d2a5SAravind Gopalakrishnan 	unsigned shift = 0;
2092a597d2a5SAravind Gopalakrishnan 	int cs_size = 0;
2093a597d2a5SAravind Gopalakrishnan 
2094a597d2a5SAravind Gopalakrishnan 	if (i < 4 || i == 6)
2095a597d2a5SAravind Gopalakrishnan 		cs_size = -1;
2096a597d2a5SAravind Gopalakrishnan 	else if (i == 12)
2097a597d2a5SAravind Gopalakrishnan 		shift = 7;
2098a597d2a5SAravind Gopalakrishnan 	else if (!(i & 0x1))
2099a597d2a5SAravind Gopalakrishnan 		shift = i >> 1;
2100a597d2a5SAravind Gopalakrishnan 	else
2101a597d2a5SAravind Gopalakrishnan 		shift = (i + 1) >> 1;
2102a597d2a5SAravind Gopalakrishnan 
2103a597d2a5SAravind Gopalakrishnan 	if (cs_size != -1)
2104a597d2a5SAravind Gopalakrishnan 		cs_size = rank_multiply * (128 << shift);
2105a597d2a5SAravind Gopalakrishnan 
2106a597d2a5SAravind Gopalakrishnan 	return cs_size;
2107a597d2a5SAravind Gopalakrishnan }
2108a597d2a5SAravind Gopalakrishnan 
2109a597d2a5SAravind Gopalakrishnan static int ddr4_cs_size(unsigned i)
2110a597d2a5SAravind Gopalakrishnan {
2111a597d2a5SAravind Gopalakrishnan 	int cs_size = 0;
2112a597d2a5SAravind Gopalakrishnan 
2113a597d2a5SAravind Gopalakrishnan 	if (i == 0)
2114a597d2a5SAravind Gopalakrishnan 		cs_size = -1;
2115a597d2a5SAravind Gopalakrishnan 	else if (i == 1)
2116a597d2a5SAravind Gopalakrishnan 		cs_size = 1024;
2117a597d2a5SAravind Gopalakrishnan 	else
2118a597d2a5SAravind Gopalakrishnan 		/* Min cs_size = 1G */
2119a597d2a5SAravind Gopalakrishnan 		cs_size = 1024 * (1 << (i >> 1));
2120a597d2a5SAravind Gopalakrishnan 
2121a597d2a5SAravind Gopalakrishnan 	return cs_size;
2122a597d2a5SAravind Gopalakrishnan }
2123a597d2a5SAravind Gopalakrishnan 
212441d8bfabSBorislav Petkov static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
2125a597d2a5SAravind Gopalakrishnan 				   unsigned cs_mode, int cs_mask_nr)
212641d8bfabSBorislav Petkov {
212741d8bfabSBorislav Petkov 	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
212841d8bfabSBorislav Petkov 
212941d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 11);
21301433eb99SBorislav Petkov 
21311433eb99SBorislav Petkov 	if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
213241d8bfabSBorislav Petkov 		return ddr3_cs_size(cs_mode, dclr & WIDTH_128);
21331433eb99SBorislav Petkov 	else
213441d8bfabSBorislav Petkov 		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
213541d8bfabSBorislav Petkov }
21361433eb99SBorislav Petkov 
213741d8bfabSBorislav Petkov /*
213841d8bfabSBorislav Petkov  * F15h supports only 64bit DCT interfaces
213941d8bfabSBorislav Petkov  */
214041d8bfabSBorislav Petkov static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
2141a597d2a5SAravind Gopalakrishnan 				   unsigned cs_mode, int cs_mask_nr)
214241d8bfabSBorislav Petkov {
214341d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 12);
214441d8bfabSBorislav Petkov 
214541d8bfabSBorislav Petkov 	return ddr3_cs_size(cs_mode, false);
21461afd3c98SDoug Thompson }
21471afd3c98SDoug Thompson 
2148a597d2a5SAravind Gopalakrishnan /* F15h M60h supports DDR4 mapping as well.. */
2149a597d2a5SAravind Gopalakrishnan static int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
2150a597d2a5SAravind Gopalakrishnan 					unsigned cs_mode, int cs_mask_nr)
2151a597d2a5SAravind Gopalakrishnan {
2152a597d2a5SAravind Gopalakrishnan 	int cs_size;
2153a597d2a5SAravind Gopalakrishnan 	u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr];
2154a597d2a5SAravind Gopalakrishnan 
2155a597d2a5SAravind Gopalakrishnan 	WARN_ON(cs_mode > 12);
2156a597d2a5SAravind Gopalakrishnan 
2157a597d2a5SAravind Gopalakrishnan 	if (pvt->dram_type == MEM_DDR4) {
2158a597d2a5SAravind Gopalakrishnan 		if (cs_mode > 9)
2159a597d2a5SAravind Gopalakrishnan 			return -1;
2160a597d2a5SAravind Gopalakrishnan 
2161a597d2a5SAravind Gopalakrishnan 		cs_size = ddr4_cs_size(cs_mode);
2162a597d2a5SAravind Gopalakrishnan 	} else if (pvt->dram_type == MEM_LRDDR3) {
2163a597d2a5SAravind Gopalakrishnan 		unsigned rank_multiply = dcsm & 0xf;
2164a597d2a5SAravind Gopalakrishnan 
2165a597d2a5SAravind Gopalakrishnan 		if (rank_multiply == 3)
2166a597d2a5SAravind Gopalakrishnan 			rank_multiply = 4;
2167a597d2a5SAravind Gopalakrishnan 		cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply);
2168a597d2a5SAravind Gopalakrishnan 	} else {
2169a597d2a5SAravind Gopalakrishnan 		/* Minimum cs size is 512mb for F15hM60h*/
2170a597d2a5SAravind Gopalakrishnan 		if (cs_mode == 0x1)
2171a597d2a5SAravind Gopalakrishnan 			return -1;
2172a597d2a5SAravind Gopalakrishnan 
2173a597d2a5SAravind Gopalakrishnan 		cs_size = ddr3_cs_size(cs_mode, false);
2174a597d2a5SAravind Gopalakrishnan 	}
2175a597d2a5SAravind Gopalakrishnan 
2176a597d2a5SAravind Gopalakrishnan 	return cs_size;
2177a597d2a5SAravind Gopalakrishnan }
2178a597d2a5SAravind Gopalakrishnan 
217994c1acf2SAravind Gopalakrishnan /*
218018b94f66SAravind Gopalakrishnan  * F16h and F15h model 30h have only limited cs_modes.
218194c1acf2SAravind Gopalakrishnan  */
218294c1acf2SAravind Gopalakrishnan static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
2183a597d2a5SAravind Gopalakrishnan 				unsigned cs_mode, int cs_mask_nr)
218494c1acf2SAravind Gopalakrishnan {
218594c1acf2SAravind Gopalakrishnan 	WARN_ON(cs_mode > 12);
218694c1acf2SAravind Gopalakrishnan 
218794c1acf2SAravind Gopalakrishnan 	if (cs_mode == 6 || cs_mode == 8 ||
218894c1acf2SAravind Gopalakrishnan 	    cs_mode == 9 || cs_mode == 12)
218994c1acf2SAravind Gopalakrishnan 		return -1;
219094c1acf2SAravind Gopalakrishnan 	else
219194c1acf2SAravind Gopalakrishnan 		return ddr3_cs_size(cs_mode, false);
219294c1acf2SAravind Gopalakrishnan }
219394c1acf2SAravind Gopalakrishnan 
2194e53a3b26SYazen Ghannam static int f17_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc,
2195f1cbbec9SYazen Ghannam 				    unsigned int cs_mode, int csrow_nr)
2196f1cbbec9SYazen Ghannam {
2197e53a3b26SYazen Ghannam 	u32 addr_mask_orig, addr_mask_deinterleaved;
2198e53a3b26SYazen Ghannam 	u32 msb, weight, num_zero_bits;
2199*2151c84eSYazen Ghannam 	int cs_mask_nr = csrow_nr;
2200e53a3b26SYazen Ghannam 	int dimm, size = 0;
2201f1cbbec9SYazen Ghannam 
2202e53a3b26SYazen Ghannam 	/* No Chip Selects are enabled. */
2203e53a3b26SYazen Ghannam 	if (!cs_mode)
2204e53a3b26SYazen Ghannam 		return size;
2205e53a3b26SYazen Ghannam 
2206e53a3b26SYazen Ghannam 	/* Requested size of an even CS but none are enabled. */
2207e53a3b26SYazen Ghannam 	if (!(cs_mode & CS_EVEN) && !(csrow_nr & 1))
2208e53a3b26SYazen Ghannam 		return size;
2209e53a3b26SYazen Ghannam 
2210e53a3b26SYazen Ghannam 	/* Requested size of an odd CS but none are enabled. */
2211e53a3b26SYazen Ghannam 	if (!(cs_mode & CS_ODD) && (csrow_nr & 1))
2212e53a3b26SYazen Ghannam 		return size;
2213e53a3b26SYazen Ghannam 
2214e53a3b26SYazen Ghannam 	/*
2215*2151c84eSYazen Ghannam 	 * Family 17h introduced systems with one mask per DIMM,
2216*2151c84eSYazen Ghannam 	 * and two Chip Selects per DIMM.
2217*2151c84eSYazen Ghannam 	 *
2218*2151c84eSYazen Ghannam 	 *	CS0 and CS1 -> MASK0 / DIMM0
2219*2151c84eSYazen Ghannam 	 *	CS2 and CS3 -> MASK1 / DIMM1
2220*2151c84eSYazen Ghannam 	 *
2221*2151c84eSYazen Ghannam 	 * Family 19h Model 10h introduced systems with one mask per Chip Select,
2222*2151c84eSYazen Ghannam 	 * and two Chip Selects per DIMM.
2223*2151c84eSYazen Ghannam 	 *
2224*2151c84eSYazen Ghannam 	 *	CS0 -> MASK0 -> DIMM0
2225*2151c84eSYazen Ghannam 	 *	CS1 -> MASK1 -> DIMM0
2226*2151c84eSYazen Ghannam 	 *	CS2 -> MASK2 -> DIMM1
2227*2151c84eSYazen Ghannam 	 *	CS3 -> MASK3 -> DIMM1
2228*2151c84eSYazen Ghannam 	 *
2229*2151c84eSYazen Ghannam 	 * Keep the mask number equal to the Chip Select number for newer systems,
2230*2151c84eSYazen Ghannam 	 * and shift the mask number for older systems.
2231e53a3b26SYazen Ghannam 	 */
2232e53a3b26SYazen Ghannam 	dimm = csrow_nr >> 1;
2233e53a3b26SYazen Ghannam 
2234*2151c84eSYazen Ghannam 	if (!fam_type->flags.zn_regs_v2)
2235*2151c84eSYazen Ghannam 		cs_mask_nr >>= 1;
2236*2151c84eSYazen Ghannam 
223781f5090dSYazen Ghannam 	/* Asymmetric dual-rank DIMM support. */
223881f5090dSYazen Ghannam 	if ((csrow_nr & 1) && (cs_mode & CS_ODD_SECONDARY))
2239*2151c84eSYazen Ghannam 		addr_mask_orig = pvt->csels[umc].csmasks_sec[cs_mask_nr];
224081f5090dSYazen Ghannam 	else
2241*2151c84eSYazen Ghannam 		addr_mask_orig = pvt->csels[umc].csmasks[cs_mask_nr];
2242e53a3b26SYazen Ghannam 
2243e53a3b26SYazen Ghannam 	/*
2244e53a3b26SYazen Ghannam 	 * The number of zero bits in the mask is equal to the number of bits
2245e53a3b26SYazen Ghannam 	 * in a full mask minus the number of bits in the current mask.
2246e53a3b26SYazen Ghannam 	 *
2247e53a3b26SYazen Ghannam 	 * The MSB is the number of bits in the full mask because BIT[0] is
2248e53a3b26SYazen Ghannam 	 * always 0.
22499f4873fbSYazen Ghannam 	 *
22509f4873fbSYazen Ghannam 	 * In the special 3 Rank interleaving case, a single bit is flipped
22519f4873fbSYazen Ghannam 	 * without swapping with the most significant bit. This can be handled
22529f4873fbSYazen Ghannam 	 * by keeping the MSB where it is and ignoring the single zero bit.
2253e53a3b26SYazen Ghannam 	 */
2254e53a3b26SYazen Ghannam 	msb = fls(addr_mask_orig) - 1;
2255e53a3b26SYazen Ghannam 	weight = hweight_long(addr_mask_orig);
22569f4873fbSYazen Ghannam 	num_zero_bits = msb - weight - !!(cs_mode & CS_3R_INTERLEAVE);
2257e53a3b26SYazen Ghannam 
2258e53a3b26SYazen Ghannam 	/* Take the number of zero bits off from the top of the mask. */
2259e53a3b26SYazen Ghannam 	addr_mask_deinterleaved = GENMASK_ULL(msb - num_zero_bits, 1);
2260e53a3b26SYazen Ghannam 
2261e53a3b26SYazen Ghannam 	edac_dbg(1, "CS%d DIMM%d AddrMasks:\n", csrow_nr, dimm);
2262e53a3b26SYazen Ghannam 	edac_dbg(1, "  Original AddrMask: 0x%x\n", addr_mask_orig);
2263e53a3b26SYazen Ghannam 	edac_dbg(1, "  Deinterleaved AddrMask: 0x%x\n", addr_mask_deinterleaved);
2264f1cbbec9SYazen Ghannam 
2265f1cbbec9SYazen Ghannam 	/* Register [31:1] = Address [39:9]. Size is in kBs here. */
2266e53a3b26SYazen Ghannam 	size = (addr_mask_deinterleaved >> 2) + 1;
2267f1cbbec9SYazen Ghannam 
2268f1cbbec9SYazen Ghannam 	/* Return size in MBs. */
2269f1cbbec9SYazen Ghannam 	return size >> 10;
2270f1cbbec9SYazen Ghannam }
2271f1cbbec9SYazen Ghannam 
22725a5d2371SBorislav Petkov static void read_dram_ctl_register(struct amd64_pvt *pvt)
22736163b5d4SDoug Thompson {
22746163b5d4SDoug Thompson 
2275a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
22765a5d2371SBorislav Petkov 		return;
22775a5d2371SBorislav Petkov 
22787981a28fSAravind Gopalakrishnan 	if (!amd64_read_pci_cfg(pvt->F2, DCT_SEL_LO, &pvt->dct_sel_lo)) {
2279956b9ba1SJoe Perches 		edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
228078da121eSBorislav Petkov 			 pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
22816163b5d4SDoug Thompson 
2282956b9ba1SJoe Perches 		edac_dbg(0, "  DCTs operate in %s mode\n",
22835a5d2371SBorislav Petkov 			 (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
22846163b5d4SDoug Thompson 
228572381bd5SBorislav Petkov 		if (!dct_ganging_enabled(pvt))
2286956b9ba1SJoe Perches 			edac_dbg(0, "  Address range split per DCT: %s\n",
228772381bd5SBorislav Petkov 				 (dct_high_range_enabled(pvt) ? "yes" : "no"));
228872381bd5SBorislav Petkov 
2289956b9ba1SJoe Perches 		edac_dbg(0, "  data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n",
229072381bd5SBorislav Petkov 			 (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
229172381bd5SBorislav Petkov 			 (dct_memory_cleared(pvt) ? "yes" : "no"));
229272381bd5SBorislav Petkov 
2293956b9ba1SJoe Perches 		edac_dbg(0, "  channel interleave: %s, "
229478da121eSBorislav Petkov 			 "interleave bits selector: 0x%x\n",
229572381bd5SBorislav Petkov 			 (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
22966163b5d4SDoug Thompson 			 dct_sel_interleave_addr(pvt));
22976163b5d4SDoug Thompson 	}
22986163b5d4SDoug Thompson 
22997981a28fSAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F2, DCT_SEL_HI, &pvt->dct_sel_hi);
23006163b5d4SDoug Thompson }
23016163b5d4SDoug Thompson 
2302f71d0a05SDoug Thompson /*
230318b94f66SAravind Gopalakrishnan  * Determine channel (DCT) based on the interleaving mode (see F15h M30h BKDG,
230418b94f66SAravind Gopalakrishnan  * 2.10.12 Memory Interleaving Modes).
230518b94f66SAravind Gopalakrishnan  */
230618b94f66SAravind Gopalakrishnan static u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
230718b94f66SAravind Gopalakrishnan 				     u8 intlv_en, int num_dcts_intlv,
230818b94f66SAravind Gopalakrishnan 				     u32 dct_sel)
230918b94f66SAravind Gopalakrishnan {
231018b94f66SAravind Gopalakrishnan 	u8 channel = 0;
231118b94f66SAravind Gopalakrishnan 	u8 select;
231218b94f66SAravind Gopalakrishnan 
231318b94f66SAravind Gopalakrishnan 	if (!(intlv_en))
231418b94f66SAravind Gopalakrishnan 		return (u8)(dct_sel);
231518b94f66SAravind Gopalakrishnan 
231618b94f66SAravind Gopalakrishnan 	if (num_dcts_intlv == 2) {
231718b94f66SAravind Gopalakrishnan 		select = (sys_addr >> 8) & 0x3;
231818b94f66SAravind Gopalakrishnan 		channel = select ? 0x3 : 0;
23199d0e8d83SAravind Gopalakrishnan 	} else if (num_dcts_intlv == 4) {
23209d0e8d83SAravind Gopalakrishnan 		u8 intlv_addr = dct_sel_interleave_addr(pvt);
23219d0e8d83SAravind Gopalakrishnan 		switch (intlv_addr) {
23229d0e8d83SAravind Gopalakrishnan 		case 0x4:
23239d0e8d83SAravind Gopalakrishnan 			channel = (sys_addr >> 8) & 0x3;
23249d0e8d83SAravind Gopalakrishnan 			break;
23259d0e8d83SAravind Gopalakrishnan 		case 0x5:
23269d0e8d83SAravind Gopalakrishnan 			channel = (sys_addr >> 9) & 0x3;
23279d0e8d83SAravind Gopalakrishnan 			break;
23289d0e8d83SAravind Gopalakrishnan 		}
23299d0e8d83SAravind Gopalakrishnan 	}
233018b94f66SAravind Gopalakrishnan 	return channel;
233118b94f66SAravind Gopalakrishnan }
233218b94f66SAravind Gopalakrishnan 
233318b94f66SAravind Gopalakrishnan /*
2334229a7a11SBorislav Petkov  * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory
2335f71d0a05SDoug Thompson  * Interleaving Modes.
2336f71d0a05SDoug Thompson  */
2337b15f0fcaSBorislav Petkov static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
2338229a7a11SBorislav Petkov 				bool hi_range_sel, u8 intlv_en)
23396163b5d4SDoug Thompson {
2340151fa71cSBorislav Petkov 	u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1;
23416163b5d4SDoug Thompson 
23426163b5d4SDoug Thompson 	if (dct_ganging_enabled(pvt))
2343229a7a11SBorislav Petkov 		return 0;
2344229a7a11SBorislav Petkov 
2345229a7a11SBorislav Petkov 	if (hi_range_sel)
2346229a7a11SBorislav Petkov 		return dct_sel_high;
2347229a7a11SBorislav Petkov 
2348f71d0a05SDoug Thompson 	/*
2349f71d0a05SDoug Thompson 	 * see F2x110[DctSelIntLvAddr] - channel interleave mode
2350f71d0a05SDoug Thompson 	 */
2351229a7a11SBorislav Petkov 	if (dct_interleave_enabled(pvt)) {
2352229a7a11SBorislav Petkov 		u8 intlv_addr = dct_sel_interleave_addr(pvt);
23536163b5d4SDoug Thompson 
2354229a7a11SBorislav Petkov 		/* return DCT select function: 0=DCT0, 1=DCT1 */
2355229a7a11SBorislav Petkov 		if (!intlv_addr)
2356229a7a11SBorislav Petkov 			return sys_addr >> 6 & 1;
23576163b5d4SDoug Thompson 
2358229a7a11SBorislav Petkov 		if (intlv_addr & 0x2) {
2359229a7a11SBorislav Petkov 			u8 shift = intlv_addr & 0x1 ? 9 : 6;
2360dc0a50a8SYazen Ghannam 			u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) & 1;
2361229a7a11SBorislav Petkov 
2362229a7a11SBorislav Petkov 			return ((sys_addr >> shift) & 1) ^ temp;
23636163b5d4SDoug Thompson 		}
23646163b5d4SDoug Thompson 
2365dc0a50a8SYazen Ghannam 		if (intlv_addr & 0x4) {
2366dc0a50a8SYazen Ghannam 			u8 shift = intlv_addr & 0x1 ? 9 : 8;
2367dc0a50a8SYazen Ghannam 
2368dc0a50a8SYazen Ghannam 			return (sys_addr >> shift) & 1;
2369dc0a50a8SYazen Ghannam 		}
2370dc0a50a8SYazen Ghannam 
2371229a7a11SBorislav Petkov 		return (sys_addr >> (12 + hweight8(intlv_en))) & 1;
2372229a7a11SBorislav Petkov 	}
2373229a7a11SBorislav Petkov 
2374229a7a11SBorislav Petkov 	if (dct_high_range_enabled(pvt))
2375229a7a11SBorislav Petkov 		return ~dct_sel_high & 1;
23766163b5d4SDoug Thompson 
23776163b5d4SDoug Thompson 	return 0;
23786163b5d4SDoug Thompson }
23796163b5d4SDoug Thompson 
2380c8e518d5SBorislav Petkov /* Convert the sys_addr to the normalized DCT address */
2381c7e5301aSDaniel J Blueman static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range,
2382c8e518d5SBorislav Petkov 				 u64 sys_addr, bool hi_rng,
2383c8e518d5SBorislav Petkov 				 u32 dct_sel_base_addr)
23846163b5d4SDoug Thompson {
23856163b5d4SDoug Thompson 	u64 chan_off;
2386c8e518d5SBorislav Petkov 	u64 dram_base		= get_dram_base(pvt, range);
2387c8e518d5SBorislav Petkov 	u64 hole_off		= f10_dhar_offset(pvt);
23886f3508f6SDan Carpenter 	u64 dct_sel_base_off	= (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16;
23896163b5d4SDoug Thompson 
2390c8e518d5SBorislav Petkov 	if (hi_rng) {
2391c8e518d5SBorislav Petkov 		/*
2392c8e518d5SBorislav Petkov 		 * if
2393c8e518d5SBorislav Petkov 		 * base address of high range is below 4Gb
2394c8e518d5SBorislav Petkov 		 * (bits [47:27] at [31:11])
2395c8e518d5SBorislav Petkov 		 * DRAM address space on this DCT is hoisted above 4Gb	&&
2396c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
2397c8e518d5SBorislav Petkov 		 *
2398c8e518d5SBorislav Petkov 		 *	remove hole offset from sys_addr
2399c8e518d5SBorislav Petkov 		 * else
2400c8e518d5SBorislav Petkov 		 *	remove high range offset from sys_addr
2401c8e518d5SBorislav Petkov 		 */
2402c8e518d5SBorislav Petkov 		if ((!(dct_sel_base_addr >> 16) ||
2403c8e518d5SBorislav Petkov 		     dct_sel_base_addr < dhar_base(pvt)) &&
2404972ea17aSBorislav Petkov 		    dhar_valid(pvt) &&
2405c8e518d5SBorislav Petkov 		    (sys_addr >= BIT_64(32)))
2406bc21fa57SBorislav Petkov 			chan_off = hole_off;
24076163b5d4SDoug Thompson 		else
24086163b5d4SDoug Thompson 			chan_off = dct_sel_base_off;
24096163b5d4SDoug Thompson 	} else {
2410c8e518d5SBorislav Petkov 		/*
2411c8e518d5SBorislav Petkov 		 * if
2412c8e518d5SBorislav Petkov 		 * we have a valid hole		&&
2413c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
2414c8e518d5SBorislav Petkov 		 *
2415c8e518d5SBorislav Petkov 		 *	remove hole
2416c8e518d5SBorislav Petkov 		 * else
2417c8e518d5SBorislav Petkov 		 *	remove dram base to normalize to DCT address
2418c8e518d5SBorislav Petkov 		 */
2419972ea17aSBorislav Petkov 		if (dhar_valid(pvt) && (sys_addr >= BIT_64(32)))
2420bc21fa57SBorislav Petkov 			chan_off = hole_off;
24216163b5d4SDoug Thompson 		else
2422c8e518d5SBorislav Petkov 			chan_off = dram_base;
24236163b5d4SDoug Thompson 	}
24246163b5d4SDoug Thompson 
242510ef6b0dSChen, Gong 	return (sys_addr & GENMASK_ULL(47,6)) - (chan_off & GENMASK_ULL(47,23));
24266163b5d4SDoug Thompson }
24276163b5d4SDoug Thompson 
24286163b5d4SDoug Thompson /*
24296163b5d4SDoug Thompson  * checks if the csrow passed in is marked as SPARED, if so returns the new
24306163b5d4SDoug Thompson  * spare row
24316163b5d4SDoug Thompson  */
243211c75eadSBorislav Petkov static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow)
24336163b5d4SDoug Thompson {
2434614ec9d8SBorislav Petkov 	int tmp_cs;
24356163b5d4SDoug Thompson 
2436614ec9d8SBorislav Petkov 	if (online_spare_swap_done(pvt, dct) &&
2437614ec9d8SBorislav Petkov 	    csrow == online_spare_bad_dramcs(pvt, dct)) {
2438614ec9d8SBorislav Petkov 
2439614ec9d8SBorislav Petkov 		for_each_chip_select(tmp_cs, dct, pvt) {
2440614ec9d8SBorislav Petkov 			if (chip_select_base(tmp_cs, dct, pvt) & 0x2) {
2441614ec9d8SBorislav Petkov 				csrow = tmp_cs;
2442614ec9d8SBorislav Petkov 				break;
2443614ec9d8SBorislav Petkov 			}
2444614ec9d8SBorislav Petkov 		}
24456163b5d4SDoug Thompson 	}
24466163b5d4SDoug Thompson 	return csrow;
24476163b5d4SDoug Thompson }
24486163b5d4SDoug Thompson 
24496163b5d4SDoug Thompson /*
24506163b5d4SDoug Thompson  * Iterate over the DRAM DCT "base" and "mask" registers looking for a
24516163b5d4SDoug Thompson  * SystemAddr match on the specified 'ChannelSelect' and 'NodeID'
24526163b5d4SDoug Thompson  *
24536163b5d4SDoug Thompson  * Return:
24546163b5d4SDoug Thompson  *	-EINVAL:  NOT FOUND
24556163b5d4SDoug Thompson  *	0..csrow = Chip-Select Row
24566163b5d4SDoug Thompson  */
2457c7e5301aSDaniel J Blueman static int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct)
24586163b5d4SDoug Thompson {
24596163b5d4SDoug Thompson 	struct mem_ctl_info *mci;
24606163b5d4SDoug Thompson 	struct amd64_pvt *pvt;
246111c75eadSBorislav Petkov 	u64 cs_base, cs_mask;
24626163b5d4SDoug Thompson 	int cs_found = -EINVAL;
24636163b5d4SDoug Thompson 	int csrow;
24646163b5d4SDoug Thompson 
24652ec591acSBorislav Petkov 	mci = edac_mc_find(nid);
24666163b5d4SDoug Thompson 	if (!mci)
24676163b5d4SDoug Thompson 		return cs_found;
24686163b5d4SDoug Thompson 
24696163b5d4SDoug Thompson 	pvt = mci->pvt_info;
24706163b5d4SDoug Thompson 
2471956b9ba1SJoe Perches 	edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct);
24726163b5d4SDoug Thompson 
247311c75eadSBorislav Petkov 	for_each_chip_select(csrow, dct, pvt) {
247411c75eadSBorislav Petkov 		if (!csrow_enabled(csrow, dct, pvt))
24756163b5d4SDoug Thompson 			continue;
24766163b5d4SDoug Thompson 
247711c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask);
24786163b5d4SDoug Thompson 
2479956b9ba1SJoe Perches 		edac_dbg(1, "    CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
24806163b5d4SDoug Thompson 			 csrow, cs_base, cs_mask);
24816163b5d4SDoug Thompson 
248211c75eadSBorislav Petkov 		cs_mask = ~cs_mask;
24836163b5d4SDoug Thompson 
2484956b9ba1SJoe Perches 		edac_dbg(1, "    (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n",
248511c75eadSBorislav Petkov 			 (in_addr & cs_mask), (cs_base & cs_mask));
24866163b5d4SDoug Thompson 
248711c75eadSBorislav Petkov 		if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
248818b94f66SAravind Gopalakrishnan 			if (pvt->fam == 0x15 && pvt->model >= 0x30) {
248918b94f66SAravind Gopalakrishnan 				cs_found =  csrow;
249018b94f66SAravind Gopalakrishnan 				break;
249118b94f66SAravind Gopalakrishnan 			}
249211c75eadSBorislav Petkov 			cs_found = f10_process_possible_spare(pvt, dct, csrow);
24936163b5d4SDoug Thompson 
2494956b9ba1SJoe Perches 			edac_dbg(1, " MATCH csrow=%d\n", cs_found);
24956163b5d4SDoug Thompson 			break;
24966163b5d4SDoug Thompson 		}
24976163b5d4SDoug Thompson 	}
24986163b5d4SDoug Thompson 	return cs_found;
24996163b5d4SDoug Thompson }
25006163b5d4SDoug Thompson 
250195b0ef55SBorislav Petkov /*
250295b0ef55SBorislav Petkov  * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is
250395b0ef55SBorislav Petkov  * swapped with a region located at the bottom of memory so that the GPU can use
250495b0ef55SBorislav Petkov  * the interleaved region and thus two channels.
250595b0ef55SBorislav Petkov  */
2506b15f0fcaSBorislav Petkov static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr)
250795b0ef55SBorislav Petkov {
250895b0ef55SBorislav Petkov 	u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr;
250995b0ef55SBorislav Petkov 
2510a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10) {
251195b0ef55SBorislav Petkov 		/* only revC3 and revE have that feature */
2512a4b4bedcSBorislav Petkov 		if (pvt->model < 4 || (pvt->model < 0xa && pvt->stepping < 3))
251395b0ef55SBorislav Petkov 			return sys_addr;
251495b0ef55SBorislav Petkov 	}
251595b0ef55SBorislav Petkov 
25167981a28fSAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F2, SWAP_INTLV_REG, &swap_reg);
251795b0ef55SBorislav Petkov 
251895b0ef55SBorislav Petkov 	if (!(swap_reg & 0x1))
251995b0ef55SBorislav Petkov 		return sys_addr;
252095b0ef55SBorislav Petkov 
252195b0ef55SBorislav Petkov 	swap_base	= (swap_reg >> 3) & 0x7f;
252295b0ef55SBorislav Petkov 	swap_limit	= (swap_reg >> 11) & 0x7f;
252395b0ef55SBorislav Petkov 	rgn_size	= (swap_reg >> 20) & 0x7f;
252495b0ef55SBorislav Petkov 	tmp_addr	= sys_addr >> 27;
252595b0ef55SBorislav Petkov 
252695b0ef55SBorislav Petkov 	if (!(sys_addr >> 34) &&
252795b0ef55SBorislav Petkov 	    (((tmp_addr >= swap_base) &&
252895b0ef55SBorislav Petkov 	     (tmp_addr <= swap_limit)) ||
252995b0ef55SBorislav Petkov 	     (tmp_addr < rgn_size)))
253095b0ef55SBorislav Petkov 		return sys_addr ^ (u64)swap_base << 27;
253195b0ef55SBorislav Petkov 
253295b0ef55SBorislav Petkov 	return sys_addr;
253395b0ef55SBorislav Petkov }
253495b0ef55SBorislav Petkov 
2535f71d0a05SDoug Thompson /* For a given @dram_range, check if @sys_addr falls within it. */
2536e761359aSBorislav Petkov static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
253733ca0643SBorislav Petkov 				  u64 sys_addr, int *chan_sel)
2538f71d0a05SDoug Thompson {
2539229a7a11SBorislav Petkov 	int cs_found = -EINVAL;
2540c8e518d5SBorislav Petkov 	u64 chan_addr;
25415d4b58e8SBorislav Petkov 	u32 dct_sel_base;
254211c75eadSBorislav Petkov 	u8 channel;
2543229a7a11SBorislav Petkov 	bool high_range = false;
2544f71d0a05SDoug Thompson 
25457f19bf75SBorislav Petkov 	u8 node_id    = dram_dst_node(pvt, range);
2546229a7a11SBorislav Petkov 	u8 intlv_en   = dram_intlv_en(pvt, range);
25477f19bf75SBorislav Petkov 	u32 intlv_sel = dram_intlv_sel(pvt, range);
2548f71d0a05SDoug Thompson 
2549956b9ba1SJoe Perches 	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
2550c8e518d5SBorislav Petkov 		 range, sys_addr, get_dram_limit(pvt, range));
2551f71d0a05SDoug Thompson 
2552355fba60SBorislav Petkov 	if (dhar_valid(pvt) &&
2553355fba60SBorislav Petkov 	    dhar_base(pvt) <= sys_addr &&
2554355fba60SBorislav Petkov 	    sys_addr < BIT_64(32)) {
2555355fba60SBorislav Petkov 		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
2556355fba60SBorislav Petkov 			    sys_addr);
2557f71d0a05SDoug Thompson 		return -EINVAL;
2558355fba60SBorislav Petkov 	}
2559355fba60SBorislav Petkov 
2560f030ddfbSBorislav Petkov 	if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en)))
2561355fba60SBorislav Petkov 		return -EINVAL;
2562f71d0a05SDoug Thompson 
2563b15f0fcaSBorislav Petkov 	sys_addr = f1x_swap_interleaved_region(pvt, sys_addr);
256495b0ef55SBorislav Petkov 
2565f71d0a05SDoug Thompson 	dct_sel_base = dct_sel_baseaddr(pvt);
2566f71d0a05SDoug Thompson 
2567f71d0a05SDoug Thompson 	/*
2568f71d0a05SDoug Thompson 	 * check whether addresses >= DctSelBaseAddr[47:27] are to be used to
2569f71d0a05SDoug Thompson 	 * select between DCT0 and DCT1.
2570f71d0a05SDoug Thompson 	 */
2571f71d0a05SDoug Thompson 	if (dct_high_range_enabled(pvt) &&
2572f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt) &&
2573f71d0a05SDoug Thompson 	   ((sys_addr >> 27) >= (dct_sel_base >> 11)))
2574229a7a11SBorislav Petkov 		high_range = true;
2575f71d0a05SDoug Thompson 
2576b15f0fcaSBorislav Petkov 	channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en);
2577f71d0a05SDoug Thompson 
2578b15f0fcaSBorislav Petkov 	chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr,
2579c8e518d5SBorislav Petkov 					  high_range, dct_sel_base);
2580f71d0a05SDoug Thompson 
2581e2f79dbdSBorislav Petkov 	/* Remove node interleaving, see F1x120 */
2582e2f79dbdSBorislav Petkov 	if (intlv_en)
2583e2f79dbdSBorislav Petkov 		chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) |
2584e2f79dbdSBorislav Petkov 			    (chan_addr & 0xfff);
2585f71d0a05SDoug Thompson 
25865d4b58e8SBorislav Petkov 	/* remove channel interleave */
2587f71d0a05SDoug Thompson 	if (dct_interleave_enabled(pvt) &&
2588f71d0a05SDoug Thompson 	   !dct_high_range_enabled(pvt) &&
2589f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt)) {
25905d4b58e8SBorislav Petkov 
25915d4b58e8SBorislav Petkov 		if (dct_sel_interleave_addr(pvt) != 1) {
25925d4b58e8SBorislav Petkov 			if (dct_sel_interleave_addr(pvt) == 0x3)
25935d4b58e8SBorislav Petkov 				/* hash 9 */
25945d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 10) << 9) |
25955d4b58e8SBorislav Petkov 					     (chan_addr & 0x1ff);
25965d4b58e8SBorislav Petkov 			else
25975d4b58e8SBorislav Petkov 				/* A[6] or hash 6 */
25985d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 7) << 6) |
25995d4b58e8SBorislav Petkov 					     (chan_addr & 0x3f);
26005d4b58e8SBorislav Petkov 		} else
26015d4b58e8SBorislav Petkov 			/* A[12] */
26025d4b58e8SBorislav Petkov 			chan_addr = ((chan_addr >> 13) << 12) |
26035d4b58e8SBorislav Petkov 				     (chan_addr & 0xfff);
2604f71d0a05SDoug Thompson 	}
2605f71d0a05SDoug Thompson 
2606956b9ba1SJoe Perches 	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
2607f71d0a05SDoug Thompson 
2608b15f0fcaSBorislav Petkov 	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel);
2609f71d0a05SDoug Thompson 
261033ca0643SBorislav Petkov 	if (cs_found >= 0)
2611f71d0a05SDoug Thompson 		*chan_sel = channel;
261233ca0643SBorislav Petkov 
2613f71d0a05SDoug Thompson 	return cs_found;
2614f71d0a05SDoug Thompson }
2615f71d0a05SDoug Thompson 
261618b94f66SAravind Gopalakrishnan static int f15_m30h_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
261718b94f66SAravind Gopalakrishnan 					u64 sys_addr, int *chan_sel)
261818b94f66SAravind Gopalakrishnan {
261918b94f66SAravind Gopalakrishnan 	int cs_found = -EINVAL;
262018b94f66SAravind Gopalakrishnan 	int num_dcts_intlv = 0;
262118b94f66SAravind Gopalakrishnan 	u64 chan_addr, chan_offset;
262218b94f66SAravind Gopalakrishnan 	u64 dct_base, dct_limit;
262318b94f66SAravind Gopalakrishnan 	u32 dct_cont_base_reg, dct_cont_limit_reg, tmp;
262418b94f66SAravind Gopalakrishnan 	u8 channel, alias_channel, leg_mmio_hole, dct_sel, dct_offset_en;
262518b94f66SAravind Gopalakrishnan 
262618b94f66SAravind Gopalakrishnan 	u64 dhar_offset		= f10_dhar_offset(pvt);
262718b94f66SAravind Gopalakrishnan 	u8 intlv_addr		= dct_sel_interleave_addr(pvt);
262818b94f66SAravind Gopalakrishnan 	u8 node_id		= dram_dst_node(pvt, range);
262918b94f66SAravind Gopalakrishnan 	u8 intlv_en		= dram_intlv_en(pvt, range);
263018b94f66SAravind Gopalakrishnan 
263118b94f66SAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F1, DRAM_CONT_BASE, &dct_cont_base_reg);
263218b94f66SAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F1, DRAM_CONT_LIMIT, &dct_cont_limit_reg);
263318b94f66SAravind Gopalakrishnan 
263418b94f66SAravind Gopalakrishnan 	dct_offset_en		= (u8) ((dct_cont_base_reg >> 3) & BIT(0));
263518b94f66SAravind Gopalakrishnan 	dct_sel			= (u8) ((dct_cont_base_reg >> 4) & 0x7);
263618b94f66SAravind Gopalakrishnan 
263718b94f66SAravind Gopalakrishnan 	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
263818b94f66SAravind Gopalakrishnan 		 range, sys_addr, get_dram_limit(pvt, range));
263918b94f66SAravind Gopalakrishnan 
264018b94f66SAravind Gopalakrishnan 	if (!(get_dram_base(pvt, range)  <= sys_addr) &&
264118b94f66SAravind Gopalakrishnan 	    !(get_dram_limit(pvt, range) >= sys_addr))
264218b94f66SAravind Gopalakrishnan 		return -EINVAL;
264318b94f66SAravind Gopalakrishnan 
264418b94f66SAravind Gopalakrishnan 	if (dhar_valid(pvt) &&
264518b94f66SAravind Gopalakrishnan 	    dhar_base(pvt) <= sys_addr &&
264618b94f66SAravind Gopalakrishnan 	    sys_addr < BIT_64(32)) {
264718b94f66SAravind Gopalakrishnan 		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
264818b94f66SAravind Gopalakrishnan 			    sys_addr);
264918b94f66SAravind Gopalakrishnan 		return -EINVAL;
265018b94f66SAravind Gopalakrishnan 	}
265118b94f66SAravind Gopalakrishnan 
265218b94f66SAravind Gopalakrishnan 	/* Verify sys_addr is within DCT Range. */
26534fc06b31SAravind Gopalakrishnan 	dct_base = (u64) dct_sel_baseaddr(pvt);
26544fc06b31SAravind Gopalakrishnan 	dct_limit = (dct_cont_limit_reg >> 11) & 0x1FFF;
265518b94f66SAravind Gopalakrishnan 
265618b94f66SAravind Gopalakrishnan 	if (!(dct_cont_base_reg & BIT(0)) &&
26574fc06b31SAravind Gopalakrishnan 	    !(dct_base <= (sys_addr >> 27) &&
26584fc06b31SAravind Gopalakrishnan 	      dct_limit >= (sys_addr >> 27)))
265918b94f66SAravind Gopalakrishnan 		return -EINVAL;
266018b94f66SAravind Gopalakrishnan 
266118b94f66SAravind Gopalakrishnan 	/* Verify number of dct's that participate in channel interleaving. */
266218b94f66SAravind Gopalakrishnan 	num_dcts_intlv = (int) hweight8(intlv_en);
266318b94f66SAravind Gopalakrishnan 
266418b94f66SAravind Gopalakrishnan 	if (!(num_dcts_intlv % 2 == 0) || (num_dcts_intlv > 4))
266518b94f66SAravind Gopalakrishnan 		return -EINVAL;
266618b94f66SAravind Gopalakrishnan 
2667dc0a50a8SYazen Ghannam 	if (pvt->model >= 0x60)
2668dc0a50a8SYazen Ghannam 		channel = f1x_determine_channel(pvt, sys_addr, false, intlv_en);
2669dc0a50a8SYazen Ghannam 	else
267018b94f66SAravind Gopalakrishnan 		channel = f15_m30h_determine_channel(pvt, sys_addr, intlv_en,
267118b94f66SAravind Gopalakrishnan 						     num_dcts_intlv, dct_sel);
267218b94f66SAravind Gopalakrishnan 
267318b94f66SAravind Gopalakrishnan 	/* Verify we stay within the MAX number of channels allowed */
26747f3f5240SAravind Gopalakrishnan 	if (channel > 3)
267518b94f66SAravind Gopalakrishnan 		return -EINVAL;
267618b94f66SAravind Gopalakrishnan 
267718b94f66SAravind Gopalakrishnan 	leg_mmio_hole = (u8) (dct_cont_base_reg >> 1 & BIT(0));
267818b94f66SAravind Gopalakrishnan 
267918b94f66SAravind Gopalakrishnan 	/* Get normalized DCT addr */
268018b94f66SAravind Gopalakrishnan 	if (leg_mmio_hole && (sys_addr >= BIT_64(32)))
268118b94f66SAravind Gopalakrishnan 		chan_offset = dhar_offset;
268218b94f66SAravind Gopalakrishnan 	else
26834fc06b31SAravind Gopalakrishnan 		chan_offset = dct_base << 27;
268418b94f66SAravind Gopalakrishnan 
268518b94f66SAravind Gopalakrishnan 	chan_addr = sys_addr - chan_offset;
268618b94f66SAravind Gopalakrishnan 
268718b94f66SAravind Gopalakrishnan 	/* remove channel interleave */
268818b94f66SAravind Gopalakrishnan 	if (num_dcts_intlv == 2) {
268918b94f66SAravind Gopalakrishnan 		if (intlv_addr == 0x4)
269018b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 9) << 8) |
269118b94f66SAravind Gopalakrishnan 						(chan_addr & 0xff);
269218b94f66SAravind Gopalakrishnan 		else if (intlv_addr == 0x5)
269318b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 10) << 9) |
269418b94f66SAravind Gopalakrishnan 						(chan_addr & 0x1ff);
269518b94f66SAravind Gopalakrishnan 		else
269618b94f66SAravind Gopalakrishnan 			return -EINVAL;
269718b94f66SAravind Gopalakrishnan 
269818b94f66SAravind Gopalakrishnan 	} else if (num_dcts_intlv == 4) {
269918b94f66SAravind Gopalakrishnan 		if (intlv_addr == 0x4)
270018b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 10) << 8) |
270118b94f66SAravind Gopalakrishnan 							(chan_addr & 0xff);
270218b94f66SAravind Gopalakrishnan 		else if (intlv_addr == 0x5)
270318b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 11) << 9) |
270418b94f66SAravind Gopalakrishnan 							(chan_addr & 0x1ff);
270518b94f66SAravind Gopalakrishnan 		else
270618b94f66SAravind Gopalakrishnan 			return -EINVAL;
270718b94f66SAravind Gopalakrishnan 	}
270818b94f66SAravind Gopalakrishnan 
270918b94f66SAravind Gopalakrishnan 	if (dct_offset_en) {
271018b94f66SAravind Gopalakrishnan 		amd64_read_pci_cfg(pvt->F1,
271118b94f66SAravind Gopalakrishnan 				   DRAM_CONT_HIGH_OFF + (int) channel * 4,
271218b94f66SAravind Gopalakrishnan 				   &tmp);
27134fc06b31SAravind Gopalakrishnan 		chan_addr +=  (u64) ((tmp >> 11) & 0xfff) << 27;
271418b94f66SAravind Gopalakrishnan 	}
271518b94f66SAravind Gopalakrishnan 
271618b94f66SAravind Gopalakrishnan 	f15h_select_dct(pvt, channel);
271718b94f66SAravind Gopalakrishnan 
271818b94f66SAravind Gopalakrishnan 	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
271918b94f66SAravind Gopalakrishnan 
272018b94f66SAravind Gopalakrishnan 	/*
272118b94f66SAravind Gopalakrishnan 	 * Find Chip select:
272218b94f66SAravind Gopalakrishnan 	 * if channel = 3, then alias it to 1. This is because, in F15 M30h,
272318b94f66SAravind Gopalakrishnan 	 * there is support for 4 DCT's, but only 2 are currently functional.
272418b94f66SAravind Gopalakrishnan 	 * They are DCT0 and DCT3. But we have read all registers of DCT3 into
272518b94f66SAravind Gopalakrishnan 	 * pvt->csels[1]. So we need to use '1' here to get correct info.
272618b94f66SAravind Gopalakrishnan 	 * Refer F15 M30h BKDG Section 2.10 and 2.10.3 for clarifications.
272718b94f66SAravind Gopalakrishnan 	 */
272818b94f66SAravind Gopalakrishnan 	alias_channel =  (channel == 3) ? 1 : channel;
272918b94f66SAravind Gopalakrishnan 
273018b94f66SAravind Gopalakrishnan 	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, alias_channel);
273118b94f66SAravind Gopalakrishnan 
273218b94f66SAravind Gopalakrishnan 	if (cs_found >= 0)
273318b94f66SAravind Gopalakrishnan 		*chan_sel = alias_channel;
273418b94f66SAravind Gopalakrishnan 
273518b94f66SAravind Gopalakrishnan 	return cs_found;
273618b94f66SAravind Gopalakrishnan }
273718b94f66SAravind Gopalakrishnan 
273818b94f66SAravind Gopalakrishnan static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt,
273918b94f66SAravind Gopalakrishnan 					u64 sys_addr,
274033ca0643SBorislav Petkov 					int *chan_sel)
2741f71d0a05SDoug Thompson {
2742e761359aSBorislav Petkov 	int cs_found = -EINVAL;
2743e761359aSBorislav Petkov 	unsigned range;
2744f71d0a05SDoug Thompson 
27457f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
27467f19bf75SBorislav Petkov 		if (!dram_rw(pvt, range))
2747f71d0a05SDoug Thompson 			continue;
2748f71d0a05SDoug Thompson 
274918b94f66SAravind Gopalakrishnan 		if (pvt->fam == 0x15 && pvt->model >= 0x30)
275018b94f66SAravind Gopalakrishnan 			cs_found = f15_m30h_match_to_this_node(pvt, range,
275118b94f66SAravind Gopalakrishnan 							       sys_addr,
275218b94f66SAravind Gopalakrishnan 							       chan_sel);
2753f71d0a05SDoug Thompson 
275418b94f66SAravind Gopalakrishnan 		else if ((get_dram_base(pvt, range)  <= sys_addr) &&
275518b94f66SAravind Gopalakrishnan 			 (get_dram_limit(pvt, range) >= sys_addr)) {
2756b15f0fcaSBorislav Petkov 			cs_found = f1x_match_to_this_node(pvt, range,
275733ca0643SBorislav Petkov 							  sys_addr, chan_sel);
2758f71d0a05SDoug Thompson 			if (cs_found >= 0)
2759f71d0a05SDoug Thompson 				break;
2760f71d0a05SDoug Thompson 		}
2761f71d0a05SDoug Thompson 	}
2762f71d0a05SDoug Thompson 	return cs_found;
2763f71d0a05SDoug Thompson }
2764f71d0a05SDoug Thompson 
2765f71d0a05SDoug Thompson /*
2766bdc30a0cSBorislav Petkov  * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
2767bdc30a0cSBorislav Petkov  * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
2768f71d0a05SDoug Thompson  *
2769bdc30a0cSBorislav Petkov  * The @sys_addr is usually an error address received from the hardware
2770bdc30a0cSBorislav Petkov  * (MCX_ADDR).
2771f71d0a05SDoug Thompson  */
2772b15f0fcaSBorislav Petkov static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
277333ca0643SBorislav Petkov 				     struct err_info *err)
2774f71d0a05SDoug Thompson {
2775f71d0a05SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
2776f71d0a05SDoug Thompson 
277733ca0643SBorislav Petkov 	error_address_to_page_and_offset(sys_addr, err);
2778ab5a503cSMauro Carvalho Chehab 
277933ca0643SBorislav Petkov 	err->csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &err->channel);
278033ca0643SBorislav Petkov 	if (err->csrow < 0) {
278133ca0643SBorislav Petkov 		err->err_code = ERR_CSROW;
2782bdc30a0cSBorislav Petkov 		return;
2783bdc30a0cSBorislav Petkov 	}
2784bdc30a0cSBorislav Petkov 
2785f71d0a05SDoug Thompson 	/*
2786bdc30a0cSBorislav Petkov 	 * We need the syndromes for channel detection only when we're
2787bdc30a0cSBorislav Petkov 	 * ganged. Otherwise @chan should already contain the channel at
2788bdc30a0cSBorislav Petkov 	 * this point.
2789f71d0a05SDoug Thompson 	 */
2790a97fa68eSBorislav Petkov 	if (dct_ganging_enabled(pvt))
279133ca0643SBorislav Petkov 		err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
2792f71d0a05SDoug Thompson }
2793f71d0a05SDoug Thompson 
2794f71d0a05SDoug Thompson /*
27958566c4dfSBorislav Petkov  * debug routine to display the memory sizes of all logical DIMMs and its
2796cb328507SBorislav Petkov  * CSROWs
2797f71d0a05SDoug Thompson  */
2798d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
2799f71d0a05SDoug Thompson {
2800bb89f5a0SBorislav Petkov 	int dimm, size0, size1;
2801525a1b20SBorislav Petkov 	u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases;
2802525a1b20SBorislav Petkov 	u32 dbam  = ctrl ? pvt->dbam1 : pvt->dbam0;
2803f71d0a05SDoug Thompson 
2804a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf) {
28058566c4dfSBorislav Petkov 		/* K8 families < revF not supported yet */
28061433eb99SBorislav Petkov 	       if (pvt->ext_model < K8_REV_F)
28078566c4dfSBorislav Petkov 			return;
28088566c4dfSBorislav Petkov 	       else
28098566c4dfSBorislav Petkov 		       WARN_ON(ctrl != 0);
28108566c4dfSBorislav Petkov 	}
28118566c4dfSBorislav Petkov 
28127981a28fSAravind Gopalakrishnan 	if (pvt->fam == 0x10) {
28137981a28fSAravind Gopalakrishnan 		dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1
28147981a28fSAravind Gopalakrishnan 							   : pvt->dbam0;
28157981a28fSAravind Gopalakrishnan 		dcsb = (ctrl && !dct_ganging_enabled(pvt)) ?
28167981a28fSAravind Gopalakrishnan 				 pvt->csels[1].csbases :
28177981a28fSAravind Gopalakrishnan 				 pvt->csels[0].csbases;
28187981a28fSAravind Gopalakrishnan 	} else if (ctrl) {
28197981a28fSAravind Gopalakrishnan 		dbam = pvt->dbam0;
28207981a28fSAravind Gopalakrishnan 		dcsb = pvt->csels[1].csbases;
28217981a28fSAravind Gopalakrishnan 	}
2822956b9ba1SJoe Perches 	edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
2823956b9ba1SJoe Perches 		 ctrl, dbam);
2824f71d0a05SDoug Thompson 
28258566c4dfSBorislav Petkov 	edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
28268566c4dfSBorislav Petkov 
2827f71d0a05SDoug Thompson 	/* Dump memory sizes for DIMM and its CSROWs */
2828f71d0a05SDoug Thompson 	for (dimm = 0; dimm < 4; dimm++) {
2829f71d0a05SDoug Thompson 
2830f71d0a05SDoug Thompson 		size0 = 0;
283111c75eadSBorislav Petkov 		if (dcsb[dimm*2] & DCSB_CS_ENABLE)
283207ed82efSYazen Ghannam 			/*
283307ed82efSYazen Ghannam 			 * For F15m60h, we need multiplier for LRDIMM cs_size
283407ed82efSYazen Ghannam 			 * calculation. We pass dimm value to the dbam_to_cs
2835a597d2a5SAravind Gopalakrishnan 			 * mapper so we can find the multiplier from the
2836a597d2a5SAravind Gopalakrishnan 			 * corresponding DCSM.
2837a597d2a5SAravind Gopalakrishnan 			 */
283841d8bfabSBorislav Petkov 			size0 = pvt->ops->dbam_to_cs(pvt, ctrl,
2839a597d2a5SAravind Gopalakrishnan 						     DBAM_DIMM(dimm, dbam),
2840a597d2a5SAravind Gopalakrishnan 						     dimm);
2841f71d0a05SDoug Thompson 
2842f71d0a05SDoug Thompson 		size1 = 0;
284311c75eadSBorislav Petkov 		if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE)
284441d8bfabSBorislav Petkov 			size1 = pvt->ops->dbam_to_cs(pvt, ctrl,
2845a597d2a5SAravind Gopalakrishnan 						     DBAM_DIMM(dimm, dbam),
2846a597d2a5SAravind Gopalakrishnan 						     dimm);
2847f71d0a05SDoug Thompson 
284824f9a7feSBorislav Petkov 		amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
2849bb89f5a0SBorislav Petkov 				dimm * 2,     size0,
2850bb89f5a0SBorislav Petkov 				dimm * 2 + 1, size1);
2851f71d0a05SDoug Thompson 	}
2852f71d0a05SDoug Thompson }
2853f71d0a05SDoug Thompson 
2854d1ea71cdSBorislav Petkov static struct amd64_family_type family_types[] = {
28554d37607aSDoug Thompson 	[K8_CPUS] = {
28560092b20dSBorislav Petkov 		.ctl_name = "K8",
28578d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
28583f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
28595e4c5527SYazen Ghannam 		.max_mcs = 2,
28604d37607aSDoug Thompson 		.ops = {
28614d37607aSDoug Thompson 			.early_channel_count	= k8_early_channel_count,
28624d37607aSDoug Thompson 			.map_sysaddr_to_csrow	= k8_map_sysaddr_to_csrow,
28631433eb99SBorislav Petkov 			.dbam_to_cs		= k8_dbam_to_chip_select,
28644d37607aSDoug Thompson 		}
28654d37607aSDoug Thompson 	},
28664d37607aSDoug Thompson 	[F10_CPUS] = {
28670092b20dSBorislav Petkov 		.ctl_name = "F10h",
28688d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP,
28693f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM,
28705e4c5527SYazen Ghannam 		.max_mcs = 2,
28714d37607aSDoug Thompson 		.ops = {
28727d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
2873b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
28741433eb99SBorislav Petkov 			.dbam_to_cs		= f10_dbam_to_chip_select,
2875b2b0c605SBorislav Petkov 		}
2876b2b0c605SBorislav Petkov 	},
2877b2b0c605SBorislav Petkov 	[F15_CPUS] = {
2878b2b0c605SBorislav Petkov 		.ctl_name = "F15h",
2879df71a053SBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1,
28803f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2,
28815e4c5527SYazen Ghannam 		.max_mcs = 2,
2882b2b0c605SBorislav Petkov 		.ops = {
28837d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
2884b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
288541d8bfabSBorislav Petkov 			.dbam_to_cs		= f15_dbam_to_chip_select,
28864d37607aSDoug Thompson 		}
28874d37607aSDoug Thompson 	},
288818b94f66SAravind Gopalakrishnan 	[F15_M30H_CPUS] = {
288918b94f66SAravind Gopalakrishnan 		.ctl_name = "F15h_M30h",
289018b94f66SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1,
28913f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2,
28925e4c5527SYazen Ghannam 		.max_mcs = 2,
289318b94f66SAravind Gopalakrishnan 		.ops = {
289418b94f66SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
289518b94f66SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
289618b94f66SAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
289718b94f66SAravind Gopalakrishnan 		}
289818b94f66SAravind Gopalakrishnan 	},
2899a597d2a5SAravind Gopalakrishnan 	[F15_M60H_CPUS] = {
2900a597d2a5SAravind Gopalakrishnan 		.ctl_name = "F15h_M60h",
2901a597d2a5SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1,
29023f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2,
29035e4c5527SYazen Ghannam 		.max_mcs = 2,
2904a597d2a5SAravind Gopalakrishnan 		.ops = {
2905a597d2a5SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
2906a597d2a5SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
2907a597d2a5SAravind Gopalakrishnan 			.dbam_to_cs		= f15_m60h_dbam_to_chip_select,
2908a597d2a5SAravind Gopalakrishnan 		}
2909a597d2a5SAravind Gopalakrishnan 	},
291094c1acf2SAravind Gopalakrishnan 	[F16_CPUS] = {
291194c1acf2SAravind Gopalakrishnan 		.ctl_name = "F16h",
291294c1acf2SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1,
29133f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2,
29145e4c5527SYazen Ghannam 		.max_mcs = 2,
291594c1acf2SAravind Gopalakrishnan 		.ops = {
291694c1acf2SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
291794c1acf2SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
291894c1acf2SAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
291994c1acf2SAravind Gopalakrishnan 		}
292094c1acf2SAravind Gopalakrishnan 	},
292185a8885bSAravind Gopalakrishnan 	[F16_M30H_CPUS] = {
292285a8885bSAravind Gopalakrishnan 		.ctl_name = "F16h_M30h",
292385a8885bSAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F1,
29243f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2,
29255e4c5527SYazen Ghannam 		.max_mcs = 2,
292685a8885bSAravind Gopalakrishnan 		.ops = {
292785a8885bSAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
292885a8885bSAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
292985a8885bSAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
293085a8885bSAravind Gopalakrishnan 		}
293185a8885bSAravind Gopalakrishnan 	},
2932f1cbbec9SYazen Ghannam 	[F17_CPUS] = {
2933f1cbbec9SYazen Ghannam 		.ctl_name = "F17h",
2934f1cbbec9SYazen Ghannam 		.f0_id = PCI_DEVICE_ID_AMD_17H_DF_F0,
2935f1cbbec9SYazen Ghannam 		.f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6,
29365e4c5527SYazen Ghannam 		.max_mcs = 2,
2937f1cbbec9SYazen Ghannam 		.ops = {
2938f1cbbec9SYazen Ghannam 			.early_channel_count	= f17_early_channel_count,
2939e53a3b26SYazen Ghannam 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
2940f1cbbec9SYazen Ghannam 		}
2941f1cbbec9SYazen Ghannam 	},
29428960de4aSMichael Jin 	[F17_M10H_CPUS] = {
29438960de4aSMichael Jin 		.ctl_name = "F17h_M10h",
29448960de4aSMichael Jin 		.f0_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F0,
29458960de4aSMichael Jin 		.f6_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F6,
29465e4c5527SYazen Ghannam 		.max_mcs = 2,
29478960de4aSMichael Jin 		.ops = {
29488960de4aSMichael Jin 			.early_channel_count	= f17_early_channel_count,
2949e53a3b26SYazen Ghannam 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
29508960de4aSMichael Jin 		}
29518960de4aSMichael Jin 	},
29526e846239SYazen Ghannam 	[F17_M30H_CPUS] = {
29536e846239SYazen Ghannam 		.ctl_name = "F17h_M30h",
29546e846239SYazen Ghannam 		.f0_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F0,
29556e846239SYazen Ghannam 		.f6_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F6,
29565e4c5527SYazen Ghannam 		.max_mcs = 8,
29576e846239SYazen Ghannam 		.ops = {
29586e846239SYazen Ghannam 			.early_channel_count	= f17_early_channel_count,
2959e53a3b26SYazen Ghannam 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
29606e846239SYazen Ghannam 		}
29616e846239SYazen Ghannam 	},
2962b6bea24dSAlexander Monakov 	[F17_M60H_CPUS] = {
2963b6bea24dSAlexander Monakov 		.ctl_name = "F17h_M60h",
2964b6bea24dSAlexander Monakov 		.f0_id = PCI_DEVICE_ID_AMD_17H_M60H_DF_F0,
2965b6bea24dSAlexander Monakov 		.f6_id = PCI_DEVICE_ID_AMD_17H_M60H_DF_F6,
2966b6bea24dSAlexander Monakov 		.max_mcs = 2,
2967b6bea24dSAlexander Monakov 		.ops = {
2968b6bea24dSAlexander Monakov 			.early_channel_count	= f17_early_channel_count,
2969b6bea24dSAlexander Monakov 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
2970b6bea24dSAlexander Monakov 		}
2971b6bea24dSAlexander Monakov 	},
29723e443eb3SIsaac Vaughn 	[F17_M70H_CPUS] = {
29733e443eb3SIsaac Vaughn 		.ctl_name = "F17h_M70h",
29743e443eb3SIsaac Vaughn 		.f0_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F0,
29753e443eb3SIsaac Vaughn 		.f6_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F6,
29765e4c5527SYazen Ghannam 		.max_mcs = 2,
29773e443eb3SIsaac Vaughn 		.ops = {
29783e443eb3SIsaac Vaughn 			.early_channel_count	= f17_early_channel_count,
29793e443eb3SIsaac Vaughn 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
29803e443eb3SIsaac Vaughn 		}
29813e443eb3SIsaac Vaughn 	},
29822eb61c91SYazen Ghannam 	[F19_CPUS] = {
29832eb61c91SYazen Ghannam 		.ctl_name = "F19h",
29842eb61c91SYazen Ghannam 		.f0_id = PCI_DEVICE_ID_AMD_19H_DF_F0,
29852eb61c91SYazen Ghannam 		.f6_id = PCI_DEVICE_ID_AMD_19H_DF_F6,
29862eb61c91SYazen Ghannam 		.max_mcs = 8,
29872eb61c91SYazen Ghannam 		.ops = {
29882eb61c91SYazen Ghannam 			.early_channel_count	= f17_early_channel_count,
29892eb61c91SYazen Ghannam 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
29902eb61c91SYazen Ghannam 		}
29912eb61c91SYazen Ghannam 	},
2992e2be5955SYazen Ghannam 	[F19_M10H_CPUS] = {
2993e2be5955SYazen Ghannam 		.ctl_name = "F19h_M10h",
2994e2be5955SYazen Ghannam 		.f0_id = PCI_DEVICE_ID_AMD_19H_M10H_DF_F0,
2995e2be5955SYazen Ghannam 		.f6_id = PCI_DEVICE_ID_AMD_19H_M10H_DF_F6,
2996e2be5955SYazen Ghannam 		.max_mcs = 12,
2997*2151c84eSYazen Ghannam 		.flags.zn_regs_v2 = 1,
2998e2be5955SYazen Ghannam 		.ops = {
2999e2be5955SYazen Ghannam 			.early_channel_count	= f17_early_channel_count,
3000e2be5955SYazen Ghannam 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
3001e2be5955SYazen Ghannam 		}
3002e2be5955SYazen Ghannam 	},
30030b8bf9cbSMarc Bevand 	[F19_M50H_CPUS] = {
30040b8bf9cbSMarc Bevand 		.ctl_name = "F19h_M50h",
30050b8bf9cbSMarc Bevand 		.f0_id = PCI_DEVICE_ID_AMD_19H_M50H_DF_F0,
30060b8bf9cbSMarc Bevand 		.f6_id = PCI_DEVICE_ID_AMD_19H_M50H_DF_F6,
30070b8bf9cbSMarc Bevand 		.max_mcs = 2,
30080b8bf9cbSMarc Bevand 		.ops = {
30090b8bf9cbSMarc Bevand 			.early_channel_count	= f17_early_channel_count,
30100b8bf9cbSMarc Bevand 			.dbam_to_cs		= f17_addr_mask_to_cs_size,
30110b8bf9cbSMarc Bevand 		}
30120b8bf9cbSMarc Bevand 	},
30134d37607aSDoug Thompson };
30144d37607aSDoug Thompson 
3015b1289d6fSDoug Thompson /*
3016bfc04aecSBorislav Petkov  * These are tables of eigenvectors (one per line) which can be used for the
3017bfc04aecSBorislav Petkov  * construction of the syndrome tables. The modified syndrome search algorithm
3018bfc04aecSBorislav Petkov  * uses those to find the symbol in error and thus the DIMM.
3019b1289d6fSDoug Thompson  *
3020bfc04aecSBorislav Petkov  * Algorithm courtesy of Ross LaFetra from AMD.
3021b1289d6fSDoug Thompson  */
3022c7e5301aSDaniel J Blueman static const u16 x4_vectors[] = {
3023bfc04aecSBorislav Petkov 	0x2f57, 0x1afe, 0x66cc, 0xdd88,
3024bfc04aecSBorislav Petkov 	0x11eb, 0x3396, 0x7f4c, 0xeac8,
3025bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008,
3026bfc04aecSBorislav Petkov 	0x1013, 0x3032, 0x4044, 0x8088,
3027bfc04aecSBorislav Petkov 	0x106b, 0x30d6, 0x70fc, 0xe0a8,
3028bfc04aecSBorislav Petkov 	0x4857, 0xc4fe, 0x13cc, 0x3288,
3029bfc04aecSBorislav Petkov 	0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
3030bfc04aecSBorislav Petkov 	0x1f39, 0x251e, 0xbd6c, 0x6bd8,
3031bfc04aecSBorislav Petkov 	0x15c1, 0x2a42, 0x89ac, 0x4758,
3032bfc04aecSBorislav Petkov 	0x2b03, 0x1602, 0x4f0c, 0xca08,
3033bfc04aecSBorislav Petkov 	0x1f07, 0x3a0e, 0x6b04, 0xbd08,
3034bfc04aecSBorislav Petkov 	0x8ba7, 0x465e, 0x244c, 0x1cc8,
3035bfc04aecSBorislav Petkov 	0x2b87, 0x164e, 0x642c, 0xdc18,
3036bfc04aecSBorislav Petkov 	0x40b9, 0x80de, 0x1094, 0x20e8,
3037bfc04aecSBorislav Petkov 	0x27db, 0x1eb6, 0x9dac, 0x7b58,
3038bfc04aecSBorislav Petkov 	0x11c1, 0x2242, 0x84ac, 0x4c58,
3039bfc04aecSBorislav Petkov 	0x1be5, 0x2d7a, 0x5e34, 0xa718,
3040bfc04aecSBorislav Petkov 	0x4b39, 0x8d1e, 0x14b4, 0x28d8,
3041bfc04aecSBorislav Petkov 	0x4c97, 0xc87e, 0x11fc, 0x33a8,
3042bfc04aecSBorislav Petkov 	0x8e97, 0x497e, 0x2ffc, 0x1aa8,
3043bfc04aecSBorislav Petkov 	0x16b3, 0x3d62, 0x4f34, 0x8518,
3044bfc04aecSBorislav Petkov 	0x1e2f, 0x391a, 0x5cac, 0xf858,
3045bfc04aecSBorislav Petkov 	0x1d9f, 0x3b7a, 0x572c, 0xfe18,
3046bfc04aecSBorislav Petkov 	0x15f5, 0x2a5a, 0x5264, 0xa3b8,
3047bfc04aecSBorislav Petkov 	0x1dbb, 0x3b66, 0x715c, 0xe3f8,
3048bfc04aecSBorislav Petkov 	0x4397, 0xc27e, 0x17fc, 0x3ea8,
3049bfc04aecSBorislav Petkov 	0x1617, 0x3d3e, 0x6464, 0xb8b8,
3050bfc04aecSBorislav Petkov 	0x23ff, 0x12aa, 0xab6c, 0x56d8,
3051bfc04aecSBorislav Petkov 	0x2dfb, 0x1ba6, 0x913c, 0x7328,
3052bfc04aecSBorislav Petkov 	0x185d, 0x2ca6, 0x7914, 0x9e28,
3053bfc04aecSBorislav Petkov 	0x171b, 0x3e36, 0x7d7c, 0xebe8,
3054bfc04aecSBorislav Petkov 	0x4199, 0x82ee, 0x19f4, 0x2e58,
3055bfc04aecSBorislav Petkov 	0x4807, 0xc40e, 0x130c, 0x3208,
3056bfc04aecSBorislav Petkov 	0x1905, 0x2e0a, 0x5804, 0xac08,
3057bfc04aecSBorislav Petkov 	0x213f, 0x132a, 0xadfc, 0x5ba8,
3058bfc04aecSBorislav Petkov 	0x19a9, 0x2efe, 0xb5cc, 0x6f88,
3059b1289d6fSDoug Thompson };
3060b1289d6fSDoug Thompson 
3061c7e5301aSDaniel J Blueman static const u16 x8_vectors[] = {
3062bfc04aecSBorislav Petkov 	0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
3063bfc04aecSBorislav Petkov 	0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
3064bfc04aecSBorislav Petkov 	0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
3065bfc04aecSBorislav Petkov 	0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
3066bfc04aecSBorislav Petkov 	0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
3067bfc04aecSBorislav Petkov 	0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
3068bfc04aecSBorislav Petkov 	0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
3069bfc04aecSBorislav Petkov 	0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
3070bfc04aecSBorislav Petkov 	0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
3071bfc04aecSBorislav Petkov 	0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
3072bfc04aecSBorislav Petkov 	0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
3073bfc04aecSBorislav Petkov 	0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
3074bfc04aecSBorislav Petkov 	0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
3075bfc04aecSBorislav Petkov 	0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
3076bfc04aecSBorislav Petkov 	0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
3077bfc04aecSBorislav Petkov 	0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
3078bfc04aecSBorislav Petkov 	0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
3079bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
3080bfc04aecSBorislav Petkov 	0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
3081bfc04aecSBorislav Petkov };
3082bfc04aecSBorislav Petkov 
3083c7e5301aSDaniel J Blueman static int decode_syndrome(u16 syndrome, const u16 *vectors, unsigned num_vecs,
3084d34a6ecdSBorislav Petkov 			   unsigned v_dim)
3085b1289d6fSDoug Thompson {
3086bfc04aecSBorislav Petkov 	unsigned int i, err_sym;
3087b1289d6fSDoug Thompson 
3088bfc04aecSBorislav Petkov 	for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
3089bfc04aecSBorislav Petkov 		u16 s = syndrome;
3090d34a6ecdSBorislav Petkov 		unsigned v_idx =  err_sym * v_dim;
3091d34a6ecdSBorislav Petkov 		unsigned v_end = (err_sym + 1) * v_dim;
3092b1289d6fSDoug Thompson 
3093bfc04aecSBorislav Petkov 		/* walk over all 16 bits of the syndrome */
3094bfc04aecSBorislav Petkov 		for (i = 1; i < (1U << 16); i <<= 1) {
3095bfc04aecSBorislav Petkov 
3096bfc04aecSBorislav Petkov 			/* if bit is set in that eigenvector... */
3097bfc04aecSBorislav Petkov 			if (v_idx < v_end && vectors[v_idx] & i) {
3098bfc04aecSBorislav Petkov 				u16 ev_comp = vectors[v_idx++];
3099bfc04aecSBorislav Petkov 
3100bfc04aecSBorislav Petkov 				/* ... and bit set in the modified syndrome, */
3101bfc04aecSBorislav Petkov 				if (s & i) {
3102bfc04aecSBorislav Petkov 					/* remove it. */
3103bfc04aecSBorislav Petkov 					s ^= ev_comp;
3104bfc04aecSBorislav Petkov 
3105bfc04aecSBorislav Petkov 					if (!s)
3106bfc04aecSBorislav Petkov 						return err_sym;
3107bfc04aecSBorislav Petkov 				}
3108bfc04aecSBorislav Petkov 
3109bfc04aecSBorislav Petkov 			} else if (s & i)
3110bfc04aecSBorislav Petkov 				/* can't get to zero, move to next symbol */
3111bfc04aecSBorislav Petkov 				break;
3112bfc04aecSBorislav Petkov 		}
3113b1289d6fSDoug Thompson 	}
3114b1289d6fSDoug Thompson 
3115956b9ba1SJoe Perches 	edac_dbg(0, "syndrome(%x) not found\n", syndrome);
3116b1289d6fSDoug Thompson 	return -1;
3117b1289d6fSDoug Thompson }
3118d27bf6faSDoug Thompson 
3119bfc04aecSBorislav Petkov static int map_err_sym_to_channel(int err_sym, int sym_size)
3120bfc04aecSBorislav Petkov {
3121bfc04aecSBorislav Petkov 	if (sym_size == 4)
3122bfc04aecSBorislav Petkov 		switch (err_sym) {
3123bfc04aecSBorislav Petkov 		case 0x20:
3124bfc04aecSBorislav Petkov 		case 0x21:
3125bfc04aecSBorislav Petkov 			return 0;
3126bfc04aecSBorislav Petkov 		case 0x22:
3127bfc04aecSBorislav Petkov 		case 0x23:
3128bfc04aecSBorislav Petkov 			return 1;
3129bfc04aecSBorislav Petkov 		default:
3130bfc04aecSBorislav Petkov 			return err_sym >> 4;
3131bfc04aecSBorislav Petkov 		}
3132bfc04aecSBorislav Petkov 	/* x8 symbols */
3133bfc04aecSBorislav Petkov 	else
3134bfc04aecSBorislav Petkov 		switch (err_sym) {
3135bfc04aecSBorislav Petkov 		/* imaginary bits not in a DIMM */
3136bfc04aecSBorislav Petkov 		case 0x10:
3137bfc04aecSBorislav Petkov 			WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
3138bfc04aecSBorislav Petkov 					  err_sym);
3139bfc04aecSBorislav Petkov 			return -1;
3140bfc04aecSBorislav Petkov 		case 0x11:
3141bfc04aecSBorislav Petkov 			return 0;
3142bfc04aecSBorislav Petkov 		case 0x12:
3143bfc04aecSBorislav Petkov 			return 1;
3144bfc04aecSBorislav Petkov 		default:
3145bfc04aecSBorislav Petkov 			return err_sym >> 3;
3146bfc04aecSBorislav Petkov 		}
3147bfc04aecSBorislav Petkov 	return -1;
3148bfc04aecSBorislav Petkov }
3149bfc04aecSBorislav Petkov 
3150bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
3151bfc04aecSBorislav Petkov {
3152bfc04aecSBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
3153ad6a32e9SBorislav Petkov 	int err_sym = -1;
3154bfc04aecSBorislav Petkov 
3155a3b7db09SBorislav Petkov 	if (pvt->ecc_sym_sz == 8)
3156bfc04aecSBorislav Petkov 		err_sym = decode_syndrome(syndrome, x8_vectors,
3157ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x8_vectors),
3158a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
3159a3b7db09SBorislav Petkov 	else if (pvt->ecc_sym_sz == 4)
3160ad6a32e9SBorislav Petkov 		err_sym = decode_syndrome(syndrome, x4_vectors,
3161ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x4_vectors),
3162a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
3163ad6a32e9SBorislav Petkov 	else {
3164a3b7db09SBorislav Petkov 		amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz);
3165ad6a32e9SBorislav Petkov 		return err_sym;
3166bfc04aecSBorislav Petkov 	}
3167ad6a32e9SBorislav Petkov 
3168a3b7db09SBorislav Petkov 	return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz);
316941c31044SBorislav Petkov }
3170bfc04aecSBorislav Petkov 
3171e70984d9SYazen Ghannam static void __log_ecc_error(struct mem_ctl_info *mci, struct err_info *err,
317233ca0643SBorislav Petkov 			    u8 ecc_type)
3173d27bf6faSDoug Thompson {
317433ca0643SBorislav Petkov 	enum hw_event_mc_err_type err_type;
317533ca0643SBorislav Petkov 	const char *string;
3176d27bf6faSDoug Thompson 
317733ca0643SBorislav Petkov 	if (ecc_type == 2)
317833ca0643SBorislav Petkov 		err_type = HW_EVENT_ERR_CORRECTED;
317933ca0643SBorislav Petkov 	else if (ecc_type == 1)
318033ca0643SBorislav Petkov 		err_type = HW_EVENT_ERR_UNCORRECTED;
3181d12a969eSYazen Ghannam 	else if (ecc_type == 3)
3182d12a969eSYazen Ghannam 		err_type = HW_EVENT_ERR_DEFERRED;
318333ca0643SBorislav Petkov 	else {
318433ca0643SBorislav Petkov 		WARN(1, "Something is rotten in the state of Denmark.\n");
3185d27bf6faSDoug Thompson 		return;
3186d27bf6faSDoug Thompson 	}
3187d27bf6faSDoug Thompson 
318833ca0643SBorislav Petkov 	switch (err->err_code) {
318933ca0643SBorislav Petkov 	case DECODE_OK:
319033ca0643SBorislav Petkov 		string = "";
319133ca0643SBorislav Petkov 		break;
319233ca0643SBorislav Petkov 	case ERR_NODE:
319333ca0643SBorislav Petkov 		string = "Failed to map error addr to a node";
319433ca0643SBorislav Petkov 		break;
319533ca0643SBorislav Petkov 	case ERR_CSROW:
319633ca0643SBorislav Petkov 		string = "Failed to map error addr to a csrow";
319733ca0643SBorislav Petkov 		break;
319833ca0643SBorislav Petkov 	case ERR_CHANNEL:
3199713ad546SYazen Ghannam 		string = "Unknown syndrome - possible error reporting race";
3200713ad546SYazen Ghannam 		break;
3201713ad546SYazen Ghannam 	case ERR_SYND:
3202713ad546SYazen Ghannam 		string = "MCA_SYND not valid - unknown syndrome and csrow";
3203713ad546SYazen Ghannam 		break;
3204713ad546SYazen Ghannam 	case ERR_NORM_ADDR:
3205713ad546SYazen Ghannam 		string = "Cannot decode normalized address";
320633ca0643SBorislav Petkov 		break;
320733ca0643SBorislav Petkov 	default:
320833ca0643SBorislav Petkov 		string = "WTF error";
320933ca0643SBorislav Petkov 		break;
3210d27bf6faSDoug Thompson 	}
321133ca0643SBorislav Petkov 
321233ca0643SBorislav Petkov 	edac_mc_handle_error(err_type, mci, 1,
321333ca0643SBorislav Petkov 			     err->page, err->offset, err->syndrome,
321433ca0643SBorislav Petkov 			     err->csrow, err->channel, -1,
321533ca0643SBorislav Petkov 			     string, "");
3216d27bf6faSDoug Thompson }
3217d27bf6faSDoug Thompson 
3218df781d03SBorislav Petkov static inline void decode_bus_error(int node_id, struct mce *m)
3219d27bf6faSDoug Thompson {
32200c510cc8SDaniel J Blueman 	struct mem_ctl_info *mci;
32210c510cc8SDaniel J Blueman 	struct amd64_pvt *pvt;
3222f192c7b1SBorislav Petkov 	u8 ecc_type = (m->status >> 45) & 0x3;
322366fed2d4SBorislav Petkov 	u8 xec = XEC(m->status, 0x1f);
322466fed2d4SBorislav Petkov 	u16 ec = EC(m->status);
322533ca0643SBorislav Petkov 	u64 sys_addr;
322633ca0643SBorislav Petkov 	struct err_info err;
3227d27bf6faSDoug Thompson 
32280c510cc8SDaniel J Blueman 	mci = edac_mc_find(node_id);
32290c510cc8SDaniel J Blueman 	if (!mci)
32300c510cc8SDaniel J Blueman 		return;
32310c510cc8SDaniel J Blueman 
32320c510cc8SDaniel J Blueman 	pvt = mci->pvt_info;
32330c510cc8SDaniel J Blueman 
323466fed2d4SBorislav Petkov 	/* Bail out early if this was an 'observed' error */
32355980bb9cSBorislav Petkov 	if (PP(ec) == NBSL_PP_OBS)
3236b70ef010SBorislav Petkov 		return;
3237d27bf6faSDoug Thompson 
3238ecaf5606SBorislav Petkov 	/* Do only ECC errors */
3239ecaf5606SBorislav Petkov 	if (xec && xec != F10_NBSL_EXT_ERR_ECC)
3240d27bf6faSDoug Thompson 		return;
3241d27bf6faSDoug Thompson 
324233ca0643SBorislav Petkov 	memset(&err, 0, sizeof(err));
324333ca0643SBorislav Petkov 
3244a4b4bedcSBorislav Petkov 	sys_addr = get_error_address(pvt, m);
324533ca0643SBorislav Petkov 
3246ecaf5606SBorislav Petkov 	if (ecc_type == 2)
324733ca0643SBorislav Petkov 		err.syndrome = extract_syndrome(m->status);
324833ca0643SBorislav Petkov 
324933ca0643SBorislav Petkov 	pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err);
325033ca0643SBorislav Petkov 
3251e70984d9SYazen Ghannam 	__log_ecc_error(mci, &err, ecc_type);
3252d27bf6faSDoug Thompson }
3253d27bf6faSDoug Thompson 
32540ec449eeSDoug Thompson /*
3255713ad546SYazen Ghannam  * To find the UMC channel represented by this bank we need to match on its
3256713ad546SYazen Ghannam  * instance_id. The instance_id of a bank is held in the lower 32 bits of its
3257713ad546SYazen Ghannam  * IPID.
3258bdcee774SYazen Ghannam  *
3259bdcee774SYazen Ghannam  * Currently, we can derive the channel number by looking at the 6th nibble in
3260bdcee774SYazen Ghannam  * the instance_id. For example, instance_id=0xYXXXXX where Y is the channel
3261bdcee774SYazen Ghannam  * number.
3262713ad546SYazen Ghannam  */
3263bdcee774SYazen Ghannam static int find_umc_channel(struct mce *m)
3264713ad546SYazen Ghannam {
3265bdcee774SYazen Ghannam 	return (m->ipid & GENMASK(31, 0)) >> 20;
3266713ad546SYazen Ghannam }
3267713ad546SYazen Ghannam 
3268713ad546SYazen Ghannam static void decode_umc_error(int node_id, struct mce *m)
3269713ad546SYazen Ghannam {
3270713ad546SYazen Ghannam 	u8 ecc_type = (m->status >> 45) & 0x3;
3271713ad546SYazen Ghannam 	struct mem_ctl_info *mci;
3272713ad546SYazen Ghannam 	struct amd64_pvt *pvt;
3273713ad546SYazen Ghannam 	struct err_info err;
3274713ad546SYazen Ghannam 	u64 sys_addr;
3275713ad546SYazen Ghannam 
3276713ad546SYazen Ghannam 	mci = edac_mc_find(node_id);
3277713ad546SYazen Ghannam 	if (!mci)
3278713ad546SYazen Ghannam 		return;
3279713ad546SYazen Ghannam 
3280713ad546SYazen Ghannam 	pvt = mci->pvt_info;
3281713ad546SYazen Ghannam 
3282713ad546SYazen Ghannam 	memset(&err, 0, sizeof(err));
3283713ad546SYazen Ghannam 
3284713ad546SYazen Ghannam 	if (m->status & MCI_STATUS_DEFERRED)
3285713ad546SYazen Ghannam 		ecc_type = 3;
3286713ad546SYazen Ghannam 
3287bdcee774SYazen Ghannam 	err.channel = find_umc_channel(m);
3288713ad546SYazen Ghannam 
3289713ad546SYazen Ghannam 	if (!(m->status & MCI_STATUS_SYNDV)) {
3290713ad546SYazen Ghannam 		err.err_code = ERR_SYND;
3291713ad546SYazen Ghannam 		goto log_error;
3292713ad546SYazen Ghannam 	}
3293713ad546SYazen Ghannam 
3294713ad546SYazen Ghannam 	if (ecc_type == 2) {
3295713ad546SYazen Ghannam 		u8 length = (m->synd >> 18) & 0x3f;
3296713ad546SYazen Ghannam 
3297713ad546SYazen Ghannam 		if (length)
3298713ad546SYazen Ghannam 			err.syndrome = (m->synd >> 32) & GENMASK(length - 1, 0);
3299713ad546SYazen Ghannam 		else
3300713ad546SYazen Ghannam 			err.err_code = ERR_CHANNEL;
3301713ad546SYazen Ghannam 	}
3302713ad546SYazen Ghannam 
3303713ad546SYazen Ghannam 	err.csrow = m->synd & 0x7;
3304713ad546SYazen Ghannam 
33058a2eaab7SYazen Ghannam 	if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) {
33068a2eaab7SYazen Ghannam 		err.err_code = ERR_NORM_ADDR;
33078a2eaab7SYazen Ghannam 		goto log_error;
33088a2eaab7SYazen Ghannam 	}
33098a2eaab7SYazen Ghannam 
33108a2eaab7SYazen Ghannam 	error_address_to_page_and_offset(sys_addr, &err);
33118a2eaab7SYazen Ghannam 
3312713ad546SYazen Ghannam log_error:
3313713ad546SYazen Ghannam 	__log_ecc_error(mci, &err, ecc_type);
3314713ad546SYazen Ghannam }
3315713ad546SYazen Ghannam 
3316713ad546SYazen Ghannam /*
33173f37a36bSBorislav Petkov  * Use pvt->F3 which contains the F3 CPU PCI device to get the related
33183f37a36bSBorislav Petkov  * F1 (AddrMap) and F2 (Dct) devices. Return negative value on error.
3319936fc3afSYazen Ghannam  * Reserve F0 and F6 on systems with a UMC.
33200ec449eeSDoug Thompson  */
3321936fc3afSYazen Ghannam static int
3322936fc3afSYazen Ghannam reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2)
33230ec449eeSDoug Thompson {
3324936fc3afSYazen Ghannam 	if (pvt->umc) {
3325936fc3afSYazen Ghannam 		pvt->F0 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
3326936fc3afSYazen Ghannam 		if (!pvt->F0) {
33276a4afe38SYazen Ghannam 			edac_dbg(1, "F0 not found, device 0x%x\n", pci_id1);
3328936fc3afSYazen Ghannam 			return -ENODEV;
3329936fc3afSYazen Ghannam 		}
3330936fc3afSYazen Ghannam 
3331936fc3afSYazen Ghannam 		pvt->F6 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3);
3332936fc3afSYazen Ghannam 		if (!pvt->F6) {
3333936fc3afSYazen Ghannam 			pci_dev_put(pvt->F0);
3334936fc3afSYazen Ghannam 			pvt->F0 = NULL;
3335936fc3afSYazen Ghannam 
33366a4afe38SYazen Ghannam 			edac_dbg(1, "F6 not found: device 0x%x\n", pci_id2);
3337936fc3afSYazen Ghannam 			return -ENODEV;
3338936fc3afSYazen Ghannam 		}
33395246c540SBorislav Petkov 
3340706657b1SBorislav Petkov 		if (!pci_ctl_dev)
3341706657b1SBorislav Petkov 			pci_ctl_dev = &pvt->F0->dev;
3342706657b1SBorislav Petkov 
3343936fc3afSYazen Ghannam 		edac_dbg(1, "F0: %s\n", pci_name(pvt->F0));
3344936fc3afSYazen Ghannam 		edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
3345936fc3afSYazen Ghannam 		edac_dbg(1, "F6: %s\n", pci_name(pvt->F6));
3346936fc3afSYazen Ghannam 
3347936fc3afSYazen Ghannam 		return 0;
3348936fc3afSYazen Ghannam 	}
3349936fc3afSYazen Ghannam 
33500ec449eeSDoug Thompson 	/* Reserve the ADDRESS MAP Device */
3351936fc3afSYazen Ghannam 	pvt->F1 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
33528d5b5d9cSBorislav Petkov 	if (!pvt->F1) {
33536a4afe38SYazen Ghannam 		edac_dbg(1, "F1 not found: device 0x%x\n", pci_id1);
3354bbd0c1f6SBorislav Petkov 		return -ENODEV;
33550ec449eeSDoug Thompson 	}
33560ec449eeSDoug Thompson 
33573f37a36bSBorislav Petkov 	/* Reserve the DCT Device */
3358936fc3afSYazen Ghannam 	pvt->F2 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3);
33593f37a36bSBorislav Petkov 	if (!pvt->F2) {
33608d5b5d9cSBorislav Petkov 		pci_dev_put(pvt->F1);
33618d5b5d9cSBorislav Petkov 		pvt->F1 = NULL;
33620ec449eeSDoug Thompson 
33636a4afe38SYazen Ghannam 		edac_dbg(1, "F2 not found: device 0x%x\n", pci_id2);
3364bbd0c1f6SBorislav Petkov 		return -ENODEV;
33650ec449eeSDoug Thompson 	}
3366936fc3afSYazen Ghannam 
3367706657b1SBorislav Petkov 	if (!pci_ctl_dev)
3368706657b1SBorislav Petkov 		pci_ctl_dev = &pvt->F2->dev;
3369706657b1SBorislav Petkov 
3370956b9ba1SJoe Perches 	edac_dbg(1, "F1: %s\n", pci_name(pvt->F1));
3371956b9ba1SJoe Perches 	edac_dbg(1, "F2: %s\n", pci_name(pvt->F2));
3372956b9ba1SJoe Perches 	edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
33730ec449eeSDoug Thompson 
33740ec449eeSDoug Thompson 	return 0;
33750ec449eeSDoug Thompson }
33760ec449eeSDoug Thompson 
3377360b7f3cSBorislav Petkov static void free_mc_sibling_devs(struct amd64_pvt *pvt)
33780ec449eeSDoug Thompson {
3379936fc3afSYazen Ghannam 	if (pvt->umc) {
3380936fc3afSYazen Ghannam 		pci_dev_put(pvt->F0);
3381936fc3afSYazen Ghannam 		pci_dev_put(pvt->F6);
3382936fc3afSYazen Ghannam 	} else {
33838d5b5d9cSBorislav Petkov 		pci_dev_put(pvt->F1);
33843f37a36bSBorislav Petkov 		pci_dev_put(pvt->F2);
33850ec449eeSDoug Thompson 	}
3386936fc3afSYazen Ghannam }
33870ec449eeSDoug Thompson 
3388b64ce7cdSYazen Ghannam static void determine_ecc_sym_sz(struct amd64_pvt *pvt)
3389b64ce7cdSYazen Ghannam {
3390b64ce7cdSYazen Ghannam 	pvt->ecc_sym_sz = 4;
3391b64ce7cdSYazen Ghannam 
3392b64ce7cdSYazen Ghannam 	if (pvt->umc) {
3393b64ce7cdSYazen Ghannam 		u8 i;
3394b64ce7cdSYazen Ghannam 
33954d30d2bcSYazen Ghannam 		for_each_umc(i) {
3396b64ce7cdSYazen Ghannam 			/* Check enabled channels only: */
33977835961dSYazen Ghannam 			if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) {
33987835961dSYazen Ghannam 				if (pvt->umc[i].ecc_ctrl & BIT(9)) {
33997835961dSYazen Ghannam 					pvt->ecc_sym_sz = 16;
34007835961dSYazen Ghannam 					return;
34017835961dSYazen Ghannam 				} else if (pvt->umc[i].ecc_ctrl & BIT(7)) {
3402b64ce7cdSYazen Ghannam 					pvt->ecc_sym_sz = 8;
3403b64ce7cdSYazen Ghannam 					return;
3404b64ce7cdSYazen Ghannam 				}
34057835961dSYazen Ghannam 			}
34067835961dSYazen Ghannam 		}
34077835961dSYazen Ghannam 	} else if (pvt->fam >= 0x10) {
3408b64ce7cdSYazen Ghannam 		u32 tmp;
3409b64ce7cdSYazen Ghannam 
3410b64ce7cdSYazen Ghannam 		amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp);
3411b64ce7cdSYazen Ghannam 		/* F16h has only DCT0, so no need to read dbam1. */
3412b64ce7cdSYazen Ghannam 		if (pvt->fam != 0x16)
3413b64ce7cdSYazen Ghannam 			amd64_read_dct_pci_cfg(pvt, 1, DBAM0, &pvt->dbam1);
3414b64ce7cdSYazen Ghannam 
3415b64ce7cdSYazen Ghannam 		/* F10h, revD and later can do x8 ECC too. */
3416b64ce7cdSYazen Ghannam 		if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25))
3417b64ce7cdSYazen Ghannam 			pvt->ecc_sym_sz = 8;
3418b64ce7cdSYazen Ghannam 	}
3419b64ce7cdSYazen Ghannam }
3420b64ce7cdSYazen Ghannam 
3421b64ce7cdSYazen Ghannam /*
3422b64ce7cdSYazen Ghannam  * Retrieve the hardware registers of the memory controller.
3423b64ce7cdSYazen Ghannam  */
3424b64ce7cdSYazen Ghannam static void __read_mc_regs_df(struct amd64_pvt *pvt)
3425b64ce7cdSYazen Ghannam {
3426b64ce7cdSYazen Ghannam 	u8 nid = pvt->mc_node_id;
3427b64ce7cdSYazen Ghannam 	struct amd64_umc *umc;
3428b64ce7cdSYazen Ghannam 	u32 i, umc_base;
3429b64ce7cdSYazen Ghannam 
3430b64ce7cdSYazen Ghannam 	/* Read registers from each UMC */
34314d30d2bcSYazen Ghannam 	for_each_umc(i) {
3432b64ce7cdSYazen Ghannam 
3433b64ce7cdSYazen Ghannam 		umc_base = get_umc_base(i);
3434b64ce7cdSYazen Ghannam 		umc = &pvt->umc[i];
3435b64ce7cdSYazen Ghannam 
3436*2151c84eSYazen Ghannam 		amd_smn_read(nid, umc_base + get_umc_reg(UMCCH_DIMM_CFG), &umc->dimm_cfg);
343707ed82efSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_UMC_CFG, &umc->umc_cfg);
3438b64ce7cdSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_SDP_CTRL, &umc->sdp_ctrl);
3439b64ce7cdSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_ECC_CTRL, &umc->ecc_ctrl);
344007ed82efSYazen Ghannam 		amd_smn_read(nid, umc_base + UMCCH_UMC_CAP_HI, &umc->umc_cap_hi);
3441b64ce7cdSYazen Ghannam 	}
3442b64ce7cdSYazen Ghannam }
3443b64ce7cdSYazen Ghannam 
34440ec449eeSDoug Thompson /*
34450ec449eeSDoug Thompson  * Retrieve the hardware registers of the memory controller (this includes the
34460ec449eeSDoug Thompson  * 'Address Map' and 'Misc' device regs)
34470ec449eeSDoug Thompson  */
3448360b7f3cSBorislav Petkov static void read_mc_regs(struct amd64_pvt *pvt)
34490ec449eeSDoug Thompson {
3450b64ce7cdSYazen Ghannam 	unsigned int range;
34510ec449eeSDoug Thompson 	u64 msr_val;
34520ec449eeSDoug Thompson 
34530ec449eeSDoug Thompson 	/*
34540ec449eeSDoug Thompson 	 * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
3455b64ce7cdSYazen Ghannam 	 * those are Read-As-Zero.
34560ec449eeSDoug Thompson 	 */
3457e97f8bb8SBorislav Petkov 	rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
3458956b9ba1SJoe Perches 	edac_dbg(0, "  TOP_MEM:  0x%016llx\n", pvt->top_mem);
34590ec449eeSDoug Thompson 
3460b64ce7cdSYazen Ghannam 	/* Check first whether TOP_MEM2 is enabled: */
3461059e5c32SBrijesh Singh 	rdmsrl(MSR_AMD64_SYSCFG, msr_val);
3462b64ce7cdSYazen Ghannam 	if (msr_val & BIT(21)) {
3463e97f8bb8SBorislav Petkov 		rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
3464956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
3465b64ce7cdSYazen Ghannam 	} else {
3466956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2 disabled\n");
3467b64ce7cdSYazen Ghannam 	}
3468b64ce7cdSYazen Ghannam 
3469b64ce7cdSYazen Ghannam 	if (pvt->umc) {
3470b64ce7cdSYazen Ghannam 		__read_mc_regs_df(pvt);
3471b64ce7cdSYazen Ghannam 		amd64_read_pci_cfg(pvt->F0, DF_DHAR, &pvt->dhar);
3472b64ce7cdSYazen Ghannam 
3473b64ce7cdSYazen Ghannam 		goto skip;
3474b64ce7cdSYazen Ghannam 	}
34750ec449eeSDoug Thompson 
34765980bb9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap);
34770ec449eeSDoug Thompson 
34785a5d2371SBorislav Petkov 	read_dram_ctl_register(pvt);
34790ec449eeSDoug Thompson 
34807f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
34817f19bf75SBorislav Petkov 		u8 rw;
34820ec449eeSDoug Thompson 
34837f19bf75SBorislav Petkov 		/* read settings for this DRAM range */
34847f19bf75SBorislav Petkov 		read_dram_base_limit_regs(pvt, range);
3485e97f8bb8SBorislav Petkov 
34867f19bf75SBorislav Petkov 		rw = dram_rw(pvt, range);
34877f19bf75SBorislav Petkov 		if (!rw)
34887f19bf75SBorislav Petkov 			continue;
34897f19bf75SBorislav Petkov 
3490956b9ba1SJoe Perches 		edac_dbg(1, "  DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
34917f19bf75SBorislav Petkov 			 range,
34927f19bf75SBorislav Petkov 			 get_dram_base(pvt, range),
34937f19bf75SBorislav Petkov 			 get_dram_limit(pvt, range));
34947f19bf75SBorislav Petkov 
3495956b9ba1SJoe Perches 		edac_dbg(1, "   IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
34967f19bf75SBorislav Petkov 			 dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
34977f19bf75SBorislav Petkov 			 (rw & 0x1) ? "R" : "-",
34987f19bf75SBorislav Petkov 			 (rw & 0x2) ? "W" : "-",
34997f19bf75SBorislav Petkov 			 dram_intlv_sel(pvt, range),
35007f19bf75SBorislav Petkov 			 dram_dst_node(pvt, range));
35010ec449eeSDoug Thompson 	}
35020ec449eeSDoug Thompson 
3503bc21fa57SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar);
35047981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DBAM0, &pvt->dbam0);
35050ec449eeSDoug Thompson 
35068d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare);
35070ec449eeSDoug Thompson 
35087981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DCLR0, &pvt->dclr0);
35097981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DCHR0, &pvt->dchr0);
35100ec449eeSDoug Thompson 
351178da121eSBorislav Petkov 	if (!dct_ganging_enabled(pvt)) {
35127981a28fSAravind Gopalakrishnan 		amd64_read_dct_pci_cfg(pvt, 1, DCLR0, &pvt->dclr1);
35137981a28fSAravind Gopalakrishnan 		amd64_read_dct_pci_cfg(pvt, 1, DCHR0, &pvt->dchr1);
35140ec449eeSDoug Thompson 	}
3515b2b0c605SBorislav Petkov 
3516b64ce7cdSYazen Ghannam skip:
3517b64ce7cdSYazen Ghannam 	read_dct_base_mask(pvt);
3518b64ce7cdSYazen Ghannam 
3519a597d2a5SAravind Gopalakrishnan 	determine_memory_type(pvt);
352075aeaaf2SYazen Ghannam 
352175aeaaf2SYazen Ghannam 	if (!pvt->umc)
3522a597d2a5SAravind Gopalakrishnan 		edac_dbg(1, "  DIMM type: %s\n", edac_mem_types[pvt->dram_type]);
3523a3b7db09SBorislav Petkov 
3524b64ce7cdSYazen Ghannam 	determine_ecc_sym_sz(pvt);
35250ec449eeSDoug Thompson }
35260ec449eeSDoug Thompson 
35270ec449eeSDoug Thompson /*
35280ec449eeSDoug Thompson  * NOTE: CPU Revision Dependent code
35290ec449eeSDoug Thompson  *
35300ec449eeSDoug Thompson  * Input:
353111c75eadSBorislav Petkov  *	@csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1)
35320ec449eeSDoug Thompson  *	k8 private pointer to -->
35330ec449eeSDoug Thompson  *			DRAM Bank Address mapping register
35340ec449eeSDoug Thompson  *			node_id
35350ec449eeSDoug Thompson  *			DCL register where dual_channel_active is
35360ec449eeSDoug Thompson  *
35370ec449eeSDoug Thompson  * The DBAM register consists of 4 sets of 4 bits each definitions:
35380ec449eeSDoug Thompson  *
35390ec449eeSDoug Thompson  * Bits:	CSROWs
35400ec449eeSDoug Thompson  * 0-3		CSROWs 0 and 1
35410ec449eeSDoug Thompson  * 4-7		CSROWs 2 and 3
35420ec449eeSDoug Thompson  * 8-11		CSROWs 4 and 5
35430ec449eeSDoug Thompson  * 12-15	CSROWs 6 and 7
35440ec449eeSDoug Thompson  *
35450ec449eeSDoug Thompson  * Values range from: 0 to 15
35460ec449eeSDoug Thompson  * The meaning of the values depends on CPU revision and dual-channel state,
35470ec449eeSDoug Thompson  * see relevant BKDG more info.
35480ec449eeSDoug Thompson  *
35490ec449eeSDoug Thompson  * The memory controller provides for total of only 8 CSROWs in its current
35500ec449eeSDoug Thompson  * architecture. Each "pair" of CSROWs normally represents just one DIMM in
35510ec449eeSDoug Thompson  * single channel or two (2) DIMMs in dual channel mode.
35520ec449eeSDoug Thompson  *
35530ec449eeSDoug Thompson  * The following code logic collapses the various tables for CSROW based on CPU
35540ec449eeSDoug Thompson  * revision.
35550ec449eeSDoug Thompson  *
35560ec449eeSDoug Thompson  * Returns:
35570ec449eeSDoug Thompson  *	The number of PAGE_SIZE pages on the specified CSROW number it
35580ec449eeSDoug Thompson  *	encompasses
35590ec449eeSDoug Thompson  *
35600ec449eeSDoug Thompson  */
3561eb77e6b8SYazen Ghannam static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr_orig)
35620ec449eeSDoug Thompson {
3563f92cae45SAshish Shenoy 	u32 dbam = dct ? pvt->dbam1 : pvt->dbam0;
3564eb77e6b8SYazen Ghannam 	int csrow_nr = csrow_nr_orig;
3565eb77e6b8SYazen Ghannam 	u32 cs_mode, nr_pages;
35660ec449eeSDoug Thompson 
3567e53a3b26SYazen Ghannam 	if (!pvt->umc) {
3568eb77e6b8SYazen Ghannam 		csrow_nr >>= 1;
3569eb77e6b8SYazen Ghannam 		cs_mode = DBAM_DIMM(csrow_nr, dbam);
3570e53a3b26SYazen Ghannam 	} else {
3571e53a3b26SYazen Ghannam 		cs_mode = f17_get_cs_mode(csrow_nr >> 1, dct, pvt);
3572e53a3b26SYazen Ghannam 	}
35730ec449eeSDoug Thompson 
3574eb77e6b8SYazen Ghannam 	nr_pages   = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, csrow_nr);
3575eb77e6b8SYazen Ghannam 	nr_pages <<= 20 - PAGE_SHIFT;
35760ec449eeSDoug Thompson 
357710de6497SBorislav Petkov 	edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n",
3578eb77e6b8SYazen Ghannam 		    csrow_nr_orig, dct,  cs_mode);
357910de6497SBorislav Petkov 	edac_dbg(0, "nr_pages/channel: %u\n", nr_pages);
35800ec449eeSDoug Thompson 
35810ec449eeSDoug Thompson 	return nr_pages;
35820ec449eeSDoug Thompson }
35830ec449eeSDoug Thompson 
3584353a1fcbSYazen Ghannam static int init_csrows_df(struct mem_ctl_info *mci)
3585353a1fcbSYazen Ghannam {
3586353a1fcbSYazen Ghannam 	struct amd64_pvt *pvt = mci->pvt_info;
3587353a1fcbSYazen Ghannam 	enum edac_type edac_mode = EDAC_NONE;
3588353a1fcbSYazen Ghannam 	enum dev_type dev_type = DEV_UNKNOWN;
3589353a1fcbSYazen Ghannam 	struct dimm_info *dimm;
3590353a1fcbSYazen Ghannam 	int empty = 1;
3591353a1fcbSYazen Ghannam 	u8 umc, cs;
3592353a1fcbSYazen Ghannam 
3593353a1fcbSYazen Ghannam 	if (mci->edac_ctl_cap & EDAC_FLAG_S16ECD16ED) {
3594353a1fcbSYazen Ghannam 		edac_mode = EDAC_S16ECD16ED;
3595353a1fcbSYazen Ghannam 		dev_type = DEV_X16;
3596353a1fcbSYazen Ghannam 	} else if (mci->edac_ctl_cap & EDAC_FLAG_S8ECD8ED) {
3597353a1fcbSYazen Ghannam 		edac_mode = EDAC_S8ECD8ED;
3598353a1fcbSYazen Ghannam 		dev_type = DEV_X8;
3599353a1fcbSYazen Ghannam 	} else if (mci->edac_ctl_cap & EDAC_FLAG_S4ECD4ED) {
3600353a1fcbSYazen Ghannam 		edac_mode = EDAC_S4ECD4ED;
3601353a1fcbSYazen Ghannam 		dev_type = DEV_X4;
3602353a1fcbSYazen Ghannam 	} else if (mci->edac_ctl_cap & EDAC_FLAG_SECDED) {
3603353a1fcbSYazen Ghannam 		edac_mode = EDAC_SECDED;
3604353a1fcbSYazen Ghannam 	}
3605353a1fcbSYazen Ghannam 
3606353a1fcbSYazen Ghannam 	for_each_umc(umc) {
3607353a1fcbSYazen Ghannam 		for_each_chip_select(cs, umc, pvt) {
3608353a1fcbSYazen Ghannam 			if (!csrow_enabled(cs, umc, pvt))
3609353a1fcbSYazen Ghannam 				continue;
3610353a1fcbSYazen Ghannam 
3611353a1fcbSYazen Ghannam 			empty = 0;
3612353a1fcbSYazen Ghannam 			dimm = mci->csrows[cs]->channels[umc]->dimm;
3613353a1fcbSYazen Ghannam 
3614353a1fcbSYazen Ghannam 			edac_dbg(1, "MC node: %d, csrow: %d\n",
3615353a1fcbSYazen Ghannam 					pvt->mc_node_id, cs);
3616353a1fcbSYazen Ghannam 
3617353a1fcbSYazen Ghannam 			dimm->nr_pages = get_csrow_nr_pages(pvt, umc, cs);
361875aeaaf2SYazen Ghannam 			dimm->mtype = pvt->umc[umc].dram_type;
3619353a1fcbSYazen Ghannam 			dimm->edac_mode = edac_mode;
3620353a1fcbSYazen Ghannam 			dimm->dtype = dev_type;
3621466503d6SYazen Ghannam 			dimm->grain = 64;
3622353a1fcbSYazen Ghannam 		}
3623353a1fcbSYazen Ghannam 	}
3624353a1fcbSYazen Ghannam 
3625353a1fcbSYazen Ghannam 	return empty;
3626353a1fcbSYazen Ghannam }
3627353a1fcbSYazen Ghannam 
36280ec449eeSDoug Thompson /*
36290ec449eeSDoug Thompson  * Initialize the array of csrow attribute instances, based on the values
36300ec449eeSDoug Thompson  * from pci config hardware registers.
36310ec449eeSDoug Thompson  */
3632360b7f3cSBorislav Petkov static int init_csrows(struct mem_ctl_info *mci)
36330ec449eeSDoug Thompson {
363410de6497SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
36352d09d8f3SYazen Ghannam 	enum edac_type edac_mode = EDAC_NONE;
36360ec449eeSDoug Thompson 	struct csrow_info *csrow;
3637de3910ebSMauro Carvalho Chehab 	struct dimm_info *dimm;
363810de6497SBorislav Petkov 	int i, j, empty = 1;
3639a895bf8bSMauro Carvalho Chehab 	int nr_pages = 0;
364010de6497SBorislav Petkov 	u32 val;
36410ec449eeSDoug Thompson 
3642353a1fcbSYazen Ghannam 	if (pvt->umc)
3643353a1fcbSYazen Ghannam 		return init_csrows_df(mci);
3644353a1fcbSYazen Ghannam 
3645a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
36460ec449eeSDoug Thompson 
36472299ef71SBorislav Petkov 	pvt->nbcfg = val;
36480ec449eeSDoug Thompson 
3649956b9ba1SJoe Perches 	edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
36502299ef71SBorislav Petkov 		 pvt->mc_node_id, val,
3651a97fa68eSBorislav Petkov 		 !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
36520ec449eeSDoug Thompson 
365310de6497SBorislav Petkov 	/*
365410de6497SBorislav Petkov 	 * We iterate over DCT0 here but we look at DCT1 in parallel, if needed.
365510de6497SBorislav Petkov 	 */
365611c75eadSBorislav Petkov 	for_each_chip_select(i, 0, pvt) {
365710de6497SBorislav Petkov 		bool row_dct0 = !!csrow_enabled(i, 0, pvt);
365810de6497SBorislav Petkov 		bool row_dct1 = false;
36590ec449eeSDoug Thompson 
3660a4b4bedcSBorislav Petkov 		if (pvt->fam != 0xf)
366110de6497SBorislav Petkov 			row_dct1 = !!csrow_enabled(i, 1, pvt);
366210de6497SBorislav Petkov 
366310de6497SBorislav Petkov 		if (!row_dct0 && !row_dct1)
36640ec449eeSDoug Thompson 			continue;
36650ec449eeSDoug Thompson 
366610de6497SBorislav Petkov 		csrow = mci->csrows[i];
36670ec449eeSDoug Thompson 		empty = 0;
366811c75eadSBorislav Petkov 
366910de6497SBorislav Petkov 		edac_dbg(1, "MC node: %d, csrow: %d\n",
367010de6497SBorislav Petkov 			    pvt->mc_node_id, i);
367110de6497SBorislav Petkov 
36721eef1282SMauro Carvalho Chehab 		if (row_dct0) {
3673d1ea71cdSBorislav Petkov 			nr_pages = get_csrow_nr_pages(pvt, 0, i);
36741eef1282SMauro Carvalho Chehab 			csrow->channels[0]->dimm->nr_pages = nr_pages;
36751eef1282SMauro Carvalho Chehab 		}
367610de6497SBorislav Petkov 
367710de6497SBorislav Petkov 		/* K8 has only one DCT */
3678a4b4bedcSBorislav Petkov 		if (pvt->fam != 0xf && row_dct1) {
3679d1ea71cdSBorislav Petkov 			int row_dct1_pages = get_csrow_nr_pages(pvt, 1, i);
36801eef1282SMauro Carvalho Chehab 
36811eef1282SMauro Carvalho Chehab 			csrow->channels[1]->dimm->nr_pages = row_dct1_pages;
36821eef1282SMauro Carvalho Chehab 			nr_pages += row_dct1_pages;
36831eef1282SMauro Carvalho Chehab 		}
36840ec449eeSDoug Thompson 
368510de6497SBorislav Petkov 		edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages);
36860ec449eeSDoug Thompson 
36872d09d8f3SYazen Ghannam 		/* Determine DIMM ECC mode: */
3688353a1fcbSYazen Ghannam 		if (pvt->nbcfg & NBCFG_ECC_ENABLE) {
36892d09d8f3SYazen Ghannam 			edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL)
36902d09d8f3SYazen Ghannam 					? EDAC_S4ECD4ED
36912d09d8f3SYazen Ghannam 					: EDAC_SECDED;
36922d09d8f3SYazen Ghannam 		}
3693084a4fccSMauro Carvalho Chehab 
3694084a4fccSMauro Carvalho Chehab 		for (j = 0; j < pvt->channel_count; j++) {
3695de3910ebSMauro Carvalho Chehab 			dimm = csrow->channels[j]->dimm;
3696a597d2a5SAravind Gopalakrishnan 			dimm->mtype = pvt->dram_type;
3697de3910ebSMauro Carvalho Chehab 			dimm->edac_mode = edac_mode;
3698466503d6SYazen Ghannam 			dimm->grain = 64;
3699084a4fccSMauro Carvalho Chehab 		}
37000ec449eeSDoug Thompson 	}
37010ec449eeSDoug Thompson 
37020ec449eeSDoug Thompson 	return empty;
37030ec449eeSDoug Thompson }
3704d27bf6faSDoug Thompson 
370506724535SBorislav Petkov /* get all cores on this DCT */
37068b84c8dfSDaniel J Blueman static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, u16 nid)
3707f9431992SDoug Thompson {
370806724535SBorislav Petkov 	int cpu;
3709f9431992SDoug Thompson 
371006724535SBorislav Petkov 	for_each_online_cpu(cpu)
3711db970bd2SYazen Ghannam 		if (topology_die_id(cpu) == nid)
371206724535SBorislav Petkov 			cpumask_set_cpu(cpu, mask);
3713f9431992SDoug Thompson }
3714f9431992SDoug Thompson 
3715f9431992SDoug Thompson /* check MCG_CTL on all the cpus on this node */
3716d1ea71cdSBorislav Petkov static bool nb_mce_bank_enabled_on_node(u16 nid)
3717f9431992SDoug Thompson {
3718ba578cb3SRusty Russell 	cpumask_var_t mask;
371950542251SBorislav Petkov 	int cpu, nbe;
372006724535SBorislav Petkov 	bool ret = false;
3721f9431992SDoug Thompson 
3722ba578cb3SRusty Russell 	if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
372324f9a7feSBorislav Petkov 		amd64_warn("%s: Error allocating mask\n", __func__);
372406724535SBorislav Petkov 		return false;
372506724535SBorislav Petkov 	}
372606724535SBorislav Petkov 
3727ba578cb3SRusty Russell 	get_cpus_on_this_dct_cpumask(mask, nid);
372806724535SBorislav Petkov 
3729ba578cb3SRusty Russell 	rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
3730ba578cb3SRusty Russell 
3731ba578cb3SRusty Russell 	for_each_cpu(cpu, mask) {
373250542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
37335980bb9cSBorislav Petkov 		nbe = reg->l & MSR_MCGCTL_NBE;
373406724535SBorislav Petkov 
3735956b9ba1SJoe Perches 		edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
373650542251SBorislav Petkov 			 cpu, reg->q,
373706724535SBorislav Petkov 			 (nbe ? "enabled" : "disabled"));
373806724535SBorislav Petkov 
373906724535SBorislav Petkov 		if (!nbe)
374006724535SBorislav Petkov 			goto out;
374106724535SBorislav Petkov 	}
374206724535SBorislav Petkov 	ret = true;
374306724535SBorislav Petkov 
374406724535SBorislav Petkov out:
3745ba578cb3SRusty Russell 	free_cpumask_var(mask);
3746f9431992SDoug Thompson 	return ret;
3747f9431992SDoug Thompson }
3748f9431992SDoug Thompson 
3749c7e5301aSDaniel J Blueman static int toggle_ecc_err_reporting(struct ecc_settings *s, u16 nid, bool on)
3750f6d6ae96SBorislav Petkov {
3751f6d6ae96SBorislav Petkov 	cpumask_var_t cmask;
375250542251SBorislav Petkov 	int cpu;
3753f6d6ae96SBorislav Petkov 
3754f6d6ae96SBorislav Petkov 	if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
375524f9a7feSBorislav Petkov 		amd64_warn("%s: error allocating mask\n", __func__);
37560de27884SPan Bian 		return -ENOMEM;
3757f6d6ae96SBorislav Petkov 	}
3758f6d6ae96SBorislav Petkov 
3759ae7bb7c6SBorislav Petkov 	get_cpus_on_this_dct_cpumask(cmask, nid);
3760f6d6ae96SBorislav Petkov 
3761f6d6ae96SBorislav Petkov 	rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
3762f6d6ae96SBorislav Petkov 
3763f6d6ae96SBorislav Petkov 	for_each_cpu(cpu, cmask) {
3764f6d6ae96SBorislav Petkov 
376550542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
376650542251SBorislav Petkov 
3767f6d6ae96SBorislav Petkov 		if (on) {
37685980bb9cSBorislav Petkov 			if (reg->l & MSR_MCGCTL_NBE)
3769ae7bb7c6SBorislav Petkov 				s->flags.nb_mce_enable = 1;
3770f6d6ae96SBorislav Petkov 
37715980bb9cSBorislav Petkov 			reg->l |= MSR_MCGCTL_NBE;
3772f6d6ae96SBorislav Petkov 		} else {
3773f6d6ae96SBorislav Petkov 			/*
3774d95cf4deSBorislav Petkov 			 * Turn off NB MCE reporting only when it was off before
3775f6d6ae96SBorislav Petkov 			 */
3776ae7bb7c6SBorislav Petkov 			if (!s->flags.nb_mce_enable)
37775980bb9cSBorislav Petkov 				reg->l &= ~MSR_MCGCTL_NBE;
3778f6d6ae96SBorislav Petkov 		}
3779f6d6ae96SBorislav Petkov 	}
3780f6d6ae96SBorislav Petkov 	wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
3781f6d6ae96SBorislav Petkov 
3782f6d6ae96SBorislav Petkov 	free_cpumask_var(cmask);
3783f6d6ae96SBorislav Petkov 
3784f6d6ae96SBorislav Petkov 	return 0;
3785f6d6ae96SBorislav Petkov }
3786f6d6ae96SBorislav Petkov 
3787c7e5301aSDaniel J Blueman static bool enable_ecc_error_reporting(struct ecc_settings *s, u16 nid,
37882299ef71SBorislav Petkov 				       struct pci_dev *F3)
3789f6d6ae96SBorislav Petkov {
37902299ef71SBorislav Petkov 	bool ret = true;
3791c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
3792f6d6ae96SBorislav Petkov 
37932299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, ON)) {
37942299ef71SBorislav Petkov 		amd64_warn("Error enabling ECC reporting over MCGCTL!\n");
37952299ef71SBorislav Petkov 		return false;
37962299ef71SBorislav Petkov 	}
37972299ef71SBorislav Petkov 
3798c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
3799f6d6ae96SBorislav Petkov 
3800ae7bb7c6SBorislav Petkov 	s->old_nbctl   = value & mask;
3801ae7bb7c6SBorislav Petkov 	s->nbctl_valid = true;
3802f6d6ae96SBorislav Petkov 
3803f6d6ae96SBorislav Petkov 	value |= mask;
3804c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
3805f6d6ae96SBorislav Petkov 
3806a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCFG, &value);
3807f6d6ae96SBorislav Petkov 
3808956b9ba1SJoe Perches 	edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
3809a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
3810f6d6ae96SBorislav Petkov 
3811a97fa68eSBorislav Petkov 	if (!(value & NBCFG_ECC_ENABLE)) {
381224f9a7feSBorislav Petkov 		amd64_warn("DRAM ECC disabled on this node, enabling...\n");
3813f6d6ae96SBorislav Petkov 
3814ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 0;
3815d95cf4deSBorislav Petkov 
3816f6d6ae96SBorislav Petkov 		/* Attempt to turn on DRAM ECC Enable */
3817a97fa68eSBorislav Petkov 		value |= NBCFG_ECC_ENABLE;
3818a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
3819f6d6ae96SBorislav Petkov 
3820a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
3821f6d6ae96SBorislav Petkov 
3822a97fa68eSBorislav Petkov 		if (!(value & NBCFG_ECC_ENABLE)) {
382324f9a7feSBorislav Petkov 			amd64_warn("Hardware rejected DRAM ECC enable,"
382424f9a7feSBorislav Petkov 				   "check memory DIMM configuration.\n");
38252299ef71SBorislav Petkov 			ret = false;
3826f6d6ae96SBorislav Petkov 		} else {
382724f9a7feSBorislav Petkov 			amd64_info("Hardware accepted DRAM ECC Enable\n");
3828f6d6ae96SBorislav Petkov 		}
3829d95cf4deSBorislav Petkov 	} else {
3830ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 1;
3831f6d6ae96SBorislav Petkov 	}
3832d95cf4deSBorislav Petkov 
3833956b9ba1SJoe Perches 	edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
3834a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
3835f6d6ae96SBorislav Petkov 
38362299ef71SBorislav Petkov 	return ret;
3837f6d6ae96SBorislav Petkov }
3838f6d6ae96SBorislav Petkov 
3839c7e5301aSDaniel J Blueman static void restore_ecc_error_reporting(struct ecc_settings *s, u16 nid,
3840360b7f3cSBorislav Petkov 					struct pci_dev *F3)
3841f6d6ae96SBorislav Petkov {
3842c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
3843c9f4f26eSBorislav Petkov 
3844ae7bb7c6SBorislav Petkov 	if (!s->nbctl_valid)
3845f6d6ae96SBorislav Petkov 		return;
3846f6d6ae96SBorislav Petkov 
3847c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
3848f6d6ae96SBorislav Petkov 	value &= ~mask;
3849ae7bb7c6SBorislav Petkov 	value |= s->old_nbctl;
3850f6d6ae96SBorislav Petkov 
3851c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
3852f6d6ae96SBorislav Petkov 
3853ae7bb7c6SBorislav Petkov 	/* restore previous BIOS DRAM ECC "off" setting we force-enabled */
3854ae7bb7c6SBorislav Petkov 	if (!s->flags.nb_ecc_prev) {
3855a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
3856a97fa68eSBorislav Petkov 		value &= ~NBCFG_ECC_ENABLE;
3857a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
3858d95cf4deSBorislav Petkov 	}
3859d95cf4deSBorislav Petkov 
3860d95cf4deSBorislav Petkov 	/* restore the NB Enable MCGCTL bit */
38612299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, OFF))
386224f9a7feSBorislav Petkov 		amd64_warn("Error restoring NB MCGCTL settings!\n");
3863f6d6ae96SBorislav Petkov }
3864f6d6ae96SBorislav Petkov 
38651c9b08baSYazen Ghannam static bool ecc_enabled(struct amd64_pvt *pvt)
3866f9431992SDoug Thompson {
38671c9b08baSYazen Ghannam 	u16 nid = pvt->mc_node_id;
386806724535SBorislav Petkov 	bool nb_mce_en = false;
3869196b79fcSYazen Ghannam 	u8 ecc_en = 0, i;
3870196b79fcSYazen Ghannam 	u32 value;
3871f9431992SDoug Thompson 
3872196b79fcSYazen Ghannam 	if (boot_cpu_data.x86 >= 0x17) {
3873196b79fcSYazen Ghannam 		u8 umc_en_mask = 0, ecc_en_mask = 0;
38741c9b08baSYazen Ghannam 		struct amd64_umc *umc;
3875196b79fcSYazen Ghannam 
38764d30d2bcSYazen Ghannam 		for_each_umc(i) {
38771c9b08baSYazen Ghannam 			umc = &pvt->umc[i];
3878196b79fcSYazen Ghannam 
3879196b79fcSYazen Ghannam 			/* Only check enabled UMCs. */
38801c9b08baSYazen Ghannam 			if (!(umc->sdp_ctrl & UMC_SDP_INIT))
3881196b79fcSYazen Ghannam 				continue;
3882196b79fcSYazen Ghannam 
3883196b79fcSYazen Ghannam 			umc_en_mask |= BIT(i);
3884196b79fcSYazen Ghannam 
38851c9b08baSYazen Ghannam 			if (umc->umc_cap_hi & UMC_ECC_ENABLED)
3886196b79fcSYazen Ghannam 				ecc_en_mask |= BIT(i);
3887196b79fcSYazen Ghannam 		}
3888196b79fcSYazen Ghannam 
3889196b79fcSYazen Ghannam 		/* Check whether at least one UMC is enabled: */
3890196b79fcSYazen Ghannam 		if (umc_en_mask)
3891196b79fcSYazen Ghannam 			ecc_en = umc_en_mask == ecc_en_mask;
389211ab1caeSYazen Ghannam 		else
389311ab1caeSYazen Ghannam 			edac_dbg(0, "Node %d: No enabled UMCs.\n", nid);
3894196b79fcSYazen Ghannam 
3895196b79fcSYazen Ghannam 		/* Assume UMC MCA banks are enabled. */
3896196b79fcSYazen Ghannam 		nb_mce_en = true;
3897196b79fcSYazen Ghannam 	} else {
38981c9b08baSYazen Ghannam 		amd64_read_pci_cfg(pvt->F3, NBCFG, &value);
3899f9431992SDoug Thompson 
3900a97fa68eSBorislav Petkov 		ecc_en = !!(value & NBCFG_ECC_ENABLE);
3901be3468e8SBorislav Petkov 
3902d1ea71cdSBorislav Petkov 		nb_mce_en = nb_mce_bank_enabled_on_node(nid);
390306724535SBorislav Petkov 		if (!nb_mce_en)
390411ab1caeSYazen Ghannam 			edac_dbg(0, "NB MCE bank disabled, set MSR 0x%08x[4] on node %d to enable.\n",
39052299ef71SBorislav Petkov 				     MSR_IA32_MCG_CTL, nid);
3906196b79fcSYazen Ghannam 	}
3907196b79fcSYazen Ghannam 
39084cbcb73bSBorislav Petkov 	edac_dbg(3, "Node %d: DRAM ECC %s.\n", nid, (ecc_en ? "enabled" : "disabled"));
3909be3468e8SBorislav Petkov 
39107fdfee92SBorislav Petkov 	if (!ecc_en || !nb_mce_en)
39112299ef71SBorislav Petkov 		return false;
39127fdfee92SBorislav Petkov 	else
39132299ef71SBorislav Petkov 		return true;
3914f9431992SDoug Thompson }
3915f9431992SDoug Thompson 
39162d09d8f3SYazen Ghannam static inline void
39172d09d8f3SYazen Ghannam f17h_determine_edac_ctl_cap(struct mem_ctl_info *mci, struct amd64_pvt *pvt)
39182d09d8f3SYazen Ghannam {
3919f8be8e56SYazen Ghannam 	u8 i, ecc_en = 1, cpk_en = 1, dev_x4 = 1, dev_x16 = 1;
39202d09d8f3SYazen Ghannam 
39214d30d2bcSYazen Ghannam 	for_each_umc(i) {
39222d09d8f3SYazen Ghannam 		if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) {
39232d09d8f3SYazen Ghannam 			ecc_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_ENABLED);
39242d09d8f3SYazen Ghannam 			cpk_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_CHIPKILL_CAP);
3925f8be8e56SYazen Ghannam 
3926f8be8e56SYazen Ghannam 			dev_x4  &= !!(pvt->umc[i].dimm_cfg & BIT(6));
3927f8be8e56SYazen Ghannam 			dev_x16 &= !!(pvt->umc[i].dimm_cfg & BIT(7));
39282d09d8f3SYazen Ghannam 		}
39292d09d8f3SYazen Ghannam 	}
39302d09d8f3SYazen Ghannam 
39312d09d8f3SYazen Ghannam 	/* Set chipkill only if ECC is enabled: */
39322d09d8f3SYazen Ghannam 	if (ecc_en) {
39332d09d8f3SYazen Ghannam 		mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
39342d09d8f3SYazen Ghannam 
3935f8be8e56SYazen Ghannam 		if (!cpk_en)
3936f8be8e56SYazen Ghannam 			return;
3937f8be8e56SYazen Ghannam 
3938f8be8e56SYazen Ghannam 		if (dev_x4)
39392d09d8f3SYazen Ghannam 			mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
3940f8be8e56SYazen Ghannam 		else if (dev_x16)
3941f8be8e56SYazen Ghannam 			mci->edac_ctl_cap |= EDAC_FLAG_S16ECD16ED;
3942f8be8e56SYazen Ghannam 		else
3943f8be8e56SYazen Ghannam 			mci->edac_ctl_cap |= EDAC_FLAG_S8ECD8ED;
39442d09d8f3SYazen Ghannam 	}
39452d09d8f3SYazen Ghannam }
39462d09d8f3SYazen Ghannam 
394738ddd4d1SYazen Ghannam static void setup_mci_misc_attrs(struct mem_ctl_info *mci)
39487d6034d3SDoug Thompson {
39497d6034d3SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
39507d6034d3SDoug Thompson 
39517d6034d3SDoug Thompson 	mci->mtype_cap		= MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
39527d6034d3SDoug Thompson 	mci->edac_ctl_cap	= EDAC_FLAG_NONE;
39537d6034d3SDoug Thompson 
39542d09d8f3SYazen Ghannam 	if (pvt->umc) {
39552d09d8f3SYazen Ghannam 		f17h_determine_edac_ctl_cap(mci, pvt);
39562d09d8f3SYazen Ghannam 	} else {
39575980bb9cSBorislav Petkov 		if (pvt->nbcap & NBCAP_SECDED)
39587d6034d3SDoug Thompson 			mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
39597d6034d3SDoug Thompson 
39605980bb9cSBorislav Petkov 		if (pvt->nbcap & NBCAP_CHIPKILL)
39617d6034d3SDoug Thompson 			mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
39622d09d8f3SYazen Ghannam 	}
39637d6034d3SDoug Thompson 
3964d1ea71cdSBorislav Petkov 	mci->edac_cap		= determine_edac_cap(pvt);
39657d6034d3SDoug Thompson 	mci->mod_name		= EDAC_MOD_STR;
396638ddd4d1SYazen Ghannam 	mci->ctl_name		= fam_type->ctl_name;
3967e7934b70SYazen Ghannam 	mci->dev_name		= pci_name(pvt->F3);
39687d6034d3SDoug Thompson 	mci->ctl_page_to_phys	= NULL;
39697d6034d3SDoug Thompson 
39707d6034d3SDoug Thompson 	/* memory scrubber interface */
3971d1ea71cdSBorislav Petkov 	mci->set_sdram_scrub_rate = set_scrub_rate;
3972d1ea71cdSBorislav Petkov 	mci->get_sdram_scrub_rate = get_scrub_rate;
39737d6034d3SDoug Thompson }
39747d6034d3SDoug Thompson 
39750092b20dSBorislav Petkov /*
39760092b20dSBorislav Petkov  * returns a pointer to the family descriptor on success, NULL otherwise.
39770092b20dSBorislav Petkov  */
3978d1ea71cdSBorislav Petkov static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt)
3979395ae783SBorislav Petkov {
398018b94f66SAravind Gopalakrishnan 	pvt->ext_model  = boot_cpu_data.x86_model >> 4;
3981b399151cSJia Zhang 	pvt->stepping	= boot_cpu_data.x86_stepping;
398218b94f66SAravind Gopalakrishnan 	pvt->model	= boot_cpu_data.x86_model;
398318b94f66SAravind Gopalakrishnan 	pvt->fam	= boot_cpu_data.x86;
398418b94f66SAravind Gopalakrishnan 
398518b94f66SAravind Gopalakrishnan 	switch (pvt->fam) {
3986395ae783SBorislav Petkov 	case 0xf:
3987d1ea71cdSBorislav Petkov 		fam_type	= &family_types[K8_CPUS];
3988d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[K8_CPUS].ops;
3989395ae783SBorislav Petkov 		break;
3990df71a053SBorislav Petkov 
3991395ae783SBorislav Petkov 	case 0x10:
3992d1ea71cdSBorislav Petkov 		fam_type	= &family_types[F10_CPUS];
3993d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[F10_CPUS].ops;
3994df71a053SBorislav Petkov 		break;
3995df71a053SBorislav Petkov 
3996df71a053SBorislav Petkov 	case 0x15:
399718b94f66SAravind Gopalakrishnan 		if (pvt->model == 0x30) {
3998d1ea71cdSBorislav Petkov 			fam_type = &family_types[F15_M30H_CPUS];
3999d1ea71cdSBorislav Petkov 			pvt->ops = &family_types[F15_M30H_CPUS].ops;
400018b94f66SAravind Gopalakrishnan 			break;
4001a597d2a5SAravind Gopalakrishnan 		} else if (pvt->model == 0x60) {
4002a597d2a5SAravind Gopalakrishnan 			fam_type = &family_types[F15_M60H_CPUS];
4003a597d2a5SAravind Gopalakrishnan 			pvt->ops = &family_types[F15_M60H_CPUS].ops;
4004a597d2a5SAravind Gopalakrishnan 			break;
40056c13d7ffSBorislav Petkov 		/* Richland is only client */
40066c13d7ffSBorislav Petkov 		} else if (pvt->model == 0x13) {
40076c13d7ffSBorislav Petkov 			return NULL;
40086c13d7ffSBorislav Petkov 		} else {
4009d1ea71cdSBorislav Petkov 			fam_type	= &family_types[F15_CPUS];
4010d1ea71cdSBorislav Petkov 			pvt->ops	= &family_types[F15_CPUS].ops;
40116c13d7ffSBorislav Petkov 		}
4012395ae783SBorislav Petkov 		break;
4013395ae783SBorislav Petkov 
401494c1acf2SAravind Gopalakrishnan 	case 0x16:
401585a8885bSAravind Gopalakrishnan 		if (pvt->model == 0x30) {
401685a8885bSAravind Gopalakrishnan 			fam_type = &family_types[F16_M30H_CPUS];
401785a8885bSAravind Gopalakrishnan 			pvt->ops = &family_types[F16_M30H_CPUS].ops;
401885a8885bSAravind Gopalakrishnan 			break;
401985a8885bSAravind Gopalakrishnan 		}
4020d1ea71cdSBorislav Petkov 		fam_type	= &family_types[F16_CPUS];
4021d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[F16_CPUS].ops;
402294c1acf2SAravind Gopalakrishnan 		break;
402394c1acf2SAravind Gopalakrishnan 
4024f1cbbec9SYazen Ghannam 	case 0x17:
40258960de4aSMichael Jin 		if (pvt->model >= 0x10 && pvt->model <= 0x2f) {
40268960de4aSMichael Jin 			fam_type = &family_types[F17_M10H_CPUS];
40278960de4aSMichael Jin 			pvt->ops = &family_types[F17_M10H_CPUS].ops;
40288960de4aSMichael Jin 			break;
40296e846239SYazen Ghannam 		} else if (pvt->model >= 0x30 && pvt->model <= 0x3f) {
40306e846239SYazen Ghannam 			fam_type = &family_types[F17_M30H_CPUS];
40316e846239SYazen Ghannam 			pvt->ops = &family_types[F17_M30H_CPUS].ops;
40326e846239SYazen Ghannam 			break;
4033b6bea24dSAlexander Monakov 		} else if (pvt->model >= 0x60 && pvt->model <= 0x6f) {
4034b6bea24dSAlexander Monakov 			fam_type = &family_types[F17_M60H_CPUS];
4035b6bea24dSAlexander Monakov 			pvt->ops = &family_types[F17_M60H_CPUS].ops;
4036b6bea24dSAlexander Monakov 			break;
40373e443eb3SIsaac Vaughn 		} else if (pvt->model >= 0x70 && pvt->model <= 0x7f) {
40383e443eb3SIsaac Vaughn 			fam_type = &family_types[F17_M70H_CPUS];
40393e443eb3SIsaac Vaughn 			pvt->ops = &family_types[F17_M70H_CPUS].ops;
40403e443eb3SIsaac Vaughn 			break;
40418960de4aSMichael Jin 		}
4042df561f66SGustavo A. R. Silva 		fallthrough;
4043c4a3e946SPu Wen 	case 0x18:
4044f1cbbec9SYazen Ghannam 		fam_type	= &family_types[F17_CPUS];
4045f1cbbec9SYazen Ghannam 		pvt->ops	= &family_types[F17_CPUS].ops;
4046c4a3e946SPu Wen 
4047c4a3e946SPu Wen 		if (pvt->fam == 0x18)
4048c4a3e946SPu Wen 			family_types[F17_CPUS].ctl_name = "F18h";
4049f1cbbec9SYazen Ghannam 		break;
4050f1cbbec9SYazen Ghannam 
40512eb61c91SYazen Ghannam 	case 0x19:
4052e2be5955SYazen Ghannam 		if (pvt->model >= 0x10 && pvt->model <= 0x1f) {
4053e2be5955SYazen Ghannam 			fam_type = &family_types[F19_M10H_CPUS];
4054e2be5955SYazen Ghannam 			pvt->ops = &family_types[F19_M10H_CPUS].ops;
4055e2be5955SYazen Ghannam 			break;
4056e2be5955SYazen Ghannam 		} else if (pvt->model >= 0x20 && pvt->model <= 0x2f) {
4057b4210eabSYazen Ghannam 			fam_type = &family_types[F17_M70H_CPUS];
4058b4210eabSYazen Ghannam 			pvt->ops = &family_types[F17_M70H_CPUS].ops;
4059b4210eabSYazen Ghannam 			fam_type->ctl_name = "F19h_M20h";
4060b4210eabSYazen Ghannam 			break;
40610b8bf9cbSMarc Bevand 		} else if (pvt->model >= 0x50 && pvt->model <= 0x5f) {
40620b8bf9cbSMarc Bevand 			fam_type = &family_types[F19_M50H_CPUS];
40630b8bf9cbSMarc Bevand 			pvt->ops = &family_types[F19_M50H_CPUS].ops;
40640b8bf9cbSMarc Bevand 			fam_type->ctl_name = "F19h_M50h";
40650b8bf9cbSMarc Bevand 			break;
4066e2be5955SYazen Ghannam 		} else if (pvt->model >= 0xa0 && pvt->model <= 0xaf) {
4067e2be5955SYazen Ghannam 			fam_type = &family_types[F19_M10H_CPUS];
4068e2be5955SYazen Ghannam 			pvt->ops = &family_types[F19_M10H_CPUS].ops;
4069e2be5955SYazen Ghannam 			fam_type->ctl_name = "F19h_MA0h";
4070e2be5955SYazen Ghannam 			break;
4071b4210eabSYazen Ghannam 		}
40722eb61c91SYazen Ghannam 		fam_type	= &family_types[F19_CPUS];
40732eb61c91SYazen Ghannam 		pvt->ops	= &family_types[F19_CPUS].ops;
40742eb61c91SYazen Ghannam 		family_types[F19_CPUS].ctl_name = "F19h";
40752eb61c91SYazen Ghannam 		break;
40762eb61c91SYazen Ghannam 
4077395ae783SBorislav Petkov 	default:
407824f9a7feSBorislav Petkov 		amd64_err("Unsupported family!\n");
40790092b20dSBorislav Petkov 		return NULL;
4080395ae783SBorislav Petkov 	}
40810092b20dSBorislav Petkov 
40820092b20dSBorislav Petkov 	return fam_type;
4083395ae783SBorislav Petkov }
4084395ae783SBorislav Petkov 
4085e339f1ecSTakashi Iwai static const struct attribute_group *amd64_edac_attr_groups[] = {
4086e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_DEBUG
40872a28ceefSBorislav Petkov 	&dbg_group,
408861810096SBorislav Petkov 	&inj_group,
4089e339f1ecSTakashi Iwai #endif
4090e339f1ecSTakashi Iwai 	NULL
4091e339f1ecSTakashi Iwai };
4092e339f1ecSTakashi Iwai 
409380355a3bSYazen Ghannam static int hw_info_get(struct amd64_pvt *pvt)
40947d6034d3SDoug Thompson {
4095936fc3afSYazen Ghannam 	u16 pci_id1, pci_id2;
4096f00eb5ffSColin Ian King 	int ret;
4097395ae783SBorislav Petkov 
4098936fc3afSYazen Ghannam 	if (pvt->fam >= 0x17) {
40995e4c5527SYazen Ghannam 		pvt->umc = kcalloc(fam_type->max_mcs, sizeof(struct amd64_umc), GFP_KERNEL);
410080355a3bSYazen Ghannam 		if (!pvt->umc)
410180355a3bSYazen Ghannam 			return -ENOMEM;
4102936fc3afSYazen Ghannam 
4103936fc3afSYazen Ghannam 		pci_id1 = fam_type->f0_id;
4104936fc3afSYazen Ghannam 		pci_id2 = fam_type->f6_id;
4105936fc3afSYazen Ghannam 	} else {
4106936fc3afSYazen Ghannam 		pci_id1 = fam_type->f1_id;
4107936fc3afSYazen Ghannam 		pci_id2 = fam_type->f2_id;
4108936fc3afSYazen Ghannam 	}
4109936fc3afSYazen Ghannam 
411080355a3bSYazen Ghannam 	ret = reserve_mc_sibling_devs(pvt, pci_id1, pci_id2);
411180355a3bSYazen Ghannam 	if (ret)
411280355a3bSYazen Ghannam 		return ret;
41137d6034d3SDoug Thompson 
4114360b7f3cSBorislav Petkov 	read_mc_regs(pvt);
41157d6034d3SDoug Thompson 
411680355a3bSYazen Ghannam 	return 0;
411780355a3bSYazen Ghannam }
411880355a3bSYazen Ghannam 
411980355a3bSYazen Ghannam static void hw_info_put(struct amd64_pvt *pvt)
412080355a3bSYazen Ghannam {
412180355a3bSYazen Ghannam 	if (pvt->F0 || pvt->F1)
412280355a3bSYazen Ghannam 		free_mc_sibling_devs(pvt);
412380355a3bSYazen Ghannam 
412480355a3bSYazen Ghannam 	kfree(pvt->umc);
412580355a3bSYazen Ghannam }
412680355a3bSYazen Ghannam 
412780355a3bSYazen Ghannam static int init_one_instance(struct amd64_pvt *pvt)
412880355a3bSYazen Ghannam {
412980355a3bSYazen Ghannam 	struct mem_ctl_info *mci = NULL;
413080355a3bSYazen Ghannam 	struct edac_mc_layer layers[2];
413180355a3bSYazen Ghannam 	int ret = -EINVAL;
413280355a3bSYazen Ghannam 
41337d6034d3SDoug Thompson 	/*
41347d6034d3SDoug Thompson 	 * We need to determine how many memory channels there are. Then use
41357d6034d3SDoug Thompson 	 * that information for calculating the size of the dynamic instance
4136360b7f3cSBorislav Petkov 	 * tables in the 'mci' structure.
41377d6034d3SDoug Thompson 	 */
41387d6034d3SDoug Thompson 	pvt->channel_count = pvt->ops->early_channel_count(pvt);
41397d6034d3SDoug Thompson 	if (pvt->channel_count < 0)
414080355a3bSYazen Ghannam 		return ret;
41417d6034d3SDoug Thompson 
41427d6034d3SDoug Thompson 	ret = -ENOMEM;
4143ab5a503cSMauro Carvalho Chehab 	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
4144ab5a503cSMauro Carvalho Chehab 	layers[0].size = pvt->csels[0].b_cnt;
4145ab5a503cSMauro Carvalho Chehab 	layers[0].is_virt_csrow = true;
4146ab5a503cSMauro Carvalho Chehab 	layers[1].type = EDAC_MC_LAYER_CHANNEL;
4147f0a56c48SBorislav Petkov 
4148f0a56c48SBorislav Petkov 	/*
4149f0a56c48SBorislav Petkov 	 * Always allocate two channels since we can have setups with DIMMs on
4150f0a56c48SBorislav Petkov 	 * only one channel. Also, this simplifies handling later for the price
4151f0a56c48SBorislav Petkov 	 * of a couple of KBs tops.
4152f0a56c48SBorislav Petkov 	 */
41535e4c5527SYazen Ghannam 	layers[1].size = fam_type->max_mcs;
4154ab5a503cSMauro Carvalho Chehab 	layers[1].is_virt_csrow = false;
4155f0a56c48SBorislav Petkov 
415680355a3bSYazen Ghannam 	mci = edac_mc_alloc(pvt->mc_node_id, ARRAY_SIZE(layers), layers, 0);
41577d6034d3SDoug Thompson 	if (!mci)
415880355a3bSYazen Ghannam 		return ret;
41597d6034d3SDoug Thompson 
41607d6034d3SDoug Thompson 	mci->pvt_info = pvt;
41613f37a36bSBorislav Petkov 	mci->pdev = &pvt->F3->dev;
41627d6034d3SDoug Thompson 
416338ddd4d1SYazen Ghannam 	setup_mci_misc_attrs(mci);
4164360b7f3cSBorislav Petkov 
4165360b7f3cSBorislav Petkov 	if (init_csrows(mci))
41667d6034d3SDoug Thompson 		mci->edac_cap = EDAC_FLAG_NONE;
41677d6034d3SDoug Thompson 
41687d6034d3SDoug Thompson 	ret = -ENODEV;
4169e339f1ecSTakashi Iwai 	if (edac_mc_add_mc_with_groups(mci, amd64_edac_attr_groups)) {
4170956b9ba1SJoe Perches 		edac_dbg(1, "failed edac_mc_add_mc()\n");
417180355a3bSYazen Ghannam 		edac_mc_free(mci);
417280355a3bSYazen Ghannam 		return ret;
41737d6034d3SDoug Thompson 	}
41747d6034d3SDoug Thompson 
41757d6034d3SDoug Thompson 	return 0;
41767d6034d3SDoug Thompson }
41777d6034d3SDoug Thompson 
4178582f94b5SYazen Ghannam static bool instance_has_memory(struct amd64_pvt *pvt)
4179582f94b5SYazen Ghannam {
4180582f94b5SYazen Ghannam 	bool cs_enabled = false;
4181582f94b5SYazen Ghannam 	int cs = 0, dct = 0;
4182582f94b5SYazen Ghannam 
4183582f94b5SYazen Ghannam 	for (dct = 0; dct < fam_type->max_mcs; dct++) {
4184582f94b5SYazen Ghannam 		for_each_chip_select(cs, dct, pvt)
4185582f94b5SYazen Ghannam 			cs_enabled |= csrow_enabled(cs, dct, pvt);
4186582f94b5SYazen Ghannam 	}
4187582f94b5SYazen Ghannam 
4188582f94b5SYazen Ghannam 	return cs_enabled;
4189582f94b5SYazen Ghannam }
4190582f94b5SYazen Ghannam 
41913f37a36bSBorislav Petkov static int probe_one_instance(unsigned int nid)
41927d6034d3SDoug Thompson {
41932299ef71SBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
419480355a3bSYazen Ghannam 	struct amd64_pvt *pvt = NULL;
4195ae7bb7c6SBorislav Petkov 	struct ecc_settings *s;
41963f37a36bSBorislav Petkov 	int ret;
4197b8cfa02fSBorislav Petkov 
4198ae7bb7c6SBorislav Petkov 	ret = -ENOMEM;
4199ae7bb7c6SBorislav Petkov 	s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL);
4200ae7bb7c6SBorislav Petkov 	if (!s)
42012299ef71SBorislav Petkov 		goto err_out;
4202ae7bb7c6SBorislav Petkov 
4203ae7bb7c6SBorislav Petkov 	ecc_stngs[nid] = s;
4204ae7bb7c6SBorislav Petkov 
420580355a3bSYazen Ghannam 	pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
420680355a3bSYazen Ghannam 	if (!pvt)
420780355a3bSYazen Ghannam 		goto err_settings;
420880355a3bSYazen Ghannam 
420980355a3bSYazen Ghannam 	pvt->mc_node_id	= nid;
421080355a3bSYazen Ghannam 	pvt->F3 = F3;
421180355a3bSYazen Ghannam 
42126c13d7ffSBorislav Petkov 	ret = -ENODEV;
421380355a3bSYazen Ghannam 	fam_type = per_family_init(pvt);
421480355a3bSYazen Ghannam 	if (!fam_type)
421580355a3bSYazen Ghannam 		goto err_enable;
421680355a3bSYazen Ghannam 
421780355a3bSYazen Ghannam 	ret = hw_info_get(pvt);
421880355a3bSYazen Ghannam 	if (ret < 0)
421980355a3bSYazen Ghannam 		goto err_enable;
422080355a3bSYazen Ghannam 
42214688c9b4SYazen Ghannam 	ret = 0;
4222582f94b5SYazen Ghannam 	if (!instance_has_memory(pvt)) {
4223582f94b5SYazen Ghannam 		amd64_info("Node %d: No DIMMs detected.\n", nid);
4224582f94b5SYazen Ghannam 		goto err_enable;
4225582f94b5SYazen Ghannam 	}
4226582f94b5SYazen Ghannam 
4227582f94b5SYazen Ghannam 	if (!ecc_enabled(pvt)) {
4228582f94b5SYazen Ghannam 		ret = -ENODEV;
42292299ef71SBorislav Petkov 
42302299ef71SBorislav Petkov 		if (!ecc_enable_override)
42312299ef71SBorislav Petkov 			goto err_enable;
42322299ef71SBorislav Petkov 
4233044e7a41SYazen Ghannam 		if (boot_cpu_data.x86 >= 0x17) {
4234044e7a41SYazen Ghannam 			amd64_warn("Forcing ECC on is not recommended on newer systems. Please enable ECC in BIOS.");
4235044e7a41SYazen Ghannam 			goto err_enable;
4236044e7a41SYazen Ghannam 		} else
42372299ef71SBorislav Petkov 			amd64_warn("Forcing ECC on!\n");
42382299ef71SBorislav Petkov 
42392299ef71SBorislav Petkov 		if (!enable_ecc_error_reporting(s, nid, F3))
42402299ef71SBorislav Petkov 			goto err_enable;
42412299ef71SBorislav Petkov 	}
42422299ef71SBorislav Petkov 
424380355a3bSYazen Ghannam 	ret = init_one_instance(pvt);
4244360b7f3cSBorislav Petkov 	if (ret < 0) {
4245ae7bb7c6SBorislav Petkov 		amd64_err("Error probing instance: %d\n", nid);
4246044e7a41SYazen Ghannam 
4247044e7a41SYazen Ghannam 		if (boot_cpu_data.x86 < 0x17)
4248360b7f3cSBorislav Petkov 			restore_ecc_error_reporting(s, nid, F3);
42492b9b2c46SYazen Ghannam 
42502b9b2c46SYazen Ghannam 		goto err_enable;
4251360b7f3cSBorislav Petkov 	}
42527d6034d3SDoug Thompson 
42534cbcb73bSBorislav Petkov 	amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name,
42544cbcb73bSBorislav Petkov 		     (pvt->fam == 0xf ?
42554cbcb73bSBorislav Petkov 				(pvt->ext_model >= K8_REV_F  ? "revF or later "
42564cbcb73bSBorislav Petkov 							     : "revE or earlier ")
42574cbcb73bSBorislav Petkov 				 : ""), pvt->mc_node_id);
42584cbcb73bSBorislav Petkov 
4259582f94b5SYazen Ghannam 	dump_misc_regs(pvt);
4260582f94b5SYazen Ghannam 
42617d6034d3SDoug Thompson 	return ret;
42622299ef71SBorislav Petkov 
42632299ef71SBorislav Petkov err_enable:
426480355a3bSYazen Ghannam 	hw_info_put(pvt);
426580355a3bSYazen Ghannam 	kfree(pvt);
426680355a3bSYazen Ghannam 
426780355a3bSYazen Ghannam err_settings:
42682299ef71SBorislav Petkov 	kfree(s);
42692299ef71SBorislav Petkov 	ecc_stngs[nid] = NULL;
42702299ef71SBorislav Petkov 
42712299ef71SBorislav Petkov err_out:
42722299ef71SBorislav Petkov 	return ret;
42737d6034d3SDoug Thompson }
42747d6034d3SDoug Thompson 
42753f37a36bSBorislav Petkov static void remove_one_instance(unsigned int nid)
42767d6034d3SDoug Thompson {
4277360b7f3cSBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
4278360b7f3cSBorislav Petkov 	struct ecc_settings *s = ecc_stngs[nid];
42793f37a36bSBorislav Petkov 	struct mem_ctl_info *mci;
42803f37a36bSBorislav Petkov 	struct amd64_pvt *pvt;
42817d6034d3SDoug Thompson 
42827d6034d3SDoug Thompson 	/* Remove from EDAC CORE tracking list */
42833f37a36bSBorislav Petkov 	mci = edac_mc_del_mc(&F3->dev);
42847d6034d3SDoug Thompson 	if (!mci)
42857d6034d3SDoug Thompson 		return;
42867d6034d3SDoug Thompson 
42877d6034d3SDoug Thompson 	pvt = mci->pvt_info;
42887d6034d3SDoug Thompson 
4289360b7f3cSBorislav Petkov 	restore_ecc_error_reporting(s, nid, F3);
42907d6034d3SDoug Thompson 
4291360b7f3cSBorislav Petkov 	kfree(ecc_stngs[nid]);
4292360b7f3cSBorislav Petkov 	ecc_stngs[nid] = NULL;
4293ae7bb7c6SBorislav Petkov 
42947d6034d3SDoug Thompson 	/* Free the EDAC CORE resources */
42958f68ed97SBorislav Petkov 	mci->pvt_info = NULL;
42968f68ed97SBorislav Petkov 
429780355a3bSYazen Ghannam 	hw_info_put(pvt);
42988f68ed97SBorislav Petkov 	kfree(pvt);
42997d6034d3SDoug Thompson 	edac_mc_free(mci);
43007d6034d3SDoug Thompson }
43017d6034d3SDoug Thompson 
4302360b7f3cSBorislav Petkov static void setup_pci_device(void)
43037d6034d3SDoug Thompson {
4304d1ea71cdSBorislav Petkov 	if (pci_ctl)
43057d6034d3SDoug Thompson 		return;
43067d6034d3SDoug Thompson 
4307706657b1SBorislav Petkov 	pci_ctl = edac_pci_create_generic_ctl(pci_ctl_dev, EDAC_MOD_STR);
4308d1ea71cdSBorislav Petkov 	if (!pci_ctl) {
4309d1ea71cdSBorislav Petkov 		pr_warn("%s(): Unable to create PCI control\n", __func__);
4310d1ea71cdSBorislav Petkov 		pr_warn("%s(): PCI error report via EDAC not set\n", __func__);
43117d6034d3SDoug Thompson 	}
43127d6034d3SDoug Thompson }
43137d6034d3SDoug Thompson 
4314d6efab74SYazen Ghannam static const struct x86_cpu_id amd64_cpuids[] = {
431529842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x0F, NULL),
431629842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x10, NULL),
431729842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x15, NULL),
431829842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x16, NULL),
431929842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x17, NULL),
432029842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(HYGON,	0x18, NULL),
432129842621SThomas Gleixner 	X86_MATCH_VENDOR_FAM(AMD,	0x19, NULL),
4322d6efab74SYazen Ghannam 	{ }
4323d6efab74SYazen Ghannam };
4324d6efab74SYazen Ghannam MODULE_DEVICE_TABLE(x86cpu, amd64_cpuids);
4325d6efab74SYazen Ghannam 
43267d6034d3SDoug Thompson static int __init amd64_edac_init(void)
43277d6034d3SDoug Thompson {
4328301375e7SToshi Kani 	const char *owner;
4329360b7f3cSBorislav Petkov 	int err = -ENODEV;
43303f37a36bSBorislav Petkov 	int i;
43317d6034d3SDoug Thompson 
4332301375e7SToshi Kani 	owner = edac_get_owner();
4333301375e7SToshi Kani 	if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
4334301375e7SToshi Kani 		return -EBUSY;
4335301375e7SToshi Kani 
43361bd9900bSYazen Ghannam 	if (!x86_match_cpu(amd64_cpuids))
43371bd9900bSYazen Ghannam 		return -ENODEV;
43381bd9900bSYazen Ghannam 
43399653a5c7SHans Rosenfeld 	if (amd_cache_northbridges() < 0)
43401bd9900bSYazen Ghannam 		return -ENODEV;
43417d6034d3SDoug Thompson 
43426ba92feaSBorislav Petkov 	opstate_init();
43436ba92feaSBorislav Petkov 
4344cc4d8860SBorislav Petkov 	err = -ENOMEM;
43456396bb22SKees Cook 	ecc_stngs = kcalloc(amd_nb_num(), sizeof(ecc_stngs[0]), GFP_KERNEL);
43462ec591acSBorislav Petkov 	if (!ecc_stngs)
4347a9f0fbe2SBorislav Petkov 		goto err_free;
4348cc4d8860SBorislav Petkov 
434950542251SBorislav Petkov 	msrs = msrs_alloc();
435056b34b91SBorislav Petkov 	if (!msrs)
4351360b7f3cSBorislav Petkov 		goto err_free;
435250542251SBorislav Petkov 
43532287c636SYazen Ghannam 	for (i = 0; i < amd_nb_num(); i++) {
43542287c636SYazen Ghannam 		err = probe_one_instance(i);
43552287c636SYazen Ghannam 		if (err) {
43563f37a36bSBorislav Petkov 			/* unwind properly */
43573f37a36bSBorislav Petkov 			while (--i >= 0)
43583f37a36bSBorislav Petkov 				remove_one_instance(i);
43597d6034d3SDoug Thompson 
43603f37a36bSBorislav Petkov 			goto err_pci;
43613f37a36bSBorislav Petkov 		}
43622287c636SYazen Ghannam 	}
43637d6034d3SDoug Thompson 
43644688c9b4SYazen Ghannam 	if (!edac_has_mcs()) {
43654688c9b4SYazen Ghannam 		err = -ENODEV;
43664688c9b4SYazen Ghannam 		goto err_pci;
43674688c9b4SYazen Ghannam 	}
43684688c9b4SYazen Ghannam 
4369234365f5SYazen Ghannam 	/* register stuff with EDAC MCE */
4370234365f5SYazen Ghannam 	if (boot_cpu_data.x86 >= 0x17)
4371234365f5SYazen Ghannam 		amd_register_ecc_decoder(decode_umc_error);
4372234365f5SYazen Ghannam 	else
4373234365f5SYazen Ghannam 		amd_register_ecc_decoder(decode_bus_error);
4374234365f5SYazen Ghannam 
4375360b7f3cSBorislav Petkov 	setup_pci_device();
4376f5b10c45STomasz Pala 
4377f5b10c45STomasz Pala #ifdef CONFIG_X86_32
4378f5b10c45STomasz Pala 	amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR);
4379f5b10c45STomasz Pala #endif
4380f5b10c45STomasz Pala 
4381de0336b3SBorislav Petkov 	printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION);
4382de0336b3SBorislav Petkov 
43837d6034d3SDoug Thompson 	return 0;
43847d6034d3SDoug Thompson 
438556b34b91SBorislav Petkov err_pci:
4386706657b1SBorislav Petkov 	pci_ctl_dev = NULL;
4387706657b1SBorislav Petkov 
438856b34b91SBorislav Petkov 	msrs_free(msrs);
438956b34b91SBorislav Petkov 	msrs = NULL;
4390cc4d8860SBorislav Petkov 
4391360b7f3cSBorislav Petkov err_free:
4392360b7f3cSBorislav Petkov 	kfree(ecc_stngs);
4393360b7f3cSBorislav Petkov 	ecc_stngs = NULL;
4394360b7f3cSBorislav Petkov 
43957d6034d3SDoug Thompson 	return err;
43967d6034d3SDoug Thompson }
43977d6034d3SDoug Thompson 
43987d6034d3SDoug Thompson static void __exit amd64_edac_exit(void)
43997d6034d3SDoug Thompson {
44003f37a36bSBorislav Petkov 	int i;
44013f37a36bSBorislav Petkov 
4402d1ea71cdSBorislav Petkov 	if (pci_ctl)
4403d1ea71cdSBorislav Petkov 		edac_pci_release_generic_ctl(pci_ctl);
44047d6034d3SDoug Thompson 
4405234365f5SYazen Ghannam 	/* unregister from EDAC MCE */
4406234365f5SYazen Ghannam 	if (boot_cpu_data.x86 >= 0x17)
4407234365f5SYazen Ghannam 		amd_unregister_ecc_decoder(decode_umc_error);
4408234365f5SYazen Ghannam 	else
4409234365f5SYazen Ghannam 		amd_unregister_ecc_decoder(decode_bus_error);
4410234365f5SYazen Ghannam 
44113f37a36bSBorislav Petkov 	for (i = 0; i < amd_nb_num(); i++)
44123f37a36bSBorislav Petkov 		remove_one_instance(i);
441350542251SBorislav Petkov 
4414ae7bb7c6SBorislav Petkov 	kfree(ecc_stngs);
4415ae7bb7c6SBorislav Petkov 	ecc_stngs = NULL;
4416ae7bb7c6SBorislav Petkov 
4417706657b1SBorislav Petkov 	pci_ctl_dev = NULL;
4418706657b1SBorislav Petkov 
441950542251SBorislav Petkov 	msrs_free(msrs);
442050542251SBorislav Petkov 	msrs = NULL;
44217d6034d3SDoug Thompson }
44227d6034d3SDoug Thompson 
44237d6034d3SDoug Thompson module_init(amd64_edac_init);
44247d6034d3SDoug Thompson module_exit(amd64_edac_exit);
44257d6034d3SDoug Thompson 
44267d6034d3SDoug Thompson MODULE_LICENSE("GPL");
44277d6034d3SDoug Thompson MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, "
44287d6034d3SDoug Thompson 		"Dave Peterson, Thayne Harbaugh");
44297d6034d3SDoug Thompson MODULE_DESCRIPTION("MC support for AMD64 memory controllers - "
44307d6034d3SDoug Thompson 		EDAC_AMD64_VERSION);
44317d6034d3SDoug Thompson 
44327d6034d3SDoug Thompson module_param(edac_op_state, int, 0444);
44337d6034d3SDoug Thompson MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
4434