static int wcnss_probe()

in qcom_wcnss.c [518:649]


static int wcnss_probe(struct platform_device *pdev)
{
	const char *fw_name = WCNSS_FIRMWARE_NAME;
	const struct wcnss_data *data;
	struct qcom_wcnss *wcnss;
	struct resource *res;
	struct rproc *rproc;
	void __iomem *mmio;
	int ret;

	data = of_device_get_match_data(&pdev->dev);

	if (!qcom_scm_is_available())
		return -EPROBE_DEFER;

	if (!qcom_scm_pas_supported(WCNSS_PAS_ID)) {
		dev_err(&pdev->dev, "PAS is not available for WCNSS\n");
		return -ENXIO;
	}

	ret = of_property_read_string(pdev->dev.of_node, "firmware-name",
				      &fw_name);
	if (ret < 0 && ret != -EINVAL)
		return ret;

	rproc = rproc_alloc(&pdev->dev, pdev->name, &wcnss_ops,
			    fw_name, sizeof(*wcnss));
	if (!rproc) {
		dev_err(&pdev->dev, "unable to allocate remoteproc\n");
		return -ENOMEM;
	}
	rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);

	wcnss = (struct qcom_wcnss *)rproc->priv;
	wcnss->dev = &pdev->dev;
	wcnss->rproc = rproc;
	platform_set_drvdata(pdev, wcnss);

	init_completion(&wcnss->start_done);
	init_completion(&wcnss->stop_done);

	mutex_init(&wcnss->iris_lock);

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmu");
	mmio = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(mmio)) {
		ret = PTR_ERR(mmio);
		goto free_rproc;
	}

	ret = wcnss_alloc_memory_region(wcnss);
	if (ret)
		goto free_rproc;

	wcnss->pmu_cfg = mmio + data->pmu_offset;
	wcnss->spare_out = mmio + data->spare_offset;

	/*
	 * We might need to fallback to regulators instead of power domains
	 * for old device trees. Don't report an error in that case.
	 */
	ret = wcnss_init_pds(wcnss, data->pd_names);
	if (ret && (ret != -ENODATA || !data->num_pd_vregs))
		goto free_rproc;

	ret = wcnss_init_regulators(wcnss, data->vregs, data->num_vregs,
				    data->num_pd_vregs);
	if (ret)
		goto detach_pds;

	ret = wcnss_request_irq(wcnss, pdev, "wdog", false, wcnss_wdog_interrupt);
	if (ret < 0)
		goto detach_pds;
	wcnss->wdog_irq = ret;

	ret = wcnss_request_irq(wcnss, pdev, "fatal", false, wcnss_fatal_interrupt);
	if (ret < 0)
		goto detach_pds;
	wcnss->fatal_irq = ret;

	ret = wcnss_request_irq(wcnss, pdev, "ready", true, wcnss_ready_interrupt);
	if (ret < 0)
		goto detach_pds;
	wcnss->ready_irq = ret;

	ret = wcnss_request_irq(wcnss, pdev, "handover", true, wcnss_handover_interrupt);
	if (ret < 0)
		goto detach_pds;
	wcnss->handover_irq = ret;

	ret = wcnss_request_irq(wcnss, pdev, "stop-ack", true, wcnss_stop_ack_interrupt);
	if (ret < 0)
		goto detach_pds;
	wcnss->stop_ack_irq = ret;

	if (wcnss->stop_ack_irq) {
		wcnss->state = devm_qcom_smem_state_get(&pdev->dev, "stop",
							&wcnss->stop_bit);
		if (IS_ERR(wcnss->state)) {
			ret = PTR_ERR(wcnss->state);
			goto detach_pds;
		}
	}

	qcom_add_smd_subdev(rproc, &wcnss->smd_subdev);
	wcnss->sysmon = qcom_add_sysmon_subdev(rproc, "wcnss", WCNSS_SSCTL_ID);
	if (IS_ERR(wcnss->sysmon)) {
		ret = PTR_ERR(wcnss->sysmon);
		goto detach_pds;
	}

	wcnss->iris = qcom_iris_probe(&pdev->dev, &wcnss->use_48mhz_xo);
	if (IS_ERR(wcnss->iris)) {
		ret = PTR_ERR(wcnss->iris);
		goto detach_pds;
	}

	ret = rproc_add(rproc);
	if (ret)
		goto remove_iris;

	return 0;

remove_iris:
	qcom_iris_remove(wcnss->iris);
detach_pds:
	wcnss_release_pds(wcnss);
free_rproc:
	rproc_free(rproc);

	return ret;
}