mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 10:58:48 +09:00
usb: typec: tcpm: husb311: add runtime power save
While TCPM invoke set_vbus(), we checked the vbus and charge status to disable 24M OSC to save power consumption via a delay worker. Signed-off-by: Frank Wang <frank.wang@rock-chips.com> Change-Id: I4e5b191d4071b3668413fc7362498d6ea3889b7a
This commit is contained in:
@@ -24,12 +24,18 @@
|
||||
#define HUSB311_TCPC_TDRP 0xA2
|
||||
#define HUSB311_TCPC_DCSRCDRP 0xA3
|
||||
|
||||
#define HUSB311_PM_DELAY_S (3 * HZ)
|
||||
|
||||
struct husb311_chip {
|
||||
struct tcpci_data data;
|
||||
struct tcpci *tcpci;
|
||||
struct device *dev;
|
||||
struct regulator *vbus;
|
||||
struct mutex lock; /* lock for sharing chip states */
|
||||
struct delayed_work pm_work;
|
||||
bool vbus_on;
|
||||
bool charge_on;
|
||||
bool suspended;
|
||||
};
|
||||
|
||||
static int husb311_read8(struct husb311_chip *chip, unsigned int reg, u8 *val)
|
||||
@@ -58,6 +64,49 @@ static struct husb311_chip *tdata_to_husb311(struct tcpci_data *tdata)
|
||||
return container_of(tdata, struct husb311_chip, data);
|
||||
}
|
||||
|
||||
static int husb311_disable_osc24m(struct husb311_chip *chip)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 pwr;
|
||||
|
||||
/*
|
||||
* Disable 24M oscillator to save power consumption, and it will be
|
||||
* enabled automatically when INT occurs.
|
||||
*/
|
||||
ret = husb311_read8(chip, HUSB311_TCPC_POWER, &pwr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pwr &= ~BIT(0);
|
||||
ret = husb311_write8(chip, HUSB311_TCPC_POWER, pwr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void husb311_pm_work(struct work_struct *work)
|
||||
{
|
||||
struct husb311_chip *chip =
|
||||
container_of(work, struct husb311_chip, pm_work.work);
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
if (!chip->vbus_on && !chip->charge_on) {
|
||||
if (chip->suspended)
|
||||
goto exit;
|
||||
|
||||
husb311_disable_osc24m(chip);
|
||||
chip->suspended = 1;
|
||||
} else {
|
||||
if (chip->suspended)
|
||||
chip->suspended = 0;
|
||||
}
|
||||
|
||||
exit:
|
||||
mutex_unlock(&chip->lock);
|
||||
}
|
||||
|
||||
static int husb311_sw_reset(struct husb311_chip *chip)
|
||||
{
|
||||
/* soft reset */
|
||||
@@ -88,24 +137,35 @@ static int husb311_set_vbus(struct tcpci *tcpci, struct tcpci_data *tdata,
|
||||
struct husb311_chip *chip = tdata_to_husb311(tdata);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
if (chip->vbus_on == on) {
|
||||
dev_dbg(chip->dev, "vbus is already %s", on ? "On" : "Off");
|
||||
goto done;
|
||||
} else {
|
||||
if (on)
|
||||
ret = regulator_enable(chip->vbus);
|
||||
else
|
||||
ret = regulator_disable(chip->vbus);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "cannot %s vbus regulator, ret=%d",
|
||||
on ? "enable" : "disable", ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
chip->vbus_on = on;
|
||||
dev_dbg(chip->dev, "vbus := %s", on ? "On" : "Off");
|
||||
}
|
||||
|
||||
if (on)
|
||||
ret = regulator_enable(chip->vbus);
|
||||
if (chip->charge_on == charge)
|
||||
dev_dbg(chip->dev, "charge is already %s",
|
||||
charge ? "On" : "Off");
|
||||
else
|
||||
ret = regulator_disable(chip->vbus);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "cannot %s vbus regulator, ret=%d",
|
||||
on ? "enable" : "disable", ret);
|
||||
goto done;
|
||||
}
|
||||
chip->charge_on = charge;
|
||||
|
||||
chip->vbus_on = on;
|
||||
queue_delayed_work(system_freezable_wq, &chip->pm_work, HUSB311_PM_DELAY_S);
|
||||
|
||||
done:
|
||||
mutex_unlock(&chip->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -169,6 +229,9 @@ static int husb311_probe(struct i2c_client *client,
|
||||
chip->dev = &client->dev;
|
||||
i2c_set_clientdata(client, chip);
|
||||
|
||||
mutex_init(&chip->lock);
|
||||
INIT_DELAYED_WORK(&chip->pm_work, husb311_pm_work);
|
||||
|
||||
chip->vbus = devm_regulator_get_optional(chip->dev, "vbus");
|
||||
if (IS_ERR(chip->vbus)) {
|
||||
ret = PTR_ERR(chip->vbus);
|
||||
@@ -209,6 +272,7 @@ static void husb311_remove(struct i2c_client *client)
|
||||
struct husb311_chip *chip = i2c_get_clientdata(client);
|
||||
|
||||
device_init_wakeup(chip->dev, false);
|
||||
cancel_delayed_work_sync(&chip->pm_work);
|
||||
tcpci_unregister_port(chip->tcpci);
|
||||
}
|
||||
|
||||
@@ -216,27 +280,17 @@ static int husb311_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct husb311_chip *chip = dev->driver_data;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int ret = 0;
|
||||
u8 pwr;
|
||||
|
||||
/*
|
||||
* Disable 12M oscillator to save power consumption, and it will be
|
||||
* enabled automatically when INT occur after system resume.
|
||||
*/
|
||||
ret = husb311_read8(chip, HUSB311_TCPC_POWER, &pwr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pwr &= ~BIT(0);
|
||||
ret = husb311_write8(chip, HUSB311_TCPC_POWER, pwr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (device_may_wakeup(dev) && !chip->vbus_on)
|
||||
enable_irq_wake(client->irq);
|
||||
else
|
||||
disable_irq(client->irq);
|
||||
|
||||
if (!chip->suspended) {
|
||||
chip->suspended = 1;
|
||||
husb311_disable_osc24m(chip);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user