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        {"", 0},
  30};
  31MODULE_DEVICE_TABLE(acpi, fan_device_ids);
  32
  33#ifdef CONFIG_PM_SLEEP
  34static int acpi_fan_suspend(struct device *dev);
  35static int acpi_fan_resume(struct device *dev);
  36static const struct dev_pm_ops acpi_fan_pm = {
  37        .resume = acpi_fan_resume,
  38        .freeze = acpi_fan_suspend,
  39        .thaw = acpi_fan_resume,
  40        .restore = acpi_fan_resume,
  41};
  42#define FAN_PM_OPS_PTR (&acpi_fan_pm)
  43#else
  44#define FAN_PM_OPS_PTR NULL
  45#endif
  46
  47struct acpi_fan_fps {
  48        u64 control;
  49        u64 trip_point;
  50        u64 speed;
  51        u64 noise_level;
  52        u64 power;
  53};
  54
  55struct acpi_fan_fif {
  56        u64 revision;
  57        u64 fine_grain_ctrl;
  58        u64 step_size;
  59        u64 low_speed_notification;
  60};
  61
  62struct acpi_fan {
  63        bool acpi4;
  64        struct acpi_fan_fif fif;
  65        struct acpi_fan_fps *fps;
  66        int fps_count;
  67        struct thermal_cooling_device *cdev;
  68};
  69
  70static struct platform_driver acpi_fan_driver = {
  71        .probe = acpi_fan_probe,
  72        .remove = acpi_fan_remove,
  73        .driver = {
  74                .name = "acpi-fan",
  75                .acpi_match_table = fan_device_ids,
  76                .pm = FAN_PM_OPS_PTR,
  77        },
  78};
  79
  80/* thermal cooling device callbacks */
  81static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
  82                             *state)
  83{
  84        struct acpi_device *device = cdev->devdata;
  85        struct acpi_fan *fan = acpi_driver_data(device);
  86
  87        if (fan->acpi4)
  88                *state = fan->fps_count - 1;
  89        else
  90                *state = 1;
  91        return 0;
  92}
  93
  94static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
  95{
  96        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
  97        struct acpi_fan *fan = acpi_driver_data(device);
  98        union acpi_object *obj;
  99        acpi_status status;
 100        int control, i;
 101
 102        status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer);
 103        if (ACPI_FAILURE(status)) {
 104                dev_err(&device->dev, "Get fan state failed\n");
 105                return status;
 106        }
 107
 108        obj = buffer.pointer;
 109        if (!obj || obj->type != ACPI_TYPE_PACKAGE ||
 110            obj->package.count != 3 ||
 111            obj->package.elements[1].type != ACPI_TYPE_INTEGER) {
 112                dev_err(&device->dev, "Invalid _FST data\n");
 113                status = -EINVAL;
 114                goto err;
 115        }
 116
 117        control = obj->package.elements[1].integer.value;
 118        for (i = 0; i < fan->fps_count; i++) {
 119                /*
 120                 * When Fine Grain Control is set, return the state
 121                 * corresponding to maximum fan->fps[i].control
 122                 * value compared to the current speed. Here the
 123                 * fan->fps[] is sorted array with increasing speed.
 124                 */
 125                if (fan->fif.fine_grain_ctrl && control < fan->fps[i].control) {
 126                        i = (i > 0) ? i - 1 : 0;
 127                        break;
 128                } else if (control == fan->fps[i].control) {
 129                        break;
 130                }
 131        }
 132        if (i == fan->fps_count) {
 133                dev_dbg(&device->dev, "Invalid control value returned\n");
 134                status = -EINVAL;
 135                goto err;
 136        }
 137
 138        *state = i;
 139
 140err:
 141        kfree(obj);
 142        return status;
 143}
 144
 145static int fan_get_state(struct acpi_device *device, unsigned long *state)
 146{
 147        int result;
 148        int acpi_state = ACPI_STATE_D0;
 149
 150        result = acpi_device_update_power(device, &acpi_state);
 151        if (result)
 152                return result;
 153
 154        *state = acpi_state == ACPI_STATE_D3_COLD
 155                        || acpi_state == ACPI_STATE_D3_HOT ?
 156                0 : (acpi_state == ACPI_STATE_D0 ? 1 : -1);
 157        return 0;
 158}
 159
 160static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
 161                             *state)
 162{
 163        struct acpi_device *device = cdev->devdata;
 164        struct acpi_fan *fan = acpi_driver_data(device);
 165
 166        if (fan->acpi4)
 167                return fan_get_state_acpi4(device, state);
 168        else
 169                return fan_get_state(device, state);
 170}
 171
 172static int fan_set_state(struct acpi_device *device, unsigned long state)
 173{
 174        if (state != 0 && state != 1)
 175                return -EINVAL;
 176
 177        return acpi_device_set_power(device,
 178                                     state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD);
 179}
 180
 181static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state)
 182{
 183        struct acpi_fan *fan = acpi_driver_data(device);
 184        acpi_status status;
 185
 186        if (state >= fan->fps_count)
 187                return -EINVAL;
 188
 189        status = acpi_execute_simple_method(device->handle, "_FSL",
 190                                            fan->fps[state].control);
 191        if (ACPI_FAILURE(status)) {
 192                dev_dbg(&device->dev, "Failed to set state by _FSL\n");
 193                return status;
 194        }
 195
 196        return 0;
 197}
 198
 199static int
 200fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
 201{
 202        struct acpi_device *device = cdev->devdata;
 203        struct acpi_fan *fan = acpi_driver_data(device);
 204
 205        if (fan->acpi4)
 206                return fan_set_state_acpi4(device, state);
 207        else
 208                return fan_set_state(device, state);
 209}
 210
 211static const struct thermal_cooling_device_ops fan_cooling_ops = {
 212        .get_max_state = fan_get_max_state,
 213        .get_cur_state = fan_get_cur_state,
 214        .set_cur_state = fan_set_cur_state,
 215};
 216
 217/* --------------------------------------------------------------------------
 218 *                               Driver Interface
 219 * --------------------------------------------------------------------------
 220*/
 221
 222static bool acpi_fan_is_acpi4(struct acpi_device *device)
 223{
 224        return acpi_has_method(device->handle, "_FIF") &&
 225               acpi_has_method(device->handle, "_FPS") &&
 226               acpi_has_method(device->handle, "_FSL") &&
 227               acpi_has_method(device->handle, "_FST");
 228}
 229
 230static int acpi_fan_get_fif(struct acpi_device *device)
 231{
 232        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 233        struct acpi_fan *fan = acpi_driver_data(device);
 234        struct acpi_buffer format = { sizeof("NNNN"), "NNNN" };
 235        struct acpi_buffer fif = { sizeof(fan->fif), &fan->fif };
 236        union acpi_object *obj;
 237        acpi_status status;
 238
 239        status = acpi_evaluate_object(device->handle, "_FIF", NULL, &buffer);
 240        if (ACPI_FAILURE(status))
 241                return status;
 242
 243        obj = buffer.pointer;
 244        if (!obj || obj->type != ACPI_TYPE_PACKAGE) {
 245                dev_err(&device->dev, "Invalid _FIF data\n");
 246                status = -EINVAL;
 247                goto err;
 248        }
 249
 250        status = acpi_extract_package(obj, &format, &fif);
 251        if (ACPI_FAILURE(status)) {
 252                dev_err(&device->dev, "Invalid _FIF element\n");
 253                status = -EINVAL;
 254        }
 255
 256err:
 257        kfree(obj);
 258        return status;
 259}
 260
 261static int acpi_fan_speed_cmp(const void *a, const void *b)
 262{
 263        const struct acpi_fan_fps *fps1 = a;
 264        const struct acpi_fan_fps *fps2 = b;
 265        return fps1->speed - fps2->speed;
 266}
 267
 268static int acpi_fan_get_fps(struct acpi_device *device)
 269{
 270        struct acpi_fan *fan = acpi_driver_data(device);
 271        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 272        union acpi_object *obj;
 273        acpi_status status;
 274        int i;
 275
 276        status = acpi_evaluate_object(device->handle, "_FPS", NULL, &buffer);
 277        if (ACPI_FAILURE(status))
 278                return status;
 279
 280        obj = buffer.pointer;
 281        if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count < 2) {
 282                dev_err(&device->dev, "Invalid _FPS data\n");
 283                status = -EINVAL;
 284                goto err;
 285        }
 286
 287        fan->fps_count = obj->package.count - 1; /* minus revision field */
 288        fan->fps = devm_kcalloc(&device->dev,
 289                                fan->fps_count, sizeof(struct acpi_fan_fps),
 290                                GFP_KERNEL);
 291        if (!fan->fps) {
 292                dev_err(&device->dev, "Not enough memory\n");
 293                status = -ENOMEM;
 294                goto err;
 295        }
 296        for (i = 0; i < fan->fps_count; i++) {
 297                struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" };
 298                struct acpi_buffer fps = { sizeof(fan->fps[i]), &fan->fps[i] };
 299                status = acpi_extract_package(&obj->package.elements[i + 1],
 300                                              &format, &fps);
 301                if (ACPI_FAILURE(status)) {
 302                        dev_err(&device->dev, "Invalid _FPS element\n");
 303                        break;
 304                }
 305        }
 306
 307        /* sort the state array according to fan speed in increase order */
 308        sort(fan->fps, fan->fps_count, sizeof(*fan->fps),
 309             acpi_fan_speed_cmp, NULL);
 310
 311err:
 312        kfree(obj);
 313        return status;
 314}
 315
 316static int acpi_fan_probe(struct platform_device *pdev)
 317{
 318        int result = 0;
 319        struct thermal_cooling_device *cdev;
 320        struct acpi_fan *fan;
 321        struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
 322        char *name;
 323
 324        fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL);
 325        if (!fan) {
 326                dev_err(&device->dev, "No memory for fan\n");
 327                return -ENOMEM;
 328        }
 329        device->driver_data = fan;
 330        platform_set_drvdata(pdev, fan);
 331
 332        if (acpi_fan_is_acpi4(device)) {
 333                if (acpi_fan_get_fif(device) || acpi_fan_get_fps(device))
 334                        goto end;
 335                fan->acpi4 = true;
 336        } else {
 337                result = acpi_device_update_power(device, NULL);
 338                if (result) {
 339                        dev_err(&device->dev, "Failed to set initial power state\n");
 340                        goto end;
 341                }
 342        }
 343
 344        if (!strncmp(pdev->name, "PNP0C0B", strlen("PNP0C0B")))
 345                name = "Fan";
 346        else
 347                name = acpi_device_bid(device);
 348
 349        cdev = thermal_cooling_device_register(name, device,
 350                                                &fan_cooling_ops);
 351        if (IS_ERR(cdev)) {
 352                result = PTR_ERR(cdev);
 353                goto end;
 354        }
 355
 356        dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id);
 357
 358        fan->cdev = cdev;
 359        result = sysfs_create_link(&pdev->dev.kobj,
 360                                   &cdev->device.kobj,
 361                                   "thermal_cooling");
 362        if (result)
 363                dev_err(&pdev->dev, "Failed to create sysfs link 'thermal_cooling'\n");
 364
 365        result = sysfs_create_link(&cdev->device.kobj,
 366                                   &pdev->dev.kobj,
 367                                   "device");
 368        if (result)
 369                dev_err(&pdev->dev, "Failed to create sysfs link 'device'\n");
 370
 371end:
 372        return result;
 373}
 374
 375static int acpi_fan_remove(struct platform_device *pdev)
 376{
 377        struct acpi_fan *fan = platform_get_drvdata(pdev);
 378
 379        sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling");
 380        sysfs_remove_link(&fan->cdev->device.kobj, "device");
 381        thermal_cooling_device_unregister(fan->cdev);
 382
 383        return 0;
 384}
 385
 386#ifdef CONFIG_PM_SLEEP
 387static int acpi_fan_suspend(struct device *dev)
 388{
 389        struct acpi_fan *fan = dev_get_drvdata(dev);
 390        if (fan->acpi4)
 391                return 0;
 392
 393        acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D0);
 394
 395        return AE_OK;
 396}
 397
 398static int acpi_fan_resume(struct device *dev)
 399{
 400        int result;
 401        struct acpi_fan *fan = dev_get_drvdata(dev);
 402
 403        if (fan->acpi4)
 404                return 0;
 405
 406        result = acpi_device_update_power(ACPI_COMPANION(dev), NULL);
 407        if (result)
 408                dev_err(dev, "Error updating fan power state\n");
 409
 410        return result;
 411}
 412#endif
 413
 414module_platform_driver(acpi_fan_driver);
 415