Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
# SPDX-License-Identifier: GPL-2.0-only
menu "NVIDIA Tegra thermal drivers"
depends on ARCH_TEGRA || COMPILE_TEST
config TEGRA_SOCTHERM
tristate "Tegra SOCTHERM thermal management"
help
Enable this option for integrated thermal management support on NVIDIA
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.
config TEGRA_BPMP_THERMAL
tristate "Tegra BPMP thermal sensing"
depends on TEGRA_BPMP || COMPILE_TEST
help
Enable this option for support for sensing system temperature of NVIDIA
Tegra systems-on-chip with the BPMP coprocessor (Tegra186).
config TEGRA30_TSENSOR
tristate "Tegra30 Thermal Sensor"
depends on ARCH_TEGRA_3x_SOC || COMPILE_TEST
help
Enable this option to support thermal management of NVIDIA Tegra30
system-on-chip.
endmenu
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TEGRA_SOCTHERM) += tegra-soctherm.o
obj-$(CONFIG_TEGRA_BPMP_THERMAL) += tegra-bpmp-thermal.o
obj-$(CONFIG_TEGRA30_TSENSOR) += tegra30-tsensor.o
tegra-soctherm-y := soctherm.o soctherm-fuse.o
tegra-soctherm-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114-soctherm.o
tegra-soctherm-$(CONFIG_ARCH_TEGRA_124_SOC) += tegra124-soctherm.o
tegra-soctherm-$(CONFIG_ARCH_TEGRA_132_SOC) += tegra132-soctherm.o
tegra-soctherm-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-soctherm.o
@@ -0,0 +1,166 @@
// 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_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
/*
* 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 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Tegra124:
* 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 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Tegra114: Layout of bits in FUSE_TSENSOR_COMMON aka FUSE_VSENSOR_CALIB:
* 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 | SHIFT_CP | BASE_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(tfuse->fuse_common_reg, &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 = (val & tfuse->fuse_shift_cp_mask) >>
tfuse->fuse_shift_cp_shift;
shifted_cp = sign_extend32(val, 5);
shared->actual_temp_cp = 2 * NOMINAL_CALIB_CP + shifted_cp;
shared->actual_temp_ft = 2 * tfuse->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");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,162 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* 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 THERMCTL_LEVEL0_GROUP_CPU 0x0
#define THERMCTL_LEVEL0_GROUP_GPU 0x4
#define THERMCTL_LEVEL0_GROUP_MEM 0x8
#define THERMCTL_LEVEL0_GROUP_TSENSE 0xc
#define SENSOR_CONFIG2 8
#define SENSOR_CONFIG2_THERMA_MASK (0xffff << 16)
#define SENSOR_CONFIG2_THERMA_SHIFT 16
#define SENSOR_CONFIG2_THERMB_MASK 0xffff
#define SENSOR_CONFIG2_THERMB_SHIFT 0
#define THERMCTL_THERMTRIP_CTL 0x80
/* BITs are defined in device file */
#define THERMCTL_INTR_ENABLE 0x88
#define THERMCTL_INTR_DISABLE 0x8c
#define TH_INTR_UP_DN_EN 0x3
#define THERM_IRQ_MEM_MASK (TH_INTR_UP_DN_EN << 24)
#define THERM_IRQ_GPU_MASK (TH_INTR_UP_DN_EN << 16)
#define THERM_IRQ_CPU_MASK (TH_INTR_UP_DN_EN << 8)
#define THERM_IRQ_TSENSE_MASK (TH_INTR_UP_DN_EN << 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
#define FUSE_VSENSOR_CALIB 0x08c
#define FUSE_TSENSOR_COMMON 0x180
/**
* 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;
u32 thermtrip_enable_mask;
u32 thermtrip_any_en_mask;
u32 thermtrip_threshold_mask;
u32 thermctl_isr_mask;
u16 thermctl_lvl0_offset;
u32 thermctl_lvl0_up_thresh_mask;
u32 thermctl_lvl0_dn_thresh_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 tsensor_group_thermtrips {
u8 id;
u32 temp;
};
struct tegra_soctherm_fuse {
u32 fuse_base_cp_mask, fuse_base_cp_shift;
u32 fuse_shift_cp_mask, fuse_shift_cp_shift;
u32 fuse_base_ft_mask, fuse_base_ft_shift;
u32 fuse_shift_ft_mask, fuse_shift_ft_shift;
u32 fuse_common_reg, fuse_spare_realignment;
u32 nominal_calib_ft;
};
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;
const int thresh_grain;
const unsigned int bptt;
const bool use_ccroc;
struct tsensor_group_thermtrips *thermtrips;
};
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_114_SOC
extern const struct tegra_soctherm_soc tegra114_soctherm;
#endif
#ifdef CONFIG_ARCH_TEGRA_124_SOC
extern const struct tegra_soctherm_soc tegra124_soctherm;
#endif
#ifdef CONFIG_ARCH_TEGRA_132_SOC
extern const struct tegra_soctherm_soc tegra132_soctherm;
#endif
#ifdef CONFIG_ARCH_TEGRA_210_SOC
extern const struct tegra_soctherm_soc tegra210_soctherm;
#endif
#endif
@@ -0,0 +1,328 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2017, NVIDIA CORPORATION. All rights reserved.
*
* Author:
* Mikko Perttunen <mperttunen@nvidia.com>
* Aapo Vienamo <avienamo@nvidia.com>
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/thermal.h>
#include <linux/workqueue.h>
#include <soc/tegra/bpmp.h>
#include <soc/tegra/bpmp-abi.h>
struct tegra_bpmp_thermal_zone {
struct tegra_bpmp_thermal *tegra;
struct thermal_zone_device *tzd;
struct work_struct tz_device_update_work;
unsigned int idx;
};
struct tegra_bpmp_thermal {
struct device *dev;
struct tegra_bpmp *bpmp;
unsigned int num_zones;
struct tegra_bpmp_thermal_zone **zones;
};
static int __tegra_bpmp_thermal_get_temp(struct tegra_bpmp_thermal_zone *zone,
int *out_temp)
{
struct mrq_thermal_host_to_bpmp_request req;
union mrq_thermal_bpmp_to_host_response reply;
struct tegra_bpmp_message msg;
int err;
memset(&req, 0, sizeof(req));
req.type = CMD_THERMAL_GET_TEMP;
req.get_temp.zone = zone->idx;
memset(&msg, 0, sizeof(msg));
msg.mrq = MRQ_THERMAL;
msg.tx.data = &req;
msg.tx.size = sizeof(req);
msg.rx.data = &reply;
msg.rx.size = sizeof(reply);
err = tegra_bpmp_transfer(zone->tegra->bpmp, &msg);
if (err)
return err;
if (msg.rx.ret == -BPMP_EFAULT)
return -EAGAIN;
if (msg.rx.ret)
return -EINVAL;
*out_temp = reply.get_temp.temp;
return 0;
}
static int tegra_bpmp_thermal_get_temp(struct thermal_zone_device *tz, int *out_temp)
{
struct tegra_bpmp_thermal_zone *zone = thermal_zone_device_priv(tz);
return __tegra_bpmp_thermal_get_temp(zone, out_temp);
}
static int tegra_bpmp_thermal_set_trips(struct thermal_zone_device *tz, int low, int high)
{
struct tegra_bpmp_thermal_zone *zone = thermal_zone_device_priv(tz);
struct mrq_thermal_host_to_bpmp_request req;
struct tegra_bpmp_message msg;
int err;
memset(&req, 0, sizeof(req));
req.type = CMD_THERMAL_SET_TRIP;
req.set_trip.zone = zone->idx;
req.set_trip.enabled = true;
req.set_trip.low = low;
req.set_trip.high = high;
memset(&msg, 0, sizeof(msg));
msg.mrq = MRQ_THERMAL;
msg.tx.data = &req;
msg.tx.size = sizeof(req);
err = tegra_bpmp_transfer(zone->tegra->bpmp, &msg);
if (err)
return err;
if (msg.rx.ret)
return -EINVAL;
return 0;
}
static void tz_device_update_work_fn(struct work_struct *work)
{
struct tegra_bpmp_thermal_zone *zone;
zone = container_of(work, struct tegra_bpmp_thermal_zone,
tz_device_update_work);
thermal_zone_device_update(zone->tzd, THERMAL_TRIP_VIOLATED);
}
static void bpmp_mrq_thermal(unsigned int mrq, struct tegra_bpmp_channel *ch,
void *data)
{
struct mrq_thermal_bpmp_to_host_request req;
struct tegra_bpmp_thermal *tegra = data;
size_t offset;
int i;
offset = offsetof(struct tegra_bpmp_mb_data, data);
iosys_map_memcpy_from(&req, &ch->ib, offset, sizeof(req));
if (req.type != CMD_THERMAL_HOST_TRIP_REACHED) {
dev_err(tegra->dev, "%s: invalid request type: %d\n", __func__, req.type);
tegra_bpmp_mrq_return(ch, -EINVAL, NULL, 0);
return;
}
for (i = 0; i < tegra->num_zones; ++i) {
if (tegra->zones[i]->idx != req.host_trip_reached.zone)
continue;
schedule_work(&tegra->zones[i]->tz_device_update_work);
tegra_bpmp_mrq_return(ch, 0, NULL, 0);
return;
}
dev_err(tegra->dev, "%s: invalid thermal zone: %d\n", __func__,
req.host_trip_reached.zone);
tegra_bpmp_mrq_return(ch, -EINVAL, NULL, 0);
}
static int tegra_bpmp_thermal_get_num_zones(struct tegra_bpmp *bpmp,
int *num_zones)
{
struct mrq_thermal_host_to_bpmp_request req;
union mrq_thermal_bpmp_to_host_response reply;
struct tegra_bpmp_message msg;
int err;
memset(&req, 0, sizeof(req));
req.type = CMD_THERMAL_GET_NUM_ZONES;
memset(&msg, 0, sizeof(msg));
msg.mrq = MRQ_THERMAL;
msg.tx.data = &req;
msg.tx.size = sizeof(req);
msg.rx.data = &reply;
msg.rx.size = sizeof(reply);
err = tegra_bpmp_transfer(bpmp, &msg);
if (err)
return err;
if (msg.rx.ret)
return -EINVAL;
*num_zones = reply.get_num_zones.num;
return 0;
}
static int tegra_bpmp_thermal_trips_supported(struct tegra_bpmp *bpmp, bool *supported)
{
struct mrq_thermal_host_to_bpmp_request req;
union mrq_thermal_bpmp_to_host_response reply;
struct tegra_bpmp_message msg;
int err;
memset(&req, 0, sizeof(req));
req.type = CMD_THERMAL_QUERY_ABI;
req.query_abi.type = CMD_THERMAL_SET_TRIP;
memset(&msg, 0, sizeof(msg));
msg.mrq = MRQ_THERMAL;
msg.tx.data = &req;
msg.tx.size = sizeof(req);
msg.rx.data = &reply;
msg.rx.size = sizeof(reply);
err = tegra_bpmp_transfer(bpmp, &msg);
if (err)
return err;
if (msg.rx.ret == 0) {
*supported = true;
return 0;
} else if (msg.rx.ret == -BPMP_ENODEV) {
*supported = false;
return 0;
} else {
return -EINVAL;
}
}
static const struct thermal_zone_device_ops tegra_bpmp_of_thermal_ops = {
.get_temp = tegra_bpmp_thermal_get_temp,
.set_trips = tegra_bpmp_thermal_set_trips,
};
static const struct thermal_zone_device_ops tegra_bpmp_of_thermal_ops_notrips = {
.get_temp = tegra_bpmp_thermal_get_temp,
};
static int tegra_bpmp_thermal_probe(struct platform_device *pdev)
{
struct tegra_bpmp *bpmp = dev_get_drvdata(pdev->dev.parent);
const struct thermal_zone_device_ops *thermal_ops;
struct tegra_bpmp_thermal *tegra;
struct thermal_zone_device *tzd;
unsigned int i, max_num_zones;
bool supported;
int err;
err = tegra_bpmp_thermal_trips_supported(bpmp, &supported);
if (err) {
dev_err(&pdev->dev, "failed to determine if trip points are supported\n");
return err;
}
if (supported)
thermal_ops = &tegra_bpmp_of_thermal_ops;
else
thermal_ops = &tegra_bpmp_of_thermal_ops_notrips;
tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
if (!tegra)
return -ENOMEM;
tegra->dev = &pdev->dev;
tegra->bpmp = bpmp;
err = tegra_bpmp_thermal_get_num_zones(bpmp, &max_num_zones);
if (err) {
dev_err(&pdev->dev, "failed to get the number of zones: %d\n",
err);
return err;
}
tegra->zones = devm_kcalloc(&pdev->dev, max_num_zones,
sizeof(*tegra->zones), GFP_KERNEL);
if (!tegra->zones)
return -ENOMEM;
for (i = 0; i < max_num_zones; ++i) {
struct tegra_bpmp_thermal_zone *zone;
int temp;
zone = devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL);
if (!zone)
return -ENOMEM;
zone->idx = i;
zone->tegra = tegra;
err = __tegra_bpmp_thermal_get_temp(zone, &temp);
/*
* Sensors in powergated domains may temporarily fail to be read
* (-EAGAIN), but will become accessible when the domain is powered on.
*/
if (err < 0 && err != -EAGAIN) {
devm_kfree(&pdev->dev, zone);
continue;
}
tzd = devm_thermal_of_zone_register(
&pdev->dev, i, zone, thermal_ops);
if (IS_ERR(tzd)) {
if (PTR_ERR(tzd) == -EPROBE_DEFER)
return -EPROBE_DEFER;
devm_kfree(&pdev->dev, zone);
continue;
}
zone->tzd = tzd;
INIT_WORK(&zone->tz_device_update_work,
tz_device_update_work_fn);
tegra->zones[tegra->num_zones++] = zone;
}
err = tegra_bpmp_request_mrq(bpmp, MRQ_THERMAL, bpmp_mrq_thermal,
tegra);
if (err) {
dev_err(&pdev->dev, "failed to register mrq handler: %d\n",
err);
return err;
}
platform_set_drvdata(pdev, tegra);
return 0;
}
static void tegra_bpmp_thermal_remove(struct platform_device *pdev)
{
struct tegra_bpmp_thermal *tegra = platform_get_drvdata(pdev);
tegra_bpmp_free_mrq(tegra->bpmp, MRQ_THERMAL, tegra);
}
static const struct of_device_id tegra_bpmp_thermal_of_match[] = {
{ .compatible = "nvidia,tegra186-bpmp-thermal" },
{ },
};
MODULE_DEVICE_TABLE(of, tegra_bpmp_thermal_of_match);
static struct platform_driver tegra_bpmp_thermal_driver = {
.probe = tegra_bpmp_thermal_probe,
.remove = tegra_bpmp_thermal_remove,
.driver = {
.name = "tegra-bpmp-thermal",
.of_match_table = tegra_bpmp_thermal_of_match,
},
};
module_platform_driver(tegra_bpmp_thermal_driver);
MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra BPMP thermal sensor driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,209 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2024, Svyatoslav Ryhel <clamor95@gmail.com>
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <dt-bindings/thermal/tegra114-soctherm.h>
#include "soctherm.h"
#define TEGRA114_THERMTRIP_ANY_EN_MASK (0x1 << 28)
#define TEGRA114_THERMTRIP_MEM_EN_MASK (0x1 << 27)
#define TEGRA114_THERMTRIP_GPU_EN_MASK (0x1 << 26)
#define TEGRA114_THERMTRIP_CPU_EN_MASK (0x1 << 25)
#define TEGRA114_THERMTRIP_TSENSE_EN_MASK (0x1 << 24)
#define TEGRA114_THERMTRIP_GPUMEM_THRESH_MASK (0xff << 16)
#define TEGRA114_THERMTRIP_CPU_THRESH_MASK (0xff << 8)
#define TEGRA114_THERMTRIP_TSENSE_THRESH_MASK 0xff
#define TEGRA114_THERMCTL_LVL0_UP_THRESH_MASK (0xff << 17)
#define TEGRA114_THERMCTL_LVL0_DN_THRESH_MASK (0xff << 9)
#define TEGRA114_THRESH_GRAIN 1000
#define TEGRA114_BPTT 8
static const struct tegra_tsensor_configuration tegra114_tsensor_config = {
.tall = 16300,
.tiddq_en = 1,
.ten_count = 1,
.tsample = 163,
.tsample_ate = 655,
};
static const struct tegra_tsensor_group tegra114_tsensor_group_cpu = {
.id = TEGRA114_SOCTHERM_SENSOR_CPU,
.name = "cpu",
.sensor_temp_offset = SENSOR_TEMP1,
.sensor_temp_mask = SENSOR_TEMP1_CPU_TEMP_MASK,
.pdiv = 10,
.pdiv_ate = 10,
.pdiv_mask = SENSOR_PDIV_CPU_MASK,
.pllx_hotspot_diff = 6,
.pllx_hotspot_mask = SENSOR_HOTSPOT_CPU_MASK,
.thermtrip_any_en_mask = TEGRA114_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA114_THERMTRIP_CPU_EN_MASK,
.thermtrip_threshold_mask = TEGRA114_THERMTRIP_CPU_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_CPU_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU,
.thermctl_lvl0_up_thresh_mask = TEGRA114_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA114_THERMCTL_LVL0_DN_THRESH_MASK,
};
static const struct tegra_tsensor_group tegra114_tsensor_group_gpu = {
.id = TEGRA114_SOCTHERM_SENSOR_GPU,
.name = "gpu",
.sensor_temp_offset = SENSOR_TEMP1,
.sensor_temp_mask = SENSOR_TEMP1_GPU_TEMP_MASK,
.pdiv = 10,
.pdiv_ate = 10,
.pdiv_mask = SENSOR_PDIV_GPU_MASK,
.pllx_hotspot_diff = 6,
.pllx_hotspot_mask = SENSOR_HOTSPOT_GPU_MASK,
.thermtrip_any_en_mask = TEGRA114_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA114_THERMTRIP_GPU_EN_MASK,
.thermtrip_threshold_mask = TEGRA114_THERMTRIP_GPUMEM_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_GPU_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU,
.thermctl_lvl0_up_thresh_mask = TEGRA114_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA114_THERMCTL_LVL0_DN_THRESH_MASK,
};
static const struct tegra_tsensor_group tegra114_tsensor_group_pll = {
.id = TEGRA114_SOCTHERM_SENSOR_PLLX,
.name = "pll",
.sensor_temp_offset = SENSOR_TEMP2,
.sensor_temp_mask = SENSOR_TEMP2_PLLX_TEMP_MASK,
.pdiv = 10,
.pdiv_ate = 10,
.pdiv_mask = SENSOR_PDIV_PLLX_MASK,
.thermtrip_any_en_mask = TEGRA114_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA114_THERMTRIP_TSENSE_EN_MASK,
.thermtrip_threshold_mask = TEGRA114_THERMTRIP_TSENSE_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_TSENSE_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE,
.thermctl_lvl0_up_thresh_mask = TEGRA114_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA114_THERMCTL_LVL0_DN_THRESH_MASK,
};
static const struct tegra_tsensor_group tegra114_tsensor_group_mem = {
.id = TEGRA114_SOCTHERM_SENSOR_MEM,
.name = "mem",
.sensor_temp_offset = SENSOR_TEMP2,
.sensor_temp_mask = SENSOR_TEMP2_MEM_TEMP_MASK,
.pdiv = 10,
.pdiv_ate = 10,
.pdiv_mask = SENSOR_PDIV_MEM_MASK,
.pllx_hotspot_diff = 0,
.pllx_hotspot_mask = SENSOR_HOTSPOT_MEM_MASK,
.thermtrip_any_en_mask = TEGRA114_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA114_THERMTRIP_MEM_EN_MASK,
.thermtrip_threshold_mask = TEGRA114_THERMTRIP_GPUMEM_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_MEM_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM,
.thermctl_lvl0_up_thresh_mask = TEGRA114_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA114_THERMCTL_LVL0_DN_THRESH_MASK,
};
static const struct tegra_tsensor_group *tegra114_tsensor_groups[] = {
&tegra114_tsensor_group_cpu,
&tegra114_tsensor_group_gpu,
&tegra114_tsensor_group_pll,
&tegra114_tsensor_group_mem,
};
static const struct tegra_tsensor tegra114_tsensors[] = {
{
.name = "cpu0",
.base = 0xc0,
.config = &tegra114_tsensor_config,
.calib_fuse_offset = 0x098,
.fuse_corr_alpha = 1196400,
.fuse_corr_beta = -13600000,
.group = &tegra114_tsensor_group_cpu,
}, {
.name = "cpu1",
.base = 0xe0,
.config = &tegra114_tsensor_config,
.calib_fuse_offset = 0x084,
.fuse_corr_alpha = 1196400,
.fuse_corr_beta = -13600000,
.group = &tegra114_tsensor_group_cpu,
}, {
.name = "cpu2",
.base = 0x100,
.config = &tegra114_tsensor_config,
.calib_fuse_offset = 0x088,
.fuse_corr_alpha = 1196400,
.fuse_corr_beta = -13600000,
.group = &tegra114_tsensor_group_cpu,
}, {
.name = "cpu3",
.base = 0x120,
.config = &tegra114_tsensor_config,
.calib_fuse_offset = 0x12c,
.fuse_corr_alpha = 1196400,
.fuse_corr_beta = -13600000,
.group = &tegra114_tsensor_group_cpu,
}, {
.name = "mem0",
.base = 0x140,
.config = &tegra114_tsensor_config,
.calib_fuse_offset = 0x158,
.fuse_corr_alpha = 1000000,
.fuse_corr_beta = 0,
.group = &tegra114_tsensor_group_mem,
}, {
.name = "mem1",
.base = 0x160,
.config = &tegra114_tsensor_config,
.calib_fuse_offset = 0x15c,
.fuse_corr_alpha = 1000000,
.fuse_corr_beta = 0,
.group = &tegra114_tsensor_group_mem,
}, {
.name = "gpu",
.base = 0x180,
.config = &tegra114_tsensor_config,
.calib_fuse_offset = 0x154,
.fuse_corr_alpha = 1124500,
.fuse_corr_beta = -9793100,
.group = &tegra114_tsensor_group_gpu,
}, {
.name = "pllx",
.base = 0x1a0,
.config = &tegra114_tsensor_config,
.calib_fuse_offset = 0x160,
.fuse_corr_alpha = 1224200,
.fuse_corr_beta = -14665000,
.group = &tegra114_tsensor_group_pll,
},
};
static const struct tegra_soctherm_fuse tegra114_soctherm_fuse = {
.fuse_base_cp_mask = 0x3ff,
.fuse_base_cp_shift = 0,
.fuse_shift_cp_mask = 0x3f << 10,
.fuse_shift_cp_shift = 10,
.fuse_base_ft_mask = 0x7ff << 16,
.fuse_base_ft_shift = 16,
.fuse_shift_ft_mask = 0x1f << 27,
.fuse_shift_ft_shift = 27,
.fuse_common_reg = FUSE_VSENSOR_CALIB,
.fuse_spare_realignment = 0,
.nominal_calib_ft = 90,
};
const struct tegra_soctherm_soc tegra114_soctherm = {
.tsensors = tegra114_tsensors,
.num_tsensors = ARRAY_SIZE(tegra114_tsensors),
.ttgs = tegra114_tsensor_groups,
.num_ttgs = ARRAY_SIZE(tegra114_tsensor_groups),
.tfuse = &tegra114_soctherm_fuse,
.thresh_grain = TEGRA114_THRESH_GRAIN,
.bptt = TEGRA114_BPTT,
.use_ccroc = false,
};
@@ -0,0 +1,223 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014-2018, 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"
#define TEGRA124_THERMTRIP_ANY_EN_MASK (0x1 << 28)
#define TEGRA124_THERMTRIP_MEM_EN_MASK (0x1 << 27)
#define TEGRA124_THERMTRIP_GPU_EN_MASK (0x1 << 26)
#define TEGRA124_THERMTRIP_CPU_EN_MASK (0x1 << 25)
#define TEGRA124_THERMTRIP_TSENSE_EN_MASK (0x1 << 24)
#define TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK (0xff << 16)
#define TEGRA124_THERMTRIP_CPU_THRESH_MASK (0xff << 8)
#define TEGRA124_THERMTRIP_TSENSE_THRESH_MASK 0xff
#define TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK (0xff << 17)
#define TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK (0xff << 9)
#define TEGRA124_THRESH_GRAIN 1000
#define TEGRA124_BPTT 8
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,
.thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA124_THERMTRIP_CPU_EN_MASK,
.thermtrip_threshold_mask = TEGRA124_THERMTRIP_CPU_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_CPU_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU,
.thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_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,
.thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA124_THERMTRIP_GPU_EN_MASK,
.thermtrip_threshold_mask = TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_GPU_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU,
.thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_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,
.thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA124_THERMTRIP_TSENSE_EN_MASK,
.thermtrip_threshold_mask = TEGRA124_THERMTRIP_TSENSE_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_TSENSE_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE,
.thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_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,
.thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA124_THERMTRIP_MEM_EN_MASK,
.thermtrip_threshold_mask = TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_MEM_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM,
.thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_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_shift_cp_mask = 0x3f,
.fuse_shift_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_common_reg = FUSE_TSENSOR_COMMON,
.fuse_spare_realignment = 0x1fc,
.nominal_calib_ft = 105,
};
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,
.thresh_grain = TEGRA124_THRESH_GRAIN,
.bptt = TEGRA124_BPTT,
.use_ccroc = false,
};
@@ -0,0 +1,223 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014-2018, 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"
#define TEGRA132_THERMTRIP_ANY_EN_MASK (0x1 << 28)
#define TEGRA132_THERMTRIP_MEM_EN_MASK (0x1 << 27)
#define TEGRA132_THERMTRIP_GPU_EN_MASK (0x1 << 26)
#define TEGRA132_THERMTRIP_CPU_EN_MASK (0x1 << 25)
#define TEGRA132_THERMTRIP_TSENSE_EN_MASK (0x1 << 24)
#define TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK (0xff << 16)
#define TEGRA132_THERMTRIP_CPU_THRESH_MASK (0xff << 8)
#define TEGRA132_THERMTRIP_TSENSE_THRESH_MASK 0xff
#define TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK (0xff << 17)
#define TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK (0xff << 9)
#define TEGRA132_THRESH_GRAIN 1000
#define TEGRA132_BPTT 8
static const struct tegra_tsensor_configuration tegra132_tsensor_config = {
.tall = 16300,
.tiddq_en = 1,
.ten_count = 1,
.tsample = 120,
.tsample_ate = 480,
};
static const struct tegra_tsensor_group tegra132_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,
.thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA132_THERMTRIP_CPU_EN_MASK,
.thermtrip_threshold_mask = TEGRA132_THERMTRIP_CPU_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_CPU_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU,
.thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
};
static const struct tegra_tsensor_group tegra132_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,
.thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA132_THERMTRIP_GPU_EN_MASK,
.thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_GPU_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU,
.thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
};
static const struct tegra_tsensor_group tegra132_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,
.thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA132_THERMTRIP_TSENSE_EN_MASK,
.thermtrip_threshold_mask = TEGRA132_THERMTRIP_TSENSE_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_TSENSE_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE,
.thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
};
static const struct tegra_tsensor_group tegra132_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,
.thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA132_THERMTRIP_MEM_EN_MASK,
.thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_MEM_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM,
.thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
};
static const struct tegra_tsensor_group *tegra132_tsensor_groups[] = {
&tegra132_tsensor_group_cpu,
&tegra132_tsensor_group_gpu,
&tegra132_tsensor_group_pll,
&tegra132_tsensor_group_mem,
};
static struct tegra_tsensor tegra132_tsensors[] = {
{
.name = "cpu0",
.base = 0xc0,
.config = &tegra132_tsensor_config,
.calib_fuse_offset = 0x098,
.fuse_corr_alpha = 1126600,
.fuse_corr_beta = -9433500,
.group = &tegra132_tsensor_group_cpu,
}, {
.name = "cpu1",
.base = 0xe0,
.config = &tegra132_tsensor_config,
.calib_fuse_offset = 0x084,
.fuse_corr_alpha = 1110800,
.fuse_corr_beta = -7383000,
.group = &tegra132_tsensor_group_cpu,
}, {
.name = "cpu2",
.base = 0x100,
.config = &tegra132_tsensor_config,
.calib_fuse_offset = 0x088,
.fuse_corr_alpha = 1113800,
.fuse_corr_beta = -6215200,
.group = &tegra132_tsensor_group_cpu,
}, {
.name = "cpu3",
.base = 0x120,
.config = &tegra132_tsensor_config,
.calib_fuse_offset = 0x12c,
.fuse_corr_alpha = 1129600,
.fuse_corr_beta = -8196100,
.group = &tegra132_tsensor_group_cpu,
}, {
.name = "mem0",
.base = 0x140,
.config = &tegra132_tsensor_config,
.calib_fuse_offset = 0x158,
.fuse_corr_alpha = 1132900,
.fuse_corr_beta = -6755300,
.group = &tegra132_tsensor_group_mem,
}, {
.name = "mem1",
.base = 0x160,
.config = &tegra132_tsensor_config,
.calib_fuse_offset = 0x15c,
.fuse_corr_alpha = 1142300,
.fuse_corr_beta = -7374200,
.group = &tegra132_tsensor_group_mem,
}, {
.name = "gpu",
.base = 0x180,
.config = &tegra132_tsensor_config,
.calib_fuse_offset = 0x154,
.fuse_corr_alpha = 1125100,
.fuse_corr_beta = -6350400,
.group = &tegra132_tsensor_group_gpu,
}, {
.name = "pllx",
.base = 0x1a0,
.config = &tegra132_tsensor_config,
.calib_fuse_offset = 0x160,
.fuse_corr_alpha = 1118100,
.fuse_corr_beta = -8208800,
.group = &tegra132_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 tegra132_soctherm_fuse = {
.fuse_base_cp_mask = 0x3ff,
.fuse_base_cp_shift = 0,
.fuse_shift_cp_mask = 0x3f,
.fuse_shift_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_common_reg = FUSE_TSENSOR_COMMON,
.fuse_spare_realignment = 0x1fc,
.nominal_calib_ft = 105,
};
const struct tegra_soctherm_soc tegra132_soctherm = {
.tsensors = tegra132_tsensors,
.num_tsensors = ARRAY_SIZE(tegra132_tsensors),
.ttgs = tegra132_tsensor_groups,
.num_ttgs = ARRAY_SIZE(tegra132_tsensor_groups),
.tfuse = &tegra132_soctherm_fuse,
.thresh_grain = TEGRA132_THRESH_GRAIN,
.bptt = TEGRA132_BPTT,
.use_ccroc = true,
};
@@ -0,0 +1,232 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014-2018, 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 <dt-bindings/thermal/tegra124-soctherm.h>
#include "soctherm.h"
#define TEGRA210_THERMTRIP_ANY_EN_MASK (0x1 << 31)
#define TEGRA210_THERMTRIP_MEM_EN_MASK (0x1 << 30)
#define TEGRA210_THERMTRIP_GPU_EN_MASK (0x1 << 29)
#define TEGRA210_THERMTRIP_CPU_EN_MASK (0x1 << 28)
#define TEGRA210_THERMTRIP_TSENSE_EN_MASK (0x1 << 27)
#define TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK (0x1ff << 18)
#define TEGRA210_THERMTRIP_CPU_THRESH_MASK (0x1ff << 9)
#define TEGRA210_THERMTRIP_TSENSE_THRESH_MASK 0x1ff
#define TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK (0x1ff << 18)
#define TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK (0x1ff << 9)
#define TEGRA210_THRESH_GRAIN 500
#define TEGRA210_BPTT 9
static const struct tegra_tsensor_configuration tegra210_tsensor_config = {
.tall = 16300,
.tiddq_en = 1,
.ten_count = 1,
.tsample = 120,
.tsample_ate = 480,
};
static const struct tegra_tsensor_group tegra210_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,
.thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA210_THERMTRIP_CPU_EN_MASK,
.thermtrip_threshold_mask = TEGRA210_THERMTRIP_CPU_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_CPU_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU,
.thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
};
static const struct tegra_tsensor_group tegra210_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,
.thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA210_THERMTRIP_GPU_EN_MASK,
.thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_GPU_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU,
.thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
};
static const struct tegra_tsensor_group tegra210_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,
.thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA210_THERMTRIP_TSENSE_EN_MASK,
.thermtrip_threshold_mask = TEGRA210_THERMTRIP_TSENSE_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_TSENSE_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE,
.thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
};
static const struct tegra_tsensor_group tegra210_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,
.thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA210_THERMTRIP_MEM_EN_MASK,
.thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_MEM_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM,
.thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
};
static const struct tegra_tsensor_group *tegra210_tsensor_groups[] = {
&tegra210_tsensor_group_cpu,
&tegra210_tsensor_group_gpu,
&tegra210_tsensor_group_pll,
&tegra210_tsensor_group_mem,
};
static const struct tegra_tsensor tegra210_tsensors[] = {
{
.name = "cpu0",
.base = 0xc0,
.config = &tegra210_tsensor_config,
.calib_fuse_offset = 0x098,
.fuse_corr_alpha = 1085000,
.fuse_corr_beta = 3244200,
.group = &tegra210_tsensor_group_cpu,
}, {
.name = "cpu1",
.base = 0xe0,
.config = &tegra210_tsensor_config,
.calib_fuse_offset = 0x084,
.fuse_corr_alpha = 1126200,
.fuse_corr_beta = -67500,
.group = &tegra210_tsensor_group_cpu,
}, {
.name = "cpu2",
.base = 0x100,
.config = &tegra210_tsensor_config,
.calib_fuse_offset = 0x088,
.fuse_corr_alpha = 1098400,
.fuse_corr_beta = 2251100,
.group = &tegra210_tsensor_group_cpu,
}, {
.name = "cpu3",
.base = 0x120,
.config = &tegra210_tsensor_config,
.calib_fuse_offset = 0x12c,
.fuse_corr_alpha = 1108000,
.fuse_corr_beta = 602700,
.group = &tegra210_tsensor_group_cpu,
}, {
.name = "mem0",
.base = 0x140,
.config = &tegra210_tsensor_config,
.calib_fuse_offset = 0x158,
.fuse_corr_alpha = 1069200,
.fuse_corr_beta = 3549900,
.group = &tegra210_tsensor_group_mem,
}, {
.name = "mem1",
.base = 0x160,
.config = &tegra210_tsensor_config,
.calib_fuse_offset = 0x15c,
.fuse_corr_alpha = 1173700,
.fuse_corr_beta = -6263600,
.group = &tegra210_tsensor_group_mem,
}, {
.name = "gpu",
.base = 0x180,
.config = &tegra210_tsensor_config,
.calib_fuse_offset = 0x154,
.fuse_corr_alpha = 1074300,
.fuse_corr_beta = 2734900,
.group = &tegra210_tsensor_group_gpu,
}, {
.name = "pllx",
.base = 0x1a0,
.config = &tegra210_tsensor_config,
.calib_fuse_offset = 0x160,
.fuse_corr_alpha = 1039700,
.fuse_corr_beta = 6829100,
.group = &tegra210_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 tegra210_soctherm_fuse = {
.fuse_base_cp_mask = 0x3ff << 11,
.fuse_base_cp_shift = 11,
.fuse_shift_cp_mask = 0x3f,
.fuse_shift_cp_shift = 0,
.fuse_base_ft_mask = 0x7ff << 21,
.fuse_base_ft_shift = 21,
.fuse_shift_ft_mask = 0x1f << 6,
.fuse_shift_ft_shift = 6,
.fuse_common_reg = FUSE_TSENSOR_COMMON,
.fuse_spare_realignment = 0,
.nominal_calib_ft = 105,
};
static struct tsensor_group_thermtrips tegra210_tsensor_thermtrips[] = {
{.id = TEGRA124_SOCTHERM_SENSOR_NUM},
{.id = TEGRA124_SOCTHERM_SENSOR_NUM},
{.id = TEGRA124_SOCTHERM_SENSOR_NUM},
{.id = TEGRA124_SOCTHERM_SENSOR_NUM},
};
const struct tegra_soctherm_soc tegra210_soctherm = {
.tsensors = tegra210_tsensors,
.num_tsensors = ARRAY_SIZE(tegra210_tsensors),
.ttgs = tegra210_tsensor_groups,
.num_ttgs = ARRAY_SIZE(tegra210_tsensor_groups),
.tfuse = &tegra210_soctherm_fuse,
.thresh_grain = TEGRA210_THRESH_GRAIN,
.bptt = TEGRA210_BPTT,
.use_ccroc = false,
.thermtrips = tegra210_tsensor_thermtrips,
};
@@ -0,0 +1,678 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Tegra30 SoC Thermal Sensor driver
*
* Based on downstream HWMON driver from NVIDIA.
* Copyright (C) 2011 NVIDIA Corporation
*
* Author: Dmitry Osipenko <digetx@gmail.com>
* Copyright (C) 2021 GRATE-DRIVER project
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/math.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/thermal.h>
#include <linux/types.h>
#include <soc/tegra/fuse.h>
#include "../thermal_hwmon.h"
#define TSENSOR_SENSOR0_CONFIG0 0x0
#define TSENSOR_SENSOR0_CONFIG0_SENSOR_STOP BIT(0)
#define TSENSOR_SENSOR0_CONFIG0_HW_FREQ_DIV_EN BIT(1)
#define TSENSOR_SENSOR0_CONFIG0_THERMAL_RST_EN BIT(2)
#define TSENSOR_SENSOR0_CONFIG0_DVFS_EN BIT(3)
#define TSENSOR_SENSOR0_CONFIG0_INTR_OVERFLOW_EN BIT(4)
#define TSENSOR_SENSOR0_CONFIG0_INTR_HW_FREQ_DIV_EN BIT(5)
#define TSENSOR_SENSOR0_CONFIG0_INTR_THERMAL_RST_EN BIT(6)
#define TSENSOR_SENSOR0_CONFIG0_M GENMASK(23, 8)
#define TSENSOR_SENSOR0_CONFIG0_N GENMASK(31, 24)
#define TSENSOR_SENSOR0_CONFIG1 0x8
#define TSENSOR_SENSOR0_CONFIG1_TH1 GENMASK(15, 0)
#define TSENSOR_SENSOR0_CONFIG1_TH2 GENMASK(31, 16)
#define TSENSOR_SENSOR0_CONFIG2 0xc
#define TSENSOR_SENSOR0_CONFIG2_TH3 GENMASK(15, 0)
#define TSENSOR_SENSOR0_STATUS0 0x18
#define TSENSOR_SENSOR0_STATUS0_STATE GENMASK(2, 0)
#define TSENSOR_SENSOR0_STATUS0_INTR BIT(8)
#define TSENSOR_SENSOR0_STATUS0_CURRENT_VALID BIT(9)
#define TSENSOR_SENSOR0_TS_STATUS1 0x1c
#define TSENSOR_SENSOR0_TS_STATUS1_CURRENT_COUNT GENMASK(31, 16)
#define TEGRA30_FUSE_TEST_PROG_VER 0x28
#define TEGRA30_FUSE_TSENSOR_CALIB 0x98
#define TEGRA30_FUSE_TSENSOR_CALIB_LOW GENMASK(15, 0)
#define TEGRA30_FUSE_TSENSOR_CALIB_HIGH GENMASK(31, 16)
#define TEGRA30_FUSE_SPARE_BIT 0x144
struct tegra_tsensor;
struct tegra_tsensor_calibration_data {
int a, b, m, n, p, r;
};
struct tegra_tsensor_channel {
void __iomem *regs;
unsigned int id;
struct tegra_tsensor *ts;
struct thermal_zone_device *tzd;
};
struct tegra_tsensor {
void __iomem *regs;
bool swap_channels;
struct clk *clk;
struct device *dev;
struct reset_control *rst;
struct tegra_tsensor_channel ch[2];
struct tegra_tsensor_calibration_data calib;
};
static int tegra_tsensor_hw_enable(const struct tegra_tsensor *ts)
{
u32 val;
int err;
err = reset_control_assert(ts->rst);
if (err) {
dev_err(ts->dev, "failed to assert hardware reset: %d\n", err);
return err;
}
err = clk_prepare_enable(ts->clk);
if (err) {
dev_err(ts->dev, "failed to enable clock: %d\n", err);
return err;
}
fsleep(1000);
err = reset_control_deassert(ts->rst);
if (err) {
dev_err(ts->dev, "failed to deassert hardware reset: %d\n", err);
goto disable_clk;
}
/*
* Sensors are enabled after reset by default, but not gauging
* until clock counter is programmed.
*
* M: number of reference clock pulses after which every
* temperature / voltage measurement is made
*
* N: number of reference clock counts for which the counter runs
*/
val = FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_M, 12500);
val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_N, 255);
/* apply the same configuration to both channels */
writel_relaxed(val, ts->regs + 0x40 + TSENSOR_SENSOR0_CONFIG0);
writel_relaxed(val, ts->regs + 0x80 + TSENSOR_SENSOR0_CONFIG0);
return 0;
disable_clk:
clk_disable_unprepare(ts->clk);
return err;
}
static int tegra_tsensor_hw_disable(const struct tegra_tsensor *ts)
{
int err;
err = reset_control_assert(ts->rst);
if (err) {
dev_err(ts->dev, "failed to assert hardware reset: %d\n", err);
return err;
}
clk_disable_unprepare(ts->clk);
return 0;
}
static void devm_tegra_tsensor_hw_disable(void *data)
{
const struct tegra_tsensor *ts = data;
tegra_tsensor_hw_disable(ts);
}
static int tegra_tsensor_get_temp(struct thermal_zone_device *tz, int *temp)
{
const struct tegra_tsensor_channel *tsc = thermal_zone_device_priv(tz);
const struct tegra_tsensor *ts = tsc->ts;
int err, c1, c2, c3, c4, counter;
u32 val;
/*
* Counter will be invalid if hardware is misprogrammed or not enough
* time passed since the time when sensor was enabled.
*/
err = readl_relaxed_poll_timeout(tsc->regs + TSENSOR_SENSOR0_STATUS0, val,
val & TSENSOR_SENSOR0_STATUS0_CURRENT_VALID,
21 * USEC_PER_MSEC,
21 * USEC_PER_MSEC * 50);
if (err) {
dev_err_once(ts->dev, "ch%u: counter invalid\n", tsc->id);
return err;
}
val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_TS_STATUS1);
counter = FIELD_GET(TSENSOR_SENSOR0_TS_STATUS1_CURRENT_COUNT, val);
/*
* This shouldn't happen with a valid counter status, nevertheless
* lets verify the value since it's in a separate (from status)
* register.
*/
if (counter == 0xffff) {
dev_err_once(ts->dev, "ch%u: counter overflow\n", tsc->id);
return -EINVAL;
}
/*
* temperature = a * counter + b
* temperature = m * (temperature ^ 2) + n * temperature + p
*/
c1 = DIV_ROUND_CLOSEST(ts->calib.a * counter + ts->calib.b, 1000000);
c1 = c1 ?: 1;
c2 = DIV_ROUND_CLOSEST(ts->calib.p, c1);
c3 = c1 * ts->calib.m;
c4 = ts->calib.n;
*temp = DIV_ROUND_CLOSEST(c1 * (c2 + c3 + c4), 1000);
return 0;
}
static int tegra_tsensor_temp_to_counter(const struct tegra_tsensor *ts, int temp)
{
int c1, c2;
c1 = DIV_ROUND_CLOSEST(ts->calib.p - temp * 1000, ts->calib.m);
c2 = -ts->calib.r - int_sqrt(ts->calib.r * ts->calib.r - c1);
return DIV_ROUND_CLOSEST(c2 * 1000000 - ts->calib.b, ts->calib.a);
}
static int tegra_tsensor_set_trips(struct thermal_zone_device *tz, int low, int high)
{
const struct tegra_tsensor_channel *tsc = thermal_zone_device_priv(tz);
const struct tegra_tsensor *ts = tsc->ts;
u32 val;
/*
* TSENSOR doesn't trigger interrupt on the "low" temperature breach,
* hence bail out if high temperature is unspecified.
*/
if (high == INT_MAX)
return 0;
val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG1);
val &= ~TSENSOR_SENSOR0_CONFIG1_TH1;
high = tegra_tsensor_temp_to_counter(ts, high);
val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG1_TH1, high);
writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG1);
return 0;
}
static const struct thermal_zone_device_ops ops = {
.get_temp = tegra_tsensor_get_temp,
.set_trips = tegra_tsensor_set_trips,
};
static bool
tegra_tsensor_handle_channel_interrupt(const struct tegra_tsensor *ts,
unsigned int id)
{
const struct tegra_tsensor_channel *tsc = &ts->ch[id];
u32 val;
val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_STATUS0);
writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_STATUS0);
if (FIELD_GET(TSENSOR_SENSOR0_STATUS0_STATE, val) == 5)
dev_err_ratelimited(ts->dev, "ch%u: counter overflowed\n", id);
if (!FIELD_GET(TSENSOR_SENSOR0_STATUS0_INTR, val))
return false;
thermal_zone_device_update(tsc->tzd, THERMAL_EVENT_UNSPECIFIED);
return true;
}
static irqreturn_t tegra_tsensor_isr(int irq, void *data)
{
const struct tegra_tsensor *ts = data;
bool handled = false;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(ts->ch); i++)
handled |= tegra_tsensor_handle_channel_interrupt(ts, i);
return handled ? IRQ_HANDLED : IRQ_NONE;
}
static int tegra_tsensor_disable_hw_channel(const struct tegra_tsensor *ts,
unsigned int id)
{
const struct tegra_tsensor_channel *tsc = &ts->ch[id];
struct thermal_zone_device *tzd = tsc->tzd;
u32 val;
int err;
if (!tzd)
goto stop_channel;
err = thermal_zone_device_disable(tzd);
if (err) {
dev_err(ts->dev, "ch%u: failed to disable zone: %d\n", id, err);
return err;
}
stop_channel:
/* stop channel gracefully */
val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG0);
val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_SENSOR_STOP, 1);
writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG0);
return 0;
}
struct trip_temps {
int hot_trip;
int crit_trip;
};
static int tegra_tsensor_get_trips_cb(struct thermal_trip *trip, void *arg)
{
struct trip_temps *temps = arg;
if (trip->type == THERMAL_TRIP_HOT)
temps->hot_trip = trip->temperature;
else if (trip->type == THERMAL_TRIP_CRITICAL)
temps->crit_trip = trip->temperature;
return 0;
}
static void tegra_tsensor_get_hw_channel_trips(struct thermal_zone_device *tzd,
struct trip_temps *temps)
{
/*
* 90C is the maximal critical temperature of all Tegra30 SoC variants,
* use it for the default trip if unspecified in a device-tree.
*/
temps->hot_trip = 85000;
temps->crit_trip = 90000;
thermal_zone_for_each_trip(tzd, tegra_tsensor_get_trips_cb, temps);
/* clamp hardware trips to the calibration limits */
temps->hot_trip = clamp(temps->hot_trip, 25000, 90000);
/*
* Kernel will perform a normal system shut down if it will
* see that critical temperature is breached, hence set the
* hardware limit by 5C higher in order to allow system to
* shut down gracefully before sending signal to the Power
* Management controller.
*/
temps->crit_trip = clamp(temps->crit_trip + 5000, 25000, 90000);
}
static int tegra_tsensor_enable_hw_channel(const struct tegra_tsensor *ts,
unsigned int id)
{
const struct tegra_tsensor_channel *tsc = &ts->ch[id];
struct thermal_zone_device *tzd = tsc->tzd;
struct trip_temps temps = { 0 };
int err;
u32 val;
if (!tzd) {
val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG0);
val &= ~TSENSOR_SENSOR0_CONFIG0_SENSOR_STOP;
writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG0);
return 0;
}
tegra_tsensor_get_hw_channel_trips(tzd, &temps);
dev_info_once(ts->dev, "ch%u: PMC emergency shutdown trip set to %dC\n",
id, DIV_ROUND_CLOSEST(temps.crit_trip, 1000));
temps.hot_trip = tegra_tsensor_temp_to_counter(ts, temps.hot_trip);
temps.crit_trip = tegra_tsensor_temp_to_counter(ts, temps.crit_trip);
/* program LEVEL2 counter threshold */
val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG1);
val &= ~TSENSOR_SENSOR0_CONFIG1_TH2;
val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG1_TH2, temps.hot_trip);
writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG1);
/* program LEVEL3 counter threshold */
val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG2);
val &= ~TSENSOR_SENSOR0_CONFIG2_TH3;
val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG2_TH3, temps.crit_trip);
writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG2);
/*
* Enable sensor, emergency shutdown, interrupts for level 1/2/3
* breaches and counter overflow condition.
*
* Disable DIV2 throttle for now since we need to figure out how
* to integrate it properly with the thermal framework.
*
* Thermal levels supported by hardware:
*
* Level 0 = cold
* Level 1 = passive cooling (cpufreq DVFS)
* Level 2 = passive cooling assisted by hardware (DIV2)
* Level 3 = emergency shutdown assisted by hardware (PMC)
*/
val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG0);
val &= ~TSENSOR_SENSOR0_CONFIG0_SENSOR_STOP;
val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_DVFS_EN, 1);
val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_HW_FREQ_DIV_EN, 0);
val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_THERMAL_RST_EN, 1);
val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_INTR_OVERFLOW_EN, 1);
val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_INTR_HW_FREQ_DIV_EN, 1);
val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_INTR_THERMAL_RST_EN, 1);
writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG0);
err = thermal_zone_device_enable(tzd);
if (err) {
dev_err(ts->dev, "ch%u: failed to enable zone: %d\n", id, err);
return err;
}
return 0;
}
static bool tegra_tsensor_fuse_read_spare(unsigned int spare)
{
u32 val = 0;
tegra_fuse_readl(TEGRA30_FUSE_SPARE_BIT + spare * 4, &val);
return !!val;
}
static int tegra_tsensor_nvmem_setup(struct tegra_tsensor *ts)
{
u32 i, ate_ver = 0, cal = 0, t1_25C = 0, t2_90C = 0;
int err, c1_25C, c2_90C;
err = tegra_fuse_readl(TEGRA30_FUSE_TEST_PROG_VER, &ate_ver);
if (err) {
dev_err_probe(ts->dev, err, "failed to get ATE version\n");
return err;
}
if (ate_ver < 8) {
dev_info(ts->dev, "unsupported ATE version: %u\n", ate_ver);
return -ENODEV;
}
/*
* We have two TSENSOR channels in a two different spots on SoC.
* Second channel provides more accurate data on older SoC versions,
* use it as a primary channel.
*/
if (ate_ver <= 21) {
dev_info_once(ts->dev,
"older ATE version detected, channels remapped\n");
ts->swap_channels = true;
}
err = tegra_fuse_readl(TEGRA30_FUSE_TSENSOR_CALIB, &cal);
if (err) {
dev_err(ts->dev, "failed to get calibration data: %d\n", err);
return err;
}
/* get calibrated counter values for 25C/90C thresholds */
c1_25C = FIELD_GET(TEGRA30_FUSE_TSENSOR_CALIB_LOW, cal);
c2_90C = FIELD_GET(TEGRA30_FUSE_TSENSOR_CALIB_HIGH, cal);
/* and calibrated temperatures corresponding to the counter values */
for (i = 0; i < 7; i++) {
t1_25C |= tegra_tsensor_fuse_read_spare(14 + i) << i;
t1_25C |= tegra_tsensor_fuse_read_spare(21 + i) << i;
t2_90C |= tegra_tsensor_fuse_read_spare(0 + i) << i;
t2_90C |= tegra_tsensor_fuse_read_spare(7 + i) << i;
}
if (c2_90C - c1_25C <= t2_90C - t1_25C) {
dev_err(ts->dev, "invalid calibration data: %d %d %u %u\n",
c2_90C, c1_25C, t2_90C, t1_25C);
return -EINVAL;
}
/* all calibration coefficients are premultiplied by 1000000 */
ts->calib.a = DIV_ROUND_CLOSEST((t2_90C - t1_25C) * 1000000,
(c2_90C - c1_25C));
ts->calib.b = t1_25C * 1000000 - ts->calib.a * c1_25C;
if (tegra_sku_info.revision == TEGRA_REVISION_A01) {
ts->calib.m = -2775;
ts->calib.n = 1338811;
ts->calib.p = -7300000;
} else {
ts->calib.m = -3512;
ts->calib.n = 1528943;
ts->calib.p = -11100000;
}
/* except the coefficient of a reduced quadratic equation */
ts->calib.r = DIV_ROUND_CLOSEST(ts->calib.n, ts->calib.m * 2);
dev_info_once(ts->dev,
"calibration: %d %d %u %u ATE ver: %u SoC rev: %u\n",
c2_90C, c1_25C, t2_90C, t1_25C, ate_ver,
tegra_sku_info.revision);
return 0;
}
static int tegra_tsensor_register_channel(struct tegra_tsensor *ts,
unsigned int id)
{
struct tegra_tsensor_channel *tsc = &ts->ch[id];
unsigned int hw_id = ts->swap_channels ? !id : id;
tsc->ts = ts;
tsc->id = id;
tsc->regs = ts->regs + 0x40 * (hw_id + 1);
tsc->tzd = devm_thermal_of_zone_register(ts->dev, id, tsc, &ops);
if (IS_ERR(tsc->tzd)) {
if (PTR_ERR(tsc->tzd) != -ENODEV)
return dev_err_probe(ts->dev, PTR_ERR(tsc->tzd),
"failed to register thermal zone\n");
/*
* It's okay if sensor isn't assigned to any thermal zone
* in a device-tree.
*/
tsc->tzd = NULL;
return 0;
}
devm_thermal_add_hwmon_sysfs(ts->dev, tsc->tzd);
return 0;
}
static int tegra_tsensor_probe(struct platform_device *pdev)
{
struct tegra_tsensor *ts;
unsigned int i;
int err, irq;
ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL);
if (!ts)
return -ENOMEM;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ts->dev = &pdev->dev;
platform_set_drvdata(pdev, ts);
ts->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ts->regs))
return PTR_ERR(ts->regs);
ts->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(ts->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(ts->clk),
"failed to get clock\n");
ts->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
if (IS_ERR(ts->rst))
return dev_err_probe(&pdev->dev, PTR_ERR(ts->rst),
"failed to get reset control\n");
err = tegra_tsensor_nvmem_setup(ts);
if (err)
return err;
err = tegra_tsensor_hw_enable(ts);
if (err)
return err;
err = devm_add_action_or_reset(&pdev->dev,
devm_tegra_tsensor_hw_disable,
ts);
if (err)
return err;
for (i = 0; i < ARRAY_SIZE(ts->ch); i++) {
err = tegra_tsensor_register_channel(ts, i);
if (err)
return err;
}
/*
* Enable the channels before setting the interrupt so
* set_trips() can not be called while we are setting up the
* register TSENSOR_SENSOR0_CONFIG1. With this we close a
* potential race window where we are setting up the TH2 and
* the temperature hits TH1 resulting to an update of the
* TSENSOR_SENSOR0_CONFIG1 register in the ISR.
*/
for (i = 0; i < ARRAY_SIZE(ts->ch); i++) {
err = tegra_tsensor_enable_hw_channel(ts, i);
if (err)
return err;
}
err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
tegra_tsensor_isr, IRQF_ONESHOT,
"tegra_tsensor", ts);
if (err)
return dev_err_probe(&pdev->dev, err,
"failed to request interrupt\n");
return 0;
}
static int __maybe_unused tegra_tsensor_suspend(struct device *dev)
{
struct tegra_tsensor *ts = dev_get_drvdata(dev);
unsigned int i;
int err;
for (i = 0; i < ARRAY_SIZE(ts->ch); i++) {
err = tegra_tsensor_disable_hw_channel(ts, i);
if (err)
goto enable_channel;
}
err = tegra_tsensor_hw_disable(ts);
if (err)
goto enable_channel;
return 0;
enable_channel:
while (i--)
tegra_tsensor_enable_hw_channel(ts, i);
return err;
}
static int __maybe_unused tegra_tsensor_resume(struct device *dev)
{
struct tegra_tsensor *ts = dev_get_drvdata(dev);
unsigned int i;
int err;
err = tegra_tsensor_hw_enable(ts);
if (err)
return err;
for (i = 0; i < ARRAY_SIZE(ts->ch); i++) {
err = tegra_tsensor_enable_hw_channel(ts, i);
if (err)
return err;
}
return 0;
}
static const struct dev_pm_ops tegra_tsensor_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_tsensor_suspend,
tegra_tsensor_resume)
};
static const struct of_device_id tegra_tsensor_of_match[] = {
{ .compatible = "nvidia,tegra30-tsensor", },
{},
};
MODULE_DEVICE_TABLE(of, tegra_tsensor_of_match);
static struct platform_driver tegra_tsensor_driver = {
.probe = tegra_tsensor_probe,
.driver = {
.name = "tegra30-tsensor",
.of_match_table = tegra_tsensor_of_match,
.pm = &tegra_tsensor_pm_ops,
},
};
module_platform_driver(tegra_tsensor_driver);
MODULE_DESCRIPTION("NVIDIA Tegra30 Thermal Sensor driver");
MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
MODULE_LICENSE("GPL");