2014-06-19 11:52:39

by Mikko Perttunen

[permalink] [raw]
Subject: [RFC 0/5] Tegra124 thermal management

Hi, this patchset implements basic polled thermal sensing support and
hardware initiated shutdown ("thermtrip") in an overheating situations
for the Tegra124 system-on-chip.

The driver uses the of-thermal framework to expose the sensors to the
thermal subsystem, and this works well for the simplest case of polled
sensors. However, there are two features I'd like to implement that
as far as I know the framework isn't really ready for:
- hardware based trip points, i.e., interrupts at a configured temperature
- hardware initiated cooling

Thermtrip is an example of the latter, but the hardware is also capable
of less drastic measures. Currently, the driver doesn't attempt to expose
thermtrip using the framework, instead opting to use custom device tree
properties, but this is something I'd like to move away from.

To implement hardware based trip points, the sensor - of-thermal interface
would need a new function, which the of-thermal framework would use to
tell the driver of a new trip point. I suppose the best way to do this
would be to have of-thermal manage trip points, and when one is reached,
tell the driver to prepare for the next one. The sensor driver should
be capable of managing two trip points, and below and one above the
current temperature.

Hardware initiated cooling is a bit more interesting. The hardware that
initiates the cooling procedure necessarily has some view of the thermal
sensors / thermal zones, so the thermal zones defined in the device tree
should map exactly to those ones. Hardware based cooling devices should
be defined in the device tree just like any other cooling devices.
The problematic part is binding a trip point to a hardware cooling device.
There would need some additional interface to tell the cooling device
the temperature of the trip point it is bound to.

Hardware cooling devices can also track multiple thermal zones (for example,
thermtrip can trigger based on cpu, gpu, memory and tsense sensors). To
distinguish between these in the device tree, I can think of two options:
1. Implement each tracked zone as a separate cooling device. (Probably by
having an additional cooling-cell) This would make the interface simpler
but allow impossible cooling mappings to be made in the device tree.
2. When telling the cooling device of a particular trip point, also tell it
which thermal zone it is related to. This would require the cooling device
to have some kind of ability to detect that a thermal zone object is a
specific thermal zone.

One more question related to hardware-initiated reset in an overheating
situation: the "critical" trip level is designed to initiate a controller
shutdown. Should there be a new trip level for an uncontrolled shutdown?

Any thoughts would be appreciated.

Thanks,
Mikko Perttunen

Mikko Perttunen (5):
ARM: tegra: Add PMC thermtrip programming to Jetson TK1 device tree
ARM: tegra: Add soctherm and thermal zones to Tegra124 device tree
ARM: tegra: Add thermal reset (thermtrip) support to PMC
clk: tegra: Add soctherm and tsensor clocks to T124 initialization
table
thermal: Add Tegra SOCTHERM thermal management driver

arch/arm/boot/dts/tegra124-jetson-tk1.dts | 5 +
arch/arm/boot/dts/tegra124.dtsi | 47 +++
arch/arm/mach-tegra/pmc.c | 95 +++++-
drivers/clk/tegra/clk-tegra124.c | 2 +
drivers/thermal/Kconfig | 6 +
drivers/thermal/Makefile | 1 +
drivers/thermal/tegra_soctherm.c | 502 ++++++++++++++++++++++++++++++
7 files changed, 654 insertions(+), 4 deletions(-)
create mode 100644 drivers/thermal/tegra_soctherm.c

--
1.8.1.5


2014-06-19 11:52:45

by Mikko Perttunen

[permalink] [raw]
Subject: [RFC 1/5] ARM: tegra: Add PMC thermtrip programming to Jetson TK1 device tree

This adds the required information to reset the board during an overheating
situation to the Jetson TK1 device tree. The thermal reset is handled by the
PMC by sending an I2C message to the PMIC. The entries specify the I2C
message to be sent.

Signed-off-by: Mikko Perttunen <[email protected]>
---
arch/arm/boot/dts/tegra124-jetson-tk1.dts | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/arch/arm/boot/dts/tegra124-jetson-tk1.dts b/arch/arm/boot/dts/tegra124-jetson-tk1.dts
index 16082c0..896c24b 100644
--- a/arch/arm/boot/dts/tegra124-jetson-tk1.dts
+++ b/arch/arm/boot/dts/tegra124-jetson-tk1.dts
@@ -1617,6 +1617,11 @@
nvidia,core-pwr-off-time = <61036>;
nvidia,core-power-req-active-high;
nvidia,sys-clock-req-active-high;
+
+ nvidia,thermtrip-pmu-i2c-addr = <0x40>;
+ nvidia,thermtrip-i2c-controller = <4>;
+ nvidia,thermtrip-reg-addr = <0x36>;
+ nvidia,thermtrip-reg-data = <0x2>;
};

/* SD card */
--
1.8.1.5

2014-06-19 11:52:51

by Mikko Perttunen

[permalink] [raw]
Subject: [RFC 3/5] ARM: tegra: Add thermal reset (thermtrip) support to PMC

This adds a device tree controlled option to enable PMC-based
thermal reset in overheating situations. Thermtrip is supported on
Tegra114 and Tegra124. The thermal reset only works when the thermal
sensors are calibrated, so a soctherm driver is also required.

Signed-off-by: Mikko Perttunen <[email protected]>
---
arch/arm/mach-tegra/pmc.c | 95 +++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 91 insertions(+), 4 deletions(-)

diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c
index e1677c0..69143cd 100644
--- a/arch/arm/mach-tegra/pmc.c
+++ b/arch/arm/mach-tegra/pmc.c
@@ -51,6 +51,22 @@
#define PMC_CPUPWRGOOD_TIMER 0xc8
#define PMC_CPUPWROFF_TIMER 0xcc

+#define PMC_SENSOR_CTRL 0x1b0
+#define PMC_SENSOR_CTRL_SCRATCH_WRITE (1 << 2)
+#define PMC_SENSOR_CTRL_ENABLE_RST (1 << 1)
+
+#define PMC_SCRATCH54 0x258
+#define PMC_SCRATCH54_DATA_SHIFT 8
+#define PMC_SCRATCH54_ADDR_SHIFT 0
+
+#define PMC_SCRATCH55 0x25c
+#define PMC_SCRATCH55_RESET_TEGRA (1 << 31)
+#define PMC_SCRATCH55_CNTRL_ID_SHIFT 27
+#define PMC_SCRATCH55_PINMUX_SHIFT 24
+#define PMC_SCRATCH55_16BITOP (1 << 15)
+#define PMC_SCRATCH55_CHECKSUM_SHIFT 16
+#define PMC_SCRATCH55_I2CSLV1_SHIFT 0
+
static u8 tegra_cpu_domains[] = {
0xFF, /* not available for CPU0 */
TEGRA_POWERGATE_CPU1,
@@ -295,11 +311,19 @@ void tegra_pmc_suspend_init(void)
}
#endif

+#define PMC_HAS_THERMAL_RESET (1 << 0)
+
static const struct of_device_id matches[] __initconst = {
- { .compatible = "nvidia,tegra124-pmc" },
- { .compatible = "nvidia,tegra114-pmc" },
- { .compatible = "nvidia,tegra30-pmc" },
- { .compatible = "nvidia,tegra20-pmc" },
+ {
+ .compatible = "nvidia,tegra124-pmc",
+ .data = (void *)PMC_HAS_THERMAL_RESET
+ },
+ {
+ .compatible = "nvidia,tegra114-pmc",
+ .data = (void *)PMC_HAS_THERMAL_RESET
+ },
+ { .compatible = "nvidia,tegra30-pmc", .data = 0 },
+ { .compatible = "nvidia,tegra20-pmc", .data = 0 },
{ }
};

@@ -324,6 +348,67 @@ void __init tegra_pmc_init_irq(void)
tegra_pmc_writel(val, PMC_CTRL);
}

