* Re: [PATCH v2 7/7] platform/x86: uniwill-laptop: Add support for battery charge modes [not found] ` <c475d7de-48c8-4fe0-b414-2d32988f52dd@tuxedocomputers.com> @ 2026-05-06 7:47 ` Armin Wolf 2026-05-11 17:40 ` Werner Sembach 0 siblings, 1 reply; 3+ messages in thread From: Armin Wolf @ 2026-05-06 7:47 UTC (permalink / raw) To: Werner Sembach, Ilpo Järvinen Cc: Hans de Goede, platform-driver-x86, LKML Am 04.05.26 um 10:44 schrieb Werner Sembach: > > Am 30.04.26 um 15:41 schrieb Armin Wolf: >> Am 30.04.26 um 15:22 schrieb Ilpo Järvinen: >>> On Fri, 17 Apr 2026, Armin Wolf wrote: >>> >>>> Many Uniwill-based devices do not supports the already existing >>>> charge limit functionality, but instead support an alternative >>>> interface for controlling the battery charge algorithm. >>>> >>>> Add support for this interface and update the documentation. >>>> >>>> Signed-off-by: Armin Wolf <W_Armin@gmx.de> >>>> --- >>>> .../admin-guide/laptops/uniwill-laptop.rst | 19 +- >>>> drivers/platform/x86/uniwill/uniwill-acpi.c | 243 +++++++++++++ >>>> +---- >>>> drivers/platform/x86/uniwill/uniwill-wmi.c | 5 +- >>>> 3 files changed, 215 insertions(+), 52 deletions(-) >>>> >>>> diff --git a/Documentation/admin-guide/laptops/uniwill-laptop.rst b/ >>>> Documentation/admin-guide/laptops/uniwill-laptop.rst >>>> index 1f3ca84c7d88..24b41dbab886 100644 >>>> --- a/Documentation/admin-guide/laptops/uniwill-laptop.rst >>>> +++ b/Documentation/admin-guide/laptops/uniwill-laptop.rst >>>> @@ -46,11 +46,20 @@ Battery Charging Control >>>> .. warning:: Some devices do not properly implement the charging >>>> threshold interface. Forcing >>>> the driver to enable access to said interface on such >>>> devices might damage the >>>> battery [1]_. Because of this the driver will not >>>> enable said feature even when >>>> - using the ``force`` module parameter. >>>> - >>>> -The ``uniwill-laptop`` driver supports controlling the battery >>>> charge limit. This happens over >>>> -the standard ``charge_control_end_threshold`` power supply sysfs >>>> attribute. All values >>>> -between 1 and 100 percent are supported. >>>> + using the ``force`` module parameter. The charging >>>> profile interface will be >>>> + available instead. >>>> + >>>> +The ``uniwill-laptop`` driver supports controlling the battery >>>> charge limit. This either happens >>>> +over the standard ``charge_control_end_threshold`` or >>>> ``charge_types`` power supply sysfs attribute, >>>> +depending on the device. When using the >>>> ``charge_control_end_threshold`` sysfs attribute, all values >>>> +between 1 and 100 percent are supported. When using the >>>> ``charge_types`` sysfs attribute, the driver >>>> +supports switching between the ``Standard``, ``Trickle`` and ``Long >>>> Life`` profiles. >>>> + >>>> +Keep in mind that when using the ``charge_types`` sysfs attribute, >>>> the EC firmware will hide the >>>> +true charging status of the battery from the operating system, >>>> potentially misleading users into >>>> +thinking that the charging profile does not work. Checking the >>>> ``current_now`` sysfs attribute >>>> +tells you the true charging status of the battery even when using >>>> the ``charge_types`` sysfs >>>> +attribute (0 means that the battery is currently not charging). >>>> Additionally the driver signals the presence of battery charging >>>> issues through the standard >>>> ``health`` power supply sysfs attribute. >>>> diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/ >>>> platform/x86/uniwill/uniwill-acpi.c >>>> index d4abcaf87e39..e11b6c8aeb0d 100644 >>>> --- a/drivers/platform/x86/uniwill/uniwill-acpi.c >>>> +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c >>>> @@ -254,6 +254,10 @@ >>>> #define EC_ADDR_OEM_4 0x07A6 >>>> #define OVERBOOST_DYN_TEMP_OFF BIT(1) >>>> +#define CHARGING_PROFILE_MASK GENMASK(5, 4) >>>> +#define CHARGING_PROFILE_HIGH_CAPACITY 0x00 >>>> +#define CHARGING_PROFILE_BALANCED 0x01 >>>> +#define CHARGING_PROFILE_STATIONARY 0x02 >>>> #define TOUCHPAD_TOGGLE_OFF BIT(6) >>>> #define EC_ADDR_CHARGE_CTRL 0x07B9 >>>> @@ -320,13 +324,15 @@ >>>> #define UNIWILL_FEATURE_SUPER_KEY BIT(1) >>>> #define UNIWILL_FEATURE_TOUCHPAD_TOGGLE BIT(2) >>>> #define UNIWILL_FEATURE_LIGHTBAR BIT(3) >>>> -#define UNIWILL_FEATURE_BATTERY BIT(4) >>>> -#define UNIWILL_FEATURE_CPU_TEMP BIT(5) >>>> -#define UNIWILL_FEATURE_GPU_TEMP BIT(6) >>>> -#define UNIWILL_FEATURE_PRIMARY_FAN BIT(7) >>>> -#define UNIWILL_FEATURE_SECONDARY_FAN BIT(8) >>>> -#define UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL BIT(9) >>>> -#define UNIWILL_FEATURE_USB_C_POWER_PRIORITY BIT(10) >>>> +#define UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT BIT(4) >>>> +/* Mutually exclusive with the charge limit feature */ >>>> +#define UNIWILL_FEATURE_BATTERY_CHARGE_MODES BIT(5) >>> >>> This feature seems to be only available through force parameter? >>> >> >> Yes, this is expected to change as soon as Tuxedo can verify if their >> devices support this feature. I tested it on my Tuxedo device, but i do >> not want to split the feature descriptors too much. > Shouldn't require too much testing, will spin up a patch for it. Thank you :) Armin Wolf >> >> Thanks, >> Armin Wolf >> >>> -- >>> i. >>> >>>> +#define UNIWILL_FEATURE_CPU_TEMP BIT(6) >>>> +#define UNIWILL_FEATURE_GPU_TEMP BIT(7) >>>> +#define UNIWILL_FEATURE_PRIMARY_FAN BIT(8) >>>> +#define UNIWILL_FEATURE_SECONDARY_FAN BIT(9) >>>> +#define UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL BIT(10) >>>> +#define UNIWILL_FEATURE_USB_C_POWER_PRIORITY BIT(11) >>>> enum usb_c_power_priority_options { >>>> USB_C_POWER_PRIORITY_CHARGING = 0, >>>> @@ -339,8 +345,15 @@ struct uniwill_data { >>>> struct regmap *regmap; >>>> unsigned int features; >>>> struct acpi_battery_hook hook; >>>> - unsigned int last_charge_ctrl; >>>> struct mutex battery_lock; /* Protects the list of >>>> currently registered batteries */ >>>> + union { >>>> + struct { >>>> + /* Protects writes to last_charge_type */ >>>> + struct mutex charge_type_lock; >>>> + enum power_supply_charge_type last_charge_type; >>>> + }; >>>> + unsigned int last_charge_ctrl; >>>> + }; >>>> bool last_fn_lock_state; >>>> bool last_super_key_enable_state; >>>> bool last_touchpad_toggle_enable_state; >>>> @@ -447,6 +460,12 @@ static inline bool >>>> uniwill_device_supports(const struct uniwill_data *data, >>>> return (data->features & features) == features; >>>> } >>>> +static inline bool uniwill_device_supports_any(const struct >>>> uniwill_data *data, >>>> + unsigned int features) >>>> +{ >>>> + return data->features & features; >>>> +} >>>> + >>>> static int uniwill_ec_reg_write(void *context, unsigned int reg, >>>> unsigned int val) >>>> { >>>> union acpi_object params[2] = { >>>> @@ -1421,6 +1440,30 @@ static int uniwill_led_init(struct >>>> uniwill_data *data) >>>> &init_data); >>>> } >>>> +static int uniwill_read_charge_type(struct uniwill_data *data, >>>> enum power_supply_charge_type *type) >>>> +{ >>>> + unsigned int value; >>>> + int ret; >>>> + >>>> + ret = regmap_read(data->regmap, EC_ADDR_OEM_4, &value); >>>> + if (ret < 0) >>>> + return ret; >>>> + >>>> + switch (FIELD_GET(CHARGING_PROFILE_MASK, value)) { >>>> + case CHARGING_PROFILE_HIGH_CAPACITY: >>>> + *type = POWER_SUPPLY_CHARGE_TYPE_STANDARD; >>>> + return 0; >>>> + case CHARGING_PROFILE_BALANCED: >>>> + *type = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE; >>>> + return 0; >>>> + case CHARGING_PROFILE_STATIONARY: >>>> + *type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; >>>> + return 0; >>>> + default: >>>> + return -EPROTO; >>>> + } >>>> +} >>>> + >>>> static int uniwill_get_property(struct power_supply *psy, const >>>> struct power_supply_ext *ext, >>>> void *drvdata, enum power_supply_property psp, >>>> union power_supply_propval *val) >>>> @@ -1431,6 +1474,16 @@ static int uniwill_get_property(struct >>>> power_supply *psy, const struct power_sup >>>> int ret; >>>> switch (psp) { >>>> + case POWER_SUPPLY_PROP_CHARGE_TYPES: >>>> + /* >>>> + * We need to use the cached value here because the >>>> charging mode >>>> + * reported by the EC might temporarily change when a >>>> external power >>>> + * source has been connected. >>>> + */ >>>> + mutex_lock(&data->charge_type_lock); >>>> + val->intval = data->last_charge_type; >>>> + mutex_unlock(&data->charge_type_lock); >>>> + return 0; >>>> case POWER_SUPPLY_PROP_HEALTH: >>>> ret = power_supply_get_property_direct(psy, >>>> POWER_SUPPLY_PROP_PRESENT, &prop); >>>> if (ret < 0) >>>> @@ -1479,13 +1532,52 @@ static int uniwill_get_property(struct >>>> power_supply *psy, const struct power_sup >>>> } >>>> } >>>> +static int uniwill_write_charge_type(struct uniwill_data *data, >>>> enum power_supply_charge_type type) >>>> +{ >>>> + unsigned int value; >>>> + >>>> + switch (type) { >>>> + case POWER_SUPPLY_CHARGE_TYPE_TRICKLE: >>>> + value = FIELD_PREP(CHARGING_PROFILE_MASK, >>>> CHARGING_PROFILE_STATIONARY); >>>> + break; >>>> + case POWER_SUPPLY_CHARGE_TYPE_STANDARD: >>>> + value = FIELD_PREP(CHARGING_PROFILE_MASK, >>>> CHARGING_PROFILE_HIGH_CAPACITY); >>>> + break; >>>> + case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE: >>>> + value = FIELD_PREP(CHARGING_PROFILE_MASK, >>>> CHARGING_PROFILE_BALANCED); >>>> + break; >>>> + default: >>>> + return -EINVAL; >>>> + } >>>> + >>>> + return regmap_update_bits(data->regmap, EC_ADDR_OEM_4, >>>> CHARGING_PROFILE_MASK, value); >>>> +} >>>> + >>>> +static int uniwill_restore_charge_type(struct uniwill_data *data) >>>> +{ >>>> + guard(mutex)(&data->charge_type_lock); >>>> + >>>> + return uniwill_write_charge_type(data, data->last_charge_type); >>>> +} >>>> + >>>> static int uniwill_set_property(struct power_supply *psy, const >>>> struct power_supply_ext *ext, >>>> void *drvdata, enum power_supply_property psp, >>>> const union power_supply_propval *val) >>>> { >>>> struct uniwill_data *data = drvdata; >>>> + int ret; >>>> switch (psp) { >>>> + case POWER_SUPPLY_PROP_CHARGE_TYPES: >>>> + mutex_lock(&data->charge_type_lock); >>>> + >>>> + ret = uniwill_write_charge_type(data, val->intval); >>>> + if (ret >= 0) >>>> + data->last_charge_type = val->intval; >>>> + >>>> + mutex_unlock(&data->charge_type_lock); >>>> + >>>> + return ret; >>>> case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: >>>> if (val->intval < 0 || val->intval > 100) >>>> return -EINVAL; >>>> @@ -1501,21 +1593,41 @@ static int >>>> uniwill_property_is_writeable(struct power_supply *psy, >>>> const struct power_supply_ext *ext, void >>>> *drvdata, >>>> enum power_supply_property psp) >>>> { >>>> - if (psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD) >>>> + switch (psp) { >>>> + case POWER_SUPPLY_PROP_CHARGE_TYPES: >>>> + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: >>>> return true; >>>> - >>>> - return false; >>>> + default: >>>> + return false; >>>> + } >>>> } >>>> -static const enum power_supply_property uniwill_properties[] = { >>>> +static const enum power_supply_property >>>> uniwill_charge_limit_properties[] = { >>>> POWER_SUPPLY_PROP_HEALTH, >>>> POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD, >>>> }; >>>> -static const struct power_supply_ext uniwill_extension = { >>>> +static const struct power_supply_ext uniwill_charge_limit_extension >>>> = { >>>> .name = DRIVER_NAME, >>>> - .properties = uniwill_properties, >>>> - .num_properties = ARRAY_SIZE(uniwill_properties), >>>> + .properties = uniwill_charge_limit_properties, >>>> + .num_properties = ARRAY_SIZE(uniwill_charge_limit_properties), >>>> + .get_property = uniwill_get_property, >>>> + .set_property = uniwill_set_property, >>>> + .property_is_writeable = uniwill_property_is_writeable, >>>> +}; >>>> + >>>> +static const enum power_supply_property >>>> uniwill_charge_modes_properties[] = { >>>> + POWER_SUPPLY_PROP_CHARGE_TYPES, >>>> + POWER_SUPPLY_PROP_HEALTH, >>>> +}; >>>> + >>>> +static const struct power_supply_ext uniwill_charge_modes_extension >>>> = { >>>> + .name = DRIVER_NAME, >>>> + .charge_types = BIT(POWER_SUPPLY_CHARGE_TYPE_TRICKLE) | >>>> + BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) | >>>> + BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE), >>>> + .properties = uniwill_charge_modes_properties, >>>> + .num_properties = ARRAY_SIZE(uniwill_charge_modes_properties), >>>> .get_property = uniwill_get_property, >>>> .set_property = uniwill_set_property, >>>> .property_is_writeable = uniwill_property_is_writeable, >>>> @@ -1531,7 +1643,13 @@ static int uniwill_add_battery(struct >>>> power_supply *battery, struct acpi_battery >>>> if (!entry) >>>> return -ENOMEM; >>>> - ret = power_supply_register_extension(battery, >>>> &uniwill_extension, data->dev, data); >>>> + if (uniwill_device_supports(data, >>>> UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) >>>> + ret = power_supply_register_extension(battery, >>>> &uniwill_charge_limit_extension, >>>> + data->dev, data); >>>> + else >>>> + ret = power_supply_register_extension(battery, >>>> &uniwill_charge_modes_extension, >>>> + data->dev, data); >>>> + >>>> if (ret < 0) { >>>> kfree(entry); >>>> return ret; >>>> @@ -1560,7 +1678,10 @@ static int uniwill_remove_battery(struct >>>> power_supply *battery, struct acpi_batt >>>> } >>>> } >>>> - power_supply_unregister_extension(battery, &uniwill_extension); >>>> + if (uniwill_device_supports(data, >>>> UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) >>>> + power_supply_unregister_extension(battery, >>>> &uniwill_charge_limit_extension); >>>> + else >>>> + power_supply_unregister_extension(battery, >>>> &uniwill_charge_modes_extension); >>>> return 0; >>>> } >>>> @@ -1570,27 +1691,36 @@ static int uniwill_battery_init(struct >>>> uniwill_data *data) >>>> unsigned int value, threshold; >>>> int ret; >>>> - if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) >>>> - return 0; >>>> + if (uniwill_device_supports(data, >>>> UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) { >>>> + ret = regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, &value); >>>> + if (ret < 0) >>>> + return ret; >>>> - ret = regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, &value); >>>> - if (ret < 0) >>>> - return ret; >>>> + /* >>>> + * The charge control threshold might be initialized with 0 by >>>> + * the EC to signal that said threshold is uninitialized. >>>> We thus >>>> + * need to replace this value with 100 to signal that we >>>> want to >>>> + * take control of battery charging. For the sake of >>>> completeness >>>> + * we also set the charging threshold to 100 if the EC- >>>> provided >>>> + * value is invalid. >>>> + */ >>>> + threshold = FIELD_GET(CHARGE_CTRL_MASK, value); >>>> + if (threshold == 0 || threshold > 100) { >>>> + FIELD_MODIFY(CHARGE_CTRL_MASK, &value, 100); >>>> + ret = regmap_write(data->regmap, EC_ADDR_CHARGE_CTRL, >>>> value); >>>> + if (ret < 0) >>>> + return ret; >>>> + } >>>> + } else if (uniwill_device_supports(data, >>>> UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) { >>>> + ret = devm_mutex_init(data->dev, &data->charge_type_lock); >>>> + if (ret < 0) >>>> + return ret; >>>> - /* >>>> - * The charge control threshold might be initialized with 0 by >>>> - * the EC to signal that said threshold is uninitialized. We thus >>>> - * need to replace this value with 100 to signal that we want to >>>> - * take control of battery charging. For the sake of completeness >>>> - * we also set the charging threshold to 100 if the EC-provided >>>> - * value is invalid. >>>> - */ >>>> - threshold = FIELD_GET(CHARGE_CTRL_MASK, value); >>>> - if (threshold == 0 || threshold > 100) { >>>> - FIELD_MODIFY(CHARGE_CTRL_MASK, &value, 100); >>>> - ret = regmap_write(data->regmap, EC_ADDR_CHARGE_CTRL, value); >>>> + ret = uniwill_read_charge_type(data, &data->last_charge_type); >>>> if (ret < 0) >>>> return ret; >>>> + } else { >>>> + return 0; >>>> } >>>> ret = devm_mutex_init(data->dev, &data->battery_lock); >>>> @@ -1609,10 +1739,13 @@ static int uniwill_notifier_call(struct >>>> notifier_block *nb, unsigned long action >>>> { >>>> struct uniwill_data *data = container_of(nb, struct >>>> uniwill_data, nb); >>>> struct uniwill_battery_entry *entry; >>>> + int ret; >>>> switch (action) { >>>> case UNIWILL_OSD_BATTERY_ALERT: >>>> - if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) >>>> + if (!uniwill_device_supports_any(data, >>>> + UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT | >>>> + UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) >>>> return NOTIFY_DONE; >>>> mutex_lock(&data->battery_lock); >>>> @@ -1623,10 +1756,24 @@ static int uniwill_notifier_call(struct >>>> notifier_block *nb, unsigned long action >>>> return NOTIFY_OK; >>>> case UNIWILL_OSD_DC_ADAPTER_CHANGED: >>>> - if (!uniwill_device_supports(data, >>>> UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) >>>> + if (!uniwill_device_supports_any(data, >>>> + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | >>>> + UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) >>>> return NOTIFY_DONE; >>>> - return >>>> notifier_from_errno(usb_c_power_priority_restore(data)); >>>> + if (uniwill_device_supports(data, >>>> UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) { >>>> + ret = uniwill_restore_charge_type(data); >>>> + if (ret < 0) >>>> + return notifier_from_errno(ret); >>>> + } >>>> + >>>> + if (uniwill_device_supports(data, >>>> UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) { >>>> + ret = usb_c_power_priority_restore(data); >>>> + if (ret < 0) >>>> + return notifier_from_errno(ret); >>>> + } >>>> + >>>> + return NOTIFY_OK; >>>> case UNIWILL_OSD_FN_LOCK: >>>> if (!uniwill_device_supports(data, UNIWILL_FEATURE_FN_LOCK)) >>>> return NOTIFY_DONE; >>>> @@ -1810,7 +1957,7 @@ static int >>>> uniwill_suspend_touchpad_toggle(struct uniwill_data *data) >>>> static int uniwill_suspend_battery(struct uniwill_data *data) >>>> { >>>> - if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) >>>> + if (!uniwill_device_supports(data, >>>> UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) >>>> return 0; >>>> /* >>>> @@ -1887,11 +2034,15 @@ static int >>>> uniwill_resume_touchpad_toggle(struct uniwill_data *data) >>>> static int uniwill_resume_battery(struct uniwill_data *data) >>>> { >>>> - if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) >>>> - return 0; >>>> - return regmap_update_bits(data->regmap, EC_ADDR_CHARGE_CTRL, >>>> CHARGE_CTRL_MASK, >>>> - data->last_charge_ctrl); >>>> + if (uniwill_device_supports(data, >>>> UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) >>>> + return uniwill_restore_charge_type(data); >>>> + >>>> + if (uniwill_device_supports(data, >>>> UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) >>>> + return regmap_update_bits(data->regmap, >>>> EC_ADDR_CHARGE_CTRL, CHARGE_CTRL_MASK, >>>> + data->last_charge_ctrl); >>>> + >>>> + return 0; >>>> } >>>> static int uniwill_resume_nvidia_ctgp(struct uniwill_data *data) >>>> @@ -1970,7 +2121,7 @@ static struct platform_driver uniwill_driver = { >>>> static struct uniwill_device_descriptor >>>> lapqc71a_lapqc71b_descriptor __initdata = { >>>> .features = UNIWILL_FEATURE_SUPER_KEY | >>>> - UNIWILL_FEATURE_BATTERY | >>>> + UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT | >>>> UNIWILL_FEATURE_CPU_TEMP | >>>> UNIWILL_FEATURE_GPU_TEMP | >>>> UNIWILL_FEATURE_PRIMARY_FAN | >>>> @@ -1981,7 +2132,7 @@ static struct uniwill_device_descriptor >>>> lapac71h_descriptor __initdata = { >>>> .features = UNIWILL_FEATURE_FN_LOCK | >>>> UNIWILL_FEATURE_SUPER_KEY | >>>> UNIWILL_FEATURE_TOUCHPAD_TOGGLE | >>>> - UNIWILL_FEATURE_BATTERY | >>>> + UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT | >>>> UNIWILL_FEATURE_CPU_TEMP | >>>> UNIWILL_FEATURE_GPU_TEMP | >>>> UNIWILL_FEATURE_PRIMARY_FAN | >>>> @@ -1993,7 +2144,7 @@ static struct uniwill_device_descriptor >>>> lapkc71f_descriptor __initdata = { >>>> UNIWILL_FEATURE_SUPER_KEY | >>>> UNIWILL_FEATURE_TOUCHPAD_TOGGLE | >>>> UNIWILL_FEATURE_LIGHTBAR | >>>> - UNIWILL_FEATURE_BATTERY | >>>> + UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT | >>>> UNIWILL_FEATURE_CPU_TEMP | >>>> UNIWILL_FEATURE_GPU_TEMP | >>>> UNIWILL_FEATURE_PRIMARY_FAN | >>>> @@ -2579,7 +2730,7 @@ static int __init uniwill_init(void) >>>> if (force) { >>>> /* Assume that the device supports all features except the >>>> charge limit */ >>>> - device_descriptor.features = UINT_MAX & >>>> ~UNIWILL_FEATURE_BATTERY; >>>> + device_descriptor.features = UINT_MAX & >>>> ~UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT; >>>> pr_warn("Enabling potentially unsupported features\n"); >>>> } >>>> diff --git a/drivers/platform/x86/uniwill/uniwill-wmi.c b/drivers/ >>>> platform/x86/uniwill/uniwill-wmi.c >>>> index 31d9c39f14ab..f1b89bc63df6 100644 >>>> --- a/drivers/platform/x86/uniwill/uniwill-wmi.c >>>> +++ b/drivers/platform/x86/uniwill/uniwill-wmi.c >>>> @@ -48,6 +48,7 @@ int devm_uniwill_wmi_register_notifier(struct >>>> device *dev, struct notifier_block >>>> static void uniwill_wmi_notify(struct wmi_device *wdev, union >>>> acpi_object *obj) >>>> { >>>> u32 value; >>>> + int ret; >>>> if (obj->type != ACPI_TYPE_INTEGER) >>>> return; >>>> @@ -56,7 +57,9 @@ static void uniwill_wmi_notify(struct wmi_device >>>> *wdev, union acpi_object *obj) >>>> dev_dbg(&wdev->dev, "Received WMI event %u\n", value); >>>> - blocking_notifier_call_chain(&uniwill_wmi_chain_head, value, NULL); >>>> + ret = blocking_notifier_call_chain(&uniwill_wmi_chain_head, >>>> value, NULL); >>>> + if (notifier_to_errno(ret) < 0) >>>> + dev_err(&wdev->dev, "Failed to handle event %u\n", value); >>>> } >>>> /* >>>> >>> >> > ^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v2 7/7] platform/x86: uniwill-laptop: Add support for battery charge modes 2026-05-06 7:47 ` [PATCH v2 7/7] platform/x86: uniwill-laptop: Add support for battery charge modes Armin Wolf @ 2026-05-11 17:40 ` Werner Sembach 0 siblings, 0 replies; 3+ messages in thread From: Werner Sembach @ 2026-05-11 17:40 UTC (permalink / raw) To: Armin Wolf, Ilpo Järvinen; +Cc: Hans de Goede, platform-driver-x86, LKML Hi, Am 06.05.26 um 09:47 schrieb Armin Wolf: > Am 04.05.26 um 10:44 schrieb Werner Sembach: >> >> Am 30.04.26 um 15:41 schrieb Armin Wolf: >>> Am 30.04.26 um 15:22 schrieb Ilpo Järvinen: >>>> On Fri, 17 Apr 2026, Armin Wolf wrote: >>>> >>>>> Many Uniwill-based devices do not supports the already existing >>>>> charge limit functionality, but instead support an alternative >>>>> interface for controlling the battery charge algorithm. >>>>> >>>>> Add support for this interface and update the documentation. >>>>> >>>>> Signed-off-by: Armin Wolf <W_Armin@gmx.de> >>>>> --- >>>>> .../admin-guide/laptops/uniwill-laptop.rst | 19 +- >>>>> drivers/platform/x86/uniwill/uniwill-acpi.c | 243 +++++++++++++ +---- >>>>> drivers/platform/x86/uniwill/uniwill-wmi.c | 5 +- >>>>> 3 files changed, 215 insertions(+), 52 deletions(-) >>>>> >>>>> diff --git a/Documentation/admin-guide/laptops/uniwill-laptop.rst b/ >>>>> Documentation/admin-guide/laptops/uniwill-laptop.rst >>>>> index 1f3ca84c7d88..24b41dbab886 100644 >>>>> --- a/Documentation/admin-guide/laptops/uniwill-laptop.rst >>>>> +++ b/Documentation/admin-guide/laptops/uniwill-laptop.rst >>>>> @@ -46,11 +46,20 @@ Battery Charging Control >>>>> .. warning:: Some devices do not properly implement the charging >>>>> threshold interface. Forcing >>>>> the driver to enable access to said interface on such >>>>> devices might damage the >>>>> battery [1]_. Because of this the driver will not enable >>>>> said feature even when >>>>> - using the ``force`` module parameter. >>>>> - >>>>> -The ``uniwill-laptop`` driver supports controlling the battery charge >>>>> limit. This happens over >>>>> -the standard ``charge_control_end_threshold`` power supply sysfs >>>>> attribute. All values >>>>> -between 1 and 100 percent are supported. >>>>> + using the ``force`` module parameter. The charging profile >>>>> interface will be >>>>> + available instead. >>>>> + >>>>> +The ``uniwill-laptop`` driver supports controlling the battery charge >>>>> limit. This either happens >>>>> +over the standard ``charge_control_end_threshold`` or ``charge_types`` >>>>> power supply sysfs attribute, >>>>> +depending on the device. When using the ``charge_control_end_threshold`` >>>>> sysfs attribute, all values >>>>> +between 1 and 100 percent are supported. When using the ``charge_types`` >>>>> sysfs attribute, the driver >>>>> +supports switching between the ``Standard``, ``Trickle`` and ``Long >>>>> Life`` profiles. >>>>> + >>>>> +Keep in mind that when using the ``charge_types`` sysfs attribute, the EC >>>>> firmware will hide the >>>>> +true charging status of the battery from the operating system, >>>>> potentially misleading users into >>>>> +thinking that the charging profile does not work. Checking the >>>>> ``current_now`` sysfs attribute >>>>> +tells you the true charging status of the battery even when using the >>>>> ``charge_types`` sysfs >>>>> +attribute (0 means that the battery is currently not charging). >>>>> Additionally the driver signals the presence of battery charging >>>>> issues through the standard >>>>> ``health`` power supply sysfs attribute. >>>>> diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/ >>>>> platform/x86/uniwill/uniwill-acpi.c >>>>> index d4abcaf87e39..e11b6c8aeb0d 100644 >>>>> --- a/drivers/platform/x86/uniwill/uniwill-acpi.c >>>>> +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c >>>>> @@ -254,6 +254,10 @@ >>>>> #define EC_ADDR_OEM_4 0x07A6 >>>>> #define OVERBOOST_DYN_TEMP_OFF BIT(1) >>>>> +#define CHARGING_PROFILE_MASK GENMASK(5, 4) >>>>> +#define CHARGING_PROFILE_HIGH_CAPACITY 0x00 >>>>> +#define CHARGING_PROFILE_BALANCED 0x01 >>>>> +#define CHARGING_PROFILE_STATIONARY 0x02 >>>>> #define TOUCHPAD_TOGGLE_OFF BIT(6) >>>>> #define EC_ADDR_CHARGE_CTRL 0x07B9 >>>>> @@ -320,13 +324,15 @@ >>>>> #define UNIWILL_FEATURE_SUPER_KEY BIT(1) >>>>> #define UNIWILL_FEATURE_TOUCHPAD_TOGGLE BIT(2) >>>>> #define UNIWILL_FEATURE_LIGHTBAR BIT(3) >>>>> -#define UNIWILL_FEATURE_BATTERY BIT(4) >>>>> -#define UNIWILL_FEATURE_CPU_TEMP BIT(5) >>>>> -#define UNIWILL_FEATURE_GPU_TEMP BIT(6) >>>>> -#define UNIWILL_FEATURE_PRIMARY_FAN BIT(7) >>>>> -#define UNIWILL_FEATURE_SECONDARY_FAN BIT(8) >>>>> -#define UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL BIT(9) >>>>> -#define UNIWILL_FEATURE_USB_C_POWER_PRIORITY BIT(10) >>>>> +#define UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT BIT(4) >>>>> +/* Mutually exclusive with the charge limit feature */ >>>>> +#define UNIWILL_FEATURE_BATTERY_CHARGE_MODES BIT(5) >>>> >>>> This feature seems to be only available through force parameter? >>>> >>> >>> Yes, this is expected to change as soon as Tuxedo can verify if their >>> devices support this feature. I tested it on my Tuxedo device, but i do >>> not want to split the feature descriptors too much. >> Shouldn't require too much testing, will spin up a patch for it. > > Thank you :) Sorry for the delay, here is the patch ready to be included in v3: https://gitlab.com/tuxedocomputers/development/packages/linux/-/commit/b367ce0952a4d641ef1db9c98e9a03c00902b2ab best regards Werner > > Armin Wolf > >>> >>> Thanks, >>> Armin Wolf >>> >>>> -- >>>> i. >>>> >>>>> +#define UNIWILL_FEATURE_CPU_TEMP BIT(6) >>>>> +#define UNIWILL_FEATURE_GPU_TEMP BIT(7) >>>>> +#define UNIWILL_FEATURE_PRIMARY_FAN BIT(8) >>>>> +#define UNIWILL_FEATURE_SECONDARY_FAN BIT(9) >>>>> +#define UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL BIT(10) >>>>> +#define UNIWILL_FEATURE_USB_C_POWER_PRIORITY BIT(11) >>>>> enum usb_c_power_priority_options { >>>>> USB_C_POWER_PRIORITY_CHARGING = 0, >>>>> @@ -339,8 +345,15 @@ struct uniwill_data { >>>>> struct regmap *regmap; >>>>> unsigned int features; >>>>> struct acpi_battery_hook hook; >>>>> - unsigned int last_charge_ctrl; >>>>> struct mutex battery_lock; /* Protects the list of currently >>>>> registered batteries */ >>>>> + union { >>>>> + struct { >>>>> + /* Protects writes to last_charge_type */ >>>>> + struct mutex charge_type_lock; >>>>> + enum power_supply_charge_type last_charge_type; >>>>> + }; >>>>> + unsigned int last_charge_ctrl; >>>>> + }; >>>>> bool last_fn_lock_state; >>>>> bool last_super_key_enable_state; >>>>> bool last_touchpad_toggle_enable_state; >>>>> @@ -447,6 +460,12 @@ static inline bool uniwill_device_supports(const >>>>> struct uniwill_data *data, >>>>> return (data->features & features) == features; >>>>> } >>>>> +static inline bool uniwill_device_supports_any(const struct >>>>> uniwill_data *data, >>>>> + unsigned int features) >>>>> +{ >>>>> + return data->features & features; >>>>> +} >>>>> + >>>>> static int uniwill_ec_reg_write(void *context, unsigned int reg, >>>>> unsigned int val) >>>>> { >>>>> union acpi_object params[2] = { >>>>> @@ -1421,6 +1440,30 @@ static int uniwill_led_init(struct uniwill_data *data) >>>>> &init_data); >>>>> } >>>>> +static int uniwill_read_charge_type(struct uniwill_data *data, enum >>>>> power_supply_charge_type *type) >>>>> +{ >>>>> + unsigned int value; >>>>> + int ret; >>>>> + >>>>> + ret = regmap_read(data->regmap, EC_ADDR_OEM_4, &value); >>>>> + if (ret < 0) >>>>> + return ret; >>>>> + >>>>> + switch (FIELD_GET(CHARGING_PROFILE_MASK, value)) { >>>>> + case CHARGING_PROFILE_HIGH_CAPACITY: >>>>> + *type = POWER_SUPPLY_CHARGE_TYPE_STANDARD; >>>>> + return 0; >>>>> + case CHARGING_PROFILE_BALANCED: >>>>> + *type = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE; >>>>> + return 0; >>>>> + case CHARGING_PROFILE_STATIONARY: >>>>> + *type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; >>>>> + return 0; >>>>> + default: >>>>> + return -EPROTO; >>>>> + } >>>>> +} >>>>> + >>>>> static int uniwill_get_property(struct power_supply *psy, const struct >>>>> power_supply_ext *ext, >>>>> void *drvdata, enum power_supply_property psp, >>>>> union power_supply_propval *val) >>>>> @@ -1431,6 +1474,16 @@ static int uniwill_get_property(struct power_supply >>>>> *psy, const struct power_sup >>>>> int ret; >>>>> switch (psp) { >>>>> + case POWER_SUPPLY_PROP_CHARGE_TYPES: >>>>> + /* >>>>> + * We need to use the cached value here because the charging mode >>>>> + * reported by the EC might temporarily change when a external power >>>>> + * source has been connected. >>>>> + */ >>>>> + mutex_lock(&data->charge_type_lock); >>>>> + val->intval = data->last_charge_type; >>>>> + mutex_unlock(&data->charge_type_lock); >>>>> + return 0; >>>>> case POWER_SUPPLY_PROP_HEALTH: >>>>> ret = power_supply_get_property_direct(psy, >>>>> POWER_SUPPLY_PROP_PRESENT, &prop); >>>>> if (ret < 0) >>>>> @@ -1479,13 +1532,52 @@ static int uniwill_get_property(struct >>>>> power_supply *psy, const struct power_sup >>>>> } >>>>> } >>>>> +static int uniwill_write_charge_type(struct uniwill_data *data, enum >>>>> power_supply_charge_type type) >>>>> +{ >>>>> + unsigned int value; >>>>> + >>>>> + switch (type) { >>>>> + case POWER_SUPPLY_CHARGE_TYPE_TRICKLE: >>>>> + value = FIELD_PREP(CHARGING_PROFILE_MASK, >>>>> CHARGING_PROFILE_STATIONARY); >>>>> + break; >>>>> + case POWER_SUPPLY_CHARGE_TYPE_STANDARD: >>>>> + value = FIELD_PREP(CHARGING_PROFILE_MASK, >>>>> CHARGING_PROFILE_HIGH_CAPACITY); >>>>> + break; >>>>> + case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE: >>>>> + value = FIELD_PREP(CHARGING_PROFILE_MASK, >>>>> CHARGING_PROFILE_BALANCED); >>>>> + break; >>>>> + default: >>>>> + return -EINVAL; >>>>> + } >>>>> + >>>>> + return regmap_update_bits(data->regmap, EC_ADDR_OEM_4, >>>>> CHARGING_PROFILE_MASK, value); >>>>> +} >>>>> + >>>>> +static int uniwill_restore_charge_type(struct uniwill_data *data) >>>>> +{ >>>>> + guard(mutex)(&data->charge_type_lock); >>>>> + >>>>> + return uniwill_write_charge_type(data, data->last_charge_type); >>>>> +} >>>>> + >>>>> static int uniwill_set_property(struct power_supply *psy, const struct >>>>> power_supply_ext *ext, >>>>> void *drvdata, enum power_supply_property psp, >>>>> const union power_supply_propval *val) >>>>> { >>>>> struct uniwill_data *data = drvdata; >>>>> + int ret; >>>>> switch (psp) { >>>>> + case POWER_SUPPLY_PROP_CHARGE_TYPES: >>>>> + mutex_lock(&data->charge_type_lock); >>>>> + >>>>> + ret = uniwill_write_charge_type(data, val->intval); >>>>> + if (ret >= 0) >>>>> + data->last_charge_type = val->intval; >>>>> + >>>>> + mutex_unlock(&data->charge_type_lock); >>>>> + >>>>> + return ret; >>>>> case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: >>>>> if (val->intval < 0 || val->intval > 100) >>>>> return -EINVAL; >>>>> @@ -1501,21 +1593,41 @@ static int uniwill_property_is_writeable(struct >>>>> power_supply *psy, >>>>> const struct power_supply_ext *ext, void *drvdata, >>>>> enum power_supply_property psp) >>>>> { >>>>> - if (psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD) >>>>> + switch (psp) { >>>>> + case POWER_SUPPLY_PROP_CHARGE_TYPES: >>>>> + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: >>>>> return true; >>>>> - >>>>> - return false; >>>>> + default: >>>>> + return false; >>>>> + } >>>>> } >>>>> -static const enum power_supply_property uniwill_properties[] = { >>>>> +static const enum power_supply_property uniwill_charge_limit_properties[] >>>>> = { >>>>> POWER_SUPPLY_PROP_HEALTH, >>>>> POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD, >>>>> }; >>>>> -static const struct power_supply_ext uniwill_extension = { >>>>> +static const struct power_supply_ext uniwill_charge_limit_extension = { >>>>> .name = DRIVER_NAME, >>>>> - .properties = uniwill_properties, >>>>> - .num_properties = ARRAY_SIZE(uniwill_properties), >>>>> + .properties = uniwill_charge_limit_properties, >>>>> + .num_properties = ARRAY_SIZE(uniwill_charge_limit_properties), >>>>> + .get_property = uniwill_get_property, >>>>> + .set_property = uniwill_set_property, >>>>> + .property_is_writeable = uniwill_property_is_writeable, >>>>> +}; >>>>> + >>>>> +static const enum power_supply_property uniwill_charge_modes_properties[] >>>>> = { >>>>> + POWER_SUPPLY_PROP_CHARGE_TYPES, >>>>> + POWER_SUPPLY_PROP_HEALTH, >>>>> +}; >>>>> + >>>>> +static const struct power_supply_ext uniwill_charge_modes_extension = { >>>>> + .name = DRIVER_NAME, >>>>> + .charge_types = BIT(POWER_SUPPLY_CHARGE_TYPE_TRICKLE) | >>>>> + BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) | >>>>> + BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE), >>>>> + .properties = uniwill_charge_modes_properties, >>>>> + .num_properties = ARRAY_SIZE(uniwill_charge_modes_properties), >>>>> .get_property = uniwill_get_property, >>>>> .set_property = uniwill_set_property, >>>>> .property_is_writeable = uniwill_property_is_writeable, >>>>> @@ -1531,7 +1643,13 @@ static int uniwill_add_battery(struct power_supply >>>>> *battery, struct acpi_battery >>>>> if (!entry) >>>>> return -ENOMEM; >>>>> - ret = power_supply_register_extension(battery, &uniwill_extension, >>>>> data->dev, data); >>>>> + if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) >>>>> + ret = power_supply_register_extension(battery, >>>>> &uniwill_charge_limit_extension, >>>>> + data->dev, data); >>>>> + else >>>>> + ret = power_supply_register_extension(battery, >>>>> &uniwill_charge_modes_extension, >>>>> + data->dev, data); >>>>> + >>>>> if (ret < 0) { >>>>> kfree(entry); >>>>> return ret; >>>>> @@ -1560,7 +1678,10 @@ static int uniwill_remove_battery(struct >>>>> power_supply *battery, struct acpi_batt >>>>> } >>>>> } >>>>> - power_supply_unregister_extension(battery, &uniwill_extension); >>>>> + if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) >>>>> + power_supply_unregister_extension(battery, >>>>> &uniwill_charge_limit_extension); >>>>> + else >>>>> + power_supply_unregister_extension(battery, >>>>> &uniwill_charge_modes_extension); >>>>> return 0; >>>>> } >>>>> @@ -1570,27 +1691,36 @@ static int uniwill_battery_init(struct >>>>> uniwill_data *data) >>>>> unsigned int value, threshold; >>>>> int ret; >>>>> - if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) >>>>> - return 0; >>>>> + if (uniwill_device_supports(data, >>>>> UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) { >>>>> + ret = regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, &value); >>>>> + if (ret < 0) >>>>> + return ret; >>>>> - ret = regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, &value); >>>>> - if (ret < 0) >>>>> - return ret; >>>>> + /* >>>>> + * The charge control threshold might be initialized with 0 by >>>>> + * the EC to signal that said threshold is uninitialized. We thus >>>>> + * need to replace this value with 100 to signal that we want to >>>>> + * take control of battery charging. For the sake of completeness >>>>> + * we also set the charging threshold to 100 if the EC- provided >>>>> + * value is invalid. >>>>> + */ >>>>> + threshold = FIELD_GET(CHARGE_CTRL_MASK, value); >>>>> + if (threshold == 0 || threshold > 100) { >>>>> + FIELD_MODIFY(CHARGE_CTRL_MASK, &value, 100); >>>>> + ret = regmap_write(data->regmap, EC_ADDR_CHARGE_CTRL, value); >>>>> + if (ret < 0) >>>>> + return ret; >>>>> + } >>>>> + } else if (uniwill_device_supports(data, >>>>> UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) { >>>>> + ret = devm_mutex_init(data->dev, &data->charge_type_lock); >>>>> + if (ret < 0) >>>>> + return ret; >>>>> - /* >>>>> - * The charge control threshold might be initialized with 0 by >>>>> - * the EC to signal that said threshold is uninitialized. We thus >>>>> - * need to replace this value with 100 to signal that we want to >>>>> - * take control of battery charging. For the sake of completeness >>>>> - * we also set the charging threshold to 100 if the EC-provided >>>>> - * value is invalid. >>>>> - */ >>>>> - threshold = FIELD_GET(CHARGE_CTRL_MASK, value); >>>>> - if (threshold == 0 || threshold > 100) { >>>>> - FIELD_MODIFY(CHARGE_CTRL_MASK, &value, 100); >>>>> - ret = regmap_write(data->regmap, EC_ADDR_CHARGE_CTRL, value); >>>>> + ret = uniwill_read_charge_type(data, &data->last_charge_type); >>>>> if (ret < 0) >>>>> return ret; >>>>> + } else { >>>>> + return 0; >>>>> } >>>>> ret = devm_mutex_init(data->dev, &data->battery_lock); >>>>> @@ -1609,10 +1739,13 @@ static int uniwill_notifier_call(struct >>>>> notifier_block *nb, unsigned long action >>>>> { >>>>> struct uniwill_data *data = container_of(nb, struct uniwill_data, nb); >>>>> struct uniwill_battery_entry *entry; >>>>> + int ret; >>>>> switch (action) { >>>>> case UNIWILL_OSD_BATTERY_ALERT: >>>>> - if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) >>>>> + if (!uniwill_device_supports_any(data, >>>>> + UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT | >>>>> + UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) >>>>> return NOTIFY_DONE; >>>>> mutex_lock(&data->battery_lock); >>>>> @@ -1623,10 +1756,24 @@ static int uniwill_notifier_call(struct >>>>> notifier_block *nb, unsigned long action >>>>> return NOTIFY_OK; >>>>> case UNIWILL_OSD_DC_ADAPTER_CHANGED: >>>>> - if (!uniwill_device_supports(data, >>>>> UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) >>>>> + if (!uniwill_device_supports_any(data, >>>>> + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | >>>>> + UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) >>>>> return NOTIFY_DONE; >>>>> - return notifier_from_errno(usb_c_power_priority_restore(data)); >>>>> + if (uniwill_device_supports(data, >>>>> UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) { >>>>> + ret = uniwill_restore_charge_type(data); >>>>> + if (ret < 0) >>>>> + return notifier_from_errno(ret); >>>>> + } >>>>> + >>>>> + if (uniwill_device_supports(data, >>>>> UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) { >>>>> + ret = usb_c_power_priority_restore(data); >>>>> + if (ret < 0) >>>>> + return notifier_from_errno(ret); >>>>> + } >>>>> + >>>>> + return NOTIFY_OK; >>>>> case UNIWILL_OSD_FN_LOCK: >>>>> if (!uniwill_device_supports(data, UNIWILL_FEATURE_FN_LOCK)) >>>>> return NOTIFY_DONE; >>>>> @@ -1810,7 +1957,7 @@ static int uniwill_suspend_touchpad_toggle(struct >>>>> uniwill_data *data) >>>>> static int uniwill_suspend_battery(struct uniwill_data *data) >>>>> { >>>>> - if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) >>>>> + if (!uniwill_device_supports(data, >>>>> UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) >>>>> return 0; >>>>> /* >>>>> @@ -1887,11 +2034,15 @@ static int uniwill_resume_touchpad_toggle(struct >>>>> uniwill_data *data) >>>>> static int uniwill_resume_battery(struct uniwill_data *data) >>>>> { >>>>> - if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) >>>>> - return 0; >>>>> - return regmap_update_bits(data->regmap, EC_ADDR_CHARGE_CTRL, >>>>> CHARGE_CTRL_MASK, >>>>> - data->last_charge_ctrl); >>>>> + if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) >>>>> + return uniwill_restore_charge_type(data); >>>>> + >>>>> + if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) >>>>> + return regmap_update_bits(data->regmap, EC_ADDR_CHARGE_CTRL, >>>>> CHARGE_CTRL_MASK, >>>>> + data->last_charge_ctrl); >>>>> + >>>>> + return 0; >>>>> } >>>>> static int uniwill_resume_nvidia_ctgp(struct uniwill_data *data) >>>>> @@ -1970,7 +2121,7 @@ static struct platform_driver uniwill_driver = { >>>>> static struct uniwill_device_descriptor lapqc71a_lapqc71b_descriptor >>>>> __initdata = { >>>>> .features = UNIWILL_FEATURE_SUPER_KEY | >>>>> - UNIWILL_FEATURE_BATTERY | >>>>> + UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT | >>>>> UNIWILL_FEATURE_CPU_TEMP | >>>>> UNIWILL_FEATURE_GPU_TEMP | >>>>> UNIWILL_FEATURE_PRIMARY_FAN | >>>>> @@ -1981,7 +2132,7 @@ static struct uniwill_device_descriptor >>>>> lapac71h_descriptor __initdata = { >>>>> .features = UNIWILL_FEATURE_FN_LOCK | >>>>> UNIWILL_FEATURE_SUPER_KEY | >>>>> UNIWILL_FEATURE_TOUCHPAD_TOGGLE | >>>>> - UNIWILL_FEATURE_BATTERY | >>>>> + UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT | >>>>> UNIWILL_FEATURE_CPU_TEMP | >>>>> UNIWILL_FEATURE_GPU_TEMP | >>>>> UNIWILL_FEATURE_PRIMARY_FAN | >>>>> @@ -1993,7 +2144,7 @@ static struct uniwill_device_descriptor >>>>> lapkc71f_descriptor __initdata = { >>>>> UNIWILL_FEATURE_SUPER_KEY | >>>>> UNIWILL_FEATURE_TOUCHPAD_TOGGLE | >>>>> UNIWILL_FEATURE_LIGHTBAR | >>>>> - UNIWILL_FEATURE_BATTERY | >>>>> + UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT | >>>>> UNIWILL_FEATURE_CPU_TEMP | >>>>> UNIWILL_FEATURE_GPU_TEMP | >>>>> UNIWILL_FEATURE_PRIMARY_FAN | >>>>> @@ -2579,7 +2730,7 @@ static int __init uniwill_init(void) >>>>> if (force) { >>>>> /* Assume that the device supports all features except the >>>>> charge limit */ >>>>> - device_descriptor.features = UINT_MAX & ~UNIWILL_FEATURE_BATTERY; >>>>> + device_descriptor.features = UINT_MAX & >>>>> ~UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT; >>>>> pr_warn("Enabling potentially unsupported features\n"); >>>>> } >>>>> diff --git a/drivers/platform/x86/uniwill/uniwill-wmi.c b/drivers/ >>>>> platform/x86/uniwill/uniwill-wmi.c >>>>> index 31d9c39f14ab..f1b89bc63df6 100644 >>>>> --- a/drivers/platform/x86/uniwill/uniwill-wmi.c >>>>> +++ b/drivers/platform/x86/uniwill/uniwill-wmi.c >>>>> @@ -48,6 +48,7 @@ int devm_uniwill_wmi_register_notifier(struct device >>>>> *dev, struct notifier_block >>>>> static void uniwill_wmi_notify(struct wmi_device *wdev, union >>>>> acpi_object *obj) >>>>> { >>>>> u32 value; >>>>> + int ret; >>>>> if (obj->type != ACPI_TYPE_INTEGER) >>>>> return; >>>>> @@ -56,7 +57,9 @@ static void uniwill_wmi_notify(struct wmi_device *wdev, >>>>> union acpi_object *obj) >>>>> dev_dbg(&wdev->dev, "Received WMI event %u\n", value); >>>>> - blocking_notifier_call_chain(&uniwill_wmi_chain_head, value, NULL); >>>>> + ret = blocking_notifier_call_chain(&uniwill_wmi_chain_head, value, >>>>> NULL); >>>>> + if (notifier_to_errno(ret) < 0) >>>>> + dev_err(&wdev->dev, "Failed to handle event %u\n", value); >>>>> } >>>>> /* >>>>> >>>> >>> >> ^ permalink raw reply [flat|nested] 3+ messages in thread
[parent not found: <20260417050912.5582-2-W_Armin@gmx.de>]
[parent not found: <9cc1d286-f41a-c920-aaad-1c24c4e6f2f7@linux.intel.com>]
[parent not found: <903a013e-a3fb-49ea-a6f9-ad6577d57a44@gmx.de>]
* Re: [PATCH v2 1/7] platform/x86: uniwill-laptop: Properly initialize charging threshold [not found] ` <903a013e-a3fb-49ea-a6f9-ad6577d57a44@gmx.de> @ 2026-05-06 13:42 ` Ilpo Järvinen 0 siblings, 0 replies; 3+ messages in thread From: Ilpo Järvinen @ 2026-05-06 13:42 UTC (permalink / raw) To: Armin Wolf; +Cc: Hans de Goede, wse, platform-driver-x86, LKML [-- Attachment #1: Type: text/plain, Size: 2680 bytes --] On Sun, 3 May 2026, Armin Wolf wrote: > Am 30.04.26 um 14:53 schrieb Ilpo Järvinen: > > On Fri, 17 Apr 2026, Armin Wolf wrote: > > > > > The EC might initialize the charge threshold with 0 to signal that > > > said threshold is uninitialized. Detect this and replace said value > > > with 100 to signal the EC that we want to take control of battery > > > charging. Also set the threshold to 100 if the EC-provided value > > > is invalid. > > > > > > Fixes: d050479693bb ("platform/x86: Add Uniwill laptop driver") > > > Reviewed-by: Werner Sembach <wse@tuxedocomputers.com> > > > Signed-off-by: Armin Wolf <W_Armin@gmx.de> > > > --- > > > drivers/platform/x86/uniwill/uniwill-acpi.c | 28 ++++++++++++++++++++- > > > 1 file changed, 27 insertions(+), 1 deletion(-) > > > > > > diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c > > > b/drivers/platform/x86/uniwill/uniwill-acpi.c > > > index faade4cf08be..8f16c94221aa 100644 > > > --- a/drivers/platform/x86/uniwill/uniwill-acpi.c > > > +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c > > > @@ -1404,7 +1404,12 @@ static int uniwill_get_property(struct power_supply > > > *psy, const struct power_sup > > > if (ret < 0) > > > return ret; > > > - val->intval = clamp_val(FIELD_GET(CHARGE_CTRL_MASK, regval), > > > 0, 100); > > > + regval = FIELD_GET(CHARGE_CTRL_MASK, regval); > > > + if (!regval) > > > + val->intval = 100; > > > + else > > > + val->intval = min(regval, 100); > > > > ... > > > > > + /* > > > + * The charge control threshold might be initialized with 0 by > > > + * the EC to signal that said threshold is uninitialized. We thus > > > + * need to replace this value with 100 to signal that we want to > > > + * take control of battery charging. For the sake of completeness > > > + * we also set the charging threshold to 100 if the EC-provided > > > + * value is invalid. > > > + */ > > > + threshold = FIELD_GET(CHARGE_CTRL_MASK, value); > > > + if (threshold == 0 || threshold > 100) { > > > + FIELD_MODIFY(CHARGE_CTRL_MASK, &value, 100); > > > > AFAICT, this does exactly the same thing as the other code above (but > > looks very different on surface). Wouldn't it make sense to have them > > share code? > > I do not think that this would be a good idea. The two call sides are two > different, creating a helper function for both would likely be very > difficult. Both seem to be sanitizing the charge control threshold (AFAICT, both map 0 and out-of-range values to 100) isn't that the case? Why cannot we have uniwill_charge_ctrl_thres_sanitize() or something along those lines? -- i. ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-05-11 17:40 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260417050912.5582-1-W_Armin@gmx.de>
[not found] ` <20260417050912.5582-8-W_Armin@gmx.de>
[not found] ` <e71bdb43-7fcf-519c-c25f-f59cf1489043@linux.intel.com>
[not found] ` <6570157d-d431-4275-9e73-d09bb1b344c8@gmx.de>
[not found] ` <c475d7de-48c8-4fe0-b414-2d32988f52dd@tuxedocomputers.com>
2026-05-06 7:47 ` [PATCH v2 7/7] platform/x86: uniwill-laptop: Add support for battery charge modes Armin Wolf
2026-05-11 17:40 ` Werner Sembach
[not found] ` <20260417050912.5582-2-W_Armin@gmx.de>
[not found] ` <9cc1d286-f41a-c920-aaad-1c24c4e6f2f7@linux.intel.com>
[not found] ` <903a013e-a3fb-49ea-a6f9-ad6577d57a44@gmx.de>
2026-05-06 13:42 ` [PATCH v2 1/7] platform/x86: uniwill-laptop: Properly initialize charging threshold Ilpo Järvinen
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox