[PATCH 4/4] power: supply: bq25630: Add support for liquid detection

From: Waqar Hameed

Date: Wed Oct 28 2026 - 17:57:04 EST


The framework supports reporting and controlling liquid detection. Map
this to the different liquid detection registers available in the
device. Moreover, use the default reset detection threshold values since
the framework currently has no way to specify this.

Signed-off-by: Waqar Hameed <waqar.hameed@xxxxxxxx>
---
drivers/power/supply/bq25630_charger.c | 158 ++++++++++++++++++++++++-
1 file changed, 157 insertions(+), 1 deletion(-)

diff --git a/drivers/power/supply/bq25630_charger.c b/drivers/power/supply/bq25630_charger.c
index a934f3deba63b..dac791718bd98 100644
--- a/drivers/power/supply/bq25630_charger.c
+++ b/drivers/power/supply/bq25630_charger.c
@@ -198,6 +198,16 @@ enum bq25630_regfield {
/* VBUS fault. */
BQ25630_REGF_VBUS_FAULT_STAT,

+ /* Force liquid detection. */
+ BQ25630_REGF_FORCE_LQD_DET,
+ /* Automatic liquid detection. */
+ BQ25630_REGF_AUTO_LQD_DET,
+ /* Enable liquid detection. */
+ BQ25630_REGF_EN_LQD_DET,
+
+ /* Liquid detection status. */
+ BQ25630_REGF_LQD_STAT,
+
/* Sentinel value. */
BQ25630_REGF_MAX
};
@@ -250,6 +260,16 @@ static const struct reg_field bq25630_regfields[] = {
REG_FIELD(BQ25630_REG_FAULT_STATUS, 6, 6),
[BQ25630_REGF_VBUS_FAULT_STAT] =
REG_FIELD(BQ25630_REG_FAULT_STATUS, 7, 7),
+
+ [BQ25630_REGF_FORCE_LQD_DET] =
+ REG_FIELD(BQ25630_REG_LIQUID_CONTROL_0, 3, 3),
+ [BQ25630_REGF_AUTO_LQD_DET] =
+ REG_FIELD(BQ25630_REG_LIQUID_CONTROL_0, 6, 6),
+ [BQ25630_REGF_EN_LQD_DET] =
+ REG_FIELD(BQ25630_REG_LIQUID_CONTROL_0, 7, 7),
+
+ [BQ25630_REGF_LQD_STAT] =
+ REG_FIELD(BQ25630_REG_USB_C_INFORMATION_0, 6, 6),
};

/* 8-bit value regmap. */
@@ -451,6 +471,115 @@ static int bq25630_write_limit(struct bq25630_data *data,
return 0;
}

