Commit 0629d57b authored by Miquel Raynal's avatar Miquel Raynal Committed by Kishon Vijay Abraham I

phy: mvebu-cp110-comphy: Add clocks support

There is no public clock tree that implies such dependencies between
the MG/MG-core/AXI clocks and the COMPHY IP but accessing the COMPHY
registers while one of the three clocks are disabled stalls the CPU.

This happens if, for instance, the COMPHY driver probe is deferred
(eg. the USB Vbus regulator driver is not yet visible). The MVPP2
driver which also needs these clocks (among others) will
prepare/enable the clocks, then be deferred, and disable/unprepare
them. Next COMPHY lane to be configured would produce an infinite
stall.
Signed-off-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
Tested-by: default avatarGrzegorz Jaszczyk <jaz@semihalf.com>
Signed-off-by: default avatarKishon Vijay Abraham I <kishon@ti.com>
parent 64ea5957
......@@ -5,6 +5,7 @@
* Antoine Tenart <antoine.tenart@free-electrons.com>
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
......@@ -160,6 +161,9 @@ struct mvebu_comphy_priv {
void __iomem *base;
struct regmap *regmap;
struct device *dev;
struct clk *mg_domain_clk;
struct clk *mg_core_clk;
struct clk *axi_clk;
};
struct mvebu_comphy_lane {
......@@ -585,12 +589,72 @@ static struct phy *mvebu_comphy_xlate(struct device *dev,
return phy;
}
static int mvebu_comphy_init_clks(struct mvebu_comphy_priv *priv)
{
int ret;
priv->mg_domain_clk = devm_clk_get(priv->dev, "mg_clk");
if (IS_ERR(priv->mg_domain_clk))
return PTR_ERR(priv->mg_domain_clk);
ret = clk_prepare_enable(priv->mg_domain_clk);
if (ret < 0)
return ret;
priv->mg_core_clk = devm_clk_get(priv->dev, "mg_core_clk");
if (IS_ERR(priv->mg_core_clk)) {
ret = PTR_ERR(priv->mg_core_clk);
goto dis_mg_domain_clk;
}
ret = clk_prepare_enable(priv->mg_core_clk);
if (ret < 0)
goto dis_mg_domain_clk;
priv->axi_clk = devm_clk_get(priv->dev, "axi_clk");
if (IS_ERR(priv->axi_clk)) {
ret = PTR_ERR(priv->axi_clk);
goto dis_mg_core_clk;
}
ret = clk_prepare_enable(priv->axi_clk);
if (ret < 0)
goto dis_mg_core_clk;
return 0;
dis_mg_core_clk:
clk_disable_unprepare(priv->mg_core_clk);
dis_mg_domain_clk:
clk_disable_unprepare(priv->mg_domain_clk);
priv->mg_domain_clk = NULL;
priv->mg_core_clk = NULL;
priv->axi_clk = NULL;
return ret;
};
static void mvebu_comphy_disable_unprepare_clks(struct mvebu_comphy_priv *priv)
{
if (priv->axi_clk)
clk_disable_unprepare(priv->axi_clk);
if (priv->mg_core_clk)
clk_disable_unprepare(priv->mg_core_clk);
if (priv->mg_domain_clk)
clk_disable_unprepare(priv->mg_domain_clk);
}
static int mvebu_comphy_probe(struct platform_device *pdev)
{
struct mvebu_comphy_priv *priv;
struct phy_provider *provider;
struct device_node *child;
struct resource *res;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
......@@ -607,10 +671,20 @@ static int mvebu_comphy_probe(struct platform_device *pdev)
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
/*
* Ignore error if clocks have not been initialized properly for DT
* compatibility reasons.
*/
ret = mvebu_comphy_init_clks(priv);
if (ret) {
if (ret == -EPROBE_DEFER)
return ret;
dev_warn(&pdev->dev, "cannot initialize clocks\n");
}
for_each_available_child_of_node(pdev->dev.of_node, child) {
struct mvebu_comphy_lane *lane;
struct phy *phy;
int ret;
u32 val;
ret = of_property_read_u32(child, "reg", &val);
......@@ -628,13 +702,15 @@ static int mvebu_comphy_probe(struct platform_device *pdev)
lane = devm_kzalloc(&pdev->dev, sizeof(*lane), GFP_KERNEL);
if (!lane) {
of_node_put(child);
return -ENOMEM;
ret = -ENOMEM;
goto disable_clks;
}
phy = devm_phy_create(&pdev->dev, child, &mvebu_comphy_ops);
if (IS_ERR(phy)) {
of_node_put(child);
return PTR_ERR(phy);
ret = PTR_ERR(phy);
goto disable_clks;
}
lane->priv = priv;
......@@ -653,7 +729,13 @@ static int mvebu_comphy_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, priv);
provider = devm_of_phy_provider_register(&pdev->dev,
mvebu_comphy_xlate);
return PTR_ERR_OR_ZERO(provider);
disable_clks:
mvebu_comphy_disable_unprepare_clks(priv);
return ret;
}
static const struct of_device_id mvebu_comphy_of_match_table[] = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment