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 controller power and modules attributes

The Ayaneo 3 features hot-swappable controller modules. The ejection
and management is done through HID. However, after ejecting the modules,
the controller needs to be power cycled via the EC to re-initialize.

For this, the EC provides a variable that holds whether the left or
right modules are connected, and a power control register to turn
the controller on or off. After ejecting the modules, the controller
should be turned off. Then, after both modules are reinserted,
the controller may be powered on again to re-initialize.

This patch introduces two new sysfs attributes:
- `controller_modules`: a read-only attribute that indicates whether
the left and right modules are connected (none, left, right, both).
- `controller_power`: a read-write attribute that allows the user
to turn the controller on or off (with '1'/'0').

Therefore, after ejection is complete, userspace can power off the
controller, then wait until both modules have been reinserted
(`controller_modules` will return 'both') to turn on the controller.

Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
Link: https://patch.msgid.link/20251119174505.597218-5-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
e921a8b4 6d710ec3

+127
+19
Documentation/ABI/testing/sysfs-platform-ayaneo-ec
··· 1 + What: /sys/devices/platform/ayaneo-ec/controller_power 2 + Date: Nov 2025 3 + KernelVersion: 6.19 4 + Contact: "Antheas Kapenekakis" <lkml@antheas.dev> 5 + Description: 6 + Current controller power state. Allows turning on and off 7 + the controller power (e.g. for power savings). Write 1 to 8 + turn on, 0 to turn off. File is readable and writable. 9 + 10 + What: /sys/devices/platform/ayaneo-ec/controller_modules 11 + Date: Nov 2025 12 + KernelVersion: 6.19 13 + Contact: "Antheas Kapenekakis" <lkml@antheas.dev> 14 + Description: 15 + Shows which controller modules are currently connected to 16 + the device. Possible values are "left", "right" and "both". 17 + File is read-only. The Windows software for this device 18 + will only set controller power to 1 if both module sides 19 + are connected (i.e. this file returns "both").
+1
MAINTAINERS
··· 4191 4191 M: Antheas Kapenekakis <lkml@antheas.dev> 4192 4192 L: platform-driver-x86@vger.kernel.org 4193 4193 S: Maintained 4194 + F: Documentation/ABI/testing/sysfs-platform-ayaneo 4194 4195 F: drivers/platform/x86/ayaneo-ec.c 4195 4196 4196 4197 AZ6007 DVB DRIVER
+107
drivers/platform/x86/ayaneo-ec.c
··· 8 8 */ 9 9 10 10 #include <linux/acpi.h> 11 + #include <linux/bits.h> 11 12 #include <linux/dmi.h> 12 13 #include <linux/err.h> 13 14 #include <linux/hwmon.h> ··· 17 16 #include <linux/module.h> 18 17 #include <linux/platform_device.h> 19 18 #include <linux/power_supply.h> 19 + #include <linux/sysfs.h> 20 20 #include <acpi/battery.h> 21 21 22 22 #define AYANEO_PWM_ENABLE_REG 0x4A ··· 34 32 #define AYANEO_CHARGE_VAL_AUTO 0xaa 35 33 #define AYANEO_CHARGE_VAL_INHIBIT 0x55 36 34 35 + #define AYANEO_POWER_REG 0x2d 36 + #define AYANEO_POWER_OFF 0xfe 37 + #define AYANEO_POWER_ON 0xff 38 + #define AYANEO_MODULE_REG 0x2f 39 + #define AYANEO_MODULE_LEFT BIT(0) 40 + #define AYANEO_MODULE_RIGHT BIT(1) 41 + #define AYANEO_MODULE_MASK (AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT) 42 + 37 43 struct ayaneo_ec_quirk { 38 44 bool has_fan_control; 39 45 bool has_charge_control; 46 + bool has_magic_modules; 40 47 }; 41 48 42 49 struct ayaneo_ec_platform_data { ··· 57 46 static const struct ayaneo_ec_quirk quirk_ayaneo3 = { 58 47 .has_fan_control = true, 59 48 .has_charge_control = true, 49 + .has_magic_modules = true, 60 50 }; 61 51 62 52 static const struct dmi_system_id dmi_table[] = { ··· 278 266 return 0; 279 267 } 280 268 269 + static ssize_t controller_power_store(struct device *dev, 270 + struct device_attribute *attr, 271 + const char *buf, 272 + size_t count) 273 + { 274 + bool value; 275 + int ret; 276 + 277 + ret = kstrtobool(buf, &value); 278 + if (ret) 279 + return ret; 280 + 281 + ret = ec_write(AYANEO_POWER_REG, value ? AYANEO_POWER_ON : AYANEO_POWER_OFF); 282 + if (ret) 283 + return ret; 284 + 285 + return count; 286 + } 287 + 288 + static ssize_t controller_power_show(struct device *dev, 289 + struct device_attribute *attr, 290 + char *buf) 291 + { 292 + int ret; 293 + u8 val; 294 + 295 + ret = ec_read(AYANEO_POWER_REG, &val); 296 + if (ret) 297 + return ret; 298 + 299 + return sysfs_emit(buf, "%d\n", val == AYANEO_POWER_ON); 300 + } 301 + 302 + static DEVICE_ATTR_RW(controller_power); 303 + 304 + static ssize_t controller_modules_show(struct device *dev, 305 + struct device_attribute *attr, char *buf) 306 + { 307 + u8 unconnected_modules; 308 + char *out; 309 + int ret; 310 + 311 + ret = ec_read(AYANEO_MODULE_REG, &unconnected_modules); 312 + if (ret) 313 + return ret; 314 + 315 + switch (~unconnected_modules & AYANEO_MODULE_MASK) { 316 + case AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT: 317 + out = "both"; 318 + break; 319 + case AYANEO_MODULE_LEFT: 320 + out = "left"; 321 + break; 322 + case AYANEO_MODULE_RIGHT: 323 + out = "right"; 324 + break; 325 + default: 326 + out = "none"; 327 + break; 328 + } 329 + 330 + return sysfs_emit(buf, "%s\n", out); 331 + } 332 + 333 + static DEVICE_ATTR_RO(controller_modules); 334 + 335 + static struct attribute *aya_mm_attrs[] = { 336 + &dev_attr_controller_power.attr, 337 + &dev_attr_controller_modules.attr, 338 + NULL 339 + }; 340 + 341 + static umode_t aya_mm_is_visible(struct kobject *kobj, 342 + struct attribute *attr, int n) 343 + { 344 + struct device *dev = kobj_to_dev(kobj); 345 + struct platform_device *pdev = to_platform_device(dev); 346 + struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev); 347 + 348 + if (data->quirks->has_magic_modules) 349 + return attr->mode; 350 + return 0; 351 + } 352 + 353 + static const struct attribute_group aya_mm_attribute_group = { 354 + .is_visible = aya_mm_is_visible, 355 + .attrs = aya_mm_attrs, 356 + }; 357 + 358 + static const struct attribute_group *ayaneo_ec_groups[] = { 359 + &aya_mm_attribute_group, 360 + NULL 361 + }; 362 + 281 363 static int ayaneo_ec_probe(struct platform_device *pdev) 282 364 { 283 365 const struct dmi_system_id *dmi_entry; ··· 413 307 static struct platform_driver ayaneo_platform_driver = { 414 308 .driver = { 415 309 .name = "ayaneo-ec", 310 + .dev_groups = ayaneo_ec_groups, 416 311 }, 417 312 .probe = ayaneo_ec_probe, 418 313 };