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