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 *, |