Skip to content

Commit 564e73d

Browse files
Wolfram SangEduardo Valentin
authored andcommitted
thermal: rcar_gen3_thermal: Add R-Car Gen3 thermal driver
Add support for R-Car Gen3 thermal sensors. Polling only for now, interrupts will be added incrementally. Same goes for reading fuses. This is documented already, but no hardware available for now. Signed-off-by: Hien Dang <[email protected]> Signed-off-by: Thao Nguyen <[email protected]> Signed-off-by: Khiem Nguyen <[email protected]> Signed-off-by: Wolfram Sang <[email protected]> [Niklas: document and rework temperature calculation] Signed-off-by: Niklas Söderlund <[email protected]> Signed-off-by: Eduardo Valentin <[email protected]>
1 parent b022e9b commit 564e73d

File tree

3 files changed

+345
-0
lines changed

3 files changed

+345
-0
lines changed

drivers/thermal/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,15 @@ config RCAR_THERMAL
245245
Enable this to plug the R-Car thermal sensor driver into the Linux
246246
thermal framework.
247247

248+
config RCAR_GEN3_THERMAL
249+
tristate "Renesas R-Car Gen3 thermal driver"
250+
depends on ARCH_RENESAS || COMPILE_TEST
251+
depends on HAS_IOMEM
252+
depends on OF
253+
help
254+
Enable this to plug the R-Car Gen3 thermal sensor driver into the Linux
255+
thermal framework.
256+
248257
config KIRKWOOD_THERMAL
249258
tristate "Temperature sensor on Marvell Kirkwood SoCs"
250259
depends on MACH_KIRKWOOD || COMPILE_TEST

drivers/thermal/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o
3131
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
3232
obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o
3333
obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o
34+
obj-$(CONFIG_RCAR_GEN3_THERMAL) += rcar_gen3_thermal.o
3435
obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o
3536
obj-y += samsung/
3637
obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o

