in cadence/phy-cadence-sierra.c [1334:1475]
static int cdns_sierra_phy_probe(struct platform_device *pdev)
{
struct cdns_sierra_phy *sp;
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
const struct cdns_sierra_data *data;
unsigned int id_value;
int i, ret, node = 0;
void __iomem *base;
struct device_node *dn = dev->of_node, *child;
if (of_get_child_count(dn) == 0)
return -ENODEV;
/* Get init data for this PHY */
data = of_device_get_match_data(dev);
if (!data)
return -EINVAL;
sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL);
if (!sp)
return -ENOMEM;
dev_set_drvdata(dev, sp);
sp->dev = dev;
sp->init_data = data;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base)) {
dev_err(dev, "missing \"reg\"\n");
return PTR_ERR(base);
}
ret = cdns_regmap_init_blocks(sp, base, data->block_offset_shift,
data->reg_offset_shift);
if (ret)
return ret;
ret = cdns_regfield_init(sp);
if (ret)
return ret;
platform_set_drvdata(pdev, sp);
ret = cdns_sierra_phy_get_clocks(sp, dev);
if (ret)
return ret;
ret = cdns_sierra_clk_register(sp);
if (ret)
return ret;
ret = cdns_sierra_phy_get_resets(sp, dev);
if (ret)
goto unregister_clk;
ret = cdns_sierra_phy_enable_clocks(sp);
if (ret)
goto unregister_clk;
/* Enable APB */
reset_control_deassert(sp->apb_rst);
/* Check that PHY is present */
regmap_field_read(sp->macro_id_type, &id_value);
if (sp->init_data->id_value != id_value) {
ret = -EINVAL;
goto clk_disable;
}
sp->autoconf = of_property_read_bool(dn, "cdns,autoconf");
for_each_available_child_of_node(dn, child) {
struct phy *gphy;
if (!(of_node_name_eq(child, "phy") ||
of_node_name_eq(child, "link")))
continue;
sp->phys[node].lnk_rst =
of_reset_control_array_get_exclusive(child);
if (IS_ERR(sp->phys[node].lnk_rst)) {
dev_err(dev, "failed to get reset %s\n",
child->full_name);
ret = PTR_ERR(sp->phys[node].lnk_rst);
goto put_child2;
}
if (!sp->autoconf) {
ret = cdns_sierra_get_optional(&sp->phys[node], child);
if (ret) {
dev_err(dev, "missing property in node %s\n",
child->name);
goto put_child;
}
}
sp->num_lanes += sp->phys[node].num_lanes;
gphy = devm_phy_create(dev, child, &ops);
if (IS_ERR(gphy)) {
ret = PTR_ERR(gphy);
goto put_child;
}
sp->phys[node].phy = gphy;
phy_set_drvdata(gphy, &sp->phys[node]);
node++;
}
sp->nsubnodes = node;
if (sp->num_lanes > SIERRA_MAX_LANES) {
ret = -EINVAL;
dev_err(dev, "Invalid lane configuration\n");
goto put_child2;
}
/* If more than one subnode, configure the PHY as multilink */
if (!sp->autoconf && sp->nsubnodes > 1) {
ret = cdns_sierra_phy_configure_multilink(sp);
if (ret)
goto put_child2;
}
pm_runtime_enable(dev);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
put_child:
node++;
put_child2:
for (i = 0; i < node; i++)
reset_control_put(sp->phys[i].lnk_rst);
of_node_put(child);
clk_disable:
cdns_sierra_phy_disable_clocks(sp);
reset_control_assert(sp->apb_rst);
unregister_clk:
cdns_sierra_clk_unregister(sp);
return ret;
}