xref: /openbmc/linux/drivers/hwmon/oxp-sensors.c (revision 7d0c2c61b1a4d1cf5641b35b491fe58ffafe26bc)
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/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 */
29 static u32 oxp_mutex;
30 
31 #define ACPI_LOCK_DELAY_MS	500
32 
33 static bool lock_global_acpi_lock(void)
34 {
35 	return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS, &oxp_mutex));
36 }
37 
38 static bool unlock_global_acpi_lock(void)
39 {
40 	return ACPI_SUCCESS(acpi_release_global_lock(oxp_mutex));
41 }
42 
43 enum oxp_board {
44 	aok_zoe_a1 = 1,
45 	aya_neo_2,
46 	aya_neo_air,
47 	aya_neo_air_pro,
48 	aya_neo_geek,
49 	oxp_mini_amd,
50 	oxp_mini_amd_pro,
51 };
52 
53 static enum oxp_board board;
54 
55 #define OXP_SENSOR_FAN_REG		0x76 /* Fan reading is 2 registers long */
56 #define OXP_SENSOR_PWM_ENABLE_REG	0x4A /* PWM enable is 1 register long */
57 #define OXP_SENSOR_PWM_REG		0x4B /* PWM reading is 1 register long */
58 
59 static const struct dmi_system_id dmi_table[] = {
60 	{
61 		.matches = {
62 			DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
63 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 AR07"),
64 		},
65 		.driver_data = (void *)aok_zoe_a1,
66 	},
67 	{
68 		.matches = {
69 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
70 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AYANEO 2"),
71 		},
72 		.driver_data = (void *)aya_neo_2,
73 	},
74 	{
75 		.matches = {
76 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
77 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"),
78 		},
79 		.driver_data = (void *)aya_neo_air,
80 	},
81 	{
82 		.matches = {
83 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
84 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"),
85 		},
86 		.driver_data = (void *)aya_neo_air_pro,
87 	},
88 	{
89 		.matches = {
90 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
91 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "GEEK"),
92 		},
93 		.driver_data = (void *)aya_neo_geek,
94 	},
95 	{
96 		.matches = {
97 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
98 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONE XPLAYER"),
99 		},
100 		.driver_data = (void *)oxp_mini_amd,
101 	},
102 	{
103 		.matches = {
104 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
105 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER mini A07"),
106 		},
107 		.driver_data = (void *)oxp_mini_amd,
108 	},
109 	{
110 		.matches = {
111 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
112 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER Mini Pro"),
113 		},
114 		.driver_data = (void *)oxp_mini_amd_pro,
115 	},
116 	{},
117 };
118 
119 /* Helper functions to handle EC read/write */
120 static int read_from_ec(u8 reg, int size, long *val)
121 {
122 	int i;
123 	int ret;
124 	u8 buffer;
125 
126 	if (!lock_global_acpi_lock())
127 		return -EBUSY;
128 
129 	*val = 0;
130 	for (i = 0; i < size; i++) {
131 		ret = ec_read(reg + i, &buffer);
132 		if (ret)
133 			return ret;
134 		*val <<= i * 8;
135 		*val += buffer;
136 	}
137 
138 	if (!unlock_global_acpi_lock())
139 		return -EBUSY;
140 
141 	return 0;
142 }
143 
144 static int write_to_ec(const struct device *dev, u8 reg, u8 value)
145 {
146 	int ret;
147 
148 	if (!lock_global_acpi_lock())
149 		return -EBUSY;
150 
151 	ret = ec_write(reg, value);
152 
153 	if (!unlock_global_acpi_lock())
154 		return -EBUSY;
155 
156 	return ret;
157 }
158 
159 static int oxp_pwm_enable(const struct device *dev)
160 {
161 	return write_to_ec(dev, OXP_SENSOR_PWM_ENABLE_REG, 0x01);
162 }
163 
164 static int oxp_pwm_disable(const struct device *dev)
165 {
166 	return write_to_ec(dev, OXP_SENSOR_PWM_ENABLE_REG, 0x00);
167 }
168 
169 /* Callbacks for hwmon interface */
170 static umode_t oxp_ec_hwmon_is_visible(const void *drvdata,
171 				       enum hwmon_sensor_types type, u32 attr, int channel)
172 {
173 	switch (type) {
174 	case hwmon_fan:
175 		return 0444;
176 	case hwmon_pwm:
177 		return 0644;
178 	default:
179 		return 0;
180 	}
181 }
182 
183 static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
184 			     u32 attr, int channel, long *val)
185 {
186 	int ret;
187 
188 	switch (type) {
189 	case hwmon_fan:
190 		switch (attr) {
191 		case hwmon_fan_input:
192 			return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);
193 		default:
194 			break;
195 		}
196 		break;
197 	case hwmon_pwm:
198 		switch (attr) {
199 		case hwmon_pwm_input:
200 			ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
201 			if (ret)
202 				return ret;
203 			switch (board) {
204 			case aya_neo_2:
205 			case aya_neo_air:
206 			case aya_neo_air_pro:
207 			case aya_neo_geek:
208 			case oxp_mini_amd:
209 				*val = (*val * 255) / 100;
210 				break;
211 			case oxp_mini_amd_pro:
212 			case aok_zoe_a1:
213 			default:
214 				break;
215 			}
216 			return 0;
217 		case hwmon_pwm_enable:
218 			return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
219 		default:
220 			break;
221 		}
222 		break;
223 	default:
224 		break;
225 	}
226 	return -EOPNOTSUPP;
227 }
228 
229 static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type,
230 			      u32 attr, int channel, long val)
231 {
232 	switch (type) {
233 	case hwmon_pwm:
234 		switch (attr) {
235 		case hwmon_pwm_enable:
236 			if (val == 1)
237 				return oxp_pwm_enable(dev);
238 			else if (val == 0)
239 				return oxp_pwm_disable(dev);
240 			return -EINVAL;
241 		case hwmon_pwm_input:
242 			if (val < 0 || val > 255)
243 				return -EINVAL;
244 			switch (board) {
245 			case aya_neo_2:
246 			case aya_neo_air:
247 			case aya_neo_air_pro:
248 			case aya_neo_geek:
249 			case oxp_mini_amd:
250 				val = (val * 100) / 255;
251 				break;
252 			case aok_zoe_a1:
253 			case oxp_mini_amd_pro:
254 			default:
255 				break;
256 			}
257 			return write_to_ec(dev, OXP_SENSOR_PWM_REG, val);
258 		default:
259 			break;
260 		}
261 		break;
262 	default:
263 		break;
264 	}
265 	return -EOPNOTSUPP;
266 }
267 
268 /* Known sensors in the OXP EC controllers */
269 static const struct hwmon_channel_info * const oxp_platform_sensors[] = {
270 	HWMON_CHANNEL_INFO(fan,
271 			   HWMON_F_INPUT),
272 	HWMON_CHANNEL_INFO(pwm,
273 			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
274 	NULL,
275 };
276 
277 static const struct hwmon_ops oxp_ec_hwmon_ops = {
278 	.is_visible = oxp_ec_hwmon_is_visible,
279 	.read = oxp_platform_read,
280 	.write = oxp_platform_write,
281 };
282 
283 static const struct hwmon_chip_info oxp_ec_chip_info = {
284 	.ops = &oxp_ec_hwmon_ops,
285 	.info = oxp_platform_sensors,
286 };
287 
288 /* Initialization logic */
289 static int oxp_platform_probe(struct platform_device *pdev)
290 {
291 	const struct dmi_system_id *dmi_entry;
292 	struct device *dev = &pdev->dev;
293 	struct device *hwdev;
294 
295 	/*
296 	 * Have to check for AMD processor here because DMI strings are the
297 	 * same between Intel and AMD boards, the only way to tell them apart
298 	 * is the CPU.
299 	 * Intel boards seem to have different EC registers and values to
300 	 * read/write.
301 	 */
302 	dmi_entry = dmi_first_match(dmi_table);
303 	if (!dmi_entry || boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
304 		return -ENODEV;
305 
306 	board = (enum oxp_board)(unsigned long)dmi_entry->driver_data;
307 
308 	hwdev = devm_hwmon_device_register_with_info(dev, "oxpec", NULL,
309 						     &oxp_ec_chip_info, NULL);
310 
311 	return PTR_ERR_OR_ZERO(hwdev);
312 }
313 
314 static struct platform_driver oxp_platform_driver = {
315 	.driver = {
316 		.name = "oxp-platform",
317 	},
318 	.probe = oxp_platform_probe,
319 };
320 
321 static struct platform_device *oxp_platform_device;
322 
323 static int __init oxp_platform_init(void)
324 {
325 	oxp_platform_device =
326 		platform_create_bundle(&oxp_platform_driver,
327 				       oxp_platform_probe, NULL, 0, NULL, 0);
328 
329 	return PTR_ERR_OR_ZERO(oxp_platform_device);
330 }
331 
332 static void __exit oxp_platform_exit(void)
333 {
334 	platform_device_unregister(oxp_platform_device);
335 	platform_driver_unregister(&oxp_platform_driver);
336 }
337 
338 MODULE_DEVICE_TABLE(dmi, dmi_table);
339 
340 module_init(oxp_platform_init);
341 module_exit(oxp_platform_exit);
342 
343 MODULE_AUTHOR("Joaquín Ignacio Aramendía <samsagax@gmail.com>");
344 MODULE_DESCRIPTION("Platform driver that handles EC sensors of OneXPlayer devices");
345 MODULE_LICENSE("GPL");
346