diff options
Diffstat (limited to 'arch/arm/mach-tegra/kfuse.c')
-rw-r--r-- | arch/arm/mach-tegra/kfuse.c | 114 |
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 00000000000..9e4b482e469 --- /dev/null +++ b/arch/arm/mach-tegra/kfuse.c | |||
@@ -0,0 +1,114 @@ | |||
1 | /* | ||
2 | * arch/arm/mach-tegra/kfuse.c | ||
3 | * | ||
4 | * Copyright (C) 2010-2011 NVIDIA Corporation. | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | /* The kfuse block stores downstream and upstream HDCP keys for use by HDMI | ||
18 | * module. | ||
19 | */ | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/io.h> | ||
23 | #include <linux/err.h> | ||
24 | #include <linux/string.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/clk.h> | ||
27 | |||
28 | #include <mach/iomap.h> | ||
29 | #include <mach/kfuse.h> | ||
30 | |||
31 | #include "clock.h" | ||
32 | #include "apbio.h" | ||
33 | |||
34 | static struct clk *kfuse_clk = NULL; | ||
35 | |||
36 | /* register definition */ | ||
37 | #define KFUSE_STATE 0x80 | ||
38 | #define KFUSE_STATE_DONE (1u << 16) | ||
39 | #define KFUSE_STATE_CRCPASS (1u << 17) | ||
40 | #define KFUSE_KEYADDR 0x88 | ||
41 | #define KFUSE_KEYADDR_AUTOINC (1u << 16) | ||
42 | #define KFUSE_KEYS 0x8c | ||
43 | |||
44 | static inline u32 tegra_kfuse_readl(unsigned long offset) | ||
45 | { | ||
46 | return tegra_apb_readl(TEGRA_KFUSE_BASE + offset); | ||
47 | } | ||
48 | |||
49 | static inline void tegra_kfuse_writel(u32 value, unsigned long offset) | ||
50 | { | ||
51 | tegra_apb_writel(value, TEGRA_KFUSE_BASE + offset); | ||
52 | } | ||
53 | |||
54 | static int wait_for_done(void) | ||
55 | { | ||
56 | u32 reg; | ||
57 | int retries = 50; | ||
58 | do { | ||
59 | reg = tegra_kfuse_readl(KFUSE_STATE); | ||
60 | if (reg & KFUSE_STATE_DONE) | ||
61 | return 0; | ||
62 | msleep(10); | ||
63 | } while(--retries); | ||
64 | return -ETIMEDOUT; | ||
65 | } | ||
66 | |||
67 | /* read up to KFUSE_DATA_SZ bytes into dest. | ||
68 | * always starts at the first kfuse. | ||
69 | */ | ||
70 | int tegra_kfuse_read(void *dest, size_t len) | ||
71 | { | ||
72 | int err; | ||
73 | u32 v; | ||
74 | unsigned cnt; | ||
75 | |||
76 | if (len > KFUSE_DATA_SZ) | ||
77 | return -EINVAL; | ||
78 | |||
79 | if (kfuse_clk == NULL) { | ||
80 | kfuse_clk = tegra_get_clock_by_name("kfuse"); | ||
81 | if (IS_ERR_OR_NULL(kfuse_clk)) { | ||
82 | pr_err("kfuse: can't get kfuse clock\n"); | ||
83 | return -EINVAL; | ||
84 | } | ||
85 | } | ||
86 | |||
87 | err = clk_enable(kfuse_clk); | ||
88 | if (err) | ||
89 | return err; | ||
90 | |||
91 | tegra_kfuse_writel(KFUSE_KEYADDR_AUTOINC, KFUSE_KEYADDR); | ||
92 | |||
93 | err = wait_for_done(); | ||
94 | if (err) { | ||
95 | pr_err("kfuse: read timeout\n"); | ||
96 | clk_disable(kfuse_clk); | ||
97 | return err; | ||
98 | } | ||
99 | |||
100 | if ((tegra_kfuse_readl(KFUSE_STATE) & KFUSE_STATE_CRCPASS) == 0) { | ||
101 | pr_err("kfuse: crc failed\n"); | ||
102 | clk_disable(kfuse_clk); | ||
103 | return -EIO; | ||
104 | } | ||
105 | |||
106 | for (cnt = 0; cnt < len; cnt += 4) { | ||
107 | v = tegra_kfuse_readl(KFUSE_KEYS); | ||
108 | memcpy(dest + cnt, &v, sizeof v); | ||
109 | } | ||
110 | |||
111 | clk_disable(kfuse_clk); | ||
112 | |||
113 | return 0; | ||
114 | } | ||