power: supply: Add support for liquid detection
cover power: supply: Add support for liquid detection
During discussions of the development of a new driver [1], it was concluded that a new sysfs ABI for reporting and controlling liquid detection is wanted. This patchset introduces this.
Since there are currently not many devices with support for this. ABI for setting/getting detection threshold values has been left out (as discussed in [1]). Hopefully when more devices are supported, a good common way to specify this will be designed.
[1] https://lore.kernel.org/lkml/[email protected]/
Waqar Hameed (4):
power: supply: Add sysfs entry for liquid detection control
power: supply: Add liquid detection to health property enum
power: supply: bq25630: Add macro define for IRQ cycle time
power: supply: bq25630: Add support for liquid detection
Documentation/ABI/testing/sysfs-class-power | 22 ++- drivers/power/supply/bq25630_charger.c | 163 +++++++++++++++++++- drivers/power/supply/power_supply_sysfs.c | 9 ++ include/linux/power_supply.h | 9 ++ 4 files changed, 199 insertions(+), 4 deletions(-)
base-commit: 151a134a4e6ec66f69db8750c66006f5207f7a9d
Signature · 1 line
2.43.0
PATCH 1/4 power: supply: Add sysfs entry for liquid detection control
There are devices that have built-in liquid detection. For example to mitigate corrosion caused by moisture in a port. In order to be able to configure such devices, add sysfs entry for controlling a device's liquid detection.
--- Documentation/ABI/testing/sysfs-class-power | 19 +++++++++++++++++++ drivers/power/supply/power_supply_sysfs.c | 8 ++++++++ include/linux/power_supply.h | 8 ++++++++ 3 files changed, 35 insertions(+)
Documentation/ABI/testing/sysfs-class-power +19 -0
@@ -619,6 +619,25 @@ Description: "Ship" (4) Ship mode. ============= ================================== +What: /sys/class/power_supply/<supply_name>/liquid_detection_control +Date: October 2026 +Contact: [email protected] +Description: + Devices can have built-in liquid detection functionality. This + can for example be used to mitigate port corrosion caused by + moisture. This entry controls a device's liquid detection. + + Access: Read, Write + + Valid values: + + ============= =============================================== + "Unknown" (0) Unknown state. + "Off" (1) Liquid detection is disabled. + "Auto" (2) Liquid detection is checked periodically. + "Once" (3) Check for liquid once then transition to "Off". + ============= =============================================== + **USB Properties** What: /sys/class/power_supply/<supply_name>/input_current_limit
drivers/power/supply/power_supply_sysfs.c +8 -0
@@ -160,6 +160,13 @@ static const char *const POWER_SUPPLY_LOAD_SWITCH_TEXT[] = { [POWER_SUPPLY_LOAD_SWITCH_SHIP] = "Ship", }; +static const char *const POWER_SUPPLY_LIQUID_DETECTION_CONTROL_TEXT [] = { + [POWER_SUPPLY_LIQUID_DETECTION_CONTROL_UNKNOWN ] ="Unknown" , + [POWER_SUPPLY_LIQUID_DETECTION_CONTROL_OFF ] ="Off" , + [POWER_SUPPLY_LIQUID_DETECTION_CONTROL_AUTO ] ="Auto" , + [POWER_SUPPLY_LIQUID_DETECTION_CONTROL_ONCE ] ="Once" , +}; +static struct power_supply_attr power_supply_attrs[]__ro_after_init = {/* Properties of type `int' */ POWER_SUPPLY_ENUM_ATTR (STATUS ),
@@ -240,6 +247,7 @@ static struct power_supply_attr power_supply_attrs[] __ro_after_init = {POWER_SUPPLY_ATTR (INTERNAL_RESISTANCE ),POWER_SUPPLY_ATTR (STATE_OF_HEALTH ),POWER_SUPPLY_ENUM_ATTR (LOAD_SWITCH ), +POWER_SUPPLY_ENUM_ATTR (LIQUID_DETECTION_CONTROL ),/* Properties of type `const char *' */ POWER_SUPPLY_ATTR (MODEL_NAME ), POWER_SUPPLY_ATTR(MANUFACTURER),
include/linux/power_supply.h +8 -0
@@ -112,6 +112,13 @@ enum { POWER_SUPPLY_LOAD_SWITCH_SHIP, }; +enum { +POWER_SUPPLY_LIQUID_DETECTION_CONTROL_UNKNOWN =0 , +POWER_SUPPLY_LIQUID_DETECTION_CONTROL_OFF , +POWER_SUPPLY_LIQUID_DETECTION_CONTROL_AUTO , +POWER_SUPPLY_LIQUID_DETECTION_CONTROL_ONCE , +}; +enum power_supply_property {/* Properties of type `int' */ POWER_SUPPLY_PROP_STATUS =0 ,
@@ -191,6 +198,7 @@ enum power_supply_property {POWER_SUPPLY_PROP_INTERNAL_RESISTANCE ,POWER_SUPPLY_PROP_STATE_OF_HEALTH ,POWER_SUPPLY_PROP_LOAD_SWITCH , +POWER_SUPPLY_PROP_LIQUID_DETECTION_CONTROL ,/* Properties of type `const char *' */ POWER_SUPPLY_PROP_MODEL_NAME , POWER_SUPPLY_PROP_MANUFACTURER,
Signature · 1 line
2.43.0
PATCH 2/4 power: supply: Add liquid detection to health property enum
In reply to Waqar HameedThere are devices that can detect liquid. For example, moisture in a port. To be able to report this state for such devices, add "liquid detected" to the health property enum.
--- Documentation/ABI/testing/sysfs-class-power | 3 ++- drivers/power/supply/power_supply_sysfs.c | 1 + include/linux/power_supply.h | 1 + 3 files changed, 4 insertions(+), 1 deletion(-)
Documentation/ABI/testing/sysfs-class-power +2 -1
@@ -460,7 +460,8 @@ Description: "Over voltage", "Under voltage", "Unspecified failure", "Cold", "Watchdog timer expire", "Safety timer expire", "Over current", "Calibration required", "Warm", - "Cool", "Hot", "No battery", "Blown fuse", "Cell imbalance" + "Cool", "Hot", "No battery", "Blown fuse", "Cell imbalance", + "Liquid detected" What: /sys/class/power_supply/<supply_name>/precharge_current Date: June 2017
drivers/power/supply/power_supply_sysfs.c +1 -0
@@ -114,6 +114,7 @@ static const char * const POWER_SUPPLY_HEALTH_TEXT[] = { [POWER_SUPPLY_HEALTH_NO_BATTERY] = "No battery", [POWER_SUPPLY_HEALTH_BLOWN_FUSE] = "Blown fuse", [POWER_SUPPLY_HEALTH_CELL_IMBALANCE] = "Cell imbalance", + [POWER_SUPPLY_HEALTH_LIQUID_DETECTED] = "Liquid detected", };static const char *const POWER_SUPPLY_TECHNOLOGY_TEXT [] = {
include/linux/power_supply.h +1 -0
@@ -73,6 +73,7 @@ enum {POWER_SUPPLY_HEALTH_NO_BATTERY ,POWER_SUPPLY_HEALTH_BLOWN_FUSE ,POWER_SUPPLY_HEALTH_CELL_IMBALANCE , + POWER_SUPPLY_HEALTH_LIQUID_DETECTED, }; enum {
Signature · 1 line
2.43.0
PATCH 3/4 power: supply: bq25630: Add macro define for IRQ cycle time
In reply to Waqar HameedThe interrupt pulse time of 256 µs can be seen as an "inherent" property of the device. It is currently only used for when polling the reset register, but it could/should be used when polling whatever register (e.g. liquid check). To facilitate this, add a macro define for the interrupt pulse time.
--- drivers/power/supply/bq25630_charger.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/power/supply/bq25630_charger.c +3 -2
@@ -15,6 +15,7 @@#include <linux/power_supply.h> #define BQ25630_DRV_NAME "bq25630-charger" +#define BQ25630_IRQ_CYCLE 256/* µs */ /* Registers. */ #define BQ25630_REG_CHARGE_CURRENT_LIMIT 0x02
@@ -726,8 +727,8 @@ static int bq25630_reset(struct bq25630_data *data)* this register field to 0. Try reading it for some interrupt cycles. */ ret =regmap_field_read_poll_timeout ( - data->regfields[BQ25630_REGF_REG_RST ], regval, regval ==0 ,256 , -100000 ); + data->regfields[BQ25630_REGF_REG_RST ], regval, regval ==0 , +BQ25630_IRQ_CYCLE ,100000 );if (ret) {dev_err (data->dev,"Could not read reset register field (%d)\n" , ret);
Signature · 1 line
2.43.0
PATCH 4/4 power: supply: bq25630: Add support for liquid detection
In reply to Waqar HameedThe 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.
--- drivers/power/supply/bq25630_charger.c | 158 ++++++++++++++++++++++++- 1 file changed, 157 insertions(+), 1 deletion(-)
drivers/power/supply/bq25630_charger.c +157 -1
@@ -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 ], + ®val); +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 ], + ®val); +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 ], + ®val); +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 , ®val);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;
Signature · 1 line
2.43.0