xref: /openbmc/linux/drivers/edac/amd64_edac.c (revision 772c3ff3)
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 
6366fed2d4SBorislav Petkov int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset,
64b2b0c605SBorislav Petkov 			       u32 *val, const char *func)
65b2b0c605SBorislav Petkov {
66b2b0c605SBorislav Petkov 	int err = 0;
67b2b0c605SBorislav Petkov 
68b2b0c605SBorislav Petkov 	err = pci_read_config_dword(pdev, offset, val);
69b2b0c605SBorislav Petkov 	if (err)
70b2b0c605SBorislav Petkov 		amd64_warn("%s: error reading F%dx%03x.\n",
71b2b0c605SBorislav Petkov 			   func, PCI_FUNC(pdev->devfn), offset);
72b2b0c605SBorislav Petkov 
73b2b0c605SBorislav Petkov 	return err;
74b2b0c605SBorislav Petkov }
75b2b0c605SBorislav Petkov 
76b2b0c605SBorislav Petkov int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset,
77b2b0c605SBorislav Petkov 				u32 val, const char *func)
78b2b0c605SBorislav Petkov {
79b2b0c605SBorislav Petkov 	int err = 0;
80b2b0c605SBorislav Petkov 
81b2b0c605SBorislav Petkov 	err = pci_write_config_dword(pdev, offset, val);
82b2b0c605SBorislav Petkov 	if (err)
83b2b0c605SBorislav Petkov 		amd64_warn("%s: error writing to F%dx%03x.\n",
84b2b0c605SBorislav Petkov 			   func, PCI_FUNC(pdev->devfn), offset);
85b2b0c605SBorislav Petkov 
86b2b0c605SBorislav Petkov 	return err;
87b2b0c605SBorislav Petkov }
88b2b0c605SBorislav Petkov 
89b2b0c605SBorislav Petkov /*
90b2b0c605SBorislav Petkov  *
91b2b0c605SBorislav Petkov  * Depending on the family, F2 DCT reads need special handling:
92b2b0c605SBorislav Petkov  *
93b2b0c605SBorislav Petkov  * K8: has a single DCT only
94b2b0c605SBorislav Petkov  *
95b2b0c605SBorislav Petkov  * F10h: each DCT has its own set of regs
96b2b0c605SBorislav Petkov  *	DCT0 -> F2x040..
97b2b0c605SBorislav Petkov  *	DCT1 -> F2x140..
98b2b0c605SBorislav Petkov  *
99b2b0c605SBorislav Petkov  * F15h: we select which DCT we access using F1x10C[DctCfgSel]
100b2b0c605SBorislav Petkov  *
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 
427e2ce7255SDoug Thompson 	/* only revE and later have the DRAM Hole Address Register */
4281433eb99SBorislav Petkov 	if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_E) {
429956b9ba1SJoe Perches 		edac_dbg(1, "  revision %d for node %d does not support DHAR\n",
430e2ce7255SDoug Thompson 			 pvt->ext_model, pvt->mc_node_id);
431e2ce7255SDoug Thompson 		return 1;
432e2ce7255SDoug Thompson 	}
433e2ce7255SDoug Thompson 
434bc21fa57SBorislav Petkov 	/* valid for Fam10h and above */
435c8e518d5SBorislav Petkov 	if (boot_cpu_data.x86 >= 0x10 && !dhar_mem_hoist_valid(pvt)) {
436956b9ba1SJoe Perches 		edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this system\n");
437e2ce7255SDoug Thompson 		return 1;
438e2ce7255SDoug Thompson 	}
439e2ce7255SDoug Thompson 
440c8e518d5SBorislav Petkov 	if (!dhar_valid(pvt)) {
441956b9ba1SJoe Perches 		edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this node %d\n",
442e2ce7255SDoug Thompson 			 pvt->mc_node_id);
443e2ce7255SDoug Thompson 		return 1;
444e2ce7255SDoug Thompson 	}
445e2ce7255SDoug Thompson 
446e2ce7255SDoug Thompson 	/* This node has Memory Hoisting */
447e2ce7255SDoug Thompson 
448e2ce7255SDoug Thompson 	/* +------------------+--------------------+--------------------+-----
449e2ce7255SDoug Thompson 	 * | memory           | DRAM hole          | relocated          |
450e2ce7255SDoug Thompson 	 * | [0, (x - 1)]     | [x, 0xffffffff]    | addresses from     |
451e2ce7255SDoug Thompson 	 * |                  |                    | DRAM hole          |
452e2ce7255SDoug Thompson 	 * |                  |                    | [0x100000000,      |
453e2ce7255SDoug Thompson 	 * |                  |                    |  (0x100000000+     |
454e2ce7255SDoug Thompson 	 * |                  |                    |   (0xffffffff-x))] |
455e2ce7255SDoug Thompson 	 * +------------------+--------------------+--------------------+-----
456e2ce7255SDoug Thompson 	 *
457e2ce7255SDoug Thompson 	 * Above is a diagram of physical memory showing the DRAM hole and the
458e2ce7255SDoug Thompson 	 * relocated addresses from the DRAM hole.  As shown, the DRAM hole
459e2ce7255SDoug Thompson 	 * starts at address x (the base address) and extends through address
460e2ce7255SDoug Thompson 	 * 0xffffffff.  The DRAM Hole Address Register (DHAR) relocates the
461e2ce7255SDoug Thompson 	 * addresses in the hole so that they start at 0x100000000.
462e2ce7255SDoug Thompson 	 */
463e2ce7255SDoug Thompson 
4641f31677eSBorislav Petkov 	*hole_base = dhar_base(pvt);
4651f31677eSBorislav Petkov 	*hole_size = (1ULL << 32) - *hole_base;
466e2ce7255SDoug Thompson 
467e2ce7255SDoug Thompson 	if (boot_cpu_data.x86 > 0xf)
468bc21fa57SBorislav Petkov 		*hole_offset = f10_dhar_offset(pvt);
469e2ce7255SDoug Thompson 	else
470bc21fa57SBorislav Petkov 		*hole_offset = k8_dhar_offset(pvt);
471e2ce7255SDoug Thompson 
472956b9ba1SJoe Perches 	edac_dbg(1, "  DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
473e2ce7255SDoug Thompson 		 pvt->mc_node_id, (unsigned long)*hole_base,
474e2ce7255SDoug Thompson 		 (unsigned long)*hole_offset, (unsigned long)*hole_size);
475e2ce7255SDoug Thompson 
476e2ce7255SDoug Thompson 	return 0;
477e2ce7255SDoug Thompson }
478e2ce7255SDoug Thompson EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info);
479e2ce7255SDoug Thompson 
48093c2df58SDoug Thompson /*
48193c2df58SDoug Thompson  * Return the DramAddr that the SysAddr given by @sys_addr maps to.  It is
48293c2df58SDoug Thompson  * assumed that sys_addr maps to the node given by mci.
48393c2df58SDoug Thompson  *
48493c2df58SDoug Thompson  * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section
48593c2df58SDoug Thompson  * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a
48693c2df58SDoug Thompson  * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled,
48793c2df58SDoug Thompson  * then it is also involved in translating a SysAddr to a DramAddr. Sections
48893c2df58SDoug Thompson  * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting.
48993c2df58SDoug Thompson  * These parts of the documentation are unclear. I interpret them as follows:
49093c2df58SDoug Thompson  *
49193c2df58SDoug Thompson  * When node n receives a SysAddr, it processes the SysAddr as follows:
49293c2df58SDoug Thompson  *
49393c2df58SDoug Thompson  * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM
49493c2df58SDoug Thompson  *    Limit registers for node n. If the SysAddr is not within the range
49593c2df58SDoug Thompson  *    specified by the base and limit values, then node n ignores the Sysaddr
49693c2df58SDoug Thompson  *    (since it does not map to node n). Otherwise continue to step 2 below.
49793c2df58SDoug Thompson  *
49893c2df58SDoug Thompson  * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is
49993c2df58SDoug Thompson  *    disabled so skip to step 3 below. Otherwise see if the SysAddr is within
50093c2df58SDoug Thompson  *    the range of relocated addresses (starting at 0x100000000) from the DRAM
50193c2df58SDoug Thompson  *    hole. If not, skip to step 3 below. Else get the value of the
50293c2df58SDoug Thompson  *    DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the
50393c2df58SDoug Thompson  *    offset defined by this value from the SysAddr.
50493c2df58SDoug Thompson  *
50593c2df58SDoug Thompson  * 3. Obtain the base address for node n from the DRAMBase field of the DRAM
50693c2df58SDoug Thompson  *    Base register for node n. To obtain the DramAddr, subtract the base
50793c2df58SDoug Thompson  *    address from the SysAddr, as shown near the start of section 3.4.4 (p.70).
50893c2df58SDoug Thompson  */
50993c2df58SDoug Thompson static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
51093c2df58SDoug Thompson {
5117f19bf75SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
51293c2df58SDoug Thompson 	u64 dram_base, hole_base, hole_offset, hole_size, dram_addr;
5131f31677eSBorislav Petkov 	int ret;
51493c2df58SDoug Thompson 
5157f19bf75SBorislav Petkov 	dram_base = get_dram_base(pvt, pvt->mc_node_id);
51693c2df58SDoug Thompson 
51793c2df58SDoug Thompson 	ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
51893c2df58SDoug Thompson 				      &hole_size);
51993c2df58SDoug Thompson 	if (!ret) {
5201f31677eSBorislav Petkov 		if ((sys_addr >= (1ULL << 32)) &&
5211f31677eSBorislav Petkov 		    (sys_addr < ((1ULL << 32) + hole_size))) {
52293c2df58SDoug Thompson 			/* use DHAR to translate SysAddr to DramAddr */
52393c2df58SDoug Thompson 			dram_addr = sys_addr - hole_offset;
52493c2df58SDoug Thompson 
525956b9ba1SJoe Perches 			edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
52693c2df58SDoug Thompson 				 (unsigned long)sys_addr,
52793c2df58SDoug Thompson 				 (unsigned long)dram_addr);
52893c2df58SDoug Thompson 
52993c2df58SDoug Thompson 			return dram_addr;
53093c2df58SDoug Thompson 		}
53193c2df58SDoug Thompson 	}
53293c2df58SDoug Thompson 
53393c2df58SDoug Thompson 	/*
53493c2df58SDoug Thompson 	 * Translate the SysAddr to a DramAddr as shown near the start of
53593c2df58SDoug Thompson 	 * section 3.4.4 (p. 70).  Although sys_addr is a 64-bit value, the k8
53693c2df58SDoug Thompson 	 * only deals with 40-bit values.  Therefore we discard bits 63-40 of
53793c2df58SDoug Thompson 	 * sys_addr below.  If bit 39 of sys_addr is 1 then the bits we
53893c2df58SDoug Thompson 	 * discard are all 1s.  Otherwise the bits we discard are all 0s.  See
53993c2df58SDoug Thompson 	 * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture
54093c2df58SDoug Thompson 	 * Programmer's Manual Volume 1 Application Programming.
54193c2df58SDoug Thompson 	 */
542f678b8ccSBorislav Petkov 	dram_addr = (sys_addr & GENMASK(0, 39)) - dram_base;
54393c2df58SDoug Thompson 
544956b9ba1SJoe Perches 	edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
545956b9ba1SJoe Perches 		 (unsigned long)sys_addr, (unsigned long)dram_addr);
54693c2df58SDoug Thompson 	return dram_addr;
54793c2df58SDoug Thompson }
54893c2df58SDoug Thompson 
54993c2df58SDoug Thompson /*
55093c2df58SDoug Thompson  * @intlv_en is the value of the IntlvEn field from a DRAM Base register
55193c2df58SDoug Thompson  * (section 3.4.4.1).  Return the number of bits from a SysAddr that are used
55293c2df58SDoug Thompson  * for node interleaving.
55393c2df58SDoug Thompson  */
55493c2df58SDoug Thompson static int num_node_interleave_bits(unsigned intlv_en)
55593c2df58SDoug Thompson {
55693c2df58SDoug Thompson 	static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 };
55793c2df58SDoug Thompson 	int n;
55893c2df58SDoug Thompson 
55993c2df58SDoug Thompson 	BUG_ON(intlv_en > 7);
56093c2df58SDoug Thompson 	n = intlv_shift_table[intlv_en];
56193c2df58SDoug Thompson 	return n;
56293c2df58SDoug Thompson }
56393c2df58SDoug Thompson 
56493c2df58SDoug Thompson /* Translate the DramAddr given by @dram_addr to an InputAddr. */
56593c2df58SDoug Thompson static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
56693c2df58SDoug Thompson {
56793c2df58SDoug Thompson 	struct amd64_pvt *pvt;
56893c2df58SDoug Thompson 	int intlv_shift;
56993c2df58SDoug Thompson 	u64 input_addr;
57093c2df58SDoug Thompson 
57193c2df58SDoug Thompson 	pvt = mci->pvt_info;
57293c2df58SDoug Thompson 
57393c2df58SDoug Thompson 	/*
57493c2df58SDoug Thompson 	 * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
57593c2df58SDoug Thompson 	 * concerning translating a DramAddr to an InputAddr.
57693c2df58SDoug Thompson 	 */
5777f19bf75SBorislav Petkov 	intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
578f678b8ccSBorislav Petkov 	input_addr = ((dram_addr >> intlv_shift) & GENMASK(12, 35)) +
57993c2df58SDoug Thompson 		      (dram_addr & 0xfff);
58093c2df58SDoug Thompson 
581956b9ba1SJoe Perches 	edac_dbg(2, "  Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
58293c2df58SDoug Thompson 		 intlv_shift, (unsigned long)dram_addr,
58393c2df58SDoug Thompson 		 (unsigned long)input_addr);
58493c2df58SDoug Thompson 
58593c2df58SDoug Thompson 	return input_addr;
58693c2df58SDoug Thompson }
58793c2df58SDoug Thompson 
58893c2df58SDoug Thompson /*
58993c2df58SDoug Thompson  * Translate the SysAddr represented by @sys_addr to an InputAddr.  It is
59093c2df58SDoug Thompson  * assumed that @sys_addr maps to the node given by mci.
59193c2df58SDoug Thompson  */
59293c2df58SDoug Thompson static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
59393c2df58SDoug Thompson {
59493c2df58SDoug Thompson 	u64 input_addr;
59593c2df58SDoug Thompson 
59693c2df58SDoug Thompson 	input_addr =
59793c2df58SDoug Thompson 	    dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
59893c2df58SDoug Thompson 
599956b9ba1SJoe Perches 	edac_dbg(2, "SysAdddr 0x%lx translates to InputAddr 0x%lx\n",
60093c2df58SDoug Thompson 		 (unsigned long)sys_addr, (unsigned long)input_addr);
60193c2df58SDoug Thompson 
60293c2df58SDoug Thompson 	return input_addr;
60393c2df58SDoug Thompson }
60493c2df58SDoug Thompson 
60593c2df58SDoug Thompson 
60693c2df58SDoug Thompson /*
60793c2df58SDoug Thompson  * @input_addr is an InputAddr associated with the node represented by mci.
60893c2df58SDoug Thompson  * Translate @input_addr to a DramAddr and return the result.
60993c2df58SDoug Thompson  */
61093c2df58SDoug Thompson static u64 input_addr_to_dram_addr(struct mem_ctl_info *mci, u64 input_addr)
61193c2df58SDoug Thompson {
61293c2df58SDoug Thompson 	struct amd64_pvt *pvt;
613b487c33eSBorislav Petkov 	unsigned node_id, intlv_shift;
61493c2df58SDoug Thompson 	u64 bits, dram_addr;
61593c2df58SDoug Thompson 	u32 intlv_sel;
61693c2df58SDoug Thompson 
61793c2df58SDoug Thompson 	/*
61893c2df58SDoug Thompson 	 * Near the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
61993c2df58SDoug Thompson 	 * shows how to translate a DramAddr to an InputAddr. Here we reverse
62093c2df58SDoug Thompson 	 * this procedure. When translating from a DramAddr to an InputAddr, the
62193c2df58SDoug Thompson 	 * bits used for node interleaving are discarded.  Here we recover these
62293c2df58SDoug Thompson 	 * bits from the IntlvSel field of the DRAM Limit register (section
62393c2df58SDoug Thompson 	 * 3.4.4.2) for the node that input_addr is associated with.
62493c2df58SDoug Thompson 	 */
62593c2df58SDoug Thompson 	pvt = mci->pvt_info;
62693c2df58SDoug Thompson 	node_id = pvt->mc_node_id;
627b487c33eSBorislav Petkov 
628b487c33eSBorislav Petkov 	BUG_ON(node_id > 7);
62993c2df58SDoug Thompson 
6307f19bf75SBorislav Petkov 	intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
63193c2df58SDoug Thompson 	if (intlv_shift == 0) {
632956b9ba1SJoe Perches 		edac_dbg(1, "    InputAddr 0x%lx translates to DramAddr of same value\n",
633956b9ba1SJoe Perches 			 (unsigned long)input_addr);
63493c2df58SDoug Thompson 
63593c2df58SDoug Thompson 		return input_addr;
63693c2df58SDoug Thompson 	}
63793c2df58SDoug Thompson 
638f678b8ccSBorislav Petkov 	bits = ((input_addr & GENMASK(12, 35)) << intlv_shift) +
63993c2df58SDoug Thompson 		(input_addr & 0xfff);
64093c2df58SDoug Thompson 
6417f19bf75SBorislav Petkov 	intlv_sel = dram_intlv_sel(pvt, node_id) & ((1 << intlv_shift) - 1);
64293c2df58SDoug Thompson 	dram_addr = bits + (intlv_sel << 12);
64393c2df58SDoug Thompson 
644956b9ba1SJoe Perches 	edac_dbg(1, "InputAddr 0x%lx translates to DramAddr 0x%lx (%d node interleave bits)\n",
645956b9ba1SJoe Perches 		 (unsigned long)input_addr,
64693c2df58SDoug Thompson 		 (unsigned long)dram_addr, intlv_shift);
64793c2df58SDoug Thompson 
64893c2df58SDoug Thompson 	return dram_addr;
64993c2df58SDoug Thompson }
65093c2df58SDoug Thompson 
65193c2df58SDoug Thompson /*
65293c2df58SDoug Thompson  * @dram_addr is a DramAddr that maps to the node represented by mci. Convert
65393c2df58SDoug Thompson  * @dram_addr to a SysAddr.
65493c2df58SDoug Thompson  */
65593c2df58SDoug Thompson static u64 dram_addr_to_sys_addr(struct mem_ctl_info *mci, u64 dram_addr)
65693c2df58SDoug Thompson {
65793c2df58SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
6587f19bf75SBorislav Petkov 	u64 hole_base, hole_offset, hole_size, base, sys_addr;
65993c2df58SDoug Thompson 	int ret = 0;
66093c2df58SDoug Thompson 
66193c2df58SDoug Thompson 	ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
66293c2df58SDoug Thompson 				      &hole_size);
66393c2df58SDoug Thompson 	if (!ret) {
66493c2df58SDoug Thompson 		if ((dram_addr >= hole_base) &&
66593c2df58SDoug Thompson 		    (dram_addr < (hole_base + hole_size))) {
66693c2df58SDoug Thompson 			sys_addr = dram_addr + hole_offset;
66793c2df58SDoug Thompson 
668956b9ba1SJoe Perches 			edac_dbg(1, "using DHAR to translate DramAddr 0x%lx to SysAddr 0x%lx\n",
669956b9ba1SJoe Perches 				 (unsigned long)dram_addr,
67093c2df58SDoug Thompson 				 (unsigned long)sys_addr);
67193c2df58SDoug Thompson 
67293c2df58SDoug Thompson 			return sys_addr;
67393c2df58SDoug Thompson 		}
67493c2df58SDoug Thompson 	}
67593c2df58SDoug Thompson 
6767f19bf75SBorislav Petkov 	base     = get_dram_base(pvt, pvt->mc_node_id);
67793c2df58SDoug Thompson 	sys_addr = dram_addr + base;
67893c2df58SDoug Thompson 
67993c2df58SDoug Thompson 	/*
68093c2df58SDoug Thompson 	 * The sys_addr we have computed up to this point is a 40-bit value
68193c2df58SDoug Thompson 	 * because the k8 deals with 40-bit values.  However, the value we are
68293c2df58SDoug Thompson 	 * supposed to return is a full 64-bit physical address.  The AMD
68393c2df58SDoug Thompson 	 * x86-64 architecture specifies that the most significant implemented
68493c2df58SDoug Thompson 	 * address bit through bit 63 of a physical address must be either all
68593c2df58SDoug Thompson 	 * 0s or all 1s.  Therefore we sign-extend the 40-bit sys_addr to a
68693c2df58SDoug Thompson 	 * 64-bit value below.  See section 3.4.2 of AMD publication 24592:
68793c2df58SDoug Thompson 	 * AMD x86-64 Architecture Programmer's Manual Volume 1 Application
68893c2df58SDoug Thompson 	 * Programming.
68993c2df58SDoug Thompson 	 */
69093c2df58SDoug Thompson 	sys_addr |= ~((sys_addr & (1ull << 39)) - 1);
69193c2df58SDoug Thompson 
692956b9ba1SJoe Perches 	edac_dbg(1, "    Node %d, DramAddr 0x%lx to SysAddr 0x%lx\n",
69393c2df58SDoug Thompson 		 pvt->mc_node_id, (unsigned long)dram_addr,
69493c2df58SDoug Thompson 		 (unsigned long)sys_addr);
69593c2df58SDoug Thompson 
69693c2df58SDoug Thompson 	return sys_addr;
69793c2df58SDoug Thompson }
69893c2df58SDoug Thompson 
69993c2df58SDoug Thompson /*
70093c2df58SDoug Thompson  * @input_addr is an InputAddr associated with the node given by mci. Translate
70193c2df58SDoug Thompson  * @input_addr to a SysAddr.
70293c2df58SDoug Thompson  */
70393c2df58SDoug Thompson static inline u64 input_addr_to_sys_addr(struct mem_ctl_info *mci,
70493c2df58SDoug Thompson 					 u64 input_addr)
70593c2df58SDoug Thompson {
70693c2df58SDoug Thompson 	return dram_addr_to_sys_addr(mci,
70793c2df58SDoug Thompson 				     input_addr_to_dram_addr(mci, input_addr));
70893c2df58SDoug Thompson }
70993c2df58SDoug Thompson 
71093c2df58SDoug Thompson /* Map the Error address to a PAGE and PAGE OFFSET. */
71193c2df58SDoug Thompson static inline void error_address_to_page_and_offset(u64 error_address,
71233ca0643SBorislav Petkov 						    struct err_info *err)
71393c2df58SDoug Thompson {
71433ca0643SBorislav Petkov 	err->page = (u32) (error_address >> PAGE_SHIFT);
71533ca0643SBorislav Petkov 	err->offset = ((u32) error_address) & ~PAGE_MASK;
71693c2df58SDoug Thompson }
71793c2df58SDoug Thompson 
71893c2df58SDoug Thompson /*
71993c2df58SDoug Thompson  * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address
72093c2df58SDoug Thompson  * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers
72193c2df58SDoug Thompson  * of a node that detected an ECC memory error.  mci represents the node that
72293c2df58SDoug Thompson  * the error address maps to (possibly different from the node that detected
72393c2df58SDoug Thompson  * the error).  Return the number of the csrow that sys_addr maps to, or -1 on
72493c2df58SDoug Thompson  * error.
72593c2df58SDoug Thompson  */
72693c2df58SDoug Thompson static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
72793c2df58SDoug Thompson {
72893c2df58SDoug Thompson 	int csrow;
72993c2df58SDoug Thompson 
73093c2df58SDoug Thompson 	csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr));
73193c2df58SDoug Thompson 
73293c2df58SDoug Thompson 	if (csrow == -1)
73324f9a7feSBorislav Petkov 		amd64_mc_err(mci, "Failed to translate InputAddr to csrow for "
73493c2df58SDoug Thompson 				  "address 0x%lx\n", (unsigned long)sys_addr);
73593c2df58SDoug Thompson 	return csrow;
73693c2df58SDoug Thompson }
737e2ce7255SDoug Thompson 
738bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
7392da11654SDoug Thompson 
7402da11654SDoug Thompson /*
7412da11654SDoug Thompson  * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
7422da11654SDoug Thompson  * are ECC capable.
7432da11654SDoug Thompson  */
7441f6189edSDan Carpenter static unsigned long amd64_determine_edac_cap(struct amd64_pvt *pvt)
7452da11654SDoug Thompson {
746cb328507SBorislav Petkov 	u8 bit;
7471f6189edSDan Carpenter 	unsigned long edac_cap = EDAC_FLAG_NONE;
7482da11654SDoug Thompson 
7491433eb99SBorislav Petkov 	bit = (boot_cpu_data.x86 > 0xf || pvt->ext_model >= K8_REV_F)
7502da11654SDoug Thompson 		? 19
7512da11654SDoug Thompson 		: 17;
7522da11654SDoug Thompson 
753584fcff4SBorislav Petkov 	if (pvt->dclr0 & BIT(bit))
7542da11654SDoug Thompson 		edac_cap = EDAC_FLAG_SECDED;
7552da11654SDoug Thompson 
7562da11654SDoug Thompson 	return edac_cap;
7572da11654SDoug Thompson }
7582da11654SDoug Thompson 
7598c671751SBorislav Petkov static void amd64_debug_display_dimm_sizes(struct amd64_pvt *, u8);
7602da11654SDoug Thompson 
76168798e17SBorislav Petkov static void amd64_dump_dramcfg_low(u32 dclr, int chan)
76268798e17SBorislav Petkov {
763956b9ba1SJoe Perches 	edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
76468798e17SBorislav Petkov 
765956b9ba1SJoe Perches 	edac_dbg(1, "  DIMM type: %sbuffered; all DIMMs support ECC: %s\n",
76668798e17SBorislav Petkov 		 (dclr & BIT(16)) ?  "un" : "",
76768798e17SBorislav Petkov 		 (dclr & BIT(19)) ? "yes" : "no");
76868798e17SBorislav Petkov 
769956b9ba1SJoe Perches 	edac_dbg(1, "  PAR/ERR parity: %s\n",
77068798e17SBorislav Petkov 		 (dclr & BIT(8)) ?  "enabled" : "disabled");
77168798e17SBorislav Petkov 
772cb328507SBorislav Petkov 	if (boot_cpu_data.x86 == 0x10)
773956b9ba1SJoe Perches 		edac_dbg(1, "  DCT 128bit mode width: %s\n",
77468798e17SBorislav Petkov 			 (dclr & BIT(11)) ?  "128b" : "64b");
77568798e17SBorislav Petkov 
776956b9ba1SJoe Perches 	edac_dbg(1, "  x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
77768798e17SBorislav Petkov 		 (dclr & BIT(12)) ?  "yes" : "no",
77868798e17SBorislav Petkov 		 (dclr & BIT(13)) ?  "yes" : "no",
77968798e17SBorislav Petkov 		 (dclr & BIT(14)) ?  "yes" : "no",
78068798e17SBorislav Petkov 		 (dclr & BIT(15)) ?  "yes" : "no");
78168798e17SBorislav Petkov }
78268798e17SBorislav Petkov 
7832da11654SDoug Thompson /* Display and decode various NB registers for debug purposes. */
784b2b0c605SBorislav Petkov static void dump_misc_regs(struct amd64_pvt *pvt)
7852da11654SDoug Thompson {
786956b9ba1SJoe Perches 	edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
7872da11654SDoug Thompson 
788956b9ba1SJoe Perches 	edac_dbg(1, "  NB two channel DRAM capable: %s\n",
7895980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
79068798e17SBorislav Petkov 
791956b9ba1SJoe Perches 	edac_dbg(1, "  ECC capable: %s, ChipKill ECC capable: %s\n",
7925980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
7935980bb9cSBorislav Petkov 		 (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
79468798e17SBorislav Petkov 
79568798e17SBorislav Petkov 	amd64_dump_dramcfg_low(pvt->dclr0, 0);
7962da11654SDoug Thompson 
797956b9ba1SJoe Perches 	edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
7982da11654SDoug Thompson 
799956b9ba1SJoe Perches 	edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n",
800bc21fa57SBorislav Petkov 		 pvt->dhar, dhar_base(pvt),
801bc21fa57SBorislav Petkov 		 (boot_cpu_data.x86 == 0xf) ? k8_dhar_offset(pvt)
802bc21fa57SBorislav Petkov 		 : f10_dhar_offset(pvt));
8032da11654SDoug Thompson 
804956b9ba1SJoe Perches 	edac_dbg(1, "  DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
8052da11654SDoug Thompson 
8068c671751SBorislav Petkov 	amd64_debug_display_dimm_sizes(pvt, 0);
8074d796364SBorislav Petkov 
8084d796364SBorislav Petkov 	/* everything below this point is Fam10h and above */
8094d796364SBorislav Petkov 	if (boot_cpu_data.x86 == 0xf)
8102da11654SDoug Thompson 		return;
8114d796364SBorislav Petkov 
8128c671751SBorislav Petkov 	amd64_debug_display_dimm_sizes(pvt, 1);
8132da11654SDoug Thompson 
814a3b7db09SBorislav Petkov 	amd64_info("using %s syndromes.\n", ((pvt->ecc_sym_sz == 8) ? "x8" : "x4"));
815ad6a32e9SBorislav Petkov 
8168de1d91eSBorislav Petkov 	/* Only if NOT ganged does dclr1 have valid info */
81768798e17SBorislav Petkov 	if (!dct_ganging_enabled(pvt))
81868798e17SBorislav Petkov 		amd64_dump_dramcfg_low(pvt->dclr1, 1);
8192da11654SDoug Thompson }
8202da11654SDoug Thompson 
82194be4bffSDoug Thompson /*
82211c75eadSBorislav Petkov  * see BKDG, F2x[1,0][5C:40], F2[1,0][6C:60]
82394be4bffSDoug Thompson  */
82411c75eadSBorislav Petkov static void prep_chip_selects(struct amd64_pvt *pvt)
82594be4bffSDoug Thompson {
8261433eb99SBorislav Petkov 	if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_F) {
82711c75eadSBorislav Petkov 		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
82811c75eadSBorislav Petkov 		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8;
8299d858bb1SBorislav Petkov 	} else {
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 = 4;
8329d858bb1SBorislav Petkov 	}
83394be4bffSDoug Thompson }
83494be4bffSDoug Thompson 
83594be4bffSDoug Thompson /*
83611c75eadSBorislav Petkov  * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers
83794be4bffSDoug Thompson  */
838b2b0c605SBorislav Petkov static void read_dct_base_mask(struct amd64_pvt *pvt)
83994be4bffSDoug Thompson {
84011c75eadSBorislav Petkov 	int cs;
84194be4bffSDoug Thompson 
84211c75eadSBorislav Petkov 	prep_chip_selects(pvt);
84394be4bffSDoug Thompson 
84411c75eadSBorislav Petkov 	for_each_chip_select(cs, 0, pvt) {
84571d2a32eSBorislav Petkov 		int reg0   = DCSB0 + (cs * 4);
84671d2a32eSBorislav Petkov 		int reg1   = DCSB1 + (cs * 4);
84711c75eadSBorislav Petkov 		u32 *base0 = &pvt->csels[0].csbases[cs];
84811c75eadSBorislav Petkov 		u32 *base1 = &pvt->csels[1].csbases[cs];
849b2b0c605SBorislav Petkov 
85011c75eadSBorislav Petkov 		if (!amd64_read_dct_pci_cfg(pvt, reg0, base0))
851956b9ba1SJoe Perches 			edac_dbg(0, "  DCSB0[%d]=0x%08x reg: F2x%x\n",
85211c75eadSBorislav Petkov 				 cs, *base0, reg0);
85394be4bffSDoug Thompson 
85411c75eadSBorislav Petkov 		if (boot_cpu_data.x86 == 0xf || dct_ganging_enabled(pvt))
85511c75eadSBorislav Petkov 			continue;
856b2b0c605SBorislav Petkov 
85711c75eadSBorislav Petkov 		if (!amd64_read_dct_pci_cfg(pvt, reg1, base1))
858956b9ba1SJoe Perches 			edac_dbg(0, "  DCSB1[%d]=0x%08x reg: F2x%x\n",
85911c75eadSBorislav Petkov 				 cs, *base1, reg1);
86094be4bffSDoug Thompson 	}
86194be4bffSDoug Thompson 
86211c75eadSBorislav Petkov 	for_each_chip_select_mask(cs, 0, pvt) {
86371d2a32eSBorislav Petkov 		int reg0   = DCSM0 + (cs * 4);
86471d2a32eSBorislav Petkov 		int reg1   = DCSM1 + (cs * 4);
86511c75eadSBorislav Petkov 		u32 *mask0 = &pvt->csels[0].csmasks[cs];
86611c75eadSBorislav Petkov 		u32 *mask1 = &pvt->csels[1].csmasks[cs];
867b2b0c605SBorislav Petkov 
86811c75eadSBorislav Petkov 		if (!amd64_read_dct_pci_cfg(pvt, reg0, mask0))
869956b9ba1SJoe Perches 			edac_dbg(0, "    DCSM0[%d]=0x%08x reg: F2x%x\n",
87011c75eadSBorislav Petkov 				 cs, *mask0, reg0);
87194be4bffSDoug Thompson 
87211c75eadSBorislav Petkov 		if (boot_cpu_data.x86 == 0xf || dct_ganging_enabled(pvt))
87311c75eadSBorislav Petkov 			continue;
874b2b0c605SBorislav Petkov 
87511c75eadSBorislav Petkov 		if (!amd64_read_dct_pci_cfg(pvt, reg1, mask1))
876956b9ba1SJoe Perches 			edac_dbg(0, "    DCSM1[%d]=0x%08x reg: F2x%x\n",
87711c75eadSBorislav Petkov 				 cs, *mask1, reg1);
87894be4bffSDoug Thompson 	}
8796ba5dcdcSBorislav Petkov }
88094be4bffSDoug Thompson 
88124f9a7feSBorislav Petkov static enum mem_type amd64_determine_memory_type(struct amd64_pvt *pvt, int cs)
88294be4bffSDoug Thompson {
88394be4bffSDoug Thompson 	enum mem_type type;
88494be4bffSDoug Thompson 
885cb328507SBorislav Petkov 	/* F15h supports only DDR3 */
886cb328507SBorislav Petkov 	if (boot_cpu_data.x86 >= 0x15)
887cb328507SBorislav Petkov 		type = (pvt->dclr0 & BIT(16)) ?	MEM_DDR3 : MEM_RDDR3;
888cb328507SBorislav Petkov 	else if (boot_cpu_data.x86 == 0x10 || pvt->ext_model >= K8_REV_F) {
8896b4c0bdeSBorislav Petkov 		if (pvt->dchr0 & DDR3_MODE)
8906b4c0bdeSBorislav Petkov 			type = (pvt->dclr0 & BIT(16)) ?	MEM_DDR3 : MEM_RDDR3;
8916b4c0bdeSBorislav Petkov 		else
89294be4bffSDoug Thompson 			type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
89394be4bffSDoug Thompson 	} else {
89494be4bffSDoug Thompson 		type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
89594be4bffSDoug Thompson 	}
89694be4bffSDoug Thompson 
89724f9a7feSBorislav Petkov 	amd64_info("CS%d: %s\n", cs, edac_mem_types[type]);
89894be4bffSDoug Thompson 
89994be4bffSDoug Thompson 	return type;
90094be4bffSDoug Thompson }
90194be4bffSDoug Thompson 
902cb328507SBorislav Petkov /* Get the number of DCT channels the memory controller is using. */
903ddff876dSDoug Thompson static int k8_early_channel_count(struct amd64_pvt *pvt)
904ddff876dSDoug Thompson {
905cb328507SBorislav Petkov 	int flag;
906ddff876dSDoug Thompson 
9079f56da0eSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F)
908ddff876dSDoug Thompson 		/* RevF (NPT) and later */
90941d8bfabSBorislav Petkov 		flag = pvt->dclr0 & WIDTH_128;
9109f56da0eSBorislav Petkov 	else
911ddff876dSDoug Thompson 		/* RevE and earlier */
912ddff876dSDoug Thompson 		flag = pvt->dclr0 & REVE_WIDTH_128;
913ddff876dSDoug Thompson 
914ddff876dSDoug Thompson 	/* not used */
915ddff876dSDoug Thompson 	pvt->dclr1 = 0;
916ddff876dSDoug Thompson 
917ddff876dSDoug Thompson 	return (flag) ? 2 : 1;
918ddff876dSDoug Thompson }
919ddff876dSDoug Thompson 
92070046624SBorislav Petkov /* On F10h and later ErrAddr is MC4_ADDR[47:1] */
92170046624SBorislav Petkov static u64 get_error_address(struct mce *m)
922ddff876dSDoug Thompson {
923c1ae6830SBorislav Petkov 	struct cpuinfo_x86 *c = &boot_cpu_data;
924c1ae6830SBorislav Petkov 	u64 addr;
92570046624SBorislav Petkov 	u8 start_bit = 1;
92670046624SBorislav Petkov 	u8 end_bit   = 47;
92770046624SBorislav Petkov 
928c1ae6830SBorislav Petkov 	if (c->x86 == 0xf) {
92970046624SBorislav Petkov 		start_bit = 3;
93070046624SBorislav Petkov 		end_bit   = 39;
93170046624SBorislav Petkov 	}
93270046624SBorislav Petkov 
933c1ae6830SBorislav Petkov 	addr = m->addr & GENMASK(start_bit, end_bit);
934c1ae6830SBorislav Petkov 
935c1ae6830SBorislav Petkov 	/*
936c1ae6830SBorislav Petkov 	 * Erratum 637 workaround
937c1ae6830SBorislav Petkov 	 */
938c1ae6830SBorislav Petkov 	if (c->x86 == 0x15) {
939c1ae6830SBorislav Petkov 		struct amd64_pvt *pvt;
940c1ae6830SBorislav Petkov 		u64 cc6_base, tmp_addr;
941c1ae6830SBorislav Petkov 		u32 tmp;
942c1ae6830SBorislav Petkov 		u8 mce_nid, intlv_en;
943c1ae6830SBorislav Petkov 
944c1ae6830SBorislav Petkov 		if ((addr & GENMASK(24, 47)) >> 24 != 0x00fdf7)
945c1ae6830SBorislav Petkov 			return addr;
946c1ae6830SBorislav Petkov 
947c1ae6830SBorislav Petkov 		mce_nid	= amd_get_nb_id(m->extcpu);
948c1ae6830SBorislav Petkov 		pvt	= mcis[mce_nid]->pvt_info;
949c1ae6830SBorislav Petkov 
950c1ae6830SBorislav Petkov 		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp);
951c1ae6830SBorislav Petkov 		intlv_en = tmp >> 21 & 0x7;
952c1ae6830SBorislav Petkov 
953c1ae6830SBorislav Petkov 		/* add [47:27] + 3 trailing bits */
954c1ae6830SBorislav Petkov 		cc6_base  = (tmp & GENMASK(0, 20)) << 3;
955c1ae6830SBorislav Petkov 
956c1ae6830SBorislav Petkov 		/* reverse and add DramIntlvEn */
957c1ae6830SBorislav Petkov 		cc6_base |= intlv_en ^ 0x7;
958c1ae6830SBorislav Petkov 
959c1ae6830SBorislav Petkov 		/* pin at [47:24] */
960c1ae6830SBorislav Petkov 		cc6_base <<= 24;
961c1ae6830SBorislav Petkov 
962c1ae6830SBorislav Petkov 		if (!intlv_en)
963c1ae6830SBorislav Petkov 			return cc6_base | (addr & GENMASK(0, 23));
964c1ae6830SBorislav Petkov 
965c1ae6830SBorislav Petkov 		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp);
966c1ae6830SBorislav Petkov 
967c1ae6830SBorislav Petkov 							/* faster log2 */
968c1ae6830SBorislav Petkov 		tmp_addr  = (addr & GENMASK(12, 23)) << __fls(intlv_en + 1);
969c1ae6830SBorislav Petkov 
970c1ae6830SBorislav Petkov 		/* OR DramIntlvSel into bits [14:12] */
971c1ae6830SBorislav Petkov 		tmp_addr |= (tmp & GENMASK(21, 23)) >> 9;
972c1ae6830SBorislav Petkov 
973c1ae6830SBorislav Petkov 		/* add remaining [11:0] bits from original MC4_ADDR */
974c1ae6830SBorislav Petkov 		tmp_addr |= addr & GENMASK(0, 11);
975c1ae6830SBorislav Petkov 
976c1ae6830SBorislav Petkov 		return cc6_base | tmp_addr;
977c1ae6830SBorislav Petkov 	}
978c1ae6830SBorislav Petkov 
979c1ae6830SBorislav Petkov 	return addr;
980ddff876dSDoug Thompson }
981ddff876dSDoug Thompson 
9827f19bf75SBorislav Petkov static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
983ddff876dSDoug Thompson {
984f08e457cSBorislav Petkov 	struct cpuinfo_x86 *c = &boot_cpu_data;
98571d2a32eSBorislav Petkov 	int off = range << 3;
986ddff876dSDoug Thompson 
9877f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off,  &pvt->ranges[range].base.lo);
9887f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo);
989ddff876dSDoug Thompson 
990f08e457cSBorislav Petkov 	if (c->x86 == 0xf)
9917f19bf75SBorislav Petkov 		return;
992ddff876dSDoug Thompson 
9937f19bf75SBorislav Petkov 	if (!dram_rw(pvt, range))
9947f19bf75SBorislav Petkov 		return;
995ddff876dSDoug Thompson 
9967f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off,  &pvt->ranges[range].base.hi);
9977f19bf75SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi);
998f08e457cSBorislav Petkov 
999f08e457cSBorislav Petkov 	/* Factor in CC6 save area by reading dst node's limit reg */
1000f08e457cSBorislav Petkov 	if (c->x86 == 0x15) {
1001f08e457cSBorislav Petkov 		struct pci_dev *f1 = NULL;
1002f08e457cSBorislav Petkov 		u8 nid = dram_dst_node(pvt, range);
1003f08e457cSBorislav Petkov 		u32 llim;
1004f08e457cSBorislav Petkov 
1005f08e457cSBorislav Petkov 		f1 = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0x18 + nid, 1));
1006f08e457cSBorislav Petkov 		if (WARN_ON(!f1))
1007f08e457cSBorislav Petkov 			return;
1008f08e457cSBorislav Petkov 
1009f08e457cSBorislav Petkov 		amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim);
1010f08e457cSBorislav Petkov 
1011f08e457cSBorislav Petkov 		pvt->ranges[range].lim.lo &= GENMASK(0, 15);
1012f08e457cSBorislav Petkov 
1013f08e457cSBorislav Petkov 					    /* {[39:27],111b} */
1014f08e457cSBorislav Petkov 		pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16;
1015f08e457cSBorislav Petkov 
1016f08e457cSBorislav Petkov 		pvt->ranges[range].lim.hi &= GENMASK(0, 7);
1017f08e457cSBorislav Petkov 
1018f08e457cSBorislav Petkov 					    /* [47:40] */
1019f08e457cSBorislav Petkov 		pvt->ranges[range].lim.hi |= llim >> 13;
1020f08e457cSBorislav Petkov 
1021f08e457cSBorislav Petkov 		pci_dev_put(f1);
1022f08e457cSBorislav Petkov 	}
1023ddff876dSDoug Thompson }
1024ddff876dSDoug Thompson 
1025f192c7b1SBorislav Petkov static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
102633ca0643SBorislav Petkov 				    struct err_info *err)
1027ddff876dSDoug Thompson {
1028f192c7b1SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
1029ddff876dSDoug Thompson 
103033ca0643SBorislav Petkov 	error_address_to_page_and_offset(sys_addr, err);
1031ab5a503cSMauro Carvalho Chehab 
1032ab5a503cSMauro Carvalho Chehab 	/*
1033ab5a503cSMauro Carvalho Chehab 	 * Find out which node the error address belongs to. This may be
1034ab5a503cSMauro Carvalho Chehab 	 * different from the node that detected the error.
1035ab5a503cSMauro Carvalho Chehab 	 */
103633ca0643SBorislav Petkov 	err->src_mci = find_mc_by_sys_addr(mci, sys_addr);
103733ca0643SBorislav Petkov 	if (!err->src_mci) {
1038ab5a503cSMauro Carvalho Chehab 		amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
1039ab5a503cSMauro Carvalho Chehab 			     (unsigned long)sys_addr);
104033ca0643SBorislav Petkov 		err->err_code = ERR_NODE;
1041ab5a503cSMauro Carvalho Chehab 		return;
1042ab5a503cSMauro Carvalho Chehab 	}
1043ab5a503cSMauro Carvalho Chehab 
1044ab5a503cSMauro Carvalho Chehab 	/* Now map the sys_addr to a CSROW */
104533ca0643SBorislav Petkov 	err->csrow = sys_addr_to_csrow(err->src_mci, sys_addr);
104633ca0643SBorislav Petkov 	if (err->csrow < 0) {
104733ca0643SBorislav Petkov 		err->err_code = ERR_CSROW;
1048ab5a503cSMauro Carvalho Chehab 		return;
1049ab5a503cSMauro Carvalho Chehab 	}
1050ab5a503cSMauro Carvalho Chehab 
1051ddff876dSDoug Thompson 	/* CHIPKILL enabled */
1052f192c7b1SBorislav Petkov 	if (pvt->nbcfg & NBCFG_CHIPKILL) {
105333ca0643SBorislav Petkov 		err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
105433ca0643SBorislav Petkov 		if (err->channel < 0) {
1055ddff876dSDoug Thompson 			/*
1056ddff876dSDoug Thompson 			 * Syndrome didn't map, so we don't know which of the
1057ddff876dSDoug Thompson 			 * 2 DIMMs is in error. So we need to ID 'both' of them
1058ddff876dSDoug Thompson 			 * as suspect.
1059ddff876dSDoug Thompson 			 */
106033ca0643SBorislav Petkov 			amd64_mc_warn(err->src_mci, "unknown syndrome 0x%04x - "
1061ab5a503cSMauro Carvalho Chehab 				      "possible error reporting race\n",
106233ca0643SBorislav Petkov 				      err->syndrome);
106333ca0643SBorislav Petkov 			err->err_code = ERR_CHANNEL;
1064ddff876dSDoug Thompson 			return;
1065ddff876dSDoug Thompson 		}
1066ddff876dSDoug Thompson 	} else {
1067ddff876dSDoug Thompson 		/*
1068ddff876dSDoug Thompson 		 * non-chipkill ecc mode
1069ddff876dSDoug Thompson 		 *
1070ddff876dSDoug Thompson 		 * The k8 documentation is unclear about how to determine the
1071ddff876dSDoug Thompson 		 * channel number when using non-chipkill memory.  This method
1072ddff876dSDoug Thompson 		 * was obtained from email communication with someone at AMD.
1073ddff876dSDoug Thompson 		 * (Wish the email was placed in this comment - norsk)
1074ddff876dSDoug Thompson 		 */
107533ca0643SBorislav Petkov 		err->channel = ((sys_addr & BIT(3)) != 0);
1076ddff876dSDoug Thompson 	}
1077ddff876dSDoug Thompson }
1078ddff876dSDoug Thompson 
107941d8bfabSBorislav Petkov static int ddr2_cs_size(unsigned i, bool dct_width)
1080ddff876dSDoug Thompson {
108141d8bfabSBorislav Petkov 	unsigned shift = 0;
1082ddff876dSDoug Thompson 
108341d8bfabSBorislav Petkov 	if (i <= 2)
108441d8bfabSBorislav Petkov 		shift = i;
108541d8bfabSBorislav Petkov 	else if (!(i & 0x1))
108641d8bfabSBorislav Petkov 		shift = i >> 1;
10871433eb99SBorislav Petkov 	else
108841d8bfabSBorislav Petkov 		shift = (i + 1) >> 1;
1089ddff876dSDoug Thompson 
109041d8bfabSBorislav Petkov 	return 128 << (shift + !!dct_width);
109141d8bfabSBorislav Petkov }
109241d8bfabSBorislav Petkov 
109341d8bfabSBorislav Petkov static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
109441d8bfabSBorislav Petkov 				  unsigned cs_mode)
109541d8bfabSBorislav Petkov {
109641d8bfabSBorislav Petkov 	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
109741d8bfabSBorislav Petkov 
109841d8bfabSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F) {
109941d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 11);
110041d8bfabSBorislav Petkov 		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
110141d8bfabSBorislav Petkov 	}
110241d8bfabSBorislav Petkov 	else if (pvt->ext_model >= K8_REV_D) {
110311b0a314SBorislav Petkov 		unsigned diff;
110441d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 10);
110541d8bfabSBorislav Petkov 
110611b0a314SBorislav Petkov 		/*
110711b0a314SBorislav Petkov 		 * the below calculation, besides trying to win an obfuscated C
110811b0a314SBorislav Petkov 		 * contest, maps cs_mode values to DIMM chip select sizes. The
110911b0a314SBorislav Petkov 		 * mappings are:
111011b0a314SBorislav Petkov 		 *
111111b0a314SBorislav Petkov 		 * cs_mode	CS size (mb)
111211b0a314SBorislav Petkov 		 * =======	============
111311b0a314SBorislav Petkov 		 * 0		32
111411b0a314SBorislav Petkov 		 * 1		64
111511b0a314SBorislav Petkov 		 * 2		128
111611b0a314SBorislav Petkov 		 * 3		128
111711b0a314SBorislav Petkov 		 * 4		256
111811b0a314SBorislav Petkov 		 * 5		512
111911b0a314SBorislav Petkov 		 * 6		256
112011b0a314SBorislav Petkov 		 * 7		512
112111b0a314SBorislav Petkov 		 * 8		1024
112211b0a314SBorislav Petkov 		 * 9		1024
112311b0a314SBorislav Petkov 		 * 10		2048
112411b0a314SBorislav Petkov 		 *
112511b0a314SBorislav Petkov 		 * Basically, it calculates a value with which to shift the
112611b0a314SBorislav Petkov 		 * smallest CS size of 32MB.
112711b0a314SBorislav Petkov 		 *
112811b0a314SBorislav Petkov 		 * ddr[23]_cs_size have a similar purpose.
112911b0a314SBorislav Petkov 		 */
113011b0a314SBorislav Petkov 		diff = cs_mode/3 + (unsigned)(cs_mode > 5);
113111b0a314SBorislav Petkov 
113211b0a314SBorislav Petkov 		return 32 << (cs_mode - diff);
113341d8bfabSBorislav Petkov 	}
113441d8bfabSBorislav Petkov 	else {
113541d8bfabSBorislav Petkov 		WARN_ON(cs_mode > 6);
113641d8bfabSBorislav Petkov 		return 32 << cs_mode;
113741d8bfabSBorislav Petkov 	}
1138ddff876dSDoug Thompson }
1139ddff876dSDoug Thompson 
11401afd3c98SDoug Thompson /*
11411afd3c98SDoug Thompson  * Get the number of DCT channels in use.
11421afd3c98SDoug Thompson  *
11431afd3c98SDoug Thompson  * Return:
11441afd3c98SDoug Thompson  *	number of Memory Channels in operation
11451afd3c98SDoug Thompson  * Pass back:
11461afd3c98SDoug Thompson  *	contents of the DCL0_LOW register
11471afd3c98SDoug Thompson  */
11487d20d14dSBorislav Petkov static int f1x_early_channel_count(struct amd64_pvt *pvt)
11491afd3c98SDoug Thompson {
11506ba5dcdcSBorislav Petkov 	int i, j, channels = 0;
1151ddff876dSDoug Thompson 
11527d20d14dSBorislav Petkov 	/* On F10h, if we are in 128 bit mode, then we are using 2 channels */
115341d8bfabSBorislav Petkov 	if (boot_cpu_data.x86 == 0x10 && (pvt->dclr0 & WIDTH_128))
11547d20d14dSBorislav Petkov 		return 2;
11551afd3c98SDoug Thompson 
11561afd3c98SDoug Thompson 	/*
1157d16149e8SBorislav Petkov 	 * Need to check if in unganged mode: In such, there are 2 channels,
1158d16149e8SBorislav Petkov 	 * but they are not in 128 bit mode and thus the above 'dclr0' status
1159d16149e8SBorislav Petkov 	 * bit will be OFF.
11601afd3c98SDoug Thompson 	 *
11611afd3c98SDoug Thompson 	 * Need to check DCT0[0] and DCT1[0] to see if only one of them has
11621afd3c98SDoug Thompson 	 * their CSEnable bit on. If so, then SINGLE DIMM case.
11631afd3c98SDoug Thompson 	 */
1164956b9ba1SJoe Perches 	edac_dbg(0, "Data width is not 128 bits - need more decoding\n");
11651afd3c98SDoug Thompson 
11661afd3c98SDoug Thompson 	/*
11671afd3c98SDoug Thompson 	 * Check DRAM Bank Address Mapping values for each DIMM to see if there
11681afd3c98SDoug Thompson 	 * is more than just one DIMM present in unganged mode. Need to check
11691afd3c98SDoug Thompson 	 * both controllers since DIMMs can be placed in either one.
11701afd3c98SDoug Thompson 	 */
1171525a1b20SBorislav Petkov 	for (i = 0; i < 2; i++) {
1172525a1b20SBorislav Petkov 		u32 dbam = (i ? pvt->dbam1 : pvt->dbam0);
11731afd3c98SDoug Thompson 
117457a30854SWan Wei 		for (j = 0; j < 4; j++) {
117557a30854SWan Wei 			if (DBAM_DIMM(j, dbam) > 0) {
11761afd3c98SDoug Thompson 				channels++;
117757a30854SWan Wei 				break;
11781afd3c98SDoug Thompson 			}
117957a30854SWan Wei 		}
118057a30854SWan Wei 	}
11811afd3c98SDoug Thompson 
1182d16149e8SBorislav Petkov 	if (channels > 2)
1183d16149e8SBorislav Petkov 		channels = 2;
1184d16149e8SBorislav Petkov 
118524f9a7feSBorislav Petkov 	amd64_info("MCT channel count: %d\n", channels);
11861afd3c98SDoug Thompson 
11871afd3c98SDoug Thompson 	return channels;
11881afd3c98SDoug Thompson }
11891afd3c98SDoug Thompson 
119041d8bfabSBorislav Petkov static int ddr3_cs_size(unsigned i, bool dct_width)
11911afd3c98SDoug Thompson {
119241d8bfabSBorislav Petkov 	unsigned shift = 0;
119341d8bfabSBorislav Petkov 	int cs_size = 0;
119441d8bfabSBorislav Petkov 
119541d8bfabSBorislav Petkov 	if (i == 0 || i == 3 || i == 4)
119641d8bfabSBorislav Petkov 		cs_size = -1;
119741d8bfabSBorislav Petkov 	else if (i <= 2)
119841d8bfabSBorislav Petkov 		shift = i;
119941d8bfabSBorislav Petkov 	else if (i == 12)
120041d8bfabSBorislav Petkov 		shift = 7;
120141d8bfabSBorislav Petkov 	else if (!(i & 0x1))
120241d8bfabSBorislav Petkov 		shift = i >> 1;
120341d8bfabSBorislav Petkov 	else
120441d8bfabSBorislav Petkov 		shift = (i + 1) >> 1;
120541d8bfabSBorislav Petkov 
120641d8bfabSBorislav Petkov 	if (cs_size != -1)
120741d8bfabSBorislav Petkov 		cs_size = (128 * (1 << !!dct_width)) << shift;
120841d8bfabSBorislav Petkov 
120941d8bfabSBorislav Petkov 	return cs_size;
121041d8bfabSBorislav Petkov }
121141d8bfabSBorislav Petkov 
121241d8bfabSBorislav Petkov static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
121341d8bfabSBorislav Petkov 				   unsigned cs_mode)
121441d8bfabSBorislav Petkov {
121541d8bfabSBorislav Petkov 	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
121641d8bfabSBorislav Petkov 
121741d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 11);
12181433eb99SBorislav Petkov 
12191433eb99SBorislav Petkov 	if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
122041d8bfabSBorislav Petkov 		return ddr3_cs_size(cs_mode, dclr & WIDTH_128);
12211433eb99SBorislav Petkov 	else
122241d8bfabSBorislav Petkov 		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
122341d8bfabSBorislav Petkov }
12241433eb99SBorislav Petkov 
122541d8bfabSBorislav Petkov /*
122641d8bfabSBorislav Petkov  * F15h supports only 64bit DCT interfaces
122741d8bfabSBorislav Petkov  */
122841d8bfabSBorislav Petkov static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
122941d8bfabSBorislav Petkov 				   unsigned cs_mode)
123041d8bfabSBorislav Petkov {
123141d8bfabSBorislav Petkov 	WARN_ON(cs_mode > 12);
123241d8bfabSBorislav Petkov 
123341d8bfabSBorislav Petkov 	return ddr3_cs_size(cs_mode, false);
12341afd3c98SDoug Thompson }
12351afd3c98SDoug Thompson 
12365a5d2371SBorislav Petkov static void read_dram_ctl_register(struct amd64_pvt *pvt)
12376163b5d4SDoug Thompson {
12386163b5d4SDoug Thompson 
12395a5d2371SBorislav Petkov 	if (boot_cpu_data.x86 == 0xf)
12405a5d2371SBorislav Petkov 		return;
12415a5d2371SBorislav Petkov 
124278da121eSBorislav Petkov 	if (!amd64_read_dct_pci_cfg(pvt, DCT_SEL_LO, &pvt->dct_sel_lo)) {
1243956b9ba1SJoe Perches 		edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
124478da121eSBorislav Petkov 			 pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
12456163b5d4SDoug Thompson 
1246956b9ba1SJoe Perches 		edac_dbg(0, "  DCTs operate in %s mode\n",
12475a5d2371SBorislav Petkov 			 (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
12486163b5d4SDoug Thompson 
124972381bd5SBorislav Petkov 		if (!dct_ganging_enabled(pvt))
1250956b9ba1SJoe Perches 			edac_dbg(0, "  Address range split per DCT: %s\n",
125172381bd5SBorislav Petkov 				 (dct_high_range_enabled(pvt) ? "yes" : "no"));
125272381bd5SBorislav Petkov 
1253956b9ba1SJoe Perches 		edac_dbg(0, "  data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n",
125472381bd5SBorislav Petkov 			 (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
125572381bd5SBorislav Petkov 			 (dct_memory_cleared(pvt) ? "yes" : "no"));
125672381bd5SBorislav Petkov 
1257956b9ba1SJoe Perches 		edac_dbg(0, "  channel interleave: %s, "
125878da121eSBorislav Petkov 			 "interleave bits selector: 0x%x\n",
125972381bd5SBorislav Petkov 			 (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
12606163b5d4SDoug Thompson 			 dct_sel_interleave_addr(pvt));
12616163b5d4SDoug Thompson 	}
12626163b5d4SDoug Thompson 
126378da121eSBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, DCT_SEL_HI, &pvt->dct_sel_hi);
12646163b5d4SDoug Thompson }
12656163b5d4SDoug Thompson 
1266f71d0a05SDoug Thompson /*
1267229a7a11SBorislav Petkov  * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory
1268f71d0a05SDoug Thompson  * Interleaving Modes.
1269f71d0a05SDoug Thompson  */
1270b15f0fcaSBorislav Petkov static u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
1271229a7a11SBorislav Petkov 				bool hi_range_sel, u8 intlv_en)
12726163b5d4SDoug Thompson {
1273151fa71cSBorislav Petkov 	u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1;
12746163b5d4SDoug Thompson 
12756163b5d4SDoug Thompson 	if (dct_ganging_enabled(pvt))
1276229a7a11SBorislav Petkov 		return 0;
1277229a7a11SBorislav Petkov 
1278229a7a11SBorislav Petkov 	if (hi_range_sel)
1279229a7a11SBorislav Petkov 		return dct_sel_high;
1280229a7a11SBorislav Petkov 
1281f71d0a05SDoug Thompson 	/*
1282f71d0a05SDoug Thompson 	 * see F2x110[DctSelIntLvAddr] - channel interleave mode
1283f71d0a05SDoug Thompson 	 */
1284229a7a11SBorislav Petkov 	if (dct_interleave_enabled(pvt)) {
1285229a7a11SBorislav Petkov 		u8 intlv_addr = dct_sel_interleave_addr(pvt);
12866163b5d4SDoug Thompson 
1287229a7a11SBorislav Petkov 		/* return DCT select function: 0=DCT0, 1=DCT1 */
1288229a7a11SBorislav Petkov 		if (!intlv_addr)
1289229a7a11SBorislav Petkov 			return sys_addr >> 6 & 1;
12906163b5d4SDoug Thompson 
1291229a7a11SBorislav Petkov 		if (intlv_addr & 0x2) {
1292229a7a11SBorislav Petkov 			u8 shift = intlv_addr & 0x1 ? 9 : 6;
1293229a7a11SBorislav Petkov 			u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) % 2;
1294229a7a11SBorislav Petkov 
1295229a7a11SBorislav Petkov 			return ((sys_addr >> shift) & 1) ^ temp;
12966163b5d4SDoug Thompson 		}
12976163b5d4SDoug Thompson 
1298229a7a11SBorislav Petkov 		return (sys_addr >> (12 + hweight8(intlv_en))) & 1;
1299229a7a11SBorislav Petkov 	}
1300229a7a11SBorislav Petkov 
1301229a7a11SBorislav Petkov 	if (dct_high_range_enabled(pvt))
1302229a7a11SBorislav Petkov 		return ~dct_sel_high & 1;
13036163b5d4SDoug Thompson 
13046163b5d4SDoug Thompson 	return 0;
13056163b5d4SDoug Thompson }
13066163b5d4SDoug Thompson 
1307c8e518d5SBorislav Petkov /* Convert the sys_addr to the normalized DCT address */
1308e761359aSBorislav Petkov static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, unsigned range,
1309c8e518d5SBorislav Petkov 				 u64 sys_addr, bool hi_rng,
1310c8e518d5SBorislav Petkov 				 u32 dct_sel_base_addr)
13116163b5d4SDoug Thompson {
13126163b5d4SDoug Thompson 	u64 chan_off;
1313c8e518d5SBorislav Petkov 	u64 dram_base		= get_dram_base(pvt, range);
1314c8e518d5SBorislav Petkov 	u64 hole_off		= f10_dhar_offset(pvt);
1315c8e518d5SBorislav Petkov 	u64 dct_sel_base_off	= (pvt->dct_sel_hi & 0xFFFFFC00) << 16;
13166163b5d4SDoug Thompson 
1317c8e518d5SBorislav Petkov 	if (hi_rng) {
1318c8e518d5SBorislav Petkov 		/*
1319c8e518d5SBorislav Petkov 		 * if
1320c8e518d5SBorislav Petkov 		 * base address of high range is below 4Gb
1321c8e518d5SBorislav Petkov 		 * (bits [47:27] at [31:11])
1322c8e518d5SBorislav Petkov 		 * DRAM address space on this DCT is hoisted above 4Gb	&&
1323c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1324c8e518d5SBorislav Petkov 		 *
1325c8e518d5SBorislav Petkov 		 *	remove hole offset from sys_addr
1326c8e518d5SBorislav Petkov 		 * else
1327c8e518d5SBorislav Petkov 		 *	remove high range offset from sys_addr
1328c8e518d5SBorislav Petkov 		 */
1329c8e518d5SBorislav Petkov 		if ((!(dct_sel_base_addr >> 16) ||
1330c8e518d5SBorislav Petkov 		     dct_sel_base_addr < dhar_base(pvt)) &&
1331972ea17aSBorislav Petkov 		    dhar_valid(pvt) &&
1332c8e518d5SBorislav Petkov 		    (sys_addr >= BIT_64(32)))
1333bc21fa57SBorislav Petkov 			chan_off = hole_off;
13346163b5d4SDoug Thompson 		else
13356163b5d4SDoug Thompson 			chan_off = dct_sel_base_off;
13366163b5d4SDoug Thompson 	} else {
1337c8e518d5SBorislav Petkov 		/*
1338c8e518d5SBorislav Petkov 		 * if
1339c8e518d5SBorislav Petkov 		 * we have a valid hole		&&
1340c8e518d5SBorislav Petkov 		 * sys_addr > 4Gb
1341c8e518d5SBorislav Petkov 		 *
1342c8e518d5SBorislav Petkov 		 *	remove hole
1343c8e518d5SBorislav Petkov 		 * else
1344c8e518d5SBorislav Petkov 		 *	remove dram base to normalize to DCT address
1345c8e518d5SBorislav Petkov 		 */
1346972ea17aSBorislav Petkov 		if (dhar_valid(pvt) && (sys_addr >= BIT_64(32)))
1347bc21fa57SBorislav Petkov 			chan_off = hole_off;
13486163b5d4SDoug Thompson 		else
1349c8e518d5SBorislav Petkov 			chan_off = dram_base;
13506163b5d4SDoug Thompson 	}
13516163b5d4SDoug Thompson 
1352c8e518d5SBorislav Petkov 	return (sys_addr & GENMASK(6,47)) - (chan_off & GENMASK(23,47));
13536163b5d4SDoug Thompson }
13546163b5d4SDoug Thompson 
13556163b5d4SDoug Thompson /*
13566163b5d4SDoug Thompson  * checks if the csrow passed in is marked as SPARED, if so returns the new
13576163b5d4SDoug Thompson  * spare row
13586163b5d4SDoug Thompson  */
135911c75eadSBorislav Petkov static int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow)
13606163b5d4SDoug Thompson {
1361614ec9d8SBorislav Petkov 	int tmp_cs;
13626163b5d4SDoug Thompson 
1363614ec9d8SBorislav Petkov 	if (online_spare_swap_done(pvt, dct) &&
1364614ec9d8SBorislav Petkov 	    csrow == online_spare_bad_dramcs(pvt, dct)) {
1365614ec9d8SBorislav Petkov 
1366614ec9d8SBorislav Petkov 		for_each_chip_select(tmp_cs, dct, pvt) {
1367614ec9d8SBorislav Petkov 			if (chip_select_base(tmp_cs, dct, pvt) & 0x2) {
1368614ec9d8SBorislav Petkov 				csrow = tmp_cs;
1369614ec9d8SBorislav Petkov 				break;
1370614ec9d8SBorislav Petkov 			}
1371614ec9d8SBorislav Petkov 		}
13726163b5d4SDoug Thompson 	}
13736163b5d4SDoug Thompson 	return csrow;
13746163b5d4SDoug Thompson }
13756163b5d4SDoug Thompson 
13766163b5d4SDoug Thompson /*
13776163b5d4SDoug Thompson  * Iterate over the DRAM DCT "base" and "mask" registers looking for a
13786163b5d4SDoug Thompson  * SystemAddr match on the specified 'ChannelSelect' and 'NodeID'
13796163b5d4SDoug Thompson  *
13806163b5d4SDoug Thompson  * Return:
13816163b5d4SDoug Thompson  *	-EINVAL:  NOT FOUND
13826163b5d4SDoug Thompson  *	0..csrow = Chip-Select Row
13836163b5d4SDoug Thompson  */
1384b15f0fcaSBorislav Petkov static int f1x_lookup_addr_in_dct(u64 in_addr, u32 nid, u8 dct)
13856163b5d4SDoug Thompson {
13866163b5d4SDoug Thompson 	struct mem_ctl_info *mci;
13876163b5d4SDoug Thompson 	struct amd64_pvt *pvt;
138811c75eadSBorislav Petkov 	u64 cs_base, cs_mask;
13896163b5d4SDoug Thompson 	int cs_found = -EINVAL;
13906163b5d4SDoug Thompson 	int csrow;
13916163b5d4SDoug Thompson 
1392cc4d8860SBorislav Petkov 	mci = mcis[nid];
13936163b5d4SDoug Thompson 	if (!mci)
13946163b5d4SDoug Thompson 		return cs_found;
13956163b5d4SDoug Thompson 
13966163b5d4SDoug Thompson 	pvt = mci->pvt_info;
13976163b5d4SDoug Thompson 
1398956b9ba1SJoe Perches 	edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct);
13996163b5d4SDoug Thompson 
140011c75eadSBorislav Petkov 	for_each_chip_select(csrow, dct, pvt) {
140111c75eadSBorislav Petkov 		if (!csrow_enabled(csrow, dct, pvt))
14026163b5d4SDoug Thompson 			continue;
14036163b5d4SDoug Thompson 
140411c75eadSBorislav Petkov 		get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask);
14056163b5d4SDoug Thompson 
1406956b9ba1SJoe Perches 		edac_dbg(1, "    CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
14076163b5d4SDoug Thompson 			 csrow, cs_base, cs_mask);
14086163b5d4SDoug Thompson 
140911c75eadSBorislav Petkov 		cs_mask = ~cs_mask;
14106163b5d4SDoug Thompson 
1411956b9ba1SJoe Perches 		edac_dbg(1, "    (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n",
141211c75eadSBorislav Petkov 			 (in_addr & cs_mask), (cs_base & cs_mask));
14136163b5d4SDoug Thompson 
141411c75eadSBorislav Petkov 		if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
141511c75eadSBorislav Petkov 			cs_found = f10_process_possible_spare(pvt, dct, csrow);
14166163b5d4SDoug Thompson 
1417956b9ba1SJoe Perches 			edac_dbg(1, " MATCH csrow=%d\n", cs_found);
14186163b5d4SDoug Thompson 			break;
14196163b5d4SDoug Thompson 		}
14206163b5d4SDoug Thompson 	}
14216163b5d4SDoug Thompson 	return cs_found;
14226163b5d4SDoug Thompson }
14236163b5d4SDoug Thompson 
142495b0ef55SBorislav Petkov /*
142595b0ef55SBorislav Petkov  * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is
142695b0ef55SBorislav Petkov  * swapped with a region located at the bottom of memory so that the GPU can use
142795b0ef55SBorislav Petkov  * the interleaved region and thus two channels.
142895b0ef55SBorislav Petkov  */
1429b15f0fcaSBorislav Petkov static u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr)
143095b0ef55SBorislav Petkov {
143195b0ef55SBorislav Petkov 	u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr;
143295b0ef55SBorislav Petkov 
143395b0ef55SBorislav Petkov 	if (boot_cpu_data.x86 == 0x10) {
143495b0ef55SBorislav Petkov 		/* only revC3 and revE have that feature */
143595b0ef55SBorislav Petkov 		if (boot_cpu_data.x86_model < 4 ||
143695b0ef55SBorislav Petkov 		    (boot_cpu_data.x86_model < 0xa &&
143795b0ef55SBorislav Petkov 		     boot_cpu_data.x86_mask < 3))
143895b0ef55SBorislav Petkov 			return sys_addr;
143995b0ef55SBorislav Petkov 	}
144095b0ef55SBorislav Petkov 
144195b0ef55SBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, SWAP_INTLV_REG, &swap_reg);
144295b0ef55SBorislav Petkov 
144395b0ef55SBorislav Petkov 	if (!(swap_reg & 0x1))
144495b0ef55SBorislav Petkov 		return sys_addr;
144595b0ef55SBorislav Petkov 
144695b0ef55SBorislav Petkov 	swap_base	= (swap_reg >> 3) & 0x7f;
144795b0ef55SBorislav Petkov 	swap_limit	= (swap_reg >> 11) & 0x7f;
144895b0ef55SBorislav Petkov 	rgn_size	= (swap_reg >> 20) & 0x7f;
144995b0ef55SBorislav Petkov 	tmp_addr	= sys_addr >> 27;
145095b0ef55SBorislav Petkov 
145195b0ef55SBorislav Petkov 	if (!(sys_addr >> 34) &&
145295b0ef55SBorislav Petkov 	    (((tmp_addr >= swap_base) &&
145395b0ef55SBorislav Petkov 	     (tmp_addr <= swap_limit)) ||
145495b0ef55SBorislav Petkov 	     (tmp_addr < rgn_size)))
145595b0ef55SBorislav Petkov 		return sys_addr ^ (u64)swap_base << 27;
145695b0ef55SBorislav Petkov 
145795b0ef55SBorislav Petkov 	return sys_addr;
145895b0ef55SBorislav Petkov }
145995b0ef55SBorislav Petkov 
1460f71d0a05SDoug Thompson /* For a given @dram_range, check if @sys_addr falls within it. */
1461e761359aSBorislav Petkov static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
146233ca0643SBorislav Petkov 				  u64 sys_addr, int *chan_sel)
1463f71d0a05SDoug Thompson {
1464229a7a11SBorislav Petkov 	int cs_found = -EINVAL;
1465c8e518d5SBorislav Petkov 	u64 chan_addr;
14665d4b58e8SBorislav Petkov 	u32 dct_sel_base;
146711c75eadSBorislav Petkov 	u8 channel;
1468229a7a11SBorislav Petkov 	bool high_range = false;
1469f71d0a05SDoug Thompson 
14707f19bf75SBorislav Petkov 	u8 node_id    = dram_dst_node(pvt, range);
1471229a7a11SBorislav Petkov 	u8 intlv_en   = dram_intlv_en(pvt, range);
14727f19bf75SBorislav Petkov 	u32 intlv_sel = dram_intlv_sel(pvt, range);
1473f71d0a05SDoug Thompson 
1474956b9ba1SJoe Perches 	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
1475c8e518d5SBorislav Petkov 		 range, sys_addr, get_dram_limit(pvt, range));
1476f71d0a05SDoug Thompson 
1477355fba60SBorislav Petkov 	if (dhar_valid(pvt) &&
1478355fba60SBorislav Petkov 	    dhar_base(pvt) <= sys_addr &&
1479355fba60SBorislav Petkov 	    sys_addr < BIT_64(32)) {
1480355fba60SBorislav Petkov 		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
1481355fba60SBorislav Petkov 			    sys_addr);
1482f71d0a05SDoug Thompson 		return -EINVAL;
1483355fba60SBorislav Petkov 	}
1484355fba60SBorislav Petkov 
1485f030ddfbSBorislav Petkov 	if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en)))
1486355fba60SBorislav Petkov 		return -EINVAL;
1487f71d0a05SDoug Thompson 
1488b15f0fcaSBorislav Petkov 	sys_addr = f1x_swap_interleaved_region(pvt, sys_addr);
148995b0ef55SBorislav Petkov 
1490f71d0a05SDoug Thompson 	dct_sel_base = dct_sel_baseaddr(pvt);
1491f71d0a05SDoug Thompson 
1492f71d0a05SDoug Thompson 	/*
1493f71d0a05SDoug Thompson 	 * check whether addresses >= DctSelBaseAddr[47:27] are to be used to
1494f71d0a05SDoug Thompson 	 * select between DCT0 and DCT1.
1495f71d0a05SDoug Thompson 	 */
1496f71d0a05SDoug Thompson 	if (dct_high_range_enabled(pvt) &&
1497f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt) &&
1498f71d0a05SDoug Thompson 	   ((sys_addr >> 27) >= (dct_sel_base >> 11)))
1499229a7a11SBorislav Petkov 		high_range = true;
1500f71d0a05SDoug Thompson 
1501b15f0fcaSBorislav Petkov 	channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en);
1502f71d0a05SDoug Thompson 
1503b15f0fcaSBorislav Petkov 	chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr,
1504c8e518d5SBorislav Petkov 					  high_range, dct_sel_base);
1505f71d0a05SDoug Thompson 
1506e2f79dbdSBorislav Petkov 	/* Remove node interleaving, see F1x120 */
1507e2f79dbdSBorislav Petkov 	if (intlv_en)
1508e2f79dbdSBorislav Petkov 		chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) |
1509e2f79dbdSBorislav Petkov 			    (chan_addr & 0xfff);
1510f71d0a05SDoug Thompson 
15115d4b58e8SBorislav Petkov 	/* remove channel interleave */
1512f71d0a05SDoug Thompson 	if (dct_interleave_enabled(pvt) &&
1513f71d0a05SDoug Thompson 	   !dct_high_range_enabled(pvt) &&
1514f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt)) {
15155d4b58e8SBorislav Petkov 
15165d4b58e8SBorislav Petkov 		if (dct_sel_interleave_addr(pvt) != 1) {
15175d4b58e8SBorislav Petkov 			if (dct_sel_interleave_addr(pvt) == 0x3)
15185d4b58e8SBorislav Petkov 				/* hash 9 */
15195d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 10) << 9) |
15205d4b58e8SBorislav Petkov 					     (chan_addr & 0x1ff);
15215d4b58e8SBorislav Petkov 			else
15225d4b58e8SBorislav Petkov 				/* A[6] or hash 6 */
15235d4b58e8SBorislav Petkov 				chan_addr = ((chan_addr >> 7) << 6) |
15245d4b58e8SBorislav Petkov 					     (chan_addr & 0x3f);
15255d4b58e8SBorislav Petkov 		} else
15265d4b58e8SBorislav Petkov 			/* A[12] */
15275d4b58e8SBorislav Petkov 			chan_addr = ((chan_addr >> 13) << 12) |
15285d4b58e8SBorislav Petkov 				     (chan_addr & 0xfff);
1529f71d0a05SDoug Thompson 	}
1530f71d0a05SDoug Thompson 
1531956b9ba1SJoe Perches 	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
1532f71d0a05SDoug Thompson 
1533b15f0fcaSBorislav Petkov 	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel);
1534f71d0a05SDoug Thompson 
153533ca0643SBorislav Petkov 	if (cs_found >= 0)
1536f71d0a05SDoug Thompson 		*chan_sel = channel;
153733ca0643SBorislav Petkov 
1538f71d0a05SDoug Thompson 	return cs_found;
1539f71d0a05SDoug Thompson }
1540f71d0a05SDoug Thompson 
1541b15f0fcaSBorislav Petkov static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt, u64 sys_addr,
154233ca0643SBorislav Petkov 				       int *chan_sel)
1543f71d0a05SDoug Thompson {
1544e761359aSBorislav Petkov 	int cs_found = -EINVAL;
1545e761359aSBorislav Petkov 	unsigned range;
1546f71d0a05SDoug Thompson 
15477f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
1548f71d0a05SDoug Thompson 
15497f19bf75SBorislav Petkov 		if (!dram_rw(pvt, range))
1550f71d0a05SDoug Thompson 			continue;
1551f71d0a05SDoug Thompson 
15527f19bf75SBorislav Petkov 		if ((get_dram_base(pvt, range)  <= sys_addr) &&
15537f19bf75SBorislav Petkov 		    (get_dram_limit(pvt, range) >= sys_addr)) {
1554f71d0a05SDoug Thompson 
1555b15f0fcaSBorislav Petkov 			cs_found = f1x_match_to_this_node(pvt, range,
155633ca0643SBorislav Petkov 							  sys_addr, chan_sel);
1557f71d0a05SDoug Thompson 			if (cs_found >= 0)
1558f71d0a05SDoug Thompson 				break;
1559f71d0a05SDoug Thompson 		}
1560f71d0a05SDoug Thompson 	}
1561f71d0a05SDoug Thompson 	return cs_found;
1562f71d0a05SDoug Thompson }
1563f71d0a05SDoug Thompson 
1564f71d0a05SDoug Thompson /*
1565bdc30a0cSBorislav Petkov  * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
1566bdc30a0cSBorislav Petkov  * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
1567f71d0a05SDoug Thompson  *
1568bdc30a0cSBorislav Petkov  * The @sys_addr is usually an error address received from the hardware
1569bdc30a0cSBorislav Petkov  * (MCX_ADDR).
1570f71d0a05SDoug Thompson  */
1571b15f0fcaSBorislav Petkov static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
157233ca0643SBorislav Petkov 				     struct err_info *err)
1573f71d0a05SDoug Thompson {
1574f71d0a05SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
1575f71d0a05SDoug Thompson 
157633ca0643SBorislav Petkov 	error_address_to_page_and_offset(sys_addr, err);
1577ab5a503cSMauro Carvalho Chehab 
157833ca0643SBorislav Petkov 	err->csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &err->channel);
157933ca0643SBorislav Petkov 	if (err->csrow < 0) {
158033ca0643SBorislav Petkov 		err->err_code = ERR_CSROW;
1581bdc30a0cSBorislav Petkov 		return;
1582bdc30a0cSBorislav Petkov 	}
1583bdc30a0cSBorislav Petkov 
1584f71d0a05SDoug Thompson 	/*
1585bdc30a0cSBorislav Petkov 	 * We need the syndromes for channel detection only when we're
1586bdc30a0cSBorislav Petkov 	 * ganged. Otherwise @chan should already contain the channel at
1587bdc30a0cSBorislav Petkov 	 * this point.
1588f71d0a05SDoug Thompson 	 */
1589a97fa68eSBorislav Petkov 	if (dct_ganging_enabled(pvt))
159033ca0643SBorislav Petkov 		err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
1591f71d0a05SDoug Thompson }
1592f71d0a05SDoug Thompson 
1593f71d0a05SDoug Thompson /*
15948566c4dfSBorislav Petkov  * debug routine to display the memory sizes of all logical DIMMs and its
1595cb328507SBorislav Petkov  * CSROWs
1596f71d0a05SDoug Thompson  */
15978c671751SBorislav Petkov static void amd64_debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
1598f71d0a05SDoug Thompson {
1599bb89f5a0SBorislav Petkov 	int dimm, size0, size1;
1600525a1b20SBorislav Petkov 	u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases;
1601525a1b20SBorislav Petkov 	u32 dbam  = ctrl ? pvt->dbam1 : pvt->dbam0;
1602f71d0a05SDoug Thompson 
16038566c4dfSBorislav Petkov 	if (boot_cpu_data.x86 == 0xf) {
16048566c4dfSBorislav Petkov 		/* K8 families < revF not supported yet */
16051433eb99SBorislav Petkov 	       if (pvt->ext_model < K8_REV_F)
16068566c4dfSBorislav Petkov 			return;
16078566c4dfSBorislav Petkov 	       else
16088566c4dfSBorislav Petkov 		       WARN_ON(ctrl != 0);
16098566c4dfSBorislav Petkov 	}
16108566c4dfSBorislav Petkov 
16114d796364SBorislav Petkov 	dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1 : pvt->dbam0;
161211c75eadSBorislav Petkov 	dcsb = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->csels[1].csbases
161311c75eadSBorislav Petkov 						   : pvt->csels[0].csbases;
1614f71d0a05SDoug Thompson 
1615956b9ba1SJoe Perches 	edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
1616956b9ba1SJoe Perches 		 ctrl, dbam);
1617f71d0a05SDoug Thompson 
16188566c4dfSBorislav Petkov 	edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
16198566c4dfSBorislav Petkov 
1620f71d0a05SDoug Thompson 	/* Dump memory sizes for DIMM and its CSROWs */
1621f71d0a05SDoug Thompson 	for (dimm = 0; dimm < 4; dimm++) {
1622f71d0a05SDoug Thompson 
1623f71d0a05SDoug Thompson 		size0 = 0;
162411c75eadSBorislav Petkov 		if (dcsb[dimm*2] & DCSB_CS_ENABLE)
162541d8bfabSBorislav Petkov 			size0 = pvt->ops->dbam_to_cs(pvt, ctrl,
162641d8bfabSBorislav Petkov 						     DBAM_DIMM(dimm, dbam));
1627f71d0a05SDoug Thompson 
1628f71d0a05SDoug Thompson 		size1 = 0;
162911c75eadSBorislav Petkov 		if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE)
163041d8bfabSBorislav Petkov 			size1 = pvt->ops->dbam_to_cs(pvt, ctrl,
163141d8bfabSBorislav Petkov 						     DBAM_DIMM(dimm, dbam));
1632f71d0a05SDoug Thompson 
163324f9a7feSBorislav Petkov 		amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
1634bb89f5a0SBorislav Petkov 				dimm * 2,     size0,
1635bb89f5a0SBorislav Petkov 				dimm * 2 + 1, size1);
1636f71d0a05SDoug Thompson 	}
1637f71d0a05SDoug Thompson }
1638f71d0a05SDoug Thompson 
16394d37607aSDoug Thompson static struct amd64_family_type amd64_family_types[] = {
16404d37607aSDoug Thompson 	[K8_CPUS] = {
16410092b20dSBorislav Petkov 		.ctl_name = "K8",
16428d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
16438d5b5d9cSBorislav Petkov 		.f3_id = PCI_DEVICE_ID_AMD_K8_NB_MISC,
16444d37607aSDoug Thompson 		.ops = {
16454d37607aSDoug Thompson 			.early_channel_count	= k8_early_channel_count,
16464d37607aSDoug Thompson 			.map_sysaddr_to_csrow	= k8_map_sysaddr_to_csrow,
16471433eb99SBorislav Petkov 			.dbam_to_cs		= k8_dbam_to_chip_select,
1648b2b0c605SBorislav Petkov 			.read_dct_pci_cfg	= k8_read_dct_pci_cfg,
16494d37607aSDoug Thompson 		}
16504d37607aSDoug Thompson 	},
16514d37607aSDoug Thompson 	[F10_CPUS] = {
16520092b20dSBorislav Petkov 		.ctl_name = "F10h",
16538d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP,
16548d5b5d9cSBorislav Petkov 		.f3_id = PCI_DEVICE_ID_AMD_10H_NB_MISC,
16554d37607aSDoug Thompson 		.ops = {
16567d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
1657b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
16581433eb99SBorislav Petkov 			.dbam_to_cs		= f10_dbam_to_chip_select,
1659b2b0c605SBorislav Petkov 			.read_dct_pci_cfg	= f10_read_dct_pci_cfg,
1660b2b0c605SBorislav Petkov 		}
1661b2b0c605SBorislav Petkov 	},
1662b2b0c605SBorislav Petkov 	[F15_CPUS] = {
1663b2b0c605SBorislav Petkov 		.ctl_name = "F15h",
1664df71a053SBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1,
1665df71a053SBorislav Petkov 		.f3_id = PCI_DEVICE_ID_AMD_15H_NB_F3,
1666b2b0c605SBorislav Petkov 		.ops = {
16677d20d14dSBorislav Petkov 			.early_channel_count	= f1x_early_channel_count,
1668b15f0fcaSBorislav Petkov 			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
166941d8bfabSBorislav Petkov 			.dbam_to_cs		= f15_dbam_to_chip_select,
1670b2b0c605SBorislav Petkov 			.read_dct_pci_cfg	= f15_read_dct_pci_cfg,
16714d37607aSDoug Thompson 		}
16724d37607aSDoug Thompson 	},
16734d37607aSDoug Thompson };
16744d37607aSDoug Thompson 
16754d37607aSDoug Thompson static struct pci_dev *pci_get_related_function(unsigned int vendor,
16764d37607aSDoug Thompson 						unsigned int device,
16774d37607aSDoug Thompson 						struct pci_dev *related)
16784d37607aSDoug Thompson {
16794d37607aSDoug Thompson 	struct pci_dev *dev = NULL;
16804d37607aSDoug Thompson 
16814d37607aSDoug Thompson 	dev = pci_get_device(vendor, device, dev);
16824d37607aSDoug Thompson 	while (dev) {
16834d37607aSDoug Thompson 		if ((dev->bus->number == related->bus->number) &&
16844d37607aSDoug Thompson 		    (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn)))
16854d37607aSDoug Thompson 			break;
16864d37607aSDoug Thompson 		dev = pci_get_device(vendor, device, dev);
16874d37607aSDoug Thompson 	}
16884d37607aSDoug Thompson 
16894d37607aSDoug Thompson 	return dev;
16904d37607aSDoug Thompson }
16914d37607aSDoug Thompson 
1692b1289d6fSDoug Thompson /*
1693bfc04aecSBorislav Petkov  * These are tables of eigenvectors (one per line) which can be used for the
1694bfc04aecSBorislav Petkov  * construction of the syndrome tables. The modified syndrome search algorithm
1695bfc04aecSBorislav Petkov  * uses those to find the symbol in error and thus the DIMM.
1696b1289d6fSDoug Thompson  *
1697bfc04aecSBorislav Petkov  * Algorithm courtesy of Ross LaFetra from AMD.
1698b1289d6fSDoug Thompson  */
1699bfc04aecSBorislav Petkov static u16 x4_vectors[] = {
1700bfc04aecSBorislav Petkov 	0x2f57, 0x1afe, 0x66cc, 0xdd88,
1701bfc04aecSBorislav Petkov 	0x11eb, 0x3396, 0x7f4c, 0xeac8,
1702bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008,
1703bfc04aecSBorislav Petkov 	0x1013, 0x3032, 0x4044, 0x8088,
1704bfc04aecSBorislav Petkov 	0x106b, 0x30d6, 0x70fc, 0xe0a8,
1705bfc04aecSBorislav Petkov 	0x4857, 0xc4fe, 0x13cc, 0x3288,
1706bfc04aecSBorislav Petkov 	0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
1707bfc04aecSBorislav Petkov 	0x1f39, 0x251e, 0xbd6c, 0x6bd8,
1708bfc04aecSBorislav Petkov 	0x15c1, 0x2a42, 0x89ac, 0x4758,
1709bfc04aecSBorislav Petkov 	0x2b03, 0x1602, 0x4f0c, 0xca08,
1710bfc04aecSBorislav Petkov 	0x1f07, 0x3a0e, 0x6b04, 0xbd08,
1711bfc04aecSBorislav Petkov 	0x8ba7, 0x465e, 0x244c, 0x1cc8,
1712bfc04aecSBorislav Petkov 	0x2b87, 0x164e, 0x642c, 0xdc18,
1713bfc04aecSBorislav Petkov 	0x40b9, 0x80de, 0x1094, 0x20e8,
1714bfc04aecSBorislav Petkov 	0x27db, 0x1eb6, 0x9dac, 0x7b58,
1715bfc04aecSBorislav Petkov 	0x11c1, 0x2242, 0x84ac, 0x4c58,
1716bfc04aecSBorislav Petkov 	0x1be5, 0x2d7a, 0x5e34, 0xa718,
1717bfc04aecSBorislav Petkov 	0x4b39, 0x8d1e, 0x14b4, 0x28d8,
1718bfc04aecSBorislav Petkov 	0x4c97, 0xc87e, 0x11fc, 0x33a8,
1719bfc04aecSBorislav Petkov 	0x8e97, 0x497e, 0x2ffc, 0x1aa8,
1720bfc04aecSBorislav Petkov 	0x16b3, 0x3d62, 0x4f34, 0x8518,
1721bfc04aecSBorislav Petkov 	0x1e2f, 0x391a, 0x5cac, 0xf858,
1722bfc04aecSBorislav Petkov 	0x1d9f, 0x3b7a, 0x572c, 0xfe18,
1723bfc04aecSBorislav Petkov 	0x15f5, 0x2a5a, 0x5264, 0xa3b8,
1724bfc04aecSBorislav Petkov 	0x1dbb, 0x3b66, 0x715c, 0xe3f8,
1725bfc04aecSBorislav Petkov 	0x4397, 0xc27e, 0x17fc, 0x3ea8,
1726bfc04aecSBorislav Petkov 	0x1617, 0x3d3e, 0x6464, 0xb8b8,
1727bfc04aecSBorislav Petkov 	0x23ff, 0x12aa, 0xab6c, 0x56d8,
1728bfc04aecSBorislav Petkov 	0x2dfb, 0x1ba6, 0x913c, 0x7328,
1729bfc04aecSBorislav Petkov 	0x185d, 0x2ca6, 0x7914, 0x9e28,
1730bfc04aecSBorislav Petkov 	0x171b, 0x3e36, 0x7d7c, 0xebe8,
1731bfc04aecSBorislav Petkov 	0x4199, 0x82ee, 0x19f4, 0x2e58,
1732bfc04aecSBorislav Petkov 	0x4807, 0xc40e, 0x130c, 0x3208,
1733bfc04aecSBorislav Petkov 	0x1905, 0x2e0a, 0x5804, 0xac08,
1734bfc04aecSBorislav Petkov 	0x213f, 0x132a, 0xadfc, 0x5ba8,
1735bfc04aecSBorislav Petkov 	0x19a9, 0x2efe, 0xb5cc, 0x6f88,
1736b1289d6fSDoug Thompson };
1737b1289d6fSDoug Thompson 
1738bfc04aecSBorislav Petkov static u16 x8_vectors[] = {
1739bfc04aecSBorislav Petkov 	0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
1740bfc04aecSBorislav Petkov 	0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
1741bfc04aecSBorislav Petkov 	0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
1742bfc04aecSBorislav Petkov 	0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
1743bfc04aecSBorislav Petkov 	0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
1744bfc04aecSBorislav Petkov 	0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
1745bfc04aecSBorislav Petkov 	0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
1746bfc04aecSBorislav Petkov 	0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
1747bfc04aecSBorislav Petkov 	0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
1748bfc04aecSBorislav Petkov 	0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
1749bfc04aecSBorislav Petkov 	0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
1750bfc04aecSBorislav Petkov 	0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
1751bfc04aecSBorislav Petkov 	0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
1752bfc04aecSBorislav Petkov 	0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
1753bfc04aecSBorislav Petkov 	0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
1754bfc04aecSBorislav Petkov 	0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
1755bfc04aecSBorislav Petkov 	0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
1756bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
1757bfc04aecSBorislav Petkov 	0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
1758bfc04aecSBorislav Petkov };
1759bfc04aecSBorislav Petkov 
1760d34a6ecdSBorislav Petkov static int decode_syndrome(u16 syndrome, u16 *vectors, unsigned num_vecs,
1761d34a6ecdSBorislav Petkov 			   unsigned v_dim)
1762b1289d6fSDoug Thompson {
1763bfc04aecSBorislav Petkov 	unsigned int i, err_sym;
1764b1289d6fSDoug Thompson 
1765bfc04aecSBorislav Petkov 	for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
1766bfc04aecSBorislav Petkov 		u16 s = syndrome;
1767d34a6ecdSBorislav Petkov 		unsigned v_idx =  err_sym * v_dim;
1768d34a6ecdSBorislav Petkov 		unsigned v_end = (err_sym + 1) * v_dim;
1769b1289d6fSDoug Thompson 
1770bfc04aecSBorislav Petkov 		/* walk over all 16 bits of the syndrome */
1771bfc04aecSBorislav Petkov 		for (i = 1; i < (1U << 16); i <<= 1) {
1772bfc04aecSBorislav Petkov 
1773bfc04aecSBorislav Petkov 			/* if bit is set in that eigenvector... */
1774bfc04aecSBorislav Petkov 			if (v_idx < v_end && vectors[v_idx] & i) {
1775bfc04aecSBorislav Petkov 				u16 ev_comp = vectors[v_idx++];
1776bfc04aecSBorislav Petkov 
1777bfc04aecSBorislav Petkov 				/* ... and bit set in the modified syndrome, */
1778bfc04aecSBorislav Petkov 				if (s & i) {
1779bfc04aecSBorislav Petkov 					/* remove it. */
1780bfc04aecSBorislav Petkov 					s ^= ev_comp;
1781bfc04aecSBorislav Petkov 
1782bfc04aecSBorislav Petkov 					if (!s)
1783bfc04aecSBorislav Petkov 						return err_sym;
1784bfc04aecSBorislav Petkov 				}
1785bfc04aecSBorislav Petkov 
1786bfc04aecSBorislav Petkov 			} else if (s & i)
1787bfc04aecSBorislav Petkov 				/* can't get to zero, move to next symbol */
1788bfc04aecSBorislav Petkov 				break;
1789bfc04aecSBorislav Petkov 		}
1790b1289d6fSDoug Thompson 	}
1791b1289d6fSDoug Thompson 
1792956b9ba1SJoe Perches 	edac_dbg(0, "syndrome(%x) not found\n", syndrome);
1793b1289d6fSDoug Thompson 	return -1;
1794b1289d6fSDoug Thompson }
1795d27bf6faSDoug Thompson 
1796bfc04aecSBorislav Petkov static int map_err_sym_to_channel(int err_sym, int sym_size)
1797bfc04aecSBorislav Petkov {
1798bfc04aecSBorislav Petkov 	if (sym_size == 4)
1799bfc04aecSBorislav Petkov 		switch (err_sym) {
1800bfc04aecSBorislav Petkov 		case 0x20:
1801bfc04aecSBorislav Petkov 		case 0x21:
1802bfc04aecSBorislav Petkov 			return 0;
1803bfc04aecSBorislav Petkov 			break;
1804bfc04aecSBorislav Petkov 		case 0x22:
1805bfc04aecSBorislav Petkov 		case 0x23:
1806bfc04aecSBorislav Petkov 			return 1;
1807bfc04aecSBorislav Petkov 			break;
1808bfc04aecSBorislav Petkov 		default:
1809bfc04aecSBorislav Petkov 			return err_sym >> 4;
1810bfc04aecSBorislav Petkov 			break;
1811bfc04aecSBorislav Petkov 		}
1812bfc04aecSBorislav Petkov 	/* x8 symbols */
1813bfc04aecSBorislav Petkov 	else
1814bfc04aecSBorislav Petkov 		switch (err_sym) {
1815bfc04aecSBorislav Petkov 		/* imaginary bits not in a DIMM */
1816bfc04aecSBorislav Petkov 		case 0x10:
1817bfc04aecSBorislav Petkov 			WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
1818bfc04aecSBorislav Petkov 					  err_sym);
1819bfc04aecSBorislav Petkov 			return -1;
1820bfc04aecSBorislav Petkov 			break;
1821bfc04aecSBorislav Petkov 
1822bfc04aecSBorislav Petkov 		case 0x11:
1823bfc04aecSBorislav Petkov 			return 0;
1824bfc04aecSBorislav Petkov 			break;
1825bfc04aecSBorislav Petkov 		case 0x12:
1826bfc04aecSBorislav Petkov 			return 1;
1827bfc04aecSBorislav Petkov 			break;
1828bfc04aecSBorislav Petkov 		default:
1829bfc04aecSBorislav Petkov 			return err_sym >> 3;
1830bfc04aecSBorislav Petkov 			break;
1831bfc04aecSBorislav Petkov 		}
1832bfc04aecSBorislav Petkov 	return -1;
1833bfc04aecSBorislav Petkov }
1834bfc04aecSBorislav Petkov 
1835bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
1836bfc04aecSBorislav Petkov {
1837bfc04aecSBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
1838ad6a32e9SBorislav Petkov 	int err_sym = -1;
1839bfc04aecSBorislav Petkov 
1840a3b7db09SBorislav Petkov 	if (pvt->ecc_sym_sz == 8)
1841bfc04aecSBorislav Petkov 		err_sym = decode_syndrome(syndrome, x8_vectors,
1842ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x8_vectors),
1843a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
1844a3b7db09SBorislav Petkov 	else if (pvt->ecc_sym_sz == 4)
1845ad6a32e9SBorislav Petkov 		err_sym = decode_syndrome(syndrome, x4_vectors,
1846ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x4_vectors),
1847a3b7db09SBorislav Petkov 					  pvt->ecc_sym_sz);
1848ad6a32e9SBorislav Petkov 	else {
1849a3b7db09SBorislav Petkov 		amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz);
1850ad6a32e9SBorislav Petkov 		return err_sym;
1851bfc04aecSBorislav Petkov 	}
1852ad6a32e9SBorislav Petkov 
1853a3b7db09SBorislav Petkov 	return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz);
185441c31044SBorislav Petkov }
1855bfc04aecSBorislav Petkov 
185633ca0643SBorislav Petkov static void __log_bus_error(struct mem_ctl_info *mci, struct err_info *err,
185733ca0643SBorislav Petkov 			    u8 ecc_type)
1858d27bf6faSDoug Thompson {
185933ca0643SBorislav Petkov 	enum hw_event_mc_err_type err_type;
186033ca0643SBorislav Petkov 	const char *string;
1861d27bf6faSDoug Thompson 
186233ca0643SBorislav Petkov 	if (ecc_type == 2)
186333ca0643SBorislav Petkov 		err_type = HW_EVENT_ERR_CORRECTED;
186433ca0643SBorislav Petkov 	else if (ecc_type == 1)
186533ca0643SBorislav Petkov 		err_type = HW_EVENT_ERR_UNCORRECTED;
186633ca0643SBorislav Petkov 	else {
186733ca0643SBorislav Petkov 		WARN(1, "Something is rotten in the state of Denmark.\n");
1868d27bf6faSDoug Thompson 		return;
1869d27bf6faSDoug Thompson 	}
1870d27bf6faSDoug Thompson 
187133ca0643SBorislav Petkov 	switch (err->err_code) {
187233ca0643SBorislav Petkov 	case DECODE_OK:
187333ca0643SBorislav Petkov 		string = "";
187433ca0643SBorislav Petkov 		break;
187533ca0643SBorislav Petkov 	case ERR_NODE:
187633ca0643SBorislav Petkov 		string = "Failed to map error addr to a node";
187733ca0643SBorislav Petkov 		break;
187833ca0643SBorislav Petkov 	case ERR_CSROW:
187933ca0643SBorislav Petkov 		string = "Failed to map error addr to a csrow";
188033ca0643SBorislav Petkov 		break;
188133ca0643SBorislav Petkov 	case ERR_CHANNEL:
188233ca0643SBorislav Petkov 		string = "unknown syndrome - possible error reporting race";
188333ca0643SBorislav Petkov 		break;
188433ca0643SBorislav Petkov 	default:
188533ca0643SBorislav Petkov 		string = "WTF error";
188633ca0643SBorislav Petkov 		break;
1887d27bf6faSDoug Thompson 	}
188833ca0643SBorislav Petkov 
188933ca0643SBorislav Petkov 	edac_mc_handle_error(err_type, mci, 1,
189033ca0643SBorislav Petkov 			     err->page, err->offset, err->syndrome,
189133ca0643SBorislav Petkov 			     err->csrow, err->channel, -1,
189233ca0643SBorislav Petkov 			     string, "");
1893d27bf6faSDoug Thompson }
1894d27bf6faSDoug Thompson 
1895549d042dSBorislav Petkov static inline void __amd64_decode_bus_error(struct mem_ctl_info *mci,
1896f192c7b1SBorislav Petkov 					    struct mce *m)
1897d27bf6faSDoug Thompson {
189833ca0643SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
1899f192c7b1SBorislav Petkov 	u8 ecc_type = (m->status >> 45) & 0x3;
190066fed2d4SBorislav Petkov 	u8 xec = XEC(m->status, 0x1f);
190166fed2d4SBorislav Petkov 	u16 ec = EC(m->status);
190233ca0643SBorislav Petkov 	u64 sys_addr;
190333ca0643SBorislav Petkov 	struct err_info err;
1904d27bf6faSDoug Thompson 
190566fed2d4SBorislav Petkov 	/* Bail out early if this was an 'observed' error */
19065980bb9cSBorislav Petkov 	if (PP(ec) == NBSL_PP_OBS)
1907b70ef010SBorislav Petkov 		return;
1908d27bf6faSDoug Thompson 
1909ecaf5606SBorislav Petkov 	/* Do only ECC errors */
1910ecaf5606SBorislav Petkov 	if (xec && xec != F10_NBSL_EXT_ERR_ECC)
1911d27bf6faSDoug Thompson 		return;
1912d27bf6faSDoug Thompson 
191333ca0643SBorislav Petkov 	memset(&err, 0, sizeof(err));
191433ca0643SBorislav Petkov 
191533ca0643SBorislav Petkov 	sys_addr = get_error_address(m);
191633ca0643SBorislav Petkov 
1917ecaf5606SBorislav Petkov 	if (ecc_type == 2)
191833ca0643SBorislav Petkov 		err.syndrome = extract_syndrome(m->status);
191933ca0643SBorislav Petkov 
192033ca0643SBorislav Petkov 	pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err);
192133ca0643SBorislav Petkov 
192233ca0643SBorislav Petkov 	__log_bus_error(mci, &err, ecc_type);
1923d27bf6faSDoug Thompson }
1924d27bf6faSDoug Thompson 
1925b0b07a2bSBorislav Petkov void amd64_decode_bus_error(int node_id, struct mce *m)
1926d27bf6faSDoug Thompson {
1927b0b07a2bSBorislav Petkov 	__amd64_decode_bus_error(mcis[node_id], m);
1928d27bf6faSDoug Thompson }
1929d27bf6faSDoug Thompson 
19300ec449eeSDoug Thompson /*
19318d5b5d9cSBorislav Petkov  * Use pvt->F2 which contains the F2 CPU PCI device to get the related
1932bbd0c1f6SBorislav Petkov  * F1 (AddrMap) and F3 (Misc) devices. Return negative value on error.
19330ec449eeSDoug Thompson  */
1934360b7f3cSBorislav Petkov static int reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 f1_id, u16 f3_id)
19350ec449eeSDoug Thompson {
19360ec449eeSDoug Thompson 	/* Reserve the ADDRESS MAP Device */
19378d5b5d9cSBorislav Petkov 	pvt->F1 = pci_get_related_function(pvt->F2->vendor, f1_id, pvt->F2);
19388d5b5d9cSBorislav Petkov 	if (!pvt->F1) {
193924f9a7feSBorislav Petkov 		amd64_err("error address map device not found: "
19400ec449eeSDoug Thompson 			  "vendor %x device 0x%x (broken BIOS?)\n",
1941bbd0c1f6SBorislav Petkov 			  PCI_VENDOR_ID_AMD, f1_id);
1942bbd0c1f6SBorislav Petkov 		return -ENODEV;
19430ec449eeSDoug Thompson 	}
19440ec449eeSDoug Thompson 
19450ec449eeSDoug Thompson 	/* Reserve the MISC Device */
19468d5b5d9cSBorislav Petkov 	pvt->F3 = pci_get_related_function(pvt->F2->vendor, f3_id, pvt->F2);
19478d5b5d9cSBorislav Petkov 	if (!pvt->F3) {
19488d5b5d9cSBorislav Petkov 		pci_dev_put(pvt->F1);
19498d5b5d9cSBorislav Petkov 		pvt->F1 = NULL;
19500ec449eeSDoug Thompson 
195124f9a7feSBorislav Petkov 		amd64_err("error F3 device not found: "
19520ec449eeSDoug Thompson 			  "vendor %x device 0x%x (broken BIOS?)\n",
1953bbd0c1f6SBorislav Petkov 			  PCI_VENDOR_ID_AMD, f3_id);
19548d5b5d9cSBorislav Petkov 
1955bbd0c1f6SBorislav Petkov 		return -ENODEV;
19560ec449eeSDoug Thompson 	}
1957956b9ba1SJoe Perches 	edac_dbg(1, "F1: %s\n", pci_name(pvt->F1));
1958956b9ba1SJoe Perches 	edac_dbg(1, "F2: %s\n", pci_name(pvt->F2));
1959956b9ba1SJoe Perches 	edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
19600ec449eeSDoug Thompson 
19610ec449eeSDoug Thompson 	return 0;
19620ec449eeSDoug Thompson }
19630ec449eeSDoug Thompson 
1964360b7f3cSBorislav Petkov static void free_mc_sibling_devs(struct amd64_pvt *pvt)
19650ec449eeSDoug Thompson {
19668d5b5d9cSBorislav Petkov 	pci_dev_put(pvt->F1);
19678d5b5d9cSBorislav Petkov 	pci_dev_put(pvt->F3);
19680ec449eeSDoug Thompson }
19690ec449eeSDoug Thompson 
19700ec449eeSDoug Thompson /*
19710ec449eeSDoug Thompson  * Retrieve the hardware registers of the memory controller (this includes the
19720ec449eeSDoug Thompson  * 'Address Map' and 'Misc' device regs)
19730ec449eeSDoug Thompson  */
1974360b7f3cSBorislav Petkov static void read_mc_regs(struct amd64_pvt *pvt)
19750ec449eeSDoug Thompson {
1976a3b7db09SBorislav Petkov 	struct cpuinfo_x86 *c = &boot_cpu_data;
19770ec449eeSDoug Thompson 	u64 msr_val;
1978ad6a32e9SBorislav Petkov 	u32 tmp;
1979e761359aSBorislav Petkov 	unsigned range;
19800ec449eeSDoug Thompson 
19810ec449eeSDoug Thompson 	/*
19820ec449eeSDoug Thompson 	 * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
19830ec449eeSDoug Thompson 	 * those are Read-As-Zero
19840ec449eeSDoug Thompson 	 */
1985e97f8bb8SBorislav Petkov 	rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
1986956b9ba1SJoe Perches 	edac_dbg(0, "  TOP_MEM:  0x%016llx\n", pvt->top_mem);
19870ec449eeSDoug Thompson 
19880ec449eeSDoug Thompson 	/* check first whether TOP_MEM2 is enabled */
19890ec449eeSDoug Thompson 	rdmsrl(MSR_K8_SYSCFG, msr_val);
19900ec449eeSDoug Thompson 	if (msr_val & (1U << 21)) {
1991e97f8bb8SBorislav Petkov 		rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
1992956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
19930ec449eeSDoug Thompson 	} else
1994956b9ba1SJoe Perches 		edac_dbg(0, "  TOP_MEM2 disabled\n");
19950ec449eeSDoug Thompson 
19965980bb9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap);
19970ec449eeSDoug Thompson 
19985a5d2371SBorislav Petkov 	read_dram_ctl_register(pvt);
19990ec449eeSDoug Thompson 
20007f19bf75SBorislav Petkov 	for (range = 0; range < DRAM_RANGES; range++) {
20017f19bf75SBorislav Petkov 		u8 rw;
20020ec449eeSDoug Thompson 
20037f19bf75SBorislav Petkov 		/* read settings for this DRAM range */
20047f19bf75SBorislav Petkov 		read_dram_base_limit_regs(pvt, range);
2005e97f8bb8SBorislav Petkov 
20067f19bf75SBorislav Petkov 		rw = dram_rw(pvt, range);
20077f19bf75SBorislav Petkov 		if (!rw)
20087f19bf75SBorislav Petkov 			continue;
20097f19bf75SBorislav Petkov 
2010956b9ba1SJoe Perches 		edac_dbg(1, "  DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
20117f19bf75SBorislav Petkov 			 range,
20127f19bf75SBorislav Petkov 			 get_dram_base(pvt, range),
20137f19bf75SBorislav Petkov 			 get_dram_limit(pvt, range));
20147f19bf75SBorislav Petkov 
2015956b9ba1SJoe Perches 		edac_dbg(1, "   IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
20167f19bf75SBorislav Petkov 			 dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
20177f19bf75SBorislav Petkov 			 (rw & 0x1) ? "R" : "-",
20187f19bf75SBorislav Petkov 			 (rw & 0x2) ? "W" : "-",
20197f19bf75SBorislav Petkov 			 dram_intlv_sel(pvt, range),
20207f19bf75SBorislav Petkov 			 dram_dst_node(pvt, range));
20210ec449eeSDoug Thompson 	}
20220ec449eeSDoug Thompson 
2023b2b0c605SBorislav Petkov 	read_dct_base_mask(pvt);
20240ec449eeSDoug Thompson 
2025bc21fa57SBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar);
2026525a1b20SBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, DBAM0, &pvt->dbam0);
20270ec449eeSDoug Thompson 
20288d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare);
20290ec449eeSDoug Thompson 
2030cb328507SBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, DCLR0, &pvt->dclr0);
2031cb328507SBorislav Petkov 	amd64_read_dct_pci_cfg(pvt, DCHR0, &pvt->dchr0);
20320ec449eeSDoug Thompson 
203378da121eSBorislav Petkov 	if (!dct_ganging_enabled(pvt)) {
2034cb328507SBorislav Petkov 		amd64_read_dct_pci_cfg(pvt, DCLR1, &pvt->dclr1);
2035cb328507SBorislav Petkov 		amd64_read_dct_pci_cfg(pvt, DCHR1, &pvt->dchr1);
20360ec449eeSDoug Thompson 	}
2037b2b0c605SBorislav Petkov 
2038a3b7db09SBorislav Petkov 	pvt->ecc_sym_sz = 4;
2039a3b7db09SBorislav Petkov 
2040a3b7db09SBorislav Petkov 	if (c->x86 >= 0x10) {
20418d5b5d9cSBorislav Petkov 		amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp);
2042525a1b20SBorislav Petkov 		amd64_read_dct_pci_cfg(pvt, DBAM1, &pvt->dbam1);
2043a3b7db09SBorislav Petkov 
2044a3b7db09SBorislav Petkov 		/* F10h, revD and later can do x8 ECC too */
2045a3b7db09SBorislav Petkov 		if ((c->x86 > 0x10 || c->x86_model > 7) && tmp & BIT(25))
2046a3b7db09SBorislav Petkov 			pvt->ecc_sym_sz = 8;
2047525a1b20SBorislav Petkov 	}
2048b2b0c605SBorislav Petkov 	dump_misc_regs(pvt);
20490ec449eeSDoug Thompson }
20500ec449eeSDoug Thompson 
20510ec449eeSDoug Thompson /*
20520ec449eeSDoug Thompson  * NOTE: CPU Revision Dependent code
20530ec449eeSDoug Thompson  *
20540ec449eeSDoug Thompson  * Input:
205511c75eadSBorislav Petkov  *	@csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1)
20560ec449eeSDoug Thompson  *	k8 private pointer to -->
20570ec449eeSDoug Thompson  *			DRAM Bank Address mapping register
20580ec449eeSDoug Thompson  *			node_id
20590ec449eeSDoug Thompson  *			DCL register where dual_channel_active is
20600ec449eeSDoug Thompson  *
20610ec449eeSDoug Thompson  * The DBAM register consists of 4 sets of 4 bits each definitions:
20620ec449eeSDoug Thompson  *
20630ec449eeSDoug Thompson  * Bits:	CSROWs
20640ec449eeSDoug Thompson  * 0-3		CSROWs 0 and 1
20650ec449eeSDoug Thompson  * 4-7		CSROWs 2 and 3
20660ec449eeSDoug Thompson  * 8-11		CSROWs 4 and 5
20670ec449eeSDoug Thompson  * 12-15	CSROWs 6 and 7
20680ec449eeSDoug Thompson  *
20690ec449eeSDoug Thompson  * Values range from: 0 to 15
20700ec449eeSDoug Thompson  * The meaning of the values depends on CPU revision and dual-channel state,
20710ec449eeSDoug Thompson  * see relevant BKDG more info.
20720ec449eeSDoug Thompson  *
20730ec449eeSDoug Thompson  * The memory controller provides for total of only 8 CSROWs in its current
20740ec449eeSDoug Thompson  * architecture. Each "pair" of CSROWs normally represents just one DIMM in
20750ec449eeSDoug Thompson  * single channel or two (2) DIMMs in dual channel mode.
20760ec449eeSDoug Thompson  *
20770ec449eeSDoug Thompson  * The following code logic collapses the various tables for CSROW based on CPU
20780ec449eeSDoug Thompson  * revision.
20790ec449eeSDoug Thompson  *
20800ec449eeSDoug Thompson  * Returns:
20810ec449eeSDoug Thompson  *	The number of PAGE_SIZE pages on the specified CSROW number it
20820ec449eeSDoug Thompson  *	encompasses
20830ec449eeSDoug Thompson  *
20840ec449eeSDoug Thompson  */
208541d8bfabSBorislav Petkov static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr)
20860ec449eeSDoug Thompson {
20871433eb99SBorislav Petkov 	u32 cs_mode, nr_pages;
2088f92cae45SAshish Shenoy 	u32 dbam = dct ? pvt->dbam1 : pvt->dbam0;
20890ec449eeSDoug Thompson 
209010de6497SBorislav Petkov 
20910ec449eeSDoug Thompson 	/*
20920ec449eeSDoug Thompson 	 * The math on this doesn't look right on the surface because x/2*4 can
20930ec449eeSDoug Thompson 	 * be simplified to x*2 but this expression makes use of the fact that
20940ec449eeSDoug Thompson 	 * it is integral math where 1/2=0. This intermediate value becomes the
20950ec449eeSDoug Thompson 	 * number of bits to shift the DBAM register to extract the proper CSROW
20960ec449eeSDoug Thompson 	 * field.
20970ec449eeSDoug Thompson 	 */
20980a5dfc31SBorislav Petkov 	cs_mode = DBAM_DIMM(csrow_nr / 2, dbam);
20990ec449eeSDoug Thompson 
210041d8bfabSBorislav Petkov 	nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode) << (20 - PAGE_SHIFT);
21010ec449eeSDoug Thompson 
210210de6497SBorislav Petkov 	edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n",
210310de6497SBorislav Petkov 		    csrow_nr, dct,  cs_mode);
210410de6497SBorislav Petkov 	edac_dbg(0, "nr_pages/channel: %u\n", nr_pages);
21050ec449eeSDoug Thompson 
21060ec449eeSDoug Thompson 	return nr_pages;
21070ec449eeSDoug Thompson }
21080ec449eeSDoug Thompson 
21090ec449eeSDoug Thompson /*
21100ec449eeSDoug Thompson  * Initialize the array of csrow attribute instances, based on the values
21110ec449eeSDoug Thompson  * from pci config hardware registers.
21120ec449eeSDoug Thompson  */
2113360b7f3cSBorislav Petkov static int init_csrows(struct mem_ctl_info *mci)
21140ec449eeSDoug Thompson {
211510de6497SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
21160ec449eeSDoug Thompson 	struct csrow_info *csrow;
2117de3910ebSMauro Carvalho Chehab 	struct dimm_info *dimm;
2118084a4fccSMauro Carvalho Chehab 	enum edac_type edac_mode;
211910de6497SBorislav Petkov 	enum mem_type mtype;
212010de6497SBorislav Petkov 	int i, j, empty = 1;
2121a895bf8bSMauro Carvalho Chehab 	int nr_pages = 0;
212210de6497SBorislav Petkov 	u32 val;
21230ec449eeSDoug Thompson 
2124a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
21250ec449eeSDoug Thompson 
21262299ef71SBorislav Petkov 	pvt->nbcfg = val;
21270ec449eeSDoug Thompson 
2128956b9ba1SJoe Perches 	edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
21292299ef71SBorislav Petkov 		 pvt->mc_node_id, val,
2130a97fa68eSBorislav Petkov 		 !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
21310ec449eeSDoug Thompson 
213210de6497SBorislav Petkov 	/*
213310de6497SBorislav Petkov 	 * We iterate over DCT0 here but we look at DCT1 in parallel, if needed.
213410de6497SBorislav Petkov 	 */
213511c75eadSBorislav Petkov 	for_each_chip_select(i, 0, pvt) {
213610de6497SBorislav Petkov 		bool row_dct0 = !!csrow_enabled(i, 0, pvt);
213710de6497SBorislav Petkov 		bool row_dct1 = false;
21380ec449eeSDoug Thompson 
213910de6497SBorislav Petkov 		if (boot_cpu_data.x86 != 0xf)
214010de6497SBorislav Petkov 			row_dct1 = !!csrow_enabled(i, 1, pvt);
214110de6497SBorislav Petkov 
214210de6497SBorislav Petkov 		if (!row_dct0 && !row_dct1)
21430ec449eeSDoug Thompson 			continue;
21440ec449eeSDoug Thompson 
214510de6497SBorislav Petkov 		csrow = mci->csrows[i];
21460ec449eeSDoug Thompson 		empty = 0;
214711c75eadSBorislav Petkov 
214810de6497SBorislav Petkov 		edac_dbg(1, "MC node: %d, csrow: %d\n",
214910de6497SBorislav Petkov 			    pvt->mc_node_id, i);
215010de6497SBorislav Petkov 
215110de6497SBorislav Petkov 		if (row_dct0)
215210de6497SBorislav Petkov 			nr_pages = amd64_csrow_nr_pages(pvt, 0, i);
215310de6497SBorislav Petkov 
215410de6497SBorislav Petkov 		/* K8 has only one DCT */
215510de6497SBorislav Petkov 		if (boot_cpu_data.x86 != 0xf && row_dct1)
215610de6497SBorislav Petkov 			nr_pages += amd64_csrow_nr_pages(pvt, 1, i);
21570ec449eeSDoug Thompson 
2158084a4fccSMauro Carvalho Chehab 		mtype = amd64_determine_memory_type(pvt, i);
21590ec449eeSDoug Thompson 
216010de6497SBorislav Petkov 		edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages);
21610ec449eeSDoug Thompson 
21620ec449eeSDoug Thompson 		/*
21630ec449eeSDoug Thompson 		 * determine whether CHIPKILL or JUST ECC or NO ECC is operating
21640ec449eeSDoug Thompson 		 */
2165a97fa68eSBorislav Petkov 		if (pvt->nbcfg & NBCFG_ECC_ENABLE)
2166084a4fccSMauro Carvalho Chehab 			edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) ?
21670ec449eeSDoug Thompson 				    EDAC_S4ECD4ED : EDAC_SECDED;
21680ec449eeSDoug Thompson 		else
2169084a4fccSMauro Carvalho Chehab 			edac_mode = EDAC_NONE;
2170084a4fccSMauro Carvalho Chehab 
2171084a4fccSMauro Carvalho Chehab 		for (j = 0; j < pvt->channel_count; j++) {
2172de3910ebSMauro Carvalho Chehab 			dimm = csrow->channels[j]->dimm;
2173de3910ebSMauro Carvalho Chehab 			dimm->mtype = mtype;
2174de3910ebSMauro Carvalho Chehab 			dimm->edac_mode = edac_mode;
2175de3910ebSMauro Carvalho Chehab 			dimm->nr_pages = nr_pages;
2176084a4fccSMauro Carvalho Chehab 		}
217716a528eeSBorislav Petkov 		csrow->nr_pages = nr_pages;
21780ec449eeSDoug Thompson 	}
21790ec449eeSDoug Thompson 
21800ec449eeSDoug Thompson 	return empty;
21810ec449eeSDoug Thompson }
2182d27bf6faSDoug Thompson 
218306724535SBorislav Petkov /* get all cores on this DCT */
2184b487c33eSBorislav Petkov static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, unsigned nid)
2185f9431992SDoug Thompson {
218606724535SBorislav Petkov 	int cpu;
2187f9431992SDoug Thompson 
218806724535SBorislav Petkov 	for_each_online_cpu(cpu)
218906724535SBorislav Petkov 		if (amd_get_nb_id(cpu) == nid)
219006724535SBorislav Petkov 			cpumask_set_cpu(cpu, mask);
2191f9431992SDoug Thompson }
2192f9431992SDoug Thompson 
2193f9431992SDoug Thompson /* check MCG_CTL on all the cpus on this node */
2194b487c33eSBorislav Petkov static bool amd64_nb_mce_bank_enabled_on_node(unsigned nid)
2195f9431992SDoug Thompson {
2196ba578cb3SRusty Russell 	cpumask_var_t mask;
219750542251SBorislav Petkov 	int cpu, nbe;
219806724535SBorislav Petkov 	bool ret = false;
2199f9431992SDoug Thompson 
2200ba578cb3SRusty Russell 	if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
220124f9a7feSBorislav Petkov 		amd64_warn("%s: Error allocating mask\n", __func__);
220206724535SBorislav Petkov 		return false;
220306724535SBorislav Petkov 	}
220406724535SBorislav Petkov 
2205ba578cb3SRusty Russell 	get_cpus_on_this_dct_cpumask(mask, nid);
220606724535SBorislav Petkov 
2207ba578cb3SRusty Russell 	rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
2208ba578cb3SRusty Russell 
2209ba578cb3SRusty Russell 	for_each_cpu(cpu, mask) {
221050542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
22115980bb9cSBorislav Petkov 		nbe = reg->l & MSR_MCGCTL_NBE;
221206724535SBorislav Petkov 
2213956b9ba1SJoe Perches 		edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
221450542251SBorislav Petkov 			 cpu, reg->q,
221506724535SBorislav Petkov 			 (nbe ? "enabled" : "disabled"));
221606724535SBorislav Petkov 
221706724535SBorislav Petkov 		if (!nbe)
221806724535SBorislav Petkov 			goto out;
221906724535SBorislav Petkov 	}
222006724535SBorislav Petkov 	ret = true;
222106724535SBorislav Petkov 
222206724535SBorislav Petkov out:
2223ba578cb3SRusty Russell 	free_cpumask_var(mask);
2224f9431992SDoug Thompson 	return ret;
2225f9431992SDoug Thompson }
2226f9431992SDoug Thompson 
22272299ef71SBorislav Petkov static int toggle_ecc_err_reporting(struct ecc_settings *s, u8 nid, bool on)
2228f6d6ae96SBorislav Petkov {
2229f6d6ae96SBorislav Petkov 	cpumask_var_t cmask;
223050542251SBorislav Petkov 	int cpu;
2231f6d6ae96SBorislav Petkov 
2232f6d6ae96SBorislav Petkov 	if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
223324f9a7feSBorislav Petkov 		amd64_warn("%s: error allocating mask\n", __func__);
2234f6d6ae96SBorislav Petkov 		return false;
2235f6d6ae96SBorislav Petkov 	}
2236f6d6ae96SBorislav Petkov 
2237ae7bb7c6SBorislav Petkov 	get_cpus_on_this_dct_cpumask(cmask, nid);
2238f6d6ae96SBorislav Petkov 
2239f6d6ae96SBorislav Petkov 	rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2240f6d6ae96SBorislav Petkov 
2241f6d6ae96SBorislav Petkov 	for_each_cpu(cpu, cmask) {
2242f6d6ae96SBorislav Petkov 
224350542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
224450542251SBorislav Petkov 
2245f6d6ae96SBorislav Petkov 		if (on) {
22465980bb9cSBorislav Petkov 			if (reg->l & MSR_MCGCTL_NBE)
2247ae7bb7c6SBorislav Petkov 				s->flags.nb_mce_enable = 1;
2248f6d6ae96SBorislav Petkov 
22495980bb9cSBorislav Petkov 			reg->l |= MSR_MCGCTL_NBE;
2250f6d6ae96SBorislav Petkov 		} else {
2251f6d6ae96SBorislav Petkov 			/*
2252d95cf4deSBorislav Petkov 			 * Turn off NB MCE reporting only when it was off before
2253f6d6ae96SBorislav Petkov 			 */
2254ae7bb7c6SBorislav Petkov 			if (!s->flags.nb_mce_enable)
22555980bb9cSBorislav Petkov 				reg->l &= ~MSR_MCGCTL_NBE;
2256f6d6ae96SBorislav Petkov 		}
2257f6d6ae96SBorislav Petkov 	}
2258f6d6ae96SBorislav Petkov 	wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2259f6d6ae96SBorislav Petkov 
2260f6d6ae96SBorislav Petkov 	free_cpumask_var(cmask);
2261f6d6ae96SBorislav Petkov 
2262f6d6ae96SBorislav Petkov 	return 0;
2263f6d6ae96SBorislav Petkov }
2264f6d6ae96SBorislav Petkov 
22652299ef71SBorislav Petkov static bool enable_ecc_error_reporting(struct ecc_settings *s, u8 nid,
22662299ef71SBorislav Petkov 				       struct pci_dev *F3)
2267f6d6ae96SBorislav Petkov {
22682299ef71SBorislav Petkov 	bool ret = true;
2269c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
2270f6d6ae96SBorislav Petkov 
22712299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, ON)) {
22722299ef71SBorislav Petkov 		amd64_warn("Error enabling ECC reporting over MCGCTL!\n");
22732299ef71SBorislav Petkov 		return false;
22742299ef71SBorislav Petkov 	}
22752299ef71SBorislav Petkov 
2276c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
2277f6d6ae96SBorislav Petkov 
2278ae7bb7c6SBorislav Petkov 	s->old_nbctl   = value & mask;
2279ae7bb7c6SBorislav Petkov 	s->nbctl_valid = true;
2280f6d6ae96SBorislav Petkov 
2281f6d6ae96SBorislav Petkov 	value |= mask;
2282c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
2283f6d6ae96SBorislav Petkov 
2284a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCFG, &value);
2285f6d6ae96SBorislav Petkov 
2286956b9ba1SJoe Perches 	edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2287a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
2288f6d6ae96SBorislav Petkov 
2289a97fa68eSBorislav Petkov 	if (!(value & NBCFG_ECC_ENABLE)) {
229024f9a7feSBorislav Petkov 		amd64_warn("DRAM ECC disabled on this node, enabling...\n");
2291f6d6ae96SBorislav Petkov 
2292ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 0;
2293d95cf4deSBorislav Petkov 
2294f6d6ae96SBorislav Petkov 		/* Attempt to turn on DRAM ECC Enable */
2295a97fa68eSBorislav Petkov 		value |= NBCFG_ECC_ENABLE;
2296a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
2297f6d6ae96SBorislav Petkov 
2298a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
2299f6d6ae96SBorislav Petkov 
2300a97fa68eSBorislav Petkov 		if (!(value & NBCFG_ECC_ENABLE)) {
230124f9a7feSBorislav Petkov 			amd64_warn("Hardware rejected DRAM ECC enable,"
230224f9a7feSBorislav Petkov 				   "check memory DIMM configuration.\n");
23032299ef71SBorislav Petkov 			ret = false;
2304f6d6ae96SBorislav Petkov 		} else {
230524f9a7feSBorislav Petkov 			amd64_info("Hardware accepted DRAM ECC Enable\n");
2306f6d6ae96SBorislav Petkov 		}
2307d95cf4deSBorislav Petkov 	} else {
2308ae7bb7c6SBorislav Petkov 		s->flags.nb_ecc_prev = 1;
2309f6d6ae96SBorislav Petkov 	}
2310d95cf4deSBorislav Petkov 
2311956b9ba1SJoe Perches 	edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
2312a97fa68eSBorislav Petkov 		 nid, value, !!(value & NBCFG_ECC_ENABLE));
2313f6d6ae96SBorislav Petkov 
23142299ef71SBorislav Petkov 	return ret;
2315f6d6ae96SBorislav Petkov }
2316f6d6ae96SBorislav Petkov 
2317360b7f3cSBorislav Petkov static void restore_ecc_error_reporting(struct ecc_settings *s, u8 nid,
2318360b7f3cSBorislav Petkov 					struct pci_dev *F3)
2319f6d6ae96SBorislav Petkov {
2320c9f4f26eSBorislav Petkov 	u32 value, mask = 0x3;		/* UECC/CECC enable */
2321c9f4f26eSBorislav Petkov 
2322f6d6ae96SBorislav Petkov 
2323ae7bb7c6SBorislav Petkov 	if (!s->nbctl_valid)
2324f6d6ae96SBorislav Petkov 		return;
2325f6d6ae96SBorislav Petkov 
2326c9f4f26eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCTL, &value);
2327f6d6ae96SBorislav Petkov 	value &= ~mask;
2328ae7bb7c6SBorislav Petkov 	value |= s->old_nbctl;
2329f6d6ae96SBorislav Petkov 
2330c9f4f26eSBorislav Petkov 	amd64_write_pci_cfg(F3, NBCTL, value);
2331f6d6ae96SBorislav Petkov 
2332ae7bb7c6SBorislav Petkov 	/* restore previous BIOS DRAM ECC "off" setting we force-enabled */
2333ae7bb7c6SBorislav Petkov 	if (!s->flags.nb_ecc_prev) {
2334a97fa68eSBorislav Petkov 		amd64_read_pci_cfg(F3, NBCFG, &value);
2335a97fa68eSBorislav Petkov 		value &= ~NBCFG_ECC_ENABLE;
2336a97fa68eSBorislav Petkov 		amd64_write_pci_cfg(F3, NBCFG, value);
2337d95cf4deSBorislav Petkov 	}
2338d95cf4deSBorislav Petkov 
2339d95cf4deSBorislav Petkov 	/* restore the NB Enable MCGCTL bit */
23402299ef71SBorislav Petkov 	if (toggle_ecc_err_reporting(s, nid, OFF))
234124f9a7feSBorislav Petkov 		amd64_warn("Error restoring NB MCGCTL settings!\n");
2342f6d6ae96SBorislav Petkov }
2343f6d6ae96SBorislav Petkov 
2344f9431992SDoug Thompson /*
23452299ef71SBorislav Petkov  * EDAC requires that the BIOS have ECC enabled before
23462299ef71SBorislav Petkov  * taking over the processing of ECC errors. A command line
23472299ef71SBorislav Petkov  * option allows to force-enable hardware ECC later in
23482299ef71SBorislav Petkov  * enable_ecc_error_reporting().
2349f9431992SDoug Thompson  */
2350cab4d277SBorislav Petkov static const char *ecc_msg =
2351cab4d277SBorislav Petkov 	"ECC disabled in the BIOS or no ECC capability, module will not load.\n"
2352cab4d277SBorislav Petkov 	" Either enable ECC checking or force module loading by setting "
2353cab4d277SBorislav Petkov 	"'ecc_enable_override'.\n"
2354cab4d277SBorislav Petkov 	" (Note that use of the override may cause unknown side effects.)\n";
2355be3468e8SBorislav Petkov 
23562299ef71SBorislav Petkov static bool ecc_enabled(struct pci_dev *F3, u8 nid)
2357f9431992SDoug Thompson {
2358f9431992SDoug Thompson 	u32 value;
23592299ef71SBorislav Petkov 	u8 ecc_en = 0;
236006724535SBorislav Petkov 	bool nb_mce_en = false;
2361f9431992SDoug Thompson 
2362a97fa68eSBorislav Petkov 	amd64_read_pci_cfg(F3, NBCFG, &value);
2363f9431992SDoug Thompson 
2364a97fa68eSBorislav Petkov 	ecc_en = !!(value & NBCFG_ECC_ENABLE);
23652299ef71SBorislav Petkov 	amd64_info("DRAM ECC %s.\n", (ecc_en ? "enabled" : "disabled"));
2366be3468e8SBorislav Petkov 
23672299ef71SBorislav Petkov 	nb_mce_en = amd64_nb_mce_bank_enabled_on_node(nid);
236806724535SBorislav Petkov 	if (!nb_mce_en)
23692299ef71SBorislav Petkov 		amd64_notice("NB MCE bank disabled, set MSR "
23702299ef71SBorislav Petkov 			     "0x%08x[4] on node %d to enable.\n",
23712299ef71SBorislav Petkov 			     MSR_IA32_MCG_CTL, nid);
2372be3468e8SBorislav Petkov 
23732299ef71SBorislav Petkov 	if (!ecc_en || !nb_mce_en) {
237424f9a7feSBorislav Petkov 		amd64_notice("%s", ecc_msg);
23752299ef71SBorislav Petkov 		return false;
2376be3468e8SBorislav Petkov 	}
23772299ef71SBorislav Petkov 	return true;
2378f9431992SDoug Thompson }
2379f9431992SDoug Thompson 
2380c5608759SMauro Carvalho Chehab static int set_mc_sysfs_attrs(struct mem_ctl_info *mci)
23817d6034d3SDoug Thompson {
2382c5608759SMauro Carvalho Chehab 	int rc;
23837d6034d3SDoug Thompson 
2384c5608759SMauro Carvalho Chehab 	rc = amd64_create_sysfs_dbg_files(mci);
2385c5608759SMauro Carvalho Chehab 	if (rc < 0)
2386c5608759SMauro Carvalho Chehab 		return rc;
2387c5608759SMauro Carvalho Chehab 
2388c5608759SMauro Carvalho Chehab 	if (boot_cpu_data.x86 >= 0x10) {
2389c5608759SMauro Carvalho Chehab 		rc = amd64_create_sysfs_inject_files(mci);
2390c5608759SMauro Carvalho Chehab 		if (rc < 0)
2391c5608759SMauro Carvalho Chehab 			return rc;
2392c5608759SMauro Carvalho Chehab 	}
2393c5608759SMauro Carvalho Chehab 
2394c5608759SMauro Carvalho Chehab 	return 0;
2395c5608759SMauro Carvalho Chehab }
2396c5608759SMauro Carvalho Chehab 
2397c5608759SMauro Carvalho Chehab static void del_mc_sysfs_attrs(struct mem_ctl_info *mci)
2398c5608759SMauro Carvalho Chehab {
2399c5608759SMauro Carvalho Chehab 	amd64_remove_sysfs_dbg_files(mci);
24007d6034d3SDoug Thompson 
2401a135cef7SBorislav Petkov 	if (boot_cpu_data.x86 >= 0x10)
2402c5608759SMauro Carvalho Chehab 		amd64_remove_sysfs_inject_files(mci);
24037d6034d3SDoug Thompson }
24047d6034d3SDoug Thompson 
2405df71a053SBorislav Petkov static void setup_mci_misc_attrs(struct mem_ctl_info *mci,
2406df71a053SBorislav Petkov 				 struct amd64_family_type *fam)
24077d6034d3SDoug Thompson {
24087d6034d3SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
24097d6034d3SDoug Thompson 
24107d6034d3SDoug Thompson 	mci->mtype_cap		= MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
24117d6034d3SDoug Thompson 	mci->edac_ctl_cap	= EDAC_FLAG_NONE;
24127d6034d3SDoug Thompson 
24135980bb9cSBorislav Petkov 	if (pvt->nbcap & NBCAP_SECDED)
24147d6034d3SDoug Thompson 		mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
24157d6034d3SDoug Thompson 
24165980bb9cSBorislav Petkov 	if (pvt->nbcap & NBCAP_CHIPKILL)
24177d6034d3SDoug Thompson 		mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
24187d6034d3SDoug Thompson 
24197d6034d3SDoug Thompson 	mci->edac_cap		= amd64_determine_edac_cap(pvt);
24207d6034d3SDoug Thompson 	mci->mod_name		= EDAC_MOD_STR;
24217d6034d3SDoug Thompson 	mci->mod_ver		= EDAC_AMD64_VERSION;
2422df71a053SBorislav Petkov 	mci->ctl_name		= fam->ctl_name;
24238d5b5d9cSBorislav Petkov 	mci->dev_name		= pci_name(pvt->F2);
24247d6034d3SDoug Thompson 	mci->ctl_page_to_phys	= NULL;
24257d6034d3SDoug Thompson 
24267d6034d3SDoug Thompson 	/* memory scrubber interface */
24277d6034d3SDoug Thompson 	mci->set_sdram_scrub_rate = amd64_set_scrub_rate;
24287d6034d3SDoug Thompson 	mci->get_sdram_scrub_rate = amd64_get_scrub_rate;
24297d6034d3SDoug Thompson }
24307d6034d3SDoug Thompson 
24310092b20dSBorislav Petkov /*
24320092b20dSBorislav Petkov  * returns a pointer to the family descriptor on success, NULL otherwise.
24330092b20dSBorislav Petkov  */
24340092b20dSBorislav Petkov static struct amd64_family_type *amd64_per_family_init(struct amd64_pvt *pvt)
2435395ae783SBorislav Petkov {
24360092b20dSBorislav Petkov 	u8 fam = boot_cpu_data.x86;
24370092b20dSBorislav Petkov 	struct amd64_family_type *fam_type = NULL;
24380092b20dSBorislav Petkov 
24390092b20dSBorislav Petkov 	switch (fam) {
2440395ae783SBorislav Petkov 	case 0xf:
24410092b20dSBorislav Petkov 		fam_type		= &amd64_family_types[K8_CPUS];
2442b8cfa02fSBorislav Petkov 		pvt->ops		= &amd64_family_types[K8_CPUS].ops;
2443395ae783SBorislav Petkov 		break;
2444df71a053SBorislav Petkov 
2445395ae783SBorislav Petkov 	case 0x10:
24460092b20dSBorislav Petkov 		fam_type		= &amd64_family_types[F10_CPUS];
2447b8cfa02fSBorislav Petkov 		pvt->ops		= &amd64_family_types[F10_CPUS].ops;
2448df71a053SBorislav Petkov 		break;
2449df71a053SBorislav Petkov 
2450df71a053SBorislav Petkov 	case 0x15:
2451df71a053SBorislav Petkov 		fam_type		= &amd64_family_types[F15_CPUS];
2452df71a053SBorislav Petkov 		pvt->ops		= &amd64_family_types[F15_CPUS].ops;
2453395ae783SBorislav Petkov 		break;
2454395ae783SBorislav Petkov 
2455395ae783SBorislav Petkov 	default:
245624f9a7feSBorislav Petkov 		amd64_err("Unsupported family!\n");
24570092b20dSBorislav Petkov 		return NULL;
2458395ae783SBorislav Petkov 	}
24590092b20dSBorislav Petkov 
2460b8cfa02fSBorislav Petkov 	pvt->ext_model = boot_cpu_data.x86_model >> 4;
2461b8cfa02fSBorislav Petkov 
2462df71a053SBorislav Petkov 	amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name,
24630092b20dSBorislav Petkov 		     (fam == 0xf ?
24640092b20dSBorislav Petkov 				(pvt->ext_model >= K8_REV_F  ? "revF or later "
24650092b20dSBorislav Petkov 							     : "revE or earlier ")
246624f9a7feSBorislav Petkov 				 : ""), pvt->mc_node_id);
24670092b20dSBorislav Petkov 	return fam_type;
2468395ae783SBorislav Petkov }
2469395ae783SBorislav Petkov 
24702299ef71SBorislav Petkov static int amd64_init_one_instance(struct pci_dev *F2)
24717d6034d3SDoug Thompson {
24727d6034d3SDoug Thompson 	struct amd64_pvt *pvt = NULL;
24730092b20dSBorislav Petkov 	struct amd64_family_type *fam_type = NULL;
2474360b7f3cSBorislav Petkov 	struct mem_ctl_info *mci = NULL;
2475ab5a503cSMauro Carvalho Chehab 	struct edac_mc_layer layers[2];
24767d6034d3SDoug Thompson 	int err = 0, ret;
2477772c3ff3SDaniel J Blueman 	u16 nid = amd_get_node_id(F2);
24787d6034d3SDoug Thompson 
24797d6034d3SDoug Thompson 	ret = -ENOMEM;
24807d6034d3SDoug Thompson 	pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
24817d6034d3SDoug Thompson 	if (!pvt)
2482360b7f3cSBorislav Petkov 		goto err_ret;
24837d6034d3SDoug Thompson 
2484360b7f3cSBorislav Petkov 	pvt->mc_node_id	= nid;
24858d5b5d9cSBorislav Petkov 	pvt->F2 = F2;
24867d6034d3SDoug Thompson 
2487395ae783SBorislav Petkov 	ret = -EINVAL;
24880092b20dSBorislav Petkov 	fam_type = amd64_per_family_init(pvt);
24890092b20dSBorislav Petkov 	if (!fam_type)
2490395ae783SBorislav Petkov 		goto err_free;
2491395ae783SBorislav Petkov 
24927d6034d3SDoug Thompson 	ret = -ENODEV;
2493360b7f3cSBorislav Petkov 	err = reserve_mc_sibling_devs(pvt, fam_type->f1_id, fam_type->f3_id);
24947d6034d3SDoug Thompson 	if (err)
24957d6034d3SDoug Thompson 		goto err_free;
24967d6034d3SDoug Thompson 
2497360b7f3cSBorislav Petkov 	read_mc_regs(pvt);
24987d6034d3SDoug Thompson 
24997d6034d3SDoug Thompson 	/*
25007d6034d3SDoug Thompson 	 * We need to determine how many memory channels there are. Then use
25017d6034d3SDoug Thompson 	 * that information for calculating the size of the dynamic instance
2502360b7f3cSBorislav Petkov 	 * tables in the 'mci' structure.
25037d6034d3SDoug Thompson 	 */
2504360b7f3cSBorislav Petkov 	ret = -EINVAL;
25057d6034d3SDoug Thompson 	pvt->channel_count = pvt->ops->early_channel_count(pvt);
25067d6034d3SDoug Thompson 	if (pvt->channel_count < 0)
2507360b7f3cSBorislav Petkov 		goto err_siblings;
25087d6034d3SDoug Thompson 
25097d6034d3SDoug Thompson 	ret = -ENOMEM;
2510ab5a503cSMauro Carvalho Chehab 	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
2511ab5a503cSMauro Carvalho Chehab 	layers[0].size = pvt->csels[0].b_cnt;
2512ab5a503cSMauro Carvalho Chehab 	layers[0].is_virt_csrow = true;
2513ab5a503cSMauro Carvalho Chehab 	layers[1].type = EDAC_MC_LAYER_CHANNEL;
2514ab5a503cSMauro Carvalho Chehab 	layers[1].size = pvt->channel_count;
2515ab5a503cSMauro Carvalho Chehab 	layers[1].is_virt_csrow = false;
2516ca0907b9SMauro Carvalho Chehab 	mci = edac_mc_alloc(nid, ARRAY_SIZE(layers), layers, 0);
25177d6034d3SDoug Thompson 	if (!mci)
2518360b7f3cSBorislav Petkov 		goto err_siblings;
25197d6034d3SDoug Thompson 
25207d6034d3SDoug Thompson 	mci->pvt_info = pvt;
2521fd687502SMauro Carvalho Chehab 	mci->pdev = &pvt->F2->dev;
252211652769SBorislav Petkov 	mci->csbased = 1;
25237d6034d3SDoug Thompson 
2524df71a053SBorislav Petkov 	setup_mci_misc_attrs(mci, fam_type);
2525360b7f3cSBorislav Petkov 
2526360b7f3cSBorislav Petkov 	if (init_csrows(mci))
25277d6034d3SDoug Thompson 		mci->edac_cap = EDAC_FLAG_NONE;
25287d6034d3SDoug Thompson 
25297d6034d3SDoug Thompson 	ret = -ENODEV;
25307d6034d3SDoug Thompson 	if (edac_mc_add_mc(mci)) {
2531956b9ba1SJoe Perches 		edac_dbg(1, "failed edac_mc_add_mc()\n");
25327d6034d3SDoug Thompson 		goto err_add_mc;
25337d6034d3SDoug Thompson 	}
2534c5608759SMauro Carvalho Chehab 	if (set_mc_sysfs_attrs(mci)) {
2535956b9ba1SJoe Perches 		edac_dbg(1, "failed edac_mc_add_mc()\n");
2536c5608759SMauro Carvalho Chehab 		goto err_add_sysfs;
2537c5608759SMauro Carvalho Chehab 	}
25387d6034d3SDoug Thompson 
2539549d042dSBorislav Petkov 	/* register stuff with EDAC MCE */
2540549d042dSBorislav Petkov 	if (report_gart_errors)
2541549d042dSBorislav Petkov 		amd_report_gart_errors(true);
2542549d042dSBorislav Petkov 
2543549d042dSBorislav Petkov 	amd_register_ecc_decoder(amd64_decode_bus_error);
2544549d042dSBorislav Petkov 
2545360b7f3cSBorislav Petkov 	mcis[nid] = mci;
2546360b7f3cSBorislav Petkov 
2547360b7f3cSBorislav Petkov 	atomic_inc(&drv_instances);
2548360b7f3cSBorislav Petkov 
25497d6034d3SDoug Thompson 	return 0;
25507d6034d3SDoug Thompson 
2551c5608759SMauro Carvalho Chehab err_add_sysfs:
2552c5608759SMauro Carvalho Chehab 	edac_mc_del_mc(mci->pdev);
25537d6034d3SDoug Thompson err_add_mc:
25547d6034d3SDoug Thompson 	edac_mc_free(mci);
25557d6034d3SDoug Thompson 
2556360b7f3cSBorislav Petkov err_siblings:
2557360b7f3cSBorislav Petkov 	free_mc_sibling_devs(pvt);
25587d6034d3SDoug Thompson 
2559360b7f3cSBorislav Petkov err_free:
2560360b7f3cSBorislav Petkov 	kfree(pvt);
25617d6034d3SDoug Thompson 
2562360b7f3cSBorislav Petkov err_ret:
25637d6034d3SDoug Thompson 	return ret;
25647d6034d3SDoug Thompson }
25657d6034d3SDoug Thompson 
25669b3c6e85SGreg Kroah-Hartman static int amd64_probe_one_instance(struct pci_dev *pdev,
25677d6034d3SDoug Thompson 				    const struct pci_device_id *mc_type)
25687d6034d3SDoug Thompson {
2569772c3ff3SDaniel J Blueman 	u16 nid = amd_get_node_id(pdev);
25702299ef71SBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
2571ae7bb7c6SBorislav Petkov 	struct ecc_settings *s;
25722299ef71SBorislav Petkov 	int ret = 0;
25737d6034d3SDoug Thompson 
25747d6034d3SDoug Thompson 	ret = pci_enable_device(pdev);
2575b8cfa02fSBorislav Petkov 	if (ret < 0) {
2576956b9ba1SJoe Perches 		edac_dbg(0, "ret=%d\n", ret);
2577b8cfa02fSBorislav Petkov 		return -EIO;
2578b8cfa02fSBorislav Petkov 	}
2579b8cfa02fSBorislav Petkov 
2580ae7bb7c6SBorislav Petkov 	ret = -ENOMEM;
2581ae7bb7c6SBorislav Petkov 	s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL);
2582ae7bb7c6SBorislav Petkov 	if (!s)
25832299ef71SBorislav Petkov 		goto err_out;
2584ae7bb7c6SBorislav Petkov 
2585ae7bb7c6SBorislav Petkov 	ecc_stngs[nid] = s;
2586ae7bb7c6SBorislav Petkov 
25872299ef71SBorislav Petkov 	if (!ecc_enabled(F3, nid)) {
25882299ef71SBorislav Petkov 		ret = -ENODEV;
25892299ef71SBorislav Petkov 
25902299ef71SBorislav Petkov 		if (!ecc_enable_override)
25912299ef71SBorislav Petkov 			goto err_enable;
25922299ef71SBorislav Petkov 
25932299ef71SBorislav Petkov 		amd64_warn("Forcing ECC on!\n");
25942299ef71SBorislav Petkov 
25952299ef71SBorislav Petkov 		if (!enable_ecc_error_reporting(s, nid, F3))
25962299ef71SBorislav Petkov 			goto err_enable;
25972299ef71SBorislav Petkov 	}
25982299ef71SBorislav Petkov 
25992299ef71SBorislav Petkov 	ret = amd64_init_one_instance(pdev);
2600360b7f3cSBorislav Petkov 	if (ret < 0) {
2601ae7bb7c6SBorislav Petkov 		amd64_err("Error probing instance: %d\n", nid);
2602360b7f3cSBorislav Petkov 		restore_ecc_error_reporting(s, nid, F3);
2603360b7f3cSBorislav Petkov 	}
26047d6034d3SDoug Thompson 
26057d6034d3SDoug Thompson 	return ret;
26062299ef71SBorislav Petkov 
26072299ef71SBorislav Petkov err_enable:
26082299ef71SBorislav Petkov 	kfree(s);
26092299ef71SBorislav Petkov 	ecc_stngs[nid] = NULL;
26102299ef71SBorislav Petkov 
26112299ef71SBorislav Petkov err_out:
26122299ef71SBorislav Petkov 	return ret;
26137d6034d3SDoug Thompson }
26147d6034d3SDoug Thompson 
26159b3c6e85SGreg Kroah-Hartman static void amd64_remove_one_instance(struct pci_dev *pdev)
26167d6034d3SDoug Thompson {
26177d6034d3SDoug Thompson 	struct mem_ctl_info *mci;
26187d6034d3SDoug Thompson 	struct amd64_pvt *pvt;
2619772c3ff3SDaniel J Blueman 	u16 nid = amd_get_node_id(pdev);
2620360b7f3cSBorislav Petkov 	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
2621360b7f3cSBorislav Petkov 	struct ecc_settings *s = ecc_stngs[nid];
26227d6034d3SDoug Thompson 
2623c5608759SMauro Carvalho Chehab 	mci = find_mci_by_dev(&pdev->dev);
2624c5608759SMauro Carvalho Chehab 	del_mc_sysfs_attrs(mci);
26257d6034d3SDoug Thompson 	/* Remove from EDAC CORE tracking list */
26267d6034d3SDoug Thompson 	mci = edac_mc_del_mc(&pdev->dev);
26277d6034d3SDoug Thompson 	if (!mci)
26287d6034d3SDoug Thompson 		return;
26297d6034d3SDoug Thompson 
26307d6034d3SDoug Thompson 	pvt = mci->pvt_info;
26317d6034d3SDoug Thompson 
2632360b7f3cSBorislav Petkov 	restore_ecc_error_reporting(s, nid, F3);
26337d6034d3SDoug Thompson 
2634360b7f3cSBorislav Petkov 	free_mc_sibling_devs(pvt);
26357d6034d3SDoug Thompson 
2636549d042dSBorislav Petkov 	/* unregister from EDAC MCE */
2637549d042dSBorislav Petkov 	amd_report_gart_errors(false);
2638549d042dSBorislav Petkov 	amd_unregister_ecc_decoder(amd64_decode_bus_error);
2639549d042dSBorislav Petkov 
2640360b7f3cSBorislav Petkov 	kfree(ecc_stngs[nid]);
2641360b7f3cSBorislav Petkov 	ecc_stngs[nid] = NULL;
2642ae7bb7c6SBorislav Petkov 
26437d6034d3SDoug Thompson 	/* Free the EDAC CORE resources */
26448f68ed97SBorislav Petkov 	mci->pvt_info = NULL;
2645360b7f3cSBorislav Petkov 	mcis[nid] = NULL;
26468f68ed97SBorislav Petkov 
26478f68ed97SBorislav Petkov 	kfree(pvt);
26487d6034d3SDoug Thompson 	edac_mc_free(mci);
26497d6034d3SDoug Thompson }
26507d6034d3SDoug Thompson 
26517d6034d3SDoug Thompson /*
26527d6034d3SDoug Thompson  * This table is part of the interface for loading drivers for PCI devices. The
26537d6034d3SDoug Thompson  * PCI core identifies what devices are on a system during boot, and then
26547d6034d3SDoug Thompson  * inquiry this table to see if this driver is for a given device found.
26557d6034d3SDoug Thompson  */
265636c46f31SLionel Debroux static DEFINE_PCI_DEVICE_TABLE(amd64_pci_table) = {
26577d6034d3SDoug Thompson 	{
26587d6034d3SDoug Thompson 		.vendor		= PCI_VENDOR_ID_AMD,
26597d6034d3SDoug Thompson 		.device		= PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
26607d6034d3SDoug Thompson 		.subvendor	= PCI_ANY_ID,
26617d6034d3SDoug Thompson 		.subdevice	= PCI_ANY_ID,
26627d6034d3SDoug Thompson 		.class		= 0,
26637d6034d3SDoug Thompson 		.class_mask	= 0,
26647d6034d3SDoug Thompson 	},
26657d6034d3SDoug Thompson 	{
26667d6034d3SDoug Thompson 		.vendor		= PCI_VENDOR_ID_AMD,
26677d6034d3SDoug Thompson 		.device		= PCI_DEVICE_ID_AMD_10H_NB_DRAM,
26687d6034d3SDoug Thompson 		.subvendor	= PCI_ANY_ID,
26697d6034d3SDoug Thompson 		.subdevice	= PCI_ANY_ID,
26707d6034d3SDoug Thompson 		.class		= 0,
26717d6034d3SDoug Thompson 		.class_mask	= 0,
26727d6034d3SDoug Thompson 	},
2673df71a053SBorislav Petkov 	{
2674df71a053SBorislav Petkov 		.vendor		= PCI_VENDOR_ID_AMD,
2675df71a053SBorislav Petkov 		.device		= PCI_DEVICE_ID_AMD_15H_NB_F2,
2676df71a053SBorislav Petkov 		.subvendor	= PCI_ANY_ID,
2677df71a053SBorislav Petkov 		.subdevice	= PCI_ANY_ID,
2678df71a053SBorislav Petkov 		.class		= 0,
2679df71a053SBorislav Petkov 		.class_mask	= 0,
2680df71a053SBorislav Petkov 	},
2681df71a053SBorislav Petkov 
26827d6034d3SDoug Thompson 	{0, }
26837d6034d3SDoug Thompson };
26847d6034d3SDoug Thompson MODULE_DEVICE_TABLE(pci, amd64_pci_table);
26857d6034d3SDoug Thompson 
26867d6034d3SDoug Thompson static struct pci_driver amd64_pci_driver = {
26877d6034d3SDoug Thompson 	.name		= EDAC_MOD_STR,
26882299ef71SBorislav Petkov 	.probe		= amd64_probe_one_instance,
26899b3c6e85SGreg Kroah-Hartman 	.remove		= amd64_remove_one_instance,
26907d6034d3SDoug Thompson 	.id_table	= amd64_pci_table,
26917d6034d3SDoug Thompson };
26927d6034d3SDoug Thompson 
2693360b7f3cSBorislav Petkov static void setup_pci_device(void)
26947d6034d3SDoug Thompson {
26957d6034d3SDoug Thompson 	struct mem_ctl_info *mci;
26967d6034d3SDoug Thompson 	struct amd64_pvt *pvt;
26977d6034d3SDoug Thompson 
26987d6034d3SDoug Thompson 	if (amd64_ctl_pci)
26997d6034d3SDoug Thompson 		return;
27007d6034d3SDoug Thompson 
2701cc4d8860SBorislav Petkov 	mci = mcis[0];
27027d6034d3SDoug Thompson 	if (mci) {
27037d6034d3SDoug Thompson 
27047d6034d3SDoug Thompson 		pvt = mci->pvt_info;
27057d6034d3SDoug Thompson 		amd64_ctl_pci =
27068d5b5d9cSBorislav Petkov 			edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR);
27077d6034d3SDoug Thompson 
27087d6034d3SDoug Thompson 		if (!amd64_ctl_pci) {
27097d6034d3SDoug Thompson 			pr_warning("%s(): Unable to create PCI control\n",
27107d6034d3SDoug Thompson 				   __func__);
27117d6034d3SDoug Thompson 
27127d6034d3SDoug Thompson 			pr_warning("%s(): PCI error report via EDAC not set\n",
27137d6034d3SDoug Thompson 				   __func__);
27147d6034d3SDoug Thompson 			}
27157d6034d3SDoug Thompson 	}
27167d6034d3SDoug Thompson }
27177d6034d3SDoug Thompson 
27187d6034d3SDoug Thompson static int __init amd64_edac_init(void)
27197d6034d3SDoug Thompson {
2720360b7f3cSBorislav Petkov 	int err = -ENODEV;
27217d6034d3SDoug Thompson 
2722df71a053SBorislav Petkov 	printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION);
27237d6034d3SDoug Thompson 
27247d6034d3SDoug Thompson 	opstate_init();
27257d6034d3SDoug Thompson 
27269653a5c7SHans Rosenfeld 	if (amd_cache_northbridges() < 0)
272756b34b91SBorislav Petkov 		goto err_ret;
27287d6034d3SDoug Thompson 
2729cc4d8860SBorislav Petkov 	err = -ENOMEM;
2730cc4d8860SBorislav Petkov 	mcis	  = kzalloc(amd_nb_num() * sizeof(mcis[0]), GFP_KERNEL);
2731ae7bb7c6SBorislav Petkov 	ecc_stngs = kzalloc(amd_nb_num() * sizeof(ecc_stngs[0]), GFP_KERNEL);
2732360b7f3cSBorislav Petkov 	if (!(mcis && ecc_stngs))
2733a9f0fbe2SBorislav Petkov 		goto err_free;
2734cc4d8860SBorislav Petkov 
273550542251SBorislav Petkov 	msrs = msrs_alloc();
273656b34b91SBorislav Petkov 	if (!msrs)
2737360b7f3cSBorislav Petkov 		goto err_free;
273850542251SBorislav Petkov 
27397d6034d3SDoug Thompson 	err = pci_register_driver(&amd64_pci_driver);
27407d6034d3SDoug Thompson 	if (err)
274156b34b91SBorislav Petkov 		goto err_pci;
27427d6034d3SDoug Thompson 
274356b34b91SBorislav Petkov 	err = -ENODEV;
2744360b7f3cSBorislav Petkov 	if (!atomic_read(&drv_instances))
2745360b7f3cSBorislav Petkov 		goto err_no_instances;
27467d6034d3SDoug Thompson 
2747360b7f3cSBorislav Petkov 	setup_pci_device();
27487d6034d3SDoug Thompson 	return 0;
27497d6034d3SDoug Thompson 
2750360b7f3cSBorislav Petkov err_no_instances:
27517d6034d3SDoug Thompson 	pci_unregister_driver(&amd64_pci_driver);
2752cc4d8860SBorislav Petkov 
275356b34b91SBorislav Petkov err_pci:
275456b34b91SBorislav Petkov 	msrs_free(msrs);
275556b34b91SBorislav Petkov 	msrs = NULL;
2756cc4d8860SBorislav Petkov 
2757360b7f3cSBorislav Petkov err_free:
2758360b7f3cSBorislav Petkov 	kfree(mcis);
2759360b7f3cSBorislav Petkov 	mcis = NULL;
2760360b7f3cSBorislav Petkov 
2761360b7f3cSBorislav Petkov 	kfree(ecc_stngs);
2762360b7f3cSBorislav Petkov 	ecc_stngs = NULL;
2763360b7f3cSBorislav Petkov 
276456b34b91SBorislav Petkov err_ret:
27657d6034d3SDoug Thompson 	return err;
27667d6034d3SDoug Thompson }
27677d6034d3SDoug Thompson 
27687d6034d3SDoug Thompson static void __exit amd64_edac_exit(void)
27697d6034d3SDoug Thompson {
27707d6034d3SDoug Thompson 	if (amd64_ctl_pci)
27717d6034d3SDoug Thompson 		edac_pci_release_generic_ctl(amd64_ctl_pci);
27727d6034d3SDoug Thompson 
27737d6034d3SDoug Thompson 	pci_unregister_driver(&amd64_pci_driver);
277450542251SBorislav Petkov 
2775ae7bb7c6SBorislav Petkov 	kfree(ecc_stngs);
2776ae7bb7c6SBorislav Petkov 	ecc_stngs = NULL;
2777ae7bb7c6SBorislav Petkov 
2778cc4d8860SBorislav Petkov 	kfree(mcis);
2779cc4d8860SBorislav Petkov 	mcis = NULL;
2780cc4d8860SBorislav Petkov 
278150542251SBorislav Petkov 	msrs_free(msrs);
278250542251SBorislav Petkov 	msrs = NULL;
27837d6034d3SDoug Thompson }
27847d6034d3SDoug Thompson 
27857d6034d3SDoug Thompson module_init(amd64_edac_init);
27867d6034d3SDoug Thompson module_exit(amd64_edac_exit);
27877d6034d3SDoug Thompson 
27887d6034d3SDoug Thompson MODULE_LICENSE("GPL");
27897d6034d3SDoug Thompson MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, "
27907d6034d3SDoug Thompson 		"Dave Peterson, Thayne Harbaugh");
27917d6034d3SDoug Thompson MODULE_DESCRIPTION("MC support for AMD64 memory controllers - "
27927d6034d3SDoug Thompson 		EDAC_AMD64_VERSION);
27937d6034d3SDoug Thompson 
27947d6034d3SDoug Thompson module_param(edac_op_state, int, 0444);
27957d6034d3SDoug Thompson MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
2796