diff options
| -rw-r--r-- | drivers/thermal/thermal.c | 163 | ||||
| -rw-r--r-- | include/linux/thermal.h | 24 |
2 files changed, 187 insertions, 0 deletions
diff --git a/drivers/thermal/thermal.c b/drivers/thermal/thermal.c index cf56af4b7e02..6098787341f3 100644 --- a/drivers/thermal/thermal.c +++ b/drivers/thermal/thermal.c | |||
| @@ -295,6 +295,164 @@ thermal_cooling_device_trip_point_show(struct device *dev, | |||
| 295 | 295 | ||
| 296 | /* Device management */ | 296 | /* Device management */ |
| 297 | 297 | ||
| 298 | #if defined(CONFIG_HWMON) || \ | ||
| 299 | (defined(CONFIG_HWMON_MODULE) && defined(CONFIG_THERMAL_MODULE)) | ||
| 300 | /* hwmon sys I/F */ | ||
| 301 | #include <linux/hwmon.h> | ||
| 302 | static LIST_HEAD(thermal_hwmon_list); | ||
| 303 | |||
| 304 | static ssize_t | ||
| 305 | name_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 306 | { | ||
| 307 | struct thermal_hwmon_device *hwmon = dev->driver_data; | ||
| 308 | return sprintf(buf, "%s\n", hwmon->type); | ||
| 309 | } | ||
| 310 | static DEVICE_ATTR(name, 0444, name_show, NULL); | ||
| 311 | |||
| 312 | static ssize_t | ||
| 313 | temp_input_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 314 | { | ||
| 315 | struct thermal_hwmon_attr *hwmon_attr | ||
| 316 | = container_of(attr, struct thermal_hwmon_attr, attr); | ||
| 317 | struct thermal_zone_device *tz | ||
| 318 | = container_of(hwmon_attr, struct thermal_zone_device, | ||
| 319 | temp_input); | ||
| 320 | |||
| 321 | return tz->ops->get_temp(tz, buf); | ||
| 322 | } | ||
| 323 | |||
| 324 | static ssize_t | ||
| 325 | temp_crit_show(struct device *dev, struct device_attribute *attr, | ||
| 326 | char *buf) | ||
| 327 | { | ||
| 328 | struct thermal_hwmon_attr *hwmon_attr | ||
| 329 | = container_of(attr, struct thermal_hwmon_attr, attr); | ||
| 330 | struct thermal_zone_device *tz | ||
| 331 | = container_of(hwmon_attr, struct thermal_zone_device, | ||
| 332 | temp_crit); | ||
| 333 | |||
| 334 | return tz->ops->get_trip_temp(tz, 0, buf); | ||
| 335 | } | ||
| 336 | |||
| 337 | |||
| 338 | static int | ||
| 339 | thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) | ||
| 340 | { | ||
| 341 | struct thermal_hwmon_device *hwmon; | ||
| 342 | int new_hwmon_device = 1; | ||
| 343 | int result; | ||
| 344 | |||
| 345 | mutex_lock(&thermal_list_lock); | ||
| 346 | list_for_each_entry(hwmon, &thermal_hwmon_list, node) | ||
| 347 | if (!strcmp(hwmon->type, tz->type)) { | ||
| 348 | new_hwmon_device = 0; | ||
| 349 | mutex_unlock(&thermal_list_lock); | ||
| 350 | goto register_sys_interface; | ||
| 351 | } | ||
| 352 | mutex_unlock(&thermal_list_lock); | ||
| 353 | |||
| 354 | hwmon = kzalloc(sizeof(struct thermal_hwmon_device), GFP_KERNEL); | ||
| 355 | if (!hwmon) | ||
| 356 | return -ENOMEM; | ||
| 357 | |||
| 358 | INIT_LIST_HEAD(&hwmon->tz_list); | ||
| 359 | strlcpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH); | ||
| 360 | hwmon->device = hwmon_device_register(NULL); | ||
| 361 | if (IS_ERR(hwmon->device)) { | ||
| 362 | result = PTR_ERR(hwmon->device); | ||
| 363 | goto free_mem; | ||
| 364 | } | ||
| 365 | hwmon->device->driver_data = hwmon; | ||
| 366 | result = device_create_file(hwmon->device, &dev_attr_name); | ||
| 367 | if (result) | ||
| 368 | goto unregister_hwmon_device; | ||
| 369 | |||
| 370 | register_sys_interface: | ||
| 371 | tz->hwmon = hwmon; | ||
| 372 | hwmon->count++; | ||
| 373 | |||
| 374 | snprintf(tz->temp_input.name, THERMAL_NAME_LENGTH, | ||
| 375 | "temp%d_input", hwmon->count); | ||
| 376 | tz->temp_input.attr.attr.name = tz->temp_input.name; | ||
| 377 | tz->temp_input.attr.attr.mode = 0444; | ||
| 378 | tz->temp_input.attr.show = temp_input_show; | ||
| 379 | result = device_create_file(hwmon->device, &tz->temp_input.attr); | ||
| 380 | if (result) | ||
| 381 | goto unregister_hwmon_device; | ||
| 382 | |||
| 383 | if (tz->ops->get_crit_temp) { | ||
| 384 | unsigned long temperature; | ||
| 385 | if (!tz->ops->get_crit_temp(tz, &temperature)) { | ||
| 386 | snprintf(tz->temp_crit.name, THERMAL_NAME_LENGTH, | ||
| 387 | "temp%d_crit", hwmon->count); | ||
| 388 | tz->temp_crit.attr.attr.name = tz->temp_crit.name; | ||
| 389 | tz->temp_crit.attr.attr.mode = 0444; | ||
| 390 | tz->temp_crit.attr.show = temp_crit_show; | ||
| 391 | result = device_create_file(hwmon->device, | ||
| 392 | &tz->temp_crit.attr); | ||
| 393 | if (result) | ||
| 394 | goto unregister_hwmon_device; | ||
| 395 | } | ||
| 396 | } | ||
| 397 | |||
| 398 | mutex_lock(&thermal_list_lock); | ||
| 399 | if (new_hwmon_device) | ||
| 400 | list_add_tail(&hwmon->node, &thermal_hwmon_list); | ||
| 401 | list_add_tail(&tz->hwmon_node, &hwmon->tz_list); | ||
| 402 | mutex_unlock(&thermal_list_lock); | ||
| 403 | |||
| 404 | return 0; | ||
| 405 | |||
| 406 | unregister_hwmon_device: | ||
| 407 | device_remove_file(hwmon->device, &tz->temp_crit.attr); | ||
| 408 | device_remove_file(hwmon->device, &tz->temp_input.attr); | ||
| 409 | if (new_hwmon_device) { | ||
| 410 | device_remove_file(hwmon->device, &dev_attr_name); | ||
| 411 | hwmon_device_unregister(hwmon->device); | ||
| 412 | } | ||
| 413 | free_mem: | ||
| 414 | if (new_hwmon_device) | ||
| 415 | kfree(hwmon); | ||
| 416 | |||
| 417 | return result; | ||
| 418 | } | ||
| 419 | |||
| 420 | static void | ||
| 421 | thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) | ||
| 422 | { | ||
| 423 | struct thermal_hwmon_device *hwmon = tz->hwmon; | ||
| 424 | |||
| 425 | tz->hwmon = NULL; | ||
| 426 | device_remove_file(hwmon->device, &tz->temp_input.attr); | ||
| 427 | device_remove_file(hwmon->device, &tz->temp_crit.attr); | ||
| 428 | |||
| 429 | mutex_lock(&thermal_list_lock); | ||
| 430 | list_del(&tz->hwmon_node); | ||
| 431 | if (!list_empty(&hwmon->tz_list)) { | ||
| 432 | mutex_unlock(&thermal_list_lock); | ||
| 433 | return; | ||
| 434 | } | ||
| 435 | list_del(&hwmon->node); | ||
| 436 | mutex_unlock(&thermal_list_lock); | ||
| 437 | |||
| 438 | device_remove_file(hwmon->device, &dev_attr_name); | ||
| 439 | hwmon_device_unregister(hwmon->device); | ||
| 440 | kfree(hwmon); | ||
| 441 | } | ||
| 442 | #else | ||
| 443 | static int | ||
| 444 | thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) | ||
| 445 | { | ||
| 446 | return 0; | ||
| 447 | } | ||
| 448 | |||
| 449 | static void | ||
| 450 | thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) | ||
| 451 | { | ||
| 452 | } | ||
| 453 | #endif | ||
| 454 | |||
| 455 | |||
| 298 | /** | 456 | /** |
| 299 | * thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone | 457 | * thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone |
| 300 | * @tz: thermal zone device | 458 | * @tz: thermal zone device |
| @@ -642,6 +800,10 @@ struct thermal_zone_device *thermal_zone_device_register(char *type, | |||
| 642 | goto unregister; | 800 | goto unregister; |
| 643 | } | 801 | } |
| 644 | 802 | ||
| 803 | result = thermal_add_hwmon_sysfs(tz); | ||
| 804 | if (result) | ||
| 805 | goto unregister; | ||
| 806 | |||
| 645 | mutex_lock(&thermal_list_lock); | 807 | mutex_lock(&thermal_list_lock); |
| 646 | list_add_tail(&tz->node, &thermal_tz_list); | 808 | list_add_tail(&tz->node, &thermal_tz_list); |
| 647 | if (ops->bind) | 809 | if (ops->bind) |
| @@ -700,6 +862,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) | |||
| 700 | for (count = 0; count < tz->trips; count++) | 862 | for (count = 0; count < tz->trips; count++) |
| 701 | TRIP_POINT_ATTR_REMOVE(&tz->device, count); | 863 | TRIP_POINT_ATTR_REMOVE(&tz->device, count); |
| 702 | 864 | ||
| 865 | thermal_remove_hwmon_sysfs(tz); | ||
| 703 | release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); | 866 | release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); |
| 704 | idr_destroy(&tz->idr); | 867 | idr_destroy(&tz->idr); |
| 705 | mutex_destroy(&tz->lock); | 868 | mutex_destroy(&tz->lock); |
diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 16e6a8bdeb3c..06d3e6eb9ca8 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h | |||
| @@ -66,6 +66,23 @@ struct thermal_cooling_device { | |||
| 66 | ((long)t-2732+5)/10 : ((long)t-2732-5)/10) | 66 | ((long)t-2732+5)/10 : ((long)t-2732-5)/10) |
| 67 | #define CELSIUS_TO_KELVIN(t) ((t)*10+2732) | 67 | #define CELSIUS_TO_KELVIN(t) ((t)*10+2732) |
| 68 | 68 | ||
| 69 | #if defined(CONFIG_HWMON) || \ | ||
| 70 | (defined(CONFIG_HWMON_MODULE) && defined(CONFIG_THERMAL_MODULE)) | ||
| 71 | /* thermal zone devices with the same type share one hwmon device */ | ||
| 72 | struct thermal_hwmon_device { | ||
| 73 | char type[THERMAL_NAME_LENGTH]; | ||
| 74 | struct device *device; | ||
| 75 | int count; | ||
| 76 | struct list_head tz_list; | ||
| 77 | struct list_head node; | ||
| 78 | }; | ||
| 79 | |||
| 80 | struct thermal_hwmon_attr { | ||
| 81 | struct device_attribute attr; | ||
| 82 | char name[16]; | ||
| 83 | }; | ||
| 84 | #endif | ||
| 85 | |||
| 69 | struct thermal_zone_device { | 86 | struct thermal_zone_device { |
| 70 | int id; | 87 | int id; |
| 71 | char type[THERMAL_NAME_LENGTH]; | 88 | char type[THERMAL_NAME_LENGTH]; |
| @@ -77,6 +94,13 @@ struct thermal_zone_device { | |||
| 77 | struct idr idr; | 94 | struct idr idr; |
| 78 | struct mutex lock; /* protect cooling devices list */ | 95 | struct mutex lock; /* protect cooling devices list */ |
| 79 | struct list_head node; | 96 | struct list_head node; |
| 97 | #if defined(CONFIG_HWMON) || \ | ||
| 98 | (defined(CONFIG_HWMON_MODULE) && defined(CONFIG_THERMAL_MODULE)) | ||
| 99 | struct list_head hwmon_node; | ||
| 100 | struct thermal_hwmon_device *hwmon; | ||
| 101 | struct thermal_hwmon_attr temp_input; /* hwmon sys attr */ | ||
| 102 | struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */ | ||
| 103 | #endif | ||
| 80 | }; | 104 | }; |
| 81 | 105 | ||
| 82 | struct thermal_zone_device *thermal_zone_device_register(char *, int, void *, | 106 | struct thermal_zone_device *thermal_zone_device_register(char *, int, void *, |
