xref: /openbmc/linux/drivers/edac/amd64_edac.c (revision 168bfeef)
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  */
3439094443SBorislav Petkov 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 
63b2b0c605SBorislav Petkov static 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  *
101b2b0c605SBorislav Petkov  */
102b2b0c605SBorislav Petkov static int k8_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
103b2b0c605SBorislav Petkov 			       const char *func)
104b2b0c605SBorislav Petkov {
105b2b0c605SBorislav Petkov 	if (addr >= 0x100)
106b2b0c605SBorislav Petkov 		return -EINVAL;
107b2b0c605SBorislav Petkov 
108b2b0c605SBorislav Petkov 	return __amd64_read_pci_cfg_dword(pvt->F2, addr, val, func);
109b2b0c605SBorislav Petkov }
110b2b0c605SBorislav Petkov 
111b2b0c605SBorislav Petkov static int f10_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
112b2b0c605SBorislav Petkov 				 const char *func)
113b2b0c605SBorislav Petkov {
114b2b0c605SBorislav Petkov 	return __amd64_read_pci_cfg_dword(pvt->F2, addr, val, func);
115b2b0c605SBorislav Petkov }
116b2b0c605SBorislav Petkov 
11773ba8593SBorislav Petkov /*
11873ba8593SBorislav Petkov  * Select DCT to which PCI cfg accesses are routed
11973ba8593SBorislav Petkov  */
12073ba8593SBorislav Petkov static void f15h_select_dct(struct amd64_pvt *pvt, u8 dct)
12173ba8593SBorislav Petkov {
12273ba8593SBorislav Petkov 	u32 reg = 0;
12373ba8593SBorislav Petkov 
12473ba8593SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, &reg);
12573ba8593SBorislav Petkov 	reg &= 0xfffffffe;
12673ba8593SBorislav Petkov 	reg |= dct;
12773ba8593SBorislav Petkov 	amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg);
12873ba8593SBorislav Petkov }
12973ba8593SBorislav Petkov 
130b2b0c605SBorislav Petkov static int f15_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
131b2b0c605SBorislav Petkov 				 const char *func)
132b2b0c605SBorislav Petkov {
133b2b0c605SBorislav Petkov 	u8 dct  = 0;
134b2b0c605SBorislav Petkov 
135b2b0c605SBorislav Petkov 	if (addr >= 0x140 && addr <= 0x1a0) {
136b2b0c605SBorislav Petkov 		dct   = 1;
137b2b0c605SBorislav Petkov 		addr -= 0x100;
138b2b0c605SBorislav Petkov 	}
139b2b0c605SBorislav Petkov 
14073ba8593SBorislav Petkov 	f15h_select_dct(pvt, dct);
141b2b0c605SBorislav Petkov 
142b2b0c605SBorislav Petkov 	return __amd64_read_pci_cfg_dword(pvt->F2, addr, val, func);
143b2b0c605SBorislav Petkov }
144b2b0c605SBorislav Petkov 
145b70ef010SBorislav Petkov /*
1462bc65418SDoug Thompson  * Memory scrubber control interface. For K8, memory scrubbing is handled by
1472bc65418SDoug Thompson  * hardware and can involve L2 cache, dcache as well as the main memory. With
1482bc65418SDoug Thompson  * F10, this is extended to L3 cache scrubbing on CPU models sporting that
1492bc65418SDoug Thompson  * functionality.
1502bc65418SDoug Thompson  *
1512bc65418SDoug Thompson  * This causes the "units" for the scrubbing speed to vary from 64 byte blocks
1522bc65418SDoug Thompson  * (dram) over to cache lines. This is nasty, so we will use bandwidth in
1532bc65418SDoug Thompson  * bytes/sec for the setting.
1542bc65418SDoug Thompson  *
1552bc65418SDoug Thompson  * Currently, we only do dram scrubbing. If the scrubbing is done in software on
1562bc65418SDoug Thompson  * other archs, we might not have access to the caches directly.
1572bc65418SDoug Thompson  */
1582bc65418SDoug Thompson 
1592bc65418SDoug Thompson /*
1602bc65418SDoug Thompson  * scan the scrub rate mapping table for a close or matching bandwidth value to
1612bc65418SDoug Thompson  * issue. If requested is too big, then use last maximum value found.
1622bc65418SDoug Thompson  */
163395ae783SBorislav Petkov static int __amd64_set_scrub_rate(struct pci_dev *ctl, u32 new_bw, u32 min_rate)
1642bc65418SDoug Thompson {
1652bc65418SDoug Thompson 	u32 scrubval;
1662bc65418SDoug Thompson 	int i;
1672bc65418SDoug Thompson 
1682bc65418SDoug Thompson 	/*
1692bc65418SDoug Thompson 	 * map the configured rate (new_bw) to a value specific to the AMD64
1702bc65418SDoug Thompson 	 * memory controller and apply to register. Search for the first
1712bc65418SDoug Thompson 	 * bandwidth entry that is greater or equal than the setting requested
1722bc65418SDoug Thompson 	 * and program that. If at last entry, turn off DRAM scrubbing.
173168bfeefSAndrew Morton 	 *
174168bfeefSAndrew Morton 	 * If no suitable bandwidth is found, turn off DRAM scrubbing entirely
175168bfeefSAndrew Morton 	 * by falling back to the last element in scrubrates[].
1762bc65418SDoug Thompson 	 */
177168bfeefSAndrew Morton 	for (i = 0; i < ARRAY_SIZE(scrubrates) - 1; i++) {
1782bc65418SDoug Thompson 		/*
1792bc65418SDoug Thompson 		 * skip scrub rates which aren't recommended
1802bc65418SDoug Thompson 		 * (see F10 BKDG, F3x58)
1812bc65418SDoug Thompson 		 */
182395ae783SBorislav Petkov 		if (scrubrates[i].scrubval < min_rate)
1832bc65418SDoug Thompson 			continue;
1842bc65418SDoug Thompson 
1852bc65418SDoug Thompson 		if (scrubrates[i].bandwidth <= new_bw)
1862bc65418SDoug Thompson 			break;
1872bc65418SDoug Thompson 	}
1882bc65418SDoug Thompson 
1892bc65418SDoug Thompson 	scrubval = scrubrates[i].scrubval;
1902bc65418SDoug Thompson 
1915980bb9cSBorislav Petkov 	pci_write_bits32(ctl, SCRCTRL, scrubval, 0x001F);
1922bc65418SDoug Thompson 
19339094443SBorislav Petkov 	if (scrubval)
19439094443SBorislav Petkov 		return scrubrates[i].bandwidth;
19539094443SBorislav Petkov 
1962bc65418SDoug Thompson 	return 0;
1972bc65418SDoug Thompson }
1982bc65418SDoug Thompson 
199395ae783SBorislav Petkov static int amd64_set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
2002bc65418SDoug Thompson {
2012bc65418SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
20287b3e0e6SBorislav Petkov 	u32 min_scrubrate = 0x5;
2032bc65418SDoug Thompson 
20487b3e0e6SBorislav Petkov 	if (boot_cpu_data.x86 == 0xf)
20587b3e0e6SBorislav Petkov 		min_scrubrate = 0x0;
20687b3e0e6SBorislav Petkov 
20773ba8593SBorislav Petkov 	/* F15h Erratum #505 */
20873ba8593SBorislav Petkov 	if (boot_cpu_data.x86 == 0x15)
20973ba8593SBorislav Petkov 		f15h_select_dct(pvt, 0);
21073ba8593SBorislav Petkov 
21187b3e0e6SBorislav Petkov 	return __amd64_set_scrub_rate(pvt->F3, bw, min_scrubrate);
2122bc65418SDoug Thompson }
2132bc65418SDoug Thompson 
21439094443SBorislav Petkov static int amd64_get_scrub_rate(struct mem_ctl_info *mci)
2152bc65418SDoug Thompson {
2162bc65418SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
2172bc65418SDoug Thompson 	u32 scrubval = 0;
21839094443SBorislav Petkov 	int i, retval = -EINVAL;
2192bc65418SDoug Thompson 
22073ba8593SBorislav Petkov 	/* F15h Erratum #505 */
22173ba8593SBorislav Petkov 	if (boot_cpu_data.x86 == 0x15)
22273ba8593SBorislav Petkov 		f15h_select_dct(pvt, 0);
22373ba8593SBorislav Petkov 
2245980bb9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
2252bc65418SDoug Thompson 
2262bc65418SDoug Thompson 	scrubval = scrubval & 0x001F;
2272bc65418SDoug Thompson 
228926311fdSRoel Kluin 	for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
2292bc65418SDoug Thompson 		if (scrubrates[i].scrubval == scrubval) {
23039094443SBorislav Petkov 			retval = scrubrates[i].bandwidth;
2312bc65418SDoug Thompson 			break;
2322bc65418SDoug Thompson 		}
2332bc65418SDoug Thompson 	}
23439094443SBorislav Petkov 	return retval;
2352bc65418SDoug Thompson }
2362bc65418SDoug Thompson 
2376775763aSDoug Thompson /*
2387f19bf75SBorislav Petkov  * returns true if the SysAddr given by sys_addr matches the
2397f19bf75SBorislav Petkov  * DRAM base/limit associated with node_id
2406775763aSDoug Thompson  */
241b487c33eSBorislav Petkov static bool amd64_base_limit_match(struct amd64_pvt *pvt, u64 sys_addr,
242b487c33eSBorislav Petkov 				   unsigned nid)
2436775763aSDoug Thompson {
2447f19bf75SBorislav Petkov 	u64 addr;
2456775763aSDoug Thompson 
2466775763aSDoug Thompson 	/* The K8 treats this as a 40-bit value.  However, bits 63-40 will be
2476775763aSDoug Thompson 	 * all ones if the most significant implemented address bit is 1.
2486775763aSDoug Thompson 	 * Here we discard bits 63-40.  See section 3.4.2 of AMD publication
2496775763aSDoug Thompson 	 * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1
2506775763aSDoug Thompson 	 * Application Programming.
2516775763aSDoug Thompson 	 */
2526775763aSDoug Thompson 	addr = sys_addr & 0x000000ffffffffffull;
2536775763aSDoug Thompson 
2547f19bf75SBorislav Petkov 	return ((addr >= get_dram_base(pvt, nid)) &&
2557f19bf75SBorislav Petkov 		(addr <= get_dram_limit(pvt, nid)));
2566775763aSDoug Thompson }
2576775763aSDoug Thompson 
2586775763aSDoug Thompson /*
2596775763aSDoug Thompson  * Attempt to map a SysAddr to a node. On success, return a pointer to the
2606775763aSDoug Thompson  * mem_ctl_info structure for the node that the SysAddr maps to.
2616775763aSDoug Thompson  *
2626775763aSDoug Thompson  * On failure, return NULL.
2636775763aSDoug Thompson  */
2646775763aSDoug Thompson static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci,
2656775763aSDoug Thompson 						u64 sys_addr)
2666775763aSDoug Thompson {
2676775763aSDoug Thompson 	struct amd64_pvt *pvt;
268b487c33eSBorislav Petkov 	unsigned node_id;
2696775763aSDoug Thompson 	u32 intlv_en, bits;
2706775763aSDoug Thompson 
2716775763aSDoug Thompson 	/*
2726775763aSDoug Thompson 	 * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section
2736775763aSDoug Thompson 	 * 3.4.4.2) registers to map the SysAddr to a node ID.
2746775763aSDoug Thompson 	 */
2756775763aSDoug Thompson 	pvt = mci->pvt_info;
2766775763aSDoug Thompson 
2776775763aSDoug Thompson 	/*
2786775763aSDoug Thompson 	 * The value of this field should be the same for all DRAM Base
2796775763aSDoug Thompson 	 * registers.  Therefore we arbitrarily choose to read it from the
2806775763aSDoug Thompson 	 * register for node 0.
2816775763aSDoug Thompson 	 */
2827f19bf75SBorislav Petkov 	intlv_en = dram_intlv_en(pvt, 0);
2836775763aSDoug Thompson 
2846775763aSDoug Thompson 	if (intlv_en == 0) {
2857f19bf75SBorislav Petkov 		for (node_id = 0; node_id < DRAM_RANGES; node_id++) {
2866775763aSDoug Thompson 			if (amd64_base_limit_match(pvt, sys_addr, node_id))
2876775763aSDoug Thompson 				goto found;
2886775763aSDoug Thompson 		}
2898edc5445SBorislav Petkov 		goto err_no_match;
2908edc5445SBorislav Petkov 	}
2916775763aSDoug Thompson 
29272f158feSBorislav Petkov 	if (unlikely((intlv_en != 0x01) &&
29372f158feSBorislav Petkov 		     (intlv_en != 0x03) &&
29472f158feSBorislav Petkov 		     (intlv_en != 0x07))) {
29524f9a7feSBorislav Petkov 		amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en);
2966775763aSDoug Thompson 		return NULL;
2976775763aSDoug Thompson 	}
2986775763aSDoug Thompson 
2996775763aSDoug Thompson 	bits = (((u32) sys_addr) >> 12) & intlv_en;
3006775763aSDoug Thompson 
3016775763aSDoug Thompson 	for (node_id = 0; ; ) {
3027f19bf75SBorislav Petkov 		if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits)
3036775763aSDoug Thompson 			break;	/* intlv_sel field matches */
3046775763aSDoug Thompson 
3057f19bf75SBorislav Petkov 		if (++node_id >= DRAM_RANGES)
3066775763aSDoug Thompson 			goto err_no_match;
3076775763aSDoug Thompson 	}
3086775763aSDoug Thompson 
3096775763aSDoug Thompson 	/* sanity test for sys_addr */
3106775763aSDoug Thompson 	if (unlikely(!amd64_base_limit_match(pvt, sys_addr, node_id))) {
31124f9a7feSBorislav Petkov 		amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address"
31224f9a7feSBorislav Petkov 			   "range for node %d with node interleaving enabled.\n",
3138edc5445SBorislav Petkov 			   __func__, sys_addr, node_id);
3146775763aSDoug Thompson 		return NULL;
3156775763aSDoug Thompson 	}
3166775763aSDoug Thompson 
3176775763aSDoug Thompson found:
318b487c33eSBorislav Petkov 	return edac_mc_find((int)node_id);
3196775763aSDoug Thompson 
3206775763aSDoug Thompson err_no_match:
321956b9ba1SJoe Perches 	edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n",
3226775763aSDoug Thompson 		 (unsigned long)sys_addr);
3236775763aSDoug Thompson 
3246775763aSDoug Thompson 	return NULL;
3256775763aSDoug Thompson }
326e2ce7255SDoug Thompson 
327e2ce7255SDoug Thompson /*
32811c75eadSBorislav Petkov  * compute the CS base address of the @csrow on the DRAM controller @dct.
32911c75eadSBorislav Petkov  * For details see F2x[5C:40] in the processor's BKDG
330e2ce7255SDoug Thompson  */
33111c75eadSBorislav Petkov static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct,
33211c75eadSBorislav Petkov 				 u64 *base, u64 *mask)
333e2ce7255SDoug Thompson {
33411c75eadSBorislav Petkov 	u64 csbase, csmask, base_bits, mask_bits;
33511c75eadSBorislav Petkov 	u8 addr_shift;
33611c75eadSBorislav Petkov 
33711c75eadSBorislav Petkov 	if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_F) {
33811c75eadSBorislav Petkov 		csbase		= pvt->csels[dct].csbases[csrow];
33911c75eadSBorislav Petkov 		csmask		= pvt->csels[dct].csmasks[csrow];
34011c75eadSBorislav Petkov 		base_bits	= GENMASK(21, 31) | GENMASK(9, 15);
34111c75eadSBorislav Petkov 		mask_bits	= GENMASK(21, 29) | GENMASK(9, 15);
34211c75eadSBorislav Petkov 		addr_shift	= 4;
34311c75eadSBorislav Petkov 	} else {
34411c75eadSBorislav Petkov 		csbase		= pvt->csels[dct].csbases[csrow];
34511c75eadSBorislav Petkov 		csmask		= pvt->csels[dct].csmasks[csrow >> 1];
34611c75eadSBorislav Petkov 		addr_shift	= 8;
34711c75eadSBorislav Petkov 
34811c75eadSBorislav Petkov 		if (boot_cpu_data.x86 == 0x15)
34911c75eadSBorislav Petkov 			base_bits = mask_bits = GENMASK(19,30) | GENMASK(5,13);
35011c75eadSBorislav Petkov 		else
35111c75eadSBorislav Petkov 			base_bits = mask_bits = GENMASK(19,28) | GENMASK(5,13);
352e2ce7255SDoug Thompson 	}
353e2ce7255SDoug Thompson 
35411c75eadSBorislav Petkov 	*base  = (csbase & base_bits) << addr_shift;
355e2ce7255SDoug Thompson 
35611c75eadSBorislav Petkov 	*mask  = ~0ULL;
35711c75eadSBorislav Petkov 	/* poke holes for the csmask */
35811c75eadSBorislav Petkov 	*mask &= ~(mask_bits << addr_shift);
35911c75eadSBorislav Petkov 	/* OR them in */
36011c75eadSBorislav Petkov 	*mask |= (csmask & mask_bits) << addr_shift;
361e2ce7255SDoug Thompson }
362e2ce7255SDoug Thompson 
36311c75eadSBorislav Petkov #define for_each_chip_select(i, dct, pvt) \
36411c75eadSBorislav Petkov 	for (i = 0; i < pvt->csels[dct].b_cnt; i++)
36511c75eadSBorislav Petkov 
366614ec9d8SBorislav Petkov #define chip_select_base(i, dct, pvt) \
367614ec9d8SBorislav Petkov 	pvt->csels[dct].csbases[i]
368614ec9d8SBorislav Petkov 
36911c75eadSBorislav Petkov #define for_each_chip_select_mask(i, dct, pvt) \
37011c75eadSBorislav Petkov 	for (i = 0; i < pvt->csels[dct].m_cnt; i++)
37111c75eadSBorislav Petkov 
372e2ce7255SDoug Thompson /*
373e2ce7255SDoug Thompson  * @input_addr is an InputAddr associated with the node given by mci. Return the
374e2ce7255SDoug Thompson  * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr).
375e2ce7255SDoug Thompson  */
376e2ce7255SDoug Thompson static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr)
377e2ce7255SDoug Thompson {
378e2ce7255SDoug Thompson 	struct amd64_pvt *pvt;
379e2ce7255SDoug Thompson 	int csrow;
380e2ce7255SDoug Thompson 	u64 base, mask;
381e2ce7255SDoug Thompson 
382e2ce7255SDoug Thompson 	pvt = mci->pvt_info;
383e2ce7255SDoug Thompson 
38411c75eadSBorislav Petkov 	for_each_chip_select(csrow, 0, pvt) {
38511c75eadSBorislav Petkov 		if (!csrow_enabled(csrow, 0, pvt))
386e2ce7255SDoug Thompson 			continue;
387e2ce7255SDoug Thompson 
38811c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, csrow, 0, &base, &mask);
38911c75eadSBorislav Petkov 
39011c75eadSBorislav Petkov 		mask = ~mask;
391e2ce7255SDoug Thompson 
392e2ce7255SDoug Thompson 		if ((input_addr & mask) == (base & mask)) {
393956b9ba1SJoe Perches 			edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n",
394e2ce7255SDoug Thompson 				 (unsigned long)input_addr, csrow,
395e2ce7255SDoug Thompson 				 pvt->mc_node_id);
396e2ce7255SDoug Thompson 
397e2ce7255SDoug Thompson 			return csrow;
398e2ce7255SDoug Thompson 		}
399e2ce7255SDoug Thompson 	}
400956b9ba1SJoe Perches 	edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n",
401e2ce7255SDoug Thompson 		 (unsigned long)input_addr, pvt->mc_node_id);
402e2ce7255SDoug Thompson 
403e2ce7255SDoug Thompson 	return -1;
404e2ce7255SDoug Thompson }
405e2ce7255SDoug Thompson 
406e2ce7255SDoug Thompson /*
407e2ce7255SDoug Thompson  * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094)
408e2ce7255SDoug Thompson  * for the node represented by mci. Info is passed back in *hole_base,
409e2ce7255SDoug Thompson  * *hole_offset, and *hole_size.  Function returns 0 if info is valid or 1 if
410e2ce7255SDoug Thompson  * info is invalid. Info may be invalid for either of the following reasons:
411e2ce7255SDoug Thompson  *
412e2ce7255SDoug Thompson  * - The revision of the node is not E or greater.  In this case, the DRAM Hole
413e2ce7255SDoug Thompson  *   Address Register does not exist.
414e2ce7255SDoug Thompson  *
415e2ce7255SDoug Thompson  * - The DramHoleValid bit is cleared in the DRAM Hole Address Register,
416e2ce7255SDoug Thompson  *   indicating that its contents are not valid.
417e2ce7255SDoug Thompson  *
418e2ce7255SDoug Thompson  * The values passed back in *hole_base, *hole_offset, and *hole_size are
419e2ce7255SDoug Thompson  * complete 32-bit values despite the fact that the bitfields in the DHAR
420e2ce7255SDoug Thompson  * only represent bits 31-24 of the base and offset values.
421e2ce7255SDoug Thompson  */
422e2ce7255SDoug Thompson int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
423e2ce7255SDoug Thompson 			     u64 *hole_offset, u64 *hole_size)
424e2ce7255SDoug Thompson {
425e2ce7255SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
426e2ce7255SDoug Thompson 	u64 base;
427e2ce7255SDoug Thompson 
428e2ce7255SDoug Thompson 	/* only revE and later have the DRAM Hole Address Register */
4291433eb99SBorislav Petkov 	if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_E) {
430956b9ba1SJoe Perches 		edac_dbg(1, "  revision %d for node %d does not support DHAR\n",
431e2ce7255SDoug Thompson 			 pvt->ext_model, pvt->mc_node_id);
432e2ce7255SDoug Thompson 		return 1;
433e2ce7255SDoug Thompson 	}
434e2ce7255SDoug Thompson 
435bc21fa57SBorislav Petkov 	/* valid for Fam10h and above */
436c8e518d5SBorislav Petkov 	if (boot_cpu_data.x86 >= 0x10 && !dhar_mem_hoist_valid(pvt)) {
437956b9ba1SJoe Perches 		edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this system\n");
438e2ce7255SDoug Thompson 		return 1;
439e2ce7255SDoug Thompson 	}
440e2ce7255SDoug Thompson 
441c8e518d5SBorislav Petkov 	if (!dhar_valid(pvt)) {
442956b9ba1SJoe Perches 		edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this node %d\n",
443e2ce7255SDoug Thompson 			 pvt->mc_node_id);
444e2ce7255SDoug Thompson 		return 1;
445e2ce7255SDoug Thompson 	}
446e2ce7255SDoug Thompson 
447e2ce7255SDoug Thompson 	/* This node has Memory Hoisting */
448e2ce7255SDoug Thompson 
449e2ce7255SDoug Thompson 	/* +------------------+--------------------+--------------------+-----
450e2ce7255SDoug Thompson 	 * | memory           | DRAM hole          | relocated          |
451e2ce7255SDoug Thompson 	 * | [0, (x - 1)]     | [x, 0xffffffff]    | addresses from     |
452e2ce7255SDoug Thompson 	 * |                  |                    | DRAM hole          |
453e2ce7255SDoug Thompson 	 * |                  |                    | [0x100000000,      |
454e2ce7255SDoug Thompson 	 * |                  |                    |  (0x100000000+     |
455e2ce7255SDoug Thompson 	 * |                  |                    |   (0xffffffff-x))] |
456e2ce7255SDoug Thompson 	 * +------------------+--------------------+--------------------+-----
457e2ce7255SDoug Thompson 	 *
458e2ce7255SDoug Thompson 	 * Above is a diagram of physical memory showing the DRAM hole and the
459e2ce7255SDoug Thompson 	 * relocated addresses from the DRAM hole.  As shown, the DRAM hole
460e2ce7255SDoug Thompson 	 * starts at address x (the base address) and extends through address
461e2ce7255SDoug Thompson 	 * 0xffffffff.  The DRAM Hole Address Register (DHAR) relocates the
462e2ce7255SDoug Thompson 	 * addresses in the hole so that they start at 0x100000000.
463e2ce7255SDoug Thompson 	 */
464e2ce7255SDoug Thompson 
465bc21fa57SBorislav Petkov 	base = dhar_base(pvt);
466e2ce7255SDoug Thompson 
467e2ce7255SDoug Thompson 	*hole_base = base;
468e2ce7255SDoug Thompson 	*hole_size = (0x1ull << 32) - base;
469e2ce7255SDoug Thompson 
470e2ce7255SDoug Thompson 	if (boot_cpu_data.x86 > 0xf)
471bc21fa57SBorislav Petkov 		*hole_offset = f10_dhar_offset(pvt);
472e2ce7255SDoug Thompson 	else
473bc21fa57SBorislav Petkov 		*hole_offset = k8_dhar_offset(pvt);
474e2ce7255SDoug Thompson 
475956b9ba1SJoe Perches 	edac_dbg(1, "  DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
476e2ce7255SDoug Thompson 		 pvt->mc_node_id, (unsigned long)*hole_base,
477e2ce7255SDoug Thompson 		 (unsigned long)*hole_offset, (unsigned long)*hole_size);
478e2ce7255SDoug Thompson 
479e2ce7255SDoug Thompson 	return 0;
480e2ce7255SDoug Thompson }
481e2ce7255SDoug Thompson EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info);
482e2ce7255SDoug Thompson 
48393c2df58SDoug Thompson /*
48493c2df58SDoug Thompson  * Return the DramAddr that the SysAddr given by @sys_addr maps to.  It is
48593c2df58SDoug Thompson  * assumed that sys_addr maps to the node given by mci.
48693c2df58SDoug Thompson  *
48793c2df58SDoug Thompson  * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section
48893c2df58SDoug Thompson  * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a
48993c2df58SDoug Thompson  * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled,
49093c2df58SDoug Thompson  * then it is also involved in translating a SysAddr to a DramAddr. Sections
49193c2df58SDoug Thompson  * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting.
49293c2df58SDoug Thompson  * These parts of the documentation are unclear. I interpret them as follows:
49393c2df58SDoug Thompson  *
49493c2df58SDoug Thompson  * When node n receives a SysAddr, it processes the SysAddr as follows:
49593c2df58SDoug Thompson  *
49693c2df58SDoug Thompson  * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM
49793c2df58SDoug Thompson  *    Limit registers for node n. If the SysAddr is not within the range
49893c2df58SDoug Thompson  *    specified by the base and limit values, then node n ignores the Sysaddr
49993c2df58SDoug Thompson  *    (since it does not map to node n). Otherwise continue to step 2 below.
50093c2df58SDoug Thompson  *
50193c2df58SDoug Thompson  * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is
50293c2df58SDoug Thompson  *    disabled so skip to step 3 below. Otherwise see if the SysAddr is within
50393c2df58SDoug Thompson  *    the range of relocated addresses (starting at 0x100000000) from the DRAM
50493c2df58SDoug Thompson  *    hole. If not, skip to step 3 below. Else get the value of the
50593c2df58SDoug Thompson  *    DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the
50693c2df58SDoug Thompson  *    offset defined by this value from the SysAddr.
50793c2df58SDoug Thompson  *
50893c2df58SDoug Thompson  * 3. Obtain the base address for node n from the DRAMBase field of the DRAM
50993c2df58SDoug Thompson  *    Base register for node n. To obtain the DramAddr, subtract the base
51093c2df58SDoug Thompson  *    address from the SysAddr, as shown near the start of section 3.4.4 (p.70).
51193c2df58SDoug Thompson  */
51293c2df58SDoug Thompson static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
51393c2df58SDoug Thompson {
5147f19bf75SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
51593c2df58SDoug Thompson 	u64 dram_base, hole_base, hole_offset, hole_size, dram_addr;
51693c2df58SDoug Thompson 	int ret = 0;
51793c2df58SDoug Thompson 
5187f19bf75SBorislav Petkov 	dram_base = get_dram_base(pvt, pvt->mc_node_id);
51993c2df58SDoug Thompson 
52093c2df58SDoug Thompson 	ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
52193c2df58SDoug Thompson 				      &hole_size);
52293c2df58SDoug Thompson 	if (!ret) {
52393c2df58SDoug Thompson 		if ((sys_addr >= (1ull << 32)) &&
52493c2df58SDoug Thompson 		    (sys_addr < ((1ull << 32) + hole_size))) {
52593c2df58SDoug Thompson 			/* use DHAR to translate SysAddr to DramAddr */
52693c2df58SDoug Thompson 			dram_addr = sys_addr - hole_offset;
52793c2df58SDoug Thompson 
528956b9ba1SJoe Perches 			edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
52993c2df58SDoug Thompson 				 (unsigned long)sys_addr,
53093c2df58SDoug Thompson 				 (unsigned long)dram_addr);
53193c2df58SDoug Thompson 
53293c2df58SDoug Thompson 			return dram_addr;
53393c2df58SDoug Thompson 		}
53493c2df58SDoug Thompson 	}
53593c2df58SDoug Thompson 
53693c2df58SDoug Thompson 	/*
53793c2df58SDoug Thompson 	 * Translate the SysAddr to a DramAddr as shown near the start of
53893c2df58SDoug Thompson 	 * section 3.4.4 (p. 70).  Although sys_addr is a 64-bit value, the k8
53993c2df58SDoug Thompson 	 * only deals with 40-bit values.  Therefore we discard bits 63-40 of
54093c2df58SDoug Thompson 	 * sys_addr below.  If bit 39 of sys_addr is 1 then the bits we
54193c2df58SDoug Thompson 	 * discard are all 1s.  Otherwise the bits we discard are all 0s.  See
54293c2df58SDoug Thompson 	 * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture
54393c2df58SDoug Thompson 	 * Programmer's Manual Volume 1 Application Programming.
54493c2df58SDoug Thompson 	 */
545f678b8ccSBorislav Petkov 	dram_addr = (sys_addr & GENMASK(0, 39)) - dram_base;
54693c2df58SDoug Thompson 
547956b9ba1SJoe Perches 	edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
548956b9ba1SJoe Perches 		 (unsigned long)sys_addr, (unsigned long)dram_addr);
54993c2df58SDoug Thompson 	return dram_addr;
55093c2df58SDoug Thompson }
55193c2df58SDoug Thompson 
55293c2df58SDoug Thompson /*
55393c2df58SDoug Thompson  * @intlv_en is the value of the IntlvEn field from a DRAM Base register
55493c2df58SDoug Thompson  * (section 3.4.4.1).  Return the number of bits from a SysAddr that are used
55593c2df58SDoug Thompson  * for node interleaving.
55693c2df58SDoug Thompson  */
55793c2df58SDoug Thompson static int num_node_interleave_bits(unsigned intlv_en)
55893c2df58SDoug Thompson {
55993c2df58SDoug Thompson 	static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 };
56093c2df58SDoug Thompson 	int n;
56193c2df58SDoug Thompson 
56293c2df58SDoug Thompson 	BUG_ON(intlv_en > 7);
56393c2df58SDoug Thompson 	n = intlv_shift_table[intlv_en];
56493c2df58SDoug Thompson 	return n;
56593c2df58SDoug Thompson }
56693c2df58SDoug Thompson 
56793c2df58SDoug Thompson /* Translate the DramAddr given by @dram_addr to an InputAddr. */
56893c2df58SDoug Thompson static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
56993c2df58SDoug Thompson {
57093c2df58SDoug Thompson 	struct amd64_pvt *pvt;
57193c2df58SDoug Thompson 	int intlv_shift;
57293c2df58SDoug Thompson 	u64 input_addr;
57393c2df58SDoug Thompson 
57493c2df58SDoug Thompson 	pvt = mci->pvt_info;
57593c2df58SDoug Thompson 
57693c2df58SDoug Thompson 	/*
57793c2df58SDoug Thompson 	 * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
57893c2df58SDoug Thompson 	 * concerning translating a DramAddr to an InputAddr.
57993c2df58SDoug Thompson 	 */
5807f19bf75SBorislav Petkov 	intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
581f678b8ccSBorislav Petkov 	input_addr = ((dram_addr >> intlv_shift) & GENMASK(12, 35)) +
58293c2df58SDoug Thompson 		      (dram_addr & 0xfff);
58393c2df58SDoug Thompson 
584956b9ba1SJoe Perches 	edac_dbg(2, "  Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
58593c2df58SDoug Thompson 		 intlv_shift, (unsigned long)dram_addr,
58693c2df58SDoug Thompson 		 (unsigned long)input_addr);
58793c2df58SDoug Thompson 
58893c2df58SDoug Thompson 	return input_addr;
58993c2df58SDoug Thompson }
59093c2df58SDoug Thompson 
59193c2df58SDoug Thompson /*
59293c2df58SDoug Thompson  * Translate the SysAddr represented by @sys_addr to an InputAddr.  It is
59393c2df58SDoug Thompson  * assumed that @sys_addr maps to the node given by mci.
59493c2df58SDoug Thompson  */
59593c2df58SDoug Thompson static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
59693c2df58SDoug Thompson {
59793c2df58SDoug Thompson 	u64 input_addr;
59893c2df58SDoug Thompson 
59993c2df58SDoug Thompson 	input_addr =
60093c2df58SDoug Thompson 	    dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
60193c2df58SDoug Thompson 
602956b9ba1SJoe Perches 	edac_dbg(2, "SysAdddr 0x%lx translates to InputAddr 0x%lx\n",
60393c2df58SDoug Thompson 		 (unsigned long)sys_addr, (unsigned long)input_addr);
60493c2df58SDoug Thompson 
60593c2df58SDoug Thompson 	return input_addr;
60693c2df58SDoug Thompson }
60793c2df58SDoug Thompson 
60893c2df58SDoug Thompson 
60993c2df58SDoug Thompson /*
61093c2df58SDoug Thompson  * @input_addr is an InputAddr associated with the node represented by mci.
61193c2df58SDoug Thompson  * Translate @input_addr to a DramAddr and return the result.
61293c2df58SDoug Thompson  */
61393c2df58SDoug Thompson static u64 input_addr_to_dram_addr(struct mem_ctl_info *mci, u64 input_addr)
61493c2df58SDoug Thompson {
61593c2df58SDoug Thompson 	struct amd64_pvt *pvt;
616b487c33eSBorislav Petkov 	unsigned node_id, intlv_shift;
61793c2df58SDoug Thompson 	u64 bits, dram_addr;
61893c2df58SDoug Thompson 	u32 intlv_sel;
61993c2df58SDoug Thompson 
62093c2df58SDoug Thompson 	/*
62193c2df58SDoug Thompson 	 * Near the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
62293c2df58SDoug Thompson 	 * shows how to translate a DramAddr to an InputAddr. Here we reverse
62393c2df58SDoug Thompson 	 * this procedure. When translating from a DramAddr to an InputAddr, the
62493c2df58SDoug Thompson 	 * bits used for node interleaving are discarded.  Here we recover these
62593c2df58SDoug Thompson 	 * bits from the IntlvSel field of the DRAM Limit register (section
62693c2df58SDoug Thompson 	 * 3.4.4.2) for the node that input_addr is associated with.
62793c2df58SDoug Thompson 	 */
62893c2df58SDoug Thompson 	pvt = mci->pvt_info;
62993c2df58SDoug Thompson 	node_id = pvt->mc_node_id;
630b487c33eSBorislav Petkov 
631b487c33eSBorislav Petkov 	BUG_ON(node_id > 7);
63293c2df58SDoug Thompson 
6337f19bf75SBorislav Petkov 	intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
63493c2df58SDoug Thompson 	if (intlv_shift == 0) {
635956b9ba1SJoe Perches 		edac_dbg(1, "    InputAddr 0x%lx translates to DramAddr of same value\n",
636956b9ba1SJoe Perches 			 (unsigned long)input_addr);
63793c2df58SDoug Thompson 
63893c2df58SDoug Thompson 		return input_addr;
63993c2df58SDoug Thompson 	}
64093c2df58SDoug Thompson 
641f678b8ccSBorislav Petkov 	bits = ((input_addr & GENMASK(12, 35)) << intlv_shift) +
64293c2df58SDoug Thompson 		(input_addr & 0xfff);
64393c2df58SDoug Thompson 
6447f19bf75SBorislav Petkov 	intlv_sel = dram_intlv_sel(pvt, node_id) & ((1 << intlv_shift) - 1);
64593c2df58SDoug Thompson 	dram_addr = bits + (intlv_sel << 12);
64693c2df58SDoug Thompson 
647956b9ba1SJoe Perches 	edac_dbg(1, "InputAddr 0x%lx translates to DramAddr 0x%lx (%d node interleave bits)\n",
648956b9ba1SJoe Perches 		 (unsigned long)input_addr,
64993c2df58SDoug Thompson 		 (unsigned long)dram_addr, intlv_shift);
65093c2df58SDoug Thompson 
65193c2df58SDoug Thompson 	return dram_addr;
65293c2df58SDoug Thompson }
65393c2df58SDoug Thompson 
65493c2df58SDoug Thompson /*
65593c2df58SDoug Thompson  * @dram_addr is a DramAddr that maps to the node represented by mci. Convert
65693c2df58SDoug Thompson  * @dram_addr to a SysAddr.
65793c2df58SDoug Thompson  */
65893c2df58SDoug Thompson static u64 dram_addr_to_sys_addr(struct mem_ctl_info *mci, u64 dram_addr)
65993c2df58SDoug Thompson {
66093c2df58SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
6617f19bf75SBorislav Petkov 	u64 hole_base, hole_offset, hole_size, base, sys_addr;
66293c2df58SDoug Thompson 	int ret = 0;
66393c2df58SDoug Thompson 
66493c2df58SDoug Thompson 	ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
66593c2df58SDoug Thompson 				      &hole_size);
66693c2df58SDoug Thompson 	if (!ret) {
66793c2df58SDoug Thompson 		if ((dram_addr >= hole_base) &&
66893c2df58SDoug Thompson 		    (dram_addr < (hole_base + hole_size))) {
66993c2df58SDoug Thompson 			sys_addr = dram_addr + hole_offset;
67093c2df58SDoug Thompson 
671956b9ba1SJoe Perches 			edac_dbg(1, "using DHAR to translate DramAddr 0x%lx to SysAddr 0x%lx\n",
672956b9ba1SJoe Perches 				 (unsigned long)dram_addr,
67393c2df58SDoug Thompson 				 (unsigned long)sys_addr);
67493c2df58SDoug Thompson 
67593c2df58SDoug Thompson 			return sys_addr;
67693c2df58SDoug Thompson 		}
67793c2df58SDoug Thompson 	}
67893c2df58SDoug Thompson 
6797f19bf75SBorislav Petkov 	base     = get_dram_base(pvt, pvt->mc_node_id);
68093c2df58SDoug Thompson 	sys_addr = dram_addr + base;
68193c2df58SDoug Thompson 
68293c2df58SDoug Thompson 	/*
68393c2df58SDoug Thompson 	 * The sys_addr we have computed up to this point is a 40-bit value
68493c2df58SDoug Thompson 	 * because the k8 deals with 40-bit values.  However, the value we are
68593c2df58SDoug Thompson 	 * supposed to return is a full 64-bit physical address.  The AMD
68693c2df58SDoug Thompson 	 * x86-64 architecture specifies that the most significant implemented
68793c2df58SDoug Thompson 	 * address bit through bit 63 of a physical address must be either all
68893c2df58SDoug Thompson 	 * 0s or all 1s.  Therefore we sign-extend the 40-bit sys_addr to a
68993c2df58SDoug Thompson 	 * 64-bit value below.  See section 3.4.2 of AMD publication 24592:
69093c2df58SDoug Thompson 	 * AMD x86-64 Architecture Programmer's Manual Volume 1 Application
69193c2df58SDoug Thompson 	 * Programming.
69293c2df58SDoug Thompson 	 */
69393c2df58SDoug Thompson 	sys_addr |= ~((sys_addr & (1ull << 39)) - 1);
69493c2df58SDoug Thompson 
695956b9ba1SJoe Perches 	edac_dbg(1, "    Node %d, DramAddr 0x%lx to SysAddr 0x%lx\n",
69693c2df58SDoug Thompson 		 pvt->mc_node_id, (unsigned long)dram_addr,
69793c2df58SDoug Thompson 		 (unsigned long)sys_addr);
69893c2df58SDoug Thompson 
69993c2df58SDoug Thompson 	return sys_addr;
70093c2df58SDoug Thompson }
70193c2df58SDoug Thompson 
70293c2df58SDoug Thompson /*
70393c2df58SDoug Thompson  * @input_addr is an InputAddr associated with the node given by mci. Translate
70493c2df58SDoug Thompson  * @input_addr to a SysAddr.
70593c2df58SDoug Thompson  */
70693c2df58SDoug Thompson static inline u64 input_addr_to_sys_addr(struct mem_ctl_info *mci,
70793c2df58SDoug Thompson 					 u64 input_addr)
70893c2df58SDoug Thompson {
70993c2df58SDoug Thompson 	return dram_addr_to_sys_addr(mci,
71093c2df58SDoug Thompson 				     input_addr_to_dram_addr(mci, input_addr));
71193c2df58SDoug Thompson }
71293c2df58SDoug Thompson 
71393c2df58SDoug Thompson /* Map the Error address to a PAGE and PAGE OFFSET. */
71493c2df58SDoug Thompson static inline void error_address_to_page_and_offset(u64 error_address,
71593c2df58SDoug Thompson 						    u32 *page, u32 *offset)
71693c2df58SDoug Thompson {
71793c2df58SDoug Thompson 	*page = (u32) (error_address >> PAGE_SHIFT);
71893c2df58SDoug Thompson 	*offset = ((u32) error_address) & ~PAGE_MASK;
71993c2df58SDoug Thompson }
72093c2df58SDoug Thompson 
72193c2df58SDoug Thompson /*
72293c2df58SDoug Thompson  * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address
72393c2df58SDoug Thompson  * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers
72493c2df58SDoug Thompson  * of a node that detected an ECC memory error.  mci represents the node that
72593c2df58SDoug Thompson  * the error address maps to (possibly different from the node that detected
72693c2df58SDoug Thompson  * the error).  Return the number of the csrow that sys_addr maps to, or -1 on
72793c2df58SDoug Thompson  * error.
72893c2df58SDoug Thompson  */
72993c2df58SDoug Thompson static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
73093c2df58SDoug Thompson {
73193c2df58SDoug Thompson 	int csrow;
73293c2df58SDoug Thompson 
73393c2df58SDoug Thompson 	csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr));
73493c2df58SDoug Thompson 
73593c2df58SDoug Thompson 	if (csrow == -1)
73624f9a7feSBorislav Petkov 		amd64_mc_err(mci, "Failed to translate InputAddr to csrow for "
73793c2df58SDoug Thompson 				  "address 0x%lx\n", (unsigned long)sys_addr);
73893c2df58SDoug Thompson 	return csrow;
73993c2df58SDoug Thompson }
740e2ce7255SDoug Thompson 
741bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
7422da11654SDoug Thompson 
7432da11654SDoug Thompson /*
7442da11654SDoug Thompson  * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
7452da11654SDoug Thompson  * are ECC capable.
7462da11654SDoug Thompson  */
7471f6189edSDan Carpenter static unsigned long amd64_determine_edac_cap(struct amd64_pvt *pvt)
7482da11654SDoug Thompson {
749cb328507SBorislav Petkov 	u8 bit;
7501f6189edSDan Carpenter 	unsigned long edac_cap = EDAC_FLAG_NONE;
7512da11654SDoug Thompson 
7521433eb99SBorislav Petkov 	bit = (boot_cpu_data.x86 > 0xf || pvt->ext_model >= K8_REV_F)
7532da11654SDoug Thompson 		? 19
7542da11654SDoug Thompson 		: 17;
7552da11654SDoug Thompson 
756584fcff4SBorislav Petkov 	if (pvt->dclr0 & BIT(bit))
7572da11654SDoug Thompson 		edac_cap = EDAC_FLAG_SECDED;
7582da11654SDoug Thompson 
7592da11654SDoug Thompson 	return edac_cap;
7602da11654SDoug Thompson }
7612da11654SDoug Thompson 
7628c671751SBorislav Petkov static void amd64_debug_display_dimm_sizes(struct amd64_pvt *, u8);
7632da11654SDoug Thompson 
76468798e17SBorislav Petkov static void amd64_dump_dramcfg_low(u32 dclr, int chan)
76568798e17SBorislav Petkov {
766956b9ba1SJoe Perches 	edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
76768798e17SBorislav Petkov 
768956b9ba1SJoe Perches 	edac_dbg(1, "  DIMM type: %sbuffered; all DIMMs support ECC: %s\n",
76968798e17SBorislav Petkov 		 (dclr & BIT(16)) ?  "un" : "",
77068798e17SBorislav Petkov 		 (dclr & BIT(19)) ? "yes" : "no");
77168798e17SBorislav Petkov 
772956b9ba1SJoe Perches 	edac_dbg(1, "  PAR/ERR parity: %s\n",
77368798e17SBorislav Petkov 		 (dclr & BIT(8)) ?  "enabled" : "disabled");
77468798e17SBorislav Petkov 
775cb328507SBorislav Petkov 	if (boot_cpu_data.x86 == 0x10)
776956b9ba1SJoe Perches 		edac_dbg(1, "  DCT 128bit mode width: %s\n",
77768798e17SBorislav Petkov 			 (dclr & BIT(11)) ?  "128b" : "64b");
77868798e17SBorislav Petkov 
779956b9ba1SJoe Perches 	edac_dbg(1, "  x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
78068798e17SBorislav Petkov 		 (dclr & BIT(12)) ?  "yes" : "no",
78168798e17SBorislav Petkov 		 (dclr & BIT(13)) ?  "yes" : "no",
78268798e17SBorislav Petkov 		 (dclr & BIT(14)) ?  "yes" : "no",
78368798e17SBorislav Petkov 		 (dclr & BIT(15)) ?  "yes" : "no");
78468798e17SBorislav Petkov }
78568798e17SBorislav Petkov 
7862da11654SDoug Thompson /* Display and decode various NB registers for debug purposes. */
787b2b0c605SBorislav Petkov static void dump_misc_regs(struct amd64_pvt *pvt)
7882da11654SDoug Thompson {
789956b9ba1SJoe Perches 	edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
7902da11654SDoug Thompson 
791956b9ba1SJoe Perches 	edac_dbg(1, "  NB two channel DRAM capable: %s\n",
7925980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
79368798e17SBorislav Petkov 
794956b9ba1SJoe Perches 	edac_dbg(1, "  ECC capable: %s, ChipKill ECC capable: %s\n",
7955980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
7965980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
79768798e17SBorislav Petkov 
79868798e17SBorislav Petkov 	amd64_dump_dramcfg_low(pvt->dclr0, 0);
7992da11654SDoug Thompson 
800956b9ba1SJoe Perches 	edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
8012da11654SDoug Thompson 
802956b9ba1SJoe Perches 	edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n",
803bc21fa57SBorislav Petkov 		 pvt->dhar, dhar_base(pvt),
804bc21fa57SBorislav Petkov 		 (boot_cpu_data.x86 == 0xf) ? k8_dhar_offset(pvt)
805bc21fa57SBorislav Petkov 		 : f10_dhar_offset(pvt));
8062da11654SDoug Thompson 
807956b9ba1SJoe Perches 	edac_dbg(1, "  DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
8082da11654SDoug Thompson 
8098c671751SBorislav Petkov 	amd64_debug_display_dimm_sizes(pvt, 0);
8104d796364SBorislav Petkov 
8114d796364SBorislav Petkov 	/* everything below this point is Fam10h and above */
8124d796364SBorislav Petkov 	if (boot_cpu_data.x86 == 0xf)
8132da11654SDoug Thompson 		return;
8144d796364SBorislav Petkov 
8158c671751SBorislav Petkov 	amd64_debug_display_dimm_sizes(pvt, 1);
8162da11654SDoug Thompson 
817a3b7db09SBorislav Petkov 	amd64_info("using %s syndromes.\n", ((pvt->ecc_sym_sz == 8) ? "x8" : "x4"));
818ad6a32e9SBorislav Petkov 
8198de1d91eSBorislav Petkov 	/* Only if NOT ganged does dclr1 have valid info */
82068798e17SBorislav Petkov 	if (!dct_ganging_enabled(pvt))
82168798e17SBorislav Petkov 		amd64_dump_dramcfg_low(pvt->dclr1, 1);
8222da11654SDoug Thompson }
8232da11654SDoug Thompson 
82494be4bffSDoug Thompson /*
82511c75eadSBorislav Petkov  * see BKDG, F2x[1,0][5C:40], F2[1,0][6C:60]
82694be4bffSDoug Thompson  */
82711c75eadSBorislav Petkov static void prep_chip_selects(struct amd64_pvt *pvt)
82894be4bffSDoug Thompson {
8291433eb99SBorislav Petkov 	if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_F) {
83011c75eadSBorislav Petkov 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
83111c75eadSBorislav Petkov 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8;
8329d858bb1SBorislav Petkov 	} else {
83311c75eadSBorislav Petkov 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
83411c75eadSBorislav Petkov 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4;
8359d858bb1SBorislav Petkov 	}
83694be4bffSDoug Thompson }
83794be4bffSDoug Thompson 
83894be4bffSDoug Thompson /*
83911c75eadSBorislav Petkov  * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers
84094be4bffSDoug Thompson  */
841b2b0c605SBorislav Petkov static void read_dct_base_mask(struct amd64_pvt *pvt)
84294be4bffSDoug Thompson {
84311c75eadSBorislav Petkov 	int cs;
84494be4bffSDoug Thompson 
84511c75eadSBorislav Petkov 	prep_chip_selects(pvt);
84694be4bffSDoug Thompson 
84711c75eadSBorislav Petkov 	for_each_chip_select(cs, 0, pvt) {
84871d2a32eSBorislav Petkov 		int reg0   = DCSB0 + (cs * 4);
84971d2a32eSBorislav Petkov 		int reg1   = DCSB1 + (cs * 4);
85011c75eadSBorislav Petkov 		u32 *base0 = &pvt->csels[0].csbases[cs];
85111c75eadSBorislav Petkov 		u32 *base1 = &pvt->csels[1].csbases[cs];
852b2b0c605SBorislav Petkov 
85311c75eadSBorislav Petkov 		if (!amd64_read_dct_pci_cfg(pvt, reg0, base0))
854956b9ba1SJoe Perches 			edac_dbg(0, "  DCSB0[%d]=0x%08x reg: F2x%x\n",
85511c75eadSBorislav Petkov 				 cs, *base0, reg0);
85694be4bffSDoug Thompson 
85711c75eadSBorislav Petkov 		if (boot_cpu_data.x86 == 0xf || dct_ganging_enabled(pvt))
85811c75eadSBorislav Petkov 			continue;
859b2b0c605SBorislav Petkov 
86011c75eadSBorislav Petkov 		if (!amd64_read_dct_pci_cfg(pvt, reg1, base1))
861956b9ba1SJoe Perches 			edac_dbg(0, "  DCSB1[%d]=0x%08x reg: F2x%x\n",
86211c75eadSBorislav Petkov 				 cs, *base1, reg1);
86394be4bffSDoug Thompson 	}
86494be4bffSDoug Thompson 
86511c75eadSBorislav Petkov 	for_each_chip_select_mask(cs, 0, pvt) {
86671d2a32eSBorislav Petkov 		int reg0   = DCSM0 + (cs * 4);
86771d2a32eSBorislav Petkov 		int reg1   = DCSM1 + (cs * 4);
86811c75eadSBorislav Petkov 		u32 *mask0 = &pvt->csels[0].csmasks[cs];
86911c75eadSBorislav Petkov 		u32 *mask1 = &pvt->csels[1].csmasks[cs];
870b2b0c605SBorislav Petkov 
87111c75eadSBorislav Petkov 		if (!amd64_read_dct_pci_cfg(pvt, reg0, mask0))
872956b9ba1SJoe Perches 			edac_dbg(0, "    DCSM0[%d]=0x%08x reg: F2x%x\n",
87311c75eadSBorislav Petkov 				 cs, *mask0, reg0);
87494be4bffSDoug Thompson 
87511c75eadSBorislav Petkov 		if (boot_cpu_data.x86 == 0xf || dct_ganging_enabled(pvt))
87611c75eadSBorislav Petkov 			continue;
877b2b0c605SBorislav Petkov 
87811c75eadSBorislav Petkov 		if (!amd64_read_dct_pci_cfg(pvt, reg1, mask1))
879956b9ba1SJoe Perches 			edac_dbg(0, "    DCSM1[%d]=0x%08x reg: F2x%x\n",
88011c75eadSBorislav Petkov 				 cs, *mask1, reg1);
88194be4bffSDoug Thompson 	}
8826ba5dcdcSBorislav Petkov }
88394be4bffSDoug Thompson 
88424f9a7feSBorislav Petkov static enum mem_type amd64_determine_memory_type(struct amd64_pvt *pvt, int cs)
88594be4bffSDoug Thompson {
88694be4bffSDoug Thompson 	enum mem_type type;
88794be4bffSDoug Thompson 
888cb328507SBorislav Petkov 	/* F15h supports only DDR3 */
889cb328507SBorislav Petkov 	if (boot_cpu_data.x86 >= 0x15)
890cb328507SBorislav Petkov 		type = (pvt->dclr0 & BIT(16)) ?	MEM_DDR3 : MEM_RDDR3;
891cb328507SBorislav Petkov 	else if (boot_cpu_data.x86 == 0x10 || pvt->ext_model >= K8_REV_F) {
8926b4c0bdeSBorislav Petkov 		if (pvt->dchr0 & DDR3_MODE)
8936b4c0bdeSBorislav Petkov 			type = (pvt->dclr0 & BIT(16)) ?	MEM_DDR3 : MEM_RDDR3;
8946b4c0bdeSBorislav Petkov 		else
89594be4bffSDoug Thompson 			type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
89694be4bffSDoug Thompson 	} else {
89794be4bffSDoug Thompson 		type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
89894be4bffSDoug Thompson 	}
89994be4bffSDoug Thompson 
90024f9a7feSBorislav Petkov 	amd64_info("CS%d: %s\n", cs, edac_mem_types[type]);
90194be4bffSDoug Thompson 
90294be4bffSDoug Thompson 	return type;
90394be4bffSDoug Thompson }
90494be4bffSDoug Thompson 
905cb328507SBorislav Petkov /* Get the number of DCT channels the memory controller is using. */
906ddff876dSDoug Thompson static int k8_early_channel_count(struct amd64_pvt *pvt)
907ddff876dSDoug Thompson {
908cb328507SBorislav Petkov 	int flag;
909ddff876dSDoug Thompson 
9109f56da0eSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F)
911ddff876dSDoug Thompson 		/* RevF (NPT) and later */
91241d8bfabSBorislav Petkov 		flag = pvt->dclr0 & WIDTH_128;
9139f56da0eSBorislav Petkov 	else
914ddff876dSDoug Thompson 		/* RevE and earlier */
915ddff876dSDoug Thompson 		flag = pvt->dclr0 & REVE_WIDTH_128;
916ddff876dSDoug Thompson 
917ddff876dSDoug Thompson 	/* not used */
918ddff876dSDoug Thompson 	pvt->dclr1 = 0;
919ddff876dSDoug Thompson 
920ddff876dSDoug Thompson 	return (flag) ? 2 : 1;
921ddff876dSDoug Thompson }
922ddff876dSDoug Thompson 
92370046624SBorislav Petkov /* On F10h and later ErrAddr is MC4_ADDR[47:1] */
92470046624SBorislav Petkov static u64 get_error_address(struct mce *m)
925ddff876dSDoug Thompson {
926c1ae6830SBorislav Petkov 	struct cpuinfo_x86 *c = &boot_cpu_data;
927c1ae6830SBorislav Petkov 	u64 addr;
92870046624SBorislav Petkov 	u8 start_bit = 1;
92970046624SBorislav Petkov 	u8 end_bit   = 47;
93070046624SBorislav Petkov 
931c1ae6830SBorislav Petkov 	if (c->x86 == 0xf) {
93270046624SBorislav Petkov 		start_bit = 3;
93370046624SBorislav Petkov 		end_bit   = 39;
93470046624SBorislav Petkov 	}
93570046624SBorislav Petkov 
936c1ae6830SBorislav Petkov 	addr = m->addr & GENMASK(start_bit, end_bit);
937c1ae6830SBorislav Petkov 
938c1ae6830SBorislav Petkov 	/*
939c1ae6830SBorislav Petkov 	 * Erratum 637 workaround
940c1ae6830SBorislav Petkov 	 */
941c1ae6830SBorislav Petkov 	if (c->x86 == 0x15) {
942c1ae6830SBorislav Petkov 		struct amd64_pvt *pvt;
943c1ae6830SBorislav Petkov 		u64 cc6_base, tmp_addr;
944c1ae6830SBorislav Petkov 		u32 tmp;
945c1ae6830SBorislav Petkov 		u8 mce_nid, intlv_en;
946c1ae6830SBorislav Petkov 
947c1ae6830SBorislav Petkov 		if ((addr & GENMASK(24, 47)) >> 24 != 0x00fdf7)
948c1ae6830SBorislav Petkov 			return addr;
949c1ae6830SBorislav Petkov 
950c1ae6830SBorislav Petkov 		mce_nid	= amd_get_nb_id(m->extcpu);
951c1ae6830SBorislav Petkov 		pvt	= mcis[mce_nid]->pvt_info;
952c1ae6830SBorislav Petkov 
953c1ae6830SBorislav Petkov 		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp);
954c1ae6830SBorislav Petkov 		intlv_en = tmp >> 21 & 0x7;
955c1ae6830SBorislav Petkov 
956c1ae6830SBorislav Petkov 		/* add [47:27] + 3 trailing bits */
957c1ae6830SBorislav Petkov 		cc6_base  = (tmp & GENMASK(0, 20)) << 3;
958c1ae6830SBorislav Petkov 
959c1ae6830SBorislav Petkov 		/* reverse and add DramIntlvEn */
960c1ae6830SBorislav Petkov 		cc6_base |= intlv_en ^ 0x7;
961c1ae6830SBorislav Petkov 
962c1ae6830SBorislav Petkov 		/* pin at [47:24] */
963c1ae6830SBorislav Petkov 		cc6_base <<= 24;
964c1ae6830SBorislav Petkov 
965c1ae6830SBorislav Petkov 		if (!intlv_en)
966c1ae6830SBorislav Petkov 			return cc6_base | (addr & GENMASK(0, 23));
967c1ae6830SBorislav Petkov 
968c1ae6830SBorislav Petkov 		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp);
969c1ae6830SBorislav Petkov 
970c1ae6830SBorislav Petkov 							/* faster log2 */
971c1ae6830SBorislav Petkov 		tmp_addr  = (addr & GENMASK(12, 23)) << __fls(intlv_en + 1);
972c1ae6830SBorislav Petkov 
973c1ae6830SBorislav Petkov 		/* OR DramIntlvSel into bits [14:12] */
974c1ae6830SBorislav Petkov 		tmp_addr |= (tmp & GENMASK(21, 23)) >> 9;
975c1ae6830SBorislav Petkov 
976c1ae6830SBorislav Petkov 		/* add remaining [11:0] bits from original MC4_ADDR */
977c1ae6830SBorislav Petkov 		tmp_addr |= addr & GENMASK(0, 11);
978c1ae6830SBorislav Petkov 
979c1ae6830SBorislav Petkov 		return cc6_base | tmp_addr;
980c1ae6830SBorislav Petkov 	}
981c1ae6830SBorislav Petkov 
982c1ae6830SBorislav Petkov 	return addr;
983ddff876dSDoug Thompson }
984ddff876dSDoug Thompson 
9857f19bf75SBorislav Petkov static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
986ddff876dSDoug Thompson {
987f08e457cSBorislav Petkov 	struct cpuinfo_x86 *c = &boot_cpu_data;
98871d2a32eSBorislav Petkov 	int off = range << 3;
989ddff876dSDoug Thompson 
9907f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off,  &pvt->ranges[range].base.lo);
9917f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo);
992ddff876dSDoug Thompson 
993f08e457cSBorislav Petkov 	if (c->x86 == 0xf)
9947f19bf75SBorislav Petkov 		return;
995ddff876dSDoug Thompson 
9967f19bf75SBorislav Petkov 	if (!dram_rw(pvt, range))
9977f19bf75SBorislav Petkov 		return;
998ddff876dSDoug Thompson 
9997f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off,  &pvt->ranges[range].base.hi);
10007f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi);
1001f08e457cSBorislav Petkov 
1002f08e457cSBorislav Petkov 	/* Factor in CC6 save area by reading dst node's limit reg */
1003f08e457cSBorislav Petkov 	if (c->x86 == 0x15) {
1004f08e457cSBorislav Petkov 		struct pci_dev *f1 = NULL;
1005f08e457cSBorislav Petkov 		u8 nid = dram_dst_node(pvt, range);
1006f08e457cSBorislav Petkov 		u32 llim;
1007f08e457cSBorislav Petkov 
1008f08e457cSBorislav Petkov 		f1 = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0x18 + nid, 1));
1009f08e457cSBorislav Petkov 		if (WARN_ON(!f1))
1010f08e457cSBorislav Petkov 			return;
1011f08e457cSBorislav Petkov 
1012f08e457cSBorislav Petkov 		amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim);
1013f08e457cSBorislav Petkov 
1014f08e457cSBorislav Petkov 		pvt->ranges[range].lim.lo &= GENMASK(0, 15);
1015f08e457cSBorislav Petkov 
1016f08e457cSBorislav Petkov 					    /* {[39:27],111b} */
1017f08e457cSBorislav Petkov 		pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16;
1018f08e457cSBorislav Petkov 
1019f08e457cSBorislav Petkov 		pvt->ranges[range].lim.hi &= GENMASK(0, 7);
1020f08e457cSBorislav Petkov 
1021f08e457cSBorislav Petkov 					    /* [47:40] */
1022f08e457cSBorislav Petkov 		pvt->ranges[range].lim.hi |= llim >> 13;
1023f08e457cSBorislav Petkov 
1024f08e457cSBorislav Petkov 		pci_dev_put(f1);
1025f08e457cSBorislav Petkov 	}
1026ddff876dSDoug Thompson }
1027ddff876dSDoug Thompson 
1028f192c7b1SBorislav Petkov static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
1029f192c7b1SBorislav Petkov 				    u16 syndrome)
1030ddff876dSDoug Thompson {
1031ddff876dSDoug Thompson 	struct mem_ctl_info *src_mci;
1032f192c7b1SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
1033ddff876dSDoug Thompson 	int channel, csrow;
1034ddff876dSDoug Thompson 	u32 page, offset;
1035ddff876dSDoug Thompson 
1036ab5a503cSMauro Carvalho Chehab 	error_address_to_page_and_offset(sys_addr, &page, &offset);
1037ab5a503cSMauro Carvalho Chehab 
1038ab5a503cSMauro Carvalho Chehab 	/*
1039ab5a503cSMauro Carvalho Chehab 	 * Find out which node the error address belongs to. This may be
1040ab5a503cSMauro Carvalho Chehab 	 * different from the node that detected the error.
1041ab5a503cSMauro Carvalho Chehab 	 */
1042ab5a503cSMauro Carvalho Chehab 	src_mci = find_mc_by_sys_addr(mci, sys_addr);
1043ab5a503cSMauro Carvalho Chehab 	if (!src_mci) {
1044ab5a503cSMauro Carvalho Chehab 		amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
1045ab5a503cSMauro Carvalho Chehab 			     (unsigned long)sys_addr);
10469eb07a7fSMauro Carvalho Chehab 		edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
1047ab5a503cSMauro Carvalho Chehab 				     page, offset, syndrome,
1048ab5a503cSMauro Carvalho Chehab 				     -1, -1, -1,
1049ab5a503cSMauro Carvalho Chehab 				     "failed to map error addr to a node",
105003f7eae8SMauro Carvalho Chehab 				     "");
1051ab5a503cSMauro Carvalho Chehab 		return;
1052ab5a503cSMauro Carvalho Chehab 	}
1053ab5a503cSMauro Carvalho Chehab 
1054ab5a503cSMauro Carvalho Chehab 	/* Now map the sys_addr to a CSROW */
1055ab5a503cSMauro Carvalho Chehab 	csrow = sys_addr_to_csrow(src_mci, sys_addr);
1056ab5a503cSMauro Carvalho Chehab 	if (csrow < 0) {
10579eb07a7fSMauro Carvalho Chehab 		edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
1058ab5a503cSMauro Carvalho Chehab 				     page, offset, syndrome,
1059ab5a503cSMauro Carvalho Chehab 				     -1, -1, -1,
1060ab5a503cSMauro Carvalho Chehab 				     "failed to map error addr to a csrow",
106103f7eae8SMauro Carvalho Chehab 				     "");
1062ab5a503cSMauro Carvalho Chehab 		return;
1063ab5a503cSMauro Carvalho Chehab 	}
1064ab5a503cSMauro Carvalho Chehab 
1065ddff876dSDoug Thompson 	/* CHIPKILL enabled */
1066f192c7b1SBorislav Petkov 	if (pvt->nbcfg & NBCFG_CHIPKILL) {
1067bfc04aecSBorislav Petkov 		channel = get_channel_from_ecc_syndrome(mci, syndrome);
1068ddff876dSDoug Thompson 		if (channel < 0) {
1069ddff876dSDoug Thompson 			/*
1070ddff876dSDoug Thompson 			 * Syndrome didn't map, so we don't know which of the
1071ddff876dSDoug Thompson 			 * 2 DIMMs is in error. So we need to ID 'both' of them
1072ddff876dSDoug Thompson 			 * as suspect.
1073ddff876dSDoug Thompson 			 */
1074ab5a503cSMauro Carvalho Chehab 			amd64_mc_warn(src_mci, "unknown syndrome 0x%04x - "
1075ab5a503cSMauro Carvalho Chehab 				      "possible error reporting race\n",
1076ab5a503cSMauro Carvalho Chehab 				      syndrome);
10779eb07a7fSMauro Carvalho Chehab 			edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
1078ab5a503cSMauro Carvalho Chehab 					     page, offset, syndrome,
1079ab5a503cSMauro Carvalho Chehab 					     csrow, -1, -1,
1080ab5a503cSMauro Carvalho Chehab 					     "unknown syndrome - possible error reporting race",
108103f7eae8SMauro Carvalho Chehab 					     "");
1082ddff876dSDoug Thompson 			return;
1083ddff876dSDoug Thompson 		}
1084ddff876dSDoug Thompson 	} else {
1085ddff876dSDoug Thompson 		/*
1086ddff876dSDoug Thompson 		 * non-chipkill ecc mode
1087ddff876dSDoug Thompson 		 *
1088ddff876dSDoug Thompson 		 * The k8 documentation is unclear about how to determine the
1089ddff876dSDoug Thompson 		 * channel number when using non-chipkill memory.  This method
1090ddff876dSDoug Thompson 		 * was obtained from email communication with someone at AMD.
1091ddff876dSDoug Thompson 		 * (Wish the email was placed in this comment - norsk)
1092ddff876dSDoug Thompson 		 */
109344e9e2eeSBorislav Petkov 		channel = ((sys_addr & BIT(3)) != 0);
1094ddff876dSDoug Thompson 	}
1095ddff876dSDoug Thompson 
10969eb07a7fSMauro Carvalho Chehab 	edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, src_mci, 1,
1097ab5a503cSMauro Carvalho Chehab 			     page, offset, syndrome,
1098ab5a503cSMauro Carvalho Chehab 			     csrow, channel, -1,
109903f7eae8SMauro Carvalho Chehab 			     "", "");
1100ddff876dSDoug Thompson }
1101ddff876dSDoug Thompson 
110241d8bfabSBorislav Petkov static int ddr2_cs_size(unsigned i, bool dct_width)
1103ddff876dSDoug Thompson {
110441d8bfabSBorislav Petkov 	unsigned shift = 0;
1105ddff876dSDoug Thompson 
110641d8bfabSBorislav Petkov 	if (i <= 2)
110741d8bfabSBorislav Petkov 		shift = i;
110841d8bfabSBorislav Petkov 	else if (!(i & 0x1))
110941d8bfabSBorislav Petkov 		shift = i >> 1;
11101433eb99SBorislav Petkov 	else
111141d8bfabSBorislav Petkov 		shift = (i + 1) >> 1;
1112ddff876dSDoug Thompson 
111341d8bfabSBorislav Petkov 	return 128 << (shift + !!dct_width);
111441d8bfabSBorislav Petkov }
111541d8bfabSBorislav Petkov 
111641d8bfabSBorislav Petkov static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
111741d8bfabSBorislav Petkov 				  unsigned cs_mode)
111841d8bfabSBorislav Petkov {
111941d8bfabSBorislav Petkov 	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
112041d8bfabSBorislav Petkov 
112141d8bfabSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F) {
112241d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 11);
112341d8bfabSBorislav Petkov 		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
112441d8bfabSBorislav Petkov 	}
112541d8bfabSBorislav Petkov 	else if (pvt->ext_model >= K8_REV_D) {
112611b0a314SBorislav Petkov 		unsigned diff;
112741d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 10);
112841d8bfabSBorislav Petkov 
112911b0a314SBorislav Petkov 		/*
113011b0a314SBorislav Petkov 		 * the below calculation, besides trying to win an obfuscated C
113111b0a314SBorislav Petkov 		 * contest, maps cs_mode values to DIMM chip select sizes. The
113211b0a314SBorislav Petkov 		 * mappings are:
113311b0a314SBorislav Petkov 		 *
113411b0a314SBorislav Petkov 		 * cs_mode	CS size (mb)
113511b0a314SBorislav Petkov 		 * =======	============
113611b0a314SBorislav Petkov 		 * 0		32
113711b0a314SBorislav Petkov 		 * 1		64
113811b0a314SBorislav Petkov 		 * 2		128
113911b0a314SBorislav Petkov 		 * 3		128
114011b0a314SBorislav Petkov 		 * 4		256
114111b0a314SBorislav Petkov 		 * 5		512
114211b0a314SBorislav Petkov 		 * 6		256
114311b0a314SBorislav Petkov 		 * 7		512
114411b0a314SBorislav Petkov 		 * 8		1024
114511b0a314SBorislav Petkov 		 * 9		1024
114611b0a314SBorislav Petkov 		 * 10		2048
114711b0a314SBorislav Petkov 		 *
114811b0a314SBorislav Petkov 		 * Basically, it calculates a value with which to shift the
114911b0a314SBorislav Petkov 		 * smallest CS size of 32MB.
115011b0a314SBorislav Petkov 		 *
115111b0a314SBorislav Petkov 		 * ddr[23]_cs_size have a similar purpose.
115211b0a314SBorislav Petkov 		 */
115311b0a314SBorislav Petkov 		diff = cs_mode/3 + (unsigned)(cs_mode > 5);
115411b0a314SBorislav Petkov 
115511b0a314SBorislav Petkov 		return 32 << (cs_mode - diff);
115641d8bfabSBorislav Petkov 	}
115741d8bfabSBorislav Petkov 	else {
115841d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 6);
115941d8bfabSBorislav Petkov 		return 32 << cs_mode;
116041d8bfabSBorislav Petkov 	}
1161ddff876dSDoug Thompson }
1162ddff876dSDoug Thompson 
11631afd3c98SDoug Thompson /*
11641afd3c98SDoug Thompson  * Get the number of DCT channels in use.
11651afd3c98SDoug Thompson  *
11661afd3c98SDoug Thompson  * Return:
11671afd3c98SDoug Thompson  *	number of Memory Channels in operation
11681afd3c98SDoug Thompson  * Pass back:
11691afd3c98SDoug Thompson  *	contents of the DCL0_LOW register
11701afd3c98SDoug Thompson  */
11717d20d14dSBorislav Petkov static int f1x_early_channel_count(struct amd64_pvt *pvt)
11721afd3c98SDoug Thompson {
11736ba5dcdcSBorislav Petkov 	int i, j, channels = 0;
1174ddff876dSDoug Thompson 
11757d20d14dSBorislav Petkov 	/* On F10h, if we are in 128 bit mode, then we are using 2 channels */
117641d8bfabSBorislav Petkov 	if (boot_cpu_data.x86 == 0x10 && (pvt->dclr0 & WIDTH_128))
11777d20d14dSBorislav Petkov 		return 2;
11781afd3c98SDoug Thompson 
11791afd3c98SDoug Thompson 	/*
1180d16149e8SBorislav Petkov 	 * Need to check if in unganged mode: In such, there are 2 channels,
1181d16149e8SBorislav Petkov 	 * but they are not in 128 bit mode and thus the above 'dclr0' status
1182d16149e8SBorislav Petkov 	 * bit will be OFF.
11831afd3c98SDoug Thompson 	 *
11841afd3c98SDoug Thompson 	 * Need to check DCT0[0] and DCT1[0] to see if only one of them has
11851afd3c98SDoug Thompson 	 * their CSEnable bit on. If so, then SINGLE DIMM case.
11861afd3c98SDoug Thompson 	 */
1187956b9ba1SJoe Perches 	edac_dbg(0, "Data width is not 128 bits - need more decoding\n");
11881afd3c98SDoug Thompson 
11891afd3c98SDoug Thompson 	/*
11901afd3c98SDoug Thompson 	 * Check DRAM Bank Address Mapping values for each DIMM to see if there
11911afd3c98SDoug Thompson 	 * is more than just one DIMM present in unganged mode. Need to check
11921afd3c98SDoug Thompson 	 * both controllers since DIMMs can be placed in either one.
11931afd3c98SDoug Thompson 	 */
1194525a1b20SBorislav Petkov 	for (i = 0; i < 2; i++) {
1195525a1b20SBorislav Petkov 		u32 dbam = (i ? pvt->dbam1 : pvt->dbam0);
11961afd3c98SDoug Thompson 
119757a30854SWan Wei 		for (j = 0; j < 4; j++) {
119857a30854SWan Wei 			if (DBAM_DIMM(j, dbam) > 0) {
11991afd3c98SDoug Thompson 				channels++;
120057a30854SWan Wei 				break;
12011afd3c98SDoug Thompson 			}
120257a30854SWan Wei 		}
120357a30854SWan Wei 	}
12041afd3c98SDoug Thompson 
1205d16149e8SBorislav Petkov 	if (channels > 2)
1206d16149e8SBorislav Petkov 		channels = 2;
1207d16149e8SBorislav Petkov 
120824f9a7feSBorislav Petkov 	amd64_info("MCT channel count: %d\n", channels);
12091afd3c98SDoug Thompson 
12101afd3c98SDoug Thompson 	return channels;
12111afd3c98SDoug Thompson }
12121afd3c98SDoug Thompson 
121341d8bfabSBorislav Petkov static int ddr3_cs_size(unsigned i, bool dct_width)
12141afd3c98SDoug Thompson {
121541d8bfabSBorislav Petkov 	unsigned shift = 0;
121641d8bfabSBorislav Petkov 	int cs_size = 0;
121741d8bfabSBorislav Petkov 
121841d8bfabSBorislav Petkov 	if (i == 0 || i == 3 || i == 4)
121941d8bfabSBorislav Petkov 		cs_size = -1;
122041d8bfabSBorislav Petkov 	else if (i <= 2)
122141d8bfabSBorislav Petkov 		shift = i;
122241d8bfabSBorislav Petkov 	else if (i == 12)
122341d8bfabSBorislav Petkov 		shift = 7;
122441d8bfabSBorislav Petkov 	else if (!(i & 0x1))
122541d8bfabSBorislav Petkov 		shift = i >> 1;
122641d8bfabSBorislav Petkov 	else
122741d8bfabSBorislav Petkov 		shift = (i + 1) >> 1;
122841d8bfabSBorislav Petkov 
122941d8bfabSBorislav Petkov 	if (cs_size != -1)
123041d8bfabSBorislav Petkov 		cs_size = (128 * (1 << !!dct_width)) << shift;
123141d8bfabSBorislav Petkov 
123241d8bfabSBorislav Petkov 	return cs_size;
123341d8bfabSBorislav Petkov }
123441d8bfabSBorislav Petkov 
123541d8bfabSBorislav Petkov static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
123641d8bfabSBorislav Petkov 				   unsigned cs_mode)
123741d8bfabSBorislav Petkov {
123841d8bfabSBorislav Petkov 	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
123941d8bfabSBorislav Petkov 
124041d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 11);
12411433eb99SBorislav Petkov 
12421433eb99SBorislav Petkov 	if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
124341d8bfabSBorislav Petkov 		return ddr3_cs_size(cs_mode, dclr & WIDTH_128);
12441433eb99SBorislav Petkov 	else
124541d8bfabSBorislav Petkov 		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
124641d8bfabSBorislav Petkov }
12471433eb99SBorislav Petkov 
124841d8bfabSBorislav Petkov /*
124941d8bfabSBorislav Petkov  * F15h supports only 64bit DCT interfaces
125041d8bfabSBorislav Petkov  */
125141d8bfabSBorislav Petkov static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
125241d8bfabSBorislav Petkov 				   unsigned cs_mode)
125341d8bfabSBorislav Petkov {
125441d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 12);
125541d8bfabSBorislav Petkov 
125641d8bfabSBorislav Petkov 	return ddr3_cs_size(cs_mode, false);
12571afd3c98SDoug Thompson }
12581afd3c98SDoug Thompson 
12595a5d2371SBorislav Petkov static void read_dram_ctl_register(struct amd64_pvt *pvt)
12606163b5d4SDoug Thompson {
12616163b5d4SDoug Thompson 
12625a5d2371SBorislav Petkov 	if (boot_cpu_data.x86 == 0xf)
12635a5d2371SBorislav Petkov 		return;
12645a5d2371SBorislav Petkov 
126578da121eSBorislav Petkov 	if (!amd64_read_dct_pci_cfg(pvt, DCT_SEL_LO, &pvt->dct_sel_lo)) {
1266956b9ba1SJoe Perches 		edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
126778da121eSBorislav Petkov 			 pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
12686163b5d4SDoug Thompson 
1269956b9ba1SJoe Perches 		edac_dbg(0, "  DCTs operate in %s mode\n",
12705a5d2371SBorislav Petkov 			 (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
12716163b5d4SDoug Thompson 
127272381bd5SBorislav Petkov 		if (!dct_ganging_enabled(pvt))
1273956b9ba1SJoe Perches 			edac_dbg(0, "  Address range split per DCT: %s\n",
127472381bd5SBorislav Petkov 				 (dct_high_range_enabled(pvt) ? "yes" : "no"));
127572381bd5SBorislav Petkov 
1276956b9ba1SJoe Perches 		edac_dbg(0, "  data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n",
127772381bd5SBorislav Petkov 			 (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
127872381bd5SBorislav Petkov 			 (dct_memory_cleared(pvt) ? "yes" : "no"));
127972381bd5SBorislav Petkov 
1280956b9ba1SJoe Perches 		edac_dbg(0, "  channel interleave: %s, "
128178da121eSBorislav Petkov 			 "interleave bits selector: 0x%x\n",
128272381bd5SBorislav Petkov 			 (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
12836163b5d4SDoug Thompson 			 dct_sel_interleave_addr(pvt));
12846163b5d4SDoug Thompson 	}
12856163b5d4SDoug Thompson 
128678da121eSBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, DCT_SEL_HI, &pvt->dct_sel_hi);
12876163b5d4SDoug Thompson }
12886163b5d4SDoug Thompson 
1289f71d0a05SDoug Thompson /*
1290229a7a11SBorislav Petkov  * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory
1291f71d0a05SDoug Thompson  * Interleaving Modes.
1292f71d0a05SDoug Thompson  */
1293b15f0fcaSBorislav Petkov static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
1294229a7a11SBorislav Petkov 				bool hi_range_sel, u8 intlv_en)
12956163b5d4SDoug Thompson {
1296151fa71cSBorislav Petkov 	u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1;
12976163b5d4SDoug Thompson 
12986163b5d4SDoug Thompson 	if (dct_ganging_enabled(pvt))
1299229a7a11SBorislav Petkov 		return 0;
1300229a7a11SBorislav Petkov 
1301229a7a11SBorislav Petkov 	if (hi_range_sel)
1302229a7a11SBorislav Petkov 		return dct_sel_high;
1303229a7a11SBorislav Petkov 
1304f71d0a05SDoug Thompson 	/*
1305f71d0a05SDoug Thompson 	 * see F2x110[DctSelIntLvAddr] - channel interleave mode
1306f71d0a05SDoug Thompson 	 */
1307229a7a11SBorislav Petkov 	if (dct_interleave_enabled(pvt)) {
1308229a7a11SBorislav Petkov 		u8 intlv_addr = dct_sel_interleave_addr(pvt);
13096163b5d4SDoug Thompson 
1310229a7a11SBorislav Petkov 		/* return DCT select function: 0=DCT0, 1=DCT1 */
1311229a7a11SBorislav Petkov 		if (!intlv_addr)
1312229a7a11SBorislav Petkov 			return sys_addr >> 6 & 1;
13136163b5d4SDoug Thompson 
1314229a7a11SBorislav Petkov 		if (intlv_addr & 0x2) {
1315229a7a11SBorislav Petkov 			u8 shift = intlv_addr & 0x1 ? 9 : 6;
1316229a7a11SBorislav Petkov 			u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) % 2;
1317229a7a11SBorislav Petkov 
1318229a7a11SBorislav Petkov 			return ((sys_addr >> shift) & 1) ^ temp;
13196163b5d4SDoug Thompson 		}
13206163b5d4SDoug Thompson 
1321229a7a11SBorislav Petkov 		return (sys_addr >> (12 + hweight8(intlv_en))) & 1;
1322229a7a11SBorislav Petkov 	}
1323229a7a11SBorislav Petkov 
1324229a7a11SBorislav Petkov 	if (dct_high_range_enabled(pvt))
1325229a7a11SBorislav Petkov 		return ~dct_sel_high & 1;
13266163b5d4SDoug Thompson 
13276163b5d4SDoug Thompson 	return 0;
13286163b5d4SDoug Thompson }
13296163b5d4SDoug Thompson 
1330c8e518d5SBorislav Petkov /* Convert the sys_addr to the normalized DCT address */
1331e761359aSBorislav Petkov static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, unsigned range,
1332c8e518d5SBorislav Petkov 				 u64 sys_addr, bool hi_rng,
1333c8e518d5SBorislav Petkov 				 u32 dct_sel_base_addr)
13346163b5d4SDoug Thompson {
13356163b5d4SDoug Thompson 	u64 chan_off;
1336c8e518d5SBorislav Petkov 	u64 dram_base		= get_dram_base(pvt, range);
1337c8e518d5SBorislav Petkov 	u64 hole_off		= f10_dhar_offset(pvt);
1338c8e518d5SBorislav Petkov 	u64 dct_sel_base_off	= (pvt->dct_sel_hi & 0xFFFFFC00) << 16;
13396163b5d4SDoug Thompson 
1340c8e518d5SBorislav Petkov 	if (hi_rng) {
1341c8e518d5SBorislav Petkov 		/*
1342c8e518d5SBorislav Petkov 		 * if
1343c8e518d5SBorislav Petkov 		 * base address of high range is below 4Gb
1344c8e518d5SBorislav Petkov 		 * (bits [47:27] at [31:11])
1345c8e518d5SBorislav Petkov 		 * DRAM address space on this DCT is hoisted above 4Gb	&&
1346c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1347c8e518d5SBorislav Petkov 		 *
1348c8e518d5SBorislav Petkov 		 *	remove hole offset from sys_addr
1349c8e518d5SBorislav Petkov 		 * else
1350c8e518d5SBorislav Petkov 		 *	remove high range offset from sys_addr
1351c8e518d5SBorislav Petkov 		 */
1352c8e518d5SBorislav Petkov 		if ((!(dct_sel_base_addr >> 16) ||
1353c8e518d5SBorislav Petkov 		     dct_sel_base_addr < dhar_base(pvt)) &&
1354972ea17aSBorislav Petkov 		    dhar_valid(pvt) &&
1355c8e518d5SBorislav Petkov 		    (sys_addr >= BIT_64(32)))
1356bc21fa57SBorislav Petkov 			chan_off = hole_off;
13576163b5d4SDoug Thompson 		else
13586163b5d4SDoug Thompson 			chan_off = dct_sel_base_off;
13596163b5d4SDoug Thompson 	} else {
1360c8e518d5SBorislav Petkov 		/*
1361c8e518d5SBorislav Petkov 		 * if
1362c8e518d5SBorislav Petkov 		 * we have a valid hole		&&
1363c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1364c8e518d5SBorislav Petkov 		 *
1365c8e518d5SBorislav Petkov 		 *	remove hole
1366c8e518d5SBorislav Petkov 		 * else
1367c8e518d5SBorislav Petkov 		 *	remove dram base to normalize to DCT address
1368c8e518d5SBorislav Petkov 		 */
1369972ea17aSBorislav Petkov 		if (dhar_valid(pvt) && (sys_addr >= BIT_64(32)))
1370bc21fa57SBorislav Petkov 			chan_off = hole_off;
13716163b5d4SDoug Thompson 		else
1372c8e518d5SBorislav Petkov 			chan_off = dram_base;
13736163b5d4SDoug Thompson 	}
13746163b5d4SDoug Thompson 
1375c8e518d5SBorislav Petkov 	return (sys_addr & GENMASK(6,47)) - (chan_off & GENMASK(23,47));
13766163b5d4SDoug Thompson }
13776163b5d4SDoug Thompson 
13786163b5d4SDoug Thompson /*
13796163b5d4SDoug Thompson  * checks if the csrow passed in is marked as SPARED, if so returns the new
13806163b5d4SDoug Thompson  * spare row
13816163b5d4SDoug Thompson  */
138211c75eadSBorislav Petkov static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow)
13836163b5d4SDoug Thompson {
1384614ec9d8SBorislav Petkov 	int tmp_cs;
13856163b5d4SDoug Thompson 
1386614ec9d8SBorislav Petkov 	if (online_spare_swap_done(pvt, dct) &&
1387614ec9d8SBorislav Petkov 	    csrow == online_spare_bad_dramcs(pvt, dct)) {
1388614ec9d8SBorislav Petkov 
1389614ec9d8SBorislav Petkov 		for_each_chip_select(tmp_cs, dct, pvt) {
1390614ec9d8SBorislav Petkov 			if (chip_select_base(tmp_cs, dct, pvt) & 0x2) {
1391614ec9d8SBorislav Petkov 				csrow = tmp_cs;
1392614ec9d8SBorislav Petkov 				break;
1393614ec9d8SBorislav Petkov 			}
1394614ec9d8SBorislav Petkov 		}
13956163b5d4SDoug Thompson 	}
13966163b5d4SDoug Thompson 	return csrow;
13976163b5d4SDoug Thompson }
13986163b5d4SDoug Thompson 
13996163b5d4SDoug Thompson /*
14006163b5d4SDoug Thompson  * Iterate over the DRAM DCT "base" and "mask" registers looking for a
14016163b5d4SDoug Thompson  * SystemAddr match on the specified 'ChannelSelect' and 'NodeID'
14026163b5d4SDoug Thompson  *
14036163b5d4SDoug Thompson  * Return:
14046163b5d4SDoug Thompson  *	-EINVAL:  NOT FOUND
14056163b5d4SDoug Thompson  *	0..csrow = Chip-Select Row
14066163b5d4SDoug Thompson  */
1407b15f0fcaSBorislav Petkov static int f1x_lookup_addr_in_dct(u64 in_addr, u32 nid, u8 dct)
14086163b5d4SDoug Thompson {
14096163b5d4SDoug Thompson 	struct mem_ctl_info *mci;
14106163b5d4SDoug Thompson 	struct amd64_pvt *pvt;
141111c75eadSBorislav Petkov 	u64 cs_base, cs_mask;
14126163b5d4SDoug Thompson 	int cs_found = -EINVAL;
14136163b5d4SDoug Thompson 	int csrow;
14146163b5d4SDoug Thompson 
1415cc4d8860SBorislav Petkov 	mci = mcis[nid];
14166163b5d4SDoug Thompson 	if (!mci)
14176163b5d4SDoug Thompson 		return cs_found;
14186163b5d4SDoug Thompson 
14196163b5d4SDoug Thompson 	pvt = mci->pvt_info;
14206163b5d4SDoug Thompson 
1421956b9ba1SJoe Perches 	edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct);
14226163b5d4SDoug Thompson 
142311c75eadSBorislav Petkov 	for_each_chip_select(csrow, dct, pvt) {
142411c75eadSBorislav Petkov 		if (!csrow_enabled(csrow, dct, pvt))
14256163b5d4SDoug Thompson 			continue;
14266163b5d4SDoug Thompson 
142711c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask);
14286163b5d4SDoug Thompson 
1429956b9ba1SJoe Perches 		edac_dbg(1, "    CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
14306163b5d4SDoug Thompson 			 csrow, cs_base, cs_mask);
14316163b5d4SDoug Thompson 
143211c75eadSBorislav Petkov 		cs_mask = ~cs_mask;
14336163b5d4SDoug Thompson 
1434956b9ba1SJoe Perches 		edac_dbg(1, "    (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n",
143511c75eadSBorislav Petkov 			 (in_addr & cs_mask), (cs_base & cs_mask));
14366163b5d4SDoug Thompson 
143711c75eadSBorislav Petkov 		if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
143811c75eadSBorislav Petkov 			cs_found = f10_process_possible_spare(pvt, dct, csrow);
14396163b5d4SDoug Thompson 
1440956b9ba1SJoe Perches 			edac_dbg(1, " MATCH csrow=%d\n", cs_found);
14416163b5d4SDoug Thompson 			break;
14426163b5d4SDoug Thompson 		}
14436163b5d4SDoug Thompson 	}
14446163b5d4SDoug Thompson 	return cs_found;
14456163b5d4SDoug Thompson }
14466163b5d4SDoug Thompson 
144795b0ef55SBorislav Petkov /*
144895b0ef55SBorislav Petkov  * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is
144995b0ef55SBorislav Petkov  * swapped with a region located at the bottom of memory so that the GPU can use
145095b0ef55SBorislav Petkov  * the interleaved region and thus two channels.
145195b0ef55SBorislav Petkov  */
1452b15f0fcaSBorislav Petkov static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr)
145395b0ef55SBorislav Petkov {
145495b0ef55SBorislav Petkov 	u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr;
145595b0ef55SBorislav Petkov 
145695b0ef55SBorislav Petkov 	if (boot_cpu_data.x86 == 0x10) {
145795b0ef55SBorislav Petkov 		/* only revC3 and revE have that feature */
145895b0ef55SBorislav Petkov 		if (boot_cpu_data.x86_model < 4 ||
145995b0ef55SBorislav Petkov 		    (boot_cpu_data.x86_model < 0xa &&
146095b0ef55SBorislav Petkov 		     boot_cpu_data.x86_mask < 3))
146195b0ef55SBorislav Petkov 			return sys_addr;
146295b0ef55SBorislav Petkov 	}
146395b0ef55SBorislav Petkov 
146495b0ef55SBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, SWAP_INTLV_REG, &swap_reg);
146595b0ef55SBorislav Petkov 
146695b0ef55SBorislav Petkov 	if (!(swap_reg & 0x1))
146795b0ef55SBorislav Petkov 		return sys_addr;
146895b0ef55SBorislav Petkov 
146995b0ef55SBorislav Petkov 	swap_base	= (swap_reg >> 3) & 0x7f;
147095b0ef55SBorislav Petkov 	swap_limit	= (swap_reg >> 11) & 0x7f;
147195b0ef55SBorislav Petkov 	rgn_size	= (swap_reg >> 20) & 0x7f;
147295b0ef55SBorislav Petkov 	tmp_addr	= sys_addr >> 27;
147395b0ef55SBorislav Petkov 
147495b0ef55SBorislav Petkov 	if (!(sys_addr >> 34) &&
147595b0ef55SBorislav Petkov 	    (((tmp_addr >= swap_base) &&
147695b0ef55SBorislav Petkov 	     (tmp_addr <= swap_limit)) ||
147795b0ef55SBorislav Petkov 	     (tmp_addr < rgn_size)))
147895b0ef55SBorislav Petkov 		return sys_addr ^ (u64)swap_base << 27;
147995b0ef55SBorislav Petkov 
148095b0ef55SBorislav Petkov 	return sys_addr;
148195b0ef55SBorislav Petkov }
148295b0ef55SBorislav Petkov 
1483f71d0a05SDoug Thompson /* For a given @dram_range, check if @sys_addr falls within it. */
1484e761359aSBorislav Petkov static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
1485f71d0a05SDoug Thompson 				  u64 sys_addr, int *nid, int *chan_sel)
1486f71d0a05SDoug Thompson {
1487229a7a11SBorislav Petkov 	int cs_found = -EINVAL;
1488c8e518d5SBorislav Petkov 	u64 chan_addr;
14895d4b58e8SBorislav Petkov 	u32 dct_sel_base;
149011c75eadSBorislav Petkov 	u8 channel;
1491229a7a11SBorislav Petkov 	bool high_range = false;
1492f71d0a05SDoug Thompson 
14937f19bf75SBorislav Petkov 	u8 node_id    = dram_dst_node(pvt, range);
1494229a7a11SBorislav Petkov 	u8 intlv_en   = dram_intlv_en(pvt, range);
14957f19bf75SBorislav Petkov 	u32 intlv_sel = dram_intlv_sel(pvt, range);
1496f71d0a05SDoug Thompson 
1497956b9ba1SJoe Perches 	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
1498c8e518d5SBorislav Petkov 		 range, sys_addr, get_dram_limit(pvt, range));
1499f71d0a05SDoug Thompson 
1500355fba60SBorislav Petkov 	if (dhar_valid(pvt) &&
1501355fba60SBorislav Petkov 	    dhar_base(pvt) <= sys_addr &&
1502355fba60SBorislav Petkov 	    sys_addr < BIT_64(32)) {
1503355fba60SBorislav Petkov 		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
1504355fba60SBorislav Petkov 			    sys_addr);
1505f71d0a05SDoug Thompson 		return -EINVAL;
1506355fba60SBorislav Petkov 	}
1507355fba60SBorislav Petkov 
1508f030ddfbSBorislav Petkov 	if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en)))
1509355fba60SBorislav Petkov 		return -EINVAL;
1510f71d0a05SDoug Thompson 
1511b15f0fcaSBorislav Petkov 	sys_addr = f1x_swap_interleaved_region(pvt, sys_addr);
151295b0ef55SBorislav Petkov 
1513f71d0a05SDoug Thompson 	dct_sel_base = dct_sel_baseaddr(pvt);
1514f71d0a05SDoug Thompson 
1515f71d0a05SDoug Thompson 	/*
1516f71d0a05SDoug Thompson 	 * check whether addresses >= DctSelBaseAddr[47:27] are to be used to
1517f71d0a05SDoug Thompson 	 * select between DCT0 and DCT1.
1518f71d0a05SDoug Thompson 	 */
1519f71d0a05SDoug Thompson 	if (dct_high_range_enabled(pvt) &&
1520f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt) &&
1521f71d0a05SDoug Thompson 	   ((sys_addr >> 27) >= (dct_sel_base >> 11)))
1522229a7a11SBorislav Petkov 		high_range = true;
1523f71d0a05SDoug Thompson 
1524b15f0fcaSBorislav Petkov 	channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en);
1525f71d0a05SDoug Thompson 
1526b15f0fcaSBorislav Petkov 	chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr,
1527c8e518d5SBorislav Petkov 					  high_range, dct_sel_base);
1528f71d0a05SDoug Thompson 
1529e2f79dbdSBorislav Petkov 	/* Remove node interleaving, see F1x120 */
1530e2f79dbdSBorislav Petkov 	if (intlv_en)
1531e2f79dbdSBorislav Petkov 		chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) |
1532e2f79dbdSBorislav Petkov 			    (chan_addr & 0xfff);
1533f71d0a05SDoug Thompson 
15345d4b58e8SBorislav Petkov 	/* remove channel interleave */
1535f71d0a05SDoug Thompson 	if (dct_interleave_enabled(pvt) &&
1536f71d0a05SDoug Thompson 	   !dct_high_range_enabled(pvt) &&
1537f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt)) {
15385d4b58e8SBorislav Petkov 
15395d4b58e8SBorislav Petkov 		if (dct_sel_interleave_addr(pvt) != 1) {
15405d4b58e8SBorislav Petkov 			if (dct_sel_interleave_addr(pvt) == 0x3)
15415d4b58e8SBorislav Petkov 				/* hash 9 */
15425d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 10) << 9) |
15435d4b58e8SBorislav Petkov 					     (chan_addr & 0x1ff);
15445d4b58e8SBorislav Petkov 			else
15455d4b58e8SBorislav Petkov 				/* A[6] or hash 6 */
15465d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 7) << 6) |
15475d4b58e8SBorislav Petkov 					     (chan_addr & 0x3f);
15485d4b58e8SBorislav Petkov 		} else
15495d4b58e8SBorislav Petkov 			/* A[12] */
15505d4b58e8SBorislav Petkov 			chan_addr = ((chan_addr >> 13) << 12) |
15515d4b58e8SBorislav Petkov 				     (chan_addr & 0xfff);
1552f71d0a05SDoug Thompson 	}
1553f71d0a05SDoug Thompson 
1554956b9ba1SJoe Perches 	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
1555f71d0a05SDoug Thompson 
1556b15f0fcaSBorislav Petkov 	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel);
1557f71d0a05SDoug Thompson 
1558f71d0a05SDoug Thompson 	if (cs_found >= 0) {
1559f71d0a05SDoug Thompson 		*nid = node_id;
1560f71d0a05SDoug Thompson 		*chan_sel = channel;
1561f71d0a05SDoug Thompson 	}
1562f71d0a05SDoug Thompson 	return cs_found;
1563f71d0a05SDoug Thompson }
1564f71d0a05SDoug Thompson 
1565b15f0fcaSBorislav Petkov static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt, u64 sys_addr,
1566f71d0a05SDoug Thompson 				       int *node, int *chan_sel)
1567f71d0a05SDoug Thompson {
1568e761359aSBorislav Petkov 	int cs_found = -EINVAL;
1569e761359aSBorislav Petkov 	unsigned range;
1570f71d0a05SDoug Thompson 
15717f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
1572f71d0a05SDoug Thompson 
15737f19bf75SBorislav Petkov 		if (!dram_rw(pvt, range))
1574f71d0a05SDoug Thompson 			continue;
1575f71d0a05SDoug Thompson 
15767f19bf75SBorislav Petkov 		if ((get_dram_base(pvt, range)  <= sys_addr) &&
15777f19bf75SBorislav Petkov 		    (get_dram_limit(pvt, range) >= sys_addr)) {
1578f71d0a05SDoug Thompson 
1579b15f0fcaSBorislav Petkov 			cs_found = f1x_match_to_this_node(pvt, range,
1580f71d0a05SDoug Thompson 							  sys_addr, node,
1581f71d0a05SDoug Thompson 							  chan_sel);
1582f71d0a05SDoug Thompson 			if (cs_found >= 0)
1583f71d0a05SDoug Thompson 				break;
1584f71d0a05SDoug Thompson 		}
1585f71d0a05SDoug Thompson 	}
1586f71d0a05SDoug Thompson 	return cs_found;
1587f71d0a05SDoug Thompson }
1588f71d0a05SDoug Thompson 
1589f71d0a05SDoug Thompson /*
1590bdc30a0cSBorislav Petkov  * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
1591bdc30a0cSBorislav Petkov  * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
1592f71d0a05SDoug Thompson  *
1593bdc30a0cSBorislav Petkov  * The @sys_addr is usually an error address received from the hardware
1594bdc30a0cSBorislav Petkov  * (MCX_ADDR).
1595f71d0a05SDoug Thompson  */
1596b15f0fcaSBorislav Petkov static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
1597f192c7b1SBorislav Petkov 				     u16 syndrome)
1598f71d0a05SDoug Thompson {
1599f71d0a05SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
1600f71d0a05SDoug Thompson 	u32 page, offset;
1601f71d0a05SDoug Thompson 	int nid, csrow, chan = 0;
1602f71d0a05SDoug Thompson 
1603ab5a503cSMauro Carvalho Chehab 	error_address_to_page_and_offset(sys_addr, &page, &offset);
1604ab5a503cSMauro Carvalho Chehab 
1605b15f0fcaSBorislav Petkov 	csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &nid, &chan);
1606f71d0a05SDoug Thompson 
1607bdc30a0cSBorislav Petkov 	if (csrow < 0) {
16089eb07a7fSMauro Carvalho Chehab 		edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
1609ab5a503cSMauro Carvalho Chehab 				     page, offset, syndrome,
1610ab5a503cSMauro Carvalho Chehab 				     -1, -1, -1,
1611ab5a503cSMauro Carvalho Chehab 				     "failed to map error addr to a csrow",
161203f7eae8SMauro Carvalho Chehab 				     "");
1613bdc30a0cSBorislav Petkov 		return;
1614bdc30a0cSBorislav Petkov 	}
1615bdc30a0cSBorislav Petkov 
1616f71d0a05SDoug Thompson 	/*
1617bdc30a0cSBorislav Petkov 	 * We need the syndromes for channel detection only when we're
1618bdc30a0cSBorislav Petkov 	 * ganged. Otherwise @chan should already contain the channel at
1619bdc30a0cSBorislav Petkov 	 * this point.
1620f71d0a05SDoug Thompson 	 */
1621a97fa68eSBorislav Petkov 	if (dct_ganging_enabled(pvt))
1622bfc04aecSBorislav Petkov 		chan = get_channel_from_ecc_syndrome(mci, syndrome);
1623f71d0a05SDoug Thompson 
16249eb07a7fSMauro Carvalho Chehab 	edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
1625ab5a503cSMauro Carvalho Chehab 			     page, offset, syndrome,
1626ab5a503cSMauro Carvalho Chehab 			     csrow, chan, -1,
162703f7eae8SMauro Carvalho Chehab 			     "", "");
1628f71d0a05SDoug Thompson }
1629f71d0a05SDoug Thompson 
1630f71d0a05SDoug Thompson /*
16318566c4dfSBorislav Petkov  * debug routine to display the memory sizes of all logical DIMMs and its
1632cb328507SBorislav Petkov  * CSROWs
1633f71d0a05SDoug Thompson  */
16348c671751SBorislav Petkov static void amd64_debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
1635f71d0a05SDoug Thompson {
1636603adaf6SBorislav Petkov 	int dimm, size0, size1, factor = 0;
1637525a1b20SBorislav Petkov 	u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases;
1638525a1b20SBorislav Petkov 	u32 dbam  = ctrl ? pvt->dbam1 : pvt->dbam0;
1639f71d0a05SDoug Thompson 
16408566c4dfSBorislav Petkov 	if (boot_cpu_data.x86 == 0xf) {
164141d8bfabSBorislav Petkov 		if (pvt->dclr0 & WIDTH_128)
1642603adaf6SBorislav Petkov 			factor = 1;
1643603adaf6SBorislav Petkov 
16448566c4dfSBorislav Petkov 		/* K8 families < revF not supported yet */
16451433eb99SBorislav Petkov 	       if (pvt->ext_model < K8_REV_F)
16468566c4dfSBorislav Petkov 			return;
16478566c4dfSBorislav Petkov 	       else
16488566c4dfSBorislav Petkov 		       WARN_ON(ctrl != 0);
16498566c4dfSBorislav Petkov 	}
16508566c4dfSBorislav Petkov 
16514d796364SBorislav Petkov 	dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1 : pvt->dbam0;
165211c75eadSBorislav Petkov 	dcsb = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->csels[1].csbases
165311c75eadSBorislav Petkov 						   : pvt->csels[0].csbases;
1654f71d0a05SDoug Thompson 
1655956b9ba1SJoe Perches 	edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
1656956b9ba1SJoe Perches 		 ctrl, dbam);
1657f71d0a05SDoug Thompson 
16588566c4dfSBorislav Petkov 	edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
16598566c4dfSBorislav Petkov 
1660f71d0a05SDoug Thompson 	/* Dump memory sizes for DIMM and its CSROWs */
1661f71d0a05SDoug Thompson 	for (dimm = 0; dimm < 4; dimm++) {
1662f71d0a05SDoug Thompson 
1663f71d0a05SDoug Thompson 		size0 = 0;
166411c75eadSBorislav Petkov 		if (dcsb[dimm*2] & DCSB_CS_ENABLE)
166541d8bfabSBorislav Petkov 			size0 = pvt->ops->dbam_to_cs(pvt, ctrl,
166641d8bfabSBorislav Petkov 						     DBAM_DIMM(dimm, dbam));
1667f71d0a05SDoug Thompson 
1668f71d0a05SDoug Thompson 		size1 = 0;
166911c75eadSBorislav Petkov 		if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE)
167041d8bfabSBorislav Petkov 			size1 = pvt->ops->dbam_to_cs(pvt, ctrl,
167141d8bfabSBorislav Petkov 						     DBAM_DIMM(dimm, dbam));
1672f71d0a05SDoug Thompson 
167324f9a7feSBorislav Petkov 		amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
1674603adaf6SBorislav Petkov 				dimm * 2,     size0 << factor,
1675603adaf6SBorislav Petkov 				dimm * 2 + 1, size1 << factor);
1676f71d0a05SDoug Thompson 	}
1677f71d0a05SDoug Thompson }
1678f71d0a05SDoug Thompson 
16794d37607aSDoug Thompson static struct amd64_family_type amd64_family_types[] = {
16804d37607aSDoug Thompson 	[K8_CPUS] = {
16810092b20dSBorislav Petkov 		.ctl_name = "K8",
16828d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
16838d5b5d9cSBorislav Petkov 		.f3_id = PCI_DEVICE_ID_AMD_K8_NB_MISC,
16844d37607aSDoug Thompson 		.ops = {
16854d37607aSDoug Thompson 			.early_channel_count	= k8_early_channel_count,
16864d37607aSDoug Thompson 			.map_sysaddr_to_csrow	= k8_map_sysaddr_to_csrow,
16871433eb99SBorislav Petkov 			.dbam_to_cs		= k8_dbam_to_chip_select,
1688b2b0c605SBorislav Petkov 			.read_dct_pci_cfg	= k8_read_dct_pci_cfg,
16894d37607aSDoug Thompson 		}
16904d37607aSDoug Thompson 	},
16914d37607aSDoug Thompson 	[F10_CPUS] = {
16920092b20dSBorislav Petkov 		.ctl_name = "F10h",
16938d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP,
16948d5b5d9cSBorislav Petkov 		.f3_id = PCI_DEVICE_ID_AMD_10H_NB_MISC,
16954d37607aSDoug Thompson 		.ops = {
16967d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
1697b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
16981433eb99SBorislav Petkov 			.dbam_to_cs		= f10_dbam_to_chip_select,
1699b2b0c605SBorislav Petkov 			.read_dct_pci_cfg	= f10_read_dct_pci_cfg,
1700b2b0c605SBorislav Petkov 		}
1701b2b0c605SBorislav Petkov 	},
1702b2b0c605SBorislav Petkov 	[F15_CPUS] = {
1703b2b0c605SBorislav Petkov 		.ctl_name = "F15h",
1704df71a053SBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1,
1705df71a053SBorislav Petkov 		.f3_id = PCI_DEVICE_ID_AMD_15H_NB_F3,
1706b2b0c605SBorislav Petkov 		.ops = {
17077d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
1708b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
170941d8bfabSBorislav Petkov 			.dbam_to_cs		= f15_dbam_to_chip_select,
1710b2b0c605SBorislav Petkov 			.read_dct_pci_cfg	= f15_read_dct_pci_cfg,
17114d37607aSDoug Thompson 		}
17124d37607aSDoug Thompson 	},
17134d37607aSDoug Thompson };
17144d37607aSDoug Thompson 
17154d37607aSDoug Thompson static struct pci_dev *pci_get_related_function(unsigned int vendor,
17164d37607aSDoug Thompson 						unsigned int device,
17174d37607aSDoug Thompson 						struct pci_dev *related)
17184d37607aSDoug Thompson {
17194d37607aSDoug Thompson 	struct pci_dev *dev = NULL;
17204d37607aSDoug Thompson 
17214d37607aSDoug Thompson 	dev = pci_get_device(vendor, device, dev);
17224d37607aSDoug Thompson 	while (dev) {
17234d37607aSDoug Thompson 		if ((dev->bus->number == related->bus->number) &&
17244d37607aSDoug Thompson 		    (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn)))
17254d37607aSDoug Thompson 			break;
17264d37607aSDoug Thompson 		dev = pci_get_device(vendor, device, dev);
17274d37607aSDoug Thompson 	}
17284d37607aSDoug Thompson 
17294d37607aSDoug Thompson 	return dev;
17304d37607aSDoug Thompson }
17314d37607aSDoug Thompson 
1732b1289d6fSDoug Thompson /*
1733bfc04aecSBorislav Petkov  * These are tables of eigenvectors (one per line) which can be used for the
1734bfc04aecSBorislav Petkov  * construction of the syndrome tables. The modified syndrome search algorithm
1735bfc04aecSBorislav Petkov  * uses those to find the symbol in error and thus the DIMM.
1736b1289d6fSDoug Thompson  *
1737bfc04aecSBorislav Petkov  * Algorithm courtesy of Ross LaFetra from AMD.
1738b1289d6fSDoug Thompson  */
1739bfc04aecSBorislav Petkov static u16 x4_vectors[] = {
1740bfc04aecSBorislav Petkov 	0x2f57, 0x1afe, 0x66cc, 0xdd88,
1741bfc04aecSBorislav Petkov 	0x11eb, 0x3396, 0x7f4c, 0xeac8,
1742bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008,
1743bfc04aecSBorislav Petkov 	0x1013, 0x3032, 0x4044, 0x8088,
1744bfc04aecSBorislav Petkov 	0x106b, 0x30d6, 0x70fc, 0xe0a8,
1745bfc04aecSBorislav Petkov 	0x4857, 0xc4fe, 0x13cc, 0x3288,
1746bfc04aecSBorislav Petkov 	0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
1747bfc04aecSBorislav Petkov 	0x1f39, 0x251e, 0xbd6c, 0x6bd8,
1748bfc04aecSBorislav Petkov 	0x15c1, 0x2a42, 0x89ac, 0x4758,
1749bfc04aecSBorislav Petkov 	0x2b03, 0x1602, 0x4f0c, 0xca08,
1750bfc04aecSBorislav Petkov 	0x1f07, 0x3a0e, 0x6b04, 0xbd08,
1751bfc04aecSBorislav Petkov 	0x8ba7, 0x465e, 0x244c, 0x1cc8,
1752bfc04aecSBorislav Petkov 	0x2b87, 0x164e, 0x642c, 0xdc18,
1753bfc04aecSBorislav Petkov 	0x40b9, 0x80de, 0x1094, 0x20e8,
1754bfc04aecSBorislav Petkov 	0x27db, 0x1eb6, 0x9dac, 0x7b58,
1755bfc04aecSBorislav Petkov 	0x11c1, 0x2242, 0x84ac, 0x4c58,
1756bfc04aecSBorislav Petkov 	0x1be5, 0x2d7a, 0x5e34, 0xa718,
1757bfc04aecSBorislav Petkov 	0x4b39, 0x8d1e, 0x14b4, 0x28d8,
1758bfc04aecSBorislav Petkov 	0x4c97, 0xc87e, 0x11fc, 0x33a8,
1759bfc04aecSBorislav Petkov 	0x8e97, 0x497e, 0x2ffc, 0x1aa8,
1760bfc04aecSBorislav Petkov 	0x16b3, 0x3d62, 0x4f34, 0x8518,
1761bfc04aecSBorislav Petkov 	0x1e2f, 0x391a, 0x5cac, 0xf858,
1762bfc04aecSBorislav Petkov 	0x1d9f, 0x3b7a, 0x572c, 0xfe18,
1763bfc04aecSBorislav Petkov 	0x15f5, 0x2a5a, 0x5264, 0xa3b8,
1764bfc04aecSBorislav Petkov 	0x1dbb, 0x3b66, 0x715c, 0xe3f8,
1765bfc04aecSBorislav Petkov 	0x4397, 0xc27e, 0x17fc, 0x3ea8,
1766bfc04aecSBorislav Petkov 	0x1617, 0x3d3e, 0x6464, 0xb8b8,
1767bfc04aecSBorislav Petkov 	0x23ff, 0x12aa, 0xab6c, 0x56d8,
1768bfc04aecSBorislav Petkov 	0x2dfb, 0x1ba6, 0x913c, 0x7328,
1769bfc04aecSBorislav Petkov 	0x185d, 0x2ca6, 0x7914, 0x9e28,
1770bfc04aecSBorislav Petkov 	0x171b, 0x3e36, 0x7d7c, 0xebe8,
1771bfc04aecSBorislav Petkov 	0x4199, 0x82ee, 0x19f4, 0x2e58,
1772bfc04aecSBorislav Petkov 	0x4807, 0xc40e, 0x130c, 0x3208,
1773bfc04aecSBorislav Petkov 	0x1905, 0x2e0a, 0x5804, 0xac08,
1774bfc04aecSBorislav Petkov 	0x213f, 0x132a, 0xadfc, 0x5ba8,
1775bfc04aecSBorislav Petkov 	0x19a9, 0x2efe, 0xb5cc, 0x6f88,
1776b1289d6fSDoug Thompson };
1777b1289d6fSDoug Thompson 
1778bfc04aecSBorislav Petkov static u16 x8_vectors[] = {
1779bfc04aecSBorislav Petkov 	0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
1780bfc04aecSBorislav Petkov 	0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
1781bfc04aecSBorislav Petkov 	0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
1782bfc04aecSBorislav Petkov 	0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
1783bfc04aecSBorislav Petkov 	0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
1784bfc04aecSBorislav Petkov 	0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
1785bfc04aecSBorislav Petkov 	0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
1786bfc04aecSBorislav Petkov 	0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
1787bfc04aecSBorislav Petkov 	0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
1788bfc04aecSBorislav Petkov 	0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
1789bfc04aecSBorislav Petkov 	0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
1790bfc04aecSBorislav Petkov 	0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
1791bfc04aecSBorislav Petkov 	0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
1792bfc04aecSBorislav Petkov 	0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
1793bfc04aecSBorislav Petkov 	0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
1794bfc04aecSBorislav Petkov 	0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
1795bfc04aecSBorislav Petkov 	0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
1796bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
1797bfc04aecSBorislav Petkov 	0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
1798bfc04aecSBorislav Petkov };
1799bfc04aecSBorislav Petkov 
1800d34a6ecdSBorislav Petkov static int decode_syndrome(u16 syndrome, u16 *vectors, unsigned num_vecs,
1801d34a6ecdSBorislav Petkov 			   unsigned v_dim)
1802b1289d6fSDoug Thompson {
1803bfc04aecSBorislav Petkov 	unsigned int i, err_sym;
1804b1289d6fSDoug Thompson 
1805bfc04aecSBorislav Petkov 	for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
1806bfc04aecSBorislav Petkov 		u16 s = syndrome;
1807d34a6ecdSBorislav Petkov 		unsigned v_idx =  err_sym * v_dim;
1808d34a6ecdSBorislav Petkov 		unsigned v_end = (err_sym + 1) * v_dim;
1809b1289d6fSDoug Thompson 
1810bfc04aecSBorislav Petkov 		/* walk over all 16 bits of the syndrome */
1811bfc04aecSBorislav Petkov 		for (i = 1; i < (1U << 16); i <<= 1) {
1812bfc04aecSBorislav Petkov 
1813bfc04aecSBorislav Petkov 			/* if bit is set in that eigenvector... */
1814bfc04aecSBorislav Petkov 			if (v_idx < v_end && vectors[v_idx] & i) {
1815bfc04aecSBorislav Petkov 				u16 ev_comp = vectors[v_idx++];
1816bfc04aecSBorislav Petkov 
1817bfc04aecSBorislav Petkov 				/* ... and bit set in the modified syndrome, */
1818bfc04aecSBorislav Petkov 				if (s & i) {
1819bfc04aecSBorislav Petkov 					/* remove it. */
1820bfc04aecSBorislav Petkov 					s ^= ev_comp;
1821bfc04aecSBorislav Petkov 
1822bfc04aecSBorislav Petkov 					if (!s)
1823bfc04aecSBorislav Petkov 						return err_sym;
1824bfc04aecSBorislav Petkov 				}
1825bfc04aecSBorislav Petkov 
1826bfc04aecSBorislav Petkov 			} else if (s & i)
1827bfc04aecSBorislav Petkov 				/* can't get to zero, move to next symbol */
1828bfc04aecSBorislav Petkov 				break;
1829bfc04aecSBorislav Petkov 		}
1830b1289d6fSDoug Thompson 	}
1831b1289d6fSDoug Thompson 
1832956b9ba1SJoe Perches 	edac_dbg(0, "syndrome(%x) not found\n", syndrome);
1833b1289d6fSDoug Thompson 	return -1;
1834b1289d6fSDoug Thompson }
1835d27bf6faSDoug Thompson 
1836bfc04aecSBorislav Petkov static int map_err_sym_to_channel(int err_sym, int sym_size)
1837bfc04aecSBorislav Petkov {
1838bfc04aecSBorislav Petkov 	if (sym_size == 4)
1839bfc04aecSBorislav Petkov 		switch (err_sym) {
1840bfc04aecSBorislav Petkov 		case 0x20:
1841bfc04aecSBorislav Petkov 		case 0x21:
1842bfc04aecSBorislav Petkov 			return 0;
1843bfc04aecSBorislav Petkov 			break;
1844bfc04aecSBorislav Petkov 		case 0x22:
1845bfc04aecSBorislav Petkov 		case 0x23:
1846bfc04aecSBorislav Petkov 			return 1;
1847bfc04aecSBorislav Petkov 			break;
1848bfc04aecSBorislav Petkov 		default:
1849bfc04aecSBorislav Petkov 			return err_sym >> 4;
1850bfc04aecSBorislav Petkov 			break;
1851bfc04aecSBorislav Petkov 		}
1852bfc04aecSBorislav Petkov 	/* x8 symbols */
1853bfc04aecSBorislav Petkov 	else
1854bfc04aecSBorislav Petkov 		switch (err_sym) {
1855bfc04aecSBorislav Petkov 		/* imaginary bits not in a DIMM */
1856bfc04aecSBorislav Petkov 		case 0x10:
1857bfc04aecSBorislav Petkov 			WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
1858bfc04aecSBorislav Petkov 					  err_sym);
1859bfc04aecSBorislav Petkov 			return -1;
1860bfc04aecSBorislav Petkov 			break;
1861bfc04aecSBorislav Petkov 
1862bfc04aecSBorislav Petkov 		case 0x11:
1863bfc04aecSBorislav Petkov 			return 0;
1864bfc04aecSBorislav Petkov 			break;
1865bfc04aecSBorislav Petkov 		case 0x12:
1866bfc04aecSBorislav Petkov 			return 1;
1867bfc04aecSBorislav Petkov 			break;
1868bfc04aecSBorislav Petkov 		default:
1869bfc04aecSBorislav Petkov 			return err_sym >> 3;
1870bfc04aecSBorislav Petkov 			break;
1871bfc04aecSBorislav Petkov 		}
1872bfc04aecSBorislav Petkov 	return -1;
1873bfc04aecSBorislav Petkov }
1874bfc04aecSBorislav Petkov 
1875bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
1876bfc04aecSBorislav Petkov {
1877bfc04aecSBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
1878ad6a32e9SBorislav Petkov 	int err_sym = -1;
1879bfc04aecSBorislav Petkov 
1880a3b7db09SBorislav Petkov 	if (pvt->ecc_sym_sz == 8)
1881bfc04aecSBorislav Petkov 		err_sym = decode_syndrome(syndrome, x8_vectors,
1882ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x8_vectors),
1883a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
1884a3b7db09SBorislav Petkov 	else if (pvt->ecc_sym_sz == 4)
1885ad6a32e9SBorislav Petkov 		err_sym = decode_syndrome(syndrome, x4_vectors,
1886ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x4_vectors),
1887a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
1888ad6a32e9SBorislav Petkov 	else {
1889a3b7db09SBorislav Petkov 		amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz);
1890ad6a32e9SBorislav Petkov 		return err_sym;
1891bfc04aecSBorislav Petkov 	}
1892ad6a32e9SBorislav Petkov 
1893a3b7db09SBorislav Petkov 	return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz);
189441c31044SBorislav Petkov }
1895bfc04aecSBorislav Petkov 
1896d27bf6faSDoug Thompson /*
1897d27bf6faSDoug Thompson  * Handle any Correctable Errors (CEs) that have occurred. Check for valid ERROR
1898d27bf6faSDoug Thompson  * ADDRESS and process.
1899d27bf6faSDoug Thompson  */
1900f192c7b1SBorislav Petkov static void amd64_handle_ce(struct mem_ctl_info *mci, struct mce *m)
1901d27bf6faSDoug Thompson {
1902d27bf6faSDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
190344e9e2eeSBorislav Petkov 	u64 sys_addr;
1904f192c7b1SBorislav Petkov 	u16 syndrome;
1905d27bf6faSDoug Thompson 
1906d27bf6faSDoug Thompson 	/* Ensure that the Error Address is VALID */
1907f192c7b1SBorislav Petkov 	if (!(m->status & MCI_STATUS_ADDRV)) {
190824f9a7feSBorislav Petkov 		amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n");
19099eb07a7fSMauro Carvalho Chehab 		edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
1910ab5a503cSMauro Carvalho Chehab 				     0, 0, 0,
1911ab5a503cSMauro Carvalho Chehab 				     -1, -1, -1,
1912ab5a503cSMauro Carvalho Chehab 				     "HW has no ERROR_ADDRESS available",
191303f7eae8SMauro Carvalho Chehab 				     "");
1914d27bf6faSDoug Thompson 		return;
1915d27bf6faSDoug Thompson 	}
1916d27bf6faSDoug Thompson 
191770046624SBorislav Petkov 	sys_addr = get_error_address(m);
1918f192c7b1SBorislav Petkov 	syndrome = extract_syndrome(m->status);
1919d27bf6faSDoug Thompson 
192024f9a7feSBorislav Petkov 	amd64_mc_err(mci, "CE ERROR_ADDRESS= 0x%llx\n", sys_addr);
1921d27bf6faSDoug Thompson 
1922f192c7b1SBorislav Petkov 	pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, syndrome);
1923d27bf6faSDoug Thompson }
1924d27bf6faSDoug Thompson 
1925d27bf6faSDoug Thompson /* Handle any Un-correctable Errors (UEs) */
1926f192c7b1SBorislav Petkov static void amd64_handle_ue(struct mem_ctl_info *mci, struct mce *m)
1927d27bf6faSDoug Thompson {
19281f6bcee7SBorislav Petkov 	struct mem_ctl_info *log_mci, *src_mci = NULL;
1929d27bf6faSDoug Thompson 	int csrow;
193044e9e2eeSBorislav Petkov 	u64 sys_addr;
1931d27bf6faSDoug Thompson 	u32 page, offset;
1932d27bf6faSDoug Thompson 
1933d27bf6faSDoug Thompson 	log_mci = mci;
1934d27bf6faSDoug Thompson 
1935f192c7b1SBorislav Petkov 	if (!(m->status & MCI_STATUS_ADDRV)) {
193624f9a7feSBorislav Petkov 		amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n");
19379eb07a7fSMauro Carvalho Chehab 		edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
1938ab5a503cSMauro Carvalho Chehab 				     0, 0, 0,
1939ab5a503cSMauro Carvalho Chehab 				     -1, -1, -1,
1940ab5a503cSMauro Carvalho Chehab 				     "HW has no ERROR_ADDRESS available",
194103f7eae8SMauro Carvalho Chehab 				     "");
1942d27bf6faSDoug Thompson 		return;
1943d27bf6faSDoug Thompson 	}
1944d27bf6faSDoug Thompson 
194570046624SBorislav Petkov 	sys_addr = get_error_address(m);
1946ab5a503cSMauro Carvalho Chehab 	error_address_to_page_and_offset(sys_addr, &page, &offset);
1947d27bf6faSDoug Thompson 
1948d27bf6faSDoug Thompson 	/*
1949d27bf6faSDoug Thompson 	 * Find out which node the error address belongs to. This may be
1950d27bf6faSDoug Thompson 	 * different from the node that detected the error.
1951d27bf6faSDoug Thompson 	 */
195244e9e2eeSBorislav Petkov 	src_mci = find_mc_by_sys_addr(mci, sys_addr);
1953d27bf6faSDoug Thompson 	if (!src_mci) {
195424f9a7feSBorislav Petkov 		amd64_mc_err(mci, "ERROR ADDRESS (0x%lx) NOT mapped to a MC\n",
195544e9e2eeSBorislav Petkov 				  (unsigned long)sys_addr);
19569eb07a7fSMauro Carvalho Chehab 		edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
1957ab5a503cSMauro Carvalho Chehab 				     page, offset, 0,
1958ab5a503cSMauro Carvalho Chehab 				     -1, -1, -1,
1959075f3090SMauro Carvalho Chehab 				     "ERROR ADDRESS NOT mapped to a MC",
196003f7eae8SMauro Carvalho Chehab 				     "");
1961d27bf6faSDoug Thompson 		return;
1962d27bf6faSDoug Thompson 	}
1963d27bf6faSDoug Thompson 
1964d27bf6faSDoug Thompson 	log_mci = src_mci;
1965d27bf6faSDoug Thompson 
196644e9e2eeSBorislav Petkov 	csrow = sys_addr_to_csrow(log_mci, sys_addr);
1967d27bf6faSDoug Thompson 	if (csrow < 0) {
196824f9a7feSBorislav Petkov 		amd64_mc_err(mci, "ERROR_ADDRESS (0x%lx) NOT mapped to CS\n",
196944e9e2eeSBorislav Petkov 				  (unsigned long)sys_addr);
19709eb07a7fSMauro Carvalho Chehab 		edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
1971ab5a503cSMauro Carvalho Chehab 				     page, offset, 0,
1972ab5a503cSMauro Carvalho Chehab 				     -1, -1, -1,
1973ab5a503cSMauro Carvalho Chehab 				     "ERROR ADDRESS NOT mapped to CS",
197403f7eae8SMauro Carvalho Chehab 				     "");
1975d27bf6faSDoug Thompson 	} else {
19769eb07a7fSMauro Carvalho Chehab 		edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
1977ab5a503cSMauro Carvalho Chehab 				     page, offset, 0,
1978ab5a503cSMauro Carvalho Chehab 				     csrow, -1, -1,
197903f7eae8SMauro Carvalho Chehab 				     "", "");
1980d27bf6faSDoug Thompson 	}
1981d27bf6faSDoug Thompson }
1982d27bf6faSDoug Thompson 
1983549d042dSBorislav Petkov static inline void __amd64_decode_bus_error(struct mem_ctl_info *mci,
1984f192c7b1SBorislav Petkov 					    struct mce *m)
1985d27bf6faSDoug Thompson {
1986f192c7b1SBorislav Petkov 	u16 ec = EC(m->status);
1987f192c7b1SBorislav Petkov 	u8 xec = XEC(m->status, 0x1f);
1988f192c7b1SBorislav Petkov 	u8 ecc_type = (m->status >> 45) & 0x3;
1989d27bf6faSDoug Thompson 
1990b70ef010SBorislav Petkov 	/* Bail early out if this was an 'observed' error */
19915980bb9cSBorislav Petkov 	if (PP(ec) == NBSL_PP_OBS)
1992b70ef010SBorislav Petkov 		return;
1993d27bf6faSDoug Thompson 
1994ecaf5606SBorislav Petkov 	/* Do only ECC errors */
1995ecaf5606SBorislav Petkov 	if (xec && xec != F10_NBSL_EXT_ERR_ECC)
1996d27bf6faSDoug Thompson 		return;
1997d27bf6faSDoug Thompson 
1998ecaf5606SBorislav Petkov 	if (ecc_type == 2)
1999f192c7b1SBorislav Petkov 		amd64_handle_ce(mci, m);
2000ecaf5606SBorislav Petkov 	else if (ecc_type == 1)
2001f192c7b1SBorislav Petkov 		amd64_handle_ue(mci, m);
2002d27bf6faSDoug Thompson }
2003d27bf6faSDoug Thompson 
2004b0b07a2bSBorislav Petkov void amd64_decode_bus_error(int node_id, struct mce *m)
2005d27bf6faSDoug Thompson {
2006b0b07a2bSBorislav Petkov 	__amd64_decode_bus_error(mcis[node_id], m);
2007d27bf6faSDoug Thompson }
2008d27bf6faSDoug Thompson 
20090ec449eeSDoug Thompson /*
20108d5b5d9cSBorislav Petkov  * Use pvt->F2 which contains the F2 CPU PCI device to get the related
2011bbd0c1f6SBorislav Petkov  * F1 (AddrMap) and F3 (Misc) devices. Return negative value on error.
20120ec449eeSDoug Thompson  */
2013360b7f3cSBorislav Petkov static int reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 f1_id, u16 f3_id)
20140ec449eeSDoug Thompson {
20150ec449eeSDoug Thompson 	/* Reserve the ADDRESS MAP Device */
20168d5b5d9cSBorislav Petkov 	pvt->F1 = pci_get_related_function(pvt->F2->vendor, f1_id, pvt->F2);
20178d5b5d9cSBorislav Petkov 	if (!pvt->F1) {
201824f9a7feSBorislav Petkov 		amd64_err("error address map device not found: "
20190ec449eeSDoug Thompson 			  "vendor %x device 0x%x (broken BIOS?)\n",
2020bbd0c1f6SBorislav Petkov 			  PCI_VENDOR_ID_AMD, f1_id);
2021bbd0c1f6SBorislav Petkov 		return -ENODEV;
20220ec449eeSDoug Thompson 	}
20230ec449eeSDoug Thompson 
20240ec449eeSDoug Thompson 	/* Reserve the MISC Device */
20258d5b5d9cSBorislav Petkov 	pvt->F3 = pci_get_related_function(pvt->F2->vendor, f3_id, pvt->F2);
20268d5b5d9cSBorislav Petkov 	if (!pvt->F3) {
20278d5b5d9cSBorislav Petkov 		pci_dev_put(pvt->F1);
20288d5b5d9cSBorislav Petkov 		pvt->F1 = NULL;
20290ec449eeSDoug Thompson 
203024f9a7feSBorislav Petkov 		amd64_err("error F3 device not found: "
20310ec449eeSDoug Thompson 			  "vendor %x device 0x%x (broken BIOS?)\n",
2032bbd0c1f6SBorislav Petkov 			  PCI_VENDOR_ID_AMD, f3_id);
20338d5b5d9cSBorislav Petkov 
2034bbd0c1f6SBorislav Petkov 		return -ENODEV;
20350ec449eeSDoug Thompson 	}
2036956b9ba1SJoe Perches 	edac_dbg(1, "F1: %s\n", pci_name(pvt->F1));
2037956b9ba1SJoe Perches 	edac_dbg(1, "F2: %s\n", pci_name(pvt->F2));
2038956b9ba1SJoe Perches 	edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
20390ec449eeSDoug Thompson 
20400ec449eeSDoug Thompson 	return 0;
20410ec449eeSDoug Thompson }
20420ec449eeSDoug Thompson 
2043360b7f3cSBorislav Petkov static void free_mc_sibling_devs(struct amd64_pvt *pvt)
20440ec449eeSDoug Thompson {
20458d5b5d9cSBorislav Petkov 	pci_dev_put(pvt->F1);
20468d5b5d9cSBorislav Petkov 	pci_dev_put(pvt->F3);
20470ec449eeSDoug Thompson }
20480ec449eeSDoug Thompson 
20490ec449eeSDoug Thompson /*
20500ec449eeSDoug Thompson  * Retrieve the hardware registers of the memory controller (this includes the
20510ec449eeSDoug Thompson  * 'Address Map' and 'Misc' device regs)
20520ec449eeSDoug Thompson  */
2053360b7f3cSBorislav Petkov static void read_mc_regs(struct amd64_pvt *pvt)
20540ec449eeSDoug Thompson {
2055a3b7db09SBorislav Petkov 	struct cpuinfo_x86 *c = &boot_cpu_data;
20560ec449eeSDoug Thompson 	u64 msr_val;
2057ad6a32e9SBorislav Petkov 	u32 tmp;
2058e761359aSBorislav Petkov 	unsigned range;
20590ec449eeSDoug Thompson 
20600ec449eeSDoug Thompson 	/*
20610ec449eeSDoug Thompson 	 * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
20620ec449eeSDoug Thompson 	 * those are Read-As-Zero
20630ec449eeSDoug Thompson 	 */
2064e97f8bb8SBorislav Petkov 	rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
2065956b9ba1SJoe Perches 	edac_dbg(0, "  TOP_MEM:  0x%016llx\n", pvt->top_mem);
20660ec449eeSDoug Thompson 
20670ec449eeSDoug Thompson 	/* check first whether TOP_MEM2 is enabled */
20680ec449eeSDoug Thompson 	rdmsrl(MSR_K8_SYSCFG, msr_val);
20690ec449eeSDoug Thompson 	if (msr_val & (1U << 21)) {
2070e97f8bb8SBorislav Petkov 		rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
2071956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
20720ec449eeSDoug Thompson 	} else
2073956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2 disabled\n");
20740ec449eeSDoug Thompson 
20755980bb9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap);
20760ec449eeSDoug Thompson 
20775a5d2371SBorislav Petkov 	read_dram_ctl_register(pvt);
20780ec449eeSDoug Thompson 
20797f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
20807f19bf75SBorislav Petkov 		u8 rw;
20810ec449eeSDoug Thompson 
20827f19bf75SBorislav Petkov 		/* read settings for this DRAM range */
20837f19bf75SBorislav Petkov 		read_dram_base_limit_regs(pvt, range);
2084e97f8bb8SBorislav Petkov 
20857f19bf75SBorislav Petkov 		rw = dram_rw(pvt, range);
20867f19bf75SBorislav Petkov 		if (!rw)
20877f19bf75SBorislav Petkov 			continue;
20887f19bf75SBorislav Petkov 
2089956b9ba1SJoe Perches 		edac_dbg(1, "  DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
20907f19bf75SBorislav Petkov 			 range,
20917f19bf75SBorislav Petkov 			 get_dram_base(pvt, range),
20927f19bf75SBorislav Petkov 			 get_dram_limit(pvt, range));
20937f19bf75SBorislav Petkov 
2094956b9ba1SJoe Perches 		edac_dbg(1, "   IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
20957f19bf75SBorislav Petkov 			 dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
20967f19bf75SBorislav Petkov 			 (rw & 0x1) ? "R" : "-",
20977f19bf75SBorislav Petkov 			 (rw & 0x2) ? "W" : "-",
20987f19bf75SBorislav Petkov 			 dram_intlv_sel(pvt, range),
20997f19bf75SBorislav Petkov 			 dram_dst_node(pvt, range));
21000ec449eeSDoug Thompson 	}
21010ec449eeSDoug Thompson 
2102b2b0c605SBorislav Petkov 	read_dct_base_mask(pvt);
21030ec449eeSDoug Thompson 
2104bc21fa57SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar);
2105525a1b20SBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, DBAM0, &pvt->dbam0);
21060ec449eeSDoug Thompson 
21078d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare);
21080ec449eeSDoug Thompson 
2109cb328507SBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, DCLR0, &pvt->dclr0);
2110cb328507SBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, DCHR0, &pvt->dchr0);
21110ec449eeSDoug Thompson 
211278da121eSBorislav Petkov 	if (!dct_ganging_enabled(pvt)) {
2113cb328507SBorislav Petkov 		amd64_read_dct_pci_cfg(pvt, DCLR1, &pvt->dclr1);
2114cb328507SBorislav Petkov 		amd64_read_dct_pci_cfg(pvt, DCHR1, &pvt->dchr1);
21150ec449eeSDoug Thompson 	}
2116b2b0c605SBorislav Petkov 
2117a3b7db09SBorislav Petkov 	pvt->ecc_sym_sz = 4;
2118a3b7db09SBorislav Petkov 
2119a3b7db09SBorislav Petkov 	if (c->x86 >= 0x10) {
21208d5b5d9cSBorislav Petkov 		amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp);
2121525a1b20SBorislav Petkov 		amd64_read_dct_pci_cfg(pvt, DBAM1, &pvt->dbam1);
2122a3b7db09SBorislav Petkov 
2123a3b7db09SBorislav Petkov 		/* F10h, revD and later can do x8 ECC too */
2124a3b7db09SBorislav Petkov 		if ((c->x86 > 0x10 || c->x86_model > 7) && tmp & BIT(25))
2125a3b7db09SBorislav Petkov 			pvt->ecc_sym_sz = 8;
2126525a1b20SBorislav Petkov 	}
2127b2b0c605SBorislav Petkov 	dump_misc_regs(pvt);
21280ec449eeSDoug Thompson }
21290ec449eeSDoug Thompson 
21300ec449eeSDoug Thompson /*
21310ec449eeSDoug Thompson  * NOTE: CPU Revision Dependent code
21320ec449eeSDoug Thompson  *
21330ec449eeSDoug Thompson  * Input:
213411c75eadSBorislav Petkov  *	@csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1)
21350ec449eeSDoug Thompson  *	k8 private pointer to -->
21360ec449eeSDoug Thompson  *			DRAM Bank Address mapping register
21370ec449eeSDoug Thompson  *			node_id
21380ec449eeSDoug Thompson  *			DCL register where dual_channel_active is
21390ec449eeSDoug Thompson  *
21400ec449eeSDoug Thompson  * The DBAM register consists of 4 sets of 4 bits each definitions:
21410ec449eeSDoug Thompson  *
21420ec449eeSDoug Thompson  * Bits:	CSROWs
21430ec449eeSDoug Thompson  * 0-3		CSROWs 0 and 1
21440ec449eeSDoug Thompson  * 4-7		CSROWs 2 and 3
21450ec449eeSDoug Thompson  * 8-11		CSROWs 4 and 5
21460ec449eeSDoug Thompson  * 12-15	CSROWs 6 and 7
21470ec449eeSDoug Thompson  *
21480ec449eeSDoug Thompson  * Values range from: 0 to 15
21490ec449eeSDoug Thompson  * The meaning of the values depends on CPU revision and dual-channel state,
21500ec449eeSDoug Thompson  * see relevant BKDG more info.
21510ec449eeSDoug Thompson  *
21520ec449eeSDoug Thompson  * The memory controller provides for total of only 8 CSROWs in its current
21530ec449eeSDoug Thompson  * architecture. Each "pair" of CSROWs normally represents just one DIMM in
21540ec449eeSDoug Thompson  * single channel or two (2) DIMMs in dual channel mode.
21550ec449eeSDoug Thompson  *
21560ec449eeSDoug Thompson  * The following code logic collapses the various tables for CSROW based on CPU
21570ec449eeSDoug Thompson  * revision.
21580ec449eeSDoug Thompson  *
21590ec449eeSDoug Thompson  * Returns:
21600ec449eeSDoug Thompson  *	The number of PAGE_SIZE pages on the specified CSROW number it
21610ec449eeSDoug Thompson  *	encompasses
21620ec449eeSDoug Thompson  *
21630ec449eeSDoug Thompson  */
216441d8bfabSBorislav Petkov static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr)
21650ec449eeSDoug Thompson {
21661433eb99SBorislav Petkov 	u32 cs_mode, nr_pages;
2167f92cae45SAshish Shenoy 	u32 dbam = dct ? pvt->dbam1 : pvt->dbam0;
21680ec449eeSDoug Thompson 
21690ec449eeSDoug Thompson 	/*
21700ec449eeSDoug Thompson 	 * The math on this doesn't look right on the surface because x/2*4 can
21710ec449eeSDoug Thompson 	 * be simplified to x*2 but this expression makes use of the fact that
21720ec449eeSDoug Thompson 	 * it is integral math where 1/2=0. This intermediate value becomes the
21730ec449eeSDoug Thompson 	 * number of bits to shift the DBAM register to extract the proper CSROW
21740ec449eeSDoug Thompson 	 * field.
21750ec449eeSDoug Thompson 	 */
2176f92cae45SAshish Shenoy 	cs_mode =  (dbam >> ((csrow_nr / 2) * 4)) & 0xF;
21770ec449eeSDoug Thompson 
217841d8bfabSBorislav Petkov 	nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode) << (20 - PAGE_SHIFT);
21790ec449eeSDoug Thompson 
2180956b9ba1SJoe Perches 	edac_dbg(0, "  (csrow=%d) DBAM map index= %d\n", csrow_nr, cs_mode);
2181956b9ba1SJoe Perches 	edac_dbg(0, "    nr_pages/channel= %u  channel-count = %d\n",
21820ec449eeSDoug Thompson 		 nr_pages, pvt->channel_count);
21830ec449eeSDoug Thompson 
21840ec449eeSDoug Thompson 	return nr_pages;
21850ec449eeSDoug Thompson }
21860ec449eeSDoug Thompson 
21870ec449eeSDoug Thompson /*
21880ec449eeSDoug Thompson  * Initialize the array of csrow attribute instances, based on the values
21890ec449eeSDoug Thompson  * from pci config hardware registers.
21900ec449eeSDoug Thompson  */
2191360b7f3cSBorislav Petkov static int init_csrows(struct mem_ctl_info *mci)
21920ec449eeSDoug Thompson {
21930ec449eeSDoug Thompson 	struct csrow_info *csrow;
2194de3910ebSMauro Carvalho Chehab 	struct dimm_info *dimm;
21952299ef71SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
21965e2af0c0SMauro Carvalho Chehab 	u64 base, mask;
21972299ef71SBorislav Petkov 	u32 val;
2198084a4fccSMauro Carvalho Chehab 	int i, j, empty = 1;
2199084a4fccSMauro Carvalho Chehab 	enum mem_type mtype;
2200084a4fccSMauro Carvalho Chehab 	enum edac_type edac_mode;
2201a895bf8bSMauro Carvalho Chehab 	int nr_pages = 0;
22020ec449eeSDoug Thompson 
2203a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
22040ec449eeSDoug Thompson 
22052299ef71SBorislav Petkov 	pvt->nbcfg = val;
22060ec449eeSDoug Thompson 
2207956b9ba1SJoe Perches 	edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
22082299ef71SBorislav Petkov 		 pvt->mc_node_id, val,
2209a97fa68eSBorislav Petkov 		 !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
22100ec449eeSDoug Thompson 
221111c75eadSBorislav Petkov 	for_each_chip_select(i, 0, pvt) {
2212de3910ebSMauro Carvalho Chehab 		csrow = mci->csrows[i];
22130ec449eeSDoug Thompson 
2214f92cae45SAshish Shenoy 		if (!csrow_enabled(i, 0, pvt) && !csrow_enabled(i, 1, pvt)) {
2215956b9ba1SJoe Perches 			edac_dbg(1, "----CSROW %d VALID for MC node %d\n",
2216956b9ba1SJoe Perches 				 i, pvt->mc_node_id);
22170ec449eeSDoug Thompson 			continue;
22180ec449eeSDoug Thompson 		}
22190ec449eeSDoug Thompson 
22200ec449eeSDoug Thompson 		empty = 0;
2221f92cae45SAshish Shenoy 		if (csrow_enabled(i, 0, pvt))
2222a895bf8bSMauro Carvalho Chehab 			nr_pages = amd64_csrow_nr_pages(pvt, 0, i);
2223f92cae45SAshish Shenoy 		if (csrow_enabled(i, 1, pvt))
2224a895bf8bSMauro Carvalho Chehab 			nr_pages += amd64_csrow_nr_pages(pvt, 1, i);
222511c75eadSBorislav Petkov 
222611c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, i, 0, &base, &mask);
22270ec449eeSDoug Thompson 		/* 8 bytes of resolution */
22280ec449eeSDoug Thompson 
2229084a4fccSMauro Carvalho Chehab 		mtype = amd64_determine_memory_type(pvt, i);
22300ec449eeSDoug Thompson 
2231956b9ba1SJoe Perches 		edac_dbg(1, "  for MC node %d csrow %d:\n", pvt->mc_node_id, i);
2232956b9ba1SJoe Perches 		edac_dbg(1, "    nr_pages: %u\n",
2233956b9ba1SJoe Perches 			 nr_pages * pvt->channel_count);
22340ec449eeSDoug Thompson 
22350ec449eeSDoug Thompson 		/*
22360ec449eeSDoug Thompson 		 * determine whether CHIPKILL or JUST ECC or NO ECC is operating
22370ec449eeSDoug Thompson 		 */
2238a97fa68eSBorislav Petkov 		if (pvt->nbcfg & NBCFG_ECC_ENABLE)
2239084a4fccSMauro Carvalho Chehab 			edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) ?
22400ec449eeSDoug Thompson 				    EDAC_S4ECD4ED : EDAC_SECDED;
22410ec449eeSDoug Thompson 		else
2242084a4fccSMauro Carvalho Chehab 			edac_mode = EDAC_NONE;
2243084a4fccSMauro Carvalho Chehab 
2244084a4fccSMauro Carvalho Chehab 		for (j = 0; j < pvt->channel_count; j++) {
2245de3910ebSMauro Carvalho Chehab 			dimm = csrow->channels[j]->dimm;
2246de3910ebSMauro Carvalho Chehab 			dimm->mtype = mtype;
2247de3910ebSMauro Carvalho Chehab 			dimm->edac_mode = edac_mode;
2248de3910ebSMauro Carvalho Chehab 			dimm->nr_pages = nr_pages;
2249084a4fccSMauro Carvalho Chehab 		}
22500ec449eeSDoug Thompson 	}
22510ec449eeSDoug Thompson 
22520ec449eeSDoug Thompson 	return empty;
22530ec449eeSDoug Thompson }
2254d27bf6faSDoug Thompson 
225506724535SBorislav Petkov /* get all cores on this DCT */
2256b487c33eSBorislav Petkov static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, unsigned nid)
2257f9431992SDoug Thompson {
225806724535SBorislav Petkov 	int cpu;
2259f9431992SDoug Thompson 
226006724535SBorislav Petkov 	for_each_online_cpu(cpu)
226106724535SBorislav Petkov 		if (amd_get_nb_id(cpu) == nid)
226206724535SBorislav Petkov 			cpumask_set_cpu(cpu, mask);
2263f9431992SDoug Thompson }
2264f9431992SDoug Thompson 
2265f9431992SDoug Thompson /* check MCG_CTL on all the cpus on this node */
2266b487c33eSBorislav Petkov static bool amd64_nb_mce_bank_enabled_on_node(unsigned nid)
2267f9431992SDoug Thompson {
2268ba578cb3SRusty Russell 	cpumask_var_t mask;
226950542251SBorislav Petkov 	int cpu, nbe;
227006724535SBorislav Petkov 	bool ret = false;
2271f9431992SDoug Thompson 
2272ba578cb3SRusty Russell 	if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
227324f9a7feSBorislav Petkov 		amd64_warn("%s: Error allocating mask\n", __func__);
227406724535SBorislav Petkov 		return false;
227506724535SBorislav Petkov 	}
227606724535SBorislav Petkov 
2277ba578cb3SRusty Russell 	get_cpus_on_this_dct_cpumask(mask, nid);
227806724535SBorislav Petkov 
2279ba578cb3SRusty Russell 	rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
2280ba578cb3SRusty Russell 
2281ba578cb3SRusty Russell 	for_each_cpu(cpu, mask) {
228250542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
22835980bb9cSBorislav Petkov 		nbe = reg->l & MSR_MCGCTL_NBE;
228406724535SBorislav Petkov 
2285956b9ba1SJoe Perches 		edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
228650542251SBorislav Petkov 			 cpu, reg->q,
228706724535SBorislav Petkov 			 (nbe ? "enabled" : "disabled"));
228806724535SBorislav Petkov 
228906724535SBorislav Petkov 		if (!nbe)
229006724535SBorislav Petkov 			goto out;
229106724535SBorislav Petkov 	}
229206724535SBorislav Petkov 	ret = true;
229306724535SBorislav Petkov 
229406724535SBorislav Petkov out:
2295ba578cb3SRusty Russell 	free_cpumask_var(mask);
2296f9431992SDoug Thompson 	return ret;
2297f9431992SDoug Thompson }
2298f9431992SDoug Thompson 
22992299ef71SBorislav Petkov static int toggle_ecc_err_reporting(struct ecc_settings *s, u8 nid, bool on)
2300f6d6ae96SBorislav Petkov {
2301f6d6ae96SBorislav Petkov 	cpumask_var_t cmask;
230250542251SBorislav Petkov 	int cpu;
2303f6d6ae96SBorislav Petkov 
2304f6d6ae96SBorislav Petkov 	if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
230524f9a7feSBorislav Petkov 		amd64_warn("%s: error allocating mask\n", __func__);
2306f6d6ae96SBorislav Petkov 		return false;
2307f6d6ae96SBorislav Petkov 	}
2308f6d6ae96SBorislav Petkov 
2309ae7bb7c6SBorislav Petkov 	get_cpus_on_this_dct_cpumask(cmask, nid);
2310f6d6ae96SBorislav Petkov 
2311f6d6ae96SBorislav Petkov 	rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2312f6d6ae96SBorislav Petkov 
2313f6d6ae96SBorislav Petkov 	for_each_cpu(cpu, cmask) {
2314f6d6ae96SBorislav Petkov 
231550542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
231650542251SBorislav Petkov 
2317f6d6ae96SBorislav Petkov 		if (on) {
23185980bb9cSBorislav Petkov 			if (reg->l & MSR_MCGCTL_NBE)
2319ae7bb7c6SBorislav Petkov 				s->flags.nb_mce_enable = 1;
2320f6d6ae96SBorislav Petkov 
23215980bb9cSBorislav Petkov 			reg->l |= MSR_MCGCTL_NBE;
2322f6d6ae96SBorislav Petkov 		} else {
2323f6d6ae96SBorislav Petkov 			/*
2324d95cf4deSBorislav Petkov 			 * Turn off NB MCE reporting only when it was off before
2325f6d6ae96SBorislav Petkov 			 */
2326ae7bb7c6SBorislav Petkov 			if (!s->flags.nb_mce_enable)
23275980bb9cSBorislav Petkov 				reg->l &= ~MSR_MCGCTL_NBE;
2328f6d6ae96SBorislav Petkov 		}
2329f6d6ae96SBorislav Petkov 	}
2330f6d6ae96SBorislav Petkov 	wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2331f6d6ae96SBorislav Petkov 
2332f6d6ae96SBorislav Petkov 	free_cpumask_var(cmask);
2333f6d6ae96SBorislav Petkov 
2334f6d6ae96SBorislav Petkov 	return 0;
2335f6d6ae96SBorislav Petkov }
2336f6d6ae96SBorislav Petkov 
23372299ef71SBorislav Petkov static bool enable_ecc_error_reporting(struct ecc_settings *s, u8 nid,
23382299ef71SBorislav Petkov 				       struct pci_dev *F3)
2339f6d6ae96SBorislav Petkov {
23402299ef71SBorislav Petkov 	bool ret = true;
2341c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
2342f6d6ae96SBorislav Petkov 
23432299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, ON)) {
23442299ef71SBorislav Petkov 		amd64_warn("Error enabling ECC reporting over MCGCTL!\n");
23452299ef71SBorislav Petkov 		return false;
23462299ef71SBorislav Petkov 	}
23472299ef71SBorislav Petkov 
2348c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
2349f6d6ae96SBorislav Petkov 
2350ae7bb7c6SBorislav Petkov 	s->old_nbctl   = value & mask;
2351ae7bb7c6SBorislav Petkov 	s->nbctl_valid = true;
2352f6d6ae96SBorislav Petkov 
2353f6d6ae96SBorislav Petkov 	value |= mask;
2354c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
2355f6d6ae96SBorislav Petkov 
2356a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCFG, &value);
2357f6d6ae96SBorislav Petkov 
2358956b9ba1SJoe Perches 	edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2359a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
2360f6d6ae96SBorislav Petkov 
2361a97fa68eSBorislav Petkov 	if (!(value & NBCFG_ECC_ENABLE)) {
236224f9a7feSBorislav Petkov 		amd64_warn("DRAM ECC disabled on this node, enabling...\n");
2363f6d6ae96SBorislav Petkov 
2364ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 0;
2365d95cf4deSBorislav Petkov 
2366f6d6ae96SBorislav Petkov 		/* Attempt to turn on DRAM ECC Enable */
2367a97fa68eSBorislav Petkov 		value |= NBCFG_ECC_ENABLE;
2368a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
2369f6d6ae96SBorislav Petkov 
2370a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
2371f6d6ae96SBorislav Petkov 
2372a97fa68eSBorislav Petkov 		if (!(value & NBCFG_ECC_ENABLE)) {
237324f9a7feSBorislav Petkov 			amd64_warn("Hardware rejected DRAM ECC enable,"
237424f9a7feSBorislav Petkov 				   "check memory DIMM configuration.\n");
23752299ef71SBorislav Petkov 			ret = false;
2376f6d6ae96SBorislav Petkov 		} else {
237724f9a7feSBorislav Petkov 			amd64_info("Hardware accepted DRAM ECC Enable\n");
2378f6d6ae96SBorislav Petkov 		}
2379d95cf4deSBorislav Petkov 	} else {
2380ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 1;
2381f6d6ae96SBorislav Petkov 	}
2382d95cf4deSBorislav Petkov 
2383956b9ba1SJoe Perches 	edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2384a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
2385f6d6ae96SBorislav Petkov 
23862299ef71SBorislav Petkov 	return ret;
2387f6d6ae96SBorislav Petkov }
2388f6d6ae96SBorislav Petkov 
2389360b7f3cSBorislav Petkov static void restore_ecc_error_reporting(struct ecc_settings *s, u8 nid,
2390360b7f3cSBorislav Petkov 					struct pci_dev *F3)
2391f6d6ae96SBorislav Petkov {
2392c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
2393c9f4f26eSBorislav Petkov 
2394f6d6ae96SBorislav Petkov 
2395ae7bb7c6SBorislav Petkov 	if (!s->nbctl_valid)
2396f6d6ae96SBorislav Petkov 		return;
2397f6d6ae96SBorislav Petkov 
2398c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
2399f6d6ae96SBorislav Petkov 	value &= ~mask;
2400ae7bb7c6SBorislav Petkov 	value |= s->old_nbctl;
2401f6d6ae96SBorislav Petkov 
2402c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
2403f6d6ae96SBorislav Petkov 
2404ae7bb7c6SBorislav Petkov 	/* restore previous BIOS DRAM ECC "off" setting we force-enabled */
2405ae7bb7c6SBorislav Petkov 	if (!s->flags.nb_ecc_prev) {
2406a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
2407a97fa68eSBorislav Petkov 		value &= ~NBCFG_ECC_ENABLE;
2408a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
2409d95cf4deSBorislav Petkov 	}
2410d95cf4deSBorislav Petkov 
2411d95cf4deSBorislav Petkov 	/* restore the NB Enable MCGCTL bit */
24122299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, OFF))
241324f9a7feSBorislav Petkov 		amd64_warn("Error restoring NB MCGCTL settings!\n");
2414f6d6ae96SBorislav Petkov }
2415f6d6ae96SBorislav Petkov 
2416f9431992SDoug Thompson /*
24172299ef71SBorislav Petkov  * EDAC requires that the BIOS have ECC enabled before
24182299ef71SBorislav Petkov  * taking over the processing of ECC errors. A command line
24192299ef71SBorislav Petkov  * option allows to force-enable hardware ECC later in
24202299ef71SBorislav Petkov  * enable_ecc_error_reporting().
2421f9431992SDoug Thompson  */
2422cab4d277SBorislav Petkov static const char *ecc_msg =
2423cab4d277SBorislav Petkov 	"ECC disabled in the BIOS or no ECC capability, module will not load.\n"
2424cab4d277SBorislav Petkov 	" Either enable ECC checking or force module loading by setting "
2425cab4d277SBorislav Petkov 	"'ecc_enable_override'.\n"
2426cab4d277SBorislav Petkov 	" (Note that use of the override may cause unknown side effects.)\n";
2427be3468e8SBorislav Petkov 
24282299ef71SBorislav Petkov static bool ecc_enabled(struct pci_dev *F3, u8 nid)
2429f9431992SDoug Thompson {
2430f9431992SDoug Thompson 	u32 value;
24312299ef71SBorislav Petkov 	u8 ecc_en = 0;
243206724535SBorislav Petkov 	bool nb_mce_en = false;
2433f9431992SDoug Thompson 
2434a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCFG, &value);
2435f9431992SDoug Thompson 
2436a97fa68eSBorislav Petkov 	ecc_en = !!(value & NBCFG_ECC_ENABLE);
24372299ef71SBorislav Petkov 	amd64_info("DRAM ECC %s.\n", (ecc_en ? "enabled" : "disabled"));
2438be3468e8SBorislav Petkov 
24392299ef71SBorislav Petkov 	nb_mce_en = amd64_nb_mce_bank_enabled_on_node(nid);
244006724535SBorislav Petkov 	if (!nb_mce_en)
24412299ef71SBorislav Petkov 		amd64_notice("NB MCE bank disabled, set MSR "
24422299ef71SBorislav Petkov 			     "0x%08x[4] on node %d to enable.\n",
24432299ef71SBorislav Petkov 			     MSR_IA32_MCG_CTL, nid);
2444be3468e8SBorislav Petkov 
24452299ef71SBorislav Petkov 	if (!ecc_en || !nb_mce_en) {
244624f9a7feSBorislav Petkov 		amd64_notice("%s", ecc_msg);
24472299ef71SBorislav Petkov 		return false;
2448be3468e8SBorislav Petkov 	}
24492299ef71SBorislav Petkov 	return true;
2450f9431992SDoug Thompson }
2451f9431992SDoug Thompson 
2452c5608759SMauro Carvalho Chehab static int set_mc_sysfs_attrs(struct mem_ctl_info *mci)
24537d6034d3SDoug Thompson {
2454c5608759SMauro Carvalho Chehab 	int rc;
24557d6034d3SDoug Thompson 
2456c5608759SMauro Carvalho Chehab 	rc = amd64_create_sysfs_dbg_files(mci);
2457c5608759SMauro Carvalho Chehab 	if (rc < 0)
2458c5608759SMauro Carvalho Chehab 		return rc;
2459c5608759SMauro Carvalho Chehab 
2460c5608759SMauro Carvalho Chehab 	if (boot_cpu_data.x86 >= 0x10) {
2461c5608759SMauro Carvalho Chehab 		rc = amd64_create_sysfs_inject_files(mci);
2462c5608759SMauro Carvalho Chehab 		if (rc < 0)
2463c5608759SMauro Carvalho Chehab 			return rc;
2464c5608759SMauro Carvalho Chehab 	}
2465c5608759SMauro Carvalho Chehab 
2466c5608759SMauro Carvalho Chehab 	return 0;
2467c5608759SMauro Carvalho Chehab }
2468c5608759SMauro Carvalho Chehab 
2469c5608759SMauro Carvalho Chehab static void del_mc_sysfs_attrs(struct mem_ctl_info *mci)
2470c5608759SMauro Carvalho Chehab {
2471c5608759SMauro Carvalho Chehab 	amd64_remove_sysfs_dbg_files(mci);
24727d6034d3SDoug Thompson 
2473a135cef7SBorislav Petkov 	if (boot_cpu_data.x86 >= 0x10)
2474c5608759SMauro Carvalho Chehab 		amd64_remove_sysfs_inject_files(mci);
24757d6034d3SDoug Thompson }
24767d6034d3SDoug Thompson 
2477df71a053SBorislav Petkov static void setup_mci_misc_attrs(struct mem_ctl_info *mci,
2478df71a053SBorislav Petkov 				 struct amd64_family_type *fam)
24797d6034d3SDoug Thompson {
24807d6034d3SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
24817d6034d3SDoug Thompson 
24827d6034d3SDoug Thompson 	mci->mtype_cap		= MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
24837d6034d3SDoug Thompson 	mci->edac_ctl_cap	= EDAC_FLAG_NONE;
24847d6034d3SDoug Thompson 
24855980bb9cSBorislav Petkov 	if (pvt->nbcap & NBCAP_SECDED)
24867d6034d3SDoug Thompson 		mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
24877d6034d3SDoug Thompson 
24885980bb9cSBorislav Petkov 	if (pvt->nbcap & NBCAP_CHIPKILL)
24897d6034d3SDoug Thompson 		mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
24907d6034d3SDoug Thompson 
24917d6034d3SDoug Thompson 	mci->edac_cap		= amd64_determine_edac_cap(pvt);
24927d6034d3SDoug Thompson 	mci->mod_name		= EDAC_MOD_STR;
24937d6034d3SDoug Thompson 	mci->mod_ver		= EDAC_AMD64_VERSION;
2494df71a053SBorislav Petkov 	mci->ctl_name		= fam->ctl_name;
24958d5b5d9cSBorislav Petkov 	mci->dev_name		= pci_name(pvt->F2);
24967d6034d3SDoug Thompson 	mci->ctl_page_to_phys	= NULL;
24977d6034d3SDoug Thompson 
24987d6034d3SDoug Thompson 	/* memory scrubber interface */
24997d6034d3SDoug Thompson 	mci->set_sdram_scrub_rate = amd64_set_scrub_rate;
25007d6034d3SDoug Thompson 	mci->get_sdram_scrub_rate = amd64_get_scrub_rate;
25017d6034d3SDoug Thompson }
25027d6034d3SDoug Thompson 
25030092b20dSBorislav Petkov /*
25040092b20dSBorislav Petkov  * returns a pointer to the family descriptor on success, NULL otherwise.
25050092b20dSBorislav Petkov  */
25060092b20dSBorislav Petkov static struct amd64_family_type *amd64_per_family_init(struct amd64_pvt *pvt)
2507395ae783SBorislav Petkov {
25080092b20dSBorislav Petkov 	u8 fam = boot_cpu_data.x86;
25090092b20dSBorislav Petkov 	struct amd64_family_type *fam_type = NULL;
25100092b20dSBorislav Petkov 
25110092b20dSBorislav Petkov 	switch (fam) {
2512395ae783SBorislav Petkov 	case 0xf:
25130092b20dSBorislav Petkov 		fam_type		= &amd64_family_types[K8_CPUS];
2514b8cfa02fSBorislav Petkov 		pvt->ops		= &amd64_family_types[K8_CPUS].ops;
2515395ae783SBorislav Petkov 		break;
2516df71a053SBorislav Petkov 
2517395ae783SBorislav Petkov 	case 0x10:
25180092b20dSBorislav Petkov 		fam_type		= &amd64_family_types[F10_CPUS];
2519b8cfa02fSBorislav Petkov 		pvt->ops		= &amd64_family_types[F10_CPUS].ops;
2520df71a053SBorislav Petkov 		break;
2521df71a053SBorislav Petkov 
2522df71a053SBorislav Petkov 	case 0x15:
2523df71a053SBorislav Petkov 		fam_type		= &amd64_family_types[F15_CPUS];
2524df71a053SBorislav Petkov 		pvt->ops		= &amd64_family_types[F15_CPUS].ops;
2525395ae783SBorislav Petkov 		break;
2526395ae783SBorislav Petkov 
2527395ae783SBorislav Petkov 	default:
252824f9a7feSBorislav Petkov 		amd64_err("Unsupported family!\n");
25290092b20dSBorislav Petkov 		return NULL;
2530395ae783SBorislav Petkov 	}
25310092b20dSBorislav Petkov 
2532b8cfa02fSBorislav Petkov 	pvt->ext_model = boot_cpu_data.x86_model >> 4;
2533b8cfa02fSBorislav Petkov 
2534df71a053SBorislav Petkov 	amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name,
25350092b20dSBorislav Petkov 		     (fam == 0xf ?
25360092b20dSBorislav Petkov 				(pvt->ext_model >= K8_REV_F  ? "revF or later "
25370092b20dSBorislav Petkov 							     : "revE or earlier ")
253824f9a7feSBorislav Petkov 				 : ""), pvt->mc_node_id);
25390092b20dSBorislav Petkov 	return fam_type;
2540395ae783SBorislav Petkov }
2541395ae783SBorislav Petkov 
25422299ef71SBorislav Petkov static int amd64_init_one_instance(struct pci_dev *F2)
25437d6034d3SDoug Thompson {
25447d6034d3SDoug Thompson 	struct amd64_pvt *pvt = NULL;
25450092b20dSBorislav Petkov 	struct amd64_family_type *fam_type = NULL;
2546360b7f3cSBorislav Petkov 	struct mem_ctl_info *mci = NULL;
2547ab5a503cSMauro Carvalho Chehab 	struct edac_mc_layer layers[2];
25487d6034d3SDoug Thompson 	int err = 0, ret;
2549360b7f3cSBorislav Petkov 	u8 nid = get_node_id(F2);
25507d6034d3SDoug Thompson 
25517d6034d3SDoug Thompson 	ret = -ENOMEM;
25527d6034d3SDoug Thompson 	pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
25537d6034d3SDoug Thompson 	if (!pvt)
2554360b7f3cSBorislav Petkov 		goto err_ret;
25557d6034d3SDoug Thompson 
2556360b7f3cSBorislav Petkov 	pvt->mc_node_id	= nid;
25578d5b5d9cSBorislav Petkov 	pvt->F2 = F2;
25587d6034d3SDoug Thompson 
2559395ae783SBorislav Petkov 	ret = -EINVAL;
25600092b20dSBorislav Petkov 	fam_type = amd64_per_family_init(pvt);
25610092b20dSBorislav Petkov 	if (!fam_type)
2562395ae783SBorislav Petkov 		goto err_free;
2563395ae783SBorislav Petkov 
25647d6034d3SDoug Thompson 	ret = -ENODEV;
2565360b7f3cSBorislav Petkov 	err = reserve_mc_sibling_devs(pvt, fam_type->f1_id, fam_type->f3_id);
25667d6034d3SDoug Thompson 	if (err)
25677d6034d3SDoug Thompson 		goto err_free;
25687d6034d3SDoug Thompson 
2569360b7f3cSBorislav Petkov 	read_mc_regs(pvt);
25707d6034d3SDoug Thompson 
25717d6034d3SDoug Thompson 	/*
25727d6034d3SDoug Thompson 	 * We need to determine how many memory channels there are. Then use
25737d6034d3SDoug Thompson 	 * that information for calculating the size of the dynamic instance
2574360b7f3cSBorislav Petkov 	 * tables in the 'mci' structure.
25757d6034d3SDoug Thompson 	 */
2576360b7f3cSBorislav Petkov 	ret = -EINVAL;
25777d6034d3SDoug Thompson 	pvt->channel_count = pvt->ops->early_channel_count(pvt);
25787d6034d3SDoug Thompson 	if (pvt->channel_count < 0)
2579360b7f3cSBorislav Petkov 		goto err_siblings;
25807d6034d3SDoug Thompson 
25817d6034d3SDoug Thompson 	ret = -ENOMEM;
2582ab5a503cSMauro Carvalho Chehab 	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
2583ab5a503cSMauro Carvalho Chehab 	layers[0].size = pvt->csels[0].b_cnt;
2584ab5a503cSMauro Carvalho Chehab 	layers[0].is_virt_csrow = true;
2585ab5a503cSMauro Carvalho Chehab 	layers[1].type = EDAC_MC_LAYER_CHANNEL;
2586ab5a503cSMauro Carvalho Chehab 	layers[1].size = pvt->channel_count;
2587ab5a503cSMauro Carvalho Chehab 	layers[1].is_virt_csrow = false;
2588ca0907b9SMauro Carvalho Chehab 	mci = edac_mc_alloc(nid, ARRAY_SIZE(layers), layers, 0);
25897d6034d3SDoug Thompson 	if (!mci)
2590360b7f3cSBorislav Petkov 		goto err_siblings;
25917d6034d3SDoug Thompson 
25927d6034d3SDoug Thompson 	mci->pvt_info = pvt;
2593fd687502SMauro Carvalho Chehab 	mci->pdev = &pvt->F2->dev;
25947d6034d3SDoug Thompson 
2595df71a053SBorislav Petkov 	setup_mci_misc_attrs(mci, fam_type);
2596360b7f3cSBorislav Petkov 
2597360b7f3cSBorislav Petkov 	if (init_csrows(mci))
25987d6034d3SDoug Thompson 		mci->edac_cap = EDAC_FLAG_NONE;
25997d6034d3SDoug Thompson 
26007d6034d3SDoug Thompson 	ret = -ENODEV;
26017d6034d3SDoug Thompson 	if (edac_mc_add_mc(mci)) {
2602956b9ba1SJoe Perches 		edac_dbg(1, "failed edac_mc_add_mc()\n");
26037d6034d3SDoug Thompson 		goto err_add_mc;
26047d6034d3SDoug Thompson 	}
2605c5608759SMauro Carvalho Chehab 	if (set_mc_sysfs_attrs(mci)) {
2606956b9ba1SJoe Perches 		edac_dbg(1, "failed edac_mc_add_mc()\n");
2607c5608759SMauro Carvalho Chehab 		goto err_add_sysfs;
2608c5608759SMauro Carvalho Chehab 	}
26097d6034d3SDoug Thompson 
2610549d042dSBorislav Petkov 	/* register stuff with EDAC MCE */
2611549d042dSBorislav Petkov 	if (report_gart_errors)
2612549d042dSBorislav Petkov 		amd_report_gart_errors(true);
2613549d042dSBorislav Petkov 
2614549d042dSBorislav Petkov 	amd_register_ecc_decoder(amd64_decode_bus_error);
2615549d042dSBorislav Petkov 
2616360b7f3cSBorislav Petkov 	mcis[nid] = mci;
2617360b7f3cSBorislav Petkov 
2618360b7f3cSBorislav Petkov 	atomic_inc(&drv_instances);
2619360b7f3cSBorislav Petkov 
26207d6034d3SDoug Thompson 	return 0;
26217d6034d3SDoug Thompson 
2622c5608759SMauro Carvalho Chehab err_add_sysfs:
2623c5608759SMauro Carvalho Chehab 	edac_mc_del_mc(mci->pdev);
26247d6034d3SDoug Thompson err_add_mc:
26257d6034d3SDoug Thompson 	edac_mc_free(mci);
26267d6034d3SDoug Thompson 
2627360b7f3cSBorislav Petkov err_siblings:
2628360b7f3cSBorislav Petkov 	free_mc_sibling_devs(pvt);
26297d6034d3SDoug Thompson 
2630360b7f3cSBorislav Petkov err_free:
2631360b7f3cSBorislav Petkov 	kfree(pvt);
26327d6034d3SDoug Thompson 
2633360b7f3cSBorislav Petkov err_ret:
26347d6034d3SDoug Thompson 	return ret;
26357d6034d3SDoug Thompson }
26367d6034d3SDoug Thompson 
26372299ef71SBorislav Petkov static int __devinit amd64_probe_one_instance(struct pci_dev *pdev,
26387d6034d3SDoug Thompson 					     const struct pci_device_id *mc_type)
26397d6034d3SDoug Thompson {
2640ae7bb7c6SBorislav Petkov 	u8 nid = get_node_id(pdev);
26412299ef71SBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
2642ae7bb7c6SBorislav Petkov 	struct ecc_settings *s;
26432299ef71SBorislav Petkov 	int ret = 0;
26447d6034d3SDoug Thompson 
26457d6034d3SDoug Thompson 	ret = pci_enable_device(pdev);
2646b8cfa02fSBorislav Petkov 	if (ret < 0) {
2647956b9ba1SJoe Perches 		edac_dbg(0, "ret=%d\n", ret);
2648b8cfa02fSBorislav Petkov 		return -EIO;
2649b8cfa02fSBorislav Petkov 	}
2650b8cfa02fSBorislav Petkov 
2651ae7bb7c6SBorislav Petkov 	ret = -ENOMEM;
2652ae7bb7c6SBorislav Petkov 	s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL);
2653ae7bb7c6SBorislav Petkov 	if (!s)
26542299ef71SBorislav Petkov 		goto err_out;
2655ae7bb7c6SBorislav Petkov 
2656ae7bb7c6SBorislav Petkov 	ecc_stngs[nid] = s;
2657ae7bb7c6SBorislav Petkov 
26582299ef71SBorislav Petkov 	if (!ecc_enabled(F3, nid)) {
26592299ef71SBorislav Petkov 		ret = -ENODEV;
26602299ef71SBorislav Petkov 
26612299ef71SBorislav Petkov 		if (!ecc_enable_override)
26622299ef71SBorislav Petkov 			goto err_enable;
26632299ef71SBorislav Petkov 
26642299ef71SBorislav Petkov 		amd64_warn("Forcing ECC on!\n");
26652299ef71SBorislav Petkov 
26662299ef71SBorislav Petkov 		if (!enable_ecc_error_reporting(s, nid, F3))
26672299ef71SBorislav Petkov 			goto err_enable;
26682299ef71SBorislav Petkov 	}
26692299ef71SBorislav Petkov 
26702299ef71SBorislav Petkov 	ret = amd64_init_one_instance(pdev);
2671360b7f3cSBorislav Petkov 	if (ret < 0) {
2672ae7bb7c6SBorislav Petkov 		amd64_err("Error probing instance: %d\n", nid);
2673360b7f3cSBorislav Petkov 		restore_ecc_error_reporting(s, nid, F3);
2674360b7f3cSBorislav Petkov 	}
26757d6034d3SDoug Thompson 
26767d6034d3SDoug Thompson 	return ret;
26772299ef71SBorislav Petkov 
26782299ef71SBorislav Petkov err_enable:
26792299ef71SBorislav Petkov 	kfree(s);
26802299ef71SBorislav Petkov 	ecc_stngs[nid] = NULL;
26812299ef71SBorislav Petkov 
26822299ef71SBorislav Petkov err_out:
26832299ef71SBorislav Petkov 	return ret;
26847d6034d3SDoug Thompson }
26857d6034d3SDoug Thompson 
26867d6034d3SDoug Thompson static void __devexit amd64_remove_one_instance(struct pci_dev *pdev)
26877d6034d3SDoug Thompson {
26887d6034d3SDoug Thompson 	struct mem_ctl_info *mci;
26897d6034d3SDoug Thompson 	struct amd64_pvt *pvt;
2690360b7f3cSBorislav Petkov 	u8 nid = get_node_id(pdev);
2691360b7f3cSBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
2692360b7f3cSBorislav Petkov 	struct ecc_settings *s = ecc_stngs[nid];
26937d6034d3SDoug Thompson 
2694c5608759SMauro Carvalho Chehab 	mci = find_mci_by_dev(&pdev->dev);
2695c5608759SMauro Carvalho Chehab 	del_mc_sysfs_attrs(mci);
26967d6034d3SDoug Thompson 	/* Remove from EDAC CORE tracking list */
26977d6034d3SDoug Thompson 	mci = edac_mc_del_mc(&pdev->dev);
26987d6034d3SDoug Thompson 	if (!mci)
26997d6034d3SDoug Thompson 		return;
27007d6034d3SDoug Thompson 
27017d6034d3SDoug Thompson 	pvt = mci->pvt_info;
27027d6034d3SDoug Thompson 
2703360b7f3cSBorislav Petkov 	restore_ecc_error_reporting(s, nid, F3);
27047d6034d3SDoug Thompson 
2705360b7f3cSBorislav Petkov 	free_mc_sibling_devs(pvt);
27067d6034d3SDoug Thompson 
2707549d042dSBorislav Petkov 	/* unregister from EDAC MCE */
2708549d042dSBorislav Petkov 	amd_report_gart_errors(false);
2709549d042dSBorislav Petkov 	amd_unregister_ecc_decoder(amd64_decode_bus_error);
2710549d042dSBorislav Petkov 
2711360b7f3cSBorislav Petkov 	kfree(ecc_stngs[nid]);
2712360b7f3cSBorislav Petkov 	ecc_stngs[nid] = NULL;
2713ae7bb7c6SBorislav Petkov 
27147d6034d3SDoug Thompson 	/* Free the EDAC CORE resources */
27158f68ed97SBorislav Petkov 	mci->pvt_info = NULL;
2716360b7f3cSBorislav Petkov 	mcis[nid] = NULL;
27178f68ed97SBorislav Petkov 
27188f68ed97SBorislav Petkov 	kfree(pvt);
27197d6034d3SDoug Thompson 	edac_mc_free(mci);
27207d6034d3SDoug Thompson }
27217d6034d3SDoug Thompson 
27227d6034d3SDoug Thompson /*
27237d6034d3SDoug Thompson  * This table is part of the interface for loading drivers for PCI devices. The
27247d6034d3SDoug Thompson  * PCI core identifies what devices are on a system during boot, and then
27257d6034d3SDoug Thompson  * inquiry this table to see if this driver is for a given device found.
27267d6034d3SDoug Thompson  */
272736c46f31SLionel Debroux static DEFINE_PCI_DEVICE_TABLE(amd64_pci_table) = {
27287d6034d3SDoug Thompson 	{
27297d6034d3SDoug Thompson 		.vendor		= PCI_VENDOR_ID_AMD,
27307d6034d3SDoug Thompson 		.device		= PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
27317d6034d3SDoug Thompson 		.subvendor	= PCI_ANY_ID,
27327d6034d3SDoug Thompson 		.subdevice	= PCI_ANY_ID,
27337d6034d3SDoug Thompson 		.class		= 0,
27347d6034d3SDoug Thompson 		.class_mask	= 0,
27357d6034d3SDoug Thompson 	},
27367d6034d3SDoug Thompson 	{
27377d6034d3SDoug Thompson 		.vendor		= PCI_VENDOR_ID_AMD,
27387d6034d3SDoug Thompson 		.device		= PCI_DEVICE_ID_AMD_10H_NB_DRAM,
27397d6034d3SDoug Thompson 		.subvendor	= PCI_ANY_ID,
27407d6034d3SDoug Thompson 		.subdevice	= PCI_ANY_ID,
27417d6034d3SDoug Thompson 		.class		= 0,
27427d6034d3SDoug Thompson 		.class_mask	= 0,
27437d6034d3SDoug Thompson 	},
2744df71a053SBorislav Petkov 	{
2745df71a053SBorislav Petkov 		.vendor		= PCI_VENDOR_ID_AMD,
2746df71a053SBorislav Petkov 		.device		= PCI_DEVICE_ID_AMD_15H_NB_F2,
2747df71a053SBorislav Petkov 		.subvendor	= PCI_ANY_ID,
2748df71a053SBorislav Petkov 		.subdevice	= PCI_ANY_ID,
2749df71a053SBorislav Petkov 		.class		= 0,
2750df71a053SBorislav Petkov 		.class_mask	= 0,
2751df71a053SBorislav Petkov 	},
2752df71a053SBorislav Petkov 
27537d6034d3SDoug Thompson 	{0, }
27547d6034d3SDoug Thompson };
27557d6034d3SDoug Thompson MODULE_DEVICE_TABLE(pci, amd64_pci_table);
27567d6034d3SDoug Thompson 
27577d6034d3SDoug Thompson static struct pci_driver amd64_pci_driver = {
27587d6034d3SDoug Thompson 	.name		= EDAC_MOD_STR,
27592299ef71SBorislav Petkov 	.probe		= amd64_probe_one_instance,
27607d6034d3SDoug Thompson 	.remove		= __devexit_p(amd64_remove_one_instance),
27617d6034d3SDoug Thompson 	.id_table	= amd64_pci_table,
27627d6034d3SDoug Thompson };
27637d6034d3SDoug Thompson 
2764360b7f3cSBorislav Petkov static void setup_pci_device(void)
27657d6034d3SDoug Thompson {
27667d6034d3SDoug Thompson 	struct mem_ctl_info *mci;
27677d6034d3SDoug Thompson 	struct amd64_pvt *pvt;
27687d6034d3SDoug Thompson 
27697d6034d3SDoug Thompson 	if (amd64_ctl_pci)
27707d6034d3SDoug Thompson 		return;
27717d6034d3SDoug Thompson 
2772cc4d8860SBorislav Petkov 	mci = mcis[0];
27737d6034d3SDoug Thompson 	if (mci) {
27747d6034d3SDoug Thompson 
27757d6034d3SDoug Thompson 		pvt = mci->pvt_info;
27767d6034d3SDoug Thompson 		amd64_ctl_pci =
27778d5b5d9cSBorislav Petkov 			edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR);
27787d6034d3SDoug Thompson 
27797d6034d3SDoug Thompson 		if (!amd64_ctl_pci) {
27807d6034d3SDoug Thompson 			pr_warning("%s(): Unable to create PCI control\n",
27817d6034d3SDoug Thompson 				   __func__);
27827d6034d3SDoug Thompson 
27837d6034d3SDoug Thompson 			pr_warning("%s(): PCI error report via EDAC not set\n",
27847d6034d3SDoug Thompson 				   __func__);
27857d6034d3SDoug Thompson 			}
27867d6034d3SDoug Thompson 	}
27877d6034d3SDoug Thompson }
27887d6034d3SDoug Thompson 
27897d6034d3SDoug Thompson static int __init amd64_edac_init(void)
27907d6034d3SDoug Thompson {
2791360b7f3cSBorislav Petkov 	int err = -ENODEV;
27927d6034d3SDoug Thompson 
2793df71a053SBorislav Petkov 	printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION);
27947d6034d3SDoug Thompson 
27957d6034d3SDoug Thompson 	opstate_init();
27967d6034d3SDoug Thompson 
27979653a5c7SHans Rosenfeld 	if (amd_cache_northbridges() < 0)
279856b34b91SBorislav Petkov 		goto err_ret;
27997d6034d3SDoug Thompson 
2800cc4d8860SBorislav Petkov 	err = -ENOMEM;
2801cc4d8860SBorislav Petkov 	mcis	  = kzalloc(amd_nb_num() * sizeof(mcis[0]), GFP_KERNEL);
2802ae7bb7c6SBorislav Petkov 	ecc_stngs = kzalloc(amd_nb_num() * sizeof(ecc_stngs[0]), GFP_KERNEL);
2803360b7f3cSBorislav Petkov 	if (!(mcis && ecc_stngs))
2804a9f0fbe2SBorislav Petkov 		goto err_free;
2805cc4d8860SBorislav Petkov 
280650542251SBorislav Petkov 	msrs = msrs_alloc();
280756b34b91SBorislav Petkov 	if (!msrs)
2808360b7f3cSBorislav Petkov 		goto err_free;
280950542251SBorislav Petkov 
28107d6034d3SDoug Thompson 	err = pci_register_driver(&amd64_pci_driver);
28117d6034d3SDoug Thompson 	if (err)
281256b34b91SBorislav Petkov 		goto err_pci;
28137d6034d3SDoug Thompson 
281456b34b91SBorislav Petkov 	err = -ENODEV;
2815360b7f3cSBorislav Petkov 	if (!atomic_read(&drv_instances))
2816360b7f3cSBorislav Petkov 		goto err_no_instances;
28177d6034d3SDoug Thompson 
2818360b7f3cSBorislav Petkov 	setup_pci_device();
28197d6034d3SDoug Thompson 	return 0;
28207d6034d3SDoug Thompson 
2821360b7f3cSBorislav Petkov err_no_instances:
28227d6034d3SDoug Thompson 	pci_unregister_driver(&amd64_pci_driver);
2823cc4d8860SBorislav Petkov 
282456b34b91SBorislav Petkov err_pci:
282556b34b91SBorislav Petkov 	msrs_free(msrs);
282656b34b91SBorislav Petkov 	msrs = NULL;
2827cc4d8860SBorislav Petkov 
2828360b7f3cSBorislav Petkov err_free:
2829360b7f3cSBorislav Petkov 	kfree(mcis);
2830360b7f3cSBorislav Petkov 	mcis = NULL;
2831360b7f3cSBorislav Petkov 
2832360b7f3cSBorislav Petkov 	kfree(ecc_stngs);
2833360b7f3cSBorislav Petkov 	ecc_stngs = NULL;
2834360b7f3cSBorislav Petkov 
283556b34b91SBorislav Petkov err_ret:
28367d6034d3SDoug Thompson 	return err;
28377d6034d3SDoug Thompson }
28387d6034d3SDoug Thompson 
28397d6034d3SDoug Thompson static void __exit amd64_edac_exit(void)
28407d6034d3SDoug Thompson {
28417d6034d3SDoug Thompson 	if (amd64_ctl_pci)
28427d6034d3SDoug Thompson 		edac_pci_release_generic_ctl(amd64_ctl_pci);
28437d6034d3SDoug Thompson 
28447d6034d3SDoug Thompson 	pci_unregister_driver(&amd64_pci_driver);
284550542251SBorislav Petkov 
2846ae7bb7c6SBorislav Petkov 	kfree(ecc_stngs);
2847ae7bb7c6SBorislav Petkov 	ecc_stngs = NULL;
2848ae7bb7c6SBorislav Petkov 
2849cc4d8860SBorislav Petkov 	kfree(mcis);
2850cc4d8860SBorislav Petkov 	mcis = NULL;
2851cc4d8860SBorislav Petkov 
285250542251SBorislav Petkov 	msrs_free(msrs);
285350542251SBorislav Petkov 	msrs = NULL;
28547d6034d3SDoug Thompson }
28557d6034d3SDoug Thompson 
28567d6034d3SDoug Thompson module_init(amd64_edac_init);
28577d6034d3SDoug Thompson module_exit(amd64_edac_exit);
28587d6034d3SDoug Thompson 
28597d6034d3SDoug Thompson MODULE_LICENSE("GPL");
28607d6034d3SDoug Thompson MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, "
28617d6034d3SDoug Thompson 		"Dave Peterson, Thayne Harbaugh");
28627d6034d3SDoug Thompson MODULE_DESCRIPTION("MC support for AMD64 memory controllers - "
28637d6034d3SDoug Thompson 		EDAC_AMD64_VERSION);
28647d6034d3SDoug Thompson 
28657d6034d3SDoug Thompson module_param(edac_op_state, int, 0444);
28667d6034d3SDoug Thompson MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
2867