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:
Frank Wang
2024-03-21 14:23:46 +08:00
committed by Tao Huang
parent 49f8a4c330
commit 7c7e551f0c

View File

@@ -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;
}