+void __init tegra_pmc_init_thermal_reset(struct device_node *np)
+{
+ u32 pmu_i2c_addr, i2c_ctrl_id, reg_addr, reg_data, pinmux;
+ bool pmu_16bit_ops;
+ u32 val, checksum;
+ const struct of_device_id *match = of_match_node(matches, np);
+
+ if (!((u32)match->data & PMC_HAS_THERMAL_RESET))
+ return;
+
+ pmu_16bit_ops =
+ of_property_read_bool(np, "nvidia,thermtrip-pmu-16bit-ops");
+ if (of_property_read_u32(
+ np, "nvidia,thermtrip-pmu-i2c-addr", &pmu_i2c_addr))
+ goto disabled;
+ if (of_property_read_u32(
+ np, "nvidia,thermtrip-i2c-controller", &i2c_ctrl_id))
+ goto disabled;
+ if (of_property_read_u32(
+ np, "nvidia,thermtrip-reg-addr", &reg_addr))
+ goto disabled;
+ if (of_property_read_u32(
+ np, "nvidia,thermtrip-reg-data", &reg_data))
+ goto disabled;
+ if (of_property_read_u32(
+ np, "nvidia,thermtrip-pinmux", &pinmux))
+ pinmux = 0;
+
+ val = tegra_pmc_readl(PMC_SENSOR_CTRL);
+ val |= PMC_SENSOR_CTRL_SCRATCH_WRITE | PMC_SENSOR_CTRL_ENABLE_RST;
+ tegra_pmc_writel(val, PMC_SENSOR_CTRL);
+
+ val = (reg_data << PMC_SCRATCH54_DATA_SHIFT) |
+ (reg_addr << PMC_SCRATCH54_ADDR_SHIFT);
+ tegra_pmc_writel(val, PMC_SCRATCH54);
+
+ val = 0;
+ val |= PMC_SCRATCH55_RESET_TEGRA;
+ val |= i2c_ctrl_id << PMC_SCRATCH55_CNTRL_ID_SHIFT;
+ val |= pinmux << PMC_SCRATCH55_PINMUX_SHIFT;
+ if (pmu_16bit_ops)
+ val |= PMC_SCRATCH55_16BITOP;
+ val |= pmu_i2c_addr << PMC_SCRATCH55_I2CSLV1_SHIFT;
+
+ checksum = reg_addr + reg_data + (val & 0xFF) + ((val >> 8) & 0xFF) +
+ ((val >> 24) & 0xFF);
+ checksum &= 0xFF;
+ checksum = 0x100 - checksum;
+
+ val |= checksum << PMC_SCRATCH55_CHECKSUM_SHIFT;
+
+ tegra_pmc_writel(val, PMC_SCRATCH55);
+
+ pr_info("Tegra: PMC thermal reset enabled\n");
+
+ return;
+
+disabled:
+ pr_warn("Tegra: PMC thermal reset disabled\n");
+}
+
void __init tegra_pmc_init(void)
{
struct device_node *np;
@@ -399,4 +484,6 @@ void __init tegra_pmc_init(void)
pmc_pm_data.lp0_vec_size = lp0_vec[1];

pmc_pm_data.suspend_mode = suspend_mode;
+
+ tegra_pmc_init_thermal_reset(np);
}
--
1.8.1.5

2014-06-19 11:52:54

by Mikko Perttunen

[permalink] [raw]
Subject: [RFC 4/5] clk: tegra: Add soctherm and tsensor clocks to T124 initialization table

This adds the two clocks, soctherm and tsensor, to the T124 initialization table.
They are required for soctherm-based thermal sensing.

Signed-off-by: Mikko Perttunen <[email protected]>
---
drivers/clk/tegra/clk-tegra124.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c
index 80efe51..abeec63 100644
--- a/drivers/clk/tegra/clk-tegra124.c
+++ b/drivers/clk/tegra/clk-tegra124.c
@@ -1369,6 +1369,8 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{TEGRA124_CLK_XUSB_HS_SRC, TEGRA124_CLK_PLL_U_60M, 60000000, 0},
{TEGRA124_CLK_XUSB_FALCON_SRC, TEGRA124_CLK_PLL_RE_OUT, 224000000, 0},
{TEGRA124_CLK_XUSB_HOST_SRC, TEGRA124_CLK_PLL_RE_OUT, 112000000, 0},
+ {TEGRA124_CLK_SOC_THERM, TEGRA124_CLK_PLL_P, 51000000, 0},
+ {TEGRA124_CLK_TSENSOR, TEGRA124_CLK_CLK_M, 400000, 0},
/* This MUST be the last entry. */
{TEGRA124_CLK_CLK_MAX, TEGRA124_CLK_CLK_MAX, 0, 0},
};
--
1.8.1.5

2014-06-19 11:53:00

by Mikko Perttunen

[permalink] [raw]
Subject: [RFC 5/5] thermal: Add Tegra SOCTHERM thermal management driver

This adds support for the Tegra SOCTHERM thermal sensing and management
system found in the Tegra124 system-on-chip. Only polling-based thermal
sensing and overheating reset are supported for now.

Signed-off-by: Mikko Perttunen <[email protected]>
---
drivers/thermal/Kconfig | 6 +
drivers/thermal/Makefile | 1 +
drivers/thermal/tegra_soctherm.c | 502 +++++++++++++++++++++++++++++++++++++++
3 files changed, 509 insertions(+)
create mode 100644 drivers/thermal/tegra_soctherm.c

diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index f9a1386..70835af 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -175,6 +175,12 @@ config ARMADA_THERMAL
Enable this option if you want to have support for thermal management
controller present in Armada 370 and Armada XP SoC.

+config TEGRA_SOCTHERM
+ tristate "Tegra SOCTHERM thermal management"
+ depends on ARCH_TEGRA
+ help
+ Enable this option for thermal management support on Tegra124 SoCs.
+
config DB8500_CPUFREQ_COOLING
tristate "DB8500 cpufreq cooling"
depends on ARCH_U8500
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index de0636a..d5d964b 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -32,3 +32,4 @@ obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o
obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o
+obj-$(CONFIG_TEGRA_SOCTHERM) += tegra_soctherm.o
diff --git a/drivers/thermal/tegra_soctherm.c b/drivers/thermal/tegra_soctherm.c
new file mode 100644
index 0000000..e1352f1
--- /dev/null
+++ b/drivers/thermal/tegra_soctherm.c
@@ -0,0 +1,502 @@
+/*
+ * drivers/thermal/tegra_soctherm.c
+ *
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Author:
+ * Mikko Perttunen <[email protected]>
+ *
+ * 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/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/thermal.h>
+#include <linux/tegra-soc.h>
+
+#define SENSOR_CONFIG0 0
+#define SENSOR_CONFIG0_STOP BIT(0)
+#define SENSOR_CONFIG0_TALL_SHIFT 8
+#define SENSOR_CONFIG0_TCALC_OVER BIT(4)
+#define SENSOR_CONFIG0_OVER BIT(3)
+#define SENSOR_CONFIG0_CPTR_OVER BIT(2)
+#define SENSOR_CONFIG1 4
+#define SENSOR_CONFIG1_TSAMPLE_SHIFT 0
+#define SENSOR_CONFIG1_TIDDQ_EN_SHIFT 15
+#define SENSOR_CONFIG1_TEN_COUNT_SHIFT 24
+#define SENSOR_CONFIG1_TEMP_ENABLE BIT(31)
+#define SENSOR_CONFIG2 8
+#define SENSOR_CONFIG2_THERMA_SHIFT 16
+#define SENSOR_CONFIG2_THERMB_SHIFT 0
+
+#define THERMTRIP_CTL 0x80
+#define THERMTRIP_CTL_ANY_EN BIT(28)
+#define THERMTRIP_CTL_TSENSE_MASK 0xff
+#define THERMTRIP_CTL_TSENSE_SHIFT 0
+#define THERMTRIP_CTL_CPU_MASK 0xff00
+#define THERMTRIP_CTL_CPU_SHIFT 8
+#define THERMTRIP_CTL_GPU_MEM_MASK 0xff0000
+#define THERMTRIP_CTL_GPU_MEM_SHIFT 16
+#define THERMTRIP_DEFAULT_THRESHOLD 105
+
+#define SENSOR_PDIV 0x1c0
+#define SENSOR_PDIV_T124 0x8888
+#define SENSOR_HOTSPOT_OFF 0x1c4
+#define SENSOR_HOTSPOT_OFF_T124 0x00060600
+#define SENSOR_TEMP1 0x1c8
+#define SENSOR_TEMP1_CPU_TEMP_SHIFT 16
+#define SENSOR_TEMP1_GPU_TEMP_MASK 0xffff
+#define SENSOR_TEMP2 0x1cc
+#define SENSOR_TEMP2_MEM_TEMP_SHIFT 16
+#define SENSOR_TEMP2_PLLX_TEMP_MASK 0xffff
+
+#define FUSE_TSENSOR8_CALIB 0x180
+#define FUSE_SPARE_REALIGNMENT_REG_0 0x1fc
+
+#define NOMINAL_CALIB_FT_T124 105
+
+struct tegra_tsensor_configuration {
+ u32 tall, tsample, tiddq_en, ten_count;
+ u32 pdiv, tsample_ate, pdiv_ate;
+};
+
+struct tegra_tsensor {
+ const char *name;
+ u32 base;
+ struct tegra_tsensor_configuration *config;
+ u32 calib_fuse_offset;
+ s32 fuse_corr_alpha, fuse_corr_beta;
+};
+
+struct tegra_thermctl_zone {
+ struct tegra_soctherm *tegra;
+ int sensor;
+};
+
+static struct tegra_tsensor_configuration t124_tsensor_config = {
+ .tall = 16300,
+ .tsample = 120,
+ .tiddq_en = 1,
+ .ten_count = 1,
+ .pdiv = 8,
+ .tsample_ate = 481,
+ .pdiv_ate = 8
+};
+
+static struct tegra_tsensor t124_tsensors[] = {
+ {
+ .base = 0xc0,
+ .name = "cpu0",
+ .config = &t124_tsensor_config,
+ .calib_fuse_offset = 0x098,
+ .fuse_corr_alpha = 1135400,
+ .fuse_corr_beta = -6266900,
+ },
+ {
+ .base = 0xe0,
+ .name = "cpu1",
+ .config = &t124_tsensor_config,
+ .calib_fuse_offset = 0x084,
+ .fuse_corr_alpha = 1122220,
+ .fuse_corr_beta = -5700700,
+ },
+ {
+ .base = 0x100,
+ .name = "cpu2",
+ .config = &t124_tsensor_config,
+ .calib_fuse_offset = 0x088,
+ .fuse_corr_alpha = 1127000,
+ .fuse_corr_beta = -6768200,
+ },
+ {
+ .base = 0x120,
+ .name = "cpu3",
+ .config = &t124_tsensor_config,
+ .calib_fuse_offset = 0x12c,
+ .fuse_corr_alpha = 1110900,
+ .fuse_corr_beta = -6232000,
+ },
+ {
+ .base = 0x140,
+ .name = "mem0",
+ .config = &t124_tsensor_config,
+ .calib_fuse_offset = 0x158,
+ .fuse_corr_alpha = 1122300,
+ .fuse_corr_beta = -5936400,
+ },
+ {
+ .base = 0x160,
+ .name = "mem1",
+ .config = &t124_tsensor_config,
+ .calib_fuse_offset = 0x15c,
+ .fuse_corr_alpha = 1145700,
+ .fuse_corr_beta = -7124600,
+ },
+ {
+ .base = 0x180,
+ .name = "gpu",
+ .config = &t124_tsensor_config,
+ .calib_fuse_offset = 0x154,
+ .fuse_corr_alpha = 1120100,
+ .fuse_corr_beta = -6000500,
+ },
+ {
+ .base = 0x1a0,
+ .name = "pllx",
+ .config = &t124_tsensor_config,
+ .calib_fuse_offset = 0x160,
+ .fuse_corr_alpha = 1106500,
+ .fuse_corr_beta = -6729300,
+ },
+ { .name = NULL },
+};
+
+struct tegra_soctherm {
+ struct reset_control *reset;
+ struct clk *clock_tsensor;
+ struct clk *clock_soctherm;
+ void __iomem *regs;
+
+ struct thermal_zone_device *thermctl_tzs[4];
+};
+
+struct tsensor_shared_calibration {
+ u32 base_cp, base_ft;
+ u32 actual_temp_cp, actual_temp_ft;
+};
+
+static int calculate_shared_calibration(struct tsensor_shared_calibration *r)
+{
+ u32 val;
+ u32 shifted_cp, shifted_ft;
+ int err;
+
+ err = tegra_fuse_readl(FUSE_TSENSOR8_CALIB, &val);
+ if (err)
+ return err;
+ r->base_cp = val & 0x3ff;
+ r->base_ft = (val & (0x7ff << 10)) >> 10;
+
+ err = tegra_fuse_readl(FUSE_SPARE_REALIGNMENT_REG_0, &val);
+ if (err)
+ return err;
+ /* Sign extend from 6 bits to 32 bits */
+ shifted_cp = (s32)((val & 0x1f) | ((val & 0x20) ? 0xffffffe0 : 0x0));
+ val = ((val & (0x1f << 21)) >> 21);
+ /* Sign extend from 5 bits to 32 bits */
+ shifted_ft = (s32)((val & 0xf) | ((val & 0x10) ? 0xfffffff0 : 0x0));
+
+ r->actual_temp_cp = 2 * 25 + shifted_cp;
+ r->actual_temp_ft = 2 * NOMINAL_CALIB_FT_T124 + shifted_ft;
+
+ return 0;
+}
+
+static int calculate_tsensor_calibration(
+ struct tegra_tsensor *sensor,
+ struct tsensor_shared_calibration shared,
+ u32 *calib
+)
+{
+ u32 val;
+ s32 actual_tsensor_ft, actual_tsensor_cp;
+ s32 delta_sens, delta_temp;
+ s32 mult, div;
+ s16 therma, thermb;
+ int err;
+
+ err = tegra_fuse_readl(sensor->calib_fuse_offset, &val);
+ if (err)
+ return err;
+
+ /* Sign extend from 13 bits to 32 bits */
+ actual_tsensor_cp = (shared.base_cp * 64) +
+ (s32)((val & 0xfff) | ((val & 0x1000) ? 0xfffff000 : 0x0));
+ val = (val & (0x1fff << 13)) >> 13;
+ /* Sign extend from 13 bits to 32 bits */
+ actual_tsensor_ft = (shared.base_ft * 32) +
+ (s32)((val & 0xfff) | ((val & 0x1000) ? 0xfffff000 : 0x0));
+
+ delta_sens = actual_tsensor_ft - actual_tsensor_cp;
+ delta_temp = shared.actual_temp_ft - shared.actual_temp_cp;
+
+ mult = sensor->config->pdiv * sensor->config->tsample_ate;
+ div = sensor->config->tsample * sensor->config->pdiv_ate;
+
+ therma = div_s64((s64) delta_temp * (1LL << 13) * mult,
+ (s64) delta_sens * div);
+ thermb = div_s64(((s64) actual_tsensor_ft * shared.actual_temp_cp) -
+ ((s64) actual_tsensor_cp * shared.actual_temp_ft),
+ (s64) delta_sens);
+
+ therma = div_s64((s64) therma * sensor->fuse_corr_alpha,
+ (s64) 1000000LL);
+ thermb = div_s64((s64) thermb * sensor->fuse_corr_alpha +
+ sensor->fuse_corr_beta,
+ (s64) 1000000LL);
+
+ *calib = ((u16)(therma) << SENSOR_CONFIG2_THERMA_SHIFT) |
+ ((u16)thermb << SENSOR_CONFIG2_THERMB_SHIFT);
+
+ return 0;
+}
+
+static int enable_tsensor(struct tegra_soctherm *tegra,
+ struct tegra_tsensor *sensor,
+ struct tsensor_shared_calibration shared)
+{
+ void * __iomem base = tegra->regs + sensor->base;
+ unsigned int val;
+ u32 calib;
+ int err;
+
+ err = calculate_tsensor_calibration(sensor, shared, &calib);
+ if (err)
+ return err;
+
+ val = 0;
+ val |= sensor->config->tall << SENSOR_CONFIG0_TALL_SHIFT;
+ writel(val, base + SENSOR_CONFIG0);
+
+ val = 0;
+ val |= (sensor->config->tsample - 1) << SENSOR_CONFIG1_TSAMPLE_SHIFT;
+ val |= sensor->config->tiddq_en << SENSOR_CONFIG1_TIDDQ_EN_SHIFT;
+ val |= sensor->config->ten_count << SENSOR_CONFIG1_TEN_COUNT_SHIFT;
+ val |= SENSOR_CONFIG1_TEMP_ENABLE;
+ writel(val, base + SENSOR_CONFIG1);
+
+ writel(calib, base + SENSOR_CONFIG2);
+
+ return 0;
+}
+
+static inline long translate_temp(u32 val)
+{
+ long t;
+
+ t = ((val & 0xff00) >> 8) * 1000;
+ if (val & 0x80)
+ t += 500;
+ if (val & 0x01)
+ t *= -1;
+
+ return t;
+}
+
+static int tegra_thermctl_get_temp(void *data, long *out_temp)
+{
+ struct tegra_thermctl_zone *zone = data;
+ u32 val;
+
+ switch (zone->sensor) {
+ case 0:
+ val = readl(zone->tegra->regs + SENSOR_TEMP1)
+ >> SENSOR_TEMP1_CPU_TEMP_SHIFT;
+ break;
+ case 1:
+ val = readl(zone->tegra->regs + SENSOR_TEMP2)
+ >> SENSOR_TEMP2_MEM_TEMP_SHIFT;
+ break;
+ case 2:
+ val = readl(zone->tegra->regs + SENSOR_TEMP1)
+ & SENSOR_TEMP1_GPU_TEMP_MASK;
+ break;
+ case 3:
+ val = readl(zone->tegra->regs + SENSOR_TEMP2)
+ & SENSOR_TEMP2_PLLX_TEMP_MASK;
+ break;
+ default:
+ BUG();
+ }
+
+ *out_temp = translate_temp(val);
+
+ return 0;
+}
+
+static struct of_device_id tegra_soctherm_of_match[] = {
+ { .compatible = "nvidia,tegra124-soctherm" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tegra_soctherm_of_match);
+
+static int tegra_soctherm_probe(struct platform_device *pdev)
+{
+ struct tegra_soctherm *tegra;
+ struct thermal_zone_device *tz;
+ struct tsensor_shared_calibration shared_calib;
+ int i;
+ int err = 0;
+ u32 val, prop;
+
+ struct tegra_tsensor *tsensors = t124_tsensors;
+
+ tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
+ if (!tegra)
+ return -ENOMEM;
+
+ tegra->regs = devm_ioremap_resource(&pdev->dev,
+ platform_get_resource(pdev, IORESOURCE_MEM, 0));
+ if (IS_ERR(tegra->regs)) {
+ dev_err(&pdev->dev, "can't get registers");
+ return PTR_ERR(tegra->regs);
+ }
+
+ tegra->reset = devm_reset_control_get(&pdev->dev, "soc-therm");
+ if (IS_ERR(tegra->reset)) {
+ dev_err(&pdev->dev, "can't get soc-therm reset\n");
+ return PTR_ERR(tegra->reset);
+ }
+
+ tegra->clock_tsensor = devm_clk_get(&pdev->dev, "tsensor");
+ if (IS_ERR(tegra->clock_tsensor)) {
+ dev_err(&pdev->dev, "can't get clock tsensor\n");
+ return PTR_ERR(tegra->clock_tsensor);
+ }
+
+ tegra->clock_soctherm = devm_clk_get(&pdev->dev, "soc-therm");
+ if (IS_ERR(tegra->clock_soctherm)) {
+ dev_err(&pdev->dev, "can't get clock soc-therm\n");
+ return PTR_ERR(tegra->clock_soctherm);
+ }
+
+ reset_control_assert(tegra->reset);
+
+ err = clk_prepare_enable(tegra->clock_soctherm);
+ if (err) {
+ reset_control_deassert(tegra->reset);
+ return err;
+ }
+
+ err = clk_prepare_enable(tegra->clock_tsensor);
+ if (err) {
+ clk_disable_unprepare(tegra->clock_soctherm);
+ reset_control_deassert(tegra->reset);
+ return err;
+ }
+
+ reset_control_deassert(tegra->reset);
+
+ /* Initialize raw sensors */
+
+ err = calculate_shared_calibration(&shared_calib);
+ if (err)
+ goto disable_clocks;
+
+ for (i = 0; tsensors[i].name; ++i) {
+ err = enable_tsensor(tegra, tsensors + i, shared_calib);
+ if (err)
+ goto disable_clocks;
+ }
+
+ writel(SENSOR_PDIV_T124, tegra->regs + SENSOR_PDIV);
+ writel(SENSOR_HOTSPOT_OFF_T124, tegra->regs + SENSOR_HOTSPOT_OFF);
+
+ /* Initialize thermctl sensors */
+
+ for (i = 0; i < 4; ++i) {
+ struct tegra_thermctl_zone *zone =
+ devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL);
+ if (!zone) {
+ err = -ENOMEM;
+ goto unregister_tzs;
+ }
+
+ zone->sensor = i;
+ zone->tegra = tegra;
+
+ tz = thermal_zone_of_sensor_register(
+ &pdev->dev, i, zone, tegra_thermctl_get_temp, NULL);
+ if (IS_ERR(tz)) {
+ err = PTR_ERR(tz);
+ goto unregister_tzs;
+ }
+
+ tegra->thermctl_tzs[i] = tz;
+ }
+
+ val = 0;
+ val |= THERMTRIP_CTL_ANY_EN;
+
+ if (of_property_read_u32(
+ pdev->dev.of_node, "nvidia,thermtrip-threshold-cpu", &prop))
+ prop = THERMTRIP_DEFAULT_THRESHOLD;
+ val |= (s8)prop << THERMTRIP_CTL_CPU_SHIFT;
+
+ if (of_property_read_u32(
+ pdev->dev.of_node, "nvidia,thermtrip-threshold-gpu-mem", &prop))
+ prop = THERMTRIP_DEFAULT_THRESHOLD;
+ val |= (s8)prop << THERMTRIP_CTL_GPU_MEM_SHIFT;
+
+ if (of_property_read_u32(
+ pdev->dev.of_node, "nvidia,thermtrip-threshold-tsense", &prop))
+ prop = THERMTRIP_DEFAULT_THRESHOLD;
+ val |= (s8)prop << THERMTRIP_CTL_TSENSE_SHIFT;
+
+ writel(val, tegra->regs + THERMTRIP_CTL);
+
+ dev_info(&pdev->dev, "Thermal reset thresholds configured:\n"
+ " cpu %hhd gpu/mem %hhd tsense %hhd\n",
+ (s8)((val & THERMTRIP_CTL_CPU_MASK) >> THERMTRIP_CTL_CPU_SHIFT),
+ (s8)((val & THERMTRIP_CTL_GPU_MEM_MASK) >>
+ THERMTRIP_CTL_GPU_MEM_SHIFT),
+ (s8)((val & THERMTRIP_CTL_TSENSE_MASK) >>
+ THERMTRIP_CTL_TSENSE_SHIFT));
+
+ return 0;
+
+unregister_tzs:
+ for (--i; i >= 0; i--)
+ thermal_zone_of_sensor_unregister(&pdev->dev,
+ tegra->thermctl_tzs[i]);
+
+disable_clocks:
+ clk_disable_unprepare(tegra->clock_tsensor);
+ clk_disable_unprepare(tegra->clock_soctherm);
+
+ return err;
+}
+
+static int tegra_soctherm_remove(struct platform_device *pdev)
+{
+ struct tegra_soctherm *tegra = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tegra->thermctl_tzs); ++i) {
+ thermal_zone_of_sensor_unregister(&pdev->dev,
+ tegra->thermctl_tzs[i]);
+ }
+
+ clk_disable_unprepare(tegra->clock_tsensor);
+ clk_disable_unprepare(tegra->clock_soctherm);
+
+ return 0;
+}
+
+static struct platform_driver tegra_soctherm_driver = {
+ .probe = tegra_soctherm_probe,
+ .remove = tegra_soctherm_remove,
+ .driver = {
+ .name = "tegra_soctherm",
+ .owner = THIS_MODULE,
+ .of_match_table = tegra_soctherm_of_match,
+ },
+};
+module_platform_driver(tegra_soctherm_driver);
+
+MODULE_AUTHOR("Mikko Perttunen <[email protected]>");
+MODULE_DESCRIPTION("Tegra SOCTHERM thermal management driver");
+MODULE_LICENSE("GPL v2");
--
1.8.1.5

