xref: /linux/drivers/platform/x86/oxpec.c (revision b79ad5e8ba5cfda93a83e0cf71d4743829cc9f83)
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 
32 static bool lock_global_acpi_lock(void)
33 {
34 	return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS, &oxp_mutex));
35 }
36 
37 static bool unlock_global_acpi_lock(void)
38 {
39 	return ACPI_SUCCESS(acpi_release_global_lock(oxp_mutex));
40 }
41 
42 enum oxp_board {
43 	aok_zoe_a1 = 1,
44 	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 SUPER X"),
212 		},
213 		.driver_data = (void *)oxp_g1_a,
214 	},
215 	{
216 		.matches = {
217 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
218 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER G1 i"),
219 		},
220 		.driver_data = (void *)oxp_g1_i,
221 	},
222 	{
223 		.matches = {
224 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
225 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER mini A07"),
226 		},
227 		.driver_data = (void *)oxp_mini_amd_a07,
228 	},
229 	{
230 		.matches = {
231 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
232 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER Mini Pro"),
233 		},
234 		.driver_data = (void *)oxp_mini_amd_pro,
235 	},
236 	{
237 		.matches = {
238 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
239 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1z"),
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 A"),
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 X1 i"),
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 X1Air"),
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 X1 mini"),
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 X1Mini Pro"),
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"),
282 		},
283 		.driver_data = (void *)oxp_x1,
284 	},
285 	{
286 		.matches = {
287 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
288 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1Pro EVA-02"),
289 		},
290 		.driver_data = (void *)oxp_x1,
291 	},
292 	{},
293 };
294 
295 /* Helper functions to handle EC read/write */
296 static int read_from_ec(u8 reg, int size, long *val)
297 {
298 	u8 buffer;
299 	int ret;
300 	int i;
301 
302 	if (!lock_global_acpi_lock())
303 		return -EBUSY;
304 
305 	*val = 0;
306 	for (i = 0; i < size; i++) {
307 		ret = ec_read(reg + i, &buffer);
308 		if (ret)
309 			return ret;
310 		*val <<= i * 8;
311 		*val += buffer;
312 	}
313 
314 	if (!unlock_global_acpi_lock())
315 		return -EBUSY;
316 
317 	return 0;
318 }
319 
320 static int write_to_ec(u8 reg, u8 value)
321 {
322 	int ret;
323 
324 	if (!lock_global_acpi_lock())
325 		return -EBUSY;
326 
327 	ret = ec_write(reg, value);
328 
329 	if (!unlock_global_acpi_lock())
330 		return -EBUSY;
331 
332 	return ret;
333 }
334 
335 /* Callbacks for turbo toggle attribute */
336 static umode_t tt_toggle_is_visible(struct kobject *kobj,
337 				    struct attribute *attr, int n)
338 {
339 	switch (board) {
340 	case aok_zoe_a1:
341 	case oxp_2:
342 	case oxp_fly:
343 	case oxp_mini_amd_a07:
344 	case oxp_mini_amd_pro:
345 	case oxp_x1:
346 	case oxp_g1_i:
347 	case oxp_g1_a:
348 		return attr->mode;
349 	default:
350 		break;
351 	}
352 	return 0;
353 }
354 
355 static ssize_t tt_toggle_store(struct device *dev,
356 			       struct device_attribute *attr, const char *buf,
357 			       size_t count)
358 {
359 	u8 reg, mask, val;
360 	long raw_val;
361 	bool enable;
362 	int ret;
363 
364 	ret = kstrtobool(buf, &enable);
365 	if (ret)
366 		return ret;
367 
368 	switch (board) {
369 	case oxp_mini_amd_a07:
370 		reg = OXP_MINI_TURBO_SWITCH_REG;
371 		mask = OXP_MINI_TURBO_TAKE_VAL;
372 		break;
373 	case aok_zoe_a1:
374 	case oxp_fly:
375 	case oxp_mini_amd_pro:
376 	case oxp_g1_a:
377 		reg = OXP_TURBO_SWITCH_REG;
378 		mask = OXP_TURBO_TAKE_VAL;
379 		break;
380 	case oxp_2:
381 	case oxp_x1:
382 	case oxp_g1_i:
383 		reg = OXP_2_TURBO_SWITCH_REG;
384 		mask = OXP_TURBO_TAKE_VAL;
385 		break;
386 	default:
387 		return -EINVAL;
388 	}
389 
390 	ret = read_from_ec(reg, 1, &raw_val);
391 	if (ret)
392 		return ret;
393 
394 	val = raw_val;
395 	if (enable)
396 		val |= mask;
397 	else
398 		val &= ~mask;
399 
400 	ret = write_to_ec(reg, val);
401 	if (ret)
402 		return ret;
403 
404 	return count;
405 }
406 
407 static ssize_t tt_toggle_show(struct device *dev,
408 			      struct device_attribute *attr, char *buf)
409 {
410 	u8 reg, mask;
411 	int retval;
412 	long val;
413 
414 	switch (board) {
415 	case oxp_mini_amd_a07:
416 		reg = OXP_MINI_TURBO_SWITCH_REG;
417 		mask = OXP_MINI_TURBO_TAKE_VAL;
418 		break;
419 	case aok_zoe_a1:
420 	case oxp_fly:
421 	case oxp_mini_amd_pro:
422 	case oxp_g1_a:
423 		reg = OXP_TURBO_SWITCH_REG;
424 		mask = OXP_TURBO_TAKE_VAL;
425 		break;
426 	case oxp_2:
427 	case oxp_x1:
428 	case oxp_g1_i:
429 		reg = OXP_2_TURBO_SWITCH_REG;
430 		mask = OXP_TURBO_TAKE_VAL;
431 		break;
432 	default:
433 		return -EINVAL;
434 	}
435 
436 	retval = read_from_ec(reg, 1, &val);
437 	if (retval)
438 		return retval;
439 
440 	return sysfs_emit(buf, "%d\n", (val & mask) == mask);
441 }
442 
443 static DEVICE_ATTR_RW(tt_toggle);
444 
445 /* Callbacks for turbo LED attribute */
446 static umode_t tt_led_is_visible(struct kobject *kobj,
447 				 struct attribute *attr, int n)
448 {
449 	switch (board) {
450 	case oxp_x1:
451 		return attr->mode;
452 	default:
453 		break;
454 	}
455 	return 0;
456 }
457 
458 static ssize_t tt_led_store(struct device *dev,
459 			    struct device_attribute *attr, const char *buf,
460 			    size_t count)
461 {
462 	u8 reg, val;
463 	bool value;
464 	int ret;
465 
466 	ret = kstrtobool(buf, &value);
467 	if (ret)
468 		return ret;
469 
470 	switch (board) {
471 	case oxp_x1:
472 		reg = OXP_X1_TURBO_LED_REG;
473 		val = value ? OXP_X1_TURBO_LED_ON : OXP_X1_TURBO_LED_OFF;
474 		break;
475 	default:
476 		return -EINVAL;
477 	}
478 
479 	ret = write_to_ec(reg, val);
480 	if (ret)
481 		return ret;
482 
483 	return count;
484 }
485 
486 static ssize_t tt_led_show(struct device *dev,
487 			   struct device_attribute *attr, char *buf)
488 {
489 	long enval;
490 	long val;
491 	int ret;
492 	u8 reg;
493 
494 	switch (board) {
495 	case oxp_x1:
496 		reg = OXP_X1_TURBO_LED_REG;
497 		enval = OXP_X1_TURBO_LED_ON;
498 		break;
499 	default:
500 		return -EINVAL;
501 	}
502 
503 	ret = read_from_ec(reg, 1, &val);
504 	if (ret)
505 		return ret;
506 
507 	return sysfs_emit(buf, "%d\n", val == enval);
508 }
509 
510 static DEVICE_ATTR_RW(tt_led);
511 
512 /* Callbacks for charge behaviour attributes */
513 static bool oxp_psy_ext_supported(void)
514 {
515 	switch (board) {
516 	case oxp_x1:
517 	case oxp_g1_i:
518 	case oxp_g1_a:
519 	case oxp_fly:
520 		return true;
521 	default:
522 		break;
523 	}
524 	return false;
525 }
526 
527 static int oxp_psy_ext_get_prop(struct power_supply *psy,
528 				const struct power_supply_ext *ext,
529 				void *data,
530 				enum power_supply_property psp,
531 				union power_supply_propval *val)
532 {
533 	long raw_val;
534 	int ret;
535 
536 	switch (psp) {
537 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
538 		ret = read_from_ec(OXP_X1_CHARGE_LIMIT_REG, 1, &raw_val);
539 		if (ret)
540 			return ret;
541 		if (raw_val < 0 || raw_val > 100)
542 			return -EINVAL;
543 		val->intval = raw_val;
544 		return 0;
545 	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
546 		ret = read_from_ec(OXP_X1_CHARGE_INHIBIT_REG, 1, &raw_val);
547 		if (ret)
548 			return ret;
549 		if ((raw_val & OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS) ==
550 		    OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS)
551 			val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
552 		else if ((raw_val & OXP_X1_CHARGE_INHIBIT_MASK_AWAKE) ==
553 			 OXP_X1_CHARGE_INHIBIT_MASK_AWAKE)
554 			val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE;
555 		else
556 			val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
557 		return 0;
558 	default:
559 		return -EINVAL;
560 	}
561 }
562 
563 static int oxp_psy_ext_set_prop(struct power_supply *psy,
564 				const struct power_supply_ext *ext,
565 				void *data,
566 				enum power_supply_property psp,
567 				const union power_supply_propval *val)
568 {
569 	long raw_val;
570 
571 	switch (psp) {
572 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
573 		if (val->intval < 0 || val->intval > 100)
574 			return -EINVAL;
575 		return write_to_ec(OXP_X1_CHARGE_LIMIT_REG, val->intval);
576 	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
577 		switch (val->intval) {
578 		case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
579 			raw_val = 0;
580 			break;
581 		case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE:
582 			raw_val = OXP_X1_CHARGE_INHIBIT_MASK_AWAKE;
583 			break;
584 		case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
585 			raw_val = OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS;
586 			break;
587 		default:
588 			return -EINVAL;
589 		}
590 
591 		return write_to_ec(OXP_X1_CHARGE_INHIBIT_REG, raw_val);
592 	default:
593 		return -EINVAL;
594 	}
595 }
596 
597 static int oxp_psy_prop_is_writeable(struct power_supply *psy,
598 				     const struct power_supply_ext *ext,
599 				     void *data,
600 				     enum power_supply_property psp)
601 {
602 	return true;
603 }
604 
605 static const enum power_supply_property oxp_psy_ext_props[] = {
606 	POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
607 	POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
608 };
609 
610 static const struct power_supply_ext oxp_psy_ext = {
611 	.name			= "oxp-charge-control",
612 	.properties		= oxp_psy_ext_props,
613 	.num_properties		= ARRAY_SIZE(oxp_psy_ext_props),
614 	.charge_behaviours	= EC_CHARGE_CONTROL_BEHAVIOURS,
615 	.get_property		= oxp_psy_ext_get_prop,
616 	.set_property		= oxp_psy_ext_set_prop,
617 	.property_is_writeable	= oxp_psy_prop_is_writeable,
618 };
619 
620 static int oxp_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
621 {
622 	return power_supply_register_extension(battery, &oxp_psy_ext, oxp_dev, NULL);
623 }
624 
625 static int oxp_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
626 {
627 	power_supply_unregister_extension(battery, &oxp_psy_ext);
628 	return 0;
629 }
630 
631 static struct acpi_battery_hook battery_hook = {
632 	.add_battery	= oxp_add_battery,
633 	.remove_battery	= oxp_remove_battery,
634 	.name		= "OneXPlayer Battery",
635 };
636 
637 /* PWM enable/disable functions */
638 static int oxp_pwm_enable(void)
639 {
640 	switch (board) {
641 	case orange_pi_neo:
642 		return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
643 	case aok_zoe_a1:
644 	case oxp_2:
645 	case oxp_fly:
646 	case oxp_mini_amd:
647 	case oxp_mini_amd_a07:
648 	case oxp_mini_amd_pro:
649 	case oxp_x1:
650 	case oxp_g1_i:
651 	case oxp_g1_a:
652 		return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
653 	default:
654 		return -EINVAL;
655 	}
656 }
657 
658 static int oxp_pwm_disable(void)
659 {
660 	switch (board) {
661 	case orange_pi_neo:
662 		return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
663 	case aok_zoe_a1:
664 	case oxp_2:
665 	case oxp_fly:
666 	case oxp_mini_amd:
667 	case oxp_mini_amd_a07:
668 	case oxp_mini_amd_pro:
669 	case oxp_x1:
670 	case oxp_g1_i:
671 	case oxp_g1_a:
672 		return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
673 	default:
674 		return -EINVAL;
675 	}
676 }
677 
678 static int oxp_pwm_read(long *val)
679 {
680 	switch (board) {
681 	case orange_pi_neo:
682 		return read_from_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, 1, val);
683 	case aok_zoe_a1:
684 	case oxp_2:
685 	case oxp_fly:
686 	case oxp_mini_amd:
687 	case oxp_mini_amd_a07:
688 	case oxp_mini_amd_pro:
689 	case oxp_x1:
690 	case oxp_g1_i:
691 	case oxp_g1_a:
692 		return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
693 	default:
694 		return -EOPNOTSUPP;
695 	}
696 }
697 
698 /* Callbacks for hwmon interface */
699 static umode_t oxp_ec_hwmon_is_visible(const void *drvdata,
700 				       enum hwmon_sensor_types type, u32 attr, int channel)
701 {
702 	switch (type) {
703 	case hwmon_fan:
704 		return 0444;
705 	case hwmon_pwm:
706 		return 0644;
707 	default:
708 		return 0;
709 	}
710 }
711 
712 /* Fan speed read function */
713 static int oxp_pwm_fan_speed(long *val)
714 {
715 	switch (board) {
716 	case orange_pi_neo:
717 		return read_from_ec(ORANGEPI_SENSOR_FAN_REG, 2, val);
718 	case oxp_2:
719 	case oxp_x1:
720 	case oxp_g1_i:
721 		return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val);
722 	case aok_zoe_a1:
723 	case oxp_fly:
724 	case oxp_mini_amd:
725 	case oxp_mini_amd_a07:
726 	case oxp_mini_amd_pro:
727 	case oxp_g1_a:
728 		return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);
729 	default:
730 		return -EOPNOTSUPP;
731 	}
732 }
733 
734 /* PWM input read/write functions */
735 static int oxp_pwm_input_write(long val)
736 {
737 	if (val < 0 || val > 255)
738 		return -EINVAL;
739 
740 	switch (board) {
741 	case orange_pi_neo:
742 		/* scale to range [1-244] */
743 		val = ((val - 1) * 243 / 254) + 1;
744 		return write_to_ec(ORANGEPI_SENSOR_PWM_REG, val);
745 	case oxp_2:
746 	case oxp_x1:
747 	case oxp_g1_i:
748 		/* scale to range [0-184] */
749 		val = (val * 184) / 255;
750 		return write_to_ec(OXP_SENSOR_PWM_REG, val);
751 	case oxp_mini_amd:
752 	case oxp_mini_amd_a07:
753 		/* scale to range [0-100] */
754 		val = (val * 100) / 255;
755 		return write_to_ec(OXP_SENSOR_PWM_REG, val);
756 	case aok_zoe_a1:
757 	case oxp_fly:
758 	case oxp_mini_amd_pro:
759 	case oxp_g1_a:
760 		return write_to_ec(OXP_SENSOR_PWM_REG, val);
761 	default:
762 		return -EOPNOTSUPP;
763 	}
764 }
765 
766 static int oxp_pwm_input_read(long *val)
767 {
768 	int ret;
769 
770 	switch (board) {
771 	case orange_pi_neo:
772 		ret = read_from_ec(ORANGEPI_SENSOR_PWM_REG, 1, val);
773 		if (ret)
774 			return ret;
775 		/* scale from range [1-244] */
776 		*val = ((*val - 1) * 254 / 243) + 1;
777 		break;
778 	case oxp_2:
779 	case oxp_x1:
780 	case oxp_g1_i:
781 		ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
782 		if (ret)
783 			return ret;
784 		/* scale from range [0-184] */
785 		*val = (*val * 255) / 184;
786 		break;
787 	case oxp_mini_amd:
788 	case oxp_mini_amd_a07:
789 		ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
790 		if (ret)
791 			return ret;
792 		/* scale from range [0-100] */
793 		*val = (*val * 255) / 100;
794 		break;
795 	case aok_zoe_a1:
796 	case oxp_fly:
797 	case oxp_mini_amd_pro:
798 	case oxp_g1_a:
799 	default:
800 		ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
801 		if (ret)
802 			return ret;
803 		break;
804 	}
805 	return 0;
806 }
807 
808 static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
809 			     u32 attr, int channel, long *val)
810 {
811 	int ret;
812 
813 	switch (type) {
814 	case hwmon_fan:
815 		switch (attr) {
816 		case hwmon_fan_input:
817 			return oxp_pwm_fan_speed(val);
818 		default:
819 			break;
820 		}
821 		break;
822 	case hwmon_pwm:
823 		switch (attr) {
824 		case hwmon_pwm_input:
825 			return oxp_pwm_input_read(val);
826 		case hwmon_pwm_enable:
827 			ret = oxp_pwm_read(val);
828 			if (ret)
829 				return ret;
830 
831 			/* Check for auto and return 2 */
832 			if (!*val) {
833 				*val = 2;
834 				return 0;
835 			}
836 
837 			/* Return 0 if at full fan speed, 1 otherwise */
838 			ret = oxp_pwm_fan_speed(val);
839 			if (ret)
840 				return ret;
841 
842 			if (*val == 255)
843 				*val = 0;
844 			else
845 				*val = 1;
846 
847 			return 0;
848 		default:
849 			break;
850 		}
851 		break;
852 	default:
853 		break;
854 	}
855 	return -EOPNOTSUPP;
856 }
857 
858 static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type,
859 			      u32 attr, int channel, long val)
860 {
861 	int ret;
862 
863 	switch (type) {
864 	case hwmon_pwm:
865 		switch (attr) {
866 		case hwmon_pwm_enable:
867 			if (val == 1)
868 				return oxp_pwm_enable();
869 			else if (val == 2)
870 				return oxp_pwm_disable();
871 			else if (val != 0)
872 				return -EINVAL;
873 
874 			/* Enable PWM and set to max speed */
875 			ret = oxp_pwm_enable();
876 			if (ret)
877 				return ret;
878 			return oxp_pwm_input_write(255);
879 		case hwmon_pwm_input:
880 			return oxp_pwm_input_write(val);
881 		default:
882 			break;
883 		}
884 		break;
885 	default:
886 		break;
887 	}
888 	return -EOPNOTSUPP;
889 }
890 
891 /* Known sensors in the OXP EC controllers */
892 static const struct hwmon_channel_info * const oxp_platform_sensors[] = {
893 	HWMON_CHANNEL_INFO(fan,
894 			   HWMON_F_INPUT),
895 	HWMON_CHANNEL_INFO(pwm,
896 			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
897 	NULL,
898 };
899 
900 static struct attribute *oxp_tt_toggle_attrs[] = {
901 	&dev_attr_tt_toggle.attr,
902 	NULL
903 };
904 
905 static const struct attribute_group oxp_tt_toggle_attribute_group = {
906 	.is_visible = tt_toggle_is_visible,
907 	.attrs = oxp_tt_toggle_attrs,
908 };
909 
910 static struct attribute *oxp_tt_led_attrs[] = {
911 	&dev_attr_tt_led.attr,
912 	NULL
913 };
914 
915 static const struct attribute_group oxp_tt_led_attribute_group = {
916 	.is_visible = tt_led_is_visible,
917 	.attrs = oxp_tt_led_attrs,
918 };
919 
920 static const struct attribute_group *oxp_ec_groups[] = {
921 	&oxp_tt_toggle_attribute_group,
922 	&oxp_tt_led_attribute_group,
923 	NULL
924 };
925 
926 static const struct hwmon_ops oxp_ec_hwmon_ops = {
927 	.is_visible = oxp_ec_hwmon_is_visible,
928 	.read = oxp_platform_read,
929 	.write = oxp_platform_write,
930 };
931 
932 static const struct hwmon_chip_info oxp_ec_chip_info = {
933 	.ops = &oxp_ec_hwmon_ops,
934 	.info = oxp_platform_sensors,
935 };
936 
937 /* Initialization logic */
938 static int oxp_platform_probe(struct platform_device *pdev)
939 {
940 	struct device *dev = &pdev->dev;
941 	struct device *hwdev;
942 	int ret;
943 
944 	oxp_dev = dev;
945 	hwdev = devm_hwmon_device_register_with_info(dev, "oxp_ec", NULL,
946 						     &oxp_ec_chip_info, NULL);
947 
948 	if (IS_ERR(hwdev))
949 		return PTR_ERR(hwdev);
950 
951 	if (oxp_psy_ext_supported()) {
952 		ret = devm_battery_hook_register(dev, &battery_hook);
953 		if (ret)
954 			return ret;
955 	}
956 
957 	return 0;
958 }
959 
960 static struct platform_driver oxp_platform_driver = {
961 	.driver = {
962 		.name = "oxp-platform",
963 		.dev_groups = oxp_ec_groups,
964 	},
965 	.probe = oxp_platform_probe,
966 };
967 
968 static struct platform_device *oxp_platform_device;
969 
970 static int __init oxp_platform_init(void)
971 {
972 	const struct dmi_system_id *dmi_entry;
973 
974 	dmi_entry = dmi_first_match(dmi_table);
975 	if (!dmi_entry)
976 		return -ENODEV;
977 
978 	board = (enum oxp_board)(unsigned long)dmi_entry->driver_data;
979 
980 	/*
981 	 * Have to check for AMD processor here because DMI strings are the same
982 	 * between Intel and AMD boards on older OneXPlayer devices, the only way
983 	 * to tell them apart is the CPU. Old Intel boards have an unsupported EC.
984 	 */
985 	if (board == oxp_mini_amd && boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
986 		return -ENODEV;
987 
988 	oxp_platform_device =
989 		platform_create_bundle(&oxp_platform_driver,
990 				       oxp_platform_probe, NULL, 0, NULL, 0);
991 
992 	return PTR_ERR_OR_ZERO(oxp_platform_device);
993 }
994 
995 static void __exit oxp_platform_exit(void)
996 {
997 	platform_device_unregister(oxp_platform_device);
998 	platform_driver_unregister(&oxp_platform_driver);
999 }
1000 
1001 MODULE_DEVICE_TABLE(dmi, dmi_table);
1002 
1003 module_init(oxp_platform_init);
1004 module_exit(oxp_platform_exit);
1005 
1006 MODULE_AUTHOR("Joaquín Ignacio Aramendía <samsagax@gmail.com>");
1007 MODULE_DESCRIPTION("Platform driver that handles EC sensors of OneXPlayer devices");
1008 MODULE_LICENSE("GPL");
1009