linux/drivers/thermal/st/st_thermal.c
<<
>>
Prefs
   1/*
   2 * ST Thermal Sensor Driver core routines
   3 * Author: Ajit Pal Singh <ajitpal.singh@st.com>
   4 *
   5 * Copyright (C) 2003-2014 STMicroelectronics (R&D) Limited
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11 *
  12 */
  13
  14#include <linux/clk.h>
  15#include <linux/module.h>
  16#include <linux/of.h>
  17#include <linux/of_device.h>
  18
  19#include "st_thermal.h"
  20
  21/* The Thermal Framework expects millidegrees */
  22#define mcelsius(temp)                  ((temp) * 1000)
  23
  24/*
  25 * Function to allocate regfields which are common
  26 * between syscfg and memory mapped based sensors
  27 */
  28static int st_thermal_alloc_regfields(struct st_thermal_sensor *sensor)
  29{
  30        struct device *dev = sensor->dev;
  31        struct regmap *regmap = sensor->regmap;
  32        const struct reg_field *reg_fields = sensor->cdata->reg_fields;
  33
  34        sensor->dcorrect = devm_regmap_field_alloc(dev, regmap,
  35                                                   reg_fields[DCORRECT]);
  36
  37        sensor->overflow = devm_regmap_field_alloc(dev, regmap,
  38                                                   reg_fields[OVERFLOW]);
  39
  40        sensor->temp_data = devm_regmap_field_alloc(dev, regmap,
  41                                                    reg_fields[DATA]);
  42
  43        if (IS_ERR(sensor->dcorrect) ||
  44            IS_ERR(sensor->overflow) ||
  45            IS_ERR(sensor->temp_data)) {
  46                dev_err(dev, "failed to allocate common regfields\n");
  47                return -EINVAL;
  48        }
  49
  50        return sensor->ops->alloc_regfields(sensor);
  51}
  52
  53static int st_thermal_sensor_on(struct st_thermal_sensor *sensor)
  54{
  55        int ret;
  56        struct device *dev = sensor->dev;
  57
  58        ret = clk_prepare_enable(sensor->clk);
  59        if (ret) {
  60                dev_err(dev, "failed to enable clk\n");
  61                return ret;
  62        }
  63
  64        ret = sensor->ops->power_ctrl(sensor, POWER_ON);
  65        if (ret) {
  66                dev_err(dev, "failed to power on sensor\n");
  67                clk_disable_unprepare(sensor->clk);
  68        }
  69
  70        return ret;
  71}
  72
  73static int st_thermal_sensor_off(struct st_thermal_sensor *sensor)
  74{
  75        int ret;
  76
  77        ret = sensor->ops->power_ctrl(sensor, POWER_OFF);
  78        if (ret)
  79                return ret;
  80
  81        clk_disable_unprepare(sensor->clk);
  82
  83        return 0;
  84}
  85
  86static int st_thermal_calibration(struct st_thermal_sensor *sensor)
  87{
  88        int ret;
  89        unsigned int val;
  90        struct device *dev = sensor->dev;
  91
  92        /* Check if sensor calibration data is already written */
  93        ret = regmap_field_read(sensor->dcorrect, &val);
  94        if (ret) {
  95                dev_err(dev, "failed to read calibration data\n");
  96                return ret;
  97        }
  98
  99        if (!val) {
 100                /*
 101                 * Sensor calibration value not set by bootloader,
 102                 * default calibration data to be used
 103                 */
 104                ret = regmap_field_write(sensor->dcorrect,
 105                                         sensor->cdata->calibration_val);
 106                if (ret)
 107                        dev_err(dev, "failed to set calibration data\n");
 108        }
 109
 110        return ret;
 111}
 112
 113/* Callback to get temperature from HW*/
 114static int st_thermal_get_temp(struct thermal_zone_device *th, int *temperature)
 115{
 116        struct st_thermal_sensor *sensor = th->devdata;
 117        struct device *dev = sensor->dev;
 118        unsigned int temp;
 119        unsigned int overflow;
 120        int ret;
 121
 122        ret = regmap_field_read(sensor->overflow, &overflow);
 123        if (ret)
 124                return ret;
 125        if (overflow)
 126                return -EIO;
 127
 128        ret = regmap_field_read(sensor->temp_data, &temp);
 129        if (ret)
 130                return ret;
 131
 132        temp += sensor->cdata->temp_adjust_val;
 133        temp = mcelsius(temp);
 134
 135        dev_dbg(dev, "temperature: %d\n", temp);
 136
 137        *temperature = temp;
 138
 139        return 0;
 140}
 141
 142static int st_thermal_get_trip_type(struct thermal_zone_device *th,
 143                                int trip, enum thermal_trip_type *type)
 144{
 145        struct st_thermal_sensor *sensor = th->devdata;
 146        struct device *dev = sensor->dev;
 147
 148        switch (trip) {
 149        case 0:
 150                *type = THERMAL_TRIP_CRITICAL;
 151                break;
 152        default:
 153                dev_err(dev, "invalid trip point\n");
 154                return -EINVAL;
 155        }
 156
 157        return 0;
 158}
 159
 160static int st_thermal_get_trip_temp(struct thermal_zone_device *th,
 161                                    int trip, int *temp)
 162{
 163        struct st_thermal_sensor *sensor = th->devdata;
 164        struct device *dev = sensor->dev;
 165
 166        switch (trip) {
 167        case 0:
 168                *temp = mcelsius(sensor->cdata->crit_temp);
 169                break;
 170        default:
 171                dev_err(dev, "Invalid trip point\n");
 172                return -EINVAL;
 173        }
 174
 175        return 0;
 176}
 177
 178static struct thermal_zone_device_ops st_tz_ops = {
 179        .get_temp       = st_thermal_get_temp,
 180        .get_trip_type  = st_thermal_get_trip_type,
 181        .get_trip_temp  = st_thermal_get_trip_temp,
 182};
 183
 184int st_thermal_register(struct platform_device *pdev,
 185                        const struct of_device_id *st_thermal_of_match)
 186{
 187        struct st_thermal_sensor *sensor;
 188        struct device *dev = &pdev->dev;
 189        struct device_node *np = dev->of_node;
 190        const struct of_device_id *match;
 191
 192        int polling_delay;
 193        int ret;
 194
 195        if (!np) {
 196                dev_err(dev, "device tree node not found\n");
 197                return -EINVAL;
 198        }
 199
 200        sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
 201        if (!sensor)
 202                return -ENOMEM;
 203
 204        sensor->dev = dev;
 205
 206        match = of_match_device(st_thermal_of_match, dev);
 207        if (!(match && match->data))
 208                return -EINVAL;
 209
 210        sensor->cdata = match->data;
 211        if (!sensor->cdata->ops)
 212                return -EINVAL;
 213
 214        sensor->ops = sensor->cdata->ops;
 215
 216        ret = (sensor->ops->regmap_init)(sensor);
 217        if (ret)
 218                return ret;
 219
 220        ret = st_thermal_alloc_regfields(sensor);
 221        if (ret)
 222                return ret;
 223
 224        sensor->clk = devm_clk_get(dev, "thermal");
 225        if (IS_ERR(sensor->clk)) {
 226                dev_err(dev, "failed to fetch clock\n");
 227                return PTR_ERR(sensor->clk);
 228        }
 229
 230        if (sensor->ops->register_enable_irq) {
 231                ret = sensor->ops->register_enable_irq(sensor);
 232                if (ret)
 233                        return ret;
 234        }
 235
 236        ret = st_thermal_sensor_on(sensor);
 237        if (ret)
 238                return ret;
 239
 240        ret = st_thermal_calibration(sensor);
 241        if (ret)
 242                goto sensor_off;
 243
 244        polling_delay = sensor->ops->register_enable_irq ? 0 : 1000;
 245
 246        sensor->thermal_dev =
 247                thermal_zone_device_register(dev_name(dev), 1, 0, sensor,
 248                                             &st_tz_ops, NULL, 0, polling_delay);
 249        if (IS_ERR(sensor->thermal_dev)) {
 250                dev_err(dev, "failed to register thermal zone device\n");
 251                ret = PTR_ERR(sensor->thermal_dev);
 252                goto sensor_off;
 253        }
 254
 255        platform_set_drvdata(pdev, sensor);
 256
 257        return 0;
 258
 259sensor_off:
 260        st_thermal_sensor_off(sensor);
 261
 262        return ret;
 263}
 264EXPORT_SYMBOL_GPL(st_thermal_register);
 265
 266int st_thermal_unregister(struct platform_device *pdev)
 267{
 268        struct st_thermal_sensor *sensor = platform_get_drvdata(pdev);
 269
 270        st_thermal_sensor_off(sensor);
 271        thermal_zone_device_unregister(sensor->thermal_dev);
 272
 273        return 0;
 274}
 275EXPORT_SYMBOL_GPL(st_thermal_unregister);
 276
 277#ifdef CONFIG_PM_SLEEP
 278static int st_thermal_suspend(struct device *dev)
 279{
 280        struct platform_device *pdev = to_platform_device(dev);
 281        struct st_thermal_sensor *sensor = platform_get_drvdata(pdev);
 282
 283        return st_thermal_sensor_off(sensor);
 284}
 285
 286static int st_thermal_resume(struct device *dev)
 287{
 288        int ret;
 289        struct platform_device *pdev = to_platform_device(dev);
 290        struct st_thermal_sensor *sensor = platform_get_drvdata(pdev);
 291
 292        ret = st_thermal_sensor_on(sensor);
 293        if (ret)
 294                return ret;
 295
 296        ret = st_thermal_calibration(sensor);
 297        if (ret)
 298                return ret;
 299
 300        if (sensor->ops->enable_irq) {
 301                ret = sensor->ops->enable_irq(sensor);
 302                if (ret)
 303                        return ret;
 304        }
 305
 306        return 0;
 307}
 308#endif
 309
 310SIMPLE_DEV_PM_OPS(st_thermal_pm_ops, st_thermal_suspend, st_thermal_resume);
 311EXPORT_SYMBOL_GPL(st_thermal_pm_ops);
 312
 313MODULE_AUTHOR("STMicroelectronics (R&D) Limited <ajitpal.singh@st.com>");
 314MODULE_DESCRIPTION("STMicroelectronics STi SoC Thermal Sensor Driver");
 315MODULE_LICENSE("GPL v2");
 316