xref: /openbmc/linux/drivers/edac/i10nm_base.c (revision ecc23d0a422a3118fcf6e4f0a46e17a6c2047b02)
1d4dc89d0SQiuxu Zhuo // SPDX-License-Identifier: GPL-2.0
2d4dc89d0SQiuxu Zhuo /*
3d4dc89d0SQiuxu Zhuo  * Driver for Intel(R) 10nm server memory controller.
4d4dc89d0SQiuxu Zhuo  * Copyright (c) 2019, Intel Corporation.
5d4dc89d0SQiuxu Zhuo  *
6d4dc89d0SQiuxu Zhuo  */
7d4dc89d0SQiuxu Zhuo 
8d4dc89d0SQiuxu Zhuo #include <linux/kernel.h>
983ff51c4SQiuxu Zhuo #include <linux/io.h>
10d4dc89d0SQiuxu Zhuo #include <asm/cpu_device_id.h>
11d4dc89d0SQiuxu Zhuo #include <asm/intel-family.h>
12d4dc89d0SQiuxu Zhuo #include <asm/mce.h>
13d4dc89d0SQiuxu Zhuo #include "edac_module.h"
14d4dc89d0SQiuxu Zhuo #include "skx_common.h"
15d4dc89d0SQiuxu Zhuo 
16ba987eaaSQiuxu Zhuo #define I10NM_REVISION	"v0.0.6"
17d4dc89d0SQiuxu Zhuo #define EDAC_MOD_STR	"i10nm_edac"
18d4dc89d0SQiuxu Zhuo 
19d4dc89d0SQiuxu Zhuo /* Debug macros */
20d4dc89d0SQiuxu Zhuo #define i10nm_printk(level, fmt, arg...)	\
21d4dc89d0SQiuxu Zhuo 	edac_printk(level, "i10nm", fmt, ##arg)
22d4dc89d0SQiuxu Zhuo 
23d4dc89d0SQiuxu Zhuo #define I10NM_GET_SCK_BAR(d, reg)	\
24d4dc89d0SQiuxu Zhuo 	pci_read_config_dword((d)->uracu, 0xd0, &(reg))
25d4dc89d0SQiuxu Zhuo #define I10NM_GET_IMC_BAR(d, i, reg)		\
26ba987eaaSQiuxu Zhuo 	pci_read_config_dword((d)->uracu,	\
27ba987eaaSQiuxu Zhuo 	(res_cfg->type == GNR ? 0xd4 : 0xd8) + (i) * 4, &(reg))
284bd4d32eSQiuxu Zhuo #define I10NM_GET_SAD(d, offset, i, reg)\
29ba987eaaSQiuxu Zhuo 	pci_read_config_dword((d)->sad_all, (offset) + (i) * \
30ba987eaaSQiuxu Zhuo 	(res_cfg->type == GNR ? 12 : 8), &(reg))
31c9450883SQiuxu Zhuo #define I10NM_GET_HBM_IMC_BAR(d, reg)	\
32c9450883SQiuxu Zhuo 	pci_read_config_dword((d)->uracu, 0xd4, &(reg))
33c9450883SQiuxu Zhuo #define I10NM_GET_CAPID3_CFG(d, reg)	\
34ba987eaaSQiuxu Zhuo 	pci_read_config_dword((d)->pcu_cr3,	\
35ba987eaaSQiuxu Zhuo 	res_cfg->type == GNR ? 0x290 : 0x90, &(reg))
36ba987eaaSQiuxu Zhuo #define I10NM_GET_CAPID5_CFG(d, reg)	\
37ba987eaaSQiuxu Zhuo 	pci_read_config_dword((d)->pcu_cr3,	\
38ba987eaaSQiuxu Zhuo 	res_cfg->type == GNR ? 0x298 : 0x98, &(reg))
39d4dc89d0SQiuxu Zhuo #define I10NM_GET_DIMMMTR(m, i, j)	\
40ba987eaaSQiuxu Zhuo 	readl((m)->mbase + ((m)->hbm_mc ? 0x80c :	\
41ba987eaaSQiuxu Zhuo 	(res_cfg->type == GNR ? 0xc0c : 0x2080c)) +	\
42c9450883SQiuxu Zhuo 	(i) * (m)->chan_mmio_sz + (j) * 4)
432294a729SQiuxu Zhuo #define I10NM_GET_MCDDRTCFG(m, i)	\
44c9450883SQiuxu Zhuo 	readl((m)->mbase + ((m)->hbm_mc ? 0x970 : 0x20970) + \
452294a729SQiuxu Zhuo 	(i) * (m)->chan_mmio_sz)
4683ff51c4SQiuxu Zhuo #define I10NM_GET_MCMTR(m, i)		\
47ba987eaaSQiuxu Zhuo 	readl((m)->mbase + ((m)->hbm_mc ? 0xef8 :	\
48ba987eaaSQiuxu Zhuo 	(res_cfg->type == GNR ? 0xaf8 : 0x20ef8)) +	\
49c9450883SQiuxu Zhuo 	(i) * (m)->chan_mmio_sz)
50479f58ddSQiuxu Zhuo #define I10NM_GET_AMAP(m, i)		\
51ba987eaaSQiuxu Zhuo 	readl((m)->mbase + ((m)->hbm_mc ? 0x814 :	\
52ba987eaaSQiuxu Zhuo 	(res_cfg->type == GNR ? 0xc14 : 0x20814)) +	\
53c9450883SQiuxu Zhuo 	(i) * (m)->chan_mmio_sz)
54cf4e6d52SYouquan Song #define I10NM_GET_REG32(m, i, offset)	\
55cf4e6d52SYouquan Song 	readl((m)->mbase + (i) * (m)->chan_mmio_sz + (offset))
56cf4e6d52SYouquan Song #define I10NM_GET_REG64(m, i, offset)	\
57cf4e6d52SYouquan Song 	readq((m)->mbase + (i) * (m)->chan_mmio_sz + (offset))
58cf4e6d52SYouquan Song #define I10NM_SET_REG32(m, i, offset, v)	\
59cf4e6d52SYouquan Song 	writel(v, (m)->mbase + (i) * (m)->chan_mmio_sz + (offset))
60d4dc89d0SQiuxu Zhuo 
61d4dc89d0SQiuxu Zhuo #define I10NM_GET_SCK_MMIO_BASE(reg)	(GET_BITFIELD(reg, 0, 28) << 23)
62d4dc89d0SQiuxu Zhuo #define I10NM_GET_IMC_MMIO_OFFSET(reg)	(GET_BITFIELD(reg, 0, 10) << 12)
63d4dc89d0SQiuxu Zhuo #define I10NM_GET_IMC_MMIO_SIZE(reg)	((GET_BITFIELD(reg, 13, 23) - \
64d4dc89d0SQiuxu Zhuo 					 GET_BITFIELD(reg, 0, 10) + 1) << 12)
65c9450883SQiuxu Zhuo #define I10NM_GET_HBM_IMC_MMIO_OFFSET(reg)	\
66c9450883SQiuxu Zhuo 	((GET_BITFIELD(reg, 0, 10) << 12) + 0x140000)
67c9450883SQiuxu Zhuo 
68ba987eaaSQiuxu Zhuo #define I10NM_GNR_IMC_MMIO_OFFSET	0x24c000
69ba987eaaSQiuxu Zhuo #define I10NM_GNR_IMC_MMIO_SIZE		0x4000
70c9450883SQiuxu Zhuo #define I10NM_HBM_IMC_MMIO_SIZE		0x9000
71ba987eaaSQiuxu Zhuo #define I10NM_DDR_IMC_CH_CNT(reg)	GET_BITFIELD(reg, 21, 24)
72c9450883SQiuxu Zhuo #define I10NM_IS_HBM_PRESENT(reg)	GET_BITFIELD(reg, 27, 30)
73c9450883SQiuxu Zhuo #define I10NM_IS_HBM_IMC(reg)		GET_BITFIELD(reg, 29, 29)
74d4dc89d0SQiuxu Zhuo 
754bd4d32eSQiuxu Zhuo #define I10NM_MAX_SAD			16
764bd4d32eSQiuxu Zhuo #define I10NM_SAD_ENABLE(reg)		GET_BITFIELD(reg, 0, 0)
774bd4d32eSQiuxu Zhuo #define I10NM_SAD_NM_CACHEABLE(reg)	GET_BITFIELD(reg, 5, 5)
784bd4d32eSQiuxu Zhuo 
79cf4e6d52SYouquan Song #define RETRY_RD_ERR_LOG_UC		BIT(1)
80cf4e6d52SYouquan Song #define RETRY_RD_ERR_LOG_NOOVER		BIT(14)
81cf4e6d52SYouquan Song #define RETRY_RD_ERR_LOG_EN		BIT(15)
82cf4e6d52SYouquan Song #define RETRY_RD_ERR_LOG_NOOVER_UC	(BIT(14) | BIT(1))
83cf4e6d52SYouquan Song #define RETRY_RD_ERR_LOG_OVER_UC_V	(BIT(2) | BIT(1) | BIT(0))
84cf4e6d52SYouquan Song 
85d4dc89d0SQiuxu Zhuo static struct list_head *i10nm_edac_list;
86d4dc89d0SQiuxu Zhuo 
87cf4e6d52SYouquan Song static struct res_config *res_cfg;
88cf4e6d52SYouquan Song static int retry_rd_err_log;
892738c69aSYouquan Song static int decoding_via_mca;
902738c69aSYouquan Song static bool mem_cfg_2lm;
91cf4e6d52SYouquan Song 
92cf4e6d52SYouquan Song static u32 offsets_scrub_icx[]  = {0x22c60, 0x22c54, 0x22c5c, 0x22c58, 0x22c28, 0x20ed8};
93cf4e6d52SYouquan Song static u32 offsets_scrub_spr[]  = {0x22c60, 0x22c54, 0x22f08, 0x22c58, 0x22c28, 0x20ed8};
94acd4cf68SQiuxu Zhuo static u32 offsets_scrub_spr_hbm0[]  = {0x2860, 0x2854, 0x2b08, 0x2858, 0x2828, 0x0ed8};
95acd4cf68SQiuxu Zhuo static u32 offsets_scrub_spr_hbm1[]  = {0x2c60, 0x2c54, 0x2f08, 0x2c58, 0x2c28, 0x0fa8};
96cf4e6d52SYouquan Song static u32 offsets_demand_icx[] = {0x22e54, 0x22e60, 0x22e64, 0x22e58, 0x22e5c, 0x20ee0};
97cf4e6d52SYouquan Song static u32 offsets_demand_spr[] = {0x22e54, 0x22e60, 0x22f10, 0x22e58, 0x22e5c, 0x20ee0};
98d5f5e499SQiuxu Zhuo static u32 offsets_demand2_spr[] = {0x22c70, 0x22d80, 0x22f18, 0x22d58, 0x22c64, 0x20f10};
99acd4cf68SQiuxu Zhuo static u32 offsets_demand_spr_hbm0[] = {0x2a54, 0x2a60, 0x2b10, 0x2a58, 0x2a5c, 0x0ee0};
100acd4cf68SQiuxu Zhuo static u32 offsets_demand_spr_hbm1[] = {0x2e54, 0x2e60, 0x2f10, 0x2e58, 0x2e5c, 0x0fb0};
101cf4e6d52SYouquan Song 
__enable_retry_rd_err_log(struct skx_imc * imc,int chan,bool enable,u32 * offsets_scrub,u32 * offsets_demand,u32 * offsets_demand2)102acd4cf68SQiuxu Zhuo static void __enable_retry_rd_err_log(struct skx_imc *imc, int chan, bool enable,
103d5f5e499SQiuxu Zhuo 				      u32 *offsets_scrub, u32 *offsets_demand,
104d5f5e499SQiuxu Zhuo 				      u32 *offsets_demand2)
105cf4e6d52SYouquan Song {
106d5f5e499SQiuxu Zhuo 	u32 s, d, d2;
107cf4e6d52SYouquan Song 
108acd4cf68SQiuxu Zhuo 	s = I10NM_GET_REG32(imc, chan, offsets_scrub[0]);
109acd4cf68SQiuxu Zhuo 	d = I10NM_GET_REG32(imc, chan, offsets_demand[0]);
110d5f5e499SQiuxu Zhuo 	if (offsets_demand2)
111d5f5e499SQiuxu Zhuo 		d2 = I10NM_GET_REG32(imc, chan, offsets_demand2[0]);
112cf4e6d52SYouquan Song 
113cf4e6d52SYouquan Song 	if (enable) {
114cf4e6d52SYouquan Song 		/* Save default configurations */
115cf4e6d52SYouquan Song 		imc->chan[chan].retry_rd_err_log_s = s;
116cf4e6d52SYouquan Song 		imc->chan[chan].retry_rd_err_log_d = d;
117d5f5e499SQiuxu Zhuo 		if (offsets_demand2)
118d5f5e499SQiuxu Zhuo 			imc->chan[chan].retry_rd_err_log_d2 = d2;
119cf4e6d52SYouquan Song 
120cf4e6d52SYouquan Song 		s &= ~RETRY_RD_ERR_LOG_NOOVER_UC;
121cf4e6d52SYouquan Song 		s |=  RETRY_RD_ERR_LOG_EN;
122cf4e6d52SYouquan Song 		d &= ~RETRY_RD_ERR_LOG_NOOVER_UC;
123cf4e6d52SYouquan Song 		d |=  RETRY_RD_ERR_LOG_EN;
124d5f5e499SQiuxu Zhuo 
125d5f5e499SQiuxu Zhuo 		if (offsets_demand2) {
126d5f5e499SQiuxu Zhuo 			d2 &= ~RETRY_RD_ERR_LOG_UC;
127d5f5e499SQiuxu Zhuo 			d2 |=  RETRY_RD_ERR_LOG_NOOVER;
128d5f5e499SQiuxu Zhuo 			d2 |=  RETRY_RD_ERR_LOG_EN;
129d5f5e499SQiuxu Zhuo 		}
130cf4e6d52SYouquan Song 	} else {
131cf4e6d52SYouquan Song 		/* Restore default configurations */
132cf4e6d52SYouquan Song 		if (imc->chan[chan].retry_rd_err_log_s & RETRY_RD_ERR_LOG_UC)
133cf4e6d52SYouquan Song 			s |=  RETRY_RD_ERR_LOG_UC;
134cf4e6d52SYouquan Song 		if (imc->chan[chan].retry_rd_err_log_s & RETRY_RD_ERR_LOG_NOOVER)
135cf4e6d52SYouquan Song 			s |=  RETRY_RD_ERR_LOG_NOOVER;
136cf4e6d52SYouquan Song 		if (!(imc->chan[chan].retry_rd_err_log_s & RETRY_RD_ERR_LOG_EN))
137cf4e6d52SYouquan Song 			s &= ~RETRY_RD_ERR_LOG_EN;
138cf4e6d52SYouquan Song 		if (imc->chan[chan].retry_rd_err_log_d & RETRY_RD_ERR_LOG_UC)
139cf4e6d52SYouquan Song 			d |=  RETRY_RD_ERR_LOG_UC;
140cf4e6d52SYouquan Song 		if (imc->chan[chan].retry_rd_err_log_d & RETRY_RD_ERR_LOG_NOOVER)
141cf4e6d52SYouquan Song 			d |=  RETRY_RD_ERR_LOG_NOOVER;
142cf4e6d52SYouquan Song 		if (!(imc->chan[chan].retry_rd_err_log_d & RETRY_RD_ERR_LOG_EN))
143cf4e6d52SYouquan Song 			d &= ~RETRY_RD_ERR_LOG_EN;
144d5f5e499SQiuxu Zhuo 
145d5f5e499SQiuxu Zhuo 		if (offsets_demand2) {
146d5f5e499SQiuxu Zhuo 			if (imc->chan[chan].retry_rd_err_log_d2 & RETRY_RD_ERR_LOG_UC)
147d5f5e499SQiuxu Zhuo 				d2 |=  RETRY_RD_ERR_LOG_UC;
148d5f5e499SQiuxu Zhuo 			if (!(imc->chan[chan].retry_rd_err_log_d2 & RETRY_RD_ERR_LOG_NOOVER))
149d5f5e499SQiuxu Zhuo 				d2 &=  ~RETRY_RD_ERR_LOG_NOOVER;
150d5f5e499SQiuxu Zhuo 			if (!(imc->chan[chan].retry_rd_err_log_d2 & RETRY_RD_ERR_LOG_EN))
151d5f5e499SQiuxu Zhuo 				d2 &= ~RETRY_RD_ERR_LOG_EN;
152d5f5e499SQiuxu Zhuo 		}
153cf4e6d52SYouquan Song 	}
154cf4e6d52SYouquan Song 
155acd4cf68SQiuxu Zhuo 	I10NM_SET_REG32(imc, chan, offsets_scrub[0], s);
156acd4cf68SQiuxu Zhuo 	I10NM_SET_REG32(imc, chan, offsets_demand[0], d);
157d5f5e499SQiuxu Zhuo 	if (offsets_demand2)
158d5f5e499SQiuxu Zhuo 		I10NM_SET_REG32(imc, chan, offsets_demand2[0], d2);
159cf4e6d52SYouquan Song }
160cf4e6d52SYouquan Song 
enable_retry_rd_err_log(bool enable)161cf4e6d52SYouquan Song static void enable_retry_rd_err_log(bool enable)
162cf4e6d52SYouquan Song {
163dd7814b7SQiuxu Zhuo 	int i, j, imc_num, chan_num;
164acd4cf68SQiuxu Zhuo 	struct skx_imc *imc;
165cf4e6d52SYouquan Song 	struct skx_dev *d;
166cf4e6d52SYouquan Song 
167cf4e6d52SYouquan Song 	edac_dbg(2, "\n");
168cf4e6d52SYouquan Song 
169dd7814b7SQiuxu Zhuo 	list_for_each_entry(d, i10nm_edac_list, list) {
170dd7814b7SQiuxu Zhuo 		imc_num  = res_cfg->ddr_imc_num;
171dd7814b7SQiuxu Zhuo 		chan_num = res_cfg->ddr_chan_num;
172dd7814b7SQiuxu Zhuo 
173dd7814b7SQiuxu Zhuo 		for (i = 0; i < imc_num; i++) {
174acd4cf68SQiuxu Zhuo 			imc = &d->imc[i];
175acd4cf68SQiuxu Zhuo 			if (!imc->mbase)
176acd4cf68SQiuxu Zhuo 				continue;
177acd4cf68SQiuxu Zhuo 
178dd7814b7SQiuxu Zhuo 			for (j = 0; j < chan_num; j++)
179dd7814b7SQiuxu Zhuo 				__enable_retry_rd_err_log(imc, j, enable,
180dd7814b7SQiuxu Zhuo 							  res_cfg->offsets_scrub,
181dd7814b7SQiuxu Zhuo 							  res_cfg->offsets_demand,
182dd7814b7SQiuxu Zhuo 							  res_cfg->offsets_demand2);
183dd7814b7SQiuxu Zhuo 		}
184dd7814b7SQiuxu Zhuo 
185dd7814b7SQiuxu Zhuo 		imc_num += res_cfg->hbm_imc_num;
186dd7814b7SQiuxu Zhuo 		chan_num = res_cfg->hbm_chan_num;
187dd7814b7SQiuxu Zhuo 
188dd7814b7SQiuxu Zhuo 		for (; i < imc_num; i++) {
189dd7814b7SQiuxu Zhuo 			imc = &d->imc[i];
190dd7814b7SQiuxu Zhuo 			if (!imc->mbase || !imc->hbm_mc)
191dd7814b7SQiuxu Zhuo 				continue;
192dd7814b7SQiuxu Zhuo 
193dd7814b7SQiuxu Zhuo 			for (j = 0; j < chan_num; j++) {
194acd4cf68SQiuxu Zhuo 				__enable_retry_rd_err_log(imc, j, enable,
195acd4cf68SQiuxu Zhuo 							  res_cfg->offsets_scrub_hbm0,
196d5f5e499SQiuxu Zhuo 							  res_cfg->offsets_demand_hbm0,
197d5f5e499SQiuxu Zhuo 							  NULL);
198acd4cf68SQiuxu Zhuo 				__enable_retry_rd_err_log(imc, j, enable,
199acd4cf68SQiuxu Zhuo 							  res_cfg->offsets_scrub_hbm1,
200d5f5e499SQiuxu Zhuo 							  res_cfg->offsets_demand_hbm1,
201d5f5e499SQiuxu Zhuo 							  NULL);
202acd4cf68SQiuxu Zhuo 			}
203acd4cf68SQiuxu Zhuo 		}
204acd4cf68SQiuxu Zhuo 	}
205cf4e6d52SYouquan Song }
206cf4e6d52SYouquan Song 
show_retry_rd_err_log(struct decoded_addr * res,char * msg,int len,bool scrub_err)207cf4e6d52SYouquan Song static void show_retry_rd_err_log(struct decoded_addr *res, char *msg,
208cf4e6d52SYouquan Song 				  int len, bool scrub_err)
209cf4e6d52SYouquan Song {
210cf4e6d52SYouquan Song 	struct skx_imc *imc = &res->dev->imc[res->imc];
211cf4e6d52SYouquan Song 	u32 log0, log1, log2, log3, log4;
212cf4e6d52SYouquan Song 	u32 corr0, corr1, corr2, corr3;
213d5f5e499SQiuxu Zhuo 	u32 lxg0, lxg1, lxg3, lxg4;
214d5f5e499SQiuxu Zhuo 	u32 *xffsets = NULL;
215cf4e6d52SYouquan Song 	u64 log2a, log5;
216d5f5e499SQiuxu Zhuo 	u64 lxg2a, lxg5;
217cf4e6d52SYouquan Song 	u32 *offsets;
218acd4cf68SQiuxu Zhuo 	int n, pch;
219cf4e6d52SYouquan Song 
220cf4e6d52SYouquan Song 	if (!imc->mbase)
221cf4e6d52SYouquan Song 		return;
222cf4e6d52SYouquan Song 
223acd4cf68SQiuxu Zhuo 	if (imc->hbm_mc) {
224acd4cf68SQiuxu Zhuo 		pch = res->cs & 1;
225acd4cf68SQiuxu Zhuo 
226acd4cf68SQiuxu Zhuo 		if (pch)
227acd4cf68SQiuxu Zhuo 			offsets = scrub_err ? res_cfg->offsets_scrub_hbm1 :
228acd4cf68SQiuxu Zhuo 					      res_cfg->offsets_demand_hbm1;
229acd4cf68SQiuxu Zhuo 		else
230acd4cf68SQiuxu Zhuo 			offsets = scrub_err ? res_cfg->offsets_scrub_hbm0 :
231acd4cf68SQiuxu Zhuo 					      res_cfg->offsets_demand_hbm0;
232acd4cf68SQiuxu Zhuo 	} else {
233d5f5e499SQiuxu Zhuo 		if (scrub_err) {
234d5f5e499SQiuxu Zhuo 			offsets = res_cfg->offsets_scrub;
235d5f5e499SQiuxu Zhuo 		} else {
236d5f5e499SQiuxu Zhuo 			offsets = res_cfg->offsets_demand;
237d5f5e499SQiuxu Zhuo 			xffsets = res_cfg->offsets_demand2;
238d5f5e499SQiuxu Zhuo 		}
239acd4cf68SQiuxu Zhuo 	}
240cf4e6d52SYouquan Song 
241cf4e6d52SYouquan Song 	log0 = I10NM_GET_REG32(imc, res->channel, offsets[0]);
242cf4e6d52SYouquan Song 	log1 = I10NM_GET_REG32(imc, res->channel, offsets[1]);
243cf4e6d52SYouquan Song 	log3 = I10NM_GET_REG32(imc, res->channel, offsets[3]);
244cf4e6d52SYouquan Song 	log4 = I10NM_GET_REG32(imc, res->channel, offsets[4]);
245cf4e6d52SYouquan Song 	log5 = I10NM_GET_REG64(imc, res->channel, offsets[5]);
246cf4e6d52SYouquan Song 
247d5f5e499SQiuxu Zhuo 	if (xffsets) {
248d5f5e499SQiuxu Zhuo 		lxg0 = I10NM_GET_REG32(imc, res->channel, xffsets[0]);
249d5f5e499SQiuxu Zhuo 		lxg1 = I10NM_GET_REG32(imc, res->channel, xffsets[1]);
250d5f5e499SQiuxu Zhuo 		lxg3 = I10NM_GET_REG32(imc, res->channel, xffsets[3]);
251d5f5e499SQiuxu Zhuo 		lxg4 = I10NM_GET_REG32(imc, res->channel, xffsets[4]);
252d5f5e499SQiuxu Zhuo 		lxg5 = I10NM_GET_REG64(imc, res->channel, xffsets[5]);
253d5f5e499SQiuxu Zhuo 	}
254d5f5e499SQiuxu Zhuo 
255cf4e6d52SYouquan Song 	if (res_cfg->type == SPR) {
256cf4e6d52SYouquan Song 		log2a = I10NM_GET_REG64(imc, res->channel, offsets[2]);
257d5f5e499SQiuxu Zhuo 		n = snprintf(msg, len, " retry_rd_err_log[%.8x %.8x %.16llx %.8x %.8x %.16llx",
258cf4e6d52SYouquan Song 			     log0, log1, log2a, log3, log4, log5);
259d5f5e499SQiuxu Zhuo 
260d5f5e499SQiuxu Zhuo 		if (len - n > 0) {
261d5f5e499SQiuxu Zhuo 			if (xffsets) {
262d5f5e499SQiuxu Zhuo 				lxg2a = I10NM_GET_REG64(imc, res->channel, xffsets[2]);
263d5f5e499SQiuxu Zhuo 				n += snprintf(msg + n, len - n, " %.8x %.8x %.16llx %.8x %.8x %.16llx]",
264d5f5e499SQiuxu Zhuo 					     lxg0, lxg1, lxg2a, lxg3, lxg4, lxg5);
265d5f5e499SQiuxu Zhuo 			} else {
266d5f5e499SQiuxu Zhuo 				n += snprintf(msg + n, len - n, "]");
267d5f5e499SQiuxu Zhuo 			}
268d5f5e499SQiuxu Zhuo 		}
269cf4e6d52SYouquan Song 	} else {
270cf4e6d52SYouquan Song 		log2 = I10NM_GET_REG32(imc, res->channel, offsets[2]);
271cf4e6d52SYouquan Song 		n = snprintf(msg, len, " retry_rd_err_log[%.8x %.8x %.8x %.8x %.8x %.16llx]",
272cf4e6d52SYouquan Song 			     log0, log1, log2, log3, log4, log5);
273cf4e6d52SYouquan Song 	}
274cf4e6d52SYouquan Song 
275acd4cf68SQiuxu Zhuo 	if (imc->hbm_mc) {
276acd4cf68SQiuxu Zhuo 		if (pch) {
277acd4cf68SQiuxu Zhuo 			corr0 = I10NM_GET_REG32(imc, res->channel, 0x2c18);
278acd4cf68SQiuxu Zhuo 			corr1 = I10NM_GET_REG32(imc, res->channel, 0x2c1c);
279acd4cf68SQiuxu Zhuo 			corr2 = I10NM_GET_REG32(imc, res->channel, 0x2c20);
280acd4cf68SQiuxu Zhuo 			corr3 = I10NM_GET_REG32(imc, res->channel, 0x2c24);
281acd4cf68SQiuxu Zhuo 		} else {
282acd4cf68SQiuxu Zhuo 			corr0 = I10NM_GET_REG32(imc, res->channel, 0x2818);
283acd4cf68SQiuxu Zhuo 			corr1 = I10NM_GET_REG32(imc, res->channel, 0x281c);
284acd4cf68SQiuxu Zhuo 			corr2 = I10NM_GET_REG32(imc, res->channel, 0x2820);
285acd4cf68SQiuxu Zhuo 			corr3 = I10NM_GET_REG32(imc, res->channel, 0x2824);
286acd4cf68SQiuxu Zhuo 		}
287acd4cf68SQiuxu Zhuo 	} else {
288cf4e6d52SYouquan Song 		corr0 = I10NM_GET_REG32(imc, res->channel, 0x22c18);
289cf4e6d52SYouquan Song 		corr1 = I10NM_GET_REG32(imc, res->channel, 0x22c1c);
290cf4e6d52SYouquan Song 		corr2 = I10NM_GET_REG32(imc, res->channel, 0x22c20);
291cf4e6d52SYouquan Song 		corr3 = I10NM_GET_REG32(imc, res->channel, 0x22c24);
292acd4cf68SQiuxu Zhuo 	}
293cf4e6d52SYouquan Song 
294cf4e6d52SYouquan Song 	if (len - n > 0)
295cf4e6d52SYouquan Song 		snprintf(msg + n, len - n,
296cf4e6d52SYouquan Song 			 " correrrcnt[%.4x %.4x %.4x %.4x %.4x %.4x %.4x %.4x]",
297cf4e6d52SYouquan Song 			 corr0 & 0xffff, corr0 >> 16,
298cf4e6d52SYouquan Song 			 corr1 & 0xffff, corr1 >> 16,
299cf4e6d52SYouquan Song 			 corr2 & 0xffff, corr2 >> 16,
300cf4e6d52SYouquan Song 			 corr3 & 0xffff, corr3 >> 16);
301cf4e6d52SYouquan Song 
302cf4e6d52SYouquan Song 	/* Clear status bits */
303d5f5e499SQiuxu Zhuo 	if (retry_rd_err_log == 2) {
304d5f5e499SQiuxu Zhuo 		if (log0 & RETRY_RD_ERR_LOG_OVER_UC_V) {
305cf4e6d52SYouquan Song 			log0 &= ~RETRY_RD_ERR_LOG_OVER_UC_V;
306cf4e6d52SYouquan Song 			I10NM_SET_REG32(imc, res->channel, offsets[0], log0);
307cf4e6d52SYouquan Song 		}
308d5f5e499SQiuxu Zhuo 
309d5f5e499SQiuxu Zhuo 		if (xffsets && (lxg0 & RETRY_RD_ERR_LOG_OVER_UC_V)) {
310d5f5e499SQiuxu Zhuo 			lxg0 &= ~RETRY_RD_ERR_LOG_OVER_UC_V;
311d5f5e499SQiuxu Zhuo 			I10NM_SET_REG32(imc, res->channel, xffsets[0], lxg0);
312d5f5e499SQiuxu Zhuo 		}
313d5f5e499SQiuxu Zhuo 	}
314cf4e6d52SYouquan Song }
315cf4e6d52SYouquan Song 
pci_get_dev_wrapper(int dom,unsigned int bus,unsigned int dev,unsigned int fun)316d4dc89d0SQiuxu Zhuo static struct pci_dev *pci_get_dev_wrapper(int dom, unsigned int bus,
317d4dc89d0SQiuxu Zhuo 					   unsigned int dev, unsigned int fun)
318d4dc89d0SQiuxu Zhuo {
319d4dc89d0SQiuxu Zhuo 	struct pci_dev *pdev;
320d4dc89d0SQiuxu Zhuo 
321d4dc89d0SQiuxu Zhuo 	pdev = pci_get_domain_bus_and_slot(dom, bus, PCI_DEVFN(dev, fun));
322d4dc89d0SQiuxu Zhuo 	if (!pdev) {
323d4dc89d0SQiuxu Zhuo 		edac_dbg(2, "No device %02x:%02x.%x\n",
324d4dc89d0SQiuxu Zhuo 			 bus, dev, fun);
325d4dc89d0SQiuxu Zhuo 		return NULL;
326d4dc89d0SQiuxu Zhuo 	}
327d4dc89d0SQiuxu Zhuo 
328d4dc89d0SQiuxu Zhuo 	if (unlikely(pci_enable_device(pdev) < 0)) {
329d4dc89d0SQiuxu Zhuo 		edac_dbg(2, "Failed to enable device %02x:%02x.%x\n",
330d4dc89d0SQiuxu Zhuo 			 bus, dev, fun);
3319c892155SYang Yingliang 		pci_dev_put(pdev);
332d4dc89d0SQiuxu Zhuo 		return NULL;
333d4dc89d0SQiuxu Zhuo 	}
334d4dc89d0SQiuxu Zhuo 
335d4dc89d0SQiuxu Zhuo 	return pdev;
336d4dc89d0SQiuxu Zhuo }
337d4dc89d0SQiuxu Zhuo 
338ba987eaaSQiuxu Zhuo /**
339ba987eaaSQiuxu Zhuo  * i10nm_get_imc_num() - Get the number of present DDR memory controllers.
340ba987eaaSQiuxu Zhuo  *
341ba987eaaSQiuxu Zhuo  * @cfg : The pointer to the structure of EDAC resource configurations.
342ba987eaaSQiuxu Zhuo  *
343ba987eaaSQiuxu Zhuo  * For Granite Rapids CPUs, the number of present DDR memory controllers read
344ba987eaaSQiuxu Zhuo  * at runtime overwrites the value statically configured in @cfg->ddr_imc_num.
345ba987eaaSQiuxu Zhuo  * For other CPUs, the number of present DDR memory controllers is statically
346ba987eaaSQiuxu Zhuo  * configured in @cfg->ddr_imc_num.
347ba987eaaSQiuxu Zhuo  *
348ba987eaaSQiuxu Zhuo  * RETURNS : 0 on success, < 0 on failure.
349ba987eaaSQiuxu Zhuo  */
i10nm_get_imc_num(struct res_config * cfg)350ba987eaaSQiuxu Zhuo static int i10nm_get_imc_num(struct res_config *cfg)
351ba987eaaSQiuxu Zhuo {
352ba987eaaSQiuxu Zhuo 	int n, imc_num, chan_num = 0;
353ba987eaaSQiuxu Zhuo 	struct skx_dev *d;
354ba987eaaSQiuxu Zhuo 	u32 reg;
355ba987eaaSQiuxu Zhuo 
356ba987eaaSQiuxu Zhuo 	list_for_each_entry(d, i10nm_edac_list, list) {
357ba987eaaSQiuxu Zhuo 		d->pcu_cr3 = pci_get_dev_wrapper(d->seg, d->bus[res_cfg->pcu_cr3_bdf.bus],
358ba987eaaSQiuxu Zhuo 						 res_cfg->pcu_cr3_bdf.dev,
359ba987eaaSQiuxu Zhuo 						 res_cfg->pcu_cr3_bdf.fun);
360ba987eaaSQiuxu Zhuo 		if (!d->pcu_cr3)
361ba987eaaSQiuxu Zhuo 			continue;
362ba987eaaSQiuxu Zhuo 
363ba987eaaSQiuxu Zhuo 		if (I10NM_GET_CAPID5_CFG(d, reg))
364ba987eaaSQiuxu Zhuo 			continue;
365ba987eaaSQiuxu Zhuo 
366ba987eaaSQiuxu Zhuo 		n = I10NM_DDR_IMC_CH_CNT(reg);
367ba987eaaSQiuxu Zhuo 
368ba987eaaSQiuxu Zhuo 		if (!chan_num) {
369ba987eaaSQiuxu Zhuo 			chan_num = n;
370ba987eaaSQiuxu Zhuo 			edac_dbg(2, "Get DDR CH number: %d\n", chan_num);
371ba987eaaSQiuxu Zhuo 		} else if (chan_num != n) {
372ba987eaaSQiuxu Zhuo 			i10nm_printk(KERN_NOTICE, "Get DDR CH numbers: %d, %d\n", chan_num, n);
373ba987eaaSQiuxu Zhuo 		}
374ba987eaaSQiuxu Zhuo 	}
375ba987eaaSQiuxu Zhuo 
376ba987eaaSQiuxu Zhuo 	switch (cfg->type) {
377ba987eaaSQiuxu Zhuo 	case GNR:
378ba987eaaSQiuxu Zhuo 		/*
379ba987eaaSQiuxu Zhuo 		 * One channel per DDR memory controller for Granite Rapids CPUs.
380ba987eaaSQiuxu Zhuo 		 */
381ba987eaaSQiuxu Zhuo 		imc_num = chan_num;
382ba987eaaSQiuxu Zhuo 
383ba987eaaSQiuxu Zhuo 		if (!imc_num) {
384ba987eaaSQiuxu Zhuo 			i10nm_printk(KERN_ERR, "Invalid DDR MC number\n");
385ba987eaaSQiuxu Zhuo 			return -ENODEV;
386ba987eaaSQiuxu Zhuo 		}
387ba987eaaSQiuxu Zhuo 
388ba987eaaSQiuxu Zhuo 		if (imc_num > I10NM_NUM_DDR_IMC) {
389ba987eaaSQiuxu Zhuo 			i10nm_printk(KERN_ERR, "Need to make I10NM_NUM_DDR_IMC >= %d\n", imc_num);
390ba987eaaSQiuxu Zhuo 			return -EINVAL;
391ba987eaaSQiuxu Zhuo 		}
392ba987eaaSQiuxu Zhuo 
393ba987eaaSQiuxu Zhuo 		if (cfg->ddr_imc_num != imc_num) {
394ba987eaaSQiuxu Zhuo 			/*
395ba987eaaSQiuxu Zhuo 			 * Store the number of present DDR memory controllers.
396ba987eaaSQiuxu Zhuo 			 */
397ba987eaaSQiuxu Zhuo 			cfg->ddr_imc_num = imc_num;
398ba987eaaSQiuxu Zhuo 			edac_dbg(2, "Set DDR MC number: %d", imc_num);
399ba987eaaSQiuxu Zhuo 		}
400ba987eaaSQiuxu Zhuo 
401ba987eaaSQiuxu Zhuo 		return 0;
402ba987eaaSQiuxu Zhuo 	default:
403ba987eaaSQiuxu Zhuo 		/*
404ba987eaaSQiuxu Zhuo 		 * For other CPUs, the number of present DDR memory controllers
405ba987eaaSQiuxu Zhuo 		 * is statically pre-configured in cfg->ddr_imc_num.
406ba987eaaSQiuxu Zhuo 		 */
407ba987eaaSQiuxu Zhuo 		return 0;
408ba987eaaSQiuxu Zhuo 	}
409ba987eaaSQiuxu Zhuo }
410ba987eaaSQiuxu Zhuo 
i10nm_check_2lm(struct res_config * cfg)4114bd4d32eSQiuxu Zhuo static bool i10nm_check_2lm(struct res_config *cfg)
4124bd4d32eSQiuxu Zhuo {
4134bd4d32eSQiuxu Zhuo 	struct skx_dev *d;
4144bd4d32eSQiuxu Zhuo 	u32 reg;
4154bd4d32eSQiuxu Zhuo 	int i;
4164bd4d32eSQiuxu Zhuo 
4174bd4d32eSQiuxu Zhuo 	list_for_each_entry(d, i10nm_edac_list, list) {
418dd7814b7SQiuxu Zhuo 		d->sad_all = pci_get_dev_wrapper(d->seg, d->bus[res_cfg->sad_all_bdf.bus],
419dd7814b7SQiuxu Zhuo 						 res_cfg->sad_all_bdf.dev,
420dd7814b7SQiuxu Zhuo 						 res_cfg->sad_all_bdf.fun);
4214bd4d32eSQiuxu Zhuo 		if (!d->sad_all)
4224bd4d32eSQiuxu Zhuo 			continue;
4234bd4d32eSQiuxu Zhuo 
4244bd4d32eSQiuxu Zhuo 		for (i = 0; i < I10NM_MAX_SAD; i++) {
4254bd4d32eSQiuxu Zhuo 			I10NM_GET_SAD(d, cfg->sad_all_offset, i, reg);
4264bd4d32eSQiuxu Zhuo 			if (I10NM_SAD_ENABLE(reg) && I10NM_SAD_NM_CACHEABLE(reg)) {
4274bd4d32eSQiuxu Zhuo 				edac_dbg(2, "2-level memory configuration.\n");
4284bd4d32eSQiuxu Zhuo 				return true;
4294bd4d32eSQiuxu Zhuo 			}
4304bd4d32eSQiuxu Zhuo 		}
4314bd4d32eSQiuxu Zhuo 	}
4324bd4d32eSQiuxu Zhuo 
4334bd4d32eSQiuxu Zhuo 	return false;
4344bd4d32eSQiuxu Zhuo }
4354bd4d32eSQiuxu Zhuo 
4362738c69aSYouquan Song /*
437221aa03fSYouquan Song  * Check whether the error comes from DDRT by ICX/Tremont/SPR model specific error code.
438221aa03fSYouquan Song  * Refer to SDM vol3B 17.11.3/17.13.2 Intel IMC MC error codes for IA32_MCi_STATUS.
4392738c69aSYouquan Song  */
i10nm_mscod_is_ddrt(u32 mscod)4402738c69aSYouquan Song static bool i10nm_mscod_is_ddrt(u32 mscod)
4412738c69aSYouquan Song {
442221aa03fSYouquan Song 	switch (res_cfg->type) {
443221aa03fSYouquan Song 	case I10NM:
4442738c69aSYouquan Song 		switch (mscod) {
4452738c69aSYouquan Song 		case 0x0106: case 0x0107:
4462738c69aSYouquan Song 		case 0x0800: case 0x0804:
4472738c69aSYouquan Song 		case 0x0806 ... 0x0808:
4482738c69aSYouquan Song 		case 0x080a ... 0x080e:
4492738c69aSYouquan Song 		case 0x0810: case 0x0811:
4502738c69aSYouquan Song 		case 0x0816: case 0x081e:
4512738c69aSYouquan Song 		case 0x081f:
4522738c69aSYouquan Song 			return true;
4532738c69aSYouquan Song 		}
4542738c69aSYouquan Song 
455221aa03fSYouquan Song 		break;
456221aa03fSYouquan Song 	case SPR:
457221aa03fSYouquan Song 		switch (mscod) {
458221aa03fSYouquan Song 		case 0x0800: case 0x0804:
459221aa03fSYouquan Song 		case 0x0806 ... 0x0808:
460221aa03fSYouquan Song 		case 0x080a ... 0x080e:
461221aa03fSYouquan Song 		case 0x0810: case 0x0811:
462221aa03fSYouquan Song 		case 0x0816: case 0x081e:
463221aa03fSYouquan Song 		case 0x081f:
464221aa03fSYouquan Song 			return true;
465221aa03fSYouquan Song 		}
466221aa03fSYouquan Song 
467221aa03fSYouquan Song 		break;
468221aa03fSYouquan Song 	default:
469221aa03fSYouquan Song 		return false;
470221aa03fSYouquan Song 	}
471221aa03fSYouquan Song 
4722738c69aSYouquan Song 	return false;
4732738c69aSYouquan Song }
4742738c69aSYouquan Song 
i10nm_mc_decode_available(struct mce * mce)4752738c69aSYouquan Song static bool i10nm_mc_decode_available(struct mce *mce)
4762738c69aSYouquan Song {
477221aa03fSYouquan Song #define ICX_IMCx_CHy		0x06666000
4782738c69aSYouquan Song 	u8 bank;
4792738c69aSYouquan Song 
4802738c69aSYouquan Song 	if (!decoding_via_mca || mem_cfg_2lm)
4812738c69aSYouquan Song 		return false;
4822738c69aSYouquan Song 
4832738c69aSYouquan Song 	if ((mce->status & (MCI_STATUS_MISCV | MCI_STATUS_ADDRV))
4842738c69aSYouquan Song 			!= (MCI_STATUS_MISCV | MCI_STATUS_ADDRV))
4852738c69aSYouquan Song 		return false;
4862738c69aSYouquan Song 
4872738c69aSYouquan Song 	bank = mce->bank;
4882738c69aSYouquan Song 
4892738c69aSYouquan Song 	switch (res_cfg->type) {
4902738c69aSYouquan Song 	case I10NM:
491221aa03fSYouquan Song 		/* Check whether the bank is one of {13,14,17,18,21,22,25,26} */
492221aa03fSYouquan Song 		if (!(ICX_IMCx_CHy & (1 << bank)))
4932738c69aSYouquan Song 			return false;
494221aa03fSYouquan Song 		break;
495221aa03fSYouquan Song 	case SPR:
496221aa03fSYouquan Song 		if (bank < 13 || bank > 20)
497221aa03fSYouquan Song 			return false;
498221aa03fSYouquan Song 		break;
499221aa03fSYouquan Song 	default:
500221aa03fSYouquan Song 		return false;
501221aa03fSYouquan Song 	}
5022738c69aSYouquan Song 
5032738c69aSYouquan Song 	/* DDRT errors can't be decoded from MCA bank registers */
5042738c69aSYouquan Song 	if (MCI_MISC_ECC_MODE(mce->misc) == MCI_MISC_ECC_DDRT)
5052738c69aSYouquan Song 		return false;
5062738c69aSYouquan Song 
5072738c69aSYouquan Song 	if (i10nm_mscod_is_ddrt(MCI_STATUS_MSCOD(mce->status)))
5082738c69aSYouquan Song 		return false;
5092738c69aSYouquan Song 
510221aa03fSYouquan Song 	return true;
5112738c69aSYouquan Song }
5122738c69aSYouquan Song 
i10nm_mc_decode(struct decoded_addr * res)5132738c69aSYouquan Song static bool i10nm_mc_decode(struct decoded_addr *res)
5142738c69aSYouquan Song {
5152738c69aSYouquan Song 	struct mce *m = res->mce;
5162738c69aSYouquan Song 	struct skx_dev *d;
5172738c69aSYouquan Song 	u8 bank;
5182738c69aSYouquan Song 
5192738c69aSYouquan Song 	if (!i10nm_mc_decode_available(m))
5202738c69aSYouquan Song 		return false;
5212738c69aSYouquan Song 
5222738c69aSYouquan Song 	list_for_each_entry(d, i10nm_edac_list, list) {
5232738c69aSYouquan Song 		if (d->imc[0].src_id == m->socketid) {
5242738c69aSYouquan Song 			res->socket = m->socketid;
5252738c69aSYouquan Song 			res->dev = d;
5262738c69aSYouquan Song 			break;
5272738c69aSYouquan Song 		}
5282738c69aSYouquan Song 	}
5292738c69aSYouquan Song 
5302738c69aSYouquan Song 	switch (res_cfg->type) {
5312738c69aSYouquan Song 	case I10NM:
5322738c69aSYouquan Song 		bank              = m->bank - 13;
5332738c69aSYouquan Song 		res->imc          = bank / 4;
5342738c69aSYouquan Song 		res->channel      = bank % 2;
535221aa03fSYouquan Song 		res->column       = GET_BITFIELD(m->misc, 9, 18) << 2;
536221aa03fSYouquan Song 		res->row          = GET_BITFIELD(m->misc, 19, 39);
537221aa03fSYouquan Song 		res->bank_group   = GET_BITFIELD(m->misc, 40, 41);
538221aa03fSYouquan Song 		res->bank_address = GET_BITFIELD(m->misc, 42, 43);
539221aa03fSYouquan Song 		res->bank_group  |= GET_BITFIELD(m->misc, 44, 44) << 2;
540221aa03fSYouquan Song 		res->rank         = GET_BITFIELD(m->misc, 56, 58);
541221aa03fSYouquan Song 		res->dimm         = res->rank >> 2;
542221aa03fSYouquan Song 		res->rank         = res->rank % 4;
543221aa03fSYouquan Song 		break;
544221aa03fSYouquan Song 	case SPR:
545221aa03fSYouquan Song 		bank              = m->bank - 13;
546221aa03fSYouquan Song 		res->imc          = bank / 2;
547221aa03fSYouquan Song 		res->channel      = bank % 2;
548221aa03fSYouquan Song 		res->column       = GET_BITFIELD(m->misc, 9, 18) << 2;
549221aa03fSYouquan Song 		res->row          = GET_BITFIELD(m->misc, 19, 36);
550221aa03fSYouquan Song 		res->bank_group   = GET_BITFIELD(m->misc, 37, 38);
551221aa03fSYouquan Song 		res->bank_address = GET_BITFIELD(m->misc, 39, 40);
552221aa03fSYouquan Song 		res->bank_group  |= GET_BITFIELD(m->misc, 41, 41) << 2;
553221aa03fSYouquan Song 		res->rank         = GET_BITFIELD(m->misc, 57, 57);
554221aa03fSYouquan Song 		res->dimm         = GET_BITFIELD(m->misc, 58, 58);
5552738c69aSYouquan Song 		break;
5562738c69aSYouquan Song 	default:
5572738c69aSYouquan Song 		return false;
5582738c69aSYouquan Song 	}
5592738c69aSYouquan Song 
5602738c69aSYouquan Song 	if (!res->dev) {
5612738c69aSYouquan Song 		skx_printk(KERN_ERR, "No device for src_id %d imc %d\n",
5622738c69aSYouquan Song 			   m->socketid, res->imc);
5632738c69aSYouquan Song 		return false;
5642738c69aSYouquan Song 	}
5652738c69aSYouquan Song 
5662738c69aSYouquan Song 	return true;
5672738c69aSYouquan Song }
5682738c69aSYouquan Song 
569ba987eaaSQiuxu Zhuo /**
570ba987eaaSQiuxu Zhuo  * get_gnr_mdev() - Get the PCI device of the @logical_idx-th DDR memory controller.
571ba987eaaSQiuxu Zhuo  *
572ba987eaaSQiuxu Zhuo  * @d            : The pointer to the structure of CPU socket EDAC device.
573ba987eaaSQiuxu Zhuo  * @logical_idx  : The logical index of the present memory controller (0 ~ max present MC# - 1).
574ba987eaaSQiuxu Zhuo  * @physical_idx : To store the corresponding physical index of @logical_idx.
575ba987eaaSQiuxu Zhuo  *
576ba987eaaSQiuxu Zhuo  * RETURNS       : The PCI device of the @logical_idx-th DDR memory controller, NULL on failure.
577ba987eaaSQiuxu Zhuo  */
get_gnr_mdev(struct skx_dev * d,int logical_idx,int * physical_idx)578ba987eaaSQiuxu Zhuo static struct pci_dev *get_gnr_mdev(struct skx_dev *d, int logical_idx, int *physical_idx)
579ba987eaaSQiuxu Zhuo {
580ba987eaaSQiuxu Zhuo #define GNR_MAX_IMC_PCI_CNT	28
581ba987eaaSQiuxu Zhuo 
582ba987eaaSQiuxu Zhuo 	struct pci_dev *mdev;
583ba987eaaSQiuxu Zhuo 	int i, logical = 0;
584ba987eaaSQiuxu Zhuo 
585ba987eaaSQiuxu Zhuo 	/*
586ba987eaaSQiuxu Zhuo 	 * Detect present memory controllers from { PCI device: 8-5, function 7-1 }
587ba987eaaSQiuxu Zhuo 	 */
588ba987eaaSQiuxu Zhuo 	for (i = 0; i < GNR_MAX_IMC_PCI_CNT; i++) {
589ba987eaaSQiuxu Zhuo 		mdev = pci_get_dev_wrapper(d->seg,
590ba987eaaSQiuxu Zhuo 					   d->bus[res_cfg->ddr_mdev_bdf.bus],
591ba987eaaSQiuxu Zhuo 					   res_cfg->ddr_mdev_bdf.dev + i / 7,
592ba987eaaSQiuxu Zhuo 					   res_cfg->ddr_mdev_bdf.fun + i % 7);
593ba987eaaSQiuxu Zhuo 
594ba987eaaSQiuxu Zhuo 		if (mdev) {
595ba987eaaSQiuxu Zhuo 			if (logical == logical_idx) {
596ba987eaaSQiuxu Zhuo 				*physical_idx = i;
597ba987eaaSQiuxu Zhuo 				return mdev;
598ba987eaaSQiuxu Zhuo 			}
599ba987eaaSQiuxu Zhuo 
600ba987eaaSQiuxu Zhuo 			pci_dev_put(mdev);
601ba987eaaSQiuxu Zhuo 			logical++;
602ba987eaaSQiuxu Zhuo 		}
603ba987eaaSQiuxu Zhuo 	}
604ba987eaaSQiuxu Zhuo 
605ba987eaaSQiuxu Zhuo 	return NULL;
606ba987eaaSQiuxu Zhuo }
607ba987eaaSQiuxu Zhuo 
608ba987eaaSQiuxu Zhuo /**
609ba987eaaSQiuxu Zhuo  * get_ddr_munit() - Get the resource of the i-th DDR memory controller.
610ba987eaaSQiuxu Zhuo  *
611ba987eaaSQiuxu Zhuo  * @d      : The pointer to the structure of CPU socket EDAC device.
612ba987eaaSQiuxu Zhuo  * @i      : The index of the CPU socket relative DDR memory controller.
613ba987eaaSQiuxu Zhuo  * @offset : To store the MMIO offset of the i-th DDR memory controller.
614ba987eaaSQiuxu Zhuo  * @size   : To store the MMIO size of the i-th DDR memory controller.
615ba987eaaSQiuxu Zhuo  *
616ba987eaaSQiuxu Zhuo  * RETURNS : The PCI device of the i-th DDR memory controller, NULL on failure.
617ba987eaaSQiuxu Zhuo  */
get_ddr_munit(struct skx_dev * d,int i,u32 * offset,unsigned long * size)618ba987eaaSQiuxu Zhuo static struct pci_dev *get_ddr_munit(struct skx_dev *d, int i, u32 *offset, unsigned long *size)
619ba987eaaSQiuxu Zhuo {
620ba987eaaSQiuxu Zhuo 	struct pci_dev *mdev;
621ba987eaaSQiuxu Zhuo 	int physical_idx;
622ba987eaaSQiuxu Zhuo 	u32 reg;
623ba987eaaSQiuxu Zhuo 
624ba987eaaSQiuxu Zhuo 	switch (res_cfg->type) {
625ba987eaaSQiuxu Zhuo 	case GNR:
626ba987eaaSQiuxu Zhuo 		if (I10NM_GET_IMC_BAR(d, 0, reg)) {
627ba987eaaSQiuxu Zhuo 			i10nm_printk(KERN_ERR, "Failed to get mc0 bar\n");
628ba987eaaSQiuxu Zhuo 			return NULL;
629ba987eaaSQiuxu Zhuo 		}
630ba987eaaSQiuxu Zhuo 
631ba987eaaSQiuxu Zhuo 		mdev = get_gnr_mdev(d, i, &physical_idx);
632ba987eaaSQiuxu Zhuo 		if (!mdev)
633ba987eaaSQiuxu Zhuo 			return NULL;
634ba987eaaSQiuxu Zhuo 
635ba987eaaSQiuxu Zhuo 		*offset = I10NM_GET_IMC_MMIO_OFFSET(reg) +
636ba987eaaSQiuxu Zhuo 			  I10NM_GNR_IMC_MMIO_OFFSET +
637ba987eaaSQiuxu Zhuo 			  physical_idx * I10NM_GNR_IMC_MMIO_SIZE;
638ba987eaaSQiuxu Zhuo 		*size   = I10NM_GNR_IMC_MMIO_SIZE;
639ba987eaaSQiuxu Zhuo 
640ba987eaaSQiuxu Zhuo 		break;
641ba987eaaSQiuxu Zhuo 	default:
642ba987eaaSQiuxu Zhuo 		if (I10NM_GET_IMC_BAR(d, i, reg)) {
643ba987eaaSQiuxu Zhuo 			i10nm_printk(KERN_ERR, "Failed to get mc%d bar\n", i);
644ba987eaaSQiuxu Zhuo 			return NULL;
645ba987eaaSQiuxu Zhuo 		}
646ba987eaaSQiuxu Zhuo 
647ba987eaaSQiuxu Zhuo 		mdev = pci_get_dev_wrapper(d->seg,
648ba987eaaSQiuxu Zhuo 					   d->bus[res_cfg->ddr_mdev_bdf.bus],
649ba987eaaSQiuxu Zhuo 					   res_cfg->ddr_mdev_bdf.dev + i,
650ba987eaaSQiuxu Zhuo 					   res_cfg->ddr_mdev_bdf.fun);
651ba987eaaSQiuxu Zhuo 		if (!mdev)
652ba987eaaSQiuxu Zhuo 			return NULL;
653ba987eaaSQiuxu Zhuo 
654ba987eaaSQiuxu Zhuo 		*offset  = I10NM_GET_IMC_MMIO_OFFSET(reg);
655ba987eaaSQiuxu Zhuo 		*size    = I10NM_GET_IMC_MMIO_SIZE(reg);
656ba987eaaSQiuxu Zhuo 	}
657ba987eaaSQiuxu Zhuo 
658ba987eaaSQiuxu Zhuo 	return mdev;
659ba987eaaSQiuxu Zhuo }
660ba987eaaSQiuxu Zhuo 
661c545f5e4SQiuxu Zhuo /**
662c545f5e4SQiuxu Zhuo  * i10nm_imc_absent() - Check whether the memory controller @imc is absent
663c545f5e4SQiuxu Zhuo  *
664c545f5e4SQiuxu Zhuo  * @imc    : The pointer to the structure of memory controller EDAC device.
665c545f5e4SQiuxu Zhuo  *
666c545f5e4SQiuxu Zhuo  * RETURNS : true if the memory controller EDAC device is absent, false otherwise.
667c545f5e4SQiuxu Zhuo  */
i10nm_imc_absent(struct skx_imc * imc)668c545f5e4SQiuxu Zhuo static bool i10nm_imc_absent(struct skx_imc *imc)
669c545f5e4SQiuxu Zhuo {
670c545f5e4SQiuxu Zhuo 	u32 mcmtr;
671c545f5e4SQiuxu Zhuo 	int i;
672c545f5e4SQiuxu Zhuo 
673c545f5e4SQiuxu Zhuo 	switch (res_cfg->type) {
674c545f5e4SQiuxu Zhuo 	case SPR:
675c545f5e4SQiuxu Zhuo 		for (i = 0; i < res_cfg->ddr_chan_num; i++) {
676c545f5e4SQiuxu Zhuo 			mcmtr = I10NM_GET_MCMTR(imc, i);
677c545f5e4SQiuxu Zhuo 			edac_dbg(1, "ch%d mcmtr reg %x\n", i, mcmtr);
678c545f5e4SQiuxu Zhuo 			if (mcmtr != ~0)
679c545f5e4SQiuxu Zhuo 				return false;
680c545f5e4SQiuxu Zhuo 		}
681c545f5e4SQiuxu Zhuo 
682c545f5e4SQiuxu Zhuo 		/*
683c545f5e4SQiuxu Zhuo 		 * Some workstations' absent memory controllers still
684c545f5e4SQiuxu Zhuo 		 * appear as PCIe devices, misleading the EDAC driver.
685c545f5e4SQiuxu Zhuo 		 * By observing that the MMIO registers of these absent
686c545f5e4SQiuxu Zhuo 		 * memory controllers consistently hold the value of ~0.
687c545f5e4SQiuxu Zhuo 		 *
688c545f5e4SQiuxu Zhuo 		 * We identify a memory controller as absent by checking
689c545f5e4SQiuxu Zhuo 		 * if its MMIO register "mcmtr" == ~0 in all its channels.
690c545f5e4SQiuxu Zhuo 		 */
691c545f5e4SQiuxu Zhuo 		return true;
692c545f5e4SQiuxu Zhuo 	default:
693c545f5e4SQiuxu Zhuo 		return false;
694c545f5e4SQiuxu Zhuo 	}
695c545f5e4SQiuxu Zhuo }
696c545f5e4SQiuxu Zhuo 
i10nm_get_ddr_munits(void)697c9450883SQiuxu Zhuo static int i10nm_get_ddr_munits(void)
698d4dc89d0SQiuxu Zhuo {
699d4dc89d0SQiuxu Zhuo 	struct pci_dev *mdev;
700d4dc89d0SQiuxu Zhuo 	void __iomem *mbase;
701d4dc89d0SQiuxu Zhuo 	unsigned long size;
702d4dc89d0SQiuxu Zhuo 	struct skx_dev *d;
703c545f5e4SQiuxu Zhuo 	int i, lmc, j = 0;
704d4dc89d0SQiuxu Zhuo 	u32 reg, off;
705d4dc89d0SQiuxu Zhuo 	u64 base;
706d4dc89d0SQiuxu Zhuo 
707d4dc89d0SQiuxu Zhuo 	list_for_each_entry(d, i10nm_edac_list, list) {
708dd7814b7SQiuxu Zhuo 		d->util_all = pci_get_dev_wrapper(d->seg, d->bus[res_cfg->util_all_bdf.bus],
709dd7814b7SQiuxu Zhuo 						  res_cfg->util_all_bdf.dev,
710dd7814b7SQiuxu Zhuo 						  res_cfg->util_all_bdf.fun);
711d4dc89d0SQiuxu Zhuo 		if (!d->util_all)
712d4dc89d0SQiuxu Zhuo 			return -ENODEV;
713d4dc89d0SQiuxu Zhuo 
714dd7814b7SQiuxu Zhuo 		d->uracu = pci_get_dev_wrapper(d->seg, d->bus[res_cfg->uracu_bdf.bus],
715dd7814b7SQiuxu Zhuo 					       res_cfg->uracu_bdf.dev,
716dd7814b7SQiuxu Zhuo 					       res_cfg->uracu_bdf.fun);
717d4dc89d0SQiuxu Zhuo 		if (!d->uracu)
718d4dc89d0SQiuxu Zhuo 			return -ENODEV;
719d4dc89d0SQiuxu Zhuo 
720d4dc89d0SQiuxu Zhuo 		if (I10NM_GET_SCK_BAR(d, reg)) {
721d4dc89d0SQiuxu Zhuo 			i10nm_printk(KERN_ERR, "Failed to socket bar\n");
722d4dc89d0SQiuxu Zhuo 			return -ENODEV;
723d4dc89d0SQiuxu Zhuo 		}
724d4dc89d0SQiuxu Zhuo 
725d4dc89d0SQiuxu Zhuo 		base = I10NM_GET_SCK_MMIO_BASE(reg);
726d4dc89d0SQiuxu Zhuo 		edac_dbg(2, "socket%d mmio base 0x%llx (reg 0x%x)\n",
727d4dc89d0SQiuxu Zhuo 			 j++, base, reg);
728d4dc89d0SQiuxu Zhuo 
729c545f5e4SQiuxu Zhuo 		for (lmc = 0, i = 0; i < res_cfg->ddr_imc_num; i++) {
730ba987eaaSQiuxu Zhuo 			mdev = get_ddr_munit(d, i, &off, &size);
731ba987eaaSQiuxu Zhuo 
732d4dc89d0SQiuxu Zhuo 			if (i == 0 && !mdev) {
733d4dc89d0SQiuxu Zhuo 				i10nm_printk(KERN_ERR, "No IMC found\n");
734d4dc89d0SQiuxu Zhuo 				return -ENODEV;
735d4dc89d0SQiuxu Zhuo 			}
736d4dc89d0SQiuxu Zhuo 			if (!mdev)
737d4dc89d0SQiuxu Zhuo 				continue;
738d4dc89d0SQiuxu Zhuo 
739d4dc89d0SQiuxu Zhuo 			edac_dbg(2, "mc%d mmio base 0x%llx size 0x%lx (reg 0x%x)\n",
740d4dc89d0SQiuxu Zhuo 				 i, base + off, size, reg);
741d4dc89d0SQiuxu Zhuo 
742d4dc89d0SQiuxu Zhuo 			mbase = ioremap(base + off, size);
743d4dc89d0SQiuxu Zhuo 			if (!mbase) {
744d4dc89d0SQiuxu Zhuo 				i10nm_printk(KERN_ERR, "Failed to ioremap 0x%llx\n",
745d4dc89d0SQiuxu Zhuo 					     base + off);
746d4dc89d0SQiuxu Zhuo 				return -ENODEV;
747d4dc89d0SQiuxu Zhuo 			}
748d4dc89d0SQiuxu Zhuo 
749c545f5e4SQiuxu Zhuo 			d->imc[lmc].mbase = mbase;
750c545f5e4SQiuxu Zhuo 			if (i10nm_imc_absent(&d->imc[lmc])) {
751c545f5e4SQiuxu Zhuo 				pci_dev_put(mdev);
752c545f5e4SQiuxu Zhuo 				iounmap(mbase);
753c545f5e4SQiuxu Zhuo 				d->imc[lmc].mbase = NULL;
754c545f5e4SQiuxu Zhuo 				edac_dbg(2, "Skip absent mc%d\n", i);
755c545f5e4SQiuxu Zhuo 				continue;
756c545f5e4SQiuxu Zhuo 			} else {
757c545f5e4SQiuxu Zhuo 				d->imc[lmc].mdev = mdev;
758c545f5e4SQiuxu Zhuo 				lmc++;
759c545f5e4SQiuxu Zhuo 			}
760d4dc89d0SQiuxu Zhuo 		}
761d4dc89d0SQiuxu Zhuo 	}
762d4dc89d0SQiuxu Zhuo 
763d4dc89d0SQiuxu Zhuo 	return 0;
764d4dc89d0SQiuxu Zhuo }
765d4dc89d0SQiuxu Zhuo 
i10nm_check_hbm_imc(struct skx_dev * d)766c9450883SQiuxu Zhuo static bool i10nm_check_hbm_imc(struct skx_dev *d)
767c9450883SQiuxu Zhuo {
768c9450883SQiuxu Zhuo 	u32 reg;
769c9450883SQiuxu Zhuo 
770c9450883SQiuxu Zhuo 	if (I10NM_GET_CAPID3_CFG(d, reg)) {
771c9450883SQiuxu Zhuo 		i10nm_printk(KERN_ERR, "Failed to get capid3_cfg\n");
772c9450883SQiuxu Zhuo 		return false;
773c9450883SQiuxu Zhuo 	}
774c9450883SQiuxu Zhuo 
775c9450883SQiuxu Zhuo 	return I10NM_IS_HBM_PRESENT(reg) != 0;
776c9450883SQiuxu Zhuo }
777c9450883SQiuxu Zhuo 
i10nm_get_hbm_munits(void)778c9450883SQiuxu Zhuo static int i10nm_get_hbm_munits(void)
779c9450883SQiuxu Zhuo {
780c9450883SQiuxu Zhuo 	struct pci_dev *mdev;
781c9450883SQiuxu Zhuo 	void __iomem *mbase;
782c9450883SQiuxu Zhuo 	u32 reg, off, mcmtr;
783c9450883SQiuxu Zhuo 	struct skx_dev *d;
784c9450883SQiuxu Zhuo 	int i, lmc;
785c9450883SQiuxu Zhuo 	u64 base;
786c9450883SQiuxu Zhuo 
787c9450883SQiuxu Zhuo 	list_for_each_entry(d, i10nm_edac_list, list) {
788c9450883SQiuxu Zhuo 		if (!d->pcu_cr3)
789c9450883SQiuxu Zhuo 			return -ENODEV;
790c9450883SQiuxu Zhuo 
791c9450883SQiuxu Zhuo 		if (!i10nm_check_hbm_imc(d)) {
792c9450883SQiuxu Zhuo 			i10nm_printk(KERN_DEBUG, "No hbm memory\n");
793c9450883SQiuxu Zhuo 			return -ENODEV;
794c9450883SQiuxu Zhuo 		}
795c9450883SQiuxu Zhuo 
796c9450883SQiuxu Zhuo 		if (I10NM_GET_SCK_BAR(d, reg)) {
797c9450883SQiuxu Zhuo 			i10nm_printk(KERN_ERR, "Failed to get socket bar\n");
798c9450883SQiuxu Zhuo 			return -ENODEV;
799c9450883SQiuxu Zhuo 		}
800c9450883SQiuxu Zhuo 		base = I10NM_GET_SCK_MMIO_BASE(reg);
801c9450883SQiuxu Zhuo 
802c9450883SQiuxu Zhuo 		if (I10NM_GET_HBM_IMC_BAR(d, reg)) {
803c9450883SQiuxu Zhuo 			i10nm_printk(KERN_ERR, "Failed to get hbm mc bar\n");
804c9450883SQiuxu Zhuo 			return -ENODEV;
805c9450883SQiuxu Zhuo 		}
806c9450883SQiuxu Zhuo 		base += I10NM_GET_HBM_IMC_MMIO_OFFSET(reg);
807c9450883SQiuxu Zhuo 
808dd7814b7SQiuxu Zhuo 		lmc = res_cfg->ddr_imc_num;
809c9450883SQiuxu Zhuo 
810dd7814b7SQiuxu Zhuo 		for (i = 0; i < res_cfg->hbm_imc_num; i++) {
811dd7814b7SQiuxu Zhuo 			mdev = pci_get_dev_wrapper(d->seg, d->bus[res_cfg->hbm_mdev_bdf.bus],
812dd7814b7SQiuxu Zhuo 						   res_cfg->hbm_mdev_bdf.dev + i / 4,
813dd7814b7SQiuxu Zhuo 						   res_cfg->hbm_mdev_bdf.fun + i % 4);
814dd7814b7SQiuxu Zhuo 
815c9450883SQiuxu Zhuo 			if (i == 0 && !mdev) {
816c9450883SQiuxu Zhuo 				i10nm_printk(KERN_ERR, "No hbm mc found\n");
817c9450883SQiuxu Zhuo 				return -ENODEV;
818c9450883SQiuxu Zhuo 			}
819c9450883SQiuxu Zhuo 			if (!mdev)
820c9450883SQiuxu Zhuo 				continue;
821c9450883SQiuxu Zhuo 
822c9450883SQiuxu Zhuo 			d->imc[lmc].mdev = mdev;
823c9450883SQiuxu Zhuo 			off = i * I10NM_HBM_IMC_MMIO_SIZE;
824c9450883SQiuxu Zhuo 
825c9450883SQiuxu Zhuo 			edac_dbg(2, "hbm mc%d mmio base 0x%llx size 0x%x\n",
826c9450883SQiuxu Zhuo 				 lmc, base + off, I10NM_HBM_IMC_MMIO_SIZE);
827c9450883SQiuxu Zhuo 
828c9450883SQiuxu Zhuo 			mbase = ioremap(base + off, I10NM_HBM_IMC_MMIO_SIZE);
829c9450883SQiuxu Zhuo 			if (!mbase) {
830c370baa3SQiuxu Zhuo 				pci_dev_put(d->imc[lmc].mdev);
831c370baa3SQiuxu Zhuo 				d->imc[lmc].mdev = NULL;
832c370baa3SQiuxu Zhuo 
833c9450883SQiuxu Zhuo 				i10nm_printk(KERN_ERR, "Failed to ioremap for hbm mc 0x%llx\n",
834c9450883SQiuxu Zhuo 					     base + off);
835c9450883SQiuxu Zhuo 				return -ENOMEM;
836c9450883SQiuxu Zhuo 			}
837c9450883SQiuxu Zhuo 
838c9450883SQiuxu Zhuo 			d->imc[lmc].mbase = mbase;
839c9450883SQiuxu Zhuo 			d->imc[lmc].hbm_mc = true;
840c9450883SQiuxu Zhuo 
841c9450883SQiuxu Zhuo 			mcmtr = I10NM_GET_MCMTR(&d->imc[lmc], 0);
842c9450883SQiuxu Zhuo 			if (!I10NM_IS_HBM_IMC(mcmtr)) {
843c370baa3SQiuxu Zhuo 				iounmap(d->imc[lmc].mbase);
844c370baa3SQiuxu Zhuo 				d->imc[lmc].mbase = NULL;
845c370baa3SQiuxu Zhuo 				d->imc[lmc].hbm_mc = false;
846c370baa3SQiuxu Zhuo 				pci_dev_put(d->imc[lmc].mdev);
847c370baa3SQiuxu Zhuo 				d->imc[lmc].mdev = NULL;
848c370baa3SQiuxu Zhuo 
849c9450883SQiuxu Zhuo 				i10nm_printk(KERN_ERR, "This isn't an hbm mc!\n");
850c9450883SQiuxu Zhuo 				return -ENODEV;
851c9450883SQiuxu Zhuo 			}
852c9450883SQiuxu Zhuo 
853c9450883SQiuxu Zhuo 			lmc++;
854c9450883SQiuxu Zhuo 		}
855c9450883SQiuxu Zhuo 	}
856c9450883SQiuxu Zhuo 
857c9450883SQiuxu Zhuo 	return 0;
858c9450883SQiuxu Zhuo }
859c9450883SQiuxu Zhuo 
860ce206708SQiuxu Zhuo static struct res_config i10nm_cfg0 = {
861ee5340abSQiuxu Zhuo 	.type			= I10NM,
862ee5340abSQiuxu Zhuo 	.decs_did		= 0x3452,
863ee5340abSQiuxu Zhuo 	.busno_cfg_offset	= 0xcc,
864dd7814b7SQiuxu Zhuo 	.ddr_imc_num		= 4,
865dd7814b7SQiuxu Zhuo 	.ddr_chan_num		= 2,
866dd7814b7SQiuxu Zhuo 	.ddr_dimm_num		= 2,
867479f58ddSQiuxu Zhuo 	.ddr_chan_mmio_sz	= 0x4000,
868dd7814b7SQiuxu Zhuo 	.sad_all_bdf		= {1, 29, 0},
869dd7814b7SQiuxu Zhuo 	.pcu_cr3_bdf		= {1, 30, 3},
870dd7814b7SQiuxu Zhuo 	.util_all_bdf		= {1, 29, 1},
871dd7814b7SQiuxu Zhuo 	.uracu_bdf		= {0, 0, 1},
872dd7814b7SQiuxu Zhuo 	.ddr_mdev_bdf		= {0, 12, 0},
873dd7814b7SQiuxu Zhuo 	.hbm_mdev_bdf		= {0, 12, 1},
8744bd4d32eSQiuxu Zhuo 	.sad_all_offset		= 0x108,
875cf4e6d52SYouquan Song 	.offsets_scrub		= offsets_scrub_icx,
876cf4e6d52SYouquan Song 	.offsets_demand		= offsets_demand_icx,
877ee5340abSQiuxu Zhuo };
878ee5340abSQiuxu Zhuo 
879ce206708SQiuxu Zhuo static struct res_config i10nm_cfg1 = {
880ce206708SQiuxu Zhuo 	.type			= I10NM,
881ce206708SQiuxu Zhuo 	.decs_did		= 0x3452,
882ce206708SQiuxu Zhuo 	.busno_cfg_offset	= 0xd0,
883dd7814b7SQiuxu Zhuo 	.ddr_imc_num		= 4,
884dd7814b7SQiuxu Zhuo 	.ddr_chan_num		= 2,
885dd7814b7SQiuxu Zhuo 	.ddr_dimm_num		= 2,
886479f58ddSQiuxu Zhuo 	.ddr_chan_mmio_sz	= 0x4000,
887dd7814b7SQiuxu Zhuo 	.sad_all_bdf		= {1, 29, 0},
888dd7814b7SQiuxu Zhuo 	.pcu_cr3_bdf		= {1, 30, 3},
889dd7814b7SQiuxu Zhuo 	.util_all_bdf		= {1, 29, 1},
890dd7814b7SQiuxu Zhuo 	.uracu_bdf		= {0, 0, 1},
891dd7814b7SQiuxu Zhuo 	.ddr_mdev_bdf		= {0, 12, 0},
892dd7814b7SQiuxu Zhuo 	.hbm_mdev_bdf		= {0, 12, 1},
8934bd4d32eSQiuxu Zhuo 	.sad_all_offset		= 0x108,
894cf4e6d52SYouquan Song 	.offsets_scrub		= offsets_scrub_icx,
895cf4e6d52SYouquan Song 	.offsets_demand		= offsets_demand_icx,
896479f58ddSQiuxu Zhuo };
897479f58ddSQiuxu Zhuo 
898479f58ddSQiuxu Zhuo static struct res_config spr_cfg = {
899479f58ddSQiuxu Zhuo 	.type			= SPR,
900479f58ddSQiuxu Zhuo 	.decs_did		= 0x3252,
901479f58ddSQiuxu Zhuo 	.busno_cfg_offset	= 0xd0,
902dd7814b7SQiuxu Zhuo 	.ddr_imc_num		= 4,
903dd7814b7SQiuxu Zhuo 	.ddr_chan_num		= 2,
904dd7814b7SQiuxu Zhuo 	.ddr_dimm_num		= 2,
905dd7814b7SQiuxu Zhuo 	.hbm_imc_num		= 16,
906dd7814b7SQiuxu Zhuo 	.hbm_chan_num		= 2,
907dd7814b7SQiuxu Zhuo 	.hbm_dimm_num		= 1,
908479f58ddSQiuxu Zhuo 	.ddr_chan_mmio_sz	= 0x8000,
909c9450883SQiuxu Zhuo 	.hbm_chan_mmio_sz	= 0x4000,
910479f58ddSQiuxu Zhuo 	.support_ddr5		= true,
911dd7814b7SQiuxu Zhuo 	.sad_all_bdf		= {1, 10, 0},
912dd7814b7SQiuxu Zhuo 	.pcu_cr3_bdf		= {1, 30, 3},
913dd7814b7SQiuxu Zhuo 	.util_all_bdf		= {1, 29, 1},
914dd7814b7SQiuxu Zhuo 	.uracu_bdf		= {0, 0, 1},
915dd7814b7SQiuxu Zhuo 	.ddr_mdev_bdf		= {0, 12, 0},
916dd7814b7SQiuxu Zhuo 	.hbm_mdev_bdf		= {0, 12, 1},
9174bd4d32eSQiuxu Zhuo 	.sad_all_offset		= 0x300,
918cf4e6d52SYouquan Song 	.offsets_scrub		= offsets_scrub_spr,
919acd4cf68SQiuxu Zhuo 	.offsets_scrub_hbm0	= offsets_scrub_spr_hbm0,
920acd4cf68SQiuxu Zhuo 	.offsets_scrub_hbm1	= offsets_scrub_spr_hbm1,
921cf4e6d52SYouquan Song 	.offsets_demand		= offsets_demand_spr,
922d5f5e499SQiuxu Zhuo 	.offsets_demand2	= offsets_demand2_spr,
923acd4cf68SQiuxu Zhuo 	.offsets_demand_hbm0	= offsets_demand_spr_hbm0,
924acd4cf68SQiuxu Zhuo 	.offsets_demand_hbm1	= offsets_demand_spr_hbm1,
925ce206708SQiuxu Zhuo };
926ce206708SQiuxu Zhuo 
927ba987eaaSQiuxu Zhuo static struct res_config gnr_cfg = {
928ba987eaaSQiuxu Zhuo 	.type			= GNR,
929ba987eaaSQiuxu Zhuo 	.decs_did		= 0x3252,
930ba987eaaSQiuxu Zhuo 	.busno_cfg_offset	= 0xd0,
931ba987eaaSQiuxu Zhuo 	.ddr_imc_num		= 12,
932ba987eaaSQiuxu Zhuo 	.ddr_chan_num		= 1,
933ba987eaaSQiuxu Zhuo 	.ddr_dimm_num		= 2,
934ba987eaaSQiuxu Zhuo 	.ddr_chan_mmio_sz	= 0x4000,
935ba987eaaSQiuxu Zhuo 	.support_ddr5		= true,
936ba987eaaSQiuxu Zhuo 	.sad_all_bdf		= {0, 13, 0},
937ba987eaaSQiuxu Zhuo 	.pcu_cr3_bdf		= {0, 5, 0},
938ba987eaaSQiuxu Zhuo 	.util_all_bdf		= {0, 13, 1},
939ba987eaaSQiuxu Zhuo 	.uracu_bdf		= {0, 0, 1},
940ba987eaaSQiuxu Zhuo 	.ddr_mdev_bdf		= {0, 5, 1},
941ba987eaaSQiuxu Zhuo 	.sad_all_offset		= 0x300,
942ba987eaaSQiuxu Zhuo };
943ba987eaaSQiuxu Zhuo 
944d4dc89d0SQiuxu Zhuo static const struct x86_cpu_id i10nm_cpuids[] = {
9458807e155SQiuxu Zhuo 	X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ATOM_TREMONT_D,	X86_STEPPINGS(0x0, 0x3), &i10nm_cfg0),
9468807e155SQiuxu Zhuo 	X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ATOM_TREMONT_D,	X86_STEPPINGS(0x4, 0xf), &i10nm_cfg1),
9478807e155SQiuxu Zhuo 	X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ICELAKE_X,		X86_STEPPINGS(0x0, 0x3), &i10nm_cfg0),
9488807e155SQiuxu Zhuo 	X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ICELAKE_X,		X86_STEPPINGS(0x4, 0xf), &i10nm_cfg1),
9498807e155SQiuxu Zhuo 	X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ICELAKE_D,		X86_STEPPINGS(0x0, 0xf), &i10nm_cfg1),
950479f58ddSQiuxu Zhuo 	X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(SAPPHIRERAPIDS_X,	X86_STEPPINGS(0x0, 0xf), &spr_cfg),
951e4b2bc66SQiuxu Zhuo 	X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(EMERALDRAPIDS_X,	X86_STEPPINGS(0x0, 0xf), &spr_cfg),
952ba987eaaSQiuxu Zhuo 	X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(GRANITERAPIDS_X,	X86_STEPPINGS(0x0, 0xf), &gnr_cfg),
9530cfd8fbaSPeter Zijlstra 	X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ATOM_CRESTMONT_X,	X86_STEPPINGS(0x0, 0xf), &gnr_cfg),
954d4dc89d0SQiuxu Zhuo 	{}
955d4dc89d0SQiuxu Zhuo };
956d4dc89d0SQiuxu Zhuo MODULE_DEVICE_TABLE(x86cpu, i10nm_cpuids);
957d4dc89d0SQiuxu Zhuo 
i10nm_check_ecc(struct skx_imc * imc,int chan)958d4dc89d0SQiuxu Zhuo static bool i10nm_check_ecc(struct skx_imc *imc, int chan)
959d4dc89d0SQiuxu Zhuo {
960d4dc89d0SQiuxu Zhuo 	u32 mcmtr;
961d4dc89d0SQiuxu Zhuo 
96283ff51c4SQiuxu Zhuo 	mcmtr = I10NM_GET_MCMTR(imc, chan);
963d4dc89d0SQiuxu Zhuo 	edac_dbg(1, "ch%d mcmtr reg %x\n", chan, mcmtr);
964d4dc89d0SQiuxu Zhuo 
965d4dc89d0SQiuxu Zhuo 	return !!GET_BITFIELD(mcmtr, 2, 2);
966d4dc89d0SQiuxu Zhuo }
967d4dc89d0SQiuxu Zhuo 
i10nm_get_dimm_config(struct mem_ctl_info * mci,struct res_config * cfg)968479f58ddSQiuxu Zhuo static int i10nm_get_dimm_config(struct mem_ctl_info *mci,
969479f58ddSQiuxu Zhuo 				 struct res_config *cfg)
970d4dc89d0SQiuxu Zhuo {
971d4dc89d0SQiuxu Zhuo 	struct skx_pvt *pvt = mci->pvt_info;
972d4dc89d0SQiuxu Zhuo 	struct skx_imc *imc = pvt->imc;
973ba987eaaSQiuxu Zhuo 	u32 mtr, amap, mcddrtcfg = 0;
974d4dc89d0SQiuxu Zhuo 	struct dimm_info *dimm;
975d4dc89d0SQiuxu Zhuo 	int i, j, ndimms;
976d4dc89d0SQiuxu Zhuo 
977c9450883SQiuxu Zhuo 	for (i = 0; i < imc->num_channels; i++) {
978d4dc89d0SQiuxu Zhuo 		if (!imc->mbase)
979d4dc89d0SQiuxu Zhuo 			continue;
980d4dc89d0SQiuxu Zhuo 
981d4dc89d0SQiuxu Zhuo 		ndimms = 0;
982479f58ddSQiuxu Zhuo 		amap = I10NM_GET_AMAP(imc, i);
983ba987eaaSQiuxu Zhuo 
984ba987eaaSQiuxu Zhuo 		if (res_cfg->type != GNR)
9852294a729SQiuxu Zhuo 			mcddrtcfg = I10NM_GET_MCDDRTCFG(imc, i);
986ba987eaaSQiuxu Zhuo 
987c9450883SQiuxu Zhuo 		for (j = 0; j < imc->num_dimms; j++) {
988bc9ad9e4SRobert Richter 			dimm = edac_get_dimm(mci, i, j, 0);
989d4dc89d0SQiuxu Zhuo 			mtr = I10NM_GET_DIMMMTR(imc, i, j);
990d4dc89d0SQiuxu Zhuo 			edac_dbg(1, "dimmmtr 0x%x mcddrtcfg 0x%x (mc%d ch%d dimm%d)\n",
991d4dc89d0SQiuxu Zhuo 				 mtr, mcddrtcfg, imc->mc, i, j);
992d4dc89d0SQiuxu Zhuo 
993d4dc89d0SQiuxu Zhuo 			if (IS_DIMM_PRESENT(mtr))
994479f58ddSQiuxu Zhuo 				ndimms += skx_get_dimm_info(mtr, 0, amap, dimm,
995479f58ddSQiuxu Zhuo 							    imc, i, j, cfg);
996d4dc89d0SQiuxu Zhuo 			else if (IS_NVDIMM_PRESENT(mcddrtcfg, j))
997d4dc89d0SQiuxu Zhuo 				ndimms += skx_get_nvdimm_info(dimm, imc, i, j,
998d4dc89d0SQiuxu Zhuo 							      EDAC_MOD_STR);
999d4dc89d0SQiuxu Zhuo 		}
1000c4a1dd9eSQiuxu Zhuo 		if (ndimms && !i10nm_check_ecc(imc, i)) {
1001c4a1dd9eSQiuxu Zhuo 			i10nm_printk(KERN_ERR, "ECC is disabled on imc %d channel %d\n",
1002c4a1dd9eSQiuxu Zhuo 				     imc->mc, i);
1003d4dc89d0SQiuxu Zhuo 			return -ENODEV;
1004d4dc89d0SQiuxu Zhuo 		}
1005d4dc89d0SQiuxu Zhuo 	}
1006d4dc89d0SQiuxu Zhuo 
1007d4dc89d0SQiuxu Zhuo 	return 0;
1008d4dc89d0SQiuxu Zhuo }
1009d4dc89d0SQiuxu Zhuo 
1010d4dc89d0SQiuxu Zhuo static struct notifier_block i10nm_mce_dec = {
1011d4dc89d0SQiuxu Zhuo 	.notifier_call	= skx_mce_check_error,
1012d4dc89d0SQiuxu Zhuo 	.priority	= MCE_PRIO_EDAC,
1013d4dc89d0SQiuxu Zhuo };
1014d4dc89d0SQiuxu Zhuo 
1015fe783516SQiuxu Zhuo #ifdef CONFIG_EDAC_DEBUG
1016fe783516SQiuxu Zhuo /*
1017fe783516SQiuxu Zhuo  * Debug feature.
1018fe783516SQiuxu Zhuo  * Exercise the address decode logic by writing an address to
1019fe783516SQiuxu Zhuo  * /sys/kernel/debug/edac/i10nm_test/addr.
1020fe783516SQiuxu Zhuo  */
1021fe783516SQiuxu Zhuo static struct dentry *i10nm_test;
1022fe783516SQiuxu Zhuo 
debugfs_u64_set(void * data,u64 val)1023fe783516SQiuxu Zhuo static int debugfs_u64_set(void *data, u64 val)
1024fe783516SQiuxu Zhuo {
1025fe783516SQiuxu Zhuo 	struct mce m;
1026fe783516SQiuxu Zhuo 
1027fe783516SQiuxu Zhuo 	pr_warn_once("Fake error to 0x%llx injected via debugfs\n", val);
1028fe783516SQiuxu Zhuo 
1029fe783516SQiuxu Zhuo 	memset(&m, 0, sizeof(m));
1030fe783516SQiuxu Zhuo 	/* ADDRV + MemRd + Unknown channel */
1031fe783516SQiuxu Zhuo 	m.status = MCI_STATUS_ADDRV + 0x90;
1032fe783516SQiuxu Zhuo 	/* One corrected error */
1033fe783516SQiuxu Zhuo 	m.status |= BIT_ULL(MCI_STATUS_CEC_SHIFT);
1034fe783516SQiuxu Zhuo 	m.addr = val;
1035fe783516SQiuxu Zhuo 	skx_mce_check_error(NULL, 0, &m);
1036fe783516SQiuxu Zhuo 
1037fe783516SQiuxu Zhuo 	return 0;
1038fe783516SQiuxu Zhuo }
1039fe783516SQiuxu Zhuo DEFINE_SIMPLE_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n");
1040fe783516SQiuxu Zhuo 
setup_i10nm_debug(void)1041fe783516SQiuxu Zhuo static void setup_i10nm_debug(void)
1042fe783516SQiuxu Zhuo {
1043fe783516SQiuxu Zhuo 	i10nm_test = edac_debugfs_create_dir("i10nm_test");
1044fe783516SQiuxu Zhuo 	if (!i10nm_test)
1045fe783516SQiuxu Zhuo 		return;
1046fe783516SQiuxu Zhuo 
1047fe783516SQiuxu Zhuo 	if (!edac_debugfs_create_file("addr", 0200, i10nm_test,
1048fe783516SQiuxu Zhuo 				      NULL, &fops_u64_wo)) {
1049fe783516SQiuxu Zhuo 		debugfs_remove(i10nm_test);
1050fe783516SQiuxu Zhuo 		i10nm_test = NULL;
1051fe783516SQiuxu Zhuo 	}
1052fe783516SQiuxu Zhuo }
1053fe783516SQiuxu Zhuo 
teardown_i10nm_debug(void)1054fe783516SQiuxu Zhuo static void teardown_i10nm_debug(void)
1055fe783516SQiuxu Zhuo {
1056fe783516SQiuxu Zhuo 	debugfs_remove_recursive(i10nm_test);
1057fe783516SQiuxu Zhuo }
1058fe783516SQiuxu Zhuo #else
setup_i10nm_debug(void)1059fe783516SQiuxu Zhuo static inline void setup_i10nm_debug(void) {}
teardown_i10nm_debug(void)1060fe783516SQiuxu Zhuo static inline void teardown_i10nm_debug(void) {}
1061fe783516SQiuxu Zhuo #endif /*CONFIG_EDAC_DEBUG*/
1062fe783516SQiuxu Zhuo 
i10nm_init(void)1063d4dc89d0SQiuxu Zhuo static int __init i10nm_init(void)
1064d4dc89d0SQiuxu Zhuo {
1065d4dc89d0SQiuxu Zhuo 	u8 mc = 0, src_id = 0, node_id = 0;
1066d4dc89d0SQiuxu Zhuo 	const struct x86_cpu_id *id;
1067ee5340abSQiuxu Zhuo 	struct res_config *cfg;
1068d4dc89d0SQiuxu Zhuo 	const char *owner;
1069d4dc89d0SQiuxu Zhuo 	struct skx_dev *d;
1070d4dc89d0SQiuxu Zhuo 	int rc, i, off[3] = {0xd0, 0xc8, 0xcc};
1071d4dc89d0SQiuxu Zhuo 	u64 tolm, tohm;
1072dd7814b7SQiuxu Zhuo 	int imc_num;
1073d4dc89d0SQiuxu Zhuo 
1074d4dc89d0SQiuxu Zhuo 	edac_dbg(2, "\n");
1075d4dc89d0SQiuxu Zhuo 
1076315bada6SJia He 	if (ghes_get_devices())
1077315bada6SJia He 		return -EBUSY;
1078315bada6SJia He 
1079d4dc89d0SQiuxu Zhuo 	owner = edac_get_owner();
1080d4dc89d0SQiuxu Zhuo 	if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
1081d4dc89d0SQiuxu Zhuo 		return -EBUSY;
1082d4dc89d0SQiuxu Zhuo 
1083f0a029ffSLuck, Tony 	if (cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
1084f0a029ffSLuck, Tony 		return -ENODEV;
1085f0a029ffSLuck, Tony 
1086d4dc89d0SQiuxu Zhuo 	id = x86_match_cpu(i10nm_cpuids);
1087d4dc89d0SQiuxu Zhuo 	if (!id)
1088d4dc89d0SQiuxu Zhuo 		return -ENODEV;
1089d4dc89d0SQiuxu Zhuo 
1090ee5340abSQiuxu Zhuo 	cfg = (struct res_config *)id->driver_data;
1091*d9338b78SQiuxu Zhuo 	skx_set_res_cfg(cfg);
1092cf4e6d52SYouquan Song 	res_cfg = cfg;
1093ee5340abSQiuxu Zhuo 
1094d4dc89d0SQiuxu Zhuo 	rc = skx_get_hi_lo(0x09a2, off, &tolm, &tohm);
1095d4dc89d0SQiuxu Zhuo 	if (rc)
1096d4dc89d0SQiuxu Zhuo 		return rc;
1097d4dc89d0SQiuxu Zhuo 
1098ee5340abSQiuxu Zhuo 	rc = skx_get_all_bus_mappings(cfg, &i10nm_edac_list);
1099d4dc89d0SQiuxu Zhuo 	if (rc < 0)
1100d4dc89d0SQiuxu Zhuo 		goto fail;
1101d4dc89d0SQiuxu Zhuo 	if (rc == 0) {
1102d4dc89d0SQiuxu Zhuo 		i10nm_printk(KERN_ERR, "No memory controllers found\n");
1103d4dc89d0SQiuxu Zhuo 		return -ENODEV;
1104d4dc89d0SQiuxu Zhuo 	}
1105d4dc89d0SQiuxu Zhuo 
1106ba987eaaSQiuxu Zhuo 	rc = i10nm_get_imc_num(cfg);
1107ba987eaaSQiuxu Zhuo 	if (rc < 0)
1108ba987eaaSQiuxu Zhuo 		goto fail;
1109ba987eaaSQiuxu Zhuo 
11102738c69aSYouquan Song 	mem_cfg_2lm = i10nm_check_2lm(cfg);
11112738c69aSYouquan Song 	skx_set_mem_cfg(mem_cfg_2lm);
11124bd4d32eSQiuxu Zhuo 
1113c9450883SQiuxu Zhuo 	rc = i10nm_get_ddr_munits();
1114c9450883SQiuxu Zhuo 
1115c9450883SQiuxu Zhuo 	if (i10nm_get_hbm_munits() && rc)
1116d4dc89d0SQiuxu Zhuo 		goto fail;
1117d4dc89d0SQiuxu Zhuo 
1118dd7814b7SQiuxu Zhuo 	imc_num = res_cfg->ddr_imc_num + res_cfg->hbm_imc_num;
1119dd7814b7SQiuxu Zhuo 
1120d4dc89d0SQiuxu Zhuo 	list_for_each_entry(d, i10nm_edac_list, list) {
11211dc78f1fSQiuxu Zhuo 		rc = skx_get_src_id(d, 0xf8, &src_id);
1122d4dc89d0SQiuxu Zhuo 		if (rc < 0)
1123d4dc89d0SQiuxu Zhuo 			goto fail;
1124d4dc89d0SQiuxu Zhuo 
1125d4dc89d0SQiuxu Zhuo 		rc = skx_get_node_id(d, &node_id);
1126d4dc89d0SQiuxu Zhuo 		if (rc < 0)
1127d4dc89d0SQiuxu Zhuo 			goto fail;
1128d4dc89d0SQiuxu Zhuo 
1129d4dc89d0SQiuxu Zhuo 		edac_dbg(2, "src_id = %d node_id = %d\n", src_id, node_id);
1130dd7814b7SQiuxu Zhuo 		for (i = 0; i < imc_num; i++) {
1131d4dc89d0SQiuxu Zhuo 			if (!d->imc[i].mdev)
1132d4dc89d0SQiuxu Zhuo 				continue;
1133d4dc89d0SQiuxu Zhuo 
1134d4dc89d0SQiuxu Zhuo 			d->imc[i].mc  = mc++;
1135d4dc89d0SQiuxu Zhuo 			d->imc[i].lmc = i;
1136d4dc89d0SQiuxu Zhuo 			d->imc[i].src_id  = src_id;
1137d4dc89d0SQiuxu Zhuo 			d->imc[i].node_id = node_id;
1138c9450883SQiuxu Zhuo 			if (d->imc[i].hbm_mc) {
1139c9450883SQiuxu Zhuo 				d->imc[i].chan_mmio_sz = cfg->hbm_chan_mmio_sz;
1140dd7814b7SQiuxu Zhuo 				d->imc[i].num_channels = cfg->hbm_chan_num;
1141dd7814b7SQiuxu Zhuo 				d->imc[i].num_dimms    = cfg->hbm_dimm_num;
1142c9450883SQiuxu Zhuo 			} else {
1143479f58ddSQiuxu Zhuo 				d->imc[i].chan_mmio_sz = cfg->ddr_chan_mmio_sz;
1144dd7814b7SQiuxu Zhuo 				d->imc[i].num_channels = cfg->ddr_chan_num;
1145dd7814b7SQiuxu Zhuo 				d->imc[i].num_dimms    = cfg->ddr_dimm_num;
1146c9450883SQiuxu Zhuo 			}
1147d4dc89d0SQiuxu Zhuo 
1148d4dc89d0SQiuxu Zhuo 			rc = skx_register_mci(&d->imc[i], d->imc[i].mdev,
1149d4dc89d0SQiuxu Zhuo 					      "Intel_10nm Socket", EDAC_MOD_STR,
1150479f58ddSQiuxu Zhuo 					      i10nm_get_dimm_config, cfg);
1151d4dc89d0SQiuxu Zhuo 			if (rc < 0)
1152d4dc89d0SQiuxu Zhuo 				goto fail;
1153d4dc89d0SQiuxu Zhuo 		}
1154d4dc89d0SQiuxu Zhuo 	}
1155d4dc89d0SQiuxu Zhuo 
1156d4dc89d0SQiuxu Zhuo 	rc = skx_adxl_get();
1157d4dc89d0SQiuxu Zhuo 	if (rc)
1158d4dc89d0SQiuxu Zhuo 		goto fail;
1159d4dc89d0SQiuxu Zhuo 
1160d4dc89d0SQiuxu Zhuo 	opstate_init();
1161d4dc89d0SQiuxu Zhuo 	mce_register_decode_chain(&i10nm_mce_dec);
1162fe783516SQiuxu Zhuo 	setup_i10nm_debug();
1163d4dc89d0SQiuxu Zhuo 
1164cf4e6d52SYouquan Song 	if (retry_rd_err_log && res_cfg->offsets_scrub && res_cfg->offsets_demand) {
11652738c69aSYouquan Song 		skx_set_decode(i10nm_mc_decode, show_retry_rd_err_log);
1166cf4e6d52SYouquan Song 		if (retry_rd_err_log == 2)
1167cf4e6d52SYouquan Song 			enable_retry_rd_err_log(true);
11682738c69aSYouquan Song 	} else {
11692738c69aSYouquan Song 		skx_set_decode(i10nm_mc_decode, NULL);
1170cf4e6d52SYouquan Song 	}
1171cf4e6d52SYouquan Song 
1172d4dc89d0SQiuxu Zhuo 	i10nm_printk(KERN_INFO, "%s\n", I10NM_REVISION);
1173d4dc89d0SQiuxu Zhuo 
1174d4dc89d0SQiuxu Zhuo 	return 0;
1175d4dc89d0SQiuxu Zhuo fail:
1176d4dc89d0SQiuxu Zhuo 	skx_remove();
1177d4dc89d0SQiuxu Zhuo 	return rc;
1178d4dc89d0SQiuxu Zhuo }
1179d4dc89d0SQiuxu Zhuo 
i10nm_exit(void)1180d4dc89d0SQiuxu Zhuo static void __exit i10nm_exit(void)
1181d4dc89d0SQiuxu Zhuo {
1182d4dc89d0SQiuxu Zhuo 	edac_dbg(2, "\n");
1183cf4e6d52SYouquan Song 
1184cf4e6d52SYouquan Song 	if (retry_rd_err_log && res_cfg->offsets_scrub && res_cfg->offsets_demand) {
1185cf4e6d52SYouquan Song 		skx_set_decode(NULL, NULL);
1186cf4e6d52SYouquan Song 		if (retry_rd_err_log == 2)
1187cf4e6d52SYouquan Song 			enable_retry_rd_err_log(false);
1188cf4e6d52SYouquan Song 	}
1189cf4e6d52SYouquan Song 
1190fe783516SQiuxu Zhuo 	teardown_i10nm_debug();
1191d4dc89d0SQiuxu Zhuo 	mce_unregister_decode_chain(&i10nm_mce_dec);
1192d4dc89d0SQiuxu Zhuo 	skx_adxl_put();
1193d4dc89d0SQiuxu Zhuo 	skx_remove();
1194d4dc89d0SQiuxu Zhuo }
1195d4dc89d0SQiuxu Zhuo 
1196d4dc89d0SQiuxu Zhuo module_init(i10nm_init);
1197d4dc89d0SQiuxu Zhuo module_exit(i10nm_exit);
1198d4dc89d0SQiuxu Zhuo 
set_decoding_via_mca(const char * buf,const struct kernel_param * kp)11992738c69aSYouquan Song static int set_decoding_via_mca(const char *buf, const struct kernel_param *kp)
12002738c69aSYouquan Song {
12012738c69aSYouquan Song 	unsigned long val;
12022738c69aSYouquan Song 	int ret;
12032738c69aSYouquan Song 
12042738c69aSYouquan Song 	ret = kstrtoul(buf, 0, &val);
12052738c69aSYouquan Song 
12062738c69aSYouquan Song 	if (ret || val > 1)
12072738c69aSYouquan Song 		return -EINVAL;
12082738c69aSYouquan Song 
12092738c69aSYouquan Song 	if (val && mem_cfg_2lm) {
12102738c69aSYouquan Song 		i10nm_printk(KERN_NOTICE, "Decoding errors via MCA banks for 2LM isn't supported yet\n");
12112738c69aSYouquan Song 		return -EIO;
12122738c69aSYouquan Song 	}
12132738c69aSYouquan Song 
12142738c69aSYouquan Song 	ret = param_set_int(buf, kp);
12152738c69aSYouquan Song 
12162738c69aSYouquan Song 	return ret;
12172738c69aSYouquan Song }
12182738c69aSYouquan Song 
12192738c69aSYouquan Song static const struct kernel_param_ops decoding_via_mca_param_ops = {
12202738c69aSYouquan Song 	.set = set_decoding_via_mca,
12212738c69aSYouquan Song 	.get = param_get_int,
12222738c69aSYouquan Song };
12232738c69aSYouquan Song 
12242738c69aSYouquan Song module_param_cb(decoding_via_mca, &decoding_via_mca_param_ops, &decoding_via_mca, 0644);
12252738c69aSYouquan Song MODULE_PARM_DESC(decoding_via_mca, "decoding_via_mca: 0=off(default), 1=enable");
12262738c69aSYouquan Song 
1227cf4e6d52SYouquan Song module_param(retry_rd_err_log, int, 0444);
1228cf4e6d52SYouquan Song MODULE_PARM_DESC(retry_rd_err_log, "retry_rd_err_log: 0=off(default), 1=bios(Linux doesn't reset any control bits, but just reports values.), 2=linux(Linux tries to take control and resets mode bits, clear valid/UC bits after reading.)");
1229cf4e6d52SYouquan Song 
1230d4dc89d0SQiuxu Zhuo MODULE_LICENSE("GPL v2");
1231d4dc89d0SQiuxu Zhuo MODULE_DESCRIPTION("MC Driver for Intel 10nm server processors");
1232