]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
platform/x86: ayaneo-ec: Add charge control support
authorAntheas Kapenekakis <lkml@antheas.dev>
Wed, 19 Nov 2025 17:45:02 +0000 (18:45 +0100)
committerIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Fri, 21 Nov 2025 15:36:17 +0000 (17:36 +0200)
Ayaneo devices support charge inhibition via the EC. This inhibition
only works while the device is powered on, and resets between restarts.
However, it is maintained across suspend/resume cycles.

The EC does not support charge threshold control. Instead, userspace
software on Windows manually toggles charge inhibition depending on
battery level.

Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
Link: https://patch.msgid.link/20251119174505.597218-4-lkml@antheas.dev
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
drivers/platform/x86/Kconfig
drivers/platform/x86/ayaneo-ec.c

index 11850e1060204fd096ad7b265a3876310943b51a..81461c6edd925583437e62d3488e6c2f896a6f46 100644 (file)
@@ -315,6 +315,7 @@ config AYANEO_EC
        tristate "Ayaneo EC platform control"
        depends on DMI
        depends on ACPI_EC
+       depends on ACPI_BATTERY
        depends on HWMON
        help
          Enables support for the platform EC of Ayaneo devices. This
index 86d4eed49f420bc75ba579d7eafd4aec65e2e744..69901ac335eb90c85bc798f3b6c040fa5525352d 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <acpi/battery.h>
 
 #define AYANEO_PWM_ENABLE_REG   0x4A
 #define AYANEO_PWM_REG          0x4B
 
 #define AYANEO_FAN_REG          0x76
 
+#define EC_CHARGE_CONTROL_BEHAVIOURS                         \
+       (BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) |           \
+        BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE))
+#define AYANEO_CHARGE_REG              0x1e
+#define AYANEO_CHARGE_VAL_AUTO         0xaa
+#define AYANEO_CHARGE_VAL_INHIBIT      0x55
+
 struct ayaneo_ec_quirk {
        bool has_fan_control;
+       bool has_charge_control;
 };
 
 struct ayaneo_ec_platform_data {
        struct platform_device *pdev;
        struct ayaneo_ec_quirk *quirks;
+       struct acpi_battery_hook battery_hook;
 };
 
 static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
        .has_fan_control = true,
+       .has_charge_control = true,
 };
 
 static const struct dmi_system_id dmi_table[] = {
@@ -164,11 +176,102 @@ static const struct hwmon_chip_info ayaneo_ec_chip_info = {
        .info = ayaneo_ec_sensors,
 };
 
+static int ayaneo_psy_ext_get_prop(struct power_supply *psy,
+                                  const struct power_supply_ext *ext,
+                                  void *data,
+                                  enum power_supply_property psp,
+                                  union power_supply_propval *val)
+{
+       int ret;
+       u8 tmp;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
+               ret = ec_read(AYANEO_CHARGE_REG, &tmp);
+               if (ret)
+                       return ret;
+
+               if (tmp == AYANEO_CHARGE_VAL_INHIBIT)
+                       val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
+               else
+                       val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int ayaneo_psy_ext_set_prop(struct power_supply *psy,
+                                  const struct power_supply_ext *ext,
+                                  void *data,
+                                  enum power_supply_property psp,
+                                  const union power_supply_propval *val)
+{
+       u8 raw_val;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
+               switch (val->intval) {
+               case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
+                       raw_val = AYANEO_CHARGE_VAL_AUTO;
+                       break;
+               case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
+                       raw_val = AYANEO_CHARGE_VAL_INHIBIT;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               return ec_write(AYANEO_CHARGE_REG, raw_val);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int ayaneo_psy_prop_is_writeable(struct power_supply *psy,
+                                       const struct power_supply_ext *ext,
+                                       void *data,
+                                       enum power_supply_property psp)
+{
+       return true;
+}
+
+static const enum power_supply_property ayaneo_psy_ext_props[] = {
+       POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
+};
+
+static const struct power_supply_ext ayaneo_psy_ext = {
+       .name                   = "ayaneo-charge-control",
+       .properties             = ayaneo_psy_ext_props,
+       .num_properties         = ARRAY_SIZE(ayaneo_psy_ext_props),
+       .charge_behaviours      = EC_CHARGE_CONTROL_BEHAVIOURS,
+       .get_property           = ayaneo_psy_ext_get_prop,
+       .set_property           = ayaneo_psy_ext_set_prop,
+       .property_is_writeable  = ayaneo_psy_prop_is_writeable,
+};
+
+static int ayaneo_add_battery(struct power_supply *battery,
+                             struct acpi_battery_hook *hook)
+{
+       struct ayaneo_ec_platform_data *data =
+               container_of(hook, struct ayaneo_ec_platform_data, battery_hook);
+
+       return power_supply_register_extension(battery, &ayaneo_psy_ext,
+                                              &data->pdev->dev, NULL);
+}
+
+static int ayaneo_remove_battery(struct power_supply *battery,
+                                struct acpi_battery_hook *hook)
+{
+       power_supply_unregister_extension(battery, &ayaneo_psy_ext);
+       return 0;
+}
+
 static int ayaneo_ec_probe(struct platform_device *pdev)
 {
        const struct dmi_system_id *dmi_entry;
        struct ayaneo_ec_platform_data *data;
        struct device *hwdev;
+       int ret;
 
        dmi_entry = dmi_first_match(dmi_table);
        if (!dmi_entry)
@@ -189,6 +292,15 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
                        return PTR_ERR(hwdev);
        }
 
+       if (data->quirks->has_charge_control) {
+               data->battery_hook.add_battery = ayaneo_add_battery;
+               data->battery_hook.remove_battery = ayaneo_remove_battery;
+               data->battery_hook.name = "Ayaneo Battery";
+               ret = devm_battery_hook_register(&pdev->dev, &data->battery_hook);
+               if (ret)
+                       return ret;
+       }
+
        return 0;
 }