diff options
| -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 | ||
