linux/drivers/thermal/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#include <linux/of.h>
  35
  36#include "ti-thermal.h"
  37#include "ti-bandgap.h"
  38
  39/* common data structures */
  40struct ti_thermal_data {
  41        struct cpufreq_policy *policy;
  42        struct thermal_zone_device *ti_thermal;
  43        struct thermal_zone_device *pcb_tz;
  44        struct thermal_cooling_device *cool_dev;
  45        struct ti_bandgap *bgp;
  46        enum thermal_device_mode mode;
  47        struct work_struct thermal_wq;
  48        int sensor_id;
  49        bool our_zone;
  50};
  51
  52static void ti_thermal_work(struct work_struct *work)
  53{
  54        struct ti_thermal_data *data = container_of(work,
  55                                        struct ti_thermal_data, thermal_wq);
  56
  57        thermal_zone_device_update(data->ti_thermal, THERMAL_EVENT_UNSPECIFIED);
  58
  59        dev_dbg(&data->ti_thermal->device, "updated thermal zone %s\n",
  60                data->ti_thermal->type);
  61}
  62
  63/**
  64 * ti_thermal_hotspot_temperature - returns sensor extrapolated temperature
  65 * @t:  omap sensor temperature
  66 * @s:  omap sensor slope value
  67 * @c:  omap sensor const value
  68 */
  69static inline int ti_thermal_hotspot_temperature(int t, int s, int c)
  70{
  71        int delta = t * s / 1000 + c;
  72
  73        if (delta < 0)
  74                delta = 0;
  75
  76        return t + delta;
  77}
  78
  79/* thermal zone ops */
  80/* Get temperature callback function for thermal zone */
  81static inline int __ti_thermal_get_temp(void *devdata, int *temp)
  82{
  83        struct thermal_zone_device *pcb_tz = NULL;
  84        struct ti_thermal_data *data = devdata;
  85        struct ti_bandgap *bgp;
  86        const struct ti_temp_sensor *s;
  87        int ret, tmp, slope, constant;
  88        int pcb_temp;
  89
  90        if (!data)
  91                return 0;
  92
  93        bgp = data->bgp;
  94        s = &bgp->conf->sensors[data->sensor_id];
  95
  96        ret = ti_bandgap_read_temperature(bgp, data->sensor_id, &tmp);
  97        if (ret)
  98                return ret;
  99
 100        /* Default constants */
 101        slope = thermal_zone_get_slope(data->ti_thermal);
 102        constant = thermal_zone_get_offset(data->ti_thermal);
 103
 104        pcb_tz = data->pcb_tz;
 105        /* In case pcb zone is available, use the extrapolation rule with it */
 106        if (!IS_ERR(pcb_tz)) {
 107                ret = thermal_zone_get_temp(pcb_tz, &pcb_temp);
 108                if (!ret) {
 109                        tmp -= pcb_temp; /* got a valid PCB temp */
 110                        slope = s->slope_pcb;
 111                        constant = s->constant_pcb;
 112                } else {
 113                        dev_err(bgp->dev,
 114                                "Failed to read PCB state. Using defaults\n");
 115                        ret = 0;
 116                }
 117        }
 118        *temp = ti_thermal_hotspot_temperature(tmp, slope, constant);
 119
 120        return ret;
 121}
 122
 123static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal,
 124                                      int *temp)
 125{
 126        struct ti_thermal_data *data = thermal->devdata;
 127
 128        return __ti_thermal_get_temp(data, temp);
 129}
 130
 131static int __ti_thermal_get_trend(void *p, int trip, enum thermal_trend *trend)
 132{
 133        struct ti_thermal_data *data = p;
 134        struct ti_bandgap *bgp;
 135        int id, tr, ret = 0;
 136
 137        bgp = data->bgp;
 138        id = data->sensor_id;
 139
 140        ret = ti_bandgap_get_trend(bgp, id, &tr);
 141        if (ret)
 142                return ret;
 143
 144        if (tr > 0)
 145                *trend = THERMAL_TREND_RAISING;
 146        else if (tr < 0)
 147                *trend = THERMAL_TREND_DROPPING;
 148        else
 149                *trend = THERMAL_TREND_STABLE;
 150
 151        return 0;
 152}
 153
 154static const struct thermal_zone_of_device_ops ti_of_thermal_ops = {
 155        .get_temp = __ti_thermal_get_temp,
 156        .get_trend = __ti_thermal_get_trend,
 157};
 158
 159static struct ti_thermal_data
 160*ti_thermal_build_data(struct ti_bandgap *bgp, int id)
 161{
 162        struct ti_thermal_data *data;
 163
 164        data = devm_kzalloc(bgp->dev, sizeof(*data), GFP_KERNEL);
 165        if (!data) {
 166                dev_err(bgp->dev, "kzalloc fail\n");
 167                return NULL;
 168        }
 169        data->sensor_id = id;
 170        data->bgp = bgp;
 171        data->mode = THERMAL_DEVICE_ENABLED;
 172        /* pcb_tz will be either valid or PTR_ERR() */
 173        data->pcb_tz = thermal_zone_get_zone_by_name("pcb");
 174        INIT_WORK(&data->thermal_wq, ti_thermal_work);
 175
 176        return data;
 177}
 178
 179int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
 180                             char *domain)
 181{
 182        struct ti_thermal_data *data;
 183
 184        data = ti_bandgap_get_sensor_data(bgp, id);
 185
 186        if (!data || IS_ERR(data))
 187                data = ti_thermal_build_data(bgp, id);
 188
 189        if (!data)
 190                return -EINVAL;
 191
 192        /* in case this is specified by DT */
 193        data->ti_thermal = devm_thermal_zone_of_sensor_register(bgp->dev, id,
 194                                        data, &ti_of_thermal_ops);
 195        if (IS_ERR(data->ti_thermal)) {
 196                dev_err(bgp->dev, "thermal zone device is NULL\n");
 197                return PTR_ERR(data->ti_thermal);
 198        }
 199
 200        ti_bandgap_set_sensor_data(bgp, id, data);
 201        ti_bandgap_write_update_interval(bgp, data->sensor_id,
 202                                        data->ti_thermal->polling_delay);
 203
 204        return 0;
 205}
 206
 207int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id)
 208{
 209        struct ti_thermal_data *data;
 210
 211        data = ti_bandgap_get_sensor_data(bgp, id);
 212
 213        if (data && data->ti_thermal) {
 214                if (data->our_zone)
 215                        thermal_zone_device_unregister(data->ti_thermal);
 216        }
 217
 218        return 0;
 219}
 220
 221int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id)
 222{
 223        struct ti_thermal_data *data;
 224
 225        data = ti_bandgap_get_sensor_data(bgp, id);
 226
 227        schedule_work(&data->thermal_wq);
 228
 229        return 0;
 230}
 231
 232int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id)
 233{
 234        struct ti_thermal_data *data;
 235        struct device_node *np = bgp->dev->of_node;
 236
 237        /*
 238         * We are assuming here that if one deploys the zone
 239         * using DT, then it must be aware that the cooling device
 240         * loading has to happen via cpufreq driver.
 241         */
 242        if (of_find_property(np, "#thermal-sensor-cells", NULL))
 243                return 0;
 244
 245        data = ti_bandgap_get_sensor_data(bgp, id);
 246        if (!data || IS_ERR(data))
 247                data = ti_thermal_build_data(bgp, id);
 248
 249        if (!data)
 250                return -EINVAL;
 251
 252        data->policy = cpufreq_cpu_get(0);
 253        if (!data->policy) {
 254                pr_debug("%s: CPUFreq policy not found\n", __func__);
 255                return -EPROBE_DEFER;
 256        }
 257
 258        /* Register cooling device */
 259        data->cool_dev = cpufreq_cooling_register(data->policy);
 260        if (IS_ERR(data->cool_dev)) {
 261                int ret = PTR_ERR(data->cool_dev);
 262                dev_err(bgp->dev, "Failed to register cpu cooling device %d\n",
 263                        ret);
 264                cpufreq_cpu_put(data->policy);
 265
 266                return ret;
 267        }
 268        ti_bandgap_set_sensor_data(bgp, id, data);
 269
 270        return 0;
 271}
 272
 273int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id)
 274{
 275        struct ti_thermal_data *data;
 276
 277        data = ti_bandgap_get_sensor_data(bgp, id);
 278
 279        if (data) {
 280                cpufreq_cooling_unregister(data->cool_dev);
 281                if (data->policy)
 282                        cpufreq_cpu_put(data->policy);
 283        }
 284
 285        return 0;
 286}
 287