drivers/thermal/rcar_gen3_thermal.c

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
/*
2+
* R-Car Gen3 THS thermal sensor driver
3+
* Based on rcar_thermal.c and work from Hien Dang and Khiem Nguyen.
4+
*
5+
* Copyright (C) 2016 Renesas Electronics Corporation.
6+
* Copyright (C) 2016 Sang Engineering
7+
*
8+
* This program is free software; you can redistribute it and/or modify
9+
* it under the terms of the GNU General Public License as published by
10+
* the Free Software Foundation; version 2 of the License.
11+
*
12+
* This program is distributed in the hope that it will be useful, but
13+
* WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
* General Public License for more details.
16+
*
17+
*/
18+
#include <linux/delay.h>
19+
#include <linux/err.h>
20+
#include <linux/interrupt.h>
21+
#include <linux/io.h>
22+
#include <linux/module.h>
23+
#include <linux/mutex.h>
24+
#include <linux/of_device.h>
25+
#include <linux/platform_device.h>
26+
#include <linux/pm_runtime.h>
27+
#include <linux/thermal.h>
28+
29+
/* Register offsets */
30+
#define REG_GEN3_IRQSTR 0x04
31+
#define REG_GEN3_IRQMSK 0x08
32+
#define REG_GEN3_IRQCTL 0x0C
33+
#define REG_GEN3_IRQEN 0x10
34+
#define REG_GEN3_IRQTEMP1 0x14
35+
#define REG_GEN3_IRQTEMP2 0x18
36+
#define REG_GEN3_IRQTEMP3 0x1C
37+
#define REG_GEN3_CTSR 0x20
38+
#define REG_GEN3_THCTR 0x20
39+
#define REG_GEN3_TEMP 0x28
40+
#define REG_GEN3_THCODE1 0x50
41+
#define REG_GEN3_THCODE2 0x54
42+
#define REG_GEN3_THCODE3 0x58
43+
44+
/* CTSR bits */
45+
#define CTSR_PONM BIT(8)
46+
#define CTSR_AOUT BIT(7)
47+
#define CTSR_THBGR BIT(5)
48+
#define CTSR_VMEN BIT(4)
49+
#define CTSR_VMST BIT(1)
50+
#define CTSR_THSST BIT(0)
51+
52+
/* THCTR bits */
53+
#define THCTR_PONM BIT(6)
54+
#define THCTR_THSST BIT(0)
55+
56+
#define CTEMP_MASK 0xFFF
57+
58+
#define MCELSIUS(temp) ((temp) * 1000)
59+
#define GEN3_FUSE_MASK 0xFFF
60+
61+
#define TSC_MAX_NUM 3
62+
63+
/* Structure for thermal temperature calculation */
64+
struct equation_coefs {
65+
int a1;
66+
int b1;
67+
int a2;
68+
int b2;
69+
};
70+
71+
struct rcar_gen3_thermal_tsc {
72+
void __iomem *base;
73+
struct thermal_zone_device *zone;
74+
struct equation_coefs coef;
75+
struct mutex lock;
76+
};
77+
78+
struct rcar_gen3_thermal_priv {
79+
struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM];
80+
};
81+
82+
struct rcar_gen3_thermal_data {
83+
void (*thermal_init)(struct rcar_gen3_thermal_tsc *tsc);
84+
};
85+
86+
static inline u32 rcar_gen3_thermal_read(struct rcar_gen3_thermal_tsc *tsc,
87+
u32 reg)
88+
{
89+
return ioread32(tsc->base + reg);
90+
}
91+
92+
static inline void rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc *tsc,
93+
u32 reg, u32 data)
94+
{
95+
iowrite32(data, tsc->base + reg);
96+
}
97+
98+
/*
99+
* Linear approximation for temperature
100+
*
101+
* [reg] = [temp] * a + b => [temp] = ([reg] - b) / a
102+
*
103+
* The constants a and b are calculated using two triplets of int values PTAT
104+
* and THCODE. PTAT and THCODE can either be read from hardware or use hard
105+
* coded values from driver. The formula to calculate a and b are taken from
106+
* BSP and sparsely documented and understood.
107+
*
108+
* Examining the linear formula and the formula used to calculate constants a
109+
* and b while knowing that the span for PTAT and THCODE values are between
110+
* 0x000 and 0xfff the largest integer possible is 0xfff * 0xfff == 0xffe001.
111+
* Integer also needs to be signed so that leaves 7 bits for binary
112+
* fixed point scaling.
113+
*/
114+
115+
#define FIXPT_SHIFT 7
116+
#define FIXPT_INT(_x) ((_x) << FIXPT_SHIFT)
117+
#define FIXPT_DIV(_a, _b) DIV_ROUND_CLOSEST(((_a) << FIXPT_SHIFT), (_b))
118+
#define FIXPT_TO_MCELSIUS(_x) ((_x) * 1000 >> FIXPT_SHIFT)
119+
120+
#define RCAR3_THERMAL_GRAN 500 /* mili Celsius */
121+
122+
/* no idea where these constants come from */
123+
#define TJ_1 96
124+
#define TJ_3 -41
125+
126+
static void rcar_gen3_thermal_calc_coefs(struct equation_coefs *coef,
127+
int *ptat, int *thcode)
128+
{
129+
int tj_2;
130+
131+
/* TODO: Find documentation and document constant calculation formula */
132+
133+
/*
134+
* Division is not scaled in BSP and if scaled it might overflow
135+
* the dividend (4095 * 4095 << 14 > INT_MAX) so keep it unscaled
136+
*/
137+
tj_2 = (FIXPT_INT((ptat[1] - ptat[2]) * 137)
138+
/ (ptat[0] - ptat[2])) - FIXPT_INT(41);
139+
140+
coef->a1 = FIXPT_DIV(FIXPT_INT(thcode[1] - thcode[2]),
141+
tj_2 - FIXPT_INT(TJ_3));
142+
coef->b1 = FIXPT_INT(thcode[2]) - coef->a1 * TJ_3;
143+
144+
coef->a2 = FIXPT_DIV(FIXPT_INT(thcode[1] - thcode[0]),
145+
tj_2 - FIXPT_INT(TJ_1));
146+
coef->b2 = FIXPT_INT(thcode[0]) - coef->a2 * TJ_1;
147+
}
148+
149+
static int rcar_gen3_thermal_round(int temp)
150+
{
151+
int result, round_offs;
152+
153+
round_offs = temp >= 0 ? RCAR3_THERMAL_GRAN / 2 :
154+
-RCAR3_THERMAL_GRAN / 2;
155+
result = (temp + round_offs) / RCAR3_THERMAL_GRAN;
156+
return result * RCAR3_THERMAL_GRAN;
157+
}
158+
159+
static int rcar_gen3_thermal_get_temp(void *devdata, int *temp)
160+
{
161+
struct rcar_gen3_thermal_tsc *tsc = devdata;
162+
int mcelsius, val1, val2;
163+
u32 reg;
164+
165+
/* Read register and convert to mili Celsius */
166+
mutex_lock(&tsc->lock);
167+
168+
reg = rcar_gen3_thermal_read(tsc, REG_GEN3_TEMP) & CTEMP_MASK;
169+
170+
val1 = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b1, tsc->coef.a1);
171+
val2 = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b2, tsc->coef.a2);
172+
mcelsius = FIXPT_TO_MCELSIUS((val1 + val2) / 2);
173+
174+
mutex_unlock(&tsc->lock);
175+
176+
/* Make sure we are inside specifications */
177+
if ((mcelsius < MCELSIUS(-40)) || (mcelsius > MCELSIUS(125)))
178+
return -EIO;
179+
180+
/* Round value to device granularity setting */
181+
*temp = rcar_gen3_thermal_round(mcelsius);
182+
183+
return 0;
184+
}
185+
186+
static struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = {
187+
.get_temp = rcar_gen3_thermal_get_temp,
188+
};
189+
190+
static void r8a7795_thermal_init(struct rcar_gen3_thermal_tsc *tsc)
191+
{
192+
rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_THBGR);
193+
rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, 0x0);
194+
195+
usleep_range(1000, 2000);
196+
197+
rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_PONM);
198+
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F);
199+
rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR,
200+
CTSR_PONM | CTSR_AOUT | CTSR_THBGR | CTSR_VMEN);
201+
202+
usleep_range(100, 200);
203+
204+
rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR,
205+
CTSR_PONM | CTSR_AOUT | CTSR_THBGR | CTSR_VMEN |
206+
CTSR_VMST | CTSR_THSST);
207+
208+
usleep_range(1000, 2000);
209+
}
210+
211+
static void r8a7796_thermal_init(struct rcar_gen3_thermal_tsc *tsc)
212+
{
213+
u32 reg_val;
214+
215+
reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR);
216+
reg_val &= ~THCTR_PONM;
217+
rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val);
218+
219+
usleep_range(1000, 2000);
220+
221+
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F);
222+
reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR);
223+
reg_val |= THCTR_THSST;
224+
rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val);
225+
}
226+
227+
static const struct rcar_gen3_thermal_data r8a7795_data = {
228+
.thermal_init = r8a7795_thermal_init,
229+
};
230+
231+
static const struct rcar_gen3_thermal_data r8a7796_data = {
232+
.thermal_init = r8a7796_thermal_init,
233+
};
234+
235+
static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
236+
{ .compatible = "renesas,r8a7795-thermal", .data = &r8a7795_data},
237+
{ .compatible = "renesas,r8a7796-thermal", .data = &r8a7796_data},
238+
{},
239+
};
240+
MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids);
241+
242+
static int rcar_gen3_thermal_remove(struct platform_device *pdev)
243+
{
244+
struct device *dev = &pdev->dev;
245+
246+
pm_runtime_put(dev);
247+
pm_runtime_disable(dev);
248+
249+
return 0;
250+
}
251+
252+
static int rcar_gen3_thermal_probe(struct platform_device *pdev)
253+
{
254+
struct rcar_gen3_thermal_priv *priv;
255+
struct device *dev = &pdev->dev;
256+
struct resource *res;
257+
struct thermal_zone_device *zone;
258+
int ret, i;
259+
const struct rcar_gen3_thermal_data *match_data =
260+
of_device_get_match_data(dev);
261+
262+
/* default values if FUSEs are missing */
263+
/* TODO: Read values from hardware on supported platforms */
264+
int ptat[3] = { 2351, 1509, 435 };
265+
int thcode[TSC_MAX_NUM][3] = {
266+
{ 3248, 2800, 2221 },
267+
{ 3245, 2795, 2216 },
268+
{ 3250, 2805, 2237 },
269+
};
270+
271+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
272+
if (!priv)
273+
return -ENOMEM;
274+
275+
platform_set_drvdata(pdev, priv);
276+
277+
pm_runtime_enable(dev);
278+
pm_runtime_get_sync(dev);
279+
280+
for (i = 0; i < TSC_MAX_NUM; i++) {
281+
struct rcar_gen3_thermal_tsc *tsc;
282+
283+
tsc = devm_kzalloc(dev, sizeof(*tsc), GFP_KERNEL);
284+
if (!tsc) {
285+
ret = -ENOMEM;
286+
goto error_unregister;
287+
}
288+
289+
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
290+
if (!res)
291+
break;
292+
293+
tsc->base = devm_ioremap_resource(dev, res);
294+
if (IS_ERR(tsc->base)) {
295+
ret = PTR_ERR(tsc->base);
296+
goto error_unregister;
297+
}
298+
299+
priv->tscs[i] = tsc;
300+
mutex_init(&tsc->lock);
301+
302+
match_data->thermal_init(tsc);
303+
rcar_gen3_thermal_calc_coefs(&tsc->coef, ptat, thcode[i]);
304+
305+
zone = devm_thermal_zone_of_sensor_register(dev, i, tsc,
306+
&rcar_gen3_tz_of_ops);
307+
if (IS_ERR(zone)) {
308+
dev_err(dev, "Can't register thermal zone\n");
309+
ret = PTR_ERR(zone);
310+
goto error_unregister;
311+
}
312+
tsc->zone = zone;
313+
}
314+
315+
return 0;
316+
317+
error_unregister:
318+
rcar_gen3_thermal_remove(pdev);
319+
320+
return ret;
321+
}
322+
323+
static struct platform_driver rcar_gen3_thermal_driver = {
324+
.driver = {
325+
.name = "rcar_gen3_thermal",
326+
.of_match_table = rcar_gen3_thermal_dt_ids,
327+
},
328+
.probe = rcar_gen3_thermal_probe,
329+
.remove = rcar_gen3_thermal_remove,
330+
};
331+
module_platform_driver(rcar_gen3_thermal_driver);
332+
333+
MODULE_LICENSE("GPL v2");
334+
MODULE_DESCRIPTION("R-Car Gen3 THS thermal sensor driver");
335+
MODULE_AUTHOR("Wolfram Sang <[email protected]>");

0 commit comments

Comments
 (0)