Commit 65b6d57c authored by Wei Ni's avatar Wei Ni Committed by Eduardo Valentin

thermal: tegra: split tegra_soctherm driver

Split most of the Tegra124 data and code into a Tegra124-specific
file.
Split most of the fuse-related code into a fuse-related source file.
This is in preparation for adding a Tegra210-specific driver in a
future patch.

Beyond the maintainability improvements, this is intended to separate
chip-specific ATE and characterization-related hacks into chip-specific
files, in the hopes that they won't pollute code for other chips.
Signed-off-by: default avatarWei Ni <wni@nvidia.com>
Signed-off-by: default avatarEduardo Valentin <edubezval@gmail.com>
parent 1c3bdc16
......@@ -5,7 +5,7 @@ config TEGRA_SOCTHERM
tristate "Tegra SOCTHERM thermal management"
help
Enable this option for integrated thermal management support on NVIDIA
Tegra124 systems-on-chip. The driver supports four thermal zones
Tegra systems-on-chip. The driver supports four thermal zones
(CPU, GPU, MEM, PLLX). Cooling devices can be bound to the thermal
zones to manage temperatures. This option is also required for the
emergency thermal reset (thermtrip) feature to function.
......
obj-$(CONFIG_TEGRA_SOCTHERM) += tegra-soctherm.o
tegra-soctherm-y := soctherm.o soctherm-fuse.o
tegra-soctherm-$(CONFIG_ARCH_TEGRA_124_SOC) += tegra124-soctherm.o
/*
* Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#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
/*
* Tegra12x, etc:
* 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");
/*
* Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __DRIVERS_THERMAL_TEGRA_SOCTHERM_H
#define __DRIVERS_THERMAL_TEGRA_SOCTHERM_H
#define SENSOR_CONFIG2 8
#define SENSOR_CONFIG2_THERMA_SHIFT 16
#define SENSOR_CONFIG2_THERMB_SHIFT 0
#define SENSOR_PDIV 0x1c0
#define SENSOR_PDIV_CPU_MASK (0xf << 12)
#define SENSOR_PDIV_GPU_MASK (0xf << 8)
#define SENSOR_PDIV_MEM_MASK (0xf << 4)
#define SENSOR_PDIV_PLLX_MASK (0xf << 0)
#define SENSOR_HOTSPOT_OFF 0x1c4
#define SENSOR_HOTSPOT_CPU_MASK (0xff << 16)
#define SENSOR_HOTSPOT_GPU_MASK (0xff << 8)
#define SENSOR_HOTSPOT_MEM_MASK (0xff << 0)
#define SENSOR_TEMP1 0x1c8
#define SENSOR_TEMP1_CPU_TEMP_MASK (0xffff << 16)
#define SENSOR_TEMP1_GPU_TEMP_MASK 0xffff
#define SENSOR_TEMP2 0x1cc
#define SENSOR_TEMP2_MEM_TEMP_MASK (0xffff << 16)
#define SENSOR_TEMP2_PLLX_TEMP_MASK 0xffff
/**
* struct tegra_tsensor_group - SOC_THERM sensor group data
* @name: short name of the temperature sensor group
* @id: numeric ID of the temperature sensor group
* @sensor_temp_offset: offset of the SENSOR_TEMP* register
* @sensor_temp_mask: bit mask for this sensor group in SENSOR_TEMP* register
* @pdiv: the sensor count post-divider to use during runtime
* @pdiv_ate: the sensor count post-divider used during automated test
* @pdiv_mask: register bitfield mask for the PDIV field for this sensor
* @pllx_hotspot_diff: hotspot offset from the PLLX sensor, must be 0 for
PLLX sensor group
* @pllx_hotspot_mask: register bitfield mask for the HOTSPOT field
*/
struct tegra_tsensor_group {
const char *name;
u8 id;
u16 sensor_temp_offset;
u32 sensor_temp_mask;
u32 pdiv, pdiv_ate, pdiv_mask;
u32 pllx_hotspot_diff, pllx_hotspot_mask;
};
struct tegra_tsensor_configuration {
u32 tall, tiddq_en, ten_count, pdiv, pdiv_ate, tsample, tsample_ate;
};
struct tegra_tsensor {
const char *name;
const u32 base;
const struct tegra_tsensor_configuration *config;
const u32 calib_fuse_offset;
/*
* Correction values used to modify values read from
* calibration fuses
*/
const s32 fuse_corr_alpha, fuse_corr_beta;
const struct tegra_tsensor_group *group;
};
struct tegra_soctherm_fuse {
u32 fuse_base_cp_mask, fuse_base_cp_shift;
u32 fuse_base_ft_mask, fuse_base_ft_shift;
u32 fuse_shift_ft_mask, fuse_shift_ft_shift;
u32 fuse_spare_realignment;
};
struct tsensor_shared_calib {
u32 base_cp, base_ft;
u32 actual_temp_cp, actual_temp_ft;
};
struct tegra_soctherm_soc {
const struct tegra_tsensor *tsensors;
const unsigned int num_tsensors;
const struct tegra_tsensor_group **ttgs;
const unsigned int num_ttgs;
const struct tegra_soctherm_fuse *tfuse;
};
int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse,
struct tsensor_shared_calib *shared);
int tegra_calc_tsensor_calib(const struct tegra_tsensor *sensor,
const struct tsensor_shared_calib *shared,
u32 *calib);
#ifdef CONFIG_ARCH_TEGRA_124_SOC
extern const struct tegra_soctherm_soc tegra124_soctherm;
#endif
#endif
/*
* Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <dt-bindings/thermal/tegra124-soctherm.h>
#include "soctherm.h"
static const struct tegra_tsensor_configuration tegra124_tsensor_config = {
.tall = 16300,
.tiddq_en = 1,
.ten_count = 1,
.tsample = 120,
.tsample_ate = 480,
};
static const struct tegra_tsensor_group tegra124_tsensor_group_cpu = {
.id = TEGRA124_SOCTHERM_SENSOR_CPU,
.name = "cpu",
.sensor_temp_offset = SENSOR_TEMP1,
.sensor_temp_mask = SENSOR_TEMP1_CPU_TEMP_MASK,
.pdiv = 8,
.pdiv_ate = 8,
.pdiv_mask = SENSOR_PDIV_CPU_MASK,
.pllx_hotspot_diff = 10,
.pllx_hotspot_mask = SENSOR_HOTSPOT_CPU_MASK,
};
static const struct tegra_tsensor_group tegra124_tsensor_group_gpu = {
.id = TEGRA124_SOCTHERM_SENSOR_GPU,
.name = "gpu",
.sensor_temp_offset = SENSOR_TEMP1,
.sensor_temp_mask = SENSOR_TEMP1_GPU_TEMP_MASK,
.pdiv = 8,
.pdiv_ate = 8,
.pdiv_mask = SENSOR_PDIV_GPU_MASK,
.pllx_hotspot_diff = 5,
.pllx_hotspot_mask = SENSOR_HOTSPOT_GPU_MASK,
};
static const struct tegra_tsensor_group tegra124_tsensor_group_pll = {
.id = TEGRA124_SOCTHERM_SENSOR_PLLX,
.name = "pll",
.sensor_temp_offset = SENSOR_TEMP2,
.sensor_temp_mask = SENSOR_TEMP2_PLLX_TEMP_MASK,
.pdiv = 8,
.pdiv_ate = 8,
.pdiv_mask = SENSOR_PDIV_PLLX_MASK,
};
static const struct tegra_tsensor_group tegra124_tsensor_group_mem = {
.id = TEGRA124_SOCTHERM_SENSOR_MEM,
.name = "mem",
.sensor_temp_offset = SENSOR_TEMP2,
.sensor_temp_mask = SENSOR_TEMP2_MEM_TEMP_MASK,
.pdiv = 8,
.pdiv_ate = 8,
.pdiv_mask = SENSOR_PDIV_MEM_MASK,
.pllx_hotspot_diff = 0,
.pllx_hotspot_mask = SENSOR_HOTSPOT_MEM_MASK,
};
static const struct tegra_tsensor_group *tegra124_tsensor_groups[] = {
&tegra124_tsensor_group_cpu,
&tegra124_tsensor_group_gpu,
&tegra124_tsensor_group_pll,
&tegra124_tsensor_group_mem,
};
static const struct tegra_tsensor tegra124_tsensors[] = {
{
.name = "cpu0",
.base = 0xc0,
.config = &tegra124_tsensor_config,
.calib_fuse_offset = 0x098,
.fuse_corr_alpha = 1135400,
.fuse_corr_beta = -6266900,
.group = &tegra124_tsensor_group_cpu,
}, {
.name = "cpu1",
.base = 0xe0,
.config = &tegra124_tsensor_config,
.calib_fuse_offset = 0x084,
.fuse_corr_alpha = 1122220,
.fuse_corr_beta = -5700700,
.group = &tegra124_tsensor_group_cpu,
}, {
.name = "cpu2",
.base = 0x100,
.config = &tegra124_tsensor_config,
.calib_fuse_offset = 0x088,
.fuse_corr_alpha = 1127000,
.fuse_corr_beta = -6768200,
.group = &tegra124_tsensor_group_cpu,
}, {
.name = "cpu3",
.base = 0x120,
.config = &tegra124_tsensor_config,
.calib_fuse_offset = 0x12c,
.fuse_corr_alpha = 1110900,
.fuse_corr_beta = -6232000,
.group = &tegra124_tsensor_group_cpu,
}, {
.name = "mem0",
.base = 0x140,
.config = &tegra124_tsensor_config,
.calib_fuse_offset = 0x158,
.fuse_corr_alpha = 1122300,
.fuse_corr_beta = -5936400,
.group = &tegra124_tsensor_group_mem,
}, {
.name = "mem1",
.base = 0x160,
.config = &tegra124_tsensor_config,
.calib_fuse_offset = 0x15c,
.fuse_corr_alpha = 1145700,
.fuse_corr_beta = -7124600,
.group = &tegra124_tsensor_group_mem,
}, {
.name = "gpu",
.base = 0x180,
.config = &tegra124_tsensor_config,
.calib_fuse_offset = 0x154,
.fuse_corr_alpha = 1120100,
.fuse_corr_beta = -6000500,
.group = &tegra124_tsensor_group_gpu,
}, {
.name = "pllx",
.base = 0x1a0,
.config = &tegra124_tsensor_config,
.calib_fuse_offset = 0x160,
.fuse_corr_alpha = 1106500,
.fuse_corr_beta = -6729300,
.group = &tegra124_tsensor_group_pll,
},
};
/*
* Mask/shift bits in FUSE_TSENSOR_COMMON and
* FUSE_TSENSOR_COMMON, which are described in
* tegra_soctherm_fuse.c
*/
static const struct tegra_soctherm_fuse tegra124_soctherm_fuse = {
.fuse_base_cp_mask = 0x3ff,
.fuse_base_cp_shift = 0,
.fuse_base_ft_mask = 0x7ff << 10,
.fuse_base_ft_shift = 10,
.fuse_shift_ft_mask = 0x1f << 21,
.fuse_shift_ft_shift = 21,
.fuse_spare_realignment = 0x1fc,
};
const struct tegra_soctherm_soc tegra124_soctherm = {
.tsensors = tegra124_tsensors,
.num_tsensors = ARRAY_SIZE(tegra124_tsensors),
.ttgs = tegra124_tsensor_groups,
.num_ttgs = ARRAY_SIZE(tegra124_tsensor_groups),
.tfuse = &tegra124_soctherm_fuse,
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment