xref: /openbmc/linux/drivers/edac/amd64_edac.c (revision 3f0aba4f)
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 /*
28b70ef010SBorislav Petkov  * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing
29b70ef010SBorislav Petkov  * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching-
30b70ef010SBorislav Petkov  * or higher value'.
31b70ef010SBorislav Petkov  *
32b70ef010SBorislav Petkov  *FIXME: Produce a better mapping/linearisation.
33b70ef010SBorislav Petkov  */
34c7e5301aSDaniel J Blueman static const struct scrubrate {
3539094443SBorislav Petkov        u32 scrubval;           /* bit pattern for scrub rate */
3639094443SBorislav Petkov        u32 bandwidth;          /* bandwidth consumed (bytes/sec) */
3739094443SBorislav Petkov } scrubrates[] = {
38b70ef010SBorislav Petkov 	{ 0x01, 1600000000UL},
39b70ef010SBorislav Petkov 	{ 0x02, 800000000UL},
40b70ef010SBorislav Petkov 	{ 0x03, 400000000UL},
41b70ef010SBorislav Petkov 	{ 0x04, 200000000UL},
42b70ef010SBorislav Petkov 	{ 0x05, 100000000UL},
43b70ef010SBorislav Petkov 	{ 0x06, 50000000UL},
44b70ef010SBorislav Petkov 	{ 0x07, 25000000UL},
45b70ef010SBorislav Petkov 	{ 0x08, 12284069UL},
46b70ef010SBorislav Petkov 	{ 0x09, 6274509UL},
47b70ef010SBorislav Petkov 	{ 0x0A, 3121951UL},
48b70ef010SBorislav Petkov 	{ 0x0B, 1560975UL},
49b70ef010SBorislav Petkov 	{ 0x0C, 781440UL},
50b70ef010SBorislav Petkov 	{ 0x0D, 390720UL},
51b70ef010SBorislav Petkov 	{ 0x0E, 195300UL},
52b70ef010SBorislav Petkov 	{ 0x0F, 97650UL},
53b70ef010SBorislav Petkov 	{ 0x10, 48854UL},
54b70ef010SBorislav Petkov 	{ 0x11, 24427UL},
55b70ef010SBorislav Petkov 	{ 0x12, 12213UL},
56b70ef010SBorislav Petkov 	{ 0x13, 6101UL},
57b70ef010SBorislav Petkov 	{ 0x14, 3051UL},
58b70ef010SBorislav Petkov 	{ 0x15, 1523UL},
59b70ef010SBorislav Petkov 	{ 0x16, 761UL},
60b70ef010SBorislav Petkov 	{ 0x00, 0UL},        /* scrubbing off */
61b70ef010SBorislav Petkov };
62b70ef010SBorislav Petkov 
6366fed2d4SBorislav Petkov int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset,
64b2b0c605SBorislav Petkov 			       u32 *val, const char *func)
65b2b0c605SBorislav Petkov {
66b2b0c605SBorislav Petkov 	int err = 0;
67b2b0c605SBorislav Petkov 
68b2b0c605SBorislav Petkov 	err = pci_read_config_dword(pdev, offset, val);
69b2b0c605SBorislav Petkov 	if (err)
70b2b0c605SBorislav Petkov 		amd64_warn("%s: error reading F%dx%03x.\n",
71b2b0c605SBorislav Petkov 			   func, PCI_FUNC(pdev->devfn), offset);
72b2b0c605SBorislav Petkov 
73b2b0c605SBorislav Petkov 	return err;
74b2b0c605SBorislav Petkov }
75b2b0c605SBorislav Petkov 
76b2b0c605SBorislav Petkov int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset,
77b2b0c605SBorislav Petkov 				u32 val, const char *func)
78b2b0c605SBorislav Petkov {
79b2b0c605SBorislav Petkov 	int err = 0;
80b2b0c605SBorislav Petkov 
81b2b0c605SBorislav Petkov 	err = pci_write_config_dword(pdev, offset, val);
82b2b0c605SBorislav Petkov 	if (err)
83b2b0c605SBorislav Petkov 		amd64_warn("%s: error writing to F%dx%03x.\n",
84b2b0c605SBorislav Petkov 			   func, PCI_FUNC(pdev->devfn), offset);
85b2b0c605SBorislav Petkov 
86b2b0c605SBorislav Petkov 	return err;
87b2b0c605SBorislav Petkov }
88b2b0c605SBorislav Petkov 
89b2b0c605SBorislav Petkov /*
90b2b0c605SBorislav Petkov  *
91b2b0c605SBorislav Petkov  * Depending on the family, F2 DCT reads need special handling:
92b2b0c605SBorislav Petkov  *
93b2b0c605SBorislav Petkov  * K8: has a single DCT only
94b2b0c605SBorislav Petkov  *
95b2b0c605SBorislav Petkov  * F10h: each DCT has its own set of regs
96b2b0c605SBorislav Petkov  *	DCT0 -> F2x040..
97b2b0c605SBorislav Petkov  *	DCT1 -> F2x140..
98b2b0c605SBorislav Petkov  *
99b2b0c605SBorislav Petkov  * F15h: we select which DCT we access using F1x10C[DctCfgSel]
100b2b0c605SBorislav Petkov  *
10194c1acf2SAravind Gopalakrishnan  * F16h: has only 1 DCT
102b2b0c605SBorislav Petkov  */
103b2b0c605SBorislav Petkov static int k8_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
104b2b0c605SBorislav Petkov 			       const char *func)
105b2b0c605SBorislav Petkov {
106b2b0c605SBorislav Petkov 	if (addr >= 0x100)
107b2b0c605SBorislav Petkov 		return -EINVAL;
108b2b0c605SBorislav Petkov 
109b2b0c605SBorislav Petkov 	return __amd64_read_pci_cfg_dword(pvt->F2, addr, val, func);
110b2b0c605SBorislav Petkov }
111b2b0c605SBorislav Petkov 
112b2b0c605SBorislav Petkov static int f10_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
113b2b0c605SBorislav Petkov 				 const char *func)
114b2b0c605SBorislav Petkov {
115b2b0c605SBorislav Petkov 	return __amd64_read_pci_cfg_dword(pvt->F2, addr, val, func);
116b2b0c605SBorislav Petkov }
117b2b0c605SBorislav Petkov 
11873ba8593SBorislav Petkov /*
11973ba8593SBorislav Petkov  * Select DCT to which PCI cfg accesses are routed
12073ba8593SBorislav Petkov  */
12173ba8593SBorislav Petkov static void f15h_select_dct(struct amd64_pvt *pvt, u8 dct)
12273ba8593SBorislav Petkov {
12373ba8593SBorislav Petkov 	u32 reg = 0;
12473ba8593SBorislav Petkov 
12573ba8593SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, &reg);
12618b94f66SAravind Gopalakrishnan 	reg &= (pvt->model >= 0x30) ? ~3 : ~1;
12773ba8593SBorislav Petkov 	reg |= dct;
12873ba8593SBorislav Petkov 	amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg);
12973ba8593SBorislav Petkov }
13073ba8593SBorislav Petkov 
131b2b0c605SBorislav Petkov static int f15_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
132b2b0c605SBorislav Petkov 				 const char *func)
133b2b0c605SBorislav Petkov {
134b2b0c605SBorislav Petkov 	u8 dct  = 0;
135b2b0c605SBorislav Petkov 
13618b94f66SAravind Gopalakrishnan 	/* For F15 M30h, the second dct is DCT 3, refer to BKDG Section 2.10 */
137b2b0c605SBorislav Petkov 	if (addr >= 0x140 && addr <= 0x1a0) {
13818b94f66SAravind Gopalakrishnan 		dct   = (pvt->model >= 0x30) ? 3 : 1;
139b2b0c605SBorislav Petkov 		addr -= 0x100;
140b2b0c605SBorislav Petkov 	}
141b2b0c605SBorislav Petkov 
14273ba8593SBorislav Petkov 	f15h_select_dct(pvt, dct);
143b2b0c605SBorislav Petkov 
144b2b0c605SBorislav Petkov 	return __amd64_read_pci_cfg_dword(pvt->F2, addr, val, func);
145b2b0c605SBorislav Petkov }
146b2b0c605SBorislav Petkov 
147b70ef010SBorislav Petkov /*
1482bc65418SDoug Thompson  * Memory scrubber control interface. For K8, memory scrubbing is handled by
1492bc65418SDoug Thompson  * hardware and can involve L2 cache, dcache as well as the main memory. With
1502bc65418SDoug Thompson  * F10, this is extended to L3 cache scrubbing on CPU models sporting that
1512bc65418SDoug Thompson  * functionality.
1522bc65418SDoug Thompson  *
1532bc65418SDoug Thompson  * This causes the "units" for the scrubbing speed to vary from 64 byte blocks
1542bc65418SDoug Thompson  * (dram) over to cache lines. This is nasty, so we will use bandwidth in
1552bc65418SDoug Thompson  * bytes/sec for the setting.
1562bc65418SDoug Thompson  *
1572bc65418SDoug Thompson  * Currently, we only do dram scrubbing. If the scrubbing is done in software on
1582bc65418SDoug Thompson  * other archs, we might not have access to the caches directly.
1592bc65418SDoug Thompson  */
1602bc65418SDoug Thompson 
1612bc65418SDoug Thompson /*
1622bc65418SDoug Thompson  * scan the scrub rate mapping table for a close or matching bandwidth value to
1632bc65418SDoug Thompson  * issue. If requested is too big, then use last maximum value found.
1642bc65418SDoug Thompson  */
165395ae783SBorislav Petkov static int __amd64_set_scrub_rate(struct pci_dev *ctl, u32 new_bw, u32 min_rate)
1662bc65418SDoug Thompson {
1672bc65418SDoug Thompson 	u32 scrubval;
1682bc65418SDoug Thompson 	int i;
1692bc65418SDoug Thompson 
1702bc65418SDoug Thompson 	/*
1712bc65418SDoug Thompson 	 * map the configured rate (new_bw) to a value specific to the AMD64
1722bc65418SDoug Thompson 	 * memory controller and apply to register. Search for the first
1732bc65418SDoug Thompson 	 * bandwidth entry that is greater or equal than the setting requested
1742bc65418SDoug Thompson 	 * and program that. If at last entry, turn off DRAM scrubbing.
175168bfeefSAndrew Morton 	 *
176168bfeefSAndrew Morton 	 * If no suitable bandwidth is found, turn off DRAM scrubbing entirely
177168bfeefSAndrew Morton 	 * by falling back to the last element in scrubrates[].
1782bc65418SDoug Thompson 	 */
179168bfeefSAndrew Morton 	for (i = 0; i < ARRAY_SIZE(scrubrates) - 1; i++) {
1802bc65418SDoug Thompson 		/*
1812bc65418SDoug Thompson 		 * skip scrub rates which aren't recommended
1822bc65418SDoug Thompson 		 * (see F10 BKDG, F3x58)
1832bc65418SDoug Thompson 		 */
184395ae783SBorislav Petkov 		if (scrubrates[i].scrubval < min_rate)
1852bc65418SDoug Thompson 			continue;
1862bc65418SDoug Thompson 
1872bc65418SDoug Thompson 		if (scrubrates[i].bandwidth <= new_bw)
1882bc65418SDoug Thompson 			break;
1892bc65418SDoug Thompson 	}
1902bc65418SDoug Thompson 
1912bc65418SDoug Thompson 	scrubval = scrubrates[i].scrubval;
1922bc65418SDoug Thompson 
1935980bb9cSBorislav Petkov 	pci_write_bits32(ctl, SCRCTRL, scrubval, 0x001F);
1942bc65418SDoug Thompson 
19539094443SBorislav Petkov 	if (scrubval)
19639094443SBorislav Petkov 		return scrubrates[i].bandwidth;
19739094443SBorislav Petkov 
1982bc65418SDoug Thompson 	return 0;
1992bc65418SDoug Thompson }
2002bc65418SDoug Thompson 
201395ae783SBorislav Petkov static int amd64_set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
2022bc65418SDoug Thompson {
2032bc65418SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
20487b3e0e6SBorislav Petkov 	u32 min_scrubrate = 0x5;
2052bc65418SDoug Thompson 
206a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
20787b3e0e6SBorislav Petkov 		min_scrubrate = 0x0;
20887b3e0e6SBorislav Petkov 
2093f0aba4fSBorislav Petkov 	/* Erratum #505 */
2103f0aba4fSBorislav Petkov 	if (pvt->fam == 0x15 && pvt->model < 0x10)
21173ba8593SBorislav Petkov 		f15h_select_dct(pvt, 0);
21273ba8593SBorislav Petkov 
21387b3e0e6SBorislav Petkov 	return __amd64_set_scrub_rate(pvt->F3, bw, min_scrubrate);
2142bc65418SDoug Thompson }
2152bc65418SDoug Thompson 
21639094443SBorislav Petkov static int amd64_get_scrub_rate(struct mem_ctl_info *mci)
2172bc65418SDoug Thompson {
2182bc65418SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
2192bc65418SDoug Thompson 	u32 scrubval = 0;
22039094443SBorislav Petkov 	int i, retval = -EINVAL;
2212bc65418SDoug Thompson 
2223f0aba4fSBorislav Petkov 	/* Erratum #505 */
2233f0aba4fSBorislav Petkov 	if (pvt->fam == 0x15 && pvt->model < 0x10)
22473ba8593SBorislav Petkov 		f15h_select_dct(pvt, 0);
22573ba8593SBorislav Petkov 
2265980bb9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
2272bc65418SDoug Thompson 
2282bc65418SDoug Thompson 	scrubval = scrubval & 0x001F;
2292bc65418SDoug Thompson 
230926311fdSRoel Kluin 	for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
2312bc65418SDoug Thompson 		if (scrubrates[i].scrubval == scrubval) {
23239094443SBorislav Petkov 			retval = scrubrates[i].bandwidth;
2332bc65418SDoug Thompson 			break;
2342bc65418SDoug Thompson 		}
2352bc65418SDoug Thompson 	}
23639094443SBorislav Petkov 	return retval;
2372bc65418SDoug Thompson }
2382bc65418SDoug Thompson 
2396775763aSDoug Thompson /*
2407f19bf75SBorislav Petkov  * returns true if the SysAddr given by sys_addr matches the
2417f19bf75SBorislav Petkov  * DRAM base/limit associated with node_id
2426775763aSDoug Thompson  */
243b487c33eSBorislav Petkov static bool amd64_base_limit_match(struct amd64_pvt *pvt, u64 sys_addr,
244c7e5301aSDaniel J Blueman 				   u8 nid)
2456775763aSDoug Thompson {
2467f19bf75SBorislav Petkov 	u64 addr;
2476775763aSDoug Thompson 
2486775763aSDoug Thompson 	/* The K8 treats this as a 40-bit value.  However, bits 63-40 will be
2496775763aSDoug Thompson 	 * all ones if the most significant implemented address bit is 1.
2506775763aSDoug Thompson 	 * Here we discard bits 63-40.  See section 3.4.2 of AMD publication
2516775763aSDoug Thompson 	 * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1
2526775763aSDoug Thompson 	 * Application Programming.
2536775763aSDoug Thompson 	 */
2546775763aSDoug Thompson 	addr = sys_addr & 0x000000ffffffffffull;
2556775763aSDoug Thompson 
2567f19bf75SBorislav Petkov 	return ((addr >= get_dram_base(pvt, nid)) &&
2577f19bf75SBorislav Petkov 		(addr <= get_dram_limit(pvt, nid)));
2586775763aSDoug Thompson }
2596775763aSDoug Thompson 
2606775763aSDoug Thompson /*
2616775763aSDoug Thompson  * Attempt to map a SysAddr to a node. On success, return a pointer to the
2626775763aSDoug Thompson  * mem_ctl_info structure for the node that the SysAddr maps to.
2636775763aSDoug Thompson  *
2646775763aSDoug Thompson  * On failure, return NULL.
2656775763aSDoug Thompson  */
2666775763aSDoug Thompson static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci,
2676775763aSDoug Thompson 						u64 sys_addr)
2686775763aSDoug Thompson {
2696775763aSDoug Thompson 	struct amd64_pvt *pvt;
270c7e5301aSDaniel J Blueman 	u8 node_id;
2716775763aSDoug Thompson 	u32 intlv_en, bits;
2726775763aSDoug Thompson 
2736775763aSDoug Thompson 	/*
2746775763aSDoug Thompson 	 * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section
2756775763aSDoug Thompson 	 * 3.4.4.2) registers to map the SysAddr to a node ID.
2766775763aSDoug Thompson 	 */
2776775763aSDoug Thompson 	pvt = mci->pvt_info;
2786775763aSDoug Thompson 
2796775763aSDoug Thompson 	/*
2806775763aSDoug Thompson 	 * The value of this field should be the same for all DRAM Base
2816775763aSDoug Thompson 	 * registers.  Therefore we arbitrarily choose to read it from the
2826775763aSDoug Thompson 	 * register for node 0.
2836775763aSDoug Thompson 	 */
2847f19bf75SBorislav Petkov 	intlv_en = dram_intlv_en(pvt, 0);
2856775763aSDoug Thompson 
2866775763aSDoug Thompson 	if (intlv_en == 0) {
2877f19bf75SBorislav Petkov 		for (node_id = 0; node_id < DRAM_RANGES; node_id++) {
2886775763aSDoug Thompson 			if (amd64_base_limit_match(pvt, sys_addr, node_id))
2896775763aSDoug Thompson 				goto found;
2906775763aSDoug Thompson 		}
2918edc5445SBorislav Petkov 		goto err_no_match;
2928edc5445SBorislav Petkov 	}
2936775763aSDoug Thompson 
29472f158feSBorislav Petkov 	if (unlikely((intlv_en != 0x01) &&
29572f158feSBorislav Petkov 		     (intlv_en != 0x03) &&
29672f158feSBorislav Petkov 		     (intlv_en != 0x07))) {
29724f9a7feSBorislav Petkov 		amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en);
2986775763aSDoug Thompson 		return NULL;
2996775763aSDoug Thompson 	}
3006775763aSDoug Thompson 
3016775763aSDoug Thompson 	bits = (((u32) sys_addr) >> 12) & intlv_en;
3026775763aSDoug Thompson 
3036775763aSDoug Thompson 	for (node_id = 0; ; ) {
3047f19bf75SBorislav Petkov 		if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits)
3056775763aSDoug Thompson 			break;	/* intlv_sel field matches */
3066775763aSDoug Thompson 
3077f19bf75SBorislav Petkov 		if (++node_id >= DRAM_RANGES)
3086775763aSDoug Thompson 			goto err_no_match;
3096775763aSDoug Thompson 	}
3106775763aSDoug Thompson 
3116775763aSDoug Thompson 	/* sanity test for sys_addr */
3126775763aSDoug Thompson 	if (unlikely(!amd64_base_limit_match(pvt, sys_addr, node_id))) {
31324f9a7feSBorislav Petkov 		amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address"
31424f9a7feSBorislav Petkov 			   "range for node %d with node interleaving enabled.\n",
3158edc5445SBorislav Petkov 			   __func__, sys_addr, node_id);
3166775763aSDoug Thompson 		return NULL;
3176775763aSDoug Thompson 	}
3186775763aSDoug Thompson 
3196775763aSDoug Thompson found:
320b487c33eSBorislav Petkov 	return edac_mc_find((int)node_id);
3216775763aSDoug Thompson 
3226775763aSDoug Thompson err_no_match:
323956b9ba1SJoe Perches 	edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n",
3246775763aSDoug Thompson 		 (unsigned long)sys_addr);
3256775763aSDoug Thompson 
3266775763aSDoug Thompson 	return NULL;
3276775763aSDoug Thompson }
328e2ce7255SDoug Thompson 
329e2ce7255SDoug Thompson /*
33011c75eadSBorislav Petkov  * compute the CS base address of the @csrow on the DRAM controller @dct.
33111c75eadSBorislav Petkov  * For details see F2x[5C:40] in the processor's BKDG
332e2ce7255SDoug Thompson  */
33311c75eadSBorislav Petkov static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct,
33411c75eadSBorislav Petkov 				 u64 *base, u64 *mask)
335e2ce7255SDoug Thompson {
33611c75eadSBorislav Petkov 	u64 csbase, csmask, base_bits, mask_bits;
33711c75eadSBorislav Petkov 	u8 addr_shift;
33811c75eadSBorislav Petkov 
33918b94f66SAravind Gopalakrishnan 	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
34011c75eadSBorislav Petkov 		csbase		= pvt->csels[dct].csbases[csrow];
34111c75eadSBorislav Petkov 		csmask		= pvt->csels[dct].csmasks[csrow];
34211c75eadSBorislav Petkov 		base_bits	= GENMASK(21, 31) | GENMASK(9, 15);
34311c75eadSBorislav Petkov 		mask_bits	= GENMASK(21, 29) | GENMASK(9, 15);
34411c75eadSBorislav Petkov 		addr_shift	= 4;
34594c1acf2SAravind Gopalakrishnan 
34694c1acf2SAravind Gopalakrishnan 	/*
34718b94f66SAravind Gopalakrishnan 	 * F16h and F15h, models 30h and later need two addr_shift values:
34818b94f66SAravind Gopalakrishnan 	 * 8 for high and 6 for low (cf. F16h BKDG).
34994c1acf2SAravind Gopalakrishnan 	 */
35018b94f66SAravind Gopalakrishnan 	} else if (pvt->fam == 0x16 ||
35118b94f66SAravind Gopalakrishnan 		  (pvt->fam == 0x15 && pvt->model >= 0x30)) {
35294c1acf2SAravind Gopalakrishnan 		csbase          = pvt->csels[dct].csbases[csrow];
35394c1acf2SAravind Gopalakrishnan 		csmask          = pvt->csels[dct].csmasks[csrow >> 1];
35494c1acf2SAravind Gopalakrishnan 
35594c1acf2SAravind Gopalakrishnan 		*base  = (csbase & GENMASK(5,  15)) << 6;
35694c1acf2SAravind Gopalakrishnan 		*base |= (csbase & GENMASK(19, 30)) << 8;
35794c1acf2SAravind Gopalakrishnan 
35894c1acf2SAravind Gopalakrishnan 		*mask = ~0ULL;
35994c1acf2SAravind Gopalakrishnan 		/* poke holes for the csmask */
36094c1acf2SAravind Gopalakrishnan 		*mask &= ~((GENMASK(5, 15)  << 6) |
36194c1acf2SAravind Gopalakrishnan 			   (GENMASK(19, 30) << 8));
36294c1acf2SAravind Gopalakrishnan 
36394c1acf2SAravind Gopalakrishnan 		*mask |= (csmask & GENMASK(5, 15))  << 6;
36494c1acf2SAravind Gopalakrishnan 		*mask |= (csmask & GENMASK(19, 30)) << 8;
36594c1acf2SAravind Gopalakrishnan 
36694c1acf2SAravind Gopalakrishnan 		return;
36711c75eadSBorislav Petkov 	} else {
36811c75eadSBorislav Petkov 		csbase		= pvt->csels[dct].csbases[csrow];
36911c75eadSBorislav Petkov 		csmask		= pvt->csels[dct].csmasks[csrow >> 1];
37011c75eadSBorislav Petkov 		addr_shift	= 8;
37111c75eadSBorislav Petkov 
372a4b4bedcSBorislav Petkov 		if (pvt->fam == 0x15)
37311c75eadSBorislav Petkov 			base_bits = mask_bits = GENMASK(19,30) | GENMASK(5,13);
37411c75eadSBorislav Petkov 		else
37511c75eadSBorislav Petkov 			base_bits = mask_bits = GENMASK(19,28) | GENMASK(5,13);
376e2ce7255SDoug Thompson 	}
377e2ce7255SDoug Thompson 
37811c75eadSBorislav Petkov 	*base  = (csbase & base_bits) << addr_shift;
379e2ce7255SDoug Thompson 
38011c75eadSBorislav Petkov 	*mask  = ~0ULL;
38111c75eadSBorislav Petkov 	/* poke holes for the csmask */
38211c75eadSBorislav Petkov 	*mask &= ~(mask_bits << addr_shift);
38311c75eadSBorislav Petkov 	/* OR them in */
38411c75eadSBorislav Petkov 	*mask |= (csmask & mask_bits) << addr_shift;
385e2ce7255SDoug Thompson }
386e2ce7255SDoug Thompson 
38711c75eadSBorislav Petkov #define for_each_chip_select(i, dct, pvt) \
38811c75eadSBorislav Petkov 	for (i = 0; i < pvt->csels[dct].b_cnt; i++)
38911c75eadSBorislav Petkov 
390614ec9d8SBorislav Petkov #define chip_select_base(i, dct, pvt) \
391614ec9d8SBorislav Petkov 	pvt->csels[dct].csbases[i]
392614ec9d8SBorislav Petkov 
39311c75eadSBorislav Petkov #define for_each_chip_select_mask(i, dct, pvt) \
39411c75eadSBorislav Petkov 	for (i = 0; i < pvt->csels[dct].m_cnt; i++)
39511c75eadSBorislav Petkov 
396e2ce7255SDoug Thompson /*
397e2ce7255SDoug Thompson  * @input_addr is an InputAddr associated with the node given by mci. Return the
398e2ce7255SDoug Thompson  * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr).
399e2ce7255SDoug Thompson  */
400e2ce7255SDoug Thompson static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr)
401e2ce7255SDoug Thompson {
402e2ce7255SDoug Thompson 	struct amd64_pvt *pvt;
403e2ce7255SDoug Thompson 	int csrow;
404e2ce7255SDoug Thompson 	u64 base, mask;
405e2ce7255SDoug Thompson 
406e2ce7255SDoug Thompson 	pvt = mci->pvt_info;
407e2ce7255SDoug Thompson 
40811c75eadSBorislav Petkov 	for_each_chip_select(csrow, 0, pvt) {
40911c75eadSBorislav Petkov 		if (!csrow_enabled(csrow, 0, pvt))
410e2ce7255SDoug Thompson 			continue;
411e2ce7255SDoug Thompson 
41211c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, csrow, 0, &base, &mask);
41311c75eadSBorislav Petkov 
41411c75eadSBorislav Petkov 		mask = ~mask;
415e2ce7255SDoug Thompson 
416e2ce7255SDoug Thompson 		if ((input_addr & mask) == (base & mask)) {
417956b9ba1SJoe Perches 			edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n",
418e2ce7255SDoug Thompson 				 (unsigned long)input_addr, csrow,
419e2ce7255SDoug Thompson 				 pvt->mc_node_id);
420e2ce7255SDoug Thompson 
421e2ce7255SDoug Thompson 			return csrow;
422e2ce7255SDoug Thompson 		}
423e2ce7255SDoug Thompson 	}
424956b9ba1SJoe Perches 	edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n",
425e2ce7255SDoug Thompson 		 (unsigned long)input_addr, pvt->mc_node_id);
426e2ce7255SDoug Thompson 
427e2ce7255SDoug Thompson 	return -1;
428e2ce7255SDoug Thompson }
429e2ce7255SDoug Thompson 
430e2ce7255SDoug Thompson /*
431e2ce7255SDoug Thompson  * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094)
432e2ce7255SDoug Thompson  * for the node represented by mci. Info is passed back in *hole_base,
433e2ce7255SDoug Thompson  * *hole_offset, and *hole_size.  Function returns 0 if info is valid or 1 if
434e2ce7255SDoug Thompson  * info is invalid. Info may be invalid for either of the following reasons:
435e2ce7255SDoug Thompson  *
436e2ce7255SDoug Thompson  * - The revision of the node is not E or greater.  In this case, the DRAM Hole
437e2ce7255SDoug Thompson  *   Address Register does not exist.
438e2ce7255SDoug Thompson  *
439e2ce7255SDoug Thompson  * - The DramHoleValid bit is cleared in the DRAM Hole Address Register,
440e2ce7255SDoug Thompson  *   indicating that its contents are not valid.
441e2ce7255SDoug Thompson  *
442e2ce7255SDoug Thompson  * The values passed back in *hole_base, *hole_offset, and *hole_size are
443e2ce7255SDoug Thompson  * complete 32-bit values despite the fact that the bitfields in the DHAR
444e2ce7255SDoug Thompson  * only represent bits 31-24 of the base and offset values.
445e2ce7255SDoug Thompson  */
446e2ce7255SDoug Thompson int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
447e2ce7255SDoug Thompson 			     u64 *hole_offset, u64 *hole_size)
448e2ce7255SDoug Thompson {
449e2ce7255SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
450e2ce7255SDoug Thompson 
451e2ce7255SDoug Thompson 	/* only revE and later have the DRAM Hole Address Register */
452a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_E) {
453956b9ba1SJoe Perches 		edac_dbg(1, "  revision %d for node %d does not support DHAR\n",
454e2ce7255SDoug Thompson 			 pvt->ext_model, pvt->mc_node_id);
455e2ce7255SDoug Thompson 		return 1;
456e2ce7255SDoug Thompson 	}
457e2ce7255SDoug Thompson 
458bc21fa57SBorislav Petkov 	/* valid for Fam10h and above */
459a4b4bedcSBorislav Petkov 	if (pvt->fam >= 0x10 && !dhar_mem_hoist_valid(pvt)) {
460956b9ba1SJoe Perches 		edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this system\n");
461e2ce7255SDoug Thompson 		return 1;
462e2ce7255SDoug Thompson 	}
463e2ce7255SDoug Thompson 
464c8e518d5SBorislav Petkov 	if (!dhar_valid(pvt)) {
465956b9ba1SJoe Perches 		edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this node %d\n",
466e2ce7255SDoug Thompson 			 pvt->mc_node_id);
467e2ce7255SDoug Thompson 		return 1;
468e2ce7255SDoug Thompson 	}
469e2ce7255SDoug Thompson 
470e2ce7255SDoug Thompson 	/* This node has Memory Hoisting */
471e2ce7255SDoug Thompson 
472e2ce7255SDoug Thompson 	/* +------------------+--------------------+--------------------+-----
473e2ce7255SDoug Thompson 	 * | memory           | DRAM hole          | relocated          |
474e2ce7255SDoug Thompson 	 * | [0, (x - 1)]     | [x, 0xffffffff]    | addresses from     |
475e2ce7255SDoug Thompson 	 * |                  |                    | DRAM hole          |
476e2ce7255SDoug Thompson 	 * |                  |                    | [0x100000000,      |
477e2ce7255SDoug Thompson 	 * |                  |                    |  (0x100000000+     |
478e2ce7255SDoug Thompson 	 * |                  |                    |   (0xffffffff-x))] |
479e2ce7255SDoug Thompson 	 * +------------------+--------------------+--------------------+-----
480e2ce7255SDoug Thompson 	 *
481e2ce7255SDoug Thompson 	 * Above is a diagram of physical memory showing the DRAM hole and the
482e2ce7255SDoug Thompson 	 * relocated addresses from the DRAM hole.  As shown, the DRAM hole
483e2ce7255SDoug Thompson 	 * starts at address x (the base address) and extends through address
484e2ce7255SDoug Thompson 	 * 0xffffffff.  The DRAM Hole Address Register (DHAR) relocates the
485e2ce7255SDoug Thompson 	 * addresses in the hole so that they start at 0x100000000.
486e2ce7255SDoug Thompson 	 */
487e2ce7255SDoug Thompson 
4881f31677eSBorislav Petkov 	*hole_base = dhar_base(pvt);
4891f31677eSBorislav Petkov 	*hole_size = (1ULL << 32) - *hole_base;
490e2ce7255SDoug Thompson 
491a4b4bedcSBorislav Petkov 	*hole_offset = (pvt->fam > 0xf) ? f10_dhar_offset(pvt)
492a4b4bedcSBorislav Petkov 					: k8_dhar_offset(pvt);
493e2ce7255SDoug Thompson 
494956b9ba1SJoe Perches 	edac_dbg(1, "  DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
495e2ce7255SDoug Thompson 		 pvt->mc_node_id, (unsigned long)*hole_base,
496e2ce7255SDoug Thompson 		 (unsigned long)*hole_offset, (unsigned long)*hole_size);
497e2ce7255SDoug Thompson 
498e2ce7255SDoug Thompson 	return 0;
499e2ce7255SDoug Thompson }
500e2ce7255SDoug Thompson EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info);
501e2ce7255SDoug Thompson 
50293c2df58SDoug Thompson /*
50393c2df58SDoug Thompson  * Return the DramAddr that the SysAddr given by @sys_addr maps to.  It is
50493c2df58SDoug Thompson  * assumed that sys_addr maps to the node given by mci.
50593c2df58SDoug Thompson  *
50693c2df58SDoug Thompson  * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section
50793c2df58SDoug Thompson  * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a
50893c2df58SDoug Thompson  * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled,
50993c2df58SDoug Thompson  * then it is also involved in translating a SysAddr to a DramAddr. Sections
51093c2df58SDoug Thompson  * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting.
51193c2df58SDoug Thompson  * These parts of the documentation are unclear. I interpret them as follows:
51293c2df58SDoug Thompson  *
51393c2df58SDoug Thompson  * When node n receives a SysAddr, it processes the SysAddr as follows:
51493c2df58SDoug Thompson  *
51593c2df58SDoug Thompson  * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM
51693c2df58SDoug Thompson  *    Limit registers for node n. If the SysAddr is not within the range
51793c2df58SDoug Thompson  *    specified by the base and limit values, then node n ignores the Sysaddr
51893c2df58SDoug Thompson  *    (since it does not map to node n). Otherwise continue to step 2 below.
51993c2df58SDoug Thompson  *
52093c2df58SDoug Thompson  * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is
52193c2df58SDoug Thompson  *    disabled so skip to step 3 below. Otherwise see if the SysAddr is within
52293c2df58SDoug Thompson  *    the range of relocated addresses (starting at 0x100000000) from the DRAM
52393c2df58SDoug Thompson  *    hole. If not, skip to step 3 below. Else get the value of the
52493c2df58SDoug Thompson  *    DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the
52593c2df58SDoug Thompson  *    offset defined by this value from the SysAddr.
52693c2df58SDoug Thompson  *
52793c2df58SDoug Thompson  * 3. Obtain the base address for node n from the DRAMBase field of the DRAM
52893c2df58SDoug Thompson  *    Base register for node n. To obtain the DramAddr, subtract the base
52993c2df58SDoug Thompson  *    address from the SysAddr, as shown near the start of section 3.4.4 (p.70).
53093c2df58SDoug Thompson  */
53193c2df58SDoug Thompson static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
53293c2df58SDoug Thompson {
5337f19bf75SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
53493c2df58SDoug Thompson 	u64 dram_base, hole_base, hole_offset, hole_size, dram_addr;
5351f31677eSBorislav Petkov 	int ret;
53693c2df58SDoug Thompson 
5377f19bf75SBorislav Petkov 	dram_base = get_dram_base(pvt, pvt->mc_node_id);
53893c2df58SDoug Thompson 
53993c2df58SDoug Thompson 	ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
54093c2df58SDoug Thompson 				      &hole_size);
54193c2df58SDoug Thompson 	if (!ret) {
5421f31677eSBorislav Petkov 		if ((sys_addr >= (1ULL << 32)) &&
5431f31677eSBorislav Petkov 		    (sys_addr < ((1ULL << 32) + hole_size))) {
54493c2df58SDoug Thompson 			/* use DHAR to translate SysAddr to DramAddr */
54593c2df58SDoug Thompson 			dram_addr = sys_addr - hole_offset;
54693c2df58SDoug Thompson 
547956b9ba1SJoe Perches 			edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
54893c2df58SDoug Thompson 				 (unsigned long)sys_addr,
54993c2df58SDoug Thompson 				 (unsigned long)dram_addr);
55093c2df58SDoug Thompson 
55193c2df58SDoug Thompson 			return dram_addr;
55293c2df58SDoug Thompson 		}
55393c2df58SDoug Thompson 	}
55493c2df58SDoug Thompson 
55593c2df58SDoug Thompson 	/*
55693c2df58SDoug Thompson 	 * Translate the SysAddr to a DramAddr as shown near the start of
55793c2df58SDoug Thompson 	 * section 3.4.4 (p. 70).  Although sys_addr is a 64-bit value, the k8
55893c2df58SDoug Thompson 	 * only deals with 40-bit values.  Therefore we discard bits 63-40 of
55993c2df58SDoug Thompson 	 * sys_addr below.  If bit 39 of sys_addr is 1 then the bits we
56093c2df58SDoug Thompson 	 * discard are all 1s.  Otherwise the bits we discard are all 0s.  See
56193c2df58SDoug Thompson 	 * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture
56293c2df58SDoug Thompson 	 * Programmer's Manual Volume 1 Application Programming.
56393c2df58SDoug Thompson 	 */
564f678b8ccSBorislav Petkov 	dram_addr = (sys_addr & GENMASK(0, 39)) - dram_base;
56593c2df58SDoug Thompson 
566956b9ba1SJoe Perches 	edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
567956b9ba1SJoe Perches 		 (unsigned long)sys_addr, (unsigned long)dram_addr);
56893c2df58SDoug Thompson 	return dram_addr;
56993c2df58SDoug Thompson }
57093c2df58SDoug Thompson 
57193c2df58SDoug Thompson /*
57293c2df58SDoug Thompson  * @intlv_en is the value of the IntlvEn field from a DRAM Base register
57393c2df58SDoug Thompson  * (section 3.4.4.1).  Return the number of bits from a SysAddr that are used
57493c2df58SDoug Thompson  * for node interleaving.
57593c2df58SDoug Thompson  */
57693c2df58SDoug Thompson static int num_node_interleave_bits(unsigned intlv_en)
57793c2df58SDoug Thompson {
57893c2df58SDoug Thompson 	static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 };
57993c2df58SDoug Thompson 	int n;
58093c2df58SDoug Thompson 
58193c2df58SDoug Thompson 	BUG_ON(intlv_en > 7);
58293c2df58SDoug Thompson 	n = intlv_shift_table[intlv_en];
58393c2df58SDoug Thompson 	return n;
58493c2df58SDoug Thompson }
58593c2df58SDoug Thompson 
58693c2df58SDoug Thompson /* Translate the DramAddr given by @dram_addr to an InputAddr. */
58793c2df58SDoug Thompson static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
58893c2df58SDoug Thompson {
58993c2df58SDoug Thompson 	struct amd64_pvt *pvt;
59093c2df58SDoug Thompson 	int intlv_shift;
59193c2df58SDoug Thompson 	u64 input_addr;
59293c2df58SDoug Thompson 
59393c2df58SDoug Thompson 	pvt = mci->pvt_info;
59493c2df58SDoug Thompson 
59593c2df58SDoug Thompson 	/*
59693c2df58SDoug Thompson 	 * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
59793c2df58SDoug Thompson 	 * concerning translating a DramAddr to an InputAddr.
59893c2df58SDoug Thompson 	 */
5997f19bf75SBorislav Petkov 	intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
600f678b8ccSBorislav Petkov 	input_addr = ((dram_addr >> intlv_shift) & GENMASK(12, 35)) +
60193c2df58SDoug Thompson 		      (dram_addr & 0xfff);
60293c2df58SDoug Thompson 
603956b9ba1SJoe Perches 	edac_dbg(2, "  Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
60493c2df58SDoug Thompson 		 intlv_shift, (unsigned long)dram_addr,
60593c2df58SDoug Thompson 		 (unsigned long)input_addr);
60693c2df58SDoug Thompson 
60793c2df58SDoug Thompson 	return input_addr;
60893c2df58SDoug Thompson }
60993c2df58SDoug Thompson 
61093c2df58SDoug Thompson /*
61193c2df58SDoug Thompson  * Translate the SysAddr represented by @sys_addr to an InputAddr.  It is
61293c2df58SDoug Thompson  * assumed that @sys_addr maps to the node given by mci.
61393c2df58SDoug Thompson  */
61493c2df58SDoug Thompson static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
61593c2df58SDoug Thompson {
61693c2df58SDoug Thompson 	u64 input_addr;
61793c2df58SDoug Thompson 
61893c2df58SDoug Thompson 	input_addr =
61993c2df58SDoug Thompson 	    dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
62093c2df58SDoug Thompson 
621956b9ba1SJoe Perches 	edac_dbg(2, "SysAdddr 0x%lx translates to InputAddr 0x%lx\n",
62293c2df58SDoug Thompson 		 (unsigned long)sys_addr, (unsigned long)input_addr);
62393c2df58SDoug Thompson 
62493c2df58SDoug Thompson 	return input_addr;
62593c2df58SDoug Thompson }
62693c2df58SDoug Thompson 
62793c2df58SDoug Thompson /* Map the Error address to a PAGE and PAGE OFFSET. */
62893c2df58SDoug Thompson static inline void error_address_to_page_and_offset(u64 error_address,
62933ca0643SBorislav Petkov 						    struct err_info *err)
63093c2df58SDoug Thompson {
63133ca0643SBorislav Petkov 	err->page = (u32) (error_address >> PAGE_SHIFT);
63233ca0643SBorislav Petkov 	err->offset = ((u32) error_address) & ~PAGE_MASK;
63393c2df58SDoug Thompson }
63493c2df58SDoug Thompson 
63593c2df58SDoug Thompson /*
63693c2df58SDoug Thompson  * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address
63793c2df58SDoug Thompson  * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers
63893c2df58SDoug Thompson  * of a node that detected an ECC memory error.  mci represents the node that
63993c2df58SDoug Thompson  * the error address maps to (possibly different from the node that detected
64093c2df58SDoug Thompson  * the error).  Return the number of the csrow that sys_addr maps to, or -1 on
64193c2df58SDoug Thompson  * error.
64293c2df58SDoug Thompson  */
64393c2df58SDoug Thompson static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
64493c2df58SDoug Thompson {
64593c2df58SDoug Thompson 	int csrow;
64693c2df58SDoug Thompson 
64793c2df58SDoug Thompson 	csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr));
64893c2df58SDoug Thompson 
64993c2df58SDoug Thompson 	if (csrow == -1)
65024f9a7feSBorislav Petkov 		amd64_mc_err(mci, "Failed to translate InputAddr to csrow for "
65193c2df58SDoug Thompson 				  "address 0x%lx\n", (unsigned long)sys_addr);
65293c2df58SDoug Thompson 	return csrow;
65393c2df58SDoug Thompson }
654e2ce7255SDoug Thompson 
655bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
6562da11654SDoug Thompson 
6572da11654SDoug Thompson /*
6582da11654SDoug Thompson  * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
6592da11654SDoug Thompson  * are ECC capable.
6602da11654SDoug Thompson  */
6611f6189edSDan Carpenter static unsigned long amd64_determine_edac_cap(struct amd64_pvt *pvt)
6622da11654SDoug Thompson {
663cb328507SBorislav Petkov 	u8 bit;
6641f6189edSDan Carpenter 	unsigned long edac_cap = EDAC_FLAG_NONE;
6652da11654SDoug Thompson 
666a4b4bedcSBorislav Petkov 	bit = (pvt->fam > 0xf || pvt->ext_model >= K8_REV_F)
6672da11654SDoug Thompson 		? 19
6682da11654SDoug Thompson 		: 17;
6692da11654SDoug Thompson 
670584fcff4SBorislav Petkov 	if (pvt->dclr0 & BIT(bit))
6712da11654SDoug Thompson 		edac_cap = EDAC_FLAG_SECDED;
6722da11654SDoug Thompson 
6732da11654SDoug Thompson 	return edac_cap;
6742da11654SDoug Thompson }
6752da11654SDoug Thompson 
6768c671751SBorislav Petkov static void amd64_debug_display_dimm_sizes(struct amd64_pvt *, u8);
6772da11654SDoug Thompson 
678a4b4bedcSBorislav Petkov static void amd64_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan)
67968798e17SBorislav Petkov {
680956b9ba1SJoe Perches 	edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
68168798e17SBorislav Petkov 
682956b9ba1SJoe Perches 	edac_dbg(1, "  DIMM type: %sbuffered; all DIMMs support ECC: %s\n",
68368798e17SBorislav Petkov 		 (dclr & BIT(16)) ?  "un" : "",
68468798e17SBorislav Petkov 		 (dclr & BIT(19)) ? "yes" : "no");
68568798e17SBorislav Petkov 
686956b9ba1SJoe Perches 	edac_dbg(1, "  PAR/ERR parity: %s\n",
68768798e17SBorislav Petkov 		 (dclr & BIT(8)) ?  "enabled" : "disabled");
68868798e17SBorislav Petkov 
689a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10)
690956b9ba1SJoe Perches 		edac_dbg(1, "  DCT 128bit mode width: %s\n",
69168798e17SBorislav Petkov 			 (dclr & BIT(11)) ?  "128b" : "64b");
69268798e17SBorislav Petkov 
693956b9ba1SJoe Perches 	edac_dbg(1, "  x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
69468798e17SBorislav Petkov 		 (dclr & BIT(12)) ?  "yes" : "no",
69568798e17SBorislav Petkov 		 (dclr & BIT(13)) ?  "yes" : "no",
69668798e17SBorislav Petkov 		 (dclr & BIT(14)) ?  "yes" : "no",
69768798e17SBorislav Petkov 		 (dclr & BIT(15)) ?  "yes" : "no");
69868798e17SBorislav Petkov }
69968798e17SBorislav Petkov 
7002da11654SDoug Thompson /* Display and decode various NB registers for debug purposes. */
701b2b0c605SBorislav Petkov static void dump_misc_regs(struct amd64_pvt *pvt)
7022da11654SDoug Thompson {
703956b9ba1SJoe Perches 	edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
7042da11654SDoug Thompson 
705956b9ba1SJoe Perches 	edac_dbg(1, "  NB two channel DRAM capable: %s\n",
7065980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
70768798e17SBorislav Petkov 
708956b9ba1SJoe Perches 	edac_dbg(1, "  ECC capable: %s, ChipKill ECC capable: %s\n",
7095980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
7105980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
71168798e17SBorislav Petkov 
712a4b4bedcSBorislav Petkov 	amd64_dump_dramcfg_low(pvt, pvt->dclr0, 0);
7132da11654SDoug Thompson 
714956b9ba1SJoe Perches 	edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
7152da11654SDoug Thompson 
716956b9ba1SJoe Perches 	edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n",
717bc21fa57SBorislav Petkov 		 pvt->dhar, dhar_base(pvt),
718a4b4bedcSBorislav Petkov 		 (pvt->fam == 0xf) ? k8_dhar_offset(pvt)
719bc21fa57SBorislav Petkov 				   : f10_dhar_offset(pvt));
7202da11654SDoug Thompson 
721956b9ba1SJoe Perches 	edac_dbg(1, "  DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
7222da11654SDoug Thompson 
7238c671751SBorislav Petkov 	amd64_debug_display_dimm_sizes(pvt, 0);
7244d796364SBorislav Petkov 
7254d796364SBorislav Petkov 	/* everything below this point is Fam10h and above */
726a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
7272da11654SDoug Thompson 		return;
7284d796364SBorislav Petkov 
7298c671751SBorislav Petkov 	amd64_debug_display_dimm_sizes(pvt, 1);
7302da11654SDoug Thompson 
731a3b7db09SBorislav Petkov 	amd64_info("using %s syndromes.\n", ((pvt->ecc_sym_sz == 8) ? "x8" : "x4"));
732ad6a32e9SBorislav Petkov 
7338de1d91eSBorislav Petkov 	/* Only if NOT ganged does dclr1 have valid info */
73468798e17SBorislav Petkov 	if (!dct_ganging_enabled(pvt))
735a4b4bedcSBorislav Petkov 		amd64_dump_dramcfg_low(pvt, pvt->dclr1, 1);
7362da11654SDoug Thompson }
7372da11654SDoug Thompson 
73894be4bffSDoug Thompson /*
73918b94f66SAravind Gopalakrishnan  * See BKDG, F2x[1,0][5C:40], F2[1,0][6C:60]
74094be4bffSDoug Thompson  */
74111c75eadSBorislav Petkov static void prep_chip_selects(struct amd64_pvt *pvt)
74294be4bffSDoug Thompson {
74318b94f66SAravind Gopalakrishnan 	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
74411c75eadSBorislav Petkov 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
74511c75eadSBorislav Petkov 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8;
74618b94f66SAravind Gopalakrishnan 	} else if (pvt->fam == 0x15 && pvt->model >= 0x30) {
74718b94f66SAravind Gopalakrishnan 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4;
74818b94f66SAravind Gopalakrishnan 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2;
7499d858bb1SBorislav Petkov 	} else {
75011c75eadSBorislav Petkov 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
75111c75eadSBorislav Petkov 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4;
7529d858bb1SBorislav Petkov 	}
75394be4bffSDoug Thompson }
75494be4bffSDoug Thompson 
75594be4bffSDoug Thompson /*
75611c75eadSBorislav Petkov  * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers
75794be4bffSDoug Thompson  */
758b2b0c605SBorislav Petkov static void read_dct_base_mask(struct amd64_pvt *pvt)
75994be4bffSDoug Thompson {
76011c75eadSBorislav Petkov 	int cs;
76194be4bffSDoug Thompson 
76211c75eadSBorislav Petkov 	prep_chip_selects(pvt);
76394be4bffSDoug Thompson 
76411c75eadSBorislav Petkov 	for_each_chip_select(cs, 0, pvt) {
76571d2a32eSBorislav Petkov 		int reg0   = DCSB0 + (cs * 4);
76671d2a32eSBorislav Petkov 		int reg1   = DCSB1 + (cs * 4);
76711c75eadSBorislav Petkov 		u32 *base0 = &pvt->csels[0].csbases[cs];
76811c75eadSBorislav Petkov 		u32 *base1 = &pvt->csels[1].csbases[cs];
769b2b0c605SBorislav Petkov 
77011c75eadSBorislav Petkov 		if (!amd64_read_dct_pci_cfg(pvt, reg0, base0))
771956b9ba1SJoe Perches 			edac_dbg(0, "  DCSB0[%d]=0x%08x reg: F2x%x\n",
77211c75eadSBorislav Petkov 				 cs, *base0, reg0);
77394be4bffSDoug Thompson 
774a4b4bedcSBorislav Petkov 		if (pvt->fam == 0xf || dct_ganging_enabled(pvt))
77511c75eadSBorislav Petkov 			continue;
776b2b0c605SBorislav Petkov 
77711c75eadSBorislav Petkov 		if (!amd64_read_dct_pci_cfg(pvt, reg1, base1))
778956b9ba1SJoe Perches 			edac_dbg(0, "  DCSB1[%d]=0x%08x reg: F2x%x\n",
77911c75eadSBorislav Petkov 				 cs, *base1, reg1);
78094be4bffSDoug Thompson 	}
78194be4bffSDoug Thompson 
78211c75eadSBorislav Petkov 	for_each_chip_select_mask(cs, 0, pvt) {
78371d2a32eSBorislav Petkov 		int reg0   = DCSM0 + (cs * 4);
78471d2a32eSBorislav Petkov 		int reg1   = DCSM1 + (cs * 4);
78511c75eadSBorislav Petkov 		u32 *mask0 = &pvt->csels[0].csmasks[cs];
78611c75eadSBorislav Petkov 		u32 *mask1 = &pvt->csels[1].csmasks[cs];
787b2b0c605SBorislav Petkov 
78811c75eadSBorislav Petkov 		if (!amd64_read_dct_pci_cfg(pvt, reg0, mask0))
789956b9ba1SJoe Perches 			edac_dbg(0, "    DCSM0[%d]=0x%08x reg: F2x%x\n",
79011c75eadSBorislav Petkov 				 cs, *mask0, reg0);
79194be4bffSDoug Thompson 
792a4b4bedcSBorislav Petkov 		if (pvt->fam == 0xf || dct_ganging_enabled(pvt))
79311c75eadSBorislav Petkov 			continue;
794b2b0c605SBorislav Petkov 
79511c75eadSBorislav Petkov 		if (!amd64_read_dct_pci_cfg(pvt, reg1, mask1))
796956b9ba1SJoe Perches 			edac_dbg(0, "    DCSM1[%d]=0x%08x reg: F2x%x\n",
79711c75eadSBorislav Petkov 				 cs, *mask1, reg1);
79894be4bffSDoug Thompson 	}
7996ba5dcdcSBorislav Petkov }
80094be4bffSDoug Thompson 
80124f9a7feSBorislav Petkov static enum mem_type amd64_determine_memory_type(struct amd64_pvt *pvt, int cs)
80294be4bffSDoug Thompson {
80394be4bffSDoug Thompson 	enum mem_type type;
80494be4bffSDoug Thompson 
805cb328507SBorislav Petkov 	/* F15h supports only DDR3 */
806a4b4bedcSBorislav Petkov 	if (pvt->fam >= 0x15)
807cb328507SBorislav Petkov 		type = (pvt->dclr0 & BIT(16)) ?	MEM_DDR3 : MEM_RDDR3;
808a4b4bedcSBorislav Petkov 	else if (pvt->fam == 0x10 || pvt->ext_model >= K8_REV_F) {
8096b4c0bdeSBorislav Petkov 		if (pvt->dchr0 & DDR3_MODE)
8106b4c0bdeSBorislav Petkov 			type = (pvt->dclr0 & BIT(16)) ?	MEM_DDR3 : MEM_RDDR3;
8116b4c0bdeSBorislav Petkov 		else
81294be4bffSDoug Thompson 			type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
81394be4bffSDoug Thompson 	} else {
81494be4bffSDoug Thompson 		type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
81594be4bffSDoug Thompson 	}
81694be4bffSDoug Thompson 
81724f9a7feSBorislav Petkov 	amd64_info("CS%d: %s\n", cs, edac_mem_types[type]);
81894be4bffSDoug Thompson 
81994be4bffSDoug Thompson 	return type;
82094be4bffSDoug Thompson }
82194be4bffSDoug Thompson 
822cb328507SBorislav Petkov /* Get the number of DCT channels the memory controller is using. */
823ddff876dSDoug Thompson static int k8_early_channel_count(struct amd64_pvt *pvt)
824ddff876dSDoug Thompson {
825cb328507SBorislav Petkov 	int flag;
826ddff876dSDoug Thompson 
8279f56da0eSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F)
828ddff876dSDoug Thompson 		/* RevF (NPT) and later */
82941d8bfabSBorislav Petkov 		flag = pvt->dclr0 & WIDTH_128;
8309f56da0eSBorislav Petkov 	else
831ddff876dSDoug Thompson 		/* RevE and earlier */
832ddff876dSDoug Thompson 		flag = pvt->dclr0 & REVE_WIDTH_128;
833ddff876dSDoug Thompson 
834ddff876dSDoug Thompson 	/* not used */
835ddff876dSDoug Thompson 	pvt->dclr1 = 0;
836ddff876dSDoug Thompson 
837ddff876dSDoug Thompson 	return (flag) ? 2 : 1;
838ddff876dSDoug Thompson }
839ddff876dSDoug Thompson 
84070046624SBorislav Petkov /* On F10h and later ErrAddr is MC4_ADDR[47:1] */
841a4b4bedcSBorislav Petkov static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m)
842ddff876dSDoug Thompson {
843c1ae6830SBorislav Petkov 	u64 addr;
84470046624SBorislav Petkov 	u8 start_bit = 1;
84570046624SBorislav Petkov 	u8 end_bit   = 47;
84670046624SBorislav Petkov 
847a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf) {
84870046624SBorislav Petkov 		start_bit = 3;
84970046624SBorislav Petkov 		end_bit   = 39;
85070046624SBorislav Petkov 	}
85170046624SBorislav Petkov 
852c1ae6830SBorislav Petkov 	addr = m->addr & GENMASK(start_bit, end_bit);
853c1ae6830SBorislav Petkov 
854c1ae6830SBorislav Petkov 	/*
855c1ae6830SBorislav Petkov 	 * Erratum 637 workaround
856c1ae6830SBorislav Petkov 	 */
857a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x15) {
858c1ae6830SBorislav Petkov 		struct amd64_pvt *pvt;
859c1ae6830SBorislav Petkov 		u64 cc6_base, tmp_addr;
860c1ae6830SBorislav Petkov 		u32 tmp;
8618b84c8dfSDaniel J Blueman 		u16 mce_nid;
8628b84c8dfSDaniel J Blueman 		u8 intlv_en;
863c1ae6830SBorislav Petkov 
864c1ae6830SBorislav Petkov 		if ((addr & GENMASK(24, 47)) >> 24 != 0x00fdf7)
865c1ae6830SBorislav Petkov 			return addr;
866c1ae6830SBorislav Petkov 
867c1ae6830SBorislav Petkov 		mce_nid	= amd_get_nb_id(m->extcpu);
868c1ae6830SBorislav Petkov 		pvt	= mcis[mce_nid]->pvt_info;
869c1ae6830SBorislav Petkov 
870c1ae6830SBorislav Petkov 		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp);
871c1ae6830SBorislav Petkov 		intlv_en = tmp >> 21 & 0x7;
872c1ae6830SBorislav Petkov 
873c1ae6830SBorislav Petkov 		/* add [47:27] + 3 trailing bits */
874c1ae6830SBorislav Petkov 		cc6_base  = (tmp & GENMASK(0, 20)) << 3;
875c1ae6830SBorislav Petkov 
876c1ae6830SBorislav Petkov 		/* reverse and add DramIntlvEn */
877c1ae6830SBorislav Petkov 		cc6_base |= intlv_en ^ 0x7;
878c1ae6830SBorislav Petkov 
879c1ae6830SBorislav Petkov 		/* pin at [47:24] */
880c1ae6830SBorislav Petkov 		cc6_base <<= 24;
881c1ae6830SBorislav Petkov 
882c1ae6830SBorislav Petkov 		if (!intlv_en)
883c1ae6830SBorislav Petkov 			return cc6_base | (addr & GENMASK(0, 23));
884c1ae6830SBorislav Petkov 
885c1ae6830SBorislav Petkov 		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp);
886c1ae6830SBorislav Petkov 
887c1ae6830SBorislav Petkov 							/* faster log2 */
888c1ae6830SBorislav Petkov 		tmp_addr  = (addr & GENMASK(12, 23)) << __fls(intlv_en + 1);
889c1ae6830SBorislav Petkov 
890c1ae6830SBorislav Petkov 		/* OR DramIntlvSel into bits [14:12] */
891c1ae6830SBorislav Petkov 		tmp_addr |= (tmp & GENMASK(21, 23)) >> 9;
892c1ae6830SBorislav Petkov 
893c1ae6830SBorislav Petkov 		/* add remaining [11:0] bits from original MC4_ADDR */
894c1ae6830SBorislav Petkov 		tmp_addr |= addr & GENMASK(0, 11);
895c1ae6830SBorislav Petkov 
896c1ae6830SBorislav Petkov 		return cc6_base | tmp_addr;
897c1ae6830SBorislav Petkov 	}
898c1ae6830SBorislav Petkov 
899c1ae6830SBorislav Petkov 	return addr;
900ddff876dSDoug Thompson }
901ddff876dSDoug Thompson 
902e2c0bffeSDaniel J Blueman static struct pci_dev *pci_get_related_function(unsigned int vendor,
903e2c0bffeSDaniel J Blueman 						unsigned int device,
904e2c0bffeSDaniel J Blueman 						struct pci_dev *related)
905e2c0bffeSDaniel J Blueman {
906e2c0bffeSDaniel J Blueman 	struct pci_dev *dev = NULL;
907e2c0bffeSDaniel J Blueman 
908e2c0bffeSDaniel J Blueman 	while ((dev = pci_get_device(vendor, device, dev))) {
909e2c0bffeSDaniel J Blueman 		if (pci_domain_nr(dev->bus) == pci_domain_nr(related->bus) &&
910e2c0bffeSDaniel J Blueman 		    (dev->bus->number == related->bus->number) &&
911e2c0bffeSDaniel J Blueman 		    (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn)))
912e2c0bffeSDaniel J Blueman 			break;
913e2c0bffeSDaniel J Blueman 	}
914e2c0bffeSDaniel J Blueman 
915e2c0bffeSDaniel J Blueman 	return dev;
916e2c0bffeSDaniel J Blueman }
917e2c0bffeSDaniel J Blueman 
9187f19bf75SBorislav Petkov static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
919ddff876dSDoug Thompson {
920e2c0bffeSDaniel J Blueman 	struct amd_northbridge *nb;
92118b94f66SAravind Gopalakrishnan 	struct pci_dev *f1 = NULL;
92218b94f66SAravind Gopalakrishnan 	unsigned int pci_func;
92371d2a32eSBorislav Petkov 	int off = range << 3;
924e2c0bffeSDaniel J Blueman 	u32 llim;
925ddff876dSDoug Thompson 
9267f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off,  &pvt->ranges[range].base.lo);
9277f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo);
928ddff876dSDoug Thompson 
92918b94f66SAravind Gopalakrishnan 	if (pvt->fam == 0xf)
9307f19bf75SBorislav Petkov 		return;
931ddff876dSDoug Thompson 
9327f19bf75SBorislav Petkov 	if (!dram_rw(pvt, range))
9337f19bf75SBorislav Petkov 		return;
934ddff876dSDoug Thompson 
9357f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off,  &pvt->ranges[range].base.hi);
9367f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi);
937f08e457cSBorislav Petkov 
938e2c0bffeSDaniel J Blueman 	/* F15h: factor in CC6 save area by reading dst node's limit reg */
93918b94f66SAravind Gopalakrishnan 	if (pvt->fam != 0x15)
940e2c0bffeSDaniel J Blueman 		return;
941f08e457cSBorislav Petkov 
942e2c0bffeSDaniel J Blueman 	nb = node_to_amd_nb(dram_dst_node(pvt, range));
943e2c0bffeSDaniel J Blueman 	if (WARN_ON(!nb))
944e2c0bffeSDaniel J Blueman 		return;
945e2c0bffeSDaniel J Blueman 
94618b94f66SAravind Gopalakrishnan 	pci_func = (pvt->model == 0x30) ? PCI_DEVICE_ID_AMD_15H_M30H_NB_F1
94718b94f66SAravind Gopalakrishnan 					: PCI_DEVICE_ID_AMD_15H_NB_F1;
94818b94f66SAravind Gopalakrishnan 
94918b94f66SAravind Gopalakrishnan 	f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc);
950f08e457cSBorislav Petkov 	if (WARN_ON(!f1))
951f08e457cSBorislav Petkov 		return;
952f08e457cSBorislav Petkov 
953f08e457cSBorislav Petkov 	amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim);
954f08e457cSBorislav Petkov 
955f08e457cSBorislav Petkov 	pvt->ranges[range].lim.lo &= GENMASK(0, 15);
956f08e457cSBorislav Petkov 
957f08e457cSBorislav Petkov 				    /* {[39:27],111b} */
958f08e457cSBorislav Petkov 	pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16;
959f08e457cSBorislav Petkov 
960f08e457cSBorislav Petkov 	pvt->ranges[range].lim.hi &= GENMASK(0, 7);
961f08e457cSBorislav Petkov 
962f08e457cSBorislav Petkov 				    /* [47:40] */
963f08e457cSBorislav Petkov 	pvt->ranges[range].lim.hi |= llim >> 13;
964f08e457cSBorislav Petkov 
965f08e457cSBorislav Petkov 	pci_dev_put(f1);
966f08e457cSBorislav Petkov }
967ddff876dSDoug Thompson 
968f192c7b1SBorislav Petkov static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
96933ca0643SBorislav Petkov 				    struct err_info *err)
970ddff876dSDoug Thompson {
971f192c7b1SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
972ddff876dSDoug Thompson 
97333ca0643SBorislav Petkov 	error_address_to_page_and_offset(sys_addr, err);
974ab5a503cSMauro Carvalho Chehab 
975ab5a503cSMauro Carvalho Chehab 	/*
976ab5a503cSMauro Carvalho Chehab 	 * Find out which node the error address belongs to. This may be
977ab5a503cSMauro Carvalho Chehab 	 * different from the node that detected the error.
978ab5a503cSMauro Carvalho Chehab 	 */
97933ca0643SBorislav Petkov 	err->src_mci = find_mc_by_sys_addr(mci, sys_addr);
98033ca0643SBorislav Petkov 	if (!err->src_mci) {
981ab5a503cSMauro Carvalho Chehab 		amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
982ab5a503cSMauro Carvalho Chehab 			     (unsigned long)sys_addr);
98333ca0643SBorislav Petkov 		err->err_code = ERR_NODE;
984ab5a503cSMauro Carvalho Chehab 		return;
985ab5a503cSMauro Carvalho Chehab 	}
986ab5a503cSMauro Carvalho Chehab 
987ab5a503cSMauro Carvalho Chehab 	/* Now map the sys_addr to a CSROW */
98833ca0643SBorislav Petkov 	err->csrow = sys_addr_to_csrow(err->src_mci, sys_addr);
98933ca0643SBorislav Petkov 	if (err->csrow < 0) {
99033ca0643SBorislav Petkov 		err->err_code = ERR_CSROW;
991ab5a503cSMauro Carvalho Chehab 		return;
992ab5a503cSMauro Carvalho Chehab 	}
993ab5a503cSMauro Carvalho Chehab 
994ddff876dSDoug Thompson 	/* CHIPKILL enabled */
995f192c7b1SBorislav Petkov 	if (pvt->nbcfg & NBCFG_CHIPKILL) {
99633ca0643SBorislav Petkov 		err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
99733ca0643SBorislav Petkov 		if (err->channel < 0) {
998ddff876dSDoug Thompson 			/*
999ddff876dSDoug Thompson 			 * Syndrome didn't map, so we don't know which of the
1000ddff876dSDoug Thompson 			 * 2 DIMMs is in error. So we need to ID 'both' of them
1001ddff876dSDoug Thompson 			 * as suspect.
1002ddff876dSDoug Thompson 			 */
100333ca0643SBorislav Petkov 			amd64_mc_warn(err->src_mci, "unknown syndrome 0x%04x - "
1004ab5a503cSMauro Carvalho Chehab 				      "possible error reporting race\n",
100533ca0643SBorislav Petkov 				      err->syndrome);
100633ca0643SBorislav Petkov 			err->err_code = ERR_CHANNEL;
1007ddff876dSDoug Thompson 			return;
1008ddff876dSDoug Thompson 		}
1009ddff876dSDoug Thompson 	} else {
1010ddff876dSDoug Thompson 		/*
1011ddff876dSDoug Thompson 		 * non-chipkill ecc mode
1012ddff876dSDoug Thompson 		 *
1013ddff876dSDoug Thompson 		 * The k8 documentation is unclear about how to determine the
1014ddff876dSDoug Thompson 		 * channel number when using non-chipkill memory.  This method
1015ddff876dSDoug Thompson 		 * was obtained from email communication with someone at AMD.
1016ddff876dSDoug Thompson 		 * (Wish the email was placed in this comment - norsk)
1017ddff876dSDoug Thompson 		 */
101833ca0643SBorislav Petkov 		err->channel = ((sys_addr & BIT(3)) != 0);
1019ddff876dSDoug Thompson 	}
1020ddff876dSDoug Thompson }
1021ddff876dSDoug Thompson 
102241d8bfabSBorislav Petkov static int ddr2_cs_size(unsigned i, bool dct_width)
1023ddff876dSDoug Thompson {
102441d8bfabSBorislav Petkov 	unsigned shift = 0;
1025ddff876dSDoug Thompson 
102641d8bfabSBorislav Petkov 	if (i <= 2)
102741d8bfabSBorislav Petkov 		shift = i;
102841d8bfabSBorislav Petkov 	else if (!(i & 0x1))
102941d8bfabSBorislav Petkov 		shift = i >> 1;
10301433eb99SBorislav Petkov 	else
103141d8bfabSBorislav Petkov 		shift = (i + 1) >> 1;
1032ddff876dSDoug Thompson 
103341d8bfabSBorislav Petkov 	return 128 << (shift + !!dct_width);
103441d8bfabSBorislav Petkov }
103541d8bfabSBorislav Petkov 
103641d8bfabSBorislav Petkov static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
103741d8bfabSBorislav Petkov 				  unsigned cs_mode)
103841d8bfabSBorislav Petkov {
103941d8bfabSBorislav Petkov 	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
104041d8bfabSBorislav Petkov 
104141d8bfabSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F) {
104241d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 11);
104341d8bfabSBorislav Petkov 		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
104441d8bfabSBorislav Petkov 	}
104541d8bfabSBorislav Petkov 	else if (pvt->ext_model >= K8_REV_D) {
104611b0a314SBorislav Petkov 		unsigned diff;
104741d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 10);
104841d8bfabSBorislav Petkov 
104911b0a314SBorislav Petkov 		/*
105011b0a314SBorislav Petkov 		 * the below calculation, besides trying to win an obfuscated C
105111b0a314SBorislav Petkov 		 * contest, maps cs_mode values to DIMM chip select sizes. The
105211b0a314SBorislav Petkov 		 * mappings are:
105311b0a314SBorislav Petkov 		 *
105411b0a314SBorislav Petkov 		 * cs_mode	CS size (mb)
105511b0a314SBorislav Petkov 		 * =======	============
105611b0a314SBorislav Petkov 		 * 0		32
105711b0a314SBorislav Petkov 		 * 1		64
105811b0a314SBorislav Petkov 		 * 2		128
105911b0a314SBorislav Petkov 		 * 3		128
106011b0a314SBorislav Petkov 		 * 4		256
106111b0a314SBorislav Petkov 		 * 5		512
106211b0a314SBorislav Petkov 		 * 6		256
106311b0a314SBorislav Petkov 		 * 7		512
106411b0a314SBorislav Petkov 		 * 8		1024
106511b0a314SBorislav Petkov 		 * 9		1024
106611b0a314SBorislav Petkov 		 * 10		2048
106711b0a314SBorislav Petkov 		 *
106811b0a314SBorislav Petkov 		 * Basically, it calculates a value with which to shift the
106911b0a314SBorislav Petkov 		 * smallest CS size of 32MB.
107011b0a314SBorislav Petkov 		 *
107111b0a314SBorislav Petkov 		 * ddr[23]_cs_size have a similar purpose.
107211b0a314SBorislav Petkov 		 */
107311b0a314SBorislav Petkov 		diff = cs_mode/3 + (unsigned)(cs_mode > 5);
107411b0a314SBorislav Petkov 
107511b0a314SBorislav Petkov 		return 32 << (cs_mode - diff);
107641d8bfabSBorislav Petkov 	}
107741d8bfabSBorislav Petkov 	else {
107841d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 6);
107941d8bfabSBorislav Petkov 		return 32 << cs_mode;
108041d8bfabSBorislav Petkov 	}
1081ddff876dSDoug Thompson }
1082ddff876dSDoug Thompson 
10831afd3c98SDoug Thompson /*
10841afd3c98SDoug Thompson  * Get the number of DCT channels in use.
10851afd3c98SDoug Thompson  *
10861afd3c98SDoug Thompson  * Return:
10871afd3c98SDoug Thompson  *	number of Memory Channels in operation
10881afd3c98SDoug Thompson  * Pass back:
10891afd3c98SDoug Thompson  *	contents of the DCL0_LOW register
10901afd3c98SDoug Thompson  */
10917d20d14dSBorislav Petkov static int f1x_early_channel_count(struct amd64_pvt *pvt)
10921afd3c98SDoug Thompson {
10936ba5dcdcSBorislav Petkov 	int i, j, channels = 0;
1094ddff876dSDoug Thompson 
10957d20d14dSBorislav Petkov 	/* On F10h, if we are in 128 bit mode, then we are using 2 channels */
1096a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10 && (pvt->dclr0 & WIDTH_128))
10977d20d14dSBorislav Petkov 		return 2;
10981afd3c98SDoug Thompson 
10991afd3c98SDoug Thompson 	/*
1100d16149e8SBorislav Petkov 	 * Need to check if in unganged mode: In such, there are 2 channels,
1101d16149e8SBorislav Petkov 	 * but they are not in 128 bit mode and thus the above 'dclr0' status
1102d16149e8SBorislav Petkov 	 * bit will be OFF.
11031afd3c98SDoug Thompson 	 *
11041afd3c98SDoug Thompson 	 * Need to check DCT0[0] and DCT1[0] to see if only one of them has
11051afd3c98SDoug Thompson 	 * their CSEnable bit on. If so, then SINGLE DIMM case.
11061afd3c98SDoug Thompson 	 */
1107956b9ba1SJoe Perches 	edac_dbg(0, "Data width is not 128 bits - need more decoding\n");
11081afd3c98SDoug Thompson 
11091afd3c98SDoug Thompson 	/*
11101afd3c98SDoug Thompson 	 * Check DRAM Bank Address Mapping values for each DIMM to see if there
11111afd3c98SDoug Thompson 	 * is more than just one DIMM present in unganged mode. Need to check
11121afd3c98SDoug Thompson 	 * both controllers since DIMMs can be placed in either one.
11131afd3c98SDoug Thompson 	 */
1114525a1b20SBorislav Petkov 	for (i = 0; i < 2; i++) {
1115525a1b20SBorislav Petkov 		u32 dbam = (i ? pvt->dbam1 : pvt->dbam0);
11161afd3c98SDoug Thompson 
111757a30854SWan Wei 		for (j = 0; j < 4; j++) {
111857a30854SWan Wei 			if (DBAM_DIMM(j, dbam) > 0) {
11191afd3c98SDoug Thompson 				channels++;
112057a30854SWan Wei 				break;
11211afd3c98SDoug Thompson 			}
112257a30854SWan Wei 		}
112357a30854SWan Wei 	}
11241afd3c98SDoug Thompson 
1125d16149e8SBorislav Petkov 	if (channels > 2)
1126d16149e8SBorislav Petkov 		channels = 2;
1127d16149e8SBorislav Petkov 
112824f9a7feSBorislav Petkov 	amd64_info("MCT channel count: %d\n", channels);
11291afd3c98SDoug Thompson 
11301afd3c98SDoug Thompson 	return channels;
11311afd3c98SDoug Thompson }
11321afd3c98SDoug Thompson 
113341d8bfabSBorislav Petkov static int ddr3_cs_size(unsigned i, bool dct_width)
11341afd3c98SDoug Thompson {
113541d8bfabSBorislav Petkov 	unsigned shift = 0;
113641d8bfabSBorislav Petkov 	int cs_size = 0;
113741d8bfabSBorislav Petkov 
113841d8bfabSBorislav Petkov 	if (i == 0 || i == 3 || i == 4)
113941d8bfabSBorislav Petkov 		cs_size = -1;
114041d8bfabSBorislav Petkov 	else if (i <= 2)
114141d8bfabSBorislav Petkov 		shift = i;
114241d8bfabSBorislav Petkov 	else if (i == 12)
114341d8bfabSBorislav Petkov 		shift = 7;
114441d8bfabSBorislav Petkov 	else if (!(i & 0x1))
114541d8bfabSBorislav Petkov 		shift = i >> 1;
114641d8bfabSBorislav Petkov 	else
114741d8bfabSBorislav Petkov 		shift = (i + 1) >> 1;
114841d8bfabSBorislav Petkov 
114941d8bfabSBorislav Petkov 	if (cs_size != -1)
115041d8bfabSBorislav Petkov 		cs_size = (128 * (1 << !!dct_width)) << shift;
115141d8bfabSBorislav Petkov 
115241d8bfabSBorislav Petkov 	return cs_size;
115341d8bfabSBorislav Petkov }
115441d8bfabSBorislav Petkov 
115541d8bfabSBorislav Petkov static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
115641d8bfabSBorislav Petkov 				   unsigned cs_mode)
115741d8bfabSBorislav Petkov {
115841d8bfabSBorislav Petkov 	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
115941d8bfabSBorislav Petkov 
116041d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 11);
11611433eb99SBorislav Petkov 
11621433eb99SBorislav Petkov 	if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
116341d8bfabSBorislav Petkov 		return ddr3_cs_size(cs_mode, dclr & WIDTH_128);
11641433eb99SBorislav Petkov 	else
116541d8bfabSBorislav Petkov 		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
116641d8bfabSBorislav Petkov }
11671433eb99SBorislav Petkov 
116841d8bfabSBorislav Petkov /*
116941d8bfabSBorislav Petkov  * F15h supports only 64bit DCT interfaces
117041d8bfabSBorislav Petkov  */
117141d8bfabSBorislav Petkov static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
117241d8bfabSBorislav Petkov 				   unsigned cs_mode)
117341d8bfabSBorislav Petkov {
117441d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 12);
117541d8bfabSBorislav Petkov 
117641d8bfabSBorislav Petkov 	return ddr3_cs_size(cs_mode, false);
11771afd3c98SDoug Thompson }
11781afd3c98SDoug Thompson 
117994c1acf2SAravind Gopalakrishnan /*
118018b94f66SAravind Gopalakrishnan  * F16h and F15h model 30h have only limited cs_modes.
118194c1acf2SAravind Gopalakrishnan  */
118294c1acf2SAravind Gopalakrishnan static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
118394c1acf2SAravind Gopalakrishnan 				unsigned cs_mode)
118494c1acf2SAravind Gopalakrishnan {
118594c1acf2SAravind Gopalakrishnan 	WARN_ON(cs_mode > 12);
118694c1acf2SAravind Gopalakrishnan 
118794c1acf2SAravind Gopalakrishnan 	if (cs_mode == 6 || cs_mode == 8 ||
118894c1acf2SAravind Gopalakrishnan 	    cs_mode == 9 || cs_mode == 12)
118994c1acf2SAravind Gopalakrishnan 		return -1;
119094c1acf2SAravind Gopalakrishnan 	else
119194c1acf2SAravind Gopalakrishnan 		return ddr3_cs_size(cs_mode, false);
119294c1acf2SAravind Gopalakrishnan }
119394c1acf2SAravind Gopalakrishnan 
11945a5d2371SBorislav Petkov static void read_dram_ctl_register(struct amd64_pvt *pvt)
11956163b5d4SDoug Thompson {
11966163b5d4SDoug Thompson 
1197a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf)
11985a5d2371SBorislav Petkov 		return;
11995a5d2371SBorislav Petkov 
120078da121eSBorislav Petkov 	if (!amd64_read_dct_pci_cfg(pvt, DCT_SEL_LO, &pvt->dct_sel_lo)) {
1201956b9ba1SJoe Perches 		edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
120278da121eSBorislav Petkov 			 pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
12036163b5d4SDoug Thompson 
1204956b9ba1SJoe Perches 		edac_dbg(0, "  DCTs operate in %s mode\n",
12055a5d2371SBorislav Petkov 			 (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
12066163b5d4SDoug Thompson 
120772381bd5SBorislav Petkov 		if (!dct_ganging_enabled(pvt))
1208956b9ba1SJoe Perches 			edac_dbg(0, "  Address range split per DCT: %s\n",
120972381bd5SBorislav Petkov 				 (dct_high_range_enabled(pvt) ? "yes" : "no"));
121072381bd5SBorislav Petkov 
1211956b9ba1SJoe Perches 		edac_dbg(0, "  data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n",
121272381bd5SBorislav Petkov 			 (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
121372381bd5SBorislav Petkov 			 (dct_memory_cleared(pvt) ? "yes" : "no"));
121472381bd5SBorislav Petkov 
1215956b9ba1SJoe Perches 		edac_dbg(0, "  channel interleave: %s, "
121678da121eSBorislav Petkov 			 "interleave bits selector: 0x%x\n",
121772381bd5SBorislav Petkov 			 (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
12186163b5d4SDoug Thompson 			 dct_sel_interleave_addr(pvt));
12196163b5d4SDoug Thompson 	}
12206163b5d4SDoug Thompson 
122178da121eSBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, DCT_SEL_HI, &pvt->dct_sel_hi);
12226163b5d4SDoug Thompson }
12236163b5d4SDoug Thompson 
1224f71d0a05SDoug Thompson /*
122518b94f66SAravind Gopalakrishnan  * Determine channel (DCT) based on the interleaving mode (see F15h M30h BKDG,
122618b94f66SAravind Gopalakrishnan  * 2.10.12 Memory Interleaving Modes).
122718b94f66SAravind Gopalakrishnan  */
122818b94f66SAravind Gopalakrishnan static u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
122918b94f66SAravind Gopalakrishnan 				     u8 intlv_en, int num_dcts_intlv,
123018b94f66SAravind Gopalakrishnan 				     u32 dct_sel)
123118b94f66SAravind Gopalakrishnan {
123218b94f66SAravind Gopalakrishnan 	u8 channel = 0;
123318b94f66SAravind Gopalakrishnan 	u8 select;
123418b94f66SAravind Gopalakrishnan 
123518b94f66SAravind Gopalakrishnan 	if (!(intlv_en))
123618b94f66SAravind Gopalakrishnan 		return (u8)(dct_sel);
123718b94f66SAravind Gopalakrishnan 
123818b94f66SAravind Gopalakrishnan 	if (num_dcts_intlv == 2) {
123918b94f66SAravind Gopalakrishnan 		select = (sys_addr >> 8) & 0x3;
124018b94f66SAravind Gopalakrishnan 		channel = select ? 0x3 : 0;
124118b94f66SAravind Gopalakrishnan 	} else if (num_dcts_intlv == 4)
124218b94f66SAravind Gopalakrishnan 		channel = (sys_addr >> 8) & 0x7;
124318b94f66SAravind Gopalakrishnan 
124418b94f66SAravind Gopalakrishnan 	return channel;
124518b94f66SAravind Gopalakrishnan }
124618b94f66SAravind Gopalakrishnan 
124718b94f66SAravind Gopalakrishnan /*
1248229a7a11SBorislav Petkov  * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory
1249f71d0a05SDoug Thompson  * Interleaving Modes.
1250f71d0a05SDoug Thompson  */
1251b15f0fcaSBorislav Petkov static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
1252229a7a11SBorislav Petkov 				bool hi_range_sel, u8 intlv_en)
12536163b5d4SDoug Thompson {
1254151fa71cSBorislav Petkov 	u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1;
12556163b5d4SDoug Thompson 
12566163b5d4SDoug Thompson 	if (dct_ganging_enabled(pvt))
1257229a7a11SBorislav Petkov 		return 0;
1258229a7a11SBorislav Petkov 
1259229a7a11SBorislav Petkov 	if (hi_range_sel)
1260229a7a11SBorislav Petkov 		return dct_sel_high;
1261229a7a11SBorislav Petkov 
1262f71d0a05SDoug Thompson 	/*
1263f71d0a05SDoug Thompson 	 * see F2x110[DctSelIntLvAddr] - channel interleave mode
1264f71d0a05SDoug Thompson 	 */
1265229a7a11SBorislav Petkov 	if (dct_interleave_enabled(pvt)) {
1266229a7a11SBorislav Petkov 		u8 intlv_addr = dct_sel_interleave_addr(pvt);
12676163b5d4SDoug Thompson 
1268229a7a11SBorislav Petkov 		/* return DCT select function: 0=DCT0, 1=DCT1 */
1269229a7a11SBorislav Petkov 		if (!intlv_addr)
1270229a7a11SBorislav Petkov 			return sys_addr >> 6 & 1;
12716163b5d4SDoug Thompson 
1272229a7a11SBorislav Petkov 		if (intlv_addr & 0x2) {
1273229a7a11SBorislav Petkov 			u8 shift = intlv_addr & 0x1 ? 9 : 6;
1274229a7a11SBorislav Petkov 			u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) % 2;
1275229a7a11SBorislav Petkov 
1276229a7a11SBorislav Petkov 			return ((sys_addr >> shift) & 1) ^ temp;
12776163b5d4SDoug Thompson 		}
12786163b5d4SDoug Thompson 
1279229a7a11SBorislav Petkov 		return (sys_addr >> (12 + hweight8(intlv_en))) & 1;
1280229a7a11SBorislav Petkov 	}
1281229a7a11SBorislav Petkov 
1282229a7a11SBorislav Petkov 	if (dct_high_range_enabled(pvt))
1283229a7a11SBorislav Petkov 		return ~dct_sel_high & 1;
12846163b5d4SDoug Thompson 
12856163b5d4SDoug Thompson 	return 0;
12866163b5d4SDoug Thompson }
12876163b5d4SDoug Thompson 
1288c8e518d5SBorislav Petkov /* Convert the sys_addr to the normalized DCT address */
1289c7e5301aSDaniel J Blueman static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range,
1290c8e518d5SBorislav Petkov 				 u64 sys_addr, bool hi_rng,
1291c8e518d5SBorislav Petkov 				 u32 dct_sel_base_addr)
12926163b5d4SDoug Thompson {
12936163b5d4SDoug Thompson 	u64 chan_off;
1294c8e518d5SBorislav Petkov 	u64 dram_base		= get_dram_base(pvt, range);
1295c8e518d5SBorislav Petkov 	u64 hole_off		= f10_dhar_offset(pvt);
1296c8e518d5SBorislav Petkov 	u64 dct_sel_base_off	= (pvt->dct_sel_hi & 0xFFFFFC00) << 16;
12976163b5d4SDoug Thompson 
1298c8e518d5SBorislav Petkov 	if (hi_rng) {
1299c8e518d5SBorislav Petkov 		/*
1300c8e518d5SBorislav Petkov 		 * if
1301c8e518d5SBorislav Petkov 		 * base address of high range is below 4Gb
1302c8e518d5SBorislav Petkov 		 * (bits [47:27] at [31:11])
1303c8e518d5SBorislav Petkov 		 * DRAM address space on this DCT is hoisted above 4Gb	&&
1304c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1305c8e518d5SBorislav Petkov 		 *
1306c8e518d5SBorislav Petkov 		 *	remove hole offset from sys_addr
1307c8e518d5SBorislav Petkov 		 * else
1308c8e518d5SBorislav Petkov 		 *	remove high range offset from sys_addr
1309c8e518d5SBorislav Petkov 		 */
1310c8e518d5SBorislav Petkov 		if ((!(dct_sel_base_addr >> 16) ||
1311c8e518d5SBorislav Petkov 		     dct_sel_base_addr < dhar_base(pvt)) &&
1312972ea17aSBorislav Petkov 		    dhar_valid(pvt) &&
1313c8e518d5SBorislav Petkov 		    (sys_addr >= BIT_64(32)))
1314bc21fa57SBorislav Petkov 			chan_off = hole_off;
13156163b5d4SDoug Thompson 		else
13166163b5d4SDoug Thompson 			chan_off = dct_sel_base_off;
13176163b5d4SDoug Thompson 	} else {
1318c8e518d5SBorislav Petkov 		/*
1319c8e518d5SBorislav Petkov 		 * if
1320c8e518d5SBorislav Petkov 		 * we have a valid hole		&&
1321c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1322c8e518d5SBorislav Petkov 		 *
1323c8e518d5SBorislav Petkov 		 *	remove hole
1324c8e518d5SBorislav Petkov 		 * else
1325c8e518d5SBorislav Petkov 		 *	remove dram base to normalize to DCT address
1326c8e518d5SBorislav Petkov 		 */
1327972ea17aSBorislav Petkov 		if (dhar_valid(pvt) && (sys_addr >= BIT_64(32)))
1328bc21fa57SBorislav Petkov 			chan_off = hole_off;
13296163b5d4SDoug Thompson 		else
1330c8e518d5SBorislav Petkov 			chan_off = dram_base;
13316163b5d4SDoug Thompson 	}
13326163b5d4SDoug Thompson 
1333c8e518d5SBorislav Petkov 	return (sys_addr & GENMASK(6,47)) - (chan_off & GENMASK(23,47));
13346163b5d4SDoug Thompson }
13356163b5d4SDoug Thompson 
13366163b5d4SDoug Thompson /*
13376163b5d4SDoug Thompson  * checks if the csrow passed in is marked as SPARED, if so returns the new
13386163b5d4SDoug Thompson  * spare row
13396163b5d4SDoug Thompson  */
134011c75eadSBorislav Petkov static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow)
13416163b5d4SDoug Thompson {
1342614ec9d8SBorislav Petkov 	int tmp_cs;
13436163b5d4SDoug Thompson 
1344614ec9d8SBorislav Petkov 	if (online_spare_swap_done(pvt, dct) &&
1345614ec9d8SBorislav Petkov 	    csrow == online_spare_bad_dramcs(pvt, dct)) {
1346614ec9d8SBorislav Petkov 
1347614ec9d8SBorislav Petkov 		for_each_chip_select(tmp_cs, dct, pvt) {
1348614ec9d8SBorislav Petkov 			if (chip_select_base(tmp_cs, dct, pvt) & 0x2) {
1349614ec9d8SBorislav Petkov 				csrow = tmp_cs;
1350614ec9d8SBorislav Petkov 				break;
1351614ec9d8SBorislav Petkov 			}
1352614ec9d8SBorislav Petkov 		}
13536163b5d4SDoug Thompson 	}
13546163b5d4SDoug Thompson 	return csrow;
13556163b5d4SDoug Thompson }
13566163b5d4SDoug Thompson 
13576163b5d4SDoug Thompson /*
13586163b5d4SDoug Thompson  * Iterate over the DRAM DCT "base" and "mask" registers looking for a
13596163b5d4SDoug Thompson  * SystemAddr match on the specified 'ChannelSelect' and 'NodeID'
13606163b5d4SDoug Thompson  *
13616163b5d4SDoug Thompson  * Return:
13626163b5d4SDoug Thompson  *	-EINVAL:  NOT FOUND
13636163b5d4SDoug Thompson  *	0..csrow = Chip-Select Row
13646163b5d4SDoug Thompson  */
1365c7e5301aSDaniel J Blueman static int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct)
13666163b5d4SDoug Thompson {
13676163b5d4SDoug Thompson 	struct mem_ctl_info *mci;
13686163b5d4SDoug Thompson 	struct amd64_pvt *pvt;
136911c75eadSBorislav Petkov 	u64 cs_base, cs_mask;
13706163b5d4SDoug Thompson 	int cs_found = -EINVAL;
13716163b5d4SDoug Thompson 	int csrow;
13726163b5d4SDoug Thompson 
1373cc4d8860SBorislav Petkov 	mci = mcis[nid];
13746163b5d4SDoug Thompson 	if (!mci)
13756163b5d4SDoug Thompson 		return cs_found;
13766163b5d4SDoug Thompson 
13776163b5d4SDoug Thompson 	pvt = mci->pvt_info;
13786163b5d4SDoug Thompson 
1379956b9ba1SJoe Perches 	edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct);
13806163b5d4SDoug Thompson 
138111c75eadSBorislav Petkov 	for_each_chip_select(csrow, dct, pvt) {
138211c75eadSBorislav Petkov 		if (!csrow_enabled(csrow, dct, pvt))
13836163b5d4SDoug Thompson 			continue;
13846163b5d4SDoug Thompson 
138511c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask);
13866163b5d4SDoug Thompson 
1387956b9ba1SJoe Perches 		edac_dbg(1, "    CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
13886163b5d4SDoug Thompson 			 csrow, cs_base, cs_mask);
13896163b5d4SDoug Thompson 
139011c75eadSBorislav Petkov 		cs_mask = ~cs_mask;
13916163b5d4SDoug Thompson 
1392956b9ba1SJoe Perches 		edac_dbg(1, "    (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n",
139311c75eadSBorislav Petkov 			 (in_addr & cs_mask), (cs_base & cs_mask));
13946163b5d4SDoug Thompson 
139511c75eadSBorislav Petkov 		if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
139618b94f66SAravind Gopalakrishnan 			if (pvt->fam == 0x15 && pvt->model >= 0x30) {
139718b94f66SAravind Gopalakrishnan 				cs_found =  csrow;
139818b94f66SAravind Gopalakrishnan 				break;
139918b94f66SAravind Gopalakrishnan 			}
140011c75eadSBorislav Petkov 			cs_found = f10_process_possible_spare(pvt, dct, csrow);
14016163b5d4SDoug Thompson 
1402956b9ba1SJoe Perches 			edac_dbg(1, " MATCH csrow=%d\n", cs_found);
14036163b5d4SDoug Thompson 			break;
14046163b5d4SDoug Thompson 		}
14056163b5d4SDoug Thompson 	}
14066163b5d4SDoug Thompson 	return cs_found;
14076163b5d4SDoug Thompson }
14086163b5d4SDoug Thompson 
140995b0ef55SBorislav Petkov /*
141095b0ef55SBorislav Petkov  * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is
141195b0ef55SBorislav Petkov  * swapped with a region located at the bottom of memory so that the GPU can use
141295b0ef55SBorislav Petkov  * the interleaved region and thus two channels.
141395b0ef55SBorislav Petkov  */
1414b15f0fcaSBorislav Petkov static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr)
141595b0ef55SBorislav Petkov {
141695b0ef55SBorislav Petkov 	u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr;
141795b0ef55SBorislav Petkov 
1418a4b4bedcSBorislav Petkov 	if (pvt->fam == 0x10) {
141995b0ef55SBorislav Petkov 		/* only revC3 and revE have that feature */
1420a4b4bedcSBorislav Petkov 		if (pvt->model < 4 || (pvt->model < 0xa && pvt->stepping < 3))
142195b0ef55SBorislav Petkov 			return sys_addr;
142295b0ef55SBorislav Petkov 	}
142395b0ef55SBorislav Petkov 
142495b0ef55SBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, SWAP_INTLV_REG, &swap_reg);
142595b0ef55SBorislav Petkov 
142695b0ef55SBorislav Petkov 	if (!(swap_reg & 0x1))
142795b0ef55SBorislav Petkov 		return sys_addr;
142895b0ef55SBorislav Petkov 
142995b0ef55SBorislav Petkov 	swap_base	= (swap_reg >> 3) & 0x7f;
143095b0ef55SBorislav Petkov 	swap_limit	= (swap_reg >> 11) & 0x7f;
143195b0ef55SBorislav Petkov 	rgn_size	= (swap_reg >> 20) & 0x7f;
143295b0ef55SBorislav Petkov 	tmp_addr	= sys_addr >> 27;
143395b0ef55SBorislav Petkov 
143495b0ef55SBorislav Petkov 	if (!(sys_addr >> 34) &&
143595b0ef55SBorislav Petkov 	    (((tmp_addr >= swap_base) &&
143695b0ef55SBorislav Petkov 	     (tmp_addr <= swap_limit)) ||
143795b0ef55SBorislav Petkov 	     (tmp_addr < rgn_size)))
143895b0ef55SBorislav Petkov 		return sys_addr ^ (u64)swap_base << 27;
143995b0ef55SBorislav Petkov 
144095b0ef55SBorislav Petkov 	return sys_addr;
144195b0ef55SBorislav Petkov }
144295b0ef55SBorislav Petkov 
1443f71d0a05SDoug Thompson /* For a given @dram_range, check if @sys_addr falls within it. */
1444e761359aSBorislav Petkov static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
144533ca0643SBorislav Petkov 				  u64 sys_addr, int *chan_sel)
1446f71d0a05SDoug Thompson {
1447229a7a11SBorislav Petkov 	int cs_found = -EINVAL;
1448c8e518d5SBorislav Petkov 	u64 chan_addr;
14495d4b58e8SBorislav Petkov 	u32 dct_sel_base;
145011c75eadSBorislav Petkov 	u8 channel;
1451229a7a11SBorislav Petkov 	bool high_range = false;
1452f71d0a05SDoug Thompson 
14537f19bf75SBorislav Petkov 	u8 node_id    = dram_dst_node(pvt, range);
1454229a7a11SBorislav Petkov 	u8 intlv_en   = dram_intlv_en(pvt, range);
14557f19bf75SBorislav Petkov 	u32 intlv_sel = dram_intlv_sel(pvt, range);
1456f71d0a05SDoug Thompson 
1457956b9ba1SJoe Perches 	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
1458c8e518d5SBorislav Petkov 		 range, sys_addr, get_dram_limit(pvt, range));
1459f71d0a05SDoug Thompson 
1460355fba60SBorislav Petkov 	if (dhar_valid(pvt) &&
1461355fba60SBorislav Petkov 	    dhar_base(pvt) <= sys_addr &&
1462355fba60SBorislav Petkov 	    sys_addr < BIT_64(32)) {
1463355fba60SBorislav Petkov 		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
1464355fba60SBorislav Petkov 			    sys_addr);
1465f71d0a05SDoug Thompson 		return -EINVAL;
1466355fba60SBorislav Petkov 	}
1467355fba60SBorislav Petkov 
1468f030ddfbSBorislav Petkov 	if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en)))
1469355fba60SBorislav Petkov 		return -EINVAL;
1470f71d0a05SDoug Thompson 
1471b15f0fcaSBorislav Petkov 	sys_addr = f1x_swap_interleaved_region(pvt, sys_addr);
147295b0ef55SBorislav Petkov 
1473f71d0a05SDoug Thompson 	dct_sel_base = dct_sel_baseaddr(pvt);
1474f71d0a05SDoug Thompson 
1475f71d0a05SDoug Thompson 	/*
1476f71d0a05SDoug Thompson 	 * check whether addresses >= DctSelBaseAddr[47:27] are to be used to
1477f71d0a05SDoug Thompson 	 * select between DCT0 and DCT1.
1478f71d0a05SDoug Thompson 	 */
1479f71d0a05SDoug Thompson 	if (dct_high_range_enabled(pvt) &&
1480f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt) &&
1481f71d0a05SDoug Thompson 	   ((sys_addr >> 27) >= (dct_sel_base >> 11)))
1482229a7a11SBorislav Petkov 		high_range = true;
1483f71d0a05SDoug Thompson 
1484b15f0fcaSBorislav Petkov 	channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en);
1485f71d0a05SDoug Thompson 
1486b15f0fcaSBorislav Petkov 	chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr,
1487c8e518d5SBorislav Petkov 					  high_range, dct_sel_base);
1488f71d0a05SDoug Thompson 
1489e2f79dbdSBorislav Petkov 	/* Remove node interleaving, see F1x120 */
1490e2f79dbdSBorislav Petkov 	if (intlv_en)
1491e2f79dbdSBorislav Petkov 		chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) |
1492e2f79dbdSBorislav Petkov 			    (chan_addr & 0xfff);
1493f71d0a05SDoug Thompson 
14945d4b58e8SBorislav Petkov 	/* remove channel interleave */
1495f71d0a05SDoug Thompson 	if (dct_interleave_enabled(pvt) &&
1496f71d0a05SDoug Thompson 	   !dct_high_range_enabled(pvt) &&
1497f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt)) {
14985d4b58e8SBorislav Petkov 
14995d4b58e8SBorislav Petkov 		if (dct_sel_interleave_addr(pvt) != 1) {
15005d4b58e8SBorislav Petkov 			if (dct_sel_interleave_addr(pvt) == 0x3)
15015d4b58e8SBorislav Petkov 				/* hash 9 */
15025d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 10) << 9) |
15035d4b58e8SBorislav Petkov 					     (chan_addr & 0x1ff);
15045d4b58e8SBorislav Petkov 			else
15055d4b58e8SBorislav Petkov 				/* A[6] or hash 6 */
15065d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 7) << 6) |
15075d4b58e8SBorislav Petkov 					     (chan_addr & 0x3f);
15085d4b58e8SBorislav Petkov 		} else
15095d4b58e8SBorislav Petkov 			/* A[12] */
15105d4b58e8SBorislav Petkov 			chan_addr = ((chan_addr >> 13) << 12) |
15115d4b58e8SBorislav Petkov 				     (chan_addr & 0xfff);
1512f71d0a05SDoug Thompson 	}
1513f71d0a05SDoug Thompson 
1514956b9ba1SJoe Perches 	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
1515f71d0a05SDoug Thompson 
1516b15f0fcaSBorislav Petkov 	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel);
1517f71d0a05SDoug Thompson 
151833ca0643SBorislav Petkov 	if (cs_found >= 0)
1519f71d0a05SDoug Thompson 		*chan_sel = channel;
152033ca0643SBorislav Petkov 
1521f71d0a05SDoug Thompson 	return cs_found;
1522f71d0a05SDoug Thompson }
1523f71d0a05SDoug Thompson 
152418b94f66SAravind Gopalakrishnan static int f15_m30h_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
152518b94f66SAravind Gopalakrishnan 					u64 sys_addr, int *chan_sel)
152618b94f66SAravind Gopalakrishnan {
152718b94f66SAravind Gopalakrishnan 	int cs_found = -EINVAL;
152818b94f66SAravind Gopalakrishnan 	int num_dcts_intlv = 0;
152918b94f66SAravind Gopalakrishnan 	u64 chan_addr, chan_offset;
153018b94f66SAravind Gopalakrishnan 	u64 dct_base, dct_limit;
153118b94f66SAravind Gopalakrishnan 	u32 dct_cont_base_reg, dct_cont_limit_reg, tmp;
153218b94f66SAravind Gopalakrishnan 	u8 channel, alias_channel, leg_mmio_hole, dct_sel, dct_offset_en;
153318b94f66SAravind Gopalakrishnan 
153418b94f66SAravind Gopalakrishnan 	u64 dhar_offset		= f10_dhar_offset(pvt);
153518b94f66SAravind Gopalakrishnan 	u8 intlv_addr		= dct_sel_interleave_addr(pvt);
153618b94f66SAravind Gopalakrishnan 	u8 node_id		= dram_dst_node(pvt, range);
153718b94f66SAravind Gopalakrishnan 	u8 intlv_en		= dram_intlv_en(pvt, range);
153818b94f66SAravind Gopalakrishnan 
153918b94f66SAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F1, DRAM_CONT_BASE, &dct_cont_base_reg);
154018b94f66SAravind Gopalakrishnan 	amd64_read_pci_cfg(pvt->F1, DRAM_CONT_LIMIT, &dct_cont_limit_reg);
154118b94f66SAravind Gopalakrishnan 
154218b94f66SAravind Gopalakrishnan 	dct_offset_en		= (u8) ((dct_cont_base_reg >> 3) & BIT(0));
154318b94f66SAravind Gopalakrishnan 	dct_sel			= (u8) ((dct_cont_base_reg >> 4) & 0x7);
154418b94f66SAravind Gopalakrishnan 
154518b94f66SAravind Gopalakrishnan 	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
154618b94f66SAravind Gopalakrishnan 		 range, sys_addr, get_dram_limit(pvt, range));
154718b94f66SAravind Gopalakrishnan 
154818b94f66SAravind Gopalakrishnan 	if (!(get_dram_base(pvt, range)  <= sys_addr) &&
154918b94f66SAravind Gopalakrishnan 	    !(get_dram_limit(pvt, range) >= sys_addr))
155018b94f66SAravind Gopalakrishnan 		return -EINVAL;
155118b94f66SAravind Gopalakrishnan 
155218b94f66SAravind Gopalakrishnan 	if (dhar_valid(pvt) &&
155318b94f66SAravind Gopalakrishnan 	    dhar_base(pvt) <= sys_addr &&
155418b94f66SAravind Gopalakrishnan 	    sys_addr < BIT_64(32)) {
155518b94f66SAravind Gopalakrishnan 		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
155618b94f66SAravind Gopalakrishnan 			    sys_addr);
155718b94f66SAravind Gopalakrishnan 		return -EINVAL;
155818b94f66SAravind Gopalakrishnan 	}
155918b94f66SAravind Gopalakrishnan 
156018b94f66SAravind Gopalakrishnan 	/* Verify sys_addr is within DCT Range. */
156118b94f66SAravind Gopalakrishnan 	dct_base = (dct_sel_baseaddr(pvt) << 27);
156218b94f66SAravind Gopalakrishnan 	dct_limit = (((dct_cont_limit_reg >> 11) & 0x1FFF) << 27) | 0x7FFFFFF;
156318b94f66SAravind Gopalakrishnan 
156418b94f66SAravind Gopalakrishnan 	if (!(dct_cont_base_reg & BIT(0)) &&
156518b94f66SAravind Gopalakrishnan 	    !(dct_base <= sys_addr && dct_limit >= sys_addr))
156618b94f66SAravind Gopalakrishnan 		return -EINVAL;
156718b94f66SAravind Gopalakrishnan 
156818b94f66SAravind Gopalakrishnan 	/* Verify number of dct's that participate in channel interleaving. */
156918b94f66SAravind Gopalakrishnan 	num_dcts_intlv = (int) hweight8(intlv_en);
157018b94f66SAravind Gopalakrishnan 
157118b94f66SAravind Gopalakrishnan 	if (!(num_dcts_intlv % 2 == 0) || (num_dcts_intlv > 4))
157218b94f66SAravind Gopalakrishnan 		return -EINVAL;
157318b94f66SAravind Gopalakrishnan 
157418b94f66SAravind Gopalakrishnan 	channel = f15_m30h_determine_channel(pvt, sys_addr, intlv_en,
157518b94f66SAravind Gopalakrishnan 					     num_dcts_intlv, dct_sel);
157618b94f66SAravind Gopalakrishnan 
157718b94f66SAravind Gopalakrishnan 	/* Verify we stay within the MAX number of channels allowed */
157818b94f66SAravind Gopalakrishnan 	if (channel > 4 || channel < 0)
157918b94f66SAravind Gopalakrishnan 		return -EINVAL;
158018b94f66SAravind Gopalakrishnan 
158118b94f66SAravind Gopalakrishnan 	leg_mmio_hole = (u8) (dct_cont_base_reg >> 1 & BIT(0));
158218b94f66SAravind Gopalakrishnan 
158318b94f66SAravind Gopalakrishnan 	/* Get normalized DCT addr */
158418b94f66SAravind Gopalakrishnan 	if (leg_mmio_hole && (sys_addr >= BIT_64(32)))
158518b94f66SAravind Gopalakrishnan 		chan_offset = dhar_offset;
158618b94f66SAravind Gopalakrishnan 	else
158718b94f66SAravind Gopalakrishnan 		chan_offset = dct_base;
158818b94f66SAravind Gopalakrishnan 
158918b94f66SAravind Gopalakrishnan 	chan_addr = sys_addr - chan_offset;
159018b94f66SAravind Gopalakrishnan 
159118b94f66SAravind Gopalakrishnan 	/* remove channel interleave */
159218b94f66SAravind Gopalakrishnan 	if (num_dcts_intlv == 2) {
159318b94f66SAravind Gopalakrishnan 		if (intlv_addr == 0x4)
159418b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 9) << 8) |
159518b94f66SAravind Gopalakrishnan 						(chan_addr & 0xff);
159618b94f66SAravind Gopalakrishnan 		else if (intlv_addr == 0x5)
159718b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 10) << 9) |
159818b94f66SAravind Gopalakrishnan 						(chan_addr & 0x1ff);
159918b94f66SAravind Gopalakrishnan 		else
160018b94f66SAravind Gopalakrishnan 			return -EINVAL;
160118b94f66SAravind Gopalakrishnan 
160218b94f66SAravind Gopalakrishnan 	} else if (num_dcts_intlv == 4) {
160318b94f66SAravind Gopalakrishnan 		if (intlv_addr == 0x4)
160418b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 10) << 8) |
160518b94f66SAravind Gopalakrishnan 							(chan_addr & 0xff);
160618b94f66SAravind Gopalakrishnan 		else if (intlv_addr == 0x5)
160718b94f66SAravind Gopalakrishnan 			chan_addr = ((chan_addr >> 11) << 9) |
160818b94f66SAravind Gopalakrishnan 							(chan_addr & 0x1ff);
160918b94f66SAravind Gopalakrishnan 		else
161018b94f66SAravind Gopalakrishnan 			return -EINVAL;
161118b94f66SAravind Gopalakrishnan 	}
161218b94f66SAravind Gopalakrishnan 
161318b94f66SAravind Gopalakrishnan 	if (dct_offset_en) {
161418b94f66SAravind Gopalakrishnan 		amd64_read_pci_cfg(pvt->F1,
161518b94f66SAravind Gopalakrishnan 				   DRAM_CONT_HIGH_OFF + (int) channel * 4,
161618b94f66SAravind Gopalakrishnan 				   &tmp);
161718b94f66SAravind Gopalakrishnan 		chan_addr +=  ((tmp >> 11) & 0xfff) << 27;
161818b94f66SAravind Gopalakrishnan 	}
161918b94f66SAravind Gopalakrishnan 
162018b94f66SAravind Gopalakrishnan 	f15h_select_dct(pvt, channel);
162118b94f66SAravind Gopalakrishnan 
162218b94f66SAravind Gopalakrishnan 	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
162318b94f66SAravind Gopalakrishnan 
162418b94f66SAravind Gopalakrishnan 	/*
162518b94f66SAravind Gopalakrishnan 	 * Find Chip select:
162618b94f66SAravind Gopalakrishnan 	 * if channel = 3, then alias it to 1. This is because, in F15 M30h,
162718b94f66SAravind Gopalakrishnan 	 * there is support for 4 DCT's, but only 2 are currently functional.
162818b94f66SAravind Gopalakrishnan 	 * They are DCT0 and DCT3. But we have read all registers of DCT3 into
162918b94f66SAravind Gopalakrishnan 	 * pvt->csels[1]. So we need to use '1' here to get correct info.
163018b94f66SAravind Gopalakrishnan 	 * Refer F15 M30h BKDG Section 2.10 and 2.10.3 for clarifications.
163118b94f66SAravind Gopalakrishnan 	 */
163218b94f66SAravind Gopalakrishnan 	alias_channel =  (channel == 3) ? 1 : channel;
163318b94f66SAravind Gopalakrishnan 
163418b94f66SAravind Gopalakrishnan 	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, alias_channel);
163518b94f66SAravind Gopalakrishnan 
163618b94f66SAravind Gopalakrishnan 	if (cs_found >= 0)
163718b94f66SAravind Gopalakrishnan 		*chan_sel = alias_channel;
163818b94f66SAravind Gopalakrishnan 
163918b94f66SAravind Gopalakrishnan 	return cs_found;
164018b94f66SAravind Gopalakrishnan }
164118b94f66SAravind Gopalakrishnan 
164218b94f66SAravind Gopalakrishnan static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt,
164318b94f66SAravind Gopalakrishnan 					u64 sys_addr,
164433ca0643SBorislav Petkov 					int *chan_sel)
1645f71d0a05SDoug Thompson {
1646e761359aSBorislav Petkov 	int cs_found = -EINVAL;
1647e761359aSBorislav Petkov 	unsigned range;
1648f71d0a05SDoug Thompson 
16497f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
16507f19bf75SBorislav Petkov 		if (!dram_rw(pvt, range))
1651f71d0a05SDoug Thompson 			continue;
1652f71d0a05SDoug Thompson 
165318b94f66SAravind Gopalakrishnan 		if (pvt->fam == 0x15 && pvt->model >= 0x30)
165418b94f66SAravind Gopalakrishnan 			cs_found = f15_m30h_match_to_this_node(pvt, range,
165518b94f66SAravind Gopalakrishnan 							       sys_addr,
165618b94f66SAravind Gopalakrishnan 							       chan_sel);
1657f71d0a05SDoug Thompson 
165818b94f66SAravind Gopalakrishnan 		else if ((get_dram_base(pvt, range)  <= sys_addr) &&
165918b94f66SAravind Gopalakrishnan 			 (get_dram_limit(pvt, range) >= sys_addr)) {
1660b15f0fcaSBorislav Petkov 			cs_found = f1x_match_to_this_node(pvt, range,
166133ca0643SBorislav Petkov 							  sys_addr, chan_sel);
1662f71d0a05SDoug Thompson 			if (cs_found >= 0)
1663f71d0a05SDoug Thompson 				break;
1664f71d0a05SDoug Thompson 		}
1665f71d0a05SDoug Thompson 	}
1666f71d0a05SDoug Thompson 	return cs_found;
1667f71d0a05SDoug Thompson }
1668f71d0a05SDoug Thompson 
1669f71d0a05SDoug Thompson /*
1670bdc30a0cSBorislav Petkov  * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
1671bdc30a0cSBorislav Petkov  * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
1672f71d0a05SDoug Thompson  *
1673bdc30a0cSBorislav Petkov  * The @sys_addr is usually an error address received from the hardware
1674bdc30a0cSBorislav Petkov  * (MCX_ADDR).
1675f71d0a05SDoug Thompson  */
1676b15f0fcaSBorislav Petkov static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
167733ca0643SBorislav Petkov 				     struct err_info *err)
1678f71d0a05SDoug Thompson {
1679f71d0a05SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
1680f71d0a05SDoug Thompson 
168133ca0643SBorislav Petkov 	error_address_to_page_and_offset(sys_addr, err);
1682ab5a503cSMauro Carvalho Chehab 
168333ca0643SBorislav Petkov 	err->csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &err->channel);
168433ca0643SBorislav Petkov 	if (err->csrow < 0) {
168533ca0643SBorislav Petkov 		err->err_code = ERR_CSROW;
1686bdc30a0cSBorislav Petkov 		return;
1687bdc30a0cSBorislav Petkov 	}
1688bdc30a0cSBorislav Petkov 
1689f71d0a05SDoug Thompson 	/*
1690bdc30a0cSBorislav Petkov 	 * We need the syndromes for channel detection only when we're
1691bdc30a0cSBorislav Petkov 	 * ganged. Otherwise @chan should already contain the channel at
1692bdc30a0cSBorislav Petkov 	 * this point.
1693f71d0a05SDoug Thompson 	 */
1694a97fa68eSBorislav Petkov 	if (dct_ganging_enabled(pvt))
169533ca0643SBorislav Petkov 		err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
1696f71d0a05SDoug Thompson }
1697f71d0a05SDoug Thompson 
1698f71d0a05SDoug Thompson /*
16998566c4dfSBorislav Petkov  * debug routine to display the memory sizes of all logical DIMMs and its
1700cb328507SBorislav Petkov  * CSROWs
1701f71d0a05SDoug Thompson  */
17028c671751SBorislav Petkov static void amd64_debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
1703f71d0a05SDoug Thompson {
1704bb89f5a0SBorislav Petkov 	int dimm, size0, size1;
1705525a1b20SBorislav Petkov 	u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases;
1706525a1b20SBorislav Petkov 	u32 dbam  = ctrl ? pvt->dbam1 : pvt->dbam0;
1707f71d0a05SDoug Thompson 
1708a4b4bedcSBorislav Petkov 	if (pvt->fam == 0xf) {
17098566c4dfSBorislav Petkov 		/* K8 families < revF not supported yet */
17101433eb99SBorislav Petkov 	       if (pvt->ext_model < K8_REV_F)
17118566c4dfSBorislav Petkov 			return;
17128566c4dfSBorislav Petkov 	       else
17138566c4dfSBorislav Petkov 		       WARN_ON(ctrl != 0);
17148566c4dfSBorislav Petkov 	}
17158566c4dfSBorislav Petkov 
17164d796364SBorislav Petkov 	dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1 : pvt->dbam0;
171711c75eadSBorislav Petkov 	dcsb = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->csels[1].csbases
171811c75eadSBorislav Petkov 						   : pvt->csels[0].csbases;
1719f71d0a05SDoug Thompson 
1720956b9ba1SJoe Perches 	edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
1721956b9ba1SJoe Perches 		 ctrl, dbam);
1722f71d0a05SDoug Thompson 
17238566c4dfSBorislav Petkov 	edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
17248566c4dfSBorislav Petkov 
1725f71d0a05SDoug Thompson 	/* Dump memory sizes for DIMM and its CSROWs */
1726f71d0a05SDoug Thompson 	for (dimm = 0; dimm < 4; dimm++) {
1727f71d0a05SDoug Thompson 
1728f71d0a05SDoug Thompson 		size0 = 0;
172911c75eadSBorislav Petkov 		if (dcsb[dimm*2] & DCSB_CS_ENABLE)
173041d8bfabSBorislav Petkov 			size0 = pvt->ops->dbam_to_cs(pvt, ctrl,
173141d8bfabSBorislav Petkov 						     DBAM_DIMM(dimm, dbam));
1732f71d0a05SDoug Thompson 
1733f71d0a05SDoug Thompson 		size1 = 0;
173411c75eadSBorislav Petkov 		if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE)
173541d8bfabSBorislav Petkov 			size1 = pvt->ops->dbam_to_cs(pvt, ctrl,
173641d8bfabSBorislav Petkov 						     DBAM_DIMM(dimm, dbam));
1737f71d0a05SDoug Thompson 
173824f9a7feSBorislav Petkov 		amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
1739bb89f5a0SBorislav Petkov 				dimm * 2,     size0,
1740bb89f5a0SBorislav Petkov 				dimm * 2 + 1, size1);
1741f71d0a05SDoug Thompson 	}
1742f71d0a05SDoug Thompson }
1743f71d0a05SDoug Thompson 
17444d37607aSDoug Thompson static struct amd64_family_type amd64_family_types[] = {
17454d37607aSDoug Thompson 	[K8_CPUS] = {
17460092b20dSBorislav Petkov 		.ctl_name = "K8",
17478d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
17488d5b5d9cSBorislav Petkov 		.f3_id = PCI_DEVICE_ID_AMD_K8_NB_MISC,
17494d37607aSDoug Thompson 		.ops = {
17504d37607aSDoug Thompson 			.early_channel_count	= k8_early_channel_count,
17514d37607aSDoug Thompson 			.map_sysaddr_to_csrow	= k8_map_sysaddr_to_csrow,
17521433eb99SBorislav Petkov 			.dbam_to_cs		= k8_dbam_to_chip_select,
1753b2b0c605SBorislav Petkov 			.read_dct_pci_cfg	= k8_read_dct_pci_cfg,
17544d37607aSDoug Thompson 		}
17554d37607aSDoug Thompson 	},
17564d37607aSDoug Thompson 	[F10_CPUS] = {
17570092b20dSBorislav Petkov 		.ctl_name = "F10h",
17588d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP,
17598d5b5d9cSBorislav Petkov 		.f3_id = PCI_DEVICE_ID_AMD_10H_NB_MISC,
17604d37607aSDoug Thompson 		.ops = {
17617d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
1762b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
17631433eb99SBorislav Petkov 			.dbam_to_cs		= f10_dbam_to_chip_select,
1764b2b0c605SBorislav Petkov 			.read_dct_pci_cfg	= f10_read_dct_pci_cfg,
1765b2b0c605SBorislav Petkov 		}
1766b2b0c605SBorislav Petkov 	},
1767b2b0c605SBorislav Petkov 	[F15_CPUS] = {
1768b2b0c605SBorislav Petkov 		.ctl_name = "F15h",
1769df71a053SBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1,
1770df71a053SBorislav Petkov 		.f3_id = PCI_DEVICE_ID_AMD_15H_NB_F3,
1771b2b0c605SBorislav Petkov 		.ops = {
17727d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
1773b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
177441d8bfabSBorislav Petkov 			.dbam_to_cs		= f15_dbam_to_chip_select,
1775b2b0c605SBorislav Petkov 			.read_dct_pci_cfg	= f15_read_dct_pci_cfg,
17764d37607aSDoug Thompson 		}
17774d37607aSDoug Thompson 	},
177818b94f66SAravind Gopalakrishnan 	[F15_M30H_CPUS] = {
177918b94f66SAravind Gopalakrishnan 		.ctl_name = "F15h_M30h",
178018b94f66SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1,
178118b94f66SAravind Gopalakrishnan 		.f3_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F3,
178218b94f66SAravind Gopalakrishnan 		.ops = {
178318b94f66SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
178418b94f66SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
178518b94f66SAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
178618b94f66SAravind Gopalakrishnan 			.read_dct_pci_cfg	= f15_read_dct_pci_cfg,
178718b94f66SAravind Gopalakrishnan 		}
178818b94f66SAravind Gopalakrishnan 	},
178994c1acf2SAravind Gopalakrishnan 	[F16_CPUS] = {
179094c1acf2SAravind Gopalakrishnan 		.ctl_name = "F16h",
179194c1acf2SAravind Gopalakrishnan 		.f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1,
179294c1acf2SAravind Gopalakrishnan 		.f3_id = PCI_DEVICE_ID_AMD_16H_NB_F3,
179394c1acf2SAravind Gopalakrishnan 		.ops = {
179494c1acf2SAravind Gopalakrishnan 			.early_channel_count	= f1x_early_channel_count,
179594c1acf2SAravind Gopalakrishnan 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
179694c1acf2SAravind Gopalakrishnan 			.dbam_to_cs		= f16_dbam_to_chip_select,
179794c1acf2SAravind Gopalakrishnan 			.read_dct_pci_cfg	= f10_read_dct_pci_cfg,
179894c1acf2SAravind Gopalakrishnan 		}
179994c1acf2SAravind Gopalakrishnan 	},
18004d37607aSDoug Thompson };
18014d37607aSDoug Thompson 
1802b1289d6fSDoug Thompson /*
1803bfc04aecSBorislav Petkov  * These are tables of eigenvectors (one per line) which can be used for the
1804bfc04aecSBorislav Petkov  * construction of the syndrome tables. The modified syndrome search algorithm
1805bfc04aecSBorislav Petkov  * uses those to find the symbol in error and thus the DIMM.
1806b1289d6fSDoug Thompson  *
1807bfc04aecSBorislav Petkov  * Algorithm courtesy of Ross LaFetra from AMD.
1808b1289d6fSDoug Thompson  */
1809c7e5301aSDaniel J Blueman static const u16 x4_vectors[] = {
1810bfc04aecSBorislav Petkov 	0x2f57, 0x1afe, 0x66cc, 0xdd88,
1811bfc04aecSBorislav Petkov 	0x11eb, 0x3396, 0x7f4c, 0xeac8,
1812bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008,
1813bfc04aecSBorislav Petkov 	0x1013, 0x3032, 0x4044, 0x8088,
1814bfc04aecSBorislav Petkov 	0x106b, 0x30d6, 0x70fc, 0xe0a8,
1815bfc04aecSBorislav Petkov 	0x4857, 0xc4fe, 0x13cc, 0x3288,
1816bfc04aecSBorislav Petkov 	0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
1817bfc04aecSBorislav Petkov 	0x1f39, 0x251e, 0xbd6c, 0x6bd8,
1818bfc04aecSBorislav Petkov 	0x15c1, 0x2a42, 0x89ac, 0x4758,
1819bfc04aecSBorislav Petkov 	0x2b03, 0x1602, 0x4f0c, 0xca08,
1820bfc04aecSBorislav Petkov 	0x1f07, 0x3a0e, 0x6b04, 0xbd08,
1821bfc04aecSBorislav Petkov 	0x8ba7, 0x465e, 0x244c, 0x1cc8,
1822bfc04aecSBorislav Petkov 	0x2b87, 0x164e, 0x642c, 0xdc18,
1823bfc04aecSBorislav Petkov 	0x40b9, 0x80de, 0x1094, 0x20e8,
1824bfc04aecSBorislav Petkov 	0x27db, 0x1eb6, 0x9dac, 0x7b58,
1825bfc04aecSBorislav Petkov 	0x11c1, 0x2242, 0x84ac, 0x4c58,
1826bfc04aecSBorislav Petkov 	0x1be5, 0x2d7a, 0x5e34, 0xa718,
1827bfc04aecSBorislav Petkov 	0x4b39, 0x8d1e, 0x14b4, 0x28d8,
1828bfc04aecSBorislav Petkov 	0x4c97, 0xc87e, 0x11fc, 0x33a8,
1829bfc04aecSBorislav Petkov 	0x8e97, 0x497e, 0x2ffc, 0x1aa8,
1830bfc04aecSBorislav Petkov 	0x16b3, 0x3d62, 0x4f34, 0x8518,
1831bfc04aecSBorislav Petkov 	0x1e2f, 0x391a, 0x5cac, 0xf858,
1832bfc04aecSBorislav Petkov 	0x1d9f, 0x3b7a, 0x572c, 0xfe18,
1833bfc04aecSBorislav Petkov 	0x15f5, 0x2a5a, 0x5264, 0xa3b8,
1834bfc04aecSBorislav Petkov 	0x1dbb, 0x3b66, 0x715c, 0xe3f8,
1835bfc04aecSBorislav Petkov 	0x4397, 0xc27e, 0x17fc, 0x3ea8,
1836bfc04aecSBorislav Petkov 	0x1617, 0x3d3e, 0x6464, 0xb8b8,
1837bfc04aecSBorislav Petkov 	0x23ff, 0x12aa, 0xab6c, 0x56d8,
1838bfc04aecSBorislav Petkov 	0x2dfb, 0x1ba6, 0x913c, 0x7328,
1839bfc04aecSBorislav Petkov 	0x185d, 0x2ca6, 0x7914, 0x9e28,
1840bfc04aecSBorislav Petkov 	0x171b, 0x3e36, 0x7d7c, 0xebe8,
1841bfc04aecSBorislav Petkov 	0x4199, 0x82ee, 0x19f4, 0x2e58,
1842bfc04aecSBorislav Petkov 	0x4807, 0xc40e, 0x130c, 0x3208,
1843bfc04aecSBorislav Petkov 	0x1905, 0x2e0a, 0x5804, 0xac08,
1844bfc04aecSBorislav Petkov 	0x213f, 0x132a, 0xadfc, 0x5ba8,
1845bfc04aecSBorislav Petkov 	0x19a9, 0x2efe, 0xb5cc, 0x6f88,
1846b1289d6fSDoug Thompson };
1847b1289d6fSDoug Thompson 
1848c7e5301aSDaniel J Blueman static const u16 x8_vectors[] = {
1849bfc04aecSBorislav Petkov 	0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
1850bfc04aecSBorislav Petkov 	0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
1851bfc04aecSBorislav Petkov 	0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
1852bfc04aecSBorislav Petkov 	0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
1853bfc04aecSBorislav Petkov 	0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
1854bfc04aecSBorislav Petkov 	0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
1855bfc04aecSBorislav Petkov 	0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
1856bfc04aecSBorislav Petkov 	0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
1857bfc04aecSBorislav Petkov 	0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
1858bfc04aecSBorislav Petkov 	0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
1859bfc04aecSBorislav Petkov 	0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
1860bfc04aecSBorislav Petkov 	0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
1861bfc04aecSBorislav Petkov 	0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
1862bfc04aecSBorislav Petkov 	0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
1863bfc04aecSBorislav Petkov 	0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
1864bfc04aecSBorislav Petkov 	0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
1865bfc04aecSBorislav Petkov 	0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
1866bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
1867bfc04aecSBorislav Petkov 	0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
1868bfc04aecSBorislav Petkov };
1869bfc04aecSBorislav Petkov 
1870c7e5301aSDaniel J Blueman static int decode_syndrome(u16 syndrome, const u16 *vectors, unsigned num_vecs,
1871d34a6ecdSBorislav Petkov 			   unsigned v_dim)
1872b1289d6fSDoug Thompson {
1873bfc04aecSBorislav Petkov 	unsigned int i, err_sym;
1874b1289d6fSDoug Thompson 
1875bfc04aecSBorislav Petkov 	for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
1876bfc04aecSBorislav Petkov 		u16 s = syndrome;
1877d34a6ecdSBorislav Petkov 		unsigned v_idx =  err_sym * v_dim;
1878d34a6ecdSBorislav Petkov 		unsigned v_end = (err_sym + 1) * v_dim;
1879b1289d6fSDoug Thompson 
1880bfc04aecSBorislav Petkov 		/* walk over all 16 bits of the syndrome */
1881bfc04aecSBorislav Petkov 		for (i = 1; i < (1U << 16); i <<= 1) {
1882bfc04aecSBorislav Petkov 
1883bfc04aecSBorislav Petkov 			/* if bit is set in that eigenvector... */
1884bfc04aecSBorislav Petkov 			if (v_idx < v_end && vectors[v_idx] & i) {
1885bfc04aecSBorislav Petkov 				u16 ev_comp = vectors[v_idx++];
1886bfc04aecSBorislav Petkov 
1887bfc04aecSBorislav Petkov 				/* ... and bit set in the modified syndrome, */
1888bfc04aecSBorislav Petkov 				if (s & i) {
1889bfc04aecSBorislav Petkov 					/* remove it. */
1890bfc04aecSBorislav Petkov 					s ^= ev_comp;
1891bfc04aecSBorislav Petkov 
1892bfc04aecSBorislav Petkov 					if (!s)
1893bfc04aecSBorislav Petkov 						return err_sym;
1894bfc04aecSBorislav Petkov 				}
1895bfc04aecSBorislav Petkov 
1896bfc04aecSBorislav Petkov 			} else if (s & i)
1897bfc04aecSBorislav Petkov 				/* can't get to zero, move to next symbol */
1898bfc04aecSBorislav Petkov 				break;
1899bfc04aecSBorislav Petkov 		}
1900b1289d6fSDoug Thompson 	}
1901b1289d6fSDoug Thompson 
1902956b9ba1SJoe Perches 	edac_dbg(0, "syndrome(%x) not found\n", syndrome);
1903b1289d6fSDoug Thompson 	return -1;
1904b1289d6fSDoug Thompson }
1905d27bf6faSDoug Thompson 
1906bfc04aecSBorislav Petkov static int map_err_sym_to_channel(int err_sym, int sym_size)
1907bfc04aecSBorislav Petkov {
1908bfc04aecSBorislav Petkov 	if (sym_size == 4)
1909bfc04aecSBorislav Petkov 		switch (err_sym) {
1910bfc04aecSBorislav Petkov 		case 0x20:
1911bfc04aecSBorislav Petkov 		case 0x21:
1912bfc04aecSBorislav Petkov 			return 0;
1913bfc04aecSBorislav Petkov 			break;
1914bfc04aecSBorislav Petkov 		case 0x22:
1915bfc04aecSBorislav Petkov 		case 0x23:
1916bfc04aecSBorislav Petkov 			return 1;
1917bfc04aecSBorislav Petkov 			break;
1918bfc04aecSBorislav Petkov 		default:
1919bfc04aecSBorislav Petkov 			return err_sym >> 4;
1920bfc04aecSBorislav Petkov 			break;
1921bfc04aecSBorislav Petkov 		}
1922bfc04aecSBorislav Petkov 	/* x8 symbols */
1923bfc04aecSBorislav Petkov 	else
1924bfc04aecSBorislav Petkov 		switch (err_sym) {
1925bfc04aecSBorislav Petkov 		/* imaginary bits not in a DIMM */
1926bfc04aecSBorislav Petkov 		case 0x10:
1927bfc04aecSBorislav Petkov 			WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
1928bfc04aecSBorislav Petkov 					  err_sym);
1929bfc04aecSBorislav Petkov 			return -1;
1930bfc04aecSBorislav Petkov 			break;
1931bfc04aecSBorislav Petkov 
1932bfc04aecSBorislav Petkov 		case 0x11:
1933bfc04aecSBorislav Petkov 			return 0;
1934bfc04aecSBorislav Petkov 			break;
1935bfc04aecSBorislav Petkov 		case 0x12:
1936bfc04aecSBorislav Petkov 			return 1;
1937bfc04aecSBorislav Petkov 			break;
1938bfc04aecSBorislav Petkov 		default:
1939bfc04aecSBorislav Petkov 			return err_sym >> 3;
1940bfc04aecSBorislav Petkov 			break;
1941bfc04aecSBorislav Petkov 		}
1942bfc04aecSBorislav Petkov 	return -1;
1943bfc04aecSBorislav Petkov }
1944bfc04aecSBorislav Petkov 
1945bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
1946bfc04aecSBorislav Petkov {
1947bfc04aecSBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
1948ad6a32e9SBorislav Petkov 	int err_sym = -1;
1949bfc04aecSBorislav Petkov 
1950a3b7db09SBorislav Petkov 	if (pvt->ecc_sym_sz == 8)
1951bfc04aecSBorislav Petkov 		err_sym = decode_syndrome(syndrome, x8_vectors,
1952ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x8_vectors),
1953a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
1954a3b7db09SBorislav Petkov 	else if (pvt->ecc_sym_sz == 4)
1955ad6a32e9SBorislav Petkov 		err_sym = decode_syndrome(syndrome, x4_vectors,
1956ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x4_vectors),
1957a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
1958ad6a32e9SBorislav Petkov 	else {
1959a3b7db09SBorislav Petkov 		amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz);
1960ad6a32e9SBorislav Petkov 		return err_sym;
1961bfc04aecSBorislav Petkov 	}
1962ad6a32e9SBorislav Petkov 
1963a3b7db09SBorislav Petkov 	return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz);
196441c31044SBorislav Petkov }
1965bfc04aecSBorislav Petkov 
196633ca0643SBorislav Petkov static void __log_bus_error(struct mem_ctl_info *mci, struct err_info *err,
196733ca0643SBorislav Petkov 			    u8 ecc_type)
1968d27bf6faSDoug Thompson {
196933ca0643SBorislav Petkov 	enum hw_event_mc_err_type err_type;
197033ca0643SBorislav Petkov 	const char *string;
1971d27bf6faSDoug Thompson 
197233ca0643SBorislav Petkov 	if (ecc_type == 2)
197333ca0643SBorislav Petkov 		err_type = HW_EVENT_ERR_CORRECTED;
197433ca0643SBorislav Petkov 	else if (ecc_type == 1)
197533ca0643SBorislav Petkov 		err_type = HW_EVENT_ERR_UNCORRECTED;
197633ca0643SBorislav Petkov 	else {
197733ca0643SBorislav Petkov 		WARN(1, "Something is rotten in the state of Denmark.\n");
1978d27bf6faSDoug Thompson 		return;
1979d27bf6faSDoug Thompson 	}
1980d27bf6faSDoug Thompson 
198133ca0643SBorislav Petkov 	switch (err->err_code) {
198233ca0643SBorislav Petkov 	case DECODE_OK:
198333ca0643SBorislav Petkov 		string = "";
198433ca0643SBorislav Petkov 		break;
198533ca0643SBorislav Petkov 	case ERR_NODE:
198633ca0643SBorislav Petkov 		string = "Failed to map error addr to a node";
198733ca0643SBorislav Petkov 		break;
198833ca0643SBorislav Petkov 	case ERR_CSROW:
198933ca0643SBorislav Petkov 		string = "Failed to map error addr to a csrow";
199033ca0643SBorislav Petkov 		break;
199133ca0643SBorislav Petkov 	case ERR_CHANNEL:
199233ca0643SBorislav Petkov 		string = "unknown syndrome - possible error reporting race";
199333ca0643SBorislav Petkov 		break;
199433ca0643SBorislav Petkov 	default:
199533ca0643SBorislav Petkov 		string = "WTF error";
199633ca0643SBorislav Petkov 		break;
1997d27bf6faSDoug Thompson 	}
199833ca0643SBorislav Petkov 
199933ca0643SBorislav Petkov 	edac_mc_handle_error(err_type, mci, 1,
200033ca0643SBorislav Petkov 			     err->page, err->offset, err->syndrome,
200133ca0643SBorislav Petkov 			     err->csrow, err->channel, -1,
200233ca0643SBorislav Petkov 			     string, "");
2003d27bf6faSDoug Thompson }
2004d27bf6faSDoug Thompson 
2005549d042dSBorislav Petkov static inline void __amd64_decode_bus_error(struct mem_ctl_info *mci,
2006f192c7b1SBorislav Petkov 					    struct mce *m)
2007d27bf6faSDoug Thompson {
200833ca0643SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
2009f192c7b1SBorislav Petkov 	u8 ecc_type = (m->status >> 45) & 0x3;
201066fed2d4SBorislav Petkov 	u8 xec = XEC(m->status, 0x1f);
201166fed2d4SBorislav Petkov 	u16 ec = EC(m->status);
201233ca0643SBorislav Petkov 	u64 sys_addr;
201333ca0643SBorislav Petkov 	struct err_info err;
2014d27bf6faSDoug Thompson 
201566fed2d4SBorislav Petkov 	/* Bail out early if this was an 'observed' error */
20165980bb9cSBorislav Petkov 	if (PP(ec) == NBSL_PP_OBS)
2017b70ef010SBorislav Petkov 		return;
2018d27bf6faSDoug Thompson 
2019ecaf5606SBorislav Petkov 	/* Do only ECC errors */
2020ecaf5606SBorislav Petkov 	if (xec && xec != F10_NBSL_EXT_ERR_ECC)
2021d27bf6faSDoug Thompson 		return;
2022d27bf6faSDoug Thompson 
202333ca0643SBorislav Petkov 	memset(&err, 0, sizeof(err));
202433ca0643SBorislav Petkov 
2025a4b4bedcSBorislav Petkov 	sys_addr = get_error_address(pvt, m);
202633ca0643SBorislav Petkov 
2027ecaf5606SBorislav Petkov 	if (ecc_type == 2)
202833ca0643SBorislav Petkov 		err.syndrome = extract_syndrome(m->status);
202933ca0643SBorislav Petkov 
203033ca0643SBorislav Petkov 	pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err);
203133ca0643SBorislav Petkov 
203233ca0643SBorislav Petkov 	__log_bus_error(mci, &err, ecc_type);
2033d27bf6faSDoug Thompson }
2034d27bf6faSDoug Thompson 
2035b0b07a2bSBorislav Petkov void amd64_decode_bus_error(int node_id, struct mce *m)
2036d27bf6faSDoug Thompson {
2037b0b07a2bSBorislav Petkov 	__amd64_decode_bus_error(mcis[node_id], m);
2038d27bf6faSDoug Thompson }
2039d27bf6faSDoug Thompson 
20400ec449eeSDoug Thompson /*
20418d5b5d9cSBorislav Petkov  * Use pvt->F2 which contains the F2 CPU PCI device to get the related
2042bbd0c1f6SBorislav Petkov  * F1 (AddrMap) and F3 (Misc) devices. Return negative value on error.
20430ec449eeSDoug Thompson  */
2044360b7f3cSBorislav Petkov static int reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 f1_id, u16 f3_id)
20450ec449eeSDoug Thompson {
20460ec449eeSDoug Thompson 	/* Reserve the ADDRESS MAP Device */
20478d5b5d9cSBorislav Petkov 	pvt->F1 = pci_get_related_function(pvt->F2->vendor, f1_id, pvt->F2);
20488d5b5d9cSBorislav Petkov 	if (!pvt->F1) {
204924f9a7feSBorislav Petkov 		amd64_err("error address map device not found: "
20500ec449eeSDoug Thompson 			  "vendor %x device 0x%x (broken BIOS?)\n",
2051bbd0c1f6SBorislav Petkov 			  PCI_VENDOR_ID_AMD, f1_id);
2052bbd0c1f6SBorislav Petkov 		return -ENODEV;
20530ec449eeSDoug Thompson 	}
20540ec449eeSDoug Thompson 
20550ec449eeSDoug Thompson 	/* Reserve the MISC Device */
20568d5b5d9cSBorislav Petkov 	pvt->F3 = pci_get_related_function(pvt->F2->vendor, f3_id, pvt->F2);
20578d5b5d9cSBorislav Petkov 	if (!pvt->F3) {
20588d5b5d9cSBorislav Petkov 		pci_dev_put(pvt->F1);
20598d5b5d9cSBorislav Petkov 		pvt->F1 = NULL;
20600ec449eeSDoug Thompson 
206124f9a7feSBorislav Petkov 		amd64_err("error F3 device not found: "
20620ec449eeSDoug Thompson 			  "vendor %x device 0x%x (broken BIOS?)\n",
2063bbd0c1f6SBorislav Petkov 			  PCI_VENDOR_ID_AMD, f3_id);
20648d5b5d9cSBorislav Petkov 
2065bbd0c1f6SBorislav Petkov 		return -ENODEV;
20660ec449eeSDoug Thompson 	}
2067956b9ba1SJoe Perches 	edac_dbg(1, "F1: %s\n", pci_name(pvt->F1));
2068956b9ba1SJoe Perches 	edac_dbg(1, "F2: %s\n", pci_name(pvt->F2));
2069956b9ba1SJoe Perches 	edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
20700ec449eeSDoug Thompson 
20710ec449eeSDoug Thompson 	return 0;
20720ec449eeSDoug Thompson }
20730ec449eeSDoug Thompson 
2074360b7f3cSBorislav Petkov static void free_mc_sibling_devs(struct amd64_pvt *pvt)
20750ec449eeSDoug Thompson {
20768d5b5d9cSBorislav Petkov 	pci_dev_put(pvt->F1);
20778d5b5d9cSBorislav Petkov 	pci_dev_put(pvt->F3);
20780ec449eeSDoug Thompson }
20790ec449eeSDoug Thompson 
20800ec449eeSDoug Thompson /*
20810ec449eeSDoug Thompson  * Retrieve the hardware registers of the memory controller (this includes the
20820ec449eeSDoug Thompson  * 'Address Map' and 'Misc' device regs)
20830ec449eeSDoug Thompson  */
2084360b7f3cSBorislav Petkov static void read_mc_regs(struct amd64_pvt *pvt)
20850ec449eeSDoug Thompson {
2086a4b4bedcSBorislav Petkov 	unsigned range;
20870ec449eeSDoug Thompson 	u64 msr_val;
2088ad6a32e9SBorislav Petkov 	u32 tmp;
20890ec449eeSDoug Thompson 
20900ec449eeSDoug Thompson 	/*
20910ec449eeSDoug Thompson 	 * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
20920ec449eeSDoug Thompson 	 * those are Read-As-Zero
20930ec449eeSDoug Thompson 	 */
2094e97f8bb8SBorislav Petkov 	rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
2095956b9ba1SJoe Perches 	edac_dbg(0, "  TOP_MEM:  0x%016llx\n", pvt->top_mem);
20960ec449eeSDoug Thompson 
20970ec449eeSDoug Thompson 	/* check first whether TOP_MEM2 is enabled */
20980ec449eeSDoug Thompson 	rdmsrl(MSR_K8_SYSCFG, msr_val);
20990ec449eeSDoug Thompson 	if (msr_val & (1U << 21)) {
2100e97f8bb8SBorislav Petkov 		rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
2101956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
21020ec449eeSDoug Thompson 	} else
2103956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2 disabled\n");
21040ec449eeSDoug Thompson 
21055980bb9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap);
21060ec449eeSDoug Thompson 
21075a5d2371SBorislav Petkov 	read_dram_ctl_register(pvt);
21080ec449eeSDoug Thompson 
21097f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
21107f19bf75SBorislav Petkov 		u8 rw;
21110ec449eeSDoug Thompson 
21127f19bf75SBorislav Petkov 		/* read settings for this DRAM range */
21137f19bf75SBorislav Petkov 		read_dram_base_limit_regs(pvt, range);
2114e97f8bb8SBorislav Petkov 
21157f19bf75SBorislav Petkov 		rw = dram_rw(pvt, range);
21167f19bf75SBorislav Petkov 		if (!rw)
21177f19bf75SBorislav Petkov 			continue;
21187f19bf75SBorislav Petkov 
2119956b9ba1SJoe Perches 		edac_dbg(1, "  DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
21207f19bf75SBorislav Petkov 			 range,
21217f19bf75SBorislav Petkov 			 get_dram_base(pvt, range),
21227f19bf75SBorislav Petkov 			 get_dram_limit(pvt, range));
21237f19bf75SBorislav Petkov 
2124956b9ba1SJoe Perches 		edac_dbg(1, "   IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
21257f19bf75SBorislav Petkov 			 dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
21267f19bf75SBorislav Petkov 			 (rw & 0x1) ? "R" : "-",
21277f19bf75SBorislav Petkov 			 (rw & 0x2) ? "W" : "-",
21287f19bf75SBorislav Petkov 			 dram_intlv_sel(pvt, range),
21297f19bf75SBorislav Petkov 			 dram_dst_node(pvt, range));
21300ec449eeSDoug Thompson 	}
21310ec449eeSDoug Thompson 
2132b2b0c605SBorislav Petkov 	read_dct_base_mask(pvt);
21330ec449eeSDoug Thompson 
2134bc21fa57SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar);
2135525a1b20SBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, DBAM0, &pvt->dbam0);
21360ec449eeSDoug Thompson 
21378d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare);
21380ec449eeSDoug Thompson 
2139cb328507SBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, DCLR0, &pvt->dclr0);
2140cb328507SBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, DCHR0, &pvt->dchr0);
21410ec449eeSDoug Thompson 
214278da121eSBorislav Petkov 	if (!dct_ganging_enabled(pvt)) {
2143cb328507SBorislav Petkov 		amd64_read_dct_pci_cfg(pvt, DCLR1, &pvt->dclr1);
2144cb328507SBorislav Petkov 		amd64_read_dct_pci_cfg(pvt, DCHR1, &pvt->dchr1);
21450ec449eeSDoug Thompson 	}
2146b2b0c605SBorislav Petkov 
2147a3b7db09SBorislav Petkov 	pvt->ecc_sym_sz = 4;
2148a3b7db09SBorislav Petkov 
2149a4b4bedcSBorislav Petkov 	if (pvt->fam >= 0x10) {
21508d5b5d9cSBorislav Petkov 		amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp);
2151a4b4bedcSBorislav Petkov 		if (pvt->fam != 0x16)
215294c1acf2SAravind Gopalakrishnan 			/* F16h has only DCT0 */
2153525a1b20SBorislav Petkov 			amd64_read_dct_pci_cfg(pvt, DBAM1, &pvt->dbam1);
2154a3b7db09SBorislav Petkov 
2155a3b7db09SBorislav Petkov 		/* F10h, revD and later can do x8 ECC too */
2156a4b4bedcSBorislav Petkov 		if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25))
2157a3b7db09SBorislav Petkov 			pvt->ecc_sym_sz = 8;
2158525a1b20SBorislav Petkov 	}
2159b2b0c605SBorislav Petkov 	dump_misc_regs(pvt);
21600ec449eeSDoug Thompson }
21610ec449eeSDoug Thompson 
21620ec449eeSDoug Thompson /*
21630ec449eeSDoug Thompson  * NOTE: CPU Revision Dependent code
21640ec449eeSDoug Thompson  *
21650ec449eeSDoug Thompson  * Input:
216611c75eadSBorislav Petkov  *	@csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1)
21670ec449eeSDoug Thompson  *	k8 private pointer to -->
21680ec449eeSDoug Thompson  *			DRAM Bank Address mapping register
21690ec449eeSDoug Thompson  *			node_id
21700ec449eeSDoug Thompson  *			DCL register where dual_channel_active is
21710ec449eeSDoug Thompson  *
21720ec449eeSDoug Thompson  * The DBAM register consists of 4 sets of 4 bits each definitions:
21730ec449eeSDoug Thompson  *
21740ec449eeSDoug Thompson  * Bits:	CSROWs
21750ec449eeSDoug Thompson  * 0-3		CSROWs 0 and 1
21760ec449eeSDoug Thompson  * 4-7		CSROWs 2 and 3
21770ec449eeSDoug Thompson  * 8-11		CSROWs 4 and 5
21780ec449eeSDoug Thompson  * 12-15	CSROWs 6 and 7
21790ec449eeSDoug Thompson  *
21800ec449eeSDoug Thompson  * Values range from: 0 to 15
21810ec449eeSDoug Thompson  * The meaning of the values depends on CPU revision and dual-channel state,
21820ec449eeSDoug Thompson  * see relevant BKDG more info.
21830ec449eeSDoug Thompson  *
21840ec449eeSDoug Thompson  * The memory controller provides for total of only 8 CSROWs in its current
21850ec449eeSDoug Thompson  * architecture. Each "pair" of CSROWs normally represents just one DIMM in
21860ec449eeSDoug Thompson  * single channel or two (2) DIMMs in dual channel mode.
21870ec449eeSDoug Thompson  *
21880ec449eeSDoug Thompson  * The following code logic collapses the various tables for CSROW based on CPU
21890ec449eeSDoug Thompson  * revision.
21900ec449eeSDoug Thompson  *
21910ec449eeSDoug Thompson  * Returns:
21920ec449eeSDoug Thompson  *	The number of PAGE_SIZE pages on the specified CSROW number it
21930ec449eeSDoug Thompson  *	encompasses
21940ec449eeSDoug Thompson  *
21950ec449eeSDoug Thompson  */
219641d8bfabSBorislav Petkov static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr)
21970ec449eeSDoug Thompson {
21981433eb99SBorislav Petkov 	u32 cs_mode, nr_pages;
2199f92cae45SAshish Shenoy 	u32 dbam = dct ? pvt->dbam1 : pvt->dbam0;
22000ec449eeSDoug Thompson 
220110de6497SBorislav Petkov 
22020ec449eeSDoug Thompson 	/*
22030ec449eeSDoug Thompson 	 * The math on this doesn't look right on the surface because x/2*4 can
22040ec449eeSDoug Thompson 	 * be simplified to x*2 but this expression makes use of the fact that
22050ec449eeSDoug Thompson 	 * it is integral math where 1/2=0. This intermediate value becomes the
22060ec449eeSDoug Thompson 	 * number of bits to shift the DBAM register to extract the proper CSROW
22070ec449eeSDoug Thompson 	 * field.
22080ec449eeSDoug Thompson 	 */
22090a5dfc31SBorislav Petkov 	cs_mode = DBAM_DIMM(csrow_nr / 2, dbam);
22100ec449eeSDoug Thompson 
221141d8bfabSBorislav Petkov 	nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode) << (20 - PAGE_SHIFT);
22120ec449eeSDoug Thompson 
221310de6497SBorislav Petkov 	edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n",
221410de6497SBorislav Petkov 		    csrow_nr, dct,  cs_mode);
221510de6497SBorislav Petkov 	edac_dbg(0, "nr_pages/channel: %u\n", nr_pages);
22160ec449eeSDoug Thompson 
22170ec449eeSDoug Thompson 	return nr_pages;
22180ec449eeSDoug Thompson }
22190ec449eeSDoug Thompson 
22200ec449eeSDoug Thompson /*
22210ec449eeSDoug Thompson  * Initialize the array of csrow attribute instances, based on the values
22220ec449eeSDoug Thompson  * from pci config hardware registers.
22230ec449eeSDoug Thompson  */
2224360b7f3cSBorislav Petkov static int init_csrows(struct mem_ctl_info *mci)
22250ec449eeSDoug Thompson {
222610de6497SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
22270ec449eeSDoug Thompson 	struct csrow_info *csrow;
2228de3910ebSMauro Carvalho Chehab 	struct dimm_info *dimm;
2229084a4fccSMauro Carvalho Chehab 	enum edac_type edac_mode;
223010de6497SBorislav Petkov 	enum mem_type mtype;
223110de6497SBorislav Petkov 	int i, j, empty = 1;
2232a895bf8bSMauro Carvalho Chehab 	int nr_pages = 0;
223310de6497SBorislav Petkov 	u32 val;
22340ec449eeSDoug Thompson 
2235a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
22360ec449eeSDoug Thompson 
22372299ef71SBorislav Petkov 	pvt->nbcfg = val;
22380ec449eeSDoug Thompson 
2239956b9ba1SJoe Perches 	edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
22402299ef71SBorislav Petkov 		 pvt->mc_node_id, val,
2241a97fa68eSBorislav Petkov 		 !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
22420ec449eeSDoug Thompson 
224310de6497SBorislav Petkov 	/*
224410de6497SBorislav Petkov 	 * We iterate over DCT0 here but we look at DCT1 in parallel, if needed.
224510de6497SBorislav Petkov 	 */
224611c75eadSBorislav Petkov 	for_each_chip_select(i, 0, pvt) {
224710de6497SBorislav Petkov 		bool row_dct0 = !!csrow_enabled(i, 0, pvt);
224810de6497SBorislav Petkov 		bool row_dct1 = false;
22490ec449eeSDoug Thompson 
2250a4b4bedcSBorislav Petkov 		if (pvt->fam != 0xf)
225110de6497SBorislav Petkov 			row_dct1 = !!csrow_enabled(i, 1, pvt);
225210de6497SBorislav Petkov 
225310de6497SBorislav Petkov 		if (!row_dct0 && !row_dct1)
22540ec449eeSDoug Thompson 			continue;
22550ec449eeSDoug Thompson 
225610de6497SBorislav Petkov 		csrow = mci->csrows[i];
22570ec449eeSDoug Thompson 		empty = 0;
225811c75eadSBorislav Petkov 
225910de6497SBorislav Petkov 		edac_dbg(1, "MC node: %d, csrow: %d\n",
226010de6497SBorislav Petkov 			    pvt->mc_node_id, i);
226110de6497SBorislav Petkov 
22621eef1282SMauro Carvalho Chehab 		if (row_dct0) {
226310de6497SBorislav Petkov 			nr_pages = amd64_csrow_nr_pages(pvt, 0, i);
22641eef1282SMauro Carvalho Chehab 			csrow->channels[0]->dimm->nr_pages = nr_pages;
22651eef1282SMauro Carvalho Chehab 		}
226610de6497SBorislav Petkov 
226710de6497SBorislav Petkov 		/* K8 has only one DCT */
2268a4b4bedcSBorislav Petkov 		if (pvt->fam != 0xf && row_dct1) {
22691eef1282SMauro Carvalho Chehab 			int row_dct1_pages = amd64_csrow_nr_pages(pvt, 1, i);
22701eef1282SMauro Carvalho Chehab 
22711eef1282SMauro Carvalho Chehab 			csrow->channels[1]->dimm->nr_pages = row_dct1_pages;
22721eef1282SMauro Carvalho Chehab 			nr_pages += row_dct1_pages;
22731eef1282SMauro Carvalho Chehab 		}
22740ec449eeSDoug Thompson 
2275084a4fccSMauro Carvalho Chehab 		mtype = amd64_determine_memory_type(pvt, i);
22760ec449eeSDoug Thompson 
227710de6497SBorislav Petkov 		edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages);
22780ec449eeSDoug Thompson 
22790ec449eeSDoug Thompson 		/*
22800ec449eeSDoug Thompson 		 * determine whether CHIPKILL or JUST ECC or NO ECC is operating
22810ec449eeSDoug Thompson 		 */
2282a97fa68eSBorislav Petkov 		if (pvt->nbcfg & NBCFG_ECC_ENABLE)
2283084a4fccSMauro Carvalho Chehab 			edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) ?
22840ec449eeSDoug Thompson 				    EDAC_S4ECD4ED : EDAC_SECDED;
22850ec449eeSDoug Thompson 		else
2286084a4fccSMauro Carvalho Chehab 			edac_mode = EDAC_NONE;
2287084a4fccSMauro Carvalho Chehab 
2288084a4fccSMauro Carvalho Chehab 		for (j = 0; j < pvt->channel_count; j++) {
2289de3910ebSMauro Carvalho Chehab 			dimm = csrow->channels[j]->dimm;
2290de3910ebSMauro Carvalho Chehab 			dimm->mtype = mtype;
2291de3910ebSMauro Carvalho Chehab 			dimm->edac_mode = edac_mode;
2292084a4fccSMauro Carvalho Chehab 		}
22930ec449eeSDoug Thompson 	}
22940ec449eeSDoug Thompson 
22950ec449eeSDoug Thompson 	return empty;
22960ec449eeSDoug Thompson }
2297d27bf6faSDoug Thompson 
229806724535SBorislav Petkov /* get all cores on this DCT */
22998b84c8dfSDaniel J Blueman static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, u16 nid)
2300f9431992SDoug Thompson {
230106724535SBorislav Petkov 	int cpu;
2302f9431992SDoug Thompson 
230306724535SBorislav Petkov 	for_each_online_cpu(cpu)
230406724535SBorislav Petkov 		if (amd_get_nb_id(cpu) == nid)
230506724535SBorislav Petkov 			cpumask_set_cpu(cpu, mask);
2306f9431992SDoug Thompson }
2307f9431992SDoug Thompson 
2308f9431992SDoug Thompson /* check MCG_CTL on all the cpus on this node */
2309c7e5301aSDaniel J Blueman static bool amd64_nb_mce_bank_enabled_on_node(u16 nid)
2310f9431992SDoug Thompson {
2311ba578cb3SRusty Russell 	cpumask_var_t mask;
231250542251SBorislav Petkov 	int cpu, nbe;
231306724535SBorislav Petkov 	bool ret = false;
2314f9431992SDoug Thompson 
2315ba578cb3SRusty Russell 	if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
231624f9a7feSBorislav Petkov 		amd64_warn("%s: Error allocating mask\n", __func__);
231706724535SBorislav Petkov 		return false;
231806724535SBorislav Petkov 	}
231906724535SBorislav Petkov 
2320ba578cb3SRusty Russell 	get_cpus_on_this_dct_cpumask(mask, nid);
232106724535SBorislav Petkov 
2322ba578cb3SRusty Russell 	rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
2323ba578cb3SRusty Russell 
2324ba578cb3SRusty Russell 	for_each_cpu(cpu, mask) {
232550542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
23265980bb9cSBorislav Petkov 		nbe = reg->l & MSR_MCGCTL_NBE;
232706724535SBorislav Petkov 
2328956b9ba1SJoe Perches 		edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
232950542251SBorislav Petkov 			 cpu, reg->q,
233006724535SBorislav Petkov 			 (nbe ? "enabled" : "disabled"));
233106724535SBorislav Petkov 
233206724535SBorislav Petkov 		if (!nbe)
233306724535SBorislav Petkov 			goto out;
233406724535SBorislav Petkov 	}
233506724535SBorislav Petkov 	ret = true;
233606724535SBorislav Petkov 
233706724535SBorislav Petkov out:
2338ba578cb3SRusty Russell 	free_cpumask_var(mask);
2339f9431992SDoug Thompson 	return ret;
2340f9431992SDoug Thompson }
2341f9431992SDoug Thompson 
2342c7e5301aSDaniel J Blueman static int toggle_ecc_err_reporting(struct ecc_settings *s, u16 nid, bool on)
2343f6d6ae96SBorislav Petkov {
2344f6d6ae96SBorislav Petkov 	cpumask_var_t cmask;
234550542251SBorislav Petkov 	int cpu;
2346f6d6ae96SBorislav Petkov 
2347f6d6ae96SBorislav Petkov 	if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
234824f9a7feSBorislav Petkov 		amd64_warn("%s: error allocating mask\n", __func__);
2349f6d6ae96SBorislav Petkov 		return false;
2350f6d6ae96SBorislav Petkov 	}
2351f6d6ae96SBorislav Petkov 
2352ae7bb7c6SBorislav Petkov 	get_cpus_on_this_dct_cpumask(cmask, nid);
2353f6d6ae96SBorislav Petkov 
2354f6d6ae96SBorislav Petkov 	rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2355f6d6ae96SBorislav Petkov 
2356f6d6ae96SBorislav Petkov 	for_each_cpu(cpu, cmask) {
2357f6d6ae96SBorislav Petkov 
235850542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
235950542251SBorislav Petkov 
2360f6d6ae96SBorislav Petkov 		if (on) {
23615980bb9cSBorislav Petkov 			if (reg->l & MSR_MCGCTL_NBE)
2362ae7bb7c6SBorislav Petkov 				s->flags.nb_mce_enable = 1;
2363f6d6ae96SBorislav Petkov 
23645980bb9cSBorislav Petkov 			reg->l |= MSR_MCGCTL_NBE;
2365f6d6ae96SBorislav Petkov 		} else {
2366f6d6ae96SBorislav Petkov 			/*
2367d95cf4deSBorislav Petkov 			 * Turn off NB MCE reporting only when it was off before
2368f6d6ae96SBorislav Petkov 			 */
2369ae7bb7c6SBorislav Petkov 			if (!s->flags.nb_mce_enable)
23705980bb9cSBorislav Petkov 				reg->l &= ~MSR_MCGCTL_NBE;
2371f6d6ae96SBorislav Petkov 		}
2372f6d6ae96SBorislav Petkov 	}
2373f6d6ae96SBorislav Petkov 	wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2374f6d6ae96SBorislav Petkov 
2375f6d6ae96SBorislav Petkov 	free_cpumask_var(cmask);
2376f6d6ae96SBorislav Petkov 
2377f6d6ae96SBorislav Petkov 	return 0;
2378f6d6ae96SBorislav Petkov }
2379f6d6ae96SBorislav Petkov 
2380c7e5301aSDaniel J Blueman static bool enable_ecc_error_reporting(struct ecc_settings *s, u16 nid,
23812299ef71SBorislav Petkov 				       struct pci_dev *F3)
2382f6d6ae96SBorislav Petkov {
23832299ef71SBorislav Petkov 	bool ret = true;
2384c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
2385f6d6ae96SBorislav Petkov 
23862299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, ON)) {
23872299ef71SBorislav Petkov 		amd64_warn("Error enabling ECC reporting over MCGCTL!\n");
23882299ef71SBorislav Petkov 		return false;
23892299ef71SBorislav Petkov 	}
23902299ef71SBorislav Petkov 
2391c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
2392f6d6ae96SBorislav Petkov 
2393ae7bb7c6SBorislav Petkov 	s->old_nbctl   = value & mask;
2394ae7bb7c6SBorislav Petkov 	s->nbctl_valid = true;
2395f6d6ae96SBorislav Petkov 
2396f6d6ae96SBorislav Petkov 	value |= mask;
2397c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
2398f6d6ae96SBorislav Petkov 
2399a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCFG, &value);
2400f6d6ae96SBorislav Petkov 
2401956b9ba1SJoe Perches 	edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2402a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
2403f6d6ae96SBorislav Petkov 
2404a97fa68eSBorislav Petkov 	if (!(value & NBCFG_ECC_ENABLE)) {
240524f9a7feSBorislav Petkov 		amd64_warn("DRAM ECC disabled on this node, enabling...\n");
2406f6d6ae96SBorislav Petkov 
2407ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 0;
2408d95cf4deSBorislav Petkov 
2409f6d6ae96SBorislav Petkov 		/* Attempt to turn on DRAM ECC Enable */
2410a97fa68eSBorislav Petkov 		value |= NBCFG_ECC_ENABLE;
2411a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
2412f6d6ae96SBorislav Petkov 
2413a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
2414f6d6ae96SBorislav Petkov 
2415a97fa68eSBorislav Petkov 		if (!(value & NBCFG_ECC_ENABLE)) {
241624f9a7feSBorislav Petkov 			amd64_warn("Hardware rejected DRAM ECC enable,"
241724f9a7feSBorislav Petkov 				   "check memory DIMM configuration.\n");
24182299ef71SBorislav Petkov 			ret = false;
2419f6d6ae96SBorislav Petkov 		} else {
242024f9a7feSBorislav Petkov 			amd64_info("Hardware accepted DRAM ECC Enable\n");
2421f6d6ae96SBorislav Petkov 		}
2422d95cf4deSBorislav Petkov 	} else {
2423ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 1;
2424f6d6ae96SBorislav Petkov 	}
2425d95cf4deSBorislav Petkov 
2426956b9ba1SJoe Perches 	edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2427a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
2428f6d6ae96SBorislav Petkov 
24292299ef71SBorislav Petkov 	return ret;
2430f6d6ae96SBorislav Petkov }
2431f6d6ae96SBorislav Petkov 
2432c7e5301aSDaniel J Blueman static void restore_ecc_error_reporting(struct ecc_settings *s, u16 nid,
2433360b7f3cSBorislav Petkov 					struct pci_dev *F3)
2434f6d6ae96SBorislav Petkov {
2435c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
2436c9f4f26eSBorislav Petkov 
2437f6d6ae96SBorislav Petkov 
2438ae7bb7c6SBorislav Petkov 	if (!s->nbctl_valid)
2439f6d6ae96SBorislav Petkov 		return;
2440f6d6ae96SBorislav Petkov 
2441c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
2442f6d6ae96SBorislav Petkov 	value &= ~mask;
2443ae7bb7c6SBorislav Petkov 	value |= s->old_nbctl;
2444f6d6ae96SBorislav Petkov 
2445c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
2446f6d6ae96SBorislav Petkov 
2447ae7bb7c6SBorislav Petkov 	/* restore previous BIOS DRAM ECC "off" setting we force-enabled */
2448ae7bb7c6SBorislav Petkov 	if (!s->flags.nb_ecc_prev) {
2449a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
2450a97fa68eSBorislav Petkov 		value &= ~NBCFG_ECC_ENABLE;
2451a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
2452d95cf4deSBorislav Petkov 	}
2453d95cf4deSBorislav Petkov 
2454d95cf4deSBorislav Petkov 	/* restore the NB Enable MCGCTL bit */
24552299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, OFF))
245624f9a7feSBorislav Petkov 		amd64_warn("Error restoring NB MCGCTL settings!\n");
2457f6d6ae96SBorislav Petkov }
2458f6d6ae96SBorislav Petkov 
2459f9431992SDoug Thompson /*
24602299ef71SBorislav Petkov  * EDAC requires that the BIOS have ECC enabled before
24612299ef71SBorislav Petkov  * taking over the processing of ECC errors. A command line
24622299ef71SBorislav Petkov  * option allows to force-enable hardware ECC later in
24632299ef71SBorislav Petkov  * enable_ecc_error_reporting().
2464f9431992SDoug Thompson  */
2465cab4d277SBorislav Petkov static const char *ecc_msg =
2466cab4d277SBorislav Petkov 	"ECC disabled in the BIOS or no ECC capability, module will not load.\n"
2467cab4d277SBorislav Petkov 	" Either enable ECC checking or force module loading by setting "
2468cab4d277SBorislav Petkov 	"'ecc_enable_override'.\n"
2469cab4d277SBorislav Petkov 	" (Note that use of the override may cause unknown side effects.)\n";
2470be3468e8SBorislav Petkov 
2471c7e5301aSDaniel J Blueman static bool ecc_enabled(struct pci_dev *F3, u16 nid)
2472f9431992SDoug Thompson {
2473f9431992SDoug Thompson 	u32 value;
24742299ef71SBorislav Petkov 	u8 ecc_en = 0;
247506724535SBorislav Petkov 	bool nb_mce_en = false;
2476f9431992SDoug Thompson 
2477a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCFG, &value);
2478f9431992SDoug Thompson 
2479a97fa68eSBorislav Petkov 	ecc_en = !!(value & NBCFG_ECC_ENABLE);
24802299ef71SBorislav Petkov 	amd64_info("DRAM ECC %s.\n", (ecc_en ? "enabled" : "disabled"));
2481be3468e8SBorislav Petkov 
24822299ef71SBorislav Petkov 	nb_mce_en = amd64_nb_mce_bank_enabled_on_node(nid);
248306724535SBorislav Petkov 	if (!nb_mce_en)
24842299ef71SBorislav Petkov 		amd64_notice("NB MCE bank disabled, set MSR "
24852299ef71SBorislav Petkov 			     "0x%08x[4] on node %d to enable.\n",
24862299ef71SBorislav Petkov 			     MSR_IA32_MCG_CTL, nid);
2487be3468e8SBorislav Petkov 
24882299ef71SBorislav Petkov 	if (!ecc_en || !nb_mce_en) {
248924f9a7feSBorislav Petkov 		amd64_notice("%s", ecc_msg);
24902299ef71SBorislav Petkov 		return false;
2491be3468e8SBorislav Petkov 	}
24922299ef71SBorislav Petkov 	return true;
2493f9431992SDoug Thompson }
2494f9431992SDoug Thompson 
2495c5608759SMauro Carvalho Chehab static int set_mc_sysfs_attrs(struct mem_ctl_info *mci)
24967d6034d3SDoug Thompson {
2497a4b4bedcSBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
2498c5608759SMauro Carvalho Chehab 	int rc;
24997d6034d3SDoug Thompson 
2500c5608759SMauro Carvalho Chehab 	rc = amd64_create_sysfs_dbg_files(mci);
2501c5608759SMauro Carvalho Chehab 	if (rc < 0)
2502c5608759SMauro Carvalho Chehab 		return rc;
2503c5608759SMauro Carvalho Chehab 
2504a4b4bedcSBorislav Petkov 	if (pvt->fam >= 0x10) {
2505c5608759SMauro Carvalho Chehab 		rc = amd64_create_sysfs_inject_files(mci);
2506c5608759SMauro Carvalho Chehab 		if (rc < 0)
2507c5608759SMauro Carvalho Chehab 			return rc;
2508c5608759SMauro Carvalho Chehab 	}
2509c5608759SMauro Carvalho Chehab 
2510c5608759SMauro Carvalho Chehab 	return 0;
2511c5608759SMauro Carvalho Chehab }
2512c5608759SMauro Carvalho Chehab 
2513c5608759SMauro Carvalho Chehab static void del_mc_sysfs_attrs(struct mem_ctl_info *mci)
2514c5608759SMauro Carvalho Chehab {
2515a4b4bedcSBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
2516a4b4bedcSBorislav Petkov 
2517c5608759SMauro Carvalho Chehab 	amd64_remove_sysfs_dbg_files(mci);
25187d6034d3SDoug Thompson 
2519a4b4bedcSBorislav Petkov 	if (pvt->fam >= 0x10)
2520c5608759SMauro Carvalho Chehab 		amd64_remove_sysfs_inject_files(mci);
25217d6034d3SDoug Thompson }
25227d6034d3SDoug Thompson 
2523df71a053SBorislav Petkov static void setup_mci_misc_attrs(struct mem_ctl_info *mci,
2524df71a053SBorislav Petkov 				 struct amd64_family_type *fam)
25257d6034d3SDoug Thompson {
25267d6034d3SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
25277d6034d3SDoug Thompson 
25287d6034d3SDoug Thompson 	mci->mtype_cap		= MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
25297d6034d3SDoug Thompson 	mci->edac_ctl_cap	= EDAC_FLAG_NONE;
25307d6034d3SDoug Thompson 
25315980bb9cSBorislav Petkov 	if (pvt->nbcap & NBCAP_SECDED)
25327d6034d3SDoug Thompson 		mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
25337d6034d3SDoug Thompson 
25345980bb9cSBorislav Petkov 	if (pvt->nbcap & NBCAP_CHIPKILL)
25357d6034d3SDoug Thompson 		mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
25367d6034d3SDoug Thompson 
25377d6034d3SDoug Thompson 	mci->edac_cap		= amd64_determine_edac_cap(pvt);
25387d6034d3SDoug Thompson 	mci->mod_name		= EDAC_MOD_STR;
25397d6034d3SDoug Thompson 	mci->mod_ver		= EDAC_AMD64_VERSION;
2540df71a053SBorislav Petkov 	mci->ctl_name		= fam->ctl_name;
25418d5b5d9cSBorislav Petkov 	mci->dev_name		= pci_name(pvt->F2);
25427d6034d3SDoug Thompson 	mci->ctl_page_to_phys	= NULL;
25437d6034d3SDoug Thompson 
25447d6034d3SDoug Thompson 	/* memory scrubber interface */
25457d6034d3SDoug Thompson 	mci->set_sdram_scrub_rate = amd64_set_scrub_rate;
25467d6034d3SDoug Thompson 	mci->get_sdram_scrub_rate = amd64_get_scrub_rate;
25477d6034d3SDoug Thompson }
25487d6034d3SDoug Thompson 
25490092b20dSBorislav Petkov /*
25500092b20dSBorislav Petkov  * returns a pointer to the family descriptor on success, NULL otherwise.
25510092b20dSBorislav Petkov  */
25520092b20dSBorislav Petkov static struct amd64_family_type *amd64_per_family_init(struct amd64_pvt *pvt)
2553395ae783SBorislav Petkov {
25540092b20dSBorislav Petkov 	struct amd64_family_type *fam_type = NULL;
25550092b20dSBorislav Petkov 
255618b94f66SAravind Gopalakrishnan 	pvt->ext_model  = boot_cpu_data.x86_model >> 4;
2557a4b4bedcSBorislav Petkov 	pvt->stepping	= boot_cpu_data.x86_mask;
255818b94f66SAravind Gopalakrishnan 	pvt->model	= boot_cpu_data.x86_model;
255918b94f66SAravind Gopalakrishnan 	pvt->fam	= boot_cpu_data.x86;
256018b94f66SAravind Gopalakrishnan 
256118b94f66SAravind Gopalakrishnan 	switch (pvt->fam) {
2562395ae783SBorislav Petkov 	case 0xf:
25630092b20dSBorislav Petkov 		fam_type		= &amd64_family_types[K8_CPUS];
2564b8cfa02fSBorislav Petkov 		pvt->ops		= &amd64_family_types[K8_CPUS].ops;
2565395ae783SBorislav Petkov 		break;
2566df71a053SBorislav Petkov 
2567395ae783SBorislav Petkov 	case 0x10:
25680092b20dSBorislav Petkov 		fam_type		= &amd64_family_types[F10_CPUS];
2569b8cfa02fSBorislav Petkov 		pvt->ops		= &amd64_family_types[F10_CPUS].ops;
2570df71a053SBorislav Petkov 		break;
2571df71a053SBorislav Petkov 
2572df71a053SBorislav Petkov 	case 0x15:
257318b94f66SAravind Gopalakrishnan 		if (pvt->model == 0x30) {
257418b94f66SAravind Gopalakrishnan 			fam_type	= &amd64_family_types[F15_M30H_CPUS];
257518b94f66SAravind Gopalakrishnan 			pvt->ops	= &amd64_family_types[F15_M30H_CPUS].ops;
257618b94f66SAravind Gopalakrishnan 			break;
257718b94f66SAravind Gopalakrishnan 		}
257818b94f66SAravind Gopalakrishnan 
2579df71a053SBorislav Petkov 		fam_type		= &amd64_family_types[F15_CPUS];
2580df71a053SBorislav Petkov 		pvt->ops		= &amd64_family_types[F15_CPUS].ops;
2581395ae783SBorislav Petkov 		break;
2582395ae783SBorislav Petkov 
258394c1acf2SAravind Gopalakrishnan 	case 0x16:
258494c1acf2SAravind Gopalakrishnan 		fam_type		= &amd64_family_types[F16_CPUS];
258594c1acf2SAravind Gopalakrishnan 		pvt->ops		= &amd64_family_types[F16_CPUS].ops;
258694c1acf2SAravind Gopalakrishnan 		break;
258794c1acf2SAravind Gopalakrishnan 
2588395ae783SBorislav Petkov 	default:
258924f9a7feSBorislav Petkov 		amd64_err("Unsupported family!\n");
25900092b20dSBorislav Petkov 		return NULL;
2591395ae783SBorislav Petkov 	}
25920092b20dSBorislav Petkov 
2593df71a053SBorislav Petkov 	amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name,
259418b94f66SAravind Gopalakrishnan 		     (pvt->fam == 0xf ?
25950092b20dSBorislav Petkov 				(pvt->ext_model >= K8_REV_F  ? "revF or later "
25960092b20dSBorislav Petkov 							     : "revE or earlier ")
259724f9a7feSBorislav Petkov 				 : ""), pvt->mc_node_id);
25980092b20dSBorislav Petkov 	return fam_type;
2599395ae783SBorislav Petkov }
2600395ae783SBorislav Petkov 
26012299ef71SBorislav Petkov static int amd64_init_one_instance(struct pci_dev *F2)
26027d6034d3SDoug Thompson {
26037d6034d3SDoug Thompson 	struct amd64_pvt *pvt = NULL;
26040092b20dSBorislav Petkov 	struct amd64_family_type *fam_type = NULL;
2605360b7f3cSBorislav Petkov 	struct mem_ctl_info *mci = NULL;
2606ab5a503cSMauro Carvalho Chehab 	struct edac_mc_layer layers[2];
26077d6034d3SDoug Thompson 	int err = 0, ret;
2608772c3ff3SDaniel J Blueman 	u16 nid = amd_get_node_id(F2);
26097d6034d3SDoug Thompson 
26107d6034d3SDoug Thompson 	ret = -ENOMEM;
26117d6034d3SDoug Thompson 	pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
26127d6034d3SDoug Thompson 	if (!pvt)
2613360b7f3cSBorislav Petkov 		goto err_ret;
26147d6034d3SDoug Thompson 
2615360b7f3cSBorislav Petkov 	pvt->mc_node_id	= nid;
26168d5b5d9cSBorislav Petkov 	pvt->F2 = F2;
26177d6034d3SDoug Thompson 
2618395ae783SBorislav Petkov 	ret = -EINVAL;
26190092b20dSBorislav Petkov 	fam_type = amd64_per_family_init(pvt);
26200092b20dSBorislav Petkov 	if (!fam_type)
2621395ae783SBorislav Petkov 		goto err_free;
2622395ae783SBorislav Petkov 
26237d6034d3SDoug Thompson 	ret = -ENODEV;
2624360b7f3cSBorislav Petkov 	err = reserve_mc_sibling_devs(pvt, fam_type->f1_id, fam_type->f3_id);
26257d6034d3SDoug Thompson 	if (err)
26267d6034d3SDoug Thompson 		goto err_free;
26277d6034d3SDoug Thompson 
2628360b7f3cSBorislav Petkov 	read_mc_regs(pvt);
26297d6034d3SDoug Thompson 
26307d6034d3SDoug Thompson 	/*
26317d6034d3SDoug Thompson 	 * We need to determine how many memory channels there are. Then use
26327d6034d3SDoug Thompson 	 * that information for calculating the size of the dynamic instance
2633360b7f3cSBorislav Petkov 	 * tables in the 'mci' structure.
26347d6034d3SDoug Thompson 	 */
2635360b7f3cSBorislav Petkov 	ret = -EINVAL;
26367d6034d3SDoug Thompson 	pvt->channel_count = pvt->ops->early_channel_count(pvt);
26377d6034d3SDoug Thompson 	if (pvt->channel_count < 0)
2638360b7f3cSBorislav Petkov 		goto err_siblings;
26397d6034d3SDoug Thompson 
26407d6034d3SDoug Thompson 	ret = -ENOMEM;
2641ab5a503cSMauro Carvalho Chehab 	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
2642ab5a503cSMauro Carvalho Chehab 	layers[0].size = pvt->csels[0].b_cnt;
2643ab5a503cSMauro Carvalho Chehab 	layers[0].is_virt_csrow = true;
2644ab5a503cSMauro Carvalho Chehab 	layers[1].type = EDAC_MC_LAYER_CHANNEL;
2645f0a56c48SBorislav Petkov 
2646f0a56c48SBorislav Petkov 	/*
2647f0a56c48SBorislav Petkov 	 * Always allocate two channels since we can have setups with DIMMs on
2648f0a56c48SBorislav Petkov 	 * only one channel. Also, this simplifies handling later for the price
2649f0a56c48SBorislav Petkov 	 * of a couple of KBs tops.
2650f0a56c48SBorislav Petkov 	 */
2651f0a56c48SBorislav Petkov 	layers[1].size = 2;
2652ab5a503cSMauro Carvalho Chehab 	layers[1].is_virt_csrow = false;
2653f0a56c48SBorislav Petkov 
2654ca0907b9SMauro Carvalho Chehab 	mci = edac_mc_alloc(nid, ARRAY_SIZE(layers), layers, 0);
26557d6034d3SDoug Thompson 	if (!mci)
2656360b7f3cSBorislav Petkov 		goto err_siblings;
26577d6034d3SDoug Thompson 
26587d6034d3SDoug Thompson 	mci->pvt_info = pvt;
2659fd687502SMauro Carvalho Chehab 	mci->pdev = &pvt->F2->dev;
26607d6034d3SDoug Thompson 
2661df71a053SBorislav Petkov 	setup_mci_misc_attrs(mci, fam_type);
2662360b7f3cSBorislav Petkov 
2663360b7f3cSBorislav Petkov 	if (init_csrows(mci))
26647d6034d3SDoug Thompson 		mci->edac_cap = EDAC_FLAG_NONE;
26657d6034d3SDoug Thompson 
26667d6034d3SDoug Thompson 	ret = -ENODEV;
26677d6034d3SDoug Thompson 	if (edac_mc_add_mc(mci)) {
2668956b9ba1SJoe Perches 		edac_dbg(1, "failed edac_mc_add_mc()\n");
26697d6034d3SDoug Thompson 		goto err_add_mc;
26707d6034d3SDoug Thompson 	}
2671c5608759SMauro Carvalho Chehab 	if (set_mc_sysfs_attrs(mci)) {
2672956b9ba1SJoe Perches 		edac_dbg(1, "failed edac_mc_add_mc()\n");
2673c5608759SMauro Carvalho Chehab 		goto err_add_sysfs;
2674c5608759SMauro Carvalho Chehab 	}
26757d6034d3SDoug Thompson 
2676549d042dSBorislav Petkov 	/* register stuff with EDAC MCE */
2677549d042dSBorislav Petkov 	if (report_gart_errors)
2678549d042dSBorislav Petkov 		amd_report_gart_errors(true);
2679549d042dSBorislav Petkov 
2680549d042dSBorislav Petkov 	amd_register_ecc_decoder(amd64_decode_bus_error);
2681549d042dSBorislav Petkov 
2682360b7f3cSBorislav Petkov 	mcis[nid] = mci;
2683360b7f3cSBorislav Petkov 
2684360b7f3cSBorislav Petkov 	atomic_inc(&drv_instances);
2685360b7f3cSBorislav Petkov 
26867d6034d3SDoug Thompson 	return 0;
26877d6034d3SDoug Thompson 
2688c5608759SMauro Carvalho Chehab err_add_sysfs:
2689c5608759SMauro Carvalho Chehab 	edac_mc_del_mc(mci->pdev);
26907d6034d3SDoug Thompson err_add_mc:
26917d6034d3SDoug Thompson 	edac_mc_free(mci);
26927d6034d3SDoug Thompson 
2693360b7f3cSBorislav Petkov err_siblings:
2694360b7f3cSBorislav Petkov 	free_mc_sibling_devs(pvt);
26957d6034d3SDoug Thompson 
2696360b7f3cSBorislav Petkov err_free:
2697360b7f3cSBorislav Petkov 	kfree(pvt);
26987d6034d3SDoug Thompson 
2699360b7f3cSBorislav Petkov err_ret:
27007d6034d3SDoug Thompson 	return ret;
27017d6034d3SDoug Thompson }
27027d6034d3SDoug Thompson 
27039b3c6e85SGreg Kroah-Hartman static int amd64_probe_one_instance(struct pci_dev *pdev,
27047d6034d3SDoug Thompson 				    const struct pci_device_id *mc_type)
27057d6034d3SDoug Thompson {
2706772c3ff3SDaniel J Blueman 	u16 nid = amd_get_node_id(pdev);
27072299ef71SBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
2708ae7bb7c6SBorislav Petkov 	struct ecc_settings *s;
27092299ef71SBorislav Petkov 	int ret = 0;
27107d6034d3SDoug Thompson 
27117d6034d3SDoug Thompson 	ret = pci_enable_device(pdev);
2712b8cfa02fSBorislav Petkov 	if (ret < 0) {
2713956b9ba1SJoe Perches 		edac_dbg(0, "ret=%d\n", ret);
2714b8cfa02fSBorislav Petkov 		return -EIO;
2715b8cfa02fSBorislav Petkov 	}
2716b8cfa02fSBorislav Petkov 
2717ae7bb7c6SBorislav Petkov 	ret = -ENOMEM;
2718ae7bb7c6SBorislav Petkov 	s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL);
2719ae7bb7c6SBorislav Petkov 	if (!s)
27202299ef71SBorislav Petkov 		goto err_out;
2721ae7bb7c6SBorislav Petkov 
2722ae7bb7c6SBorislav Petkov 	ecc_stngs[nid] = s;
2723ae7bb7c6SBorislav Petkov 
27242299ef71SBorislav Petkov 	if (!ecc_enabled(F3, nid)) {
27252299ef71SBorislav Petkov 		ret = -ENODEV;
27262299ef71SBorislav Petkov 
27272299ef71SBorislav Petkov 		if (!ecc_enable_override)
27282299ef71SBorislav Petkov 			goto err_enable;
27292299ef71SBorislav Petkov 
27302299ef71SBorislav Petkov 		amd64_warn("Forcing ECC on!\n");
27312299ef71SBorislav Petkov 
27322299ef71SBorislav Petkov 		if (!enable_ecc_error_reporting(s, nid, F3))
27332299ef71SBorislav Petkov 			goto err_enable;
27342299ef71SBorislav Petkov 	}
27352299ef71SBorislav Petkov 
27362299ef71SBorislav Petkov 	ret = amd64_init_one_instance(pdev);
2737360b7f3cSBorislav Petkov 	if (ret < 0) {
2738ae7bb7c6SBorislav Petkov 		amd64_err("Error probing instance: %d\n", nid);
2739360b7f3cSBorislav Petkov 		restore_ecc_error_reporting(s, nid, F3);
2740360b7f3cSBorislav Petkov 	}
27417d6034d3SDoug Thompson 
27427d6034d3SDoug Thompson 	return ret;
27432299ef71SBorislav Petkov 
27442299ef71SBorislav Petkov err_enable:
27452299ef71SBorislav Petkov 	kfree(s);
27462299ef71SBorislav Petkov 	ecc_stngs[nid] = NULL;
27472299ef71SBorislav Petkov 
27482299ef71SBorislav Petkov err_out:
27492299ef71SBorislav Petkov 	return ret;
27507d6034d3SDoug Thompson }
27517d6034d3SDoug Thompson 
27529b3c6e85SGreg Kroah-Hartman static void amd64_remove_one_instance(struct pci_dev *pdev)
27537d6034d3SDoug Thompson {
27547d6034d3SDoug Thompson 	struct mem_ctl_info *mci;
27557d6034d3SDoug Thompson 	struct amd64_pvt *pvt;
2756772c3ff3SDaniel J Blueman 	u16 nid = amd_get_node_id(pdev);
2757360b7f3cSBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
2758360b7f3cSBorislav Petkov 	struct ecc_settings *s = ecc_stngs[nid];
27597d6034d3SDoug Thompson 
2760c5608759SMauro Carvalho Chehab 	mci = find_mci_by_dev(&pdev->dev);
2761a4b4bedcSBorislav Petkov 	WARN_ON(!mci);
2762a4b4bedcSBorislav Petkov 
2763c5608759SMauro Carvalho Chehab 	del_mc_sysfs_attrs(mci);
27647d6034d3SDoug Thompson 	/* Remove from EDAC CORE tracking list */
27657d6034d3SDoug Thompson 	mci = edac_mc_del_mc(&pdev->dev);
27667d6034d3SDoug Thompson 	if (!mci)
27677d6034d3SDoug Thompson 		return;
27687d6034d3SDoug Thompson 
27697d6034d3SDoug Thompson 	pvt = mci->pvt_info;
27707d6034d3SDoug Thompson 
2771360b7f3cSBorislav Petkov 	restore_ecc_error_reporting(s, nid, F3);
27727d6034d3SDoug Thompson 
2773360b7f3cSBorislav Petkov 	free_mc_sibling_devs(pvt);
27747d6034d3SDoug Thompson 
2775549d042dSBorislav Petkov 	/* unregister from EDAC MCE */
2776549d042dSBorislav Petkov 	amd_report_gart_errors(false);
2777549d042dSBorislav Petkov 	amd_unregister_ecc_decoder(amd64_decode_bus_error);
2778549d042dSBorislav Petkov 
2779360b7f3cSBorislav Petkov 	kfree(ecc_stngs[nid]);
2780360b7f3cSBorislav Petkov 	ecc_stngs[nid] = NULL;
2781ae7bb7c6SBorislav Petkov 
27827d6034d3SDoug Thompson 	/* Free the EDAC CORE resources */
27838f68ed97SBorislav Petkov 	mci->pvt_info = NULL;
2784360b7f3cSBorislav Petkov 	mcis[nid] = NULL;
27858f68ed97SBorislav Petkov 
27868f68ed97SBorislav Petkov 	kfree(pvt);
27877d6034d3SDoug Thompson 	edac_mc_free(mci);
27887d6034d3SDoug Thompson }
27897d6034d3SDoug Thompson 
27907d6034d3SDoug Thompson /*
27917d6034d3SDoug Thompson  * This table is part of the interface for loading drivers for PCI devices. The
27927d6034d3SDoug Thompson  * PCI core identifies what devices are on a system during boot, and then
27937d6034d3SDoug Thompson  * inquiry this table to see if this driver is for a given device found.
27947d6034d3SDoug Thompson  */
279536c46f31SLionel Debroux static DEFINE_PCI_DEVICE_TABLE(amd64_pci_table) = {
27967d6034d3SDoug Thompson 	{
27977d6034d3SDoug Thompson 		.vendor		= PCI_VENDOR_ID_AMD,
27987d6034d3SDoug Thompson 		.device		= PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
27997d6034d3SDoug Thompson 		.subvendor	= PCI_ANY_ID,
28007d6034d3SDoug Thompson 		.subdevice	= PCI_ANY_ID,
28017d6034d3SDoug Thompson 		.class		= 0,
28027d6034d3SDoug Thompson 		.class_mask	= 0,
28037d6034d3SDoug Thompson 	},
28047d6034d3SDoug Thompson 	{
28057d6034d3SDoug Thompson 		.vendor		= PCI_VENDOR_ID_AMD,
28067d6034d3SDoug Thompson 		.device		= PCI_DEVICE_ID_AMD_10H_NB_DRAM,
28077d6034d3SDoug Thompson 		.subvendor	= PCI_ANY_ID,
28087d6034d3SDoug Thompson 		.subdevice	= PCI_ANY_ID,
28097d6034d3SDoug Thompson 		.class		= 0,
28107d6034d3SDoug Thompson 		.class_mask	= 0,
28117d6034d3SDoug Thompson 	},
2812df71a053SBorislav Petkov 	{
2813df71a053SBorislav Petkov 		.vendor		= PCI_VENDOR_ID_AMD,
2814df71a053SBorislav Petkov 		.device		= PCI_DEVICE_ID_AMD_15H_NB_F2,
2815df71a053SBorislav Petkov 		.subvendor	= PCI_ANY_ID,
2816df71a053SBorislav Petkov 		.subdevice	= PCI_ANY_ID,
2817df71a053SBorislav Petkov 		.class		= 0,
2818df71a053SBorislav Petkov 		.class_mask	= 0,
2819df71a053SBorislav Petkov 	},
282094c1acf2SAravind Gopalakrishnan 	{
282194c1acf2SAravind Gopalakrishnan 		.vendor		= PCI_VENDOR_ID_AMD,
282218b94f66SAravind Gopalakrishnan 		.device		= PCI_DEVICE_ID_AMD_15H_M30H_NB_F2,
282318b94f66SAravind Gopalakrishnan 		.subvendor	= PCI_ANY_ID,
282418b94f66SAravind Gopalakrishnan 		.subdevice	= PCI_ANY_ID,
282518b94f66SAravind Gopalakrishnan 		.class		= 0,
282618b94f66SAravind Gopalakrishnan 		.class_mask	= 0,
282718b94f66SAravind Gopalakrishnan 	},
282818b94f66SAravind Gopalakrishnan 	{
282918b94f66SAravind Gopalakrishnan 		.vendor		= PCI_VENDOR_ID_AMD,
283094c1acf2SAravind Gopalakrishnan 		.device		= PCI_DEVICE_ID_AMD_16H_NB_F2,
283194c1acf2SAravind Gopalakrishnan 		.subvendor	= PCI_ANY_ID,
283294c1acf2SAravind Gopalakrishnan 		.subdevice	= PCI_ANY_ID,
283394c1acf2SAravind Gopalakrishnan 		.class		= 0,
283494c1acf2SAravind Gopalakrishnan 		.class_mask	= 0,
283594c1acf2SAravind Gopalakrishnan 	},
2836df71a053SBorislav Petkov 
28377d6034d3SDoug Thompson 	{0, }
28387d6034d3SDoug Thompson };
28397d6034d3SDoug Thompson MODULE_DEVICE_TABLE(pci, amd64_pci_table);
28407d6034d3SDoug Thompson 
28417d6034d3SDoug Thompson static struct pci_driver amd64_pci_driver = {
28427d6034d3SDoug Thompson 	.name		= EDAC_MOD_STR,
28432299ef71SBorislav Petkov 	.probe		= amd64_probe_one_instance,
28449b3c6e85SGreg Kroah-Hartman 	.remove		= amd64_remove_one_instance,
28457d6034d3SDoug Thompson 	.id_table	= amd64_pci_table,
28467d6034d3SDoug Thompson };
28477d6034d3SDoug Thompson 
2848360b7f3cSBorislav Petkov static void setup_pci_device(void)
28497d6034d3SDoug Thompson {
28507d6034d3SDoug Thompson 	struct mem_ctl_info *mci;
28517d6034d3SDoug Thompson 	struct amd64_pvt *pvt;
28527d6034d3SDoug Thompson 
28537d6034d3SDoug Thompson 	if (amd64_ctl_pci)
28547d6034d3SDoug Thompson 		return;
28557d6034d3SDoug Thompson 
2856cc4d8860SBorislav Petkov 	mci = mcis[0];
28577d6034d3SDoug Thompson 	if (mci) {
28587d6034d3SDoug Thompson 
28597d6034d3SDoug Thompson 		pvt = mci->pvt_info;
28607d6034d3SDoug Thompson 		amd64_ctl_pci =
28618d5b5d9cSBorislav Petkov 			edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR);
28627d6034d3SDoug Thompson 
28637d6034d3SDoug Thompson 		if (!amd64_ctl_pci) {
28647d6034d3SDoug Thompson 			pr_warning("%s(): Unable to create PCI control\n",
28657d6034d3SDoug Thompson 				   __func__);
28667d6034d3SDoug Thompson 
28677d6034d3SDoug Thompson 			pr_warning("%s(): PCI error report via EDAC not set\n",
28687d6034d3SDoug Thompson 				   __func__);
28697d6034d3SDoug Thompson 			}
28707d6034d3SDoug Thompson 	}
28717d6034d3SDoug Thompson }
28727d6034d3SDoug Thompson 
28737d6034d3SDoug Thompson static int __init amd64_edac_init(void)
28747d6034d3SDoug Thompson {
2875360b7f3cSBorislav Petkov 	int err = -ENODEV;
28767d6034d3SDoug Thompson 
2877df71a053SBorislav Petkov 	printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION);
28787d6034d3SDoug Thompson 
28797d6034d3SDoug Thompson 	opstate_init();
28807d6034d3SDoug Thompson 
28819653a5c7SHans Rosenfeld 	if (amd_cache_northbridges() < 0)
288256b34b91SBorislav Petkov 		goto err_ret;
28837d6034d3SDoug Thompson 
2884cc4d8860SBorislav Petkov 	err = -ENOMEM;
2885cc4d8860SBorislav Petkov 	mcis	  = kzalloc(amd_nb_num() * sizeof(mcis[0]), GFP_KERNEL);
2886ae7bb7c6SBorislav Petkov 	ecc_stngs = kzalloc(amd_nb_num() * sizeof(ecc_stngs[0]), GFP_KERNEL);
2887360b7f3cSBorislav Petkov 	if (!(mcis && ecc_stngs))
2888a9f0fbe2SBorislav Petkov 		goto err_free;
2889cc4d8860SBorislav Petkov 
289050542251SBorislav Petkov 	msrs = msrs_alloc();
289156b34b91SBorislav Petkov 	if (!msrs)
2892360b7f3cSBorislav Petkov 		goto err_free;
289350542251SBorislav Petkov 
28947d6034d3SDoug Thompson 	err = pci_register_driver(&amd64_pci_driver);
28957d6034d3SDoug Thompson 	if (err)
289656b34b91SBorislav Petkov 		goto err_pci;
28977d6034d3SDoug Thompson 
289856b34b91SBorislav Petkov 	err = -ENODEV;
2899360b7f3cSBorislav Petkov 	if (!atomic_read(&drv_instances))
2900360b7f3cSBorislav Petkov 		goto err_no_instances;
29017d6034d3SDoug Thompson 
2902360b7f3cSBorislav Petkov 	setup_pci_device();
29037d6034d3SDoug Thompson 	return 0;
29047d6034d3SDoug Thompson 
2905360b7f3cSBorislav Petkov err_no_instances:
29067d6034d3SDoug Thompson 	pci_unregister_driver(&amd64_pci_driver);
2907cc4d8860SBorislav Petkov 
290856b34b91SBorislav Petkov err_pci:
290956b34b91SBorislav Petkov 	msrs_free(msrs);
291056b34b91SBorislav Petkov 	msrs = NULL;
2911cc4d8860SBorislav Petkov 
2912360b7f3cSBorislav Petkov err_free:
2913360b7f3cSBorislav Petkov 	kfree(mcis);
2914360b7f3cSBorislav Petkov 	mcis = NULL;
2915360b7f3cSBorislav Petkov 
2916360b7f3cSBorislav Petkov 	kfree(ecc_stngs);
2917360b7f3cSBorislav Petkov 	ecc_stngs = NULL;
2918360b7f3cSBorislav Petkov 
291956b34b91SBorislav Petkov err_ret:
29207d6034d3SDoug Thompson 	return err;
29217d6034d3SDoug Thompson }
29227d6034d3SDoug Thompson 
29237d6034d3SDoug Thompson static void __exit amd64_edac_exit(void)
29247d6034d3SDoug Thompson {
29257d6034d3SDoug Thompson 	if (amd64_ctl_pci)
29267d6034d3SDoug Thompson 		edac_pci_release_generic_ctl(amd64_ctl_pci);
29277d6034d3SDoug Thompson 
29287d6034d3SDoug Thompson 	pci_unregister_driver(&amd64_pci_driver);
292950542251SBorislav Petkov 
2930ae7bb7c6SBorislav Petkov 	kfree(ecc_stngs);
2931ae7bb7c6SBorislav Petkov 	ecc_stngs = NULL;
2932ae7bb7c6SBorislav Petkov 
2933cc4d8860SBorislav Petkov 	kfree(mcis);
2934cc4d8860SBorislav Petkov 	mcis = NULL;
2935cc4d8860SBorislav Petkov 
293650542251SBorislav Petkov 	msrs_free(msrs);
293750542251SBorislav Petkov 	msrs = NULL;
29387d6034d3SDoug Thompson }
29397d6034d3SDoug Thompson 
29407d6034d3SDoug Thompson module_init(amd64_edac_init);
29417d6034d3SDoug Thompson module_exit(amd64_edac_exit);
29427d6034d3SDoug Thompson 
29437d6034d3SDoug Thompson MODULE_LICENSE("GPL");
29447d6034d3SDoug Thompson MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, "
29457d6034d3SDoug Thompson 		"Dave Peterson, Thayne Harbaugh");
29467d6034d3SDoug Thompson MODULE_DESCRIPTION("MC support for AMD64 memory controllers - "
29477d6034d3SDoug Thompson 		EDAC_AMD64_VERSION);
29487d6034d3SDoug Thompson 
29497d6034d3SDoug Thompson module_param(edac_op_state, int, 0444);
29507d6034d3SDoug Thompson MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
2951