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