summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/kfuse.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/kfuse.c')
-rw-r--r--arch/arm/mach-tegra/kfuse.c114
1 files changed, 114 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/kfuse.c b/arch/arm/mach-tegra/kfuse.c
new file mode 100644
index 000000000000..9e4b482e4691
--- /dev/null
+++ b/arch/arm/mach-tegra/kfuse.c
@@ -0,0 +1,114 @@
+/*
+ * 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 <linux/clk.h>
+
+#include <mach/iomap.h>
+#include <mach/kfuse.h>
+
+#include "clock.h"
+#include "apbio.h"
+
+static struct clk *kfuse_clk = NULL;
+
+/* 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)
+{
+ int err;
+ u32 v;
+ unsigned cnt;
+
+ if (len > KFUSE_DATA_SZ)
+ return -EINVAL;
+
+ if (kfuse_clk == NULL) {
+ kfuse_clk = tegra_get_clock_by_name("kfuse");
+ if (IS_ERR_OR_NULL(kfuse_clk)) {
+ pr_err("kfuse: can't get kfuse clock\n");
+ return -EINVAL;
+ }
+ }
+
+ err = clk_enable(kfuse_clk);
+ if (err)
+ return err;
+
+ tegra_kfuse_writel(KFUSE_KEYADDR_AUTOINC, KFUSE_KEYADDR);
+
+ err = wait_for_done();
+ if (err) {
+ pr_err("kfuse: read timeout\n");
+ clk_disable(kfuse_clk);
+ return err;
+ }
+
+ if ((tegra_kfuse_readl(KFUSE_STATE) & KFUSE_STATE_CRCPASS) == 0) {
+ pr_err("kfuse: crc failed\n");
+ clk_disable(kfuse_clk);
+ return -EIO;
+ }
+
+ for (cnt = 0; cnt < len; cnt += 4) {
+ v = tegra_kfuse_readl(KFUSE_KEYS);
+ memcpy(dest + cnt, &v, sizeof v);
+ }
+
+ clk_disable(kfuse_clk);
+
+ return 0;
+}