xref: /openbmc/linux/drivers/edac/amd64_edac.c (revision 24f9a7fe)
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 
182bc65418SDoug Thompson /* Lookup table for all possible MC control instances */
192bc65418SDoug Thompson struct amd64_pvt;
203011b20dSBorislav Petkov static struct mem_ctl_info *mci_lookup[EDAC_MAX_NUMNODES];
213011b20dSBorislav Petkov static struct amd64_pvt *pvt_lookup[EDAC_MAX_NUMNODES];
222bc65418SDoug Thompson 
232bc65418SDoug Thompson /*
241433eb99SBorislav Petkov  * Address to DRAM bank mapping: see F2x80 for K8 and F2x[1,0]80 for Fam10 and
251433eb99SBorislav Petkov  * later.
26b70ef010SBorislav Petkov  */
271433eb99SBorislav Petkov static int ddr2_dbam_revCG[] = {
281433eb99SBorislav Petkov 			   [0]		= 32,
291433eb99SBorislav Petkov 			   [1]		= 64,
301433eb99SBorislav Petkov 			   [2]		= 128,
311433eb99SBorislav Petkov 			   [3]		= 256,
321433eb99SBorislav Petkov 			   [4]		= 512,
331433eb99SBorislav Petkov 			   [5]		= 1024,
341433eb99SBorislav Petkov 			   [6]		= 2048,
351433eb99SBorislav Petkov };
361433eb99SBorislav Petkov 
371433eb99SBorislav Petkov static int ddr2_dbam_revD[] = {
381433eb99SBorislav Petkov 			   [0]		= 32,
391433eb99SBorislav Petkov 			   [1]		= 64,
401433eb99SBorislav Petkov 			   [2 ... 3]	= 128,
411433eb99SBorislav Petkov 			   [4]		= 256,
421433eb99SBorislav Petkov 			   [5]		= 512,
431433eb99SBorislav Petkov 			   [6]		= 256,
441433eb99SBorislav Petkov 			   [7]		= 512,
451433eb99SBorislav Petkov 			   [8 ... 9]	= 1024,
461433eb99SBorislav Petkov 			   [10]		= 2048,
471433eb99SBorislav Petkov };
481433eb99SBorislav Petkov 
491433eb99SBorislav Petkov static int ddr2_dbam[] = { [0]		= 128,
501433eb99SBorislav Petkov 			   [1]		= 256,
511433eb99SBorislav Petkov 			   [2 ... 4]	= 512,
521433eb99SBorislav Petkov 			   [5 ... 6]	= 1024,
531433eb99SBorislav Petkov 			   [7 ... 8]	= 2048,
541433eb99SBorislav Petkov 			   [9 ... 10]	= 4096,
551433eb99SBorislav Petkov 			   [11]		= 8192,
561433eb99SBorislav Petkov };
571433eb99SBorislav Petkov 
581433eb99SBorislav Petkov static int ddr3_dbam[] = { [0]		= -1,
591433eb99SBorislav Petkov 			   [1]		= 256,
601433eb99SBorislav Petkov 			   [2]		= 512,
611433eb99SBorislav Petkov 			   [3 ... 4]	= -1,
621433eb99SBorislav Petkov 			   [5 ... 6]	= 1024,
631433eb99SBorislav Petkov 			   [7 ... 8]	= 2048,
641433eb99SBorislav Petkov 			   [9 ... 10]	= 4096,
651433eb99SBorislav Petkov 			   [11]		= 8192,
66b70ef010SBorislav Petkov };
67b70ef010SBorislav Petkov 
68b70ef010SBorislav Petkov /*
69b70ef010SBorislav Petkov  * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing
70b70ef010SBorislav Petkov  * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching-
71b70ef010SBorislav Petkov  * or higher value'.
72b70ef010SBorislav Petkov  *
73b70ef010SBorislav Petkov  *FIXME: Produce a better mapping/linearisation.
74b70ef010SBorislav Petkov  */
75b70ef010SBorislav Petkov 
76b70ef010SBorislav Petkov struct scrubrate scrubrates[] = {
77b70ef010SBorislav Petkov 	{ 0x01, 1600000000UL},
78b70ef010SBorislav Petkov 	{ 0x02, 800000000UL},
79b70ef010SBorislav Petkov 	{ 0x03, 400000000UL},
80b70ef010SBorislav Petkov 	{ 0x04, 200000000UL},
81b70ef010SBorislav Petkov 	{ 0x05, 100000000UL},
82b70ef010SBorislav Petkov 	{ 0x06, 50000000UL},
83b70ef010SBorislav Petkov 	{ 0x07, 25000000UL},
84b70ef010SBorislav Petkov 	{ 0x08, 12284069UL},
85b70ef010SBorislav Petkov 	{ 0x09, 6274509UL},
86b70ef010SBorislav Petkov 	{ 0x0A, 3121951UL},
87b70ef010SBorislav Petkov 	{ 0x0B, 1560975UL},
88b70ef010SBorislav Petkov 	{ 0x0C, 781440UL},
89b70ef010SBorislav Petkov 	{ 0x0D, 390720UL},
90b70ef010SBorislav Petkov 	{ 0x0E, 195300UL},
91b70ef010SBorislav Petkov 	{ 0x0F, 97650UL},
92b70ef010SBorislav Petkov 	{ 0x10, 48854UL},
93b70ef010SBorislav Petkov 	{ 0x11, 24427UL},
94b70ef010SBorislav Petkov 	{ 0x12, 12213UL},
95b70ef010SBorislav Petkov 	{ 0x13, 6101UL},
96b70ef010SBorislav Petkov 	{ 0x14, 3051UL},
97b70ef010SBorislav Petkov 	{ 0x15, 1523UL},
98b70ef010SBorislav Petkov 	{ 0x16, 761UL},
99b70ef010SBorislav Petkov 	{ 0x00, 0UL},        /* scrubbing off */
100b70ef010SBorislav Petkov };
101b70ef010SBorislav Petkov 
102b70ef010SBorislav Petkov /*
1032bc65418SDoug Thompson  * Memory scrubber control interface. For K8, memory scrubbing is handled by
1042bc65418SDoug Thompson  * hardware and can involve L2 cache, dcache as well as the main memory. With
1052bc65418SDoug Thompson  * F10, this is extended to L3 cache scrubbing on CPU models sporting that
1062bc65418SDoug Thompson  * functionality.
1072bc65418SDoug Thompson  *
1082bc65418SDoug Thompson  * This causes the "units" for the scrubbing speed to vary from 64 byte blocks
1092bc65418SDoug Thompson  * (dram) over to cache lines. This is nasty, so we will use bandwidth in
1102bc65418SDoug Thompson  * bytes/sec for the setting.
1112bc65418SDoug Thompson  *
1122bc65418SDoug Thompson  * Currently, we only do dram scrubbing. If the scrubbing is done in software on
1132bc65418SDoug Thompson  * other archs, we might not have access to the caches directly.
1142bc65418SDoug Thompson  */
1152bc65418SDoug Thompson 
1162bc65418SDoug Thompson /*
1172bc65418SDoug Thompson  * scan the scrub rate mapping table for a close or matching bandwidth value to
1182bc65418SDoug Thompson  * issue. If requested is too big, then use last maximum value found.
1192bc65418SDoug Thompson  */
120395ae783SBorislav Petkov static int __amd64_set_scrub_rate(struct pci_dev *ctl, u32 new_bw, u32 min_rate)
1212bc65418SDoug Thompson {
1222bc65418SDoug Thompson 	u32 scrubval;
1232bc65418SDoug Thompson 	int i;
1242bc65418SDoug Thompson 
1252bc65418SDoug Thompson 	/*
1262bc65418SDoug Thompson 	 * map the configured rate (new_bw) to a value specific to the AMD64
1272bc65418SDoug Thompson 	 * memory controller and apply to register. Search for the first
1282bc65418SDoug Thompson 	 * bandwidth entry that is greater or equal than the setting requested
1292bc65418SDoug Thompson 	 * and program that. If at last entry, turn off DRAM scrubbing.
1302bc65418SDoug Thompson 	 */
1312bc65418SDoug Thompson 	for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
1322bc65418SDoug Thompson 		/*
1332bc65418SDoug Thompson 		 * skip scrub rates which aren't recommended
1342bc65418SDoug Thompson 		 * (see F10 BKDG, F3x58)
1352bc65418SDoug Thompson 		 */
136395ae783SBorislav Petkov 		if (scrubrates[i].scrubval < min_rate)
1372bc65418SDoug Thompson 			continue;
1382bc65418SDoug Thompson 
1392bc65418SDoug Thompson 		if (scrubrates[i].bandwidth <= new_bw)
1402bc65418SDoug Thompson 			break;
1412bc65418SDoug Thompson 
1422bc65418SDoug Thompson 		/*
1432bc65418SDoug Thompson 		 * if no suitable bandwidth found, turn off DRAM scrubbing
1442bc65418SDoug Thompson 		 * entirely by falling back to the last element in the
1452bc65418SDoug Thompson 		 * scrubrates array.
1462bc65418SDoug Thompson 		 */
1472bc65418SDoug Thompson 	}
1482bc65418SDoug Thompson 
1492bc65418SDoug Thompson 	scrubval = scrubrates[i].scrubval;
1502bc65418SDoug Thompson 	if (scrubval)
15124f9a7feSBorislav Petkov 		amd64_info("Setting scrub rate bandwidth: %u\n",
1522bc65418SDoug Thompson 			   scrubrates[i].bandwidth);
1532bc65418SDoug Thompson 	else
15424f9a7feSBorislav Petkov 		amd64_info("Turning scrubbing off.\n");
1552bc65418SDoug Thompson 
1562bc65418SDoug Thompson 	pci_write_bits32(ctl, K8_SCRCTRL, scrubval, 0x001F);
1572bc65418SDoug Thompson 
1582bc65418SDoug Thompson 	return 0;
1592bc65418SDoug Thompson }
1602bc65418SDoug Thompson 
161395ae783SBorislav Petkov static int amd64_set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
1622bc65418SDoug Thompson {
1632bc65418SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
1642bc65418SDoug Thompson 
1658d5b5d9cSBorislav Petkov 	return __amd64_set_scrub_rate(pvt->F3, bw, pvt->min_scrubrate);
1662bc65418SDoug Thompson }
1672bc65418SDoug Thompson 
1682bc65418SDoug Thompson static int amd64_get_scrub_rate(struct mem_ctl_info *mci, u32 *bw)
1692bc65418SDoug Thompson {
1702bc65418SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
1712bc65418SDoug Thompson 	u32 scrubval = 0;
1726ba5dcdcSBorislav Petkov 	int status = -1, i;
1732bc65418SDoug Thompson 
1748d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, K8_SCRCTRL, &scrubval);
1752bc65418SDoug Thompson 
1762bc65418SDoug Thompson 	scrubval = scrubval & 0x001F;
1772bc65418SDoug Thompson 
17824f9a7feSBorislav Petkov 	amd64_debug("pci-read, sdram scrub control value: %d\n", scrubval);
1792bc65418SDoug Thompson 
180926311fdSRoel Kluin 	for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
1812bc65418SDoug Thompson 		if (scrubrates[i].scrubval == scrubval) {
1822bc65418SDoug Thompson 			*bw = scrubrates[i].bandwidth;
1832bc65418SDoug Thompson 			status = 0;
1842bc65418SDoug Thompson 			break;
1852bc65418SDoug Thompson 		}
1862bc65418SDoug Thompson 	}
1872bc65418SDoug Thompson 
1882bc65418SDoug Thompson 	return status;
1892bc65418SDoug Thompson }
1902bc65418SDoug Thompson 
1916775763aSDoug Thompson /* Map from a CSROW entry to the mask entry that operates on it */
1926775763aSDoug Thompson static inline u32 amd64_map_to_dcs_mask(struct amd64_pvt *pvt, int csrow)
1936775763aSDoug Thompson {
1941433eb99SBorislav Petkov 	if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_F)
1959d858bb1SBorislav Petkov 		return csrow;
1969d858bb1SBorislav Petkov 	else
1979d858bb1SBorislav Petkov 		return csrow >> 1;
1986775763aSDoug Thompson }
1996775763aSDoug Thompson 
2006775763aSDoug Thompson /* return the 'base' address the i'th CS entry of the 'dct' DRAM controller */
2016775763aSDoug Thompson static u32 amd64_get_dct_base(struct amd64_pvt *pvt, int dct, int csrow)
2026775763aSDoug Thompson {
2036775763aSDoug Thompson 	if (dct == 0)
2046775763aSDoug Thompson 		return pvt->dcsb0[csrow];
2056775763aSDoug Thompson 	else
2066775763aSDoug Thompson 		return pvt->dcsb1[csrow];
2076775763aSDoug Thompson }
2086775763aSDoug Thompson 
2096775763aSDoug Thompson /*
2106775763aSDoug Thompson  * Return the 'mask' address the i'th CS entry. This function is needed because
2116775763aSDoug Thompson  * there number of DCSM registers on Rev E and prior vs Rev F and later is
2126775763aSDoug Thompson  * different.
2136775763aSDoug Thompson  */
2146775763aSDoug Thompson static u32 amd64_get_dct_mask(struct amd64_pvt *pvt, int dct, int csrow)
2156775763aSDoug Thompson {
2166775763aSDoug Thompson 	if (dct == 0)
2176775763aSDoug Thompson 		return pvt->dcsm0[amd64_map_to_dcs_mask(pvt, csrow)];
2186775763aSDoug Thompson 	else
2196775763aSDoug Thompson 		return pvt->dcsm1[amd64_map_to_dcs_mask(pvt, csrow)];
2206775763aSDoug Thompson }
2216775763aSDoug Thompson 
2226775763aSDoug Thompson 
2236775763aSDoug Thompson /*
2246775763aSDoug Thompson  * In *base and *limit, pass back the full 40-bit base and limit physical
2256775763aSDoug Thompson  * addresses for the node given by node_id.  This information is obtained from
2266775763aSDoug Thompson  * DRAM Base (section 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers. The
2276775763aSDoug Thompson  * base and limit addresses are of type SysAddr, as defined at the start of
2286775763aSDoug Thompson  * section 3.4.4 (p. 70).  They are the lowest and highest physical addresses
2296775763aSDoug Thompson  * in the address range they represent.
2306775763aSDoug Thompson  */
2316775763aSDoug Thompson static void amd64_get_base_and_limit(struct amd64_pvt *pvt, int node_id,
2326775763aSDoug Thompson 			       u64 *base, u64 *limit)
2336775763aSDoug Thompson {
2346775763aSDoug Thompson 	*base = pvt->dram_base[node_id];
2356775763aSDoug Thompson 	*limit = pvt->dram_limit[node_id];
2366775763aSDoug Thompson }
2376775763aSDoug Thompson 
2386775763aSDoug Thompson /*
2396775763aSDoug Thompson  * Return 1 if the SysAddr given by sys_addr matches the base/limit associated
2406775763aSDoug Thompson  * with node_id
2416775763aSDoug Thompson  */
2426775763aSDoug Thompson static int amd64_base_limit_match(struct amd64_pvt *pvt,
2436775763aSDoug Thompson 					u64 sys_addr, int node_id)
2446775763aSDoug Thompson {
2456775763aSDoug Thompson 	u64 base, limit, addr;
2466775763aSDoug Thompson 
2476775763aSDoug Thompson 	amd64_get_base_and_limit(pvt, node_id, &base, &limit);
2486775763aSDoug Thompson 
2496775763aSDoug Thompson 	/* The K8 treats this as a 40-bit value.  However, bits 63-40 will be
2506775763aSDoug Thompson 	 * all ones if the most significant implemented address bit is 1.
2516775763aSDoug Thompson 	 * Here we discard bits 63-40.  See section 3.4.2 of AMD publication
2526775763aSDoug Thompson 	 * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1
2536775763aSDoug Thompson 	 * Application Programming.
2546775763aSDoug Thompson 	 */
2556775763aSDoug Thompson 	addr = sys_addr & 0x000000ffffffffffull;
2566775763aSDoug Thompson 
2576775763aSDoug Thompson 	return (addr >= base) && (addr <= limit);
2586775763aSDoug Thompson }
2596775763aSDoug Thompson 
2606775763aSDoug Thompson /*
2616775763aSDoug Thompson  * Attempt to map a SysAddr to a node. On success, return a pointer to the
2626775763aSDoug Thompson  * mem_ctl_info structure for the node that the SysAddr maps to.
2636775763aSDoug Thompson  *
2646775763aSDoug Thompson  * On failure, return NULL.
2656775763aSDoug Thompson  */
2666775763aSDoug Thompson static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci,
2676775763aSDoug Thompson 						u64 sys_addr)
2686775763aSDoug Thompson {
2696775763aSDoug Thompson 	struct amd64_pvt *pvt;
2706775763aSDoug Thompson 	int node_id;
2716775763aSDoug Thompson 	u32 intlv_en, bits;
2726775763aSDoug Thompson 
2736775763aSDoug Thompson 	/*
2746775763aSDoug Thompson 	 * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section
2756775763aSDoug Thompson 	 * 3.4.4.2) registers to map the SysAddr to a node ID.
2766775763aSDoug Thompson 	 */
2776775763aSDoug Thompson 	pvt = mci->pvt_info;
2786775763aSDoug Thompson 
2796775763aSDoug Thompson 	/*
2806775763aSDoug Thompson 	 * The value of this field should be the same for all DRAM Base
2816775763aSDoug Thompson 	 * registers.  Therefore we arbitrarily choose to read it from the
2826775763aSDoug Thompson 	 * register for node 0.
2836775763aSDoug Thompson 	 */
2846775763aSDoug Thompson 	intlv_en = pvt->dram_IntlvEn[0];
2856775763aSDoug Thompson 
2866775763aSDoug Thompson 	if (intlv_en == 0) {
2878edc5445SBorislav Petkov 		for (node_id = 0; node_id < DRAM_REG_COUNT; node_id++) {
2886775763aSDoug Thompson 			if (amd64_base_limit_match(pvt, sys_addr, node_id))
2896775763aSDoug Thompson 				goto found;
2906775763aSDoug Thompson 		}
2918edc5445SBorislav Petkov 		goto err_no_match;
2928edc5445SBorislav Petkov 	}
2936775763aSDoug Thompson 
29472f158feSBorislav Petkov 	if (unlikely((intlv_en != 0x01) &&
29572f158feSBorislav Petkov 		     (intlv_en != 0x03) &&
29672f158feSBorislav Petkov 		     (intlv_en != 0x07))) {
29724f9a7feSBorislav Petkov 		amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en);
2986775763aSDoug Thompson 		return NULL;
2996775763aSDoug Thompson 	}
3006775763aSDoug Thompson 
3016775763aSDoug Thompson 	bits = (((u32) sys_addr) >> 12) & intlv_en;
3026775763aSDoug Thompson 
3036775763aSDoug Thompson 	for (node_id = 0; ; ) {
3048edc5445SBorislav Petkov 		if ((pvt->dram_IntlvSel[node_id] & intlv_en) == bits)
3056775763aSDoug Thompson 			break;	/* intlv_sel field matches */
3066775763aSDoug Thompson 
3076775763aSDoug Thompson 		if (++node_id >= DRAM_REG_COUNT)
3086775763aSDoug Thompson 			goto err_no_match;
3096775763aSDoug Thompson 	}
3106775763aSDoug Thompson 
3116775763aSDoug Thompson 	/* sanity test for sys_addr */
3126775763aSDoug Thompson 	if (unlikely(!amd64_base_limit_match(pvt, sys_addr, node_id))) {
31324f9a7feSBorislav Petkov 		amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address"
31424f9a7feSBorislav Petkov 			   "range for node %d with node interleaving enabled.\n",
3158edc5445SBorislav Petkov 			   __func__, sys_addr, node_id);
3166775763aSDoug Thompson 		return NULL;
3176775763aSDoug Thompson 	}
3186775763aSDoug Thompson 
3196775763aSDoug Thompson found:
3206775763aSDoug Thompson 	return edac_mc_find(node_id);
3216775763aSDoug Thompson 
3226775763aSDoug Thompson err_no_match:
3236775763aSDoug Thompson 	debugf2("sys_addr 0x%lx doesn't match any node\n",
3246775763aSDoug Thompson 		(unsigned long)sys_addr);
3256775763aSDoug Thompson 
3266775763aSDoug Thompson 	return NULL;
3276775763aSDoug Thompson }
328e2ce7255SDoug Thompson 
329e2ce7255SDoug Thompson /*
330e2ce7255SDoug Thompson  * Extract the DRAM CS base address from selected csrow register.
331e2ce7255SDoug Thompson  */
332e2ce7255SDoug Thompson static u64 base_from_dct_base(struct amd64_pvt *pvt, int csrow)
333e2ce7255SDoug Thompson {
334e2ce7255SDoug Thompson 	return ((u64) (amd64_get_dct_base(pvt, 0, csrow) & pvt->dcsb_base)) <<
335e2ce7255SDoug Thompson 				pvt->dcs_shift;
336e2ce7255SDoug Thompson }
337e2ce7255SDoug Thompson 
338e2ce7255SDoug Thompson /*
339e2ce7255SDoug Thompson  * Extract the mask from the dcsb0[csrow] entry in a CPU revision-specific way.
340e2ce7255SDoug Thompson  */
341e2ce7255SDoug Thompson static u64 mask_from_dct_mask(struct amd64_pvt *pvt, int csrow)
342e2ce7255SDoug Thompson {
343e2ce7255SDoug Thompson 	u64 dcsm_bits, other_bits;
344e2ce7255SDoug Thompson 	u64 mask;
345e2ce7255SDoug Thompson 
346e2ce7255SDoug Thompson 	/* Extract bits from DRAM CS Mask. */
347e2ce7255SDoug Thompson 	dcsm_bits = amd64_get_dct_mask(pvt, 0, csrow) & pvt->dcsm_mask;
348e2ce7255SDoug Thompson 
349e2ce7255SDoug Thompson 	other_bits = pvt->dcsm_mask;
350e2ce7255SDoug Thompson 	other_bits = ~(other_bits << pvt->dcs_shift);
351e2ce7255SDoug Thompson 
352e2ce7255SDoug Thompson 	/*
353e2ce7255SDoug Thompson 	 * The extracted bits from DCSM belong in the spaces represented by
354e2ce7255SDoug Thompson 	 * the cleared bits in other_bits.
355e2ce7255SDoug Thompson 	 */
356e2ce7255SDoug Thompson 	mask = (dcsm_bits << pvt->dcs_shift) | other_bits;
357e2ce7255SDoug Thompson 
358e2ce7255SDoug Thompson 	return mask;
359e2ce7255SDoug Thompson }
360e2ce7255SDoug Thompson 
361e2ce7255SDoug Thompson /*
362e2ce7255SDoug Thompson  * @input_addr is an InputAddr associated with the node given by mci. Return the
363e2ce7255SDoug Thompson  * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr).
364e2ce7255SDoug Thompson  */
365e2ce7255SDoug Thompson static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr)
366e2ce7255SDoug Thompson {
367e2ce7255SDoug Thompson 	struct amd64_pvt *pvt;
368e2ce7255SDoug Thompson 	int csrow;
369e2ce7255SDoug Thompson 	u64 base, mask;
370e2ce7255SDoug Thompson 
371e2ce7255SDoug Thompson 	pvt = mci->pvt_info;
372e2ce7255SDoug Thompson 
373e2ce7255SDoug Thompson 	/*
374e2ce7255SDoug Thompson 	 * Here we use the DRAM CS Base and DRAM CS Mask registers. For each CS
375e2ce7255SDoug Thompson 	 * base/mask register pair, test the condition shown near the start of
376e2ce7255SDoug Thompson 	 * section 3.5.4 (p. 84, BKDG #26094, K8, revA-E).
377e2ce7255SDoug Thompson 	 */
3789d858bb1SBorislav Petkov 	for (csrow = 0; csrow < pvt->cs_count; csrow++) {
379e2ce7255SDoug Thompson 
380e2ce7255SDoug Thompson 		/* This DRAM chip select is disabled on this node */
381e2ce7255SDoug Thompson 		if ((pvt->dcsb0[csrow] & K8_DCSB_CS_ENABLE) == 0)
382e2ce7255SDoug Thompson 			continue;
383e2ce7255SDoug Thompson 
384e2ce7255SDoug Thompson 		base = base_from_dct_base(pvt, csrow);
385e2ce7255SDoug Thompson 		mask = ~mask_from_dct_mask(pvt, csrow);
386e2ce7255SDoug Thompson 
387e2ce7255SDoug Thompson 		if ((input_addr & mask) == (base & mask)) {
388e2ce7255SDoug Thompson 			debugf2("InputAddr 0x%lx matches csrow %d (node %d)\n",
389e2ce7255SDoug Thompson 				(unsigned long)input_addr, csrow,
390e2ce7255SDoug Thompson 				pvt->mc_node_id);
391e2ce7255SDoug Thompson 
392e2ce7255SDoug Thompson 			return csrow;
393e2ce7255SDoug Thompson 		}
394e2ce7255SDoug Thompson 	}
395e2ce7255SDoug Thompson 
396e2ce7255SDoug Thompson 	debugf2("no matching csrow for InputAddr 0x%lx (MC node %d)\n",
397e2ce7255SDoug Thompson 		(unsigned long)input_addr, pvt->mc_node_id);
398e2ce7255SDoug Thompson 
399e2ce7255SDoug Thompson 	return -1;
400e2ce7255SDoug Thompson }
401e2ce7255SDoug Thompson 
402e2ce7255SDoug Thompson /*
403e2ce7255SDoug Thompson  * Return the base value defined by the DRAM Base register for the node
404e2ce7255SDoug Thompson  * represented by mci.  This function returns the full 40-bit value despite the
405e2ce7255SDoug Thompson  * fact that the register only stores bits 39-24 of the value. See section
406e2ce7255SDoug Thompson  * 3.4.4.1 (BKDG #26094, K8, revA-E)
407e2ce7255SDoug Thompson  */
408e2ce7255SDoug Thompson static inline u64 get_dram_base(struct mem_ctl_info *mci)
409e2ce7255SDoug Thompson {
410e2ce7255SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
411e2ce7255SDoug Thompson 
412e2ce7255SDoug Thompson 	return pvt->dram_base[pvt->mc_node_id];
413e2ce7255SDoug Thompson }
414e2ce7255SDoug Thompson 
415e2ce7255SDoug Thompson /*
416e2ce7255SDoug Thompson  * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094)
417e2ce7255SDoug Thompson  * for the node represented by mci. Info is passed back in *hole_base,
418e2ce7255SDoug Thompson  * *hole_offset, and *hole_size.  Function returns 0 if info is valid or 1 if
419e2ce7255SDoug Thompson  * info is invalid. Info may be invalid for either of the following reasons:
420e2ce7255SDoug Thompson  *
421e2ce7255SDoug Thompson  * - The revision of the node is not E or greater.  In this case, the DRAM Hole
422e2ce7255SDoug Thompson  *   Address Register does not exist.
423e2ce7255SDoug Thompson  *
424e2ce7255SDoug Thompson  * - The DramHoleValid bit is cleared in the DRAM Hole Address Register,
425e2ce7255SDoug Thompson  *   indicating that its contents are not valid.
426e2ce7255SDoug Thompson  *
427e2ce7255SDoug Thompson  * The values passed back in *hole_base, *hole_offset, and *hole_size are
428e2ce7255SDoug Thompson  * complete 32-bit values despite the fact that the bitfields in the DHAR
429e2ce7255SDoug Thompson  * only represent bits 31-24 of the base and offset values.
430e2ce7255SDoug Thompson  */
431e2ce7255SDoug Thompson int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
432e2ce7255SDoug Thompson 			     u64 *hole_offset, u64 *hole_size)
433e2ce7255SDoug Thompson {
434e2ce7255SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
435e2ce7255SDoug Thompson 	u64 base;
436e2ce7255SDoug Thompson 
437e2ce7255SDoug Thompson 	/* only revE and later have the DRAM Hole Address Register */
4381433eb99SBorislav Petkov 	if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_E) {
439e2ce7255SDoug Thompson 		debugf1("  revision %d for node %d does not support DHAR\n",
440e2ce7255SDoug Thompson 			pvt->ext_model, pvt->mc_node_id);
441e2ce7255SDoug Thompson 		return 1;
442e2ce7255SDoug Thompson 	}
443e2ce7255SDoug Thompson 
444e2ce7255SDoug Thompson 	/* only valid for Fam10h */
445e2ce7255SDoug Thompson 	if (boot_cpu_data.x86 == 0x10 &&
446e2ce7255SDoug Thompson 	    (pvt->dhar & F10_DRAM_MEM_HOIST_VALID) == 0) {
447e2ce7255SDoug Thompson 		debugf1("  Dram Memory Hoisting is DISABLED on this system\n");
448e2ce7255SDoug Thompson 		return 1;
449e2ce7255SDoug Thompson 	}
450e2ce7255SDoug Thompson 
451e2ce7255SDoug Thompson 	if ((pvt->dhar & DHAR_VALID) == 0) {
452e2ce7255SDoug Thompson 		debugf1("  Dram Memory Hoisting is DISABLED on this node %d\n",
453e2ce7255SDoug Thompson 			pvt->mc_node_id);
454e2ce7255SDoug Thompson 		return 1;
455e2ce7255SDoug Thompson 	}
456e2ce7255SDoug Thompson 
457e2ce7255SDoug Thompson 	/* This node has Memory Hoisting */
458e2ce7255SDoug Thompson 
459e2ce7255SDoug Thompson 	/* +------------------+--------------------+--------------------+-----
460e2ce7255SDoug Thompson 	 * | memory           | DRAM hole          | relocated          |
461e2ce7255SDoug Thompson 	 * | [0, (x - 1)]     | [x, 0xffffffff]    | addresses from     |
462e2ce7255SDoug Thompson 	 * |                  |                    | DRAM hole          |
463e2ce7255SDoug Thompson 	 * |                  |                    | [0x100000000,      |
464e2ce7255SDoug Thompson 	 * |                  |                    |  (0x100000000+     |
465e2ce7255SDoug Thompson 	 * |                  |                    |   (0xffffffff-x))] |
466e2ce7255SDoug Thompson 	 * +------------------+--------------------+--------------------+-----
467e2ce7255SDoug Thompson 	 *
468e2ce7255SDoug Thompson 	 * Above is a diagram of physical memory showing the DRAM hole and the
469e2ce7255SDoug Thompson 	 * relocated addresses from the DRAM hole.  As shown, the DRAM hole
470e2ce7255SDoug Thompson 	 * starts at address x (the base address) and extends through address
471e2ce7255SDoug Thompson 	 * 0xffffffff.  The DRAM Hole Address Register (DHAR) relocates the
472e2ce7255SDoug Thompson 	 * addresses in the hole so that they start at 0x100000000.
473e2ce7255SDoug Thompson 	 */
474e2ce7255SDoug Thompson 
475e2ce7255SDoug Thompson 	base = dhar_base(pvt->dhar);
476e2ce7255SDoug Thompson 
477e2ce7255SDoug Thompson 	*hole_base = base;
478e2ce7255SDoug Thompson 	*hole_size = (0x1ull << 32) - base;
479e2ce7255SDoug Thompson 
480e2ce7255SDoug Thompson 	if (boot_cpu_data.x86 > 0xf)
481e2ce7255SDoug Thompson 		*hole_offset = f10_dhar_offset(pvt->dhar);
482e2ce7255SDoug Thompson 	else
483e2ce7255SDoug Thompson 		*hole_offset = k8_dhar_offset(pvt->dhar);
484e2ce7255SDoug Thompson 
485e2ce7255SDoug Thompson 	debugf1("  DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
486e2ce7255SDoug Thompson 		pvt->mc_node_id, (unsigned long)*hole_base,
487e2ce7255SDoug Thompson 		(unsigned long)*hole_offset, (unsigned long)*hole_size);
488e2ce7255SDoug Thompson 
489e2ce7255SDoug Thompson 	return 0;
490e2ce7255SDoug Thompson }
491e2ce7255SDoug Thompson EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info);
492e2ce7255SDoug Thompson 
49393c2df58SDoug Thompson /*
49493c2df58SDoug Thompson  * Return the DramAddr that the SysAddr given by @sys_addr maps to.  It is
49593c2df58SDoug Thompson  * assumed that sys_addr maps to the node given by mci.
49693c2df58SDoug Thompson  *
49793c2df58SDoug Thompson  * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section
49893c2df58SDoug Thompson  * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a
49993c2df58SDoug Thompson  * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled,
50093c2df58SDoug Thompson  * then it is also involved in translating a SysAddr to a DramAddr. Sections
50193c2df58SDoug Thompson  * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting.
50293c2df58SDoug Thompson  * These parts of the documentation are unclear. I interpret them as follows:
50393c2df58SDoug Thompson  *
50493c2df58SDoug Thompson  * When node n receives a SysAddr, it processes the SysAddr as follows:
50593c2df58SDoug Thompson  *
50693c2df58SDoug Thompson  * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM
50793c2df58SDoug Thompson  *    Limit registers for node n. If the SysAddr is not within the range
50893c2df58SDoug Thompson  *    specified by the base and limit values, then node n ignores the Sysaddr
50993c2df58SDoug Thompson  *    (since it does not map to node n). Otherwise continue to step 2 below.
51093c2df58SDoug Thompson  *
51193c2df58SDoug Thompson  * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is
51293c2df58SDoug Thompson  *    disabled so skip to step 3 below. Otherwise see if the SysAddr is within
51393c2df58SDoug Thompson  *    the range of relocated addresses (starting at 0x100000000) from the DRAM
51493c2df58SDoug Thompson  *    hole. If not, skip to step 3 below. Else get the value of the
51593c2df58SDoug Thompson  *    DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the
51693c2df58SDoug Thompson  *    offset defined by this value from the SysAddr.
51793c2df58SDoug Thompson  *
51893c2df58SDoug Thompson  * 3. Obtain the base address for node n from the DRAMBase field of the DRAM
51993c2df58SDoug Thompson  *    Base register for node n. To obtain the DramAddr, subtract the base
52093c2df58SDoug Thompson  *    address from the SysAddr, as shown near the start of section 3.4.4 (p.70).
52193c2df58SDoug Thompson  */
52293c2df58SDoug Thompson static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
52393c2df58SDoug Thompson {
52493c2df58SDoug Thompson 	u64 dram_base, hole_base, hole_offset, hole_size, dram_addr;
52593c2df58SDoug Thompson 	int ret = 0;
52693c2df58SDoug Thompson 
52793c2df58SDoug Thompson 	dram_base = get_dram_base(mci);
52893c2df58SDoug Thompson 
52993c2df58SDoug Thompson 	ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
53093c2df58SDoug Thompson 				      &hole_size);
53193c2df58SDoug Thompson 	if (!ret) {
53293c2df58SDoug Thompson 		if ((sys_addr >= (1ull << 32)) &&
53393c2df58SDoug Thompson 		    (sys_addr < ((1ull << 32) + hole_size))) {
53493c2df58SDoug Thompson 			/* use DHAR to translate SysAddr to DramAddr */
53593c2df58SDoug Thompson 			dram_addr = sys_addr - hole_offset;
53693c2df58SDoug Thompson 
53793c2df58SDoug Thompson 			debugf2("using DHAR to translate SysAddr 0x%lx to "
53893c2df58SDoug Thompson 				"DramAddr 0x%lx\n",
53993c2df58SDoug Thompson 				(unsigned long)sys_addr,
54093c2df58SDoug Thompson 				(unsigned long)dram_addr);
54193c2df58SDoug Thompson 
54293c2df58SDoug Thompson 			return dram_addr;
54393c2df58SDoug Thompson 		}
54493c2df58SDoug Thompson 	}
54593c2df58SDoug Thompson 
54693c2df58SDoug Thompson 	/*
54793c2df58SDoug Thompson 	 * Translate the SysAddr to a DramAddr as shown near the start of
54893c2df58SDoug Thompson 	 * section 3.4.4 (p. 70).  Although sys_addr is a 64-bit value, the k8
54993c2df58SDoug Thompson 	 * only deals with 40-bit values.  Therefore we discard bits 63-40 of
55093c2df58SDoug Thompson 	 * sys_addr below.  If bit 39 of sys_addr is 1 then the bits we
55193c2df58SDoug Thompson 	 * discard are all 1s.  Otherwise the bits we discard are all 0s.  See
55293c2df58SDoug Thompson 	 * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture
55393c2df58SDoug Thompson 	 * Programmer's Manual Volume 1 Application Programming.
55493c2df58SDoug Thompson 	 */
55593c2df58SDoug Thompson 	dram_addr = (sys_addr & 0xffffffffffull) - dram_base;
55693c2df58SDoug Thompson 
55793c2df58SDoug Thompson 	debugf2("using DRAM Base register to translate SysAddr 0x%lx to "
55893c2df58SDoug Thompson 		"DramAddr 0x%lx\n", (unsigned long)sys_addr,
55993c2df58SDoug Thompson 		(unsigned long)dram_addr);
56093c2df58SDoug Thompson 	return dram_addr;
56193c2df58SDoug Thompson }
56293c2df58SDoug Thompson 
56393c2df58SDoug Thompson /*
56493c2df58SDoug Thompson  * @intlv_en is the value of the IntlvEn field from a DRAM Base register
56593c2df58SDoug Thompson  * (section 3.4.4.1).  Return the number of bits from a SysAddr that are used
56693c2df58SDoug Thompson  * for node interleaving.
56793c2df58SDoug Thompson  */
56893c2df58SDoug Thompson static int num_node_interleave_bits(unsigned intlv_en)
56993c2df58SDoug Thompson {
57093c2df58SDoug Thompson 	static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 };
57193c2df58SDoug Thompson 	int n;
57293c2df58SDoug Thompson 
57393c2df58SDoug Thompson 	BUG_ON(intlv_en > 7);
57493c2df58SDoug Thompson 	n = intlv_shift_table[intlv_en];
57593c2df58SDoug Thompson 	return n;
57693c2df58SDoug Thompson }
57793c2df58SDoug Thompson 
57893c2df58SDoug Thompson /* Translate the DramAddr given by @dram_addr to an InputAddr. */
57993c2df58SDoug Thompson static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
58093c2df58SDoug Thompson {
58193c2df58SDoug Thompson 	struct amd64_pvt *pvt;
58293c2df58SDoug Thompson 	int intlv_shift;
58393c2df58SDoug Thompson 	u64 input_addr;
58493c2df58SDoug Thompson 
58593c2df58SDoug Thompson 	pvt = mci->pvt_info;
58693c2df58SDoug Thompson 
58793c2df58SDoug Thompson 	/*
58893c2df58SDoug Thompson 	 * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
58993c2df58SDoug Thompson 	 * concerning translating a DramAddr to an InputAddr.
59093c2df58SDoug Thompson 	 */
59193c2df58SDoug Thompson 	intlv_shift = num_node_interleave_bits(pvt->dram_IntlvEn[0]);
59293c2df58SDoug Thompson 	input_addr = ((dram_addr >> intlv_shift) & 0xffffff000ull) +
59393c2df58SDoug Thompson 	    (dram_addr & 0xfff);
59493c2df58SDoug Thompson 
59593c2df58SDoug Thompson 	debugf2("  Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
59693c2df58SDoug Thompson 		intlv_shift, (unsigned long)dram_addr,
59793c2df58SDoug Thompson 		(unsigned long)input_addr);
59893c2df58SDoug Thompson 
59993c2df58SDoug Thompson 	return input_addr;
60093c2df58SDoug Thompson }
60193c2df58SDoug Thompson 
60293c2df58SDoug Thompson /*
60393c2df58SDoug Thompson  * Translate the SysAddr represented by @sys_addr to an InputAddr.  It is
60493c2df58SDoug Thompson  * assumed that @sys_addr maps to the node given by mci.
60593c2df58SDoug Thompson  */
60693c2df58SDoug Thompson static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
60793c2df58SDoug Thompson {
60893c2df58SDoug Thompson 	u64 input_addr;
60993c2df58SDoug Thompson 
61093c2df58SDoug Thompson 	input_addr =
61193c2df58SDoug Thompson 	    dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
61293c2df58SDoug Thompson 
61393c2df58SDoug Thompson 	debugf2("SysAdddr 0x%lx translates to InputAddr 0x%lx\n",
61493c2df58SDoug Thompson 		(unsigned long)sys_addr, (unsigned long)input_addr);
61593c2df58SDoug Thompson 
61693c2df58SDoug Thompson 	return input_addr;
61793c2df58SDoug Thompson }
61893c2df58SDoug Thompson 
61993c2df58SDoug Thompson 
62093c2df58SDoug Thompson /*
62193c2df58SDoug Thompson  * @input_addr is an InputAddr associated with the node represented by mci.
62293c2df58SDoug Thompson  * Translate @input_addr to a DramAddr and return the result.
62393c2df58SDoug Thompson  */
62493c2df58SDoug Thompson static u64 input_addr_to_dram_addr(struct mem_ctl_info *mci, u64 input_addr)
62593c2df58SDoug Thompson {
62693c2df58SDoug Thompson 	struct amd64_pvt *pvt;
62793c2df58SDoug Thompson 	int node_id, intlv_shift;
62893c2df58SDoug Thompson 	u64 bits, dram_addr;
62993c2df58SDoug Thompson 	u32 intlv_sel;
63093c2df58SDoug Thompson 
63193c2df58SDoug Thompson 	/*
63293c2df58SDoug Thompson 	 * Near the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
63393c2df58SDoug Thompson 	 * shows how to translate a DramAddr to an InputAddr. Here we reverse
63493c2df58SDoug Thompson 	 * this procedure. When translating from a DramAddr to an InputAddr, the
63593c2df58SDoug Thompson 	 * bits used for node interleaving are discarded.  Here we recover these
63693c2df58SDoug Thompson 	 * bits from the IntlvSel field of the DRAM Limit register (section
63793c2df58SDoug Thompson 	 * 3.4.4.2) for the node that input_addr is associated with.
63893c2df58SDoug Thompson 	 */
63993c2df58SDoug Thompson 	pvt = mci->pvt_info;
64093c2df58SDoug Thompson 	node_id = pvt->mc_node_id;
64193c2df58SDoug Thompson 	BUG_ON((node_id < 0) || (node_id > 7));
64293c2df58SDoug Thompson 
64393c2df58SDoug Thompson 	intlv_shift = num_node_interleave_bits(pvt->dram_IntlvEn[0]);
64493c2df58SDoug Thompson 
64593c2df58SDoug Thompson 	if (intlv_shift == 0) {
64693c2df58SDoug Thompson 		debugf1("    InputAddr 0x%lx translates to DramAddr of "
64793c2df58SDoug Thompson 			"same value\n",	(unsigned long)input_addr);
64893c2df58SDoug Thompson 
64993c2df58SDoug Thompson 		return input_addr;
65093c2df58SDoug Thompson 	}
65193c2df58SDoug Thompson 
65293c2df58SDoug Thompson 	bits = ((input_addr & 0xffffff000ull) << intlv_shift) +
65393c2df58SDoug Thompson 	    (input_addr & 0xfff);
65493c2df58SDoug Thompson 
65593c2df58SDoug Thompson 	intlv_sel = pvt->dram_IntlvSel[node_id] & ((1 << intlv_shift) - 1);
65693c2df58SDoug Thompson 	dram_addr = bits + (intlv_sel << 12);
65793c2df58SDoug Thompson 
65893c2df58SDoug Thompson 	debugf1("InputAddr 0x%lx translates to DramAddr 0x%lx "
65993c2df58SDoug Thompson 		"(%d node interleave bits)\n", (unsigned long)input_addr,
66093c2df58SDoug Thompson 		(unsigned long)dram_addr, intlv_shift);
66193c2df58SDoug Thompson 
66293c2df58SDoug Thompson 	return dram_addr;
66393c2df58SDoug Thompson }
66493c2df58SDoug Thompson 
66593c2df58SDoug Thompson /*
66693c2df58SDoug Thompson  * @dram_addr is a DramAddr that maps to the node represented by mci. Convert
66793c2df58SDoug Thompson  * @dram_addr to a SysAddr.
66893c2df58SDoug Thompson  */
66993c2df58SDoug Thompson static u64 dram_addr_to_sys_addr(struct mem_ctl_info *mci, u64 dram_addr)
67093c2df58SDoug Thompson {
67193c2df58SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
67293c2df58SDoug Thompson 	u64 hole_base, hole_offset, hole_size, base, limit, sys_addr;
67393c2df58SDoug Thompson 	int ret = 0;
67493c2df58SDoug Thompson 
67593c2df58SDoug Thompson 	ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
67693c2df58SDoug Thompson 				      &hole_size);
67793c2df58SDoug Thompson 	if (!ret) {
67893c2df58SDoug Thompson 		if ((dram_addr >= hole_base) &&
67993c2df58SDoug Thompson 		    (dram_addr < (hole_base + hole_size))) {
68093c2df58SDoug Thompson 			sys_addr = dram_addr + hole_offset;
68193c2df58SDoug Thompson 
68293c2df58SDoug Thompson 			debugf1("using DHAR to translate DramAddr 0x%lx to "
68393c2df58SDoug Thompson 				"SysAddr 0x%lx\n", (unsigned long)dram_addr,
68493c2df58SDoug Thompson 				(unsigned long)sys_addr);
68593c2df58SDoug Thompson 
68693c2df58SDoug Thompson 			return sys_addr;
68793c2df58SDoug Thompson 		}
68893c2df58SDoug Thompson 	}
68993c2df58SDoug Thompson 
69093c2df58SDoug Thompson 	amd64_get_base_and_limit(pvt, pvt->mc_node_id, &base, &limit);
69193c2df58SDoug Thompson 	sys_addr = dram_addr + base;
69293c2df58SDoug Thompson 
69393c2df58SDoug Thompson 	/*
69493c2df58SDoug Thompson 	 * The sys_addr we have computed up to this point is a 40-bit value
69593c2df58SDoug Thompson 	 * because the k8 deals with 40-bit values.  However, the value we are
69693c2df58SDoug Thompson 	 * supposed to return is a full 64-bit physical address.  The AMD
69793c2df58SDoug Thompson 	 * x86-64 architecture specifies that the most significant implemented
69893c2df58SDoug Thompson 	 * address bit through bit 63 of a physical address must be either all
69993c2df58SDoug Thompson 	 * 0s or all 1s.  Therefore we sign-extend the 40-bit sys_addr to a
70093c2df58SDoug Thompson 	 * 64-bit value below.  See section 3.4.2 of AMD publication 24592:
70193c2df58SDoug Thompson 	 * AMD x86-64 Architecture Programmer's Manual Volume 1 Application
70293c2df58SDoug Thompson 	 * Programming.
70393c2df58SDoug Thompson 	 */
70493c2df58SDoug Thompson 	sys_addr |= ~((sys_addr & (1ull << 39)) - 1);
70593c2df58SDoug Thompson 
70693c2df58SDoug Thompson 	debugf1("    Node %d, DramAddr 0x%lx to SysAddr 0x%lx\n",
70793c2df58SDoug Thompson 		pvt->mc_node_id, (unsigned long)dram_addr,
70893c2df58SDoug Thompson 		(unsigned long)sys_addr);
70993c2df58SDoug Thompson 
71093c2df58SDoug Thompson 	return sys_addr;
71193c2df58SDoug Thompson }
71293c2df58SDoug Thompson 
71393c2df58SDoug Thompson /*
71493c2df58SDoug Thompson  * @input_addr is an InputAddr associated with the node given by mci. Translate
71593c2df58SDoug Thompson  * @input_addr to a SysAddr.
71693c2df58SDoug Thompson  */
71793c2df58SDoug Thompson static inline u64 input_addr_to_sys_addr(struct mem_ctl_info *mci,
71893c2df58SDoug Thompson 					 u64 input_addr)
71993c2df58SDoug Thompson {
72093c2df58SDoug Thompson 	return dram_addr_to_sys_addr(mci,
72193c2df58SDoug Thompson 				     input_addr_to_dram_addr(mci, input_addr));
72293c2df58SDoug Thompson }
72393c2df58SDoug Thompson 
72493c2df58SDoug Thompson /*
72593c2df58SDoug Thompson  * Find the minimum and maximum InputAddr values that map to the given @csrow.
72693c2df58SDoug Thompson  * Pass back these values in *input_addr_min and *input_addr_max.
72793c2df58SDoug Thompson  */
72893c2df58SDoug Thompson static void find_csrow_limits(struct mem_ctl_info *mci, int csrow,
72993c2df58SDoug Thompson 			      u64 *input_addr_min, u64 *input_addr_max)
73093c2df58SDoug Thompson {
73193c2df58SDoug Thompson 	struct amd64_pvt *pvt;
73293c2df58SDoug Thompson 	u64 base, mask;
73393c2df58SDoug Thompson 
73493c2df58SDoug Thompson 	pvt = mci->pvt_info;
7359d858bb1SBorislav Petkov 	BUG_ON((csrow < 0) || (csrow >= pvt->cs_count));
73693c2df58SDoug Thompson 
73793c2df58SDoug Thompson 	base = base_from_dct_base(pvt, csrow);
73893c2df58SDoug Thompson 	mask = mask_from_dct_mask(pvt, csrow);
73993c2df58SDoug Thompson 
74093c2df58SDoug Thompson 	*input_addr_min = base & ~mask;
74193c2df58SDoug Thompson 	*input_addr_max = base | mask | pvt->dcs_mask_notused;
74293c2df58SDoug Thompson }
74393c2df58SDoug Thompson 
74493c2df58SDoug Thompson /* Map the Error address to a PAGE and PAGE OFFSET. */
74593c2df58SDoug Thompson static inline void error_address_to_page_and_offset(u64 error_address,
74693c2df58SDoug Thompson 						    u32 *page, u32 *offset)
74793c2df58SDoug Thompson {
74893c2df58SDoug Thompson 	*page = (u32) (error_address >> PAGE_SHIFT);
74993c2df58SDoug Thompson 	*offset = ((u32) error_address) & ~PAGE_MASK;
75093c2df58SDoug Thompson }
75193c2df58SDoug Thompson 
75293c2df58SDoug Thompson /*
75393c2df58SDoug Thompson  * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address
75493c2df58SDoug Thompson  * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers
75593c2df58SDoug Thompson  * of a node that detected an ECC memory error.  mci represents the node that
75693c2df58SDoug Thompson  * the error address maps to (possibly different from the node that detected
75793c2df58SDoug Thompson  * the error).  Return the number of the csrow that sys_addr maps to, or -1 on
75893c2df58SDoug Thompson  * error.
75993c2df58SDoug Thompson  */
76093c2df58SDoug Thompson static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
76193c2df58SDoug Thompson {
76293c2df58SDoug Thompson 	int csrow;
76393c2df58SDoug Thompson 
76493c2df58SDoug Thompson 	csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr));
76593c2df58SDoug Thompson 
76693c2df58SDoug Thompson 	if (csrow == -1)
76724f9a7feSBorislav Petkov 		amd64_mc_err(mci, "Failed to translate InputAddr to csrow for "
76893c2df58SDoug Thompson 				  "address 0x%lx\n", (unsigned long)sys_addr);
76993c2df58SDoug Thompson 	return csrow;
77093c2df58SDoug Thompson }
771e2ce7255SDoug Thompson 
772bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
7732da11654SDoug Thompson 
774ad6a32e9SBorislav Petkov static u16 extract_syndrome(struct err_regs *err)
775ad6a32e9SBorislav Petkov {
776ad6a32e9SBorislav Petkov 	return ((err->nbsh >> 15) & 0xff) | ((err->nbsl >> 16) & 0xff00);
777ad6a32e9SBorislav Petkov }
778ad6a32e9SBorislav Petkov 
7792da11654SDoug Thompson /*
7802da11654SDoug Thompson  * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
7812da11654SDoug Thompson  * are ECC capable.
7822da11654SDoug Thompson  */
7832da11654SDoug Thompson static enum edac_type amd64_determine_edac_cap(struct amd64_pvt *pvt)
7842da11654SDoug Thompson {
7852da11654SDoug Thompson 	int bit;
786584fcff4SBorislav Petkov 	enum dev_type edac_cap = EDAC_FLAG_NONE;
7872da11654SDoug Thompson 
7881433eb99SBorislav Petkov 	bit = (boot_cpu_data.x86 > 0xf || pvt->ext_model >= K8_REV_F)
7892da11654SDoug Thompson 		? 19
7902da11654SDoug Thompson 		: 17;
7912da11654SDoug Thompson 
792584fcff4SBorislav Petkov 	if (pvt->dclr0 & BIT(bit))
7932da11654SDoug Thompson 		edac_cap = EDAC_FLAG_SECDED;
7942da11654SDoug Thompson 
7952da11654SDoug Thompson 	return edac_cap;
7962da11654SDoug Thompson }
7972da11654SDoug Thompson 
7982da11654SDoug Thompson 
7998566c4dfSBorislav Petkov static void amd64_debug_display_dimm_sizes(int ctrl, struct amd64_pvt *pvt);
8002da11654SDoug Thompson 
80168798e17SBorislav Petkov static void amd64_dump_dramcfg_low(u32 dclr, int chan)
80268798e17SBorislav Petkov {
80368798e17SBorislav Petkov 	debugf1("F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
80468798e17SBorislav Petkov 
80568798e17SBorislav Petkov 	debugf1("  DIMM type: %sbuffered; all DIMMs support ECC: %s\n",
80668798e17SBorislav Petkov 		(dclr & BIT(16)) ?  "un" : "",
80768798e17SBorislav Petkov 		(dclr & BIT(19)) ? "yes" : "no");
80868798e17SBorislav Petkov 
80968798e17SBorislav Petkov 	debugf1("  PAR/ERR parity: %s\n",
81068798e17SBorislav Petkov 		(dclr & BIT(8)) ?  "enabled" : "disabled");
81168798e17SBorislav Petkov 
81268798e17SBorislav Petkov 	debugf1("  DCT 128bit mode width: %s\n",
81368798e17SBorislav Petkov 		(dclr & BIT(11)) ?  "128b" : "64b");
81468798e17SBorislav Petkov 
81568798e17SBorislav Petkov 	debugf1("  x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
81668798e17SBorislav Petkov 		(dclr & BIT(12)) ?  "yes" : "no",
81768798e17SBorislav Petkov 		(dclr & BIT(13)) ?  "yes" : "no",
81868798e17SBorislav Petkov 		(dclr & BIT(14)) ?  "yes" : "no",
81968798e17SBorislav Petkov 		(dclr & BIT(15)) ?  "yes" : "no");
82068798e17SBorislav Petkov }
82168798e17SBorislav Petkov 
8222da11654SDoug Thompson /* Display and decode various NB registers for debug purposes. */
8232da11654SDoug Thompson static void amd64_dump_misc_regs(struct amd64_pvt *pvt)
8242da11654SDoug Thompson {
8252da11654SDoug Thompson 	int ganged;
8262da11654SDoug Thompson 
82768798e17SBorislav Petkov 	debugf1("F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
8282da11654SDoug Thompson 
82968798e17SBorislav Petkov 	debugf1("  NB two channel DRAM capable: %s\n",
83068798e17SBorislav Petkov 		(pvt->nbcap & K8_NBCAP_DCT_DUAL) ? "yes" : "no");
83168798e17SBorislav Petkov 
83268798e17SBorislav Petkov 	debugf1("  ECC capable: %s, ChipKill ECC capable: %s\n",
83368798e17SBorislav Petkov 		(pvt->nbcap & K8_NBCAP_SECDED) ? "yes" : "no",
83468798e17SBorislav Petkov 		(pvt->nbcap & K8_NBCAP_CHIPKILL) ? "yes" : "no");
83568798e17SBorislav Petkov 
83668798e17SBorislav Petkov 	amd64_dump_dramcfg_low(pvt->dclr0, 0);
8372da11654SDoug Thompson 
8388de1d91eSBorislav Petkov 	debugf1("F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
8392da11654SDoug Thompson 
8408de1d91eSBorislav Petkov 	debugf1("F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, "
8418de1d91eSBorislav Petkov 			"offset: 0x%08x\n",
8428de1d91eSBorislav Petkov 			pvt->dhar,
8438de1d91eSBorislav Petkov 			dhar_base(pvt->dhar),
8448de1d91eSBorislav Petkov 			(boot_cpu_data.x86 == 0xf) ? k8_dhar_offset(pvt->dhar)
8458de1d91eSBorislav Petkov 						   : f10_dhar_offset(pvt->dhar));
8462da11654SDoug Thompson 
8478de1d91eSBorislav Petkov 	debugf1("  DramHoleValid: %s\n",
8488de1d91eSBorislav Petkov 		(pvt->dhar & DHAR_VALID) ? "yes" : "no");
8492da11654SDoug Thompson 
8502da11654SDoug Thompson 	/* everything below this point is Fam10h and above */
8518566c4dfSBorislav Petkov 	if (boot_cpu_data.x86 == 0xf) {
8528566c4dfSBorislav Petkov 		amd64_debug_display_dimm_sizes(0, pvt);
8532da11654SDoug Thompson 		return;
8548566c4dfSBorislav Petkov 	}
8552da11654SDoug Thompson 
85624f9a7feSBorislav Petkov 	amd64_info("using %s syndromes.\n", ((pvt->syn_type == 8) ? "x8" : "x4"));
857ad6a32e9SBorislav Petkov 
8588de1d91eSBorislav Petkov 	/* Only if NOT ganged does dclr1 have valid info */
85968798e17SBorislav Petkov 	if (!dct_ganging_enabled(pvt))
86068798e17SBorislav Petkov 		amd64_dump_dramcfg_low(pvt->dclr1, 1);
8612da11654SDoug Thompson 
8622da11654SDoug Thompson 	/*
8632da11654SDoug Thompson 	 * Determine if ganged and then dump memory sizes for first controller,
8642da11654SDoug Thompson 	 * and if NOT ganged dump info for 2nd controller.
8652da11654SDoug Thompson 	 */
8662da11654SDoug Thompson 	ganged = dct_ganging_enabled(pvt);
8672da11654SDoug Thompson 
8688566c4dfSBorislav Petkov 	amd64_debug_display_dimm_sizes(0, pvt);
8692da11654SDoug Thompson 
8702da11654SDoug Thompson 	if (!ganged)
8718566c4dfSBorislav Petkov 		amd64_debug_display_dimm_sizes(1, pvt);
8722da11654SDoug Thompson }
8732da11654SDoug Thompson 
8742da11654SDoug Thompson /* Read in both of DBAM registers */
8752da11654SDoug Thompson static void amd64_read_dbam_reg(struct amd64_pvt *pvt)
8762da11654SDoug Thompson {
8778d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F2, DBAM0, &pvt->dbam0);
8782da11654SDoug Thompson 
8796ba5dcdcSBorislav Petkov 	if (boot_cpu_data.x86 >= 0x10)
8808d5b5d9cSBorislav Petkov 		amd64_read_pci_cfg(pvt->F2, DBAM1, &pvt->dbam1);
8812da11654SDoug Thompson }
8822da11654SDoug Thompson 
88394be4bffSDoug Thompson /*
88494be4bffSDoug Thompson  * NOTE: CPU Revision Dependent code: Rev E and Rev F
88594be4bffSDoug Thompson  *
88694be4bffSDoug Thompson  * Set the DCSB and DCSM mask values depending on the CPU revision value. Also
88794be4bffSDoug Thompson  * set the shift factor for the DCSB and DCSM values.
88894be4bffSDoug Thompson  *
88994be4bffSDoug Thompson  * ->dcs_mask_notused, RevE:
89094be4bffSDoug Thompson  *
89194be4bffSDoug Thompson  * To find the max InputAddr for the csrow, start with the base address and set
89294be4bffSDoug Thompson  * all bits that are "don't care" bits in the test at the start of section
89394be4bffSDoug Thompson  * 3.5.4 (p. 84).
89494be4bffSDoug Thompson  *
89594be4bffSDoug Thompson  * The "don't care" bits are all set bits in the mask and all bits in the gaps
89694be4bffSDoug Thompson  * between bit ranges [35:25] and [19:13]. The value REV_E_DCS_NOTUSED_BITS
89794be4bffSDoug Thompson  * represents bits [24:20] and [12:0], which are all bits in the above-mentioned
89894be4bffSDoug Thompson  * gaps.
89994be4bffSDoug Thompson  *
90094be4bffSDoug Thompson  * ->dcs_mask_notused, RevF and later:
90194be4bffSDoug Thompson  *
90294be4bffSDoug Thompson  * To find the max InputAddr for the csrow, start with the base address and set
90394be4bffSDoug Thompson  * all bits that are "don't care" bits in the test at the start of NPT section
90494be4bffSDoug Thompson  * 4.5.4 (p. 87).
90594be4bffSDoug Thompson  *
90694be4bffSDoug Thompson  * The "don't care" bits are all set bits in the mask and all bits in the gaps
90794be4bffSDoug Thompson  * between bit ranges [36:27] and [21:13].
90894be4bffSDoug Thompson  *
90994be4bffSDoug Thompson  * The value REV_F_F1Xh_DCS_NOTUSED_BITS represents bits [26:22] and [12:0],
91094be4bffSDoug Thompson  * which are all bits in the above-mentioned gaps.
91194be4bffSDoug Thompson  */
91294be4bffSDoug Thompson static void amd64_set_dct_base_and_mask(struct amd64_pvt *pvt)
91394be4bffSDoug Thompson {
9149d858bb1SBorislav Petkov 
9151433eb99SBorislav Petkov 	if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_F) {
9169d858bb1SBorislav Petkov 		pvt->dcsb_base		= REV_E_DCSB_BASE_BITS;
9179d858bb1SBorislav Petkov 		pvt->dcsm_mask		= REV_E_DCSM_MASK_BITS;
9189d858bb1SBorislav Petkov 		pvt->dcs_mask_notused	= REV_E_DCS_NOTUSED_BITS;
9199d858bb1SBorislav Petkov 		pvt->dcs_shift		= REV_E_DCS_SHIFT;
9209d858bb1SBorislav Petkov 		pvt->cs_count		= 8;
9219d858bb1SBorislav Petkov 		pvt->num_dcsm		= 8;
9229d858bb1SBorislav Petkov 	} else {
92394be4bffSDoug Thompson 		pvt->dcsb_base		= REV_F_F1Xh_DCSB_BASE_BITS;
92494be4bffSDoug Thompson 		pvt->dcsm_mask		= REV_F_F1Xh_DCSM_MASK_BITS;
92594be4bffSDoug Thompson 		pvt->dcs_mask_notused	= REV_F_F1Xh_DCS_NOTUSED_BITS;
92694be4bffSDoug Thompson 		pvt->dcs_shift		= REV_F_F1Xh_DCS_SHIFT;
9279d858bb1SBorislav Petkov 		pvt->cs_count		= 8;
9289d858bb1SBorislav Petkov 		pvt->num_dcsm		= 4;
9299d858bb1SBorislav Petkov 	}
93094be4bffSDoug Thompson }
93194be4bffSDoug Thompson 
93294be4bffSDoug Thompson /*
93394be4bffSDoug Thompson  * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask hw registers
93494be4bffSDoug Thompson  */
93594be4bffSDoug Thompson static void amd64_read_dct_base_mask(struct amd64_pvt *pvt)
93694be4bffSDoug Thompson {
9376ba5dcdcSBorislav Petkov 	int cs, reg;
93894be4bffSDoug Thompson 
93994be4bffSDoug Thompson 	amd64_set_dct_base_and_mask(pvt);
94094be4bffSDoug Thompson 
9419d858bb1SBorislav Petkov 	for (cs = 0; cs < pvt->cs_count; cs++) {
94294be4bffSDoug Thompson 		reg = K8_DCSB0 + (cs * 4);
9438d5b5d9cSBorislav Petkov 		if (!amd64_read_pci_cfg(pvt->F2, reg, &pvt->dcsb0[cs]))
94494be4bffSDoug Thompson 			debugf0("  DCSB0[%d]=0x%08x reg: F2x%x\n",
94594be4bffSDoug Thompson 				cs, pvt->dcsb0[cs], reg);
94694be4bffSDoug Thompson 
94794be4bffSDoug Thompson 		/* If DCT are NOT ganged, then read in DCT1's base */
94894be4bffSDoug Thompson 		if (boot_cpu_data.x86 >= 0x10 && !dct_ganging_enabled(pvt)) {
94994be4bffSDoug Thompson 			reg = F10_DCSB1 + (cs * 4);
9508d5b5d9cSBorislav Petkov 			if (!amd64_read_pci_cfg(pvt->F2, reg,
9516ba5dcdcSBorislav Petkov 						&pvt->dcsb1[cs]))
95294be4bffSDoug Thompson 				debugf0("  DCSB1[%d]=0x%08x reg: F2x%x\n",
95394be4bffSDoug Thompson 					cs, pvt->dcsb1[cs], reg);
95494be4bffSDoug Thompson 		} else {
95594be4bffSDoug Thompson 			pvt->dcsb1[cs] = 0;
95694be4bffSDoug Thompson 		}
95794be4bffSDoug Thompson 	}
95894be4bffSDoug Thompson 
95994be4bffSDoug Thompson 	for (cs = 0; cs < pvt->num_dcsm; cs++) {
9604afcd2dcSWan Wei 		reg = K8_DCSM0 + (cs * 4);
9618d5b5d9cSBorislav Petkov 		if (!amd64_read_pci_cfg(pvt->F2, reg, &pvt->dcsm0[cs]))
96294be4bffSDoug Thompson 			debugf0("    DCSM0[%d]=0x%08x reg: F2x%x\n",
96394be4bffSDoug Thompson 				cs, pvt->dcsm0[cs], reg);
96494be4bffSDoug Thompson 
96594be4bffSDoug Thompson 		/* If DCT are NOT ganged, then read in DCT1's mask */
96694be4bffSDoug Thompson 		if (boot_cpu_data.x86 >= 0x10 && !dct_ganging_enabled(pvt)) {
96794be4bffSDoug Thompson 			reg = F10_DCSM1 + (cs * 4);
9688d5b5d9cSBorislav Petkov 			if (!amd64_read_pci_cfg(pvt->F2, reg,
9696ba5dcdcSBorislav Petkov 						&pvt->dcsm1[cs]))
97094be4bffSDoug Thompson 				debugf0("    DCSM1[%d]=0x%08x reg: F2x%x\n",
97194be4bffSDoug Thompson 					cs, pvt->dcsm1[cs], reg);
9726ba5dcdcSBorislav Petkov 		} else {
97394be4bffSDoug Thompson 			pvt->dcsm1[cs] = 0;
97494be4bffSDoug Thompson 		}
97594be4bffSDoug Thompson 	}
9766ba5dcdcSBorislav Petkov }
97794be4bffSDoug Thompson 
97824f9a7feSBorislav Petkov static enum mem_type amd64_determine_memory_type(struct amd64_pvt *pvt, int cs)
97994be4bffSDoug Thompson {
98094be4bffSDoug Thompson 	enum mem_type type;
98194be4bffSDoug Thompson 
9821433eb99SBorislav Petkov 	if (boot_cpu_data.x86 >= 0x10 || pvt->ext_model >= K8_REV_F) {
9836b4c0bdeSBorislav Petkov 		if (pvt->dchr0 & DDR3_MODE)
9846b4c0bdeSBorislav Petkov 			type = (pvt->dclr0 & BIT(16)) ?	MEM_DDR3 : MEM_RDDR3;
9856b4c0bdeSBorislav Petkov 		else
98694be4bffSDoug Thompson 			type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
98794be4bffSDoug Thompson 	} else {
98894be4bffSDoug Thompson 		type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
98994be4bffSDoug Thompson 	}
99094be4bffSDoug Thompson 
99124f9a7feSBorislav Petkov 	amd64_info("CS%d: %s\n", cs, edac_mem_types[type]);
99294be4bffSDoug Thompson 
99394be4bffSDoug Thompson 	return type;
99494be4bffSDoug Thompson }
99594be4bffSDoug Thompson 
996ddff876dSDoug Thompson /*
997ddff876dSDoug Thompson  * Read the DRAM Configuration Low register. It differs between CG, D & E revs
998ddff876dSDoug Thompson  * and the later RevF memory controllers (DDR vs DDR2)
999ddff876dSDoug Thompson  *
1000ddff876dSDoug Thompson  * Return:
1001ddff876dSDoug Thompson  *      number of memory channels in operation
1002ddff876dSDoug Thompson  * Pass back:
1003ddff876dSDoug Thompson  *      contents of the DCL0_LOW register
1004ddff876dSDoug Thompson  */
1005ddff876dSDoug Thompson static int k8_early_channel_count(struct amd64_pvt *pvt)
1006ddff876dSDoug Thompson {
1007ddff876dSDoug Thompson 	int flag, err = 0;
1008ddff876dSDoug Thompson 
10098d5b5d9cSBorislav Petkov 	err = amd64_read_pci_cfg(pvt->F2, F10_DCLR_0, &pvt->dclr0);
1010ddff876dSDoug Thompson 	if (err)
1011ddff876dSDoug Thompson 		return err;
1012ddff876dSDoug Thompson 
10139f56da0eSBorislav Petkov 	if (pvt->ext_model >= K8_REV_F)
1014ddff876dSDoug Thompson 		/* RevF (NPT) and later */
1015ddff876dSDoug Thompson 		flag = pvt->dclr0 & F10_WIDTH_128;
10169f56da0eSBorislav Petkov 	else
1017ddff876dSDoug Thompson 		/* RevE and earlier */
1018ddff876dSDoug Thompson 		flag = pvt->dclr0 & REVE_WIDTH_128;
1019ddff876dSDoug Thompson 
1020ddff876dSDoug Thompson 	/* not used */
1021ddff876dSDoug Thompson 	pvt->dclr1 = 0;
1022ddff876dSDoug Thompson 
1023ddff876dSDoug Thompson 	return (flag) ? 2 : 1;
1024ddff876dSDoug Thompson }
1025ddff876dSDoug Thompson 
1026ddff876dSDoug Thompson /* extract the ERROR ADDRESS for the K8 CPUs */
1027ddff876dSDoug Thompson static u64 k8_get_error_address(struct mem_ctl_info *mci,
1028ef44cc4cSBorislav Petkov 				struct err_regs *info)
1029ddff876dSDoug Thompson {
1030ddff876dSDoug Thompson 	return (((u64) (info->nbeah & 0xff)) << 32) +
1031ddff876dSDoug Thompson 			(info->nbeal & ~0x03);
1032ddff876dSDoug Thompson }
1033ddff876dSDoug Thompson 
1034ddff876dSDoug Thompson /*
1035ddff876dSDoug Thompson  * Read the Base and Limit registers for K8 based Memory controllers; extract
1036ddff876dSDoug Thompson  * fields from the 'raw' reg into separate data fields
1037ddff876dSDoug Thompson  *
1038ddff876dSDoug Thompson  * Isolates: BASE, LIMIT, IntlvEn, IntlvSel, RW_EN
1039ddff876dSDoug Thompson  */
1040ddff876dSDoug Thompson static void k8_read_dram_base_limit(struct amd64_pvt *pvt, int dram)
1041ddff876dSDoug Thompson {
1042ddff876dSDoug Thompson 	u32 low;
1043ddff876dSDoug Thompson 	u32 off = dram << 3;	/* 8 bytes between DRAM entries */
1044ddff876dSDoug Thompson 
10458d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, K8_DRAM_BASE_LOW + off, &low);
1046ddff876dSDoug Thompson 
1047ddff876dSDoug Thompson 	/* Extract parts into separate data entries */
10484997811eSBorislav Petkov 	pvt->dram_base[dram] = ((u64) low & 0xFFFF0000) << 8;
1049ddff876dSDoug Thompson 	pvt->dram_IntlvEn[dram] = (low >> 8) & 0x7;
1050ddff876dSDoug Thompson 	pvt->dram_rw_en[dram] = (low & 0x3);
1051ddff876dSDoug Thompson 
10528d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, K8_DRAM_LIMIT_LOW + off, &low);
1053ddff876dSDoug Thompson 
1054ddff876dSDoug Thompson 	/*
1055ddff876dSDoug Thompson 	 * Extract parts into separate data entries. Limit is the HIGHEST memory
1056ddff876dSDoug Thompson 	 * location of the region, so lower 24 bits need to be all ones
1057ddff876dSDoug Thompson 	 */
10584997811eSBorislav Petkov 	pvt->dram_limit[dram] = (((u64) low & 0xFFFF0000) << 8) | 0x00FFFFFF;
1059ddff876dSDoug Thompson 	pvt->dram_IntlvSel[dram] = (low >> 8) & 0x7;
1060ddff876dSDoug Thompson 	pvt->dram_DstNode[dram] = (low & 0x7);
1061ddff876dSDoug Thompson }
1062ddff876dSDoug Thompson 
1063ddff876dSDoug Thompson static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
1064ad6a32e9SBorislav Petkov 				    struct err_regs *err_info, u64 sys_addr)
1065ddff876dSDoug Thompson {
1066ddff876dSDoug Thompson 	struct mem_ctl_info *src_mci;
1067ddff876dSDoug Thompson 	int channel, csrow;
1068ddff876dSDoug Thompson 	u32 page, offset;
1069ad6a32e9SBorislav Petkov 	u16 syndrome;
1070ddff876dSDoug Thompson 
1071ad6a32e9SBorislav Petkov 	syndrome = extract_syndrome(err_info);
1072ddff876dSDoug Thompson 
1073ddff876dSDoug Thompson 	/* CHIPKILL enabled */
1074ad6a32e9SBorislav Petkov 	if (err_info->nbcfg & K8_NBCFG_CHIPKILL) {
1075bfc04aecSBorislav Petkov 		channel = get_channel_from_ecc_syndrome(mci, syndrome);
1076ddff876dSDoug Thompson 		if (channel < 0) {
1077ddff876dSDoug Thompson 			/*
1078ddff876dSDoug Thompson 			 * Syndrome didn't map, so we don't know which of the
1079ddff876dSDoug Thompson 			 * 2 DIMMs is in error. So we need to ID 'both' of them
1080ddff876dSDoug Thompson 			 * as suspect.
1081ddff876dSDoug Thompson 			 */
108224f9a7feSBorislav Petkov 			amd64_mc_warn(mci, "unknown syndrome 0x%04x - possible "
1083ad6a32e9SBorislav Petkov 					   "error reporting race\n", syndrome);
1084ddff876dSDoug Thompson 			edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
1085ddff876dSDoug Thompson 			return;
1086ddff876dSDoug Thompson 		}
1087ddff876dSDoug Thompson 	} else {
1088ddff876dSDoug Thompson 		/*
1089ddff876dSDoug Thompson 		 * non-chipkill ecc mode
1090ddff876dSDoug Thompson 		 *
1091ddff876dSDoug Thompson 		 * The k8 documentation is unclear about how to determine the
1092ddff876dSDoug Thompson 		 * channel number when using non-chipkill memory.  This method
1093ddff876dSDoug Thompson 		 * was obtained from email communication with someone at AMD.
1094ddff876dSDoug Thompson 		 * (Wish the email was placed in this comment - norsk)
1095ddff876dSDoug Thompson 		 */
109644e9e2eeSBorislav Petkov 		channel = ((sys_addr & BIT(3)) != 0);
1097ddff876dSDoug Thompson 	}
1098ddff876dSDoug Thompson 
1099ddff876dSDoug Thompson 	/*
1100ddff876dSDoug Thompson 	 * Find out which node the error address belongs to. This may be
1101ddff876dSDoug Thompson 	 * different from the node that detected the error.
1102ddff876dSDoug Thompson 	 */
110344e9e2eeSBorislav Petkov 	src_mci = find_mc_by_sys_addr(mci, sys_addr);
11042cff18c2SKeith Mannthey 	if (!src_mci) {
110524f9a7feSBorislav Petkov 		amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
110644e9e2eeSBorislav Petkov 			     (unsigned long)sys_addr);
1107ddff876dSDoug Thompson 		edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
1108ddff876dSDoug Thompson 		return;
1109ddff876dSDoug Thompson 	}
1110ddff876dSDoug Thompson 
111144e9e2eeSBorislav Petkov 	/* Now map the sys_addr to a CSROW */
111244e9e2eeSBorislav Petkov 	csrow = sys_addr_to_csrow(src_mci, sys_addr);
1113ddff876dSDoug Thompson 	if (csrow < 0) {
1114ddff876dSDoug Thompson 		edac_mc_handle_ce_no_info(src_mci, EDAC_MOD_STR);
1115ddff876dSDoug Thompson 	} else {
111644e9e2eeSBorislav Petkov 		error_address_to_page_and_offset(sys_addr, &page, &offset);
1117ddff876dSDoug Thompson 
1118ddff876dSDoug Thompson 		edac_mc_handle_ce(src_mci, page, offset, syndrome, csrow,
1119ddff876dSDoug Thompson 				  channel, EDAC_MOD_STR);
1120ddff876dSDoug Thompson 	}
1121ddff876dSDoug Thompson }
1122ddff876dSDoug Thompson 
11231433eb99SBorislav Petkov static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, int cs_mode)
1124ddff876dSDoug Thompson {
11251433eb99SBorislav Petkov 	int *dbam_map;
1126ddff876dSDoug Thompson 
11271433eb99SBorislav Petkov 	if (pvt->ext_model >= K8_REV_F)
11281433eb99SBorislav Petkov 		dbam_map = ddr2_dbam;
11291433eb99SBorislav Petkov 	else if (pvt->ext_model >= K8_REV_D)
11301433eb99SBorislav Petkov 		dbam_map = ddr2_dbam_revD;
11311433eb99SBorislav Petkov 	else
11321433eb99SBorislav Petkov 		dbam_map = ddr2_dbam_revCG;
1133ddff876dSDoug Thompson 
11341433eb99SBorislav Petkov 	return dbam_map[cs_mode];
1135ddff876dSDoug Thompson }
1136ddff876dSDoug Thompson 
11371afd3c98SDoug Thompson /*
11381afd3c98SDoug Thompson  * Get the number of DCT channels in use.
11391afd3c98SDoug Thompson  *
11401afd3c98SDoug Thompson  * Return:
11411afd3c98SDoug Thompson  *	number of Memory Channels in operation
11421afd3c98SDoug Thompson  * Pass back:
11431afd3c98SDoug Thompson  *	contents of the DCL0_LOW register
11441afd3c98SDoug Thompson  */
11451afd3c98SDoug Thompson static int f10_early_channel_count(struct amd64_pvt *pvt)
11461afd3c98SDoug Thompson {
114757a30854SWan Wei 	int dbams[] = { DBAM0, DBAM1 };
11486ba5dcdcSBorislav Petkov 	int i, j, channels = 0;
11491afd3c98SDoug Thompson 	u32 dbam;
1150ddff876dSDoug Thompson 
11511afd3c98SDoug Thompson 	/* If we are in 128 bit mode, then we are using 2 channels */
11521afd3c98SDoug Thompson 	if (pvt->dclr0 & F10_WIDTH_128) {
11531afd3c98SDoug Thompson 		channels = 2;
11541afd3c98SDoug Thompson 		return channels;
11551afd3c98SDoug Thompson 	}
11561afd3c98SDoug Thompson 
11571afd3c98SDoug Thompson 	/*
1158d16149e8SBorislav Petkov 	 * Need to check if in unganged mode: In such, there are 2 channels,
1159d16149e8SBorislav Petkov 	 * but they are not in 128 bit mode and thus the above 'dclr0' status
1160d16149e8SBorislav Petkov 	 * bit will be OFF.
11611afd3c98SDoug Thompson 	 *
11621afd3c98SDoug Thompson 	 * Need to check DCT0[0] and DCT1[0] to see if only one of them has
11631afd3c98SDoug Thompson 	 * their CSEnable bit on. If so, then SINGLE DIMM case.
11641afd3c98SDoug Thompson 	 */
1165d16149e8SBorislav Petkov 	debugf0("Data width is not 128 bits - need more decoding\n");
11661afd3c98SDoug Thompson 
11671afd3c98SDoug Thompson 	/*
11681afd3c98SDoug Thompson 	 * Check DRAM Bank Address Mapping values for each DIMM to see if there
11691afd3c98SDoug Thompson 	 * is more than just one DIMM present in unganged mode. Need to check
11701afd3c98SDoug Thompson 	 * both controllers since DIMMs can be placed in either one.
11711afd3c98SDoug Thompson 	 */
117257a30854SWan Wei 	for (i = 0; i < ARRAY_SIZE(dbams); i++) {
11738d5b5d9cSBorislav Petkov 		if (amd64_read_pci_cfg(pvt->F2, dbams[i], &dbam))
11741afd3c98SDoug Thompson 			goto err_reg;
11751afd3c98SDoug Thompson 
117657a30854SWan Wei 		for (j = 0; j < 4; j++) {
117757a30854SWan Wei 			if (DBAM_DIMM(j, dbam) > 0) {
11781afd3c98SDoug Thompson 				channels++;
117957a30854SWan Wei 				break;
11801afd3c98SDoug Thompson 			}
118157a30854SWan Wei 		}
118257a30854SWan Wei 	}
11831afd3c98SDoug Thompson 
1184d16149e8SBorislav Petkov 	if (channels > 2)
1185d16149e8SBorislav Petkov 		channels = 2;
1186d16149e8SBorislav Petkov 
118724f9a7feSBorislav Petkov 	amd64_info("MCT channel count: %d\n", channels);
11881afd3c98SDoug Thompson 
11891afd3c98SDoug Thompson 	return channels;
11901afd3c98SDoug Thompson 
11911afd3c98SDoug Thompson err_reg:
11921afd3c98SDoug Thompson 	return -1;
11931afd3c98SDoug Thompson 
11941afd3c98SDoug Thompson }
11951afd3c98SDoug Thompson 
11961433eb99SBorislav Petkov static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, int cs_mode)
11971afd3c98SDoug Thompson {
11981433eb99SBorislav Petkov 	int *dbam_map;
11991433eb99SBorislav Petkov 
12001433eb99SBorislav Petkov 	if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
12011433eb99SBorislav Petkov 		dbam_map = ddr3_dbam;
12021433eb99SBorislav Petkov 	else
12031433eb99SBorislav Petkov 		dbam_map = ddr2_dbam;
12041433eb99SBorislav Petkov 
12051433eb99SBorislav Petkov 	return dbam_map[cs_mode];
12061afd3c98SDoug Thompson }
12071afd3c98SDoug Thompson 
12081afd3c98SDoug Thompson /* Enable extended configuration access via 0xCF8 feature */
12091afd3c98SDoug Thompson static void amd64_setup(struct amd64_pvt *pvt)
12101afd3c98SDoug Thompson {
12111afd3c98SDoug Thompson 	u32 reg;
12121afd3c98SDoug Thompson 
12138d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, F10_NB_CFG_HIGH, &reg);
12141afd3c98SDoug Thompson 
12151afd3c98SDoug Thompson 	pvt->flags.cf8_extcfg = !!(reg & F10_NB_CFG_LOW_ENABLE_EXT_CFG);
12161afd3c98SDoug Thompson 	reg |= F10_NB_CFG_LOW_ENABLE_EXT_CFG;
12178d5b5d9cSBorislav Petkov 	pci_write_config_dword(pvt->F3, F10_NB_CFG_HIGH, reg);
12181afd3c98SDoug Thompson }
12191afd3c98SDoug Thompson 
12201afd3c98SDoug Thompson /* Restore the extended configuration access via 0xCF8 feature */
12211afd3c98SDoug Thompson static void amd64_teardown(struct amd64_pvt *pvt)
12221afd3c98SDoug Thompson {
12231afd3c98SDoug Thompson 	u32 reg;
12241afd3c98SDoug Thompson 
12258d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, F10_NB_CFG_HIGH, &reg);
12261afd3c98SDoug Thompson 
12271afd3c98SDoug Thompson 	reg &= ~F10_NB_CFG_LOW_ENABLE_EXT_CFG;
12281afd3c98SDoug Thompson 	if (pvt->flags.cf8_extcfg)
12291afd3c98SDoug Thompson 		reg |= F10_NB_CFG_LOW_ENABLE_EXT_CFG;
12308d5b5d9cSBorislav Petkov 	pci_write_config_dword(pvt->F3, F10_NB_CFG_HIGH, reg);
12311afd3c98SDoug Thompson }
12321afd3c98SDoug Thompson 
12331afd3c98SDoug Thompson static u64 f10_get_error_address(struct mem_ctl_info *mci,
1234ef44cc4cSBorislav Petkov 			struct err_regs *info)
12351afd3c98SDoug Thompson {
12361afd3c98SDoug Thompson 	return (((u64) (info->nbeah & 0xffff)) << 32) +
12371afd3c98SDoug Thompson 			(info->nbeal & ~0x01);
12381afd3c98SDoug Thompson }
12391afd3c98SDoug Thompson 
12401afd3c98SDoug Thompson /*
12411afd3c98SDoug Thompson  * Read the Base and Limit registers for F10 based Memory controllers. Extract
12421afd3c98SDoug Thompson  * fields from the 'raw' reg into separate data fields.
12431afd3c98SDoug Thompson  *
12441afd3c98SDoug Thompson  * Isolates: BASE, LIMIT, IntlvEn, IntlvSel, RW_EN.
12451afd3c98SDoug Thompson  */
12461afd3c98SDoug Thompson static void f10_read_dram_base_limit(struct amd64_pvt *pvt, int dram)
12471afd3c98SDoug Thompson {
12481afd3c98SDoug Thompson 	u32 high_offset, low_offset, high_base, low_base, high_limit, low_limit;
12491afd3c98SDoug Thompson 
12501afd3c98SDoug Thompson 	low_offset = K8_DRAM_BASE_LOW + (dram << 3);
12511afd3c98SDoug Thompson 	high_offset = F10_DRAM_BASE_HIGH + (dram << 3);
12521afd3c98SDoug Thompson 
12531afd3c98SDoug Thompson 	/* read the 'raw' DRAM BASE Address register */
12548d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, low_offset, &low_base);
12551afd3c98SDoug Thompson 
12561afd3c98SDoug Thompson 	/* Read from the ECS data register */
12578d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, high_offset, &high_base);
12581afd3c98SDoug Thompson 
12591afd3c98SDoug Thompson 	/* Extract parts into separate data entries */
12601afd3c98SDoug Thompson 	pvt->dram_rw_en[dram] = (low_base & 0x3);
12611afd3c98SDoug Thompson 
12621afd3c98SDoug Thompson 	if (pvt->dram_rw_en[dram] == 0)
12631afd3c98SDoug Thompson 		return;
12641afd3c98SDoug Thompson 
12651afd3c98SDoug Thompson 	pvt->dram_IntlvEn[dram] = (low_base >> 8) & 0x7;
12661afd3c98SDoug Thompson 
126766216a7aSBorislav Petkov 	pvt->dram_base[dram] = (((u64)high_base & 0x000000FF) << 40) |
12684997811eSBorislav Petkov 			       (((u64)low_base  & 0xFFFF0000) << 8);
12691afd3c98SDoug Thompson 
12701afd3c98SDoug Thompson 	low_offset = K8_DRAM_LIMIT_LOW + (dram << 3);
12711afd3c98SDoug Thompson 	high_offset = F10_DRAM_LIMIT_HIGH + (dram << 3);
12721afd3c98SDoug Thompson 
12731afd3c98SDoug Thompson 	/* read the 'raw' LIMIT registers */
12748d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, low_offset, &low_limit);
12751afd3c98SDoug Thompson 
12761afd3c98SDoug Thompson 	/* Read from the ECS data register for the HIGH portion */
12778d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, high_offset, &high_limit);
12781afd3c98SDoug Thompson 
12791afd3c98SDoug Thompson 	pvt->dram_DstNode[dram] = (low_limit & 0x7);
12801afd3c98SDoug Thompson 	pvt->dram_IntlvSel[dram] = (low_limit >> 8) & 0x7;
12811afd3c98SDoug Thompson 
12821afd3c98SDoug Thompson 	/*
12831afd3c98SDoug Thompson 	 * Extract address values and form a LIMIT address. Limit is the HIGHEST
12841afd3c98SDoug Thompson 	 * memory location of the region, so low 24 bits need to be all ones.
12851afd3c98SDoug Thompson 	 */
128666216a7aSBorislav Petkov 	pvt->dram_limit[dram] = (((u64)high_limit & 0x000000FF) << 40) |
12874997811eSBorislav Petkov 				(((u64) low_limit & 0xFFFF0000) << 8) |
128866216a7aSBorislav Petkov 				0x00FFFFFF;
12891afd3c98SDoug Thompson }
12906163b5d4SDoug Thompson 
12916163b5d4SDoug Thompson static void f10_read_dram_ctl_register(struct amd64_pvt *pvt)
12926163b5d4SDoug Thompson {
12936163b5d4SDoug Thompson 
12948d5b5d9cSBorislav Petkov 	if (!amd64_read_pci_cfg(pvt->F2, F10_DCTL_SEL_LOW,
12956ba5dcdcSBorislav Petkov 				&pvt->dram_ctl_select_low)) {
129672381bd5SBorislav Petkov 		debugf0("F2x110 (DCTL Sel. Low): 0x%08x, "
129772381bd5SBorislav Petkov 			"High range addresses at: 0x%x\n",
129872381bd5SBorislav Petkov 			pvt->dram_ctl_select_low,
129972381bd5SBorislav Petkov 			dct_sel_baseaddr(pvt));
13006163b5d4SDoug Thompson 
130172381bd5SBorislav Petkov 		debugf0("  DCT mode: %s, All DCTs on: %s\n",
130272381bd5SBorislav Petkov 			(dct_ganging_enabled(pvt) ? "ganged" : "unganged"),
130372381bd5SBorislav Petkov 			(dct_dram_enabled(pvt) ? "yes"   : "no"));
13046163b5d4SDoug Thompson 
130572381bd5SBorislav Petkov 		if (!dct_ganging_enabled(pvt))
130672381bd5SBorislav Petkov 			debugf0("  Address range split per DCT: %s\n",
130772381bd5SBorislav Petkov 				(dct_high_range_enabled(pvt) ? "yes" : "no"));
130872381bd5SBorislav Petkov 
130972381bd5SBorislav Petkov 		debugf0("  DCT data interleave for ECC: %s, "
131072381bd5SBorislav Petkov 			"DRAM cleared since last warm reset: %s\n",
131172381bd5SBorislav Petkov 			(dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
131272381bd5SBorislav Petkov 			(dct_memory_cleared(pvt) ? "yes" : "no"));
131372381bd5SBorislav Petkov 
131472381bd5SBorislav Petkov 		debugf0("  DCT channel interleave: %s, "
131572381bd5SBorislav Petkov 			"DCT interleave bits selector: 0x%x\n",
131672381bd5SBorislav Petkov 			(dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
13176163b5d4SDoug Thompson 			dct_sel_interleave_addr(pvt));
13186163b5d4SDoug Thompson 	}
13196163b5d4SDoug Thompson 
13208d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F2, F10_DCTL_SEL_HIGH,
13216163b5d4SDoug Thompson 			   &pvt->dram_ctl_select_high);
13226163b5d4SDoug Thompson }
13236163b5d4SDoug Thompson 
1324f71d0a05SDoug Thompson /*
1325f71d0a05SDoug Thompson  * determine channel based on the interleaving mode: F10h BKDG, 2.8.9 Memory
1326f71d0a05SDoug Thompson  * Interleaving Modes.
1327f71d0a05SDoug Thompson  */
13286163b5d4SDoug Thompson static u32 f10_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
13296163b5d4SDoug Thompson 				int hi_range_sel, u32 intlv_en)
13306163b5d4SDoug Thompson {
13316163b5d4SDoug Thompson 	u32 cs, temp, dct_sel_high = (pvt->dram_ctl_select_low >> 1) & 1;
13326163b5d4SDoug Thompson 
13336163b5d4SDoug Thompson 	if (dct_ganging_enabled(pvt))
13346163b5d4SDoug Thompson 		cs = 0;
13356163b5d4SDoug Thompson 	else if (hi_range_sel)
13366163b5d4SDoug Thompson 		cs = dct_sel_high;
13376163b5d4SDoug Thompson 	else if (dct_interleave_enabled(pvt)) {
1338f71d0a05SDoug Thompson 		/*
1339f71d0a05SDoug Thompson 		 * see F2x110[DctSelIntLvAddr] - channel interleave mode
1340f71d0a05SDoug Thompson 		 */
13416163b5d4SDoug Thompson 		if (dct_sel_interleave_addr(pvt) == 0)
13426163b5d4SDoug Thompson 			cs = sys_addr >> 6 & 1;
13436163b5d4SDoug Thompson 		else if ((dct_sel_interleave_addr(pvt) >> 1) & 1) {
13446163b5d4SDoug Thompson 			temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) % 2;
13456163b5d4SDoug Thompson 
13466163b5d4SDoug Thompson 			if (dct_sel_interleave_addr(pvt) & 1)
13476163b5d4SDoug Thompson 				cs = (sys_addr >> 9 & 1) ^ temp;
13486163b5d4SDoug Thompson 			else
13496163b5d4SDoug Thompson 				cs = (sys_addr >> 6 & 1) ^ temp;
13506163b5d4SDoug Thompson 		} else if (intlv_en & 4)
13516163b5d4SDoug Thompson 			cs = sys_addr >> 15 & 1;
13526163b5d4SDoug Thompson 		else if (intlv_en & 2)
13536163b5d4SDoug Thompson 			cs = sys_addr >> 14 & 1;
13546163b5d4SDoug Thompson 		else if (intlv_en & 1)
13556163b5d4SDoug Thompson 			cs = sys_addr >> 13 & 1;
13566163b5d4SDoug Thompson 		else
13576163b5d4SDoug Thompson 			cs = sys_addr >> 12 & 1;
13586163b5d4SDoug Thompson 	} else if (dct_high_range_enabled(pvt) && !dct_ganging_enabled(pvt))
13596163b5d4SDoug Thompson 		cs = ~dct_sel_high & 1;
13606163b5d4SDoug Thompson 	else
13616163b5d4SDoug Thompson 		cs = 0;
13626163b5d4SDoug Thompson 
13636163b5d4SDoug Thompson 	return cs;
13646163b5d4SDoug Thompson }
13656163b5d4SDoug Thompson 
13666163b5d4SDoug Thompson static inline u32 f10_map_intlv_en_to_shift(u32 intlv_en)
13676163b5d4SDoug Thompson {
13686163b5d4SDoug Thompson 	if (intlv_en == 1)
13696163b5d4SDoug Thompson 		return 1;
13706163b5d4SDoug Thompson 	else if (intlv_en == 3)
13716163b5d4SDoug Thompson 		return 2;
13726163b5d4SDoug Thompson 	else if (intlv_en == 7)
13736163b5d4SDoug Thompson 		return 3;
13746163b5d4SDoug Thompson 
13756163b5d4SDoug Thompson 	return 0;
13766163b5d4SDoug Thompson }
13776163b5d4SDoug Thompson 
1378f71d0a05SDoug Thompson /* See F10h BKDG, 2.8.10.2 DctSelBaseOffset Programming */
1379f71d0a05SDoug Thompson static inline u64 f10_get_base_addr_offset(u64 sys_addr, int hi_range_sel,
13806163b5d4SDoug Thompson 						 u32 dct_sel_base_addr,
13816163b5d4SDoug Thompson 						 u64 dct_sel_base_off,
1382f71d0a05SDoug Thompson 						 u32 hole_valid, u32 hole_off,
13836163b5d4SDoug Thompson 						 u64 dram_base)
13846163b5d4SDoug Thompson {
13856163b5d4SDoug Thompson 	u64 chan_off;
13866163b5d4SDoug Thompson 
13876163b5d4SDoug Thompson 	if (hi_range_sel) {
13889975a5f2SBorislav Petkov 		if (!(dct_sel_base_addr & 0xFFFF0000) &&
1389f71d0a05SDoug Thompson 		   hole_valid && (sys_addr >= 0x100000000ULL))
13906163b5d4SDoug Thompson 			chan_off = hole_off << 16;
13916163b5d4SDoug Thompson 		else
13926163b5d4SDoug Thompson 			chan_off = dct_sel_base_off;
13936163b5d4SDoug Thompson 	} else {
1394f71d0a05SDoug Thompson 		if (hole_valid && (sys_addr >= 0x100000000ULL))
13956163b5d4SDoug Thompson 			chan_off = hole_off << 16;
13966163b5d4SDoug Thompson 		else
13976163b5d4SDoug Thompson 			chan_off = dram_base & 0xFFFFF8000000ULL;
13986163b5d4SDoug Thompson 	}
13996163b5d4SDoug Thompson 
14006163b5d4SDoug Thompson 	return (sys_addr & 0x0000FFFFFFFFFFC0ULL) -
14016163b5d4SDoug Thompson 			(chan_off & 0x0000FFFFFF800000ULL);
14026163b5d4SDoug Thompson }
14036163b5d4SDoug Thompson 
14046163b5d4SDoug Thompson /* Hack for the time being - Can we get this from BIOS?? */
14056163b5d4SDoug Thompson #define	CH0SPARE_RANK	0
14066163b5d4SDoug Thompson #define	CH1SPARE_RANK	1
14076163b5d4SDoug Thompson 
14086163b5d4SDoug Thompson /*
14096163b5d4SDoug Thompson  * checks if the csrow passed in is marked as SPARED, if so returns the new
14106163b5d4SDoug Thompson  * spare row
14116163b5d4SDoug Thompson  */
14126163b5d4SDoug Thompson static inline int f10_process_possible_spare(int csrow,
14136163b5d4SDoug Thompson 				u32 cs, struct amd64_pvt *pvt)
14146163b5d4SDoug Thompson {
14156163b5d4SDoug Thompson 	u32 swap_done;
14166163b5d4SDoug Thompson 	u32 bad_dram_cs;
14176163b5d4SDoug Thompson 
14186163b5d4SDoug Thompson 	/* Depending on channel, isolate respective SPARING info */
14196163b5d4SDoug Thompson 	if (cs) {
14206163b5d4SDoug Thompson 		swap_done = F10_ONLINE_SPARE_SWAPDONE1(pvt->online_spare);
14216163b5d4SDoug Thompson 		bad_dram_cs = F10_ONLINE_SPARE_BADDRAM_CS1(pvt->online_spare);
14226163b5d4SDoug Thompson 		if (swap_done && (csrow == bad_dram_cs))
14236163b5d4SDoug Thompson 			csrow = CH1SPARE_RANK;
14246163b5d4SDoug Thompson 	} else {
14256163b5d4SDoug Thompson 		swap_done = F10_ONLINE_SPARE_SWAPDONE0(pvt->online_spare);
14266163b5d4SDoug Thompson 		bad_dram_cs = F10_ONLINE_SPARE_BADDRAM_CS0(pvt->online_spare);
14276163b5d4SDoug Thompson 		if (swap_done && (csrow == bad_dram_cs))
14286163b5d4SDoug Thompson 			csrow = CH0SPARE_RANK;
14296163b5d4SDoug Thompson 	}
14306163b5d4SDoug Thompson 	return csrow;
14316163b5d4SDoug Thompson }
14326163b5d4SDoug Thompson 
14336163b5d4SDoug Thompson /*
14346163b5d4SDoug Thompson  * Iterate over the DRAM DCT "base" and "mask" registers looking for a
14356163b5d4SDoug Thompson  * SystemAddr match on the specified 'ChannelSelect' and 'NodeID'
14366163b5d4SDoug Thompson  *
14376163b5d4SDoug Thompson  * Return:
14386163b5d4SDoug Thompson  *	-EINVAL:  NOT FOUND
14396163b5d4SDoug Thompson  *	0..csrow = Chip-Select Row
14406163b5d4SDoug Thompson  */
14416163b5d4SDoug Thompson static int f10_lookup_addr_in_dct(u32 in_addr, u32 nid, u32 cs)
14426163b5d4SDoug Thompson {
14436163b5d4SDoug Thompson 	struct mem_ctl_info *mci;
14446163b5d4SDoug Thompson 	struct amd64_pvt *pvt;
14456163b5d4SDoug Thompson 	u32 cs_base, cs_mask;
14466163b5d4SDoug Thompson 	int cs_found = -EINVAL;
14476163b5d4SDoug Thompson 	int csrow;
14486163b5d4SDoug Thompson 
14496163b5d4SDoug Thompson 	mci = mci_lookup[nid];
14506163b5d4SDoug Thompson 	if (!mci)
14516163b5d4SDoug Thompson 		return cs_found;
14526163b5d4SDoug Thompson 
14536163b5d4SDoug Thompson 	pvt = mci->pvt_info;
14546163b5d4SDoug Thompson 
14556163b5d4SDoug Thompson 	debugf1("InputAddr=0x%x  channelselect=%d\n", in_addr, cs);
14566163b5d4SDoug Thompson 
14579d858bb1SBorislav Petkov 	for (csrow = 0; csrow < pvt->cs_count; csrow++) {
14586163b5d4SDoug Thompson 
14596163b5d4SDoug Thompson 		cs_base = amd64_get_dct_base(pvt, cs, csrow);
14606163b5d4SDoug Thompson 		if (!(cs_base & K8_DCSB_CS_ENABLE))
14616163b5d4SDoug Thompson 			continue;
14626163b5d4SDoug Thompson 
14636163b5d4SDoug Thompson 		/*
14646163b5d4SDoug Thompson 		 * We have an ENABLED CSROW, Isolate just the MASK bits of the
14656163b5d4SDoug Thompson 		 * target: [28:19] and [13:5], which map to [36:27] and [21:13]
14666163b5d4SDoug Thompson 		 * of the actual address.
14676163b5d4SDoug Thompson 		 */
14686163b5d4SDoug Thompson 		cs_base &= REV_F_F1Xh_DCSB_BASE_BITS;
14696163b5d4SDoug Thompson 
14706163b5d4SDoug Thompson 		/*
14716163b5d4SDoug Thompson 		 * Get the DCT Mask, and ENABLE the reserved bits: [18:16] and
14726163b5d4SDoug Thompson 		 * [4:0] to become ON. Then mask off bits [28:0] ([36:8])
14736163b5d4SDoug Thompson 		 */
14746163b5d4SDoug Thompson 		cs_mask = amd64_get_dct_mask(pvt, cs, csrow);
14756163b5d4SDoug Thompson 
14766163b5d4SDoug Thompson 		debugf1("    CSROW=%d CSBase=0x%x RAW CSMask=0x%x\n",
14776163b5d4SDoug Thompson 				csrow, cs_base, cs_mask);
14786163b5d4SDoug Thompson 
14796163b5d4SDoug Thompson 		cs_mask = (cs_mask | 0x0007C01F) & 0x1FFFFFFF;
14806163b5d4SDoug Thompson 
14816163b5d4SDoug Thompson 		debugf1("              Final CSMask=0x%x\n", cs_mask);
14826163b5d4SDoug Thompson 		debugf1("    (InputAddr & ~CSMask)=0x%x "
14836163b5d4SDoug Thompson 				"(CSBase & ~CSMask)=0x%x\n",
14846163b5d4SDoug Thompson 				(in_addr & ~cs_mask), (cs_base & ~cs_mask));
14856163b5d4SDoug Thompson 
14866163b5d4SDoug Thompson 		if ((in_addr & ~cs_mask) == (cs_base & ~cs_mask)) {
14876163b5d4SDoug Thompson 			cs_found = f10_process_possible_spare(csrow, cs, pvt);
14886163b5d4SDoug Thompson 
14896163b5d4SDoug Thompson 			debugf1(" MATCH csrow=%d\n", cs_found);
14906163b5d4SDoug Thompson 			break;
14916163b5d4SDoug Thompson 		}
14926163b5d4SDoug Thompson 	}
14936163b5d4SDoug Thompson 	return cs_found;
14946163b5d4SDoug Thompson }
14956163b5d4SDoug Thompson 
1496f71d0a05SDoug Thompson /* For a given @dram_range, check if @sys_addr falls within it. */
1497f71d0a05SDoug Thompson static int f10_match_to_this_node(struct amd64_pvt *pvt, int dram_range,
1498f71d0a05SDoug Thompson 				  u64 sys_addr, int *nid, int *chan_sel)
1499f71d0a05SDoug Thompson {
1500f71d0a05SDoug Thompson 	int node_id, cs_found = -EINVAL, high_range = 0;
1501f71d0a05SDoug Thompson 	u32 intlv_en, intlv_sel, intlv_shift, hole_off;
1502f71d0a05SDoug Thompson 	u32 hole_valid, tmp, dct_sel_base, channel;
1503f71d0a05SDoug Thompson 	u64 dram_base, chan_addr, dct_sel_base_off;
1504f71d0a05SDoug Thompson 
1505f71d0a05SDoug Thompson 	dram_base = pvt->dram_base[dram_range];
1506f71d0a05SDoug Thompson 	intlv_en = pvt->dram_IntlvEn[dram_range];
1507f71d0a05SDoug Thompson 
1508f71d0a05SDoug Thompson 	node_id = pvt->dram_DstNode[dram_range];
1509f71d0a05SDoug Thompson 	intlv_sel = pvt->dram_IntlvSel[dram_range];
1510f71d0a05SDoug Thompson 
1511f71d0a05SDoug Thompson 	debugf1("(dram=%d) Base=0x%llx SystemAddr= 0x%llx Limit=0x%llx\n",
1512f71d0a05SDoug Thompson 		dram_range, dram_base, sys_addr, pvt->dram_limit[dram_range]);
1513f71d0a05SDoug Thompson 
1514f71d0a05SDoug Thompson 	/*
1515f71d0a05SDoug Thompson 	 * This assumes that one node's DHAR is the same as all the other
1516f71d0a05SDoug Thompson 	 * nodes' DHAR.
1517f71d0a05SDoug Thompson 	 */
1518f71d0a05SDoug Thompson 	hole_off = (pvt->dhar & 0x0000FF80);
1519f71d0a05SDoug Thompson 	hole_valid = (pvt->dhar & 0x1);
1520f71d0a05SDoug Thompson 	dct_sel_base_off = (pvt->dram_ctl_select_high & 0xFFFFFC00) << 16;
1521f71d0a05SDoug Thompson 
1522f71d0a05SDoug Thompson 	debugf1("   HoleOffset=0x%x  HoleValid=0x%x IntlvSel=0x%x\n",
1523f71d0a05SDoug Thompson 			hole_off, hole_valid, intlv_sel);
1524f71d0a05SDoug Thompson 
1525e726f3c3SBorislav Petkov 	if (intlv_en &&
1526f71d0a05SDoug Thompson 	    (intlv_sel != ((sys_addr >> 12) & intlv_en)))
1527f71d0a05SDoug Thompson 		return -EINVAL;
1528f71d0a05SDoug Thompson 
1529f71d0a05SDoug Thompson 	dct_sel_base = dct_sel_baseaddr(pvt);
1530f71d0a05SDoug Thompson 
1531f71d0a05SDoug Thompson 	/*
1532f71d0a05SDoug Thompson 	 * check whether addresses >= DctSelBaseAddr[47:27] are to be used to
1533f71d0a05SDoug Thompson 	 * select between DCT0 and DCT1.
1534f71d0a05SDoug Thompson 	 */
1535f71d0a05SDoug Thompson 	if (dct_high_range_enabled(pvt) &&
1536f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt) &&
1537f71d0a05SDoug Thompson 	   ((sys_addr >> 27) >= (dct_sel_base >> 11)))
1538f71d0a05SDoug Thompson 		high_range = 1;
1539f71d0a05SDoug Thompson 
1540f71d0a05SDoug Thompson 	channel = f10_determine_channel(pvt, sys_addr, high_range, intlv_en);
1541f71d0a05SDoug Thompson 
1542f71d0a05SDoug Thompson 	chan_addr = f10_get_base_addr_offset(sys_addr, high_range, dct_sel_base,
1543f71d0a05SDoug Thompson 					     dct_sel_base_off, hole_valid,
1544f71d0a05SDoug Thompson 					     hole_off, dram_base);
1545f71d0a05SDoug Thompson 
1546f71d0a05SDoug Thompson 	intlv_shift = f10_map_intlv_en_to_shift(intlv_en);
1547f71d0a05SDoug Thompson 
1548f71d0a05SDoug Thompson 	/* remove Node ID (in case of memory interleaving) */
1549f71d0a05SDoug Thompson 	tmp = chan_addr & 0xFC0;
1550f71d0a05SDoug Thompson 
1551f71d0a05SDoug Thompson 	chan_addr = ((chan_addr >> intlv_shift) & 0xFFFFFFFFF000ULL) | tmp;
1552f71d0a05SDoug Thompson 
1553f71d0a05SDoug Thompson 	/* remove channel interleave and hash */
1554f71d0a05SDoug Thompson 	if (dct_interleave_enabled(pvt) &&
1555f71d0a05SDoug Thompson 	   !dct_high_range_enabled(pvt) &&
1556f71d0a05SDoug Thompson 	   !dct_ganging_enabled(pvt)) {
1557f71d0a05SDoug Thompson 		if (dct_sel_interleave_addr(pvt) != 1)
1558f71d0a05SDoug Thompson 			chan_addr = (chan_addr >> 1) & 0xFFFFFFFFFFFFFFC0ULL;
1559f71d0a05SDoug Thompson 		else {
1560f71d0a05SDoug Thompson 			tmp = chan_addr & 0xFC0;
1561f71d0a05SDoug Thompson 			chan_addr = ((chan_addr & 0xFFFFFFFFFFFFC000ULL) >> 1)
1562f71d0a05SDoug Thompson 					| tmp;
1563f71d0a05SDoug Thompson 		}
1564f71d0a05SDoug Thompson 	}
1565f71d0a05SDoug Thompson 
1566f71d0a05SDoug Thompson 	debugf1("   (ChannelAddrLong=0x%llx) >> 8 becomes InputAddr=0x%x\n",
1567f71d0a05SDoug Thompson 		chan_addr, (u32)(chan_addr >> 8));
1568f71d0a05SDoug Thompson 
1569f71d0a05SDoug Thompson 	cs_found = f10_lookup_addr_in_dct(chan_addr >> 8, node_id, channel);
1570f71d0a05SDoug Thompson 
1571f71d0a05SDoug Thompson 	if (cs_found >= 0) {
1572f71d0a05SDoug Thompson 		*nid = node_id;
1573f71d0a05SDoug Thompson 		*chan_sel = channel;
1574f71d0a05SDoug Thompson 	}
1575f71d0a05SDoug Thompson 	return cs_found;
1576f71d0a05SDoug Thompson }
1577f71d0a05SDoug Thompson 
1578f71d0a05SDoug Thompson static int f10_translate_sysaddr_to_cs(struct amd64_pvt *pvt, u64 sys_addr,
1579f71d0a05SDoug Thompson 				       int *node, int *chan_sel)
1580f71d0a05SDoug Thompson {
1581f71d0a05SDoug Thompson 	int dram_range, cs_found = -EINVAL;
1582f71d0a05SDoug Thompson 	u64 dram_base, dram_limit;
1583f71d0a05SDoug Thompson 
1584f71d0a05SDoug Thompson 	for (dram_range = 0; dram_range < DRAM_REG_COUNT; dram_range++) {
1585f71d0a05SDoug Thompson 
1586f71d0a05SDoug Thompson 		if (!pvt->dram_rw_en[dram_range])
1587f71d0a05SDoug Thompson 			continue;
1588f71d0a05SDoug Thompson 
1589f71d0a05SDoug Thompson 		dram_base = pvt->dram_base[dram_range];
1590f71d0a05SDoug Thompson 		dram_limit = pvt->dram_limit[dram_range];
1591f71d0a05SDoug Thompson 
1592f71d0a05SDoug Thompson 		if ((dram_base <= sys_addr) && (sys_addr <= dram_limit)) {
1593f71d0a05SDoug Thompson 
1594f71d0a05SDoug Thompson 			cs_found = f10_match_to_this_node(pvt, dram_range,
1595f71d0a05SDoug Thompson 							  sys_addr, node,
1596f71d0a05SDoug Thompson 							  chan_sel);
1597f71d0a05SDoug Thompson 			if (cs_found >= 0)
1598f71d0a05SDoug Thompson 				break;
1599f71d0a05SDoug Thompson 		}
1600f71d0a05SDoug Thompson 	}
1601f71d0a05SDoug Thompson 	return cs_found;
1602f71d0a05SDoug Thompson }
1603f71d0a05SDoug Thompson 
1604f71d0a05SDoug Thompson /*
1605bdc30a0cSBorislav Petkov  * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
1606bdc30a0cSBorislav Petkov  * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
1607f71d0a05SDoug Thompson  *
1608bdc30a0cSBorislav Petkov  * The @sys_addr is usually an error address received from the hardware
1609bdc30a0cSBorislav Petkov  * (MCX_ADDR).
1610f71d0a05SDoug Thompson  */
1611f71d0a05SDoug Thompson static void f10_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
1612ad6a32e9SBorislav Petkov 				     struct err_regs *err_info,
1613f71d0a05SDoug Thompson 				     u64 sys_addr)
1614f71d0a05SDoug Thompson {
1615f71d0a05SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
1616f71d0a05SDoug Thompson 	u32 page, offset;
1617f71d0a05SDoug Thompson 	int nid, csrow, chan = 0;
1618ad6a32e9SBorislav Petkov 	u16 syndrome;
1619f71d0a05SDoug Thompson 
1620f71d0a05SDoug Thompson 	csrow = f10_translate_sysaddr_to_cs(pvt, sys_addr, &nid, &chan);
1621f71d0a05SDoug Thompson 
1622bdc30a0cSBorislav Petkov 	if (csrow < 0) {
1623bdc30a0cSBorislav Petkov 		edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
1624bdc30a0cSBorislav Petkov 		return;
1625bdc30a0cSBorislav Petkov 	}
1626bdc30a0cSBorislav Petkov 
1627f71d0a05SDoug Thompson 	error_address_to_page_and_offset(sys_addr, &page, &offset);
1628f71d0a05SDoug Thompson 
1629ad6a32e9SBorislav Petkov 	syndrome = extract_syndrome(err_info);
1630f71d0a05SDoug Thompson 
1631f71d0a05SDoug Thompson 	/*
1632bdc30a0cSBorislav Petkov 	 * We need the syndromes for channel detection only when we're
1633bdc30a0cSBorislav Petkov 	 * ganged. Otherwise @chan should already contain the channel at
1634bdc30a0cSBorislav Petkov 	 * this point.
1635f71d0a05SDoug Thompson 	 */
1636962b70a1SBorislav Petkov 	if (dct_ganging_enabled(pvt) && (pvt->nbcfg & K8_NBCFG_CHIPKILL))
1637bfc04aecSBorislav Petkov 		chan = get_channel_from_ecc_syndrome(mci, syndrome);
1638f71d0a05SDoug Thompson 
1639bdc30a0cSBorislav Petkov 	if (chan >= 0)
1640bdc30a0cSBorislav Petkov 		edac_mc_handle_ce(mci, page, offset, syndrome, csrow, chan,
1641bdc30a0cSBorislav Petkov 				  EDAC_MOD_STR);
1642bdc30a0cSBorislav Petkov 	else
1643bdc30a0cSBorislav Petkov 		/*
1644bdc30a0cSBorislav Petkov 		 * Channel unknown, report all channels on this CSROW as failed.
1645bdc30a0cSBorislav Petkov 		 */
1646bdc30a0cSBorislav Petkov 		for (chan = 0; chan < mci->csrows[csrow].nr_channels; chan++)
1647f71d0a05SDoug Thompson 			edac_mc_handle_ce(mci, page, offset, syndrome,
1648f71d0a05SDoug Thompson 					  csrow, chan, EDAC_MOD_STR);
1649f71d0a05SDoug Thompson }
1650f71d0a05SDoug Thompson 
1651f71d0a05SDoug Thompson /*
16528566c4dfSBorislav Petkov  * debug routine to display the memory sizes of all logical DIMMs and its
1653f71d0a05SDoug Thompson  * CSROWs as well
1654f71d0a05SDoug Thompson  */
16558566c4dfSBorislav Petkov static void amd64_debug_display_dimm_sizes(int ctrl, struct amd64_pvt *pvt)
1656f71d0a05SDoug Thompson {
1657603adaf6SBorislav Petkov 	int dimm, size0, size1, factor = 0;
1658f71d0a05SDoug Thompson 	u32 dbam;
1659f71d0a05SDoug Thompson 	u32 *dcsb;
1660f71d0a05SDoug Thompson 
16618566c4dfSBorislav Petkov 	if (boot_cpu_data.x86 == 0xf) {
1662603adaf6SBorislav Petkov 		if (pvt->dclr0 & F10_WIDTH_128)
1663603adaf6SBorislav Petkov 			factor = 1;
1664603adaf6SBorislav Petkov 
16658566c4dfSBorislav Petkov 		/* K8 families < revF not supported yet */
16661433eb99SBorislav Petkov 	       if (pvt->ext_model < K8_REV_F)
16678566c4dfSBorislav Petkov 			return;
16688566c4dfSBorislav Petkov 	       else
16698566c4dfSBorislav Petkov 		       WARN_ON(ctrl != 0);
16708566c4dfSBorislav Petkov 	}
16718566c4dfSBorislav Petkov 
16728566c4dfSBorislav Petkov 	debugf1("F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
16738566c4dfSBorislav Petkov 		ctrl, ctrl ? pvt->dbam1 : pvt->dbam0);
1674f71d0a05SDoug Thompson 
1675f71d0a05SDoug Thompson 	dbam = ctrl ? pvt->dbam1 : pvt->dbam0;
1676f71d0a05SDoug Thompson 	dcsb = ctrl ? pvt->dcsb1 : pvt->dcsb0;
1677f71d0a05SDoug Thompson 
16788566c4dfSBorislav Petkov 	edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
16798566c4dfSBorislav Petkov 
1680f71d0a05SDoug Thompson 	/* Dump memory sizes for DIMM and its CSROWs */
1681f71d0a05SDoug Thompson 	for (dimm = 0; dimm < 4; dimm++) {
1682f71d0a05SDoug Thompson 
1683f71d0a05SDoug Thompson 		size0 = 0;
1684f71d0a05SDoug Thompson 		if (dcsb[dimm*2] & K8_DCSB_CS_ENABLE)
16851433eb99SBorislav Petkov 			size0 = pvt->ops->dbam_to_cs(pvt, DBAM_DIMM(dimm, dbam));
1686f71d0a05SDoug Thompson 
1687f71d0a05SDoug Thompson 		size1 = 0;
1688f71d0a05SDoug Thompson 		if (dcsb[dimm*2 + 1] & K8_DCSB_CS_ENABLE)
16891433eb99SBorislav Petkov 			size1 = pvt->ops->dbam_to_cs(pvt, DBAM_DIMM(dimm, dbam));
1690f71d0a05SDoug Thompson 
169124f9a7feSBorislav Petkov 		amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
1692603adaf6SBorislav Petkov 				dimm * 2,     size0 << factor,
1693603adaf6SBorislav Petkov 				dimm * 2 + 1, size1 << factor);
1694f71d0a05SDoug Thompson 	}
1695f71d0a05SDoug Thompson }
1696f71d0a05SDoug Thompson 
16974d37607aSDoug Thompson static struct amd64_family_type amd64_family_types[] = {
16984d37607aSDoug Thompson 	[K8_CPUS] = {
16990092b20dSBorislav Petkov 		.ctl_name = "K8",
17008d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
17018d5b5d9cSBorislav Petkov 		.f3_id = PCI_DEVICE_ID_AMD_K8_NB_MISC,
17024d37607aSDoug Thompson 		.ops = {
17034d37607aSDoug Thompson 			.early_channel_count	= k8_early_channel_count,
17044d37607aSDoug Thompson 			.get_error_address	= k8_get_error_address,
17054d37607aSDoug Thompson 			.read_dram_base_limit	= k8_read_dram_base_limit,
17064d37607aSDoug Thompson 			.map_sysaddr_to_csrow	= k8_map_sysaddr_to_csrow,
17071433eb99SBorislav Petkov 			.dbam_to_cs		= k8_dbam_to_chip_select,
17084d37607aSDoug Thompson 		}
17094d37607aSDoug Thompson 	},
17104d37607aSDoug Thompson 	[F10_CPUS] = {
17110092b20dSBorislav Petkov 		.ctl_name = "F10h",
17128d5b5d9cSBorislav Petkov 		.f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP,
17138d5b5d9cSBorislav Petkov 		.f3_id = PCI_DEVICE_ID_AMD_10H_NB_MISC,
17144d37607aSDoug Thompson 		.ops = {
17154d37607aSDoug Thompson 			.early_channel_count	= f10_early_channel_count,
17164d37607aSDoug Thompson 			.get_error_address	= f10_get_error_address,
17174d37607aSDoug Thompson 			.read_dram_base_limit	= f10_read_dram_base_limit,
17184d37607aSDoug Thompson 			.read_dram_ctl_register	= f10_read_dram_ctl_register,
17194d37607aSDoug Thompson 			.map_sysaddr_to_csrow	= f10_map_sysaddr_to_csrow,
17201433eb99SBorislav Petkov 			.dbam_to_cs		= f10_dbam_to_chip_select,
17214d37607aSDoug Thompson 		}
17224d37607aSDoug Thompson 	},
17234d37607aSDoug Thompson };
17244d37607aSDoug Thompson 
17254d37607aSDoug Thompson static struct pci_dev *pci_get_related_function(unsigned int vendor,
17264d37607aSDoug Thompson 						unsigned int device,
17274d37607aSDoug Thompson 						struct pci_dev *related)
17284d37607aSDoug Thompson {
17294d37607aSDoug Thompson 	struct pci_dev *dev = NULL;
17304d37607aSDoug Thompson 
17314d37607aSDoug Thompson 	dev = pci_get_device(vendor, device, dev);
17324d37607aSDoug Thompson 	while (dev) {
17334d37607aSDoug Thompson 		if ((dev->bus->number == related->bus->number) &&
17344d37607aSDoug Thompson 		    (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn)))
17354d37607aSDoug Thompson 			break;
17364d37607aSDoug Thompson 		dev = pci_get_device(vendor, device, dev);
17374d37607aSDoug Thompson 	}
17384d37607aSDoug Thompson 
17394d37607aSDoug Thompson 	return dev;
17404d37607aSDoug Thompson }
17414d37607aSDoug Thompson 
1742b1289d6fSDoug Thompson /*
1743bfc04aecSBorislav Petkov  * These are tables of eigenvectors (one per line) which can be used for the
1744bfc04aecSBorislav Petkov  * construction of the syndrome tables. The modified syndrome search algorithm
1745bfc04aecSBorislav Petkov  * uses those to find the symbol in error and thus the DIMM.
1746b1289d6fSDoug Thompson  *
1747bfc04aecSBorislav Petkov  * Algorithm courtesy of Ross LaFetra from AMD.
1748b1289d6fSDoug Thompson  */
1749bfc04aecSBorislav Petkov static u16 x4_vectors[] = {
1750bfc04aecSBorislav Petkov 	0x2f57, 0x1afe, 0x66cc, 0xdd88,
1751bfc04aecSBorislav Petkov 	0x11eb, 0x3396, 0x7f4c, 0xeac8,
1752bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008,
1753bfc04aecSBorislav Petkov 	0x1013, 0x3032, 0x4044, 0x8088,
1754bfc04aecSBorislav Petkov 	0x106b, 0x30d6, 0x70fc, 0xe0a8,
1755bfc04aecSBorislav Petkov 	0x4857, 0xc4fe, 0x13cc, 0x3288,
1756bfc04aecSBorislav Petkov 	0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
1757bfc04aecSBorislav Petkov 	0x1f39, 0x251e, 0xbd6c, 0x6bd8,
1758bfc04aecSBorislav Petkov 	0x15c1, 0x2a42, 0x89ac, 0x4758,
1759bfc04aecSBorislav Petkov 	0x2b03, 0x1602, 0x4f0c, 0xca08,
1760bfc04aecSBorislav Petkov 	0x1f07, 0x3a0e, 0x6b04, 0xbd08,
1761bfc04aecSBorislav Petkov 	0x8ba7, 0x465e, 0x244c, 0x1cc8,
1762bfc04aecSBorislav Petkov 	0x2b87, 0x164e, 0x642c, 0xdc18,
1763bfc04aecSBorislav Petkov 	0x40b9, 0x80de, 0x1094, 0x20e8,
1764bfc04aecSBorislav Petkov 	0x27db, 0x1eb6, 0x9dac, 0x7b58,
1765bfc04aecSBorislav Petkov 	0x11c1, 0x2242, 0x84ac, 0x4c58,
1766bfc04aecSBorislav Petkov 	0x1be5, 0x2d7a, 0x5e34, 0xa718,
1767bfc04aecSBorislav Petkov 	0x4b39, 0x8d1e, 0x14b4, 0x28d8,
1768bfc04aecSBorislav Petkov 	0x4c97, 0xc87e, 0x11fc, 0x33a8,
1769bfc04aecSBorislav Petkov 	0x8e97, 0x497e, 0x2ffc, 0x1aa8,
1770bfc04aecSBorislav Petkov 	0x16b3, 0x3d62, 0x4f34, 0x8518,
1771bfc04aecSBorislav Petkov 	0x1e2f, 0x391a, 0x5cac, 0xf858,
1772bfc04aecSBorislav Petkov 	0x1d9f, 0x3b7a, 0x572c, 0xfe18,
1773bfc04aecSBorislav Petkov 	0x15f5, 0x2a5a, 0x5264, 0xa3b8,
1774bfc04aecSBorislav Petkov 	0x1dbb, 0x3b66, 0x715c, 0xe3f8,
1775bfc04aecSBorislav Petkov 	0x4397, 0xc27e, 0x17fc, 0x3ea8,
1776bfc04aecSBorislav Petkov 	0x1617, 0x3d3e, 0x6464, 0xb8b8,
1777bfc04aecSBorislav Petkov 	0x23ff, 0x12aa, 0xab6c, 0x56d8,
1778bfc04aecSBorislav Petkov 	0x2dfb, 0x1ba6, 0x913c, 0x7328,
1779bfc04aecSBorislav Petkov 	0x185d, 0x2ca6, 0x7914, 0x9e28,
1780bfc04aecSBorislav Petkov 	0x171b, 0x3e36, 0x7d7c, 0xebe8,
1781bfc04aecSBorislav Petkov 	0x4199, 0x82ee, 0x19f4, 0x2e58,
1782bfc04aecSBorislav Petkov 	0x4807, 0xc40e, 0x130c, 0x3208,
1783bfc04aecSBorislav Petkov 	0x1905, 0x2e0a, 0x5804, 0xac08,
1784bfc04aecSBorislav Petkov 	0x213f, 0x132a, 0xadfc, 0x5ba8,
1785bfc04aecSBorislav Petkov 	0x19a9, 0x2efe, 0xb5cc, 0x6f88,
1786b1289d6fSDoug Thompson };
1787b1289d6fSDoug Thompson 
1788bfc04aecSBorislav Petkov static u16 x8_vectors[] = {
1789bfc04aecSBorislav Petkov 	0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
1790bfc04aecSBorislav Petkov 	0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
1791bfc04aecSBorislav Petkov 	0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
1792bfc04aecSBorislav Petkov 	0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
1793bfc04aecSBorislav Petkov 	0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
1794bfc04aecSBorislav Petkov 	0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
1795bfc04aecSBorislav Petkov 	0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
1796bfc04aecSBorislav Petkov 	0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
1797bfc04aecSBorislav Petkov 	0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
1798bfc04aecSBorislav Petkov 	0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
1799bfc04aecSBorislav Petkov 	0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
1800bfc04aecSBorislav Petkov 	0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
1801bfc04aecSBorislav Petkov 	0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
1802bfc04aecSBorislav Petkov 	0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
1803bfc04aecSBorislav Petkov 	0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
1804bfc04aecSBorislav Petkov 	0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
1805bfc04aecSBorislav Petkov 	0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
1806bfc04aecSBorislav Petkov 	0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
1807bfc04aecSBorislav Petkov 	0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
1808bfc04aecSBorislav Petkov };
1809bfc04aecSBorislav Petkov 
1810bfc04aecSBorislav Petkov static int decode_syndrome(u16 syndrome, u16 *vectors, int num_vecs,
1811bfc04aecSBorislav Petkov 			   int v_dim)
1812b1289d6fSDoug Thompson {
1813bfc04aecSBorislav Petkov 	unsigned int i, err_sym;
1814b1289d6fSDoug Thompson 
1815bfc04aecSBorislav Petkov 	for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
1816bfc04aecSBorislav Petkov 		u16 s = syndrome;
1817bfc04aecSBorislav Petkov 		int v_idx =  err_sym * v_dim;
1818bfc04aecSBorislav Petkov 		int v_end = (err_sym + 1) * v_dim;
1819b1289d6fSDoug Thompson 
1820bfc04aecSBorislav Petkov 		/* walk over all 16 bits of the syndrome */
1821bfc04aecSBorislav Petkov 		for (i = 1; i < (1U << 16); i <<= 1) {
1822bfc04aecSBorislav Petkov 
1823bfc04aecSBorislav Petkov 			/* if bit is set in that eigenvector... */
1824bfc04aecSBorislav Petkov 			if (v_idx < v_end && vectors[v_idx] & i) {
1825bfc04aecSBorislav Petkov 				u16 ev_comp = vectors[v_idx++];
1826bfc04aecSBorislav Petkov 
1827bfc04aecSBorislav Petkov 				/* ... and bit set in the modified syndrome, */
1828bfc04aecSBorislav Petkov 				if (s & i) {
1829bfc04aecSBorislav Petkov 					/* remove it. */
1830bfc04aecSBorislav Petkov 					s ^= ev_comp;
1831bfc04aecSBorislav Petkov 
1832bfc04aecSBorislav Petkov 					if (!s)
1833bfc04aecSBorislav Petkov 						return err_sym;
1834bfc04aecSBorislav Petkov 				}
1835bfc04aecSBorislav Petkov 
1836bfc04aecSBorislav Petkov 			} else if (s & i)
1837bfc04aecSBorislav Petkov 				/* can't get to zero, move to next symbol */
1838bfc04aecSBorislav Petkov 				break;
1839bfc04aecSBorislav Petkov 		}
1840b1289d6fSDoug Thompson 	}
1841b1289d6fSDoug Thompson 
1842b1289d6fSDoug Thompson 	debugf0("syndrome(%x) not found\n", syndrome);
1843b1289d6fSDoug Thompson 	return -1;
1844b1289d6fSDoug Thompson }
1845d27bf6faSDoug Thompson 
1846bfc04aecSBorislav Petkov static int map_err_sym_to_channel(int err_sym, int sym_size)
1847bfc04aecSBorislav Petkov {
1848bfc04aecSBorislav Petkov 	if (sym_size == 4)
1849bfc04aecSBorislav Petkov 		switch (err_sym) {
1850bfc04aecSBorislav Petkov 		case 0x20:
1851bfc04aecSBorislav Petkov 		case 0x21:
1852bfc04aecSBorislav Petkov 			return 0;
1853bfc04aecSBorislav Petkov 			break;
1854bfc04aecSBorislav Petkov 		case 0x22:
1855bfc04aecSBorislav Petkov 		case 0x23:
1856bfc04aecSBorislav Petkov 			return 1;
1857bfc04aecSBorislav Petkov 			break;
1858bfc04aecSBorislav Petkov 		default:
1859bfc04aecSBorislav Petkov 			return err_sym >> 4;
1860bfc04aecSBorislav Petkov 			break;
1861bfc04aecSBorislav Petkov 		}
1862bfc04aecSBorislav Petkov 	/* x8 symbols */
1863bfc04aecSBorislav Petkov 	else
1864bfc04aecSBorislav Petkov 		switch (err_sym) {
1865bfc04aecSBorislav Petkov 		/* imaginary bits not in a DIMM */
1866bfc04aecSBorislav Petkov 		case 0x10:
1867bfc04aecSBorislav Petkov 			WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
1868bfc04aecSBorislav Petkov 					  err_sym);
1869bfc04aecSBorislav Petkov 			return -1;
1870bfc04aecSBorislav Petkov 			break;
1871bfc04aecSBorislav Petkov 
1872bfc04aecSBorislav Petkov 		case 0x11:
1873bfc04aecSBorislav Petkov 			return 0;
1874bfc04aecSBorislav Petkov 			break;
1875bfc04aecSBorislav Petkov 		case 0x12:
1876bfc04aecSBorislav Petkov 			return 1;
1877bfc04aecSBorislav Petkov 			break;
1878bfc04aecSBorislav Petkov 		default:
1879bfc04aecSBorislav Petkov 			return err_sym >> 3;
1880bfc04aecSBorislav Petkov 			break;
1881bfc04aecSBorislav Petkov 		}
1882bfc04aecSBorislav Petkov 	return -1;
1883bfc04aecSBorislav Petkov }
1884bfc04aecSBorislav Petkov 
1885bfc04aecSBorislav Petkov static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
1886bfc04aecSBorislav Petkov {
1887bfc04aecSBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
1888ad6a32e9SBorislav Petkov 	int err_sym = -1;
1889bfc04aecSBorislav Petkov 
1890ad6a32e9SBorislav Petkov 	if (pvt->syn_type == 8)
1891bfc04aecSBorislav Petkov 		err_sym = decode_syndrome(syndrome, x8_vectors,
1892ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x8_vectors),
1893ad6a32e9SBorislav Petkov 					  pvt->syn_type);
1894ad6a32e9SBorislav Petkov 	else if (pvt->syn_type == 4)
1895ad6a32e9SBorislav Petkov 		err_sym = decode_syndrome(syndrome, x4_vectors,
1896ad6a32e9SBorislav Petkov 					  ARRAY_SIZE(x4_vectors),
1897ad6a32e9SBorislav Petkov 					  pvt->syn_type);
1898ad6a32e9SBorislav Petkov 	else {
189924f9a7feSBorislav Petkov 		amd64_warn("Illegal syndrome type: %u\n", pvt->syn_type);
1900ad6a32e9SBorislav Petkov 		return err_sym;
1901bfc04aecSBorislav Petkov 	}
1902ad6a32e9SBorislav Petkov 
1903ad6a32e9SBorislav Petkov 	return map_err_sym_to_channel(err_sym, pvt->syn_type);
190441c31044SBorislav Petkov }
1905bfc04aecSBorislav Petkov 
1906d27bf6faSDoug Thompson /*
1907d27bf6faSDoug Thompson  * Handle any Correctable Errors (CEs) that have occurred. Check for valid ERROR
1908d27bf6faSDoug Thompson  * ADDRESS and process.
1909d27bf6faSDoug Thompson  */
1910d27bf6faSDoug Thompson static void amd64_handle_ce(struct mem_ctl_info *mci,
1911ef44cc4cSBorislav Petkov 			    struct err_regs *info)
1912d27bf6faSDoug Thompson {
1913d27bf6faSDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
191444e9e2eeSBorislav Petkov 	u64 sys_addr;
1915d27bf6faSDoug Thompson 
1916d27bf6faSDoug Thompson 	/* Ensure that the Error Address is VALID */
191724f9a7feSBorislav Petkov 	if (!(info->nbsh & K8_NBSH_VALID_ERROR_ADDR)) {
191824f9a7feSBorislav Petkov 		amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n");
1919d27bf6faSDoug Thompson 		edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
1920d27bf6faSDoug Thompson 		return;
1921d27bf6faSDoug Thompson 	}
1922d27bf6faSDoug Thompson 
19231f6bcee7SBorislav Petkov 	sys_addr = pvt->ops->get_error_address(mci, info);
1924d27bf6faSDoug Thompson 
192524f9a7feSBorislav Petkov 	amd64_mc_err(mci, "CE ERROR_ADDRESS= 0x%llx\n", sys_addr);
1926d27bf6faSDoug Thompson 
192744e9e2eeSBorislav Petkov 	pvt->ops->map_sysaddr_to_csrow(mci, info, sys_addr);
1928d27bf6faSDoug Thompson }
1929d27bf6faSDoug Thompson 
1930d27bf6faSDoug Thompson /* Handle any Un-correctable Errors (UEs) */
1931d27bf6faSDoug Thompson static void amd64_handle_ue(struct mem_ctl_info *mci,
1932ef44cc4cSBorislav Petkov 			    struct err_regs *info)
1933d27bf6faSDoug Thompson {
19341f6bcee7SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
19351f6bcee7SBorislav Petkov 	struct mem_ctl_info *log_mci, *src_mci = NULL;
1936d27bf6faSDoug Thompson 	int csrow;
193744e9e2eeSBorislav Petkov 	u64 sys_addr;
1938d27bf6faSDoug Thompson 	u32 page, offset;
1939d27bf6faSDoug Thompson 
1940d27bf6faSDoug Thompson 	log_mci = mci;
1941d27bf6faSDoug Thompson 
194224f9a7feSBorislav Petkov 	if (!(info->nbsh & K8_NBSH_VALID_ERROR_ADDR)) {
194324f9a7feSBorislav Petkov 		amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n");
1944d27bf6faSDoug Thompson 		edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
1945d27bf6faSDoug Thompson 		return;
1946d27bf6faSDoug Thompson 	}
1947d27bf6faSDoug Thompson 
19481f6bcee7SBorislav Petkov 	sys_addr = pvt->ops->get_error_address(mci, info);
1949d27bf6faSDoug Thompson 
1950d27bf6faSDoug Thompson 	/*
1951d27bf6faSDoug Thompson 	 * Find out which node the error address belongs to. This may be
1952d27bf6faSDoug Thompson 	 * different from the node that detected the error.
1953d27bf6faSDoug Thompson 	 */
195444e9e2eeSBorislav Petkov 	src_mci = find_mc_by_sys_addr(mci, sys_addr);
1955d27bf6faSDoug Thompson 	if (!src_mci) {
195624f9a7feSBorislav Petkov 		amd64_mc_err(mci, "ERROR ADDRESS (0x%lx) NOT mapped to a MC\n",
195744e9e2eeSBorislav Petkov 				  (unsigned long)sys_addr);
1958d27bf6faSDoug Thompson 		edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
1959d27bf6faSDoug Thompson 		return;
1960d27bf6faSDoug Thompson 	}
1961d27bf6faSDoug Thompson 
1962d27bf6faSDoug Thompson 	log_mci = src_mci;
1963d27bf6faSDoug Thompson 
196444e9e2eeSBorislav Petkov 	csrow = sys_addr_to_csrow(log_mci, sys_addr);
1965d27bf6faSDoug Thompson 	if (csrow < 0) {
196624f9a7feSBorislav Petkov 		amd64_mc_err(mci, "ERROR_ADDRESS (0x%lx) NOT mapped to CS\n",
196744e9e2eeSBorislav Petkov 				  (unsigned long)sys_addr);
1968d27bf6faSDoug Thompson 		edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
1969d27bf6faSDoug Thompson 	} else {
197044e9e2eeSBorislav Petkov 		error_address_to_page_and_offset(sys_addr, &page, &offset);
1971d27bf6faSDoug Thompson 		edac_mc_handle_ue(log_mci, page, offset, csrow, EDAC_MOD_STR);
1972d27bf6faSDoug Thompson 	}
1973d27bf6faSDoug Thompson }
1974d27bf6faSDoug Thompson 
1975549d042dSBorislav Petkov static inline void __amd64_decode_bus_error(struct mem_ctl_info *mci,
1976b69b29deSBorislav Petkov 					    struct err_regs *info)
1977d27bf6faSDoug Thompson {
1978b70ef010SBorislav Petkov 	u32 ec  = ERROR_CODE(info->nbsl);
1979b70ef010SBorislav Petkov 	u32 xec = EXT_ERROR_CODE(info->nbsl);
198017adea01SBorislav Petkov 	int ecc_type = (info->nbsh >> 13) & 0x3;
1981d27bf6faSDoug Thompson 
1982b70ef010SBorislav Petkov 	/* Bail early out if this was an 'observed' error */
1983b70ef010SBorislav Petkov 	if (PP(ec) == K8_NBSL_PP_OBS)
1984b70ef010SBorislav Petkov 		return;
1985d27bf6faSDoug Thompson 
1986ecaf5606SBorislav Petkov 	/* Do only ECC errors */
1987ecaf5606SBorislav Petkov 	if (xec && xec != F10_NBSL_EXT_ERR_ECC)
1988d27bf6faSDoug Thompson 		return;
1989d27bf6faSDoug Thompson 
1990ecaf5606SBorislav Petkov 	if (ecc_type == 2)
1991d27bf6faSDoug Thompson 		amd64_handle_ce(mci, info);
1992ecaf5606SBorislav Petkov 	else if (ecc_type == 1)
1993d27bf6faSDoug Thompson 		amd64_handle_ue(mci, info);
1994d27bf6faSDoug Thompson }
1995d27bf6faSDoug Thompson 
19967cfd4a87SBorislav Petkov void amd64_decode_bus_error(int node_id, struct mce *m, u32 nbcfg)
1997d27bf6faSDoug Thompson {
1998549d042dSBorislav Petkov 	struct mem_ctl_info *mci = mci_lookup[node_id];
19997cfd4a87SBorislav Petkov 	struct err_regs regs;
2000d27bf6faSDoug Thompson 
20017cfd4a87SBorislav Petkov 	regs.nbsl  = (u32) m->status;
20027cfd4a87SBorislav Petkov 	regs.nbsh  = (u32)(m->status >> 32);
20037cfd4a87SBorislav Petkov 	regs.nbeal = (u32) m->addr;
20047cfd4a87SBorislav Petkov 	regs.nbeah = (u32)(m->addr >> 32);
20057cfd4a87SBorislav Petkov 	regs.nbcfg = nbcfg;
20067cfd4a87SBorislav Petkov 
20077cfd4a87SBorislav Petkov 	__amd64_decode_bus_error(mci, &regs);
2008d27bf6faSDoug Thompson 
2009d27bf6faSDoug Thompson 	/*
2010d27bf6faSDoug Thompson 	 * Check the UE bit of the NB status high register, if set generate some
2011d27bf6faSDoug Thompson 	 * logs. If NOT a GART error, then process the event as a NO-INFO event.
2012d27bf6faSDoug Thompson 	 * If it was a GART error, skip that process.
2013549d042dSBorislav Petkov 	 *
2014549d042dSBorislav Petkov 	 * FIXME: this should go somewhere else, if at all.
2015d27bf6faSDoug Thompson 	 */
20167cfd4a87SBorislav Petkov 	if (regs.nbsh & K8_NBSH_UC_ERR && !report_gart_errors)
20175110dbdeSBorislav Petkov 		edac_mc_handle_ue_no_info(mci, "UE bit is set");
2018549d042dSBorislav Petkov 
2019d27bf6faSDoug Thompson }
2020d27bf6faSDoug Thompson 
20210ec449eeSDoug Thompson /*
20228d5b5d9cSBorislav Petkov  * Use pvt->F2 which contains the F2 CPU PCI device to get the related
2023bbd0c1f6SBorislav Petkov  * F1 (AddrMap) and F3 (Misc) devices. Return negative value on error.
20240ec449eeSDoug Thompson  */
2025bbd0c1f6SBorislav Petkov static int amd64_reserve_mc_sibling_devices(struct amd64_pvt *pvt, u16 f1_id,
2026bbd0c1f6SBorislav Petkov 					    u16 f3_id)
20270ec449eeSDoug Thompson {
20280ec449eeSDoug Thompson 	/* Reserve the ADDRESS MAP Device */
20298d5b5d9cSBorislav Petkov 	pvt->F1 = pci_get_related_function(pvt->F2->vendor, f1_id, pvt->F2);
20308d5b5d9cSBorislav Petkov 	if (!pvt->F1) {
203124f9a7feSBorislav Petkov 		amd64_err("error address map device not found: "
20320ec449eeSDoug Thompson 			  "vendor %x device 0x%x (broken BIOS?)\n",
2033bbd0c1f6SBorislav Petkov 			  PCI_VENDOR_ID_AMD, f1_id);
2034bbd0c1f6SBorislav Petkov 		return -ENODEV;
20350ec449eeSDoug Thompson 	}
20360ec449eeSDoug Thompson 
20370ec449eeSDoug Thompson 	/* Reserve the MISC Device */
20388d5b5d9cSBorislav Petkov 	pvt->F3 = pci_get_related_function(pvt->F2->vendor, f3_id, pvt->F2);
20398d5b5d9cSBorislav Petkov 	if (!pvt->F3) {
20408d5b5d9cSBorislav Petkov 		pci_dev_put(pvt->F1);
20418d5b5d9cSBorislav Petkov 		pvt->F1 = NULL;
20420ec449eeSDoug Thompson 
204324f9a7feSBorislav Petkov 		amd64_err("error F3 device not found: "
20440ec449eeSDoug Thompson 			  "vendor %x device 0x%x (broken BIOS?)\n",
2045bbd0c1f6SBorislav Petkov 			  PCI_VENDOR_ID_AMD, f3_id);
20468d5b5d9cSBorislav Petkov 
2047bbd0c1f6SBorislav Petkov 		return -ENODEV;
20480ec449eeSDoug Thompson 	}
20498d5b5d9cSBorislav Petkov 	debugf1("F1: %s\n", pci_name(pvt->F1));
20508d5b5d9cSBorislav Petkov 	debugf1("F2: %s\n", pci_name(pvt->F2));
20518d5b5d9cSBorislav Petkov 	debugf1("F3: %s\n", pci_name(pvt->F3));
20520ec449eeSDoug Thompson 
20530ec449eeSDoug Thompson 	return 0;
20540ec449eeSDoug Thompson }
20550ec449eeSDoug Thompson 
20560ec449eeSDoug Thompson static void amd64_free_mc_sibling_devices(struct amd64_pvt *pvt)
20570ec449eeSDoug Thompson {
20588d5b5d9cSBorislav Petkov 	pci_dev_put(pvt->F1);
20598d5b5d9cSBorislav Petkov 	pci_dev_put(pvt->F3);
20600ec449eeSDoug Thompson }
20610ec449eeSDoug Thompson 
20620ec449eeSDoug Thompson /*
20630ec449eeSDoug Thompson  * Retrieve the hardware registers of the memory controller (this includes the
20640ec449eeSDoug Thompson  * 'Address Map' and 'Misc' device regs)
20650ec449eeSDoug Thompson  */
20660ec449eeSDoug Thompson static void amd64_read_mc_registers(struct amd64_pvt *pvt)
20670ec449eeSDoug Thompson {
20680ec449eeSDoug Thompson 	u64 msr_val;
2069ad6a32e9SBorislav Petkov 	u32 tmp;
20706ba5dcdcSBorislav Petkov 	int dram;
20710ec449eeSDoug Thompson 
20720ec449eeSDoug Thompson 	/*
20730ec449eeSDoug Thompson 	 * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
20740ec449eeSDoug Thompson 	 * those are Read-As-Zero
20750ec449eeSDoug Thompson 	 */
2076e97f8bb8SBorislav Petkov 	rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
2077e97f8bb8SBorislav Petkov 	debugf0("  TOP_MEM:  0x%016llx\n", pvt->top_mem);
20780ec449eeSDoug Thompson 
20790ec449eeSDoug Thompson 	/* check first whether TOP_MEM2 is enabled */
20800ec449eeSDoug Thompson 	rdmsrl(MSR_K8_SYSCFG, msr_val);
20810ec449eeSDoug Thompson 	if (msr_val & (1U << 21)) {
2082e97f8bb8SBorislav Petkov 		rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
2083e97f8bb8SBorislav Petkov 		debugf0("  TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
20840ec449eeSDoug Thompson 	} else
20850ec449eeSDoug Thompson 		debugf0("  TOP_MEM2 disabled.\n");
20860ec449eeSDoug Thompson 
20878d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, K8_NBCAP, &pvt->nbcap);
20880ec449eeSDoug Thompson 
20890ec449eeSDoug Thompson 	if (pvt->ops->read_dram_ctl_register)
20900ec449eeSDoug Thompson 		pvt->ops->read_dram_ctl_register(pvt);
20910ec449eeSDoug Thompson 
20920ec449eeSDoug Thompson 	for (dram = 0; dram < DRAM_REG_COUNT; dram++) {
20930ec449eeSDoug Thompson 		/*
20940ec449eeSDoug Thompson 		 * Call CPU specific READ function to get the DRAM Base and
20950ec449eeSDoug Thompson 		 * Limit values from the DCT.
20960ec449eeSDoug Thompson 		 */
20970ec449eeSDoug Thompson 		pvt->ops->read_dram_base_limit(pvt, dram);
20980ec449eeSDoug Thompson 
20990ec449eeSDoug Thompson 		/*
21000ec449eeSDoug Thompson 		 * Only print out debug info on rows with both R and W Enabled.
21010ec449eeSDoug Thompson 		 * Normal processing, compiler should optimize this whole 'if'
21020ec449eeSDoug Thompson 		 * debug output block away.
21030ec449eeSDoug Thompson 		 */
21040ec449eeSDoug Thompson 		if (pvt->dram_rw_en[dram] != 0) {
2105e97f8bb8SBorislav Petkov 			debugf1("  DRAM-BASE[%d]: 0x%016llx "
2106e97f8bb8SBorislav Petkov 				"DRAM-LIMIT:  0x%016llx\n",
21070ec449eeSDoug Thompson 				dram,
2108e97f8bb8SBorislav Petkov 				pvt->dram_base[dram],
2109e97f8bb8SBorislav Petkov 				pvt->dram_limit[dram]);
2110e97f8bb8SBorislav Petkov 
21110ec449eeSDoug Thompson 			debugf1("        IntlvEn=%s %s %s "
21120ec449eeSDoug Thompson 				"IntlvSel=%d DstNode=%d\n",
21130ec449eeSDoug Thompson 				pvt->dram_IntlvEn[dram] ?
21140ec449eeSDoug Thompson 					"Enabled" : "Disabled",
21150ec449eeSDoug Thompson 				(pvt->dram_rw_en[dram] & 0x2) ? "W" : "!W",
21160ec449eeSDoug Thompson 				(pvt->dram_rw_en[dram] & 0x1) ? "R" : "!R",
21170ec449eeSDoug Thompson 				pvt->dram_IntlvSel[dram],
21180ec449eeSDoug Thompson 				pvt->dram_DstNode[dram]);
21190ec449eeSDoug Thompson 		}
21200ec449eeSDoug Thompson 	}
21210ec449eeSDoug Thompson 
21220ec449eeSDoug Thompson 	amd64_read_dct_base_mask(pvt);
21230ec449eeSDoug Thompson 
21248d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F1, K8_DHAR, &pvt->dhar);
21250ec449eeSDoug Thompson 	amd64_read_dbam_reg(pvt);
21260ec449eeSDoug Thompson 
21278d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare);
21280ec449eeSDoug Thompson 
21298d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F2, F10_DCLR_0, &pvt->dclr0);
21308d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F2, F10_DCHR_0, &pvt->dchr0);
21310ec449eeSDoug Thompson 
2132ad6a32e9SBorislav Petkov 	if (boot_cpu_data.x86 >= 0x10) {
2133ad6a32e9SBorislav Petkov 		if (!dct_ganging_enabled(pvt)) {
21348d5b5d9cSBorislav Petkov 			amd64_read_pci_cfg(pvt->F2, F10_DCLR_1, &pvt->dclr1);
21358d5b5d9cSBorislav Petkov 			amd64_read_pci_cfg(pvt->F2, F10_DCHR_1, &pvt->dchr1);
21360ec449eeSDoug Thompson 		}
21378d5b5d9cSBorislav Petkov 		amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp);
2138ad6a32e9SBorislav Petkov 	}
2139ad6a32e9SBorislav Petkov 
2140ad6a32e9SBorislav Petkov 	if (boot_cpu_data.x86 == 0x10 &&
2141ad6a32e9SBorislav Petkov 	    boot_cpu_data.x86_model > 7 &&
2142ad6a32e9SBorislav Petkov 	    /* F3x180[EccSymbolSize]=1 => x8 symbols */
2143ad6a32e9SBorislav Petkov 	    tmp & BIT(25))
2144ad6a32e9SBorislav Petkov 		pvt->syn_type = 8;
2145ad6a32e9SBorislav Petkov 	else
2146ad6a32e9SBorislav Petkov 		pvt->syn_type = 4;
2147ad6a32e9SBorislav Petkov 
21480ec449eeSDoug Thompson 	amd64_dump_misc_regs(pvt);
21490ec449eeSDoug Thompson }
21500ec449eeSDoug Thompson 
21510ec449eeSDoug Thompson /*
21520ec449eeSDoug Thompson  * NOTE: CPU Revision Dependent code
21530ec449eeSDoug Thompson  *
21540ec449eeSDoug Thompson  * Input:
21559d858bb1SBorislav Petkov  *	@csrow_nr ChipSelect Row Number (0..pvt->cs_count-1)
21560ec449eeSDoug Thompson  *	k8 private pointer to -->
21570ec449eeSDoug Thompson  *			DRAM Bank Address mapping register
21580ec449eeSDoug Thompson  *			node_id
21590ec449eeSDoug Thompson  *			DCL register where dual_channel_active is
21600ec449eeSDoug Thompson  *
21610ec449eeSDoug Thompson  * The DBAM register consists of 4 sets of 4 bits each definitions:
21620ec449eeSDoug Thompson  *
21630ec449eeSDoug Thompson  * Bits:	CSROWs
21640ec449eeSDoug Thompson  * 0-3		CSROWs 0 and 1
21650ec449eeSDoug Thompson  * 4-7		CSROWs 2 and 3
21660ec449eeSDoug Thompson  * 8-11		CSROWs 4 and 5
21670ec449eeSDoug Thompson  * 12-15	CSROWs 6 and 7
21680ec449eeSDoug Thompson  *
21690ec449eeSDoug Thompson  * Values range from: 0 to 15
21700ec449eeSDoug Thompson  * The meaning of the values depends on CPU revision and dual-channel state,
21710ec449eeSDoug Thompson  * see relevant BKDG more info.
21720ec449eeSDoug Thompson  *
21730ec449eeSDoug Thompson  * The memory controller provides for total of only 8 CSROWs in its current
21740ec449eeSDoug Thompson  * architecture. Each "pair" of CSROWs normally represents just one DIMM in
21750ec449eeSDoug Thompson  * single channel or two (2) DIMMs in dual channel mode.
21760ec449eeSDoug Thompson  *
21770ec449eeSDoug Thompson  * The following code logic collapses the various tables for CSROW based on CPU
21780ec449eeSDoug Thompson  * revision.
21790ec449eeSDoug Thompson  *
21800ec449eeSDoug Thompson  * Returns:
21810ec449eeSDoug Thompson  *	The number of PAGE_SIZE pages on the specified CSROW number it
21820ec449eeSDoug Thompson  *	encompasses
21830ec449eeSDoug Thompson  *
21840ec449eeSDoug Thompson  */
21850ec449eeSDoug Thompson static u32 amd64_csrow_nr_pages(int csrow_nr, struct amd64_pvt *pvt)
21860ec449eeSDoug Thompson {
21871433eb99SBorislav Petkov 	u32 cs_mode, nr_pages;
21880ec449eeSDoug Thompson 
21890ec449eeSDoug Thompson 	/*
21900ec449eeSDoug Thompson 	 * The math on this doesn't look right on the surface because x/2*4 can
21910ec449eeSDoug Thompson 	 * be simplified to x*2 but this expression makes use of the fact that
21920ec449eeSDoug Thompson 	 * it is integral math where 1/2=0. This intermediate value becomes the
21930ec449eeSDoug Thompson 	 * number of bits to shift the DBAM register to extract the proper CSROW
21940ec449eeSDoug Thompson 	 * field.
21950ec449eeSDoug Thompson 	 */
21961433eb99SBorislav Petkov 	cs_mode = (pvt->dbam0 >> ((csrow_nr / 2) * 4)) & 0xF;
21970ec449eeSDoug Thompson 
21981433eb99SBorislav Petkov 	nr_pages = pvt->ops->dbam_to_cs(pvt, cs_mode) << (20 - PAGE_SHIFT);
21990ec449eeSDoug Thompson 
22000ec449eeSDoug Thompson 	/*
22010ec449eeSDoug Thompson 	 * If dual channel then double the memory size of single channel.
22020ec449eeSDoug Thompson 	 * Channel count is 1 or 2
22030ec449eeSDoug Thompson 	 */
22040ec449eeSDoug Thompson 	nr_pages <<= (pvt->channel_count - 1);
22050ec449eeSDoug Thompson 
22061433eb99SBorislav Petkov 	debugf0("  (csrow=%d) DBAM map index= %d\n", csrow_nr, cs_mode);
22070ec449eeSDoug Thompson 	debugf0("    nr_pages= %u  channel-count = %d\n",
22080ec449eeSDoug Thompson 		nr_pages, pvt->channel_count);
22090ec449eeSDoug Thompson 
22100ec449eeSDoug Thompson 	return nr_pages;
22110ec449eeSDoug Thompson }
22120ec449eeSDoug Thompson 
22130ec449eeSDoug Thompson /*
22140ec449eeSDoug Thompson  * Initialize the array of csrow attribute instances, based on the values
22150ec449eeSDoug Thompson  * from pci config hardware registers.
22160ec449eeSDoug Thompson  */
22170ec449eeSDoug Thompson static int amd64_init_csrows(struct mem_ctl_info *mci)
22180ec449eeSDoug Thompson {
22190ec449eeSDoug Thompson 	struct csrow_info *csrow;
22200ec449eeSDoug Thompson 	struct amd64_pvt *pvt;
22210ec449eeSDoug Thompson 	u64 input_addr_min, input_addr_max, sys_addr;
22226ba5dcdcSBorislav Petkov 	int i, empty = 1;
22230ec449eeSDoug Thompson 
22240ec449eeSDoug Thompson 	pvt = mci->pvt_info;
22250ec449eeSDoug Thompson 
22268d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, K8_NBCFG, &pvt->nbcfg);
22270ec449eeSDoug Thompson 
22280ec449eeSDoug Thompson 	debugf0("NBCFG= 0x%x  CHIPKILL= %s DRAM ECC= %s\n", pvt->nbcfg,
22290ec449eeSDoug Thompson 		(pvt->nbcfg & K8_NBCFG_CHIPKILL) ? "Enabled" : "Disabled",
22300ec449eeSDoug Thompson 		(pvt->nbcfg & K8_NBCFG_ECC_ENABLE) ? "Enabled" : "Disabled"
22310ec449eeSDoug Thompson 		);
22320ec449eeSDoug Thompson 
22339d858bb1SBorislav Petkov 	for (i = 0; i < pvt->cs_count; i++) {
22340ec449eeSDoug Thompson 		csrow = &mci->csrows[i];
22350ec449eeSDoug Thompson 
22360ec449eeSDoug Thompson 		if ((pvt->dcsb0[i] & K8_DCSB_CS_ENABLE) == 0) {
22370ec449eeSDoug Thompson 			debugf1("----CSROW %d EMPTY for node %d\n", i,
22380ec449eeSDoug Thompson 				pvt->mc_node_id);
22390ec449eeSDoug Thompson 			continue;
22400ec449eeSDoug Thompson 		}
22410ec449eeSDoug Thompson 
22420ec449eeSDoug Thompson 		debugf1("----CSROW %d VALID for MC node %d\n",
22430ec449eeSDoug Thompson 			i, pvt->mc_node_id);
22440ec449eeSDoug Thompson 
22450ec449eeSDoug Thompson 		empty = 0;
22460ec449eeSDoug Thompson 		csrow->nr_pages = amd64_csrow_nr_pages(i, pvt);
22470ec449eeSDoug Thompson 		find_csrow_limits(mci, i, &input_addr_min, &input_addr_max);
22480ec449eeSDoug Thompson 		sys_addr = input_addr_to_sys_addr(mci, input_addr_min);
22490ec449eeSDoug Thompson 		csrow->first_page = (u32) (sys_addr >> PAGE_SHIFT);
22500ec449eeSDoug Thompson 		sys_addr = input_addr_to_sys_addr(mci, input_addr_max);
22510ec449eeSDoug Thompson 		csrow->last_page = (u32) (sys_addr >> PAGE_SHIFT);
22520ec449eeSDoug Thompson 		csrow->page_mask = ~mask_from_dct_mask(pvt, i);
22530ec449eeSDoug Thompson 		/* 8 bytes of resolution */
22540ec449eeSDoug Thompson 
225524f9a7feSBorislav Petkov 		csrow->mtype = amd64_determine_memory_type(pvt, i);
22560ec449eeSDoug Thompson 
22570ec449eeSDoug Thompson 		debugf1("  for MC node %d csrow %d:\n", pvt->mc_node_id, i);
22580ec449eeSDoug Thompson 		debugf1("    input_addr_min: 0x%lx input_addr_max: 0x%lx\n",
22590ec449eeSDoug Thompson 			(unsigned long)input_addr_min,
22600ec449eeSDoug Thompson 			(unsigned long)input_addr_max);
22610ec449eeSDoug Thompson 		debugf1("    sys_addr: 0x%lx  page_mask: 0x%lx\n",
22620ec449eeSDoug Thompson 			(unsigned long)sys_addr, csrow->page_mask);
22630ec449eeSDoug Thompson 		debugf1("    nr_pages: %u  first_page: 0x%lx "
22640ec449eeSDoug Thompson 			"last_page: 0x%lx\n",
22650ec449eeSDoug Thompson 			(unsigned)csrow->nr_pages,
22660ec449eeSDoug Thompson 			csrow->first_page, csrow->last_page);
22670ec449eeSDoug Thompson 
22680ec449eeSDoug Thompson 		/*
22690ec449eeSDoug Thompson 		 * determine whether CHIPKILL or JUST ECC or NO ECC is operating
22700ec449eeSDoug Thompson 		 */
22710ec449eeSDoug Thompson 		if (pvt->nbcfg & K8_NBCFG_ECC_ENABLE)
22720ec449eeSDoug Thompson 			csrow->edac_mode =
22730ec449eeSDoug Thompson 			    (pvt->nbcfg & K8_NBCFG_CHIPKILL) ?
22740ec449eeSDoug Thompson 			    EDAC_S4ECD4ED : EDAC_SECDED;
22750ec449eeSDoug Thompson 		else
22760ec449eeSDoug Thompson 			csrow->edac_mode = EDAC_NONE;
22770ec449eeSDoug Thompson 	}
22780ec449eeSDoug Thompson 
22790ec449eeSDoug Thompson 	return empty;
22800ec449eeSDoug Thompson }
2281d27bf6faSDoug Thompson 
228206724535SBorislav Petkov /* get all cores on this DCT */
2283ba578cb3SRusty Russell static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, int nid)
2284f9431992SDoug Thompson {
228506724535SBorislav Petkov 	int cpu;
2286f9431992SDoug Thompson 
228706724535SBorislav Petkov 	for_each_online_cpu(cpu)
228806724535SBorislav Petkov 		if (amd_get_nb_id(cpu) == nid)
228906724535SBorislav Petkov 			cpumask_set_cpu(cpu, mask);
2290f9431992SDoug Thompson }
2291f9431992SDoug Thompson 
2292f9431992SDoug Thompson /* check MCG_CTL on all the cpus on this node */
229306724535SBorislav Petkov static bool amd64_nb_mce_bank_enabled_on_node(int nid)
2294f9431992SDoug Thompson {
2295ba578cb3SRusty Russell 	cpumask_var_t mask;
229650542251SBorislav Petkov 	int cpu, nbe;
229706724535SBorislav Petkov 	bool ret = false;
2298f9431992SDoug Thompson 
2299ba578cb3SRusty Russell 	if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
230024f9a7feSBorislav Petkov 		amd64_warn("%s: Error allocating mask\n", __func__);
230106724535SBorislav Petkov 		return false;
230206724535SBorislav Petkov 	}
230306724535SBorislav Petkov 
2304ba578cb3SRusty Russell 	get_cpus_on_this_dct_cpumask(mask, nid);
230506724535SBorislav Petkov 
2306ba578cb3SRusty Russell 	rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
2307ba578cb3SRusty Russell 
2308ba578cb3SRusty Russell 	for_each_cpu(cpu, mask) {
230950542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
231050542251SBorislav Petkov 		nbe = reg->l & K8_MSR_MCGCTL_NBE;
231106724535SBorislav Petkov 
231206724535SBorislav Petkov 		debugf0("core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
231350542251SBorislav Petkov 			cpu, reg->q,
231406724535SBorislav Petkov 			(nbe ? "enabled" : "disabled"));
231506724535SBorislav Petkov 
231606724535SBorislav Petkov 		if (!nbe)
231706724535SBorislav Petkov 			goto out;
231806724535SBorislav Petkov 	}
231906724535SBorislav Petkov 	ret = true;
232006724535SBorislav Petkov 
232106724535SBorislav Petkov out:
2322ba578cb3SRusty Russell 	free_cpumask_var(mask);
2323f9431992SDoug Thompson 	return ret;
2324f9431992SDoug Thompson }
2325f9431992SDoug Thompson 
2326f6d6ae96SBorislav Petkov static int amd64_toggle_ecc_err_reporting(struct amd64_pvt *pvt, bool on)
2327f6d6ae96SBorislav Petkov {
2328f6d6ae96SBorislav Petkov 	cpumask_var_t cmask;
232950542251SBorislav Petkov 	int cpu;
2330f6d6ae96SBorislav Petkov 
2331f6d6ae96SBorislav Petkov 	if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
233224f9a7feSBorislav Petkov 		amd64_warn("%s: error allocating mask\n", __func__);
2333f6d6ae96SBorislav Petkov 		return false;
2334f6d6ae96SBorislav Petkov 	}
2335f6d6ae96SBorislav Petkov 
2336f6d6ae96SBorislav Petkov 	get_cpus_on_this_dct_cpumask(cmask, pvt->mc_node_id);
2337f6d6ae96SBorislav Petkov 
2338f6d6ae96SBorislav Petkov 	rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2339f6d6ae96SBorislav Petkov 
2340f6d6ae96SBorislav Petkov 	for_each_cpu(cpu, cmask) {
2341f6d6ae96SBorislav Petkov 
234250542251SBorislav Petkov 		struct msr *reg = per_cpu_ptr(msrs, cpu);
234350542251SBorislav Petkov 
2344f6d6ae96SBorislav Petkov 		if (on) {
234550542251SBorislav Petkov 			if (reg->l & K8_MSR_MCGCTL_NBE)
2346d95cf4deSBorislav Petkov 				pvt->flags.nb_mce_enable = 1;
2347f6d6ae96SBorislav Petkov 
234850542251SBorislav Petkov 			reg->l |= K8_MSR_MCGCTL_NBE;
2349f6d6ae96SBorislav Petkov 		} else {
2350f6d6ae96SBorislav Petkov 			/*
2351d95cf4deSBorislav Petkov 			 * Turn off NB MCE reporting only when it was off before
2352f6d6ae96SBorislav Petkov 			 */
2353d95cf4deSBorislav Petkov 			if (!pvt->flags.nb_mce_enable)
235450542251SBorislav Petkov 				reg->l &= ~K8_MSR_MCGCTL_NBE;
2355f6d6ae96SBorislav Petkov 		}
2356f6d6ae96SBorislav Petkov 	}
2357f6d6ae96SBorislav Petkov 	wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
2358f6d6ae96SBorislav Petkov 
2359f6d6ae96SBorislav Petkov 	free_cpumask_var(cmask);
2360f6d6ae96SBorislav Petkov 
2361f6d6ae96SBorislav Petkov 	return 0;
2362f6d6ae96SBorislav Petkov }
2363f6d6ae96SBorislav Petkov 
2364f6d6ae96SBorislav Petkov static void amd64_enable_ecc_error_reporting(struct mem_ctl_info *mci)
2365f6d6ae96SBorislav Petkov {
2366f6d6ae96SBorislav Petkov 	struct amd64_pvt *pvt = mci->pvt_info;
2367f6d6ae96SBorislav Petkov 	u32 value, mask = K8_NBCTL_CECCEn | K8_NBCTL_UECCEn;
2368f6d6ae96SBorislav Petkov 
23698d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, K8_NBCTL, &value);
2370f6d6ae96SBorislav Petkov 
2371f6d6ae96SBorislav Petkov 	/* turn on UECCn and CECCEn bits */
2372f6d6ae96SBorislav Petkov 	pvt->old_nbctl = value & mask;
2373f6d6ae96SBorislav Petkov 	pvt->nbctl_mcgctl_saved = 1;
2374f6d6ae96SBorislav Petkov 
2375f6d6ae96SBorislav Petkov 	value |= mask;
23768d5b5d9cSBorislav Petkov 	pci_write_config_dword(pvt->F3, K8_NBCTL, value);
2377f6d6ae96SBorislav Petkov 
2378f6d6ae96SBorislav Petkov 	if (amd64_toggle_ecc_err_reporting(pvt, ON))
237924f9a7feSBorislav Petkov 		amd64_warn("Error enabling ECC reporting over MCGCTL!\n");
2380f6d6ae96SBorislav Petkov 
23818d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, K8_NBCFG, &value);
2382f6d6ae96SBorislav Petkov 
2383f6d6ae96SBorislav Petkov 	debugf0("NBCFG(1)= 0x%x  CHIPKILL= %s ECC_ENABLE= %s\n", value,
2384f6d6ae96SBorislav Petkov 		(value & K8_NBCFG_CHIPKILL) ? "Enabled" : "Disabled",
2385f6d6ae96SBorislav Petkov 		(value & K8_NBCFG_ECC_ENABLE) ? "Enabled" : "Disabled");
2386f6d6ae96SBorislav Petkov 
2387f6d6ae96SBorislav Petkov 	if (!(value & K8_NBCFG_ECC_ENABLE)) {
238824f9a7feSBorislav Petkov 		amd64_warn("DRAM ECC disabled on this node, enabling...\n");
2389f6d6ae96SBorislav Petkov 
2390d95cf4deSBorislav Petkov 		pvt->flags.nb_ecc_prev = 0;
2391d95cf4deSBorislav Petkov 
2392f6d6ae96SBorislav Petkov 		/* Attempt to turn on DRAM ECC Enable */
2393f6d6ae96SBorislav Petkov 		value |= K8_NBCFG_ECC_ENABLE;
23948d5b5d9cSBorislav Petkov 		pci_write_config_dword(pvt->F3, K8_NBCFG, value);
2395f6d6ae96SBorislav Petkov 
23968d5b5d9cSBorislav Petkov 		amd64_read_pci_cfg(pvt->F3, K8_NBCFG, &value);
2397f6d6ae96SBorislav Petkov 
2398f6d6ae96SBorislav Petkov 		if (!(value & K8_NBCFG_ECC_ENABLE)) {
239924f9a7feSBorislav Petkov 			amd64_warn("Hardware rejected DRAM ECC enable,"
240024f9a7feSBorislav Petkov 				   "check memory DIMM configuration.\n");
2401f6d6ae96SBorislav Petkov 		} else {
240224f9a7feSBorislav Petkov 			amd64_info("Hardware accepted DRAM ECC Enable\n");
2403f6d6ae96SBorislav Petkov 		}
2404d95cf4deSBorislav Petkov 	} else {
2405d95cf4deSBorislav Petkov 		pvt->flags.nb_ecc_prev = 1;
2406f6d6ae96SBorislav Petkov 	}
2407d95cf4deSBorislav Petkov 
2408f6d6ae96SBorislav Petkov 	debugf0("NBCFG(2)= 0x%x  CHIPKILL= %s ECC_ENABLE= %s\n", value,
2409f6d6ae96SBorislav Petkov 		(value & K8_NBCFG_CHIPKILL) ? "Enabled" : "Disabled",
2410f6d6ae96SBorislav Petkov 		(value & K8_NBCFG_ECC_ENABLE) ? "Enabled" : "Disabled");
2411f6d6ae96SBorislav Petkov 
2412f6d6ae96SBorislav Petkov 	pvt->ctl_error_info.nbcfg = value;
2413f6d6ae96SBorislav Petkov }
2414f6d6ae96SBorislav Petkov 
2415f6d6ae96SBorislav Petkov static void amd64_restore_ecc_error_reporting(struct amd64_pvt *pvt)
2416f6d6ae96SBorislav Petkov {
2417f6d6ae96SBorislav Petkov 	u32 value, mask = K8_NBCTL_CECCEn | K8_NBCTL_UECCEn;
2418f6d6ae96SBorislav Petkov 
2419f6d6ae96SBorislav Petkov 	if (!pvt->nbctl_mcgctl_saved)
2420f6d6ae96SBorislav Petkov 		return;
2421f6d6ae96SBorislav Petkov 
24228d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, K8_NBCTL, &value);
2423f6d6ae96SBorislav Petkov 	value &= ~mask;
2424f6d6ae96SBorislav Petkov 	value |= pvt->old_nbctl;
2425f6d6ae96SBorislav Petkov 
24268d5b5d9cSBorislav Petkov 	pci_write_config_dword(pvt->F3, K8_NBCTL, value);
2427f6d6ae96SBorislav Petkov 
2428d95cf4deSBorislav Petkov 	/* restore previous BIOS DRAM ECC "off" setting which we force-enabled */
2429d95cf4deSBorislav Petkov 	if (!pvt->flags.nb_ecc_prev) {
24308d5b5d9cSBorislav Petkov 		amd64_read_pci_cfg(pvt->F3, K8_NBCFG, &value);
2431d95cf4deSBorislav Petkov 		value &= ~K8_NBCFG_ECC_ENABLE;
24328d5b5d9cSBorislav Petkov 		pci_write_config_dword(pvt->F3, K8_NBCFG, value);
2433d95cf4deSBorislav Petkov 	}
2434d95cf4deSBorislav Petkov 
2435d95cf4deSBorislav Petkov 	/* restore the NB Enable MCGCTL bit */
2436f6d6ae96SBorislav Petkov 	if (amd64_toggle_ecc_err_reporting(pvt, OFF))
243724f9a7feSBorislav Petkov 		amd64_warn("Error restoring NB MCGCTL settings!\n");
2438f6d6ae96SBorislav Petkov }
2439f6d6ae96SBorislav Petkov 
2440f9431992SDoug Thompson /*
2441f9431992SDoug Thompson  * EDAC requires that the BIOS have ECC enabled before taking over the
2442f9431992SDoug Thompson  * processing of ECC errors. This is because the BIOS can properly initialize
2443f9431992SDoug Thompson  * the memory system completely. A command line option allows to force-enable
2444f9431992SDoug Thompson  * hardware ECC later in amd64_enable_ecc_error_reporting().
2445f9431992SDoug Thompson  */
2446cab4d277SBorislav Petkov static const char *ecc_msg =
2447cab4d277SBorislav Petkov 	"ECC disabled in the BIOS or no ECC capability, module will not load.\n"
2448cab4d277SBorislav Petkov 	" Either enable ECC checking or force module loading by setting "
2449cab4d277SBorislav Petkov 	"'ecc_enable_override'.\n"
2450cab4d277SBorislav Petkov 	" (Note that use of the override may cause unknown side effects.)\n";
2451be3468e8SBorislav Petkov 
2452f9431992SDoug Thompson static int amd64_check_ecc_enabled(struct amd64_pvt *pvt)
2453f9431992SDoug Thompson {
2454f9431992SDoug Thompson 	u32 value;
245506724535SBorislav Petkov 	u8 ecc_enabled = 0;
245606724535SBorislav Petkov 	bool nb_mce_en = false;
2457f9431992SDoug Thompson 
24588d5b5d9cSBorislav Petkov 	amd64_read_pci_cfg(pvt->F3, K8_NBCFG, &value);
2459f9431992SDoug Thompson 
2460f9431992SDoug Thompson 	ecc_enabled = !!(value & K8_NBCFG_ECC_ENABLE);
246124f9a7feSBorislav Petkov 	amd64_info("DRAM ECC %s.\n", (ecc_enabled ? "enabled" : "disabled"));
2462be3468e8SBorislav Petkov 
246306724535SBorislav Petkov 	nb_mce_en = amd64_nb_mce_bank_enabled_on_node(pvt->mc_node_id);
246406724535SBorislav Petkov 	if (!nb_mce_en)
246524f9a7feSBorislav Petkov 		amd64_notice("NB MCE bank disabled, "
246624f9a7feSBorislav Petkov 			     "set MSR 0x%08x[4] on node %d to enable.\n",
2467be3468e8SBorislav Petkov 			     MSR_IA32_MCG_CTL, pvt->mc_node_id);
2468be3468e8SBorislav Petkov 
246906724535SBorislav Petkov 	if (!ecc_enabled || !nb_mce_en) {
2470f9431992SDoug Thompson 		if (!ecc_enable_override) {
247124f9a7feSBorislav Petkov 			amd64_notice("%s", ecc_msg);
2472be3468e8SBorislav Petkov 			return -ENODEV;
2473d95cf4deSBorislav Petkov 		} else {
247424f9a7feSBorislav Petkov 			amd64_warn("Forcing ECC on!\n");
2475be3468e8SBorislav Petkov 		}
247643f5e687SBorislav Petkov 	}
2477f9431992SDoug Thompson 
2478be3468e8SBorislav Petkov 	return 0;
2479f9431992SDoug Thompson }
2480f9431992SDoug Thompson 
24817d6034d3SDoug Thompson struct mcidev_sysfs_attribute sysfs_attrs[ARRAY_SIZE(amd64_dbg_attrs) +
24827d6034d3SDoug Thompson 					  ARRAY_SIZE(amd64_inj_attrs) +
24837d6034d3SDoug Thompson 					  1];
24847d6034d3SDoug Thompson 
24857d6034d3SDoug Thompson struct mcidev_sysfs_attribute terminator = { .attr = { .name = NULL } };
24867d6034d3SDoug Thompson 
24877d6034d3SDoug Thompson static void amd64_set_mc_sysfs_attributes(struct mem_ctl_info *mci)
24887d6034d3SDoug Thompson {
24897d6034d3SDoug Thompson 	unsigned int i = 0, j = 0;
24907d6034d3SDoug Thompson 
24917d6034d3SDoug Thompson 	for (; i < ARRAY_SIZE(amd64_dbg_attrs); i++)
24927d6034d3SDoug Thompson 		sysfs_attrs[i] = amd64_dbg_attrs[i];
24937d6034d3SDoug Thompson 
24947d6034d3SDoug Thompson 	for (j = 0; j < ARRAY_SIZE(amd64_inj_attrs); j++, i++)
24957d6034d3SDoug Thompson 		sysfs_attrs[i] = amd64_inj_attrs[j];
24967d6034d3SDoug Thompson 
24977d6034d3SDoug Thompson 	sysfs_attrs[i] = terminator;
24987d6034d3SDoug Thompson 
24997d6034d3SDoug Thompson 	mci->mc_driver_sysfs_attributes = sysfs_attrs;
25007d6034d3SDoug Thompson }
25017d6034d3SDoug Thompson 
25027d6034d3SDoug Thompson static void amd64_setup_mci_misc_attributes(struct mem_ctl_info *mci)
25037d6034d3SDoug Thompson {
25047d6034d3SDoug Thompson 	struct amd64_pvt *pvt = mci->pvt_info;
25057d6034d3SDoug Thompson 
25067d6034d3SDoug Thompson 	mci->mtype_cap		= MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
25077d6034d3SDoug Thompson 	mci->edac_ctl_cap	= EDAC_FLAG_NONE;
25087d6034d3SDoug Thompson 
25097d6034d3SDoug Thompson 	if (pvt->nbcap & K8_NBCAP_SECDED)
25107d6034d3SDoug Thompson 		mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
25117d6034d3SDoug Thompson 
25127d6034d3SDoug Thompson 	if (pvt->nbcap & K8_NBCAP_CHIPKILL)
25137d6034d3SDoug Thompson 		mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
25147d6034d3SDoug Thompson 
25157d6034d3SDoug Thompson 	mci->edac_cap		= amd64_determine_edac_cap(pvt);
25167d6034d3SDoug Thompson 	mci->mod_name		= EDAC_MOD_STR;
25177d6034d3SDoug Thompson 	mci->mod_ver		= EDAC_AMD64_VERSION;
25180092b20dSBorislav Petkov 	mci->ctl_name		= pvt->ctl_name;
25198d5b5d9cSBorislav Petkov 	mci->dev_name		= pci_name(pvt->F2);
25207d6034d3SDoug Thompson 	mci->ctl_page_to_phys	= NULL;
25217d6034d3SDoug Thompson 
25227d6034d3SDoug Thompson 	/* memory scrubber interface */
25237d6034d3SDoug Thompson 	mci->set_sdram_scrub_rate = amd64_set_scrub_rate;
25247d6034d3SDoug Thompson 	mci->get_sdram_scrub_rate = amd64_get_scrub_rate;
25257d6034d3SDoug Thompson }
25267d6034d3SDoug Thompson 
25270092b20dSBorislav Petkov /*
25280092b20dSBorislav Petkov  * returns a pointer to the family descriptor on success, NULL otherwise.
25290092b20dSBorislav Petkov  */
25300092b20dSBorislav Petkov static struct amd64_family_type *amd64_per_family_init(struct amd64_pvt *pvt)
2531395ae783SBorislav Petkov {
25320092b20dSBorislav Petkov 	u8 fam = boot_cpu_data.x86;
25330092b20dSBorislav Petkov 	struct amd64_family_type *fam_type = NULL;
25340092b20dSBorislav Petkov 
25350092b20dSBorislav Petkov 	switch (fam) {
2536395ae783SBorislav Petkov 	case 0xf:
25370092b20dSBorislav Petkov 		fam_type		= &amd64_family_types[K8_CPUS];
2538b8cfa02fSBorislav Petkov 		pvt->ops		= &amd64_family_types[K8_CPUS].ops;
25390092b20dSBorislav Petkov 		pvt->ctl_name		= fam_type->ctl_name;
2540395ae783SBorislav Petkov 		pvt->min_scrubrate	= K8_MIN_SCRUB_RATE_BITS;
2541395ae783SBorislav Petkov 		break;
2542395ae783SBorislav Petkov 	case 0x10:
25430092b20dSBorislav Petkov 		fam_type		= &amd64_family_types[F10_CPUS];
2544b8cfa02fSBorislav Petkov 		pvt->ops		= &amd64_family_types[F10_CPUS].ops;
25450092b20dSBorislav Petkov 		pvt->ctl_name		= fam_type->ctl_name;
2546395ae783SBorislav Petkov 		pvt->min_scrubrate	= F10_MIN_SCRUB_RATE_BITS;
2547395ae783SBorislav Petkov 		break;
2548395ae783SBorislav Petkov 
2549395ae783SBorislav Petkov 	default:
255024f9a7feSBorislav Petkov 		amd64_err("Unsupported family!\n");
25510092b20dSBorislav Petkov 		return NULL;
2552395ae783SBorislav Petkov 	}
25530092b20dSBorislav Petkov 
2554b8cfa02fSBorislav Petkov 	pvt->ext_model = boot_cpu_data.x86_model >> 4;
2555b8cfa02fSBorislav Petkov 
255624f9a7feSBorislav Petkov 	amd64_info("%s %sdetected (node %d).\n", pvt->ctl_name,
25570092b20dSBorislav Petkov 		     (fam == 0xf ?
25580092b20dSBorislav Petkov 				(pvt->ext_model >= K8_REV_F  ? "revF or later "
25590092b20dSBorislav Petkov 							     : "revE or earlier ")
256024f9a7feSBorislav Petkov 				 : ""), pvt->mc_node_id);
25610092b20dSBorislav Petkov 	return fam_type;
2562395ae783SBorislav Petkov }
2563395ae783SBorislav Petkov 
25647d6034d3SDoug Thompson /*
25657d6034d3SDoug Thompson  * Init stuff for this DRAM Controller device.
25667d6034d3SDoug Thompson  *
25677d6034d3SDoug Thompson  * Due to a hardware feature on Fam10h CPUs, the Enable Extended Configuration
25687d6034d3SDoug Thompson  * Space feature MUST be enabled on ALL Processors prior to actually reading
25697d6034d3SDoug Thompson  * from the ECS registers. Since the loading of the module can occur on any
25707d6034d3SDoug Thompson  * 'core', and cores don't 'see' all the other processors ECS data when the
25717d6034d3SDoug Thompson  * others are NOT enabled. Our solution is to first enable ECS access in this
25727d6034d3SDoug Thompson  * routine on all processors, gather some data in a amd64_pvt structure and
25737d6034d3SDoug Thompson  * later come back in a finish-setup function to perform that final
25747d6034d3SDoug Thompson  * initialization. See also amd64_init_2nd_stage() for that.
25757d6034d3SDoug Thompson  */
25768d5b5d9cSBorislav Petkov static int amd64_probe_one_instance(struct pci_dev *F2)
25777d6034d3SDoug Thompson {
25787d6034d3SDoug Thompson 	struct amd64_pvt *pvt = NULL;
25790092b20dSBorislav Petkov 	struct amd64_family_type *fam_type = NULL;
25807d6034d3SDoug Thompson 	int err = 0, ret;
25817d6034d3SDoug Thompson 
25827d6034d3SDoug Thompson 	ret = -ENOMEM;
25837d6034d3SDoug Thompson 	pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
25847d6034d3SDoug Thompson 	if (!pvt)
25857d6034d3SDoug Thompson 		goto err_exit;
25867d6034d3SDoug Thompson 
25878d5b5d9cSBorislav Petkov 	pvt->mc_node_id	 = get_node_id(F2);
25888d5b5d9cSBorislav Petkov 	pvt->F2 = F2;
25897d6034d3SDoug Thompson 
2590395ae783SBorislav Petkov 	ret = -EINVAL;
25910092b20dSBorislav Petkov 	fam_type = amd64_per_family_init(pvt);
25920092b20dSBorislav Petkov 	if (!fam_type)
2593395ae783SBorislav Petkov 		goto err_free;
2594395ae783SBorislav Petkov 
25957d6034d3SDoug Thompson 	ret = -ENODEV;
25968d5b5d9cSBorislav Petkov 	err = amd64_reserve_mc_sibling_devices(pvt, fam_type->f1_id,
25978d5b5d9cSBorislav Petkov 						fam_type->f3_id);
25987d6034d3SDoug Thompson 	if (err)
25997d6034d3SDoug Thompson 		goto err_free;
26007d6034d3SDoug Thompson 
26017d6034d3SDoug Thompson 	ret = -EINVAL;
26027d6034d3SDoug Thompson 	err = amd64_check_ecc_enabled(pvt);
26037d6034d3SDoug Thompson 	if (err)
26047d6034d3SDoug Thompson 		goto err_put;
26057d6034d3SDoug Thompson 
26067d6034d3SDoug Thompson 	/*
26077d6034d3SDoug Thompson 	 * Key operation here: setup of HW prior to performing ops on it. Some
26087d6034d3SDoug Thompson 	 * setup is required to access ECS data. After this is performed, the
26097d6034d3SDoug Thompson 	 * 'teardown' function must be called upon error and normal exit paths.
26107d6034d3SDoug Thompson 	 */
26117d6034d3SDoug Thompson 	if (boot_cpu_data.x86 >= 0x10)
26127d6034d3SDoug Thompson 		amd64_setup(pvt);
26137d6034d3SDoug Thompson 
26147d6034d3SDoug Thompson 	/*
26157d6034d3SDoug Thompson 	 * Save the pointer to the private data for use in 2nd initialization
26167d6034d3SDoug Thompson 	 * stage
26177d6034d3SDoug Thompson 	 */
26187d6034d3SDoug Thompson 	pvt_lookup[pvt->mc_node_id] = pvt;
26197d6034d3SDoug Thompson 
26207d6034d3SDoug Thompson 	return 0;
26217d6034d3SDoug Thompson 
26227d6034d3SDoug Thompson err_put:
26237d6034d3SDoug Thompson 	amd64_free_mc_sibling_devices(pvt);
26247d6034d3SDoug Thompson 
26257d6034d3SDoug Thompson err_free:
26267d6034d3SDoug Thompson 	kfree(pvt);
26277d6034d3SDoug Thompson 
26287d6034d3SDoug Thompson err_exit:
26297d6034d3SDoug Thompson 	return ret;
26307d6034d3SDoug Thompson }
26317d6034d3SDoug Thompson 
26327d6034d3SDoug Thompson /*
26337d6034d3SDoug Thompson  * This is the finishing stage of the init code. Needs to be performed after all
26347d6034d3SDoug Thompson  * MCs' hardware have been prepped for accessing extended config space.
26357d6034d3SDoug Thompson  */
26367d6034d3SDoug Thompson static int amd64_init_2nd_stage(struct amd64_pvt *pvt)
26377d6034d3SDoug Thompson {
26387d6034d3SDoug Thompson 	int node_id = pvt->mc_node_id;
26397d6034d3SDoug Thompson 	struct mem_ctl_info *mci;
264018ba54acSAndrew Morton 	int ret = -ENODEV;
26417d6034d3SDoug Thompson 
26427d6034d3SDoug Thompson 	amd64_read_mc_registers(pvt);
26437d6034d3SDoug Thompson 
26447d6034d3SDoug Thompson 	/*
26457d6034d3SDoug Thompson 	 * We need to determine how many memory channels there are. Then use
26467d6034d3SDoug Thompson 	 * that information for calculating the size of the dynamic instance
26477d6034d3SDoug Thompson 	 * tables in the 'mci' structure
26487d6034d3SDoug Thompson 	 */
26497d6034d3SDoug Thompson 	pvt->channel_count = pvt->ops->early_channel_count(pvt);
26507d6034d3SDoug Thompson 	if (pvt->channel_count < 0)
26517d6034d3SDoug Thompson 		goto err_exit;
26527d6034d3SDoug Thompson 
26537d6034d3SDoug Thompson 	ret = -ENOMEM;
26549d858bb1SBorislav Petkov 	mci = edac_mc_alloc(0, pvt->cs_count, pvt->channel_count, node_id);
26557d6034d3SDoug Thompson 	if (!mci)
26567d6034d3SDoug Thompson 		goto err_exit;
26577d6034d3SDoug Thompson 
26587d6034d3SDoug Thompson 	mci->pvt_info = pvt;
26597d6034d3SDoug Thompson 
26608d5b5d9cSBorislav Petkov 	mci->dev = &pvt->F2->dev;
26617d6034d3SDoug Thompson 	amd64_setup_mci_misc_attributes(mci);
26627d6034d3SDoug Thompson 
26637d6034d3SDoug Thompson 	if (amd64_init_csrows(mci))
26647d6034d3SDoug Thompson 		mci->edac_cap = EDAC_FLAG_NONE;
26657d6034d3SDoug Thompson 
26667d6034d3SDoug Thompson 	amd64_enable_ecc_error_reporting(mci);
26677d6034d3SDoug Thompson 	amd64_set_mc_sysfs_attributes(mci);
26687d6034d3SDoug Thompson 
26697d6034d3SDoug Thompson 	ret = -ENODEV;
26707d6034d3SDoug Thompson 	if (edac_mc_add_mc(mci)) {
26717d6034d3SDoug Thompson 		debugf1("failed edac_mc_add_mc()\n");
26727d6034d3SDoug Thompson 		goto err_add_mc;
26737d6034d3SDoug Thompson 	}
26747d6034d3SDoug Thompson 
26757d6034d3SDoug Thompson 	mci_lookup[node_id] = mci;
26767d6034d3SDoug Thompson 	pvt_lookup[node_id] = NULL;
2677549d042dSBorislav Petkov 
2678549d042dSBorislav Petkov 	/* register stuff with EDAC MCE */
2679549d042dSBorislav Petkov 	if (report_gart_errors)
2680549d042dSBorislav Petkov 		amd_report_gart_errors(true);
2681549d042dSBorislav Petkov 
2682549d042dSBorislav Petkov 	amd_register_ecc_decoder(amd64_decode_bus_error);
2683549d042dSBorislav Petkov 
26847d6034d3SDoug Thompson 	return 0;
26857d6034d3SDoug Thompson 
26867d6034d3SDoug Thompson err_add_mc:
26877d6034d3SDoug Thompson 	edac_mc_free(mci);
26887d6034d3SDoug Thompson 
26897d6034d3SDoug Thompson err_exit:
26907d6034d3SDoug Thompson 	debugf0("failure to init 2nd stage: ret=%d\n", ret);
26917d6034d3SDoug Thompson 
26927d6034d3SDoug Thompson 	amd64_restore_ecc_error_reporting(pvt);
26937d6034d3SDoug Thompson 
26947d6034d3SDoug Thompson 	if (boot_cpu_data.x86 > 0xf)
26957d6034d3SDoug Thompson 		amd64_teardown(pvt);
26967d6034d3SDoug Thompson 
26977d6034d3SDoug Thompson 	amd64_free_mc_sibling_devices(pvt);
26987d6034d3SDoug Thompson 
26997d6034d3SDoug Thompson 	kfree(pvt_lookup[pvt->mc_node_id]);
27007d6034d3SDoug Thompson 	pvt_lookup[node_id] = NULL;
27017d6034d3SDoug Thompson 
27027d6034d3SDoug Thompson 	return ret;
27037d6034d3SDoug Thompson }
27047d6034d3SDoug Thompson 
27057d6034d3SDoug Thompson 
27067d6034d3SDoug Thompson static int __devinit amd64_init_one_instance(struct pci_dev *pdev,
27077d6034d3SDoug Thompson 					     const struct pci_device_id *mc_type)
27087d6034d3SDoug Thompson {
27097d6034d3SDoug Thompson 	int ret = 0;
27107d6034d3SDoug Thompson 
27117d6034d3SDoug Thompson 	ret = pci_enable_device(pdev);
2712b8cfa02fSBorislav Petkov 	if (ret < 0) {
27137d6034d3SDoug Thompson 		debugf0("ret=%d\n", ret);
2714b8cfa02fSBorislav Petkov 		return -EIO;
2715b8cfa02fSBorislav Petkov 	}
2716b8cfa02fSBorislav Petkov 
2717b8cfa02fSBorislav Petkov 	ret = amd64_probe_one_instance(pdev);
2718b8cfa02fSBorislav Petkov 	if (ret < 0)
271924f9a7feSBorislav Petkov 		amd64_err("Error probing instance: %d\n", get_node_id(pdev));
27207d6034d3SDoug Thompson 
27217d6034d3SDoug Thompson 	return ret;
27227d6034d3SDoug Thompson }
27237d6034d3SDoug Thompson 
27247d6034d3SDoug Thompson static void __devexit amd64_remove_one_instance(struct pci_dev *pdev)
27257d6034d3SDoug Thompson {
27267d6034d3SDoug Thompson 	struct mem_ctl_info *mci;
27277d6034d3SDoug Thompson 	struct amd64_pvt *pvt;
27287d6034d3SDoug Thompson 
27297d6034d3SDoug Thompson 	/* Remove from EDAC CORE tracking list */
27307d6034d3SDoug Thompson 	mci = edac_mc_del_mc(&pdev->dev);
27317d6034d3SDoug Thompson 	if (!mci)
27327d6034d3SDoug Thompson 		return;
27337d6034d3SDoug Thompson 
27347d6034d3SDoug Thompson 	pvt = mci->pvt_info;
27357d6034d3SDoug Thompson 
27367d6034d3SDoug Thompson 	amd64_restore_ecc_error_reporting(pvt);
27377d6034d3SDoug Thompson 
27387d6034d3SDoug Thompson 	if (boot_cpu_data.x86 > 0xf)
27397d6034d3SDoug Thompson 		amd64_teardown(pvt);
27407d6034d3SDoug Thompson 
27417d6034d3SDoug Thompson 	amd64_free_mc_sibling_devices(pvt);
27427d6034d3SDoug Thompson 
2743549d042dSBorislav Petkov 	/* unregister from EDAC MCE */
2744549d042dSBorislav Petkov 	amd_report_gart_errors(false);
2745549d042dSBorislav Petkov 	amd_unregister_ecc_decoder(amd64_decode_bus_error);
2746549d042dSBorislav Petkov 
27477d6034d3SDoug Thompson 	/* Free the EDAC CORE resources */
27488f68ed97SBorislav Petkov 	mci->pvt_info = NULL;
27498f68ed97SBorislav Petkov 	mci_lookup[pvt->mc_node_id] = NULL;
27508f68ed97SBorislav Petkov 
27518f68ed97SBorislav Petkov 	kfree(pvt);
27527d6034d3SDoug Thompson 	edac_mc_free(mci);
27537d6034d3SDoug Thompson }
27547d6034d3SDoug Thompson 
27557d6034d3SDoug Thompson /*
27567d6034d3SDoug Thompson  * This table is part of the interface for loading drivers for PCI devices. The
27577d6034d3SDoug Thompson  * PCI core identifies what devices are on a system during boot, and then
27587d6034d3SDoug Thompson  * inquiry this table to see if this driver is for a given device found.
27597d6034d3SDoug Thompson  */
27607d6034d3SDoug Thompson static const struct pci_device_id amd64_pci_table[] __devinitdata = {
27617d6034d3SDoug Thompson 	{
27627d6034d3SDoug Thompson 		.vendor		= PCI_VENDOR_ID_AMD,
27637d6034d3SDoug Thompson 		.device		= PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
27647d6034d3SDoug Thompson 		.subvendor	= PCI_ANY_ID,
27657d6034d3SDoug Thompson 		.subdevice	= PCI_ANY_ID,
27667d6034d3SDoug Thompson 		.class		= 0,
27677d6034d3SDoug Thompson 		.class_mask	= 0,
27687d6034d3SDoug Thompson 	},
27697d6034d3SDoug Thompson 	{
27707d6034d3SDoug Thompson 		.vendor		= PCI_VENDOR_ID_AMD,
27717d6034d3SDoug Thompson 		.device		= PCI_DEVICE_ID_AMD_10H_NB_DRAM,
27727d6034d3SDoug Thompson 		.subvendor	= PCI_ANY_ID,
27737d6034d3SDoug Thompson 		.subdevice	= PCI_ANY_ID,
27747d6034d3SDoug Thompson 		.class		= 0,
27757d6034d3SDoug Thompson 		.class_mask	= 0,
27767d6034d3SDoug Thompson 	},
27777d6034d3SDoug Thompson 	{0, }
27787d6034d3SDoug Thompson };
27797d6034d3SDoug Thompson MODULE_DEVICE_TABLE(pci, amd64_pci_table);
27807d6034d3SDoug Thompson 
27817d6034d3SDoug Thompson static struct pci_driver amd64_pci_driver = {
27827d6034d3SDoug Thompson 	.name		= EDAC_MOD_STR,
27837d6034d3SDoug Thompson 	.probe		= amd64_init_one_instance,
27847d6034d3SDoug Thompson 	.remove		= __devexit_p(amd64_remove_one_instance),
27857d6034d3SDoug Thompson 	.id_table	= amd64_pci_table,
27867d6034d3SDoug Thompson };
27877d6034d3SDoug Thompson 
27887d6034d3SDoug Thompson static void amd64_setup_pci_device(void)
27897d6034d3SDoug Thompson {
27907d6034d3SDoug Thompson 	struct mem_ctl_info *mci;
27917d6034d3SDoug Thompson 	struct amd64_pvt *pvt;
27927d6034d3SDoug Thompson 
27937d6034d3SDoug Thompson 	if (amd64_ctl_pci)
27947d6034d3SDoug Thompson 		return;
27957d6034d3SDoug Thompson 
27967d6034d3SDoug Thompson 	mci = mci_lookup[0];
27977d6034d3SDoug Thompson 	if (mci) {
27987d6034d3SDoug Thompson 
27997d6034d3SDoug Thompson 		pvt = mci->pvt_info;
28007d6034d3SDoug Thompson 		amd64_ctl_pci =
28018d5b5d9cSBorislav Petkov 			edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR);
28027d6034d3SDoug Thompson 
28037d6034d3SDoug Thompson 		if (!amd64_ctl_pci) {
28047d6034d3SDoug Thompson 			pr_warning("%s(): Unable to create PCI control\n",
28057d6034d3SDoug Thompson 				   __func__);
28067d6034d3SDoug Thompson 
28077d6034d3SDoug Thompson 			pr_warning("%s(): PCI error report via EDAC not set\n",
28087d6034d3SDoug Thompson 				   __func__);
28097d6034d3SDoug Thompson 			}
28107d6034d3SDoug Thompson 	}
28117d6034d3SDoug Thompson }
28127d6034d3SDoug Thompson 
28137d6034d3SDoug Thompson static int __init amd64_edac_init(void)
28147d6034d3SDoug Thompson {
28157d6034d3SDoug Thompson 	int nb, err = -ENODEV;
281656b34b91SBorislav Petkov 	bool load_ok = false;
28177d6034d3SDoug Thompson 
28187d6034d3SDoug Thompson 	edac_printk(KERN_INFO, EDAC_MOD_STR, EDAC_AMD64_VERSION "\n");
28197d6034d3SDoug Thompson 
28207d6034d3SDoug Thompson 	opstate_init();
28217d6034d3SDoug Thompson 
28229653a5c7SHans Rosenfeld 	if (amd_cache_northbridges() < 0)
282356b34b91SBorislav Petkov 		goto err_ret;
28247d6034d3SDoug Thompson 
282550542251SBorislav Petkov 	msrs = msrs_alloc();
282656b34b91SBorislav Petkov 	if (!msrs)
282756b34b91SBorislav Petkov 		goto err_ret;
282850542251SBorislav Petkov 
28297d6034d3SDoug Thompson 	err = pci_register_driver(&amd64_pci_driver);
28307d6034d3SDoug Thompson 	if (err)
283156b34b91SBorislav Petkov 		goto err_pci;
28327d6034d3SDoug Thompson 
28337d6034d3SDoug Thompson 	/*
28347d6034d3SDoug Thompson 	 * At this point, the array 'pvt_lookup[]' contains pointers to alloc'd
28357d6034d3SDoug Thompson 	 * amd64_pvt structs. These will be used in the 2nd stage init function
28367d6034d3SDoug Thompson 	 * to finish initialization of the MC instances.
28377d6034d3SDoug Thompson 	 */
283856b34b91SBorislav Petkov 	err = -ENODEV;
28399653a5c7SHans Rosenfeld 	for (nb = 0; nb < amd_nb_num(); nb++) {
28407d6034d3SDoug Thompson 		if (!pvt_lookup[nb])
28417d6034d3SDoug Thompson 			continue;
28427d6034d3SDoug Thompson 
28437d6034d3SDoug Thompson 		err = amd64_init_2nd_stage(pvt_lookup[nb]);
28447d6034d3SDoug Thompson 		if (err)
284537da0450SBorislav Petkov 			goto err_2nd_stage;
284656b34b91SBorislav Petkov 
284756b34b91SBorislav Petkov 		load_ok = true;
28487d6034d3SDoug Thompson 	}
28497d6034d3SDoug Thompson 
285056b34b91SBorislav Petkov 	if (load_ok) {
28517d6034d3SDoug Thompson 		amd64_setup_pci_device();
28527d6034d3SDoug Thompson 		return 0;
285356b34b91SBorislav Petkov 	}
28547d6034d3SDoug Thompson 
285537da0450SBorislav Petkov err_2nd_stage:
28567d6034d3SDoug Thompson 	pci_unregister_driver(&amd64_pci_driver);
285756b34b91SBorislav Petkov err_pci:
285856b34b91SBorislav Petkov 	msrs_free(msrs);
285956b34b91SBorislav Petkov 	msrs = NULL;
286056b34b91SBorislav Petkov err_ret:
28617d6034d3SDoug Thompson 	return err;
28627d6034d3SDoug Thompson }
28637d6034d3SDoug Thompson 
28647d6034d3SDoug Thompson static void __exit amd64_edac_exit(void)
28657d6034d3SDoug Thompson {
28667d6034d3SDoug Thompson 	if (amd64_ctl_pci)
28677d6034d3SDoug Thompson 		edac_pci_release_generic_ctl(amd64_ctl_pci);
28687d6034d3SDoug Thompson 
28697d6034d3SDoug Thompson 	pci_unregister_driver(&amd64_pci_driver);
287050542251SBorislav Petkov 
287150542251SBorislav Petkov 	msrs_free(msrs);
287250542251SBorislav Petkov 	msrs = NULL;
28737d6034d3SDoug Thompson }
28747d6034d3SDoug Thompson 
28757d6034d3SDoug Thompson module_init(amd64_edac_init);
28767d6034d3SDoug Thompson module_exit(amd64_edac_exit);
28777d6034d3SDoug Thompson 
28787d6034d3SDoug Thompson MODULE_LICENSE("GPL");
28797d6034d3SDoug Thompson MODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, "
28807d6034d3SDoug Thompson 		"Dave Peterson, Thayne Harbaugh");
28817d6034d3SDoug Thompson MODULE_DESCRIPTION("MC support for AMD64 memory controllers - "
28827d6034d3SDoug Thompson 		EDAC_AMD64_VERSION);
28837d6034d3SDoug Thompson 
28847d6034d3SDoug Thompson module_param(edac_op_state, int, 0444);
28857d6034d3SDoug Thompson MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
2886