Commit 37713a1e8e4c1a1067ad4c99296f78d3c82ed9c4

Authored by Philipp Zabel
Committed by Zhang Rui
1 parent 017e51420c

thermal: imx: implement thermal alarm interrupt handling

Enable automatic measurements at 10 Hz and use the alarm interrupt to react
more quickly to sudden temperature changes above the passive or critical
temperature trip points.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Acked-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>

Showing 1 changed file with 127 additions and 13 deletions Side-by-side Diff

drivers/thermal/imx_thermal.c
... ... @@ -12,6 +12,7 @@
12 12 #include <linux/delay.h>
13 13 #include <linux/device.h>
14 14 #include <linux/init.h>
  15 +#include <linux/interrupt.h>
15 16 #include <linux/io.h>
16 17 #include <linux/kernel.h>
17 18 #include <linux/mfd/syscon.h>
... ... @@ -31,6 +32,8 @@
31 32 #define MISC0_REFTOP_SELBIASOFF (1 << 3)
32 33  
33 34 #define TEMPSENSE0 0x0180
  35 +#define TEMPSENSE0_ALARM_VALUE_SHIFT 20
  36 +#define TEMPSENSE0_ALARM_VALUE_MASK (0xfff << TEMPSENSE0_ALARM_VALUE_SHIFT)
34 37 #define TEMPSENSE0_TEMP_CNT_SHIFT 8
35 38 #define TEMPSENSE0_TEMP_CNT_MASK (0xfff << TEMPSENSE0_TEMP_CNT_SHIFT)
36 39 #define TEMPSENSE0_FINISHED (1 << 2)
37 40  
38 41  
39 42  
40 43  
41 44  
42 45  
43 46  
44 47  
... ... @@ -66,34 +69,63 @@
66 69 int c1, c2; /* See formula in imx_get_sensor_data() */
67 70 unsigned long temp_passive;
68 71 unsigned long temp_critical;
  72 + unsigned long alarm_temp;
  73 + unsigned long last_temp;
  74 + bool irq_enabled;
  75 + int irq;
69 76 };
70 77  
  78 +static void imx_set_alarm_temp(struct imx_thermal_data *data,
  79 + signed long alarm_temp)
  80 +{
  81 + struct regmap *map = data->tempmon;
  82 + int alarm_value;
  83 +
  84 + data->alarm_temp = alarm_temp;
  85 + alarm_value = (alarm_temp - data->c2) / data->c1;
  86 + regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_ALARM_VALUE_MASK);
  87 + regmap_write(map, TEMPSENSE0 + REG_SET, alarm_value <<
  88 + TEMPSENSE0_ALARM_VALUE_SHIFT);
  89 +}
  90 +
71 91 static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
72 92 {
73 93 struct imx_thermal_data *data = tz->devdata;
74 94 struct regmap *map = data->tempmon;
75   - static unsigned long last_temp;
76 95 unsigned int n_meas;
  96 + bool wait;
77 97 u32 val;
78 98  
79   - /*
80   - * Every time we measure the temperature, we will power on the
81   - * temperature sensor, enable measurements, take a reading,
82   - * disable measurements, power off the temperature sensor.
83   - */
84   - regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
85   - regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
  99 + if (data->mode == THERMAL_DEVICE_ENABLED) {
  100 + /* Check if a measurement is currently in progress */
  101 + regmap_read(map, TEMPSENSE0, &val);
  102 + wait = !(val & TEMPSENSE0_FINISHED);
  103 + } else {
  104 + /*
  105 + * Every time we measure the temperature, we will power on the
  106 + * temperature sensor, enable measurements, take a reading,
  107 + * disable measurements, power off the temperature sensor.
  108 + */
  109 + regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
  110 + regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
86 111  
  112 + wait = true;
  113 + }
  114 +
87 115 /*
88 116 * According to the temp sensor designers, it may require up to ~17us
89 117 * to complete a measurement.
90 118 */
91   - usleep_range(20, 50);
  119 + if (wait)
  120 + usleep_range(20, 50);
92 121  
93 122 regmap_read(map, TEMPSENSE0, &val);
94   - regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
95   - regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
96 123  
  124 + if (data->mode != THERMAL_DEVICE_ENABLED) {
  125 + regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
  126 + regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
  127 + }
  128 +
97 129 if ((val & TEMPSENSE0_FINISHED) == 0) {
98 130 dev_dbg(&tz->device, "temp measurement never finished\n");
99 131 return -EAGAIN;
100 132  
101 133  
... ... @@ -104,11 +136,26 @@
104 136 /* See imx_get_sensor_data() for formula derivation */
105 137 *temp = data->c2 + data->c1 * n_meas;
106 138  
107   - if (*temp != last_temp) {
  139 + /* Update alarm value to next higher trip point */
  140 + if (data->alarm_temp == data->temp_passive && *temp >= data->temp_passive)
  141 + imx_set_alarm_temp(data, data->temp_critical);
  142 + if (data->alarm_temp == data->temp_critical && *temp < data->temp_passive) {
  143 + imx_set_alarm_temp(data, data->temp_passive);
  144 + dev_dbg(&tz->device, "thermal alarm off: T < %lu\n",
  145 + data->alarm_temp / 1000);
  146 + }
  147 +
  148 + if (*temp != data->last_temp) {
108 149 dev_dbg(&tz->device, "millicelsius: %ld\n", *temp);
109   - last_temp = *temp;
  150 + data->last_temp = *temp;
110 151 }
111 152  
  153 + /* Reenable alarm IRQ if temperature below alarm temperature */
  154 + if (!data->irq_enabled && *temp < data->alarm_temp) {
  155 + data->irq_enabled = true;
  156 + enable_irq(data->irq);
  157 + }
  158 +
112 159 return 0;
113 160 }
114 161  
115 162  
116 163  
117 164  
... ... @@ -126,13 +173,30 @@
126 173 enum thermal_device_mode mode)
127 174 {
128 175 struct imx_thermal_data *data = tz->devdata;
  176 + struct regmap *map = data->tempmon;
129 177  
130 178 if (mode == THERMAL_DEVICE_ENABLED) {
131 179 tz->polling_delay = IMX_POLLING_DELAY;
132 180 tz->passive_delay = IMX_PASSIVE_DELAY;
  181 +
  182 + regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
  183 + regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
  184 +
  185 + if (!data->irq_enabled) {
  186 + data->irq_enabled = true;
  187 + enable_irq(data->irq);
  188 + }
133 189 } else {
  190 + regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
  191 + regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
  192 +
134 193 tz->polling_delay = 0;
135 194 tz->passive_delay = 0;
  195 +
  196 + if (data->irq_enabled) {
  197 + disable_irq(data->irq);
  198 + data->irq_enabled = false;
  199 + }
136 200 }
137 201  
138 202 data->mode = mode;
... ... @@ -181,6 +245,8 @@
181 245  
182 246 data->temp_passive = temp;
183 247  
  248 + imx_set_alarm_temp(data, temp);
  249 +
184 250 return 0;
185 251 }
186 252  
187 253  
... ... @@ -299,11 +365,34 @@
299 365 return 0;
300 366 }
301 367  
  368 +static irqreturn_t imx_thermal_alarm_irq(int irq, void *dev)
  369 +{
  370 + struct imx_thermal_data *data = dev;
  371 +
  372 + disable_irq_nosync(irq);
  373 + data->irq_enabled = false;
  374 +
  375 + return IRQ_WAKE_THREAD;
  376 +}
  377 +
  378 +static irqreturn_t imx_thermal_alarm_irq_thread(int irq, void *dev)
  379 +{
  380 + struct imx_thermal_data *data = dev;
  381 +
  382 + dev_dbg(&data->tz->device, "THERMAL ALARM: T > %lu\n",
  383 + data->alarm_temp / 1000);
  384 +
  385 + thermal_zone_device_update(data->tz);
  386 +
  387 + return IRQ_HANDLED;
  388 +}
  389 +
302 390 static int imx_thermal_probe(struct platform_device *pdev)
303 391 {
304 392 struct imx_thermal_data *data;
305 393 struct cpumask clip_cpus;
306 394 struct regmap *map;
  395 + int measure_freq;
307 396 int ret;
308 397  
309 398 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
... ... @@ -318,6 +407,18 @@
318 407 }
319 408 data->tempmon = map;
320 409  
  410 + data->irq = platform_get_irq(pdev, 0);
  411 + if (data->irq < 0)
  412 + return data->irq;
  413 +
  414 + ret = devm_request_threaded_irq(&pdev->dev, data->irq,
  415 + imx_thermal_alarm_irq, imx_thermal_alarm_irq_thread,
  416 + 0, "imx_thermal", data);
  417 + if (ret < 0) {
  418 + dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret);
  419 + return ret;
  420 + }
  421 +
321 422 platform_set_drvdata(pdev, data);
322 423  
323 424 ret = imx_get_sensor_data(pdev);
... ... @@ -356,6 +457,15 @@
356 457 return ret;
357 458 }
358 459  
  460 + /* Enable measurements at ~ 10 Hz */
  461 + regmap_write(map, TEMPSENSE1 + REG_CLR, TEMPSENSE1_MEASURE_FREQ);
  462 + measure_freq = DIV_ROUND_UP(32768, 10); /* 10 Hz */
  463 + regmap_write(map, TEMPSENSE1 + REG_SET, measure_freq);
  464 + imx_set_alarm_temp(data, data->temp_passive);
  465 + regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
  466 + regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
  467 +
  468 + data->irq_enabled = true;
359 469 data->mode = THERMAL_DEVICE_ENABLED;
360 470  
361 471 return 0;
... ... @@ -364,6 +474,10 @@
364 474 static int imx_thermal_remove(struct platform_device *pdev)
365 475 {
366 476 struct imx_thermal_data *data = platform_get_drvdata(pdev);
  477 + struct regmap *map = data->tempmon;
  478 +
  479 + /* Disable measurements */
  480 + regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
367 481  
368 482 thermal_zone_device_unregister(data->tz);
369 483 cpufreq_cooling_unregister(data->cdev);