aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/phy
diff options
context:
space:
mode:
authorTony Lindgren <tony@atomide.com>2014-08-20 15:07:00 -0400
committerKishon Vijay Abraham I <kishon@ti.com>2014-08-24 08:17:53 -0400
commit96be39ab34b77c6f6f5cd6ae03aac6c6449ee5c4 (patch)
treef92d3c02c670b4dd8696ca75246ebc1fe4dfcc27 /drivers/phy
parent451fd72219dd6f3355e2d036c598544c760ee532 (diff)
usb: phy: twl4030-usb: Fix regressions to runtime PM on omaps
Commit 30a70b026b4cd ("usb: musb: fix obex in g_nokia.ko causing kernel panic") attempted to fix runtime PM handling for PHYs that are on the I2C bus. Commit 3063a12be2b0 ("usb: musb: fix PHY power on/off") then changed things around to enable of PHYs that rely on runtime PM. These changes however broke idling of the PHY and causes at least 100 mW extra power consumption on omaps, which is a lot with the idle power consumption being below 10 mW range on many devices. As calling phy_power_on/off from runtime PM calls in the USB causes complicated issues with I2C connected PHYs, let's just let the PHY do it's own runtime PM as needed. This leaves out the dependency between PHYs and USB controller drivers for runtime PM. Let's fix the regression for twl4030-usb by adding minimal runtime PM support. This allows idling the PHY on disconnect. Note that we are changing to use standard runtime PM handling for twl4030_phy_init() as that function just checks the state and does not initialize the PHY. The PHY won't get initialized until in twl4030_phy_power_on(). Fixes: 30a70b026b4cd ("usb: musb: fix obex in g_nokia.ko causing kernel panic") Fixes: 3063a12be2b0 ("usb: musb: fix PHY power on/off") Cc: stable@vger.kernel.org # v3.15+ Acked-by: Felipe Balbi <balbi@ti.com> Signed-off-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Diffstat (limited to 'drivers/phy')
-rw-r--r--drivers/phy/phy-twl4030-usb.c88
1 files changed, 63 insertions, 25 deletions
diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c
index e1a6623d4696..0cb872bdbe7e 100644
--- a/drivers/phy/phy-twl4030-usb.c
+++ b/drivers/phy/phy-twl4030-usb.c
@@ -34,6 +34,7 @@
34#include <linux/delay.h> 34#include <linux/delay.h>
35#include <linux/usb/otg.h> 35#include <linux/usb/otg.h>
36#include <linux/phy/phy.h> 36#include <linux/phy/phy.h>
37#include <linux/pm_runtime.h>
37#include <linux/usb/musb-omap.h> 38#include <linux/usb/musb-omap.h>
38#include <linux/usb/ulpi.h> 39#include <linux/usb/ulpi.h>
39#include <linux/i2c/twl.h> 40#include <linux/i2c/twl.h>
@@ -422,37 +423,55 @@ static void twl4030_phy_power(struct twl4030_usb *twl, int on)
422 } 423 }
423} 424}
424 425
425static int twl4030_phy_power_off(struct phy *phy) 426static int twl4030_usb_runtime_suspend(struct device *dev)
426{ 427{
427 struct twl4030_usb *twl = phy_get_drvdata(phy); 428 struct twl4030_usb *twl = dev_get_drvdata(dev);
428 429
430 dev_dbg(twl->dev, "%s\n", __func__);
429 if (twl->asleep) 431 if (twl->asleep)
430 return 0; 432 return 0;
431 433
432 twl4030_phy_power(twl, 0); 434 twl4030_phy_power(twl, 0);
433 twl->asleep = 1; 435 twl->asleep = 1;
434 dev_dbg(twl->dev, "%s\n", __func__); 436
435 return 0; 437 return 0;
436} 438}
437 439
438static void __twl4030_phy_power_on(struct twl4030_usb *twl) 440static int twl4030_usb_runtime_resume(struct device *dev)
439{ 441{
442 struct twl4030_usb *twl = dev_get_drvdata(dev);
443
444 dev_dbg(twl->dev, "%s\n", __func__);
445 if (!twl->asleep)
446 return 0;
447
440 twl4030_phy_power(twl, 1); 448 twl4030_phy_power(twl, 1);
441 twl4030_i2c_access(twl, 1); 449 twl->asleep = 0;
442 twl4030_usb_set_mode(twl, twl->usb_mode); 450
443 if (twl->usb_mode == T2_USB_MODE_ULPI) 451 return 0;
444 twl4030_i2c_access(twl, 0); 452}
453
454static int twl4030_phy_power_off(struct phy *phy)
455{
456 struct twl4030_usb *twl = phy_get_drvdata(phy);
457
458 dev_dbg(twl->dev, "%s\n", __func__);
459 pm_runtime_mark_last_busy(twl->dev);
460 pm_runtime_put_autosuspend(twl->dev);
461
462 return 0;
445} 463}
446 464
447static int twl4030_phy_power_on(struct phy *phy) 465static int twl4030_phy_power_on(struct phy *phy)
448{ 466{
449 struct twl4030_usb *twl = phy_get_drvdata(phy); 467 struct twl4030_usb *twl = phy_get_drvdata(phy);
450 468
451 if (!twl->asleep)
452 return 0;
453 __twl4030_phy_power_on(twl);
454 twl->asleep = 0;
455 dev_dbg(twl->dev, "%s\n", __func__); 469 dev_dbg(twl->dev, "%s\n", __func__);
470 pm_runtime_get_sync(twl->dev);
471 twl4030_i2c_access(twl, 1);
472 twl4030_usb_set_mode(twl, twl->usb_mode);
473 if (twl->usb_mode == T2_USB_MODE_ULPI)
474 twl4030_i2c_access(twl, 0);
456 475
457 /* 476 /*
458 * XXX When VBUS gets driven after musb goes to A mode, 477 * XXX When VBUS gets driven after musb goes to A mode,
@@ -558,6 +577,16 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
558 * USB_LINK_VBUS state. musb_hdrc won't care until it 577 * USB_LINK_VBUS state. musb_hdrc won't care until it
559 * starts to handle softconnect right. 578 * starts to handle softconnect right.
560 */ 579 */
580 if ((status == OMAP_MUSB_VBUS_VALID) ||
581 (status == OMAP_MUSB_ID_GROUND)) {
582 if (twl->asleep)
583 pm_runtime_get_sync(twl->dev);
584 } else {
585 if (!twl->asleep) {
586 pm_runtime_mark_last_busy(twl->dev);
587 pm_runtime_put_autosuspend(twl->dev);
588 }
589 }
561 omap_musb_mailbox(status); 590 omap_musb_mailbox(status);
562 } 591 }
563 sysfs_notify(&twl->dev->kobj, NULL, "vbus"); 592 sysfs_notify(&twl->dev->kobj, NULL, "vbus");
@@ -599,22 +628,17 @@ static int twl4030_phy_init(struct phy *phy)
599 struct twl4030_usb *twl = phy_get_drvdata(phy); 628 struct twl4030_usb *twl = phy_get_drvdata(phy);
600 enum omap_musb_vbus_id_status status; 629 enum omap_musb_vbus_id_status status;
601 630
602 /* 631 pm_runtime_get_sync(twl->dev);
603 * Start in sleep state, we'll get called through set_suspend()
604 * callback when musb is runtime resumed and it's time to start.
605 */
606 __twl4030_phy_power(twl, 0);
607 twl->asleep = 1;
608
609 status = twl4030_usb_linkstat(twl); 632 status = twl4030_usb_linkstat(twl);
610 twl->linkstat = status; 633 twl->linkstat = status;
611 634
612 if (status == OMAP_MUSB_ID_GROUND || status == OMAP_MUSB_VBUS_VALID) { 635 if (status == OMAP_MUSB_ID_GROUND || status == OMAP_MUSB_VBUS_VALID)
613 omap_musb_mailbox(twl->linkstat); 636 omap_musb_mailbox(twl->linkstat);
614 twl4030_phy_power_on(phy);
615 }
616 637
617 sysfs_notify(&twl->dev->kobj, NULL, "vbus"); 638 sysfs_notify(&twl->dev->kobj, NULL, "vbus");
639 pm_runtime_mark_last_busy(twl->dev);
640 pm_runtime_put_autosuspend(twl->dev);
641
618 return 0; 642 return 0;
619} 643}
620 644
@@ -650,6 +674,11 @@ static const struct phy_ops ops = {
650 .owner = THIS_MODULE, 674 .owner = THIS_MODULE,
651}; 675};
652 676
677static const struct dev_pm_ops twl4030_usb_pm_ops = {
678 SET_RUNTIME_PM_OPS(twl4030_usb_runtime_suspend,
679 twl4030_usb_runtime_resume, NULL)
680};
681
653static int twl4030_usb_probe(struct platform_device *pdev) 682static int twl4030_usb_probe(struct platform_device *pdev)
654{ 683{
655 struct twl4030_usb_data *pdata = dev_get_platdata(&pdev->dev); 684 struct twl4030_usb_data *pdata = dev_get_platdata(&pdev->dev);
@@ -726,6 +755,11 @@ static int twl4030_usb_probe(struct platform_device *pdev)
726 755
727 ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier); 756 ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier);
728 757
758 pm_runtime_use_autosuspend(&pdev->dev);
759 pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
760 pm_runtime_enable(&pdev->dev);
761 pm_runtime_get_sync(&pdev->dev);
762
729 /* Our job is to use irqs and status from the power module 763 /* Our job is to use irqs and status from the power module
730 * to keep the transceiver disabled when nothing's connected. 764 * to keep the transceiver disabled when nothing's connected.
731 * 765 *
@@ -744,6 +778,9 @@ static int twl4030_usb_probe(struct platform_device *pdev)
744 return status; 778 return status;
745 } 779 }
746 780
781 pm_runtime_mark_last_busy(&pdev->dev);
782 pm_runtime_put_autosuspend(twl->dev);
783
747 dev_info(&pdev->dev, "Initialized TWL4030 USB module\n"); 784 dev_info(&pdev->dev, "Initialized TWL4030 USB module\n");
748 return 0; 785 return 0;
749} 786}
@@ -753,6 +790,7 @@ static int twl4030_usb_remove(struct platform_device *pdev)
753 struct twl4030_usb *twl = platform_get_drvdata(pdev); 790 struct twl4030_usb *twl = platform_get_drvdata(pdev);
754 int val; 791 int val;
755 792
793 pm_runtime_get_sync(twl->dev);
756 cancel_delayed_work(&twl->id_workaround_work); 794 cancel_delayed_work(&twl->id_workaround_work);
757 device_remove_file(twl->dev, &dev_attr_vbus); 795 device_remove_file(twl->dev, &dev_attr_vbus);
758 796
@@ -772,9 +810,8 @@ static int twl4030_usb_remove(struct platform_device *pdev)
772 810
773 /* disable complete OTG block */ 811 /* disable complete OTG block */
774 twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); 812 twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
775 813 pm_runtime_mark_last_busy(twl->dev);
776 if (!twl->asleep) 814 pm_runtime_put(twl->dev);
777 twl4030_phy_power(twl, 0);
778 815
779 return 0; 816 return 0;
780} 817}
@@ -792,6 +829,7 @@ static struct platform_driver twl4030_usb_driver = {
792 .remove = twl4030_usb_remove, 829 .remove = twl4030_usb_remove,
793 .driver = { 830 .driver = {
794 .name = "twl4030_usb", 831 .name = "twl4030_usb",
832 .pm = &twl4030_usb_pm_ops,
795 .owner = THIS_MODULE, 833 .owner = THIS_MODULE,
796 .of_match_table = of_match_ptr(twl4030_usb_id_table), 834 .of_match_table = of_match_ptr(twl4030_usb_id_table),
797 }, 835 },