diff options
-rw-r--r-- | drivers/thermal/imx_thermal.c | 140 |
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 | ||
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 | static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp) | 91 | static 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 | ||
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 | static int imx_thermal_probe(struct platform_device *pdev) | 390 | static 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) | |||
364 | static int imx_thermal_remove(struct platform_device *pdev) | 474 | static 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); |