mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/9] platform/x86: uniwill-laptop: Complete battery support
@ 2026-11-10  0:06 Armin Wolf
  2026-11-10  0:06 ` [PATCH 1/9] power: supply: core: Add power_supply_has_property_direct() Armin Wolf
                   ` (8 more replies)
  0 siblings, 9 replies; 11+ messages in thread
From: Armin Wolf @ 2026-11-10  0:06 UTC (permalink / raw)
  To: hansg, ilpo.jarvinen
  Cc: platform-driver-x86, linux-kernel, bifangknt, nicokessler, sre,
	linux-pm, wse

According to the work item for upstreaming the out-of-tree
tuxedo-drivers package, the mainline uniwill-laptop driver still does
not allow userspace to read the cycle count and raw capacity of the
battery, preventing Tuxedo Computers from switching over to the
mainline driver (among other things).

This patch series aims to add the missing battery-related bits. The
first two patches extend the power supply subsystem, while the third
patch uses the new API to expose the battery cycle count on devices
where this not already being done by the ACPI firmware itself. The
fourth patch also exposes the raw battery capacity using a custom
sysfs attribute below the battery power supply device.
The last three patches add support for new devices based on user
reports.

All patches have been tested on a TUXEDO InfinityBook Pro 14 Gen10 AMD
and appear to work without issues.

Link: https://gitlab.com/tuxedocomputers/development/packages/tuxedo-drivers/-/work_items/253

Armin Wolf (9):
  power: supply: core: Add power_supply_has_property_direct()
  power: supply: Enable struct power_supply_ext to be construct
    dynamically
  platform/x86: uniwill-laptop: Report battery cycle count
  platform/x86: uniwill-laptop: Expose battery health and cycle count on
    all devices
  platform/x86: uniwill-laptop: Expose raw battery capacity to userspace
  platform/x86: uniwill-laptop: Mark sysfs attributes as const
  platform/x86: uniwill-laptop: Add support for MECHREVO Yilong15Pro
  platform/x86: uniwill-laptop: Add MECHREVO JIAOLONG Series-X6xR55xK
  platform/x86: uniwill-laptop: Add support for XMG EVO 14 (E25)

 .../ABI/testing/sysfs-driver-uniwill-laptop   |  11 +
 drivers/platform/x86/uniwill/uniwill-acpi.c   | 265 ++++++++++++------
 drivers/power/supply/power_supply_core.c      |  26 +-
 include/linux/power_supply.h                  |   3 +-
 4 files changed, 218 insertions(+), 87 deletions(-)

-- 
2.56.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 1/9] power: supply: core: Add power_supply_has_property_direct()
  2026-11-10  0:06 [PATCH 0/9] platform/x86: uniwill-laptop: Complete battery support Armin Wolf
@ 2026-11-10  0:06 ` Armin Wolf
  2026-11-10  0:06 ` [PATCH 2/9] power: supply: Enable struct power_supply_ext to be construct dynamically Armin Wolf
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Armin Wolf @ 2026-11-10  0:06 UTC (permalink / raw)
  To: hansg, ilpo.jarvinen
  Cc: platform-driver-x86, linux-kernel, bifangknt, nicokessler, sre,
	linux-pm, wse

Providers of power supply extensions might want to check if a given
power supply supports a given property in order to select a suitable
extension to load.

Add power_supply_has_property_direct() to enable such use cases.

Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
 drivers/power/supply/power_supply_core.c | 26 ++++++++++++++++++++----
 include/linux/power_supply.h             |  1 +
 2 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
index 1cc203f2780b..75b53aec5db4 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -1367,17 +1367,35 @@ bool power_supply_ext_has_property(const struct power_supply_ext *psy_ext,
 	return false;
 }
 
