Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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: 7a343c8bf4b5 ("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>

authored by

Devarsh Thakkar and committed by
Vinod Koul
284fb19a 356590cd

+92 -32
+92 -32
drivers/phy/cadence/cdns-dphy.c
··· 92 92 void (*set_pll_cfg)(struct cdns_dphy *dphy, 93 93 const struct cdns_dphy_cfg *cfg); 94 94 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); 95 97 }; 96 98 97 99 struct cdns_dphy { ··· 103 101 struct clk *pll_ref_clk; 104 102 const struct cdns_dphy_ops *ops; 105 103 struct phy *phy; 104 + bool is_configured; 105 + bool is_powered; 106 106 }; 107 107 108 108 /* Order of bands is important since the index is the band number. */ ··· 190 186 return dphy->ops->get_wakeup_time_ns(dphy); 191 187 } 192 188 189 + static int cdns_dphy_wait_for_pll_lock(struct cdns_dphy *dphy) 190 + { 191 + return dphy->ops->wait_for_pll_lock ? dphy->ops->wait_for_pll_lock(dphy) : 0; 192 + } 193 + 194 + static int cdns_dphy_wait_for_cmn_ready(struct cdns_dphy *dphy) 195 + { 196 + return dphy->ops->wait_for_cmn_ready ? dphy->ops->wait_for_cmn_ready(dphy) : 0; 197 + } 198 + 193 199 static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy) 194 200 { 195 201 /* Default wakeup time is 800 ns (in a simulated environment). */ ··· 241 227 static void cdns_dphy_j721e_set_pll_cfg(struct cdns_dphy *dphy, 242 228 const struct cdns_dphy_cfg *cfg) 243 229 { 244 - u32 status; 245 230 246 231 /* 247 232 * set the PWM and PLL Byteclk divider settings to recommended values ··· 257 244 258 245 writel(DPHY_TX_J721E_WIZ_LANE_RSTB, 259 246 dphy->regs + DPHY_TX_J721E_WIZ_RST_CTRL); 260 - 261 - readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_PLL_CTRL, status, 262 - (status & DPHY_TX_WIZ_PLL_LOCK), 0, POLL_TIMEOUT_US); 263 - 264 - readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_STATUS, status, 265 - (status & DPHY_TX_WIZ_O_CMN_READY), 0, 266 - POLL_TIMEOUT_US); 267 247 } 268 248 269 249 static void cdns_dphy_j721e_set_psm_div(struct cdns_dphy *dphy, u8 div) 270 250 { 271 251 writel(div, dphy->regs + DPHY_TX_J721E_WIZ_PSM_FREQ); 252 + } 253 + 254 + static int cdns_dphy_j721e_wait_for_pll_lock(struct cdns_dphy *dphy) 255 + { 256 + u32 status; 257 + 258 + return readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_PLL_CTRL, status, 259 + status & DPHY_TX_WIZ_PLL_LOCK, 0, POLL_TIMEOUT_US); 260 + } 261 + 262 + static int cdns_dphy_j721e_wait_for_cmn_ready(struct cdns_dphy *dphy) 263 + { 264 + u32 status; 265 + 266 + return readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_STATUS, status, 267 + status & DPHY_TX_WIZ_O_CMN_READY, 0, 268 + POLL_TIMEOUT_US); 272 269 } 273 270 274 271 /* ··· 296 273 .get_wakeup_time_ns = cdns_dphy_j721e_get_wakeup_time_ns, 297 274 .set_pll_cfg = cdns_dphy_j721e_set_pll_cfg, 298 275 .set_psm_div = cdns_dphy_j721e_set_psm_div, 276 + .wait_for_pll_lock = cdns_dphy_j721e_wait_for_pll_lock, 277 + .wait_for_cmn_ready = cdns_dphy_j721e_wait_for_cmn_ready, 299 278 }; 300 279 301 280 static int cdns_dphy_config_from_opts(struct phy *phy, ··· 353 328 static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts) 354 329 { 355 330 struct cdns_dphy *dphy = phy_get_drvdata(phy); 356 - struct cdns_dphy_cfg cfg = { 0 }; 357 - int ret, band_ctrl; 358 - unsigned int reg; 331 + int ret; 359 332 360 - ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg); 361 - if (ret) 362 - return ret; 333 + ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &dphy->cfg); 334 + if (!ret) 335 + dphy->is_configured = true; 336 + 337 + return ret; 338 + } 339 + 340 + static int cdns_dphy_power_on(struct phy *phy) 341 + { 342 + struct cdns_dphy *dphy = phy_get_drvdata(phy); 343 + int ret; 344 + u32 reg; 345 + 346 + if (!dphy->is_configured || dphy->is_powered) 347 + return -EINVAL; 348 + 349 + clk_prepare_enable(dphy->psm_clk); 350 + clk_prepare_enable(dphy->pll_ref_clk); 363 351 364 352 /* 365 353 * Configure the internal PSM clk divider so that the DPHY has a 366 354 * 1MHz clk (or something close). 367 355 */ 368 356 ret = cdns_dphy_setup_psm(dphy); 369 - if (ret) 370 - return ret; 357 + if (ret) { 358 + dev_err(&dphy->phy->dev, "Failed to setup PSM with error %d\n", ret); 359 + goto err_power_on; 360 + } 371 361 372 362 /* 373 363 * Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes ··· 397 357 * Configure the DPHY PLL that will be used to generate the TX byte 398 358 * clk. 399 359 */ 400 - cdns_dphy_set_pll_cfg(dphy, &cfg); 360 + cdns_dphy_set_pll_cfg(dphy, &dphy->cfg); 401 361 402 - band_ctrl = cdns_dphy_tx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate); 403 - if (band_ctrl < 0) 404 - return band_ctrl; 362 + ret = cdns_dphy_tx_get_band_ctrl(dphy->cfg.hs_clk_rate); 363 + if (ret < 0) { 364 + dev_err(&dphy->phy->dev, "Failed to get band control value with error %d\n", ret); 365 + goto err_power_on; 366 + } 405 367 406 - reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) | 407 - FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl); 368 + reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, ret) | 369 + FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, ret); 408 370 writel(reg, dphy->regs + DPHY_BAND_CFG); 409 - 410 - return 0; 411 - } 412 - 413 - static int cdns_dphy_power_on(struct phy *phy) 414 - { 415 - struct cdns_dphy *dphy = phy_get_drvdata(phy); 416 - 417 - clk_prepare_enable(dphy->psm_clk); 418 - clk_prepare_enable(dphy->pll_ref_clk); 419 371 420 372 /* Start TX state machine. */ 421 373 writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN, 422 374 dphy->regs + DPHY_CMN_SSM); 423 375 376 + ret = cdns_dphy_wait_for_pll_lock(dphy); 377 + if (ret) { 378 + dev_err(&dphy->phy->dev, "Failed to lock PLL with error %d\n", ret); 379 + goto err_power_on; 380 + } 381 + 382 + ret = cdns_dphy_wait_for_cmn_ready(dphy); 383 + if (ret) { 384 + dev_err(&dphy->phy->dev, "O_CMN_READY signal failed to assert with error %d\n", 385 + ret); 386 + goto err_power_on; 387 + } 388 + 389 + dphy->is_powered = true; 390 + 424 391 return 0; 392 + 393 + err_power_on: 394 + clk_disable_unprepare(dphy->pll_ref_clk); 395 + clk_disable_unprepare(dphy->psm_clk); 396 + 397 + return ret; 425 398 } 426 399 427 400 static int cdns_dphy_power_off(struct phy *phy) 428 401 { 429 402 struct cdns_dphy *dphy = phy_get_drvdata(phy); 403 + u32 reg; 430 404 431 405 clk_disable_unprepare(dphy->pll_ref_clk); 432 406 clk_disable_unprepare(dphy->psm_clk); 407 + 408 + /* Stop TX state machine. */ 409 + reg = readl(dphy->regs + DPHY_CMN_SSM); 410 + writel(reg & ~DPHY_CMN_SSM_EN, dphy->regs + DPHY_CMN_SSM); 411 + 412 + dphy->is_powered = false; 433 413 434 414 return 0; 435 415 }