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