linux/drivers/staging/ti-soc-thermal/ti-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 "ti-thermal.h"
  36#include "ti-bandgap.h"
  37
  38/* common data structures */
  39struct ti_thermal_data {
  40        struct thermal_zone_device *ti_thermal;
  41        struct thermal_cooling_device *cool_dev;
  42        struct ti_bandgap *bgp;
  43        enum thermal_device_mode mode;
  44        struct work_struct thermal_wq;
  45        int sensor_id;
  46};
  47
  48static void ti_thermal_work(struct work_struct *work)
  49{
  50        struct ti_thermal_data *data = container_of(work,
  51                                        struct ti_thermal_data, thermal_wq);
  52
  53        thermal_zone_device_update(data->ti_thermal);
  54
  55        dev_dbg(&data->ti_thermal->device, "updated thermal zone %s\n",
  56                data->ti_thermal->type);
  57}
  58
  59/**
  60 * ti_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 ti_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 ti_thermal_get_temp(struct thermal_zone_device *thermal,
  78                                      unsigned long *temp)
  79{
  80        struct ti_thermal_data *data = thermal->devdata;
  81        struct ti_bandgap *bgp;
  82        const struct ti_temp_sensor *s;
  83        int ret, tmp, pcb_temp, slope, constant;
  84
  85        if (!data)
  86                return 0;
  87
  88        bgp = data->bgp;
  89        s = &bgp->conf->sensors[data->sensor_id];
  90
  91        ret = ti_bandgap_read_temperature(bgp, 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 = ti_thermal_hotspot_temperature(tmp, slope, constant);
 107
 108        return ret;
 109}
 110
 111/* Bind callback functions for thermal zone */
 112static int ti_thermal_bind(struct thermal_zone_device *thermal,
 113                           struct thermal_cooling_device *cdev)
 114{
 115        struct ti_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        /* Simple thing, two trips, one passive another critical */
 128        return thermal_zone_bind_cooling_device(thermal, 0, cdev,
 129        /* bind with min and max states defined by cpu_cooling */
 130                                                THERMAL_NO_LIMIT,
 131                                                THERMAL_NO_LIMIT);
 132}
 133
 134/* Unbind callback functions for thermal zone */
 135static int ti_thermal_unbind(struct thermal_zone_device *thermal,
 136                             struct thermal_cooling_device *cdev)
 137{
 138        struct ti_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 ti_thermal_get_mode(struct thermal_zone_device *thermal,
 153                               enum thermal_device_mode *mode)
 154{
 155        struct ti_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 ti_thermal_set_mode(struct thermal_zone_device *thermal,
 165                               enum thermal_device_mode mode)
 166{
 167        struct ti_thermal_data *data = thermal->devdata;
 168
 169        if (!data->ti_thermal) {
 170                dev_notice(&thermal->device, "thermal zone not registered\n");
 171                return 0;
 172        }
 173
 174        mutex_lock(&data->ti_thermal->lock);
 175
 176        if (mode == THERMAL_DEVICE_ENABLED)
 177                data->ti_thermal->polling_delay = FAST_TEMP_MONITORING_RATE;
 178        else
 179                data->ti_thermal->polling_delay = 0;
 180
 181        mutex_unlock(&data->ti_thermal->lock);
 182
 183        data->mode = mode;
 184        thermal_zone_device_update(data->ti_thermal);
 185        dev_dbg(&thermal->device, "thermal polling set for duration=%d msec\n",
 186                data->ti_thermal->polling_delay);
 187
 188        return 0;
 189}
 190
 191/* Get trip type callback functions for thermal zone */
 192static int ti_thermal_get_trip_type(struct thermal_zone_device *thermal,
 193                                    int trip, enum thermal_trip_type *type)
 194{
 195        if (!ti_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 ti_thermal_get_trip_temp(struct thermal_zone_device *thermal,
 208                                    int trip, unsigned long *temp)
 209{
 210        if (!ti_thermal_is_valid_trip(trip))
 211                return -EINVAL;
 212
 213        *temp = ti_thermal_get_trip_value(trip);
 214
 215        return 0;
 216}
 217
 218/* Get the temperature trend callback functions for thermal zone */
 219static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
 220                                int trip, enum thermal_trend *trend)
 221{
 222        struct ti_thermal_data *data = thermal->devdata;
 223        struct ti_bandgap *bgp;
 224        int id, tr, ret = 0;
 225
 226        bgp = data->bgp;
 227        id = data->sensor_id;
 228
 229        ret = ti_bandgap_get_trend(bgp, id, &tr);
 230        if (ret)
 231                return ret;
 232
 233        if (tr > 0)
 234                *trend = THERMAL_TREND_RAISING;
 235        else if (tr < 0)
 236                *trend = THERMAL_TREND_DROPPING;
 237        else
 238                *trend = THERMAL_TREND_STABLE;
 239
 240        return 0;
 241}
 242
 243/* Get critical temperature callback functions for thermal zone */
 244static int ti_thermal_get_crit_temp(struct thermal_zone_device *thermal,
 245                                    unsigned long *temp)
 246{
 247        /* shutdown zone */
 248        return ti_thermal_get_trip_temp(thermal, OMAP_TRIP_NUMBER - 1, temp);
 249}
 250
 251static struct thermal_zone_device_ops ti_thermal_ops = {
 252        .get_temp = ti_thermal_get_temp,
 253        .get_trend = ti_thermal_get_trend,
 254        .bind = ti_thermal_bind,
 255        .unbind = ti_thermal_unbind,
 256        .get_mode = ti_thermal_get_mode,
 257        .set_mode = ti_thermal_set_mode,
 258        .get_trip_type = ti_thermal_get_trip_type,
 259        .get_trip_temp = ti_thermal_get_trip_temp,
 260        .get_crit_temp = ti_thermal_get_crit_temp,
 261};
 262
 263static struct ti_thermal_data
 264*ti_thermal_build_data(struct ti_bandgap *bgp, int id)
 265{
 266        struct ti_thermal_data *data;
 267
 268        data = devm_kzalloc(bgp->dev, sizeof(*data), GFP_KERNEL);
 269        if (!data) {
 270                dev_err(bgp->dev, "kzalloc fail\n");
 271                return NULL;
 272        }
 273        data->sensor_id = id;
 274        data->bgp = bgp;
 275        data->mode = THERMAL_DEVICE_ENABLED;
 276        INIT_WORK(&data->thermal_wq, ti_thermal_work);
 277
 278        return data;
 279}
 280
 281int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
 282                             char *domain)
 283{
 284        struct ti_thermal_data *data;
 285
 286        data = ti_bandgap_get_sensor_data(bgp, id);
 287
 288        if (IS_ERR_OR_NULL(data))
 289                data = ti_thermal_build_data(bgp, id);
 290
 291        if (!data)
 292                return -EINVAL;
 293
 294        /* Create thermal zone */
 295        data->ti_thermal = thermal_zone_device_register(domain,
 296                                OMAP_TRIP_NUMBER, 0, data, &ti_thermal_ops,
 297                                NULL, FAST_TEMP_MONITORING_RATE,
 298                                FAST_TEMP_MONITORING_RATE);
 299        if (IS_ERR_OR_NULL(data->ti_thermal)) {
 300                dev_err(bgp->dev, "thermal zone device is NULL\n");
 301                return PTR_ERR(data->ti_thermal);
 302        }
 303        data->ti_thermal->polling_delay = FAST_TEMP_MONITORING_RATE;
 304        ti_bandgap_set_sensor_data(bgp, id, data);
 305
 306        return 0;
 307}
 308
 309int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id)
 310{
 311        struct ti_thermal_data *data;
 312
 313        data = ti_bandgap_get_sensor_data(bgp, id);
 314
 315        thermal_zone_device_unregister(data->ti_thermal);
 316
 317        return 0;
 318}
 319
 320int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id)
 321{
 322        struct ti_thermal_data *data;
 323
 324        data = ti_bandgap_get_sensor_data(bgp, id);
 325
 326        schedule_work(&data->thermal_wq);
 327
 328        return 0;
 329}
 330
 331int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id)
 332{
 333        struct ti_thermal_data *data;
 334
 335        data = ti_bandgap_get_sensor_data(bgp, id);
 336        if (IS_ERR_OR_NULL(data))
 337                data = ti_thermal_build_data(bgp, id);
 338
 339        if (!data)
 340                return -EINVAL;
 341
 342        if (!cpufreq_get_current_driver()) {
 343                dev_dbg(bgp->dev, "no cpufreq driver yet\n");
 344                return -EPROBE_DEFER;
 345        }
 346
 347        /* Register cooling device */
 348        data->cool_dev = cpufreq_cooling_register(cpu_present_mask);
 349        if (IS_ERR_OR_NULL(data->cool_dev)) {
 350                dev_err(bgp->dev,
 351                        "Failed to register cpufreq cooling device\n");
 352                return PTR_ERR(data->cool_dev);
 353        }
 354        ti_bandgap_set_sensor_data(bgp, id, data);
 355
 356        return 0;
 357}
 358
 359int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id)
 360{
 361        struct ti_thermal_data *data;
 362
 363        data = ti_bandgap_get_sensor_data(bgp, id);
 364        cpufreq_cooling_unregister(data->cool_dev);
 365
 366        return 0;
 367}
 368