xref: /openbmc/linux/drivers/edac/amd64_edac.c (revision 6ba92fea)
12bc65418SDoug Thompson #include "amd64_edac.h"
223ac4ae8SAndreas Herrmann #include <asm/amd_nb.h>
32bc65418SDoug Thompson 
4d1ea71cdSBorislav Petkov static struct edac_pci_ctl_info *pci_ctl;
52bc65418SDoug Thompson 
62bc65418SDoug Thompson static int report_gart_errors;
72bc65418SDoug Thompson module_param(report_gart_errors, int, 0644);
82bc65418SDoug Thompson 
92bc65418SDoug Thompson /*
102bc65418SDoug Thompson  * Set by command line parameter. If BIOS has enabled the ECC, this override is
112bc65418SDoug Thompson  * cleared to prevent re-enabling the hardware by this driver.
122bc65418SDoug Thompson  */
132bc65418SDoug Thompson static int ecc_enable_override;
142bc65418SDoug Thompson module_param(ecc_enable_override, int, 0644);
152bc65418SDoug Thompson 
16a29d8b8eSTejun Heo static struct msr __percpu *msrs;
1750542251SBorislav Petkov 
182ec591acSBorislav Petkov /* Per-node stuff */
19ae7bb7c6SBorislav Petkov static struct ecc_settings **ecc_stngs;
202bc65418SDoug Thompson 
212bc65418SDoug Thompson /*
22b70ef010SBorislav Petkov  * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing
23b70ef010SBorislav Petkov  * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching-
24b70ef010SBorislav Petkov  * or higher value'.
25b70ef010SBorislav Petkov  *
26b70ef010SBorislav Petkov  *FIXME: Produce a better mapping/linearisation.
27b70ef010SBorislav Petkov  */
28c7e5301aSDaniel J Blueman static const struct scrubrate {
2939094443SBorislav Petkov        u32 scrubval;           /* bit pattern for scrub rate */
3039094443SBorislav Petkov        u32 bandwidth;          /* bandwidth consumed (bytes/sec) */
3139094443SBorislav Petkov } scrubrates[] = {
32b70ef010SBorislav Petkov 	{ 0x01, 1600000000UL},
33b70ef010SBorislav Petkov 	{ 0x02, 800000000UL},
34b70ef010SBorislav Petkov 	{ 0x03, 400000000UL},
35b70ef010SBorislav Petkov 	{ 0x04, 200000000UL},
36b70ef010SBorislav Petkov 	{ 0x05, 100000000UL},
37b70ef010SBorislav Petkov 	{ 0x06, 50000000UL},
38b70ef010SBorislav Petkov 	{ 0x07, 25000000UL},
39b70ef010SBorislav Petkov 	{ 0x08, 12284069UL},
40b70ef010SBorislav Petkov 	{ 0x09, 6274509UL},
41b70ef010SBorislav Petkov 	{ 0x0A, 3121951UL},
42b70ef010SBorislav Petkov 	{ 0x0B, 1560975UL},
43b70ef010SBorislav Petkov 	{ 0x0C, 781440UL},
44b70ef010SBorislav Petkov 	{ 0x0D, 390720UL},
45b70ef010SBorislav Petkov 	{ 0x0E, 195300UL},
46b70ef010SBorislav Petkov 	{ 0x0F, 97650UL},
47b70ef010SBorislav Petkov 	{ 0x10, 48854UL},
48b70ef010SBorislav Petkov 	{ 0x11, 24427UL},
49b70ef010SBorislav Petkov 	{ 0x12, 12213UL},
50b70ef010SBorislav Petkov 	{ 0x13, 6101UL},
51b70ef010SBorislav Petkov 	{ 0x14, 3051UL},
52b70ef010SBorislav Petkov 	{ 0x15, 1523UL},
53b70ef010SBorislav Petkov 	{ 0x16, 761UL},
54b70ef010SBorislav Petkov 	{ 0x00, 0UL},        /* scrubbing off */
55b70ef010SBorislav Petkov };
56b70ef010SBorislav Petkov 
5766fed2d4SBorislav Petkov int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset,
58b2b0c605SBorislav Petkov 			       u32 *val, const char *func)
59b2b0c605SBorislav Petkov {
60b2b0c605SBorislav Petkov 	int err = 0;
61b2b0c605SBorislav Petkov 
62b2b0c605SBorislav Petkov 	err = pci_read_config_dword(pdev, offset, val);
63b2b0c605SBorislav Petkov 	if (err)
64b2b0c605SBorislav Petkov 		amd64_warn("%s: error reading F%dx%03x.\n",
65b2b0c605SBorislav Petkov 			   func, PCI_FUNC(pdev->devfn), offset);
66b2b0c605SBorislav Petkov 
67b2b0c605SBorislav Petkov 	return err;
68b2b0c605SBorislav Petkov }
69b2b0c605SBorislav Petkov 
70b2b0c605SBorislav Petkov int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset,
71b2b0c605SBorislav Petkov 				u32 val, const char *func)
72b2b0c605SBorislav Petkov {
73b2b0c605SBorislav Petkov 	int err = 0;
74b2b0c605SBorislav Petkov 
75b2b0c605SBorislav Petkov 	err = pci_write_config_dword(pdev, offset, val);
76b2b0c605SBorislav Petkov 	if (err)
77b2b0c605SBorislav Petkov 		amd64_warn("%s: error writing to F%dx%03x.\n",
78b2b0c605SBorislav Petkov 			   func, PCI_FUNC(pdev->devfn), offset);
79b2b0c605SBorislav Petkov 
80b2b0c605SBorislav Petkov 	return err;
81b2b0c605SBorislav Petkov }
82b2b0c605SBorislav Petkov 
83b2b0c605SBorislav Petkov /*
8473ba8593SBorislav Petkov  * Select DCT to which PCI cfg accesses are routed
8573ba8593SBorislav Petkov  */
8673ba8593SBorislav Petkov static void f15h_select_dct(struct amd64_pvt *pvt, u8 dct)
8773ba8593SBorislav Petkov {
8873ba8593SBorislav Petkov 	u32 reg = 0;
8973ba8593SBorislav Petkov 
9073ba8593SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, &reg);
917981a28fSAravind Gopalakrishnan 	reg &= (pvt->model == 0x30) ? ~3 : ~1;
9273ba8593SBorislav Petkov 	reg |= dct;
9373ba8593SBorislav Petkov 	amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg);
9473ba8593SBorislav Petkov }
9573ba8593SBorislav Petkov 
967981a28fSAravind Gopalakrishnan /*
977981a28fSAravind Gopalakrishnan  *
987981a28fSAravind Gopalakrishnan  * Depending on the family, F2 DCT reads need special handling:
997981a28fSAravind Gopalakrishnan  *
1007981a28fSAravind Gopalakrishnan  * K8: has a single DCT only and no address offsets >= 0x100
1017981a28fSAravind Gopalakrishnan  *
1027981a28fSAravind Gopalakrishnan  * F10h: each DCT has its own set of regs
1037981a28fSAravind Gopalakrishnan  *	DCT0 -> F2x040..
1047981a28fSAravind Gopalakrishnan  *	DCT1 -> F2x140..
1057981a28fSAravind Gopalakrishnan  *
1067981a28fSAravind Gopalakrishnan  * F16h: has only 1 DCT
1077981a28fSAravind Gopalakrishnan  *
1087981a28fSAravind Gopalakrishnan  * F15h: we select which DCT we access using F1x10C[DctCfgSel]
1097981a28fSAravind Gopalakrishnan  */
1107981a28fSAravind Gopalakrishnan static inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct,
1117981a28fSAravind Gopalakrishnan 					 int offset, u32 *val)
112b2b0c605SBorislav Petkov {
1137981a28fSAravind Gopalakrishnan 	switch (pvt->fam) {
1147981a28fSAravind Gopalakrishnan 	case 0xf:
1157981a28fSAravind Gopalakrishnan 		if (dct || offset >= 0x100)
1167981a28fSAravind Gopalakrishnan 			return -EINVAL;
1177981a28fSAravind Gopalakrishnan 		break;
118b2b0c605SBorislav Petkov 
1197981a28fSAravind Gopalakrishnan 	case 0x10:
1207981a28fSAravind Gopalakrishnan 		if (dct) {
1217981a28fSAravind Gopalakrishnan 			/*
1227981a28fSAravind Gopalakrishnan 			 * Note: If ganging is enabled, barring the regs
1237981a28fSAravind Gopalakrishnan 			 * F2x[1,0]98 and F2x[1,0]9C; reads reads to F2x1xx
1247981a28fSAravind Gopalakrishnan 			 * return 0. (cf. Section 2.8.1 F10h BKDG)
1257981a28fSAravind Gopalakrishnan 			 */
1267981a28fSAravind Gopalakrishnan 			if (dct_ganging_enabled(pvt))
1277981a28fSAravind Gopalakrishnan 				return 0;
1287981a28fSAravind Gopalakrishnan 
1297981a28fSAravind Gopalakrishnan 			offset += 0x100;
130b2b0c605SBorislav Petkov 		}
1317981a28fSAravind Gopalakrishnan 		break;
132b2b0c605SBorislav Petkov 
1337981a28fSAravind Gopalakrishnan 	case 0x15:
1347981a28fSAravind Gopalakrishnan 		/*
1357981a28fSAravind Gopalakrishnan 		 * F15h: F2x1xx addresses do not map explicitly to DCT1.
1367981a28fSAravind Gopalakrishnan 		 * We should select which DCT we access using F1x10C[DctCfgSel]
1377981a28fSAravind Gopalakrishnan 		 */
1387981a28fSAravind Gopalakrishnan 		dct = (dct && pvt->model == 0x30) ? 3 : dct;
13973ba8593SBorislav Petkov 		f15h_select_dct(pvt, dct);
1407981a28fSAravind Gopalakrishnan 		break;
141b2b0c605SBorislav Petkov 
1427981a28fSAravind Gopalakrishnan 	case 0x16:
1437981a28fSAravind Gopalakrishnan 		if (dct)
1447981a28fSAravind Gopalakrishnan 			return -EINVAL;
1457981a28fSAravind Gopalakrishnan 		break;
1467981a28fSAravind Gopalakrishnan 
1477981a28fSAravind Gopalakrishnan 	default:
1487981a28fSAravind Gopalakrishnan 		break;
1497981a28fSAravind Gopalakrishnan 	}
1507981a28fSAravind Gopalakrishnan 	return amd64_read_pci_cfg(pvt->F2, offset, val);
151b2b0c605SBorislav Petkov }
152b2b0c605SBorislav Petkov 
153b70ef010SBorislav Petkov /*
1542bc65418SDoug Thompson  * Memory scrubber control interface. For K8, memory scrubbing is handled by
1552bc65418SDoug Thompson  * hardware and can involve L2 cache, dcache as well as the main memory. With
1562bc65418SDoug Thompson  * F10, this is extended to L3 cache scrubbing on CPU models sporting that
1572bc65418SDoug Thompson  * functionality.
1582bc65418SDoug Thompson  *
1592bc65418SDoug Thompson  * This causes the "units" for the scrubbing speed to vary from 64 byte blocks
1602bc65418SDoug Thompson  * (dram) over to cache lines. This is nasty, so we will use bandwidth in
1612bc65418SDoug Thompson  * bytes/sec for the setting.
1622bc65418SDoug Thompson  *
1632bc65418SDoug Thompson  * Currently, we only do dram scrubbing. If the scrubbing is done in software on
1642bc65418SDoug Thompson  * other archs, we might not have access to the caches directly.
1652bc65418SDoug Thompson  */
1662bc65418SDoug Thompson 
1672bc65418SDoug Thompson /*
1682bc65418SDoug Thompson  * scan the scrub rate mapping table for a close or matching bandwidth value to
1692bc65418SDoug Thompson  * issue. If requested is too big, then use last maximum value found.
1702bc65418SDoug Thompson  */
171da92110dSAravind Gopalakrishnan static int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate)
1722bc65418SDoug Thompson {
1732bc65418SDoug Thompson 	u32 scrubval;
1742bc65418SDoug Thompson 	int i;
1752bc65418SDoug Thompson 
1762bc65418SDoug Thompson 	/*
1772bc65418SDoug Thompson 	 * map the configured rate (new_bw) to a value specific to the AMD64
1782bc65418SDoug Thompson 	 * memory controller and apply to register. Search for the first
1792bc65418SDoug Thompson 	 * bandwidth entry that is greater or equal than the setting requested
1802bc65418SDoug Thompson 	 * and program that. If at last entry, turn off DRAM scrubbing.
181168bfeefSAndrew Morton 	 *
182168bfeefSAndrew Morton 	 * If no suitable bandwidth is found, turn off DRAM scrubbing entirely
183168bfeefSAndrew Morton 	 * by falling back to the last element in scrubrates[].
1842bc65418SDoug Thompson 	 */
185168bfeefSAndrew Morton 	for (i = 0; i < ARRAY_SIZE(scrubrates) - 1; i++) {
1862bc65418SDoug Thompson 		/*
1872bc65418SDoug Thompson 		 * skip scrub rates which aren't recommended
1882bc65418SDoug Thompson 		 * (see F10 BKDG, F3x58)
1892bc65418SDoug Thompson 		 */
190395ae783SBorislav Petkov 		if (scrubrates[i].scrubval < min_rate)
1912bc65418SDoug Thompson 			continue;
1922bc65418SDoug Thompson 
1932bc65418SDoug Thompson 		if (scrubrates[i].bandwidth <= new_bw)
1942bc65418SDoug Thompson 			break;
1952bc65418SDoug Thompson 	}
1962bc65418SDoug Thompson 
1972bc65418SDoug Thompson 	scrubval = scrubrates[i].scrubval;
1982bc65418SDoug Thompson 
199da92110dSAravind Gopalakrishnan 	if (pvt->fam == 0x15 && pvt->model == 0x60) {
200da92110dSAravind Gopalakrishnan 		f15h_select_dct(pvt, 0);
201da92110dSAravind Gopalakrishnan 		pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
202da92110dSAravind Gopalakrishnan 		f15h_select_dct(pvt, 1);
203da92110dSAravind Gopalakrishnan 		pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
204da92110dSAravind Gopalakrishnan 	} else {
205da92110dSAravind Gopalakrishnan 		pci_write_bits32(pvt->F3, SCRCTRL, scrubval, 0x001F);
206da92110dSAravind Gopalakrishnan 	}
2072bc65418SDoug Thompson 
20839094443SBorislav Petkov 	if (scrubval)
20939094443SBorislav Petkov 		return scrubrates[i].bandwidth;
21039094443SBorislav Petkov 
2112bc65418SDoug Thompson 	return 0;
2122bc65418SDoug Thompson }
2132bc65418SDoug Thompson 
214d1ea71cdSBorislav Petkov static int set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
2152bc65418SDoug Thompson {
2162bc65418SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
21787b3e0e6SBorislav Petkov 	u32 min_scrubrate = 0x5;
2182bc65418SDoug Thompson 
219a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
22087b3e0e6SBorislav Petkov 		min_scrubrate = 0x0;
22187b3e0e6SBorislav Petkov 
222da92110dSAravind Gopalakrishnan 	if (pvt->fam == 0x15) {
2233f0aba4fSBorislav Petkov 		/* Erratum #505 */
224da92110dSAravind Gopalakrishnan 		if (pvt->model < 0x10)
22573ba8593SBorislav Petkov 			f15h_select_dct(pvt, 0);
22673ba8593SBorislav Petkov 
227da92110dSAravind Gopalakrishnan 		if (pvt->model == 0x60)
228da92110dSAravind Gopalakrishnan 			min_scrubrate = 0x6;
229da92110dSAravind Gopalakrishnan 	}
230da92110dSAravind Gopalakrishnan 	return __set_scrub_rate(pvt, bw, min_scrubrate);
2312bc65418SDoug Thompson }
2322bc65418SDoug Thompson 
233d1ea71cdSBorislav Petkov static int get_scrub_rate(struct mem_ctl_info *mci)
2342bc65418SDoug Thompson {
2352bc65418SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
2362bc65418SDoug Thompson 	u32 scrubval = 0;
23739094443SBorislav Petkov 	int i, retval = -EINVAL;
2382bc65418SDoug Thompson 
239da92110dSAravind Gopalakrishnan 	if (pvt->fam == 0x15) {
2403f0aba4fSBorislav Petkov 		/* Erratum #505 */
241da92110dSAravind Gopalakrishnan 		if (pvt->model < 0x10)
24273ba8593SBorislav Petkov 			f15h_select_dct(pvt, 0);
24373ba8593SBorislav Petkov 
244da92110dSAravind Gopalakrishnan 		if (pvt->model == 0x60)
245da92110dSAravind Gopalakrishnan 			amd64_read_pci_cfg(pvt->F2, F15H_M60H_SCRCTRL, &scrubval);
246da92110dSAravind Gopalakrishnan 	} else
2475980bb9cSBorislav Petkov 		amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
2482bc65418SDoug Thompson 
2492bc65418SDoug Thompson 	scrubval = scrubval & 0x001F;
2502bc65418SDoug Thompson 
251926311fdSRoel Kluin 	for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
2522bc65418SDoug Thompson 		if (scrubrates[i].scrubval == scrubval) {
25339094443SBorislav Petkov 			retval = scrubrates[i].bandwidth;
2542bc65418SDoug Thompson 			break;
2552bc65418SDoug Thompson 		}
2562bc65418SDoug Thompson 	}
25739094443SBorislav Petkov 	return retval;
2582bc65418SDoug Thompson }
2592bc65418SDoug Thompson 
2606775763aSDoug Thompson /*
2617f19bf75SBorislav Petkov  * returns true if the SysAddr given by sys_addr matches the
2627f19bf75SBorislav Petkov  * DRAM base/limit associated with node_id
2636775763aSDoug Thompson  */
264d1ea71cdSBorislav Petkov static bool base_limit_match(struct amd64_pvt *pvt, u64 sys_addr, u8 nid)
2656775763aSDoug Thompson {
2667f19bf75SBorislav Petkov 	u64 addr;
2676775763aSDoug Thompson 
2686775763aSDoug Thompson 	/* The K8 treats this as a 40-bit value.  However, bits 63-40 will be
2696775763aSDoug Thompson 	 * all ones if the most significant implemented address bit is 1.
2706775763aSDoug Thompson 	 * Here we discard bits 63-40.  See section 3.4.2 of AMD publication
2716775763aSDoug Thompson 	 * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1
2726775763aSDoug Thompson 	 * Application Programming.
2736775763aSDoug Thompson 	 */
2746775763aSDoug Thompson 	addr = sys_addr & 0x000000ffffffffffull;
2756775763aSDoug Thompson 
2767f19bf75SBorislav Petkov 	return ((addr >= get_dram_base(pvt, nid)) &&
2777f19bf75SBorislav Petkov 		(addr <= get_dram_limit(pvt, nid)));
2786775763aSDoug Thompson }
2796775763aSDoug Thompson 
2806775763aSDoug Thompson /*
2816775763aSDoug Thompson  * Attempt to map a SysAddr to a node. On success, return a pointer to the
2826775763aSDoug Thompson  * mem_ctl_info structure for the node that the SysAddr maps to.
2836775763aSDoug Thompson  *
2846775763aSDoug Thompson  * On failure, return NULL.
2856775763aSDoug Thompson  */
2866775763aSDoug Thompson static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci,
2876775763aSDoug Thompson 						u64 sys_addr)
2886775763aSDoug Thompson {
2896775763aSDoug Thompson 	struct amd64_pvt *pvt;
290c7e5301aSDaniel J Blueman 	u8 node_id;
2916775763aSDoug Thompson 	u32 intlv_en, bits;
2926775763aSDoug Thompson 
2936775763aSDoug Thompson 	/*
2946775763aSDoug Thompson 	 * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section
2956775763aSDoug Thompson 	 * 3.4.4.2) registers to map the SysAddr to a node ID.
2966775763aSDoug Thompson 	 */
2976775763aSDoug Thompson 	pvt = mci->pvt_info;
2986775763aSDoug Thompson 
2996775763aSDoug Thompson 	/*
3006775763aSDoug Thompson 	 * The value of this field should be the same for all DRAM Base
3016775763aSDoug Thompson 	 * registers.  Therefore we arbitrarily choose to read it from the
3026775763aSDoug Thompson 	 * register for node 0.
3036775763aSDoug Thompson 	 */
3047f19bf75SBorislav Petkov 	intlv_en = dram_intlv_en(pvt, 0);
3056775763aSDoug Thompson 
3066775763aSDoug Thompson 	if (intlv_en == 0) {
3077f19bf75SBorislav Petkov 		for (node_id = 0; node_id < DRAM_RANGES; node_id++) {
308d1ea71cdSBorislav Petkov 			if (base_limit_match(pvt, sys_addr, node_id))
3096775763aSDoug Thompson 				goto found;
3106775763aSDoug Thompson 		}
3118edc5445SBorislav Petkov 		goto err_no_match;
3128edc5445SBorislav Petkov 	}
3136775763aSDoug Thompson 
31472f158feSBorislav Petkov 	if (unlikely((intlv_en != 0x01) &&
31572f158feSBorislav Petkov 		     (intlv_en != 0x03) &&
31672f158feSBorislav Petkov 		     (intlv_en != 0x07))) {
31724f9a7feSBorislav Petkov 		amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en);
3186775763aSDoug Thompson 		return NULL;
3196775763aSDoug Thompson 	}
3206775763aSDoug Thompson 
3216775763aSDoug Thompson 	bits = (((u32) sys_addr) >> 12) & intlv_en;
3226775763aSDoug Thompson 
3236775763aSDoug Thompson 	for (node_id = 0; ; ) {
3247f19bf75SBorislav Petkov 		if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits)
3256775763aSDoug Thompson 			break;	/* intlv_sel field matches */
3266775763aSDoug Thompson 
3277f19bf75SBorislav Petkov 		if (++node_id >= DRAM_RANGES)
3286775763aSDoug Thompson 			goto err_no_match;
3296775763aSDoug Thompson 	}
3306775763aSDoug Thompson 
3316775763aSDoug Thompson 	/* sanity test for sys_addr */
332d1ea71cdSBorislav Petkov 	if (unlikely(!base_limit_match(pvt, sys_addr, node_id))) {
33324f9a7feSBorislav Petkov 		amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address"
33424f9a7feSBorislav Petkov 			   "range for node %d with node interleaving enabled.\n",
3358edc5445SBorislav Petkov 			   __func__, sys_addr, node_id);
3366775763aSDoug Thompson 		return NULL;
3376775763aSDoug Thompson 	}
3386775763aSDoug Thompson 
3396775763aSDoug Thompson found:
340b487c33eSBorislav Petkov 	return edac_mc_find((int)node_id);
3416775763aSDoug Thompson 
3426775763aSDoug Thompson err_no_match:
343956b9ba1SJoe Perches 	edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n",
3446775763aSDoug Thompson 		 (unsigned long)sys_addr);
3456775763aSDoug Thompson 
3466775763aSDoug Thompson 	return NULL;
3476775763aSDoug Thompson }
348e2ce7255SDoug Thompson 
349e2ce7255SDoug Thompson /*
35011c75eadSBorislav Petkov  * compute the CS base address of the @csrow on the DRAM controller @dct.
35111c75eadSBorislav Petkov  * For details see F2x[5C:40] in the processor's BKDG
352e2ce7255SDoug Thompson  */
35311c75eadSBorislav Petkov static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct,
35411c75eadSBorislav Petkov 				 u64 *base, u64 *mask)
355e2ce7255SDoug Thompson {
35611c75eadSBorislav Petkov 	u64 csbase, csmask, base_bits, mask_bits;
35711c75eadSBorislav Petkov 	u8 addr_shift;
35811c75eadSBorislav Petkov 
35918b94f66SAravind Gopalakrishnan 	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
36011c75eadSBorislav Petkov 		csbase		= pvt->csels[dct].csbases[csrow];
36111c75eadSBorislav Petkov 		csmask		= pvt->csels[dct].csmasks[csrow];
36210ef6b0dSChen, Gong 		base_bits	= GENMASK_ULL(31, 21) | GENMASK_ULL(15, 9);
36310ef6b0dSChen, Gong 		mask_bits	= GENMASK_ULL(29, 21) | GENMASK_ULL(15, 9);
36411c75eadSBorislav Petkov 		addr_shift	= 4;
36594c1acf2SAravind Gopalakrishnan 
36694c1acf2SAravind Gopalakrishnan 	/*
36718b94f66SAravind Gopalakrishnan 	 * F16h and F15h, models 30h and later need two addr_shift values:
36818b94f66SAravind Gopalakrishnan 	 * 8 for high and 6 for low (cf. F16h BKDG).
36994c1acf2SAravind Gopalakrishnan 	 */
37018b94f66SAravind Gopalakrishnan 	} else if (pvt->fam == 0x16 ||
37118b94f66SAravind Gopalakrishnan 		  (pvt->fam == 0x15 && pvt->model >= 0x30)) {
37294c1acf2SAravind Gopalakrishnan 		csbase          = pvt->csels[dct].csbases[csrow];
37394c1acf2SAravind Gopalakrishnan 		csmask          = pvt->csels[dct].csmasks[csrow >> 1];
37494c1acf2SAravind Gopalakrishnan 
37510ef6b0dSChen, Gong 		*base  = (csbase & GENMASK_ULL(15,  5)) << 6;
37610ef6b0dSChen, Gong 		*base |= (csbase & GENMASK_ULL(30, 19)) << 8;
37794c1acf2SAravind Gopalakrishnan 
37894c1acf2SAravind Gopalakrishnan 		*mask = ~0ULL;
37994c1acf2SAravind Gopalakrishnan 		/* poke holes for the csmask */
38010ef6b0dSChen, Gong 		*mask &= ~((GENMASK_ULL(15, 5)  << 6) |
38110ef6b0dSChen, Gong 			   (GENMASK_ULL(30, 19) << 8));
38294c1acf2SAravind Gopalakrishnan 
38310ef6b0dSChen, Gong 		*mask |= (csmask & GENMASK_ULL(15, 5))  << 6;
38410ef6b0dSChen, Gong 		*mask |= (csmask & GENMASK_ULL(30, 19)) << 8;
38594c1acf2SAravind Gopalakrishnan 
38694c1acf2SAravind Gopalakrishnan 		return;
38711c75eadSBorislav Petkov 	} else {
38811c75eadSBorislav Petkov 		csbase		= pvt->csels[dct].csbases[csrow];
38911c75eadSBorislav Petkov 		csmask		= pvt->csels[dct].csmasks[csrow >> 1];
39011c75eadSBorislav Petkov 		addr_shift	= 8;
39111c75eadSBorislav Petkov 
392a4b4bedcSBorislav Petkov 		if (pvt->fam == 0x15)
39310ef6b0dSChen, Gong 			base_bits = mask_bits =
39410ef6b0dSChen, Gong 				GENMASK_ULL(30,19) | GENMASK_ULL(13,5);
39511c75eadSBorislav Petkov 		else
39610ef6b0dSChen, Gong 			base_bits = mask_bits =
39710ef6b0dSChen, Gong 				GENMASK_ULL(28,19) | GENMASK_ULL(13,5);
398e2ce7255SDoug Thompson 	}
399e2ce7255SDoug Thompson 
40011c75eadSBorislav Petkov 	*base  = (csbase & base_bits) << addr_shift;
401e2ce7255SDoug Thompson 
40211c75eadSBorislav Petkov 	*mask  = ~0ULL;
40311c75eadSBorislav Petkov 	/* poke holes for the csmask */
40411c75eadSBorislav Petkov 	*mask &= ~(mask_bits << addr_shift);
40511c75eadSBorislav Petkov 	/* OR them in */
40611c75eadSBorislav Petkov 	*mask |= (csmask & mask_bits) << addr_shift;
407e2ce7255SDoug Thompson }
408e2ce7255SDoug Thompson 
40911c75eadSBorislav Petkov #define for_each_chip_select(i, dct, pvt) \
41011c75eadSBorislav Petkov 	for (i = 0; i < pvt->csels[dct].b_cnt; i++)
41111c75eadSBorislav Petkov 
412614ec9d8SBorislav Petkov #define chip_select_base(i, dct, pvt) \
413614ec9d8SBorislav Petkov 	pvt->csels[dct].csbases[i]
414614ec9d8SBorislav Petkov 
41511c75eadSBorislav Petkov #define for_each_chip_select_mask(i, dct, pvt) \
41611c75eadSBorislav Petkov 	for (i = 0; i < pvt->csels[dct].m_cnt; i++)
41711c75eadSBorislav Petkov 
418e2ce7255SDoug Thompson /*
419e2ce7255SDoug Thompson  * @input_addr is an InputAddr associated with the node given by mci. Return the
420e2ce7255SDoug Thompson  * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr).
421e2ce7255SDoug Thompson  */
422e2ce7255SDoug Thompson static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr)
423e2ce7255SDoug Thompson {
424e2ce7255SDoug Thompson 	struct amd64_pvt *pvt;
425e2ce7255SDoug Thompson 	int csrow;
426e2ce7255SDoug Thompson 	u64 base, mask;
427e2ce7255SDoug Thompson 
428e2ce7255SDoug Thompson 	pvt = mci->pvt_info;
429e2ce7255SDoug Thompson 
43011c75eadSBorislav Petkov 	for_each_chip_select(csrow, 0, pvt) {
43111c75eadSBorislav Petkov 		if (!csrow_enabled(csrow, 0, pvt))
432e2ce7255SDoug Thompson 			continue;
433e2ce7255SDoug Thompson 
43411c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, csrow, 0, &base, &mask);
43511c75eadSBorislav Petkov 
43611c75eadSBorislav Petkov 		mask = ~mask;
437e2ce7255SDoug Thompson 
438e2ce7255SDoug Thompson 		if ((input_addr & mask) == (base & mask)) {
439956b9ba1SJoe Perches 			edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n",
440e2ce7255SDoug Thompson 				 (unsigned long)input_addr, csrow,
441e2ce7255SDoug Thompson 				 pvt->mc_node_id);
442e2ce7255SDoug Thompson 
443e2ce7255SDoug Thompson 			return csrow;
444e2ce7255SDoug Thompson 		}
445e2ce7255SDoug Thompson 	}
446956b9ba1SJoe Perches 	edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n",
447e2ce7255SDoug Thompson 		 (unsigned long)input_addr, pvt->mc_node_id);
448e2ce7255SDoug Thompson 
449e2ce7255SDoug Thompson 	return -1;
450e2ce7255SDoug Thompson }
451e2ce7255SDoug Thompson 
452e2ce7255SDoug Thompson /*
453e2ce7255SDoug Thompson  * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094)
454e2ce7255SDoug Thompson  * for the node represented by mci. Info is passed back in *hole_base,
455e2ce7255SDoug Thompson  * *hole_offset, and *hole_size.  Function returns 0 if info is valid or 1 if
456e2ce7255SDoug Thompson  * info is invalid. Info may be invalid for either of the following reasons:
457e2ce7255SDoug Thompson  *
458e2ce7255SDoug Thompson  * - The revision of the node is not E or greater.  In this case, the DRAM Hole
459e2ce7255SDoug Thompson  *   Address Register does not exist.
460e2ce7255SDoug Thompson  *
461e2ce7255SDoug Thompson  * - The DramHoleValid bit is cleared in the DRAM Hole Address Register,
462e2ce7255SDoug Thompson  *   indicating that its contents are not valid.
463e2ce7255SDoug Thompson  *
464e2ce7255SDoug Thompson  * The values passed back in *hole_base, *hole_offset, and *hole_size are
465e2ce7255SDoug Thompson  * complete 32-bit values despite the fact that the bitfields in the DHAR
466e2ce7255SDoug Thompson  * only represent bits 31-24 of the base and offset values.
467e2ce7255SDoug Thompson  */
468e2ce7255SDoug Thompson int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
469e2ce7255SDoug Thompson 			     u64 *hole_offset, u64 *hole_size)
470e2ce7255SDoug Thompson {
471e2ce7255SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
472e2ce7255SDoug Thompson 
473e2ce7255SDoug Thompson 	/* only revE and later have the DRAM Hole Address Register */
474a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_E) {
475956b9ba1SJoe Perches 		edac_dbg(1, "  revision %d for node %d does not support DHAR\n",
476e2ce7255SDoug Thompson 			 pvt->ext_model, pvt->mc_node_id);
477e2ce7255SDoug Thompson 		return 1;
478e2ce7255SDoug Thompson 	}
479e2ce7255SDoug Thompson 
480bc21fa57SBorislav Petkov 	/* valid for Fam10h and above */
481a4b4bedcSBorislav Petkov 	if (pvt->fam >= 0x10 && !dhar_mem_hoist_valid(pvt)) {
482956b9ba1SJoe Perches 		edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this system\n");
483e2ce7255SDoug Thompson 		return 1;
484e2ce7255SDoug Thompson 	}
485e2ce7255SDoug Thompson 
486c8e518d5SBorislav Petkov 	if (!dhar_valid(pvt)) {
487956b9ba1SJoe Perches 		edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this node %d\n",
488e2ce7255SDoug Thompson 			 pvt->mc_node_id);
489e2ce7255SDoug Thompson 		return 1;
490e2ce7255SDoug Thompson 	}
491e2ce7255SDoug Thompson 
492e2ce7255SDoug Thompson 	/* This node has Memory Hoisting */
493e2ce7255SDoug Thompson 
494e2ce7255SDoug Thompson 	/* +------------------+--------------------+--------------------+-----
495e2ce7255SDoug Thompson 	 * | memory           | DRAM hole          | relocated          |
496e2ce7255SDoug Thompson 	 * | [0, (x - 1)]     | [x, 0xffffffff]    | addresses from     |
497e2ce7255SDoug Thompson 	 * |                  |                    | DRAM hole          |
498e2ce7255SDoug Thompson 	 * |                  |                    | [0x100000000,      |
499e2ce7255SDoug Thompson 	 * |                  |                    |  (0x100000000+     |
500e2ce7255SDoug Thompson 	 * |                  |                    |   (0xffffffff-x))] |
501e2ce7255SDoug Thompson 	 * +------------------+--------------------+--------------------+-----
502e2ce7255SDoug Thompson 	 *
503e2ce7255SDoug Thompson 	 * Above is a diagram of physical memory showing the DRAM hole and the
504e2ce7255SDoug Thompson 	 * relocated addresses from the DRAM hole.  As shown, the DRAM hole
505e2ce7255SDoug Thompson 	 * starts at address x (the base address) and extends through address
506e2ce7255SDoug Thompson 	 * 0xffffffff.  The DRAM Hole Address Register (DHAR) relocates the
507e2ce7255SDoug Thompson 	 * addresses in the hole so that they start at 0x100000000.
508e2ce7255SDoug Thompson 	 */
509e2ce7255SDoug Thompson 
5101f31677eSBorislav Petkov 	*hole_base = dhar_base(pvt);
5111f31677eSBorislav Petkov 	*hole_size = (1ULL << 32) - *hole_base;
512e2ce7255SDoug Thompson 
513a4b4bedcSBorislav Petkov 	*hole_offset = (pvt->fam > 0xf) ? f10_dhar_offset(pvt)
514a4b4bedcSBorislav Petkov 					: k8_dhar_offset(pvt);
515e2ce7255SDoug Thompson 
516956b9ba1SJoe Perches 	edac_dbg(1, "  DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
517e2ce7255SDoug Thompson 		 pvt->mc_node_id, (unsigned long)*hole_base,
518e2ce7255SDoug Thompson 		 (unsigned long)*hole_offset, (unsigned long)*hole_size);
519e2ce7255SDoug Thompson 
520e2ce7255SDoug Thompson 	return 0;
521e2ce7255SDoug Thompson }
522e2ce7255SDoug Thompson EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info);
523e2ce7255SDoug Thompson 
52493c2df58SDoug Thompson /*
52593c2df58SDoug Thompson  * Return the DramAddr that the SysAddr given by @sys_addr maps to.  It is
52693c2df58SDoug Thompson  * assumed that sys_addr maps to the node given by mci.
52793c2df58SDoug Thompson  *
52893c2df58SDoug Thompson  * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section
52993c2df58SDoug Thompson  * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a
53093c2df58SDoug Thompson  * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled,
53193c2df58SDoug Thompson  * then it is also involved in translating a SysAddr to a DramAddr. Sections
53293c2df58SDoug Thompson  * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting.
53393c2df58SDoug Thompson  * These parts of the documentation are unclear. I interpret them as follows:
53493c2df58SDoug Thompson  *
53593c2df58SDoug Thompson  * When node n receives a SysAddr, it processes the SysAddr as follows:
53693c2df58SDoug Thompson  *
53793c2df58SDoug Thompson  * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM
53893c2df58SDoug Thompson  *    Limit registers for node n. If the SysAddr is not within the range
53993c2df58SDoug Thompson  *    specified by the base and limit values, then node n ignores the Sysaddr
54093c2df58SDoug Thompson  *    (since it does not map to node n). Otherwise continue to step 2 below.
54193c2df58SDoug Thompson  *
54293c2df58SDoug Thompson  * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is
54393c2df58SDoug Thompson  *    disabled so skip to step 3 below. Otherwise see if the SysAddr is within
54493c2df58SDoug Thompson  *    the range of relocated addresses (starting at 0x100000000) from the DRAM
54593c2df58SDoug Thompson  *    hole. If not, skip to step 3 below. Else get the value of the
54693c2df58SDoug Thompson  *    DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the
54793c2df58SDoug Thompson  *    offset defined by this value from the SysAddr.
54893c2df58SDoug Thompson  *
54993c2df58SDoug Thompson  * 3. Obtain the base address for node n from the DRAMBase field of the DRAM
55093c2df58SDoug Thompson  *    Base register for node n. To obtain the DramAddr, subtract the base
55193c2df58SDoug Thompson  *    address from the SysAddr, as shown near the start of section 3.4.4 (p.70).
55293c2df58SDoug Thompson  */
55393c2df58SDoug Thompson static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
55493c2df58SDoug Thompson {
5557f19bf75SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
55693c2df58SDoug Thompson 	u64 dram_base, hole_base, hole_offset, hole_size, dram_addr;
5571f31677eSBorislav Petkov 	int ret;
55893c2df58SDoug Thompson 
5597f19bf75SBorislav Petkov 	dram_base = get_dram_base(pvt, pvt->mc_node_id);
56093c2df58SDoug Thompson 
56193c2df58SDoug Thompson 	ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
56293c2df58SDoug Thompson 				      &hole_size);
56393c2df58SDoug Thompson 	if (!ret) {
5641f31677eSBorislav Petkov 		if ((sys_addr >= (1ULL << 32)) &&
5651f31677eSBorislav Petkov 		    (sys_addr < ((1ULL << 32) + hole_size))) {
56693c2df58SDoug Thompson 			/* use DHAR to translate SysAddr to DramAddr */
56793c2df58SDoug Thompson 			dram_addr = sys_addr - hole_offset;
56893c2df58SDoug Thompson 
569956b9ba1SJoe Perches 			edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
57093c2df58SDoug Thompson 				 (unsigned long)sys_addr,
57193c2df58SDoug Thompson 				 (unsigned long)dram_addr);
57293c2df58SDoug Thompson 
57393c2df58SDoug Thompson 			return dram_addr;
57493c2df58SDoug Thompson 		}
57593c2df58SDoug Thompson 	}
57693c2df58SDoug Thompson 
57793c2df58SDoug Thompson 	/*
57893c2df58SDoug Thompson 	 * Translate the SysAddr to a DramAddr as shown near the start of
57993c2df58SDoug Thompson 	 * section 3.4.4 (p. 70).  Although sys_addr is a 64-bit value, the k8
58093c2df58SDoug Thompson 	 * only deals with 40-bit values.  Therefore we discard bits 63-40 of
58193c2df58SDoug Thompson 	 * sys_addr below.  If bit 39 of sys_addr is 1 then the bits we
58293c2df58SDoug Thompson 	 * discard are all 1s.  Otherwise the bits we discard are all 0s.  See
58393c2df58SDoug Thompson 	 * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture
58493c2df58SDoug Thompson 	 * Programmer's Manual Volume 1 Application Programming.
58593c2df58SDoug Thompson 	 */
58610ef6b0dSChen, Gong 	dram_addr = (sys_addr & GENMASK_ULL(39, 0)) - dram_base;
58793c2df58SDoug Thompson 
588956b9ba1SJoe Perches 	edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
589956b9ba1SJoe Perches 		 (unsigned long)sys_addr, (unsigned long)dram_addr);
59093c2df58SDoug Thompson 	return dram_addr;
59193c2df58SDoug Thompson }
59293c2df58SDoug Thompson 
59393c2df58SDoug Thompson /*
59493c2df58SDoug Thompson  * @intlv_en is the value of the IntlvEn field from a DRAM Base register
59593c2df58SDoug Thompson  * (section 3.4.4.1).  Return the number of bits from a SysAddr that are used
59693c2df58SDoug Thompson  * for node interleaving.
59793c2df58SDoug Thompson  */
59893c2df58SDoug Thompson static int num_node_interleave_bits(unsigned intlv_en)
59993c2df58SDoug Thompson {
60093c2df58SDoug Thompson 	static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 };
60193c2df58SDoug Thompson 	int n;
60293c2df58SDoug Thompson 
60393c2df58SDoug Thompson 	BUG_ON(intlv_en > 7);
60493c2df58SDoug Thompson 	n = intlv_shift_table[intlv_en];
60593c2df58SDoug Thompson 	return n;
60693c2df58SDoug Thompson }
60793c2df58SDoug Thompson 
60893c2df58SDoug Thompson /* Translate the DramAddr given by @dram_addr to an InputAddr. */
60993c2df58SDoug Thompson static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
61093c2df58SDoug Thompson {
61193c2df58SDoug Thompson 	struct amd64_pvt *pvt;
61293c2df58SDoug Thompson 	int intlv_shift;
61393c2df58SDoug Thompson 	u64 input_addr;
61493c2df58SDoug Thompson 
61593c2df58SDoug Thompson 	pvt = mci->pvt_info;
61693c2df58SDoug Thompson 
61793c2df58SDoug Thompson 	/*
61893c2df58SDoug Thompson 	 * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
61993c2df58SDoug Thompson 	 * concerning translating a DramAddr to an InputAddr.
62093c2df58SDoug Thompson 	 */
6217f19bf75SBorislav Petkov 	intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
62210ef6b0dSChen, Gong 	input_addr = ((dram_addr >> intlv_shift) & GENMASK_ULL(35, 12)) +
62393c2df58SDoug Thompson 		      (dram_addr & 0xfff);
62493c2df58SDoug Thompson 
625956b9ba1SJoe Perches 	edac_dbg(2, "  Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
62693c2df58SDoug Thompson 		 intlv_shift, (unsigned long)dram_addr,
62793c2df58SDoug Thompson 		 (unsigned long)input_addr);
62893c2df58SDoug Thompson 
62993c2df58SDoug Thompson 	return input_addr;
63093c2df58SDoug Thompson }
63193c2df58SDoug Thompson 
63293c2df58SDoug Thompson /*
63393c2df58SDoug Thompson  * Translate the SysAddr represented by @sys_addr to an InputAddr.  It is
63493c2df58SDoug Thompson  * assumed that @sys_addr maps to the node given by mci.
63593c2df58SDoug Thompson  */
63693c2df58SDoug Thompson static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
63793c2df58SDoug Thompson {
63893c2df58SDoug Thompson 	u64 input_addr;
63993c2df58SDoug Thompson 
64093c2df58SDoug Thompson 	input_addr =
64193c2df58SDoug Thompson 	    dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
64293c2df58SDoug Thompson 
643c19ca6cbSMasanari Iida 	edac_dbg(2, "SysAddr 0x%lx translates to InputAddr 0x%lx\n",
64493c2df58SDoug Thompson 		 (unsigned long)sys_addr, (unsigned long)input_addr);
64593c2df58SDoug Thompson 
64693c2df58SDoug Thompson 	return input_addr;
64793c2df58SDoug Thompson }
64893c2df58SDoug Thompson 
64993c2df58SDoug Thompson /* Map the Error address to a PAGE and PAGE OFFSET. */
65093c2df58SDoug Thompson static inline void error_address_to_page_and_offset(u64 error_address,
65133ca0643SBorislav Petkov 						    struct err_info *err)
65293c2df58SDoug Thompson {
65333ca0643SBorislav Petkov 	err->page = (u32) (error_address >> PAGE_SHIFT);
65433ca0643SBorislav Petkov 	err->offset = ((u32) error_address) & ~PAGE_MASK;
65593c2df58SDoug Thompson }
65693c2df58SDoug Thompson 
65793c2df58SDoug Thompson /*
65893c2df58SDoug Thompson  * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address
65993c2df58SDoug Thompson  * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers
66093c2df58SDoug Thompson  * of a node that detected an ECC memory error.  mci represents the node that
66193c2df58SDoug Thompson  * the error address maps to (possibly different from the node that detected
66293c2df58SDoug Thompson  * the error).  Return the number of the csrow that sys_addr maps to, or -1 on
66393c2df58SDoug Thompson  * error.
66493c2df58SDoug Thompson  */
66593c2df58SDoug Thompson static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
66693c2df58SDoug Thompson {
66793c2df58SDoug Thompson 	int csrow;
66893c2df58SDoug Thompson 
66993c2df58SDoug Thompson 	csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr));
67093c2df58SDoug Thompson 
67193c2df58SDoug Thompson 	if (csrow == -1)
67224f9a7feSBorislav Petkov 		amd64_mc_err(mci, "Failed to translate InputAddr to csrow for "
67393c2df58SDoug Thompson 				  "address 0x%lx\n", (unsigned long)sys_addr);
67493c2df58SDoug Thompson 	return csrow;
67593c2df58SDoug Thompson }
676e2ce7255SDoug Thompson 
677bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
6782da11654SDoug Thompson 
6792da11654SDoug Thompson /*
6802da11654SDoug Thompson  * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
6812da11654SDoug Thompson  * are ECC capable.
6822da11654SDoug Thompson  */
683d1ea71cdSBorislav Petkov static unsigned long determine_edac_cap(struct amd64_pvt *pvt)
6842da11654SDoug Thompson {
685cb328507SBorislav Petkov 	u8 bit;
6861f6189edSDan Carpenter 	unsigned long edac_cap = EDAC_FLAG_NONE;
6872da11654SDoug Thompson 
688a4b4bedcSBorislav Petkov 	bit = (pvt->fam > 0xf || pvt->ext_model >= K8_REV_F)
6892da11654SDoug Thompson 		? 19
6902da11654SDoug Thompson 		: 17;
6912da11654SDoug Thompson 
692584fcff4SBorislav Petkov 	if (pvt->dclr0 & BIT(bit))
6932da11654SDoug Thompson 		edac_cap = EDAC_FLAG_SECDED;
6942da11654SDoug Thompson 
6952da11654SDoug Thompson 	return edac_cap;
6962da11654SDoug Thompson }
6972da11654SDoug Thompson 
698d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *, u8);
6992da11654SDoug Thompson 
700d1ea71cdSBorislav Petkov static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan)
70168798e17SBorislav Petkov {
702956b9ba1SJoe Perches 	edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
70368798e17SBorislav Petkov 
704a597d2a5SAravind Gopalakrishnan 	if (pvt->dram_type == MEM_LRDDR3) {
705a597d2a5SAravind Gopalakrishnan 		u32 dcsm = pvt->csels[chan].csmasks[0];
706a597d2a5SAravind Gopalakrishnan 		/*
707a597d2a5SAravind Gopalakrishnan 		 * It's assumed all LRDIMMs in a DCT are going to be of
708a597d2a5SAravind Gopalakrishnan 		 * same 'type' until proven otherwise. So, use a cs
709a597d2a5SAravind Gopalakrishnan 		 * value of '0' here to get dcsm value.
710a597d2a5SAravind Gopalakrishnan 		 */
711a597d2a5SAravind Gopalakrishnan 		edac_dbg(1, " LRDIMM %dx rank multiply\n", (dcsm & 0x3));
712a597d2a5SAravind Gopalakrishnan 	}
713a597d2a5SAravind Gopalakrishnan 
714a597d2a5SAravind Gopalakrishnan 	edac_dbg(1, "All DIMMs support ECC:%s\n",
71568798e17SBorislav Petkov 		    (dclr & BIT(19)) ? "yes" : "no");
71668798e17SBorislav Petkov 
717a597d2a5SAravind Gopalakrishnan 
718956b9ba1SJoe Perches 	edac_dbg(1, "  PAR/ERR parity: %s\n",
71968798e17SBorislav Petkov 		 (dclr & BIT(8)) ?  "enabled" : "disabled");
72068798e17SBorislav Petkov 
721a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10)
722956b9ba1SJoe Perches 		edac_dbg(1, "  DCT 128bit mode width: %s\n",
72368798e17SBorislav Petkov 			 (dclr & BIT(11)) ?  "128b" : "64b");
72468798e17SBorislav Petkov 
725956b9ba1SJoe Perches 	edac_dbg(1, "  x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
72668798e17SBorislav Petkov 		 (dclr & BIT(12)) ?  "yes" : "no",
72768798e17SBorislav Petkov 		 (dclr & BIT(13)) ?  "yes" : "no",
72868798e17SBorislav Petkov 		 (dclr & BIT(14)) ?  "yes" : "no",
72968798e17SBorislav Petkov 		 (dclr & BIT(15)) ?  "yes" : "no");
73068798e17SBorislav Petkov }
73168798e17SBorislav Petkov 
7322da11654SDoug Thompson /* Display and decode various NB registers for debug purposes. */
733b2b0c605SBorislav Petkov static void dump_misc_regs(struct amd64_pvt *pvt)
7342da11654SDoug Thompson {
735956b9ba1SJoe Perches 	edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
7362da11654SDoug Thompson 
737956b9ba1SJoe Perches 	edac_dbg(1, "  NB two channel DRAM capable: %s\n",
7385980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
73968798e17SBorislav Petkov 
740956b9ba1SJoe Perches 	edac_dbg(1, "  ECC capable: %s, ChipKill ECC capable: %s\n",
7415980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
7425980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
74368798e17SBorislav Petkov 
744d1ea71cdSBorislav Petkov 	debug_dump_dramcfg_low(pvt, pvt->dclr0, 0);
7452da11654SDoug Thompson 
746956b9ba1SJoe Perches 	edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
7472da11654SDoug Thompson 
748956b9ba1SJoe Perches 	edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n",
749bc21fa57SBorislav Petkov 		 pvt->dhar, dhar_base(pvt),
750a4b4bedcSBorislav Petkov 		 (pvt->fam == 0xf) ? k8_dhar_offset(pvt)
751bc21fa57SBorislav Petkov 				   : f10_dhar_offset(pvt));
7522da11654SDoug Thompson 
753956b9ba1SJoe Perches 	edac_dbg(1, "  DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
7542da11654SDoug Thompson 
755d1ea71cdSBorislav Petkov 	debug_display_dimm_sizes(pvt, 0);
7564d796364SBorislav Petkov 
7574d796364SBorislav Petkov 	/* everything below this point is Fam10h and above */
758a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
7592da11654SDoug Thompson 		return;
7604d796364SBorislav Petkov 
761d1ea71cdSBorislav Petkov 	debug_display_dimm_sizes(pvt, 1);
7622da11654SDoug Thompson 
763a3b7db09SBorislav Petkov 	amd64_info("using %s syndromes.\n", ((pvt->ecc_sym_sz == 8) ? "x8" : "x4"));
764ad6a32e9SBorislav Petkov 
7658de1d91eSBorislav Petkov 	/* Only if NOT ganged does dclr1 have valid info */
76668798e17SBorislav Petkov 	if (!dct_ganging_enabled(pvt))
767d1ea71cdSBorislav Petkov 		debug_dump_dramcfg_low(pvt, pvt->dclr1, 1);
7682da11654SDoug Thompson }
7692da11654SDoug Thompson 
77094be4bffSDoug Thompson /*
77118b94f66SAravind Gopalakrishnan  * See BKDG, F2x[1,0][5C:40], F2[1,0][6C:60]
77294be4bffSDoug Thompson  */
77311c75eadSBorislav Petkov static void prep_chip_selects(struct amd64_pvt *pvt)
77494be4bffSDoug Thompson {
77518b94f66SAravind Gopalakrishnan 	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
77611c75eadSBorislav Petkov 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
77711c75eadSBorislav Petkov 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8;
778a597d2a5SAravind Gopalakrishnan 	} else if (pvt->fam == 0x15 && pvt->model == 0x30) {
77918b94f66SAravind Gopalakrishnan 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4;
78018b94f66SAravind Gopalakrishnan 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2;
7819d858bb1SBorislav Petkov 	} else {
78211c75eadSBorislav Petkov 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
78311c75eadSBorislav Petkov 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4;
7849d858bb1SBorislav Petkov 	}
78594be4bffSDoug Thompson }
78694be4bffSDoug Thompson 
78794be4bffSDoug Thompson /*
78811c75eadSBorislav Petkov  * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers
78994be4bffSDoug Thompson  */
790b2b0c605SBorislav Petkov static void read_dct_base_mask(struct amd64_pvt *pvt)
79194be4bffSDoug Thompson {
79211c75eadSBorislav Petkov 	int cs;
79394be4bffSDoug Thompson 
79411c75eadSBorislav Petkov 	prep_chip_selects(pvt);
79594be4bffSDoug Thompson 
79611c75eadSBorislav Petkov 	for_each_chip_select(cs, 0, pvt) {
79771d2a32eSBorislav Petkov 		int reg0   = DCSB0 + (cs * 4);
79871d2a32eSBorislav Petkov 		int reg1   = DCSB1 + (cs * 4);
79911c75eadSBorislav Petkov 		u32 *base0 = &pvt->csels[0].csbases[cs];
80011c75eadSBorislav Petkov 		u32 *base1 = &pvt->csels[1].csbases[cs];
801b2b0c605SBorislav Petkov 
8027981a28fSAravind Gopalakrishnan 		if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0))
803956b9ba1SJoe Perches 			edac_dbg(0, "  DCSB0[%d]=0x%08x reg: F2x%x\n",
80411c75eadSBorislav Petkov 				 cs, *base0, reg0);
80594be4bffSDoug Thompson 
8067981a28fSAravind Gopalakrishnan 		if (pvt->fam == 0xf)
80711c75eadSBorislav Petkov 			continue;
808b2b0c605SBorislav Petkov 
8097981a28fSAravind Gopalakrishnan 		if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1))
810956b9ba1SJoe Perches 			edac_dbg(0, "  DCSB1[%d]=0x%08x reg: F2x%x\n",
8117981a28fSAravind Gopalakrishnan 				 cs, *base1, (pvt->fam == 0x10) ? reg1
8127981a28fSAravind Gopalakrishnan 								: reg0);
81394be4bffSDoug Thompson 	}
81494be4bffSDoug Thompson 
81511c75eadSBorislav Petkov 	for_each_chip_select_mask(cs, 0, pvt) {
81671d2a32eSBorislav Petkov 		int reg0   = DCSM0 + (cs * 4);
81771d2a32eSBorislav Petkov 		int reg1   = DCSM1 + (cs * 4);
81811c75eadSBorislav Petkov 		u32 *mask0 = &pvt->csels[0].csmasks[cs];
81911c75eadSBorislav Petkov 		u32 *mask1 = &pvt->csels[1].csmasks[cs];
820b2b0c605SBorislav Petkov 
8217981a28fSAravind Gopalakrishnan 		if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0))
822956b9ba1SJoe Perches 			edac_dbg(0, "    DCSM0[%d]=0x%08x reg: F2x%x\n",
82311c75eadSBorislav Petkov 				 cs, *mask0, reg0);
82494be4bffSDoug Thompson 
8257981a28fSAravind Gopalakrishnan 		if (pvt->fam == 0xf)
82611c75eadSBorislav Petkov 			continue;
827b2b0c605SBorislav Petkov 
8287981a28fSAravind Gopalakrishnan 		if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1))
829956b9ba1SJoe Perches 			edac_dbg(0, "    DCSM1[%d]=0x%08x reg: F2x%x\n",
8307981a28fSAravind Gopalakrishnan 				 cs, *mask1, (pvt->fam == 0x10) ? reg1
8317981a28fSAravind Gopalakrishnan 								: reg0);
83294be4bffSDoug Thompson 	}
8336ba5dcdcSBorislav Petkov }
83494be4bffSDoug Thompson 
835a597d2a5SAravind Gopalakrishnan static void determine_memory_type(struct amd64_pvt *pvt)
83694be4bffSDoug Thompson {
837a597d2a5SAravind Gopalakrishnan 	u32 dram_ctrl, dcsm;
83894be4bffSDoug Thompson 
839a597d2a5SAravind Gopalakrishnan 	switch (pvt->fam) {
840a597d2a5SAravind Gopalakrishnan 	case 0xf:
841a597d2a5SAravind Gopalakrishnan 		if (pvt->ext_model >= K8_REV_F)
842a597d2a5SAravind Gopalakrishnan 			goto ddr3;
843a597d2a5SAravind Gopalakrishnan 
844a597d2a5SAravind Gopalakrishnan 		pvt->dram_type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
845a597d2a5SAravind Gopalakrishnan 		return;
846a597d2a5SAravind Gopalakrishnan 
847a597d2a5SAravind Gopalakrishnan 	case 0x10:
8486b4c0bdeSBorislav Petkov 		if (pvt->dchr0 & DDR3_MODE)
849a597d2a5SAravind Gopalakrishnan 			goto ddr3;
850a597d2a5SAravind Gopalakrishnan 
851a597d2a5SAravind Gopalakrishnan 		pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
852a597d2a5SAravind Gopalakrishnan 		return;
853a597d2a5SAravind Gopalakrishnan 
854a597d2a5SAravind Gopalakrishnan 	case 0x15:
855a597d2a5SAravind Gopalakrishnan 		if (pvt->model < 0x60)
856a597d2a5SAravind Gopalakrishnan 			goto ddr3;
857a597d2a5SAravind Gopalakrishnan 
858a597d2a5SAravind Gopalakrishnan 		/*
859a597d2a5SAravind Gopalakrishnan 		 * Model 0x60h needs special handling:
860a597d2a5SAravind Gopalakrishnan 		 *
861a597d2a5SAravind Gopalakrishnan 		 * We use a Chip Select value of '0' to obtain dcsm.
862a597d2a5SAravind Gopalakrishnan 		 * Theoretically, it is possible to populate LRDIMMs of different
863a597d2a5SAravind Gopalakrishnan 		 * 'Rank' value on a DCT. But this is not the common case. So,
864a597d2a5SAravind Gopalakrishnan 		 * it's reasonable to assume all DIMMs are going to be of same
865a597d2a5SAravind Gopalakrishnan 		 * 'type' until proven otherwise.
866a597d2a5SAravind Gopalakrishnan 		 */
867a597d2a5SAravind Gopalakrishnan 		amd64_read_dct_pci_cfg(pvt, 0, DRAM_CONTROL, &dram_ctrl);
868a597d2a5SAravind Gopalakrishnan 		dcsm = pvt->csels[0].csmasks[0];
869a597d2a5SAravind Gopalakrishnan 
870a597d2a5SAravind Gopalakrishnan 		if (((dram_ctrl >> 8) & 0x7) == 0x2)
871a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_DDR4;
872a597d2a5SAravind Gopalakrishnan 		else if (pvt->dclr0 & BIT(16))
873a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_DDR3;
874a597d2a5SAravind Gopalakrishnan 		else if (dcsm & 0x3)
875a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_LRDDR3;
8766b4c0bdeSBorislav Petkov 		else
877a597d2a5SAravind Gopalakrishnan 			pvt->dram_type = MEM_RDDR3;
878a597d2a5SAravind Gopalakrishnan 
879a597d2a5SAravind Gopalakrishnan 		return;
880a597d2a5SAravind Gopalakrishnan 
881a597d2a5SAravind Gopalakrishnan 	case 0x16:
882a597d2a5SAravind Gopalakrishnan 		goto ddr3;
883a597d2a5SAravind Gopalakrishnan 
884a597d2a5SAravind Gopalakrishnan 	default:
885a597d2a5SAravind Gopalakrishnan 		WARN(1, KERN_ERR "%s: Family??? 0x%x\n", __func__, pvt->fam);
886a597d2a5SAravind Gopalakrishnan 		pvt->dram_type = MEM_EMPTY;
88794be4bffSDoug Thompson 	}
888a597d2a5SAravind Gopalakrishnan 	return;
88994be4bffSDoug Thompson 
890a597d2a5SAravind Gopalakrishnan ddr3:
891a597d2a5SAravind Gopalakrishnan 	pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
89294be4bffSDoug Thompson }
89394be4bffSDoug Thompson 
894cb328507SBorislav Petkov /* Get the number of DCT channels the memory controller is using. */
895ddff876dSDoug Thompson static int k8_early_channel_count(struct amd64_pvt *pvt)
896ddff876dSDoug Thompson {
897cb328507SBorislav Petkov 	int flag;
898ddff876dSDoug Thompson 
8999f56da0eSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F)
900ddff876dSDoug Thompson 		/* RevF (NPT) and later */
90141d8bfabSBorislav Petkov 		flag = pvt->dclr0 & WIDTH_128;
9029f56da0eSBorislav Petkov 	else
903ddff876dSDoug Thompson 		/* RevE and earlier */
904ddff876dSDoug Thompson 		flag = pvt->dclr0 & REVE_WIDTH_128;
905ddff876dSDoug Thompson 
906ddff876dSDoug Thompson 	/* not used */
907ddff876dSDoug Thompson 	pvt->dclr1 = 0;
908ddff876dSDoug Thompson 
909ddff876dSDoug Thompson 	return (flag) ? 2 : 1;
910ddff876dSDoug Thompson }
911ddff876dSDoug Thompson 
91270046624SBorislav Petkov /* On F10h and later ErrAddr is MC4_ADDR[47:1] */
913a4b4bedcSBorislav Petkov static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m)
914ddff876dSDoug Thompson {
9152ec591acSBorislav Petkov 	u16 mce_nid = amd_get_nb_id(m->extcpu);
9162ec591acSBorislav Petkov 	struct mem_ctl_info *mci;
91770046624SBorislav Petkov 	u8 start_bit = 1;
91870046624SBorislav Petkov 	u8 end_bit   = 47;
9192ec591acSBorislav Petkov 	u64 addr;
9202ec591acSBorislav Petkov 
9212ec591acSBorislav Petkov 	mci = edac_mc_find(mce_nid);
9222ec591acSBorislav Petkov 	if (!mci)
9232ec591acSBorislav Petkov 		return 0;
9242ec591acSBorislav Petkov 
9252ec591acSBorislav Petkov 	pvt = mci->pvt_info;
92670046624SBorislav Petkov 
927a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf) {
92870046624SBorislav Petkov 		start_bit = 3;
92970046624SBorislav Petkov 		end_bit   = 39;
93070046624SBorislav Petkov 	}
93170046624SBorislav Petkov 
93210ef6b0dSChen, Gong 	addr = m->addr & GENMASK_ULL(end_bit, start_bit);
933c1ae6830SBorislav Petkov 
934c1ae6830SBorislav Petkov 	/*
935c1ae6830SBorislav Petkov 	 * Erratum 637 workaround
936c1ae6830SBorislav Petkov 	 */
937a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x15) {
938c1ae6830SBorislav Petkov 		u64 cc6_base, tmp_addr;
939c1ae6830SBorislav Petkov 		u32 tmp;
9408b84c8dfSDaniel J Blueman 		u8 intlv_en;
941c1ae6830SBorislav Petkov 
94210ef6b0dSChen, Gong 		if ((addr & GENMASK_ULL(47, 24)) >> 24 != 0x00fdf7)
943c1ae6830SBorislav Petkov 			return addr;
944c1ae6830SBorislav Petkov 
945c1ae6830SBorislav Petkov 
946c1ae6830SBorislav Petkov 		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp);
947c1ae6830SBorislav Petkov 		intlv_en = tmp >> 21 & 0x7;
948c1ae6830SBorislav Petkov 
949c1ae6830SBorislav Petkov 		/* add [47:27] + 3 trailing bits */
95010ef6b0dSChen, Gong 		cc6_base  = (tmp & GENMASK_ULL(20, 0)) << 3;
951c1ae6830SBorislav Petkov 
952c1ae6830SBorislav Petkov 		/* reverse and add DramIntlvEn */
953c1ae6830SBorislav Petkov 		cc6_base |= intlv_en ^ 0x7;
954c1ae6830SBorislav Petkov 
955c1ae6830SBorislav Petkov 		/* pin at [47:24] */
956c1ae6830SBorislav Petkov 		cc6_base <<= 24;
957c1ae6830SBorislav Petkov 
958c1ae6830SBorislav Petkov 		if (!intlv_en)
95910ef6b0dSChen, Gong 			return cc6_base | (addr & GENMASK_ULL(23, 0));
960c1ae6830SBorislav Petkov 
961c1ae6830SBorislav Petkov 		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp);
962c1ae6830SBorislav Petkov 
963c1ae6830SBorislav Petkov 							/* faster log2 */
96410ef6b0dSChen, Gong 		tmp_addr  = (addr & GENMASK_ULL(23, 12)) << __fls(intlv_en + 1);
965c1ae6830SBorislav Petkov 
966c1ae6830SBorislav Petkov 		/* OR DramIntlvSel into bits [14:12] */
96710ef6b0dSChen, Gong 		tmp_addr |= (tmp & GENMASK_ULL(23, 21)) >> 9;
968c1ae6830SBorislav Petkov 
969c1ae6830SBorislav Petkov 		/* add remaining [11:0] bits from original MC4_ADDR */
97010ef6b0dSChen, Gong 		tmp_addr |= addr & GENMASK_ULL(11, 0);
971c1ae6830SBorislav Petkov 
972c1ae6830SBorislav Petkov 		return cc6_base | tmp_addr;
973c1ae6830SBorislav Petkov 	}
974c1ae6830SBorislav Petkov 
975c1ae6830SBorislav Petkov 	return addr;
976ddff876dSDoug Thompson }
977ddff876dSDoug Thompson 
978e2c0bffeSDaniel J Blueman static struct pci_dev *pci_get_related_function(unsigned int vendor,
979e2c0bffeSDaniel J Blueman 						unsigned int device,
980e2c0bffeSDaniel J Blueman 						struct pci_dev *related)
981e2c0bffeSDaniel J Blueman {
982e2c0bffeSDaniel J Blueman 	struct pci_dev *dev = NULL;
983e2c0bffeSDaniel J Blueman 
984e2c0bffeSDaniel J Blueman 	while ((dev = pci_get_device(vendor, device, dev))) {
985e2c0bffeSDaniel J Blueman 		if (pci_domain_nr(dev->bus) == pci_domain_nr(related->bus) &&
986e2c0bffeSDaniel J Blueman 		    (dev->bus->number == related->bus->number) &&
987e2c0bffeSDaniel J Blueman 		    (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn)))
988e2c0bffeSDaniel J Blueman 			break;
989e2c0bffeSDaniel J Blueman 	}
990e2c0bffeSDaniel J Blueman 
991e2c0bffeSDaniel J Blueman 	return dev;
992e2c0bffeSDaniel J Blueman }
993e2c0bffeSDaniel J Blueman 
9947f19bf75SBorislav Petkov static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
995ddff876dSDoug Thompson {
996e2c0bffeSDaniel J Blueman 	struct amd_northbridge *nb;
99718b94f66SAravind Gopalakrishnan 	struct pci_dev *f1 = NULL;
99818b94f66SAravind Gopalakrishnan 	unsigned int pci_func;
99971d2a32eSBorislav Petkov 	int off = range << 3;
1000e2c0bffeSDaniel J Blueman 	u32 llim;
1001ddff876dSDoug Thompson 
10027f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off,  &pvt->ranges[range].base.lo);
10037f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo);
1004ddff876dSDoug Thompson 
100518b94f66SAravind Gopalakrishnan 	if (pvt->fam == 0xf)
10067f19bf75SBorislav Petkov 		return;
1007ddff876dSDoug Thompson 
10087f19bf75SBorislav Petkov 	if (!dram_rw(pvt, range))
10097f19bf75SBorislav Petkov 		return;
1010ddff876dSDoug Thompson 
10117f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off,  &pvt->ranges[range].base.hi);
10127f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi);
1013f08e457cSBorislav Petkov 
1014e2c0bffeSDaniel J Blueman 	/* F15h: factor in CC6 save area by reading dst node's limit reg */
101518b94f66SAravind Gopalakrishnan 	if (pvt->fam != 0x15)
1016e2c0bffeSDaniel J Blueman 		return;
1017f08e457cSBorislav Petkov 
1018e2c0bffeSDaniel J Blueman 	nb = node_to_amd_nb(dram_dst_node(pvt, range));
1019e2c0bffeSDaniel J Blueman 	if (WARN_ON(!nb))
1020e2c0bffeSDaniel J Blueman 		return;
1021e2c0bffeSDaniel J Blueman 
1022a597d2a5SAravind Gopalakrishnan 	if (pvt->model == 0x60)
1023a597d2a5SAravind Gopalakrishnan 		pci_func = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1;
1024a597d2a5SAravind Gopalakrishnan 	else if (pvt->model == 0x30)
1025a597d2a5SAravind Gopalakrishnan 		pci_func = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1;
1026a597d2a5SAravind Gopalakrishnan 	else
1027a597d2a5SAravind Gopalakrishnan 		pci_func = PCI_DEVICE_ID_AMD_15H_NB_F1;
102818b94f66SAravind Gopalakrishnan 
102918b94f66SAravind Gopalakrishnan 	f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc);
1030f08e457cSBorislav Petkov 	if (WARN_ON(!f1))
1031f08e457cSBorislav Petkov 		return;
1032f08e457cSBorislav Petkov 
1033f08e457cSBorislav Petkov 	amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim);
1034f08e457cSBorislav Petkov 
103510ef6b0dSChen, Gong 	pvt->ranges[range].lim.lo &= GENMASK_ULL(15, 0);
1036f08e457cSBorislav Petkov 
1037f08e457cSBorislav Petkov 				    /* {[39:27],111b} */
1038f08e457cSBorislav Petkov 	pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16;
1039f08e457cSBorislav Petkov 
104010ef6b0dSChen, Gong 	pvt->ranges[range].lim.hi &= GENMASK_ULL(7, 0);
1041f08e457cSBorislav Petkov 
1042f08e457cSBorislav Petkov 				    /* [47:40] */
1043f08e457cSBorislav Petkov 	pvt->ranges[range].lim.hi |= llim >> 13;
1044f08e457cSBorislav Petkov 
1045f08e457cSBorislav Petkov 	pci_dev_put(f1);
1046f08e457cSBorislav Petkov }
1047ddff876dSDoug Thompson 
1048f192c7b1SBorislav Petkov static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
104933ca0643SBorislav Petkov 				    struct err_info *err)
1050ddff876dSDoug Thompson {
1051f192c7b1SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
1052ddff876dSDoug Thompson 
105333ca0643SBorislav Petkov 	error_address_to_page_and_offset(sys_addr, err);
1054ab5a503cSMauro Carvalho Chehab 
1055ab5a503cSMauro Carvalho Chehab 	/*
1056ab5a503cSMauro Carvalho Chehab 	 * Find out which node the error address belongs to. This may be
1057ab5a503cSMauro Carvalho Chehab 	 * different from the node that detected the error.
1058ab5a503cSMauro Carvalho Chehab 	 */
105933ca0643SBorislav Petkov 	err->src_mci = find_mc_by_sys_addr(mci, sys_addr);
106033ca0643SBorislav Petkov 	if (!err->src_mci) {
1061ab5a503cSMauro Carvalho Chehab 		amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
1062ab5a503cSMauro Carvalho Chehab 			     (unsigned long)sys_addr);
106333ca0643SBorislav Petkov 		err->err_code = ERR_NODE;
1064ab5a503cSMauro Carvalho Chehab 		return;
1065ab5a503cSMauro Carvalho Chehab 	}
1066ab5a503cSMauro Carvalho Chehab 
1067ab5a503cSMauro Carvalho Chehab 	/* Now map the sys_addr to a CSROW */
106833ca0643SBorislav Petkov 	err->csrow = sys_addr_to_csrow(err->src_mci, sys_addr);
106933ca0643SBorislav Petkov 	if (err->csrow < 0) {
107033ca0643SBorislav Petkov 		err->err_code = ERR_CSROW;
1071ab5a503cSMauro Carvalho Chehab 		return;
1072ab5a503cSMauro Carvalho Chehab 	}
1073ab5a503cSMauro Carvalho Chehab 
1074ddff876dSDoug Thompson 	/* CHIPKILL enabled */
1075f192c7b1SBorislav Petkov 	if (pvt->nbcfg & NBCFG_CHIPKILL) {
107633ca0643SBorislav Petkov 		err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
107733ca0643SBorislav Petkov 		if (err->channel < 0) {
1078ddff876dSDoug Thompson 			/*
1079ddff876dSDoug Thompson 			 * Syndrome didn't map, so we don't know which of the
1080ddff876dSDoug Thompson 			 * 2 DIMMs is in error. So we need to ID 'both' of them
1081ddff876dSDoug Thompson 			 * as suspect.
1082ddff876dSDoug Thompson 			 */
108333ca0643SBorislav Petkov 			amd64_mc_warn(err->src_mci, "unknown syndrome 0x%04x - "
1084ab5a503cSMauro Carvalho Chehab 				      "possible error reporting race\n",
108533ca0643SBorislav Petkov 				      err->syndrome);
108633ca0643SBorislav Petkov 			err->err_code = ERR_CHANNEL;
1087ddff876dSDoug Thompson 			return;
1088ddff876dSDoug Thompson 		}
1089ddff876dSDoug Thompson 	} else {
1090ddff876dSDoug Thompson 		/*
1091ddff876dSDoug Thompson 		 * non-chipkill ecc mode
1092ddff876dSDoug Thompson 		 *
1093ddff876dSDoug Thompson 		 * The k8 documentation is unclear about how to determine the
1094ddff876dSDoug Thompson 		 * channel number when using non-chipkill memory.  This method
1095ddff876dSDoug Thompson 		 * was obtained from email communication with someone at AMD.
1096ddff876dSDoug Thompson 		 * (Wish the email was placed in this comment - norsk)
1097ddff876dSDoug Thompson 		 */
109833ca0643SBorislav Petkov 		err->channel = ((sys_addr & BIT(3)) != 0);
1099ddff876dSDoug Thompson 	}
1100ddff876dSDoug Thompson }
1101ddff876dSDoug Thompson 
110241d8bfabSBorislav Petkov static int ddr2_cs_size(unsigned i, bool dct_width)
1103ddff876dSDoug Thompson {
110441d8bfabSBorislav Petkov 	unsigned shift = 0;
1105ddff876dSDoug Thompson 
110641d8bfabSBorislav Petkov 	if (i <= 2)
110741d8bfabSBorislav Petkov 		shift = i;
110841d8bfabSBorislav Petkov 	else if (!(i & 0x1))
110941d8bfabSBorislav Petkov 		shift = i >> 1;
11101433eb99SBorislav Petkov 	else
111141d8bfabSBorislav Petkov 		shift = (i + 1) >> 1;
1112ddff876dSDoug Thompson 
111341d8bfabSBorislav Petkov 	return 128 << (shift + !!dct_width);
111441d8bfabSBorislav Petkov }
111541d8bfabSBorislav Petkov 
111641d8bfabSBorislav Petkov static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1117a597d2a5SAravind Gopalakrishnan 				  unsigned cs_mode, int cs_mask_nr)
111841d8bfabSBorislav Petkov {
111941d8bfabSBorislav Petkov 	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
112041d8bfabSBorislav Petkov 
112141d8bfabSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F) {
112241d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 11);
112341d8bfabSBorislav Petkov 		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
112441d8bfabSBorislav Petkov 	}
112541d8bfabSBorislav Petkov 	else if (pvt->ext_model >= K8_REV_D) {
112611b0a314SBorislav Petkov 		unsigned diff;
112741d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 10);
112841d8bfabSBorislav Petkov 
112911b0a314SBorislav Petkov 		/*
113011b0a314SBorislav Petkov 		 * the below calculation, besides trying to win an obfuscated C
113111b0a314SBorislav Petkov 		 * contest, maps cs_mode values to DIMM chip select sizes. The
113211b0a314SBorislav Petkov 		 * mappings are:
113311b0a314SBorislav Petkov 		 *
113411b0a314SBorislav Petkov 		 * cs_mode	CS size (mb)
113511b0a314SBorislav Petkov 		 * =======	============
113611b0a314SBorislav Petkov 		 * 0		32
113711b0a314SBorislav Petkov 		 * 1		64
113811b0a314SBorislav Petkov 		 * 2		128
113911b0a314SBorislav Petkov 		 * 3		128
114011b0a314SBorislav Petkov 		 * 4		256
114111b0a314SBorislav Petkov 		 * 5		512
114211b0a314SBorislav Petkov 		 * 6		256
114311b0a314SBorislav Petkov 		 * 7		512
114411b0a314SBorislav Petkov 		 * 8		1024
114511b0a314SBorislav Petkov 		 * 9		1024
114611b0a314SBorislav Petkov 		 * 10		2048
114711b0a314SBorislav Petkov 		 *
114811b0a314SBorislav Petkov 		 * Basically, it calculates a value with which to shift the
114911b0a314SBorislav Petkov 		 * smallest CS size of 32MB.
115011b0a314SBorislav Petkov 		 *
115111b0a314SBorislav Petkov 		 * ddr[23]_cs_size have a similar purpose.
115211b0a314SBorislav Petkov 		 */
115311b0a314SBorislav Petkov 		diff = cs_mode/3 + (unsigned)(cs_mode > 5);
115411b0a314SBorislav Petkov 
115511b0a314SBorislav Petkov 		return 32 << (cs_mode - diff);
115641d8bfabSBorislav Petkov 	}
115741d8bfabSBorislav Petkov 	else {
115841d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 6);
115941d8bfabSBorislav Petkov 		return 32 << cs_mode;
116041d8bfabSBorislav Petkov 	}
1161ddff876dSDoug Thompson }
1162ddff876dSDoug Thompson 
11631afd3c98SDoug Thompson /*
11641afd3c98SDoug Thompson  * Get the number of DCT channels in use.
11651afd3c98SDoug Thompson  *
11661afd3c98SDoug Thompson  * Return:
11671afd3c98SDoug Thompson  *	number of Memory Channels in operation
11681afd3c98SDoug Thompson  * Pass back:
11691afd3c98SDoug Thompson  *	contents of the DCL0_LOW register
11701afd3c98SDoug Thompson  */
11717d20d14dSBorislav Petkov static int f1x_early_channel_count(struct amd64_pvt *pvt)
11721afd3c98SDoug Thompson {
11736ba5dcdcSBorislav Petkov 	int i, j, channels = 0;
1174ddff876dSDoug Thompson 
11757d20d14dSBorislav Petkov 	/* On F10h, if we are in 128 bit mode, then we are using 2 channels */
1176a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10 && (pvt->dclr0 & WIDTH_128))
11777d20d14dSBorislav Petkov 		return 2;
11781afd3c98SDoug Thompson 
11791afd3c98SDoug Thompson 	/*
1180d16149e8SBorislav Petkov 	 * Need to check if in unganged mode: In such, there are 2 channels,
1181d16149e8SBorislav Petkov 	 * but they are not in 128 bit mode and thus the above 'dclr0' status
1182d16149e8SBorislav Petkov 	 * bit will be OFF.
11831afd3c98SDoug Thompson 	 *
11841afd3c98SDoug Thompson 	 * Need to check DCT0[0] and DCT1[0] to see if only one of them has
11851afd3c98SDoug Thompson 	 * their CSEnable bit on. If so, then SINGLE DIMM case.
11861afd3c98SDoug Thompson 	 */
1187956b9ba1SJoe Perches 	edac_dbg(0, "Data width is not 128 bits - need more decoding\n");
11881afd3c98SDoug Thompson 
11891afd3c98SDoug Thompson 	/*
11901afd3c98SDoug Thompson 	 * Check DRAM Bank Address Mapping values for each DIMM to see if there
11911afd3c98SDoug Thompson 	 * is more than just one DIMM present in unganged mode. Need to check
11921afd3c98SDoug Thompson 	 * both controllers since DIMMs can be placed in either one.
11931afd3c98SDoug Thompson 	 */
1194525a1b20SBorislav Petkov 	for (i = 0; i < 2; i++) {
1195525a1b20SBorislav Petkov 		u32 dbam = (i ? pvt->dbam1 : pvt->dbam0);
11961afd3c98SDoug Thompson 
119757a30854SWan Wei 		for (j = 0; j < 4; j++) {
119857a30854SWan Wei 			if (DBAM_DIMM(j, dbam) > 0) {
11991afd3c98SDoug Thompson 				channels++;
120057a30854SWan Wei 				break;
12011afd3c98SDoug Thompson 			}
120257a30854SWan Wei 		}
120357a30854SWan Wei 	}
12041afd3c98SDoug Thompson 
1205d16149e8SBorislav Petkov 	if (channels > 2)
1206d16149e8SBorislav Petkov 		channels = 2;
1207d16149e8SBorislav Petkov 
120824f9a7feSBorislav Petkov 	amd64_info("MCT channel count: %d\n", channels);
12091afd3c98SDoug Thompson 
12101afd3c98SDoug Thompson 	return channels;
12111afd3c98SDoug Thompson }
12121afd3c98SDoug Thompson 
121341d8bfabSBorislav Petkov static int ddr3_cs_size(unsigned i, bool dct_width)
12141afd3c98SDoug Thompson {
121541d8bfabSBorislav Petkov 	unsigned shift = 0;
121641d8bfabSBorislav Petkov 	int cs_size = 0;
121741d8bfabSBorislav Petkov 
121841d8bfabSBorislav Petkov 	if (i == 0 || i == 3 || i == 4)
121941d8bfabSBorislav Petkov 		cs_size = -1;
122041d8bfabSBorislav Petkov 	else if (i <= 2)
122141d8bfabSBorislav Petkov 		shift = i;
122241d8bfabSBorislav Petkov 	else if (i == 12)
122341d8bfabSBorislav Petkov 		shift = 7;
122441d8bfabSBorislav Petkov 	else if (!(i & 0x1))
122541d8bfabSBorislav Petkov 		shift = i >> 1;
122641d8bfabSBorislav Petkov 	else
122741d8bfabSBorislav Petkov 		shift = (i + 1) >> 1;
122841d8bfabSBorislav Petkov 
122941d8bfabSBorislav Petkov 	if (cs_size != -1)
123041d8bfabSBorislav Petkov 		cs_size = (128 * (1 << !!dct_width)) << shift;
123141d8bfabSBorislav Petkov 
123241d8bfabSBorislav Petkov 	return cs_size;
123341d8bfabSBorislav Petkov }
123441d8bfabSBorislav Petkov 
1235a597d2a5SAravind Gopalakrishnan static int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply)
1236a597d2a5SAravind Gopalakrishnan {
1237a597d2a5SAravind Gopalakrishnan 	unsigned shift = 0;
1238a597d2a5SAravind Gopalakrishnan 	int cs_size = 0;
1239a597d2a5SAravind Gopalakrishnan 
1240a597d2a5SAravind Gopalakrishnan 	if (i < 4 || i == 6)
1241a597d2a5SAravind Gopalakrishnan 		cs_size = -1;
1242a597d2a5SAravind Gopalakrishnan 	else if (i == 12)
1243a597d2a5SAravind Gopalakrishnan 		shift = 7;
1244a597d2a5SAravind Gopalakrishnan 	else if (!(i & 0x1))
1245a597d2a5SAravind Gopalakrishnan 		shift = i >> 1;
1246a597d2a5SAravind Gopalakrishnan 	else
1247a597d2a5SAravind Gopalakrishnan 		shift = (i + 1) >> 1;
1248a597d2a5SAravind Gopalakrishnan 
1249a597d2a5SAravind Gopalakrishnan 	if (cs_size != -1)
1250a597d2a5SAravind Gopalakrishnan 		cs_size = rank_multiply * (128 << shift);
1251a597d2a5SAravind Gopalakrishnan 
1252a597d2a5SAravind Gopalakrishnan 	return cs_size;
1253a597d2a5SAravind Gopalakrishnan }
1254a597d2a5SAravind Gopalakrishnan 
1255a597d2a5SAravind Gopalakrishnan static int ddr4_cs_size(unsigned i)
1256a597d2a5SAravind Gopalakrishnan {
1257a597d2a5SAravind Gopalakrishnan 	int cs_size = 0;
1258a597d2a5SAravind Gopalakrishnan 
1259a597d2a5SAravind Gopalakrishnan 	if (i == 0)
1260a597d2a5SAravind Gopalakrishnan 		cs_size = -1;
1261a597d2a5SAravind Gopalakrishnan 	else if (i == 1)
1262a597d2a5SAravind Gopalakrishnan 		cs_size = 1024;
1263a597d2a5SAravind Gopalakrishnan 	else
1264a597d2a5SAravind Gopalakrishnan 		/* Min cs_size = 1G */
1265a597d2a5SAravind Gopalakrishnan 		cs_size = 1024 * (1 << (i >> 1));
1266a597d2a5SAravind Gopalakrishnan 
1267a597d2a5SAravind Gopalakrishnan 	return cs_size;
1268a597d2a5SAravind Gopalakrishnan }
1269a597d2a5SAravind Gopalakrishnan 
127041d8bfabSBorislav Petkov static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1271a597d2a5SAravind Gopalakrishnan 				   unsigned cs_mode, int cs_mask_nr)
127241d8bfabSBorislav Petkov {
127341d8bfabSBorislav Petkov 	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
127441d8bfabSBorislav Petkov 
127541d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 11);
12761433eb99SBorislav Petkov 
12771433eb99SBorislav Petkov 	if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
127841d8bfabSBorislav Petkov 		return ddr3_cs_size(cs_mode, dclr & WIDTH_128);
12791433eb99SBorislav Petkov 	else
128041d8bfabSBorislav Petkov 		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
128141d8bfabSBorislav Petkov }
12821433eb99SBorislav Petkov 
128341d8bfabSBorislav Petkov /*
128441d8bfabSBorislav Petkov  * F15h supports only 64bit DCT interfaces
128541d8bfabSBorislav Petkov  */
128641d8bfabSBorislav Petkov static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1287a597d2a5SAravind Gopalakrishnan 				   unsigned cs_mode, int cs_mask_nr)
128841d8bfabSBorislav Petkov {
128941d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 12);
129041d8bfabSBorislav Petkov 
129141d8bfabSBorislav Petkov 	return ddr3_cs_size(cs_mode, false);
12921afd3c98SDoug Thompson }
12931afd3c98SDoug Thompson 
1294a597d2a5SAravind Gopalakrishnan /* F15h M60h supports DDR4 mapping as well.. */
1295a597d2a5SAravind Gopalakrishnan static int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1296a597d2a5SAravind Gopalakrishnan 					unsigned cs_mode, int cs_mask_nr)
1297a597d2a5SAravind Gopalakrishnan {
1298a597d2a5SAravind Gopalakrishnan 	int cs_size;
1299a597d2a5SAravind Gopalakrishnan 	u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr];
1300a597d2a5SAravind Gopalakrishnan 
1301a597d2a5SAravind Gopalakrishnan 	WARN_ON(cs_mode > 12);
1302a597d2a5SAravind Gopalakrishnan 
1303a597d2a5SAravind Gopalakrishnan 	if (pvt->dram_type == MEM_DDR4) {
1304a597d2a5SAravind Gopalakrishnan 		if (cs_mode > 9)
1305a597d2a5SAravind Gopalakrishnan 			return -1;
1306a597d2a5SAravind Gopalakrishnan 
1307a597d2a5SAravind Gopalakrishnan 		cs_size = ddr4_cs_size(cs_mode);
1308a597d2a5SAravind Gopalakrishnan 	} else if (pvt->dram_type == MEM_LRDDR3) {
1309a597d2a5SAravind Gopalakrishnan 		unsigned rank_multiply = dcsm & 0xf;
1310a597d2a5SAravind Gopalakrishnan 
1311a597d2a5SAravind Gopalakrishnan 		if (rank_multiply == 3)
1312a597d2a5SAravind Gopalakrishnan 			rank_multiply = 4;
1313a597d2a5SAravind Gopalakrishnan 		cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply);
1314a597d2a5SAravind Gopalakrishnan 	} else {
1315a597d2a5SAravind Gopalakrishnan 		/* Minimum cs size is 512mb for F15hM60h*/
1316a597d2a5SAravind Gopalakrishnan 		if (cs_mode == 0x1)
1317a597d2a5SAravind Gopalakrishnan 			return -1;
1318a597d2a5SAravind Gopalakrishnan 
1319a597d2a5SAravind Gopalakrishnan 		cs_size = ddr3_cs_size(cs_mode, false);
1320a597d2a5SAravind Gopalakrishnan 	}
1321a597d2a5SAravind Gopalakrishnan 
1322a597d2a5SAravind Gopalakrishnan 	return cs_size;
1323a597d2a5SAravind Gopalakrishnan }
1324a597d2a5SAravind Gopalakrishnan 
132594c1acf2SAravind Gopalakrishnan /*
132618b94f66SAravind Gopalakrishnan  * F16h and F15h model 30h have only limited cs_modes.
132794c1acf2SAravind Gopalakrishnan  */
132894c1acf2SAravind Gopalakrishnan static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1329a597d2a5SAravind Gopalakrishnan 				unsigned cs_mode, int cs_mask_nr)
133094c1acf2SAravind Gopalakrishnan {
133194c1acf2SAravind Gopalakrishnan 	WARN_ON(cs_mode > 12);
133294c1acf2SAravind Gopalakrishnan 
133394c1acf2SAravind Gopalakrishnan 	if (cs_mode == 6 || cs_mode == 8 ||
133494c1acf2SAravind Gopalakrishnan 	    cs_mode == 9 || cs_mode == 12)
133594c1acf2SAravind Gopalakrishnan 		return -1;
133694c1acf2SAravind Gopalakrishnan 	else
133794c1acf2SAravind Gopalakrishnan 		return ddr3_cs_size(cs_mode, false);
133894c1acf2SAravind Gopalakrishnan }
133994c1acf2SAravind Gopalakrishnan 
13405a5d2371SBorislav Petkov static void read_dram_ctl_register(struct amd64_pvt *pvt)
13416163b5d4SDoug Thompson {
13426163b5d4SDoug Thompson 
1343a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
13445a5d2371SBorislav Petkov 		return;
13455a5d2371SBorislav Petkov 
13467981a28fSAravind Gopalakrishnan 	if (!amd64_read_pci_cfg(pvt->F2, DCT_SEL_LO, &pvt->dct_sel_lo)) {
1347956b9ba1SJoe Perches 		edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
134878da121eSBorislav Petkov 			 pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
13496163b5d4SDoug Thompson 
1350956b9ba1SJoe Perches 		edac_dbg(0, "  DCTs operate in %s mode\n",
13515a5d2371SBorislav Petkov 			 (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
13526163b5d4SDoug Thompson 
135372381bd5SBorislav Petkov 		if (!dct_ganging_enabled(pvt))
1354956b9ba1SJoe Perches 			edac_dbg(0, "  Address range split per DCT: %s\n",
135572381bd5SBorislav Petkov 				 (dct_high_range_enabled(pvt) ? "yes" : "no"));
135672381bd5SBorislav Petkov 
1357956b9ba1SJoe Perches 		edac_dbg(0, "  data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n",
135872381bd5SBorislav Petkov 			 (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
135972381bd5SBorislav Petkov 			 (dct_memory_cleared(pvt) ? "yes" : "no"));
136072381bd5SBorislav Petkov 
1361956b9ba1SJoe Perches 		edac_dbg(0, "  channel interleave: %s, "
136278da121eSBorislav Petkov 			 "interleave bits selector: 0x%x\n",
136372381bd5SBorislav Petkov 			 (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
13646163b5d4SDoug Thompson 			 dct_sel_interleave_addr(pvt));
13656163b5d4SDoug Thompson 	}
13666163b5d4SDoug Thompson 
13677981a28fSAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F2, DCT_SEL_HI, &pvt->dct_sel_hi);
13686163b5d4SDoug Thompson }
13696163b5d4SDoug Thompson 
1370f71d0a05SDoug Thompson /*
137118b94f66SAravind Gopalakrishnan  * Determine channel (DCT) based on the interleaving mode (see F15h M30h BKDG,
137218b94f66SAravind Gopalakrishnan  * 2.10.12 Memory Interleaving Modes).
137318b94f66SAravind Gopalakrishnan  */
137418b94f66SAravind Gopalakrishnan static u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
137518b94f66SAravind Gopalakrishnan 				     u8 intlv_en, int num_dcts_intlv,
137618b94f66SAravind Gopalakrishnan 				     u32 dct_sel)
137718b94f66SAravind Gopalakrishnan {
137818b94f66SAravind Gopalakrishnan 	u8 channel = 0;
137918b94f66SAravind Gopalakrishnan 	u8 select;
138018b94f66SAravind Gopalakrishnan 
138118b94f66SAravind Gopalakrishnan 	if (!(intlv_en))
138218b94f66SAravind Gopalakrishnan 		return (u8)(dct_sel);
138318b94f66SAravind Gopalakrishnan 
138418b94f66SAravind Gopalakrishnan 	if (num_dcts_intlv == 2) {
138518b94f66SAravind Gopalakrishnan 		select = (sys_addr >> 8) & 0x3;
138618b94f66SAravind Gopalakrishnan 		channel = select ? 0x3 : 0;
13879d0e8d83SAravind Gopalakrishnan 	} else if (num_dcts_intlv == 4) {
13889d0e8d83SAravind Gopalakrishnan 		u8 intlv_addr = dct_sel_interleave_addr(pvt);
13899d0e8d83SAravind Gopalakrishnan 		switch (intlv_addr) {
13909d0e8d83SAravind Gopalakrishnan 		case 0x4:
13919d0e8d83SAravind Gopalakrishnan 			channel = (sys_addr >> 8) & 0x3;
13929d0e8d83SAravind Gopalakrishnan 			break;
13939d0e8d83SAravind Gopalakrishnan 		case 0x5:
13949d0e8d83SAravind Gopalakrishnan 			channel = (sys_addr >> 9) & 0x3;
13959d0e8d83SAravind Gopalakrishnan 			break;
13969d0e8d83SAravind Gopalakrishnan 		}
13979d0e8d83SAravind Gopalakrishnan 	}
139818b94f66SAravind Gopalakrishnan 	return channel;
139918b94f66SAravind Gopalakrishnan }
140018b94f66SAravind Gopalakrishnan 
140118b94f66SAravind Gopalakrishnan /*
1402229a7a11SBorislav Petkov  * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory
1403f71d0a05SDoug Thompson  * Interleaving Modes.
1404f71d0a05SDoug Thompson  */
1405b15f0fcaSBorislav Petkov static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
1406229a7a11SBorislav Petkov 				bool hi_range_sel, u8 intlv_en)
14076163b5d4SDoug Thompson {
1408151fa71cSBorislav Petkov 	u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1;
14096163b5d4SDoug Thompson 
14106163b5d4SDoug Thompson 	if (dct_ganging_enabled(pvt))
1411229a7a11SBorislav Petkov 		return 0;
1412229a7a11SBorislav Petkov 
1413229a7a11SBorislav Petkov 	if (hi_range_sel)
1414229a7a11SBorislav Petkov 		return dct_sel_high;
1415229a7a11SBorislav Petkov 
1416f71d0a05SDoug Thompson 	/*
1417f71d0a05SDoug Thompson 	 * see F2x110[DctSelIntLvAddr] - channel interleave mode
1418f71d0a05SDoug Thompson 	 */
1419229a7a11SBorislav Petkov 	if (dct_interleave_enabled(pvt)) {
1420229a7a11SBorislav Petkov 		u8 intlv_addr = dct_sel_interleave_addr(pvt);
14216163b5d4SDoug Thompson 
1422229a7a11SBorislav Petkov 		/* return DCT select function: 0=DCT0, 1=DCT1 */
1423229a7a11SBorislav Petkov 		if (!intlv_addr)
1424229a7a11SBorislav Petkov 			return sys_addr >> 6 & 1;
14256163b5d4SDoug Thompson 
1426229a7a11SBorislav Petkov 		if (intlv_addr & 0x2) {
1427229a7a11SBorislav Petkov 			u8 shift = intlv_addr & 0x1 ? 9 : 6;
1428229a7a11SBorislav Petkov 			u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) % 2;
1429229a7a11SBorislav Petkov 
1430229a7a11SBorislav Petkov 			return ((sys_addr >> shift) & 1) ^ temp;
14316163b5d4SDoug Thompson 		}
14326163b5d4SDoug Thompson 
1433229a7a11SBorislav Petkov 		return (sys_addr >> (12 + hweight8(intlv_en))) & 1;
1434229a7a11SBorislav Petkov 	}
1435229a7a11SBorislav Petkov 
1436229a7a11SBorislav Petkov 	if (dct_high_range_enabled(pvt))
1437229a7a11SBorislav Petkov 		return ~dct_sel_high & 1;
14386163b5d4SDoug Thompson 
14396163b5d4SDoug Thompson 	return 0;
14406163b5d4SDoug Thompson }
14416163b5d4SDoug Thompson 
1442c8e518d5SBorislav Petkov /* Convert the sys_addr to the normalized DCT address */
1443c7e5301aSDaniel J Blueman static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range,
1444c8e518d5SBorislav Petkov 				 u64 sys_addr, bool hi_rng,
1445c8e518d5SBorislav Petkov 				 u32 dct_sel_base_addr)
14466163b5d4SDoug Thompson {
14476163b5d4SDoug Thompson 	u64 chan_off;
1448c8e518d5SBorislav Petkov 	u64 dram_base		= get_dram_base(pvt, range);
1449c8e518d5SBorislav Petkov 	u64 hole_off		= f10_dhar_offset(pvt);
14506f3508f6SDan Carpenter 	u64 dct_sel_base_off	= (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16;
14516163b5d4SDoug Thompson 
1452c8e518d5SBorislav Petkov 	if (hi_rng) {
1453c8e518d5SBorislav Petkov 		/*
1454c8e518d5SBorislav Petkov 		 * if
1455c8e518d5SBorislav Petkov 		 * base address of high range is below 4Gb
1456c8e518d5SBorislav Petkov 		 * (bits [47:27] at [31:11])
1457c8e518d5SBorislav Petkov 		 * DRAM address space on this DCT is hoisted above 4Gb	&&
1458c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1459c8e518d5SBorislav Petkov 		 *
1460c8e518d5SBorislav Petkov 		 *	remove hole offset from sys_addr
1461c8e518d5SBorislav Petkov 		 * else
1462c8e518d5SBorislav Petkov 		 *	remove high range offset from sys_addr
1463c8e518d5SBorislav Petkov 		 */
1464c8e518d5SBorislav Petkov 		if ((!(dct_sel_base_addr >> 16) ||
1465c8e518d5SBorislav Petkov 		     dct_sel_base_addr < dhar_base(pvt)) &&
1466972ea17aSBorislav Petkov 		    dhar_valid(pvt) &&
1467c8e518d5SBorislav Petkov 		    (sys_addr >= BIT_64(32)))
1468bc21fa57SBorislav Petkov 			chan_off = hole_off;
14696163b5d4SDoug Thompson 		else
14706163b5d4SDoug Thompson 			chan_off = dct_sel_base_off;
14716163b5d4SDoug Thompson 	} else {
1472c8e518d5SBorislav Petkov 		/*
1473c8e518d5SBorislav Petkov 		 * if
1474c8e518d5SBorislav Petkov 		 * we have a valid hole		&&
1475c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1476c8e518d5SBorislav Petkov 		 *
1477c8e518d5SBorislav Petkov 		 *	remove hole
1478c8e518d5SBorislav Petkov 		 * else
1479c8e518d5SBorislav Petkov 		 *	remove dram base to normalize to DCT address
1480c8e518d5SBorislav Petkov 		 */
1481972ea17aSBorislav Petkov 		if (dhar_valid(pvt) && (sys_addr >= BIT_64(32)))
1482bc21fa57SBorislav Petkov 			chan_off = hole_off;
14836163b5d4SDoug Thompson 		else
1484c8e518d5SBorislav Petkov 			chan_off = dram_base;
14856163b5d4SDoug Thompson 	}
14866163b5d4SDoug Thompson 
148710ef6b0dSChen, Gong 	return (sys_addr & GENMASK_ULL(47,6)) - (chan_off & GENMASK_ULL(47,23));
14886163b5d4SDoug Thompson }
14896163b5d4SDoug Thompson 
14906163b5d4SDoug Thompson /*
14916163b5d4SDoug Thompson  * checks if the csrow passed in is marked as SPARED, if so returns the new
14926163b5d4SDoug Thompson  * spare row
14936163b5d4SDoug Thompson  */
149411c75eadSBorislav Petkov static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow)
14956163b5d4SDoug Thompson {
1496614ec9d8SBorislav Petkov 	int tmp_cs;
14976163b5d4SDoug Thompson 
1498614ec9d8SBorislav Petkov 	if (online_spare_swap_done(pvt, dct) &&
1499614ec9d8SBorislav Petkov 	    csrow == online_spare_bad_dramcs(pvt, dct)) {
1500614ec9d8SBorislav Petkov 
1501614ec9d8SBorislav Petkov 		for_each_chip_select(tmp_cs, dct, pvt) {
1502614ec9d8SBorislav Petkov 			if (chip_select_base(tmp_cs, dct, pvt) & 0x2) {
1503614ec9d8SBorislav Petkov 				csrow = tmp_cs;
1504614ec9d8SBorislav Petkov 				break;
1505614ec9d8SBorislav Petkov 			}
1506614ec9d8SBorislav Petkov 		}
15076163b5d4SDoug Thompson 	}
15086163b5d4SDoug Thompson 	return csrow;
15096163b5d4SDoug Thompson }
15106163b5d4SDoug Thompson 
15116163b5d4SDoug Thompson /*
15126163b5d4SDoug Thompson  * Iterate over the DRAM DCT "base" and "mask" registers looking for a
15136163b5d4SDoug Thompson  * SystemAddr match on the specified 'ChannelSelect' and 'NodeID'
15146163b5d4SDoug Thompson  *
15156163b5d4SDoug Thompson  * Return:
15166163b5d4SDoug Thompson  *	-EINVAL:  NOT FOUND
15176163b5d4SDoug Thompson  *	0..csrow = Chip-Select Row
15186163b5d4SDoug Thompson  */
1519c7e5301aSDaniel J Blueman static int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct)
15206163b5d4SDoug Thompson {
15216163b5d4SDoug Thompson 	struct mem_ctl_info *mci;
15226163b5d4SDoug Thompson 	struct amd64_pvt *pvt;
152311c75eadSBorislav Petkov 	u64 cs_base, cs_mask;
15246163b5d4SDoug Thompson 	int cs_found = -EINVAL;
15256163b5d4SDoug Thompson 	int csrow;
15266163b5d4SDoug Thompson 
15272ec591acSBorislav Petkov 	mci = edac_mc_find(nid);
15286163b5d4SDoug Thompson 	if (!mci)
15296163b5d4SDoug Thompson 		return cs_found;
15306163b5d4SDoug Thompson 
15316163b5d4SDoug Thompson 	pvt = mci->pvt_info;
15326163b5d4SDoug Thompson 
1533956b9ba1SJoe Perches 	edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct);
15346163b5d4SDoug Thompson 
153511c75eadSBorislav Petkov 	for_each_chip_select(csrow, dct, pvt) {
153611c75eadSBorislav Petkov 		if (!csrow_enabled(csrow, dct, pvt))
15376163b5d4SDoug Thompson 			continue;
15386163b5d4SDoug Thompson 
153911c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask);
15406163b5d4SDoug Thompson 
1541956b9ba1SJoe Perches 		edac_dbg(1, "    CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
15426163b5d4SDoug Thompson 			 csrow, cs_base, cs_mask);
15436163b5d4SDoug Thompson 
154411c75eadSBorislav Petkov 		cs_mask = ~cs_mask;
15456163b5d4SDoug Thompson 
1546956b9ba1SJoe Perches 		edac_dbg(1, "    (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n",
154711c75eadSBorislav Petkov 			 (in_addr & cs_mask), (cs_base & cs_mask));
15486163b5d4SDoug Thompson 
154911c75eadSBorislav Petkov 		if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
155018b94f66SAravind Gopalakrishnan 			if (pvt->fam == 0x15 && pvt->model >= 0x30) {
155118b94f66SAravind Gopalakrishnan 				cs_found =  csrow;
155218b94f66SAravind Gopalakrishnan 				break;
155318b94f66SAravind Gopalakrishnan 			}
155411c75eadSBorislav Petkov 			cs_found = f10_process_possible_spare(pvt, dct, csrow);
15556163b5d4SDoug Thompson 
1556956b9ba1SJoe Perches 			edac_dbg(1, " MATCH csrow=%d\n", cs_found);
15576163b5d4SDoug Thompson 			break;
15586163b5d4SDoug Thompson 		}
15596163b5d4SDoug Thompson 	}
15606163b5d4SDoug Thompson 	return cs_found;
15616163b5d4SDoug Thompson }
15626163b5d4SDoug Thompson 
156395b0ef55SBorislav Petkov /*
156495b0ef55SBorislav Petkov  * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is
156595b0ef55SBorislav Petkov  * swapped with a region located at the bottom of memory so that the GPU can use
156695b0ef55SBorislav Petkov  * the interleaved region and thus two channels.
156795b0ef55SBorislav Petkov  */
1568b15f0fcaSBorislav Petkov static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr)
156995b0ef55SBorislav Petkov {
157095b0ef55SBorislav Petkov 	u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr;
157195b0ef55SBorislav Petkov 
1572a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10) {
157395b0ef55SBorislav Petkov 		/* only revC3 and revE have that feature */
1574a4b4bedcSBorislav Petkov 		if (pvt->model < 4 || (pvt->model < 0xa && pvt->stepping < 3))
157595b0ef55SBorislav Petkov 			return sys_addr;
157695b0ef55SBorislav Petkov 	}
157795b0ef55SBorislav Petkov 
15787981a28fSAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F2, SWAP_INTLV_REG, &swap_reg);
157995b0ef55SBorislav Petkov 
158095b0ef55SBorislav Petkov 	if (!(swap_reg & 0x1))
158195b0ef55SBorislav Petkov 		return sys_addr;
158295b0ef55SBorislav Petkov 
158395b0ef55SBorislav Petkov 	swap_base	= (swap_reg >> 3) & 0x7f;
158495b0ef55SBorislav Petkov 	swap_limit	= (swap_reg >> 11) & 0x7f;
158595b0ef55SBorislav Petkov 	rgn_size	= (swap_reg >> 20) & 0x7f;
158695b0ef55SBorislav Petkov 	tmp_addr	= sys_addr >> 27;
158795b0ef55SBorislav Petkov 
158895b0ef55SBorislav Petkov 	if (!(sys_addr >> 34) &&
158995b0ef55SBorislav Petkov 	    (((tmp_addr >= swap_base) &&
159095b0ef55SBorislav Petkov 	     (tmp_addr <= swap_limit)) ||
159195b0ef55SBorislav Petkov 	     (tmp_addr < rgn_size)))
159295b0ef55SBorislav Petkov 		return sys_addr ^ (u64)swap_base << 27;
159395b0ef55SBorislav Petkov 
159495b0ef55SBorislav Petkov 	return sys_addr;
159595b0ef55SBorislav Petkov }
159695b0ef55SBorislav Petkov 
1597f71d0a05SDoug Thompson /* For a given @dram_range, check if @sys_addr falls within it. */
1598e761359aSBorislav Petkov static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
159933ca0643SBorislav Petkov 				  u64 sys_addr, int *chan_sel)
1600f71d0a05SDoug Thompson {
1601229a7a11SBorislav Petkov 	int cs_found = -EINVAL;
1602c8e518d5SBorislav Petkov 	u64 chan_addr;
16035d4b58e8SBorislav Petkov 	u32 dct_sel_base;
160411c75eadSBorislav Petkov 	u8 channel;
1605229a7a11SBorislav Petkov 	bool high_range = false;
1606f71d0a05SDoug Thompson 
16077f19bf75SBorislav Petkov 	u8 node_id    = dram_dst_node(pvt, range);
1608229a7a11SBorislav Petkov 	u8 intlv_en   = dram_intlv_en(pvt, range);
16097f19bf75SBorislav Petkov 	u32 intlv_sel = dram_intlv_sel(pvt, range);
1610f71d0a05SDoug Thompson 
1611956b9ba1SJoe Perches 	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
1612c8e518d5SBorislav Petkov 		 range, sys_addr, get_dram_limit(pvt, range));
1613f71d0a05SDoug Thompson 
1614355fba60SBorislav Petkov 	if (dhar_valid(pvt) &&
1615355fba60SBorislav Petkov 	    dhar_base(pvt) <= sys_addr &&
1616355fba60SBorislav Petkov 	    sys_addr < BIT_64(32)) {
1617355fba60SBorislav Petkov 		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
1618355fba60SBorislav Petkov 			    sys_addr);
1619f71d0a05SDoug Thompson 		return -EINVAL;
1620355fba60SBorislav Petkov 	}
1621355fba60SBorislav Petkov 
1622f030ddfbSBorislav Petkov 	if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en)))
1623355fba60SBorislav Petkov 		return -EINVAL;
1624f71d0a05SDoug Thompson 
1625b15f0fcaSBorislav Petkov 	sys_addr = f1x_swap_interleaved_region(pvt, sys_addr);
162695b0ef55SBorislav Petkov 
1627f71d0a05SDoug Thompson 	dct_sel_base = dct_sel_baseaddr(pvt);
1628f71d0a05SDoug Thompson 
1629f71d0a05SDoug Thompson 	/*
1630f71d0a05SDoug Thompson 	 * check whether addresses >= DctSelBaseAddr[47:27] are to be used to
1631f71d0a05SDoug Thompson 	 * select between DCT0 and DCT1.
1632f71d0a05SDoug Thompson 	 */
1633f71d0a05SDoug Thompson 	if (dct_high_range_enabled(pvt) &&
1634f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt) &&
1635f71d0a05SDoug Thompson 	   ((sys_addr >> 27) >= (dct_sel_base >> 11)))
1636229a7a11SBorislav Petkov 		high_range = true;
1637f71d0a05SDoug Thompson 
1638b15f0fcaSBorislav Petkov 	channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en);
1639f71d0a05SDoug Thompson 
1640b15f0fcaSBorislav Petkov 	chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr,
1641c8e518d5SBorislav Petkov 					  high_range, dct_sel_base);
1642f71d0a05SDoug Thompson 
1643e2f79dbdSBorislav Petkov 	/* Remove node interleaving, see F1x120 */
1644e2f79dbdSBorislav Petkov 	if (intlv_en)
1645e2f79dbdSBorislav Petkov 		chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) |
1646e2f79dbdSBorislav Petkov 			    (chan_addr & 0xfff);
1647f71d0a05SDoug Thompson 
16485d4b58e8SBorislav Petkov 	/* remove channel interleave */
1649f71d0a05SDoug Thompson 	if (dct_interleave_enabled(pvt) &&
1650f71d0a05SDoug Thompson 	   !dct_high_range_enabled(pvt) &&
1651f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt)) {
16525d4b58e8SBorislav Petkov 
16535d4b58e8SBorislav Petkov 		if (dct_sel_interleave_addr(pvt) != 1) {
16545d4b58e8SBorislav Petkov 			if (dct_sel_interleave_addr(pvt) == 0x3)
16555d4b58e8SBorislav Petkov 				/* hash 9 */
16565d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 10) << 9) |
16575d4b58e8SBorislav Petkov 					     (chan_addr & 0x1ff);
16585d4b58e8SBorislav Petkov 			else
16595d4b58e8SBorislav Petkov 				/* A[6] or hash 6 */
16605d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 7) << 6) |
16615d4b58e8SBorislav Petkov 					     (chan_addr & 0x3f);
16625d4b58e8SBorislav Petkov 		} else
16635d4b58e8SBorislav Petkov 			/* A[12] */
16645d4b58e8SBorislav Petkov 			chan_addr = ((chan_addr >> 13) << 12) |
16655d4b58e8SBorislav Petkov 				     (chan_addr & 0xfff);
1666f71d0a05SDoug Thompson 	}
1667f71d0a05SDoug Thompson 
1668956b9ba1SJoe Perches 	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
1669f71d0a05SDoug Thompson 
1670b15f0fcaSBorislav Petkov 	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel);
1671f71d0a05SDoug Thompson 
167233ca0643SBorislav Petkov 	if (cs_found >= 0)
1673f71d0a05SDoug Thompson 		*chan_sel = channel;
167433ca0643SBorislav Petkov 
1675f71d0a05SDoug Thompson 	return cs_found;
1676f71d0a05SDoug Thompson }
1677f71d0a05SDoug Thompson 
167818b94f66SAravind Gopalakrishnan static int f15_m30h_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
167918b94f66SAravind Gopalakrishnan 					u64 sys_addr, int *chan_sel)
168018b94f66SAravind Gopalakrishnan {
168118b94f66SAravind Gopalakrishnan 	int cs_found = -EINVAL;
168218b94f66SAravind Gopalakrishnan 	int num_dcts_intlv = 0;
168318b94f66SAravind Gopalakrishnan 	u64 chan_addr, chan_offset;
168418b94f66SAravind Gopalakrishnan 	u64 dct_base, dct_limit;
168518b94f66SAravind Gopalakrishnan 	u32 dct_cont_base_reg, dct_cont_limit_reg, tmp;
168618b94f66SAravind Gopalakrishnan 	u8 channel, alias_channel, leg_mmio_hole, dct_sel, dct_offset_en;
168718b94f66SAravind Gopalakrishnan 
168818b94f66SAravind Gopalakrishnan 	u64 dhar_offset		= f10_dhar_offset(pvt);
168918b94f66SAravind Gopalakrishnan 	u8 intlv_addr		= dct_sel_interleave_addr(pvt);
169018b94f66SAravind Gopalakrishnan 	u8 node_id		= dram_dst_node(pvt, range);
169118b94f66SAravind Gopalakrishnan 	u8 intlv_en		= dram_intlv_en(pvt, range);
169218b94f66SAravind Gopalakrishnan 
169318b94f66SAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F1, DRAM_CONT_BASE, &dct_cont_base_reg);
169418b94f66SAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F1, DRAM_CONT_LIMIT, &dct_cont_limit_reg);
169518b94f66SAravind Gopalakrishnan 
169618b94f66SAravind Gopalakrishnan 	dct_offset_en		= (u8) ((dct_cont_base_reg >> 3) & BIT(0));
169718b94f66SAravind Gopalakrishnan 	dct_sel			= (u8) ((dct_cont_base_reg >> 4) & 0x7);
169818b94f66SAravind Gopalakrishnan 
169918b94f66SAravind Gopalakrishnan 	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
170018b94f66SAravind Gopalakrishnan 		 range, sys_addr, get_dram_limit(pvt, range));
170118b94f66SAravind Gopalakrishnan 
170218b94f66SAravind Gopalakrishnan 	if (!(get_dram_base(pvt, range)  <= sys_addr) &&
170318b94f66SAravind Gopalakrishnan 	    !(get_dram_limit(pvt, range) >= sys_addr))
170418b94f66SAravind Gopalakrishnan 		return -EINVAL;
170518b94f66SAravind Gopalakrishnan 
170618b94f66SAravind Gopalakrishnan 	if (dhar_valid(pvt) &&
170718b94f66SAravind Gopalakrishnan 	    dhar_base(pvt) <= sys_addr &&
170818b94f66SAravind Gopalakrishnan 	    sys_addr < BIT_64(32)) {
170918b94f66SAravind Gopalakrishnan 		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
171018b94f66SAravind Gopalakrishnan 			    sys_addr);
171118b94f66SAravind Gopalakrishnan 		return -EINVAL;
171218b94f66SAravind Gopalakrishnan 	}
171318b94f66SAravind Gopalakrishnan 
171418b94f66SAravind Gopalakrishnan 	/* Verify sys_addr is within DCT Range. */
17154fc06b31SAravind Gopalakrishnan 	dct_base = (u64) dct_sel_baseaddr(pvt);
17164fc06b31SAravind Gopalakrishnan 	dct_limit = (dct_cont_limit_reg >> 11) & 0x1FFF;
171718b94f66SAravind Gopalakrishnan 
171818b94f66SAravind Gopalakrishnan 	if (!(dct_cont_base_reg & BIT(0)) &&
17194fc06b31SAravind Gopalakrishnan 	    !(dct_base <= (sys_addr >> 27) &&
17204fc06b31SAravind Gopalakrishnan 	      dct_limit >= (sys_addr >> 27)))
172118b94f66SAravind Gopalakrishnan 		return -EINVAL;
172218b94f66SAravind Gopalakrishnan 
172318b94f66SAravind Gopalakrishnan 	/* Verify number of dct's that participate in channel interleaving. */
172418b94f66SAravind Gopalakrishnan 	num_dcts_intlv = (int) hweight8(intlv_en);
172518b94f66SAravind Gopalakrishnan 
172618b94f66SAravind Gopalakrishnan 	if (!(num_dcts_intlv % 2 == 0) || (num_dcts_intlv > 4))
172718b94f66SAravind Gopalakrishnan 		return -EINVAL;
172818b94f66SAravind Gopalakrishnan 
172918b94f66SAravind Gopalakrishnan 	channel = f15_m30h_determine_channel(pvt, sys_addr, intlv_en,
173018b94f66SAravind Gopalakrishnan 					     num_dcts_intlv, dct_sel);
173118b94f66SAravind Gopalakrishnan 
173218b94f66SAravind Gopalakrishnan 	/* Verify we stay within the MAX number of channels allowed */
17337f3f5240SAravind Gopalakrishnan 	if (channel > 3)
173418b94f66SAravind Gopalakrishnan 		return -EINVAL;
173518b94f66SAravind Gopalakrishnan 
173618b94f66SAravind Gopalakrishnan 	leg_mmio_hole = (u8) (dct_cont_base_reg >> 1 & BIT(0));
173718b94f66SAravind Gopalakrishnan 
173818b94f66SAravind Gopalakrishnan 	/* Get normalized DCT addr */
173918b94f66SAravind Gopalakrishnan 	if (leg_mmio_hole && (sys_addr >= BIT_64(32)))
174018b94f66SAravind Gopalakrishnan 		chan_offset = dhar_offset;
174118b94f66SAravind Gopalakrishnan 	else
17424fc06b31SAravind Gopalakrishnan 		chan_offset = dct_base << 27;
174318b94f66SAravind Gopalakrishnan 
174418b94f66SAravind Gopalakrishnan 	chan_addr = sys_addr - chan_offset;
174518b94f66SAravind Gopalakrishnan 
174618b94f66SAravind Gopalakrishnan 	/* remove channel interleave */
174718b94f66SAravind Gopalakrishnan 	if (num_dcts_intlv == 2) {
174818b94f66SAravind Gopalakrishnan 		if (intlv_addr == 0x4)
174918b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 9) << 8) |
175018b94f66SAravind Gopalakrishnan 						(chan_addr & 0xff);
175118b94f66SAravind Gopalakrishnan 		else if (intlv_addr == 0x5)
175218b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 10) << 9) |
175318b94f66SAravind Gopalakrishnan 						(chan_addr & 0x1ff);
175418b94f66SAravind Gopalakrishnan 		else
175518b94f66SAravind Gopalakrishnan 			return -EINVAL;
175618b94f66SAravind Gopalakrishnan 
175718b94f66SAravind Gopalakrishnan 	} else if (num_dcts_intlv == 4) {
175818b94f66SAravind Gopalakrishnan 		if (intlv_addr == 0x4)
175918b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 10) << 8) |
176018b94f66SAravind Gopalakrishnan 							(chan_addr & 0xff);
176118b94f66SAravind Gopalakrishnan 		else if (intlv_addr == 0x5)
176218b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 11) << 9) |
176318b94f66SAravind Gopalakrishnan 							(chan_addr & 0x1ff);
176418b94f66SAravind Gopalakrishnan 		else
176518b94f66SAravind Gopalakrishnan 			return -EINVAL;
176618b94f66SAravind Gopalakrishnan 	}
176718b94f66SAravind Gopalakrishnan 
176818b94f66SAravind Gopalakrishnan 	if (dct_offset_en) {
176918b94f66SAravind Gopalakrishnan 		amd64_read_pci_cfg(pvt->F1,
177018b94f66SAravind Gopalakrishnan 				   DRAM_CONT_HIGH_OFF + (int) channel * 4,
177118b94f66SAravind Gopalakrishnan 				   &tmp);
17724fc06b31SAravind Gopalakrishnan 		chan_addr +=  (u64) ((tmp >> 11) & 0xfff) << 27;
177318b94f66SAravind Gopalakrishnan 	}
177418b94f66SAravind Gopalakrishnan 
177518b94f66SAravind Gopalakrishnan 	f15h_select_dct(pvt, channel);
177618b94f66SAravind Gopalakrishnan 
177718b94f66SAravind Gopalakrishnan 	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
177818b94f66SAravind Gopalakrishnan 
177918b94f66SAravind Gopalakrishnan 	/*
178018b94f66SAravind Gopalakrishnan 	 * Find Chip select:
178118b94f66SAravind Gopalakrishnan 	 * if channel = 3, then alias it to 1. This is because, in F15 M30h,
178218b94f66SAravind Gopalakrishnan 	 * there is support for 4 DCT's, but only 2 are currently functional.
178318b94f66SAravind Gopalakrishnan 	 * They are DCT0 and DCT3. But we have read all registers of DCT3 into
178418b94f66SAravind Gopalakrishnan 	 * pvt->csels[1]. So we need to use '1' here to get correct info.
178518b94f66SAravind Gopalakrishnan 	 * Refer F15 M30h BKDG Section 2.10 and 2.10.3 for clarifications.
178618b94f66SAravind Gopalakrishnan 	 */
178718b94f66SAravind Gopalakrishnan 	alias_channel =  (channel == 3) ? 1 : channel;
178818b94f66SAravind Gopalakrishnan 
178918b94f66SAravind Gopalakrishnan 	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, alias_channel);
179018b94f66SAravind Gopalakrishnan 
179118b94f66SAravind Gopalakrishnan 	if (cs_found >= 0)
179218b94f66SAravind Gopalakrishnan 		*chan_sel = alias_channel;
179318b94f66SAravind Gopalakrishnan 
179418b94f66SAravind Gopalakrishnan 	return cs_found;
179518b94f66SAravind Gopalakrishnan }
179618b94f66SAravind Gopalakrishnan 
179718b94f66SAravind Gopalakrishnan static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt,
179818b94f66SAravind Gopalakrishnan 					u64 sys_addr,
179933ca0643SBorislav Petkov 					int *chan_sel)
1800f71d0a05SDoug Thompson {
1801e761359aSBorislav Petkov 	int cs_found = -EINVAL;
1802e761359aSBorislav Petkov 	unsigned range;
1803f71d0a05SDoug Thompson 
18047f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
18057f19bf75SBorislav Petkov 		if (!dram_rw(pvt, range))
1806f71d0a05SDoug Thompson 			continue;
1807f71d0a05SDoug Thompson 
180818b94f66SAravind Gopalakrishnan 		if (pvt->fam == 0x15 && pvt->model >= 0x30)
180918b94f66SAravind Gopalakrishnan 			cs_found = f15_m30h_match_to_this_node(pvt, range,
181018b94f66SAravind Gopalakrishnan 							       sys_addr,
181118b94f66SAravind Gopalakrishnan 							       chan_sel);
1812f71d0a05SDoug Thompson 
181318b94f66SAravind Gopalakrishnan 		else if ((get_dram_base(pvt, range)  <= sys_addr) &&
181418b94f66SAravind Gopalakrishnan 			 (get_dram_limit(pvt, range) >= sys_addr)) {
1815b15f0fcaSBorislav Petkov 			cs_found = f1x_match_to_this_node(pvt, range,
181633ca0643SBorislav Petkov 							  sys_addr, chan_sel);
1817f71d0a05SDoug Thompson 			if (cs_found >= 0)
1818f71d0a05SDoug Thompson 				break;
1819f71d0a05SDoug Thompson 		}
1820f71d0a05SDoug Thompson 	}
1821f71d0a05SDoug Thompson 	return cs_found;
1822f71d0a05SDoug Thompson }
1823f71d0a05SDoug Thompson 
1824f71d0a05SDoug Thompson /*
1825bdc30a0cSBorislav Petkov  * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
1826bdc30a0cSBorislav Petkov  * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
1827f71d0a05SDoug Thompson  *
1828bdc30a0cSBorislav Petkov  * The @sys_addr is usually an error address received from the hardware
1829bdc30a0cSBorislav Petkov  * (MCX_ADDR).
1830f71d0a05SDoug Thompson  */
1831b15f0fcaSBorislav Petkov static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
183233ca0643SBorislav Petkov 				     struct err_info *err)
1833f71d0a05SDoug Thompson {
1834f71d0a05SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
1835f71d0a05SDoug Thompson 
183633ca0643SBorislav Petkov 	error_address_to_page_and_offset(sys_addr, err);
1837ab5a503cSMauro Carvalho Chehab 
183833ca0643SBorislav Petkov 	err->csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &err->channel);
183933ca0643SBorislav Petkov 	if (err->csrow < 0) {
184033ca0643SBorislav Petkov 		err->err_code = ERR_CSROW;
1841bdc30a0cSBorislav Petkov 		return;
1842bdc30a0cSBorislav Petkov 	}
1843bdc30a0cSBorislav Petkov 
1844f71d0a05SDoug Thompson 	/*
1845bdc30a0cSBorislav Petkov 	 * We need the syndromes for channel detection only when we're
1846bdc30a0cSBorislav Petkov 	 * ganged. Otherwise @chan should already contain the channel at
1847bdc30a0cSBorislav Petkov 	 * this point.
1848f71d0a05SDoug Thompson 	 */
1849a97fa68eSBorislav Petkov 	if (dct_ganging_enabled(pvt))
185033ca0643SBorislav Petkov 		err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
1851f71d0a05SDoug Thompson }
1852f71d0a05SDoug Thompson 
1853f71d0a05SDoug Thompson /*
18548566c4dfSBorislav Petkov  * debug routine to display the memory sizes of all logical DIMMs and its
1855cb328507SBorislav Petkov  * CSROWs
1856f71d0a05SDoug Thompson  */
1857d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
1858f71d0a05SDoug Thompson {
1859bb89f5a0SBorislav Petkov 	int dimm, size0, size1;
1860525a1b20SBorislav Petkov 	u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases;
1861525a1b20SBorislav Petkov 	u32 dbam  = ctrl ? pvt->dbam1 : pvt->dbam0;
1862f71d0a05SDoug Thompson 
1863a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf) {
18648566c4dfSBorislav Petkov 		/* K8 families < revF not supported yet */
18651433eb99SBorislav Petkov 	       if (pvt->ext_model < K8_REV_F)
18668566c4dfSBorislav Petkov 			return;
18678566c4dfSBorislav Petkov 	       else
18688566c4dfSBorislav Petkov 		       WARN_ON(ctrl != 0);
18698566c4dfSBorislav Petkov 	}
18708566c4dfSBorislav Petkov 
18717981a28fSAravind Gopalakrishnan 	if (pvt->fam == 0x10) {
18727981a28fSAravind Gopalakrishnan 		dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1
18737981a28fSAravind Gopalakrishnan 							   : pvt->dbam0;
18747981a28fSAravind Gopalakrishnan 		dcsb = (ctrl && !dct_ganging_enabled(pvt)) ?
18757981a28fSAravind Gopalakrishnan 				 pvt->csels[1].csbases :
18767981a28fSAravind Gopalakrishnan 				 pvt->csels[0].csbases;
18777981a28fSAravind Gopalakrishnan 	} else if (ctrl) {
18787981a28fSAravind Gopalakrishnan 		dbam = pvt->dbam0;
18797981a28fSAravind Gopalakrishnan 		dcsb = pvt->csels[1].csbases;
18807981a28fSAravind Gopalakrishnan 	}
1881956b9ba1SJoe Perches 	edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
1882956b9ba1SJoe Perches 		 ctrl, dbam);
1883f71d0a05SDoug Thompson 
18848566c4dfSBorislav Petkov 	edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
18858566c4dfSBorislav Petkov 
1886f71d0a05SDoug Thompson 	/* Dump memory sizes for DIMM and its CSROWs */
1887f71d0a05SDoug Thompson 	for (dimm = 0; dimm < 4; dimm++) {
1888f71d0a05SDoug Thompson 
1889f71d0a05SDoug Thompson 		size0 = 0;
189011c75eadSBorislav Petkov 		if (dcsb[dimm*2] & DCSB_CS_ENABLE)
1891a597d2a5SAravind Gopalakrishnan 			/* For f15m60h, need multiplier for LRDIMM cs_size
1892a597d2a5SAravind Gopalakrishnan 			 * calculation. We pass 'dimm' value to the dbam_to_cs
1893a597d2a5SAravind Gopalakrishnan 			 * mapper so we can find the multiplier from the
1894a597d2a5SAravind Gopalakrishnan 			 * corresponding DCSM.
1895a597d2a5SAravind Gopalakrishnan 			 */
189641d8bfabSBorislav Petkov 			size0 = pvt->ops->dbam_to_cs(pvt, ctrl,
1897a597d2a5SAravind Gopalakrishnan 						     DBAM_DIMM(dimm, dbam),
1898a597d2a5SAravind Gopalakrishnan 						     dimm);
1899f71d0a05SDoug Thompson 
1900f71d0a05SDoug Thompson 		size1 = 0;
190111c75eadSBorislav Petkov 		if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE)
190241d8bfabSBorislav Petkov 			size1 = pvt->ops->dbam_to_cs(pvt, ctrl,
1903a597d2a5SAravind Gopalakrishnan 						     DBAM_DIMM(dimm, dbam),
1904a597d2a5SAravind Gopalakrishnan 						     dimm);
1905f71d0a05SDoug Thompson 
190624f9a7feSBorislav Petkov 		amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
1907bb89f5a0SBorislav Petkov 				dimm * 2,     size0,
1908bb89f5a0SBorislav Petkov 				dimm * 2 + 1, size1);
1909f71d0a05SDoug Thompson 	}
1910f71d0a05SDoug Thompson }
1911f71d0a05SDoug Thompson 
1912d1ea71cdSBorislav Petkov static struct amd64_family_type family_types[] = {
19134d37607aSDoug Thompson 	[K8_CPUS] = {
19140092b20dSBorislav Petkov 		.ctl_name = "K8",
19158d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
19163f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
19174d37607aSDoug Thompson 		.ops = {
19184d37607aSDoug Thompson 			.early_channel_count	= k8_early_channel_count,
19194d37607aSDoug Thompson 			.map_sysaddr_to_csrow	= k8_map_sysaddr_to_csrow,
19201433eb99SBorislav Petkov 			.dbam_to_cs		= k8_dbam_to_chip_select,
19214d37607aSDoug Thompson 		}
19224d37607aSDoug Thompson 	},
19234d37607aSDoug Thompson 	[F10_CPUS] = {
19240092b20dSBorislav Petkov 		.ctl_name = "F10h",
19258d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP,
19263f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM,
19274d37607aSDoug Thompson 		.ops = {
19287d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
1929b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
19301433eb99SBorislav Petkov 			.dbam_to_cs		= f10_dbam_to_chip_select,
1931b2b0c605SBorislav Petkov 		}
1932b2b0c605SBorislav Petkov 	},
1933b2b0c605SBorislav Petkov 	[F15_CPUS] = {
1934b2b0c605SBorislav Petkov 		.ctl_name = "F15h",
1935df71a053SBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1,
19363f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2,
1937b2b0c605SBorislav Petkov 		.ops = {
19387d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
1939b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
194041d8bfabSBorislav Petkov 			.dbam_to_cs		= f15_dbam_to_chip_select,
19414d37607aSDoug Thompson 		}
19424d37607aSDoug Thompson 	},
194318b94f66SAravind Gopalakrishnan 	[F15_M30H_CPUS] = {
194418b94f66SAravind Gopalakrishnan 		.ctl_name = "F15h_M30h",
194518b94f66SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1,
19463f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2,
194718b94f66SAravind Gopalakrishnan 		.ops = {
194818b94f66SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
194918b94f66SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
195018b94f66SAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
195118b94f66SAravind Gopalakrishnan 		}
195218b94f66SAravind Gopalakrishnan 	},
1953a597d2a5SAravind Gopalakrishnan 	[F15_M60H_CPUS] = {
1954a597d2a5SAravind Gopalakrishnan 		.ctl_name = "F15h_M60h",
1955a597d2a5SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1,
19563f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2,
1957a597d2a5SAravind Gopalakrishnan 		.ops = {
1958a597d2a5SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
1959a597d2a5SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
1960a597d2a5SAravind Gopalakrishnan 			.dbam_to_cs		= f15_m60h_dbam_to_chip_select,
1961a597d2a5SAravind Gopalakrishnan 		}
1962a597d2a5SAravind Gopalakrishnan 	},
196394c1acf2SAravind Gopalakrishnan 	[F16_CPUS] = {
196494c1acf2SAravind Gopalakrishnan 		.ctl_name = "F16h",
196594c1acf2SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1,
19663f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2,
196794c1acf2SAravind Gopalakrishnan 		.ops = {
196894c1acf2SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
196994c1acf2SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
197094c1acf2SAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
197194c1acf2SAravind Gopalakrishnan 		}
197294c1acf2SAravind Gopalakrishnan 	},
197385a8885bSAravind Gopalakrishnan 	[F16_M30H_CPUS] = {
197485a8885bSAravind Gopalakrishnan 		.ctl_name = "F16h_M30h",
197585a8885bSAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F1,
19763f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2,
197785a8885bSAravind Gopalakrishnan 		.ops = {
197885a8885bSAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
197985a8885bSAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
198085a8885bSAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
198185a8885bSAravind Gopalakrishnan 		}
198285a8885bSAravind Gopalakrishnan 	},
19834d37607aSDoug Thompson };
19844d37607aSDoug Thompson 
1985b1289d6fSDoug Thompson /*
1986bfc04aecSBorislav Petkov  * These are tables of eigenvectors (one per line) which can be used for the
1987bfc04aecSBorislav Petkov  * construction of the syndrome tables. The modified syndrome search algorithm
1988bfc04aecSBorislav Petkov  * uses those to find the symbol in error and thus the DIMM.
1989b1289d6fSDoug Thompson  *
1990bfc04aecSBorislav Petkov  * Algorithm courtesy of Ross LaFetra from AMD.
1991b1289d6fSDoug Thompson  */
1992c7e5301aSDaniel J Blueman static const u16 x4_vectors[] = {
1993bfc04aecSBorislav Petkov 	0x2f57, 0x1afe, 0x66cc, 0xdd88,
1994bfc04aecSBorislav Petkov 	0x11eb, 0x3396, 0x7f4c, 0xeac8,
1995bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008,
1996bfc04aecSBorislav Petkov 	0x1013, 0x3032, 0x4044, 0x8088,
1997bfc04aecSBorislav Petkov 	0x106b, 0x30d6, 0x70fc, 0xe0a8,
1998bfc04aecSBorislav Petkov 	0x4857, 0xc4fe, 0x13cc, 0x3288,
1999bfc04aecSBorislav Petkov 	0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
2000bfc04aecSBorislav Petkov 	0x1f39, 0x251e, 0xbd6c, 0x6bd8,
2001bfc04aecSBorislav Petkov 	0x15c1, 0x2a42, 0x89ac, 0x4758,
2002bfc04aecSBorislav Petkov 	0x2b03, 0x1602, 0x4f0c, 0xca08,
2003bfc04aecSBorislav Petkov 	0x1f07, 0x3a0e, 0x6b04, 0xbd08,
2004bfc04aecSBorislav Petkov 	0x8ba7, 0x465e, 0x244c, 0x1cc8,
2005bfc04aecSBorislav Petkov 	0x2b87, 0x164e, 0x642c, 0xdc18,
2006bfc04aecSBorislav Petkov 	0x40b9, 0x80de, 0x1094, 0x20e8,
2007bfc04aecSBorislav Petkov 	0x27db, 0x1eb6, 0x9dac, 0x7b58,
2008bfc04aecSBorislav Petkov 	0x11c1, 0x2242, 0x84ac, 0x4c58,
2009bfc04aecSBorislav Petkov 	0x1be5, 0x2d7a, 0x5e34, 0xa718,
2010bfc04aecSBorislav Petkov 	0x4b39, 0x8d1e, 0x14b4, 0x28d8,
2011bfc04aecSBorislav Petkov 	0x4c97, 0xc87e, 0x11fc, 0x33a8,
2012bfc04aecSBorislav Petkov 	0x8e97, 0x497e, 0x2ffc, 0x1aa8,
2013bfc04aecSBorislav Petkov 	0x16b3, 0x3d62, 0x4f34, 0x8518,
2014bfc04aecSBorislav Petkov 	0x1e2f, 0x391a, 0x5cac, 0xf858,
2015bfc04aecSBorislav Petkov 	0x1d9f, 0x3b7a, 0x572c, 0xfe18,
2016bfc04aecSBorislav Petkov 	0x15f5, 0x2a5a, 0x5264, 0xa3b8,
2017bfc04aecSBorislav Petkov 	0x1dbb, 0x3b66, 0x715c, 0xe3f8,
2018bfc04aecSBorislav Petkov 	0x4397, 0xc27e, 0x17fc, 0x3ea8,
2019bfc04aecSBorislav Petkov 	0x1617, 0x3d3e, 0x6464, 0xb8b8,
2020bfc04aecSBorislav Petkov 	0x23ff, 0x12aa, 0xab6c, 0x56d8,
2021bfc04aecSBorislav Petkov 	0x2dfb, 0x1ba6, 0x913c, 0x7328,
2022bfc04aecSBorislav Petkov 	0x185d, 0x2ca6, 0x7914, 0x9e28,
2023bfc04aecSBorislav Petkov 	0x171b, 0x3e36, 0x7d7c, 0xebe8,
2024bfc04aecSBorislav Petkov 	0x4199, 0x82ee, 0x19f4, 0x2e58,
2025bfc04aecSBorislav Petkov 	0x4807, 0xc40e, 0x130c, 0x3208,
2026bfc04aecSBorislav Petkov 	0x1905, 0x2e0a, 0x5804, 0xac08,
2027bfc04aecSBorislav Petkov 	0x213f, 0x132a, 0xadfc, 0x5ba8,
2028bfc04aecSBorislav Petkov 	0x19a9, 0x2efe, 0xb5cc, 0x6f88,
2029b1289d6fSDoug Thompson };
2030b1289d6fSDoug Thompson 
2031c7e5301aSDaniel J Blueman static const u16 x8_vectors[] = {
2032bfc04aecSBorislav Petkov 	0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
2033bfc04aecSBorislav Petkov 	0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
2034bfc04aecSBorislav Petkov 	0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
2035bfc04aecSBorislav Petkov 	0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
2036bfc04aecSBorislav Petkov 	0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
2037bfc04aecSBorislav Petkov 	0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
2038bfc04aecSBorislav Petkov 	0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
2039bfc04aecSBorislav Petkov 	0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
2040bfc04aecSBorislav Petkov 	0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
2041bfc04aecSBorislav Petkov 	0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
2042bfc04aecSBorislav Petkov 	0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
2043bfc04aecSBorislav Petkov 	0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
2044bfc04aecSBorislav Petkov 	0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
2045bfc04aecSBorislav Petkov 	0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
2046bfc04aecSBorislav Petkov 	0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
2047bfc04aecSBorislav Petkov 	0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
2048bfc04aecSBorislav Petkov 	0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
2049bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
2050bfc04aecSBorislav Petkov 	0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
2051bfc04aecSBorislav Petkov };
2052bfc04aecSBorislav Petkov 
2053c7e5301aSDaniel J Blueman static int decode_syndrome(u16 syndrome, const u16 *vectors, unsigned num_vecs,
2054d34a6ecdSBorislav Petkov 			   unsigned v_dim)
2055b1289d6fSDoug Thompson {
2056bfc04aecSBorislav Petkov 	unsigned int i, err_sym;
2057b1289d6fSDoug Thompson 
2058bfc04aecSBorislav Petkov 	for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
2059bfc04aecSBorislav Petkov 		u16 s = syndrome;
2060d34a6ecdSBorislav Petkov 		unsigned v_idx =  err_sym * v_dim;
2061d34a6ecdSBorislav Petkov 		unsigned v_end = (err_sym + 1) * v_dim;
2062b1289d6fSDoug Thompson 
2063bfc04aecSBorislav Petkov 		/* walk over all 16 bits of the syndrome */
2064bfc04aecSBorislav Petkov 		for (i = 1; i < (1U << 16); i <<= 1) {
2065bfc04aecSBorislav Petkov 
2066bfc04aecSBorislav Petkov 			/* if bit is set in that eigenvector... */
2067bfc04aecSBorislav Petkov 			if (v_idx < v_end && vectors[v_idx] & i) {
2068bfc04aecSBorislav Petkov 				u16 ev_comp = vectors[v_idx++];
2069bfc04aecSBorislav Petkov 
2070bfc04aecSBorislav Petkov 				/* ... and bit set in the modified syndrome, */
2071bfc04aecSBorislav Petkov 				if (s & i) {
2072bfc04aecSBorislav Petkov 					/* remove it. */
2073bfc04aecSBorislav Petkov 					s ^= ev_comp;
2074bfc04aecSBorislav Petkov 
2075bfc04aecSBorislav Petkov 					if (!s)
2076bfc04aecSBorislav Petkov 						return err_sym;
2077bfc04aecSBorislav Petkov 				}
2078bfc04aecSBorislav Petkov 
2079bfc04aecSBorislav Petkov 			} else if (s & i)
2080bfc04aecSBorislav Petkov 				/* can't get to zero, move to next symbol */
2081bfc04aecSBorislav Petkov 				break;
2082bfc04aecSBorislav Petkov 		}
2083b1289d6fSDoug Thompson 	}
2084b1289d6fSDoug Thompson 
2085956b9ba1SJoe Perches 	edac_dbg(0, "syndrome(%x) not found\n", syndrome);
2086b1289d6fSDoug Thompson 	return -1;
2087b1289d6fSDoug Thompson }
2088d27bf6faSDoug Thompson 
2089bfc04aecSBorislav Petkov static int map_err_sym_to_channel(int err_sym, int sym_size)
2090bfc04aecSBorislav Petkov {
2091bfc04aecSBorislav Petkov 	if (sym_size == 4)
2092bfc04aecSBorislav Petkov 		switch (err_sym) {
2093bfc04aecSBorislav Petkov 		case 0x20:
2094bfc04aecSBorislav Petkov 		case 0x21:
2095bfc04aecSBorislav Petkov 			return 0;
2096bfc04aecSBorislav Petkov 			break;
2097bfc04aecSBorislav Petkov 		case 0x22:
2098bfc04aecSBorislav Petkov 		case 0x23:
2099bfc04aecSBorislav Petkov 			return 1;
2100bfc04aecSBorislav Petkov 			break;
2101bfc04aecSBorislav Petkov 		default:
2102bfc04aecSBorislav Petkov 			return err_sym >> 4;
2103bfc04aecSBorislav Petkov 			break;
2104bfc04aecSBorislav Petkov 		}
2105bfc04aecSBorislav Petkov 	/* x8 symbols */
2106bfc04aecSBorislav Petkov 	else
2107bfc04aecSBorislav Petkov 		switch (err_sym) {
2108bfc04aecSBorislav Petkov 		/* imaginary bits not in a DIMM */
2109bfc04aecSBorislav Petkov 		case 0x10:
2110bfc04aecSBorislav Petkov 			WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
2111bfc04aecSBorislav Petkov 					  err_sym);
2112bfc04aecSBorislav Petkov 			return -1;
2113bfc04aecSBorislav Petkov 			break;
2114bfc04aecSBorislav Petkov 
2115bfc04aecSBorislav Petkov 		case 0x11:
2116bfc04aecSBorislav Petkov 			return 0;
2117bfc04aecSBorislav Petkov 			break;
2118bfc04aecSBorislav Petkov 		case 0x12:
2119bfc04aecSBorislav Petkov 			return 1;
2120bfc04aecSBorislav Petkov 			break;
2121bfc04aecSBorislav Petkov 		default:
2122bfc04aecSBorislav Petkov 			return err_sym >> 3;
2123bfc04aecSBorislav Petkov 			break;
2124bfc04aecSBorislav Petkov 		}
2125bfc04aecSBorislav Petkov 	return -1;
2126bfc04aecSBorislav Petkov }
2127bfc04aecSBorislav Petkov 
2128bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
2129bfc04aecSBorislav Petkov {
2130bfc04aecSBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
2131ad6a32e9SBorislav Petkov 	int err_sym = -1;
2132bfc04aecSBorislav Petkov 
2133a3b7db09SBorislav Petkov 	if (pvt->ecc_sym_sz == 8)
2134bfc04aecSBorislav Petkov 		err_sym = decode_syndrome(syndrome, x8_vectors,
2135ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x8_vectors),
2136a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
2137a3b7db09SBorislav Petkov 	else if (pvt->ecc_sym_sz == 4)
2138ad6a32e9SBorislav Petkov 		err_sym = decode_syndrome(syndrome, x4_vectors,
2139ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x4_vectors),
2140a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
2141ad6a32e9SBorislav Petkov 	else {
2142a3b7db09SBorislav Petkov 		amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz);
2143ad6a32e9SBorislav Petkov 		return err_sym;
2144bfc04aecSBorislav Petkov 	}
2145ad6a32e9SBorislav Petkov 
2146a3b7db09SBorislav Petkov 	return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz);
214741c31044SBorislav Petkov }
2148bfc04aecSBorislav Petkov 
214933ca0643SBorislav Petkov static void __log_bus_error(struct mem_ctl_info *mci, struct err_info *err,
215033ca0643SBorislav Petkov 			    u8 ecc_type)
2151d27bf6faSDoug Thompson {
215233ca0643SBorislav Petkov 	enum hw_event_mc_err_type err_type;
215333ca0643SBorislav Petkov 	const char *string;
2154d27bf6faSDoug Thompson 
215533ca0643SBorislav Petkov 	if (ecc_type == 2)
215633ca0643SBorislav Petkov 		err_type = HW_EVENT_ERR_CORRECTED;
215733ca0643SBorislav Petkov 	else if (ecc_type == 1)
215833ca0643SBorislav Petkov 		err_type = HW_EVENT_ERR_UNCORRECTED;
215933ca0643SBorislav Petkov 	else {
216033ca0643SBorislav Petkov 		WARN(1, "Something is rotten in the state of Denmark.\n");
2161d27bf6faSDoug Thompson 		return;
2162d27bf6faSDoug Thompson 	}
2163d27bf6faSDoug Thompson 
216433ca0643SBorislav Petkov 	switch (err->err_code) {
216533ca0643SBorislav Petkov 	case DECODE_OK:
216633ca0643SBorislav Petkov 		string = "";
216733ca0643SBorislav Petkov 		break;
216833ca0643SBorislav Petkov 	case ERR_NODE:
216933ca0643SBorislav Petkov 		string = "Failed to map error addr to a node";
217033ca0643SBorislav Petkov 		break;
217133ca0643SBorislav Petkov 	case ERR_CSROW:
217233ca0643SBorislav Petkov 		string = "Failed to map error addr to a csrow";
217333ca0643SBorislav Petkov 		break;
217433ca0643SBorislav Petkov 	case ERR_CHANNEL:
217533ca0643SBorislav Petkov 		string = "unknown syndrome - possible error reporting race";
217633ca0643SBorislav Petkov 		break;
217733ca0643SBorislav Petkov 	default:
217833ca0643SBorislav Petkov 		string = "WTF error";
217933ca0643SBorislav Petkov 		break;
2180d27bf6faSDoug Thompson 	}
218133ca0643SBorislav Petkov 
218233ca0643SBorislav Petkov 	edac_mc_handle_error(err_type, mci, 1,
218333ca0643SBorislav Petkov 			     err->page, err->offset, err->syndrome,
218433ca0643SBorislav Petkov 			     err->csrow, err->channel, -1,
218533ca0643SBorislav Petkov 			     string, "");
2186d27bf6faSDoug Thompson }
2187d27bf6faSDoug Thompson 
2188df781d03SBorislav Petkov static inline void decode_bus_error(int node_id, struct mce *m)
2189d27bf6faSDoug Thompson {
21900c510cc8SDaniel J Blueman 	struct mem_ctl_info *mci;
21910c510cc8SDaniel J Blueman 	struct amd64_pvt *pvt;
2192f192c7b1SBorislav Petkov 	u8 ecc_type = (m->status >> 45) & 0x3;
219366fed2d4SBorislav Petkov 	u8 xec = XEC(m->status, 0x1f);
219466fed2d4SBorislav Petkov 	u16 ec = EC(m->status);
219533ca0643SBorislav Petkov 	u64 sys_addr;
219633ca0643SBorislav Petkov 	struct err_info err;
2197d27bf6faSDoug Thompson 
21980c510cc8SDaniel J Blueman 	mci = edac_mc_find(node_id);
21990c510cc8SDaniel J Blueman 	if (!mci)
22000c510cc8SDaniel J Blueman 		return;
22010c510cc8SDaniel J Blueman 
22020c510cc8SDaniel J Blueman 	pvt = mci->pvt_info;
22030c510cc8SDaniel J Blueman 
220466fed2d4SBorislav Petkov 	/* Bail out early if this was an 'observed' error */
22055980bb9cSBorislav Petkov 	if (PP(ec) == NBSL_PP_OBS)
2206b70ef010SBorislav Petkov 		return;
2207d27bf6faSDoug Thompson 
2208ecaf5606SBorislav Petkov 	/* Do only ECC errors */
2209ecaf5606SBorislav Petkov 	if (xec && xec != F10_NBSL_EXT_ERR_ECC)
2210d27bf6faSDoug Thompson 		return;
2211d27bf6faSDoug Thompson 
221233ca0643SBorislav Petkov 	memset(&err, 0, sizeof(err));
221333ca0643SBorislav Petkov 
2214a4b4bedcSBorislav Petkov 	sys_addr = get_error_address(pvt, m);
221533ca0643SBorislav Petkov 
2216ecaf5606SBorislav Petkov 	if (ecc_type == 2)
221733ca0643SBorislav Petkov 		err.syndrome = extract_syndrome(m->status);
221833ca0643SBorislav Petkov 
221933ca0643SBorislav Petkov 	pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err);
222033ca0643SBorislav Petkov 
222133ca0643SBorislav Petkov 	__log_bus_error(mci, &err, ecc_type);
2222d27bf6faSDoug Thompson }
2223d27bf6faSDoug Thompson 
22240ec449eeSDoug Thompson /*
22253f37a36bSBorislav Petkov  * Use pvt->F3 which contains the F3 CPU PCI device to get the related
22263f37a36bSBorislav Petkov  * F1 (AddrMap) and F2 (Dct) devices. Return negative value on error.
22270ec449eeSDoug Thompson  */
22283f37a36bSBorislav Petkov static int reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 f1_id, u16 f2_id)
22290ec449eeSDoug Thompson {
22300ec449eeSDoug Thompson 	/* Reserve the ADDRESS MAP Device */
22313f37a36bSBorislav Petkov 	pvt->F1 = pci_get_related_function(pvt->F3->vendor, f1_id, pvt->F3);
22328d5b5d9cSBorislav Petkov 	if (!pvt->F1) {
223324f9a7feSBorislav Petkov 		amd64_err("error address map device not found: "
22340ec449eeSDoug Thompson 			  "vendor %x device 0x%x (broken BIOS?)\n",
2235bbd0c1f6SBorislav Petkov 			  PCI_VENDOR_ID_AMD, f1_id);
2236bbd0c1f6SBorislav Petkov 		return -ENODEV;
22370ec449eeSDoug Thompson 	}
22380ec449eeSDoug Thompson 
22393f37a36bSBorislav Petkov 	/* Reserve the DCT Device */
22403f37a36bSBorislav Petkov 	pvt->F2 = pci_get_related_function(pvt->F3->vendor, f2_id, pvt->F3);
22413f37a36bSBorislav Petkov 	if (!pvt->F2) {
22428d5b5d9cSBorislav Petkov 		pci_dev_put(pvt->F1);
22438d5b5d9cSBorislav Petkov 		pvt->F1 = NULL;
22440ec449eeSDoug Thompson 
22453f37a36bSBorislav Petkov 		amd64_err("error F2 device not found: "
22460ec449eeSDoug Thompson 			  "vendor %x device 0x%x (broken BIOS?)\n",
22473f37a36bSBorislav Petkov 			  PCI_VENDOR_ID_AMD, f2_id);
22488d5b5d9cSBorislav Petkov 
2249bbd0c1f6SBorislav Petkov 		return -ENODEV;
22500ec449eeSDoug Thompson 	}
2251956b9ba1SJoe Perches 	edac_dbg(1, "F1: %s\n", pci_name(pvt->F1));
2252956b9ba1SJoe Perches 	edac_dbg(1, "F2: %s\n", pci_name(pvt->F2));
2253956b9ba1SJoe Perches 	edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
22540ec449eeSDoug Thompson 
22550ec449eeSDoug Thompson 	return 0;
22560ec449eeSDoug Thompson }
22570ec449eeSDoug Thompson 
2258360b7f3cSBorislav Petkov static void free_mc_sibling_devs(struct amd64_pvt *pvt)
22590ec449eeSDoug Thompson {
22608d5b5d9cSBorislav Petkov 	pci_dev_put(pvt->F1);
22613f37a36bSBorislav Petkov 	pci_dev_put(pvt->F2);
22620ec449eeSDoug Thompson }
22630ec449eeSDoug Thompson 
22640ec449eeSDoug Thompson /*
22650ec449eeSDoug Thompson  * Retrieve the hardware registers of the memory controller (this includes the
22660ec449eeSDoug Thompson  * 'Address Map' and 'Misc' device regs)
22670ec449eeSDoug Thompson  */
2268360b7f3cSBorislav Petkov static void read_mc_regs(struct amd64_pvt *pvt)
22690ec449eeSDoug Thompson {
2270a4b4bedcSBorislav Petkov 	unsigned range;
22710ec449eeSDoug Thompson 	u64 msr_val;
2272ad6a32e9SBorislav Petkov 	u32 tmp;
22730ec449eeSDoug Thompson 
22740ec449eeSDoug Thompson 	/*
22750ec449eeSDoug Thompson 	 * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
22760ec449eeSDoug Thompson 	 * those are Read-As-Zero
22770ec449eeSDoug Thompson 	 */
2278e97f8bb8SBorislav Petkov 	rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
2279956b9ba1SJoe Perches 	edac_dbg(0, "  TOP_MEM:  0x%016llx\n", pvt->top_mem);
22800ec449eeSDoug Thompson 
22810ec449eeSDoug Thompson 	/* check first whether TOP_MEM2 is enabled */
22820ec449eeSDoug Thompson 	rdmsrl(MSR_K8_SYSCFG, msr_val);
22830ec449eeSDoug Thompson 	if (msr_val & (1U << 21)) {
2284e97f8bb8SBorislav Petkov 		rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
2285956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
22860ec449eeSDoug Thompson 	} else
2287956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2 disabled\n");
22880ec449eeSDoug Thompson 
22895980bb9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap);
22900ec449eeSDoug Thompson 
22915a5d2371SBorislav Petkov 	read_dram_ctl_register(pvt);
22920ec449eeSDoug Thompson 
22937f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
22947f19bf75SBorislav Petkov 		u8 rw;
22950ec449eeSDoug Thompson 
22967f19bf75SBorislav Petkov 		/* read settings for this DRAM range */
22977f19bf75SBorislav Petkov 		read_dram_base_limit_regs(pvt, range);
2298e97f8bb8SBorislav Petkov 
22997f19bf75SBorislav Petkov 		rw = dram_rw(pvt, range);
23007f19bf75SBorislav Petkov 		if (!rw)
23017f19bf75SBorislav Petkov 			continue;
23027f19bf75SBorislav Petkov 
2303956b9ba1SJoe Perches 		edac_dbg(1, "  DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
23047f19bf75SBorislav Petkov 			 range,
23057f19bf75SBorislav Petkov 			 get_dram_base(pvt, range),
23067f19bf75SBorislav Petkov 			 get_dram_limit(pvt, range));
23077f19bf75SBorislav Petkov 
2308956b9ba1SJoe Perches 		edac_dbg(1, "   IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
23097f19bf75SBorislav Petkov 			 dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
23107f19bf75SBorislav Petkov 			 (rw & 0x1) ? "R" : "-",
23117f19bf75SBorislav Petkov 			 (rw & 0x2) ? "W" : "-",
23127f19bf75SBorislav Petkov 			 dram_intlv_sel(pvt, range),
23137f19bf75SBorislav Petkov 			 dram_dst_node(pvt, range));
23140ec449eeSDoug Thompson 	}
23150ec449eeSDoug Thompson 
2316b2b0c605SBorislav Petkov 	read_dct_base_mask(pvt);
23170ec449eeSDoug Thompson 
2318bc21fa57SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar);
23197981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DBAM0, &pvt->dbam0);
23200ec449eeSDoug Thompson 
23218d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare);
23220ec449eeSDoug Thompson 
23237981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DCLR0, &pvt->dclr0);
23247981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DCHR0, &pvt->dchr0);
23250ec449eeSDoug Thompson 
232678da121eSBorislav Petkov 	if (!dct_ganging_enabled(pvt)) {
23277981a28fSAravind Gopalakrishnan 		amd64_read_dct_pci_cfg(pvt, 1, DCLR0, &pvt->dclr1);
23287981a28fSAravind Gopalakrishnan 		amd64_read_dct_pci_cfg(pvt, 1, DCHR0, &pvt->dchr1);
23290ec449eeSDoug Thompson 	}
2330b2b0c605SBorislav Petkov 
2331a3b7db09SBorislav Petkov 	pvt->ecc_sym_sz = 4;
2332a597d2a5SAravind Gopalakrishnan 	determine_memory_type(pvt);
2333a597d2a5SAravind Gopalakrishnan 	edac_dbg(1, "  DIMM type: %s\n", edac_mem_types[pvt->dram_type]);
2334a3b7db09SBorislav Petkov 
2335a4b4bedcSBorislav Petkov 	if (pvt->fam >= 0x10) {
23368d5b5d9cSBorislav Petkov 		amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp);
23377981a28fSAravind Gopalakrishnan 		/* F16h has only DCT0, so no need to read dbam1 */
2338a4b4bedcSBorislav Petkov 		if (pvt->fam != 0x16)
23397981a28fSAravind Gopalakrishnan 			amd64_read_dct_pci_cfg(pvt, 1, DBAM0, &pvt->dbam1);
2340a3b7db09SBorislav Petkov 
2341a3b7db09SBorislav Petkov 		/* F10h, revD and later can do x8 ECC too */
2342a4b4bedcSBorislav Petkov 		if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25))
2343a3b7db09SBorislav Petkov 			pvt->ecc_sym_sz = 8;
2344525a1b20SBorislav Petkov 	}
2345b2b0c605SBorislav Petkov 	dump_misc_regs(pvt);
23460ec449eeSDoug Thompson }
23470ec449eeSDoug Thompson 
23480ec449eeSDoug Thompson /*
23490ec449eeSDoug Thompson  * NOTE: CPU Revision Dependent code
23500ec449eeSDoug Thompson  *
23510ec449eeSDoug Thompson  * Input:
235211c75eadSBorislav Petkov  *	@csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1)
23530ec449eeSDoug Thompson  *	k8 private pointer to -->
23540ec449eeSDoug Thompson  *			DRAM Bank Address mapping register
23550ec449eeSDoug Thompson  *			node_id
23560ec449eeSDoug Thompson  *			DCL register where dual_channel_active is
23570ec449eeSDoug Thompson  *
23580ec449eeSDoug Thompson  * The DBAM register consists of 4 sets of 4 bits each definitions:
23590ec449eeSDoug Thompson  *
23600ec449eeSDoug Thompson  * Bits:	CSROWs
23610ec449eeSDoug Thompson  * 0-3		CSROWs 0 and 1
23620ec449eeSDoug Thompson  * 4-7		CSROWs 2 and 3
23630ec449eeSDoug Thompson  * 8-11		CSROWs 4 and 5
23640ec449eeSDoug Thompson  * 12-15	CSROWs 6 and 7
23650ec449eeSDoug Thompson  *
23660ec449eeSDoug Thompson  * Values range from: 0 to 15
23670ec449eeSDoug Thompson  * The meaning of the values depends on CPU revision and dual-channel state,
23680ec449eeSDoug Thompson  * see relevant BKDG more info.
23690ec449eeSDoug Thompson  *
23700ec449eeSDoug Thompson  * The memory controller provides for total of only 8 CSROWs in its current
23710ec449eeSDoug Thompson  * architecture. Each "pair" of CSROWs normally represents just one DIMM in
23720ec449eeSDoug Thompson  * single channel or two (2) DIMMs in dual channel mode.
23730ec449eeSDoug Thompson  *
23740ec449eeSDoug Thompson  * The following code logic collapses the various tables for CSROW based on CPU
23750ec449eeSDoug Thompson  * revision.
23760ec449eeSDoug Thompson  *
23770ec449eeSDoug Thompson  * Returns:
23780ec449eeSDoug Thompson  *	The number of PAGE_SIZE pages on the specified CSROW number it
23790ec449eeSDoug Thompson  *	encompasses
23800ec449eeSDoug Thompson  *
23810ec449eeSDoug Thompson  */
2382d1ea71cdSBorislav Petkov static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr)
23830ec449eeSDoug Thompson {
23841433eb99SBorislav Petkov 	u32 cs_mode, nr_pages;
2385f92cae45SAshish Shenoy 	u32 dbam = dct ? pvt->dbam1 : pvt->dbam0;
23860ec449eeSDoug Thompson 
238710de6497SBorislav Petkov 
23880ec449eeSDoug Thompson 	/*
23890ec449eeSDoug Thompson 	 * The math on this doesn't look right on the surface because x/2*4 can
23900ec449eeSDoug Thompson 	 * be simplified to x*2 but this expression makes use of the fact that
23910ec449eeSDoug Thompson 	 * it is integral math where 1/2=0. This intermediate value becomes the
23920ec449eeSDoug Thompson 	 * number of bits to shift the DBAM register to extract the proper CSROW
23930ec449eeSDoug Thompson 	 * field.
23940ec449eeSDoug Thompson 	 */
23950a5dfc31SBorislav Petkov 	cs_mode = DBAM_DIMM(csrow_nr / 2, dbam);
23960ec449eeSDoug Thompson 
2397a597d2a5SAravind Gopalakrishnan 	nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, (csrow_nr / 2))
2398a597d2a5SAravind Gopalakrishnan 							   << (20 - PAGE_SHIFT);
23990ec449eeSDoug Thompson 
240010de6497SBorislav Petkov 	edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n",
240110de6497SBorislav Petkov 		    csrow_nr, dct,  cs_mode);
240210de6497SBorislav Petkov 	edac_dbg(0, "nr_pages/channel: %u\n", nr_pages);
24030ec449eeSDoug Thompson 
24040ec449eeSDoug Thompson 	return nr_pages;
24050ec449eeSDoug Thompson }
24060ec449eeSDoug Thompson 
24070ec449eeSDoug Thompson /*
24080ec449eeSDoug Thompson  * Initialize the array of csrow attribute instances, based on the values
24090ec449eeSDoug Thompson  * from pci config hardware registers.
24100ec449eeSDoug Thompson  */
2411360b7f3cSBorislav Petkov static int init_csrows(struct mem_ctl_info *mci)
24120ec449eeSDoug Thompson {
241310de6497SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
24140ec449eeSDoug Thompson 	struct csrow_info *csrow;
2415de3910ebSMauro Carvalho Chehab 	struct dimm_info *dimm;
2416084a4fccSMauro Carvalho Chehab 	enum edac_type edac_mode;
241710de6497SBorislav Petkov 	int i, j, empty = 1;
2418a895bf8bSMauro Carvalho Chehab 	int nr_pages = 0;
241910de6497SBorislav Petkov 	u32 val;
24200ec449eeSDoug Thompson 
2421a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
24220ec449eeSDoug Thompson 
24232299ef71SBorislav Petkov 	pvt->nbcfg = val;
24240ec449eeSDoug Thompson 
2425956b9ba1SJoe Perches 	edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
24262299ef71SBorislav Petkov 		 pvt->mc_node_id, val,
2427a97fa68eSBorislav Petkov 		 !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
24280ec449eeSDoug Thompson 
242910de6497SBorislav Petkov 	/*
243010de6497SBorislav Petkov 	 * We iterate over DCT0 here but we look at DCT1 in parallel, if needed.
243110de6497SBorislav Petkov 	 */
243211c75eadSBorislav Petkov 	for_each_chip_select(i, 0, pvt) {
243310de6497SBorislav Petkov 		bool row_dct0 = !!csrow_enabled(i, 0, pvt);
243410de6497SBorislav Petkov 		bool row_dct1 = false;
24350ec449eeSDoug Thompson 
2436a4b4bedcSBorislav Petkov 		if (pvt->fam != 0xf)
243710de6497SBorislav Petkov 			row_dct1 = !!csrow_enabled(i, 1, pvt);
243810de6497SBorislav Petkov 
243910de6497SBorislav Petkov 		if (!row_dct0 && !row_dct1)
24400ec449eeSDoug Thompson 			continue;
24410ec449eeSDoug Thompson 
244210de6497SBorislav Petkov 		csrow = mci->csrows[i];
24430ec449eeSDoug Thompson 		empty = 0;
244411c75eadSBorislav Petkov 
244510de6497SBorislav Petkov 		edac_dbg(1, "MC node: %d, csrow: %d\n",
244610de6497SBorislav Petkov 			    pvt->mc_node_id, i);
244710de6497SBorislav Petkov 
24481eef1282SMauro Carvalho Chehab 		if (row_dct0) {
2449d1ea71cdSBorislav Petkov 			nr_pages = get_csrow_nr_pages(pvt, 0, i);
24501eef1282SMauro Carvalho Chehab 			csrow->channels[0]->dimm->nr_pages = nr_pages;
24511eef1282SMauro Carvalho Chehab 		}
245210de6497SBorislav Petkov 
245310de6497SBorislav Petkov 		/* K8 has only one DCT */
2454a4b4bedcSBorislav Petkov 		if (pvt->fam != 0xf && row_dct1) {
2455d1ea71cdSBorislav Petkov 			int row_dct1_pages = get_csrow_nr_pages(pvt, 1, i);
24561eef1282SMauro Carvalho Chehab 
24571eef1282SMauro Carvalho Chehab 			csrow->channels[1]->dimm->nr_pages = row_dct1_pages;
24581eef1282SMauro Carvalho Chehab 			nr_pages += row_dct1_pages;
24591eef1282SMauro Carvalho Chehab 		}
24600ec449eeSDoug Thompson 
246110de6497SBorislav Petkov 		edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages);
24620ec449eeSDoug Thompson 
24630ec449eeSDoug Thompson 		/*
24640ec449eeSDoug Thompson 		 * determine whether CHIPKILL or JUST ECC or NO ECC is operating
24650ec449eeSDoug Thompson 		 */
2466a97fa68eSBorislav Petkov 		if (pvt->nbcfg & NBCFG_ECC_ENABLE)
2467084a4fccSMauro Carvalho Chehab 			edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) ?
24680ec449eeSDoug Thompson 				    EDAC_S4ECD4ED : EDAC_SECDED;
24690ec449eeSDoug Thompson 		else
2470084a4fccSMauro Carvalho Chehab 			edac_mode = EDAC_NONE;
2471084a4fccSMauro Carvalho Chehab 
2472084a4fccSMauro Carvalho Chehab 		for (j = 0; j < pvt->channel_count; j++) {
2473de3910ebSMauro Carvalho Chehab 			dimm = csrow->channels[j]->dimm;
2474a597d2a5SAravind Gopalakrishnan 			dimm->mtype = pvt->dram_type;
2475de3910ebSMauro Carvalho Chehab 			dimm->edac_mode = edac_mode;
2476084a4fccSMauro Carvalho Chehab 		}
24770ec449eeSDoug Thompson 	}
24780ec449eeSDoug Thompson 
24790ec449eeSDoug Thompson 	return empty;
24800ec449eeSDoug Thompson }
2481d27bf6faSDoug Thompson 
248206724535SBorislav Petkov /* get all cores on this DCT */
24838b84c8dfSDaniel J Blueman static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, u16 nid)
2484f9431992SDoug Thompson {
248506724535SBorislav Petkov 	int cpu;
2486f9431992SDoug Thompson 
248706724535SBorislav Petkov 	for_each_online_cpu(cpu)
248806724535SBorislav Petkov 		if (amd_get_nb_id(cpu) == nid)
248906724535SBorislav Petkov 			cpumask_set_cpu(cpu, mask);
2490f9431992SDoug Thompson }
2491f9431992SDoug Thompson 
2492f9431992SDoug Thompson /* check MCG_CTL on all the cpus on this node */
2493d1ea71cdSBorislav Petkov static bool nb_mce_bank_enabled_on_node(u16 nid)
2494f9431992SDoug Thompson {
2495ba578cb3SRusty Russell 	cpumask_var_t mask;
249650542251SBorislav Petkov 	int cpu, nbe;
249706724535SBorislav Petkov 	bool ret = false;
2498f9431992SDoug Thompson 
2499ba578cb3SRusty Russell 	if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
250024f9a7feSBorislav Petkov 		amd64_warn("%s: Error allocating mask\n", __func__);
250106724535SBorislav Petkov 		return false;
250206724535SBorislav Petkov 	}
250306724535SBorislav Petkov 
2504ba578cb3SRusty Russell 	get_cpus_on_this_dct_cpumask(mask, nid);
250506724535SBorislav Petkov 
2506ba578cb3SRusty Russell 	rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
2507ba578cb3SRusty Russell 
2508ba578cb3SRusty Russell 	for_each_cpu(cpu, mask) {
250950542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
25105980bb9cSBorislav Petkov 		nbe = reg->l & MSR_MCGCTL_NBE;
251106724535SBorislav Petkov 
2512956b9ba1SJoe Perches 		edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
251350542251SBorislav Petkov 			 cpu, reg->q,
251406724535SBorislav Petkov 			 (nbe ? "enabled" : "disabled"));
251506724535SBorislav Petkov 
251606724535SBorislav Petkov 		if (!nbe)
251706724535SBorislav Petkov 			goto out;
251806724535SBorislav Petkov 	}
251906724535SBorislav Petkov 	ret = true;
252006724535SBorislav Petkov 
252106724535SBorislav Petkov out:
2522ba578cb3SRusty Russell 	free_cpumask_var(mask);
2523f9431992SDoug Thompson 	return ret;
2524f9431992SDoug Thompson }
2525f9431992SDoug Thompson 
2526c7e5301aSDaniel J Blueman static int toggle_ecc_err_reporting(struct ecc_settings *s, u16 nid, bool on)
2527f6d6ae96SBorislav Petkov {
2528f6d6ae96SBorislav Petkov 	cpumask_var_t cmask;
252950542251SBorislav Petkov 	int cpu;
2530f6d6ae96SBorislav Petkov 
2531f6d6ae96SBorislav Petkov 	if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
253224f9a7feSBorislav Petkov 		amd64_warn("%s: error allocating mask\n", __func__);
2533f6d6ae96SBorislav Petkov 		return false;
2534f6d6ae96SBorislav Petkov 	}
2535f6d6ae96SBorislav Petkov 
2536ae7bb7c6SBorislav Petkov 	get_cpus_on_this_dct_cpumask(cmask, nid);
2537f6d6ae96SBorislav Petkov 
2538f6d6ae96SBorislav Petkov 	rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2539f6d6ae96SBorislav Petkov 
2540f6d6ae96SBorislav Petkov 	for_each_cpu(cpu, cmask) {
2541f6d6ae96SBorislav Petkov 
254250542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
254350542251SBorislav Petkov 
2544f6d6ae96SBorislav Petkov 		if (on) {
25455980bb9cSBorislav Petkov 			if (reg->l & MSR_MCGCTL_NBE)
2546ae7bb7c6SBorislav Petkov 				s->flags.nb_mce_enable = 1;
2547f6d6ae96SBorislav Petkov 
25485980bb9cSBorislav Petkov 			reg->l |= MSR_MCGCTL_NBE;
2549f6d6ae96SBorislav Petkov 		} else {
2550f6d6ae96SBorislav Petkov 			/*
2551d95cf4deSBorislav Petkov 			 * Turn off NB MCE reporting only when it was off before
2552f6d6ae96SBorislav Petkov 			 */
2553ae7bb7c6SBorislav Petkov 			if (!s->flags.nb_mce_enable)
25545980bb9cSBorislav Petkov 				reg->l &= ~MSR_MCGCTL_NBE;
2555f6d6ae96SBorislav Petkov 		}
2556f6d6ae96SBorislav Petkov 	}
2557f6d6ae96SBorislav Petkov 	wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2558f6d6ae96SBorislav Petkov 
2559f6d6ae96SBorislav Petkov 	free_cpumask_var(cmask);
2560f6d6ae96SBorislav Petkov 
2561f6d6ae96SBorislav Petkov 	return 0;
2562f6d6ae96SBorislav Petkov }
2563f6d6ae96SBorislav Petkov 
2564c7e5301aSDaniel J Blueman static bool enable_ecc_error_reporting(struct ecc_settings *s, u16 nid,
25652299ef71SBorislav Petkov 				       struct pci_dev *F3)
2566f6d6ae96SBorislav Petkov {
25672299ef71SBorislav Petkov 	bool ret = true;
2568c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
2569f6d6ae96SBorislav Petkov 
25702299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, ON)) {
25712299ef71SBorislav Petkov 		amd64_warn("Error enabling ECC reporting over MCGCTL!\n");
25722299ef71SBorislav Petkov 		return false;
25732299ef71SBorislav Petkov 	}
25742299ef71SBorislav Petkov 
2575c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
2576f6d6ae96SBorislav Petkov 
2577ae7bb7c6SBorislav Petkov 	s->old_nbctl   = value & mask;
2578ae7bb7c6SBorislav Petkov 	s->nbctl_valid = true;
2579f6d6ae96SBorislav Petkov 
2580f6d6ae96SBorislav Petkov 	value |= mask;
2581c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
2582f6d6ae96SBorislav Petkov 
2583a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCFG, &value);
2584f6d6ae96SBorislav Petkov 
2585956b9ba1SJoe Perches 	edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2586a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
2587f6d6ae96SBorislav Petkov 
2588a97fa68eSBorislav Petkov 	if (!(value & NBCFG_ECC_ENABLE)) {
258924f9a7feSBorislav Petkov 		amd64_warn("DRAM ECC disabled on this node, enabling...\n");
2590f6d6ae96SBorislav Petkov 
2591ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 0;
2592d95cf4deSBorislav Petkov 
2593f6d6ae96SBorislav Petkov 		/* Attempt to turn on DRAM ECC Enable */
2594a97fa68eSBorislav Petkov 		value |= NBCFG_ECC_ENABLE;
2595a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
2596f6d6ae96SBorislav Petkov 
2597a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
2598f6d6ae96SBorislav Petkov 
2599a97fa68eSBorislav Petkov 		if (!(value & NBCFG_ECC_ENABLE)) {
260024f9a7feSBorislav Petkov 			amd64_warn("Hardware rejected DRAM ECC enable,"
260124f9a7feSBorislav Petkov 				   "check memory DIMM configuration.\n");
26022299ef71SBorislav Petkov 			ret = false;
2603f6d6ae96SBorislav Petkov 		} else {
260424f9a7feSBorislav Petkov 			amd64_info("Hardware accepted DRAM ECC Enable\n");
2605f6d6ae96SBorislav Petkov 		}
2606d95cf4deSBorislav Petkov 	} else {
2607ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 1;
2608f6d6ae96SBorislav Petkov 	}
2609d95cf4deSBorislav Petkov 
2610956b9ba1SJoe Perches 	edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2611a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
2612f6d6ae96SBorislav Petkov 
26132299ef71SBorislav Petkov 	return ret;
2614f6d6ae96SBorislav Petkov }
2615f6d6ae96SBorislav Petkov 
2616c7e5301aSDaniel J Blueman static void restore_ecc_error_reporting(struct ecc_settings *s, u16 nid,
2617360b7f3cSBorislav Petkov 					struct pci_dev *F3)
2618f6d6ae96SBorislav Petkov {
2619c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
2620c9f4f26eSBorislav Petkov 
2621f6d6ae96SBorislav Petkov 
2622ae7bb7c6SBorislav Petkov 	if (!s->nbctl_valid)
2623f6d6ae96SBorislav Petkov 		return;
2624f6d6ae96SBorislav Petkov 
2625c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
2626f6d6ae96SBorislav Petkov 	value &= ~mask;
2627ae7bb7c6SBorislav Petkov 	value |= s->old_nbctl;
2628f6d6ae96SBorislav Petkov 
2629c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
2630f6d6ae96SBorislav Petkov 
2631ae7bb7c6SBorislav Petkov 	/* restore previous BIOS DRAM ECC "off" setting we force-enabled */
2632ae7bb7c6SBorislav Petkov 	if (!s->flags.nb_ecc_prev) {
2633a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
2634a97fa68eSBorislav Petkov 		value &= ~NBCFG_ECC_ENABLE;
2635a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
2636d95cf4deSBorislav Petkov 	}
2637d95cf4deSBorislav Petkov 
2638d95cf4deSBorislav Petkov 	/* restore the NB Enable MCGCTL bit */
26392299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, OFF))
264024f9a7feSBorislav Petkov 		amd64_warn("Error restoring NB MCGCTL settings!\n");
2641f6d6ae96SBorislav Petkov }
2642f6d6ae96SBorislav Petkov 
2643f9431992SDoug Thompson /*
26442299ef71SBorislav Petkov  * EDAC requires that the BIOS have ECC enabled before
26452299ef71SBorislav Petkov  * taking over the processing of ECC errors. A command line
26462299ef71SBorislav Petkov  * option allows to force-enable hardware ECC later in
26472299ef71SBorislav Petkov  * enable_ecc_error_reporting().
2648f9431992SDoug Thompson  */
2649cab4d277SBorislav Petkov static const char *ecc_msg =
2650cab4d277SBorislav Petkov 	"ECC disabled in the BIOS or no ECC capability, module will not load.\n"
2651cab4d277SBorislav Petkov 	" Either enable ECC checking or force module loading by setting "
2652cab4d277SBorislav Petkov 	"'ecc_enable_override'.\n"
2653cab4d277SBorislav Petkov 	" (Note that use of the override may cause unknown side effects.)\n";
2654be3468e8SBorislav Petkov 
2655c7e5301aSDaniel J Blueman static bool ecc_enabled(struct pci_dev *F3, u16 nid)
2656f9431992SDoug Thompson {
2657f9431992SDoug Thompson 	u32 value;
26582299ef71SBorislav Petkov 	u8 ecc_en = 0;
265906724535SBorislav Petkov 	bool nb_mce_en = false;
2660f9431992SDoug Thompson 
2661a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCFG, &value);
2662f9431992SDoug Thompson 
2663a97fa68eSBorislav Petkov 	ecc_en = !!(value & NBCFG_ECC_ENABLE);
26642299ef71SBorislav Petkov 	amd64_info("DRAM ECC %s.\n", (ecc_en ? "enabled" : "disabled"));
2665be3468e8SBorislav Petkov 
2666d1ea71cdSBorislav Petkov 	nb_mce_en = nb_mce_bank_enabled_on_node(nid);
266706724535SBorislav Petkov 	if (!nb_mce_en)
26682299ef71SBorislav Petkov 		amd64_notice("NB MCE bank disabled, set MSR "
26692299ef71SBorislav Petkov 			     "0x%08x[4] on node %d to enable.\n",
26702299ef71SBorislav Petkov 			     MSR_IA32_MCG_CTL, nid);
2671be3468e8SBorislav Petkov 
26722299ef71SBorislav Petkov 	if (!ecc_en || !nb_mce_en) {
267324f9a7feSBorislav Petkov 		amd64_notice("%s", ecc_msg);
26742299ef71SBorislav Petkov 		return false;
2675be3468e8SBorislav Petkov 	}
26762299ef71SBorislav Petkov 	return true;
2677f9431992SDoug Thompson }
2678f9431992SDoug Thompson 
2679df71a053SBorislav Petkov static void setup_mci_misc_attrs(struct mem_ctl_info *mci,
2680df71a053SBorislav Petkov 				 struct amd64_family_type *fam)
26817d6034d3SDoug Thompson {
26827d6034d3SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
26837d6034d3SDoug Thompson 
26847d6034d3SDoug Thompson 	mci->mtype_cap		= MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
26857d6034d3SDoug Thompson 	mci->edac_ctl_cap	= EDAC_FLAG_NONE;
26867d6034d3SDoug Thompson 
26875980bb9cSBorislav Petkov 	if (pvt->nbcap & NBCAP_SECDED)
26887d6034d3SDoug Thompson 		mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
26897d6034d3SDoug Thompson 
26905980bb9cSBorislav Petkov 	if (pvt->nbcap & NBCAP_CHIPKILL)
26917d6034d3SDoug Thompson 		mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
26927d6034d3SDoug Thompson 
2693d1ea71cdSBorislav Petkov 	mci->edac_cap		= determine_edac_cap(pvt);
26947d6034d3SDoug Thompson 	mci->mod_name		= EDAC_MOD_STR;
26957d6034d3SDoug Thompson 	mci->mod_ver		= EDAC_AMD64_VERSION;
2696df71a053SBorislav Petkov 	mci->ctl_name		= fam->ctl_name;
26978d5b5d9cSBorislav Petkov 	mci->dev_name		= pci_name(pvt->F2);
26987d6034d3SDoug Thompson 	mci->ctl_page_to_phys	= NULL;
26997d6034d3SDoug Thompson 
27007d6034d3SDoug Thompson 	/* memory scrubber interface */
2701d1ea71cdSBorislav Petkov 	mci->set_sdram_scrub_rate = set_scrub_rate;
2702d1ea71cdSBorislav Petkov 	mci->get_sdram_scrub_rate = get_scrub_rate;
27037d6034d3SDoug Thompson }
27047d6034d3SDoug Thompson 
27050092b20dSBorislav Petkov /*
27060092b20dSBorislav Petkov  * returns a pointer to the family descriptor on success, NULL otherwise.
27070092b20dSBorislav Petkov  */
2708d1ea71cdSBorislav Petkov static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt)
2709395ae783SBorislav Petkov {
27100092b20dSBorislav Petkov 	struct amd64_family_type *fam_type = NULL;
27110092b20dSBorislav Petkov 
271218b94f66SAravind Gopalakrishnan 	pvt->ext_model  = boot_cpu_data.x86_model >> 4;
2713a4b4bedcSBorislav Petkov 	pvt->stepping	= boot_cpu_data.x86_mask;
271418b94f66SAravind Gopalakrishnan 	pvt->model	= boot_cpu_data.x86_model;
271518b94f66SAravind Gopalakrishnan 	pvt->fam	= boot_cpu_data.x86;
271618b94f66SAravind Gopalakrishnan 
271718b94f66SAravind Gopalakrishnan 	switch (pvt->fam) {
2718395ae783SBorislav Petkov 	case 0xf:
2719d1ea71cdSBorislav Petkov 		fam_type	= &family_types[K8_CPUS];
2720d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[K8_CPUS].ops;
2721395ae783SBorislav Petkov 		break;
2722df71a053SBorislav Petkov 
2723395ae783SBorislav Petkov 	case 0x10:
2724d1ea71cdSBorislav Petkov 		fam_type	= &family_types[F10_CPUS];
2725d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[F10_CPUS].ops;
2726df71a053SBorislav Petkov 		break;
2727df71a053SBorislav Petkov 
2728df71a053SBorislav Petkov 	case 0x15:
272918b94f66SAravind Gopalakrishnan 		if (pvt->model == 0x30) {
2730d1ea71cdSBorislav Petkov 			fam_type = &family_types[F15_M30H_CPUS];
2731d1ea71cdSBorislav Petkov 			pvt->ops = &family_types[F15_M30H_CPUS].ops;
273218b94f66SAravind Gopalakrishnan 			break;
2733a597d2a5SAravind Gopalakrishnan 		} else if (pvt->model == 0x60) {
2734a597d2a5SAravind Gopalakrishnan 			fam_type = &family_types[F15_M60H_CPUS];
2735a597d2a5SAravind Gopalakrishnan 			pvt->ops = &family_types[F15_M60H_CPUS].ops;
2736a597d2a5SAravind Gopalakrishnan 			break;
273718b94f66SAravind Gopalakrishnan 		}
273818b94f66SAravind Gopalakrishnan 
2739d1ea71cdSBorislav Petkov 		fam_type	= &family_types[F15_CPUS];
2740d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[F15_CPUS].ops;
2741395ae783SBorislav Petkov 		break;
2742395ae783SBorislav Petkov 
274394c1acf2SAravind Gopalakrishnan 	case 0x16:
274485a8885bSAravind Gopalakrishnan 		if (pvt->model == 0x30) {
274585a8885bSAravind Gopalakrishnan 			fam_type = &family_types[F16_M30H_CPUS];
274685a8885bSAravind Gopalakrishnan 			pvt->ops = &family_types[F16_M30H_CPUS].ops;
274785a8885bSAravind Gopalakrishnan 			break;
274885a8885bSAravind Gopalakrishnan 		}
2749d1ea71cdSBorislav Petkov 		fam_type	= &family_types[F16_CPUS];
2750d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[F16_CPUS].ops;
275194c1acf2SAravind Gopalakrishnan 		break;
275294c1acf2SAravind Gopalakrishnan 
2753395ae783SBorislav Petkov 	default:
275424f9a7feSBorislav Petkov 		amd64_err("Unsupported family!\n");
27550092b20dSBorislav Petkov 		return NULL;
2756395ae783SBorislav Petkov 	}
27570092b20dSBorislav Petkov 
2758df71a053SBorislav Petkov 	amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name,
275918b94f66SAravind Gopalakrishnan 		     (pvt->fam == 0xf ?
27600092b20dSBorislav Petkov 				(pvt->ext_model >= K8_REV_F  ? "revF or later "
27610092b20dSBorislav Petkov 							     : "revE or earlier ")
276224f9a7feSBorislav Petkov 				 : ""), pvt->mc_node_id);
27630092b20dSBorislav Petkov 	return fam_type;
2764395ae783SBorislav Petkov }
2765395ae783SBorislav Petkov 
2766e339f1ecSTakashi Iwai static const struct attribute_group *amd64_edac_attr_groups[] = {
2767e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_DEBUG
2768e339f1ecSTakashi Iwai 	&amd64_edac_dbg_group,
2769e339f1ecSTakashi Iwai #endif
2770e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION
2771e339f1ecSTakashi Iwai 	&amd64_edac_inj_group,
2772e339f1ecSTakashi Iwai #endif
2773e339f1ecSTakashi Iwai 	NULL
2774e339f1ecSTakashi Iwai };
2775e339f1ecSTakashi Iwai 
27763f37a36bSBorislav Petkov static int init_one_instance(unsigned int nid)
27777d6034d3SDoug Thompson {
27783f37a36bSBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
27790092b20dSBorislav Petkov 	struct amd64_family_type *fam_type = NULL;
2780360b7f3cSBorislav Petkov 	struct mem_ctl_info *mci = NULL;
2781ab5a503cSMauro Carvalho Chehab 	struct edac_mc_layer layers[2];
27823f37a36bSBorislav Petkov 	struct amd64_pvt *pvt = NULL;
27837d6034d3SDoug Thompson 	int err = 0, ret;
27847d6034d3SDoug Thompson 
27857d6034d3SDoug Thompson 	ret = -ENOMEM;
27867d6034d3SDoug Thompson 	pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
27877d6034d3SDoug Thompson 	if (!pvt)
2788360b7f3cSBorislav Petkov 		goto err_ret;
27897d6034d3SDoug Thompson 
2790360b7f3cSBorislav Petkov 	pvt->mc_node_id	= nid;
27913f37a36bSBorislav Petkov 	pvt->F3 = F3;
27927d6034d3SDoug Thompson 
2793395ae783SBorislav Petkov 	ret = -EINVAL;
2794d1ea71cdSBorislav Petkov 	fam_type = per_family_init(pvt);
27950092b20dSBorislav Petkov 	if (!fam_type)
2796395ae783SBorislav Petkov 		goto err_free;
2797395ae783SBorislav Petkov 
27987d6034d3SDoug Thompson 	ret = -ENODEV;
27993f37a36bSBorislav Petkov 	err = reserve_mc_sibling_devs(pvt, fam_type->f1_id, fam_type->f2_id);
28007d6034d3SDoug Thompson 	if (err)
28017d6034d3SDoug Thompson 		goto err_free;
28027d6034d3SDoug Thompson 
2803360b7f3cSBorislav Petkov 	read_mc_regs(pvt);
28047d6034d3SDoug Thompson 
28057d6034d3SDoug Thompson 	/*
28067d6034d3SDoug Thompson 	 * We need to determine how many memory channels there are. Then use
28077d6034d3SDoug Thompson 	 * that information for calculating the size of the dynamic instance
2808360b7f3cSBorislav Petkov 	 * tables in the 'mci' structure.
28097d6034d3SDoug Thompson 	 */
2810360b7f3cSBorislav Petkov 	ret = -EINVAL;
28117d6034d3SDoug Thompson 	pvt->channel_count = pvt->ops->early_channel_count(pvt);
28127d6034d3SDoug Thompson 	if (pvt->channel_count < 0)
2813360b7f3cSBorislav Petkov 		goto err_siblings;
28147d6034d3SDoug Thompson 
28157d6034d3SDoug Thompson 	ret = -ENOMEM;
2816ab5a503cSMauro Carvalho Chehab 	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
2817ab5a503cSMauro Carvalho Chehab 	layers[0].size = pvt->csels[0].b_cnt;
2818ab5a503cSMauro Carvalho Chehab 	layers[0].is_virt_csrow = true;
2819ab5a503cSMauro Carvalho Chehab 	layers[1].type = EDAC_MC_LAYER_CHANNEL;
2820f0a56c48SBorislav Petkov 
2821f0a56c48SBorislav Petkov 	/*
2822f0a56c48SBorislav Petkov 	 * Always allocate two channels since we can have setups with DIMMs on
2823f0a56c48SBorislav Petkov 	 * only one channel. Also, this simplifies handling later for the price
2824f0a56c48SBorislav Petkov 	 * of a couple of KBs tops.
2825f0a56c48SBorislav Petkov 	 */
2826f0a56c48SBorislav Petkov 	layers[1].size = 2;
2827ab5a503cSMauro Carvalho Chehab 	layers[1].is_virt_csrow = false;
2828f0a56c48SBorislav Petkov 
2829ca0907b9SMauro Carvalho Chehab 	mci = edac_mc_alloc(nid, ARRAY_SIZE(layers), layers, 0);
28307d6034d3SDoug Thompson 	if (!mci)
2831360b7f3cSBorislav Petkov 		goto err_siblings;
28327d6034d3SDoug Thompson 
28337d6034d3SDoug Thompson 	mci->pvt_info = pvt;
28343f37a36bSBorislav Petkov 	mci->pdev = &pvt->F3->dev;
28357d6034d3SDoug Thompson 
2836df71a053SBorislav Petkov 	setup_mci_misc_attrs(mci, fam_type);
2837360b7f3cSBorislav Petkov 
2838360b7f3cSBorislav Petkov 	if (init_csrows(mci))
28397d6034d3SDoug Thompson 		mci->edac_cap = EDAC_FLAG_NONE;
28407d6034d3SDoug Thompson 
28417d6034d3SDoug Thompson 	ret = -ENODEV;
2842e339f1ecSTakashi Iwai 	if (edac_mc_add_mc_with_groups(mci, amd64_edac_attr_groups)) {
2843956b9ba1SJoe Perches 		edac_dbg(1, "failed edac_mc_add_mc()\n");
28447d6034d3SDoug Thompson 		goto err_add_mc;
28457d6034d3SDoug Thompson 	}
28467d6034d3SDoug Thompson 
2847549d042dSBorislav Petkov 	/* register stuff with EDAC MCE */
2848549d042dSBorislav Petkov 	if (report_gart_errors)
2849549d042dSBorislav Petkov 		amd_report_gart_errors(true);
2850549d042dSBorislav Petkov 
2851df781d03SBorislav Petkov 	amd_register_ecc_decoder(decode_bus_error);
2852549d042dSBorislav Petkov 
28537d6034d3SDoug Thompson 	return 0;
28547d6034d3SDoug Thompson 
28557d6034d3SDoug Thompson err_add_mc:
28567d6034d3SDoug Thompson 	edac_mc_free(mci);
28577d6034d3SDoug Thompson 
2858360b7f3cSBorislav Petkov err_siblings:
2859360b7f3cSBorislav Petkov 	free_mc_sibling_devs(pvt);
28607d6034d3SDoug Thompson 
2861360b7f3cSBorislav Petkov err_free:
2862360b7f3cSBorislav Petkov 	kfree(pvt);
28637d6034d3SDoug Thompson 
2864360b7f3cSBorislav Petkov err_ret:
28657d6034d3SDoug Thompson 	return ret;
28667d6034d3SDoug Thompson }
28677d6034d3SDoug Thompson 
28683f37a36bSBorislav Petkov static int probe_one_instance(unsigned int nid)
28697d6034d3SDoug Thompson {
28702299ef71SBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
2871ae7bb7c6SBorislav Petkov 	struct ecc_settings *s;
28723f37a36bSBorislav Petkov 	int ret;
2873b8cfa02fSBorislav Petkov 
2874ae7bb7c6SBorislav Petkov 	ret = -ENOMEM;
2875ae7bb7c6SBorislav Petkov 	s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL);
2876ae7bb7c6SBorislav Petkov 	if (!s)
28772299ef71SBorislav Petkov 		goto err_out;
2878ae7bb7c6SBorislav Petkov 
2879ae7bb7c6SBorislav Petkov 	ecc_stngs[nid] = s;
2880ae7bb7c6SBorislav Petkov 
28812299ef71SBorislav Petkov 	if (!ecc_enabled(F3, nid)) {
28822299ef71SBorislav Petkov 		ret = -ENODEV;
28832299ef71SBorislav Petkov 
28842299ef71SBorislav Petkov 		if (!ecc_enable_override)
28852299ef71SBorislav Petkov 			goto err_enable;
28862299ef71SBorislav Petkov 
28872299ef71SBorislav Petkov 		amd64_warn("Forcing ECC on!\n");
28882299ef71SBorislav Petkov 
28892299ef71SBorislav Petkov 		if (!enable_ecc_error_reporting(s, nid, F3))
28902299ef71SBorislav Petkov 			goto err_enable;
28912299ef71SBorislav Petkov 	}
28922299ef71SBorislav Petkov 
28933f37a36bSBorislav Petkov 	ret = init_one_instance(nid);
2894360b7f3cSBorislav Petkov 	if (ret < 0) {
2895ae7bb7c6SBorislav Petkov 		amd64_err("Error probing instance: %d\n", nid);
2896360b7f3cSBorislav Petkov 		restore_ecc_error_reporting(s, nid, F3);
2897360b7f3cSBorislav Petkov 	}
28987d6034d3SDoug Thompson 
28997d6034d3SDoug Thompson 	return ret;
29002299ef71SBorislav Petkov 
29012299ef71SBorislav Petkov err_enable:
29022299ef71SBorislav Petkov 	kfree(s);
29032299ef71SBorislav Petkov 	ecc_stngs[nid] = NULL;
29042299ef71SBorislav Petkov 
29052299ef71SBorislav Petkov err_out:
29062299ef71SBorislav Petkov 	return ret;
29077d6034d3SDoug Thompson }
29087d6034d3SDoug Thompson 
29093f37a36bSBorislav Petkov static void remove_one_instance(unsigned int nid)
29107d6034d3SDoug Thompson {
2911360b7f3cSBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
2912360b7f3cSBorislav Petkov 	struct ecc_settings *s = ecc_stngs[nid];
29133f37a36bSBorislav Petkov 	struct mem_ctl_info *mci;
29143f37a36bSBorislav Petkov 	struct amd64_pvt *pvt;
29157d6034d3SDoug Thompson 
29163f37a36bSBorislav Petkov 	mci = find_mci_by_dev(&F3->dev);
2917a4b4bedcSBorislav Petkov 	WARN_ON(!mci);
2918a4b4bedcSBorislav Petkov 
29197d6034d3SDoug Thompson 	/* Remove from EDAC CORE tracking list */
29203f37a36bSBorislav Petkov 	mci = edac_mc_del_mc(&F3->dev);
29217d6034d3SDoug Thompson 	if (!mci)
29227d6034d3SDoug Thompson 		return;
29237d6034d3SDoug Thompson 
29247d6034d3SDoug Thompson 	pvt = mci->pvt_info;
29257d6034d3SDoug Thompson 
2926360b7f3cSBorislav Petkov 	restore_ecc_error_reporting(s, nid, F3);
29277d6034d3SDoug Thompson 
2928360b7f3cSBorislav Petkov 	free_mc_sibling_devs(pvt);
29297d6034d3SDoug Thompson 
2930549d042dSBorislav Petkov 	/* unregister from EDAC MCE */
2931549d042dSBorislav Petkov 	amd_report_gart_errors(false);
2932df781d03SBorislav Petkov 	amd_unregister_ecc_decoder(decode_bus_error);
2933549d042dSBorislav Petkov 
2934360b7f3cSBorislav Petkov 	kfree(ecc_stngs[nid]);
2935360b7f3cSBorislav Petkov 	ecc_stngs[nid] = NULL;
2936ae7bb7c6SBorislav Petkov 
29377d6034d3SDoug Thompson 	/* Free the EDAC CORE resources */
29388f68ed97SBorislav Petkov 	mci->pvt_info = NULL;
29398f68ed97SBorislav Petkov 
29408f68ed97SBorislav Petkov 	kfree(pvt);
29417d6034d3SDoug Thompson 	edac_mc_free(mci);
29427d6034d3SDoug Thompson }
29437d6034d3SDoug Thompson 
2944360b7f3cSBorislav Petkov static void setup_pci_device(void)
29457d6034d3SDoug Thompson {
29467d6034d3SDoug Thompson 	struct mem_ctl_info *mci;
29477d6034d3SDoug Thompson 	struct amd64_pvt *pvt;
29487d6034d3SDoug Thompson 
2949d1ea71cdSBorislav Petkov 	if (pci_ctl)
29507d6034d3SDoug Thompson 		return;
29517d6034d3SDoug Thompson 
29522ec591acSBorislav Petkov 	mci = edac_mc_find(0);
2953d1ea71cdSBorislav Petkov 	if (!mci)
2954d1ea71cdSBorislav Petkov 		return;
29557d6034d3SDoug Thompson 
29567d6034d3SDoug Thompson 	pvt = mci->pvt_info;
2957d1ea71cdSBorislav Petkov 	pci_ctl = edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR);
2958d1ea71cdSBorislav Petkov 	if (!pci_ctl) {
2959d1ea71cdSBorislav Petkov 		pr_warn("%s(): Unable to create PCI control\n", __func__);
2960d1ea71cdSBorislav Petkov 		pr_warn("%s(): PCI error report via EDAC not set\n", __func__);
29617d6034d3SDoug Thompson 	}
29627d6034d3SDoug Thompson }
29637d6034d3SDoug Thompson 
29647d6034d3SDoug Thompson static int __init amd64_edac_init(void)
29657d6034d3SDoug Thompson {
2966360b7f3cSBorislav Petkov 	int err = -ENODEV;
29673f37a36bSBorislav Petkov 	int i;
29687d6034d3SDoug Thompson 
29699653a5c7SHans Rosenfeld 	if (amd_cache_northbridges() < 0)
297056b34b91SBorislav Petkov 		goto err_ret;
29717d6034d3SDoug Thompson 
29726ba92feaSBorislav Petkov 	opstate_init();
29736ba92feaSBorislav Petkov 
2974cc4d8860SBorislav Petkov 	err = -ENOMEM;
2975ae7bb7c6SBorislav Petkov 	ecc_stngs = kzalloc(amd_nb_num() * sizeof(ecc_stngs[0]), GFP_KERNEL);
29762ec591acSBorislav Petkov 	if (!ecc_stngs)
2977a9f0fbe2SBorislav Petkov 		goto err_free;
2978cc4d8860SBorislav Petkov 
297950542251SBorislav Petkov 	msrs = msrs_alloc();
298056b34b91SBorislav Petkov 	if (!msrs)
2981360b7f3cSBorislav Petkov 		goto err_free;
298250542251SBorislav Petkov 
29833f37a36bSBorislav Petkov 	for (i = 0; i < amd_nb_num(); i++)
29843f37a36bSBorislav Petkov 		if (probe_one_instance(i)) {
29853f37a36bSBorislav Petkov 			/* unwind properly */
29863f37a36bSBorislav Petkov 			while (--i >= 0)
29873f37a36bSBorislav Petkov 				remove_one_instance(i);
29887d6034d3SDoug Thompson 
29893f37a36bSBorislav Petkov 			goto err_pci;
29903f37a36bSBorislav Petkov 		}
29917d6034d3SDoug Thompson 
2992360b7f3cSBorislav Petkov 	setup_pci_device();
2993f5b10c45STomasz Pala 
2994f5b10c45STomasz Pala #ifdef CONFIG_X86_32
2995f5b10c45STomasz Pala 	amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR);
2996f5b10c45STomasz Pala #endif
2997f5b10c45STomasz Pala 
2998de0336b3SBorislav Petkov 	printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION);
2999de0336b3SBorislav Petkov 
30007d6034d3SDoug Thompson 	return 0;
30017d6034d3SDoug Thompson 
300256b34b91SBorislav Petkov err_pci:
300356b34b91SBorislav Petkov 	msrs_free(msrs);
300456b34b91SBorislav Petkov 	msrs = NULL;
3005cc4d8860SBorislav Petkov 
3006360b7f3cSBorislav Petkov err_free:
3007360b7f3cSBorislav Petkov 	kfree(ecc_stngs);
3008360b7f3cSBorislav Petkov 	ecc_stngs = NULL;
3009360b7f3cSBorislav Petkov 
301056b34b91SBorislav Petkov err_ret:
30117d6034d3SDoug Thompson 	return err;
30127d6034d3SDoug Thompson }
30137d6034d3SDoug Thompson 
30147d6034d3SDoug Thompson static void __exit amd64_edac_exit(void)
30157d6034d3SDoug Thompson {
30163f37a36bSBorislav Petkov 	int i;
30173f37a36bSBorislav Petkov 
3018d1ea71cdSBorislav Petkov 	if (pci_ctl)
3019d1ea71cdSBorislav Petkov 		edac_pci_release_generic_ctl(pci_ctl);
30207d6034d3SDoug Thompson 
30213f37a36bSBorislav Petkov 	for (i = 0; i < amd_nb_num(); i++)
30223f37a36bSBorislav Petkov 		remove_one_instance(i);
302350542251SBorislav Petkov 
3024ae7bb7c6SBorislav Petkov 	kfree(ecc_stngs);
3025ae7bb7c6SBorislav Petkov 	ecc_stngs = NULL;
3026ae7bb7c6SBorislav Petkov 
302750542251SBorislav Petkov 	msrs_free(msrs);
302850542251SBorislav Petkov 	msrs = NULL;
30297d6034d3SDoug Thompson }
30307d6034d3SDoug Thompson 
30317d6034d3SDoug Thompson module_init(amd64_edac_init);
30327d6034d3SDoug Thompson module_exit(amd64_edac_exit);
30337d6034d3SDoug Thompson 
30347d6034d3SDoug Thompson MODULE_LICENSE("GPL");
30357d6034d3SDoug Thompson MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, "
30367d6034d3SDoug Thompson 		"Dave Peterson, Thayne Harbaugh");
30377d6034d3SDoug Thompson MODULE_DESCRIPTION("MC support for AMD64 memory controllers - "
30387d6034d3SDoug Thompson 		EDAC_AMD64_VERSION);
30397d6034d3SDoug Thompson 
30407d6034d3SDoug Thompson module_param(edac_op_state, int, 0444);
30417d6034d3SDoug Thompson MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
3042