From: Qiuxu Zhuo Date: Thu, 21 May 2026 07:31:09 +0000 (+0800) Subject: EDAC/{skx_common,i10nm}: Move RRL handling to common code X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=8998a4a646471dc218f6499c5580e252891ccaa4;p=thirdparty%2Fkernel%2Flinux.git EDAC/{skx_common,i10nm}: Move RRL handling to common code Move RRL (Retry Read error Log) handling from i10nm_edac to skx_common so it can be shared across EDAC drivers (e.g. imh_edac). - Move RRL enable/disable and log dumping helpers to skx_common to avoid code duplication and enable reuse by other drivers. - Export skx_enable_rrl() and skx_show_rrl() so common RRL handling can be used by i10nm_edac and imh_edac. No functional change intended. Signed-off-by: Qiuxu Zhuo Signed-off-by: Tony Luck Tested-by: Yi Lai Link: https://patch.msgid.link/20260521073112.3881223-6-qiuxu.zhuo@intel.com --- diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c index fe148f1f2319d..2e21eefbb4f5d 100644 --- a/drivers/edac/i10nm_base.c +++ b/drivers/edac/i10nm_base.c @@ -183,191 +183,6 @@ static struct reg_rrl gnr_reg_rrl_ddr = { .cecnt_widths = {4, 4, 4, 4, 4, 4, 4, 4}, }; -static void enable_rrl(struct skx_imc *imc, int chan, struct reg_rrl *rrl, - int rrl_set, bool enable, u32 *rrl_ctl) -{ - enum rrl_source_type source = rrl->sources[rrl_set]; - u32 offset = rrl->offsets[rrl_set][0], v; - u8 width = rrl->widths[0]; - bool first, scrub; - - /* First or last read error. */ - first = (source == RRL_SRC_FRE_SCRUB || source == RRL_SRC_FRE_DEMAND); - /* Patrol scrub or on-demand read error. */ - scrub = (source == RRL_SRC_FRE_SCRUB || source == RRL_SRC_LRE_SCRUB); - - v = skx_read_imc_reg(imc, chan, offset, width); - - if (enable) { - /* Save default configurations. */ - *rrl_ctl = v; - v &= ~rrl->uc_mask; - - if (first) - v |= rrl->noover_mask; - else - v &= ~rrl->noover_mask; - - if (scrub) - v |= rrl->en_patspr_mask; - else - v &= ~rrl->en_patspr_mask; - - v |= rrl->en_mask; - } else { - /* Restore default configurations. */ - if (*rrl_ctl & rrl->uc_mask) - v |= rrl->uc_mask; - - if (first) { - if (!(*rrl_ctl & rrl->noover_mask)) - v &= ~rrl->noover_mask; - } else { - if (*rrl_ctl & rrl->noover_mask) - v |= rrl->noover_mask; - } - - if (scrub) { - if (!(*rrl_ctl & rrl->en_patspr_mask)) - v &= ~rrl->en_patspr_mask; - } else { - if (*rrl_ctl & rrl->en_patspr_mask) - v |= rrl->en_patspr_mask; - } - - if (!(*rrl_ctl & rrl->en_mask)) - v &= ~rrl->en_mask; - } - - skx_write_imc_reg(imc, chan, offset, width, v); -} - -static void enable_rrls(struct skx_imc *imc, int chan, struct reg_rrl *rrl, - bool enable, u32 *rrl_ctl) -{ - for (int i = 0; i < rrl->set_num; i++) - enable_rrl(imc, chan, rrl, i, enable, rrl_ctl + i); -} - -static void enable_rrls_ddr(struct skx_imc *imc, bool enable) -{ - struct reg_rrl *rrl_ddr = res_cfg->reg_rrl_ddr; - int i, chan_num = res_cfg->ddr_chan_num; - struct skx_channel *chan = imc->chan; - - if (!imc->mbase) - return; - - for (i = 0; i < chan_num; i++) - enable_rrls(imc, i, rrl_ddr, enable, chan[i].rrl_ctl[0]); -} - -static void enable_rrls_hbm(struct skx_imc *imc, bool enable) -{ - struct reg_rrl **rrl_hbm = res_cfg->reg_rrl_hbm; - int i, chan_num = res_cfg->hbm_chan_num; - struct skx_channel *chan = imc->chan; - - if (!imc->mbase || !imc->hbm_mc || !rrl_hbm[0] || !rrl_hbm[1]) - return; - - for (i = 0; i < chan_num; i++) { - enable_rrls(imc, i, rrl_hbm[0], enable, chan[i].rrl_ctl[0]); - enable_rrls(imc, i, rrl_hbm[1], enable, chan[i].rrl_ctl[1]); - } -} - -static void enable_retry_rd_err_log(bool enable) -{ - struct skx_dev *d; - int i, imc_num; - - edac_dbg(2, "\n"); - - list_for_each_entry(d, i10nm_edac_list, list) { - imc_num = res_cfg->ddr_imc_num; - for (i = 0; i < imc_num; i++) - enable_rrls_ddr(&d->imc[i], enable); - - imc_num += res_cfg->hbm_imc_num; - for (; i < imc_num; i++) - enable_rrls_hbm(&d->imc[i], enable); - } -} - -static void show_retry_rd_err_log(struct decoded_addr *res, char *msg, - int len, bool scrub_err) -{ - int i, j, n, ch = res->channel, pch = res->cs & 1; - struct skx_imc *imc = &res->dev->imc[res->imc]; - u64 log, corr, status_mask; - struct reg_rrl *rrl; - bool scrub; - u32 offset; - u8 width; - - if (!imc->mbase) - return; - - rrl = imc->hbm_mc ? res_cfg->reg_rrl_hbm[pch] : res_cfg->reg_rrl_ddr; - - if (!rrl) - return; - - status_mask = rrl->over_mask | rrl->uc_mask | rrl->v_mask; - - n = scnprintf(msg, len, " retry_rd_err_log["); - for (i = 0; i < rrl->set_num; i++) { - scrub = (rrl->sources[i] == RRL_SRC_FRE_SCRUB || rrl->sources[i] == RRL_SRC_LRE_SCRUB); - if (scrub_err != scrub) - continue; - - for (j = 0; j < rrl->reg_num && len - n > 0; j++) { - offset = rrl->offsets[i][j]; - width = rrl->widths[j]; - log = skx_read_imc_reg(imc, ch, offset, width); - - if (width == 4) - n += scnprintf(msg + n, len - n, "%.8llx ", log); - else - n += scnprintf(msg + n, len - n, "%.16llx ", log); - - /* Clear RRL status if RRL in Linux control mode. */ - if (res_cfg->rrl_ctrl_mode == RRL_CTRL_LINUX && !j && (log & status_mask)) - skx_write_imc_reg(imc, ch, offset, width, log & ~status_mask); - } - } - - /* Move back one space. */ - n--; - n += scnprintf(msg + n, len - n, "]"); - - if (len - n > 0) { - n += scnprintf(msg + n, len - n, " correrrcnt["); - for (i = 0; i < rrl->cecnt_num && len - n > 0; i++) { - offset = rrl->cecnt_offsets[i]; - width = rrl->cecnt_widths[i]; - corr = skx_read_imc_reg(imc, ch, offset, width); - - /* CPUs {ICX,SPR} encode two counters per 4-byte CORRERRCNT register. */ - if (res_cfg->type <= SPR) { - n += scnprintf(msg + n, len - n, "%.4llx %.4llx ", - corr & 0xffff, corr >> 16); - } else { - /* CPUs {GNR} encode one counter per CORRERRCNT register. */ - if (width == 4) - n += scnprintf(msg + n, len - n, "%.8llx ", corr); - else - n += scnprintf(msg + n, len - n, "%.16llx ", corr); - } - } - - /* Move back one space. */ - n--; - n += scnprintf(msg + n, len - n, "]"); - } -} - static struct pci_dev *pci_get_dev_wrapper(int dom, unsigned int bus, unsigned int dev, unsigned int fun) { @@ -1209,9 +1024,9 @@ static int __init i10nm_init(void) res_cfg->rrl_ctrl_mode = retry_rd_err_log; if (retry_rd_err_log && res_cfg->reg_rrl_ddr) { - skx_set_show_rrl(show_retry_rd_err_log); + skx_set_show_rrl(skx_show_rrl); if (retry_rd_err_log == RRL_CTRL_LINUX) - enable_retry_rd_err_log(true); + skx_enable_rrl(true); } skx_set_decode(i10nm_mc_decode); @@ -1232,7 +1047,7 @@ static void __exit i10nm_exit(void) if (retry_rd_err_log && res_cfg->reg_rrl_ddr) { if (retry_rd_err_log == RRL_CTRL_LINUX) - enable_retry_rd_err_log(false); + skx_enable_rrl(false); skx_set_show_rrl(NULL); } diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c index 2cdef2e69d71e..f769a354ea45a 100644 --- a/drivers/edac/skx_common.c +++ b/drivers/edac/skx_common.c @@ -102,6 +102,192 @@ void skx_write_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width, u64 } EXPORT_SYMBOL_GPL(skx_write_imc_reg); +static void enable_rrl(struct skx_imc *imc, int chan, struct reg_rrl *rrl, + int rrl_set, bool enable, u32 *rrl_ctl) +{ + enum rrl_source_type source = rrl->sources[rrl_set]; + u32 offset = rrl->offsets[rrl_set][0], v; + u8 width = rrl->widths[0]; + bool first, scrub; + + /* First or last read error. */ + first = (source == RRL_SRC_FRE_SCRUB || source == RRL_SRC_FRE_DEMAND); + /* Patrol scrub or on-demand read error. */ + scrub = (source == RRL_SRC_FRE_SCRUB || source == RRL_SRC_LRE_SCRUB); + + v = skx_read_imc_reg(imc, chan, offset, width); + + if (enable) { + /* Save default configurations. */ + *rrl_ctl = v; + v &= ~rrl->uc_mask; + + if (first) + v |= rrl->noover_mask; + else + v &= ~rrl->noover_mask; + + if (scrub) + v |= rrl->en_patspr_mask; + else + v &= ~rrl->en_patspr_mask; + + v |= rrl->en_mask; + } else { + /* Restore default configurations. */ + if (*rrl_ctl & rrl->uc_mask) + v |= rrl->uc_mask; + + if (first) { + if (!(*rrl_ctl & rrl->noover_mask)) + v &= ~rrl->noover_mask; + } else { + if (*rrl_ctl & rrl->noover_mask) + v |= rrl->noover_mask; + } + + if (scrub) { + if (!(*rrl_ctl & rrl->en_patspr_mask)) + v &= ~rrl->en_patspr_mask; + } else { + if (*rrl_ctl & rrl->en_patspr_mask) + v |= rrl->en_patspr_mask; + } + + if (!(*rrl_ctl & rrl->en_mask)) + v &= ~rrl->en_mask; + } + + skx_write_imc_reg(imc, chan, offset, width, v); +} + +static void enable_rrls(struct skx_imc *imc, int chan, struct reg_rrl *rrl, + bool enable, u32 *rrl_ctl) +{ + for (int i = 0; i < rrl->set_num; i++) + enable_rrl(imc, chan, rrl, i, enable, rrl_ctl + i); +} + +static void enable_rrls_ddr(struct skx_imc *imc, bool enable) +{ + struct reg_rrl *rrl_ddr = skx_res_cfg->reg_rrl_ddr; + int i, chan_num = skx_res_cfg->ddr_chan_num; + struct skx_channel *chan = imc->chan; + + if (!imc->mbase) + return; + + for (i = 0; i < chan_num; i++) + enable_rrls(imc, i, rrl_ddr, enable, chan[i].rrl_ctl[0]); +} + +static void enable_rrls_hbm(struct skx_imc *imc, bool enable) +{ + struct reg_rrl **rrl_hbm = skx_res_cfg->reg_rrl_hbm; + int i, chan_num = skx_res_cfg->hbm_chan_num; + struct skx_channel *chan = imc->chan; + + if (!imc->mbase || !imc->hbm_mc || !rrl_hbm[0] || !rrl_hbm[1]) + return; + + for (i = 0; i < chan_num; i++) { + enable_rrls(imc, i, rrl_hbm[0], enable, chan[i].rrl_ctl[0]); + enable_rrls(imc, i, rrl_hbm[1], enable, chan[i].rrl_ctl[1]); + } +} + +void skx_enable_rrl(bool enable) +{ + struct skx_dev *d; + int i, imc_num; + + edac_dbg(2, "\n"); + + list_for_each_entry(d, &dev_edac_list, list) { + imc_num = skx_res_cfg->ddr_imc_num; + for (i = 0; i < imc_num; i++) + enable_rrls_ddr(&d->imc[i], enable); + + imc_num += skx_res_cfg->hbm_imc_num; + for (; i < imc_num; i++) + enable_rrls_hbm(&d->imc[i], enable); + } +} +EXPORT_SYMBOL_GPL(skx_enable_rrl); + +void skx_show_rrl(struct decoded_addr *res, char *msg, int len, bool scrub_err) +{ + int i, j, n, ch = res->channel, pch = res->cs & 1; + struct skx_imc *imc = &res->dev->imc[res->imc]; + u64 log, corr, status_mask; + struct reg_rrl *rrl; + bool scrub; + u32 offset; + u8 width; + + if (!imc->mbase) + return; + + rrl = imc->hbm_mc ? skx_res_cfg->reg_rrl_hbm[pch] : skx_res_cfg->reg_rrl_ddr; + + if (!rrl) + return; + + status_mask = rrl->over_mask | rrl->uc_mask | rrl->v_mask; + + n = scnprintf(msg, len, " retry_rd_err_log["); + for (i = 0; i < rrl->set_num; i++) { + scrub = (rrl->sources[i] == RRL_SRC_FRE_SCRUB || rrl->sources[i] == RRL_SRC_LRE_SCRUB); + if (scrub_err != scrub) + continue; + + for (j = 0; j < rrl->reg_num && len - n > 0; j++) { + offset = rrl->offsets[i][j]; + width = rrl->widths[j]; + log = skx_read_imc_reg(imc, ch, offset, width); + + if (width == 4) + n += scnprintf(msg + n, len - n, "%.8llx ", log); + else + n += scnprintf(msg + n, len - n, "%.16llx ", log); + + /* Clear RRL status if RRL in Linux control mode. */ + if (skx_res_cfg->rrl_ctrl_mode == RRL_CTRL_LINUX && !j && (log & status_mask)) + skx_write_imc_reg(imc, ch, offset, width, log & ~status_mask); + } + } + + /* Move back one space. */ + n--; + n += scnprintf(msg + n, len - n, "]"); + + if (len - n > 0) { + n += scnprintf(msg + n, len - n, " correrrcnt["); + for (i = 0; i < rrl->cecnt_num && len - n > 0; i++) { + offset = rrl->cecnt_offsets[i]; + width = rrl->cecnt_widths[i]; + corr = skx_read_imc_reg(imc, ch, offset, width); + + /* CPUs {ICX,SPR} encode two counters per 4-byte CORRERRCNT register. */ + if (skx_res_cfg->type <= SPR) { + n += scnprintf(msg + n, len - n, "%.4llx %.4llx ", + corr & 0xffff, corr >> 16); + } else { + /* CPUs {GNR} encode one counter per CORRERRCNT register. */ + if (width == 4) + n += scnprintf(msg + n, len - n, "%.8llx ", corr); + else + n += scnprintf(msg + n, len - n, "%.16llx ", corr); + } + } + + /* Move back one space. */ + n--; + n += scnprintf(msg + n, len - n, "]"); + } +} +EXPORT_SYMBOL_GPL(skx_show_rrl); + int skx_adxl_get(void) { const char * const *names; diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h index 4091431356d61..eea2d95cc0ac6 100644 --- a/drivers/edac/skx_common.h +++ b/drivers/edac/skx_common.h @@ -344,6 +344,8 @@ int skx_adxl_get(void); void skx_adxl_put(void); void skx_set_decode(skx_decode_f decode); void skx_set_show_rrl(skx_show_rrl_f rrl); +void skx_show_rrl(struct decoded_addr *res, char *msg, int len, bool scrub_err); +void skx_enable_rrl(bool enable); void skx_set_mem_cfg(bool mem_cfg_2lm); void skx_set_res_cfg(struct res_config *cfg); void skx_init_mc_mapping(struct skx_dev *d);