xref: /OK3568_Linux_fs/kernel/drivers/thermal/tegra/soctherm-fuse.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (c) 2014-2016, NVIDIA CORPORATION.  All rights reserved.
4*4882a593Smuzhiyun  */
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include <linux/module.h>
7*4882a593Smuzhiyun #include <linux/platform_device.h>
8*4882a593Smuzhiyun #include <soc/tegra/fuse.h>
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include "soctherm.h"
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #define NOMINAL_CALIB_FT			105
13*4882a593Smuzhiyun #define NOMINAL_CALIB_CP			25
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #define FUSE_TSENSOR_CALIB_CP_TS_BASE_MASK	0x1fff
16*4882a593Smuzhiyun #define FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK	(0x1fff << 13)
17*4882a593Smuzhiyun #define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT	13
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #define FUSE_TSENSOR_COMMON			0x180
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun /*
22*4882a593Smuzhiyun  * Tegra210: Layout of bits in FUSE_TSENSOR_COMMON:
23*4882a593Smuzhiyun  *    3                   2                   1                   0
24*4882a593Smuzhiyun  *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
25*4882a593Smuzhiyun  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
26*4882a593Smuzhiyun  * |       BASE_FT       |      BASE_CP      | SHFT_FT | SHIFT_CP  |
27*4882a593Smuzhiyun  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
28*4882a593Smuzhiyun  *
29*4882a593Smuzhiyun  * Tegra12x, etc:
30*4882a593Smuzhiyun  * In chips prior to Tegra210, this fuse was incorrectly sized as 26 bits,
31*4882a593Smuzhiyun  * and didn't hold SHIFT_CP in [31:26]. Therefore these missing six bits
32*4882a593Smuzhiyun  * were obtained via the FUSE_SPARE_REALIGNMENT_REG register [5:0].
33*4882a593Smuzhiyun  *
34*4882a593Smuzhiyun  * FUSE_TSENSOR_COMMON:
35*4882a593Smuzhiyun  *    3                   2                   1                   0
36*4882a593Smuzhiyun  *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
37*4882a593Smuzhiyun  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38*4882a593Smuzhiyun  * |-----------| SHFT_FT |       BASE_FT       |      BASE_CP      |
39*4882a593Smuzhiyun  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
40*4882a593Smuzhiyun  *
41*4882a593Smuzhiyun  * FUSE_SPARE_REALIGNMENT_REG:
42*4882a593Smuzhiyun  *    3                   2                   1                   0
43*4882a593Smuzhiyun  *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
44*4882a593Smuzhiyun  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45*4882a593Smuzhiyun  * |---------------------------------------------------| SHIFT_CP  |
46*4882a593Smuzhiyun  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47*4882a593Smuzhiyun  */
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun #define CALIB_COEFFICIENT 1000000LL
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun /**
52*4882a593Smuzhiyun  * div64_s64_precise() - wrapper for div64_s64()
53*4882a593Smuzhiyun  * @a:  the dividend
54*4882a593Smuzhiyun  * @b:  the divisor
55*4882a593Smuzhiyun  *
56*4882a593Smuzhiyun  * Implements division with fairly accurate rounding instead of truncation by
57*4882a593Smuzhiyun  * shifting the dividend to the left by 16 so that the quotient has a
58*4882a593Smuzhiyun  * much higher precision.
59*4882a593Smuzhiyun  *
60*4882a593Smuzhiyun  * Return: the quotient of a / b.
61*4882a593Smuzhiyun  */
div64_s64_precise(s64 a,s32 b)62*4882a593Smuzhiyun static s64 div64_s64_precise(s64 a, s32 b)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun 	s64 r, al;
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	/* Scale up for increased precision division */
67*4882a593Smuzhiyun 	al = a << 16;
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun 	r = div64_s64(al * 2 + 1, 2 * b);
70*4882a593Smuzhiyun 	return r >> 16;
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun 
tegra_calc_shared_calib(const struct tegra_soctherm_fuse * tfuse,struct tsensor_shared_calib * shared)73*4882a593Smuzhiyun int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse,
74*4882a593Smuzhiyun 			    struct tsensor_shared_calib *shared)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun 	u32 val;
77*4882a593Smuzhiyun 	s32 shifted_cp, shifted_ft;
78*4882a593Smuzhiyun 	int err;
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	err = tegra_fuse_readl(FUSE_TSENSOR_COMMON, &val);
81*4882a593Smuzhiyun 	if (err)
82*4882a593Smuzhiyun 		return err;
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	shared->base_cp = (val & tfuse->fuse_base_cp_mask) >>
85*4882a593Smuzhiyun 			  tfuse->fuse_base_cp_shift;
86*4882a593Smuzhiyun 	shared->base_ft = (val & tfuse->fuse_base_ft_mask) >>
87*4882a593Smuzhiyun 			  tfuse->fuse_base_ft_shift;
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	shifted_ft = (val & tfuse->fuse_shift_ft_mask) >>
90*4882a593Smuzhiyun 		     tfuse->fuse_shift_ft_shift;
91*4882a593Smuzhiyun 	shifted_ft = sign_extend32(shifted_ft, 4);
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun 	if (tfuse->fuse_spare_realignment) {
94*4882a593Smuzhiyun 		err = tegra_fuse_readl(tfuse->fuse_spare_realignment, &val);
95*4882a593Smuzhiyun 		if (err)
96*4882a593Smuzhiyun 			return err;
97*4882a593Smuzhiyun 	}
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	shifted_cp = sign_extend32(val, 5);
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	shared->actual_temp_cp = 2 * NOMINAL_CALIB_CP + shifted_cp;
102*4882a593Smuzhiyun 	shared->actual_temp_ft = 2 * NOMINAL_CALIB_FT + shifted_ft;
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	return 0;
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun 
tegra_calc_tsensor_calib(const struct tegra_tsensor * sensor,const struct tsensor_shared_calib * shared,u32 * calibration)107*4882a593Smuzhiyun int tegra_calc_tsensor_calib(const struct tegra_tsensor *sensor,
108*4882a593Smuzhiyun 			     const struct tsensor_shared_calib *shared,
109*4882a593Smuzhiyun 			     u32 *calibration)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun 	const struct tegra_tsensor_group *sensor_group;
112*4882a593Smuzhiyun 	u32 val, calib;
113*4882a593Smuzhiyun 	s32 actual_tsensor_ft, actual_tsensor_cp;
114*4882a593Smuzhiyun 	s32 delta_sens, delta_temp;
115*4882a593Smuzhiyun 	s32 mult, div;
116*4882a593Smuzhiyun 	s16 therma, thermb;
117*4882a593Smuzhiyun 	s64 temp;
118*4882a593Smuzhiyun 	int err;
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	sensor_group = sensor->group;
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	err = tegra_fuse_readl(sensor->calib_fuse_offset, &val);
123*4882a593Smuzhiyun 	if (err)
124*4882a593Smuzhiyun 		return err;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	actual_tsensor_cp = (shared->base_cp * 64) + sign_extend32(val, 12);
127*4882a593Smuzhiyun 	val = (val & FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK) >>
128*4882a593Smuzhiyun 	      FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT;
129*4882a593Smuzhiyun 	actual_tsensor_ft = (shared->base_ft * 32) + sign_extend32(val, 12);
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	delta_sens = actual_tsensor_ft - actual_tsensor_cp;
132*4882a593Smuzhiyun 	delta_temp = shared->actual_temp_ft - shared->actual_temp_cp;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	mult = sensor_group->pdiv * sensor->config->tsample_ate;
135*4882a593Smuzhiyun 	div = sensor->config->tsample * sensor_group->pdiv_ate;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	temp = (s64)delta_temp * (1LL << 13) * mult;
138*4882a593Smuzhiyun 	therma = div64_s64_precise(temp, (s64)delta_sens * div);
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	temp = ((s64)actual_tsensor_ft * shared->actual_temp_cp) -
141*4882a593Smuzhiyun 		((s64)actual_tsensor_cp * shared->actual_temp_ft);
142*4882a593Smuzhiyun 	thermb = div64_s64_precise(temp, delta_sens);
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 	temp = (s64)therma * sensor->fuse_corr_alpha;
145*4882a593Smuzhiyun 	therma = div64_s64_precise(temp, CALIB_COEFFICIENT);
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	temp = (s64)thermb * sensor->fuse_corr_alpha + sensor->fuse_corr_beta;
148*4882a593Smuzhiyun 	thermb = div64_s64_precise(temp, CALIB_COEFFICIENT);
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	calib = ((u16)therma << SENSOR_CONFIG2_THERMA_SHIFT) |
151*4882a593Smuzhiyun 		((u16)thermb << SENSOR_CONFIG2_THERMB_SHIFT);
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	*calibration = calib;
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	return 0;
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun MODULE_AUTHOR("Wei Ni <wni@nvidia.com>");
159*4882a593Smuzhiyun MODULE_DESCRIPTION("Tegra SOCTHERM fuse management");
160*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
161