From: Faiz Abbas <faiz_abbas@xxxxxx> Serdes lanes might be shared between multiple cores in some usecases and its not possible to lock PLLs for both the lanes independently by the two cores. This requires a bootloader to configure both the lanes at early boot time. To handle this case, skip all configuration if any of the plls are already locked. This is done by adding an already_configured flag and using it to gate every register access as well as any phy_ops. Signed-off-by: Faiz Abbas <faiz_abbas@xxxxxx> Signed-off-by: Kishon Vijay Abraham I <kishon@xxxxxx> --- drivers/phy/cadence/phy-cadence-sierra.c | 127 ++++++++++++++--------- 1 file changed, 78 insertions(+), 49 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index e08548417bce..145e42837b7b 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -364,6 +364,10 @@ static const struct phy_ops ops = { .owner = THIS_MODULE, }; +static const struct phy_ops noop_ops = { + .owner = THIS_MODULE, +}; + static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst, struct device_node *child) { @@ -477,6 +481,49 @@ static int cdns_regmap_init_blocks(struct cdns_sierra_phy *sp, return 0; } +static int cdns_sierra_phy_get_clocks(struct cdns_sierra_phy *sp, + struct device *dev) +{ + struct clk *clk; + int ret; + + sp->clk = devm_clk_get_optional(dev, "phy_clk"); + if (IS_ERR(sp->clk)) { + dev_err(dev, "failed to get clock phy_clk\n"); + return PTR_ERR(sp->clk); + } + + sp->phy_rst = devm_reset_control_get(dev, "sierra_reset"); + if (IS_ERR(sp->phy_rst)) { + dev_err(dev, "failed to get reset\n"); + return PTR_ERR(sp->phy_rst); + } + + sp->apb_rst = devm_reset_control_get_optional(dev, "sierra_apb"); + if (IS_ERR(sp->apb_rst)) { + dev_err(dev, "failed to get apb reset\n"); + return PTR_ERR(sp->apb_rst); + } + + clk = devm_clk_get_optional(dev, "cmn_refclk_dig_div"); + if (IS_ERR(clk)) { + dev_err(dev, "cmn_refclk_dig_div clock not found\n"); + ret = PTR_ERR(clk); + return ret; + } + sp->cmn_refclk_dig_div = clk; + + clk = devm_clk_get_optional(dev, "cmn_refclk1_dig_div"); + if (IS_ERR(clk)) { + dev_err(dev, "cmn_refclk1_dig_div clock not found\n"); + ret = PTR_ERR(clk); + return ret; + } + sp->cmn_refclk1_dig_div = clk; + + return 0; +} + static int cdns_sierra_phy_probe(struct platform_device *pdev) { struct cdns_sierra_phy *sp; @@ -486,10 +533,10 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) struct cdns_sierra_data *data; unsigned int id_value; struct resource *res; - int i, ret, node = 0; + int i, val, ret, node = 0; void __iomem *base; - struct clk *clk; struct device_node *dn = dev->of_node, *child; + bool already_configured = false; if (of_get_child_count(dn) == 0) return -ENODEV; @@ -524,54 +571,33 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) if (ret) return ret; - platform_set_drvdata(pdev, sp); - - sp->clk = devm_clk_get_optional(dev, "phy_clk"); - if (IS_ERR(sp->clk)) { - dev_err(dev, "failed to get clock phy_clk\n"); - return PTR_ERR(sp->clk); - } - - sp->phy_rst = devm_reset_control_get(dev, "sierra_reset"); - if (IS_ERR(sp->phy_rst)) { - dev_err(dev, "failed to get reset\n"); - return PTR_ERR(sp->phy_rst); - } - - sp->apb_rst = devm_reset_control_get_optional(dev, "sierra_apb"); - if (IS_ERR(sp->apb_rst)) { - dev_err(dev, "failed to get apb reset\n"); - return PTR_ERR(sp->apb_rst); - } - - clk = devm_clk_get_optional(dev, "cmn_refclk_dig_div"); - if (IS_ERR(clk)) { - dev_err(dev, "cmn_refclk_dig_div clock not found\n"); - ret = PTR_ERR(clk); - return ret; - } - sp->cmn_refclk_dig_div = clk; - - clk = devm_clk_get_optional(dev, "cmn_refclk1_dig_div"); - if (IS_ERR(clk)) { - dev_err(dev, "cmn_refclk1_dig_div clock not found\n"); - ret = PTR_ERR(clk); - return ret; + for (i = 0; i < SIERRA_MAX_LANES; i++) { + regmap_field_read(sp->pllctrl_lock[i], &val); + if (val) { + already_configured = true; + break; + } } - sp->cmn_refclk1_dig_div = clk; - ret = clk_prepare_enable(sp->clk); - if (ret) - return ret; - - /* Enable APB */ - reset_control_deassert(sp->apb_rst); + platform_set_drvdata(pdev, sp); - /* 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; + if (!already_configured) { + ret = cdns_sierra_phy_get_clocks(sp, dev); + if (ret) + return ret; + + ret = clk_prepare_enable(sp->clk); + if (ret) + return ret; + /* 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"); @@ -603,7 +629,10 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) sp->num_lanes += sp->phys[node].num_lanes; - gphy = devm_phy_create(dev, child, &ops); + if (already_configured) + gphy = devm_phy_create(dev, child, &noop_ops); + else + gphy = devm_phy_create(dev, child, &ops); if (IS_ERR(gphy)) { ret = PTR_ERR(gphy); @@ -622,7 +651,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) } /* If more than one subnode, configure the PHY as multilink */ - if (!sp->autoconf && sp->nsubnodes > 1) + if (!sp->autoconf && sp->nsubnodes > 1 && !already_configured) regmap_field_write(sp->phy_pll_cfg_1, 0x1); pm_runtime_enable(dev); -- 2.17.1