summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDanny Huang <dahuang@nvidia.com>2012-11-15 15:42:34 +0800
committerStephen Warren <swarren@nvidia.com>2012-11-15 14:36:59 -0700
commitf8ddda713b9ea6c10012429c089c81bc9a5cd49f (patch)
treec469d4819326e0aadccd58f5523bd5f4d1f70852
parent25cd5a391478b1e29ef7de172b3bd612159a07cc (diff)
ARM: tegra: Tegra30 speedo-based process identification
This patch adds speedo-based process identification support for Tegra30. Signed-off-by: Danny Huang <dahuang@nvidia.com> [swarren s/Tegra3/Tegra30/ in log print, s/T30/Tegra30/ in commit description] Signed-off-by: Stephen Warren <swarren@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/Makefile1
-rw-r--r--arch/arm/mach-tegra/fuse.c11
-rw-r--r--arch/arm/mach-tegra/fuse.h7
-rw-r--r--arch/arm/mach-tegra/tegra30_speedo.c292
4 files changed, 309 insertions, 2 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 07ed4bdaba3a..6cc23cc83509 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += sleep-t20.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks_data.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_speedo.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += sleep-t30.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
obj-$(CONFIG_SMP) += reset.o
diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c
index 9fd02c50ae29..8121742711fe 100644
--- a/arch/arm/mach-tegra/fuse.c
+++ b/arch/arm/mach-tegra/fuse.c
@@ -30,11 +30,13 @@
#define FUSE_SKU_INFO 0x110
#define TEGRA20_FUSE_SPARE_BIT 0x200
+#define TEGRA30_FUSE_SPARE_BIT 0x244
int tegra_sku_id;
int tegra_cpu_process_id;
int tegra_core_process_id;
int tegra_chip_id;
+int tegra_cpu_speedo_id; /* only exist in Tegra30 and later */
int tegra_soc_speedo_id;
enum tegra_revision tegra_revision;
@@ -120,13 +122,18 @@ void tegra_init_fuse(void)
id = readl_relaxed(IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804);
tegra_chip_id = (id >> 8) & 0xff;
- tegra_fuse_spare_bit = TEGRA20_FUSE_SPARE_BIT;
-
switch (tegra_chip_id) {
case TEGRA20:
+ tegra_fuse_spare_bit = TEGRA20_FUSE_SPARE_BIT;
tegra_init_speedo_data = &tegra20_init_speedo_data;
break;
+ case TEGRA30:
+ tegra_fuse_spare_bit = TEGRA30_FUSE_SPARE_BIT;
+ tegra_init_speedo_data = &tegra30_init_speedo_data;
+ break;
default:
+ pr_warn("Tegra: unknown chip id %d\n", tegra_chip_id);
+ tegra_fuse_spare_bit = TEGRA20_FUSE_SPARE_BIT;
tegra_init_speedo_data = &tegra_get_process_id;
}
diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h
index 7347c8818937..ff1383dd61a7 100644
--- a/arch/arm/mach-tegra/fuse.h
+++ b/arch/arm/mach-tegra/fuse.h
@@ -42,6 +42,7 @@ extern int tegra_sku_id;
extern int tegra_cpu_process_id;
extern int tegra_core_process_id;
extern int tegra_chip_id;
+extern int tegra_cpu_speedo_id; /* only exist in Tegra30 and later */
extern int tegra_soc_speedo_id;
extern enum tegra_revision tegra_revision;
@@ -58,4 +59,10 @@ void tegra20_init_speedo_data(void);
static inline void tegra20_init_speedo_data(void) {}
#endif
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+void tegra30_init_speedo_data(void);
+#else
+static inline void tegra30_init_speedo_data(void) {}
+#endif
+
#endif
diff --git a/arch/arm/mach-tegra/tegra30_speedo.c b/arch/arm/mach-tegra/tegra30_speedo.c
new file mode 100644
index 000000000000..125cb16424a6
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra30_speedo.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/bug.h>
+
+#include "fuse.h"
+
+#define CORE_PROCESS_CORNERS_NUM 1
+#define CPU_PROCESS_CORNERS_NUM 6
+
+#define FUSE_SPEEDO_CALIB_0 0x114
+#define FUSE_PACKAGE_INFO 0X1FC
+#define FUSE_TEST_PROG_VER 0X128
+
+#define G_SPEEDO_BIT_MINUS1 58
+#define G_SPEEDO_BIT_MINUS1_R 59
+#define G_SPEEDO_BIT_MINUS2 60
+#define G_SPEEDO_BIT_MINUS2_R 61
+#define LP_SPEEDO_BIT_MINUS1 62
+#define LP_SPEEDO_BIT_MINUS1_R 63
+#define LP_SPEEDO_BIT_MINUS2 64
+#define LP_SPEEDO_BIT_MINUS2_R 65
+
+enum {
+ THRESHOLD_INDEX_0,
+ THRESHOLD_INDEX_1,
+ THRESHOLD_INDEX_2,
+ THRESHOLD_INDEX_3,
+ THRESHOLD_INDEX_4,
+ THRESHOLD_INDEX_5,
+ THRESHOLD_INDEX_6,
+ THRESHOLD_INDEX_7,
+ THRESHOLD_INDEX_8,
+ THRESHOLD_INDEX_9,
+ THRESHOLD_INDEX_10,
+ THRESHOLD_INDEX_11,
+ THRESHOLD_INDEX_COUNT,
+};
+
+static const u32 core_process_speedos[][CORE_PROCESS_CORNERS_NUM] = {
+ {180},
+ {170},
+ {195},
+ {180},
+ {168},
+ {192},
+ {180},
+ {170},
+ {195},
+ {180},
+ {180},
+ {180},
+};
+
+static const u32 cpu_process_speedos[][CPU_PROCESS_CORNERS_NUM] = {
+ {306, 338, 360, 376, UINT_MAX},
+ {295, 336, 358, 375, UINT_MAX},
+ {325, 325, 358, 375, UINT_MAX},
+ {325, 325, 358, 375, UINT_MAX},
+ {292, 324, 348, 364, UINT_MAX},
+ {324, 324, 348, 364, UINT_MAX},
+ {324, 324, 348, 364, UINT_MAX},
+ {295, 336, 358, 375, UINT_MAX},
+ {358, 358, 358, 358, 397, UINT_MAX},
+ {364, 364, 364, 364, 397, UINT_MAX},
+ {295, 336, 358, 375, 391, UINT_MAX},
+ {295, 336, 358, 375, 391, UINT_MAX},
+};
+
+static int threshold_index;
+static int package_id;
+
+static void fuse_speedo_calib(u32 *speedo_g, u32 *speedo_lp)
+{
+ u32 reg;
+ int ate_ver;
+ int bit_minus1;
+ int bit_minus2;
+
+ reg = tegra_fuse_readl(FUSE_SPEEDO_CALIB_0);
+
+ *speedo_lp = (reg & 0xFFFF) * 4;
+ *speedo_g = ((reg >> 16) & 0xFFFF) * 4;
+
+ ate_ver = tegra_fuse_readl(FUSE_TEST_PROG_VER);
+ pr_info("%s: ATE prog ver %d.%d\n", __func__, ate_ver/10, ate_ver%10);
+
+ if (ate_ver >= 26) {
+ bit_minus1 = tegra_spare_fuse(LP_SPEEDO_BIT_MINUS1);
+ bit_minus1 |= tegra_spare_fuse(LP_SPEEDO_BIT_MINUS1_R);
+ bit_minus2 = tegra_spare_fuse(LP_SPEEDO_BIT_MINUS2);
+ bit_minus2 |= tegra_spare_fuse(LP_SPEEDO_BIT_MINUS2_R);
+ *speedo_lp |= (bit_minus1 << 1) | bit_minus2;
+
+ bit_minus1 = tegra_spare_fuse(G_SPEEDO_BIT_MINUS1);
+ bit_minus1 |= tegra_spare_fuse(G_SPEEDO_BIT_MINUS1_R);
+ bit_minus2 = tegra_spare_fuse(G_SPEEDO_BIT_MINUS2);
+ bit_minus2 |= tegra_spare_fuse(G_SPEEDO_BIT_MINUS2_R);
+ *speedo_g |= (bit_minus1 << 1) | bit_minus2;
+ } else {
+ *speedo_lp |= 0x3;
+ *speedo_g |= 0x3;
+ }
+}
+
+static void rev_sku_to_speedo_ids(int rev, int sku)
+{
+ switch (rev) {
+ case TEGRA_REVISION_A01:
+ tegra_cpu_speedo_id = 0;
+ tegra_soc_speedo_id = 0;
+ threshold_index = THRESHOLD_INDEX_0;
+ break;
+ case TEGRA_REVISION_A02:
+ case TEGRA_REVISION_A03:
+ switch (sku) {
+ case 0x87:
+ case 0x82:
+ tegra_cpu_speedo_id = 1;
+ tegra_soc_speedo_id = 1;
+ threshold_index = THRESHOLD_INDEX_1;
+ break;
+ case 0x81:
+ switch (package_id) {
+ case 1:
+ tegra_cpu_speedo_id = 2;
+ tegra_soc_speedo_id = 2;
+ threshold_index = THRESHOLD_INDEX_2;
+ break;
+ case 2:
+ tegra_cpu_speedo_id = 4;
+ tegra_soc_speedo_id = 1;
+ threshold_index = THRESHOLD_INDEX_7;
+ break;
+ default:
+ pr_err("Tegra30: Unknown pkg %d\n", package_id);
+ BUG();
+ break;
+ }
+ break;
+ case 0x80:
+ switch (package_id) {
+ case 1:
+ tegra_cpu_speedo_id = 5;
+ tegra_soc_speedo_id = 2;
+ threshold_index = THRESHOLD_INDEX_8;
+ break;
+ case 2:
+ tegra_cpu_speedo_id = 6;
+ tegra_soc_speedo_id = 2;
+ threshold_index = THRESHOLD_INDEX_9;
+ break;
+ default:
+ pr_err("Tegra30: Unknown pkg %d\n", package_id);
+ BUG();
+ break;
+ }
+ break;
+ case 0x83:
+ switch (package_id) {
+ case 1:
+ tegra_cpu_speedo_id = 7;
+ tegra_soc_speedo_id = 1;
+ threshold_index = THRESHOLD_INDEX_10;
+ break;
+ case 2:
+ tegra_cpu_speedo_id = 3;
+ tegra_soc_speedo_id = 2;
+ threshold_index = THRESHOLD_INDEX_3;
+ break;
+ default:
+ pr_err("Tegra30: Unknown pkg %d\n", package_id);
+ BUG();
+ break;
+ }
+ break;
+ case 0x8F:
+ tegra_cpu_speedo_id = 8;
+ tegra_soc_speedo_id = 1;
+ threshold_index = THRESHOLD_INDEX_11;
+ break;
+ case 0x08:
+ tegra_cpu_speedo_id = 1;
+ tegra_soc_speedo_id = 1;
+ threshold_index = THRESHOLD_INDEX_4;
+ break;
+ case 0x02:
+ tegra_cpu_speedo_id = 2;
+ tegra_soc_speedo_id = 2;
+ threshold_index = THRESHOLD_INDEX_5;
+ break;
+ case 0x04:
+ tegra_cpu_speedo_id = 3;
+ tegra_soc_speedo_id = 2;
+ threshold_index = THRESHOLD_INDEX_6;
+ break;
+ case 0:
+ switch (package_id) {
+ case 1:
+ tegra_cpu_speedo_id = 2;
+ tegra_soc_speedo_id = 2;
+ threshold_index = THRESHOLD_INDEX_2;
+ break;
+ case 2:
+ tegra_cpu_speedo_id = 3;
+ tegra_soc_speedo_id = 2;
+ threshold_index = THRESHOLD_INDEX_3;
+ break;
+ default:
+ pr_err("Tegra30: Unknown pkg %d\n", package_id);
+ BUG();
+ break;
+ }
+ break;
+ default:
+ pr_warn("Tegra30: Unknown SKU %d\n", sku);
+ tegra_cpu_speedo_id = 0;
+ tegra_soc_speedo_id = 0;
+ threshold_index = THRESHOLD_INDEX_0;
+ break;
+ }
+ break;
+ default:
+ pr_warn("Tegra30: Unknown chip rev %d\n", rev);
+ tegra_cpu_speedo_id = 0;
+ tegra_soc_speedo_id = 0;
+ threshold_index = THRESHOLD_INDEX_0;
+ break;
+ }
+}
+
+void tegra30_init_speedo_data(void)
+{
+ u32 cpu_speedo_val;
+ u32 core_speedo_val;
+ int i;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) !=
+ THRESHOLD_INDEX_COUNT);
+ BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) !=
+ THRESHOLD_INDEX_COUNT);
+
+ package_id = tegra_fuse_readl(FUSE_PACKAGE_INFO) & 0x0F;
+
+ rev_sku_to_speedo_ids(tegra_revision, tegra_sku_id);
+ 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 (i = 0; i < CPU_PROCESS_CORNERS_NUM; i++) {
+ if (cpu_speedo_val < cpu_process_speedos[threshold_index][i])
+ break;
+ }
+ tegra_cpu_process_id = i - 1;
+
+ if (tegra_cpu_process_id == -1) {
+ pr_warn("Tegra30: CPU speedo value %3d out of range",
+ cpu_speedo_val);
+ tegra_cpu_process_id = 0;
+ tegra_cpu_speedo_id = 1;
+ }
+
+ for (i = 0; i < CORE_PROCESS_CORNERS_NUM; i++) {
+ if (core_speedo_val < core_process_speedos[threshold_index][i])
+ break;
+ }
+ tegra_core_process_id = i - 1;
+
+ if (tegra_core_process_id == -1) {
+ pr_warn("Tegra30: CORE speedo value %3d out of range",
+ core_speedo_val);
+ tegra_core_process_id = 0;
+ tegra_soc_speedo_id = 1;
+ }
+
+ pr_info("Tegra30: CPU Speedo ID %d, Soc Speedo ID %d",
+ tegra_cpu_speedo_id, tegra_soc_speedo_id);
+}