Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/acpi/fan_core.c
49760 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* fan_core.c - ACPI Fan core Driver
4
*
5
* Copyright (C) 2001, 2002 Andy Grover <[email protected]>
6
* Copyright (C) 2001, 2002 Paul Diefenbaugh <[email protected]>
7
* Copyright (C) 2022 Intel Corporation. All rights reserved.
8
*/
9
10
#include <linux/bits.h>
11
#include <linux/kernel.h>
12
#include <linux/limits.h>
13
#include <linux/math.h>
14
#include <linux/math64.h>
15
#include <linux/module.h>
16
#include <linux/init.h>
17
#include <linux/types.h>
18
#include <linux/uaccess.h>
19
#include <linux/uuid.h>
20
#include <linux/thermal.h>
21
#include <linux/acpi.h>
22
#include <linux/platform_device.h>
23
#include <linux/sort.h>
24
25
#include "fan.h"
26
27
#define ACPI_FAN_NOTIFY_STATE_CHANGED 0x80
28
29
/*
30
* Defined inside the "Fan Noise Signal" section at
31
* https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/design-guide.
32
*/
33
static const guid_t acpi_fan_microsoft_guid = GUID_INIT(0xA7611840, 0x99FE, 0x41AE, 0xA4, 0x88,
34
0x35, 0xC7, 0x59, 0x26, 0xC8, 0xEB);
35
#define ACPI_FAN_DSM_GET_TRIP_POINT_GRANULARITY 1
36
#define ACPI_FAN_DSM_SET_TRIP_POINTS 2
37
#define ACPI_FAN_DSM_GET_OPERATING_RANGES 3
38
39
/*
40
* Ensures that fans with a very low trip point granularity
41
* do not send too many notifications.
42
*/
43
static uint min_trip_distance = 100;
44
module_param(min_trip_distance, uint, 0);
45
MODULE_PARM_DESC(min_trip_distance, "Minimum distance between fan speed trip points in RPM");
46
47
static const struct acpi_device_id fan_device_ids[] = {
48
ACPI_FAN_DEVICE_IDS,
49
{"", 0},
50
};
51
MODULE_DEVICE_TABLE(acpi, fan_device_ids);
52
53
/* thermal cooling device callbacks */
54
static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
55
*state)
56
{
57
struct acpi_device *device = cdev->devdata;
58
struct acpi_fan *fan = acpi_driver_data(device);
59
60
if (fan->acpi4) {
61
if (fan->fif.fine_grain_ctrl)
62
*state = 100 / fan->fif.step_size;
63
else
64
*state = fan->fps_count - 1;
65
} else {
66
*state = 1;
67
}
68
69
return 0;
70
}
71
72
int acpi_fan_get_fst(acpi_handle handle, struct acpi_fan_fst *fst)
73
{
74
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
75
union acpi_object *obj;
76
acpi_status status;
77
int ret = 0;
78
79
status = acpi_evaluate_object(handle, "_FST", NULL, &buffer);
80
if (ACPI_FAILURE(status))
81
return -EIO;
82
83
obj = buffer.pointer;
84
if (!obj)
85
return -ENODATA;
86
87
if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 3) {
88
ret = -EPROTO;
89
goto err;
90
}
91
92
if (obj->package.elements[0].type != ACPI_TYPE_INTEGER ||
93
obj->package.elements[1].type != ACPI_TYPE_INTEGER ||
94
obj->package.elements[2].type != ACPI_TYPE_INTEGER) {
95
ret = -EPROTO;
96
goto err;
97
}
98
99
fst->revision = obj->package.elements[0].integer.value;
100
fst->control = obj->package.elements[1].integer.value;
101
fst->speed = obj->package.elements[2].integer.value;
102
103
err:
104
kfree(obj);
105
return ret;
106
}
107
108
static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
109
{
110
struct acpi_fan *fan = acpi_driver_data(device);
111
struct acpi_fan_fst fst;
112
int status, i;
113
114
status = acpi_fan_get_fst(device->handle, &fst);
115
if (status)
116
return status;
117
118
if (fan->fif.fine_grain_ctrl) {
119
/* This control should be same what we set using _FSL by spec */
120
if (fst.control > 100) {
121
dev_dbg(&device->dev, "Invalid control value returned\n");
122
goto match_fps;
123
}
124
125
*state = (int) fst.control / fan->fif.step_size;
126
return 0;
127
}
128
129
match_fps:
130
for (i = 0; i < fan->fps_count; i++) {
131
if (fst.control == fan->fps[i].control)
132
break;
133
}
134
if (i == fan->fps_count) {
135
dev_dbg(&device->dev, "No matching fps control value\n");
136
return -EINVAL;
137
}
138
139
*state = i;
140
141
return status;
142
}
143
144
static int fan_get_state(struct acpi_device *device, unsigned long *state)
145
{
146
int result;
147
int acpi_state = ACPI_STATE_D0;
148
149
result = acpi_device_update_power(device, &acpi_state);
150
if (result)
151
return result;
152
153
*state = acpi_state == ACPI_STATE_D3_COLD
154
|| acpi_state == ACPI_STATE_D3_HOT ?
155
0 : (acpi_state == ACPI_STATE_D0 ? 1 : -1);
156
return 0;
157
}
158
159
static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
160
*state)
161
{
162
struct acpi_device *device = cdev->devdata;
163
struct acpi_fan *fan = acpi_driver_data(device);
164
165
if (fan->acpi4)
166
return fan_get_state_acpi4(device, state);
167
else
168
return fan_get_state(device, state);
169
}
170
171
static int fan_set_state(struct acpi_device *device, unsigned long state)
172
{
173
if (state != 0 && state != 1)
174
return -EINVAL;
175
176
return acpi_device_set_power(device,
177
state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD);
178
}
179
180
static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state)
181
{
182
struct acpi_fan *fan = acpi_driver_data(device);
183
acpi_status status;
184
u64 value = state;
185
int max_state;
186
187
if (fan->fif.fine_grain_ctrl)
188
max_state = 100 / fan->fif.step_size;
189
else
190
max_state = fan->fps_count - 1;
191
192
if (state > max_state)
193
return -EINVAL;
194
195
if (fan->fif.fine_grain_ctrl) {
196
value *= fan->fif.step_size;
197
/* Spec allows compensate the last step only */
198
if (value + fan->fif.step_size > 100)
199
value = 100;
200
} else {
201
value = fan->fps[state].control;
202
}
203
204
status = acpi_execute_simple_method(device->handle, "_FSL", value);
205
if (ACPI_FAILURE(status)) {
206
dev_dbg(&device->dev, "Failed to set state by _FSL\n");
207
return -ENODEV;
208
}
209
210
return 0;
211
}
212
213
static int
214
fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
215
{
216
struct acpi_device *device = cdev->devdata;
217
struct acpi_fan *fan = acpi_driver_data(device);
218
219
if (fan->acpi4)
220
return fan_set_state_acpi4(device, state);
221
else
222
return fan_set_state(device, state);
223
}
224
225
static const struct thermal_cooling_device_ops fan_cooling_ops = {
226
.get_max_state = fan_get_max_state,
227
.get_cur_state = fan_get_cur_state,
228
.set_cur_state = fan_set_cur_state,
229
};
230
231
/* --------------------------------------------------------------------------
232
* Driver Interface
233
* --------------------------------------------------------------------------
234
*/
235
236
static int acpi_fan_get_fif(struct acpi_device *device)
237
{
238
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
239
struct acpi_fan *fan = acpi_driver_data(device);
240
struct acpi_buffer format = { sizeof("NNNN"), "NNNN" };
241
u64 fields[4];
242
struct acpi_buffer fif = { sizeof(fields), fields };
243
union acpi_object *obj;
244
acpi_status status;
245
246
status = acpi_evaluate_object(device->handle, "_FIF", NULL, &buffer);
247
if (ACPI_FAILURE(status))
248
return status;
249
250
obj = buffer.pointer;
251
if (!obj || obj->type != ACPI_TYPE_PACKAGE) {
252
dev_err(&device->dev, "Invalid _FIF data\n");
253
status = -EINVAL;
254
goto err;
255
}
256
257
status = acpi_extract_package(obj, &format, &fif);
258
if (ACPI_FAILURE(status)) {
259
dev_err(&device->dev, "Invalid _FIF element\n");
260
status = -EINVAL;
261
goto err;
262
}
263
264
fan->fif.revision = fields[0];
265
fan->fif.fine_grain_ctrl = fields[1];
266
fan->fif.step_size = fields[2];
267
fan->fif.low_speed_notification = fields[3];
268
269
/* If there is a bug in step size and set as 0, change to 1 */
270
if (!fan->fif.step_size)
271
fan->fif.step_size = 1;
272
/* If step size > 9, change to 9 (by spec valid values 1-9) */
273
else if (fan->fif.step_size > 9)
274
fan->fif.step_size = 9;
275
err:
276
kfree(obj);
277
return status;
278
}
279
280
static int acpi_fan_speed_cmp(const void *a, const void *b)
281
{
282
const struct acpi_fan_fps *fps1 = a;
283
const struct acpi_fan_fps *fps2 = b;
284
return fps1->speed - fps2->speed;
285
}
286
287
static int acpi_fan_get_fps(struct acpi_device *device)
288
{
289
struct acpi_fan *fan = acpi_driver_data(device);
290
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
291
union acpi_object *obj;
292
acpi_status status;
293
int i;
294
295
status = acpi_evaluate_object(device->handle, "_FPS", NULL, &buffer);
296
if (ACPI_FAILURE(status))
297
return status;
298
299
obj = buffer.pointer;
300
if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count < 2) {
301
dev_err(&device->dev, "Invalid _FPS data\n");
302
status = -EINVAL;
303
goto err;
304
}
305
306
fan->fps_count = obj->package.count - 1; /* minus revision field */
307
fan->fps = devm_kcalloc(&device->dev,
308
fan->fps_count, sizeof(struct acpi_fan_fps),
309
GFP_KERNEL);
310
if (!fan->fps) {
311
dev_err(&device->dev, "Not enough memory\n");
312
status = -ENOMEM;
313
goto err;
314
}
315
for (i = 0; i < fan->fps_count; i++) {
316
struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" };
317
struct acpi_buffer fps = { offsetof(struct acpi_fan_fps, name),
318
&fan->fps[i] };
319
status = acpi_extract_package(&obj->package.elements[i + 1],
320
&format, &fps);
321
if (ACPI_FAILURE(status)) {
322
dev_err(&device->dev, "Invalid _FPS element\n");
323
goto err;
324
}
325
}
326
327
/* sort the state array according to fan speed in increase order */
328
sort(fan->fps, fan->fps_count, sizeof(*fan->fps),
329
acpi_fan_speed_cmp, NULL);
330
331
err:
332
kfree(obj);
333
return status;
334
}
335
336
static int acpi_fan_dsm_init(struct device *dev)
337
{
338
union acpi_object dummy = {
339
.package = {
340
.type = ACPI_TYPE_PACKAGE,
341
.count = 0,
342
.elements = NULL,
343
},
344
};
345
struct acpi_fan *fan = dev_get_drvdata(dev);
346
union acpi_object *obj;
347
int ret = 0;
348
349
if (!acpi_check_dsm(fan->handle, &acpi_fan_microsoft_guid, 0,
350
BIT(ACPI_FAN_DSM_GET_TRIP_POINT_GRANULARITY) |
351
BIT(ACPI_FAN_DSM_SET_TRIP_POINTS)))
352
return 0;
353
354
dev_info(dev, "Using Microsoft fan extensions\n");
355
356
obj = acpi_evaluate_dsm_typed(fan->handle, &acpi_fan_microsoft_guid, 0,
357
ACPI_FAN_DSM_GET_TRIP_POINT_GRANULARITY, &dummy,
358
ACPI_TYPE_INTEGER);
359
if (!obj)
360
return -EIO;
361
362
if (obj->integer.value > U32_MAX)
363
ret = -EOVERFLOW;
364
else
365
fan->fan_trip_granularity = obj->integer.value;
366
367
kfree(obj);
368
369
return ret;
370
}
371
372
static int acpi_fan_dsm_set_trip_points(struct device *dev, u64 upper, u64 lower)
373
{
374
union acpi_object args[2] = {
375
{
376
.integer = {
377
.type = ACPI_TYPE_INTEGER,
378
.value = lower,
379
},
380
},
381
{
382
.integer = {
383
.type = ACPI_TYPE_INTEGER,
384
.value = upper,
385
},
386
},
387
};
388
struct acpi_fan *fan = dev_get_drvdata(dev);
389
union acpi_object in = {
390
.package = {
391
.type = ACPI_TYPE_PACKAGE,
392
.count = ARRAY_SIZE(args),
393
.elements = args,
394
},
395
};
396
union acpi_object *obj;
397
398
obj = acpi_evaluate_dsm(fan->handle, &acpi_fan_microsoft_guid, 0,
399
ACPI_FAN_DSM_SET_TRIP_POINTS, &in);
400
kfree(obj);
401
402
return 0;
403
}
404
405
static int acpi_fan_dsm_start(struct device *dev)
406
{
407
struct acpi_fan *fan = dev_get_drvdata(dev);
408
int ret;
409
410
if (!fan->fan_trip_granularity)
411
return 0;
412
413
/*
414
* Some firmware implementations only update the values returned by the
415
* _FST control method when a notification is received. This usually
416
* works with Microsoft Windows as setting up trip points will keep
417
* triggering said notifications, but will cause issues when using _FST
418
* without the Microsoft-specific trip point extension.
419
*
420
* Because of this, an initial notification needs to be triggered to
421
* start the cycle of trip points updates. This is achieved by setting
422
* the trip points sequencially to two separate ranges. As by the
423
* Microsoft specification the firmware should trigger a notification
424
* immediately if the fan speed is outside the trip point range. This
425
* _should_ result in at least one notification as both ranges do not
426
* overlap, meaning that the current fan speed needs to be outside at
427
* least one range.
428
*/
429
ret = acpi_fan_dsm_set_trip_points(dev, fan->fan_trip_granularity, 0);
430
if (ret < 0)
431
return ret;
432
433
return acpi_fan_dsm_set_trip_points(dev, fan->fan_trip_granularity * 3,
434
fan->fan_trip_granularity * 2);
435
}
436
437
static int acpi_fan_dsm_update_trips_points(struct device *dev, struct acpi_fan_fst *fst)
438
{
439
struct acpi_fan *fan = dev_get_drvdata(dev);
440
u64 upper, lower;
441
442
if (!fan->fan_trip_granularity)
443
return 0;
444
445
if (!acpi_fan_speed_valid(fst->speed))
446
return -EINVAL;
447
448
upper = roundup_u64(fst->speed + min_trip_distance, fan->fan_trip_granularity);
449
if (fst->speed <= min_trip_distance) {
450
lower = 0;
451
} else {
452
/*
453
* Valid fan speed values cannot be larger than 32 bit, so
454
* we can safely assume that no overflow will happen here.
455
*/
456
lower = rounddown((u32)fst->speed - min_trip_distance, fan->fan_trip_granularity);
457
}
458
459
return acpi_fan_dsm_set_trip_points(dev, upper, lower);
460
}
461
462
static void acpi_fan_notify_handler(acpi_handle handle, u32 event, void *context)
463
{
464
struct device *dev = context;
465
struct acpi_fan_fst fst;
466
int ret;
467
468
switch (event) {
469
case ACPI_FAN_NOTIFY_STATE_CHANGED:
470
/*
471
* The ACPI specification says that we must evaluate _FST when we
472
* receive an ACPI event indicating that the fan state has changed.
473
*/
474
ret = acpi_fan_get_fst(handle, &fst);
475
if (ret < 0) {
476
dev_err(dev, "Error retrieving current fan status: %d\n", ret);
477
} else {
478
ret = acpi_fan_dsm_update_trips_points(dev, &fst);
479
if (ret < 0)
480
dev_err(dev, "Failed to update trip points: %d\n", ret);
481
}
482
483
acpi_fan_notify_hwmon(dev);
484
acpi_bus_generate_netlink_event("fan", dev_name(dev), event, 0);
485
break;
486
default:
487
dev_dbg(dev, "Unsupported ACPI notification 0x%x\n", event);
488
break;
489
}
490
}
491
492
static void acpi_fan_notify_remove(void *data)
493
{
494
struct acpi_fan *fan = data;
495
496
acpi_remove_notify_handler(fan->handle, ACPI_DEVICE_NOTIFY, acpi_fan_notify_handler);
497
}
498
499
static int devm_acpi_fan_notify_init(struct device *dev)
500
{
501
struct acpi_fan *fan = dev_get_drvdata(dev);
502
acpi_status status;
503
504
status = acpi_install_notify_handler(fan->handle, ACPI_DEVICE_NOTIFY,
505
acpi_fan_notify_handler, dev);
506
if (ACPI_FAILURE(status))
507
return -EIO;
508
509
return devm_add_action_or_reset(dev, acpi_fan_notify_remove, fan);
510
}
511
512
static int acpi_fan_probe(struct platform_device *pdev)
513
{
514
int result = 0;
515
struct thermal_cooling_device *cdev;
516
struct acpi_fan *fan;
517
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
518
char *name;
519
520
if (!device)
521
return -ENODEV;
522
523
fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL);
524
if (!fan) {
525
dev_err(&device->dev, "No memory for fan\n");
526
return -ENOMEM;
527
}
528
529
fan->handle = device->handle;
530
device->driver_data = fan;
531
platform_set_drvdata(pdev, fan);
532
533
if (acpi_has_method(device->handle, "_FST")) {
534
fan->has_fst = true;
535
fan->acpi4 = acpi_has_method(device->handle, "_FIF") &&
536
acpi_has_method(device->handle, "_FPS") &&
537
acpi_has_method(device->handle, "_FSL");
538
}
539
540
if (fan->acpi4) {
541
result = acpi_fan_get_fif(device);
542
if (result)
543
return result;
544
545
result = acpi_fan_get_fps(device);
546
if (result)
547
return result;
548
}
549
550
if (fan->has_fst) {
551
result = acpi_fan_dsm_init(&pdev->dev);
552
if (result)
553
return result;
554
555
result = devm_acpi_fan_create_hwmon(&pdev->dev);
556
if (result)
557
return result;
558
559
result = devm_acpi_fan_notify_init(&pdev->dev);
560
if (result)
561
return result;
562
563
result = acpi_fan_dsm_start(&pdev->dev);
564
if (result) {
565
dev_err(&pdev->dev, "Failed to start Microsoft fan extensions\n");
566
return result;
567
}
568
569
result = acpi_fan_create_attributes(device);
570
if (result)
571
return result;
572
}
573
574
if (!fan->acpi4) {
575
result = acpi_device_update_power(device, NULL);
576
if (result) {
577
dev_err(&device->dev, "Failed to set initial power state\n");
578
goto err_end;
579
}
580
}
581
582
if (!strncmp(pdev->name, "PNP0C0B", strlen("PNP0C0B")))
583
name = "Fan";
584
else
585
name = acpi_device_bid(device);
586
587
cdev = thermal_cooling_device_register(name, device,
588
&fan_cooling_ops);
589
if (IS_ERR(cdev)) {
590
result = PTR_ERR(cdev);
591
goto err_end;
592
}
593
594
dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id);
595
596
fan->cdev = cdev;
597
result = sysfs_create_link(&pdev->dev.kobj,
598
&cdev->device.kobj,
599
"thermal_cooling");
600
if (result) {
601
dev_err(&pdev->dev, "Failed to create sysfs link 'thermal_cooling'\n");
602
goto err_unregister;
603
}
604
605
result = sysfs_create_link(&cdev->device.kobj,
606
&pdev->dev.kobj,
607
"device");
608
if (result) {
609
dev_err(&pdev->dev, "Failed to create sysfs link 'device'\n");
610
goto err_remove_link;
611
}
612
613
return 0;
614
615
err_remove_link:
616
sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling");
617
err_unregister:
618
thermal_cooling_device_unregister(cdev);
619
err_end:
620
if (fan->has_fst)
621
acpi_fan_delete_attributes(device);
622
623
return result;
624
}
625
626
static void acpi_fan_remove(struct platform_device *pdev)
627
{
628
struct acpi_fan *fan = platform_get_drvdata(pdev);
629
630
if (fan->has_fst) {
631
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
632
633
acpi_fan_delete_attributes(device);
634
}
635
sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling");
636
sysfs_remove_link(&fan->cdev->device.kobj, "device");
637
thermal_cooling_device_unregister(fan->cdev);
638
}
639
640
#ifdef CONFIG_PM_SLEEP
641
static int acpi_fan_suspend(struct device *dev)
642
{
643
struct acpi_fan *fan = dev_get_drvdata(dev);
644
if (fan->acpi4)
645
return 0;
646
647
acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D0);
648
649
return AE_OK;
650
}
651
652
static int acpi_fan_resume(struct device *dev)
653
{
654
struct acpi_fan *fan = dev_get_drvdata(dev);
655
int result;
656
657
if (fan->has_fst) {
658
result = acpi_fan_dsm_start(dev);
659
if (result)
660
dev_err(dev, "Failed to start Microsoft fan extensions: %d\n", result);
661
}
662
663
if (fan->acpi4)
664
return 0;
665
666
result = acpi_device_update_power(ACPI_COMPANION(dev), NULL);
667
if (result)
668
dev_err(dev, "Error updating fan power state\n");
669
670
return result;
671
}
672
673
static const struct dev_pm_ops acpi_fan_pm = {
674
.resume = acpi_fan_resume,
675
.freeze = acpi_fan_suspend,
676
.thaw = acpi_fan_resume,
677
.restore = acpi_fan_resume,
678
};
679
#define FAN_PM_OPS_PTR (&acpi_fan_pm)
680
681
#else
682
683
#define FAN_PM_OPS_PTR NULL
684
685
#endif
686
687
static struct platform_driver acpi_fan_driver = {
688
.probe = acpi_fan_probe,
689
.remove = acpi_fan_remove,
690
.driver = {
691
.name = "acpi-fan",
692
.acpi_match_table = fan_device_ids,
693
.pm = FAN_PM_OPS_PTR,
694
},
695
};
696
697
module_platform_driver(acpi_fan_driver);
698
699
MODULE_AUTHOR("Paul Diefenbaugh");
700
MODULE_DESCRIPTION("ACPI Fan Driver");
701
MODULE_LICENSE("GPL");
702
703