Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

platform/x86: ayaneo-ec: Add charge control support

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>

authored by

Antheas Kapenekakis and committed by
Ilpo Järvinen
6d710ec3 536522f0

+113
+1
drivers/platform/x86/Kconfig
··· 315 315 tristate "Ayaneo EC platform control" 316 316 depends on DMI 317 317 depends on ACPI_EC 318 + depends on ACPI_BATTERY 318 319 depends on HWMON 319 320 help 320 321 Enables support for the platform EC of Ayaneo devices. This
+112
drivers/platform/x86/ayaneo-ec.c
··· 15 15 #include <linux/kernel.h> 16 16 #include <linux/module.h> 17 17 #include <linux/platform_device.h> 18 + #include <linux/power_supply.h> 19 + #include <acpi/battery.h> 18 20 19 21 #define AYANEO_PWM_ENABLE_REG 0x4A 20 22 #define AYANEO_PWM_REG 0x4B ··· 25 23 26 24 #define AYANEO_FAN_REG 0x76 27 25 26 + #define EC_CHARGE_CONTROL_BEHAVIOURS \ 27 + (BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) | \ 28 + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE)) 29 + #define AYANEO_CHARGE_REG 0x1e 30 + #define AYANEO_CHARGE_VAL_AUTO 0xaa 31 + #define AYANEO_CHARGE_VAL_INHIBIT 0x55 32 + 28 33 struct ayaneo_ec_quirk { 29 34 bool has_fan_control; 35 + bool has_charge_control; 30 36 }; 31 37 32 38 struct ayaneo_ec_platform_data { 33 39 struct platform_device *pdev; 34 40 struct ayaneo_ec_quirk *quirks; 41 + struct acpi_battery_hook battery_hook; 35 42 }; 36 43 37 44 static const struct ayaneo_ec_quirk quirk_ayaneo3 = { 38 45 .has_fan_control = true, 46 + .has_charge_control = true, 39 47 }; 40 48 41 49 static const struct dmi_system_id dmi_table[] = { ··· 176 164 .info = ayaneo_ec_sensors, 177 165 }; 178 166 167 + static int ayaneo_psy_ext_get_prop(struct power_supply *psy, 168 + const struct power_supply_ext *ext, 169 + void *data, 170 + enum power_supply_property psp, 171 + union power_supply_propval *val) 172 + { 173 + int ret; 174 + u8 tmp; 175 + 176 + switch (psp) { 177 + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: 178 + ret = ec_read(AYANEO_CHARGE_REG, &tmp); 179 + if (ret) 180 + return ret; 181 + 182 + if (tmp == AYANEO_CHARGE_VAL_INHIBIT) 183 + val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE; 184 + else 185 + val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; 186 + return 0; 187 + default: 188 + return -EINVAL; 189 + } 190 + } 191 + 192 + static int ayaneo_psy_ext_set_prop(struct power_supply *psy, 193 + const struct power_supply_ext *ext, 194 + void *data, 195 + enum power_supply_property psp, 196 + const union power_supply_propval *val) 197 + { 198 + u8 raw_val; 199 + 200 + switch (psp) { 201 + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: 202 + switch (val->intval) { 203 + case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO: 204 + raw_val = AYANEO_CHARGE_VAL_AUTO; 205 + break; 206 + case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE: 207 + raw_val = AYANEO_CHARGE_VAL_INHIBIT; 208 + break; 209 + default: 210 + return -EINVAL; 211 + } 212 + return ec_write(AYANEO_CHARGE_REG, raw_val); 213 + default: 214 + return -EINVAL; 215 + } 216 + } 217 + 218 + static int ayaneo_psy_prop_is_writeable(struct power_supply *psy, 219 + const struct power_supply_ext *ext, 220 + void *data, 221 + enum power_supply_property psp) 222 + { 223 + return true; 224 + } 225 + 226 + static const enum power_supply_property ayaneo_psy_ext_props[] = { 227 + POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, 228 + }; 229 + 230 + static const struct power_supply_ext ayaneo_psy_ext = { 231 + .name = "ayaneo-charge-control", 232 + .properties = ayaneo_psy_ext_props, 233 + .num_properties = ARRAY_SIZE(ayaneo_psy_ext_props), 234 + .charge_behaviours = EC_CHARGE_CONTROL_BEHAVIOURS, 235 + .get_property = ayaneo_psy_ext_get_prop, 236 + .set_property = ayaneo_psy_ext_set_prop, 237 + .property_is_writeable = ayaneo_psy_prop_is_writeable, 238 + }; 239 + 240 + static int ayaneo_add_battery(struct power_supply *battery, 241 + struct acpi_battery_hook *hook) 242 + { 243 + struct ayaneo_ec_platform_data *data = 244 + container_of(hook, struct ayaneo_ec_platform_data, battery_hook); 245 + 246 + return power_supply_register_extension(battery, &ayaneo_psy_ext, 247 + &data->pdev->dev, NULL); 248 + } 249 + 250 + static int ayaneo_remove_battery(struct power_supply *battery, 251 + struct acpi_battery_hook *hook) 252 + { 253 + power_supply_unregister_extension(battery, &ayaneo_psy_ext); 254 + return 0; 255 + } 256 + 179 257 static int ayaneo_ec_probe(struct platform_device *pdev) 180 258 { 181 259 const struct dmi_system_id *dmi_entry; 182 260 struct ayaneo_ec_platform_data *data; 183 261 struct device *hwdev; 262 + int ret; 184 263 185 264 dmi_entry = dmi_first_match(dmi_table); 186 265 if (!dmi_entry) ··· 290 187 "ayaneo_ec", NULL, &ayaneo_ec_chip_info, NULL); 291 188 if (IS_ERR(hwdev)) 292 189 return PTR_ERR(hwdev); 190 + } 191 + 192 + if (data->quirks->has_charge_control) { 193 + data->battery_hook.add_battery = ayaneo_add_battery; 194 + data->battery_hook.remove_battery = ayaneo_remove_battery; 195 + data->battery_hook.name = "Ayaneo Battery"; 196 + ret = devm_battery_hook_register(&pdev->dev, &data->battery_hook); 197 + if (ret) 198 + return ret; 293 199 } 294 200 295 201 return 0;