arm: tegra: soctherm: Fix setting hi-thresh for passive trips
[linux-2.6.git] / arch / arm / mach-tegra / tegra11_soctherm.c
index 7b626e6..e5a2abd 100644 (file)
 #include <mach/tegra_fuse.h>
 #include <mach/iomap.h>
 
+#include "tegra3_tsensor.h"
 #include "tegra11_soctherm.h"
 
 /* Min temp granularity specified as X in 2^X.
- * -1: Hi precision option: 2^-1 = 0.5C (default)
- *  0: Lo precision option: 2^0  = 1.0C (for use if therm_a/b overflows 16 bits)
+ * -1: Hi precision option: 2^-1 = 0.5C
+ *  0: Lo precision option: 2^0  = 1.0C
+ *  NB: We must use lower precision (0) due to cp_fuse corrections
+ *  (see Sec9.2 T35_Thermal_Sensing_IAS.docx)
  */
-static int soc_therm_precision = -1;
+static const int soc_therm_precision; /* default 0 -> low precision */
 
 #define TS_TSENSE_REGS_SIZE            0x20
 #define TS_TSENSE_REG_OFFSET(reg, ts)  ((reg) + ((ts) * TS_TSENSE_REGS_SIZE))
@@ -245,6 +248,43 @@ static int soc_therm_precision = -1;
 #define FUSE_TSENSOR_CALIB_CP_MASK     0x1fff
 #define FUSE_TSENSOR_CALIB_BITS                13
 
+/* car register offsets needed for enabling HW throttling */
+#define CAR_SUPER_CCLK_DIVIDER         0x24
+#define CDIV_USE_THERM_CONTROLS_SHIFT  30
+#define CDIV_USE_THERM_CONTROLS_MASK   0x1
+
+/* pmc register offsets needed for powering off PMU */
+#define PMC_SCRATCH_WRITE_SHIFT                        2
+#define PMC_SCRATCH_WRITE_MASK                 0x1
+#define PMC_ENABLE_RST_SHIFT                   1
+#define PMC_ENABLE_RST_MASK                    0x1
+#define PMC_SENSOR_CTRL                                0x1B0
+#define PMC_SCRATCH54                          0x258
+#define PMC_SCRATCH55                          0x25C
+
+/* scratch54 register bit fields */
+#define PMU_OFF_DATA_SHIFT                     8
+#define PMU_OFF_DATA_MASK                      0xff
+#define PMU_OFF_ADDR_SHIFT                     0
+#define PMU_OFF_ADDR_MASK                      0xff
+
+/* scratch55 register bit fields */
+#define RESET_TEGRA_SHIFT                      31
+#define RESET_TEGRA_MASK                       0x1
+#define CONTROLLER_TYPE_SHIFT                  30
+#define CONTROLLER_TYPE_MASK                   0x1
+#define I2C_CONTROLLER_ID_SHIFT                        27
+#define I2C_CONTROLLER_ID_MASK                 0x7
+#define PINMUX_SHIFT                           24
+#define PINMUX_MASK                            0x7
+#define CHECKSUM_SHIFT                         16
+#define CHECKSUM_MASK                          0xff
+#define PMU_16BIT_SUPPORT_SHIFT                        15
+#define PMU_16BIT_SUPPORT_MASK                 0x1
+#define PMU_I2C_ADDRESS_SHIFT                  0
+#define PMU_I2C_ADDRESS_MASK                   0x7f
+
+
 #define PSKIP_CTRL_OC1_CPU                     0x490
 
 #define THROT_PSKIP_CTRL(throt, dev)           (THROT_PSKIP_CTRL_LITE_CPU + \
@@ -260,9 +300,9 @@ static int soc_therm_precision = -1;
 #define REG_GET(r, _name)      (REG_GET_BIT(r, _name) >> _name##_SHIFT)
 #define MAKE_SIGNED32(val, nb) ((s32)(val) << (32 - (nb)) >> (32 - (nb)))
 
