[ARM] tegra: use dma to read/write fuse registers
[linux-2.6.git] / arch / arm / mach-tegra / fuse.c
1 /*
2  * arch/arm/mach-tegra/fuse.c
3  *
4  * Copyright (C) 2010 Google, Inc.
5  *
6  * Author:
7  *      Colin Cross <ccross@android.com>
8  *
9  * This software is licensed under the terms of the GNU General Public
10  * License version 2, as published by the Free Software Foundation, and
11  * may be copied, distributed, and modified under those terms.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  */
19
20 #include <linux/kernel.h>
21 #include <linux/io.h>
22 #include <linux/dma-mapping.h>
23 #include <linux/spinlock.h>
24 #include <linux/completion.h>
25 #include <linux/sched.h>
26 #include <linux/mutex.h>
27
28 #include <mach/dma.h>
29 #include <mach/iomap.h>
30
31 #include "fuse.h"
32
33 #define FUSE_UID_LOW            0x108
34 #define FUSE_UID_HIGH           0x10c
35 #define FUSE_SKU_INFO           0x110
36 #define FUSE_SPARE_BIT          0x200
37
38 DEFINE_MUTEX(lock);
39
40 #ifdef CONFIG_TEGRA_SYSTEM_DMA
41 struct tegra_dma_channel *dma;
42 u32 *fuse_bb;
43 dma_addr_t fuse_bb_phys;
44 struct completion rd_wait;
45 struct completion wr_wait;
46
47 static void fuse_dma_complete(struct tegra_dma_req *req)
48 {
49         if (req)
50                 req->to_memory ? complete(&rd_wait) : complete(&wr_wait);
51 }
52
53 static inline u32 fuse_readl(unsigned long offset)
54 {
55         struct tegra_dma_req req;
56
57         if (!dma)
58                 return -EINVAL;
59
60         mutex_lock(&lock);
61         req.complete = fuse_dma_complete;
62         req.to_memory = 1;
63         req.dest_addr = fuse_bb_phys;
64         req.dest_bus_width = 32;
65         req.dest_wrap = 1;
66         req.source_addr = TEGRA_FUSE_BASE + offset;
67         req.source_bus_width = 32;
68         req.source_wrap = 4;
69         req.req_sel = 0;
70         req.size = 4;
71
72         init_completion(&rd_wait);
73         tegra_dma_enqueue_req(dma, &req);
74         if (wait_for_completion_timeout(&rd_wait, msecs_to_jiffies(50)) == 0) {
75                 WARN_ON(1);
76                 mutex_unlock(&lock);
77                 return 0;
78         }
79
80         mutex_unlock(&lock);
81         return *((u32 *)fuse_bb);
82 }
83
84 static inline void fuse_writel(u32 value, unsigned long offset)
85 {
86         struct tegra_dma_req req;
87
88         if (!dma || !fuse_bb)
89                 return;
90
91         mutex_lock(&lock);
92         *((u32 *)fuse_bb) = value;
93         req.complete = fuse_dma_complete;
94         req.to_memory = 0;
95         req.dest_addr = TEGRA_FUSE_BASE + offset;
96         req.dest_wrap = 4;
97         req.dest_bus_width = 32;
98         req.source_addr = fuse_bb_phys;
99         req.source_bus_width = 32;
100         req.source_wrap = 1;
101         req.req_sel = 0;
102         req.size = 4;
103
104         init_completion(&wr_wait);
105         tegra_dma_enqueue_req(dma, &req);
106         if (wait_for_completion_timeout(&wr_wait, msecs_to_jiffies(50)) == 0)
107                 WARN_ON(1);
108         mutex_unlock(&lock);
109 }
110 #else
111 static inline u32 fuse_readl(unsigned long offset)
112 {
113         return readl(IO_TO_VIRT(TEGRA_FUSE_BASE + offset));
114 }
115
116 static inline void fuse_writel(u32 value, unsigned long offset)
117 {
118         writel(value, IO_TO_VIRT(TEGRA_FUSE_BASE + offset));
119 }
120 #endif
121
122 void tegra_init_fuse(void)
123 {
124         u32 reg = readl(IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48));
125         reg |= 1 << 28;
126         writel(reg, IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48));
127
128 #ifdef CONFIG_TEGRA_SYSTEM_DMA
129         dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT |
130                 TEGRA_DMA_SHARED);
131         if (!dma) {
132                 pr_err("%s: can not allocate dma channel\n", __func__);
133                 return;
134         }
135
136         fuse_bb = dma_alloc_coherent(NULL, sizeof(u32),
137                 &fuse_bb_phys, GFP_KERNEL);
138         if (!fuse_bb) {
139                 pr_err("%s: can not allocate bounce buffer\n", __func__);
140                 tegra_dma_free_channel(dma);
141                 dma = NULL;
142                 return;
143         }
144         mutex_init(&lock);
145 #endif
146
147         pr_info("Tegra SKU: %d CPU Process: %d Core Process: %d\n",
148                 tegra_sku_id(), tegra_cpu_process_id(),
149                 tegra_core_process_id());
150 }
151
152 unsigned long long tegra_chip_uid(void)
153 {
154         unsigned long long lo, hi;
155
156         lo = fuse_readl(FUSE_UID_LOW);
157         hi = fuse_readl(FUSE_UID_HIGH);
158         return (hi << 32ull) | lo;
159 }
160
161 int tegra_sku_id(void)
162 {
163         int sku_id;
164         u32 reg = fuse_readl(FUSE_SKU_INFO);
165         sku_id = reg & 0xFF;
166         return sku_id;
167 }
168
169 int tegra_cpu_process_id(void)
170 {
171         int cpu_process_id;
172         u32 reg = fuse_readl(FUSE_SPARE_BIT);
173         cpu_process_id = (reg >> 6) & 3;
174         return cpu_process_id;
175 }
176
177 int tegra_core_process_id(void)
178 {
179         int core_process_id;
180         u32 reg = fuse_readl(FUSE_SPARE_BIT);
181         core_process_id = (reg >> 12) & 3;
182         return core_process_id;
183 }