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