]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/misc/imx_iim.c
TX6 Release 2013-04-22
[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/sys_proto.h>
38 #include <asm/arch/imx-regs.h>
39 #include <asm/arch/imx_iim.h>
40
41 static struct iim_regs *imx_iim = (void *)IMX_IIM_BASE;
42
43 /* slen - string length, e.g.: 23 -> slen=2; abcd -> slen=4 */
44 /* only convert hex value as string input. so "12" is 0x12. */
45 static u32 quick_atoi(char *a, u32 slen)
46 {
47         u32 i, num = 0, digit;
48
49         for (i = 0; i < slen; i++) {
50                 if (a[i] >= '0' && a[i] <= '9') {
51                         digit = a[i] - '0';
52                 } else if (a[i] >= 'a' && a[i] <= 'f') {
53                         digit = a[i] - 'a' + 10;
54                 } else if (a[i] >= 'A' && a[i] <= 'F') {
55                         digit = a[i] - 'A' + 10;
56                 } else {
57                         printf("ERROR: %c\n", a[i]);
58                         return -1;
59                 }
60                 num = (num * 16) + digit;
61         }
62
63     return num;
64 }
65
66 static void fuse_op_start(void)
67 {
68         /* Do not generate interrupt */
69         writel(0, &imx_iim->statm);
70         /* clear the status bits and error bits */
71         writel(0x3, &imx_iim->stat);
72         writel(0xfe, &imx_iim->err);
73 }
74
75 /*
76  * The action should be either:
77  *          POLL_FUSE_PRGD
78  * or:
79  *          POLL_FUSE_SNSD
80  */
81 static s32 poll_fuse_op_done(s32 action)
82 {
83         u32 status, error;
84
85         if (action != POLL_FUSE_PRGD && action != POLL_FUSE_SNSD) {
86                 printf("%s(%d) invalid operation\n", __func__, action);
87                 return -1;
88         }
89
90         /* Poll busy bit till it is NOT set */
91         while ((readl(&imx_iim->stat) & IIM_STAT_BUSY) != 0)
92                 ;
93
94         /* Test for successful write */
95         status = readl(&imx_iim->stat);
96         error = readl(&imx_iim->err);
97
98         if ((status & action) != 0 && \
99                         (error & (action >> IIM_ERR_SHIFT)) == 0) {
100                 if (error) {
101                         printf("Even though the operation"
102                                 "seems successful...\n");
103                         printf("There are some error(s) "
104                                 "at addr=%p: 0x%x\n",
105                                 &imx_iim->err, error);
106                 }
107                 return 0;
108         }
109         printf("%s(%d) failed\n", __func__, action);
110         printf("status address=%p, value=0x%x\n",
111                 &imx_iim->stat, status);
112         printf("There are some error(s) at addr=%p: 0x%x\n",
113                 &imx_iim->err, error);
114         return -1;
115 }
116
117 static u32 sense_fuse(s32 bank, s32 row, s32 bit)
118 {
119         s32 addr, addr_l, addr_h;
120         void *reg_addr;
121
122         fuse_op_start();
123
124         addr = ((bank << 11) | (row << 3) | (bit & 0x7));
125         /* Set IIM Program Upper Address */
126         addr_h = (addr >> 8) & 0x000000FF;
127         /* Set IIM Program Lower Address */
128         addr_l = (addr & 0x000000FF);
129
130 #ifdef IIM_FUSE_DEBUG
131         printf("%s: addr_h=0x%x, addr_l=0x%x\n",
132                         __func__, addr_h, addr_l);
133 #endif
134         writel(addr_h, &imx_iim->ua);
135         writel(addr_l, &imx_iim->la);
136
137         /* Start sensing */
138         writel(0x8, &imx_iim->fctl);
139         if (poll_fuse_op_done(POLL_FUSE_SNSD) != 0) {
140                 printf("%s(bank: %d, row: %d, bit: %d failed\n",
141                         __func__, bank, row, bit);
142         }
143         reg_addr = &imx_iim->sdat;
144
145         return readl(reg_addr);
146 }
147
148 int iim_read(int bank, char row)
149 {
150         u32 fuse_val;
151         s32 err = 0;
152
153         printf("Read fuse at bank:%d row:%d\n", bank, row);
154         fuse_val = sense_fuse(bank, row, 0);
155         printf("fuses at (bank:%d, row:%d) = 0x%x\n", bank, row, fuse_val);
156
157         return err;
158 }
159
160 /* Blow fuses based on the bank, row and bit positions (all 0-based)
161 */
162 static s32 fuse_blow_bit(s32 bank, s32 row, s32 bit)
163 {
164         int addr, addr_l, addr_h, ret = -1;
165
166         fuse_op_start();
167
168         /* Disable IIM Program Protect */
169         writel(0xaa, &imx_iim->preg_p);
170
171         addr = ((bank << 11) | (row << 3) | (bit & 0x7));
172         /* Set IIM Program Upper Address */
173         addr_h = (addr >> 8) & 0x000000FF;
174         /* Set IIM Program Lower Address */
175         addr_l = (addr & 0x000000FF);
176
177 #ifdef IIM_FUSE_DEBUG
178         printf("blowing addr_h=0x%x, addr_l=0x%x\n", addr_h, addr_l);
179 #endif
180
181         writel(addr_h, &imx_iim->ua);
182         writel(addr_l, &imx_iim->la);
183
184         /* Start Programming */
185         writel(0x31, &imx_iim->fctl);
186         if (poll_fuse_op_done(POLL_FUSE_PRGD) == 0)
187                 ret = 0;
188
189         /* Enable IIM Program Protect */
190         writel(0x0, &imx_iim->preg_p);
191
192         return ret;
193 }
194
195 static void fuse_blow_row(s32 bank, s32 row, s32 value)
196 {
197         u32 reg, i;
198
199         /* enable fuse blown */
200         reg = readl(CCM_BASE_ADDR + 0x64);
201         reg |= 0x10;
202         writel(reg, CCM_BASE_ADDR + 0x64);
203
204         for (i = 0; i < 8; i++) {
205                 if (((value >> i) & 0x1) == 0)
206                         continue;
207         if (fuse_blow_bit(bank, row, i) != 0) {
208                         printf("fuse_blow_bit(bank: %d, row: %d, "
209                                 "bit: %d failed\n",
210                                 bank, row, i);
211                 }
212     }
213     reg &= ~0x10;
214     writel(reg, CCM_BASE_ADDR + 0x64);
215 }
216
217 int iim_blow(int bank, int row, int val)
218 {
219         u32 fuse_val, err = 0;
220
221         printf("Blowing fuse at bank:%d row:%d value:%d\n",
222                         bank, row, val);
223         fuse_blow_row(bank, row, val);
224         fuse_val = sense_fuse(bank, row, 0);
225         printf("fuses at (bank:%d, row:%d) = 0x%x\n", bank, row, fuse_val);
226
227         return err;
228 }
229
230 int iim_blow_func(char *func_name, char *func_val)
231 {
232         u32 value, i;
233         char *s;
234         char val[3];
235         s32 err = 0;
236
237         if (strcmp(func_name, "scc") == 0) {
238                 /* fuse_blow scc
239         C3D153EDFD2EA9982226EF5047D3B9A0B9C7138EA87C028401D28C2C2C0B9AA2 */
240                 printf("Ready to burn SCC fuses\n");
241                 s = func_val;
242                 for (i = 0; ; ++i) {
243                         memcpy(val, s, 2);
244                         val[2] = '\0';
245                         value = quick_atoi(val, 2);
246                         /* printf("fuse_blow_row(2, %d, value=0x%x)\n",
247                                         i, value); */
248                         fuse_blow_row(2, i, value);
249
250                         if (*(++s) == '\0') {
251                                 printf("ERROR: Odd string input\n");
252                                 err = -1;
253                                 break;
254                         }
255                         if (*(++s) == '\0') {
256                                 printf("Successful\n");
257                                 break;
258                         }
259                 }
260         } else if (strcmp(func_name, "srk") == 0) {
261                 /* fuse_blow srk
262         418bccd09b53bee1ab59e2662b3c7877bc0094caee201052add49be8780dff95 */
263                 printf("Ready to burn SRK key fuses\n");
264                 s = func_val;
265                 for (i = 0; ; ++i) {
266                         memcpy(val, s, 2);
267                         val[2] = '\0';
268                         value = quick_atoi(val, 2);
269                         if (i == 0) {
270                                 /* 0x41 goes to SRK_HASH[255:248],
271                                  * bank 1, row 1 */
272                                 fuse_blow_row(1, 1, value);
273                         } else {
274                                 /* 0x8b in SRK_HASH[247:240] bank 3, row 1 */
275                                 /* 0xcc in SRK_HASH[239:232] bank 3, row 2 */
276                                 /* ... */
277                                 fuse_blow_row(3, i, value);
278
279                                 if (*(++s) == '\0') {
280                                         printf("ERROR: Odd string input\n");
281                                         err = -1;
282                                         break;
283                                 }
284                                 if (*(++s) == '\0') {
285                                         printf("Successful\n");
286                                         break;
287                                 }
288                         }
289                 }
290         } else if (strcmp(func_name, "fecmac") == 0) {
291                 u8 ea[6] = { 0 };
292
293                 if (func_val == NULL) {
294                         /* Read the Mac address and print it */
295                         imx_get_mac_from_fuse(0, ea);
296
297                         printf("FEC MAC address: ");
298                         printf("0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n\n",
299                                 ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]);
300
301                         return 0;
302                 }
303
304                 eth_parse_enetaddr(func_val, ea);
305                 if (!is_valid_ether_addr(ea)) {
306                         printf("Error: invalid mac address parameter!\n");
307                         err = -1;
308                 } else {
309                         for (i = 0; i < 6; ++i)
310                                 fuse_blow_row(1, i + 9, ea[i]);
311                 }
312         } else {
313                 printf("This command is not supported\n");
314         }
315
316         return err;
317 }
318
319