aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/phy
diff options
context:
space:
mode:
authorGrazvydas Ignotas <notasas@gmail.com>2013-03-17 14:23:24 -0400
committerFelipe Balbi <balbi@ti.com>2013-03-21 08:20:30 -0400
commit249751f22380386042056cce6a73c934f5b942a3 (patch)
tree4bf864c472d2476cd48245de9b0d61183f23485e /drivers/usb/phy
parent15460de7f3d9cf0846af1cfdb4a3995d2f270ce7 (diff)
usb: phy: twl4030-usb: poll for ID disconnect
On pandora, STS_USB interrupt doesn't arrive on USB host cable disconnect for some reason while VBUS is driven by twl itself, but STS_HW_CONDITIONS is updated correctly. It does work fine when PHY is powered down though. To work around that we have to poll. This patch also moves twl->linkstat update code to callers so that changes can be handled in thread safe way (as polling work can trigger at the same time as real irq now). TI PSP kernels have similar workarounds, so (many?) more boards are likely affected. Signed-off-by: Grazvydas Ignotas <notasas@gmail.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/phy')
-rw-r--r--drivers/usb/phy/phy-twl4030-usb.c64
1 files changed, 57 insertions, 7 deletions
diff --git a/drivers/usb/phy/phy-twl4030-usb.c b/drivers/usb/phy/phy-twl4030-usb.c
index f4ec53a58103..61fe7e29c9c3 100644
--- a/drivers/usb/phy/phy-twl4030-usb.c
+++ b/drivers/usb/phy/phy-twl4030-usb.c
@@ -163,6 +163,8 @@ struct twl4030_usb {
163 bool vbus_supplied; 163 bool vbus_supplied;
164 u8 asleep; 164 u8 asleep;
165 bool irq_enabled; 165 bool irq_enabled;
166
167 struct delayed_work id_workaround_work;
166}; 168};
167 169
168/* internal define on top of container_of */ 170/* internal define on top of container_of */
@@ -287,10 +289,6 @@ static enum omap_musb_vbus_id_status
287 * are registered, and that both are active... 289 * are registered, and that both are active...
288 */ 290 */
289 291
290 spin_lock_irq(&twl->lock);
291 twl->linkstat = linkstat;
292 spin_unlock_irq(&twl->lock);
293
294 return linkstat; 292 return linkstat;
295} 293}
296 294
@@ -412,6 +410,16 @@ static void twl4030_phy_resume(struct twl4030_usb *twl)
412 __twl4030_phy_resume(twl); 410 __twl4030_phy_resume(twl);
413 twl->asleep = 0; 411 twl->asleep = 0;
414 dev_dbg(twl->dev, "%s\n", __func__); 412 dev_dbg(twl->dev, "%s\n", __func__);
413
414 /*
415 * XXX When VBUS gets driven after musb goes to A mode,
416 * ID_PRES related interrupts no longer arrive, why?
417 * Register itself is updated fine though, so we must poll.
418 */
419 if (twl->linkstat == OMAP_MUSB_ID_GROUND) {
420 cancel_delayed_work(&twl->id_workaround_work);
421 schedule_delayed_work(&twl->id_workaround_work, HZ);
422 }
415} 423}
416 424
417static int twl4030_usb_ldo_init(struct twl4030_usb *twl) 425static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
@@ -483,10 +491,18 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
483{ 491{
484 struct twl4030_usb *twl = _twl; 492 struct twl4030_usb *twl = _twl;
485 enum omap_musb_vbus_id_status status; 493 enum omap_musb_vbus_id_status status;
486 enum omap_musb_vbus_id_status status_prev = twl->linkstat; 494 bool status_changed = false;
487 495
488 status = twl4030_usb_linkstat(twl); 496 status = twl4030_usb_linkstat(twl);
489 if (status > 0 && status != status_prev) { 497
498 spin_lock_irq(&twl->lock);
499 if (status >= 0 && status != twl->linkstat) {
500 twl->linkstat = status;
501 status_changed = true;
502 }
503 spin_unlock_irq(&twl->lock);
504
505 if (status_changed) {
490 /* FIXME add a set_power() method so that B-devices can 506 /* FIXME add a set_power() method so that B-devices can
491 * configure the charger appropriately. It's not always 507 * configure the charger appropriately. It's not always
492 * correct to consume VBUS power, and how much current to 508 * correct to consume VBUS power, and how much current to
@@ -498,13 +514,42 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
498 * USB_LINK_VBUS state. musb_hdrc won't care until it 514 * USB_LINK_VBUS state. musb_hdrc won't care until it
499 * starts to handle softconnect right. 515 * starts to handle softconnect right.
500 */ 516 */
501 omap_musb_mailbox(twl->linkstat); 517 omap_musb_mailbox(status);
502 } 518 }
503 sysfs_notify(&twl->dev->kobj, NULL, "vbus"); 519 sysfs_notify(&twl->dev->kobj, NULL, "vbus");
504 520
505 return IRQ_HANDLED; 521 return IRQ_HANDLED;
506} 522}
507 523
524static void twl4030_id_workaround_work(struct work_struct *work)
525{
526 struct twl4030_usb *twl = container_of(work, struct twl4030_usb,
527 id_workaround_work.work);
528 enum omap_musb_vbus_id_status status;
529 bool status_changed = false;
530
531 status = twl4030_usb_linkstat(twl);
532
533 spin_lock_irq(&twl->lock);
534 if (status >= 0 && status != twl->linkstat) {
535 twl->linkstat = status;
536 status_changed = true;
537 }
538 spin_unlock_irq(&twl->lock);
539
540 if (status_changed) {
541 dev_dbg(twl->dev, "handle missing status change to %d\n",
542 status);
543 omap_musb_mailbox(status);
544 }
545
546 /* don't schedule during sleep - irq works right then */
547 if (status == OMAP_MUSB_ID_GROUND && !twl->asleep) {
548 cancel_delayed_work(&twl->id_workaround_work);
549 schedule_delayed_work(&twl->id_workaround_work, HZ);
550 }
551}
552
508static int twl4030_usb_phy_init(struct usb_phy *phy) 553static int twl4030_usb_phy_init(struct usb_phy *phy)
509{ 554{
510 struct twl4030_usb *twl = phy_to_twl(phy); 555 struct twl4030_usb *twl = phy_to_twl(phy);
@@ -518,6 +563,8 @@ static int twl4030_usb_phy_init(struct usb_phy *phy)
518 twl->asleep = 1; 563 twl->asleep = 1;
519 564
520 status = twl4030_usb_linkstat(twl); 565 status = twl4030_usb_linkstat(twl);
566 twl->linkstat = status;
567
521 if (status == OMAP_MUSB_ID_GROUND || status == OMAP_MUSB_VBUS_VALID) 568 if (status == OMAP_MUSB_ID_GROUND || status == OMAP_MUSB_VBUS_VALID)
522 omap_musb_mailbox(twl->linkstat); 569 omap_musb_mailbox(twl->linkstat);
523 570
@@ -608,6 +655,8 @@ static int twl4030_usb_probe(struct platform_device *pdev)
608 /* init spinlock for workqueue */ 655 /* init spinlock for workqueue */
609 spin_lock_init(&twl->lock); 656 spin_lock_init(&twl->lock);
610 657
658 INIT_DELAYED_WORK(&twl->id_workaround_work, twl4030_id_workaround_work);
659
611 err = twl4030_usb_ldo_init(twl); 660 err = twl4030_usb_ldo_init(twl);
612 if (err) { 661 if (err) {
613 dev_err(&pdev->dev, "ldo init failed\n"); 662 dev_err(&pdev->dev, "ldo init failed\n");
@@ -646,6 +695,7 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev)
646 struct twl4030_usb *twl = platform_get_drvdata(pdev); 695 struct twl4030_usb *twl = platform_get_drvdata(pdev);
647 int val; 696 int val;
648 697
698 cancel_delayed_work(&twl->id_workaround_work);
649 device_remove_file(twl->dev, &dev_attr_vbus); 699 device_remove_file(twl->dev, &dev_attr_vbus);
650 700
651 /* set transceiver mode to power on defaults */ 701 /* set transceiver mode to power on defaults */