464 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * fan_core.c - ACPI Fan core Driver4 *5 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>6 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>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 22static const struct acpi_device_id fan_device_ids[] = {23 ACPI_FAN_DEVICE_IDS,24 {"", 0},25};26MODULE_DEVICE_TABLE(acpi, fan_device_ids);27 28/* thermal cooling device callbacks */29static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long30 *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 else39 *state = fan->fps_count - 1;40 } else {41 *state = 1;42 }43 44 return 0;45}46 47int 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 73err:74 kfree(obj);75 return ret;76}77 78static 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 99match_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 114static 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_COLD124 || acpi_state == ACPI_STATE_D3_HOT ?125 0 : (acpi_state == ACPI_STATE_D0 ? 1 : -1);126 return 0;127}128 129static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long130 *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 else138 return fan_get_state(device, state);139}140 141static 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 150static 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 else160 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 183static int184fan_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 else192 return fan_set_state(device, state);193}194 195static 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 Interface203 * --------------------------------------------------------------------------204*/205 206static 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 214static 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;253err:254 kfree(obj);255 return status;256}257 258static 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 265static 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 309err:310 kfree(obj);311 return status;312}313 314static 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 else359 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 387err_end:388 if (fan->acpi4)389 acpi_fan_delete_attributes(device);390 391 return result;392}393 394static 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_SLEEP409static 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 420static 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 435static 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#else444 445#define FAN_PM_OPS_PTR NULL446 447#endif448 449static struct platform_driver acpi_fan_driver = {450 .probe = acpi_fan_probe,451 .remove_new = 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 459module_platform_driver(acpi_fan_driver);460 461MODULE_AUTHOR("Paul Diefenbaugh");462MODULE_DESCRIPTION("ACPI Fan Driver");463MODULE_LICENSE("GPL");464