root/drivers/thermal/db8500_thermal.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. db8500_thermal_get_temp
  2. db8500_thermal_get_trend
  3. db8500_thermal_update_config
  4. prcmu_low_irq_handler
  5. prcmu_high_irq_handler
  6. db8500_thermal_probe
  7. db8500_thermal_suspend
  8. db8500_thermal_resume

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * db8500_thermal.c - DB8500 Thermal Management Implementation
   4  *
   5  * Copyright (C) 2012 ST-Ericsson
   6  * Copyright (C) 2012-2019 Linaro Ltd.
   7  *
   8  * Authors: Hongbo Zhang, Linus Walleij
   9  */
  10 
  11 #include <linux/cpu_cooling.h>
  12 #include <linux/interrupt.h>
  13 #include <linux/mfd/dbx500-prcmu.h>
  14 #include <linux/module.h>
  15 #include <linux/of.h>
  16 #include <linux/platform_device.h>
  17 #include <linux/slab.h>
  18 #include <linux/thermal.h>
  19 
  20 #define PRCMU_DEFAULT_MEASURE_TIME      0xFFF
  21 #define PRCMU_DEFAULT_LOW_TEMP          0
  22 
  23 /**
  24  * db8500_thermal_points - the interpolation points that trigger
  25  * interrupts
  26  */
  27 static const unsigned long db8500_thermal_points[] = {
  28         15000,
  29         20000,
  30         25000,
  31         30000,
  32         35000,
  33         40000,
  34         45000,
  35         50000,
  36         55000,
  37         60000,
  38         65000,
  39         70000,
  40         75000,
  41         80000,
  42         /*
  43          * This is where things start to get really bad for the
  44          * SoC and the thermal zones should be set up to trigger
  45          * critical temperature at 85000 mC so we don't get above
  46          * this point.
  47          */
  48         85000,
  49         90000,
  50         95000,
  51         100000,
  52 };
  53 
  54 struct db8500_thermal_zone {
  55         struct thermal_zone_device *tz;
  56         enum thermal_trend trend;
  57         unsigned long interpolated_temp;
  58         unsigned int cur_index;
  59 };
  60 
  61 /* Callback to get current temperature */
  62 static int db8500_thermal_get_temp(void *data, int *temp)
  63 {
  64         struct db8500_thermal_zone *th = data;
  65 
  66         /*
  67          * TODO: There is no PRCMU interface to get temperature data currently,
  68          * so a pseudo temperature is returned , it works for thermal framework
  69          * and this will be fixed when the PRCMU interface is available.
  70          */
  71         *temp = th->interpolated_temp;
  72 
  73         return 0;
  74 }
  75 
  76 /* Callback to get temperature changing trend */
  77 static int db8500_thermal_get_trend(void *data, int trip, enum thermal_trend *trend)
  78 {
  79         struct db8500_thermal_zone *th = data;
  80 
  81         *trend = th->trend;
  82 
  83         return 0;
  84 }
  85 
  86 static struct thermal_zone_of_device_ops thdev_ops = {
  87         .get_temp = db8500_thermal_get_temp,
  88         .get_trend = db8500_thermal_get_trend,
  89 };
  90 
  91 static void db8500_thermal_update_config(struct db8500_thermal_zone *th,
  92                                          unsigned int idx,
  93                                          enum thermal_trend trend,
  94                                          unsigned long next_low,
  95                                          unsigned long next_high)
  96 {
  97         prcmu_stop_temp_sense();
  98 
  99         th->cur_index = idx;
 100         th->interpolated_temp = (next_low + next_high)/2;
 101         th->trend = trend;
 102 
 103         /*
 104          * The PRCMU accept absolute temperatures in celsius so divide
 105          * down the millicelsius with 1000
 106          */
 107         prcmu_config_hotmon((u8)(next_low/1000), (u8)(next_high/1000));
 108         prcmu_start_temp_sense(PRCMU_DEFAULT_MEASURE_TIME);
 109 }
 110 
 111 static irqreturn_t prcmu_low_irq_handler(int irq, void *irq_data)
 112 {
 113         struct db8500_thermal_zone *th = irq_data;
 114         unsigned int idx = th->cur_index;
 115         unsigned long next_low, next_high;
 116 
 117         if (idx == 0)
 118                 /* Meaningless for thermal management, ignoring it */
 119                 return IRQ_HANDLED;
 120 
 121         if (idx == 1) {
 122                 next_high = db8500_thermal_points[0];
 123                 next_low = PRCMU_DEFAULT_LOW_TEMP;
 124         } else {
 125                 next_high = db8500_thermal_points[idx - 1];
 126                 next_low = db8500_thermal_points[idx - 2];
 127         }
 128         idx -= 1;
 129 
 130         db8500_thermal_update_config(th, idx, THERMAL_TREND_DROPPING,
 131                                      next_low, next_high);
 132         dev_dbg(&th->tz->device,
 133                 "PRCMU set max %ld, min %ld\n", next_high, next_low);
 134 
 135         thermal_zone_device_update(th->tz, THERMAL_EVENT_UNSPECIFIED);
 136 
 137         return IRQ_HANDLED;
 138 }
 139 
 140 static irqreturn_t prcmu_high_irq_handler(int irq, void *irq_data)
 141 {
 142         struct db8500_thermal_zone *th = irq_data;
 143         unsigned int idx = th->cur_index;
 144         unsigned long next_low, next_high;
 145         int num_points = ARRAY_SIZE(db8500_thermal_points);
 146 
 147         if (idx < num_points - 1) {
 148                 next_high = db8500_thermal_points[idx+1];
 149                 next_low = db8500_thermal_points[idx];
 150                 idx += 1;
 151 
 152                 db8500_thermal_update_config(th, idx, THERMAL_TREND_RAISING,
 153                                              next_low, next_high);
 154 
 155                 dev_dbg(&th->tz->device,
 156                         "PRCMU set max %ld, min %ld\n", next_high, next_low);
 157         } else if (idx == num_points - 1)
 158                 /* So we roof out 1 degree over the max point */
 159                 th->interpolated_temp = db8500_thermal_points[idx] + 1;
 160 
 161         thermal_zone_device_update(th->tz, THERMAL_EVENT_UNSPECIFIED);
 162 
 163         return IRQ_HANDLED;
 164 }
 165 
 166 static int db8500_thermal_probe(struct platform_device *pdev)
 167 {
 168         struct db8500_thermal_zone *th = NULL;
 169         struct device *dev = &pdev->dev;
 170         int low_irq, high_irq, ret = 0;
 171 
 172         th = devm_kzalloc(dev, sizeof(*th), GFP_KERNEL);
 173         if (!th)
 174                 return -ENOMEM;
 175 
 176         low_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_LOW");
 177         if (low_irq < 0) {
 178                 dev_err(dev, "Get IRQ_HOTMON_LOW failed\n");
 179                 return low_irq;
 180         }
 181 
 182         ret = devm_request_threaded_irq(dev, low_irq, NULL,
 183                 prcmu_low_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
 184                 "dbx500_temp_low", th);
 185         if (ret < 0) {
 186                 dev_err(dev, "failed to allocate temp low irq\n");
 187                 return ret;
 188         }
 189 
 190         high_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_HIGH");
 191         if (high_irq < 0) {
 192                 dev_err(dev, "Get IRQ_HOTMON_HIGH failed\n");
 193                 return high_irq;
 194         }
 195 
 196         ret = devm_request_threaded_irq(dev, high_irq, NULL,
 197                 prcmu_high_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
 198                 "dbx500_temp_high", th);
 199         if (ret < 0) {
 200                 dev_err(dev, "failed to allocate temp high irq\n");
 201                 return ret;
 202         }
 203 
 204         /* register of thermal sensor and get info from DT */
 205         th->tz = devm_thermal_zone_of_sensor_register(dev, 0, th, &thdev_ops);
 206         if (IS_ERR(th->tz)) {
 207                 dev_err(dev, "register thermal zone sensor failed\n");
 208                 return PTR_ERR(th->tz);
 209         }
 210         dev_info(dev, "thermal zone sensor registered\n");
 211 
 212         /* Start measuring at the lowest point */
 213         db8500_thermal_update_config(th, 0, THERMAL_TREND_STABLE,
 214                                      PRCMU_DEFAULT_LOW_TEMP,
 215                                      db8500_thermal_points[0]);
 216 
 217         platform_set_drvdata(pdev, th);
 218 
 219         return 0;
 220 }
 221 
 222 static int db8500_thermal_suspend(struct platform_device *pdev,
 223                 pm_message_t state)
 224 {
 225         prcmu_stop_temp_sense();
 226 
 227         return 0;
 228 }
 229 
 230 static int db8500_thermal_resume(struct platform_device *pdev)
 231 {
 232         struct db8500_thermal_zone *th = platform_get_drvdata(pdev);
 233 
 234         /* Resume and start measuring at the lowest point */
 235         db8500_thermal_update_config(th, 0, THERMAL_TREND_STABLE,
 236                                      PRCMU_DEFAULT_LOW_TEMP,
 237                                      db8500_thermal_points[0]);
 238 
 239         return 0;
 240 }
 241 
 242 static const struct of_device_id db8500_thermal_match[] = {
 243         { .compatible = "stericsson,db8500-thermal" },
 244         {},
 245 };
 246 MODULE_DEVICE_TABLE(of, db8500_thermal_match);
 247 
 248 static struct platform_driver db8500_thermal_driver = {
 249         .driver = {
 250                 .name = "db8500-thermal",
 251                 .of_match_table = of_match_ptr(db8500_thermal_match),
 252         },
 253         .probe = db8500_thermal_probe,
 254         .suspend = db8500_thermal_suspend,
 255         .resume = db8500_thermal_resume,
 256 };
 257 
 258 module_platform_driver(db8500_thermal_driver);
 259 
 260 MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@stericsson.com>");
 261 MODULE_DESCRIPTION("DB8500 thermal driver");
 262 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */