1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Platform driver for OneXPlayer and AOKZOE devices. For the time being,
4 * it also exposes fan controls for AYANEO, and OrangePi Handhelds via
5 * hwmon sysfs.
6 *
7 * Fan control is provided via pwm interface in the range [0-255].
8 * Old AMD boards use [0-100] as range in the EC, the written value is
9 * scaled to accommodate for that. Newer boards like the mini PRO and
10 * AOKZOE are not scaled but have the same EC layout. Newer models
11 * like the 2 and X1 are [0-184] and are scaled to 0-255. OrangePi
12 * are [1-244] and scaled to 0-255.
13 *
14 * Copyright (C) 2022 Joaquín I. Aramendía <samsagax@gmail.com>
15 * Copyright (C) 2024 Derek J. Clark <derekjohn.clark@gmail.com>
16 * Copyright (C) 2025 Antheas Kapenekakis <lkml@antheas.dev>
17 */
18
19 #include <linux/acpi.h>
20 #include <linux/dmi.h>
21 #include <linux/hwmon.h>
22 #include <linux/init.h>
23 #include <linux/kernel.h>
24 #include <linux/module.h>
25 #include <linux/platform_device.h>
26 #include <linux/processor.h>
27 #include <acpi/battery.h>
28
29 /* Handle ACPI lock mechanism */
30 static u32 oxp_mutex;
31
32 #define ACPI_LOCK_DELAY_MS 500
33
lock_global_acpi_lock(void)34 static bool lock_global_acpi_lock(void)
35 {
36 return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS, &oxp_mutex));
37 }
38
unlock_global_acpi_lock(void)39 static bool unlock_global_acpi_lock(void)
40 {
41 return ACPI_SUCCESS(acpi_release_global_lock(oxp_mutex));
42 }
43
44 enum oxp_board {
45 aok_zoe_a1 = 1,
46 aya_neo_2,
47 aya_neo_air,
48 aya_neo_air_1s,
49 aya_neo_air_plus_mendo,
50 aya_neo_air_pro,
51 aya_neo_flip,
52 aya_neo_geek,
53 aya_neo_kun,
54 orange_pi_neo,
55 oxp_2,
56 oxp_fly,
57 oxp_mini_amd,
58 oxp_mini_amd_a07,
59 oxp_mini_amd_pro,
60 oxp_x1,
61 oxp_g1,
62 };
63
64 static enum oxp_board board;
65 static struct device *oxp_dev;
66
67 /* Fan reading and PWM */
68 #define OXP_SENSOR_FAN_REG 0x76 /* Fan reading is 2 registers long */
69 #define OXP_2_SENSOR_FAN_REG 0x58 /* Fan reading is 2 registers long */
70 #define OXP_SENSOR_PWM_ENABLE_REG 0x4A /* PWM enable is 1 register long */
71 #define OXP_SENSOR_PWM_REG 0x4B /* PWM reading is 1 register long */
72 #define PWM_MODE_AUTO 0x00
73 #define PWM_MODE_MANUAL 0x01
74
75 /* OrangePi fan reading and PWM */
76 #define ORANGEPI_SENSOR_FAN_REG 0x78 /* Fan reading is 2 registers long */
77 #define ORANGEPI_SENSOR_PWM_ENABLE_REG 0x40 /* PWM enable is 1 register long */
78 #define ORANGEPI_SENSOR_PWM_REG 0x38 /* PWM reading is 1 register long */
79
80 /* Turbo button takeover function
81 * Different boards have different values and EC registers
82 * for the same function
83 */
84 #define OXP_TURBO_SWITCH_REG 0xF1 /* Mini Pro, OneXFly, AOKZOE */
85 #define OXP_2_TURBO_SWITCH_REG 0xEB /* OXP2 and X1 */
86 #define OXP_MINI_TURBO_SWITCH_REG 0x1E /* Mini AO7 */
87
88 #define OXP_MINI_TURBO_TAKE_VAL 0x01 /* Mini AO7 */
89 #define OXP_TURBO_TAKE_VAL 0x40 /* All other models */
90
91 /* X1 Turbo LED */
92 #define OXP_X1_TURBO_LED_REG 0x57
93
94 #define OXP_X1_TURBO_LED_OFF 0x01
95 #define OXP_X1_TURBO_LED_ON 0x02
96
97 /* Battery extension settings */
98 #define EC_CHARGE_CONTROL_BEHAVIOURS (BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) | \
99 BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE) | \
100 BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE))
101
102 #define OXP_X1_CHARGE_LIMIT_REG 0xA3 /* X1 charge limit (%) */
103 #define OXP_X1_CHARGE_INHIBIT_REG 0xA4 /* X1 bypass charging */
104
105 #define OXP_X1_CHARGE_INHIBIT_MASK_AWAKE 0x01
106 /* X1 Mask is 0x0A, F1Pro is 0x02 but the extra bit on the X1 does nothing. */
107 #define OXP_X1_CHARGE_INHIBIT_MASK_OFF 0x02
108 #define OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS (OXP_X1_CHARGE_INHIBIT_MASK_AWAKE | \
109 OXP_X1_CHARGE_INHIBIT_MASK_OFF)
110
111 static const struct dmi_system_id dmi_table[] = {
112 {
113 .matches = {
114 DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
115 DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 AR07"),
116 },
117 .driver_data = (void *)aok_zoe_a1,
118 },
119 {
120 .matches = {
121 DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
122 DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 Pro"),
123 },
124 .driver_data = (void *)aok_zoe_a1,
125 },
126 {
127 .matches = {
128 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
129 DMI_MATCH(DMI_BOARD_NAME, "AYANEO 2"),
130 },
131 .driver_data = (void *)aya_neo_2,
132 },
133 {
134 .matches = {
135 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
136 DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"),
137 },
138 .driver_data = (void *)aya_neo_air,
139 },
140 {
141 .matches = {
142 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
143 DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR 1S"),
144 },
145 .driver_data = (void *)aya_neo_air_1s,
146 },
147 {
148 .matches = {
149 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
150 DMI_EXACT_MATCH(DMI_BOARD_NAME, "AB05-Mendocino"),
151 },
152 .driver_data = (void *)aya_neo_air_plus_mendo,
153 },
154 {
155 .matches = {
156 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
157 DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"),
158 },
159 .driver_data = (void *)aya_neo_air_pro,
160 },
161 {
162 .matches = {
163 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
164 DMI_MATCH(DMI_BOARD_NAME, "FLIP"),
165 },
166 .driver_data = (void *)aya_neo_flip,
167 },
168 {
169 .matches = {
170 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
171 DMI_MATCH(DMI_BOARD_NAME, "GEEK"),
172 },
173 .driver_data = (void *)aya_neo_geek,
174 },
175 {
176 .matches = {
177 DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
178 DMI_EXACT_MATCH(DMI_BOARD_NAME, "KUN"),
179 },
180 .driver_data = (void *)aya_neo_kun,
181 },
182 {
183 .matches = {
184 DMI_MATCH(DMI_BOARD_VENDOR, "OrangePi"),
185 DMI_EXACT_MATCH(DMI_BOARD_NAME, "NEO-01"),
186 },
187 .driver_data = (void *)orange_pi_neo,
188 },
189 {
190 .matches = {
191 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
192 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONE XPLAYER"),
193 },
194 .driver_data = (void *)oxp_mini_amd,
195 },
196 {
197 .matches = {
198 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
199 DMI_MATCH(DMI_BOARD_NAME, "ONEXPLAYER 2"),
200 },
201 .driver_data = (void *)oxp_2,
202 },
203 {
204 .matches = {
205 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
206 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1"),
207 },
208 .driver_data = (void *)oxp_fly,
209 },
210 {
211 .matches = {
212 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
213 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1 EVA-01"),
214 },
215 .driver_data = (void *)oxp_fly,
216 },
217 {
218 .matches = {
219 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
220 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1 OLED"),
221 },
222 .driver_data = (void *)oxp_fly,
223 },
224 {
225 .matches = {
226 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
227 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1L"),
228 },
229 .driver_data = (void *)oxp_fly,
230 },
231 {
232 .matches = {
233 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
234 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1Pro"),
235 },
236 .driver_data = (void *)oxp_fly,
237 },
238 {
239 .matches = {
240 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
241 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1 EVA-02"),
242 },
243 .driver_data = (void *)oxp_fly,
244 },
245 {
246 .matches = {
247 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
248 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER G1 A"),
249 },
250 .driver_data = (void *)oxp_g1,
251 },
252 {
253 .matches = {
254 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
255 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER G1 i"),
256 },
257 .driver_data = (void *)oxp_g1,
258 },
259 {
260 .matches = {
261 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
262 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER mini A07"),
263 },
264 .driver_data = (void *)oxp_mini_amd_a07,
265 },
266 {
267 .matches = {
268 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
269 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER Mini Pro"),
270 },
271 .driver_data = (void *)oxp_mini_amd_pro,
272 },
273 {
274 .matches = {
275 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
276 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1 A"),
277 },
278 .driver_data = (void *)oxp_x1,
279 },
280 {
281 .matches = {
282 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
283 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1 i"),
284 },
285 .driver_data = (void *)oxp_x1,
286 },
287 {
288 .matches = {
289 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
290 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1 mini"),
291 },
292 .driver_data = (void *)oxp_x1,
293 },
294 {
295 .matches = {
296 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
297 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1Pro"),
298 },
299 .driver_data = (void *)oxp_x1,
300 },
301 {},
302 };
303
304 /* Helper functions to handle EC read/write */
read_from_ec(u8 reg,int size,long * val)305 static int read_from_ec(u8 reg, int size, long *val)
306 {
307 u8 buffer;
308 int ret;
309 int i;
310
311 if (!lock_global_acpi_lock())
312 return -EBUSY;
313
314 *val = 0;
315 for (i = 0; i < size; i++) {
316 ret = ec_read(reg + i, &buffer);
317 if (ret)
318 return ret;
319 *val <<= i * 8;
320 *val += buffer;
321 }
322
323 if (!unlock_global_acpi_lock())
324 return -EBUSY;
325
326 return 0;
327 }
328
write_to_ec(u8 reg,u8 value)329 static int write_to_ec(u8 reg, u8 value)
330 {
331 int ret;
332
333 if (!lock_global_acpi_lock())
334 return -EBUSY;
335
336 ret = ec_write(reg, value);
337
338 if (!unlock_global_acpi_lock())
339 return -EBUSY;
340
341 return ret;
342 }
343
344 /* Callbacks for turbo toggle attribute */
tt_toggle_is_visible(struct kobject * kobj,struct attribute * attr,int n)345 static umode_t tt_toggle_is_visible(struct kobject *kobj,
346 struct attribute *attr, int n)
347 {
348 switch (board) {
349 case aok_zoe_a1:
350 case oxp_2:
351 case oxp_fly:
352 case oxp_mini_amd_a07:
353 case oxp_mini_amd_pro:
354 case oxp_x1:
355 case oxp_g1:
356 return attr->mode;
357 default:
358 break;
359 }
360 return 0;
361 }
362
tt_toggle_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)363 static ssize_t tt_toggle_store(struct device *dev,
364 struct device_attribute *attr, const char *buf,
365 size_t count)
366 {
367 u8 reg, mask, val;
368 long raw_val;
369 bool enable;
370 int ret;
371
372 ret = kstrtobool(buf, &enable);
373 if (ret)
374 return ret;
375
376 switch (board) {
377 case oxp_mini_amd_a07:
378 reg = OXP_MINI_TURBO_SWITCH_REG;
379 mask = OXP_MINI_TURBO_TAKE_VAL;
380 break;
381 case aok_zoe_a1:
382 case oxp_fly:
383 case oxp_mini_amd_pro:
384 reg = OXP_TURBO_SWITCH_REG;
385 mask = OXP_TURBO_TAKE_VAL;
386 break;
387 case oxp_2:
388 case oxp_x1:
389 case oxp_g1:
390 reg = OXP_2_TURBO_SWITCH_REG;
391 mask = OXP_TURBO_TAKE_VAL;
392 break;
393 default:
394 return -EINVAL;
395 }
396
397 ret = read_from_ec(reg, 1, &raw_val);
398 if (ret)
399 return ret;
400
401 val = raw_val;
402 if (enable)
403 val |= mask;
404 else
405 val &= ~mask;
406
407 ret = write_to_ec(reg, val);
408 if (ret)
409 return ret;
410
411 return count;
412 }
413
tt_toggle_show(struct device * dev,struct device_attribute * attr,char * buf)414 static ssize_t tt_toggle_show(struct device *dev,
415 struct device_attribute *attr, char *buf)
416 {
417 u8 reg, mask;
418 int retval;
419 long val;
420
421 switch (board) {
422 case oxp_mini_amd_a07:
423 reg = OXP_MINI_TURBO_SWITCH_REG;
424 mask = OXP_MINI_TURBO_TAKE_VAL;
425 break;
426 case aok_zoe_a1:
427 case oxp_fly:
428 case oxp_mini_amd_pro:
429 reg = OXP_TURBO_SWITCH_REG;
430 mask = OXP_TURBO_TAKE_VAL;
431 break;
432 case oxp_2:
433 case oxp_x1:
434 case oxp_g1:
435 reg = OXP_2_TURBO_SWITCH_REG;
436 mask = OXP_TURBO_TAKE_VAL;
437 break;
438 default:
439 return -EINVAL;
440 }
441
442 retval = read_from_ec(reg, 1, &val);
443 if (retval)
444 return retval;
445
446 return sysfs_emit(buf, "%d\n", (val & mask) == mask);
447 }
448
449 static DEVICE_ATTR_RW(tt_toggle);
450
451 /* Callbacks for turbo LED attribute */
tt_led_is_visible(struct kobject * kobj,struct attribute * attr,int n)452 static umode_t tt_led_is_visible(struct kobject *kobj,
453 struct attribute *attr, int n)
454 {
455 switch (board) {
456 case oxp_x1:
457 return attr->mode;
458 default:
459 break;
460 }
461 return 0;
462 }
463
tt_led_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)464 static ssize_t tt_led_store(struct device *dev,
465 struct device_attribute *attr, const char *buf,
466 size_t count)
467 {
468 u8 reg, val;
469 bool value;
470 int ret;
471
472 ret = kstrtobool(buf, &value);
473 if (ret)
474 return ret;
475
476 switch (board) {
477 case oxp_x1:
478 reg = OXP_X1_TURBO_LED_REG;
479 val = value ? OXP_X1_TURBO_LED_ON : OXP_X1_TURBO_LED_OFF;
480 break;
481 default:
482 return -EINVAL;
483 }
484
485 ret = write_to_ec(reg, val);
486 if (ret)
487 return ret;
488
489 return count;
490 }
491
tt_led_show(struct device * dev,struct device_attribute * attr,char * buf)492 static ssize_t tt_led_show(struct device *dev,
493 struct device_attribute *attr, char *buf)
494 {
495 long enval;
496 long val;
497 int ret;
498 u8 reg;
499
500 switch (board) {
501 case oxp_x1:
502 reg = OXP_X1_TURBO_LED_REG;
503 enval = OXP_X1_TURBO_LED_ON;
504 break;
505 default:
506 return -EINVAL;
507 }
508
509 ret = read_from_ec(reg, 1, &val);
510 if (ret)
511 return ret;
512
513 return sysfs_emit(buf, "%d\n", val == enval);
514 }
515
516 static DEVICE_ATTR_RW(tt_led);
517
518 /* Callbacks for charge behaviour attributes */
oxp_psy_ext_supported(void)519 static bool oxp_psy_ext_supported(void)
520 {
521 switch (board) {
522 case oxp_x1:
523 case oxp_g1:
524 case oxp_fly:
525 return true;
526 default:
527 break;
528 }
529 return false;
530 }
531
oxp_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)532 static int oxp_psy_ext_get_prop(struct power_supply *psy,
533 const struct power_supply_ext *ext,
534 void *data,
535 enum power_supply_property psp,
536 union power_supply_propval *val)
537 {
538 long raw_val;
539 int ret;
540
541 switch (psp) {
542 case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
543 ret = read_from_ec(OXP_X1_CHARGE_LIMIT_REG, 1, &raw_val);
544 if (ret)
545 return ret;
546 if (raw_val < 0 || raw_val > 100)
547 return -EINVAL;
548 val->intval = raw_val;
549 return 0;
550 case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
551 ret = read_from_ec(OXP_X1_CHARGE_INHIBIT_REG, 1, &raw_val);
552 if (ret)
553 return ret;
554 if ((raw_val & OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS) ==
555 OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS)
556 val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
557 else if ((raw_val & OXP_X1_CHARGE_INHIBIT_MASK_AWAKE) ==
558 OXP_X1_CHARGE_INHIBIT_MASK_AWAKE)
559 val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE;
560 else
561 val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
562 return 0;
563 default:
564 return -EINVAL;
565 }
566 }
567
oxp_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)568 static int oxp_psy_ext_set_prop(struct power_supply *psy,
569 const struct power_supply_ext *ext,
570 void *data,
571 enum power_supply_property psp,
572 const union power_supply_propval *val)
573 {
574 long raw_val;
575
576 switch (psp) {
577 case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
578 if (val->intval < 0 || val->intval > 100)
579 return -EINVAL;
580 return write_to_ec(OXP_X1_CHARGE_LIMIT_REG, val->intval);
581 case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
582 switch (val->intval) {
583 case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
584 raw_val = 0;
585 break;
586 case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE:
587 raw_val = OXP_X1_CHARGE_INHIBIT_MASK_AWAKE;
588 break;
589 case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
590 raw_val = OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS;
591 break;
592 default:
593 return -EINVAL;
594 }
595
596 return write_to_ec(OXP_X1_CHARGE_INHIBIT_REG, raw_val);
597 default:
598 return -EINVAL;
599 }
600 }
601
oxp_psy_prop_is_writeable(struct power_supply * psy,const struct power_supply_ext * ext,void * data,enum power_supply_property psp)602 static int oxp_psy_prop_is_writeable(struct power_supply *psy,
603 const struct power_supply_ext *ext,
604 void *data,
605 enum power_supply_property psp)
606 {
607 return true;
608 }
609
610 static const enum power_supply_property oxp_psy_ext_props[] = {
611 POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
612 POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
613 };
614
615 static const struct power_supply_ext oxp_psy_ext = {
616 .name = "oxp-charge-control",
617 .properties = oxp_psy_ext_props,
618 .num_properties = ARRAY_SIZE(oxp_psy_ext_props),
619 .charge_behaviours = EC_CHARGE_CONTROL_BEHAVIOURS,
620 .get_property = oxp_psy_ext_get_prop,
621 .set_property = oxp_psy_ext_set_prop,
622 .property_is_writeable = oxp_psy_prop_is_writeable,
623 };
624
oxp_add_battery(struct power_supply * battery,struct acpi_battery_hook * hook)625 static int oxp_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
626 {
627 return power_supply_register_extension(battery, &oxp_psy_ext, oxp_dev, NULL);
628 }
629
oxp_remove_battery(struct power_supply * battery,struct acpi_battery_hook * hook)630 static int oxp_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
631 {
632 power_supply_unregister_extension(battery, &oxp_psy_ext);
633 return 0;
634 }
635
636 static struct acpi_battery_hook battery_hook = {
637 .add_battery = oxp_add_battery,
638 .remove_battery = oxp_remove_battery,
639 .name = "OneXPlayer Battery",
640 };
641
642 /* PWM enable/disable functions */
oxp_pwm_enable(void)643 static int oxp_pwm_enable(void)
644 {
645 switch (board) {
646 case orange_pi_neo:
647 return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
648 case aok_zoe_a1:
649 case aya_neo_2:
650 case aya_neo_air:
651 case aya_neo_air_plus_mendo:
652 case aya_neo_air_pro:
653 case aya_neo_flip:
654 case aya_neo_geek:
655 case aya_neo_kun:
656 case oxp_2:
657 case oxp_fly:
658 case oxp_mini_amd:
659 case oxp_mini_amd_a07:
660 case oxp_mini_amd_pro:
661 case oxp_x1:
662 case oxp_g1:
663 return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
664 default:
665 return -EINVAL;
666 }
667 }
668
oxp_pwm_disable(void)669 static int oxp_pwm_disable(void)
670 {
671 switch (board) {
672 case orange_pi_neo:
673 return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
674 case aok_zoe_a1:
675 case aya_neo_2:
676 case aya_neo_air:
677 case aya_neo_air_1s:
678 case aya_neo_air_plus_mendo:
679 case aya_neo_air_pro:
680 case aya_neo_flip:
681 case aya_neo_geek:
682 case aya_neo_kun:
683 case oxp_2:
684 case oxp_fly:
685 case oxp_mini_amd:
686 case oxp_mini_amd_a07:
687 case oxp_mini_amd_pro:
688 case oxp_x1:
689 case oxp_g1:
690 return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
691 default:
692 return -EINVAL;
693 }
694 }
695
oxp_pwm_read(long * val)696 static int oxp_pwm_read(long *val)
697 {
698 switch (board) {
699 case orange_pi_neo:
700 return read_from_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, 1, val);
701 case aok_zoe_a1:
702 case aya_neo_2:
703 case aya_neo_air:
704 case aya_neo_air_1s:
705 case aya_neo_air_plus_mendo:
706 case aya_neo_air_pro:
707 case aya_neo_flip:
708 case aya_neo_geek:
709 case aya_neo_kun:
710 case oxp_2:
711 case oxp_fly:
712 case oxp_mini_amd:
713 case oxp_mini_amd_a07:
714 case oxp_mini_amd_pro:
715 case oxp_x1:
716 case oxp_g1:
717 return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
718 default:
719 return -EOPNOTSUPP;
720 }
721 }
722
723 /* Callbacks for hwmon interface */
oxp_ec_hwmon_is_visible(const void * drvdata,enum hwmon_sensor_types type,u32 attr,int channel)724 static umode_t oxp_ec_hwmon_is_visible(const void *drvdata,
725 enum hwmon_sensor_types type, u32 attr, int channel)
726 {
727 switch (type) {
728 case hwmon_fan:
729 return 0444;
730 case hwmon_pwm:
731 return 0644;
732 default:
733 return 0;
734 }
735 }
736
737 /* Fan speed read function */
oxp_pwm_fan_speed(long * val)738 static int oxp_pwm_fan_speed(long *val)
739 {
740 switch (board) {
741 case orange_pi_neo:
742 return read_from_ec(ORANGEPI_SENSOR_FAN_REG, 2, val);
743 case oxp_2:
744 case oxp_x1:
745 case oxp_g1:
746 return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val);
747 case aok_zoe_a1:
748 case aya_neo_2:
749 case aya_neo_air:
750 case aya_neo_air_1s:
751 case aya_neo_air_plus_mendo:
752 case aya_neo_air_pro:
753 case aya_neo_flip:
754 case aya_neo_geek:
755 case aya_neo_kun:
756 case oxp_fly:
757 case oxp_mini_amd:
758 case oxp_mini_amd_a07:
759 case oxp_mini_amd_pro:
760 return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);
761 default:
762 return -EOPNOTSUPP;
763 }
764 }
765
766 /* PWM input read/write functions */
oxp_pwm_input_write(long val)767 static int oxp_pwm_input_write(long val)
768 {
769 if (val < 0 || val > 255)
770 return -EINVAL;
771
772 switch (board) {
773 case orange_pi_neo:
774 /* scale to range [1-244] */
775 val = ((val - 1) * 243 / 254) + 1;
776 return write_to_ec(ORANGEPI_SENSOR_PWM_REG, val);
777 case oxp_2:
778 case oxp_x1:
779 case oxp_g1:
780 /* scale to range [0-184] */
781 val = (val * 184) / 255;
782 return write_to_ec(OXP_SENSOR_PWM_REG, val);
783 case aya_neo_2:
784 case aya_neo_air:
785 case aya_neo_air_1s:
786 case aya_neo_air_plus_mendo:
787 case aya_neo_air_pro:
788 case aya_neo_flip:
789 case aya_neo_geek:
790 case aya_neo_kun:
791 case oxp_mini_amd:
792 case oxp_mini_amd_a07:
793 /* scale to range [0-100] */
794 val = (val * 100) / 255;
795 return write_to_ec(OXP_SENSOR_PWM_REG, val);
796 case aok_zoe_a1:
797 case oxp_fly:
798 case oxp_mini_amd_pro:
799 return write_to_ec(OXP_SENSOR_PWM_REG, val);
800 default:
801 return -EOPNOTSUPP;
802 }
803 }
804
oxp_pwm_input_read(long * val)805 static int oxp_pwm_input_read(long *val)
806 {
807 int ret;
808
809 switch (board) {
810 case orange_pi_neo:
811 ret = read_from_ec(ORANGEPI_SENSOR_PWM_REG, 1, val);
812 if (ret)
813 return ret;
814 /* scale from range [1-244] */
815 *val = ((*val - 1) * 254 / 243) + 1;
816 break;
817 case oxp_2:
818 case oxp_x1:
819 case oxp_g1:
820 ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
821 if (ret)
822 return ret;
823 /* scale from range [0-184] */
824 *val = (*val * 255) / 184;
825 break;
826 case aya_neo_2:
827 case aya_neo_air:
828 case aya_neo_air_1s:
829 case aya_neo_air_plus_mendo:
830 case aya_neo_air_pro:
831 case aya_neo_flip:
832 case aya_neo_geek:
833 case aya_neo_kun:
834 case oxp_mini_amd:
835 case oxp_mini_amd_a07:
836 ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
837 if (ret)
838 return ret;
839 /* scale from range [0-100] */
840 *val = (*val * 255) / 100;
841 break;
842 case aok_zoe_a1:
843 case oxp_fly:
844 case oxp_mini_amd_pro:
845 default:
846 ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
847 if (ret)
848 return ret;
849 break;
850 }
851 return 0;
852 }
853
oxp_platform_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)854 static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
855 u32 attr, int channel, long *val)
856 {
857 int ret;
858
859 switch (type) {
860 case hwmon_fan:
861 switch (attr) {
862 case hwmon_fan_input:
863 return oxp_pwm_fan_speed(val);
864 default:
865 break;
866 }
867 break;
868 case hwmon_pwm:
869 switch (attr) {
870 case hwmon_pwm_input:
871 return oxp_pwm_input_read(val);
872 case hwmon_pwm_enable:
873 ret = oxp_pwm_read(val);
874 if (ret)
875 return ret;
876
877 /* Check for auto and return 2 */
878 if (!*val) {
879 *val = 2;
880 return 0;
881 }
882
883 /* Return 0 if at full fan speed, 1 otherwise */
884 ret = oxp_pwm_fan_speed(val);
885 if (ret)
886 return ret;
887
888 if (*val == 255)
889 *val = 0;
890 else
891 *val = 1;
892
893 return 0;
894 default:
895 break;
896 }
897 break;
898 default:
899 break;
900 }
901 return -EOPNOTSUPP;
902 }
903
oxp_platform_write(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long val)904 static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type,
905 u32 attr, int channel, long val)
906 {
907 int ret;
908
909 switch (type) {
910 case hwmon_pwm:
911 switch (attr) {
912 case hwmon_pwm_enable:
913 if (val == 1)
914 return oxp_pwm_enable();
915 else if (val == 2)
916 return oxp_pwm_disable();
917 else if (val != 0)
918 return -EINVAL;
919
920 /* Enable PWM and set to max speed */
921 ret = oxp_pwm_enable();
922 if (ret)
923 return ret;
924 return oxp_pwm_input_write(255);
925 case hwmon_pwm_input:
926 return oxp_pwm_input_write(val);
927 default:
928 break;
929 }
930 break;
931 default:
932 break;
933 }
934 return -EOPNOTSUPP;
935 }
936
937 /* Known sensors in the OXP EC controllers */
938 static const struct hwmon_channel_info * const oxp_platform_sensors[] = {
939 HWMON_CHANNEL_INFO(fan,
940 HWMON_F_INPUT),
941 HWMON_CHANNEL_INFO(pwm,
942 HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
943 NULL,
944 };
945
946 static struct attribute *oxp_tt_toggle_attrs[] = {
947 &dev_attr_tt_toggle.attr,
948 NULL
949 };
950
951 static const struct attribute_group oxp_tt_toggle_attribute_group = {
952 .is_visible = tt_toggle_is_visible,
953 .attrs = oxp_tt_toggle_attrs,
954 };
955
956 static struct attribute *oxp_tt_led_attrs[] = {
957 &dev_attr_tt_led.attr,
958 NULL
959 };
960
961 static const struct attribute_group oxp_tt_led_attribute_group = {
962 .is_visible = tt_led_is_visible,
963 .attrs = oxp_tt_led_attrs,
964 };
965
966 static const struct attribute_group *oxp_ec_groups[] = {
967 &oxp_tt_toggle_attribute_group,
968 &oxp_tt_led_attribute_group,
969 NULL
970 };
971
972 static const struct hwmon_ops oxp_ec_hwmon_ops = {
973 .is_visible = oxp_ec_hwmon_is_visible,
974 .read = oxp_platform_read,
975 .write = oxp_platform_write,
976 };
977
978 static const struct hwmon_chip_info oxp_ec_chip_info = {
979 .ops = &oxp_ec_hwmon_ops,
980 .info = oxp_platform_sensors,
981 };
982
983 /* Initialization logic */
oxp_platform_probe(struct platform_device * pdev)984 static int oxp_platform_probe(struct platform_device *pdev)
985 {
986 struct device *dev = &pdev->dev;
987 struct device *hwdev;
988 int ret;
989
990 oxp_dev = dev;
991 hwdev = devm_hwmon_device_register_with_info(dev, "oxp_ec", NULL,
992 &oxp_ec_chip_info, NULL);
993
994 if (IS_ERR(hwdev))
995 return PTR_ERR(hwdev);
996
997 if (oxp_psy_ext_supported()) {
998 ret = devm_battery_hook_register(dev, &battery_hook);
999 if (ret)
1000 return ret;
1001 }
1002
1003 return 0;
1004 }
1005
1006 static struct platform_driver oxp_platform_driver = {
1007 .driver = {
1008 .name = "oxp-platform",
1009 .dev_groups = oxp_ec_groups,
1010 },
1011 .probe = oxp_platform_probe,
1012 };
1013
1014 static struct platform_device *oxp_platform_device;
1015
oxp_platform_init(void)1016 static int __init oxp_platform_init(void)
1017 {
1018 const struct dmi_system_id *dmi_entry;
1019
1020 dmi_entry = dmi_first_match(dmi_table);
1021 if (!dmi_entry)
1022 return -ENODEV;
1023
1024 board = (enum oxp_board)(unsigned long)dmi_entry->driver_data;
1025
1026 /*
1027 * Have to check for AMD processor here because DMI strings are the same
1028 * between Intel and AMD boards on older OneXPlayer devices, the only way
1029 * to tell them apart is the CPU. Old Intel boards have an unsupported EC.
1030 */
1031 if (board == oxp_mini_amd && boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
1032 return -ENODEV;
1033
1034 oxp_platform_device =
1035 platform_create_bundle(&oxp_platform_driver,
1036 oxp_platform_probe, NULL, 0, NULL, 0);
1037
1038 return PTR_ERR_OR_ZERO(oxp_platform_device);
1039 }
1040
oxp_platform_exit(void)1041 static void __exit oxp_platform_exit(void)
1042 {
1043 platform_device_unregister(oxp_platform_device);
1044 platform_driver_unregister(&oxp_platform_driver);
1045 }
1046
1047 MODULE_DEVICE_TABLE(dmi, dmi_table);
1048
1049 module_init(oxp_platform_init);
1050 module_exit(oxp_platform_exit);
1051
1052 MODULE_AUTHOR("Joaquín Ignacio Aramendía <samsagax@gmail.com>");
1053 MODULE_DESCRIPTION("Platform driver that handles EC sensors of OneXPlayer devices");
1054 MODULE_LICENSE("GPL");
1055