1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Platform driver for OneXPlayer, AOK ZOE, and Aya Neo Handhelds that expose 4 * fan reading and control via hwmon sysfs. 5 * 6 * Old OXP boards have the same DMI strings and they are told apart by 7 * the boot cpu vendor (Intel/AMD). Currently only AMD boards are 8 * supported but the code is made to be simple to add other handheld 9 * boards in the future. 10 * Fan control is provided via pwm interface in the range [0-255]. 11 * Old AMD boards use [0-100] as range in the EC, the written value is 12 * scaled to accommodate for that. Newer boards like the mini PRO and 13 * AOK ZOE are not scaled but have the same EC layout. 14 * 15 * Copyright (C) 2022 Joaquín I. Aramendía <samsagax@gmail.com> 16 */ 17 18 #include <linux/acpi.h> 19 #include <linux/dmi.h> 20 #include <linux/hwmon.h> 21 #include <linux/init.h> 22 #include <linux/kernel.h> 23 #include <linux/module.h> 24 #include <linux/platform_device.h> 25 #include <linux/processor.h> 26 27 /* Handle ACPI lock mechanism */ 28 static u32 oxp_mutex; 29 30 #define ACPI_LOCK_DELAY_MS 500 31 32 static bool lock_global_acpi_lock(void) 33 { 34 return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS, &oxp_mutex)); 35 } 36 37 static bool unlock_global_acpi_lock(void) 38 { 39 return ACPI_SUCCESS(acpi_release_global_lock(oxp_mutex)); 40 } 41 42 enum oxp_board { 43 aok_zoe_a1 = 1, 44 aya_neo_2, 45 aya_neo_air, 46 aya_neo_air_pro, 47 aya_neo_geek, 48 oxp_mini_amd, 49 oxp_mini_amd_a07, 50 oxp_mini_amd_pro, 51 }; 52 53 static enum oxp_board board; 54 55 /* Fan reading and PWM */ 56 #define OXP_SENSOR_FAN_REG 0x76 /* Fan reading is 2 registers long */ 57 #define OXP_SENSOR_PWM_ENABLE_REG 0x4A /* PWM enable is 1 register long */ 58 #define OXP_SENSOR_PWM_REG 0x4B /* PWM reading is 1 register long */ 59 60 /* Turbo button takeover function 61 * Older boards have different values and EC registers 62 * for the same function 63 */ 64 #define OXP_OLD_TURBO_SWITCH_REG 0x1E 65 #define OXP_OLD_TURBO_TAKE_VAL 0x01 66 #define OXP_OLD_TURBO_RETURN_VAL 0x00 67 68 #define OXP_TURBO_SWITCH_REG 0xF1 69 #define OXP_TURBO_TAKE_VAL 0x40 70 #define OXP_TURBO_RETURN_VAL 0x00 71 72 static const struct dmi_system_id dmi_table[] = { 73 { 74 .matches = { 75 DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"), 76 DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 AR07"), 77 }, 78 .driver_data = (void *)aok_zoe_a1, 79 }, 80 { 81 .matches = { 82 DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"), 83 DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 Pro"), 84 }, 85 .driver_data = (void *)aok_zoe_a1, 86 }, 87 { 88 .matches = { 89 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 90 DMI_EXACT_MATCH(DMI_BOARD_NAME, "AYANEO 2"), 91 }, 92 .driver_data = (void *)aya_neo_2, 93 }, 94 { 95 .matches = { 96 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 97 DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"), 98 }, 99 .driver_data = (void *)aya_neo_air, 100 }, 101 { 102 .matches = { 103 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 104 DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"), 105 }, 106 .driver_data = (void *)aya_neo_air_pro, 107 }, 108 { 109 .matches = { 110 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 111 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GEEK"), 112 }, 113 .driver_data = (void *)aya_neo_geek, 114 }, 115 { 116 .matches = { 117 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), 118 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONE XPLAYER"), 119 }, 120 .driver_data = (void *)oxp_mini_amd, 121 }, 122 { 123 .matches = { 124 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), 125 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER mini A07"), 126 }, 127 .driver_data = (void *)oxp_mini_amd_a07, 128 }, 129 { 130 .matches = { 131 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), 132 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER Mini Pro"), 133 }, 134 .driver_data = (void *)oxp_mini_amd_pro, 135 }, 136 {}, 137 }; 138 139 /* Helper functions to handle EC read/write */ 140 static int read_from_ec(u8 reg, int size, long *val) 141 { 142 int i; 143 int ret; 144 u8 buffer; 145 146 if (!lock_global_acpi_lock()) 147 return -EBUSY; 148 149 *val = 0; 150 for (i = 0; i < size; i++) { 151 ret = ec_read(reg + i, &buffer); 152 if (ret) 153 return ret; 154 *val <<= i * 8; 155 *val += buffer; 156 } 157 158 if (!unlock_global_acpi_lock()) 159 return -EBUSY; 160 161 return 0; 162 } 163 164 static int write_to_ec(u8 reg, u8 value) 165 { 166 int ret; 167 168 if (!lock_global_acpi_lock()) 169 return -EBUSY; 170 171 ret = ec_write(reg, value); 172 173 if (!unlock_global_acpi_lock()) 174 return -EBUSY; 175 176 return ret; 177 } 178 179 /* Turbo button toggle functions */ 180 static int tt_toggle_enable(void) 181 { 182 u8 reg; 183 u8 val; 184 185 switch (board) { 186 case oxp_mini_amd_a07: 187 reg = OXP_OLD_TURBO_SWITCH_REG; 188 val = OXP_OLD_TURBO_TAKE_VAL; 189 break; 190 case oxp_mini_amd_pro: 191 case aok_zoe_a1: 192 reg = OXP_TURBO_SWITCH_REG; 193 val = OXP_TURBO_TAKE_VAL; 194 break; 195 default: 196 return -EINVAL; 197 } 198 return write_to_ec(reg, val); 199 } 200 201 static int tt_toggle_disable(void) 202 { 203 u8 reg; 204 u8 val; 205 206 switch (board) { 207 case oxp_mini_amd_a07: 208 reg = OXP_OLD_TURBO_SWITCH_REG; 209 val = OXP_OLD_TURBO_RETURN_VAL; 210 break; 211 case oxp_mini_amd_pro: 212 case aok_zoe_a1: 213 reg = OXP_TURBO_SWITCH_REG; 214 val = OXP_TURBO_RETURN_VAL; 215 break; 216 default: 217 return -EINVAL; 218 } 219 return write_to_ec(reg, val); 220 } 221 222 /* Callbacks for turbo toggle attribute */ 223 static ssize_t tt_toggle_store(struct device *dev, 224 struct device_attribute *attr, const char *buf, 225 size_t count) 226 { 227 int rval; 228 bool value; 229 230 rval = kstrtobool(buf, &value); 231 if (rval) 232 return rval; 233 234 if (value) { 235 rval = tt_toggle_enable(); 236 } else { 237 rval = tt_toggle_disable(); 238 } 239 if (rval) 240 return rval; 241 242 return count; 243 } 244 245 static ssize_t tt_toggle_show(struct device *dev, 246 struct device_attribute *attr, char *buf) 247 { 248 int retval; 249 u8 reg; 250 long val; 251 252 switch (board) { 253 case oxp_mini_amd_a07: 254 reg = OXP_OLD_TURBO_SWITCH_REG; 255 break; 256 case oxp_mini_amd_pro: 257 case aok_zoe_a1: 258 reg = OXP_TURBO_SWITCH_REG; 259 break; 260 default: 261 return -EINVAL; 262 } 263 264 retval = read_from_ec(reg, 1, &val); 265 if (retval) 266 return retval; 267 268 return sysfs_emit(buf, "%d\n", !!val); 269 } 270 271 static DEVICE_ATTR_RW(tt_toggle); 272 273 /* PWM enable/disable functions */ 274 static int oxp_pwm_enable(void) 275 { 276 return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, 0x01); 277 } 278 279 static int oxp_pwm_disable(void) 280 { 281 return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, 0x00); 282 } 283 284 /* Callbacks for hwmon interface */ 285 static umode_t oxp_ec_hwmon_is_visible(const void *drvdata, 286 enum hwmon_sensor_types type, u32 attr, int channel) 287 { 288 switch (type) { 289 case hwmon_fan: 290 return 0444; 291 case hwmon_pwm: 292 return 0644; 293 default: 294 return 0; 295 } 296 } 297 298 static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type, 299 u32 attr, int channel, long *val) 300 { 301 int ret; 302 303 switch (type) { 304 case hwmon_fan: 305 switch (attr) { 306 case hwmon_fan_input: 307 return read_from_ec(OXP_SENSOR_FAN_REG, 2, val); 308 default: 309 break; 310 } 311 break; 312 case hwmon_pwm: 313 switch (attr) { 314 case hwmon_pwm_input: 315 ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); 316 if (ret) 317 return ret; 318 switch (board) { 319 case aya_neo_2: 320 case aya_neo_air: 321 case aya_neo_air_pro: 322 case aya_neo_geek: 323 case oxp_mini_amd: 324 case oxp_mini_amd_a07: 325 *val = (*val * 255) / 100; 326 break; 327 case oxp_mini_amd_pro: 328 case aok_zoe_a1: 329 default: 330 break; 331 } 332 return 0; 333 case hwmon_pwm_enable: 334 return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val); 335 default: 336 break; 337 } 338 break; 339 default: 340 break; 341 } 342 return -EOPNOTSUPP; 343 } 344 345 static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type, 346 u32 attr, int channel, long val) 347 { 348 switch (type) { 349 case hwmon_pwm: 350 switch (attr) { 351 case hwmon_pwm_enable: 352 if (val == 1) 353 return oxp_pwm_enable(); 354 else if (val == 0) 355 return oxp_pwm_disable(); 356 return -EINVAL; 357 case hwmon_pwm_input: 358 if (val < 0 || val > 255) 359 return -EINVAL; 360 switch (board) { 361 case aya_neo_2: 362 case aya_neo_air: 363 case aya_neo_air_pro: 364 case aya_neo_geek: 365 case oxp_mini_amd: 366 case oxp_mini_amd_a07: 367 val = (val * 100) / 255; 368 break; 369 case aok_zoe_a1: 370 case oxp_mini_amd_pro: 371 default: 372 break; 373 } 374 return write_to_ec(OXP_SENSOR_PWM_REG, val); 375 default: 376 break; 377 } 378 break; 379 default: 380 break; 381 } 382 return -EOPNOTSUPP; 383 } 384 385 /* Known sensors in the OXP EC controllers */ 386 static const struct hwmon_channel_info * const oxp_platform_sensors[] = { 387 HWMON_CHANNEL_INFO(fan, 388 HWMON_F_INPUT), 389 HWMON_CHANNEL_INFO(pwm, 390 HWMON_PWM_INPUT | HWMON_PWM_ENABLE), 391 NULL, 392 }; 393 394 static struct attribute *oxp_ec_attrs[] = { 395 &dev_attr_tt_toggle.attr, 396 NULL 397 }; 398 399 ATTRIBUTE_GROUPS(oxp_ec); 400 401 static const struct hwmon_ops oxp_ec_hwmon_ops = { 402 .is_visible = oxp_ec_hwmon_is_visible, 403 .read = oxp_platform_read, 404 .write = oxp_platform_write, 405 }; 406 407 static const struct hwmon_chip_info oxp_ec_chip_info = { 408 .ops = &oxp_ec_hwmon_ops, 409 .info = oxp_platform_sensors, 410 }; 411 412 /* Initialization logic */ 413 static int oxp_platform_probe(struct platform_device *pdev) 414 { 415 const struct dmi_system_id *dmi_entry; 416 struct device *dev = &pdev->dev; 417 struct device *hwdev; 418 int ret; 419 420 /* 421 * Have to check for AMD processor here because DMI strings are the 422 * same between Intel and AMD boards, the only way to tell them apart 423 * is the CPU. 424 * Intel boards seem to have different EC registers and values to 425 * read/write. 426 */ 427 dmi_entry = dmi_first_match(dmi_table); 428 if (!dmi_entry || boot_cpu_data.x86_vendor != X86_VENDOR_AMD) 429 return -ENODEV; 430 431 board = (enum oxp_board)(unsigned long)dmi_entry->driver_data; 432 433 switch (board) { 434 case aok_zoe_a1: 435 case oxp_mini_amd_a07: 436 case oxp_mini_amd_pro: 437 ret = devm_device_add_groups(dev, oxp_ec_groups); 438 if (ret) 439 return ret; 440 break; 441 default: 442 break; 443 } 444 445 hwdev = devm_hwmon_device_register_with_info(dev, "oxpec", NULL, 446 &oxp_ec_chip_info, NULL); 447 448 return PTR_ERR_OR_ZERO(hwdev); 449 } 450 451 static struct platform_driver oxp_platform_driver = { 452 .driver = { 453 .name = "oxp-platform", 454 }, 455 .probe = oxp_platform_probe, 456 }; 457 458 static struct platform_device *oxp_platform_device; 459 460 static int __init oxp_platform_init(void) 461 { 462 oxp_platform_device = 463 platform_create_bundle(&oxp_platform_driver, 464 oxp_platform_probe, NULL, 0, NULL, 0); 465 466 return PTR_ERR_OR_ZERO(oxp_platform_device); 467 } 468 469 static void __exit oxp_platform_exit(void) 470 { 471 platform_device_unregister(oxp_platform_device); 472 platform_driver_unregister(&oxp_platform_driver); 473 } 474 475 MODULE_DEVICE_TABLE(dmi, dmi_table); 476 477 module_init(oxp_platform_init); 478 module_exit(oxp_platform_exit); 479 480 MODULE_AUTHOR("Joaquín Ignacio Aramendía <samsagax@gmail.com>"); 481 MODULE_DESCRIPTION("Platform driver that handles EC sensors of OneXPlayer devices"); 482 MODULE_LICENSE("GPL"); 483