Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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