phy: qcom-snps-femto-v2: properly enable ref clock

The driver is not enabling the ref clock, which thus gets disabled by
the clk_disable_unused() initcall. This leads to the dwc3 controller
failing to initialize if probed after clk_disable_unused() is called,
for instance when the driver is built as a module.

To fix this, switch to the clk_bulk API to handle both cfg_ahb and ref
clocks at the proper places.

Note that the cfg_ahb clock is currently not used by any device tree
instantiation of the PHY. Work needs to be done separately to fix this.

Link: https://lore.kernel.org/linux-arm-msm/ZEqvy+khHeTkC2hf@fedora/
Fixes: 51e8114f80 ("phy: qcom-snps: Add SNPS USB PHY driver for QCOM based SOCs")
Signed-off-by: Adrien Thierry <athierry@redhat.com>
Link: https://lore.kernel.org/r/20230629144542.14906-3-athierry@redhat.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
Adrien Thierry 2023-06-29 10:45:39 -04:00 committed by Vinod Koul
parent 45d89a344e
commit 8a0eb8f9b9
1 changed files with 48 additions and 15 deletions

View File

@ -110,11 +110,13 @@ struct phy_override_seq {
/**
* struct qcom_snps_hsphy - snps hs phy attributes
*
* @dev: device structure
*
* @phy: generic phy
* @base: iomapped memory space for snps hs phy
*
* @cfg_ahb_clk: AHB2PHY interface clock
* @ref_clk: phy reference clock
* @num_clks: number of clocks
* @clks: array of clocks
* @phy_reset: phy reset control
* @vregs: regulator supplies bulk data
* @phy_initialized: if PHY has been initialized correctly
@ -122,11 +124,13 @@ struct phy_override_seq {
* @update_seq_cfg: tuning parameters for phy init
*/
struct qcom_snps_hsphy {
struct device *dev;
struct phy *phy;
void __iomem *base;
struct clk *cfg_ahb_clk;
struct clk *ref_clk;
int num_clks;
struct clk_bulk_data *clks;
struct reset_control *phy_reset;
struct regulator_bulk_data vregs[SNPS_HS_NUM_VREGS];
@ -135,6 +139,34 @@ struct qcom_snps_hsphy {
struct phy_override_seq update_seq_cfg[NUM_HSPHY_TUNING_PARAMS];
};
static int qcom_snps_hsphy_clk_init(struct qcom_snps_hsphy *hsphy)
{
struct device *dev = hsphy->dev;
hsphy->num_clks = 2;
hsphy->clks = devm_kcalloc(dev, hsphy->num_clks, sizeof(*hsphy->clks), GFP_KERNEL);
if (!hsphy->clks)
return -ENOMEM;
/*
* TODO: Currently no device tree instantiation of the PHY is using the clock.
* This needs to be fixed in order for this code to be able to use devm_clk_bulk_get().
*/
hsphy->clks[0].id = "cfg_ahb";
hsphy->clks[0].clk = devm_clk_get_optional(dev, "cfg_ahb");
if (IS_ERR(hsphy->clks[0].clk))
return dev_err_probe(dev, PTR_ERR(hsphy->clks[0].clk),
"failed to get cfg_ahb clk\n");
hsphy->clks[1].id = "ref";
hsphy->clks[1].clk = devm_clk_get(dev, "ref");
if (IS_ERR(hsphy->clks[1].clk))
return dev_err_probe(dev, PTR_ERR(hsphy->clks[1].clk),
"failed to get ref clk\n");
return 0;
}
static inline void qcom_snps_hsphy_write_mask(void __iomem *base, u32 offset,
u32 mask, u32 val)
{
@ -365,16 +397,16 @@ static int qcom_snps_hsphy_init(struct phy *phy)
if (ret)
return ret;
ret = clk_prepare_enable(hsphy->cfg_ahb_clk);
ret = clk_bulk_prepare_enable(hsphy->num_clks, hsphy->clks);
if (ret) {
dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret);
dev_err(&phy->dev, "failed to enable clocks, %d\n", ret);
goto poweroff_phy;
}
ret = reset_control_assert(hsphy->phy_reset);
if (ret) {
dev_err(&phy->dev, "failed to assert phy_reset, %d\n", ret);
goto disable_ahb_clk;
goto disable_clks;
}
usleep_range(100, 150);
@ -382,7 +414,7 @@ static int qcom_snps_hsphy_init(struct phy *phy)
ret = reset_control_deassert(hsphy->phy_reset);
if (ret) {
dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n", ret);
goto disable_ahb_clk;
goto disable_clks;
}
qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_CFG0,
@ -439,8 +471,8 @@ static int qcom_snps_hsphy_init(struct phy *phy)
return 0;
disable_ahb_clk:
clk_disable_unprepare(hsphy->cfg_ahb_clk);
disable_clks:
clk_bulk_disable_unprepare(hsphy->num_clks, hsphy->clks);
poweroff_phy:
regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);
@ -452,7 +484,7 @@ static int qcom_snps_hsphy_exit(struct phy *phy)
struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);
reset_control_assert(hsphy->phy_reset);
clk_disable_unprepare(hsphy->cfg_ahb_clk);
clk_bulk_disable_unprepare(hsphy->num_clks, hsphy->clks);
regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);
hsphy->phy_initialized = false;
@ -545,14 +577,15 @@ static int qcom_snps_hsphy_probe(struct platform_device *pdev)
if (!hsphy)
return -ENOMEM;
hsphy->dev = dev;
hsphy->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hsphy->base))
return PTR_ERR(hsphy->base);
hsphy->ref_clk = devm_clk_get(dev, "ref");
if (IS_ERR(hsphy->ref_clk))
return dev_err_probe(dev, PTR_ERR(hsphy->ref_clk),
"failed to get ref clk\n");
ret = qcom_snps_hsphy_clk_init(hsphy);
if (ret)
return dev_err_probe(dev, ret, "failed to initialize clocks\n");
hsphy->phy_reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
if (IS_ERR(hsphy->phy_reset)) {