forked from Minki/linux
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: Miquel Raynal <miquel.raynal@bootlin.com> Tested-by: Grzegorz Jaszczyk <jaz@semihalf.com> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
This commit is contained in:
parent
64ea59577f
commit
0629d57bbd
@ -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[] = {
|
||||
|
Loading…
Reference in New Issue
Block a user