soc/tegra: Add efuse driver for Tegra
authorPeter De Schrijver <pdeschrijver@nvidia.com>
Thu, 12 Jun 2014 15:36:37 +0000 (18:36 +0300)
committerThierry Reding <treding@nvidia.com>
Thu, 17 Jul 2014 12:36:01 +0000 (14:36 +0200)
Implement fuse driver for Tegra20, Tegra30, Tegra114 and Tegra124. This
replaces functionality previously provided in arch/arm/mach-tegra, which
is removed in this patch.

While at it, move the only user of the global tegra_revision variable
over to tegra_sku_info.revision and export tegra_fuse_readl() to allow
drivers to read calibration fuses.

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
23 files changed:
Documentation/ABI/testing/sysfs-driver-tegra-fuse [new file with mode: 0644]
arch/arm/mach-tegra/Makefile
arch/arm/mach-tegra/fuse.c [deleted file]
arch/arm/mach-tegra/fuse.h [deleted file]
arch/arm/mach-tegra/reset.c
arch/arm/mach-tegra/tegra.c
arch/arm/mach-tegra/tegra114_speedo.c [deleted file]
arch/arm/mach-tegra/tegra20_speedo.c [deleted file]
arch/arm/mach-tegra/tegra30_speedo.c [deleted file]
drivers/misc/fuse/Makefile [new file with mode: 0644]
drivers/soc/Makefile
drivers/soc/tegra/Makefile [new file with mode: 0644]
drivers/soc/tegra/fuse/Makefile [new file with mode: 0644]
drivers/soc/tegra/fuse/fuse-tegra.c [new file with mode: 0644]
drivers/soc/tegra/fuse/fuse-tegra20.c [new file with mode: 0644]
drivers/soc/tegra/fuse/fuse-tegra30.c [new file with mode: 0644]
drivers/soc/tegra/fuse/fuse.h [new file with mode: 0644]
drivers/soc/tegra/fuse/speedo-tegra114.c [new file with mode: 0644]
drivers/soc/tegra/fuse/speedo-tegra124.c [new file with mode: 0644]
drivers/soc/tegra/fuse/speedo-tegra20.c [new file with mode: 0644]
drivers/soc/tegra/fuse/speedo-tegra30.c [new file with mode: 0644]
drivers/soc/tegra/fuse/tegra-apbmisc.c [new file with mode: 0644]
include/soc/tegra/fuse.h

