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