diff mbox

[v5,3/3] ARM: tegra: Add thermal reset (thermtrip) support to PMC

Message ID 1420541578-27968-4-git-send-email-mikko.perttunen@kapsi.fi (mailing list archive)
State New, archived
Headers show

Commit Message

Mikko Perttunen Jan. 6, 2015, 10:52 a.m. UTC
From: Mikko Perttunen <mperttunen@nvidia.com>

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

The thermtrip event is triggered by the soctherm block, and all
soctherm sensors default to showing a temperature of zero Celsius
before they are initialized. Because of this, it is safe to initialize
thermtrip and soctherm in any order.

Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
 drivers/soc/tegra/pmc.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 107 insertions(+)

Comments

Thierry Reding Jan. 6, 2015, 3:14 p.m. UTC | #1
On Tue, Jan 06, 2015 at 12:52:58PM +0200, Mikko Perttunen wrote:
> From: Mikko Perttunen <mperttunen@nvidia.com>
> 
> This adds a device tree controlled option to enable PMC-based
> thermal reset in overheating situations. Thermtrip is supported on
> Tegra30, Tegra114 and Tegra124. The thermal reset only works when
> the thermal sensors are calibrated, so a soctherm driver is also
> required.
> 
> The thermtrip event is triggered by the soctherm block, and all
> soctherm sensors default to showing a temperature of zero Celsius
> before they are initialized. Because of this, it is safe to initialize
> thermtrip and soctherm in any order.
> 
> Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
> ---
>  drivers/soc/tegra/pmc.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 107 insertions(+)

This looks good to me. I'm not sure if I asked before, but can this be
tested? Can we somehow trick thermtrip into reporting a temperature that
would trigger this?

Thierry
Mikko Perttunen Jan. 6, 2015, 3:56 p.m. UTC | #2
On 01/06/2015 05:14 PM, Thierry Reding wrote:
> On Tue, Jan 06, 2015 at 12:52:58PM +0200, Mikko Perttunen wrote:
>> From: Mikko Perttunen <mperttunen@nvidia.com>
>>
>> This adds a device tree controlled option to enable PMC-based
>> thermal reset in overheating situations. Thermtrip is supported on
>> Tegra30, Tegra114 and Tegra124. The thermal reset only works when
>> the thermal sensors are calibrated, so a soctherm driver is also
>> required.
>>
>> The thermtrip event is triggered by the soctherm block, and all
>> soctherm sensors default to showing a temperature of zero Celsius
>> before they are initialized. Because of this, it is safe to initialize
>> thermtrip and soctherm in any order.
>>
>> Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
>> ---
>>   drivers/soc/tegra/pmc.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++
>>   1 file changed, 107 insertions(+)
>
> This looks good to me. I'm not sure if I asked before, but can this be
> tested? Can we somehow trick thermtrip into reporting a temperature that
> would trigger this?
>
> Thierry
>

Yep, you can find a program here: 
https://gist.github.com/cyndis/66126c9c176b5f94a76f

It sets the thermtrip threshold to zero degrees.

Mikko
diff mbox

Patch

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index a2c0ceb..9ed8be5 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -83,11 +83,28 @@ 
 
 #define GPU_RG_CNTRL			0x2d4
 
