aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/thermal/Kconfig10
-rw-r--r--drivers/thermal/Makefile1
-rw-r--r--drivers/thermal/tegra_soctherm.c473
3 files changed, 484 insertions, 0 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index efbdb8c0bb1d..911c38aba6ef 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -197,6 +197,16 @@ config ARMADA_THERMAL
197 Enable this option if you want to have support for thermal management 197 Enable this option if you want to have support for thermal management
198 controller present in Armada 370 and Armada XP SoC. 198 controller present in Armada 370 and Armada XP SoC.
199 199
200config TEGRA_SOCTHERM
201 tristate "Tegra SOCTHERM thermal management"
202 depends on ARCH_TEGRA
203 help
204 Enable this option for integrated thermal management support on NVIDIA
205 Tegra124 systems-on-chip. The driver supports four thermal zones
206 (CPU, GPU, MEM, PLLX). Cooling devices can be bound to the thermal
207 zones to manage temperatures. This option is also required for the
208 emergency thermal reset (thermtrip) feature to function.
209
200config DB8500_CPUFREQ_COOLING 210config DB8500_CPUFREQ_COOLING
201 tristate "DB8500 cpufreq cooling" 211 tristate "DB8500 cpufreq cooling"
202 depends on ARCH_U8500 212 depends on ARCH_U8500
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 9d33532cae9f..2b18e3ac581f 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -37,3 +37,4 @@ obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o
37obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ 37obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
38obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ 38obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/
39obj-$(CONFIG_ST_THERMAL) += st/ 39obj-$(CONFIG_ST_THERMAL) += st/
40obj-$(CONFIG_TEGRA_SOCTHERM) += tegra_soctherm.o
diff --git a/drivers/thermal/tegra_soctherm.c b/drivers/thermal/tegra_soctherm.c
new file mode 100644
index 000000000000..70f7e9ef4355
--- /dev/null
+++ b/drivers/thermal/tegra_soctherm.c
@@ -0,0 +1,473 @@
1/*
2 * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
3 *
4 * Author:
5 * Mikko Perttunen <mperttunen@nvidia.com>
6 *
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17
18#include <linux/bitops.h>
19#include <linux/clk.h>
20#include <linux/delay.h>
21#include <linux/err.h>
22#include <linux/interrupt.h>
23#include <linux/io.h>
24#include <linux/module.h>
25#include <linux/of.h>
26#include <linux/platform_device.h>
27#include <linux/reset.h>
28#include <linux/thermal.h>
29
30#include <soc/tegra/fuse.h>
31
32#define SENSOR_CONFIG0 0
33#define SENSOR_CONFIG0_STOP BIT(0)
34#define SENSOR_CONFIG0_TALL_SHIFT 8
35#define SENSOR_CONFIG0_TCALC_OVER BIT(4)
36#define SENSOR_CONFIG0_OVER BIT(3)
37#define SENSOR_CONFIG0_CPTR_OVER BIT(2)
38
39#define SENSOR_CONFIG1 4
40#define SENSOR_CONFIG1_TSAMPLE_SHIFT 0
41#define SENSOR_CONFIG1_TIDDQ_EN_SHIFT 15
42#define SENSOR_CONFIG1_TEN_COUNT_SHIFT 24
43#define SENSOR_CONFIG1_TEMP_ENABLE BIT(31)
44
45#define SENSOR_CONFIG2 8
46#define SENSOR_CONFIG2_THERMA_SHIFT 16
47#define SENSOR_CONFIG2_THERMB_SHIFT 0
48
49#define SENSOR_PDIV 0x1c0
50#define SENSOR_PDIV_T124 0x8888
51#define SENSOR_HOTSPOT_OFF 0x1c4
52#define SENSOR_HOTSPOT_OFF_T124 0x00060600
53#define SENSOR_TEMP1 0x1c8
54#define SENSOR_TEMP2 0x1cc
55
56#define SENSOR_TEMP_MASK 0xffff
57#define READBACK_VALUE_MASK 0xff00
58#define READBACK_VALUE_SHIFT 8
59#define READBACK_ADD_HALF BIT(7)
60#define READBACK_NEGATE BIT(1)
61
62#define FUSE_TSENSOR8_CALIB 0x180
63#define FUSE_SPARE_REALIGNMENT_REG_0 0x1fc
64
65#define FUSE_TSENSOR_CALIB_CP_TS_BASE_MASK 0x1fff
66#define FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK (0x1fff << 13)
67#define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13
68
69#define FUSE_TSENSOR8_CALIB_CP_TS_BASE_MASK 0x3ff
70#define FUSE_TSENSOR8_CALIB_FT_TS_BASE_MASK (0x7ff << 10)
71#define FUSE_TSENSOR8_CALIB_FT_TS_BASE_SHIFT 10
72
73#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_MASK 0x3f
74#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_MASK (0x1f << 21)
75#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT 21
76
77#define NOMINAL_CALIB_FT_T124 105
78#define NOMINAL_CALIB_CP_T124 25
79
80struct tegra_tsensor_configuration {
81 u32 tall, tsample, tiddq_en, ten_count, pdiv, tsample_ate, pdiv_ate;
82};
83
84struct tegra_tsensor {
85 const struct tegra_tsensor_configuration *config;
86 u32 base, calib_fuse_offset;
87 /* Correction values used to modify values read from calibration fuses */
88 s32 fuse_corr_alpha, fuse_corr_beta;
89};
90
91struct tegra_thermctl_zone {
92 void __iomem *reg;
93 unsigned int shift;
94};
95
96static const struct tegra_tsensor_configuration t124_tsensor_config = {
97 .tall = 16300,
98 .tsample = 120,
99 .tiddq_en = 1,
100 .ten_count = 1,
101 .pdiv = 8,
102 .tsample_ate = 480,
103 .pdiv_ate = 8
104};
105
106static const struct tegra_tsensor t124_tsensors[] = {
107 {
108 .config = &t124_tsensor_config,
109 .base = 0xc0,
110 .calib_fuse_offset = 0x098,
111 .fuse_corr_alpha = 1135400,
112 .fuse_corr_beta = -6266900,
113 },
114 {
115 .config = &t124_tsensor_config,
116 .base = 0xe0,
117 .calib_fuse_offset = 0x084,
118 .fuse_corr_alpha = 1122220,
119 .fuse_corr_beta = -5700700,
120 },
121 {
122 .config = &t124_tsensor_config,
123 .base = 0x100,
124 .calib_fuse_offset = 0x088,
125 .fuse_corr_alpha = 1127000,
126 .fuse_corr_beta = -6768200,
127 },
128 {
129 .config = &t124_tsensor_config,
130 .base = 0x120,
131 .calib_fuse_offset = 0x12c,
132 .fuse_corr_alpha = 1110900,
133 .fuse_corr_beta = -6232000,
134 },
135 {
136 .config = &t124_tsensor_config,
137 .base = 0x140,
138 .calib_fuse_offset = 0x158,
139 .fuse_corr_alpha = 1122300,
140 .fuse_corr_beta = -5936400,
141 },
142 {
143 .config = &t124_tsensor_config,
144 .base = 0x160,
145 .calib_fuse_offset = 0x15c,
146 .fuse_corr_alpha = 1145700,
147 .fuse_corr_beta = -7124600,
148 },
149 {
150 .config = &t124_tsensor_config,
151 .base = 0x180,
152 .calib_fuse_offset = 0x154,
153 .fuse_corr_alpha = 1120100,
154 .fuse_corr_beta = -6000500,
155 },
156 {
157 .config = &t124_tsensor_config,
158 .base = 0x1a0,
159 .calib_fuse_offset = 0x160,
160 .fuse_corr_alpha = 1106500,
161 .fuse_corr_beta = -6729300,
162 },
163};
164
165struct tegra_soctherm {
166 struct reset_control *reset;
167 struct clk *clock_tsensor;
168 struct clk *clock_soctherm;
169 void __iomem *regs;
170
171 struct thermal_zone_device *thermctl_tzs[4];
172};
173
174struct tsensor_shared_calibration {
175 u32 base_cp, base_ft;
176 u32 actual_temp_cp, actual_temp_ft;
177};
178
179static int calculate_shared_calibration(struct tsensor_shared_calibration *r)
180{
181 u32 val, shifted_cp, shifted_ft;
182 int err;
183
184 err = tegra_fuse_readl(FUSE_TSENSOR8_CALIB, &val);
185 if (err)
186 return err;
187 r->base_cp = val & FUSE_TSENSOR8_CALIB_CP_TS_BASE_MASK;
188 r->base_ft = (val & FUSE_TSENSOR8_CALIB_FT_TS_BASE_MASK)
189 >> FUSE_TSENSOR8_CALIB_FT_TS_BASE_SHIFT;
190 val = ((val & FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_MASK)
191 >> FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT);
192 shifted_ft = sign_extend32(val, 4);
193
194 err = tegra_fuse_readl(FUSE_SPARE_REALIGNMENT_REG_0, &val);
195 if (err)
196 return err;
197 shifted_cp = sign_extend32(val, 5);
198
199 r->actual_temp_cp = 2 * NOMINAL_CALIB_CP_T124 + shifted_cp;
200 r->actual_temp_ft = 2 * NOMINAL_CALIB_FT_T124 + shifted_ft;
201
202 return 0;
203}
204
205static s64 div64_s64_precise(s64 a, s64 b)
206{
207 s64 r, al;
208
209 /* Scale up for increased precision division */
210 al = a << 16;
211
212 r = div64_s64(al * 2 + 1, 2 * b);
213 return r >> 16;
214}
215
216static int
217calculate_tsensor_calibration(const struct tegra_tsensor *sensor,
218 const struct tsensor_shared_calibration *shared,
219 u32 *calib)
220{
221 u32 val;
222 s32 actual_tsensor_ft, actual_tsensor_cp, delta_sens, delta_temp,
223 mult, div;
224 s16 therma, thermb;
225 s64 tmp;
226 int err;
227
228 err = tegra_fuse_readl(sensor->calib_fuse_offset, &val);
229 if (err)
230 return err;
231
232 actual_tsensor_cp = (shared->base_cp * 64) + sign_extend32(val, 12);
233 val = (val & FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK)
234 >> FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT;
235 actual_tsensor_ft = (shared->base_ft * 32) + sign_extend32(val, 12);
236
237 delta_sens = actual_tsensor_ft - actual_tsensor_cp;
238 delta_temp = shared->actual_temp_ft - shared->actual_temp_cp;
239
240 mult = sensor->config->pdiv * sensor->config->tsample_ate;
241 div = sensor->config->tsample * sensor->config->pdiv_ate;
242
243 therma = div64_s64_precise((s64) delta_temp * (1LL << 13) * mult,
244 (s64) delta_sens * div);
245
246 tmp = (s64)actual_tsensor_ft * shared->actual_temp_cp -
247 (s64)actual_tsensor_cp * shared->actual_temp_ft;
248 thermb = div64_s64_precise(tmp, (s64)delta_sens);
249
250 therma = div64_s64_precise((s64)therma * sensor->fuse_corr_alpha,
251 (s64)1000000LL);
252 thermb = div64_s64_precise((s64)thermb * sensor->fuse_corr_alpha +
253 sensor->fuse_corr_beta, (s64)1000000LL);
254
255 *calib = ((u16)therma << SENSOR_CONFIG2_THERMA_SHIFT) |
256 ((u16)thermb << SENSOR_CONFIG2_THERMB_SHIFT);
257
258 return 0;
259}
260
261static int enable_tsensor(struct tegra_soctherm *tegra,
262 const struct tegra_tsensor *sensor,
263 const struct tsensor_shared_calibration *shared)
264{
265 void __iomem *base = tegra->regs + sensor->base;
266 unsigned int val;
267 u32 calib;
268 int err;
269
270 err = calculate_tsensor_calibration(sensor, shared, &calib);
271 if (err)
272 return err;
273
274 val = sensor->config->tall << SENSOR_CONFIG0_TALL_SHIFT;
275 writel(val, base + SENSOR_CONFIG0);
276
277 val = (sensor->config->tsample - 1) << SENSOR_CONFIG1_TSAMPLE_SHIFT;
278 val |= sensor->config->tiddq_en << SENSOR_CONFIG1_TIDDQ_EN_SHIFT;
279 val |= sensor->config->ten_count << SENSOR_CONFIG1_TEN_COUNT_SHIFT;
280 val |= SENSOR_CONFIG1_TEMP_ENABLE;
281 writel(val, base + SENSOR_CONFIG1);
282
283 writel(calib, base + SENSOR_CONFIG2);
284
285 return 0;
286}
287
288/*
289 * Translate from soctherm readback format to millicelsius.
290 * The soctherm readback format in bits is as follows:
291 * TTTTTTTT H______N
292 * where T's contain the temperature in Celsius,
293 * H denotes an addition of 0.5 Celsius and N denotes negation
294 * of the final value.
295 */
296static long translate_temp(u16 val)
297{
298 long t;
299
300 t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000;
301 if (val & READBACK_ADD_HALF)
302 t += 500;
303 if (val & READBACK_NEGATE)
304 t *= -1;
305
306 return t;
307}
308
309static int tegra_thermctl_get_temp(void *data, long *out_temp)
310{
311 struct tegra_thermctl_zone *zone = data;
312 u32 val;
313
314 val = (readl(zone->reg) >> zone->shift) & SENSOR_TEMP_MASK;
315 *out_temp = translate_temp(val);
316
317 return 0;
318}
319
320static const struct of_device_id tegra_soctherm_of_match[] = {
321 { .compatible = "nvidia,tegra124-soctherm" },
322 { },
323};
324MODULE_DEVICE_TABLE(of, tegra_soctherm_of_match);
325
326struct thermctl_zone_desc {
327 unsigned int offset;
328 unsigned int shift;
329};
330
331static const struct thermctl_zone_desc t124_thermctl_temp_zones[] = {
332 { SENSOR_TEMP1, 16 },
333 { SENSOR_TEMP2, 16 },
334 { SENSOR_TEMP1, 0 },
335 { SENSOR_TEMP2, 0 }
336};
337
338static int tegra_soctherm_probe(struct platform_device *pdev)
339{
340 struct tegra_soctherm *tegra;
341 struct thermal_zone_device *tz;
342 struct tsensor_shared_calibration shared_calib;
343 struct resource *res;
344 unsigned int i;
345 int err;
346
347 const struct tegra_tsensor *tsensors = t124_tsensors;
348
349 tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
350 if (!tegra)
351 return -ENOMEM;
352
353 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
354 tegra->regs = devm_ioremap_resource(&pdev->dev, res);
355 if (IS_ERR(tegra->regs))
356 return PTR_ERR(tegra->regs);
357
358 tegra->reset = devm_reset_control_get(&pdev->dev, "soctherm");
359 if (IS_ERR(tegra->reset)) {
360 dev_err(&pdev->dev, "can't get soctherm reset\n");
361 return PTR_ERR(tegra->reset);
362 }
363
364 tegra->clock_tsensor = devm_clk_get(&pdev->dev, "tsensor");
365 if (IS_ERR(tegra->clock_tsensor)) {
366 dev_err(&pdev->dev, "can't get tsensor clock\n");
367 return PTR_ERR(tegra->clock_tsensor);
368 }
369
370 tegra->clock_soctherm = devm_clk_get(&pdev->dev, "soctherm");
371 if (IS_ERR(tegra->clock_soctherm)) {
372 dev_err(&pdev->dev, "can't get soctherm clock\n");
373 return PTR_ERR(tegra->clock_soctherm);
374 }
375
376 reset_control_assert(tegra->reset);
377
378 err = clk_prepare_enable(tegra->clock_soctherm);
379 if (err)
380 return err;
381
382 err = clk_prepare_enable(tegra->clock_tsensor);
383 if (err) {
384 clk_disable_unprepare(tegra->clock_soctherm);
385 return err;
386 }
387
388 reset_control_deassert(tegra->reset);
389
390 /* Initialize raw sensors */
391
392 err = calculate_shared_calibration(&shared_calib);
393 if (err)
394 goto disable_clocks;
395
396 for (i = 0; i < ARRAY_SIZE(t124_tsensors); ++i) {
397 err = enable_tsensor(tegra, tsensors + i, &shared_calib);
398 if (err)
399 goto disable_clocks;
400 }
401
402 writel(SENSOR_PDIV_T124, tegra->regs + SENSOR_PDIV);
403 writel(SENSOR_HOTSPOT_OFF_T124, tegra->regs + SENSOR_HOTSPOT_OFF);
404
405 /* Initialize thermctl sensors */
406
407 for (i = 0; i < ARRAY_SIZE(tegra->thermctl_tzs); ++i) {
408 struct tegra_thermctl_zone *zone =
409 devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL);
410 if (!zone) {
411 err = -ENOMEM;
412 goto unregister_tzs;
413 }
414
415 zone->reg = tegra->regs + t124_thermctl_temp_zones[i].offset;
416 zone->shift = t124_thermctl_temp_zones[i].shift;
417
418 tz = thermal_zone_of_sensor_register(&pdev->dev, i, zone,
419 tegra_thermctl_get_temp,
420 NULL);
421 if (IS_ERR(tz)) {
422 err = PTR_ERR(tz);
423 dev_err(&pdev->dev, "failed to register sensor: %d\n",
424 err);
425 goto unregister_tzs;
426 }
427
428 tegra->thermctl_tzs[i] = tz;
429 }
430
431 return 0;
432
433unregister_tzs:
434 while (i--)
435 thermal_zone_of_sensor_unregister(&pdev->dev,
436 tegra->thermctl_tzs[i]);
437
438disable_clocks:
439 clk_disable_unprepare(tegra->clock_tsensor);
440 clk_disable_unprepare(tegra->clock_soctherm);
441
442 return err;
443}
444
445static int tegra_soctherm_remove(struct platform_device *pdev)
446{
447 struct tegra_soctherm *tegra = platform_get_drvdata(pdev);
448 unsigned int i;
449
450 for (i = 0; i < ARRAY_SIZE(tegra->thermctl_tzs); ++i) {
451 thermal_zone_of_sensor_unregister(&pdev->dev,
452 tegra->thermctl_tzs[i]);
453 }
454
455 clk_disable_unprepare(tegra->clock_tsensor);
456 clk_disable_unprepare(tegra->clock_soctherm);
457
458 return 0;
459}
460
461static struct platform_driver tegra_soctherm_driver = {
462 .probe = tegra_soctherm_probe,
463 .remove = tegra_soctherm_remove,
464 .driver = {
465 .name = "tegra-soctherm",
466 .of_match_table = tegra_soctherm_of_match,
467 },
468};
469module_platform_driver(tegra_soctherm_driver);
470
471MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
472MODULE_DESCRIPTION("NVIDIA Tegra SOCTHERM thermal management driver");
473MODULE_LICENSE("GPL v2");