1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Lenovo Other Mode WMI interface driver.
4 *
5 * This driver uses the fw_attributes class to expose the various WMI functions
6 * provided by the "Other Mode" WMI interface. This enables CPU and GPU power
7 * limit as well as various other attributes for devices that fall under the
8 * "Gaming Series" of Lenovo laptop devices. Each attribute exposed by the
9 * "Other Mode" interface has a corresponding Capability Data struct that
10 * allows the driver to probe details about the attribute such as if it is
11 * supported by the hardware, the default_value, max_value, min_value, and step
12 * increment.
13 *
14 * These attributes typically don't fit anywhere else in the sysfs and are set
15 * in Windows using one of Lenovo's multiple user applications.
16 *
17 * Additionally, this driver also exports tunable fan speed RPM to HWMON.
18 * Min/max RPM are also provided for reference.
19 *
20 * Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com>
21 * - fw_attributes
22 * - binding to Capability Data 01
23 *
24 * Copyright (C) 2025 Rong Zhang <i@rong.moe>
25 * - HWMON
26 * - binding to Capability Data 00 and Fan
27 */
28
29 #include <linux/acpi.h>
30 #include <linux/bitfield.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/export.h>
36 #include <linux/gfp_types.h>
37 #include <linux/hwmon.h>
38 #include <linux/idr.h>
39 #include <linux/kdev_t.h>
40 #include <linux/kobject.h>
41 #include <linux/limits.h>
42 #include <linux/module.h>
43 #include <linux/platform_profile.h>
44 #include <linux/types.h>
45 #include <linux/wmi.h>
46
47 #include "wmi-capdata.h"
48 #include "wmi-events.h"
49 #include "wmi-helpers.h"
50 #include "../firmware_attributes_class.h"
51
52 #define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B"
53
54 #define LWMI_DEVICE_ID_CPU 0x01
55
56 #define LWMI_FEATURE_ID_CPU_SPPT 0x01
57 #define LWMI_FEATURE_ID_CPU_SPL 0x02
58 #define LWMI_FEATURE_ID_CPU_FPPT 0x03
59
60 #define LWMI_FEATURE_ID_FAN_RPM 0x03
61
62 #define LWMI_FEATURE_VALUE_GET 17
63 #define LWMI_FEATURE_VALUE_SET 18
64
65 #define LWMI_FAN_ID_BASE 1
66 #define LWMI_FAN_NR 4
67 #define LWMI_FAN_ID(x) ((x) + LWMI_FAN_ID_BASE)
68
69 #define LWMI_FAN_DIV 100
70
71 #define LWMI_ATTR_ID_FAN_RPM(x) \
72 lwmi_attr_id(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_RPM, \
73 LWMI_GZ_THERMAL_MODE_NONE, LWMI_FAN_ID(x))
74
75 #define LWMI_OM_FW_ATTR_BASE_PATH "lenovo-wmi-other"
76 #define LWMI_OM_HWMON_NAME "lenovo_wmi_other"
77
78 static DEFINE_IDA(lwmi_om_ida);
79
80 enum attribute_property {
81 DEFAULT_VAL,
82 MAX_VAL,
83 MIN_VAL,
84 STEP_VAL,
85 SUPPORTED,
86 };
87
88 struct lwmi_fan_info {
89 u32 supported;
90 u32 last_target;
91 long min_rpm;
92 long max_rpm;
93 };
94
95 struct lwmi_om_priv {
96 struct component_master_ops *ops;
97
98 /* only valid after capdata bind */
99 struct cd_list *cd00_list;
100 struct cd_list *cd01_list;
101
102 struct device *hwmon_dev;
103 struct device *fw_attr_dev;
104 struct kset *fw_attr_kset;
105 struct wmi_device *wdev;
106 int ida_id;
107
108 struct lwmi_fan_info fan_info[LWMI_FAN_NR];
109
110 struct {
111 bool capdata00_collected : 1;
112 bool capdata_fan_collected : 1;
113 } fan_flags;
114 };
115
116 /*
117 * Visibility of fan channels:
118 *
119 * +-------------------+---------+------------------+-----------------------+------------+
120 * | | default | +expose_all_fans | +relax_fan_constraint | +both |
121 * +-------------------+---------+------------------+-----------------------+------------+
122 * | canonical | RW | RW | RW+relaxed | RW+relaxed |
123 * +-------------------+---------+------------------+-----------------------+------------+
124 * | -capdata_fan[idx] | N | RO | N | RW+relaxed |
125 * +-------------------+---------+------------------+-----------------------+------------+
126 *
127 * Note:
128 * 1. LWMI_ATTR_ID_FAN_RPM[idx].supported is always checked before exposing a channel.
129 * 2. -capdata_fan implies -capdata_fan[idx].
130 */
131 static bool expose_all_fans;
132 module_param(expose_all_fans, bool, 0444);
133 MODULE_PARM_DESC(expose_all_fans,
134 "This option skips some capability checks and solely relies on per-channel ones "
135 "to expose fan attributes. Use with caution.");
136
137 static bool relax_fan_constraint;
138 module_param(relax_fan_constraint, bool, 0444);
139 MODULE_PARM_DESC(relax_fan_constraint,
140 "Do not enforce fan RPM constraint (div/min/max) "
141 "and enables fan tuning when such data is missing. "
142 "Enabling this may results in HWMON attributes being out-of-sync, "
143 "and setting a too low RPM stops the fan. Use with caution.");
144
145 /* ======== HWMON (component: lenovo-wmi-capdata 00 & fan) ======== */
146
147 /**
148 * lwmi_om_fan_get_set() - Get or set fan RPM value of specified fan
149 * @priv: Driver private data structure
150 * @channel: Fan channel index (0-based)
151 * @val: Pointer to value (input for set, output for get)
152 * @set: True to set value, false to get value
153 *
154 * Communicates with WMI interface to either retrieve current fan RPM
155 * or set target fan RPM.
156 *
157 * Return: 0 on success, or an error code.
158 */
lwmi_om_fan_get_set(struct lwmi_om_priv * priv,int channel,u32 * val,bool set)159 static int lwmi_om_fan_get_set(struct lwmi_om_priv *priv, int channel, u32 *val, bool set)
160 {
161 struct wmi_method_args_32 args = {};
162 u32 method_id, retval;
163 int err;
164
165 method_id = set ? LWMI_FEATURE_VALUE_SET : LWMI_FEATURE_VALUE_GET;
166 args.arg0 = LWMI_ATTR_ID_FAN_RPM(channel);
167 args.arg1 = set ? *val : 0;
168
169 err = lwmi_dev_evaluate_int(priv->wdev, 0x0, method_id,
170 (unsigned char *)&args, sizeof(args), &retval);
171 if (err)
172 return err;
173
174 if (!set) {
175 *val = retval;
176 return 0;
177 }
178
179 /*
180 * It seems that 0 means "no error" and 1 means "done". Apparently
181 * different firmware teams have different thoughts on indicating
182 * success, so we accepts both.
183 */
184 return (retval == 0 || retval == 1) ? 0 : -EIO;
185 }
186
187 /**
188 * lwmi_om_hwmon_is_visible() - Determine visibility of HWMON attributes
189 * @drvdata: Driver private data
190 * @type: Sensor type
191 * @attr: Attribute identifier
192 * @channel: Channel index
193 *
194 * Determines whether an HWMON attribute should be visible in sysfs
195 * based on hardware capabilities and current configuration.
196 *
197 * Return: permission mode, or 0 if invisible.
198 */
lwmi_om_hwmon_is_visible(const void * drvdata,enum hwmon_sensor_types type,u32 attr,int channel)199 static umode_t lwmi_om_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
200 u32 attr, int channel)
201 {
202 struct lwmi_om_priv *priv = (struct lwmi_om_priv *)drvdata;
203 bool visible = false;
204
205 if (type == hwmon_fan) {
206 if (!(priv->fan_info[channel].supported & LWMI_SUPP_VALID))
207 return 0;
208
209 switch (attr) {
210 case hwmon_fan_target:
211 if (!(priv->fan_info[channel].supported & LWMI_SUPP_SET))
212 return 0;
213
214 if (relax_fan_constraint ||
215 (priv->fan_info[channel].min_rpm >= 0 &&
216 priv->fan_info[channel].max_rpm >= 0))
217 return 0644;
218
219 /*
220 * Reaching here implies expose_all_fans is set.
221 * See lwmi_om_hwmon_add().
222 */
223 dev_warn_once(&priv->wdev->dev,
224 "fan tuning disabled due to missing RPM constraint\n");
225 return 0;
226 case hwmon_fan_div:
227 case hwmon_fan_input:
228 visible = priv->fan_info[channel].supported & LWMI_SUPP_GET;
229 break;
230 case hwmon_fan_min:
231 visible = priv->fan_info[channel].min_rpm >= 0;
232 break;
233 case hwmon_fan_max:
234 visible = priv->fan_info[channel].max_rpm >= 0;
235 break;
236 }
237 }
238
239 return visible ? 0444 : 0;
240 }
241
242 /**
243 * lwmi_om_hwmon_read() - Read HWMON sensor data
244 * @dev: Device pointer
245 * @type: Sensor type
246 * @attr: Attribute identifier
247 * @channel: Channel index
248 * @val: Pointer to store value
249 *
250 * Reads current sensor values from hardware through WMI interface.
251 *
252 * Return: 0 on success, or an error code.
253 */
lwmi_om_hwmon_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)254 static int lwmi_om_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
255 u32 attr, int channel, long *val)
256 {
257 struct lwmi_om_priv *priv = dev_get_drvdata(dev);
258 u32 retval = 0;
259 int err;
260
261 if (type == hwmon_fan) {
262 switch (attr) {
263 /*
264 * The EC has an internal RPM divisor (i.e., the raw register value is
265 * RPM / fanY_div). For fanY_input, the WMI method reads the register
266 * value and returns raw * fanY_div. For fanY_target, the WMI method
267 * divides the written value by fanY_div before writing it to the EC.
268 *
269 * As a result, reading fanY_input always returns a multiple of fanY_div,
270 * while writing to fanY_target loses the remainder.
271 */
272 case hwmon_fan_div:
273 *val = LWMI_FAN_DIV;
274 return 0;
275 case hwmon_fan_input:
276 err = lwmi_om_fan_get_set(priv, channel, &retval, false);
277 if (err)
278 return err;
279
280 *val = retval;
281 return 0;
282 case hwmon_fan_target:
283 *val = priv->fan_info[channel].last_target;
284 return 0;
285 case hwmon_fan_min:
286 *val = priv->fan_info[channel].min_rpm;
287 return 0;
288 case hwmon_fan_max:
289 *val = priv->fan_info[channel].max_rpm;
290 return 0;
291 }
292 }
293
294 return -EOPNOTSUPP;
295 }
296
297 /**
298 * lwmi_om_hwmon_write() - Write HWMON sensor data
299 * @dev: Device pointer
300 * @type: Sensor type
301 * @attr: Attribute identifier
302 * @channel: Channel index
303 * @val: Value to write
304 *
305 * Writes configuration values to hardware through WMI interface.
306 *
307 * Return: 0 on success, or an error code.
308 */
lwmi_om_hwmon_write(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long val)309 static int lwmi_om_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
310 u32 attr, int channel, long val)
311 {
312 struct lwmi_om_priv *priv = dev_get_drvdata(dev);
313 u32 raw, min_rpm, max_rpm;
314 int err;
315
316 if (type == hwmon_fan) {
317 switch (attr) {
318 case hwmon_fan_target:
319 if (relax_fan_constraint) {
320 min_rpm = 1;
321 max_rpm = U16_MAX;
322 } else {
323 min_rpm = priv->fan_info[channel].min_rpm;
324 max_rpm = priv->fan_info[channel].max_rpm;
325 }
326
327 /* 0 means "auto". */
328 if (val != 0 && (val < min_rpm || val > max_rpm))
329 return -EINVAL;
330
331 /*
332 * The effective fanY_target is always a multiple of fanY_div
333 * due to the EC's internal RPM divisor (see lwmi_om_hwmon_read).
334 *
335 * Round down the written value to the nearest multiple of fanY_div
336 * to prevent mismatch between the effective value and last_target.
337 *
338 * For relax_fan_constraint, skip this conversion as setting a
339 * sub-fanY_div value is necessary to completely stop the fan on
340 * some devices.
341 */
342 if (!relax_fan_constraint)
343 raw = val / LWMI_FAN_DIV * LWMI_FAN_DIV;
344 else
345 raw = val;
346
347 err = lwmi_om_fan_get_set(priv, channel, &raw, true);
348 if (err)
349 return err;
350
351 priv->fan_info[channel].last_target = raw;
352 return 0;
353 }
354 }
355
356 return -EOPNOTSUPP;
357 }
358
359 static const struct hwmon_channel_info * const lwmi_om_hwmon_info[] = {
360 /* Must match LWMI_FAN_NR. */
361 HWMON_CHANNEL_INFO(fan,
362 HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV |
363 HWMON_F_MIN | HWMON_F_MAX,
364 HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV |
365 HWMON_F_MIN | HWMON_F_MAX,
366 HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV |
367 HWMON_F_MIN | HWMON_F_MAX,
368 HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV |
369 HWMON_F_MIN | HWMON_F_MAX),
370 NULL
371 };
372
373 static const struct hwmon_ops lwmi_om_hwmon_ops = {
374 .is_visible = lwmi_om_hwmon_is_visible,
375 .read = lwmi_om_hwmon_read,
376 .write = lwmi_om_hwmon_write,
377 };
378
379 static const struct hwmon_chip_info lwmi_om_hwmon_chip_info = {
380 .ops = &lwmi_om_hwmon_ops,
381 .info = lwmi_om_hwmon_info,
382 };
383
384 /**
385 * lwmi_om_hwmon_add() - Register HWMON device if all info is collected
386 * @priv: Driver private data
387 */
lwmi_om_hwmon_add(struct lwmi_om_priv * priv)388 static void lwmi_om_hwmon_add(struct lwmi_om_priv *priv)
389 {
390 int i, valid;
391
392 if (WARN_ON(priv->hwmon_dev))
393 return;
394
395 if (!priv->fan_flags.capdata00_collected || !priv->fan_flags.capdata_fan_collected) {
396 dev_dbg(&priv->wdev->dev, "HWMON registration pending (00: %d, fan: %d)\n",
397 priv->fan_flags.capdata00_collected,
398 priv->fan_flags.capdata_fan_collected);
399 return;
400 }
401
402 if (expose_all_fans)
403 dev_warn(&priv->wdev->dev, "all fans exposed. Use with caution\n");
404
405 if (relax_fan_constraint)
406 dev_warn(&priv->wdev->dev, "fan RPM constraint relaxed. Use with caution\n");
407
408 valid = 0;
409 for (i = 0; i < LWMI_FAN_NR; i++) {
410 if (!(priv->fan_info[i].supported & LWMI_SUPP_VALID))
411 continue;
412
413 valid++;
414
415 if (!expose_all_fans &&
416 (priv->fan_info[i].min_rpm < 0 || priv->fan_info[i].max_rpm < 0)) {
417 dev_dbg(&priv->wdev->dev, "missing RPM constraint for fan%d, hiding\n",
418 LWMI_FAN_ID(i));
419 priv->fan_info[i].supported = 0;
420 valid--;
421 }
422 }
423
424 if (valid == 0) {
425 dev_warn(&priv->wdev->dev,
426 "fan reporting/tuning is unsupported on this device\n");
427 return;
428 }
429
430 priv->hwmon_dev = hwmon_device_register_with_info(&priv->wdev->dev,
431 LWMI_OM_HWMON_NAME, priv,
432 &lwmi_om_hwmon_chip_info,
433 NULL);
434 if (IS_ERR(priv->hwmon_dev)) {
435 dev_warn(&priv->wdev->dev, "failed to register HWMON device: %ld\n",
436 PTR_ERR(priv->hwmon_dev));
437 priv->hwmon_dev = NULL;
438 return;
439 }
440
441 dev_dbg(&priv->wdev->dev, "registered HWMON device\n");
442 }
443
444 /**
445 * lwmi_om_hwmon_remove() - Unregister HWMON device
446 * @priv: Driver private data
447 *
448 * Unregisters the HWMON device if applicable.
449 */
lwmi_om_hwmon_remove(struct lwmi_om_priv * priv)450 static void lwmi_om_hwmon_remove(struct lwmi_om_priv *priv)
451 {
452 if (!priv->hwmon_dev)
453 return;
454
455 hwmon_device_unregister(priv->hwmon_dev);
456 priv->hwmon_dev = NULL;
457 }
458
459 /**
460 * lwmi_om_fan_info_init() - Initialzie fan info
461 * @priv: Driver private data
462 *
463 * lwmi_om_fan_info_collect_cd00() and lwmi_om_fan_info_collect_cd_fan() may be
464 * called in an arbitrary order. Hence, initializion must be done before.
465 */
lwmi_om_fan_info_init(struct lwmi_om_priv * priv)466 static void lwmi_om_fan_info_init(struct lwmi_om_priv *priv)
467 {
468 int i;
469
470 for (i = 0; i < LWMI_FAN_NR; i++) {
471 priv->fan_info[i] = (struct lwmi_fan_info) {
472 .supported = 0,
473 /*
474 * Assume 0 on probe as the EC resets all fans to auto mode on (re)boot.
475 *
476 * Note that S0ix (s2idle) preserves the RPM target, so we don't need
477 * suspend/resume callbacks. This behavior has not been tested on S3-
478 * capable devices, but I doubt if such devices even have this interface.
479 */
480 .last_target = 0,
481 .min_rpm = -ENODATA,
482 .max_rpm = -ENODATA,
483 };
484 }
485
486 priv->fan_flags.capdata00_collected = false;
487 priv->fan_flags.capdata_fan_collected = false;
488 }
489
490 /**
491 * lwmi_om_fan_info_collect_cd00() - Collect fan info from capdata 00
492 * @priv: Driver private data
493 */
lwmi_om_fan_info_collect_cd00(struct lwmi_om_priv * priv)494 static void lwmi_om_fan_info_collect_cd00(struct lwmi_om_priv *priv)
495 {
496 struct capdata00 capdata00;
497 int i, err;
498
499 dev_dbg(&priv->wdev->dev, "Collecting fan info from capdata00\n");
500
501 for (i = 0; i < LWMI_FAN_NR; i++) {
502 err = lwmi_cd00_get_data(priv->cd00_list, LWMI_ATTR_ID_FAN_RPM(i), &capdata00);
503 priv->fan_info[i].supported = err ? 0 : capdata00.supported;
504 }
505
506 priv->fan_flags.capdata00_collected = true;
507 lwmi_om_hwmon_add(priv);
508 }
509
510 /**
511 * lwmi_om_fan_info_collect_cd_fan() - Collect fan info from capdata fan
512 * @dev: Pointer to the lenovo-wmi-other device
513 * @cd_fan_list: Pointer to the capdata fan list
514 */
lwmi_om_fan_info_collect_cd_fan(struct device * dev,struct cd_list * cd_fan_list)515 static void lwmi_om_fan_info_collect_cd_fan(struct device *dev, struct cd_list *cd_fan_list)
516 {
517 struct lwmi_om_priv *priv = dev_get_drvdata(dev);
518 struct capdata_fan capdata_fan;
519 int i, err;
520
521 dev_dbg(dev, "Collecting fan info from capdata_fan\n");
522
523 if (!cd_fan_list)
524 goto out;
525
526 for (i = 0; i < LWMI_FAN_NR; i++) {
527 err = lwmi_cd_fan_get_data(cd_fan_list, LWMI_FAN_ID(i), &capdata_fan);
528 if (err)
529 continue;
530
531 priv->fan_info[i].min_rpm = capdata_fan.min_rpm;
532 priv->fan_info[i].max_rpm = capdata_fan.max_rpm;
533 }
534
535 out:
536 priv->fan_flags.capdata_fan_collected = true;
537 lwmi_om_hwmon_add(priv);
538 }
539
540 /* ======== fw_attributes (component: lenovo-wmi-capdata 01) ======== */
541
542 struct tunable_attr_01 {
543 struct device *dev;
544 u8 feature_id;
545 u8 device_id;
546 u8 type_id;
547 u8 cd_mode_id; /* mode arg for searching capdata */
548 u8 cv_mode_id; /* mode arg for set/get current_value */
549 };
550
551 /**
552 * tunable_attr_01_id() - Formats a tunable_attr_01 to a capdata attribute ID
553 * @attr: The tunable_attr_01 to format.
554 * @mode: The u8 corresponding to the wmi-gamezone mode for set/get.
555 *
556 * Return: encoded capability data attribute ID.
557 */
tunable_attr_01_id(struct tunable_attr_01 * attr,u8 mode)558 static u32 tunable_attr_01_id(struct tunable_attr_01 *attr, u8 mode)
559 {
560 return lwmi_attr_id(attr->device_id, attr->feature_id, mode, attr->type_id);
561 }
562
563 static struct tunable_attr_01 ppt_pl1_spl = {
564 .device_id = LWMI_DEVICE_ID_CPU,
565 .feature_id = LWMI_FEATURE_ID_CPU_SPL,
566 .type_id = LWMI_TYPE_ID_NONE,
567 };
568
569 static struct tunable_attr_01 ppt_pl2_sppt = {
570 .device_id = LWMI_DEVICE_ID_CPU,
571 .feature_id = LWMI_FEATURE_ID_CPU_SPPT,
572 .type_id = LWMI_TYPE_ID_NONE,
573 };
574
575 static struct tunable_attr_01 ppt_pl3_fppt = {
576 .device_id = LWMI_DEVICE_ID_CPU,
577 .feature_id = LWMI_FEATURE_ID_CPU_FPPT,
578 .type_id = LWMI_TYPE_ID_NONE,
579 };
580
581 struct capdata01_attr_group {
582 const struct attribute_group *attr_group;
583 struct tunable_attr_01 *tunable_attr;
584 };
585
586 /* Attribute Methods */
587
588 /**
589 * int_type_show() - Emit the data type for an integer attribute
590 * @kobj: Pointer to the driver object.
591 * @kattr: Pointer to the attribute calling this function.
592 * @buf: The buffer to write to.
593 *
594 * Return: Number of characters written to buf.
595 */
int_type_show(struct kobject * kobj,struct kobj_attribute * kattr,char * buf)596 static ssize_t int_type_show(struct kobject *kobj, struct kobj_attribute *kattr,
597 char *buf)
598 {
599 return sysfs_emit(buf, "integer\n");
600 }
601
602 /**
603 * attr_capdata01_show() - Get the value of the specified attribute property
604 *
605 * @kobj: Pointer to the driver object.
606 * @kattr: Pointer to the attribute calling this function.
607 * @buf: The buffer to write to.
608 * @tunable_attr: The attribute to be read.
609 * @prop: The property of this attribute to be read.
610 *
611 * Retrieves the given property from the capability data 01 struct for the
612 * specified attribute's "custom" thermal mode. This function is intended
613 * to be generic so it can be called from any integer attributes "_show"
614 * function.
615 *
616 * If the WMI is success the sysfs attribute is notified.
617 *
618 * Return: Either number of characters written to buf, or an error code.
619 */
attr_capdata01_show(struct kobject * kobj,struct kobj_attribute * kattr,char * buf,struct tunable_attr_01 * tunable_attr,enum attribute_property prop)620 static ssize_t attr_capdata01_show(struct kobject *kobj,
621 struct kobj_attribute *kattr, char *buf,
622 struct tunable_attr_01 *tunable_attr,
623 enum attribute_property prop)
624 {
625 struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
626 struct capdata01 capdata;
627 u32 attribute_id;
628 int value, ret;
629
630 attribute_id = tunable_attr_01_id(tunable_attr, tunable_attr->cd_mode_id);
631
632 ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
633 if (ret)
634 return ret;
635
636 switch (prop) {
637 case DEFAULT_VAL:
638 value = capdata.default_value;
639 break;
640 case MAX_VAL:
641 value = capdata.max_value;
642 break;
643 case MIN_VAL:
644 value = capdata.min_value;
645 break;
646 case STEP_VAL:
647 value = capdata.step;
648 break;
649 default:
650 return -EINVAL;
651 }
652
653 return sysfs_emit(buf, "%d\n", value);
654 }
655
656 /**
657 * attr_current_value_store() - Set the current value of the given attribute
658 * @kobj: Pointer to the driver object.
659 * @kattr: Pointer to the attribute calling this function.
660 * @buf: The buffer to read from, this is parsed to `int` type.
661 * @count: Required by sysfs attribute macros, pass in from the callee attr.
662 * @tunable_attr: The attribute to be stored.
663 *
664 * Sets the value of the given attribute when operating under the "custom"
665 * smartfan profile. The current smartfan profile is retrieved from the
666 * lenovo-wmi-gamezone driver and error is returned if the result is not
667 * "custom". This function is intended to be generic so it can be called from
668 * any integer attribute's "_store" function. The integer to be sent to the WMI
669 * method is range checked and an error code is returned if out of range.
670 *
671 * If the value is valid and WMI is success, then the sysfs attribute is
672 * notified.
673 *
674 * Return: Either count, or an error code.
675 */
attr_current_value_store(struct kobject * kobj,struct kobj_attribute * kattr,const char * buf,size_t count,struct tunable_attr_01 * tunable_attr)676 static ssize_t attr_current_value_store(struct kobject *kobj,
677 struct kobj_attribute *kattr,
678 const char *buf, size_t count,
679 struct tunable_attr_01 *tunable_attr)
680 {
681 struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
682 struct wmi_method_args_32 args = {};
683 struct capdata01 capdata;
684 enum thermal_mode mode;
685 u32 value;
686 int ret;
687
688 ret = lwmi_tm_notifier_call(&mode);
689 if (ret)
690 return ret;
691
692 if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
693 return -EBUSY;
694
695 args.arg0 = tunable_attr_01_id(tunable_attr, tunable_attr->cd_mode_id);
696
697 ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
698 if (ret)
699 return ret;
700
701 ret = kstrtouint(buf, 10, &value);
702 if (ret)
703 return ret;
704
705 if (value < capdata.min_value || value > capdata.max_value)
706 return -EINVAL;
707
708 args.arg0 = tunable_attr_01_id(tunable_attr, tunable_attr->cv_mode_id);
709 args.arg1 = value;
710
711 ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
712 (unsigned char *)&args, sizeof(args), NULL);
713 if (ret)
714 return ret;
715
716 return count;
717 };
718
719 /**
720 * attr_current_value_show() - Get the current value of the given attribute
721 * @kobj: Pointer to the driver object.
722 * @kattr: Pointer to the attribute calling this function.
723 * @buf: The buffer to write to.
724 * @tunable_attr: The attribute to be read.
725 *
726 * Retrieves the value of the given attribute for the current smartfan profile.
727 * The current smartfan profile is retrieved from the lenovo-wmi-gamezone driver.
728 * This function is intended to be generic so it can be called from any integer
729 * attribute's "_show" function.
730 *
731 * If the WMI is success the sysfs attribute is notified.
732 *
733 * Return: Either number of characters written to buf, or an error code.
734 */
attr_current_value_show(struct kobject * kobj,struct kobj_attribute * kattr,char * buf,struct tunable_attr_01 * tunable_attr)735 static ssize_t attr_current_value_show(struct kobject *kobj,
736 struct kobj_attribute *kattr, char *buf,
737 struct tunable_attr_01 *tunable_attr)
738 {
739 struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
740 struct wmi_method_args_32 args = {};
741 enum thermal_mode mode;
742 int retval;
743 int ret;
744
745 ret = lwmi_tm_notifier_call(&mode);
746 if (ret)
747 return ret;
748
749 /* If "no-mode" is the supported mode, ensure we never send current mode */
750 if (tunable_attr->cv_mode_id == LWMI_GZ_THERMAL_MODE_NONE)
751 mode = tunable_attr->cv_mode_id;
752
753 args.arg0 = tunable_attr_01_id(tunable_attr, mode);
754
755 ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
756 (unsigned char *)&args, sizeof(args),
757 &retval);
758 if (ret)
759 return ret;
760
761 return sysfs_emit(buf, "%d\n", retval);
762 }
763
764 /**
765 * lwmi_attr_01_is_supported() - Determine if the given attribute is supported.
766 * @tunable_attr: The attribute to verify.
767 *
768 * For an attribute to be supported it must have a functional get/set method,
769 * as well as associated capability data stored in the capdata01 table.
770 *
771 * First check if the attribute has a corresponding data table under custom mode
772 * (0xff), then under no mode (0x00). If either of those passes, check if the
773 * supported field of the capdata struct is > 0. If it is supported, store the
774 * successful mode in the cd_mode_id field of tunable_attr.
775 *
776 * If the attribute capdata shows it is supported, attempt to determine the mode
777 * for the current value property get/set methods using a similar pattern to the
778 * capdata table check. If the value returned by either mode is 0 or an error,
779 * assume that mode is not supported. Otherwise, store the successful mode in the
780 * cv_mode_id field of tunable_attr.
781 *
782 * If any of the above checks fail then the attribute is not fully supported.
783 *
784 * Return: true if capdata and set/get modes are found, otherwise false.
785 */
lwmi_attr_01_is_supported(struct tunable_attr_01 * tunable_attr)786 static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr)
787 {
788 u8 modes[2] = { LWMI_GZ_THERMAL_MODE_CUSTOM, LWMI_GZ_THERMAL_MODE_NONE };
789 struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
790 struct wmi_method_args_32 args = {};
791 bool cd_mode_found = false;
792 bool cv_mode_found = false;
793 struct capdata01 capdata;
794 int retval, ret, i;
795
796 /* Determine tunable_attr->cd_mode_id */
797 for (i = 0; i < ARRAY_SIZE(modes); i++) {
798 args.arg0 = tunable_attr_01_id(tunable_attr, modes[i]);
799
800 ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
801 if (ret || !capdata.supported)
802 continue;
803
804 tunable_attr->cd_mode_id = modes[i];
805 cd_mode_found = true;
806 break;
807 }
808
809 if (!cd_mode_found)
810 return cd_mode_found;
811
812 dev_dbg(tunable_attr->dev,
813 "cd_mode_id: %#010x\n", args.arg0);
814
815 /* Determine tunable_attr->cv_mode_id, returns 1 if supported */
816 for (i = 0; i < ARRAY_SIZE(modes); i++) {
817 args.arg0 = tunable_attr_01_id(tunable_attr, modes[i]);
818
819 ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
820 (u8 *)&args, sizeof(args),
821 &retval);
822 if (ret || !retval)
823 continue;
824
825 tunable_attr->cv_mode_id = modes[i];
826 cv_mode_found = true;
827 break;
828 }
829
830 if (!cv_mode_found)
831 return cv_mode_found;
832
833 dev_dbg(tunable_attr->dev, "cv_mode_id: %#010x, attribute support level: %#010x\n",
834 args.arg0, capdata.supported);
835
836 return capdata.supported > 0;
837 }
838
839 /* Lenovo WMI Other Mode Attribute macros */
840 #define __LWMI_ATTR_RO(_func, _name) \
841 { \
842 .attr = { .name = __stringify(_name), .mode = 0444 }, \
843 .show = _func##_##_name##_show, \
844 }
845
846 #define __LWMI_ATTR_RO_AS(_name, _show) \
847 { \
848 .attr = { .name = __stringify(_name), .mode = 0444 }, \
849 .show = _show, \
850 }
851
852 #define __LWMI_ATTR_RW(_func, _name) \
853 __ATTR(_name, 0644, _func##_##_name##_show, _func##_##_name##_store)
854
855 /* Shows a formatted static variable */
856 #define __LWMI_ATTR_SHOW_FMT(_prop, _attrname, _fmt, _val) \
857 static ssize_t _attrname##_##_prop##_show( \
858 struct kobject *kobj, struct kobj_attribute *kattr, char *buf) \
859 { \
860 return sysfs_emit(buf, _fmt, _val); \
861 } \
862 static struct kobj_attribute attr_##_attrname##_##_prop = \
863 __LWMI_ATTR_RO(_attrname, _prop)
864
865 /* Attribute current value read/write */
866 #define __LWMI_TUNABLE_CURRENT_VALUE_CAP01(_attrname) \
867 static ssize_t _attrname##_current_value_store( \
868 struct kobject *kobj, struct kobj_attribute *kattr, \
869 const char *buf, size_t count) \
870 { \
871 return attr_current_value_store(kobj, kattr, buf, count, \
872 &_attrname); \
873 } \
874 static ssize_t _attrname##_current_value_show( \
875 struct kobject *kobj, struct kobj_attribute *kattr, char *buf) \
876 { \
877 return attr_current_value_show(kobj, kattr, buf, &_attrname); \
878 } \
879 static struct kobj_attribute attr_##_attrname##_current_value = \
880 __LWMI_ATTR_RW(_attrname, current_value)
881
882 /* Attribute property read only */
883 #define __LWMI_TUNABLE_RO_CAP01(_prop, _attrname, _prop_type) \
884 static ssize_t _attrname##_##_prop##_show( \
885 struct kobject *kobj, struct kobj_attribute *kattr, char *buf) \
886 { \
887 return attr_capdata01_show(kobj, kattr, buf, &_attrname, \
888 _prop_type); \
889 } \
890 static struct kobj_attribute attr_##_attrname##_##_prop = \
891 __LWMI_ATTR_RO(_attrname, _prop)
892
893 #define LWMI_ATTR_GROUP_TUNABLE_CAP01(_attrname, _fsname, _dispname) \
894 __LWMI_TUNABLE_CURRENT_VALUE_CAP01(_attrname); \
895 __LWMI_TUNABLE_RO_CAP01(default_value, _attrname, DEFAULT_VAL); \
896 __LWMI_ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
897 __LWMI_TUNABLE_RO_CAP01(max_value, _attrname, MAX_VAL); \
898 __LWMI_TUNABLE_RO_CAP01(min_value, _attrname, MIN_VAL); \
899 __LWMI_TUNABLE_RO_CAP01(scalar_increment, _attrname, STEP_VAL); \
900 static struct kobj_attribute attr_##_attrname##_type = \
901 __LWMI_ATTR_RO_AS(type, int_type_show); \
902 static struct attribute *_attrname##_attrs[] = { \
903 &attr_##_attrname##_current_value.attr, \
904 &attr_##_attrname##_default_value.attr, \
905 &attr_##_attrname##_display_name.attr, \
906 &attr_##_attrname##_max_value.attr, \
907 &attr_##_attrname##_min_value.attr, \
908 &attr_##_attrname##_scalar_increment.attr, \
909 &attr_##_attrname##_type.attr, \
910 NULL, \
911 }; \
912 static const struct attribute_group _attrname##_attr_group = { \
913 .name = _fsname, .attrs = _attrname##_attrs \
914 }
915
916 LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_spl, "ppt_pl1_spl",
917 "Set the CPU sustained power limit");
918 LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl2_sppt, "ppt_pl2_sppt",
919 "Set the CPU slow package power tracking limit");
920 LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl3_fppt, "ppt_pl3_fppt",
921 "Set the CPU fast package power tracking limit");
922
923 static struct capdata01_attr_group cd01_attr_groups[] = {
924 { &ppt_pl1_spl_attr_group, &ppt_pl1_spl },
925 { &ppt_pl2_sppt_attr_group, &ppt_pl2_sppt },
926 { &ppt_pl3_fppt_attr_group, &ppt_pl3_fppt },
927 {},
928 };
929
930 /**
931 * lwmi_om_fw_attr_add() - Register all firmware_attributes_class members
932 * @priv: The Other Mode driver data.
933 */
lwmi_om_fw_attr_add(struct lwmi_om_priv * priv)934 static void lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
935 {
936 unsigned int i;
937 int err;
938
939 err = ida_alloc(&lwmi_om_ida, GFP_KERNEL);
940 if (err < 0)
941 goto err_no_ida;
942
943 priv->ida_id = err;
944
945 priv->fw_attr_dev = device_create(&firmware_attributes_class, NULL,
946 MKDEV(0, 0), NULL, "%s-%u",
947 LWMI_OM_FW_ATTR_BASE_PATH,
948 priv->ida_id);
949 if (IS_ERR(priv->fw_attr_dev)) {
950 err = PTR_ERR(priv->fw_attr_dev);
951 goto err_free_ida;
952 }
953
954 priv->fw_attr_kset = kset_create_and_add("attributes", NULL,
955 &priv->fw_attr_dev->kobj);
956 if (!priv->fw_attr_kset) {
957 err = -ENOMEM;
958 goto err_destroy_classdev;
959 }
960
961 for (i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) {
962 cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
963 if (!lwmi_attr_01_is_supported(cd01_attr_groups[i].tunable_attr))
964 continue;
965
966 err = sysfs_create_group(&priv->fw_attr_kset->kobj,
967 cd01_attr_groups[i].attr_group);
968 if (err)
969 goto err_remove_groups;
970 }
971 return;
972
973 err_remove_groups:
974 while (i--)
975 sysfs_remove_group(&priv->fw_attr_kset->kobj,
976 cd01_attr_groups[i].attr_group);
977
978 kset_unregister(priv->fw_attr_kset);
979
980 err_destroy_classdev:
981 device_unregister(priv->fw_attr_dev);
982
983 err_free_ida:
984 ida_free(&lwmi_om_ida, priv->ida_id);
985
986 err_no_ida:
987 priv->ida_id = -EIDRM;
988
989 dev_warn(&priv->wdev->dev,
990 "failed to register firmware-attributes device: %d\n", err);
991 }
992
993 /**
994 * lwmi_om_fw_attr_remove() - Unregister all capability data attribute groups
995 * @priv: the lenovo-wmi-other driver data.
996 */
lwmi_om_fw_attr_remove(struct lwmi_om_priv * priv)997 static void lwmi_om_fw_attr_remove(struct lwmi_om_priv *priv)
998 {
999 if (priv->ida_id < 0)
1000 return;
1001
1002 for (unsigned int i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++)
1003 sysfs_remove_group(&priv->fw_attr_kset->kobj,
1004 cd01_attr_groups[i].attr_group);
1005
1006 kset_unregister(priv->fw_attr_kset);
1007 device_unregister(priv->fw_attr_dev);
1008 ida_free(&lwmi_om_ida, priv->ida_id);
1009 priv->ida_id = -EIDRM;
1010 }
1011
1012 /* ======== Self (master: lenovo-wmi-other) ======== */
1013
1014 /**
1015 * lwmi_om_master_bind() - Bind all components of the other mode driver
1016 * @dev: The lenovo-wmi-other driver basic device.
1017 *
1018 * Call component_bind_all to bind the lenovo-wmi-capdata devices to the
1019 * lenovo-wmi-other master driver, with a callback to collect fan info from
1020 * capdata_fan. On success, assign the capability data list pointers to the
1021 * driver data struct for later access. These pointers are only valid while the
1022 * capdata interfaces exist. Finally, collect fan info from capdata00 and
1023 * register all firmware attribute groups. Note that the HWMON device is
1024 * registered only if all fan info is collected. Hence, it is not registered
1025 * here. See lwmi_om_fan_info_collect_cd00() and
1026 * lwmi_om_fan_info_collect_cd_fan().
1027 *
1028 * Return: 0 on success, or an error code.
1029 */
lwmi_om_master_bind(struct device * dev)1030 static int lwmi_om_master_bind(struct device *dev)
1031 {
1032 struct lwmi_om_priv *priv = dev_get_drvdata(dev);
1033 struct lwmi_cd_binder binder = {
1034 .cd_fan_list_cb = lwmi_om_fan_info_collect_cd_fan,
1035 };
1036 int ret;
1037
1038 lwmi_om_fan_info_init(priv);
1039
1040 ret = component_bind_all(dev, &binder);
1041 if (ret)
1042 return ret;
1043
1044 priv->cd00_list = binder.cd00_list;
1045 priv->cd01_list = binder.cd01_list;
1046 if (!priv->cd00_list || !priv->cd01_list) {
1047 component_unbind_all(dev, NULL);
1048
1049 return -ENODEV;
1050 }
1051
1052 lwmi_om_fan_info_collect_cd00(priv);
1053
1054 lwmi_om_fw_attr_add(priv);
1055
1056 return 0;
1057 }
1058
1059 /**
1060 * lwmi_om_master_unbind() - Unbind all components of the other mode driver
1061 * @dev: The lenovo-wmi-other driver basic device
1062 *
1063 * Unregister all firmware attribute groups and the HWMON device. Then call
1064 * component_unbind_all to unbind lenovo-wmi-capdata devices from the
1065 * lenovo-wmi-other master driver.
1066 */
lwmi_om_master_unbind(struct device * dev)1067 static void lwmi_om_master_unbind(struct device *dev)
1068 {
1069 struct lwmi_om_priv *priv = dev_get_drvdata(dev);
1070
1071 lwmi_om_fw_attr_remove(priv);
1072
1073 lwmi_om_hwmon_remove(priv);
1074
1075 component_unbind_all(dev, NULL);
1076 }
1077
1078 static const struct component_master_ops lwmi_om_master_ops = {
1079 .bind = lwmi_om_master_bind,
1080 .unbind = lwmi_om_master_unbind,
1081 };
1082
lwmi_other_probe(struct wmi_device * wdev,const void * context)1083 static int lwmi_other_probe(struct wmi_device *wdev, const void *context)
1084 {
1085 struct component_match *master_match = NULL;
1086 struct lwmi_om_priv *priv;
1087
1088 priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
1089 if (!priv)
1090 return -ENOMEM;
1091
1092 /* Sentinel for on-demand ida_free(). */
1093 priv->ida_id = -EIDRM;
1094
1095 priv->wdev = wdev;
1096 dev_set_drvdata(&wdev->dev, priv);
1097
1098 lwmi_cd_match_add_all(&wdev->dev, &master_match);
1099 if (IS_ERR(master_match))
1100 return PTR_ERR(master_match);
1101
1102 return component_master_add_with_match(&wdev->dev, &lwmi_om_master_ops,
1103 master_match);
1104 }
1105
lwmi_other_remove(struct wmi_device * wdev)1106 static void lwmi_other_remove(struct wmi_device *wdev)
1107 {
1108 component_master_del(&wdev->dev, &lwmi_om_master_ops);
1109 }
1110
1111 static const struct wmi_device_id lwmi_other_id_table[] = {
1112 { LENOVO_OTHER_MODE_GUID, NULL },
1113 {}
1114 };
1115
1116 static struct wmi_driver lwmi_other_driver = {
1117 .driver = {
1118 .name = "lenovo_wmi_other",
1119 .probe_type = PROBE_PREFER_ASYNCHRONOUS,
1120 },
1121 .id_table = lwmi_other_id_table,
1122 .probe = lwmi_other_probe,
1123 .remove = lwmi_other_remove,
1124 .no_singleton = true,
1125 };
1126
1127 module_wmi_driver(lwmi_other_driver);
1128
1129 MODULE_IMPORT_NS("LENOVO_WMI_CAPDATA");
1130 MODULE_IMPORT_NS("LENOVO_WMI_HELPERS");
1131 MODULE_DEVICE_TABLE(wmi, lwmi_other_id_table);
1132 MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
1133 MODULE_AUTHOR("Rong Zhang <i@rong.moe>");
1134 MODULE_DESCRIPTION("Lenovo Other Mode WMI Driver");
1135 MODULE_LICENSE("GPL");
1136