Skip to content

Commit 2bd1870

Browse files
Wer-Wolfij-intel
authored andcommitted
platform/x86: dell-ddv: Expose the battery health to userspace
The health of a given battery is exposed over the Dell DDV WMI interface using the "BatteryManufactureAceess" WMI method. The resulting data contains, among other data, the health status of the battery. Expose this value to userspace using the power supply extension interface. Tested on a Dell Inspiron 3505. Signed-off-by: Armin Wolf <[email protected]> Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Ilpo Järvinen <[email protected]> Signed-off-by: Ilpo Järvinen <[email protected]>
1 parent 303ecf6 commit 2bd1870

File tree

2 files changed

+123
-1
lines changed

2 files changed

+123
-1
lines changed

Documentation/wmi/devices/dell-wmi-ddv.rst

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,40 @@ Returns the voltage flow of the battery in mV as an u16.
150150
WMI method BatteryManufactureAccess()
151151
-------------------------------------
152152

153-
Returns a manufacture-defined value as an u16.
153+
Returns the health status of the battery as a u16.
154+
The health status encoded in the following manner:
155+
156+
- the third nibble contains the general failure mode
157+
- the fourth nibble contains the specific failure code
158+
159+
Valid failure modes are:
160+
161+
- permanent failure (``0x9``)
162+
- overheat failure (``0xa``)
163+
- overcurrent failure (``0xb``)
164+
165+
All other failure modes are to be considered normal.
166+
167+
The following failure codes are valid for a permanent failure:
168+
169+
- fuse blown (``0x0``)
170+
- cell imbalance (``0x1``)
171+
- overvoltage (``0x2``)
172+
- fet failure (``0x3``)
173+
174+
The last two bits of the failure code are to be ignored when the battery
175+
signals a permanent failure.
176+
177+
The following failure codes a valid for a overheat failure:
178+
179+
- overheat at start of charging (``0x5``)
180+
- overheat during charging (``0x7``)
181+
- overheat during discharging (``0x8``)
182+
183+
The following failure codes are valid for a overcurrent failure:
184+
185+
- overcurrent during charging (``0x6``)
186+
- overcurrent during discharging (``0xb``)
154187

155188
WMI method BatteryRelativeStateOfCharge()
156189
-----------------------------------------

drivers/platform/x86/dell/dell-wmi-ddv.c

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,26 @@
4747
#define SBS_MANUFACTURE_MONTH_MASK GENMASK(8, 5)
4848
#define SBS_MANUFACTURE_DAY_MASK GENMASK(4, 0)
4949

50+
#define MA_FAILURE_MODE_MASK GENMASK(11, 8)
51+
#define MA_FAILURE_MODE_PERMANENT 0x9
52+
#define MA_FAILURE_MODE_OVERHEAT 0xA
53+
#define MA_FAILURE_MODE_OVERCURRENT 0xB
54+
55+
#define MA_PERMANENT_FAILURE_CODE_MASK GENMASK(13, 12)
56+
#define MA_PERMANENT_FAILURE_FUSE_BLOWN 0x0
57+
#define MA_PERMANENT_FAILURE_CELL_IMBALANCE 0x1
58+
#define MA_PERMANENT_FAILURE_OVERVOLTAGE 0x2
59+
#define MA_PERMANENT_FAILURE_FET_FAILURE 0x3
60+
61+
#define MA_OVERHEAT_FAILURE_CODE_MASK GENMASK(15, 12)
62+
#define MA_OVERHEAT_FAILURE_START 0x5
63+
#define MA_OVERHEAT_FAILURE_CHARGING 0x7
64+
#define MA_OVERHEAT_FAILURE_DISCHARGING 0x8
65+
66+
#define MA_OVERCURRENT_FAILURE_CODE_MASK GENMASK(15, 12)
67+
#define MA_OVERCURRENT_FAILURE_CHARGING 0x6
68+
#define MA_OVERCURRENT_FAILURE_DISCHARGING 0xB
69+
5070
#define DELL_EPPID_LENGTH 20
5171
#define DELL_EPPID_EXT_LENGTH 23
5272

@@ -749,6 +769,72 @@ static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, cha
749769
return ret;
750770
}
751771

