xref: /openbmc/linux/drivers/edac/amd64_edac.c (revision f1cbbec9)
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 
1213f1cbbec9SYazen Ghannam static int f17_early_channel_count(struct amd64_pvt *pvt)
1214f1cbbec9SYazen Ghannam {
1215f1cbbec9SYazen Ghannam 	int i, channels = 0;
1216f1cbbec9SYazen Ghannam 
1217f1cbbec9SYazen Ghannam 	/* SDP Control bit 31 (SdpInit) is clear for unused UMC channels */
1218f1cbbec9SYazen Ghannam 	for (i = 0; i < NUM_UMCS; i++)
1219f1cbbec9SYazen Ghannam 		channels += !!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT);
1220f1cbbec9SYazen Ghannam 
1221f1cbbec9SYazen Ghannam 	amd64_info("MCT channel count: %d\n", channels);
1222f1cbbec9SYazen Ghannam 
1223f1cbbec9SYazen Ghannam 	return channels;
1224f1cbbec9SYazen Ghannam }
1225f1cbbec9SYazen Ghannam 
122641d8bfabSBorislav Petkov static int ddr3_cs_size(unsigned i, bool dct_width)
12271afd3c98SDoug Thompson {
122841d8bfabSBorislav Petkov 	unsigned shift = 0;
122941d8bfabSBorislav Petkov 	int cs_size = 0;
123041d8bfabSBorislav Petkov 
123141d8bfabSBorislav Petkov 	if (i == 0 || i == 3 || i == 4)
123241d8bfabSBorislav Petkov 		cs_size = -1;
123341d8bfabSBorislav Petkov 	else if (i <= 2)
123441d8bfabSBorislav Petkov 		shift = i;
123541d8bfabSBorislav Petkov 	else if (i == 12)
123641d8bfabSBorislav Petkov 		shift = 7;
123741d8bfabSBorislav Petkov 	else if (!(i & 0x1))
123841d8bfabSBorislav Petkov 		shift = i >> 1;
123941d8bfabSBorislav Petkov 	else
124041d8bfabSBorislav Petkov 		shift = (i + 1) >> 1;
124141d8bfabSBorislav Petkov 
124241d8bfabSBorislav Petkov 	if (cs_size != -1)
124341d8bfabSBorislav Petkov 		cs_size = (128 * (1 << !!dct_width)) << shift;
124441d8bfabSBorislav Petkov 
124541d8bfabSBorislav Petkov 	return cs_size;
124641d8bfabSBorislav Petkov }
124741d8bfabSBorislav Petkov 
1248a597d2a5SAravind Gopalakrishnan static int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply)
1249a597d2a5SAravind Gopalakrishnan {
1250a597d2a5SAravind Gopalakrishnan 	unsigned shift = 0;
1251a597d2a5SAravind Gopalakrishnan 	int cs_size = 0;
1252a597d2a5SAravind Gopalakrishnan 
1253a597d2a5SAravind Gopalakrishnan 	if (i < 4 || i == 6)
1254a597d2a5SAravind Gopalakrishnan 		cs_size = -1;
1255a597d2a5SAravind Gopalakrishnan 	else if (i == 12)
1256a597d2a5SAravind Gopalakrishnan 		shift = 7;
1257a597d2a5SAravind Gopalakrishnan 	else if (!(i & 0x1))
1258a597d2a5SAravind Gopalakrishnan 		shift = i >> 1;
1259a597d2a5SAravind Gopalakrishnan 	else
1260a597d2a5SAravind Gopalakrishnan 		shift = (i + 1) >> 1;
1261a597d2a5SAravind Gopalakrishnan 
1262a597d2a5SAravind Gopalakrishnan 	if (cs_size != -1)
1263a597d2a5SAravind Gopalakrishnan 		cs_size = rank_multiply * (128 << shift);
1264a597d2a5SAravind Gopalakrishnan 
1265a597d2a5SAravind Gopalakrishnan 	return cs_size;
1266a597d2a5SAravind Gopalakrishnan }
1267a597d2a5SAravind Gopalakrishnan 
1268a597d2a5SAravind Gopalakrishnan static int ddr4_cs_size(unsigned i)
1269a597d2a5SAravind Gopalakrishnan {
1270a597d2a5SAravind Gopalakrishnan 	int cs_size = 0;
1271a597d2a5SAravind Gopalakrishnan 
1272a597d2a5SAravind Gopalakrishnan 	if (i == 0)
1273a597d2a5SAravind Gopalakrishnan 		cs_size = -1;
1274a597d2a5SAravind Gopalakrishnan 	else if (i == 1)
1275a597d2a5SAravind Gopalakrishnan 		cs_size = 1024;
1276a597d2a5SAravind Gopalakrishnan 	else
1277a597d2a5SAravind Gopalakrishnan 		/* Min cs_size = 1G */
1278a597d2a5SAravind Gopalakrishnan 		cs_size = 1024 * (1 << (i >> 1));
1279a597d2a5SAravind Gopalakrishnan 
1280a597d2a5SAravind Gopalakrishnan 	return cs_size;
1281a597d2a5SAravind Gopalakrishnan }
1282a597d2a5SAravind Gopalakrishnan 
128341d8bfabSBorislav Petkov static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1284a597d2a5SAravind Gopalakrishnan 				   unsigned cs_mode, int cs_mask_nr)
128541d8bfabSBorislav Petkov {
128641d8bfabSBorislav Petkov 	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
128741d8bfabSBorislav Petkov 
128841d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 11);
12891433eb99SBorislav Petkov 
12901433eb99SBorislav Petkov 	if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
129141d8bfabSBorislav Petkov 		return ddr3_cs_size(cs_mode, dclr & WIDTH_128);
12921433eb99SBorislav Petkov 	else
129341d8bfabSBorislav Petkov 		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
129441d8bfabSBorislav Petkov }
12951433eb99SBorislav Petkov 
129641d8bfabSBorislav Petkov /*
129741d8bfabSBorislav Petkov  * F15h supports only 64bit DCT interfaces
129841d8bfabSBorislav Petkov  */
129941d8bfabSBorislav Petkov static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1300a597d2a5SAravind Gopalakrishnan 				   unsigned cs_mode, int cs_mask_nr)
130141d8bfabSBorislav Petkov {
130241d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 12);
130341d8bfabSBorislav Petkov 
130441d8bfabSBorislav Petkov 	return ddr3_cs_size(cs_mode, false);
13051afd3c98SDoug Thompson }
13061afd3c98SDoug Thompson 
1307a597d2a5SAravind Gopalakrishnan /* F15h M60h supports DDR4 mapping as well.. */
1308a597d2a5SAravind Gopalakrishnan static int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1309a597d2a5SAravind Gopalakrishnan 					unsigned cs_mode, int cs_mask_nr)
1310a597d2a5SAravind Gopalakrishnan {
1311a597d2a5SAravind Gopalakrishnan 	int cs_size;
1312a597d2a5SAravind Gopalakrishnan 	u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr];
1313a597d2a5SAravind Gopalakrishnan 
1314a597d2a5SAravind Gopalakrishnan 	WARN_ON(cs_mode > 12);
1315a597d2a5SAravind Gopalakrishnan 
1316a597d2a5SAravind Gopalakrishnan 	if (pvt->dram_type == MEM_DDR4) {
1317a597d2a5SAravind Gopalakrishnan 		if (cs_mode > 9)
1318a597d2a5SAravind Gopalakrishnan 			return -1;
1319a597d2a5SAravind Gopalakrishnan 
1320a597d2a5SAravind Gopalakrishnan 		cs_size = ddr4_cs_size(cs_mode);
1321a597d2a5SAravind Gopalakrishnan 	} else if (pvt->dram_type == MEM_LRDDR3) {
1322a597d2a5SAravind Gopalakrishnan 		unsigned rank_multiply = dcsm & 0xf;
1323a597d2a5SAravind Gopalakrishnan 
1324a597d2a5SAravind Gopalakrishnan 		if (rank_multiply == 3)
1325a597d2a5SAravind Gopalakrishnan 			rank_multiply = 4;
1326a597d2a5SAravind Gopalakrishnan 		cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply);
1327a597d2a5SAravind Gopalakrishnan 	} else {
1328a597d2a5SAravind Gopalakrishnan 		/* Minimum cs size is 512mb for F15hM60h*/
1329a597d2a5SAravind Gopalakrishnan 		if (cs_mode == 0x1)
1330a597d2a5SAravind Gopalakrishnan 			return -1;
1331a597d2a5SAravind Gopalakrishnan 
1332a597d2a5SAravind Gopalakrishnan 		cs_size = ddr3_cs_size(cs_mode, false);
1333a597d2a5SAravind Gopalakrishnan 	}
1334a597d2a5SAravind Gopalakrishnan 
1335a597d2a5SAravind Gopalakrishnan 	return cs_size;
1336a597d2a5SAravind Gopalakrishnan }
1337a597d2a5SAravind Gopalakrishnan 
133894c1acf2SAravind Gopalakrishnan /*
133918b94f66SAravind Gopalakrishnan  * F16h and F15h model 30h have only limited cs_modes.
134094c1acf2SAravind Gopalakrishnan  */
134194c1acf2SAravind Gopalakrishnan static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
1342a597d2a5SAravind Gopalakrishnan 				unsigned cs_mode, int cs_mask_nr)
134394c1acf2SAravind Gopalakrishnan {
134494c1acf2SAravind Gopalakrishnan 	WARN_ON(cs_mode > 12);
134594c1acf2SAravind Gopalakrishnan 
134694c1acf2SAravind Gopalakrishnan 	if (cs_mode == 6 || cs_mode == 8 ||
134794c1acf2SAravind Gopalakrishnan 	    cs_mode == 9 || cs_mode == 12)
134894c1acf2SAravind Gopalakrishnan 		return -1;
134994c1acf2SAravind Gopalakrishnan 	else
135094c1acf2SAravind Gopalakrishnan 		return ddr3_cs_size(cs_mode, false);
135194c1acf2SAravind Gopalakrishnan }
135294c1acf2SAravind Gopalakrishnan 
1353f1cbbec9SYazen Ghannam static int f17_base_addr_to_cs_size(struct amd64_pvt *pvt, u8 umc,
1354f1cbbec9SYazen Ghannam 				    unsigned int cs_mode, int csrow_nr)
1355f1cbbec9SYazen Ghannam {
1356f1cbbec9SYazen Ghannam 	u32 base_addr = pvt->csels[umc].csbases[csrow_nr];
1357f1cbbec9SYazen Ghannam 
1358f1cbbec9SYazen Ghannam 	/*  Each mask is used for every two base addresses. */
1359f1cbbec9SYazen Ghannam 	u32 addr_mask = pvt->csels[umc].csmasks[csrow_nr >> 1];
1360f1cbbec9SYazen Ghannam 
1361f1cbbec9SYazen Ghannam 	/*  Register [31:1] = Address [39:9]. Size is in kBs here. */
1362f1cbbec9SYazen Ghannam 	u32 size = ((addr_mask >> 1) - (base_addr >> 1) + 1) >> 1;
1363f1cbbec9SYazen Ghannam 
1364f1cbbec9SYazen Ghannam 	edac_dbg(1, "BaseAddr: 0x%x, AddrMask: 0x%x\n", base_addr, addr_mask);
1365f1cbbec9SYazen Ghannam 
1366f1cbbec9SYazen Ghannam 	/* Return size in MBs. */
1367f1cbbec9SYazen Ghannam 	return size >> 10;
1368f1cbbec9SYazen Ghannam }
1369f1cbbec9SYazen Ghannam 
13705a5d2371SBorislav Petkov static void read_dram_ctl_register(struct amd64_pvt *pvt)
13716163b5d4SDoug Thompson {
13726163b5d4SDoug Thompson 
1373a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
13745a5d2371SBorislav Petkov 		return;
13755a5d2371SBorislav Petkov 
13767981a28fSAravind Gopalakrishnan 	if (!amd64_read_pci_cfg(pvt->F2, DCT_SEL_LO, &pvt->dct_sel_lo)) {
1377956b9ba1SJoe Perches 		edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
137878da121eSBorislav Petkov 			 pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
13796163b5d4SDoug Thompson 
1380956b9ba1SJoe Perches 		edac_dbg(0, "  DCTs operate in %s mode\n",
13815a5d2371SBorislav Petkov 			 (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
13826163b5d4SDoug Thompson 
138372381bd5SBorislav Petkov 		if (!dct_ganging_enabled(pvt))
1384956b9ba1SJoe Perches 			edac_dbg(0, "  Address range split per DCT: %s\n",
138572381bd5SBorislav Petkov 				 (dct_high_range_enabled(pvt) ? "yes" : "no"));
138672381bd5SBorislav Petkov 
1387956b9ba1SJoe Perches 		edac_dbg(0, "  data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n",
138872381bd5SBorislav Petkov 			 (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
138972381bd5SBorislav Petkov 			 (dct_memory_cleared(pvt) ? "yes" : "no"));
139072381bd5SBorislav Petkov 
1391956b9ba1SJoe Perches 		edac_dbg(0, "  channel interleave: %s, "
139278da121eSBorislav Petkov 			 "interleave bits selector: 0x%x\n",
139372381bd5SBorislav Petkov 			 (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
13946163b5d4SDoug Thompson 			 dct_sel_interleave_addr(pvt));
13956163b5d4SDoug Thompson 	}
13966163b5d4SDoug Thompson 
13977981a28fSAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F2, DCT_SEL_HI, &pvt->dct_sel_hi);
13986163b5d4SDoug Thompson }
13996163b5d4SDoug Thompson 
1400f71d0a05SDoug Thompson /*
140118b94f66SAravind Gopalakrishnan  * Determine channel (DCT) based on the interleaving mode (see F15h M30h BKDG,
140218b94f66SAravind Gopalakrishnan  * 2.10.12 Memory Interleaving Modes).
140318b94f66SAravind Gopalakrishnan  */
140418b94f66SAravind Gopalakrishnan static u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
140518b94f66SAravind Gopalakrishnan 				     u8 intlv_en, int num_dcts_intlv,
140618b94f66SAravind Gopalakrishnan 				     u32 dct_sel)
140718b94f66SAravind Gopalakrishnan {
140818b94f66SAravind Gopalakrishnan 	u8 channel = 0;
140918b94f66SAravind Gopalakrishnan 	u8 select;
141018b94f66SAravind Gopalakrishnan 
141118b94f66SAravind Gopalakrishnan 	if (!(intlv_en))
141218b94f66SAravind Gopalakrishnan 		return (u8)(dct_sel);
141318b94f66SAravind Gopalakrishnan 
141418b94f66SAravind Gopalakrishnan 	if (num_dcts_intlv == 2) {
141518b94f66SAravind Gopalakrishnan 		select = (sys_addr >> 8) & 0x3;
141618b94f66SAravind Gopalakrishnan 		channel = select ? 0x3 : 0;
14179d0e8d83SAravind Gopalakrishnan 	} else if (num_dcts_intlv == 4) {
14189d0e8d83SAravind Gopalakrishnan 		u8 intlv_addr = dct_sel_interleave_addr(pvt);
14199d0e8d83SAravind Gopalakrishnan 		switch (intlv_addr) {
14209d0e8d83SAravind Gopalakrishnan 		case 0x4:
14219d0e8d83SAravind Gopalakrishnan 			channel = (sys_addr >> 8) & 0x3;
14229d0e8d83SAravind Gopalakrishnan 			break;
14239d0e8d83SAravind Gopalakrishnan 		case 0x5:
14249d0e8d83SAravind Gopalakrishnan 			channel = (sys_addr >> 9) & 0x3;
14259d0e8d83SAravind Gopalakrishnan 			break;
14269d0e8d83SAravind Gopalakrishnan 		}
14279d0e8d83SAravind Gopalakrishnan 	}
142818b94f66SAravind Gopalakrishnan 	return channel;
142918b94f66SAravind Gopalakrishnan }
143018b94f66SAravind Gopalakrishnan 
143118b94f66SAravind Gopalakrishnan /*
1432229a7a11SBorislav Petkov  * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory
1433f71d0a05SDoug Thompson  * Interleaving Modes.
1434f71d0a05SDoug Thompson  */
1435b15f0fcaSBorislav Petkov static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
1436229a7a11SBorislav Petkov 				bool hi_range_sel, u8 intlv_en)
14376163b5d4SDoug Thompson {
1438151fa71cSBorislav Petkov 	u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1;
14396163b5d4SDoug Thompson 
14406163b5d4SDoug Thompson 	if (dct_ganging_enabled(pvt))
1441229a7a11SBorislav Petkov 		return 0;
1442229a7a11SBorislav Petkov 
1443229a7a11SBorislav Petkov 	if (hi_range_sel)
1444229a7a11SBorislav Petkov 		return dct_sel_high;
1445229a7a11SBorislav Petkov 
1446f71d0a05SDoug Thompson 	/*
1447f71d0a05SDoug Thompson 	 * see F2x110[DctSelIntLvAddr] - channel interleave mode
1448f71d0a05SDoug Thompson 	 */
1449229a7a11SBorislav Petkov 	if (dct_interleave_enabled(pvt)) {
1450229a7a11SBorislav Petkov 		u8 intlv_addr = dct_sel_interleave_addr(pvt);
14516163b5d4SDoug Thompson 
1452229a7a11SBorislav Petkov 		/* return DCT select function: 0=DCT0, 1=DCT1 */
1453229a7a11SBorislav Petkov 		if (!intlv_addr)
1454229a7a11SBorislav Petkov 			return sys_addr >> 6 & 1;
14556163b5d4SDoug Thompson 
1456229a7a11SBorislav Petkov 		if (intlv_addr & 0x2) {
1457229a7a11SBorislav Petkov 			u8 shift = intlv_addr & 0x1 ? 9 : 6;
1458dc0a50a8SYazen Ghannam 			u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) & 1;
1459229a7a11SBorislav Petkov 
1460229a7a11SBorislav Petkov 			return ((sys_addr >> shift) & 1) ^ temp;
14616163b5d4SDoug Thompson 		}
14626163b5d4SDoug Thompson 
1463dc0a50a8SYazen Ghannam 		if (intlv_addr & 0x4) {
1464dc0a50a8SYazen Ghannam 			u8 shift = intlv_addr & 0x1 ? 9 : 8;
1465dc0a50a8SYazen Ghannam 
1466dc0a50a8SYazen Ghannam 			return (sys_addr >> shift) & 1;
1467dc0a50a8SYazen Ghannam 		}
1468dc0a50a8SYazen Ghannam 
1469229a7a11SBorislav Petkov 		return (sys_addr >> (12 + hweight8(intlv_en))) & 1;
1470229a7a11SBorislav Petkov 	}
1471229a7a11SBorislav Petkov 
1472229a7a11SBorislav Petkov 	if (dct_high_range_enabled(pvt))
1473229a7a11SBorislav Petkov 		return ~dct_sel_high & 1;
14746163b5d4SDoug Thompson 
14756163b5d4SDoug Thompson 	return 0;
14766163b5d4SDoug Thompson }
14776163b5d4SDoug Thompson 
1478c8e518d5SBorislav Petkov /* Convert the sys_addr to the normalized DCT address */
1479c7e5301aSDaniel J Blueman static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range,
1480c8e518d5SBorislav Petkov 				 u64 sys_addr, bool hi_rng,
1481c8e518d5SBorislav Petkov 				 u32 dct_sel_base_addr)
14826163b5d4SDoug Thompson {
14836163b5d4SDoug Thompson 	u64 chan_off;
1484c8e518d5SBorislav Petkov 	u64 dram_base		= get_dram_base(pvt, range);
1485c8e518d5SBorislav Petkov 	u64 hole_off		= f10_dhar_offset(pvt);
14866f3508f6SDan Carpenter 	u64 dct_sel_base_off	= (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16;
14876163b5d4SDoug Thompson 
1488c8e518d5SBorislav Petkov 	if (hi_rng) {
1489c8e518d5SBorislav Petkov 		/*
1490c8e518d5SBorislav Petkov 		 * if
1491c8e518d5SBorislav Petkov 		 * base address of high range is below 4Gb
1492c8e518d5SBorislav Petkov 		 * (bits [47:27] at [31:11])
1493c8e518d5SBorislav Petkov 		 * DRAM address space on this DCT is hoisted above 4Gb	&&
1494c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1495c8e518d5SBorislav Petkov 		 *
1496c8e518d5SBorislav Petkov 		 *	remove hole offset from sys_addr
1497c8e518d5SBorislav Petkov 		 * else
1498c8e518d5SBorislav Petkov 		 *	remove high range offset from sys_addr
1499c8e518d5SBorislav Petkov 		 */
1500c8e518d5SBorislav Petkov 		if ((!(dct_sel_base_addr >> 16) ||
1501c8e518d5SBorislav Petkov 		     dct_sel_base_addr < dhar_base(pvt)) &&
1502972ea17aSBorislav Petkov 		    dhar_valid(pvt) &&
1503c8e518d5SBorislav Petkov 		    (sys_addr >= BIT_64(32)))
1504bc21fa57SBorislav Petkov 			chan_off = hole_off;
15056163b5d4SDoug Thompson 		else
15066163b5d4SDoug Thompson 			chan_off = dct_sel_base_off;
15076163b5d4SDoug Thompson 	} else {
1508c8e518d5SBorislav Petkov 		/*
1509c8e518d5SBorislav Petkov 		 * if
1510c8e518d5SBorislav Petkov 		 * we have a valid hole		&&
1511c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1512c8e518d5SBorislav Petkov 		 *
1513c8e518d5SBorislav Petkov 		 *	remove hole
1514c8e518d5SBorislav Petkov 		 * else
1515c8e518d5SBorislav Petkov 		 *	remove dram base to normalize to DCT address
1516c8e518d5SBorislav Petkov 		 */
1517972ea17aSBorislav Petkov 		if (dhar_valid(pvt) && (sys_addr >= BIT_64(32)))
1518bc21fa57SBorislav Petkov 			chan_off = hole_off;
15196163b5d4SDoug Thompson 		else
1520c8e518d5SBorislav Petkov 			chan_off = dram_base;
15216163b5d4SDoug Thompson 	}
15226163b5d4SDoug Thompson 
152310ef6b0dSChen, Gong 	return (sys_addr & GENMASK_ULL(47,6)) - (chan_off & GENMASK_ULL(47,23));
15246163b5d4SDoug Thompson }
15256163b5d4SDoug Thompson 
15266163b5d4SDoug Thompson /*
15276163b5d4SDoug Thompson  * checks if the csrow passed in is marked as SPARED, if so returns the new
15286163b5d4SDoug Thompson  * spare row
15296163b5d4SDoug Thompson  */
153011c75eadSBorislav Petkov static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow)
15316163b5d4SDoug Thompson {
1532614ec9d8SBorislav Petkov 	int tmp_cs;
15336163b5d4SDoug Thompson 
1534614ec9d8SBorislav Petkov 	if (online_spare_swap_done(pvt, dct) &&
1535614ec9d8SBorislav Petkov 	    csrow == online_spare_bad_dramcs(pvt, dct)) {
1536614ec9d8SBorislav Petkov 
1537614ec9d8SBorislav Petkov 		for_each_chip_select(tmp_cs, dct, pvt) {
1538614ec9d8SBorislav Petkov 			if (chip_select_base(tmp_cs, dct, pvt) & 0x2) {
1539614ec9d8SBorislav Petkov 				csrow = tmp_cs;
1540614ec9d8SBorislav Petkov 				break;
1541614ec9d8SBorislav Petkov 			}
1542614ec9d8SBorislav Petkov 		}
15436163b5d4SDoug Thompson 	}
15446163b5d4SDoug Thompson 	return csrow;
15456163b5d4SDoug Thompson }
15466163b5d4SDoug Thompson 
15476163b5d4SDoug Thompson /*
15486163b5d4SDoug Thompson  * Iterate over the DRAM DCT "base" and "mask" registers looking for a
15496163b5d4SDoug Thompson  * SystemAddr match on the specified 'ChannelSelect' and 'NodeID'
15506163b5d4SDoug Thompson  *
15516163b5d4SDoug Thompson  * Return:
15526163b5d4SDoug Thompson  *	-EINVAL:  NOT FOUND
15536163b5d4SDoug Thompson  *	0..csrow = Chip-Select Row
15546163b5d4SDoug Thompson  */
1555c7e5301aSDaniel J Blueman static int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct)
15566163b5d4SDoug Thompson {
15576163b5d4SDoug Thompson 	struct mem_ctl_info *mci;
15586163b5d4SDoug Thompson 	struct amd64_pvt *pvt;
155911c75eadSBorislav Petkov 	u64 cs_base, cs_mask;
15606163b5d4SDoug Thompson 	int cs_found = -EINVAL;
15616163b5d4SDoug Thompson 	int csrow;
15626163b5d4SDoug Thompson 
15632ec591acSBorislav Petkov 	mci = edac_mc_find(nid);
15646163b5d4SDoug Thompson 	if (!mci)
15656163b5d4SDoug Thompson 		return cs_found;
15666163b5d4SDoug Thompson 
15676163b5d4SDoug Thompson 	pvt = mci->pvt_info;
15686163b5d4SDoug Thompson 
1569956b9ba1SJoe Perches 	edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct);
15706163b5d4SDoug Thompson 
157111c75eadSBorislav Petkov 	for_each_chip_select(csrow, dct, pvt) {
157211c75eadSBorislav Petkov 		if (!csrow_enabled(csrow, dct, pvt))
15736163b5d4SDoug Thompson 			continue;
15746163b5d4SDoug Thompson 
157511c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask);
15766163b5d4SDoug Thompson 
1577956b9ba1SJoe Perches 		edac_dbg(1, "    CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
15786163b5d4SDoug Thompson 			 csrow, cs_base, cs_mask);
15796163b5d4SDoug Thompson 
158011c75eadSBorislav Petkov 		cs_mask = ~cs_mask;
15816163b5d4SDoug Thompson 
1582956b9ba1SJoe Perches 		edac_dbg(1, "    (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n",
158311c75eadSBorislav Petkov 			 (in_addr & cs_mask), (cs_base & cs_mask));
15846163b5d4SDoug Thompson 
158511c75eadSBorislav Petkov 		if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
158618b94f66SAravind Gopalakrishnan 			if (pvt->fam == 0x15 && pvt->model >= 0x30) {
158718b94f66SAravind Gopalakrishnan 				cs_found =  csrow;
158818b94f66SAravind Gopalakrishnan 				break;
158918b94f66SAravind Gopalakrishnan 			}
159011c75eadSBorislav Petkov 			cs_found = f10_process_possible_spare(pvt, dct, csrow);
15916163b5d4SDoug Thompson 
1592956b9ba1SJoe Perches 			edac_dbg(1, " MATCH csrow=%d\n", cs_found);
15936163b5d4SDoug Thompson 			break;
15946163b5d4SDoug Thompson 		}
15956163b5d4SDoug Thompson 	}
15966163b5d4SDoug Thompson 	return cs_found;
15976163b5d4SDoug Thompson }
15986163b5d4SDoug Thompson 
159995b0ef55SBorislav Petkov /*
160095b0ef55SBorislav Petkov  * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is
160195b0ef55SBorislav Petkov  * swapped with a region located at the bottom of memory so that the GPU can use
160295b0ef55SBorislav Petkov  * the interleaved region and thus two channels.
160395b0ef55SBorislav Petkov  */
1604b15f0fcaSBorislav Petkov static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr)
160595b0ef55SBorislav Petkov {
160695b0ef55SBorislav Petkov 	u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr;
160795b0ef55SBorislav Petkov 
1608a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10) {
160995b0ef55SBorislav Petkov 		/* only revC3 and revE have that feature */
1610a4b4bedcSBorislav Petkov 		if (pvt->model < 4 || (pvt->model < 0xa && pvt->stepping < 3))
161195b0ef55SBorislav Petkov 			return sys_addr;
161295b0ef55SBorislav Petkov 	}
161395b0ef55SBorislav Petkov 
16147981a28fSAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F2, SWAP_INTLV_REG, &swap_reg);
161595b0ef55SBorislav Petkov 
161695b0ef55SBorislav Petkov 	if (!(swap_reg & 0x1))
161795b0ef55SBorislav Petkov 		return sys_addr;
161895b0ef55SBorislav Petkov 
161995b0ef55SBorislav Petkov 	swap_base	= (swap_reg >> 3) & 0x7f;
162095b0ef55SBorislav Petkov 	swap_limit	= (swap_reg >> 11) & 0x7f;
162195b0ef55SBorislav Petkov 	rgn_size	= (swap_reg >> 20) & 0x7f;
162295b0ef55SBorislav Petkov 	tmp_addr	= sys_addr >> 27;
162395b0ef55SBorislav Petkov 
162495b0ef55SBorislav Petkov 	if (!(sys_addr >> 34) &&
162595b0ef55SBorislav Petkov 	    (((tmp_addr >= swap_base) &&
162695b0ef55SBorislav Petkov 	     (tmp_addr <= swap_limit)) ||
162795b0ef55SBorislav Petkov 	     (tmp_addr < rgn_size)))
162895b0ef55SBorislav Petkov 		return sys_addr ^ (u64)swap_base << 27;
162995b0ef55SBorislav Petkov 
163095b0ef55SBorislav Petkov 	return sys_addr;
163195b0ef55SBorislav Petkov }
163295b0ef55SBorislav Petkov 
1633f71d0a05SDoug Thompson /* For a given @dram_range, check if @sys_addr falls within it. */
1634e761359aSBorislav Petkov static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
163533ca0643SBorislav Petkov 				  u64 sys_addr, int *chan_sel)
1636f71d0a05SDoug Thompson {
1637229a7a11SBorislav Petkov 	int cs_found = -EINVAL;
1638c8e518d5SBorislav Petkov 	u64 chan_addr;
16395d4b58e8SBorislav Petkov 	u32 dct_sel_base;
164011c75eadSBorislav Petkov 	u8 channel;
1641229a7a11SBorislav Petkov 	bool high_range = false;
1642f71d0a05SDoug Thompson 
16437f19bf75SBorislav Petkov 	u8 node_id    = dram_dst_node(pvt, range);
1644229a7a11SBorislav Petkov 	u8 intlv_en   = dram_intlv_en(pvt, range);
16457f19bf75SBorislav Petkov 	u32 intlv_sel = dram_intlv_sel(pvt, range);
1646f71d0a05SDoug Thompson 
1647956b9ba1SJoe Perches 	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
1648c8e518d5SBorislav Petkov 		 range, sys_addr, get_dram_limit(pvt, range));
1649f71d0a05SDoug Thompson 
1650355fba60SBorislav Petkov 	if (dhar_valid(pvt) &&
1651355fba60SBorislav Petkov 	    dhar_base(pvt) <= sys_addr &&
1652355fba60SBorislav Petkov 	    sys_addr < BIT_64(32)) {
1653355fba60SBorislav Petkov 		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
1654355fba60SBorislav Petkov 			    sys_addr);
1655f71d0a05SDoug Thompson 		return -EINVAL;
1656355fba60SBorislav Petkov 	}
1657355fba60SBorislav Petkov 
1658f030ddfbSBorislav Petkov 	if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en)))
1659355fba60SBorislav Petkov 		return -EINVAL;
1660f71d0a05SDoug Thompson 
1661b15f0fcaSBorislav Petkov 	sys_addr = f1x_swap_interleaved_region(pvt, sys_addr);
166295b0ef55SBorislav Petkov 
1663f71d0a05SDoug Thompson 	dct_sel_base = dct_sel_baseaddr(pvt);
1664f71d0a05SDoug Thompson 
1665f71d0a05SDoug Thompson 	/*
1666f71d0a05SDoug Thompson 	 * check whether addresses >= DctSelBaseAddr[47:27] are to be used to
1667f71d0a05SDoug Thompson 	 * select between DCT0 and DCT1.
1668f71d0a05SDoug Thompson 	 */
1669f71d0a05SDoug Thompson 	if (dct_high_range_enabled(pvt) &&
1670f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt) &&
1671f71d0a05SDoug Thompson 	   ((sys_addr >> 27) >= (dct_sel_base >> 11)))
1672229a7a11SBorislav Petkov 		high_range = true;
1673f71d0a05SDoug Thompson 
1674b15f0fcaSBorislav Petkov 	channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en);
1675f71d0a05SDoug Thompson 
1676b15f0fcaSBorislav Petkov 	chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr,
1677c8e518d5SBorislav Petkov 					  high_range, dct_sel_base);
1678f71d0a05SDoug Thompson 
1679e2f79dbdSBorislav Petkov 	/* Remove node interleaving, see F1x120 */
1680e2f79dbdSBorislav Petkov 	if (intlv_en)
1681e2f79dbdSBorislav Petkov 		chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) |
1682e2f79dbdSBorislav Petkov 			    (chan_addr & 0xfff);
1683f71d0a05SDoug Thompson 
16845d4b58e8SBorislav Petkov 	/* remove channel interleave */
1685f71d0a05SDoug Thompson 	if (dct_interleave_enabled(pvt) &&
1686f71d0a05SDoug Thompson 	   !dct_high_range_enabled(pvt) &&
1687f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt)) {
16885d4b58e8SBorislav Petkov 
16895d4b58e8SBorislav Petkov 		if (dct_sel_interleave_addr(pvt) != 1) {
16905d4b58e8SBorislav Petkov 			if (dct_sel_interleave_addr(pvt) == 0x3)
16915d4b58e8SBorislav Petkov 				/* hash 9 */
16925d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 10) << 9) |
16935d4b58e8SBorislav Petkov 					     (chan_addr & 0x1ff);
16945d4b58e8SBorislav Petkov 			else
16955d4b58e8SBorislav Petkov 				/* A[6] or hash 6 */
16965d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 7) << 6) |
16975d4b58e8SBorislav Petkov 					     (chan_addr & 0x3f);
16985d4b58e8SBorislav Petkov 		} else
16995d4b58e8SBorislav Petkov 			/* A[12] */
17005d4b58e8SBorislav Petkov 			chan_addr = ((chan_addr >> 13) << 12) |
17015d4b58e8SBorislav Petkov 				     (chan_addr & 0xfff);
1702f71d0a05SDoug Thompson 	}
1703f71d0a05SDoug Thompson 
1704956b9ba1SJoe Perches 	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
1705f71d0a05SDoug Thompson 
1706b15f0fcaSBorislav Petkov 	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel);
1707f71d0a05SDoug Thompson 
170833ca0643SBorislav Petkov 	if (cs_found >= 0)
1709f71d0a05SDoug Thompson 		*chan_sel = channel;
171033ca0643SBorislav Petkov 
1711f71d0a05SDoug Thompson 	return cs_found;
1712f71d0a05SDoug Thompson }
1713f71d0a05SDoug Thompson 
171418b94f66SAravind Gopalakrishnan static int f15_m30h_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
171518b94f66SAravind Gopalakrishnan 					u64 sys_addr, int *chan_sel)
171618b94f66SAravind Gopalakrishnan {
171718b94f66SAravind Gopalakrishnan 	int cs_found = -EINVAL;
171818b94f66SAravind Gopalakrishnan 	int num_dcts_intlv = 0;
171918b94f66SAravind Gopalakrishnan 	u64 chan_addr, chan_offset;
172018b94f66SAravind Gopalakrishnan 	u64 dct_base, dct_limit;
172118b94f66SAravind Gopalakrishnan 	u32 dct_cont_base_reg, dct_cont_limit_reg, tmp;
172218b94f66SAravind Gopalakrishnan 	u8 channel, alias_channel, leg_mmio_hole, dct_sel, dct_offset_en;
172318b94f66SAravind Gopalakrishnan 
172418b94f66SAravind Gopalakrishnan 	u64 dhar_offset		= f10_dhar_offset(pvt);
172518b94f66SAravind Gopalakrishnan 	u8 intlv_addr		= dct_sel_interleave_addr(pvt);
172618b94f66SAravind Gopalakrishnan 	u8 node_id		= dram_dst_node(pvt, range);
172718b94f66SAravind Gopalakrishnan 	u8 intlv_en		= dram_intlv_en(pvt, range);
172818b94f66SAravind Gopalakrishnan 
172918b94f66SAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F1, DRAM_CONT_BASE, &dct_cont_base_reg);
173018b94f66SAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F1, DRAM_CONT_LIMIT, &dct_cont_limit_reg);
173118b94f66SAravind Gopalakrishnan 
173218b94f66SAravind Gopalakrishnan 	dct_offset_en		= (u8) ((dct_cont_base_reg >> 3) & BIT(0));
173318b94f66SAravind Gopalakrishnan 	dct_sel			= (u8) ((dct_cont_base_reg >> 4) & 0x7);
173418b94f66SAravind Gopalakrishnan 
173518b94f66SAravind Gopalakrishnan 	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
173618b94f66SAravind Gopalakrishnan 		 range, sys_addr, get_dram_limit(pvt, range));
173718b94f66SAravind Gopalakrishnan 
173818b94f66SAravind Gopalakrishnan 	if (!(get_dram_base(pvt, range)  <= sys_addr) &&
173918b94f66SAravind Gopalakrishnan 	    !(get_dram_limit(pvt, range) >= sys_addr))
174018b94f66SAravind Gopalakrishnan 		return -EINVAL;
174118b94f66SAravind Gopalakrishnan 
174218b94f66SAravind Gopalakrishnan 	if (dhar_valid(pvt) &&
174318b94f66SAravind Gopalakrishnan 	    dhar_base(pvt) <= sys_addr &&
174418b94f66SAravind Gopalakrishnan 	    sys_addr < BIT_64(32)) {
174518b94f66SAravind Gopalakrishnan 		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
174618b94f66SAravind Gopalakrishnan 			    sys_addr);
174718b94f66SAravind Gopalakrishnan 		return -EINVAL;
174818b94f66SAravind Gopalakrishnan 	}
174918b94f66SAravind Gopalakrishnan 
175018b94f66SAravind Gopalakrishnan 	/* Verify sys_addr is within DCT Range. */
17514fc06b31SAravind Gopalakrishnan 	dct_base = (u64) dct_sel_baseaddr(pvt);
17524fc06b31SAravind Gopalakrishnan 	dct_limit = (dct_cont_limit_reg >> 11) & 0x1FFF;
175318b94f66SAravind Gopalakrishnan 
175418b94f66SAravind Gopalakrishnan 	if (!(dct_cont_base_reg & BIT(0)) &&
17554fc06b31SAravind Gopalakrishnan 	    !(dct_base <= (sys_addr >> 27) &&
17564fc06b31SAravind Gopalakrishnan 	      dct_limit >= (sys_addr >> 27)))
175718b94f66SAravind Gopalakrishnan 		return -EINVAL;
175818b94f66SAravind Gopalakrishnan 
175918b94f66SAravind Gopalakrishnan 	/* Verify number of dct's that participate in channel interleaving. */
176018b94f66SAravind Gopalakrishnan 	num_dcts_intlv = (int) hweight8(intlv_en);
176118b94f66SAravind Gopalakrishnan 
176218b94f66SAravind Gopalakrishnan 	if (!(num_dcts_intlv % 2 == 0) || (num_dcts_intlv > 4))
176318b94f66SAravind Gopalakrishnan 		return -EINVAL;
176418b94f66SAravind Gopalakrishnan 
1765dc0a50a8SYazen Ghannam 	if (pvt->model >= 0x60)
1766dc0a50a8SYazen Ghannam 		channel = f1x_determine_channel(pvt, sys_addr, false, intlv_en);
1767dc0a50a8SYazen Ghannam 	else
176818b94f66SAravind Gopalakrishnan 		channel = f15_m30h_determine_channel(pvt, sys_addr, intlv_en,
176918b94f66SAravind Gopalakrishnan 						     num_dcts_intlv, dct_sel);
177018b94f66SAravind Gopalakrishnan 
177118b94f66SAravind Gopalakrishnan 	/* Verify we stay within the MAX number of channels allowed */
17727f3f5240SAravind Gopalakrishnan 	if (channel > 3)
177318b94f66SAravind Gopalakrishnan 		return -EINVAL;
177418b94f66SAravind Gopalakrishnan 
177518b94f66SAravind Gopalakrishnan 	leg_mmio_hole = (u8) (dct_cont_base_reg >> 1 & BIT(0));
177618b94f66SAravind Gopalakrishnan 
177718b94f66SAravind Gopalakrishnan 	/* Get normalized DCT addr */
177818b94f66SAravind Gopalakrishnan 	if (leg_mmio_hole && (sys_addr >= BIT_64(32)))
177918b94f66SAravind Gopalakrishnan 		chan_offset = dhar_offset;
178018b94f66SAravind Gopalakrishnan 	else
17814fc06b31SAravind Gopalakrishnan 		chan_offset = dct_base << 27;
178218b94f66SAravind Gopalakrishnan 
178318b94f66SAravind Gopalakrishnan 	chan_addr = sys_addr - chan_offset;
178418b94f66SAravind Gopalakrishnan 
178518b94f66SAravind Gopalakrishnan 	/* remove channel interleave */
178618b94f66SAravind Gopalakrishnan 	if (num_dcts_intlv == 2) {
178718b94f66SAravind Gopalakrishnan 		if (intlv_addr == 0x4)
178818b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 9) << 8) |
178918b94f66SAravind Gopalakrishnan 						(chan_addr & 0xff);
179018b94f66SAravind Gopalakrishnan 		else if (intlv_addr == 0x5)
179118b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 10) << 9) |
179218b94f66SAravind Gopalakrishnan 						(chan_addr & 0x1ff);
179318b94f66SAravind Gopalakrishnan 		else
179418b94f66SAravind Gopalakrishnan 			return -EINVAL;
179518b94f66SAravind Gopalakrishnan 
179618b94f66SAravind Gopalakrishnan 	} else if (num_dcts_intlv == 4) {
179718b94f66SAravind Gopalakrishnan 		if (intlv_addr == 0x4)
179818b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 10) << 8) |
179918b94f66SAravind Gopalakrishnan 							(chan_addr & 0xff);
180018b94f66SAravind Gopalakrishnan 		else if (intlv_addr == 0x5)
180118b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 11) << 9) |
180218b94f66SAravind Gopalakrishnan 							(chan_addr & 0x1ff);
180318b94f66SAravind Gopalakrishnan 		else
180418b94f66SAravind Gopalakrishnan 			return -EINVAL;
180518b94f66SAravind Gopalakrishnan 	}
180618b94f66SAravind Gopalakrishnan 
180718b94f66SAravind Gopalakrishnan 	if (dct_offset_en) {
180818b94f66SAravind Gopalakrishnan 		amd64_read_pci_cfg(pvt->F1,
180918b94f66SAravind Gopalakrishnan 				   DRAM_CONT_HIGH_OFF + (int) channel * 4,
181018b94f66SAravind Gopalakrishnan 				   &tmp);
18114fc06b31SAravind Gopalakrishnan 		chan_addr +=  (u64) ((tmp >> 11) & 0xfff) << 27;
181218b94f66SAravind Gopalakrishnan 	}
181318b94f66SAravind Gopalakrishnan 
181418b94f66SAravind Gopalakrishnan 	f15h_select_dct(pvt, channel);
181518b94f66SAravind Gopalakrishnan 
181618b94f66SAravind Gopalakrishnan 	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
181718b94f66SAravind Gopalakrishnan 
181818b94f66SAravind Gopalakrishnan 	/*
181918b94f66SAravind Gopalakrishnan 	 * Find Chip select:
182018b94f66SAravind Gopalakrishnan 	 * if channel = 3, then alias it to 1. This is because, in F15 M30h,
182118b94f66SAravind Gopalakrishnan 	 * there is support for 4 DCT's, but only 2 are currently functional.
182218b94f66SAravind Gopalakrishnan 	 * They are DCT0 and DCT3. But we have read all registers of DCT3 into
182318b94f66SAravind Gopalakrishnan 	 * pvt->csels[1]. So we need to use '1' here to get correct info.
182418b94f66SAravind Gopalakrishnan 	 * Refer F15 M30h BKDG Section 2.10 and 2.10.3 for clarifications.
182518b94f66SAravind Gopalakrishnan 	 */
182618b94f66SAravind Gopalakrishnan 	alias_channel =  (channel == 3) ? 1 : channel;
182718b94f66SAravind Gopalakrishnan 
182818b94f66SAravind Gopalakrishnan 	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, alias_channel);
182918b94f66SAravind Gopalakrishnan 
183018b94f66SAravind Gopalakrishnan 	if (cs_found >= 0)
183118b94f66SAravind Gopalakrishnan 		*chan_sel = alias_channel;
183218b94f66SAravind Gopalakrishnan 
183318b94f66SAravind Gopalakrishnan 	return cs_found;
183418b94f66SAravind Gopalakrishnan }
183518b94f66SAravind Gopalakrishnan 
183618b94f66SAravind Gopalakrishnan static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt,
183718b94f66SAravind Gopalakrishnan 					u64 sys_addr,
183833ca0643SBorislav Petkov 					int *chan_sel)
1839f71d0a05SDoug Thompson {
1840e761359aSBorislav Petkov 	int cs_found = -EINVAL;
1841e761359aSBorislav Petkov 	unsigned range;
1842f71d0a05SDoug Thompson 
18437f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
18447f19bf75SBorislav Petkov 		if (!dram_rw(pvt, range))
1845f71d0a05SDoug Thompson 			continue;
1846f71d0a05SDoug Thompson 
184718b94f66SAravind Gopalakrishnan 		if (pvt->fam == 0x15 && pvt->model >= 0x30)
184818b94f66SAravind Gopalakrishnan 			cs_found = f15_m30h_match_to_this_node(pvt, range,
184918b94f66SAravind Gopalakrishnan 							       sys_addr,
185018b94f66SAravind Gopalakrishnan 							       chan_sel);
1851f71d0a05SDoug Thompson 
185218b94f66SAravind Gopalakrishnan 		else if ((get_dram_base(pvt, range)  <= sys_addr) &&
185318b94f66SAravind Gopalakrishnan 			 (get_dram_limit(pvt, range) >= sys_addr)) {
1854b15f0fcaSBorislav Petkov 			cs_found = f1x_match_to_this_node(pvt, range,
185533ca0643SBorislav Petkov 							  sys_addr, chan_sel);
1856f71d0a05SDoug Thompson 			if (cs_found >= 0)
1857f71d0a05SDoug Thompson 				break;
1858f71d0a05SDoug Thompson 		}
1859f71d0a05SDoug Thompson 	}
1860f71d0a05SDoug Thompson 	return cs_found;
1861f71d0a05SDoug Thompson }
1862f71d0a05SDoug Thompson 
1863f71d0a05SDoug Thompson /*
1864bdc30a0cSBorislav Petkov  * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
1865bdc30a0cSBorislav Petkov  * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
1866f71d0a05SDoug Thompson  *
1867bdc30a0cSBorislav Petkov  * The @sys_addr is usually an error address received from the hardware
1868bdc30a0cSBorislav Petkov  * (MCX_ADDR).
1869f71d0a05SDoug Thompson  */
1870b15f0fcaSBorislav Petkov static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
187133ca0643SBorislav Petkov 				     struct err_info *err)
1872f71d0a05SDoug Thompson {
1873f71d0a05SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
1874f71d0a05SDoug Thompson 
187533ca0643SBorislav Petkov 	error_address_to_page_and_offset(sys_addr, err);
1876ab5a503cSMauro Carvalho Chehab 
187733ca0643SBorislav Petkov 	err->csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &err->channel);
187833ca0643SBorislav Petkov 	if (err->csrow < 0) {
187933ca0643SBorislav Petkov 		err->err_code = ERR_CSROW;
1880bdc30a0cSBorislav Petkov 		return;
1881bdc30a0cSBorislav Petkov 	}
1882bdc30a0cSBorislav Petkov 
1883f71d0a05SDoug Thompson 	/*
1884bdc30a0cSBorislav Petkov 	 * We need the syndromes for channel detection only when we're
1885bdc30a0cSBorislav Petkov 	 * ganged. Otherwise @chan should already contain the channel at
1886bdc30a0cSBorislav Petkov 	 * this point.
1887f71d0a05SDoug Thompson 	 */
1888a97fa68eSBorislav Petkov 	if (dct_ganging_enabled(pvt))
188933ca0643SBorislav Petkov 		err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
1890f71d0a05SDoug Thompson }
1891f71d0a05SDoug Thompson 
1892f71d0a05SDoug Thompson /*
18938566c4dfSBorislav Petkov  * debug routine to display the memory sizes of all logical DIMMs and its
1894cb328507SBorislav Petkov  * CSROWs
1895f71d0a05SDoug Thompson  */
1896d1ea71cdSBorislav Petkov static void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
1897f71d0a05SDoug Thompson {
1898bb89f5a0SBorislav Petkov 	int dimm, size0, size1;
1899525a1b20SBorislav Petkov 	u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases;
1900525a1b20SBorislav Petkov 	u32 dbam  = ctrl ? pvt->dbam1 : pvt->dbam0;
1901f71d0a05SDoug Thompson 
1902a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf) {
19038566c4dfSBorislav Petkov 		/* K8 families < revF not supported yet */
19041433eb99SBorislav Petkov 	       if (pvt->ext_model < K8_REV_F)
19058566c4dfSBorislav Petkov 			return;
19068566c4dfSBorislav Petkov 	       else
19078566c4dfSBorislav Petkov 		       WARN_ON(ctrl != 0);
19088566c4dfSBorislav Petkov 	}
19098566c4dfSBorislav Petkov 
19107981a28fSAravind Gopalakrishnan 	if (pvt->fam == 0x10) {
19117981a28fSAravind Gopalakrishnan 		dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1
19127981a28fSAravind Gopalakrishnan 							   : pvt->dbam0;
19137981a28fSAravind Gopalakrishnan 		dcsb = (ctrl && !dct_ganging_enabled(pvt)) ?
19147981a28fSAravind Gopalakrishnan 				 pvt->csels[1].csbases :
19157981a28fSAravind Gopalakrishnan 				 pvt->csels[0].csbases;
19167981a28fSAravind Gopalakrishnan 	} else if (ctrl) {
19177981a28fSAravind Gopalakrishnan 		dbam = pvt->dbam0;
19187981a28fSAravind Gopalakrishnan 		dcsb = pvt->csels[1].csbases;
19197981a28fSAravind Gopalakrishnan 	}
1920956b9ba1SJoe Perches 	edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
1921956b9ba1SJoe Perches 		 ctrl, dbam);
1922f71d0a05SDoug Thompson 
19238566c4dfSBorislav Petkov 	edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
19248566c4dfSBorislav Petkov 
1925f71d0a05SDoug Thompson 	/* Dump memory sizes for DIMM and its CSROWs */
1926f71d0a05SDoug Thompson 	for (dimm = 0; dimm < 4; dimm++) {
1927f71d0a05SDoug Thompson 
1928f71d0a05SDoug Thompson 		size0 = 0;
192911c75eadSBorislav Petkov 		if (dcsb[dimm*2] & DCSB_CS_ENABLE)
1930a597d2a5SAravind Gopalakrishnan 			/* For f15m60h, need multiplier for LRDIMM cs_size
1931a597d2a5SAravind Gopalakrishnan 			 * calculation. We pass 'dimm' value to the dbam_to_cs
1932a597d2a5SAravind Gopalakrishnan 			 * mapper so we can find the multiplier from the
1933a597d2a5SAravind Gopalakrishnan 			 * corresponding DCSM.
1934a597d2a5SAravind Gopalakrishnan 			 */
193541d8bfabSBorislav Petkov 			size0 = pvt->ops->dbam_to_cs(pvt, ctrl,
1936a597d2a5SAravind Gopalakrishnan 						     DBAM_DIMM(dimm, dbam),
1937a597d2a5SAravind Gopalakrishnan 						     dimm);
1938f71d0a05SDoug Thompson 
1939f71d0a05SDoug Thompson 		size1 = 0;
194011c75eadSBorislav Petkov 		if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE)
194141d8bfabSBorislav Petkov 			size1 = pvt->ops->dbam_to_cs(pvt, ctrl,
1942a597d2a5SAravind Gopalakrishnan 						     DBAM_DIMM(dimm, dbam),
1943a597d2a5SAravind Gopalakrishnan 						     dimm);
1944f71d0a05SDoug Thompson 
194524f9a7feSBorislav Petkov 		amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
1946bb89f5a0SBorislav Petkov 				dimm * 2,     size0,
1947bb89f5a0SBorislav Petkov 				dimm * 2 + 1, size1);
1948f71d0a05SDoug Thompson 	}
1949f71d0a05SDoug Thompson }
1950f71d0a05SDoug Thompson 
1951d1ea71cdSBorislav Petkov static struct amd64_family_type family_types[] = {
19524d37607aSDoug Thompson 	[K8_CPUS] = {
19530092b20dSBorislav Petkov 		.ctl_name = "K8",
19548d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
19553f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
19564d37607aSDoug Thompson 		.ops = {
19574d37607aSDoug Thompson 			.early_channel_count	= k8_early_channel_count,
19584d37607aSDoug Thompson 			.map_sysaddr_to_csrow	= k8_map_sysaddr_to_csrow,
19591433eb99SBorislav Petkov 			.dbam_to_cs		= k8_dbam_to_chip_select,
19604d37607aSDoug Thompson 		}
19614d37607aSDoug Thompson 	},
19624d37607aSDoug Thompson 	[F10_CPUS] = {
19630092b20dSBorislav Petkov 		.ctl_name = "F10h",
19648d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP,
19653f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM,
19664d37607aSDoug Thompson 		.ops = {
19677d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
1968b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
19691433eb99SBorislav Petkov 			.dbam_to_cs		= f10_dbam_to_chip_select,
1970b2b0c605SBorislav Petkov 		}
1971b2b0c605SBorislav Petkov 	},
1972b2b0c605SBorislav Petkov 	[F15_CPUS] = {
1973b2b0c605SBorislav Petkov 		.ctl_name = "F15h",
1974df71a053SBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1,
19753f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2,
1976b2b0c605SBorislav Petkov 		.ops = {
19777d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
1978b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
197941d8bfabSBorislav Petkov 			.dbam_to_cs		= f15_dbam_to_chip_select,
19804d37607aSDoug Thompson 		}
19814d37607aSDoug Thompson 	},
198218b94f66SAravind Gopalakrishnan 	[F15_M30H_CPUS] = {
198318b94f66SAravind Gopalakrishnan 		.ctl_name = "F15h_M30h",
198418b94f66SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1,
19853f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2,
198618b94f66SAravind Gopalakrishnan 		.ops = {
198718b94f66SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
198818b94f66SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
198918b94f66SAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
199018b94f66SAravind Gopalakrishnan 		}
199118b94f66SAravind Gopalakrishnan 	},
1992a597d2a5SAravind Gopalakrishnan 	[F15_M60H_CPUS] = {
1993a597d2a5SAravind Gopalakrishnan 		.ctl_name = "F15h_M60h",
1994a597d2a5SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1,
19953f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2,
1996a597d2a5SAravind Gopalakrishnan 		.ops = {
1997a597d2a5SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
1998a597d2a5SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
1999a597d2a5SAravind Gopalakrishnan 			.dbam_to_cs		= f15_m60h_dbam_to_chip_select,
2000a597d2a5SAravind Gopalakrishnan 		}
2001a597d2a5SAravind Gopalakrishnan 	},
200294c1acf2SAravind Gopalakrishnan 	[F16_CPUS] = {
200394c1acf2SAravind Gopalakrishnan 		.ctl_name = "F16h",
200494c1acf2SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1,
20053f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2,
200694c1acf2SAravind Gopalakrishnan 		.ops = {
200794c1acf2SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
200894c1acf2SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
200994c1acf2SAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
201094c1acf2SAravind Gopalakrishnan 		}
201194c1acf2SAravind Gopalakrishnan 	},
201285a8885bSAravind Gopalakrishnan 	[F16_M30H_CPUS] = {
201385a8885bSAravind Gopalakrishnan 		.ctl_name = "F16h_M30h",
201485a8885bSAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F1,
20153f37a36bSBorislav Petkov 		.f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2,
201685a8885bSAravind Gopalakrishnan 		.ops = {
201785a8885bSAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
201885a8885bSAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
201985a8885bSAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
202085a8885bSAravind Gopalakrishnan 		}
202185a8885bSAravind Gopalakrishnan 	},
2022f1cbbec9SYazen Ghannam 	[F17_CPUS] = {
2023f1cbbec9SYazen Ghannam 		.ctl_name = "F17h",
2024f1cbbec9SYazen Ghannam 		.f0_id = PCI_DEVICE_ID_AMD_17H_DF_F0,
2025f1cbbec9SYazen Ghannam 		.f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6,
2026f1cbbec9SYazen Ghannam 		.ops = {
2027f1cbbec9SYazen Ghannam 			.early_channel_count	= f17_early_channel_count,
2028f1cbbec9SYazen Ghannam 			.dbam_to_cs		= f17_base_addr_to_cs_size,
2029f1cbbec9SYazen Ghannam 		}
2030f1cbbec9SYazen Ghannam 	},
20314d37607aSDoug Thompson };
20324d37607aSDoug Thompson 
2033b1289d6fSDoug Thompson /*
2034bfc04aecSBorislav Petkov  * These are tables of eigenvectors (one per line) which can be used for the
2035bfc04aecSBorislav Petkov  * construction of the syndrome tables. The modified syndrome search algorithm
2036bfc04aecSBorislav Petkov  * uses those to find the symbol in error and thus the DIMM.
2037b1289d6fSDoug Thompson  *
2038bfc04aecSBorislav Petkov  * Algorithm courtesy of Ross LaFetra from AMD.
2039b1289d6fSDoug Thompson  */
2040c7e5301aSDaniel J Blueman static const u16 x4_vectors[] = {
2041bfc04aecSBorislav Petkov 	0x2f57, 0x1afe, 0x66cc, 0xdd88,
2042bfc04aecSBorislav Petkov 	0x11eb, 0x3396, 0x7f4c, 0xeac8,
2043bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008,
2044bfc04aecSBorislav Petkov 	0x1013, 0x3032, 0x4044, 0x8088,
2045bfc04aecSBorislav Petkov 	0x106b, 0x30d6, 0x70fc, 0xe0a8,
2046bfc04aecSBorislav Petkov 	0x4857, 0xc4fe, 0x13cc, 0x3288,
2047bfc04aecSBorislav Petkov 	0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
2048bfc04aecSBorislav Petkov 	0x1f39, 0x251e, 0xbd6c, 0x6bd8,
2049bfc04aecSBorislav Petkov 	0x15c1, 0x2a42, 0x89ac, 0x4758,
2050bfc04aecSBorislav Petkov 	0x2b03, 0x1602, 0x4f0c, 0xca08,
2051bfc04aecSBorislav Petkov 	0x1f07, 0x3a0e, 0x6b04, 0xbd08,
2052bfc04aecSBorislav Petkov 	0x8ba7, 0x465e, 0x244c, 0x1cc8,
2053bfc04aecSBorislav Petkov 	0x2b87, 0x164e, 0x642c, 0xdc18,
2054bfc04aecSBorislav Petkov 	0x40b9, 0x80de, 0x1094, 0x20e8,
2055bfc04aecSBorislav Petkov 	0x27db, 0x1eb6, 0x9dac, 0x7b58,
2056bfc04aecSBorislav Petkov 	0x11c1, 0x2242, 0x84ac, 0x4c58,
2057bfc04aecSBorislav Petkov 	0x1be5, 0x2d7a, 0x5e34, 0xa718,
2058bfc04aecSBorislav Petkov 	0x4b39, 0x8d1e, 0x14b4, 0x28d8,
2059bfc04aecSBorislav Petkov 	0x4c97, 0xc87e, 0x11fc, 0x33a8,
2060bfc04aecSBorislav Petkov 	0x8e97, 0x497e, 0x2ffc, 0x1aa8,
2061bfc04aecSBorislav Petkov 	0x16b3, 0x3d62, 0x4f34, 0x8518,
2062bfc04aecSBorislav Petkov 	0x1e2f, 0x391a, 0x5cac, 0xf858,
2063bfc04aecSBorislav Petkov 	0x1d9f, 0x3b7a, 0x572c, 0xfe18,
2064bfc04aecSBorislav Petkov 	0x15f5, 0x2a5a, 0x5264, 0xa3b8,
2065bfc04aecSBorislav Petkov 	0x1dbb, 0x3b66, 0x715c, 0xe3f8,
2066bfc04aecSBorislav Petkov 	0x4397, 0xc27e, 0x17fc, 0x3ea8,
2067bfc04aecSBorislav Petkov 	0x1617, 0x3d3e, 0x6464, 0xb8b8,
2068bfc04aecSBorislav Petkov 	0x23ff, 0x12aa, 0xab6c, 0x56d8,
2069bfc04aecSBorislav Petkov 	0x2dfb, 0x1ba6, 0x913c, 0x7328,
2070bfc04aecSBorislav Petkov 	0x185d, 0x2ca6, 0x7914, 0x9e28,
2071bfc04aecSBorislav Petkov 	0x171b, 0x3e36, 0x7d7c, 0xebe8,
2072bfc04aecSBorislav Petkov 	0x4199, 0x82ee, 0x19f4, 0x2e58,
2073bfc04aecSBorislav Petkov 	0x4807, 0xc40e, 0x130c, 0x3208,
2074bfc04aecSBorislav Petkov 	0x1905, 0x2e0a, 0x5804, 0xac08,
2075bfc04aecSBorislav Petkov 	0x213f, 0x132a, 0xadfc, 0x5ba8,
2076bfc04aecSBorislav Petkov 	0x19a9, 0x2efe, 0xb5cc, 0x6f88,
2077b1289d6fSDoug Thompson };
2078b1289d6fSDoug Thompson 
2079c7e5301aSDaniel J Blueman static const u16 x8_vectors[] = {
2080bfc04aecSBorislav Petkov 	0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
2081bfc04aecSBorislav Petkov 	0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
2082bfc04aecSBorislav Petkov 	0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
2083bfc04aecSBorislav Petkov 	0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
2084bfc04aecSBorislav Petkov 	0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
2085bfc04aecSBorislav Petkov 	0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
2086bfc04aecSBorislav Petkov 	0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
2087bfc04aecSBorislav Petkov 	0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
2088bfc04aecSBorislav Petkov 	0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
2089bfc04aecSBorislav Petkov 	0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
2090bfc04aecSBorislav Petkov 	0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
2091bfc04aecSBorislav Petkov 	0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
2092bfc04aecSBorislav Petkov 	0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
2093bfc04aecSBorislav Petkov 	0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
2094bfc04aecSBorislav Petkov 	0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
2095bfc04aecSBorislav Petkov 	0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
2096bfc04aecSBorislav Petkov 	0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
2097bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
2098bfc04aecSBorislav Petkov 	0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
2099bfc04aecSBorislav Petkov };
2100bfc04aecSBorislav Petkov 
2101c7e5301aSDaniel J Blueman static int decode_syndrome(u16 syndrome, const u16 *vectors, unsigned num_vecs,
2102d34a6ecdSBorislav Petkov 			   unsigned v_dim)
2103b1289d6fSDoug Thompson {
2104bfc04aecSBorislav Petkov 	unsigned int i, err_sym;
2105b1289d6fSDoug Thompson 
2106bfc04aecSBorislav Petkov 	for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
2107bfc04aecSBorislav Petkov 		u16 s = syndrome;
2108d34a6ecdSBorislav Petkov 		unsigned v_idx =  err_sym * v_dim;
2109d34a6ecdSBorislav Petkov 		unsigned v_end = (err_sym + 1) * v_dim;
2110b1289d6fSDoug Thompson 
2111bfc04aecSBorislav Petkov 		/* walk over all 16 bits of the syndrome */
2112bfc04aecSBorislav Petkov 		for (i = 1; i < (1U << 16); i <<= 1) {
2113bfc04aecSBorislav Petkov 
2114bfc04aecSBorislav Petkov 			/* if bit is set in that eigenvector... */
2115bfc04aecSBorislav Petkov 			if (v_idx < v_end && vectors[v_idx] & i) {
2116bfc04aecSBorislav Petkov 				u16 ev_comp = vectors[v_idx++];
2117bfc04aecSBorislav Petkov 
2118bfc04aecSBorislav Petkov 				/* ... and bit set in the modified syndrome, */
2119bfc04aecSBorislav Petkov 				if (s & i) {
2120bfc04aecSBorislav Petkov 					/* remove it. */
2121bfc04aecSBorislav Petkov 					s ^= ev_comp;
2122bfc04aecSBorislav Petkov 
2123bfc04aecSBorislav Petkov 					if (!s)
2124bfc04aecSBorislav Petkov 						return err_sym;
2125bfc04aecSBorislav Petkov 				}
2126bfc04aecSBorislav Petkov 
2127bfc04aecSBorislav Petkov 			} else if (s & i)
2128bfc04aecSBorislav Petkov 				/* can't get to zero, move to next symbol */
2129bfc04aecSBorislav Petkov 				break;
2130bfc04aecSBorislav Petkov 		}
2131b1289d6fSDoug Thompson 	}
2132b1289d6fSDoug Thompson 
2133956b9ba1SJoe Perches 	edac_dbg(0, "syndrome(%x) not found\n", syndrome);
2134b1289d6fSDoug Thompson 	return -1;
2135b1289d6fSDoug Thompson }
2136d27bf6faSDoug Thompson 
2137bfc04aecSBorislav Petkov static int map_err_sym_to_channel(int err_sym, int sym_size)
2138bfc04aecSBorislav Petkov {
2139bfc04aecSBorislav Petkov 	if (sym_size == 4)
2140bfc04aecSBorislav Petkov 		switch (err_sym) {
2141bfc04aecSBorislav Petkov 		case 0x20:
2142bfc04aecSBorislav Petkov 		case 0x21:
2143bfc04aecSBorislav Petkov 			return 0;
2144bfc04aecSBorislav Petkov 			break;
2145bfc04aecSBorislav Petkov 		case 0x22:
2146bfc04aecSBorislav Petkov 		case 0x23:
2147bfc04aecSBorislav Petkov 			return 1;
2148bfc04aecSBorislav Petkov 			break;
2149bfc04aecSBorislav Petkov 		default:
2150bfc04aecSBorislav Petkov 			return err_sym >> 4;
2151bfc04aecSBorislav Petkov 			break;
2152bfc04aecSBorislav Petkov 		}
2153bfc04aecSBorislav Petkov 	/* x8 symbols */
2154bfc04aecSBorislav Petkov 	else
2155bfc04aecSBorislav Petkov 		switch (err_sym) {
2156bfc04aecSBorislav Petkov 		/* imaginary bits not in a DIMM */
2157bfc04aecSBorislav Petkov 		case 0x10:
2158bfc04aecSBorislav Petkov 			WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
2159bfc04aecSBorislav Petkov 					  err_sym);
2160bfc04aecSBorislav Petkov 			return -1;
2161bfc04aecSBorislav Petkov 			break;
2162bfc04aecSBorislav Petkov 
2163bfc04aecSBorislav Petkov 		case 0x11:
2164bfc04aecSBorislav Petkov 			return 0;
2165bfc04aecSBorislav Petkov 			break;
2166bfc04aecSBorislav Petkov 		case 0x12:
2167bfc04aecSBorislav Petkov 			return 1;
2168bfc04aecSBorislav Petkov 			break;
2169bfc04aecSBorislav Petkov 		default:
2170bfc04aecSBorislav Petkov 			return err_sym >> 3;
2171bfc04aecSBorislav Petkov 			break;
2172bfc04aecSBorislav Petkov 		}
2173bfc04aecSBorislav Petkov 	return -1;
2174bfc04aecSBorislav Petkov }
2175bfc04aecSBorislav Petkov 
2176bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
2177bfc04aecSBorislav Petkov {
2178bfc04aecSBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
2179ad6a32e9SBorislav Petkov 	int err_sym = -1;
2180bfc04aecSBorislav Petkov 
2181a3b7db09SBorislav Petkov 	if (pvt->ecc_sym_sz == 8)
2182bfc04aecSBorislav Petkov 		err_sym = decode_syndrome(syndrome, x8_vectors,
2183ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x8_vectors),
2184a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
2185a3b7db09SBorislav Petkov 	else if (pvt->ecc_sym_sz == 4)
2186ad6a32e9SBorislav Petkov 		err_sym = decode_syndrome(syndrome, x4_vectors,
2187ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x4_vectors),
2188a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
2189ad6a32e9SBorislav Petkov 	else {
2190a3b7db09SBorislav Petkov 		amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz);
2191ad6a32e9SBorislav Petkov 		return err_sym;
2192bfc04aecSBorislav Petkov 	}
2193ad6a32e9SBorislav Petkov 
2194a3b7db09SBorislav Petkov 	return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz);
219541c31044SBorislav Petkov }
2196bfc04aecSBorislav Petkov 
2197e70984d9SYazen Ghannam static void __log_ecc_error(struct mem_ctl_info *mci, struct err_info *err,
219833ca0643SBorislav Petkov 			    u8 ecc_type)
2199d27bf6faSDoug Thompson {
220033ca0643SBorislav Petkov 	enum hw_event_mc_err_type err_type;
220133ca0643SBorislav Petkov 	const char *string;
2202d27bf6faSDoug Thompson 
220333ca0643SBorislav Petkov 	if (ecc_type == 2)
220433ca0643SBorislav Petkov 		err_type = HW_EVENT_ERR_CORRECTED;
220533ca0643SBorislav Petkov 	else if (ecc_type == 1)
220633ca0643SBorislav Petkov 		err_type = HW_EVENT_ERR_UNCORRECTED;
2207d12a969eSYazen Ghannam 	else if (ecc_type == 3)
2208d12a969eSYazen Ghannam 		err_type = HW_EVENT_ERR_DEFERRED;
220933ca0643SBorislav Petkov 	else {
221033ca0643SBorislav Petkov 		WARN(1, "Something is rotten in the state of Denmark.\n");
2211d27bf6faSDoug Thompson 		return;
2212d27bf6faSDoug Thompson 	}
2213d27bf6faSDoug Thompson 
221433ca0643SBorislav Petkov 	switch (err->err_code) {
221533ca0643SBorislav Petkov 	case DECODE_OK:
221633ca0643SBorislav Petkov 		string = "";
221733ca0643SBorislav Petkov 		break;
221833ca0643SBorislav Petkov 	case ERR_NODE:
221933ca0643SBorislav Petkov 		string = "Failed to map error addr to a node";
222033ca0643SBorislav Petkov 		break;
222133ca0643SBorislav Petkov 	case ERR_CSROW:
222233ca0643SBorislav Petkov 		string = "Failed to map error addr to a csrow";
222333ca0643SBorislav Petkov 		break;
222433ca0643SBorislav Petkov 	case ERR_CHANNEL:
222533ca0643SBorislav Petkov 		string = "unknown syndrome - possible error reporting race";
222633ca0643SBorislav Petkov 		break;
222733ca0643SBorislav Petkov 	default:
222833ca0643SBorislav Petkov 		string = "WTF error";
222933ca0643SBorislav Petkov 		break;
2230d27bf6faSDoug Thompson 	}
223133ca0643SBorislav Petkov 
223233ca0643SBorislav Petkov 	edac_mc_handle_error(err_type, mci, 1,
223333ca0643SBorislav Petkov 			     err->page, err->offset, err->syndrome,
223433ca0643SBorislav Petkov 			     err->csrow, err->channel, -1,
223533ca0643SBorislav Petkov 			     string, "");
2236d27bf6faSDoug Thompson }
2237d27bf6faSDoug Thompson 
2238df781d03SBorislav Petkov static inline void decode_bus_error(int node_id, struct mce *m)
2239d27bf6faSDoug Thompson {
22400c510cc8SDaniel J Blueman 	struct mem_ctl_info *mci;
22410c510cc8SDaniel J Blueman 	struct amd64_pvt *pvt;
2242f192c7b1SBorislav Petkov 	u8 ecc_type = (m->status >> 45) & 0x3;
224366fed2d4SBorislav Petkov 	u8 xec = XEC(m->status, 0x1f);
224466fed2d4SBorislav Petkov 	u16 ec = EC(m->status);
224533ca0643SBorislav Petkov 	u64 sys_addr;
224633ca0643SBorislav Petkov 	struct err_info err;
2247d27bf6faSDoug Thompson 
22480c510cc8SDaniel J Blueman 	mci = edac_mc_find(node_id);
22490c510cc8SDaniel J Blueman 	if (!mci)
22500c510cc8SDaniel J Blueman 		return;
22510c510cc8SDaniel J Blueman 
22520c510cc8SDaniel J Blueman 	pvt = mci->pvt_info;
22530c510cc8SDaniel J Blueman 
225466fed2d4SBorislav Petkov 	/* Bail out early if this was an 'observed' error */
22555980bb9cSBorislav Petkov 	if (PP(ec) == NBSL_PP_OBS)
2256b70ef010SBorislav Petkov 		return;
2257d27bf6faSDoug Thompson 
2258ecaf5606SBorislav Petkov 	/* Do only ECC errors */
2259ecaf5606SBorislav Petkov 	if (xec && xec != F10_NBSL_EXT_ERR_ECC)
2260d27bf6faSDoug Thompson 		return;
2261d27bf6faSDoug Thompson 
226233ca0643SBorislav Petkov 	memset(&err, 0, sizeof(err));
226333ca0643SBorislav Petkov 
2264a4b4bedcSBorislav Petkov 	sys_addr = get_error_address(pvt, m);
226533ca0643SBorislav Petkov 
2266ecaf5606SBorislav Petkov 	if (ecc_type == 2)
226733ca0643SBorislav Petkov 		err.syndrome = extract_syndrome(m->status);
226833ca0643SBorislav Petkov 
226933ca0643SBorislav Petkov 	pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err);
227033ca0643SBorislav Petkov 
2271e70984d9SYazen Ghannam 	__log_ecc_error(mci, &err, ecc_type);
2272d27bf6faSDoug Thompson }
2273d27bf6faSDoug Thompson 
22740ec449eeSDoug Thompson /*
22753f37a36bSBorislav Petkov  * Use pvt->F3 which contains the F3 CPU PCI device to get the related
22763f37a36bSBorislav Petkov  * F1 (AddrMap) and F2 (Dct) devices. Return negative value on error.
22770ec449eeSDoug Thompson  */
22783f37a36bSBorislav Petkov static int reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 f1_id, u16 f2_id)
22790ec449eeSDoug Thompson {
22800ec449eeSDoug Thompson 	/* Reserve the ADDRESS MAP Device */
22813f37a36bSBorislav Petkov 	pvt->F1 = pci_get_related_function(pvt->F3->vendor, f1_id, pvt->F3);
22828d5b5d9cSBorislav Petkov 	if (!pvt->F1) {
228324f9a7feSBorislav Petkov 		amd64_err("error address map device not found: "
22840ec449eeSDoug Thompson 			  "vendor %x device 0x%x (broken BIOS?)\n",
2285bbd0c1f6SBorislav Petkov 			  PCI_VENDOR_ID_AMD, f1_id);
2286bbd0c1f6SBorislav Petkov 		return -ENODEV;
22870ec449eeSDoug Thompson 	}
22880ec449eeSDoug Thompson 
22893f37a36bSBorislav Petkov 	/* Reserve the DCT Device */
22903f37a36bSBorislav Petkov 	pvt->F2 = pci_get_related_function(pvt->F3->vendor, f2_id, pvt->F3);
22913f37a36bSBorislav Petkov 	if (!pvt->F2) {
22928d5b5d9cSBorislav Petkov 		pci_dev_put(pvt->F1);
22938d5b5d9cSBorislav Petkov 		pvt->F1 = NULL;
22940ec449eeSDoug Thompson 
22953f37a36bSBorislav Petkov 		amd64_err("error F2 device not found: "
22960ec449eeSDoug Thompson 			  "vendor %x device 0x%x (broken BIOS?)\n",
22973f37a36bSBorislav Petkov 			  PCI_VENDOR_ID_AMD, f2_id);
22988d5b5d9cSBorislav Petkov 
2299bbd0c1f6SBorislav Petkov 		return -ENODEV;
23000ec449eeSDoug Thompson 	}
2301956b9ba1SJoe Perches 	edac_dbg(1, "F1: %s\n", pci_name(pvt->F1));
2302956b9ba1SJoe Perches 	edac_dbg(1, "F2: %s\n", pci_name(pvt->F2));
2303956b9ba1SJoe Perches 	edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
23040ec449eeSDoug Thompson 
23050ec449eeSDoug Thompson 	return 0;
23060ec449eeSDoug Thompson }
23070ec449eeSDoug Thompson 
2308360b7f3cSBorislav Petkov static void free_mc_sibling_devs(struct amd64_pvt *pvt)
23090ec449eeSDoug Thompson {
23108d5b5d9cSBorislav Petkov 	pci_dev_put(pvt->F1);
23113f37a36bSBorislav Petkov 	pci_dev_put(pvt->F2);
23120ec449eeSDoug Thompson }
23130ec449eeSDoug Thompson 
23140ec449eeSDoug Thompson /*
23150ec449eeSDoug Thompson  * Retrieve the hardware registers of the memory controller (this includes the
23160ec449eeSDoug Thompson  * 'Address Map' and 'Misc' device regs)
23170ec449eeSDoug Thompson  */
2318360b7f3cSBorislav Petkov static void read_mc_regs(struct amd64_pvt *pvt)
23190ec449eeSDoug Thompson {
2320a4b4bedcSBorislav Petkov 	unsigned range;
23210ec449eeSDoug Thompson 	u64 msr_val;
2322ad6a32e9SBorislav Petkov 	u32 tmp;
23230ec449eeSDoug Thompson 
23240ec449eeSDoug Thompson 	/*
23250ec449eeSDoug Thompson 	 * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
23260ec449eeSDoug Thompson 	 * those are Read-As-Zero
23270ec449eeSDoug Thompson 	 */
2328e97f8bb8SBorislav Petkov 	rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
2329956b9ba1SJoe Perches 	edac_dbg(0, "  TOP_MEM:  0x%016llx\n", pvt->top_mem);
23300ec449eeSDoug Thompson 
23310ec449eeSDoug Thompson 	/* check first whether TOP_MEM2 is enabled */
23320ec449eeSDoug Thompson 	rdmsrl(MSR_K8_SYSCFG, msr_val);
23330ec449eeSDoug Thompson 	if (msr_val & (1U << 21)) {
2334e97f8bb8SBorislav Petkov 		rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
2335956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
23360ec449eeSDoug Thompson 	} else
2337956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2 disabled\n");
23380ec449eeSDoug Thompson 
23395980bb9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap);
23400ec449eeSDoug Thompson 
23415a5d2371SBorislav Petkov 	read_dram_ctl_register(pvt);
23420ec449eeSDoug Thompson 
23437f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
23447f19bf75SBorislav Petkov 		u8 rw;
23450ec449eeSDoug Thompson 
23467f19bf75SBorislav Petkov 		/* read settings for this DRAM range */
23477f19bf75SBorislav Petkov 		read_dram_base_limit_regs(pvt, range);
2348e97f8bb8SBorislav Petkov 
23497f19bf75SBorislav Petkov 		rw = dram_rw(pvt, range);
23507f19bf75SBorislav Petkov 		if (!rw)
23517f19bf75SBorislav Petkov 			continue;
23527f19bf75SBorislav Petkov 
2353956b9ba1SJoe Perches 		edac_dbg(1, "  DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
23547f19bf75SBorislav Petkov 			 range,
23557f19bf75SBorislav Petkov 			 get_dram_base(pvt, range),
23567f19bf75SBorislav Petkov 			 get_dram_limit(pvt, range));
23577f19bf75SBorislav Petkov 
2358956b9ba1SJoe Perches 		edac_dbg(1, "   IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
23597f19bf75SBorislav Petkov 			 dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
23607f19bf75SBorislav Petkov 			 (rw & 0x1) ? "R" : "-",
23617f19bf75SBorislav Petkov 			 (rw & 0x2) ? "W" : "-",
23627f19bf75SBorislav Petkov 			 dram_intlv_sel(pvt, range),
23637f19bf75SBorislav Petkov 			 dram_dst_node(pvt, range));
23640ec449eeSDoug Thompson 	}
23650ec449eeSDoug Thompson 
2366b2b0c605SBorislav Petkov 	read_dct_base_mask(pvt);
23670ec449eeSDoug Thompson 
2368bc21fa57SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar);
23697981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DBAM0, &pvt->dbam0);
23700ec449eeSDoug Thompson 
23718d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare);
23720ec449eeSDoug Thompson 
23737981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DCLR0, &pvt->dclr0);
23747981a28fSAravind Gopalakrishnan 	amd64_read_dct_pci_cfg(pvt, 0, DCHR0, &pvt->dchr0);
23750ec449eeSDoug Thompson 
237678da121eSBorislav Petkov 	if (!dct_ganging_enabled(pvt)) {
23777981a28fSAravind Gopalakrishnan 		amd64_read_dct_pci_cfg(pvt, 1, DCLR0, &pvt->dclr1);
23787981a28fSAravind Gopalakrishnan 		amd64_read_dct_pci_cfg(pvt, 1, DCHR0, &pvt->dchr1);
23790ec449eeSDoug Thompson 	}
2380b2b0c605SBorislav Petkov 
2381a3b7db09SBorislav Petkov 	pvt->ecc_sym_sz = 4;
2382a597d2a5SAravind Gopalakrishnan 	determine_memory_type(pvt);
2383a597d2a5SAravind Gopalakrishnan 	edac_dbg(1, "  DIMM type: %s\n", edac_mem_types[pvt->dram_type]);
2384a3b7db09SBorislav Petkov 
2385a4b4bedcSBorislav Petkov 	if (pvt->fam >= 0x10) {
23868d5b5d9cSBorislav Petkov 		amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp);
23877981a28fSAravind Gopalakrishnan 		/* F16h has only DCT0, so no need to read dbam1 */
2388a4b4bedcSBorislav Petkov 		if (pvt->fam != 0x16)
23897981a28fSAravind Gopalakrishnan 			amd64_read_dct_pci_cfg(pvt, 1, DBAM0, &pvt->dbam1);
2390a3b7db09SBorislav Petkov 
2391a3b7db09SBorislav Petkov 		/* F10h, revD and later can do x8 ECC too */
2392a4b4bedcSBorislav Petkov 		if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25))
2393a3b7db09SBorislav Petkov 			pvt->ecc_sym_sz = 8;
2394525a1b20SBorislav Petkov 	}
2395b2b0c605SBorislav Petkov 	dump_misc_regs(pvt);
23960ec449eeSDoug Thompson }
23970ec449eeSDoug Thompson 
23980ec449eeSDoug Thompson /*
23990ec449eeSDoug Thompson  * NOTE: CPU Revision Dependent code
24000ec449eeSDoug Thompson  *
24010ec449eeSDoug Thompson  * Input:
240211c75eadSBorislav Petkov  *	@csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1)
24030ec449eeSDoug Thompson  *	k8 private pointer to -->
24040ec449eeSDoug Thompson  *			DRAM Bank Address mapping register
24050ec449eeSDoug Thompson  *			node_id
24060ec449eeSDoug Thompson  *			DCL register where dual_channel_active is
24070ec449eeSDoug Thompson  *
24080ec449eeSDoug Thompson  * The DBAM register consists of 4 sets of 4 bits each definitions:
24090ec449eeSDoug Thompson  *
24100ec449eeSDoug Thompson  * Bits:	CSROWs
24110ec449eeSDoug Thompson  * 0-3		CSROWs 0 and 1
24120ec449eeSDoug Thompson  * 4-7		CSROWs 2 and 3
24130ec449eeSDoug Thompson  * 8-11		CSROWs 4 and 5
24140ec449eeSDoug Thompson  * 12-15	CSROWs 6 and 7
24150ec449eeSDoug Thompson  *
24160ec449eeSDoug Thompson  * Values range from: 0 to 15
24170ec449eeSDoug Thompson  * The meaning of the values depends on CPU revision and dual-channel state,
24180ec449eeSDoug Thompson  * see relevant BKDG more info.
24190ec449eeSDoug Thompson  *
24200ec449eeSDoug Thompson  * The memory controller provides for total of only 8 CSROWs in its current
24210ec449eeSDoug Thompson  * architecture. Each "pair" of CSROWs normally represents just one DIMM in
24220ec449eeSDoug Thompson  * single channel or two (2) DIMMs in dual channel mode.
24230ec449eeSDoug Thompson  *
24240ec449eeSDoug Thompson  * The following code logic collapses the various tables for CSROW based on CPU
24250ec449eeSDoug Thompson  * revision.
24260ec449eeSDoug Thompson  *
24270ec449eeSDoug Thompson  * Returns:
24280ec449eeSDoug Thompson  *	The number of PAGE_SIZE pages on the specified CSROW number it
24290ec449eeSDoug Thompson  *	encompasses
24300ec449eeSDoug Thompson  *
24310ec449eeSDoug Thompson  */
2432d1ea71cdSBorislav Petkov static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr)
24330ec449eeSDoug Thompson {
24341433eb99SBorislav Petkov 	u32 cs_mode, nr_pages;
2435f92cae45SAshish Shenoy 	u32 dbam = dct ? pvt->dbam1 : pvt->dbam0;
24360ec449eeSDoug Thompson 
243710de6497SBorislav Petkov 
24380ec449eeSDoug Thompson 	/*
24390ec449eeSDoug Thompson 	 * The math on this doesn't look right on the surface because x/2*4 can
24400ec449eeSDoug Thompson 	 * be simplified to x*2 but this expression makes use of the fact that
24410ec449eeSDoug Thompson 	 * it is integral math where 1/2=0. This intermediate value becomes the
24420ec449eeSDoug Thompson 	 * number of bits to shift the DBAM register to extract the proper CSROW
24430ec449eeSDoug Thompson 	 * field.
24440ec449eeSDoug Thompson 	 */
24450a5dfc31SBorislav Petkov 	cs_mode = DBAM_DIMM(csrow_nr / 2, dbam);
24460ec449eeSDoug Thompson 
2447a597d2a5SAravind Gopalakrishnan 	nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, (csrow_nr / 2))
2448a597d2a5SAravind Gopalakrishnan 							   << (20 - PAGE_SHIFT);
24490ec449eeSDoug Thompson 
245010de6497SBorislav Petkov 	edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n",
245110de6497SBorislav Petkov 		    csrow_nr, dct,  cs_mode);
245210de6497SBorislav Petkov 	edac_dbg(0, "nr_pages/channel: %u\n", nr_pages);
24530ec449eeSDoug Thompson 
24540ec449eeSDoug Thompson 	return nr_pages;
24550ec449eeSDoug Thompson }
24560ec449eeSDoug Thompson 
24570ec449eeSDoug Thompson /*
24580ec449eeSDoug Thompson  * Initialize the array of csrow attribute instances, based on the values
24590ec449eeSDoug Thompson  * from pci config hardware registers.
24600ec449eeSDoug Thompson  */
2461360b7f3cSBorislav Petkov static int init_csrows(struct mem_ctl_info *mci)
24620ec449eeSDoug Thompson {
246310de6497SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
24640ec449eeSDoug Thompson 	struct csrow_info *csrow;
2465de3910ebSMauro Carvalho Chehab 	struct dimm_info *dimm;
2466084a4fccSMauro Carvalho Chehab 	enum edac_type edac_mode;
246710de6497SBorislav Petkov 	int i, j, empty = 1;
2468a895bf8bSMauro Carvalho Chehab 	int nr_pages = 0;
246910de6497SBorislav Petkov 	u32 val;
24700ec449eeSDoug Thompson 
2471a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
24720ec449eeSDoug Thompson 
24732299ef71SBorislav Petkov 	pvt->nbcfg = val;
24740ec449eeSDoug Thompson 
2475956b9ba1SJoe Perches 	edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
24762299ef71SBorislav Petkov 		 pvt->mc_node_id, val,
2477a97fa68eSBorislav Petkov 		 !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
24780ec449eeSDoug Thompson 
247910de6497SBorislav Petkov 	/*
248010de6497SBorislav Petkov 	 * We iterate over DCT0 here but we look at DCT1 in parallel, if needed.
248110de6497SBorislav Petkov 	 */
248211c75eadSBorislav Petkov 	for_each_chip_select(i, 0, pvt) {
248310de6497SBorislav Petkov 		bool row_dct0 = !!csrow_enabled(i, 0, pvt);
248410de6497SBorislav Petkov 		bool row_dct1 = false;
24850ec449eeSDoug Thompson 
2486a4b4bedcSBorislav Petkov 		if (pvt->fam != 0xf)
248710de6497SBorislav Petkov 			row_dct1 = !!csrow_enabled(i, 1, pvt);
248810de6497SBorislav Petkov 
248910de6497SBorislav Petkov 		if (!row_dct0 && !row_dct1)
24900ec449eeSDoug Thompson 			continue;
24910ec449eeSDoug Thompson 
249210de6497SBorislav Petkov 		csrow = mci->csrows[i];
24930ec449eeSDoug Thompson 		empty = 0;
249411c75eadSBorislav Petkov 
249510de6497SBorislav Petkov 		edac_dbg(1, "MC node: %d, csrow: %d\n",
249610de6497SBorislav Petkov 			    pvt->mc_node_id, i);
249710de6497SBorislav Petkov 
24981eef1282SMauro Carvalho Chehab 		if (row_dct0) {
2499d1ea71cdSBorislav Petkov 			nr_pages = get_csrow_nr_pages(pvt, 0, i);
25001eef1282SMauro Carvalho Chehab 			csrow->channels[0]->dimm->nr_pages = nr_pages;
25011eef1282SMauro Carvalho Chehab 		}
250210de6497SBorislav Petkov 
250310de6497SBorislav Petkov 		/* K8 has only one DCT */
2504a4b4bedcSBorislav Petkov 		if (pvt->fam != 0xf && row_dct1) {
2505d1ea71cdSBorislav Petkov 			int row_dct1_pages = get_csrow_nr_pages(pvt, 1, i);
25061eef1282SMauro Carvalho Chehab 
25071eef1282SMauro Carvalho Chehab 			csrow->channels[1]->dimm->nr_pages = row_dct1_pages;
25081eef1282SMauro Carvalho Chehab 			nr_pages += row_dct1_pages;
25091eef1282SMauro Carvalho Chehab 		}
25100ec449eeSDoug Thompson 
251110de6497SBorislav Petkov 		edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages);
25120ec449eeSDoug Thompson 
25130ec449eeSDoug Thompson 		/*
25140ec449eeSDoug Thompson 		 * determine whether CHIPKILL or JUST ECC or NO ECC is operating
25150ec449eeSDoug Thompson 		 */
2516a97fa68eSBorislav Petkov 		if (pvt->nbcfg & NBCFG_ECC_ENABLE)
2517084a4fccSMauro Carvalho Chehab 			edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) ?
25180ec449eeSDoug Thompson 				    EDAC_S4ECD4ED : EDAC_SECDED;
25190ec449eeSDoug Thompson 		else
2520084a4fccSMauro Carvalho Chehab 			edac_mode = EDAC_NONE;
2521084a4fccSMauro Carvalho Chehab 
2522084a4fccSMauro Carvalho Chehab 		for (j = 0; j < pvt->channel_count; j++) {
2523de3910ebSMauro Carvalho Chehab 			dimm = csrow->channels[j]->dimm;
2524a597d2a5SAravind Gopalakrishnan 			dimm->mtype = pvt->dram_type;
2525de3910ebSMauro Carvalho Chehab 			dimm->edac_mode = edac_mode;
2526084a4fccSMauro Carvalho Chehab 		}
25270ec449eeSDoug Thompson 	}
25280ec449eeSDoug Thompson 
25290ec449eeSDoug Thompson 	return empty;
25300ec449eeSDoug Thompson }
2531d27bf6faSDoug Thompson 
253206724535SBorislav Petkov /* get all cores on this DCT */
25338b84c8dfSDaniel J Blueman static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, u16 nid)
2534f9431992SDoug Thompson {
253506724535SBorislav Petkov 	int cpu;
2536f9431992SDoug Thompson 
253706724535SBorislav Petkov 	for_each_online_cpu(cpu)
253806724535SBorislav Petkov 		if (amd_get_nb_id(cpu) == nid)
253906724535SBorislav Petkov 			cpumask_set_cpu(cpu, mask);
2540f9431992SDoug Thompson }
2541f9431992SDoug Thompson 
2542f9431992SDoug Thompson /* check MCG_CTL on all the cpus on this node */
2543d1ea71cdSBorislav Petkov static bool nb_mce_bank_enabled_on_node(u16 nid)
2544f9431992SDoug Thompson {
2545ba578cb3SRusty Russell 	cpumask_var_t mask;
254650542251SBorislav Petkov 	int cpu, nbe;
254706724535SBorislav Petkov 	bool ret = false;
2548f9431992SDoug Thompson 
2549ba578cb3SRusty Russell 	if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
255024f9a7feSBorislav Petkov 		amd64_warn("%s: Error allocating mask\n", __func__);
255106724535SBorislav Petkov 		return false;
255206724535SBorislav Petkov 	}
255306724535SBorislav Petkov 
2554ba578cb3SRusty Russell 	get_cpus_on_this_dct_cpumask(mask, nid);
255506724535SBorislav Petkov 
2556ba578cb3SRusty Russell 	rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
2557ba578cb3SRusty Russell 
2558ba578cb3SRusty Russell 	for_each_cpu(cpu, mask) {
255950542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
25605980bb9cSBorislav Petkov 		nbe = reg->l & MSR_MCGCTL_NBE;
256106724535SBorislav Petkov 
2562956b9ba1SJoe Perches 		edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
256350542251SBorislav Petkov 			 cpu, reg->q,
256406724535SBorislav Petkov 			 (nbe ? "enabled" : "disabled"));
256506724535SBorislav Petkov 
256606724535SBorislav Petkov 		if (!nbe)
256706724535SBorislav Petkov 			goto out;
256806724535SBorislav Petkov 	}
256906724535SBorislav Petkov 	ret = true;
257006724535SBorislav Petkov 
257106724535SBorislav Petkov out:
2572ba578cb3SRusty Russell 	free_cpumask_var(mask);
2573f9431992SDoug Thompson 	return ret;
2574f9431992SDoug Thompson }
2575f9431992SDoug Thompson 
2576c7e5301aSDaniel J Blueman static int toggle_ecc_err_reporting(struct ecc_settings *s, u16 nid, bool on)
2577f6d6ae96SBorislav Petkov {
2578f6d6ae96SBorislav Petkov 	cpumask_var_t cmask;
257950542251SBorislav Petkov 	int cpu;
2580f6d6ae96SBorislav Petkov 
2581f6d6ae96SBorislav Petkov 	if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
258224f9a7feSBorislav Petkov 		amd64_warn("%s: error allocating mask\n", __func__);
2583f6d6ae96SBorislav Petkov 		return false;
2584f6d6ae96SBorislav Petkov 	}
2585f6d6ae96SBorislav Petkov 
2586ae7bb7c6SBorislav Petkov 	get_cpus_on_this_dct_cpumask(cmask, nid);
2587f6d6ae96SBorislav Petkov 
2588f6d6ae96SBorislav Petkov 	rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2589f6d6ae96SBorislav Petkov 
2590f6d6ae96SBorislav Petkov 	for_each_cpu(cpu, cmask) {
2591f6d6ae96SBorislav Petkov 
259250542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
259350542251SBorislav Petkov 
2594f6d6ae96SBorislav Petkov 		if (on) {
25955980bb9cSBorislav Petkov 			if (reg->l & MSR_MCGCTL_NBE)
2596ae7bb7c6SBorislav Petkov 				s->flags.nb_mce_enable = 1;
2597f6d6ae96SBorislav Petkov 
25985980bb9cSBorislav Petkov 			reg->l |= MSR_MCGCTL_NBE;
2599f6d6ae96SBorislav Petkov 		} else {
2600f6d6ae96SBorislav Petkov 			/*
2601d95cf4deSBorislav Petkov 			 * Turn off NB MCE reporting only when it was off before
2602f6d6ae96SBorislav Petkov 			 */
2603ae7bb7c6SBorislav Petkov 			if (!s->flags.nb_mce_enable)
26045980bb9cSBorislav Petkov 				reg->l &= ~MSR_MCGCTL_NBE;
2605f6d6ae96SBorislav Petkov 		}
2606f6d6ae96SBorislav Petkov 	}
2607f6d6ae96SBorislav Petkov 	wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2608f6d6ae96SBorislav Petkov 
2609f6d6ae96SBorislav Petkov 	free_cpumask_var(cmask);
2610f6d6ae96SBorislav Petkov 
2611f6d6ae96SBorislav Petkov 	return 0;
2612f6d6ae96SBorislav Petkov }
2613f6d6ae96SBorislav Petkov 
2614c7e5301aSDaniel J Blueman static bool enable_ecc_error_reporting(struct ecc_settings *s, u16 nid,
26152299ef71SBorislav Petkov 				       struct pci_dev *F3)
2616f6d6ae96SBorislav Petkov {
26172299ef71SBorislav Petkov 	bool ret = true;
2618c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
2619f6d6ae96SBorislav Petkov 
26202299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, ON)) {
26212299ef71SBorislav Petkov 		amd64_warn("Error enabling ECC reporting over MCGCTL!\n");
26222299ef71SBorislav Petkov 		return false;
26232299ef71SBorislav Petkov 	}
26242299ef71SBorislav Petkov 
2625c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
2626f6d6ae96SBorislav Petkov 
2627ae7bb7c6SBorislav Petkov 	s->old_nbctl   = value & mask;
2628ae7bb7c6SBorislav Petkov 	s->nbctl_valid = true;
2629f6d6ae96SBorislav Petkov 
2630f6d6ae96SBorislav Petkov 	value |= mask;
2631c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
2632f6d6ae96SBorislav Petkov 
2633a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCFG, &value);
2634f6d6ae96SBorislav Petkov 
2635956b9ba1SJoe Perches 	edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2636a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
2637f6d6ae96SBorislav Petkov 
2638a97fa68eSBorislav Petkov 	if (!(value & NBCFG_ECC_ENABLE)) {
263924f9a7feSBorislav Petkov 		amd64_warn("DRAM ECC disabled on this node, enabling...\n");
2640f6d6ae96SBorislav Petkov 
2641ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 0;
2642d95cf4deSBorislav Petkov 
2643f6d6ae96SBorislav Petkov 		/* Attempt to turn on DRAM ECC Enable */
2644a97fa68eSBorislav Petkov 		value |= NBCFG_ECC_ENABLE;
2645a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
2646f6d6ae96SBorislav Petkov 
2647a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
2648f6d6ae96SBorislav Petkov 
2649a97fa68eSBorislav Petkov 		if (!(value & NBCFG_ECC_ENABLE)) {
265024f9a7feSBorislav Petkov 			amd64_warn("Hardware rejected DRAM ECC enable,"
265124f9a7feSBorislav Petkov 				   "check memory DIMM configuration.\n");
26522299ef71SBorislav Petkov 			ret = false;
2653f6d6ae96SBorislav Petkov 		} else {
265424f9a7feSBorislav Petkov 			amd64_info("Hardware accepted DRAM ECC Enable\n");
2655f6d6ae96SBorislav Petkov 		}
2656d95cf4deSBorislav Petkov 	} else {
2657ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 1;
2658f6d6ae96SBorislav Petkov 	}
2659d95cf4deSBorislav Petkov 
2660956b9ba1SJoe Perches 	edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2661a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
2662f6d6ae96SBorislav Petkov 
26632299ef71SBorislav Petkov 	return ret;
2664f6d6ae96SBorislav Petkov }
2665f6d6ae96SBorislav Petkov 
2666c7e5301aSDaniel J Blueman static void restore_ecc_error_reporting(struct ecc_settings *s, u16 nid,
2667360b7f3cSBorislav Petkov 					struct pci_dev *F3)
2668f6d6ae96SBorislav Petkov {
2669c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
2670c9f4f26eSBorislav Petkov 
2671ae7bb7c6SBorislav Petkov 	if (!s->nbctl_valid)
2672f6d6ae96SBorislav Petkov 		return;
2673f6d6ae96SBorislav Petkov 
2674c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
2675f6d6ae96SBorislav Petkov 	value &= ~mask;
2676ae7bb7c6SBorislav Petkov 	value |= s->old_nbctl;
2677f6d6ae96SBorislav Petkov 
2678c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
2679f6d6ae96SBorislav Petkov 
2680ae7bb7c6SBorislav Petkov 	/* restore previous BIOS DRAM ECC "off" setting we force-enabled */
2681ae7bb7c6SBorislav Petkov 	if (!s->flags.nb_ecc_prev) {
2682a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
2683a97fa68eSBorislav Petkov 		value &= ~NBCFG_ECC_ENABLE;
2684a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
2685d95cf4deSBorislav Petkov 	}
2686d95cf4deSBorislav Petkov 
2687d95cf4deSBorislav Petkov 	/* restore the NB Enable MCGCTL bit */
26882299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, OFF))
268924f9a7feSBorislav Petkov 		amd64_warn("Error restoring NB MCGCTL settings!\n");
2690f6d6ae96SBorislav Petkov }
2691f6d6ae96SBorislav Petkov 
2692f9431992SDoug Thompson /*
26932299ef71SBorislav Petkov  * EDAC requires that the BIOS have ECC enabled before
26942299ef71SBorislav Petkov  * taking over the processing of ECC errors. A command line
26952299ef71SBorislav Petkov  * option allows to force-enable hardware ECC later in
26962299ef71SBorislav Petkov  * enable_ecc_error_reporting().
2697f9431992SDoug Thompson  */
2698cab4d277SBorislav Petkov static const char *ecc_msg =
2699cab4d277SBorislav Petkov 	"ECC disabled in the BIOS or no ECC capability, module will not load.\n"
2700cab4d277SBorislav Petkov 	" Either enable ECC checking or force module loading by setting "
2701cab4d277SBorislav Petkov 	"'ecc_enable_override'.\n"
2702cab4d277SBorislav Petkov 	" (Note that use of the override may cause unknown side effects.)\n";
2703be3468e8SBorislav Petkov 
2704c7e5301aSDaniel J Blueman static bool ecc_enabled(struct pci_dev *F3, u16 nid)
2705f9431992SDoug Thompson {
270606724535SBorislav Petkov 	bool nb_mce_en = false;
2707196b79fcSYazen Ghannam 	u8 ecc_en = 0, i;
2708196b79fcSYazen Ghannam 	u32 value;
2709f9431992SDoug Thompson 
2710196b79fcSYazen Ghannam 	if (boot_cpu_data.x86 >= 0x17) {
2711196b79fcSYazen Ghannam 		u8 umc_en_mask = 0, ecc_en_mask = 0;
2712196b79fcSYazen Ghannam 
2713196b79fcSYazen Ghannam 		for (i = 0; i < NUM_UMCS; i++) {
2714196b79fcSYazen Ghannam 			u32 base = get_umc_base(i);
2715196b79fcSYazen Ghannam 
2716196b79fcSYazen Ghannam 			/* Only check enabled UMCs. */
2717196b79fcSYazen Ghannam 			if (amd_smn_read(nid, base + UMCCH_SDP_CTRL, &value))
2718196b79fcSYazen Ghannam 				continue;
2719196b79fcSYazen Ghannam 
2720196b79fcSYazen Ghannam 			if (!(value & UMC_SDP_INIT))
2721196b79fcSYazen Ghannam 				continue;
2722196b79fcSYazen Ghannam 
2723196b79fcSYazen Ghannam 			umc_en_mask |= BIT(i);
2724196b79fcSYazen Ghannam 
2725196b79fcSYazen Ghannam 			if (amd_smn_read(nid, base + UMCCH_UMC_CAP_HI, &value))
2726196b79fcSYazen Ghannam 				continue;
2727196b79fcSYazen Ghannam 
2728196b79fcSYazen Ghannam 			if (value & UMC_ECC_ENABLED)
2729196b79fcSYazen Ghannam 				ecc_en_mask |= BIT(i);
2730196b79fcSYazen Ghannam 		}
2731196b79fcSYazen Ghannam 
2732196b79fcSYazen Ghannam 		/* Check whether at least one UMC is enabled: */
2733196b79fcSYazen Ghannam 		if (umc_en_mask)
2734196b79fcSYazen Ghannam 			ecc_en = umc_en_mask == ecc_en_mask;
2735196b79fcSYazen Ghannam 
2736196b79fcSYazen Ghannam 		/* Assume UMC MCA banks are enabled. */
2737196b79fcSYazen Ghannam 		nb_mce_en = true;
2738196b79fcSYazen Ghannam 	} else {
2739a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
2740f9431992SDoug Thompson 
2741a97fa68eSBorislav Petkov 		ecc_en = !!(value & NBCFG_ECC_ENABLE);
2742be3468e8SBorislav Petkov 
2743d1ea71cdSBorislav Petkov 		nb_mce_en = nb_mce_bank_enabled_on_node(nid);
274406724535SBorislav Petkov 		if (!nb_mce_en)
2745196b79fcSYazen Ghannam 			amd64_notice("NB MCE bank disabled, set MSR 0x%08x[4] on node %d to enable.\n",
27462299ef71SBorislav Petkov 				     MSR_IA32_MCG_CTL, nid);
2747196b79fcSYazen Ghannam 	}
2748196b79fcSYazen Ghannam 
2749196b79fcSYazen Ghannam 	amd64_info("DRAM ECC %s.\n", (ecc_en ? "enabled" : "disabled"));
2750be3468e8SBorislav Petkov 
27512299ef71SBorislav Petkov 	if (!ecc_en || !nb_mce_en) {
275224f9a7feSBorislav Petkov 		amd64_notice("%s", ecc_msg);
27532299ef71SBorislav Petkov 		return false;
2754be3468e8SBorislav Petkov 	}
27552299ef71SBorislav Petkov 	return true;
2756f9431992SDoug Thompson }
2757f9431992SDoug Thompson 
2758df71a053SBorislav Petkov static void setup_mci_misc_attrs(struct mem_ctl_info *mci,
2759df71a053SBorislav Petkov 				 struct amd64_family_type *fam)
27607d6034d3SDoug Thompson {
27617d6034d3SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
27627d6034d3SDoug Thompson 
27637d6034d3SDoug Thompson 	mci->mtype_cap		= MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
27647d6034d3SDoug Thompson 	mci->edac_ctl_cap	= EDAC_FLAG_NONE;
27657d6034d3SDoug Thompson 
27665980bb9cSBorislav Petkov 	if (pvt->nbcap & NBCAP_SECDED)
27677d6034d3SDoug Thompson 		mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
27687d6034d3SDoug Thompson 
27695980bb9cSBorislav Petkov 	if (pvt->nbcap & NBCAP_CHIPKILL)
27707d6034d3SDoug Thompson 		mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
27717d6034d3SDoug Thompson 
2772d1ea71cdSBorislav Petkov 	mci->edac_cap		= determine_edac_cap(pvt);
27737d6034d3SDoug Thompson 	mci->mod_name		= EDAC_MOD_STR;
27747d6034d3SDoug Thompson 	mci->mod_ver		= EDAC_AMD64_VERSION;
2775df71a053SBorislav Petkov 	mci->ctl_name		= fam->ctl_name;
2776e7934b70SYazen Ghannam 	mci->dev_name		= pci_name(pvt->F3);
27777d6034d3SDoug Thompson 	mci->ctl_page_to_phys	= NULL;
27787d6034d3SDoug Thompson 
27797d6034d3SDoug Thompson 	/* memory scrubber interface */
2780d1ea71cdSBorislav Petkov 	mci->set_sdram_scrub_rate = set_scrub_rate;
2781d1ea71cdSBorislav Petkov 	mci->get_sdram_scrub_rate = get_scrub_rate;
27827d6034d3SDoug Thompson }
27837d6034d3SDoug Thompson 
27840092b20dSBorislav Petkov /*
27850092b20dSBorislav Petkov  * returns a pointer to the family descriptor on success, NULL otherwise.
27860092b20dSBorislav Petkov  */
2787d1ea71cdSBorislav Petkov static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt)
2788395ae783SBorislav Petkov {
27890092b20dSBorislav Petkov 	struct amd64_family_type *fam_type = NULL;
27900092b20dSBorislav Petkov 
279118b94f66SAravind Gopalakrishnan 	pvt->ext_model  = boot_cpu_data.x86_model >> 4;
2792a4b4bedcSBorislav Petkov 	pvt->stepping	= boot_cpu_data.x86_mask;
279318b94f66SAravind Gopalakrishnan 	pvt->model	= boot_cpu_data.x86_model;
279418b94f66SAravind Gopalakrishnan 	pvt->fam	= boot_cpu_data.x86;
279518b94f66SAravind Gopalakrishnan 
279618b94f66SAravind Gopalakrishnan 	switch (pvt->fam) {
2797395ae783SBorislav Petkov 	case 0xf:
2798d1ea71cdSBorislav Petkov 		fam_type	= &family_types[K8_CPUS];
2799d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[K8_CPUS].ops;
2800395ae783SBorislav Petkov 		break;
2801df71a053SBorislav Petkov 
2802395ae783SBorislav Petkov 	case 0x10:
2803d1ea71cdSBorislav Petkov 		fam_type	= &family_types[F10_CPUS];
2804d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[F10_CPUS].ops;
2805df71a053SBorislav Petkov 		break;
2806df71a053SBorislav Petkov 
2807df71a053SBorislav Petkov 	case 0x15:
280818b94f66SAravind Gopalakrishnan 		if (pvt->model == 0x30) {
2809d1ea71cdSBorislav Petkov 			fam_type = &family_types[F15_M30H_CPUS];
2810d1ea71cdSBorislav Petkov 			pvt->ops = &family_types[F15_M30H_CPUS].ops;
281118b94f66SAravind Gopalakrishnan 			break;
2812a597d2a5SAravind Gopalakrishnan 		} else if (pvt->model == 0x60) {
2813a597d2a5SAravind Gopalakrishnan 			fam_type = &family_types[F15_M60H_CPUS];
2814a597d2a5SAravind Gopalakrishnan 			pvt->ops = &family_types[F15_M60H_CPUS].ops;
2815a597d2a5SAravind Gopalakrishnan 			break;
281618b94f66SAravind Gopalakrishnan 		}
281718b94f66SAravind Gopalakrishnan 
2818d1ea71cdSBorislav Petkov 		fam_type	= &family_types[F15_CPUS];
2819d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[F15_CPUS].ops;
2820395ae783SBorislav Petkov 		break;
2821395ae783SBorislav Petkov 
282294c1acf2SAravind Gopalakrishnan 	case 0x16:
282385a8885bSAravind Gopalakrishnan 		if (pvt->model == 0x30) {
282485a8885bSAravind Gopalakrishnan 			fam_type = &family_types[F16_M30H_CPUS];
282585a8885bSAravind Gopalakrishnan 			pvt->ops = &family_types[F16_M30H_CPUS].ops;
282685a8885bSAravind Gopalakrishnan 			break;
282785a8885bSAravind Gopalakrishnan 		}
2828d1ea71cdSBorislav Petkov 		fam_type	= &family_types[F16_CPUS];
2829d1ea71cdSBorislav Petkov 		pvt->ops	= &family_types[F16_CPUS].ops;
283094c1acf2SAravind Gopalakrishnan 		break;
283194c1acf2SAravind Gopalakrishnan 
2832f1cbbec9SYazen Ghannam 	case 0x17:
2833f1cbbec9SYazen Ghannam 		fam_type	= &family_types[F17_CPUS];
2834f1cbbec9SYazen Ghannam 		pvt->ops	= &family_types[F17_CPUS].ops;
2835f1cbbec9SYazen Ghannam 		break;
2836f1cbbec9SYazen Ghannam 
2837395ae783SBorislav Petkov 	default:
283824f9a7feSBorislav Petkov 		amd64_err("Unsupported family!\n");
28390092b20dSBorislav Petkov 		return NULL;
2840395ae783SBorislav Petkov 	}
28410092b20dSBorislav Petkov 
2842df71a053SBorislav Petkov 	amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name,
284318b94f66SAravind Gopalakrishnan 		     (pvt->fam == 0xf ?
28440092b20dSBorislav Petkov 				(pvt->ext_model >= K8_REV_F  ? "revF or later "
28450092b20dSBorislav Petkov 							     : "revE or earlier ")
284624f9a7feSBorislav Petkov 				 : ""), pvt->mc_node_id);
28470092b20dSBorislav Petkov 	return fam_type;
2848395ae783SBorislav Petkov }
2849395ae783SBorislav Petkov 
2850e339f1ecSTakashi Iwai static const struct attribute_group *amd64_edac_attr_groups[] = {
2851e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_DEBUG
2852e339f1ecSTakashi Iwai 	&amd64_edac_dbg_group,
2853e339f1ecSTakashi Iwai #endif
2854e339f1ecSTakashi Iwai #ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION
2855e339f1ecSTakashi Iwai 	&amd64_edac_inj_group,
2856e339f1ecSTakashi Iwai #endif
2857e339f1ecSTakashi Iwai 	NULL
2858e339f1ecSTakashi Iwai };
2859e339f1ecSTakashi Iwai 
28603f37a36bSBorislav Petkov static int init_one_instance(unsigned int nid)
28617d6034d3SDoug Thompson {
28623f37a36bSBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
28630092b20dSBorislav Petkov 	struct amd64_family_type *fam_type = NULL;
2864360b7f3cSBorislav Petkov 	struct mem_ctl_info *mci = NULL;
2865ab5a503cSMauro Carvalho Chehab 	struct edac_mc_layer layers[2];
28663f37a36bSBorislav Petkov 	struct amd64_pvt *pvt = NULL;
28677d6034d3SDoug Thompson 	int err = 0, ret;
28687d6034d3SDoug Thompson 
28697d6034d3SDoug Thompson 	ret = -ENOMEM;
28707d6034d3SDoug Thompson 	pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
28717d6034d3SDoug Thompson 	if (!pvt)
2872360b7f3cSBorislav Petkov 		goto err_ret;
28737d6034d3SDoug Thompson 
2874360b7f3cSBorislav Petkov 	pvt->mc_node_id	= nid;
28753f37a36bSBorislav Petkov 	pvt->F3 = F3;
28767d6034d3SDoug Thompson 
2877395ae783SBorislav Petkov 	ret = -EINVAL;
2878d1ea71cdSBorislav Petkov 	fam_type = per_family_init(pvt);
28790092b20dSBorislav Petkov 	if (!fam_type)
2880395ae783SBorislav Petkov 		goto err_free;
2881395ae783SBorislav Petkov 
28827d6034d3SDoug Thompson 	ret = -ENODEV;
28833f37a36bSBorislav Petkov 	err = reserve_mc_sibling_devs(pvt, fam_type->f1_id, fam_type->f2_id);
28847d6034d3SDoug Thompson 	if (err)
28857d6034d3SDoug Thompson 		goto err_free;
28867d6034d3SDoug Thompson 
2887360b7f3cSBorislav Petkov 	read_mc_regs(pvt);
28887d6034d3SDoug Thompson 
28897d6034d3SDoug Thompson 	/*
28907d6034d3SDoug Thompson 	 * We need to determine how many memory channels there are. Then use
28917d6034d3SDoug Thompson 	 * that information for calculating the size of the dynamic instance
2892360b7f3cSBorislav Petkov 	 * tables in the 'mci' structure.
28937d6034d3SDoug Thompson 	 */
2894360b7f3cSBorislav Petkov 	ret = -EINVAL;
28957d6034d3SDoug Thompson 	pvt->channel_count = pvt->ops->early_channel_count(pvt);
28967d6034d3SDoug Thompson 	if (pvt->channel_count < 0)
2897360b7f3cSBorislav Petkov 		goto err_siblings;
28987d6034d3SDoug Thompson 
28997d6034d3SDoug Thompson 	ret = -ENOMEM;
2900ab5a503cSMauro Carvalho Chehab 	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
2901ab5a503cSMauro Carvalho Chehab 	layers[0].size = pvt->csels[0].b_cnt;
2902ab5a503cSMauro Carvalho Chehab 	layers[0].is_virt_csrow = true;
2903ab5a503cSMauro Carvalho Chehab 	layers[1].type = EDAC_MC_LAYER_CHANNEL;
2904f0a56c48SBorislav Petkov 
2905f0a56c48SBorislav Petkov 	/*
2906f0a56c48SBorislav Petkov 	 * Always allocate two channels since we can have setups with DIMMs on
2907f0a56c48SBorislav Petkov 	 * only one channel. Also, this simplifies handling later for the price
2908f0a56c48SBorislav Petkov 	 * of a couple of KBs tops.
2909f0a56c48SBorislav Petkov 	 */
2910f0a56c48SBorislav Petkov 	layers[1].size = 2;
2911ab5a503cSMauro Carvalho Chehab 	layers[1].is_virt_csrow = false;
2912f0a56c48SBorislav Petkov 
2913ca0907b9SMauro Carvalho Chehab 	mci = edac_mc_alloc(nid, ARRAY_SIZE(layers), layers, 0);
29147d6034d3SDoug Thompson 	if (!mci)
2915360b7f3cSBorislav Petkov 		goto err_siblings;
29167d6034d3SDoug Thompson 
29177d6034d3SDoug Thompson 	mci->pvt_info = pvt;
29183f37a36bSBorislav Petkov 	mci->pdev = &pvt->F3->dev;
29197d6034d3SDoug Thompson 
2920df71a053SBorislav Petkov 	setup_mci_misc_attrs(mci, fam_type);
2921360b7f3cSBorislav Petkov 
2922360b7f3cSBorislav Petkov 	if (init_csrows(mci))
29237d6034d3SDoug Thompson 		mci->edac_cap = EDAC_FLAG_NONE;
29247d6034d3SDoug Thompson 
29257d6034d3SDoug Thompson 	ret = -ENODEV;
2926e339f1ecSTakashi Iwai 	if (edac_mc_add_mc_with_groups(mci, amd64_edac_attr_groups)) {
2927956b9ba1SJoe Perches 		edac_dbg(1, "failed edac_mc_add_mc()\n");
29287d6034d3SDoug Thompson 		goto err_add_mc;
29297d6034d3SDoug Thompson 	}
29307d6034d3SDoug Thompson 
2931549d042dSBorislav Petkov 	/* register stuff with EDAC MCE */
2932549d042dSBorislav Petkov 	if (report_gart_errors)
2933549d042dSBorislav Petkov 		amd_report_gart_errors(true);
2934549d042dSBorislav Petkov 
2935df781d03SBorislav Petkov 	amd_register_ecc_decoder(decode_bus_error);
2936549d042dSBorislav Petkov 
29377d6034d3SDoug Thompson 	return 0;
29387d6034d3SDoug Thompson 
29397d6034d3SDoug Thompson err_add_mc:
29407d6034d3SDoug Thompson 	edac_mc_free(mci);
29417d6034d3SDoug Thompson 
2942360b7f3cSBorislav Petkov err_siblings:
2943360b7f3cSBorislav Petkov 	free_mc_sibling_devs(pvt);
29447d6034d3SDoug Thompson 
2945360b7f3cSBorislav Petkov err_free:
2946360b7f3cSBorislav Petkov 	kfree(pvt);
29477d6034d3SDoug Thompson 
2948360b7f3cSBorislav Petkov err_ret:
29497d6034d3SDoug Thompson 	return ret;
29507d6034d3SDoug Thompson }
29517d6034d3SDoug Thompson 
29523f37a36bSBorislav Petkov static int probe_one_instance(unsigned int nid)
29537d6034d3SDoug Thompson {
29542299ef71SBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
2955ae7bb7c6SBorislav Petkov 	struct ecc_settings *s;
29563f37a36bSBorislav Petkov 	int ret;
2957b8cfa02fSBorislav Petkov 
2958ae7bb7c6SBorislav Petkov 	ret = -ENOMEM;
2959ae7bb7c6SBorislav Petkov 	s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL);
2960ae7bb7c6SBorislav Petkov 	if (!s)
29612299ef71SBorislav Petkov 		goto err_out;
2962ae7bb7c6SBorislav Petkov 
2963ae7bb7c6SBorislav Petkov 	ecc_stngs[nid] = s;
2964ae7bb7c6SBorislav Petkov 
29652299ef71SBorislav Petkov 	if (!ecc_enabled(F3, nid)) {
29662299ef71SBorislav Petkov 		ret = -ENODEV;
29672299ef71SBorislav Petkov 
29682299ef71SBorislav Petkov 		if (!ecc_enable_override)
29692299ef71SBorislav Petkov 			goto err_enable;
29702299ef71SBorislav Petkov 
2971044e7a41SYazen Ghannam 		if (boot_cpu_data.x86 >= 0x17) {
2972044e7a41SYazen Ghannam 			amd64_warn("Forcing ECC on is not recommended on newer systems. Please enable ECC in BIOS.");
2973044e7a41SYazen Ghannam 			goto err_enable;
2974044e7a41SYazen Ghannam 		} else
29752299ef71SBorislav Petkov 			amd64_warn("Forcing ECC on!\n");
29762299ef71SBorislav Petkov 
29772299ef71SBorislav Petkov 		if (!enable_ecc_error_reporting(s, nid, F3))
29782299ef71SBorislav Petkov 			goto err_enable;
29792299ef71SBorislav Petkov 	}
29802299ef71SBorislav Petkov 
29813f37a36bSBorislav Petkov 	ret = init_one_instance(nid);
2982360b7f3cSBorislav Petkov 	if (ret < 0) {
2983ae7bb7c6SBorislav Petkov 		amd64_err("Error probing instance: %d\n", nid);
2984044e7a41SYazen Ghannam 
2985044e7a41SYazen Ghannam 		if (boot_cpu_data.x86 < 0x17)
2986360b7f3cSBorislav Petkov 			restore_ecc_error_reporting(s, nid, F3);
2987360b7f3cSBorislav Petkov 	}
29887d6034d3SDoug Thompson 
29897d6034d3SDoug Thompson 	return ret;
29902299ef71SBorislav Petkov 
29912299ef71SBorislav Petkov err_enable:
29922299ef71SBorislav Petkov 	kfree(s);
29932299ef71SBorislav Petkov 	ecc_stngs[nid] = NULL;
29942299ef71SBorislav Petkov 
29952299ef71SBorislav Petkov err_out:
29962299ef71SBorislav Petkov 	return ret;
29977d6034d3SDoug Thompson }
29987d6034d3SDoug Thompson 
29993f37a36bSBorislav Petkov static void remove_one_instance(unsigned int nid)
30007d6034d3SDoug Thompson {
3001360b7f3cSBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
3002360b7f3cSBorislav Petkov 	struct ecc_settings *s = ecc_stngs[nid];
30033f37a36bSBorislav Petkov 	struct mem_ctl_info *mci;
30043f37a36bSBorislav Petkov 	struct amd64_pvt *pvt;
30057d6034d3SDoug Thompson 
30063f37a36bSBorislav Petkov 	mci = find_mci_by_dev(&F3->dev);
3007a4b4bedcSBorislav Petkov 	WARN_ON(!mci);
3008a4b4bedcSBorislav Petkov 
30097d6034d3SDoug Thompson 	/* Remove from EDAC CORE tracking list */
30103f37a36bSBorislav Petkov 	mci = edac_mc_del_mc(&F3->dev);
30117d6034d3SDoug Thompson 	if (!mci)
30127d6034d3SDoug Thompson 		return;
30137d6034d3SDoug Thompson 
30147d6034d3SDoug Thompson 	pvt = mci->pvt_info;
30157d6034d3SDoug Thompson 
3016360b7f3cSBorislav Petkov 	restore_ecc_error_reporting(s, nid, F3);
30177d6034d3SDoug Thompson 
3018360b7f3cSBorislav Petkov 	free_mc_sibling_devs(pvt);
30197d6034d3SDoug Thompson 
3020549d042dSBorislav Petkov 	/* unregister from EDAC MCE */
3021549d042dSBorislav Petkov 	amd_report_gart_errors(false);
3022df781d03SBorislav Petkov 	amd_unregister_ecc_decoder(decode_bus_error);
3023549d042dSBorislav Petkov 
3024360b7f3cSBorislav Petkov 	kfree(ecc_stngs[nid]);
3025360b7f3cSBorislav Petkov 	ecc_stngs[nid] = NULL;
3026ae7bb7c6SBorislav Petkov 
30277d6034d3SDoug Thompson 	/* Free the EDAC CORE resources */
30288f68ed97SBorislav Petkov 	mci->pvt_info = NULL;
30298f68ed97SBorislav Petkov 
30308f68ed97SBorislav Petkov 	kfree(pvt);
30317d6034d3SDoug Thompson 	edac_mc_free(mci);
30327d6034d3SDoug Thompson }
30337d6034d3SDoug Thompson 
3034360b7f3cSBorislav Petkov static void setup_pci_device(void)
30357d6034d3SDoug Thompson {
30367d6034d3SDoug Thompson 	struct mem_ctl_info *mci;
30377d6034d3SDoug Thompson 	struct amd64_pvt *pvt;
30387d6034d3SDoug Thompson 
3039d1ea71cdSBorislav Petkov 	if (pci_ctl)
30407d6034d3SDoug Thompson 		return;
30417d6034d3SDoug Thompson 
30422ec591acSBorislav Petkov 	mci = edac_mc_find(0);
3043d1ea71cdSBorislav Petkov 	if (!mci)
3044d1ea71cdSBorislav Petkov 		return;
30457d6034d3SDoug Thompson 
30467d6034d3SDoug Thompson 	pvt = mci->pvt_info;
3047d1ea71cdSBorislav Petkov 	pci_ctl = edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR);
3048d1ea71cdSBorislav Petkov 	if (!pci_ctl) {
3049d1ea71cdSBorislav Petkov 		pr_warn("%s(): Unable to create PCI control\n", __func__);
3050d1ea71cdSBorislav Petkov 		pr_warn("%s(): PCI error report via EDAC not set\n", __func__);
30517d6034d3SDoug Thompson 	}
30527d6034d3SDoug Thompson }
30537d6034d3SDoug Thompson 
3054d6efab74SYazen Ghannam static const struct x86_cpu_id amd64_cpuids[] = {
3055d6efab74SYazen Ghannam 	{ X86_VENDOR_AMD, 0xF,	X86_MODEL_ANY,	X86_FEATURE_ANY, 0 },
3056d6efab74SYazen Ghannam 	{ X86_VENDOR_AMD, 0x10, X86_MODEL_ANY,	X86_FEATURE_ANY, 0 },
3057d6efab74SYazen Ghannam 	{ X86_VENDOR_AMD, 0x15, X86_MODEL_ANY,	X86_FEATURE_ANY, 0 },
3058d6efab74SYazen Ghannam 	{ X86_VENDOR_AMD, 0x16, X86_MODEL_ANY,	X86_FEATURE_ANY, 0 },
3059d6efab74SYazen Ghannam 	{ }
3060d6efab74SYazen Ghannam };
3061d6efab74SYazen Ghannam MODULE_DEVICE_TABLE(x86cpu, amd64_cpuids);
3062d6efab74SYazen Ghannam 
30637d6034d3SDoug Thompson static int __init amd64_edac_init(void)
30647d6034d3SDoug Thompson {
3065360b7f3cSBorislav Petkov 	int err = -ENODEV;
30663f37a36bSBorislav Petkov 	int i;
30677d6034d3SDoug Thompson 
30689653a5c7SHans Rosenfeld 	if (amd_cache_northbridges() < 0)
306956b34b91SBorislav Petkov 		goto err_ret;
30707d6034d3SDoug Thompson 
30716ba92feaSBorislav Petkov 	opstate_init();
30726ba92feaSBorislav Petkov 
3073cc4d8860SBorislav Petkov 	err = -ENOMEM;
3074ae7bb7c6SBorislav Petkov 	ecc_stngs = kzalloc(amd_nb_num() * sizeof(ecc_stngs[0]), GFP_KERNEL);
30752ec591acSBorislav Petkov 	if (!ecc_stngs)
3076a9f0fbe2SBorislav Petkov 		goto err_free;
3077cc4d8860SBorislav Petkov 
307850542251SBorislav Petkov 	msrs = msrs_alloc();
307956b34b91SBorislav Petkov 	if (!msrs)
3080360b7f3cSBorislav Petkov 		goto err_free;
308150542251SBorislav Petkov 
30823f37a36bSBorislav Petkov 	for (i = 0; i < amd_nb_num(); i++)
30833f37a36bSBorislav Petkov 		if (probe_one_instance(i)) {
30843f37a36bSBorislav Petkov 			/* unwind properly */
30853f37a36bSBorislav Petkov 			while (--i >= 0)
30863f37a36bSBorislav Petkov 				remove_one_instance(i);
30877d6034d3SDoug Thompson 
30883f37a36bSBorislav Petkov 			goto err_pci;
30893f37a36bSBorislav Petkov 		}
30907d6034d3SDoug Thompson 
3091360b7f3cSBorislav Petkov 	setup_pci_device();
3092f5b10c45STomasz Pala 
3093f5b10c45STomasz Pala #ifdef CONFIG_X86_32
3094f5b10c45STomasz Pala 	amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR);
3095f5b10c45STomasz Pala #endif
3096f5b10c45STomasz Pala 
3097de0336b3SBorislav Petkov 	printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION);
3098de0336b3SBorislav Petkov 
30997d6034d3SDoug Thompson 	return 0;
31007d6034d3SDoug Thompson 
310156b34b91SBorislav Petkov err_pci:
310256b34b91SBorislav Petkov 	msrs_free(msrs);
310356b34b91SBorislav Petkov 	msrs = NULL;
3104cc4d8860SBorislav Petkov 
3105360b7f3cSBorislav Petkov err_free:
3106360b7f3cSBorislav Petkov 	kfree(ecc_stngs);
3107360b7f3cSBorislav Petkov 	ecc_stngs = NULL;
3108360b7f3cSBorislav Petkov 
310956b34b91SBorislav Petkov err_ret:
31107d6034d3SDoug Thompson 	return err;
31117d6034d3SDoug Thompson }
31127d6034d3SDoug Thompson 
31137d6034d3SDoug Thompson static void __exit amd64_edac_exit(void)
31147d6034d3SDoug Thompson {
31153f37a36bSBorislav Petkov 	int i;
31163f37a36bSBorislav Petkov 
3117d1ea71cdSBorislav Petkov 	if (pci_ctl)
3118d1ea71cdSBorislav Petkov 		edac_pci_release_generic_ctl(pci_ctl);
31197d6034d3SDoug Thompson 
31203f37a36bSBorislav Petkov 	for (i = 0; i < amd_nb_num(); i++)
31213f37a36bSBorislav Petkov 		remove_one_instance(i);
312250542251SBorislav Petkov 
3123ae7bb7c6SBorislav Petkov 	kfree(ecc_stngs);
3124ae7bb7c6SBorislav Petkov 	ecc_stngs = NULL;
3125ae7bb7c6SBorislav Petkov 
312650542251SBorislav Petkov 	msrs_free(msrs);
312750542251SBorislav Petkov 	msrs = NULL;
31287d6034d3SDoug Thompson }
31297d6034d3SDoug Thompson 
31307d6034d3SDoug Thompson module_init(amd64_edac_init);
31317d6034d3SDoug Thompson module_exit(amd64_edac_exit);
31327d6034d3SDoug Thompson 
31337d6034d3SDoug Thompson MODULE_LICENSE("GPL");
31347d6034d3SDoug Thompson MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, "
31357d6034d3SDoug Thompson 		"Dave Peterson, Thayne Harbaugh");
31367d6034d3SDoug Thompson MODULE_DESCRIPTION("MC support for AMD64 memory controllers - "
31377d6034d3SDoug Thompson 		EDAC_AMD64_VERSION);
31387d6034d3SDoug Thompson 
31397d6034d3SDoug Thompson module_param(edac_op_state, int, 0444);
31407d6034d3SDoug Thompson MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
3141