Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 66760169 authored by Jouni Hogander's avatar Jouni Hogander Committed by Greg Kroah-Hartman
Browse files

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: default avatarJouni Hogander <jouni.hogander@nokia.com>
Signed-off-by: default avatarKalle Jokiniemi <kalle.jokiniemi@digia.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 471c604d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -43,7 +43,7 @@ config ISP1301_OMAP

config TWL4030_USB
	tristate "TWL4030 USB Transceiver Driver"
	depends on TWL4030_CORE
	depends on TWL4030_CORE && REGULATOR_TWL4030
	select USB_OTG_UTILS
	help
	  Enable this to support the USB OTG transceiver on TWL4030
+64 −9
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@
#include <linux/delay.h>
#include <linux/usb/otg.h>
#include <linux/i2c/twl4030.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>


/* Register defines */
@@ -246,6 +248,11 @@ struct twl4030_usb {
	struct otg_transceiver	otg;
	struct device		*dev;

	/* TWL4030 internal USB regulator supplies */
	struct regulator	*usb1v5;
	struct regulator	*usb1v8;
	struct regulator	*usb3v1;

	/* for vbus reporting with irqs disabled */
	spinlock_t		lock;

@@ -434,6 +441,18 @@ static void twl4030_phy_power(struct twl4030_usb *twl, int on)

	pwr = twl4030_usb_read(twl, PHY_PWR_CTRL);
	if (on) {
		regulator_enable(twl->usb3v1);
		regulator_enable(twl->usb1v8);
		/*
		 * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP
		 * in twl4030) resets the VUSB_DEDICATED2 register. This reset
		 * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to
		 * SLEEP. We work around this by clearing the bit after usv3v1
		 * is re-activated. This ensures that VUSB3V1 is really active.
		 */
		twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0,
							VUSB_DEDICATED2);
		regulator_enable(twl->usb1v5);
		pwr &= ~PHY_PWR_PHYPWD;
		WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
		twl4030_usb_write(twl, PHY_CLK_CTRL,
@@ -443,6 +462,9 @@ static void twl4030_phy_power(struct twl4030_usb *twl, int on)
	} else  {
		pwr |= PHY_PWR_PHYPWD;
		WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
		regulator_disable(twl->usb1v5);
		regulator_disable(twl->usb1v8);
		regulator_disable(twl->usb3v1);
	}
}

@@ -468,7 +490,7 @@ static void twl4030_phy_resume(struct twl4030_usb *twl)
	twl->asleep = 0;
}

static void twl4030_usb_ldo_init(struct twl4030_usb *twl)
static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
{
	/* Enable writing to power configuration registers */
	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)
	/* input to VUSB3V1 LDO is from VBAT, not VBUS */
	twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1);

	/* turn on 3.1V regulator */
	twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB3V1_DEV_GRP);
	/* Initialize 3.1V regulator */
	twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP);

	twl->usb3v1 = regulator_get(twl->dev, "usb3v1");
	if (IS_ERR(twl->usb3v1))
		return -ENODEV;

	twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE);

	/* turn on 1.5V regulator */
	twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB1V5_DEV_GRP);
	/* Initialize 1.5V regulator */
	twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP);

	twl->usb1v5 = regulator_get(twl->dev, "usb1v5");
	if (IS_ERR(twl->usb1v5))
		goto fail1;

	twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE);

	/* turn on 1.8V regulator */
	twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB1V8_DEV_GRP);
	/* Initialize 1.8V regulator */
	twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP);

	twl->usb1v8 = regulator_get(twl->dev, "usb1v8");
	if (IS_ERR(twl->usb1v8))
		goto fail2;

	twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE);

	/* disable access to power configuration registers */
	twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, PROTECT_KEY);

	return 0;

fail2:
	regulator_put(twl->usb1v5);
	twl->usb1v5 = NULL;
fail1:
	regulator_put(twl->usb3v1);
	twl->usb3v1 = NULL;
	return -ENODEV;
}

static ssize_t twl4030_usb_vbus_show(struct device *dev,
@@ -598,7 +645,7 @@ static int __init twl4030_usb_probe(struct platform_device *pdev)
{
	struct twl4030_usb_data *pdata = pdev->dev.platform_data;
	struct twl4030_usb	*twl;
	int			status;
	int			status, err;

	if (!pdata) {
		dev_dbg(&pdev->dev, "platform_data not available\n");
@@ -622,7 +669,12 @@ static int __init twl4030_usb_probe(struct platform_device *pdev)
	/* init spinlock for workqueue */
	spin_lock_init(&twl->lock);

	twl4030_usb_ldo_init(twl);
	err = twl4030_usb_ldo_init(twl);
	if (err) {
		dev_err(&pdev->dev, "ldo init failed\n");
		kfree(twl);
		return err;
	}
	otg_set_transceiver(&twl->otg);

	platform_set_drvdata(pdev, twl);
@@ -688,6 +740,9 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev)
	twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);

	twl4030_phy_power(twl, 0);
	regulator_put(twl->usb1v5);
	regulator_put(twl->usb1v8);
	regulator_put(twl->usb3v1);

	kfree(twl);