summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikko Perttunen <mperttunen@nvidia.com>2015-01-06 12:52:58 +0200
committerThierry Reding <treding@nvidia.com>2015-01-23 16:20:49 +0100
commit3568df3d31d62b4368830cc6aac868cecdefd187 (patch)
treedbc08541aa9608ba4da691c7d00d174107abfc13
parent9c963301538f857495d8a168de510d511a28d759 (diff)
soc: 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 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> Signed-off-by: Thierry Reding <treding@nvidia.com>
-rw-r--r--drivers/soc/tegra/pmc.c103
1 files changed, 103 insertions, 0 deletions
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 0f096e76574a..c956395cf46f 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -70,6 +70,10 @@
#define PMC_SCRATCH41 0x140
+#define PMC_SENSOR_CTRL 0x1b0
+#define PMC_SENSOR_CTRL_SCRATCH_WRITE (1 << 2)
+#define PMC_SENSOR_CTRL_ENABLE_RST (1 << 1)
+
#define IO_DPD_REQ 0x1b8
#define IO_DPD_REQ_CODE_IDLE (0 << 30)
#define IO_DPD_REQ_CODE_OFF (1 << 30)
@@ -81,6 +85,18 @@
#define IO_DPD2_STATUS 0x1c4
#define SEL_DPD_TIM 0x1c8
+#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
+
#define GPU_RG_CNTRL 0x2d4
struct tegra_pmc_soc {
@@ -89,6 +105,7 @@ struct tegra_pmc_soc {
unsigned int num_cpu_powergates;
const u8 *cpu_powergates;
+ bool has_tsense_reset;
bool has_gpu_clamps;
};
@@ -112,6 +129,7 @@ struct tegra_pmc_soc {
* @powergates_lock: mutex for power gate register access
*/
struct tegra_pmc {
+ struct device *dev;
void __iomem *base;
struct clk *clk;
@@ -705,6 +723,83 @@ static void tegra_pmc_init(struct tegra_pmc *pmc)
tegra_pmc_writel(value, PMC_CNTRL);
}
+void tegra_pmc_init_tsense_reset(struct tegra_pmc *pmc)
+{
+ static const char disabled[] = "emergency thermal reset disabled";
+ u32 pmu_addr, ctrl_id, reg_addr, reg_data, pinmux;
+ struct device *dev = pmc->dev;
+ struct device_node *np;
+ u32 value, checksum;
+
+ if (!pmc->soc->has_tsense_reset)
+ goto out;
+
+ np = of_find_node_by_name(pmc->dev->of_node, "i2c-thermtrip");
+ if (!np) {
+ dev_warn(dev, "i2c-thermtrip node not found, %s.\n", disabled);
+ goto out;
+ }
+
+ if (of_property_read_u32(np, "nvidia,i2c-controller-id", &ctrl_id)) {
+ dev_err(dev, "I2C controller ID missing, %s.\n", disabled);
+ goto out;
+ }
+
+ if (of_property_read_u32(np, "nvidia,bus-addr", &pmu_addr)) {
+ dev_err(dev, "nvidia,bus-addr missing, %s.\n", disabled);
+ goto out;
+ }
+
+ if (of_property_read_u32(np, "nvidia,reg-addr", &reg_addr)) {
+ dev_err(dev, "nvidia,reg-addr missing, %s.\n", disabled);
+ goto out;
+ }
+
+ if (of_property_read_u32(np, "nvidia,reg-data", &reg_data)) {
+ dev_err(dev, "nvidia,reg-data missing, %s.\n", disabled);
+ goto out;
+ }
+
+ if (of_property_read_u32(np, "nvidia,pinmux-id", &pinmux))
+ pinmux = 0;
+
+ 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 = PMC_SCRATCH55_RESET_TEGRA;
+ value |= ctrl_id << PMC_SCRATCH55_CNTRL_ID_SHIFT;
+ value |= pinmux << PMC_SCRATCH55_PINMUX_SHIFT;
+ value |= pmu_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(pmc->dev, "emergency thermal reset enabled\n");
+
+out:
+ of_node_put(np);
+ return;
+}
+
static int tegra_pmc_probe(struct platform_device *pdev)
{
void __iomem *base = pmc->base;
@@ -730,8 +825,12 @@ static int tegra_pmc_probe(struct platform_device *pdev)
return err;
}
+ pmc->dev = &pdev->dev;
+
tegra_pmc_init(pmc);
+ tegra_pmc_init_tsense_reset(pmc);
+
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
err = tegra_powergate_debugfs_init();
if (err < 0)
@@ -775,6 +874,7 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = {
.powergates = tegra20_powergates,
.num_cpu_powergates = 0,
.cpu_powergates = NULL,
+ .has_tsense_reset = false,
.has_gpu_clamps = false,
};
@@ -807,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,
.has_gpu_clamps = false,
};
@@ -843,6 +944,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,
.has_gpu_clamps = false,
};
@@ -885,6 +987,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,
.has_gpu_clamps = true,
};