1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Lenovo Capability Data WMI Data Block driver.
4 *
5 * Lenovo Capability Data provides information on tunable attributes used by
6 * the "Other Mode" WMI interface.
7 *
8 * Capability Data 00 includes if the attribute is supported by the hardware,
9 * and the default_value. All attributes are independent of thermal modes.
10 *
11 * Capability Data 01 includes if the attribute is supported by the hardware,
12 * and the default_value, max_value, min_value, and step increment. Each
13 * attribute has multiple pages, one for each of the thermal modes managed by
14 * the Gamezone interface.
15 *
16 * Fan Test Data includes the max/min fan speed RPM for each fan. This is
17 * reference data for self-test. If the fan is in good condition, it is capable
18 * to spin faster than max RPM or slower than min RPM.
19 *
20 * Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com>
21 * - Initial implementation (formerly named lenovo-wmi-capdata01)
22 *
23 * Copyright (C) 2025 Rong Zhang <i@rong.moe>
24 * - Unified implementation
25 */
26
27 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
28
29 #include <linux/acpi.h>
30 #include <linux/bug.h>
31 #include <linux/cleanup.h>
32 #include <linux/component.h>
33 #include <linux/container_of.h>
34 #include <linux/device.h>
35 #include <linux/dev_printk.h>
36 #include <linux/err.h>
37 #include <linux/export.h>
38 #include <linux/gfp_types.h>
39 #include <linux/limits.h>
40 #include <linux/module.h>
41 #include <linux/mutex.h>
42 #include <linux/mutex_types.h>
43 #include <linux/notifier.h>
44 #include <linux/overflow.h>
45 #include <linux/stddef.h>
46 #include <linux/types.h>
47 #include <linux/wmi.h>
48
49 #include "wmi-capdata.h"
50 #include "wmi-helpers.h"
51
52 #define LENOVO_CAPABILITY_DATA_00_GUID "362A3AFE-3D96-4665-8530-96DAD5BB300E"
53 #define LENOVO_CAPABILITY_DATA_01_GUID "7A8F5407-CB67-4D6E-B547-39B3BE018154"
54 #define LENOVO_FAN_TEST_DATA_GUID "B642801B-3D21-45DE-90AE-6E86F164FB21"
55
56 #define ACPI_AC_NOTIFY_STATUS 0x80
57
58 #define LWMI_FEATURE_ID_FAN_TEST 0x05
59
60 #define LWMI_ATTR_ID_FAN_TEST \
61 lwmi_attr_id(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_TEST, \
62 LWMI_GZ_THERMAL_MODE_NONE, LWMI_TYPE_ID_NONE)
63
64 enum lwmi_cd_type {
65 LENOVO_CAPABILITY_DATA_00,
66 LENOVO_CAPABILITY_DATA_01,
67 LENOVO_FAN_TEST_DATA,
68 CD_TYPE_NONE = -1,
69 };
70
71 #define LWMI_CD_TABLE_ITEM(_type) \
72 [_type] = { \
73 .name = #_type, \
74 .type = _type, \
75 }
76
77 static const struct lwmi_cd_info {
78 const char *name;
79 enum lwmi_cd_type type;
80 } lwmi_cd_table[] = {
81 LWMI_CD_TABLE_ITEM(LENOVO_CAPABILITY_DATA_00),
82 LWMI_CD_TABLE_ITEM(LENOVO_CAPABILITY_DATA_01),
83 LWMI_CD_TABLE_ITEM(LENOVO_FAN_TEST_DATA),
84 };
85
86 struct lwmi_cd_priv {
87 struct notifier_block acpi_nb; /* ACPI events */
88 struct wmi_device *wdev;
89 struct cd_list *list;
90
91 /*
92 * A capdata device may be a component master of another capdata device.
93 * E.g., lenovo-wmi-other <-> capdata00 <-> capdata_fan
94 * |- master |- component
95 * |- sub-master
96 * |- sub-component
97 */
98 struct lwmi_cd_sub_master_priv {
99 struct device *master_dev;
100 cd_list_cb_t master_cb;
101 struct cd_list *sub_component_list; /* ERR_PTR(-ENODEV) implies no sub-component. */
102 bool registered; /* Has the sub-master been registered? */
103 } *sub_master;
104 };
105
106 struct cd_list {
107 struct mutex list_mutex; /* list R/W mutex */
108 enum lwmi_cd_type type;
109 u8 count;
110
111 union {
112 DECLARE_FLEX_ARRAY(struct capdata00, cd00);
113 DECLARE_FLEX_ARRAY(struct capdata01, cd01);
114 DECLARE_FLEX_ARRAY(struct capdata_fan, cd_fan);
115 };
116 };
117
118 static struct wmi_driver lwmi_cd_driver;
119
120 /**
121 * lwmi_cd_match() - Match rule for the master driver.
122 * @dev: Pointer to the capability data parent device.
123 * @type: Pointer to capability data type (enum lwmi_cd_type *) to match.
124 *
125 * Return: int.
126 */
lwmi_cd_match(struct device * dev,void * type)127 static int lwmi_cd_match(struct device *dev, void *type)
128 {
129 struct lwmi_cd_priv *priv;
130
131 if (dev->driver != &lwmi_cd_driver.driver)
132 return false;
133
134 priv = dev_get_drvdata(dev);
135 return priv->list->type == *(enum lwmi_cd_type *)type;
136 }
137
138 /**
139 * lwmi_cd_match_add_all() - Add all match rule for the master driver.
140 * @master: Pointer to the master device.
141 * @matchptr: Pointer to the returned component_match pointer.
142 *
143 * Adds all component matches to the list stored in @matchptr for the @master
144 * device. @matchptr must be initialized to NULL.
145 */
lwmi_cd_match_add_all(struct device * master,struct component_match ** matchptr)146 void lwmi_cd_match_add_all(struct device *master, struct component_match **matchptr)
147 {
148 int i;
149
150 if (WARN_ON(*matchptr))
151 return;
152
153 for (i = 0; i < ARRAY_SIZE(lwmi_cd_table); i++) {
154 /* Skip sub-components. */
155 if (lwmi_cd_table[i].type == LENOVO_FAN_TEST_DATA)
156 continue;
157
158 component_match_add(master, matchptr, lwmi_cd_match,
159 (void *)&lwmi_cd_table[i].type);
160 if (IS_ERR(*matchptr))
161 return;
162 }
163 }
164 EXPORT_SYMBOL_NS_GPL(lwmi_cd_match_add_all, "LENOVO_WMI_CAPDATA");
165
166 /**
167 * lwmi_cd_call_master_cb() - Call the master callback for the sub-component.
168 * @priv: Pointer to the capability data private data.
169 *
170 * Call the master callback and pass the sub-component list to it if the
171 * dependency chain (master <-> sub-master <-> sub-component) is complete.
172 */
lwmi_cd_call_master_cb(struct lwmi_cd_priv * priv)173 static void lwmi_cd_call_master_cb(struct lwmi_cd_priv *priv)
174 {
175 struct cd_list *sub_component_list = priv->sub_master->sub_component_list;
176
177 /*
178 * Call the callback only if the dependency chain is ready:
179 * - Binding between master and sub-master: fills master_dev and master_cb
180 * - Binding between sub-master and sub-component: fills sub_component_list
181 *
182 * If a binding has been unbound before the other binding is bound, the
183 * corresponding members filled by the former are guaranteed to be cleared.
184 *
185 * This function is only called in bind callbacks, and the component
186 * framework guarantees bind/unbind callbacks may never execute
187 * simultaneously, which implies that it's impossible to have a race
188 * condition.
189 *
190 * Hence, this check is sufficient to ensure that the callback is called
191 * at most once and with the correct state, without relying on a specific
192 * sequence of binding establishment.
193 */
194 if (!sub_component_list ||
195 !priv->sub_master->master_dev ||
196 !priv->sub_master->master_cb)
197 return;
198
199 if (PTR_ERR(sub_component_list) == -ENODEV)
200 sub_component_list = NULL;
201 else if (WARN_ON(IS_ERR(sub_component_list)))
202 return;
203
204 priv->sub_master->master_cb(priv->sub_master->master_dev,
205 sub_component_list);
206
207 /*
208 * Userspace may unbind a device from its driver and bind it again
209 * through sysfs. Let's call this operation "reprobe" to distinguish it
210 * from component "rebind".
211 *
212 * When reprobing capdata00/01 or the master device, the master device
213 * is unbound from us with appropriate cleanup before we bind to it and
214 * call master_cb. Everything is fine in this case.
215 *
216 * When reprobing capdata_fan, the master device has never been unbound
217 * from us (hence no cleanup is done)[1], but we call master_cb the
218 * second time. To solve this issue, we clear master_cb and master_dev
219 * so we won't call master_cb twice while a binding is still complete.
220 *
221 * Note that we can't clear sub_component_list, otherwise reprobing
222 * capdata01 or the master device causes master_cb to be never called
223 * after we rebind to the master device.
224 *
225 * [1]: The master device does not need capdata_fan in run time, so
226 * losing capdata_fan will not break the binding to the master device.
227 */
228 priv->sub_master->master_cb = NULL;
229 priv->sub_master->master_dev = NULL;
230 }
231
232 /**
233 * lwmi_cd_component_bind() - Bind component to master device.
234 * @cd_dev: Pointer to the lenovo-wmi-capdata driver parent device.
235 * @om_dev: Pointer to the lenovo-wmi-other driver parent device.
236 * @data: lwmi_cd_binder object pointer used to return the capability data.
237 *
238 * On lenovo-wmi-other's master bind, provide a pointer to the local capdata
239 * list. This is used to call lwmi_cd*_get_data to look up attribute data
240 * from the lenovo-wmi-other driver.
241 *
242 * If cd_dev is a sub-master, try to call the master callback.
243 *
244 * Return: 0
245 */
lwmi_cd_component_bind(struct device * cd_dev,struct device * om_dev,void * data)246 static int lwmi_cd_component_bind(struct device *cd_dev,
247 struct device *om_dev, void *data)
248 {
249 struct lwmi_cd_priv *priv = dev_get_drvdata(cd_dev);
250 struct lwmi_cd_binder *binder = data;
251
252 switch (priv->list->type) {
253 case LENOVO_CAPABILITY_DATA_00:
254 binder->cd00_list = priv->list;
255
256 priv->sub_master->master_dev = om_dev;
257 priv->sub_master->master_cb = binder->cd_fan_list_cb;
258 lwmi_cd_call_master_cb(priv);
259
260 break;
261 case LENOVO_CAPABILITY_DATA_01:
262 binder->cd01_list = priv->list;
263 break;
264 default:
265 return -EINVAL;
266 }
267
268 return 0;
269 }
270
271 /**
272 * lwmi_cd_component_unbind() - Unbind component to master device.
273 * @cd_dev: Pointer to the lenovo-wmi-capdata driver parent device.
274 * @om_dev: Pointer to the lenovo-wmi-other driver parent device.
275 * @data: Unused.
276 *
277 * If cd_dev is a sub-master, clear the collected data from the master device to
278 * prevent the binding establishment between the sub-master and the sub-
279 * component (if it's about to happen) from calling the master callback.
280 */
lwmi_cd_component_unbind(struct device * cd_dev,struct device * om_dev,void * data)281 static void lwmi_cd_component_unbind(struct device *cd_dev,
282 struct device *om_dev, void *data)
283 {
284 struct lwmi_cd_priv *priv = dev_get_drvdata(cd_dev);
285
286 switch (priv->list->type) {
287 case LENOVO_CAPABILITY_DATA_00:
288 priv->sub_master->master_dev = NULL;
289 priv->sub_master->master_cb = NULL;
290 return;
291 default:
292 return;
293 }
294 }
295
296 static const struct component_ops lwmi_cd_component_ops = {
297 .bind = lwmi_cd_component_bind,
298 .unbind = lwmi_cd_component_unbind,
299 };
300
301 /**
302 * lwmi_cd_sub_master_bind() - Bind sub-component of sub-master device
303 * @dev: The sub-master capdata basic device.
304 *
305 * Call component_bind_all to bind the sub-component device to the sub-master
306 * device. On success, collect the pointer to the sub-component list and try
307 * to call the master callback.
308 *
309 * Return: 0 on success, or an error code.
310 */
lwmi_cd_sub_master_bind(struct device * dev)311 static int lwmi_cd_sub_master_bind(struct device *dev)
312 {
313 struct lwmi_cd_priv *priv = dev_get_drvdata(dev);
314 struct cd_list *sub_component_list;
315 int ret;
316
317 ret = component_bind_all(dev, &sub_component_list);
318 if (ret)
319 return ret;
320
321 priv->sub_master->sub_component_list = sub_component_list;
322 lwmi_cd_call_master_cb(priv);
323
324 return 0;
325 }
326
327 /**
328 * lwmi_cd_sub_master_unbind() - Unbind sub-component of sub-master device
329 * @dev: The sub-master capdata basic device
330 *
331 * Clear the collected pointer to the sub-component list to prevent the binding
332 * establishment between the sub-master and the sub-component (if it's about to
333 * happen) from calling the master callback. Then, call component_unbind_all to
334 * unbind the sub-component device from the sub-master device.
335 */
lwmi_cd_sub_master_unbind(struct device * dev)336 static void lwmi_cd_sub_master_unbind(struct device *dev)
337 {
338 struct lwmi_cd_priv *priv = dev_get_drvdata(dev);
339
340 priv->sub_master->sub_component_list = NULL;
341
342 component_unbind_all(dev, NULL);
343 }
344
345 static const struct component_master_ops lwmi_cd_sub_master_ops = {
346 .bind = lwmi_cd_sub_master_bind,
347 .unbind = lwmi_cd_sub_master_unbind,
348 };
349
350 /**
351 * lwmi_cd_sub_master_add() - Register a sub-master with its sub-component
352 * @priv: Pointer to the sub-master capdata device private data.
353 * @sub_component_type: Type of the sub-component.
354 *
355 * Match the sub-component type and register the current capdata device as a
356 * sub-master. If the given sub-component type is CD_TYPE_NONE, mark the sub-
357 * component as non-existent without registering sub-master.
358 *
359 * Return: 0 on success, or an error code.
360 */
lwmi_cd_sub_master_add(struct lwmi_cd_priv * priv,enum lwmi_cd_type sub_component_type)361 static int lwmi_cd_sub_master_add(struct lwmi_cd_priv *priv,
362 enum lwmi_cd_type sub_component_type)
363 {
364 struct component_match *master_match = NULL;
365 int ret;
366
367 priv->sub_master = devm_kzalloc(&priv->wdev->dev, sizeof(*priv->sub_master), GFP_KERNEL);
368 if (!priv->sub_master)
369 return -ENOMEM;
370
371 if (sub_component_type == CD_TYPE_NONE) {
372 /* The master callback will be called with NULL on bind. */
373 priv->sub_master->sub_component_list = ERR_PTR(-ENODEV);
374 priv->sub_master->registered = false;
375 return 0;
376 }
377
378 /*
379 * lwmi_cd_match() needs a pointer to enum lwmi_cd_type, but on-stack
380 * data cannot be used here. Steal one from lwmi_cd_table.
381 */
382 component_match_add(&priv->wdev->dev, &master_match, lwmi_cd_match,
383 (void *)&lwmi_cd_table[sub_component_type].type);
384 if (IS_ERR(master_match))
385 return PTR_ERR(master_match);
386
387 ret = component_master_add_with_match(&priv->wdev->dev, &lwmi_cd_sub_master_ops,
388 master_match);
389 if (ret)
390 return ret;
391
392 priv->sub_master->registered = true;
393 return 0;
394 }
395
396 /**
397 * lwmi_cd_sub_master_del() - Unregister a sub-master if it's registered
398 * @priv: Pointer to the sub-master capdata device private data.
399 */
lwmi_cd_sub_master_del(struct lwmi_cd_priv * priv)400 static void lwmi_cd_sub_master_del(struct lwmi_cd_priv *priv)
401 {
402 if (!priv->sub_master->registered)
403 return;
404
405 component_master_del(&priv->wdev->dev, &lwmi_cd_sub_master_ops);
406 priv->sub_master->registered = false;
407 }
408
409 /**
410 * lwmi_cd_sub_component_bind() - Bind sub-component to sub-master device.
411 * @sc_dev: Pointer to the sub-component capdata parent device.
412 * @sm_dev: Pointer to the sub-master capdata parent device.
413 * @data: Pointer used to return the capability data list pointer.
414 *
415 * On sub-master's bind, provide a pointer to the local capdata list.
416 * This is used by the sub-master to call the master callback.
417 *
418 * Return: 0
419 */
lwmi_cd_sub_component_bind(struct device * sc_dev,struct device * sm_dev,void * data)420 static int lwmi_cd_sub_component_bind(struct device *sc_dev,
421 struct device *sm_dev, void *data)
422 {
423 struct lwmi_cd_priv *priv = dev_get_drvdata(sc_dev);
424 struct cd_list **listp = data;
425
426 *listp = priv->list;
427
428 return 0;
429 }
430
431 static const struct component_ops lwmi_cd_sub_component_ops = {
432 .bind = lwmi_cd_sub_component_bind,
433 };
434
435 /*
436 * lwmi_cd*_get_data - Get the data of the specified attribute
437 * @list: The lenovo-wmi-capdata pointer to its cd_list struct.
438 * @attribute_id: The capdata attribute ID to be found.
439 * @output: Pointer to a capdata* struct to return the data.
440 *
441 * Retrieves the capability data struct pointer for the given
442 * attribute.
443 *
444 * Return: 0 on success, or -EINVAL.
445 */
446 #define DEF_LWMI_CDXX_GET_DATA(_cdxx, _cd_type, _output_t) \
447 int lwmi_##_cdxx##_get_data(struct cd_list *list, u32 attribute_id, _output_t *output) \
448 { \
449 u8 idx; \
450 \
451 if (WARN_ON(list->type != _cd_type)) \
452 return -EINVAL; \
453 \
454 guard(mutex)(&list->list_mutex); \
455 for (idx = 0; idx < list->count; idx++) { \
456 if (list->_cdxx[idx].id != attribute_id) \
457 continue; \
458 memcpy(output, &list->_cdxx[idx], sizeof(list->_cdxx[idx])); \
459 return 0; \
460 } \
461 return -EINVAL; \
462 }
463
464 DEF_LWMI_CDXX_GET_DATA(cd00, LENOVO_CAPABILITY_DATA_00, struct capdata00);
465 EXPORT_SYMBOL_NS_GPL(lwmi_cd00_get_data, "LENOVO_WMI_CAPDATA");
466
467 DEF_LWMI_CDXX_GET_DATA(cd01, LENOVO_CAPABILITY_DATA_01, struct capdata01);
468 EXPORT_SYMBOL_NS_GPL(lwmi_cd01_get_data, "LENOVO_WMI_CAPDATA");
469
470 DEF_LWMI_CDXX_GET_DATA(cd_fan, LENOVO_FAN_TEST_DATA, struct capdata_fan);
471 EXPORT_SYMBOL_NS_GPL(lwmi_cd_fan_get_data, "LENOVO_WMI_CAPDATA");
472
473 /**
474 * lwmi_cd_cache() - Cache all WMI data block information
475 * @priv: lenovo-wmi-capdata driver data.
476 *
477 * Loop through each WMI data block and cache the data.
478 *
479 * Return: 0 on success, or an error.
480 */
lwmi_cd_cache(struct lwmi_cd_priv * priv)481 static int lwmi_cd_cache(struct lwmi_cd_priv *priv)
482 {
483 size_t size;
484 int idx;
485 void *p;
486
487 switch (priv->list->type) {
488 case LENOVO_CAPABILITY_DATA_00:
489 p = &priv->list->cd00[0];
490 size = sizeof(priv->list->cd00[0]);
491 break;
492 case LENOVO_CAPABILITY_DATA_01:
493 p = &priv->list->cd01[0];
494 size = sizeof(priv->list->cd01[0]);
495 break;
496 case LENOVO_FAN_TEST_DATA:
497 /* Done by lwmi_cd_alloc() => lwmi_cd_fan_list_alloc_cache(). */
498 return 0;
499 default:
500 return -EINVAL;
501 }
502
503 guard(mutex)(&priv->list->list_mutex);
504 for (idx = 0; idx < priv->list->count; idx++, p += size) {
505 union acpi_object *ret_obj __free(kfree) = NULL;
506
507 ret_obj = wmidev_block_query(priv->wdev, idx);
508 if (!ret_obj)
509 return -ENODEV;
510
511 if (ret_obj->type != ACPI_TYPE_BUFFER ||
512 ret_obj->buffer.length < size)
513 continue;
514
515 memcpy(p, ret_obj->buffer.pointer, size);
516 }
517
518 return 0;
519 }
520
521 /**
522 * lwmi_cd_fan_list_alloc_cache() - Alloc and cache Fan Test Data list
523 * @priv: lenovo-wmi-capdata driver data.
524 * @listptr: Pointer to returned cd_list pointer.
525 *
526 * Return: count of fans found, or an error.
527 */
lwmi_cd_fan_list_alloc_cache(struct lwmi_cd_priv * priv,struct cd_list ** listptr)528 static int lwmi_cd_fan_list_alloc_cache(struct lwmi_cd_priv *priv, struct cd_list **listptr)
529 {
530 struct cd_list *list;
531 size_t size;
532 u32 count;
533 int idx;
534
535 /* Emit unaligned access to u8 buffer with __packed. */
536 struct cd_fan_block {
537 u32 nr;
538 u32 data[]; /* id[nr], max_rpm[nr], min_rpm[nr] */
539 } __packed * block;
540
541 union acpi_object *ret_obj __free(kfree) = wmidev_block_query(priv->wdev, 0);
542 if (!ret_obj)
543 return -ENODEV;
544
545 if (ret_obj->type == ACPI_TYPE_BUFFER) {
546 block = (struct cd_fan_block *)ret_obj->buffer.pointer;
547 size = ret_obj->buffer.length;
548
549 count = size >= sizeof(*block) ? block->nr : 0;
550 if (size < struct_size(block, data, count * 3)) {
551 dev_warn(&priv->wdev->dev,
552 "incomplete fan test data block: %zu < %zu, ignoring\n",
553 size, struct_size(block, data, count * 3));
554 count = 0;
555 } else if (count > U8_MAX) {
556 dev_warn(&priv->wdev->dev,
557 "too many fans reported: %u > %u, truncating\n",
558 count, U8_MAX);
559 count = U8_MAX;
560 }
561 } else {
562 /*
563 * This is usually caused by a dummy ACPI method. Do not return an error
564 * as failing to probe this device will result in sub-master device being
565 * unbound. This behavior aligns with lwmi_cd_cache().
566 */
567 count = 0;
568 }
569
570 list = devm_kzalloc(&priv->wdev->dev, struct_size(list, cd_fan, count), GFP_KERNEL);
571 if (!list)
572 return -ENOMEM;
573
574 for (idx = 0; idx < count; idx++) {
575 /* Do not calculate array index using count, as it may be truncated. */
576 list->cd_fan[idx] = (struct capdata_fan) {
577 .id = block->data[idx],
578 .max_rpm = block->data[idx + block->nr],
579 .min_rpm = block->data[idx + (2 * block->nr)],
580 };
581 }
582
583 *listptr = list;
584 return count;
585 }
586
587 /**
588 * lwmi_cd_alloc() - Allocate a cd_list struct in drvdata
589 * @priv: lenovo-wmi-capdata driver data.
590 * @type: The type of capability data.
591 *
592 * Allocate a cd_list struct large enough to contain data from all WMI data
593 * blocks provided by the interface.
594 *
595 * Return: 0 on success, or an error.
596 */
lwmi_cd_alloc(struct lwmi_cd_priv * priv,enum lwmi_cd_type type)597 static int lwmi_cd_alloc(struct lwmi_cd_priv *priv, enum lwmi_cd_type type)
598 {
599 struct cd_list *list;
600 size_t list_size;
601 int count, ret;
602
603 count = wmidev_instance_count(priv->wdev);
604
605 switch (type) {
606 case LENOVO_CAPABILITY_DATA_00:
607 list_size = struct_size(list, cd00, count);
608 break;
609 case LENOVO_CAPABILITY_DATA_01:
610 list_size = struct_size(list, cd01, count);
611 break;
612 case LENOVO_FAN_TEST_DATA:
613 count = lwmi_cd_fan_list_alloc_cache(priv, &list);
614 if (count < 0)
615 return count;
616
617 goto got_list;
618 default:
619 return -EINVAL;
620 }
621
622 list = devm_kzalloc(&priv->wdev->dev, list_size, GFP_KERNEL);
623 if (!list)
624 return -ENOMEM;
625
626 got_list:
627 ret = devm_mutex_init(&priv->wdev->dev, &list->list_mutex);
628 if (ret)
629 return ret;
630
631 list->type = type;
632 list->count = count;
633 priv->list = list;
634
635 return 0;
636 }
637
638 /**
639 * lwmi_cd_setup() - Cache all WMI data block information
640 * @priv: lenovo-wmi-capdata driver data.
641 * @type: The type of capability data.
642 *
643 * Allocate a cd_list struct large enough to contain data from all WMI data
644 * blocks provided by the interface. Then loop through each data block and
645 * cache the data.
646 *
647 * Return: 0 on success, or an error code.
648 */
lwmi_cd_setup(struct lwmi_cd_priv * priv,enum lwmi_cd_type type)649 static int lwmi_cd_setup(struct lwmi_cd_priv *priv, enum lwmi_cd_type type)
650 {
651 int ret;
652
653 ret = lwmi_cd_alloc(priv, type);
654 if (ret)
655 return ret;
656
657 return lwmi_cd_cache(priv);
658 }
659
660 /**
661 * lwmi_cd01_notifier_call() - Call method for cd01 notifier.
662 * block call chain.
663 * @nb: The notifier_block registered to lenovo-wmi-events driver.
664 * @action: Unused.
665 * @data: The ACPI event.
666 *
667 * For LWMI_EVENT_THERMAL_MODE, set current_mode and notify platform_profile
668 * of a change.
669 *
670 * Return: notifier_block status.
671 */
lwmi_cd01_notifier_call(struct notifier_block * nb,unsigned long action,void * data)672 static int lwmi_cd01_notifier_call(struct notifier_block *nb, unsigned long action,
673 void *data)
674 {
675 struct acpi_bus_event *event = data;
676 struct lwmi_cd_priv *priv;
677 int ret;
678
679 if (strcmp(event->device_class, ACPI_AC_CLASS) != 0)
680 return NOTIFY_DONE;
681
682 priv = container_of(nb, struct lwmi_cd_priv, acpi_nb);
683
684 switch (event->type) {
685 case ACPI_AC_NOTIFY_STATUS:
686 ret = lwmi_cd_cache(priv);
687 if (ret)
688 return NOTIFY_BAD;
689
690 return NOTIFY_OK;
691 default:
692 return NOTIFY_DONE;
693 }
694 }
695
696 /**
697 * lwmi_cd01_unregister() - Unregister the cd01 ACPI notifier_block.
698 * @data: The ACPI event notifier_block to unregister.
699 */
lwmi_cd01_unregister(void * data)700 static void lwmi_cd01_unregister(void *data)
701 {
702 struct notifier_block *acpi_nb = data;
703
704 unregister_acpi_notifier(acpi_nb);
705 }
706
lwmi_cd_probe(struct wmi_device * wdev,const void * context)707 static int lwmi_cd_probe(struct wmi_device *wdev, const void *context)
708 {
709 const struct lwmi_cd_info *info = context;
710 struct lwmi_cd_priv *priv;
711 int ret;
712
713 if (!info)
714 return -EINVAL;
715
716 priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
717 if (!priv)
718 return -ENOMEM;
719
720 priv->wdev = wdev;
721 dev_set_drvdata(&wdev->dev, priv);
722
723 ret = lwmi_cd_setup(priv, info->type);
724 if (ret)
725 goto out;
726
727 switch (info->type) {
728 case LENOVO_CAPABILITY_DATA_00: {
729 enum lwmi_cd_type sub_component_type = LENOVO_FAN_TEST_DATA;
730 struct capdata00 capdata00;
731
732 ret = lwmi_cd00_get_data(priv->list, LWMI_ATTR_ID_FAN_TEST, &capdata00);
733 if (ret || !(capdata00.supported & LWMI_SUPP_VALID)) {
734 dev_dbg(&wdev->dev, "capdata00 declares no fan test support\n");
735 sub_component_type = CD_TYPE_NONE;
736 }
737
738 /* Sub-master (capdata00) <-> sub-component (capdata_fan) */
739 ret = lwmi_cd_sub_master_add(priv, sub_component_type);
740 if (ret)
741 goto out;
742
743 /* Master (lenovo-wmi-other) <-> sub-master (capdata00) */
744 ret = component_add(&wdev->dev, &lwmi_cd_component_ops);
745 if (ret)
746 lwmi_cd_sub_master_del(priv);
747
748 goto out;
749 }
750 case LENOVO_CAPABILITY_DATA_01:
751 priv->acpi_nb.notifier_call = lwmi_cd01_notifier_call;
752
753 ret = register_acpi_notifier(&priv->acpi_nb);
754 if (ret)
755 goto out;
756
757 ret = devm_add_action_or_reset(&wdev->dev, lwmi_cd01_unregister,
758 &priv->acpi_nb);
759 if (ret)
760 goto out;
761
762 ret = component_add(&wdev->dev, &lwmi_cd_component_ops);
763 goto out;
764 case LENOVO_FAN_TEST_DATA:
765 ret = component_add(&wdev->dev, &lwmi_cd_sub_component_ops);
766 goto out;
767 default:
768 return -EINVAL;
769 }
770 out:
771 if (ret) {
772 dev_err(&wdev->dev, "failed to register %s: %d\n",
773 info->name, ret);
774 } else {
775 dev_dbg(&wdev->dev, "registered %s with %u items\n",
776 info->name, priv->list->count);
777 }
778 return ret;
779 }
780
lwmi_cd_remove(struct wmi_device * wdev)781 static void lwmi_cd_remove(struct wmi_device *wdev)
782 {
783 struct lwmi_cd_priv *priv = dev_get_drvdata(&wdev->dev);
784
785 switch (priv->list->type) {
786 case LENOVO_CAPABILITY_DATA_00:
787 lwmi_cd_sub_master_del(priv);
788 fallthrough;
789 case LENOVO_CAPABILITY_DATA_01:
790 component_del(&wdev->dev, &lwmi_cd_component_ops);
791 break;
792 case LENOVO_FAN_TEST_DATA:
793 component_del(&wdev->dev, &lwmi_cd_sub_component_ops);
794 break;
795 default:
796 WARN_ON(1);
797 }
798 }
799
800 #define LWMI_CD_WDEV_ID(_type) \
801 .guid_string = _type##_GUID, \
802 .context = &lwmi_cd_table[_type],
803
804 static const struct wmi_device_id lwmi_cd_id_table[] = {
805 { LWMI_CD_WDEV_ID(LENOVO_CAPABILITY_DATA_00) },
806 { LWMI_CD_WDEV_ID(LENOVO_CAPABILITY_DATA_01) },
807 { LWMI_CD_WDEV_ID(LENOVO_FAN_TEST_DATA) },
808 {}
809 };
810
811 static struct wmi_driver lwmi_cd_driver = {
812 .driver = {
813 .name = "lenovo_wmi_capdata",
814 .probe_type = PROBE_PREFER_ASYNCHRONOUS,
815 },
816 .id_table = lwmi_cd_id_table,
817 .probe = lwmi_cd_probe,
818 .remove = lwmi_cd_remove,
819 .no_singleton = true,
820 };
821
822 module_wmi_driver(lwmi_cd_driver);
823
824 MODULE_DEVICE_TABLE(wmi, lwmi_cd_id_table);
825 MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
826 MODULE_AUTHOR("Rong Zhang <i@rong.moe>");
827 MODULE_DESCRIPTION("Lenovo Capability Data WMI Driver");
828 MODULE_LICENSE("GPL");
829