static int mpc85xx_pci_err_probe()

in mpc85xx_edac.c [144:300]


static int mpc85xx_pci_err_probe(struct platform_device *op)
{
	struct edac_pci_ctl_info *pci;
	struct mpc85xx_pci_pdata *pdata;
	struct mpc85xx_edac_pci_plat_data *plat_data;
	struct device_node *of_node;
	struct resource r;
	int res = 0;

	if (!devres_open_group(&op->dev, mpc85xx_pci_err_probe, GFP_KERNEL))
		return -ENOMEM;

	pci = edac_pci_alloc_ctl_info(sizeof(*pdata), "mpc85xx_pci_err");
	if (!pci)
		return -ENOMEM;

	/* make sure error reporting method is sane */
	switch (edac_op_state) {
	case EDAC_OPSTATE_POLL:
	case EDAC_OPSTATE_INT:
		break;
	default:
		edac_op_state = EDAC_OPSTATE_INT;
		break;
	}

	pdata = pci->pvt_info;
	pdata->name = "mpc85xx_pci_err";

	plat_data = op->dev.platform_data;
	if (!plat_data) {
		dev_err(&op->dev, "no platform data");
		res = -ENXIO;
		goto err;
	}
	of_node = plat_data->of_node;

	if (mpc85xx_pcie_find_capability(of_node) > 0)
		pdata->is_pcie = true;

	dev_set_drvdata(&op->dev, pci);
	pci->dev = &op->dev;
	pci->mod_name = EDAC_MOD_STR;
	pci->ctl_name = pdata->name;
	pci->dev_name = dev_name(&op->dev);

	if (edac_op_state == EDAC_OPSTATE_POLL) {
		if (pdata->is_pcie)
			pci->edac_check = mpc85xx_pcie_check;
		else
			pci->edac_check = mpc85xx_pci_check;
	}

	pdata->edac_idx = edac_pci_idx++;

	res = of_address_to_resource(of_node, 0, &r);
	if (res) {
		pr_err("%s: Unable to get resource for PCI err regs\n", __func__);
		goto err;
	}

	/* we only need the error registers */
	r.start += 0xe00;

	if (!devm_request_mem_region(&op->dev, r.start, resource_size(&r),
					pdata->name)) {
		pr_err("%s: Error while requesting mem region\n", __func__);
		res = -EBUSY;
		goto err;
	}

	pdata->pci_vbase = devm_ioremap(&op->dev, r.start, resource_size(&r));
	if (!pdata->pci_vbase) {
		pr_err("%s: Unable to setup PCI err regs\n", __func__);
		res = -ENOMEM;
		goto err;
	}

	if (pdata->is_pcie) {
		orig_pci_err_cap_dr =
		    in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ADDR);
		out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ADDR, ~0);
		orig_pci_err_en =
		    in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN);
		out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, 0);
	} else {
		orig_pci_err_cap_dr =
		    in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR);

		/* PCI master abort is expected during config cycles */
		out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR, 0x40);

		orig_pci_err_en =
		    in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN);

		/* disable master abort reporting */
		out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, ~0x40);
	}

	/* clear error bits */
	out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, ~0);

	/* reset error capture */
	out_be32(pdata->pci_vbase + MPC85XX_PCI_GAS_TIMR, 0x1);

	if (edac_pci_add_device(pci, pdata->edac_idx) > 0) {
		edac_dbg(3, "failed edac_pci_add_device()\n");
		goto err;
	}

	if (edac_op_state == EDAC_OPSTATE_INT) {
		pdata->irq = irq_of_parse_and_map(of_node, 0);
		res = devm_request_irq(&op->dev, pdata->irq,
				       mpc85xx_pci_isr,
				       IRQF_SHARED,
				       "[EDAC] PCI err", pci);
		if (res < 0) {
			pr_err("%s: Unable to request irq %d for MPC85xx PCI err\n",
				__func__, pdata->irq);
			irq_dispose_mapping(pdata->irq);
			res = -ENODEV;
			goto err2;
		}

		pr_info(EDAC_MOD_STR " acquired irq %d for PCI Err\n",
		       pdata->irq);
	}

	if (pdata->is_pcie) {
		/*
		 * Enable all PCIe error interrupt & error detect except invalid
		 * PEX_CONFIG_ADDR/PEX_CONFIG_DATA access interrupt generation
		 * enable bit and invalid PEX_CONFIG_ADDR/PEX_CONFIG_DATA access
		 * detection enable bit. Because PCIe bus code to initialize and
		 * configure these PCIe devices on booting will use some invalid
		 * PEX_CONFIG_ADDR/PEX_CONFIG_DATA, edac driver prints the much
		 * notice information. So disable this detect to fix ugly print.
		 */
		out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, ~0
			 & ~PEX_ERR_ICCAIE_EN_BIT);
		out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ADDR, 0
			 | PEX_ERR_ICCAD_DISR_BIT);
	}

	devres_remove_group(&op->dev, mpc85xx_pci_err_probe);
	edac_dbg(3, "success\n");
	pr_info(EDAC_MOD_STR " PCI err registered\n");

	return 0;

err2:
	edac_pci_del_device(&op->dev);
err:
	edac_pci_free_ctl_info(pci);
	devres_release_group(&op->dev, mpc85xx_pci_err_probe);
	return res;
}