in qualcomm/phy-qcom-qmp.c [6093:6244]
static int qcom_qmp_phy_probe(struct platform_device *pdev)
{
struct qcom_qmp *qmp;
struct device *dev = &pdev->dev;
struct device_node *child;
struct phy_provider *phy_provider;
void __iomem *serdes;
void __iomem *usb_serdes;
void __iomem *dp_serdes = NULL;
const struct qmp_phy_combo_cfg *combo_cfg = NULL;
const struct qmp_phy_cfg *cfg = NULL;
const struct qmp_phy_cfg *usb_cfg = NULL;
const struct qmp_phy_cfg *dp_cfg = NULL;
int num, id, expected_phys;
int ret;
qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL);
if (!qmp)
return -ENOMEM;
qmp->dev = dev;
dev_set_drvdata(dev, qmp);
/* Get the specific init parameters of QMP phy */
cfg = of_device_get_match_data(dev);
if (!cfg) {
const struct of_device_id *match;
match = of_match_device(qcom_qmp_combo_phy_of_match_table, dev);
if (!match)
return -EINVAL;
combo_cfg = match->data;
if (!combo_cfg)
return -EINVAL;
usb_cfg = combo_cfg->usb_cfg;
cfg = usb_cfg; /* Setup clks and regulators */
}
/* per PHY serdes; usually located at base address */
usb_serdes = serdes = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(serdes))
return PTR_ERR(serdes);
/* per PHY dp_com; if PHY has dp_com control block */
if (combo_cfg || cfg->has_phy_dp_com_ctrl) {
qmp->dp_com = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(qmp->dp_com))
return PTR_ERR(qmp->dp_com);
}
if (combo_cfg) {
/* Only two serdes for combo PHY */
dp_serdes = devm_platform_ioremap_resource(pdev, 2);
if (IS_ERR(dp_serdes))
return PTR_ERR(dp_serdes);
dp_cfg = combo_cfg->dp_cfg;
expected_phys = 2;
} else {
expected_phys = cfg->nlanes;
}
mutex_init(&qmp->phy_mutex);
ret = qcom_qmp_phy_clk_init(dev, cfg);
if (ret)
return ret;
ret = qcom_qmp_phy_reset_init(dev, cfg);
if (ret)
return ret;
ret = qcom_qmp_phy_vreg_init(dev, cfg);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to get regulator supplies: %d\n",
ret);
return ret;
}
num = of_get_available_child_count(dev->of_node);
/* do we have a rogue child node ? */
if (num > expected_phys)
return -EINVAL;
qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL);
if (!qmp->phys)
return -ENOMEM;
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
/*
* Prevent runtime pm from being ON by default. Users can enable
* it using power/control in sysfs.
*/
pm_runtime_forbid(dev);
id = 0;
for_each_available_child_of_node(dev->of_node, child) {
if (of_node_name_eq(child, "dp-phy")) {
cfg = dp_cfg;
serdes = dp_serdes;
} else if (of_node_name_eq(child, "usb3-phy")) {
cfg = usb_cfg;
serdes = usb_serdes;
}
/* Create per-lane phy */
ret = qcom_qmp_phy_create(dev, child, id, serdes, cfg);
if (ret) {
dev_err(dev, "failed to create lane%d phy, %d\n",
id, ret);
goto err_node_put;
}
/*
* Register the pipe clock provided by phy.
* See function description to see details of this pipe clock.
*/
if (cfg->type == PHY_TYPE_USB3 || cfg->type == PHY_TYPE_PCIE) {
ret = phy_pipe_clk_register(qmp, child);
if (ret) {
dev_err(qmp->dev,
"failed to register pipe clock source\n");
goto err_node_put;
}
} else if (cfg->type == PHY_TYPE_DP) {
ret = phy_dp_clks_register(qmp, qmp->phys[id], child);
if (ret) {
dev_err(qmp->dev,
"failed to register DP clock source\n");
goto err_node_put;
}
}
id++;
}
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (!IS_ERR(phy_provider))
dev_info(dev, "Registered Qcom-QMP phy\n");
else
pm_runtime_disable(dev);
return PTR_ERR_OR_ZERO(phy_provider);
err_node_put:
pm_runtime_disable(dev);
of_node_put(child);
return ret;
}