]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/misc/imx_iim.c
Unified codebase for TX28, TX48, TX51, TX53
[karo-tx-uboot.git] / drivers / misc / imx_iim.c
1 /*
2  * (C) Copyright 2008-2010 Freescale Semiconductor, Inc.
3  * Terry Lv
4  *
5  * Copyright 2007, Freescale Semiconductor, Inc
6  * Andy Fleming
7  *
8  * Based vaguely on the pxa mmc code:
9  * (C) Copyright 2003
10  * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net
11  *
12  * Adapted for U-Boot version 2012-04-01 by Lothar Waßmann <LW@KARO-electronics.de>
13  *
14  * See file CREDITS for list of people who contributed to this
15  * project.
16  *
17  * This program is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU General Public License as
19  * published by the Free Software Foundation; either version 2 of
20  * the License, or (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
30  * MA 02111-1307 USA
31  */
32
33 #include <common.h>
34 #include <net.h>
35 #include <linux/types.h>
36 #include <asm/io.h>
37 #include <asm/arch/imx-regs.h>
38 #include <asm/arch/imx_iim.h>
39
40 static struct iim_regs *imx_iim = (void *)IMX_IIM_BASE;
41
42 /* slen - string length, e.g.: 23 -> slen=2; abcd -> slen=4 */
43 /* only convert hex value as string input. so "12" is 0x12. */
44 static u32 quick_atoi(char *a, u32 slen)
45 {
46         u32 i, num = 0, digit;
47
48         for (i = 0; i < slen; i++) {
49                 if (a[i] >= '0' && a[i] <= '9') {
50                         digit = a[i] - '0';
51                 } else if (a[i] >= 'a' && a[i] <= 'f') {
52                         digit = a[i] - 'a' + 10;
53                 } else if (a[i] >= 'A' && a[i] <= 'F') {
54                         digit = a[i] - 'A' + 10;
55                 } else {
56                         printf("ERROR: %c\n", a[i]);
57                         return -1;
58                 }
59                 num = (num * 16) + digit;
60         }
61
62     return num;
63 }
64
65 static void fuse_op_start(void)
66 {
67         /* Do not generate interrupt */
68         writel(0, &imx_iim->statm);
69         /* clear the status bits and error bits */
70         writel(0x3, &imx_iim->stat);
71         writel(0xfe, &imx_iim->err);
72 }
73
74 /*
75  * The action should be either:
76  *          POLL_FUSE_PRGD
77  * or:
78  *          POLL_FUSE_SNSD
79  */
80 static s32 poll_fuse_op_done(s32 action)
81 {
82         u32 status, error;
83
84         if (action != POLL_FUSE_PRGD && action != POLL_FUSE_SNSD) {
85                 printf("%s(%d) invalid operation\n", __func__, action);
86                 return -1;
87         }
88
89         /* Poll busy bit till it is NOT set */
90         while ((readl(&imx_iim->stat) & IIM_STAT_BUSY) != 0)
91                 ;
92
93         /* Test for successful write */
94         status = readl(&imx_iim->stat);
95         error = readl(&imx_iim->err);
96
97         if ((status & action) != 0 && \
98                         (error & (action >> IIM_ERR_SHIFT)) == 0) {
99                 if (error) {
100                         printf("Even though the operation"
101                                 "seems successful...\n");
102                         printf("There are some error(s) "
103                                 "at addr=%p: 0x%x\n",
104                                 &imx_iim->err, error);
105                 }
106                 return 0;
107         }
108         printf("%s(%d) failed\n", __func__, action);
109         printf("status address=%p, value=0x%x\n",
110                 &imx_iim->stat, status);
111         printf("There are some error(s) at addr=%p: 0x%x\n",
112                 &imx_iim->err, error);
113         return -1;
114 }
115
116 static u32 sense_fuse(s32 bank, s32 row, s32 bit)
117 {
118         s32 addr, addr_l, addr_h;
119         void *reg_addr;
120
121         fuse_op_start();
122
123         addr = ((bank << 11) | (row << 3) | (bit & 0x7));
124         /* Set IIM Program Upper Address */
125         addr_h = (addr >> 8) & 0x000000FF;
126         /* Set IIM Program Lower Address */
127         addr_l = (addr & 0x000000FF);
128
129 #ifdef IIM_FUSE_DEBUG
130         printf("%s: addr_h=0x%x, addr_l=0x%x\n",
131                         __func__, addr_h, addr_l);
132 #endif
133         writel(addr_h, &imx_iim->ua);
134         writel(addr_l, &imx_iim->la);
135
136         /* Start sensing */
137         writel(0x8, &imx_iim->fctl);
138         if (poll_fuse_op_done(POLL_FUSE_SNSD) != 0) {
139                 printf("%s(bank: %d, row: %d, bit: %d failed\n",
140                         __func__, bank, row, bit);
141         }
142         reg_addr = &imx_iim->sdat;
143
144         return readl(reg_addr);
145 }
146
147 int iim_read(int bank, char row)
148 {
149         u32 fuse_val;
150         s32 err = 0;
151
152         printf("Read fuse at bank:%d row:%d\n", bank, row);
153         fuse_val = sense_fuse(bank, row, 0);
154         printf("fuses at (bank:%d, row:%d) = 0x%x\n", bank, row, fuse_val);
155
156         return err;
157 }
158
159 /* Blow fuses based on the bank, row and bit positions (all 0-based)
160 */
161 static s32 fuse_blow_bit(s32 bank, s32 row, s32 bit)
162 {
163         int addr, addr_l, addr_h, ret = -1;
164
165         fuse_op_start();
166
167         /* Disable IIM Program Protect */
168         writel(0xaa, &imx_iim->preg_p);
169
170         addr = ((bank << 11) | (row << 3) | (bit & 0x7));
171         /* Set IIM Program Upper Address */
172         addr_h = (addr >> 8) & 0x000000FF;
173         /* Set IIM Program Lower Address */
174         addr_l = (addr & 0x000000FF);
175
176 #ifdef IIM_FUSE_DEBUG
177         printf("blowing addr_h=0x%x, addr_l=0x%x\n", addr_h, addr_l);
178 #endif
179
180         writel(addr_h, &imx_iim->ua);
181         writel(addr_l, &imx_iim->la);
182
183         /* Start Programming */
184         writel(0x31, &imx_iim->fctl);
185         if (poll_fuse_op_done(POLL_FUSE_PRGD) == 0)
186                 ret = 0;
187
188         /* Enable IIM Program Protect */
189         writel(0x0, &imx_iim->preg_p);
190
191         return ret;
192 }
193
194 static void fuse_blow_row(s32 bank, s32 row, s32 value)
195 {
196         u32 reg, i;
197
198         /* enable fuse blown */
199         reg = readl(CCM_BASE_ADDR + 0x64);
200         reg |= 0x10;
201         writel(reg, CCM_BASE_ADDR + 0x64);
202
203         for (i = 0; i < 8; i++) {
204                 if (((value >> i) & 0x1) == 0)
205                         continue;
206         if (fuse_blow_bit(bank, row, i) != 0) {
207                         printf("fuse_blow_bit(bank: %d, row: %d, "
208                                 "bit: %d failed\n",
209                                 bank, row, i);
210                 }
211     }
212     reg &= ~0x10;
213     writel(reg, CCM_BASE_ADDR + 0x64);
214 }
215
216 int iim_blow(int bank, int row, int val)
217 {
218         u32 fuse_val, err = 0;
219
220         printf("Blowing fuse at bank:%d row:%d value:%d\n",
221                         bank, row, val);
222         fuse_blow_row(bank, row, val);
223         fuse_val = sense_fuse(bank, row, 0);
224         printf("fuses at (bank:%d, row:%d) = 0x%x\n", bank, row, fuse_val);
225
226         return err;
227 }
228
229 int iim_blow_func(char *func_name, char *func_val)
230 {
231         u32 value, i;
232         char *s;
233         char val[3];
234         s32 err = 0;
235
236         if (strcmp(func_name, "scc") == 0) {
237                 /* fuse_blow scc
238         C3D153EDFD2EA9982226EF5047D3B9A0B9C7138EA87C028401D28C2C2C0B9AA2 */
239                 printf("Ready to burn SCC fuses\n");
240                 s = func_val;
241                 for (i = 0; ; ++i) {
242                         memcpy(val, s, 2);
243                         val[2] = '\0';
244                         value = quick_atoi(val, 2);
245                         /* printf("fuse_blow_row(2, %d, value=0x%x)\n",
246                                         i, value); */
247                         fuse_blow_row(2, i, value);
248
249                         if (*(++s) == '\0') {
250                                 printf("ERROR: Odd string input\n");
251                                 err = -1;
252                                 break;
253                         }
254                         if (*(++s) == '\0') {
255                                 printf("Successful\n");
256                                 break;
257                         }
258                 }
259         } else if (strcmp(func_name, "srk") == 0) {
260                 /* fuse_blow srk
261         418bccd09b53bee1ab59e2662b3c7877bc0094caee201052add49be8780dff95 */
262                 printf("Ready to burn SRK key fuses\n");
263                 s = func_val;
264                 for (i = 0; ; ++i) {
265                         memcpy(val, s, 2);
266                         val[2] = '\0';
267                         value = quick_atoi(val, 2);
268                         if (i == 0) {
269                                 /* 0x41 goes to SRK_HASH[255:248],
270                                  * bank 1, row 1 */
271                                 fuse_blow_row(1, 1, value);
272                         } else {
273                                 /* 0x8b in SRK_HASH[247:240] bank 3, row 1 */
274                                 /* 0xcc in SRK_HASH[239:232] bank 3, row 2 */
275                                 /* ... */
276                                 fuse_blow_row(3, i, value);
277
278                                 if (*(++s) == '\0') {
279                                         printf("ERROR: Odd string input\n");
280                                         err = -1;
281                                         break;
282                                 }
283                                 if (*(++s) == '\0') {
284                                         printf("Successful\n");
285                                         break;
286                                 }
287                         }
288                 }
289         } else if (strcmp(func_name, "fecmac") == 0) {
290                 u8 ea[6] = { 0 };
291
292                 if (func_val == NULL) {
293                         /* Read the Mac address and print it */
294                         imx_get_mac_from_fuse(0, ea);
295
296                         printf("FEC MAC address: ");
297                         printf("0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n\n",
298                                 ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]);
299
300                         return 0;
301                 }
302
303                 eth_parse_enetaddr(func_val, ea);
304                 if (!is_valid_ether_addr(ea)) {
305                         printf("Error: invalid mac address parameter!\n");
306                         err = -1;
307                 } else {
308                         for (i = 0; i < 6; ++i)
309                                 fuse_blow_row(1, i + 9, ea[i]);
310                 }
311         } else {
312                 printf("This command is not supported\n");
313         }
314
315         return err;
316 }
317
318