| .. | .. |
|---|
| 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); |
|---|