18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci#include "amd64_edac.h"
38c2ecf20Sopenharmony_ci#include <asm/amd_nb.h>
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_cistatic struct edac_pci_ctl_info *pci_ctl;
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci/*
88c2ecf20Sopenharmony_ci * Set by command line parameter. If BIOS has enabled the ECC, this override is
98c2ecf20Sopenharmony_ci * cleared to prevent re-enabling the hardware by this driver.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_cistatic int ecc_enable_override;
128c2ecf20Sopenharmony_cimodule_param(ecc_enable_override, int, 0644);
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic struct msr __percpu *msrs;
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic struct amd64_family_type *fam_type;
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/* Per-node stuff */
198c2ecf20Sopenharmony_cistatic struct ecc_settings **ecc_stngs;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/* Device for the PCI component */
228c2ecf20Sopenharmony_cistatic struct device *pci_ctl_dev;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/*
258c2ecf20Sopenharmony_ci * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing
268c2ecf20Sopenharmony_ci * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching-
278c2ecf20Sopenharmony_ci * or higher value'.
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci *FIXME: Produce a better mapping/linearisation.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_cistatic const struct scrubrate {
328c2ecf20Sopenharmony_ci       u32 scrubval;           /* bit pattern for scrub rate */
338c2ecf20Sopenharmony_ci       u32 bandwidth;          /* bandwidth consumed (bytes/sec) */
348c2ecf20Sopenharmony_ci} scrubrates[] = {
358c2ecf20Sopenharmony_ci	{ 0x01, 1600000000UL},
368c2ecf20Sopenharmony_ci	{ 0x02, 800000000UL},
378c2ecf20Sopenharmony_ci	{ 0x03, 400000000UL},
388c2ecf20Sopenharmony_ci	{ 0x04, 200000000UL},
398c2ecf20Sopenharmony_ci	{ 0x05, 100000000UL},
408c2ecf20Sopenharmony_ci	{ 0x06, 50000000UL},
418c2ecf20Sopenharmony_ci	{ 0x07, 25000000UL},
428c2ecf20Sopenharmony_ci	{ 0x08, 12284069UL},
438c2ecf20Sopenharmony_ci	{ 0x09, 6274509UL},
448c2ecf20Sopenharmony_ci	{ 0x0A, 3121951UL},
458c2ecf20Sopenharmony_ci	{ 0x0B, 1560975UL},
468c2ecf20Sopenharmony_ci	{ 0x0C, 781440UL},
478c2ecf20Sopenharmony_ci	{ 0x0D, 390720UL},
488c2ecf20Sopenharmony_ci	{ 0x0E, 195300UL},
498c2ecf20Sopenharmony_ci	{ 0x0F, 97650UL},
508c2ecf20Sopenharmony_ci	{ 0x10, 48854UL},
518c2ecf20Sopenharmony_ci	{ 0x11, 24427UL},
528c2ecf20Sopenharmony_ci	{ 0x12, 12213UL},
538c2ecf20Sopenharmony_ci	{ 0x13, 6101UL},
548c2ecf20Sopenharmony_ci	{ 0x14, 3051UL},
558c2ecf20Sopenharmony_ci	{ 0x15, 1523UL},
568c2ecf20Sopenharmony_ci	{ 0x16, 761UL},
578c2ecf20Sopenharmony_ci	{ 0x00, 0UL},        /* scrubbing off */
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ciint __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset,
618c2ecf20Sopenharmony_ci			       u32 *val, const char *func)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	int err = 0;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	err = pci_read_config_dword(pdev, offset, val);
668c2ecf20Sopenharmony_ci	if (err)
678c2ecf20Sopenharmony_ci		amd64_warn("%s: error reading F%dx%03x.\n",
688c2ecf20Sopenharmony_ci			   func, PCI_FUNC(pdev->devfn), offset);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	return err;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ciint __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset,
748c2ecf20Sopenharmony_ci				u32 val, const char *func)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	int err = 0;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	err = pci_write_config_dword(pdev, offset, val);
798c2ecf20Sopenharmony_ci	if (err)
808c2ecf20Sopenharmony_ci		amd64_warn("%s: error writing to F%dx%03x.\n",
818c2ecf20Sopenharmony_ci			   func, PCI_FUNC(pdev->devfn), offset);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	return err;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/*
878c2ecf20Sopenharmony_ci * Select DCT to which PCI cfg accesses are routed
888c2ecf20Sopenharmony_ci */
898c2ecf20Sopenharmony_cistatic void f15h_select_dct(struct amd64_pvt *pvt, u8 dct)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	u32 reg = 0;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, &reg);
948c2ecf20Sopenharmony_ci	reg &= (pvt->model == 0x30) ? ~3 : ~1;
958c2ecf20Sopenharmony_ci	reg |= dct;
968c2ecf20Sopenharmony_ci	amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci/*
1008c2ecf20Sopenharmony_ci *
1018c2ecf20Sopenharmony_ci * Depending on the family, F2 DCT reads need special handling:
1028c2ecf20Sopenharmony_ci *
1038c2ecf20Sopenharmony_ci * K8: has a single DCT only and no address offsets >= 0x100
1048c2ecf20Sopenharmony_ci *
1058c2ecf20Sopenharmony_ci * F10h: each DCT has its own set of regs
1068c2ecf20Sopenharmony_ci *	DCT0 -> F2x040..
1078c2ecf20Sopenharmony_ci *	DCT1 -> F2x140..
1088c2ecf20Sopenharmony_ci *
1098c2ecf20Sopenharmony_ci * F16h: has only 1 DCT
1108c2ecf20Sopenharmony_ci *
1118c2ecf20Sopenharmony_ci * F15h: we select which DCT we access using F1x10C[DctCfgSel]
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_cistatic inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct,
1148c2ecf20Sopenharmony_ci					 int offset, u32 *val)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	switch (pvt->fam) {
1178c2ecf20Sopenharmony_ci	case 0xf:
1188c2ecf20Sopenharmony_ci		if (dct || offset >= 0x100)
1198c2ecf20Sopenharmony_ci			return -EINVAL;
1208c2ecf20Sopenharmony_ci		break;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	case 0x10:
1238c2ecf20Sopenharmony_ci		if (dct) {
1248c2ecf20Sopenharmony_ci			/*
1258c2ecf20Sopenharmony_ci			 * Note: If ganging is enabled, barring the regs
1268c2ecf20Sopenharmony_ci			 * F2x[1,0]98 and F2x[1,0]9C; reads reads to F2x1xx
1278c2ecf20Sopenharmony_ci			 * return 0. (cf. Section 2.8.1 F10h BKDG)
1288c2ecf20Sopenharmony_ci			 */
1298c2ecf20Sopenharmony_ci			if (dct_ganging_enabled(pvt))
1308c2ecf20Sopenharmony_ci				return 0;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci			offset += 0x100;
1338c2ecf20Sopenharmony_ci		}
1348c2ecf20Sopenharmony_ci		break;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	case 0x15:
1378c2ecf20Sopenharmony_ci		/*
1388c2ecf20Sopenharmony_ci		 * F15h: F2x1xx addresses do not map explicitly to DCT1.
1398c2ecf20Sopenharmony_ci		 * We should select which DCT we access using F1x10C[DctCfgSel]
1408c2ecf20Sopenharmony_ci		 */
1418c2ecf20Sopenharmony_ci		dct = (dct && pvt->model == 0x30) ? 3 : dct;
1428c2ecf20Sopenharmony_ci		f15h_select_dct(pvt, dct);
1438c2ecf20Sopenharmony_ci		break;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	case 0x16:
1468c2ecf20Sopenharmony_ci		if (dct)
1478c2ecf20Sopenharmony_ci			return -EINVAL;
1488c2ecf20Sopenharmony_ci		break;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	default:
1518c2ecf20Sopenharmony_ci		break;
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci	return amd64_read_pci_cfg(pvt->F2, offset, val);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/*
1578c2ecf20Sopenharmony_ci * Memory scrubber control interface. For K8, memory scrubbing is handled by
1588c2ecf20Sopenharmony_ci * hardware and can involve L2 cache, dcache as well as the main memory. With
1598c2ecf20Sopenharmony_ci * F10, this is extended to L3 cache scrubbing on CPU models sporting that
1608c2ecf20Sopenharmony_ci * functionality.
1618c2ecf20Sopenharmony_ci *
1628c2ecf20Sopenharmony_ci * This causes the "units" for the scrubbing speed to vary from 64 byte blocks
1638c2ecf20Sopenharmony_ci * (dram) over to cache lines. This is nasty, so we will use bandwidth in
1648c2ecf20Sopenharmony_ci * bytes/sec for the setting.
1658c2ecf20Sopenharmony_ci *
1668c2ecf20Sopenharmony_ci * Currently, we only do dram scrubbing. If the scrubbing is done in software on
1678c2ecf20Sopenharmony_ci * other archs, we might not have access to the caches directly.
1688c2ecf20Sopenharmony_ci */
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic inline void __f17h_set_scrubval(struct amd64_pvt *pvt, u32 scrubval)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	/*
1738c2ecf20Sopenharmony_ci	 * Fam17h supports scrub values between 0x5 and 0x14. Also, the values
1748c2ecf20Sopenharmony_ci	 * are shifted down by 0x5, so scrubval 0x5 is written to the register
1758c2ecf20Sopenharmony_ci	 * as 0x0, scrubval 0x6 as 0x1, etc.
1768c2ecf20Sopenharmony_ci	 */
1778c2ecf20Sopenharmony_ci	if (scrubval >= 0x5 && scrubval <= 0x14) {
1788c2ecf20Sopenharmony_ci		scrubval -= 0x5;
1798c2ecf20Sopenharmony_ci		pci_write_bits32(pvt->F6, F17H_SCR_LIMIT_ADDR, scrubval, 0xF);
1808c2ecf20Sopenharmony_ci		pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 1, 0x1);
1818c2ecf20Sopenharmony_ci	} else {
1828c2ecf20Sopenharmony_ci		pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 0, 0x1);
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci/*
1868c2ecf20Sopenharmony_ci * Scan the scrub rate mapping table for a close or matching bandwidth value to
1878c2ecf20Sopenharmony_ci * issue. If requested is too big, then use last maximum value found.
1888c2ecf20Sopenharmony_ci */
1898c2ecf20Sopenharmony_cistatic int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	u32 scrubval;
1928c2ecf20Sopenharmony_ci	int i;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	/*
1958c2ecf20Sopenharmony_ci	 * map the configured rate (new_bw) to a value specific to the AMD64
1968c2ecf20Sopenharmony_ci	 * memory controller and apply to register. Search for the first
1978c2ecf20Sopenharmony_ci	 * bandwidth entry that is greater or equal than the setting requested
1988c2ecf20Sopenharmony_ci	 * and program that. If at last entry, turn off DRAM scrubbing.
1998c2ecf20Sopenharmony_ci	 *
2008c2ecf20Sopenharmony_ci	 * If no suitable bandwidth is found, turn off DRAM scrubbing entirely
2018c2ecf20Sopenharmony_ci	 * by falling back to the last element in scrubrates[].
2028c2ecf20Sopenharmony_ci	 */
2038c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(scrubrates) - 1; i++) {
2048c2ecf20Sopenharmony_ci		/*
2058c2ecf20Sopenharmony_ci		 * skip scrub rates which aren't recommended
2068c2ecf20Sopenharmony_ci		 * (see F10 BKDG, F3x58)
2078c2ecf20Sopenharmony_ci		 */
2088c2ecf20Sopenharmony_ci		if (scrubrates[i].scrubval < min_rate)
2098c2ecf20Sopenharmony_ci			continue;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci		if (scrubrates[i].bandwidth <= new_bw)
2128c2ecf20Sopenharmony_ci			break;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	scrubval = scrubrates[i].scrubval;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (pvt->umc) {
2188c2ecf20Sopenharmony_ci		__f17h_set_scrubval(pvt, scrubval);
2198c2ecf20Sopenharmony_ci	} else if (pvt->fam == 0x15 && pvt->model == 0x60) {
2208c2ecf20Sopenharmony_ci		f15h_select_dct(pvt, 0);
2218c2ecf20Sopenharmony_ci		pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
2228c2ecf20Sopenharmony_ci		f15h_select_dct(pvt, 1);
2238c2ecf20Sopenharmony_ci		pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
2248c2ecf20Sopenharmony_ci	} else {
2258c2ecf20Sopenharmony_ci		pci_write_bits32(pvt->F3, SCRCTRL, scrubval, 0x001F);
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (scrubval)
2298c2ecf20Sopenharmony_ci		return scrubrates[i].bandwidth;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	return 0;
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic int set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt = mci->pvt_info;
2378c2ecf20Sopenharmony_ci	u32 min_scrubrate = 0x5;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (pvt->fam == 0xf)
2408c2ecf20Sopenharmony_ci		min_scrubrate = 0x0;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (pvt->fam == 0x15) {
2438c2ecf20Sopenharmony_ci		/* Erratum #505 */
2448c2ecf20Sopenharmony_ci		if (pvt->model < 0x10)
2458c2ecf20Sopenharmony_ci			f15h_select_dct(pvt, 0);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		if (pvt->model == 0x60)
2488c2ecf20Sopenharmony_ci			min_scrubrate = 0x6;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci	return __set_scrub_rate(pvt, bw, min_scrubrate);
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic int get_scrub_rate(struct mem_ctl_info *mci)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt = mci->pvt_info;
2568c2ecf20Sopenharmony_ci	int i, retval = -EINVAL;
2578c2ecf20Sopenharmony_ci	u32 scrubval = 0;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (pvt->umc) {
2608c2ecf20Sopenharmony_ci		amd64_read_pci_cfg(pvt->F6, F17H_SCR_BASE_ADDR, &scrubval);
2618c2ecf20Sopenharmony_ci		if (scrubval & BIT(0)) {
2628c2ecf20Sopenharmony_ci			amd64_read_pci_cfg(pvt->F6, F17H_SCR_LIMIT_ADDR, &scrubval);
2638c2ecf20Sopenharmony_ci			scrubval &= 0xF;
2648c2ecf20Sopenharmony_ci			scrubval += 0x5;
2658c2ecf20Sopenharmony_ci		} else {
2668c2ecf20Sopenharmony_ci			scrubval = 0;
2678c2ecf20Sopenharmony_ci		}
2688c2ecf20Sopenharmony_ci	} else if (pvt->fam == 0x15) {
2698c2ecf20Sopenharmony_ci		/* Erratum #505 */
2708c2ecf20Sopenharmony_ci		if (pvt->model < 0x10)
2718c2ecf20Sopenharmony_ci			f15h_select_dct(pvt, 0);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		if (pvt->model == 0x60)
2748c2ecf20Sopenharmony_ci			amd64_read_pci_cfg(pvt->F2, F15H_M60H_SCRCTRL, &scrubval);
2758c2ecf20Sopenharmony_ci		else
2768c2ecf20Sopenharmony_ci			amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
2778c2ecf20Sopenharmony_ci	} else {
2788c2ecf20Sopenharmony_ci		amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	scrubval = scrubval & 0x001F;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
2848c2ecf20Sopenharmony_ci		if (scrubrates[i].scrubval == scrubval) {
2858c2ecf20Sopenharmony_ci			retval = scrubrates[i].bandwidth;
2868c2ecf20Sopenharmony_ci			break;
2878c2ecf20Sopenharmony_ci		}
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci	return retval;
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci/*
2938c2ecf20Sopenharmony_ci * returns true if the SysAddr given by sys_addr matches the
2948c2ecf20Sopenharmony_ci * DRAM base/limit associated with node_id
2958c2ecf20Sopenharmony_ci */
2968c2ecf20Sopenharmony_cistatic bool base_limit_match(struct amd64_pvt *pvt, u64 sys_addr, u8 nid)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	u64 addr;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	/* The K8 treats this as a 40-bit value.  However, bits 63-40 will be
3018c2ecf20Sopenharmony_ci	 * all ones if the most significant implemented address bit is 1.
3028c2ecf20Sopenharmony_ci	 * Here we discard bits 63-40.  See section 3.4.2 of AMD publication
3038c2ecf20Sopenharmony_ci	 * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1
3048c2ecf20Sopenharmony_ci	 * Application Programming.
3058c2ecf20Sopenharmony_ci	 */
3068c2ecf20Sopenharmony_ci	addr = sys_addr & 0x000000ffffffffffull;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	return ((addr >= get_dram_base(pvt, nid)) &&
3098c2ecf20Sopenharmony_ci		(addr <= get_dram_limit(pvt, nid)));
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci/*
3138c2ecf20Sopenharmony_ci * Attempt to map a SysAddr to a node. On success, return a pointer to the
3148c2ecf20Sopenharmony_ci * mem_ctl_info structure for the node that the SysAddr maps to.
3158c2ecf20Sopenharmony_ci *
3168c2ecf20Sopenharmony_ci * On failure, return NULL.
3178c2ecf20Sopenharmony_ci */
3188c2ecf20Sopenharmony_cistatic struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci,
3198c2ecf20Sopenharmony_ci						u64 sys_addr)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt;
3228c2ecf20Sopenharmony_ci	u8 node_id;
3238c2ecf20Sopenharmony_ci	u32 intlv_en, bits;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	/*
3268c2ecf20Sopenharmony_ci	 * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section
3278c2ecf20Sopenharmony_ci	 * 3.4.4.2) registers to map the SysAddr to a node ID.
3288c2ecf20Sopenharmony_ci	 */
3298c2ecf20Sopenharmony_ci	pvt = mci->pvt_info;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/*
3328c2ecf20Sopenharmony_ci	 * The value of this field should be the same for all DRAM Base
3338c2ecf20Sopenharmony_ci	 * registers.  Therefore we arbitrarily choose to read it from the
3348c2ecf20Sopenharmony_ci	 * register for node 0.
3358c2ecf20Sopenharmony_ci	 */
3368c2ecf20Sopenharmony_ci	intlv_en = dram_intlv_en(pvt, 0);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (intlv_en == 0) {
3398c2ecf20Sopenharmony_ci		for (node_id = 0; node_id < DRAM_RANGES; node_id++) {
3408c2ecf20Sopenharmony_ci			if (base_limit_match(pvt, sys_addr, node_id))
3418c2ecf20Sopenharmony_ci				goto found;
3428c2ecf20Sopenharmony_ci		}
3438c2ecf20Sopenharmony_ci		goto err_no_match;
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if (unlikely((intlv_en != 0x01) &&
3478c2ecf20Sopenharmony_ci		     (intlv_en != 0x03) &&
3488c2ecf20Sopenharmony_ci		     (intlv_en != 0x07))) {
3498c2ecf20Sopenharmony_ci		amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en);
3508c2ecf20Sopenharmony_ci		return NULL;
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	bits = (((u32) sys_addr) >> 12) & intlv_en;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	for (node_id = 0; ; ) {
3568c2ecf20Sopenharmony_ci		if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits)
3578c2ecf20Sopenharmony_ci			break;	/* intlv_sel field matches */
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci		if (++node_id >= DRAM_RANGES)
3608c2ecf20Sopenharmony_ci			goto err_no_match;
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	/* sanity test for sys_addr */
3648c2ecf20Sopenharmony_ci	if (unlikely(!base_limit_match(pvt, sys_addr, node_id))) {
3658c2ecf20Sopenharmony_ci		amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address"
3668c2ecf20Sopenharmony_ci			   "range for node %d with node interleaving enabled.\n",
3678c2ecf20Sopenharmony_ci			   __func__, sys_addr, node_id);
3688c2ecf20Sopenharmony_ci		return NULL;
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cifound:
3728c2ecf20Sopenharmony_ci	return edac_mc_find((int)node_id);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cierr_no_match:
3758c2ecf20Sopenharmony_ci	edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n",
3768c2ecf20Sopenharmony_ci		 (unsigned long)sys_addr);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	return NULL;
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci/*
3828c2ecf20Sopenharmony_ci * compute the CS base address of the @csrow on the DRAM controller @dct.
3838c2ecf20Sopenharmony_ci * For details see F2x[5C:40] in the processor's BKDG
3848c2ecf20Sopenharmony_ci */
3858c2ecf20Sopenharmony_cistatic void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct,
3868c2ecf20Sopenharmony_ci				 u64 *base, u64 *mask)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	u64 csbase, csmask, base_bits, mask_bits;
3898c2ecf20Sopenharmony_ci	u8 addr_shift;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
3928c2ecf20Sopenharmony_ci		csbase		= pvt->csels[dct].csbases[csrow];
3938c2ecf20Sopenharmony_ci		csmask		= pvt->csels[dct].csmasks[csrow];
3948c2ecf20Sopenharmony_ci		base_bits	= GENMASK_ULL(31, 21) | GENMASK_ULL(15, 9);
3958c2ecf20Sopenharmony_ci		mask_bits	= GENMASK_ULL(29, 21) | GENMASK_ULL(15, 9);
3968c2ecf20Sopenharmony_ci		addr_shift	= 4;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	/*
3998c2ecf20Sopenharmony_ci	 * F16h and F15h, models 30h and later need two addr_shift values:
4008c2ecf20Sopenharmony_ci	 * 8 for high and 6 for low (cf. F16h BKDG).
4018c2ecf20Sopenharmony_ci	 */
4028c2ecf20Sopenharmony_ci	} else if (pvt->fam == 0x16 ||
4038c2ecf20Sopenharmony_ci		  (pvt->fam == 0x15 && pvt->model >= 0x30)) {
4048c2ecf20Sopenharmony_ci		csbase          = pvt->csels[dct].csbases[csrow];
4058c2ecf20Sopenharmony_ci		csmask          = pvt->csels[dct].csmasks[csrow >> 1];
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		*base  = (csbase & GENMASK_ULL(15,  5)) << 6;
4088c2ecf20Sopenharmony_ci		*base |= (csbase & GENMASK_ULL(30, 19)) << 8;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci		*mask = ~0ULL;
4118c2ecf20Sopenharmony_ci		/* poke holes for the csmask */
4128c2ecf20Sopenharmony_ci		*mask &= ~((GENMASK_ULL(15, 5)  << 6) |
4138c2ecf20Sopenharmony_ci			   (GENMASK_ULL(30, 19) << 8));
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci		*mask |= (csmask & GENMASK_ULL(15, 5))  << 6;
4168c2ecf20Sopenharmony_ci		*mask |= (csmask & GENMASK_ULL(30, 19)) << 8;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		return;
4198c2ecf20Sopenharmony_ci	} else {
4208c2ecf20Sopenharmony_ci		csbase		= pvt->csels[dct].csbases[csrow];
4218c2ecf20Sopenharmony_ci		csmask		= pvt->csels[dct].csmasks[csrow >> 1];
4228c2ecf20Sopenharmony_ci		addr_shift	= 8;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci		if (pvt->fam == 0x15)
4258c2ecf20Sopenharmony_ci			base_bits = mask_bits =
4268c2ecf20Sopenharmony_ci				GENMASK_ULL(30,19) | GENMASK_ULL(13,5);
4278c2ecf20Sopenharmony_ci		else
4288c2ecf20Sopenharmony_ci			base_bits = mask_bits =
4298c2ecf20Sopenharmony_ci				GENMASK_ULL(28,19) | GENMASK_ULL(13,5);
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	*base  = (csbase & base_bits) << addr_shift;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	*mask  = ~0ULL;
4358c2ecf20Sopenharmony_ci	/* poke holes for the csmask */
4368c2ecf20Sopenharmony_ci	*mask &= ~(mask_bits << addr_shift);
4378c2ecf20Sopenharmony_ci	/* OR them in */
4388c2ecf20Sopenharmony_ci	*mask |= (csmask & mask_bits) << addr_shift;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci#define for_each_chip_select(i, dct, pvt) \
4428c2ecf20Sopenharmony_ci	for (i = 0; i < pvt->csels[dct].b_cnt; i++)
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci#define chip_select_base(i, dct, pvt) \
4458c2ecf20Sopenharmony_ci	pvt->csels[dct].csbases[i]
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci#define for_each_chip_select_mask(i, dct, pvt) \
4488c2ecf20Sopenharmony_ci	for (i = 0; i < pvt->csels[dct].m_cnt; i++)
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci#define for_each_umc(i) \
4518c2ecf20Sopenharmony_ci	for (i = 0; i < fam_type->max_mcs; i++)
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci/*
4548c2ecf20Sopenharmony_ci * @input_addr is an InputAddr associated with the node given by mci. Return the
4558c2ecf20Sopenharmony_ci * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr).
4568c2ecf20Sopenharmony_ci */
4578c2ecf20Sopenharmony_cistatic int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt;
4608c2ecf20Sopenharmony_ci	int csrow;
4618c2ecf20Sopenharmony_ci	u64 base, mask;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	pvt = mci->pvt_info;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	for_each_chip_select(csrow, 0, pvt) {
4668c2ecf20Sopenharmony_ci		if (!csrow_enabled(csrow, 0, pvt))
4678c2ecf20Sopenharmony_ci			continue;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci		get_cs_base_and_mask(pvt, csrow, 0, &base, &mask);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci		mask = ~mask;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci		if ((input_addr & mask) == (base & mask)) {
4748c2ecf20Sopenharmony_ci			edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n",
4758c2ecf20Sopenharmony_ci				 (unsigned long)input_addr, csrow,
4768c2ecf20Sopenharmony_ci				 pvt->mc_node_id);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci			return csrow;
4798c2ecf20Sopenharmony_ci		}
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci	edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n",
4828c2ecf20Sopenharmony_ci		 (unsigned long)input_addr, pvt->mc_node_id);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	return -1;
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci/*
4888c2ecf20Sopenharmony_ci * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094)
4898c2ecf20Sopenharmony_ci * for the node represented by mci. Info is passed back in *hole_base,
4908c2ecf20Sopenharmony_ci * *hole_offset, and *hole_size.  Function returns 0 if info is valid or 1 if
4918c2ecf20Sopenharmony_ci * info is invalid. Info may be invalid for either of the following reasons:
4928c2ecf20Sopenharmony_ci *
4938c2ecf20Sopenharmony_ci * - The revision of the node is not E or greater.  In this case, the DRAM Hole
4948c2ecf20Sopenharmony_ci *   Address Register does not exist.
4958c2ecf20Sopenharmony_ci *
4968c2ecf20Sopenharmony_ci * - The DramHoleValid bit is cleared in the DRAM Hole Address Register,
4978c2ecf20Sopenharmony_ci *   indicating that its contents are not valid.
4988c2ecf20Sopenharmony_ci *
4998c2ecf20Sopenharmony_ci * The values passed back in *hole_base, *hole_offset, and *hole_size are
5008c2ecf20Sopenharmony_ci * complete 32-bit values despite the fact that the bitfields in the DHAR
5018c2ecf20Sopenharmony_ci * only represent bits 31-24 of the base and offset values.
5028c2ecf20Sopenharmony_ci */
5038c2ecf20Sopenharmony_ciint amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
5048c2ecf20Sopenharmony_ci			     u64 *hole_offset, u64 *hole_size)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt = mci->pvt_info;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	/* only revE and later have the DRAM Hole Address Register */
5098c2ecf20Sopenharmony_ci	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_E) {
5108c2ecf20Sopenharmony_ci		edac_dbg(1, "  revision %d for node %d does not support DHAR\n",
5118c2ecf20Sopenharmony_ci			 pvt->ext_model, pvt->mc_node_id);
5128c2ecf20Sopenharmony_ci		return 1;
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	/* valid for Fam10h and above */
5168c2ecf20Sopenharmony_ci	if (pvt->fam >= 0x10 && !dhar_mem_hoist_valid(pvt)) {
5178c2ecf20Sopenharmony_ci		edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this system\n");
5188c2ecf20Sopenharmony_ci		return 1;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (!dhar_valid(pvt)) {
5228c2ecf20Sopenharmony_ci		edac_dbg(1, "  Dram Memory Hoisting is DISABLED on this node %d\n",
5238c2ecf20Sopenharmony_ci			 pvt->mc_node_id);
5248c2ecf20Sopenharmony_ci		return 1;
5258c2ecf20Sopenharmony_ci	}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	/* This node has Memory Hoisting */
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	/* +------------------+--------------------+--------------------+-----
5308c2ecf20Sopenharmony_ci	 * | memory           | DRAM hole          | relocated          |
5318c2ecf20Sopenharmony_ci	 * | [0, (x - 1)]     | [x, 0xffffffff]    | addresses from     |
5328c2ecf20Sopenharmony_ci	 * |                  |                    | DRAM hole          |
5338c2ecf20Sopenharmony_ci	 * |                  |                    | [0x100000000,      |
5348c2ecf20Sopenharmony_ci	 * |                  |                    |  (0x100000000+     |
5358c2ecf20Sopenharmony_ci	 * |                  |                    |   (0xffffffff-x))] |
5368c2ecf20Sopenharmony_ci	 * +------------------+--------------------+--------------------+-----
5378c2ecf20Sopenharmony_ci	 *
5388c2ecf20Sopenharmony_ci	 * Above is a diagram of physical memory showing the DRAM hole and the
5398c2ecf20Sopenharmony_ci	 * relocated addresses from the DRAM hole.  As shown, the DRAM hole
5408c2ecf20Sopenharmony_ci	 * starts at address x (the base address) and extends through address
5418c2ecf20Sopenharmony_ci	 * 0xffffffff.  The DRAM Hole Address Register (DHAR) relocates the
5428c2ecf20Sopenharmony_ci	 * addresses in the hole so that they start at 0x100000000.
5438c2ecf20Sopenharmony_ci	 */
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	*hole_base = dhar_base(pvt);
5468c2ecf20Sopenharmony_ci	*hole_size = (1ULL << 32) - *hole_base;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	*hole_offset = (pvt->fam > 0xf) ? f10_dhar_offset(pvt)
5498c2ecf20Sopenharmony_ci					: k8_dhar_offset(pvt);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	edac_dbg(1, "  DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
5528c2ecf20Sopenharmony_ci		 pvt->mc_node_id, (unsigned long)*hole_base,
5538c2ecf20Sopenharmony_ci		 (unsigned long)*hole_offset, (unsigned long)*hole_size);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	return 0;
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(amd64_get_dram_hole_info);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci/*
5608c2ecf20Sopenharmony_ci * Return the DramAddr that the SysAddr given by @sys_addr maps to.  It is
5618c2ecf20Sopenharmony_ci * assumed that sys_addr maps to the node given by mci.
5628c2ecf20Sopenharmony_ci *
5638c2ecf20Sopenharmony_ci * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section
5648c2ecf20Sopenharmony_ci * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a
5658c2ecf20Sopenharmony_ci * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled,
5668c2ecf20Sopenharmony_ci * then it is also involved in translating a SysAddr to a DramAddr. Sections
5678c2ecf20Sopenharmony_ci * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting.
5688c2ecf20Sopenharmony_ci * These parts of the documentation are unclear. I interpret them as follows:
5698c2ecf20Sopenharmony_ci *
5708c2ecf20Sopenharmony_ci * When node n receives a SysAddr, it processes the SysAddr as follows:
5718c2ecf20Sopenharmony_ci *
5728c2ecf20Sopenharmony_ci * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM
5738c2ecf20Sopenharmony_ci *    Limit registers for node n. If the SysAddr is not within the range
5748c2ecf20Sopenharmony_ci *    specified by the base and limit values, then node n ignores the Sysaddr
5758c2ecf20Sopenharmony_ci *    (since it does not map to node n). Otherwise continue to step 2 below.
5768c2ecf20Sopenharmony_ci *
5778c2ecf20Sopenharmony_ci * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is
5788c2ecf20Sopenharmony_ci *    disabled so skip to step 3 below. Otherwise see if the SysAddr is within
5798c2ecf20Sopenharmony_ci *    the range of relocated addresses (starting at 0x100000000) from the DRAM
5808c2ecf20Sopenharmony_ci *    hole. If not, skip to step 3 below. Else get the value of the
5818c2ecf20Sopenharmony_ci *    DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the
5828c2ecf20Sopenharmony_ci *    offset defined by this value from the SysAddr.
5838c2ecf20Sopenharmony_ci *
5848c2ecf20Sopenharmony_ci * 3. Obtain the base address for node n from the DRAMBase field of the DRAM
5858c2ecf20Sopenharmony_ci *    Base register for node n. To obtain the DramAddr, subtract the base
5868c2ecf20Sopenharmony_ci *    address from the SysAddr, as shown near the start of section 3.4.4 (p.70).
5878c2ecf20Sopenharmony_ci */
5888c2ecf20Sopenharmony_cistatic u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt = mci->pvt_info;
5918c2ecf20Sopenharmony_ci	u64 dram_base, hole_base, hole_offset, hole_size, dram_addr;
5928c2ecf20Sopenharmony_ci	int ret;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	dram_base = get_dram_base(pvt, pvt->mc_node_id);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
5978c2ecf20Sopenharmony_ci				      &hole_size);
5988c2ecf20Sopenharmony_ci	if (!ret) {
5998c2ecf20Sopenharmony_ci		if ((sys_addr >= (1ULL << 32)) &&
6008c2ecf20Sopenharmony_ci		    (sys_addr < ((1ULL << 32) + hole_size))) {
6018c2ecf20Sopenharmony_ci			/* use DHAR to translate SysAddr to DramAddr */
6028c2ecf20Sopenharmony_ci			dram_addr = sys_addr - hole_offset;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci			edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
6058c2ecf20Sopenharmony_ci				 (unsigned long)sys_addr,
6068c2ecf20Sopenharmony_ci				 (unsigned long)dram_addr);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci			return dram_addr;
6098c2ecf20Sopenharmony_ci		}
6108c2ecf20Sopenharmony_ci	}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	/*
6138c2ecf20Sopenharmony_ci	 * Translate the SysAddr to a DramAddr as shown near the start of
6148c2ecf20Sopenharmony_ci	 * section 3.4.4 (p. 70).  Although sys_addr is a 64-bit value, the k8
6158c2ecf20Sopenharmony_ci	 * only deals with 40-bit values.  Therefore we discard bits 63-40 of
6168c2ecf20Sopenharmony_ci	 * sys_addr below.  If bit 39 of sys_addr is 1 then the bits we
6178c2ecf20Sopenharmony_ci	 * discard are all 1s.  Otherwise the bits we discard are all 0s.  See
6188c2ecf20Sopenharmony_ci	 * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture
6198c2ecf20Sopenharmony_ci	 * Programmer's Manual Volume 1 Application Programming.
6208c2ecf20Sopenharmony_ci	 */
6218c2ecf20Sopenharmony_ci	dram_addr = (sys_addr & GENMASK_ULL(39, 0)) - dram_base;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
6248c2ecf20Sopenharmony_ci		 (unsigned long)sys_addr, (unsigned long)dram_addr);
6258c2ecf20Sopenharmony_ci	return dram_addr;
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci/*
6298c2ecf20Sopenharmony_ci * @intlv_en is the value of the IntlvEn field from a DRAM Base register
6308c2ecf20Sopenharmony_ci * (section 3.4.4.1).  Return the number of bits from a SysAddr that are used
6318c2ecf20Sopenharmony_ci * for node interleaving.
6328c2ecf20Sopenharmony_ci */
6338c2ecf20Sopenharmony_cistatic int num_node_interleave_bits(unsigned intlv_en)
6348c2ecf20Sopenharmony_ci{
6358c2ecf20Sopenharmony_ci	static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 };
6368c2ecf20Sopenharmony_ci	int n;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	BUG_ON(intlv_en > 7);
6398c2ecf20Sopenharmony_ci	n = intlv_shift_table[intlv_en];
6408c2ecf20Sopenharmony_ci	return n;
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci/* Translate the DramAddr given by @dram_addr to an InputAddr. */
6448c2ecf20Sopenharmony_cistatic u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt;
6478c2ecf20Sopenharmony_ci	int intlv_shift;
6488c2ecf20Sopenharmony_ci	u64 input_addr;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	pvt = mci->pvt_info;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	/*
6538c2ecf20Sopenharmony_ci	 * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
6548c2ecf20Sopenharmony_ci	 * concerning translating a DramAddr to an InputAddr.
6558c2ecf20Sopenharmony_ci	 */
6568c2ecf20Sopenharmony_ci	intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
6578c2ecf20Sopenharmony_ci	input_addr = ((dram_addr >> intlv_shift) & GENMASK_ULL(35, 12)) +
6588c2ecf20Sopenharmony_ci		      (dram_addr & 0xfff);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	edac_dbg(2, "  Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
6618c2ecf20Sopenharmony_ci		 intlv_shift, (unsigned long)dram_addr,
6628c2ecf20Sopenharmony_ci		 (unsigned long)input_addr);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	return input_addr;
6658c2ecf20Sopenharmony_ci}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci/*
6688c2ecf20Sopenharmony_ci * Translate the SysAddr represented by @sys_addr to an InputAddr.  It is
6698c2ecf20Sopenharmony_ci * assumed that @sys_addr maps to the node given by mci.
6708c2ecf20Sopenharmony_ci */
6718c2ecf20Sopenharmony_cistatic u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
6728c2ecf20Sopenharmony_ci{
6738c2ecf20Sopenharmony_ci	u64 input_addr;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	input_addr =
6768c2ecf20Sopenharmony_ci	    dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	edac_dbg(2, "SysAddr 0x%lx translates to InputAddr 0x%lx\n",
6798c2ecf20Sopenharmony_ci		 (unsigned long)sys_addr, (unsigned long)input_addr);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	return input_addr;
6828c2ecf20Sopenharmony_ci}
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci/* Map the Error address to a PAGE and PAGE OFFSET. */
6858c2ecf20Sopenharmony_cistatic inline void error_address_to_page_and_offset(u64 error_address,
6868c2ecf20Sopenharmony_ci						    struct err_info *err)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	err->page = (u32) (error_address >> PAGE_SHIFT);
6898c2ecf20Sopenharmony_ci	err->offset = ((u32) error_address) & ~PAGE_MASK;
6908c2ecf20Sopenharmony_ci}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci/*
6938c2ecf20Sopenharmony_ci * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address
6948c2ecf20Sopenharmony_ci * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers
6958c2ecf20Sopenharmony_ci * of a node that detected an ECC memory error.  mci represents the node that
6968c2ecf20Sopenharmony_ci * the error address maps to (possibly different from the node that detected
6978c2ecf20Sopenharmony_ci * the error).  Return the number of the csrow that sys_addr maps to, or -1 on
6988c2ecf20Sopenharmony_ci * error.
6998c2ecf20Sopenharmony_ci */
7008c2ecf20Sopenharmony_cistatic int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	int csrow;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr));
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	if (csrow == -1)
7078c2ecf20Sopenharmony_ci		amd64_mc_err(mci, "Failed to translate InputAddr to csrow for "
7088c2ecf20Sopenharmony_ci				  "address 0x%lx\n", (unsigned long)sys_addr);
7098c2ecf20Sopenharmony_ci	return csrow;
7108c2ecf20Sopenharmony_ci}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_cistatic int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci/*
7158c2ecf20Sopenharmony_ci * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs
7168c2ecf20Sopenharmony_ci * are ECC capable.
7178c2ecf20Sopenharmony_ci */
7188c2ecf20Sopenharmony_cistatic unsigned long determine_edac_cap(struct amd64_pvt *pvt)
7198c2ecf20Sopenharmony_ci{
7208c2ecf20Sopenharmony_ci	unsigned long edac_cap = EDAC_FLAG_NONE;
7218c2ecf20Sopenharmony_ci	u8 bit;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	if (pvt->umc) {
7248c2ecf20Sopenharmony_ci		u8 i, umc_en_mask = 0, dimm_ecc_en_mask = 0;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci		for_each_umc(i) {
7278c2ecf20Sopenharmony_ci			if (!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT))
7288c2ecf20Sopenharmony_ci				continue;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci			umc_en_mask |= BIT(i);
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci			/* UMC Configuration bit 12 (DimmEccEn) */
7338c2ecf20Sopenharmony_ci			if (pvt->umc[i].umc_cfg & BIT(12))
7348c2ecf20Sopenharmony_ci				dimm_ecc_en_mask |= BIT(i);
7358c2ecf20Sopenharmony_ci		}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci		if (umc_en_mask == dimm_ecc_en_mask)
7388c2ecf20Sopenharmony_ci			edac_cap = EDAC_FLAG_SECDED;
7398c2ecf20Sopenharmony_ci	} else {
7408c2ecf20Sopenharmony_ci		bit = (pvt->fam > 0xf || pvt->ext_model >= K8_REV_F)
7418c2ecf20Sopenharmony_ci			? 19
7428c2ecf20Sopenharmony_ci			: 17;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci		if (pvt->dclr0 & BIT(bit))
7458c2ecf20Sopenharmony_ci			edac_cap = EDAC_FLAG_SECDED;
7468c2ecf20Sopenharmony_ci	}
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	return edac_cap;
7498c2ecf20Sopenharmony_ci}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_cistatic void debug_display_dimm_sizes(struct amd64_pvt *, u8);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_cistatic void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan)
7548c2ecf20Sopenharmony_ci{
7558c2ecf20Sopenharmony_ci	edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	if (pvt->dram_type == MEM_LRDDR3) {
7588c2ecf20Sopenharmony_ci		u32 dcsm = pvt->csels[chan].csmasks[0];
7598c2ecf20Sopenharmony_ci		/*
7608c2ecf20Sopenharmony_ci		 * It's assumed all LRDIMMs in a DCT are going to be of
7618c2ecf20Sopenharmony_ci		 * same 'type' until proven otherwise. So, use a cs
7628c2ecf20Sopenharmony_ci		 * value of '0' here to get dcsm value.
7638c2ecf20Sopenharmony_ci		 */
7648c2ecf20Sopenharmony_ci		edac_dbg(1, " LRDIMM %dx rank multiply\n", (dcsm & 0x3));
7658c2ecf20Sopenharmony_ci	}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	edac_dbg(1, "All DIMMs support ECC:%s\n",
7688c2ecf20Sopenharmony_ci		    (dclr & BIT(19)) ? "yes" : "no");
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	edac_dbg(1, "  PAR/ERR parity: %s\n",
7728c2ecf20Sopenharmony_ci		 (dclr & BIT(8)) ?  "enabled" : "disabled");
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	if (pvt->fam == 0x10)
7758c2ecf20Sopenharmony_ci		edac_dbg(1, "  DCT 128bit mode width: %s\n",
7768c2ecf20Sopenharmony_ci			 (dclr & BIT(11)) ?  "128b" : "64b");
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	edac_dbg(1, "  x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
7798c2ecf20Sopenharmony_ci		 (dclr & BIT(12)) ?  "yes" : "no",
7808c2ecf20Sopenharmony_ci		 (dclr & BIT(13)) ?  "yes" : "no",
7818c2ecf20Sopenharmony_ci		 (dclr & BIT(14)) ?  "yes" : "no",
7828c2ecf20Sopenharmony_ci		 (dclr & BIT(15)) ?  "yes" : "no");
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci#define CS_EVEN_PRIMARY		BIT(0)
7868c2ecf20Sopenharmony_ci#define CS_ODD_PRIMARY		BIT(1)
7878c2ecf20Sopenharmony_ci#define CS_EVEN_SECONDARY	BIT(2)
7888c2ecf20Sopenharmony_ci#define CS_ODD_SECONDARY	BIT(3)
7898c2ecf20Sopenharmony_ci#define CS_3R_INTERLEAVE	BIT(4)
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci#define CS_EVEN			(CS_EVEN_PRIMARY | CS_EVEN_SECONDARY)
7928c2ecf20Sopenharmony_ci#define CS_ODD			(CS_ODD_PRIMARY | CS_ODD_SECONDARY)
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_cistatic int f17_get_cs_mode(int dimm, u8 ctrl, struct amd64_pvt *pvt)
7958c2ecf20Sopenharmony_ci{
7968c2ecf20Sopenharmony_ci	u8 base, count = 0;
7978c2ecf20Sopenharmony_ci	int cs_mode = 0;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	if (csrow_enabled(2 * dimm, ctrl, pvt))
8008c2ecf20Sopenharmony_ci		cs_mode |= CS_EVEN_PRIMARY;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	if (csrow_enabled(2 * dimm + 1, ctrl, pvt))
8038c2ecf20Sopenharmony_ci		cs_mode |= CS_ODD_PRIMARY;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	/* Asymmetric dual-rank DIMM support. */
8068c2ecf20Sopenharmony_ci	if (csrow_sec_enabled(2 * dimm + 1, ctrl, pvt))
8078c2ecf20Sopenharmony_ci		cs_mode |= CS_ODD_SECONDARY;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	/*
8108c2ecf20Sopenharmony_ci	 * 3 Rank inteleaving support.
8118c2ecf20Sopenharmony_ci	 * There should be only three bases enabled and their two masks should
8128c2ecf20Sopenharmony_ci	 * be equal.
8138c2ecf20Sopenharmony_ci	 */
8148c2ecf20Sopenharmony_ci	for_each_chip_select(base, ctrl, pvt)
8158c2ecf20Sopenharmony_ci		count += csrow_enabled(base, ctrl, pvt);
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	if (count == 3 &&
8188c2ecf20Sopenharmony_ci	    pvt->csels[ctrl].csmasks[0] == pvt->csels[ctrl].csmasks[1]) {
8198c2ecf20Sopenharmony_ci		edac_dbg(1, "3R interleaving in use.\n");
8208c2ecf20Sopenharmony_ci		cs_mode |= CS_3R_INTERLEAVE;
8218c2ecf20Sopenharmony_ci	}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	return cs_mode;
8248c2ecf20Sopenharmony_ci}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_cistatic void debug_display_dimm_sizes_df(struct amd64_pvt *pvt, u8 ctrl)
8278c2ecf20Sopenharmony_ci{
8288c2ecf20Sopenharmony_ci	int dimm, size0, size1, cs0, cs1, cs_mode;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	edac_printk(KERN_DEBUG, EDAC_MC, "UMC%d chip selects:\n", ctrl);
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	for (dimm = 0; dimm < 2; dimm++) {
8338c2ecf20Sopenharmony_ci		cs0 = dimm * 2;
8348c2ecf20Sopenharmony_ci		cs1 = dimm * 2 + 1;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci		cs_mode = f17_get_cs_mode(dimm, ctrl, pvt);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci		size0 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs0);
8398c2ecf20Sopenharmony_ci		size1 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs1);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci		amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
8428c2ecf20Sopenharmony_ci				cs0,	size0,
8438c2ecf20Sopenharmony_ci				cs1,	size1);
8448c2ecf20Sopenharmony_ci	}
8458c2ecf20Sopenharmony_ci}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_cistatic void __dump_misc_regs_df(struct amd64_pvt *pvt)
8488c2ecf20Sopenharmony_ci{
8498c2ecf20Sopenharmony_ci	struct amd64_umc *umc;
8508c2ecf20Sopenharmony_ci	u32 i, tmp, umc_base;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	for_each_umc(i) {
8538c2ecf20Sopenharmony_ci		umc_base = get_umc_base(i);
8548c2ecf20Sopenharmony_ci		umc = &pvt->umc[i];
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci		edac_dbg(1, "UMC%d DIMM cfg: 0x%x\n", i, umc->dimm_cfg);
8578c2ecf20Sopenharmony_ci		edac_dbg(1, "UMC%d UMC cfg: 0x%x\n", i, umc->umc_cfg);
8588c2ecf20Sopenharmony_ci		edac_dbg(1, "UMC%d SDP ctrl: 0x%x\n", i, umc->sdp_ctrl);
8598c2ecf20Sopenharmony_ci		edac_dbg(1, "UMC%d ECC ctrl: 0x%x\n", i, umc->ecc_ctrl);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci		amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ECC_BAD_SYMBOL, &tmp);
8628c2ecf20Sopenharmony_ci		edac_dbg(1, "UMC%d ECC bad symbol: 0x%x\n", i, tmp);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci		amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_UMC_CAP, &tmp);
8658c2ecf20Sopenharmony_ci		edac_dbg(1, "UMC%d UMC cap: 0x%x\n", i, tmp);
8668c2ecf20Sopenharmony_ci		edac_dbg(1, "UMC%d UMC cap high: 0x%x\n", i, umc->umc_cap_hi);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci		edac_dbg(1, "UMC%d ECC capable: %s, ChipKill ECC capable: %s\n",
8698c2ecf20Sopenharmony_ci				i, (umc->umc_cap_hi & BIT(30)) ? "yes" : "no",
8708c2ecf20Sopenharmony_ci				    (umc->umc_cap_hi & BIT(31)) ? "yes" : "no");
8718c2ecf20Sopenharmony_ci		edac_dbg(1, "UMC%d All DIMMs support ECC: %s\n",
8728c2ecf20Sopenharmony_ci				i, (umc->umc_cfg & BIT(12)) ? "yes" : "no");
8738c2ecf20Sopenharmony_ci		edac_dbg(1, "UMC%d x4 DIMMs present: %s\n",
8748c2ecf20Sopenharmony_ci				i, (umc->dimm_cfg & BIT(6)) ? "yes" : "no");
8758c2ecf20Sopenharmony_ci		edac_dbg(1, "UMC%d x16 DIMMs present: %s\n",
8768c2ecf20Sopenharmony_ci				i, (umc->dimm_cfg & BIT(7)) ? "yes" : "no");
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci		if (pvt->dram_type == MEM_LRDDR4) {
8798c2ecf20Sopenharmony_ci			amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ADDR_CFG, &tmp);
8808c2ecf20Sopenharmony_ci			edac_dbg(1, "UMC%d LRDIMM %dx rank multiply\n",
8818c2ecf20Sopenharmony_ci					i, 1 << ((tmp >> 4) & 0x3));
8828c2ecf20Sopenharmony_ci		}
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci		debug_display_dimm_sizes_df(pvt, i);
8858c2ecf20Sopenharmony_ci	}
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	edac_dbg(1, "F0x104 (DRAM Hole Address): 0x%08x, base: 0x%08x\n",
8888c2ecf20Sopenharmony_ci		 pvt->dhar, dhar_base(pvt));
8898c2ecf20Sopenharmony_ci}
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci/* Display and decode various NB registers for debug purposes. */
8928c2ecf20Sopenharmony_cistatic void __dump_misc_regs(struct amd64_pvt *pvt)
8938c2ecf20Sopenharmony_ci{
8948c2ecf20Sopenharmony_ci	edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	edac_dbg(1, "  NB two channel DRAM capable: %s\n",
8978c2ecf20Sopenharmony_ci		 (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	edac_dbg(1, "  ECC capable: %s, ChipKill ECC capable: %s\n",
9008c2ecf20Sopenharmony_ci		 (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
9018c2ecf20Sopenharmony_ci		 (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	debug_dump_dramcfg_low(pvt, pvt->dclr0, 0);
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n",
9088c2ecf20Sopenharmony_ci		 pvt->dhar, dhar_base(pvt),
9098c2ecf20Sopenharmony_ci		 (pvt->fam == 0xf) ? k8_dhar_offset(pvt)
9108c2ecf20Sopenharmony_ci				   : f10_dhar_offset(pvt));
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	debug_display_dimm_sizes(pvt, 0);
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	/* everything below this point is Fam10h and above */
9158c2ecf20Sopenharmony_ci	if (pvt->fam == 0xf)
9168c2ecf20Sopenharmony_ci		return;
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	debug_display_dimm_sizes(pvt, 1);
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	/* Only if NOT ganged does dclr1 have valid info */
9218c2ecf20Sopenharmony_ci	if (!dct_ganging_enabled(pvt))
9228c2ecf20Sopenharmony_ci		debug_dump_dramcfg_low(pvt, pvt->dclr1, 1);
9238c2ecf20Sopenharmony_ci}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci/* Display and decode various NB registers for debug purposes. */
9268c2ecf20Sopenharmony_cistatic void dump_misc_regs(struct amd64_pvt *pvt)
9278c2ecf20Sopenharmony_ci{
9288c2ecf20Sopenharmony_ci	if (pvt->umc)
9298c2ecf20Sopenharmony_ci		__dump_misc_regs_df(pvt);
9308c2ecf20Sopenharmony_ci	else
9318c2ecf20Sopenharmony_ci		__dump_misc_regs(pvt);
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	edac_dbg(1, "  DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	amd64_info("using x%u syndromes.\n", pvt->ecc_sym_sz);
9368c2ecf20Sopenharmony_ci}
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci/*
9398c2ecf20Sopenharmony_ci * See BKDG, F2x[1,0][5C:40], F2[1,0][6C:60]
9408c2ecf20Sopenharmony_ci */
9418c2ecf20Sopenharmony_cistatic void prep_chip_selects(struct amd64_pvt *pvt)
9428c2ecf20Sopenharmony_ci{
9438c2ecf20Sopenharmony_ci	if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
9448c2ecf20Sopenharmony_ci		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
9458c2ecf20Sopenharmony_ci		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8;
9468c2ecf20Sopenharmony_ci	} else if (pvt->fam == 0x15 && pvt->model == 0x30) {
9478c2ecf20Sopenharmony_ci		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4;
9488c2ecf20Sopenharmony_ci		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2;
9498c2ecf20Sopenharmony_ci	} else if (pvt->fam >= 0x17) {
9508c2ecf20Sopenharmony_ci		int umc;
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci		for_each_umc(umc) {
9538c2ecf20Sopenharmony_ci			pvt->csels[umc].b_cnt = 4;
9548c2ecf20Sopenharmony_ci			pvt->csels[umc].m_cnt = 2;
9558c2ecf20Sopenharmony_ci		}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	} else {
9588c2ecf20Sopenharmony_ci		pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
9598c2ecf20Sopenharmony_ci		pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4;
9608c2ecf20Sopenharmony_ci	}
9618c2ecf20Sopenharmony_ci}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_cistatic void read_umc_base_mask(struct amd64_pvt *pvt)
9648c2ecf20Sopenharmony_ci{
9658c2ecf20Sopenharmony_ci	u32 umc_base_reg, umc_base_reg_sec;
9668c2ecf20Sopenharmony_ci	u32 umc_mask_reg, umc_mask_reg_sec;
9678c2ecf20Sopenharmony_ci	u32 base_reg, base_reg_sec;
9688c2ecf20Sopenharmony_ci	u32 mask_reg, mask_reg_sec;
9698c2ecf20Sopenharmony_ci	u32 *base, *base_sec;
9708c2ecf20Sopenharmony_ci	u32 *mask, *mask_sec;
9718c2ecf20Sopenharmony_ci	int cs, umc;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	for_each_umc(umc) {
9748c2ecf20Sopenharmony_ci		umc_base_reg = get_umc_base(umc) + UMCCH_BASE_ADDR;
9758c2ecf20Sopenharmony_ci		umc_base_reg_sec = get_umc_base(umc) + UMCCH_BASE_ADDR_SEC;
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci		for_each_chip_select(cs, umc, pvt) {
9788c2ecf20Sopenharmony_ci			base = &pvt->csels[umc].csbases[cs];
9798c2ecf20Sopenharmony_ci			base_sec = &pvt->csels[umc].csbases_sec[cs];
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci			base_reg = umc_base_reg + (cs * 4);
9828c2ecf20Sopenharmony_ci			base_reg_sec = umc_base_reg_sec + (cs * 4);
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci			if (!amd_smn_read(pvt->mc_node_id, base_reg, base))
9858c2ecf20Sopenharmony_ci				edac_dbg(0, "  DCSB%d[%d]=0x%08x reg: 0x%x\n",
9868c2ecf20Sopenharmony_ci					 umc, cs, *base, base_reg);
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci			if (!amd_smn_read(pvt->mc_node_id, base_reg_sec, base_sec))
9898c2ecf20Sopenharmony_ci				edac_dbg(0, "    DCSB_SEC%d[%d]=0x%08x reg: 0x%x\n",
9908c2ecf20Sopenharmony_ci					 umc, cs, *base_sec, base_reg_sec);
9918c2ecf20Sopenharmony_ci		}
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci		umc_mask_reg = get_umc_base(umc) + UMCCH_ADDR_MASK;
9948c2ecf20Sopenharmony_ci		umc_mask_reg_sec = get_umc_base(umc) + UMCCH_ADDR_MASK_SEC;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci		for_each_chip_select_mask(cs, umc, pvt) {
9978c2ecf20Sopenharmony_ci			mask = &pvt->csels[umc].csmasks[cs];
9988c2ecf20Sopenharmony_ci			mask_sec = &pvt->csels[umc].csmasks_sec[cs];
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci			mask_reg = umc_mask_reg + (cs * 4);
10018c2ecf20Sopenharmony_ci			mask_reg_sec = umc_mask_reg_sec + (cs * 4);
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci			if (!amd_smn_read(pvt->mc_node_id, mask_reg, mask))
10048c2ecf20Sopenharmony_ci				edac_dbg(0, "  DCSM%d[%d]=0x%08x reg: 0x%x\n",
10058c2ecf20Sopenharmony_ci					 umc, cs, *mask, mask_reg);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci			if (!amd_smn_read(pvt->mc_node_id, mask_reg_sec, mask_sec))
10088c2ecf20Sopenharmony_ci				edac_dbg(0, "    DCSM_SEC%d[%d]=0x%08x reg: 0x%x\n",
10098c2ecf20Sopenharmony_ci					 umc, cs, *mask_sec, mask_reg_sec);
10108c2ecf20Sopenharmony_ci		}
10118c2ecf20Sopenharmony_ci	}
10128c2ecf20Sopenharmony_ci}
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci/*
10158c2ecf20Sopenharmony_ci * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers
10168c2ecf20Sopenharmony_ci */
10178c2ecf20Sopenharmony_cistatic void read_dct_base_mask(struct amd64_pvt *pvt)
10188c2ecf20Sopenharmony_ci{
10198c2ecf20Sopenharmony_ci	int cs;
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	prep_chip_selects(pvt);
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	if (pvt->umc)
10248c2ecf20Sopenharmony_ci		return read_umc_base_mask(pvt);
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	for_each_chip_select(cs, 0, pvt) {
10278c2ecf20Sopenharmony_ci		int reg0   = DCSB0 + (cs * 4);
10288c2ecf20Sopenharmony_ci		int reg1   = DCSB1 + (cs * 4);
10298c2ecf20Sopenharmony_ci		u32 *base0 = &pvt->csels[0].csbases[cs];
10308c2ecf20Sopenharmony_ci		u32 *base1 = &pvt->csels[1].csbases[cs];
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci		if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0))
10338c2ecf20Sopenharmony_ci			edac_dbg(0, "  DCSB0[%d]=0x%08x reg: F2x%x\n",
10348c2ecf20Sopenharmony_ci				 cs, *base0, reg0);
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci		if (pvt->fam == 0xf)
10378c2ecf20Sopenharmony_ci			continue;
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci		if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1))
10408c2ecf20Sopenharmony_ci			edac_dbg(0, "  DCSB1[%d]=0x%08x reg: F2x%x\n",
10418c2ecf20Sopenharmony_ci				 cs, *base1, (pvt->fam == 0x10) ? reg1
10428c2ecf20Sopenharmony_ci							: reg0);
10438c2ecf20Sopenharmony_ci	}
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	for_each_chip_select_mask(cs, 0, pvt) {
10468c2ecf20Sopenharmony_ci		int reg0   = DCSM0 + (cs * 4);
10478c2ecf20Sopenharmony_ci		int reg1   = DCSM1 + (cs * 4);
10488c2ecf20Sopenharmony_ci		u32 *mask0 = &pvt->csels[0].csmasks[cs];
10498c2ecf20Sopenharmony_ci		u32 *mask1 = &pvt->csels[1].csmasks[cs];
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci		if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0))
10528c2ecf20Sopenharmony_ci			edac_dbg(0, "    DCSM0[%d]=0x%08x reg: F2x%x\n",
10538c2ecf20Sopenharmony_ci				 cs, *mask0, reg0);
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci		if (pvt->fam == 0xf)
10568c2ecf20Sopenharmony_ci			continue;
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci		if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1))
10598c2ecf20Sopenharmony_ci			edac_dbg(0, "    DCSM1[%d]=0x%08x reg: F2x%x\n",
10608c2ecf20Sopenharmony_ci				 cs, *mask1, (pvt->fam == 0x10) ? reg1
10618c2ecf20Sopenharmony_ci							: reg0);
10628c2ecf20Sopenharmony_ci	}
10638c2ecf20Sopenharmony_ci}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_cistatic void determine_memory_type(struct amd64_pvt *pvt)
10668c2ecf20Sopenharmony_ci{
10678c2ecf20Sopenharmony_ci	u32 dram_ctrl, dcsm;
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	if (pvt->umc) {
10708c2ecf20Sopenharmony_ci		if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(5))
10718c2ecf20Sopenharmony_ci			pvt->dram_type = MEM_LRDDR4;
10728c2ecf20Sopenharmony_ci		else if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(4))
10738c2ecf20Sopenharmony_ci			pvt->dram_type = MEM_RDDR4;
10748c2ecf20Sopenharmony_ci		else
10758c2ecf20Sopenharmony_ci			pvt->dram_type = MEM_DDR4;
10768c2ecf20Sopenharmony_ci		return;
10778c2ecf20Sopenharmony_ci	}
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	switch (pvt->fam) {
10808c2ecf20Sopenharmony_ci	case 0xf:
10818c2ecf20Sopenharmony_ci		if (pvt->ext_model >= K8_REV_F)
10828c2ecf20Sopenharmony_ci			goto ddr3;
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci		pvt->dram_type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
10858c2ecf20Sopenharmony_ci		return;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	case 0x10:
10888c2ecf20Sopenharmony_ci		if (pvt->dchr0 & DDR3_MODE)
10898c2ecf20Sopenharmony_ci			goto ddr3;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci		pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
10928c2ecf20Sopenharmony_ci		return;
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	case 0x15:
10958c2ecf20Sopenharmony_ci		if (pvt->model < 0x60)
10968c2ecf20Sopenharmony_ci			goto ddr3;
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci		/*
10998c2ecf20Sopenharmony_ci		 * Model 0x60h needs special handling:
11008c2ecf20Sopenharmony_ci		 *
11018c2ecf20Sopenharmony_ci		 * We use a Chip Select value of '0' to obtain dcsm.
11028c2ecf20Sopenharmony_ci		 * Theoretically, it is possible to populate LRDIMMs of different
11038c2ecf20Sopenharmony_ci		 * 'Rank' value on a DCT. But this is not the common case. So,
11048c2ecf20Sopenharmony_ci		 * it's reasonable to assume all DIMMs are going to be of same
11058c2ecf20Sopenharmony_ci		 * 'type' until proven otherwise.
11068c2ecf20Sopenharmony_ci		 */
11078c2ecf20Sopenharmony_ci		amd64_read_dct_pci_cfg(pvt, 0, DRAM_CONTROL, &dram_ctrl);
11088c2ecf20Sopenharmony_ci		dcsm = pvt->csels[0].csmasks[0];
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci		if (((dram_ctrl >> 8) & 0x7) == 0x2)
11118c2ecf20Sopenharmony_ci			pvt->dram_type = MEM_DDR4;
11128c2ecf20Sopenharmony_ci		else if (pvt->dclr0 & BIT(16))
11138c2ecf20Sopenharmony_ci			pvt->dram_type = MEM_DDR3;
11148c2ecf20Sopenharmony_ci		else if (dcsm & 0x3)
11158c2ecf20Sopenharmony_ci			pvt->dram_type = MEM_LRDDR3;
11168c2ecf20Sopenharmony_ci		else
11178c2ecf20Sopenharmony_ci			pvt->dram_type = MEM_RDDR3;
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci		return;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	case 0x16:
11228c2ecf20Sopenharmony_ci		goto ddr3;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	default:
11258c2ecf20Sopenharmony_ci		WARN(1, KERN_ERR "%s: Family??? 0x%x\n", __func__, pvt->fam);
11268c2ecf20Sopenharmony_ci		pvt->dram_type = MEM_EMPTY;
11278c2ecf20Sopenharmony_ci	}
11288c2ecf20Sopenharmony_ci	return;
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ciddr3:
11318c2ecf20Sopenharmony_ci	pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
11328c2ecf20Sopenharmony_ci}
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci/* Get the number of DCT channels the memory controller is using. */
11358c2ecf20Sopenharmony_cistatic int k8_early_channel_count(struct amd64_pvt *pvt)
11368c2ecf20Sopenharmony_ci{
11378c2ecf20Sopenharmony_ci	int flag;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	if (pvt->ext_model >= K8_REV_F)
11408c2ecf20Sopenharmony_ci		/* RevF (NPT) and later */
11418c2ecf20Sopenharmony_ci		flag = pvt->dclr0 & WIDTH_128;
11428c2ecf20Sopenharmony_ci	else
11438c2ecf20Sopenharmony_ci		/* RevE and earlier */
11448c2ecf20Sopenharmony_ci		flag = pvt->dclr0 & REVE_WIDTH_128;
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	/* not used */
11478c2ecf20Sopenharmony_ci	pvt->dclr1 = 0;
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	return (flag) ? 2 : 1;
11508c2ecf20Sopenharmony_ci}
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci/* On F10h and later ErrAddr is MC4_ADDR[47:1] */
11538c2ecf20Sopenharmony_cistatic u64 get_error_address(struct amd64_pvt *pvt, struct mce *m)
11548c2ecf20Sopenharmony_ci{
11558c2ecf20Sopenharmony_ci	u16 mce_nid = amd_get_nb_id(m->extcpu);
11568c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci;
11578c2ecf20Sopenharmony_ci	u8 start_bit = 1;
11588c2ecf20Sopenharmony_ci	u8 end_bit   = 47;
11598c2ecf20Sopenharmony_ci	u64 addr;
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	mci = edac_mc_find(mce_nid);
11628c2ecf20Sopenharmony_ci	if (!mci)
11638c2ecf20Sopenharmony_ci		return 0;
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	pvt = mci->pvt_info;
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	if (pvt->fam == 0xf) {
11688c2ecf20Sopenharmony_ci		start_bit = 3;
11698c2ecf20Sopenharmony_ci		end_bit   = 39;
11708c2ecf20Sopenharmony_ci	}
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	addr = m->addr & GENMASK_ULL(end_bit, start_bit);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	/*
11758c2ecf20Sopenharmony_ci	 * Erratum 637 workaround
11768c2ecf20Sopenharmony_ci	 */
11778c2ecf20Sopenharmony_ci	if (pvt->fam == 0x15) {
11788c2ecf20Sopenharmony_ci		u64 cc6_base, tmp_addr;
11798c2ecf20Sopenharmony_ci		u32 tmp;
11808c2ecf20Sopenharmony_ci		u8 intlv_en;
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci		if ((addr & GENMASK_ULL(47, 24)) >> 24 != 0x00fdf7)
11838c2ecf20Sopenharmony_ci			return addr;
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp);
11878c2ecf20Sopenharmony_ci		intlv_en = tmp >> 21 & 0x7;
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci		/* add [47:27] + 3 trailing bits */
11908c2ecf20Sopenharmony_ci		cc6_base  = (tmp & GENMASK_ULL(20, 0)) << 3;
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci		/* reverse and add DramIntlvEn */
11938c2ecf20Sopenharmony_ci		cc6_base |= intlv_en ^ 0x7;
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci		/* pin at [47:24] */
11968c2ecf20Sopenharmony_ci		cc6_base <<= 24;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci		if (!intlv_en)
11998c2ecf20Sopenharmony_ci			return cc6_base | (addr & GENMASK_ULL(23, 0));
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci		amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp);
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci							/* faster log2 */
12048c2ecf20Sopenharmony_ci		tmp_addr  = (addr & GENMASK_ULL(23, 12)) << __fls(intlv_en + 1);
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci		/* OR DramIntlvSel into bits [14:12] */
12078c2ecf20Sopenharmony_ci		tmp_addr |= (tmp & GENMASK_ULL(23, 21)) >> 9;
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci		/* add remaining [11:0] bits from original MC4_ADDR */
12108c2ecf20Sopenharmony_ci		tmp_addr |= addr & GENMASK_ULL(11, 0);
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci		return cc6_base | tmp_addr;
12138c2ecf20Sopenharmony_ci	}
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	return addr;
12168c2ecf20Sopenharmony_ci}
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_cistatic struct pci_dev *pci_get_related_function(unsigned int vendor,
12198c2ecf20Sopenharmony_ci						unsigned int device,
12208c2ecf20Sopenharmony_ci						struct pci_dev *related)
12218c2ecf20Sopenharmony_ci{
12228c2ecf20Sopenharmony_ci	struct pci_dev *dev = NULL;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	while ((dev = pci_get_device(vendor, device, dev))) {
12258c2ecf20Sopenharmony_ci		if (pci_domain_nr(dev->bus) == pci_domain_nr(related->bus) &&
12268c2ecf20Sopenharmony_ci		    (dev->bus->number == related->bus->number) &&
12278c2ecf20Sopenharmony_ci		    (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn)))
12288c2ecf20Sopenharmony_ci			break;
12298c2ecf20Sopenharmony_ci	}
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	return dev;
12328c2ecf20Sopenharmony_ci}
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_cistatic void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
12358c2ecf20Sopenharmony_ci{
12368c2ecf20Sopenharmony_ci	struct amd_northbridge *nb;
12378c2ecf20Sopenharmony_ci	struct pci_dev *f1 = NULL;
12388c2ecf20Sopenharmony_ci	unsigned int pci_func;
12398c2ecf20Sopenharmony_ci	int off = range << 3;
12408c2ecf20Sopenharmony_ci	u32 llim;
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off,  &pvt->ranges[range].base.lo);
12438c2ecf20Sopenharmony_ci	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo);
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	if (pvt->fam == 0xf)
12468c2ecf20Sopenharmony_ci		return;
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	if (!dram_rw(pvt, range))
12498c2ecf20Sopenharmony_ci		return;
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off,  &pvt->ranges[range].base.hi);
12528c2ecf20Sopenharmony_ci	amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	/* F15h: factor in CC6 save area by reading dst node's limit reg */
12558c2ecf20Sopenharmony_ci	if (pvt->fam != 0x15)
12568c2ecf20Sopenharmony_ci		return;
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	nb = node_to_amd_nb(dram_dst_node(pvt, range));
12598c2ecf20Sopenharmony_ci	if (WARN_ON(!nb))
12608c2ecf20Sopenharmony_ci		return;
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	if (pvt->model == 0x60)
12638c2ecf20Sopenharmony_ci		pci_func = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1;
12648c2ecf20Sopenharmony_ci	else if (pvt->model == 0x30)
12658c2ecf20Sopenharmony_ci		pci_func = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1;
12668c2ecf20Sopenharmony_ci	else
12678c2ecf20Sopenharmony_ci		pci_func = PCI_DEVICE_ID_AMD_15H_NB_F1;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc);
12708c2ecf20Sopenharmony_ci	if (WARN_ON(!f1))
12718c2ecf20Sopenharmony_ci		return;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim);
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	pvt->ranges[range].lim.lo &= GENMASK_ULL(15, 0);
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci				    /* {[39:27],111b} */
12788c2ecf20Sopenharmony_ci	pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16;
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	pvt->ranges[range].lim.hi &= GENMASK_ULL(7, 0);
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci				    /* [47:40] */
12838c2ecf20Sopenharmony_ci	pvt->ranges[range].lim.hi |= llim >> 13;
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	pci_dev_put(f1);
12868c2ecf20Sopenharmony_ci}
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_cistatic void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
12898c2ecf20Sopenharmony_ci				    struct err_info *err)
12908c2ecf20Sopenharmony_ci{
12918c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt = mci->pvt_info;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	error_address_to_page_and_offset(sys_addr, err);
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	/*
12968c2ecf20Sopenharmony_ci	 * Find out which node the error address belongs to. This may be
12978c2ecf20Sopenharmony_ci	 * different from the node that detected the error.
12988c2ecf20Sopenharmony_ci	 */
12998c2ecf20Sopenharmony_ci	err->src_mci = find_mc_by_sys_addr(mci, sys_addr);
13008c2ecf20Sopenharmony_ci	if (!err->src_mci) {
13018c2ecf20Sopenharmony_ci		amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
13028c2ecf20Sopenharmony_ci			     (unsigned long)sys_addr);
13038c2ecf20Sopenharmony_ci		err->err_code = ERR_NODE;
13048c2ecf20Sopenharmony_ci		return;
13058c2ecf20Sopenharmony_ci	}
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	/* Now map the sys_addr to a CSROW */
13088c2ecf20Sopenharmony_ci	err->csrow = sys_addr_to_csrow(err->src_mci, sys_addr);
13098c2ecf20Sopenharmony_ci	if (err->csrow < 0) {
13108c2ecf20Sopenharmony_ci		err->err_code = ERR_CSROW;
13118c2ecf20Sopenharmony_ci		return;
13128c2ecf20Sopenharmony_ci	}
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci	/* CHIPKILL enabled */
13158c2ecf20Sopenharmony_ci	if (pvt->nbcfg & NBCFG_CHIPKILL) {
13168c2ecf20Sopenharmony_ci		err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
13178c2ecf20Sopenharmony_ci		if (err->channel < 0) {
13188c2ecf20Sopenharmony_ci			/*
13198c2ecf20Sopenharmony_ci			 * Syndrome didn't map, so we don't know which of the
13208c2ecf20Sopenharmony_ci			 * 2 DIMMs is in error. So we need to ID 'both' of them
13218c2ecf20Sopenharmony_ci			 * as suspect.
13228c2ecf20Sopenharmony_ci			 */
13238c2ecf20Sopenharmony_ci			amd64_mc_warn(err->src_mci, "unknown syndrome 0x%04x - "
13248c2ecf20Sopenharmony_ci				      "possible error reporting race\n",
13258c2ecf20Sopenharmony_ci				      err->syndrome);
13268c2ecf20Sopenharmony_ci			err->err_code = ERR_CHANNEL;
13278c2ecf20Sopenharmony_ci			return;
13288c2ecf20Sopenharmony_ci		}
13298c2ecf20Sopenharmony_ci	} else {
13308c2ecf20Sopenharmony_ci		/*
13318c2ecf20Sopenharmony_ci		 * non-chipkill ecc mode
13328c2ecf20Sopenharmony_ci		 *
13338c2ecf20Sopenharmony_ci		 * The k8 documentation is unclear about how to determine the
13348c2ecf20Sopenharmony_ci		 * channel number when using non-chipkill memory.  This method
13358c2ecf20Sopenharmony_ci		 * was obtained from email communication with someone at AMD.
13368c2ecf20Sopenharmony_ci		 * (Wish the email was placed in this comment - norsk)
13378c2ecf20Sopenharmony_ci		 */
13388c2ecf20Sopenharmony_ci		err->channel = ((sys_addr & BIT(3)) != 0);
13398c2ecf20Sopenharmony_ci	}
13408c2ecf20Sopenharmony_ci}
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_cistatic int ddr2_cs_size(unsigned i, bool dct_width)
13438c2ecf20Sopenharmony_ci{
13448c2ecf20Sopenharmony_ci	unsigned shift = 0;
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	if (i <= 2)
13478c2ecf20Sopenharmony_ci		shift = i;
13488c2ecf20Sopenharmony_ci	else if (!(i & 0x1))
13498c2ecf20Sopenharmony_ci		shift = i >> 1;
13508c2ecf20Sopenharmony_ci	else
13518c2ecf20Sopenharmony_ci		shift = (i + 1) >> 1;
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	return 128 << (shift + !!dct_width);
13548c2ecf20Sopenharmony_ci}
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_cistatic int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
13578c2ecf20Sopenharmony_ci				  unsigned cs_mode, int cs_mask_nr)
13588c2ecf20Sopenharmony_ci{
13598c2ecf20Sopenharmony_ci	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	if (pvt->ext_model >= K8_REV_F) {
13628c2ecf20Sopenharmony_ci		WARN_ON(cs_mode > 11);
13638c2ecf20Sopenharmony_ci		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
13648c2ecf20Sopenharmony_ci	}
13658c2ecf20Sopenharmony_ci	else if (pvt->ext_model >= K8_REV_D) {
13668c2ecf20Sopenharmony_ci		unsigned diff;
13678c2ecf20Sopenharmony_ci		WARN_ON(cs_mode > 10);
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci		/*
13708c2ecf20Sopenharmony_ci		 * the below calculation, besides trying to win an obfuscated C
13718c2ecf20Sopenharmony_ci		 * contest, maps cs_mode values to DIMM chip select sizes. The
13728c2ecf20Sopenharmony_ci		 * mappings are:
13738c2ecf20Sopenharmony_ci		 *
13748c2ecf20Sopenharmony_ci		 * cs_mode	CS size (mb)
13758c2ecf20Sopenharmony_ci		 * =======	============
13768c2ecf20Sopenharmony_ci		 * 0		32
13778c2ecf20Sopenharmony_ci		 * 1		64
13788c2ecf20Sopenharmony_ci		 * 2		128
13798c2ecf20Sopenharmony_ci		 * 3		128
13808c2ecf20Sopenharmony_ci		 * 4		256
13818c2ecf20Sopenharmony_ci		 * 5		512
13828c2ecf20Sopenharmony_ci		 * 6		256
13838c2ecf20Sopenharmony_ci		 * 7		512
13848c2ecf20Sopenharmony_ci		 * 8		1024
13858c2ecf20Sopenharmony_ci		 * 9		1024
13868c2ecf20Sopenharmony_ci		 * 10		2048
13878c2ecf20Sopenharmony_ci		 *
13888c2ecf20Sopenharmony_ci		 * Basically, it calculates a value with which to shift the
13898c2ecf20Sopenharmony_ci		 * smallest CS size of 32MB.
13908c2ecf20Sopenharmony_ci		 *
13918c2ecf20Sopenharmony_ci		 * ddr[23]_cs_size have a similar purpose.
13928c2ecf20Sopenharmony_ci		 */
13938c2ecf20Sopenharmony_ci		diff = cs_mode/3 + (unsigned)(cs_mode > 5);
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci		return 32 << (cs_mode - diff);
13968c2ecf20Sopenharmony_ci	}
13978c2ecf20Sopenharmony_ci	else {
13988c2ecf20Sopenharmony_ci		WARN_ON(cs_mode > 6);
13998c2ecf20Sopenharmony_ci		return 32 << cs_mode;
14008c2ecf20Sopenharmony_ci	}
14018c2ecf20Sopenharmony_ci}
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci/*
14048c2ecf20Sopenharmony_ci * Get the number of DCT channels in use.
14058c2ecf20Sopenharmony_ci *
14068c2ecf20Sopenharmony_ci * Return:
14078c2ecf20Sopenharmony_ci *	number of Memory Channels in operation
14088c2ecf20Sopenharmony_ci * Pass back:
14098c2ecf20Sopenharmony_ci *	contents of the DCL0_LOW register
14108c2ecf20Sopenharmony_ci */
14118c2ecf20Sopenharmony_cistatic int f1x_early_channel_count(struct amd64_pvt *pvt)
14128c2ecf20Sopenharmony_ci{
14138c2ecf20Sopenharmony_ci	int i, j, channels = 0;
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	/* On F10h, if we are in 128 bit mode, then we are using 2 channels */
14168c2ecf20Sopenharmony_ci	if (pvt->fam == 0x10 && (pvt->dclr0 & WIDTH_128))
14178c2ecf20Sopenharmony_ci		return 2;
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	/*
14208c2ecf20Sopenharmony_ci	 * Need to check if in unganged mode: In such, there are 2 channels,
14218c2ecf20Sopenharmony_ci	 * but they are not in 128 bit mode and thus the above 'dclr0' status
14228c2ecf20Sopenharmony_ci	 * bit will be OFF.
14238c2ecf20Sopenharmony_ci	 *
14248c2ecf20Sopenharmony_ci	 * Need to check DCT0[0] and DCT1[0] to see if only one of them has
14258c2ecf20Sopenharmony_ci	 * their CSEnable bit on. If so, then SINGLE DIMM case.
14268c2ecf20Sopenharmony_ci	 */
14278c2ecf20Sopenharmony_ci	edac_dbg(0, "Data width is not 128 bits - need more decoding\n");
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	/*
14308c2ecf20Sopenharmony_ci	 * Check DRAM Bank Address Mapping values for each DIMM to see if there
14318c2ecf20Sopenharmony_ci	 * is more than just one DIMM present in unganged mode. Need to check
14328c2ecf20Sopenharmony_ci	 * both controllers since DIMMs can be placed in either one.
14338c2ecf20Sopenharmony_ci	 */
14348c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
14358c2ecf20Sopenharmony_ci		u32 dbam = (i ? pvt->dbam1 : pvt->dbam0);
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci		for (j = 0; j < 4; j++) {
14388c2ecf20Sopenharmony_ci			if (DBAM_DIMM(j, dbam) > 0) {
14398c2ecf20Sopenharmony_ci				channels++;
14408c2ecf20Sopenharmony_ci				break;
14418c2ecf20Sopenharmony_ci			}
14428c2ecf20Sopenharmony_ci		}
14438c2ecf20Sopenharmony_ci	}
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	if (channels > 2)
14468c2ecf20Sopenharmony_ci		channels = 2;
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	amd64_info("MCT channel count: %d\n", channels);
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	return channels;
14518c2ecf20Sopenharmony_ci}
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_cistatic int f17_early_channel_count(struct amd64_pvt *pvt)
14548c2ecf20Sopenharmony_ci{
14558c2ecf20Sopenharmony_ci	int i, channels = 0;
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	/* SDP Control bit 31 (SdpInit) is clear for unused UMC channels */
14588c2ecf20Sopenharmony_ci	for_each_umc(i)
14598c2ecf20Sopenharmony_ci		channels += !!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT);
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	amd64_info("MCT channel count: %d\n", channels);
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	return channels;
14648c2ecf20Sopenharmony_ci}
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_cistatic int ddr3_cs_size(unsigned i, bool dct_width)
14678c2ecf20Sopenharmony_ci{
14688c2ecf20Sopenharmony_ci	unsigned shift = 0;
14698c2ecf20Sopenharmony_ci	int cs_size = 0;
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	if (i == 0 || i == 3 || i == 4)
14728c2ecf20Sopenharmony_ci		cs_size = -1;
14738c2ecf20Sopenharmony_ci	else if (i <= 2)
14748c2ecf20Sopenharmony_ci		shift = i;
14758c2ecf20Sopenharmony_ci	else if (i == 12)
14768c2ecf20Sopenharmony_ci		shift = 7;
14778c2ecf20Sopenharmony_ci	else if (!(i & 0x1))
14788c2ecf20Sopenharmony_ci		shift = i >> 1;
14798c2ecf20Sopenharmony_ci	else
14808c2ecf20Sopenharmony_ci		shift = (i + 1) >> 1;
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci	if (cs_size != -1)
14838c2ecf20Sopenharmony_ci		cs_size = (128 * (1 << !!dct_width)) << shift;
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci	return cs_size;
14868c2ecf20Sopenharmony_ci}
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_cistatic int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply)
14898c2ecf20Sopenharmony_ci{
14908c2ecf20Sopenharmony_ci	unsigned shift = 0;
14918c2ecf20Sopenharmony_ci	int cs_size = 0;
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	if (i < 4 || i == 6)
14948c2ecf20Sopenharmony_ci		cs_size = -1;
14958c2ecf20Sopenharmony_ci	else if (i == 12)
14968c2ecf20Sopenharmony_ci		shift = 7;
14978c2ecf20Sopenharmony_ci	else if (!(i & 0x1))
14988c2ecf20Sopenharmony_ci		shift = i >> 1;
14998c2ecf20Sopenharmony_ci	else
15008c2ecf20Sopenharmony_ci		shift = (i + 1) >> 1;
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	if (cs_size != -1)
15038c2ecf20Sopenharmony_ci		cs_size = rank_multiply * (128 << shift);
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	return cs_size;
15068c2ecf20Sopenharmony_ci}
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_cistatic int ddr4_cs_size(unsigned i)
15098c2ecf20Sopenharmony_ci{
15108c2ecf20Sopenharmony_ci	int cs_size = 0;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	if (i == 0)
15138c2ecf20Sopenharmony_ci		cs_size = -1;
15148c2ecf20Sopenharmony_ci	else if (i == 1)
15158c2ecf20Sopenharmony_ci		cs_size = 1024;
15168c2ecf20Sopenharmony_ci	else
15178c2ecf20Sopenharmony_ci		/* Min cs_size = 1G */
15188c2ecf20Sopenharmony_ci		cs_size = 1024 * (1 << (i >> 1));
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_ci	return cs_size;
15218c2ecf20Sopenharmony_ci}
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_cistatic int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
15248c2ecf20Sopenharmony_ci				   unsigned cs_mode, int cs_mask_nr)
15258c2ecf20Sopenharmony_ci{
15268c2ecf20Sopenharmony_ci	u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	WARN_ON(cs_mode > 11);
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
15318c2ecf20Sopenharmony_ci		return ddr3_cs_size(cs_mode, dclr & WIDTH_128);
15328c2ecf20Sopenharmony_ci	else
15338c2ecf20Sopenharmony_ci		return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
15348c2ecf20Sopenharmony_ci}
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci/*
15378c2ecf20Sopenharmony_ci * F15h supports only 64bit DCT interfaces
15388c2ecf20Sopenharmony_ci */
15398c2ecf20Sopenharmony_cistatic int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
15408c2ecf20Sopenharmony_ci				   unsigned cs_mode, int cs_mask_nr)
15418c2ecf20Sopenharmony_ci{
15428c2ecf20Sopenharmony_ci	WARN_ON(cs_mode > 12);
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	return ddr3_cs_size(cs_mode, false);
15458c2ecf20Sopenharmony_ci}
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_ci/* F15h M60h supports DDR4 mapping as well.. */
15488c2ecf20Sopenharmony_cistatic int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
15498c2ecf20Sopenharmony_ci					unsigned cs_mode, int cs_mask_nr)
15508c2ecf20Sopenharmony_ci{
15518c2ecf20Sopenharmony_ci	int cs_size;
15528c2ecf20Sopenharmony_ci	u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr];
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	WARN_ON(cs_mode > 12);
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci	if (pvt->dram_type == MEM_DDR4) {
15578c2ecf20Sopenharmony_ci		if (cs_mode > 9)
15588c2ecf20Sopenharmony_ci			return -1;
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci		cs_size = ddr4_cs_size(cs_mode);
15618c2ecf20Sopenharmony_ci	} else if (pvt->dram_type == MEM_LRDDR3) {
15628c2ecf20Sopenharmony_ci		unsigned rank_multiply = dcsm & 0xf;
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci		if (rank_multiply == 3)
15658c2ecf20Sopenharmony_ci			rank_multiply = 4;
15668c2ecf20Sopenharmony_ci		cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply);
15678c2ecf20Sopenharmony_ci	} else {
15688c2ecf20Sopenharmony_ci		/* Minimum cs size is 512mb for F15hM60h*/
15698c2ecf20Sopenharmony_ci		if (cs_mode == 0x1)
15708c2ecf20Sopenharmony_ci			return -1;
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci		cs_size = ddr3_cs_size(cs_mode, false);
15738c2ecf20Sopenharmony_ci	}
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci	return cs_size;
15768c2ecf20Sopenharmony_ci}
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_ci/*
15798c2ecf20Sopenharmony_ci * F16h and F15h model 30h have only limited cs_modes.
15808c2ecf20Sopenharmony_ci */
15818c2ecf20Sopenharmony_cistatic int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
15828c2ecf20Sopenharmony_ci				unsigned cs_mode, int cs_mask_nr)
15838c2ecf20Sopenharmony_ci{
15848c2ecf20Sopenharmony_ci	WARN_ON(cs_mode > 12);
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci	if (cs_mode == 6 || cs_mode == 8 ||
15878c2ecf20Sopenharmony_ci	    cs_mode == 9 || cs_mode == 12)
15888c2ecf20Sopenharmony_ci		return -1;
15898c2ecf20Sopenharmony_ci	else
15908c2ecf20Sopenharmony_ci		return ddr3_cs_size(cs_mode, false);
15918c2ecf20Sopenharmony_ci}
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_cistatic int f17_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc,
15948c2ecf20Sopenharmony_ci				    unsigned int cs_mode, int csrow_nr)
15958c2ecf20Sopenharmony_ci{
15968c2ecf20Sopenharmony_ci	u32 addr_mask_orig, addr_mask_deinterleaved;
15978c2ecf20Sopenharmony_ci	u32 msb, weight, num_zero_bits;
15988c2ecf20Sopenharmony_ci	int dimm, size = 0;
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	/* No Chip Selects are enabled. */
16018c2ecf20Sopenharmony_ci	if (!cs_mode)
16028c2ecf20Sopenharmony_ci		return size;
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci	/* Requested size of an even CS but none are enabled. */
16058c2ecf20Sopenharmony_ci	if (!(cs_mode & CS_EVEN) && !(csrow_nr & 1))
16068c2ecf20Sopenharmony_ci		return size;
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci	/* Requested size of an odd CS but none are enabled. */
16098c2ecf20Sopenharmony_ci	if (!(cs_mode & CS_ODD) && (csrow_nr & 1))
16108c2ecf20Sopenharmony_ci		return size;
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci	/*
16138c2ecf20Sopenharmony_ci	 * There is one mask per DIMM, and two Chip Selects per DIMM.
16148c2ecf20Sopenharmony_ci	 *	CS0 and CS1 -> DIMM0
16158c2ecf20Sopenharmony_ci	 *	CS2 and CS3 -> DIMM1
16168c2ecf20Sopenharmony_ci	 */
16178c2ecf20Sopenharmony_ci	dimm = csrow_nr >> 1;
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	/* Asymmetric dual-rank DIMM support. */
16208c2ecf20Sopenharmony_ci	if ((csrow_nr & 1) && (cs_mode & CS_ODD_SECONDARY))
16218c2ecf20Sopenharmony_ci		addr_mask_orig = pvt->csels[umc].csmasks_sec[dimm];
16228c2ecf20Sopenharmony_ci	else
16238c2ecf20Sopenharmony_ci		addr_mask_orig = pvt->csels[umc].csmasks[dimm];
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci	/*
16268c2ecf20Sopenharmony_ci	 * The number of zero bits in the mask is equal to the number of bits
16278c2ecf20Sopenharmony_ci	 * in a full mask minus the number of bits in the current mask.
16288c2ecf20Sopenharmony_ci	 *
16298c2ecf20Sopenharmony_ci	 * The MSB is the number of bits in the full mask because BIT[0] is
16308c2ecf20Sopenharmony_ci	 * always 0.
16318c2ecf20Sopenharmony_ci	 *
16328c2ecf20Sopenharmony_ci	 * In the special 3 Rank interleaving case, a single bit is flipped
16338c2ecf20Sopenharmony_ci	 * without swapping with the most significant bit. This can be handled
16348c2ecf20Sopenharmony_ci	 * by keeping the MSB where it is and ignoring the single zero bit.
16358c2ecf20Sopenharmony_ci	 */
16368c2ecf20Sopenharmony_ci	msb = fls(addr_mask_orig) - 1;
16378c2ecf20Sopenharmony_ci	weight = hweight_long(addr_mask_orig);
16388c2ecf20Sopenharmony_ci	num_zero_bits = msb - weight - !!(cs_mode & CS_3R_INTERLEAVE);
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci	/* Take the number of zero bits off from the top of the mask. */
16418c2ecf20Sopenharmony_ci	addr_mask_deinterleaved = GENMASK_ULL(msb - num_zero_bits, 1);
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	edac_dbg(1, "CS%d DIMM%d AddrMasks:\n", csrow_nr, dimm);
16448c2ecf20Sopenharmony_ci	edac_dbg(1, "  Original AddrMask: 0x%x\n", addr_mask_orig);
16458c2ecf20Sopenharmony_ci	edac_dbg(1, "  Deinterleaved AddrMask: 0x%x\n", addr_mask_deinterleaved);
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	/* Register [31:1] = Address [39:9]. Size is in kBs here. */
16488c2ecf20Sopenharmony_ci	size = (addr_mask_deinterleaved >> 2) + 1;
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	/* Return size in MBs. */
16518c2ecf20Sopenharmony_ci	return size >> 10;
16528c2ecf20Sopenharmony_ci}
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_cistatic void read_dram_ctl_register(struct amd64_pvt *pvt)
16558c2ecf20Sopenharmony_ci{
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	if (pvt->fam == 0xf)
16588c2ecf20Sopenharmony_ci		return;
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	if (!amd64_read_pci_cfg(pvt->F2, DCT_SEL_LO, &pvt->dct_sel_lo)) {
16618c2ecf20Sopenharmony_ci		edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
16628c2ecf20Sopenharmony_ci			 pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ci		edac_dbg(0, "  DCTs operate in %s mode\n",
16658c2ecf20Sopenharmony_ci			 (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ci		if (!dct_ganging_enabled(pvt))
16688c2ecf20Sopenharmony_ci			edac_dbg(0, "  Address range split per DCT: %s\n",
16698c2ecf20Sopenharmony_ci				 (dct_high_range_enabled(pvt) ? "yes" : "no"));
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci		edac_dbg(0, "  data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n",
16728c2ecf20Sopenharmony_ci			 (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
16738c2ecf20Sopenharmony_ci			 (dct_memory_cleared(pvt) ? "yes" : "no"));
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci		edac_dbg(0, "  channel interleave: %s, "
16768c2ecf20Sopenharmony_ci			 "interleave bits selector: 0x%x\n",
16778c2ecf20Sopenharmony_ci			 (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
16788c2ecf20Sopenharmony_ci			 dct_sel_interleave_addr(pvt));
16798c2ecf20Sopenharmony_ci	}
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	amd64_read_pci_cfg(pvt->F2, DCT_SEL_HI, &pvt->dct_sel_hi);
16828c2ecf20Sopenharmony_ci}
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci/*
16858c2ecf20Sopenharmony_ci * Determine channel (DCT) based on the interleaving mode (see F15h M30h BKDG,
16868c2ecf20Sopenharmony_ci * 2.10.12 Memory Interleaving Modes).
16878c2ecf20Sopenharmony_ci */
16888c2ecf20Sopenharmony_cistatic u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
16898c2ecf20Sopenharmony_ci				     u8 intlv_en, int num_dcts_intlv,
16908c2ecf20Sopenharmony_ci				     u32 dct_sel)
16918c2ecf20Sopenharmony_ci{
16928c2ecf20Sopenharmony_ci	u8 channel = 0;
16938c2ecf20Sopenharmony_ci	u8 select;
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	if (!(intlv_en))
16968c2ecf20Sopenharmony_ci		return (u8)(dct_sel);
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	if (num_dcts_intlv == 2) {
16998c2ecf20Sopenharmony_ci		select = (sys_addr >> 8) & 0x3;
17008c2ecf20Sopenharmony_ci		channel = select ? 0x3 : 0;
17018c2ecf20Sopenharmony_ci	} else if (num_dcts_intlv == 4) {
17028c2ecf20Sopenharmony_ci		u8 intlv_addr = dct_sel_interleave_addr(pvt);
17038c2ecf20Sopenharmony_ci		switch (intlv_addr) {
17048c2ecf20Sopenharmony_ci		case 0x4:
17058c2ecf20Sopenharmony_ci			channel = (sys_addr >> 8) & 0x3;
17068c2ecf20Sopenharmony_ci			break;
17078c2ecf20Sopenharmony_ci		case 0x5:
17088c2ecf20Sopenharmony_ci			channel = (sys_addr >> 9) & 0x3;
17098c2ecf20Sopenharmony_ci			break;
17108c2ecf20Sopenharmony_ci		}
17118c2ecf20Sopenharmony_ci	}
17128c2ecf20Sopenharmony_ci	return channel;
17138c2ecf20Sopenharmony_ci}
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ci/*
17168c2ecf20Sopenharmony_ci * Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory
17178c2ecf20Sopenharmony_ci * Interleaving Modes.
17188c2ecf20Sopenharmony_ci */
17198c2ecf20Sopenharmony_cistatic u8 f1x_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
17208c2ecf20Sopenharmony_ci				bool hi_range_sel, u8 intlv_en)
17218c2ecf20Sopenharmony_ci{
17228c2ecf20Sopenharmony_ci	u8 dct_sel_high = (pvt->dct_sel_lo >> 1) & 1;
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	if (dct_ganging_enabled(pvt))
17258c2ecf20Sopenharmony_ci		return 0;
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci	if (hi_range_sel)
17288c2ecf20Sopenharmony_ci		return dct_sel_high;
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	/*
17318c2ecf20Sopenharmony_ci	 * see F2x110[DctSelIntLvAddr] - channel interleave mode
17328c2ecf20Sopenharmony_ci	 */
17338c2ecf20Sopenharmony_ci	if (dct_interleave_enabled(pvt)) {
17348c2ecf20Sopenharmony_ci		u8 intlv_addr = dct_sel_interleave_addr(pvt);
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci		/* return DCT select function: 0=DCT0, 1=DCT1 */
17378c2ecf20Sopenharmony_ci		if (!intlv_addr)
17388c2ecf20Sopenharmony_ci			return sys_addr >> 6 & 1;
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci		if (intlv_addr & 0x2) {
17418c2ecf20Sopenharmony_ci			u8 shift = intlv_addr & 0x1 ? 9 : 6;
17428c2ecf20Sopenharmony_ci			u32 temp = hweight_long((u32) ((sys_addr >> 16) & 0x1F)) & 1;
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci			return ((sys_addr >> shift) & 1) ^ temp;
17458c2ecf20Sopenharmony_ci		}
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci		if (intlv_addr & 0x4) {
17488c2ecf20Sopenharmony_ci			u8 shift = intlv_addr & 0x1 ? 9 : 8;
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_ci			return (sys_addr >> shift) & 1;
17518c2ecf20Sopenharmony_ci		}
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci		return (sys_addr >> (12 + hweight8(intlv_en))) & 1;
17548c2ecf20Sopenharmony_ci	}
17558c2ecf20Sopenharmony_ci
17568c2ecf20Sopenharmony_ci	if (dct_high_range_enabled(pvt))
17578c2ecf20Sopenharmony_ci		return ~dct_sel_high & 1;
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci	return 0;
17608c2ecf20Sopenharmony_ci}
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_ci/* Convert the sys_addr to the normalized DCT address */
17638c2ecf20Sopenharmony_cistatic u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range,
17648c2ecf20Sopenharmony_ci				 u64 sys_addr, bool hi_rng,
17658c2ecf20Sopenharmony_ci				 u32 dct_sel_base_addr)
17668c2ecf20Sopenharmony_ci{
17678c2ecf20Sopenharmony_ci	u64 chan_off;
17688c2ecf20Sopenharmony_ci	u64 dram_base		= get_dram_base(pvt, range);
17698c2ecf20Sopenharmony_ci	u64 hole_off		= f10_dhar_offset(pvt);
17708c2ecf20Sopenharmony_ci	u64 dct_sel_base_off	= (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16;
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	if (hi_rng) {
17738c2ecf20Sopenharmony_ci		/*
17748c2ecf20Sopenharmony_ci		 * if
17758c2ecf20Sopenharmony_ci		 * base address of high range is below 4Gb
17768c2ecf20Sopenharmony_ci		 * (bits [47:27] at [31:11])
17778c2ecf20Sopenharmony_ci		 * DRAM address space on this DCT is hoisted above 4Gb	&&
17788c2ecf20Sopenharmony_ci		 * sys_addr > 4Gb
17798c2ecf20Sopenharmony_ci		 *
17808c2ecf20Sopenharmony_ci		 *	remove hole offset from sys_addr
17818c2ecf20Sopenharmony_ci		 * else
17828c2ecf20Sopenharmony_ci		 *	remove high range offset from sys_addr
17838c2ecf20Sopenharmony_ci		 */
17848c2ecf20Sopenharmony_ci		if ((!(dct_sel_base_addr >> 16) ||
17858c2ecf20Sopenharmony_ci		     dct_sel_base_addr < dhar_base(pvt)) &&
17868c2ecf20Sopenharmony_ci		    dhar_valid(pvt) &&
17878c2ecf20Sopenharmony_ci		    (sys_addr >= BIT_64(32)))
17888c2ecf20Sopenharmony_ci			chan_off = hole_off;
17898c2ecf20Sopenharmony_ci		else
17908c2ecf20Sopenharmony_ci			chan_off = dct_sel_base_off;
17918c2ecf20Sopenharmony_ci	} else {
17928c2ecf20Sopenharmony_ci		/*
17938c2ecf20Sopenharmony_ci		 * if
17948c2ecf20Sopenharmony_ci		 * we have a valid hole		&&
17958c2ecf20Sopenharmony_ci		 * sys_addr > 4Gb
17968c2ecf20Sopenharmony_ci		 *
17978c2ecf20Sopenharmony_ci		 *	remove hole
17988c2ecf20Sopenharmony_ci		 * else
17998c2ecf20Sopenharmony_ci		 *	remove dram base to normalize to DCT address
18008c2ecf20Sopenharmony_ci		 */
18018c2ecf20Sopenharmony_ci		if (dhar_valid(pvt) && (sys_addr >= BIT_64(32)))
18028c2ecf20Sopenharmony_ci			chan_off = hole_off;
18038c2ecf20Sopenharmony_ci		else
18048c2ecf20Sopenharmony_ci			chan_off = dram_base;
18058c2ecf20Sopenharmony_ci	}
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci	return (sys_addr & GENMASK_ULL(47,6)) - (chan_off & GENMASK_ULL(47,23));
18088c2ecf20Sopenharmony_ci}
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_ci/*
18118c2ecf20Sopenharmony_ci * checks if the csrow passed in is marked as SPARED, if so returns the new
18128c2ecf20Sopenharmony_ci * spare row
18138c2ecf20Sopenharmony_ci */
18148c2ecf20Sopenharmony_cistatic int f10_process_possible_spare(struct amd64_pvt *pvt, u8 dct, int csrow)
18158c2ecf20Sopenharmony_ci{
18168c2ecf20Sopenharmony_ci	int tmp_cs;
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci	if (online_spare_swap_done(pvt, dct) &&
18198c2ecf20Sopenharmony_ci	    csrow == online_spare_bad_dramcs(pvt, dct)) {
18208c2ecf20Sopenharmony_ci
18218c2ecf20Sopenharmony_ci		for_each_chip_select(tmp_cs, dct, pvt) {
18228c2ecf20Sopenharmony_ci			if (chip_select_base(tmp_cs, dct, pvt) & 0x2) {
18238c2ecf20Sopenharmony_ci				csrow = tmp_cs;
18248c2ecf20Sopenharmony_ci				break;
18258c2ecf20Sopenharmony_ci			}
18268c2ecf20Sopenharmony_ci		}
18278c2ecf20Sopenharmony_ci	}
18288c2ecf20Sopenharmony_ci	return csrow;
18298c2ecf20Sopenharmony_ci}
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci/*
18328c2ecf20Sopenharmony_ci * Iterate over the DRAM DCT "base" and "mask" registers looking for a
18338c2ecf20Sopenharmony_ci * SystemAddr match on the specified 'ChannelSelect' and 'NodeID'
18348c2ecf20Sopenharmony_ci *
18358c2ecf20Sopenharmony_ci * Return:
18368c2ecf20Sopenharmony_ci *	-EINVAL:  NOT FOUND
18378c2ecf20Sopenharmony_ci *	0..csrow = Chip-Select Row
18388c2ecf20Sopenharmony_ci */
18398c2ecf20Sopenharmony_cistatic int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct)
18408c2ecf20Sopenharmony_ci{
18418c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci;
18428c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt;
18438c2ecf20Sopenharmony_ci	u64 cs_base, cs_mask;
18448c2ecf20Sopenharmony_ci	int cs_found = -EINVAL;
18458c2ecf20Sopenharmony_ci	int csrow;
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_ci	mci = edac_mc_find(nid);
18488c2ecf20Sopenharmony_ci	if (!mci)
18498c2ecf20Sopenharmony_ci		return cs_found;
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	pvt = mci->pvt_info;
18528c2ecf20Sopenharmony_ci
18538c2ecf20Sopenharmony_ci	edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct);
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_ci	for_each_chip_select(csrow, dct, pvt) {
18568c2ecf20Sopenharmony_ci		if (!csrow_enabled(csrow, dct, pvt))
18578c2ecf20Sopenharmony_ci			continue;
18588c2ecf20Sopenharmony_ci
18598c2ecf20Sopenharmony_ci		get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask);
18608c2ecf20Sopenharmony_ci
18618c2ecf20Sopenharmony_ci		edac_dbg(1, "    CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
18628c2ecf20Sopenharmony_ci			 csrow, cs_base, cs_mask);
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci		cs_mask = ~cs_mask;
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_ci		edac_dbg(1, "    (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n",
18678c2ecf20Sopenharmony_ci			 (in_addr & cs_mask), (cs_base & cs_mask));
18688c2ecf20Sopenharmony_ci
18698c2ecf20Sopenharmony_ci		if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
18708c2ecf20Sopenharmony_ci			if (pvt->fam == 0x15 && pvt->model >= 0x30) {
18718c2ecf20Sopenharmony_ci				cs_found =  csrow;
18728c2ecf20Sopenharmony_ci				break;
18738c2ecf20Sopenharmony_ci			}
18748c2ecf20Sopenharmony_ci			cs_found = f10_process_possible_spare(pvt, dct, csrow);
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_ci			edac_dbg(1, " MATCH csrow=%d\n", cs_found);
18778c2ecf20Sopenharmony_ci			break;
18788c2ecf20Sopenharmony_ci		}
18798c2ecf20Sopenharmony_ci	}
18808c2ecf20Sopenharmony_ci	return cs_found;
18818c2ecf20Sopenharmony_ci}
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci/*
18848c2ecf20Sopenharmony_ci * See F2x10C. Non-interleaved graphics framebuffer memory under the 16G is
18858c2ecf20Sopenharmony_ci * swapped with a region located at the bottom of memory so that the GPU can use
18868c2ecf20Sopenharmony_ci * the interleaved region and thus two channels.
18878c2ecf20Sopenharmony_ci */
18888c2ecf20Sopenharmony_cistatic u64 f1x_swap_interleaved_region(struct amd64_pvt *pvt, u64 sys_addr)
18898c2ecf20Sopenharmony_ci{
18908c2ecf20Sopenharmony_ci	u32 swap_reg, swap_base, swap_limit, rgn_size, tmp_addr;
18918c2ecf20Sopenharmony_ci
18928c2ecf20Sopenharmony_ci	if (pvt->fam == 0x10) {
18938c2ecf20Sopenharmony_ci		/* only revC3 and revE have that feature */
18948c2ecf20Sopenharmony_ci		if (pvt->model < 4 || (pvt->model < 0xa && pvt->stepping < 3))
18958c2ecf20Sopenharmony_ci			return sys_addr;
18968c2ecf20Sopenharmony_ci	}
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci	amd64_read_pci_cfg(pvt->F2, SWAP_INTLV_REG, &swap_reg);
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_ci	if (!(swap_reg & 0x1))
19018c2ecf20Sopenharmony_ci		return sys_addr;
19028c2ecf20Sopenharmony_ci
19038c2ecf20Sopenharmony_ci	swap_base	= (swap_reg >> 3) & 0x7f;
19048c2ecf20Sopenharmony_ci	swap_limit	= (swap_reg >> 11) & 0x7f;
19058c2ecf20Sopenharmony_ci	rgn_size	= (swap_reg >> 20) & 0x7f;
19068c2ecf20Sopenharmony_ci	tmp_addr	= sys_addr >> 27;
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_ci	if (!(sys_addr >> 34) &&
19098c2ecf20Sopenharmony_ci	    (((tmp_addr >= swap_base) &&
19108c2ecf20Sopenharmony_ci	     (tmp_addr <= swap_limit)) ||
19118c2ecf20Sopenharmony_ci	     (tmp_addr < rgn_size)))
19128c2ecf20Sopenharmony_ci		return sys_addr ^ (u64)swap_base << 27;
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_ci	return sys_addr;
19158c2ecf20Sopenharmony_ci}
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci/* For a given @dram_range, check if @sys_addr falls within it. */
19188c2ecf20Sopenharmony_cistatic int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
19198c2ecf20Sopenharmony_ci				  u64 sys_addr, int *chan_sel)
19208c2ecf20Sopenharmony_ci{
19218c2ecf20Sopenharmony_ci	int cs_found = -EINVAL;
19228c2ecf20Sopenharmony_ci	u64 chan_addr;
19238c2ecf20Sopenharmony_ci	u32 dct_sel_base;
19248c2ecf20Sopenharmony_ci	u8 channel;
19258c2ecf20Sopenharmony_ci	bool high_range = false;
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci	u8 node_id    = dram_dst_node(pvt, range);
19288c2ecf20Sopenharmony_ci	u8 intlv_en   = dram_intlv_en(pvt, range);
19298c2ecf20Sopenharmony_ci	u32 intlv_sel = dram_intlv_sel(pvt, range);
19308c2ecf20Sopenharmony_ci
19318c2ecf20Sopenharmony_ci	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
19328c2ecf20Sopenharmony_ci		 range, sys_addr, get_dram_limit(pvt, range));
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_ci	if (dhar_valid(pvt) &&
19358c2ecf20Sopenharmony_ci	    dhar_base(pvt) <= sys_addr &&
19368c2ecf20Sopenharmony_ci	    sys_addr < BIT_64(32)) {
19378c2ecf20Sopenharmony_ci		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
19388c2ecf20Sopenharmony_ci			    sys_addr);
19398c2ecf20Sopenharmony_ci		return -EINVAL;
19408c2ecf20Sopenharmony_ci	}
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci	if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en)))
19438c2ecf20Sopenharmony_ci		return -EINVAL;
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_ci	sys_addr = f1x_swap_interleaved_region(pvt, sys_addr);
19468c2ecf20Sopenharmony_ci
19478c2ecf20Sopenharmony_ci	dct_sel_base = dct_sel_baseaddr(pvt);
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci	/*
19508c2ecf20Sopenharmony_ci	 * check whether addresses >= DctSelBaseAddr[47:27] are to be used to
19518c2ecf20Sopenharmony_ci	 * select between DCT0 and DCT1.
19528c2ecf20Sopenharmony_ci	 */
19538c2ecf20Sopenharmony_ci	if (dct_high_range_enabled(pvt) &&
19548c2ecf20Sopenharmony_ci	   !dct_ganging_enabled(pvt) &&
19558c2ecf20Sopenharmony_ci	   ((sys_addr >> 27) >= (dct_sel_base >> 11)))
19568c2ecf20Sopenharmony_ci		high_range = true;
19578c2ecf20Sopenharmony_ci
19588c2ecf20Sopenharmony_ci	channel = f1x_determine_channel(pvt, sys_addr, high_range, intlv_en);
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci	chan_addr = f1x_get_norm_dct_addr(pvt, range, sys_addr,
19618c2ecf20Sopenharmony_ci					  high_range, dct_sel_base);
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ci	/* Remove node interleaving, see F1x120 */
19648c2ecf20Sopenharmony_ci	if (intlv_en)
19658c2ecf20Sopenharmony_ci		chan_addr = ((chan_addr >> (12 + hweight8(intlv_en))) << 12) |
19668c2ecf20Sopenharmony_ci			    (chan_addr & 0xfff);
19678c2ecf20Sopenharmony_ci
19688c2ecf20Sopenharmony_ci	/* remove channel interleave */
19698c2ecf20Sopenharmony_ci	if (dct_interleave_enabled(pvt) &&
19708c2ecf20Sopenharmony_ci	   !dct_high_range_enabled(pvt) &&
19718c2ecf20Sopenharmony_ci	   !dct_ganging_enabled(pvt)) {
19728c2ecf20Sopenharmony_ci
19738c2ecf20Sopenharmony_ci		if (dct_sel_interleave_addr(pvt) != 1) {
19748c2ecf20Sopenharmony_ci			if (dct_sel_interleave_addr(pvt) == 0x3)
19758c2ecf20Sopenharmony_ci				/* hash 9 */
19768c2ecf20Sopenharmony_ci				chan_addr = ((chan_addr >> 10) << 9) |
19778c2ecf20Sopenharmony_ci					     (chan_addr & 0x1ff);
19788c2ecf20Sopenharmony_ci			else
19798c2ecf20Sopenharmony_ci				/* A[6] or hash 6 */
19808c2ecf20Sopenharmony_ci				chan_addr = ((chan_addr >> 7) << 6) |
19818c2ecf20Sopenharmony_ci					     (chan_addr & 0x3f);
19828c2ecf20Sopenharmony_ci		} else
19838c2ecf20Sopenharmony_ci			/* A[12] */
19848c2ecf20Sopenharmony_ci			chan_addr = ((chan_addr >> 13) << 12) |
19858c2ecf20Sopenharmony_ci				     (chan_addr & 0xfff);
19868c2ecf20Sopenharmony_ci	}
19878c2ecf20Sopenharmony_ci
19888c2ecf20Sopenharmony_ci	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_ci	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel);
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci	if (cs_found >= 0)
19938c2ecf20Sopenharmony_ci		*chan_sel = channel;
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	return cs_found;
19968c2ecf20Sopenharmony_ci}
19978c2ecf20Sopenharmony_ci
19988c2ecf20Sopenharmony_cistatic int f15_m30h_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
19998c2ecf20Sopenharmony_ci					u64 sys_addr, int *chan_sel)
20008c2ecf20Sopenharmony_ci{
20018c2ecf20Sopenharmony_ci	int cs_found = -EINVAL;
20028c2ecf20Sopenharmony_ci	int num_dcts_intlv = 0;
20038c2ecf20Sopenharmony_ci	u64 chan_addr, chan_offset;
20048c2ecf20Sopenharmony_ci	u64 dct_base, dct_limit;
20058c2ecf20Sopenharmony_ci	u32 dct_cont_base_reg, dct_cont_limit_reg, tmp;
20068c2ecf20Sopenharmony_ci	u8 channel, alias_channel, leg_mmio_hole, dct_sel, dct_offset_en;
20078c2ecf20Sopenharmony_ci
20088c2ecf20Sopenharmony_ci	u64 dhar_offset		= f10_dhar_offset(pvt);
20098c2ecf20Sopenharmony_ci	u8 intlv_addr		= dct_sel_interleave_addr(pvt);
20108c2ecf20Sopenharmony_ci	u8 node_id		= dram_dst_node(pvt, range);
20118c2ecf20Sopenharmony_ci	u8 intlv_en		= dram_intlv_en(pvt, range);
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_ci	amd64_read_pci_cfg(pvt->F1, DRAM_CONT_BASE, &dct_cont_base_reg);
20148c2ecf20Sopenharmony_ci	amd64_read_pci_cfg(pvt->F1, DRAM_CONT_LIMIT, &dct_cont_limit_reg);
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci	dct_offset_en		= (u8) ((dct_cont_base_reg >> 3) & BIT(0));
20178c2ecf20Sopenharmony_ci	dct_sel			= (u8) ((dct_cont_base_reg >> 4) & 0x7);
20188c2ecf20Sopenharmony_ci
20198c2ecf20Sopenharmony_ci	edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
20208c2ecf20Sopenharmony_ci		 range, sys_addr, get_dram_limit(pvt, range));
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci	if (!(get_dram_base(pvt, range)  <= sys_addr) &&
20238c2ecf20Sopenharmony_ci	    !(get_dram_limit(pvt, range) >= sys_addr))
20248c2ecf20Sopenharmony_ci		return -EINVAL;
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_ci	if (dhar_valid(pvt) &&
20278c2ecf20Sopenharmony_ci	    dhar_base(pvt) <= sys_addr &&
20288c2ecf20Sopenharmony_ci	    sys_addr < BIT_64(32)) {
20298c2ecf20Sopenharmony_ci		amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
20308c2ecf20Sopenharmony_ci			    sys_addr);
20318c2ecf20Sopenharmony_ci		return -EINVAL;
20328c2ecf20Sopenharmony_ci	}
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci	/* Verify sys_addr is within DCT Range. */
20358c2ecf20Sopenharmony_ci	dct_base = (u64) dct_sel_baseaddr(pvt);
20368c2ecf20Sopenharmony_ci	dct_limit = (dct_cont_limit_reg >> 11) & 0x1FFF;
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_ci	if (!(dct_cont_base_reg & BIT(0)) &&
20398c2ecf20Sopenharmony_ci	    !(dct_base <= (sys_addr >> 27) &&
20408c2ecf20Sopenharmony_ci	      dct_limit >= (sys_addr >> 27)))
20418c2ecf20Sopenharmony_ci		return -EINVAL;
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	/* Verify number of dct's that participate in channel interleaving. */
20448c2ecf20Sopenharmony_ci	num_dcts_intlv = (int) hweight8(intlv_en);
20458c2ecf20Sopenharmony_ci
20468c2ecf20Sopenharmony_ci	if (!(num_dcts_intlv % 2 == 0) || (num_dcts_intlv > 4))
20478c2ecf20Sopenharmony_ci		return -EINVAL;
20488c2ecf20Sopenharmony_ci
20498c2ecf20Sopenharmony_ci	if (pvt->model >= 0x60)
20508c2ecf20Sopenharmony_ci		channel = f1x_determine_channel(pvt, sys_addr, false, intlv_en);
20518c2ecf20Sopenharmony_ci	else
20528c2ecf20Sopenharmony_ci		channel = f15_m30h_determine_channel(pvt, sys_addr, intlv_en,
20538c2ecf20Sopenharmony_ci						     num_dcts_intlv, dct_sel);
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_ci	/* Verify we stay within the MAX number of channels allowed */
20568c2ecf20Sopenharmony_ci	if (channel > 3)
20578c2ecf20Sopenharmony_ci		return -EINVAL;
20588c2ecf20Sopenharmony_ci
20598c2ecf20Sopenharmony_ci	leg_mmio_hole = (u8) (dct_cont_base_reg >> 1 & BIT(0));
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci	/* Get normalized DCT addr */
20628c2ecf20Sopenharmony_ci	if (leg_mmio_hole && (sys_addr >= BIT_64(32)))
20638c2ecf20Sopenharmony_ci		chan_offset = dhar_offset;
20648c2ecf20Sopenharmony_ci	else
20658c2ecf20Sopenharmony_ci		chan_offset = dct_base << 27;
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci	chan_addr = sys_addr - chan_offset;
20688c2ecf20Sopenharmony_ci
20698c2ecf20Sopenharmony_ci	/* remove channel interleave */
20708c2ecf20Sopenharmony_ci	if (num_dcts_intlv == 2) {
20718c2ecf20Sopenharmony_ci		if (intlv_addr == 0x4)
20728c2ecf20Sopenharmony_ci			chan_addr = ((chan_addr >> 9) << 8) |
20738c2ecf20Sopenharmony_ci						(chan_addr & 0xff);
20748c2ecf20Sopenharmony_ci		else if (intlv_addr == 0x5)
20758c2ecf20Sopenharmony_ci			chan_addr = ((chan_addr >> 10) << 9) |
20768c2ecf20Sopenharmony_ci						(chan_addr & 0x1ff);
20778c2ecf20Sopenharmony_ci		else
20788c2ecf20Sopenharmony_ci			return -EINVAL;
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_ci	} else if (num_dcts_intlv == 4) {
20818c2ecf20Sopenharmony_ci		if (intlv_addr == 0x4)
20828c2ecf20Sopenharmony_ci			chan_addr = ((chan_addr >> 10) << 8) |
20838c2ecf20Sopenharmony_ci							(chan_addr & 0xff);
20848c2ecf20Sopenharmony_ci		else if (intlv_addr == 0x5)
20858c2ecf20Sopenharmony_ci			chan_addr = ((chan_addr >> 11) << 9) |
20868c2ecf20Sopenharmony_ci							(chan_addr & 0x1ff);
20878c2ecf20Sopenharmony_ci		else
20888c2ecf20Sopenharmony_ci			return -EINVAL;
20898c2ecf20Sopenharmony_ci	}
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_ci	if (dct_offset_en) {
20928c2ecf20Sopenharmony_ci		amd64_read_pci_cfg(pvt->F1,
20938c2ecf20Sopenharmony_ci				   DRAM_CONT_HIGH_OFF + (int) channel * 4,
20948c2ecf20Sopenharmony_ci				   &tmp);
20958c2ecf20Sopenharmony_ci		chan_addr +=  (u64) ((tmp >> 11) & 0xfff) << 27;
20968c2ecf20Sopenharmony_ci	}
20978c2ecf20Sopenharmony_ci
20988c2ecf20Sopenharmony_ci	f15h_select_dct(pvt, channel);
20998c2ecf20Sopenharmony_ci
21008c2ecf20Sopenharmony_ci	edac_dbg(1, "   Normalized DCT addr: 0x%llx\n", chan_addr);
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	/*
21038c2ecf20Sopenharmony_ci	 * Find Chip select:
21048c2ecf20Sopenharmony_ci	 * if channel = 3, then alias it to 1. This is because, in F15 M30h,
21058c2ecf20Sopenharmony_ci	 * there is support for 4 DCT's, but only 2 are currently functional.
21068c2ecf20Sopenharmony_ci	 * They are DCT0 and DCT3. But we have read all registers of DCT3 into
21078c2ecf20Sopenharmony_ci	 * pvt->csels[1]. So we need to use '1' here to get correct info.
21088c2ecf20Sopenharmony_ci	 * Refer F15 M30h BKDG Section 2.10 and 2.10.3 for clarifications.
21098c2ecf20Sopenharmony_ci	 */
21108c2ecf20Sopenharmony_ci	alias_channel =  (channel == 3) ? 1 : channel;
21118c2ecf20Sopenharmony_ci
21128c2ecf20Sopenharmony_ci	cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, alias_channel);
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci	if (cs_found >= 0)
21158c2ecf20Sopenharmony_ci		*chan_sel = alias_channel;
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ci	return cs_found;
21188c2ecf20Sopenharmony_ci}
21198c2ecf20Sopenharmony_ci
21208c2ecf20Sopenharmony_cistatic int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt,
21218c2ecf20Sopenharmony_ci					u64 sys_addr,
21228c2ecf20Sopenharmony_ci					int *chan_sel)
21238c2ecf20Sopenharmony_ci{
21248c2ecf20Sopenharmony_ci	int cs_found = -EINVAL;
21258c2ecf20Sopenharmony_ci	unsigned range;
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	for (range = 0; range < DRAM_RANGES; range++) {
21288c2ecf20Sopenharmony_ci		if (!dram_rw(pvt, range))
21298c2ecf20Sopenharmony_ci			continue;
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_ci		if (pvt->fam == 0x15 && pvt->model >= 0x30)
21328c2ecf20Sopenharmony_ci			cs_found = f15_m30h_match_to_this_node(pvt, range,
21338c2ecf20Sopenharmony_ci							       sys_addr,
21348c2ecf20Sopenharmony_ci							       chan_sel);
21358c2ecf20Sopenharmony_ci
21368c2ecf20Sopenharmony_ci		else if ((get_dram_base(pvt, range)  <= sys_addr) &&
21378c2ecf20Sopenharmony_ci			 (get_dram_limit(pvt, range) >= sys_addr)) {
21388c2ecf20Sopenharmony_ci			cs_found = f1x_match_to_this_node(pvt, range,
21398c2ecf20Sopenharmony_ci							  sys_addr, chan_sel);
21408c2ecf20Sopenharmony_ci			if (cs_found >= 0)
21418c2ecf20Sopenharmony_ci				break;
21428c2ecf20Sopenharmony_ci		}
21438c2ecf20Sopenharmony_ci	}
21448c2ecf20Sopenharmony_ci	return cs_found;
21458c2ecf20Sopenharmony_ci}
21468c2ecf20Sopenharmony_ci
21478c2ecf20Sopenharmony_ci/*
21488c2ecf20Sopenharmony_ci * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
21498c2ecf20Sopenharmony_ci * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
21508c2ecf20Sopenharmony_ci *
21518c2ecf20Sopenharmony_ci * The @sys_addr is usually an error address received from the hardware
21528c2ecf20Sopenharmony_ci * (MCX_ADDR).
21538c2ecf20Sopenharmony_ci */
21548c2ecf20Sopenharmony_cistatic void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
21558c2ecf20Sopenharmony_ci				     struct err_info *err)
21568c2ecf20Sopenharmony_ci{
21578c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt = mci->pvt_info;
21588c2ecf20Sopenharmony_ci
21598c2ecf20Sopenharmony_ci	error_address_to_page_and_offset(sys_addr, err);
21608c2ecf20Sopenharmony_ci
21618c2ecf20Sopenharmony_ci	err->csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &err->channel);
21628c2ecf20Sopenharmony_ci	if (err->csrow < 0) {
21638c2ecf20Sopenharmony_ci		err->err_code = ERR_CSROW;
21648c2ecf20Sopenharmony_ci		return;
21658c2ecf20Sopenharmony_ci	}
21668c2ecf20Sopenharmony_ci
21678c2ecf20Sopenharmony_ci	/*
21688c2ecf20Sopenharmony_ci	 * We need the syndromes for channel detection only when we're
21698c2ecf20Sopenharmony_ci	 * ganged. Otherwise @chan should already contain the channel at
21708c2ecf20Sopenharmony_ci	 * this point.
21718c2ecf20Sopenharmony_ci	 */
21728c2ecf20Sopenharmony_ci	if (dct_ganging_enabled(pvt))
21738c2ecf20Sopenharmony_ci		err->channel = get_channel_from_ecc_syndrome(mci, err->syndrome);
21748c2ecf20Sopenharmony_ci}
21758c2ecf20Sopenharmony_ci
21768c2ecf20Sopenharmony_ci/*
21778c2ecf20Sopenharmony_ci * debug routine to display the memory sizes of all logical DIMMs and its
21788c2ecf20Sopenharmony_ci * CSROWs
21798c2ecf20Sopenharmony_ci */
21808c2ecf20Sopenharmony_cistatic void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
21818c2ecf20Sopenharmony_ci{
21828c2ecf20Sopenharmony_ci	int dimm, size0, size1;
21838c2ecf20Sopenharmony_ci	u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases;
21848c2ecf20Sopenharmony_ci	u32 dbam  = ctrl ? pvt->dbam1 : pvt->dbam0;
21858c2ecf20Sopenharmony_ci
21868c2ecf20Sopenharmony_ci	if (pvt->fam == 0xf) {
21878c2ecf20Sopenharmony_ci		/* K8 families < revF not supported yet */
21888c2ecf20Sopenharmony_ci	       if (pvt->ext_model < K8_REV_F)
21898c2ecf20Sopenharmony_ci			return;
21908c2ecf20Sopenharmony_ci	       else
21918c2ecf20Sopenharmony_ci		       WARN_ON(ctrl != 0);
21928c2ecf20Sopenharmony_ci	}
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_ci	if (pvt->fam == 0x10) {
21958c2ecf20Sopenharmony_ci		dbam = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->dbam1
21968c2ecf20Sopenharmony_ci							   : pvt->dbam0;
21978c2ecf20Sopenharmony_ci		dcsb = (ctrl && !dct_ganging_enabled(pvt)) ?
21988c2ecf20Sopenharmony_ci				 pvt->csels[1].csbases :
21998c2ecf20Sopenharmony_ci				 pvt->csels[0].csbases;
22008c2ecf20Sopenharmony_ci	} else if (ctrl) {
22018c2ecf20Sopenharmony_ci		dbam = pvt->dbam0;
22028c2ecf20Sopenharmony_ci		dcsb = pvt->csels[1].csbases;
22038c2ecf20Sopenharmony_ci	}
22048c2ecf20Sopenharmony_ci	edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
22058c2ecf20Sopenharmony_ci		 ctrl, dbam);
22068c2ecf20Sopenharmony_ci
22078c2ecf20Sopenharmony_ci	edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_ci	/* Dump memory sizes for DIMM and its CSROWs */
22108c2ecf20Sopenharmony_ci	for (dimm = 0; dimm < 4; dimm++) {
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci		size0 = 0;
22138c2ecf20Sopenharmony_ci		if (dcsb[dimm*2] & DCSB_CS_ENABLE)
22148c2ecf20Sopenharmony_ci			/*
22158c2ecf20Sopenharmony_ci			 * For F15m60h, we need multiplier for LRDIMM cs_size
22168c2ecf20Sopenharmony_ci			 * calculation. We pass dimm value to the dbam_to_cs
22178c2ecf20Sopenharmony_ci			 * mapper so we can find the multiplier from the
22188c2ecf20Sopenharmony_ci			 * corresponding DCSM.
22198c2ecf20Sopenharmony_ci			 */
22208c2ecf20Sopenharmony_ci			size0 = pvt->ops->dbam_to_cs(pvt, ctrl,
22218c2ecf20Sopenharmony_ci						     DBAM_DIMM(dimm, dbam),
22228c2ecf20Sopenharmony_ci						     dimm);
22238c2ecf20Sopenharmony_ci
22248c2ecf20Sopenharmony_ci		size1 = 0;
22258c2ecf20Sopenharmony_ci		if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE)
22268c2ecf20Sopenharmony_ci			size1 = pvt->ops->dbam_to_cs(pvt, ctrl,
22278c2ecf20Sopenharmony_ci						     DBAM_DIMM(dimm, dbam),
22288c2ecf20Sopenharmony_ci						     dimm);
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_ci		amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
22318c2ecf20Sopenharmony_ci				dimm * 2,     size0,
22328c2ecf20Sopenharmony_ci				dimm * 2 + 1, size1);
22338c2ecf20Sopenharmony_ci	}
22348c2ecf20Sopenharmony_ci}
22358c2ecf20Sopenharmony_ci
22368c2ecf20Sopenharmony_cistatic struct amd64_family_type family_types[] = {
22378c2ecf20Sopenharmony_ci	[K8_CPUS] = {
22388c2ecf20Sopenharmony_ci		.ctl_name = "K8",
22398c2ecf20Sopenharmony_ci		.f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
22408c2ecf20Sopenharmony_ci		.f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
22418c2ecf20Sopenharmony_ci		.max_mcs = 2,
22428c2ecf20Sopenharmony_ci		.ops = {
22438c2ecf20Sopenharmony_ci			.early_channel_count	= k8_early_channel_count,
22448c2ecf20Sopenharmony_ci			.map_sysaddr_to_csrow	= k8_map_sysaddr_to_csrow,
22458c2ecf20Sopenharmony_ci			.dbam_to_cs		= k8_dbam_to_chip_select,
22468c2ecf20Sopenharmony_ci		}
22478c2ecf20Sopenharmony_ci	},
22488c2ecf20Sopenharmony_ci	[F10_CPUS] = {
22498c2ecf20Sopenharmony_ci		.ctl_name = "F10h",
22508c2ecf20Sopenharmony_ci		.f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP,
22518c2ecf20Sopenharmony_ci		.f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM,
22528c2ecf20Sopenharmony_ci		.max_mcs = 2,
22538c2ecf20Sopenharmony_ci		.ops = {
22548c2ecf20Sopenharmony_ci			.early_channel_count	= f1x_early_channel_count,
22558c2ecf20Sopenharmony_ci			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
22568c2ecf20Sopenharmony_ci			.dbam_to_cs		= f10_dbam_to_chip_select,
22578c2ecf20Sopenharmony_ci		}
22588c2ecf20Sopenharmony_ci	},
22598c2ecf20Sopenharmony_ci	[F15_CPUS] = {
22608c2ecf20Sopenharmony_ci		.ctl_name = "F15h",
22618c2ecf20Sopenharmony_ci		.f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1,
22628c2ecf20Sopenharmony_ci		.f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2,
22638c2ecf20Sopenharmony_ci		.max_mcs = 2,
22648c2ecf20Sopenharmony_ci		.ops = {
22658c2ecf20Sopenharmony_ci			.early_channel_count	= f1x_early_channel_count,
22668c2ecf20Sopenharmony_ci			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
22678c2ecf20Sopenharmony_ci			.dbam_to_cs		= f15_dbam_to_chip_select,
22688c2ecf20Sopenharmony_ci		}
22698c2ecf20Sopenharmony_ci	},
22708c2ecf20Sopenharmony_ci	[F15_M30H_CPUS] = {
22718c2ecf20Sopenharmony_ci		.ctl_name = "F15h_M30h",
22728c2ecf20Sopenharmony_ci		.f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1,
22738c2ecf20Sopenharmony_ci		.f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2,
22748c2ecf20Sopenharmony_ci		.max_mcs = 2,
22758c2ecf20Sopenharmony_ci		.ops = {
22768c2ecf20Sopenharmony_ci			.early_channel_count	= f1x_early_channel_count,
22778c2ecf20Sopenharmony_ci			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
22788c2ecf20Sopenharmony_ci			.dbam_to_cs		= f16_dbam_to_chip_select,
22798c2ecf20Sopenharmony_ci		}
22808c2ecf20Sopenharmony_ci	},
22818c2ecf20Sopenharmony_ci	[F15_M60H_CPUS] = {
22828c2ecf20Sopenharmony_ci		.ctl_name = "F15h_M60h",
22838c2ecf20Sopenharmony_ci		.f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1,
22848c2ecf20Sopenharmony_ci		.f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2,
22858c2ecf20Sopenharmony_ci		.max_mcs = 2,
22868c2ecf20Sopenharmony_ci		.ops = {
22878c2ecf20Sopenharmony_ci			.early_channel_count	= f1x_early_channel_count,
22888c2ecf20Sopenharmony_ci			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
22898c2ecf20Sopenharmony_ci			.dbam_to_cs		= f15_m60h_dbam_to_chip_select,
22908c2ecf20Sopenharmony_ci		}
22918c2ecf20Sopenharmony_ci	},
22928c2ecf20Sopenharmony_ci	[F16_CPUS] = {
22938c2ecf20Sopenharmony_ci		.ctl_name = "F16h",
22948c2ecf20Sopenharmony_ci		.f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1,
22958c2ecf20Sopenharmony_ci		.f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2,
22968c2ecf20Sopenharmony_ci		.max_mcs = 2,
22978c2ecf20Sopenharmony_ci		.ops = {
22988c2ecf20Sopenharmony_ci			.early_channel_count	= f1x_early_channel_count,
22998c2ecf20Sopenharmony_ci			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
23008c2ecf20Sopenharmony_ci			.dbam_to_cs		= f16_dbam_to_chip_select,
23018c2ecf20Sopenharmony_ci		}
23028c2ecf20Sopenharmony_ci	},
23038c2ecf20Sopenharmony_ci	[F16_M30H_CPUS] = {
23048c2ecf20Sopenharmony_ci		.ctl_name = "F16h_M30h",
23058c2ecf20Sopenharmony_ci		.f1_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F1,
23068c2ecf20Sopenharmony_ci		.f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2,
23078c2ecf20Sopenharmony_ci		.max_mcs = 2,
23088c2ecf20Sopenharmony_ci		.ops = {
23098c2ecf20Sopenharmony_ci			.early_channel_count	= f1x_early_channel_count,
23108c2ecf20Sopenharmony_ci			.map_sysaddr_to_csrow	= f1x_map_sysaddr_to_csrow,
23118c2ecf20Sopenharmony_ci			.dbam_to_cs		= f16_dbam_to_chip_select,
23128c2ecf20Sopenharmony_ci		}
23138c2ecf20Sopenharmony_ci	},
23148c2ecf20Sopenharmony_ci	[F17_CPUS] = {
23158c2ecf20Sopenharmony_ci		.ctl_name = "F17h",
23168c2ecf20Sopenharmony_ci		.f0_id = PCI_DEVICE_ID_AMD_17H_DF_F0,
23178c2ecf20Sopenharmony_ci		.f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6,
23188c2ecf20Sopenharmony_ci		.max_mcs = 2,
23198c2ecf20Sopenharmony_ci		.ops = {
23208c2ecf20Sopenharmony_ci			.early_channel_count	= f17_early_channel_count,
23218c2ecf20Sopenharmony_ci			.dbam_to_cs		= f17_addr_mask_to_cs_size,
23228c2ecf20Sopenharmony_ci		}
23238c2ecf20Sopenharmony_ci	},
23248c2ecf20Sopenharmony_ci	[F17_M10H_CPUS] = {
23258c2ecf20Sopenharmony_ci		.ctl_name = "F17h_M10h",
23268c2ecf20Sopenharmony_ci		.f0_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F0,
23278c2ecf20Sopenharmony_ci		.f6_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F6,
23288c2ecf20Sopenharmony_ci		.max_mcs = 2,
23298c2ecf20Sopenharmony_ci		.ops = {
23308c2ecf20Sopenharmony_ci			.early_channel_count	= f17_early_channel_count,
23318c2ecf20Sopenharmony_ci			.dbam_to_cs		= f17_addr_mask_to_cs_size,
23328c2ecf20Sopenharmony_ci		}
23338c2ecf20Sopenharmony_ci	},
23348c2ecf20Sopenharmony_ci	[F17_M30H_CPUS] = {
23358c2ecf20Sopenharmony_ci		.ctl_name = "F17h_M30h",
23368c2ecf20Sopenharmony_ci		.f0_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F0,
23378c2ecf20Sopenharmony_ci		.f6_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F6,
23388c2ecf20Sopenharmony_ci		.max_mcs = 8,
23398c2ecf20Sopenharmony_ci		.ops = {
23408c2ecf20Sopenharmony_ci			.early_channel_count	= f17_early_channel_count,
23418c2ecf20Sopenharmony_ci			.dbam_to_cs		= f17_addr_mask_to_cs_size,
23428c2ecf20Sopenharmony_ci		}
23438c2ecf20Sopenharmony_ci	},
23448c2ecf20Sopenharmony_ci	[F17_M60H_CPUS] = {
23458c2ecf20Sopenharmony_ci		.ctl_name = "F17h_M60h",
23468c2ecf20Sopenharmony_ci		.f0_id = PCI_DEVICE_ID_AMD_17H_M60H_DF_F0,
23478c2ecf20Sopenharmony_ci		.f6_id = PCI_DEVICE_ID_AMD_17H_M60H_DF_F6,
23488c2ecf20Sopenharmony_ci		.max_mcs = 2,
23498c2ecf20Sopenharmony_ci		.ops = {
23508c2ecf20Sopenharmony_ci			.early_channel_count	= f17_early_channel_count,
23518c2ecf20Sopenharmony_ci			.dbam_to_cs		= f17_addr_mask_to_cs_size,
23528c2ecf20Sopenharmony_ci		}
23538c2ecf20Sopenharmony_ci	},
23548c2ecf20Sopenharmony_ci	[F17_M70H_CPUS] = {
23558c2ecf20Sopenharmony_ci		.ctl_name = "F17h_M70h",
23568c2ecf20Sopenharmony_ci		.f0_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F0,
23578c2ecf20Sopenharmony_ci		.f6_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F6,
23588c2ecf20Sopenharmony_ci		.max_mcs = 2,
23598c2ecf20Sopenharmony_ci		.ops = {
23608c2ecf20Sopenharmony_ci			.early_channel_count	= f17_early_channel_count,
23618c2ecf20Sopenharmony_ci			.dbam_to_cs		= f17_addr_mask_to_cs_size,
23628c2ecf20Sopenharmony_ci		}
23638c2ecf20Sopenharmony_ci	},
23648c2ecf20Sopenharmony_ci	[F19_CPUS] = {
23658c2ecf20Sopenharmony_ci		.ctl_name = "F19h",
23668c2ecf20Sopenharmony_ci		.f0_id = PCI_DEVICE_ID_AMD_19H_DF_F0,
23678c2ecf20Sopenharmony_ci		.f6_id = PCI_DEVICE_ID_AMD_19H_DF_F6,
23688c2ecf20Sopenharmony_ci		.max_mcs = 8,
23698c2ecf20Sopenharmony_ci		.ops = {
23708c2ecf20Sopenharmony_ci			.early_channel_count	= f17_early_channel_count,
23718c2ecf20Sopenharmony_ci			.dbam_to_cs		= f17_addr_mask_to_cs_size,
23728c2ecf20Sopenharmony_ci		}
23738c2ecf20Sopenharmony_ci	},
23748c2ecf20Sopenharmony_ci};
23758c2ecf20Sopenharmony_ci
23768c2ecf20Sopenharmony_ci/*
23778c2ecf20Sopenharmony_ci * These are tables of eigenvectors (one per line) which can be used for the
23788c2ecf20Sopenharmony_ci * construction of the syndrome tables. The modified syndrome search algorithm
23798c2ecf20Sopenharmony_ci * uses those to find the symbol in error and thus the DIMM.
23808c2ecf20Sopenharmony_ci *
23818c2ecf20Sopenharmony_ci * Algorithm courtesy of Ross LaFetra from AMD.
23828c2ecf20Sopenharmony_ci */
23838c2ecf20Sopenharmony_cistatic const u16 x4_vectors[] = {
23848c2ecf20Sopenharmony_ci	0x2f57, 0x1afe, 0x66cc, 0xdd88,
23858c2ecf20Sopenharmony_ci	0x11eb, 0x3396, 0x7f4c, 0xeac8,
23868c2ecf20Sopenharmony_ci	0x0001, 0x0002, 0x0004, 0x0008,
23878c2ecf20Sopenharmony_ci	0x1013, 0x3032, 0x4044, 0x8088,
23888c2ecf20Sopenharmony_ci	0x106b, 0x30d6, 0x70fc, 0xe0a8,
23898c2ecf20Sopenharmony_ci	0x4857, 0xc4fe, 0x13cc, 0x3288,
23908c2ecf20Sopenharmony_ci	0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
23918c2ecf20Sopenharmony_ci	0x1f39, 0x251e, 0xbd6c, 0x6bd8,
23928c2ecf20Sopenharmony_ci	0x15c1, 0x2a42, 0x89ac, 0x4758,
23938c2ecf20Sopenharmony_ci	0x2b03, 0x1602, 0x4f0c, 0xca08,
23948c2ecf20Sopenharmony_ci	0x1f07, 0x3a0e, 0x6b04, 0xbd08,
23958c2ecf20Sopenharmony_ci	0x8ba7, 0x465e, 0x244c, 0x1cc8,
23968c2ecf20Sopenharmony_ci	0x2b87, 0x164e, 0x642c, 0xdc18,
23978c2ecf20Sopenharmony_ci	0x40b9, 0x80de, 0x1094, 0x20e8,
23988c2ecf20Sopenharmony_ci	0x27db, 0x1eb6, 0x9dac, 0x7b58,
23998c2ecf20Sopenharmony_ci	0x11c1, 0x2242, 0x84ac, 0x4c58,
24008c2ecf20Sopenharmony_ci	0x1be5, 0x2d7a, 0x5e34, 0xa718,
24018c2ecf20Sopenharmony_ci	0x4b39, 0x8d1e, 0x14b4, 0x28d8,
24028c2ecf20Sopenharmony_ci	0x4c97, 0xc87e, 0x11fc, 0x33a8,
24038c2ecf20Sopenharmony_ci	0x8e97, 0x497e, 0x2ffc, 0x1aa8,
24048c2ecf20Sopenharmony_ci	0x16b3, 0x3d62, 0x4f34, 0x8518,
24058c2ecf20Sopenharmony_ci	0x1e2f, 0x391a, 0x5cac, 0xf858,
24068c2ecf20Sopenharmony_ci	0x1d9f, 0x3b7a, 0x572c, 0xfe18,
24078c2ecf20Sopenharmony_ci	0x15f5, 0x2a5a, 0x5264, 0xa3b8,
24088c2ecf20Sopenharmony_ci	0x1dbb, 0x3b66, 0x715c, 0xe3f8,
24098c2ecf20Sopenharmony_ci	0x4397, 0xc27e, 0x17fc, 0x3ea8,
24108c2ecf20Sopenharmony_ci	0x1617, 0x3d3e, 0x6464, 0xb8b8,
24118c2ecf20Sopenharmony_ci	0x23ff, 0x12aa, 0xab6c, 0x56d8,
24128c2ecf20Sopenharmony_ci	0x2dfb, 0x1ba6, 0x913c, 0x7328,
24138c2ecf20Sopenharmony_ci	0x185d, 0x2ca6, 0x7914, 0x9e28,
24148c2ecf20Sopenharmony_ci	0x171b, 0x3e36, 0x7d7c, 0xebe8,
24158c2ecf20Sopenharmony_ci	0x4199, 0x82ee, 0x19f4, 0x2e58,
24168c2ecf20Sopenharmony_ci	0x4807, 0xc40e, 0x130c, 0x3208,
24178c2ecf20Sopenharmony_ci	0x1905, 0x2e0a, 0x5804, 0xac08,
24188c2ecf20Sopenharmony_ci	0x213f, 0x132a, 0xadfc, 0x5ba8,
24198c2ecf20Sopenharmony_ci	0x19a9, 0x2efe, 0xb5cc, 0x6f88,
24208c2ecf20Sopenharmony_ci};
24218c2ecf20Sopenharmony_ci
24228c2ecf20Sopenharmony_cistatic const u16 x8_vectors[] = {
24238c2ecf20Sopenharmony_ci	0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
24248c2ecf20Sopenharmony_ci	0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
24258c2ecf20Sopenharmony_ci	0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
24268c2ecf20Sopenharmony_ci	0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
24278c2ecf20Sopenharmony_ci	0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
24288c2ecf20Sopenharmony_ci	0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
24298c2ecf20Sopenharmony_ci	0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
24308c2ecf20Sopenharmony_ci	0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
24318c2ecf20Sopenharmony_ci	0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
24328c2ecf20Sopenharmony_ci	0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
24338c2ecf20Sopenharmony_ci	0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
24348c2ecf20Sopenharmony_ci	0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
24358c2ecf20Sopenharmony_ci	0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
24368c2ecf20Sopenharmony_ci	0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
24378c2ecf20Sopenharmony_ci	0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
24388c2ecf20Sopenharmony_ci	0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
24398c2ecf20Sopenharmony_ci	0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
24408c2ecf20Sopenharmony_ci	0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
24418c2ecf20Sopenharmony_ci	0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
24428c2ecf20Sopenharmony_ci};
24438c2ecf20Sopenharmony_ci
24448c2ecf20Sopenharmony_cistatic int decode_syndrome(u16 syndrome, const u16 *vectors, unsigned num_vecs,
24458c2ecf20Sopenharmony_ci			   unsigned v_dim)
24468c2ecf20Sopenharmony_ci{
24478c2ecf20Sopenharmony_ci	unsigned int i, err_sym;
24488c2ecf20Sopenharmony_ci
24498c2ecf20Sopenharmony_ci	for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
24508c2ecf20Sopenharmony_ci		u16 s = syndrome;
24518c2ecf20Sopenharmony_ci		unsigned v_idx =  err_sym * v_dim;
24528c2ecf20Sopenharmony_ci		unsigned v_end = (err_sym + 1) * v_dim;
24538c2ecf20Sopenharmony_ci
24548c2ecf20Sopenharmony_ci		/* walk over all 16 bits of the syndrome */
24558c2ecf20Sopenharmony_ci		for (i = 1; i < (1U << 16); i <<= 1) {
24568c2ecf20Sopenharmony_ci
24578c2ecf20Sopenharmony_ci			/* if bit is set in that eigenvector... */
24588c2ecf20Sopenharmony_ci			if (v_idx < v_end && vectors[v_idx] & i) {
24598c2ecf20Sopenharmony_ci				u16 ev_comp = vectors[v_idx++];
24608c2ecf20Sopenharmony_ci
24618c2ecf20Sopenharmony_ci				/* ... and bit set in the modified syndrome, */
24628c2ecf20Sopenharmony_ci				if (s & i) {
24638c2ecf20Sopenharmony_ci					/* remove it. */
24648c2ecf20Sopenharmony_ci					s ^= ev_comp;
24658c2ecf20Sopenharmony_ci
24668c2ecf20Sopenharmony_ci					if (!s)
24678c2ecf20Sopenharmony_ci						return err_sym;
24688c2ecf20Sopenharmony_ci				}
24698c2ecf20Sopenharmony_ci
24708c2ecf20Sopenharmony_ci			} else if (s & i)
24718c2ecf20Sopenharmony_ci				/* can't get to zero, move to next symbol */
24728c2ecf20Sopenharmony_ci				break;
24738c2ecf20Sopenharmony_ci		}
24748c2ecf20Sopenharmony_ci	}
24758c2ecf20Sopenharmony_ci
24768c2ecf20Sopenharmony_ci	edac_dbg(0, "syndrome(%x) not found\n", syndrome);
24778c2ecf20Sopenharmony_ci	return -1;
24788c2ecf20Sopenharmony_ci}
24798c2ecf20Sopenharmony_ci
24808c2ecf20Sopenharmony_cistatic int map_err_sym_to_channel(int err_sym, int sym_size)
24818c2ecf20Sopenharmony_ci{
24828c2ecf20Sopenharmony_ci	if (sym_size == 4)
24838c2ecf20Sopenharmony_ci		switch (err_sym) {
24848c2ecf20Sopenharmony_ci		case 0x20:
24858c2ecf20Sopenharmony_ci		case 0x21:
24868c2ecf20Sopenharmony_ci			return 0;
24878c2ecf20Sopenharmony_ci			break;
24888c2ecf20Sopenharmony_ci		case 0x22:
24898c2ecf20Sopenharmony_ci		case 0x23:
24908c2ecf20Sopenharmony_ci			return 1;
24918c2ecf20Sopenharmony_ci			break;
24928c2ecf20Sopenharmony_ci		default:
24938c2ecf20Sopenharmony_ci			return err_sym >> 4;
24948c2ecf20Sopenharmony_ci			break;
24958c2ecf20Sopenharmony_ci		}
24968c2ecf20Sopenharmony_ci	/* x8 symbols */
24978c2ecf20Sopenharmony_ci	else
24988c2ecf20Sopenharmony_ci		switch (err_sym) {
24998c2ecf20Sopenharmony_ci		/* imaginary bits not in a DIMM */
25008c2ecf20Sopenharmony_ci		case 0x10:
25018c2ecf20Sopenharmony_ci			WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
25028c2ecf20Sopenharmony_ci					  err_sym);
25038c2ecf20Sopenharmony_ci			return -1;
25048c2ecf20Sopenharmony_ci			break;
25058c2ecf20Sopenharmony_ci
25068c2ecf20Sopenharmony_ci		case 0x11:
25078c2ecf20Sopenharmony_ci			return 0;
25088c2ecf20Sopenharmony_ci			break;
25098c2ecf20Sopenharmony_ci		case 0x12:
25108c2ecf20Sopenharmony_ci			return 1;
25118c2ecf20Sopenharmony_ci			break;
25128c2ecf20Sopenharmony_ci		default:
25138c2ecf20Sopenharmony_ci			return err_sym >> 3;
25148c2ecf20Sopenharmony_ci			break;
25158c2ecf20Sopenharmony_ci		}
25168c2ecf20Sopenharmony_ci	return -1;
25178c2ecf20Sopenharmony_ci}
25188c2ecf20Sopenharmony_ci
25198c2ecf20Sopenharmony_cistatic int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
25208c2ecf20Sopenharmony_ci{
25218c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt = mci->pvt_info;
25228c2ecf20Sopenharmony_ci	int err_sym = -1;
25238c2ecf20Sopenharmony_ci
25248c2ecf20Sopenharmony_ci	if (pvt->ecc_sym_sz == 8)
25258c2ecf20Sopenharmony_ci		err_sym = decode_syndrome(syndrome, x8_vectors,
25268c2ecf20Sopenharmony_ci					  ARRAY_SIZE(x8_vectors),
25278c2ecf20Sopenharmony_ci					  pvt->ecc_sym_sz);
25288c2ecf20Sopenharmony_ci	else if (pvt->ecc_sym_sz == 4)
25298c2ecf20Sopenharmony_ci		err_sym = decode_syndrome(syndrome, x4_vectors,
25308c2ecf20Sopenharmony_ci					  ARRAY_SIZE(x4_vectors),
25318c2ecf20Sopenharmony_ci					  pvt->ecc_sym_sz);
25328c2ecf20Sopenharmony_ci	else {
25338c2ecf20Sopenharmony_ci		amd64_warn("Illegal syndrome type: %u\n", pvt->ecc_sym_sz);
25348c2ecf20Sopenharmony_ci		return err_sym;
25358c2ecf20Sopenharmony_ci	}
25368c2ecf20Sopenharmony_ci
25378c2ecf20Sopenharmony_ci	return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz);
25388c2ecf20Sopenharmony_ci}
25398c2ecf20Sopenharmony_ci
25408c2ecf20Sopenharmony_cistatic void __log_ecc_error(struct mem_ctl_info *mci, struct err_info *err,
25418c2ecf20Sopenharmony_ci			    u8 ecc_type)
25428c2ecf20Sopenharmony_ci{
25438c2ecf20Sopenharmony_ci	enum hw_event_mc_err_type err_type;
25448c2ecf20Sopenharmony_ci	const char *string;
25458c2ecf20Sopenharmony_ci
25468c2ecf20Sopenharmony_ci	if (ecc_type == 2)
25478c2ecf20Sopenharmony_ci		err_type = HW_EVENT_ERR_CORRECTED;
25488c2ecf20Sopenharmony_ci	else if (ecc_type == 1)
25498c2ecf20Sopenharmony_ci		err_type = HW_EVENT_ERR_UNCORRECTED;
25508c2ecf20Sopenharmony_ci	else if (ecc_type == 3)
25518c2ecf20Sopenharmony_ci		err_type = HW_EVENT_ERR_DEFERRED;
25528c2ecf20Sopenharmony_ci	else {
25538c2ecf20Sopenharmony_ci		WARN(1, "Something is rotten in the state of Denmark.\n");
25548c2ecf20Sopenharmony_ci		return;
25558c2ecf20Sopenharmony_ci	}
25568c2ecf20Sopenharmony_ci
25578c2ecf20Sopenharmony_ci	switch (err->err_code) {
25588c2ecf20Sopenharmony_ci	case DECODE_OK:
25598c2ecf20Sopenharmony_ci		string = "";
25608c2ecf20Sopenharmony_ci		break;
25618c2ecf20Sopenharmony_ci	case ERR_NODE:
25628c2ecf20Sopenharmony_ci		string = "Failed to map error addr to a node";
25638c2ecf20Sopenharmony_ci		break;
25648c2ecf20Sopenharmony_ci	case ERR_CSROW:
25658c2ecf20Sopenharmony_ci		string = "Failed to map error addr to a csrow";
25668c2ecf20Sopenharmony_ci		break;
25678c2ecf20Sopenharmony_ci	case ERR_CHANNEL:
25688c2ecf20Sopenharmony_ci		string = "Unknown syndrome - possible error reporting race";
25698c2ecf20Sopenharmony_ci		break;
25708c2ecf20Sopenharmony_ci	case ERR_SYND:
25718c2ecf20Sopenharmony_ci		string = "MCA_SYND not valid - unknown syndrome and csrow";
25728c2ecf20Sopenharmony_ci		break;
25738c2ecf20Sopenharmony_ci	case ERR_NORM_ADDR:
25748c2ecf20Sopenharmony_ci		string = "Cannot decode normalized address";
25758c2ecf20Sopenharmony_ci		break;
25768c2ecf20Sopenharmony_ci	default:
25778c2ecf20Sopenharmony_ci		string = "WTF error";
25788c2ecf20Sopenharmony_ci		break;
25798c2ecf20Sopenharmony_ci	}
25808c2ecf20Sopenharmony_ci
25818c2ecf20Sopenharmony_ci	edac_mc_handle_error(err_type, mci, 1,
25828c2ecf20Sopenharmony_ci			     err->page, err->offset, err->syndrome,
25838c2ecf20Sopenharmony_ci			     err->csrow, err->channel, -1,
25848c2ecf20Sopenharmony_ci			     string, "");
25858c2ecf20Sopenharmony_ci}
25868c2ecf20Sopenharmony_ci
25878c2ecf20Sopenharmony_cistatic inline void decode_bus_error(int node_id, struct mce *m)
25888c2ecf20Sopenharmony_ci{
25898c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci;
25908c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt;
25918c2ecf20Sopenharmony_ci	u8 ecc_type = (m->status >> 45) & 0x3;
25928c2ecf20Sopenharmony_ci	u8 xec = XEC(m->status, 0x1f);
25938c2ecf20Sopenharmony_ci	u16 ec = EC(m->status);
25948c2ecf20Sopenharmony_ci	u64 sys_addr;
25958c2ecf20Sopenharmony_ci	struct err_info err;
25968c2ecf20Sopenharmony_ci
25978c2ecf20Sopenharmony_ci	mci = edac_mc_find(node_id);
25988c2ecf20Sopenharmony_ci	if (!mci)
25998c2ecf20Sopenharmony_ci		return;
26008c2ecf20Sopenharmony_ci
26018c2ecf20Sopenharmony_ci	pvt = mci->pvt_info;
26028c2ecf20Sopenharmony_ci
26038c2ecf20Sopenharmony_ci	/* Bail out early if this was an 'observed' error */
26048c2ecf20Sopenharmony_ci	if (PP(ec) == NBSL_PP_OBS)
26058c2ecf20Sopenharmony_ci		return;
26068c2ecf20Sopenharmony_ci
26078c2ecf20Sopenharmony_ci	/* Do only ECC errors */
26088c2ecf20Sopenharmony_ci	if (xec && xec != F10_NBSL_EXT_ERR_ECC)
26098c2ecf20Sopenharmony_ci		return;
26108c2ecf20Sopenharmony_ci
26118c2ecf20Sopenharmony_ci	memset(&err, 0, sizeof(err));
26128c2ecf20Sopenharmony_ci
26138c2ecf20Sopenharmony_ci	sys_addr = get_error_address(pvt, m);
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_ci	if (ecc_type == 2)
26168c2ecf20Sopenharmony_ci		err.syndrome = extract_syndrome(m->status);
26178c2ecf20Sopenharmony_ci
26188c2ecf20Sopenharmony_ci	pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err);
26198c2ecf20Sopenharmony_ci
26208c2ecf20Sopenharmony_ci	__log_ecc_error(mci, &err, ecc_type);
26218c2ecf20Sopenharmony_ci}
26228c2ecf20Sopenharmony_ci
26238c2ecf20Sopenharmony_ci/*
26248c2ecf20Sopenharmony_ci * To find the UMC channel represented by this bank we need to match on its
26258c2ecf20Sopenharmony_ci * instance_id. The instance_id of a bank is held in the lower 32 bits of its
26268c2ecf20Sopenharmony_ci * IPID.
26278c2ecf20Sopenharmony_ci *
26288c2ecf20Sopenharmony_ci * Currently, we can derive the channel number by looking at the 6th nibble in
26298c2ecf20Sopenharmony_ci * the instance_id. For example, instance_id=0xYXXXXX where Y is the channel
26308c2ecf20Sopenharmony_ci * number.
26318c2ecf20Sopenharmony_ci */
26328c2ecf20Sopenharmony_cistatic int find_umc_channel(struct mce *m)
26338c2ecf20Sopenharmony_ci{
26348c2ecf20Sopenharmony_ci	return (m->ipid & GENMASK(31, 0)) >> 20;
26358c2ecf20Sopenharmony_ci}
26368c2ecf20Sopenharmony_ci
26378c2ecf20Sopenharmony_cistatic void decode_umc_error(int node_id, struct mce *m)
26388c2ecf20Sopenharmony_ci{
26398c2ecf20Sopenharmony_ci	u8 ecc_type = (m->status >> 45) & 0x3;
26408c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci;
26418c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt;
26428c2ecf20Sopenharmony_ci	struct err_info err;
26438c2ecf20Sopenharmony_ci	u64 sys_addr;
26448c2ecf20Sopenharmony_ci
26458c2ecf20Sopenharmony_ci	mci = edac_mc_find(node_id);
26468c2ecf20Sopenharmony_ci	if (!mci)
26478c2ecf20Sopenharmony_ci		return;
26488c2ecf20Sopenharmony_ci
26498c2ecf20Sopenharmony_ci	pvt = mci->pvt_info;
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_ci	memset(&err, 0, sizeof(err));
26528c2ecf20Sopenharmony_ci
26538c2ecf20Sopenharmony_ci	if (m->status & MCI_STATUS_DEFERRED)
26548c2ecf20Sopenharmony_ci		ecc_type = 3;
26558c2ecf20Sopenharmony_ci
26568c2ecf20Sopenharmony_ci	err.channel = find_umc_channel(m);
26578c2ecf20Sopenharmony_ci
26588c2ecf20Sopenharmony_ci	if (!(m->status & MCI_STATUS_SYNDV)) {
26598c2ecf20Sopenharmony_ci		err.err_code = ERR_SYND;
26608c2ecf20Sopenharmony_ci		goto log_error;
26618c2ecf20Sopenharmony_ci	}
26628c2ecf20Sopenharmony_ci
26638c2ecf20Sopenharmony_ci	if (ecc_type == 2) {
26648c2ecf20Sopenharmony_ci		u8 length = (m->synd >> 18) & 0x3f;
26658c2ecf20Sopenharmony_ci
26668c2ecf20Sopenharmony_ci		if (length)
26678c2ecf20Sopenharmony_ci			err.syndrome = (m->synd >> 32) & GENMASK(length - 1, 0);
26688c2ecf20Sopenharmony_ci		else
26698c2ecf20Sopenharmony_ci			err.err_code = ERR_CHANNEL;
26708c2ecf20Sopenharmony_ci	}
26718c2ecf20Sopenharmony_ci
26728c2ecf20Sopenharmony_ci	err.csrow = m->synd & 0x7;
26738c2ecf20Sopenharmony_ci
26748c2ecf20Sopenharmony_ci	if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) {
26758c2ecf20Sopenharmony_ci		err.err_code = ERR_NORM_ADDR;
26768c2ecf20Sopenharmony_ci		goto log_error;
26778c2ecf20Sopenharmony_ci	}
26788c2ecf20Sopenharmony_ci
26798c2ecf20Sopenharmony_ci	error_address_to_page_and_offset(sys_addr, &err);
26808c2ecf20Sopenharmony_ci
26818c2ecf20Sopenharmony_cilog_error:
26828c2ecf20Sopenharmony_ci	__log_ecc_error(mci, &err, ecc_type);
26838c2ecf20Sopenharmony_ci}
26848c2ecf20Sopenharmony_ci
26858c2ecf20Sopenharmony_ci/*
26868c2ecf20Sopenharmony_ci * Use pvt->F3 which contains the F3 CPU PCI device to get the related
26878c2ecf20Sopenharmony_ci * F1 (AddrMap) and F2 (Dct) devices. Return negative value on error.
26888c2ecf20Sopenharmony_ci * Reserve F0 and F6 on systems with a UMC.
26898c2ecf20Sopenharmony_ci */
26908c2ecf20Sopenharmony_cistatic int
26918c2ecf20Sopenharmony_cireserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2)
26928c2ecf20Sopenharmony_ci{
26938c2ecf20Sopenharmony_ci	if (pvt->umc) {
26948c2ecf20Sopenharmony_ci		pvt->F0 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
26958c2ecf20Sopenharmony_ci		if (!pvt->F0) {
26968c2ecf20Sopenharmony_ci			amd64_err("F0 not found, device 0x%x (broken BIOS?)\n", pci_id1);
26978c2ecf20Sopenharmony_ci			return -ENODEV;
26988c2ecf20Sopenharmony_ci		}
26998c2ecf20Sopenharmony_ci
27008c2ecf20Sopenharmony_ci		pvt->F6 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3);
27018c2ecf20Sopenharmony_ci		if (!pvt->F6) {
27028c2ecf20Sopenharmony_ci			pci_dev_put(pvt->F0);
27038c2ecf20Sopenharmony_ci			pvt->F0 = NULL;
27048c2ecf20Sopenharmony_ci
27058c2ecf20Sopenharmony_ci			amd64_err("F6 not found: device 0x%x (broken BIOS?)\n", pci_id2);
27068c2ecf20Sopenharmony_ci			return -ENODEV;
27078c2ecf20Sopenharmony_ci		}
27088c2ecf20Sopenharmony_ci
27098c2ecf20Sopenharmony_ci		if (!pci_ctl_dev)
27108c2ecf20Sopenharmony_ci			pci_ctl_dev = &pvt->F0->dev;
27118c2ecf20Sopenharmony_ci
27128c2ecf20Sopenharmony_ci		edac_dbg(1, "F0: %s\n", pci_name(pvt->F0));
27138c2ecf20Sopenharmony_ci		edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
27148c2ecf20Sopenharmony_ci		edac_dbg(1, "F6: %s\n", pci_name(pvt->F6));
27158c2ecf20Sopenharmony_ci
27168c2ecf20Sopenharmony_ci		return 0;
27178c2ecf20Sopenharmony_ci	}
27188c2ecf20Sopenharmony_ci
27198c2ecf20Sopenharmony_ci	/* Reserve the ADDRESS MAP Device */
27208c2ecf20Sopenharmony_ci	pvt->F1 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
27218c2ecf20Sopenharmony_ci	if (!pvt->F1) {
27228c2ecf20Sopenharmony_ci		amd64_err("F1 not found: device 0x%x (broken BIOS?)\n", pci_id1);
27238c2ecf20Sopenharmony_ci		return -ENODEV;
27248c2ecf20Sopenharmony_ci	}
27258c2ecf20Sopenharmony_ci
27268c2ecf20Sopenharmony_ci	/* Reserve the DCT Device */
27278c2ecf20Sopenharmony_ci	pvt->F2 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3);
27288c2ecf20Sopenharmony_ci	if (!pvt->F2) {
27298c2ecf20Sopenharmony_ci		pci_dev_put(pvt->F1);
27308c2ecf20Sopenharmony_ci		pvt->F1 = NULL;
27318c2ecf20Sopenharmony_ci
27328c2ecf20Sopenharmony_ci		amd64_err("F2 not found: device 0x%x (broken BIOS?)\n", pci_id2);
27338c2ecf20Sopenharmony_ci		return -ENODEV;
27348c2ecf20Sopenharmony_ci	}
27358c2ecf20Sopenharmony_ci
27368c2ecf20Sopenharmony_ci	if (!pci_ctl_dev)
27378c2ecf20Sopenharmony_ci		pci_ctl_dev = &pvt->F2->dev;
27388c2ecf20Sopenharmony_ci
27398c2ecf20Sopenharmony_ci	edac_dbg(1, "F1: %s\n", pci_name(pvt->F1));
27408c2ecf20Sopenharmony_ci	edac_dbg(1, "F2: %s\n", pci_name(pvt->F2));
27418c2ecf20Sopenharmony_ci	edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
27428c2ecf20Sopenharmony_ci
27438c2ecf20Sopenharmony_ci	return 0;
27448c2ecf20Sopenharmony_ci}
27458c2ecf20Sopenharmony_ci
27468c2ecf20Sopenharmony_cistatic void free_mc_sibling_devs(struct amd64_pvt *pvt)
27478c2ecf20Sopenharmony_ci{
27488c2ecf20Sopenharmony_ci	if (pvt->umc) {
27498c2ecf20Sopenharmony_ci		pci_dev_put(pvt->F0);
27508c2ecf20Sopenharmony_ci		pci_dev_put(pvt->F6);
27518c2ecf20Sopenharmony_ci	} else {
27528c2ecf20Sopenharmony_ci		pci_dev_put(pvt->F1);
27538c2ecf20Sopenharmony_ci		pci_dev_put(pvt->F2);
27548c2ecf20Sopenharmony_ci	}
27558c2ecf20Sopenharmony_ci}
27568c2ecf20Sopenharmony_ci
27578c2ecf20Sopenharmony_cistatic void determine_ecc_sym_sz(struct amd64_pvt *pvt)
27588c2ecf20Sopenharmony_ci{
27598c2ecf20Sopenharmony_ci	pvt->ecc_sym_sz = 4;
27608c2ecf20Sopenharmony_ci
27618c2ecf20Sopenharmony_ci	if (pvt->umc) {
27628c2ecf20Sopenharmony_ci		u8 i;
27638c2ecf20Sopenharmony_ci
27648c2ecf20Sopenharmony_ci		for_each_umc(i) {
27658c2ecf20Sopenharmony_ci			/* Check enabled channels only: */
27668c2ecf20Sopenharmony_ci			if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) {
27678c2ecf20Sopenharmony_ci				if (pvt->umc[i].ecc_ctrl & BIT(9)) {
27688c2ecf20Sopenharmony_ci					pvt->ecc_sym_sz = 16;
27698c2ecf20Sopenharmony_ci					return;
27708c2ecf20Sopenharmony_ci				} else if (pvt->umc[i].ecc_ctrl & BIT(7)) {
27718c2ecf20Sopenharmony_ci					pvt->ecc_sym_sz = 8;
27728c2ecf20Sopenharmony_ci					return;
27738c2ecf20Sopenharmony_ci				}
27748c2ecf20Sopenharmony_ci			}
27758c2ecf20Sopenharmony_ci		}
27768c2ecf20Sopenharmony_ci	} else if (pvt->fam >= 0x10) {
27778c2ecf20Sopenharmony_ci		u32 tmp;
27788c2ecf20Sopenharmony_ci
27798c2ecf20Sopenharmony_ci		amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp);
27808c2ecf20Sopenharmony_ci		/* F16h has only DCT0, so no need to read dbam1. */
27818c2ecf20Sopenharmony_ci		if (pvt->fam != 0x16)
27828c2ecf20Sopenharmony_ci			amd64_read_dct_pci_cfg(pvt, 1, DBAM0, &pvt->dbam1);
27838c2ecf20Sopenharmony_ci
27848c2ecf20Sopenharmony_ci		/* F10h, revD and later can do x8 ECC too. */
27858c2ecf20Sopenharmony_ci		if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25))
27868c2ecf20Sopenharmony_ci			pvt->ecc_sym_sz = 8;
27878c2ecf20Sopenharmony_ci	}
27888c2ecf20Sopenharmony_ci}
27898c2ecf20Sopenharmony_ci
27908c2ecf20Sopenharmony_ci/*
27918c2ecf20Sopenharmony_ci * Retrieve the hardware registers of the memory controller.
27928c2ecf20Sopenharmony_ci */
27938c2ecf20Sopenharmony_cistatic void __read_mc_regs_df(struct amd64_pvt *pvt)
27948c2ecf20Sopenharmony_ci{
27958c2ecf20Sopenharmony_ci	u8 nid = pvt->mc_node_id;
27968c2ecf20Sopenharmony_ci	struct amd64_umc *umc;
27978c2ecf20Sopenharmony_ci	u32 i, umc_base;
27988c2ecf20Sopenharmony_ci
27998c2ecf20Sopenharmony_ci	/* Read registers from each UMC */
28008c2ecf20Sopenharmony_ci	for_each_umc(i) {
28018c2ecf20Sopenharmony_ci
28028c2ecf20Sopenharmony_ci		umc_base = get_umc_base(i);
28038c2ecf20Sopenharmony_ci		umc = &pvt->umc[i];
28048c2ecf20Sopenharmony_ci
28058c2ecf20Sopenharmony_ci		amd_smn_read(nid, umc_base + UMCCH_DIMM_CFG, &umc->dimm_cfg);
28068c2ecf20Sopenharmony_ci		amd_smn_read(nid, umc_base + UMCCH_UMC_CFG, &umc->umc_cfg);
28078c2ecf20Sopenharmony_ci		amd_smn_read(nid, umc_base + UMCCH_SDP_CTRL, &umc->sdp_ctrl);
28088c2ecf20Sopenharmony_ci		amd_smn_read(nid, umc_base + UMCCH_ECC_CTRL, &umc->ecc_ctrl);
28098c2ecf20Sopenharmony_ci		amd_smn_read(nid, umc_base + UMCCH_UMC_CAP_HI, &umc->umc_cap_hi);
28108c2ecf20Sopenharmony_ci	}
28118c2ecf20Sopenharmony_ci}
28128c2ecf20Sopenharmony_ci
28138c2ecf20Sopenharmony_ci/*
28148c2ecf20Sopenharmony_ci * Retrieve the hardware registers of the memory controller (this includes the
28158c2ecf20Sopenharmony_ci * 'Address Map' and 'Misc' device regs)
28168c2ecf20Sopenharmony_ci */
28178c2ecf20Sopenharmony_cistatic void read_mc_regs(struct amd64_pvt *pvt)
28188c2ecf20Sopenharmony_ci{
28198c2ecf20Sopenharmony_ci	unsigned int range;
28208c2ecf20Sopenharmony_ci	u64 msr_val;
28218c2ecf20Sopenharmony_ci
28228c2ecf20Sopenharmony_ci	/*
28238c2ecf20Sopenharmony_ci	 * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
28248c2ecf20Sopenharmony_ci	 * those are Read-As-Zero.
28258c2ecf20Sopenharmony_ci	 */
28268c2ecf20Sopenharmony_ci	rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
28278c2ecf20Sopenharmony_ci	edac_dbg(0, "  TOP_MEM:  0x%016llx\n", pvt->top_mem);
28288c2ecf20Sopenharmony_ci
28298c2ecf20Sopenharmony_ci	/* Check first whether TOP_MEM2 is enabled: */
28308c2ecf20Sopenharmony_ci	rdmsrl(MSR_K8_SYSCFG, msr_val);
28318c2ecf20Sopenharmony_ci	if (msr_val & BIT(21)) {
28328c2ecf20Sopenharmony_ci		rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
28338c2ecf20Sopenharmony_ci		edac_dbg(0, "  TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
28348c2ecf20Sopenharmony_ci	} else {
28358c2ecf20Sopenharmony_ci		edac_dbg(0, "  TOP_MEM2 disabled\n");
28368c2ecf20Sopenharmony_ci	}
28378c2ecf20Sopenharmony_ci
28388c2ecf20Sopenharmony_ci	if (pvt->umc) {
28398c2ecf20Sopenharmony_ci		__read_mc_regs_df(pvt);
28408c2ecf20Sopenharmony_ci		amd64_read_pci_cfg(pvt->F0, DF_DHAR, &pvt->dhar);
28418c2ecf20Sopenharmony_ci
28428c2ecf20Sopenharmony_ci		goto skip;
28438c2ecf20Sopenharmony_ci	}
28448c2ecf20Sopenharmony_ci
28458c2ecf20Sopenharmony_ci	amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap);
28468c2ecf20Sopenharmony_ci
28478c2ecf20Sopenharmony_ci	read_dram_ctl_register(pvt);
28488c2ecf20Sopenharmony_ci
28498c2ecf20Sopenharmony_ci	for (range = 0; range < DRAM_RANGES; range++) {
28508c2ecf20Sopenharmony_ci		u8 rw;
28518c2ecf20Sopenharmony_ci
28528c2ecf20Sopenharmony_ci		/* read settings for this DRAM range */
28538c2ecf20Sopenharmony_ci		read_dram_base_limit_regs(pvt, range);
28548c2ecf20Sopenharmony_ci
28558c2ecf20Sopenharmony_ci		rw = dram_rw(pvt, range);
28568c2ecf20Sopenharmony_ci		if (!rw)
28578c2ecf20Sopenharmony_ci			continue;
28588c2ecf20Sopenharmony_ci
28598c2ecf20Sopenharmony_ci		edac_dbg(1, "  DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
28608c2ecf20Sopenharmony_ci			 range,
28618c2ecf20Sopenharmony_ci			 get_dram_base(pvt, range),
28628c2ecf20Sopenharmony_ci			 get_dram_limit(pvt, range));
28638c2ecf20Sopenharmony_ci
28648c2ecf20Sopenharmony_ci		edac_dbg(1, "   IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
28658c2ecf20Sopenharmony_ci			 dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
28668c2ecf20Sopenharmony_ci			 (rw & 0x1) ? "R" : "-",
28678c2ecf20Sopenharmony_ci			 (rw & 0x2) ? "W" : "-",
28688c2ecf20Sopenharmony_ci			 dram_intlv_sel(pvt, range),
28698c2ecf20Sopenharmony_ci			 dram_dst_node(pvt, range));
28708c2ecf20Sopenharmony_ci	}
28718c2ecf20Sopenharmony_ci
28728c2ecf20Sopenharmony_ci	amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar);
28738c2ecf20Sopenharmony_ci	amd64_read_dct_pci_cfg(pvt, 0, DBAM0, &pvt->dbam0);
28748c2ecf20Sopenharmony_ci
28758c2ecf20Sopenharmony_ci	amd64_read_pci_cfg(pvt->F3, F10_ONLINE_SPARE, &pvt->online_spare);
28768c2ecf20Sopenharmony_ci
28778c2ecf20Sopenharmony_ci	amd64_read_dct_pci_cfg(pvt, 0, DCLR0, &pvt->dclr0);
28788c2ecf20Sopenharmony_ci	amd64_read_dct_pci_cfg(pvt, 0, DCHR0, &pvt->dchr0);
28798c2ecf20Sopenharmony_ci
28808c2ecf20Sopenharmony_ci	if (!dct_ganging_enabled(pvt)) {
28818c2ecf20Sopenharmony_ci		amd64_read_dct_pci_cfg(pvt, 1, DCLR0, &pvt->dclr1);
28828c2ecf20Sopenharmony_ci		amd64_read_dct_pci_cfg(pvt, 1, DCHR0, &pvt->dchr1);
28838c2ecf20Sopenharmony_ci	}
28848c2ecf20Sopenharmony_ci
28858c2ecf20Sopenharmony_ciskip:
28868c2ecf20Sopenharmony_ci	read_dct_base_mask(pvt);
28878c2ecf20Sopenharmony_ci
28888c2ecf20Sopenharmony_ci	determine_memory_type(pvt);
28898c2ecf20Sopenharmony_ci	edac_dbg(1, "  DIMM type: %s\n", edac_mem_types[pvt->dram_type]);
28908c2ecf20Sopenharmony_ci
28918c2ecf20Sopenharmony_ci	determine_ecc_sym_sz(pvt);
28928c2ecf20Sopenharmony_ci}
28938c2ecf20Sopenharmony_ci
28948c2ecf20Sopenharmony_ci/*
28958c2ecf20Sopenharmony_ci * NOTE: CPU Revision Dependent code
28968c2ecf20Sopenharmony_ci *
28978c2ecf20Sopenharmony_ci * Input:
28988c2ecf20Sopenharmony_ci *	@csrow_nr ChipSelect Row Number (0..NUM_CHIPSELECTS-1)
28998c2ecf20Sopenharmony_ci *	k8 private pointer to -->
29008c2ecf20Sopenharmony_ci *			DRAM Bank Address mapping register
29018c2ecf20Sopenharmony_ci *			node_id
29028c2ecf20Sopenharmony_ci *			DCL register where dual_channel_active is
29038c2ecf20Sopenharmony_ci *
29048c2ecf20Sopenharmony_ci * The DBAM register consists of 4 sets of 4 bits each definitions:
29058c2ecf20Sopenharmony_ci *
29068c2ecf20Sopenharmony_ci * Bits:	CSROWs
29078c2ecf20Sopenharmony_ci * 0-3		CSROWs 0 and 1
29088c2ecf20Sopenharmony_ci * 4-7		CSROWs 2 and 3
29098c2ecf20Sopenharmony_ci * 8-11		CSROWs 4 and 5
29108c2ecf20Sopenharmony_ci * 12-15	CSROWs 6 and 7
29118c2ecf20Sopenharmony_ci *
29128c2ecf20Sopenharmony_ci * Values range from: 0 to 15
29138c2ecf20Sopenharmony_ci * The meaning of the values depends on CPU revision and dual-channel state,
29148c2ecf20Sopenharmony_ci * see relevant BKDG more info.
29158c2ecf20Sopenharmony_ci *
29168c2ecf20Sopenharmony_ci * The memory controller provides for total of only 8 CSROWs in its current
29178c2ecf20Sopenharmony_ci * architecture. Each "pair" of CSROWs normally represents just one DIMM in
29188c2ecf20Sopenharmony_ci * single channel or two (2) DIMMs in dual channel mode.
29198c2ecf20Sopenharmony_ci *
29208c2ecf20Sopenharmony_ci * The following code logic collapses the various tables for CSROW based on CPU
29218c2ecf20Sopenharmony_ci * revision.
29228c2ecf20Sopenharmony_ci *
29238c2ecf20Sopenharmony_ci * Returns:
29248c2ecf20Sopenharmony_ci *	The number of PAGE_SIZE pages on the specified CSROW number it
29258c2ecf20Sopenharmony_ci *	encompasses
29268c2ecf20Sopenharmony_ci *
29278c2ecf20Sopenharmony_ci */
29288c2ecf20Sopenharmony_cistatic u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr_orig)
29298c2ecf20Sopenharmony_ci{
29308c2ecf20Sopenharmony_ci	u32 dbam = dct ? pvt->dbam1 : pvt->dbam0;
29318c2ecf20Sopenharmony_ci	int csrow_nr = csrow_nr_orig;
29328c2ecf20Sopenharmony_ci	u32 cs_mode, nr_pages;
29338c2ecf20Sopenharmony_ci
29348c2ecf20Sopenharmony_ci	if (!pvt->umc) {
29358c2ecf20Sopenharmony_ci		csrow_nr >>= 1;
29368c2ecf20Sopenharmony_ci		cs_mode = DBAM_DIMM(csrow_nr, dbam);
29378c2ecf20Sopenharmony_ci	} else {
29388c2ecf20Sopenharmony_ci		cs_mode = f17_get_cs_mode(csrow_nr >> 1, dct, pvt);
29398c2ecf20Sopenharmony_ci	}
29408c2ecf20Sopenharmony_ci
29418c2ecf20Sopenharmony_ci	nr_pages   = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, csrow_nr);
29428c2ecf20Sopenharmony_ci	nr_pages <<= 20 - PAGE_SHIFT;
29438c2ecf20Sopenharmony_ci
29448c2ecf20Sopenharmony_ci	edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n",
29458c2ecf20Sopenharmony_ci		    csrow_nr_orig, dct,  cs_mode);
29468c2ecf20Sopenharmony_ci	edac_dbg(0, "nr_pages/channel: %u\n", nr_pages);
29478c2ecf20Sopenharmony_ci
29488c2ecf20Sopenharmony_ci	return nr_pages;
29498c2ecf20Sopenharmony_ci}
29508c2ecf20Sopenharmony_ci
29518c2ecf20Sopenharmony_cistatic int init_csrows_df(struct mem_ctl_info *mci)
29528c2ecf20Sopenharmony_ci{
29538c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt = mci->pvt_info;
29548c2ecf20Sopenharmony_ci	enum edac_type edac_mode = EDAC_NONE;
29558c2ecf20Sopenharmony_ci	enum dev_type dev_type = DEV_UNKNOWN;
29568c2ecf20Sopenharmony_ci	struct dimm_info *dimm;
29578c2ecf20Sopenharmony_ci	int empty = 1;
29588c2ecf20Sopenharmony_ci	u8 umc, cs;
29598c2ecf20Sopenharmony_ci
29608c2ecf20Sopenharmony_ci	if (mci->edac_ctl_cap & EDAC_FLAG_S16ECD16ED) {
29618c2ecf20Sopenharmony_ci		edac_mode = EDAC_S16ECD16ED;
29628c2ecf20Sopenharmony_ci		dev_type = DEV_X16;
29638c2ecf20Sopenharmony_ci	} else if (mci->edac_ctl_cap & EDAC_FLAG_S8ECD8ED) {
29648c2ecf20Sopenharmony_ci		edac_mode = EDAC_S8ECD8ED;
29658c2ecf20Sopenharmony_ci		dev_type = DEV_X8;
29668c2ecf20Sopenharmony_ci	} else if (mci->edac_ctl_cap & EDAC_FLAG_S4ECD4ED) {
29678c2ecf20Sopenharmony_ci		edac_mode = EDAC_S4ECD4ED;
29688c2ecf20Sopenharmony_ci		dev_type = DEV_X4;
29698c2ecf20Sopenharmony_ci	} else if (mci->edac_ctl_cap & EDAC_FLAG_SECDED) {
29708c2ecf20Sopenharmony_ci		edac_mode = EDAC_SECDED;
29718c2ecf20Sopenharmony_ci	}
29728c2ecf20Sopenharmony_ci
29738c2ecf20Sopenharmony_ci	for_each_umc(umc) {
29748c2ecf20Sopenharmony_ci		for_each_chip_select(cs, umc, pvt) {
29758c2ecf20Sopenharmony_ci			if (!csrow_enabled(cs, umc, pvt))
29768c2ecf20Sopenharmony_ci				continue;
29778c2ecf20Sopenharmony_ci
29788c2ecf20Sopenharmony_ci			empty = 0;
29798c2ecf20Sopenharmony_ci			dimm = mci->csrows[cs]->channels[umc]->dimm;
29808c2ecf20Sopenharmony_ci
29818c2ecf20Sopenharmony_ci			edac_dbg(1, "MC node: %d, csrow: %d\n",
29828c2ecf20Sopenharmony_ci					pvt->mc_node_id, cs);
29838c2ecf20Sopenharmony_ci
29848c2ecf20Sopenharmony_ci			dimm->nr_pages = get_csrow_nr_pages(pvt, umc, cs);
29858c2ecf20Sopenharmony_ci			dimm->mtype = pvt->dram_type;
29868c2ecf20Sopenharmony_ci			dimm->edac_mode = edac_mode;
29878c2ecf20Sopenharmony_ci			dimm->dtype = dev_type;
29888c2ecf20Sopenharmony_ci			dimm->grain = 64;
29898c2ecf20Sopenharmony_ci		}
29908c2ecf20Sopenharmony_ci	}
29918c2ecf20Sopenharmony_ci
29928c2ecf20Sopenharmony_ci	return empty;
29938c2ecf20Sopenharmony_ci}
29948c2ecf20Sopenharmony_ci
29958c2ecf20Sopenharmony_ci/*
29968c2ecf20Sopenharmony_ci * Initialize the array of csrow attribute instances, based on the values
29978c2ecf20Sopenharmony_ci * from pci config hardware registers.
29988c2ecf20Sopenharmony_ci */
29998c2ecf20Sopenharmony_cistatic int init_csrows(struct mem_ctl_info *mci)
30008c2ecf20Sopenharmony_ci{
30018c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt = mci->pvt_info;
30028c2ecf20Sopenharmony_ci	enum edac_type edac_mode = EDAC_NONE;
30038c2ecf20Sopenharmony_ci	struct csrow_info *csrow;
30048c2ecf20Sopenharmony_ci	struct dimm_info *dimm;
30058c2ecf20Sopenharmony_ci	int i, j, empty = 1;
30068c2ecf20Sopenharmony_ci	int nr_pages = 0;
30078c2ecf20Sopenharmony_ci	u32 val;
30088c2ecf20Sopenharmony_ci
30098c2ecf20Sopenharmony_ci	if (pvt->umc)
30108c2ecf20Sopenharmony_ci		return init_csrows_df(mci);
30118c2ecf20Sopenharmony_ci
30128c2ecf20Sopenharmony_ci	amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
30138c2ecf20Sopenharmony_ci
30148c2ecf20Sopenharmony_ci	pvt->nbcfg = val;
30158c2ecf20Sopenharmony_ci
30168c2ecf20Sopenharmony_ci	edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
30178c2ecf20Sopenharmony_ci		 pvt->mc_node_id, val,
30188c2ecf20Sopenharmony_ci		 !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
30198c2ecf20Sopenharmony_ci
30208c2ecf20Sopenharmony_ci	/*
30218c2ecf20Sopenharmony_ci	 * We iterate over DCT0 here but we look at DCT1 in parallel, if needed.
30228c2ecf20Sopenharmony_ci	 */
30238c2ecf20Sopenharmony_ci	for_each_chip_select(i, 0, pvt) {
30248c2ecf20Sopenharmony_ci		bool row_dct0 = !!csrow_enabled(i, 0, pvt);
30258c2ecf20Sopenharmony_ci		bool row_dct1 = false;
30268c2ecf20Sopenharmony_ci
30278c2ecf20Sopenharmony_ci		if (pvt->fam != 0xf)
30288c2ecf20Sopenharmony_ci			row_dct1 = !!csrow_enabled(i, 1, pvt);
30298c2ecf20Sopenharmony_ci
30308c2ecf20Sopenharmony_ci		if (!row_dct0 && !row_dct1)
30318c2ecf20Sopenharmony_ci			continue;
30328c2ecf20Sopenharmony_ci
30338c2ecf20Sopenharmony_ci		csrow = mci->csrows[i];
30348c2ecf20Sopenharmony_ci		empty = 0;
30358c2ecf20Sopenharmony_ci
30368c2ecf20Sopenharmony_ci		edac_dbg(1, "MC node: %d, csrow: %d\n",
30378c2ecf20Sopenharmony_ci			    pvt->mc_node_id, i);
30388c2ecf20Sopenharmony_ci
30398c2ecf20Sopenharmony_ci		if (row_dct0) {
30408c2ecf20Sopenharmony_ci			nr_pages = get_csrow_nr_pages(pvt, 0, i);
30418c2ecf20Sopenharmony_ci			csrow->channels[0]->dimm->nr_pages = nr_pages;
30428c2ecf20Sopenharmony_ci		}
30438c2ecf20Sopenharmony_ci
30448c2ecf20Sopenharmony_ci		/* K8 has only one DCT */
30458c2ecf20Sopenharmony_ci		if (pvt->fam != 0xf && row_dct1) {
30468c2ecf20Sopenharmony_ci			int row_dct1_pages = get_csrow_nr_pages(pvt, 1, i);
30478c2ecf20Sopenharmony_ci
30488c2ecf20Sopenharmony_ci			csrow->channels[1]->dimm->nr_pages = row_dct1_pages;
30498c2ecf20Sopenharmony_ci			nr_pages += row_dct1_pages;
30508c2ecf20Sopenharmony_ci		}
30518c2ecf20Sopenharmony_ci
30528c2ecf20Sopenharmony_ci		edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages);
30538c2ecf20Sopenharmony_ci
30548c2ecf20Sopenharmony_ci		/* Determine DIMM ECC mode: */
30558c2ecf20Sopenharmony_ci		if (pvt->nbcfg & NBCFG_ECC_ENABLE) {
30568c2ecf20Sopenharmony_ci			edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL)
30578c2ecf20Sopenharmony_ci					? EDAC_S4ECD4ED
30588c2ecf20Sopenharmony_ci					: EDAC_SECDED;
30598c2ecf20Sopenharmony_ci		}
30608c2ecf20Sopenharmony_ci
30618c2ecf20Sopenharmony_ci		for (j = 0; j < pvt->channel_count; j++) {
30628c2ecf20Sopenharmony_ci			dimm = csrow->channels[j]->dimm;
30638c2ecf20Sopenharmony_ci			dimm->mtype = pvt->dram_type;
30648c2ecf20Sopenharmony_ci			dimm->edac_mode = edac_mode;
30658c2ecf20Sopenharmony_ci			dimm->grain = 64;
30668c2ecf20Sopenharmony_ci		}
30678c2ecf20Sopenharmony_ci	}
30688c2ecf20Sopenharmony_ci
30698c2ecf20Sopenharmony_ci	return empty;
30708c2ecf20Sopenharmony_ci}
30718c2ecf20Sopenharmony_ci
30728c2ecf20Sopenharmony_ci/* get all cores on this DCT */
30738c2ecf20Sopenharmony_cistatic void get_cpus_on_this_dct_cpumask(struct cpumask *mask, u16 nid)
30748c2ecf20Sopenharmony_ci{
30758c2ecf20Sopenharmony_ci	int cpu;
30768c2ecf20Sopenharmony_ci
30778c2ecf20Sopenharmony_ci	for_each_online_cpu(cpu)
30788c2ecf20Sopenharmony_ci		if (amd_get_nb_id(cpu) == nid)
30798c2ecf20Sopenharmony_ci			cpumask_set_cpu(cpu, mask);
30808c2ecf20Sopenharmony_ci}
30818c2ecf20Sopenharmony_ci
30828c2ecf20Sopenharmony_ci/* check MCG_CTL on all the cpus on this node */
30838c2ecf20Sopenharmony_cistatic bool nb_mce_bank_enabled_on_node(u16 nid)
30848c2ecf20Sopenharmony_ci{
30858c2ecf20Sopenharmony_ci	cpumask_var_t mask;
30868c2ecf20Sopenharmony_ci	int cpu, nbe;
30878c2ecf20Sopenharmony_ci	bool ret = false;
30888c2ecf20Sopenharmony_ci
30898c2ecf20Sopenharmony_ci	if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
30908c2ecf20Sopenharmony_ci		amd64_warn("%s: Error allocating mask\n", __func__);
30918c2ecf20Sopenharmony_ci		return false;
30928c2ecf20Sopenharmony_ci	}
30938c2ecf20Sopenharmony_ci
30948c2ecf20Sopenharmony_ci	get_cpus_on_this_dct_cpumask(mask, nid);
30958c2ecf20Sopenharmony_ci
30968c2ecf20Sopenharmony_ci	rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
30978c2ecf20Sopenharmony_ci
30988c2ecf20Sopenharmony_ci	for_each_cpu(cpu, mask) {
30998c2ecf20Sopenharmony_ci		struct msr *reg = per_cpu_ptr(msrs, cpu);
31008c2ecf20Sopenharmony_ci		nbe = reg->l & MSR_MCGCTL_NBE;
31018c2ecf20Sopenharmony_ci
31028c2ecf20Sopenharmony_ci		edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
31038c2ecf20Sopenharmony_ci			 cpu, reg->q,
31048c2ecf20Sopenharmony_ci			 (nbe ? "enabled" : "disabled"));
31058c2ecf20Sopenharmony_ci
31068c2ecf20Sopenharmony_ci		if (!nbe)
31078c2ecf20Sopenharmony_ci			goto out;
31088c2ecf20Sopenharmony_ci	}
31098c2ecf20Sopenharmony_ci	ret = true;
31108c2ecf20Sopenharmony_ci
31118c2ecf20Sopenharmony_ciout:
31128c2ecf20Sopenharmony_ci	free_cpumask_var(mask);
31138c2ecf20Sopenharmony_ci	return ret;
31148c2ecf20Sopenharmony_ci}
31158c2ecf20Sopenharmony_ci
31168c2ecf20Sopenharmony_cistatic int toggle_ecc_err_reporting(struct ecc_settings *s, u16 nid, bool on)
31178c2ecf20Sopenharmony_ci{
31188c2ecf20Sopenharmony_ci	cpumask_var_t cmask;
31198c2ecf20Sopenharmony_ci	int cpu;
31208c2ecf20Sopenharmony_ci
31218c2ecf20Sopenharmony_ci	if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
31228c2ecf20Sopenharmony_ci		amd64_warn("%s: error allocating mask\n", __func__);
31238c2ecf20Sopenharmony_ci		return -ENOMEM;
31248c2ecf20Sopenharmony_ci	}
31258c2ecf20Sopenharmony_ci
31268c2ecf20Sopenharmony_ci	get_cpus_on_this_dct_cpumask(cmask, nid);
31278c2ecf20Sopenharmony_ci
31288c2ecf20Sopenharmony_ci	rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
31298c2ecf20Sopenharmony_ci
31308c2ecf20Sopenharmony_ci	for_each_cpu(cpu, cmask) {
31318c2ecf20Sopenharmony_ci
31328c2ecf20Sopenharmony_ci		struct msr *reg = per_cpu_ptr(msrs, cpu);
31338c2ecf20Sopenharmony_ci
31348c2ecf20Sopenharmony_ci		if (on) {
31358c2ecf20Sopenharmony_ci			if (reg->l & MSR_MCGCTL_NBE)
31368c2ecf20Sopenharmony_ci				s->flags.nb_mce_enable = 1;
31378c2ecf20Sopenharmony_ci
31388c2ecf20Sopenharmony_ci			reg->l |= MSR_MCGCTL_NBE;
31398c2ecf20Sopenharmony_ci		} else {
31408c2ecf20Sopenharmony_ci			/*
31418c2ecf20Sopenharmony_ci			 * Turn off NB MCE reporting only when it was off before
31428c2ecf20Sopenharmony_ci			 */
31438c2ecf20Sopenharmony_ci			if (!s->flags.nb_mce_enable)
31448c2ecf20Sopenharmony_ci				reg->l &= ~MSR_MCGCTL_NBE;
31458c2ecf20Sopenharmony_ci		}
31468c2ecf20Sopenharmony_ci	}
31478c2ecf20Sopenharmony_ci	wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
31488c2ecf20Sopenharmony_ci
31498c2ecf20Sopenharmony_ci	free_cpumask_var(cmask);
31508c2ecf20Sopenharmony_ci
31518c2ecf20Sopenharmony_ci	return 0;
31528c2ecf20Sopenharmony_ci}
31538c2ecf20Sopenharmony_ci
31548c2ecf20Sopenharmony_cistatic bool enable_ecc_error_reporting(struct ecc_settings *s, u16 nid,
31558c2ecf20Sopenharmony_ci				       struct pci_dev *F3)
31568c2ecf20Sopenharmony_ci{
31578c2ecf20Sopenharmony_ci	bool ret = true;
31588c2ecf20Sopenharmony_ci	u32 value, mask = 0x3;		/* UECC/CECC enable */
31598c2ecf20Sopenharmony_ci
31608c2ecf20Sopenharmony_ci	if (toggle_ecc_err_reporting(s, nid, ON)) {
31618c2ecf20Sopenharmony_ci		amd64_warn("Error enabling ECC reporting over MCGCTL!\n");
31628c2ecf20Sopenharmony_ci		return false;
31638c2ecf20Sopenharmony_ci	}
31648c2ecf20Sopenharmony_ci
31658c2ecf20Sopenharmony_ci	amd64_read_pci_cfg(F3, NBCTL, &value);
31668c2ecf20Sopenharmony_ci
31678c2ecf20Sopenharmony_ci	s->old_nbctl   = value & mask;
31688c2ecf20Sopenharmony_ci	s->nbctl_valid = true;
31698c2ecf20Sopenharmony_ci
31708c2ecf20Sopenharmony_ci	value |= mask;
31718c2ecf20Sopenharmony_ci	amd64_write_pci_cfg(F3, NBCTL, value);
31728c2ecf20Sopenharmony_ci
31738c2ecf20Sopenharmony_ci	amd64_read_pci_cfg(F3, NBCFG, &value);
31748c2ecf20Sopenharmony_ci
31758c2ecf20Sopenharmony_ci	edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
31768c2ecf20Sopenharmony_ci		 nid, value, !!(value & NBCFG_ECC_ENABLE));
31778c2ecf20Sopenharmony_ci
31788c2ecf20Sopenharmony_ci	if (!(value & NBCFG_ECC_ENABLE)) {
31798c2ecf20Sopenharmony_ci		amd64_warn("DRAM ECC disabled on this node, enabling...\n");
31808c2ecf20Sopenharmony_ci
31818c2ecf20Sopenharmony_ci		s->flags.nb_ecc_prev = 0;
31828c2ecf20Sopenharmony_ci
31838c2ecf20Sopenharmony_ci		/* Attempt to turn on DRAM ECC Enable */
31848c2ecf20Sopenharmony_ci		value |= NBCFG_ECC_ENABLE;
31858c2ecf20Sopenharmony_ci		amd64_write_pci_cfg(F3, NBCFG, value);
31868c2ecf20Sopenharmony_ci
31878c2ecf20Sopenharmony_ci		amd64_read_pci_cfg(F3, NBCFG, &value);
31888c2ecf20Sopenharmony_ci
31898c2ecf20Sopenharmony_ci		if (!(value & NBCFG_ECC_ENABLE)) {
31908c2ecf20Sopenharmony_ci			amd64_warn("Hardware rejected DRAM ECC enable,"
31918c2ecf20Sopenharmony_ci				   "check memory DIMM configuration.\n");
31928c2ecf20Sopenharmony_ci			ret = false;
31938c2ecf20Sopenharmony_ci		} else {
31948c2ecf20Sopenharmony_ci			amd64_info("Hardware accepted DRAM ECC Enable\n");
31958c2ecf20Sopenharmony_ci		}
31968c2ecf20Sopenharmony_ci	} else {
31978c2ecf20Sopenharmony_ci		s->flags.nb_ecc_prev = 1;
31988c2ecf20Sopenharmony_ci	}
31998c2ecf20Sopenharmony_ci
32008c2ecf20Sopenharmony_ci	edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
32018c2ecf20Sopenharmony_ci		 nid, value, !!(value & NBCFG_ECC_ENABLE));
32028c2ecf20Sopenharmony_ci
32038c2ecf20Sopenharmony_ci	return ret;
32048c2ecf20Sopenharmony_ci}
32058c2ecf20Sopenharmony_ci
32068c2ecf20Sopenharmony_cistatic void restore_ecc_error_reporting(struct ecc_settings *s, u16 nid,
32078c2ecf20Sopenharmony_ci					struct pci_dev *F3)
32088c2ecf20Sopenharmony_ci{
32098c2ecf20Sopenharmony_ci	u32 value, mask = 0x3;		/* UECC/CECC enable */
32108c2ecf20Sopenharmony_ci
32118c2ecf20Sopenharmony_ci	if (!s->nbctl_valid)
32128c2ecf20Sopenharmony_ci		return;
32138c2ecf20Sopenharmony_ci
32148c2ecf20Sopenharmony_ci	amd64_read_pci_cfg(F3, NBCTL, &value);
32158c2ecf20Sopenharmony_ci	value &= ~mask;
32168c2ecf20Sopenharmony_ci	value |= s->old_nbctl;
32178c2ecf20Sopenharmony_ci
32188c2ecf20Sopenharmony_ci	amd64_write_pci_cfg(F3, NBCTL, value);
32198c2ecf20Sopenharmony_ci
32208c2ecf20Sopenharmony_ci	/* restore previous BIOS DRAM ECC "off" setting we force-enabled */
32218c2ecf20Sopenharmony_ci	if (!s->flags.nb_ecc_prev) {
32228c2ecf20Sopenharmony_ci		amd64_read_pci_cfg(F3, NBCFG, &value);
32238c2ecf20Sopenharmony_ci		value &= ~NBCFG_ECC_ENABLE;
32248c2ecf20Sopenharmony_ci		amd64_write_pci_cfg(F3, NBCFG, value);
32258c2ecf20Sopenharmony_ci	}
32268c2ecf20Sopenharmony_ci
32278c2ecf20Sopenharmony_ci	/* restore the NB Enable MCGCTL bit */
32288c2ecf20Sopenharmony_ci	if (toggle_ecc_err_reporting(s, nid, OFF))
32298c2ecf20Sopenharmony_ci		amd64_warn("Error restoring NB MCGCTL settings!\n");
32308c2ecf20Sopenharmony_ci}
32318c2ecf20Sopenharmony_ci
32328c2ecf20Sopenharmony_cistatic bool ecc_enabled(struct amd64_pvt *pvt)
32338c2ecf20Sopenharmony_ci{
32348c2ecf20Sopenharmony_ci	u16 nid = pvt->mc_node_id;
32358c2ecf20Sopenharmony_ci	bool nb_mce_en = false;
32368c2ecf20Sopenharmony_ci	u8 ecc_en = 0, i;
32378c2ecf20Sopenharmony_ci	u32 value;
32388c2ecf20Sopenharmony_ci
32398c2ecf20Sopenharmony_ci	if (boot_cpu_data.x86 >= 0x17) {
32408c2ecf20Sopenharmony_ci		u8 umc_en_mask = 0, ecc_en_mask = 0;
32418c2ecf20Sopenharmony_ci		struct amd64_umc *umc;
32428c2ecf20Sopenharmony_ci
32438c2ecf20Sopenharmony_ci		for_each_umc(i) {
32448c2ecf20Sopenharmony_ci			umc = &pvt->umc[i];
32458c2ecf20Sopenharmony_ci
32468c2ecf20Sopenharmony_ci			/* Only check enabled UMCs. */
32478c2ecf20Sopenharmony_ci			if (!(umc->sdp_ctrl & UMC_SDP_INIT))
32488c2ecf20Sopenharmony_ci				continue;
32498c2ecf20Sopenharmony_ci
32508c2ecf20Sopenharmony_ci			umc_en_mask |= BIT(i);
32518c2ecf20Sopenharmony_ci
32528c2ecf20Sopenharmony_ci			if (umc->umc_cap_hi & UMC_ECC_ENABLED)
32538c2ecf20Sopenharmony_ci				ecc_en_mask |= BIT(i);
32548c2ecf20Sopenharmony_ci		}
32558c2ecf20Sopenharmony_ci
32568c2ecf20Sopenharmony_ci		/* Check whether at least one UMC is enabled: */
32578c2ecf20Sopenharmony_ci		if (umc_en_mask)
32588c2ecf20Sopenharmony_ci			ecc_en = umc_en_mask == ecc_en_mask;
32598c2ecf20Sopenharmony_ci		else
32608c2ecf20Sopenharmony_ci			edac_dbg(0, "Node %d: No enabled UMCs.\n", nid);
32618c2ecf20Sopenharmony_ci
32628c2ecf20Sopenharmony_ci		/* Assume UMC MCA banks are enabled. */
32638c2ecf20Sopenharmony_ci		nb_mce_en = true;
32648c2ecf20Sopenharmony_ci	} else {
32658c2ecf20Sopenharmony_ci		amd64_read_pci_cfg(pvt->F3, NBCFG, &value);
32668c2ecf20Sopenharmony_ci
32678c2ecf20Sopenharmony_ci		ecc_en = !!(value & NBCFG_ECC_ENABLE);
32688c2ecf20Sopenharmony_ci
32698c2ecf20Sopenharmony_ci		nb_mce_en = nb_mce_bank_enabled_on_node(nid);
32708c2ecf20Sopenharmony_ci		if (!nb_mce_en)
32718c2ecf20Sopenharmony_ci			edac_dbg(0, "NB MCE bank disabled, set MSR 0x%08x[4] on node %d to enable.\n",
32728c2ecf20Sopenharmony_ci				     MSR_IA32_MCG_CTL, nid);
32738c2ecf20Sopenharmony_ci	}
32748c2ecf20Sopenharmony_ci
32758c2ecf20Sopenharmony_ci	amd64_info("Node %d: DRAM ECC %s.\n",
32768c2ecf20Sopenharmony_ci		   nid, (ecc_en ? "enabled" : "disabled"));
32778c2ecf20Sopenharmony_ci
32788c2ecf20Sopenharmony_ci	if (!ecc_en || !nb_mce_en)
32798c2ecf20Sopenharmony_ci		return false;
32808c2ecf20Sopenharmony_ci	else
32818c2ecf20Sopenharmony_ci		return true;
32828c2ecf20Sopenharmony_ci}
32838c2ecf20Sopenharmony_ci
32848c2ecf20Sopenharmony_cistatic inline void
32858c2ecf20Sopenharmony_cif17h_determine_edac_ctl_cap(struct mem_ctl_info *mci, struct amd64_pvt *pvt)
32868c2ecf20Sopenharmony_ci{
32878c2ecf20Sopenharmony_ci	u8 i, ecc_en = 1, cpk_en = 1, dev_x4 = 1, dev_x16 = 1;
32888c2ecf20Sopenharmony_ci
32898c2ecf20Sopenharmony_ci	for_each_umc(i) {
32908c2ecf20Sopenharmony_ci		if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) {
32918c2ecf20Sopenharmony_ci			ecc_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_ENABLED);
32928c2ecf20Sopenharmony_ci			cpk_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_CHIPKILL_CAP);
32938c2ecf20Sopenharmony_ci
32948c2ecf20Sopenharmony_ci			dev_x4  &= !!(pvt->umc[i].dimm_cfg & BIT(6));
32958c2ecf20Sopenharmony_ci			dev_x16 &= !!(pvt->umc[i].dimm_cfg & BIT(7));
32968c2ecf20Sopenharmony_ci		}
32978c2ecf20Sopenharmony_ci	}
32988c2ecf20Sopenharmony_ci
32998c2ecf20Sopenharmony_ci	/* Set chipkill only if ECC is enabled: */
33008c2ecf20Sopenharmony_ci	if (ecc_en) {
33018c2ecf20Sopenharmony_ci		mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
33028c2ecf20Sopenharmony_ci
33038c2ecf20Sopenharmony_ci		if (!cpk_en)
33048c2ecf20Sopenharmony_ci			return;
33058c2ecf20Sopenharmony_ci
33068c2ecf20Sopenharmony_ci		if (dev_x4)
33078c2ecf20Sopenharmony_ci			mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
33088c2ecf20Sopenharmony_ci		else if (dev_x16)
33098c2ecf20Sopenharmony_ci			mci->edac_ctl_cap |= EDAC_FLAG_S16ECD16ED;
33108c2ecf20Sopenharmony_ci		else
33118c2ecf20Sopenharmony_ci			mci->edac_ctl_cap |= EDAC_FLAG_S8ECD8ED;
33128c2ecf20Sopenharmony_ci	}
33138c2ecf20Sopenharmony_ci}
33148c2ecf20Sopenharmony_ci
33158c2ecf20Sopenharmony_cistatic void setup_mci_misc_attrs(struct mem_ctl_info *mci)
33168c2ecf20Sopenharmony_ci{
33178c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt = mci->pvt_info;
33188c2ecf20Sopenharmony_ci
33198c2ecf20Sopenharmony_ci	mci->mtype_cap		= MEM_FLAG_DDR2 | MEM_FLAG_RDDR2;
33208c2ecf20Sopenharmony_ci	mci->edac_ctl_cap	= EDAC_FLAG_NONE;
33218c2ecf20Sopenharmony_ci
33228c2ecf20Sopenharmony_ci	if (pvt->umc) {
33238c2ecf20Sopenharmony_ci		f17h_determine_edac_ctl_cap(mci, pvt);
33248c2ecf20Sopenharmony_ci	} else {
33258c2ecf20Sopenharmony_ci		if (pvt->nbcap & NBCAP_SECDED)
33268c2ecf20Sopenharmony_ci			mci->edac_ctl_cap |= EDAC_FLAG_SECDED;
33278c2ecf20Sopenharmony_ci
33288c2ecf20Sopenharmony_ci		if (pvt->nbcap & NBCAP_CHIPKILL)
33298c2ecf20Sopenharmony_ci			mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED;
33308c2ecf20Sopenharmony_ci	}
33318c2ecf20Sopenharmony_ci
33328c2ecf20Sopenharmony_ci	mci->edac_cap		= determine_edac_cap(pvt);
33338c2ecf20Sopenharmony_ci	mci->mod_name		= EDAC_MOD_STR;
33348c2ecf20Sopenharmony_ci	mci->ctl_name		= fam_type->ctl_name;
33358c2ecf20Sopenharmony_ci	mci->dev_name		= pci_name(pvt->F3);
33368c2ecf20Sopenharmony_ci	mci->ctl_page_to_phys	= NULL;
33378c2ecf20Sopenharmony_ci
33388c2ecf20Sopenharmony_ci	/* memory scrubber interface */
33398c2ecf20Sopenharmony_ci	mci->set_sdram_scrub_rate = set_scrub_rate;
33408c2ecf20Sopenharmony_ci	mci->get_sdram_scrub_rate = get_scrub_rate;
33418c2ecf20Sopenharmony_ci}
33428c2ecf20Sopenharmony_ci
33438c2ecf20Sopenharmony_ci/*
33448c2ecf20Sopenharmony_ci * returns a pointer to the family descriptor on success, NULL otherwise.
33458c2ecf20Sopenharmony_ci */
33468c2ecf20Sopenharmony_cistatic struct amd64_family_type *per_family_init(struct amd64_pvt *pvt)
33478c2ecf20Sopenharmony_ci{
33488c2ecf20Sopenharmony_ci	pvt->ext_model  = boot_cpu_data.x86_model >> 4;
33498c2ecf20Sopenharmony_ci	pvt->stepping	= boot_cpu_data.x86_stepping;
33508c2ecf20Sopenharmony_ci	pvt->model	= boot_cpu_data.x86_model;
33518c2ecf20Sopenharmony_ci	pvt->fam	= boot_cpu_data.x86;
33528c2ecf20Sopenharmony_ci
33538c2ecf20Sopenharmony_ci	switch (pvt->fam) {
33548c2ecf20Sopenharmony_ci	case 0xf:
33558c2ecf20Sopenharmony_ci		fam_type	= &family_types[K8_CPUS];
33568c2ecf20Sopenharmony_ci		pvt->ops	= &family_types[K8_CPUS].ops;
33578c2ecf20Sopenharmony_ci		break;
33588c2ecf20Sopenharmony_ci
33598c2ecf20Sopenharmony_ci	case 0x10:
33608c2ecf20Sopenharmony_ci		fam_type	= &family_types[F10_CPUS];
33618c2ecf20Sopenharmony_ci		pvt->ops	= &family_types[F10_CPUS].ops;
33628c2ecf20Sopenharmony_ci		break;
33638c2ecf20Sopenharmony_ci
33648c2ecf20Sopenharmony_ci	case 0x15:
33658c2ecf20Sopenharmony_ci		if (pvt->model == 0x30) {
33668c2ecf20Sopenharmony_ci			fam_type = &family_types[F15_M30H_CPUS];
33678c2ecf20Sopenharmony_ci			pvt->ops = &family_types[F15_M30H_CPUS].ops;
33688c2ecf20Sopenharmony_ci			break;
33698c2ecf20Sopenharmony_ci		} else if (pvt->model == 0x60) {
33708c2ecf20Sopenharmony_ci			fam_type = &family_types[F15_M60H_CPUS];
33718c2ecf20Sopenharmony_ci			pvt->ops = &family_types[F15_M60H_CPUS].ops;
33728c2ecf20Sopenharmony_ci			break;
33738c2ecf20Sopenharmony_ci		/* Richland is only client */
33748c2ecf20Sopenharmony_ci		} else if (pvt->model == 0x13) {
33758c2ecf20Sopenharmony_ci			return NULL;
33768c2ecf20Sopenharmony_ci		} else {
33778c2ecf20Sopenharmony_ci			fam_type	= &family_types[F15_CPUS];
33788c2ecf20Sopenharmony_ci			pvt->ops	= &family_types[F15_CPUS].ops;
33798c2ecf20Sopenharmony_ci		}
33808c2ecf20Sopenharmony_ci		break;
33818c2ecf20Sopenharmony_ci
33828c2ecf20Sopenharmony_ci	case 0x16:
33838c2ecf20Sopenharmony_ci		if (pvt->model == 0x30) {
33848c2ecf20Sopenharmony_ci			fam_type = &family_types[F16_M30H_CPUS];
33858c2ecf20Sopenharmony_ci			pvt->ops = &family_types[F16_M30H_CPUS].ops;
33868c2ecf20Sopenharmony_ci			break;
33878c2ecf20Sopenharmony_ci		}
33888c2ecf20Sopenharmony_ci		fam_type	= &family_types[F16_CPUS];
33898c2ecf20Sopenharmony_ci		pvt->ops	= &family_types[F16_CPUS].ops;
33908c2ecf20Sopenharmony_ci		break;
33918c2ecf20Sopenharmony_ci
33928c2ecf20Sopenharmony_ci	case 0x17:
33938c2ecf20Sopenharmony_ci		if (pvt->model >= 0x10 && pvt->model <= 0x2f) {
33948c2ecf20Sopenharmony_ci			fam_type = &family_types[F17_M10H_CPUS];
33958c2ecf20Sopenharmony_ci			pvt->ops = &family_types[F17_M10H_CPUS].ops;
33968c2ecf20Sopenharmony_ci			break;
33978c2ecf20Sopenharmony_ci		} else if (pvt->model >= 0x30 && pvt->model <= 0x3f) {
33988c2ecf20Sopenharmony_ci			fam_type = &family_types[F17_M30H_CPUS];
33998c2ecf20Sopenharmony_ci			pvt->ops = &family_types[F17_M30H_CPUS].ops;
34008c2ecf20Sopenharmony_ci			break;
34018c2ecf20Sopenharmony_ci		} else if (pvt->model >= 0x60 && pvt->model <= 0x6f) {
34028c2ecf20Sopenharmony_ci			fam_type = &family_types[F17_M60H_CPUS];
34038c2ecf20Sopenharmony_ci			pvt->ops = &family_types[F17_M60H_CPUS].ops;
34048c2ecf20Sopenharmony_ci			break;
34058c2ecf20Sopenharmony_ci		} else if (pvt->model >= 0x70 && pvt->model <= 0x7f) {
34068c2ecf20Sopenharmony_ci			fam_type = &family_types[F17_M70H_CPUS];
34078c2ecf20Sopenharmony_ci			pvt->ops = &family_types[F17_M70H_CPUS].ops;
34088c2ecf20Sopenharmony_ci			break;
34098c2ecf20Sopenharmony_ci		}
34108c2ecf20Sopenharmony_ci		fallthrough;
34118c2ecf20Sopenharmony_ci	case 0x18:
34128c2ecf20Sopenharmony_ci		fam_type	= &family_types[F17_CPUS];
34138c2ecf20Sopenharmony_ci		pvt->ops	= &family_types[F17_CPUS].ops;
34148c2ecf20Sopenharmony_ci
34158c2ecf20Sopenharmony_ci		if (pvt->fam == 0x18)
34168c2ecf20Sopenharmony_ci			family_types[F17_CPUS].ctl_name = "F18h";
34178c2ecf20Sopenharmony_ci		break;
34188c2ecf20Sopenharmony_ci
34198c2ecf20Sopenharmony_ci	case 0x19:
34208c2ecf20Sopenharmony_ci		if (pvt->model >= 0x20 && pvt->model <= 0x2f) {
34218c2ecf20Sopenharmony_ci			fam_type = &family_types[F17_M70H_CPUS];
34228c2ecf20Sopenharmony_ci			pvt->ops = &family_types[F17_M70H_CPUS].ops;
34238c2ecf20Sopenharmony_ci			fam_type->ctl_name = "F19h_M20h";
34248c2ecf20Sopenharmony_ci			break;
34258c2ecf20Sopenharmony_ci		}
34268c2ecf20Sopenharmony_ci		fam_type	= &family_types[F19_CPUS];
34278c2ecf20Sopenharmony_ci		pvt->ops	= &family_types[F19_CPUS].ops;
34288c2ecf20Sopenharmony_ci		family_types[F19_CPUS].ctl_name = "F19h";
34298c2ecf20Sopenharmony_ci		break;
34308c2ecf20Sopenharmony_ci
34318c2ecf20Sopenharmony_ci	default:
34328c2ecf20Sopenharmony_ci		amd64_err("Unsupported family!\n");
34338c2ecf20Sopenharmony_ci		return NULL;
34348c2ecf20Sopenharmony_ci	}
34358c2ecf20Sopenharmony_ci
34368c2ecf20Sopenharmony_ci	amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name,
34378c2ecf20Sopenharmony_ci		     (pvt->fam == 0xf ?
34388c2ecf20Sopenharmony_ci				(pvt->ext_model >= K8_REV_F  ? "revF or later "
34398c2ecf20Sopenharmony_ci							     : "revE or earlier ")
34408c2ecf20Sopenharmony_ci				 : ""), pvt->mc_node_id);
34418c2ecf20Sopenharmony_ci	return fam_type;
34428c2ecf20Sopenharmony_ci}
34438c2ecf20Sopenharmony_ci
34448c2ecf20Sopenharmony_cistatic const struct attribute_group *amd64_edac_attr_groups[] = {
34458c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG
34468c2ecf20Sopenharmony_ci	&amd64_edac_dbg_group,
34478c2ecf20Sopenharmony_ci#endif
34488c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION
34498c2ecf20Sopenharmony_ci	&amd64_edac_inj_group,
34508c2ecf20Sopenharmony_ci#endif
34518c2ecf20Sopenharmony_ci	NULL
34528c2ecf20Sopenharmony_ci};
34538c2ecf20Sopenharmony_ci
34548c2ecf20Sopenharmony_cistatic int hw_info_get(struct amd64_pvt *pvt)
34558c2ecf20Sopenharmony_ci{
34568c2ecf20Sopenharmony_ci	u16 pci_id1, pci_id2;
34578c2ecf20Sopenharmony_ci	int ret;
34588c2ecf20Sopenharmony_ci
34598c2ecf20Sopenharmony_ci	if (pvt->fam >= 0x17) {
34608c2ecf20Sopenharmony_ci		pvt->umc = kcalloc(fam_type->max_mcs, sizeof(struct amd64_umc), GFP_KERNEL);
34618c2ecf20Sopenharmony_ci		if (!pvt->umc)
34628c2ecf20Sopenharmony_ci			return -ENOMEM;
34638c2ecf20Sopenharmony_ci
34648c2ecf20Sopenharmony_ci		pci_id1 = fam_type->f0_id;
34658c2ecf20Sopenharmony_ci		pci_id2 = fam_type->f6_id;
34668c2ecf20Sopenharmony_ci	} else {
34678c2ecf20Sopenharmony_ci		pci_id1 = fam_type->f1_id;
34688c2ecf20Sopenharmony_ci		pci_id2 = fam_type->f2_id;
34698c2ecf20Sopenharmony_ci	}
34708c2ecf20Sopenharmony_ci
34718c2ecf20Sopenharmony_ci	ret = reserve_mc_sibling_devs(pvt, pci_id1, pci_id2);
34728c2ecf20Sopenharmony_ci	if (ret)
34738c2ecf20Sopenharmony_ci		return ret;
34748c2ecf20Sopenharmony_ci
34758c2ecf20Sopenharmony_ci	read_mc_regs(pvt);
34768c2ecf20Sopenharmony_ci
34778c2ecf20Sopenharmony_ci	return 0;
34788c2ecf20Sopenharmony_ci}
34798c2ecf20Sopenharmony_ci
34808c2ecf20Sopenharmony_cistatic void hw_info_put(struct amd64_pvt *pvt)
34818c2ecf20Sopenharmony_ci{
34828c2ecf20Sopenharmony_ci	if (pvt->F0 || pvt->F1)
34838c2ecf20Sopenharmony_ci		free_mc_sibling_devs(pvt);
34848c2ecf20Sopenharmony_ci
34858c2ecf20Sopenharmony_ci	kfree(pvt->umc);
34868c2ecf20Sopenharmony_ci}
34878c2ecf20Sopenharmony_ci
34888c2ecf20Sopenharmony_cistatic int init_one_instance(struct amd64_pvt *pvt)
34898c2ecf20Sopenharmony_ci{
34908c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci = NULL;
34918c2ecf20Sopenharmony_ci	struct edac_mc_layer layers[2];
34928c2ecf20Sopenharmony_ci	int ret = -EINVAL;
34938c2ecf20Sopenharmony_ci
34948c2ecf20Sopenharmony_ci	/*
34958c2ecf20Sopenharmony_ci	 * We need to determine how many memory channels there are. Then use
34968c2ecf20Sopenharmony_ci	 * that information for calculating the size of the dynamic instance
34978c2ecf20Sopenharmony_ci	 * tables in the 'mci' structure.
34988c2ecf20Sopenharmony_ci	 */
34998c2ecf20Sopenharmony_ci	pvt->channel_count = pvt->ops->early_channel_count(pvt);
35008c2ecf20Sopenharmony_ci	if (pvt->channel_count < 0)
35018c2ecf20Sopenharmony_ci		return ret;
35028c2ecf20Sopenharmony_ci
35038c2ecf20Sopenharmony_ci	ret = -ENOMEM;
35048c2ecf20Sopenharmony_ci	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
35058c2ecf20Sopenharmony_ci	layers[0].size = pvt->csels[0].b_cnt;
35068c2ecf20Sopenharmony_ci	layers[0].is_virt_csrow = true;
35078c2ecf20Sopenharmony_ci	layers[1].type = EDAC_MC_LAYER_CHANNEL;
35088c2ecf20Sopenharmony_ci
35098c2ecf20Sopenharmony_ci	/*
35108c2ecf20Sopenharmony_ci	 * Always allocate two channels since we can have setups with DIMMs on
35118c2ecf20Sopenharmony_ci	 * only one channel. Also, this simplifies handling later for the price
35128c2ecf20Sopenharmony_ci	 * of a couple of KBs tops.
35138c2ecf20Sopenharmony_ci	 */
35148c2ecf20Sopenharmony_ci	layers[1].size = fam_type->max_mcs;
35158c2ecf20Sopenharmony_ci	layers[1].is_virt_csrow = false;
35168c2ecf20Sopenharmony_ci
35178c2ecf20Sopenharmony_ci	mci = edac_mc_alloc(pvt->mc_node_id, ARRAY_SIZE(layers), layers, 0);
35188c2ecf20Sopenharmony_ci	if (!mci)
35198c2ecf20Sopenharmony_ci		return ret;
35208c2ecf20Sopenharmony_ci
35218c2ecf20Sopenharmony_ci	mci->pvt_info = pvt;
35228c2ecf20Sopenharmony_ci	mci->pdev = &pvt->F3->dev;
35238c2ecf20Sopenharmony_ci
35248c2ecf20Sopenharmony_ci	setup_mci_misc_attrs(mci);
35258c2ecf20Sopenharmony_ci
35268c2ecf20Sopenharmony_ci	if (init_csrows(mci))
35278c2ecf20Sopenharmony_ci		mci->edac_cap = EDAC_FLAG_NONE;
35288c2ecf20Sopenharmony_ci
35298c2ecf20Sopenharmony_ci	ret = -ENODEV;
35308c2ecf20Sopenharmony_ci	if (edac_mc_add_mc_with_groups(mci, amd64_edac_attr_groups)) {
35318c2ecf20Sopenharmony_ci		edac_dbg(1, "failed edac_mc_add_mc()\n");
35328c2ecf20Sopenharmony_ci		edac_mc_free(mci);
35338c2ecf20Sopenharmony_ci		return ret;
35348c2ecf20Sopenharmony_ci	}
35358c2ecf20Sopenharmony_ci
35368c2ecf20Sopenharmony_ci	return 0;
35378c2ecf20Sopenharmony_ci}
35388c2ecf20Sopenharmony_ci
35398c2ecf20Sopenharmony_cistatic bool instance_has_memory(struct amd64_pvt *pvt)
35408c2ecf20Sopenharmony_ci{
35418c2ecf20Sopenharmony_ci	bool cs_enabled = false;
35428c2ecf20Sopenharmony_ci	int cs = 0, dct = 0;
35438c2ecf20Sopenharmony_ci
35448c2ecf20Sopenharmony_ci	for (dct = 0; dct < fam_type->max_mcs; dct++) {
35458c2ecf20Sopenharmony_ci		for_each_chip_select(cs, dct, pvt)
35468c2ecf20Sopenharmony_ci			cs_enabled |= csrow_enabled(cs, dct, pvt);
35478c2ecf20Sopenharmony_ci	}
35488c2ecf20Sopenharmony_ci
35498c2ecf20Sopenharmony_ci	return cs_enabled;
35508c2ecf20Sopenharmony_ci}
35518c2ecf20Sopenharmony_ci
35528c2ecf20Sopenharmony_cistatic int probe_one_instance(unsigned int nid)
35538c2ecf20Sopenharmony_ci{
35548c2ecf20Sopenharmony_ci	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
35558c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt = NULL;
35568c2ecf20Sopenharmony_ci	struct ecc_settings *s;
35578c2ecf20Sopenharmony_ci	int ret;
35588c2ecf20Sopenharmony_ci
35598c2ecf20Sopenharmony_ci	ret = -ENOMEM;
35608c2ecf20Sopenharmony_ci	s = kzalloc(sizeof(struct ecc_settings), GFP_KERNEL);
35618c2ecf20Sopenharmony_ci	if (!s)
35628c2ecf20Sopenharmony_ci		goto err_out;
35638c2ecf20Sopenharmony_ci
35648c2ecf20Sopenharmony_ci	ecc_stngs[nid] = s;
35658c2ecf20Sopenharmony_ci
35668c2ecf20Sopenharmony_ci	pvt = kzalloc(sizeof(struct amd64_pvt), GFP_KERNEL);
35678c2ecf20Sopenharmony_ci	if (!pvt)
35688c2ecf20Sopenharmony_ci		goto err_settings;
35698c2ecf20Sopenharmony_ci
35708c2ecf20Sopenharmony_ci	pvt->mc_node_id	= nid;
35718c2ecf20Sopenharmony_ci	pvt->F3 = F3;
35728c2ecf20Sopenharmony_ci
35738c2ecf20Sopenharmony_ci	ret = -ENODEV;
35748c2ecf20Sopenharmony_ci	fam_type = per_family_init(pvt);
35758c2ecf20Sopenharmony_ci	if (!fam_type)
35768c2ecf20Sopenharmony_ci		goto err_enable;
35778c2ecf20Sopenharmony_ci
35788c2ecf20Sopenharmony_ci	ret = hw_info_get(pvt);
35798c2ecf20Sopenharmony_ci	if (ret < 0)
35808c2ecf20Sopenharmony_ci		goto err_enable;
35818c2ecf20Sopenharmony_ci
35828c2ecf20Sopenharmony_ci	ret = 0;
35838c2ecf20Sopenharmony_ci	if (!instance_has_memory(pvt)) {
35848c2ecf20Sopenharmony_ci		amd64_info("Node %d: No DIMMs detected.\n", nid);
35858c2ecf20Sopenharmony_ci		goto err_enable;
35868c2ecf20Sopenharmony_ci	}
35878c2ecf20Sopenharmony_ci
35888c2ecf20Sopenharmony_ci	if (!ecc_enabled(pvt)) {
35898c2ecf20Sopenharmony_ci		ret = -ENODEV;
35908c2ecf20Sopenharmony_ci
35918c2ecf20Sopenharmony_ci		if (!ecc_enable_override)
35928c2ecf20Sopenharmony_ci			goto err_enable;
35938c2ecf20Sopenharmony_ci
35948c2ecf20Sopenharmony_ci		if (boot_cpu_data.x86 >= 0x17) {
35958c2ecf20Sopenharmony_ci			amd64_warn("Forcing ECC on is not recommended on newer systems. Please enable ECC in BIOS.");
35968c2ecf20Sopenharmony_ci			goto err_enable;
35978c2ecf20Sopenharmony_ci		} else
35988c2ecf20Sopenharmony_ci			amd64_warn("Forcing ECC on!\n");
35998c2ecf20Sopenharmony_ci
36008c2ecf20Sopenharmony_ci		if (!enable_ecc_error_reporting(s, nid, F3))
36018c2ecf20Sopenharmony_ci			goto err_enable;
36028c2ecf20Sopenharmony_ci	}
36038c2ecf20Sopenharmony_ci
36048c2ecf20Sopenharmony_ci	ret = init_one_instance(pvt);
36058c2ecf20Sopenharmony_ci	if (ret < 0) {
36068c2ecf20Sopenharmony_ci		amd64_err("Error probing instance: %d\n", nid);
36078c2ecf20Sopenharmony_ci
36088c2ecf20Sopenharmony_ci		if (boot_cpu_data.x86 < 0x17)
36098c2ecf20Sopenharmony_ci			restore_ecc_error_reporting(s, nid, F3);
36108c2ecf20Sopenharmony_ci
36118c2ecf20Sopenharmony_ci		goto err_enable;
36128c2ecf20Sopenharmony_ci	}
36138c2ecf20Sopenharmony_ci
36148c2ecf20Sopenharmony_ci	dump_misc_regs(pvt);
36158c2ecf20Sopenharmony_ci
36168c2ecf20Sopenharmony_ci	return ret;
36178c2ecf20Sopenharmony_ci
36188c2ecf20Sopenharmony_cierr_enable:
36198c2ecf20Sopenharmony_ci	hw_info_put(pvt);
36208c2ecf20Sopenharmony_ci	kfree(pvt);
36218c2ecf20Sopenharmony_ci
36228c2ecf20Sopenharmony_cierr_settings:
36238c2ecf20Sopenharmony_ci	kfree(s);
36248c2ecf20Sopenharmony_ci	ecc_stngs[nid] = NULL;
36258c2ecf20Sopenharmony_ci
36268c2ecf20Sopenharmony_cierr_out:
36278c2ecf20Sopenharmony_ci	return ret;
36288c2ecf20Sopenharmony_ci}
36298c2ecf20Sopenharmony_ci
36308c2ecf20Sopenharmony_cistatic void remove_one_instance(unsigned int nid)
36318c2ecf20Sopenharmony_ci{
36328c2ecf20Sopenharmony_ci	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
36338c2ecf20Sopenharmony_ci	struct ecc_settings *s = ecc_stngs[nid];
36348c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci;
36358c2ecf20Sopenharmony_ci	struct amd64_pvt *pvt;
36368c2ecf20Sopenharmony_ci
36378c2ecf20Sopenharmony_ci	/* Remove from EDAC CORE tracking list */
36388c2ecf20Sopenharmony_ci	mci = edac_mc_del_mc(&F3->dev);
36398c2ecf20Sopenharmony_ci	if (!mci)
36408c2ecf20Sopenharmony_ci		return;
36418c2ecf20Sopenharmony_ci
36428c2ecf20Sopenharmony_ci	pvt = mci->pvt_info;
36438c2ecf20Sopenharmony_ci
36448c2ecf20Sopenharmony_ci	restore_ecc_error_reporting(s, nid, F3);
36458c2ecf20Sopenharmony_ci
36468c2ecf20Sopenharmony_ci	kfree(ecc_stngs[nid]);
36478c2ecf20Sopenharmony_ci	ecc_stngs[nid] = NULL;
36488c2ecf20Sopenharmony_ci
36498c2ecf20Sopenharmony_ci	/* Free the EDAC CORE resources */
36508c2ecf20Sopenharmony_ci	mci->pvt_info = NULL;
36518c2ecf20Sopenharmony_ci
36528c2ecf20Sopenharmony_ci	hw_info_put(pvt);
36538c2ecf20Sopenharmony_ci	kfree(pvt);
36548c2ecf20Sopenharmony_ci	edac_mc_free(mci);
36558c2ecf20Sopenharmony_ci}
36568c2ecf20Sopenharmony_ci
36578c2ecf20Sopenharmony_cistatic void setup_pci_device(void)
36588c2ecf20Sopenharmony_ci{
36598c2ecf20Sopenharmony_ci	if (pci_ctl)
36608c2ecf20Sopenharmony_ci		return;
36618c2ecf20Sopenharmony_ci
36628c2ecf20Sopenharmony_ci	pci_ctl = edac_pci_create_generic_ctl(pci_ctl_dev, EDAC_MOD_STR);
36638c2ecf20Sopenharmony_ci	if (!pci_ctl) {
36648c2ecf20Sopenharmony_ci		pr_warn("%s(): Unable to create PCI control\n", __func__);
36658c2ecf20Sopenharmony_ci		pr_warn("%s(): PCI error report via EDAC not set\n", __func__);
36668c2ecf20Sopenharmony_ci	}
36678c2ecf20Sopenharmony_ci}
36688c2ecf20Sopenharmony_ci
36698c2ecf20Sopenharmony_cistatic const struct x86_cpu_id amd64_cpuids[] = {
36708c2ecf20Sopenharmony_ci	X86_MATCH_VENDOR_FAM(AMD,	0x0F, NULL),
36718c2ecf20Sopenharmony_ci	X86_MATCH_VENDOR_FAM(AMD,	0x10, NULL),
36728c2ecf20Sopenharmony_ci	X86_MATCH_VENDOR_FAM(AMD,	0x15, NULL),
36738c2ecf20Sopenharmony_ci	X86_MATCH_VENDOR_FAM(AMD,	0x16, NULL),
36748c2ecf20Sopenharmony_ci	X86_MATCH_VENDOR_FAM(AMD,	0x17, NULL),
36758c2ecf20Sopenharmony_ci	X86_MATCH_VENDOR_FAM(HYGON,	0x18, NULL),
36768c2ecf20Sopenharmony_ci	X86_MATCH_VENDOR_FAM(AMD,	0x19, NULL),
36778c2ecf20Sopenharmony_ci	{ }
36788c2ecf20Sopenharmony_ci};
36798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(x86cpu, amd64_cpuids);
36808c2ecf20Sopenharmony_ci
36818c2ecf20Sopenharmony_cistatic int __init amd64_edac_init(void)
36828c2ecf20Sopenharmony_ci{
36838c2ecf20Sopenharmony_ci	const char *owner;
36848c2ecf20Sopenharmony_ci	int err = -ENODEV;
36858c2ecf20Sopenharmony_ci	int i;
36868c2ecf20Sopenharmony_ci
36878c2ecf20Sopenharmony_ci	owner = edac_get_owner();
36888c2ecf20Sopenharmony_ci	if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
36898c2ecf20Sopenharmony_ci		return -EBUSY;
36908c2ecf20Sopenharmony_ci
36918c2ecf20Sopenharmony_ci	if (!x86_match_cpu(amd64_cpuids))
36928c2ecf20Sopenharmony_ci		return -ENODEV;
36938c2ecf20Sopenharmony_ci
36948c2ecf20Sopenharmony_ci	if (amd_cache_northbridges() < 0)
36958c2ecf20Sopenharmony_ci		return -ENODEV;
36968c2ecf20Sopenharmony_ci
36978c2ecf20Sopenharmony_ci	opstate_init();
36988c2ecf20Sopenharmony_ci
36998c2ecf20Sopenharmony_ci	err = -ENOMEM;
37008c2ecf20Sopenharmony_ci	ecc_stngs = kcalloc(amd_nb_num(), sizeof(ecc_stngs[0]), GFP_KERNEL);
37018c2ecf20Sopenharmony_ci	if (!ecc_stngs)
37028c2ecf20Sopenharmony_ci		goto err_free;
37038c2ecf20Sopenharmony_ci
37048c2ecf20Sopenharmony_ci	msrs = msrs_alloc();
37058c2ecf20Sopenharmony_ci	if (!msrs)
37068c2ecf20Sopenharmony_ci		goto err_free;
37078c2ecf20Sopenharmony_ci
37088c2ecf20Sopenharmony_ci	for (i = 0; i < amd_nb_num(); i++) {
37098c2ecf20Sopenharmony_ci		err = probe_one_instance(i);
37108c2ecf20Sopenharmony_ci		if (err) {
37118c2ecf20Sopenharmony_ci			/* unwind properly */
37128c2ecf20Sopenharmony_ci			while (--i >= 0)
37138c2ecf20Sopenharmony_ci				remove_one_instance(i);
37148c2ecf20Sopenharmony_ci
37158c2ecf20Sopenharmony_ci			goto err_pci;
37168c2ecf20Sopenharmony_ci		}
37178c2ecf20Sopenharmony_ci	}
37188c2ecf20Sopenharmony_ci
37198c2ecf20Sopenharmony_ci	if (!edac_has_mcs()) {
37208c2ecf20Sopenharmony_ci		err = -ENODEV;
37218c2ecf20Sopenharmony_ci		goto err_pci;
37228c2ecf20Sopenharmony_ci	}
37238c2ecf20Sopenharmony_ci
37248c2ecf20Sopenharmony_ci	/* register stuff with EDAC MCE */
37258c2ecf20Sopenharmony_ci	if (boot_cpu_data.x86 >= 0x17)
37268c2ecf20Sopenharmony_ci		amd_register_ecc_decoder(decode_umc_error);
37278c2ecf20Sopenharmony_ci	else
37288c2ecf20Sopenharmony_ci		amd_register_ecc_decoder(decode_bus_error);
37298c2ecf20Sopenharmony_ci
37308c2ecf20Sopenharmony_ci	setup_pci_device();
37318c2ecf20Sopenharmony_ci
37328c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_32
37338c2ecf20Sopenharmony_ci	amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR);
37348c2ecf20Sopenharmony_ci#endif
37358c2ecf20Sopenharmony_ci
37368c2ecf20Sopenharmony_ci	printk(KERN_INFO "AMD64 EDAC driver v%s\n", EDAC_AMD64_VERSION);
37378c2ecf20Sopenharmony_ci
37388c2ecf20Sopenharmony_ci	return 0;
37398c2ecf20Sopenharmony_ci
37408c2ecf20Sopenharmony_cierr_pci:
37418c2ecf20Sopenharmony_ci	pci_ctl_dev = NULL;
37428c2ecf20Sopenharmony_ci
37438c2ecf20Sopenharmony_ci	msrs_free(msrs);
37448c2ecf20Sopenharmony_ci	msrs = NULL;
37458c2ecf20Sopenharmony_ci
37468c2ecf20Sopenharmony_cierr_free:
37478c2ecf20Sopenharmony_ci	kfree(ecc_stngs);
37488c2ecf20Sopenharmony_ci	ecc_stngs = NULL;
37498c2ecf20Sopenharmony_ci
37508c2ecf20Sopenharmony_ci	return err;
37518c2ecf20Sopenharmony_ci}
37528c2ecf20Sopenharmony_ci
37538c2ecf20Sopenharmony_cistatic void __exit amd64_edac_exit(void)
37548c2ecf20Sopenharmony_ci{
37558c2ecf20Sopenharmony_ci	int i;
37568c2ecf20Sopenharmony_ci
37578c2ecf20Sopenharmony_ci	if (pci_ctl)
37588c2ecf20Sopenharmony_ci		edac_pci_release_generic_ctl(pci_ctl);
37598c2ecf20Sopenharmony_ci
37608c2ecf20Sopenharmony_ci	/* unregister from EDAC MCE */
37618c2ecf20Sopenharmony_ci	if (boot_cpu_data.x86 >= 0x17)
37628c2ecf20Sopenharmony_ci		amd_unregister_ecc_decoder(decode_umc_error);
37638c2ecf20Sopenharmony_ci	else
37648c2ecf20Sopenharmony_ci		amd_unregister_ecc_decoder(decode_bus_error);
37658c2ecf20Sopenharmony_ci
37668c2ecf20Sopenharmony_ci	for (i = 0; i < amd_nb_num(); i++)
37678c2ecf20Sopenharmony_ci		remove_one_instance(i);
37688c2ecf20Sopenharmony_ci
37698c2ecf20Sopenharmony_ci	kfree(ecc_stngs);
37708c2ecf20Sopenharmony_ci	ecc_stngs = NULL;
37718c2ecf20Sopenharmony_ci
37728c2ecf20Sopenharmony_ci	pci_ctl_dev = NULL;
37738c2ecf20Sopenharmony_ci
37748c2ecf20Sopenharmony_ci	msrs_free(msrs);
37758c2ecf20Sopenharmony_ci	msrs = NULL;
37768c2ecf20Sopenharmony_ci}
37778c2ecf20Sopenharmony_ci
37788c2ecf20Sopenharmony_cimodule_init(amd64_edac_init);
37798c2ecf20Sopenharmony_cimodule_exit(amd64_edac_exit);
37808c2ecf20Sopenharmony_ci
37818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
37828c2ecf20Sopenharmony_ciMODULE_AUTHOR("SoftwareBitMaker: Doug Thompson, "
37838c2ecf20Sopenharmony_ci		"Dave Peterson, Thayne Harbaugh");
37848c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MC support for AMD64 memory controllers - "
37858c2ecf20Sopenharmony_ci		EDAC_AMD64_VERSION);
37868c2ecf20Sopenharmony_ci
37878c2ecf20Sopenharmony_cimodule_param(edac_op_state, int, 0444);
37888c2ecf20Sopenharmony_ciMODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
3789