Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

platform/x86: dell-ddv: Expose the battery manufacture date to userspace

The manufacture date of a given battery is exposed over the Dell DDV
WMI interface using the "BatteryManufactureDate" WMI method. The
resulting data contains the manufacture date of the battery encoded
inside a 16-bit value as described in the Smart Battery Data
Specification.

Expose this value to userspace using the power supply extension
interface.

Tested on a Dell Inspiron 3505.

Signed-off-by: Armin Wolf <W_Armin@gmx.de>
Link: https://lore.kernel.org/r/20250429003606.303870-3-W_Armin@gmx.de
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

authored by

Armin Wolf and committed by
Ilpo Järvinen
303ecf69 058de163

+56 -3
-3
Documentation/wmi/devices/dell-wmi-ddv.rst
··· 118 118 - bits 5 to 8 contain the manufacture month. 119 119 - bits 9 to 15 contain the manufacture year biased by 1980. 120 120 121 - .. note:: 122 - The data format needs to be verified on more machines. 123 - 124 121 WMI method BatterySerialNumber() 125 122 -------------------------------- 126 123
+56
drivers/platform/x86/dell/dell-wmi-ddv.c
··· 8 8 #define pr_format(fmt) KBUILD_MODNAME ": " fmt 9 9 10 10 #include <linux/acpi.h> 11 + #include <linux/bitfield.h> 11 12 #include <linux/debugfs.h> 12 13 #include <linux/device.h> 13 14 #include <linux/device/driver.h> ··· 42 41 43 42 /* Battery indices 1, 2 and 3 */ 44 43 #define DELL_DDV_NUM_BATTERIES 3 44 + 45 + #define SBS_MANUFACTURE_YEAR_MASK GENMASK(15, 9) 46 + #define SBS_MANUFACTURE_MONTH_MASK GENMASK(8, 5) 47 + #define SBS_MANUFACTURE_DAY_MASK GENMASK(4, 0) 45 48 46 49 #define DELL_EPPID_LENGTH 20 47 50 #define DELL_EPPID_EXT_LENGTH 23 ··· 749 744 return ret; 750 745 } 751 746 747 + static int dell_wmi_ddv_get_manufacture_date(struct dell_wmi_ddv_data *data, u32 index, 748 + enum power_supply_property psp, 749 + union power_supply_propval *val) 750 + { 751 + u16 year, month, day; 752 + u32 value; 753 + int ret; 754 + 755 + ret = dell_wmi_ddv_query_integer(data->wdev, DELL_DDV_BATTERY_MANUFACTURE_DATE, 756 + index, &value); 757 + if (ret < 0) 758 + return ret; 759 + if (value > U16_MAX) 760 + return -ENXIO; 761 + 762 + /* 763 + * Some devices report a invalid manufacture date value 764 + * like 0.0.1980. Because of this we have to check the 765 + * whole value before exposing parts of it to user space. 766 + */ 767 + year = FIELD_GET(SBS_MANUFACTURE_YEAR_MASK, value) + 1980; 768 + month = FIELD_GET(SBS_MANUFACTURE_MONTH_MASK, value); 769 + if (month < 1 || month > 12) 770 + return -ENODATA; 771 + 772 + day = FIELD_GET(SBS_MANUFACTURE_DAY_MASK, value); 773 + if (day < 1 || day > 31) 774 + return -ENODATA; 775 + 776 + switch (psp) { 777 + case POWER_SUPPLY_PROP_MANUFACTURE_YEAR: 778 + val->intval = year; 779 + return 0; 780 + case POWER_SUPPLY_PROP_MANUFACTURE_MONTH: 781 + val->intval = month; 782 + return 0; 783 + case POWER_SUPPLY_PROP_MANUFACTURE_DAY: 784 + val->intval = day; 785 + return 0; 786 + default: 787 + return -EINVAL; 788 + } 789 + } 790 + 752 791 static int dell_wmi_ddv_get_property(struct power_supply *psy, const struct power_supply_ext *ext, 753 792 void *drvdata, enum power_supply_property psp, 754 793 union power_supply_propval *val) ··· 817 768 */ 818 769 val->intval = value - 2732; 819 770 return 0; 771 + case POWER_SUPPLY_PROP_MANUFACTURE_YEAR: 772 + case POWER_SUPPLY_PROP_MANUFACTURE_MONTH: 773 + case POWER_SUPPLY_PROP_MANUFACTURE_DAY: 774 + return dell_wmi_ddv_get_manufacture_date(data, index, psp, val); 820 775 default: 821 776 return -EINVAL; 822 777 } ··· 828 775 829 776 static const enum power_supply_property dell_wmi_ddv_properties[] = { 830 777 POWER_SUPPLY_PROP_TEMP, 778 + POWER_SUPPLY_PROP_MANUFACTURE_YEAR, 779 + POWER_SUPPLY_PROP_MANUFACTURE_MONTH, 780 + POWER_SUPPLY_PROP_MANUFACTURE_DAY, 831 781 }; 832 782 833 783 static const struct power_supply_ext dell_wmi_ddv_extension = {