diff options
Diffstat (limited to 'drivers/phy/phy-twl4030-usb.c')
-rw-r--r-- | drivers/phy/phy-twl4030-usb.c | 125 |
1 files changed, 46 insertions, 79 deletions
diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 9cd33a4bcfb1..7b04befd5271 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c | |||
@@ -28,7 +28,6 @@ | |||
28 | #include <linux/init.h> | 28 | #include <linux/init.h> |
29 | #include <linux/interrupt.h> | 29 | #include <linux/interrupt.h> |
30 | #include <linux/platform_device.h> | 30 | #include <linux/platform_device.h> |
31 | #include <linux/spinlock.h> | ||
32 | #include <linux/workqueue.h> | 31 | #include <linux/workqueue.h> |
33 | #include <linux/io.h> | 32 | #include <linux/io.h> |
34 | #include <linux/delay.h> | 33 | #include <linux/delay.h> |
@@ -155,7 +154,7 @@ struct twl4030_usb { | |||
155 | struct regulator *usb3v1; | 154 | struct regulator *usb3v1; |
156 | 155 | ||
157 | /* for vbus reporting with irqs disabled */ | 156 | /* for vbus reporting with irqs disabled */ |
158 | spinlock_t lock; | 157 | struct mutex lock; |
159 | 158 | ||
160 | /* pin configuration */ | 159 | /* pin configuration */ |
161 | enum twl4030_usb_mode usb_mode; | 160 | enum twl4030_usb_mode usb_mode; |
@@ -163,8 +162,6 @@ struct twl4030_usb { | |||
163 | int irq; | 162 | int irq; |
164 | enum omap_musb_vbus_id_status linkstat; | 163 | enum omap_musb_vbus_id_status linkstat; |
165 | bool vbus_supplied; | 164 | bool vbus_supplied; |
166 | u8 asleep; | ||
167 | bool irq_enabled; | ||
168 | 165 | ||
169 | struct delayed_work id_workaround_work; | 166 | struct delayed_work id_workaround_work; |
170 | }; | 167 | }; |
@@ -384,55 +381,18 @@ static void __twl4030_phy_power(struct twl4030_usb *twl, int on) | |||
384 | WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); | 381 | WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); |
385 | } | 382 | } |
386 | 383 | ||
387 | static void twl4030_phy_power(struct twl4030_usb *twl, int on) | ||
388 | { | ||
389 | int ret; | ||
390 | |||
391 | if (on) { | ||
392 | ret = regulator_enable(twl->usb3v1); | ||
393 | if (ret) | ||
394 | dev_err(twl->dev, "Failed to enable usb3v1\n"); | ||
395 | |||
396 | ret = regulator_enable(twl->usb1v8); | ||
397 | if (ret) | ||
398 | dev_err(twl->dev, "Failed to enable usb1v8\n"); | ||
399 | |||
400 | /* | ||
401 | * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP | ||
402 | * in twl4030) resets the VUSB_DEDICATED2 register. This reset | ||
403 | * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to | ||
404 | * SLEEP. We work around this by clearing the bit after usv3v1 | ||
405 | * is re-activated. This ensures that VUSB3V1 is really active. | ||
406 | */ | ||
407 | twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2); | ||
408 | |||
409 | ret = regulator_enable(twl->usb1v5); | ||
410 | if (ret) | ||
411 | dev_err(twl->dev, "Failed to enable usb1v5\n"); | ||
412 | |||
413 | __twl4030_phy_power(twl, 1); | ||
414 | twl4030_usb_write(twl, PHY_CLK_CTRL, | ||
415 | twl4030_usb_read(twl, PHY_CLK_CTRL) | | ||
416 | (PHY_CLK_CTRL_CLOCKGATING_EN | | ||
417 | PHY_CLK_CTRL_CLK32K_EN)); | ||
418 | } else { | ||
419 | __twl4030_phy_power(twl, 0); | ||
420 | regulator_disable(twl->usb1v5); | ||
421 | regulator_disable(twl->usb1v8); | ||
422 | regulator_disable(twl->usb3v1); | ||
423 | } | ||
424 | } | ||
425 | |||
426 | static int twl4030_usb_runtime_suspend(struct device *dev) | 384 | static int twl4030_usb_runtime_suspend(struct device *dev) |
427 | { | 385 | { |
428 | struct twl4030_usb *twl = dev_get_drvdata(dev); | 386 | struct twl4030_usb *twl = dev_get_drvdata(dev); |
429 | 387 | ||
430 | dev_dbg(twl->dev, "%s\n", __func__); | 388 | dev_dbg(twl->dev, "%s\n", __func__); |
431 | if (twl->asleep) | 389 | if (pm_runtime_suspended(dev)) |
432 | return 0; | 390 | return 0; |
433 | 391 | ||
434 | twl4030_phy_power(twl, 0); | 392 | __twl4030_phy_power(twl, 0); |
435 | twl->asleep = 1; | 393 | regulator_disable(twl->usb1v5); |
394 | regulator_disable(twl->usb1v8); | ||
395 | regulator_disable(twl->usb3v1); | ||
436 | 396 | ||
437 | return 0; | 397 | return 0; |
438 | } | 398 | } |
@@ -440,13 +400,38 @@ static int twl4030_usb_runtime_suspend(struct device *dev) | |||
440 | static int twl4030_usb_runtime_resume(struct device *dev) | 400 | static int twl4030_usb_runtime_resume(struct device *dev) |
441 | { | 401 | { |
442 | struct twl4030_usb *twl = dev_get_drvdata(dev); | 402 | struct twl4030_usb *twl = dev_get_drvdata(dev); |
403 | int res; | ||
443 | 404 | ||
444 | dev_dbg(twl->dev, "%s\n", __func__); | 405 | dev_dbg(twl->dev, "%s\n", __func__); |
445 | if (!twl->asleep) | 406 | if (pm_runtime_active(dev)) |
446 | return 0; | 407 | return 0; |
447 | 408 | ||
448 | twl4030_phy_power(twl, 1); | 409 | res = regulator_enable(twl->usb3v1); |
449 | twl->asleep = 0; | 410 | if (res) |
411 | dev_err(twl->dev, "Failed to enable usb3v1\n"); | ||
412 | |||
413 | res = regulator_enable(twl->usb1v8); | ||
414 | if (res) | ||
415 | dev_err(twl->dev, "Failed to enable usb1v8\n"); | ||
416 | |||
417 | /* | ||
418 | * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP | ||
419 | * in twl4030) resets the VUSB_DEDICATED2 register. This reset | ||
420 | * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to | ||
421 | * SLEEP. We work around this by clearing the bit after usv3v1 | ||
422 | * is re-activated. This ensures that VUSB3V1 is really active. | ||
423 | */ | ||
424 | twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2); | ||
425 | |||
426 | res = regulator_enable(twl->usb1v5); | ||
427 | if (res) | ||
428 | dev_err(twl->dev, "Failed to enable usb1v5\n"); | ||
429 | |||
430 | __twl4030_phy_power(twl, 1); | ||
431 | twl4030_usb_write(twl, PHY_CLK_CTRL, | ||
432 | twl4030_usb_read(twl, PHY_CLK_CTRL) | | ||
433 | (PHY_CLK_CTRL_CLOCKGATING_EN | | ||
434 | PHY_CLK_CTRL_CLK32K_EN)); | ||
450 | 435 | ||
451 | return 0; | 436 | return 0; |
452 | } | 437 | } |
@@ -472,16 +457,8 @@ static int twl4030_phy_power_on(struct phy *phy) | |||
472 | twl4030_usb_set_mode(twl, twl->usb_mode); | 457 | twl4030_usb_set_mode(twl, twl->usb_mode); |
473 | if (twl->usb_mode == T2_USB_MODE_ULPI) | 458 | if (twl->usb_mode == T2_USB_MODE_ULPI) |
474 | twl4030_i2c_access(twl, 0); | 459 | twl4030_i2c_access(twl, 0); |
460 | schedule_delayed_work(&twl->id_workaround_work, 0); | ||
475 | 461 | ||
476 | /* | ||
477 | * XXX When VBUS gets driven after musb goes to A mode, | ||
478 | * ID_PRES related interrupts no longer arrive, why? | ||
479 | * Register itself is updated fine though, so we must poll. | ||
480 | */ | ||
481 | if (twl->linkstat == OMAP_MUSB_ID_GROUND) { | ||
482 | cancel_delayed_work(&twl->id_workaround_work); | ||
483 | schedule_delayed_work(&twl->id_workaround_work, HZ); | ||
484 | } | ||
485 | return 0; | 462 | return 0; |
486 | } | 463 | } |
487 | 464 | ||
@@ -538,13 +515,12 @@ static ssize_t twl4030_usb_vbus_show(struct device *dev, | |||
538 | struct device_attribute *attr, char *buf) | 515 | struct device_attribute *attr, char *buf) |
539 | { | 516 | { |
540 | struct twl4030_usb *twl = dev_get_drvdata(dev); | 517 | struct twl4030_usb *twl = dev_get_drvdata(dev); |
541 | unsigned long flags; | ||
542 | int ret = -EINVAL; | 518 | int ret = -EINVAL; |
543 | 519 | ||
544 | spin_lock_irqsave(&twl->lock, flags); | 520 | mutex_lock(&twl->lock); |
545 | ret = sprintf(buf, "%s\n", | 521 | ret = sprintf(buf, "%s\n", |
546 | twl->vbus_supplied ? "on" : "off"); | 522 | twl->vbus_supplied ? "on" : "off"); |
547 | spin_unlock_irqrestore(&twl->lock, flags); | 523 | mutex_unlock(&twl->lock); |
548 | 524 | ||
549 | return ret; | 525 | return ret; |
550 | } | 526 | } |
@@ -558,12 +534,12 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) | |||
558 | 534 | ||
559 | status = twl4030_usb_linkstat(twl); | 535 | status = twl4030_usb_linkstat(twl); |
560 | 536 | ||
561 | spin_lock_irq(&twl->lock); | 537 | mutex_lock(&twl->lock); |
562 | if (status >= 0 && status != twl->linkstat) { | 538 | if (status >= 0 && status != twl->linkstat) { |
563 | twl->linkstat = status; | 539 | twl->linkstat = status; |
564 | status_changed = true; | 540 | status_changed = true; |
565 | } | 541 | } |
566 | spin_unlock_irq(&twl->lock); | 542 | mutex_unlock(&twl->lock); |
567 | 543 | ||
568 | if (status_changed) { | 544 | if (status_changed) { |
569 | /* FIXME add a set_power() method so that B-devices can | 545 | /* FIXME add a set_power() method so that B-devices can |
@@ -579,10 +555,10 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) | |||
579 | */ | 555 | */ |
580 | if ((status == OMAP_MUSB_VBUS_VALID) || | 556 | if ((status == OMAP_MUSB_VBUS_VALID) || |
581 | (status == OMAP_MUSB_ID_GROUND)) { | 557 | (status == OMAP_MUSB_ID_GROUND)) { |
582 | if (twl->asleep) | 558 | if (pm_runtime_suspended(twl->dev)) |
583 | pm_runtime_get_sync(twl->dev); | 559 | pm_runtime_get_sync(twl->dev); |
584 | } else { | 560 | } else { |
585 | if (!twl->asleep) { | 561 | if (pm_runtime_active(twl->dev)) { |
586 | pm_runtime_mark_last_busy(twl->dev); | 562 | pm_runtime_mark_last_busy(twl->dev); |
587 | pm_runtime_put_autosuspend(twl->dev); | 563 | pm_runtime_put_autosuspend(twl->dev); |
588 | } | 564 | } |
@@ -591,7 +567,7 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) | |||
591 | } | 567 | } |
592 | 568 | ||
593 | /* don't schedule during sleep - irq works right then */ | 569 | /* don't schedule during sleep - irq works right then */ |
594 | if (status == OMAP_MUSB_ID_GROUND && !twl->asleep) { | 570 | if (status == OMAP_MUSB_ID_GROUND && pm_runtime_active(twl->dev)) { |
595 | cancel_delayed_work(&twl->id_workaround_work); | 571 | cancel_delayed_work(&twl->id_workaround_work); |
596 | schedule_delayed_work(&twl->id_workaround_work, HZ); | 572 | schedule_delayed_work(&twl->id_workaround_work, HZ); |
597 | } | 573 | } |
@@ -613,16 +589,9 @@ static void twl4030_id_workaround_work(struct work_struct *work) | |||
613 | static int twl4030_phy_init(struct phy *phy) | 589 | static int twl4030_phy_init(struct phy *phy) |
614 | { | 590 | { |
615 | struct twl4030_usb *twl = phy_get_drvdata(phy); | 591 | struct twl4030_usb *twl = phy_get_drvdata(phy); |
616 | enum omap_musb_vbus_id_status status; | ||
617 | 592 | ||
618 | pm_runtime_get_sync(twl->dev); | 593 | pm_runtime_get_sync(twl->dev); |
619 | status = twl4030_usb_linkstat(twl); | 594 | schedule_delayed_work(&twl->id_workaround_work, 0); |
620 | twl->linkstat = status; | ||
621 | |||
622 | if (status == OMAP_MUSB_ID_GROUND || status == OMAP_MUSB_VBUS_VALID) | ||
623 | omap_musb_mailbox(twl->linkstat); | ||
624 | |||
625 | sysfs_notify(&twl->dev->kobj, NULL, "vbus"); | ||
626 | pm_runtime_mark_last_busy(twl->dev); | 595 | pm_runtime_mark_last_busy(twl->dev); |
627 | pm_runtime_put_autosuspend(twl->dev); | 596 | pm_runtime_put_autosuspend(twl->dev); |
628 | 597 | ||
@@ -699,7 +668,7 @@ static int twl4030_usb_probe(struct platform_device *pdev) | |||
699 | twl->dev = &pdev->dev; | 668 | twl->dev = &pdev->dev; |
700 | twl->irq = platform_get_irq(pdev, 0); | 669 | twl->irq = platform_get_irq(pdev, 0); |
701 | twl->vbus_supplied = false; | 670 | twl->vbus_supplied = false; |
702 | twl->asleep = 1; | 671 | twl->linkstat = -EINVAL; |
703 | twl->linkstat = OMAP_MUSB_UNKNOWN; | 672 | twl->linkstat = OMAP_MUSB_UNKNOWN; |
704 | 673 | ||
705 | twl->phy.dev = twl->dev; | 674 | twl->phy.dev = twl->dev; |
@@ -724,8 +693,8 @@ static int twl4030_usb_probe(struct platform_device *pdev) | |||
724 | if (IS_ERR(phy_provider)) | 693 | if (IS_ERR(phy_provider)) |
725 | return PTR_ERR(phy_provider); | 694 | return PTR_ERR(phy_provider); |
726 | 695 | ||
727 | /* init spinlock for workqueue */ | 696 | /* init mutex for workqueue */ |
728 | spin_lock_init(&twl->lock); | 697 | mutex_init(&twl->lock); |
729 | 698 | ||
730 | INIT_DELAYED_WORK(&twl->id_workaround_work, twl4030_id_workaround_work); | 699 | INIT_DELAYED_WORK(&twl->id_workaround_work, twl4030_id_workaround_work); |
731 | 700 | ||
@@ -755,7 +724,6 @@ static int twl4030_usb_probe(struct platform_device *pdev) | |||
755 | * set_host() and/or set_peripheral() ... OTG_capable boards | 724 | * set_host() and/or set_peripheral() ... OTG_capable boards |
756 | * need both handles, otherwise just one suffices. | 725 | * need both handles, otherwise just one suffices. |
757 | */ | 726 | */ |
758 | twl->irq_enabled = true; | ||
759 | status = devm_request_threaded_irq(twl->dev, twl->irq, NULL, | 727 | status = devm_request_threaded_irq(twl->dev, twl->irq, NULL, |
760 | twl4030_usb_irq, IRQF_TRIGGER_FALLING | | 728 | twl4030_usb_irq, IRQF_TRIGGER_FALLING | |
761 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, "twl4030_usb", twl); | 729 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, "twl4030_usb", twl); |
@@ -817,7 +785,6 @@ static struct platform_driver twl4030_usb_driver = { | |||
817 | .driver = { | 785 | .driver = { |
818 | .name = "twl4030_usb", | 786 | .name = "twl4030_usb", |
819 | .pm = &twl4030_usb_pm_ops, | 787 | .pm = &twl4030_usb_pm_ops, |
820 | .owner = THIS_MODULE, | ||
821 | .of_match_table = of_match_ptr(twl4030_usb_id_table), | 788 | .of_match_table = of_match_ptr(twl4030_usb_id_table), |
822 | }, | 789 | }, |
823 | }; | 790 | }; |