diff options
author | Daniel Lezcano <daniel.lezcano@linaro.org> | 2017-10-19 13:05:51 -0400 |
---|---|---|
committer | Eduardo Valentin <edubezval@gmail.com> | 2017-10-31 22:32:16 -0400 |
commit | 10d7e9a9181f4637640f388d334c6740c1b5d0e8 (patch) | |
tree | 51467cf32cef550e044cdafd425844b9d143cc76 | |
parent | b424315a287c70eeb5f920f84c92492bd2f5658e (diff) |
thermal/drivers/hisi: Remove costly sensor inspection
The sensor is all setup, bind, resetted, acked, etc... every single second.
That was the way to workaround a problem with the interrupt bouncing again and
again.
With the following changes, we fix all in one:
- Do the setup, one time, at probe time
- Add the IRQF_ONESHOT, ack the interrupt in the threaded handler
- Remove the interrupt handler
- Set the correct value for the LAG register
- Remove all the irq_enabled stuff in the code as the interruption
handling is fixed
- Remove the 3ms delay
- Reorder the initialization routine to be in the right order
It ends up to a nicer code and more efficient, the 3-5ms delay is removed from
the get_temp() path.
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Reviewed-by: Leo Yan <leo.yan@linaro.org>
Tested-by: Leo Yan <leo.yan@linaro.org>
Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
-rw-r--r-- | drivers/thermal/hisi_thermal.c | 203 |
1 files changed, 93 insertions, 110 deletions
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c index f3fb8e2a7f0c..7c5d4647cb5e 100644 --- a/drivers/thermal/hisi_thermal.c +++ b/drivers/thermal/hisi_thermal.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #define HISI_TEMP_BASE (-60000) | 41 | #define HISI_TEMP_BASE (-60000) |
42 | #define HISI_TEMP_RESET (100000) | 42 | #define HISI_TEMP_RESET (100000) |
43 | #define HISI_TEMP_STEP (784) | 43 | #define HISI_TEMP_STEP (784) |
44 | #define HISI_TEMP_LAG (3500) | ||
44 | 45 | ||
45 | #define HISI_MAX_SENSORS 4 | 46 | #define HISI_MAX_SENSORS 4 |
46 | #define HISI_DEFAULT_SENSOR 2 | 47 | #define HISI_DEFAULT_SENSOR 2 |
@@ -60,8 +61,6 @@ struct hisi_thermal_data { | |||
60 | struct clk *clk; | 61 | struct clk *clk; |
61 | struct hisi_thermal_sensor sensors; | 62 | struct hisi_thermal_sensor sensors; |
62 | int irq; | 63 | int irq; |
63 | bool irq_enabled; | ||
64 | |||
65 | void __iomem *regs; | 64 | void __iomem *regs; |
66 | }; | 65 | }; |
67 | 66 | ||
@@ -99,9 +98,40 @@ static inline long hisi_thermal_round_temp(int temp) | |||
99 | hisi_thermal_temp_to_step(temp)); | 98 | hisi_thermal_temp_to_step(temp)); |
100 | } | 99 | } |
101 | 100 | ||
101 | /* | ||
102 | * The lag register contains 5 bits encoding the temperature in steps. | ||
103 | * | ||
104 | * Each time the temperature crosses the threshold boundary, an | ||
105 | * interrupt is raised. It could be when the temperature is going | ||
106 | * above the threshold or below. However, if the temperature is | ||
107 | * fluctuating around this value due to the load, we can receive | ||
108 | * several interrupts which may not desired. | ||
109 | * | ||
110 | * We can setup a temperature representing the delta between the | ||
111 | * threshold and the current temperature when the temperature is | ||
112 | * decreasing. | ||
113 | * | ||
114 | * For instance: the lag register is 5°C, the threshold is 65°C, when | ||
115 | * the temperature reaches 65°C an interrupt is raised and when the | ||
116 | * temperature decrease to 65°C - 5°C another interrupt is raised. | ||
117 | * | ||
118 | * A very short lag can lead to an interrupt storm, a long lag | ||
119 | * increase the latency to react to the temperature changes. In our | ||
120 | * case, that is not really a problem as we are polling the | ||
121 | * temperature. | ||
122 | * | ||
123 | * [0:4] : lag register | ||
124 | * | ||
125 | * The temperature is coded in steps, cf. HISI_TEMP_STEP. | ||
126 | * | ||
127 | * Min : 0x00 : 0.0 °C | ||
128 | * Max : 0x1F : 24.3 °C | ||
129 | * | ||
130 | * The 'value' parameter is in milliCelsius. | ||
131 | */ | ||
102 | static inline void hisi_thermal_set_lag(void __iomem *addr, int value) | 132 | static inline void hisi_thermal_set_lag(void __iomem *addr, int value) |
103 | { | 133 | { |
104 | writel(value, addr + TEMP0_LAG); | 134 | writel((value / HISI_TEMP_STEP) & 0x1F, addr + TEMP0_LAG); |
105 | } | 135 | } |
106 | 136 | ||
107 | static inline void hisi_thermal_alarm_clear(void __iomem *addr, int value) | 137 | static inline void hisi_thermal_alarm_clear(void __iomem *addr, int value) |
@@ -171,71 +201,6 @@ static inline void hisi_thermal_hdak_set(void __iomem *addr, int value) | |||
171 | (value << 4), addr + TEMP0_CFG); | 201 | (value << 4), addr + TEMP0_CFG); |
172 | } | 202 | } |
173 | 203 | ||
174 | static long hisi_thermal_get_sensor_temp(struct hisi_thermal_data *data, | ||
175 | struct hisi_thermal_sensor *sensor) | ||
176 | { | ||
177 | long val; | ||
178 | |||
179 | mutex_lock(&data->thermal_lock); | ||
180 | |||
181 | /* disable interrupt */ | ||
182 | hisi_thermal_alarm_enable(data->regs, 0); | ||
183 | hisi_thermal_alarm_clear(data->regs, 1); | ||
184 | |||
185 | /* disable module firstly */ | ||
186 | hisi_thermal_enable(data->regs, 0); | ||
187 | |||
188 | /* select sensor id */ | ||
189 | hisi_thermal_sensor_select(data->regs, sensor->id); | ||
190 | |||
191 | /* enable module */ | ||
192 | hisi_thermal_enable(data->regs, 1); | ||
193 | |||
194 | usleep_range(3000, 5000); | ||
195 | |||
196 | val = hisi_thermal_get_temperature(data->regs); | ||
197 | |||
198 | mutex_unlock(&data->thermal_lock); | ||
199 | |||
200 | return val; | ||
201 | } | ||
202 | |||
203 | static void hisi_thermal_enable_bind_irq_sensor | ||
204 | (struct hisi_thermal_data *data) | ||
205 | { | ||
206 | struct hisi_thermal_sensor *sensor; | ||
207 | |||
208 | mutex_lock(&data->thermal_lock); | ||
209 | |||
210 | sensor = &data->sensors; | ||
211 | |||
212 | /* setting the hdak time */ | ||
213 | hisi_thermal_hdak_set(data->regs, 0); | ||
214 | |||
215 | /* disable module firstly */ | ||
216 | hisi_thermal_reset_enable(data->regs, 0); | ||
217 | hisi_thermal_enable(data->regs, 0); | ||
218 | |||
219 | /* select sensor id */ | ||
220 | hisi_thermal_sensor_select(data->regs, sensor->id); | ||
221 | |||
222 | /* enable for interrupt */ | ||
223 | hisi_thermal_alarm_set(data->regs, sensor->thres_temp); | ||
224 | |||
225 | hisi_thermal_reset_set(data->regs, HISI_TEMP_RESET); | ||
226 | |||
227 | /* enable module */ | ||
228 | hisi_thermal_reset_enable(data->regs, 1); | ||
229 | hisi_thermal_enable(data->regs, 1); | ||
230 | |||
231 | hisi_thermal_alarm_clear(data->regs, 0); | ||
232 | hisi_thermal_alarm_enable(data->regs, 1); | ||
233 | |||
234 | usleep_range(3000, 5000); | ||
235 | |||
236 | mutex_unlock(&data->thermal_lock); | ||
237 | } | ||
238 | |||
239 | static void hisi_thermal_disable_sensor(struct hisi_thermal_data *data) | 204 | static void hisi_thermal_disable_sensor(struct hisi_thermal_data *data) |
240 | { | 205 | { |
241 | mutex_lock(&data->thermal_lock); | 206 | mutex_lock(&data->thermal_lock); |
@@ -253,25 +218,10 @@ static int hisi_thermal_get_temp(void *_sensor, int *temp) | |||
253 | struct hisi_thermal_sensor *sensor = _sensor; | 218 | struct hisi_thermal_sensor *sensor = _sensor; |
254 | struct hisi_thermal_data *data = sensor->thermal; | 219 | struct hisi_thermal_data *data = sensor->thermal; |
255 | 220 | ||
256 | *temp = hisi_thermal_get_sensor_temp(data, sensor); | 221 | *temp = hisi_thermal_get_temperature(data->regs); |
257 | |||
258 | dev_dbg(&data->pdev->dev, "id=%d, irq=%d, temp=%d, thres=%d\n", | ||
259 | sensor->id, data->irq_enabled, *temp, sensor->thres_temp); | ||
260 | /* | ||
261 | * Bind irq to sensor for two cases: | ||
262 | * Reenable alarm IRQ if temperature below threshold; | ||
263 | * if irq has been enabled, always set it; | ||
264 | */ | ||
265 | if (data->irq_enabled) { | ||
266 | hisi_thermal_enable_bind_irq_sensor(data); | ||
267 | return 0; | ||
268 | } | ||
269 | 222 | ||
270 | if (*temp < sensor->thres_temp) { | 223 | dev_dbg(&data->pdev->dev, "id=%d, temp=%d, thres=%d\n", |
271 | data->irq_enabled = true; | 224 | sensor->id, *temp, sensor->thres_temp); |
272 | hisi_thermal_enable_bind_irq_sensor(data); | ||
273 | enable_irq(data->irq); | ||
274 | } | ||
275 | 225 | ||
276 | return 0; | 226 | return 0; |
277 | } | 227 | } |
@@ -280,26 +230,27 @@ static const struct thermal_zone_of_device_ops hisi_of_thermal_ops = { | |||
280 | .get_temp = hisi_thermal_get_temp, | 230 | .get_temp = hisi_thermal_get_temp, |
281 | }; | 231 | }; |
282 | 232 | ||
283 | static irqreturn_t hisi_thermal_alarm_irq(int irq, void *dev) | 233 | static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev) |
284 | { | 234 | { |
285 | struct hisi_thermal_data *data = dev; | 235 | struct hisi_thermal_data *data = dev; |
236 | struct hisi_thermal_sensor *sensor = &data->sensors; | ||
237 | int temp; | ||
286 | 238 | ||
287 | disable_irq_nosync(irq); | 239 | hisi_thermal_alarm_clear(data->regs, 1); |
288 | data->irq_enabled = false; | ||
289 | 240 | ||
290 | return IRQ_WAKE_THREAD; | 241 | temp = hisi_thermal_get_temperature(data->regs); |
291 | } | ||
292 | 242 | ||
293 | static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev) | 243 | if (temp >= sensor->thres_temp) { |
294 | { | 244 | dev_crit(&data->pdev->dev, "THERMAL ALARM: %d > %d\n", |
295 | struct hisi_thermal_data *data = dev; | 245 | temp, sensor->thres_temp); |
296 | struct hisi_thermal_sensor *sensor = &data->sensors; | ||
297 | 246 | ||
298 | dev_crit(&data->pdev->dev, "THERMAL ALARM: T > %d\n", | 247 | thermal_zone_device_update(data->sensors.tzd, |
299 | sensor->thres_temp); | 248 | THERMAL_EVENT_UNSPECIFIED); |
300 | 249 | ||
301 | thermal_zone_device_update(data->sensors.tzd, | 250 | } else if (temp < sensor->thres_temp) { |
302 | THERMAL_EVENT_UNSPECIFIED); | 251 | dev_crit(&data->pdev->dev, "THERMAL ALARM stopped: %d < %d\n", |
252 | temp, sensor->thres_temp); | ||
253 | } | ||
303 | 254 | ||
304 | return IRQ_HANDLED; | 255 | return IRQ_HANDLED; |
305 | } | 256 | } |
@@ -352,6 +303,40 @@ static void hisi_thermal_toggle_sensor(struct hisi_thermal_sensor *sensor, | |||
352 | on ? THERMAL_DEVICE_ENABLED : THERMAL_DEVICE_DISABLED); | 303 | on ? THERMAL_DEVICE_ENABLED : THERMAL_DEVICE_DISABLED); |
353 | } | 304 | } |
354 | 305 | ||
306 | static int hisi_thermal_setup(struct hisi_thermal_data *data) | ||
307 | { | ||
308 | struct hisi_thermal_sensor *sensor; | ||
309 | |||
310 | sensor = &data->sensors; | ||
311 | |||
312 | /* disable module firstly */ | ||
313 | hisi_thermal_reset_enable(data->regs, 0); | ||
314 | hisi_thermal_enable(data->regs, 0); | ||
315 | |||
316 | /* select sensor id */ | ||
317 | hisi_thermal_sensor_select(data->regs, sensor->id); | ||
318 | |||
319 | /* setting the hdak time */ | ||
320 | hisi_thermal_hdak_set(data->regs, 0); | ||
321 | |||
322 | /* setting lag value between current temp and the threshold */ | ||
323 | hisi_thermal_set_lag(data->regs, HISI_TEMP_LAG); | ||
324 | |||
325 | /* enable for interrupt */ | ||
326 | hisi_thermal_alarm_set(data->regs, sensor->thres_temp); | ||
327 | |||
328 | hisi_thermal_reset_set(data->regs, HISI_TEMP_RESET); | ||
329 | |||
330 | /* enable module */ | ||
331 | hisi_thermal_reset_enable(data->regs, 1); | ||
332 | hisi_thermal_enable(data->regs, 1); | ||
333 | |||
334 | hisi_thermal_alarm_clear(data->regs, 0); | ||
335 | hisi_thermal_alarm_enable(data->regs, 1); | ||
336 | |||
337 | return 0; | ||
338 | } | ||
339 | |||
355 | static int hisi_thermal_probe(struct platform_device *pdev) | 340 | static int hisi_thermal_probe(struct platform_device *pdev) |
356 | { | 341 | { |
357 | struct hisi_thermal_data *data; | 342 | struct hisi_thermal_data *data; |
@@ -394,9 +379,6 @@ static int hisi_thermal_probe(struct platform_device *pdev) | |||
394 | return ret; | 379 | return ret; |
395 | } | 380 | } |
396 | 381 | ||
397 | hisi_thermal_enable_bind_irq_sensor(data); | ||
398 | data->irq_enabled = true; | ||
399 | |||
400 | ret = hisi_thermal_register_sensor(pdev, data, | 382 | ret = hisi_thermal_register_sensor(pdev, data, |
401 | &data->sensors, | 383 | &data->sensors, |
402 | HISI_DEFAULT_SENSOR); | 384 | HISI_DEFAULT_SENSOR); |
@@ -406,18 +388,21 @@ static int hisi_thermal_probe(struct platform_device *pdev) | |||
406 | return ret; | 388 | return ret; |
407 | } | 389 | } |
408 | 390 | ||
409 | hisi_thermal_toggle_sensor(&data->sensors, true); | 391 | ret = hisi_thermal_setup(data); |
392 | if (ret) { | ||
393 | dev_err(&pdev->dev, "Failed to setup the sensor: %d\n", ret); | ||
394 | return ret; | ||
395 | } | ||
410 | 396 | ||
411 | ret = devm_request_threaded_irq(&pdev->dev, data->irq, | 397 | ret = devm_request_threaded_irq(&pdev->dev, data->irq, NULL, |
412 | hisi_thermal_alarm_irq, | ||
413 | hisi_thermal_alarm_irq_thread, | 398 | hisi_thermal_alarm_irq_thread, |
414 | 0, "hisi_thermal", data); | 399 | IRQF_ONESHOT, "hisi_thermal", data); |
415 | if (ret < 0) { | 400 | if (ret < 0) { |
416 | dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret); | 401 | dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret); |
417 | return ret; | 402 | return ret; |
418 | } | 403 | } |
419 | 404 | ||
420 | enable_irq(data->irq); | 405 | hisi_thermal_toggle_sensor(&data->sensors, true); |
421 | 406 | ||
422 | return 0; | 407 | return 0; |
423 | } | 408 | } |
@@ -440,7 +425,6 @@ static int hisi_thermal_suspend(struct device *dev) | |||
440 | struct hisi_thermal_data *data = dev_get_drvdata(dev); | 425 | struct hisi_thermal_data *data = dev_get_drvdata(dev); |
441 | 426 | ||
442 | hisi_thermal_disable_sensor(data); | 427 | hisi_thermal_disable_sensor(data); |
443 | data->irq_enabled = false; | ||
444 | 428 | ||
445 | clk_disable_unprepare(data->clk); | 429 | clk_disable_unprepare(data->clk); |
446 | 430 | ||
@@ -456,8 +440,7 @@ static int hisi_thermal_resume(struct device *dev) | |||
456 | if (ret) | 440 | if (ret) |
457 | return ret; | 441 | return ret; |
458 | 442 | ||
459 | data->irq_enabled = true; | 443 | hisi_thermal_setup(data); |
460 | hisi_thermal_enable_bind_irq_sensor(data); | ||
461 | 444 | ||
462 | return 0; | 445 | return 0; |
463 | } | 446 | } |