Skip to content

Commit 284fb19

Browse files
Devarsh Thakkarvinodkoul
authored andcommitted
phy: cadence: cdns-dphy: Fix PLL lock and O_CMN_READY polling
PLL lockup and O_CMN_READY assertion can only happen after common state machine gets enabled by programming DPHY_CMN_SSM register, but driver was polling them before the common state machine was enabled which is incorrect. This is as per the DPHY initialization sequence as mentioned in J721E TRM [1] at section "12.7.2.4.1.2.1 Start-up Sequence Timing Diagram". It shows O_CMN_READY polling at the end after common configuration pin setup where the common configuration pin setup step enables state machine as referenced in "Table 12-1533. Common Configuration-Related Setup mentions state machine" To fix this : - Add new function callbacks for polling on PLL lock and O_CMN_READY assertion. - As state machine and clocks get enabled in power_on callback only, move the clock related programming part from configure callback to power_on callback and poll for the PLL lockup and O_CMN_READY assertion after state machine gets enabled. - The configure callback only saves the PLL configuration received from the client driver which will be applied later on in power_on callback. - Add checks to ensure configure is called before power_on and state machine is in disabled state before power_on callback is called. - Disable state machine in power_off so that client driver can re-configure the PLL by following up a power_off, configure, power_on sequence. [1]: https://www.ti.com/lit/zip/spruil1 Cc: stable@vger.kernel.org Fixes: 7a343c8 ("phy: Add Cadence D-PHY support") Signed-off-by: Devarsh Thakkar <devarsht@ti.com> Tested-by: Harikrishna Shenoy <h-shenoy@ti.com> Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> Link: https://lore.kernel.org/r/20250704125915.1224738-2-devarsht@ti.com Signed-off-by: Vinod Koul <vkoul@kernel.org>
1 parent 356590c commit 284fb19

File tree

1 file changed

+92
-32
lines changed

1 file changed

+92
-32
lines changed

drivers/phy/cadence/cdns-dphy.c

Lines changed: 92 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ struct cdns_dphy_ops {
9292
void (*set_pll_cfg)(struct cdns_dphy *dphy,
9393
const struct cdns_dphy_cfg *cfg);
9494
unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy);
95+
int (*wait_for_pll_lock)(struct cdns_dphy *dphy);
96+
int (*wait_for_cmn_ready)(struct cdns_dphy *dphy);
9597
};
9698

9799
struct cdns_dphy {
@@ -101,6 +103,8 @@ struct cdns_dphy {
101103
struct clk *pll_ref_clk;
102104
const struct cdns_dphy_ops *ops;
103105
struct phy *phy;
106+
bool is_configured;
107+
bool is_powered;
104108
};
105109

106110
/* Order of bands is important since the index is the band number. */
@@ -186,6 +190,16 @@ static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy)
186190
return dphy->ops->get_wakeup_time_ns(dphy);
187191
}
188192

193+
static int cdns_dphy_wait_for_pll_lock(struct cdns_dphy *dphy)
194+
{
195+
return dphy->ops->wait_for_pll_lock ? dphy->ops->wait_for_pll_lock(dphy) : 0;
196+
}
197+
198+
static int cdns_dphy_wait_for_cmn_ready(struct cdns_dphy *dphy)
199+
{
200+
return dphy->ops->wait_for_cmn_ready ? dphy->ops->wait_for_cmn_ready(dphy) : 0;
201+
}
202+
189203
static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy)
190204
{
191205
/* Default wakeup time is 800 ns (in a simulated environment). */
@@ -227,7 +241,6 @@ static unsigned long cdns_dphy_j721e_get_wakeup_time_ns(struct cdns_dphy *dphy)
227241
static void cdns_dphy_j721e_set_pll_cfg(struct cdns_dphy *dphy,
228242
const struct cdns_dphy_cfg *cfg)
229243
{
230-
u32 status;
231244

232245
/*
233246
* set the PWM and PLL Byteclk divider settings to recommended values
@@ -244,20 +257,30 @@ static void cdns_dphy_j721e_set_pll_cfg(struct cdns_dphy *dphy,
244257

245258
writel(DPHY_TX_J721E_WIZ_LANE_RSTB,
246259
dphy->regs + DPHY_TX_J721E_WIZ_RST_CTRL);
247-
248-
readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_PLL_CTRL, status,
249-
(status & DPHY_TX_WIZ_PLL_LOCK), 0, POLL_TIMEOUT_US);
250-
251-
readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_STATUS, status,
252-
(status & DPHY_TX_WIZ_O_CMN_READY), 0,
253-
POLL_TIMEOUT_US);
254260
}
255261

256262
static void cdns_dphy_j721e_set_psm_div(struct cdns_dphy *dphy, u8 div)
257263
{
258264
writel(div, dphy->regs + DPHY_TX_J721E_WIZ_PSM_FREQ);
259265
}
260266

267+
static int cdns_dphy_j721e_wait_for_pll_lock(struct cdns_dphy *dphy)
268+
{
269+
u32 status;
270+
271+
return readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_PLL_CTRL, status,
272+
status & DPHY_TX_WIZ_PLL_LOCK, 0, POLL_TIMEOUT_US);
273+
}
274+
275+
static int cdns_dphy_j721e_wait_for_cmn_ready(struct cdns_dphy *dphy)
276+
{
277+
u32 status;
278+
279+
return readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_STATUS, status,
280+
status & DPHY_TX_WIZ_O_CMN_READY, 0,
281+
POLL_TIMEOUT_US);
282+
}
283+
261284
/*
262285
* This is the reference implementation of DPHY hooks. Specific integration of
263286
* this IP may have to re-implement some of them depending on how they decided
@@ -273,6 +296,8 @@ static const struct cdns_dphy_ops j721e_dphy_ops = {
273296
.get_wakeup_time_ns = cdns_dphy_j721e_get_wakeup_time_ns,
274297
.set_pll_cfg = cdns_dphy_j721e_set_pll_cfg,
275298
.set_psm_div = cdns_dphy_j721e_set_psm_div,
299+
.wait_for_pll_lock = cdns_dphy_j721e_wait_for_pll_lock,
300+
.wait_for_cmn_ready = cdns_dphy_j721e_wait_for_cmn_ready,
276301
};
277302

278303
static int cdns_dphy_config_from_opts(struct phy *phy,
@@ -328,21 +353,36 @@ static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode, int submode,
328353
static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
329354
{
330355
struct cdns_dphy *dphy = phy_get_drvdata(phy);
331-
struct cdns_dphy_cfg cfg = { 0 };
332-
int ret, band_ctrl;
333-
unsigned int reg;
356+
int ret;
334357

335-
ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
336-
if (ret)
337-
return ret;
358+
ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &dphy->cfg);
359+
if (!ret)
360+
dphy->is_configured = true;
361+
362+
return ret;
363+
}
364+
365+
static int cdns_dphy_power_on(struct phy *phy)
366+
{
367+
struct cdns_dphy *dphy = phy_get_drvdata(phy);
368+
int ret;
369+
u32 reg;
370+
371+
if (!dphy->is_configured || dphy->is_powered)
372+
return -EINVAL;
373+
374+
clk_prepare_enable(dphy->psm_clk);
375+
clk_prepare_enable(dphy->pll_ref_clk);
338376

339377
/*
340378
* Configure the internal PSM clk divider so that the DPHY has a
341379
* 1MHz clk (or something close).
342380
*/
343381
ret = cdns_dphy_setup_psm(dphy);
344-
if (ret)
345-
return ret;
382+
if (ret) {
383+
dev_err(&dphy->phy->dev, "Failed to setup PSM with error %d\n", ret);
384+
goto err_power_on;
385+
}
346386

347387
/*
348388
* Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes
@@ -357,40 +397,60 @@ static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
357397
* Configure the DPHY PLL that will be used to generate the TX byte
358398
* clk.
359399
*/
360-
cdns_dphy_set_pll_cfg(dphy, &cfg);
400+
cdns_dphy_set_pll_cfg(dphy, &dphy->cfg);
361401

362-
band_ctrl = cdns_dphy_tx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate);
363-
if (band_ctrl < 0)
364-
return band_ctrl;
402+
ret = cdns_dphy_tx_get_band_ctrl(dphy->cfg.hs_clk_rate);
403+
if (ret < 0) {
404+
dev_err(&dphy->phy->dev, "Failed to get band control value with error %d\n", ret);
405+
goto err_power_on;
406+
}
365407

366-
reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) |
367-
FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl);
408+
reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, ret) |
409+
FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, ret);
368410
writel(reg, dphy->regs + DPHY_BAND_CFG);
369411

370-
return 0;
371-
}
372-
373-
static int cdns_dphy_power_on(struct phy *phy)
374-
{
375-
struct cdns_dphy *dphy = phy_get_drvdata(phy);
376-
377-
clk_prepare_enable(dphy->psm_clk);
378-
clk_prepare_enable(dphy->pll_ref_clk);
379-
380412
/* Start TX state machine. */
381413
writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN,
382414
dphy->regs + DPHY_CMN_SSM);
383415

416+
ret = cdns_dphy_wait_for_pll_lock(dphy);
417+
if (ret) {
418+
dev_err(&dphy->phy->dev, "Failed to lock PLL with error %d\n", ret);
419+
goto err_power_on;
420+
}
421+
422+
ret = cdns_dphy_wait_for_cmn_ready(dphy);
423+
if (ret) {
424+
dev_err(&dphy->phy->dev, "O_CMN_READY signal failed to assert with error %d\n",
425+
ret);
426+
goto err_power_on;
427+
}
428+
429+
dphy->is_powered = true;
430+
384431
return 0;
432+
433+
err_power_on:
434+
clk_disable_unprepare(dphy->pll_ref_clk);
435+
clk_disable_unprepare(dphy->psm_clk);
436+
437+
return ret;
385438
}
386439

387440
static int cdns_dphy_power_off(struct phy *phy)
388441
{
389442
struct cdns_dphy *dphy = phy_get_drvdata(phy);
443+
u32 reg;
390444

391445
clk_disable_unprepare(dphy->pll_ref_clk);
392446
clk_disable_unprepare(dphy->psm_clk);
393447

448+
/* Stop TX state machine. */
449+
reg = readl(dphy->regs + DPHY_CMN_SSM);
450+
writel(reg & ~DPHY_CMN_SSM_EN, dphy->regs + DPHY_CMN_SSM);
451+
452+
dphy->is_powered = false;
453+
394454
return 0;
395455
}
396456

0 commit comments

Comments
 (0)