376 lines
9.0 KiB
C
376 lines
9.0 KiB
C
/*
|
|
* Copyright (c) 2011-2013, NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/thermal.h>
|
|
#include <linux/platform_data/tegra_thermal.h>
|
|
#include <linux/tegra-soc.h>
|
|
|
|
#include "tegra_soctherm.h"
|
|
|
|
static const unsigned long t114_default_soctherm_clk_rate = 51000000;
|
|
static const unsigned long t114_default_tsensor_clk_rate = 500000;
|
|
|
|
static const s32 t114_nominal_calib_cp = 25;
|
|
static const s32 t114_nominal_calib_ft = 90;
|
|
|
|
static const struct soctherm_sensor t114_sensor_defaults = {
|
|
.tall = 16300,
|
|
.tiddq = 1,
|
|
.ten_count = 1,
|
|
.tsample = 163,
|
|
.tsamp_ate = 655,
|
|
.pdiv = 10,
|
|
.pdiv_ate = 10,
|
|
};
|
|
|
|
static struct soctherm_tsensor_pmu_data t114_pmu_data;
|
|
|
|
static struct thermal_zone_params t114_soctherm_therm_cpu_tzp = {
|
|
.governor_name = "pid_thermal_gov",
|
|
};
|
|
|
|
static struct soctherm_platform_data t114_soctherm_data = {
|
|
.therm = {
|
|
[THERM_CPU] = {
|
|
.zone_enable = true,
|
|
.passive_delay = 1000,
|
|
.hotspot_offset = 6000,
|
|
.num_trips = 3,
|
|
.trips = {
|
|
{
|
|
.cdev_type = "tegra-balanced",
|
|
.trip_temp = 90000,
|
|
.trip_type = THERMAL_TRIP_PASSIVE,
|
|
.upper = THERMAL_NO_LIMIT,
|
|
.lower = THERMAL_NO_LIMIT,
|
|
},
|
|
{
|
|
.cdev_type = "tegra-heavy",
|
|
.trip_temp = 100000,
|
|
.trip_type = THERMAL_TRIP_HOT,
|
|
.upper = THERMAL_NO_LIMIT,
|
|
.lower = THERMAL_NO_LIMIT,
|
|
},
|
|
{
|
|
.cdev_type = "tegra-shutdown",
|
|
.trip_temp = 102000,
|
|
.trip_type = THERMAL_TRIP_CRITICAL,
|
|
.upper = THERMAL_NO_LIMIT,
|
|
.lower = THERMAL_NO_LIMIT,
|
|
},
|
|
},
|
|
.tzp = &t114_soctherm_therm_cpu_tzp,
|
|
},
|
|
[THERM_GPU] = {
|
|
.zone_enable = true,
|
|
.passive_delay = 1000,
|
|
.hotspot_offset = 6000,
|
|
.num_trips = 3,
|
|
.trips = {
|
|
{
|
|
.cdev_type = "tegra-balanced",
|
|
.trip_temp = 90000,
|
|
.trip_type = THERMAL_TRIP_PASSIVE,
|
|
.upper = THERMAL_NO_LIMIT,
|
|
.lower = THERMAL_NO_LIMIT,
|
|
},
|
|
{
|
|
.cdev_type = "tegra-heavy",
|
|
.trip_temp = 100000,
|
|
.trip_type = THERMAL_TRIP_HOT,
|
|
.upper = THERMAL_NO_LIMIT,
|
|
.lower = THERMAL_NO_LIMIT,
|
|
},
|
|
{
|
|
.cdev_type = "tegra-shutdown",
|
|
.trip_temp = 102000,
|
|
.trip_type = THERMAL_TRIP_CRITICAL,
|
|
.upper = THERMAL_NO_LIMIT,
|
|
.lower = THERMAL_NO_LIMIT,
|
|
},
|
|
},
|
|
.tzp = &t114_soctherm_therm_cpu_tzp,
|
|
},
|
|
[THERM_PLL] = {
|
|
.zone_enable = true,
|
|
},
|
|
},
|
|
.throttle = {
|
|
[THROTTLE_HEAVY] = {
|
|
.devs = {
|
|
[THROTTLE_DEV_CPU] = {
|
|
.enable = 1,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
.tshut_pmu_trip_data = &t114_pmu_data,
|
|
};
|
|
|
|
static struct soctherm_sensor2tsensorcalib t114_tsensor_calib[] = {
|
|
[TSENSE_CPU0] = {
|
|
.tsensor = 0,
|
|
},
|
|
[TSENSE_CPU1] = {
|
|
.tsensor = 1,
|
|
},
|
|
[TSENSE_CPU2] = {
|
|
.tsensor = 2,
|
|
},
|
|
[TSENSE_CPU3] = {
|
|
.tsensor = 3,
|
|
},
|
|
[TSENSE_MEM0] = {
|
|
.tsensor = 5,
|
|
},
|
|
[TSENSE_MEM1] = {
|
|
.tsensor = 6,
|
|
},
|
|
[TSENSE_GPU] = {
|
|
.tsensor = 4,
|
|
},
|
|
[TSENSE_PLLX] = {
|
|
.tsensor = 7,
|
|
},
|
|
};
|
|
|
|
static int t114_fuse_corr_alpha[] = { /* scaled *1000000 */
|
|
[TSENSE_CPU0] = 1196400,
|
|
[TSENSE_CPU1] = 1196400,
|
|
[TSENSE_CPU2] = 1196400,
|
|
[TSENSE_CPU3] = 1196400,
|
|
[TSENSE_GPU] = 1124500,
|
|
[TSENSE_PLLX] = 1224200,
|
|
};
|
|
|
|
static int t114_fuse_corr_beta[] = { /* scaled *1000000 */
|
|
[TSENSE_CPU0] = -13600000,
|
|
[TSENSE_CPU1] = -13600000,
|
|
[TSENSE_CPU2] = -13600000,
|
|
[TSENSE_CPU3] = -13600000,
|
|
[TSENSE_GPU] = -9793100,
|
|
[TSENSE_PLLX] = -14665000,
|
|
};
|
|
|
|
static struct soctherm_fuse_calib_data t114_calib_data = {
|
|
.tsensor_calib = t114_tsensor_calib,
|
|
.fuse_corr_alpha = t114_fuse_corr_alpha,
|
|
.fuse_corr_beta = t114_fuse_corr_beta,
|
|
};
|
|
|
|
#define T114_FUSE_VSENSOR_CALIB_0 0x08c
|
|
#define T114_FUSE_BASE_CP_SHIFT 0
|
|
#define T114_FUSE_BASE_CP_MASK 0x3ff
|
|
#define T114_FUSE_BASE_FT_SHIFT 16
|
|
#define T114_FUSE_BASE_FT_MASK 0x7ff
|
|
#define T114_FUSE_SHIFT_CP_SHIFT 10
|
|
#define T114_FUSE_SHIFT_CP_MASK 0x3f
|
|
#define T114_FUSE_SHIFT_CP_BITS 6
|
|
#define T114_FUSE_SHIFT_FT_SHIFT 27
|
|
#define T114_FUSE_SHIFT_FT_MASK 0x1f
|
|
#define T114_FUSE_SHIFT_FT_BITS 5
|
|
|
|
static int t114_tsensor_calib_offset[] = {
|
|
[0] = 0x098,
|
|
[1] = 0x084,
|
|
[2] = 0x088,
|
|
[3] = 0x12c,
|
|
[4] = 0x154,
|
|
[5] = 0x158,
|
|
[6] = 0x15c,
|
|
[7] = 0x160,
|
|
};
|
|
|
|
static int tegra114_fuse_get_tsensor_calib(int index, u32 *calib)
|
|
{
|
|
if (index < 0 || index >= ARRAY_SIZE(t114_tsensor_calib_offset))
|
|
return -EINVAL;
|
|
*calib = tegra30_fuse_readl(t114_tsensor_calib_offset[index]);
|
|
return 0;
|
|
}
|
|
|
|
static int tegra114_fuse_calib_base_get_cp(u32 *base_cp, s32 *shifted_cp)
|
|
{
|
|
s32 cp;
|
|
u32 val = tegra30_fuse_readl(T114_FUSE_VSENSOR_CALIB_0);
|
|
|
|
if (base_cp && shifted_cp) {
|
|
*base_cp = (((val) & (T114_FUSE_BASE_CP_MASK
|
|
<< T114_FUSE_BASE_CP_SHIFT))
|
|
>> T114_FUSE_BASE_CP_SHIFT);
|
|
if (!(*base_cp))
|
|
return -EINVAL;
|
|
|
|
cp = (((val) & (T114_FUSE_SHIFT_CP_MASK
|
|
<< T114_FUSE_SHIFT_CP_SHIFT))
|
|
>> T114_FUSE_SHIFT_CP_SHIFT);
|
|
|
|
*shifted_cp = ((s32)(cp)
|
|
<< (32 - T114_FUSE_SHIFT_CP_BITS)
|
|
>> (32 - T114_FUSE_SHIFT_CP_BITS));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tegra114_fuse_calib_base_get_ft(u32 *base_ft, s32 *shifted_ft)
|
|
{
|
|
s32 ft;
|
|
u32 val = tegra30_fuse_readl(T114_FUSE_VSENSOR_CALIB_0);
|
|
|
|
if (base_ft && shifted_ft) {
|
|
*base_ft = (((val) & (T114_FUSE_BASE_FT_MASK
|
|
<< T114_FUSE_BASE_FT_SHIFT))
|
|
>> T114_FUSE_BASE_FT_SHIFT);
|
|
if (!(*base_ft))
|
|
return -EINVAL;
|
|
|
|
ft = (((val) & (T114_FUSE_SHIFT_FT_MASK
|
|
<< T114_FUSE_SHIFT_FT_SHIFT))
|
|
>> T114_FUSE_SHIFT_FT_SHIFT);
|
|
|
|
*shifted_ft = ((s32)(ft)
|
|
<< (32 - T114_FUSE_SHIFT_FT_BITS)
|
|
>> (32 - T114_FUSE_SHIFT_FT_BITS));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tegra114_soctherm_fuse_read_calib_base(void)
|
|
{
|
|
s32 calib_cp, calib_ft;
|
|
u32 fuse_calib_base_cp, fuse_calib_base_ft;
|
|
|
|
if (tegra114_fuse_calib_base_get_cp(&fuse_calib_base_cp, &calib_cp) ||
|
|
tegra114_fuse_calib_base_get_ft(&fuse_calib_base_ft, &calib_ft)) {
|
|
return -EINVAL;
|
|
}
|
|
t114_calib_data.fuse_calib_base_cp = fuse_calib_base_cp;
|
|
t114_calib_data.fuse_calib_base_ft = fuse_calib_base_ft;
|
|
|
|
/* use HI precision to calculate: use fuse_temp in 0.5C */
|
|
t114_calib_data.actual_temp_cp = 2 * t114_nominal_calib_cp + calib_cp;
|
|
t114_calib_data.actual_temp_ft = 2 * t114_nominal_calib_ft + calib_ft;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tegra114_soctherm_fuse_get_tsensor_calib(void)
|
|
{
|
|
u32 value;
|
|
int i, sensor, ret;
|
|
|
|
for (i = 0; i < TSENSE_SIZE; i++) {
|
|
sensor = t114_calib_data.tsensor_calib[i].tsensor;
|
|
ret = tegra114_fuse_get_tsensor_calib(sensor, &value);
|
|
if (ret < 0)
|
|
return ret;
|
|
t114_calib_data.tsensor_calib[i].calib_val = value;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tegra114_soctherm_init(struct device_node *soctherm_dn)
|
|
{
|
|
struct platform_device *pdev;
|
|
struct soctherm_platform_data *pdata;
|
|
struct device_node *dn;
|
|
void __iomem *base;
|
|
struct thermal_trip_info *trips;
|
|
int *num_trips;
|
|
|
|
pdev = of_find_device_by_node(soctherm_dn);
|
|
if (!pdev) {
|
|
dev_err(&pdev->dev, "Failed to get soctherm device\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
pdata = &t114_soctherm_data;
|
|
platform_set_drvdata(pdev, pdata);
|
|
|
|
if (!t114_calib_data.fuse_calib_base_cp) {
|
|
if (tegra114_soctherm_fuse_read_calib_base() < 0) {
|
|
dev_err(&pdev->dev,
|
|
"ERROR: Improper CP or FT calib fuse.\n");
|
|
return -EINVAL;
|
|
}
|
|
if (tegra114_soctherm_fuse_get_tsensor_calib() < 0) {
|
|
dev_err(&pdev->dev,
|
|
"ERROR: Improper tsensor calib fuse.\n");
|
|
return -EINVAL;
|
|
}
|
|
pdata->fuse_calib_data = &t114_calib_data;
|
|
}
|
|
|
|
trips = pdata->therm[THERM_CPU].trips;
|
|
num_trips = &pdata->therm[THERM_CPU].num_trips;
|
|
|
|
/* get soctherm base address */
|
|
base = of_iomap(soctherm_dn, 0);
|
|
if (!base) {
|
|
dev_err(&pdev->dev, "Can't map soctherm registers\n");
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
pdata->soctherm_base = base;
|
|
|
|
/* get pmc base address */
|
|
dn = of_find_compatible_node(NULL, NULL, "nvidia,tegra114-pmc");
|
|
if (!dn) {
|
|
dev_err(&pdev->dev, "Failed to find pmc node\n");
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
base = of_iomap(dn, 0);
|
|
if (!base) {
|
|
dev_err(&pdev->dev, "Can't map pmc registers\n");
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
pdata->pmc_base = base;
|
|
|
|
/* get clk base address */
|
|
dn = of_find_compatible_node(NULL, NULL, "nvidia,tegra114-car");
|
|
if (!dn) {
|
|
dev_err(&pdev->dev, "Failed to find clk node\n");
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
base = of_iomap(dn, 0);
|
|
if (!base) {
|
|
dev_err(&pdev->dev, "Can't map clk registers\n");
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
pdata->clk_reset_base = base;
|
|
|
|
soctherm_parse_pmu_dt(pdev);
|
|
|
|
/* initialize default sensor value */
|
|
soctherm_init_sensor(pdev, &t114_sensor_defaults);
|
|
|
|
/* initialize default clock rates */
|
|
soctherm_init_clk_rate(pdev,
|
|
t114_default_soctherm_clk_rate,
|
|
t114_default_tsensor_clk_rate);
|
|
|
|
return 0;
|
|
}
|