.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * acpi_fan.c - ACPI Fan Driver ($Revision: 29 $) |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> |
---|
5 | 6 | * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> |
---|
6 | | - * |
---|
7 | | - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
---|
8 | | - * |
---|
9 | | - * This program is free software; you can redistribute it and/or modify |
---|
10 | | - * it under the terms of the GNU General Public License as published by |
---|
11 | | - * the Free Software Foundation; either version 2 of the License, or (at |
---|
12 | | - * your option) any later version. |
---|
13 | | - * |
---|
14 | | - * This program is distributed in the hope that it will be useful, but |
---|
15 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
16 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
17 | | - * General Public License for more details. |
---|
18 | | - * |
---|
19 | | - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
---|
20 | 7 | */ |
---|
21 | 8 | |
---|
22 | 9 | #include <linux/kernel.h> |
---|
.. | .. |
---|
29 | 16 | #include <linux/platform_device.h> |
---|
30 | 17 | #include <linux/sort.h> |
---|
31 | 18 | |
---|
| 19 | +#include "fan.h" |
---|
| 20 | + |
---|
32 | 21 | MODULE_AUTHOR("Paul Diefenbaugh"); |
---|
33 | 22 | MODULE_DESCRIPTION("ACPI Fan Driver"); |
---|
34 | 23 | MODULE_LICENSE("GPL"); |
---|
.. | .. |
---|
37 | 26 | static int acpi_fan_remove(struct platform_device *pdev); |
---|
38 | 27 | |
---|
39 | 28 | static const struct acpi_device_id fan_device_ids[] = { |
---|
40 | | - {"PNP0C0B", 0}, |
---|
41 | | - {"INT3404", 0}, |
---|
| 29 | + ACPI_FAN_DEVICE_IDS, |
---|
42 | 30 | {"", 0}, |
---|
43 | 31 | }; |
---|
44 | 32 | MODULE_DEVICE_TABLE(acpi, fan_device_ids); |
---|
.. | .. |
---|
57 | 45 | #define FAN_PM_OPS_PTR NULL |
---|
58 | 46 | #endif |
---|
59 | 47 | |
---|
| 48 | +#define ACPI_FPS_NAME_LEN 20 |
---|
| 49 | + |
---|
60 | 50 | struct acpi_fan_fps { |
---|
61 | 51 | u64 control; |
---|
62 | 52 | u64 trip_point; |
---|
63 | 53 | u64 speed; |
---|
64 | 54 | u64 noise_level; |
---|
65 | 55 | u64 power; |
---|
| 56 | + char name[ACPI_FPS_NAME_LEN]; |
---|
| 57 | + struct device_attribute dev_attr; |
---|
66 | 58 | }; |
---|
67 | 59 | |
---|
68 | 60 | struct acpi_fan_fif { |
---|
.. | .. |
---|
278 | 270 | return fps1->speed - fps2->speed; |
---|
279 | 271 | } |
---|
280 | 272 | |
---|
| 273 | +static ssize_t show_state(struct device *dev, struct device_attribute *attr, char *buf) |
---|
| 274 | +{ |
---|
| 275 | + struct acpi_fan_fps *fps = container_of(attr, struct acpi_fan_fps, dev_attr); |
---|
| 276 | + int count; |
---|
| 277 | + |
---|
| 278 | + if (fps->control == 0xFFFFFFFF || fps->control > 100) |
---|
| 279 | + count = scnprintf(buf, PAGE_SIZE, "not-defined:"); |
---|
| 280 | + else |
---|
| 281 | + count = scnprintf(buf, PAGE_SIZE, "%lld:", fps->control); |
---|
| 282 | + |
---|
| 283 | + if (fps->trip_point == 0xFFFFFFFF || fps->trip_point > 9) |
---|
| 284 | + count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); |
---|
| 285 | + else |
---|
| 286 | + count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->trip_point); |
---|
| 287 | + |
---|
| 288 | + if (fps->speed == 0xFFFFFFFF) |
---|
| 289 | + count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); |
---|
| 290 | + else |
---|
| 291 | + count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->speed); |
---|
| 292 | + |
---|
| 293 | + if (fps->noise_level == 0xFFFFFFFF) |
---|
| 294 | + count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); |
---|
| 295 | + else |
---|
| 296 | + count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->noise_level * 100); |
---|
| 297 | + |
---|
| 298 | + if (fps->power == 0xFFFFFFFF) |
---|
| 299 | + count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined\n"); |
---|
| 300 | + else |
---|
| 301 | + count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld\n", fps->power); |
---|
| 302 | + |
---|
| 303 | + return count; |
---|
| 304 | +} |
---|
| 305 | + |
---|
281 | 306 | static int acpi_fan_get_fps(struct acpi_device *device) |
---|
282 | 307 | { |
---|
283 | 308 | struct acpi_fan *fan = acpi_driver_data(device); |
---|
.. | .. |
---|
308 | 333 | } |
---|
309 | 334 | for (i = 0; i < fan->fps_count; i++) { |
---|
310 | 335 | struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" }; |
---|
311 | | - struct acpi_buffer fps = { sizeof(fan->fps[i]), &fan->fps[i] }; |
---|
| 336 | + struct acpi_buffer fps = { offsetof(struct acpi_fan_fps, name), |
---|
| 337 | + &fan->fps[i] }; |
---|
312 | 338 | status = acpi_extract_package(&obj->package.elements[i + 1], |
---|
313 | 339 | &format, &fps); |
---|
314 | 340 | if (ACPI_FAILURE(status)) { |
---|
315 | 341 | dev_err(&device->dev, "Invalid _FPS element\n"); |
---|
316 | | - break; |
---|
| 342 | + goto err; |
---|
317 | 343 | } |
---|
318 | 344 | } |
---|
319 | 345 | |
---|
320 | 346 | /* sort the state array according to fan speed in increase order */ |
---|
321 | 347 | sort(fan->fps, fan->fps_count, sizeof(*fan->fps), |
---|
322 | 348 | acpi_fan_speed_cmp, NULL); |
---|
| 349 | + |
---|
| 350 | + for (i = 0; i < fan->fps_count; ++i) { |
---|
| 351 | + struct acpi_fan_fps *fps = &fan->fps[i]; |
---|
| 352 | + |
---|
| 353 | + snprintf(fps->name, ACPI_FPS_NAME_LEN, "state%d", i); |
---|
| 354 | + sysfs_attr_init(&fps->dev_attr.attr); |
---|
| 355 | + fps->dev_attr.show = show_state; |
---|
| 356 | + fps->dev_attr.store = NULL; |
---|
| 357 | + fps->dev_attr.attr.name = fps->name; |
---|
| 358 | + fps->dev_attr.attr.mode = 0444; |
---|
| 359 | + status = sysfs_create_file(&device->dev.kobj, &fps->dev_attr.attr); |
---|
| 360 | + if (status) { |
---|
| 361 | + int j; |
---|
| 362 | + |
---|
| 363 | + for (j = 0; j < i; ++j) |
---|
| 364 | + sysfs_remove_file(&device->dev.kobj, &fan->fps[j].dev_attr.attr); |
---|
| 365 | + break; |
---|
| 366 | + } |
---|
| 367 | + } |
---|
323 | 368 | |
---|
324 | 369 | err: |
---|
325 | 370 | kfree(obj); |
---|
.. | .. |
---|
343 | 388 | platform_set_drvdata(pdev, fan); |
---|
344 | 389 | |
---|
345 | 390 | if (acpi_fan_is_acpi4(device)) { |
---|
346 | | - if (acpi_fan_get_fif(device) || acpi_fan_get_fps(device)) |
---|
347 | | - goto end; |
---|
| 391 | + result = acpi_fan_get_fif(device); |
---|
| 392 | + if (result) |
---|
| 393 | + return result; |
---|
| 394 | + |
---|
| 395 | + result = acpi_fan_get_fps(device); |
---|
| 396 | + if (result) |
---|
| 397 | + return result; |
---|
| 398 | + |
---|
348 | 399 | fan->acpi4 = true; |
---|
349 | 400 | } else { |
---|
350 | 401 | result = acpi_device_update_power(device, NULL); |
---|
351 | 402 | if (result) { |
---|
352 | 403 | dev_err(&device->dev, "Failed to set initial power state\n"); |
---|
353 | | - goto end; |
---|
| 404 | + goto err_end; |
---|
354 | 405 | } |
---|
355 | 406 | } |
---|
356 | 407 | |
---|
.. | .. |
---|
363 | 414 | &fan_cooling_ops); |
---|
364 | 415 | if (IS_ERR(cdev)) { |
---|
365 | 416 | result = PTR_ERR(cdev); |
---|
366 | | - goto end; |
---|
| 417 | + goto err_end; |
---|
367 | 418 | } |
---|
368 | 419 | |
---|
369 | 420 | dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id); |
---|
.. | .. |
---|
378 | 429 | result = sysfs_create_link(&cdev->device.kobj, |
---|
379 | 430 | &pdev->dev.kobj, |
---|
380 | 431 | "device"); |
---|
381 | | - if (result) |
---|
| 432 | + if (result) { |
---|
382 | 433 | dev_err(&pdev->dev, "Failed to create sysfs link 'device'\n"); |
---|
| 434 | + goto err_end; |
---|
| 435 | + } |
---|
383 | 436 | |
---|
384 | | -end: |
---|
| 437 | + return 0; |
---|
| 438 | + |
---|
| 439 | +err_end: |
---|
| 440 | + if (fan->acpi4) { |
---|
| 441 | + int i; |
---|
| 442 | + |
---|
| 443 | + for (i = 0; i < fan->fps_count; ++i) |
---|
| 444 | + sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr); |
---|
| 445 | + } |
---|
| 446 | + |
---|
385 | 447 | return result; |
---|
386 | 448 | } |
---|
387 | 449 | |
---|
.. | .. |
---|
389 | 451 | { |
---|
390 | 452 | struct acpi_fan *fan = platform_get_drvdata(pdev); |
---|
391 | 453 | |
---|
| 454 | + if (fan->acpi4) { |
---|
| 455 | + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); |
---|
| 456 | + int i; |
---|
| 457 | + |
---|
| 458 | + for (i = 0; i < fan->fps_count; ++i) |
---|
| 459 | + sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr); |
---|
| 460 | + } |
---|
392 | 461 | sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling"); |
---|
393 | 462 | sysfs_remove_link(&fan->cdev->device.kobj, "device"); |
---|
394 | 463 | thermal_cooling_device_unregister(fan->cdev); |
---|