-static void __iomem *reg_soctherm_base = IO_ADDRESS(TEGRA_SOCTHERM_BASE);
-static void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
-static void __iomem *clk_reset_base = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
+static const void __iomem *reg_soctherm_base = IO_ADDRESS(TEGRA_SOCTHERM_BASE);
+static const void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+static const void __iomem *clk_reset_base = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
 
 #define clk_reset_writel(value, reg) \
        __raw_writel(value, (u32)clk_reset_base + (reg))
@@ -301,14 +341,14 @@ static u32 fuse_calib_base_ft;
 static s32 actual_temp_cp;
 static s32 actual_temp_ft;
 
-static char *therm_names[] = {
+static const char *const therm_names[] = {
        [THERM_CPU] = "CPU",
        [THERM_MEM] = "MEM",
        [THERM_GPU] = "GPU",
        [THERM_PLL] = "PLL",
 };
 
-static char *sensor_names[] = {
+static const char *const sensor_names[] = {
        [TSENSE_CPU0] = "cpu0",
        [TSENSE_CPU1] = "cpu1",
        [TSENSE_CPU2] = "cpu2",
@@ -319,7 +359,7 @@ static char *sensor_names[] = {
        [TSENSE_PLLX] = "pllx",
 };
 
-static int sensor2tsensorcalib[] = {
+static const int sensor2tsensorcalib[] = {
        [TSENSE_CPU0] = 0,
        [TSENSE_CPU1] = 1,
        [TSENSE_CPU2] = 2,
@@ -330,7 +370,7 @@ static int sensor2tsensorcalib[] = {
        [TSENSE_PLLX] = 7,
 };
 
-static int tsensor2therm_map[] = {
+static const int tsensor2therm_map[] = {
        [TSENSE_CPU0] = THERM_CPU,
        [TSENSE_CPU1] = THERM_CPU,
        [TSENSE_CPU2] = THERM_CPU,
@@ -341,14 +381,14 @@ static int tsensor2therm_map[] = {
        [TSENSE_PLLX] = THERM_PLL,
 };
 
-static enum soctherm_throttle_dev_id therm2dev[] = {
+static const enum soctherm_throttle_dev_id therm2dev[] = {
        [THERM_CPU] = THROTTLE_DEV_CPU,
        [THERM_MEM] = -1,
        [THERM_GPU] = THROTTLE_DEV_GPU,
        [THERM_PLL] = -1,
 };
 
-struct soctherm_sensor sensor_defaults = {
+static const struct soctherm_sensor sensor_defaults = {
        .tall      = 16300,
        .tiddq     = 1,
        .ten_count = 1,
@@ -356,10 +396,10 @@ struct soctherm_sensor sensor_defaults = {
        .pdiv      = 10,
 };
 
-static unsigned long default_soctherm_clk_rate = 136000000;
-static unsigned long default_tsensor_clk_rate = 500000;
+static const unsigned long default_soctherm_clk_rate = 136000000;
+static const unsigned long default_tsensor_clk_rate = 500000;
 
-static struct soctherm_throttle_dev throttle_defaults[] = {
+static const struct soctherm_throttle_dev throttle_defaults[] = {
        [THROTTLE_LIGHT] = {
                .dividend = 229,        /* 20% throttling */
                .divisor  = 255,
@@ -391,10 +431,10 @@ static inline long temp_translate(int readback)
        int lsb = (readback & 0x80) >> 7;
        int sign = readback & 0x01 ? -1 : 1;
 
-       if (soc_therm_precision == -1)
-               return (abs * 1000 + lsb * 500) * sign;
-       else
+       if (!soc_therm_precision)
                return (abs * 2000 + lsb * 1000) * sign;
+       else
+               return (abs * 1000 + lsb * 500) * sign;
 }
 
 #ifdef CONFIG_THERMAL
@@ -405,6 +445,8 @@ static inline void prog_hw_shutdown(struct thermal_trip_info *trip_state,
        u32 r;
 
        trip_temp = trip_state->trip_temp / 1000;
+       if (!soc_therm_precision)
+               trip_temp /= 2;
 
        r = soctherm_readl(THERMTRIP);
        if (therm == THERM_CPU) {
@@ -424,6 +466,8 @@ static inline void prog_hw_threshold(struct thermal_trip_info *trip_state,
        u32 r, reg_off;
 
        trip_temp = trip_state->trip_temp / 1000;
+       if (!soc_therm_precision)
+               trip_temp /= 2;
 
        /* Hardcode LITE on level-1 and HEAVY on level-2 */
        reg_off = TS_THERM_REG_OFFSET(CTL_LVL0_CPU0, throt + 1, therm);
@@ -431,7 +475,8 @@ static inline void prog_hw_threshold(struct thermal_trip_info *trip_state,
        r = soctherm_readl(reg_off);
        r = REG_SET(r, CTL_LVL0_CPU0_UP_THRESH, trip_temp);
 
-       trip_state->hysteresis = trip_state->hysteresis ?: 2000;
+       trip_state->hysteresis = trip_state->hysteresis ?:
+               !soc_therm_precision ? 1000 : 2000;
        trip_temp -= (trip_state->hysteresis / 1000);
 
        r = REG_SET(r, CTL_LVL0_CPU0_DN_THRESH, trip_temp);
@@ -448,6 +493,12 @@ static int soctherm_set_limits(enum soctherm_therm_id therm,
                                long lo_limit, long hi_limit)
 {
        u32 r = soctherm_readl(CTL_LVL0_CPU0);
+
+       if (!soc_therm_precision) {
+               lo_limit /= 2;
+               hi_limit /= 2;
+       }
+
        r = REG_SET(r, CTL_LVL0_CPU0_DN_THRESH, lo_limit);
        r = REG_SET(r, CTL_LVL0_CPU0_UP_THRESH, hi_limit);
        soctherm_writel(r, CTL_LVL0_CPU0);
@@ -459,9 +510,10 @@ static int soctherm_set_limits(enum soctherm_therm_id therm,
 
 static void soctherm_update(void)
 {
-       long temp, trip_temp, low_temp = 0, high_temp = 128000;
+       long trip_temp, hyst_temp, low_temp = 0, high_temp = 128000;
        int i, count;
        enum thermal_trip_type trip_type;
+       struct thermal_trip_info *trip_state;
 
        if (!soctherm_init_platform_done)
                return;
@@ -472,22 +524,29 @@ static void soctherm_update(void)
 
                thermal_zone_device_update(thz[i]);
 
-               thz[i]->ops->get_temp(thz[i], &temp);
-
                for (count = 0; count < thz[i]->trips; count++) {
                        thz[i]->ops->get_trip_type(thz[i], count, &trip_type);
                        if ((trip_type == THERMAL_TRIP_HOT) ||
                            (trip_type == THERMAL_TRIP_CRITICAL))
                                continue; /* handled in HW */
 
-                       thz[i]->ops->get_trip_temp(thz[i], count, &trip_temp);
+                       trip_state = &plat_data.therm[i].trips[count];
+                       trip_temp = trip_state->trip_temp;
+
+                       hyst_temp = trip_temp - trip_state->hysteresis;
+                       if (trip_type == THERMAL_TRIP_PASSIVE) {
+                               high_temp = trip_temp;
+                               if (!trip_state->tripped)
+                                       hyst_temp = trip_temp;
+                       }
 
-                       if ((trip_temp >= temp) && (trip_temp < high_temp))
+                       if ((trip_temp >= thz[i]->temperature) &&
+                           (trip_temp < high_temp))
                                high_temp = trip_temp;
 
-                       if ((trip_temp < temp) && (trip_temp > low_temp))
-                               low_temp = trip_temp -
-                                   plat_data.therm[i].trips[count].hysteresis;
+                       if ((hyst_temp < thz[i]->temperature) &&
+                           (hyst_temp > low_temp))
+                               low_temp = hyst_temp;
                }
        }
 
@@ -611,8 +670,6 @@ static int soctherm_get_temp(struct thermal_zone_device *thz,
                        *temp = temp_translate(REG_GET(r, TS_TEMP1_CPU_TEMP));
                else
                        *temp = temp_translate(REG_GET(r, TS_TEMP1_GPU_TEMP));
-
-               plat_data.therm[index].etemp = *temp;
        }
 
        return 0;
@@ -643,6 +700,17 @@ static int soctherm_get_trip_temp(struct thermal_zone_device *thz,
 
        trip_state = &plat_data.therm[index].trips[trip];
        *temp = trip_state->trip_temp;
+
+       if (trip_state->trip_type != THERMAL_TRIP_PASSIVE)
+               return 0;
+
+       if (thz->temperature >= *temp) {
+               trip_state->tripped = true;
+       } else if (trip_state->tripped) {
+               *temp -= trip_state->hysteresis;
+               if (thz->temperature < *temp)
+                       trip_state->tripped = false;
+       }
        return 0;
 }
 
@@ -651,6 +719,7 @@ static int soctherm_set_trip_temp(struct thermal_zone_device *thz,
 {
        int index = ((int)thz->devdata) - TSENSE_SIZE;
        struct thermal_trip_info *trip_state;
+       long rem;
 
        if (index < 0)
                return -EINVAL;
@@ -658,6 +727,13 @@ static int soctherm_set_trip_temp(struct thermal_zone_device *thz,
        trip_state = &plat_data.therm[index].trips[trip];
        trip_state->trip_temp = temp;
 
+       rem = trip_state->trip_temp % (!soc_therm_precision ? 2000 : 1000);
+       if (rem) {
+               pr_warn("soctherm: zone%d/trip_point%d %ld mC rounded down\n",
+                       index, trip, trip_state->trip_temp);
+               trip_state->trip_temp -= rem;
+       }
+
        if (trip_state->trip_type == THERMAL_TRIP_HOT) {
                if (strnstr(trip_state->cdev_type,
                                "heavy", THERMAL_NAME_LENGTH))
@@ -695,7 +771,7 @@ static int soctherm_get_trend(struct thermal_zone_device *thz,
                *trend = THERMAL_TREND_RAISING;
                break;
        case THERMAL_TRIP_PASSIVE:
-               if (plat_data.therm[index].etemp > trip_state->trip_temp)
+               if (thz->temperature > trip_state->trip_temp)
                        *trend = THERMAL_TREND_RAISING;
                else
                        *trend = THERMAL_TREND_DROPPING;
@@ -795,7 +871,7 @@ static int __init soctherm_thermal_sys_init(void)
                                        (1 << therm->num_trips) - 1,
                                        (void *)TSENSE_SIZE + i,
                                        &soctherm_ops,
-                                       NULL,
+                                       therm->tzp,
                                        therm->passive_delay,
                                        0);
        }
@@ -980,12 +1056,30 @@ static void soctherm_fuse_read_vsensor(void)
        actual_temp_ft = 2 * 90 + calib_ft;
 
        /* adjust: for LO precision: use fuse_temp in 1C */
-       if (soc_therm_precision != -1) {
+       if (!soc_therm_precision) {
                actual_temp_cp /= 2;
                actual_temp_ft /= 2;
        }
 }
 
+static int fuse_corr_alpha[] = { /* scaled *1000000 */
+       [TSENSE_CPU0] = 1196400,
+       [TSENSE_CPU1] = 1196400,
+       [TSENSE_CPU2] = 1196400,
+       [TSENSE_CPU3] = 1196400,
+       [TSENSE_GPU]  = 1124500,
+       [TSENSE_PLLX] = 1224200,
+};
+
+static int fuse_corr_beta[] = { /* scaled *1000000 */
+       [TSENSE_CPU0] = -13600000,
+       [TSENSE_CPU1] = -13600000,
+       [TSENSE_CPU2] = -13600000,
+       [TSENSE_CPU3] = -13600000,
+       [TSENSE_GPU]  =  -9793100,
+       [TSENSE_PLLX] = -14665000,
+};
+
 static void soctherm_fuse_read_tsensor(enum soctherm_sense sensor)
 {
        u32 r, value;
@@ -1017,16 +1111,64 @@ static void soctherm_fuse_read_tsensor(enum soctherm_sense sensor)
                                     ((s64)actual_tsensor_cp * actual_temp_ft)),
                                    (s64)delta_sens);
 
+       if (!soc_therm_precision) {
+               /* cp_fuse corrections */
+               fuse_corr_alpha[sensor] = fuse_corr_alpha[sensor] ?: 1000000;
+               therm_a = div64_s64_precise(
+                               (s64)therm_a * fuse_corr_alpha[sensor],
+                               (s64)1000000LL);
+               therm_b = div64_s64_precise(
+                               (s64)therm_b * fuse_corr_alpha[sensor] +
+                               fuse_corr_beta[sensor], (s64)1000000LL);
+       }
+
        r = REG_SET(0, TS_CPU0_CONFIG2_THERM_A, therm_a);
        r = REG_SET(r, TS_CPU0_CONFIG2_THERM_B, therm_b);
        soctherm_writel(r, TS_TSENSE_REG_OFFSET(TS_CPU0_CONFIG2, sensor));
 }
 
+static void soctherm_therm_trip_init(struct tegra_tsensor_pmu_data *data)
+{
+       u32 val, checksum;
+
+       if (!data)
+               return;
+
+       val = pmc_readl(PMC_SENSOR_CTRL);
+       val = REG_SET(val, PMC_SCRATCH_WRITE, 1);
+       val = REG_SET(val, PMC_ENABLE_RST, 1);
+       pmc_writel(val, PMC_SENSOR_CTRL);
+
+       /* Fill scratch registers to shutdown device on therm TRIP */
+       val = REG_SET(0, PMU_OFF_DATA, data->poweroff_reg_data);
+       val = REG_SET(val, PMU_OFF_ADDR, data->poweroff_reg_addr);
+       pmc_writel(val, PMC_SCRATCH54);
+
+       val = REG_SET(0, RESET_TEGRA, 1);
+       val = REG_SET(val, CONTROLLER_TYPE, data->controller_type);
+       val = REG_SET(val, I2C_CONTROLLER_ID, data->i2c_controller_id);
+       val = REG_SET(val, PINMUX, data->pinmux);
+       val = REG_SET(val, PMU_16BIT_SUPPORT, data->pmu_16bit_ops);
+       val = REG_SET(val, PMU_I2C_ADDRESS, data->pmu_i2c_addr);
+
+       checksum = data->poweroff_reg_addr +
+               data->poweroff_reg_data +
+               (val & 0xFF) +
+               ((val >> 8) & 0xFF) +
+               ((val >> 24) & 0xFF);
+       checksum &= 0xFF;
+       checksum = 0x100 - checksum;
+
+       val = REG_SET(val, CHECKSUM, checksum);
+       pmc_writel(val, PMC_SCRATCH55);
+}
+
 static int soctherm_init_platform_data(void)
 {
        struct soctherm_therm *therm;
        struct soctherm_sensor *s;
        int i, j, k;
+       long rem;
        u32 r;
 
        /* initialize default values for unspecified params */
@@ -1059,6 +1201,24 @@ static int soctherm_init_platform_data(void)
                }
        }
 
+       /* Sanitize therm trips */
+       for (i = 0; i < THERM_SIZE; i++) {
+               therm = &plat_data.therm[i];
+               if (!therm->zone_enable)
+                       continue;
+
+               for (j = 0; j < therm->num_trips; j++) {
+                       rem = therm->trips[j].trip_temp %
+                               (!soc_therm_precision ? 2000 : 1000);
+                       if (rem) {
+                               pr_warn(
+                       "soctherm: zone%d/trip_point%d %ld mC rounded down\n",
+                                       i, j, therm->trips[j].trip_temp);
+                               therm->trips[j].trip_temp -= rem;
+                       }
+               }
+       }
+
        /* Sanitize HW throttle priority */
        for (i = 0; i < THROTTLE_SIZE; i++)
                if (!plat_data.throttle[i].priority)
@@ -1097,6 +1257,13 @@ static int soctherm_init_platform_data(void)
        r = REG_SET(r, CTL_LVL0_CPU0_EN, 1);
        soctherm_writel(r, CTL_LVL0_CPU0);
 
+       /* Enable PMC to shutdown */
+       soctherm_therm_trip_init(plat_data.tshut_pmu_trip_data);
+
+       r = clk_reset_readl(CAR_SUPER_CCLK_DIVIDER);
+       r = REG_SET(r, CDIV_USE_THERM_CONTROLS, 1);
+       clk_reset_writel(r, 0x24);
+
        /* Thermtrip */
        for (i = 0; i < THERM_SIZE; i++) {
                therm = &plat_data.therm[i];
@@ -1108,15 +1275,6 @@ static int soctherm_init_platform_data(void)
                                prog_hw_shutdown(&therm->trips[j], i);
        }
 
-       /* Enable PMC to shutdown */
-       r = pmc_readl(0x1b0);
-       r |= 0x2;
-       pmc_writel(r, 0x1b0);
-
-       r = clk_reset_readl(0x24);
-       r |= (1 << 30);
-       clk_reset_writel(r, 0x24);
-
        return 0;
 }
 
@@ -1200,7 +1358,7 @@ static int regs_show(struct seq_file *s, void *data)
        int i, level;
 
        seq_printf(s, "-----TSENSE (precision %s)-----\n",
-                  soc_therm_precision == -1 ? "Hi" : "Lo");
+                  !soc_therm_precision ? "Lo" : "Hi");
        for (i = 0; i < TSENSE_SIZE; i++) {
                r = soctherm_readl(TS_TSENSE_REG_OFFSET(TS_CPU0_CONFIG1, i));
                state = REG_GET(r, TS_CPU0_CONFIG1_EN);
@@ -1274,9 +1432,11 @@ static int regs_show(struct seq_file *s, void *data)
                        r = soctherm_readl(TS_THERM_REG_OFFSET(CTL_LVL0_CPU0,
                                                                level, i));
                        state = REG_GET(r, CTL_LVL0_CPU0_UP_THRESH);
-                       seq_printf(s, "   %d: Up/Dn(%d/", level, state);
+                       seq_printf(s, "   %d: Up/Dn(%d/", level,
+                                  !soc_therm_precision ? state * 2 : state);
                        state = REG_GET(r, CTL_LVL0_CPU0_DN_THRESH);
-                       seq_printf(s, "%d) ", state);
+                       seq_printf(s, "%d) ",
+                                  !soc_therm_precision ? state * 2 : state);
                        state = REG_GET(r, CTL_LVL0_CPU0_EN);
                        seq_printf(s, "En(%d) ", state);
                        state = REG_GET(r, CTL_LVL0_CPU0_CPU_THROT);
@@ -1312,9 +1472,20 @@ static int regs_show(struct seq_file *s, void *data)
 
        r = soctherm_readl(THERMTRIP);
        state = REG_GET(r, THERMTRIP_CPU_THRESH);
-       seq_printf(s, "THERMTRIP_CPU_THRESH: %d ", state);
+       seq_printf(s, "THERMTRIP_CPU_THRESH: %d ",
+                  !soc_therm_precision ? state * 2 : state);
        state = REG_GET(r, THERMTRIP_CPU_EN);
        seq_printf(s, "%d\n", state);
+       state = REG_GET(r, THERMTRIP_TSENSE_THRESH);
+       seq_printf(s, "THERMTRIP_TSENSE_THRESH: %d ", state);
+       state = REG_GET(r, THERMTRIP_TSENSE_EN);
+       seq_printf(s, "%d\n", state);
+       state = REG_GET(r, THERMTRIP_GPUMEM_THRESH);
+       seq_printf(s, "THERMTRIP_GPUMEM_THRESH: %d ", state);
+       state = REG_GET(r, THERMTRIP_GPU_EN);
+       seq_printf(s, "gpu=%d ", state);
+       state = REG_GET(r, THERMTRIP_MEM_EN);
+       seq_printf(s, "mem=%d\n", state);
 
 
        seq_printf(s, "\n-----THROTTLE-----\n");