1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved.
6 #include <linux/module.h>
7 #include <linux/platform_device.h>
8 #include <soc/tegra/fuse.h>
12 #define NOMINAL_CALIB_FT 105
13 #define NOMINAL_CALIB_CP 25
15 #define FUSE_TSENSOR_CALIB_CP_TS_BASE_MASK 0x1fff
16 #define FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK (0x1fff << 13)
17 #define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13
19 #define FUSE_TSENSOR_COMMON 0x180
22 * Tegra210: Layout of bits in FUSE_TSENSOR_COMMON:
24 * 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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
26 * | BASE_FT | BASE_CP | SHFT_FT | SHIFT_CP |
27 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
30 * In chips prior to Tegra210, this fuse was incorrectly sized as 26 bits,
31 * and didn't hold SHIFT_CP in [31:26]. Therefore these missing six bits
32 * were obtained via the FUSE_SPARE_REALIGNMENT_REG register [5:0].
34 * FUSE_TSENSOR_COMMON:
36 * 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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38 * |-----------| SHFT_FT | BASE_FT | BASE_CP |
39 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41 * FUSE_SPARE_REALIGNMENT_REG:
43 * 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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45 * |---------------------------------------------------| SHIFT_CP |
46 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
49 #define CALIB_COEFFICIENT 1000000LL
52 * div64_s64_precise() - wrapper for div64_s64()
56 * Implements division with fairly accurate rounding instead of truncation by
57 * shifting the dividend to the left by 16 so that the quotient has a
58 * much higher precision.
60 * Return: the quotient of a / b.
62 static s64
div64_s64_precise(s64 a
, s32 b
)
66 /* Scale up for increased precision division */
69 r
= div64_s64(al
* 2 + 1, 2 * b
);
73 int tegra_calc_shared_calib(const struct tegra_soctherm_fuse
*tfuse
,
74 struct tsensor_shared_calib
*shared
)
77 s32 shifted_cp
, shifted_ft
;
80 err
= tegra_fuse_readl(FUSE_TSENSOR_COMMON
, &val
);
84 shared
->base_cp
= (val
& tfuse
->fuse_base_cp_mask
) >>
85 tfuse
->fuse_base_cp_shift
;
86 shared
->base_ft
= (val
& tfuse
->fuse_base_ft_mask
) >>
87 tfuse
->fuse_base_ft_shift
;
89 shifted_ft
= (val
& tfuse
->fuse_shift_ft_mask
) >>
90 tfuse
->fuse_shift_ft_shift
;
91 shifted_ft
= sign_extend32(shifted_ft
, 4);
93 if (tfuse
->fuse_spare_realignment
) {
94 err
= tegra_fuse_readl(tfuse
->fuse_spare_realignment
, &val
);
99 shifted_cp
= sign_extend32(val
, 5);
101 shared
->actual_temp_cp
= 2 * NOMINAL_CALIB_CP
+ shifted_cp
;
102 shared
->actual_temp_ft
= 2 * NOMINAL_CALIB_FT
+ shifted_ft
;
107 int tegra_calc_tsensor_calib(const struct tegra_tsensor
*sensor
,
108 const struct tsensor_shared_calib
*shared
,
111 const struct tegra_tsensor_group
*sensor_group
;
113 s32 actual_tsensor_ft
, actual_tsensor_cp
;
114 s32 delta_sens
, delta_temp
;
120 sensor_group
= sensor
->group
;
122 err
= tegra_fuse_readl(sensor
->calib_fuse_offset
, &val
);
126 actual_tsensor_cp
= (shared
->base_cp
* 64) + sign_extend32(val
, 12);
127 val
= (val
& FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK
) >>
128 FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT
;
129 actual_tsensor_ft
= (shared
->base_ft
* 32) + sign_extend32(val
, 12);
131 delta_sens
= actual_tsensor_ft
- actual_tsensor_cp
;
132 delta_temp
= shared
->actual_temp_ft
- shared
->actual_temp_cp
;
134 mult
= sensor_group
->pdiv
* sensor
->config
->tsample_ate
;
135 div
= sensor
->config
->tsample
* sensor_group
->pdiv_ate
;
137 temp
= (s64
)delta_temp
* (1LL << 13) * mult
;
138 therma
= div64_s64_precise(temp
, (s64
)delta_sens
* div
);
140 temp
= ((s64
)actual_tsensor_ft
* shared
->actual_temp_cp
) -
141 ((s64
)actual_tsensor_cp
* shared
->actual_temp_ft
);
142 thermb
= div64_s64_precise(temp
, delta_sens
);
144 temp
= (s64
)therma
* sensor
->fuse_corr_alpha
;
145 therma
= div64_s64_precise(temp
, CALIB_COEFFICIENT
);
147 temp
= (s64
)thermb
* sensor
->fuse_corr_alpha
+ sensor
->fuse_corr_beta
;
148 thermb
= div64_s64_precise(temp
, CALIB_COEFFICIENT
);
150 calib
= ((u16
)therma
<< SENSOR_CONFIG2_THERMA_SHIFT
) |
151 ((u16
)thermb
<< SENSOR_CONFIG2_THERMB_SHIFT
);
153 *calibration
= calib
;
158 MODULE_AUTHOR("Wei Ni <wni@nvidia.com>");
159 MODULE_DESCRIPTION("Tegra SOCTHERM fuse management");
160 MODULE_LICENSE("GPL v2");