1 // SPDX-License-Identifier: GPL-2.0
3 * TI Bandgap temperature sensor driver for J72XX SoC Family
5 * Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/
8 #include <linux/math.h>
9 #include <linux/math64.h>
10 #include <linux/module.h>
11 #include <linux/init.h>
12 #include <linux/kernel.h>
13 #include <linux/platform_device.h>
14 #include <linux/pm_runtime.h>
15 #include <linux/err.h>
16 #include <linux/types.h>
18 #include <linux/thermal.h>
20 #include <linux/delay.h>
21 #include <linux/slab.h>
23 #define K3_VTM_DEVINFO_PWR0_OFFSET 0x4
24 #define K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK 0xf0
25 #define K3_VTM_TMPSENS0_CTRL_OFFSET 0x300
26 #define K3_VTM_MISC_CTRL_OFFSET 0xc
27 #define K3_VTM_TMPSENS_STAT_OFFSET 0x8
28 #define K3_VTM_ANYMAXT_OUTRG_ALERT_EN 0x1
29 #define K3_VTM_MISC_CTRL2_OFFSET 0x10
30 #define K3_VTM_TS_STAT_DTEMP_MASK 0x3ff
31 #define K3_VTM_MAX_NUM_TS 8
32 #define K3_VTM_TMPSENS_CTRL_SOC BIT(5)
33 #define K3_VTM_TMPSENS_CTRL_CLRZ BIT(6)
34 #define K3_VTM_TMPSENS_CTRL_CLKON_REQ BIT(7)
35 #define K3_VTM_TMPSENS_CTRL_MAXT_OUTRG_EN BIT(11)
37 #define K3_VTM_CORRECTION_TEMP_CNT 3
40 #define PLUS30CREF 253
41 #define PLUS125CREF 730
42 #define PLUS150CREF 940
44 #define TABLE_SIZE 1024
45 #define MAX_TEMP 123000
46 #define COOL_DOWN_TEMP 105000
48 #define FACTORS_REDUCTION 13
49 static int *derived_table
;
51 static int compute_value(int index
, const s64
*factors
, int nr_factors
,
57 for (i
= 0; i
< nr_factors
; i
++)
58 value
+= factors
[i
] * int_pow(index
, i
);
60 return (int)div64_s64(value
, int_pow(10, reduction
));
63 static void init_table(int factors_size
, int *table
, const s64
*factors
)
67 for (i
= 0; i
< TABLE_SIZE
; i
++)
68 table
[i
] = compute_value(i
, factors
, factors_size
,
73 * struct err_values - structure containing error/reference values
74 * @refs: reference error values for -40C, 30C, 125C & 150C
75 * @errs: Actual error values for -40C, 30C, 125C & 150C read from the efuse
82 static void create_table_segments(struct err_values
*err_vals
, int seg
,
85 int m
= 0, c
, num
, den
, i
, err
, idx1
, idx2
, err1
, err2
, ref1
, ref2
;
90 idx1
= err_vals
->refs
[seg
];
92 idx2
= err_vals
->refs
[seg
+ 1];
93 err1
= err_vals
->errs
[seg
];
94 err2
= err_vals
->errs
[seg
+ 1];
95 ref1
= err_vals
->refs
[seg
];
96 ref2
= err_vals
->refs
[seg
+ 1];
99 * Calculate the slope with adc values read from the register
100 * as the y-axis param and err in adc value as x-axis param
109 * Take care of divide by zero error if error values are same
110 * Or when the slope is 0
112 if (den
!= 0 && m
!= 0) {
113 for (i
= idx1
; i
<= idx2
; i
++) {
115 if (((i
+ err
) < 0) || ((i
+ err
) >= TABLE_SIZE
))
117 derived_table
[i
] = ref_table
[i
+ err
];
119 } else { /* Constant error take care of divide by zero */
120 for (i
= idx1
; i
<= idx2
; i
++) {
121 if (((i
+ err1
) < 0) || ((i
+ err1
) >= TABLE_SIZE
))
123 derived_table
[i
] = ref_table
[i
+ err1
];
128 static int prep_lookup_table(struct err_values
*err_vals
, int *ref_table
)
133 * Fill up the lookup table under 3 segments
134 * region -40C to +30C
135 * region +30C to +125C
136 * region +125C to +150C
138 for (seg
= 0; seg
< 3; seg
++)
139 create_table_segments(err_vals
, seg
, ref_table
);
141 /* Get to the first valid temperature */
143 while (!derived_table
[i
])
147 * Get to the last zero index and back fill the temperature for
151 /* 300 milli celsius steps */
153 derived_table
[i
] = derived_table
[i
+ 1] - 300;
157 * Fill the last trailing 0s which are unfilled with increments of
158 * 100 milli celsius till 1023 code
161 while (!derived_table
[i
])
166 while (i
< TABLE_SIZE
) {
167 derived_table
[i
] = derived_table
[i
- 1] + inc
* 100;
174 struct k3_thermal_data
;
176 struct k3_j72xx_bandgap
{
179 void __iomem
*cfg2_base
;
180 struct k3_thermal_data
*ts_data
[K3_VTM_MAX_NUM_TS
];
184 /* common data structures */
185 struct k3_thermal_data
{
186 struct k3_j72xx_bandgap
*bgp
;
191 static int two_cmp(int tmp
, int mask
)
197 /* Return negative value */
201 static unsigned int vtm_get_best_value(unsigned int s0
, unsigned int s1
,
204 int d01
= abs(s0
- s1
);
205 int d02
= abs(s0
- s2
);
206 int d12
= abs(s1
- s2
);
208 if (d01
<= d02
&& d01
<= d12
)
209 return (s0
+ s1
) / 2;
211 if (d02
<= d01
&& d02
<= d12
)
212 return (s0
+ s2
) / 2;
214 return (s1
+ s2
) / 2;
217 static inline int k3_bgp_read_temp(struct k3_thermal_data
*devdata
,
220 struct k3_j72xx_bandgap
*bgp
;
221 unsigned int dtemp
, s0
, s1
, s2
;
225 * Errata is applicable for am654 pg 1.0 silicon/J7ES. There
226 * is a variation of the order for certain degree centigrade on AM654.
227 * Work around that by getting the average of two closest
228 * readings out of three readings everytime we want to
229 * report temperatures.
233 s0
= readl(bgp
->base
+ devdata
->stat_offset
) &
234 K3_VTM_TS_STAT_DTEMP_MASK
;
235 s1
= readl(bgp
->base
+ devdata
->stat_offset
) &
236 K3_VTM_TS_STAT_DTEMP_MASK
;
237 s2
= readl(bgp
->base
+ devdata
->stat_offset
) &
238 K3_VTM_TS_STAT_DTEMP_MASK
;
239 dtemp
= vtm_get_best_value(s0
, s1
, s2
);
241 if (dtemp
>= TABLE_SIZE
)
244 *temp
= derived_table
[dtemp
];
249 /* Get temperature callback function for thermal zone */
250 static int k3_thermal_get_temp(struct thermal_zone_device
*tz
, int *temp
)
252 return k3_bgp_read_temp(thermal_zone_device_priv(tz
), temp
);
255 static const struct thermal_zone_device_ops k3_of_thermal_ops
= {
256 .get_temp
= k3_thermal_get_temp
,
259 static int k3_j72xx_bandgap_temp_to_adc_code(int temp
)
261 int low
= 0, high
= TABLE_SIZE
- 1, mid
;
263 if (temp
> 160000 || temp
< -50000)
266 /* Binary search to find the adc code */
267 while (low
< (high
- 1)) {
268 mid
= (low
+ high
) / 2;
269 if (temp
<= derived_table
[mid
])
278 static void get_efuse_values(int id
, struct k3_thermal_data
*data
, int *err
,
279 void __iomem
*fuse_base
)
282 int ct_offsets
[5][K3_VTM_CORRECTION_TEMP_CNT
] = {
289 int ct_bm
[5][K3_VTM_CORRECTION_TEMP_CNT
] = {
290 { 0x3f, 0x1fe000, 0x1ff },
291 { 0xfc0, 0x1fe000, 0x3fe00 },
292 { 0x3f000, 0x7f800000, 0x7fc0000 },
293 { 0xfc0000, 0x1fe0, 0x1f800000 },
294 { 0x3f000000, 0x1fe000, 0x1ff0 }
297 for (i
= 0; i
< 3; i
++) {
298 /* Extract the offset value using bit-mask */
299 if (ct_offsets
[id
][i
] == -1 && i
== 1) {
300 /* 25C offset Case of Sensor 2 split between 2 regs */
301 tmp
= (readl(fuse_base
+ 0x8) & 0xE0000000) >> (29);
302 tmp
|= ((readl(fuse_base
+ 0xC) & 0x1F) << 3);
304 } else if (ct_offsets
[id
][i
] == -1 && i
== 2) {
305 /* 125C Case of Sensor 3 split between 2 regs */
306 tmp
= (readl(fuse_base
+ 0x4) & 0xF8000000) >> (27);
307 tmp
|= ((readl(fuse_base
+ 0x8) & 0xF) << 5);
310 tmp
= readl(fuse_base
+ ct_offsets
[id
][i
]);
312 tmp
= tmp
>> __ffs(ct_bm
[id
][i
]);
314 /* Obtain the sign bit pow*/
315 pow
= ct_bm
[id
][i
] >> __ffs(ct_bm
[id
][i
]);
320 /* Check for negative value */
322 /* 2's complement value */
323 tmp
= two_cmp(tmp
, ct_bm
[id
][i
] >> __ffs(ct_bm
[id
][i
]));
328 /* Err value for 150C is set to 0 */
332 static void print_look_up_table(struct device
*dev
, int *ref_table
)
336 dev_dbg(dev
, "The contents of derived array\n");
337 dev_dbg(dev
, "Code Temperature\n");
338 for (i
= 0; i
< TABLE_SIZE
; i
++)
339 dev_dbg(dev
, "%d %d %d\n", i
, derived_table
[i
], ref_table
[i
]);
342 static void k3_j72xx_bandgap_init_hw(struct k3_j72xx_bandgap
*bgp
)
344 struct k3_thermal_data
*data
;
345 int id
, high_max
, low_temp
;
348 for (id
= 0; id
< bgp
->cnt
; id
++) {
349 data
= bgp
->ts_data
[id
];
350 val
= readl(bgp
->cfg2_base
+ data
->ctrl_offset
);
351 val
|= (K3_VTM_TMPSENS_CTRL_MAXT_OUTRG_EN
|
352 K3_VTM_TMPSENS_CTRL_SOC
|
353 K3_VTM_TMPSENS_CTRL_CLRZ
| BIT(4));
354 writel(val
, bgp
->cfg2_base
+ data
->ctrl_offset
);
358 * Program TSHUT thresholds
359 * Step 1: set the thresholds to ~123C and 105C WKUP_VTM_MISC_CTRL2
360 * Step 2: WKUP_VTM_TMPSENS_CTRL_j set the MAXT_OUTRG_EN bit
361 * This is already taken care as per of init
362 * Step 3: WKUP_VTM_MISC_CTRL set the ANYMAXT_OUTRG_ALERT_EN bit
364 high_max
= k3_j72xx_bandgap_temp_to_adc_code(MAX_TEMP
);
365 low_temp
= k3_j72xx_bandgap_temp_to_adc_code(COOL_DOWN_TEMP
);
367 writel((low_temp
<< 16) | high_max
, bgp
->cfg2_base
+ K3_VTM_MISC_CTRL2_OFFSET
);
368 writel(K3_VTM_ANYMAXT_OUTRG_ALERT_EN
, bgp
->cfg2_base
+ K3_VTM_MISC_CTRL_OFFSET
);
371 struct k3_j72xx_bandgap_data
{
372 const bool has_errata_i2128
;
375 static int k3_j72xx_bandgap_probe(struct platform_device
*pdev
)
377 const struct k3_j72xx_bandgap_data
*driver_data
;
378 struct thermal_zone_device
*ti_thermal
;
379 struct device
*dev
= &pdev
->dev
;
380 bool workaround_needed
= false;
381 struct k3_j72xx_bandgap
*bgp
;
382 struct k3_thermal_data
*data
;
383 struct err_values err_vals
;
384 void __iomem
*fuse_base
;
385 int ret
= 0, val
, id
;
386 struct resource
*res
;
389 const s64 golden_factors
[] = {
397 const s64 pvt_wa_factors
[] = {
403 bgp
= devm_kzalloc(&pdev
->dev
, sizeof(*bgp
), GFP_KERNEL
);
408 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
409 bgp
->base
= devm_ioremap_resource(dev
, res
);
410 if (IS_ERR(bgp
->base
))
411 return PTR_ERR(bgp
->base
);
413 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 1);
414 bgp
->cfg2_base
= devm_ioremap_resource(dev
, res
);
415 if (IS_ERR(bgp
->cfg2_base
))
416 return PTR_ERR(bgp
->cfg2_base
);
418 driver_data
= of_device_get_match_data(dev
);
420 workaround_needed
= driver_data
->has_errata_i2128
;
423 * Some of TI's J721E SoCs require a software trimming procedure
424 * for the temperature monitors to function properly. To determine
425 * if this particular SoC is NOT affected, both bits in the
426 * WKUP_SPARE_FUSE0[31:30] will be set (0xC0000000) indicating
427 * when software trimming should NOT be applied.
429 * https://www.ti.com/lit/er/sprz455c/sprz455c.pdf
431 if (workaround_needed
) {
432 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 2);
433 fuse_base
= devm_ioremap_resource(dev
, res
);
434 if (IS_ERR(fuse_base
))
435 return PTR_ERR(fuse_base
);
437 if ((readl(fuse_base
) & 0xc0000000) == 0xc0000000)
438 workaround_needed
= false;
441 dev_dbg(bgp
->dev
, "Work around %sneeded\n",
442 workaround_needed
? "" : "not ");
444 pm_runtime_enable(dev
);
445 ret
= pm_runtime_get_sync(dev
);
447 pm_runtime_put_noidle(dev
);
448 pm_runtime_disable(dev
);
452 /* Get the sensor count in the VTM */
453 val
= readl(bgp
->base
+ K3_VTM_DEVINFO_PWR0_OFFSET
);
454 bgp
->cnt
= val
& K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK
;
455 bgp
->cnt
>>= __ffs(K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK
);
457 data
= devm_kcalloc(bgp
->dev
, bgp
->cnt
, sizeof(*data
), GFP_KERNEL
);
463 ref_table
= kzalloc(sizeof(*ref_table
) * TABLE_SIZE
, GFP_KERNEL
);
469 derived_table
= devm_kzalloc(bgp
->dev
, sizeof(*derived_table
) * TABLE_SIZE
,
471 if (!derived_table
) {
473 goto err_free_ref_table
;
476 if (!workaround_needed
)
477 init_table(5, ref_table
, golden_factors
);
479 init_table(3, ref_table
, pvt_wa_factors
);
481 /* Precompute the derived table & fill each thermal sensor struct */
482 for (id
= 0; id
< bgp
->cnt
; id
++) {
484 data
[id
].ctrl_offset
= K3_VTM_TMPSENS0_CTRL_OFFSET
+ id
* 0x20;
485 data
[id
].stat_offset
= data
[id
].ctrl_offset
+
486 K3_VTM_TMPSENS_STAT_OFFSET
;
488 if (workaround_needed
) {
489 /* ref adc values for -40C, 30C & 125C respectively */
490 err_vals
.refs
[0] = MINUS40CREF
;
491 err_vals
.refs
[1] = PLUS30CREF
;
492 err_vals
.refs
[2] = PLUS125CREF
;
493 err_vals
.refs
[3] = PLUS150CREF
;
494 get_efuse_values(id
, &data
[id
], err_vals
.errs
, fuse_base
);
497 if (id
== 0 && workaround_needed
)
498 prep_lookup_table(&err_vals
, ref_table
);
499 else if (id
== 0 && !workaround_needed
)
500 memcpy(derived_table
, ref_table
, TABLE_SIZE
* 4);
502 bgp
->ts_data
[id
] = &data
[id
];
505 k3_j72xx_bandgap_init_hw(bgp
);
507 /* Register the thermal sensors */
508 for (id
= 0; id
< bgp
->cnt
; id
++) {
509 ti_thermal
= devm_thermal_of_zone_register(bgp
->dev
, id
, &data
[id
],
511 if (IS_ERR(ti_thermal
)) {
512 dev_err(bgp
->dev
, "thermal zone device is NULL\n");
513 ret
= PTR_ERR(ti_thermal
);
514 goto err_free_ref_table
;
518 platform_set_drvdata(pdev
, bgp
);
520 print_look_up_table(dev
, ref_table
);
522 * Now that the derived_table has the appropriate look up values
523 * Free up the ref_table
533 pm_runtime_put_sync(&pdev
->dev
);
534 pm_runtime_disable(&pdev
->dev
);
539 static void k3_j72xx_bandgap_remove(struct platform_device
*pdev
)
541 pm_runtime_put_sync(&pdev
->dev
);
542 pm_runtime_disable(&pdev
->dev
);
545 static int k3_j72xx_bandgap_suspend(struct device
*dev
)
547 pm_runtime_put_sync(dev
);
548 pm_runtime_disable(dev
);
552 static int k3_j72xx_bandgap_resume(struct device
*dev
)
554 struct k3_j72xx_bandgap
*bgp
= dev_get_drvdata(dev
);
557 pm_runtime_enable(dev
);
558 ret
= pm_runtime_get_sync(dev
);
560 pm_runtime_put_noidle(dev
);
561 pm_runtime_disable(dev
);
565 k3_j72xx_bandgap_init_hw(bgp
);
570 static DEFINE_SIMPLE_DEV_PM_OPS(k3_j72xx_bandgap_pm_ops
,
571 k3_j72xx_bandgap_suspend
,
572 k3_j72xx_bandgap_resume
);
574 static const struct k3_j72xx_bandgap_data k3_j72xx_bandgap_j721e_data
= {
575 .has_errata_i2128
= true,
578 static const struct k3_j72xx_bandgap_data k3_j72xx_bandgap_j7200_data
= {
579 .has_errata_i2128
= false,
582 static const struct of_device_id of_k3_j72xx_bandgap_match
[] = {
584 .compatible
= "ti,j721e-vtm",
585 .data
= &k3_j72xx_bandgap_j721e_data
,
588 .compatible
= "ti,j7200-vtm",
589 .data
= &k3_j72xx_bandgap_j7200_data
,
593 MODULE_DEVICE_TABLE(of
, of_k3_j72xx_bandgap_match
);
595 static struct platform_driver k3_j72xx_bandgap_sensor_driver
= {
596 .probe
= k3_j72xx_bandgap_probe
,
597 .remove
= k3_j72xx_bandgap_remove
,
599 .name
= "k3-j72xx-soc-thermal",
600 .of_match_table
= of_k3_j72xx_bandgap_match
,
601 .pm
= pm_sleep_ptr(&k3_j72xx_bandgap_pm_ops
),
605 module_platform_driver(k3_j72xx_bandgap_sensor_driver
);
607 MODULE_DESCRIPTION("K3 bandgap temperature sensor driver");
608 MODULE_LICENSE("GPL");
609 MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");