aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/thermal/imx_thermal.c
diff options
context:
space:
mode:
authorPhilipp Zabel <p.zabel@pengutronix.de>2013-08-01 12:33:12 -0400
committerZhang Rui <rui.zhang@intel.com>2013-08-15 02:57:42 -0400
commit37713a1e8e4c1a1067ad4c99296f78d3c82ed9c4 (patch)
treeee5c675ada74baec0dff4721e7e3569c2c94a1bd /drivers/thermal/imx_thermal.c
parent017e51420cc44098308b00dffd9d4e514ddf40f3 (diff)
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>
Diffstat (limited to 'drivers/thermal/imx_thermal.c')
-rw-r--r--drivers/thermal/imx_thermal.c140
1 files changed, 127 insertions, 13 deletions
diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c
index c9a55b0214e5..1d6c801c1eb9 100644
--- a/drivers/thermal/imx_thermal.c
+++ b/drivers/thermal/imx_thermal.c
@@ -12,6 +12,7 @@
12#include <linux/delay.h> 12#include <linux/delay.h>
13#include <linux/device.h> 13#include <linux/device.h>
14#include <linux/init.h> 14#include <linux/init.h>
15#include <linux/interrupt.h>
15#include <linux/io.h> 16#include <linux/io.h>
16#include <linux/kernel.h> 17#include <linux/kernel.h>
17#include <linux/mfd/syscon.h> 18#include <linux/mfd/syscon.h>
@@ -31,6 +32,8 @@
31#define MISC0_REFTOP_SELBIASOFF (1 << 3) 32#define MISC0_REFTOP_SELBIASOFF (1 << 3)
32 33
33#define TEMPSENSE0 0x0180 34#define TEMPSENSE0 0x0180
35#define TEMPSENSE0_ALARM_VALUE_SHIFT 20
36#define TEMPSENSE0_ALARM_VALUE_MASK (0xfff << TEMPSENSE0_ALARM_VALUE_SHIFT)
34#define TEMPSENSE0_TEMP_CNT_SHIFT 8 37#define TEMPSENSE0_TEMP_CNT_SHIFT 8
35#define TEMPSENSE0_TEMP_CNT_MASK (0xfff << TEMPSENSE0_TEMP_CNT_SHIFT) 38#define TEMPSENSE0_TEMP_CNT_MASK (0xfff << TEMPSENSE0_TEMP_CNT_SHIFT)
36#define TEMPSENSE0_FINISHED (1 << 2) 39#define TEMPSENSE0_FINISHED (1 << 2)
@@ -66,33 +69,62 @@ struct imx_thermal_data {
66 int c1, c2; /* See formula in imx_get_sensor_data() */ 69 int c1, c2; /* See formula in imx_get_sensor_data() */
67 unsigned long temp_passive; 70 unsigned long temp_passive;
68 unsigned long temp_critical; 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
78static 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
71static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp) 91static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
72{ 92{
73 struct imx_thermal_data *data = tz->devdata; 93 struct imx_thermal_data *data = tz->devdata;
74 struct regmap *map = data->tempmon; 94 struct regmap *map = data->tempmon;
75 static unsigned long last_temp;
76 unsigned int n_meas; 95 unsigned int n_meas;
96 bool wait;
77 u32 val; 97 u32 val;
78 98
79 /* 99 if (data->mode == THERMAL_DEVICE_ENABLED) {
80 * Every time we measure the temperature, we will power on the 100 /* Check if a measurement is currently in progress */
81 * temperature sensor, enable measurements, take a reading, 101 regmap_read(map, TEMPSENSE0, &val);
82 * disable measurements, power off the temperature sensor. 102 wait = !(val & TEMPSENSE0_FINISHED);
83 */ 103 } else {
84 regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN); 104 /*
85 regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP); 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);
111
112 wait = true;
113 }
86 114
87 /* 115 /*
88 * According to the temp sensor designers, it may require up to ~17us 116 * According to the temp sensor designers, it may require up to ~17us
89 * to complete a measurement. 117 * to complete a measurement.
90 */ 118 */
91 usleep_range(20, 50); 119 if (wait)
120 usleep_range(20, 50);
92 121
93 regmap_read(map, TEMPSENSE0, &val); 122 regmap_read(map, TEMPSENSE0, &val);
94 regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP); 123
95 regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN); 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 }
96 128
97 if ((val & TEMPSENSE0_FINISHED) == 0) { 129 if ((val & TEMPSENSE0_FINISHED) == 0) {
98 dev_dbg(&tz->device, "temp measurement never finished\n"); 130 dev_dbg(&tz->device, "temp measurement never finished\n");
@@ -104,9 +136,24 @@ static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
104 /* See imx_get_sensor_data() for formula derivation */ 136 /* See imx_get_sensor_data() for formula derivation */
105 *temp = data->c2 + data->c1 * n_meas; 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 dev_dbg(&tz->device, "millicelsius: %ld\n", *temp); 149 dev_dbg(&tz->device, "millicelsius: %ld\n", *temp);
109 last_temp = *temp; 150 data->last_temp = *temp;
151 }
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);
110 } 157 }
111 158
112 return 0; 159 return 0;
@@ -126,13 +173,30 @@ static int imx_set_mode(struct thermal_zone_device *tz,
126 enum thermal_device_mode mode) 173 enum thermal_device_mode mode)
127{ 174{
128 struct imx_thermal_data *data = tz->devdata; 175 struct imx_thermal_data *data = tz->devdata;
176 struct regmap *map = data->tempmon;
129 177
130 if (mode == THERMAL_DEVICE_ENABLED) { 178 if (mode == THERMAL_DEVICE_ENABLED) {
131 tz->polling_delay = IMX_POLLING_DELAY; 179 tz->polling_delay = IMX_POLLING_DELAY;
132 tz->passive_delay = IMX_PASSIVE_DELAY; 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 } else { 189 } else {
190 regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
191 regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
192
134 tz->polling_delay = 0; 193 tz->polling_delay = 0;
135 tz->passive_delay = 0; 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 data->mode = mode; 202 data->mode = mode;
@@ -181,6 +245,8 @@ static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip,
181 245
182 data->temp_passive = temp; 246 data->temp_passive = temp;
183 247
248 imx_set_alarm_temp(data, temp);
249
184 return 0; 250 return 0;
185} 251}
186 252
@@ -299,11 +365,34 @@ static int imx_get_sensor_data(struct platform_device *pdev)
299 return 0; 365 return 0;
300} 366}
301 367
368static 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
378static 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
302static int imx_thermal_probe(struct platform_device *pdev) 390static int imx_thermal_probe(struct platform_device *pdev)
303{ 391{
304 struct imx_thermal_data *data; 392 struct imx_thermal_data *data;
305 struct cpumask clip_cpus; 393 struct cpumask clip_cpus;
306 struct regmap *map; 394 struct regmap *map;
395 int measure_freq;
307 int ret; 396 int ret;
308 397
309 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 398 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
@@ -318,6 +407,18 @@ static int imx_thermal_probe(struct platform_device *pdev)
318 } 407 }
319 data->tempmon = map; 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 platform_set_drvdata(pdev, data); 422 platform_set_drvdata(pdev, data);
322 423
323 ret = imx_get_sensor_data(pdev); 424 ret = imx_get_sensor_data(pdev);
@@ -356,6 +457,15 @@ static int imx_thermal_probe(struct platform_device *pdev)
356 return ret; 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 data->mode = THERMAL_DEVICE_ENABLED; 469 data->mode = THERMAL_DEVICE_ENABLED;
360 470
361 return 0; 471 return 0;
@@ -364,6 +474,10 @@ static int imx_thermal_probe(struct platform_device *pdev)
364static int imx_thermal_remove(struct platform_device *pdev) 474static int imx_thermal_remove(struct platform_device *pdev)
365{ 475{
366 struct imx_thermal_data *data = platform_get_drvdata(pdev); 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 thermal_zone_device_unregister(data->tz); 482 thermal_zone_device_unregister(data->tz);
369 cpufreq_cooling_unregister(data->cdev); 483 cpufreq_cooling_unregister(data->cdev);