]> Git Repo - linux.git/blob - drivers/acpi/fan_core.c
Merge tag 'hardening-v6.13-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / drivers / acpi / fan_core.c
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/kernel.h>
11 #include <linux/module.h>
12 #include <linux/init.h>
13 #include <linux/types.h>
14 #include <linux/uaccess.h>
15 #include <linux/thermal.h>
16 #include <linux/acpi.h>
17 #include <linux/platform_device.h>
18 #include <linux/sort.h>
19
20 #include "fan.h"
21
22 static const struct acpi_device_id fan_device_ids[] = {
23         ACPI_FAN_DEVICE_IDS,
24         {"", 0},
25 };
26 MODULE_DEVICE_TABLE(acpi, fan_device_ids);
27
28 /* thermal cooling device callbacks */
29 static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
30                              *state)
31 {
32         struct acpi_device *device = cdev->devdata;
33         struct acpi_fan *fan = acpi_driver_data(device);
34
35         if (fan->acpi4) {
36                 if (fan->fif.fine_grain_ctrl)
37                         *state = 100 / fan->fif.step_size;
38                 else
39                         *state = fan->fps_count - 1;
40         } else {
41                 *state = 1;
42         }
43
44         return 0;
45 }
46
47 int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst)
48 {
49         struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
50         union acpi_object *obj;
51         acpi_status status;
52         int ret = 0;
53
54         status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer);
55         if (ACPI_FAILURE(status)) {
56                 dev_err(&device->dev, "Get fan state failed\n");
57                 return -ENODEV;
58         }
59
60         obj = buffer.pointer;
61         if (!obj || obj->type != ACPI_TYPE_PACKAGE ||
62             obj->package.count != 3 ||
63             obj->package.elements[1].type != ACPI_TYPE_INTEGER) {
64                 dev_err(&device->dev, "Invalid _FST data\n");
65                 ret = -EINVAL;
66                 goto err;
67         }
68
69         fst->revision = obj->package.elements[0].integer.value;
70         fst->control = obj->package.elements[1].integer.value;
71         fst->speed = obj->package.elements[2].integer.value;
72
73 err:
74         kfree(obj);
75         return ret;
76 }
77
78 static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
79 {
80         struct acpi_fan *fan = acpi_driver_data(device);
81         struct acpi_fan_fst fst;
82         int status, i;
83
84         status = acpi_fan_get_fst(device, &fst);
85         if (status)
86                 return status;
87
88         if (fan->fif.fine_grain_ctrl) {
89                 /* This control should be same what we set using _FSL by spec */
90                 if (fst.control > 100) {
91                         dev_dbg(&device->dev, "Invalid control value returned\n");
92                         goto match_fps;
93                 }
94
95                 *state = (int) fst.control / fan->fif.step_size;
96                 return 0;
97         }
98
99 match_fps:
100         for (i = 0; i < fan->fps_count; i++) {
101                 if (fst.control == fan->fps[i].control)
102                         break;
103         }
104         if (i == fan->fps_count) {
105                 dev_dbg(&device->dev, "Invalid control value returned\n");
106                 return -EINVAL;
107         }
108
109         *state = i;
110
111         return status;
112 }
113
114 static int fan_get_state(struct acpi_device *device, unsigned long *state)
115 {
116         int result;
117         int acpi_state = ACPI_STATE_D0;
118
119         result = acpi_device_update_power(device, &acpi_state);
120         if (result)
121                 return result;
122
123         *state = acpi_state == ACPI_STATE_D3_COLD
124                         || acpi_state == ACPI_STATE_D3_HOT ?
125                 0 : (acpi_state == ACPI_STATE_D0 ? 1 : -1);
126         return 0;
127 }
128
129 static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
130                              *state)
131 {
132         struct acpi_device *device = cdev->devdata;
133         struct acpi_fan *fan = acpi_driver_data(device);
134
135         if (fan->acpi4)
136                 return fan_get_state_acpi4(device, state);
137         else
138                 return fan_get_state(device, state);
139 }
140
141 static int fan_set_state(struct acpi_device *device, unsigned long state)
142 {
143         if (state != 0 && state != 1)
144                 return -EINVAL;
145
146         return acpi_device_set_power(device,
147                                      state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD);
148 }
149
150 static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state)
151 {
152         struct acpi_fan *fan = acpi_driver_data(device);
153         acpi_status status;
154         u64 value = state;
155         int max_state;
156
157         if (fan->fif.fine_grain_ctrl)
158                 max_state = 100 / fan->fif.step_size;
159         else
160                 max_state = fan->fps_count - 1;
161
162         if (state > max_state)
163                 return -EINVAL;
164
165         if (fan->fif.fine_grain_ctrl) {
166                 value *= fan->fif.step_size;
167                 /* Spec allows compensate the last step only */
168                 if (value + fan->fif.step_size > 100)
169                         value = 100;
170         } else {
171                 value = fan->fps[state].control;
172         }
173
174         status = acpi_execute_simple_method(device->handle, "_FSL", value);
175         if (ACPI_FAILURE(status)) {
176                 dev_dbg(&device->dev, "Failed to set state by _FSL\n");
177                 return -ENODEV;
178         }
179
180         return 0;
181 }
182
183 static int
184 fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
185 {
186         struct acpi_device *device = cdev->devdata;
187         struct acpi_fan *fan = acpi_driver_data(device);
188
189         if (fan->acpi4)
190                 return fan_set_state_acpi4(device, state);
191         else
192                 return fan_set_state(device, state);
193 }
194
195 static const struct thermal_cooling_device_ops fan_cooling_ops = {
196         .get_max_state = fan_get_max_state,
197         .get_cur_state = fan_get_cur_state,
198         .set_cur_state = fan_set_cur_state,
199 };
200
201 /* --------------------------------------------------------------------------
202  *                               Driver Interface
203  * --------------------------------------------------------------------------
204 */
205
206 static bool acpi_fan_is_acpi4(struct acpi_device *device)
207 {
208         return acpi_has_method(device->handle, "_FIF") &&
209                acpi_has_method(device->handle, "_FPS") &&
210                acpi_has_method(device->handle, "_FSL") &&
211                acpi_has_method(device->handle, "_FST");
212 }
213
214 static int acpi_fan_get_fif(struct acpi_device *device)
215 {
216         struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
217         struct acpi_fan *fan = acpi_driver_data(device);
218         struct acpi_buffer format = { sizeof("NNNN"), "NNNN" };
219         u64 fields[4];
220         struct acpi_buffer fif = { sizeof(fields), fields };
221         union acpi_object *obj;
222         acpi_status status;
223
224         status = acpi_evaluate_object(device->handle, "_FIF", NULL, &buffer);
225         if (ACPI_FAILURE(status))
226                 return status;
227
228         obj = buffer.pointer;
229         if (!obj || obj->type != ACPI_TYPE_PACKAGE) {
230                 dev_err(&device->dev, "Invalid _FIF data\n");
231                 status = -EINVAL;
232                 goto err;
233         }
234
235         status = acpi_extract_package(obj, &format, &fif);
236         if (ACPI_FAILURE(status)) {
237                 dev_err(&device->dev, "Invalid _FIF element\n");
238                 status = -EINVAL;
239                 goto err;
240         }
241
242         fan->fif.revision = fields[0];
243         fan->fif.fine_grain_ctrl = fields[1];
244         fan->fif.step_size = fields[2];
245         fan->fif.low_speed_notification = fields[3];
246
247         /* If there is a bug in step size and set as 0, change to 1 */
248         if (!fan->fif.step_size)
249                 fan->fif.step_size = 1;
250         /* If step size > 9, change to 9 (by spec valid values 1-9) */
251         else if (fan->fif.step_size > 9)
252                 fan->fif.step_size = 9;
253 err:
254         kfree(obj);
255         return status;
256 }
257
258 static int acpi_fan_speed_cmp(const void *a, const void *b)
259 {
260         const struct acpi_fan_fps *fps1 = a;
261         const struct acpi_fan_fps *fps2 = b;
262         return fps1->speed - fps2->speed;
263 }
264
265 static int acpi_fan_get_fps(struct acpi_device *device)
266 {
267         struct acpi_fan *fan = acpi_driver_data(device);
268         struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
269         union acpi_object *obj;
270         acpi_status status;
271         int i;
272
273         status = acpi_evaluate_object(device->handle, "_FPS", NULL, &buffer);
274         if (ACPI_FAILURE(status))
275                 return status;
276
277         obj = buffer.pointer;
278         if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count < 2) {
279                 dev_err(&device->dev, "Invalid _FPS data\n");
280                 status = -EINVAL;
281                 goto err;
282         }
283
284         fan->fps_count = obj->package.count - 1; /* minus revision field */
285         fan->fps = devm_kcalloc(&device->dev,
286                                 fan->fps_count, sizeof(struct acpi_fan_fps),
287                                 GFP_KERNEL);
288         if (!fan->fps) {
289                 dev_err(&device->dev, "Not enough memory\n");
290                 status = -ENOMEM;
291                 goto err;
292         }
293         for (i = 0; i < fan->fps_count; i++) {
294                 struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" };
295                 struct acpi_buffer fps = { offsetof(struct acpi_fan_fps, name),
296                                                 &fan->fps[i] };
297                 status = acpi_extract_package(&obj->package.elements[i + 1],
298                                               &format, &fps);
299                 if (ACPI_FAILURE(status)) {
300                         dev_err(&device->dev, "Invalid _FPS element\n");
301                         goto err;
302                 }
303         }
304
305         /* sort the state array according to fan speed in increase order */
306         sort(fan->fps, fan->fps_count, sizeof(*fan->fps),
307              acpi_fan_speed_cmp, NULL);
308
309 err:
310         kfree(obj);
311         return status;
312 }
313
314 static int acpi_fan_probe(struct platform_device *pdev)
315 {
316         int result = 0;
317         struct thermal_cooling_device *cdev;
318         struct acpi_fan *fan;
319         struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
320         char *name;
321
322         fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL);
323         if (!fan) {
324                 dev_err(&device->dev, "No memory for fan\n");
325                 return -ENOMEM;
326         }
327         device->driver_data = fan;
328         platform_set_drvdata(pdev, fan);
329
330         if (acpi_fan_is_acpi4(device)) {
331                 result = acpi_fan_get_fif(device);
332                 if (result)
333                         return result;
334
335                 result = acpi_fan_get_fps(device);
336                 if (result)
337                         return result;
338
339                 result = devm_acpi_fan_create_hwmon(device);
340                 if (result)
341                         return result;
342
343                 result = acpi_fan_create_attributes(device);
344                 if (result)
345                         return result;
346
347                 fan->acpi4 = true;
348         } else {
349                 result = acpi_device_update_power(device, NULL);
350                 if (result) {
351                         dev_err(&device->dev, "Failed to set initial power state\n");
352                         goto err_end;
353                 }
354         }
355
356         if (!strncmp(pdev->name, "PNP0C0B", strlen("PNP0C0B")))
357                 name = "Fan";
358         else
359                 name = acpi_device_bid(device);
360
361         cdev = thermal_cooling_device_register(name, device,
362                                                 &fan_cooling_ops);
363         if (IS_ERR(cdev)) {
364                 result = PTR_ERR(cdev);
365                 goto err_end;
366         }
367
368         dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id);
369
370         fan->cdev = cdev;
371         result = sysfs_create_link(&pdev->dev.kobj,
372                                    &cdev->device.kobj,
373                                    "thermal_cooling");
374         if (result)
375                 dev_err(&pdev->dev, "Failed to create sysfs link 'thermal_cooling'\n");
376
377         result = sysfs_create_link(&cdev->device.kobj,
378                                    &pdev->dev.kobj,
379                                    "device");
380         if (result) {
381                 dev_err(&pdev->dev, "Failed to create sysfs link 'device'\n");
382                 goto err_end;
383         }
384
385         return 0;
386
387 err_end:
388         if (fan->acpi4)
389                 acpi_fan_delete_attributes(device);
390
391         return result;
392 }
393
394 static void acpi_fan_remove(struct platform_device *pdev)
395 {
396         struct acpi_fan *fan = platform_get_drvdata(pdev);
397
398         if (fan->acpi4) {
399                 struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
400
401                 acpi_fan_delete_attributes(device);
402         }
403         sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling");
404         sysfs_remove_link(&fan->cdev->device.kobj, "device");
405         thermal_cooling_device_unregister(fan->cdev);
406 }
407
408 #ifdef CONFIG_PM_SLEEP
409 static int acpi_fan_suspend(struct device *dev)
410 {
411         struct acpi_fan *fan = dev_get_drvdata(dev);
412         if (fan->acpi4)
413                 return 0;
414
415         acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D0);
416
417         return AE_OK;
418 }
419
420 static int acpi_fan_resume(struct device *dev)
421 {
422         int result;
423         struct acpi_fan *fan = dev_get_drvdata(dev);
424
425         if (fan->acpi4)
426                 return 0;
427
428         result = acpi_device_update_power(ACPI_COMPANION(dev), NULL);
429         if (result)
430                 dev_err(dev, "Error updating fan power state\n");
431
432         return result;
433 }
434
435 static const struct dev_pm_ops acpi_fan_pm = {
436         .resume = acpi_fan_resume,
437         .freeze = acpi_fan_suspend,
438         .thaw = acpi_fan_resume,
439         .restore = acpi_fan_resume,
440 };
441 #define FAN_PM_OPS_PTR (&acpi_fan_pm)
442
443 #else
444
445 #define FAN_PM_OPS_PTR NULL
446
447 #endif
448
449 static struct platform_driver acpi_fan_driver = {
450         .probe = acpi_fan_probe,
451         .remove = acpi_fan_remove,
452         .driver = {
453                 .name = "acpi-fan",
454                 .acpi_match_table = fan_device_ids,
455                 .pm = FAN_PM_OPS_PTR,
456         },
457 };
458
459 module_platform_driver(acpi_fan_driver);
460
461 MODULE_AUTHOR("Paul Diefenbaugh");
462 MODULE_DESCRIPTION("ACPI Fan Driver");
463 MODULE_LICENSE("GPL");
This page took 0.059019 seconds and 4 git commands to generate.