summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/Makefile1
-rw-r--r--arch/arm/mach-tegra/common.c2
-rw-r--r--arch/arm/mach-tegra/fuse-cache.c124
-rw-r--r--arch/arm/mach-tegra/include/mach/fuse.h25
-rw-r--r--arch/arm/mach-tegra/sysfs-fuse.c32
5 files changed, 184 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index fd2d7e7bdb7b..4ac66f080964 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -20,6 +20,7 @@ obj-y += platsmp.o localtimer.o headsmp.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += headsmp-t2.o
endif
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_save.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += fuse-cache.o
obj-$(CONFIG_MACH_TEGRA_GENERIC) += board-generic.o
obj-$(CONFIG_CPU_FREQ) += cpufreq.o
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index 85b61f337e3e..0d26f87b8e8b 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -28,6 +28,7 @@
#include <mach/iomap.h>
#include <mach/dma.h>
+#include <mach/fuse.h>
#include "board.h"
@@ -120,6 +121,7 @@ void __init tegra_common_init(void)
"iram", NVMEM_HEAP_CARVEOUT_IRAM);
tegra_init_clock();
tegra_init_cache();
+ tegra_init_fuse_cache();
tegra_dma_init();
tegra_mc_init();
arm_pm_restart = tegra_machine_restart;
diff --git a/arch/arm/mach-tegra/fuse-cache.c b/arch/arm/mach-tegra/fuse-cache.c
new file mode 100644
index 000000000000..1e994fb48b40
--- /dev/null
+++ b/arch/arm/mach-tegra/fuse-cache.c
@@ -0,0 +1,124 @@
+/*
+ * arch/arm/mach-tegra/fuse-cache.c
+ *
+ * Interface to kfuses on Tegra 200
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <mach/fuse.h>
+#include <mach/iomap.h>
+#include <ap20/arclk_rst.h>
+#include <ap20/arfuse.h>
+
+/* register definition */
+#define KFUSE_STATE 0x80
+#define KFUSE_STATE_DONE 0x10000
+#define KFUSE_STATE_CRCPASS 0x20000
+#define KFUSE_KEYADDR 0x88
+#define KFUSE_KEYADDR_AUTOINC 0x10000
+#define KFUSE_KEYADDR_ADDR(addr) (addr)
+#define KFUSE_KEYS 0x8c
+
+#define KFUSE_CACHE_SZ (144 * 4)
+
+static u32 *kfuse_cache;
+static int kfuse_cache_isvalid;
+
+/* set start address in auto-increment mode */
+static inline void kfuse_set_autoinc_addr(u16 addr)
+{
+ writel(KFUSE_KEYADDR_ADDR(0) | KFUSE_KEYADDR_AUTOINC,
+ IO_ADDRESS(TEGRA_KFUSE_BASE) + KFUSE_KEYADDR);
+}
+
+static inline void kfuse_clock_enable(int e)
+{
+ if (e) {
+ writel(CLK_RST_CONTROLLER_CLK_ENB_H_SET_0_SET_CLK_ENB_KFUSE_FIELD,
+ IO_ADDRESS(TEGRA_CLK_RESET_BASE +
+ CLK_RST_CONTROLLER_CLK_ENB_H_SET_0));
+ } else {
+ writel(CLK_RST_CONTROLLER_CLK_ENB_H_CLR_0_CLR_CLK_ENB_KFUSE_FIELD,
+ IO_ADDRESS(TEGRA_CLK_RESET_BASE +
+ CLK_RST_CONTROLLER_CLK_ENB_H_CLR_0));
+ }
+}
+
+static void kfuse_wait_ready(void)
+{
+ int retries = 1;
+ /* wait for hardware to finish loading and verifying key data */
+ do {
+ u32 val;
+ val = readl(IO_ADDRESS(TEGRA_KFUSE_BASE) + KFUSE_STATE);
+ if ((val & KFUSE_STATE_DONE) == KFUSE_STATE_DONE) {
+ /* hardware does CRC check */
+ kfuse_cache_isvalid = (val & KFUSE_STATE_CRCPASS) == KFUSE_STATE_CRCPASS;
+ break;
+ }
+ msleep(10);
+ } while( --retries >= 0 );
+}
+
+static void kfuse_rewind(void)
+{
+ /* force HW to decode and check fuses if it has not already done so */
+ kfuse_set_autoinc_addr(0);
+ kfuse_wait_ready();
+ // kfuse_set_autoinc_addr(0);
+}
+
+static inline u32 kfuse_read(void)
+{
+ return readl(IO_ADDRESS(TEGRA_KFUSE_BASE) + KFUSE_KEYS);
+}
+
+/* this is called very early in init because there is a bug that can cause
+ * corruption if DMA and non-DMA requests overlap on APB bus. */
+void __init tegra_init_fuse_cache(void) {
+ unsigned i;
+
+ kfuse_cache = kzalloc(KFUSE_CACHE_SZ, GFP_KERNEL);
+
+ kfuse_clock_enable(1);
+
+ kfuse_rewind();
+
+ printk(KERN_DEBUG "kfuse_cache_isvalid=%d\n", kfuse_cache_isvalid);
+
+ if (!kfuse_cache_isvalid) {
+ printk(KERN_ERR "kfuse CRC or ECC error\n");
+ } else {
+ /* load if no CRC or ECC errors */
+ for (i = 0; i < KFUSE_CACHE_SZ / sizeof (u32); i ++)
+ kfuse_cache[i] = kfuse_read();
+ }
+
+ kfuse_clock_enable(0);
+}
+
+const u32 *tegra_kfuse_cache_get(size_t *size) {
+ if (size) *size = KFUSE_CACHE_SZ;
+ return kfuse_cache;
+}
diff --git a/arch/arm/mach-tegra/include/mach/fuse.h b/arch/arm/mach-tegra/include/mach/fuse.h
new file mode 100644
index 000000000000..3a4f71e46b94
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/fuse.h
@@ -0,0 +1,25 @@
+/*
+ * arch/arm/mach-tegra/fuse.h
+ *
+ * Copyright (c) 2010, 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.
+ *
+ */
+
+#ifndef __MACH_TEGRA_FUSE_H
+#define __MACH_TEGRA_FUSE_H
+
+#include <linux/init.h>
+
+void __init tegra_init_fuse_cache(void);
+const u32 *tegra_kfuse_cache_get(size_t *size);
+
+#endif
diff --git a/arch/arm/mach-tegra/sysfs-fuse.c b/arch/arm/mach-tegra/sysfs-fuse.c
index d32d098972ab..d2a6c2b0bb04 100644
--- a/arch/arm/mach-tegra/sysfs-fuse.c
+++ b/arch/arm/mach-tegra/sysfs-fuse.c
@@ -34,6 +34,8 @@
#include <linux/kernel.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>
+#include <linux/mm.h>
+#include <mach/fuse.h>
#include "nvddk_fuse.h"
#include "mach/nvrm_linux.h"
@@ -67,6 +69,12 @@ typedef enum
} TegraFuseSizeInBytes;
+static ssize_t nvfuse_raw_read(struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t off, size_t count);
+
+static int nvfuse_raw_mmap(struct kobject *kobj,
+ struct bin_attribute *attr, struct vm_area_struct *vma);
+
static ssize_t sysfsfuse_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf);
@@ -74,6 +82,15 @@ static ssize_t sysfsfuse_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count);
+static struct bin_attribute nvfuse_raw_attr = {
+ .attr = {
+ .name = "kfuse_raw",
+ .mode = 0440,
+ },
+ .read = &nvfuse_raw_read,
+ .mmap = &nvfuse_raw_mmap,
+};
+
static struct kobj_attribute nvfuse_DeviceKey_attr =
__ATTR(DeviceKey, 0440, sysfsfuse_show, sysfsfuse_store);
@@ -113,7 +130,20 @@ static struct kobj_attribute nvfuse_SecBootDeviceSelectRaw_attr =
static struct kobj_attribute nvfuse_ReservedOdm_attr =
__ATTR(ReservedOdm, 0440, sysfsfuse_show, sysfsfuse_store);
+static ssize_t nvfuse_raw_read(struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count)
+{
+ memcpy(buf, attr->private + off, count);
+ return count;
+}
+static int nvfuse_raw_mmap(struct kobject *kobj, struct bin_attribute *attr, struct vm_area_struct *vma)
+{
+ if(remap_pfn_range(vma, vma->vm_start, virt_to_phys(attr->private) >> PAGE_SHIFT, attr->size, vma->vm_page_prot)) {
+ printk(KERN_ERR "nvfuse_raw_mmap failed\n");
+ return -EIO;
+ }
+ return 0;
+}
// return the fuse type based on the fuse name.
NvDdkFuseDataType GetFuseType(const char *str, unsigned int* pSize)
@@ -441,6 +471,8 @@ static int __init sysfsfuse_init(void)
sysfs_chmod_file(nvfuse_kobj, &nvfuse_OdmProduction_attr.attr, 0640);
}
+ nvfuse_raw_attr.private = tegra_kfuse_cache_get(&nvfuse_raw_attr.size);
+ CHK_ERR(sysfs_create_bin_file(nvfuse_kobj, &nvfuse_raw_attr));
CHK_ERR(sysfs_create_file(nvfuse_kobj, &nvfuse_DeviceKey_attr.attr));
CHK_ERR(sysfs_create_file(nvfuse_kobj, &nvfuse_JtagDisable_attr.attr));
CHK_ERR(sysfs_create_file(nvfuse_kobj, &nvfuse_KeyProgrammed_attr.attr));