ARM: tegra: Add speedo-based process identification
Alex Frid [Fri, 11 Feb 2011 09:20:39 +0000 (01:20 -0800)]
Original-Change-Id: If6cd2914551331bd49b128ad3143a0d7adf0f120
Reviewed-on: http://git-master/r/13396
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Tested-by: Bharat Nihalani <bnihalani@nvidia.com>

Rebase-Id: Re61c7e38519e6fde84f95f1c7ecb883c92b2d0db

arch/arm/mach-tegra/Makefile
arch/arm/mach-tegra/fuse.c
arch/arm/mach-tegra/fuse.h
arch/arm/mach-tegra/tegra2_speedo.c [new file with mode: 0644]

index 116eef5..dd8893a 100644 (file)
@@ -29,6 +29,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += dvfs.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_clocks.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_dvfs.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_fuse.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_speedo.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)                += tegra2_emc.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)                += wakeups-t2.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += pm-t2.o
index 769346d..1aa393c 100644 (file)
@@ -57,6 +57,7 @@ void tegra_init_fuse(void)
        u32 reg = readl(IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48));
        reg |= 1 << 28;
        writel(reg, IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48));
+       tegra_init_speedo_data();
 
        pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n",
                tegra_revision_name[tegra_get_revision()],
@@ -87,22 +88,6 @@ int tegra_sku_id(void)
        return sku_id;
 }
 
