aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2015-04-16 04:03:04 -0400
committerKishon Vijay Abraham I <kishon@ti.com>2015-06-01 06:38:24 -0400
commit56301df6bcaaed31e77b8c500ca1b437f46a3158 (patch)
tree52350b538831cacfc25efe2c396ead41faaa592e
parent0d486806ffd0de672f151763b38c19c0a357bf56 (diff)
phy: twl4030-usb: make runtime pm more reliable.
A construct like: if (pm_runtime_suspended(twl->dev)) pm_runtime_get_sync(twl->dev); is against the spirit of the runtime_pm interface as it makes the internal refcounting useless. In this case it is also racy, particularly as 'put_autosuspend' is used to drop a reference. When that happens a timer is started and the device is runtime-suspended after the timeout. If the above code runs in this window, the device will not be found to be suspended so no pm_runtime reference is taken. When the timer expires the device will be suspended, which is against the intention of the code. So be more direct is taking and dropping references. If twl->linkstat is VBUS_VALID or ID_GROUND, then hold a pm_runtime reference, otherwise don't. Define "cable_present()" to test for this condition. Tested-by: Tony Lindgren <tony@atomide.com> Signed-off-by: NeilBrown <neilb@suse.de> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
-rw-r--r--drivers/phy/phy-twl4030-usb.c29
1 files changed, 20 insertions, 9 deletions
diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c
index bc42d6a8939f..3078f80bf520 100644
--- a/drivers/phy/phy-twl4030-usb.c
+++ b/drivers/phy/phy-twl4030-usb.c
@@ -144,6 +144,16 @@
144#define PMBR1 0x0D 144#define PMBR1 0x0D
145#define GPIO_USB_4PIN_ULPI_2430C (3 << 0) 145#define GPIO_USB_4PIN_ULPI_2430C (3 << 0)
146 146
147/*
148 * If VBUS is valid or ID is ground, then we know a
149 * cable is present and we need to be runtime-enabled
150 */
151static inline bool cable_present(enum omap_musb_vbus_id_status stat)
152{
153 return stat == OMAP_MUSB_VBUS_VALID ||
154 stat == OMAP_MUSB_ID_GROUND;
155}
156
147struct twl4030_usb { 157struct twl4030_usb {
148 struct usb_phy phy; 158 struct usb_phy phy;
149 struct device *dev; 159 struct device *dev;
@@ -536,8 +546,10 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
536 546
537 mutex_lock(&twl->lock); 547 mutex_lock(&twl->lock);
538 if (status >= 0 && status != twl->linkstat) { 548 if (status >= 0 && status != twl->linkstat) {
549 status_changed =
550 cable_present(twl->linkstat) !=
551 cable_present(status);
539 twl->linkstat = status; 552 twl->linkstat = status;
540 status_changed = true;
541 } 553 }
542 mutex_unlock(&twl->lock); 554 mutex_unlock(&twl->lock);
543 555
@@ -553,15 +565,11 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
553 * USB_LINK_VBUS state. musb_hdrc won't care until it 565 * USB_LINK_VBUS state. musb_hdrc won't care until it
554 * starts to handle softconnect right. 566 * starts to handle softconnect right.
555 */ 567 */
556 if ((status == OMAP_MUSB_VBUS_VALID) || 568 if (cable_present(status)) {
557 (status == OMAP_MUSB_ID_GROUND)) { 569 pm_runtime_get_sync(twl->dev);
558 if (pm_runtime_suspended(twl->dev))
559 pm_runtime_get_sync(twl->dev);
560 } else { 570 } else {
561 if (pm_runtime_active(twl->dev)) { 571 pm_runtime_mark_last_busy(twl->dev);
562 pm_runtime_mark_last_busy(twl->dev); 572 pm_runtime_put_autosuspend(twl->dev);
563 pm_runtime_put_autosuspend(twl->dev);
564 }
565 } 573 }
566 omap_musb_mailbox(status); 574 omap_musb_mailbox(status);
567 } 575 }
@@ -767,6 +775,9 @@ static int twl4030_usb_remove(struct platform_device *pdev)
767 775
768 /* disable complete OTG block */ 776 /* disable complete OTG block */
769 twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); 777 twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
778
779 if (cable_present(twl->linkstat))
780 pm_runtime_put_noidle(twl->dev);
770 pm_runtime_mark_last_busy(twl->dev); 781 pm_runtime_mark_last_busy(twl->dev);
771 pm_runtime_put(twl->dev); 782 pm_runtime_put(twl->dev);
772 783