usb: typec: hd3ss3220: track VBUS enable state per consumer

regulator_is_enabled() reports the aggregate regulator state, not
whether this consumer holds an enable reference. If another consumer
enables VBUS first, the driver can skip its own regulator_enable() call
and later attempt to drop a reference it never acquired, triggering an
unbalanced regulator disable warning.

Track successful enable and disable calls locally. Keep the state
unchanged when an operation fails so a later role or ID notification
retries the operation while this consumer keeps balanced references.

Fixes: b3f9d6e491 ("usb: typec: hd3ss3220: Check if regulator needs to be switched")
Cc: stable <stable@kernel.org>
Link: https://github.com/qualcomm-linux/kernel/issues/472
Signed-off-by: Chang Wu <kunjinkao.jp@gmail.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Tested-by: Jan Remmet <j.remmet@phytec.de>
Reviewed-by: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
Link: https://patch.msgid.link/20260819152027.90994-1-kunjinkao.jp@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Chang Wu 2026-08-19 23:20:27 +08:00 committed by Greg Kroah-Hartman
parent b58e620045
commit c9a48db776

View File

@ -62,6 +62,7 @@ struct hd3ss3220 {
int id_irq;
struct regulator *vbus;
bool vbus_enabled;
};
static int hd3ss3220_set_power_opmode(struct hd3ss3220 *hd3ss3220, int power_opmode)
@ -208,7 +209,7 @@ static void hd3ss3220_regulator_control(struct hd3ss3220 *hd3ss3220, bool on)
{
int ret;
if (regulator_is_enabled(hd3ss3220->vbus) == on)
if (hd3ss3220->vbus_enabled == on)
return;
if (on)
@ -216,9 +217,13 @@ static void hd3ss3220_regulator_control(struct hd3ss3220 *hd3ss3220, bool on)
else
ret = regulator_disable(hd3ss3220->vbus);
if (ret)
if (ret) {
dev_err(hd3ss3220->dev,
"vbus regulator %s failed: %d\n", on ? "enable" : "disable", ret);
return;
}
hd3ss3220->vbus_enabled = on;
}
static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220)