-int tegra_cpu_process_id(void)
-{
-       int cpu_process_id;
-       u32 reg = tegra_fuse_readl(FUSE_SPARE_BIT);
-       cpu_process_id = (reg >> 6) & 3;
-       return cpu_process_id;
-}
-
-int tegra_core_process_id(void)
-{
-       int core_process_id;
-       u32 reg = tegra_fuse_readl(FUSE_SPARE_BIT);
-       core_process_id = (reg >> 12) & 3;
-       return core_process_id;
-}
-
 enum tegra_revision tegra_get_revision(void)
 {
        void __iomem *chip_id = IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804;
index 39fce1a..d384743 100644 (file)
@@ -30,7 +30,9 @@ unsigned int tegra_spare_fuse(int bit);
 int tegra_sku_id(void);
 int tegra_cpu_process_id(void);
 int tegra_core_process_id(void);
+int tegra_soc_speedo_id(void);
 void tegra_init_fuse(void);
+void tegra_init_speedo_data(void);
 u32 tegra_fuse_readl(unsigned long offset);
 void tegra_fuse_writel(u32 value, unsigned long offset);
 enum tegra_revision tegra_get_revision(void);
diff --git a/arch/arm/mach-tegra/tegra2_speedo.c b/arch/arm/mach-tegra/tegra2_speedo.c
new file mode 100644 (file)
index 0000000..1e5fa26
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * arch/arm/mach-tegra/tegra2_speedo.c
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+#include <mach/iomap.h>
+
+#include "fuse.h"
+
+#define CPU_SPEEDO_LSBIT               20
+#define CPU_SPEEDO_MSBIT               29
+#define CPU_SPEEDO_REDUND_LSBIT                30
+#define CPU_SPEEDO_REDUND_MSBIT                39
+#define CPU_SPEEDO_REDUND_OFFS (CPU_SPEEDO_REDUND_MSBIT - CPU_SPEEDO_MSBIT)
+
+#define CORE_SPEEDO_LSBIT              40
+#define CORE_SPEEDO_MSBIT              47
+#define CORE_SPEEDO_REDUND_LSBIT       48
+#define CORE_SPEEDO_REDUND_MSBIT       55
+#define CORE_SPEEDO_REDUND_OFFS        (CORE_SPEEDO_REDUND_MSBIT - CORE_SPEEDO_MSBIT)
+
+#define SPEEDO_MULT                    4
+
+#define CHIP_ID                                0x804
+#define CHIP_MINOR_SHIFT               16
+#define CHIP_MINOR_MASK                        (0xF << CHIP_MINOR_SHIFT)
+
+#define PROCESS_CORNERS_NUM            4
+
+#define SPEEDO_ID_SELECT_0(rev)                ((rev) <= 2)
+#define SPEEDO_ID_SELECT_1(sku)                \
+       (((sku) != 20) && ((sku) != 23) && ((sku) != 24) && \
+        ((sku) != 27) && ((sku) != 28))
+
+/* Maximum speedo levels for each CPU process corner */
+static const u32 cpu_process_speedos[][PROCESS_CORNERS_NUM] = {
+/* proc_id 0   1    2         3                  */
+       {315, 366, 420, UINT_MAX}, /* speedo_id 0 */
+       {303, 368, 419, UINT_MAX}, /* speedo_id 1 */
+       {316, 331, 383, UINT_MAX}, /* speedo_id 2 */
+};
+
+/* Maximum speedo levels for each core process corner */
+static const u32 core_process_speedos[][PROCESS_CORNERS_NUM] = {
+/* proc_id 0   1    2         3                  */
+       {165, 195, 224, UINT_MAX}, /* speedo_id 0 */
+       {165, 195, 224, UINT_MAX}, /* speedo_id 1 */
+       {165, 195, 224, UINT_MAX}, /* speedo_id 2 */
+};
+
+static int cpu_process_id;
+static int core_process_id;
+static int soc_speedo_id;
+
+void tegra_init_speedo_data(void)
+{
+       u32 reg, val;
+       int i, bit, rev;
+       int sku = tegra_sku_id();
+       void __iomem *apb_misc = IO_ADDRESS(TEGRA_APB_MISC_BASE);
+
+       reg = readl(apb_misc + CHIP_ID);
+       rev = (reg & CHIP_MINOR_MASK) >> CHIP_MINOR_SHIFT;
+       if (SPEEDO_ID_SELECT_0(rev))
+               soc_speedo_id = 0;
+       else if (SPEEDO_ID_SELECT_1(sku))
+               soc_speedo_id = 1;
+       else
+               soc_speedo_id = 2;
+       BUG_ON(soc_speedo_id >= ARRAY_SIZE(cpu_process_speedos));
+       BUG_ON(soc_speedo_id >= ARRAY_SIZE(core_process_speedos));
+
+       val = 0;
+       for (bit = CPU_SPEEDO_MSBIT; bit >= CPU_SPEEDO_LSBIT; bit--) {
+               reg = tegra_spare_fuse(bit) |
+                       tegra_spare_fuse(bit + CPU_SPEEDO_REDUND_OFFS);
+               val = (val << 1) | (reg & 0x1);
+       }
+       val = val * SPEEDO_MULT;
+       pr_debug("%s CPU speedo level %u\n", __func__, val);
+
+       for (i = 0; i < (PROCESS_CORNERS_NUM - 1); i++) {
+               if (val <= cpu_process_speedos[soc_speedo_id][i])
+                       break;
+       }
+       cpu_process_id = i;
+
+       val = 0;
+       for (bit = CORE_SPEEDO_MSBIT; bit >= CORE_SPEEDO_LSBIT; bit--) {
+               reg = tegra_spare_fuse(bit) |
+                       tegra_spare_fuse(bit + CORE_SPEEDO_REDUND_OFFS);
+               val = (val << 1) | (reg & 0x1);
+       }
+       val = val * SPEEDO_MULT;
+       pr_debug("%s Core speedo level %u\n", __func__, val);
+
+       for (i = 0; i < (PROCESS_CORNERS_NUM - 1); i++) {
+               if (val <= core_process_speedos[soc_speedo_id][i])
+                       break;
+       }
+       core_process_id = i;
+
+       pr_info("Tegra SKU: %d Rev: A%.2d CPU Process: %d Core Process: %d"
+               " Speedo ID: %d\n", sku, rev, cpu_process_id, core_process_id,
+               soc_speedo_id);
+}
+
+int tegra_cpu_process_id(void)
+{
+       return cpu_process_id;
+}
+
+int tegra_core_process_id(void)
+{
+       return core_process_id;
+}
+
+int tegra_soc_speedo_id(void)
+{
+       return soc_speedo_id;
+}