aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/otg/twl4030-usb.c
diff options
context:
space:
mode:
authorJouni Hogander <jouni.hogander@nokia.com>2009-02-20 07:02:31 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2009-03-24 19:20:36 -0400
commit66760169492445395c530c812443f58e2cfdb3dc (patch)
tree8608b8550e427c090b04b817f49225787203c416 /drivers/usb/otg/twl4030-usb.c
parent471c604daf73ff549d374ee54f9e6bfd5a54d4e8 (diff)
USB: TWL: disable VUSB regulators when cable unplugged
This patch disables USB regulators VUSB1V5, VUSB1V8, and VUSB3V1 when the USB cable is unplugged to reduce power consumption. Added a depencency from twl4030 usb driver to TWL_REGULATOR. Signed-off-by: Jouni Hogander <jouni.hogander@nokia.com> Signed-off-by: Kalle Jokiniemi <kalle.jokiniemi@digia.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/otg/twl4030-usb.c')
-rw-r--r--drivers/usb/otg/twl4030-usb.c73
1 files changed, 64 insertions, 9 deletions
diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c
index 416e4410be02..d9478d0e1c8b 100644
--- a/drivers/usb/otg/twl4030-usb.c
+++ b/drivers/usb/otg/twl4030-usb.c
@@ -34,6 +34,8 @@
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/i2c/twl4030.h> 36#include <linux/i2c/twl4030.h>
37#include <linux/regulator/consumer.h>
38#include <linux/err.h>
37 39
38 40
39/* Register defines */ 41/* Register defines */
@@ -246,6 +248,11 @@ struct twl4030_usb {
246 struct otg_transceiver otg; 248 struct otg_transceiver otg;
247 struct device *dev; 249 struct device *dev;
248 250
251 /* TWL4030 internal USB regulator supplies */
252 struct regulator *usb1v5;
253 struct regulator *usb1v8;
254 struct regulator *usb3v1;
255
249 /* for vbus reporting with irqs disabled */ 256 /* for vbus reporting with irqs disabled */
250 spinlock_t lock; 257 spinlock_t lock;
251 258
@@ -434,6 +441,18 @@ static void twl4030_phy_power(struct twl4030_usb *twl, int on)
434 441
435 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); 442 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL);
436 if (on) { 443 if (on) {
444 regulator_enable(twl->usb3v1);
445 regulator_enable(twl->usb1v8);
446 /*
447 * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP
448 * in twl4030) resets the VUSB_DEDICATED2 register. This reset
449 * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to
450 * SLEEP. We work around this by clearing the bit after usv3v1
451 * is re-activated. This ensures that VUSB3V1 is really active.
452 */
453 twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0,
454 VUSB_DEDICATED2);
455 regulator_enable(twl->usb1v5);
437 pwr &= ~PHY_PWR_PHYPWD; 456 pwr &= ~PHY_PWR_PHYPWD;
438 WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); 457 WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
439 twl4030_usb_write(twl, PHY_CLK_CTRL, 458 twl4030_usb_write(twl, PHY_CLK_CTRL,
@@ -443,6 +462,9 @@ static void twl4030_phy_power(struct twl4030_usb *twl, int on)
443 } else { 462 } else {
444 pwr |= PHY_PWR_PHYPWD; 463 pwr |= PHY_PWR_PHYPWD;
445 WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); 464 WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
465 regulator_disable(twl->usb1v5);
466 regulator_disable(twl->usb1v8);
467 regulator_disable(twl->usb3v1);
446 } 468 }
447} 469}
448 470
@@ -468,7 +490,7 @@ static void twl4030_phy_resume(struct twl4030_usb *twl)
468 twl->asleep = 0; 490 twl->asleep = 0;
469} 491}
470 492
471static void twl4030_usb_ldo_init(struct twl4030_usb *twl) 493static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
472{ 494{
473 /* Enable writing to power configuration registers */ 495 /* Enable writing to power configuration registers */
474 twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0xC0, PROTECT_KEY); 496 twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0xC0, PROTECT_KEY);
@@ -480,20 +502,45 @@ static void twl4030_usb_ldo_init(struct twl4030_usb *twl)
480 /* input to VUSB3V1 LDO is from VBAT, not VBUS */ 502 /* input to VUSB3V1 LDO is from VBAT, not VBUS */
481 twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1); 503 twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1);
482 504
483 /* turn on 3.1V regulator */ 505 /* Initialize 3.1V regulator */
484 twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB3V1_DEV_GRP); 506 twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP);
507
508 twl->usb3v1 = regulator_get(twl->dev, "usb3v1");
509 if (IS_ERR(twl->usb3v1))
510 return -ENODEV;
511
485 twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE); 512 twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE);
486 513
487 /* turn on 1.5V regulator */ 514 /* Initialize 1.5V regulator */
488 twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB1V5_DEV_GRP); 515 twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP);
516
517 twl->usb1v5 = regulator_get(twl->dev, "usb1v5");
518 if (IS_ERR(twl->usb1v5))
519 goto fail1;
520
489 twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE); 521 twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE);
490 522
491 /* turn on 1.8V regulator */ 523 /* Initialize 1.8V regulator */
492 twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB1V8_DEV_GRP); 524 twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP);
525
526 twl->usb1v8 = regulator_get(twl->dev, "usb1v8");
527 if (IS_ERR(twl->usb1v8))
528 goto fail2;
529
493 twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE); 530 twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE);
494 531
495 /* disable access to power configuration registers */ 532 /* disable access to power configuration registers */
496 twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, PROTECT_KEY); 533 twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, PROTECT_KEY);
534
535 return 0;
536
537fail2:
538 regulator_put(twl->usb1v5);
539 twl->usb1v5 = NULL;
540fail1:
541 regulator_put(twl->usb3v1);
542 twl->usb3v1 = NULL;
543 return -ENODEV;
497} 544}
498 545
499static ssize_t twl4030_usb_vbus_show(struct device *dev, 546static ssize_t twl4030_usb_vbus_show(struct device *dev,
@@ -598,7 +645,7 @@ static int __init twl4030_usb_probe(struct platform_device *pdev)
598{ 645{
599 struct twl4030_usb_data *pdata = pdev->dev.platform_data; 646 struct twl4030_usb_data *pdata = pdev->dev.platform_data;
600 struct twl4030_usb *twl; 647 struct twl4030_usb *twl;
601 int status; 648 int status, err;
602 649
603 if (!pdata) { 650 if (!pdata) {
604 dev_dbg(&pdev->dev, "platform_data not available\n"); 651 dev_dbg(&pdev->dev, "platform_data not available\n");
@@ -622,7 +669,12 @@ static int __init twl4030_usb_probe(struct platform_device *pdev)
622 /* init spinlock for workqueue */ 669 /* init spinlock for workqueue */
623 spin_lock_init(&twl->lock); 670 spin_lock_init(&twl->lock);
624 671
625 twl4030_usb_ldo_init(twl); 672 err = twl4030_usb_ldo_init(twl);
673 if (err) {
674 dev_err(&pdev->dev, "ldo init failed\n");
675 kfree(twl);
676 return err;
677 }
626 otg_set_transceiver(&twl->otg); 678 otg_set_transceiver(&twl->otg);
627 679
628 platform_set_drvdata(pdev, twl); 680 platform_set_drvdata(pdev, twl);
@@ -688,6 +740,9 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev)
688 twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); 740 twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
689 741
690 twl4030_phy_power(twl, 0); 742 twl4030_phy_power(twl, 0);
743 regulator_put(twl->usb1v5);
744 regulator_put(twl->usb1v8);
745 regulator_put(twl->usb3v1);
691 746
692 kfree(twl); 747 kfree(twl);
693 748