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