ARM: tegra: emc: Derating support
Alex Waterman [Wed, 3 Jul 2013 21:55:14 +0000 (14:55 -0700)]
Add support for derating via keeping two separate sets of EMC tables.
One set is nominal, the other set is derated. Based on the temperature
reported by the DRAM the EMC thermal driver can specify which set of
tables the EMC driver should use when swapping frequencies.

This patch also adds support for a more graduated response to rising
temperature. The DRAM reports 3 levels of refresh and derating
requirements:

  0x4: Refresh x2
  0x5: Refresh x4
  0x6: Refresh x4 + derating

The particular combination of refresh modification and derating is now
picked based on the particular level of throttling necessary.

This feature is not imlpemented in older chips. The old approach is
maintained - just a 4x refresh timing for all temperature throttle
states.

Reviewed-on: http://git-master/r/252624
(cherry picked from commit 5073cf9a13344c7e9c35475bce17539615ec4956)
Change-Id: I16f0477a0b0eadc194c9f1f48a66a0bf098b0df3
Signed-off-by: Alex Waterman <alexw@nvidia.com>
Reviewed-on: http://git-master/r/257646
Reviewed-by: Simone Willett <swillett@nvidia.com>
Tested-by: Simone Willett <swillett@nvidia.com>

arch/arm/mach-tegra/tegra11_emc.c
arch/arm/mach-tegra/tegra14_emc.c
arch/arm/mach-tegra/tegra3_emc.c
arch/arm/mach-tegra/tegra_emc.h
arch/arm/mach-tegra/tegra_emc_therm.c
include/linux/platform_data/tegra_emc.h

