in thunderx_edac.c [661:801]
static int thunderx_lmc_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct thunderx_lmc *lmc;
struct edac_mc_layer layer;
struct mem_ctl_info *mci;
u64 lmc_control, lmc_ddr_pll_ctl, lmc_config;
int ret;
u64 lmc_int;
void *l2c_ioaddr;
layer.type = EDAC_MC_LAYER_SLOT;
layer.size = 2;
layer.is_virt_csrow = false;
ret = pcim_enable_device(pdev);
if (ret) {
dev_err(&pdev->dev, "Cannot enable PCI device: %d\n", ret);
return ret;
}
ret = pcim_iomap_regions(pdev, BIT(0), "thunderx_lmc");
if (ret) {
dev_err(&pdev->dev, "Cannot map PCI resources: %d\n", ret);
return ret;
}
mci = edac_mc_alloc(pci_dev_to_mc_idx(pdev), 1, &layer,
sizeof(struct thunderx_lmc));
if (!mci)
return -ENOMEM;
mci->pdev = &pdev->dev;
lmc = mci->pvt_info;
pci_set_drvdata(pdev, mci);
lmc->regs = pcim_iomap_table(pdev)[0];
lmc_control = readq(lmc->regs + LMC_CONTROL);
lmc_ddr_pll_ctl = readq(lmc->regs + LMC_DDR_PLL_CTL);
lmc_config = readq(lmc->regs + LMC_CONFIG);
if (lmc_control & LMC_CONTROL_RDIMM) {
mci->mtype_cap = FIELD_GET(LMC_DDR_PLL_CTL_DDR4,
lmc_ddr_pll_ctl) ?
MEM_RDDR4 : MEM_RDDR3;
} else {
mci->mtype_cap = FIELD_GET(LMC_DDR_PLL_CTL_DDR4,
lmc_ddr_pll_ctl) ?
MEM_DDR4 : MEM_DDR3;
}
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
mci->edac_cap = EDAC_FLAG_SECDED;
mci->mod_name = "thunderx-lmc";
mci->ctl_name = "thunderx-lmc";
mci->dev_name = dev_name(&pdev->dev);
mci->scrub_mode = SCRUB_NONE;
lmc->pdev = pdev;
lmc->msix_ent.entry = 0;
lmc->ring_head = 0;
lmc->ring_tail = 0;
ret = pci_enable_msix_exact(pdev, &lmc->msix_ent, 1);
if (ret) {
dev_err(&pdev->dev, "Cannot enable interrupt: %d\n", ret);
goto err_free;
}
ret = devm_request_threaded_irq(&pdev->dev, lmc->msix_ent.vector,
thunderx_lmc_err_isr,
thunderx_lmc_threaded_isr, 0,
"[EDAC] ThunderX LMC", mci);
if (ret) {
dev_err(&pdev->dev, "Cannot set ISR: %d\n", ret);
goto err_free;
}
lmc->node = FIELD_GET(THUNDERX_NODE, pci_resource_start(pdev, 0));
lmc->xbits = thunderx_get_num_lmcs(lmc->node) >> 1;
lmc->bank_width = (FIELD_GET(LMC_DDR_PLL_CTL_DDR4, lmc_ddr_pll_ctl) &&
FIELD_GET(LMC_CONFIG_BG2, lmc_config)) ? 4 : 3;
lmc->pbank_lsb = (lmc_config >> 5) & 0xf;
lmc->dimm_lsb = 28 + lmc->pbank_lsb + lmc->xbits;
lmc->rank_lsb = lmc->dimm_lsb;
lmc->rank_lsb -= FIELD_GET(LMC_CONFIG_RANK_ENA, lmc_config) ? 1 : 0;
lmc->bank_lsb = 7 + lmc->xbits;
lmc->row_lsb = 14 + LMC_CONFIG_ROW_LSB(lmc_config) + lmc->xbits;
lmc->col_hi_lsb = lmc->bank_lsb + lmc->bank_width;
lmc->xor_bank = lmc_control & LMC_CONTROL_XOR_BANK;
l2c_ioaddr = ioremap(L2C_CTL | FIELD_PREP(THUNDERX_NODE, lmc->node), PAGE_SIZE);
if (!l2c_ioaddr) {
dev_err(&pdev->dev, "Cannot map L2C_CTL\n");
ret = -ENOMEM;
goto err_free;
}
lmc->l2c_alias = !(readq(l2c_ioaddr) & L2C_CTL_DISIDXALIAS);
iounmap(l2c_ioaddr);
ret = edac_mc_add_mc(mci);
if (ret) {
dev_err(&pdev->dev, "Cannot add the MC: %d\n", ret);
goto err_free;
}
lmc_int = readq(lmc->regs + LMC_INT);
writeq(lmc_int, lmc->regs + LMC_INT);
writeq(LMC_INT_ENA_ALL, lmc->regs + LMC_INT_ENA_W1S);
if (IS_ENABLED(CONFIG_EDAC_DEBUG)) {
ret = thunderx_create_debugfs_nodes(mci->debugfs,
lmc_dfs_ents,
lmc,
ARRAY_SIZE(lmc_dfs_ents));
if (ret != ARRAY_SIZE(lmc_dfs_ents)) {
dev_warn(&pdev->dev, "Error creating debugfs entries: %d%s\n",
ret, ret >= 0 ? " created" : "");
}
}
return 0;
err_free:
pci_set_drvdata(pdev, NULL);
edac_mc_free(mci);
return ret;
}