linux/drivers/thermal/thermal_hwmon.c
<<
>>
Prefs
   1/*
   2 *  thermal_hwmon.c - Generic Thermal Management hwmon support.
   3 *
   4 *  Code based on Intel thermal_core.c. Copyrights of the original code:
   5 *  Copyright (C) 2008 Intel Corp
   6 *  Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
   7 *  Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
   8 *
   9 *  Copyright (C) 2013 Texas Instruments
  10 *  Copyright (C) 2013 Eduardo Valentin <eduardo.valentin@ti.com>
  11 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  12 *
  13 *  This program is free software; you can redistribute it and/or modify
  14 *  it under the terms of the GNU General Public License as published by
  15 *  the Free Software Foundation; version 2 of the License.
  16 *
  17 *  This program is distributed in the hope that it will be useful, but
  18 *  WITHOUT ANY WARRANTY; without even the implied warranty of
  19 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  20 *  General Public License for more details.
  21 *
  22 *  You should have received a copy of the GNU General Public License along
  23 *  with this program; if not, write to the Free Software Foundation, Inc.,
  24 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  25 *
  26 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  27 */
  28#include <linux/hwmon.h>
  29#include <linux/thermal.h>
  30#include <linux/slab.h>
  31#include <linux/err.h>
  32#include "thermal_hwmon.h"
  33
  34/* hwmon sys I/F */
  35/* thermal zone devices with the same type share one hwmon device */
  36struct thermal_hwmon_device {
  37        char type[THERMAL_NAME_LENGTH];
  38        struct device *device;
  39        int count;
  40        struct list_head tz_list;
  41        struct list_head node;
  42};
  43
  44struct thermal_hwmon_attr {
  45        struct device_attribute attr;
  46        char name[16];
  47};
  48
  49/* one temperature input for each thermal zone */
  50struct thermal_hwmon_temp {
  51        struct list_head hwmon_node;
  52        struct thermal_zone_device *tz;
  53        struct thermal_hwmon_attr temp_input;   /* hwmon sys attr */
  54        struct thermal_hwmon_attr temp_crit;    /* hwmon sys attr */
  55};
  56
  57static LIST_HEAD(thermal_hwmon_list);
  58
  59static DEFINE_MUTEX(thermal_hwmon_list_lock);
  60
  61static ssize_t
  62name_show(struct device *dev, struct device_attribute *attr, char *buf)
  63{
  64        struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev);
  65        return sprintf(buf, "%s\n", hwmon->type);
  66}
  67static DEVICE_ATTR(name, 0444, name_show, NULL);
  68
  69static ssize_t
  70temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
  71{
  72        long temperature;
  73        int ret;
  74        struct thermal_hwmon_attr *hwmon_attr
  75                        = container_of(attr, struct thermal_hwmon_attr, attr);
  76        struct thermal_hwmon_temp *temp
  77                        = container_of(hwmon_attr, struct thermal_hwmon_temp,
  78                                       temp_input);
  79        struct thermal_zone_device *tz = temp->tz;
  80
  81        ret = thermal_zone_get_temp(tz, &temperature);
  82
  83        if (ret)
  84                return ret;
  85
  86        return sprintf(buf, "%ld\n", temperature);
  87}
  88
  89static ssize_t
  90temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
  91{
  92        struct thermal_hwmon_attr *hwmon_attr
  93                        = container_of(attr, struct thermal_hwmon_attr, attr);
  94        struct thermal_hwmon_temp *temp
  95                        = container_of(hwmon_attr, struct thermal_hwmon_temp,
  96                                       temp_crit);
  97        struct thermal_zone_device *tz = temp->tz;
  98        long temperature;
  99        int ret;
 100
 101        ret = tz->ops->get_trip_temp(tz, 0, &temperature);
 102        if (ret)
 103                return ret;
 104
 105        return sprintf(buf, "%ld\n", temperature);
 106}
 107
 108
 109static struct thermal_hwmon_device *
 110thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
 111{
 112        struct thermal_hwmon_device *hwmon;
 113
 114        mutex_lock(&thermal_hwmon_list_lock);
 115        list_for_each_entry(hwmon, &thermal_hwmon_list, node)
 116                if (!strcmp(hwmon->type, tz->type)) {
 117                        mutex_unlock(&thermal_hwmon_list_lock);
 118                        return hwmon;
 119                }
 120        mutex_unlock(&thermal_hwmon_list_lock);
 121
 122        return NULL;
 123}
 124
 125/* Find the temperature input matching a given thermal zone */
 126static struct thermal_hwmon_temp *
 127thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
 128                          const struct thermal_zone_device *tz)
 129{
 130        struct thermal_hwmon_temp *temp;
 131
 132        mutex_lock(&thermal_hwmon_list_lock);
 133        list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)
 134                if (temp->tz == tz) {
 135                        mutex_unlock(&thermal_hwmon_list_lock);
 136                        return temp;
 137                }
 138        mutex_unlock(&thermal_hwmon_list_lock);
 139
 140        return NULL;
 141}
 142
 143int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
 144{
 145        struct thermal_hwmon_device *hwmon;
 146        struct thermal_hwmon_temp *temp;
 147        int new_hwmon_device = 1;
 148        int result;
 149
 150        hwmon = thermal_hwmon_lookup_by_type(tz);
 151        if (hwmon) {
 152                new_hwmon_device = 0;
 153                goto register_sys_interface;
 154        }
 155
 156        hwmon = kzalloc(sizeof(*hwmon), GFP_KERNEL);
 157        if (!hwmon)
 158                return -ENOMEM;
 159
 160        INIT_LIST_HEAD(&hwmon->tz_list);
 161        strlcpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
 162        hwmon->device = hwmon_device_register(NULL);
 163        if (IS_ERR(hwmon->device)) {
 164                result = PTR_ERR(hwmon->device);
 165                goto free_mem;
 166        }
 167        dev_set_drvdata(hwmon->device, hwmon);
 168        result = device_create_file(hwmon->device, &dev_attr_name);
 169        if (result)
 170                goto free_mem;
 171
 172 register_sys_interface:
 173        temp = kzalloc(sizeof(*temp), GFP_KERNEL);
 174        if (!temp) {
 175                result = -ENOMEM;
 176                goto unregister_name;
 177        }
 178
 179        temp->tz = tz;
 180        hwmon->count++;
 181
 182        snprintf(temp->temp_input.name, sizeof(temp->temp_input.name),
 183                 "temp%d_input", hwmon->count);
 184        temp->temp_input.attr.attr.name = temp->temp_input.name;
 185        temp->temp_input.attr.attr.mode = 0444;
 186        temp->temp_input.attr.show = temp_input_show;
 187        sysfs_attr_init(&temp->temp_input.attr.attr);
 188        result = device_create_file(hwmon->device, &temp->temp_input.attr);
 189        if (result)
 190                goto free_temp_mem;
 191
 192        if (tz->ops->get_crit_temp) {
 193                unsigned long temperature;
 194                if (!tz->ops->get_crit_temp(tz, &temperature)) {
 195                        snprintf(temp->temp_crit.name,
 196                                 sizeof(temp->temp_crit.name),
 197                                "temp%d_crit", hwmon->count);
 198                        temp->temp_crit.attr.attr.name = temp->temp_crit.name;
 199                        temp->temp_crit.attr.attr.mode = 0444;
 200                        temp->temp_crit.attr.show = temp_crit_show;
 201                        sysfs_attr_init(&temp->temp_crit.attr.attr);
 202                        result = device_create_file(hwmon->device,
 203                                                    &temp->temp_crit.attr);
 204                        if (result)
 205                                goto unregister_input;
 206                }
 207        }
 208
 209        mutex_lock(&thermal_hwmon_list_lock);
 210        if (new_hwmon_device)
 211                list_add_tail(&hwmon->node, &thermal_hwmon_list);
 212        list_add_tail(&temp->hwmon_node, &hwmon->tz_list);
 213        mutex_unlock(&thermal_hwmon_list_lock);
 214
 215        return 0;
 216
 217 unregister_input:
 218        device_remove_file(hwmon->device, &temp->temp_input.attr);
 219 free_temp_mem:
 220        kfree(temp);
 221 unregister_name:
 222        if (new_hwmon_device) {
 223                device_remove_file(hwmon->device, &dev_attr_name);
 224                hwmon_device_unregister(hwmon->device);
 225        }
 226 free_mem:
 227        if (new_hwmon_device)
 228                kfree(hwmon);
 229
 230        return result;
 231}
 232
 233void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
 234{
 235        struct thermal_hwmon_device *hwmon;
 236        struct thermal_hwmon_temp *temp;
 237
 238        hwmon = thermal_hwmon_lookup_by_type(tz);
 239        if (unlikely(!hwmon)) {
 240                /* Should never happen... */
 241                dev_dbg(&tz->device, "hwmon device lookup failed!\n");
 242                return;
 243        }
 244
 245        temp = thermal_hwmon_lookup_temp(hwmon, tz);
 246        if (unlikely(!temp)) {
 247                /* Should never happen... */
 248                dev_dbg(&tz->device, "temperature input lookup failed!\n");
 249                return;
 250        }
 251
 252        device_remove_file(hwmon->device, &temp->temp_input.attr);
 253        if (tz->ops->get_crit_temp)
 254                device_remove_file(hwmon->device, &temp->temp_crit.attr);
 255
 256        mutex_lock(&thermal_hwmon_list_lock);
 257        list_del(&temp->hwmon_node);
 258        kfree(temp);
 259        if (!list_empty(&hwmon->tz_list)) {
 260                mutex_unlock(&thermal_hwmon_list_lock);
 261                return;
 262        }
 263        list_del(&hwmon->node);
 264        mutex_unlock(&thermal_hwmon_list_lock);
 265
 266        device_remove_file(hwmon->device, &dev_attr_name);
 267        hwmon_device_unregister(hwmon->device);
 268        kfree(hwmon);
 269}
 270