1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Platform driver for the Embedded Controller (EC) of Ayaneo devices. Handles 4 * hwmon (fan speed, fan control), battery charge limits, and magic module 5 * control (connected modules, controller disconnection). 6 * 7 * Copyright (C) 2025 Antheas Kapenekakis <lkml@antheas.dev> 8 */ 9 10 #include <linux/acpi.h> 11 #include <linux/bits.h> 12 #include <linux/dmi.h> 13 #include <linux/err.h> 14 #include <linux/hwmon.h> 15 #include <linux/init.h> 16 #include <linux/kernel.h> 17 #include <linux/module.h> 18 #include <linux/platform_device.h> 19 #include <linux/pm.h> 20 #include <linux/power_supply.h> 21 #include <linux/sysfs.h> 22 #include <acpi/battery.h> 23 24 #define AYANEO_PWM_ENABLE_REG 0x4A 25 #define AYANEO_PWM_REG 0x4B 26 #define AYANEO_PWM_MODE_AUTO 0x00 27 #define AYANEO_PWM_MODE_MANUAL 0x01 28 29 #define AYANEO_FAN_REG 0x76 30 31 #define EC_CHARGE_CONTROL_BEHAVIOURS \ 32 (BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) | \ 33 BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE)) 34 #define AYANEO_CHARGE_REG 0x1e 35 #define AYANEO_CHARGE_VAL_AUTO 0xaa 36 #define AYANEO_CHARGE_VAL_INHIBIT 0x55 37 38 #define AYANEO_POWER_REG 0x2d 39 #define AYANEO_POWER_OFF 0xfe 40 #define AYANEO_POWER_ON 0xff 41 #define AYANEO_MODULE_REG 0x2f 42 #define AYANEO_MODULE_LEFT BIT(0) 43 #define AYANEO_MODULE_RIGHT BIT(1) 44 #define AYANEO_MODULE_MASK (AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT) 45 46 struct ayaneo_ec_quirk { 47 bool has_fan_control; 48 bool has_charge_control; 49 bool has_magic_modules; 50 }; 51 52 struct ayaneo_ec_platform_data { 53 struct platform_device *pdev; 54 struct ayaneo_ec_quirk *quirks; 55 struct acpi_battery_hook battery_hook; 56 57 // Protects access to restore_pwm 58 struct mutex hwmon_lock; 59 bool restore_charge_limit; 60 bool restore_pwm; 61 }; 62 63 static const struct ayaneo_ec_quirk quirk_fan = { 64 .has_fan_control = true, 65 }; 66 67 static const struct ayaneo_ec_quirk quirk_charge_limit = { 68 .has_fan_control = true, 69 .has_charge_control = true, 70 }; 71 72 static const struct ayaneo_ec_quirk quirk_ayaneo3 = { 73 .has_fan_control = true, 74 .has_charge_control = true, 75 .has_magic_modules = true, 76 }; 77 78 static const struct dmi_system_id dmi_table[] = { 79 { 80 .matches = { 81 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 82 DMI_MATCH(DMI_BOARD_NAME, "AYANEO 2"), 83 }, 84 .driver_data = (void *)&quirk_fan, 85 }, 86 { 87 .matches = { 88 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 89 DMI_MATCH(DMI_BOARD_NAME, "FLIP"), 90 }, 91 .driver_data = (void *)&quirk_fan, 92 }, 93 { 94 .matches = { 95 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 96 DMI_MATCH(DMI_BOARD_NAME, "GEEK"), 97 }, 98 .driver_data = (void *)&quirk_fan, 99 }, 100 { 101 .matches = { 102 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 103 DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"), 104 }, 105 .driver_data = (void *)&quirk_charge_limit, 106 }, 107 { 108 .matches = { 109 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 110 DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR 1S"), 111 }, 112 .driver_data = (void *)&quirk_charge_limit, 113 }, 114 { 115 .matches = { 116 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 117 DMI_EXACT_MATCH(DMI_BOARD_NAME, "AB05-Mendocino"), 118 }, 119 .driver_data = (void *)&quirk_charge_limit, 120 }, 121 { 122 .matches = { 123 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 124 DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"), 125 }, 126 .driver_data = (void *)&quirk_charge_limit, 127 }, 128 { 129 .matches = { 130 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 131 DMI_EXACT_MATCH(DMI_BOARD_NAME, "KUN"), 132 }, 133 .driver_data = (void *)&quirk_charge_limit, 134 }, 135 { 136 .matches = { 137 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 138 DMI_EXACT_MATCH(DMI_BOARD_NAME, "AYANEO 3"), 139 }, 140 .driver_data = (void *)&quirk_ayaneo3, 141 }, 142 {}, 143 }; 144 145 /* Callbacks for hwmon interface */ 146 static umode_t ayaneo_ec_hwmon_is_visible(const void *drvdata, 147 enum hwmon_sensor_types type, u32 attr, 148 int channel) 149 { 150 switch (type) { 151 case hwmon_fan: 152 return 0444; 153 case hwmon_pwm: 154 return 0644; 155 default: 156 return 0; 157 } 158 } 159 160 static int ayaneo_ec_read(struct device *dev, enum hwmon_sensor_types type, 161 u32 attr, int channel, long *val) 162 { 163 u8 tmp; 164 int ret; 165 166 switch (type) { 167 case hwmon_fan: 168 switch (attr) { 169 case hwmon_fan_input: 170 ret = ec_read(AYANEO_FAN_REG, &tmp); 171 if (ret) 172 return ret; 173 *val = tmp << 8; 174 ret = ec_read(AYANEO_FAN_REG + 1, &tmp); 175 if (ret) 176 return ret; 177 *val |= tmp; 178 return 0; 179 default: 180 break; 181 } 182 break; 183 case hwmon_pwm: 184 switch (attr) { 185 case hwmon_pwm_input: 186 ret = ec_read(AYANEO_PWM_REG, &tmp); 187 if (ret) 188 return ret; 189 if (tmp > 100) 190 return -EIO; 191 *val = (255 * tmp) / 100; 192 return 0; 193 case hwmon_pwm_enable: 194 ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp); 195 if (ret) 196 return ret; 197 if (tmp == AYANEO_PWM_MODE_MANUAL) 198 *val = 1; 199 else if (tmp == AYANEO_PWM_MODE_AUTO) 200 *val = 2; 201 else 202 return -EIO; 203 return 0; 204 default: 205 break; 206 } 207 break; 208 default: 209 break; 210 } 211 return -EOPNOTSUPP; 212 } 213 214 static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type, 215 u32 attr, int channel, long val) 216 { 217 struct ayaneo_ec_platform_data *data = dev_get_drvdata(dev); 218 int ret; 219 220 guard(mutex)(&data->hwmon_lock); 221 222 switch (type) { 223 case hwmon_pwm: 224 switch (attr) { 225 case hwmon_pwm_enable: 226 data->restore_pwm = false; 227 switch (val) { 228 case 1: 229 return ec_write(AYANEO_PWM_ENABLE_REG, 230 AYANEO_PWM_MODE_MANUAL); 231 case 2: 232 return ec_write(AYANEO_PWM_ENABLE_REG, 233 AYANEO_PWM_MODE_AUTO); 234 default: 235 return -EINVAL; 236 } 237 case hwmon_pwm_input: 238 if (val < 0 || val > 255) 239 return -EINVAL; 240 if (data->restore_pwm) { 241 /* 242 * Defer restoring PWM control to after 243 * userspace resumes successfully 244 */ 245 ret = ec_write(AYANEO_PWM_ENABLE_REG, 246 AYANEO_PWM_MODE_MANUAL); 247 if (ret) 248 return ret; 249 data->restore_pwm = false; 250 } 251 return ec_write(AYANEO_PWM_REG, (val * 100) / 255); 252 default: 253 break; 254 } 255 break; 256 default: 257 break; 258 } 259 return -EOPNOTSUPP; 260 } 261 262 static const struct hwmon_ops ayaneo_ec_hwmon_ops = { 263 .is_visible = ayaneo_ec_hwmon_is_visible, 264 .read = ayaneo_ec_read, 265 .write = ayaneo_ec_write, 266 }; 267 268 static const struct hwmon_channel_info *const ayaneo_ec_sensors[] = { 269 HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT), 270 HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE), 271 NULL, 272 }; 273 274 static const struct hwmon_chip_info ayaneo_ec_chip_info = { 275 .ops = &ayaneo_ec_hwmon_ops, 276 .info = ayaneo_ec_sensors, 277 }; 278 279 static int ayaneo_psy_ext_get_prop(struct power_supply *psy, 280 const struct power_supply_ext *ext, 281 void *data, 282 enum power_supply_property psp, 283 union power_supply_propval *val) 284 { 285 int ret; 286 u8 tmp; 287 288 switch (psp) { 289 case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: 290 ret = ec_read(AYANEO_CHARGE_REG, &tmp); 291 if (ret) 292 return ret; 293 294 if (tmp == AYANEO_CHARGE_VAL_INHIBIT) 295 val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE; 296 else 297 val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; 298 return 0; 299 default: 300 return -EINVAL; 301 } 302 } 303 304 static int ayaneo_psy_ext_set_prop(struct power_supply *psy, 305 const struct power_supply_ext *ext, 306 void *data, 307 enum power_supply_property psp, 308 const union power_supply_propval *val) 309 { 310 u8 raw_val; 311 312 switch (psp) { 313 case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: 314 switch (val->intval) { 315 case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO: 316 raw_val = AYANEO_CHARGE_VAL_AUTO; 317 break; 318 case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE: 319 raw_val = AYANEO_CHARGE_VAL_INHIBIT; 320 break; 321 default: 322 return -EINVAL; 323 } 324 return ec_write(AYANEO_CHARGE_REG, raw_val); 325 default: 326 return -EINVAL; 327 } 328 } 329 330 static int ayaneo_psy_prop_is_writeable(struct power_supply *psy, 331 const struct power_supply_ext *ext, 332 void *data, 333 enum power_supply_property psp) 334 { 335 return true; 336 } 337 338 static const enum power_supply_property ayaneo_psy_ext_props[] = { 339 POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, 340 }; 341 342 static const struct power_supply_ext ayaneo_psy_ext = { 343 .name = "ayaneo-charge-control", 344 .properties = ayaneo_psy_ext_props, 345 .num_properties = ARRAY_SIZE(ayaneo_psy_ext_props), 346 .charge_behaviours = EC_CHARGE_CONTROL_BEHAVIOURS, 347 .get_property = ayaneo_psy_ext_get_prop, 348 .set_property = ayaneo_psy_ext_set_prop, 349 .property_is_writeable = ayaneo_psy_prop_is_writeable, 350 }; 351 352 static int ayaneo_add_battery(struct power_supply *battery, 353 struct acpi_battery_hook *hook) 354 { 355 struct ayaneo_ec_platform_data *data = 356 container_of(hook, struct ayaneo_ec_platform_data, battery_hook); 357 358 return power_supply_register_extension(battery, &ayaneo_psy_ext, 359 &data->pdev->dev, NULL); 360 } 361 362 static int ayaneo_remove_battery(struct power_supply *battery, 363 struct acpi_battery_hook *hook) 364 { 365 power_supply_unregister_extension(battery, &ayaneo_psy_ext); 366 return 0; 367 } 368 369 static ssize_t controller_power_store(struct device *dev, 370 struct device_attribute *attr, 371 const char *buf, 372 size_t count) 373 { 374 bool value; 375 int ret; 376 377 ret = kstrtobool(buf, &value); 378 if (ret) 379 return ret; 380 381 ret = ec_write(AYANEO_POWER_REG, value ? AYANEO_POWER_ON : AYANEO_POWER_OFF); 382 if (ret) 383 return ret; 384 385 return count; 386 } 387 388 static ssize_t controller_power_show(struct device *dev, 389 struct device_attribute *attr, 390 char *buf) 391 { 392 int ret; 393 u8 val; 394 395 ret = ec_read(AYANEO_POWER_REG, &val); 396 if (ret) 397 return ret; 398 399 return sysfs_emit(buf, "%d\n", val == AYANEO_POWER_ON); 400 } 401 402 static DEVICE_ATTR_RW(controller_power); 403 404 static ssize_t controller_modules_show(struct device *dev, 405 struct device_attribute *attr, char *buf) 406 { 407 u8 unconnected_modules; 408 char *out; 409 int ret; 410 411 ret = ec_read(AYANEO_MODULE_REG, &unconnected_modules); 412 if (ret) 413 return ret; 414 415 switch (~unconnected_modules & AYANEO_MODULE_MASK) { 416 case AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT: 417 out = "both"; 418 break; 419 case AYANEO_MODULE_LEFT: 420 out = "left"; 421 break; 422 case AYANEO_MODULE_RIGHT: 423 out = "right"; 424 break; 425 default: 426 out = "none"; 427 break; 428 } 429 430 return sysfs_emit(buf, "%s\n", out); 431 } 432 433 static DEVICE_ATTR_RO(controller_modules); 434 435 static struct attribute *aya_mm_attrs[] = { 436 &dev_attr_controller_power.attr, 437 &dev_attr_controller_modules.attr, 438 NULL 439 }; 440 441 static umode_t aya_mm_is_visible(struct kobject *kobj, 442 struct attribute *attr, int n) 443 { 444 struct device *dev = kobj_to_dev(kobj); 445 struct platform_device *pdev = to_platform_device(dev); 446 struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev); 447 448 if (data->quirks->has_magic_modules) 449 return attr->mode; 450 return 0; 451 } 452 453 static const struct attribute_group aya_mm_attribute_group = { 454 .is_visible = aya_mm_is_visible, 455 .attrs = aya_mm_attrs, 456 }; 457 458 static const struct attribute_group *ayaneo_ec_groups[] = { 459 &aya_mm_attribute_group, 460 NULL 461 }; 462 463 static int ayaneo_ec_probe(struct platform_device *pdev) 464 { 465 const struct dmi_system_id *dmi_entry; 466 struct ayaneo_ec_platform_data *data; 467 struct device *hwdev; 468 int ret; 469 470 dmi_entry = dmi_first_match(dmi_table); 471 if (!dmi_entry) 472 return -ENODEV; 473 474 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 475 if (!data) 476 return -ENOMEM; 477 478 data->pdev = pdev; 479 data->quirks = dmi_entry->driver_data; 480 ret = devm_mutex_init(&pdev->dev, &data->hwmon_lock); 481 if (ret) 482 return ret; 483 platform_set_drvdata(pdev, data); 484 485 if (data->quirks->has_fan_control) { 486 hwdev = devm_hwmon_device_register_with_info(&pdev->dev, 487 "ayaneo_ec", data, &ayaneo_ec_chip_info, NULL); 488 if (IS_ERR(hwdev)) 489 return PTR_ERR(hwdev); 490 } 491 492 if (data->quirks->has_charge_control) { 493 data->battery_hook.add_battery = ayaneo_add_battery; 494 data->battery_hook.remove_battery = ayaneo_remove_battery; 495 data->battery_hook.name = "Ayaneo Battery"; 496 ret = devm_battery_hook_register(&pdev->dev, &data->battery_hook); 497 if (ret) 498 return ret; 499 } 500 501 return 0; 502 } 503 504 static int ayaneo_freeze(struct device *dev) 505 { 506 struct platform_device *pdev = to_platform_device(dev); 507 struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev); 508 int ret; 509 u8 tmp; 510 511 if (data->quirks->has_charge_control) { 512 ret = ec_read(AYANEO_CHARGE_REG, &tmp); 513 if (ret) 514 return ret; 515 516 data->restore_charge_limit = tmp == AYANEO_CHARGE_VAL_INHIBIT; 517 } 518 519 if (data->quirks->has_fan_control) { 520 ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp); 521 if (ret) 522 return ret; 523 524 data->restore_pwm = tmp == AYANEO_PWM_MODE_MANUAL; 525 526 /* 527 * Release the fan when entering hibernation to avoid 528 * overheating if hibernation fails and hangs. 529 */ 530 if (data->restore_pwm) { 531 ret = ec_write(AYANEO_PWM_ENABLE_REG, AYANEO_PWM_MODE_AUTO); 532 if (ret) 533 return ret; 534 } 535 } 536 537 return 0; 538 } 539 540 static int ayaneo_restore(struct device *dev) 541 { 542 struct platform_device *pdev = to_platform_device(dev); 543 struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev); 544 int ret; 545 546 if (data->quirks->has_charge_control && data->restore_charge_limit) { 547 ret = ec_write(AYANEO_CHARGE_REG, AYANEO_CHARGE_VAL_INHIBIT); 548 if (ret) 549 return ret; 550 } 551 552 return 0; 553 } 554 555 static const struct dev_pm_ops ayaneo_pm_ops = { 556 .freeze = ayaneo_freeze, 557 .restore = ayaneo_restore, 558 }; 559 560 static struct platform_driver ayaneo_platform_driver = { 561 .driver = { 562 .name = "ayaneo-ec", 563 .dev_groups = ayaneo_ec_groups, 564 .pm = pm_sleep_ptr(&ayaneo_pm_ops), 565 }, 566 .probe = ayaneo_ec_probe, 567 }; 568 569 static struct platform_device *ayaneo_platform_device; 570 571 static int __init ayaneo_ec_init(void) 572 { 573 ayaneo_platform_device = 574 platform_create_bundle(&ayaneo_platform_driver, 575 ayaneo_ec_probe, NULL, 0, NULL, 0); 576 577 return PTR_ERR_OR_ZERO(ayaneo_platform_device); 578 } 579 580 static void __exit ayaneo_ec_exit(void) 581 { 582 platform_device_unregister(ayaneo_platform_device); 583 platform_driver_unregister(&ayaneo_platform_driver); 584 } 585 586 MODULE_DEVICE_TABLE(dmi, dmi_table); 587 588 module_init(ayaneo_ec_init); 589 module_exit(ayaneo_ec_exit); 590 591 MODULE_AUTHOR("Antheas Kapenekakis <lkml@antheas.dev>"); 592 MODULE_DESCRIPTION("Ayaneo Embedded Controller (EC) platform features"); 593 MODULE_LICENSE("GPL"); 594