diff --git a/Documentation/ABI/testing/sysfs-driver-tegra-fuse b/Documentation/ABI/testing/sysfs-driver-tegra-fuse
new file mode 100644 (file)
index 0000000..69f5af6
--- /dev/null
@@ -0,0 +1,11 @@
+What:          /sys/devices/*/<our-device>/fuse
+Date:          February 2014
+Contact:       Peter De Schrijver <pdeschrijver@nvidia.com>
+Description:   read-only access to the efuses on Tegra20, Tegra30, Tegra114
+               and Tegra124 SoC's from NVIDIA. The efuses contain write once
+               data programmed at the factory. The data is layed out in 32bit
+               words in LSB first format. Each bit represents a single value
+               as decoded from the fuse registers. Bits order/assignment
+               exactly matches the HW registers, including any unused bits.
+Users:         any user space application which wants to read the efuses on
+               Tegra SoC's
index 6fbfbb77dcd991f496da168542e4f703dd950203..e8601bb56f98f8132a70df16a9f22936889055f2 100644 (file)
@@ -2,7 +2,6 @@ asflags-y                               += -march=armv7-a
 
 obj-y                                   += io.o
 obj-y                                   += irq.o
-obj-y                                  += fuse.o
 obj-y                                  += pmc.o
 obj-y                                  += flowctrl.o
 obj-y                                  += powergate.o
@@ -13,13 +12,11 @@ obj-y                                       += reset-handler.o
 obj-y                                  += sleep.o
 obj-y                                  += tegra.o
 obj-$(CONFIG_CPU_IDLE)                 += cpuidle.o
-obj-$(CONFIG_ARCH_TEGRA_2x_SOC)                += tegra20_speedo.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)                += sleep-tegra20.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)                += pm-tegra20.o
 ifeq ($(CONFIG_CPU_IDLE),y)
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)                += cpuidle-tegra20.o
 endif
-obj-$(CONFIG_ARCH_TEGRA_3x_SOC)                += tegra30_speedo.o
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)                += sleep-tegra30.o
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)                += pm-tegra30.o
 ifeq ($(CONFIG_CPU_IDLE),y)
@@ -28,7 +25,6 @@ endif
 obj-$(CONFIG_SMP)                      += platsmp.o headsmp.o
 obj-$(CONFIG_HOTPLUG_CPU)               += hotplug.o
 
-obj-$(CONFIG_ARCH_TEGRA_114_SOC)       += tegra114_speedo.o
 obj-$(CONFIG_ARCH_TEGRA_114_SOC)       += sleep-tegra30.o
 obj-$(CONFIG_ARCH_TEGRA_114_SOC)       += pm-tegra30.o
 ifeq ($(CONFIG_CPU_IDLE),y)
diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c
deleted file mode 100644 (file)
index b22e76a..0000000
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * arch/arm/mach-tegra/fuse.c
- *
- * Copyright (C) 2010 Google, Inc.
- * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
- *
- * Author:
- *     Colin Cross <ccross@android.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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.
- *
- */
-
-#include <linux/clk.h>
-#include <linux/export.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/random.h>
-
-#include <soc/tegra/fuse.h>
-
-#include "apbio.h"
-#include "fuse.h"
-#include "iomap.h"
-
-/* Tegra20 only */
-#define FUSE_UID_LOW           0x108
-#define FUSE_UID_HIGH          0x10c
-
-/* Tegra30 and later */
-#define FUSE_VENDOR_CODE       0x200
-#define FUSE_FAB_CODE          0x204
-#define FUSE_LOT_CODE_0                0x208
-#define FUSE_LOT_CODE_1                0x20c
-#define FUSE_WAFER_ID          0x210
-#define FUSE_X_COORDINATE      0x214
-#define FUSE_Y_COORDINATE      0x218
-
-#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_cpu_speedo_id;               /* only exist in Tegra30 and later */
-int tegra_soc_speedo_id;
-enum tegra_revision tegra_revision;
-
-static struct clk *fuse_clk;
-static int tegra_fuse_spare_bit;
-static void (*tegra_init_speedo_data)(void);
-
-/* The BCT to use at boot is specified by board straps that can be read
- * through a APB misc register and decoded. 2 bits, i.e. 4 possible BCTs.
- */
-int tegra_bct_strapping;
-
-#define STRAP_OPT 0x008
-#define GMI_AD0 (1 << 4)
-#define GMI_AD1 (1 << 5)
-#define RAM_ID_MASK (GMI_AD0 | GMI_AD1)
-#define RAM_CODE_SHIFT 4
-
-static const char *tegra_revision_name[TEGRA_REVISION_MAX] = {
-       [TEGRA_REVISION_UNKNOWN] = "unknown",
-       [TEGRA_REVISION_A01]     = "A01",
-       [TEGRA_REVISION_A02]     = "A02",
-       [TEGRA_REVISION_A03]     = "A03",
-       [TEGRA_REVISION_A03p]    = "A03 prime",
-       [TEGRA_REVISION_A04]     = "A04",
-};
-
-static void tegra_fuse_enable_clk(void)
-{
-       if (IS_ERR(fuse_clk))
-               fuse_clk = clk_get_sys(NULL, "fuse");
-       if (IS_ERR(fuse_clk))
-               return;
-       clk_prepare_enable(fuse_clk);
-}
-
-static void tegra_fuse_disable_clk(void)
-{
-       if (IS_ERR(fuse_clk))
-               return;
-       clk_disable_unprepare(fuse_clk);
-}
-
-u32 tegra_fuse_readl(unsigned long offset)
-{
-       return tegra_apb_readl(TEGRA_FUSE_BASE + offset);
-}
-
-bool tegra_spare_fuse(int bit)
-{
-       bool ret;
-
-       tegra_fuse_enable_clk();
-
-       ret = tegra_fuse_readl(tegra_fuse_spare_bit + bit * 4);
-
-       tegra_fuse_disable_clk();
-
-       return ret;
-}
-
-static enum tegra_revision tegra_get_revision(u32 id)
-{
-       u32 minor_rev = (id >> 16) & 0xf;
-
-       switch (minor_rev) {
-       case 1:
-               return TEGRA_REVISION_A01;
-       case 2:
-               return TEGRA_REVISION_A02;
-       case 3:
-               if (tegra_get_chip_id() == TEGRA20 &&
-                       (tegra_spare_fuse(18) || tegra_spare_fuse(19)))
-                       return TEGRA_REVISION_A03p;
-               else
-                       return TEGRA_REVISION_A03;
-       case 4:
-               return TEGRA_REVISION_A04;
-       default:
-               return TEGRA_REVISION_UNKNOWN;
-       }
-}
-
-static void tegra_get_process_id(void)
-{
-       u32 reg;
-
-       tegra_fuse_enable_clk();
-
-       reg = tegra_fuse_readl(tegra_fuse_spare_bit);
-       tegra_cpu_process_id = (reg >> 6) & 3;
-       reg = tegra_fuse_readl(tegra_fuse_spare_bit);
-       tegra_core_process_id = (reg >> 12) & 3;
-
-       tegra_fuse_disable_clk();
-}
-
-u32 tegra_read_chipid(void)
-{
-       return readl_relaxed(IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804);
-}
-
-u8 tegra_get_chip_id(void)
-{
-       u32 id = tegra_read_chipid();
-
-       return (id >> 8) & 0xff;
-}
-
-static void __init tegra20_fuse_init_randomness(void)
-{
-       u32 randomness[2];
-
-       randomness[0] = tegra_fuse_readl(FUSE_UID_LOW);
-       randomness[1] = tegra_fuse_readl(FUSE_UID_HIGH);
-
-       add_device_randomness(randomness, sizeof(randomness));
-}
-
-/* Applies to Tegra30 or later */
-static void __init tegra30_fuse_init_randomness(void)
-{
-       u32 randomness[7];
-
-       randomness[0] = tegra_fuse_readl(FUSE_VENDOR_CODE);
-       randomness[1] = tegra_fuse_readl(FUSE_FAB_CODE);
-       randomness[2] = tegra_fuse_readl(FUSE_LOT_CODE_0);
-       randomness[3] = tegra_fuse_readl(FUSE_LOT_CODE_1);
-       randomness[4] = tegra_fuse_readl(FUSE_WAFER_ID);
-       randomness[5] = tegra_fuse_readl(FUSE_X_COORDINATE);
-       randomness[6] = tegra_fuse_readl(FUSE_Y_COORDINATE);
-
-       add_device_randomness(randomness, sizeof(randomness));
-}
-
-void __init tegra_init_fuse(void)
-{
-       u32 id;
-       u32 randomness[5];
-       u8 chip_id;
-
-       u32 reg = readl(IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x48));
-       reg |= 1 << 28;
-       writel(reg, IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x48));
-
-       /*
-        * Enable FUSE clock. This needs to be hardcoded because the clock
-        * subsystem is not active during early boot.
-        */
-       reg = readl(IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x14));
-       reg |= 1 << 7;
-       writel(reg, IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x14));
-       fuse_clk = ERR_PTR(-EINVAL);
-
-       reg = tegra_fuse_readl(FUSE_SKU_INFO);
-       randomness[0] = reg;
-       tegra_sku_id = reg & 0xFF;
-
-       reg = tegra_apb_readl(TEGRA_APB_MISC_BASE + STRAP_OPT);
-       randomness[1] = reg;
-       tegra_bct_strapping = (reg & RAM_ID_MASK) >> RAM_CODE_SHIFT;
-
-       id = tegra_read_chipid();
-       randomness[2] = id;
-       chip_id = (id >> 8) & 0xff;
-
-       switch (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;
-       case TEGRA114:
-               tegra_init_speedo_data = &tegra114_init_speedo_data;
-               break;
-       default:
-               pr_warn("Tegra: unknown chip id %d\n", chip_id);
-               tegra_fuse_spare_bit = TEGRA20_FUSE_SPARE_BIT;
-               tegra_init_speedo_data = &tegra_get_process_id;
-       }
-
-       tegra_revision = tegra_get_revision(id);
-       tegra_init_speedo_data();
-       randomness[3] = (tegra_cpu_process_id << 16) | tegra_core_process_id;
-       randomness[4] = (tegra_cpu_speedo_id << 16) | tegra_soc_speedo_id;
-
-       add_device_randomness(randomness, sizeof(randomness));
-       switch (chip_id) {
-       case TEGRA20:
-               tegra20_fuse_init_randomness();
-               break;
-       case TEGRA30:
-       case TEGRA114:
-       default:
-               tegra30_fuse_init_randomness();
-               break;
-       }
-
-       pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n",
-               tegra_revision_name[tegra_revision],
-               tegra_sku_id, tegra_cpu_process_id,
-               tegra_core_process_id);
-}
diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h
deleted file mode 100644 (file)
index 48a4886..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2010 Google, Inc.
- * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
- *
- * Author:
- *     Colin Cross <ccross@android.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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.
- *
- */
-
-#ifndef __MACH_TEGRA_FUSE_H
-#define __MACH_TEGRA_FUSE_H
-
-#define SKU_ID_T20     8
-#define SKU_ID_T25SE   20
-#define SKU_ID_AP25    23
-#define SKU_ID_T25     24
-#define SKU_ID_AP25E   27
-#define SKU_ID_T25E    28
-
-#ifndef __ASSEMBLY__
-
-extern int tegra_sku_id;
-extern int tegra_cpu_process_id;
-extern int tegra_core_process_id;
-extern int tegra_cpu_speedo_id;                /* only exist in Tegra30 and later */
-extern int tegra_soc_speedo_id;
-
-unsigned long long tegra_chip_uid(void);
-bool tegra_spare_fuse(int bit);
-u32 tegra_fuse_readl(unsigned long offset);
-
-#ifdef CONFIG_ARCH_TEGRA_2x_SOC
-void tegra20_init_speedo_data(void);
-#else
-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
-
-#ifdef CONFIG_ARCH_TEGRA_114_SOC
-void tegra114_init_speedo_data(void);
-#else
-static inline void tegra114_init_speedo_data(void) {}
-#endif
-#endif /* __ASSEMBLY__ */
-
-#endif
index f94fdf89d4575abf86b413f368d9b9870b7d023c..5377495d41b86e6afd7fbbc7e09748bb4980da02 100644 (file)
@@ -25,7 +25,6 @@
 #include <asm/firmware.h>
 #include <asm/hardware/cache-l2x0.h>
 
-#include "fuse.h"
 #include "iomap.h"
 #include "irammap.h"
 #include "reset.h"
index a359931c595253eb7cbcea693280aa81e47b1ed3..c0faae8c0822278bcdee0ce4fea391b3e6fca560 100644 (file)
@@ -104,7 +104,8 @@ static void __init tegra_dt_init(void)
                goto out;
 
        soc_dev_attr->family = kasprintf(GFP_KERNEL, "Tegra");
-       soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d", tegra_revision);
+       soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d",
+                                          tegra_sku_info.revision);
        soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%u", tegra_get_chip_id());
 
        soc_dev = soc_device_register(soc_dev_attr);
