mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Armin Wolf <W_Armin@gmx.de>
To: hansg@kernel.org, ilpo.jarvinen@linux.intel.com
Cc: platform-driver-x86@vger.kernel.org,
	linux-kernel@vger.kernel.org, bifangknt@qq.com,
	nicokessler@icloud.com, sre@kernel.org, linux-pm@vger.kernel.org,
	wse@tuxedocomputers.com
Subject: [PATCH 3/9] platform/x86: uniwill-laptop: Report battery cycle count
Date: Tue, 10 Nov 2026 01:06:45 +0100	[thread overview]
Message-ID: <20261110001725.851443-4-W_Armin@gmx.de> (raw)
In-Reply-To: <20261110001725.851443-1-W_Armin@gmx.de>

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


  parent reply	other threads:[~2026-11-10  0:19 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
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  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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261110001725.851443-4-W_Armin@gmx.de \
    --to=w_armin@gmx.de \
    --cc=bifangknt@qq.com \
    --cc=hansg@kernel.org \
    --cc=ilpo.jarvinen@linux.intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=nicokessler@icloud.com \
    --cc=platform-driver-x86@vger.kernel.org \
    --cc=sre@kernel.org \
    --cc=wse@tuxedocomputers.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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®