diff options
author | Roger Quadros <rogerq@ti.com> | 2015-01-13 07:23:19 -0500 |
---|---|---|
committer | Kishon Vijay Abraham I <kishon@ti.com> | 2015-01-21 04:53:06 -0500 |
commit | 6e7384320ffa28a3732513f51fc56119003edbd3 (patch) | |
tree | df29d5b9579a5d8aa4ad36dd234af3f920377634 /drivers/phy | |
parent | ec6f34e5b552fb0a52e6aae1a5afbbb1605cc6cc (diff) |
phy: ti-pipe3: Disable clocks on system suspend
On system suspend, the runtime_suspend() driver hook doesn't get
called for USB phy and so the clocks are not disabled in the driver.
This causes the L3INIT_960M_GFCLK and L3INIT_480M_GFCLK to remain
active on the DRA7 platform while in system suspend.
In case of pcie-phy, the runtime_suspend hook gets called after
the suspend hook so we introduce a flag phy->enabled to keep
track if our clocks are enabled or not to prevent multiple
enable/disables.
Add suspend/resume hooks to the driver.
Move enabling/disabling clock code into helper functions.
Reported-by: Nishant Menon <nm@ti.com>
Signed-off-by: Roger Quadros <rogerq@ti.com>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Diffstat (limited to 'drivers/phy')
-rw-r--r-- | drivers/phy/phy-ti-pipe3.c | 99 |
1 files changed, 77 insertions, 22 deletions
diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c index 465de2c800f2..8c854685a1a7 100644 --- a/drivers/phy/phy-ti-pipe3.c +++ b/drivers/phy/phy-ti-pipe3.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/delay.h> | 28 | #include <linux/delay.h> |
29 | #include <linux/phy/omap_control_phy.h> | 29 | #include <linux/phy/omap_control_phy.h> |
30 | #include <linux/of_platform.h> | 30 | #include <linux/of_platform.h> |
31 | #include <linux/spinlock.h> | ||
31 | 32 | ||
32 | #define PLL_STATUS 0x00000004 | 33 | #define PLL_STATUS 0x00000004 |
33 | #define PLL_GO 0x00000008 | 34 | #define PLL_GO 0x00000008 |
@@ -82,6 +83,8 @@ struct ti_pipe3 { | |||
82 | struct clk *refclk; | 83 | struct clk *refclk; |
83 | struct clk *div_clk; | 84 | struct clk *div_clk; |
84 | struct pipe3_dpll_map *dpll_map; | 85 | struct pipe3_dpll_map *dpll_map; |
86 | bool enabled; | ||
87 | spinlock_t lock; /* serialize clock enable/disable */ | ||
85 | }; | 88 | }; |
86 | 89 | ||
87 | static struct pipe3_dpll_map dpll_map_usb[] = { | 90 | static struct pipe3_dpll_map dpll_map_usb[] = { |
@@ -307,6 +310,7 @@ static int ti_pipe3_probe(struct platform_device *pdev) | |||
307 | return -ENOMEM; | 310 | return -ENOMEM; |
308 | 311 | ||
309 | phy->dev = &pdev->dev; | 312 | phy->dev = &pdev->dev; |
313 | spin_lock_init(&phy->lock); | ||
310 | 314 | ||
311 | if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { | 315 | if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { |
312 | match = of_match_device(of_match_ptr(ti_pipe3_id_table), | 316 | match = of_match_device(of_match_ptr(ti_pipe3_id_table), |
@@ -427,24 +431,14 @@ static int ti_pipe3_remove(struct platform_device *pdev) | |||
427 | 431 | ||
428 | #ifdef CONFIG_PM | 432 | #ifdef CONFIG_PM |
429 | 433 | ||
430 | static int ti_pipe3_runtime_suspend(struct device *dev) | 434 | static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy) |
431 | { | 435 | { |
432 | struct ti_pipe3 *phy = dev_get_drvdata(dev); | 436 | int ret = 0; |
433 | 437 | unsigned long flags; | |
434 | if (!IS_ERR(phy->wkupclk)) | ||
435 | clk_disable_unprepare(phy->wkupclk); | ||
436 | if (!IS_ERR(phy->refclk)) | ||
437 | clk_disable_unprepare(phy->refclk); | ||
438 | if (!IS_ERR(phy->div_clk)) | ||
439 | clk_disable_unprepare(phy->div_clk); | ||
440 | |||
441 | return 0; | ||
442 | } | ||
443 | 438 | ||
444 | static int ti_pipe3_runtime_resume(struct device *dev) | 439 | spin_lock_irqsave(&phy->lock, flags); |
445 | { | 440 | if (phy->enabled) |
446 | u32 ret = 0; | 441 | goto err1; |
447 | struct ti_pipe3 *phy = dev_get_drvdata(dev); | ||
448 | 442 | ||
449 | if (!IS_ERR(phy->refclk)) { | 443 | if (!IS_ERR(phy->refclk)) { |
450 | ret = clk_prepare_enable(phy->refclk); | 444 | ret = clk_prepare_enable(phy->refclk); |
@@ -469,6 +463,9 @@ static int ti_pipe3_runtime_resume(struct device *dev) | |||
469 | goto err3; | 463 | goto err3; |
470 | } | 464 | } |
471 | } | 465 | } |
466 | |||
467 | phy->enabled = true; | ||
468 | spin_unlock_irqrestore(&phy->lock, flags); | ||
472 | return 0; | 469 | return 0; |
473 | 470 | ||
474 | err3: | 471 | err3: |
@@ -480,19 +477,77 @@ err2: | |||
480 | clk_disable_unprepare(phy->refclk); | 477 | clk_disable_unprepare(phy->refclk); |
481 | 478 | ||
482 | err1: | 479 | err1: |
480 | spin_unlock_irqrestore(&phy->lock, flags); | ||
481 | return ret; | ||
482 | } | ||
483 | |||
484 | static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy) | ||
485 | { | ||
486 | unsigned long flags; | ||
487 | |||
488 | spin_lock_irqsave(&phy->lock, flags); | ||
489 | if (!phy->enabled) { | ||
490 | spin_unlock_irqrestore(&phy->lock, flags); | ||
491 | return; | ||
492 | } | ||
493 | |||
494 | if (!IS_ERR(phy->wkupclk)) | ||
495 | clk_disable_unprepare(phy->wkupclk); | ||
496 | if (!IS_ERR(phy->refclk)) | ||
497 | clk_disable_unprepare(phy->refclk); | ||
498 | if (!IS_ERR(phy->div_clk)) | ||
499 | clk_disable_unprepare(phy->div_clk); | ||
500 | phy->enabled = false; | ||
501 | spin_unlock_irqrestore(&phy->lock, flags); | ||
502 | } | ||
503 | |||
504 | static int ti_pipe3_runtime_suspend(struct device *dev) | ||
505 | { | ||
506 | struct ti_pipe3 *phy = dev_get_drvdata(dev); | ||
507 | |||
508 | ti_pipe3_disable_clocks(phy); | ||
509 | return 0; | ||
510 | } | ||
511 | |||
512 | static int ti_pipe3_runtime_resume(struct device *dev) | ||
513 | { | ||
514 | struct ti_pipe3 *phy = dev_get_drvdata(dev); | ||
515 | int ret = 0; | ||
516 | |||
517 | ret = ti_pipe3_enable_clocks(phy); | ||
483 | return ret; | 518 | return ret; |
484 | } | 519 | } |
485 | 520 | ||
521 | static int ti_pipe3_suspend(struct device *dev) | ||
522 | { | ||
523 | struct ti_pipe3 *phy = dev_get_drvdata(dev); | ||
524 | |||
525 | ti_pipe3_disable_clocks(phy); | ||
526 | return 0; | ||
527 | } | ||
528 | |||
529 | static int ti_pipe3_resume(struct device *dev) | ||
530 | { | ||
531 | struct ti_pipe3 *phy = dev_get_drvdata(dev); | ||
532 | int ret; | ||
533 | |||
534 | ret = ti_pipe3_enable_clocks(phy); | ||
535 | if (ret) | ||
536 | return ret; | ||
537 | |||
538 | pm_runtime_disable(dev); | ||
539 | pm_runtime_set_active(dev); | ||
540 | pm_runtime_enable(dev); | ||
541 | return 0; | ||
542 | } | ||
543 | #endif | ||
544 | |||
486 | static const struct dev_pm_ops ti_pipe3_pm_ops = { | 545 | static const struct dev_pm_ops ti_pipe3_pm_ops = { |
487 | SET_RUNTIME_PM_OPS(ti_pipe3_runtime_suspend, | 546 | SET_RUNTIME_PM_OPS(ti_pipe3_runtime_suspend, |
488 | ti_pipe3_runtime_resume, NULL) | 547 | ti_pipe3_runtime_resume, NULL) |
548 | SET_SYSTEM_SLEEP_PM_OPS(ti_pipe3_suspend, ti_pipe3_resume) | ||
489 | }; | 549 | }; |
490 | 550 | ||
491 | #define DEV_PM_OPS (&ti_pipe3_pm_ops) | ||
492 | #else | ||
493 | #define DEV_PM_OPS NULL | ||
494 | #endif | ||
495 | |||
496 | #ifdef CONFIG_OF | 551 | #ifdef CONFIG_OF |
497 | static const struct of_device_id ti_pipe3_id_table[] = { | 552 | static const struct of_device_id ti_pipe3_id_table[] = { |
498 | { | 553 | { |
@@ -520,7 +575,7 @@ static struct platform_driver ti_pipe3_driver = { | |||
520 | .remove = ti_pipe3_remove, | 575 | .remove = ti_pipe3_remove, |
521 | .driver = { | 576 | .driver = { |
522 | .name = "ti-pipe3", | 577 | .name = "ti-pipe3", |
523 | .pm = DEV_PM_OPS, | 578 | .pm = &ti_pipe3_pm_ops, |
524 | .of_match_table = of_match_ptr(ti_pipe3_id_table), | 579 | .of_match_table = of_match_ptr(ti_pipe3_id_table), |
525 | }, | 580 | }, |
526 | }; | 581 | }; |