soc: tegra: Add thermal reset (thermtrip) support to PMC
authorMikko Perttunen <mperttunen@nvidia.com>
Tue, 6 Jan 2015 10:52:58 +0000 (12:52 +0200)
committerThierry Reding <treding@nvidia.com>
Fri, 23 Jan 2015 15:20:49 +0000 (16:20 +0100)
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>
drivers/soc/tegra/pmc.c

index 0f096e76574a207b38e2090af877e78de18fa698..c956395cf46f961beaf6f56674d20f58b2c717b6 100644 (file)
 
 #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)
 #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,
 };