linux/drivers/acpi/fan.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  acpi_fan.c - ACPI Fan Driver ($Revision: 29 $)
   4 *
   5 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
   6 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
   7 */
   8
   9#include <linux/kernel.h>
  10#include <linux/module.h>
  11#include <linux/init.h>
  12#include <linux/types.h>
  13#include <linux/uaccess.h>
  14#include <linux/thermal.h>
  15#include <linux/acpi.h>
  16#include <linux/platform_device.h>
  17#include <linux/sort.h>
  18
  19MODULE_AUTHOR("Paul Diefenbaugh");
  20MODULE_DESCRIPTION("ACPI Fan Driver");
  21MODULE_LICENSE("GPL");
  22
  23static int acpi_fan_probe(struct platform_device *pdev);
  24static int acpi_fan_remove(struct platform_device *pdev);
  25
  26static const struct acpi_device_id fan_device_ids[] = {
  27        {"PNP0C0B", 0},
  28        {"INT3404", 0},
  29        {"INTC1044", 0},
  30        {"", 0},
  31};
  32MODULE_DEVICE_TABLE(acpi, fan_device_ids);
  33
  34#ifdef CONFIG_PM_SLEEP
  35static int acpi_fan_suspend(struct device *dev);
  36static int acpi_fan_resume(struct device *dev);
  37static const struct dev_pm_ops acpi_fan_pm = {
  38        .resume = acpi_fan_resume,
  39        .freeze = acpi_fan_suspend,
  40        .thaw = acpi_fan_resume,
  41        .restore = acpi_fan_resume,
  42};
  43#define FAN_PM_OPS_PTR (&acpi_fan_pm)
  44#else
  45#define FAN_PM_OPS_PTR NULL
  46#endif
  47
  48#define ACPI_FPS_NAME_LEN       20
  49
  50struct acpi_fan_fps {
  51        u64 control;
  52        u64 trip_point;
  53        u64 speed;
  54        u64 noise_level;
  55        u64 power;
  56        char name[ACPI_FPS_NAME_LEN];
  57        struct device_attribute dev_attr;
  58};
  59
  60struct acpi_fan_fif {
  61        u64 revision;
  62        u64 fine_grain_ctrl;
  63        u64 step_size;
  64        u64 low_speed_notification;
  65};
  66
  67struct acpi_fan {
  68        bool acpi4;
  69        struct acpi_fan_fif fif;
  70        struct acpi_fan_fps *fps;
  71        int fps_count;
  72        struct thermal_cooling_device *cdev;
  73};
  74
  75static struct platform_driver acpi_fan_driver = {
  76        .probe = acpi_fan_probe,
  77        .remove = acpi_fan_remove,
  78        .driver = {
  79                .name = "acpi-fan",
  80                .acpi_match_table = fan_device_ids,
  81                .pm = FAN_PM_OPS_PTR,
  82        },
  83};
  84
  85/* thermal cooling device callbacks */
  86static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
  87                             *state)
  88{
  89        struct acpi_device *device = cdev->devdata;
  90        struct acpi_fan *fan = acpi_driver_data(device);
  91
  92        if (fan->acpi4)
  93                *state = fan->fps_count - 1;
  94        else
  95                *state = 1;
  96        return 0;
  97}
  98
  99static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
 100{
 101        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 102        struct acpi_fan *fan = acpi_driver_data(device);
 103        union acpi_object *obj;
 104        acpi_status status;
 105        int control, i;
 106
 107        status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer);
 108        if (ACPI_FAILURE(status)) {
 109                dev_err(&device->dev, "Get fan state failed\n");
 110                return status;
 111        }
 112
 113        obj = buffer.pointer;
 114        if (!obj || obj->type != ACPI_TYPE_PACKAGE ||
 115            obj->package.count != 3 ||
 116            obj->package.elements[1].type != ACPI_TYPE_INTEGER) {
 117                dev_err(&device->dev, "Invalid _FST data\n");
 118                status = -EINVAL;
 119                goto err;
 120        }
 121
 122        control = obj->package.elements[1].integer.value;
 123        for (i = 0; i < fan->fps_count; i++) {
 124                /*
 125                 * When Fine Grain Control is set, return the state
 126                 * corresponding to maximum fan->fps[i].control
 127                 * value compared to the current speed. Here the
 128                 * fan->fps[] is sorted array with increasing speed.
 129                 */
 130                if (fan->fif.fine_grain_ctrl && control < fan->fps[i].control) {
 131                        i = (i > 0) ? i - 1 : 0;
 132                        break;
 133                } else if (control == fan->fps[i].control) {
 134                        break;
 135                }
 136        }
 137        if (i == fan->fps_count) {
 138                dev_dbg(&device->dev, "Invalid control value returned\n");
 139                status = -EINVAL;
 140                goto err;
 141        }
 142
 143        *state = i;
 144
 145err:
 146        kfree(obj);
 147        return status;
 148}
 149
 150static int fan_get_state(struct acpi_device *device, unsigned long *state)
 151{
 152        int result;
 153        int acpi_state = ACPI_STATE_D0;
 154
 155        result = acpi_device_update_power(device, &acpi_state);
 156        if (result)
 157                return result;
 158
 159        *state = acpi_state == ACPI_STATE_D3_COLD
 160                        || acpi_state == ACPI_STATE_D3_HOT ?
 161                0 : (acpi_state == ACPI_STATE_D0 ? 1 : -1);
 162        return 0;
 163}
 164
 165static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
 166                             *state)
 167{
 168        struct acpi_device *device = cdev->devdata;
 169        struct acpi_fan *fan = acpi_driver_data(device);
 170
 171        if (fan->acpi4)
 172                return fan_get_state_acpi4(device, state);
 173        else
 174                return fan_get_state(device, state);
 175}
 176
 177static int fan_set_state(struct acpi_device *device, unsigned long state)
 178{
 179        if (state != 0 && state != 1)
 180                return -EINVAL;
 181
 182        return acpi_device_set_power(device,
 183                                     state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD);
 184}
 185
 186static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state)
 187{
 188        struct acpi_fan *fan = acpi_driver_data(device);
 189        acpi_status status;
 190
 191        if (state >= fan->fps_count)
 192                return -EINVAL;
 193
 194        status = acpi_execute_simple_method(device->handle, "_FSL",
 195                                            fan->fps[state].control);
 196        if (ACPI_FAILURE(status)) {
 197                dev_dbg(&device->dev, "Failed to set state by _FSL\n");
 198                return status;
 199        }
 200
 201        return 0;
 202}
 203
 204static int
 205fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
 206{
 207        struct acpi_device *device = cdev->devdata;
 208        struct acpi_fan *fan = acpi_driver_data(device);
 209
 210        if (fan->acpi4)
 211                return fan_set_state_acpi4(device, state);
 212        else
 213                return fan_set_state(device, state);
 214}
 215
 216static const struct thermal_cooling_device_ops fan_cooling_ops = {
 217        .get_max_state = fan_get_max_state,
 218        .get_cur_state = fan_get_cur_state,
 219        .set_cur_state = fan_set_cur_state,
 220};
 221
 222/* --------------------------------------------------------------------------
 223 *                               Driver Interface
 224 * --------------------------------------------------------------------------
 225*/
 226
 227static bool acpi_fan_is_acpi4(struct acpi_device *device)
 228{
 229        return acpi_has_method(device->handle, "_FIF") &&
 230               acpi_has_method(device->handle, "_FPS") &&
 231               acpi_has_method(device->handle, "_FSL") &&
 232               acpi_has_method(device->handle, "_FST");
 233}
 234
 235static int acpi_fan_get_fif(struct acpi_device *device)
 236{
 237        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 238        struct acpi_fan *fan = acpi_driver_data(device);
 239        struct acpi_buffer format = { sizeof("NNNN"), "NNNN" };
 240        struct acpi_buffer fif = { sizeof(fan->fif), &fan->fif };
 241        union acpi_object *obj;
 242        acpi_status status;
 243
 244        status = acpi_evaluate_object(device->handle, "_FIF", NULL, &buffer);
 245        if (ACPI_FAILURE(status))
 246                return status;
 247
 248        obj = buffer.pointer;
 249        if (!obj || obj->type != ACPI_TYPE_PACKAGE) {
 250                dev_err(&device->dev, "Invalid _FIF data\n");
 251                status = -EINVAL;
 252                goto err;
 253        }
 254
 255        status = acpi_extract_package(obj, &format, &fif);
 256        if (ACPI_FAILURE(status)) {
 257                dev_err(&device->dev, "Invalid _FIF element\n");
 258                status = -EINVAL;
 259        }
 260
 261err:
 262        kfree(obj);
 263        return status;
 264}
 265
 266static int acpi_fan_speed_cmp(const void *a, const void *b)
 267{
 268        const struct acpi_fan_fps *fps1 = a;
 269        const struct acpi_fan_fps *fps2 = b;
 270        return fps1->speed - fps2->speed;
 271}
 272
 273static 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
 306static int acpi_fan_get_fps(struct acpi_device *device)
 307{
 308        struct acpi_fan *fan = acpi_driver_data(device);
 309        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 310        union acpi_object *obj;
 311        acpi_status status;
 312        int i;
 313
 314        status = acpi_evaluate_object(device->handle, "_FPS", NULL, &buffer);
 315        if (ACPI_FAILURE(status))
 316                return status;
 317
 318        obj = buffer.pointer;
 319        if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count < 2) {
 320                dev_err(&device->dev, "Invalid _FPS data\n");
 321                status = -EINVAL;
 322                goto err;
 323        }
 324
 325        fan->fps_count = obj->package.count - 1; /* minus revision field */
 326        fan->fps = devm_kcalloc(&device->dev,
 327                                fan->fps_count, sizeof(struct acpi_fan_fps),
 328                                GFP_KERNEL);
 329        if (!fan->fps) {
 330                dev_err(&device->dev, "Not enough memory\n");
 331                status = -ENOMEM;
 332                goto err;
 333        }
 334        for (i = 0; i < fan->fps_count; i++) {
 335                struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" };
 336                struct acpi_buffer fps = { offsetof(struct acpi_fan_fps, name),
 337                                                &fan->fps[i] };
 338                status = acpi_extract_package(&obj->package.elements[i + 1],
 339                                              &format, &fps);
 340                if (ACPI_FAILURE(status)) {
 341                        dev_err(&device->dev, "Invalid _FPS element\n");
 342                        goto err;
 343                }
 344        }
 345
 346        /* sort the state array according to fan speed in increase order */
 347        sort(fan->fps, fan->fps_count, sizeof(*fan->fps),
 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                fps->dev_attr.show = show_state;
 355                fps->dev_attr.store = NULL;
 356                fps->dev_attr.attr.name = fps->name;
 357                fps->dev_attr.attr.mode = 0444;
 358                status = sysfs_create_file(&device->dev.kobj, &fps->dev_attr.attr);
 359                if (status) {
 360                        int j;
 361
 362                        for (j = 0; j < i; ++j)
 363                                sysfs_remove_file(&device->dev.kobj, &fan->fps[j].dev_attr.attr);
 364                        break;
 365                }
 366        }
 367
 368err:
 369        kfree(obj);
 370        return status;
 371}
 372
 373static int acpi_fan_probe(struct platform_device *pdev)
 374{
 375        int result = 0;
 376        struct thermal_cooling_device *cdev;
 377        struct acpi_fan *fan;
 378        struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
 379        char *name;
 380
 381        fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL);
 382        if (!fan) {
 383                dev_err(&device->dev, "No memory for fan\n");
 384                return -ENOMEM;
 385        }
 386        device->driver_data = fan;
 387        platform_set_drvdata(pdev, fan);
 388
 389        if (acpi_fan_is_acpi4(device)) {
 390                result = acpi_fan_get_fif(device);
 391                if (result)
 392                        return result;
 393
 394                result = acpi_fan_get_fps(device);
 395                if (result)
 396                        return result;
 397
 398                fan->acpi4 = true;
 399        } else {
 400                result = acpi_device_update_power(device, NULL);
 401                if (result) {
 402                        dev_err(&device->dev, "Failed to set initial power state\n");
 403                        goto err_end;
 404                }
 405        }
 406
 407        if (!strncmp(pdev->name, "PNP0C0B", strlen("PNP0C0B")))
 408                name = "Fan";
 409        else
 410                name = acpi_device_bid(device);
 411
 412        cdev = thermal_cooling_device_register(name, device,
 413                                                &fan_cooling_ops);
 414        if (IS_ERR(cdev)) {
 415                result = PTR_ERR(cdev);
 416                goto err_end;
 417        }
 418
 419        dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id);
 420
 421        fan->cdev = cdev;
 422        result = sysfs_create_link(&pdev->dev.kobj,
 423                                   &cdev->device.kobj,
 424                                   "thermal_cooling");
 425        if (result)
 426                dev_err(&pdev->dev, "Failed to create sysfs link 'thermal_cooling'\n");
 427
 428        result = sysfs_create_link(&cdev->device.kobj,
 429                                   &pdev->dev.kobj,
 430                                   "device");
 431        if (result) {
 432                dev_err(&pdev->dev, "Failed to create sysfs link 'device'\n");
 433                goto err_end;
 434        }
 435
 436        return 0;
 437
 438err_end:
 439        if (fan->acpi4) {
 440                int i;
 441
 442                for (i = 0; i < fan->fps_count; ++i)
 443                        sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr);
 444        }
 445
 446        return result;
 447}
 448
 449static int acpi_fan_remove(struct platform_device *pdev)
 450{
 451        struct acpi_fan *fan = platform_get_drvdata(pdev);
 452
 453        if (fan->acpi4) {
 454                struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
 455                int i;
 456
 457                for (i = 0; i < fan->fps_count; ++i)
 458                        sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr);
 459        }
 460        sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling");
 461        sysfs_remove_link(&fan->cdev->device.kobj, "device");
 462        thermal_cooling_device_unregister(fan->cdev);
 463
 464        return 0;
 465}
 466
 467#ifdef CONFIG_PM_SLEEP
 468static int acpi_fan_suspend(struct device *dev)
 469{
 470        struct acpi_fan *fan = dev_get_drvdata(dev);
 471        if (fan->acpi4)
 472                return 0;
 473
 474        acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D0);
 475
 476        return AE_OK;
 477}
 478
 479static int acpi_fan_resume(struct device *dev)
 480{
 481        int result;
 482        struct acpi_fan *fan = dev_get_drvdata(dev);
 483
 484        if (fan->acpi4)
 485                return 0;
 486
 487        result = acpi_device_update_power(ACPI_COMPANION(dev), NULL);
 488        if (result)
 489                dev_err(dev, "Error updating fan power state\n");
 490
 491        return result;
 492}
 493#endif
 494
 495module_platform_driver(acpi_fan_driver);
 496