From: Sebastian Reichel <sebastian.reichel@collabora.com>
To: Svyatoslav Ryhel <clamor95@gmail.com>
Cc: "Rob Herring" <robh@kernel.org>,
"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
"Conor Dooley" <conor+dt@kernel.org>,
"Dmitry Torokhov" <dmitry.torokhov@gmail.com>,
"Lee Jones" <lee@kernel.org>, "Pavel Machek" <pavel@kernel.org>,
"Ion Agorria" <ion@agorria.com>,
"Michał Mirosław" <mirq-linux@rere.qmqm.pl>,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-input@vger.kernel.org, linux-leds@vger.kernel.org,
linux-pm@vger.kernel.org
Subject: Re: [PATCH v5 6/7] power: supply: Add driver for ASUS Transformer battery
Date: Wed, 11 Mar 2026 07:30:33 +0100 [thread overview]
Message-ID: <abELVIzUrWmno8cX@venus> (raw)
In-Reply-To: <20260304185751.83494-7-clamor95@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 10929 bytes --]
Hi,
On Wed, Mar 04, 2026 at 08:57:50PM +0200, Svyatoslav Ryhel wrote:
> From: Michał Mirosław <mirq-linux@rere.qmqm.pl>
>
> Driver implements one battery cell per EC controller and supports reading
> of battery status for ASUS Transformer's pad and mobile dock.
>
> Co-developed-by: Svyatoslav Ryhel <clamor95@gmail.com>
> Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
> ---
> drivers/power/supply/Kconfig | 11 +
> drivers/power/supply/Makefile | 1 +
> .../supply/asus-transformer-ec-battery.c | 272 ++++++++++++++++++
> 3 files changed, 284 insertions(+)
> create mode 100644 drivers/power/supply/asus-transformer-ec-battery.c
>
> diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
> index 81fadb0695a9..3c46b412632d 100644
> --- a/drivers/power/supply/Kconfig
> +++ b/drivers/power/supply/Kconfig
> @@ -122,6 +122,17 @@ config BATTERY_CHAGALL
> This driver can also be built as a module. If so, the module will be
> called chagall-battery.
>
> +config BATTERY_ASUS_TRANSFORMER_EC
> + tristate "Asus Transformer's battery driver"
> + depends on MFD_ASUS_TRANSFORMER_EC
> + help
> + Say Y here to enable support APM status emulation using
> + battery class devices.
^^^
You forgot to drop that when you used the APM_POWER config entry as
template. Otherwise the driver LGTM:
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Greetings,
-- Sebastian
> + This sub-driver supports battery cells found in Asus Transformer
> + tablets and mobile docks and controlled by special embedded
> + controller.
> +
> config BATTERY_CPCAP
> tristate "Motorola CPCAP PMIC battery driver"
> depends on MFD_CPCAP && IIO
> diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
> index 41c400bbf022..aa5e6b05b018 100644
> --- a/drivers/power/supply/Makefile
> +++ b/drivers/power/supply/Makefile
> @@ -22,6 +22,7 @@ obj-$(CONFIG_TEST_POWER) += test_power.o
> obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o
> obj-$(CONFIG_CHARGER_ADP5061) += adp5061.o
> obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.o
> +obj-$(CONFIG_BATTERY_ASUS_TRANSFORMER_EC) += asus-transformer-ec-battery.o
> obj-$(CONFIG_BATTERY_AXP20X) += axp20x_battery.o
> obj-$(CONFIG_CHARGER_AXP20X) += axp20x_ac_power.o
> obj-$(CONFIG_BATTERY_CHAGALL) += chagall-battery.o
> diff --git a/drivers/power/supply/asus-transformer-ec-battery.c b/drivers/power/supply/asus-transformer-ec-battery.c
> new file mode 100644
> index 000000000000..aefcd3fed6fe
> --- /dev/null
> +++ b/drivers/power/supply/asus-transformer-ec-battery.c
> @@ -0,0 +1,272 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +
> +#include <linux/array_size.h>
> +#include <linux/devm-helpers.h>
> +#include <linux/err.h>
> +#include <linux/mfd/asus-transformer-ec.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_device.h>
> +#include <linux/power_supply.h>
> +#include <linux/property.h>
> +#include <linux/unaligned.h>
> +
> +#define ASUSEC_BATTERY_DATA_FRESH_MSEC 5000
> +
> +#define ASUSEC_BATTERY_DISCHARGING 0x40
> +#define ASUSEC_BATTERY_FULL_CHARGED 0x20
> +#define ASUSEC_BATTERY_NOT_CHARGING 0x10
> +
> +#define TEMP_CELSIUS_OFFSET 2731
> +
> +struct asus_ec_battery_data {
> + const struct asusec_info *ec;
> + struct power_supply *battery;
> + struct power_supply_desc psy_desc;
> + struct delayed_work poll_work;
> + struct mutex battery_lock; /* for data refresh */
> + unsigned long batt_data_ts;
> + int last_state;
> + u8 batt_data[DOCKRAM_ENTRY_BUFSIZE];
> +};
> +
> +static int asus_ec_battery_refresh(struct asus_ec_battery_data *priv)
> +{
> + int ret = 0;
> +
> + guard(mutex)(&priv->battery_lock);
> +
> + if (time_before(jiffies, priv->batt_data_ts))
> + return ret;
> +
> + ret = asus_dockram_read(priv->ec->dockram, ASUSEC_DOCKRAM_BATT_CTL,
> + priv->batt_data);
> + if (ret < 0)
> + return ret;
> +
> + priv->batt_data_ts = jiffies +
> + msecs_to_jiffies(ASUSEC_BATTERY_DATA_FRESH_MSEC);
> +
> + return ret;
> +}
> +
> +static enum power_supply_property asus_ec_battery_properties[] = {
> + POWER_SUPPLY_PROP_STATUS,
> + POWER_SUPPLY_PROP_VOLTAGE_MAX,
> + POWER_SUPPLY_PROP_CURRENT_MAX,
> + POWER_SUPPLY_PROP_TEMP,
> + POWER_SUPPLY_PROP_VOLTAGE_NOW,
> + POWER_SUPPLY_PROP_CURRENT_NOW,
> + POWER_SUPPLY_PROP_CAPACITY,
> + POWER_SUPPLY_PROP_CHARGE_NOW,
> + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
> + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
> + POWER_SUPPLY_PROP_PRESENT,
> +};
> +
> +static const unsigned int asus_ec_battery_prop_offs[] = {
> + [POWER_SUPPLY_PROP_STATUS] = 1,
> + [POWER_SUPPLY_PROP_VOLTAGE_MAX] = 3,
> + [POWER_SUPPLY_PROP_CURRENT_MAX] = 5,
> + [POWER_SUPPLY_PROP_TEMP] = 7,
> + [POWER_SUPPLY_PROP_VOLTAGE_NOW] = 9,
> + [POWER_SUPPLY_PROP_CURRENT_NOW] = 11,
> + [POWER_SUPPLY_PROP_CAPACITY] = 13,
> + [POWER_SUPPLY_PROP_CHARGE_NOW] = 15,
> + [POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW] = 17,
> + [POWER_SUPPLY_PROP_TIME_TO_FULL_NOW] = 19,
> +};
> +
> +static int asus_ec_battery_get_value(struct asus_ec_battery_data *priv,
> + enum power_supply_property psp)
> +{
> + int ret, offs;
> +
> + if (psp >= ARRAY_SIZE(asus_ec_battery_prop_offs))
> + return -EINVAL;
> +
> + offs = asus_ec_battery_prop_offs[psp];
> + if (!offs)
> + return -EINVAL;
> +
> + ret = asus_ec_battery_refresh(priv);
> + if (ret < 0)
> + return ret;
> +
> + if (offs >= priv->batt_data[0])
> + return -ENODATA;
> +
> + return get_unaligned_le16(priv->batt_data + offs);
> +}
> +
> +static int asus_ec_battery_get_property(struct power_supply *psy,
> + enum power_supply_property psp,
> + union power_supply_propval *val)
> +{
> + struct asus_ec_battery_data *priv = power_supply_get_drvdata(psy);
> + int ret;
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_PRESENT:
> + val->intval = 1;
> + break;
> +
> + default:
> + ret = asus_ec_battery_get_value(priv, psp);
> + if (ret < 0)
> + return ret;
> +
> + val->intval = (s16)ret;
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_STATUS:
> + if (ret & ASUSEC_BATTERY_FULL_CHARGED)
> + val->intval = POWER_SUPPLY_STATUS_FULL;
> + else if (ret & ASUSEC_BATTERY_NOT_CHARGING)
> + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
> + else if (ret & ASUSEC_BATTERY_DISCHARGING)
> + val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
> + else
> + val->intval = POWER_SUPPLY_STATUS_CHARGING;
> + break;
> +
> + case POWER_SUPPLY_PROP_TEMP:
> + val->intval -= TEMP_CELSIUS_OFFSET;
> + break;
> +
> + case POWER_SUPPLY_PROP_CHARGE_NOW:
> + case POWER_SUPPLY_PROP_CURRENT_NOW:
> + case POWER_SUPPLY_PROP_CURRENT_MAX:
> + case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> + case POWER_SUPPLY_PROP_VOLTAGE_MAX:
> + val->intval *= 1000;
> + break;
> +
> + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
> + case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
> + val->intval *= 60;
> + break;
> +
> + default:
> + break;
> + }
> +
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static void asus_ec_battery_poll_work(struct work_struct *work)
> +{
> + struct asus_ec_battery_data *priv =
> + container_of(work, struct asus_ec_battery_data, poll_work.work);
> + int state;
> +
> + state = asus_ec_battery_get_value(priv, POWER_SUPPLY_PROP_STATUS);
> + if (state < 0)
> + return;
> +
> + if (state & ASUSEC_BATTERY_FULL_CHARGED)
> + state = POWER_SUPPLY_STATUS_FULL;
> + else if (state & ASUSEC_BATTERY_DISCHARGING)
> + state = POWER_SUPPLY_STATUS_DISCHARGING;
> + else
> + state = POWER_SUPPLY_STATUS_CHARGING;
> +
> + if (priv->last_state != state) {
> + priv->last_state = state;
> + power_supply_changed(priv->battery);
> + }
> +
> + /* continuously send uevent notification */
> + schedule_delayed_work(&priv->poll_work,
> + msecs_to_jiffies(ASUSEC_BATTERY_DATA_FRESH_MSEC));
> +}
> +
> +static const struct power_supply_desc asus_ec_battery_desc = {
> + .name = "asus-ec-battery",
> + .type = POWER_SUPPLY_TYPE_BATTERY,
> + .properties = asus_ec_battery_properties,
> + .num_properties = ARRAY_SIZE(asus_ec_battery_properties),
> + .get_property = asus_ec_battery_get_property,
> + .external_power_changed = power_supply_changed,
> +};
> +
> +static int asus_ec_battery_probe(struct platform_device *pdev)
> +{
> + struct asus_ec_battery_data *priv;
> + struct device *dev = &pdev->dev;
> + struct power_supply_config cfg = { };
> + int ret;
> +
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, priv);
> +
> + mutex_init(&priv->battery_lock);
> +
> + priv->ec = cell_to_ec(pdev);
> + priv->batt_data_ts = jiffies - 1;
> + priv->last_state = POWER_SUPPLY_STATUS_UNKNOWN;
> +
> + cfg.fwnode = dev_fwnode(dev->parent);
> + cfg.drv_data = priv;
> +
> + memcpy(&priv->psy_desc, &asus_ec_battery_desc, sizeof(priv->psy_desc));
> + priv->psy_desc.name = devm_kasprintf(dev, GFP_KERNEL, "%s-battery",
> + priv->ec->name);
> +
> + priv->battery = devm_power_supply_register(dev, &priv->psy_desc, &cfg);
> + if (IS_ERR(priv->battery))
> + return dev_err_probe(dev, PTR_ERR(priv->battery),
> + "Failed to register power supply\n");
> +
> + ret = devm_delayed_work_autocancel(dev, &priv->poll_work,
> + asus_ec_battery_poll_work);
> + if (ret)
> + return ret;
> +
> + schedule_delayed_work(&priv->poll_work,
> + msecs_to_jiffies(ASUSEC_BATTERY_DATA_FRESH_MSEC));
> +
> + return 0;
> +}
> +
> +static int __maybe_unused asus_ec_battery_suspend(struct device *dev)
> +{
> + struct asus_ec_battery_data *priv = dev_get_drvdata(dev);
> +
> + cancel_delayed_work_sync(&priv->poll_work);
> +
> + return 0;
> +}
> +
> +static int __maybe_unused asus_ec_battery_resume(struct device *dev)
> +{
> + struct asus_ec_battery_data *priv = dev_get_drvdata(dev);
> +
> + schedule_delayed_work(&priv->poll_work,
> + msecs_to_jiffies(ASUSEC_BATTERY_DATA_FRESH_MSEC));
> +
> + return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(asus_ec_battery_pm_ops,
> + asus_ec_battery_suspend, asus_ec_battery_resume);
> +
> +static struct platform_driver asus_ec_battery_driver = {
> + .driver = {
> + .name = "asus-transformer-ec-battery",
> + .pm = &asus_ec_battery_pm_ops,
> + },
> + .probe = asus_ec_battery_probe,
> +};
> +module_platform_driver(asus_ec_battery_driver);
> +
> +MODULE_AUTHOR("Michał Mirosław <mirq-linux@rere.qmqm.pl>");
> +MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
> +MODULE_DESCRIPTION("ASUS Transformer's battery driver");
> +MODULE_LICENSE("GPL");
> --
> 2.51.0
>
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
next prev parent reply other threads:[~2026-03-11 6:31 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-04 18:57 [PATCH v5 0/7] mfd: Add support for Asus Transformer embedded controller Svyatoslav Ryhel
2026-03-04 18:57 ` [PATCH v5 1/7] dt-bindings: embedded-controller: document ASUS Transformer EC Svyatoslav Ryhel
2026-03-12 14:00 ` Rob Herring (Arm)
2026-03-04 18:57 ` [PATCH v5 2/7] mfd: Add driver for ASUS Transformer embedded controller Svyatoslav Ryhel
2026-03-07 0:51 ` kernel test robot
2026-03-07 1:56 ` kernel test robot
2026-03-04 18:57 ` [PATCH v5 3/7] input: serio: Add driver for ASUS Transformer dock keyboard and touchpad Svyatoslav Ryhel
2026-03-04 18:57 ` [PATCH v5 4/7] input: keyboard: Add driver for ASUS Transformer dock multimedia keys Svyatoslav Ryhel
2026-03-04 18:57 ` [PATCH v5 5/7] leds: Add driver for ASUS Transformer LEDs Svyatoslav Ryhel
2026-03-04 18:57 ` [PATCH v5 6/7] power: supply: Add driver for ASUS Transformer battery Svyatoslav Ryhel
2026-03-11 6:30 ` Sebastian Reichel [this message]
2026-03-11 8:14 ` Svyatoslav Ryhel
2026-03-04 18:57 ` [PATCH v5 7/7] power: supply: Add charger driver for Asus Transformers Svyatoslav Ryhel
2026-03-11 6:47 ` Sebastian Reichel
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=abELVIzUrWmno8cX@venus \
--to=sebastian.reichel@collabora.com \
--cc=clamor95@gmail.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=dmitry.torokhov@gmail.com \
--cc=ion@agorria.com \
--cc=krzk+dt@kernel.org \
--cc=lee@kernel.org \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-leds@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=mirq-linux@rere.qmqm.pl \
--cc=pavel@kernel.org \
--cc=robh@kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.