linux/drivers/staging/omap-thermal/omap-thermal-common.c
<<
>>
Prefs
   1/*
   2 * OMAP thermal driver interface
   3 *
   4 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
   5 * Contact:
   6 *   Eduardo Valentin <eduardo.valentin@ti.com>
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License
  10 * version 2 as published by the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope that it will be useful, but
  13 * WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15 * General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20 * 02110-1301 USA
  21 *
  22 */
  23
  24#include <linux/device.h>
  25#include <linux/err.h>
  26#include <linux/mutex.h>
  27#include <linux/gfp.h>
  28#include <linux/kernel.h>
  29#include <linux/workqueue.h>
  30#include <linux/thermal.h>
  31#include <linux/cpufreq.h>
  32#include <linux/cpumask.h>
  33#include <linux/cpu_cooling.h>
  34
  35#include "omap-thermal.h"
  36#include "omap-bandgap.h"
  37
  38/* common data structures */
  39struct omap_thermal_data {
  40        struct thermal_zone_device *omap_thermal;
  41        struct thermal_cooling_device *cool_dev;
  42        struct omap_bandgap *bg_ptr;
  43        enum thermal_device_mode mode;
  44        struct work_struct thermal_wq;
  45        int sensor_id;
  46};
  47
  48static void omap_thermal_work(struct work_struct *work)
  49{
  50        struct omap_thermal_data *data = container_of(work,
  51                                        struct omap_thermal_data, thermal_wq);
  52
  53        thermal_zone_device_update(data->omap_thermal);
  54
  55        dev_dbg(&data->omap_thermal->device, "updated thermal zone %s\n",
  56                data->omap_thermal->type);
  57}
  58
  59/**
  60 * omap_thermal_hotspot_temperature - returns sensor extrapolated temperature
  61 * @t:  omap sensor temperature
  62 * @s:  omap sensor slope value
  63 * @c:  omap sensor const value
  64 */
  65static inline int omap_thermal_hotspot_temperature(int t, int s, int c)
  66{
  67        int delta = t * s / 1000 + c;
  68
  69        if (delta < 0)
  70                delta = 0;
  71
  72        return t + delta;
  73}
  74
  75/* thermal zone ops */
  76/* Get temperature callback function for thermal zone*/
  77static inline int omap_thermal_get_temp(struct thermal_zone_device *thermal,
  78                                         unsigned long *temp)
  79{
  80        struct omap_thermal_data *data = thermal->devdata;
  81        struct omap_bandgap *bg_ptr;
  82        struct omap_temp_sensor *s;
  83        int ret, tmp, pcb_temp, slope, constant;
  84
  85        if (!data)
  86                return 0;
  87
  88        bg_ptr = data->bg_ptr;
  89        s = &bg_ptr->conf->sensors[data->sensor_id];
  90
  91        ret = omap_bandgap_read_temperature(bg_ptr, data->sensor_id, &tmp);
  92        if (ret)
  93                return ret;
  94
  95        pcb_temp = 0;
  96        /* TODO: Introduce pcb temperature lookup */
  97        /* In case pcb zone is available, use the extrapolation rule with it */
  98        if (pcb_temp) {
  99                tmp -= pcb_temp;
 100                slope = s->slope_pcb;
 101                constant = s->constant_pcb;
 102        } else {
 103                slope = s->slope;
 104                constant = s->constant;
 105        }
 106        *temp = omap_thermal_hotspot_temperature(tmp, slope, constant);
 107
 108        return ret;
 109}
 110
 111/* Bind callback functions for thermal zone */
 112static int omap_thermal_bind(struct thermal_zone_device *thermal,
 113                              struct thermal_cooling_device *cdev)
 114{
 115        struct omap_thermal_data *data = thermal->devdata;
 116        int id;
 117
 118        if (IS_ERR_OR_NULL(data))
 119                return -ENODEV;
 120
 121        /* check if this is the cooling device we registered */
 122        if (data->cool_dev != cdev)
 123                return 0;
 124
 125        id = data->sensor_id;
 126
 127        /* TODO: bind with min and max states */
 128        /* Simple thing, two trips, one passive another critical */
 129        return thermal_zone_bind_cooling_device(thermal, 0, cdev,
 130                                                THERMAL_NO_LIMIT,
 131                                                THERMAL_NO_LIMIT);
 132}
 133
 134/* Unbind callback functions for thermal zone */
 135static int omap_thermal_unbind(struct thermal_zone_device *thermal,
 136                                struct thermal_cooling_device *cdev)
 137{
 138        struct omap_thermal_data *data = thermal->devdata;
 139
 140        if (IS_ERR_OR_NULL(data))
 141                return -ENODEV;
 142
 143        /* check if this is the cooling device we registered */
 144        if (data->cool_dev != cdev)
 145                return 0;
 146
 147        /* Simple thing, two trips, one passive another critical */
 148        return thermal_zone_unbind_cooling_device(thermal, 0, cdev);
 149}
 150
 151/* Get mode callback functions for thermal zone */
 152static int omap_thermal_get_mode(struct thermal_zone_device *thermal,
 153                                  enum thermal_device_mode *mode)
 154{
 155        struct omap_thermal_data *data = thermal->devdata;
 156
 157        if (data)
 158                *mode = data->mode;
 159
 160        return 0;
 161}
 162
 163/* Set mode callback functions for thermal zone */
 164static int omap_thermal_set_mode(struct thermal_zone_device *thermal,
 165                                  enum thermal_device_mode mode)
 166{
 167        struct omap_thermal_data *data = thermal->devdata;
 168
 169        if (!data->omap_thermal) {
 170                dev_notice(&thermal->device, "thermal zone not registered\n");
 171                return 0;
 172        }
 173
 174        mutex_lock(&data->omap_thermal->lock);
 175
 176        if (mode == THERMAL_DEVICE_ENABLED)
 177                data->omap_thermal->polling_delay = FAST_TEMP_MONITORING_RATE;
 178        else
 179                data->omap_thermal->polling_delay = 0;
 180
 181        mutex_unlock(&data->omap_thermal->lock);
 182
 183        data->mode = mode;
 184        thermal_zone_device_update(data->omap_thermal);
 185        dev_dbg(&thermal->device, "thermal polling set for duration=%d msec\n",
 186                data->omap_thermal->polling_delay);
 187
 188        return 0;
 189}
 190
 191/* Get trip type callback functions for thermal zone */
 192static int omap_thermal_get_trip_type(struct thermal_zone_device *thermal,
 193                                       int trip, enum thermal_trip_type *type)
 194{
 195        if (!omap_thermal_is_valid_trip(trip))
 196                return -EINVAL;
 197
 198        if (trip + 1 == OMAP_TRIP_NUMBER)
 199                *type = THERMAL_TRIP_CRITICAL;
 200        else
 201                *type = THERMAL_TRIP_PASSIVE;
 202
 203        return 0;
 204}
 205
 206/* Get trip temperature callback functions for thermal zone */
 207static int omap_thermal_get_trip_temp(struct thermal_zone_device *thermal,
 208                                       int trip, unsigned long *temp)
 209{
 210        if (!omap_thermal_is_valid_trip(trip))
 211                return -EINVAL;
 212
 213        *temp = omap_thermal_get_trip_value(trip);
 214
 215        return 0;
 216}
 217
 218/* Get critical temperature callback functions for thermal zone */
 219static int omap_thermal_get_crit_temp(struct thermal_zone_device *thermal,
 220                                       unsigned long *temp)
 221{
 222        /* shutdown zone */
 223        return omap_thermal_get_trip_temp(thermal, OMAP_TRIP_NUMBER - 1, temp);
 224}
 225
 226static struct thermal_zone_device_ops omap_thermal_ops = {
 227        .get_temp = omap_thermal_get_temp,
 228        /* TODO: add .get_trend */
 229        .bind = omap_thermal_bind,
 230        .unbind = omap_thermal_unbind,
 231        .get_mode = omap_thermal_get_mode,
 232        .set_mode = omap_thermal_set_mode,
 233        .get_trip_type = omap_thermal_get_trip_type,
 234        .get_trip_temp = omap_thermal_get_trip_temp,
 235        .get_crit_temp = omap_thermal_get_crit_temp,
 236};
 237
 238static struct omap_thermal_data
 239*omap_thermal_build_data(struct omap_bandgap *bg_ptr, int id)
 240{
 241        struct omap_thermal_data *data;
 242
 243        data = devm_kzalloc(bg_ptr->dev, sizeof(*data), GFP_KERNEL);
 244        if (!data) {
 245                dev_err(bg_ptr->dev, "kzalloc fail\n");
 246                return NULL;
 247        }
 248        data->sensor_id = id;
 249        data->bg_ptr = bg_ptr;
 250        data->mode = THERMAL_DEVICE_ENABLED;
 251        INIT_WORK(&data->thermal_wq, omap_thermal_work);
 252
 253        return data;
 254}
 255
 256int omap_thermal_expose_sensor(struct omap_bandgap *bg_ptr, int id,
 257                               char *domain)
 258{
 259        struct omap_thermal_data *data;
 260
 261        data = omap_bandgap_get_sensor_data(bg_ptr, id);
 262
 263        if (!data)
 264                data = omap_thermal_build_data(bg_ptr, id);
 265
 266        if (!data)
 267                return -EINVAL;
 268
 269        /* TODO: remove TC1 TC2 */
 270        /* Create thermal zone */
 271        data->omap_thermal = thermal_zone_device_register(domain,
 272                                OMAP_TRIP_NUMBER, 0, data, &omap_thermal_ops,
 273                                NULL, FAST_TEMP_MONITORING_RATE,
 274                                FAST_TEMP_MONITORING_RATE);
 275        if (IS_ERR_OR_NULL(data->omap_thermal)) {
 276                dev_err(bg_ptr->dev, "thermal zone device is NULL\n");
 277                return PTR_ERR(data->omap_thermal);
 278        }
 279        data->omap_thermal->polling_delay = FAST_TEMP_MONITORING_RATE;
 280        omap_bandgap_set_sensor_data(bg_ptr, id, data);
 281
 282        return 0;
 283}
 284
 285int omap_thermal_remove_sensor(struct omap_bandgap *bg_ptr, int id)
 286{
 287        struct omap_thermal_data *data;
 288
 289        data = omap_bandgap_get_sensor_data(bg_ptr, id);
 290
 291        thermal_zone_device_unregister(data->omap_thermal);
 292
 293        return 0;
 294}
 295
 296int omap_thermal_report_sensor_temperature(struct omap_bandgap *bg_ptr, int id)
 297{
 298        struct omap_thermal_data *data;
 299
 300        data = omap_bandgap_get_sensor_data(bg_ptr, id);
 301
 302        schedule_work(&data->thermal_wq);
 303
 304        return 0;
 305}
 306
 307int omap_thermal_register_cpu_cooling(struct omap_bandgap *bg_ptr, int id)
 308{
 309        struct omap_thermal_data *data;
 310
 311        data = omap_bandgap_get_sensor_data(bg_ptr, id);
 312        if (!data)
 313                data = omap_thermal_build_data(bg_ptr, id);
 314
 315        if (!data)
 316                return -EINVAL;
 317
 318        /* Register cooling device */
 319        data->cool_dev = cpufreq_cooling_register(cpu_present_mask);
 320        if (IS_ERR_OR_NULL(data->cool_dev)) {
 321                dev_err(bg_ptr->dev,
 322                        "Failed to register cpufreq cooling device\n");
 323                return PTR_ERR(data->cool_dev);
 324        }
 325        omap_bandgap_set_sensor_data(bg_ptr, id, data);
 326
 327        return 0;
 328}
 329
 330int omap_thermal_unregister_cpu_cooling(struct omap_bandgap *bg_ptr, int id)
 331{
 332        struct omap_thermal_data *data;
 333
 334        data = omap_bandgap_get_sensor_data(bg_ptr, id);
 335        cpufreq_cooling_unregister(data->cool_dev);
 336
 337        return 0;
 338}
 339