772+
static int dell_wmi_ddv_get_health(struct dell_wmi_ddv_data *data, u32 index,
773+
union power_supply_propval *val)
774+
{
775+
u32 value, code;
776+
int ret;
777+
778+
ret = dell_wmi_ddv_query_integer(data->wdev, DELL_DDV_BATTERY_MANUFACTURER_ACCESS, index,
779+
&value);
780+
if (ret < 0)
781+
return ret;
782+
783+
switch (FIELD_GET(MA_FAILURE_MODE_MASK, value)) {
784+
case MA_FAILURE_MODE_PERMANENT:
785+
code = FIELD_GET(MA_PERMANENT_FAILURE_CODE_MASK, value);
786+
switch (code) {
787+
case MA_PERMANENT_FAILURE_FUSE_BLOWN:
788+
val->intval = POWER_SUPPLY_HEALTH_BLOWN_FUSE;
789+
return 0;
790+
case MA_PERMANENT_FAILURE_CELL_IMBALANCE:
791+
val->intval = POWER_SUPPLY_HEALTH_CELL_IMBALANCE;
792+
return 0;
793+
case MA_PERMANENT_FAILURE_OVERVOLTAGE:
794+
val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
795+
return 0;
796+
case MA_PERMANENT_FAILURE_FET_FAILURE:
797+
val->intval = POWER_SUPPLY_HEALTH_DEAD;
798+
return 0;
799+
default:
800+
dev_notice_once(&data->wdev->dev, "Unknown permanent failure code %u\n",
801+
code);
802+
val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
803+
return 0;
804+
}
805+
case MA_FAILURE_MODE_OVERHEAT:
806+
code = FIELD_GET(MA_OVERHEAT_FAILURE_CODE_MASK, value);
807+
switch (code) {
808+
case MA_OVERHEAT_FAILURE_START:
809+
case MA_OVERHEAT_FAILURE_CHARGING:
810+
case MA_OVERHEAT_FAILURE_DISCHARGING:
811+
val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
812+
return 0;
813+
default:
814+
dev_notice_once(&data->wdev->dev, "Unknown overheat failure code %u\n",
815+
code);
816+
val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
817+
return 0;
818+
}
819+
case MA_FAILURE_MODE_OVERCURRENT:
820+
code = FIELD_GET(MA_OVERCURRENT_FAILURE_CODE_MASK, value);
821+
switch (code) {
822+
case MA_OVERCURRENT_FAILURE_CHARGING:
823+
case MA_OVERCURRENT_FAILURE_DISCHARGING:
824+
val->intval = POWER_SUPPLY_HEALTH_OVERCURRENT;
825+
return 0;
826+
default:
827+
dev_notice_once(&data->wdev->dev, "Unknown overcurrent failure code %u\n",
828+
code);
829+
val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
830+
return 0;
831+
}
832+
default:
833+
val->intval = POWER_SUPPLY_HEALTH_GOOD;
834+
return 0;
835+
}
836+
}
837+
752838
static int dell_wmi_ddv_get_manufacture_date(struct dell_wmi_ddv_data *data, u32 index,
753839
enum power_supply_property psp,
754840
union power_supply_propval *val)
@@ -806,6 +892,8 @@ static int dell_wmi_ddv_get_property(struct power_supply *psy, const struct powe
806892
return ret;
807893

808894
switch (psp) {
895+
case POWER_SUPPLY_PROP_HEALTH:
896+
return dell_wmi_ddv_get_health(data, index, val);
809897
case POWER_SUPPLY_PROP_TEMP:
810898
ret = dell_wmi_ddv_query_integer(data->wdev, DELL_DDV_BATTERY_TEMPERATURE, index,
811899
&value);
@@ -827,6 +915,7 @@ static int dell_wmi_ddv_get_property(struct power_supply *psy, const struct powe
827915
}
828916

829917
static const enum power_supply_property dell_wmi_ddv_properties[] = {
918+
POWER_SUPPLY_PROP_HEALTH,
830919
POWER_SUPPLY_PROP_TEMP,
831920
POWER_SUPPLY_PROP_MANUFACTURE_YEAR,
832921
POWER_SUPPLY_PROP_MANUFACTURE_MONTH,

0 commit comments

Comments
 (0)