2014-06-19 11:53:45

by Mikko Perttunen

[permalink] [raw]
Subject: [RFC 2/5] ARM: tegra: Add soctherm and thermal zones to Tegra124 device tree

This adds the soctherm thermal sensing and management unit to the
Tegra124 device tree along with the four thermal zones it exports.

Signed-off-by: Mikko Perttunen <[email protected]>
---
arch/arm/boot/dts/tegra124.dtsi | 47 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)

diff --git a/arch/arm/boot/dts/tegra124.dtsi b/arch/arm/boot/dts/tegra124.dtsi
index d675186..ca884e8 100644
--- a/arch/arm/boot/dts/tegra124.dtsi
+++ b/arch/arm/boot/dts/tegra124.dtsi
@@ -724,6 +724,53 @@
status = "disabled";
};

+ thermal-zones {
+ cpu {
+ polling-delay-passive = <1000>;
+ polling-delay = <1000>;
+
+ thermal-sensors = <&soctherm 0>;
+ };
+
+ mem {
+ polling-delay-passive = <1000>;
+ polling-delay = <1000>;
+
+ thermal-sensors = <&soctherm 1>;
+ };
+
+ gpu {
+ polling-delay-passive = <1000>;
+ polling-delay = <1000>;
+
+ thermal-sensors = <&soctherm 2>;
+ };
+
+ pllx {
+ polling-delay-passive = <1000>;
+ polling-delay = <1000>;
+
+ thermal-sensors = <&soctherm 3>;
+ };
+ };
+
+ soctherm: soctherm@0,700e2000 {
+ compatible = "nvidia,tegra124-soctherm";
+ reg = <0x0 0x700e2000 0x0 0x1000>;
+ interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&tegra_car TEGRA124_CLK_TSENSOR>,
+ <&tegra_car TEGRA124_CLK_SOC_THERM>;
+ clock-names = "tsensor", "soc-therm";
+ resets = <&tegra_car 78>;
+ reset-names = "soc-therm";
+
+ nvidia,thermtrip-threshold-cpu = <105>;
+ nvidia,thermtrip-threshold-gpu-mem = <105>;
+ nvidia,thermtrip-threshold-tsense = <105>;
+
+ #thermal-sensor-cells = <1>;
+ };
+
cpus {
#address-cells = <1>;
#size-cells = <0>;
--
1.8.1.5