Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2014-2016, NVIDIA CORPORATION.  All rights reserved.
0004  */
0005 
0006 #include <linux/module.h>
0007 #include <linux/platform_device.h>
0008 #include <soc/tegra/fuse.h>
0009 
0010 #include "soctherm.h"
0011 
0012 #define NOMINAL_CALIB_FT            105
0013 #define NOMINAL_CALIB_CP            25
0014 
0015 #define FUSE_TSENSOR_CALIB_CP_TS_BASE_MASK  0x1fff
0016 #define FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK  (0x1fff << 13)
0017 #define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13
0018 
0019 #define FUSE_TSENSOR_COMMON         0x180
0020 
0021 /*
0022  * Tegra210: Layout of bits in FUSE_TSENSOR_COMMON:
0023  *    3                   2                   1                   0
0024  *  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
0025  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0026  * |       BASE_FT       |      BASE_CP      | SHFT_FT | SHIFT_CP  |
0027  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0028  *
0029  * Tegra12x, etc:
0030  * In chips prior to Tegra210, this fuse was incorrectly sized as 26 bits,
0031  * and didn't hold SHIFT_CP in [31:26]. Therefore these missing six bits
0032  * were obtained via the FUSE_SPARE_REALIGNMENT_REG register [5:0].
0033  *
0034  * FUSE_TSENSOR_COMMON:
0035  *    3                   2                   1                   0
0036  *  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
0037  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0038  * |-----------| SHFT_FT |       BASE_FT       |      BASE_CP      |
0039  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0040  *
0041  * FUSE_SPARE_REALIGNMENT_REG:
0042  *    3                   2                   1                   0
0043  *  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
0044  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0045  * |---------------------------------------------------| SHIFT_CP  |
0046  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0047  */
0048 
0049 #define CALIB_COEFFICIENT 1000000LL
0050 
0051 /**
0052  * div64_s64_precise() - wrapper for div64_s64()
0053  * @a:  the dividend
0054  * @b:  the divisor
0055  *
0056  * Implements division with fairly accurate rounding instead of truncation by
0057  * shifting the dividend to the left by 16 so that the quotient has a
0058  * much higher precision.
0059  *
0060  * Return: the quotient of a / b.
0061  */
0062 static s64 div64_s64_precise(s64 a, s32 b)
0063 {
0064     s64 r, al;
0065 
0066     /* Scale up for increased precision division */
0067     al = a << 16;
0068 
0069     r = div64_s64(al * 2 + 1, 2 * b);
0070     return r >> 16;
0071 }
0072 
0073 int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse,
0074                 struct tsensor_shared_calib *shared)
0075 {
0076     u32 val;
0077     s32 shifted_cp, shifted_ft;
0078     int err;
0079 
0080     err = tegra_fuse_readl(FUSE_TSENSOR_COMMON, &val);
0081     if (err)
0082         return err;
0083 
0084     shared->base_cp = (val & tfuse->fuse_base_cp_mask) >>
0085               tfuse->fuse_base_cp_shift;
0086     shared->base_ft = (val & tfuse->fuse_base_ft_mask) >>
0087               tfuse->fuse_base_ft_shift;
0088 
0089     shifted_ft = (val & tfuse->fuse_shift_ft_mask) >>
0090              tfuse->fuse_shift_ft_shift;
0091     shifted_ft = sign_extend32(shifted_ft, 4);
0092 
0093     if (tfuse->fuse_spare_realignment) {
0094         err = tegra_fuse_readl(tfuse->fuse_spare_realignment, &val);
0095         if (err)
0096             return err;
0097     }
0098 
0099     shifted_cp = sign_extend32(val, 5);
0100 
0101     shared->actual_temp_cp = 2 * NOMINAL_CALIB_CP + shifted_cp;
0102     shared->actual_temp_ft = 2 * NOMINAL_CALIB_FT + shifted_ft;
0103 
0104     return 0;
0105 }
0106 
0107 int tegra_calc_tsensor_calib(const struct tegra_tsensor *sensor,
0108                  const struct tsensor_shared_calib *shared,
0109                  u32 *calibration)
0110 {
0111     const struct tegra_tsensor_group *sensor_group;
0112     u32 val, calib;
0113     s32 actual_tsensor_ft, actual_tsensor_cp;
0114     s32 delta_sens, delta_temp;
0115     s32 mult, div;
0116     s16 therma, thermb;
0117     s64 temp;
0118     int err;
0119 
0120     sensor_group = sensor->group;
0121 
0122     err = tegra_fuse_readl(sensor->calib_fuse_offset, &val);
0123     if (err)
0124         return err;
0125 
0126     actual_tsensor_cp = (shared->base_cp * 64) + sign_extend32(val, 12);
0127     val = (val & FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK) >>
0128           FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT;
0129     actual_tsensor_ft = (shared->base_ft * 32) + sign_extend32(val, 12);
0130 
0131     delta_sens = actual_tsensor_ft - actual_tsensor_cp;
0132     delta_temp = shared->actual_temp_ft - shared->actual_temp_cp;
0133 
0134     mult = sensor_group->pdiv * sensor->config->tsample_ate;
0135     div = sensor->config->tsample * sensor_group->pdiv_ate;
0136 
0137     temp = (s64)delta_temp * (1LL << 13) * mult;
0138     therma = div64_s64_precise(temp, (s64)delta_sens * div);
0139 
0140     temp = ((s64)actual_tsensor_ft * shared->actual_temp_cp) -
0141         ((s64)actual_tsensor_cp * shared->actual_temp_ft);
0142     thermb = div64_s64_precise(temp, delta_sens);
0143 
0144     temp = (s64)therma * sensor->fuse_corr_alpha;
0145     therma = div64_s64_precise(temp, CALIB_COEFFICIENT);
0146 
0147     temp = (s64)thermb * sensor->fuse_corr_alpha + sensor->fuse_corr_beta;
0148     thermb = div64_s64_precise(temp, CALIB_COEFFICIENT);
0149 
0150     calib = ((u16)therma << SENSOR_CONFIG2_THERMA_SHIFT) |
0151         ((u16)thermb << SENSOR_CONFIG2_THERMB_SHIFT);
0152 
0153     *calibration = calib;
0154 
0155     return 0;
0156 }
0157 
0158 MODULE_AUTHOR("Wei Ni <wni@nvidia.com>");
0159 MODULE_DESCRIPTION("Tegra SOCTHERM fuse management");
0160 MODULE_LICENSE("GPL v2");