blob: 334518a4bc9d49245f9928ecdf7e256f04b6de0e [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
TsiChung Liewdd8513c2008-07-23 17:11:47 -05002/*
3 * (C) Copyright 2000-2003
4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5 *
6 * Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
7 * TsiChung Liew (Tsi-Chung.Liew@freescale.com)
TsiChung Liewdd8513c2008-07-23 17:11:47 -05008 */
9
Tom Rinidec7ea02024-05-20 13:35:03 -060010#include <config.h>
Simon Glass8e201882020-05-10 11:39:54 -060011#include <flash.h>
Simon Glass97589732020-05-10 11:40:02 -060012#include <init.h>
Simon Glass8f3f7612019-11-14 12:57:42 -070013#include <irq_func.h>
Tom Rinidec7ea02024-05-20 13:35:03 -060014#include <time.h>
TsiChung Liewdd8513c2008-07-23 17:11:47 -050015
16#include <asm/immap.h>
17
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020018#ifndef CONFIG_SYS_FLASH_CFI
TsiChung Liewdd8513c2008-07-23 17:11:47 -050019typedef unsigned short FLASH_PORT_WIDTH;
20typedef volatile unsigned short FLASH_PORT_WIDTHV;
21
22#define FPW FLASH_PORT_WIDTH
23#define FPWV FLASH_PORT_WIDTHV
24
25#define FLASH_CYCLE1 0x5555
26#define FLASH_CYCLE2 0x2aaa
27
28#define SYNC __asm__("nop")
29
30/*-----------------------------------------------------------------------
31 * Functions
32 */
33
34ulong flash_get_size(FPWV * addr, flash_info_t * info);
35int flash_get_offsets(ulong base, flash_info_t * info);
36int write_word(flash_info_t * info, FPWV * dest, u16 data);
Tom Rini03183832017-05-08 22:14:35 -040037static inline void spin_wheel(void);
TsiChung Liewdd8513c2008-07-23 17:11:47 -050038
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020039flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS];
TsiChung Liewdd8513c2008-07-23 17:11:47 -050040
41ulong flash_init(void)
42{
43 ulong size = 0;
44 ulong fbase = 0;
45
Tom Rini6a5dccc2022-11-16 13:10:41 -050046 fbase = (ulong) CFG_SYS_FLASH_BASE;
TsiChung Liewdd8513c2008-07-23 17:11:47 -050047 flash_get_size((FPWV *) fbase, &flash_info[0]);
48 flash_get_offsets((ulong) fbase, &flash_info[0]);
49 fbase += flash_info[0].size;
50 size += flash_info[0].size;
51
52 /* Protect monitor and environment sectors */
53 flash_protect(FLAG_PROTECT_SET,
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020054 CONFIG_SYS_MONITOR_BASE,
55 CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1, &flash_info[0]);
TsiChung Liewdd8513c2008-07-23 17:11:47 -050056
57 return size;
58}
59
60int flash_get_offsets(ulong base, flash_info_t * info)
61{
Masahiro Yamadaf26bab32014-07-22 10:57:18 +090062 int i;
TsiChung Liewdd8513c2008-07-23 17:11:47 -050063
64 if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST) {
65
66 info->start[0] = base;
Masahiro Yamadaf26bab32014-07-22 10:57:18 +090067 info->protect[0] = 0;
Tom Rini6a5dccc2022-11-16 13:10:41 -050068 for (i = 1; i < CFG_SYS_SST_SECT; i++) {
Masahiro Yamadaf26bab32014-07-22 10:57:18 +090069 info->start[i] = info->start[i - 1]
Tom Rini6a5dccc2022-11-16 13:10:41 -050070 + CFG_SYS_SST_SECTSZ;
Masahiro Yamadaf26bab32014-07-22 10:57:18 +090071 info->protect[i] = 0;
TsiChung Liewdd8513c2008-07-23 17:11:47 -050072 }
73 }
74
75 return ERR_OK;
76}
77
78void flash_print_info(flash_info_t * info)
79{
80 int i;
81
82 switch (info->flash_id & FLASH_VENDMASK) {
83 case FLASH_MAN_SST:
84 printf("SST ");
85 break;
86 default:
87 printf("Unknown Vendor ");
88 break;
89 }
90
91 switch (info->flash_id & FLASH_TYPEMASK) {
92 case FLASH_SST6401B:
93 printf("SST39VF6401B\n");
94 break;
95 default:
96 printf("Unknown Chip Type\n");
97 return;
98 }
99
Angelo Dureghello71a35402023-03-15 00:43:07 +0100100 printf(" Size: %ld KB in %d Sectors\n",
101 info->size >> 10, info->sector_count);
TsiChung Liewdd8513c2008-07-23 17:11:47 -0500102
103 printf(" Sector Start Addresses:");
104 for (i = 0; i < info->sector_count; ++i) {
105 if ((i % 5) == 0)
106 printf("\n ");
107 printf(" %08lX%s",
108 info->start[i], info->protect[i] ? " (RO)" : " ");
109 }
110 printf("\n");
111}
112
113/*
114 * The following code cannot be run from FLASH!
115 */
116ulong flash_get_size(FPWV * addr, flash_info_t * info)
117{
118 u16 value;
119
120 addr[FLASH_CYCLE1] = (FPWV) 0x00AA00AA; /* for Atmel, Intel ignores this */
121 addr[FLASH_CYCLE2] = (FPWV) 0x00550055; /* for Atmel, Intel ignores this */
122 addr[FLASH_CYCLE1] = (FPWV) 0x00900090; /* selects Intel or Atmel */
123
124 switch (addr[0] & 0xffff) {
125 case (u8) SST_MANUFACT:
126 info->flash_id = FLASH_MAN_SST;
127 value = addr[1];
128 break;
129 default:
130 printf("Unknown Flash\n");
131 info->flash_id = FLASH_UNKNOWN;
132 info->sector_count = 0;
133 info->size = 0;
134
135 *addr = (FPW) 0x00F000F0;
136 return (0); /* no or unknown flash */
137 }
138
139 switch (value) {
140 case (u16) SST_ID_xF6401B:
141 info->flash_id += FLASH_SST6401B;
142 break;
143 default:
144 info->flash_id = FLASH_UNKNOWN;
145 break;
146 }
147
148 info->sector_count = 0;
149 info->size = 0;
Tom Rini6a5dccc2022-11-16 13:10:41 -0500150 info->sector_count = CFG_SYS_SST_SECT;
151 info->size = CFG_SYS_SST_SECT * CFG_SYS_SST_SECTSZ;
TsiChung Liewdd8513c2008-07-23 17:11:47 -0500152
153 /* reset ID mode */
154 *addr = (FPWV) 0x00F000F0;
155
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200156 if (info->sector_count > CONFIG_SYS_MAX_FLASH_SECT) {
TsiChung Liewdd8513c2008-07-23 17:11:47 -0500157 printf("** ERROR: sector count %d > max (%d) **\n",
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200158 info->sector_count, CONFIG_SYS_MAX_FLASH_SECT);
159 info->sector_count = CONFIG_SYS_MAX_FLASH_SECT;
TsiChung Liewdd8513c2008-07-23 17:11:47 -0500160 }
161
162 return (info->size);
163}
164
165int flash_erase(flash_info_t * info, int s_first, int s_last)
166{
167 FPWV *addr;
168 int flag, prot, sect, count;
Masahiro Yamadac0d51fe2014-04-15 13:26:34 +0900169 ulong type, start;
TsiChung Liewdd8513c2008-07-23 17:11:47 -0500170 int rcode = 0, flashtype = 0;
171
172 if ((s_first < 0) || (s_first > s_last)) {
173 if (info->flash_id == FLASH_UNKNOWN)
174 printf("- missing\n");
175 else
176 printf("- no sectors to erase\n");
177 return 1;
178 }
179
180 type = (info->flash_id & FLASH_VENDMASK);
181
182 switch (type) {
183 case FLASH_MAN_SST:
184 flashtype = 1;
185 break;
186 default:
187 type = (info->flash_id & FLASH_VENDMASK);
188 printf("Can't erase unknown flash type %08lx - aborted\n",
189 info->flash_id);
190 return 1;
191 }
192
193 prot = 0;
194 for (sect = s_first; sect <= s_last; ++sect) {
195 if (info->protect[sect]) {
196 prot++;
197 }
198 }
199
200 if (prot)
201 printf("- Warning: %d protected sectors will not be erased!\n",
202 prot);
203 else
204 printf("\n");
205
206 flag = disable_interrupts();
207
208 start = get_timer(0);
TsiChung Liewdd8513c2008-07-23 17:11:47 -0500209
Tom Rini6a5dccc2022-11-16 13:10:41 -0500210 if ((s_last - s_first) == (CFG_SYS_SST_SECT - 1)) {
TsiChung Liewdd8513c2008-07-23 17:11:47 -0500211 if (prot == 0) {
212 addr = (FPWV *) info->start[0];
213
214 addr[FLASH_CYCLE1] = 0x00AA; /* unlock */
215 addr[FLASH_CYCLE2] = 0x0055; /* unlock */
216 addr[FLASH_CYCLE1] = 0x0080; /* erase mode */
217 addr[FLASH_CYCLE1] = 0x00AA; /* unlock */
218 addr[FLASH_CYCLE2] = 0x0055; /* unlock */
219 *addr = 0x0030; /* erase chip */
220
221 count = 0;
222 start = get_timer(0);
223
224 while ((*addr & 0x0080) != 0x0080) {
225 if (count++ > 0x10000) {
226 spin_wheel();
227 count = 0;
228 }
229
Tom Rini78f88002022-07-23 13:05:00 -0400230 /* check timeout, 1000ms */
231 if (get_timer(start) > 1000) {
TsiChung Liewdd8513c2008-07-23 17:11:47 -0500232 printf("Timeout\n");
233 *addr = 0x00F0; /* reset to read mode */
234
235 return 1;
236 }
237 }
238
239 *addr = 0x00F0; /* reset to read mode */
240
241 printf("\b. done\n");
242
243 if (flag)
244 enable_interrupts();
245
246 return 0;
Tom Rini6a5dccc2022-11-16 13:10:41 -0500247 } else if (prot == CFG_SYS_SST_SECT) {
TsiChung Liewdd8513c2008-07-23 17:11:47 -0500248 return 1;
249 }
250 }
251
252 /* Start erase on unprotected sectors */
253 for (sect = s_first; sect <= s_last; sect++) {
254 if (info->protect[sect] == 0) { /* not protected */
255
256 addr = (FPWV *) (info->start[sect]);
257
258 printf(".");
259
260 /* arm simple, non interrupt dependent timer */
261 start = get_timer(0);
262
263 switch (flashtype) {
264 case 1:
265 {
266 FPWV *base; /* first address in bank */
267
268 flag = disable_interrupts();
269
Tom Rini6a5dccc2022-11-16 13:10:41 -0500270 base = (FPWV *) (CFG_SYS_FLASH_BASE); /* First sector */
TsiChung Liewdd8513c2008-07-23 17:11:47 -0500271
272 base[FLASH_CYCLE1] = 0x00AA; /* unlock */
273 base[FLASH_CYCLE2] = 0x0055; /* unlock */
274 base[FLASH_CYCLE1] = 0x0080; /* erase mode */
275 base[FLASH_CYCLE1] = 0x00AA; /* unlock */
276 base[FLASH_CYCLE2] = 0x0055; /* unlock */
277 *addr = 0x0050; /* erase sector */
278
279 if (flag)
280 enable_interrupts();
281
282 while ((*addr & 0x0080) != 0x0080) {
Tom Rini78f88002022-07-23 13:05:00 -0400283 /* check timeout, 1000ms */
284 if (get_timer(start) > 1000) {
TsiChung Liewdd8513c2008-07-23 17:11:47 -0500285 printf("Timeout\n");
286 *addr = 0x00F0; /* reset to read mode */
287
288 rcode = 1;
289 break;
290 }
291 }
292
293 *addr = 0x00F0; /* reset to read mode */
294 break;
295 }
296 } /* switch (flashtype) */
297 }
298 }
299 printf(" done\n");
300
301 if (flag)
302 enable_interrupts();
303
304 return rcode;
305}
306
307int write_buff(flash_info_t * info, uchar * src, ulong addr, ulong cnt)
308{
309 ulong wp, count;
310 u16 data;
Masahiro Yamadac0d51fe2014-04-15 13:26:34 +0900311 int rc;
TsiChung Liewdd8513c2008-07-23 17:11:47 -0500312
313 if (info->flash_id == FLASH_UNKNOWN)
314 return 4;
315
316 /* get lower word aligned address */
317 wp = addr;
TsiChung Liewdd8513c2008-07-23 17:11:47 -0500318
319 /* handle unaligned start bytes */
320 if (wp & 1) {
321 data = *((FPWV *) wp);
322 data = (data << 8) | *src;
323
324 if ((rc = write_word(info, (FPWV *) wp, data)) != 0)
325 return (rc);
326
327 wp++;
328 cnt -= 1;
329 src++;
330 }
331
332 while (cnt >= 2) {
333 /*
334 * handle word aligned part
335 */
336 count = 0;
337 data = *((FPWV *) src);
338
339 if ((rc = write_word(info, (FPWV *) wp, data)) != 0)
340 return (rc);
341
342 wp += 2;
343 src += 2;
344 cnt -= 2;
345
346 if (count++ > 0x800) {
347 spin_wheel();
348 count = 0;
349 }
350 }
351 /* handle word aligned part */
352 if (cnt) {
353 /* handle word aligned part */
354 count = 0;
355 data = *((FPWV *) wp);
356
357 data = (data & 0x00FF) | (*src << 8);
358
359 if ((rc = write_word(info, (FPWV *) wp, data)) != 0)
360 return (rc);
361
362 wp++;
363 src++;
364 cnt -= 1;
365 if (count++ > 0x800) {
366 spin_wheel();
367 count = 0;
368 }
369 }
370
371 if (cnt == 0)
372 return ERR_OK;
373
374 return ERR_OK;
375}
376
377/*-----------------------------------------------------------------------
378 * Write a word to Flash
379 * A word is 16 bits, whichever the bus width of the flash bank
380 * (not an individual chip) is.
381 *
382 * returns:
383 * 0 - OK
384 * 1 - write timeout
385 * 2 - Flash not erased
386 */
387int write_word(flash_info_t * info, FPWV * dest, u16 data)
388{
389 ulong start;
390 int flag;
391 int res = 0; /* result, assume success */
392 FPWV *base; /* first address in flash bank */
393
394 /* Check if Flash is (sufficiently) erased */
395 if ((*dest & (u8) data) != (u8) data) {
396 return (2);
397 }
398
Tom Rini6a5dccc2022-11-16 13:10:41 -0500399 base = (FPWV *) (CFG_SYS_FLASH_BASE);
TsiChung Liewdd8513c2008-07-23 17:11:47 -0500400
401 /* Disable interrupts which might cause a timeout here */
402 flag = disable_interrupts();
403
404 base[FLASH_CYCLE1] = (u8) 0x00AA00AA; /* unlock */
405 base[FLASH_CYCLE2] = (u8) 0x00550055; /* unlock */
406 base[FLASH_CYCLE1] = (u8) 0x00A000A0; /* selects program mode */
407
408 *dest = data; /* start programming the data */
409
410 /* re-enable interrupts if necessary */
411 if (flag)
412 enable_interrupts();
413
414 start = get_timer(0);
415
416 /* data polling for D7 */
417 while (res == 0
418 && (*dest & (u8) 0x00800080) != (data & (u8) 0x00800080)) {
Tom Rini78f88002022-07-23 13:05:00 -0400419 /* check timeout, 500ms */
420 if (get_timer(start) > 500) {
TsiChung Liewdd8513c2008-07-23 17:11:47 -0500421 *dest = (u8) 0x00F000F0; /* reset bank */
422 res = 1;
423 }
424 }
425
426 *dest++ = (u8) 0x00F000F0; /* reset bank */
427
428 return (res);
429}
430
Tom Rini03183832017-05-08 22:14:35 -0400431static inline void spin_wheel(void)
TsiChung Liewdd8513c2008-07-23 17:11:47 -0500432{
433 static int p = 0;
434 static char w[] = "\\/-";
435
436 printf("\010%c", w[p]);
437 (++p == 3) ? (p = 0) : 0;
438}
439
440#endif