+static int bq25630_read_liquid_detection(struct bq25630_data *data, int *val)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_field_read(data->regfields[BQ25630_REGF_EN_LQD_DET],
+ &regval);
+ if (ret) {
+ dev_err(data->dev,
+ "Could not read liquid detection setting (%d)\n", ret);
+ return ret;
+ }
+
+ if (!regval) {
+ *val = POWER_SUPPLY_LIQUID_DETECTION_CONTROL_OFF;
+ return 0;
+ }
+
+ ret = regmap_field_read(data->regfields[BQ25630_REGF_AUTO_LQD_DET],
+ &regval);
+ if (ret) {
+ dev_err(data->dev,
+ "Could not read liquid detection type (%d)\n", ret);
+ return ret;
+ }
+
+ if (regval) {
+ *val = POWER_SUPPLY_LIQUID_DETECTION_CONTROL_AUTO;
+ return 0;
+ }
+
+ /* Can only happen after unsuccessful writes... */
+ *val = POWER_SUPPLY_LIQUID_DETECTION_CONTROL_UNKNOWN;
+ return 0;
+}
+
+static int bq25630_write_liquid_detection(struct bq25630_data *data,
+ const int val)
+{
+ unsigned int regval;
+ bool automatic;
+ bool enable;
+ int ret;
+
+ switch (val) {
+ case POWER_SUPPLY_LIQUID_DETECTION_CONTROL_OFF:
+ automatic = false;
+ enable = false;
+ break;
+ case POWER_SUPPLY_LIQUID_DETECTION_CONTROL_AUTO:
+ automatic = true;
+ enable = true;
+ break;
+ case POWER_SUPPLY_LIQUID_DETECTION_CONTROL_ONCE:
+ automatic = false;
+ enable = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_field_write(data->regfields[BQ25630_REGF_AUTO_LQD_DET],
+ automatic);
+ if (ret) {
+ dev_err(data->dev,
+ "Could not write liquid detection type (%d)\n", ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(data->regfields[BQ25630_REGF_EN_LQD_DET],
+ enable);
+ if (ret) {
+ dev_err(data->dev,
+ "Could not write liquid detection setting (%d)\n", ret);
+ return ret;
+ }
+
+ if (val != POWER_SUPPLY_LIQUID_DETECTION_CONTROL_ONCE)
+ return 0;
+
+ ret = regmap_field_write(data->regfields[BQ25630_REGF_FORCE_LQD_DET],
+ 1);
+ if (ret) {
+ dev_err(data->dev,
+ "Could not write liquid detection check (%d)\n", ret);
+ return ret;
+ }
+
+ /*
+ * Datasheet says the register resets when liquid check completes. Poll
+ * it for the default check cycle of 500 ms (field LQD_WAIT in
+ * BQ25630_REG_LIQUID_CONTROL_0).
+ */
+ ret = regmap_field_read_poll_timeout(
+ data->regfields[BQ25630_REGF_FORCE_LQD_DET], regval,
+ regval == 0, BQ25630_IRQ_CYCLE, 500000);
+ if (ret)
+ dev_err(data->dev,
+ "Could not read liquid detection check (%d)\n", ret);
+
+ /* Turn it off after checking once. */
+ ret = regmap_field_write(data->regfields[BQ25630_REGF_EN_LQD_DET], 0);
+ if (ret)
+ dev_err(data->dev,
+ "Could not write liquid detection setting (%d)\n", ret);
+
+ return ret;
+}
+
static int bq25630_read_charge_type(struct bq25630_data *data, int *val)
{
unsigned int regval;
@@ -493,6 +622,19 @@ static int bq25630_read_health(struct bq25630_data *data, int *val)
int ret;
u8 temp;

+ ret = regmap_field_read(data->regfields[BQ25630_REGF_LQD_STAT],
+ &regval);
+ if (ret) {
+ dev_err(data->dev,
+ "Could not read liquid detection status (%d)\n", ret);
+ return ret;
+ }
+
+ if (regval) {
+ *val = POWER_SUPPLY_HEALTH_LIQUID_DETECTED;
+ return 0;
+ }
+
ret = regmap_read(data->regmap8, BQ25630_REG_FAULT_STATUS, &regval);
if (ret) {
dev_err(data->dev, "Could not read fault status (%d)\n", ret);
@@ -921,6 +1063,9 @@ static int bq25630_charger_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_LOAD_SWITCH:
ret = bq25630_read_batfet_ctrl(data, &val->intval);
break;
+ case POWER_SUPPLY_PROP_LIQUID_DETECTION_CONTROL:
+ ret = bq25630_read_liquid_detection(data, &val->intval);
+ break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = "BQ25630";
break;
@@ -992,6 +1137,9 @@ static int bq25630_charger_set_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_LOAD_SWITCH:
ret = bq25630_write_batfet_ctrl(data, val->intval);
break;
+ case POWER_SUPPLY_PROP_LIQUID_DETECTION_CONTROL:
+ ret = bq25630_write_liquid_detection(data, val->intval);
+ break;
default:
return -EINVAL;
}
@@ -1012,6 +1160,7 @@ static int bq25630_charger_property_is_writeable(struct power_supply *psy,
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
case POWER_SUPPLY_PROP_LOAD_SWITCH:
+ case POWER_SUPPLY_PROP_LIQUID_DETECTION_CONTROL:
return true;
default:
return false;
@@ -1035,6 +1184,7 @@ static const enum power_supply_property bq25630_charger_properties[] = {
POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
POWER_SUPPLY_PROP_LOAD_SWITCH,
+ POWER_SUPPLY_PROP_LIQUID_DETECTION_CONTROL,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
};
@@ -1084,7 +1234,13 @@ static int bq25630_probe(struct i2c_client *client)
"Could not initialize regmap8\n");

ret = bq25630_alloc_regfield_range(data, BQ25630_REGF_WATCHDOG,
- BQ25630_REGF_TS_STAT,
+ BQ25630_REGF_VBUS_STAT,
+ data->regmap8);
+ if (ret)
+ return ret;
+
+ ret = bq25630_alloc_regfield_range(data, BQ25630_REGF_FORCE_LQD_DET,
+ BQ25630_REGF_LQD_STAT,
data->regmap8);
if (ret)
return ret;
--
2.43.0