]>
Commit | Line | Data |
---|---|---|
16b013e7 WD |
1 | /* |
2 | * (C) Copyright 2005 | |
3 | * Greg Ungerer, OpenGear Inc, greg.ungerer@opengear.com | |
4 | * | |
5 | * (C) Copyright 2001 | |
6 | * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net | |
7 | * | |
8 | * (C) Copyright 2001 | |
9 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | |
10 | * | |
1a459660 | 11 | * SPDX-License-Identifier: GPL-2.0+ |
16b013e7 WD |
12 | */ |
13 | ||
14 | #include <common.h> | |
15 | #include <linux/byteorder/swab.h> | |
e103b7ae | 16 | #include <asm/sections.h> |
16b013e7 WD |
17 | |
18 | ||
6d0f6bcf | 19 | flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; /* info for FLASH chips */ |
16b013e7 WD |
20 | |
21 | #define mb() __asm__ __volatile__ ("" : : : "memory") | |
22 | ||
23 | /*----------------------------------------------------------------------- | |
24 | * Functions | |
25 | */ | |
26 | static ulong flash_get_size (unsigned char * addr, flash_info_t * info); | |
27 | static int write_data (flash_info_t * info, ulong dest, unsigned char data); | |
28 | static void flash_get_offsets (ulong base, flash_info_t * info); | |
29 | void inline spin_wheel (void); | |
30 | ||
31 | /*----------------------------------------------------------------------- | |
32 | */ | |
33 | ||
34 | unsigned long flash_init (void) | |
35 | { | |
36 | int i; | |
37 | ulong size = 0; | |
38 | ||
6d0f6bcf | 39 | for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { |
16b013e7 WD |
40 | switch (i) { |
41 | case 0: | |
42 | flash_get_size ((unsigned char *) PHYS_FLASH_1, &flash_info[i]); | |
43 | flash_get_offsets (PHYS_FLASH_1, &flash_info[i]); | |
44 | break; | |
45 | case 1: | |
46 | /* ignore for now */ | |
47 | flash_info[i].flash_id = FLASH_UNKNOWN; | |
48 | break; | |
49 | default: | |
50 | panic ("configured too many flash banks!\n"); | |
51 | break; | |
52 | } | |
53 | size += flash_info[i].size; | |
54 | } | |
55 | ||
56 | /* Protect monitor and environment sectors | |
57 | */ | |
58 | flash_protect (FLAG_PROTECT_SET, | |
6d0f6bcf | 59 | CONFIG_SYS_FLASH_BASE, |
297f18ac | 60 | CONFIG_SYS_FLASH_BASE + _bss_start_ofs, |
16b013e7 WD |
61 | &flash_info[0]); |
62 | ||
63 | return size; | |
64 | } | |
65 | ||
66 | /*----------------------------------------------------------------------- | |
67 | */ | |
68 | static void flash_get_offsets (ulong base, flash_info_t * info) | |
69 | { | |
70 | int i; | |
71 | ||
72 | if (info->flash_id == FLASH_UNKNOWN) | |
73 | return; | |
74 | ||
75 | if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL) { | |
76 | for (i = 0; i < info->sector_count; i++) { | |
77 | info->start[i] = base + (i * PHYS_FLASH_SECT_SIZE); | |
78 | info->protect[i] = 0; | |
79 | } | |
80 | } | |
81 | } | |
82 | ||
83 | /*----------------------------------------------------------------------- | |
84 | */ | |
85 | void flash_print_info (flash_info_t * info) | |
86 | { | |
87 | int i; | |
88 | ||
89 | if (info->flash_id == FLASH_UNKNOWN) { | |
90 | printf ("missing or unknown FLASH type\n"); | |
91 | return; | |
92 | } | |
93 | ||
94 | switch (info->flash_id & FLASH_VENDMASK) { | |
95 | case FLASH_MAN_INTEL: | |
96 | printf ("INTEL "); | |
97 | break; | |
98 | default: | |
99 | printf ("Unknown Vendor "); | |
100 | break; | |
101 | } | |
102 | ||
103 | switch (info->flash_id & FLASH_TYPEMASK) { | |
104 | case FLASH_28F128J3A: | |
105 | printf ("28F128J3A\n"); | |
106 | break; | |
107 | default: | |
108 | printf ("Unknown Chip Type\n"); | |
109 | break; | |
110 | } | |
111 | ||
112 | printf (" Size: %ld MB in %d Sectors\n", | |
113 | info->size >> 20, info->sector_count); | |
114 | ||
115 | printf (" Sector Start Addresses:"); | |
116 | for (i = 0; i < info->sector_count; ++i) { | |
117 | if ((i % 5) == 0) | |
118 | printf ("\n "); | |
119 | printf (" %08lX%s", | |
120 | info->start[i], info->protect[i] ? " (RO)" : " "); | |
121 | } | |
122 | printf ("\n"); | |
123 | return; | |
124 | } | |
125 | ||
126 | /* | |
127 | * The following code cannot be run from FLASH! | |
128 | */ | |
129 | static ulong flash_get_size (unsigned char * addr, flash_info_t * info) | |
130 | { | |
131 | volatile unsigned char value; | |
132 | ||
133 | /* Write auto select command: read Manufacturer ID */ | |
134 | addr[0x5555] = 0xAA; | |
135 | addr[0x2AAA] = 0x55; | |
136 | addr[0x5555] = 0x90; | |
137 | ||
138 | mb (); | |
139 | value = addr[0]; | |
140 | ||
141 | switch (value) { | |
142 | ||
143 | case (unsigned char)INTEL_MANUFACT: | |
144 | info->flash_id = FLASH_MAN_INTEL; | |
145 | break; | |
146 | ||
147 | default: | |
148 | info->flash_id = FLASH_UNKNOWN; | |
149 | info->sector_count = 0; | |
150 | info->size = 0; | |
151 | addr[0] = 0xFF; /* restore read mode */ | |
152 | return (0); /* no or unknown flash */ | |
153 | } | |
154 | ||
155 | mb (); | |
156 | value = addr[2]; /* device ID */ | |
157 | ||
158 | switch (value) { | |
159 | ||
160 | case (unsigned char)INTEL_ID_28F640J3A: | |
161 | info->flash_id += FLASH_28F640J3A; | |
162 | info->sector_count = 64; | |
163 | info->size = 0x00800000; | |
164 | break; /* => 8 MB */ | |
165 | ||
166 | case (unsigned char)INTEL_ID_28F128J3A: | |
167 | info->flash_id += FLASH_28F128J3A; | |
168 | info->sector_count = 128; | |
169 | info->size = 0x01000000; | |
170 | break; /* => 16 MB */ | |
171 | ||
172 | default: | |
173 | info->flash_id = FLASH_UNKNOWN; | |
174 | break; | |
175 | } | |
176 | ||
6d0f6bcf | 177 | if (info->sector_count > CONFIG_SYS_MAX_FLASH_SECT) { |
16b013e7 | 178 | printf ("** ERROR: sector count %d > max (%d) **\n", |
6d0f6bcf JCPV |
179 | info->sector_count, CONFIG_SYS_MAX_FLASH_SECT); |
180 | info->sector_count = CONFIG_SYS_MAX_FLASH_SECT; | |
16b013e7 WD |
181 | } |
182 | ||
183 | addr[0] = 0xFF; /* restore read mode */ | |
184 | ||
185 | return (info->size); | |
186 | } | |
187 | ||
188 | ||
189 | /*----------------------------------------------------------------------- | |
190 | */ | |
191 | ||
192 | int flash_erase (flash_info_t * info, int s_first, int s_last) | |
193 | { | |
49adbe81 | 194 | int prot, sect; |
16b013e7 WD |
195 | ulong type; |
196 | int rcode = 0; | |
a60d1e5b | 197 | ulong start; |
16b013e7 WD |
198 | |
199 | if ((s_first < 0) || (s_first > s_last)) { | |
200 | if (info->flash_id == FLASH_UNKNOWN) { | |
201 | printf ("- missing\n"); | |
202 | } else { | |
203 | printf ("- no sectors to erase\n"); | |
204 | } | |
205 | return 1; | |
206 | } | |
207 | ||
208 | type = (info->flash_id & FLASH_VENDMASK); | |
209 | if ((type != FLASH_MAN_INTEL)) { | |
210 | printf ("Can't erase unknown flash type %08lx - aborted\n", | |
211 | info->flash_id); | |
212 | return 1; | |
213 | } | |
214 | ||
215 | prot = 0; | |
216 | for (sect = s_first; sect <= s_last; ++sect) { | |
217 | if (info->protect[sect]) { | |
218 | prot++; | |
219 | } | |
220 | } | |
221 | ||
222 | if (prot) | |
223 | printf ("- Warning: %d protected sectors will not be erased!\n", prot); | |
224 | else | |
225 | printf ("\n"); | |
226 | ||
227 | /* Disable interrupts which might cause a timeout here */ | |
49adbe81 | 228 | disable_interrupts(); |
16b013e7 WD |
229 | |
230 | /* Start erase on unprotected sectors */ | |
231 | for (sect = s_first; sect <= s_last; sect++) { | |
232 | if (info->protect[sect] == 0) { /* not protected */ | |
233 | volatile unsigned char *addr; | |
234 | unsigned char status; | |
235 | ||
236 | printf ("Erasing sector %2d ... ", sect); | |
237 | ||
238 | /* arm simple, non interrupt dependent timer */ | |
a60d1e5b | 239 | start = get_timer(0); |
16b013e7 WD |
240 | |
241 | addr = (volatile unsigned char *) (info->start[sect]); | |
242 | *addr = 0x50; /* clear status register */ | |
243 | *addr = 0x20; /* erase setup */ | |
244 | *addr = 0xD0; /* erase confirm */ | |
245 | ||
246 | while (((status = *addr) & 0x80) != 0x80) { | |
a60d1e5b | 247 | if (get_timer(start) > |
6d0f6bcf | 248 | CONFIG_SYS_FLASH_ERASE_TOUT) { |
16b013e7 WD |
249 | printf ("Timeout\n"); |
250 | *addr = 0xB0; /* suspend erase */ | |
251 | *addr = 0xFF; /* reset to read mode */ | |
252 | rcode = 1; | |
253 | break; | |
254 | } | |
255 | } | |
256 | ||
257 | *addr = 0x50; /* clear status register cmd */ | |
258 | *addr = 0xFF; /* resest to read mode */ | |
259 | ||
260 | printf (" done\n"); | |
261 | } | |
262 | } | |
263 | return rcode; | |
264 | } | |
265 | ||
266 | /*----------------------------------------------------------------------- | |
267 | * Copy memory to flash, returns: | |
268 | * 0 - OK | |
269 | * 1 - write timeout | |
270 | * 2 - Flash not erased | |
271 | * 4 - Flash not identified | |
272 | */ | |
273 | ||
274 | int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt) | |
275 | { | |
276 | ulong cp, wp; | |
277 | unsigned char data; | |
278 | int count, i, l, rc, port_width; | |
279 | ||
280 | if (info->flash_id == FLASH_UNKNOWN) | |
281 | return 4; | |
282 | ||
283 | wp = addr; | |
284 | port_width = 1; | |
285 | ||
286 | /* | |
287 | * handle unaligned start bytes | |
288 | */ | |
289 | if ((l = addr - wp) != 0) { | |
290 | data = 0; | |
291 | for (i = 0, cp = wp; i < l; ++i, ++cp) { | |
292 | data = (data << 8) | (*(uchar *) cp); | |
293 | } | |
294 | for (; i < port_width && cnt > 0; ++i) { | |
295 | data = (data << 8) | *src++; | |
296 | --cnt; | |
297 | ++cp; | |
298 | } | |
299 | for (; cnt == 0 && i < port_width; ++i, ++cp) { | |
300 | data = (data << 8) | (*(uchar *) cp); | |
301 | } | |
302 | ||
303 | if ((rc = write_data (info, wp, data)) != 0) { | |
304 | return (rc); | |
305 | } | |
306 | wp += port_width; | |
307 | } | |
308 | ||
309 | /* | |
310 | * handle word aligned part | |
311 | */ | |
312 | count = 0; | |
313 | while (cnt >= port_width) { | |
314 | data = 0; | |
315 | for (i = 0; i < port_width; ++i) { | |
316 | data = (data << 8) | *src++; | |
317 | } | |
318 | if ((rc = write_data (info, wp, data)) != 0) { | |
319 | return (rc); | |
320 | } | |
321 | wp += port_width; | |
322 | cnt -= port_width; | |
323 | if (count++ > 0x800) { | |
324 | spin_wheel (); | |
325 | count = 0; | |
326 | } | |
327 | } | |
328 | ||
329 | if (cnt == 0) { | |
330 | return (0); | |
331 | } | |
332 | ||
333 | /* | |
334 | * handle unaligned tail bytes | |
335 | */ | |
336 | data = 0; | |
337 | for (i = 0, cp = wp; i < port_width && cnt > 0; ++i, ++cp) { | |
338 | data = (data << 8) | *src++; | |
339 | --cnt; | |
340 | } | |
341 | for (; i < port_width; ++i, ++cp) { | |
342 | data = (data << 8) | (*(uchar *) cp); | |
343 | } | |
344 | ||
345 | return (write_data (info, wp, data)); | |
346 | } | |
347 | ||
348 | /*----------------------------------------------------------------------- | |
349 | * Write a word or halfword to Flash, returns: | |
350 | * 0 - OK | |
351 | * 1 - write timeout | |
352 | * 2 - Flash not erased | |
353 | */ | |
354 | static int write_data (flash_info_t * info, ulong dest, unsigned char data) | |
355 | { | |
356 | volatile unsigned char *addr = (volatile unsigned char *) dest; | |
357 | ulong status; | |
a60d1e5b | 358 | ulong start; |
16b013e7 WD |
359 | |
360 | /* Check if Flash is (sufficiently) erased */ | |
361 | if ((*addr & data) != data) { | |
362 | printf ("not erased at %08lx (%lx)\n", (ulong) addr, | |
363 | (ulong) * addr); | |
364 | return (2); | |
365 | } | |
366 | /* Disable interrupts which might cause a timeout here */ | |
49adbe81 | 367 | disable_interrupts(); |
16b013e7 WD |
368 | |
369 | *addr = 0x40; /* write setup */ | |
370 | *addr = data; | |
371 | ||
372 | /* arm simple, non interrupt dependent timer */ | |
a60d1e5b | 373 | start = get_timer(0); |
16b013e7 WD |
374 | |
375 | /* wait while polling the status register */ | |
376 | while (((status = *addr) & 0x80) != 0x80) { | |
a60d1e5b | 377 | if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT) { |
16b013e7 WD |
378 | *addr = 0xFF; /* restore read mode */ |
379 | return (1); | |
380 | } | |
381 | } | |
382 | ||
383 | *addr = 0xFF; /* restore read mode */ | |
384 | ||
385 | return (0); | |
386 | } | |
387 | ||
388 | void inline spin_wheel (void) | |
389 | { | |
390 | static int p = 0; | |
391 | static char w[] = "\\/-"; | |
392 | ||
393 | printf ("\010%c", w[p]); | |
394 | (++p == 3) ? (p = 0) : 0; | |
395 | } |