Skip to content

Commit 7d4b269

Browse files
Niklas SöderlundEduardo Valentin
authored andcommitted
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 <[email protected]> Reviewed-by: Wolfram Sang <[email protected]> Signed-off-by: Eduardo Valentin <[email protected]>
1 parent 97dad1f commit 7d4b269

File tree

1 file changed

+131
-1
lines changed

1 file changed

+131
-1
lines changed

drivers/thermal/rcar_gen3_thermal.c

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@
2323
#include <linux/of_device.h>
2424
#include <linux/platform_device.h>
2525
#include <linux/pm_runtime.h>
26+
#include <linux/spinlock.h>
2627
#include <linux/thermal.h>
2728

29+
#include "thermal_core.h"
30+
2831
/* Register offsets */
2932
#define REG_GEN3_IRQSTR 0x04
3033
#define REG_GEN3_IRQMSK 0x08
@@ -40,6 +43,14 @@
4043
#define REG_GEN3_THCODE2 0x54
4144
#define REG_GEN3_THCODE3 0x58
4245

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+
4354
/* CTSR bits */
4455
#define CTSR_PONM BIT(8)
4556
#define CTSR_AOUT BIT(7)
@@ -76,6 +87,7 @@ struct rcar_gen3_thermal_tsc {
7687
struct rcar_gen3_thermal_priv {
7788
struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM];
7889
unsigned int num_tscs;
90+
spinlock_t lock; /* Protect interrupts on and off */
7991
};
8092

8193
struct rcar_gen3_thermal_data {
@@ -113,6 +125,7 @@ static inline void rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc *tsc,
113125

114126
#define FIXPT_SHIFT 7
115127
#define FIXPT_INT(_x) ((_x) << FIXPT_SHIFT)
128+
#define INT_FIXPT(_x) ((_x) >> FIXPT_SHIFT)
116129
#define FIXPT_DIV(_a, _b) DIV_ROUND_CLOSEST(((_a) << FIXPT_SHIFT), (_b))
117130
#define FIXPT_TO_MCELSIUS(_x) ((_x) * 1000 >> FIXPT_SHIFT)
118131

@@ -178,10 +191,87 @@ static int rcar_gen3_thermal_get_temp(void *devdata, int *temp)
178191
return 0;
179192
}
180193

194+
static 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+
206+
static 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+
181222
static struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = {
182223
.get_temp = rcar_gen3_thermal_get_temp,
224+
.set_trips = rcar_gen3_thermal_set_trips,
183225
};
184226

227+
static 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+
236+
static 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+
258+
static 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+
185275
static void r8a7795_thermal_init(struct rcar_gen3_thermal_tsc *tsc)
186276
{
187277
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)
190280
usleep_range(1000, 2000);
191281

192282
rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_PONM);
283+
193284
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+
194288
rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR,
195289
CTSR_PONM | CTSR_AOUT | CTSR_THBGR | CTSR_VMEN);
196290

@@ -214,6 +308,9 @@ static void r8a7796_thermal_init(struct rcar_gen3_thermal_tsc *tsc)
214308
usleep_range(1000, 2000);
215309

216310
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+
217314
reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR);
218315
reg_val |= THCTR_THSST;
219316
rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val);
@@ -252,7 +349,8 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
252349
struct device *dev = &pdev->dev;
253350
struct resource *res;
254351
struct thermal_zone_device *zone;
255-
int ret, i;
352+
int ret, irq, i;
353+
char *irqname;
256354
const struct rcar_gen3_thermal_data *match_data =
257355
of_device_get_match_data(dev);
258356

@@ -269,8 +367,32 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
269367
if (!priv)
270368
return -ENOMEM;
271369

370+
spin_lock_init(&priv->lock);
371+
272372
platform_set_drvdata(pdev, priv);
273373

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+
274396
pm_runtime_enable(dev);
275397
pm_runtime_get_sync(dev);
276398

@@ -306,6 +428,12 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
306428
goto error_unregister;
307429
}
308430
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);
309437
}
310438

311439
priv->num_tscs = i;
@@ -315,6 +443,8 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
315443
goto error_unregister;
316444
}
317445

446+
rcar_thermal_irq_set(priv, true);
447+
318448
return 0;
319449

320450
error_unregister:

0 commit comments

Comments
 (0)