static int thunderx_lmc_probe()

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;
}