xref: /openbmc/linux/drivers/hwmon/oxp-sensors.c (revision b35f4c73)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Platform driver for OneXPlayer, AOK ZOE, and Aya Neo Handhelds that expose
4  * fan reading and control via hwmon sysfs.
5  *
6  * Old OXP boards have the same DMI strings and they are told apart by
7  * the boot cpu vendor (Intel/AMD). Currently only AMD boards are
8  * supported but the code is made to be simple to add other handheld
9  * boards in the future.
10  * Fan control is provided via pwm interface in the range [0-255].
11  * Old AMD boards use [0-100] as range in the EC, the written value is
12  * scaled to accommodate for that. Newer boards like the mini PRO and
13  * AOK ZOE are not scaled but have the same EC layout.
14  *
15  * Copyright (C) 2022 Joaquín I. Aramendía <samsagax@gmail.com>
16  */
17 
18 #include <linux/acpi.h>
19 #include <linux/dmi.h>
20 #include <linux/hwmon.h>
21 #include <linux/init.h>
22 #include <linux/kernel.h>
23 #include <linux/module.h>
24 #include <linux/platform_device.h>
25 #include <linux/processor.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 	aya_neo_2,
45 	aya_neo_air,
46 	aya_neo_air_pro,
47 	aya_neo_geek,
48 	oxp_mini_amd,
49 	oxp_mini_amd_a07,
50 	oxp_mini_amd_pro,
51 };
52 
53 static enum oxp_board board;
54 
55 /* Fan reading and PWM */
56 #define OXP_SENSOR_FAN_REG		0x76 /* Fan reading is 2 registers long */
57 #define OXP_SENSOR_PWM_ENABLE_REG	0x4A /* PWM enable is 1 register long */
58 #define OXP_SENSOR_PWM_REG		0x4B /* PWM reading is 1 register long */
59 
60 /* Turbo button takeover function
61  * Older boards have different values and EC registers
62  * for the same function
63  */
64 #define OXP_OLD_TURBO_SWITCH_REG	0x1E
65 #define OXP_OLD_TURBO_TAKE_VAL		0x01
66 #define OXP_OLD_TURBO_RETURN_VAL	0x00
67 
68 #define OXP_TURBO_SWITCH_REG		0xF1
69 #define OXP_TURBO_TAKE_VAL		0x40
70 #define OXP_TURBO_RETURN_VAL		0x00
71 
72 static const struct dmi_system_id dmi_table[] = {
73 	{
74 		.matches = {
75 			DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
76 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 AR07"),
77 		},
78 		.driver_data = (void *)aok_zoe_a1,
79 	},
80 	{
81 		.matches = {
82 			DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
83 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 Pro"),
84 		},
85 		.driver_data = (void *)aok_zoe_a1,
86 	},
87 	{
88 		.matches = {
89 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
90 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AYANEO 2"),
91 		},
92 		.driver_data = (void *)aya_neo_2,
93 	},
94 	{
95 		.matches = {
96 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
97 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"),
98 		},
99 		.driver_data = (void *)aya_neo_air,
100 	},
101 	{
102 		.matches = {
103 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
104 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"),
105 		},
106 		.driver_data = (void *)aya_neo_air_pro,
107 	},
108 	{
109 		.matches = {
110 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
111 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "GEEK"),
112 		},
113 		.driver_data = (void *)aya_neo_geek,
114 	},
115 	{
116 		.matches = {
117 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
118 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONE XPLAYER"),
119 		},
120 		.driver_data = (void *)oxp_mini_amd,
121 	},
122 	{
123 		.matches = {
124 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
125 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER mini A07"),
126 		},
127 		.driver_data = (void *)oxp_mini_amd_a07,
128 	},
129 	{
130 		.matches = {
131 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
132 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER Mini Pro"),
133 		},
134 		.driver_data = (void *)oxp_mini_amd_pro,
135 	},
136 	{},
137 };
138 
139 /* Helper functions to handle EC read/write */
140 static int read_from_ec(u8 reg, int size, long *val)
141 {
142 	int i;
143 	int ret;
144 	u8 buffer;
145 
146 	if (!lock_global_acpi_lock())
147 		return -EBUSY;
148 
149 	*val = 0;
150 	for (i = 0; i < size; i++) {
151 		ret = ec_read(reg + i, &buffer);
152 		if (ret)
153 			return ret;
154 		*val <<= i * 8;
155 		*val += buffer;
156 	}
157 
158 	if (!unlock_global_acpi_lock())
159 		return -EBUSY;
160 
161 	return 0;
162 }
163 
164 static int write_to_ec(u8 reg, u8 value)
165 {
166 	int ret;
167 
168 	if (!lock_global_acpi_lock())
169 		return -EBUSY;
170 
171 	ret = ec_write(reg, value);
172 
173 	if (!unlock_global_acpi_lock())
174 		return -EBUSY;
175 
176 	return ret;
177 }
178 
179 /* Turbo button toggle functions */
180 static int tt_toggle_enable(void)
181 {
182 	u8 reg;
183 	u8 val;
184 
185 	switch (board) {
186 	case oxp_mini_amd_a07:
187 		reg = OXP_OLD_TURBO_SWITCH_REG;
188 		val = OXP_OLD_TURBO_TAKE_VAL;
189 		break;
190 	case oxp_mini_amd_pro:
191 	case aok_zoe_a1:
192 		reg = OXP_TURBO_SWITCH_REG;
193 		val = OXP_TURBO_TAKE_VAL;
194 		break;
195 	default:
196 		return -EINVAL;
197 	}
198 	return write_to_ec(reg, val);
199 }
200 
201 static int tt_toggle_disable(void)
202 {
203 	u8 reg;
204 	u8 val;
205 
206 	switch (board) {
207 	case oxp_mini_amd_a07:
208 		reg = OXP_OLD_TURBO_SWITCH_REG;
209 		val = OXP_OLD_TURBO_RETURN_VAL;
210 		break;
211 	case oxp_mini_amd_pro:
212 	case aok_zoe_a1:
213 		reg = OXP_TURBO_SWITCH_REG;
214 		val = OXP_TURBO_RETURN_VAL;
215 		break;
216 	default:
217 		return -EINVAL;
218 	}
219 	return write_to_ec(reg, val);
220 }
221 
222 /* Callbacks for turbo toggle attribute */
223 static ssize_t tt_toggle_store(struct device *dev,
224 			       struct device_attribute *attr, const char *buf,
225 			       size_t count)
226 {
227 	int rval;
228 	bool value;
229 
230 	rval = kstrtobool(buf, &value);
231 	if (rval)
232 		return rval;
233 
234 	if (value) {
235 		rval = tt_toggle_enable();
236 	} else {
237 		rval = tt_toggle_disable();
238 	}
239 	if (rval)
240 		return rval;
241 
242 	return count;
243 }
244 
245 static ssize_t tt_toggle_show(struct device *dev,
246 			      struct device_attribute *attr, char *buf)
247 {
248 	int retval;
249 	u8 reg;
250 	long val;
251 
252 	switch (board) {
253 	case oxp_mini_amd_a07:
254 		reg = OXP_OLD_TURBO_SWITCH_REG;
255 		break;
256 	case oxp_mini_amd_pro:
257 	case aok_zoe_a1:
258 		reg = OXP_TURBO_SWITCH_REG;
259 		break;
260 	default:
261 		return -EINVAL;
262 	}
263 
264 	retval = read_from_ec(reg, 1, &val);
265 	if (retval)
266 		return retval;
267 
268 	return sysfs_emit(buf, "%d\n", !!val);
269 }
270 
271 static DEVICE_ATTR_RW(tt_toggle);
272 
273 /* PWM enable/disable functions */
274 static int oxp_pwm_enable(void)
275 {
276 	return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, 0x01);
277 }
278 
279 static int oxp_pwm_disable(void)
280 {
281 	return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, 0x00);
282 }
283 
284 /* Callbacks for hwmon interface */
285 static umode_t oxp_ec_hwmon_is_visible(const void *drvdata,
286 				       enum hwmon_sensor_types type, u32 attr, int channel)
287 {
288 	switch (type) {
289 	case hwmon_fan:
290 		return 0444;
291 	case hwmon_pwm:
292 		return 0644;
293 	default:
294 		return 0;
295 	}
296 }
297 
298 static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
299 			     u32 attr, int channel, long *val)
300 {
301 	int ret;
302 
303 	switch (type) {
304 	case hwmon_fan:
305 		switch (attr) {
306 		case hwmon_fan_input:
307 			return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);
308 		default:
309 			break;
310 		}
311 		break;
312 	case hwmon_pwm:
313 		switch (attr) {
314 		case hwmon_pwm_input:
315 			ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
316 			if (ret)
317 				return ret;
318 			switch (board) {
319 			case aya_neo_2:
320 			case aya_neo_air:
321 			case aya_neo_air_pro:
322 			case aya_neo_geek:
323 			case oxp_mini_amd:
324 			case oxp_mini_amd_a07:
325 				*val = (*val * 255) / 100;
326 				break;
327 			case oxp_mini_amd_pro:
328 			case aok_zoe_a1:
329 			default:
330 				break;
331 			}
332 			return 0;
333 		case hwmon_pwm_enable:
334 			return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
335 		default:
336 			break;
337 		}
338 		break;
339 	default:
340 		break;
341 	}
342 	return -EOPNOTSUPP;
343 }
344 
345 static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type,
346 			      u32 attr, int channel, long val)
347 {
348 	switch (type) {
349 	case hwmon_pwm:
350 		switch (attr) {
351 		case hwmon_pwm_enable:
352 			if (val == 1)
353 				return oxp_pwm_enable();
354 			else if (val == 0)
355 				return oxp_pwm_disable();
356 			return -EINVAL;
357 		case hwmon_pwm_input:
358 			if (val < 0 || val > 255)
359 				return -EINVAL;
360 			switch (board) {
361 			case aya_neo_2:
362 			case aya_neo_air:
363 			case aya_neo_air_pro:
364 			case aya_neo_geek:
365 			case oxp_mini_amd:
366 			case oxp_mini_amd_a07:
367 				val = (val * 100) / 255;
368 				break;
369 			case aok_zoe_a1:
370 			case oxp_mini_amd_pro:
371 			default:
372 				break;
373 			}
374 			return write_to_ec(OXP_SENSOR_PWM_REG, val);
375 		default:
376 			break;
377 		}
378 		break;
379 	default:
380 		break;
381 	}
382 	return -EOPNOTSUPP;
383 }
384 
385 /* Known sensors in the OXP EC controllers */
386 static const struct hwmon_channel_info * const oxp_platform_sensors[] = {
387 	HWMON_CHANNEL_INFO(fan,
388 			   HWMON_F_INPUT),
389 	HWMON_CHANNEL_INFO(pwm,
390 			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
391 	NULL,
392 };
393 
394 static struct attribute *oxp_ec_attrs[] = {
395 	&dev_attr_tt_toggle.attr,
396 	NULL
397 };
398 
399 ATTRIBUTE_GROUPS(oxp_ec);
400 
401 static const struct hwmon_ops oxp_ec_hwmon_ops = {
402 	.is_visible = oxp_ec_hwmon_is_visible,
403 	.read = oxp_platform_read,
404 	.write = oxp_platform_write,
405 };
406 
407 static const struct hwmon_chip_info oxp_ec_chip_info = {
408 	.ops = &oxp_ec_hwmon_ops,
409 	.info = oxp_platform_sensors,
410 };
411 
412 /* Initialization logic */
413 static int oxp_platform_probe(struct platform_device *pdev)
414 {
415 	const struct dmi_system_id *dmi_entry;
416 	struct device *dev = &pdev->dev;
417 	struct device *hwdev;
418 	int ret;
419 
420 	/*
421 	 * Have to check for AMD processor here because DMI strings are the
422 	 * same between Intel and AMD boards, the only way to tell them apart
423 	 * is the CPU.
424 	 * Intel boards seem to have different EC registers and values to
425 	 * read/write.
426 	 */
427 	dmi_entry = dmi_first_match(dmi_table);
428 	if (!dmi_entry || boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
429 		return -ENODEV;
430 
431 	board = (enum oxp_board)(unsigned long)dmi_entry->driver_data;
432 
433 	switch (board) {
434 	case aok_zoe_a1:
435 	case oxp_mini_amd_a07:
436 	case oxp_mini_amd_pro:
437 		ret = devm_device_add_groups(dev, oxp_ec_groups);
438 		if (ret)
439 			return ret;
440 		break;
441 	default:
442 		break;
443 	}
444 
445 	hwdev = devm_hwmon_device_register_with_info(dev, "oxpec", NULL,
446 						     &oxp_ec_chip_info, NULL);
447 
448 	return PTR_ERR_OR_ZERO(hwdev);
449 }
450 
451 static struct platform_driver oxp_platform_driver = {
452 	.driver = {
453 		.name = "oxp-platform",
454 	},
455 	.probe = oxp_platform_probe,
456 };
457 
458 static struct platform_device *oxp_platform_device;
459 
460 static int __init oxp_platform_init(void)
461 {
462 	oxp_platform_device =
463 		platform_create_bundle(&oxp_platform_driver,
464 				       oxp_platform_probe, NULL, 0, NULL, 0);
465 
466 	return PTR_ERR_OR_ZERO(oxp_platform_device);
467 }
468 
469 static void __exit oxp_platform_exit(void)
470 {
471 	platform_device_unregister(oxp_platform_device);
472 	platform_driver_unregister(&oxp_platform_driver);
473 }
474 
475 MODULE_DEVICE_TABLE(dmi, dmi_table);
476 
477 module_init(oxp_platform_init);
478 module_exit(oxp_platform_exit);
479 
480 MODULE_AUTHOR("Joaquín Ignacio Aramendía <samsagax@gmail.com>");
481 MODULE_DESCRIPTION("Platform driver that handles EC sensors of OneXPlayer devices");
482 MODULE_LICENSE("GPL");
483