2 * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved.
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
15 #include <linux/module.h>
16 #include <linux/platform_device.h>
17 #include <soc/tegra/fuse.h>
21 #define NOMINAL_CALIB_FT 105
22 #define NOMINAL_CALIB_CP 25
24 #define FUSE_TSENSOR_CALIB_CP_TS_BASE_MASK 0x1fff
25 #define FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK (0x1fff << 13)
26 #define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13
28 #define FUSE_TSENSOR_COMMON 0x180
31 * Tegra210: Layout of bits in FUSE_TSENSOR_COMMON:
33 * 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
34 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35 * | BASE_FT | BASE_CP | SHFT_FT | SHIFT_CP |
36 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39 * In chips prior to Tegra210, this fuse was incorrectly sized as 26 bits,
40 * and didn't hold SHIFT_CP in [31:26]. Therefore these missing six bits
41 * were obtained via the FUSE_SPARE_REALIGNMENT_REG register [5:0].
43 * FUSE_TSENSOR_COMMON:
45 * 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
46 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47 * |-----------| SHFT_FT | BASE_FT | BASE_CP |
48 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
50 * FUSE_SPARE_REALIGNMENT_REG:
52 * 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
53 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
54 * |---------------------------------------------------| SHIFT_CP |
55 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
58 #define CALIB_COEFFICIENT 1000000LL
61 * div64_s64_precise() - wrapper for div64_s64()
65 * Implements division with fairly accurate rounding instead of truncation by
66 * shifting the dividend to the left by 16 so that the quotient has a
67 * much higher precision.
69 * Return: the quotient of a / b.
71 static s64
div64_s64_precise(s64 a
, s32 b
)
75 /* Scale up for increased precision division */
78 r
= div64_s64(al
* 2 + 1, 2 * b
);
82 int tegra_calc_shared_calib(const struct tegra_soctherm_fuse
*tfuse
,
83 struct tsensor_shared_calib
*shared
)
86 s32 shifted_cp
, shifted_ft
;
89 err
= tegra_fuse_readl(FUSE_TSENSOR_COMMON
, &val
);
93 shared
->base_cp
= (val
& tfuse
->fuse_base_cp_mask
) >>
94 tfuse
->fuse_base_cp_shift
;
95 shared
->base_ft
= (val
& tfuse
->fuse_base_ft_mask
) >>
96 tfuse
->fuse_base_ft_shift
;
98 shifted_ft
= (val
& tfuse
->fuse_shift_ft_mask
) >>
99 tfuse
->fuse_shift_ft_shift
;
100 shifted_ft
= sign_extend32(shifted_ft
, 4);
102 if (tfuse
->fuse_spare_realignment
) {
103 err
= tegra_fuse_readl(tfuse
->fuse_spare_realignment
, &val
);
108 shifted_cp
= sign_extend32(val
, 5);
110 shared
->actual_temp_cp
= 2 * NOMINAL_CALIB_CP
+ shifted_cp
;
111 shared
->actual_temp_ft
= 2 * NOMINAL_CALIB_FT
+ shifted_ft
;
116 int tegra_calc_tsensor_calib(const struct tegra_tsensor
*sensor
,
117 const struct tsensor_shared_calib
*shared
,
120 const struct tegra_tsensor_group
*sensor_group
;
122 s32 actual_tsensor_ft
, actual_tsensor_cp
;
123 s32 delta_sens
, delta_temp
;
129 sensor_group
= sensor
->group
;
131 err
= tegra_fuse_readl(sensor
->calib_fuse_offset
, &val
);
135 actual_tsensor_cp
= (shared
->base_cp
* 64) + sign_extend32(val
, 12);
136 val
= (val
& FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK
) >>
137 FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT
;
138 actual_tsensor_ft
= (shared
->base_ft
* 32) + sign_extend32(val
, 12);
140 delta_sens
= actual_tsensor_ft
- actual_tsensor_cp
;
141 delta_temp
= shared
->actual_temp_ft
- shared
->actual_temp_cp
;
143 mult
= sensor_group
->pdiv
* sensor
->config
->tsample_ate
;
144 div
= sensor
->config
->tsample
* sensor_group
->pdiv_ate
;
146 temp
= (s64
)delta_temp
* (1LL << 13) * mult
;
147 therma
= div64_s64_precise(temp
, (s64
)delta_sens
* div
);
149 temp
= ((s64
)actual_tsensor_ft
* shared
->actual_temp_cp
) -
150 ((s64
)actual_tsensor_cp
* shared
->actual_temp_ft
);
151 thermb
= div64_s64_precise(temp
, delta_sens
);
153 temp
= (s64
)therma
* sensor
->fuse_corr_alpha
;
154 therma
= div64_s64_precise(temp
, CALIB_COEFFICIENT
);
156 temp
= (s64
)thermb
* sensor
->fuse_corr_alpha
+ sensor
->fuse_corr_beta
;
157 thermb
= div64_s64_precise(temp
, CALIB_COEFFICIENT
);
159 calib
= ((u16
)therma
<< SENSOR_CONFIG2_THERMA_SHIFT
) |
160 ((u16
)thermb
<< SENSOR_CONFIG2_THERMB_SHIFT
);
162 *calibration
= calib
;
167 MODULE_AUTHOR("Wei Ni <wni@nvidia.com>");
168 MODULE_DESCRIPTION("Tegra SOCTHERM fuse management");
169 MODULE_LICENSE("GPL v2");