Linux Audio

Check our new training course

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