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