+#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
+
 struct tegra_pmc_soc {
 	unsigned int num_powergates;
 	const char *const *powergates;
 	unsigned int num_cpu_powergates;
 	const u8 *cpu_powergates;
+	bool has_tsense_reset;
 };
 
 /**
@@ -703,6 +720,90 @@  static void tegra_pmc_init(struct tegra_pmc *pmc)
 	tegra_pmc_writel(value, PMC_CNTRL);
 }
 
+static const struct of_device_id tegra_pmc_match[];
+
+void tegra_pmc_init_tsense_reset(struct device *dev)
+{
+	u32 pmu_i2c_addr, i2c_ctrl_id, reg_addr, reg_data, pinmux;
+	u32 value, checksum;
+	struct device_node *np = dev->of_node;
+	struct device_node *tt_np;
+	const struct of_device_id *match = of_match_node(tegra_pmc_match, np);
+	const struct tegra_pmc_soc *data = match->data;
+
+	if (!data->has_tsense_reset)
+		return;
+
+	tt_np = of_find_node_by_name(np, "i2c-thermtrip");
+	if (!tt_np) {
+		dev_warn(dev, "no i2c-thermtrip node found, disabling emergency thermal reset\n");
+		return;
+	}
+
+	if (of_property_read_u32(tt_np, "nvidia,i2c-controller-id", &i2c_ctrl_id)) {
+		dev_err(dev, "I2C bus controller id missing, disabling emergency thermal reset\n");
+		goto put_tt;
+	}
+
+	if (of_property_read_u32(tt_np, "nvidia,bus-addr", &pmu_i2c_addr)) {
+		dev_err(dev, "nvidia,bus-addr missing, disabling emergency thermal reset\n");
+		goto put_tt;
+	}
+
+	if (of_property_read_u32(tt_np, "nvidia,reg-addr", &reg_addr)) {
+		dev_err(dev, "nvidia,reg-addr missing, disabling emergency thermal reset\n");
+		goto put_tt;
+	}
+
+	if (of_property_read_u32(tt_np, "nvidia,reg-data", &reg_data)) {
+		dev_err(dev, "nvidia,reg-data missing, disabling emergency thermal reset\n");
+		goto put_tt;
+	}
+
+	if (of_property_read_u32(tt_np, "nvidia,pinmux-id", &pinmux))
+		pinmux = 0;
+
+	of_node_put(tt_np);
+
+	value = tegra_pmc_readl(PMC_SENSOR_CTRL);
+	value |= PMC_SENSOR_CTRL_SCRATCH_WRITE;
+	tegra_pmc_writel(value, PMC_SENSOR_CTRL);
+
+	value = (reg_data << PMC_SCRATCH54_DATA_SHIFT) |
+	      (reg_addr << PMC_SCRATCH54_ADDR_SHIFT);
+	tegra_pmc_writel(value, PMC_SCRATCH54);
+
+	value = 0;
+	value |= PMC_SCRATCH55_RESET_TEGRA;
+	value |= i2c_ctrl_id << PMC_SCRATCH55_CNTRL_ID_SHIFT;
+	value |= pinmux << PMC_SCRATCH55_PINMUX_SHIFT;
+	value |= pmu_i2c_addr << PMC_SCRATCH55_I2CSLV1_SHIFT;
+
+	/* Calculate checksum of SCRATCH54, SCRATCH55 fields.
+	 * Bits 23:16 will contain the checksum and are currently zero,
+	 * so they are not added.
+	 */
+	checksum = reg_addr + reg_data + (value & 0xff) + ((value >> 8) & 0xff)
+		+ ((value >> 24) & 0xff);
+	checksum &= 0xff;
+	checksum = 0x100 - checksum;
+
+	value |= checksum << PMC_SCRATCH55_CHECKSUM_SHIFT;
+
+	tegra_pmc_writel(value, PMC_SCRATCH55);
+
+	value = tegra_pmc_readl(PMC_SENSOR_CTRL);
+	value |= PMC_SENSOR_CTRL_ENABLE_RST;
+	tegra_pmc_writel(value, PMC_SENSOR_CTRL);
+
+	dev_info(dev, "PMC emergency thermal reset enabled\n");
+
+	return;
+
+put_tt:
+	of_node_put(tt_np);
+}
+
 static int tegra_pmc_probe(struct platform_device *pdev)
 {
 	void __iomem *base = pmc->base;
@@ -730,6 +831,8 @@  static int tegra_pmc_probe(struct platform_device *pdev)
 
 	tegra_pmc_init(pmc);
 
+	tegra_pmc_init_tsense_reset(&pdev->dev);
+
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
 		err = tegra_powergate_debugfs_init();
 		if (err < 0)
@@ -772,6 +875,7 @@  static const struct tegra_pmc_soc tegra20_pmc_soc = {
 	.powergates = tegra20_powergates,
 	.num_cpu_powergates = 0,
 	.cpu_powergates = NULL,
+	.has_tsense_reset = false,
 };
 
 static const char * const tegra30_powergates[] = {
@@ -803,6 +907,7 @@  static const struct tegra_pmc_soc tegra30_pmc_soc = {
 	.powergates = tegra30_powergates,
 	.num_cpu_powergates = ARRAY_SIZE(tegra30_cpu_powergates),
 	.cpu_powergates = tegra30_cpu_powergates,
+	.has_tsense_reset = true,
 };
 
 static const char * const tegra114_powergates[] = {
@@ -838,6 +943,7 @@  static const struct tegra_pmc_soc tegra114_pmc_soc = {
 	.powergates = tegra114_powergates,
 	.num_cpu_powergates = ARRAY_SIZE(tegra114_cpu_powergates),
 	.cpu_powergates = tegra114_cpu_powergates,
+	.has_tsense_reset = true,
 };
 
 static const char * const tegra124_powergates[] = {
@@ -879,6 +985,7 @@  static const struct tegra_pmc_soc tegra124_pmc_soc = {
 	.powergates = tegra124_powergates,
 	.num_cpu_powergates = ARRAY_SIZE(tegra124_cpu_powergates),
 	.cpu_powergates = tegra124_cpu_powergates,
+	.has_tsense_reset = true,
 };
 
 static const struct of_device_id tegra_pmc_match[] = {