xref: /openbmc/linux/drivers/hwmon/oxp-sensors.c (revision 3ca0f12a02582c3dd4029294ab0245ba77c27a77)
1ed264e8aSJoaquín Ignacio Aramendía // SPDX-License-Identifier: GPL-2.0+
2ed264e8aSJoaquín Ignacio Aramendía /*
3ed264e8aSJoaquín Ignacio Aramendía  * Platform driver for OXP Handhelds that expose fan reading and control
4ed264e8aSJoaquín Ignacio Aramendía  * via hwmon sysfs.
5ed264e8aSJoaquín Ignacio Aramendía  *
6*3ca0f12aSJoaquín Ignacio Aramendía  * Old boards have the same DMI strings and they are told appart by the
7ed264e8aSJoaquín Ignacio Aramendía  * boot cpu vendor (Intel/AMD). Currently only AMD boards are supported
8ed264e8aSJoaquín Ignacio Aramendía  * but the code is made to be simple to add other handheld boards in the
9ed264e8aSJoaquín Ignacio Aramendía  * future.
10*3ca0f12aSJoaquín Ignacio Aramendía  * Fan control is provided via pwm interface in the range [0-255].
11*3ca0f12aSJoaquín Ignacio Aramendía  * Old AMD boards use [0-100] as range in the EC, the written value is
12*3ca0f12aSJoaquín Ignacio Aramendía  * scaled to accommodate for that. Newer boards like the mini PRO and
13*3ca0f12aSJoaquín Ignacio Aramendía  * AOK ZOE are not scaled but have the same EC layout.
14ed264e8aSJoaquín Ignacio Aramendía  *
15ed264e8aSJoaquín Ignacio Aramendía  * Copyright (C) 2022 Joaquín I. Aramendía <samsagax@gmail.com>
16ed264e8aSJoaquín Ignacio Aramendía  */
17ed264e8aSJoaquín Ignacio Aramendía 
18ed264e8aSJoaquín Ignacio Aramendía #include <linux/acpi.h>
19ed264e8aSJoaquín Ignacio Aramendía #include <linux/dev_printk.h>
20ed264e8aSJoaquín Ignacio Aramendía #include <linux/dmi.h>
21ed264e8aSJoaquín Ignacio Aramendía #include <linux/hwmon.h>
22ed264e8aSJoaquín Ignacio Aramendía #include <linux/init.h>
23ed264e8aSJoaquín Ignacio Aramendía #include <linux/kernel.h>
24ed264e8aSJoaquín Ignacio Aramendía #include <linux/module.h>
25ed264e8aSJoaquín Ignacio Aramendía #include <linux/platform_device.h>
26ed264e8aSJoaquín Ignacio Aramendía #include <linux/processor.h>
27ed264e8aSJoaquín Ignacio Aramendía 
28ed264e8aSJoaquín Ignacio Aramendía /* Handle ACPI lock mechanism */
29ed264e8aSJoaquín Ignacio Aramendía static u32 oxp_mutex;
30ed264e8aSJoaquín Ignacio Aramendía 
31ed264e8aSJoaquín Ignacio Aramendía #define ACPI_LOCK_DELAY_MS	500
32ed264e8aSJoaquín Ignacio Aramendía 
33ed264e8aSJoaquín Ignacio Aramendía static bool lock_global_acpi_lock(void)
34ed264e8aSJoaquín Ignacio Aramendía {
35ed264e8aSJoaquín Ignacio Aramendía 	return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS, &oxp_mutex));
36ed264e8aSJoaquín Ignacio Aramendía }
37ed264e8aSJoaquín Ignacio Aramendía 
38ed264e8aSJoaquín Ignacio Aramendía static bool unlock_global_acpi_lock(void)
39ed264e8aSJoaquín Ignacio Aramendía {
40ed264e8aSJoaquín Ignacio Aramendía 	return ACPI_SUCCESS(acpi_release_global_lock(oxp_mutex));
41ed264e8aSJoaquín Ignacio Aramendía }
42ed264e8aSJoaquín Ignacio Aramendía 
43*3ca0f12aSJoaquín Ignacio Aramendía enum oxp_board {
44*3ca0f12aSJoaquín Ignacio Aramendía 	aok_zoe_a1 = 1,
45*3ca0f12aSJoaquín Ignacio Aramendía 	oxp_mini_amd,
46*3ca0f12aSJoaquín Ignacio Aramendía 	oxp_mini_amd_pro,
47*3ca0f12aSJoaquín Ignacio Aramendía };
48*3ca0f12aSJoaquín Ignacio Aramendía 
49*3ca0f12aSJoaquín Ignacio Aramendía static enum oxp_board board;
50*3ca0f12aSJoaquín Ignacio Aramendía 
51ed264e8aSJoaquín Ignacio Aramendía #define OXP_SENSOR_FAN_REG		0x76 /* Fan reading is 2 registers long */
52ed264e8aSJoaquín Ignacio Aramendía #define OXP_SENSOR_PWM_ENABLE_REG	0x4A /* PWM enable is 1 register long */
53ed264e8aSJoaquín Ignacio Aramendía #define OXP_SENSOR_PWM_REG		0x4B /* PWM reading is 1 register long */
54ed264e8aSJoaquín Ignacio Aramendía 
55ed264e8aSJoaquín Ignacio Aramendía static const struct dmi_system_id dmi_table[] = {
56ed264e8aSJoaquín Ignacio Aramendía 	{
57ed264e8aSJoaquín Ignacio Aramendía 		.matches = {
58*3ca0f12aSJoaquín Ignacio Aramendía 			DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
59*3ca0f12aSJoaquín Ignacio Aramendía 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 AR07"),
60*3ca0f12aSJoaquín Ignacio Aramendía 		},
61*3ca0f12aSJoaquín Ignacio Aramendía 		.driver_data = (void *) &(enum oxp_board) {aok_zoe_a1},
62*3ca0f12aSJoaquín Ignacio Aramendía 	},
63*3ca0f12aSJoaquín Ignacio Aramendía 	{
64*3ca0f12aSJoaquín Ignacio Aramendía 		.matches = {
65ed264e8aSJoaquín Ignacio Aramendía 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
66ed264e8aSJoaquín Ignacio Aramendía 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONE XPLAYER"),
67ed264e8aSJoaquín Ignacio Aramendía 		},
68*3ca0f12aSJoaquín Ignacio Aramendía 		.driver_data = (void *) &(enum oxp_board) {oxp_mini_amd},
69*3ca0f12aSJoaquín Ignacio Aramendía 	},
70*3ca0f12aSJoaquín Ignacio Aramendía 	{
71*3ca0f12aSJoaquín Ignacio Aramendía 		.matches = {
72*3ca0f12aSJoaquín Ignacio Aramendía 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
73*3ca0f12aSJoaquín Ignacio Aramendía 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER Mini Pro"),
74*3ca0f12aSJoaquín Ignacio Aramendía 		},
75*3ca0f12aSJoaquín Ignacio Aramendía 		.driver_data = (void *) &(enum oxp_board) {oxp_mini_amd_pro},
76ed264e8aSJoaquín Ignacio Aramendía 	},
77ed264e8aSJoaquín Ignacio Aramendía 	{},
78ed264e8aSJoaquín Ignacio Aramendía };
79ed264e8aSJoaquín Ignacio Aramendía 
80ed264e8aSJoaquín Ignacio Aramendía /* Helper functions to handle EC read/write */
81ed264e8aSJoaquín Ignacio Aramendía static int read_from_ec(u8 reg, int size, long *val)
82ed264e8aSJoaquín Ignacio Aramendía {
83ed264e8aSJoaquín Ignacio Aramendía 	int i;
84ed264e8aSJoaquín Ignacio Aramendía 	int ret;
85ed264e8aSJoaquín Ignacio Aramendía 	u8 buffer;
86ed264e8aSJoaquín Ignacio Aramendía 
87ed264e8aSJoaquín Ignacio Aramendía 	if (!lock_global_acpi_lock())
88ed264e8aSJoaquín Ignacio Aramendía 		return -EBUSY;
89ed264e8aSJoaquín Ignacio Aramendía 
90ed264e8aSJoaquín Ignacio Aramendía 	*val = 0;
91ed264e8aSJoaquín Ignacio Aramendía 	for (i = 0; i < size; i++) {
92ed264e8aSJoaquín Ignacio Aramendía 		ret = ec_read(reg + i, &buffer);
93ed264e8aSJoaquín Ignacio Aramendía 		if (ret)
94ed264e8aSJoaquín Ignacio Aramendía 			return ret;
95ed264e8aSJoaquín Ignacio Aramendía 		*val <<= i * 8;
96ed264e8aSJoaquín Ignacio Aramendía 		*val += buffer;
97ed264e8aSJoaquín Ignacio Aramendía 	}
98ed264e8aSJoaquín Ignacio Aramendía 
99ed264e8aSJoaquín Ignacio Aramendía 	if (!unlock_global_acpi_lock())
100ed264e8aSJoaquín Ignacio Aramendía 		return -EBUSY;
101ed264e8aSJoaquín Ignacio Aramendía 
102ed264e8aSJoaquín Ignacio Aramendía 	return 0;
103ed264e8aSJoaquín Ignacio Aramendía }
104ed264e8aSJoaquín Ignacio Aramendía 
105ed264e8aSJoaquín Ignacio Aramendía static int write_to_ec(const struct device *dev, u8 reg, u8 value)
106ed264e8aSJoaquín Ignacio Aramendía {
107ed264e8aSJoaquín Ignacio Aramendía 	int ret;
108ed264e8aSJoaquín Ignacio Aramendía 
109ed264e8aSJoaquín Ignacio Aramendía 	if (!lock_global_acpi_lock())
110ed264e8aSJoaquín Ignacio Aramendía 		return -EBUSY;
111ed264e8aSJoaquín Ignacio Aramendía 
112ed264e8aSJoaquín Ignacio Aramendía 	ret = ec_write(reg, value);
113ed264e8aSJoaquín Ignacio Aramendía 
114ed264e8aSJoaquín Ignacio Aramendía 	if (!unlock_global_acpi_lock())
115ed264e8aSJoaquín Ignacio Aramendía 		return -EBUSY;
116ed264e8aSJoaquín Ignacio Aramendía 
117ed264e8aSJoaquín Ignacio Aramendía 	return ret;
118ed264e8aSJoaquín Ignacio Aramendía }
119ed264e8aSJoaquín Ignacio Aramendía 
120ed264e8aSJoaquín Ignacio Aramendía static int oxp_pwm_enable(const struct device *dev)
121ed264e8aSJoaquín Ignacio Aramendía {
122ed264e8aSJoaquín Ignacio Aramendía 	return write_to_ec(dev, OXP_SENSOR_PWM_ENABLE_REG, 0x01);
123ed264e8aSJoaquín Ignacio Aramendía }
124ed264e8aSJoaquín Ignacio Aramendía 
125ed264e8aSJoaquín Ignacio Aramendía static int oxp_pwm_disable(const struct device *dev)
126ed264e8aSJoaquín Ignacio Aramendía {
127ed264e8aSJoaquín Ignacio Aramendía 	return write_to_ec(dev, OXP_SENSOR_PWM_ENABLE_REG, 0x00);
128ed264e8aSJoaquín Ignacio Aramendía }
129ed264e8aSJoaquín Ignacio Aramendía 
130ed264e8aSJoaquín Ignacio Aramendía /* Callbacks for hwmon interface */
131ed264e8aSJoaquín Ignacio Aramendía static umode_t oxp_ec_hwmon_is_visible(const void *drvdata,
132ed264e8aSJoaquín Ignacio Aramendía 				       enum hwmon_sensor_types type, u32 attr, int channel)
133ed264e8aSJoaquín Ignacio Aramendía {
134ed264e8aSJoaquín Ignacio Aramendía 	switch (type) {
135ed264e8aSJoaquín Ignacio Aramendía 	case hwmon_fan:
136ed264e8aSJoaquín Ignacio Aramendía 		return 0444;
137ed264e8aSJoaquín Ignacio Aramendía 	case hwmon_pwm:
138ed264e8aSJoaquín Ignacio Aramendía 		return 0644;
139ed264e8aSJoaquín Ignacio Aramendía 	default:
140ed264e8aSJoaquín Ignacio Aramendía 		return 0;
141ed264e8aSJoaquín Ignacio Aramendía 	}
142ed264e8aSJoaquín Ignacio Aramendía }
143ed264e8aSJoaquín Ignacio Aramendía 
144ed264e8aSJoaquín Ignacio Aramendía static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
145ed264e8aSJoaquín Ignacio Aramendía 			     u32 attr, int channel, long *val)
146ed264e8aSJoaquín Ignacio Aramendía {
147ed264e8aSJoaquín Ignacio Aramendía 	int ret;
148ed264e8aSJoaquín Ignacio Aramendía 
149ed264e8aSJoaquín Ignacio Aramendía 	switch (type) {
150ed264e8aSJoaquín Ignacio Aramendía 	case hwmon_fan:
151ed264e8aSJoaquín Ignacio Aramendía 		switch (attr) {
152ed264e8aSJoaquín Ignacio Aramendía 		case hwmon_fan_input:
153ed264e8aSJoaquín Ignacio Aramendía 			return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);
154ed264e8aSJoaquín Ignacio Aramendía 		default:
155ed264e8aSJoaquín Ignacio Aramendía 			break;
156ed264e8aSJoaquín Ignacio Aramendía 		}
157ed264e8aSJoaquín Ignacio Aramendía 		break;
158ed264e8aSJoaquín Ignacio Aramendía 	case hwmon_pwm:
159ed264e8aSJoaquín Ignacio Aramendía 		switch (attr) {
160ed264e8aSJoaquín Ignacio Aramendía 		case hwmon_pwm_input:
161ed264e8aSJoaquín Ignacio Aramendía 			ret = read_from_ec(OXP_SENSOR_PWM_REG, 2, val);
162ed264e8aSJoaquín Ignacio Aramendía 			if (ret)
163ed264e8aSJoaquín Ignacio Aramendía 				return ret;
164*3ca0f12aSJoaquín Ignacio Aramendía 			if (board == oxp_mini_amd)
165ed264e8aSJoaquín Ignacio Aramendía 				*val = (*val * 255) / 100;
166ed264e8aSJoaquín Ignacio Aramendía 			return 0;
167ed264e8aSJoaquín Ignacio Aramendía 		case hwmon_pwm_enable:
168ed264e8aSJoaquín Ignacio Aramendía 			return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
169ed264e8aSJoaquín Ignacio Aramendía 		default:
170ed264e8aSJoaquín Ignacio Aramendía 			break;
171ed264e8aSJoaquín Ignacio Aramendía 		}
172ed264e8aSJoaquín Ignacio Aramendía 		break;
173ed264e8aSJoaquín Ignacio Aramendía 	default:
174ed264e8aSJoaquín Ignacio Aramendía 		break;
175ed264e8aSJoaquín Ignacio Aramendía 	}
176ed264e8aSJoaquín Ignacio Aramendía 	return -EOPNOTSUPP;
177ed264e8aSJoaquín Ignacio Aramendía }
178ed264e8aSJoaquín Ignacio Aramendía 
179ed264e8aSJoaquín Ignacio Aramendía static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type,
180ed264e8aSJoaquín Ignacio Aramendía 			      u32 attr, int channel, long val)
181ed264e8aSJoaquín Ignacio Aramendía {
182ed264e8aSJoaquín Ignacio Aramendía 	switch (type) {
183ed264e8aSJoaquín Ignacio Aramendía 	case hwmon_pwm:
184ed264e8aSJoaquín Ignacio Aramendía 		switch (attr) {
185ed264e8aSJoaquín Ignacio Aramendía 		case hwmon_pwm_enable:
186ed264e8aSJoaquín Ignacio Aramendía 			if (val == 1)
187ed264e8aSJoaquín Ignacio Aramendía 				return oxp_pwm_enable(dev);
188ed264e8aSJoaquín Ignacio Aramendía 			else if (val == 0)
189ed264e8aSJoaquín Ignacio Aramendía 				return oxp_pwm_disable(dev);
190ed264e8aSJoaquín Ignacio Aramendía 			return -EINVAL;
191ed264e8aSJoaquín Ignacio Aramendía 		case hwmon_pwm_input:
192ed264e8aSJoaquín Ignacio Aramendía 			if (val < 0 || val > 255)
193ed264e8aSJoaquín Ignacio Aramendía 				return -EINVAL;
194*3ca0f12aSJoaquín Ignacio Aramendía 			if (board == oxp_mini_amd)
195ed264e8aSJoaquín Ignacio Aramendía 				val = (val * 100) / 255;
196ed264e8aSJoaquín Ignacio Aramendía 			return write_to_ec(dev, OXP_SENSOR_PWM_REG, val);
197ed264e8aSJoaquín Ignacio Aramendía 		default:
198ed264e8aSJoaquín Ignacio Aramendía 			break;
199ed264e8aSJoaquín Ignacio Aramendía 		}
200ed264e8aSJoaquín Ignacio Aramendía 		break;
201ed264e8aSJoaquín Ignacio Aramendía 	default:
202ed264e8aSJoaquín Ignacio Aramendía 		break;
203ed264e8aSJoaquín Ignacio Aramendía 	}
204ed264e8aSJoaquín Ignacio Aramendía 	return -EOPNOTSUPP;
205ed264e8aSJoaquín Ignacio Aramendía }
206ed264e8aSJoaquín Ignacio Aramendía 
207ed264e8aSJoaquín Ignacio Aramendía /* Known sensors in the OXP EC controllers */
208ed264e8aSJoaquín Ignacio Aramendía static const struct hwmon_channel_info *oxp_platform_sensors[] = {
209ed264e8aSJoaquín Ignacio Aramendía 	HWMON_CHANNEL_INFO(fan,
210ed264e8aSJoaquín Ignacio Aramendía 			   HWMON_F_INPUT),
211ed264e8aSJoaquín Ignacio Aramendía 	HWMON_CHANNEL_INFO(pwm,
212ed264e8aSJoaquín Ignacio Aramendía 			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
213ed264e8aSJoaquín Ignacio Aramendía 	NULL,
214ed264e8aSJoaquín Ignacio Aramendía };
215ed264e8aSJoaquín Ignacio Aramendía 
216ed264e8aSJoaquín Ignacio Aramendía static const struct hwmon_ops oxp_ec_hwmon_ops = {
217ed264e8aSJoaquín Ignacio Aramendía 	.is_visible = oxp_ec_hwmon_is_visible,
218ed264e8aSJoaquín Ignacio Aramendía 	.read = oxp_platform_read,
219ed264e8aSJoaquín Ignacio Aramendía 	.write = oxp_platform_write,
220ed264e8aSJoaquín Ignacio Aramendía };
221ed264e8aSJoaquín Ignacio Aramendía 
222ed264e8aSJoaquín Ignacio Aramendía static const struct hwmon_chip_info oxp_ec_chip_info = {
223ed264e8aSJoaquín Ignacio Aramendía 	.ops = &oxp_ec_hwmon_ops,
224ed264e8aSJoaquín Ignacio Aramendía 	.info = oxp_platform_sensors,
225ed264e8aSJoaquín Ignacio Aramendía };
226ed264e8aSJoaquín Ignacio Aramendía 
227ed264e8aSJoaquín Ignacio Aramendía /* Initialization logic */
228ed264e8aSJoaquín Ignacio Aramendía static int oxp_platform_probe(struct platform_device *pdev)
229ed264e8aSJoaquín Ignacio Aramendía {
230ed264e8aSJoaquín Ignacio Aramendía 	const struct dmi_system_id *dmi_entry;
231ed264e8aSJoaquín Ignacio Aramendía 	struct device *dev = &pdev->dev;
232ed264e8aSJoaquín Ignacio Aramendía 	struct device *hwdev;
233ed264e8aSJoaquín Ignacio Aramendía 
234ed264e8aSJoaquín Ignacio Aramendía 	/*
235ed264e8aSJoaquín Ignacio Aramendía 	 * Have to check for AMD processor here because DMI strings are the
236ed264e8aSJoaquín Ignacio Aramendía 	 * same between Intel and AMD boards, the only way to tell them appart
237ed264e8aSJoaquín Ignacio Aramendía 	 * is the CPU.
238ed264e8aSJoaquín Ignacio Aramendía 	 * Intel boards seem to have different EC registers and values to
239ed264e8aSJoaquín Ignacio Aramendía 	 * read/write.
240ed264e8aSJoaquín Ignacio Aramendía 	 */
241ed264e8aSJoaquín Ignacio Aramendía 	dmi_entry = dmi_first_match(dmi_table);
242ed264e8aSJoaquín Ignacio Aramendía 	if (!dmi_entry || boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
243ed264e8aSJoaquín Ignacio Aramendía 		return -ENODEV;
244ed264e8aSJoaquín Ignacio Aramendía 
245*3ca0f12aSJoaquín Ignacio Aramendía 	board = *((enum oxp_board *) dmi_entry->driver_data);
246*3ca0f12aSJoaquín Ignacio Aramendía 
247ed264e8aSJoaquín Ignacio Aramendía 	hwdev = devm_hwmon_device_register_with_info(dev, "oxpec", NULL,
248ed264e8aSJoaquín Ignacio Aramendía 						     &oxp_ec_chip_info, NULL);
249ed264e8aSJoaquín Ignacio Aramendía 
250ed264e8aSJoaquín Ignacio Aramendía 	return PTR_ERR_OR_ZERO(hwdev);
251ed264e8aSJoaquín Ignacio Aramendía }
252ed264e8aSJoaquín Ignacio Aramendía 
253ed264e8aSJoaquín Ignacio Aramendía static struct platform_driver oxp_platform_driver = {
254ed264e8aSJoaquín Ignacio Aramendía 	.driver = {
255ed264e8aSJoaquín Ignacio Aramendía 		.name = "oxp-platform",
256ed264e8aSJoaquín Ignacio Aramendía 	},
257ed264e8aSJoaquín Ignacio Aramendía 	.probe = oxp_platform_probe,
258ed264e8aSJoaquín Ignacio Aramendía };
259ed264e8aSJoaquín Ignacio Aramendía 
260ed264e8aSJoaquín Ignacio Aramendía static struct platform_device *oxp_platform_device;
261ed264e8aSJoaquín Ignacio Aramendía 
262ed264e8aSJoaquín Ignacio Aramendía static int __init oxp_platform_init(void)
263ed264e8aSJoaquín Ignacio Aramendía {
264ed264e8aSJoaquín Ignacio Aramendía 	oxp_platform_device =
265ed264e8aSJoaquín Ignacio Aramendía 		platform_create_bundle(&oxp_platform_driver,
266ed264e8aSJoaquín Ignacio Aramendía 				       oxp_platform_probe, NULL, 0, NULL, 0);
267ed264e8aSJoaquín Ignacio Aramendía 
268ed264e8aSJoaquín Ignacio Aramendía 	return PTR_ERR_OR_ZERO(oxp_platform_device);
269ed264e8aSJoaquín Ignacio Aramendía }
270ed264e8aSJoaquín Ignacio Aramendía 
271ed264e8aSJoaquín Ignacio Aramendía static void __exit oxp_platform_exit(void)
272ed264e8aSJoaquín Ignacio Aramendía {
273ed264e8aSJoaquín Ignacio Aramendía 	platform_device_unregister(oxp_platform_device);
274ed264e8aSJoaquín Ignacio Aramendía 	platform_driver_unregister(&oxp_platform_driver);
275ed264e8aSJoaquín Ignacio Aramendía }
276ed264e8aSJoaquín Ignacio Aramendía 
277ed264e8aSJoaquín Ignacio Aramendía MODULE_DEVICE_TABLE(dmi, dmi_table);
278ed264e8aSJoaquín Ignacio Aramendía 
279ed264e8aSJoaquín Ignacio Aramendía module_init(oxp_platform_init);
280ed264e8aSJoaquín Ignacio Aramendía module_exit(oxp_platform_exit);
281ed264e8aSJoaquín Ignacio Aramendía 
282ed264e8aSJoaquín Ignacio Aramendía MODULE_AUTHOR("Joaquín Ignacio Aramendía <samsagax@gmail.com>");
283ed264e8aSJoaquín Ignacio Aramendía MODULE_DESCRIPTION("Platform driver that handles EC sensors of OneXPlayer devices");
284ed264e8aSJoaquín Ignacio Aramendía MODULE_LICENSE("GPL");
285