index 8585164..5c154d6 100644 (file)
@@ -479,7 +479,9 @@ static inline void set_over_temp_timing(
        switch (state) {
        case DRAM_OVER_TEMP_NONE:
                break;
-       case DRAM_OVER_TEMP_REFRESH:
+       case DRAM_OVER_TEMP_REFRESH_X2:
+       case DRAM_OVER_TEMP_REFRESH_X4:
+       case DRAM_OVER_TEMP_THROTTLE:
                REFRESH_SPEEDUP(ref);
                REFRESH_SPEEDUP(pre_ref);
                REFRESH_SPEEDUP(dsr_cntrl);
@@ -1726,7 +1728,7 @@ int tegra_emc_set_over_temp_state(unsigned long state)
        if (dram_type != DRAM_TYPE_LPDDR2)
                return -ENODEV;
 
-       if (state > DRAM_OVER_TEMP_REFRESH)
+       if (state > DRAM_OVER_TEMP_THROTTLE)
                return -EINVAL;
 
        spin_lock_irqsave(&emc_access_lock, flags);
index 39d8fd9..a1231bb 100644 (file)
@@ -81,6 +81,7 @@ enum {
 #define EMC_CLK_SOURCE_SHIFT           29
 #define EMC_CLK_SOURCE_MASK            (0x7 << EMC_CLK_SOURCE_SHIFT)
 #define EMC_CLK_LOW_JITTER_ENABLE      (0x1 << 31)
+#define EMC_CLK_FORCE_CC_TRIGGER       (0x1 << 27)
 #define        EMC_CLK_MC_SAME_FREQ            (0x1 << 16)
 
 #define PMC_IO_DPD2_REQ                        0x1C0
@@ -271,6 +272,7 @@ static ktime_t clkchange_time;
 static int clkchange_delay = 100;
 
 static const struct tegra14_emc_table *tegra_emc_table;
+static const struct tegra14_emc_table *tegra_emc_table_derated;
 static int tegra_emc_table_size;
 
 static u32 dram_dev_num;
@@ -405,9 +407,12 @@ static inline void auto_cal_disable(void)
 static inline void set_over_temp_timing(
        const struct tegra14_emc_table *next_timing, unsigned long state)
 {
-#define REFRESH_SPEEDUP(val)                                           \
-       do {                                                            \
-               val = ((val) & 0xFFFF0000) | (((val) & 0xFFFF) >> 2);   \
+#define REFRESH_X2     1
+#define REFRESH_X4     2
+#define REFRESH_SPEEDUP(val, speedup)                          \
+       do {                                                    \
+               val = ((val) & 0xFFFF0000) |                    \
+                       (((val) & 0xFFFF) >> (speedup));        \
        } while (0)
 
        u32 ref = next_timing->burst_regs[EMC_REFRESH_INDEX];
@@ -417,10 +422,16 @@ static inline void set_over_temp_timing(
        switch (state) {
        case DRAM_OVER_TEMP_NONE:
                break;
-       case DRAM_OVER_TEMP_REFRESH:
-               REFRESH_SPEEDUP(ref);
-               REFRESH_SPEEDUP(pre_ref);
-               REFRESH_SPEEDUP(dsr_cntrl);
+       case DRAM_OVER_TEMP_REFRESH_X2:
+               REFRESH_SPEEDUP(ref, REFRESH_X2);
+               REFRESH_SPEEDUP(pre_ref, REFRESH_X2);
+               REFRESH_SPEEDUP(dsr_cntrl, REFRESH_X2);
+               break;
+       case DRAM_OVER_TEMP_REFRESH_X4:
+       case DRAM_OVER_TEMP_THROTTLE:
+               REFRESH_SPEEDUP(ref, REFRESH_X4);
+               REFRESH_SPEEDUP(pre_ref, REFRESH_X4);
+               REFRESH_SPEEDUP(dsr_cntrl, REFRESH_X4);
                break;
        default:
                WARN(1, "%s: Failed to set dram over temp state %lu\n",
@@ -650,8 +661,11 @@ static noinline void emc_set_clock(const struct tegra14_emc_table *next_timing,
        u32 dll_override, emc_cfg_dig_dll, pmc_dpd;
        u32 t_start, t_diff;
 
+       u32 use_prelock = 0;
        u32 emc_cfg_reg = emc_readl(EMC_CFG);
-       u32 use_prelock = next_timing->emc_cfg_dig_dll & EMC_CFG_DIG_DLL_EN;
+
+       if (!(clk_setting & EMC_CLK_FORCE_CC_TRIGGER))
+               use_prelock = next_timing->emc_cfg_dig_dll & EMC_CFG_DIG_DLL_EN;
 
        dyn_sref_enabled = emc_cfg_reg & EMC_CFG_DYN_SREF_ENABLE;
        dll_change = get_dll_change(next_timing, last_timing);
@@ -699,7 +713,7 @@ static noinline void emc_set_clock(const struct tegra14_emc_table *next_timing,
                        continue;
                __raw_writel(next_timing->burst_regs[i], burst_reg_addr[i]);
        }
-       if (!use_prelock)
+       if (!use_prelock && !(clk_setting & EMC_CLK_FORCE_CC_TRIGGER))
                writel(next_timing->emc_cfg_dig_dll | EMC_CFG_DIG_DLL_RESET |
                       EMC_CFG_DIG_DLL_OVERRIDE_EN, emc_base + EMC_CFG_DIG_DLL);
 
@@ -907,9 +921,16 @@ int tegra_emc_set_rate(unsigned long rate)
                udelay(clkchange_delay - (int)last_change_delay);
 
        spin_lock_irqsave(&emc_access_lock, flags);
-       emc_set_clock(&tegra_emc_table[i], last_timing, clk_setting);
-       clkchange_time = ktime_get();
-       emc_timing = &tegra_emc_table[i];
+       /* Pick from the EMC tables based on the status of the over temp state
+          flag. */
+       emc_set_clock(dram_over_temp_state != DRAM_OVER_TEMP_THROTTLE ?
+                     &tegra_emc_table[i] : &tegra_emc_table_derated[i],
+                     last_timing, clk_setting);
+       clkchange_time = timekeeping_suspended ? clkchange_time : ktime_get();
+       emc_timing = dram_over_temp_state != DRAM_OVER_TEMP_THROTTLE ?
+               &tegra_emc_table[i] : &tegra_emc_table_derated[i];
+       if (dram_over_temp_state == DRAM_OVER_TEMP_THROTTLE)
+               pr_debug("[emc] Picked derated freq.\n");
        spin_unlock_irqrestore(&emc_access_lock, flags);
 
        emc_last_stats_update(i);
@@ -1212,7 +1233,9 @@ static int purge_emc_table(unsigned long max_rate)
 #define purge_emc_table(max_rate) (0)
 #endif
 
-static int init_emc_table(const struct tegra14_emc_table *table, int table_size)
+static int init_emc_table(const struct tegra14_emc_table *table,
+                         const struct tegra14_emc_table *table_der,
+                         int table_size)
 {
        int i, mv;
        u32 reg;
@@ -1256,6 +1279,20 @@ static int init_emc_table(const struct tegra14_emc_table *table, int table_size)
                return -ENODATA;
        }
 
+       /* Check that the derated table and non-derated table match. */
+       if (WARN(!table_der, "tegra: emc: Missing derated tables!\n"))
+               return -EINVAL;
+       for (i = 0; i < tegra_emc_table_size; i++) {
+               if (table[i].rate        != table_der[i].rate ||
+                   table[i].rev         != table_der[i].rev ||
+                   table[i].emc_min_mv  != table_der[i].emc_min_mv ||
+                   table[i].src_sel_reg != table_der[i].src_sel_reg) {
+                       pr_err("tegra: emc: Derated table mismatch.\n");
+                       return -EINVAL;
+               }
+       }
+       pr_info("tegra: emc: Derated table is valid.\n");
+
        /* Match EMC source/divider settings with table entries */
        for (i = 0; i < tegra_emc_table_size; i++) {
                unsigned long table_rate = table[i].rate;
@@ -1295,6 +1332,7 @@ static int init_emc_table(const struct tegra14_emc_table *table, int table_size)
        }
 
        tegra_emc_table = table;
+       tegra_emc_table_derated = table_der;
 
        /*
         * Purge rates that cannot be reached because table does not specify
@@ -1439,7 +1477,8 @@ static int tegra14_emc_probe(struct platform_device *pdev)
        emc_writel(padctrl, EMC_XM2CMDPADCTRL);
 #endif
 
-       return init_emc_table(pdata->tables, pdata->num_tables);
+       return init_emc_table(pdata->tables, pdata->tables_derated,
+                             pdata->num_tables);
 }
 
 static struct platform_driver tegra14_emc_driver = {
@@ -1613,18 +1652,41 @@ int tegra_emc_dsr_status(void)
 
 int tegra_emc_set_over_temp_state(unsigned long state)
 {
+       int offset;
        unsigned long flags;
 
-       if (dram_type != DRAM_TYPE_LPDDR2)
+       if (dram_type != DRAM_TYPE_LPDDR2 || !emc_timing)
                return -ENODEV;
 
-       if (state > DRAM_OVER_TEMP_REFRESH)
+       if (state > DRAM_OVER_TEMP_THROTTLE)
                return -EINVAL;
 
-       spin_lock_irqsave(&emc_access_lock, flags);
+       /* Silently do nothing if there is no state change. */
+       if (state == dram_over_temp_state)
+               return 0;
 
-       /* Update refresh timing if state changed */
-       if (emc_timing && (dram_over_temp_state != state)) {
+       /*
+        * If derating needs to be turned on/off force a clock change. That
+        * will take care of the refresh as well. In derating is not going to
+        * be changed then all that is needed is an update to the refresh
+        * settings.
+        */
+       spin_lock_irqsave(&emc_access_lock, flags);
+       if (state == DRAM_OVER_TEMP_THROTTLE) {
+               dram_over_temp_state = state;
+               offset = emc_timing - tegra_emc_table;
+               emc_set_clock(emc_timing, &tegra_emc_table_derated[offset],
+                             tegra_emc_clk_sel[offset].value |
+                             EMC_CLK_FORCE_CC_TRIGGER);
+               emc_timing = &tegra_emc_table_derated[offset];
+       } else if (dram_over_temp_state == DRAM_OVER_TEMP_THROTTLE) {
+               dram_over_temp_state = state;
+               offset = emc_timing - tegra_emc_table_derated;
+               emc_set_clock(emc_timing, &tegra_emc_table[offset],
+                             tegra_emc_clk_sel[offset].value |
+                             EMC_CLK_FORCE_CC_TRIGGER);
+               emc_timing = &tegra_emc_table[offset];
+       } else {
                set_over_temp_timing(emc_timing, state);
                emc_timing_update();
                if (state != DRAM_OVER_TEMP_NONE)
@@ -1632,6 +1694,7 @@ int tegra_emc_set_over_temp_state(unsigned long state)
                dram_over_temp_state = state;
        }
        spin_unlock_irqrestore(&emc_access_lock, flags);
+
        return 0;
 }
 
index 6c8d4e6..b071894 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * arch/arm/mach-tegra/tegra3_emc.c
  *
- * Copyright (C) 2011-2012, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2011-2013, NVIDIA CORPORATION. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -323,7 +323,9 @@ static inline void set_over_temp_timing(
        switch (state) {
        case DRAM_OVER_TEMP_NONE:
                break;
-       case DRAM_OVER_TEMP_REFRESH:
+       case DRAM_OVER_TEMP_REFRESH_X2:
+       case DRAM_OVER_TEMP_REFRESH_X4:
+       case DRAM_OVER_TEMP_THROTTLE:
                REFRESH_SPEEDUP(ref);
                REFRESH_SPEEDUP(pre_ref);
                REFRESH_SPEEDUP(dsr_cntrl);
index 4191120..5710daa 100644 (file)
@@ -27,7 +27,9 @@ extern u8 tegra_emc_bw_efficiency;
 
 enum {
        DRAM_OVER_TEMP_NONE = 0,
-       DRAM_OVER_TEMP_REFRESH,
+       DRAM_OVER_TEMP_REFRESH_X2,
+       DRAM_OVER_TEMP_REFRESH_X4,
+       DRAM_OVER_TEMP_THROTTLE, /* 4x Refresh + derating. */
 };
 
 enum emc_user_id {
index 36fbcd3..b10201e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * arch/arm/mach-tegra/therm-dram.c
  *
- * Copyright (C) 2013 NVIDIA Corporation
+ * Copyright (C) 2013 NVIDIA Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -63,19 +63,24 @@ static void emc_mr4_poll(unsigned long nothing)
                /*
                 * Temp is fine - go back to regular refresh.
                 */
-               pr_info("[dram-therm] Disabling 4x refresh\n");
+               pr_info("[dram-therm] Setting nominal refresh + timings.\n");
                tegra_emc_set_over_temp_state(DRAM_OVER_TEMP_NONE);
                break;
        case 4:
-               /*
-                * Temp is high - 4x refresh.
-                */
-               pr_info("[dram-therm] Enabling 4x refresh\n");
-               tegra_emc_set_over_temp_state(DRAM_OVER_TEMP_REFRESH);
+               pr_info("[dram-therm] Enabling 2x refresh.\n");
+               tegra_emc_set_over_temp_state(DRAM_OVER_TEMP_REFRESH_X2);
+               break;
+       case 5:
+               pr_info("[dram-therm] Enabling 4x refresh.\n");
+               tegra_emc_set_over_temp_state(DRAM_OVER_TEMP_REFRESH_X4);
+               break;
+       case 6:
+               pr_info("[dram-therm] Enabling 4x refresh + derating.\n");
+               tegra_emc_set_over_temp_state(DRAM_OVER_TEMP_THROTTLE);
                break;
        default:
-               pr_info("[dram-therm] Enabling 4x refresh\n");
-               tegra_emc_set_over_temp_state(DRAM_OVER_TEMP_REFRESH);
+               WARN(1, "%s: Invalid DRAM temp state %d\n",
+                    __func__, dram_temp);
                break;
        }
        prev_temp = dram_temp;
index b8a5de9..d4a9892 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2011 Google, Inc.
- * Copyright (C) 2012 NVIDIA Corporation.
+ * Copyright (C) 2012-2013 NVIDIA Corporation. All rights reserved.
  *
  * Author:
  *     Colin Cross <ccross@android.com>
@@ -142,6 +142,7 @@ struct tegra14_emc_pdata {
        const char *description;
        int num_tables;
        struct tegra14_emc_table *tables;
+       struct tegra14_emc_table *tables_derated;
 };
 
 #define TEGRA12_EMC_MAX_NUM_REGS       300