diff --git a/arch/arm/mach-tegra/tegra114_speedo.c b/arch/arm/mach-tegra/tegra114_speedo.c
deleted file mode 100644 (file)
index d0a6d59..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (c) 2013, 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/bug.h>
-#include <linux/kernel.h>
-
-#include <soc/tegra/fuse.h>
-
-#include "fuse.h"
-
-#define CORE_PROCESS_CORNERS_NUM       2
-#define CPU_PROCESS_CORNERS_NUM                2
-
-enum {
-       THRESHOLD_INDEX_0,
-       THRESHOLD_INDEX_1,
-       THRESHOLD_INDEX_COUNT,
-};
-
-static const u32 core_process_speedos[][CORE_PROCESS_CORNERS_NUM] = {
-       {1123,     UINT_MAX},
-       {0,        UINT_MAX},
-};
-
-static const u32 cpu_process_speedos[][CPU_PROCESS_CORNERS_NUM] = {
-       {1695,     UINT_MAX},
-       {0,        UINT_MAX},
-};
-
-static void rev_sku_to_speedo_ids(int rev, int sku, int *threshold)
-{
-       u32 tmp;
-
-       switch (sku) {
-       case 0x00:
-       case 0x10:
-       case 0x05:
-       case 0x06:
-               tegra_cpu_speedo_id = 1;
-               tegra_soc_speedo_id = 0;
-               *threshold = THRESHOLD_INDEX_0;
-               break;
-
-       case 0x03:
-       case 0x04:
-               tegra_cpu_speedo_id = 2;
-               tegra_soc_speedo_id = 1;
-               *threshold = THRESHOLD_INDEX_1;
-               break;
-
-       default:
-               pr_err("Tegra114 Unknown SKU %d\n", sku);
-               tegra_cpu_speedo_id = 0;
-               tegra_soc_speedo_id = 0;
-               *threshold = THRESHOLD_INDEX_0;
-               break;
-       }
-
-       if (rev == TEGRA_REVISION_A01) {
-               tmp = tegra_fuse_readl(0x270) << 1;
-               tmp |= tegra_fuse_readl(0x26c);
-               if (!tmp)
-                       tegra_cpu_speedo_id = 0;
-       }
-}
-
-void tegra114_init_speedo_data(void)
-{
-       u32 cpu_speedo_val;
-       u32 core_speedo_val;
-       int threshold;
-       int i;
-
-       BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) !=
-                       THRESHOLD_INDEX_COUNT);
-       BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) !=
-                       THRESHOLD_INDEX_COUNT);
-
-       rev_sku_to_speedo_ids(tegra_revision, tegra_sku_id, &threshold);
-
-       cpu_speedo_val = tegra_fuse_readl(0x12c) + 1024;
-       core_speedo_val = tegra_fuse_readl(0x134);
-
-       for (i = 0; i < CPU_PROCESS_CORNERS_NUM; i++)
-               if (cpu_speedo_val < cpu_process_speedos[threshold][i])
-                       break;
-       tegra_cpu_process_id = i;
-
-       for (i = 0; i < CORE_PROCESS_CORNERS_NUM; i++)
-               if (core_speedo_val < core_process_speedos[threshold][i])
-                       break;
-       tegra_core_process_id = i;
-}
diff --git a/arch/arm/mach-tegra/tegra20_speedo.c b/arch/arm/mach-tegra/tegra20_speedo.c
deleted file mode 100644 (file)
index 2907cf8..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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/bug.h>
-#include <linux/kernel.h>
-
-#include <soc/tegra/fuse.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 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))
-
-enum {
-       SPEEDO_ID_0,
-       SPEEDO_ID_1,
-       SPEEDO_ID_2,
-       SPEEDO_ID_COUNT,
-};
-
-static const u32 cpu_process_speedos[][PROCESS_CORNERS_NUM] = {
-       {315, 366, 420, UINT_MAX},
-       {303, 368, 419, UINT_MAX},
-       {316, 331, 383, UINT_MAX},
-};
-
-static const u32 core_process_speedos[][PROCESS_CORNERS_NUM] = {
-       {165, 195, 224, UINT_MAX},
-       {165, 195, 224, UINT_MAX},
-       {165, 195, 224, UINT_MAX},
-};
-
-void tegra20_init_speedo_data(void)
-{
-       u32 reg;
-       u32 val;
-       int i;
-
-       BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != SPEEDO_ID_COUNT);
-       BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != SPEEDO_ID_COUNT);
-
-       if (SPEEDO_ID_SELECT_0(tegra_revision))
-               tegra_soc_speedo_id = SPEEDO_ID_0;
-       else if (SPEEDO_ID_SELECT_1(tegra_sku_id))
-               tegra_soc_speedo_id = SPEEDO_ID_1;
-       else
-               tegra_soc_speedo_id = SPEEDO_ID_2;
-
-       val = 0;
-       for (i = CPU_SPEEDO_MSBIT; i >= CPU_SPEEDO_LSBIT; i--) {
-               reg = tegra_spare_fuse(i) |
-                       tegra_spare_fuse(i + CPU_SPEEDO_REDUND_OFFS);
-               val = (val << 1) | (reg & 0x1);
-       }
-       val = val * SPEEDO_MULT;
-       pr_debug("%s CPU speedo value %u\n", __func__, val);
-
-       for (i = 0; i < (PROCESS_CORNERS_NUM - 1); i++) {
-               if (val <= cpu_process_speedos[tegra_soc_speedo_id][i])
-                       break;
-       }
-       tegra_cpu_process_id = i;
-
-       val = 0;
-       for (i = CORE_SPEEDO_MSBIT; i >= CORE_SPEEDO_LSBIT; i--) {
-               reg = tegra_spare_fuse(i) |
-                       tegra_spare_fuse(i + CORE_SPEEDO_REDUND_OFFS);
-               val = (val << 1) | (reg & 0x1);
-       }
-       val = val * SPEEDO_MULT;
-       pr_debug("%s Core speedo value %u\n", __func__, val);
-
-       for (i = 0; i < (PROCESS_CORNERS_NUM - 1); i++) {
-               if (val <= core_process_speedos[tegra_soc_speedo_id][i])
-                       break;
-       }
-       tegra_core_process_id = i;
-
-       pr_info("Tegra20 Soc Speedo ID %d", tegra_soc_speedo_id);
-}
diff --git a/arch/arm/mach-tegra/tegra30_speedo.c b/arch/arm/mach-tegra/tegra30_speedo.c
deleted file mode 100644 (file)
index 6f6102c..0000000
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * 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/bug.h>
-#include <linux/kernel.h>
-
-#include <soc/tegra/fuse.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);
-}
diff --git a/drivers/misc/fuse/Makefile b/drivers/misc/fuse/Makefile
new file mode 100644 (file)
index 0000000..0679c4f
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_ARCH_TEGRA)       += tegra/
index 0f7c44793b29453b6b36881e74d6bc72fe8f09b7..3b1b95d932d194e0533944ed4f0d021d405c24a3 100644 (file)
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_ARCH_QCOM)                += qcom/
+obj-$(CONFIG_ARCH_TEGRA)       += tegra/
diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile
new file mode 100644 (file)
index 0000000..236600f
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_ARCH_TEGRA) += fuse/
diff --git a/drivers/soc/tegra/fuse/Makefile b/drivers/soc/tegra/fuse/Makefile
new file mode 100644 (file)
index 0000000..3af357d
--- /dev/null
@@ -0,0 +1,8 @@
+obj-y                                  += fuse-tegra.o
+obj-y                                  += fuse-tegra30.o
+obj-y                                  += tegra-apbmisc.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC)                += fuse-tegra20.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC)                += speedo-tegra20.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC)                += speedo-tegra30.o
+obj-$(CONFIG_ARCH_TEGRA_114_SOC)       += speedo-tegra114.o
+obj-$(CONFIG_ARCH_TEGRA_124_SOC)       += speedo-tegra124.o
diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c
new file mode 100644 (file)
index 0000000..03742ed
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2013-2014, 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/device.h>
+#include <linux/kobject.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+
+#include <soc/tegra/fuse.h>
+
+#include "fuse.h"
+
+static u32 (*fuse_readl)(const unsigned int offset);
+static int fuse_size;
+struct tegra_sku_info tegra_sku_info;
+
+static const char *tegra_revision_name[TEGRA_REVISION_MAX] = {
+       [TEGRA_REVISION_UNKNOWN] = "unknown",
+       [TEGRA_REVISION_A01]     = "A01",
+       [TEGRA_REVISION_A02]     = "A02",
+       [TEGRA_REVISION_A03]     = "A03",
+       [TEGRA_REVISION_A03p]    = "A03 prime",
+       [TEGRA_REVISION_A04]     = "A04",
+};
+
+static u8 fuse_readb(const unsigned int offset)
+{
+       u32 val;
+
+       val = fuse_readl(round_down(offset, 4));
+       val >>= (offset % 4) * 8;
+       val &= 0xff;
+
+       return val;
+}
+
+static ssize_t fuse_read(struct file *fd, struct kobject *kobj,
+                       struct bin_attribute *attr, char *buf,
+                       loff_t pos, size_t size)
+{
+       int i;
+
+       if (pos < 0 || pos >= fuse_size)
+               return 0;
+
+       if (size > fuse_size - pos)
+               size = fuse_size - pos;
+
+       for (i = 0; i < size; i++)
+               buf[i] = fuse_readb(pos + i);
+
+       return i;
+}
+
+static struct bin_attribute fuse_bin_attr = {
+       .attr = { .name = "fuse", .mode = S_IRUGO, },
+       .read = fuse_read,
+};
+
+static const struct of_device_id car_match[] __initconst = {
+       { .compatible = "nvidia,tegra20-car", },
+       { .compatible = "nvidia,tegra30-car", },
+       { .compatible = "nvidia,tegra114-car", },
+       { .compatible = "nvidia,tegra124-car", },
+       {},
+};
+
+static void tegra_enable_fuse_clk(void __iomem *base)
+{
+       u32 reg;
+
+       reg = readl_relaxed(base + 0x48);
+       reg |= 1 << 28;
+       writel(reg, base + 0x48);
+
+       /*
+        * Enable FUSE clock. This needs to be hardcoded because the clock
+        * subsystem is not active during early boot.
+        */
+       reg = readl(base + 0x14);
+       reg |= 1 << 7;
+       writel(reg, base + 0x14);
+}
+
+int tegra_fuse_readl(unsigned long offset, u32 *value)
+{
+       if (!fuse_readl)
+               return -EPROBE_DEFER;
+
+       *value = fuse_readl(offset);
+
+       return 0;
+}
+EXPORT_SYMBOL(tegra_fuse_readl);
+
+int tegra_fuse_create_sysfs(struct device *dev, int size,
+                    u32 (*readl)(const unsigned int offset))
+{
+       if (fuse_size)
+               return -ENODEV;
+
+       fuse_bin_attr.size = size;
+       fuse_bin_attr.read = fuse_read;
+
+       fuse_size = size;
+       fuse_readl = readl;
+
+       return device_create_bin_file(dev, &fuse_bin_attr);
+}
+
+void __init tegra_init_fuse(void)
+{
+       struct device_node *np;
+       void __iomem *car_base;
+
+       tegra_init_apbmisc();
+
+       np = of_find_matching_node(NULL, car_match);
+       car_base = of_iomap(np, 0);
+       if (car_base) {
+               tegra_enable_fuse_clk(car_base);
+               iounmap(car_base);
+       } else {
+               pr_err("Could not enable fuse clk. ioremap tegra car failed.\n");
+               return;
+       }
+
+       if (tegra_get_chip_id() == TEGRA20)
+               tegra20_init_fuse_early();
+       else
+               tegra30_init_fuse_early();
+
+       pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n",
+               tegra_revision_name[tegra_sku_info.revision],
+               tegra_sku_info.sku_id, tegra_sku_info.cpu_process_id,
+               tegra_sku_info.core_process_id);
+       pr_debug("Tegra CPU Speedo ID %d, Soc Speedo ID %d\n",
+               tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id);
+}
diff --git a/drivers/soc/tegra/fuse/fuse-tegra20.c b/drivers/soc/tegra/fuse/fuse-tegra20.c
new file mode 100644 (file)
index 0000000..7a9d0e0
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2013-2014, 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/>.
+ *
+ * Based on drivers/misc/eeprom/sunxi_sid.c
+ */
+
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+
+#include <soc/tegra/fuse.h>
+
+#include "fuse.h"
+
+#define FUSE_BEGIN     0x100
+#define FUSE_SIZE      0x1f8
+#define FUSE_UID_LOW   0x08
+#define FUSE_UID_HIGH  0x0c
+
+static phys_addr_t fuse_phys;
+static struct clk *fuse_clk;
+static void __iomem __initdata *fuse_base;
+
+static u32 tegra20_fuse_readl(const unsigned int offset)
+{
+       int ret;
+       u32 val;
+
+       clk_prepare_enable(fuse_clk);
+
+       ret = tegra_apb_readl_using_dma(fuse_phys + FUSE_BEGIN + offset, &val);
+
+       clk_disable_unprepare(fuse_clk);
+
+       return (ret < 0) ? 0 : val;
+}
+
+static const struct of_device_id tegra20_fuse_of_match[] = {
+       { .compatible = "nvidia,tegra20-efuse" },
+       {},
+};
+
+static int tegra20_fuse_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+
+       fuse_clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(fuse_clk)) {
+               dev_err(&pdev->dev, "missing clock");
+               return PTR_ERR(fuse_clk);
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -EINVAL;
+       fuse_phys = res->start;
+
+       if (tegra_fuse_create_sysfs(&pdev->dev, FUSE_SIZE, tegra20_fuse_readl))
+               return -ENODEV;
+
+       dev_dbg(&pdev->dev, "loaded\n");
+
+       return 0;
+}
+
+static struct platform_driver tegra20_fuse_driver = {
+       .probe = tegra20_fuse_probe,
+       .driver = {
+               .name = "tegra20_fuse",
+               .owner = THIS_MODULE,
+               .of_match_table = tegra20_fuse_of_match,
+       }
+};
+
+static int __init tegra20_fuse_init(void)
+{
+       return platform_driver_register(&tegra20_fuse_driver);
+}
+postcore_initcall(tegra20_fuse_init);
+
+/* Early boot code. This code is called before the devices are created */
+
+u32 __init tegra20_fuse_early(const unsigned int offset)
+{
+       return readl_relaxed(fuse_base + FUSE_BEGIN + offset);
+}
+
+bool __init tegra20_spare_fuse_early(int spare_bit)
+{
+       u32 offset = spare_bit * 4;
+       bool value;
+
+       value = tegra20_fuse_early(offset + 0x100);
+
+       return value;
+}
+
+static void __init tegra20_fuse_add_randomness(void)
+{
+       u32 randomness[7];
+
+       randomness[0] = tegra_sku_info.sku_id;
+       randomness[1] = tegra_read_straps();
+       randomness[2] = tegra_read_chipid();
+       randomness[3] = tegra_sku_info.cpu_process_id << 16;
+       randomness[3] |= tegra_sku_info.core_process_id;
+       randomness[4] = tegra_sku_info.cpu_speedo_id << 16;
+       randomness[4] |= tegra_sku_info.soc_speedo_id;
+       randomness[5] = tegra20_fuse_early(FUSE_UID_LOW);
+       randomness[6] = tegra20_fuse_early(FUSE_UID_HIGH);
+
+       add_device_randomness(randomness, sizeof(randomness));
+}
+
+void __init tegra20_init_fuse_early(void)
+{
+       fuse_base = ioremap(TEGRA_FUSE_BASE, TEGRA_FUSE_SIZE);
+
+       tegra_init_revision();
+       tegra20_init_speedo_data(&tegra_sku_info);
+       tegra20_fuse_add_randomness();
+
+       iounmap(fuse_base);
+}
diff --git a/drivers/soc/tegra/fuse/fuse-tegra30.c b/drivers/soc/tegra/fuse/fuse-tegra30.c
new file mode 100644 (file)
index 0000000..5999cf3
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2013-2014, 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/device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+
+#include <soc/tegra/fuse.h>
+
+#include "fuse.h"
+
+#define FUSE_BEGIN     0x100
+
+/* Tegra30 and later */
+#define FUSE_VENDOR_CODE       0x100
+#define FUSE_FAB_CODE          0x104
+#define FUSE_LOT_CODE_0                0x108
+#define FUSE_LOT_CODE_1                0x10c
+#define FUSE_WAFER_ID          0x110
+#define FUSE_X_COORDINATE      0x114
+#define FUSE_Y_COORDINATE      0x118
+
+#define FUSE_HAS_REVISION_INFO BIT(0)
+
+enum speedo_idx {
+       SPEEDO_TEGRA30 = 0,
+       SPEEDO_TEGRA114,
+       SPEEDO_TEGRA124,
+};
+
+struct tegra_fuse_info {
+       int             size;
+       int             spare_bit;
+       enum speedo_idx speedo_idx;
+};
+
+static void __iomem *fuse_base;
+static struct clk *fuse_clk;
+static struct tegra_fuse_info *fuse_info;
+
+u32 tegra30_fuse_readl(const unsigned int offset)
+{
+       u32 val;
+
+       /*
+        * early in the boot, the fuse clock will be enabled by
+        * tegra_init_fuse()
+        */
+
+       if (fuse_clk)
+               clk_prepare_enable(fuse_clk);
+
+       val = readl_relaxed(fuse_base + FUSE_BEGIN + offset);
+
+       if (fuse_clk)
+               clk_disable_unprepare(fuse_clk);
+
+       return val;
+}
+
+static struct tegra_fuse_info tegra30_info = {
+       .size                   = 0x2a4,
+       .spare_bit              = 0x144,
+       .speedo_idx             = SPEEDO_TEGRA30,
+};
+
+static struct tegra_fuse_info tegra114_info = {
+       .size                   = 0x2a0,
+       .speedo_idx             = SPEEDO_TEGRA114,
+};
+
+static struct tegra_fuse_info tegra124_info = {
+       .size                   = 0x300,
+       .speedo_idx             = SPEEDO_TEGRA124,
+};
+
+static const struct of_device_id tegra30_fuse_of_match[] = {
+       { .compatible = "nvidia,tegra30-efuse", .data = &tegra30_info },
+       { .compatible = "nvidia,tegra114-efuse", .data = &tegra114_info },
+       { .compatible = "nvidia,tegra124-efuse", .data = &tegra124_info },
+       {},
+};
+
+static int tegra30_fuse_probe(struct platform_device *pdev)
+{
+       const struct of_device_id *of_dev_id;
+
+       of_dev_id = of_match_device(tegra30_fuse_of_match, &pdev->dev);
+       if (!of_dev_id)
+               return -ENODEV;
+
+       fuse_clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(fuse_clk)) {
+               dev_err(&pdev->dev, "missing clock");
+               return PTR_ERR(fuse_clk);
+       }
+
+       platform_set_drvdata(pdev, NULL);
+
+       if (tegra_fuse_create_sysfs(&pdev->dev, fuse_info->size,
+                                   tegra30_fuse_readl))
+               return -ENODEV;
+
+       dev_dbg(&pdev->dev, "loaded\n");
+
+       return 0;
+}
+
+static struct platform_driver tegra30_fuse_driver = {
+       .probe = tegra30_fuse_probe,
+       .driver = {
+               .name = "tegra_fuse",
+               .owner = THIS_MODULE,
+               .of_match_table = tegra30_fuse_of_match,
+       }
+};
+
+static int __init tegra30_fuse_init(void)
+{
+       return platform_driver_register(&tegra30_fuse_driver);
+}
+postcore_initcall(tegra30_fuse_init);
+
+/* Early boot code. This code is called before the devices are created */
+
+typedef void (*speedo_f)(struct tegra_sku_info *sku_info);
+
+static speedo_f __initdata speedo_tbl[] = {
+       [SPEEDO_TEGRA30]        = tegra30_init_speedo_data,
+       [SPEEDO_TEGRA114]       = tegra114_init_speedo_data,
+       [SPEEDO_TEGRA124]       = tegra124_init_speedo_data,
+};
+
+static void __init tegra30_fuse_add_randomness(void)
+{
+       u32 randomness[12];
+
+       randomness[0] = tegra_sku_info.sku_id;
+       randomness[1] = tegra_read_straps();
+       randomness[2] = tegra_read_chipid();
+       randomness[3] = tegra_sku_info.cpu_process_id << 16;
+       randomness[3] |= tegra_sku_info.core_process_id;
+       randomness[4] = tegra_sku_info.cpu_speedo_id << 16;
+       randomness[4] |= tegra_sku_info.soc_speedo_id;
+       randomness[5] = tegra30_fuse_readl(FUSE_VENDOR_CODE);
+       randomness[6] = tegra30_fuse_readl(FUSE_FAB_CODE);
+       randomness[7] = tegra30_fuse_readl(FUSE_LOT_CODE_0);
+       randomness[8] = tegra30_fuse_readl(FUSE_LOT_CODE_1);
+       randomness[9] = tegra30_fuse_readl(FUSE_WAFER_ID);
+       randomness[10] = tegra30_fuse_readl(FUSE_X_COORDINATE);
+       randomness[11] = tegra30_fuse_readl(FUSE_Y_COORDINATE);
+
+       add_device_randomness(randomness, sizeof(randomness));
+}
+
+static void __init legacy_fuse_init(void)
+{
+       switch (tegra_get_chip_id()) {
+       case TEGRA30:
+               fuse_info = &tegra30_info;
+               break;
+       case TEGRA114:
+               fuse_info = &tegra114_info;
+               break;
+       case TEGRA124:
+               fuse_info = &tegra124_info;
+               break;
+       default:
+               return;
+       }
+
+       fuse_base = ioremap(TEGRA_FUSE_BASE, TEGRA_FUSE_SIZE);
+}
+
+bool __init tegra30_spare_fuse(int spare_bit)
+{
+       u32 offset = fuse_info->spare_bit + spare_bit * 4;
+
+       return tegra30_fuse_readl(offset) & 1;
+}
+
+void __init tegra30_init_fuse_early(void)
+{
+       struct device_node *np;
+       const struct of_device_id *of_match;
+
+       np = of_find_matching_node_and_match(NULL, tegra30_fuse_of_match,
+                                               &of_match);
+       if (np) {
+               fuse_base = of_iomap(np, 0);
+               fuse_info = (struct tegra_fuse_info *)of_match->data;
+       } else
+               legacy_fuse_init();
+
+       if (!fuse_base) {
+               pr_warn("fuse DT node missing and unknown chip id: 0x%02x\n",
+                       tegra_get_chip_id());
+               return;
+       }
+
+       tegra_init_revision();
+       speedo_tbl[fuse_info->speedo_idx](&tegra_sku_info);
+       tegra30_fuse_add_randomness();
+}
diff --git a/drivers/soc/tegra/fuse/fuse.h b/drivers/soc/tegra/fuse/fuse.h
new file mode 100644 (file)
index 0000000..b344277
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * Author:
+ *     Colin Cross <ccross@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __DRIVERS_MISC_TEGRA_FUSE_H
+#define __DRIVERS_MISC_TEGRA_FUSE_H
+
+#define TEGRA_FUSE_BASE        0x7000f800
+#define TEGRA_FUSE_SIZE        0x400
+
+int tegra_fuse_create_sysfs(struct device *dev, int size,
+                    u32 (*readl)(const unsigned int offset));
+
+bool tegra30_spare_fuse(int bit);
+u32 tegra30_fuse_readl(const unsigned int offset);
+void tegra30_init_fuse_early(void);
+void tegra_init_revision(void);
+void tegra_init_apbmisc(void);
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+void tegra20_init_speedo_data(struct tegra_sku_info *sku_info);
+bool tegra20_spare_fuse_early(int spare_bit);
+void tegra20_init_fuse_early(void);
+u32 tegra20_fuse_early(const unsigned int offset);
+#else
+static inline void tegra20_init_speedo_data(struct tegra_sku_info *sku_info) {}
+static inline bool tegra20_spare_fuse_early(int spare_bit, void *fuse_base)
+{
+                       return false;
+}
+static inline void tegra20_init_fuse_early(void);
+static inline tegra20_fuse_early(const unsigned int offset);
+{
+                       return 0;
+}
+#endif
+
+
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+void tegra30_init_speedo_data(struct tegra_sku_info *sku_info);
+#else
+static inline void tegra30_init_speedo_data(struct tegra_sku_info *sku_info) {}
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA_114_SOC
+void tegra114_init_speedo_data(struct tegra_sku_info *sku_info);
+#else
+static inline void tegra114_init_speedo_data(struct tegra_sku_info *sku_info) {}
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA_124_SOC
+void tegra124_init_speedo_data(struct tegra_sku_info *sku_info);
+#else
+static inline void tegra124_init_speedo_data(struct tegra_sku_info *sku_info) {}
+#endif
+
+#endif
diff --git a/drivers/soc/tegra/fuse/speedo-tegra114.c b/drivers/soc/tegra/fuse/speedo-tegra114.c
new file mode 100644 (file)
index 0000000..2a6ca03
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2013-2014, 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/bug.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+
+#include <soc/tegra/fuse.h>
+
+#include "fuse.h"
+
+#define CORE_PROCESS_CORNERS   2
+#define CPU_PROCESS_CORNERS    2
+
+enum {
+       THRESHOLD_INDEX_0,
+       THRESHOLD_INDEX_1,
+       THRESHOLD_INDEX_COUNT,
+};
+
+static const u32 __initconst core_process_speedos[][CORE_PROCESS_CORNERS] = {
+       {1123,     UINT_MAX},
+       {0,        UINT_MAX},
+};
+
+static const u32 __initconst cpu_process_speedos[][CPU_PROCESS_CORNERS] = {
+       {1695,     UINT_MAX},
+       {0,        UINT_MAX},
+};
+
+static void __init rev_sku_to_speedo_ids(struct tegra_sku_info *sku_info,
+                                        int *threshold)
+{
+       u32 tmp;
+       u32 sku = sku_info->sku_id;
+       enum tegra_revision rev = sku_info->revision;
+
+       switch (sku) {
+       case 0x00:
+       case 0x10:
+       case 0x05:
+       case 0x06:
+               sku_info->cpu_speedo_id = 1;
+               sku_info->soc_speedo_id = 0;
+               *threshold = THRESHOLD_INDEX_0;
+               break;
+
+       case 0x03:
+       case 0x04:
+               sku_info->cpu_speedo_id = 2;
+               sku_info->soc_speedo_id = 1;
+               *threshold = THRESHOLD_INDEX_1;
+               break;
+
+       default:
+               pr_err("Tegra Unknown SKU %d\n", sku);
+               sku_info->cpu_speedo_id = 0;
+               sku_info->soc_speedo_id = 0;
+               *threshold = THRESHOLD_INDEX_0;
+               break;
+       }
+
+       if (rev == TEGRA_REVISION_A01) {
+               tmp = tegra30_fuse_readl(0x270) << 1;
+               tmp |= tegra30_fuse_readl(0x26c);
+               if (!tmp)
+                       sku_info->cpu_speedo_id = 0;
+       }
+}
+
+void __init tegra114_init_speedo_data(struct tegra_sku_info *sku_info)
+{
+       u32 cpu_speedo_val;
+       u32 core_speedo_val;
+       int threshold;
+       int i;
+
+       BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) !=
+                       THRESHOLD_INDEX_COUNT);
+       BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) !=
+                       THRESHOLD_INDEX_COUNT);
+
+       rev_sku_to_speedo_ids(sku_info, &threshold);
+
+       cpu_speedo_val = tegra30_fuse_readl(0x12c) + 1024;
+       core_speedo_val = tegra30_fuse_readl(0x134);
+
+       for (i = 0; i < CPU_PROCESS_CORNERS; i++)
+               if (cpu_speedo_val < cpu_process_speedos[threshold][i])
+                       break;
+       sku_info->cpu_process_id = i;
+
+       for (i = 0; i < CORE_PROCESS_CORNERS; i++)
+               if (core_speedo_val < core_process_speedos[threshold][i])
+                       break;
+       sku_info->core_process_id = i;
+}
diff --git a/drivers/soc/tegra/fuse/speedo-tegra124.c b/drivers/soc/tegra/fuse/speedo-tegra124.c
new file mode 100644 (file)
index 0000000..4636238
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2013-2014, 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/device.h>
+#include <linux/kernel.h>
+#include <linux/bug.h>
+
+#include <soc/tegra/fuse.h>
+
+#include "fuse.h"
+
+#define CPU_PROCESS_CORNERS    2
+#define GPU_PROCESS_CORNERS    2
+#define CORE_PROCESS_CORNERS   2
+
+#define FUSE_CPU_SPEEDO_0      0x14
+#define FUSE_CPU_SPEEDO_1      0x2c
+#define FUSE_CPU_SPEEDO_2      0x30
+#define FUSE_SOC_SPEEDO_0      0x34
+#define FUSE_SOC_SPEEDO_1      0x38
+#define FUSE_SOC_SPEEDO_2      0x3c
+#define FUSE_CPU_IDDQ          0x18
+#define FUSE_SOC_IDDQ          0x40
+#define FUSE_GPU_IDDQ          0x128
+#define FUSE_FT_REV            0x28
+
+enum {
+       THRESHOLD_INDEX_0,
+       THRESHOLD_INDEX_1,
+       THRESHOLD_INDEX_COUNT,
+};
+
+static const u32 __initconst cpu_process_speedos[][CPU_PROCESS_CORNERS] = {
+       {2190,  UINT_MAX},
+       {0,     UINT_MAX},
+};
+
+static const u32 __initconst gpu_process_speedos[][GPU_PROCESS_CORNERS] = {
+       {1965,  UINT_MAX},
+       {0,     UINT_MAX},
+};
+
+static const u32 __initconst core_process_speedos[][CORE_PROCESS_CORNERS] = {
+       {2101,  UINT_MAX},
+       {0,     UINT_MAX},
+};
+
+static void __init rev_sku_to_speedo_ids(struct tegra_sku_info *sku_info,
+                                        int *threshold)
+{
+       int sku = sku_info->sku_id;
+
+       /* Assign to default */
+       sku_info->cpu_speedo_id = 0;
+       sku_info->soc_speedo_id = 0;
+       sku_info->gpu_speedo_id = 0;
+       *threshold = THRESHOLD_INDEX_0;
+
+       switch (sku) {
+       case 0x00: /* Eng sku */
+       case 0x0F:
+       case 0x23:
+               /* Using the default */
+               break;
+       case 0x83:
+               sku_info->cpu_speedo_id = 2;
+               break;
+
+       case 0x1F:
+       case 0x87:
+       case 0x27:
+               sku_info->cpu_speedo_id = 2;
+               sku_info->soc_speedo_id = 0;
+               sku_info->gpu_speedo_id = 1;
+               *threshold = THRESHOLD_INDEX_0;
+               break;
+       case 0x81:
+       case 0x21:
+       case 0x07:
+               sku_info->cpu_speedo_id = 1;
+               sku_info->soc_speedo_id = 1;
+               sku_info->gpu_speedo_id = 1;
+               *threshold = THRESHOLD_INDEX_1;
+               break;
+       case 0x49:
+       case 0x4A:
+       case 0x48:
+               sku_info->cpu_speedo_id = 4;
+               sku_info->soc_speedo_id = 2;
+               sku_info->gpu_speedo_id = 3;
+               *threshold = THRESHOLD_INDEX_1;
+               break;
+       default:
+               pr_err("Tegra Unknown SKU %d\n", sku);
+               /* Using the default for the error case */
+               break;
+       }
+}
+
+void __init tegra124_init_speedo_data(struct tegra_sku_info *sku_info)
+{
+       int i, threshold, cpu_speedo_0_value, soc_speedo_0_value;
+       int cpu_iddq_value, gpu_iddq_value, soc_iddq_value;
+
+       BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) !=
+                       THRESHOLD_INDEX_COUNT);
+       BUILD_BUG_ON(ARRAY_SIZE(gpu_process_speedos) !=
+                       THRESHOLD_INDEX_COUNT);
+       BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) !=
+                       THRESHOLD_INDEX_COUNT);
+
+       cpu_speedo_0_value = tegra30_fuse_readl(FUSE_CPU_SPEEDO_0);
+
+       /* GPU Speedo is stored in CPU_SPEEDO_2 */
+       sku_info->gpu_speedo_value = tegra30_fuse_readl(FUSE_CPU_SPEEDO_2);
+
+       soc_speedo_0_value = tegra30_fuse_readl(FUSE_SOC_SPEEDO_0);
+
+       cpu_iddq_value = tegra30_fuse_readl(FUSE_CPU_IDDQ);
+       soc_iddq_value = tegra30_fuse_readl(FUSE_SOC_IDDQ);
+       gpu_iddq_value = tegra30_fuse_readl(FUSE_GPU_IDDQ);
+
+       sku_info->cpu_speedo_value = cpu_speedo_0_value;
+
+       if (sku_info->cpu_speedo_value == 0) {
+               pr_warn("Tegra Warning: Speedo value not fused.\n");
+               WARN_ON(1);
+               return;
+       }
+
+       rev_sku_to_speedo_ids(sku_info, &threshold);
+
+       sku_info->cpu_iddq_value = tegra30_fuse_readl(FUSE_CPU_IDDQ);
+
+       for (i = 0; i < GPU_PROCESS_CORNERS; i++)
+               if (sku_info->gpu_speedo_value <
+                       gpu_process_speedos[threshold][i])
+                       break;
+       sku_info->gpu_process_id = i;
+
+       for (i = 0; i < CPU_PROCESS_CORNERS; i++)
+               if (sku_info->cpu_speedo_value <
+                       cpu_process_speedos[threshold][i])
+                               break;
+       sku_info->cpu_process_id = i;
+
+       for (i = 0; i < CORE_PROCESS_CORNERS; i++)
+               if (soc_speedo_0_value <
+                       core_process_speedos[threshold][i])
+                       break;
+       sku_info->core_process_id = i;
+
+       pr_debug("Tegra GPU Speedo ID=%d, Speedo Value=%d\n",
+                sku_info->gpu_speedo_id, sku_info->gpu_speedo_value);
+}
diff --git a/drivers/soc/tegra/fuse/speedo-tegra20.c b/drivers/soc/tegra/fuse/speedo-tegra20.c
new file mode 100644 (file)
index 0000000..eff1b63
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2012-2014, 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/bug.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+
+#include <soc/tegra/fuse.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 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))
+
+enum {
+       SPEEDO_ID_0,
+       SPEEDO_ID_1,
+       SPEEDO_ID_2,
+       SPEEDO_ID_COUNT,
+};
+
+static const u32 __initconst cpu_process_speedos[][PROCESS_CORNERS_NUM] = {
+       {315, 366, 420, UINT_MAX},
+       {303, 368, 419, UINT_MAX},
+       {316, 331, 383, UINT_MAX},
+};
+
+static const u32 __initconst core_process_speedos[][PROCESS_CORNERS_NUM] = {
+       {165, 195, 224, UINT_MAX},
+       {165, 195, 224, UINT_MAX},
+       {165, 195, 224, UINT_MAX},
+};
+
+void __init tegra20_init_speedo_data(struct tegra_sku_info *sku_info)
+{
+       u32 reg;
+       u32 val;
+       int i;
+
+       BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != SPEEDO_ID_COUNT);
+       BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != SPEEDO_ID_COUNT);
+
+       if (SPEEDO_ID_SELECT_0(sku_info->revision))
+               sku_info->soc_speedo_id = SPEEDO_ID_0;
+       else if (SPEEDO_ID_SELECT_1(sku_info->sku_id))
+               sku_info->soc_speedo_id = SPEEDO_ID_1;
+       else
+               sku_info->soc_speedo_id = SPEEDO_ID_2;
+
+       val = 0;
+       for (i = CPU_SPEEDO_MSBIT; i >= CPU_SPEEDO_LSBIT; i--) {
+               reg = tegra20_spare_fuse_early(i) |
+                       tegra20_spare_fuse_early(i + CPU_SPEEDO_REDUND_OFFS);
+               val = (val << 1) | (reg & 0x1);
+       }
+       val = val * SPEEDO_MULT;
+       pr_debug("Tegra CPU speedo value %u\n", val);
+
+       for (i = 0; i < (PROCESS_CORNERS_NUM - 1); i++) {
+               if (val <= cpu_process_speedos[sku_info->soc_speedo_id][i])
+                       break;
+       }
+       sku_info->cpu_process_id = i;
+
+       val = 0;
+       for (i = CORE_SPEEDO_MSBIT; i >= CORE_SPEEDO_LSBIT; i--) {
+               reg = tegra20_spare_fuse_early(i) |
+                       tegra20_spare_fuse_early(i + CORE_SPEEDO_REDUND_OFFS);
+               val = (val << 1) | (reg & 0x1);
+       }
+       val = val * SPEEDO_MULT;
+       pr_debug("Core speedo value %u\n", val);
+
+       for (i = 0; i < (PROCESS_CORNERS_NUM - 1); i++) {
+               if (val <= core_process_speedos[sku_info->soc_speedo_id][i])
+                       break;
+       }
+       sku_info->core_process_id = i;
+}
diff --git a/drivers/soc/tegra/fuse/speedo-tegra30.c b/drivers/soc/tegra/fuse/speedo-tegra30.c
new file mode 100644 (file)
index 0000000..b17f0dc
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2012-2014, 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/bug.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+
+#include <soc/tegra/fuse.h>
+
+#include "fuse.h"
+
+#define CORE_PROCESS_CORNERS   1
+#define CPU_PROCESS_CORNERS    6
+
+#define FUSE_SPEEDO_CALIB_0    0x14
+#define FUSE_PACKAGE_INFO      0XFC
+#define FUSE_TEST_PROG_VER     0X28
+
+#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 __initconst core_process_speedos[][CORE_PROCESS_CORNERS] = {
+       {180},
+       {170},
+       {195},
+       {180},
+       {168},
+       {192},
+       {180},
+       {170},
+       {195},
+       {180},
+       {180},
+       {180},
+};
+
+static const u32 __initconst cpu_process_speedos[][CPU_PROCESS_CORNERS] = {
+       {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 __initdata;
+
+static void __init fuse_speedo_calib(u32 *speedo_g, u32 *speedo_lp)
+{
+       u32 reg;
+       int ate_ver;
+       int bit_minus1;
+       int bit_minus2;
+
+       reg = tegra30_fuse_readl(FUSE_SPEEDO_CALIB_0);
+
+       *speedo_lp = (reg & 0xFFFF) * 4;
+       *speedo_g = ((reg >> 16) & 0xFFFF) * 4;
+
+       ate_ver = tegra30_fuse_readl(FUSE_TEST_PROG_VER);
+       pr_debug("Tegra ATE prog ver %d.%d\n", ate_ver/10, ate_ver%10);
+
+       if (ate_ver >= 26) {
+               bit_minus1 = tegra30_spare_fuse(LP_SPEEDO_BIT_MINUS1);
+               bit_minus1 |= tegra30_spare_fuse(LP_SPEEDO_BIT_MINUS1_R);
+               bit_minus2 = tegra30_spare_fuse(LP_SPEEDO_BIT_MINUS2);
+               bit_minus2 |= tegra30_spare_fuse(LP_SPEEDO_BIT_MINUS2_R);
+               *speedo_lp |= (bit_minus1 << 1) | bit_minus2;
+
+               bit_minus1 = tegra30_spare_fuse(G_SPEEDO_BIT_MINUS1);
+               bit_minus1 |= tegra30_spare_fuse(G_SPEEDO_BIT_MINUS1_R);
+               bit_minus2 = tegra30_spare_fuse(G_SPEEDO_BIT_MINUS2);
+               bit_minus2 |= tegra30_spare_fuse(G_SPEEDO_BIT_MINUS2_R);
+               *speedo_g |= (bit_minus1 << 1) | bit_minus2;
+       } else {
+               *speedo_lp |= 0x3;
+               *speedo_g |= 0x3;
+       }
+}
+
+static void __init rev_sku_to_speedo_ids(struct tegra_sku_info *sku_info)
+{
+       int package_id = tegra30_fuse_readl(FUSE_PACKAGE_INFO) & 0x0F;
+
+       switch (sku_info->revision) {
+       case TEGRA_REVISION_A01:
+               sku_info->cpu_speedo_id = 0;
+               sku_info->soc_speedo_id = 0;
+               threshold_index = THRESHOLD_INDEX_0;
+               break;
+       case TEGRA_REVISION_A02:
+       case TEGRA_REVISION_A03:
+               switch (sku_info->sku_id) {
+               case 0x87:
+               case 0x82:
+                       sku_info->cpu_speedo_id = 1;
+                       sku_info->soc_speedo_id = 1;
+                       threshold_index = THRESHOLD_INDEX_1;
+                       break;
+               case 0x81:
+                       switch (package_id) {
+                       case 1:
+                               sku_info->cpu_speedo_id = 2;
+                               sku_info->soc_speedo_id = 2;
+                               threshold_index = THRESHOLD_INDEX_2;
+                               break;
+                       case 2:
+                               sku_info->cpu_speedo_id = 4;
+                               sku_info->soc_speedo_id = 1;
+                               threshold_index = THRESHOLD_INDEX_7;
+                               break;
+                       default:
+                               pr_err("Tegra Unknown pkg %d\n", package_id);
+                               break;
+                       }
+                       break;
+               case 0x80:
+                       switch (package_id) {
+                       case 1:
+                               sku_info->cpu_speedo_id = 5;
+                               sku_info->soc_speedo_id = 2;
+                               threshold_index = THRESHOLD_INDEX_8;
+                               break;
+                       case 2:
+                               sku_info->cpu_speedo_id = 6;
+                               sku_info->soc_speedo_id = 2;
+                               threshold_index = THRESHOLD_INDEX_9;
+                               break;
+                       default:
+                               pr_err("Tegra Unknown pkg %d\n", package_id);
+                               break;
+                       }
+                       break;
+               case 0x83:
+                       switch (package_id) {
+                       case 1:
+                               sku_info->cpu_speedo_id = 7;
+                               sku_info->soc_speedo_id = 1;
+                               threshold_index = THRESHOLD_INDEX_10;
+                               break;
+                       case 2:
+                               sku_info->cpu_speedo_id = 3;
+                               sku_info->soc_speedo_id = 2;
+                               threshold_index = THRESHOLD_INDEX_3;
+                               break;
+                       default:
+                               pr_err("Tegra Unknown pkg %d\n", package_id);
+                               break;
+                       }
+                       break;
+               case 0x8F:
+                       sku_info->cpu_speedo_id = 8;
+                       sku_info->soc_speedo_id = 1;
+                       threshold_index = THRESHOLD_INDEX_11;
+                       break;
+               case 0x08:
+                       sku_info->cpu_speedo_id = 1;
+                       sku_info->soc_speedo_id = 1;
+                       threshold_index = THRESHOLD_INDEX_4;
+                       break;
+               case 0x02:
+                       sku_info->cpu_speedo_id = 2;
+                       sku_info->soc_speedo_id = 2;
+                       threshold_index = THRESHOLD_INDEX_5;
+                       break;
+               case 0x04:
+                       sku_info->cpu_speedo_id = 3;
+                       sku_info->soc_speedo_id = 2;
+                       threshold_index = THRESHOLD_INDEX_6;
+                       break;
+               case 0:
+                       switch (package_id) {
+                       case 1:
+                               sku_info->cpu_speedo_id = 2;
+                               sku_info->soc_speedo_id = 2;
+                               threshold_index = THRESHOLD_INDEX_2;
+                               break;
+                       case 2:
+                               sku_info->cpu_speedo_id = 3;
+                               sku_info->soc_speedo_id = 2;
+                               threshold_index = THRESHOLD_INDEX_3;
+                               break;
+                       default:
+                               pr_err("Tegra Unknown pkg %d\n", package_id);
+                               break;
+                       }
+                       break;
+               default:
+                       pr_warn("Tegra Unknown SKU %d\n", sku_info->sku_id);
+                       sku_info->cpu_speedo_id = 0;
+                       sku_info->soc_speedo_id = 0;
+                       threshold_index = THRESHOLD_INDEX_0;
+                       break;
+               }
+               break;
+       default:
+               pr_warn("Tegra Unknown chip rev %d\n", sku_info->revision);
+               sku_info->cpu_speedo_id = 0;
+               sku_info->soc_speedo_id = 0;
+               threshold_index = THRESHOLD_INDEX_0;
+               break;
+       }
+}
+
+void __init tegra30_init_speedo_data(struct tegra_sku_info *sku_info)
+{
+       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);
+
+
+       rev_sku_to_speedo_ids(sku_info);
+       fuse_speedo_calib(&cpu_speedo_val, &core_speedo_val);
+       pr_debug("Tegra CPU speedo value %u\n", cpu_speedo_val);
+       pr_debug("Tegra Core speedo value %u\n", core_speedo_val);
+
+       for (i = 0; i < CPU_PROCESS_CORNERS; i++) {
+               if (cpu_speedo_val < cpu_process_speedos[threshold_index][i])
+                       break;
+       }
+       sku_info->cpu_process_id = i - 1;
+
+       if (sku_info->cpu_process_id == -1) {
+               pr_warn("Tegra CPU speedo value %3d out of range",
+                        cpu_speedo_val);
+               sku_info->cpu_process_id = 0;
+               sku_info->cpu_speedo_id = 1;
+       }
+
+       for (i = 0; i < CORE_PROCESS_CORNERS; i++) {
+               if (core_speedo_val < core_process_speedos[threshold_index][i])
+                       break;
+       }
+       sku_info->core_process_id = i - 1;
+
+       if (sku_info->core_process_id == -1) {
+               pr_warn("Tegra CORE speedo value %3d out of range",
+                                core_speedo_val);
+               sku_info->core_process_id = 0;
+               sku_info->soc_speedo_id = 1;
+       }
+}
diff --git a/drivers/soc/tegra/fuse/tegra-apbmisc.c b/drivers/soc/tegra/fuse/tegra-apbmisc.c
new file mode 100644 (file)
index 0000000..bfc1d54
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2014, 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/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+
+#include <soc/tegra/fuse.h>
+
+#include "fuse.h"
+
+#define APBMISC_BASE   0x70000800
+#define APBMISC_SIZE   0x64
+#define FUSE_SKU_INFO  0x10
+
+static void __iomem *apbmisc_base;
+static void __iomem *strapping_base;
+
+u32 tegra_read_chipid(void)
+{
+       return readl_relaxed(apbmisc_base + 4);
+}
+
+u8 tegra_get_chip_id(void)
+{
+       u32 id = tegra_read_chipid();
+
+       return (id >> 8) & 0xff;
+}
+
+u32 tegra_read_straps(void)
+{
+       if (strapping_base)
+               return readl_relaxed(strapping_base);
+       else
+               return 0;
+}
+
+static const struct of_device_id apbmisc_match[] __initconst = {
+       { .compatible = "nvidia,tegra20-apbmisc", },
+       {},
+};
+
+void __init tegra_init_revision(void)
+{
+       u32 id, chip_id, minor_rev;
+       int rev;
+
+       id = tegra_read_chipid();
+       chip_id = (id >> 8) & 0xff;
+       minor_rev = (id >> 16) & 0xf;
+
+       switch (minor_rev) {
+       case 1:
+               rev = TEGRA_REVISION_A01;
+               break;
+       case 2:
+               rev = TEGRA_REVISION_A02;
+               break;
+       case 3:
+               if (chip_id == TEGRA20 && (tegra20_spare_fuse_early(18) ||
+                                          tegra20_spare_fuse_early(19)))
+                       rev = TEGRA_REVISION_A03p;
+               else
+                       rev = TEGRA_REVISION_A03;
+               break;
+       case 4:
+               rev = TEGRA_REVISION_A04;
+               break;
+       default:
+               rev = TEGRA_REVISION_UNKNOWN;
+       }
+
+       tegra_sku_info.revision = rev;
+
+       if (chip_id == TEGRA20)
+               tegra_sku_info.sku_id = tegra20_fuse_early(FUSE_SKU_INFO);
+       else
+               tegra_sku_info.sku_id = tegra30_fuse_readl(FUSE_SKU_INFO);
+}
+
+void __init tegra_init_apbmisc(void)
+{
+       struct device_node *np;
+
+       np = of_find_matching_node(NULL, apbmisc_match);
+       apbmisc_base = of_iomap(np, 0);
+       if (!apbmisc_base) {
+               pr_warn("ioremap tegra apbmisc failed. using %08x instead\n",
+                       APBMISC_BASE);
+               apbmisc_base = ioremap(APBMISC_BASE, APBMISC_SIZE);
+       }
+
+       strapping_base = of_iomap(np, 1);
+       if (!strapping_base)
+               pr_err("ioremap tegra strapping_base failed\n");
+}
index 822eb348e1077e295b9d566b329374e7224ba571..51ac804deba53264f091edd2c95b9ef01b68c5e6 100644 (file)
@@ -22,6 +22,9 @@
 #define TEGRA114       0x35
 #define TEGRA124       0x40
 
+#define TEGRA_FUSE_SKU_CALIB_0 0xf0
+#define TEGRA30_FUSE_SATA_CALIB        0x124
+
 #ifndef __ASSEMBLY__
 
 u32 tegra_read_chipid(void);
@@ -37,11 +40,26 @@ enum tegra_revision {
        TEGRA_REVISION_MAX,
 };
 
+struct tegra_sku_info {
+       int sku_id;
+       int cpu_process_id;
+       int cpu_speedo_id;
+       int cpu_speedo_value;
+       int cpu_iddq_value;
+       int core_process_id;
+       int soc_speedo_id;
+       int gpu_speedo_id;
+       int gpu_process_id;
+       int gpu_speedo_value;
+       enum tegra_revision revision;
+};
+
 u32 tegra_read_straps(void);
 u32 tegra_read_chipid(void);
 void tegra_init_fuse(void);
+int tegra_fuse_readl(unsigned long offset, u32 *value);
 
-extern enum tegra_revision tegra_revision;
+extern struct tegra_sku_info tegra_sku_info;
 
 #if defined(CONFIG_TEGRA20_APB_DMA)
 int tegra_apb_readl_using_dma(unsigned long offset, u32 *value);
This page took 0.053197 seconds and 5 git commands to generate.