-bool power_supply_has_property(struct power_supply *psy,
-			       enum power_supply_property psp)
+/**
+ * power_supply_has_property_direct - Check if a power supply property exists without checking
+ *				      for extensions
+ * @psy: The power supply
+ * @psp: The power supply property to check
+ *
+ * Check if a given power supply support a given power supply property without taking into account
+ * any power supply extesions registered on said power supply. This is mostly useful for determining
+ * if a power supply extension should be registered for the power supply or not.
+ */
+bool power_supply_has_property_direct(struct power_supply *psy, enum power_supply_property psp)
 {
-	struct power_supply_ext_registration *reg;
-
 	if (psy_desc_has_property(psy->desc, psp))
 		return true;
 
 	if (power_supply_battery_info_has_prop(psy->battery_info, psp))
 		return true;
 
+	return false;
+}
+EXPORT_SYMBOL_GPL(power_supply_has_property_direct);
+
+bool power_supply_has_property(struct power_supply *psy, enum power_supply_property psp)
+{
+	struct power_supply_ext_registration *reg;
+
+	if (power_supply_has_property_direct(psy, psp))
+		return true;
+
 	power_supply_for_each_extension(reg, psy) {
 		if (power_supply_ext_has_property(reg->ext, psp))
 			return true;
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index cce61137aef4..73bcb127d0fc 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -934,6 +934,7 @@ extern int power_supply_is_system_supplied(void);
 static inline int power_supply_is_system_supplied(void) { return -ENOSYS; }
 #endif
 
+bool power_supply_has_property_direct(struct power_supply *psy, enum power_supply_property psp);
 extern int power_supply_get_property(struct power_supply *psy,
 			    enum power_supply_property psp,
 			    union power_supply_propval *val);
-- 
2.56.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 2/9] power: supply: Enable struct power_supply_ext to be construct dynamically
  2026-11-10  0:06 [PATCH 0/9] platform/x86: uniwill-laptop: Complete battery support Armin Wolf
  2026-11-10  0:06 ` [PATCH 1/9] power: supply: core: Add power_supply_has_property_direct() Armin Wolf
@ 2026-11-10  0:06 ` Armin Wolf
  2026-11-10  0:06 ` [PATCH 3/9] platform/x86: uniwill-laptop: Report battery cycle count Armin Wolf
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Armin Wolf @ 2026-11-10  0:06 UTC (permalink / raw)
  To: hansg, ilpo.jarvinen
  Cc: platform-driver-x86, linux-kernel, bifangknt, nicokessler, sre,
	linux-pm, wse

Providers of power supply extensions currently cannot construct them
dynamically because the pointer to the name string is declared as
being const, even when the remaining members of
struct power_supply_ext are not declared as being const.

Remove this pointless restriction to allow providers to dynamically
construct power supply extensions.

Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
 include/linux/power_supply.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 73bcb127d0fc..a0cabc139298 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -318,7 +318,7 @@ struct power_supply_desc {
 };
 
 struct power_supply_ext {
-	const char *const name;
+	const char *name;
 	u8 charge_behaviours;
 	u32 charge_types;
 	const enum power_supply_property *properties;
-- 
2.56.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 3/9] platform/x86: uniwill-laptop: Report battery cycle count
  2026-11-10  0:06 [PATCH 0/9] platform/x86: uniwill-laptop: Complete battery support Armin Wolf
  2026-11-10  0:06 ` [PATCH 1/9] power: supply: core: Add power_supply_has_property_direct() Armin Wolf
  2026-11-10  0:06 ` [PATCH 2/9] power: supply: Enable struct power_supply_ext to be construct dynamically Armin Wolf
@ 2026-11-10  0:06 ` Armin Wolf
  2026-11-10  0:06 ` [PATCH 4/9] platform/x86: uniwill-laptop: Expose battery health and cycle count on all devices Armin Wolf
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Armin Wolf @ 2026-11-10  0:06 UTC (permalink / raw)
  To: hansg, ilpo.jarvinen
  Cc: platform-driver-x86, linux-kernel, bifangknt, nicokessler, sre,
	linux-pm, wse

It turns out that nearly all Uniwill-based devices do not support
the _BIX ACPI control method for ACPI battery devices, instead only
providing the legacy _BIF ACPI control method. This prevents them
from reporting the cycle count of the battery, even when the EC
reports this value.

Fix this by supplying the battery cycle count using the power supply
extension on devices where the ACPI battery does not.

Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
 drivers/platform/x86/uniwill/uniwill-acpi.c | 94 ++++++++++++---------
 1 file changed, 55 insertions(+), 39 deletions(-)

diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c
index 060324ed3c4d..1958a1bfc880 100644
--- a/drivers/platform/x86/uniwill/uniwill-acpi.c
+++ b/drivers/platform/x86/uniwill/uniwill-acpi.c
@@ -392,6 +392,9 @@ struct uniwill_data {
 		};
 		unsigned int last_charge_ctrl;
 	};
+	enum power_supply_property extension_props[3];
+	struct power_supply_ext standard_extension;
+	struct power_supply_ext cycle_count_extension;
 	bool last_fn_lock_state;
 	bool last_super_key_enable_state;
 	bool last_touchpad_toggle_enable_state;
@@ -639,6 +642,8 @@ static bool uniwill_readable_reg(struct device *dev, unsigned int reg)
 	case EC_ADDR_SECOND_FAN_RPM_1:
 	case EC_ADDR_SECOND_FAN_RPM_2:
 	case EC_ADDR_BAT_ALERT:
+	case EC_ADDR_BAT_CYCLE_COUNT_1:
+	case EC_ADDR_BAT_CYCLE_COUNT_2:
 	case EC_ADDR_OEM_9:
 	case EC_ADDR_PROJECT_ID:
 	case EC_ADDR_AP_OEM:
@@ -686,6 +691,8 @@ static bool uniwill_volatile_reg(struct device *dev, unsigned int reg)
 	case EC_ADDR_SECOND_FAN_RPM_1:
 	case EC_ADDR_SECOND_FAN_RPM_2:
 	case EC_ADDR_BAT_ALERT:
+	case EC_ADDR_BAT_CYCLE_COUNT_1:
+	case EC_ADDR_BAT_CYCLE_COUNT_2:
 	case EC_ADDR_BIOS_OEM:
 	case EC_ADDR_PWM_1:
 	case EC_ADDR_PWM_2:
@@ -1966,6 +1973,7 @@ static int uniwill_get_property(struct power_supply *psy, const struct power_sup
 	struct uniwill_data *data = drvdata;
 	union power_supply_propval prop;
 	unsigned int regval;
+	__le16 cycle_count;
 	int ret;
 
 	switch (psp) {
@@ -2010,6 +2018,14 @@ static int uniwill_get_property(struct power_supply *psy, const struct power_sup
 
 		val->intval = POWER_SUPPLY_HEALTH_GOOD;
 		return 0;
+	case POWER_SUPPLY_PROP_CYCLE_COUNT:
+		ret = regmap_bulk_read(data->regmap, EC_ADDR_BAT_CYCLE_COUNT_1, &cycle_count,
+				       sizeof(cycle_count));
+		if (ret < 0)
+			return ret;
+
+		val->intval = le16_to_cpu(cycle_count);
+		return 0;
 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
 		ret = regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, &regval);
 		if (ret < 0)
@@ -2093,54 +2109,23 @@ static int uniwill_property_is_writeable(struct power_supply *psy,
 	}
 }
 
-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_charge_limit_extension = {
-	.name = DRIVER_NAME,
-	.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,
-};
-
 static int uniwill_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
 {
 	struct uniwill_data *data = container_of(hook, struct uniwill_data, hook);
 	struct uniwill_battery_entry *entry;
+	const struct power_supply_ext *ext;
 	int ret;
 
 	entry = kzalloc_obj(*entry);
 	if (!entry)
 		return -ENOMEM;
 
-	if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT))
-		ret = power_supply_register_extension(battery, &uniwill_charge_limit_extension,
-						      data->dev, data);
+	if (power_supply_has_property_direct(battery, POWER_SUPPLY_PROP_CYCLE_COUNT))
+		ext = &data->standard_extension;
 	else
-		ret = power_supply_register_extension(battery, &uniwill_charge_modes_extension,
-						      data->dev, data);
+		ext = &data->cycle_count_extension;
 
+	ret = power_supply_register_extension(battery, ext, data->dev, data);
 	if (ret < 0) {
 		kfree(entry);
 		return ret;
@@ -2158,6 +2143,7 @@ static int uniwill_remove_battery(struct power_supply *battery, struct acpi_batt
 {
 	struct uniwill_data *data = container_of(hook, struct uniwill_data, hook);
 	struct uniwill_battery_entry *entry, *tmp;
+	const struct power_supply_ext *ext;
 
 	scoped_guard(mutex, &data->battery_lock) {
 		list_for_each_entry_safe(entry, tmp, &data->batteries, head) {
@@ -2169,10 +2155,12 @@ static int uniwill_remove_battery(struct power_supply *battery, struct acpi_batt
 		}
 	}
 
-	if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT))
-		power_supply_unregister_extension(battery, &uniwill_charge_limit_extension);
+	if (power_supply_has_property_direct(battery, POWER_SUPPLY_PROP_CYCLE_COUNT))
+		ext = &data->standard_extension;
 	else
-		power_supply_unregister_extension(battery, &uniwill_charge_modes_extension);
+		ext = &data->cycle_count_extension;
+
+	power_supply_unregister_extension(battery, ext);
 
 	return 0;
 }
@@ -2180,6 +2168,9 @@ static int uniwill_remove_battery(struct power_supply *battery, struct acpi_batt
 static int uniwill_battery_init(struct uniwill_data *data)
 {
 	unsigned int value, threshold, sanitized;
+	u32 charge_types = BIT(POWER_SUPPLY_CHARGE_TYPE_TRICKLE) |
+			   BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) |
+			   BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE);
 	int ret;
 
 	if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) {
@@ -2203,6 +2194,8 @@ static int uniwill_battery_init(struct uniwill_data *data)
 			if (ret < 0)
 				return ret;
 		}
+
+		data->extension_props[0] = POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD;
 	} else if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) {
 		ret = devm_mutex_init(data->dev, &data->charge_type_lock);
 		if (ret < 0)
@@ -2211,10 +2204,33 @@ static int uniwill_battery_init(struct uniwill_data *data)
 		ret = uniwill_read_charge_type(data, &data->last_charge_type);
 		if (ret < 0)
 			return ret;
+
+		data->extension_props[0] = POWER_SUPPLY_PROP_CHARGE_TYPES;
+		data->standard_extension.charge_types = charge_types;
+		data->cycle_count_extension.charge_types = charge_types;
 	} else {
 		return 0;
 	}
 
+	data->extension_props[1] = POWER_SUPPLY_PROP_HEALTH;
+	/* Needs to come last */
+	data->extension_props[2] = POWER_SUPPLY_PROP_CYCLE_COUNT;
+
+	data->standard_extension.name = DRIVER_NAME;
+	data->standard_extension.properties = data->extension_props;
+	/* POWER_SUPPLY_PROP_CYCLE_COUNT is usually already provided by the ACPI battery */
+	data->standard_extension.num_properties = ARRAY_SIZE(data->extension_props) - 1;
+	data->standard_extension.get_property = uniwill_get_property;
+	data->standard_extension.set_property = uniwill_set_property;
+	data->standard_extension.property_is_writeable = uniwill_property_is_writeable;
+
+	data->cycle_count_extension.name = DRIVER_NAME;
+	data->cycle_count_extension.properties = data->extension_props;
+	data->cycle_count_extension.num_properties = ARRAY_SIZE(data->extension_props);
+	data->cycle_count_extension.get_property = uniwill_get_property;
+	data->cycle_count_extension.set_property = uniwill_set_property;
+	data->cycle_count_extension.property_is_writeable = uniwill_property_is_writeable;
+
 	ret = devm_mutex_init(data->dev, &data->battery_lock);
 	if (ret < 0)
 		return ret;
-- 
2.56.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 4/9] platform/x86: uniwill-laptop: Expose battery health and cycle count on all devices
  2026-11-10  0:06 [PATCH 0/9] platform/x86: uniwill-laptop: Complete battery support Armin Wolf
                   ` (2 preceding siblings ...)
  2026-11-10  0:06 ` [PATCH 3/9] platform/x86: uniwill-laptop: Report battery cycle count Armin Wolf
@ 2026-11-10  0:06 ` Armin Wolf
  2026-11-10  0:06 ` [PATCH 5/9] platform/x86: uniwill-laptop: Expose raw battery capacity to userspace Armin Wolf
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Armin Wolf @ 2026-11-10  0:06 UTC (permalink / raw)
  To: hansg, ilpo.jarvinen
  Cc: platform-driver-x86, linux-kernel, bifangknt, nicokessler, sre,
	linux-pm, wse

We can assume that all passive battery properties (health and cycle
count) are available on all devices if the underlying EC interface
is supported. Rework the battery extension initialization code to
always register a power supply extension with at least those two
properties.

Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
 drivers/platform/x86/uniwill/uniwill-acpi.c | 20 +++++++-------------
 1 file changed, 7 insertions(+), 13 deletions(-)

diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c
index 1958a1bfc880..66770f939c83 100644
--- a/drivers/platform/x86/uniwill/uniwill-acpi.c
+++ b/drivers/platform/x86/uniwill/uniwill-acpi.c
@@ -2171,6 +2171,7 @@ static int uniwill_battery_init(struct uniwill_data *data)
 	u32 charge_types = BIT(POWER_SUPPLY_CHARGE_TYPE_TRICKLE) |
 			   BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) |
 			   BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE);
+	int i = 0;
 	int ret;
 
 	if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) {
@@ -2195,7 +2196,7 @@ static int uniwill_battery_init(struct uniwill_data *data)
 				return ret;
 		}
 
-		data->extension_props[0] = POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD;
+		data->extension_props[i++] = POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD;
 	} else if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) {
 		ret = devm_mutex_init(data->dev, &data->charge_type_lock);
 		if (ret < 0)
@@ -2205,28 +2206,26 @@ static int uniwill_battery_init(struct uniwill_data *data)
 		if (ret < 0)
 			return ret;
 
-		data->extension_props[0] = POWER_SUPPLY_PROP_CHARGE_TYPES;
+		data->extension_props[i++] = POWER_SUPPLY_PROP_CHARGE_TYPES;
 		data->standard_extension.charge_types = charge_types;
 		data->cycle_count_extension.charge_types = charge_types;
-	} else {
-		return 0;
 	}
 
-	data->extension_props[1] = POWER_SUPPLY_PROP_HEALTH;
+	data->extension_props[i++] = POWER_SUPPLY_PROP_HEALTH;
 	/* Needs to come last */
-	data->extension_props[2] = POWER_SUPPLY_PROP_CYCLE_COUNT;
+	data->extension_props[i++] = POWER_SUPPLY_PROP_CYCLE_COUNT;
 
 	data->standard_extension.name = DRIVER_NAME;
 	data->standard_extension.properties = data->extension_props;
 	/* POWER_SUPPLY_PROP_CYCLE_COUNT is usually already provided by the ACPI battery */
-	data->standard_extension.num_properties = ARRAY_SIZE(data->extension_props) - 1;
+	data->standard_extension.num_properties = i - 1;
 	data->standard_extension.get_property = uniwill_get_property;
 	data->standard_extension.set_property = uniwill_set_property;
 	data->standard_extension.property_is_writeable = uniwill_property_is_writeable;
 
 	data->cycle_count_extension.name = DRIVER_NAME;
 	data->cycle_count_extension.properties = data->extension_props;
-	data->cycle_count_extension.num_properties = ARRAY_SIZE(data->extension_props);
+	data->cycle_count_extension.num_properties = i;
 	data->cycle_count_extension.get_property = uniwill_get_property;
 	data->cycle_count_extension.set_property = uniwill_set_property;
 	data->cycle_count_extension.property_is_writeable = uniwill_property_is_writeable;
@@ -2251,11 +2250,6 @@ static int uniwill_notifier_call(struct notifier_block *nb, unsigned long action
 
 	switch (action) {
 	case UNIWILL_OSD_BATTERY_ALERT:
-		if (!uniwill_device_supports_any(data,
-						 UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT |
-						 UNIWILL_FEATURE_BATTERY_CHARGE_MODES))
-			return NOTIFY_DONE;
-
 		mutex_lock(&data->battery_lock);
 		list_for_each_entry(entry, &data->batteries, head) {
 			power_supply_changed(entry->battery);
-- 
2.56.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 5/9] platform/x86: uniwill-laptop: Expose raw battery capacity to userspace
  2026-11-10  0:06 [PATCH 0/9] platform/x86: uniwill-laptop: Complete battery support Armin Wolf
                   ` (3 preceding siblings ...)
  2026-11-10  0:06 ` [PATCH 4/9] platform/x86: uniwill-laptop: Expose battery health and cycle count on all devices Armin Wolf
@ 2026-11-10  0:06 ` Armin Wolf
  2026-11-10 11:13   ` kernel test robot
  2026-11-10  0:06 ` [PATCH 6/9] platform/x86: uniwill-laptop: Mark sysfs attributes as const Armin Wolf
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 11+ messages in thread
From: Armin Wolf @ 2026-11-10  0:06 UTC (permalink / raw)
  To: hansg, ilpo.jarvinen
  Cc: platform-driver-x86, linux-kernel, bifangknt, nicokessler, sre,
	linux-pm, wse

It turns out that Uniwill will not report the true battery full charge
capacity for the first 64 charge cycles. While this is likely being
done for correctly calibrating the battery, it still prevents custom
diagnostic software from accurately detecting the battery wear.

Fix this by exposing the raw battery full charge capacity to userspace
using a custom sysfs attribute.

Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
 .../ABI/testing/sysfs-driver-uniwill-laptop   | 11 +++++
 drivers/platform/x86/uniwill/uniwill-acpi.c   | 44 +++++++++++++++++--
 2 files changed, 51 insertions(+), 4 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-driver-uniwill-laptop b/Documentation/ABI/testing/sysfs-driver-uniwill-laptop
index f3060aaf8a76..5f7eccb2d205 100644
--- a/Documentation/ABI/testing/sysfs-driver-uniwill-laptop
+++ b/Documentation/ABI/testing/sysfs-driver-uniwill-laptop
@@ -92,3 +92,14 @@ Description:
 		boot at the same time is not supported.
 
 		Reading this file returns the current status of the USB powershare functionality.
+
+What:		/sys/class/power_supply/<battery_name>/charge_full_raw
+Date:		December 2026
+KernelVersion:	7.5
+Contact:	Armin Wolf <W_Armin@gmx.de>
+Description:
+		Allows userspace applications to read the raw full charge capacity of the
+		battery. This value might differ from the full chage capacity reported by the
+		battery itself, usually caused by postprocessing done inside the ACPI firmware.
+
+		Reading this file returns the raw full charge capacity in µAh of the battery.
diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c
index 66770f939c83..3d59b9a0f5bf 100644
--- a/drivers/platform/x86/uniwill/uniwill-acpi.c
+++ b/drivers/platform/x86/uniwill/uniwill-acpi.c
@@ -395,6 +395,7 @@ struct uniwill_data {
 	enum power_supply_property extension_props[3];
 	struct power_supply_ext standard_extension;
 	struct power_supply_ext cycle_count_extension;
+	struct device_attribute charge_full_raw_attr;
 	bool last_fn_lock_state;
 	bool last_super_key_enable_state;
 	bool last_touchpad_toggle_enable_state;
@@ -635,6 +636,8 @@ static bool uniwill_writeable_reg(struct device *dev, unsigned int reg)
 static bool uniwill_readable_reg(struct device *dev, unsigned int reg)
 {
 	switch (reg) {
+	case EC_ADDR_BAT_FULL_CAPACITY_1:
+	case EC_ADDR_BAT_FULL_CAPACITY_2:
 	case EC_ADDR_CPU_TEMP:
 	case EC_ADDR_GPU_TEMP:
 	case EC_ADDR_MAIN_FAN_RPM_1:
@@ -684,6 +687,8 @@ static bool uniwill_readable_reg(struct device *dev, unsigned int reg)
 static bool uniwill_volatile_reg(struct device *dev, unsigned int reg)
 {
 	switch (reg) {
+	case EC_ADDR_BAT_FULL_CAPACITY_1:
+	case EC_ADDR_BAT_FULL_CAPACITY_2:
 	case EC_ADDR_CPU_TEMP:
 	case EC_ADDR_GPU_TEMP:
 	case EC_ADDR_MAIN_FAN_RPM_1:
@@ -1932,6 +1937,21 @@ static int uniwill_kbd_led_init(struct uniwill_data *data)
 	return uniwill_rgb_kbd_led_init(data);
 }
 
+static ssize_t charge_full_raw_show(struct device *dev, const struct device_attribute *attr,
+				    char *buf)
+{
+	const struct uniwill_data *data = container_of_const(attr, struct uniwill_data,
+							     charge_full_raw_attr);
+	__le16 value;
+	int ret;
+
+	ret = regmap_bulk_read(data->regmap, EC_ADDR_BAT_FULL_CAPACITY_1, &value, sizeof(value));
+	if (ret < 0)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", le16_to_cpu(value) * 1000);
+}
+
 static unsigned int uniwill_sanitize_battery_threshold(unsigned int value)
 {
 	/* 0 means "charging threshold not active" */
@@ -2120,16 +2140,18 @@ static int uniwill_add_battery(struct power_supply *battery, struct acpi_battery
 	if (!entry)
 		return -ENOMEM;
 
+	ret = device_create_file(&battery->dev, &data->charge_full_raw_attr);
+	if (ret < 0)
+		goto err_free;
+
 	if (power_supply_has_property_direct(battery, POWER_SUPPLY_PROP_CYCLE_COUNT))
 		ext = &data->standard_extension;
 	else
 		ext = &data->cycle_count_extension;
 
 	ret = power_supply_register_extension(battery, ext, data->dev, data);
-	if (ret < 0) {
-		kfree(entry);
-		return ret;
-	}
+	if (ret < 0)
+		goto err_remove;
 
 	guard(mutex)(&data->battery_lock);
 
@@ -2137,6 +2159,14 @@ static int uniwill_add_battery(struct power_supply *battery, struct acpi_battery
 	list_add(&entry->head, &data->batteries);
 
 	return 0;
+
+err_remove:
+	device_remove_file(&battery->dev, &data->charge_full_raw_attr);
+
+err_free:
+	kfree(entry);
+
+	return ret;
 }
 
 static int uniwill_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
@@ -2155,6 +2185,7 @@ static int uniwill_remove_battery(struct power_supply *battery, struct acpi_batt
 		}
 	}
 
+	device_remove_file(&battery->dev, &data->charge_full_raw_attr);
 	if (power_supply_has_property_direct(battery, POWER_SUPPLY_PROP_CYCLE_COUNT))
 		ext = &data->standard_extension;
 	else
@@ -2239,6 +2270,11 @@ static int uniwill_battery_init(struct uniwill_data *data)
 	data->hook.add_battery = uniwill_add_battery;
 	data->hook.remove_battery = uniwill_remove_battery;
 
+	sysfs_attr_init(&data->charge_full_raw_attr.attr);
+	data->charge_full_raw_attr.attr.name = "charge_full_raw";
+	data->charge_full_raw_attr.attr.mode = 0444;
+	data->charge_full_raw_attr.show_const = charge_full_raw_show;
+
 	return devm_battery_hook_register(data->dev, &data->hook);
 }
 
-- 
2.56.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 6/9] platform/x86: uniwill-laptop: Mark sysfs attributes as const
  2026-11-10  0:06 [PATCH 0/9] platform/x86: uniwill-laptop: Complete battery support Armin Wolf
                   ` (4 preceding siblings ...)
  2026-11-10  0:06 ` [PATCH 5/9] platform/x86: uniwill-laptop: Expose raw battery capacity to userspace Armin Wolf
@ 2026-11-10  0:06 ` Armin Wolf
  2026-11-10  0:06 ` [PATCH 7/9] platform/x86: uniwill-laptop: Add support for MECHREVO Yilong15Pro Armin Wolf
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Armin Wolf @ 2026-11-10  0:06 UTC (permalink / raw)
  To: hansg, ilpo.jarvinen
  Cc: platform-driver-x86, linux-kernel, bifangknt, nicokessler, sre,
	linux-pm, wse

Mark all sysfs attributes as const so that they can be placed in
read only memory for increased security.

Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
 drivers/platform/x86/uniwill/uniwill-acpi.c | 63 ++++++++++-----------
 1 file changed, 31 insertions(+), 32 deletions(-)

diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c
index 3d59b9a0f5bf..a987fb310b81 100644
--- a/drivers/platform/x86/uniwill/uniwill-acpi.c
+++ b/drivers/platform/x86/uniwill/uniwill-acpi.c
@@ -740,8 +740,8 @@ static int uniwill_write_fn_lock(struct uniwill_data *data, bool status)
 	return regmap_update_bits(data->regmap, EC_ADDR_BIOS_OEM, FN_LOCK_STATUS, value);
 }
 
-static ssize_t fn_lock_store(struct device *dev, struct device_attribute *attr, const char *buf,
-			     size_t count)
+static ssize_t fn_lock_store(struct device *dev, const struct device_attribute *attr,
+			     const char *buf, size_t count)
 {
 	struct uniwill_data *data = dev_get_drvdata(dev);
 	bool enable;
@@ -772,7 +772,7 @@ static int uniwill_read_fn_lock(struct uniwill_data *data, bool *status)
 	return 0;
 }
 
-static ssize_t fn_lock_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t fn_lock_show(struct device *dev, const struct device_attribute *attr, char *buf)
 {
 	struct uniwill_data *data = dev_get_drvdata(dev);
 	bool status;
@@ -785,7 +785,7 @@ static ssize_t fn_lock_show(struct device *dev, struct device_attribute *attr, c
 	return sysfs_emit(buf, "%d\n", status);
 }
 
-static DEVICE_ATTR_RW(fn_lock);
+static const DEVICE_ATTR_RW(fn_lock);
 
 static int uniwill_write_super_key_enable(struct uniwill_data *data, bool status)
 {
@@ -809,7 +809,7 @@ static int uniwill_write_super_key_enable(struct uniwill_data *data, bool status
 				 TRIGGER_SUPER_KEY_LOCK);
 }
 
-static ssize_t super_key_enable_store(struct device *dev, struct device_attribute *attr,
+static ssize_t super_key_enable_store(struct device *dev, const struct device_attribute *attr,
 				      const char *buf, size_t count)
 {
 	struct uniwill_data *data = dev_get_drvdata(dev);
@@ -841,7 +841,8 @@ static int uniwill_read_super_key_enable(struct uniwill_data *data, bool *status
 	return 0;
 }
 
-static ssize_t super_key_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t super_key_enable_show(struct device *dev, const struct device_attribute *attr,
+				     char *buf)
 {
 	struct uniwill_data *data = dev_get_drvdata(dev);
 	bool status;
@@ -854,7 +855,7 @@ static ssize_t super_key_enable_show(struct device *dev, struct device_attribute
 	return sysfs_emit(buf, "%d\n", status);
 }
 
-static DEVICE_ATTR_RW(super_key_enable);
+static const DEVICE_ATTR_RW(super_key_enable);
 
 static int uniwill_write_touchpad_toggle_enable(struct uniwill_data *data, bool status)
 {
@@ -868,7 +869,7 @@ static int uniwill_write_touchpad_toggle_enable(struct uniwill_data *data, bool
 	return regmap_update_bits(data->regmap, EC_ADDR_OEM_4, TOUCHPAD_TOGGLE_OFF, value);
 }
 
-static ssize_t touchpad_toggle_enable_store(struct device *dev, struct device_attribute *attr,
+static ssize_t touchpad_toggle_enable_store(struct device *dev, const struct device_attribute *attr,
 					    const char *buf, size_t count)
 {
 	struct uniwill_data *data = dev_get_drvdata(dev);
@@ -900,7 +901,7 @@ static int uniwill_read_touchpad_toggle_enable(struct uniwill_data *data, bool *
 	return 0;
 }
 
-static ssize_t touchpad_toggle_enable_show(struct device *dev, struct device_attribute *attr,
+static ssize_t touchpad_toggle_enable_show(struct device *dev, const struct device_attribute *attr,
 					   char *buf)
 {
 	struct uniwill_data *data = dev_get_drvdata(dev);
@@ -914,9 +915,9 @@ static ssize_t touchpad_toggle_enable_show(struct device *dev, struct device_att
 	return sysfs_emit(buf, "%d\n", status);
 }
 
-static DEVICE_ATTR_RW(touchpad_toggle_enable);
+static const DEVICE_ATTR_RW(touchpad_toggle_enable);
 
-static ssize_t breathing_in_suspend_store(struct device *dev, struct device_attribute *attr,
+static ssize_t breathing_in_suspend_store(struct device *dev, const struct device_attribute *attr,
 					  const char *buf, size_t count)
 {
 	struct uniwill_data *data = dev_get_drvdata(dev);
@@ -942,7 +943,7 @@ static ssize_t breathing_in_suspend_store(struct device *dev, struct device_attr
 	return count;
 }
 
-static ssize_t breathing_in_suspend_show(struct device *dev, struct device_attribute *attr,
+static ssize_t breathing_in_suspend_show(struct device *dev, const struct device_attribute *attr,
 					 char *buf)
 {
 	struct uniwill_data *data = dev_get_drvdata(dev);
@@ -960,9 +961,9 @@ static ssize_t breathing_in_suspend_show(struct device *dev, struct device_attri
 	return sysfs_emit(buf, "%d\n", !(value & LIGHTBAR_S3_OFF));
 }
 
-static DEVICE_ATTR_RW(breathing_in_suspend);
+static const DEVICE_ATTR_RW(breathing_in_suspend);
 
-static ssize_t ctgp_offset_store(struct device *dev, struct device_attribute *attr,
+static ssize_t ctgp_offset_store(struct device *dev, const struct device_attribute *attr,
 				 const char *buf, size_t count)
 {
 	struct uniwill_data *data = dev_get_drvdata(dev);
@@ -983,7 +984,7 @@ static ssize_t ctgp_offset_store(struct device *dev, struct device_attribute *at
 	return count;
 }
 
-static ssize_t ctgp_offset_show(struct device *dev, struct device_attribute *attr,
+static ssize_t ctgp_offset_show(struct device *dev, const struct device_attribute *attr,
 				char *buf)
 {
 	struct uniwill_data *data = dev_get_drvdata(dev);
@@ -997,7 +998,7 @@ static ssize_t ctgp_offset_show(struct device *dev, struct device_attribute *att
 	return sysfs_emit(buf, "%u\n", value);
 }
 
-static DEVICE_ATTR_RW(ctgp_offset);
+static const DEVICE_ATTR_RW(ctgp_offset);
 
 static int uniwill_nvidia_ctgp_init(struct uniwill_data *data)
 {
@@ -1036,8 +1037,7 @@ static const u8 usb_c_power_priority_value[] = {
 	[USB_C_POWER_PRIORITY_PERFORMANCE]	= USB_C_POWER_PRIORITY,
 };
 
-static ssize_t usb_c_power_priority_store(struct device *dev,
-					  struct device_attribute *attr,
+static ssize_t usb_c_power_priority_store(struct device *dev, const struct device_attribute *attr,
 					  const char *buf, size_t count)
 {
 	struct uniwill_data *data = dev_get_drvdata(dev);
@@ -1064,8 +1064,7 @@ static ssize_t usb_c_power_priority_store(struct device *dev,
 	return count;
 }
 
-static ssize_t usb_c_power_priority_show(struct device *dev,
-					 struct device_attribute *attr,
+static ssize_t usb_c_power_priority_show(struct device *dev, const struct device_attribute *attr,
 					 char *buf)
 {
 	struct uniwill_data *data = dev_get_drvdata(dev);
@@ -1085,7 +1084,7 @@ static ssize_t usb_c_power_priority_show(struct device *dev,
 	return sysfs_emit(buf, "%s\n", usb_c_power_priority_text[USB_C_POWER_PRIORITY_CHARGING]);
 }
 
-static DEVICE_ATTR_RW(usb_c_power_priority);
+static const DEVICE_ATTR_RW(usb_c_power_priority);
 
 static int usb_c_power_priority_restore(struct uniwill_data *data)
 {
@@ -1125,7 +1124,7 @@ static int usb_c_power_priority_init(struct uniwill_data *data)
 	return 0;
 }
 
-static ssize_t ac_auto_boot_store(struct device *dev, struct device_attribute *attr,
+static ssize_t ac_auto_boot_store(struct device *dev, const struct device_attribute *attr,
 				  const char *buf, size_t count)
 {
 	struct uniwill_data *data = dev_get_drvdata(dev);
@@ -1149,7 +1148,7 @@ static ssize_t ac_auto_boot_store(struct device *dev, struct device_attribute *a
 	return count;
 }
 
-static ssize_t ac_auto_boot_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t ac_auto_boot_show(struct device *dev, const struct device_attribute *attr, char *buf)
 {
 	struct uniwill_data *data = dev_get_drvdata(dev);
 	unsigned int regval;
@@ -1162,7 +1161,7 @@ static ssize_t ac_auto_boot_show(struct device *dev, struct device_attribute *at
 	return sysfs_emit(buf, "%d\n", !!(regval & AC_AUTO_BOOT_ENABLE));
 }
 
-static DEVICE_ATTR_RW(ac_auto_boot);
+static const DEVICE_ATTR_RW(ac_auto_boot);
 
 static int uniwill_write_usb_powershare_high(struct uniwill_data *data, bool status)
 {
@@ -1180,7 +1179,7 @@ static int uniwill_write_usb_powershare_high(struct uniwill_data *data, bool sta
 	return regmap_update_bits(data->regmap, EC_ADDR_TRIGGER, TRIGGER_USB_CHARGING, value);
 }
 
-static ssize_t usb_powershare_high_store(struct device *dev, struct device_attribute *attr,
+static ssize_t usb_powershare_high_store(struct device *dev, const struct device_attribute *attr,
 					 const char *buf, size_t count)
 {
 	struct uniwill_data *data = dev_get_drvdata(dev);
@@ -1212,7 +1211,7 @@ static int uniwill_read_usb_powershare_high(struct uniwill_data *data, bool *sta
 	return 0;
 }
 
-static ssize_t usb_powershare_high_show(struct device *dev, struct device_attribute *attr,
+static ssize_t usb_powershare_high_show(struct device *dev, const struct device_attribute *attr,
 					char *buf)
 {
 	struct uniwill_data *data = dev_get_drvdata(dev);
@@ -1226,9 +1225,9 @@ static ssize_t usb_powershare_high_show(struct device *dev, struct device_attrib
 	return sysfs_emit(buf, "%d\n", status);
 }
 
-static DEVICE_ATTR_RW(usb_powershare_high);
+static const DEVICE_ATTR_RW(usb_powershare_high);
 
-static struct attribute *uniwill_attrs[] = {
+static const struct attribute * const uniwill_attrs[] = {
 	/* Keyboard-related */
 	&dev_attr_fn_lock.attr,
 	&dev_attr_super_key_enable.attr,
@@ -1243,7 +1242,7 @@ static struct attribute *uniwill_attrs[] = {
 	NULL
 };
 
-static umode_t uniwill_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n)
+static umode_t uniwill_attr_is_visible(struct kobject *kobj, const struct attribute *attr, int n)
 {
 	struct device *dev = kobj_to_dev(kobj);
 	struct uniwill_data *data = dev_get_drvdata(dev);
@@ -1292,11 +1291,11 @@ static umode_t uniwill_attr_is_visible(struct kobject *kobj, struct attribute *a
 }
 
 static const struct attribute_group uniwill_group = {
-	.is_visible = uniwill_attr_is_visible,
-	.attrs = uniwill_attrs,
+	.is_visible_const = uniwill_attr_is_visible,
+	.attrs_const = uniwill_attrs,
 };
 
-static const struct attribute_group *uniwill_groups[] = {
+static const struct attribute_group *const uniwill_groups[] = {
 	&uniwill_group,
 	NULL
 };
-- 
2.56.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 7/9] platform/x86: uniwill-laptop: Add support for MECHREVO Yilong15Pro
  2026-11-10  0:06 [PATCH 0/9] platform/x86: uniwill-laptop: Complete battery support Armin Wolf
                   ` (5 preceding siblings ...)
  2026-11-10  0:06 ` [PATCH 6/9] platform/x86: uniwill-laptop: Mark sysfs attributes as const Armin Wolf
@ 2026-11-10  0:06 ` Armin Wolf
  2026-11-10  0:06 ` [PATCH 8/9] platform/x86: uniwill-laptop: Add MECHREVO JIAOLONG Series-X6xR55xK Armin Wolf
  2026-11-10  0:06 ` [PATCH 9/9] platform/x86: uniwill-laptop: Add support for XMG EVO 14 (E25) Armin Wolf
  8 siblings, 0 replies; 11+ messages in thread
From: Armin Wolf @ 2026-11-10  0:06 UTC (permalink / raw)
  To: hansg, ilpo.jarvinen
  Cc: platform-driver-x86, linux-kernel, bifangknt, nicokessler, sre,
	linux-pm, wse

A user reported that the MECHREVO Yilong15Pro works with this driver.
Add it to the DMI whitelist to let the driver automatically load on
this device.

Reported-by: Bi Fang <bifangknt@qq.com>
Closes: https://lore.kernel.org/platform-driver-x86/tencent_D521DAA244039DBFC3919D4EFD176E383607@qq.com
Tested-by: Bi Fang <bifangknt@qq.com>
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
 drivers/platform/x86/uniwill/uniwill-acpi.c | 24 +++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c
index a987fb310b81..10c26822a607 100644
--- a/drivers/platform/x86/uniwill/uniwill-acpi.c
+++ b/drivers/platform/x86/uniwill/uniwill-acpi.c
@@ -2781,6 +2781,22 @@ static struct uniwill_device_descriptor machenike_l16p_descriptor __initdata = {
 	.kbd_led_max_brightness = 4,
 };
 
+static struct uniwill_device_descriptor mechrevo_yilong15pro_descriptor __initdata = {
+	.features = UNIWILL_FEATURE_FN_LOCK |
+		    UNIWILL_FEATURE_SUPER_KEY |
+		    UNIWILL_FEATURE_BATTERY_CHARGE_MODES |
+		    UNIWILL_FEATURE_CPU_TEMP |
+		    UNIWILL_FEATURE_GPU_TEMP |
+		    UNIWILL_FEATURE_PRIMARY_FAN |
+		    UNIWILL_FEATURE_SECONDARY_FAN |
+		    UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL |
+		    UNIWILL_FEATURE_KEYBOARD_BACKLIGHT |
+		    UNIWILL_FEATURE_AC_AUTO_BOOT |
+		    UNIWILL_FEATURE_USB_POWERSHARE,
+	.kbd_led_single_color = false,
+	.kbd_led_max_brightness = 4,
+};
+
 static struct uniwill_device_descriptor lapqc71a_lapqc71b_descriptor __initdata = {
 	.features = UNIWILL_FEATURE_SUPER_KEY |
 		    UNIWILL_FEATURE_LIGHTBAR |
@@ -3134,6 +3150,14 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = {
 		},
 		.driver_data = &machenike_l16p_descriptor,
 	},
+	{
+		.ident = "MECHREVO Yilong15Pro",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "MECHREVO"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM5HG0A"),
+		},
+		.driver_data = &mechrevo_yilong15pro_descriptor,
+	},
 	{
 		.ident = "XMG FUSION 15 (L19)",
 		.matches = {
-- 
2.56.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 8/9] platform/x86: uniwill-laptop: Add MECHREVO JIAOLONG Series-X6xR55xK
  2026-11-10  0:06 [PATCH 0/9] platform/x86: uniwill-laptop: Complete battery support Armin Wolf
                   ` (6 preceding siblings ...)
  2026-11-10  0:06 ` [PATCH 7/9] platform/x86: uniwill-laptop: Add support for MECHREVO Yilong15Pro Armin Wolf
@ 2026-11-10  0:06 ` Armin Wolf
  2026-11-10  0:06 ` [PATCH 9/9] platform/x86: uniwill-laptop: Add support for XMG EVO 14 (E25) Armin Wolf
  8 siblings, 0 replies; 11+ messages in thread
From: Armin Wolf @ 2026-11-10  0:06 UTC (permalink / raw)
  To: hansg, ilpo.jarvinen
  Cc: platform-driver-x86, linux-kernel, bifangknt, nicokessler, sre,
	linux-pm, wse

The MECHREVO JIAOLONG Series-X6xR55xK is similar to the TUXEDO
Stellaris 16 Gen7, but also supports RGB keyboard backlight. Add
the necessary device descriptor for it.

Closes: https://github.com/Wer-Wolf/uniwill-laptop/issues/20
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
 drivers/platform/x86/uniwill/uniwill-acpi.c | 24 +++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c
index 10c26822a607..6b0138246599 100644
--- a/drivers/platform/x86/uniwill/uniwill-acpi.c
+++ b/drivers/platform/x86/uniwill/uniwill-acpi.c
@@ -2797,6 +2797,22 @@ static struct uniwill_device_descriptor mechrevo_yilong15pro_descriptor __initda
 	.kbd_led_max_brightness = 4,
 };
 
+static struct uniwill_device_descriptor mechrevo_jiaolong_series_x6xr55xk __initdata = {
+	.features = UNIWILL_FEATURE_FN_LOCK |
+		    UNIWILL_FEATURE_SUPER_KEY |
+		    UNIWILL_FEATURE_BATTERY_CHARGE_MODES |
+		    UNIWILL_FEATURE_CPU_TEMP |
+		    UNIWILL_FEATURE_GPU_TEMP |
+		    UNIWILL_FEATURE_PRIMARY_FAN |
+		    UNIWILL_FEATURE_SECONDARY_FAN |
+		    UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL |
+		    UNIWILL_FEATURE_KEYBOARD_BACKLIGHT |
+		    UNIWILL_FEATURE_AC_AUTO_BOOT |
+		    UNIWILL_FEATURE_USB_POWERSHARE,
+	.kbd_led_single_color = false,
+	.kbd_led_max_brightness = 4,
+};
+
 static struct uniwill_device_descriptor lapqc71a_lapqc71b_descriptor __initdata = {
 	.features = UNIWILL_FEATURE_SUPER_KEY |
 		    UNIWILL_FEATURE_LIGHTBAR |
@@ -3158,6 +3174,14 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = {
 		},
 		.driver_data = &mechrevo_yilong15pro_descriptor,
 	},
+	{
+		.ident = "MECHREVO JIAOLONG Series-X6xR55xK",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "MECHREVO"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "JIAOLONG Series-X6xR55xK"),
+		},
+		.driver_data = &mechrevo_jiaolong_series_x6xr55xk,
+	},
 	{
 		.ident = "XMG FUSION 15 (L19)",
 		.matches = {
-- 
2.56.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 9/9] platform/x86: uniwill-laptop: Add support for XMG EVO 14 (E25)
  2026-11-10  0:06 [PATCH 0/9] platform/x86: uniwill-laptop: Complete battery support Armin Wolf
                   ` (7 preceding siblings ...)
  2026-11-10  0:06 ` [PATCH 8/9] platform/x86: uniwill-laptop: Add MECHREVO JIAOLONG Series-X6xR55xK Armin Wolf
@ 2026-11-10  0:06 ` Armin Wolf
  8 siblings, 0 replies; 11+ messages in thread
From: Armin Wolf @ 2026-11-10  0:06 UTC (permalink / raw)
  To: hansg, ilpo.jarvinen
  Cc: platform-driver-x86, linux-kernel, bifangknt, nicokessler, sre,
	linux-pm, wse

The XMG EVO 14 (E25) uses the same board as the TUXEDO InfinityBook
Pro 14 Gen10 AMD, but contains a different manufacturer string.

Add the necessary DMI entry.

Reported-by: Nico Kessler <nicokessler@icloud.com>
Closes: https://lore.kernel.org/platform-driver-x86/7FEA1980-AFC6-4A6C-84E1-E820B7E6CF3C@icloud.com
Tested-by: Nico Kessler <nicokessler@icloud.com>
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
 drivers/platform/x86/uniwill/uniwill-acpi.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c
index 6b0138246599..610ffb8d76bd 100644
--- a/drivers/platform/x86/uniwill/uniwill-acpi.c
+++ b/drivers/platform/x86/uniwill/uniwill-acpi.c
@@ -3214,6 +3214,14 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = {
 		},
 		.driver_data = &lapqc71a_lapqc71b_descriptor,
 	},
+	{
+		.ident = "XMG EVO 14 (E25)",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxKK4NAx_XxSP4NAx"),
+		},
+		.driver_data = &gxxhrxx_gxxmrxx_xxxx4nxx_descriptor,
+	},
 	{
 		.ident = "Avell A60 MUV",
 		.matches = {
-- 
2.56.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 5/9] platform/x86: uniwill-laptop: Expose raw battery capacity to userspace
  2026-11-10  0:06 ` [PATCH 5/9] platform/x86: uniwill-laptop: Expose raw battery capacity to userspace Armin Wolf
@ 2026-11-10 11:13   ` kernel test robot
  0 siblings, 0 replies; 11+ messages in thread
From: kernel test robot @ 2026-11-10 11:13 UTC (permalink / raw)
  To: Armin Wolf, hansg, ilpo.jarvinen
  Cc: llvm, oe-kbuild-all, platform-driver-x86, linux-kernel,
	bifangknt, nicokessler, sre, linux-pm, wse

Hi Armin,

kernel test robot noticed the following build errors:

[auto build test ERROR on sre-power-supply/for-next]
[also build test ERROR on linus/master v7.4-rc2 next-20261109]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Armin-Wolf/power-supply-core-Add-power_supply_has_property_direct/20261110-010643
base:   https://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply.git for-next
patch link:    https://lore.kernel.org/r/20261110001725.851443-6-W_Armin%40gmx.de
patch subject: [PATCH 5/9] platform/x86: uniwill-laptop: Expose raw battery capacity to userspace
config: i386-randconfig-1009-20261110 (https://download.01.org/0day-ci/archive/20261110/202611101928.MbCyLNXk-lkp@intel.com/config)
compiler: clang version 22.1.8 (https://github.com/llvm/llvm-project ca7933e47d3a3451d81e72ac174dcb5aa28b59d1)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261110/202611101928.MbCyLNXk-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202611101928.MbCyLNXk-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/platform/x86/uniwill/uniwill-acpi.c:2148:3: error: cannot jump from this goto statement to its label
    2148 |                 goto err_remove;
         |                 ^
   drivers/platform/x86/uniwill/uniwill-acpi.c:2150:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
    2150 |         guard(mutex)(&data->battery_lock);
         |         ^
   include/linux/cleanup.h:416:2: note: expanded from macro 'guard'
     416 |         CLASS(_name, __UNIQUE_ID(guard))
         |         ^
   include/linux/cleanup.h:299:3: note: expanded from macro 'CLASS'
     299 |                 class_##_name##_constructor
         |                 ^
   <scratch space>:19:1: note: expanded from here
      19 | class_mutex_constructor
         | ^
   note: (skipping 3 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:16:23: note: expanded from macro '__PASTE'
      16 | #define __PASTE(a, b) ___PASTE(a, b)
         |                       ^
   include/linux/compiler_types.h:15:24: note: expanded from macro '___PASTE'
      15 | #define ___PASTE(a, b) a##b
         |                        ^
   <scratch space>:25:1: note: expanded from here
      25 | __UNIQUE_ID_unlock_362
         | ^
   drivers/platform/x86/uniwill/uniwill-acpi.c:2150:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
   include/linux/cleanup.h:416:15: note: expanded from macro 'guard'
     416 |         CLASS(_name, __UNIQUE_ID(guard))
         |                      ^
   include/linux/compiler.h:165:2: note: expanded from macro '__UNIQUE_ID'
     165 |         __PASTE(__UNIQUE_ID_,                                   \
         |         ^
   include/linux/compiler_types.h:16:23: note: expanded from macro '__PASTE'
      16 | #define __PASTE(a, b) ___PASTE(a, b)
         |                       ^
   include/linux/compiler_types.h:15:24: note: expanded from macro '___PASTE'
      15 | #define ___PASTE(a, b) a##b
         |                        ^
   <scratch space>:13:1: note: expanded from here
      13 | __UNIQUE_ID_guard_361
         | ^
   drivers/platform/x86/uniwill/uniwill-acpi.c:2139:3: error: cannot jump from this goto statement to its label
    2139 |                 goto err_free;
         |                 ^
   drivers/platform/x86/uniwill/uniwill-acpi.c:2150:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
    2150 |         guard(mutex)(&data->battery_lock);
         |         ^
   include/linux/cleanup.h:416:2: note: expanded from macro 'guard'
     416 |         CLASS(_name, __UNIQUE_ID(guard))
         |         ^
   include/linux/cleanup.h:299:3: note: expanded from macro 'CLASS'
     299 |                 class_##_name##_constructor
         |                 ^
   <scratch space>:19:1: note: expanded from here
      19 | class_mutex_constructor
         | ^
   note: (skipping 3 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:16:23: note: expanded from macro '__PASTE'
      16 | #define __PASTE(a, b) ___PASTE(a, b)
         |                       ^
   include/linux/compiler_types.h:15:24: note: expanded from macro '___PASTE'
      15 | #define ___PASTE(a, b) a##b
         |                        ^
   <scratch space>:25:1: note: expanded from here
      25 | __UNIQUE_ID_unlock_362
         | ^
   drivers/platform/x86/uniwill/uniwill-acpi.c:2150:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
   include/linux/cleanup.h:416:15: note: expanded from macro 'guard'
     416 |         CLASS(_name, __UNIQUE_ID(guard))
         |                      ^
   include/linux/compiler.h:165:2: note: expanded from macro '__UNIQUE_ID'
     165 |         __PASTE(__UNIQUE_ID_,                                   \
         |         ^
   include/linux/compiler_types.h:16:23: note: expanded from macro '__PASTE'
      16 | #define __PASTE(a, b) ___PASTE(a, b)
         |                       ^
   include/linux/compiler_types.h:15:24: note: expanded from macro '___PASTE'
      15 | #define ___PASTE(a, b) a##b
         |                        ^
   <scratch space>:13:1: note: expanded from here
      13 | __UNIQUE_ID_guard_361
         | ^
   2 errors generated.


vim +2148 drivers/platform/x86/uniwill/uniwill-acpi.c

  2125	
  2126	static int uniwill_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
  2127	{
  2128		struct uniwill_data *data = container_of(hook, struct uniwill_data, hook);
  2129		struct uniwill_battery_entry *entry;
  2130		const struct power_supply_ext *ext;
  2131		int ret;
  2132	
  2133		entry = kzalloc_obj(*entry);
  2134		if (!entry)
  2135			return -ENOMEM;
  2136	
  2137		ret = device_create_file(&battery->dev, &data->charge_full_raw_attr);
  2138		if (ret < 0)
  2139			goto err_free;
  2140	
  2141		if (power_supply_has_property_direct(battery, POWER_SUPPLY_PROP_CYCLE_COUNT))
  2142			ext = &data->standard_extension;
  2143		else
  2144			ext = &data->cycle_count_extension;
  2145	
  2146		ret = power_supply_register_extension(battery, ext, data->dev, data);
  2147		if (ret < 0)
> 2148			goto err_remove;
  2149	
  2150		guard(mutex)(&data->battery_lock);
  2151	
  2152		entry->battery = battery;
  2153		list_add(&entry->head, &data->batteries);
  2154	
  2155		return 0;
  2156	
  2157	err_remove:
  2158		device_remove_file(&battery->dev, &data->charge_full_raw_attr);
  2159	
  2160	err_free:
  2161		kfree(entry);
  2162	
  2163		return ret;
  2164	}
  2165	

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-11-10 11:13 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-10  0:06 [PATCH 0/9] platform/x86: uniwill-laptop: Complete battery support Armin Wolf
2026-11-10  0:06 ` [PATCH 1/9] power: supply: core: Add power_supply_has_property_direct() Armin Wolf
2026-11-10  0:06 ` [PATCH 2/9] power: supply: Enable struct power_supply_ext to be construct dynamically Armin Wolf
2026-11-10  0:06 ` [PATCH 3/9] platform/x86: uniwill-laptop: Report battery cycle count Armin Wolf
2026-11-10  0:06 ` [PATCH 4/9] platform/x86: uniwill-laptop: Expose battery health and cycle count on all devices Armin Wolf
2026-11-10  0:06 ` [PATCH 5/9] platform/x86: uniwill-laptop: Expose raw battery capacity to userspace Armin Wolf
2026-11-10 11:13   ` kernel test robot
2026-11-10  0:06 ` [PATCH 6/9] platform/x86: uniwill-laptop: Mark sysfs attributes as const Armin Wolf
2026-11-10  0:06 ` [PATCH 7/9] platform/x86: uniwill-laptop: Add support for MECHREVO Yilong15Pro Armin Wolf
2026-11-10  0:06 ` [PATCH 8/9] platform/x86: uniwill-laptop: Add MECHREVO JIAOLONG Series-X6xR55xK Armin Wolf
2026-11-10  0:06 ` [PATCH 9/9] platform/x86: uniwill-laptop: Add support for XMG EVO 14 (E25) Armin Wolf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®