diff options
author | Jouni Hogander <jouni.hogander@nokia.com> | 2009-02-20 07:02:31 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-03-24 19:20:36 -0400 |
commit | 66760169492445395c530c812443f58e2cfdb3dc (patch) | |
tree | 8608b8550e427c090b04b817f49225787203c416 /drivers/usb | |
parent | 471c604daf73ff549d374ee54f9e6bfd5a54d4e8 (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')
-rw-r--r-- | drivers/usb/otg/Kconfig | 2 | ||||
-rw-r--r-- | drivers/usb/otg/twl4030-usb.c | 73 |
2 files changed, 65 insertions, 10 deletions
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index fc1ca03ce4da..aa884d072f0b 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig | |||
@@ -43,7 +43,7 @@ config ISP1301_OMAP | |||
43 | 43 | ||
44 | config TWL4030_USB | 44 | config TWL4030_USB |
45 | tristate "TWL4030 USB Transceiver Driver" | 45 | tristate "TWL4030 USB Transceiver Driver" |
46 | depends on TWL4030_CORE | 46 | depends on TWL4030_CORE && REGULATOR_TWL4030 |
47 | select USB_OTG_UTILS | 47 | select USB_OTG_UTILS |
48 | help | 48 | help |
49 | Enable this to support the USB OTG transceiver on TWL4030 | 49 | Enable this to support the USB OTG transceiver on TWL4030 |
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 | ||
471 | static void twl4030_usb_ldo_init(struct twl4030_usb *twl) | 493 | static 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 | |||
537 | fail2: | ||
538 | regulator_put(twl->usb1v5); | ||
539 | twl->usb1v5 = NULL; | ||
540 | fail1: | ||
541 | regulator_put(twl->usb3v1); | ||
542 | twl->usb3v1 = NULL; | ||
543 | return -ENODEV; | ||
497 | } | 544 | } |
498 | 545 | ||
499 | static ssize_t twl4030_usb_vbus_show(struct device *dev, | 546 | static 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 | ||