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 5/9] platform/x86: uniwill-laptop: Expose raw battery capacity to userspace
Date: Tue, 10 Nov 2026 01:06:47 +0100 [thread overview]
Message-ID: <20261110001725.851443-6-W_Armin@gmx.de> (raw)
In-Reply-To: <20261110001725.851443-1-W_Armin@gmx.de>
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
next prev 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 ` [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 ` Armin Wolf [this message]
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-6-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®