summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorErik Gilling <konkers@android.com>2011-02-24 17:13:10 -0800
committerErik Gilling <konkers@android.com>2011-02-24 17:13:10 -0800
commit56edb8f957edbfe32739a5c709bf8f587be52724 (patch)
treedde05e7b8c912cbc1f7d6e1d665c91a376eb3474 /arch
parenta5856ce00674aa34a38a55dd6960638182604d91 (diff)
parent1249163215ede23cb139a6da9f69e19a393e70f4 (diff)
Merge branch linux-tegra-2.6.36 into android-tegra-2.6.36
Conflicts: drivers/video/tegra/dc/hdmi.c Change-Id: I10fd2dbcc07d7961dd75e10a2c4de926457c2912
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/include/asm/hardware/cache-l2x0.h1
-rw-r--r--arch/arm/mach-tegra/Makefile2
-rw-r--r--arch/arm/mach-tegra/apbio.c151
-rw-r--r--arch/arm/mach-tegra/apbio.h20
-rw-r--r--arch/arm/mach-tegra/common.c5
-rw-r--r--arch/arm/mach-tegra/cortex-a9.S2
-rw-r--r--arch/arm/mach-tegra/fuse.c135
-rw-r--r--arch/arm/mach-tegra/fuse.h1
-rw-r--r--arch/arm/mach-tegra/include/mach/dc.h10
-rw-r--r--arch/arm/mach-tegra/include/mach/kfuse.h20
-rw-r--r--arch/arm/mach-tegra/kfuse.c88
-rw-r--r--arch/arm/mach-tegra/tegra2_clocks.c1
12 files changed, 305 insertions, 131 deletions
diff --git a/arch/arm/include/asm/hardware/cache-l2x0.h b/arch/arm/include/asm/hardware/cache-l2x0.h
index 787c06ada555..d62847df3df5 100644
--- a/arch/arm/include/asm/hardware/cache-l2x0.h
+++ b/arch/arm/include/asm/hardware/cache-l2x0.h
@@ -54,6 +54,7 @@
#define L2X0_LINE_TAG 0xF30
#define L2X0_DEBUG_CTRL 0xF40
#define L2X0_PREFETCH_OFFSET 0xF60
+#define L2X0_PWR_CTRL 0xF80
#ifndef __ASSEMBLY__
extern void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask);
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index b2374b94d9ea..70ad2462cb17 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -1,4 +1,5 @@
obj-y += common.o
+obj-y += apbio.o
obj-y += io.o
obj-y += irq.o legacy_irq.o
obj-y += clock.o
@@ -11,6 +12,7 @@ obj-y += delay.o
obj-y += powergate.o
obj-y += suspend.o
obj-y += fuse.o
+obj-y += kfuse.o
obj-y += tegra_i2s_audio.o
obj-y += tegra_spdif_audio.o
obj-y += mc.o
diff --git a/arch/arm/mach-tegra/apbio.c b/arch/arm/mach-tegra/apbio.c
new file mode 100644
index 000000000000..d6e08c966e72
--- /dev/null
+++ b/arch/arm/mach-tegra/apbio.c
@@ -0,0 +1,151 @@
+/*
+ * arch/arm/mach-tegra/apbio.c
+ *
+ * Copyright (C) 2010 NVIDIA Corporation.
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * 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/kernel.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+
+#include <mach/dma.h>
+#include <mach/iomap.h>
+
+#include "apbio.h"
+
+static DEFINE_MUTEX(tegra_apb_dma_lock);
+
+#ifdef CONFIG_TEGRA_SYSTEM_DMA
+static struct tegra_dma_channel *tegra_apb_dma;
+static u32 *tegra_apb_bb;
+static dma_addr_t tegra_apb_bb_phys;
+static DECLARE_COMPLETION(tegra_apb_wait);
+
+static void apb_dma_complete(struct tegra_dma_req *req)
+{
+ complete(&tegra_apb_wait);
+}
+
+static inline u32 apb_readl(unsigned long offset)
+{
+ struct tegra_dma_req req;
+ int ret;
+
+ if (!tegra_apb_dma)
+ return readl(IO_TO_VIRT(offset));
+
+ mutex_lock(&tegra_apb_dma_lock);
+ req.complete = apb_dma_complete;
+ req.to_memory = 1;
+ req.dest_addr = tegra_apb_bb_phys;
+ req.dest_bus_width = 32;
+ req.dest_wrap = 1;
+ req.source_addr = offset;
+ req.source_bus_width = 32;
+ req.source_wrap = 4;
+ req.req_sel = 0;
+ req.size = 4;
+
+ INIT_COMPLETION(tegra_apb_wait);
+
+ tegra_dma_enqueue_req(tegra_apb_dma, &req);
+
+ ret = wait_for_completion_timeout(&tegra_apb_wait,
+ msecs_to_jiffies(50));
+
+ if (WARN(ret == 0, "apb read dma timed out"))
+ *(u32 *)tegra_apb_bb = 0;
+
+ mutex_unlock(&tegra_apb_dma_lock);
+ return *((u32 *)tegra_apb_bb);
+}
+
+static inline void apb_writel(u32 value, unsigned long offset)
+{
+ struct tegra_dma_req req;
+ int ret;
+
+ if (!tegra_apb_dma) {
+ writel(value, IO_TO_VIRT(offset));
+ return;
+ }
+
+ mutex_lock(&tegra_apb_dma_lock);
+ *((u32 *)tegra_apb_bb) = value;
+ req.complete = apb_dma_complete;
+ req.to_memory = 0;
+ req.dest_addr = offset;
+ req.dest_wrap = 4;
+ req.dest_bus_width = 32;
+ req.source_addr = tegra_apb_bb_phys;
+ req.source_bus_width = 32;
+ req.source_wrap = 1;
+ req.req_sel = 0;
+ req.size = 4;
+
+ INIT_COMPLETION(tegra_apb_wait);
+
+ tegra_dma_enqueue_req(tegra_apb_dma, &req);
+
+ ret = wait_for_completion_timeout(&tegra_apb_wait,
+ msecs_to_jiffies(50));
+
+ mutex_unlock(&tegra_apb_dma_lock);
+}
+#else
+static inline u32 apb_readl(unsigned long offset)
+{
+ return readl(IO_TO_VIRT(offset));
+}
+
+static inline void apb_writel(u32 value, unsigned long offset)
+{
+ writel(value, IO_TO_VIRT(offset));
+}
+#endif
+
+u32 tegra_apb_readl(unsigned long offset)
+{
+ return apb_readl(offset);
+}
+
+void tegra_apb_writel(u32 value, unsigned long offset)
+{
+ apb_writel(value, offset);
+}
+
+void tegra_init_apb_dma(void)
+{
+#ifdef CONFIG_TEGRA_SYSTEM_DMA
+ tegra_apb_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT |
+ TEGRA_DMA_SHARED);
+ if (!tegra_apb_dma) {
+ pr_err("%s: can not allocate dma channel\n", __func__);
+ return;
+ }
+
+ tegra_apb_bb = dma_alloc_coherent(NULL, sizeof(u32),
+ &tegra_apb_bb_phys, GFP_KERNEL);
+ if (!tegra_apb_bb) {
+ pr_err("%s: can not allocate bounce buffer\n", __func__);
+ tegra_dma_free_channel(tegra_apb_dma);
+ tegra_apb_dma = NULL;
+ return;
+ }
+#endif
+}
diff --git a/arch/arm/mach-tegra/apbio.h b/arch/arm/mach-tegra/apbio.h
new file mode 100644
index 000000000000..a8b8250c891a
--- /dev/null
+++ b/arch/arm/mach-tegra/apbio.h
@@ -0,0 +1,20 @@
+/*
+ * arch/arm/mach-tegra/apbio.h
+ *
+ * Copyright (C) 2010 NVIDIA Corporation.
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * 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.
+ *
+ */
+
+u32 tegra_apb_readl(unsigned long offset);
+void tegra_apb_writel(u32 value, unsigned long offset);
+void tegra_init_apb_dma(void);
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index 5283a17f3d2b..c1ecbdf3f0a3 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -33,6 +33,7 @@
#include <mach/powergate.h>
#include <mach/system.h>
+#include "apbio.h"
#include "board.h"
#include "clock.h"
#include "fuse.h"
@@ -70,6 +71,7 @@ static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
{ "emc", NULL, 0, true },
{ "csite", NULL, 0, true },
{ "timer", NULL, 0, true },
+ { "kfuse", NULL, 0, true },
{ "rtc", NULL, 0, true },
/* set frequencies of some device clocks */
@@ -89,6 +91,7 @@ void __init tegra_init_cache(void)
writel(0x331, p + L2X0_TAG_LATENCY_CTRL);
writel(0x441, p + L2X0_DATA_LATENCY_CTRL);
writel(7, p + L2X0_PREFETCH_OFFSET);
+ writel(2, p + L2X0_PWR_CTRL);
l2x0_init(p, 0x7C480001, 0x8200c3fe);
#endif
@@ -141,7 +144,7 @@ void __init tegra_common_init(void)
tegra_init_power();
tegra_init_cache();
tegra_dma_init();
- tegra_init_fuse_dma();
+ tegra_init_apb_dma();
}
static int __init tegra_bootloader_fb_arg(char *options)
diff --git a/arch/arm/mach-tegra/cortex-a9.S b/arch/arm/mach-tegra/cortex-a9.S
index 91d787f2adcb..1ca815d0fab8 100644
--- a/arch/arm/mach-tegra/cortex-a9.S
+++ b/arch/arm/mach-tegra/cortex-a9.S
@@ -536,6 +536,8 @@ ENTRY(__cortex_a9_l2x0_restart)
str r6, [r9, #L2X0_DATA_LATENCY_CTRL]
str r7, [r9, #L2X0_PREFETCH_OFFSET]
str r4, [r9, #L2X0_AUX_CTRL]
+ mov r4, #0x2 @ L2X0_DYNAMIC_CLK_GATING_EN
+ str r4, [r9, #L2X0_PWR_CTRL]
cmp r0, #0
beq __reenable_l2x0
diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c
index fcb6d05495fb..869860c0d41d 100644
--- a/arch/arm/mach-tegra/fuse.c
+++ b/arch/arm/mach-tegra/fuse.c
@@ -19,30 +19,17 @@
#include <linux/kernel.h>
#include <linux/io.h>
-#include <linux/dma-mapping.h>
-#include <linux/spinlock.h>
-#include <linux/completion.h>
-#include <linux/sched.h>
-#include <linux/mutex.h>
-#include <mach/dma.h>
#include <mach/iomap.h>
#include "fuse.h"
+#include "apbio.h"
#define FUSE_UID_LOW 0x108
#define FUSE_UID_HIGH 0x10c
#define FUSE_SKU_INFO 0x110
#define FUSE_SPARE_BIT 0x200
-static DEFINE_MUTEX(tegra_fuse_dma_lock);
-
-#ifdef CONFIG_TEGRA_SYSTEM_DMA
-static struct tegra_dma_channel *tegra_fuse_dma;
-static u32 *tegra_fuse_bb;
-static dma_addr_t tegra_fuse_bb_phys;
-static DECLARE_COMPLETION(tegra_fuse_wait);
-
static const char *tegra_revision_name[TEGRA_REVISION_MAX] = {
[TEGRA_REVISION_UNKNOWN] = "unknown",
[TEGRA_REVISION_A02] = "A02",
@@ -50,102 +37,19 @@ static const char *tegra_revision_name[TEGRA_REVISION_MAX] = {
[TEGRA_REVISION_A03p] = "A03 prime",
};
-static void fuse_dma_complete(struct tegra_dma_req *req)
-{
- complete(&tegra_fuse_wait);
-}
-
-static inline u32 fuse_readl(unsigned long offset)
-{
- struct tegra_dma_req req;
- int ret;
-
- if (!tegra_fuse_dma)
- return readl(IO_TO_VIRT(TEGRA_FUSE_BASE + offset));
-
- mutex_lock(&tegra_fuse_dma_lock);
- req.complete = fuse_dma_complete;
- req.to_memory = 1;
- req.dest_addr = tegra_fuse_bb_phys;
- req.dest_bus_width = 32;
- req.dest_wrap = 1;
- req.source_addr = TEGRA_FUSE_BASE + offset;
- req.source_bus_width = 32;
- req.source_wrap = 4;
- req.req_sel = 0;
- req.size = 4;
-
- INIT_COMPLETION(tegra_fuse_wait);
-
- tegra_dma_enqueue_req(tegra_fuse_dma, &req);
-
- ret = wait_for_completion_timeout(&tegra_fuse_wait,
- msecs_to_jiffies(50));
-
- if (WARN(ret == 0, "fuse read dma timed out"))
- *(u32 *)tegra_fuse_bb = 0;
-
- mutex_unlock(&tegra_fuse_dma_lock);
- return *((u32 *)tegra_fuse_bb);
-}
-
-static inline void fuse_writel(u32 value, unsigned long offset)
-{
- struct tegra_dma_req req;
- int ret;
-
- if (!tegra_fuse_dma) {
- writel(value, IO_TO_VIRT(TEGRA_FUSE_BASE + offset));
- return;
- }
-
- mutex_lock(&tegra_fuse_dma_lock);
- *((u32 *)tegra_fuse_bb) = value;
- req.complete = fuse_dma_complete;
- req.to_memory = 0;
- req.dest_addr = TEGRA_FUSE_BASE + offset;
- req.dest_wrap = 4;
- req.dest_bus_width = 32;
- req.source_addr = tegra_fuse_bb_phys;
- req.source_bus_width = 32;
- req.source_wrap = 1;
- req.req_sel = 0;
- req.size = 4;
-
- INIT_COMPLETION(tegra_fuse_wait);
-
- tegra_dma_enqueue_req(tegra_fuse_dma, &req);
-
- ret = wait_for_completion_timeout(&tegra_fuse_wait,
- msecs_to_jiffies(50));
-
- mutex_unlock(&tegra_fuse_dma_lock);
-}
-#else
-static inline u32 fuse_readl(unsigned long offset)
-{
- return readl(IO_TO_VIRT(TEGRA_FUSE_BASE + offset));
-}
-
-static inline void fuse_writel(u32 value, unsigned long offset)
-{
- writel(value, IO_TO_VIRT(TEGRA_FUSE_BASE + offset));
-}
-#endif
-
u32 tegra_fuse_readl(unsigned long offset)
{
- return fuse_readl(offset);
+ return tegra_apb_readl(TEGRA_FUSE_BASE + offset);
}
void tegra_fuse_writel(u32 value, unsigned long offset)
{
- fuse_writel(value, offset);
+ tegra_apb_writel(value, TEGRA_FUSE_BASE + offset);
}
static inline bool get_spare_fuse(int bit)
{
- return fuse_readl(FUSE_SPARE_BIT + bit * 4);
+ return tegra_apb_readl(FUSE_SPARE_BIT + bit * 4);
}
void tegra_init_fuse(void)
@@ -160,40 +64,19 @@ void tegra_init_fuse(void)
tegra_core_process_id());
}
-void tegra_init_fuse_dma(void)
-{
-#ifdef CONFIG_TEGRA_SYSTEM_DMA
- tegra_fuse_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT |
- TEGRA_DMA_SHARED);
- if (!tegra_fuse_dma) {
- pr_err("%s: can not allocate dma channel\n", __func__);
- return;
- }
-
- tegra_fuse_bb = dma_alloc_coherent(NULL, sizeof(u32),
- &tegra_fuse_bb_phys, GFP_KERNEL);
- if (!tegra_fuse_bb) {
- pr_err("%s: can not allocate bounce buffer\n", __func__);
- tegra_dma_free_channel(tegra_fuse_dma);
- tegra_fuse_dma = NULL;
- return;
- }
-#endif
-}
-
unsigned long long tegra_chip_uid(void)
{
unsigned long long lo, hi;
- lo = fuse_readl(FUSE_UID_LOW);
- hi = fuse_readl(FUSE_UID_HIGH);
+ lo = tegra_fuse_readl(FUSE_UID_LOW);
+ hi = tegra_fuse_readl(FUSE_UID_HIGH);
return (hi << 32ull) | lo;
}
int tegra_sku_id(void)
{
int sku_id;
- u32 reg = fuse_readl(FUSE_SKU_INFO);
+ u32 reg = tegra_fuse_readl(FUSE_SKU_INFO);
sku_id = reg & 0xFF;
return sku_id;
}
@@ -201,7 +84,7 @@ int tegra_sku_id(void)
int tegra_cpu_process_id(void)
{
int cpu_process_id;
- u32 reg = fuse_readl(FUSE_SPARE_BIT);
+ u32 reg = tegra_fuse_readl(FUSE_SPARE_BIT);
cpu_process_id = (reg >> 6) & 3;
return cpu_process_id;
}
@@ -209,7 +92,7 @@ int tegra_cpu_process_id(void)
int tegra_core_process_id(void)
{
int core_process_id;
- u32 reg = fuse_readl(FUSE_SPARE_BIT);
+ u32 reg = tegra_fuse_readl(FUSE_SPARE_BIT);
core_process_id = (reg >> 12) & 3;
return core_process_id;
}
diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h
index 8a9042635f2b..e48838e69e95 100644
--- a/arch/arm/mach-tegra/fuse.h
+++ b/arch/arm/mach-tegra/fuse.h
@@ -30,7 +30,6 @@ int tegra_sku_id(void);
int tegra_cpu_process_id(void);
int tegra_core_process_id(void);
void tegra_init_fuse(void);
-void tegra_init_fuse_dma(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/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h
index f5a64cba61ae..9bd26194f0c9 100644
--- a/arch/arm/mach-tegra/include/mach/dc.h
+++ b/arch/arm/mach-tegra/include/mach/dc.h
@@ -73,9 +73,13 @@ struct tegra_dc_out {
int (*disable)(void);
};
-#define TEGRA_DC_OUT_HOTPLUG_HIGH (0 << 1)
-#define TEGRA_DC_OUT_HOTPLUG_LOW (1 << 1)
-#define TEGRA_DC_OUT_HOTPLUG_MASK (1 << 1)
+/* bits for tegra_dc_out.flags */
+#define TEGRA_DC_OUT_HOTPLUG_HIGH (0 << 1)
+#define TEGRA_DC_OUT_HOTPLUG_LOW (1 << 1)
+#define TEGRA_DC_OUT_HOTPLUG_MASK (1 << 1)
+#define TEGRA_DC_OUT_NVHDCP_POLICY_ALWAYS_ON (0 << 2)
+#define TEGRA_DC_OUT_NVHDCP_POLICY_ON_DEMAND (1 << 2)
+#define TEGRA_DC_OUT_NVHDCP_POLICY_MASK (1 << 2)
#define TEGRA_DC_ALIGN_MSB 0
#define TEGRA_DC_ALIGN_LSB 1
diff --git a/arch/arm/mach-tegra/include/mach/kfuse.h b/arch/arm/mach-tegra/include/mach/kfuse.h
new file mode 100644
index 000000000000..cfe85cc86ff2
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/kfuse.h
@@ -0,0 +1,20 @@
+/*
+ * arch/arm/mach-tegra/kfuse.h
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
+ * 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.
+ *
+ */
+
+/* there are 144 32-bit values in total */
+#define KFUSE_DATA_SZ (144 * 4)
+
+int tegra_kfuse_read(void *dest, size_t len);
diff --git a/arch/arm/mach-tegra/kfuse.c b/arch/arm/mach-tegra/kfuse.c
new file mode 100644
index 000000000000..f5de828bb508
--- /dev/null
+++ b/arch/arm/mach-tegra/kfuse.c
@@ -0,0 +1,88 @@
+/*
+ * arch/arm/mach-tegra/kfuse.c
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation.
+ *
+ * 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.
+ *
+ */
+
+/* The kfuse block stores downstream and upstream HDCP keys for use by HDMI
+ * module.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+
+#include <mach/iomap.h>
+#include <mach/kfuse.h>
+
+#include "apbio.h"
+
+/* register definition */
+#define KFUSE_STATE 0x80
+#define KFUSE_STATE_DONE (1u << 16)
+#define KFUSE_STATE_CRCPASS (1u << 17)
+#define KFUSE_KEYADDR 0x88
+#define KFUSE_KEYADDR_AUTOINC (1u << 16)
+#define KFUSE_KEYS 0x8c
+
+static inline u32 tegra_kfuse_readl(unsigned long offset)
+{
+ return tegra_apb_readl(TEGRA_KFUSE_BASE + offset);
+}
+
+static inline void tegra_kfuse_writel(u32 value, unsigned long offset)
+{
+ tegra_apb_writel(value, TEGRA_KFUSE_BASE + offset);
+}
+
+static int wait_for_done(void)
+{
+ u32 reg;
+ int retries = 50;
+ do {
+ reg = tegra_kfuse_readl(KFUSE_STATE);
+ if (reg & KFUSE_STATE_DONE);
+ return 0;
+ msleep(10);
+ } while(--retries);
+ return -ETIMEDOUT;
+}
+
+/* read up to KFUSE_DATA_SZ bytes into dest.
+ * always starts at the first kfuse.
+ */
+int tegra_kfuse_read(void *dest, size_t len)
+{
+ u32 v;
+ unsigned cnt;
+
+ if (len > KFUSE_DATA_SZ)
+ return -EINVAL;
+
+ tegra_kfuse_writel(KFUSE_KEYADDR_AUTOINC, KFUSE_KEYADDR);
+ wait_for_done();
+
+ if ((tegra_kfuse_readl(KFUSE_STATE) & KFUSE_STATE_CRCPASS) == 0) {
+ pr_err("kfuse: crc failed\n");
+ return -EIO;
+ }
+
+ for (cnt = 0; cnt < len; cnt += 4) {
+ v = tegra_kfuse_readl(KFUSE_KEYS);
+ memcpy(dest + cnt, &v, sizeof v);
+ }
+
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 2f809e0219b7..446d05bbd97e 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -1955,6 +1955,7 @@ static struct clk tegra_clk_emc = {
struct clk tegra_list_clks[] = {
PERIPH_CLK("rtc", "rtc-tegra", NULL, 4, 0, 32768, mux_clk_32k, PERIPH_NO_RESET),
PERIPH_CLK("timer", "timer", NULL, 5, 0, 26000000, mux_clk_m, 0),
+ PERIPH_CLK("kfuse", "kfuse-tegra", NULL, 40, 0, 26000000, mux_clk_m, 0),
PERIPH_CLK("i2s1", "i2s.0", NULL, 11, 0x100, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71),
PERIPH_CLK("i2s2", "i2s.1", NULL, 18, 0x104, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71),
PERIPH_CLK("spdif_out", "spdif_out", NULL, 10, 0x108, 100000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71),