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