xref: /openbmc/linux/drivers/edac/amd64_edac.c (revision 70046624)
12bc65418SDoug Thompson #include "amd64_edac.h"
223ac4ae8SAndreas Herrmann #include <asm/amd_nb.h>
32bc65418SDoug Thompson 
42bc65418SDoug Thompson static struct edac_pci_ctl_info *amd64_ctl_pci;
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 
18360b7f3cSBorislav Petkov /*
19360b7f3cSBorislav Petkov  * count successfully initialized driver instances for setup_pci_device()
20360b7f3cSBorislav Petkov  */
21360b7f3cSBorislav Petkov static atomic_t drv_instances = ATOMIC_INIT(0);
22360b7f3cSBorislav Petkov 
23cc4d8860SBorislav Petkov /* Per-node driver instances */
24cc4d8860SBorislav Petkov static struct mem_ctl_info **mcis;
25ae7bb7c6SBorislav Petkov static struct ecc_settings **ecc_stngs;
262bc65418SDoug Thompson 
272bc65418SDoug Thompson /*
281433eb99SBorislav Petkov  * Address to DRAM bank mapping: see F2x80 for K8 and F2x[1,0]80 for Fam10 and
291433eb99SBorislav Petkov  * later.
30b70ef010SBorislav Petkov  */
311433eb99SBorislav Petkov static int ddr2_dbam_revCG[] = {
321433eb99SBorislav Petkov 			   [0]		= 32,
331433eb99SBorislav Petkov 			   [1]		= 64,
341433eb99SBorislav Petkov 			   [2]		= 128,
351433eb99SBorislav Petkov 			   [3]		= 256,
361433eb99SBorislav Petkov 			   [4]		= 512,
371433eb99SBorislav Petkov 			   [5]		= 1024,
381433eb99SBorislav Petkov 			   [6]		= 2048,
391433eb99SBorislav Petkov };
401433eb99SBorislav Petkov 
411433eb99SBorislav Petkov static int ddr2_dbam_revD[] = {
421433eb99SBorislav Petkov 			   [0]		= 32,
431433eb99SBorislav Petkov 			   [1]		= 64,
441433eb99SBorislav Petkov 			   [2 ... 3]	= 128,
451433eb99SBorislav Petkov 			   [4]		= 256,
461433eb99SBorislav Petkov 			   [5]		= 512,
471433eb99SBorislav Petkov 			   [6]		= 256,
481433eb99SBorislav Petkov 			   [7]		= 512,
491433eb99SBorislav Petkov 			   [8 ... 9]	= 1024,
501433eb99SBorislav Petkov 			   [10]		= 2048,
511433eb99SBorislav Petkov };
521433eb99SBorislav Petkov 
531433eb99SBorislav Petkov static int ddr2_dbam[] = { [0]		= 128,
541433eb99SBorislav Petkov 			   [1]		= 256,
551433eb99SBorislav Petkov 			   [2 ... 4]	= 512,
561433eb99SBorislav Petkov 			   [5 ... 6]	= 1024,
571433eb99SBorislav Petkov 			   [7 ... 8]	= 2048,
581433eb99SBorislav Petkov 			   [9 ... 10]	= 4096,
591433eb99SBorislav Petkov 			   [11]		= 8192,
601433eb99SBorislav Petkov };
611433eb99SBorislav Petkov 
621433eb99SBorislav Petkov static int ddr3_dbam[] = { [0]		= -1,
631433eb99SBorislav Petkov 			   [1]		= 256,
641433eb99SBorislav Petkov 			   [2]		= 512,
651433eb99SBorislav Petkov 			   [3 ... 4]	= -1,
661433eb99SBorislav Petkov 			   [5 ... 6]	= 1024,
671433eb99SBorislav Petkov 			   [7 ... 8]	= 2048,
681433eb99SBorislav Petkov 			   [9 ... 10]	= 4096,
691433eb99SBorislav Petkov 			   [11]		= 8192,
70b70ef010SBorislav Petkov };
71b70ef010SBorislav Petkov 
72b70ef010SBorislav Petkov /*
73b70ef010SBorislav Petkov  * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing
74b70ef010SBorislav Petkov  * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching-
75b70ef010SBorislav Petkov  * or higher value'.
76b70ef010SBorislav Petkov  *
77b70ef010SBorislav Petkov  *FIXME: Produce a better mapping/linearisation.
78b70ef010SBorislav Petkov  */
79b70ef010SBorislav Petkov 
8039094443SBorislav Petkov 
8139094443SBorislav Petkov struct scrubrate {
8239094443SBorislav Petkov        u32 scrubval;           /* bit pattern for scrub rate */
8339094443SBorislav Petkov        u32 bandwidth;          /* bandwidth consumed (bytes/sec) */
8439094443SBorislav Petkov } scrubrates[] = {
85b70ef010SBorislav Petkov 	{ 0x01, 1600000000UL},
86b70ef010SBorislav Petkov 	{ 0x02, 800000000UL},
87b70ef010SBorislav Petkov 	{ 0x03, 400000000UL},
88b70ef010SBorislav Petkov 	{ 0x04, 200000000UL},
89b70ef010SBorislav Petkov 	{ 0x05, 100000000UL},
90b70ef010SBorislav Petkov 	{ 0x06, 50000000UL},
91b70ef010SBorislav Petkov 	{ 0x07, 25000000UL},
92b70ef010SBorislav Petkov 	{ 0x08, 12284069UL},
93b70ef010SBorislav Petkov 	{ 0x09, 6274509UL},
94b70ef010SBorislav Petkov 	{ 0x0A, 3121951UL},
95b70ef010SBorislav Petkov 	{ 0x0B, 1560975UL},
96b70ef010SBorislav Petkov 	{ 0x0C, 781440UL},
97b70ef010SBorislav Petkov 	{ 0x0D, 390720UL},
98b70ef010SBorislav Petkov 	{ 0x0E, 195300UL},
99b70ef010SBorislav Petkov 	{ 0x0F, 97650UL},
100b70ef010SBorislav Petkov 	{ 0x10, 48854UL},
101b70ef010SBorislav Petkov 	{ 0x11, 24427UL},
102b70ef010SBorislav Petkov 	{ 0x12, 12213UL},
103b70ef010SBorislav Petkov 	{ 0x13, 6101UL},
104b70ef010SBorislav Petkov 	{ 0x14, 3051UL},
105b70ef010SBorislav Petkov 	{ 0x15, 1523UL},
106b70ef010SBorislav Petkov 	{ 0x16, 761UL},
107b70ef010SBorislav Petkov 	{ 0x00, 0UL},        /* scrubbing off */
108b70ef010SBorislav Petkov };
109b70ef010SBorislav Petkov 
110b2b0c605SBorislav Petkov static int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset,
111b2b0c605SBorislav Petkov 				      u32 *val, const char *func)
112b2b0c605SBorislav Petkov {
113b2b0c605SBorislav Petkov 	int err = 0;
114b2b0c605SBorislav Petkov 
115b2b0c605SBorislav Petkov 	err = pci_read_config_dword(pdev, offset, val);
116b2b0c605SBorislav Petkov 	if (err)
117b2b0c605SBorislav Petkov 		amd64_warn("%s: error reading F%dx%03x.\n",
118b2b0c605SBorislav Petkov 			   func, PCI_FUNC(pdev->devfn), offset);
119b2b0c605SBorislav Petkov 
120b2b0c605SBorislav Petkov 	return err;
121b2b0c605SBorislav Petkov }
122b2b0c605SBorislav Petkov 
123b2b0c605SBorislav Petkov int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset,
124b2b0c605SBorislav Petkov 				u32 val, const char *func)
125b2b0c605SBorislav Petkov {
126b2b0c605SBorislav Petkov 	int err = 0;
127b2b0c605SBorislav Petkov 
128b2b0c605SBorislav Petkov 	err = pci_write_config_dword(pdev, offset, val);
129b2b0c605SBorislav Petkov 	if (err)
130b2b0c605SBorislav Petkov 		amd64_warn("%s: error writing to F%dx%03x.\n",
131b2b0c605SBorislav Petkov 			   func, PCI_FUNC(pdev->devfn), offset);
132b2b0c605SBorislav Petkov 
133b2b0c605SBorislav Petkov 	return err;
134b2b0c605SBorislav Petkov }
135b2b0c605SBorislav Petkov 
136b2b0c605SBorislav Petkov /*
137b2b0c605SBorislav Petkov  *
138b2b0c605SBorislav Petkov  * Depending on the family, F2 DCT reads need special handling:
139b2b0c605SBorislav Petkov  *
140b2b0c605SBorislav Petkov  * K8: has a single DCT only
141b2b0c605SBorislav Petkov  *
142b2b0c605SBorislav Petkov  * F10h: each DCT has its own set of regs
143b2b0c605SBorislav Petkov  *	DCT0 -> F2x040..
144b2b0c605SBorislav Petkov  *	DCT1 -> F2x140..
145b2b0c605SBorislav Petkov  *
146b2b0c605SBorislav Petkov  * F15h: we select which DCT we access using F1x10C[DctCfgSel]
147b2b0c605SBorislav Petkov  *
148b2b0c605SBorislav Petkov  */
149b2b0c605SBorislav Petkov static int k8_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
150b2b0c605SBorislav Petkov 			       const char *func)
151b2b0c605SBorislav Petkov {
152b2b0c605SBorislav Petkov 	if (addr >= 0x100)
153b2b0c605SBorislav Petkov 		return -EINVAL;
154b2b0c605SBorislav Petkov 
155b2b0c605SBorislav Petkov 	return __amd64_read_pci_cfg_dword(pvt->F2, addr, val, func);
156b2b0c605SBorislav Petkov }
157b2b0c605SBorislav Petkov 
158b2b0c605SBorislav Petkov static int f10_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
159b2b0c605SBorislav Petkov 				 const char *func)
160b2b0c605SBorislav Petkov {
161b2b0c605SBorislav Petkov 	return __amd64_read_pci_cfg_dword(pvt->F2, addr, val, func);
162b2b0c605SBorislav Petkov }
163b2b0c605SBorislav Petkov 
164b2b0c605SBorislav Petkov static int f15_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
165b2b0c605SBorislav Petkov 				 const char *func)
166b2b0c605SBorislav Petkov {
167b2b0c605SBorislav Petkov 	u32 reg = 0;
168b2b0c605SBorislav Petkov 	u8 dct  = 0;
169b2b0c605SBorislav Petkov 
170b2b0c605SBorislav Petkov 	if (addr >= 0x140 && addr <= 0x1a0) {
171b2b0c605SBorislav Petkov 		dct   = 1;
172b2b0c605SBorislav Petkov 		addr -= 0x100;
173b2b0c605SBorislav Petkov 	}
174b2b0c605SBorislav Petkov 
175b2b0c605SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, &reg);
176b2b0c605SBorislav Petkov 	reg &= 0xfffffffe;
177b2b0c605SBorislav Petkov 	reg |= dct;
178b2b0c605SBorislav Petkov 	amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg);
179b2b0c605SBorislav Petkov 
180b2b0c605SBorislav Petkov 	return __amd64_read_pci_cfg_dword(pvt->F2, addr, val, func);
181b2b0c605SBorislav Petkov }
182b2b0c605SBorislav Petkov 
183b70ef010SBorislav Petkov /*
1842bc65418SDoug Thompson  * Memory scrubber control interface. For K8, memory scrubbing is handled by
1852bc65418SDoug Thompson  * hardware and can involve L2 cache, dcache as well as the main memory. With
1862bc65418SDoug Thompson  * F10, this is extended to L3 cache scrubbing on CPU models sporting that
1872bc65418SDoug Thompson  * functionality.
1882bc65418SDoug Thompson  *
1892bc65418SDoug Thompson  * This causes the "units" for the scrubbing speed to vary from 64 byte blocks
1902bc65418SDoug Thompson  * (dram) over to cache lines. This is nasty, so we will use bandwidth in
1912bc65418SDoug Thompson  * bytes/sec for the setting.
1922bc65418SDoug Thompson  *
1932bc65418SDoug Thompson  * Currently, we only do dram scrubbing. If the scrubbing is done in software on
1942bc65418SDoug Thompson  * other archs, we might not have access to the caches directly.
1952bc65418SDoug Thompson  */
1962bc65418SDoug Thompson 
1972bc65418SDoug Thompson /*
1982bc65418SDoug Thompson  * scan the scrub rate mapping table for a close or matching bandwidth value to
1992bc65418SDoug Thompson  * issue. If requested is too big, then use last maximum value found.
2002bc65418SDoug Thompson  */
201395ae783SBorislav Petkov static int __amd64_set_scrub_rate(struct pci_dev *ctl, u32 new_bw, u32 min_rate)
2022bc65418SDoug Thompson {
2032bc65418SDoug Thompson 	u32 scrubval;
2042bc65418SDoug Thompson 	int i;
2052bc65418SDoug Thompson 
2062bc65418SDoug Thompson 	/*
2072bc65418SDoug Thompson 	 * map the configured rate (new_bw) to a value specific to the AMD64
2082bc65418SDoug Thompson 	 * memory controller and apply to register. Search for the first
2092bc65418SDoug Thompson 	 * bandwidth entry that is greater or equal than the setting requested
2102bc65418SDoug Thompson 	 * and program that. If at last entry, turn off DRAM scrubbing.
2112bc65418SDoug Thompson 	 */
2122bc65418SDoug Thompson 	for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
2132bc65418SDoug Thompson 		/*
2142bc65418SDoug Thompson 		 * skip scrub rates which aren't recommended
2152bc65418SDoug Thompson 		 * (see F10 BKDG, F3x58)
2162bc65418SDoug Thompson 		 */
217395ae783SBorislav Petkov 		if (scrubrates[i].scrubval < min_rate)
2182bc65418SDoug Thompson 			continue;
2192bc65418SDoug Thompson 
2202bc65418SDoug Thompson 		if (scrubrates[i].bandwidth <= new_bw)
2212bc65418SDoug Thompson 			break;
2222bc65418SDoug Thompson 
2232bc65418SDoug Thompson 		/*
2242bc65418SDoug Thompson 		 * if no suitable bandwidth found, turn off DRAM scrubbing
2252bc65418SDoug Thompson 		 * entirely by falling back to the last element in the
2262bc65418SDoug Thompson 		 * scrubrates array.
2272bc65418SDoug Thompson 		 */
2282bc65418SDoug Thompson 	}
2292bc65418SDoug Thompson 
2302bc65418SDoug Thompson 	scrubval = scrubrates[i].scrubval;
2312bc65418SDoug Thompson 
2325980bb9cSBorislav Petkov 	pci_write_bits32(ctl, SCRCTRL, scrubval, 0x001F);
2332bc65418SDoug Thompson 
23439094443SBorislav Petkov 	if (scrubval)
23539094443SBorislav Petkov 		return scrubrates[i].bandwidth;
23639094443SBorislav Petkov 
2372bc65418SDoug Thompson 	return 0;
2382bc65418SDoug Thompson }
2392bc65418SDoug Thompson 
240395ae783SBorislav Petkov static int amd64_set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
2412bc65418SDoug Thompson {
2422bc65418SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
2432bc65418SDoug Thompson 
2448d5b5d9cSBorislav Petkov 	return __amd64_set_scrub_rate(pvt->F3, bw, pvt->min_scrubrate);
2452bc65418SDoug Thompson }
2462bc65418SDoug Thompson 
24739094443SBorislav Petkov static int amd64_get_scrub_rate(struct mem_ctl_info *mci)
2482bc65418SDoug Thompson {
2492bc65418SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
2502bc65418SDoug Thompson 	u32 scrubval = 0;
25139094443SBorislav Petkov 	int i, retval = -EINVAL;
2522bc65418SDoug Thompson 
2535980bb9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
2542bc65418SDoug Thompson 
2552bc65418SDoug Thompson 	scrubval = scrubval & 0x001F;
2562bc65418SDoug Thompson 
25724f9a7feSBorislav Petkov 	amd64_debug("pci-read, sdram scrub control value: %d\n", scrubval);
2582bc65418SDoug Thompson 
259926311fdSRoel Kluin 	for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
2602bc65418SDoug Thompson 		if (scrubrates[i].scrubval == scrubval) {
26139094443SBorislav Petkov 			retval = scrubrates[i].bandwidth;
2622bc65418SDoug Thompson 			break;
2632bc65418SDoug Thompson 		}
2642bc65418SDoug Thompson 	}
26539094443SBorislav Petkov 	return retval;
2662bc65418SDoug Thompson }
2672bc65418SDoug Thompson 
2686775763aSDoug Thompson /*
2697f19bf75SBorislav Petkov  * returns true if the SysAddr given by sys_addr matches the
2707f19bf75SBorislav Petkov  * DRAM base/limit associated with node_id
2716775763aSDoug Thompson  */
2727f19bf75SBorislav Petkov static bool amd64_base_limit_match(struct amd64_pvt *pvt, u64 sys_addr, int nid)
2736775763aSDoug Thompson {
2747f19bf75SBorislav Petkov 	u64 addr;
2756775763aSDoug Thompson 
2766775763aSDoug Thompson 	/* The K8 treats this as a 40-bit value.  However, bits 63-40 will be
2776775763aSDoug Thompson 	 * all ones if the most significant implemented address bit is 1.
2786775763aSDoug Thompson 	 * Here we discard bits 63-40.  See section 3.4.2 of AMD publication
2796775763aSDoug Thompson 	 * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1
2806775763aSDoug Thompson 	 * Application Programming.
2816775763aSDoug Thompson 	 */
2826775763aSDoug Thompson 	addr = sys_addr & 0x000000ffffffffffull;
2836775763aSDoug Thompson 
2847f19bf75SBorislav Petkov 	return ((addr >= get_dram_base(pvt, nid)) &&
2857f19bf75SBorislav Petkov 		(addr <= get_dram_limit(pvt, nid)));
2866775763aSDoug Thompson }
2876775763aSDoug Thompson 
2886775763aSDoug Thompson /*
2896775763aSDoug Thompson  * Attempt to map a SysAddr to a node. On success, return a pointer to the
2906775763aSDoug Thompson  * mem_ctl_info structure for the node that the SysAddr maps to.
2916775763aSDoug Thompson  *
2926775763aSDoug Thompson  * On failure, return NULL.
2936775763aSDoug Thompson  */
2946775763aSDoug Thompson static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci,
2956775763aSDoug Thompson 						u64 sys_addr)
2966775763aSDoug Thompson {
2976775763aSDoug Thompson 	struct amd64_pvt *pvt;
2986775763aSDoug Thompson 	int node_id;
2996775763aSDoug Thompson 	u32 intlv_en, bits;
3006775763aSDoug Thompson 
3016775763aSDoug Thompson 	/*
3026775763aSDoug Thompson 	 * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section
3036775763aSDoug Thompson 	 * 3.4.4.2) registers to map the SysAddr to a node ID.
3046775763aSDoug Thompson 	 */
3056775763aSDoug Thompson 	pvt = mci->pvt_info;
3066775763aSDoug Thompson 
3076775763aSDoug Thompson 	/*
3086775763aSDoug Thompson 	 * The value of this field should be the same for all DRAM Base
3096775763aSDoug Thompson 	 * registers.  Therefore we arbitrarily choose to read it from the
3106775763aSDoug Thompson 	 * register for node 0.
3116775763aSDoug Thompson 	 */
3127f19bf75SBorislav Petkov 	intlv_en = dram_intlv_en(pvt, 0);
3136775763aSDoug Thompson 
3146775763aSDoug Thompson 	if (intlv_en == 0) {
3157f19bf75SBorislav Petkov 		for (node_id = 0; node_id < DRAM_RANGES; node_id++) {
3166775763aSDoug Thompson 			if (amd64_base_limit_match(pvt, sys_addr, node_id))
3176775763aSDoug Thompson 				goto found;
3186775763aSDoug Thompson 		}
3198edc5445SBorislav Petkov 		goto err_no_match;
3208edc5445SBorislav Petkov 	}
3216775763aSDoug Thompson 
32272f158feSBorislav Petkov 	if (unlikely((intlv_en != 0x01) &&
32372f158feSBorislav Petkov 		     (intlv_en != 0x03) &&
32472f158feSBorislav Petkov 		     (intlv_en != 0x07))) {
32524f9a7feSBorislav Petkov 		amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en);
3266775763aSDoug Thompson 		return NULL;
3276775763aSDoug Thompson 	}
3286775763aSDoug Thompson 
3296775763aSDoug Thompson 	bits = (((u32) sys_addr) >> 12) & intlv_en;
3306775763aSDoug Thompson 
3316775763aSDoug Thompson 	for (node_id = 0; ; ) {
3327f19bf75SBorislav Petkov 		if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits)
3336775763aSDoug Thompson 			break;	/* intlv_sel field matches */
3346775763aSDoug Thompson 
3357f19bf75SBorislav Petkov 		if (++node_id >= DRAM_RANGES)
3366775763aSDoug Thompson 			goto err_no_match;
3376775763aSDoug Thompson 	}
3386775763aSDoug Thompson 
3396775763aSDoug Thompson 	/* sanity test for sys_addr */
3406775763aSDoug Thompson 	if (unlikely(!amd64_base_limit_match(pvt, sys_addr, node_id))) {
34124f9a7feSBorislav Petkov 		amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address"
34224f9a7feSBorislav Petkov 			   "range for node %d with node interleaving enabled.\n",
3438edc5445SBorislav Petkov 			   __func__, sys_addr, node_id);
3446775763aSDoug Thompson 		return NULL;
3456775763aSDoug Thompson 	}
3466775763aSDoug Thompson 
3476775763aSDoug Thompson found:
3486775763aSDoug Thompson 	return edac_mc_find(node_id);
3496775763aSDoug Thompson 
3506775763aSDoug Thompson err_no_match:
3516775763aSDoug Thompson 	debugf2("sys_addr 0x%lx doesn't match any node\n",
3526775763aSDoug Thompson 		(unsigned long)sys_addr);
3536775763aSDoug Thompson 
3546775763aSDoug Thompson 	return NULL;
3556775763aSDoug Thompson }
356e2ce7255SDoug Thompson 
357e2ce7255SDoug Thompson /*
35811c75eadSBorislav Petkov  * compute the CS base address of the @csrow on the DRAM controller @dct.
35911c75eadSBorislav Petkov  * For details see F2x[5C:40] in the processor's BKDG
360e2ce7255SDoug Thompson  */
36111c75eadSBorislav Petkov static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct,
36211c75eadSBorislav Petkov 				 u64 *base, u64 *mask)
363e2ce7255SDoug Thompson {
36411c75eadSBorislav Petkov 	u64 csbase, csmask, base_bits, mask_bits;
36511c75eadSBorislav Petkov 	u8 addr_shift;
36611c75eadSBorislav Petkov 
36711c75eadSBorislav Petkov 	if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_F) {
36811c75eadSBorislav Petkov 		csbase		= pvt->csels[dct].csbases[csrow];
36911c75eadSBorislav Petkov 		csmask		= pvt->csels[dct].csmasks[csrow];
37011c75eadSBorislav Petkov 		base_bits	= GENMASK(21, 31) | GENMASK(9, 15);
37111c75eadSBorislav Petkov 		mask_bits	= GENMASK(21, 29) | GENMASK(9, 15);
37211c75eadSBorislav Petkov 		addr_shift	= 4;
37311c75eadSBorislav Petkov 	} else {
37411c75eadSBorislav Petkov 		csbase		= pvt->csels[dct].csbases[csrow];
37511c75eadSBorislav Petkov 		csmask		= pvt->csels[dct].csmasks[csrow >> 1];
37611c75eadSBorislav Petkov 		addr_shift	= 8;
37711c75eadSBorislav Petkov 
37811c75eadSBorislav Petkov 		if (boot_cpu_data.x86 == 0x15)
37911c75eadSBorislav Petkov 			base_bits = mask_bits = GENMASK(19,30) | GENMASK(5,13);
38011c75eadSBorislav Petkov 		else
38111c75eadSBorislav Petkov 			base_bits = mask_bits = GENMASK(19,28) | GENMASK(5,13);
382e2ce7255SDoug Thompson 	}
383e2ce7255SDoug Thompson 
38411c75eadSBorislav Petkov 	*base  = (csbase & base_bits) << addr_shift;
385e2ce7255SDoug Thompson 
38611c75eadSBorislav Petkov 	*mask  = ~0ULL;
38711c75eadSBorislav Petkov 	/* poke holes for the csmask */
38811c75eadSBorislav Petkov 	*mask &= ~(mask_bits << addr_shift);
38911c75eadSBorislav Petkov 	/* OR them in */
39011c75eadSBorislav Petkov 	*mask |= (csmask & mask_bits) << addr_shift;
391e2ce7255SDoug Thompson }
392e2ce7255SDoug Thompson 
39311c75eadSBorislav Petkov #define for_each_chip_select(i, dct, pvt) \
39411c75eadSBorislav Petkov 	for (i = 0; i < pvt->csels[dct].b_cnt; i++)
39511c75eadSBorislav Petkov 
39611c75eadSBorislav Petkov #define for_each_chip_select_mask(i, dct, pvt) \
39711c75eadSBorislav Petkov 	for (i = 0; i < pvt->csels[dct].m_cnt; i++)
39811c75eadSBorislav Petkov 
399e2ce7255SDoug Thompson /*
400e2ce7255SDoug Thompson  * @input_addr is an InputAddr associated with the node given by mci. Return the
401e2ce7255SDoug Thompson  * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr).
402e2ce7255SDoug Thompson  */
403e2ce7255SDoug Thompson static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr)
404e2ce7255SDoug Thompson {
405e2ce7255SDoug Thompson 	struct amd64_pvt *pvt;
406e2ce7255SDoug Thompson 	int csrow;
407e2ce7255SDoug Thompson 	u64 base, mask;
408e2ce7255SDoug Thompson 
409e2ce7255SDoug Thompson 	pvt = mci->pvt_info;
410e2ce7255SDoug Thompson 
41111c75eadSBorislav Petkov 	for_each_chip_select(csrow, 0, pvt) {
41211c75eadSBorislav Petkov 		if (!csrow_enabled(csrow, 0, pvt))
413e2ce7255SDoug Thompson 			continue;
414e2ce7255SDoug Thompson 
41511c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, csrow, 0, &base, &mask);
41611c75eadSBorislav Petkov 
41711c75eadSBorislav Petkov 		mask = ~mask;
418e2ce7255SDoug Thompson 
419e2ce7255SDoug Thompson 		if ((input_addr & mask) == (base & mask)) {
420e2ce7255SDoug Thompson 			debugf2("InputAddr 0x%lx matches csrow %d (node %d)\n",
421e2ce7255SDoug Thompson 				(unsigned long)input_addr, csrow,
422e2ce7255SDoug Thompson 				pvt->mc_node_id);
423e2ce7255SDoug Thompson 
424e2ce7255SDoug Thompson 			return csrow;
425e2ce7255SDoug Thompson 		}
426e2ce7255SDoug Thompson 	}
427e2ce7255SDoug Thompson 	debugf2("no matching csrow for InputAddr 0x%lx (MC node %d)\n",
428e2ce7255SDoug Thompson 		(unsigned long)input_addr, pvt->mc_node_id);
429e2ce7255SDoug Thompson 
430e2ce7255SDoug Thompson 	return -1;
431e2ce7255SDoug Thompson }
432e2ce7255SDoug Thompson 
433e2ce7255SDoug Thompson /*
434e2ce7255SDoug Thompson  * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094)
435e2ce7255SDoug Thompson  * for the node represented by mci. Info is passed back in *hole_base,
436e2ce7255SDoug Thompson  * *hole_offset, and *hole_size.  Function returns 0 if info is valid or 1 if
437e2ce7255SDoug Thompson  * info is invalid. Info may be invalid for either of the following reasons:
438e2ce7255SDoug Thompson  *
439e2ce7255SDoug Thompson  * - The revision of the node is not E or greater.  In this case, the DRAM Hole
440e2ce7255SDoug Thompson  *   Address Register does not exist.
441e2ce7255SDoug Thompson  *
442e2ce7255SDoug Thompson  * - The DramHoleValid bit is cleared in the DRAM Hole Address Register,
443e2ce7255SDoug Thompson  *   indicating that its contents are not valid.
444e2ce7255SDoug Thompson  *
445e2ce7255SDoug Thompson  * The values passed back in *hole_base, *hole_offset, and *hole_size are
446e2ce7255SDoug Thompson  * complete 32-bit values despite the fact that the bitfields in the DHAR
447e2ce7255SDoug Thompson  * only represent bits 31-24 of the base and offset values.
448e2ce7255SDoug Thompson  */
449e2ce7255SDoug Thompson int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
450e2ce7255SDoug Thompson 			     u64 *hole_offset, u64 *hole_size)
451e2ce7255SDoug Thompson {
452e2ce7255SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
453e2ce7255SDoug Thompson 	u64 base;
454e2ce7255SDoug Thompson 
455e2ce7255SDoug Thompson 	/* only revE and later have the DRAM Hole Address Register */
4561433eb99SBorislav Petkov 	if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_E) {
457e2ce7255SDoug Thompson 		debugf1("  revision %d for node %d does not support DHAR\n",
458e2ce7255SDoug Thompson 			pvt->ext_model, pvt->mc_node_id);
459e2ce7255SDoug Thompson 		return 1;
460e2ce7255SDoug Thompson 	}
461e2ce7255SDoug Thompson 
462bc21fa57SBorislav Petkov 	/* valid for Fam10h and above */
463c8e518d5SBorislav Petkov 	if (boot_cpu_data.x86 >= 0x10 && !dhar_mem_hoist_valid(pvt)) {
464e2ce7255SDoug Thompson 		debugf1("  Dram Memory Hoisting is DISABLED on this system\n");
465e2ce7255SDoug Thompson 		return 1;
466e2ce7255SDoug Thompson 	}
467e2ce7255SDoug Thompson 
468c8e518d5SBorislav Petkov 	if (!dhar_valid(pvt)) {
469e2ce7255SDoug Thompson 		debugf1("  Dram Memory Hoisting is DISABLED on this node %d\n",
470e2ce7255SDoug Thompson 			pvt->mc_node_id);
471e2ce7255SDoug Thompson 		return 1;
472e2ce7255SDoug Thompson 	}
473e2ce7255SDoug Thompson 
474e2ce7255SDoug Thompson 	/* This node has Memory Hoisting */
475e2ce7255SDoug Thompson 
476e2ce7255SDoug Thompson 	/* +------------------+--------------------+--------------------+-----
477e2ce7255SDoug Thompson 	 * | memory           | DRAM hole          | relocated          |
478e2ce7255SDoug Thompson 	 * | [0, (x - 1)]     | [x, 0xffffffff]    | addresses from     |
479e2ce7255SDoug Thompson 	 * |                  |                    | DRAM hole          |
480e2ce7255SDoug Thompson 	 * |                  |                    | [0x100000000,      |
481e2ce7255SDoug Thompson 	 * |                  |                    |  (0x100000000+     |
482e2ce7255SDoug Thompson 	 * |                  |                    |   (0xffffffff-x))] |
483e2ce7255SDoug Thompson 	 * +------------------+--------------------+--------------------+-----
484e2ce7255SDoug Thompson 	 *
485e2ce7255SDoug Thompson 	 * Above is a diagram of physical memory showing the DRAM hole and the
486e2ce7255SDoug Thompson 	 * relocated addresses from the DRAM hole.  As shown, the DRAM hole
487e2ce7255SDoug Thompson 	 * starts at address x (the base address) and extends through address
488e2ce7255SDoug Thompson 	 * 0xffffffff.  The DRAM Hole Address Register (DHAR) relocates the
489e2ce7255SDoug Thompson 	 * addresses in the hole so that they start at 0x100000000.
490e2ce7255SDoug Thompson 	 */
491e2ce7255SDoug Thompson 
492bc21fa57SBorislav Petkov 	base = dhar_base(pvt);
493e2ce7255SDoug Thompson 
494e2ce7255SDoug Thompson 	*hole_base = base;
495e2ce7255SDoug Thompson 	*hole_size = (0x1ull << 32) - base;
496e2ce7255SDoug Thompson 
497e2ce7255SDoug Thompson 	if (boot_cpu_data.x86 > 0xf)
498bc21fa57SBorislav Petkov 		*hole_offset = f10_dhar_offset(pvt);
499e2ce7255SDoug Thompson 	else
500bc21fa57SBorislav Petkov 		*hole_offset = k8_dhar_offset(pvt);
501e2ce7255SDoug Thompson 
502e2ce7255SDoug Thompson 	debugf1("  DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
503e2ce7255SDoug Thompson 		pvt->mc_node_id, (unsigned long)*hole_base,
504e2ce7255SDoug Thompson 		(unsigned long)*hole_offset, (unsigned long)*hole_size);
505e2ce7255SDoug Thompson 
506e2ce7255SDoug Thompson 	return 0;
507e2ce7255SDoug Thompson }
508e2ce7255SDoug Thompson EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info);
509e2ce7255SDoug Thompson 
51093c2df58SDoug Thompson /*
51193c2df58SDoug Thompson  * Return the DramAddr that the SysAddr given by @sys_addr maps to.  It is
51293c2df58SDoug Thompson  * assumed that sys_addr maps to the node given by mci.
51393c2df58SDoug Thompson  *
51493c2df58SDoug Thompson  * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section
51593c2df58SDoug Thompson  * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a
51693c2df58SDoug Thompson  * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled,
51793c2df58SDoug Thompson  * then it is also involved in translating a SysAddr to a DramAddr. Sections
51893c2df58SDoug Thompson  * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting.
51993c2df58SDoug Thompson  * These parts of the documentation are unclear. I interpret them as follows:
52093c2df58SDoug Thompson  *
52193c2df58SDoug Thompson  * When node n receives a SysAddr, it processes the SysAddr as follows:
52293c2df58SDoug Thompson  *
52393c2df58SDoug Thompson  * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM
52493c2df58SDoug Thompson  *    Limit registers for node n. If the SysAddr is not within the range
52593c2df58SDoug Thompson  *    specified by the base and limit values, then node n ignores the Sysaddr
52693c2df58SDoug Thompson  *    (since it does not map to node n). Otherwise continue to step 2 below.
52793c2df58SDoug Thompson  *
52893c2df58SDoug Thompson  * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is
52993c2df58SDoug Thompson  *    disabled so skip to step 3 below. Otherwise see if the SysAddr is within
53093c2df58SDoug Thompson  *    the range of relocated addresses (starting at 0x100000000) from the DRAM
53193c2df58SDoug Thompson  *    hole. If not, skip to step 3 below. Else get the value of the
53293c2df58SDoug Thompson  *    DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the
53393c2df58SDoug Thompson  *    offset defined by this value from the SysAddr.
53493c2df58SDoug Thompson  *
53593c2df58SDoug Thompson  * 3. Obtain the base address for node n from the DRAMBase field of the DRAM
53693c2df58SDoug Thompson  *    Base register for node n. To obtain the DramAddr, subtract the base
53793c2df58SDoug Thompson  *    address from the SysAddr, as shown near the start of section 3.4.4 (p.70).
53893c2df58SDoug Thompson  */
53993c2df58SDoug Thompson static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
54093c2df58SDoug Thompson {
5417f19bf75SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
54293c2df58SDoug Thompson 	u64 dram_base, hole_base, hole_offset, hole_size, dram_addr;
54393c2df58SDoug Thompson 	int ret = 0;
54493c2df58SDoug Thompson 
5457f19bf75SBorislav Petkov 	dram_base = get_dram_base(pvt, pvt->mc_node_id);
54693c2df58SDoug Thompson 
54793c2df58SDoug Thompson 	ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
54893c2df58SDoug Thompson 				      &hole_size);
54993c2df58SDoug Thompson 	if (!ret) {
55093c2df58SDoug Thompson 		if ((sys_addr >= (1ull << 32)) &&
55193c2df58SDoug Thompson 		    (sys_addr < ((1ull << 32) + hole_size))) {
55293c2df58SDoug Thompson 			/* use DHAR to translate SysAddr to DramAddr */
55393c2df58SDoug Thompson 			dram_addr = sys_addr - hole_offset;
55493c2df58SDoug Thompson 
55593c2df58SDoug Thompson 			debugf2("using DHAR to translate SysAddr 0x%lx to "
55693c2df58SDoug Thompson 				"DramAddr 0x%lx\n",
55793c2df58SDoug Thompson 				(unsigned long)sys_addr,
55893c2df58SDoug Thompson 				(unsigned long)dram_addr);
55993c2df58SDoug Thompson 
56093c2df58SDoug Thompson 			return dram_addr;
56193c2df58SDoug Thompson 		}
56293c2df58SDoug Thompson 	}
56393c2df58SDoug Thompson 
56493c2df58SDoug Thompson 	/*
56593c2df58SDoug Thompson 	 * Translate the SysAddr to a DramAddr as shown near the start of
56693c2df58SDoug Thompson 	 * section 3.4.4 (p. 70).  Although sys_addr is a 64-bit value, the k8
56793c2df58SDoug Thompson 	 * only deals with 40-bit values.  Therefore we discard bits 63-40 of
56893c2df58SDoug Thompson 	 * sys_addr below.  If bit 39 of sys_addr is 1 then the bits we
56993c2df58SDoug Thompson 	 * discard are all 1s.  Otherwise the bits we discard are all 0s.  See
57093c2df58SDoug Thompson 	 * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture
57193c2df58SDoug Thompson 	 * Programmer's Manual Volume 1 Application Programming.
57293c2df58SDoug Thompson 	 */
573f678b8ccSBorislav Petkov 	dram_addr = (sys_addr & GENMASK(0, 39)) - dram_base;
57493c2df58SDoug Thompson 
57593c2df58SDoug Thompson 	debugf2("using DRAM Base register to translate SysAddr 0x%lx to "
57693c2df58SDoug Thompson 		"DramAddr 0x%lx\n", (unsigned long)sys_addr,
57793c2df58SDoug Thompson 		(unsigned long)dram_addr);
57893c2df58SDoug Thompson 	return dram_addr;
57993c2df58SDoug Thompson }
58093c2df58SDoug Thompson 
58193c2df58SDoug Thompson /*
58293c2df58SDoug Thompson  * @intlv_en is the value of the IntlvEn field from a DRAM Base register
58393c2df58SDoug Thompson  * (section 3.4.4.1).  Return the number of bits from a SysAddr that are used
58493c2df58SDoug Thompson  * for node interleaving.
58593c2df58SDoug Thompson  */
58693c2df58SDoug Thompson static int num_node_interleave_bits(unsigned intlv_en)
58793c2df58SDoug Thompson {
58893c2df58SDoug Thompson 	static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 };
58993c2df58SDoug Thompson 	int n;
59093c2df58SDoug Thompson 
59193c2df58SDoug Thompson 	BUG_ON(intlv_en > 7);
59293c2df58SDoug Thompson 	n = intlv_shift_table[intlv_en];
59393c2df58SDoug Thompson 	return n;
59493c2df58SDoug Thompson }
59593c2df58SDoug Thompson 
59693c2df58SDoug Thompson /* Translate the DramAddr given by @dram_addr to an InputAddr. */
59793c2df58SDoug Thompson static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
59893c2df58SDoug Thompson {
59993c2df58SDoug Thompson 	struct amd64_pvt *pvt;
60093c2df58SDoug Thompson 	int intlv_shift;
60193c2df58SDoug Thompson 	u64 input_addr;
60293c2df58SDoug Thompson 
60393c2df58SDoug Thompson 	pvt = mci->pvt_info;
60493c2df58SDoug Thompson 
60593c2df58SDoug Thompson 	/*
60693c2df58SDoug Thompson 	 * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
60793c2df58SDoug Thompson 	 * concerning translating a DramAddr to an InputAddr.
60893c2df58SDoug Thompson 	 */
6097f19bf75SBorislav Petkov 	intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
610f678b8ccSBorislav Petkov 	input_addr = ((dram_addr >> intlv_shift) & GENMASK(12, 35)) +
61193c2df58SDoug Thompson 		      (dram_addr & 0xfff);
61293c2df58SDoug Thompson 
61393c2df58SDoug Thompson 	debugf2("  Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
61493c2df58SDoug Thompson 		intlv_shift, (unsigned long)dram_addr,
61593c2df58SDoug Thompson 		(unsigned long)input_addr);
61693c2df58SDoug Thompson 
61793c2df58SDoug Thompson 	return input_addr;
61893c2df58SDoug Thompson }
61993c2df58SDoug Thompson 
62093c2df58SDoug Thompson /*
62193c2df58SDoug Thompson  * Translate the SysAddr represented by @sys_addr to an InputAddr.  It is
62293c2df58SDoug Thompson  * assumed that @sys_addr maps to the node given by mci.
62393c2df58SDoug Thompson  */
62493c2df58SDoug Thompson static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
62593c2df58SDoug Thompson {
62693c2df58SDoug Thompson 	u64 input_addr;
62793c2df58SDoug Thompson 
62893c2df58SDoug Thompson 	input_addr =
62993c2df58SDoug Thompson 	    dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
63093c2df58SDoug Thompson 
63193c2df58SDoug Thompson 	debugf2("SysAdddr 0x%lx translates to InputAddr 0x%lx\n",
63293c2df58SDoug Thompson 		(unsigned long)sys_addr, (unsigned long)input_addr);
63393c2df58SDoug Thompson 
63493c2df58SDoug Thompson 	return input_addr;
63593c2df58SDoug Thompson }
63693c2df58SDoug Thompson 
63793c2df58SDoug Thompson 
63893c2df58SDoug Thompson /*
63993c2df58SDoug Thompson  * @input_addr is an InputAddr associated with the node represented by mci.
64093c2df58SDoug Thompson  * Translate @input_addr to a DramAddr and return the result.
64193c2df58SDoug Thompson  */
64293c2df58SDoug Thompson static u64 input_addr_to_dram_addr(struct mem_ctl_info *mci, u64 input_addr)
64393c2df58SDoug Thompson {
64493c2df58SDoug Thompson 	struct amd64_pvt *pvt;
64593c2df58SDoug Thompson 	int node_id, intlv_shift;
64693c2df58SDoug Thompson 	u64 bits, dram_addr;
64793c2df58SDoug Thompson 	u32 intlv_sel;
64893c2df58SDoug Thompson 
64993c2df58SDoug Thompson 	/*
65093c2df58SDoug Thompson 	 * Near the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
65193c2df58SDoug Thompson 	 * shows how to translate a DramAddr to an InputAddr. Here we reverse
65293c2df58SDoug Thompson 	 * this procedure. When translating from a DramAddr to an InputAddr, the
65393c2df58SDoug Thompson 	 * bits used for node interleaving are discarded.  Here we recover these
65493c2df58SDoug Thompson 	 * bits from the IntlvSel field of the DRAM Limit register (section
65593c2df58SDoug Thompson 	 * 3.4.4.2) for the node that input_addr is associated with.
65693c2df58SDoug Thompson 	 */
65793c2df58SDoug Thompson 	pvt = mci->pvt_info;
65893c2df58SDoug Thompson 	node_id = pvt->mc_node_id;
65993c2df58SDoug Thompson 	BUG_ON((node_id < 0) || (node_id > 7));
66093c2df58SDoug Thompson 
6617f19bf75SBorislav Petkov 	intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
66293c2df58SDoug Thompson 
66393c2df58SDoug Thompson 	if (intlv_shift == 0) {
66493c2df58SDoug Thompson 		debugf1("    InputAddr 0x%lx translates to DramAddr of "
66593c2df58SDoug Thompson 			"same value\n",	(unsigned long)input_addr);
66693c2df58SDoug Thompson 
66793c2df58SDoug Thompson 		return input_addr;
66893c2df58SDoug Thompson 	}
66993c2df58SDoug Thompson 
670f678b8ccSBorislav Petkov 	bits = ((input_addr & GENMASK(12, 35)) << intlv_shift) +
67193c2df58SDoug Thompson 		(input_addr & 0xfff);
67293c2df58SDoug Thompson 
6737f19bf75SBorislav Petkov 	intlv_sel = dram_intlv_sel(pvt, node_id) & ((1 << intlv_shift) - 1);
67493c2df58SDoug Thompson 	dram_addr = bits + (intlv_sel << 12);
67593c2df58SDoug Thompson 
67693c2df58SDoug Thompson 	debugf1("InputAddr 0x%lx translates to DramAddr 0x%lx "
67793c2df58SDoug Thompson 		"(%d node interleave bits)\n", (unsigned long)input_addr,
67893c2df58SDoug Thompson 		(unsigned long)dram_addr, intlv_shift);
67993c2df58SDoug Thompson 
68093c2df58SDoug Thompson 	return dram_addr;
68193c2df58SDoug Thompson }
68293c2df58SDoug Thompson 
68393c2df58SDoug Thompson /*
68493c2df58SDoug Thompson  * @dram_addr is a DramAddr that maps to the node represented by mci. Convert
68593c2df58SDoug Thompson  * @dram_addr to a SysAddr.
68693c2df58SDoug Thompson  */
68793c2df58SDoug Thompson static u64 dram_addr_to_sys_addr(struct mem_ctl_info *mci, u64 dram_addr)
68893c2df58SDoug Thompson {
68993c2df58SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
6907f19bf75SBorislav Petkov 	u64 hole_base, hole_offset, hole_size, base, sys_addr;
69193c2df58SDoug Thompson 	int ret = 0;
69293c2df58SDoug Thompson 
69393c2df58SDoug Thompson 	ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
69493c2df58SDoug Thompson 				      &hole_size);
69593c2df58SDoug Thompson 	if (!ret) {
69693c2df58SDoug Thompson 		if ((dram_addr >= hole_base) &&
69793c2df58SDoug Thompson 		    (dram_addr < (hole_base + hole_size))) {
69893c2df58SDoug Thompson 			sys_addr = dram_addr + hole_offset;
69993c2df58SDoug Thompson 
70093c2df58SDoug Thompson 			debugf1("using DHAR to translate DramAddr 0x%lx to "
70193c2df58SDoug Thompson 				"SysAddr 0x%lx\n", (unsigned long)dram_addr,
70293c2df58SDoug Thompson 				(unsigned long)sys_addr);
70393c2df58SDoug Thompson 
70493c2df58SDoug Thompson 			return sys_addr;
70593c2df58SDoug Thompson 		}
70693c2df58SDoug Thompson 	}
70793c2df58SDoug Thompson 
7087f19bf75SBorislav Petkov 	base     = get_dram_base(pvt, pvt->mc_node_id);
70993c2df58SDoug Thompson 	sys_addr = dram_addr + base;
71093c2df58SDoug Thompson 
71193c2df58SDoug Thompson 	/*
71293c2df58SDoug Thompson 	 * The sys_addr we have computed up to this point is a 40-bit value
71393c2df58SDoug Thompson 	 * because the k8 deals with 40-bit values.  However, the value we are
71493c2df58SDoug Thompson 	 * supposed to return is a full 64-bit physical address.  The AMD
71593c2df58SDoug Thompson 	 * x86-64 architecture specifies that the most significant implemented
71693c2df58SDoug Thompson 	 * address bit through bit 63 of a physical address must be either all
71793c2df58SDoug Thompson 	 * 0s or all 1s.  Therefore we sign-extend the 40-bit sys_addr to a
71893c2df58SDoug Thompson 	 * 64-bit value below.  See section 3.4.2 of AMD publication 24592:
71993c2df58SDoug Thompson 	 * AMD x86-64 Architecture Programmer's Manual Volume 1 Application
72093c2df58SDoug Thompson 	 * Programming.
72193c2df58SDoug Thompson 	 */
72293c2df58SDoug Thompson 	sys_addr |= ~((sys_addr & (1ull << 39)) - 1);
72393c2df58SDoug Thompson 
72493c2df58SDoug Thompson 	debugf1("    Node %d, DramAddr 0x%lx to SysAddr 0x%lx\n",
72593c2df58SDoug Thompson 		pvt->mc_node_id, (unsigned long)dram_addr,
72693c2df58SDoug Thompson 		(unsigned long)sys_addr);
72793c2df58SDoug Thompson 
72893c2df58SDoug Thompson 	return sys_addr;
72993c2df58SDoug Thompson }
73093c2df58SDoug Thompson 
73193c2df58SDoug Thompson /*
73293c2df58SDoug Thompson  * @input_addr is an InputAddr associated with the node given by mci. Translate
73393c2df58SDoug Thompson  * @input_addr to a SysAddr.
73493c2df58SDoug Thompson  */
73593c2df58SDoug Thompson static inline u64 input_addr_to_sys_addr(struct mem_ctl_info *mci,
73693c2df58SDoug Thompson 					 u64 input_addr)
73793c2df58SDoug Thompson {
73893c2df58SDoug Thompson 	return dram_addr_to_sys_addr(mci,
73993c2df58SDoug Thompson 				     input_addr_to_dram_addr(mci, input_addr));
74093c2df58SDoug Thompson }
74193c2df58SDoug Thompson 
74293c2df58SDoug Thompson /*
74393c2df58SDoug Thompson  * Find the minimum and maximum InputAddr values that map to the given @csrow.
74493c2df58SDoug Thompson  * Pass back these values in *input_addr_min and *input_addr_max.
74593c2df58SDoug Thompson  */
74693c2df58SDoug Thompson static void find_csrow_limits(struct mem_ctl_info *mci, int csrow,
74793c2df58SDoug Thompson 			      u64 *input_addr_min, u64 *input_addr_max)
74893c2df58SDoug Thompson {
74993c2df58SDoug Thompson 	struct amd64_pvt *pvt;
75093c2df58SDoug Thompson 	u64 base, mask;
75193c2df58SDoug Thompson 
75293c2df58SDoug Thompson 	pvt = mci->pvt_info;
75311c75eadSBorislav Petkov 	BUG_ON((csrow < 0) || (csrow >= pvt->csels[0].b_cnt));
75493c2df58SDoug Thompson 
75511c75eadSBorislav Petkov 	get_cs_base_and_mask(pvt, csrow, 0, &base, &mask);
75693c2df58SDoug Thompson 
75793c2df58SDoug Thompson 	*input_addr_min = base & ~mask;
75811c75eadSBorislav Petkov 	*input_addr_max = base | mask;
75993c2df58SDoug Thompson }
76093c2df58SDoug Thompson 
76193c2df58SDoug Thompson /* Map the Error address to a PAGE and PAGE OFFSET. */
76293c2df58SDoug Thompson static inline void error_address_to_page_and_offset(u64 error_address,
76393c2df58SDoug Thompson 						    u32 *page, u32 *offset)
76493c2df58SDoug Thompson {
76593c2df58SDoug Thompson 	*page = (u32) (error_address >> PAGE_SHIFT);
76693c2df58SDoug Thompson 	*offset = ((u32) error_address) & ~PAGE_MASK;
76793c2df58SDoug Thompson }
76893c2df58SDoug Thompson 
76993c2df58SDoug Thompson /*
77093c2df58SDoug Thompson  * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address
77193c2df58SDoug Thompson  * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers
77293c2df58SDoug Thompson  * of a node that detected an ECC memory error.  mci represents the node that
77393c2df58SDoug Thompson  * the error address maps to (possibly different from the node that detected
77493c2df58SDoug Thompson  * the error).  Return the number of the csrow that sys_addr maps to, or -1 on
77593c2df58SDoug Thompson  * error.
77693c2df58SDoug Thompson  */
77793c2df58SDoug Thompson static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
77893c2df58SDoug Thompson {
77993c2df58SDoug Thompson 	int csrow;
78093c2df58SDoug Thompson 
78193c2df58SDoug Thompson 	csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr));
78293c2df58SDoug Thompson 
78393c2df58SDoug Thompson 	if (csrow == -1)
78424f9a7feSBorislav Petkov 		amd64_mc_err(mci, "Failed to translate InputAddr to csrow for "
78593c2df58SDoug Thompson 				  "address 0x%lx\n", (unsigned long)sys_addr);
78693c2df58SDoug Thompson 	return csrow;
78793c2df58SDoug Thompson }
788e2ce7255SDoug Thompson 
789bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
7902da11654SDoug Thompson 
7912da11654SDoug Thompson /*
7922da11654SDoug Thompson  * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
7932da11654SDoug Thompson  * are ECC capable.
7942da11654SDoug Thompson  */
7952da11654SDoug Thompson static enum edac_type amd64_determine_edac_cap(struct amd64_pvt *pvt)
7962da11654SDoug Thompson {
797cb328507SBorislav Petkov 	u8 bit;
798584fcff4SBorislav Petkov 	enum dev_type edac_cap = EDAC_FLAG_NONE;
7992da11654SDoug Thompson 
8001433eb99SBorislav Petkov 	bit = (boot_cpu_data.x86 > 0xf || pvt->ext_model >= K8_REV_F)
8012da11654SDoug Thompson 		? 19
8022da11654SDoug Thompson 		: 17;
8032da11654SDoug Thompson 
804584fcff4SBorislav Petkov 	if (pvt->dclr0 & BIT(bit))
8052da11654SDoug Thompson 		edac_cap = EDAC_FLAG_SECDED;
8062da11654SDoug Thompson 
8072da11654SDoug Thompson 	return edac_cap;
8082da11654SDoug Thompson }
8092da11654SDoug Thompson 
8102da11654SDoug Thompson 
8118566c4dfSBorislav Petkov static void amd64_debug_display_dimm_sizes(int ctrl, struct amd64_pvt *pvt);
8122da11654SDoug Thompson 
81368798e17SBorislav Petkov static void amd64_dump_dramcfg_low(u32 dclr, int chan)
81468798e17SBorislav Petkov {
81568798e17SBorislav Petkov 	debugf1("F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
81668798e17SBorislav Petkov 
81768798e17SBorislav Petkov 	debugf1("  DIMM type: %sbuffered; all DIMMs support ECC: %s\n",
81868798e17SBorislav Petkov 		(dclr & BIT(16)) ?  "un" : "",
81968798e17SBorislav Petkov 		(dclr & BIT(19)) ? "yes" : "no");
82068798e17SBorislav Petkov 
82168798e17SBorislav Petkov 	debugf1("  PAR/ERR parity: %s\n",
82268798e17SBorislav Petkov 		(dclr & BIT(8)) ?  "enabled" : "disabled");
82368798e17SBorislav Petkov 
824cb328507SBorislav Petkov 	if (boot_cpu_data.x86 == 0x10)
82568798e17SBorislav Petkov 		debugf1("  DCT 128bit mode width: %s\n",
82668798e17SBorislav Petkov 			(dclr & BIT(11)) ?  "128b" : "64b");
82768798e17SBorislav Petkov 
82868798e17SBorislav Petkov 	debugf1("  x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
82968798e17SBorislav Petkov 		(dclr & BIT(12)) ?  "yes" : "no",
83068798e17SBorislav Petkov 		(dclr & BIT(13)) ?  "yes" : "no",
83168798e17SBorislav Petkov 		(dclr & BIT(14)) ?  "yes" : "no",
83268798e17SBorislav Petkov 		(dclr & BIT(15)) ?  "yes" : "no");
83368798e17SBorislav Petkov }
83468798e17SBorislav Petkov 
8352da11654SDoug Thompson /* Display and decode various NB registers for debug purposes. */
836b2b0c605SBorislav Petkov static void dump_misc_regs(struct amd64_pvt *pvt)
8372da11654SDoug Thompson {
83868798e17SBorislav Petkov 	debugf1("F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
8392da11654SDoug Thompson 
84068798e17SBorislav Petkov 	debugf1("  NB two channel DRAM capable: %s\n",
8415980bb9cSBorislav Petkov 		(pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
84268798e17SBorislav Petkov 
84368798e17SBorislav Petkov 	debugf1("  ECC capable: %s, ChipKill ECC capable: %s\n",
8445980bb9cSBorislav Petkov 		(pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
8455980bb9cSBorislav Petkov 		(pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
84668798e17SBorislav Petkov 
84768798e17SBorislav Petkov 	amd64_dump_dramcfg_low(pvt->dclr0, 0);
8482da11654SDoug Thompson 
8498de1d91eSBorislav Petkov 	debugf1("F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
8502da11654SDoug Thompson 
8518de1d91eSBorislav Petkov 	debugf1("F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, "
8528de1d91eSBorislav Petkov 			"offset: 0x%08x\n",
853bc21fa57SBorislav Petkov 			pvt->dhar, dhar_base(pvt),
854bc21fa57SBorislav Petkov 			(boot_cpu_data.x86 == 0xf) ? k8_dhar_offset(pvt)
855bc21fa57SBorislav Petkov 						   : f10_dhar_offset(pvt));
8562da11654SDoug Thompson 
857c8e518d5SBorislav Petkov 	debugf1("  DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
8582da11654SDoug Thompson 
8598566c4dfSBorislav Petkov 	amd64_debug_display_dimm_sizes(0, pvt);
8604d796364SBorislav Petkov 
8614d796364SBorislav Petkov 	/* everything below this point is Fam10h and above */
8624d796364SBorislav Petkov 	if (boot_cpu_data.x86 == 0xf)
8632da11654SDoug Thompson 		return;
8644d796364SBorislav Petkov 
8654d796364SBorislav Petkov 	amd64_debug_display_dimm_sizes(1, pvt);
8662da11654SDoug Thompson 
86724f9a7feSBorislav Petkov 	amd64_info("using %s syndromes.\n", ((pvt->syn_type == 8) ? "x8" : "x4"));
868ad6a32e9SBorislav Petkov 
8698de1d91eSBorislav Petkov 	/* Only if NOT ganged does dclr1 have valid info */
87068798e17SBorislav Petkov 	if (!dct_ganging_enabled(pvt))
87168798e17SBorislav Petkov 		amd64_dump_dramcfg_low(pvt->dclr1, 1);
8722da11654SDoug Thompson }
8732da11654SDoug Thompson 
87494be4bffSDoug Thompson /*
87511c75eadSBorislav Petkov  * see BKDG, F2x[1,0][5C:40], F2[1,0][6C:60]
87694be4bffSDoug Thompson  */
87711c75eadSBorislav Petkov static void prep_chip_selects(struct amd64_pvt *pvt)
87894be4bffSDoug Thompson {
8791433eb99SBorislav Petkov 	if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_F) {
88011c75eadSBorislav Petkov 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
88111c75eadSBorislav Petkov 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8;
8829d858bb1SBorislav Petkov 	} else {
88311c75eadSBorislav Petkov 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
88411c75eadSBorislav Petkov 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4;
8859d858bb1SBorislav Petkov 	}
88694be4bffSDoug Thompson }
88794be4bffSDoug Thompson 
88894be4bffSDoug Thompson /*
88911c75eadSBorislav Petkov  * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers
89094be4bffSDoug Thompson  */
891b2b0c605SBorislav Petkov static void read_dct_base_mask(struct amd64_pvt *pvt)
89294be4bffSDoug Thompson {
89311c75eadSBorislav Petkov 	int cs;
89494be4bffSDoug Thompson 
89511c75eadSBorislav Petkov 	prep_chip_selects(pvt);
89694be4bffSDoug Thompson 
89711c75eadSBorislav Petkov 	for_each_chip_select(cs, 0, pvt) {
89811c75eadSBorislav Petkov 		u32 reg0   = DCSB0 + (cs * 4);
89911c75eadSBorislav Petkov 		u32 reg1   = DCSB1 + (cs * 4);
90011c75eadSBorislav Petkov 		u32 *base0 = &pvt->csels[0].csbases[cs];
90111c75eadSBorislav Petkov 		u32 *base1 = &pvt->csels[1].csbases[cs];
902b2b0c605SBorislav Petkov 
90311c75eadSBorislav Petkov 		if (!amd64_read_dct_pci_cfg(pvt, reg0, base0))
90494be4bffSDoug Thompson 			debugf0("  DCSB0[%d]=0x%08x reg: F2x%x\n",
90511c75eadSBorislav Petkov 				cs, *base0, reg0);
90694be4bffSDoug Thompson 
90711c75eadSBorislav Petkov 		if (boot_cpu_data.x86 == 0xf || dct_ganging_enabled(pvt))
90811c75eadSBorislav Petkov 			continue;
909b2b0c605SBorislav Petkov 
91011c75eadSBorislav Petkov 		if (!amd64_read_dct_pci_cfg(pvt, reg1, base1))
91194be4bffSDoug Thompson 			debugf0("  DCSB1[%d]=0x%08x reg: F2x%x\n",
91211c75eadSBorislav Petkov 				cs, *base1, reg1);
91394be4bffSDoug Thompson 	}
91494be4bffSDoug Thompson 
91511c75eadSBorislav Petkov 	for_each_chip_select_mask(cs, 0, pvt) {
91611c75eadSBorislav Petkov 		u32 reg0   = DCSM0 + (cs * 4);
91711c75eadSBorislav Petkov 		u32 reg1   = DCSM1 + (cs * 4);
91811c75eadSBorislav Petkov 		u32 *mask0 = &pvt->csels[0].csmasks[cs];
91911c75eadSBorislav Petkov 		u32 *mask1 = &pvt->csels[1].csmasks[cs];
920b2b0c605SBorislav Petkov 
92111c75eadSBorislav Petkov 		if (!amd64_read_dct_pci_cfg(pvt, reg0, mask0))
92294be4bffSDoug Thompson 			debugf0("    DCSM0[%d]=0x%08x reg: F2x%x\n",
92311c75eadSBorislav Petkov 				cs, *mask0, reg0);
92494be4bffSDoug Thompson 
92511c75eadSBorislav Petkov 		if (boot_cpu_data.x86 == 0xf || dct_ganging_enabled(pvt))
92611c75eadSBorislav Petkov 			continue;
927b2b0c605SBorislav Petkov 
92811c75eadSBorislav Petkov 		if (!amd64_read_dct_pci_cfg(pvt, reg1, mask1))
92994be4bffSDoug Thompson 			debugf0("    DCSM1[%d]=0x%08x reg: F2x%x\n",
93011c75eadSBorislav Petkov 				cs, *mask1, reg1);
93194be4bffSDoug Thompson 	}
9326ba5dcdcSBorislav Petkov }
93394be4bffSDoug Thompson 
93424f9a7feSBorislav Petkov static enum mem_type amd64_determine_memory_type(struct amd64_pvt *pvt, int cs)
93594be4bffSDoug Thompson {
93694be4bffSDoug Thompson 	enum mem_type type;
93794be4bffSDoug Thompson 
938cb328507SBorislav Petkov 	/* F15h supports only DDR3 */
939cb328507SBorislav Petkov 	if (boot_cpu_data.x86 >= 0x15)
940cb328507SBorislav Petkov 		type = (pvt->dclr0 & BIT(16)) ?	MEM_DDR3 : MEM_RDDR3;
941cb328507SBorislav Petkov 	else if (boot_cpu_data.x86 == 0x10 || pvt->ext_model >= K8_REV_F) {
9426b4c0bdeSBorislav Petkov 		if (pvt->dchr0 & DDR3_MODE)
9436b4c0bdeSBorislav Petkov 			type = (pvt->dclr0 & BIT(16)) ?	MEM_DDR3 : MEM_RDDR3;
9446b4c0bdeSBorislav Petkov 		else
94594be4bffSDoug Thompson 			type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
94694be4bffSDoug Thompson 	} else {
94794be4bffSDoug Thompson 		type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
94894be4bffSDoug Thompson 	}
94994be4bffSDoug Thompson 
95024f9a7feSBorislav Petkov 	amd64_info("CS%d: %s\n", cs, edac_mem_types[type]);
95194be4bffSDoug Thompson 
95294be4bffSDoug Thompson 	return type;
95394be4bffSDoug Thompson }
95494be4bffSDoug Thompson 
955cb328507SBorislav Petkov /* Get the number of DCT channels the memory controller is using. */
956ddff876dSDoug Thompson static int k8_early_channel_count(struct amd64_pvt *pvt)
957ddff876dSDoug Thompson {
958cb328507SBorislav Petkov 	int flag;
959ddff876dSDoug Thompson 
9609f56da0eSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F)
961ddff876dSDoug Thompson 		/* RevF (NPT) and later */
962ddff876dSDoug Thompson 		flag = pvt->dclr0 & F10_WIDTH_128;
9639f56da0eSBorislav Petkov 	else
964ddff876dSDoug Thompson 		/* RevE and earlier */
965ddff876dSDoug Thompson 		flag = pvt->dclr0 & REVE_WIDTH_128;
966ddff876dSDoug Thompson 
967ddff876dSDoug Thompson 	/* not used */
968ddff876dSDoug Thompson 	pvt->dclr1 = 0;
969ddff876dSDoug Thompson 
970ddff876dSDoug Thompson 	return (flag) ? 2 : 1;
971ddff876dSDoug Thompson }
972ddff876dSDoug Thompson 
97370046624SBorislav Petkov /* On F10h and later ErrAddr is MC4_ADDR[47:1] */
97470046624SBorislav Petkov static u64 get_error_address(struct mce *m)
975ddff876dSDoug Thompson {
97670046624SBorislav Petkov 	u8 start_bit = 1;
97770046624SBorislav Petkov 	u8 end_bit   = 47;
97870046624SBorislav Petkov 
97970046624SBorislav Petkov 	if (boot_cpu_data.x86 == 0xf) {
98070046624SBorislav Petkov 		start_bit = 3;
98170046624SBorislav Petkov 		end_bit   = 39;
98270046624SBorislav Petkov 	}
98370046624SBorislav Petkov 
98470046624SBorislav Petkov 	return m->addr & GENMASK(start_bit, end_bit);
985ddff876dSDoug Thompson }
986ddff876dSDoug Thompson 
9877f19bf75SBorislav Petkov static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
988ddff876dSDoug Thompson {
9897f19bf75SBorislav Petkov 	u32 off = range << 3;
990ddff876dSDoug Thompson 
9917f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off,  &pvt->ranges[range].base.lo);
9927f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo);
993ddff876dSDoug Thompson 
9947f19bf75SBorislav Petkov 	if (boot_cpu_data.x86 == 0xf)
9957f19bf75SBorislav Petkov 		return;
996ddff876dSDoug Thompson 
9977f19bf75SBorislav Petkov 	if (!dram_rw(pvt, range))
9987f19bf75SBorislav Petkov 		return;
999ddff876dSDoug Thompson 
10007f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off,  &pvt->ranges[range].base.hi);
10017f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi);
1002ddff876dSDoug Thompson }
1003ddff876dSDoug Thompson 
1004f192c7b1SBorislav Petkov static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
1005f192c7b1SBorislav Petkov 				    u16 syndrome)
1006ddff876dSDoug Thompson {
1007ddff876dSDoug Thompson 	struct mem_ctl_info *src_mci;
1008f192c7b1SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
1009ddff876dSDoug Thompson 	int channel, csrow;
1010ddff876dSDoug Thompson 	u32 page, offset;
1011ddff876dSDoug Thompson 
1012ddff876dSDoug Thompson 	/* CHIPKILL enabled */
1013f192c7b1SBorislav Petkov 	if (pvt->nbcfg & NBCFG_CHIPKILL) {
1014bfc04aecSBorislav Petkov 		channel = get_channel_from_ecc_syndrome(mci, syndrome);
1015ddff876dSDoug Thompson 		if (channel < 0) {
1016ddff876dSDoug Thompson 			/*
1017ddff876dSDoug Thompson 			 * Syndrome didn't map, so we don't know which of the
1018ddff876dSDoug Thompson 			 * 2 DIMMs is in error. So we need to ID 'both' of them
1019ddff876dSDoug Thompson 			 * as suspect.
1020ddff876dSDoug Thompson 			 */
102124f9a7feSBorislav Petkov 			amd64_mc_warn(mci, "unknown syndrome 0x%04x - possible "
1022ad6a32e9SBorislav Petkov 					   "error reporting race\n", syndrome);
1023ddff876dSDoug Thompson 			edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
1024ddff876dSDoug Thompson 			return;
1025ddff876dSDoug Thompson 		}
1026ddff876dSDoug Thompson 	} else {
1027ddff876dSDoug Thompson 		/*
1028ddff876dSDoug Thompson 		 * non-chipkill ecc mode
1029ddff876dSDoug Thompson 		 *
1030ddff876dSDoug Thompson 		 * The k8 documentation is unclear about how to determine the
1031ddff876dSDoug Thompson 		 * channel number when using non-chipkill memory.  This method
1032ddff876dSDoug Thompson 		 * was obtained from email communication with someone at AMD.
1033ddff876dSDoug Thompson 		 * (Wish the email was placed in this comment - norsk)
1034ddff876dSDoug Thompson 		 */
103544e9e2eeSBorislav Petkov 		channel = ((sys_addr & BIT(3)) != 0);
1036ddff876dSDoug Thompson 	}
1037ddff876dSDoug Thompson 
1038ddff876dSDoug Thompson 	/*
1039ddff876dSDoug Thompson 	 * Find out which node the error address belongs to. This may be
1040ddff876dSDoug Thompson 	 * different from the node that detected the error.
1041ddff876dSDoug Thompson 	 */
104244e9e2eeSBorislav Petkov 	src_mci = find_mc_by_sys_addr(mci, sys_addr);
10432cff18c2SKeith Mannthey 	if (!src_mci) {
104424f9a7feSBorislav Petkov 		amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
104544e9e2eeSBorislav Petkov 			     (unsigned long)sys_addr);
1046ddff876dSDoug Thompson 		edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
1047ddff876dSDoug Thompson 		return;
1048ddff876dSDoug Thompson 	}
1049ddff876dSDoug Thompson 
105044e9e2eeSBorislav Petkov 	/* Now map the sys_addr to a CSROW */
105144e9e2eeSBorislav Petkov 	csrow = sys_addr_to_csrow(src_mci, sys_addr);
1052ddff876dSDoug Thompson 	if (csrow < 0) {
1053ddff876dSDoug Thompson 		edac_mc_handle_ce_no_info(src_mci, EDAC_MOD_STR);
1054ddff876dSDoug Thompson 	} else {
105544e9e2eeSBorislav Petkov 		error_address_to_page_and_offset(sys_addr, &page, &offset);
1056ddff876dSDoug Thompson 
1057ddff876dSDoug Thompson 		edac_mc_handle_ce(src_mci, page, offset, syndrome, csrow,
1058ddff876dSDoug Thompson 				  channel, EDAC_MOD_STR);
1059ddff876dSDoug Thompson 	}
1060ddff876dSDoug Thompson }
1061ddff876dSDoug Thompson 
10621433eb99SBorislav Petkov static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, int cs_mode)
1063ddff876dSDoug Thompson {
10641433eb99SBorislav Petkov 	int *dbam_map;
1065ddff876dSDoug Thompson 
10661433eb99SBorislav Petkov 	if (pvt->ext_model >= K8_REV_F)
10671433eb99SBorislav Petkov 		dbam_map = ddr2_dbam;
10681433eb99SBorislav Petkov 	else if (pvt->ext_model >= K8_REV_D)
10691433eb99SBorislav Petkov 		dbam_map = ddr2_dbam_revD;
10701433eb99SBorislav Petkov 	else
10711433eb99SBorislav Petkov 		dbam_map = ddr2_dbam_revCG;
1072ddff876dSDoug Thompson 
10731433eb99SBorislav Petkov 	return dbam_map[cs_mode];
1074ddff876dSDoug Thompson }
1075ddff876dSDoug Thompson 
10761afd3c98SDoug Thompson /*
10771afd3c98SDoug Thompson  * Get the number of DCT channels in use.
10781afd3c98SDoug Thompson  *
10791afd3c98SDoug Thompson  * Return:
10801afd3c98SDoug Thompson  *	number of Memory Channels in operation
10811afd3c98SDoug Thompson  * Pass back:
10821afd3c98SDoug Thompson  *	contents of the DCL0_LOW register
10831afd3c98SDoug Thompson  */
10847d20d14dSBorislav Petkov static int f1x_early_channel_count(struct amd64_pvt *pvt)
10851afd3c98SDoug Thompson {
10866ba5dcdcSBorislav Petkov 	int i, j, channels = 0;
1087ddff876dSDoug Thompson 
10887d20d14dSBorislav Petkov 	/* On F10h, if we are in 128 bit mode, then we are using 2 channels */
10897d20d14dSBorislav Petkov 	if (boot_cpu_data.x86 == 0x10 && (pvt->dclr0 & F10_WIDTH_128))
10907d20d14dSBorislav Petkov 		return 2;
10911afd3c98SDoug Thompson 
10921afd3c98SDoug Thompson 	/*
1093d16149e8SBorislav Petkov 	 * Need to check if in unganged mode: In such, there are 2 channels,
1094d16149e8SBorislav Petkov 	 * but they are not in 128 bit mode and thus the above 'dclr0' status
1095d16149e8SBorislav Petkov 	 * bit will be OFF.
10961afd3c98SDoug Thompson 	 *
10971afd3c98SDoug Thompson 	 * Need to check DCT0[0] and DCT1[0] to see if only one of them has
10981afd3c98SDoug Thompson 	 * their CSEnable bit on. If so, then SINGLE DIMM case.
10991afd3c98SDoug Thompson 	 */
1100d16149e8SBorislav Petkov 	debugf0("Data width is not 128 bits - need more decoding\n");
11011afd3c98SDoug Thompson 
11021afd3c98SDoug Thompson 	/*
11031afd3c98SDoug Thompson 	 * Check DRAM Bank Address Mapping values for each DIMM to see if there
11041afd3c98SDoug Thompson 	 * is more than just one DIMM present in unganged mode. Need to check
11051afd3c98SDoug Thompson 	 * both controllers since DIMMs can be placed in either one.
11061afd3c98SDoug Thompson 	 */
1107525a1b20SBorislav Petkov 	for (i = 0; i < 2; i++) {
1108525a1b20SBorislav Petkov 		u32 dbam = (i ? pvt->dbam1 : pvt->dbam0);
11091afd3c98SDoug Thompson 
111057a30854SWan Wei 		for (j = 0; j < 4; j++) {
111157a30854SWan Wei 			if (DBAM_DIMM(j, dbam) > 0) {
11121afd3c98SDoug Thompson 				channels++;
111357a30854SWan Wei 				break;
11141afd3c98SDoug Thompson 			}
111557a30854SWan Wei 		}
111657a30854SWan Wei 	}
11171afd3c98SDoug Thompson 
1118d16149e8SBorislav Petkov 	if (channels > 2)
1119d16149e8SBorislav Petkov 		channels = 2;
1120d16149e8SBorislav Petkov 
112124f9a7feSBorislav Petkov 	amd64_info("MCT channel count: %d\n", channels);
11221afd3c98SDoug Thompson 
11231afd3c98SDoug Thompson 	return channels;
11241afd3c98SDoug Thompson }
11251afd3c98SDoug Thompson 
11261433eb99SBorislav Petkov static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, int cs_mode)
11271afd3c98SDoug Thompson {
11281433eb99SBorislav Petkov 	int *dbam_map;
11291433eb99SBorislav Petkov 
11301433eb99SBorislav Petkov 	if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
11311433eb99SBorislav Petkov 		dbam_map = ddr3_dbam;
11321433eb99SBorislav Petkov 	else
11331433eb99SBorislav Petkov 		dbam_map = ddr2_dbam;
11341433eb99SBorislav Petkov 
11351433eb99SBorislav Petkov 	return dbam_map[cs_mode];
11361afd3c98SDoug Thompson }
11371afd3c98SDoug Thompson 
11386163b5d4SDoug Thompson static void f10_read_dram_ctl_register(struct amd64_pvt *pvt)
11396163b5d4SDoug Thompson {
11406163b5d4SDoug Thompson 
114178da121eSBorislav Petkov 	if (!amd64_read_dct_pci_cfg(pvt, DCT_SEL_LO, &pvt->dct_sel_lo)) {
114278da121eSBorislav Petkov 		debugf0("F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
114378da121eSBorislav Petkov 			pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
11446163b5d4SDoug Thompson 
114578da121eSBorislav Petkov 		debugf0("  mode: %s, All DCTs on: %s\n",
114672381bd5SBorislav Petkov 			(dct_ganging_enabled(pvt) ? "ganged" : "unganged"),
114772381bd5SBorislav Petkov 			(dct_dram_enabled(pvt) ? "yes"   : "no"));
11486163b5d4SDoug Thompson 
114972381bd5SBorislav Petkov 		if (!dct_ganging_enabled(pvt))
115072381bd5SBorislav Petkov 			debugf0("  Address range split per DCT: %s\n",
115172381bd5SBorislav Petkov 				(dct_high_range_enabled(pvt) ? "yes" : "no"));
115272381bd5SBorislav Petkov 
115378da121eSBorislav Petkov 		debugf0("  data interleave for ECC: %s, "
115472381bd5SBorislav Petkov 			"DRAM cleared since last warm reset: %s\n",
115572381bd5SBorislav Petkov 			(dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
115672381bd5SBorislav Petkov 			(dct_memory_cleared(pvt) ? "yes" : "no"));
115772381bd5SBorislav Petkov 
115878da121eSBorislav Petkov 		debugf0("  channel interleave: %s, "
115978da121eSBorislav Petkov 			"interleave bits selector: 0x%x\n",
116072381bd5SBorislav Petkov 			(dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
11616163b5d4SDoug Thompson 			dct_sel_interleave_addr(pvt));
11626163b5d4SDoug Thompson 	}
11636163b5d4SDoug Thompson 
116478da121eSBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, DCT_SEL_HI, &pvt->dct_sel_hi);
11656163b5d4SDoug Thompson }
11666163b5d4SDoug Thompson 
1167f71d0a05SDoug Thompson /*
1168229a7a11SBorislav Petkov  * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory
1169f71d0a05SDoug Thompson  * Interleaving Modes.
1170f71d0a05SDoug Thompson  */
117111c75eadSBorislav Petkov static u8 f10_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
1172229a7a11SBorislav Petkov 				bool hi_range_sel, u8 intlv_en)
11736163b5d4SDoug Thompson {
117478da121eSBorislav Petkov 	u32 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1;
11756163b5d4SDoug Thompson 
11766163b5d4SDoug Thompson 	if (dct_ganging_enabled(pvt))
1177229a7a11SBorislav Petkov 		return 0;
1178229a7a11SBorislav Petkov 
1179229a7a11SBorislav Petkov 	if (hi_range_sel)
1180229a7a11SBorislav Petkov 		return dct_sel_high;
1181229a7a11SBorislav Petkov 
1182f71d0a05SDoug Thompson 	/*
1183f71d0a05SDoug Thompson 	 * see F2x110[DctSelIntLvAddr] - channel interleave mode
1184f71d0a05SDoug Thompson 	 */
1185229a7a11SBorislav Petkov 	if (dct_interleave_enabled(pvt)) {
1186229a7a11SBorislav Petkov 		u8 intlv_addr = dct_sel_interleave_addr(pvt);
11876163b5d4SDoug Thompson 
1188229a7a11SBorislav Petkov 		/* return DCT select function: 0=DCT0, 1=DCT1 */
1189229a7a11SBorislav Petkov 		if (!intlv_addr)
1190229a7a11SBorislav Petkov 			return sys_addr >> 6 & 1;
11916163b5d4SDoug Thompson 
1192229a7a11SBorislav Petkov 		if (intlv_addr & 0x2) {
1193229a7a11SBorislav Petkov 			u8 shift = intlv_addr & 0x1 ? 9 : 6;
1194229a7a11SBorislav Petkov 			u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) % 2;
1195229a7a11SBorislav Petkov 
1196229a7a11SBorislav Petkov 			return ((sys_addr >> shift) & 1) ^ temp;
11976163b5d4SDoug Thompson 		}
11986163b5d4SDoug Thompson 
1199229a7a11SBorislav Petkov 		return (sys_addr >> (12 + hweight8(intlv_en))) & 1;
1200229a7a11SBorislav Petkov 	}
1201229a7a11SBorislav Petkov 
1202229a7a11SBorislav Petkov 	if (dct_high_range_enabled(pvt))
1203229a7a11SBorislav Petkov 		return ~dct_sel_high & 1;
12046163b5d4SDoug Thompson 
12056163b5d4SDoug Thompson 	return 0;
12066163b5d4SDoug Thompson }
12076163b5d4SDoug Thompson 
1208c8e518d5SBorislav Petkov /* Convert the sys_addr to the normalized DCT address */
1209c8e518d5SBorislav Petkov static u64 f10_get_norm_dct_addr(struct amd64_pvt *pvt, int range,
1210c8e518d5SBorislav Petkov 				 u64 sys_addr, bool hi_rng,
1211c8e518d5SBorislav Petkov 				 u32 dct_sel_base_addr)
12126163b5d4SDoug Thompson {
12136163b5d4SDoug Thompson 	u64 chan_off;
1214c8e518d5SBorislav Petkov 	u64 dram_base		= get_dram_base(pvt, range);
1215c8e518d5SBorislav Petkov 	u64 hole_off		= f10_dhar_offset(pvt);
1216c8e518d5SBorislav Petkov 	u32 hole_valid		= dhar_valid(pvt);
1217c8e518d5SBorislav Petkov 	u64 dct_sel_base_off	= (pvt->dct_sel_hi & 0xFFFFFC00) << 16;
12186163b5d4SDoug Thompson 
1219c8e518d5SBorislav Petkov 	if (hi_rng) {
1220c8e518d5SBorislav Petkov 		/*
1221c8e518d5SBorislav Petkov 		 * if
1222c8e518d5SBorislav Petkov 		 * base address of high range is below 4Gb
1223c8e518d5SBorislav Petkov 		 * (bits [47:27] at [31:11])
1224c8e518d5SBorislav Petkov 		 * DRAM address space on this DCT is hoisted above 4Gb	&&
1225c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1226c8e518d5SBorislav Petkov 		 *
1227c8e518d5SBorislav Petkov 		 *	remove hole offset from sys_addr
1228c8e518d5SBorislav Petkov 		 * else
1229c8e518d5SBorislav Petkov 		 *	remove high range offset from sys_addr
1230c8e518d5SBorislav Petkov 		 */
1231c8e518d5SBorislav Petkov 		if ((!(dct_sel_base_addr >> 16) ||
1232c8e518d5SBorislav Petkov 		     dct_sel_base_addr < dhar_base(pvt)) &&
1233c8e518d5SBorislav Petkov 		    hole_valid &&
1234c8e518d5SBorislav Petkov 		    (sys_addr >= BIT_64(32)))
1235bc21fa57SBorislav Petkov 			chan_off = hole_off;
12366163b5d4SDoug Thompson 		else
12376163b5d4SDoug Thompson 			chan_off = dct_sel_base_off;
12386163b5d4SDoug Thompson 	} else {
1239c8e518d5SBorislav Petkov 		/*
1240c8e518d5SBorislav Petkov 		 * if
1241c8e518d5SBorislav Petkov 		 * we have a valid hole		&&
1242c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1243c8e518d5SBorislav Petkov 		 *
1244c8e518d5SBorislav Petkov 		 *	remove hole
1245c8e518d5SBorislav Petkov 		 * else
1246c8e518d5SBorislav Petkov 		 *	remove dram base to normalize to DCT address
1247c8e518d5SBorislav Petkov 		 */
1248c8e518d5SBorislav Petkov 		if (hole_valid && (sys_addr >= BIT_64(32)))
1249bc21fa57SBorislav Petkov 			chan_off = hole_off;
12506163b5d4SDoug Thompson 		else
1251c8e518d5SBorislav Petkov 			chan_off = dram_base;
12526163b5d4SDoug Thompson 	}
12536163b5d4SDoug Thompson 
1254c8e518d5SBorislav Petkov 	return (sys_addr & GENMASK(6,47)) - (chan_off & GENMASK(23,47));
12556163b5d4SDoug Thompson }
12566163b5d4SDoug Thompson 
12576163b5d4SDoug Thompson /* Hack for the time being - Can we get this from BIOS?? */
12586163b5d4SDoug Thompson #define	CH0SPARE_RANK	0
12596163b5d4SDoug Thompson #define	CH1SPARE_RANK	1
12606163b5d4SDoug Thompson 
12616163b5d4SDoug Thompson /*
12626163b5d4SDoug Thompson  * checks if the csrow passed in is marked as SPARED, if so returns the new
12636163b5d4SDoug Thompson  * spare row
12646163b5d4SDoug Thompson  */
126511c75eadSBorislav Petkov static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow)
12666163b5d4SDoug Thompson {
12676163b5d4SDoug Thompson 	u32 swap_done;
12686163b5d4SDoug Thompson 	u32 bad_dram_cs;
12696163b5d4SDoug Thompson 
12706163b5d4SDoug Thompson 	/* Depending on channel, isolate respective SPARING info */
127111c75eadSBorislav Petkov 	if (dct) {
12726163b5d4SDoug Thompson 		swap_done = F10_ONLINE_SPARE_SWAPDONE1(pvt->online_spare);
12736163b5d4SDoug Thompson 		bad_dram_cs = F10_ONLINE_SPARE_BADDRAM_CS1(pvt->online_spare);
12746163b5d4SDoug Thompson 		if (swap_done && (csrow == bad_dram_cs))
12756163b5d4SDoug Thompson 			csrow = CH1SPARE_RANK;
12766163b5d4SDoug Thompson 	} else {
12776163b5d4SDoug Thompson 		swap_done = F10_ONLINE_SPARE_SWAPDONE0(pvt->online_spare);
12786163b5d4SDoug Thompson 		bad_dram_cs = F10_ONLINE_SPARE_BADDRAM_CS0(pvt->online_spare);
12796163b5d4SDoug Thompson 		if (swap_done && (csrow == bad_dram_cs))
12806163b5d4SDoug Thompson 			csrow = CH0SPARE_RANK;
12816163b5d4SDoug Thompson 	}
12826163b5d4SDoug Thompson 	return csrow;
12836163b5d4SDoug Thompson }
12846163b5d4SDoug Thompson 
12856163b5d4SDoug Thompson /*
12866163b5d4SDoug Thompson  * Iterate over the DRAM DCT "base" and "mask" registers looking for a
12876163b5d4SDoug Thompson  * SystemAddr match on the specified 'ChannelSelect' and 'NodeID'
12886163b5d4SDoug Thompson  *
12896163b5d4SDoug Thompson  * Return:
12906163b5d4SDoug Thompson  *	-EINVAL:  NOT FOUND
12916163b5d4SDoug Thompson  *	0..csrow = Chip-Select Row
12926163b5d4SDoug Thompson  */
129311c75eadSBorislav Petkov static int f10_lookup_addr_in_dct(u64 in_addr, u32 nid, u8 dct)
12946163b5d4SDoug Thompson {
12956163b5d4SDoug Thompson 	struct mem_ctl_info *mci;
12966163b5d4SDoug Thompson 	struct amd64_pvt *pvt;
129711c75eadSBorislav Petkov 	u64 cs_base, cs_mask;
12986163b5d4SDoug Thompson 	int cs_found = -EINVAL;
12996163b5d4SDoug Thompson 	int csrow;
13006163b5d4SDoug Thompson 
1301cc4d8860SBorislav Petkov 	mci = mcis[nid];
13026163b5d4SDoug Thompson 	if (!mci)
13036163b5d4SDoug Thompson 		return cs_found;
13046163b5d4SDoug Thompson 
13056163b5d4SDoug Thompson 	pvt = mci->pvt_info;
13066163b5d4SDoug Thompson 
130711c75eadSBorislav Petkov 	debugf1("input addr: 0x%llx, DCT: %d\n", in_addr, dct);
13086163b5d4SDoug Thompson 
130911c75eadSBorislav Petkov 	for_each_chip_select(csrow, dct, pvt) {
131011c75eadSBorislav Petkov 		if (!csrow_enabled(csrow, dct, pvt))
13116163b5d4SDoug Thompson 			continue;
13126163b5d4SDoug Thompson 
131311c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask);
13146163b5d4SDoug Thompson 
131511c75eadSBorislav Petkov 		debugf1("    CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
13166163b5d4SDoug Thompson 			csrow, cs_base, cs_mask);
13176163b5d4SDoug Thompson 
131811c75eadSBorislav Petkov 		cs_mask = ~cs_mask;
13196163b5d4SDoug Thompson 
132011c75eadSBorislav Petkov 		debugf1("    (InputAddr & ~CSMask)=0x%llx "
132111c75eadSBorislav Petkov 			"(CSBase & ~CSMask)=0x%llx\n",
132211c75eadSBorislav Petkov 			(in_addr & cs_mask), (cs_base & cs_mask));
13236163b5d4SDoug Thompson 
132411c75eadSBorislav Petkov 		if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
132511c75eadSBorislav Petkov 			cs_found = f10_process_possible_spare(pvt, dct, csrow);
13266163b5d4SDoug Thompson 
13276163b5d4SDoug Thompson 			debugf1(" MATCH csrow=%d\n", cs_found);
13286163b5d4SDoug Thompson 			break;
13296163b5d4SDoug Thompson 		}
13306163b5d4SDoug Thompson 	}
13316163b5d4SDoug Thompson 	return cs_found;
13326163b5d4SDoug Thompson }
13336163b5d4SDoug Thompson 
1334f71d0a05SDoug Thompson /* For a given @dram_range, check if @sys_addr falls within it. */
13357f19bf75SBorislav Petkov static int f10_match_to_this_node(struct amd64_pvt *pvt, int range,
1336f71d0a05SDoug Thompson 				  u64 sys_addr, int *nid, int *chan_sel)
1337f71d0a05SDoug Thompson {
1338229a7a11SBorislav Petkov 	int cs_found = -EINVAL;
1339c8e518d5SBorislav Petkov 	u64 chan_addr;
1340c8e518d5SBorislav Petkov 	u32 tmp, dct_sel_base;
134111c75eadSBorislav Petkov 	u8 channel;
1342229a7a11SBorislav Petkov 	bool high_range = false;
1343f71d0a05SDoug Thompson 
13447f19bf75SBorislav Petkov 	u8 node_id    = dram_dst_node(pvt, range);
1345229a7a11SBorislav Petkov 	u8 intlv_en   = dram_intlv_en(pvt, range);
13467f19bf75SBorislav Petkov 	u32 intlv_sel = dram_intlv_sel(pvt, range);
1347f71d0a05SDoug Thompson 
1348c8e518d5SBorislav Petkov 	debugf1("(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
1349c8e518d5SBorislav Petkov 		range, sys_addr, get_dram_limit(pvt, range));
1350f71d0a05SDoug Thompson 
1351e726f3c3SBorislav Petkov 	if (intlv_en &&
1352f71d0a05SDoug Thompson 	    (intlv_sel != ((sys_addr >> 12) & intlv_en)))
1353f71d0a05SDoug Thompson 		return -EINVAL;
1354f71d0a05SDoug Thompson 
1355f71d0a05SDoug Thompson 	dct_sel_base = dct_sel_baseaddr(pvt);
1356f71d0a05SDoug Thompson 
1357f71d0a05SDoug Thompson 	/*
1358f71d0a05SDoug Thompson 	 * check whether addresses >= DctSelBaseAddr[47:27] are to be used to
1359f71d0a05SDoug Thompson 	 * select between DCT0 and DCT1.
1360f71d0a05SDoug Thompson 	 */
1361f71d0a05SDoug Thompson 	if (dct_high_range_enabled(pvt) &&
1362f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt) &&
1363f71d0a05SDoug Thompson 	   ((sys_addr >> 27) >= (dct_sel_base >> 11)))
1364229a7a11SBorislav Petkov 		high_range = true;
1365f71d0a05SDoug Thompson 
1366f71d0a05SDoug Thompson 	channel = f10_determine_channel(pvt, sys_addr, high_range, intlv_en);
1367f71d0a05SDoug Thompson 
1368c8e518d5SBorislav Petkov 	chan_addr = f10_get_norm_dct_addr(pvt, range, sys_addr,
1369c8e518d5SBorislav Petkov 					  high_range, dct_sel_base);
1370f71d0a05SDoug Thompson 
1371f678b8ccSBorislav Petkov 	/* remove Node ID (in case of node interleaving) */
1372f71d0a05SDoug Thompson 	tmp = chan_addr & 0xFC0;
1373f71d0a05SDoug Thompson 
1374f678b8ccSBorislav Petkov 	chan_addr = ((chan_addr >> hweight8(intlv_en)) & GENMASK(12, 47)) | tmp;
1375f71d0a05SDoug Thompson 
1376f71d0a05SDoug Thompson 	/* remove channel interleave and hash */
1377f71d0a05SDoug Thompson 	if (dct_interleave_enabled(pvt) &&
1378f71d0a05SDoug Thompson 	   !dct_high_range_enabled(pvt) &&
1379f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt)) {
1380f71d0a05SDoug Thompson 		if (dct_sel_interleave_addr(pvt) != 1)
1381f678b8ccSBorislav Petkov 			chan_addr = (chan_addr >> 1) & GENMASK(6, 63);
1382f71d0a05SDoug Thompson 		else {
1383f71d0a05SDoug Thompson 			tmp = chan_addr & 0xFC0;
1384f678b8ccSBorislav Petkov 			chan_addr = ((chan_addr & GENMASK(14, 63)) >> 1) | tmp;
1385f71d0a05SDoug Thompson 		}
1386f71d0a05SDoug Thompson 	}
1387f71d0a05SDoug Thompson 
138811c75eadSBorislav Petkov 	debugf1("   (ChannelAddrLong=0x%llx)\n", chan_addr);
1389f71d0a05SDoug Thompson 
139011c75eadSBorislav Petkov 	cs_found = f10_lookup_addr_in_dct(chan_addr, node_id, channel);
1391f71d0a05SDoug Thompson 
1392f71d0a05SDoug Thompson 	if (cs_found >= 0) {
1393f71d0a05SDoug Thompson 		*nid = node_id;
1394f71d0a05SDoug Thompson 		*chan_sel = channel;
1395f71d0a05SDoug Thompson 	}
1396f71d0a05SDoug Thompson 	return cs_found;
1397f71d0a05SDoug Thompson }
1398f71d0a05SDoug Thompson 
1399f71d0a05SDoug Thompson static int f10_translate_sysaddr_to_cs(struct amd64_pvt *pvt, u64 sys_addr,
1400f71d0a05SDoug Thompson 				       int *node, int *chan_sel)
1401f71d0a05SDoug Thompson {
14027f19bf75SBorislav Petkov 	int range, cs_found = -EINVAL;
1403f71d0a05SDoug Thompson 
14047f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
1405f71d0a05SDoug Thompson 
14067f19bf75SBorislav Petkov 		if (!dram_rw(pvt, range))
1407f71d0a05SDoug Thompson 			continue;
1408f71d0a05SDoug Thompson 
14097f19bf75SBorislav Petkov 		if ((get_dram_base(pvt, range)  <= sys_addr) &&
14107f19bf75SBorislav Petkov 		    (get_dram_limit(pvt, range) >= sys_addr)) {
1411f71d0a05SDoug Thompson 
14127f19bf75SBorislav Petkov 			cs_found = f10_match_to_this_node(pvt, range,
1413f71d0a05SDoug Thompson 							  sys_addr, node,
1414f71d0a05SDoug Thompson 							  chan_sel);
1415f71d0a05SDoug Thompson 			if (cs_found >= 0)
1416f71d0a05SDoug Thompson 				break;
1417f71d0a05SDoug Thompson 		}
1418f71d0a05SDoug Thompson 	}
1419f71d0a05SDoug Thompson 	return cs_found;
1420f71d0a05SDoug Thompson }
1421f71d0a05SDoug Thompson 
1422f71d0a05SDoug Thompson /*
1423bdc30a0cSBorislav Petkov  * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
1424bdc30a0cSBorislav Petkov  * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
1425f71d0a05SDoug Thompson  *
1426bdc30a0cSBorislav Petkov  * The @sys_addr is usually an error address received from the hardware
1427bdc30a0cSBorislav Petkov  * (MCX_ADDR).
1428f71d0a05SDoug Thompson  */
1429f192c7b1SBorislav Petkov static void f10_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
1430f192c7b1SBorislav Petkov 				     u16 syndrome)
1431f71d0a05SDoug Thompson {
1432f71d0a05SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
1433f71d0a05SDoug Thompson 	u32 page, offset;
1434f71d0a05SDoug Thompson 	int nid, csrow, chan = 0;
1435f71d0a05SDoug Thompson 
1436f71d0a05SDoug Thompson 	csrow = f10_translate_sysaddr_to_cs(pvt, sys_addr, &nid, &chan);
1437f71d0a05SDoug Thompson 
1438bdc30a0cSBorislav Petkov 	if (csrow < 0) {
1439bdc30a0cSBorislav Petkov 		edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
1440bdc30a0cSBorislav Petkov 		return;
1441bdc30a0cSBorislav Petkov 	}
1442bdc30a0cSBorislav Petkov 
1443f71d0a05SDoug Thompson 	error_address_to_page_and_offset(sys_addr, &page, &offset);
1444f71d0a05SDoug Thompson 
1445f71d0a05SDoug Thompson 	/*
1446bdc30a0cSBorislav Petkov 	 * We need the syndromes for channel detection only when we're
1447bdc30a0cSBorislav Petkov 	 * ganged. Otherwise @chan should already contain the channel at
1448bdc30a0cSBorislav Petkov 	 * this point.
1449f71d0a05SDoug Thompson 	 */
1450a97fa68eSBorislav Petkov 	if (dct_ganging_enabled(pvt))
1451bfc04aecSBorislav Petkov 		chan = get_channel_from_ecc_syndrome(mci, syndrome);
1452f71d0a05SDoug Thompson 
1453bdc30a0cSBorislav Petkov 	if (chan >= 0)
1454bdc30a0cSBorislav Petkov 		edac_mc_handle_ce(mci, page, offset, syndrome, csrow, chan,
1455bdc30a0cSBorislav Petkov 				  EDAC_MOD_STR);
1456bdc30a0cSBorislav Petkov 	else
1457bdc30a0cSBorislav Petkov 		/*
1458bdc30a0cSBorislav Petkov 		 * Channel unknown, report all channels on this CSROW as failed.
1459bdc30a0cSBorislav Petkov 		 */
1460bdc30a0cSBorislav Petkov 		for (chan = 0; chan < mci->csrows[csrow].nr_channels; chan++)
1461f71d0a05SDoug Thompson 			edac_mc_handle_ce(mci, page, offset, syndrome,
1462f71d0a05SDoug Thompson 					  csrow, chan, EDAC_MOD_STR);
1463f71d0a05SDoug Thompson }
1464f71d0a05SDoug Thompson 
1465f71d0a05SDoug Thompson /*
14668566c4dfSBorislav Petkov  * debug routine to display the memory sizes of all logical DIMMs and its
1467cb328507SBorislav Petkov  * CSROWs
1468f71d0a05SDoug Thompson  */
14698566c4dfSBorislav Petkov static void amd64_debug_display_dimm_sizes(int ctrl, struct amd64_pvt *pvt)
1470f71d0a05SDoug Thompson {
1471603adaf6SBorislav Petkov 	int dimm, size0, size1, factor = 0;
1472525a1b20SBorislav Petkov 	u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases;
1473525a1b20SBorislav Petkov 	u32 dbam  = ctrl ? pvt->dbam1 : pvt->dbam0;
1474f71d0a05SDoug Thompson 
14758566c4dfSBorislav Petkov 	if (boot_cpu_data.x86 == 0xf) {
1476603adaf6SBorislav Petkov 		if (pvt->dclr0 & F10_WIDTH_128)
1477603adaf6SBorislav Petkov 			factor = 1;
1478603adaf6SBorislav Petkov 
14798566c4dfSBorislav Petkov 		/* K8 families < revF not supported yet */
14801433eb99SBorislav Petkov 	       if (pvt->ext_model < K8_REV_F)
14818566c4dfSBorislav Petkov 			return;
14828566c4dfSBorislav Petkov 	       else
14838566c4dfSBorislav Petkov 		       WARN_ON(ctrl != 0);
14848566c4dfSBorislav Petkov 	}
14858566c4dfSBorislav Petkov 
14864d796364SBorislav Petkov 	dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1 : pvt->dbam0;
148711c75eadSBorislav Petkov 	dcsb = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->csels[1].csbases
148811c75eadSBorislav Petkov 						   : pvt->csels[0].csbases;
1489f71d0a05SDoug Thompson 
14904d796364SBorislav Petkov 	debugf1("F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n", ctrl, dbam);
1491f71d0a05SDoug Thompson 
14928566c4dfSBorislav Petkov 	edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
14938566c4dfSBorislav Petkov 
1494f71d0a05SDoug Thompson 	/* Dump memory sizes for DIMM and its CSROWs */
1495f71d0a05SDoug Thompson 	for (dimm = 0; dimm < 4; dimm++) {
1496f71d0a05SDoug Thompson 
1497f71d0a05SDoug Thompson 		size0 = 0;
149811c75eadSBorislav Petkov 		if (dcsb[dimm*2] & DCSB_CS_ENABLE)
14991433eb99SBorislav Petkov 			size0 = pvt->ops->dbam_to_cs(pvt, DBAM_DIMM(dimm, dbam));
1500f71d0a05SDoug Thompson 
1501f71d0a05SDoug Thompson 		size1 = 0;
150211c75eadSBorislav Petkov 		if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE)
15031433eb99SBorislav Petkov 			size1 = pvt->ops->dbam_to_cs(pvt, DBAM_DIMM(dimm, dbam));
1504f71d0a05SDoug Thompson 
150524f9a7feSBorislav Petkov 		amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
1506603adaf6SBorislav Petkov 				dimm * 2,     size0 << factor,
1507603adaf6SBorislav Petkov 				dimm * 2 + 1, size1 << factor);
1508f71d0a05SDoug Thompson 	}
1509f71d0a05SDoug Thompson }
1510f71d0a05SDoug Thompson 
15114d37607aSDoug Thompson static struct amd64_family_type amd64_family_types[] = {
15124d37607aSDoug Thompson 	[K8_CPUS] = {
15130092b20dSBorislav Petkov 		.ctl_name = "K8",
15148d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
15158d5b5d9cSBorislav Petkov 		.f3_id = PCI_DEVICE_ID_AMD_K8_NB_MISC,
15164d37607aSDoug Thompson 		.ops = {
15174d37607aSDoug Thompson 			.early_channel_count	= k8_early_channel_count,
15184d37607aSDoug Thompson 			.map_sysaddr_to_csrow	= k8_map_sysaddr_to_csrow,
15191433eb99SBorislav Petkov 			.dbam_to_cs		= k8_dbam_to_chip_select,
1520b2b0c605SBorislav Petkov 			.read_dct_pci_cfg	= k8_read_dct_pci_cfg,
15214d37607aSDoug Thompson 		}
15224d37607aSDoug Thompson 	},
15234d37607aSDoug Thompson 	[F10_CPUS] = {
15240092b20dSBorislav Petkov 		.ctl_name = "F10h",
15258d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP,
15268d5b5d9cSBorislav Petkov 		.f3_id = PCI_DEVICE_ID_AMD_10H_NB_MISC,
15274d37607aSDoug Thompson 		.ops = {
15287d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
15294d37607aSDoug Thompson 			.read_dram_ctl_register	= f10_read_dram_ctl_register,
15304d37607aSDoug Thompson 			.map_sysaddr_to_csrow	= f10_map_sysaddr_to_csrow,
15311433eb99SBorislav Petkov 			.dbam_to_cs		= f10_dbam_to_chip_select,
1532b2b0c605SBorislav Petkov 			.read_dct_pci_cfg	= f10_read_dct_pci_cfg,
1533b2b0c605SBorislav Petkov 		}
1534b2b0c605SBorislav Petkov 	},
1535b2b0c605SBorislav Petkov 	[F15_CPUS] = {
1536b2b0c605SBorislav Petkov 		.ctl_name = "F15h",
1537b2b0c605SBorislav Petkov 		.ops = {
15387d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
1539b2b0c605SBorislav Petkov 			.read_dct_pci_cfg	= f15_read_dct_pci_cfg,
15404d37607aSDoug Thompson 		}
15414d37607aSDoug Thompson 	},
15424d37607aSDoug Thompson };
15434d37607aSDoug Thompson 
15444d37607aSDoug Thompson static struct pci_dev *pci_get_related_function(unsigned int vendor,
15454d37607aSDoug Thompson 						unsigned int device,
15464d37607aSDoug Thompson 						struct pci_dev *related)
15474d37607aSDoug Thompson {
15484d37607aSDoug Thompson 	struct pci_dev *dev = NULL;
15494d37607aSDoug Thompson 
15504d37607aSDoug Thompson 	dev = pci_get_device(vendor, device, dev);
15514d37607aSDoug Thompson 	while (dev) {
15524d37607aSDoug Thompson 		if ((dev->bus->number == related->bus->number) &&
15534d37607aSDoug Thompson 		    (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn)))
15544d37607aSDoug Thompson 			break;
15554d37607aSDoug Thompson 		dev = pci_get_device(vendor, device, dev);
15564d37607aSDoug Thompson 	}
15574d37607aSDoug Thompson 
15584d37607aSDoug Thompson 	return dev;
15594d37607aSDoug Thompson }
15604d37607aSDoug Thompson 
1561b1289d6fSDoug Thompson /*
1562bfc04aecSBorislav Petkov  * These are tables of eigenvectors (one per line) which can be used for the
1563bfc04aecSBorislav Petkov  * construction of the syndrome tables. The modified syndrome search algorithm
1564bfc04aecSBorislav Petkov  * uses those to find the symbol in error and thus the DIMM.
1565b1289d6fSDoug Thompson  *
1566bfc04aecSBorislav Petkov  * Algorithm courtesy of Ross LaFetra from AMD.
1567b1289d6fSDoug Thompson  */
1568bfc04aecSBorislav Petkov static u16 x4_vectors[] = {
1569bfc04aecSBorislav Petkov 	0x2f57, 0x1afe, 0x66cc, 0xdd88,
1570bfc04aecSBorislav Petkov 	0x11eb, 0x3396, 0x7f4c, 0xeac8,
1571bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008,
1572bfc04aecSBorislav Petkov 	0x1013, 0x3032, 0x4044, 0x8088,
1573bfc04aecSBorislav Petkov 	0x106b, 0x30d6, 0x70fc, 0xe0a8,
1574bfc04aecSBorislav Petkov 	0x4857, 0xc4fe, 0x13cc, 0x3288,
1575bfc04aecSBorislav Petkov 	0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
1576bfc04aecSBorislav Petkov 	0x1f39, 0x251e, 0xbd6c, 0x6bd8,
1577bfc04aecSBorislav Petkov 	0x15c1, 0x2a42, 0x89ac, 0x4758,
1578bfc04aecSBorislav Petkov 	0x2b03, 0x1602, 0x4f0c, 0xca08,
1579bfc04aecSBorislav Petkov 	0x1f07, 0x3a0e, 0x6b04, 0xbd08,
1580bfc04aecSBorislav Petkov 	0x8ba7, 0x465e, 0x244c, 0x1cc8,
1581bfc04aecSBorislav Petkov 	0x2b87, 0x164e, 0x642c, 0xdc18,
1582bfc04aecSBorislav Petkov 	0x40b9, 0x80de, 0x1094, 0x20e8,
1583bfc04aecSBorislav Petkov 	0x27db, 0x1eb6, 0x9dac, 0x7b58,
1584bfc04aecSBorislav Petkov 	0x11c1, 0x2242, 0x84ac, 0x4c58,
1585bfc04aecSBorislav Petkov 	0x1be5, 0x2d7a, 0x5e34, 0xa718,
1586bfc04aecSBorislav Petkov 	0x4b39, 0x8d1e, 0x14b4, 0x28d8,
1587bfc04aecSBorislav Petkov 	0x4c97, 0xc87e, 0x11fc, 0x33a8,
1588bfc04aecSBorislav Petkov 	0x8e97, 0x497e, 0x2ffc, 0x1aa8,
1589bfc04aecSBorislav Petkov 	0x16b3, 0x3d62, 0x4f34, 0x8518,
1590bfc04aecSBorislav Petkov 	0x1e2f, 0x391a, 0x5cac, 0xf858,
1591bfc04aecSBorislav Petkov 	0x1d9f, 0x3b7a, 0x572c, 0xfe18,
1592bfc04aecSBorislav Petkov 	0x15f5, 0x2a5a, 0x5264, 0xa3b8,
1593bfc04aecSBorislav Petkov 	0x1dbb, 0x3b66, 0x715c, 0xe3f8,
1594bfc04aecSBorislav Petkov 	0x4397, 0xc27e, 0x17fc, 0x3ea8,
1595bfc04aecSBorislav Petkov 	0x1617, 0x3d3e, 0x6464, 0xb8b8,
1596bfc04aecSBorislav Petkov 	0x23ff, 0x12aa, 0xab6c, 0x56d8,
1597bfc04aecSBorislav Petkov 	0x2dfb, 0x1ba6, 0x913c, 0x7328,
1598bfc04aecSBorislav Petkov 	0x185d, 0x2ca6, 0x7914, 0x9e28,
1599bfc04aecSBorislav Petkov 	0x171b, 0x3e36, 0x7d7c, 0xebe8,
1600bfc04aecSBorislav Petkov 	0x4199, 0x82ee, 0x19f4, 0x2e58,
1601bfc04aecSBorislav Petkov 	0x4807, 0xc40e, 0x130c, 0x3208,
1602bfc04aecSBorislav Petkov 	0x1905, 0x2e0a, 0x5804, 0xac08,
1603bfc04aecSBorislav Petkov 	0x213f, 0x132a, 0xadfc, 0x5ba8,
1604bfc04aecSBorislav Petkov 	0x19a9, 0x2efe, 0xb5cc, 0x6f88,
1605b1289d6fSDoug Thompson };
1606b1289d6fSDoug Thompson 
1607bfc04aecSBorislav Petkov static u16 x8_vectors[] = {
1608bfc04aecSBorislav Petkov 	0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
1609bfc04aecSBorislav Petkov 	0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
1610bfc04aecSBorislav Petkov 	0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
1611bfc04aecSBorislav Petkov 	0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
1612bfc04aecSBorislav Petkov 	0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
1613bfc04aecSBorislav Petkov 	0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
1614bfc04aecSBorislav Petkov 	0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
1615bfc04aecSBorislav Petkov 	0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
1616bfc04aecSBorislav Petkov 	0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
1617bfc04aecSBorislav Petkov 	0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
1618bfc04aecSBorislav Petkov 	0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
1619bfc04aecSBorislav Petkov 	0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
1620bfc04aecSBorislav Petkov 	0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
1621bfc04aecSBorislav Petkov 	0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
1622bfc04aecSBorislav Petkov 	0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
1623bfc04aecSBorislav Petkov 	0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
1624bfc04aecSBorislav Petkov 	0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
1625bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
1626bfc04aecSBorislav Petkov 	0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
1627bfc04aecSBorislav Petkov };
1628bfc04aecSBorislav Petkov 
1629bfc04aecSBorislav Petkov static int decode_syndrome(u16 syndrome, u16 *vectors, int num_vecs,
1630bfc04aecSBorislav Petkov 			   int v_dim)
1631b1289d6fSDoug Thompson {
1632bfc04aecSBorislav Petkov 	unsigned int i, err_sym;
1633b1289d6fSDoug Thompson 
1634bfc04aecSBorislav Petkov 	for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
1635bfc04aecSBorislav Petkov 		u16 s = syndrome;
1636bfc04aecSBorislav Petkov 		int v_idx =  err_sym * v_dim;
1637bfc04aecSBorislav Petkov 		int v_end = (err_sym + 1) * v_dim;
1638b1289d6fSDoug Thompson 
1639bfc04aecSBorislav Petkov 		/* walk over all 16 bits of the syndrome */
1640bfc04aecSBorislav Petkov 		for (i = 1; i < (1U << 16); i <<= 1) {
1641bfc04aecSBorislav Petkov 
1642bfc04aecSBorislav Petkov 			/* if bit is set in that eigenvector... */
1643bfc04aecSBorislav Petkov 			if (v_idx < v_end && vectors[v_idx] & i) {
1644bfc04aecSBorislav Petkov 				u16 ev_comp = vectors[v_idx++];
1645bfc04aecSBorislav Petkov 
1646bfc04aecSBorislav Petkov 				/* ... and bit set in the modified syndrome, */
1647bfc04aecSBorislav Petkov 				if (s & i) {
1648bfc04aecSBorislav Petkov 					/* remove it. */
1649bfc04aecSBorislav Petkov 					s ^= ev_comp;
1650bfc04aecSBorislav Petkov 
1651bfc04aecSBorislav Petkov 					if (!s)
1652bfc04aecSBorislav Petkov 						return err_sym;
1653bfc04aecSBorislav Petkov 				}
1654bfc04aecSBorislav Petkov 
1655bfc04aecSBorislav Petkov 			} else if (s & i)
1656bfc04aecSBorislav Petkov 				/* can't get to zero, move to next symbol */
1657bfc04aecSBorislav Petkov 				break;
1658bfc04aecSBorislav Petkov 		}
1659b1289d6fSDoug Thompson 	}
1660b1289d6fSDoug Thompson 
1661b1289d6fSDoug Thompson 	debugf0("syndrome(%x) not found\n", syndrome);
1662b1289d6fSDoug Thompson 	return -1;
1663b1289d6fSDoug Thompson }
1664d27bf6faSDoug Thompson 
1665bfc04aecSBorislav Petkov static int map_err_sym_to_channel(int err_sym, int sym_size)
1666bfc04aecSBorislav Petkov {
1667bfc04aecSBorislav Petkov 	if (sym_size == 4)
1668bfc04aecSBorislav Petkov 		switch (err_sym) {
1669bfc04aecSBorislav Petkov 		case 0x20:
1670bfc04aecSBorislav Petkov 		case 0x21:
1671bfc04aecSBorislav Petkov 			return 0;
1672bfc04aecSBorislav Petkov 			break;
1673bfc04aecSBorislav Petkov 		case 0x22:
1674bfc04aecSBorislav Petkov 		case 0x23:
1675bfc04aecSBorislav Petkov 			return 1;
1676bfc04aecSBorislav Petkov 			break;
1677bfc04aecSBorislav Petkov 		default:
1678bfc04aecSBorislav Petkov 			return err_sym >> 4;
1679bfc04aecSBorislav Petkov 			break;
1680bfc04aecSBorislav Petkov 		}
1681bfc04aecSBorislav Petkov 	/* x8 symbols */
1682bfc04aecSBorislav Petkov 	else
1683bfc04aecSBorislav Petkov 		switch (err_sym) {
1684bfc04aecSBorislav Petkov 		/* imaginary bits not in a DIMM */
1685bfc04aecSBorislav Petkov 		case 0x10:
1686bfc04aecSBorislav Petkov 			WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
1687bfc04aecSBorislav Petkov 					  err_sym);
1688bfc04aecSBorislav Petkov 			return -1;
1689bfc04aecSBorislav Petkov 			break;
1690bfc04aecSBorislav Petkov 
1691bfc04aecSBorislav Petkov 		case 0x11:
1692bfc04aecSBorislav Petkov 			return 0;
1693bfc04aecSBorislav Petkov 			break;
1694bfc04aecSBorislav Petkov 		case 0x12:
1695bfc04aecSBorislav Petkov 			return 1;
1696bfc04aecSBorislav Petkov 			break;
1697bfc04aecSBorislav Petkov 		default:
1698bfc04aecSBorislav Petkov 			return err_sym >> 3;
1699bfc04aecSBorislav Petkov 			break;
1700bfc04aecSBorislav Petkov 		}
1701bfc04aecSBorislav Petkov 	return -1;
1702bfc04aecSBorislav Petkov }
1703bfc04aecSBorislav Petkov 
1704bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
1705bfc04aecSBorislav Petkov {
1706bfc04aecSBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
1707ad6a32e9SBorislav Petkov 	int err_sym = -1;
1708bfc04aecSBorislav Petkov 
1709ad6a32e9SBorislav Petkov 	if (pvt->syn_type == 8)
1710bfc04aecSBorislav Petkov 		err_sym = decode_syndrome(syndrome, x8_vectors,
1711ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x8_vectors),
1712ad6a32e9SBorislav Petkov 					  pvt->syn_type);
1713ad6a32e9SBorislav Petkov 	else if (pvt->syn_type == 4)
1714ad6a32e9SBorislav Petkov 		err_sym = decode_syndrome(syndrome, x4_vectors,
1715ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x4_vectors),
1716ad6a32e9SBorislav Petkov 					  pvt->syn_type);
1717ad6a32e9SBorislav Petkov 	else {
171824f9a7feSBorislav Petkov 		amd64_warn("Illegal syndrome type: %u\n", pvt->syn_type);
1719ad6a32e9SBorislav Petkov 		return err_sym;
1720bfc04aecSBorislav Petkov 	}
1721ad6a32e9SBorislav Petkov 
1722ad6a32e9SBorislav Petkov 	return map_err_sym_to_channel(err_sym, pvt->syn_type);
172341c31044SBorislav Petkov }
1724bfc04aecSBorislav Petkov 
1725d27bf6faSDoug Thompson /*
1726d27bf6faSDoug Thompson  * Handle any Correctable Errors (CEs) that have occurred. Check for valid ERROR
1727d27bf6faSDoug Thompson  * ADDRESS and process.
1728d27bf6faSDoug Thompson  */
1729f192c7b1SBorislav Petkov static void amd64_handle_ce(struct mem_ctl_info *mci, struct mce *m)
1730d27bf6faSDoug Thompson {
1731d27bf6faSDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
173244e9e2eeSBorislav Petkov 	u64 sys_addr;
1733f192c7b1SBorislav Petkov 	u16 syndrome;
1734d27bf6faSDoug Thompson 
1735d27bf6faSDoug Thompson 	/* Ensure that the Error Address is VALID */
1736f192c7b1SBorislav Petkov 	if (!(m->status & MCI_STATUS_ADDRV)) {
173724f9a7feSBorislav Petkov 		amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n");
1738d27bf6faSDoug Thompson 		edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
1739d27bf6faSDoug Thompson 		return;
1740d27bf6faSDoug Thompson 	}
1741d27bf6faSDoug Thompson 
174270046624SBorislav Petkov 	sys_addr = get_error_address(m);
1743f192c7b1SBorislav Petkov 	syndrome = extract_syndrome(m->status);
1744d27bf6faSDoug Thompson 
174524f9a7feSBorislav Petkov 	amd64_mc_err(mci, "CE ERROR_ADDRESS= 0x%llx\n", sys_addr);
1746d27bf6faSDoug Thompson 
1747f192c7b1SBorislav Petkov 	pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, syndrome);
1748d27bf6faSDoug Thompson }
1749d27bf6faSDoug Thompson 
1750d27bf6faSDoug Thompson /* Handle any Un-correctable Errors (UEs) */
1751f192c7b1SBorislav Petkov static void amd64_handle_ue(struct mem_ctl_info *mci, struct mce *m)
1752d27bf6faSDoug Thompson {
17531f6bcee7SBorislav Petkov 	struct mem_ctl_info *log_mci, *src_mci = NULL;
1754d27bf6faSDoug Thompson 	int csrow;
175544e9e2eeSBorislav Petkov 	u64 sys_addr;
1756d27bf6faSDoug Thompson 	u32 page, offset;
1757d27bf6faSDoug Thompson 
1758d27bf6faSDoug Thompson 	log_mci = mci;
1759d27bf6faSDoug Thompson 
1760f192c7b1SBorislav Petkov 	if (!(m->status & MCI_STATUS_ADDRV)) {
176124f9a7feSBorislav Petkov 		amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n");
1762d27bf6faSDoug Thompson 		edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
1763d27bf6faSDoug Thompson 		return;
1764d27bf6faSDoug Thompson 	}
1765d27bf6faSDoug Thompson 
176670046624SBorislav Petkov 	sys_addr = get_error_address(m);
1767d27bf6faSDoug Thompson 
1768d27bf6faSDoug Thompson 	/*
1769d27bf6faSDoug Thompson 	 * Find out which node the error address belongs to. This may be
1770d27bf6faSDoug Thompson 	 * different from the node that detected the error.
1771d27bf6faSDoug Thompson 	 */
177244e9e2eeSBorislav Petkov 	src_mci = find_mc_by_sys_addr(mci, sys_addr);
1773d27bf6faSDoug Thompson 	if (!src_mci) {
177424f9a7feSBorislav Petkov 		amd64_mc_err(mci, "ERROR ADDRESS (0x%lx) NOT mapped to a MC\n",
177544e9e2eeSBorislav Petkov 				  (unsigned long)sys_addr);
1776d27bf6faSDoug Thompson 		edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
1777d27bf6faSDoug Thompson 		return;
1778d27bf6faSDoug Thompson 	}
1779d27bf6faSDoug Thompson 
1780d27bf6faSDoug Thompson 	log_mci = src_mci;
1781d27bf6faSDoug Thompson 
178244e9e2eeSBorislav Petkov 	csrow = sys_addr_to_csrow(log_mci, sys_addr);
1783d27bf6faSDoug Thompson 	if (csrow < 0) {
178424f9a7feSBorislav Petkov 		amd64_mc_err(mci, "ERROR_ADDRESS (0x%lx) NOT mapped to CS\n",
178544e9e2eeSBorislav Petkov 				  (unsigned long)sys_addr);
1786d27bf6faSDoug Thompson 		edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
1787d27bf6faSDoug Thompson 	} else {
178844e9e2eeSBorislav Petkov 		error_address_to_page_and_offset(sys_addr, &page, &offset);
1789d27bf6faSDoug Thompson 		edac_mc_handle_ue(log_mci, page, offset, csrow, EDAC_MOD_STR);
1790d27bf6faSDoug Thompson 	}
1791d27bf6faSDoug Thompson }
1792d27bf6faSDoug Thompson 
1793549d042dSBorislav Petkov static inline void __amd64_decode_bus_error(struct mem_ctl_info *mci,
1794f192c7b1SBorislav Petkov 					    struct mce *m)
1795d27bf6faSDoug Thompson {
1796f192c7b1SBorislav Petkov 	u16 ec = EC(m->status);
1797f192c7b1SBorislav Petkov 	u8 xec = XEC(m->status, 0x1f);
1798f192c7b1SBorislav Petkov 	u8 ecc_type = (m->status >> 45) & 0x3;
1799d27bf6faSDoug Thompson 
1800b70ef010SBorislav Petkov 	/* Bail early out if this was an 'observed' error */
18015980bb9cSBorislav Petkov 	if (PP(ec) == NBSL_PP_OBS)
1802b70ef010SBorislav Petkov 		return;
1803d27bf6faSDoug Thompson 
1804ecaf5606SBorislav Petkov 	/* Do only ECC errors */
1805ecaf5606SBorislav Petkov 	if (xec && xec != F10_NBSL_EXT_ERR_ECC)
1806d27bf6faSDoug Thompson 		return;
1807d27bf6faSDoug Thompson 
1808ecaf5606SBorislav Petkov 	if (ecc_type == 2)
1809f192c7b1SBorislav Petkov 		amd64_handle_ce(mci, m);
1810ecaf5606SBorislav Petkov 	else if (ecc_type == 1)
1811f192c7b1SBorislav Petkov 		amd64_handle_ue(mci, m);
1812d27bf6faSDoug Thompson }
1813d27bf6faSDoug Thompson 
18147cfd4a87SBorislav Petkov void amd64_decode_bus_error(int node_id, struct mce *m, u32 nbcfg)
1815d27bf6faSDoug Thompson {
1816cc4d8860SBorislav Petkov 	struct mem_ctl_info *mci = mcis[node_id];
1817d27bf6faSDoug Thompson 
1818f192c7b1SBorislav Petkov 	__amd64_decode_bus_error(mci, m);
1819d27bf6faSDoug Thompson }
1820d27bf6faSDoug Thompson 
18210ec449eeSDoug Thompson /*
18228d5b5d9cSBorislav Petkov  * Use pvt->F2 which contains the F2 CPU PCI device to get the related
1823bbd0c1f6SBorislav Petkov  * F1 (AddrMap) and F3 (Misc) devices. Return negative value on error.
18240ec449eeSDoug Thompson  */
1825360b7f3cSBorislav Petkov static int reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 f1_id, u16 f3_id)
18260ec449eeSDoug Thompson {
18270ec449eeSDoug Thompson 	/* Reserve the ADDRESS MAP Device */
18288d5b5d9cSBorislav Petkov 	pvt->F1 = pci_get_related_function(pvt->F2->vendor, f1_id, pvt->F2);
18298d5b5d9cSBorislav Petkov 	if (!pvt->F1) {
183024f9a7feSBorislav Petkov 		amd64_err("error address map device not found: "
18310ec449eeSDoug Thompson 			  "vendor %x device 0x%x (broken BIOS?)\n",
1832bbd0c1f6SBorislav Petkov 			  PCI_VENDOR_ID_AMD, f1_id);
1833bbd0c1f6SBorislav Petkov 		return -ENODEV;
18340ec449eeSDoug Thompson 	}
18350ec449eeSDoug Thompson 
18360ec449eeSDoug Thompson 	/* Reserve the MISC Device */
18378d5b5d9cSBorislav Petkov 	pvt->F3 = pci_get_related_function(pvt->F2->vendor, f3_id, pvt->F2);
18388d5b5d9cSBorislav Petkov 	if (!pvt->F3) {
18398d5b5d9cSBorislav Petkov 		pci_dev_put(pvt->F1);
18408d5b5d9cSBorislav Petkov 		pvt->F1 = NULL;
18410ec449eeSDoug Thompson 
184224f9a7feSBorislav Petkov 		amd64_err("error F3 device not found: "
18430ec449eeSDoug Thompson 			  "vendor %x device 0x%x (broken BIOS?)\n",
1844bbd0c1f6SBorislav Petkov 			  PCI_VENDOR_ID_AMD, f3_id);
18458d5b5d9cSBorislav Petkov 
1846bbd0c1f6SBorislav Petkov 		return -ENODEV;
18470ec449eeSDoug Thompson 	}
18488d5b5d9cSBorislav Petkov 	debugf1("F1: %s\n", pci_name(pvt->F1));
18498d5b5d9cSBorislav Petkov 	debugf1("F2: %s\n", pci_name(pvt->F2));
18508d5b5d9cSBorislav Petkov 	debugf1("F3: %s\n", pci_name(pvt->F3));
18510ec449eeSDoug Thompson 
18520ec449eeSDoug Thompson 	return 0;
18530ec449eeSDoug Thompson }
18540ec449eeSDoug Thompson 
1855360b7f3cSBorislav Petkov static void free_mc_sibling_devs(struct amd64_pvt *pvt)
18560ec449eeSDoug Thompson {
18578d5b5d9cSBorislav Petkov 	pci_dev_put(pvt->F1);
18588d5b5d9cSBorislav Petkov 	pci_dev_put(pvt->F3);
18590ec449eeSDoug Thompson }
18600ec449eeSDoug Thompson 
18610ec449eeSDoug Thompson /*
18620ec449eeSDoug Thompson  * Retrieve the hardware registers of the memory controller (this includes the
18630ec449eeSDoug Thompson  * 'Address Map' and 'Misc' device regs)
18640ec449eeSDoug Thompson  */
1865360b7f3cSBorislav Petkov static void read_mc_regs(struct amd64_pvt *pvt)
18660ec449eeSDoug Thompson {
18670ec449eeSDoug Thompson 	u64 msr_val;
1868ad6a32e9SBorislav Petkov 	u32 tmp;
18697f19bf75SBorislav Petkov 	int range;
18700ec449eeSDoug Thompson 
18710ec449eeSDoug Thompson 	/*
18720ec449eeSDoug Thompson 	 * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
18730ec449eeSDoug Thompson 	 * those are Read-As-Zero
18740ec449eeSDoug Thompson 	 */
1875e97f8bb8SBorislav Petkov 	rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
1876e97f8bb8SBorislav Petkov 	debugf0("  TOP_MEM:  0x%016llx\n", pvt->top_mem);
18770ec449eeSDoug Thompson 
18780ec449eeSDoug Thompson 	/* check first whether TOP_MEM2 is enabled */
18790ec449eeSDoug Thompson 	rdmsrl(MSR_K8_SYSCFG, msr_val);
18800ec449eeSDoug Thompson 	if (msr_val & (1U << 21)) {
1881e97f8bb8SBorislav Petkov 		rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
1882e97f8bb8SBorislav Petkov 		debugf0("  TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
18830ec449eeSDoug Thompson 	} else
18840ec449eeSDoug Thompson 		debugf0("  TOP_MEM2 disabled.\n");
18850ec449eeSDoug Thompson 
18865980bb9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap);
18870ec449eeSDoug Thompson 
18880ec449eeSDoug Thompson 	if (pvt->ops->read_dram_ctl_register)
18890ec449eeSDoug Thompson 		pvt->ops->read_dram_ctl_register(pvt);
18900ec449eeSDoug Thompson 
18917f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
18927f19bf75SBorislav Petkov 		u8 rw;
18930ec449eeSDoug Thompson 
18947f19bf75SBorislav Petkov 		/* read settings for this DRAM range */
18957f19bf75SBorislav Petkov 		read_dram_base_limit_regs(pvt, range);
1896e97f8bb8SBorislav Petkov 
18977f19bf75SBorislav Petkov 		rw = dram_rw(pvt, range);
18987f19bf75SBorislav Petkov 		if (!rw)
18997f19bf75SBorislav Petkov 			continue;
19007f19bf75SBorislav Petkov 
19017f19bf75SBorislav Petkov 		debugf1("  DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
19027f19bf75SBorislav Petkov 			range,
19037f19bf75SBorislav Petkov 			get_dram_base(pvt, range),
19047f19bf75SBorislav Petkov 			get_dram_limit(pvt, range));
19057f19bf75SBorislav Petkov 
19067f19bf75SBorislav Petkov 		debugf1("   IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
19077f19bf75SBorislav Petkov 			dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
19087f19bf75SBorislav Petkov 			(rw & 0x1) ? "R" : "-",
19097f19bf75SBorislav Petkov 			(rw & 0x2) ? "W" : "-",
19107f19bf75SBorislav Petkov 			dram_intlv_sel(pvt, range),
19117f19bf75SBorislav Petkov 			dram_dst_node(pvt, range));
19120ec449eeSDoug Thompson 	}
19130ec449eeSDoug Thompson 
1914b2b0c605SBorislav Petkov 	read_dct_base_mask(pvt);
19150ec449eeSDoug Thompson 
1916bc21fa57SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar);
1917525a1b20SBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, DBAM0, &pvt->dbam0);
19180ec449eeSDoug Thompson 
19198d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare);
19200ec449eeSDoug Thompson 
1921cb328507SBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, DCLR0, &pvt->dclr0);
1922cb328507SBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, DCHR0, &pvt->dchr0);
19230ec449eeSDoug Thompson 
192478da121eSBorislav Petkov 	if (!dct_ganging_enabled(pvt)) {
1925cb328507SBorislav Petkov 		amd64_read_dct_pci_cfg(pvt, DCLR1, &pvt->dclr1);
1926cb328507SBorislav Petkov 		amd64_read_dct_pci_cfg(pvt, DCHR1, &pvt->dchr1);
19270ec449eeSDoug Thompson 	}
1928b2b0c605SBorislav Petkov 
1929525a1b20SBorislav Petkov 	if (boot_cpu_data.x86 >= 0x10) {
19308d5b5d9cSBorislav Petkov 		amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp);
1931525a1b20SBorislav Petkov 		amd64_read_dct_pci_cfg(pvt, DBAM1, &pvt->dbam1);
1932525a1b20SBorislav Petkov 	}
1933ad6a32e9SBorislav Petkov 
1934ad6a32e9SBorislav Petkov 	if (boot_cpu_data.x86 == 0x10 &&
1935ad6a32e9SBorislav Petkov 	    boot_cpu_data.x86_model > 7 &&
1936ad6a32e9SBorislav Petkov 	    /* F3x180[EccSymbolSize]=1 => x8 symbols */
1937ad6a32e9SBorislav Petkov 	    tmp & BIT(25))
1938ad6a32e9SBorislav Petkov 		pvt->syn_type = 8;
1939ad6a32e9SBorislav Petkov 	else
1940ad6a32e9SBorislav Petkov 		pvt->syn_type = 4;
1941ad6a32e9SBorislav Petkov 
1942b2b0c605SBorislav Petkov 	dump_misc_regs(pvt);
19430ec449eeSDoug Thompson }
19440ec449eeSDoug Thompson 
19450ec449eeSDoug Thompson /*
19460ec449eeSDoug Thompson  * NOTE: CPU Revision Dependent code
19470ec449eeSDoug Thompson  *
19480ec449eeSDoug Thompson  * Input:
194911c75eadSBorislav Petkov  *	@csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1)
19500ec449eeSDoug Thompson  *	k8 private pointer to -->
19510ec449eeSDoug Thompson  *			DRAM Bank Address mapping register
19520ec449eeSDoug Thompson  *			node_id
19530ec449eeSDoug Thompson  *			DCL register where dual_channel_active is
19540ec449eeSDoug Thompson  *
19550ec449eeSDoug Thompson  * The DBAM register consists of 4 sets of 4 bits each definitions:
19560ec449eeSDoug Thompson  *
19570ec449eeSDoug Thompson  * Bits:	CSROWs
19580ec449eeSDoug Thompson  * 0-3		CSROWs 0 and 1
19590ec449eeSDoug Thompson  * 4-7		CSROWs 2 and 3
19600ec449eeSDoug Thompson  * 8-11		CSROWs 4 and 5
19610ec449eeSDoug Thompson  * 12-15	CSROWs 6 and 7
19620ec449eeSDoug Thompson  *
19630ec449eeSDoug Thompson  * Values range from: 0 to 15
19640ec449eeSDoug Thompson  * The meaning of the values depends on CPU revision and dual-channel state,
19650ec449eeSDoug Thompson  * see relevant BKDG more info.
19660ec449eeSDoug Thompson  *
19670ec449eeSDoug Thompson  * The memory controller provides for total of only 8 CSROWs in its current
19680ec449eeSDoug Thompson  * architecture. Each "pair" of CSROWs normally represents just one DIMM in
19690ec449eeSDoug Thompson  * single channel or two (2) DIMMs in dual channel mode.
19700ec449eeSDoug Thompson  *
19710ec449eeSDoug Thompson  * The following code logic collapses the various tables for CSROW based on CPU
19720ec449eeSDoug Thompson  * revision.
19730ec449eeSDoug Thompson  *
19740ec449eeSDoug Thompson  * Returns:
19750ec449eeSDoug Thompson  *	The number of PAGE_SIZE pages on the specified CSROW number it
19760ec449eeSDoug Thompson  *	encompasses
19770ec449eeSDoug Thompson  *
19780ec449eeSDoug Thompson  */
19790ec449eeSDoug Thompson static u32 amd64_csrow_nr_pages(int csrow_nr, struct amd64_pvt *pvt)
19800ec449eeSDoug Thompson {
19811433eb99SBorislav Petkov 	u32 cs_mode, nr_pages;
19820ec449eeSDoug Thompson 
19830ec449eeSDoug Thompson 	/*
19840ec449eeSDoug Thompson 	 * The math on this doesn't look right on the surface because x/2*4 can
19850ec449eeSDoug Thompson 	 * be simplified to x*2 but this expression makes use of the fact that
19860ec449eeSDoug Thompson 	 * it is integral math where 1/2=0. This intermediate value becomes the
19870ec449eeSDoug Thompson 	 * number of bits to shift the DBAM register to extract the proper CSROW
19880ec449eeSDoug Thompson 	 * field.
19890ec449eeSDoug Thompson 	 */
19901433eb99SBorislav Petkov 	cs_mode = (pvt->dbam0 >> ((csrow_nr / 2) * 4)) & 0xF;
19910ec449eeSDoug Thompson 
19921433eb99SBorislav Petkov 	nr_pages = pvt->ops->dbam_to_cs(pvt, cs_mode) << (20 - PAGE_SHIFT);
19930ec449eeSDoug Thompson 
19940ec449eeSDoug Thompson 	/*
19950ec449eeSDoug Thompson 	 * If dual channel then double the memory size of single channel.
19960ec449eeSDoug Thompson 	 * Channel count is 1 or 2
19970ec449eeSDoug Thompson 	 */
19980ec449eeSDoug Thompson 	nr_pages <<= (pvt->channel_count - 1);
19990ec449eeSDoug Thompson 
20001433eb99SBorislav Petkov 	debugf0("  (csrow=%d) DBAM map index= %d\n", csrow_nr, cs_mode);
20010ec449eeSDoug Thompson 	debugf0("    nr_pages= %u  channel-count = %d\n",
20020ec449eeSDoug Thompson 		nr_pages, pvt->channel_count);
20030ec449eeSDoug Thompson 
20040ec449eeSDoug Thompson 	return nr_pages;
20050ec449eeSDoug Thompson }
20060ec449eeSDoug Thompson 
20070ec449eeSDoug Thompson /*
20080ec449eeSDoug Thompson  * Initialize the array of csrow attribute instances, based on the values
20090ec449eeSDoug Thompson  * from pci config hardware registers.
20100ec449eeSDoug Thompson  */
2011360b7f3cSBorislav Petkov static int init_csrows(struct mem_ctl_info *mci)
20120ec449eeSDoug Thompson {
20130ec449eeSDoug Thompson 	struct csrow_info *csrow;
20142299ef71SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
201511c75eadSBorislav Petkov 	u64 input_addr_min, input_addr_max, sys_addr, base, mask;
20162299ef71SBorislav Petkov 	u32 val;
20176ba5dcdcSBorislav Petkov 	int i, empty = 1;
20180ec449eeSDoug Thompson 
2019a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
20200ec449eeSDoug Thompson 
20212299ef71SBorislav Petkov 	pvt->nbcfg = val;
20220ec449eeSDoug Thompson 
20232299ef71SBorislav Petkov 	debugf0("node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
20242299ef71SBorislav Petkov 		pvt->mc_node_id, val,
2025a97fa68eSBorislav Petkov 		!!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
20260ec449eeSDoug Thompson 
202711c75eadSBorislav Petkov 	for_each_chip_select(i, 0, pvt) {
20280ec449eeSDoug Thompson 		csrow = &mci->csrows[i];
20290ec449eeSDoug Thompson 
203011c75eadSBorislav Petkov 		if (!csrow_enabled(i, 0, pvt)) {
20310ec449eeSDoug Thompson 			debugf1("----CSROW %d EMPTY for node %d\n", i,
20320ec449eeSDoug Thompson 				pvt->mc_node_id);
20330ec449eeSDoug Thompson 			continue;
20340ec449eeSDoug Thompson 		}
20350ec449eeSDoug Thompson 
20360ec449eeSDoug Thompson 		debugf1("----CSROW %d VALID for MC node %d\n",
20370ec449eeSDoug Thompson 			i, pvt->mc_node_id);
20380ec449eeSDoug Thompson 
20390ec449eeSDoug Thompson 		empty = 0;
20400ec449eeSDoug Thompson 		csrow->nr_pages = amd64_csrow_nr_pages(i, pvt);
20410ec449eeSDoug Thompson 		find_csrow_limits(mci, i, &input_addr_min, &input_addr_max);
20420ec449eeSDoug Thompson 		sys_addr = input_addr_to_sys_addr(mci, input_addr_min);
20430ec449eeSDoug Thompson 		csrow->first_page = (u32) (sys_addr >> PAGE_SHIFT);
20440ec449eeSDoug Thompson 		sys_addr = input_addr_to_sys_addr(mci, input_addr_max);
20450ec449eeSDoug Thompson 		csrow->last_page = (u32) (sys_addr >> PAGE_SHIFT);
204611c75eadSBorislav Petkov 
204711c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, i, 0, &base, &mask);
204811c75eadSBorislav Petkov 		csrow->page_mask = ~mask;
20490ec449eeSDoug Thompson 		/* 8 bytes of resolution */
20500ec449eeSDoug Thompson 
205124f9a7feSBorislav Petkov 		csrow->mtype = amd64_determine_memory_type(pvt, i);
20520ec449eeSDoug Thompson 
20530ec449eeSDoug Thompson 		debugf1("  for MC node %d csrow %d:\n", pvt->mc_node_id, i);
20540ec449eeSDoug Thompson 		debugf1("    input_addr_min: 0x%lx input_addr_max: 0x%lx\n",
20550ec449eeSDoug Thompson 			(unsigned long)input_addr_min,
20560ec449eeSDoug Thompson 			(unsigned long)input_addr_max);
20570ec449eeSDoug Thompson 		debugf1("    sys_addr: 0x%lx  page_mask: 0x%lx\n",
20580ec449eeSDoug Thompson 			(unsigned long)sys_addr, csrow->page_mask);
20590ec449eeSDoug Thompson 		debugf1("    nr_pages: %u  first_page: 0x%lx "
20600ec449eeSDoug Thompson 			"last_page: 0x%lx\n",
20610ec449eeSDoug Thompson 			(unsigned)csrow->nr_pages,
20620ec449eeSDoug Thompson 			csrow->first_page, csrow->last_page);
20630ec449eeSDoug Thompson 
20640ec449eeSDoug Thompson 		/*
20650ec449eeSDoug Thompson 		 * determine whether CHIPKILL or JUST ECC or NO ECC is operating
20660ec449eeSDoug Thompson 		 */
2067a97fa68eSBorislav Petkov 		if (pvt->nbcfg & NBCFG_ECC_ENABLE)
20680ec449eeSDoug Thompson 			csrow->edac_mode =
2069a97fa68eSBorislav Petkov 			    (pvt->nbcfg & NBCFG_CHIPKILL) ?
20700ec449eeSDoug Thompson 			    EDAC_S4ECD4ED : EDAC_SECDED;
20710ec449eeSDoug Thompson 		else
20720ec449eeSDoug Thompson 			csrow->edac_mode = EDAC_NONE;
20730ec449eeSDoug Thompson 	}
20740ec449eeSDoug Thompson 
20750ec449eeSDoug Thompson 	return empty;
20760ec449eeSDoug Thompson }
2077d27bf6faSDoug Thompson 
207806724535SBorislav Petkov /* get all cores on this DCT */
2079ba578cb3SRusty Russell static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, int nid)
2080f9431992SDoug Thompson {
208106724535SBorislav Petkov 	int cpu;
2082f9431992SDoug Thompson 
208306724535SBorislav Petkov 	for_each_online_cpu(cpu)
208406724535SBorislav Petkov 		if (amd_get_nb_id(cpu) == nid)
208506724535SBorislav Petkov 			cpumask_set_cpu(cpu, mask);
2086f9431992SDoug Thompson }
2087f9431992SDoug Thompson 
2088f9431992SDoug Thompson /* check MCG_CTL on all the cpus on this node */
208906724535SBorislav Petkov static bool amd64_nb_mce_bank_enabled_on_node(int nid)
2090f9431992SDoug Thompson {
2091ba578cb3SRusty Russell 	cpumask_var_t mask;
209250542251SBorislav Petkov 	int cpu, nbe;
209306724535SBorislav Petkov 	bool ret = false;
2094f9431992SDoug Thompson 
2095ba578cb3SRusty Russell 	if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
209624f9a7feSBorislav Petkov 		amd64_warn("%s: Error allocating mask\n", __func__);
209706724535SBorislav Petkov 		return false;
209806724535SBorislav Petkov 	}
209906724535SBorislav Petkov 
2100ba578cb3SRusty Russell 	get_cpus_on_this_dct_cpumask(mask, nid);
210106724535SBorislav Petkov 
2102ba578cb3SRusty Russell 	rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
2103ba578cb3SRusty Russell 
2104ba578cb3SRusty Russell 	for_each_cpu(cpu, mask) {
210550542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
21065980bb9cSBorislav Petkov 		nbe = reg->l & MSR_MCGCTL_NBE;
210706724535SBorislav Petkov 
210806724535SBorislav Petkov 		debugf0("core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
210950542251SBorislav Petkov 			cpu, reg->q,
211006724535SBorislav Petkov 			(nbe ? "enabled" : "disabled"));
211106724535SBorislav Petkov 
211206724535SBorislav Petkov 		if (!nbe)
211306724535SBorislav Petkov 			goto out;
211406724535SBorislav Petkov 	}
211506724535SBorislav Petkov 	ret = true;
211606724535SBorislav Petkov 
211706724535SBorislav Petkov out:
2118ba578cb3SRusty Russell 	free_cpumask_var(mask);
2119f9431992SDoug Thompson 	return ret;
2120f9431992SDoug Thompson }
2121f9431992SDoug Thompson 
21222299ef71SBorislav Petkov static int toggle_ecc_err_reporting(struct ecc_settings *s, u8 nid, bool on)
2123f6d6ae96SBorislav Petkov {
2124f6d6ae96SBorislav Petkov 	cpumask_var_t cmask;
212550542251SBorislav Petkov 	int cpu;
2126f6d6ae96SBorislav Petkov 
2127f6d6ae96SBorislav Petkov 	if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
212824f9a7feSBorislav Petkov 		amd64_warn("%s: error allocating mask\n", __func__);
2129f6d6ae96SBorislav Petkov 		return false;
2130f6d6ae96SBorislav Petkov 	}
2131f6d6ae96SBorislav Petkov 
2132ae7bb7c6SBorislav Petkov 	get_cpus_on_this_dct_cpumask(cmask, nid);
2133f6d6ae96SBorislav Petkov 
2134f6d6ae96SBorislav Petkov 	rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2135f6d6ae96SBorislav Petkov 
2136f6d6ae96SBorislav Petkov 	for_each_cpu(cpu, cmask) {
2137f6d6ae96SBorislav Petkov 
213850542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
213950542251SBorislav Petkov 
2140f6d6ae96SBorislav Petkov 		if (on) {
21415980bb9cSBorislav Petkov 			if (reg->l & MSR_MCGCTL_NBE)
2142ae7bb7c6SBorislav Petkov 				s->flags.nb_mce_enable = 1;
2143f6d6ae96SBorislav Petkov 
21445980bb9cSBorislav Petkov 			reg->l |= MSR_MCGCTL_NBE;
2145f6d6ae96SBorislav Petkov 		} else {
2146f6d6ae96SBorislav Petkov 			/*
2147d95cf4deSBorislav Petkov 			 * Turn off NB MCE reporting only when it was off before
2148f6d6ae96SBorislav Petkov 			 */
2149ae7bb7c6SBorislav Petkov 			if (!s->flags.nb_mce_enable)
21505980bb9cSBorislav Petkov 				reg->l &= ~MSR_MCGCTL_NBE;
2151f6d6ae96SBorislav Petkov 		}
2152f6d6ae96SBorislav Petkov 	}
2153f6d6ae96SBorislav Petkov 	wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2154f6d6ae96SBorislav Petkov 
2155f6d6ae96SBorislav Petkov 	free_cpumask_var(cmask);
2156f6d6ae96SBorislav Petkov 
2157f6d6ae96SBorislav Petkov 	return 0;
2158f6d6ae96SBorislav Petkov }
2159f6d6ae96SBorislav Petkov 
21602299ef71SBorislav Petkov static bool enable_ecc_error_reporting(struct ecc_settings *s, u8 nid,
21612299ef71SBorislav Petkov 				       struct pci_dev *F3)
2162f6d6ae96SBorislav Petkov {
21632299ef71SBorislav Petkov 	bool ret = true;
2164c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
2165f6d6ae96SBorislav Petkov 
21662299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, ON)) {
21672299ef71SBorislav Petkov 		amd64_warn("Error enabling ECC reporting over MCGCTL!\n");
21682299ef71SBorislav Petkov 		return false;
21692299ef71SBorislav Petkov 	}
21702299ef71SBorislav Petkov 
2171c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
2172f6d6ae96SBorislav Petkov 
2173ae7bb7c6SBorislav Petkov 	s->old_nbctl   = value & mask;
2174ae7bb7c6SBorislav Petkov 	s->nbctl_valid = true;
2175f6d6ae96SBorislav Petkov 
2176f6d6ae96SBorislav Petkov 	value |= mask;
2177c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
2178f6d6ae96SBorislav Petkov 
2179a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCFG, &value);
2180f6d6ae96SBorislav Petkov 
2181a97fa68eSBorislav Petkov 	debugf0("1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2182a97fa68eSBorislav Petkov 		nid, value, !!(value & NBCFG_ECC_ENABLE));
2183f6d6ae96SBorislav Petkov 
2184a97fa68eSBorislav Petkov 	if (!(value & NBCFG_ECC_ENABLE)) {
218524f9a7feSBorislav Petkov 		amd64_warn("DRAM ECC disabled on this node, enabling...\n");
2186f6d6ae96SBorislav Petkov 
2187ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 0;
2188d95cf4deSBorislav Petkov 
2189f6d6ae96SBorislav Petkov 		/* Attempt to turn on DRAM ECC Enable */
2190a97fa68eSBorislav Petkov 		value |= NBCFG_ECC_ENABLE;
2191a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
2192f6d6ae96SBorislav Petkov 
2193a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
2194f6d6ae96SBorislav Petkov 
2195a97fa68eSBorislav Petkov 		if (!(value & NBCFG_ECC_ENABLE)) {
219624f9a7feSBorislav Petkov 			amd64_warn("Hardware rejected DRAM ECC enable,"
219724f9a7feSBorislav Petkov 				   "check memory DIMM configuration.\n");
21982299ef71SBorislav Petkov 			ret = false;
2199f6d6ae96SBorislav Petkov 		} else {
220024f9a7feSBorislav Petkov 			amd64_info("Hardware accepted DRAM ECC Enable\n");
2201f6d6ae96SBorislav Petkov 		}
2202d95cf4deSBorislav Petkov 	} else {
2203ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 1;
2204f6d6ae96SBorislav Petkov 	}
2205d95cf4deSBorislav Petkov 
2206a97fa68eSBorislav Petkov 	debugf0("2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2207a97fa68eSBorislav Petkov 		nid, value, !!(value & NBCFG_ECC_ENABLE));
2208f6d6ae96SBorislav Petkov 
22092299ef71SBorislav Petkov 	return ret;
2210f6d6ae96SBorislav Petkov }
2211f6d6ae96SBorislav Petkov 
2212360b7f3cSBorislav Petkov static void restore_ecc_error_reporting(struct ecc_settings *s, u8 nid,
2213360b7f3cSBorislav Petkov 					struct pci_dev *F3)
2214f6d6ae96SBorislav Petkov {
2215c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
2216c9f4f26eSBorislav Petkov 
2217f6d6ae96SBorislav Petkov 
2218ae7bb7c6SBorislav Petkov 	if (!s->nbctl_valid)
2219f6d6ae96SBorislav Petkov 		return;
2220f6d6ae96SBorislav Petkov 
2221c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
2222f6d6ae96SBorislav Petkov 	value &= ~mask;
2223ae7bb7c6SBorislav Petkov 	value |= s->old_nbctl;
2224f6d6ae96SBorislav Petkov 
2225c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
2226f6d6ae96SBorislav Petkov 
2227ae7bb7c6SBorislav Petkov 	/* restore previous BIOS DRAM ECC "off" setting we force-enabled */
2228ae7bb7c6SBorislav Petkov 	if (!s->flags.nb_ecc_prev) {
2229a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
2230a97fa68eSBorislav Petkov 		value &= ~NBCFG_ECC_ENABLE;
2231a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
2232d95cf4deSBorislav Petkov 	}
2233d95cf4deSBorislav Petkov 
2234d95cf4deSBorislav Petkov 	/* restore the NB Enable MCGCTL bit */
22352299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, OFF))
223624f9a7feSBorislav Petkov 		amd64_warn("Error restoring NB MCGCTL settings!\n");
2237f6d6ae96SBorislav Petkov }
2238f6d6ae96SBorislav Petkov 
2239f9431992SDoug Thompson /*
22402299ef71SBorislav Petkov  * EDAC requires that the BIOS have ECC enabled before
22412299ef71SBorislav Petkov  * taking over the processing of ECC errors. A command line
22422299ef71SBorislav Petkov  * option allows to force-enable hardware ECC later in
22432299ef71SBorislav Petkov  * enable_ecc_error_reporting().
2244f9431992SDoug Thompson  */
2245cab4d277SBorislav Petkov static const char *ecc_msg =
2246cab4d277SBorislav Petkov 	"ECC disabled in the BIOS or no ECC capability, module will not load.\n"
2247cab4d277SBorislav Petkov 	" Either enable ECC checking or force module loading by setting "
2248cab4d277SBorislav Petkov 	"'ecc_enable_override'.\n"
2249cab4d277SBorislav Petkov 	" (Note that use of the override may cause unknown side effects.)\n";
2250be3468e8SBorislav Petkov 
22512299ef71SBorislav Petkov static bool ecc_enabled(struct pci_dev *F3, u8 nid)
2252f9431992SDoug Thompson {
2253f9431992SDoug Thompson 	u32 value;
22542299ef71SBorislav Petkov 	u8 ecc_en = 0;
225506724535SBorislav Petkov 	bool nb_mce_en = false;
2256f9431992SDoug Thompson 
2257a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCFG, &value);
2258f9431992SDoug Thompson 
2259a97fa68eSBorislav Petkov 	ecc_en = !!(value & NBCFG_ECC_ENABLE);
22602299ef71SBorislav Petkov 	amd64_info("DRAM ECC %s.\n", (ecc_en ? "enabled" : "disabled"));
2261be3468e8SBorislav Petkov 
22622299ef71SBorislav Petkov 	nb_mce_en = amd64_nb_mce_bank_enabled_on_node(nid);
226306724535SBorislav Petkov 	if (!nb_mce_en)
22642299ef71SBorislav Petkov 		amd64_notice("NB MCE bank disabled, set MSR "
22652299ef71SBorislav Petkov 			     "0x%08x[4] on node %d to enable.\n",
22662299ef71SBorislav Petkov 			     MSR_IA32_MCG_CTL, nid);
2267be3468e8SBorislav Petkov 
22682299ef71SBorislav Petkov 	if (!ecc_en || !nb_mce_en) {
226924f9a7feSBorislav Petkov 		amd64_notice("%s", ecc_msg);
22702299ef71SBorislav Petkov 		return false;
2271be3468e8SBorislav Petkov 	}
22722299ef71SBorislav Petkov 	return true;
2273f9431992SDoug Thompson }
2274f9431992SDoug Thompson 
22757d6034d3SDoug Thompson struct mcidev_sysfs_attribute sysfs_attrs[ARRAY_SIZE(amd64_dbg_attrs) +
22767d6034d3SDoug Thompson 					  ARRAY_SIZE(amd64_inj_attrs) +
22777d6034d3SDoug Thompson 					  1];
22787d6034d3SDoug Thompson 
22797d6034d3SDoug Thompson struct mcidev_sysfs_attribute terminator = { .attr = { .name = NULL } };
22807d6034d3SDoug Thompson 
2281360b7f3cSBorislav Petkov static void set_mc_sysfs_attrs(struct mem_ctl_info *mci)
22827d6034d3SDoug Thompson {
22837d6034d3SDoug Thompson 	unsigned int i = 0, j = 0;
22847d6034d3SDoug Thompson 
22857d6034d3SDoug Thompson 	for (; i < ARRAY_SIZE(amd64_dbg_attrs); i++)
22867d6034d3SDoug Thompson 		sysfs_attrs[i] = amd64_dbg_attrs[i];
22877d6034d3SDoug Thompson 
2288a135cef7SBorislav Petkov 	if (boot_cpu_data.x86 >= 0x10)
22897d6034d3SDoug Thompson 		for (j = 0; j < ARRAY_SIZE(amd64_inj_attrs); j++, i++)
22907d6034d3SDoug Thompson 			sysfs_attrs[i] = amd64_inj_attrs[j];
22917d6034d3SDoug Thompson 
22927d6034d3SDoug Thompson 	sysfs_attrs[i] = terminator;
22937d6034d3SDoug Thompson 
22947d6034d3SDoug Thompson 	mci->mc_driver_sysfs_attributes = sysfs_attrs;
22957d6034d3SDoug Thompson }
22967d6034d3SDoug Thompson 
2297360b7f3cSBorislav Petkov static void setup_mci_misc_attrs(struct mem_ctl_info *mci)
22987d6034d3SDoug Thompson {
22997d6034d3SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
23007d6034d3SDoug Thompson 
23017d6034d3SDoug Thompson 	mci->mtype_cap		= MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
23027d6034d3SDoug Thompson 	mci->edac_ctl_cap	= EDAC_FLAG_NONE;
23037d6034d3SDoug Thompson 
23045980bb9cSBorislav Petkov 	if (pvt->nbcap & NBCAP_SECDED)
23057d6034d3SDoug Thompson 		mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
23067d6034d3SDoug Thompson 
23075980bb9cSBorislav Petkov 	if (pvt->nbcap & NBCAP_CHIPKILL)
23087d6034d3SDoug Thompson 		mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
23097d6034d3SDoug Thompson 
23107d6034d3SDoug Thompson 	mci->edac_cap		= amd64_determine_edac_cap(pvt);
23117d6034d3SDoug Thompson 	mci->mod_name		= EDAC_MOD_STR;
23127d6034d3SDoug Thompson 	mci->mod_ver		= EDAC_AMD64_VERSION;
23130092b20dSBorislav Petkov 	mci->ctl_name		= pvt->ctl_name;
23148d5b5d9cSBorislav Petkov 	mci->dev_name		= pci_name(pvt->F2);
23157d6034d3SDoug Thompson 	mci->ctl_page_to_phys	= NULL;
23167d6034d3SDoug Thompson 
23177d6034d3SDoug Thompson 	/* memory scrubber interface */
23187d6034d3SDoug Thompson 	mci->set_sdram_scrub_rate = amd64_set_scrub_rate;
23197d6034d3SDoug Thompson 	mci->get_sdram_scrub_rate = amd64_get_scrub_rate;
23207d6034d3SDoug Thompson }
23217d6034d3SDoug Thompson 
23220092b20dSBorislav Petkov /*
23230092b20dSBorislav Petkov  * returns a pointer to the family descriptor on success, NULL otherwise.
23240092b20dSBorislav Petkov  */
23250092b20dSBorislav Petkov static struct amd64_family_type *amd64_per_family_init(struct amd64_pvt *pvt)
2326395ae783SBorislav Petkov {
23270092b20dSBorislav Petkov 	u8 fam = boot_cpu_data.x86;
23280092b20dSBorislav Petkov 	struct amd64_family_type *fam_type = NULL;
23290092b20dSBorislav Petkov 
23300092b20dSBorislav Petkov 	switch (fam) {
2331395ae783SBorislav Petkov 	case 0xf:
23320092b20dSBorislav Petkov 		fam_type		= &amd64_family_types[K8_CPUS];
2333b8cfa02fSBorislav Petkov 		pvt->ops		= &amd64_family_types[K8_CPUS].ops;
23340092b20dSBorislav Petkov 		pvt->ctl_name		= fam_type->ctl_name;
2335395ae783SBorislav Petkov 		pvt->min_scrubrate	= K8_MIN_SCRUB_RATE_BITS;
2336395ae783SBorislav Petkov 		break;
2337395ae783SBorislav Petkov 	case 0x10:
23380092b20dSBorislav Petkov 		fam_type		= &amd64_family_types[F10_CPUS];
2339b8cfa02fSBorislav Petkov 		pvt->ops		= &amd64_family_types[F10_CPUS].ops;
23400092b20dSBorislav Petkov 		pvt->ctl_name		= fam_type->ctl_name;
2341395ae783SBorislav Petkov 		pvt->min_scrubrate	= F10_MIN_SCRUB_RATE_BITS;
2342395ae783SBorislav Petkov 		break;
2343395ae783SBorislav Petkov 
2344395ae783SBorislav Petkov 	default:
234524f9a7feSBorislav Petkov 		amd64_err("Unsupported family!\n");
23460092b20dSBorislav Petkov 		return NULL;
2347395ae783SBorislav Petkov 	}
23480092b20dSBorislav Petkov 
2349b8cfa02fSBorislav Petkov 	pvt->ext_model = boot_cpu_data.x86_model >> 4;
2350b8cfa02fSBorislav Petkov 
235124f9a7feSBorislav Petkov 	amd64_info("%s %sdetected (node %d).\n", pvt->ctl_name,
23520092b20dSBorislav Petkov 		     (fam == 0xf ?
23530092b20dSBorislav Petkov 				(pvt->ext_model >= K8_REV_F  ? "revF or later "
23540092b20dSBorislav Petkov 							     : "revE or earlier ")
235524f9a7feSBorislav Petkov 				 : ""), pvt->mc_node_id);
23560092b20dSBorislav Petkov 	return fam_type;
2357395ae783SBorislav Petkov }
2358395ae783SBorislav Petkov 
23592299ef71SBorislav Petkov static int amd64_init_one_instance(struct pci_dev *F2)
23607d6034d3SDoug Thompson {
23617d6034d3SDoug Thompson 	struct amd64_pvt *pvt = NULL;
23620092b20dSBorislav Petkov 	struct amd64_family_type *fam_type = NULL;
2363360b7f3cSBorislav Petkov 	struct mem_ctl_info *mci = NULL;
23647d6034d3SDoug Thompson 	int err = 0, ret;
2365360b7f3cSBorislav Petkov 	u8 nid = get_node_id(F2);
23667d6034d3SDoug Thompson 
23677d6034d3SDoug Thompson 	ret = -ENOMEM;
23687d6034d3SDoug Thompson 	pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
23697d6034d3SDoug Thompson 	if (!pvt)
2370360b7f3cSBorislav Petkov 		goto err_ret;
23717d6034d3SDoug Thompson 
2372360b7f3cSBorislav Petkov 	pvt->mc_node_id	= nid;
23738d5b5d9cSBorislav Petkov 	pvt->F2 = F2;
23747d6034d3SDoug Thompson 
2375395ae783SBorislav Petkov 	ret = -EINVAL;
23760092b20dSBorislav Petkov 	fam_type = amd64_per_family_init(pvt);
23770092b20dSBorislav Petkov 	if (!fam_type)
2378395ae783SBorislav Petkov 		goto err_free;
2379395ae783SBorislav Petkov 
23807d6034d3SDoug Thompson 	ret = -ENODEV;
2381360b7f3cSBorislav Petkov 	err = reserve_mc_sibling_devs(pvt, fam_type->f1_id, fam_type->f3_id);
23827d6034d3SDoug Thompson 	if (err)
23837d6034d3SDoug Thompson 		goto err_free;
23847d6034d3SDoug Thompson 
2385360b7f3cSBorislav Petkov 	read_mc_regs(pvt);
23867d6034d3SDoug Thompson 
23877d6034d3SDoug Thompson 	/*
23887d6034d3SDoug Thompson 	 * We need to determine how many memory channels there are. Then use
23897d6034d3SDoug Thompson 	 * that information for calculating the size of the dynamic instance
2390360b7f3cSBorislav Petkov 	 * tables in the 'mci' structure.
23917d6034d3SDoug Thompson 	 */
2392360b7f3cSBorislav Petkov 	ret = -EINVAL;
23937d6034d3SDoug Thompson 	pvt->channel_count = pvt->ops->early_channel_count(pvt);
23947d6034d3SDoug Thompson 	if (pvt->channel_count < 0)
2395360b7f3cSBorislav Petkov 		goto err_siblings;
23967d6034d3SDoug Thompson 
23977d6034d3SDoug Thompson 	ret = -ENOMEM;
239811c75eadSBorislav Petkov 	mci = edac_mc_alloc(0, pvt->csels[0].b_cnt, pvt->channel_count, nid);
23997d6034d3SDoug Thompson 	if (!mci)
2400360b7f3cSBorislav Petkov 		goto err_siblings;
24017d6034d3SDoug Thompson 
24027d6034d3SDoug Thompson 	mci->pvt_info = pvt;
24038d5b5d9cSBorislav Petkov 	mci->dev = &pvt->F2->dev;
24047d6034d3SDoug Thompson 
2405360b7f3cSBorislav Petkov 	setup_mci_misc_attrs(mci);
2406360b7f3cSBorislav Petkov 
2407360b7f3cSBorislav Petkov 	if (init_csrows(mci))
24087d6034d3SDoug Thompson 		mci->edac_cap = EDAC_FLAG_NONE;
24097d6034d3SDoug Thompson 
2410360b7f3cSBorislav Petkov 	set_mc_sysfs_attrs(mci);
24117d6034d3SDoug Thompson 
24127d6034d3SDoug Thompson 	ret = -ENODEV;
24137d6034d3SDoug Thompson 	if (edac_mc_add_mc(mci)) {
24147d6034d3SDoug Thompson 		debugf1("failed edac_mc_add_mc()\n");
24157d6034d3SDoug Thompson 		goto err_add_mc;
24167d6034d3SDoug Thompson 	}
24177d6034d3SDoug Thompson 
2418549d042dSBorislav Petkov 	/* register stuff with EDAC MCE */
2419549d042dSBorislav Petkov 	if (report_gart_errors)
2420549d042dSBorislav Petkov 		amd_report_gart_errors(true);
2421549d042dSBorislav Petkov 
2422549d042dSBorislav Petkov 	amd_register_ecc_decoder(amd64_decode_bus_error);
2423549d042dSBorislav Petkov 
2424360b7f3cSBorislav Petkov 	mcis[nid] = mci;
2425360b7f3cSBorislav Petkov 
2426360b7f3cSBorislav Petkov 	atomic_inc(&drv_instances);
2427360b7f3cSBorislav Petkov 
24287d6034d3SDoug Thompson 	return 0;
24297d6034d3SDoug Thompson 
24307d6034d3SDoug Thompson err_add_mc:
24317d6034d3SDoug Thompson 	edac_mc_free(mci);
24327d6034d3SDoug Thompson 
2433360b7f3cSBorislav Petkov err_siblings:
2434360b7f3cSBorislav Petkov 	free_mc_sibling_devs(pvt);
24357d6034d3SDoug Thompson 
2436360b7f3cSBorislav Petkov err_free:
2437360b7f3cSBorislav Petkov 	kfree(pvt);
24387d6034d3SDoug Thompson 
2439360b7f3cSBorislav Petkov err_ret:
24407d6034d3SDoug Thompson 	return ret;
24417d6034d3SDoug Thompson }
24427d6034d3SDoug Thompson 
24432299ef71SBorislav Petkov static int __devinit amd64_probe_one_instance(struct pci_dev *pdev,
24447d6034d3SDoug Thompson 					     const struct pci_device_id *mc_type)
24457d6034d3SDoug Thompson {
2446ae7bb7c6SBorislav Petkov 	u8 nid = get_node_id(pdev);
24472299ef71SBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
2448ae7bb7c6SBorislav Petkov 	struct ecc_settings *s;
24492299ef71SBorislav Petkov 	int ret = 0;
24507d6034d3SDoug Thompson 
24517d6034d3SDoug Thompson 	ret = pci_enable_device(pdev);
2452b8cfa02fSBorislav Petkov 	if (ret < 0) {
24537d6034d3SDoug Thompson 		debugf0("ret=%d\n", ret);
2454b8cfa02fSBorislav Petkov 		return -EIO;
2455b8cfa02fSBorislav Petkov 	}
2456b8cfa02fSBorislav Petkov 
2457ae7bb7c6SBorislav Petkov 	ret = -ENOMEM;
2458ae7bb7c6SBorislav Petkov 	s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL);
2459ae7bb7c6SBorislav Petkov 	if (!s)
24602299ef71SBorislav Petkov 		goto err_out;
2461ae7bb7c6SBorislav Petkov 
2462ae7bb7c6SBorislav Petkov 	ecc_stngs[nid] = s;
2463ae7bb7c6SBorislav Petkov 
24642299ef71SBorislav Petkov 	if (!ecc_enabled(F3, nid)) {
24652299ef71SBorislav Petkov 		ret = -ENODEV;
24662299ef71SBorislav Petkov 
24672299ef71SBorislav Petkov 		if (!ecc_enable_override)
24682299ef71SBorislav Petkov 			goto err_enable;
24692299ef71SBorislav Petkov 
24702299ef71SBorislav Petkov 		amd64_warn("Forcing ECC on!\n");
24712299ef71SBorislav Petkov 
24722299ef71SBorislav Petkov 		if (!enable_ecc_error_reporting(s, nid, F3))
24732299ef71SBorislav Petkov 			goto err_enable;
24742299ef71SBorislav Petkov 	}
24752299ef71SBorislav Petkov 
24762299ef71SBorislav Petkov 	ret = amd64_init_one_instance(pdev);
2477360b7f3cSBorislav Petkov 	if (ret < 0) {
2478ae7bb7c6SBorislav Petkov 		amd64_err("Error probing instance: %d\n", nid);
2479360b7f3cSBorislav Petkov 		restore_ecc_error_reporting(s, nid, F3);
2480360b7f3cSBorislav Petkov 	}
24817d6034d3SDoug Thompson 
24827d6034d3SDoug Thompson 	return ret;
24832299ef71SBorislav Petkov 
24842299ef71SBorislav Petkov err_enable:
24852299ef71SBorislav Petkov 	kfree(s);
24862299ef71SBorislav Petkov 	ecc_stngs[nid] = NULL;
24872299ef71SBorislav Petkov 
24882299ef71SBorislav Petkov err_out:
24892299ef71SBorislav Petkov 	return ret;
24907d6034d3SDoug Thompson }
24917d6034d3SDoug Thompson 
24927d6034d3SDoug Thompson static void __devexit amd64_remove_one_instance(struct pci_dev *pdev)
24937d6034d3SDoug Thompson {
24947d6034d3SDoug Thompson 	struct mem_ctl_info *mci;
24957d6034d3SDoug Thompson 	struct amd64_pvt *pvt;
2496360b7f3cSBorislav Petkov 	u8 nid = get_node_id(pdev);
2497360b7f3cSBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
2498360b7f3cSBorislav Petkov 	struct ecc_settings *s = ecc_stngs[nid];
24997d6034d3SDoug Thompson 
25007d6034d3SDoug Thompson 	/* Remove from EDAC CORE tracking list */
25017d6034d3SDoug Thompson 	mci = edac_mc_del_mc(&pdev->dev);
25027d6034d3SDoug Thompson 	if (!mci)
25037d6034d3SDoug Thompson 		return;
25047d6034d3SDoug Thompson 
25057d6034d3SDoug Thompson 	pvt = mci->pvt_info;
25067d6034d3SDoug Thompson 
2507360b7f3cSBorislav Petkov 	restore_ecc_error_reporting(s, nid, F3);
25087d6034d3SDoug Thompson 
2509360b7f3cSBorislav Petkov 	free_mc_sibling_devs(pvt);
25107d6034d3SDoug Thompson 
2511549d042dSBorislav Petkov 	/* unregister from EDAC MCE */
2512549d042dSBorislav Petkov 	amd_report_gart_errors(false);
2513549d042dSBorislav Petkov 	amd_unregister_ecc_decoder(amd64_decode_bus_error);
2514549d042dSBorislav Petkov 
2515360b7f3cSBorislav Petkov 	kfree(ecc_stngs[nid]);
2516360b7f3cSBorislav Petkov 	ecc_stngs[nid] = NULL;
2517ae7bb7c6SBorislav Petkov 
25187d6034d3SDoug Thompson 	/* Free the EDAC CORE resources */
25198f68ed97SBorislav Petkov 	mci->pvt_info = NULL;
2520360b7f3cSBorislav Petkov 	mcis[nid] = NULL;
25218f68ed97SBorislav Petkov 
25228f68ed97SBorislav Petkov 	kfree(pvt);
25237d6034d3SDoug Thompson 	edac_mc_free(mci);
25247d6034d3SDoug Thompson }
25257d6034d3SDoug Thompson 
25267d6034d3SDoug Thompson /*
25277d6034d3SDoug Thompson  * This table is part of the interface for loading drivers for PCI devices. The
25287d6034d3SDoug Thompson  * PCI core identifies what devices are on a system during boot, and then
25297d6034d3SDoug Thompson  * inquiry this table to see if this driver is for a given device found.
25307d6034d3SDoug Thompson  */
25317d6034d3SDoug Thompson static const struct pci_device_id amd64_pci_table[] __devinitdata = {
25327d6034d3SDoug Thompson 	{
25337d6034d3SDoug Thompson 		.vendor		= PCI_VENDOR_ID_AMD,
25347d6034d3SDoug Thompson 		.device		= PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
25357d6034d3SDoug Thompson 		.subvendor	= PCI_ANY_ID,
25367d6034d3SDoug Thompson 		.subdevice	= PCI_ANY_ID,
25377d6034d3SDoug Thompson 		.class		= 0,
25387d6034d3SDoug Thompson 		.class_mask	= 0,
25397d6034d3SDoug Thompson 	},
25407d6034d3SDoug Thompson 	{
25417d6034d3SDoug Thompson 		.vendor		= PCI_VENDOR_ID_AMD,
25427d6034d3SDoug Thompson 		.device		= PCI_DEVICE_ID_AMD_10H_NB_DRAM,
25437d6034d3SDoug Thompson 		.subvendor	= PCI_ANY_ID,
25447d6034d3SDoug Thompson 		.subdevice	= PCI_ANY_ID,
25457d6034d3SDoug Thompson 		.class		= 0,
25467d6034d3SDoug Thompson 		.class_mask	= 0,
25477d6034d3SDoug Thompson 	},
25487d6034d3SDoug Thompson 	{0, }
25497d6034d3SDoug Thompson };
25507d6034d3SDoug Thompson MODULE_DEVICE_TABLE(pci, amd64_pci_table);
25517d6034d3SDoug Thompson 
25527d6034d3SDoug Thompson static struct pci_driver amd64_pci_driver = {
25537d6034d3SDoug Thompson 	.name		= EDAC_MOD_STR,
25542299ef71SBorislav Petkov 	.probe		= amd64_probe_one_instance,
25557d6034d3SDoug Thompson 	.remove		= __devexit_p(amd64_remove_one_instance),
25567d6034d3SDoug Thompson 	.id_table	= amd64_pci_table,
25577d6034d3SDoug Thompson };
25587d6034d3SDoug Thompson 
2559360b7f3cSBorislav Petkov static void setup_pci_device(void)
25607d6034d3SDoug Thompson {
25617d6034d3SDoug Thompson 	struct mem_ctl_info *mci;
25627d6034d3SDoug Thompson 	struct amd64_pvt *pvt;
25637d6034d3SDoug Thompson 
25647d6034d3SDoug Thompson 	if (amd64_ctl_pci)
25657d6034d3SDoug Thompson 		return;
25667d6034d3SDoug Thompson 
2567cc4d8860SBorislav Petkov 	mci = mcis[0];
25687d6034d3SDoug Thompson 	if (mci) {
25697d6034d3SDoug Thompson 
25707d6034d3SDoug Thompson 		pvt = mci->pvt_info;
25717d6034d3SDoug Thompson 		amd64_ctl_pci =
25728d5b5d9cSBorislav Petkov 			edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR);
25737d6034d3SDoug Thompson 
25747d6034d3SDoug Thompson 		if (!amd64_ctl_pci) {
25757d6034d3SDoug Thompson 			pr_warning("%s(): Unable to create PCI control\n",
25767d6034d3SDoug Thompson 				   __func__);
25777d6034d3SDoug Thompson 
25787d6034d3SDoug Thompson 			pr_warning("%s(): PCI error report via EDAC not set\n",
25797d6034d3SDoug Thompson 				   __func__);
25807d6034d3SDoug Thompson 			}
25817d6034d3SDoug Thompson 	}
25827d6034d3SDoug Thompson }
25837d6034d3SDoug Thompson 
25847d6034d3SDoug Thompson static int __init amd64_edac_init(void)
25857d6034d3SDoug Thompson {
2586360b7f3cSBorislav Petkov 	int err = -ENODEV;
25877d6034d3SDoug Thompson 
25887d6034d3SDoug Thompson 	edac_printk(KERN_INFO, EDAC_MOD_STR, EDAC_AMD64_VERSION "\n");
25897d6034d3SDoug Thompson 
25907d6034d3SDoug Thompson 	opstate_init();
25917d6034d3SDoug Thompson 
25929653a5c7SHans Rosenfeld 	if (amd_cache_northbridges() < 0)
259356b34b91SBorislav Petkov 		goto err_ret;
25947d6034d3SDoug Thompson 
2595cc4d8860SBorislav Petkov 	err = -ENOMEM;
2596cc4d8860SBorislav Petkov 	mcis	  = kzalloc(amd_nb_num() * sizeof(mcis[0]), GFP_KERNEL);
2597ae7bb7c6SBorislav Petkov 	ecc_stngs = kzalloc(amd_nb_num() * sizeof(ecc_stngs[0]), GFP_KERNEL);
2598360b7f3cSBorislav Petkov 	if (!(mcis && ecc_stngs))
2599cc4d8860SBorislav Petkov 		goto err_ret;
2600cc4d8860SBorislav Petkov 
260150542251SBorislav Petkov 	msrs = msrs_alloc();
260256b34b91SBorislav Petkov 	if (!msrs)
2603360b7f3cSBorislav Petkov 		goto err_free;
260450542251SBorislav Petkov 
26057d6034d3SDoug Thompson 	err = pci_register_driver(&amd64_pci_driver);
26067d6034d3SDoug Thompson 	if (err)
260756b34b91SBorislav Petkov 		goto err_pci;
26087d6034d3SDoug Thompson 
260956b34b91SBorislav Petkov 	err = -ENODEV;
2610360b7f3cSBorislav Petkov 	if (!atomic_read(&drv_instances))
2611360b7f3cSBorislav Petkov 		goto err_no_instances;
26127d6034d3SDoug Thompson 
2613360b7f3cSBorislav Petkov 	setup_pci_device();
26147d6034d3SDoug Thompson 	return 0;
26157d6034d3SDoug Thompson 
2616360b7f3cSBorislav Petkov err_no_instances:
26177d6034d3SDoug Thompson 	pci_unregister_driver(&amd64_pci_driver);
2618cc4d8860SBorislav Petkov 
261956b34b91SBorislav Petkov err_pci:
262056b34b91SBorislav Petkov 	msrs_free(msrs);
262156b34b91SBorislav Petkov 	msrs = NULL;
2622cc4d8860SBorislav Petkov 
2623360b7f3cSBorislav Petkov err_free:
2624360b7f3cSBorislav Petkov 	kfree(mcis);
2625360b7f3cSBorislav Petkov 	mcis = NULL;
2626360b7f3cSBorislav Petkov 
2627360b7f3cSBorislav Petkov 	kfree(ecc_stngs);
2628360b7f3cSBorislav Petkov 	ecc_stngs = NULL;
2629360b7f3cSBorislav Petkov 
263056b34b91SBorislav Petkov err_ret:
26317d6034d3SDoug Thompson 	return err;
26327d6034d3SDoug Thompson }
26337d6034d3SDoug Thompson 
26347d6034d3SDoug Thompson static void __exit amd64_edac_exit(void)
26357d6034d3SDoug Thompson {
26367d6034d3SDoug Thompson 	if (amd64_ctl_pci)
26377d6034d3SDoug Thompson 		edac_pci_release_generic_ctl(amd64_ctl_pci);
26387d6034d3SDoug Thompson 
26397d6034d3SDoug Thompson 	pci_unregister_driver(&amd64_pci_driver);
264050542251SBorislav Petkov 
2641ae7bb7c6SBorislav Petkov 	kfree(ecc_stngs);
2642ae7bb7c6SBorislav Petkov 	ecc_stngs = NULL;
2643ae7bb7c6SBorislav Petkov 
2644cc4d8860SBorislav Petkov 	kfree(mcis);
2645cc4d8860SBorislav Petkov 	mcis = NULL;
2646cc4d8860SBorislav Petkov 
264750542251SBorislav Petkov 	msrs_free(msrs);
264850542251SBorislav Petkov 	msrs = NULL;
26497d6034d3SDoug Thompson }
26507d6034d3SDoug Thompson 
26517d6034d3SDoug Thompson module_init(amd64_edac_init);
26527d6034d3SDoug Thompson module_exit(amd64_edac_exit);
26537d6034d3SDoug Thompson 
26547d6034d3SDoug Thompson MODULE_LICENSE("GPL");
26557d6034d3SDoug Thompson MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, "
26567d6034d3SDoug Thompson 		"Dave Peterson, Thayne Harbaugh");
26577d6034d3SDoug Thompson MODULE_DESCRIPTION("MC support for AMD64 memory controllers - "
26587d6034d3SDoug Thompson 		EDAC_AMD64_VERSION);
26597d6034d3SDoug Thompson 
26607d6034d3SDoug Thompson module_param(edac_op_state, int, 0444);
26617d6034d3SDoug Thompson MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
2662