aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNiklas Söderlund <niklas.soderlund+renesas@ragnatech.se>2017-03-29 14:43:54 -0400
committerEduardo Valentin <edubezval@gmail.com>2017-03-31 00:45:38 -0400
commit7d4b269776ec67c1b7d83c6c727a2771e5f39d12 (patch)
treed347d8be43ab66bde61ceec8692105c20e4a6ce2
parent97dad1f1d2b3f2a2a77551849357b7ac38b0b6ff (diff)
thermal: rcar_gen3_thermal: enable hardware interrupts for trip points
Enable hardware trip points by implementing the set_trips callback. The thermal core will take care of setting the initial trip point window and to update it once the driver reports a TSC has moved outside it. The interrupt structure for this device is a bit odd. There is not a dedicated IRQ for each TSC, instead the interrupts are shared between all TSCs. IRQn is fired if the temp monitored in IRQTEMPn is reached in any of the TSCs, example IRQ3 is fired if temperature in IRQTEMP3 is reached in either TSC0, TSC1 or TSC2. For this reason the usage of interrupts in this driver is an all-on or all-off design. When an interrupt happens all TSCs are checked and all thermal zones are updated. This could be refined to be more fine grained but the thermal core takes care of only updating the thermal zones that have left their trip point window. Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
-rw-r--r--drivers/thermal/rcar_gen3_thermal.c132
1 files changed, 131 insertions, 1 deletions
diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c
index 4a08b35533dc..d37c7d8f8fcd 100644
--- a/drivers/thermal/rcar_gen3_thermal.c
+++ b/drivers/thermal/rcar_gen3_thermal.c
@@ -23,8 +23,11 @@
23#include <linux/of_device.h> 23#include <linux/of_device.h>
24#include <linux/platform_device.h> 24#include <linux/platform_device.h>
25#include <linux/pm_runtime.h> 25#include <linux/pm_runtime.h>
26#include <linux/spinlock.h>
26#include <linux/thermal.h> 27#include <linux/thermal.h>
27 28
29#include "thermal_core.h"
30
28/* Register offsets */ 31/* Register offsets */
29#define REG_GEN3_IRQSTR 0x04 32#define REG_GEN3_IRQSTR 0x04
30#define REG_GEN3_IRQMSK 0x08 33#define REG_GEN3_IRQMSK 0x08
@@ -40,6 +43,14 @@
40#define REG_GEN3_THCODE2 0x54 43#define REG_GEN3_THCODE2 0x54
41#define REG_GEN3_THCODE3 0x58 44#define REG_GEN3_THCODE3 0x58
42 45
46/* IRQ{STR,MSK,EN} bits */
47#define IRQ_TEMP1 BIT(0)
48#define IRQ_TEMP2 BIT(1)
49#define IRQ_TEMP3 BIT(2)
50#define IRQ_TEMPD1 BIT(3)
51#define IRQ_TEMPD2 BIT(4)
52#define IRQ_TEMPD3 BIT(5)
53
43/* CTSR bits */ 54/* CTSR bits */
44#define CTSR_PONM BIT(8) 55#define CTSR_PONM BIT(8)
45#define CTSR_AOUT BIT(7) 56#define CTSR_AOUT BIT(7)
@@ -76,6 +87,7 @@ struct rcar_gen3_thermal_tsc {
76struct rcar_gen3_thermal_priv { 87struct rcar_gen3_thermal_priv {
77 struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM]; 88 struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM];
78 unsigned int num_tscs; 89 unsigned int num_tscs;
90 spinlock_t lock; /* Protect interrupts on and off */
79}; 91};
80 92
81struct rcar_gen3_thermal_data { 93struct rcar_gen3_thermal_data {
@@ -113,6 +125,7 @@ static inline void rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc *tsc,
113 125
114#define FIXPT_SHIFT 7 126#define FIXPT_SHIFT 7
115#define FIXPT_INT(_x) ((_x) << FIXPT_SHIFT) 127#define FIXPT_INT(_x) ((_x) << FIXPT_SHIFT)
128#define INT_FIXPT(_x) ((_x) >> FIXPT_SHIFT)
116#define FIXPT_DIV(_a, _b) DIV_ROUND_CLOSEST(((_a) << FIXPT_SHIFT), (_b)) 129#define FIXPT_DIV(_a, _b) DIV_ROUND_CLOSEST(((_a) << FIXPT_SHIFT), (_b))
117#define FIXPT_TO_MCELSIUS(_x) ((_x) * 1000 >> FIXPT_SHIFT) 130#define FIXPT_TO_MCELSIUS(_x) ((_x) * 1000 >> FIXPT_SHIFT)
118 131
@@ -178,10 +191,87 @@ static int rcar_gen3_thermal_get_temp(void *devdata, int *temp)
178 return 0; 191 return 0;
179} 192}
180 193
194static int rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc *tsc,
195 int mcelsius)
196{
197 int celsius, val1, val2;
198
199 celsius = DIV_ROUND_CLOSEST(mcelsius, 1000);
200 val1 = celsius * tsc->coef.a1 + tsc->coef.b1;
201 val2 = celsius * tsc->coef.a2 + tsc->coef.b2;
202
203 return INT_FIXPT((val1 + val2) / 2);
204}
205
206static int rcar_gen3_thermal_set_trips(void *devdata, int low, int high)
207{
208 struct rcar_gen3_thermal_tsc *tsc = devdata;
209
210 low = clamp_val(low, -40000, 125000);
211 high = clamp_val(high, -40000, 125000);
212
213 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1,
214 rcar_gen3_thermal_mcelsius_to_temp(tsc, low));
215
216 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP2,
217 rcar_gen3_thermal_mcelsius_to_temp(tsc, high));
218
219 return 0;
220}
221
181static struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = { 222static struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = {
182 .get_temp = rcar_gen3_thermal_get_temp, 223 .get_temp = rcar_gen3_thermal_get_temp,
224 .set_trips = rcar_gen3_thermal_set_trips,
183}; 225};
184 226
227static void rcar_thermal_irq_set(struct rcar_gen3_thermal_priv *priv, bool on)
228{
229 unsigned int i;
230 u32 val = on ? IRQ_TEMPD1 | IRQ_TEMP2 : 0;
231
232 for (i = 0; i < priv->num_tscs; i++)
233 rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQMSK, val);
234}
235
236static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data)
237{
238 struct rcar_gen3_thermal_priv *priv = data;
239 u32 status;
240 int i, ret = IRQ_HANDLED;
241
242 spin_lock(&priv->lock);
243 for (i = 0; i < priv->num_tscs; i++) {
244 status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR);
245 rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQSTR, 0);
246 if (status)
247 ret = IRQ_WAKE_THREAD;
248 }
249
250 if (ret == IRQ_WAKE_THREAD)
251 rcar_thermal_irq_set(priv, false);
252
253 spin_unlock(&priv->lock);
254
255 return ret;
256}
257
258static irqreturn_t rcar_gen3_thermal_irq_thread(int irq, void *data)
259{
260 struct rcar_gen3_thermal_priv *priv = data;
261 unsigned long flags;
262 int i;
263
264 for (i = 0; i < priv->num_tscs; i++)
265 thermal_zone_device_update(priv->tscs[i]->zone,
266 THERMAL_EVENT_UNSPECIFIED);
267
268 spin_lock_irqsave(&priv->lock, flags);
269 rcar_thermal_irq_set(priv, true);
270 spin_unlock_irqrestore(&priv->lock, flags);
271
272 return IRQ_HANDLED;
273}
274
185static void r8a7795_thermal_init(struct rcar_gen3_thermal_tsc *tsc) 275static void r8a7795_thermal_init(struct rcar_gen3_thermal_tsc *tsc)
186{ 276{
187 rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_THBGR); 277 rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_THBGR);
@@ -190,7 +280,11 @@ static void r8a7795_thermal_init(struct rcar_gen3_thermal_tsc *tsc)
190 usleep_range(1000, 2000); 280 usleep_range(1000, 2000);
191 281
192 rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_PONM); 282 rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_PONM);
283
193 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F); 284 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F);
285 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0);
286 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN, IRQ_TEMPD1 | IRQ_TEMP2);
287
194 rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, 288 rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR,
195 CTSR_PONM | CTSR_AOUT | CTSR_THBGR | CTSR_VMEN); 289 CTSR_PONM | CTSR_AOUT | CTSR_THBGR | CTSR_VMEN);
196 290
@@ -214,6 +308,9 @@ static void r8a7796_thermal_init(struct rcar_gen3_thermal_tsc *tsc)
214 usleep_range(1000, 2000); 308 usleep_range(1000, 2000);
215 309
216 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F); 310 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F);
311 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0);
312 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN, IRQ_TEMPD1 | IRQ_TEMP2);
313
217 reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR); 314 reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR);
218 reg_val |= THCTR_THSST; 315 reg_val |= THCTR_THSST;
219 rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val); 316 rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val);
@@ -252,7 +349,8 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
252 struct device *dev = &pdev->dev; 349 struct device *dev = &pdev->dev;
253 struct resource *res; 350 struct resource *res;
254 struct thermal_zone_device *zone; 351 struct thermal_zone_device *zone;
255 int ret, i; 352 int ret, irq, i;
353 char *irqname;
256 const struct rcar_gen3_thermal_data *match_data = 354 const struct rcar_gen3_thermal_data *match_data =
257 of_device_get_match_data(dev); 355 of_device_get_match_data(dev);
258 356
@@ -269,8 +367,32 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
269 if (!priv) 367 if (!priv)
270 return -ENOMEM; 368 return -ENOMEM;
271 369
370 spin_lock_init(&priv->lock);
371
272 platform_set_drvdata(pdev, priv); 372 platform_set_drvdata(pdev, priv);
273 373
374 /*
375 * Request 2 (of the 3 possible) IRQs, the driver only needs to
376 * to trigger on the low and high trip points of the current
377 * temp window at this point.
378 */
379 for (i = 0; i < 2; i++) {
380 irq = platform_get_irq(pdev, i);
381 if (irq < 0)
382 return irq;
383
384 irqname = devm_kasprintf(dev, GFP_KERNEL, "%s:ch%d",
385 dev_name(dev), i);
386 if (!irqname)
387 return -ENOMEM;
388
389 ret = devm_request_threaded_irq(dev, irq, rcar_gen3_thermal_irq,
390 rcar_gen3_thermal_irq_thread,
391 IRQF_SHARED, irqname, priv);
392 if (ret)
393 return ret;
394 }
395
274 pm_runtime_enable(dev); 396 pm_runtime_enable(dev);
275 pm_runtime_get_sync(dev); 397 pm_runtime_get_sync(dev);
276 398
@@ -306,6 +428,12 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
306 goto error_unregister; 428 goto error_unregister;
307 } 429 }
308 tsc->zone = zone; 430 tsc->zone = zone;
431
432 ret = of_thermal_get_ntrips(tsc->zone);
433 if (ret < 0)
434 goto error_unregister;
435
436 dev_info(dev, "TSC%d: Loaded %d trip points\n", i, ret);
309 } 437 }
310 438
311 priv->num_tscs = i; 439 priv->num_tscs = i;
@@ -315,6 +443,8 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
315 goto error_unregister; 443 goto error_unregister;
316 } 444 }
317 445
446 rcar_thermal_irq_set(priv, true);
447
318 return 0; 448 return 0;
319 449
320error_unregister: 450error_unregister: