Skip to content

Commit de1a3f5

Browse files
Devarsh Thakkargregkh
authored andcommitted
phy: cadence: cdns-dphy: Fix PLL lock and O_CMN_READY polling
[ Upstream commit 284fb19a3ffb1083c3ad9c00d29749d09dddb99c ] 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: [email protected] Fixes: 7a343c8 ("phy: Add Cadence D-PHY support") Signed-off-by: Devarsh Thakkar <[email protected]> Tested-by: Harikrishna Shenoy <[email protected]> Reviewed-by: Tomi Valkeinen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Vinod Koul <[email protected]> Signed-off-by: Sasha Levin <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent b0d5e35 commit de1a3f5

1 file changed

Lines changed: 92 additions & 32 deletions

File tree

drivers/phy/cadence/cdns-dphy.c

Lines changed: 92 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ struct cdns_dphy_ops {
100100
void (*set_pll_cfg)(struct cdns_dphy *dphy,
101101
const struct cdns_dphy_cfg *cfg);
102102
unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy);
103+
int (*wait_for_pll_lock)(struct cdns_dphy *dphy);
104+
int (*wait_for_cmn_ready)(struct cdns_dphy *dphy);
103105
};
104106

105107
struct cdns_dphy {
@@ -109,6 +111,8 @@ struct cdns_dphy {
109111
struct clk *pll_ref_clk;
110112
const struct cdns_dphy_ops *ops;
111113
struct phy *phy;
114+
bool is_configured;
115+
bool is_powered;
112116
};
113117

114118
/* Order of bands is important since the index is the band number. */
@@ -195,6 +199,16 @@ static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy)
195199
return dphy->ops->get_wakeup_time_ns(dphy);
196200
}
197201

202+
static int cdns_dphy_wait_for_pll_lock(struct cdns_dphy *dphy)
203+
{
204+
return dphy->ops->wait_for_pll_lock ? dphy->ops->wait_for_pll_lock(dphy) : 0;
205+
}
206+
207+
static int cdns_dphy_wait_for_cmn_ready(struct cdns_dphy *dphy)
208+
{
209+
return dphy->ops->wait_for_cmn_ready ? dphy->ops->wait_for_cmn_ready(dphy) : 0;
210+
}
211+
198212
static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy)
199213
{
200214
/* Default wakeup time is 800 ns (in a simulated environment). */
@@ -236,7 +250,6 @@ static unsigned long cdns_dphy_j721e_get_wakeup_time_ns(struct cdns_dphy *dphy)
236250
static void cdns_dphy_j721e_set_pll_cfg(struct cdns_dphy *dphy,
237251
const struct cdns_dphy_cfg *cfg)
238252
{
239-
u32 status;
240253

241254
/*
242255
* set the PWM and PLL Byteclk divider settings to recommended values
@@ -253,20 +266,30 @@ static void cdns_dphy_j721e_set_pll_cfg(struct cdns_dphy *dphy,
253266

254267
writel(DPHY_TX_J721E_WIZ_LANE_RSTB,
255268
dphy->regs + DPHY_TX_J721E_WIZ_RST_CTRL);
256-
257-
readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_PLL_CTRL, status,
258-
(status & DPHY_TX_WIZ_PLL_LOCK), 0, POLL_TIMEOUT_US);
259-
260-
readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_STATUS, status,
261-
(status & DPHY_TX_WIZ_O_CMN_READY), 0,
262-
POLL_TIMEOUT_US);
263269
}
264270

265271
static void cdns_dphy_j721e_set_psm_div(struct cdns_dphy *dphy, u8 div)
266272
{
267273
writel(div, dphy->regs + DPHY_TX_J721E_WIZ_PSM_FREQ);
268274
}
269275

276+
static int cdns_dphy_j721e_wait_for_pll_lock(struct cdns_dphy *dphy)
277+
{
278+
u32 status;
279+
280+
return readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_PLL_CTRL, status,
281+
status & DPHY_TX_WIZ_PLL_LOCK, 0, POLL_TIMEOUT_US);
282+
}
283+
284+
static int cdns_dphy_j721e_wait_for_cmn_ready(struct cdns_dphy *dphy)
285+
{
286+
u32 status;
287+
288+
return readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_STATUS, status,
289+
status & DPHY_TX_WIZ_O_CMN_READY, 0,
290+
POLL_TIMEOUT_US);
291+
}
292+
270293
/*
271294
* This is the reference implementation of DPHY hooks. Specific integration of
272295
* this IP may have to re-implement some of them depending on how they decided
@@ -282,6 +305,8 @@ static const struct cdns_dphy_ops j721e_dphy_ops = {
282305
.get_wakeup_time_ns = cdns_dphy_j721e_get_wakeup_time_ns,
283306
.set_pll_cfg = cdns_dphy_j721e_set_pll_cfg,
284307
.set_psm_div = cdns_dphy_j721e_set_psm_div,
308+
.wait_for_pll_lock = cdns_dphy_j721e_wait_for_pll_lock,
309+
.wait_for_cmn_ready = cdns_dphy_j721e_wait_for_cmn_ready,
285310
};
286311

287312
static int cdns_dphy_config_from_opts(struct phy *phy,
@@ -339,21 +364,36 @@ static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode, int submode,
339364
static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
340365
{
341366
struct cdns_dphy *dphy = phy_get_drvdata(phy);
342-
struct cdns_dphy_cfg cfg = { 0 };
343-
int ret, band_ctrl;
344-
unsigned int reg;
367+
int ret;
345368

346-
ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
347-
if (ret)
348-
return ret;
369+
ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &dphy->cfg);
370+
if (!ret)
371+
dphy->is_configured = true;
372+
373+
return ret;
374+
}
375+
376+
static int cdns_dphy_power_on(struct phy *phy)
377+
{
378+
struct cdns_dphy *dphy = phy_get_drvdata(phy);
379+
int ret;
380+
u32 reg;
381+
382+
if (!dphy->is_configured || dphy->is_powered)
383+
return -EINVAL;
384+
385+
clk_prepare_enable(dphy->psm_clk);
386+
clk_prepare_enable(dphy->pll_ref_clk);
349387

350388
/*
351389
* Configure the internal PSM clk divider so that the DPHY has a
352390
* 1MHz clk (or something close).
353391
*/
354392
ret = cdns_dphy_setup_psm(dphy);
355-
if (ret)
356-
return ret;
393+
if (ret) {
394+
dev_err(&dphy->phy->dev, "Failed to setup PSM with error %d\n", ret);
395+
goto err_power_on;
396+
}
357397

358398
/*
359399
* Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes
@@ -368,40 +408,60 @@ static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
368408
* Configure the DPHY PLL that will be used to generate the TX byte
369409
* clk.
370410
*/
371-
cdns_dphy_set_pll_cfg(dphy, &cfg);
411+
cdns_dphy_set_pll_cfg(dphy, &dphy->cfg);
372412

373-
band_ctrl = cdns_dphy_tx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate);
374-
if (band_ctrl < 0)
375-
return band_ctrl;
413+
ret = cdns_dphy_tx_get_band_ctrl(dphy->cfg.hs_clk_rate);
414+
if (ret < 0) {
415+
dev_err(&dphy->phy->dev, "Failed to get band control value with error %d\n", ret);
416+
goto err_power_on;
417+
}
376418

377-
reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) |
378-
FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl);
419+
reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, ret) |
420+
FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, ret);
379421
writel(reg, dphy->regs + DPHY_BAND_CFG);
380422

381-
return 0;
382-
}
383-
384-
static int cdns_dphy_power_on(struct phy *phy)
385-
{
386-
struct cdns_dphy *dphy = phy_get_drvdata(phy);
387-
388-
clk_prepare_enable(dphy->psm_clk);
389-
clk_prepare_enable(dphy->pll_ref_clk);
390-
391423
/* Start TX state machine. */
392424
writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN,
393425
dphy->regs + DPHY_CMN_SSM);
394426

427+
ret = cdns_dphy_wait_for_pll_lock(dphy);
428+
if (ret) {
429+
dev_err(&dphy->phy->dev, "Failed to lock PLL with error %d\n", ret);
430+
goto err_power_on;
431+
}
432+
433+
ret = cdns_dphy_wait_for_cmn_ready(dphy);
434+
if (ret) {
435+
dev_err(&dphy->phy->dev, "O_CMN_READY signal failed to assert with error %d\n",
436+
ret);
437+
goto err_power_on;
438+
}
439+
440+
dphy->is_powered = true;
441+
395442
return 0;
443+
444+
err_power_on:
445+
clk_disable_unprepare(dphy->pll_ref_clk);
446+
clk_disable_unprepare(dphy->psm_clk);
447+
448+
return ret;
396449
}
397450

398451
static int cdns_dphy_power_off(struct phy *phy)
399452
{
400453
struct cdns_dphy *dphy = phy_get_drvdata(phy);
454+
u32 reg;
401455

402456
clk_disable_unprepare(dphy->pll_ref_clk);
403457
clk_disable_unprepare(dphy->psm_clk);
404458

459+
/* Stop TX state machine. */
460+
reg = readl(dphy->regs + DPHY_CMN_SSM);
461+
writel(reg & ~DPHY_CMN_SSM_EN, dphy->regs + DPHY_CMN_SSM);
462+
463+
dphy->is_powered = false;
464+
405465
return 0;
406466
}
407467

0 commit comments

Comments
 (0)