devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC 0/5] Tegra124 thermal management
@ 2014-06-19 11:50 Mikko Perttunen
  2014-06-19 11:50 ` [RFC 1/5] ARM: tegra: Add PMC thermtrip programming to Jetson TK1 device tree Mikko Perttunen
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Mikko Perttunen @ 2014-06-19 11:50 UTC (permalink / raw)
  To: swarren, thierry.reding, pdeschrijver, rui.zhang, edubezval
  Cc: devicetree, linux-tegra, linux-arm-kernel, linux-kernel, linux-pm,
	mikko.perttunen, mlongnecker, dtundlam, talho, Mikko Perttunen

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

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [RFC 1/5] ARM: tegra: Add PMC thermtrip programming to Jetson TK1 device tree
  2014-06-19 11:50 [RFC 0/5] Tegra124 thermal management Mikko Perttunen
@ 2014-06-19 11:50 ` Mikko Perttunen
  2014-06-19 11:50 ` [RFC 2/5] ARM: tegra: Add soctherm and thermal zones to Tegra124 " Mikko Perttunen
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Mikko Perttunen @ 2014-06-19 11:50 UTC (permalink / raw)
  To: swarren, thierry.reding, pdeschrijver, rui.zhang, edubezval
  Cc: devicetree, linux-tegra, linux-arm-kernel, linux-kernel, linux-pm,
	mikko.perttunen, mlongnecker, dtundlam, talho, Mikko Perttunen

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 <mperttunen@nvidia.com>
---
 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

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC 2/5] ARM: tegra: Add soctherm and thermal zones to Tegra124 device tree
  2014-06-19 11:50 [RFC 0/5] Tegra124 thermal management Mikko Perttunen
  2014-06-19 11:50 ` [RFC 1/5] ARM: tegra: Add PMC thermtrip programming to Jetson TK1 device tree Mikko Perttunen
@ 2014-06-19 11:50 ` Mikko Perttunen
       [not found] ` <1403178640-16052-1-git-send-email-mperttunen-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Mikko Perttunen @ 2014-06-19 11:50 UTC (permalink / raw)
  To: swarren, thierry.reding, pdeschrijver, rui.zhang, edubezval
  Cc: devicetree, linux-tegra, linux-arm-kernel, linux-kernel, linux-pm,
	mikko.perttunen, mlongnecker, dtundlam, talho, Mikko Perttunen

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 <mperttunen@nvidia.com>
---
 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

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC 3/5] ARM: tegra: Add thermal reset (thermtrip) support to PMC
       [not found] ` <1403178640-16052-1-git-send-email-mperttunen-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
@ 2014-06-19 11:50   ` Mikko Perttunen
  0 siblings, 0 replies; 6+ messages in thread
From: Mikko Perttunen @ 2014-06-19 11:50 UTC (permalink / raw)
  To: swarren-3lzwWm7+Weoh9ZMKESR00Q,
	thierry.reding-Re5JQEeQqe8AvxtiuMwx3w,
	pdeschrijver-DDmLM1+adcrQT0dZR+AlfA,
	rui.zhang-ral2JQCrhuEAvxtiuMwx3w,
	edubezval-Re5JQEeQqe8AvxtiuMwx3w
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-pm-u79uwXL29TY76Z2rM5mHXA, mikko.perttunen-/1wQRMveznE,
	mlongnecker-DDmLM1+adcrQT0dZR+AlfA,
	dtundlam-DDmLM1+adcrQT0dZR+AlfA, talho-DDmLM1+adcrQT0dZR+AlfA,
	Mikko Perttunen

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 <mperttunen-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 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

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC 4/5] clk: tegra: Add soctherm and tsensor clocks to T124 initialization table
  2014-06-19 11:50 [RFC 0/5] Tegra124 thermal management Mikko Perttunen
                   ` (2 preceding siblings ...)
       [not found] ` <1403178640-16052-1-git-send-email-mperttunen-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
@ 2014-06-19 11:50 ` Mikko Perttunen
  2014-06-19 11:50 ` [RFC 5/5] thermal: Add Tegra SOCTHERM thermal management driver Mikko Perttunen
  4 siblings, 0 replies; 6+ messages in thread
From: Mikko Perttunen @ 2014-06-19 11:50 UTC (permalink / raw)
  To: swarren, thierry.reding, pdeschrijver, rui.zhang, edubezval
  Cc: devicetree, linux-tegra, linux-arm-kernel, linux-kernel, linux-pm,
	mikko.perttunen, mlongnecker, dtundlam, talho, Mikko Perttunen

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 <mperttunen@nvidia.com>
---
 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

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC 5/5] thermal: Add Tegra SOCTHERM thermal management driver
  2014-06-19 11:50 [RFC 0/5] Tegra124 thermal management Mikko Perttunen
                   ` (3 preceding siblings ...)
  2014-06-19 11:50 ` [RFC 4/5] clk: tegra: Add soctherm and tsensor clocks to T124 initialization table Mikko Perttunen
@ 2014-06-19 11:50 ` Mikko Perttunen
  4 siblings, 0 replies; 6+ messages in thread
From: Mikko Perttunen @ 2014-06-19 11:50 UTC (permalink / raw)
  To: swarren, thierry.reding, pdeschrijver, rui.zhang, edubezval
  Cc: devicetree, linux-tegra, linux-arm-kernel, linux-kernel, linux-pm,
	mikko.perttunen, mlongnecker, dtundlam, talho, Mikko Perttunen

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 <mperttunen@nvidia.com>
---
 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 <mperttunen@nvidia.com>
+ *
+ * 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 <mperttunen@nvidia.com>");
+MODULE_DESCRIPTION("Tegra SOCTHERM thermal management driver");
+MODULE_LICENSE("GPL v2");
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2014-06-19 11:50 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-06-19 11:50 [RFC 0/5] Tegra124 thermal management Mikko Perttunen
2014-06-19 11:50 ` [RFC 1/5] ARM: tegra: Add PMC thermtrip programming to Jetson TK1 device tree Mikko Perttunen
2014-06-19 11:50 ` [RFC 2/5] ARM: tegra: Add soctherm and thermal zones to Tegra124 " Mikko Perttunen
     [not found] ` <1403178640-16052-1-git-send-email-mperttunen-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2014-06-19 11:50   ` [RFC 3/5] ARM: tegra: Add thermal reset (thermtrip) support to PMC Mikko Perttunen
2014-06-19 11:50 ` [RFC 4/5] clk: tegra: Add soctherm and tsensor clocks to T124 initialization table Mikko Perttunen
2014-06-19 11:50 ` [RFC 5/5] thermal: Add Tegra SOCTHERM thermal management driver Mikko Perttunen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).