1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Linux driver for WMI sensor information on Dell notebooks.
4  *
5  * Copyright (C) 2022 Armin Wolf <W_Armin@gmx.de>
6  */
7 
8 #define pr_format(fmt) KBUILD_MODNAME ": " fmt
9 
10 #include <linux/acpi.h>
11 #include <linux/debugfs.h>
12 #include <linux/device.h>
13 #include <linux/dev_printk.h>
14 #include <linux/kernel.h>
15 #include <linux/kstrtox.h>
16 #include <linux/math.h>
17 #include <linux/module.h>
18 #include <linux/limits.h>
19 #include <linux/power_supply.h>
20 #include <linux/printk.h>
21 #include <linux/seq_file.h>
22 #include <linux/sysfs.h>
23 #include <linux/wmi.h>
24 
25 #include <acpi/battery.h>
26 
27 #define DRIVER_NAME	"dell-wmi-ddv"
28 
29 #define DELL_DDV_SUPPORTED_INTERFACE 2
30 #define DELL_DDV_GUID	"8A42EA14-4F2A-FD45-6422-0087F7A7E608"
31 
32 #define DELL_EPPID_LENGTH	20
33 #define DELL_EPPID_EXT_LENGTH	23
34 
35 enum dell_ddv_method {
36 	DELL_DDV_BATTERY_DESIGN_CAPACITY	= 0x01,
37 	DELL_DDV_BATTERY_FULL_CHARGE_CAPACITY	= 0x02,
38 	DELL_DDV_BATTERY_MANUFACTURE_NAME	= 0x03,
39 	DELL_DDV_BATTERY_MANUFACTURE_DATE	= 0x04,
40 	DELL_DDV_BATTERY_SERIAL_NUMBER		= 0x05,
41 	DELL_DDV_BATTERY_CHEMISTRY_VALUE	= 0x06,
42 	DELL_DDV_BATTERY_TEMPERATURE		= 0x07,
43 	DELL_DDV_BATTERY_CURRENT		= 0x08,
44 	DELL_DDV_BATTERY_VOLTAGE		= 0x09,
45 	DELL_DDV_BATTERY_MANUFACTURER_ACCESS	= 0x0A,
46 	DELL_DDV_BATTERY_RELATIVE_CHARGE_STATE	= 0x0B,
47 	DELL_DDV_BATTERY_CYCLE_COUNT		= 0x0C,
48 	DELL_DDV_BATTERY_EPPID			= 0x0D,
49 	DELL_DDV_BATTERY_RAW_ANALYTICS_START	= 0x0E,
50 	DELL_DDV_BATTERY_RAW_ANALYTICS		= 0x0F,
51 	DELL_DDV_BATTERY_DESIGN_VOLTAGE		= 0x10,
52 
53 	DELL_DDV_INTERFACE_VERSION		= 0x12,
54 
55 	DELL_DDV_FAN_SENSOR_INFORMATION		= 0x20,
56 	DELL_DDV_THERMAL_SENSOR_INFORMATION	= 0x22,
57 };
58 
59 struct dell_wmi_ddv_data {
60 	struct acpi_battery_hook hook;
61 	struct device_attribute temp_attr;
62 	struct device_attribute eppid_attr;
63 	struct wmi_device *wdev;
64 };
65 
66 static int dell_wmi_ddv_query_type(struct wmi_device *wdev, enum dell_ddv_method method, u32 arg,
67 				   union acpi_object **result, acpi_object_type type)
68 {
69 	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
70 	const struct acpi_buffer in = {
71 		.length = sizeof(arg),
72 		.pointer = &arg,
73 	};
74 	union acpi_object *obj;
75 	acpi_status ret;
76 
77 	ret = wmidev_evaluate_method(wdev, 0x0, method, &in, &out);
78 	if (ACPI_FAILURE(ret))
79 		return -EIO;
80 
81 	obj = out.pointer;
82 	if (!obj)
83 		return -ENODATA;
84 
85 	if (obj->type != type) {
86 		kfree(obj);
87 		return -EIO;
88 	}
89 
90 	*result = obj;
91 
92 	return 0;
93 }
94 
95 static int dell_wmi_ddv_query_integer(struct wmi_device *wdev, enum dell_ddv_method method,
96 				      u32 arg, u32 *res)
97 {
98 	union acpi_object *obj;
99 	int ret;
100 
101 	ret = dell_wmi_ddv_query_type(wdev, method, arg, &obj, ACPI_TYPE_INTEGER);
102 	if (ret < 0)
103 		return ret;
104 
105 	if (obj->integer.value <= U32_MAX)
106 		*res = (u32)obj->integer.value;
107 	else
108 		ret = -ERANGE;
109 
110 	kfree(obj);
111 
112 	return ret;
113 }
114 
115 static int dell_wmi_ddv_query_buffer(struct wmi_device *wdev, enum dell_ddv_method method,
116 				     u32 arg, union acpi_object **result)
117 {
118 	union acpi_object *obj;
119 	u64 buffer_size;
120 	int ret;
121 
122 	ret = dell_wmi_ddv_query_type(wdev, method, arg, &obj, ACPI_TYPE_PACKAGE);
123 	if (ret < 0)
124 		return ret;
125 
126 	if (obj->package.count != 2)
127 		goto err_free;
128 
129 	if (obj->package.elements[0].type != ACPI_TYPE_INTEGER)
130 		goto err_free;
131 
132 	buffer_size = obj->package.elements[0].integer.value;
133 
134 	if (obj->package.elements[1].type != ACPI_TYPE_BUFFER)
135 		goto err_free;
136 
137 	if (buffer_size > obj->package.elements[1].buffer.length) {
138 		dev_warn(&wdev->dev,
139 			 FW_WARN "WMI buffer size (%llu) exceeds ACPI buffer size (%d)\n",
140 			 buffer_size, obj->package.elements[1].buffer.length);
141 
142 		goto err_free;
143 	}
144 
145 	*result = obj;
146 
147 	return 0;
148 
149 err_free:
150 	kfree(obj);
151 
152 	return -EIO;
153 }
154 
155 static int dell_wmi_ddv_query_string(struct wmi_device *wdev, enum dell_ddv_method method,
156 				     u32 arg, union acpi_object **result)
157 {
158 	return dell_wmi_ddv_query_type(wdev, method, arg, result, ACPI_TYPE_STRING);
159 }
160 
161 static int dell_wmi_ddv_battery_index(struct acpi_device *acpi_dev, u32 *index)
162 {
163 	const char *uid_str;
164 
165 	uid_str = acpi_device_uid(acpi_dev);
166 	if (!uid_str)
167 		return -ENODEV;
168 
169 	return kstrtou32(uid_str, 10, index);
170 }
171 
172 static ssize_t temp_show(struct device *dev, struct device_attribute *attr, char *buf)
173 {
174 	struct dell_wmi_ddv_data *data = container_of(attr, struct dell_wmi_ddv_data, temp_attr);
175 	u32 index, value;
176 	int ret;
177 
178 	ret = dell_wmi_ddv_battery_index(to_acpi_device(dev->parent), &index);
179 	if (ret < 0)
180 		return ret;
181 
182 	ret = dell_wmi_ddv_query_integer(data->wdev, DELL_DDV_BATTERY_TEMPERATURE, index, &value);
183 	if (ret < 0)
184 		return ret;
185 
186 	return sysfs_emit(buf, "%d\n", DIV_ROUND_CLOSEST(value, 10));
187 }
188 
189 static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, char *buf)
190 {
191 	struct dell_wmi_ddv_data *data = container_of(attr, struct dell_wmi_ddv_data, eppid_attr);
192 	union acpi_object *obj;
193 	u32 index;
194 	int ret;
195 
196 	ret = dell_wmi_ddv_battery_index(to_acpi_device(dev->parent), &index);
197 	if (ret < 0)
198 		return ret;
199 
200 	ret = dell_wmi_ddv_query_string(data->wdev, DELL_DDV_BATTERY_EPPID, index, &obj);
201 	if (ret < 0)
202 		return ret;
203 
204 	if (obj->string.length != DELL_EPPID_LENGTH && obj->string.length != DELL_EPPID_EXT_LENGTH)
205 		dev_info_once(&data->wdev->dev, FW_INFO "Suspicious ePPID length (%d)\n",
206 			      obj->string.length);
207 
208 	ret = sysfs_emit(buf, "%s\n", obj->string.pointer);
209 
210 	kfree(obj);
211 
212 	return ret;
213 }
214 
215 static int dell_wmi_ddv_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
216 {
217 	struct dell_wmi_ddv_data *data = container_of(hook, struct dell_wmi_ddv_data, hook);
218 	u32 index;
219 	int ret;
220 
221 	/* Return 0 instead of error to avoid being unloaded */
222 	ret = dell_wmi_ddv_battery_index(to_acpi_device(battery->dev.parent), &index);
223 	if (ret < 0)
224 		return 0;
225 
226 	ret = device_create_file(&battery->dev, &data->temp_attr);
227 	if (ret < 0)
228 		return ret;
229 
230 	ret = device_create_file(&battery->dev, &data->eppid_attr);
231 	if (ret < 0) {
232 		device_remove_file(&battery->dev, &data->temp_attr);
233 
234 		return ret;
235 	}
236 
237 	return 0;
238 }
239 
240 static int dell_wmi_ddv_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
241 {
242 	struct dell_wmi_ddv_data *data = container_of(hook, struct dell_wmi_ddv_data, hook);
243 
244 	device_remove_file(&battery->dev, &data->temp_attr);
245 	device_remove_file(&battery->dev, &data->eppid_attr);
246 
247 	return 0;
248 }
249 
250 static void dell_wmi_ddv_battery_remove(void *data)
251 {
252 	struct acpi_battery_hook *hook = data;
253 
254 	battery_hook_unregister(hook);
255 }
256 
257 static int dell_wmi_ddv_battery_add(struct dell_wmi_ddv_data *data)
258 {
259 	data->hook.name = "Dell DDV Battery Extension";
260 	data->hook.add_battery = dell_wmi_ddv_add_battery;
261 	data->hook.remove_battery = dell_wmi_ddv_remove_battery;
262 
263 	sysfs_attr_init(&data->temp_attr.attr);
264 	data->temp_attr.attr.name = "temp";
265 	data->temp_attr.attr.mode = 0444;
266 	data->temp_attr.show = temp_show;
267 
268 	sysfs_attr_init(&data->eppid_attr.attr);
269 	data->eppid_attr.attr.name = "eppid";
270 	data->eppid_attr.attr.mode = 0444;
271 	data->eppid_attr.show = eppid_show;
272 
273 	battery_hook_register(&data->hook);
274 
275 	return devm_add_action_or_reset(&data->wdev->dev, dell_wmi_ddv_battery_remove, &data->hook);
276 }
277 
278 static int dell_wmi_ddv_buffer_read(struct seq_file *seq, enum dell_ddv_method method)
279 {
280 	struct device *dev = seq->private;
281 	struct dell_wmi_ddv_data *data = dev_get_drvdata(dev);
282 	union acpi_object *obj;
283 	u64 size;
284 	u8 *buf;
285 	int ret;
286 
287 	ret = dell_wmi_ddv_query_buffer(data->wdev, method, 0, &obj);
288 	if (ret < 0)
289 		return ret;
290 
291 	size = obj->package.elements[0].integer.value;
292 	buf = obj->package.elements[1].buffer.pointer;
293 	ret = seq_write(seq, buf, size);
294 	kfree(obj);
295 
296 	return ret;
297 }
298 
299 static int dell_wmi_ddv_fan_read(struct seq_file *seq, void *offset)
300 {
301 	return dell_wmi_ddv_buffer_read(seq, DELL_DDV_FAN_SENSOR_INFORMATION);
302 }
303 
304 static int dell_wmi_ddv_temp_read(struct seq_file *seq, void *offset)
305 {
306 	return dell_wmi_ddv_buffer_read(seq, DELL_DDV_THERMAL_SENSOR_INFORMATION);
307 }
308 
309 static void dell_wmi_ddv_debugfs_remove(void *data)
310 {
311 	struct dentry *entry = data;
312 
313 	debugfs_remove(entry);
314 }
315 
316 static void dell_wmi_ddv_debugfs_init(struct wmi_device *wdev)
317 {
318 	struct dentry *entry;
319 	char name[64];
320 
321 	scnprintf(name, ARRAY_SIZE(name), "%s-%s", DRIVER_NAME, dev_name(&wdev->dev));
322 	entry = debugfs_create_dir(name, NULL);
323 
324 	debugfs_create_devm_seqfile(&wdev->dev, "fan_sensor_information", entry,
325 				    dell_wmi_ddv_fan_read);
326 	debugfs_create_devm_seqfile(&wdev->dev, "thermal_sensor_information", entry,
327 				    dell_wmi_ddv_temp_read);
328 
329 	devm_add_action_or_reset(&wdev->dev, dell_wmi_ddv_debugfs_remove, entry);
330 }
331 
332 static int dell_wmi_ddv_probe(struct wmi_device *wdev, const void *context)
333 {
334 	struct dell_wmi_ddv_data *data;
335 	u32 version;
336 	int ret;
337 
338 	ret = dell_wmi_ddv_query_integer(wdev, DELL_DDV_INTERFACE_VERSION, 0, &version);
339 	if (ret < 0)
340 		return ret;
341 
342 	dev_dbg(&wdev->dev, "WMI interface version: %d\n", version);
343 	if (version != DELL_DDV_SUPPORTED_INTERFACE)
344 		return -ENODEV;
345 
346 	data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL);
347 	if (!data)
348 		return -ENOMEM;
349 
350 	dev_set_drvdata(&wdev->dev, data);
351 	data->wdev = wdev;
352 
353 	dell_wmi_ddv_debugfs_init(wdev);
354 
355 	return dell_wmi_ddv_battery_add(data);
356 }
357 
358 static const struct wmi_device_id dell_wmi_ddv_id_table[] = {
359 	{ DELL_DDV_GUID, NULL },
360 	{ }
361 };
362 MODULE_DEVICE_TABLE(wmi, dell_wmi_ddv_id_table);
363 
364 static struct wmi_driver dell_wmi_ddv_driver = {
365 	.driver = {
366 		.name = DRIVER_NAME,
367 	},
368 	.id_table = dell_wmi_ddv_id_table,
369 	.probe = dell_wmi_ddv_probe,
370 };
371 module_wmi_driver(dell_wmi_ddv_driver);
372 
373 MODULE_AUTHOR("Armin Wolf <W_Armin@gmx.de>");
374 MODULE_DESCRIPTION("Dell WMI sensor driver");
375 MODULE_LICENSE("GPL");
376