root/drivers/thermal/tegra/soctherm-fuse.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. div64_s64_precise
  2. tegra_calc_shared_calib
  3. tegra_calc_tsensor_calib

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Copyright (c) 2014-2016, NVIDIA CORPORATION.  All rights reserved.
   4  */
   5 
   6 #include <linux/module.h>
   7 #include <linux/platform_device.h>
   8 #include <soc/tegra/fuse.h>
   9 
  10 #include "soctherm.h"
  11 
  12 #define NOMINAL_CALIB_FT                        105
  13 #define NOMINAL_CALIB_CP                        25
  14 
  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
  18 
  19 #define FUSE_TSENSOR_COMMON                     0x180
  20 
  21 /*
  22  * Tegra210: Layout of bits in FUSE_TSENSOR_COMMON:
  23  *    3                   2                   1                   0
  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  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  28  *
  29  * Tegra12x, etc:
  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].
  33  *
  34  * FUSE_TSENSOR_COMMON:
  35  *    3                   2                   1                   0
  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  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  40  *
  41  * FUSE_SPARE_REALIGNMENT_REG:
  42  *    3                   2                   1                   0
  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  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  47  */
  48 
  49 #define CALIB_COEFFICIENT 1000000LL
  50 
  51 /**
  52  * div64_s64_precise() - wrapper for div64_s64()
  53  * @a:  the dividend
  54  * @b:  the divisor
  55  *
  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.
  59  *
  60  * Return: the quotient of a / b.
  61  */
  62 static s64 div64_s64_precise(s64 a, s32 b)
  63 {
  64         s64 r, al;
  65 
  66         /* Scale up for increased precision division */
  67         al = a << 16;
  68 
  69         r = div64_s64(al * 2 + 1, 2 * b);
  70         return r >> 16;
  71 }
  72 
  73 int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse,
  74                             struct tsensor_shared_calib *shared)
  75 {
  76         u32 val;
  77         s32 shifted_cp, shifted_ft;
  78         int err;
  79 
  80         err = tegra_fuse_readl(FUSE_TSENSOR_COMMON, &val);
  81         if (err)
  82                 return err;
  83 
  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;
  88 
  89         shifted_ft = (val & tfuse->fuse_shift_ft_mask) >>
  90                      tfuse->fuse_shift_ft_shift;
  91         shifted_ft = sign_extend32(shifted_ft, 4);
  92 
  93         if (tfuse->fuse_spare_realignment) {
  94                 err = tegra_fuse_readl(tfuse->fuse_spare_realignment, &val);
  95                 if (err)
  96                         return err;
  97         }
  98 
  99         shifted_cp = sign_extend32(val, 5);
 100 
 101         shared->actual_temp_cp = 2 * NOMINAL_CALIB_CP + shifted_cp;
 102         shared->actual_temp_ft = 2 * NOMINAL_CALIB_FT + shifted_ft;
 103 
 104         return 0;
 105 }
 106 
 107 int tegra_calc_tsensor_calib(const struct tegra_tsensor *sensor,
 108                              const struct tsensor_shared_calib *shared,
 109                              u32 *calibration)
 110 {
 111         const struct tegra_tsensor_group *sensor_group;
 112         u32 val, calib;
 113         s32 actual_tsensor_ft, actual_tsensor_cp;
 114         s32 delta_sens, delta_temp;
 115         s32 mult, div;
 116         s16 therma, thermb;
 117         s64 temp;
 118         int err;
 119 
 120         sensor_group = sensor->group;
 121 
 122         err = tegra_fuse_readl(sensor->calib_fuse_offset, &val);
 123         if (err)
 124                 return err;
 125 
 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);
 130 
 131         delta_sens = actual_tsensor_ft - actual_tsensor_cp;
 132         delta_temp = shared->actual_temp_ft - shared->actual_temp_cp;
 133 
 134         mult = sensor_group->pdiv * sensor->config->tsample_ate;
 135         div = sensor->config->tsample * sensor_group->pdiv_ate;
 136 
 137         temp = (s64)delta_temp * (1LL << 13) * mult;
 138         therma = div64_s64_precise(temp, (s64)delta_sens * div);
 139 
 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);
 143 
 144         temp = (s64)therma * sensor->fuse_corr_alpha;
 145         therma = div64_s64_precise(temp, CALIB_COEFFICIENT);
 146 
 147         temp = (s64)thermb * sensor->fuse_corr_alpha + sensor->fuse_corr_beta;
 148         thermb = div64_s64_precise(temp, CALIB_COEFFICIENT);
 149 
 150         calib = ((u16)therma << SENSOR_CONFIG2_THERMA_SHIFT) |
 151                 ((u16)thermb << SENSOR_CONFIG2_THERMB_SHIFT);
 152 
 153         *calibration = calib;
 154 
 155         return 0;
 156 }
 157 
 158 MODULE_AUTHOR("Wei Ni <wni@nvidia.com>");
 159 MODULE_DESCRIPTION("Tegra SOCTHERM fuse management");
 160 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */