Merge commit 'main-jb-2012.08.03-B4' into t114-0806
[linux-2.6.git] / arch / arm / mach-tegra / tegra3_speedo.c
index ec84c3c..c0c4686 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * arch/arm/mach-tegra/tegra3_speedo.c
  *
- * Copyright (c) 2011, NVIDIA Corporation.
+ * Copyright (c) 2011-2012, 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
 #include <linux/kernel.h>
 #include <linux/io.h>
 #include <linux/err.h>
+#include <linux/bug.h>                 /* For BUG_ON.  */
+
 #include <mach/iomap.h>
+#include <mach/tegra_fuse.h>
+#include <mach/hardware.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
 
 #include "fuse.h"
 
 #define CORE_PROCESS_CORNERS_NUM       1
-#define CPU_PROCESS_CORNERS_NUM                4
+#define CPU_PROCESS_CORNERS_NUM        7
 
 #define FUSE_SPEEDO_CALIB_0    0x114
 #define FUSE_PACKAGE_INFO      0X1FC
+#define FUSE_TEST_PROG_VER     0X128
+#define FUSE_SPARE_BIT_58      0x32c
+#define FUSE_SPARE_BIT_59      0x330
+#define FUSE_SPARE_BIT_60      0x334
+#define FUSE_SPARE_BIT_61      0x338
+#define FUSE_SPARE_BIT_62      0x33c
+#define FUSE_SPARE_BIT_63      0x340
+#define FUSE_SPARE_BIT_64      0x344
+#define FUSE_SPARE_BIT_65      0x348
+
+#define G_SPEEDO_BIT_MINUS1    FUSE_SPARE_BIT_58
+#define G_SPEEDO_BIT_MINUS1_R  FUSE_SPARE_BIT_59
+#define G_SPEEDO_BIT_MINUS2    FUSE_SPARE_BIT_60
+#define G_SPEEDO_BIT_MINUS2_R  FUSE_SPARE_BIT_61
+#define LP_SPEEDO_BIT_MINUS1   FUSE_SPARE_BIT_62
+#define LP_SPEEDO_BIT_MINUS1_R FUSE_SPARE_BIT_63
+#define LP_SPEEDO_BIT_MINUS2   FUSE_SPARE_BIT_64
+#define LP_SPEEDO_BIT_MINUS2_R FUSE_SPARE_BIT_65
 
 /* Maximum speedo levels for each core process corner */
 static const u32 core_process_speedos[][CORE_PROCESS_CORNERS_NUM] = {
 /* proc_id 0 */
-       {180}, /* soc_speedo_id 0 */
-       {180}, /* soc_speedo_id 1 */
-       {200}, /* soc_speedo_id 2 */
+       {180}, /* [0]: soc_speedo_id 0: any A01 */
+
+/* T30 family */
+       {170}, /* [1]: soc_speedo_id 1: AP30 */
+       {195}, /* [2]: soc_speedo_id 2: T30  */
+       {180}, /* [3]: soc_speedo_id 2: T30S */
+
+/* Characterization SKUs */
+       {168}, /* [4]: soc_speedo_id 1: AP30 char */
+       {192}, /* [5]: soc_speedo_id 2: T30  char */
+       {180}, /* [6]: soc_speedo_id 2: T30S char */
+
+/* T33 family */
+       {170}, /* [7]: soc_speedo_id = 1 - AP33 */
+       {195}, /* [8]: soc_speedo_id = 2 - T33  */
+       {180}, /* [9]: soc_speedo_id = 2 - T33S/AP37 */
+
+/* T30 'L' family */
+       {180}, /* [10]: soc_speedo_id 1: T30L */
+       {180}, /* [11]: soc_speedo_id 1: T30SL */
+
+/* T30 Automotives */
+       {185}, /* [12]: soc_speedo_id = 3 - Automotives */
+       {185}, /* [13]: soc_speedo_id = 3 - Automotives */
+
+/* T37 Family*/
+       {210}, /* [14]: soc_speedo_id 2: T37 */
 };
 
 /* Maximum speedo levels for each CPU process corner */
 static const u32 cpu_process_speedos[][CPU_PROCESS_CORNERS_NUM] = {
-/* proc_id 0    1    2    3 */
-       {306, 338, 360, 376}, /* cpu_speedo_id 0 */
-       {306, 338, 360, 376}, /* cpu_speedo_id 1 */
-       {338, 338, 360, 376}, /* cpu_speedo_id 2 */
-       {338, 338, 360, 376}, /* cpu_speedo_id 3 */
+/* proc_id 0    1    2    3    4*/
+       {306, 338, 360, 376, UINT_MAX}, /* [0]: cpu_speedo_id 0: any A01 */
+
+/* T30 family */
+       {295, 336, 358, 375, UINT_MAX}, /* [1]: cpu_speedo_id 1: AP30 */
+       {325, 325, 358, 375, UINT_MAX}, /* [2]: cpu_speedo_id 2: T30  */
+       {325, 325, 358, 375, UINT_MAX}, /* [3]: cpu_speedo_id 3: T30S */
+
+/* Characterization SKUs */
+       {292, 324, 348, 364, UINT_MAX}, /* [4]: cpu_speedo_id 1: AP30char */
+       {324, 324, 348, 364, UINT_MAX}, /* [5]: cpu_speedo_id 2: T30char  */
+       {324, 324, 348, 364, UINT_MAX}, /* [6]: cpu_speedo_id 3: T30Schar */
+
+/* T33 family */
+       {295, 336, 358, 375, UINT_MAX},      /* [7]: cpu_speedo_id: 4: AP33 */
+       {358, 358, 358, 358, 397, UINT_MAX}, /* [8]: cpu_speedo_id: 5: T33  */
+       {364, 364, 364, 364, 397, UINT_MAX}, /* [9]: cpu_speedo_id: 6/12: T33S/AP37 */
+
+/* T30 'L' family */
+       {295, 336, 358, 375, 391, UINT_MAX}, /* [10]: cpu_speedo_id 7: T30L  */
+       {295, 336, 358, 375, 391, UINT_MAX}, /* [11]: cpu_speedo_id 8: T30SL */
+
+/* T30 Automotives */
+       /* threshold_index 12: cpu_speedo_id 9 & 10
+        * 0,1,2 values correspond to speedo_id  9/14
+        * 3,4,5 values correspond to speedo_id 10/15*/
+       {300, 311, 360, 371, 381, 415, 431},
+       {300, 311, 410, 431, UINT_MAX}, /* [13]: cpu_speedo_id 11: T30 auto */
+
+/* T37 family */
+       {358, 358, 358, 358, 397, UINT_MAX}, /* [14]: cpu_speedo_id 13: T37 */
 };
 
+/*
+ * Common speedo_value array threshold index for both core_process_speedos and
+ * cpu_process_speedos arrays. Make sure these two arrays are always in synch.
+ */
+static int threshold_index;
+
 static int cpu_process_id;
 static int core_process_id;
 static int cpu_speedo_id;
 static int soc_speedo_id;
-
-static inline u8 fuse_package_info(void)
-{
-       /* Package info: 4 bits - 0,3:reserved 1:MID 2:DSC */
-       return tegra_fuse_readl(FUSE_PACKAGE_INFO) & 0x0F;
-}
+static int package_id;
+/*
+ * Only AP37 supports App Profile
+ * This informs user space of support without exposing cpu id's
+ */
+static int enable_app_profiles;
 
 static void fuse_speedo_calib(u32 *speedo_g, u32 *speedo_lp)
 {
        u32 reg;
+       int ate_ver, bit_minus1, bit_minus2;
 
        BUG_ON(!speedo_g || !speedo_lp);
        reg = tegra_fuse_readl(FUSE_SPEEDO_CALIB_0);
@@ -71,55 +152,197 @@ static void fuse_speedo_calib(u32 *speedo_g, u32 *speedo_lp)
 
        /* Speedo G = Upper 16-bits Multiplied by 4 */
        *speedo_g = ((reg >> 16) & 0xFFFF) * 4;
+
+       if (tegra_fuse_get_revision(&ate_ver))
+               return;
+       pr_info("%s: ATE prog ver %d.%d\n", __func__, ate_ver/10, ate_ver%10);
+
+       pr_debug("CPU speedo base value %u (0x%3x)\n", *speedo_g, *speedo_g);
+       pr_debug("Core speedo base value %u (0x%3x)\n", *speedo_lp, *speedo_lp);
+
+       if (ate_ver >= 26) {
+               /* read lower 2 bits of LP speedo from spare fuses */
+               bit_minus1 = tegra_fuse_readl(LP_SPEEDO_BIT_MINUS1) & 0x1;
+               bit_minus1 |= tegra_fuse_readl(LP_SPEEDO_BIT_MINUS1_R) & 0x1;
+               bit_minus2 = tegra_fuse_readl(LP_SPEEDO_BIT_MINUS2) & 0x1;
+               bit_minus2 |= tegra_fuse_readl(LP_SPEEDO_BIT_MINUS2_R) & 0x1;
+               *speedo_lp |= (bit_minus1 << 1) | bit_minus2;
+
+               /* read lower 2 bits of G speedo from spare fuses */
+               bit_minus1 = tegra_fuse_readl(G_SPEEDO_BIT_MINUS1) & 0x1;
+               bit_minus1 |= tegra_fuse_readl(G_SPEEDO_BIT_MINUS1_R) & 0x1;
+               bit_minus2 = tegra_fuse_readl(G_SPEEDO_BIT_MINUS2) & 0x1;
+               bit_minus2 |= tegra_fuse_readl(G_SPEEDO_BIT_MINUS2_R) & 0x1;
+               *speedo_g |= (bit_minus1 << 1) | bit_minus2;
+       } else {
+               /* set lower 2 bits for speedo ate-ver independent comparison */
+               *speedo_lp |= 0x3;
+               *speedo_g |= 0x3;
+       }
 }
 
 static void rev_sku_to_speedo_ids(int rev, int sku)
 {
-       u8 pkg;
-
        switch (rev) {
-       case TEGRA_REVISION_A01:
+       case TEGRA_REVISION_A01: /* any A01 */
                cpu_speedo_id = 0;
                soc_speedo_id = 0;
+               threshold_index = 0;
                break;
+
        case TEGRA_REVISION_A02:
+       case TEGRA_REVISION_A03:
                switch (sku) {
                case 0x87: /* AP30 */
+               case 0x82: /* T30V */
                        cpu_speedo_id = 1;
                        soc_speedo_id = 1;
+                       threshold_index = 1;
                        break;
+
                case 0x81: /* T30 */
+                       switch (package_id) {
+                       case 1: /* MID => T30 */
+                               cpu_speedo_id = 2;
+                               soc_speedo_id = 2;
+                               threshold_index = 2;
+                               break;
+                       case 2: /* DSC => AP33 */
+                               cpu_speedo_id = 4;
+                               soc_speedo_id = 1;
+                               threshold_index = 7;
+                               break;
+                       default:
+                               pr_err("Tegra3 Rev-A02: Reserved pkg: %d\n",
+                                      package_id);
+                               BUG();
+                               break;
+                       }
+                       break;
+
+               case 0x80: /* T33 or T33S */
+                       switch (package_id) {
+                       case 1: /* MID => T33 */
+                               cpu_speedo_id = 5;
+                               soc_speedo_id = 2;
+                               threshold_index = 8;
+                               break;
+                       case 2: /* DSC => T33S */
+                               cpu_speedo_id = 6;
+                               soc_speedo_id = 2;
+                               threshold_index = 9;
+                               break;
+                       default:
+                               pr_err("Tegra3 Rev-A02: Reserved pkg: %d\n",
+                                      package_id);
+                               BUG();
+                               break;
+                       }
+                       break;
+
+               case 0x83: /* T30L or T30S */
+                       switch (package_id) {
+                       case 1: /* MID => T30L */
+                               cpu_speedo_id = 7;
+                               soc_speedo_id = 1;
+                               threshold_index = 10;
+                               break;
+                       case 2: /* DSC => T30S */
+                               cpu_speedo_id = 3;
+                               soc_speedo_id = 2;
+                               threshold_index = 3;
+                               break;
+                       default:
+                               pr_err("Tegra3 Rev-A02: Reserved pkg: %d\n",
+                                      package_id);
+                               BUG();
+                               break;
+                       }
+                       break;
+
+               case 0x8F: /* T30SL */
+                       cpu_speedo_id = 8;
+                       soc_speedo_id = 1;
+                       threshold_index = 11;
+                       break;
+
+               case 0xA0: /* T37 or A37 */
+                       switch (package_id) {
+                       case 1: /* MID => T37 */
+                               cpu_speedo_id = 13;
+                               soc_speedo_id = 2;
+                               threshold_index = 14;
+                               break;
+                       case 2: /* DSC => AP37 */
+                               cpu_speedo_id = 12;
+                               soc_speedo_id = 2;
+                               threshold_index = 9;
+                               enable_app_profiles = 1;
+                               break;
+                       default:
+                               pr_err("Tegra3 Rev-A02: Reserved pkg: %d\n",
+                                               package_id);
+                               BUG();
+                               break;
+                       }
+                       break;
+
+/* Characterization SKUs */
+               case 0x08: /* AP30 char */
+                       cpu_speedo_id = 1;
+                       soc_speedo_id = 1;
+                       threshold_index = 4;
+                       break;
+               case 0x02: /* T30 char */
                        cpu_speedo_id = 2;
                        soc_speedo_id = 2;
+                       threshold_index = 5;
                        break;
-               case 0x83: /* T30S */
+               case 0x04: /* T30S char */
                        cpu_speedo_id = 3;
                        soc_speedo_id = 2;
+                       threshold_index = 6;
                        break;
-               case 0:    /* ENG - check PKG_SKU */
-                       pr_info("Tegra3 ENG SKU: Checking pkg info\n");
-                       pkg = fuse_package_info();
-                       switch (pkg) {
+
+               case 0x91: /* T30AGS-Ax */
+               case 0xb0: /* T30IQS-Ax */
+               case 0xb1: /* T30MQS-Ax */
+               case 0x90: /* T30AQS-Ax */
+                       soc_speedo_id = 3;
+                       threshold_index = 12;
+                       break;
+               case 0x93: /* T30AG-Ax */
+                       cpu_speedo_id = 11;
+                       soc_speedo_id = 3;
+                       threshold_index = 13;
+                       break;
+               case 0:    /* ENG - check package_id */
+                       pr_info("Tegra3 ENG SKU: Checking package_id\n");
+                       switch (package_id) {
                        case 1: /* MID => assume T30 */
                                cpu_speedo_id = 2;
                                soc_speedo_id = 2;
+                               threshold_index = 2;
                                break;
                        case 2: /* DSC => assume T30S */
                                cpu_speedo_id = 3;
                                soc_speedo_id = 2;
+                               threshold_index = 3;
                                break;
                        default:
-                               pr_err("Tegra3 Rev-A02: Reserved pkg info %d\n",
-                                      pkg);
+                               pr_err("Tegra3 Rev-A02: Reserved pkg: %d\n",
+                                      package_id);
                                BUG();
                                break;
                        }
                        break;
+
                default:
                        /* FIXME: replace with BUG() when all SKU's valid */
                        pr_err("Tegra3 Rev-A02: Unknown SKU %d\n", sku);
                        cpu_speedo_id = 0;
                        soc_speedo_id = 0;
+                       threshold_index = 0;
                        break;
                }
                break;
@@ -133,17 +356,86 @@ void tegra_init_speedo_data(void)
 {
        u32 cpu_speedo_val, core_speedo_val;
        int iv;
+       int fuse_sku = tegra_sku_id;
+       int sku_override = tegra_get_sku_override();
+       int new_sku = fuse_sku;
+
+       /* Package info: 4 bits - 0,3:reserved 1:MID 2:DSC */
+       package_id = tegra_fuse_readl(FUSE_PACKAGE_INFO) & 0x0F;
+
+       /* Arrays must be of equal size - each index corresponds to a SKU */
+       BUG_ON(ARRAY_SIZE(cpu_process_speedos) !=
+              ARRAY_SIZE(core_process_speedos));
+
+       /* SKU Overrides
+       * T33   => T30, T30L
+       * T33S  => T30S, T30SL
+       * T30   => T30L
+       * T30S  => T30SL
+       * AP33  => AP30
+       */
+       switch (sku_override) {
+       case 1:
+               /* Base sku override */
+               if (fuse_sku == 0x80) {
+                       if (package_id == 1) {
+                               /* T33 to T30 */
+                               pr_info("%s: SKU OR: T33->T30\n", __func__);
+                               new_sku = 0x81;
+                       } else if (package_id == 2) {
+                               /* T33S->T30S */
+                               pr_info("%s: SKU OR: T33S->T30S\n", __func__);
+                               new_sku = 0x83;
+                       }
+               } else if (fuse_sku == 0x81) {
+                       if (package_id == 2) {
+                               /* AP33->AP30 */
+                               pr_info("%s: SKU OR: AP33->AP30\n", __func__);
+                               new_sku = 0x87;
+                       }
+               }
+               break;
+       case 2:
+               /* L sku override */
+               if (fuse_sku == 0x80) {
+                       if (package_id == 1) {
+                               /* T33->T30L */
+                               pr_info("%s: SKU OR: T33->T30L\n", __func__);
+                               new_sku = 0x83;
+                       } else if (package_id == 2) {
+                               /* T33S->T33SL */
+                               pr_info("%s: SKU OR: T33S->T30SL\n", __func__);
+                               new_sku = 0x8f;
+                       }
+               } else if (fuse_sku == 0x81) {
+                       if (package_id == 1) {
+                               pr_info("%s: SKU OR: T30->T30L\n", __func__);
+                               /* T30->T30L */
+                               new_sku = 0x83;
+                       }
+               } else if (fuse_sku == 0x83) {
+                       if (package_id == 2) {
+                               pr_info("%s: SKU OR: T30S->T30SL\n", __func__);
+                               /* T30S to T30SL */
+                               new_sku = 0x8f;
+                       }
+               }
+               break;
+       default:
+               /* no override */
+               break;
+       }
 
-       rev_sku_to_speedo_ids(tegra_get_revision(), tegra_sku_id());
-       BUG_ON(cpu_speedo_id >= ARRAY_SIZE(cpu_process_speedos));
-       BUG_ON(soc_speedo_id >= ARRAY_SIZE(core_process_speedos));
+       rev_sku_to_speedo_ids(tegra_revision, new_sku);
+       BUG_ON(threshold_index >= ARRAY_SIZE(cpu_process_speedos));
 
        fuse_speedo_calib(&cpu_speedo_val, &core_speedo_val);
        pr_debug("%s CPU speedo value %u\n", __func__, cpu_speedo_val);
        pr_debug("%s Core speedo value %u\n", __func__, core_speedo_val);
 
        for (iv = 0; iv < CPU_PROCESS_CORNERS_NUM; iv++) {
-               if (cpu_speedo_val < cpu_process_speedos[cpu_speedo_id][iv]) {
+               if (cpu_speedo_val <
+                   cpu_process_speedos[threshold_index][iv]) {
                        break;
                }
        }
@@ -158,28 +450,55 @@ void tegra_init_speedo_data(void)
                pr_err("****************************************************");
 
                cpu_process_id = INVALID_PROCESS_ID;
-               cpu_speedo_id = 0;
+               cpu_speedo_id = 1;
        }
 
        for (iv = 0; iv < CORE_PROCESS_CORNERS_NUM; iv++) {
-               if (core_speedo_val < core_process_speedos[soc_speedo_id][iv]) {
+               if (core_speedo_val <
+                   core_process_speedos[threshold_index][iv]) {
                        break;
                }
        }
        core_process_id = iv -1;
 
        if (core_process_id == -1) {
-               pr_err("*****************************************************");
-               pr_err("*****************************************************");
+               pr_err("****************************************************");
+               pr_err("****************************************************");
                pr_err("* tegra3_speedo: CORE speedo value %3d out of range *",
                       core_speedo_val);
-               pr_err("*****************************************************");
-               pr_err("*****************************************************");
+               pr_err("****************************************************");
+               pr_err("****************************************************");
 
                core_process_id = INVALID_PROCESS_ID;
-               soc_speedo_id = 0;
+               soc_speedo_id = 1;
+       }
+       if (threshold_index == 12 && cpu_process_id != INVALID_PROCESS_ID) {
+               if (cpu_process_id <= 2) {
+                       switch(fuse_sku) {
+                       case 0xb0:
+                       case 0xb1:
+                               cpu_speedo_id = 9;
+                               break;
+                       case 0x90:
+                       case 0x91:
+                               cpu_speedo_id = 14;
+                       default:
+                               break;
+                       }
+               } else if (cpu_process_id >= 3 && cpu_process_id < 6) {
+                       switch(fuse_sku) {
+                       case 0xb0:
+                       case 0xb1:
+                               cpu_speedo_id = 10;
+                               break;
+                       case 0x90:
+                       case 0x91:
+                               cpu_speedo_id = 15;
+                       default:
+                               break;
+                       }
+               }
        }
-
        pr_info("Tegra3: CPU Speedo ID %d, Soc Speedo ID %d",
                 cpu_speedo_id, soc_speedo_id);
 }
@@ -211,3 +530,55 @@ int tegra_soc_speedo_id(void)
 {
        return soc_speedo_id;
 }
+
+int tegra_package_id(void)
+{
+       return package_id;
+}
+
+/*
+ * CPU and core nominal voltage levels as determined by chip SKU and speedo
+ * (not final - can be lowered by dvfs tables and rail dependencies; the
+ * latter is resolved by the dvfs code)
+ */
+static const int cpu_speedo_nominal_millivolts[] =
+/* speedo_id 0,    1,    2,    3,    4,    5,    6,    7,    8,   9,  10,  11,   12,    13,  14,  15 */
+       { 1125, 1150, 1150, 1150, 1237, 1237, 1237, 1150, 1150, 1007, 916, 850, 1237, 1237, 950, 900};
+
+int tegra_cpu_speedo_mv(void)
+{
+       return cpu_speedo_nominal_millivolts[cpu_speedo_id];
+}
+
+int tegra_core_speedo_mv(void)
+{
+       switch (soc_speedo_id) {
+       case 0:
+               return 1200;
+       case 1:
+               if ((cpu_speedo_id != 7) && (cpu_speedo_id != 8))
+                       return 1200;
+               /* fall thru for T30L or T30SL */
+       case 2:
+               if (cpu_speedo_id != 13)
+                       return 1300;
+               /* T37 */
+               return 1350;
+       case 3:
+               return 1250;
+       default:
+               BUG();
+       }
+}
+
+static int get_enable_app_profiles(char *val, const struct kernel_param *kp)
+{
+       return param_get_uint(val, kp);
+}
+
+static struct kernel_param_ops tegra_profiles_ops = {
+       .get = get_enable_app_profiles,
+};
+
+module_param_cb(tegra_enable_app_profiles,
+       &tegra_profiles_ops, &enable_app_profiles, 0444);