linux/drivers/thermal/imx_thermal.c
<<
>>
Prefs
   1/*
   2 * Copyright 2013 Freescale Semiconductor, Inc.
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 *
   8 */
   9
  10#include <linux/clk.h>
  11#include <linux/cpu_cooling.h>
  12#include <linux/cpufreq.h>
  13#include <linux/delay.h>
  14#include <linux/device.h>
  15#include <linux/init.h>
  16#include <linux/interrupt.h>
  17#include <linux/io.h>
  18#include <linux/kernel.h>
  19#include <linux/mfd/syscon.h>
  20#include <linux/module.h>
  21#include <linux/of.h>
  22#include <linux/platform_device.h>
  23#include <linux/regmap.h>
  24#include <linux/slab.h>
  25#include <linux/thermal.h>
  26#include <linux/types.h>
  27
  28#define REG_SET         0x4
  29#define REG_CLR         0x8
  30#define REG_TOG         0xc
  31
  32#define MISC0                           0x0150
  33#define MISC0_REFTOP_SELBIASOFF         (1 << 3)
  34
  35#define TEMPSENSE0                      0x0180
  36#define TEMPSENSE0_ALARM_VALUE_SHIFT    20
  37#define TEMPSENSE0_ALARM_VALUE_MASK     (0xfff << TEMPSENSE0_ALARM_VALUE_SHIFT)
  38#define TEMPSENSE0_TEMP_CNT_SHIFT       8
  39#define TEMPSENSE0_TEMP_CNT_MASK        (0xfff << TEMPSENSE0_TEMP_CNT_SHIFT)
  40#define TEMPSENSE0_FINISHED             (1 << 2)
  41#define TEMPSENSE0_MEASURE_TEMP         (1 << 1)
  42#define TEMPSENSE0_POWER_DOWN           (1 << 0)
  43
  44#define TEMPSENSE1                      0x0190
  45#define TEMPSENSE1_MEASURE_FREQ         0xffff
  46
  47#define OCOTP_ANA1                      0x04e0
  48
  49/* The driver supports 1 passive trip point and 1 critical trip point */
  50enum imx_thermal_trip {
  51        IMX_TRIP_PASSIVE,
  52        IMX_TRIP_CRITICAL,
  53        IMX_TRIP_NUM,
  54};
  55
  56/*
  57 * It defines the temperature in millicelsius for passive trip point
  58 * that will trigger cooling action when crossed.
  59 */
  60#define IMX_TEMP_PASSIVE                85000
  61
  62#define IMX_POLLING_DELAY               2000 /* millisecond */
  63#define IMX_PASSIVE_DELAY               1000
  64
  65struct imx_thermal_data {
  66        struct thermal_zone_device *tz;
  67        struct thermal_cooling_device *cdev;
  68        enum thermal_device_mode mode;
  69        struct regmap *tempmon;
  70        int c1, c2; /* See formula in imx_get_sensor_data() */
  71        unsigned long temp_passive;
  72        unsigned long temp_critical;
  73        unsigned long alarm_temp;
  74        unsigned long last_temp;
  75        bool irq_enabled;
  76        int irq;
  77        struct clk *thermal_clk;
  78};
  79
  80static void imx_set_alarm_temp(struct imx_thermal_data *data,
  81                               signed long alarm_temp)
  82{
  83        struct regmap *map = data->tempmon;
  84        int alarm_value;
  85
  86        data->alarm_temp = alarm_temp;
  87        alarm_value = (alarm_temp - data->c2) / data->c1;
  88        regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_ALARM_VALUE_MASK);
  89        regmap_write(map, TEMPSENSE0 + REG_SET, alarm_value <<
  90                        TEMPSENSE0_ALARM_VALUE_SHIFT);
  91}
  92
  93static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
  94{
  95        struct imx_thermal_data *data = tz->devdata;
  96        struct regmap *map = data->tempmon;
  97        unsigned int n_meas;
  98        bool wait;
  99        u32 val;
 100
 101        if (data->mode == THERMAL_DEVICE_ENABLED) {
 102                /* Check if a measurement is currently in progress */
 103                regmap_read(map, TEMPSENSE0, &val);
 104                wait = !(val & TEMPSENSE0_FINISHED);
 105        } else {
 106                /*
 107                 * Every time we measure the temperature, we will power on the
 108                 * temperature sensor, enable measurements, take a reading,
 109                 * disable measurements, power off the temperature sensor.
 110                 */
 111                regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
 112                regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
 113
 114                wait = true;
 115        }
 116
 117        /*
 118         * According to the temp sensor designers, it may require up to ~17us
 119         * to complete a measurement.
 120         */
 121        if (wait)
 122                usleep_range(20, 50);
 123
 124        regmap_read(map, TEMPSENSE0, &val);
 125
 126        if (data->mode != THERMAL_DEVICE_ENABLED) {
 127                regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
 128                regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
 129        }
 130
 131        if ((val & TEMPSENSE0_FINISHED) == 0) {
 132                dev_dbg(&tz->device, "temp measurement never finished\n");
 133                return -EAGAIN;
 134        }
 135
 136        n_meas = (val & TEMPSENSE0_TEMP_CNT_MASK) >> TEMPSENSE0_TEMP_CNT_SHIFT;
 137
 138        /* See imx_get_sensor_data() for formula derivation */
 139        *temp = data->c2 + data->c1 * n_meas;
 140
 141        /* Update alarm value to next higher trip point */
 142        if (data->alarm_temp == data->temp_passive && *temp >= data->temp_passive)
 143                imx_set_alarm_temp(data, data->temp_critical);
 144        if (data->alarm_temp == data->temp_critical && *temp < data->temp_passive) {
 145                imx_set_alarm_temp(data, data->temp_passive);
 146                dev_dbg(&tz->device, "thermal alarm off: T < %lu\n",
 147                        data->alarm_temp / 1000);
 148        }
 149
 150        if (*temp != data->last_temp) {
 151                dev_dbg(&tz->device, "millicelsius: %ld\n", *temp);
 152                data->last_temp = *temp;
 153        }
 154
 155        /* Reenable alarm IRQ if temperature below alarm temperature */
 156        if (!data->irq_enabled && *temp < data->alarm_temp) {
 157                data->irq_enabled = true;
 158                enable_irq(data->irq);
 159        }
 160
 161        return 0;
 162}
 163
 164static int imx_get_mode(struct thermal_zone_device *tz,
 165                        enum thermal_device_mode *mode)
 166{
 167        struct imx_thermal_data *data = tz->devdata;
 168
 169        *mode = data->mode;
 170
 171        return 0;
 172}
 173
 174static int imx_set_mode(struct thermal_zone_device *tz,
 175                        enum thermal_device_mode mode)
 176{
 177        struct imx_thermal_data *data = tz->devdata;
 178        struct regmap *map = data->tempmon;
 179
 180        if (mode == THERMAL_DEVICE_ENABLED) {
 181                tz->polling_delay = IMX_POLLING_DELAY;
 182                tz->passive_delay = IMX_PASSIVE_DELAY;
 183
 184                regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
 185                regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
 186
 187                if (!data->irq_enabled) {
 188                        data->irq_enabled = true;
 189                        enable_irq(data->irq);
 190                }
 191        } else {
 192                regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
 193                regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
 194
 195                tz->polling_delay = 0;
 196                tz->passive_delay = 0;
 197
 198                if (data->irq_enabled) {
 199                        disable_irq(data->irq);
 200                        data->irq_enabled = false;
 201                }
 202        }
 203
 204        data->mode = mode;
 205        thermal_zone_device_update(tz);
 206
 207        return 0;
 208}
 209
 210static int imx_get_trip_type(struct thermal_zone_device *tz, int trip,
 211                             enum thermal_trip_type *type)
 212{
 213        *type = (trip == IMX_TRIP_PASSIVE) ? THERMAL_TRIP_PASSIVE :
 214                                             THERMAL_TRIP_CRITICAL;
 215        return 0;
 216}
 217
 218static int imx_get_crit_temp(struct thermal_zone_device *tz,
 219                             unsigned long *temp)
 220{
 221        struct imx_thermal_data *data = tz->devdata;
 222
 223        *temp = data->temp_critical;
 224        return 0;
 225}
 226
 227static int imx_get_trip_temp(struct thermal_zone_device *tz, int trip,
 228                             unsigned long *temp)
 229{
 230        struct imx_thermal_data *data = tz->devdata;
 231
 232        *temp = (trip == IMX_TRIP_PASSIVE) ? data->temp_passive :
 233                                             data->temp_critical;
 234        return 0;
 235}
 236
 237static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip,
 238                             unsigned long temp)
 239{
 240        struct imx_thermal_data *data = tz->devdata;
 241
 242        if (trip == IMX_TRIP_CRITICAL)
 243                return -EPERM;
 244
 245        if (temp > IMX_TEMP_PASSIVE)
 246                return -EINVAL;
 247
 248        data->temp_passive = temp;
 249
 250        imx_set_alarm_temp(data, temp);
 251
 252        return 0;
 253}
 254
 255static int imx_bind(struct thermal_zone_device *tz,
 256                    struct thermal_cooling_device *cdev)
 257{
 258        int ret;
 259
 260        ret = thermal_zone_bind_cooling_device(tz, IMX_TRIP_PASSIVE, cdev,
 261                                               THERMAL_NO_LIMIT,
 262                                               THERMAL_NO_LIMIT);
 263        if (ret) {
 264                dev_err(&tz->device,
 265                        "binding zone %s with cdev %s failed:%d\n",
 266                        tz->type, cdev->type, ret);
 267                return ret;
 268        }
 269
 270        return 0;
 271}
 272
 273static int imx_unbind(struct thermal_zone_device *tz,
 274                      struct thermal_cooling_device *cdev)
 275{
 276        int ret;
 277
 278        ret = thermal_zone_unbind_cooling_device(tz, IMX_TRIP_PASSIVE, cdev);
 279        if (ret) {
 280                dev_err(&tz->device,
 281                        "unbinding zone %s with cdev %s failed:%d\n",
 282                        tz->type, cdev->type, ret);
 283                return ret;
 284        }
 285
 286        return 0;
 287}
 288
 289static struct thermal_zone_device_ops imx_tz_ops = {
 290        .bind = imx_bind,
 291        .unbind = imx_unbind,
 292        .get_temp = imx_get_temp,
 293        .get_mode = imx_get_mode,
 294        .set_mode = imx_set_mode,
 295        .get_trip_type = imx_get_trip_type,
 296        .get_trip_temp = imx_get_trip_temp,
 297        .get_crit_temp = imx_get_crit_temp,
 298        .set_trip_temp = imx_set_trip_temp,
 299};
 300
 301static int imx_get_sensor_data(struct platform_device *pdev)
 302{
 303        struct imx_thermal_data *data = platform_get_drvdata(pdev);
 304        struct regmap *map;
 305        int t1, t2, n1, n2;
 306        int ret;
 307        u32 val;
 308
 309        map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
 310                                              "fsl,tempmon-data");
 311        if (IS_ERR(map)) {
 312                ret = PTR_ERR(map);
 313                dev_err(&pdev->dev, "failed to get sensor regmap: %d\n", ret);
 314                return ret;
 315        }
 316
 317        ret = regmap_read(map, OCOTP_ANA1, &val);
 318        if (ret) {
 319                dev_err(&pdev->dev, "failed to read sensor data: %d\n", ret);
 320                return ret;
 321        }
 322
 323        if (val == 0 || val == ~0) {
 324                dev_err(&pdev->dev, "invalid sensor calibration data\n");
 325                return -EINVAL;
 326        }
 327
 328        /*
 329         * Sensor data layout:
 330         *   [31:20] - sensor value @ 25C
 331         *    [19:8] - sensor value of hot
 332         *     [7:0] - hot temperature value
 333         */
 334        n1 = val >> 20;
 335        n2 = (val & 0xfff00) >> 8;
 336        t2 = val & 0xff;
 337        t1 = 25; /* t1 always 25C */
 338
 339        /*
 340         * Derived from linear interpolation,
 341         * Tmeas = T2 + (Nmeas - N2) * (T1 - T2) / (N1 - N2)
 342         * We want to reduce this down to the minimum computation necessary
 343         * for each temperature read.  Also, we want Tmeas in millicelsius
 344         * and we don't want to lose precision from integer division. So...
 345         * milli_Tmeas = 1000 * T2 + 1000 * (Nmeas - N2) * (T1 - T2) / (N1 - N2)
 346         * Let constant c1 = 1000 * (T1 - T2) / (N1 - N2)
 347         * milli_Tmeas = (1000 * T2) + c1 * (Nmeas - N2)
 348         * milli_Tmeas = (1000 * T2) + (c1 * Nmeas) - (c1 * N2)
 349         * Let constant c2 = (1000 * T2) - (c1 * N2)
 350         * milli_Tmeas = c2 + (c1 * Nmeas)
 351         */
 352        data->c1 = 1000 * (t1 - t2) / (n1 - n2);
 353        data->c2 = 1000 * t2 - data->c1 * n2;
 354
 355        /*
 356         * Set the default passive cooling trip point to 20 °C below the
 357         * maximum die temperature. Can be changed from userspace.
 358         */
 359        data->temp_passive = 1000 * (t2 - 20);
 360
 361        /*
 362         * The maximum die temperature is t2, let's give 5 °C cushion
 363         * for noise and possible temperature rise between measurements.
 364         */
 365        data->temp_critical = 1000 * (t2 - 5);
 366
 367        return 0;
 368}
 369
 370static irqreturn_t imx_thermal_alarm_irq(int irq, void *dev)
 371{
 372        struct imx_thermal_data *data = dev;
 373
 374        disable_irq_nosync(irq);
 375        data->irq_enabled = false;
 376
 377        return IRQ_WAKE_THREAD;
 378}
 379
 380static irqreturn_t imx_thermal_alarm_irq_thread(int irq, void *dev)
 381{
 382        struct imx_thermal_data *data = dev;
 383
 384        dev_dbg(&data->tz->device, "THERMAL ALARM: T > %lu\n",
 385                data->alarm_temp / 1000);
 386
 387        thermal_zone_device_update(data->tz);
 388
 389        return IRQ_HANDLED;
 390}
 391
 392static int imx_thermal_probe(struct platform_device *pdev)
 393{
 394        struct imx_thermal_data *data;
 395        struct cpumask clip_cpus;
 396        struct regmap *map;
 397        int measure_freq;
 398        int ret;
 399
 400        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 401        if (!data)
 402                return -ENOMEM;
 403
 404        map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "fsl,tempmon");
 405        if (IS_ERR(map)) {
 406                ret = PTR_ERR(map);
 407                dev_err(&pdev->dev, "failed to get tempmon regmap: %d\n", ret);
 408                return ret;
 409        }
 410        data->tempmon = map;
 411
 412        data->irq = platform_get_irq(pdev, 0);
 413        if (data->irq < 0)
 414                return data->irq;
 415
 416        ret = devm_request_threaded_irq(&pdev->dev, data->irq,
 417                        imx_thermal_alarm_irq, imx_thermal_alarm_irq_thread,
 418                        0, "imx_thermal", data);
 419        if (ret < 0) {
 420                dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret);
 421                return ret;
 422        }
 423
 424        platform_set_drvdata(pdev, data);
 425
 426        ret = imx_get_sensor_data(pdev);
 427        if (ret) {
 428                dev_err(&pdev->dev, "failed to get sensor data\n");
 429                return ret;
 430        }
 431
 432        /* Make sure sensor is in known good state for measurements */
 433        regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
 434        regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
 435        regmap_write(map, TEMPSENSE1 + REG_CLR, TEMPSENSE1_MEASURE_FREQ);
 436        regmap_write(map, MISC0 + REG_SET, MISC0_REFTOP_SELBIASOFF);
 437        regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
 438
 439        cpumask_set_cpu(0, &clip_cpus);
 440        data->cdev = cpufreq_cooling_register(&clip_cpus);
 441        if (IS_ERR(data->cdev)) {
 442                ret = PTR_ERR(data->cdev);
 443                dev_err(&pdev->dev,
 444                        "failed to register cpufreq cooling device: %d\n", ret);
 445                return ret;
 446        }
 447
 448        data->tz = thermal_zone_device_register("imx_thermal_zone",
 449                                                IMX_TRIP_NUM,
 450                                                BIT(IMX_TRIP_PASSIVE), data,
 451                                                &imx_tz_ops, NULL,
 452                                                IMX_PASSIVE_DELAY,
 453                                                IMX_POLLING_DELAY);
 454        if (IS_ERR(data->tz)) {
 455                ret = PTR_ERR(data->tz);
 456                dev_err(&pdev->dev,
 457                        "failed to register thermal zone device %d\n", ret);
 458                cpufreq_cooling_unregister(data->cdev);
 459                return ret;
 460        }
 461
 462        data->thermal_clk = devm_clk_get(&pdev->dev, NULL);
 463        if (IS_ERR(data->thermal_clk)) {
 464                dev_warn(&pdev->dev, "failed to get thermal clk!\n");
 465        } else {
 466                /*
 467                 * Thermal sensor needs clk on to get correct value, normally
 468                 * we should enable its clk before taking measurement and disable
 469                 * clk after measurement is done, but if alarm function is enabled,
 470                 * hardware will auto measure the temperature periodically, so we
 471                 * need to keep the clk always on for alarm function.
 472                 */
 473                ret = clk_prepare_enable(data->thermal_clk);
 474                if (ret)
 475                        dev_warn(&pdev->dev, "failed to enable thermal clk: %d\n", ret);
 476        }
 477
 478        /* Enable measurements at ~ 10 Hz */
 479        regmap_write(map, TEMPSENSE1 + REG_CLR, TEMPSENSE1_MEASURE_FREQ);
 480        measure_freq = DIV_ROUND_UP(32768, 10); /* 10 Hz */
 481        regmap_write(map, TEMPSENSE1 + REG_SET, measure_freq);
 482        imx_set_alarm_temp(data, data->temp_passive);
 483        regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
 484        regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
 485
 486        data->irq_enabled = true;
 487        data->mode = THERMAL_DEVICE_ENABLED;
 488
 489        return 0;
 490}
 491
 492static int imx_thermal_remove(struct platform_device *pdev)
 493{
 494        struct imx_thermal_data *data = platform_get_drvdata(pdev);
 495        struct regmap *map = data->tempmon;
 496
 497        /* Disable measurements */
 498        regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
 499        if (!IS_ERR(data->thermal_clk))
 500                clk_disable_unprepare(data->thermal_clk);
 501
 502        thermal_zone_device_unregister(data->tz);
 503        cpufreq_cooling_unregister(data->cdev);
 504
 505        return 0;
 506}
 507
 508#ifdef CONFIG_PM_SLEEP
 509static int imx_thermal_suspend(struct device *dev)
 510{
 511        struct imx_thermal_data *data = dev_get_drvdata(dev);
 512        struct regmap *map = data->tempmon;
 513
 514        /*
 515         * Need to disable thermal sensor, otherwise, when thermal core
 516         * try to get temperature before thermal sensor resume, a wrong
 517         * temperature will be read as the thermal sensor is powered
 518         * down.
 519         */
 520        regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
 521        regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
 522        data->mode = THERMAL_DEVICE_DISABLED;
 523
 524        return 0;
 525}
 526
 527static int imx_thermal_resume(struct device *dev)
 528{
 529        struct imx_thermal_data *data = dev_get_drvdata(dev);
 530        struct regmap *map = data->tempmon;
 531
 532        /* Enabled thermal sensor after resume */
 533        regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
 534        regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
 535        data->mode = THERMAL_DEVICE_ENABLED;
 536
 537        return 0;
 538}
 539#endif
 540
 541static SIMPLE_DEV_PM_OPS(imx_thermal_pm_ops,
 542                         imx_thermal_suspend, imx_thermal_resume);
 543
 544static const struct of_device_id of_imx_thermal_match[] = {
 545        { .compatible = "fsl,imx6q-tempmon", },
 546        { /* end */ }
 547};
 548MODULE_DEVICE_TABLE(of, of_imx_thermal_match);
 549
 550static struct platform_driver imx_thermal = {
 551        .driver = {
 552                .name   = "imx_thermal",
 553                .owner  = THIS_MODULE,
 554                .pm     = &imx_thermal_pm_ops,
 555                .of_match_table = of_imx_thermal_match,
 556        },
 557        .probe          = imx_thermal_probe,
 558        .remove         = imx_thermal_remove,
 559};
 560module_platform_driver(imx_thermal);
 561
 562MODULE_AUTHOR("Freescale Semiconductor, Inc.");
 563MODULE_DESCRIPTION("Thermal driver for Freescale i.MX SoCs");
 564MODULE_LICENSE("GPL v2");
 565MODULE_ALIAS("platform:imx-thermal");
 566
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.