arm64: rockchip: rk3368: refactor efuse code
[firefly-linux-kernel-4.4.55.git] / arch / arm / mach-rockchip / efuse.c
1 /*
2  * Copyright (C) 2013-2015 ROCKCHIP, Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8
9 #include <linux/crc32.h>
10 #include <linux/delay.h>
11 #include <linux/of.h>
12 #include <linux/platform_device.h>
13 #include <linux/rockchip/cpu.h>
14 #include <linux/rockchip/iomap.h>
15 #include <asm/compiler.h>
16 #include <asm/psci.h>
17 #include <asm/system_info.h>
18 #include "efuse.h"
19
20 #ifdef CONFIG_ARM
21 #define efuse_readl(offset) readl_relaxed(RK_EFUSE_VIRT + offset)
22 #define efuse_writel(val, offset) writel_relaxed(val, RK_EFUSE_VIRT + offset)
23 #endif
24
25 static u8 efuse_buf[32] = {};
26
27 struct rockchip_efuse {
28         int (*get_leakage)(int ch);
29         int (*get_temp)(int ch);
30         int efuse_version;
31         int process_version;
32 };
33
34 static struct rockchip_efuse efuse;
35
36 #ifdef CONFIG_ARM64
37 /****************************secure reg access****************************/
38
39 #define SEC_REG_RW_SHT (0x0)
40 #define SEC_REG_RD (0x0)
41 #define SEC_REG_WR (0x1)
42
43 #define SEC_REG_BITS_SHT (0x1)
44 #define SEC_REG_32 (0x0)
45 #define SEC_REG_64 (0x2)
46
47 #define SEC_REG_RD_32 (SEC_REG_RD | SEC_REG_32)
48 #define SEC_REG_RD_64 (SEC_REG_RD | SEC_REG_64)
49 #define SEC_REG_WR_32 (SEC_REG_WR | SEC_REG_32)
50 #define SEC_REG_WR_64 (SEC_REG_WR | SEC_REG_64)
51
52 #define PSCI_OS_ACCESS_REG              (0xa600ffb0)
53 static phys_addr_t efuse_phys;
54
55 /*
56  * arg2: rd/wr control, bit[0] 0-rd 1-rt, bit[1] 0-32bit, 1-64bit
57  * arg1: base addr
58  * arg0: read or write val
59  * function_id: return fail/succes
60  */
61 static u32 reg_wr_fn_smc(u64 function_id, u64 arg0, u64 arg1, u64 arg2)
62 {
63         asm volatile(
64                         __asmeq("%0", "x0")
65                         __asmeq("%1", "x1")
66                         __asmeq("%2", "x2")
67                         __asmeq("%3", "x3")
68                         "smc    #0\n"
69                 : "+r" (function_id), "+r" (arg0)
70                 : "r" (arg1), "r" (arg2));
71
72         return function_id;
73 }
74
75 static u32 reg_rd_fn_smc(u64 function_id, u64 arg0, u64 arg1, u64 arg2,
76                          u64 *val)
77 {
78         asm volatile(
79                         __asmeq("%0", "x0")
80                         __asmeq("%1", "x1")
81                         __asmeq("%2", "x2")
82                         __asmeq("%3", "x3")
83                         "smc    #0\n"
84                 : "+r" (function_id), "+r" (arg0)
85                 : "r" (arg1), "r" (arg2));
86
87                 *val = arg0;
88
89         return function_id;
90 }
91
92 static u32 (*reg_wr_fn)(u64, u64, u64, u64) = reg_wr_fn_smc;
93 static u32 (*reg_rd_fn)(u64, u64, u64, u64, u64 *) = reg_rd_fn_smc;
94
95 static u32 secure_regs_rd_32(u64 addr_phy)
96 {
97         u64 val = 0;
98
99         reg_rd_fn(PSCI_OS_ACCESS_REG, 0, addr_phy, SEC_REG_RD_32, &val);
100         return val;
101 }
102
103 static u32 secure_regs_wr_32(u64 addr_phy, u32 val)
104 {
105         u64 val_64 = val;
106
107         return reg_wr_fn(PSCI_OS_ACCESS_REG, val_64, addr_phy, SEC_REG_WR_32);
108 }
109
110 static u32 efuse_readl(u32 offset)
111 {
112         return secure_regs_rd_32(efuse_phys + offset);
113 }
114
115 static void efuse_writel(u32 val, u32 offset)
116 {
117         secure_regs_wr_32(efuse_phys + offset, val);
118 }
119 #endif
120
121 static int rk3288_efuse_readregs(u32 addr, u32 length, u8 *buf)
122 {
123         int ret = length;
124
125         if (!length)
126                 return 0;
127         if (!buf)
128                 return 0;
129
130         efuse_writel(EFUSE_CSB, REG_EFUSE_CTRL);
131         efuse_writel(EFUSE_LOAD | EFUSE_PGENB, REG_EFUSE_CTRL);
132         udelay(2);
133         do {
134                 efuse_writel(efuse_readl(REG_EFUSE_CTRL) &
135                         (~(EFUSE_A_MASK << EFUSE_A_SHIFT)), REG_EFUSE_CTRL);
136                 efuse_writel(efuse_readl(REG_EFUSE_CTRL) |
137                         ((addr & EFUSE_A_MASK) << EFUSE_A_SHIFT),
138                         REG_EFUSE_CTRL);
139                 udelay(2);
140                 efuse_writel(efuse_readl(REG_EFUSE_CTRL) |
141                                 EFUSE_STROBE, REG_EFUSE_CTRL);
142                 udelay(2);
143                 *buf = efuse_readl(REG_EFUSE_DOUT);
144                 efuse_writel(efuse_readl(REG_EFUSE_CTRL) &
145                                 (~EFUSE_STROBE), REG_EFUSE_CTRL);
146                 udelay(2);
147                 buf++;
148                 addr++;
149         } while (--length);
150         udelay(2);
151         efuse_writel(efuse_readl(REG_EFUSE_CTRL) | EFUSE_CSB, REG_EFUSE_CTRL);
152         udelay(1);
153
154         return ret;
155 }
156
157 static int __init rk3288_get_efuse_version(void)
158 {
159         int ret = efuse_buf[4] & (~(0x1 << 3));
160         return ret;
161 }
162
163 static int __init rk3288_get_process_version(void)
164 {
165         int ret = efuse_buf[6]&0x0f;
166
167         return ret;
168 }
169
170 static int rk3288_get_leakage(int ch)
171 {
172         if ((ch < 0) || (ch > 2))
173                 return 0;
174
175         return efuse_buf[23+ch];
176 }
177
178 #ifdef CONFIG_ARM
179 static void __init rk3288_set_system_serial(void)
180 {
181         int i;
182         u8 buf[16];
183
184         for (i = 0; i < 8; i++) {
185                 buf[i] = efuse_buf[8 + (i << 1)];
186                 buf[i + 8] = efuse_buf[7 + (i << 1)];
187         }
188
189         system_serial_low = crc32(0, buf, 8);
190         system_serial_high = crc32(system_serial_low, buf + 8, 8);
191 }
192 #else
193 static inline void __init rk3288_set_system_serial(void) {}
194 #endif
195
196 int rk312x_efuse_readregs(u32 addr, u32 length, u8 *buf)
197 {
198         int ret = length;
199
200         if (!length)
201                 return 0;
202
203         efuse_writel(EFUSE_LOAD, REG_EFUSE_CTRL);
204         udelay(2);
205         do {
206                 efuse_writel(efuse_readl(REG_EFUSE_CTRL) &
207                                 (~(EFUSE_A_MASK << RK312X_EFUSE_A_SHIFT)),
208                                 REG_EFUSE_CTRL);
209                 efuse_writel(efuse_readl(REG_EFUSE_CTRL) |
210                                 ((addr & EFUSE_A_MASK) << RK312X_EFUSE_A_SHIFT),
211                                 REG_EFUSE_CTRL);
212                 udelay(2);
213                 efuse_writel(efuse_readl(REG_EFUSE_CTRL) |
214                                 EFUSE_STROBE, REG_EFUSE_CTRL);
215                 udelay(2);
216                 *buf = efuse_readl(REG_EFUSE_DOUT);
217                 efuse_writel(efuse_readl(REG_EFUSE_CTRL) &
218                                 (~EFUSE_STROBE), REG_EFUSE_CTRL);
219                 udelay(2);
220                 buf++;
221                 addr++;
222         } while (--length);
223         udelay(2);
224         efuse_writel(efuse_readl(REG_EFUSE_CTRL) &
225                         (~EFUSE_LOAD) , REG_EFUSE_CTRL);
226         udelay(1);
227
228         return ret;
229 }
230
231 int rockchip_efuse_version(void)
232 {
233         return efuse.efuse_version;
234 }
235
236 int rockchip_process_version(void)
237 {
238         return efuse.process_version;
239 }
240
241 int rockchip_get_leakage(int ch)
242 {
243         int ret = 0;
244
245         if (efuse.get_leakage) {
246                 return efuse.get_leakage(ch);
247         } else {
248                 ret = rk3288_efuse_readregs(0, 32, efuse_buf);
249                 if (ret == 32)
250                         return efuse_buf[23+ch];
251         }
252         return 0;
253 }
254
255 int rockchip_efuse_get_temp_adjust(int ch)
256 {
257         int temp;
258
259         if (efuse_buf[31] & 0x80)
260                 temp = -(efuse_buf[31] & 0x7f);
261         else
262                 temp = efuse_buf[31];
263
264         return temp;
265 }
266
267 static void __init rk3288_efuse_init(void)
268 {
269         int ret;
270
271         ret = rk3288_efuse_readregs(0, 32, efuse_buf);
272         if (ret == 32) {
273                 efuse.get_leakage = rk3288_get_leakage;
274                 efuse.efuse_version = rk3288_get_efuse_version();
275                 efuse.process_version = rk3288_get_process_version();
276                 rockchip_set_cpu_version((efuse_buf[6] >> 4) & 3);
277                 rk3288_set_system_serial();
278         } else {
279                 pr_err("failed to read eFuse, return %d\n", ret);
280         }
281 }
282
283 void __init rockchip_efuse_init(void)
284 {
285         int ret;
286
287         if (cpu_is_rk3288()) {
288                 rk3288_efuse_init();
289         } else if (cpu_is_rk312x()) {
290                 ret = rk312x_efuse_readregs(0, 32, efuse_buf);
291                 if (ret == 32)
292                         efuse.get_leakage = rk3288_get_leakage;
293                 else
294                         pr_err("failed to read eFuse, return %d\n", ret);
295         }
296 }
297
298 #ifdef CONFIG_ARM64
299 static int __init rockchip_efuse_probe(struct platform_device *pdev)
300 {
301         struct resource *regs;
302
303         regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
304         if (!regs) {
305                 dev_err(&pdev->dev, "failed to get I/O memory\n");
306                 return -ENODEV;
307         }
308         efuse_phys = regs->start;
309
310         rk3288_efuse_init();
311
312         return 0;
313 }
314
315 static const struct of_device_id rockchip_efuse_of_match[] = {
316         { .compatible = "rockchip,rk3368-efuse-256", .data = NULL, },
317         {},
318 };
319
320 static struct platform_driver rockchip_efuse_driver = {
321         .driver         = {
322                 .name           = "efuse",
323                 .owner          = THIS_MODULE,
324                 .of_match_table = of_match_ptr(rockchip_efuse_of_match),
325         },
326 };
327
328 static int __init rockchip_efuse_module_init(void)
329 {
330         return platform_driver_probe(&rockchip_efuse_driver,
331                                      rockchip_efuse_probe);
332 }
333 arch_initcall_sync(rockchip_efuse_module_init);
334 #endif